diff --git a/ndhc/arp.c b/ndhc/arp.c index 44a1c65..bcc4d9e 100644 --- a/ndhc/arp.c +++ b/ndhc/arp.c @@ -91,6 +91,20 @@ void arp_check(struct client_state_t *cs, struct dhcpMessage *packet) arpreply_offset = 0; } +void arp_gw_check(struct client_state_t *cs) +{ + cs->arpPrevState = cs->dhcpState; + cs->dhcpState = DS_ARP_GW_CHECK; + memset(&arp_dhcp_packet, 0, sizeof (struct dhcpMessage)); + cs->arpFd = arpping(cs->routerAddr, NULL, 0, + client_config.arp, client_config.interface); + epoll_add(cs, cs->arpFd); + cs->oldTimeout = cs->timeout; + cs->timeout = 2000; + memset(&arpreply, 0, sizeof arpreply); + arpreply_offset = 0; +} + static void arp_failed(struct client_state_t *cs) { log_line("Offered address is in use: declining."); @@ -107,6 +121,22 @@ static void arp_failed(struct client_state_t *cs) change_listen_mode(cs, LM_RAW); } +void arp_gw_failed(struct client_state_t *cs) +{ + log_line("arp: gateway appears to have changed, getting new lease"); + epoll_del(cs, cs->arpFd); + cs->arpFd = -1; + + // Same as packet.c: line 258 + ifchange(NULL, IFCHANGE_DECONFIG); + cs->dhcpState = DS_INIT_SELECTING; + cs->oldTimeout = 0; + cs->timeout = 0; + cs->requestedIP = 0; + cs->packetNum = 0; + change_listen_mode(cs, LM_RAW); +} + void arp_success(struct client_state_t *cs) { struct in_addr temp_addr; @@ -138,6 +168,15 @@ void arp_success(struct client_state_t *cs) background(cs); } +void arp_gw_success(struct client_state_t *cs) +{ + log_line("arp: gateway seems unchanged"); + epoll_del(cs, cs->arpFd); + cs->arpFd = -1; + cs->timeout = cs->oldTimeout; + cs->dhcpState = cs->arpPrevState; +} + typedef uint32_t aliased_uint32_t __attribute__((__may_alias__)); void handle_arp_response(struct client_state_t *cs) { @@ -145,7 +184,11 @@ void handle_arp_response(struct client_state_t *cs) int r = safe_read(cs->arpFd, (char *)&arpreply + arpreply_offset, sizeof arpreply - arpreply_offset); if (r < 0) { - arp_failed(cs); + // Conservative responses: assume failure. + if (cs->dhcpState == DS_ARP_CHECK) + arp_failed(cs); + else + arp_gw_failed(cs); return; } else arpreply_offset += r; @@ -156,21 +199,36 @@ void handle_arp_response(struct client_state_t *cs) //arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]); if (arpreply_offset >= ARP_MSG_SIZE) { - if (arpreply.operation == htons(ARPOP_REPLY) - /* don't check: Linux returns invalid tHaddr (fixed in 2.6.24?) */ - /* && memcmp(arpreply.tHaddr, from_mac, 6) == 0 */ - && *(aliased_uint32_t*)arpreply.sInaddr == arp_dhcp_packet.yiaddr) - { - /* if ARP source MAC matches safe_mac - * (which is client's MAC), then it's not a conflict - * (client simply already has this IP and replies to ARPs!) - */ - /* if (memcmp(safe_mac, arpreply.sHaddr, 6) == 0) */ - /* arp_success(); */ - arp_failed(cs); + if (cs->dhcpState == DS_ARP_CHECK) { + if (arpreply.operation == htons(ARPOP_REPLY) + // don't check: Linux returns invalid tHaddr (fixed in 2.6.24?) + // && !memcmp(arpreply.tHaddr, from_mac, 6) + && *(aliased_uint32_t*)arpreply.sInaddr + == arp_dhcp_packet.yiaddr) + { + // Check to see if we replied to our own ARP query. + if (!memcmp(client_config.arp, arpreply.sHaddr, 6)) + arp_success(cs); + else + arp_failed(cs); + } else { + memset(&arpreply, 0, sizeof arpreply); + arpreply_offset = 0; + } + return; } else { - memset(&arpreply, 0, sizeof arpreply); - arpreply_offset = 0; + if (arpreply.operation == htons(ARPOP_REPLY) + // don't check: Linux returns invalid tHaddr (fixed in 2.6.24?) + // && !memcmp(arpreply.tHaddr, from_mac, 6) + && *(aliased_uint32_t*)arpreply.sInaddr == cs->routerAddr) + { + // XXX: would be nice to check arp gateway mac, too + arp_gw_success(cs); + } else { + memset(&arpreply, 0, sizeof arpreply); + arpreply_offset = 0; + } + return; } } } diff --git a/ndhc/arp.h b/ndhc/arp.h index 7620b79..b52f101 100644 --- a/ndhc/arp.h +++ b/ndhc/arp.h @@ -33,5 +33,7 @@ enum { void arp_check(struct client_state_t *cs, struct dhcpMessage *packet); void arp_success(struct client_state_t *cs); void handle_arp_response(struct client_state_t *cs); +void arp_gw_check(struct client_state_t *cs); +void arp_gw_failed(struct client_state_t *cs); #endif /* ARPPING_H_ */ diff --git a/ndhc/config.h b/ndhc/config.h index e15391f..64ec851 100644 --- a/ndhc/config.h +++ b/ndhc/config.h @@ -13,6 +13,7 @@ enum { DS_BOUND, DS_RENEWING, DS_REBINDING, + DS_ARP_GW_CHECK, DS_ARP_CHECK, DS_RENEW_REQUESTED, DS_RELEASED @@ -40,8 +41,8 @@ struct client_state_t { int listenMode; int packetNum; int epollFd, signalFd, listenFd, arpFd, nlFd; - int timeout; - uint32_t requestedIP, serverAddr; + int timeout, oldTimeout; + uint32_t requestedIP, serverAddr, routerAddr; uint32_t lease, t1, t2, xid; }; diff --git a/ndhc/ifchange.c b/ndhc/ifchange.c index f4f4ede..f5815da 100644 --- a/ndhc/ifchange.c +++ b/ndhc/ifchange.c @@ -1,5 +1,5 @@ /* ifchange.c - functions to call the interface change daemon - * Time-stamp: <2011-03-30 16:37:33 nk> + * Time-stamp: <2011-03-30 19:23:54 nk> * * (c) 2004-2011 Nicholas J. Kain * @@ -38,6 +38,9 @@ #include "io.h" #include "ifchange.h" +// For access to routerAddr and nothing else. +extern struct client_state_t cs; + /* Fill buf with the ifchd command text of option 'option'. */ /* Returns 0 if successful, -1 if nothing was filled in. */ static int ifchd_cmd(char *buf, size_t buflen, uint8_t *option, ssize_t optlen, @@ -81,6 +84,12 @@ static int ifchd_cmd(char *buf, size_t buflen, uint8_t *option, ssize_t optlen, for(;;) { switch (type) { case OPTION_IP: { + // This is a bit of a layering violation, but it's necessary + // for verifying gateway existence by ARP when link returns. + if (code == DHCP_ROUTER) { + log_line("copied gateway address to cs->routerAddr"); + memcpy(&cs.routerAddr, option, 4); + } if (inet_ntop(AF_INET, option, buf, buflen - (buf - obuf) - 1)) buf += strlen(buf); break; diff --git a/ndhc/ndhc.c b/ndhc/ndhc.c index 88fa1b2..7f42c5c 100644 --- a/ndhc/ndhc.c +++ b/ndhc/ndhc.c @@ -70,9 +70,11 @@ struct client_state_t cs = { .packetNum = 0, .xid = 0, .timeout = 0, + .oldTimeout = 0, .leaseStartTime = 0, .requestedIP = 0, .serverAddr = 0, + .routerAddr = 0, .lease = 0, .t1 = 0, .t2 = 0, diff --git a/ndhc/netlink.c b/ndhc/netlink.c index fa9346f..8a4b8c4 100644 --- a/ndhc/netlink.c +++ b/ndhc/netlink.c @@ -25,6 +25,7 @@ #include "netlink.h" #include "ifchange.h" +#include "arp.h" #include "log.h" #define NLMSG_RECVSIZE 8192 @@ -95,6 +96,18 @@ void nl_queryifstatus(int ifidx, struct client_state_t *cs) send(cs->nlFd, &req, sizeof req, 0); } +static void takedown_if(struct client_state_t *cs) +{ + log_line("nl: taking down interface"); + // Same as packet.c: line 258 + ifchange(NULL, IFCHANGE_DECONFIG); + cs->dhcpState = DS_INIT_SELECTING; + cs->timeout = 0; + cs->requestedIP = 0; + cs->packetNum = 0; + change_listen_mode(cs, LM_RAW); +} + // Decode netlink messages and process them static void nl_handlemsg(struct nlmsghdr *msg, unsigned int len, struct client_state_t *cs) @@ -130,24 +143,14 @@ static void nl_handlemsg(struct nlmsghdr *msg, unsigned int len, * If we don't have a lease, state -> INIT. */ if (cs->dhcpState == DS_BOUND) { - /* arp_check_gw(cs); */ - } else if (cs->dhcpState != DS_INIT_SELECTING) { - log_line("nl: taking down interface"); - // Same as packet.c: line 258 - ifchange(NULL, IFCHANGE_DECONFIG); - cs->dhcpState = DS_INIT_SELECTING; - cs->timeout = 0; - cs->requestedIP = 0; - cs->packetNum = 0; - change_listen_mode(cs, LM_RAW); - } + arp_gw_check(cs); + } else if (cs->dhcpState != DS_INIT_SELECTING) + takedown_if(cs); } } else { if (cs->ifsPrevState != IFS_DOWN) { cs->ifsPrevState = IFS_DOWN; - /* - * state -> DOWN - */ + takedown_if(cs); } } } else { diff --git a/ndhc/packet.h b/ndhc/packet.h index e7b4360..2913044 100644 --- a/ndhc/packet.h +++ b/ndhc/packet.h @@ -45,11 +45,6 @@ enum { DHCP_SIZE = sizeof(struct dhcpMessage), }; -/* Let's see whether compiler understood us right */ -struct BUG_bad_sizeof_struct_ip_udp_dhcp_packet { - char c[IP_UPD_DHCP_SIZE == 576 ? 1 : -1]; -}; - int get_packet(struct dhcpMessage *packet, int fd); uint16_t checksum(void *addr, int count); int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, diff --git a/ndhc/timeout.c b/ndhc/timeout.c index 9d2f2cb..a2c31d3 100644 --- a/ndhc/timeout.c +++ b/ndhc/timeout.c @@ -19,7 +19,7 @@ static void init_selecting_timeout(struct client_state_t *cs) /* broadcast */ send_discover(cs->xid, cs->requestedIP); - cs->timeout = DELAY_SEC * (cs->packetNum + 1); + cs->timeout = DELAY_SEC * (cs->packetNum + 1) * 1000; cs->packetNum++; } else { if (client_config.background_if_no_lease) { @@ -127,6 +127,7 @@ void handle_timeout(struct client_state_t *cs) case DS_REBINDING: rebinding_timeout(cs); break; case DS_RELEASED: cs->timeout = -1; break; case DS_ARP_CHECK: arp_success(cs); break; + case DS_ARP_GW_CHECK: arp_gw_failed(cs); break; default: break; } }