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)