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:
parent
de23d2241d
commit
03717e1593
88
ndhc/arp.c
88
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user