Better link-local code



Remember the link-local code I posted before? Scrap it, I've got a
replacement.  It's based on the Zeroconf example code
(http://www.zeroconf.org/AVH-IPv4LL.c), with a whole bunch of
modifications. The autoip.c attached to this mail should be dumped into
the dhcpcd/ folder, and then the patch applied, and this will give
NetworkManager full auto-ip support, with no additional external
dependancies. This WFM, but may do nasty things. Testers wanted.

(This patch is dedicated to the Globecom conference I've just attended,
as thanks to them I had a 9 1/2 hour trans-atlantic flight to work on
this patch. If that's not far away from a DHCP server, I don't know
what is).

Tom
Index: dhcpcd/Makefile.am
===================================================================
RCS file: /cvs/gnome/NetworkManager/dhcpcd/Makefile.am,v
retrieving revision 1.1
diff -u -r1.1 Makefile.am
--- dhcpcd/Makefile.am	16 Nov 2004 02:41:52 -0000	1.1
+++ dhcpcd/Makefile.am	29 Nov 2004 23:10:50 -0000
@@ -3,7 +3,9 @@
 AM_CPPFLAGS =						\
 	$(NM_CFLAGS)					\
 	-DBINDIR=\"$(bindir)\"			\
-	-DDATADIR=\"$(datadir)\"
+	-DDATADIR=\"$(datadir)\" \
+	-DARP_DEBUG \
+	-DDEBUG
 
 noinst_LIBRARIES = libdhcpc.a
 
@@ -17,7 +19,8 @@
 	client.c		\
 	client.h		\
 	dhcpcd.c		\
-	dhcpcd.h
+	dhcpcd.h		\
+	autoip.c
 
 bin_PROGRAMS = dhcp_test
 dhcp_test_SOURCES = dhcp_test.c
Index: dhcpcd/arp.c
===================================================================
RCS file: /cvs/gnome/NetworkManager/dhcpcd/arp.c,v
retrieving revision 1.1
diff -u -r1.1 arp.c
--- dhcpcd/arp.c	16 Nov 2004 02:41:52 -0000	1.1
+++ dhcpcd/arp.c	29 Nov 2004 23:10:50 -0000
@@ -25,24 +25,8 @@
 #include <net/if_arp.h>
 #include <string.h>
 #include <syslog.h>
-#include "client.h"
 #include "arp.h"
 
-typedef struct arpMessage
-{
-  struct packed_ether_header	ethhdr;
-  u_short htype;	/* hardware type (must be ARPHRD_ETHER) */
-  u_short ptype;	/* protocol type (must be ETHERTYPE_IP) */
-  u_char  hlen;		/* hardware address length (must be 6) */
-  u_char  plen;		/* protocol address length (must be 4) */
-  u_short operation;	/* ARP opcode */
-  u_char  sHaddr[ETH_ALEN];	/* sender's hardware address */
-  u_char  sInaddr[4];	/* sender's IP address */
-  u_char  tHaddr[ETH_ALEN];	/* target's hardware address */
-  u_char  tInaddr[4];	/* target's IP address */
-  u_char  pad[18];	/* pad for min. Ethernet payload (60 bytes) */
-} __attribute__((packed)) arpMessage;
-
 #define BasicArpLen(A) (sizeof(A) - (sizeof(A.ethhdr) + sizeof(A.pad)))
 
 extern	int		DebugFlag;
Index: dhcpcd/arp.h
===================================================================
RCS file: /cvs/gnome/NetworkManager/dhcpcd/arp.h,v
retrieving revision 1.1
diff -u -r1.1 arp.h
--- dhcpcd/arp.h	16 Nov 2004 02:41:52 -0000	1.1
+++ dhcpcd/arp.h	29 Nov 2004 23:10:50 -0000
@@ -23,6 +23,23 @@
 #ifndef ARP_H
 #define ARP_H
 
+#include "client.h"
+
+typedef struct arpMessage
+{
+  struct packed_ether_header	ethhdr;
+  u_short htype;	/* hardware type (must be ARPHRD_ETHER) */
+  u_short ptype;	/* protocol type (must be ETHERTYPE_IP) */
+  u_char  hlen;		/* hardware address length (must be 6) */
+  u_char  plen;		/* protocol address length (must be 4) */
+  u_short operation;	/* ARP opcode */
+  u_char  sHaddr[ETH_ALEN];	/* sender's hardware address */
+  u_char  sInaddr[4];	/* sender's IP address */
+  u_char  tHaddr[ETH_ALEN];	/* target's hardware address */
+  u_char  tInaddr[4];	/* target's IP address */
+  u_char  pad[18];	/* pad for min. Ethernet payload (60 bytes) */
+} __attribute__((packed)) arpMessage;
+
 #ifdef ARPCHECK
 int arpCheck(const dhcp_interface *iface);
 #endif
Index: dhcpcd/client.c
===================================================================
RCS file: /cvs/gnome/NetworkManager/dhcpcd/client.c,v
retrieving revision 1.2
diff -u -r1.2 client.c
--- dhcpcd/client.c	22 Nov 2004 14:42:33 -0000	1.2
+++ dhcpcd/client.c	29 Nov 2004 23:10:52 -0000
@@ -44,8 +44,11 @@
 #include "udpipgen.h"
 #include "arp.h"
 
+#ifdef DEBUG
 int DebugFlag = 1;
-#define DEBUG
+#else
+int DebugFlag = 0;
+#endif
 
 void debug_dump_dhcp_options (udpipMessage *udp_msg, dhcpOptions *options);
 
@@ -771,7 +774,13 @@
 	iface->xid = random ();
 	err = dhcpSendAndRecv (iface, DHCP_OFFER, &buildDhcpDiscover, &msg);
 	if (err != RET_DHCP_SUCCESS)
+	{
+		if (iface->client_options->do_linklocal)
+		{
+			err = get_autoip(iface);
+		}
 		return err;
+	}
 
 	dhcp_msg = (dhcpMessage *)&(msg->udpipmsg[sizeof(udpiphdr)]);
 	if ( iface->client_options->send_second_discover )
@@ -1085,6 +1094,8 @@
 	return RET_DHCP_SUCCESS;
 }
 
+#ifdef DEBUG
+
 /*****************************************************************************/
 char *get_dhcp_option_name (int i)
 {
@@ -1183,4 +1194,5 @@
 				udp_msg->ethhdr.ether_shost[2], udp_msg->ethhdr.ether_shost[3],
 				udp_msg->ethhdr.ether_shost[4], udp_msg->ethhdr.ether_shost[5]);
 }
+#endif // DEBUG
 
Index: dhcpcd/client.h
===================================================================
RCS file: /cvs/gnome/NetworkManager/dhcpcd/client.h,v
retrieving revision 1.2
diff -u -r1.2 client.h
--- dhcpcd/client.h	22 Nov 2004 14:42:33 -0000	1.2
+++ dhcpcd/client.h	29 Nov 2004 23:10:52 -0000
@@ -215,4 +215,7 @@
 #endif
 int dhcp_inform(dhcp_interface *iface);
 
+// from autoip.c
+int get_autoip(struct dhcp_interface *iface);
+
 #endif
Index: dhcpcd/dhcp_test.c
===================================================================
RCS file: /cvs/gnome/NetworkManager/dhcpcd/dhcp_test.c,v
retrieving revision 1.1
diff -u -r1.1 dhcp_test.c
--- dhcpcd/dhcp_test.c	16 Nov 2004 02:41:52 -0000	1.1
+++ dhcpcd/dhcp_test.c	29 Nov 2004 23:10:52 -0000
@@ -2,11 +2,14 @@
 #include "client.h"
 #include <syslog.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 int main (int argc, char **argv)
 {
 	dhcp_client_options	opts;
 	dhcp_interface	*iface;
+	int ret;
 
 	if (argc < 2 || argv[1] == NULL)
 	{
@@ -16,13 +19,16 @@
 
 	memset (&opts, 0, sizeof (dhcp_client_options));
 	opts.base_timeout = 5;
+	opts.do_linklocal = 1; // test link-local
 
 	openlog ("dhcp_test", LOG_CONS | LOG_PERROR, LOG_USER);
 
 	if (!(iface = dhcp_interface_init (argv[1], &opts)))
 		exit (1);
 
-	dhcp_init (iface);
+	ret = dhcp_init (iface);
+
+	printf("dhcp_init returned %d\n",ret);
 
 	dhcp_interface_free (iface);
 
Index: dhcpcd/dhcpcd.h
===================================================================
RCS file: /cvs/gnome/NetworkManager/dhcpcd/dhcpcd.h,v
retrieving revision 1.2
diff -u -r1.2 dhcpcd.h
--- dhcpcd/dhcpcd.h	22 Nov 2004 14:42:33 -0000	1.2
+++ dhcpcd/dhcpcd.h	29 Nov 2004 23:10:52 -0000
@@ -119,6 +119,7 @@
 	time_t		base_timeout;
 	int			do_checksum;
 	int			send_second_discover;
+	int 		do_linklocal;
 	int			window;
 } dhcp_client_options;
 
Index: src/NetworkManagerDHCP.c
===================================================================
RCS file: /cvs/gnome/NetworkManager/src/NetworkManagerDHCP.c,v
retrieving revision 1.3
diff -u -r1.3 NetworkManagerDHCP.c
--- src/NetworkManagerDHCP.c	22 Nov 2004 14:42:34 -0000	1.3
+++ src/NetworkManagerDHCP.c	29 Nov 2004 23:10:53 -0000
@@ -98,6 +98,7 @@
 
 	memset (&opts, 0, sizeof (dhcp_client_options));
 	opts.base_timeout = 30;
+	opts.do_linklocal = 1;
 	if (!(dev->dhcp_iface = dhcp_interface_init (nm_device_get_iface (dev), &opts)))
 		return RET_DHCP_ERROR;

// Based upon http://www.zeroconf.org/AVH-IPv4LL.c
// Merged into NetworkManager by Tom Parker <palfrey tevp net>
// Original copyright continues below
// 
// ----------------------------------
// Simple IPv4 Link-Local addressing (see <http://www.zeroconf.org/>)
// @(#)llip.c, 1.5, Copyright 2003 by Arthur van Hoff (avh strangeberry com)
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// See <http://www.gnu.org/copyleft/lesser.html>
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#include <netinet/ether.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <syslog.h>
#include "arp.h"
#include "dhcpcd.h"

/* originally included, but now compiles without....

#include <sys/types.h>
#include <sys/wait.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <linux/if_packet.h>

 */

// Times here are in ms
#define LINKLOCAL_ADDR          0xa9fe0000
#define NPROBES                 3
#define PROBE_INTERVAL          200
#define NCLAIMS                 3
#define CLAIM_INTERVAL          200
#define FAILURE_INTERVAL        14000

// Times here are in seconds
#define ARP_DEFAULT_LEASETIME	100

static char *prog;
#ifdef ARP_DEBUG
static int verbose = 1;
#endif

static struct in_addr null_ip = {0};
static struct ether_addr null_addr = {{0, 0, 0, 0, 0, 0}};
static struct ether_addr broadcast_addr = {{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};

void buildFakeDHCPOptions(dhcp_interface *iface, const struct in_addr *ip, const struct ifreq *ifr);

#ifdef ARP_DEBUG
/**
 * Convert an ethernet address to a printable string.
 */
static char *ether2str(u_char *addr)
{
    return addr;
}
#endif

/**
 * Pick a random link local IP address.
 */
static void pick(struct in_addr *ip)
{
    ip->s_addr = htonl(LINKLOCAL_ADDR | ((abs(random()) % 0xFD00) + 0x0100));
}

/**
 * Send out an ARP packet.
 */
static void arp(int fd, struct sockaddr *saddr, int op,
                struct ether_addr *source_addr, struct in_addr source_ip,
                struct ether_addr *target_addr, struct in_addr target_ip)
{
    struct arpMessage p;
    memset(&p, 0, sizeof(p));

    // ether header
    p.ethhdr.ether_type = htons(ETHERTYPE_ARP);
    memcpy(p.ethhdr.ether_shost, source_addr, ETH_ALEN);
    memcpy(p.ethhdr.ether_dhost, &broadcast_addr, ETH_ALEN);

    // arp request
    p.htype = htons(ARPHRD_ETHER);
    p.ptype = htons(ETHERTYPE_IP);
    p.hlen = ETH_ALEN;
    p.plen = 4;
    p.operation = htons(op);
    memcpy(&p.sHaddr, source_addr, ETH_ALEN);
    memcpy(&p.sInaddr, &source_ip, sizeof(p.sInaddr));
    memcpy(&p.tHaddr, target_addr, ETH_ALEN);
    memcpy(&p.tInaddr, &target_ip, sizeof(p.tInaddr));

    // send it
    if (sendto(fd, &p, sizeof(p), 0, saddr, sizeof(*saddr)) < 0) {
        perror("sendto failed");
        exit(1);
    }
}

int get_autoip(struct dhcp_interface *iface)
{
    char *intf = iface->iface;
    struct sockaddr saddr;
    struct pollfd fds[1];
    arpMessage p;
    struct ifreq ifr;
    struct ether_addr addr;
    struct timeval tv;
    struct in_addr ip = {0};
    int fd;
    int timeout = 0;
    int nprobes = 0;
    int nclaims = 0;
    int failby = 0;

    // init
    gettimeofday(&tv, NULL);
    failby = time(0) + FAILURE_INTERVAL / 1000;
    prog = "NetworkManagerDHCP";

    // initialize saddr
    memset(&saddr, 0, sizeof(saddr));
    strncpy(saddr.sa_data, intf, sizeof(saddr.sa_data));

    // open an ARP socket
    if ((fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) < 0) {
        perror("open failed");
        exit(1);
    }

    // bind to the ARP socket
    if (bind(fd, &saddr, sizeof(saddr)) < 0) {
        perror("bind failed");
        exit(1);
    }

    // get the ethernet address of the interface
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, intf, sizeof(ifr.ifr_name));
    if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
        perror("ioctl failed");
        exit(1);
    }
    memcpy(&addr, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);

    // initialize pseudo random selection of IP addresses
    srandom((addr.ether_addr_octet[ETHER_ADDR_LEN-4] << 24) |
            (addr.ether_addr_octet[ETHER_ADDR_LEN-3] << 16) |
            (addr.ether_addr_octet[ETHER_ADDR_LEN-2] <<  8) |
            (addr.ether_addr_octet[ETHER_ADDR_LEN-1] <<  0));
    
    // pick an ip address
    if (ip.s_addr == 0) {
        pick(&ip);
    }

    // prepare for polling
    fds[0].fd = fd;
    fds[0].events = POLLIN | POLLERR;

    while (1) {
#ifdef ARP_DEBUG
        if (verbose) {
            printf("%s %s: polling %d, nprobes=%d, nclaims=%d\n", prog, intf, timeout, nprobes, nclaims);
        }
#endif
        fds[0].revents = 0;
        switch (poll(fds, 1, timeout)) {
            case 0:
                // timeout
                if ((failby != 0) && (failby < time(0))) {
                    syslog(LOG_ERR, "%s: failed to obtain link-local address\n", prog);
					return RET_DHCP_ERROR;
                }
                if (nprobes < NPROBES) {
                    // ARP probe
#ifdef ARP_DEBUG
                    if (verbose) {
                        syslog(LOG_ERR, "%s: ARP probe %s\n", intf, inet_ntoa(ip));
                    }
#endif
                    arp(fd, &saddr, ARPOP_REQUEST, &addr, null_ip, &null_addr, ip);
                    nprobes++;
                    timeout = PROBE_INTERVAL;
                } else if (nclaims < NCLAIMS) {
                    // ARP claim
#ifdef ARP_DEBUG
                    if (verbose) {
                        syslog(LOG_ERR, "%s: ARP claim %s\n", intf, inet_ntoa(ip));
                    }
#endif
                    arp(fd, &saddr, ARPOP_REQUEST, &addr, ip, &addr, ip);
                    nclaims++;
                    timeout = CLAIM_INTERVAL;
                } else {
#ifdef ARP_DEBUG
                    // ARP take
                    if (verbose) {
                        syslog(LOG_ERR, "%s: use %s\n", intf, inet_ntoa(ip));
                    }
#endif
					buildFakeDHCPOptions(iface,&ip,&ifr);
					return RET_DHCP_BOUND;
                }
                break;

            case 1:
                // i/o event
                if ((fds[0].revents & POLLIN) == 0) {
                    if (fds[0].revents & POLLERR) {
                        syslog(LOG_ERR, "%s: I/O error\n", intf);
						return RET_DHCP_ERROR;
                    }
                    continue;
                }

                // read ARP packet
                if (recv(fd, &p, sizeof(p), 0) < 0) {
                    syslog(LOG_ERR, "recv failed");
					return RET_DHCP_ERROR;
                }
#ifdef ARP_DEBUG
                if (verbose) {
                    syslog(LOG_ERR, "%s: recv arp type=%d, op=%d, ", intf, ntohs(p.ethhdr.ether_type), ntohs(p.operation));
                    syslog(LOG_ERR, "source=%s %s,", ether2str(p.sHaddr), p.sInaddr);
                    syslog(LOG_ERR, "target=%s %s\n", ether2str(p.tHaddr), p.tInaddr);
                }
#endif
                if ((ntohs(p.ethhdr.ether_type) == ETHERTYPE_ARP) && (ntohs(p.operation) == ARPOP_REPLY) &&
                    ((uint32_t)(*p.tInaddr) == ip.s_addr) && (memcmp(&addr, &p.tHaddr, ETH_ALEN) != 0)) {

#ifdef ARP_DEBUG
                    if (verbose) {
                        syslog(LOG_ERR, "%s: ARP conflict %s\n", intf, inet_ntoa(ip));
                    }
#endif
                    pick(&ip);
                    timeout = 0;
                    nprobes = 0;
                    nclaims = 0;
                }
                break;

            default:
                syslog(LOG_ERR, "poll failed");
				return RET_DHCP_ERROR;
        }
    }
}

void buildFakeDHCPOptions(dhcp_interface *iface, const struct in_addr *ip, const struct ifreq *ifr)
{
	dhcpOptions *options = &iface->dhcp_options;
	
	/* setup a linklocal leasetime - something small! */
	unsigned int	lease_time = htonl (ARP_DEFAULT_LEASETIME);
	options->val[dhcpIPaddrLeaseTime] = malloc(4);
	memcpy (options->val[dhcpIPaddrLeaseTime], &lease_time, 4);
	options->len[dhcpIPaddrLeaseTime] = 4;
	options->num++;
	
	/* set the server to be ourselves, as linklocal doesn't have a server */	
	options->val[dhcpServerIdentifier] = malloc(4);
	memcpy (options->val[dhcpServerIdentifier], &ip->s_addr, 4);
	options->len[dhcpServerIdentifier] = 4;
	options->num++;

	memcpy(&(iface->ciaddr),&ip->s_addr,4); /* store our ip */
	memcpy(&(iface->siaddr), options->val[dhcpServerIdentifier], 4);
    memcpy(&(iface->chaddr), &ifr->ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
    memcpy(&(iface->shaddr), &ifr->ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]