diff --git a/sys/netinet/in.c b/sys/netinet/in.c --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -79,6 +79,8 @@ 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 @@ 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 @@ * 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 @@ /* * 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 @@ 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 @@ 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 @@ } /* - * 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 @@ 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 @@ .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 @@ .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 @@ } 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 @@ /* * 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 @@ } } - 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_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 @@ return (error); } -#undef rtinitflags - void in_ifscrub_all(void) { diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h --- a/sys/netinet/in_var.h +++ b/sys/netinet/in_var.h @@ -461,7 +461,7 @@ /*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 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -1065,7 +1065,7 @@ 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;