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