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;
} rtm;
char *bp = rtm.buffer;
size_t l;
+ struct in_addr src_addr;
#define ADDSU { \
l = RT_ROUNDUP(su.sa.sa_len); \
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))
}
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) {
}
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;
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));
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;
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
{
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)
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));
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
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)
{
#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");
#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;
* 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:
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;