Address advertisemnt was used to allow IP address sharing to work.
It also required the DHCP server to allow the same IP for many
hosts, which modern DHCP servers deny you from doing.
Lastly, there are niggles with the implementation that are
impossible to fully fix due to how the various protocols work,
especially ARP.
All platforms dhcpcd supports allow better ways of doing this,
such as bonding (Linux), trunk(4) (OpenBSD), lagg(4) (Other BSDs).
ARP advertisements will only be made when addresses are added
OR defended against for kernels without RFC 5227 support.
unsigned int bpf_flags)
{
size_t fl = bpf_frame_header_len(ifp), falen;
- const struct interface *ifn;
struct arphdr ar;
struct arp_msg arm;
const struct iarp_state *state;
if ((size_t)((hw_t + ar.ar_hln + ar.ar_pln) - data) > len)
return;
/* Ignore messages from ourself */
- TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
- if (ar.ar_hln == ifn->hwlen &&
- memcmp(hw_s, ifn->hwaddr, ifn->hwlen) == 0)
- break;
- }
- if (ifn) {
+ if (ar.ar_hln == ifp->hwlen &&
+ memcmp(hw_s, ifp->hwaddr, ifp->hwlen) == 0)
+ {
#ifdef ARP_DEBUG
logdebugx("%s: ignoring ARP from self", ifp->name);
#endif
return NULL;
}
+#ifndef KERNEL_RFC5227
static void
arp_announced(void *arg)
{
return arp_ifannounceaddr(iff, ia);
}
+#endif
struct arp_state *
arp_new(struct interface *ifp, const struct in_addr *addr)
}
}
-#ifdef ARP
-static int
-dhcp_activeaddr(const struct interface *ifp, const struct in_addr *addr)
-{
- const struct interface *ifp1;
- const struct dhcp_state *state;
-
- TAILQ_FOREACH(ifp1, ifp->ctx->ifaces, next) {
- if (ifp1 == ifp)
- continue;
- if ((state = D_CSTATE(ifp1)) == NULL)
- continue;
- switch(state->state) {
- case DHS_REBOOT:
- case DHS_RENEW:
- case DHS_REBIND:
- case DHS_BOUND:
- case DHS_INFORM:
- break;
- default:
- continue;
- }
- if (state->lease.addr.s_addr == addr->s_addr)
- return 1;
- }
- return 0;
-}
-#endif
-
static void
dhcp_reboot(struct interface *ifp)
{
struct if_options *ifo;
struct dhcp_state *state = D_STATE(ifp);
-#ifdef ARP
- struct ipv4_addr *ia;
-#endif
if (state == NULL || state->state == DHS_NONE)
return;
loginfox("%s: rebinding lease of %s",
ifp->name, inet_ntoa(state->lease.addr));
-#ifdef ARP
-#ifndef KERNEL_RFC5227
+#if defined(ARP) && !defined(KERNEL_RFC5227)
/* Create the DHCP ARP state so we can defend it. */
(void)dhcp_arp_new(ifp, &state->lease.addr);
#endif
- /* If the address exists on the interface and no other interface
- * is currently using it then announce it to ensure this
- * interface gets the reply. */
- ia = ipv4_iffindaddr(ifp, &state->lease.addr, NULL);
- if (ia != NULL &&
- !(ifp->ctx->options & DHCPCD_TEST) &&
-#ifdef IN_IFF_NOTUSEABLE
- !(ia->addr_flags & IN_IFF_NOTUSEABLE) &&
-#endif
- dhcp_activeaddr(ifp, &state->lease.addr) == 0)
- arp_ifannounceaddr(ifp, &state->lease.addr);
-#endif
-
dhcp_new_xid(ifp);
state->lease.server.s_addr = INADDR_ANY;
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
eloop_timeout_delete(ifp->ctx->eloop, dhcp_start1, ifp);
- if (state != NULL && state->added) {
+ if (state != NULL && state->added)
rt_build(ifp->ctx, AF_INET);
-#ifdef ARP
- if (ifp->options->options & DHCPCD_ARP)
- arp_announceaddr(ifp->ctx, &state->addr->addr);
-#endif
- }
}
struct ipv4_addr *
if (ia->addr_flags & IN6_IFF_DUPLICATED)
logwarnx("%s: DAD detected %s", ia->iface->name, ia->saddr);
-#ifdef ND6_ADVERTISE
- else
- ipv6nd_advertise(ia);
-#endif
if (completed)
return;
dhcp6_abort(struct interface *ifp)
{
struct dhcp6_state *state;
-#ifdef ND6_ADVERTISE
- struct ipv6_addr *ia;
-#endif
eloop_timeout_delete(ifp->ctx->eloop, dhcp6_start1, ifp);
state = D6_STATE(ifp);
if (state == NULL)
return;
-#ifdef ND6_ADVERTISE
- TAILQ_FOREACH(ia, &state->addrs, next) {
- ipv6nd_advertise(ia);
- }
-#endif
-
eloop_timeout_delete(ifp->ctx->eloop, dhcp6_startdiscover, ifp);
eloop_timeout_delete(ifp->ctx->eloop, dhcp6_senddiscover, ifp);
eloop_timeout_delete(ifp->ctx->eloop, dhcp6_startinform, ifp);
if (state->added) {
ipv4_deladdr(state->addr, 0);
rt_build(ifp->ctx, AF_INET);
-#ifdef ARP
- /* Announce the preferred address to
- * kick ARP caches. */
- arp_announceaddr(ifp->ctx,&lease->addr);
-#endif
}
script_runreason(ifp, state->reason);
} else
rt_build(ifp->ctx, AF_INET);
-#ifdef ARP
- arp_announceaddr(ifp->ctx, &state->addr->addr);
-#endif
-
if (state->state == DHS_BOUND) {
script_runreason(ifp, state->reason);
dhcpcd_daemonise(ifp->ctx);
return 5;
}
+#ifndef KERNEL_RFC5227
static void
ipv4ll_announced_arp(struct arp_state *astate)
{
#endif
}
-#ifndef KERNEL_RFC5227
/* This is the callback by ARP freeing */
static void
ipv4ll_free_arp(struct arp_state *astate)
{
struct ipv4ll_state *state;
struct ipv4_addr *ia;
+#ifndef KERNEL_RFC5227
struct arp_state *astate;
+#endif
state = IPV4LL_STATE(ifp);
ia = ipv4_iffindaddr(ifp, &state->pickedaddr, &inaddr_llmask);
}
rt_build(ifp->ctx, AF_INET);
+#ifndef KERNEL_RFC5227
astate = arp_announceaddr(ifp->ctx, &ia->addr);
if (astate != NULL)
astate->announced_cb = ipv4ll_announced_arp;
+#endif
script_runreason(ifp, "IPV4LL");
dhcpcd_daemonise(ifp->ctx);
}
break;
}
}
-
-#ifdef ND6_ADVERTISE
- /* Advertise the address if it exists on another interface. */
- ipv6nd_advertise(ia);
-#endif
}
static int
int loglevel;
struct ipv6_state *state;
struct ipv6_addr *ia2;
-#ifdef ND6_ADVERTISE
- bool vltime_was_zero = ia->prefix_vltime == 0;
-#endif
#ifdef __sun
/* If we re-add then address on Solaris then the prefix
if (ia->flags & IPV6_AF_DADCOMPLETED) {
logdebugx("%s: IP address %s already exists",
ia->iface->name, ia->saddr);
-#ifdef ND6_ADVERTISE
- goto advertise;
-#else
return 0;
-#endif
}
#endif
if (ia2 == NULL) {
if ((ia2 = malloc(sizeof(*ia2))) == NULL) {
logerr(__func__);
- goto advertise; /* Well, we did add the address */
+ return 0; /* Well, we did add the address */
}
memcpy(ia2, ia, sizeof(*ia2));
TAILQ_INSERT_TAIL(&state->addrs, ia2, next);
}
-advertise:
-#ifdef ND6_ADVERTISE
- /* Re-advertise the preferred address to be safe. */
- if (!vltime_was_zero)
- ipv6nd_advertise(ia);
-#endif
-
return 0;
}
case RTM_DELADDR:
if (ia != NULL) {
TAILQ_REMOVE(&state->addrs, ia, next);
-#ifdef ND6_ADVERTISE
- /* Advertise the address if it exists on
- * another interface. */
- ipv6nd_advertise(ia);
-#endif
/* We'll free it at the end of the function. */
}
break;
int
ipv6_start(struct interface *ifp)
{
-#if defined(ND6_ADVERTISE) || defined(IPV6_POLLADDRFLAG)
+#ifdef IPV6_POLLADDRFLAG
struct ipv6_state *state;
/* We need to update the address flags. */
if ((state = IPV6_STATE(ifp)) != NULL) {
struct ipv6_addr *ia;
-#ifdef IPV6_POLLADDRFLAG
const char *alias;
int flags;
-#endif
TAILQ_FOREACH(ia, &state->addrs, next) {
-#ifdef IPV6_POLLADDRFLAG
#ifdef ALIAS_ADDR
alias = ia->alias;
#else
flags = if_addrflags6(ia->iface, &ia->addr, alias);
if (flags != -1)
ia->addr_flags = flags;
-#endif
- /* hwaddr could have changed */
- ia->flags &= ~IPV6_AF_ADVERTISED;
}
}
#endif
# define IN6_IFF_DETACHED 0
#endif
-/*
- * ND6 Advertising is only used for IP address sharing to prefer
- * the address on a specific interface or when the hardware address
- * of the interface changes.
- * This just fails to work on OpenBSD and causes erroneous duplicate
- * address messages on BSD's other then DragonFly and NetBSD.
- */
-#if !defined(SMALL) && \
- ((defined(__DragonFly_version) && __DragonFly_version >= 500703) || \
- (defined(__NetBSD_Version__) && __NetBSD_Version__ >= 899002800) || \
- defined(__linux__) || defined(__sun))
-# define ND6_ADVERTISE
-#endif
-
#ifdef INET6
TAILQ_HEAD(ipv6_addrhead, ipv6_addr);
struct ipv6_addr {
logwarnx("%s: no IPv6 Routers available", ifp->name);
}
-#ifdef ND6_ADVERTISE
-static void
-ipv6nd_sendadvertisement(void *arg)
-{
- struct ipv6_addr *ia = arg;
- struct interface *ifp = ia->iface;
- struct dhcpcd_ctx *ctx = ifp->ctx;
- struct sockaddr_in6 dst = {
- .sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT,
- .sin6_scope_id = ifp->index,
- };
- struct iovec iov = { .iov_base = ia->na, .iov_len = ia->na_len };
- union {
- struct cmsghdr hdr;
- uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
- } cmsgbuf = { .buf = { 0 } };
- struct msghdr msg = {
- .msg_name = &dst, .msg_namelen = sizeof(dst),
- .msg_iov = &iov, .msg_iovlen = 1,
- .msg_control = cmsgbuf.buf, .msg_controllen = sizeof(cmsgbuf.buf),
- };
- struct cmsghdr *cm;
- struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
- const struct rs_state *state = RS_CSTATE(ifp);
- int s;
-
- if (state == NULL || !if_is_link_up(ifp))
- goto freeit;
-
-#ifdef SIN6_LEN
- dst.sin6_len = sizeof(dst);
-#endif
-
- /* Set the outbound interface. */
- cm = CMSG_FIRSTHDR(&msg);
- assert(cm != NULL);
- cm->cmsg_level = IPPROTO_IPV6;
- cm->cmsg_type = IPV6_PKTINFO;
- cm->cmsg_len = CMSG_LEN(sizeof(pi));
- memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
- logdebugx("%s: sending NA for %s", ifp->name, ia->saddr);
-
-#ifdef PRIVSEP
- if (IN_PRIVSEP(ifp->ctx)) {
- if (ps_inet_sendnd(ifp, &msg) == -1)
- logerr(__func__);
- goto sent;
- }
-#endif
-#ifdef __sun
- s = state->nd_fd;
-#else
- s = ctx->nd_fd;
-#endif
- if (sendmsg(s, &msg, 0) == -1)
- logerr(__func__);
-
-#ifdef PRIVSEP
-sent:
-#endif
- if (++ia->na_count < MAX_NEIGHBOR_ADVERTISEMENT) {
- eloop_timeout_add_sec(ctx->eloop,
- state->retrans / 1000, ipv6nd_sendadvertisement, ia);
- return;
- }
-
-freeit:
- free(ia->na);
- ia->na = NULL;
- ia->na_count = 0;
-}
-
-void
-ipv6nd_advertise(struct ipv6_addr *ia)
-{
- struct dhcpcd_ctx *ctx;
- struct interface *ifp;
- struct ipv6_state *state;
- struct ipv6_addr *iap, *iaf;
- bool found_another = false;
- struct nd_neighbor_advert *na;
-
- if (IN6_IS_ADDR_MULTICAST(&ia->addr))
- return;
-
-#ifdef __sun
- if (!(ia->flags & IPV6_AF_AUTOCONF) && ia->flags & IPV6_AF_RAPFX)
- return;
-#endif
-
- ctx = ia->iface->ctx;
- /* Find the most preferred address to advertise. */
- iaf = NULL;
- TAILQ_FOREACH(ifp, ctx->ifaces, next) {
- state = IPV6_STATE(ifp);
- if (state == NULL)
- continue;
-
- TAILQ_FOREACH(iap, &state->addrs, next) {
- if (!IN6_ARE_ADDR_EQUAL(&iap->addr, &ia->addr))
- continue;
-
- if (iaf != NULL)
- found_another = true;
-
- /* Don't advertise what we can't use. */
- if (iap->prefix_vltime == 0 ||
- iap->addr_flags & IN6_IFF_NOTUSEABLE ||
- !if_is_link_up(ifp))
- continue;
-
- if (iaf == NULL ||
- iaf->iface->metric > iap->iface->metric)
- iaf = iap;
- }
- }
-
- /* If we have already advertised the address, return. */
- if (iaf == NULL || iaf->flags & IPV6_AF_ADVERTISED)
- return;
-
- /* Now cancel any other advertisements for the same address. */
- if (found_another) {
- TAILQ_FOREACH(ifp, ctx->ifaces, next) {
- state = IPV6_STATE(ifp);
- if (state == NULL)
- continue;
-
- TAILQ_FOREACH(iap, &state->addrs, next) {
- if (!IN6_ARE_ADDR_EQUAL(&iap->addr, &ia->addr))
- continue;
-
- iap->flags &= ~IPV6_AF_ADVERTISED;
- eloop_timeout_delete(ctx->eloop,
- ipv6nd_sendadvertisement, iap);
- }
- }
- } else {
- eloop_timeout_delete(ctx->eloop,
- ipv6nd_sendadvertisement, iaf);
- }
-
- /* Make the packet. */
- ifp = iaf->iface;
- iaf->na_len = sizeof(*na);
- if (ifp->hwlen != 0)
- iaf->na_len += (size_t)ROUNDUP8(ifp->hwlen + 2);
- na = calloc(1, iaf->na_len);
- if (na == NULL) {
- logerr(__func__);
- return;
- }
-
- na->nd_na_type = ND_NEIGHBOR_ADVERT;
- na->nd_na_flags_reserved = ND_NA_FLAG_OVERRIDE;
-#ifdef PRIVSEP_SYSCTL
- if (IN_PRIVSEP(ctx)) {
- if (ps_root_ip6forwarding(ctx, ifp->name) != 0)
- na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
- } else
-#endif
- if (ip6_forwarding(ifp->name) > 0)
- na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
- na->nd_na_target = ia->addr;
-
- if (ifp->hwlen != 0) {
- struct nd_opt_hdr *opt;
-
- opt = (struct nd_opt_hdr *)(na + 1);
- opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
- opt->nd_opt_len = (uint8_t)((ROUNDUP8(ifp->hwlen + 2)) >> 3);
- memcpy(opt + 1, ifp->hwaddr, ifp->hwlen);
- }
-
- iaf->na_count = 0;
- free(iaf->na);
- iaf->na = na;
- iaf->flags |= IPV6_AF_ADVERTISED;
- ipv6nd_sendadvertisement(iaf);
-}
-#elif !defined(SMALL)
-#warning kernel does not support userland sending ND6 advertisements
-#endif /* ND6_ADVERTISE */
-
static void
ipv6nd_expire(void *arg)
{
ipv6nd_scriptrun(rap);
}
}
-#ifdef ND6_ADVERTISE
- ipv6nd_advertise(ia);
-#endif
}
}