Add IPv4 address selection from route code so platform handlers don't

have to work it out.
This commit is contained in:
Roy Marples 2015-09-10 08:32:37 +00:00
parent a73798528c
commit 641cce4378
4 changed files with 103 additions and 83 deletions

View File

@ -508,8 +508,6 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct rt_msghdr *rtm)
int
if_route(unsigned char cmd, const struct rt *rt)
{
const struct dhcp_state *state;
const struct ipv4ll_state *istate;
union sockunion {
struct sockaddr sa;
struct sockaddr_in sin;
@ -522,6 +520,7 @@ if_route(unsigned char cmd, const struct rt *rt)
} rtm;
char *bp = rtm.buffer;
size_t l;
struct in_addr src_addr;
#define ADDSU { \
l = RT_ROUNDUP(su.sa.sa_len); \
@ -536,47 +535,35 @@ if_route(unsigned char cmd, const struct rt *rt)
ADDSU; \
}
if (cmd != RTM_DELETE) {
state = D_CSTATE(rt->iface);
istate = IPV4LL_CSTATE(rt->iface);
} else {
/* appease GCC */
state = NULL;
istate = NULL;
}
memset(&rtm, 0, sizeof(rtm));
rtm.hdr.rtm_version = RTM_VERSION;
rtm.hdr.rtm_seq = 1;
rtm.hdr.rtm_type = cmd;
rtm.hdr.rtm_addrs = RTA_DST;
if (cmd == RTM_ADD || cmd == RTM_CHANGE)
rtm.hdr.rtm_addrs |= RTA_GATEWAY;
rtm.hdr.rtm_flags = RTF_UP;
#ifdef RTF_PINNED
if (cmd != RTM_ADD)
rtm.hdr.rtm_flags |= RTF_PINNED;
#endif
if (cmd != RTM_DELETE) {
rtm.hdr.rtm_addrs |= RTA_IFA | RTA_IFP;
/* None interface subnet routes are static. */
if ((rt->gate.s_addr != INADDR_ANY ||
rt->net.s_addr != state->net.s_addr ||
rt->dest.s_addr !=
(state->addr.s_addr & state->net.s_addr)) &&
(istate == NULL ||
rt->dest.s_addr !=
(istate->addr.s_addr & inaddr_llmask.s_addr) ||
rt->net.s_addr != inaddr_llmask.s_addr))
rtm.hdr.rtm_flags |= RTF_STATIC;
else {
if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
int subnet;
rtm.hdr.rtm_addrs |= RTA_GATEWAY | RTA_IFA | RTA_IFP;
/* Subnet routes are clonning or connected if supported.
* All other routes are static. */
subnet = ipv4_srcaddr(rt, &src_addr);
if (subnet == 1) {
#ifdef RTF_CLONING
rtm.hdr.rtm_flags |= RTF_CLONING;
#endif
#ifdef RTP_CONNECTED
rtm.hdr.rtm_priority = RTP_CONNECTED;
#endif
}
} else
rtm.hdr.rtm_flags |= RTF_STATIC;
if (subnet == -1) /* unikely */
rtm.hdr.rtm_addrs &= ~RTA_IFA;
}
if (rt->net.s_addr == htonl(INADDR_BROADCAST) &&
rt->gate.s_addr == htonl(INADDR_ANY))
@ -607,7 +594,7 @@ if_route(unsigned char cmd, const struct rt *rt)
}
if ((cmd == RTM_ADD || cmd == RTM_CHANGE) &&
!(rtm.hdr.rtm_flags & RTF_GATEWAY))
rtm.hdr.rtm_addrs |= RTA_IFA | RTA_IFP;
rtm.hdr.rtm_addrs |= RTA_IFP;
ADDADDR(&rt->dest);
if (rtm.hdr.rtm_addrs & RTA_GATEWAY) {
@ -638,7 +625,7 @@ if_route(unsigned char cmd, const struct rt *rt)
}
if (rtm.hdr.rtm_addrs & RTA_IFA)
ADDADDR(istate == NULL ? &state->addr : &istate->addr);
ADDADDR(&src_addr);
if (rt->mtu) {
rtm.hdr.rtm_inits |= RTV_MTU;

View File

@ -1329,9 +1329,8 @@ int
if_route(unsigned char cmd, const struct rt *rt)
{
struct nlmr nlm;
int retval = 0;
const struct dhcp_state *state;
const struct ipv4ll_state *istate;
struct in_addr src_addr;
int subnet;
memset(&nlm, 0, sizeof(nlm));
nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
@ -1352,18 +1351,12 @@ if_route(unsigned char cmd, const struct rt *rt)
nlm.rt.rtm_family = AF_INET;
nlm.rt.rtm_table = RT_TABLE_MAIN;
state = D_CSTATE(rt->iface);
istate = IPV4LL_CSTATE(rt->iface);
if (cmd == RTM_DELETE)
if (cmd == RTM_DELETE) {
nlm.rt.rtm_scope = RT_SCOPE_NOWHERE;
else {
/* We only change route metrics for kernel routes */
if ((rt->dest.s_addr ==
(state->addr.s_addr & state->net.s_addr) &&
rt->net.s_addr == state->net.s_addr) ||
(istate && rt->dest.s_addr ==
(istate->addr.s_addr & inaddr_llmask.s_addr) &&
rt->net.s_addr == inaddr_llmask.s_addr))
subnet = -1;
} else {
/* Subnet routes are RTPROT_KERNEL otherwise RTPROT_BOOT */
if ((subnet = ipv4_srcaddr(rt, &src_addr)) == 1)
nlm.rt.rtm_protocol = RTPROT_KERNEL;
else
nlm.rt.rtm_protocol = RTPROT_BOOT;
@ -1381,37 +1374,37 @@ if_route(unsigned char cmd, const struct rt *rt)
nlm.rt.rtm_dst_len = inet_ntocidr(rt->net);
add_attr_l(&nlm.hdr, sizeof(nlm), RTA_DST,
&rt->dest.s_addr, sizeof(rt->dest.s_addr));
if (nlm.rt.rtm_protocol == RTPROT_KERNEL) {
add_attr_l(&nlm.hdr, sizeof(nlm), RTA_PREFSRC,
istate == NULL ? &state->addr.s_addr : &istate->addr.s_addr,
sizeof(state->addr.s_addr));
}
/* If a host route then don't add the gateway */
if ((cmd == RTM_ADD || cmd == RTM_CHANGE) &&
rt->net.s_addr != INADDR_BROADCAST)
add_attr_l(&nlm.hdr, sizeof(nlm), RTA_GATEWAY,
&rt->gate.s_addr, sizeof(rt->gate.s_addr));
if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
if (rt->net.s_addr != INADDR_BROADCAST)
add_attr_l(&nlm.hdr, sizeof(nlm), RTA_GATEWAY,
&rt->gate.s_addr, sizeof(rt->gate.s_addr));
if (rt->gate.s_addr != htonl(INADDR_LOOPBACK))
add_attr_32(&nlm.hdr, sizeof(nlm), RTA_OIF,
rt->iface->index);
if (subnet != -1) {
add_attr_l(&nlm.hdr, sizeof(nlm), RTA_PREFSRC,
&src_addr.s_addr, sizeof(src_addr.s_addr));
}
if (rt->mtu) {
char metricsbuf[32];
struct rtattr *metrics = (void *)metricsbuf;
metrics->rta_type = RTA_METRICS;
metrics->rta_len = RTA_LENGTH(0);
rta_add_attr_32(metrics, sizeof(metricsbuf),
RTAX_MTU, rt->mtu);
add_attr_l(&nlm.hdr, sizeof(nlm), RTA_METRICS,
RTA_DATA(metrics),
(unsigned short)RTA_PAYLOAD(metrics));
}
}
if (rt->gate.s_addr != htonl(INADDR_LOOPBACK))
add_attr_32(&nlm.hdr, sizeof(nlm), RTA_OIF, rt->iface->index);
if (rt->metric)
add_attr_32(&nlm.hdr, sizeof(nlm), RTA_PRIORITY, rt->metric);
if (cmd != RTM_DELETE && rt->mtu) {
char metricsbuf[32];
struct rtattr *metrics = (void *)metricsbuf;
metrics->rta_type = RTA_METRICS;
metrics->rta_len = RTA_LENGTH(0);
rta_add_attr_32(metrics, sizeof(metricsbuf), RTAX_MTU, rt->mtu);
add_attr_l(&nlm.hdr, sizeof(nlm), RTA_METRICS,
RTA_DATA(metrics), (unsigned short)RTA_PAYLOAD(metrics));
}
if (send_netlink(rt->iface->ctx, NULL,
NETLINK_ROUTE, &nlm.hdr, NULL) == -1)
retval = -1;
return retval;
return send_netlink(rt->iface->ctx, NULL,
NETLINK_ROUTE, &nlm.hdr, NULL);
}
static int
@ -1461,7 +1454,6 @@ if_address6(const struct ipv6_addr *ia, int action)
{
struct nlma nlm;
struct ifa_cacheinfo cinfo;
int retval = 0;
/* IFA_FLAGS is not a define, but is was added at the same time
* IFA_F_NOPREFIXROUTE was do use that. */
#if defined(IFA_F_NOPREFIXROUTE) || defined(IFA_F_MANAGETEMPADDR)
@ -1517,17 +1509,14 @@ if_address6(const struct ipv6_addr *ia, int action)
add_attr_32(&nlm.hdr, sizeof(nlm), IFA_FLAGS, flags);
#endif
if (send_netlink(ia->iface->ctx, NULL, NETLINK_ROUTE, &nlm.hdr,
NULL) == -1)
retval = -1;
return retval;
return send_netlink(ia->iface->ctx, NULL,
NETLINK_ROUTE, &nlm.hdr, NULL);
}
int
if_route6(unsigned char cmd, const struct rt6 *rt)
{
struct nlmr nlm;
int retval = 0;
memset(&nlm, 0, sizeof(nlm));
nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
@ -1590,10 +1579,8 @@ if_route6(unsigned char cmd, const struct rt6 *rt)
RTA_DATA(metrics), (unsigned short)RTA_PAYLOAD(metrics));
}
if (send_netlink(rt->iface->ctx, NULL,
NETLINK_ROUTE, &nlm.hdr, NULL) == -1)
retval = -1;
return retval;
return send_netlink(rt->iface->ctx, NULL,
NETLINK_ROUTE, &nlm.hdr, NULL);
}
static int

55
ipv4.c
View File

@ -159,6 +159,51 @@ ipv4_findaddr(struct dhcpcd_ctx *ctx, const struct in_addr *addr)
return NULL;
}
int
ipv4_srcaddr(const struct rt *rt, struct in_addr *addr)
{
const struct dhcp_state *dstate;
const struct ipv4ll_state *istate;
if (rt->iface == NULL) {
errno = ENOENT;
return -1;
}
/* Prefer DHCP source address if matching */
dstate = D_CSTATE(rt->iface);
if (dstate &&
rt->net.s_addr == dstate->net.s_addr &&
rt->dest.s_addr == (dstate->addr.s_addr & dstate->net.s_addr))
{
*addr = dstate->addr;
return 1;
}
/* Then IPv4LL source address if matching */
istate = IPV4LL_CSTATE(rt->iface);
if (istate &&
rt->net.s_addr == inaddr_llmask.s_addr &&
rt->dest.s_addr == (istate->addr.s_addr & inaddr_llmask.s_addr))
{
*addr = istate->addr;
return 1;
}
/* If neither match, return DHCP then IPv4LL */
if (dstate) {
*addr = dstate->addr;
return 0;
}
if (istate) {
*addr = istate->addr;
return 0;
}
errno = ESRCH;
return -1;
}
int
ipv4_hasaddr(const struct interface *ifp)
{
@ -434,7 +479,7 @@ nc_route(struct rt *ort, struct rt *nrt)
#endif
if (change) {
if (if_route(RTM_CHANGE, nrt) == 0)
if (if_route(RTM_CHANGE, nrt) != -1)
return 0;
if (errno != ESRCH)
logger(nrt->iface->ctx, LOG_ERR, "if_route (CHG): %m");
@ -448,7 +493,7 @@ nc_route(struct rt *ort, struct rt *nrt)
#ifdef HAVE_ROUTE_METRIC
/* With route metrics, we can safely add the new route before
* deleting the old route. */
if (if_route(RTM_ADD, nrt) == 0) {
if (if_route(RTM_ADD, nrt) != -1) {
if (ort && if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
logger(nrt->iface->ctx, LOG_ERR, "if_route (DEL): %m");
return 0;
@ -464,7 +509,7 @@ nc_route(struct rt *ort, struct rt *nrt)
* adding the new one. */
if (ort && if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
logger(nrt->iface->ctx, LOG_ERR, "if_route (DEL): %m");
if (if_route(RTM_ADD, nrt) == 0)
if (if_route(RTM_ADD, nrt) != -1)
return 0;
#ifdef HAVE_ROUTE_METRIC
logerr:
@ -479,8 +524,8 @@ d_route(struct rt *rt)
int retval;
desc_route("deleting", rt);
retval = if_route(RTM_DELETE, rt);
if (retval != 0 && errno != ENOENT && errno != ESRCH)
retval = if_route(RTM_DELETE, rt) == -1 ? -1 : 0;
if (retval == -1 && errno != ENOENT && errno != ESRCH)
logger(rt->iface->ctx, LOG_ERR,
"%s: if_delroute: %m", rt->iface->name);
return retval;

1
ipv4.h
View File

@ -121,6 +121,7 @@ struct ipv4_addr *ipv4_iffindaddr(struct interface *,
const struct in_addr *, const struct in_addr *);
struct ipv4_addr *ipv4_iffindlladdr(struct interface *);
struct ipv4_addr *ipv4_findaddr(struct dhcpcd_ctx *, const struct in_addr *);
int ipv4_srcaddr(const struct rt *, 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 *,
int);