diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-09-03 12:20:36 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-09-03 12:20:36 +0000 |
commit | 87d80dcc3e81d80caa11ed88fe6136ffe3e605dc (patch) | |
tree | f491514966641b63b74ba133b1a2177689fc6ba0 /networking/zcip.c | |
parent | 06ab5fb6b9d641ee184facb81eaee718e418d9b2 (diff) | |
download | busybox-w32-87d80dcc3e81d80caa11ed88fe6136ffe3e605dc.tar.gz busybox-w32-87d80dcc3e81d80caa11ed88fe6136ffe3e605dc.tar.bz2 busybox-w32-87d80dcc3e81d80caa11ed88fe6136ffe3e605dc.zip |
zcip: apply patch from
http://bugs.busybox.net/view.php?id=1005
zcip does not claim another IP after defending
Diffstat (limited to 'networking/zcip.c')
-rw-r--r-- | networking/zcip.c | 274 |
1 files changed, 182 insertions, 92 deletions
diff --git a/networking/zcip.c b/networking/zcip.c index d49bb7669..8e6d8792d 100644 --- a/networking/zcip.c +++ b/networking/zcip.c | |||
@@ -15,7 +15,7 @@ | |||
15 | * certainly be used. Its naming is built over multicast DNS. | 15 | * certainly be used. Its naming is built over multicast DNS. |
16 | */ | 16 | */ |
17 | 17 | ||
18 | // #define DEBUG | 18 | //#define DEBUG |
19 | 19 | ||
20 | // TODO: | 20 | // TODO: |
21 | // - more real-world usage/testing, especially daemon mode | 21 | // - more real-world usage/testing, especially daemon mode |
@@ -43,12 +43,7 @@ | |||
43 | 43 | ||
44 | struct arp_packet { | 44 | struct arp_packet { |
45 | struct ether_header hdr; | 45 | struct ether_header hdr; |
46 | // FIXME this part is netinet/if_ether.h "struct ether_arp" | 46 | struct ether_arp arp; |
47 | struct arphdr arp; | ||
48 | struct ether_addr source_addr; | ||
49 | struct in_addr source_ip; | ||
50 | struct ether_addr target_addr; | ||
51 | struct in_addr target_ip; | ||
52 | } ATTRIBUTE_PACKED; | 47 | } ATTRIBUTE_PACKED; |
53 | 48 | ||
54 | enum { | 49 | enum { |
@@ -68,10 +63,19 @@ enum { | |||
68 | DEFEND_INTERVAL = 10 | 63 | DEFEND_INTERVAL = 10 |
69 | }; | 64 | }; |
70 | 65 | ||
71 | static const struct in_addr null_ip = { 0 }; | 66 | /* States during the configuration process. */ |
72 | static const struct ether_addr null_addr = { {0, 0, 0, 0, 0, 0} }; | 67 | enum { |
68 | PROBE = 0, | ||
69 | RATE_LIMIT_PROBE, | ||
70 | ANNOUNCE, | ||
71 | MONITOR, | ||
72 | DEFEND | ||
73 | }; | ||
73 | 74 | ||
74 | static int verbose = 0; | 75 | /* Implicitly zero-initialized */ |
76 | static const struct in_addr null_ip; | ||
77 | static const struct ether_addr null_addr; | ||
78 | static int verbose; | ||
75 | 79 | ||
76 | #define DBG(fmt,args...) \ | 80 | #define DBG(fmt,args...) \ |
77 | do { } while (0) | 81 | do { } while (0) |
@@ -100,6 +104,7 @@ static int arp(int fd, struct sockaddr *saddr, int op, | |||
100 | const struct ether_addr *target_addr, struct in_addr target_ip) | 104 | const struct ether_addr *target_addr, struct in_addr target_ip) |
101 | { | 105 | { |
102 | struct arp_packet p; | 106 | struct arp_packet p; |
107 | memset(&p, 0, sizeof(p)); | ||
103 | 108 | ||
104 | // ether header | 109 | // ether header |
105 | p.hdr.ether_type = htons(ETHERTYPE_ARP); | 110 | p.hdr.ether_type = htons(ETHERTYPE_ARP); |
@@ -107,15 +112,15 @@ static int arp(int fd, struct sockaddr *saddr, int op, | |||
107 | memset(p.hdr.ether_dhost, 0xff, ETH_ALEN); | 112 | memset(p.hdr.ether_dhost, 0xff, ETH_ALEN); |
108 | 113 | ||
109 | // arp request | 114 | // arp request |
110 | p.arp.ar_hrd = htons(ARPHRD_ETHER); | 115 | p.arp.arp_hrd = htons(ARPHRD_ETHER); |
111 | p.arp.ar_pro = htons(ETHERTYPE_IP); | 116 | p.arp.arp_pro = htons(ETHERTYPE_IP); |
112 | p.arp.ar_hln = ETH_ALEN; | 117 | p.arp.arp_hln = ETH_ALEN; |
113 | p.arp.ar_pln = 4; | 118 | p.arp.arp_pln = 4; |
114 | p.arp.ar_op = htons(op); | 119 | p.arp.arp_op = htons(op); |
115 | memcpy(&p.source_addr, source_addr, ETH_ALEN); | 120 | memcpy(&p.arp.arp_sha, source_addr, ETH_ALEN); |
116 | memcpy(&p.source_ip, &source_ip, sizeof (p.source_ip)); | 121 | memcpy(&p.arp.arp_spa, &source_ip, sizeof (p.arp.arp_spa)); |
117 | memcpy(&p.target_addr, target_addr, ETH_ALEN); | 122 | memcpy(&p.arp.arp_tha, target_addr, ETH_ALEN); |
118 | memcpy(&p.target_ip, &target_ip, sizeof (p.target_ip)); | 123 | memcpy(&p.arp.arp_tpa, &target_ip, sizeof (p.arp.arp_tpa)); |
119 | 124 | ||
120 | // send it | 125 | // send it |
121 | if (sendto(fd, &p, sizeof (p), 0, saddr, sizeof (*saddr)) < 0) { | 126 | if (sendto(fd, &p, sizeof (p), 0, saddr, sizeof (*saddr)) < 0) { |
@@ -196,11 +201,11 @@ int zcip_main(int argc, char *argv[]) | |||
196 | int fd; | 201 | int fd; |
197 | int ready = 0; | 202 | int ready = 0; |
198 | suseconds_t timeout = 0; // milliseconds | 203 | suseconds_t timeout = 0; // milliseconds |
199 | time_t defend = 0; | ||
200 | unsigned conflicts = 0; | 204 | unsigned conflicts = 0; |
201 | unsigned nprobes = 0; | 205 | unsigned nprobes = 0; |
202 | unsigned nclaims = 0; | 206 | unsigned nclaims = 0; |
203 | int t; | 207 | int t; |
208 | int state = PROBE; | ||
204 | 209 | ||
205 | // parse commandline: prog [options] ifname script | 210 | // parse commandline: prog [options] ifname script |
206 | while ((t = getopt(argc, argv, "fqr:v")) != EOF) { | 211 | while ((t = getopt(argc, argv, "fqr:v")) != EOF) { |
@@ -307,6 +312,9 @@ fail: | |||
307 | fds[0].events = POLLIN; | 312 | fds[0].events = POLLIN; |
308 | fds[0].revents = 0; | 313 | fds[0].revents = 0; |
309 | 314 | ||
315 | int source_ip_conflict = 0; | ||
316 | int target_ip_conflict = 0; | ||
317 | |||
310 | // poll, being ready to adjust current timeout | 318 | // poll, being ready to adjust current timeout |
311 | if (!timeout) { | 319 | if (!timeout) { |
312 | timeout = ms_rdelay(PROBE_WAIT); | 320 | timeout = ms_rdelay(PROBE_WAIT); |
@@ -314,6 +322,7 @@ fail: | |||
314 | // make the kernel filter out all packets except | 322 | // make the kernel filter out all packets except |
315 | // ones we'd care about. | 323 | // ones we'd care about. |
316 | } | 324 | } |
325 | // set tv1 to the point in time when we timeout | ||
317 | gettimeofday(&tv1, NULL); | 326 | gettimeofday(&tv1, NULL); |
318 | tv1.tv_usec += (timeout % 1000) * 1000; | 327 | tv1.tv_usec += (timeout % 1000) * 1000; |
319 | while (tv1.tv_usec > 1000000) { | 328 | while (tv1.tv_usec > 1000000) { |
@@ -326,64 +335,113 @@ fail: | |||
326 | timeout, intf, nprobes, nclaims); | 335 | timeout, intf, nprobes, nclaims); |
327 | switch (poll(fds, 1, timeout)) { | 336 | switch (poll(fds, 1, timeout)) { |
328 | 337 | ||
329 | // timeouts trigger protocol transitions | 338 | // timeout |
330 | case 0: | 339 | case 0: |
331 | // probes | 340 | VDBG("state = %d\n", state); |
332 | if (nprobes < PROBE_NUM) { | 341 | switch (state) { |
333 | nprobes++; | 342 | case PROBE: |
334 | VDBG("probe/%d %s@%s\n", | 343 | // timeouts in the PROBE state means no conflicting ARP packets |
335 | nprobes, intf, inet_ntoa(ip)); | 344 | // have been received, so we can progress through the states |
336 | (void)arp(fd, &saddr, ARPOP_REQUEST, | ||
337 | &addr, null_ip, | ||
338 | &null_addr, ip); | ||
339 | if (nprobes < PROBE_NUM) { | 345 | if (nprobes < PROBE_NUM) { |
346 | nprobes++; | ||
347 | VDBG("probe/%d %s@%s\n", | ||
348 | nprobes, intf, inet_ntoa(ip)); | ||
349 | (void)arp(fd, &saddr, ARPOP_REQUEST, | ||
350 | &addr, null_ip, | ||
351 | &null_addr, ip); | ||
340 | timeout = PROBE_MIN * 1000; | 352 | timeout = PROBE_MIN * 1000; |
341 | timeout += ms_rdelay(PROBE_MAX | 353 | timeout += ms_rdelay(PROBE_MAX |
342 | - PROBE_MIN); | 354 | - PROBE_MIN); |
343 | } else | 355 | } |
344 | timeout = ANNOUNCE_WAIT * 1000; | 356 | else { |
345 | } | 357 | // Switch to announce state. |
346 | // then announcements | 358 | state = ANNOUNCE; |
347 | else if (nclaims < ANNOUNCE_NUM) { | 359 | nclaims = 0; |
348 | nclaims++; | 360 | VDBG("announce/%d %s@%s\n", |
361 | nclaims, intf, inet_ntoa(ip)); | ||
362 | (void)arp(fd, &saddr, ARPOP_REQUEST, | ||
363 | &addr, ip, | ||
364 | &addr, ip); | ||
365 | timeout = ANNOUNCE_INTERVAL * 1000; | ||
366 | } | ||
367 | break; | ||
368 | case RATE_LIMIT_PROBE: | ||
369 | // timeouts in the RATE_LIMIT_PROBE state means no conflicting ARP packets | ||
370 | // have been received, so we can move immediately to the announce state | ||
371 | state = ANNOUNCE; | ||
372 | nclaims = 0; | ||
349 | VDBG("announce/%d %s@%s\n", | 373 | VDBG("announce/%d %s@%s\n", |
350 | nclaims, intf, inet_ntoa(ip)); | 374 | nclaims, intf, inet_ntoa(ip)); |
351 | (void)arp(fd, &saddr, ARPOP_REQUEST, | 375 | (void)arp(fd, &saddr, ARPOP_REQUEST, |
352 | &addr, ip, | 376 | &addr, ip, |
353 | &addr, ip); | 377 | &addr, ip); |
378 | timeout = ANNOUNCE_INTERVAL * 1000; | ||
379 | break; | ||
380 | case ANNOUNCE: | ||
381 | // timeouts in the ANNOUNCE state means no conflicting ARP packets | ||
382 | // have been received, so we can progress through the states | ||
354 | if (nclaims < ANNOUNCE_NUM) { | 383 | if (nclaims < ANNOUNCE_NUM) { |
384 | nclaims++; | ||
385 | VDBG("announce/%d %s@%s\n", | ||
386 | nclaims, intf, inet_ntoa(ip)); | ||
387 | (void)arp(fd, &saddr, ARPOP_REQUEST, | ||
388 | &addr, ip, | ||
389 | &addr, ip); | ||
355 | timeout = ANNOUNCE_INTERVAL * 1000; | 390 | timeout = ANNOUNCE_INTERVAL * 1000; |
356 | } else { | 391 | } |
392 | else { | ||
393 | // Switch to monitor state. | ||
394 | state = MONITOR; | ||
357 | // link is ok to use earlier | 395 | // link is ok to use earlier |
396 | // FIXME update filters | ||
358 | run(script, "config", intf, &ip); | 397 | run(script, "config", intf, &ip); |
359 | ready = 1; | 398 | ready = 1; |
360 | conflicts = 0; | 399 | conflicts = 0; |
361 | timeout = -1; | 400 | timeout = -1; // Never timeout in the monitor state. |
362 | 401 | ||
363 | // NOTE: all other exit paths | 402 | // NOTE: all other exit paths |
364 | // should deconfig ... | 403 | // should deconfig ... |
365 | if (quit) | 404 | if (quit) |
366 | return EXIT_SUCCESS; | 405 | return EXIT_SUCCESS; |
367 | // FIXME update filters | ||
368 | } | 406 | } |
369 | } | 407 | break; |
370 | break; | 408 | case DEFEND: |
371 | 409 | // We won! No ARP replies, so just go back to monitor. | |
410 | state = MONITOR; | ||
411 | timeout = -1; | ||
412 | conflicts = 0; | ||
413 | break; | ||
414 | default: | ||
415 | // Invalid, should never happen. Restart the whole protocol. | ||
416 | state = PROBE; | ||
417 | pick(&ip); | ||
418 | timeout = 0; | ||
419 | nprobes = 0; | ||
420 | nclaims = 0; | ||
421 | break; | ||
422 | } // switch (state) | ||
423 | break; // case 0 (timeout) | ||
372 | // packets arriving | 424 | // packets arriving |
373 | case 1: | 425 | case 1: |
374 | // maybe adjust timeout | 426 | // We need to adjust the timeout in case we didn't receive |
427 | // a conflicting packet. | ||
375 | if (timeout > 0) { | 428 | if (timeout > 0) { |
376 | struct timeval tv2; | 429 | struct timeval tv2; |
377 | 430 | ||
378 | gettimeofday(&tv2, NULL); | 431 | gettimeofday(&tv2, NULL); |
379 | if (timercmp(&tv1, &tv2, <)) { | 432 | if (timercmp(&tv1, &tv2, <)) { |
433 | // Current time is greater than the expected timeout time. | ||
434 | // Should never happen. | ||
435 | VDBG("missed an expected timeout\n"); | ||
380 | timeout = 0; | 436 | timeout = 0; |
381 | } else { | 437 | } else { |
438 | VDBG("adjusting timeout\n"); | ||
382 | timersub(&tv1, &tv2, &tv1); | 439 | timersub(&tv1, &tv2, &tv1); |
383 | timeout = 1000 * tv1.tv_sec | 440 | timeout = 1000 * tv1.tv_sec |
384 | + tv1.tv_usec / 1000; | 441 | + tv1.tv_usec / 1000; |
385 | } | 442 | } |
386 | } | 443 | } |
444 | |||
387 | if ((fds[0].revents & POLLIN) == 0) { | 445 | if ((fds[0].revents & POLLIN) == 0) { |
388 | if (fds[0].revents & POLLERR) { | 446 | if (fds[0].revents & POLLERR) { |
389 | // FIXME: links routinely go down; | 447 | // FIXME: links routinely go down; |
@@ -397,6 +455,7 @@ fail: | |||
397 | } | 455 | } |
398 | continue; | 456 | continue; |
399 | } | 457 | } |
458 | |||
400 | // read ARP packet | 459 | // read ARP packet |
401 | if (recv(fd, &p, sizeof (p), 0) < 0) { | 460 | if (recv(fd, &p, sizeof (p), 0) < 0) { |
402 | why = "recv"; | 461 | why = "recv"; |
@@ -405,71 +464,102 @@ fail: | |||
405 | if (p.hdr.ether_type != htons(ETHERTYPE_ARP)) | 464 | if (p.hdr.ether_type != htons(ETHERTYPE_ARP)) |
406 | continue; | 465 | continue; |
407 | 466 | ||
408 | VDBG("%s recv arp type=%d, op=%d,\n", | 467 | #ifdef DEBUG |
468 | { | ||
469 | struct ether_addr * sha = (struct ether_addr *) p.arp.arp_sha; | ||
470 | struct ether_addr * tha = (struct ether_addr *) p.arp.arp_tha; | ||
471 | struct in_addr * spa = (struct in_addr *) p.arp.arp_spa; | ||
472 | struct in_addr * tpa = (struct in_addr *) p.arp.arp_tpa; | ||
473 | VDBG("%s recv arp type=%d, op=%d,\n", | ||
409 | intf, ntohs(p.hdr.ether_type), | 474 | intf, ntohs(p.hdr.ether_type), |
410 | ntohs(p.arp.ar_op)); | 475 | ntohs(p.arp.arp_op)); |
411 | VDBG("\tsource=%s %s\n", | 476 | VDBG("\tsource=%s %s\n", |
412 | ether_ntoa(&p.source_addr), | 477 | ether_ntoa(sha), |
413 | inet_ntoa(p.source_ip)); | 478 | inet_ntoa(*spa)); |
414 | VDBG("\ttarget=%s %s\n", | 479 | VDBG("\ttarget=%s %s\n", |
415 | ether_ntoa(&p.target_addr), | 480 | ether_ntoa(tha), |
416 | inet_ntoa(p.target_ip)); | 481 | inet_ntoa(*tpa)); |
417 | if (p.arp.ar_op != htons(ARPOP_REQUEST) | 482 | } |
418 | && p.arp.ar_op != htons(ARPOP_REPLY)) | 483 | #endif |
484 | if (p.arp.arp_op != htons(ARPOP_REQUEST) | ||
485 | && p.arp.arp_op != htons(ARPOP_REPLY)) | ||
419 | continue; | 486 | continue; |
420 | 487 | ||
421 | // some cases are always conflicts | 488 | if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0 && |
422 | if ((p.source_ip.s_addr == ip.s_addr) | 489 | memcmp(&addr, &p.arp.arp_sha, ETH_ALEN) != 0) { |
423 | && (memcmp(&addr, &p.source_addr, | 490 | source_ip_conflict = 1; |
424 | ETH_ALEN) != 0)) { | 491 | } |
425 | collision: | 492 | if (memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0 && |
426 | VDBG("%s ARP conflict from %s\n", intf, | 493 | p.arp.arp_op == htons(ARPOP_REQUEST) && |
427 | ether_ntoa(&p.source_addr)); | 494 | memcmp(&addr, &p.arp.arp_tha, ETH_ALEN) != 0) { |
428 | if (ready) { | 495 | target_ip_conflict = 1; |
429 | time_t now = time(0); | 496 | } |
430 | 497 | ||
431 | if ((defend + DEFEND_INTERVAL) | 498 | VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n", |
432 | < now) { | 499 | state, source_ip_conflict, target_ip_conflict); |
433 | defend = now; | 500 | switch (state) { |
434 | (void)arp(fd, &saddr, | 501 | case PROBE: |
435 | ARPOP_REQUEST, | 502 | case ANNOUNCE: |
436 | &addr, ip, | 503 | // When probing or announcing, check for source IP conflicts |
437 | &addr, ip); | 504 | // and other hosts doing ARP probes (target IP conflicts). |
438 | VDBG("%s defend\n", intf); | 505 | if (source_ip_conflict || target_ip_conflict) { |
439 | timeout = -1; | 506 | conflicts++; |
440 | continue; | 507 | if (conflicts >= MAX_CONFLICTS) { |
508 | VDBG("%s ratelimit\n", intf); | ||
509 | timeout = RATE_LIMIT_INTERVAL * 1000; | ||
510 | state = RATE_LIMIT_PROBE; | ||
441 | } | 511 | } |
442 | defend = now; | 512 | |
513 | // restart the whole protocol | ||
514 | pick(&ip); | ||
515 | timeout = 0; | ||
516 | nprobes = 0; | ||
517 | nclaims = 0; | ||
518 | } | ||
519 | break; | ||
520 | case MONITOR: | ||
521 | // If a conflict, we try to defend with a single ARP probe. | ||
522 | if (source_ip_conflict) { | ||
523 | VDBG("monitor conflict -- defending\n"); | ||
524 | state = DEFEND; | ||
525 | timeout = DEFEND_INTERVAL * 1000; | ||
526 | (void)arp(fd, &saddr, | ||
527 | ARPOP_REQUEST, | ||
528 | &addr, ip, | ||
529 | &addr, ip); | ||
530 | } | ||
531 | break; | ||
532 | case DEFEND: | ||
533 | // Well, we tried. Start over (on conflict). | ||
534 | if (source_ip_conflict) { | ||
535 | state = PROBE; | ||
536 | VDBG("defend conflict -- starting over\n"); | ||
443 | ready = 0; | 537 | ready = 0; |
444 | run(script, "deconfig", intf, &ip); | 538 | run(script, "deconfig", intf, &ip); |
445 | // FIXME rm filters: setsockopt(fd, | 539 | |
446 | // SO_DETACH_FILTER, ...) | 540 | // restart the whole protocol |
447 | } | 541 | pick(&ip); |
448 | conflicts++; | 542 | timeout = 0; |
449 | if (conflicts >= MAX_CONFLICTS) { | 543 | nprobes = 0; |
450 | VDBG("%s ratelimit\n", intf); | 544 | nclaims = 0; |
451 | sleep(RATE_LIMIT_INTERVAL); | ||
452 | } | 545 | } |
453 | // restart the whole protocol | 546 | break; |
547 | default: | ||
548 | // Invalid, should never happen. Restart the whole protocol. | ||
549 | VDBG("invalid state -- starting over\n"); | ||
550 | state = PROBE; | ||
454 | pick(&ip); | 551 | pick(&ip); |
455 | timeout = 0; | 552 | timeout = 0; |
456 | nprobes = 0; | 553 | nprobes = 0; |
457 | nclaims = 0; | 554 | nclaims = 0; |
458 | } | 555 | break; |
459 | // two hosts probing one address is a collision too | 556 | } // switch state |
460 | else if (p.target_ip.s_addr == ip.s_addr | ||
461 | && nclaims == 0 | ||
462 | && p.arp.ar_op == htons(ARPOP_REQUEST) | ||
463 | && memcmp(&addr, &p.target_addr, | ||
464 | ETH_ALEN) != 0) { | ||
465 | goto collision; | ||
466 | } | ||
467 | break; | ||
468 | 557 | ||
558 | break; // case 1 (packets arriving) | ||
469 | default: | 559 | default: |
470 | why = "poll"; | 560 | why = "poll"; |
471 | goto bad; | 561 | goto bad; |
472 | } | 562 | } // switch poll |
473 | } | 563 | } |
474 | bad: | 564 | bad: |
475 | if (foreground) | 565 | if (foreground) |