diff --git a/ndhc/arp.c b/ndhc/arp.c index ce64b20..b6689ac 100644 --- a/ndhc/arp.c +++ b/ndhc/arp.c @@ -485,7 +485,7 @@ void arp_success(struct client_state_t *cs) cs->init = 0; last_conflict_ts = 0; arp_wake_ts[AS_COLLISION_CHECK] = -1; - ifchange_bind(&arp_dhcp_packet); + ifchange_bind(cs, &arp_dhcp_packet); if (cs->arpPrevState == DS_RENEWING || cs->arpPrevState == DS_REBINDING) { arp_switch_state(cs, AS_DEFENSE); } else { diff --git a/ndhc/ifchange.c b/ndhc/ifchange.c index 92d0cb2..00cbb92 100644 --- a/ndhc/ifchange.c +++ b/ndhc/ifchange.c @@ -45,7 +45,6 @@ #include "ifchange.h" #include "ifch_proto.h" -static int cfg_deconfig; // Set if the interface has already been deconfigured. static struct dhcpmsg cfg_packet; // Copy of the current configuration packet. static int ifchd_cmd_u8(char *buf, size_t buflen, char *optname, @@ -155,8 +154,9 @@ static int ifchd_cmd(char *buf, size_t buflen, uint8_t *optdata, } #undef IFCHD_SW_CMD -static void pipewrite(const char *buf, size_t count) +static void pipewrite(struct client_state_t *cs, const char *buf, size_t count) { + cs->ifchWorking = 1; if (safe_write(pToIfchW, buf, count) == -1) { log_error("pipewrite: write failed: %s", strerror(errno)); return; @@ -164,18 +164,18 @@ static void pipewrite(const char *buf, size_t count) log_line("Sent to ifchd: %s", buf); } -void ifchange_deconfig(void) +void ifchange_deconfig(struct client_state_t *cs) { char buf[256]; - if (cfg_deconfig) + if (cs->ifDeconfig) return; + cs->ifDeconfig = 1; snprintf(buf, sizeof buf, "ip4:0.0.0.0,255.255.255.255;"); log_line("Resetting %s IP configuration.", client_config.interface); - pipewrite(buf, strlen(buf)); + pipewrite(cs, buf, strlen(buf)); - cfg_deconfig = 1; memset(&cfg_packet, 0, sizeof cfg_packet); } @@ -258,7 +258,7 @@ static size_t send_cmd(char *out, size_t olen, struct dhcpmsg *packet, return strlen(buf); } -void ifchange_bind(struct dhcpmsg *packet) +void ifchange_bind(struct client_state_t *cs, struct dhcpmsg *packet) { char buf[2048]; int tbs = 0; @@ -275,8 +275,8 @@ void ifchange_bind(struct dhcpmsg *packet) tbs |= send_cmd(buf, sizeof buf, packet, DCODE_MTU); tbs |= send_cmd(buf, sizeof buf, packet, DCODE_WINS); if (tbs) - pipewrite(buf, strlen(buf)); + pipewrite(cs, buf, strlen(buf)); - cfg_deconfig = 0; + cs->ifDeconfig = 0; memcpy(&cfg_packet, packet, sizeof cfg_packet); } diff --git a/ndhc/ifchange.h b/ndhc/ifchange.h index bba8039..e4b9bca 100644 --- a/ndhc/ifchange.h +++ b/ndhc/ifchange.h @@ -29,7 +29,7 @@ #ifndef IFCHANGE_H_ #define IFCHANGE_H_ -void ifchange_bind(struct dhcpmsg *packet); -void ifchange_deconfig(void); +void ifchange_bind(struct client_state_t *cs, struct dhcpmsg *packet); +void ifchange_deconfig(struct client_state_t *cs); #endif diff --git a/ndhc/ifchd.c b/ndhc/ifchd.c index 308a334..15b7ba4 100644 --- a/ndhc/ifchd.c +++ b/ndhc/ifchd.c @@ -303,6 +303,24 @@ static void signal_dispatch(void) } } +static void inform_execute(int success) +{ + int r; + char c = success ? '+' : '-'; + retry: + r = safe_write(pToNdhcW, &c, sizeof c); + if (r == 0) { + // Remote end hung up. + exit(EXIT_SUCCESS); + } else if (r < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + goto retry; + log_line("%s: (%s) error writing to ifch -> ndhc pipe: %s", + client_config.interface, __func__, strerror(errno)); + exit(EXIT_FAILURE); + } +} + static void process_client_pipe(void) { char buf[MAX_BUF]; @@ -315,14 +333,18 @@ static void process_client_pipe(void) } else if (r < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) return; - log_line("ifch: error reading from ndhc -> ifch pipe: %s", strerror(errno)); + log_line("%s: (%s) error reading from ndhc -> ifch pipe: %s", + client_config.interface, __func__, strerror(errno)); exit(EXIT_FAILURE); } if (execute_buffer(buf) == -1) { - log_line("ifch: execute_buffer was passed invalid commands: '%s'", buf); + log_line("%s: (%s) execute_buffer was passed invalid commands: '%s'", + client_config.interface, __func__, buf); + inform_execute(0); exit(EXIT_FAILURE); - } + } else + inform_execute(1); } void do_ifch_work(void) diff --git a/ndhc/ndhc.c b/ndhc/ndhc.c index f9b569a..3522281 100644 --- a/ndhc/ndhc.c +++ b/ndhc/ndhc.c @@ -71,6 +71,8 @@ #include "ifchd.h" struct client_state_t cs = { + .ifchWorking = 0, + .ifDeconfig = 0, .init = 1, .epollFd = -1, .signalFd = -1, @@ -213,9 +215,29 @@ static int get_clientid_mac_string(char *str, size_t slen) return 1; } +static void handle_ifch_message(void) +{ + char c; + int r = safe_read(pToNdhcR, &c, sizeof c); + if (r == 0) { + // Remote end hung up. + exit(EXIT_SUCCESS); + } else if (r < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + return; + log_line("%s: (%s) error reading from ifch -> ndhc pipe: %s", + client_config.interface, __func__, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (c == '+') + cs.ifchWorking = 0; +} + +#define NDHC_NUM_EP_FDS 4 static void do_ndhc_work(void) { - struct epoll_event events[3]; + struct epoll_event events[NDHC_NUM_EP_FDS]; long long nowts; int timeout; @@ -229,12 +251,13 @@ static void do_ndhc_work(void) setup_signals_ndhc(); epoll_add(cs.epollFd, cs.nlFd); + epoll_add(cs.epollFd, pToNdhcR); set_listen_raw(&cs); nowts = curms(); goto jumpstart; for (;;) { - int r = epoll_wait(cs.epollFd, events, 3, timeout); + int r = epoll_wait(cs.epollFd, events, NDHC_NUM_EP_FDS, timeout); if (r == -1) { if (errno == EINTR) continue; @@ -251,6 +274,8 @@ static void do_ndhc_work(void) handle_arp_response(&cs); else if (fd == cs.nlFd) handle_nl_message(&cs); + else if (fd == pToNdhcR) + handle_ifch_message(); else suicide("epoll_wait: unknown fd"); } @@ -336,7 +361,7 @@ static void ndhc_main(void) { drop_root(ndhc_uid, ndhc_gid); if (cs.ifsPrevState != IFS_UP) - ifchange_deconfig(); + ifchange_deconfig(&cs); do_ndhc_work(); } diff --git a/ndhc/ndhc.h b/ndhc/ndhc.h index 584d062..f34a461 100644 --- a/ndhc/ndhc.h +++ b/ndhc/ndhc.h @@ -38,6 +38,8 @@ struct client_state_t { int dhcpState; int arpPrevState; int ifsPrevState; + int ifchWorking; // ifch is performing interface changes. + int ifDeconfig; // Set if the interface has already been deconfigured. int listenMode; int epollFd, signalFd, listenFd, arpFd, nlFd; int nlPortId; diff --git a/ndhc/netlink.c b/ndhc/netlink.c index 4001de3..d13c258 100644 --- a/ndhc/netlink.c +++ b/ndhc/netlink.c @@ -89,6 +89,8 @@ static void nl_process_msgs(const struct nlmsghdr *nlh, void *data) get_if_index_and_mac(nlh, ifm); if (ifm->ifi_index != client_config.ifindex) break; + if (cs->ifchWorking) + break; // IFF_UP corresponds to ifconfig down or ifconfig up. if (ifm->ifi_flags & IFF_UP) { // IFF_RUNNING is the hardware carrier. @@ -111,6 +113,8 @@ static void nl_process_msgs(const struct nlmsghdr *nlh, void *data) case RTM_DELLINK: if (ifm->ifi_index != client_config.ifindex) break; + if (cs->ifchWorking) + break; if (cs->ifsPrevState != IFS_REMOVED) { cs->ifsPrevState = IFS_REMOVED; log_line("Interface removed. Exiting."); diff --git a/ndhc/state.c b/ndhc/state.c index a361b73..71408b4 100644 --- a/ndhc/state.c +++ b/ndhc/state.c @@ -88,7 +88,7 @@ static int delay_timeout(size_t numpackets) static void reinit_shared_deconfig(struct client_state_t *cs) { - ifchange_deconfig(); + ifchange_deconfig(cs); arp_close_fd(cs); cs->clientAddr = 0; num_dhcp_requests = 0;