Better link-local code
- From: "Tom Parker" <palfrey tevp net>
- To: NetworkManager-list gnome org
- Cc:
- Subject: Better link-local code
- Date: Sat, 04 Dec 2004 18:01:33 +0100
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]