From dca662cd27ee0613a45438b6bb2f523fb63351b9 Mon Sep 17 00:00:00 2001 From: "Nicholas J. Kain" Date: Mon, 4 Jul 2011 20:07:16 -0400 Subject: [PATCH] Massive overhaul to arp.c in preparation for adding address defense: Use a separate state machine for the arp handling. It's loosely coupled to the dhcp code, and is thus much easier to reason about than the previous approach that made the arp code use the dhcp state. Add a BPF and C emulation of the BPF for RFC5227-style address defense. Allow switchable BPF filters for the arp socket. Fix a regression introduced in the arp announcement commit. Contrary to RFC5227, the 'h_dest' field in the ARP packet should be set to all 0xff for wildcard semantics, not all 0x00. Keep track of the millisecond timestamp of the most recent ARP packet that has been sent as well as the total number of packets that have been sent in the current ARP state. Use these values to implement time-based ARP retransmit. When querying the default gateway/router, use ARP packets that have the source IP set to the client lease IP rather than 0.0.0.0. Combine common code in the arp sending functions into arp_send(). Resequence the arp_announcement() in arp_success() so that it happens after the interface is configured by ifchange() / ifchd. Get rid of layering hack in ifchange.c to set the router address in the client state. Do it in the proper place in arp.c. Add an early exit before ifchd_cmd() in ifchange.c. This change prevents sending a ':' to ifchd for a string type option with no string content. --- ndhc/arp.c | 366 ++++++++++++++++++++++++++++++++++-------------- ndhc/arp.h | 13 +- ndhc/ifchange.c | 21 +-- 3 files changed, 264 insertions(+), 136 deletions(-) diff --git a/ndhc/arp.c b/ndhc/arp.c index b5f12f6..8b14f8a 100644 --- a/ndhc/arp.c +++ b/ndhc/arp.c @@ -1,5 +1,5 @@ /* arp.c - arp ping checking - * Time-stamp: <2011-06-16 21:37:52 njk> + * Time-stamp: <2011-07-04 20:03:04 njk> * * Copyright 2010-2011 Nicholas J. Kain * @@ -35,20 +35,38 @@ #include "dhcp.h" #include "sys.h" #include "ifchange.h" +#include "options.h" #include "leasefile.h" #include "log.h" #include "io.h" #define ARP_MSG_SIZE 0x2a -#define ARP_RETRY_COUNT 3 +#define ARP_RETRANS_DELAY 5000 // ms + +typedef enum { + AS_NONE = 0, // Nothing to react to wrt ARP + AS_COLLISION_CHECK, // Checking to see if another host has our IP before + // accepting a new lease. + AS_GW_CHECK, // Seeing if the default GW still exists on the local + // segment after the hardware link was lost. + AS_GW_QUERY, // Finding the default GW MAC address. + AS_DEFENSE, // Defending our IP address (RFC5227) + AS_MAX, +} arp_state_t; + +static arp_state_t arpState; +struct arp_stats { + long long ts; + int count; +}; +static struct arp_stats arp_stats[AS_MAX-1]; +static int using_arp_bpf; // Is a BPF installed on the ARP socket? static struct arpMsg arpreply; static int arpreply_offset; -static struct dhcpmsg arp_dhcp_packet; -static int arp_packet_num; -static int using_arp_bpf; +static struct dhcpmsg arp_dhcp_packet; // Used only for AS_COLLISION_CHECK -static int arp_open_fd(struct client_state_t *cs) +static void arp_set_bpf_basic(int fd) { static const struct sock_filter sf_arp[] = { // Verify that the frame has ethernet protocol type of ARP @@ -70,7 +88,62 @@ static int arp_open_fd(struct client_state_t *cs) .len = sizeof sf_arp / sizeof sf_arp[0], .filter = (struct sock_filter *)sf_arp, }; + using_arp_bpf = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sfp_arp, + sizeof sfp_arp) != -1; +} +static void arp_set_bpf_defense(struct client_state_t *cs, int fd) +{ + uint32_t mac4b; + uint16_t mac2b; + memcpy(&mac4b, client_config.arp, 4); + memcpy(&mac2b, client_config.arp+4, 2); + + struct sock_filter sf_arp[] = { + // Verify that the frame has ethernet protocol type of ARP + // and that the ARP hardware type field indicates Ethernet. + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_ARP << 16) | ARPHRD_ETHER, + 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0), + // Verify that the ARP protocol type field indicates IP, the ARP + // hardware address length field is 6, and the ARP protocol address + // length field is 4. + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 16), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (ETH_P_IP << 16) | 0x0604, 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0), + + // If the ARP packet source IP does not match our IP address, then + // it can be ignored. + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 28), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, cs->clientAddr, 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0), + // If the first four bytes of the ARP packet source hardware address + // does not equal our hardware address, then it's a conflict and should + // be passed along. + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 22), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, mac4b, 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0x0fffffff), + // If the last two bytes of the ARP packet source hardware address + // do not equal our hardware address, then it's a conflict and should + // be passed along. + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 26), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, mac2b, 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0x0fffffff), + // Packet announces our IP address and hardware address, so it requires + // no action. + BPF_STMT(BPF_RET + BPF_K, 0), + }; + struct sock_fprog sfp_arp = { + .len = sizeof sf_arp / sizeof sf_arp[0], + .filter = (struct sock_filter *)sf_arp, + }; + using_arp_bpf = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sfp_arp, + sizeof sfp_arp) != -1; +} + +static int arp_open_fd(struct client_state_t *cs) +{ if (cs->arpFd != -1) return 0; @@ -80,9 +153,6 @@ static int arp_open_fd(struct client_state_t *cs) goto out; } - using_arp_bpf = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sfp_arp, - sizeof sfp_arp) != -1; - int opt = 1; if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof opt) == -1) { log_error("arp: failed to set broadcast: %s", strerror(errno)); @@ -118,27 +188,12 @@ int arp_close_fd(struct client_state_t *cs) epoll_del(cs, cs->arpFd); close(cs->arpFd); cs->arpFd = -1; + arpState = AS_NONE; return 1; } -// Returns 0 on success, -1 on failure. -static int arpping(struct client_state_t *cs, uint32_t test_ip) +static int arp_send(struct client_state_t *cs, struct arpMsg *arp) { - if (arp_open_fd(cs) == -1) - return -1; - - struct arpMsg arp = { - .h_proto = htons(ETH_P_ARP), - .htype = htons(ARPHRD_ETHER), - .ptype = htons(ETH_P_IP), - .hlen = 6, - .plen = 4, - .operation = htons(ARPOP_REQUEST), - }; - memcpy(arp.h_source, client_config.arp, 6); - memcpy(arp.smac, client_config.arp, 6); - memcpy(arp.dip4, &test_ip, sizeof test_ip); - struct sockaddr_ll addr = { .sll_family = AF_PACKET, .sll_ifindex = client_config.ifindex, @@ -146,21 +201,19 @@ static int arpping(struct client_state_t *cs, uint32_t test_ip) }; memcpy(addr.sll_addr, client_config.arp, 6); - if (safe_sendto(cs->arpFd, (const char *)&arp, sizeof arp, + if (safe_sendto(cs->arpFd, (const char *)arp, sizeof *arp, 0, (struct sockaddr *)&addr, sizeof addr) < 0) { log_error("arp: sendto failed: %s", strerror(errno)); arp_close_fd(cs); return -1; } - arp_packet_num = 0; + arp_stats[arpState].count++; return 0; } -static int arp_announcement(struct client_state_t *cs) +// Returns 0 on success, -1 on failure. +static int arp_ping(struct client_state_t *cs, uint32_t test_ip) { - if (arp_open_fd(cs) == -1) - return -1; - struct arpMsg arp = { .h_proto = htons(ETH_P_ARP), .htype = htons(ARPHRD_ETHER), @@ -170,24 +223,47 @@ static int arp_announcement(struct client_state_t *cs) .operation = htons(ARPOP_REQUEST), }; memcpy(arp.h_source, client_config.arp, 6); + memset(arp.h_dest, 0xff, 6); + memcpy(arp.smac, client_config.arp, 6); + memcpy(arp.dip4, &test_ip, sizeof test_ip); + memcpy(arp.sip4, &cs->clientAddr, sizeof cs->clientAddr); + return arp_send(cs, &arp); +} + +// Returns 0 on success, -1 on failure. +static int arp_ip_anon_ping(struct client_state_t *cs, uint32_t test_ip) +{ + struct arpMsg arp = { + .h_proto = htons(ETH_P_ARP), + .htype = htons(ARPHRD_ETHER), + .ptype = htons(ETH_P_IP), + .hlen = 6, + .plen = 4, + .operation = htons(ARPOP_REQUEST), + }; + memcpy(arp.h_source, client_config.arp, 6); + memset(arp.h_dest, 0xff, 6); + memcpy(arp.smac, client_config.arp, 6); + memcpy(arp.dip4, &test_ip, sizeof test_ip); + return arp_send(cs, &arp); +} + +static int arp_announcement(struct client_state_t *cs) +{ + struct arpMsg arp = { + .h_proto = htons(ETH_P_ARP), + .htype = htons(ARPHRD_ETHER), + .ptype = htons(ETH_P_IP), + .hlen = 6, + .plen = 4, + .operation = htons(ARPOP_REQUEST), + }; + memcpy(arp.h_source, client_config.arp, 6); + memset(arp.h_dest, 0xff, 6); memcpy(arp.smac, client_config.arp, 6); memcpy(arp.sip4, &cs->clientAddr, 4); memcpy(arp.dip4, &cs->clientAddr, 4); - - struct sockaddr_ll addr = { - .sll_family = AF_PACKET, - .sll_ifindex = client_config.ifindex, - .sll_halen = 6, - }; - memcpy(addr.sll_addr, client_config.arp, 6); - - if (safe_sendto(cs->arpFd, (const char *)&arp, sizeof arp, - 0, (struct sockaddr *)&addr, sizeof addr) < 0) { - log_error("arp: announcement sendto failed: %s", strerror(errno)); - arp_close_fd(cs); - return -1; - } - return 0; + return arp_send(cs, &arp); } static void arpreply_clear() @@ -196,9 +272,48 @@ static void arpreply_clear() arpreply_offset = 0; } +static void arp_switch_state(struct client_state_t *cs, arp_state_t state) +{ + arp_state_t prev_state = arpState; + log_line("DEBUG: arp_switch_state: called."); + if (arpState == state || arpState >= AS_MAX) + return; + log_line("DEBUG: arp_switch_state: passed valid state change test"); + arpState = state; + arp_stats[arpState].ts = 0; + arp_stats[arpState].count = 0; + log_line("DEBUG: arp_switch_state: state = %u", state); + if (arpState == AS_NONE) { + arp_close_fd(cs); + return; + } + if (cs->arpFd == -1) { + log_line("DEBUG: arp_switch_state: opening arpFd"); + if (arp_open_fd(cs) == -1) + suicide("arp: failed to open arpFd when changing state to %u", + arpState); + log_line("DEBUG: arp_switch_state: opened arpFd"); + if (arpState != AS_DEFENSE) + arp_set_bpf_basic(cs->arpFd); + log_line("DEBUG: arp_switch_state: installed filters"); + } + if (arpState == AS_DEFENSE) { + log_line("DEBUG: arp_switch_state: changed to DEFENSE filter"); + arp_set_bpf_defense(cs, cs->arpFd); + return; + } + if (prev_state == AS_DEFENSE) { + log_line("DEBUG: arp_switch_state: removed DEFENSE filter"); + arp_set_bpf_basic(cs->arpFd); + return; + } + log_line("DEBUG: arp_switch_state: leaving."); +} + int arp_check(struct client_state_t *cs, struct dhcpmsg *packet) { - if (arpping(cs, arp_dhcp_packet.yiaddr) == -1) + arp_switch_state(cs, AS_COLLISION_CHECK); + if (arp_ip_anon_ping(cs, arp_dhcp_packet.yiaddr) == -1) return -1; cs->arpPrevState = cs->dhcpState; cs->dhcpState = DS_ARP_CHECK; @@ -210,7 +325,8 @@ int arp_check(struct client_state_t *cs, struct dhcpmsg *packet) int arp_gw_check(struct client_state_t *cs) { - if (arpping(cs, cs->routerAddr) == -1) + arp_switch_state(cs, AS_GW_CHECK); + if (arp_ping(cs, cs->routerAddr) == -1) return -1; cs->arpPrevState = cs->dhcpState; cs->dhcpState = DS_BOUND_GW_CHECK; @@ -221,13 +337,14 @@ int arp_gw_check(struct client_state_t *cs) return 0; } -int arp_get_gw_hwaddr(struct client_state_t *cs) +static int arp_get_gw_hwaddr(struct client_state_t *cs) { if (cs->dhcpState != DS_BOUND) log_error("arp_get_gw_hwaddr: called when state != DS_BOUND"); - if (arpping(cs, cs->routerAddr) == -1) + arp_switch_state(cs, AS_GW_QUERY); + log_line("arp: Searching for gw address..."); + if (arp_ping(cs, cs->routerAddr) == -1) return -1; - log_line("arp: Searching for gw address"); memset(&arp_dhcp_packet, 0, sizeof (struct dhcpmsg)); arpreply_clear(); return 0; @@ -264,8 +381,6 @@ void arp_gw_failed(struct client_state_t *cs) void arp_success(struct client_state_t *cs) { - arp_announcement(cs); - arp_close_fd(cs); cs->timeout = (cs->renewTime * 1000) - (curms() - cs->leaseStartTime); struct in_addr temp_addr = {.s_addr = arp_dhcp_packet.yiaddr}; @@ -274,12 +389,23 @@ void arp_success(struct client_state_t *cs) cs->clientAddr = arp_dhcp_packet.yiaddr; cs->dhcpState = DS_BOUND; cs->init = 0; - ifchange(&arp_dhcp_packet, - ((cs->arpPrevState == DS_RENEWING || - cs->arpPrevState == DS_REBINDING) - ? IFCHANGE_RENEW : IFCHANGE_BOUND)); + if (cs->arpPrevState == DS_RENEWING || cs->arpPrevState == DS_REBINDING) { + ifchange(&arp_dhcp_packet, IFCHANGE_RENEW); // XXX when does this happen? + arp_switch_state(cs, AS_DEFENSE); + } else { + ssize_t ol; + uint8_t *od; + od = get_option_data(&arp_dhcp_packet, DHCP_ROUTER, &ol); + ifchange(&arp_dhcp_packet, IFCHANGE_BOUND); + if (ol == 4) { + memcpy(&cs->routerAddr, od, 4); + arp_get_gw_hwaddr(cs); + } else + arp_switch_state(cs, AS_DEFENSE); + } set_listen_none(cs); write_leasefile(temp_addr); + arp_announcement(cs); if (client_config.quit_after_lease) exit(EXIT_SUCCESS); if (!client_config.foreground) @@ -289,8 +415,8 @@ void arp_success(struct client_state_t *cs) static void arp_gw_success(struct client_state_t *cs) { log_line("arp: Gateway seems unchanged"); + arp_switch_state(cs, AS_DEFENSE); arp_announcement(cs); - arp_close_fd(cs); cs->timeout = cs->oldTimeout; cs->dhcpState = cs->arpPrevState; @@ -323,13 +449,20 @@ static int arp_validate_bpf(struct arpMsg *am) return 1; } -// Note that this function will see all ARP traffic on the interface. -// Therefore the validation shouldn't be noisy, and should silently -// reject non-malformed ARP packets that are irrelevant. -static int arp_validate(struct arpMsg *am) +// ARP validation functions that will be performed by the BPF if it is +// installed. +static int arp_validate_bpf_defense(struct client_state_t *cs, + struct arpMsg *am) { - if (!using_arp_bpf && !arp_validate_bpf(am)) + if (memcmp(am->sip4, &cs->clientAddr, 4)) return 0; + if (!memcmp(am->smac, client_config.arp, 6)) + return 0; + return 1; +} + +static int arp_is_query_reply(struct arpMsg *am) +{ if (am->operation != htons(ARPOP_REPLY)) return 0; if (memcmp(am->h_dest, client_config.arp, 6)) @@ -341,90 +474,105 @@ static int arp_validate(struct arpMsg *am) void handle_arp_response(struct client_state_t *cs) { + int r = 0; + log_line("DEBUG: handle_arp_response called"); if (arpreply_offset < sizeof arpreply) { - int r = safe_read(cs->arpFd, (char *)&arpreply + arpreply_offset, + r = safe_read(cs->arpFd, (char *)&arpreply + arpreply_offset, sizeof arpreply - arpreply_offset); - if (r < 0) { - if (errno == EWOULDBLOCK || errno == EAGAIN) - return; + if (r < 0 && errno != EWOULDBLOCK && errno != EAGAIN) { log_error("arp: ARP response read failed: %s", strerror(errno)); - switch (cs->dhcpState) { - case DS_ARP_CHECK: arp_failed(cs); break; - case DS_BOUND_GW_CHECK: arp_gw_failed(cs); break; - case DS_BOUND: break; // keep trying for finding gw mac - default: break; + switch (arpState) { + case AS_COLLISION_CHECK: arp_failed(cs); break; + case AS_GW_CHECK: arp_gw_failed(cs); break; + default: + // XXX: close and re-open the FD in ALL cases + break; } } else arpreply_offset += r; } + log_line("DEBUG: Received %u bytes.", r); + // Perform retransmission if necessary. + if (r <= 0 && curms() >= arp_stats[arpState].ts + ARP_RETRANS_DELAY) { + log_line("DEBUG: retransmission timeout in arp state %u", arpState); + switch (arpState) { + case AS_COLLISION_CHECK: break; + case AS_GW_CHECK: + log_line("arp: Still waiting for gateway to reply to arp ping..."); + arp_ping(cs, cs->routerAddr); + break; + case AS_GW_QUERY: + log_line("arp: Still looking for gateway hardware address..."); + arp_ping(cs, cs->routerAddr); + default: + break; + } + return; + } + log_line("DEBUG: Did not retransmit."); if (arpreply_offset < ARP_MSG_SIZE) return; + log_line("DEBUG: Gathered a full ARP packet."); - if (!arp_validate(&arpreply)) { - arpreply_clear(); - return; + // Emulate the BPF filters if they are not in use. + if (!using_arp_bpf) { + if (!arp_validate_bpf(&arpreply)) + return; + if (arpState == AS_DEFENSE && !arp_validate_bpf_defense(cs, &arpreply)) + return; } + log_line("DEBUG: Passed the emulated BPF filters."); - ++arp_packet_num; - switch (cs->dhcpState) { - case DS_ARP_CHECK: + switch (arpState) { + case AS_COLLISION_CHECK: + if (!arp_is_query_reply(&arpreply)) + break; if (!memcmp(arpreply.sip4, &arp_dhcp_packet.yiaddr, 4)) { // Check to see if we replied to our own ARP query. if (!memcmp(client_config.arp, arpreply.smac, 6)) arp_success(cs); else arp_failed(cs); - return; - } else { - log_line("arp: Ping noise while waiting for check timeout"); - arpreply_clear(); } break; - case DS_BOUND_GW_CHECK: + case AS_GW_CHECK: + if (!arp_is_query_reply(&arpreply)) + break; if (!memcmp(arpreply.sip4, &cs->routerAddr, 4)) { // Success only if the router/gw MAC matches stored value if (!memcmp(cs->routerArp, arpreply.smac, 6)) arp_gw_success(cs); else arp_gw_failed(cs); - return; - } else { - log_line("arp: Still waiting for gateway to reply to arp ping"); - arpreply_clear(); } break; - case DS_BOUND: - if (!memcmp(arpreply.sip4, &cs->routerAddr, 4)) { + case AS_GW_QUERY: + log_line("DEBUG: Doing work for AS_GW_QUERY state."); + if (arp_is_query_reply(&arpreply) && + !memcmp(arpreply.sip4, &cs->routerAddr, 4)) { + arp_switch_state(cs, AS_DEFENSE); memcpy(cs->routerArp, arpreply.smac, 6); - arp_close_fd(cs); - log_line("arp: 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]); - return; - } else { - log_line("arp: Still looking for gateway hardware address"); - arpreply_clear(); + break; } + log_line("DEBUG: Was not a reply from GW. Checking for defense."); + if (!arp_validate_bpf_defense(cs, &arpreply)) + break; + case AS_DEFENSE: + log_line("arp: detected a peer attempting to use our IP!"); + // XXX: actually do work... + log_line("DEBUG: TODO actually do work!"); break; default: - arp_close_fd(cs); log_error("handle_arp_response: called in invalid state 0x%02x", cs->dhcpState); - return; - } - if (arp_packet_num >= ARP_RETRY_COUNT) { - switch (cs->dhcpState) { - case DS_BOUND: - if (arpping(cs, cs->routerAddr) == -1) - log_warning("arp: Failed to retransmit arp packet for finding gw mac addr"); - break; - default: - log_line("arp: Not yet bothering with arp retransmit for non-DS_BOUND state"); - break; - } + arp_close_fd(cs); } + log_line("DEBUG: Leaving handle_arp_response."); + arpreply_clear(); } diff --git a/ndhc/arp.h b/ndhc/arp.h index 63a9cb1..20793eb 100644 --- a/ndhc/arp.h +++ b/ndhc/arp.h @@ -1,12 +1,8 @@ /* arp.h - functions to call the interface change daemon - * Time-stamp: <2011-05-31 11:11:02 njk> + * Time-stamp: <2011-07-04 18:52:07 njk> * * Copyright 2010-2011 Nicholas J. Kain * - * Originally derived from busybox's udhcpc variant, which in turn was... - * Mostly stolen from: dhcpcd - DHCP client daemon - * by Yoichi Hariguchi - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -21,8 +17,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifndef ARPPING_H_ -#define ARPPING_H_ +#ifndef ARP_H_ +#define ARP_H_ #include #include @@ -52,9 +48,8 @@ struct arpMsg { int arp_close_fd(struct client_state_t *cs); int arp_check(struct client_state_t *cs, struct dhcpmsg *packet); int arp_gw_check(struct client_state_t *cs); -int arp_get_gw_hwaddr(struct client_state_t *cs); void arp_success(struct client_state_t *cs); void arp_gw_failed(struct client_state_t *cs); void handle_arp_response(struct client_state_t *cs); -#endif /* ARPPING_H_ */ +#endif /* ARP_H_ */ diff --git a/ndhc/ifchange.c b/ndhc/ifchange.c index 3e212c0..8ab2aaa 100644 --- a/ndhc/ifchange.c +++ b/ndhc/ifchange.c @@ -1,5 +1,5 @@ /* ifchange.c - functions to call the interface change daemon - * Time-stamp: <2011-05-01 19:04:06 njk> + * Time-stamp: <2011-07-04 18:50:59 njk> * * (c) 2004-2011 Nicholas J. Kain * @@ -39,10 +39,6 @@ #include "io.h" #include "ifchange.h" -// For access to routerAddr and nothing else. -extern struct client_state_t cs; -static char router_set; - // 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, @@ -86,12 +82,6 @@ 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) { - memcpy(&cs.routerAddr, option, 4); - router_set = 1; - } if (inet_ntop(AF_INET, option, buf, buflen - (buf - obuf) - 1)) buf += strlen(buf); break; @@ -192,6 +182,8 @@ static void send_cmd(int sockfd, struct dhcpmsg *packet, uint8_t code) memset(buf, '\0', sizeof buf); optdata = get_option_data(packet, code, &optlen); + if (!optlen) + return; if (ifchd_cmd(buf, sizeof buf, optdata, optlen, code) == -1) return; sockwrite(sockfd, buf, strlen(buf)); @@ -215,7 +207,6 @@ static void bound_if(struct dhcpmsg *packet, int mode) snprintf(buf, sizeof buf, "ip:%s:", ip); sockwrite(sockfd, buf, strlen(buf)); - router_set = 0; send_cmd(sockfd, packet, DHCP_SUBNET); send_cmd(sockfd, packet, DHCP_ROUTER); send_cmd(sockfd, packet, DHCP_DNS_SERVER); @@ -226,12 +217,6 @@ static void bound_if(struct dhcpmsg *packet, int mode) send_cmd(sockfd, packet, DHCP_WINS_SERVER); close(sockfd); - if (mode == IFCHANGE_BOUND && router_set == 1) { - if (arp_get_gw_hwaddr(&cs) == -1) { - log_warning("arp_get_gw_hwaddr failed to make arp socket; setting gw mac=ff:ff:ff:ff:ff:ff"); - memset(&cs.routerAddr, 0xff, 4); - } - } } void ifchange(struct dhcpmsg *packet, int mode)