If physical link state changes to UP and a lease is bound, check to see if

the currently assigned gateway/router still replies to ARP queries.  If so,
keep the lease.  If not, get a new lease.

Save the IP address of the current gateway/router.
Remove an outdated check for a buggy compiler.
Fix a typo in the previous commit that caused timeouts to be 1000x faster than
they should be.
This commit is contained in:
Nicholas J. Kain
2011-03-30 20:13:48 -04:00
parent de23d2241d
commit 03717e1593
8 changed files with 109 additions and 38 deletions

View File

@ -91,6 +91,20 @@ void arp_check(struct client_state_t *cs, struct dhcpMessage *packet)
arpreply_offset = 0; 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) static void arp_failed(struct client_state_t *cs)
{ {
log_line("Offered address is in use: declining."); 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); 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) void arp_success(struct client_state_t *cs)
{ {
struct in_addr temp_addr; struct in_addr temp_addr;
@ -138,6 +168,15 @@ void arp_success(struct client_state_t *cs)
background(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__)); typedef uint32_t aliased_uint32_t __attribute__((__may_alias__));
void handle_arp_response(struct client_state_t *cs) 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, int r = safe_read(cs->arpFd, (char *)&arpreply + arpreply_offset,
sizeof arpreply - arpreply_offset); sizeof arpreply - arpreply_offset);
if (r < 0) { 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; return;
} else } else
arpreply_offset += r; 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]); //arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]);
if (arpreply_offset >= ARP_MSG_SIZE) { if (arpreply_offset >= ARP_MSG_SIZE) {
if (arpreply.operation == htons(ARPOP_REPLY) if (cs->dhcpState == DS_ARP_CHECK) {
/* don't check: Linux returns invalid tHaddr (fixed in 2.6.24?) */ if (arpreply.operation == htons(ARPOP_REPLY)
/* && memcmp(arpreply.tHaddr, from_mac, 6) == 0 */ // don't check: Linux returns invalid tHaddr (fixed in 2.6.24?)
&& *(aliased_uint32_t*)arpreply.sInaddr == arp_dhcp_packet.yiaddr) // && !memcmp(arpreply.tHaddr, from_mac, 6)
{ && *(aliased_uint32_t*)arpreply.sInaddr
/* if ARP source MAC matches safe_mac == arp_dhcp_packet.yiaddr)
* (which is client's MAC), then it's not a conflict {
* (client simply already has this IP and replies to ARPs!) // Check to see if we replied to our own ARP query.
*/ if (!memcmp(client_config.arp, arpreply.sHaddr, 6))
/* if (memcmp(safe_mac, arpreply.sHaddr, 6) == 0) */ arp_success(cs);
/* arp_success(); */ else
arp_failed(cs); arp_failed(cs);
} else {
memset(&arpreply, 0, sizeof arpreply);
arpreply_offset = 0;
}
return;
} else { } else {
memset(&arpreply, 0, sizeof arpreply); if (arpreply.operation == htons(ARPOP_REPLY)
arpreply_offset = 0; // 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;
} }
} }
} }

View File

@ -33,5 +33,7 @@ enum {
void arp_check(struct client_state_t *cs, struct dhcpMessage *packet); void arp_check(struct client_state_t *cs, struct dhcpMessage *packet);
void arp_success(struct client_state_t *cs); void arp_success(struct client_state_t *cs);
void handle_arp_response(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_ */ #endif /* ARPPING_H_ */

View File

@ -13,6 +13,7 @@ enum {
DS_BOUND, DS_BOUND,
DS_RENEWING, DS_RENEWING,
DS_REBINDING, DS_REBINDING,
DS_ARP_GW_CHECK,
DS_ARP_CHECK, DS_ARP_CHECK,
DS_RENEW_REQUESTED, DS_RENEW_REQUESTED,
DS_RELEASED DS_RELEASED
@ -40,8 +41,8 @@ struct client_state_t {
int listenMode; int listenMode;
int packetNum; int packetNum;
int epollFd, signalFd, listenFd, arpFd, nlFd; int epollFd, signalFd, listenFd, arpFd, nlFd;
int timeout; int timeout, oldTimeout;
uint32_t requestedIP, serverAddr; uint32_t requestedIP, serverAddr, routerAddr;
uint32_t lease, t1, t2, xid; uint32_t lease, t1, t2, xid;
}; };

View File

@ -1,5 +1,5 @@
/* ifchange.c - functions to call the interface change daemon /* 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 <njkain at gmail dot com> * (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
* *
@ -38,6 +38,9 @@
#include "io.h" #include "io.h"
#include "ifchange.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'. */ /* Fill buf with the ifchd command text of option 'option'. */
/* Returns 0 if successful, -1 if nothing was filled in. */ /* 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, 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(;;) { for(;;) {
switch (type) { switch (type) {
case OPTION_IP: { 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)) if (inet_ntop(AF_INET, option, buf, buflen - (buf - obuf) - 1))
buf += strlen(buf); buf += strlen(buf);
break; break;

View File

@ -70,9 +70,11 @@ struct client_state_t cs = {
.packetNum = 0, .packetNum = 0,
.xid = 0, .xid = 0,
.timeout = 0, .timeout = 0,
.oldTimeout = 0,
.leaseStartTime = 0, .leaseStartTime = 0,
.requestedIP = 0, .requestedIP = 0,
.serverAddr = 0, .serverAddr = 0,
.routerAddr = 0,
.lease = 0, .lease = 0,
.t1 = 0, .t1 = 0,
.t2 = 0, .t2 = 0,

View File

@ -25,6 +25,7 @@
#include "netlink.h" #include "netlink.h"
#include "ifchange.h" #include "ifchange.h"
#include "arp.h"
#include "log.h" #include "log.h"
#define NLMSG_RECVSIZE 8192 #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); 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 // Decode netlink messages and process them
static void nl_handlemsg(struct nlmsghdr *msg, unsigned int len, static void nl_handlemsg(struct nlmsghdr *msg, unsigned int len,
struct client_state_t *cs) 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 we don't have a lease, state -> INIT.
*/ */
if (cs->dhcpState == DS_BOUND) { if (cs->dhcpState == DS_BOUND) {
/* arp_check_gw(cs); */ arp_gw_check(cs);
} else if (cs->dhcpState != DS_INIT_SELECTING) { } else if (cs->dhcpState != DS_INIT_SELECTING)
log_line("nl: taking down interface"); takedown_if(cs);
// 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);
}
} }
} else { } else {
if (cs->ifsPrevState != IFS_DOWN) { if (cs->ifsPrevState != IFS_DOWN) {
cs->ifsPrevState = IFS_DOWN; cs->ifsPrevState = IFS_DOWN;
/* takedown_if(cs);
* state -> DOWN
*/
} }
} }
} else { } else {

View File

@ -45,11 +45,6 @@ enum {
DHCP_SIZE = sizeof(struct dhcpMessage), 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); int get_packet(struct dhcpMessage *packet, int fd);
uint16_t checksum(void *addr, int count); uint16_t checksum(void *addr, int count);
int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int raw_packet(struct dhcpMessage *payload, uint32_t source_ip,

View File

@ -19,7 +19,7 @@ static void init_selecting_timeout(struct client_state_t *cs)
/* broadcast */ /* broadcast */
send_discover(cs->xid, cs->requestedIP); send_discover(cs->xid, cs->requestedIP);
cs->timeout = DELAY_SEC * (cs->packetNum + 1); cs->timeout = DELAY_SEC * (cs->packetNum + 1) * 1000;
cs->packetNum++; cs->packetNum++;
} else { } else {
if (client_config.background_if_no_lease) { 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_REBINDING: rebinding_timeout(cs); break;
case DS_RELEASED: cs->timeout = -1; break; case DS_RELEASED: cs->timeout = -1; break;
case DS_ARP_CHECK: arp_success(cs); break; case DS_ARP_CHECK: arp_success(cs); break;
case DS_ARP_GW_CHECK: arp_gw_failed(cs); break;
default: break; default: break;
} }
} }