void
arp_report_conflicted(const struct arp_state *astate, const struct arp_msg *amsg)
{
- char buf[HWADDR_LEN * 3];
- logger(astate->iface->ctx, LOG_ERR, "%s: hardware address %s claims %s",
- astate->iface->name,
- hwaddr_ntoa(amsg->sha, astate->iface->hwlen, buf, sizeof(buf)),
- inet_ntoa(astate->failed));
+ if (amsg) {
+ char buf[HWADDR_LEN * 3];
+
+ logger(astate->iface->ctx, LOG_ERR,
+ "%s: hardware address %s claims %s",
+ astate->iface->name,
+ hwaddr_ntoa(amsg->sha, astate->iface->hwlen,
+ buf, sizeof(buf)),
+ inet_ntoa(astate->failed));
+ } else
+ logger(astate->iface->ctx, LOG_ERR,
+ "%s: DAD detected %s",
+ astate->iface->name, inet_ntoa(astate->failed));
}
static void
}
state = D_STATE(ifp);
astate->iface = ifp;
- astate->addr = *addr;
+ if (addr)
+ astate->addr = *addr;
TAILQ_INSERT_TAIL(&state->arp_states, astate, next);
return astate;
}
#endif
}
}
+
+void
+arp_handleifa(int cmd, struct interface *ifp, const struct in_addr *addr,
+ int flags)
+{
+#ifdef IN_IFF_DUPLICATED
+ struct dhcp_state *state = D_STATE(ifp);
+ struct arp_state *astate, *asn;
+
+ if (cmd != RTM_NEWADDR || (state = D_STATE(ifp)) == NULL)
+ return;
+
+ TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, asn) {
+ if (astate->addr.s_addr == addr->s_addr) {
+ if (flags & IN_IFF_DUPLICATED) {
+ if (astate->conflicted_cb)
+ astate->conflicted_cb(astate, NULL);
+ } else if (!(flags & IN_IFF_TENTATIVE)) {
+ if (astate->probed_cb)
+ astate->probed_cb(astate);
+ }
+ }
+ }
+#endif
+}
void arp_free(struct arp_state *);
void arp_free_but(struct arp_state *);
void arp_close(struct interface *);
+
+void arp_handleifa(int, struct interface *, const struct in_addr *, int);
#else
#define arp_close(a) {}
#endif
dhcp_discover(ifp);
}
-void
+static void
dhcp_decline(struct interface *ifp)
{
send_renew(ifp);
}
+#ifndef IN_IFF_TENTATIVE
static void
dhcp_arp_announced(struct arp_state *astate)
{
arp_close(astate->iface);
}
+#endif
static void
dhcp_rebind(void *arg)
applyaddr:
ipv4_applyaddr(ifp);
- if (dhcpcd_daemonise(ifp->ctx))
- return;
- if (ifo->options & DHCPCD_ARP) {
+ if (ifo->options & DHCPCD_ARP &&
+ !(ifp->ctx->options & DHCPCD_FORKED))
+ {
+#ifdef IN_IFF_TENTATIVE
+ if (astate)
+ arp_free_but(astate);
+#else
if (state->added) {
if (astate == NULL) {
astate = arp_new(ifp, &state->addr);
}
} else if (!ipv4ll)
arp_close(ifp);
+#endif
}
}
}
dhcp_close(astate->iface);
eloop_timeout_delete(astate->iface->ctx->eloop, NULL, astate->iface);
+#ifdef IN_IFF_TENTATIVE
+ ipv4_finaliseaddr(astate->iface);
+ arp_close(astate->iface);
+#else
dhcp_bind(astate->iface, astate);
+#endif
}
static void
ifo = astate->iface->options;
if (state->arping_index &&
state->arping_index <= ifo->arping_len &&
+ amsg &&
(amsg->sip.s_addr == ifo->arping[state->arping_index - 1] ||
(amsg->sip.s_addr == 0 &&
amsg->tip.s_addr == ifo->arping[state->arping_index - 1])))
dhcpcd_startinterface(astate->iface);
}
- if (state->offer == NULL)
- return;
-
- /* RFC 2131 3.1.5, Client-server interaction */
- if (amsg->sip.s_addr == state->offer->yiaddr ||
- (amsg->sip.s_addr == 0 && amsg->tip.s_addr == state->offer->yiaddr))
+ /* RFC 2131 3.1.5, Client-server interaction
+ * NULL amsg means IN_IFF_DUPLICATED */
+ if (amsg == NULL || (state->offer &&
+ (amsg->sip.s_addr == state->offer->yiaddr ||
+ (amsg->sip.s_addr == 0 &&
+ amsg->tip.s_addr == state->offer->yiaddr))))
{
- astate->failed.s_addr = state->offer->yiaddr;
+ if (amsg)
+ astate->failed.s_addr = state->offer->yiaddr;
+ else
+ astate->failed = astate->addr;
arp_report_conflicted(astate, amsg);
unlink(state->leasefile);
if (!state->lease.frominfo)
unsigned int i;
size_t auth_len;
char *msg;
+ struct arp_state *astate;
/* We may have found a BOOTP server */
if (get_option_uint8(ifp->ctx, &type, dhcp, DHO_MESSAGETYPE) == -1)
if ((type == 0 || type == DHCP_OFFER) &&
(state->state == DHS_DISCOVER || state->state == DHS_IPV4LL_BOUND))
{
+#ifdef IN_IFF_DUPLICATED
+ struct ipv4_addr *ia;
+#endif
+
lease->frominfo = 0;
lease->addr.s_addr = dhcp->yiaddr;
lease->cookie = dhcp->cookie;
&lease->server, dhcp, DHO_SERVERID) != 0)
lease->server.s_addr = INADDR_ANY;
log_dhcp(LOG_INFO, "offered", ifp, dhcp, from);
+#ifdef IN_IFF_DUPLICATED
+ ia = ipv4_iffindaddr(ifp, &lease->addr, NULL);
+ if (ia && ia->addr_flags & IN_IFF_DUPLICATED) {
+ log_dhcp(LOG_WARNING, "declined duplicate address",
+ ifp, dhcp, from);
+ dhcp_decline(ifp);
+ eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+ eloop_timeout_add_sec(ifp->ctx->eloop,
+ DHCP_RAND_MAX, dhcp_discover, ifp);
+ return;
+ }
+#endif
free(state->offer);
state->offer = dhcp;
*dhcpp = NULL;
lease->frominfo = 0;
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
-
+ astate = NULL;
if (ifo->options & DHCPCD_ARP &&
state->addr.s_addr != state->offer->yiaddr)
{
* then we can't ARP for duplicate detection. */
addr.s_addr = state->offer->yiaddr;
if (!ipv4_iffindaddr(ifp, &addr, NULL)) {
- struct arp_state *astate;
-
astate = arp_new(ifp, &addr);
if (astate) {
astate->probed_cb = dhcp_arp_probed;
astate->conflicted_cb = dhcp_arp_conflicted;
+#ifndef IN_IFF_TENTATIVE
arp_probe(astate);
+#endif
}
+#ifndef IN_IFF_TENTATIVE
return;
+#endif
}
}
- dhcp_bind(ifp, NULL);
+ dhcp_bind(ifp, astate);
}
static size_t
state->offer = read_lease(ifp);
/* Check the saved lease matches the type we want */
if (state->offer) {
+ struct ipv4_addr *ia;
+ struct in_addr addr;
+
+ addr.s_addr = state->offer->yiaddr;
+ ia = ipv4_iffindaddr(ifp, &addr, NULL);
if ((IS_BOOTP(ifp, state->offer) &&
!(ifo->options & DHCPCD_BOOTP)) ||
+#ifdef IN_IFF_DUPLICATED
+ (ia && ia->addr_flags & IN_IFF_DUPLICATED) ||
+#endif
(!IS_BOOTP(ifp, state->offer) &&
ifo->options & DHCPCD_BOOTP))
{
}
void
-dhcp_handleifa(int type, struct interface *ifp,
+dhcp_handleifa(int cmd, struct interface *ifp,
const struct in_addr *addr,
const struct in_addr *net,
- const struct in_addr *dst)
+ const struct in_addr *dst,
+ __unused int flags)
{
struct dhcp_state *state;
struct if_options *ifo;
if (state == NULL)
return;
- if (type == RTM_DELADDR) {
+ if (cmd == RTM_DELADDR) {
if (state->new &&
(state->new->yiaddr == addr->s_addr ||
(state->new->yiaddr == INADDR_ANY &&
return;
}
- if (type != RTM_NEWADDR)
+ if (cmd != RTM_NEWADDR)
return;
ifo = ifp->options;
int valid_dhcp_packet(unsigned char *);
void dhcp_handleifa(int, struct interface *,
- const struct in_addr *, const struct in_addr *, const struct in_addr *);
+ const struct in_addr *, const struct in_addr *, const struct in_addr *,
+ int);
void dhcp_drop(struct interface *, const char *);
void dhcp_start(struct interface *);
void dhcp_stop(struct interface *);
-void dhcp_decline(struct interface *);
void dhcp_discover(void *);
void dhcp_inform(struct interface *);
void dhcp_bind(struct interface *, struct arp_state *);
free(buf);
return 0;
}
+
+int
+if_addrflags(const struct in_addr *addr, const struct interface *ifp)
+{
+#ifdef SIOCGIFAFLAG_IN
+ int s, flags;
+ struct ifreq ifr;
+ struct sockaddr_in *sin;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ flags = -1;
+ if (s != -1) {
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
+ sin = (struct sockaddr_in *)(void *)&ifr.ifr_addr;
+ sin->sin_family = AF_INET;
+ sin->sin_addr = *addr;
+ if (ioctl(s, SIOCGIFAFLAG_IN, &ifr) != -1)
+ flags = ifr.ifr_addrflags;
+ close(s);
+ }
+ return flags;
+#else
+ errno = ENOTSUP;
+ return 0;
+#endif
+}
#endif
#ifdef INET6
struct rt6 rt6;
struct in6_addr ia6, net6;
struct sockaddr_in6 *sin6;
+#endif
+#if (defined(INET) && defined(IN_IFF_TENTATIVE)) || defined(INET6)
int ifa_flags;
#endif
COPYOUT(rt.dest, rti_info[RTAX_IFA]);
COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
COPYOUT(rt.gate, rti_info[RTAX_BRD]);
+ if (rtm->rtm_type == RTM_NEWADDR) {
+ ifa_flags = if_addrflags(&rt.dest, ifp);
+ if (ifa_flags == -1)
+ break;
+ } else
+ ifa_flags = 0;
ipv4_handleifa(ctx, rtm->rtm_type,
NULL, ifp->name,
- &rt.dest, &rt.net, &rt.gate);
+ &rt.dest, &rt.net, &rt.gate, ifa_flags);
break;
#endif
#ifdef INET6
return send_netlink(ifp->ctx, ifp,
NETLINK_ROUTE, &nlm.hdr, &_if_initrt);
}
+
+int
+if_addrflags(__unused const struct in_addr *addr,
+ __unused const struct interface *ifp)
+{
+
+ /* Linux has no support for IPv4 address flags */
+ return 0;
+}
#endif
#ifdef INET6
(void *)ifa->ifa_dstaddr;
else
dst = NULL;
+ ifa_flags = if_addrflags(&addr->sin_addr, ifp);
ipv4_handleifa(ctx, RTM_NEWADDR, ifs, ifa->ifa_name,
&addr->sin_addr,
&net->sin_addr,
- dst ? &dst->sin_addr : NULL);
+ dst ? &dst->sin_addr : NULL, ifa_flags);
break;
#endif
#ifdef INET6
#include <net/if.h>
#include <net/route.h> /* for RTM_ADD et all */
#include <netinet/in.h>
+#ifdef BSD
+#include <netinet/in_var.h> /* for IN_IFF_TENTATIVE et all */
+#endif
/* Some systems have route metrics.
* OpenBSD route priority is not this. */
#define if_deladdress(ifp, addr, net) \
if_address(ifp, addr, net, NULL, -1)
+int if_addrflags(const struct in_addr *, const struct interface *);
+
int if_route(unsigned char, const struct rt *rt);
int if_initrt(struct interface *);
#endif
static int
ipv4_addaddr(struct interface *ifp, const struct dhcp_lease *lease)
{
- int r;
+ struct ipv4_state *state;
+ struct ipv4_addr *ia;
+ if ((state = ipv4_getstate(ifp)) == NULL) {
+ logger(ifp->ctx, LOG_ERR, "%s: ipv4_getstate: %m", __func__);
+ return -1;
+ }
if (ifp->options->options & DHCPCD_NOALIAS) {
- struct ipv4_state *state;
- struct ipv4_addr *ap, *apn;
+ struct ipv4_addr *ian;
- state = IPV4_STATE(ifp);
- TAILQ_FOREACH_SAFE(ap, &state->addrs, next, apn) {
- if (ap->addr.s_addr != lease->addr.s_addr)
- delete_address1(ifp, &ap->addr, &ap->net);
+ TAILQ_FOREACH_SAFE(ia, &state->addrs, next, ian) {
+ if (ia->addr.s_addr != lease->addr.s_addr)
+ delete_address1(ifp, &ia->addr, &ia->net);
}
}
+ if ((ia = malloc(sizeof(*ia))) == NULL) {
+ logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
+ return -1;
+ }
+
logger(ifp->ctx, LOG_DEBUG, "%s: adding IP address %s/%d",
ifp->name, inet_ntoa(lease->addr),
inet_ntocidr(lease->net));
- r = if_addaddress(ifp, &lease->addr, &lease->net, &lease->brd);
- if (r == -1 && errno != EEXIST)
- logger(ifp->ctx, LOG_ERR, "%s: if_addaddress: %m", __func__);
- return r;
+ if (if_addaddress(ifp, &lease->addr, &lease->net, &lease->brd) == -1) {
+ if (errno != EEXIST)
+ logger(ifp->ctx, LOG_ERR, "%s: if_addaddress: %m",
+ __func__);
+ free(ia);
+ return -1;
+ }
+
+ ia->iface = ifp;
+ ia->addr = lease->addr;
+ ia->net = lease->net;
+#ifdef IN_IFF_TENTATIVE
+ ia->addr_flags = IN_IFF_TENTATIVE;
+#endif
+ TAILQ_INSERT_TAIL(&state->addrs, ia, next);
+ return 0;
+}
+
+static void
+ipv4_finalisert(struct interface *ifp)
+{
+ const struct dhcp_state *state = D_CSTATE(ifp);
+
+ /* Find any freshly added routes, such as the subnet route.
+ * We do this because we cannot rely on recieving the kernel
+ * notification right now via our link socket. */
+ if_initrt(ifp);
+ ipv4_buildroutes(ifp->ctx);
+ script_runreason(ifp, state->reason);
+
+ dhcpcd_daemonise(ifp->ctx);
+}
+
+void
+ipv4_finaliseaddr(struct interface *ifp)
+{
+ struct dhcp_state *state = D_STATE(ifp);
+ struct dhcp_lease *lease;
+
+ lease = &state->lease;
+
+ /* Delete the old address if different */
+ if (state->addr.s_addr != lease->addr.s_addr &&
+ state->addr.s_addr != 0 &&
+ ipv4_iffindaddr(ifp, &lease->addr, NULL))
+ delete_address(ifp);
+
+ state->added = STATE_ADDED;
+ state->defend = 0;
+ state->addr.s_addr = lease->addr.s_addr;
+ state->net.s_addr = lease->net.s_addr;
+ ipv4_finalisert(ifp);
}
void
struct dhcp_lease *lease;
struct if_options *ifo = ifp->options;
struct ipv4_addr *ap;
- struct ipv4_state *istate = NULL;
int r;
if (state == NULL)
ifn->name);
state->addr.s_addr = lease->addr.s_addr;
state->net.s_addr = lease->net.s_addr;
- goto routes;
+ ipv4_finalisert(ifp);
}
logger(ifp->ctx, LOG_INFO, "%s: preferring %s on %s",
ifn->name,
r = ipv4_addaddr(ifp, lease);
if (r == -1 && errno != EEXIST)
return;
- istate = ipv4_getstate(ifp);
- ap = malloc(sizeof(*ap));
- ap->iface = ifp;
- ap->addr = lease->addr;
- ap->net = lease->net;
- ap->dst.s_addr = INADDR_ANY;
- TAILQ_INSERT_TAIL(&istate->addrs, ap, next);
}
- /* Now delete the old address if different */
- if (state->addr.s_addr != lease->addr.s_addr &&
- state->addr.s_addr != 0 &&
- ipv4_iffindaddr(ifp, &lease->addr, NULL))
- delete_address(ifp);
-
- state->added = STATE_ADDED;
- state->defend = 0;
- state->addr.s_addr = lease->addr.s_addr;
- state->net.s_addr = lease->net.s_addr;
+#ifdef IN_IFF_TENTATIVE
+ ap = ipv4_iffindaddr(ifp, &lease->addr, NULL);
+ if (ap == NULL) {
+ logger(ifp->ctx, LOG_ERR, "%s: added address vanished",
+ ifp->name);
+ return;
+ } else if (ap->addr_flags & IN_IFF_TENTATIVE)
+ return;
+#endif
-routes:
- /* Find any freshly added routes, such as the subnet route.
- * We do this because we cannot rely on recieving the kernel
- * notification right now via our link socket. */
- if_initrt(ifp);
- ipv4_buildroutes(ifp->ctx);
- script_runreason(ifp, state->reason);
+ ipv4_finaliseaddr(ifp);
}
void
ipv4_handleifa(struct dhcpcd_ctx *ctx,
- int type, struct if_head *ifs, const char *ifname,
+ int cmd, struct if_head *ifs, const char *ifname,
const struct in_addr *addr, const struct in_addr *net,
- const struct in_addr *dst)
+ const struct in_addr *dst, int flags)
{
struct interface *ifp;
struct ipv4_state *state;
errno = EINVAL;
return;
}
-
- TAILQ_FOREACH(ifp, ifs, next) {
- if (strcmp(ifp->name, ifname) == 0)
- break;
- }
- if (ifp == NULL) {
- errno = ESRCH;
+ if ((ifp = if_find(ifs, ifname)) == NULL)
return;
- }
- state = ipv4_getstate(ifp);
- if (state == NULL) {
+ if ((state = ipv4_getstate(ifp)) == NULL) {
errno = ENOENT;
return;
}
ap = ipv4_iffindaddr(ifp, addr, net);
- if (type == RTM_NEWADDR && ap == NULL) {
- ap = malloc(sizeof(*ap));
+ if (cmd == RTM_NEWADDR) {
if (ap == NULL) {
- logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
- return;
+ if ((ap = malloc(sizeof(*ap))) == NULL) {
+ logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
+ return;
+ }
+ ap->iface = ifp;
+ ap->addr = *addr;
+ ap->net = *net;
+ if (dst)
+ ap->dst.s_addr = dst->s_addr;
+ else
+ ap->dst.s_addr = INADDR_ANY;
+ TAILQ_INSERT_TAIL(&state->addrs, ap, next);
+ }
+ ap->addr_flags = flags;
+ } else if (cmd == RTM_DELADDR) {
+ if (ap) {
+ TAILQ_REMOVE(&state->addrs, ap, next);
+ free(ap);
}
- ap->iface = ifp;
- ap->addr.s_addr = addr->s_addr;
- ap->net.s_addr = net->s_addr;
- if (dst)
- ap->dst.s_addr = dst->s_addr;
- else
- ap->dst.s_addr = INADDR_ANY;
- TAILQ_INSERT_TAIL(&state->addrs, ap, next);
- } else if (type == RTM_DELADDR) {
- if (ap == NULL)
- return;
- TAILQ_REMOVE(&state->addrs, ap, next);
- free(ap);
}
- dhcp_handleifa(type, ifp, addr, net, dst);
+ dhcp_handleifa(cmd, ifp, addr, net, dst, flags);
+ arp_handleifa(cmd, ifp, addr, flags);
}
void
struct in_addr net;
struct in_addr dst;
struct interface *iface;
+ int addr_flags;
};
TAILQ_HEAD(ipv4_addrhead, ipv4_addr);
#define STATE_FAKE 0x02
void ipv4_buildroutes(struct dhcpcd_ctx *);
+void ipv4_finaliseaddr(struct interface *);
void ipv4_applyaddr(void *);
int ipv4_handlert(struct dhcpcd_ctx *, int, struct rt *);
void ipv4_freerts(struct rt_head *);
struct ipv4_addr *ipv4_iffindlladdr(struct interface *);
struct ipv4_addr *ipv4_findaddr(struct dhcpcd_ctx *, const struct in_addr *);
void ipv4_handleifa(struct dhcpcd_ctx *, int, struct if_head *, const char *,
- const struct in_addr *, const struct in_addr *, const struct in_addr *);
+ const struct in_addr *, const struct in_addr *, const struct in_addr *,
+ int);
void ipv4_freeroutes(struct rt_head *);
ipv4ll_probed(struct arp_state *astate)
{
struct dhcp_state *state = D_STATE(astate->iface);
- struct dhcp_message *offer;
+
+ if (state->state == DHS_IPV4LL_BOUND) {
+ ipv4_finaliseaddr(astate->iface);
+ return;
+ }
if (state->state != DHS_BOUND) {
+ struct dhcp_message *offer;
+
/* A DHCP lease could have already been offered.
- * Backup and replace once the IPv4LL addres is bound */
+ * Backup and replace once the IPv4LL address is bound */
offer = state->offer;
state->offer = ipv4ll_make_lease(astate->addr.s_addr);
if (state->offer == NULL)
ipv4ll_probe(void *arg)
{
+#ifdef IN_IFF_TENTATIVE
+ ipv4ll_probed(arg);
+#else
arp_probe(arg);
+#endif
}
static void
fail = 0;
/* RFC 3927 2.2.1, Probe Conflict Detection */
- if (amsg->sip.s_addr == astate->addr.s_addr ||
- (amsg->sip.s_addr == 0 && amsg->tip.s_addr == astate->addr.s_addr))
+ if (amsg == NULL ||
+ (amsg->sip.s_addr == astate->addr.s_addr ||
+ (amsg->sip.s_addr == 0 && amsg->tip.s_addr == astate->addr.s_addr)))
fail = astate->addr.s_addr;
/* RFC 3927 2.5, Conflict Defense */
if (IN_LINKLOCAL(htonl(state->addr.s_addr)) &&
- amsg->sip.s_addr == state->addr.s_addr)
+ amsg && amsg->sip.s_addr == state->addr.s_addr)
fail = state->addr.s_addr;
if (fail == 0)
}
if (astate->addr.s_addr == INADDR_ANY)
astate->addr.s_addr = ipv4ll_pick_addr(astate);
+#ifdef IN_IFF_TENTATIVE
+ ipv4ll_probed(astate);
+#else
arp_probe(astate);
+#endif
}
void