Improve fingerprinting to support DHCP relay agents.
Mostly reverts the previous commit and instead teaches ndhc to properly handle the case when it is communicating with a DHCP relay agent on its local segment rather than directly with a DHCP server.
This commit is contained in:
parent
a395234a67
commit
27c9e2c553
49
src/arp.c
49
src/arp.c
@ -49,10 +49,6 @@
|
|||||||
#define ARP_MSG_SIZE 0x2a
|
#define ARP_MSG_SIZE 0x2a
|
||||||
#define ARP_RETRANS_DELAY 5000 // ms
|
#define ARP_RETRANS_DELAY 5000 // ms
|
||||||
|
|
||||||
// Maximum number of retries when finding the DHCP server ethernet address
|
|
||||||
// via ARP queries before assuming it is on a different segment.
|
|
||||||
#define MAX_DHCP_SERVER_HWADDR_QUERIES 1
|
|
||||||
|
|
||||||
// From RFC5227
|
// From RFC5227
|
||||||
int arp_probe_wait = 1000; // initial random delay (ms)
|
int arp_probe_wait = 1000; // initial random delay (ms)
|
||||||
int arp_probe_num = 3; // number of probe packets
|
int arp_probe_num = 3; // number of probe packets
|
||||||
@ -351,7 +347,7 @@ int arp_gw_check(struct client_state_t *cs)
|
|||||||
return 0;
|
return 0;
|
||||||
garp.gw_check_initpings = garp.send_stats[ASEND_GW_PING].count;
|
garp.gw_check_initpings = garp.send_stats[ASEND_GW_PING].count;
|
||||||
garp.server_replied = false;
|
garp.server_replied = false;
|
||||||
if (arp_ping(cs, cs->serverAddr) < 0)
|
if (arp_ping(cs, cs->srcAddr) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (cs->routerAddr) {
|
if (cs->routerAddr) {
|
||||||
garp.router_replied = false;
|
garp.router_replied = false;
|
||||||
@ -380,7 +376,7 @@ static int arp_get_gw_hwaddr(struct client_state_t *cs)
|
|||||||
log_line("%s: arp: Searching for dhcp server address...",
|
log_line("%s: arp: Searching for dhcp server address...",
|
||||||
client_config.interface);
|
client_config.interface);
|
||||||
cs->got_server_arp = 0;
|
cs->got_server_arp = 0;
|
||||||
if (arp_ping(cs, cs->serverAddr) < 0)
|
if (arp_ping(cs, cs->srcAddr) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (cs->routerAddr) {
|
if (cs->routerAddr) {
|
||||||
cs->got_router_arp = 0;
|
cs->got_router_arp = 0;
|
||||||
@ -413,13 +409,13 @@ static int act_if_arp_gw_failed(struct client_state_t *cs)
|
|||||||
{
|
{
|
||||||
if (garp.send_stats[ASEND_GW_PING].count >= garp.gw_check_initpings + 6) {
|
if (garp.send_stats[ASEND_GW_PING].count >= garp.gw_check_initpings + 6) {
|
||||||
if (garp.router_replied && !garp.server_replied)
|
if (garp.router_replied && !garp.server_replied)
|
||||||
log_line("%s: arp: DHCP server didn't reply. Getting new lease.",
|
log_line("%s: arp: DHCP agent didn't reply. Getting new lease.",
|
||||||
client_config.interface);
|
client_config.interface);
|
||||||
else if (!garp.router_replied && garp.server_replied)
|
else if (!garp.router_replied && garp.server_replied)
|
||||||
log_line("%s: arp: Gateway didn't reply. Getting new lease.",
|
log_line("%s: arp: Gateway didn't reply. Getting new lease.",
|
||||||
client_config.interface);
|
client_config.interface);
|
||||||
else
|
else
|
||||||
log_line("%s: arp: DHCP server and gateway didn't reply. Getting new lease.",
|
log_line("%s: arp: DHCP agent and gateway didn't reply. Getting new lease.",
|
||||||
client_config.interface);
|
client_config.interface);
|
||||||
arp_gw_failed(cs);
|
arp_gw_failed(cs);
|
||||||
return 1;
|
return 1;
|
||||||
@ -562,9 +558,9 @@ static void arp_gw_check_timeout(struct client_state_t *cs, long long nowts)
|
|||||||
client_config.interface);
|
client_config.interface);
|
||||||
}
|
}
|
||||||
if (!garp.server_replied) {
|
if (!garp.server_replied) {
|
||||||
log_line("%s: arp: Still waiting for DHCP server to reply to arp ping...",
|
log_line("%s: arp: Still waiting for DHCP agent to reply to arp ping...",
|
||||||
client_config.interface);
|
client_config.interface);
|
||||||
if (arp_ping(cs, cs->serverAddr) < 0)
|
if (arp_ping(cs, cs->srcAddr) < 0)
|
||||||
log_warning("%s: arp: Failed to send ARP ping in retransmission.",
|
log_warning("%s: arp: Failed to send ARP ping in retransmission.",
|
||||||
client_config.interface);
|
client_config.interface);
|
||||||
}
|
}
|
||||||
@ -595,20 +591,10 @@ static void arp_gw_query_timeout(struct client_state_t *cs, long long nowts)
|
|||||||
log_warning("%s: arp: Failed to send ARP ping in retransmission.",
|
log_warning("%s: arp: Failed to send ARP ping in retransmission.",
|
||||||
client_config.interface);
|
client_config.interface);
|
||||||
}
|
}
|
||||||
// Here it can be a bit tricky, since DHCP proxies do exist and can
|
|
||||||
// mean that the DHCP server will not be on the local segment and thus
|
|
||||||
// will not respond to ARP. Therefore, the serverAddr can only be
|
|
||||||
// treated as extra information that may or may not exist.
|
|
||||||
if (!cs->got_server_arp) {
|
if (!cs->got_server_arp) {
|
||||||
if (++cs->server_arp_tries > MAX_DHCP_SERVER_HWADDR_QUERIES) {
|
log_line("%s: arp: Still looking for DHCP agent hardware address...",
|
||||||
log_line("%s: arp: No ARP response from DHCP server after %d tries. Proceeding.",
|
|
||||||
client_config.interface, cs->server_arp_tries);
|
|
||||||
arp_do_gw_query_done(cs);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log_line("%s: arp: Still looking for DHCP server hardware address...",
|
|
||||||
client_config.interface);
|
client_config.interface);
|
||||||
if (arp_ping(cs, cs->serverAddr) < 0)
|
if (arp_ping(cs, cs->srcAddr) < 0)
|
||||||
log_warning("%s: arp: Failed to send ARP ping in retransmission.",
|
log_warning("%s: arp: Failed to send ARP ping in retransmission.",
|
||||||
client_config.interface);
|
client_config.interface);
|
||||||
}
|
}
|
||||||
@ -680,16 +666,16 @@ static void arp_do_gw_query(struct client_state_t *cs)
|
|||||||
cs->routerArp[2], cs->routerArp[3],
|
cs->routerArp[2], cs->routerArp[3],
|
||||||
cs->routerArp[4], cs->routerArp[5]);
|
cs->routerArp[4], cs->routerArp[5]);
|
||||||
cs->got_router_arp = 1;
|
cs->got_router_arp = 1;
|
||||||
if (cs->routerAddr == cs->serverAddr)
|
if (cs->routerAddr == cs->srcAddr)
|
||||||
goto server_is_router;
|
goto server_is_router;
|
||||||
if (cs->got_server_arp)
|
if (cs->got_server_arp)
|
||||||
arp_do_gw_query_done(cs);
|
arp_do_gw_query_done(cs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!memcmp(garp.reply.sip4, &cs->serverAddr, 4)) {
|
if (!memcmp(garp.reply.sip4, &cs->srcAddr, 4)) {
|
||||||
server_is_router:
|
server_is_router:
|
||||||
memcpy(cs->serverArp, garp.reply.smac, 6);
|
memcpy(cs->serverArp, garp.reply.smac, 6);
|
||||||
log_line("%s: arp: DHCP Server hardware address %02x:%02x:%02x:%02x:%02x:%02x",
|
log_line("%s: arp: DHCP agent hardware address %02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
client_config.interface, cs->serverArp[0], cs->serverArp[1],
|
client_config.interface, cs->serverArp[0], cs->serverArp[1],
|
||||||
cs->serverArp[2], cs->serverArp[3],
|
cs->serverArp[2], cs->serverArp[3],
|
||||||
cs->serverArp[4], cs->serverArp[5]);
|
cs->serverArp[4], cs->serverArp[5]);
|
||||||
@ -723,14 +709,7 @@ static void arp_do_gw_check(struct client_state_t *cs)
|
|||||||
// Success only if the router/gw MAC matches stored value
|
// Success only if the router/gw MAC matches stored value
|
||||||
if (!memcmp(cs->routerArp, garp.reply.smac, 6)) {
|
if (!memcmp(cs->routerArp, garp.reply.smac, 6)) {
|
||||||
garp.router_replied = true;
|
garp.router_replied = true;
|
||||||
// Handle the case where the DHCP server is proxed from
|
if (cs->routerAddr == cs->srcAddr)
|
||||||
// a different segment and will never reply.
|
|
||||||
if (!cs->got_server_arp &&
|
|
||||||
cs->server_arp_tries > MAX_DHCP_SERVER_HWADDR_QUERIES) {
|
|
||||||
arp_gw_success(cs);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (cs->routerAddr == cs->serverAddr)
|
|
||||||
goto server_is_router;
|
goto server_is_router;
|
||||||
if (garp.server_replied)
|
if (garp.server_replied)
|
||||||
arp_gw_success(cs);
|
arp_gw_success(cs);
|
||||||
@ -741,7 +720,7 @@ static void arp_do_gw_check(struct client_state_t *cs)
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!memcmp(garp.reply.sip4, &cs->serverAddr, 4)) {
|
if (!memcmp(garp.reply.sip4, &cs->srcAddr, 4)) {
|
||||||
server_is_router:
|
server_is_router:
|
||||||
// Success only if the server MAC matches stored value
|
// Success only if the server MAC matches stored value
|
||||||
if (!memcmp(cs->serverArp, garp.reply.smac, 6)) {
|
if (!memcmp(cs->serverArp, garp.reply.smac, 6)) {
|
||||||
@ -749,7 +728,7 @@ server_is_router:
|
|||||||
if (garp.router_replied)
|
if (garp.router_replied)
|
||||||
arp_gw_success(cs);
|
arp_gw_success(cs);
|
||||||
} else {
|
} else {
|
||||||
log_line("%s: arp: DHCP server is different. Getting a new lease.",
|
log_line("%s: arp: DHCP agent is different. Getting a new lease.",
|
||||||
client_config.interface);
|
client_config.interface);
|
||||||
arp_gw_failed(cs);
|
arp_gw_failed(cs);
|
||||||
}
|
}
|
||||||
|
10
src/dhcp.c
10
src/dhcp.c
@ -174,7 +174,8 @@ static int get_raw_packet_validate_bpf(struct ip_udp_dhcp_packet *packet)
|
|||||||
// Read a packet from a raw socket. Returns -1 on fatal error, -2 on
|
// Read a packet from a raw socket. Returns -1 on fatal error, -2 on
|
||||||
// transient error.
|
// transient error.
|
||||||
static ssize_t get_raw_packet(struct client_state_t *cs,
|
static ssize_t get_raw_packet(struct client_state_t *cs,
|
||||||
struct dhcpmsg *payload)
|
struct dhcpmsg *payload,
|
||||||
|
uint32_t *srcaddr)
|
||||||
{
|
{
|
||||||
struct ip_udp_dhcp_packet packet;
|
struct ip_udp_dhcp_packet packet;
|
||||||
memset(&packet, 0, sizeof packet);
|
memset(&packet, 0, sizeof packet);
|
||||||
@ -217,6 +218,8 @@ static ssize_t get_raw_packet(struct client_state_t *cs,
|
|||||||
client_config.interface);
|
client_config.interface);
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
if (srcaddr)
|
||||||
|
*srcaddr = packet.ip.saddr;
|
||||||
memcpy(payload, &packet.data, l);
|
memcpy(payload, &packet.data, l);
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
@ -370,7 +373,8 @@ void handle_packet(struct client_state_t *cs)
|
|||||||
if (cs->listenFd < 0)
|
if (cs->listenFd < 0)
|
||||||
return;
|
return;
|
||||||
struct dhcpmsg packet;
|
struct dhcpmsg packet;
|
||||||
ssize_t r = get_raw_packet(cs, &packet);
|
uint32_t srcaddr;
|
||||||
|
ssize_t r = get_raw_packet(cs, &packet, &srcaddr);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
// Not a transient issue handled by packet collection functions.
|
// Not a transient issue handled by packet collection functions.
|
||||||
if (r != -2) {
|
if (r != -2) {
|
||||||
@ -384,7 +388,7 @@ void handle_packet(struct client_state_t *cs)
|
|||||||
uint8_t msgtype;
|
uint8_t msgtype;
|
||||||
if (!validate_dhcp_packet(cs, (size_t)r, &packet, &msgtype))
|
if (!validate_dhcp_packet(cs, (size_t)r, &packet, &msgtype))
|
||||||
return;
|
return;
|
||||||
packet_action(cs, &packet, msgtype);
|
packet_action(cs, &packet, msgtype, srcaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize a DHCP client packet that will be sent to a server
|
// Initialize a DHCP client packet that will be sent to a server
|
||||||
|
@ -42,8 +42,7 @@ struct client_state_t {
|
|||||||
int ifDeconfig; // Set if the interface has already been deconfigured.
|
int ifDeconfig; // Set if the interface has already been deconfigured.
|
||||||
int epollFd, signalFd, listenFd, arpFd, nlFd;
|
int epollFd, signalFd, listenFd, arpFd, nlFd;
|
||||||
int nlPortId;
|
int nlPortId;
|
||||||
int server_arp_tries;
|
uint32_t clientAddr, serverAddr, srcAddr, routerAddr;
|
||||||
uint32_t clientAddr, serverAddr, routerAddr;
|
|
||||||
uint32_t lease, renewTime, rebindTime, xid;
|
uint32_t lease, renewTime, rebindTime, xid;
|
||||||
struct nk_random_state_u32 rnd32_state;
|
struct nk_random_state_u32 rnd32_state;
|
||||||
uint8_t routerArp[6], serverArp[6];
|
uint8_t routerArp[6], serverArp[6];
|
||||||
|
24
src/state.c
24
src/state.c
@ -42,9 +42,9 @@
|
|||||||
#include "sys.h"
|
#include "sys.h"
|
||||||
|
|
||||||
static void selecting_packet(struct client_state_t *cs, struct dhcpmsg *packet,
|
static void selecting_packet(struct client_state_t *cs, struct dhcpmsg *packet,
|
||||||
uint8_t msgtype);
|
uint8_t msgtype, uint32_t srcaddr);
|
||||||
static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet,
|
static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet,
|
||||||
uint8_t msgtype);
|
uint8_t msgtype, uint32_t srcaddr);
|
||||||
static void selecting_timeout(struct client_state_t *cs, long long nowts);
|
static void selecting_timeout(struct client_state_t *cs, long long nowts);
|
||||||
static void requesting_timeout(struct client_state_t *cs, long long nowts);
|
static void requesting_timeout(struct client_state_t *cs, long long nowts);
|
||||||
static void bound_timeout(struct client_state_t *cs, long long nowts);
|
static void bound_timeout(struct client_state_t *cs, long long nowts);
|
||||||
@ -57,7 +57,7 @@ static void frenew(struct client_state_t *cs);
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void (*packet_fn)(struct client_state_t *cs, struct dhcpmsg *packet,
|
void (*packet_fn)(struct client_state_t *cs, struct dhcpmsg *packet,
|
||||||
uint8_t msgtype);
|
uint8_t msgtype, uint32_t srcaddr);
|
||||||
void (*timeout_fn)(struct client_state_t *cs, long long nowts);
|
void (*timeout_fn)(struct client_state_t *cs, long long nowts);
|
||||||
void (*force_renew_fn)(struct client_state_t *cs);
|
void (*force_renew_fn)(struct client_state_t *cs);
|
||||||
void (*force_release_fn)(struct client_state_t *cs);
|
void (*force_release_fn)(struct client_state_t *cs);
|
||||||
@ -96,7 +96,6 @@ static void reinit_shared_deconfig(struct client_state_t *cs)
|
|||||||
num_dhcp_requests = 0;
|
num_dhcp_requests = 0;
|
||||||
cs->got_router_arp = 0;
|
cs->got_router_arp = 0;
|
||||||
cs->got_server_arp = 0;
|
cs->got_server_arp = 0;
|
||||||
cs->server_arp_tries = 0;
|
|
||||||
memset(&cs->routerArp, 0, sizeof cs->routerArp);
|
memset(&cs->routerArp, 0, sizeof cs->routerArp);
|
||||||
memset(&cs->serverArp, 0, sizeof cs->serverArp);
|
memset(&cs->serverArp, 0, sizeof cs->serverArp);
|
||||||
arp_reset_send_stats();
|
arp_reset_send_stats();
|
||||||
@ -225,8 +224,9 @@ static int validate_serverid(struct client_state_t *cs, struct dhcpmsg *packet,
|
|||||||
|
|
||||||
// Can transition to DS_BOUND or DS_SELECTING.
|
// Can transition to DS_BOUND or DS_SELECTING.
|
||||||
static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet,
|
static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet,
|
||||||
uint8_t msgtype)
|
uint8_t msgtype, uint32_t srcaddr)
|
||||||
{
|
{
|
||||||
|
(void)srcaddr;
|
||||||
if (msgtype == DHCPACK) {
|
if (msgtype == DHCPACK) {
|
||||||
if (!validate_serverid(cs, packet, "a DHCP ACK"))
|
if (!validate_serverid(cs, packet, "a DHCP ACK"))
|
||||||
return;
|
return;
|
||||||
@ -281,7 +281,7 @@ static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void selecting_packet(struct client_state_t *cs, struct dhcpmsg *packet,
|
static void selecting_packet(struct client_state_t *cs, struct dhcpmsg *packet,
|
||||||
uint8_t msgtype)
|
uint8_t msgtype, uint32_t srcaddr)
|
||||||
{
|
{
|
||||||
if (msgtype == DHCPOFFER) {
|
if (msgtype == DHCPOFFER) {
|
||||||
int found;
|
int found;
|
||||||
@ -289,9 +289,11 @@ static void selecting_packet(struct client_state_t *cs, struct dhcpmsg *packet,
|
|||||||
if (found) {
|
if (found) {
|
||||||
char clibuf[INET_ADDRSTRLEN];
|
char clibuf[INET_ADDRSTRLEN];
|
||||||
char svrbuf[INET_ADDRSTRLEN];
|
char svrbuf[INET_ADDRSTRLEN];
|
||||||
|
char srcbuf[INET_ADDRSTRLEN];
|
||||||
cs->serverAddr = sid;
|
cs->serverAddr = sid;
|
||||||
cs->xid = packet->xid;
|
cs->xid = packet->xid;
|
||||||
cs->clientAddr = packet->yiaddr;
|
cs->clientAddr = packet->yiaddr;
|
||||||
|
cs->srcAddr = srcaddr;
|
||||||
cs->dhcpState = DS_REQUESTING;
|
cs->dhcpState = DS_REQUESTING;
|
||||||
dhcp_wake_ts = curms();
|
dhcp_wake_ts = curms();
|
||||||
num_dhcp_requests = 0;
|
num_dhcp_requests = 0;
|
||||||
@ -299,8 +301,10 @@ static void selecting_packet(struct client_state_t *cs, struct dhcpmsg *packet,
|
|||||||
clibuf, sizeof clibuf);
|
clibuf, sizeof clibuf);
|
||||||
inet_ntop(AF_INET, &(struct in_addr){.s_addr=cs->serverAddr},
|
inet_ntop(AF_INET, &(struct in_addr){.s_addr=cs->serverAddr},
|
||||||
svrbuf, sizeof svrbuf);
|
svrbuf, sizeof svrbuf);
|
||||||
log_line("%s: Received an offer of %s from server %s.",
|
inet_ntop(AF_INET, &(struct in_addr){.s_addr=cs->srcAddr},
|
||||||
client_config.interface, clibuf, svrbuf);
|
srcbuf, sizeof srcbuf);
|
||||||
|
log_line("%s: Received IP offer: %s from server %s via %s.",
|
||||||
|
client_config.interface, clibuf, svrbuf, srcbuf);
|
||||||
} else {
|
} else {
|
||||||
log_line("%s: Invalid offer received: it didn't have a server id.",
|
log_line("%s: Invalid offer received: it didn't have a server id.",
|
||||||
client_config.interface);
|
client_config.interface);
|
||||||
@ -402,10 +406,10 @@ void ifnocarrier_action(struct client_state_t *cs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void packet_action(struct client_state_t *cs, struct dhcpmsg *packet,
|
void packet_action(struct client_state_t *cs, struct dhcpmsg *packet,
|
||||||
uint8_t msgtype)
|
uint8_t msgtype, uint32_t srcaddr)
|
||||||
{
|
{
|
||||||
if (dhcp_states[cs->dhcpState].packet_fn)
|
if (dhcp_states[cs->dhcpState].packet_fn)
|
||||||
dhcp_states[cs->dhcpState].packet_fn(cs, packet, msgtype);
|
dhcp_states[cs->dhcpState].packet_fn(cs, packet, msgtype, srcaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void timeout_action(struct client_state_t *cs, long long nowts)
|
void timeout_action(struct client_state_t *cs, long long nowts)
|
||||||
|
@ -46,7 +46,7 @@ typedef enum {
|
|||||||
void reinit_selecting(struct client_state_t *cs, int timeout);
|
void reinit_selecting(struct client_state_t *cs, int timeout);
|
||||||
|
|
||||||
void packet_action(struct client_state_t *cs, struct dhcpmsg *packet,
|
void packet_action(struct client_state_t *cs, struct dhcpmsg *packet,
|
||||||
uint8_t msgtype);
|
uint8_t msgtype, uint32_t srcaddr);
|
||||||
void timeout_action(struct client_state_t *cs, long long nowts);
|
void timeout_action(struct client_state_t *cs, long long nowts);
|
||||||
void force_renew_action(struct client_state_t *cs);
|
void force_renew_action(struct client_state_t *cs);
|
||||||
void force_release_action(struct client_state_t *cs);
|
void force_release_action(struct client_state_t *cs);
|
||||||
|
Loading…
Reference in New Issue
Block a user