Fetch the gateway hardware address after receiving a router option in a DHCP
lease. Be more aggressive about closing old arp file descriptors. Check the ARP headers to make sure that received ARP packets are addressed to our machine. Whatever bug may have existed before doesn't exist on modern Linux kernels, if it ever did. Use the stored gateway hardware address to validate a restored link in the DS_ARP_GW_CHECK state. If an ARP message is received while we are in a state that does not expect ARP messages, close the ARP socket and log a message.
This commit is contained in:
parent
e1eb41e1c0
commit
b5e5685d01
104
ndhc/arp.c
104
ndhc/arp.c
@ -1,8 +1,13 @@
|
|||||||
/*
|
/* arp.c - arp ping checking
|
||||||
* Derived from busybox's udhcpc variant, which in turn was...
|
* Copyright 2010-2011 Nicholas J. Kain <njkain@gmail.com>
|
||||||
|
*
|
||||||
|
* Originally derived from busybox's udhcpc variant, which in turn was...
|
||||||
* Mostly stolen from: dhcpcd - DHCP client daemon
|
* Mostly stolen from: dhcpcd - DHCP client daemon
|
||||||
* by Yoichi Hariguchi <yoichi@fore.com>
|
* by Yoichi Hariguchi <yoichi@fore.com>
|
||||||
* Licensed under GPLv2, see file LICENSE in this source tree.
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* version 2 as published by the Free Software Foundation
|
||||||
*/
|
*/
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -29,7 +34,7 @@ static int arpreply_offset;
|
|||||||
static struct dhcpMessage arp_dhcp_packet;
|
static struct dhcpMessage arp_dhcp_packet;
|
||||||
|
|
||||||
/* Returns fd of the arp socket, or -1 on failure. */
|
/* Returns fd of the arp socket, or -1 on failure. */
|
||||||
static int arpping(uint32_t test_nip, const uint8_t *safe_mac,
|
static int arpping(struct client_state_t *cs, uint32_t test_ip,
|
||||||
uint32_t from_ip, uint8_t *from_mac, const char *interface)
|
uint32_t from_ip, uint8_t *from_mac, const char *interface)
|
||||||
{
|
{
|
||||||
int arpfd;
|
int arpfd;
|
||||||
@ -37,6 +42,10 @@ static int arpping(uint32_t test_nip, const uint8_t *safe_mac,
|
|||||||
struct sockaddr addr; /* for interface name */
|
struct sockaddr addr; /* for interface name */
|
||||||
struct arpMsg arp;
|
struct arpMsg arp;
|
||||||
|
|
||||||
|
if (cs->arpFd != -1) {
|
||||||
|
epoll_del(cs, cs->arpFd);
|
||||||
|
close(cs->arpFd);
|
||||||
|
}
|
||||||
arpfd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
|
arpfd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
|
||||||
if (arpfd == -1) {
|
if (arpfd == -1) {
|
||||||
log_warning("arpping: failed to create socket: %s", strerror(errno));
|
log_warning("arpping: failed to create socket: %s", strerror(errno));
|
||||||
@ -65,7 +74,7 @@ static int arpping(uint32_t test_nip, const uint8_t *safe_mac,
|
|||||||
memcpy(arp.sHaddr, from_mac, 6); /* source hardware address */
|
memcpy(arp.sHaddr, from_mac, 6); /* source hardware address */
|
||||||
memcpy(arp.sInaddr, &from_ip, sizeof from_ip); /* source IP address */
|
memcpy(arp.sInaddr, &from_ip, sizeof from_ip); /* source IP address */
|
||||||
/* tHaddr is zero-filled */ /* target hardware address */
|
/* tHaddr is zero-filled */ /* target hardware address */
|
||||||
memcpy(arp.tInaddr, &test_nip, sizeof test_nip);/* target IP address */
|
memcpy(arp.tInaddr, &test_ip, sizeof test_ip); /* target IP address */
|
||||||
|
|
||||||
memset(&addr, 0, sizeof addr);
|
memset(&addr, 0, sizeof addr);
|
||||||
strlcpy(addr.sa_data, interface, sizeof addr.sa_data);
|
strlcpy(addr.sa_data, interface, sizeof addr.sa_data);
|
||||||
@ -83,8 +92,8 @@ void arp_check(struct client_state_t *cs, struct dhcpMessage *packet)
|
|||||||
cs->arpPrevState = cs->dhcpState;
|
cs->arpPrevState = cs->dhcpState;
|
||||||
cs->dhcpState = DS_ARP_CHECK;
|
cs->dhcpState = DS_ARP_CHECK;
|
||||||
memcpy(&arp_dhcp_packet, packet, sizeof (struct dhcpMessage));
|
memcpy(&arp_dhcp_packet, packet, sizeof (struct dhcpMessage));
|
||||||
cs->arpFd = arpping(arp_dhcp_packet.yiaddr, NULL, 0,
|
cs->arpFd = arpping(cs, arp_dhcp_packet.yiaddr, 0, client_config.arp,
|
||||||
client_config.arp, client_config.interface);
|
client_config.interface);
|
||||||
epoll_add(cs, cs->arpFd);
|
epoll_add(cs, cs->arpFd);
|
||||||
cs->timeout = 2000;
|
cs->timeout = 2000;
|
||||||
memset(&arpreply, 0, sizeof arpreply);
|
memset(&arpreply, 0, sizeof arpreply);
|
||||||
@ -96,8 +105,8 @@ void arp_gw_check(struct client_state_t *cs)
|
|||||||
cs->arpPrevState = cs->dhcpState;
|
cs->arpPrevState = cs->dhcpState;
|
||||||
cs->dhcpState = DS_ARP_GW_CHECK;
|
cs->dhcpState = DS_ARP_GW_CHECK;
|
||||||
memset(&arp_dhcp_packet, 0, sizeof (struct dhcpMessage));
|
memset(&arp_dhcp_packet, 0, sizeof (struct dhcpMessage));
|
||||||
cs->arpFd = arpping(cs->routerAddr, NULL, 0,
|
cs->arpFd = arpping(cs, cs->routerAddr, 0, client_config.arp,
|
||||||
client_config.arp, client_config.interface);
|
client_config.interface);
|
||||||
epoll_add(cs, cs->arpFd);
|
epoll_add(cs, cs->arpFd);
|
||||||
cs->oldTimeout = cs->timeout;
|
cs->oldTimeout = cs->timeout;
|
||||||
cs->timeout = 2000;
|
cs->timeout = 2000;
|
||||||
@ -105,10 +114,23 @@ void arp_gw_check(struct client_state_t *cs)
|
|||||||
arpreply_offset = 0;
|
arpreply_offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void arp_get_gw_hwaddr(struct client_state_t *cs)
|
||||||
|
{
|
||||||
|
if (cs->dhcpState != DS_BOUND)
|
||||||
|
log_warning("arp_get_gw_hwaddr: called when state != DS_BOUND");
|
||||||
|
memset(&arp_dhcp_packet, 0, sizeof (struct dhcpMessage));
|
||||||
|
cs->arpFd = arpping(cs, cs->routerAddr, 0, client_config.arp,
|
||||||
|
client_config.interface);
|
||||||
|
epoll_add(cs, cs->arpFd);
|
||||||
|
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.");
|
||||||
epoll_del(cs, cs->arpFd);
|
epoll_del(cs, cs->arpFd);
|
||||||
|
close(cs->arpFd);
|
||||||
cs->arpFd = -1;
|
cs->arpFd = -1;
|
||||||
send_decline(cs->xid, cs->serverAddr, arp_dhcp_packet.yiaddr);
|
send_decline(cs->xid, cs->serverAddr, arp_dhcp_packet.yiaddr);
|
||||||
|
|
||||||
@ -125,6 +147,7 @@ void arp_gw_failed(struct client_state_t *cs)
|
|||||||
{
|
{
|
||||||
log_line("arp: gateway appears to have changed, getting new lease");
|
log_line("arp: gateway appears to have changed, getting new lease");
|
||||||
epoll_del(cs, cs->arpFd);
|
epoll_del(cs, cs->arpFd);
|
||||||
|
close(cs->arpFd);
|
||||||
cs->arpFd = -1;
|
cs->arpFd = -1;
|
||||||
|
|
||||||
// Same as packet.c: line 258
|
// Same as packet.c: line 258
|
||||||
@ -142,6 +165,7 @@ void arp_success(struct client_state_t *cs)
|
|||||||
struct in_addr temp_addr;
|
struct in_addr temp_addr;
|
||||||
|
|
||||||
epoll_del(cs, cs->arpFd);
|
epoll_del(cs, cs->arpFd);
|
||||||
|
close(cs->arpFd);
|
||||||
cs->arpFd = -1;
|
cs->arpFd = -1;
|
||||||
|
|
||||||
/* enter bound state */
|
/* enter bound state */
|
||||||
@ -155,12 +179,11 @@ void arp_success(struct client_state_t *cs)
|
|||||||
log_line("Lease of %s obtained, lease time %ld.",
|
log_line("Lease of %s obtained, lease time %ld.",
|
||||||
inet_ntoa(temp_addr), cs->lease);
|
inet_ntoa(temp_addr), cs->lease);
|
||||||
cs->requestedIP = arp_dhcp_packet.yiaddr;
|
cs->requestedIP = arp_dhcp_packet.yiaddr;
|
||||||
|
cs->dhcpState = DS_BOUND;
|
||||||
ifchange(&arp_dhcp_packet,
|
ifchange(&arp_dhcp_packet,
|
||||||
((cs->arpPrevState == DS_RENEWING ||
|
((cs->arpPrevState == DS_RENEWING ||
|
||||||
cs->arpPrevState == DS_REBINDING)
|
cs->arpPrevState == DS_REBINDING)
|
||||||
? IFCHANGE_RENEW : IFCHANGE_BOUND));
|
? IFCHANGE_RENEW : IFCHANGE_BOUND));
|
||||||
|
|
||||||
cs->dhcpState = DS_BOUND;
|
|
||||||
change_listen_mode(cs, LM_NONE);
|
change_listen_mode(cs, LM_NONE);
|
||||||
if (client_config.quit_after_lease)
|
if (client_config.quit_after_lease)
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
@ -172,6 +195,7 @@ void arp_gw_success(struct client_state_t *cs)
|
|||||||
{
|
{
|
||||||
log_line("arp: gateway seems unchanged");
|
log_line("arp: gateway seems unchanged");
|
||||||
epoll_del(cs, cs->arpFd);
|
epoll_del(cs, cs->arpFd);
|
||||||
|
close(cs->arpFd);
|
||||||
cs->arpFd = -1;
|
cs->arpFd = -1;
|
||||||
cs->timeout = cs->oldTimeout;
|
cs->timeout = cs->oldTimeout;
|
||||||
cs->dhcpState = cs->arpPrevState;
|
cs->dhcpState = cs->arpPrevState;
|
||||||
@ -194,15 +218,14 @@ void handle_arp_response(struct client_state_t *cs)
|
|||||||
arpreply_offset += r;
|
arpreply_offset += r;
|
||||||
}
|
}
|
||||||
|
|
||||||
//log3("sHaddr %02x:%02x:%02x:%02x:%02x:%02x",
|
if (arpreply_offset < ARP_MSG_SIZE) {
|
||||||
//arp.sHaddr[0], arp.sHaddr[1], arp.sHaddr[2],
|
log_warning("handle_arp_response: Received short ARP message.");
|
||||||
//arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]);
|
return;
|
||||||
|
}
|
||||||
if (arpreply_offset >= ARP_MSG_SIZE) {
|
switch (cs->dhcpState) {
|
||||||
if (cs->dhcpState == DS_ARP_CHECK) {
|
case DS_ARP_CHECK:
|
||||||
if (arpreply.operation == htons(ARPOP_REPLY)
|
if (arpreply.operation == htons(ARPOP_REPLY)
|
||||||
// don't check: Linux returns invalid tHaddr (fixed in 2.6.24?)
|
&& !memcmp(arpreply.tHaddr, client_config.arp, 6)
|
||||||
// && !memcmp(arpreply.tHaddr, from_mac, 6)
|
|
||||||
&& *(aliased_uint32_t*)arpreply.sInaddr
|
&& *(aliased_uint32_t*)arpreply.sInaddr
|
||||||
== arp_dhcp_packet.yiaddr)
|
== arp_dhcp_packet.yiaddr)
|
||||||
{
|
{
|
||||||
@ -215,20 +238,47 @@ void handle_arp_response(struct client_state_t *cs)
|
|||||||
memset(&arpreply, 0, sizeof arpreply);
|
memset(&arpreply, 0, sizeof arpreply);
|
||||||
arpreply_offset = 0;
|
arpreply_offset = 0;
|
||||||
}
|
}
|
||||||
return;
|
break;
|
||||||
} else {
|
case DS_ARP_GW_CHECK:
|
||||||
if (arpreply.operation == htons(ARPOP_REPLY)
|
if (arpreply.operation == htons(ARPOP_REPLY)
|
||||||
// don't check: Linux returns invalid tHaddr (fixed in 2.6.24?)
|
&& !memcmp(arpreply.tHaddr, client_config.arp, 6)
|
||||||
// && !memcmp(arpreply.tHaddr, from_mac, 6)
|
|
||||||
&& *(aliased_uint32_t*)arpreply.sInaddr == cs->routerAddr)
|
&& *(aliased_uint32_t*)arpreply.sInaddr == cs->routerAddr)
|
||||||
{
|
{
|
||||||
// XXX: would be nice to check arp gateway mac, too
|
// Success only if the router/gw MAC matches stored value
|
||||||
arp_gw_success(cs);
|
if (!memcmp(cs->routerArp, arpreply.sHaddr, 6))
|
||||||
|
arp_gw_success(cs);
|
||||||
|
else
|
||||||
|
arp_gw_failed(cs);
|
||||||
} else {
|
} else {
|
||||||
memset(&arpreply, 0, sizeof arpreply);
|
memset(&arpreply, 0, sizeof arpreply);
|
||||||
arpreply_offset = 0;
|
arpreply_offset = 0;
|
||||||
}
|
}
|
||||||
return;
|
break;
|
||||||
}
|
case DS_BOUND:
|
||||||
|
if (arpreply.operation == htons(ARPOP_REPLY)
|
||||||
|
&& !memcmp(arpreply.tHaddr, client_config.arp, 6)
|
||||||
|
&& *(aliased_uint32_t*)arpreply.sInaddr == cs->routerAddr)
|
||||||
|
{
|
||||||
|
memcpy(cs->routerArp, arpreply.sHaddr, 6);
|
||||||
|
epoll_del(cs, cs->arpFd);
|
||||||
|
close(cs->arpFd);
|
||||||
|
cs->arpFd = -1;
|
||||||
|
log_line("gateway hardware address %02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
cs->routerArp[0], cs->routerArp[1],
|
||||||
|
cs->routerArp[2], cs->routerArp[3],
|
||||||
|
cs->routerArp[4], cs->routerArp[5]);
|
||||||
|
} else {
|
||||||
|
log_line("still looking for gateway hardware address");
|
||||||
|
memset(&arpreply, 0, sizeof arpreply);
|
||||||
|
arpreply_offset = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
epoll_del(cs, cs->arpFd);
|
||||||
|
close(cs->arpFd);
|
||||||
|
cs->arpFd = -1;
|
||||||
|
log_warning("handle_arp_response: called in invalid state 0x%02x",
|
||||||
|
cs->dhcpState);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,5 +35,6 @@ 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_check(struct client_state_t *cs);
|
||||||
void arp_gw_failed(struct client_state_t *cs);
|
void arp_gw_failed(struct client_state_t *cs);
|
||||||
|
void arp_get_gw_hwaddr(struct client_state_t *cs);
|
||||||
|
|
||||||
#endif /* ARPPING_H_ */
|
#endif /* ARPPING_H_ */
|
||||||
|
@ -44,6 +44,7 @@ struct client_state_t {
|
|||||||
int timeout, oldTimeout;
|
int timeout, oldTimeout;
|
||||||
uint32_t requestedIP, serverAddr, routerAddr;
|
uint32_t requestedIP, serverAddr, routerAddr;
|
||||||
uint32_t lease, t1, t2, xid;
|
uint32_t lease, t1, t2, xid;
|
||||||
|
unsigned char routerArp[6];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct client_config_t {
|
struct client_config_t {
|
||||||
|
@ -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 20:21:30 nk>
|
* Time-stamp: <2011-03-30 22:54:28 nk>
|
||||||
*
|
*
|
||||||
* (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
|
* (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
|
||||||
*
|
*
|
||||||
@ -34,12 +34,14 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "packet.h"
|
#include "packet.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#include "arp.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "ifchange.h"
|
#include "ifchange.h"
|
||||||
|
|
||||||
// For access to routerAddr and nothing else.
|
// For access to routerAddr and nothing else.
|
||||||
extern struct client_state_t cs;
|
extern struct client_state_t cs;
|
||||||
|
static char router_set;
|
||||||
|
|
||||||
/* 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. */
|
||||||
@ -88,6 +90,7 @@ static int ifchd_cmd(char *buf, size_t buflen, uint8_t *option, ssize_t optlen,
|
|||||||
// for verifying gateway existence by ARP when link returns.
|
// for verifying gateway existence by ARP when link returns.
|
||||||
if (code == DHCP_ROUTER) {
|
if (code == DHCP_ROUTER) {
|
||||||
memcpy(&cs.routerAddr, option, 4);
|
memcpy(&cs.routerAddr, option, 4);
|
||||||
|
router_set = 1;
|
||||||
}
|
}
|
||||||
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);
|
||||||
@ -212,6 +215,7 @@ static void bound_if(struct dhcpMessage *packet)
|
|||||||
snprintf(buf, sizeof buf, "ip:%s:", ip);
|
snprintf(buf, sizeof buf, "ip:%s:", ip);
|
||||||
sockwrite(sockfd, buf, strlen(buf));
|
sockwrite(sockfd, buf, strlen(buf));
|
||||||
|
|
||||||
|
router_set = 0;
|
||||||
send_cmd(sockfd, packet, DHCP_SUBNET);
|
send_cmd(sockfd, packet, DHCP_SUBNET);
|
||||||
send_cmd(sockfd, packet, DHCP_ROUTER);
|
send_cmd(sockfd, packet, DHCP_ROUTER);
|
||||||
send_cmd(sockfd, packet, DHCP_DNS_SERVER);
|
send_cmd(sockfd, packet, DHCP_DNS_SERVER);
|
||||||
@ -222,6 +226,8 @@ static void bound_if(struct dhcpMessage *packet)
|
|||||||
send_cmd(sockfd, packet, DHCP_WINS_SERVER);
|
send_cmd(sockfd, packet, DHCP_WINS_SERVER);
|
||||||
|
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
|
if (router_set == 1)
|
||||||
|
arp_get_gw_hwaddr(&cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ifchange(struct dhcpMessage *packet, int mode)
|
void ifchange(struct dhcpMessage *packet, int mode)
|
||||||
|
@ -83,6 +83,7 @@ struct client_state_t cs = {
|
|||||||
.listenFd = -1,
|
.listenFd = -1,
|
||||||
.arpFd = -1,
|
.arpFd = -1,
|
||||||
.nlFd = -1,
|
.nlFd = -1,
|
||||||
|
.routerArp = "\0\0\0\0\0\0",
|
||||||
};
|
};
|
||||||
|
|
||||||
struct client_config_t client_config = {
|
struct client_config_t client_config = {
|
||||||
@ -95,7 +96,7 @@ struct client_config_t client_config = {
|
|||||||
.clientid = NULL,
|
.clientid = NULL,
|
||||||
.hostname = NULL,
|
.hostname = NULL,
|
||||||
.ifindex = 0,
|
.ifindex = 0,
|
||||||
.arp = "\0",
|
.arp = "\0\0\0\0\0\0",
|
||||||
};
|
};
|
||||||
|
|
||||||
static void show_usage(void)
|
static void show_usage(void)
|
||||||
@ -132,6 +133,7 @@ static void perform_renew(void)
|
|||||||
case DS_ARP_CHECK:
|
case DS_ARP_CHECK:
|
||||||
// Cancel arp ping in progress and treat as previous state.
|
// Cancel arp ping in progress and treat as previous state.
|
||||||
epoll_del(&cs, cs.arpFd);
|
epoll_del(&cs, cs.arpFd);
|
||||||
|
close(cs.arpFd);
|
||||||
cs.arpFd = -1;
|
cs.arpFd = -1;
|
||||||
cs.dhcpState = cs.arpPrevState;
|
cs.dhcpState = cs.arpPrevState;
|
||||||
goto retry;
|
goto retry;
|
||||||
@ -178,6 +180,7 @@ static void perform_release(void)
|
|||||||
|
|
||||||
if (cs.dhcpState == DS_ARP_CHECK) {
|
if (cs.dhcpState == DS_ARP_CHECK) {
|
||||||
epoll_del(&cs, cs.arpFd);
|
epoll_del(&cs, cs.arpFd);
|
||||||
|
close(cs.arpFd);
|
||||||
cs.arpFd = -1;
|
cs.arpFd = -1;
|
||||||
}
|
}
|
||||||
change_listen_mode(&cs, LM_NONE);
|
change_listen_mode(&cs, LM_NONE);
|
||||||
|
Loading…
Reference in New Issue
Block a user