NULL
};
-static int dhcp_open(struct interface *);
+static int dhcp_openbpf(struct interface *);
#ifdef ARP
static void dhcp_arp_conflicted(struct arp_state *, const struct arp_msg *);
#endif
+static void dhcp_handledhcp(struct interface *, struct bootp *, size_t,
+ const struct in_addr *);
+static int dhcp_initstate(struct interface *);
void
dhcp_printoptions(const struct dhcpcd_ctx *ctx,
else
state->xid = arc4random();
+ /* We can't do this when sharing leases across interfaes */
+#if 0
/* As the XID changes, re-apply the filter. */
if (state->bpf_fd != -1) {
if (bpf_bootp(ifp, state->bpf_fd) == -1)
logerr(__func__); /* try to continue */
}
+#endif
}
void
struct if_options *ifo = ifp->options;
struct bootp *bootp;
struct bootp_pkt *udp;
- size_t len;
+ size_t len, ulen;
ssize_t r;
struct in_addr from, to;
- struct ipv4_addr *iap;
struct timespec tv;
- int s;
-#ifdef IN_IFF_NOTUSEABLE
- struct ipv4_addr *ia;
-#endif
- s = -1;
if (!callback) {
/* No carrier? Don't bother sending the packet. */
if (ifp->carrier == LINK_DOWN)
timespec_to_double(&tv));
}
- if (dhcp_open(ifp) == -1)
+ if (dhcp_openbpf(ifp) == -1)
return;
- iap = state->addr;
- if (state->added && !(state->added & STATE_FAKE) &&
- state->addr != NULL && state->new != NULL &&
-#ifdef IN_IFF_NOTUSEABLE
- ((ia = ipv4_iffindaddr(ifp, &state->addr->addr, NULL)) &&
- !(ia->addr_flags & IN_IFF_NOTUSEABLE)) &&
-#endif
- (state->lease.server.s_addr ||
- ifp->options->options & DHCPCD_INFORM) &&
- IS_DHCP(state->new))
- {
- s = dhcp_openudp(ifp);
- if (s == -1) {
- if (errno != EADDRINUSE)
- logerr("%s: dhcp_openudp", ifp->name);
- /* We cannot renew */
- state->addr = NULL;
- }
- }
-
r = make_message(&bootp, ifp, type);
- state->addr = iap;
if (r == -1)
goto fail;
len = (size_t)r;
from.s_addr = bootp->ciaddr;
- if (s != -1 && from.s_addr != INADDR_ANY)
+ if (from.s_addr != INADDR_ANY)
to.s_addr = state->lease.server.s_addr;
else
to.s_addr = INADDR_ANY;
- if (to.s_addr && to.s_addr != INADDR_BROADCAST) {
- struct sockaddr_in sin;
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = to.s_addr;
- sin.sin_port = htons(BOOTPS);
- r = sendto(s, (uint8_t *)bootp, len, 0,
- (struct sockaddr *)&sin, sizeof(sin));
- if (r == -1)
- logerr("%s: dhcp_sendpacket", ifp->name);
+ udp = dhcp_makeudppacket(&ulen, (uint8_t *)bootp, len, from, to);
+ if (udp == NULL) {
+ logerr("%s: dhcp_makeudppacket", ifp->name);
+ r = 0;
} else {
- size_t ulen;
-
- udp = dhcp_makeudppacket(&ulen, (uint8_t *)bootp, len, from,to);
- if (udp == NULL) {
- logerr("%s: dhcp_makeudppacket", ifp->name);
- r = 0;
- } else {
- r = bpf_send(ifp, state->bpf_fd,
- ETHERTYPE_IP, (uint8_t *)udp, ulen);
- free(udp);
- }
- /* If we failed to send a raw packet this normally means
- * we don't have the ability to work beneath the IP layer
- * for this interface.
- * As such we remove it from consideration without actually
- * stopping the interface. */
- if (r == -1) {
- logerr("%s: if_sendraw", ifp->name);
- switch(errno) {
- case ENETDOWN:
- case ENETRESET:
- case ENETUNREACH:
- case ENOBUFS:
- break;
- default:
- if (!(ifp->ctx->options & DHCPCD_TEST))
- dhcp_drop(ifp, "FAIL");
- dhcp_free(ifp);
- eloop_timeout_delete(ifp->ctx->eloop,
- NULL, ifp);
- callback = NULL;
- }
+ r = bpf_send(ifp, state->bpf_fd,
+ ETHERTYPE_IP, (uint8_t *)udp, ulen);
+ free(udp);
+ }
+ /* If we failed to send a raw packet this normally means
+ * we don't have the ability to work beneath the IP layer
+ * for this interface.
+ * As such we remove it from consideration without actually
+ * stopping the interface. */
+ if (r == -1) {
+ logerr("%s: if_sendraw", ifp->name);
+ switch(errno) {
+ case ENETDOWN:
+ case ENETRESET:
+ case ENETUNREACH:
+ case ENOBUFS:
+ break;
+ default:
+ if (!(ifp->ctx->options & DHCPCD_TEST))
+ dhcp_drop(ifp, "FAIL");
+ eloop_timeout_delete(ifp->ctx->eloop,
+ NULL, ifp);
+ callback = NULL;
}
}
free(bootp);
fail:
- if (s != -1)
- close(s);
-
/* Even if we fail to send a packet we should continue as we are
* as our failure timeouts will change out codepath when needed. */
if (callback)
dhcp_arp_probed(astate);
return;
}
- dhcp_close(ifp);
arp_free(astate);
#ifdef KERNEL_RFC5227
/* As arping is finished, close the ARP socket.
state->offer->ciaddr : state->offer->yiaddr;
/* If the interface already has the address configured
* then we can't ARP for duplicate detection. */
- ia = ipv4_findaddr(ifp->ctx, &addr);
+ ia = ipv4_iffindaddr(ifp, &addr, NULL);
if ((astate = arp_new(ifp, &addr)) == NULL)
return -1;
astate->probed_cb = dhcp_arp_probed;
struct if_options *ifo;
struct dhcp_state *state = D_STATE(ifp);
- if (state == NULL)
+ if (state == NULL || state->state == DHS_NONE)
return;
ifo = ifp->options;
if ((ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC) &&
struct if_options *ifo;
struct dhcp_state *state = D_STATE(ifp);
- if (state == NULL)
+ if (state == NULL || state->state == DHS_NONE)
return;
ifo = ifp->options;
state->state = DHS_REBOOT;
state = D_STATE(ifp);
/* dhcp_start may just have been called and we don't yet have a state
* but we do have a timeout, so punt it. */
- if (state == NULL) {
+ if (state == NULL || state->state == DHS_NONE) {
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
return;
}
#ifdef AUTH
dhcp_auth_reset(&state->auth);
#endif
- dhcp_close(ifp);
- state->state = DHS_INIT;
+ state->state = DHS_NONE;
free(state->offer);
state->offer = NULL;
state->offer_len = 0;
return 0;
}
-static int
+#define WHTLST_NONE 0
+#define WHTLST_MATCH 1
+#define WHTLST_NOMATCH 2
+static unsigned int
whitelisted_ip(const struct if_options *ifo, in_addr_t addr)
{
size_t i;
if (ifo->whitelist_len == 0)
- return -1;
+ return WHTLST_NONE;
for (i = 0; i < ifo->whitelist_len; i += 2)
if (ifo->whitelist[i] == (addr & ifo->whitelist[i + 1]))
- return 1;
- return 0;
+ return WHTLST_MATCH;
+ return WHTLST_NOMATCH;
}
static void
free(a);
}
+/* If we're sharing the same IP address with another interface on the
+ * same network, we may receive the DHCP reply on the wrong interface.
+ * Try and re-direct it here. */
+static void
+dhcp_redirect_dhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
+ const struct in_addr *from)
+{
+ struct interface *ifn;
+ const struct dhcp_state *state;
+ uint32_t xid;
+
+ xid = ntohl(bootp->xid);
+ TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
+ state = D_CSTATE(ifn);
+ if (state == NULL || state->state == DHS_NONE)
+ continue;
+ if (state->xid != xid)
+ continue;
+ if (ifn->hwlen <= sizeof(bootp->chaddr) &&
+ memcmp(bootp->chaddr, ifn->hwaddr, ifn->hwlen))
+ continue;
+ logdebugx("%s: redirecting DHCP message to %s",
+ ifp->name, ifn->name);
+ dhcp_handledhcp(ifn, bootp, bootp_len, from);
+ }
+}
+
static void
dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
const struct in_addr *from)
ifp->name, bootp->op);
return;
}
+#endif
+
+ if (state->xid != ntohl(bootp->xid)) {
+ if (state->state != DHS_BOUND)
+ logdebugx("%s: wrong xid 0x%x (expecting 0x%x) from %s",
+ ifp->name, ntohl(bootp->xid), state->xid,
+ inet_ntoa(*from));
+ dhcp_redirect_dhcp(ifp, bootp, bootp_len, from);
+ return;
+ }
if (ifp->hwlen <= sizeof(bootp->chaddr) &&
memcmp(bootp->chaddr, ifp->hwaddr, ifp->hwlen))
ifp->name, ntohl(bootp->xid),
hwaddr_ntoa(bootp->chaddr, sizeof(bootp->chaddr),
buf, sizeof(buf)));
+ dhcp_redirect_dhcp(ifp, bootp, bootp_len, from);
return;
}
-#endif
+
+ if (!ifp->active)
+ return;
+
+ i = whitelisted_ip(ifp->options, from->s_addr);
+ switch (i) {
+ case WHTLST_NOMATCH:
+ logwarnx("%s: non whitelisted DHCP packet from %s",
+ ifp->name, inet_ntoa(*from));
+ return;
+ case WHTLST_MATCH:
+ break;
+ case WHTLST_NONE:
+ if (blacklisted_ip(ifp->options, from->s_addr) == 1) {
+ logwarnx("%s: blacklisted DHCP packet from %s",
+ ifp->name, inet_ntoa(*from));
+ return;
+ }
+ }
/* We may have found a BOOTP server */
if (get_option_uint8(ifp->ctx, &type,
return;
}
- /* Handled in our BPF filter. */
-#if 0
- /* Ensure it's the right transaction */
- if (state->xid != ntohl(bootp->xid)) {
- logdebugx("%s: wrong xid 0x%x (expecting 0x%x) from %s",
- ifp->name, ntohl(bootp->xid), state->xid,
- inet_ntoa(*from));
- return;
- }
-#endif
-
if (state->state == DHS_PROBE) {
/* Ignore any DHCP messages whilst probing a lease to bind. */
LOGDHCP(logdebugx, "probing, ignoring");
{
struct bootp *bootp;
struct in_addr from;
- int i;
size_t udp_len;
const struct dhcp_state *state = D_CSTATE(ifp);
ifp->name, inet_ntoa(from));
return;
}
- i = whitelisted_ip(ifp->options, from.s_addr);
- if (i == 0) {
- logwarnx("%s: non whitelisted DHCP packet from %s",
- ifp->name, inet_ntoa(from));
- return;
- } else if (i != 1 && blacklisted_ip(ifp->options, from.s_addr) == 1) {
- logwarnx("%s: blacklisted DHCP packet from %s",
- ifp->name, inet_ntoa(from));
- return;
- }
if (ifp->flags & IFF_POINTOPOINT &&
(state->addr == NULL || state->addr->brd.s_addr != from.s_addr))
{
while (!(flags & BPF_EOF)) {
bytes = bpf_read(ifp, state->bpf_fd, buf, sizeof(buf), &flags);
if (bytes == -1) {
- logerr(__func__);
- dhcp_close(ifp);
+ if (state->state != DHS_NONE) {
+ logerr("%s: %s", __func__, ifp->name);
+ dhcp_close(ifp);
+ }
return;
}
dhcp_handlepacket(ifp, buf, (size_t)bytes, flags);
static int
-dhcp_open(struct interface *ifp)
+dhcp_openbpf1(struct interface *ifp, bool logerror)
{
struct dhcp_state *state;
/* May as well disable IPv4 entirely at
* this point as we really need it. */
ifp->options->options &= ~DHCPCD_IPV4;
- } else
- logerr(__func__);
+ } else if (logerror)
+ logerr("%s: %s", __func__, ifp->name);
return -1;
}
eloop_event_add(ifp->ctx->eloop,
return 0;
}
+/* This blows chunks.
+ * Because we may (although unlikely) get the same IP address
+ * across different interfaces we need to open a BPF socket
+ * on ALL interfaces as thanks to ARP magic we might get the
+ * DHCP reply on a different interface. */
+static int
+dhcp_openbpf(struct interface *ifp)
+{
+ struct interface *ifn;
+ int r, o;
+
+ r = -1;
+ TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
+ if (dhcp_initstate(ifn) == -1)
+ continue;
+ o = dhcp_openbpf1(ifn, ifn == ifp);
+ if (ifn == ifp)
+ r = o;
+ }
+ return r;
+}
+
int
dhcp_dump(struct interface *ifp)
{
dhcp_close(ifp);
arp_drop(ifp);
if (state) {
+ state->state = DHS_NONE;
free(state->old);
free(state->new);
free(state->offer);
free(state->clientid);
free(state);
- ifp->if_data[IF_DATA_DHCP] = NULL;
}
ctx = ifp->ctx;
* close the global socket and release resources */
if (ctx->ifaces) {
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
- if (D_STATE(ifp))
+ state = D_STATE(ifp);
+ if (state != NULL && state->state != DHS_NONE)
break;
}
}
}
static int
-dhcp_init(struct interface *ifp)
+dhcp_initstate(struct interface *ifp)
{
struct dhcp_state *state;
- const struct if_options *ifo;
- uint8_t len;
- char buf[(sizeof(ifo->clientid) - 1) * 3];
state = D_STATE(ifp);
- if (state == NULL) {
- ifp->if_data[IF_DATA_DHCP] = calloc(1, sizeof(*state));
- state = D_STATE(ifp);
- if (state == NULL)
- return -1;
- /* 0 is a valid fd, so init to -1 */
- state->bpf_fd = -1;
+ if (state != NULL)
+ return 0;
+
+ ifp->if_data[IF_DATA_DHCP] = calloc(1, sizeof(*state));
+ state = D_STATE(ifp);
+ if (state == NULL)
+ return -1;
+ state->state = DHS_NONE;
+ /* 0 is a valid fd, so init to -1 */
+ state->bpf_fd = -1;
#ifdef ARPING
- state->arping_index = -1;
+ state->arping_index = -1;
#endif
+ return 1;
+}
+
+static int
+dhcp_init(struct interface *ifp)
+{
+ struct dhcp_state *state;
+ const struct if_options *ifo;
+ uint8_t len;
+ char buf[(sizeof(ifo->clientid) - 1) * 3];
+ int r;
+
+ r = dhcp_initstate(ifp);
+ if (r == -1)
+ return -1;
+ else if (r == 1) {
/* Now is a good time to find IPv4 routes */
if_initrt(ifp->ctx, AF_INET);
}
+ state = D_STATE(ifp);
state->state = DHS_INIT;
state->reason = "PREINIT";
state->nakoff = 0;
return;
}
- if (ifo->options & DHCPCD_DHCP && dhcp_open(ifp) == -1)
+ if (ifo->options & DHCPCD_DHCP && dhcp_openbpf(ifp) == -1)
return;
if (ifo->options & DHCPCD_INFORM) {
#endif
eloop_timeout_delete(ifp->ctx->eloop, dhcp_start1, ifp);
+
+ if (state != NULL && state->added) {
+ rt_build(ifp->ctx, AF_INET);
+#ifdef ARP
+ arp_announceaddr(ifp->ctx, &state->addr->addr);
+#endif
+ }
}
void
ifp = ia->iface;
state = D_STATE(ifp);
- if (state == NULL)
+ if (state == NULL || state->state == DHS_NONE)
return;
if (cmd == RTM_DELADDR) {
return 0;
}
-int
-ipv4_preferanother(struct interface *ifp)
-{
- struct dhcp_state *state = D_STATE(ifp), *nstate;
- struct interface *ifn;
- int preferred;
-
- if (state == NULL)
- return 0;
-
- preferred = 0;
- if (!state->added)
- goto out;
-
- TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
- if (ifn == ifp)
- break; /* We are already the most preferred */
- nstate = D_STATE(ifn);
- if (nstate && !nstate->added &&
- state->addr != NULL &&
- nstate->lease.addr.s_addr == state->addr->addr.s_addr)
- {
- preferred = 1;
- delete_address(ifp);
- if (ifn->options->options & DHCPCD_ARP)
- dhcp_bind(ifn);
- else {
- ipv4_daddaddr(ifn, &nstate->lease);
- nstate->added = STATE_ADDED;
- }
- break;
- }
- }
-
-out:
- rt_build(ifp->ctx, AF_INET);
- return preferred;
-}
-
void
ipv4_applyaddr(void *arg)
{
- struct interface *ifp = arg, *ifn;
- struct dhcp_state *state = D_STATE(ifp), *nstate;
+ struct interface *ifp = arg;
+ struct dhcp_state *state = D_STATE(ifp);
struct dhcp_lease *lease;
struct if_options *ifo = ifp->options;
struct ipv4_addr *ia;
return;
lease = &state->lease;
- if_sortinterfaces(ifp->ctx);
-
if (state->new == NULL) {
if ((ifo->options & (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
(DHCPCD_EXITING | DHCPCD_PERSISTENT))
{
- if (state->added && !ipv4_preferanother(ifp)) {
+ if (state->added) {
+ struct in_addr addr;
+
+ addr = lease->addr;
delete_address(ifp);
rt_build(ifp->ctx, AF_INET);
+#ifdef ARP
+ /* Announce the preferred address to
+ * kick ARP caches. */
+ arp_announceaddr(ifp->ctx, &addr);
+#endif
}
script_runreason(ifp, state->reason);
} else
return;
}
- /* Ensure only one interface has the address */
- r = 0;
- TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
- if (ifn == ifp) {
- r = 1; /* past ourselves */
- continue;
- }
- nstate = D_STATE(ifn);
- if (nstate && nstate->added &&
- nstate->addr &&
- nstate->addr->addr.s_addr == lease->addr.s_addr)
- {
- if (r == 0) {
- loginfox("%s: preferring %s on %s",
- ifp->name,
- inet_ntoa(lease->addr),
- ifn->name);
- return;
- }
- loginfox("%s: preferring %s on %s",
- ifn->name,
- inet_ntoa(lease->addr),
- ifp->name);
- ipv4_deladdr(nstate->addr, 0);
- break;
- }
- }
-
- /* Does another interface already have the address from a prior boot? */
- if (ifn == NULL) {
- TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
- if (ifn == ifp)
- continue;
- ia = ipv4_iffindaddr(ifn, &lease->addr, NULL);
- if (ia != NULL)
- ipv4_deladdr(ia, 0);
- }
- }
-
/* If the netmask or broadcast is different, re-add the addresss */
ia = ipv4_iffindaddr(ifp, &lease->addr, NULL);
if (ia &&
rt_build(ifp->ctx, AF_INET);
#ifdef ARP
- /* Announce the address */
- if (ifo->options & DHCPCD_ARP) {
- struct arp_state *astate;
-
- if ((astate = arp_find(ifp, &state->addr->addr)) != NULL)
- arp_announce(astate);
- }
+ arp_announceaddr(ifp->ctx, &state->addr->addr);
#endif
if (state->state == DHS_BOUND) {