diff options
author | Alexander V. Chernikov <melifaro@FreeBSD.org> | 2021-01-19 23:50:34 +0000 |
---|---|---|
committer | Alexander V. Chernikov <melifaro@FreeBSD.org> | 2021-01-21 21:48:49 +0000 |
commit | 130aebbab0d38da85f7d32b6d4227f95a2cd9ec7 (patch) | |
tree | 3236bc7716d75529a3c8b82897a25c5e616b61a9 /sys/netinet | |
parent | bbff3a72b23a389aebd492ad7c9db9a51166e907 (diff) | |
download | src-130aebbab0d38da85f7d32b6d4227f95a2cd9ec7.tar.gz src-130aebbab0d38da85f7d32b6d4227f95a2cd9ec7.zip |
Further refactor IPv4 interface route creation.
* Fix bug with /32 aliases introduced in 81728a538d24.
* Explicitly document business logic for IPv4 ifa routes.
* Remove remnants of rtinit()
* Deduplicate ifa->route prefix code by moving it into ia_getrtprefix()
* Deduplicate conditional check for ifa_maintain_loopback_route() by
moving into ia_need_loopback_route()
* Remove now-unused flags argument from in_addprefix().
Reviewed by: donner
PR: 252883
Differential Revision: https://reviews.freebsd.org/D28246
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/in.c | 239 | ||||
-rw-r--r-- | sys/netinet/in_var.h | 2 | ||||
-rw-r--r-- | sys/netinet/ip_carp.c | 2 |
3 files changed, 130 insertions, 113 deletions
diff --git a/sys/netinet/in.c b/sys/netinet/in.c index d6c0c350dec5..eb58c3453cfc 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -79,6 +79,8 @@ static int in_gifaddr_ioctl(u_long, caddr_t, struct ifnet *, struct thread *); static void in_socktrim(struct sockaddr_in *); static void in_purgemaddrs(struct ifnet *); +static bool ia_need_loopback_route(const struct in_ifaddr *); + VNET_DEFINE_STATIC(int, nosameprefix); #define V_nosameprefix VNET(nosameprefix) SYSCTL_INT(_net_inet_ip, OID_AUTO, no_same_prefix, CTLFLAG_VNET | CTLFLAG_RW, @@ -445,10 +447,6 @@ in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td) if (ifp->if_flags & IFF_POINTOPOINT) ia->ia_dstaddr = *dstaddr; - /* XXXGL: rtinit() needs this strange assignment. */ - if (ifp->if_flags & IFF_LOOPBACK) - ia->ia_dstaddr = ia->ia_addr; - if (vhid != 0) { error = (*carp_attach_p)(&ia->ia_ifa, vhid); if (error) @@ -481,12 +479,7 @@ in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td) * Add route for the network. */ if (vhid == 0) { - int flags = RTF_UP; - - if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) - flags |= RTF_HOST; - - error = in_addprefix(ia, flags); + error = in_addprefix(ia); if (error) goto fail1; } @@ -494,10 +487,7 @@ in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td) /* * Add a loopback route to self. */ - if (vhid == 0 && (ifp->if_flags & IFF_LOOPBACK) == 0 && - ia->ia_addr.sin_addr.s_addr != INADDR_ANY && - !((ifp->if_flags & IFF_POINTOPOINT) && - ia->ia_dstaddr.sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr)) { + if (vhid == 0 && ia_need_loopback_route(ia)) { struct in_ifaddr *eia; eia = in_localip_more(ia); @@ -723,7 +713,8 @@ in_match_ifaddr(const struct rtentry *rt, const struct nhop_object *nh, void *ar static int in_handle_prefix_route(uint32_t fibnum, int cmd, - struct sockaddr_in *dst, struct sockaddr_in *netmask, struct ifaddr *ifa) + struct sockaddr_in *dst, struct sockaddr_in *netmask, struct ifaddr *ifa, + struct ifnet *ifp) { NET_EPOCH_ASSERT(); @@ -738,6 +729,7 @@ in_handle_prefix_route(uint32_t fibnum, int cmd, struct rt_addrinfo info = { .rti_ifa = ifa, + .rti_ifp = ifp, .rti_flags = RTF_PINNED | ((netmask != NULL) ? 0 : RTF_HOST), .rti_info = { [RTAX_DST] = (struct sockaddr *)dst, @@ -753,29 +745,105 @@ in_handle_prefix_route(uint32_t fibnum, int cmd, } /* - * Adds or delete interface route corresponding to @ifa. + * Routing table interaction with interface addresses. + * + * In general, two types of routes needs to be installed: + * a) "interface" or "prefix" route, telling user that the addresses + * behind the ifa prefix are reached directly. + * b) "loopback" route installed for the ifa address, telling user that + * the address belongs to local system. + * + * Handling for (a) and (b) differs in multi-fib aspects, hence they + * are implemented in different functions below. + * + * The cases above may intersect - /32 interface aliases results in + * the same prefix produced by (a) and (b). This blurs the definition + * of the "loopback" route and complicate interactions. The interaction + * table is defined below. The case numbers are used in the multiple + * functions below to refer to the particular test case. + * * There can be multiple options: - * 1) Adding addr with prefix on non-p2p/non-lo interface. - * Example: 192.0.2.1/24. Action: add route towards - * 192.0.2.0/24 via this interface, using ifa as an address source. - * Note: route to 192.0.2.1 will be installed separately via - * ifa_maintain_loopback_route(). - * 2) Adding addr with "host" mask. - * Example: 192.0.2.2/32. In this case no action is performed, - * as the route should be installed by ifa_maintain_loopback_route(). - * Returns 0 to indicate success. + * 1) Adding address with prefix on non-p2p/non-loopback interface. + * Example: 192.0.2.1/24. Action: + * * add "prefix" route towards 192.0.2.0/24 via @ia interface, + * using @ia as an address source. + * * add "loopback" route towards 192.0.2.1 via V_loif, saving + * @ia ifp in the gateway and using @ia as an address source. + * + * 2) Adding address with /32 mask to non-p2p/non-loopback interface. + * Example: 192.0.2.2/32. Action: + * * add "prefix" host route via V_loif, using @ia as an address source. + * * 3) Adding address with or without prefix to p2p interface. - * Example: 10.0.0.1/24->10.0.0.2. In this case, all other addresses - * covered by prefix, does not make sense in the context of p2p link. - * Action: add route towards 10.0.0.2 via this interface, using ifa as an - * address source. - * Similar to (1), route to 10.0.0.1 will be installed by - * ifa_maintain_loopback_route(). + * Example: 10.0.0.1/24->10.0.0.2. Action: + * * add "prefix" host route towards 10.0.0.2 via this interface, using @ia + * as an address source. Note: no sense in installing full /24 as the interface + * is point-to-point. + * * add "loopback" route towards 10.0.9.1 via V_loif, saving + * @ia ifp in the gateway and using @ia as an address source. + * * 4) Adding address with or without prefix to loopback interface. - * Example: 192.0.2.1/24. In this case, trafic to non-host addresses cannot - * be forwarded, as it would introduce an infinite cycle. - * Similar to (2), perform no action and return 0. Loopback route - * will be installed by ifa_maintain_loopback_route(). + * Example: 192.0.2.1/24. Action: + * * add "prefix" host route via @ia interface, using @ia as an address source. + * Note: Skip installing /24 prefix as it would introduce TTL loop + * for the traffic destined to these addresses. + */ + +/* + * Checks if @ia needs to install loopback route to @ia address via + * ifa_maintain_loopback_route(). + * + * Return true on success. + */ +static bool +ia_need_loopback_route(const struct in_ifaddr *ia) +{ + struct ifnet *ifp = ia->ia_ifp; + + /* Case 4: Skip loopback interfaces */ + if ((ifp->if_flags & IFF_LOOPBACK) || + (ia->ia_addr.sin_addr.s_addr == INADDR_ANY)) + return (false); + + /* Clash avoidance: Skip p2p interfaces with both addresses are equal */ + if ((ifp->if_flags & IFF_POINTOPOINT) && + ia->ia_dstaddr.sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr) + return (false); + + /* Case 2: skip /32 prefixes */ + if (!(ifp->if_flags & IFF_POINTOPOINT) && + (ia->ia_sockmask.sin_addr.s_addr == INADDR_BROADCAST)) + return (false); + + return (true); +} + +/* + * Calculate "prefix" route corresponding to @ia. + */ +static void +ia_getrtprefix(const struct in_ifaddr *ia, struct in_addr *prefix, struct in_addr *mask) +{ + + if (ia->ia_ifp->if_flags & IFF_POINTOPOINT) { + /* Case 3: return host route for dstaddr */ + *prefix = ia->ia_dstaddr.sin_addr; + mask->s_addr = INADDR_BROADCAST; + } else if (ia->ia_ifp->if_flags & IFF_LOOPBACK) { + /* Case 4: return host route for ifaddr */ + *prefix = ia->ia_addr.sin_addr; + mask->s_addr = INADDR_BROADCAST; + } else { + /* Cases 1,2: return actual ia prefix */ + *prefix = ia->ia_addr.sin_addr; + *mask = ia->ia_sockmask.sin_addr; + prefix->s_addr &= mask->s_addr; + } +} + +/* + * Adds or delete interface "prefix" route corresponding to @ifa. + * Returns 0 on success or errno. */ int in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia) @@ -786,25 +854,7 @@ in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia) struct epoch_tracker et; int error; - /* Case 4: ignore loopbacks */ - if (ifa->ifa_ifp->if_flags & IFF_LOOPBACK) - return (0); - - if (ifa->ifa_ifp->if_flags & IFF_POINTOPOINT) { - /* Case 3: install route towards dst addr */ - daddr = ia->ia_dstaddr.sin_addr; - pmask = NULL; - maddr.s_addr = INADDR_BROADCAST; - } else { - daddr = ia->ia_addr.sin_addr; - pmask = &ia->ia_sockmask; - maddr = pmask->sin_addr; - - if (maddr.s_addr == INADDR_BROADCAST) { - /* Case 2: ignore /32 routes */ - return (0); - } - } + ia_getrtprefix(ia, &daddr, &maddr); struct sockaddr_in mask = { .sin_family = AF_INET, @@ -812,8 +862,7 @@ in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia) .sin_addr = maddr, }; - if (pmask != NULL) - pmask = &mask; + pmask = (maddr.s_addr != INADDR_BROADCAST) ? &mask : NULL; struct sockaddr_in dst = { .sin_family = AF_INET, @@ -821,56 +870,44 @@ in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia) .sin_addr.s_addr = daddr.s_addr & maddr.s_addr, }; + struct ifnet *ifp = ia->ia_ifp; + + if ((maddr.s_addr == INADDR_BROADCAST) && + (!(ia->ia_ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)))) { + /* Case 2: host route on broadcast interface */ + ifp = V_loif; + } + uint32_t fibnum = ifa->ifa_ifp->if_fib; NET_EPOCH_ENTER(et); - error = in_handle_prefix_route(fibnum, cmd, &dst, pmask, ifa); + error = in_handle_prefix_route(fibnum, cmd, &dst, pmask, ifa, ifp); NET_EPOCH_EXIT(et); return (error); } - -#define rtinitflags(x) \ - ((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \ - ? RTF_HOST : 0) - /* * Check if we have a route for the given prefix already. */ static bool -in_hasrtprefix(struct in_ifaddr *target, int flags) +in_hasrtprefix(struct in_ifaddr *target) { struct rm_priotracker in_ifa_tracker; struct in_ifaddr *ia; struct in_addr prefix, mask, p, m; bool result = false; - if ((flags & RTF_HOST) != 0) { - prefix = target->ia_dstaddr.sin_addr; - mask.s_addr = 0; - } else { - prefix = target->ia_addr.sin_addr; - mask = target->ia_sockmask.sin_addr; - prefix.s_addr &= mask.s_addr; - } + ia_getrtprefix(target, &prefix, &mask); IN_IFADDR_RLOCK(&in_ifa_tracker); /* Look for an existing address with the same prefix, mask, and fib */ CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) { - if (rtinitflags(ia)) { - p = ia->ia_dstaddr.sin_addr; + ia_getrtprefix(ia, &p, &m); - if (prefix.s_addr != p.s_addr) - continue; - } else { - p = ia->ia_addr.sin_addr; - m = ia->ia_sockmask.sin_addr; - p.s_addr &= m.s_addr; + if (prefix.s_addr != p.s_addr || + mask.s_addr != m.s_addr) + continue; - if (prefix.s_addr != p.s_addr || - mask.s_addr != m.s_addr) - continue; - } if (target->ia_ifp->if_fib != ia->ia_ifp->if_fib) continue; @@ -889,11 +926,11 @@ in_hasrtprefix(struct in_ifaddr *target, int flags) } int -in_addprefix(struct in_ifaddr *target, int flags) +in_addprefix(struct in_ifaddr *target) { int error; - if (in_hasrtprefix(target, flags)) { + if (in_hasrtprefix(target)) { if (V_nosameprefix) return (EEXIST); else { @@ -967,9 +1004,7 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags) /* * Remove the loopback route to the interface address. */ - if ((target->ia_addr.sin_addr.s_addr != INADDR_ANY) && - !(target->ia_ifp->if_flags & IFF_LOOPBACK) && - (flags & LLE_STATIC)) { + if (ia_need_loopback_route(target) && (flags & LLE_STATIC)) { struct in_ifaddr *eia; /* @@ -989,14 +1024,7 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags) } } - if (rtinitflags(target)) { - prefix = target->ia_dstaddr.sin_addr; - mask.s_addr = 0; - } else { - prefix = target->ia_addr.sin_addr; - mask = target->ia_sockmask.sin_addr; - prefix.s_addr &= mask.s_addr; - } + ia_getrtprefix(target, &prefix, &mask); if ((target->ia_flags & IFA_ROUTE) == 0) { rt_addrmsg(RTM_DELETE, &target->ia_ifa, target->ia_ifp->if_fib); @@ -1013,20 +1041,11 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags) IN_IFADDR_RLOCK(&in_ifa_tracker); CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) { - if (rtinitflags(ia)) { - p = ia->ia_dstaddr.sin_addr; - - if (prefix.s_addr != p.s_addr) - continue; - } else { - p = ia->ia_addr.sin_addr; - m = ia->ia_sockmask.sin_addr; - p.s_addr &= m.s_addr; + ia_getrtprefix(ia, &p, &m); - if (prefix.s_addr != p.s_addr || - mask.s_addr != m.s_addr) - continue; - } + if (prefix.s_addr != p.s_addr || + mask.s_addr != m.s_addr) + continue; if ((ia->ia_ifp->if_flags & IFF_UP) == 0) continue; @@ -1077,8 +1096,6 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags) return (error); } -#undef rtinitflags - void in_ifscrub_all(void) { diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h index 3a83c5e832ab..c7ebff80e56d 100644 --- a/sys/netinet/in_var.h +++ b/sys/netinet/in_var.h @@ -461,7 +461,7 @@ int in_leavegroup_locked(struct in_multi *, /*const*/ struct in_mfilter *); int in_control(struct socket *, u_long, caddr_t, struct ifnet *, struct thread *); -int in_addprefix(struct in_ifaddr *, int); +int in_addprefix(struct in_ifaddr *); int in_scrubprefix(struct in_ifaddr *, u_int); void in_ifscrub_all(void); int in_handle_ifaddr_route(int, struct in_ifaddr *); diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index 1fe3f16a92c8..9a551a70c3ad 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -1065,7 +1065,7 @@ carp_ifa_addroute(struct ifaddr *ifa) switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: - in_addprefix(ifatoia(ifa), RTF_UP); + in_addprefix(ifatoia(ifa)); ifa_add_loopback_route(ifa, (struct sockaddr *)&ifatoia(ifa)->ia_addr); break; |