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;
}
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;
}
}
}

View File

@ -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_ */

View File

@ -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;
};

View File

@ -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 <njkain at gmail dot com>
*
@ -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;

View File

@ -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,

View File

@ -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 {

View File

@ -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,

View File

@ -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;
}
}