Re: zeroconf Debian package
Michael Schmidt <[email protected]> Wed, 04 May 2005 16:39:23 +0200
| Newsgroups | gmane.network.zeroconf.workers |
|---|---|
| Organization | University of Siegen |
| Message-ID | <[email protected]> |
This is a multi-part message in MIME format. --------------040707080305070400030207 Content-Type: text/plain; charset=ISO-8859-15 Content-Transfer-Encoding: 7bit Hi Freek, Freek Dijkstra schrieb: > Thanks for the info on the Arthur von Hoff release. Indeed, ZCIP uses > two libraries, one of which is outdated. I was hoping to update it > (though I currently have little time and can't estimate now how much > work it is). Attached is a file zcip.c, which contains a slightly modified and extended version of zcip-4 (for a proprietary project of mine), which I have updated to use libnet v1.1x. It hasn't been tested extensively, so that I can't guarantee perfect functionality. Nevertheless, I think it's quite helpful for anybody who wants to back-port the changes to the original zcip-4 code. I have, however, eventually discarded the whole zcip-4 code in favor of the van Hoff / Brownell code. I'm sorry I'm currently not having the time to do the backport to zcip-4 myself. > However, so far I thought Arthur von Hoff's implementation > was not maintained at all. It now seems it is actively maintaining by > Anand Kumria at http://www.progsoc.org/~wildfire/zeroconf/ I've just taken a look at this code. From my current understanding, it's not susceptible to the timing flaw that I had found with the van Hoff / Brownell code posted here some time ago. So this may be a good starting point. Michael -- ============================================ Michael Schmidt -------------------------------------------- Institute for Digital Communications Systems University of Siegen, Germany -------------------------------------------- http: www.dcs.uni-siegen.de e-mail: [email protected] mobile: +49 179 7810214 ============================================ --------------040707080305070400030207 Content-Type: text/plain; name="zcip.c" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="zcip.c" /*************************************************************************** zcip.c - description ------------------- begin : Mon Jul 22 2002 copyright : (C) 2002 by Transmeta Corporation ***************************************************************************/ // Copyright 2002 Transmeta Corporation. This program as released under // the 3-clause BSD license, see the file "Copyright" for details. #ifdef HAVE_CONFIG_H #include <config.h> #endif // // This program does the Zeroconf ad-hoc link-local IP configuration trick. // // // The algorithm is: // 0. seed rng with MAC address // 1. pick a random address between 169.254.1.0 and 169.254.254.0 (inclusive) // 2. arp probe for it a couple of times (source MAC=me IP=0.0.0.0, target MAC=0 IP=selected) // 3. if we get a reply to the probe or we see another probe for the same target address, goto 1 // 4. claim the address by sending a couple of arps for it (source MAC=me IP=selected, target MAC=0, IP=selected) // 5. watch for address collisions, if found defend, if defense fails goto 1 // #include <errno.h> #include <stdarg.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> // this is needed for the 48-bit random number functions in stdlib.h #define __USE_SVID 1 #define __USE_XOPEN 1 #include <stdlib.h> #include <string.h> #include <syslog.h> #include <time.h> #include <unistd.h> #include <getopt.h> #include <libnet.h> #include <pcap.h> #include <arpa/inet.h> #include <net/ethernet.h> #include <net/if.h> #include <net/if_arp.h> #include <net/route.h> #include <netinet/in.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/types.h> #define RELEASE "4" int output_to_syslog = 0; struct zc_if { char *if_name; // string name of the interface (eg eth0, or eth1:0) libnet_t *l; // our libnet descriptor pcap_t *pcap; // the packet capture device int pcap_fd; // the file descriptor to listen for packets on struct libnet_ether_addr *my_ea; // the hardware address for the interface struct in_addr ip; // the IP we're probing for or are defending (network byte sex) uint8_t *arp_defense_packet; // sent out as defense by handle_collision() struct timeval last_defense; // time we last tried to defend on this interface }; void open_log(void) { openlog("zcip", LOG_CONS | LOG_PID, LOG_DAEMON); fflush(NULL); fclose(stdin); fclose(stdout); fclose(stderr); } void die(char *format, ...) { va_list args; char buffer[1024]; va_start(args, format); if (output_to_syslog) { vsnprintf(buffer, sizeof(buffer), format, args); syslog(LOG_ERR, buffer); } else { vprintf(format, args); } va_end(args); exit(1); } void print(char *format, ...) { va_list args; char buffer[1024]; va_start(args, format); if (output_to_syslog) { vsnprintf(buffer, sizeof(buffer), format, args); syslog(LOG_INFO, buffer); } else { vprintf(format, args); } va_end(args); } // like memcmp, but for timevals // returns -1 if t0 < t1 // 0 if t0 = t1 // 1 if t0 > t1 int tvcmp(const struct timeval *t0, const struct timeval *t1) { if (t0->tv_sec < t1->tv_sec) return -1; if (t0->tv_sec > t1->tv_sec) return 1; if (t0->tv_usec < t1->tv_usec) return -1; if (t0->tv_usec > t1->tv_usec) return 1; return 0; } // t0 += t1 void tvadd(struct timeval *t0, const struct timeval *t1) { t0->tv_usec += t1->tv_usec; if (t0->tv_usec >= 1000000) { t0->tv_usec -= 1000000; t0->tv_sec ++; } t0->tv_sec += t1->tv_sec; } // t0 -= t1, but min is { 0, 0 } void tvsub(struct timeval *t0, const struct timeval *t1) { if (tvcmp(t0, t1) <= 0) { t0->tv_sec = 0; t0->tv_usec = 0; return; } t0->tv_usec -= t1->tv_usec; if (t0->tv_usec < 0) { t0->tv_usec += 1000000; t0->tv_sec --; } t0->tv_sec -= t1->tv_sec; } #ifdef linux # define CHECK_KERNEL_SOCKETFILTERS /* libpcap has user mode emulation of this, but it doesn't * seem to be kicking in */ #endif #ifdef CHECK_KERNEL_SOCKETFILTERS #include <linux/types.h> #include <linux/filter.h> // // NAME: kernel_socketfilter() // // FUNCTION: Verifies that kernel socket filters are available. // // ARGUMENTS: None // // GLOBALS: None // // RETURNS: Only if it worked. If there's a problem it prints an error // message and exits. // static void kernel_socketfilter() { #ifdef SO_ATTACH_FILTER int sock = socket (PF_INET, SOCK_DGRAM, 0); int err; struct sock_filter code = BPF_STMT (BPF_RET|BPF_K, 0); struct sock_fprog filt = { 1, &code }; if (sock < 0) die ("can't get a socket?\n"); err = setsockopt (sock, SOL_SOCKET, SO_ATTACH_FILTER, &filt, sizeof filt); close (sock); if (err == 0) return; #endif die ("kernel doesn't support BSD socket filters ... reconfigure\n"); } #endif // // NAME: ifdown() // // FUNCTION: Brings down the interface named by the argument if_name. // // ARGUMENTS: The name of the interface being handled. // // GLOBALS: None // // RETURNS: Only if it worked. If there's a problem it prints an error // message and exits. // void ifdown(struct zc_if *this_if) { int r; int sock_fd; struct ifreq ifr; sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if (sock_fd < 0) die("cannot create socket: %s\n", strerror(errno)); strncpy(ifr.ifr_name, this_if->if_name, IFNAMSIZ - 1); ifr.ifr_name[IFNAMSIZ - 1] = (char)NULL; // get flags r = ioctl(sock_fd, SIOCGIFFLAGS, &ifr); if (r < 0) die("SIOCGIFFLAGS error in ifdown: %s\n", strerror(errno)); // set flags to bring interface down ifr.ifr_flags &= ~(IFF_UP | IFF_BROADCAST | IFF_MULTICAST | IFF_PROMISC); r = ioctl(sock_fd, SIOCSIFFLAGS, &ifr); if (r < 0) die("SIOCSIFFLAGS error in ifdown2: %s\n", strerror(errno)); close(sock_fd); } // // NAME: ifup() // // FUNCTION: Brings up the interface named by the argument if_name. // The IP address is passed in. Assumes that the interface is // currently down. // // ARGUMENTS: The IP address to set the interface to (a struct in_addr // with network byte sex). // // GLOBALS: None. // // RETURNS: Only if it worked. If there's a problem it prints an error // message and exits. // void ifup(struct zc_if *this_if) { int r; int sock_fd; struct ifreq ifr; sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if (sock_fd < 0) die("cannot create socket: %s\n", strerror(errno)); strncpy(ifr.ifr_name, this_if->if_name, IFNAMSIZ - 1); ifr.ifr_name[IFNAMSIZ - 1] = (char)NULL; // set address ifr.ifr_addr.sa_family = AF_INET; ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr = this_if->ip; r = ioctl(sock_fd, SIOCSIFADDR, &ifr); if (r < 0) { // make a nice message for the most common error... if (errno == -EPERM) die("You need to run this program as root.\n"); // ...but handle other errors as well die("SIOCSIFADDR error in ifup: %s\n", strerror(errno)); } // get flags r = ioctl(sock_fd, SIOCGIFFLAGS, &ifr); if (r < 0) die("SIOCGIFFLAGS error in ifup: %s\n", strerror(errno)); // set flags to bring interface up ifr.ifr_flags |= (IFF_UP | IFF_BROADCAST | IFF_MULTICAST); r = ioctl(sock_fd, SIOCSIFFLAGS, &ifr); if (r < 0) die("SIOCSIFFLAGS error in ifup: %s(%i)\n", strerror(errno),errno); close(sock_fd); } // // NAME: seed_rng() // // FUNCTION: Seeds random-number generator with random # or the MAC address. // // ARGUMENTS: the MAC address for the applicable interface (if NULL, // then random # is used for seed). // // GLOBALS: None. // // RETURNS: Only if it worked. If there was a problem, writes an error // message to the syslog and exits with an exit code of 1. // void seed_rng(struct libnet_ether_addr *my_ea) { uint16_t seed[3]; if (my_ea == NULL) // use true random seed { int handle, length; handle = open ("/dev/urandom", O_RDONLY); if (handle == -1) { perror ("/dev/urandom not available!"); exit (-1); } length = read (handle, seed, sizeof (seed)); if (length != sizeof (seed)) { perror ("/dev/urandom read error!"); exit (-1); } close (handle); } else // seed RNG with MAC address { seed[0] = (uint8_t)my_ea->ether_addr_octet[0]; seed[0] += (uint8_t)my_ea->ether_addr_octet[1] << 8; seed[1] = (uint8_t)my_ea->ether_addr_octet[2]; seed[1] += (uint8_t)my_ea->ether_addr_octet[3] << 8; seed[2] = (uint8_t)my_ea->ether_addr_octet[4]; seed[2] += (uint8_t)my_ea->ether_addr_octet[5] << 8; } seed48(seed); } // // NAME: pick_random_address() // // FUNCTION: Picks a random address of the form 169.254.X.Y, where X // is between 1 and 254 inclusive, and Y is between 0 and 255 // inclusive. // // ARGUMENTS: The address structure to put the randomly-generated address // in. // // GLOBALS: None. // // RETURNS: If it worked. If there's a problem, writes an error // message to the syslog and exits with an exit code of 1. // // FIXME: We should keep track of what addresses we've tried, so we // dont accidentally reuse already-probed addresses. Use // something like a static 16 kilo-bit array, with each bit 0 // if the address is untested and 1 if it's been tested. // void pick_random_address(struct in_addr *ip) { uint16_t suffix; do { suffix = ((uint32_t)mrand48() >> 16); } while (suffix < 0x0100 || suffix > 0xfeff); // network byte sex ip->s_addr = htonl(0xa9fe0000|suffix); } // // NAME: build_arp_defense_packet() // // FUNCTION: Initializes the ARP packet in arp_defense_packet with the // address in probe_ip. // // ARGUMENTS: The interface to defend for. // // GLOBALS: None // // RETURNS: If all went well. If there's a problem it prints an error // message and exits with an exit code of 1. // void build_arp_defense_packet(struct zc_if *this_if) { struct ether_addr broadcast_ea = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } }; struct ether_addr null_ea = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; libnet_ptag_t t; // // build the arp packet, in case we need to defend our address later // /* * Build the packet, remmebering that order IS important. We must * build the packet from lowest protocol type on up as it would * appear on the wire. So for our ARP packet: * * ------------------------------------------- * | Ethernet | ARP | * ------------------------------------------- * ^ ^ * |------------------ | * libnet_build_ethernet()--| | * | * libnet_build_arp()-----------| */ t = libnet_build_arp( ARPHRD_ETHER, /* hardware addr */ ETHERTYPE_IP, /* protocol addr */ 6, /* hardware addr size */ 4, /* protocol addr size */ ARPOP_REQUEST, /* operation type */ this_if->my_ea->ether_addr_octet, // source ethernet address (uint8_t *)(&(this_if->ip.s_addr)), // From: my IP null_ea.ether_addr_octet, // To: 00:00:00:00:00:00 (uint8_t *)(&(this_if->ip.s_addr)), // To: my IP NULL, /* payload */ 0, /* payload size */ this_if->l, /* libnet handle */ 0); /* libnet id */ if (t == -1) die("libnet_build_arp(): error\n"); t = libnet_autobuild_ethernet( broadcast_ea.ether_addr_octet, // destination ethernet address ETHERTYPE_ARP, /* protocol type */ this_if->l); /* libnet handle */ if (t == -1) die("libnet_autobuild_ethernet(): error\n"); } void packet_handler(char *unused, struct pcap_pkthdr *h, uint8_t *packet) { #if ZCIP_DEBUG struct tm *tm_p; int i; tm_p = localtime((time_t *)&(h->ts.tv_sec)); print( "%02d:%02d:%02d.%06d\n", tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec, (int)h->ts.tv_usec ); for (i = 0; i < h->caplen; i ++) { if (i % 16 == 0) print(" "); print(" %02X", packet[i]); if (i % 16 == 15) print("\n"); else if (i % 8 == 7) print(" "); } print("\n"); print( "source EA: %02X:%02X:%02X:%02X:%02X:%02X\n", packet[LIBNET_ETH_H + 8], packet[LIBNET_ETH_H + 9], packet[LIBNET_ETH_H + 10], packet[LIBNET_ETH_H + 11], packet[LIBNET_ETH_H + 12], packet[LIBNET_ETH_H + 13] ); print( "uint32_t source EA: 0x%08X\n", *((uint32_t *)(&packet[LIBNET_ETH_H + 8])) ); print( "uint16_t source EA: 0x%04hX\n", *((uint16_t *)(&packet[LIBNET_ETH_H + 12])) ); print( "source IP: %d.%d.%d.%d\n", packet[LIBNET_ETH_H + 14], packet[LIBNET_ETH_H + 15], packet[LIBNET_ETH_H + 16], packet[LIBNET_ETH_H + 17] ); print( "target EA: %02X:%02X:%02X:%02X:%02X:%02X\n", packet[LIBNET_ETH_H + 18], packet[LIBNET_ETH_H + 19], packet[LIBNET_ETH_H + 20], packet[LIBNET_ETH_H + 21], packet[LIBNET_ETH_H + 22], packet[LIBNET_ETH_H + 23] ); print( "target IP: %d.%d.%d.%d\n", packet[LIBNET_ETH_H + 24], packet[LIBNET_ETH_H + 25], packet[LIBNET_ETH_H + 26], packet[LIBNET_ETH_H + 27] ); #else // make gcc/lint/.. be quiet unused = unused; h = h; packet = packet; #endif } // // NAME: handle_collision() // // FUNCTION: This function gets called when a collision is detected. // We check to see if we've had a collision in the recent // past, and if so just give up. If this is the first // collision in recent history, we defend. // // ARGUMENTS: The interface name that the collision is on. // // GLOBALS: None // // RETURNS: 0 if the address was successfully defended and we can go // on using it, and -1 if we need to stop using this address // and try a new one. // int handle_collision(struct zc_if *this_if) { int r; struct timeval ten_seconds = { 10, 0 }; struct timeval now, t; // print("handling collision\n"); r = gettimeofday(&now, NULL); if (r != 0) die("gettimeofday: %s\n", strerror(errno)); t = now; tvsub(&t, &(this_if->last_defense)); // t -= last_defense if (tvcmp(&t, &ten_seconds) <= 0) { print("persistent collision or too many collisions, reconfiguring\n"); return -1; } this_if->last_defense = now; // print("defending\n"); r = libnet_write(this_if->l); if (r == -1) die("libnet_write(): error in handle_collision()\n"); return 0; } // // set the default route to be through if_name // void set_default_route(struct zc_if *this_if) { int r; int sock_fd; struct in_addr tmp_ip; struct rtentry rte; sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if (sock_fd < 0) die("cannot create socket: %s\n", strerror(errno)); tmp_ip.s_addr = htonl(0); memset(&rte, 0, sizeof(rte)); // destination: 0.0.0.0 rte.rt_dst.sa_family = AF_INET; ((struct sockaddr_in *)&rte.rt_dst)->sin_addr = tmp_ip; rte.rt_flags = RTF_UP; rte.rt_dev = this_if->if_name; r = ioctl(sock_fd, SIOCADDRT, &rte); if (r < 0) die("SIOCSADDRT error: %s\n", strerror(errno)); close(sock_fd); } // // NAME: claim_address() // // FUNCTION: Sends out ARPs claiming ownership of the specified address. // // ARGUMENTS: The address to claim (network byte sex), and the interface name. // // GLOBALS: None. // // RETURNS: If everything went well it returns 0. If a collision was // detected and defense failed, it returns -1. In this case, // we need to try a new address. If there was some other // problem it prints an error message and exits with an exit // code of 1. // int claim_address(struct zc_if *this_if) { int r; int num_arps_done; libnet_ptag_t t; struct ether_addr broadcast_ea = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } }; struct ether_addr null_ea = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; print("claiming ownership of address %s\n", inet_ntoa(this_if->ip)); // // send out a couple of gratuitous arps to claim this address // t = libnet_build_arp( ARPHRD_ETHER, /* hardware addr */ ETHERTYPE_IP, /* protocol addr */ 6, /* hardware addr size */ 4, /* protocol addr size */ ARPOP_REQUEST, /* operation type */ this_if->my_ea->ether_addr_octet, // source ethernet address (uint8_t *)(&(this_if->ip.s_addr)), // From: my IP null_ea.ether_addr_octet, // destination ethernet address (uint8_t *)(&(this_if->ip.s_addr)), // To: my IP NULL, /* payload */ 0, /* payload size */ this_if->l, /* libnet handle */ 0); /* libnet id */ if (t == -1) die("libnet_build_arp(): error\n"); t = libnet_autobuild_ethernet( broadcast_ea.ether_addr_octet, // destination ethernet address ETHERTYPE_ARP, /* protocol type */ this_if->l); /* libnet handle */ if (t == -1) die("libnet_autobuild_ethernet(): error\n"); for (num_arps_done = 0; num_arps_done < 2; num_arps_done ++) { struct timeval start; r = gettimeofday(&start, NULL); if (r < 0) die("gettimeofday(): %s\n", strerror(errno)); // print("sending probe %d for %s\n", (num_probes_done + 1), inet_ntoa(probe_ip)); r = libnet_write(this_if->l); if (r == -1) die("libnet_write(): error in claim_address()\n"); while (1) { fd_set readers; struct timeval now, timeout; FD_ZERO(&readers); FD_SET(this_if->pcap_fd, &readers); timeout = start; timeout.tv_sec += 2; r = gettimeofday(&now, NULL); if (r < 0) die("gettimeofday(): %s\n", strerror(errno)); // tvsub is like a prefix -=, except that the answer is non-negative // ({ 0, 0 } if the result would have been negative) tvsub(&timeout, &now); if ((timeout.tv_sec == 0) && (timeout.tv_usec == 0)) break; r = select(this_if->pcap_fd + 1, &readers, NULL, NULL, &timeout); if (r == -1) die("select: %s\n", strerror(errno)); if (r == 0) continue; // timeout r = pcap_dispatch(this_if->pcap, 10, (pcap_handler)packet_handler, NULL); if (r == -1) die("pcap error: %s\n", pcap_geterr(this_if->pcap)); // if we get here, then we've detected a collision r = handle_collision(this_if); if (r != 0) { return -1; } } } return 0; } // // NAME: probe_for_address() // // FUNCTION: Sends ARP probes and listens for replies, to determine // if the address is currently in use by a host on the // link-local network. // // ARGUMENTS: The interface to probe on // // GLOBALS: None // // RETURNS: If any host claims the address or if any host other than // us is also probing for this address, it returns 0, // otherwise it returns -1. // If there's a problem, it exits printing an informative // error message and with an exit code of 1. // int probe_for_address(struct zc_if *this_if) { int r; char ebuf[LIBNET_ERRBUF_SIZE]; struct ether_addr broadcast_ea = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } }; struct ether_addr null_ea = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; struct in_addr null_ip; char filter_code[128]; struct bpf_program bpf_prog; libnet_ptag_t t; int max_probes = 4; // how many ARP probes to send int num_probes_done; print("probing for %s\n", inet_ntoa(this_if->ip)); null_ip.s_addr = htonl(0); // 0.0.0.0 t = libnet_build_arp( ARPHRD_ETHER, /* hardware addr */ ETHERTYPE_IP, /* protocol addr */ 6, /* hardware addr size */ 4, /* protocol addr size */ ARPOP_REQUEST, /* operation type */ this_if->my_ea->ether_addr_octet, // source ethernet address (uint8_t *)(&(null_ip.s_addr)), // From: 0.0.0.0 null_ea.ether_addr_octet, // destination ethernet address (uint8_t *)(&(this_if->ip.s_addr)), // To: 169.254.X.Y NULL, /* payload */ 0, /* payload size */ this_if->l, /* libnet handle */ 0); /* libnet id */ if (t == -1) die("libnet_build_arp(): error\n"); t = libnet_autobuild_ethernet( broadcast_ea.ether_addr_octet, // destination ethernet address ETHERTYPE_ARP, /* protocol type */ this_if->l); /* libnet handle */ if (t == -1) die("libnet_autobuild_ethernet(): error\n"); // initialize the packet capture interface // read up to the first 100 bytes of each captured packet // wait 10 ms for packets to accumulate before returning this_if->pcap = pcap_open_live(this_if->if_name, 100, 0, 10, ebuf); if (!(this_if->pcap)) die("pcap_open_live(\"%s\"): %s\n", this_if->if_name, ebuf); // From revision 4 of the IETF Internet Draft "Dynamic Configuration // of IPv4 Link-Local Addresses": // // If during this period the host receives any ARP packet where the // packet's 'sender IP address' is the address being probed for, then // the host MUST treat this address as being in use by some other // host, and MUST select a new pseudo-random address and repeat the // process. In addition, if during this period the host receives any // ARP probe where the packet's 'target IP address' is the address // being probed for, and the packet's 'sender hardware address' is // not the hardware address of any of the host's interfaces, then the // host MUST similarly treat this as an address collision and select // a new address as above. This can occur if two (or more) hosts // attempt to configure the same link-local address at the same time. // // In an ARP packet, the source Ethernet address occupies bytes 8-13 // (inclusive), source IP is in bytes 14-17, target Ethernet address // is in 18-23, and target IP is in 24-27. // // We need to match: // source ip = probe_ip // or // target ip = probe_ip and source ea != me // snprintf( filter_code, sizeof(filter_code), "arp and ((arp[14:4]=0x%02X%02X%02X%02X) or (arp[24:4]=0x%02X%02X%02X%02X and (arp[8:4] != 0x%02X%02X%02X%02X or arp[12:2]!=0x%02X%02X)))", ((uint8_t *)(&this_if->ip.s_addr))[0], // convert from network byte-order (1/4) ((uint8_t *)(&this_if->ip.s_addr))[1], // convert from network byte-order (2/4) ((uint8_t *)(&this_if->ip.s_addr))[2], // convert from network byte-order (3/4) ((uint8_t *)(&this_if->ip.s_addr))[3], // convert from network byte-order (4/4) ((uint8_t *)(&this_if->ip.s_addr))[0], // convert from network byte-order (1/4) ((uint8_t *)(&this_if->ip.s_addr))[1], // convert from network byte-order (2/4) ((uint8_t *)(&this_if->ip.s_addr))[2], // convert from network byte-order (3/4) ((uint8_t *)(&this_if->ip.s_addr))[3], // convert from network byte-order (4/4) this_if->my_ea->ether_addr_octet[0], this_if->my_ea->ether_addr_octet[1], this_if->my_ea->ether_addr_octet[2], this_if->my_ea->ether_addr_octet[3], this_if->my_ea->ether_addr_octet[4], this_if->my_ea->ether_addr_octet[5] ); // print("filter code: [%s]\n", filter_code); r = pcap_compile(this_if->pcap, &bpf_prog, filter_code, 0, -1); if (r == -1) die("pcap_compile(): error\n"); r = pcap_setfilter(this_if->pcap, &bpf_prog); if (r == -1) die("pcap_setfilter(): error\n"); pcap_freecode(&bpf_prog); this_if->pcap_fd = pcap_fileno(this_if->pcap); if (this_if->pcap_fd < 0) die("pcap_fd < 0!\n"); for (num_probes_done = 0; num_probes_done < max_probes; num_probes_done ++) { struct timeval start; r = gettimeofday(&start, NULL); if (r < 0) die("gettimeofday(): %s\n", strerror(errno)); // print("sending probe %d for %s\n", (num_probes_done + 1), inet_ntoa(probe_ip)); r = libnet_write(this_if->l); if (r == -1) die("libnet_write(): error in probe_for_address()\n"); while (1) { fd_set readers; struct timeval now, timeout; FD_ZERO(&readers); FD_SET(this_if->pcap_fd, &readers); r = gettimeofday(&now, NULL); if (r < 0) die("gettimeofday(): %s\n", strerror(errno)); timeout.tv_sec = start.tv_sec - now.tv_sec; timeout.tv_usec = (start.tv_usec + 200000) - now.tv_usec; if (timeout.tv_usec >= 1000000) { timeout.tv_usec -= 1000000; timeout.tv_sec += 1; } if ( (timeout.tv_sec < 0) || ((timeout.tv_sec == 0) && (timeout.tv_usec == 0)) ) break; r = select(this_if->pcap_fd + 1, &readers, NULL, NULL, &timeout); if (r == -1) die("select: %s\n", strerror(errno)); if (r == 0) break; print("avoiding collision - discarding address\n"); // if we get here, then the packet filter has detected an ARP // packet indicating we shouldnt use this IP address r = pcap_dispatch(this_if->pcap, 10, (pcap_handler)packet_handler, NULL); if (r == -1) die("pcap error: %s\n", pcap_geterr(this_if->pcap)); pcap_close(this_if->pcap); return 0; } } // // if we get here, no matching packet was found during the ARP probing // pcap_close(this_if->pcap); return -1; } // // NAME: wait_for_collision() // // FUNCTION: Watches the network for other hosts trying to claim our // address. If someone else tries to take our address, we // defend it. If the defense fails, this function returns. // // ARGUMENTS: The interface to watch // // RETURNS: Nothing. // void wait_for_collision(struct zc_if *this_if) { print("watching for collisions\n"); while (1) { int r; fd_set readers; FD_ZERO(&readers); FD_SET(this_if->pcap_fd, &readers); r = select(this_if->pcap_fd + 1, &readers, NULL, NULL, NULL); if (r == -1) die("select: %s\n", strerror(errno)); // if we get here, then the packet filter has detected a potential // collision that we need to defend agains r = pcap_dispatch(this_if->pcap, 10, (pcap_handler)packet_handler, NULL); if (r == -1) die("pcap error: %s\n", pcap_geterr(this_if->pcap)); if (handle_collision(this_if) != 0) return; } } // // NAME: stop_listening_for_collisions() // // FUNCTION: This function undoes the initialization done by // start_listening_for_collisions(). Shuts down the packet // filter. Destroys the ARP defense packet. // // ARGUMENTS: The interface to de-configure // // GLOBALS: None // // RETURNS: If it worked. If there's a problem initializing the // packet filter, it prints an error message and exits. // void stop_listening_for_collisions(struct zc_if *this_if) { pcap_close(this_if->pcap); } // // NAME: start_listening_for_collisions() // // FUNCTION: Initializes a packet filter to watch for address // collisions. Initializes the ARP defense packet. Resets // the timestamp keeping track of the last collision to zero // (Jan 1, 1970). // // ARGUMENTS: The interface to listen on. // // GLOBALS: The IP address in probe_ip is read and assumed to be ours. // pcap and pcap_fd are initialized. arp_defense_packet is // initialized. // // RETURNS: If it worked. If there's a problem initializing the // packet filter, it prints an error message and exits. // void start_listening_for_collisions(struct zc_if *this_if) { int r; char ebuf[LIBNET_ERRBUF_SIZE]; char filter_code[128]; struct bpf_program bpf_prog; build_arp_defense_packet(this_if); // initialize the packet capture interface // read up to the first 100 bytes of each captured packet // DON'T put interface into promiscuous mode // wait 10 ms for packets to accumulate before returning this_if->pcap = pcap_open_live(this_if->if_name, 100, 0, 10, ebuf); if (!this_if->pcap) die("pcap_open_live(\"%s\"): %s\n", this_if->if_name, ebuf); // From revision 4 of the IETF Internet Draft "Dynamic Configuration // of IPv4 Link-Local Addresses": // // At any time, if a host receives an ARP packet where the 'sender // IP address' is the host's own IP address, but the 'sender // hardware address' does not match any of the host's own // interface addresses, then this is a conflicting ARP packet, // indicating an address collision. A host MUST respond to a // conflicting ARP packet... // // In an ARP packet, the source Ethernet address occupies bytes 8-13 // (inclusive), source IP is in bytes 14-17, target Ethernet address // is in 18-23, and target IP is in 24-27. // // We need to match: // source ip = probe_ip // and // source ea != me // snprintf( filter_code, sizeof(filter_code), "arp and arp[14:4]=0x%02X%02X%02X%02X and (arp[8:4] != 0x%02X%02X%02X%02X or arp[12:2]!=0x%02X%02X)", ((uint8_t *)(&this_if->ip.s_addr))[0], // convert from network byte-order (1/4) ((uint8_t *)(&this_if->ip.s_addr))[1], // convert from network byte-order (2/4) ((uint8_t *)(&this_if->ip.s_addr))[2], // convert from network byte-order (3/4) ((uint8_t *)(&this_if->ip.s_addr))[3], // convert from network byte-order (4/4) this_if->my_ea->ether_addr_octet[0], this_if->my_ea->ether_addr_octet[1], this_if->my_ea->ether_addr_octet[2], this_if->my_ea->ether_addr_octet[3], this_if->my_ea->ether_addr_octet[4], this_if->my_ea->ether_addr_octet[5] ); // print("filter code: [%s]\n", filter_code); r = pcap_compile(this_if->pcap, &bpf_prog, filter_code, 0, -1); if (r == -1) die("pcap_compile(): error\n"); r = pcap_setfilter(this_if->pcap, &bpf_prog); if (r == -1) die("pcap_setfilter(): error\n"); pcap_freecode(&bpf_prog); this_if->pcap_fd = pcap_fileno(this_if->pcap); if (this_if->pcap_fd < 0) die("pcap_fd < 0!\n"); this_if->last_defense.tv_sec = 0; this_if->last_defense.tv_usec = 0; } void background(void) { static int is_backgrounded = 0; int r; // only do this once if (is_backgrounded) return; is_backgrounded = 1; fflush(NULL); r = fork(); if (r < 0) { print("failed to fork: %s", strerror(errno)); exit(1); } if (r > 0) { // kill off the parent // the sleep is here so that the childs last tty output comes before the return of the shell prompt if (output_to_syslog == 0) sleep(1); exit(0); } // // here we are in the child // // from now on, all output needs to go to the syslog no matter what the user asked for if (output_to_syslog == 0) { print("successfully acquired an IP address, backgrounding\n"); print("all subsequent output will go to the syslog\n"); open_log(); output_to_syslog = 1; } } struct ether_addr *strtomac (char *str) { char *ptr = str; int i; uint8_t *ea = malloc(sizeof(struct ether_addr)); if (!ea) return NULL; for(i=0; i<6; i++){ ea[i] = (uint8_t) strtol(ptr, NULL, 16); if( i!=5 && !(ptr=strchr(ptr,':')) ) ptr = ":00:00:00:00:00"; ptr++; } return (struct ether_addr *) ea; } void version(void) { printf ("zcip release " RELEASE "\nCopyright (C) 2001-2002 Various authors\n" "zcip comes with NO WARRANTY, to the extent permitted by law.\n" "You may redistribute copies of zcip under the terms of the 3-clause " "BSD license.\n" "For more information about these matters, see the file named " "Copyright."); exit (0); } void usage(int exit_code) { printf("usage: zcip [OPTIONS]\n" "Zeroconf IPv4 Link-Local Address Configuration\n" "OPTIONS:\n" " -h, --help Print this help, and exit.\n" " -i, --interface IFNAME Use interface IFNAME.\n" " If not provided it uses 'eth0'.\n" " -m, --master MACADDR Master mode (no negotiation, only defend),\n" " based on MAC address passed as parameter\n" " -r, --randseed Seeds RNG with random # rather than " "MAC address.\n" " -s, --syslog Output to syslog instead of stdout.\n" " -v, --version Print the version information, and exit.\n\n" "This program does the ad-hoc link-local IPv4 auto-configuration trick, as\n" "described in the IETF Internet Draft 'Dynamic Configuration of IPv4 " "Link-Local Addresses'.\n"); exit (exit_code); } int main(int argc, char *argv[]) { int r, rand_seed = 0, master_mode = 0; char c; char ebuf[LIBNET_ERRBUF_SIZE]; struct option longopts[] = { {"help", no_argument, NULL, 'h' }, {"version", no_argument, NULL, 'v' }, {"interface",required_argument, NULL, 'i' }, {"master",required_argument, NULL, 'm' }, {"randseed",no_argument, NULL, 'r'}, {"syslog", no_argument, NULL, 's' }, {0,0,0,0}, }; // static allocation of the device, just for now struct zc_if interface0; interface0.if_name = "eth0"; while ((c = getopt_long(argc, argv, "rshi:m:v",longopts, NULL)) != EOF) { switch (c) { case 'h': usage(0); case 'i': interface0.if_name = optarg; break; case 'm': interface0.my_ea = (struct libnet_ether_addr *)strtomac (optarg); master_mode = 1; break; case 'r': rand_seed = 1; break; case 's': output_to_syslog = 1; open_log(); break; case 'v': version(); default: print("unknown argument: %s\n", optarg); usage(1); } } #ifdef CHECK_KERNEL_SOCKETFILTERS // insist on kernel support for socket filters kernel_socketfilter(); #endif // Workaround: // Since it is not possible with libnet v1.1.1 to initialize a context // with an interface that is not up and/or has no valid IP address, we // use this temporary (random 169.254.x.y) address to initialize the // libnet context: interface0.ip.s_addr = htonl ((169 << 24) + (254 << 16) + (time (NULL) & 0xFFFF)); ifup(&interface0); // initialize libnet link-level access to the network interface interface0.l = libnet_init (LIBNET_LINK, interface0.if_name, ebuf); if (interface0.l == NULL) die("libnet_init(\"%s\"): %s\n", interface0.if_name, ebuf); // bring the interface up to 0.0.0.0 interface0.ip.s_addr = htonl(0); ifup(&interface0); if (!master_mode) { // get the Ethernet address of this interface // this will be used to seed the PRNG, and as the source MAC in the ARP probes interface0.my_ea = libnet_get_hwaddr(interface0.l); if (!(interface0.my_ea)) die("libnet_get_hwaddr(\"%s\"): %s\n", interface0.if_name, ebuf); } print( "interface: %s (%02X:%02X:%02X:%02X:%02X:%02X)\n", interface0.if_name, interface0.my_ea->ether_addr_octet[0], interface0.my_ea->ether_addr_octet[1], interface0.my_ea->ether_addr_octet[2], interface0.my_ea->ether_addr_octet[3], interface0.my_ea->ether_addr_octet[4], interface0.my_ea->ether_addr_octet[5] ); seed_rng(rand_seed ? NULL : interface0.my_ea); if (optind < argc) { r = inet_aton(argv[optind], &(interface0.ip)); if (r == 0) die("error parsing address: %s\n", argv[optind]); } PICK: pick_random_address(&(interface0.ip)); // Note: In master mode, we don't negotiate! // We simply claim and defend a given address. if (!master_mode) { if (probe_for_address(&interface0) == 0) { print("address in use\n"); goto PICK; } } // bring the interface up to the selected address ifup(&interface0); // initialize pcap to listen for address collisions start_listening_for_collisions(&interface0); // use if_name as the default route //set_default_route(&interface0); if (!master_mode) { if (claim_address(&interface0) != 0) { ifdown(&interface0); stop_listening_for_collisions(&interface0); goto PICK; } } else print("IP address set: %s\n", inet_ntoa(interface0.ip)); // fork and exit, to send the application to the background background(); wait_for_collision(&interface0); // if we get here, a collision has been detected and defense failed, // so we need to reconfigure the IP setup ifdown(&interface0); stop_listening_for_collisions(&interface0); // bring the interface up to 0.0.0.0 interface0.ip.s_addr = htonl(0); ifup(&interface0); goto PICK; } --------------040707080305070400030207-- ------------------------------------------------------- This SF.Net email is sponsored by: NEC IT Guy Games. Get your fingers limbered up and give it your best shot. 4 great events, 4 opportunities to win big! Highest score wins.NEC IT Guy Games. Play to win an NEC 61 plasma display. Visit http://www.necitguy.com/?r=20