zcip: fix link-local IP conflict detection
[oweals/busybox.git] / networking / zcip.c
index 6b0b1c4917784182f8549207a7914fcdb6a0273c..45d1f7c1c25cf148825052075a0d20ea15eb9751 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
  * Copyright (C) 2004 by David Brownell
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
 // - avoid silent script failures, especially under load...
 // - link status monitoring (restart on link-up; stop on link-down)
 
+//usage:#define zcip_trivial_usage
+//usage:       "[OPTIONS] IFACE SCRIPT"
+//usage:#define zcip_full_usage "\n\n"
+//usage:       "Manage a ZeroConf IPv4 link-local address\n"
+//usage:     "\n       -f              Run in foreground"
+//usage:     "\n       -q              Quit after obtaining address"
+//usage:     "\n       -r 169.254.x.x  Request this address first"
+//usage:     "\n       -v              Verbose"
+//usage:     "\n"
+//usage:     "\nWith no -q, runs continuously monitoring for ARP conflicts,"
+//usage:     "\nexits only on I/O errors (link down etc)"
+
+#include "libbb.h"
 #include <netinet/ether.h>
-#include <net/ethernet.h>
 #include <net/if.h>
 #include <net/if_arp.h>
-#include <linux/if_packet.h>
 #include <linux/sockios.h>
 
-#include "libbb.h"
 #include <syslog.h>
 
 /* We don't need more than 32 bits of the counter */
@@ -81,6 +91,7 @@ struct globals {
 #define G (*(struct globals*)&bb_common_bufsiz1)
 #define saddr    (G.saddr   )
 #define eth_addr (G.eth_addr)
+#define INIT_G() do { } while (0)
 
 
 /**
@@ -213,6 +224,7 @@ int zcip_main(int argc UNUSED_PARAM, char **argv)
 #define verbose    (L.verbose   )
 
        memset(&L, 0, sizeof(L));
+       INIT_G();
 
 #define FOREGROUND (opts & 1)
 #define QUIT       (opts & 2)
@@ -354,11 +366,11 @@ int zcip_main(int argc UNUSED_PARAM, char **argv)
                                        nprobes++;
                                        VDBG("probe/%u %s@%s\n",
                                                        nprobes, argv_intf, inet_ntoa(ip));
+                                       timeout_ms = PROBE_MIN * 1000;
+                                       timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
                                        arp(/* ARPOP_REQUEST, */
                                                        /* &eth_addr, */ null_ip,
                                                        &null_addr, ip);
-                                       timeout_ms = PROBE_MIN * 1000;
-                                       timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
                                }
                                else {
                                        // Switch to announce state.
@@ -366,10 +378,10 @@ int zcip_main(int argc UNUSED_PARAM, char **argv)
                                        nclaims = 0;
                                        VDBG("announce/%u %s@%s\n",
                                                        nclaims, argv_intf, inet_ntoa(ip));
+                                       timeout_ms = ANNOUNCE_INTERVAL * 1000;
                                        arp(/* ARPOP_REQUEST, */
                                                        /* &eth_addr, */ ip,
                                                        &eth_addr, ip);
-                                       timeout_ms = ANNOUNCE_INTERVAL * 1000;
                                }
                                break;
                        case RATE_LIMIT_PROBE:
@@ -379,10 +391,10 @@ int zcip_main(int argc UNUSED_PARAM, char **argv)
                                nclaims = 0;
                                VDBG("announce/%u %s@%s\n",
                                                nclaims, argv_intf, inet_ntoa(ip));
+                               timeout_ms = ANNOUNCE_INTERVAL * 1000;
                                arp(/* ARPOP_REQUEST, */
                                                /* &eth_addr, */ ip,
                                                &eth_addr, ip);
-                               timeout_ms = ANNOUNCE_INTERVAL * 1000;
                                break;
                        case ANNOUNCE:
                                // timeouts in the ANNOUNCE state mean no conflicting ARP packets
@@ -391,10 +403,10 @@ int zcip_main(int argc UNUSED_PARAM, char **argv)
                                        nclaims++;
                                        VDBG("announce/%u %s@%s\n",
                                                        nclaims, argv_intf, inet_ntoa(ip));
+                                       timeout_ms = ANNOUNCE_INTERVAL * 1000;
                                        arp(/* ARPOP_REQUEST, */
                                                        /* &eth_addr, */ ip,
                                                        &eth_addr, ip);
-                                       timeout_ms = ANNOUNCE_INTERVAL * 1000;
                                }
                                else {
                                        // Switch to monitor state.
@@ -483,22 +495,28 @@ int zcip_main(int argc UNUSED_PARAM, char **argv)
                        }
 #endif
                        if (p.arp.arp_op != htons(ARPOP_REQUEST)
-                        && p.arp.arp_op != htons(ARPOP_REPLY))
+                        && p.arp.arp_op != htons(ARPOP_REPLY)
+                       ) {
                                continue;
+                       }
 
                        source_ip_conflict = 0;
                        target_ip_conflict = 0;
 
-                       if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0
-                        && memcmp(&p.arp.arp_sha, &eth_addr, ETH_ALEN) != 0
-                       ) {
-                               source_ip_conflict = 1;
-                       }
-                       if (p.arp.arp_op == htons(ARPOP_REQUEST)
-                        && memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0
-                        && memcmp(&p.arp.arp_tha, &eth_addr, ETH_ALEN) != 0
-                       ) {
-                               target_ip_conflict = 1;
+                       if (memcmp(&p.arp.arp_sha, &eth_addr, ETH_ALEN) != 0) {
+                               if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr))) {
+                                       /* A probe or reply with source_ip == chosen ip */
+                                       source_ip_conflict = 1;
+                               }
+                               if (p.arp.arp_op == htons(ARPOP_REQUEST)
+                                && memcmp(p.arp.arp_spa, &null_ip, sizeof(struct in_addr)) == 0
+                                && memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0
+                               ) {
+                                       /* A probe with source_ip == 0.0.0.0, target_ip == chosen ip:
+                                        * another host trying to claim this ip!
+                                        */
+                                       target_ip_conflict = 1;
+                               }
                        }
 
                        VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n",