replacing an existing IPv4 address with the same values.
As such, we need to maintain a list of configured IPv4 addresses
for each interface so we know when to add it and when to skip it
to avoid receiving bogus RTM_DELADDR messages from ourself.
send_rebind(ifp);
}
-
void
dhcp_bind(void *arg)
{
state->offer = NULL;
get_lease(lease, state->new);
if (ifo->options & DHCPCD_STATIC) {
- syslog(LOG_INFO, "%s: using static address %s",
- iface->name, inet_ntoa(lease->addr));
+ syslog(LOG_INFO, "%s: using static address %s/%d",
+ iface->name, inet_ntoa(lease->addr),
+ inet_ntocidr(lease->net));
lease->leasetime = ~0U;
- lease->net.s_addr = ifo->req_mask.s_addr;
state->reason = "STATIC";
} else if (state->new->cookie != htonl(MAGIC_COOKIE)) {
syslog(LOG_INFO, "%s: using IPv4LL address %s",
}
struct dhcp_message *
-dhcp_message_new(struct in_addr *addr, struct in_addr *mask)
+dhcp_message_new(const struct in_addr *addr, const struct in_addr *mask)
{
struct dhcp_message *dhcp;
uint8_t *p;
return dhcp;
}
-static int
-handle_3rdparty(struct interface *ifp)
+static void
+dhcp_static(struct interface *ifp)
{
struct if_options *ifo;
struct dhcp_state *state;
- struct in_addr addr, net, dst;
+ state = D_STATE(ifp);
ifo = ifp->options;
- if (ifo->req_addr.s_addr != INADDR_ANY)
- return 0;
-
- if (ipv4_getaddress(ifp->name, &addr, &net, &dst) == 1)
- ipv4_handleifa(RTM_NEWADDR, ifp->name, &addr, &net, &dst);
- else {
+ if (ifo->req_addr.s_addr == INADDR_ANY) {
syslog(LOG_INFO,
- "%s: waiting for 3rd party to configure IP address",
+ "%s: waiting for 3rd party to "
+ "configure IP address",
ifp->name);
- state = D_STATE(ifp);
state->reason = "3RDPARTY";
script_runreason(ifp, state->reason);
- }
- return 1;
-}
-
-static void
-dhcp_static(struct interface *ifp)
-{
- struct if_options *ifo;
- struct dhcp_state *state;
-
- if (handle_3rdparty(ifp))
return;
- ifo = ifp->options;
- state = D_STATE(ifp);
+ }
state->offer = dhcp_message_new(&ifo->req_addr, &ifo->req_mask);
- eloop_timeout_delete(NULL, ifp);
- dhcp_bind(ifp);
+ if (state->offer) {
+ eloop_timeout_delete(NULL, ifp);
+ dhcp_bind(ifp);
+ }
}
void
dhcp_inform(struct interface *ifp)
{
struct dhcp_state *state;
-
- if (handle_3rdparty(ifp))
- return;
+ struct if_options *ifo;
+ struct ipv4_addr *ap;
state = D_STATE(ifp);
+ ifo = ifp->options;
if (options & DHCPCD_TEST) {
- state->addr.s_addr = ifp->options->req_addr.s_addr;
- state->net.s_addr = ifp->options->req_mask.s_addr;
+ state->addr.s_addr = ifo->req_addr.s_addr;
+ state->net.s_addr = ifo->req_mask.s_addr;
} else {
- ifp->options->options |= DHCPCD_STATIC;
- dhcp_static(ifp);
+ if (ifo->req_addr.s_addr == INADDR_ANY) {
+ state = D_STATE(ifp);
+ ap = ipv4_findaddr(ifp, NULL, NULL);
+ if (ap == NULL) {
+ syslog(LOG_INFO,
+ "%s: waiting for 3rd party to "
+ "configure IP address",
+ ifp->name);
+ state->reason = "3RDPARTY";
+ script_runreason(ifp, state->reason);
+ return;
+ }
+ state->offer =
+ dhcp_message_new(&ap->addr, &ap->net);
+ } else
+ state->offer =
+ dhcp_message_new(&ifo->req_addr, &ifo->req_mask);
+ if (state->offer) {
+ ifo->options |= DHCPCD_STATIC;
+ dhcp_bind(ifp);
+ ifo->options &= ~DHCPCD_STATIC;
+ }
}
state->state = DHS_INFORM;
if (!(ifo->options & DHCPCD_INFORM))
log_dhcp(LOG_DEBUG, "acknowledged", iface, dhcp, from);
+ else
+ ifo->options &= ~DHCPCD_STATIC;
}
/* BOOTP could have already assigned this above, so check we still
/* If the interface already has the address configured
* then we can't ARP for duplicate detection. */
addr.s_addr = state->offer->yiaddr;
- if (ipv4_hasaddress(iface->name, &addr, NULL) != 1) {
+ if (!ipv4_findaddr(iface, &addr, NULL)) {
state->claims = 0;
state->probes = 0;
state->conflicts = 0;
else
dhcp_reboot(ifp);
}
+
+void
+dhcp_handleifa(int type, struct interface *ifp,
+ const struct in_addr *addr,
+ const struct in_addr *net,
+ const struct in_addr *dst)
+{
+ struct dhcp_state *state;
+ struct if_options *ifo;
+ int i;
+
+ state = D_STATE(ifp);
+ if (state == NULL)
+ return;
+
+ if (type == RTM_DELADDR) {
+ if (state->new &&
+ state->new->yiaddr == addr->s_addr)
+ syslog(LOG_INFO, "%s: removing IP address %s/%d",
+ ifp->name, inet_ntoa(state->lease.addr),
+ inet_ntocidr(state->lease.net));
+ return;
+ }
+
+ if (type != RTM_NEWADDR)
+ return;
+
+ ifo = ifp->options;
+ if (ifo->options & DHCPCD_INFORM) {
+ if (state->state != DHS_INFORM)
+ dhcp_inform(ifp);
+ return;
+ }
+
+ if (!(ifo->options & DHCPCD_STATIC))
+ return;
+ if (ifo->req_addr.s_addr != INADDR_ANY)
+ return;
+
+ free(state->old);
+ state->old = state->new;
+ state->new = dhcp_message_new(addr, net);
+ if (state->new == NULL)
+ return;
+ state->dst.s_addr = dst ? dst->s_addr : INADDR_ANY;
+ if (dst) {
+ for (i = 1; i < 255; i++)
+ if (i != DHO_ROUTER && has_option_mask(ifo->dstmask,i))
+ dhcp_message_add_addr(state->new, i, *dst);
+ }
+ state->reason = "STATIC";
+ ipv4_buildroutes();
+ script_runreason(ifp, state->reason);
+ if (ifo->options & DHCPCD_INFORM) {
+ state->state = DHS_INFORM;
+ state->xid = dhcp_xid(ifp);
+ state->lease.server.s_addr = dst ? dst->s_addr : INADDR_ANY;
+ state->addr = *addr;
+ state->net = *net;
+ dhcp_inform(ifp);
+ }
+}
const struct interface *);
uint32_t dhcp_xid(const struct interface *);
-struct dhcp_message *dhcp_message_new(struct in_addr *addr,
- struct in_addr *mask);
+struct dhcp_message *dhcp_message_new(const struct in_addr *addr,
+ const struct in_addr *mask);
int dhcp_message_add_addr(struct dhcp_message *, uint8_t, struct in_addr);
ssize_t make_message(struct dhcp_message **, const struct interface *,
uint8_t);
struct dhcp_message *read_lease(const struct interface *);
void get_lease(struct dhcp_lease *, const struct dhcp_message *);
+void dhcp_handleifa(int, struct interface *,
+ const struct in_addr *, const struct in_addr *, const struct in_addr *);
+
void dhcp_drop(struct interface *, const char *);
void dhcp_start(struct interface *);
void dhcp_stop(struct interface *);
#define LINK_UNKNOWN 0
#define LINK_DOWN -1
-#define IF_DATA_DHCP 0
-#define IF_DATA_IPV6 1
-#define IF_DATA_IPV6ND 2
-#define IF_DATA_DHCP6 3
-#define IF_DATA_MAX 4
+#define IF_DATA_IPV4 0
+#define IF_DATA_DHCP 1
+#define IF_DATA_IPV6 2
+#define IF_DATA_IPV6ND 3
+#define IF_DATA_DHCP6 4
+#define IF_DATA_MAX 5
struct interface {
TAILQ_ENTRY(interface) next;
COPYOUT(rt.dest, rti_info[RTAX_IFA]);
COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
COPYOUT(rt.gate, rti_info[RTAX_BRD]);
- ipv4_handleifa(rtm->rtm_type, ifname,
+ ipv4_handleifa(rtm->rtm_type,
+ NULL, ifname,
&rt.dest, &rt.net, &rt.gate);
break;
#endif
#include <limits.h>
#include <stdint.h>
-#include "ipv4.h"
-
/* Don't set any optional arguments here so we retain POSIX
* compatibility with getopt */
#define IF_OPTS "46bc:de:f:gh:i:kl:m:no:pqr:s:t:u:v:wxy:z:ABC:DEF:GHI:JKLO:Q:S:TUVW:X:Z:"
* SUCH DAMAGE.
*/
+#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "config.h"
#include "common.h"
+#include "dhcpcd.h"
#include "dhcp.h"
#include "if-options.h"
#include "if-pref.h"
return 0;
}
+struct ipv4_addr *
+ipv4_findaddr(struct interface *ifp,
+ const struct in_addr *addr, const struct in_addr *net)
+{
+ struct ipv4_state *state;
+ struct ipv4_addr *ap;
+
+ state = IPV4_STATE(ifp);
+ if (state) {
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ if ((addr == NULL || ap->addr.s_addr == addr->s_addr) &&
+ (net == NULL || ap->net.s_addr == net->s_addr))
+ return ap;
+ }
+ }
+ return NULL;
+}
+
int
ipv4_addrexists(const struct in_addr *addr)
{
- const struct interface *ifp;
- const struct dhcp_state *state;
+ struct interface *ifp;
+ struct dhcp_state *state;
TAILQ_FOREACH(ifp, ifaces, next) {
- state = D_CSTATE(ifp);
+ state = D_STATE(ifp);
if (state) {
if (addr == NULL) {
- if (state->addr.s_addr)
+ if (state->addr.s_addr != INADDR_ANY)
return 1;
- } else if (memcmp(&addr->s_addr,
- &state->addr.s_addr,
- sizeof(state->addr.s_addr)) == 0)
+ } else if (addr->s_addr == state->addr.s_addr)
return 1;
}
+ if (addr != NULL && ipv4_findaddr(ifp, addr, NULL))
+ return 1;
}
return 0;
}
-int
-ipv4_doaddress(const char *ifname,
- struct in_addr *addr, struct in_addr *net, struct in_addr *dst, int act)
-{
- struct ifaddrs *ifaddrs, *ifa;
- const struct sockaddr_in *a, *n, *d;
- int retval;
-
- if (getifaddrs(&ifaddrs) == -1)
- return -1;
-
- retval = 0;
- for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
- if (ifa->ifa_addr == NULL ||
- ifa->ifa_addr->sa_family != AF_INET ||
- strcmp(ifa->ifa_name, ifname) != 0)
- continue;
- a = (const struct sockaddr_in *)(void *)ifa->ifa_addr;
- n = (const struct sockaddr_in *)(void *)ifa->ifa_netmask;
- if (ifa->ifa_flags & IFF_POINTOPOINT)
- d = (const struct sockaddr_in *)(void *)
- ifa->ifa_dstaddr;
- else
- d = NULL;
- if (act == 1) {
- addr->s_addr = a->sin_addr.s_addr;
- net->s_addr = n->sin_addr.s_addr;
- if (dst) {
- if (ifa->ifa_flags & IFF_POINTOPOINT)
- dst->s_addr = d->sin_addr.s_addr;
- else
- dst->s_addr = INADDR_ANY;
- }
- retval = 1;
- break;
- }
- if (addr->s_addr == a->sin_addr.s_addr &&
- (net == NULL || net->s_addr == n->sin_addr.s_addr))
- {
- retval = 1;
- break;
- }
- }
- freeifaddrs(ifaddrs);
- return retval;
-}
-
void
ipv4_freeroutes(struct rt_head *rts)
{
}
static int
-delete_address(struct interface *iface)
+delete_address1(struct interface *ifp,
+ const struct in_addr *addr, const struct in_addr *net)
{
- int retval;
+ int r;
+ struct ipv4_state *state;
+ struct ipv4_addr *ap;
+
+ syslog(LOG_DEBUG, "%s: deleting IP address %s/%d",
+ ifp->name, inet_ntoa(*addr), inet_ntocidr(*net));
+ r = ipv4_deleteaddress(ifp, addr, net);
+ if (r == -1 && errno != EADDRNOTAVAIL && errno != ENXIO &&
+ errno != ENODEV)
+ syslog(LOG_ERR, "%s: %s: %m", ifp->name, __func__);
+
+ state = IPV4_STATE(ifp);
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ if (ap->addr.s_addr == addr->s_addr &&
+ ap->net.s_addr == net->s_addr)
+ {
+ TAILQ_REMOVE(&state->addrs, ap, next);
+ free(ap);
+ break;
+ }
+ }
+ return r;
+}
+
+static int
+delete_address(struct interface *ifp)
+{
+ int r;
struct if_options *ifo;
struct dhcp_state *state;
- state = D_STATE(iface);
- ifo = iface->options;
+ state = D_STATE(ifp);
+ ifo = ifp->options;
if (ifo->options & DHCPCD_INFORM ||
(ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0))
return 0;
- syslog(LOG_DEBUG, "%s: deleting IP address %s/%d",
- iface->name, inet_ntoa(state->addr), inet_ntocidr(state->net));
- retval = ipv4_deleteaddress(iface, &state->addr, &state->net);
- if (retval == -1 && errno != EADDRNOTAVAIL && errno != ENXIO &&
- errno != ENODEV)
- syslog(LOG_ERR, "del_address: %m");
+ r = delete_address1(ifp, &state->addr, &state->net);
state->addr.s_addr = 0;
state->net.s_addr = 0;
- return retval;
+ return r;
}
void
struct dhcp_message *dhcp;
struct dhcp_lease *lease;
struct if_options *ifo = ifp->options;
+ struct ipv4_addr *ap;
struct rt *rt;
int r;
return;
}
- /* This also changes netmask */
- if (!(ifo->options & DHCPCD_INFORM) ||
- !ipv4_hasaddress(ifp->name, &lease->addr, &lease->net))
- {
+ /* If the netmask is different, delete the addresss */
+ ap = ipv4_findaddr(ifp, &lease->addr, NULL);
+ if (ap && ap->net.s_addr != lease->net.s_addr)
+ delete_address1(ifp, &ap->addr, &ap->net);
+
+ if (ipv4_findaddr(ifp, &lease->addr, &lease->net))
+ syslog(LOG_DEBUG, "%s: IP address %s/%d already exists",
+ ifp->name, inet_ntoa(lease->addr),
+ inet_ntocidr(lease->net));
+ else {
syslog(LOG_DEBUG, "%s: adding IP address %s/%d",
ifp->name, inet_ntoa(lease->addr),
inet_ntocidr(lease->net));
}
void
-ipv4_handleifa(int type, const char *ifname,
- struct in_addr *addr, struct in_addr *net, struct in_addr *dst)
+ipv4_handleifa(int type, struct if_head *ifs, const char *ifname,
+ const struct in_addr *addr, const struct in_addr *net,
+ const struct in_addr *dst)
{
struct interface *ifp;
- struct if_options *ifo;
- struct dhcp_state *state;
- int i;
+ struct ipv4_state *state;
+ struct ipv4_addr *ap;
+ if (ifs == NULL)
+ ifs = ifaces;
+ if (ifs == NULL)
+ return;
if (addr->s_addr == INADDR_ANY)
return;
- TAILQ_FOREACH(ifp, ifaces, next) {
+
+ TAILQ_FOREACH(ifp, ifs, next) {
if (strcmp(ifp->name, ifname) == 0)
break;
}
if (ifp == NULL)
return;
- state = D_STATE(ifp);
- if (state == NULL)
- return;
-
- if (type == RTM_DELADDR) {
- if (state->new &&
- state->new->yiaddr == addr->s_addr)
- syslog(LOG_INFO, "%s: removing IP address %s/%d",
- ifp->name, inet_ntoa(state->lease.addr),
- inet_ntocidr(state->lease.net));
- return;
+ state = IPV4_STATE(ifp);
+ if (state == NULL) {
+ ifp->if_data[IF_DATA_IPV4] = malloc(sizeof(*state));
+ state = IPV4_STATE(ifp);
+ if (state == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return;
+ }
+ TAILQ_INIT(&state->addrs);
+ ap = NULL;
+ } else
+ ap = ipv4_findaddr(ifp, addr, net);
+ if (type == RTM_NEWADDR && ap == NULL) {
+ ap = malloc(sizeof(*ap));
+ if (ap == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return;
+ }
+ 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);
}
- if (type != RTM_NEWADDR)
- return;
-
- ifo = ifp->options;
- if ((ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)) == 0 ||
- ifo->req_addr.s_addr != INADDR_ANY)
- return;
+ dhcp_handleifa(type, ifp, addr, net, dst);
+}
- free(state->old);
- state->old = state->new;
- state->new = dhcp_message_new(addr, net);
- state->dst.s_addr = dst ? dst->s_addr : INADDR_ANY;
- if (dst) {
- for (i = 1; i < 255; i++)
- if (i != DHO_ROUTER && has_option_mask(ifo->dstmask,i))
- dhcp_message_add_addr(state->new, i, *dst);
- }
- state->reason = "STATIC";
- ipv4_buildroutes();
- script_runreason(ifp, state->reason);
- if (ifo->options & DHCPCD_INFORM) {
- state->state = DHS_INFORM;
- state->xid = dhcp_xid(ifp);
- state->lease.server.s_addr = dst ? dst->s_addr : INADDR_ANY;
- state->addr = *addr;
- state->net = *net;
- dhcp_inform(ifp);
+void
+ipv4_free(struct interface *ifp)
+{
+ struct ipv4_state *state;
+ struct ipv4_addr *addr;
+
+ state = IPV4_STATE(ifp);
+ if (state) {
+ while ((addr = TAILQ_FIRST(&state->addrs))) {
+ TAILQ_REMOVE(&state->addrs, addr, next);
+ free(addr);
+ }
}
}
};
TAILQ_HEAD(rt_head, rt);
+struct ipv4_addr {
+ TAILQ_ENTRY(ipv4_addr) next;
+ struct in_addr addr;
+ struct in_addr net;
+ struct in_addr dst;
+};
+TAILQ_HEAD(ipv4_addrhead, ipv4_addr);
+
+struct ipv4_state {
+ struct ipv4_addrhead addrs;
+};
+
+#define IPV4_STATE(ifp) \
+ ((struct ipv4_state *)(ifp)->if_data[IF_DATA_IPV4])
+#define IPV4_CSTATE(ifp) \
+ ((const struct ipv4_state *)(ifp)->if_data[IF_DATA_IPV4])
+
#ifdef INET
int ipv4_init(void);
int inet_ntocidr(struct in_addr);
void ipv4_applyaddr(void *);
int ipv4_routedeleted(const struct rt *);
-void ipv4_handleifa(int, const char *,
- struct in_addr *, struct in_addr *, struct in_addr *);
+struct ipv4_addr *ipv4_findaddr(struct interface *,
+ const struct in_addr *, const struct in_addr *);
+void ipv4_handleifa(int, struct if_head *, const char *,
+ const struct in_addr *, const struct in_addr *, const struct in_addr *);
-int ipv4_doaddress(const char *,
- struct in_addr *, struct in_addr *, struct in_addr *, int);
int if_address(const struct interface *,
const struct in_addr *, const struct in_addr *,
const struct in_addr *, int);
if_address(iface, addr, net, brd, 2)
#define ipv4_deleteaddress(iface, addr, net) \
if_address(iface, addr, net, NULL, -1)
-#define ipv4_hasaddress(iface, addr, net) \
- ipv4_doaddress(iface, addr, net, NULL, 0)
-#define ipv4_getaddress(iface, addr, net, dst) \
- ipv4_doaddress(iface, addr, net, dst, 1)
int if_route(const struct rt *rt, int);
#define ipv4_addroute(rt) if_route(rt, 1)
ssize_t ipv4_sendrawpacket(const struct interface *,
int, const void *, ssize_t);
ssize_t ipv4_getrawpacket(struct interface *, int, void *, ssize_t, int *);
+void ipv4_free(struct interface *);
#else
#define ipv4_init() -1
#define ipv4_applyaddr(a) {}
#include "dhcp.h"
#include "dhcp6.h"
#include "if-options.h"
+#include "ipv4.h"
#include "ipv6nd.h"
#include "net.h"
if (ifp == NULL)
return;
+ ipv4_free(ifp);
dhcp_free(ifp);
ipv6_free(ifp);
dhcp6_free(ifp);
#ifdef __linux__
char ifn[IF_NAMESIZE];
#endif
+#ifdef INET
+ const struct sockaddr_in *addr;
+ const struct sockaddr_in *net;
+ const struct sockaddr_in *dst;
#ifdef INET6
const struct sockaddr_in6 *sin6;
int ifa_flags;
TAILQ_INSERT_TAIL(ifs, ifp, next);
}
-#ifdef INET6
for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
- if (ifa->ifa_addr != NULL &&
- ifa->ifa_addr->sa_family == AF_INET6)
- {
+ if (ifa->ifa_addr == NULL)
+ continue;
+ switch(ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ addr = (const struct sockaddr_in *)
+ (void *)ifa->ifa_addr;
+ net = (const struct sockaddr_in *)
+ (void *)ifa->ifa_netmask;
+ if (ifa->ifa_flags & IFF_POINTOPOINT)
+ dst = (const struct sockaddr_in *)
+ (void *)ifa->ifa_dstaddr;
+ else
+ dst = NULL;
+ ipv4_handleifa(RTM_NEWADDR, ifs, ifa->ifa_name,
+ &addr->sin_addr,
+ &net->sin_addr,
+ dst ? &dst->sin_addr : NULL);
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
sin6 = (const struct sockaddr_in6 *)
(void *)ifa->ifa_addr;
ifa_flags = in6_addr_flags(ifa->ifa_name,
ipv6_handleifa(RTM_NEWADDR, ifs,
ifa->ifa_name,
&sin6->sin6_addr, ifa_flags);
+ break;
+#endif
}
}
#endif