Date: Sun, 19 Jan 2014 16:07:28 +0000 (UTC) From: "Alexander V. Chernikov" <melifaro@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r260882 - in head/sys: netinet netinet6 Message-ID: <201401191607.s0JG7SsM084760@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: melifaro Date: Sun Jan 19 16:07:27 2014 New Revision: 260882 URL: http://svnweb.freebsd.org/changeset/base/260882 Log: Further rework netinet6 address handling code: * Set ia address/mask values BEFORE attaching to address lists. Inet6 address assignment is not atomic, so the simplest way to do this atomically is to fill in ia before attach. * Validate irfa->ia_addr field before use (we permit ANY sockaddr in old code). * Do some renamings: in6_ifinit -> in6_notify_ifa (interaction with other subsystems is here) in6_setup_ifa -> in6_broadcast_ifa (LLE/Multicast/DaD code) in6_ifaddloop -> nd6_add_ifa_lle in6_ifremloop -> nd6_rem_ifa_lle * Split working with LLE and route announce code for last two. Add temporary in6_newaddrmsg() function to mimic current rtsock behaviour. * Call device SIOCSIFADDR handler IFF we're adding first address. In IPv4 we have to call it on every address change since ARP record is installed by arp_ifinit() which is called by given handler. IPv6 stack, on the opposite is responsible to call nd6_add_ifa_lle() so there is no reason to call SIOCSIFADDR often. Modified: head/sys/netinet/ip_carp.c head/sys/netinet6/in6.c head/sys/netinet6/in6_var.h head/sys/netinet6/nd6.c head/sys/netinet6/nd6.h Modified: head/sys/netinet/ip_carp.c ============================================================================== --- head/sys/netinet/ip_carp.c Sun Jan 19 13:51:46 2014 (r260881) +++ head/sys/netinet/ip_carp.c Sun Jan 19 16:07:27 2014 (r260882) @@ -964,7 +964,7 @@ carp_ifa_addroute(struct ifaddr *ifa) case AF_INET6: ifa_add_loopback_route(ifa, (struct sockaddr *)&ifatoia6(ifa)->ia_addr); - in6_ifaddloop(ifa); + nd6_add_ifa_lle(ifatoia6(ifa)); break; #endif } @@ -995,7 +995,7 @@ carp_ifa_delroute(struct ifaddr *ifa) case AF_INET6: ifa_del_loopback_route(ifa, (struct sockaddr *)&ifatoia6(ifa)->ia_addr); - in6_ifremloop(ifa); + nd6_rem_ifa_lle(ifatoia6(ifa)); break; #endif } Modified: head/sys/netinet6/in6.c ============================================================================== --- head/sys/netinet6/in6.c Sun Jan 19 13:51:46 2014 (r260881) +++ head/sys/netinet6/in6.c Sun Jan 19 16:07:27 2014 (r260882) @@ -133,8 +133,8 @@ const struct in6_addr in6mask128 = IN6MA const struct sockaddr_in6 sa6_any = { sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0 }; -static int in6_ifinit(struct ifnet *, struct in6_ifaddr *, - struct sockaddr_in6 *, int); +static int in6_notify_ifa(struct ifnet *, struct in6_ifaddr *, + struct in6_aliasreq *, int); static void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *); int (*faithprefix_p)(struct in6_addr *); @@ -145,44 +145,26 @@ static struct in6_ifaddr *in6_alloc_ifa( struct in6_aliasreq *, int flags); static int in6_update_ifa_internal(struct ifnet *, struct in6_aliasreq *, struct in6_ifaddr *, int, int); -static int in6_setup_ifa(struct ifnet *, struct in6_aliasreq *, +static int in6_broadcast_ifa(struct ifnet *, struct in6_aliasreq *, struct in6_ifaddr *, int); #define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa)) #define ia62ifa(ia6) (&((ia6)->ia_ifa)) + void -in6_ifaddloop(struct ifaddr *ifa) +in6_newaddrmsg(struct in6_ifaddr *ia, int cmd) { struct sockaddr_dl gateway; struct sockaddr_in6 mask, addr; struct rtentry rt; - struct in6_ifaddr *ia; - struct ifnet *ifp; - struct llentry *ln; - ia = ifa2ia6(ifa); - ifp = ifa->ifa_ifp; - IF_AFDATA_LOCK(ifp); - ifa->ifa_rtrequest = nd6_rtrequest; - ln = lla_lookup(LLTABLE6(ifp), (LLE_CREATE | LLE_IFADDR | - LLE_EXCLUSIVE), (struct sockaddr *)&ia->ia_addr); - IF_AFDATA_UNLOCK(ifp); - if (ln != NULL) { - ln->la_expire = 0; /* for IPv6 this means permanent */ - ln->ln_state = ND6_LLINFO_REACHABLE; - /* - * initialize for rtmsg generation - */ - bzero(&gateway, sizeof(gateway)); - gateway.sdl_len = sizeof(gateway); - gateway.sdl_family = AF_LINK; - gateway.sdl_nlen = 0; - gateway.sdl_alen = 6; - memcpy(gateway.sdl_data, &ln->ll_addr.mac_aligned, - sizeof(ln->ll_addr)); - LLE_WUNLOCK(ln); - } + /* + * initialize for rtmsg generation + */ + bzero(&gateway, sizeof(gateway)); + gateway.sdl_len = sizeof(gateway); + gateway.sdl_family = AF_LINK; bzero(&rt, sizeof(rt)); rt.rt_gateway = (struct sockaddr *)&gateway; @@ -190,42 +172,11 @@ in6_ifaddloop(struct ifaddr *ifa) memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr)); rt_mask(&rt) = (struct sockaddr *)&mask; rt_key(&rt) = (struct sockaddr *)&addr; - rt.rt_flags = RTF_UP | RTF_HOST | RTF_STATIC; + rt.rt_flags = RTF_HOST | RTF_STATIC; + if (cmd == RTM_ADD) + rt.rt_flags |= RTF_UP; /* Announce arrival of local address to all FIBs. */ - rt_newaddrmsg(RTM_ADD, ifa, 0, &rt); -} - -void -in6_ifremloop(struct ifaddr *ifa) -{ - struct sockaddr_dl gateway; - struct sockaddr_in6 mask, addr; - struct rtentry rt0; - struct in6_ifaddr *ia; - struct ifnet *ifp; - - ia = ifa2ia6(ifa); - ifp = ifa->ifa_ifp; - memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr)); - memcpy(&mask, &ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); - lltable_prefix_free(AF_INET6, (struct sockaddr *)&addr, - (struct sockaddr *)&mask, LLE_STATIC); - - /* - * initialize for rtmsg generation - */ - bzero(&gateway, sizeof(gateway)); - gateway.sdl_len = sizeof(gateway); - gateway.sdl_family = AF_LINK; - gateway.sdl_nlen = 0; - gateway.sdl_alen = ifp->if_addrlen; - bzero(&rt0, sizeof(rt0)); - rt0.rt_gateway = (struct sockaddr *)&gateway; - rt_mask(&rt0) = (struct sockaddr *)&mask; - rt_key(&rt0) = (struct sockaddr *)&addr; - rt0.rt_flags = RTF_HOST | RTF_STATIC; - /* Announce removal of local address to all FIBs. */ - rt_newaddrmsg(RTM_DELETE, ifa, 0, &rt0); + rt_newaddrmsg(cmd, &ia->ia_ifa, 0, &rt); } int @@ -1031,13 +982,13 @@ in6_update_ifa(struct ifnet *ifp, struct } if (hostIsNew) - error = in6_setup_ifa(ifp, ifra, ia, flags); + error = in6_broadcast_ifa(ifp, ifra, ia, flags); return (error); } /* - * Fill in basic IPv6 address request info + * Fill in basic IPv6 address request info. */ void in6_prepare_ifra(struct in6_aliasreq *ifra, const struct in6_addr *addr, @@ -1078,6 +1029,14 @@ in6_validate_ifra(struct ifnet *ifp, str ifra->ifra_dstaddr.sin6_family != AF_INET6 && ifra->ifra_dstaddr.sin6_family != AF_UNSPEC) return (EAFNOSUPPORT); + + /* + * Validate address + */ + if (ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6) || + ifra->ifra_addr.sin6_family != AF_INET6) + return (EINVAL); + /* * validate ifra_prefixmask. don't check sin6_family, netmask * does not carry fields other than sin6_len. @@ -1211,10 +1170,12 @@ in6_alloc_ifa(struct ifnet *ifp, struct ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; ia->ia_addr.sin6_family = AF_INET6; ia->ia_addr.sin6_len = sizeof(ia->ia_addr); + /* XXX: Can we assign ,sin6_addr and skip the rest? */ + ia->ia_addr = ifra->ifra_addr; ia->ia6_createtime = time_uptime; if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) { /* - * XXX: some functions expect that ifa_dstaddr is not + * Some functions expect that ifa_dstaddr is not * NULL for p2p interfaces. */ ia->ia_ifa.ifa_dstaddr = @@ -1222,7 +1183,15 @@ in6_alloc_ifa(struct ifnet *ifp, struct } else { ia->ia_ifa.ifa_dstaddr = NULL; } + + /* set prefix mask if any */ ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; + if (ifra->ifra_prefixmask.sin6_len != 0) { + ia->ia_prefixmask.sin6_family = AF_INET6; + ia->ia_prefixmask.sin6_len = ifra->ifra_prefixmask.sin6_len; + ia->ia_prefixmask.sin6_addr = ifra->ifra_prefixmask.sin6_addr; + } + ia->ia_ifp = ifp; ifa_ref(&ia->ia_ifa); /* if_addrhead */ IF_ADDR_WLOCK(ifp); @@ -1232,30 +1201,28 @@ in6_alloc_ifa(struct ifnet *ifp, struct ifa_ref(&ia->ia_ifa); /* in6_ifaddrhead */ IN6_IFADDR_WLOCK(); TAILQ_INSERT_TAIL(&V_in6_ifaddrhead, ia, ia_link); - LIST_INSERT_HEAD(IN6ADDR_HASH(&ifra->ifra_addr.sin6_addr), - ia, ia6_hash); + LIST_INSERT_HEAD(IN6ADDR_HASH(&ia->ia_addr.sin6_addr), ia, ia6_hash); IN6_IFADDR_WUNLOCK(); return (ia); } +/* + * Update/configure interface address parameters: + * + * 1) Update lifetime + * 2) Update interface metric ad flags + * 3) Notify other subsystems + */ static int in6_update_ifa_internal(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *ia, int hostIsNew, int flags) { - struct sockaddr_in6 *pdst; int error; - char ip6buf[INET6_ADDRSTRLEN]; /* update timestamp */ ia->ia6_updatetime = time_uptime; - /* set prefix mask */ - if (ifra->ifra_prefixmask.sin6_len != 0) { - ia->ia_prefixmask = ifra->ifra_prefixmask; - ia->ia_prefixmask.sin6_family = AF_INET6; - } - /* * Set lifetimes. We do not refer to ia6t_expire and ia6t_preferred * to see if the address is deprecated or invalidated, but initialize @@ -1282,6 +1249,9 @@ in6_update_ifa_internal(struct ifnet *if ia->ia6_lifetime.ia6t_preferred = time_uptime; } + /* Update metric */ + ia->ia_ifa.ifa_metric = ifp->if_metric; + /* * configure address flags. */ @@ -1300,53 +1270,40 @@ in6_update_ifa_internal(struct ifnet *if if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) ia->ia6_flags |= IN6_IFF_TENTATIVE; - /* - * If a new destination address is specified, scrub the old one and - * install the new destination. Note that the interface must be - * p2p or loopback (see the check above.) - */ - pdst = &ifra->ifra_dstaddr; - if (pdst->sin6_family == AF_INET6 && - !IN6_ARE_ADDR_EQUAL(&pdst->sin6_addr, &ia->ia_dstaddr.sin6_addr)) { - if ((ia->ia_flags & IFA_ROUTE) != 0 && - (rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST) != 0)) { - nd6log((LOG_ERR, "in6_update_ifa_internal: failed to " - "remove a route to the old destination: %s\n", - ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); - /* proceed anyway... */ - } else - ia->ia_flags &= ~IFA_ROUTE; - ia->ia_dstaddr = *pdst; - } - - /* reset the interface and routing table appropriately. */ - if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0) { - /* - * XXX: if a change of an existing address failed, keep the entry - * anyway. - */ - } + /* notify other subsystems */ + error = in6_notify_ifa(ifp, ia, ifra, hostIsNew); return (error); } +/* + * Do link-level ifa job: + * 1) Add lle entry for added address + * 2) Notifies routing socket users about new address + * 3) join appropriate multicast group + * 4) start DAD if enabled + */ static int -in6_setup_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, +in6_broadcast_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *ia, int flags) { struct in6_multi *in6m_sol; int error = 0; /* Add local address to lltable, if necessary (ex. on p2p link). */ - in6_ifaddloop(&(ia->ia_ifa)); + if ((error = nd6_add_ifa_lle(ia)) != 0) { + in6_purgeaddr(&ia->ia_ifa); + ifa_free(&ia->ia_ifa); + return (error); + } /* Join necessary multicast groups. */ in6m_sol = NULL; if ((ifp->if_flags & IFF_MULTICAST) != 0) { error = in6_update_ifa_join_mc(ifp, ifra, ia, flags, &in6m_sol); if (error != 0) { - ifa_free(&ia->ia_ifa); in6_purgeaddr(&ia->ia_ifa); + ifa_free(&ia->ia_ifa); return (error); } } @@ -1571,7 +1528,7 @@ in6_purgeaddr(struct ifaddr *ifa) nd6_dad_stop(ifa); /* Remove local address entry from lltable. */ - in6_ifremloop(ifa); + nd6_rem_ifa_lle(ia); /* Leave multicast groups. */ error = in6_purgeaddr_mc(ifp, ia, ifa0); @@ -1650,29 +1607,33 @@ in6_purgeif(struct ifnet *ifp) } /* - * Initialize an interface's IPv6 address and routing table entry. + * Notifies other other subsystems about address change/arrival: + * 1) Notifies device handler on first IPv6 address assignment + * 2) Handle routing table changes for P2P links and route + * 3) Handle routing table changes for address host route */ static int -in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, - struct sockaddr_in6 *sin6, int newhost) +in6_notify_ifa(struct ifnet *ifp, struct in6_ifaddr *ia, + struct in6_aliasreq *ifra, int hostIsNew) { int error = 0, plen, ifacount = 0; struct ifaddr *ifa; + struct sockaddr_in6 *pdst; + char ip6buf[INET6_ADDRSTRLEN]; /* * Give the interface a chance to initialize * if this is its first address, - * and to validate the address if necessary. */ - IF_ADDR_RLOCK(ifp); - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - ifacount++; + if (hostIsNew != 0) { + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + ifacount++; + } + IF_ADDR_RUNLOCK(ifp); } - IF_ADDR_RUNLOCK(ifp); - - ia->ia_addr = *sin6; if (ifacount <= 1 && ifp->if_ioctl) { error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); @@ -1680,12 +1641,26 @@ in6_ifinit(struct ifnet *ifp, struct in6 return (error); } - ia->ia_ifa.ifa_metric = ifp->if_metric; - - /* we could do in(6)_socktrim here, but just omit it at this moment. */ + /* + * If a new destination address is specified, scrub the old one and + * install the new destination. Note that the interface must be + * p2p or loopback. + */ + pdst = &ifra->ifra_dstaddr; + if (pdst->sin6_family == AF_INET6 && + !IN6_ARE_ADDR_EQUAL(&pdst->sin6_addr, &ia->ia_dstaddr.sin6_addr)) { + if ((ia->ia_flags & IFA_ROUTE) != 0 && + (rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST) != 0)) { + nd6log((LOG_ERR, "in6_update_ifa_internal: failed to " + "remove a route to the old destination: %s\n", + ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); + /* proceed anyway... */ + } else + ia->ia_flags &= ~IFA_ROUTE; + ia->ia_dstaddr = *pdst; + } /* - * Special case: * If a new destination address is specified for a point-to-point * interface, install a route to the destination as an interface * direct route. @@ -1696,19 +1671,19 @@ in6_ifinit(struct ifnet *ifp, struct in6 if (!(ia->ia_flags & IFA_ROUTE) && plen == 128 && ia->ia_dstaddr.sin6_family == AF_INET6) { int rtflags = RTF_UP | RTF_HOST; - error = rtinit(&ia->ia_ifa, RTM_ADD, ia->ia_flags | rtflags); - if (error) - return (error); - ia->ia_flags |= IFA_ROUTE; /* * Handle the case for ::1 . */ if (ifp->if_flags & IFF_LOOPBACK) ia->ia_flags |= IFA_RTSELF; + error = rtinit(&ia->ia_ifa, RTM_ADD, ia->ia_flags | rtflags); + if (error) + return (error); + ia->ia_flags |= IFA_ROUTE; } /* - * add a loopback route to self + * add a loopback route to self if not exists */ if (!(ia->ia_flags & IFA_RTSELF) && V_nd6_useloopback) { error = ifa_add_loopback_route((struct ifaddr *)ia, Modified: head/sys/netinet6/in6_var.h ============================================================================== --- head/sys/netinet6/in6_var.h Sun Jan 19 13:51:46 2014 (r260881) +++ head/sys/netinet6/in6_var.h Sun Jan 19 16:07:27 2014 (r260882) @@ -822,12 +822,11 @@ int in6_prefix_ioctl(struct socket *, u_ int in6_prefix_add_ifid(int, struct in6_ifaddr *); void in6_prefix_remove_ifid(int, struct in6_ifaddr *); void in6_purgeprefix(struct ifnet *); -void in6_ifremloop(struct ifaddr *); -void in6_ifaddloop(struct ifaddr *); int in6_is_addr_deprecated(struct sockaddr_in6 *); int in6_src_ioctl(u_long, caddr_t); +void in6_newaddrmsg(struct in6_ifaddr *, int); /* * Extended API for IPv6 FIB support. */ Modified: head/sys/netinet6/nd6.c ============================================================================== --- head/sys/netinet6/nd6.c Sun Jan 19 13:51:46 2014 (r260881) +++ head/sys/netinet6/nd6.c Sun Jan 19 16:07:27 2014 (r260882) @@ -133,6 +133,7 @@ static int regen_tmpaddr(struct in6_ifad static struct llentry *nd6_free(struct llentry *, int); static void nd6_llinfo_timer(void *); static void clear_llinfo_pqueue(struct llentry *); +static void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *); static VNET_DEFINE(struct callout, nd6_slowtimo_ch); #define V_nd6_slowtimo_ch VNET(nd6_slowtimo_ch) @@ -2177,6 +2178,62 @@ nd6_need_cache(struct ifnet *ifp) } /* + * Add pernament ND6 link-layer record for given + * interface address. + * + * Very similar to IPv4 arp_ifinit(), but: + * 1) IPv6 DAD is performed in different place + * 2) It is called by IPv6 protocol stack in contrast to + * arp_ifinit() which is typically called in SIOCSIFADDR + * driver ioctl handler. + * + */ +int +nd6_add_ifa_lle(struct in6_ifaddr *ia) +{ + struct ifnet *ifp; + struct llentry *ln; + + ifp = ia->ia_ifa.ifa_ifp; + IF_AFDATA_LOCK(ifp); + ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; + ln = lla_lookup(LLTABLE6(ifp), (LLE_CREATE | LLE_IFADDR | + LLE_EXCLUSIVE), (struct sockaddr *)&ia->ia_addr); + IF_AFDATA_UNLOCK(ifp); + if (ln != NULL) { + ln->la_expire = 0; /* for IPv6 this means permanent */ + ln->ln_state = ND6_LLINFO_REACHABLE; + LLE_WUNLOCK(ln); + in6_newaddrmsg(ia, RTM_ADD); + return (0); + } + + return (ENOBUFS); +} + +/* + * Removes ALL lle records for interface address prefix. + * XXXME: That's probably not we really want to do, we need + * to remove address record only and keep other records + * until we determine if given prefix is really going + * to be removed. + */ +void +nd6_rem_ifa_lle(struct in6_ifaddr *ia) +{ + struct sockaddr_in6 mask, addr; + struct ifnet *ifp; + + in6_newaddrmsg(ia, RTM_DELETE); + + ifp = ia->ia_ifa.ifa_ifp; + memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr)); + memcpy(&mask, &ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); + lltable_prefix_free(AF_INET6, (struct sockaddr *)&addr, + (struct sockaddr *)&mask, LLE_STATIC); +} + +/* * the callers of this function need to be re-worked to drop * the lle lock, drop here for now */ Modified: head/sys/netinet6/nd6.h ============================================================================== --- head/sys/netinet6/nd6.h Sun Jan 19 13:51:46 2014 (r260881) +++ head/sys/netinet6/nd6.h Sun Jan 19 16:07:27 2014 (r260882) @@ -404,7 +404,6 @@ void nd6_purge(struct ifnet *); void nd6_nud_hint(struct rtentry *, struct in6_addr *, int); int nd6_resolve(struct ifnet *, struct rtentry *, struct mbuf *, struct sockaddr *, u_char *); -void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *); int nd6_ioctl(u_long, caddr_t, struct ifnet *); struct llentry *nd6_cache_lladdr(struct ifnet *, struct in6_addr *, char *, int, int, int); @@ -416,6 +415,8 @@ int nd6_output_lle(struct ifnet *, struc int nd6_output_flush(struct ifnet *, struct ifnet *, struct mbuf *, struct sockaddr_in6 *, struct route *); int nd6_need_cache(struct ifnet *); +int nd6_add_ifa_lle(struct in6_ifaddr *); +void nd6_rem_ifa_lle(struct in6_ifaddr *); int nd6_storelladdr(struct ifnet *, struct mbuf *, const struct sockaddr *, u_char *, struct llentry **);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201401191607.s0JG7SsM084760>