From 355a06b9d93d531cf9b5498b21db785e99ed99a9 Mon Sep 17 00:00:00 2001 From: krw Date: Tue, 4 Apr 2017 15:15:48 +0000 Subject: [PATCH] Send a RTM_PROPOSAL to clear out other dhclient instances on startup. Replaces forcing interface link state down and up to generate RTM_IFINFO messages. --- sbin/dhclient/dhclient.c | 81 ++++++++++++++++++++++++++++++++++++++-- sbin/dhclient/dhcpd.h | 3 +- sbin/dhclient/dispatch.c | 22 +++++------ 3 files changed, 89 insertions(+), 17 deletions(-) diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c index 89bf3b5b5f9..13f346750c8 100644 --- a/sbin/dhclient/dhclient.c +++ b/sbin/dhclient/dhclient.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dhclient.c,v 1.406 2017/04/04 13:01:20 krw Exp $ */ +/* $OpenBSD: dhclient.c,v 1.407 2017/04/04 15:15:48 krw Exp $ */ /* * Copyright 2004 Henning Brauer @@ -169,6 +169,7 @@ struct client_lease *packet_to_lease(struct interface_info *, struct in_addr, struct option_data *); void go_daemon(void); int rdaemon(int); +void take_charge(struct interface_info *); #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) @@ -261,6 +262,22 @@ routehandler(struct interface_info *ifi) goto done; switch (rtm->rtm_type) { + case RTM_PROPOSAL: + if (rtm->rtm_index != ifi->index || + rtm->rtm_priority != RTP_PROPOSAL_DHCLIENT) + goto done; + if ((rtm->rtm_flags & RTF_PROTO3) != 0) { + if (rtm->rtm_seq == client->xid) { + client->flags |= IN_CHARGE; + goto done; + } else if ((client->flags & IN_CHARGE) != 0) { + rslt = asprintf(&errmsg, "yielding " + "responsibility for %s", + ifi->name); + goto die; + } + } + break; case RTM_NEWADDR: ifam = (struct ifa_msghdr *)rtm; if (ifam->ifam_index != ifi->index) @@ -641,8 +658,9 @@ main(int argc, char *argv[]) if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) fatal("socket(PF_ROUTE, SOCK_RAW)"); - rtfilter = ROUTE_FILTER(RTM_NEWADDR) | ROUTE_FILTER(RTM_DELADDR) | - ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_IFANNOUNCE); + rtfilter = ROUTE_FILTER(RTM_PROPOSAL) | ROUTE_FILTER(RTM_NEWADDR) | + ROUTE_FILTER(RTM_DELADDR) | ROUTE_FILTER(RTM_IFINFO) | + ROUTE_FILTER(RTM_IFANNOUNCE); if (setsockopt(routefd, PF_ROUTE, ROUTE_MSGFILTER, &rtfilter, sizeof(rtfilter)) == -1) @@ -651,6 +669,8 @@ main(int argc, char *argv[]) sizeof(ifi->rdomain)) == -1) fatal("setsockopt(ROUTE_TABLEFILTER)"); + take_charge(ifi); + /* Register the interface. */ if_register_receive(ifi); if_register_send(ifi); @@ -2958,3 +2978,58 @@ compare_lease(struct client_lease *active, struct client_lease *new) return (0); } + +void +take_charge(struct interface_info *ifi) +{ + struct pollfd fds[1]; + struct rt_msghdr rtm; + time_t start_time, cur_time; + int retries; + + if (time(&start_time) == -1) + fatal("time"); + + /* + * Send RTM_PROPOSAL with RTF_PROTO3 set. + * + * When it comes back, we're in charge and other dhclients are + * dead processes walking. + */ + memset(&rtm, 0, sizeof(rtm)); + + rtm.rtm_version = RTM_VERSION; + rtm.rtm_type = RTM_PROPOSAL; + rtm.rtm_msglen = sizeof(rtm); + rtm.rtm_tableid = ifi->rdomain; + rtm.rtm_index = ifi->index; + rtm.rtm_seq = ifi->client->xid = arc4random(); + rtm.rtm_priority = RTP_PROPOSAL_DHCLIENT; + rtm.rtm_addrs = 0; + rtm.rtm_flags = RTF_UP | RTF_PROTO3; + + retries = 0; + while ((ifi->client->flags & IN_CHARGE) == 0) { + if (write(routefd, &rtm, sizeof(rtm)) == -1) + fatal("tried to take charge"); + time(&cur_time); + if ((cur_time - start_time) > 3) { + if (++retries <= 3) { + if (time(&start_time) == -1) + fatal("time"); + } else { + fatalx("failed to take charge of %s", + ifi->name); + } + } + fds[0].fd = routefd; + fds[0].events = POLLIN; + if (poll(fds, 1, 3) == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + fatal("routefd poll"); + } + if ((fds[0].revents & (POLLIN | POLLHUP))) + routehandler(ifi); + } +} diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h index f599a6df629..1aae45d1a2f 100644 --- a/sbin/dhclient/dhcpd.h +++ b/sbin/dhclient/dhcpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dhcpd.h,v 1.163 2017/04/04 13:01:20 krw Exp $ */ +/* $OpenBSD: dhcpd.h,v 1.164 2017/04/04 15:15:48 krw Exp $ */ /* * Copyright (c) 2004 Henning Brauer @@ -131,6 +131,7 @@ struct client_state { struct in_addr destination; int flags; #define IS_RESPONSIBLE 0x1 +#define IN_CHARGE 0x2 u_int32_t xid; u_int16_t secs; time_t first_sending; diff --git a/sbin/dhclient/dispatch.c b/sbin/dhclient/dispatch.c index dfab902c6f1..2165c0f19f2 100644 --- a/sbin/dhclient/dispatch.c +++ b/sbin/dhclient/dispatch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dispatch.c,v 1.117 2017/03/08 15:46:36 krw Exp $ */ +/* $OpenBSD: dispatch.c,v 1.118 2017/04/04 15:15:48 krw Exp $ */ /* * Copyright 2004 Henning Brauer @@ -233,21 +233,17 @@ interface_link_forceup(char *ifname) memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) { - log_warn("interface_link_forceup: SIOCGIFFLAGS failed"); + log_warn("SIOCGIFFLAGS"); return; } - /* Force it down and up so others notice link state change. */ - ifr.ifr_flags &= ~IFF_UP; - if (ioctl(sock, SIOCSIFFLAGS, (caddr_t)&ifr) == -1) { - log_warn("interface_link_forceup: SIOCSIFFLAGS DOWN failed"); - return; - } - - ifr.ifr_flags |= IFF_UP; - if (ioctl(sock, SIOCSIFFLAGS, (caddr_t)&ifr) == -1) { - log_warn("interface_link_forceup: SIOCSIFFLAGS UP failed"); - return; + /* Force it up if it isn't already. */ + if ((ifr.ifr_flags & IFF_UP) == 0) { + ifr.ifr_flags |= IFF_UP; + if (ioctl(sock, SIOCSIFFLAGS, (caddr_t)&ifr) == -1) { + log_warn("SIOCSIFFLAGS"); + return; + } } } -- 2.20.1