From owner-svn-src-all@FreeBSD.ORG Sat Jan 18 15:52:53 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 7C20059F; Sat, 18 Jan 2014 15:52:53 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 6642F1C9D; Sat, 18 Jan 2014 15:52:53 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id s0IFqrcb021465; Sat, 18 Jan 2014 15:52:53 GMT (envelope-from melifaro@svn.freebsd.org) Received: (from melifaro@localhost) by svn.freebsd.org (8.14.7/8.14.7/Submit) id s0IFqr3k021464; Sat, 18 Jan 2014 15:52:53 GMT (envelope-from melifaro@svn.freebsd.org) Message-Id: <201401181552.s0IFqr3k021464@svn.freebsd.org> From: "Alexander V. Chernikov" Date: Sat, 18 Jan 2014 15:52:53 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r260851 - head/sys/netinet6 X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 18 Jan 2014 15:52:53 -0000 Author: melifaro Date: Sat Jan 18 15:52:52 2014 New Revision: 260851 URL: http://svnweb.freebsd.org/changeset/base/260851 Log: Split in6_update_ifa() into smaller pieces leaving functionality intact. Discussed with: ae MFC after: 2 weeks Modified: head/sys/netinet6/in6.c Modified: head/sys/netinet6/in6.c ============================================================================== --- head/sys/netinet6/in6.c Sat Jan 18 14:47:34 2014 (r260850) +++ head/sys/netinet6/in6.c Sat Jan 18 15:52:52 2014 (r260851) @@ -139,6 +139,15 @@ static void in6_unlink_ifa(struct in6_if int (*faithprefix_p)(struct in6_addr *); +static int in6_validate_ifra(struct ifnet *, struct in6_aliasreq *, + struct in6_ifaddr *, int); +static struct in6_ifaddr *in6_alloc_ifa(struct ifnet *, + 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 *, + struct in6_ifaddr *, int); + #define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa)) #define ia62ifa(ia6) (&((ia6)->ia_ifa)) @@ -1001,11 +1010,39 @@ int in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *ia, int flags) { - int error = 0, hostIsNew = 0, plen = -1; + int error, hostIsNew = 0; + + if ((error = in6_validate_ifra(ifp, ifra, ia, flags)) != 0) + return (error); + + if (ia == NULL) { + hostIsNew = 1; + if ((ia = in6_alloc_ifa(ifp, ifra, flags)) == NULL) + return (ENOBUFS); + } + + error = in6_update_ifa_internal(ifp, ifra, ia, hostIsNew, flags); + if (error != 0) { + if (hostIsNew != 0) { + in6_unlink_ifa(ia, ifp); + ifa_free(&ia->ia_ifa); + } + return (error); + } + + if (hostIsNew) + error = in6_setup_ifa(ifp, ifra, ia, flags); + + return (error); +} + +static int +in6_validate_ifra(struct ifnet *ifp, struct in6_aliasreq *ifra, + struct in6_ifaddr *ia, int flags) +{ + int plen = -1; struct sockaddr_in6 dst6; struct in6_addrlifetime *lt; - struct in6_multi *in6m_sol; - int delay; char ip6buf[INET6_ADDRSTRLEN]; /* Validate parameters */ @@ -1072,6 +1109,9 @@ in6_update_ifa(struct ifnet *ifp, struct if (sa6_embedscope(&dst6, 0)) return (EINVAL); /* XXX: should be impossible */ } + /* Modify original ifra_dstaddr to reflect changes */ + ifra->ifra_dstaddr = dst6; + /* * The destination address can be specified only for a p2p or a * loopback interface. If specified, the corresponding prefix length @@ -1107,12 +1147,36 @@ in6_update_ifa(struct ifnet *ifp, struct return (0); /* there's nothing to do */ } + /* Check prefix mask */ + if (ia != NULL && ifra->ifra_prefixmask.sin6_len != 0) { + /* + * We prohibit changing the prefix length of an existing + * address, because + * + such an operation should be rare in IPv6, and + * + the operation would confuse prefix management. + */ + if (ia->ia_prefixmask.sin6_len != 0 && + in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) { + nd6log((LOG_INFO, "in6_validate_ifa: the prefix length " + "of an existing %s address should not be changed\n", + ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); + + return (EINVAL); + } + } + + return (0); +} + /* * If this is a new address, allocate a new ifaddr and link it * into chains. */ - if (ia == NULL) { - hostIsNew = 1; +static struct in6_ifaddr * +in6_alloc_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int flags) +{ + struct in6_ifaddr *ia; + /* * When in6_update_ifa() is called in a process of a received * RA, it is called under an interrupt context. So, we should @@ -1120,7 +1184,7 @@ in6_update_ifa(struct ifnet *ifp, struct */ ia = (struct in6_ifaddr *)ifa_alloc(sizeof(*ia), M_NOWAIT); if (ia == NULL) - return (ENOBUFS); + return (NULL); LIST_INIT(&ia->ia6_memberships); /* Initialize the address and masks, and put time stamp */ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; @@ -1150,52 +1214,28 @@ in6_update_ifa(struct ifnet *ifp, struct LIST_INSERT_HEAD(IN6ADDR_HASH(&ifra->ifra_addr.sin6_addr), ia, ia6_hash); IN6_IFADDR_WUNLOCK(); - } + + return (ia); +} + +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) { - /* - * We prohibit changing the prefix length of an existing - * address, because - * + such an operation should be rare in IPv6, and - * + the operation would confuse prefix management. - */ - if (ia->ia_prefixmask.sin6_len && - in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) { - nd6log((LOG_INFO, "in6_update_ifa: the prefix length of an" - " existing (%s) address should not be changed\n", - ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); - error = EINVAL; - goto unlink; - } + if (ifra->ifra_prefixmask.sin6_len != 0) { ia->ia_prefixmask = ifra->ifra_prefixmask; ia->ia_prefixmask.sin6_family = AF_INET6; } /* - * 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.) - */ - if (dst6.sin6_family == AF_INET6 && - !IN6_ARE_ADDR_EQUAL(&dst6.sin6_addr, &ia->ia_dstaddr.sin6_addr)) { - int e; - - if ((ia->ia_flags & IFA_ROUTE) != 0 && - (e = rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST)) != 0) { - nd6log((LOG_ERR, "in6_update_ifa: 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 = dst6; - } - - /* * Set lifetimes. We do not refer to ia6t_expire and ia6t_preferred * to see if the address is deprecated or invalidated, but initialize * these members for applications. @@ -1212,14 +1252,6 @@ in6_update_ifa(struct ifnet *ifp, struct } else ia->ia6_lifetime.ia6t_preferred = 0; - /* reset the interface and routing table appropriately. */ - if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0) - goto unlink; - - /* - * configure address flags. - */ - ia->ia6_flags = ifra->ifra_flags; /* * backward compatibility - if IN6_IFF_DEPRECATED is set from the * userland, make it deprecated. @@ -1228,6 +1260,12 @@ in6_update_ifa(struct ifnet *ifp, struct ia->ia6_lifetime.ia6t_pltime = 0; ia->ia6_lifetime.ia6t_preferred = time_uptime; } + + /* + * configure address flags. + */ + ia->ia6_flags = ifra->ifra_flags; + /* * Make the address tentative before joining multicast addresses, * so that corresponding MLD responses would not have a tentative @@ -1242,10 +1280,44 @@ in6_update_ifa(struct ifnet *ifp, struct ia->ia6_flags |= IN6_IFF_TENTATIVE; /* - * We are done if we have simply modified an existing address. + * 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.) */ - if (!hostIsNew) - return (error); + 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. + */ + } + + return (error); +} + +static int +in6_setup_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)); /* * Beyond this point, we should call in6_purgeaddr upon an error, @@ -1256,8 +1328,11 @@ in6_update_ifa(struct ifnet *ifp, struct in6m_sol = NULL; if ((ifp->if_flags & IFF_MULTICAST) != 0) { error = in6_update_ifa_join_mc(ifp, ifra, ia, flags, &in6m_sol); - if (error) - goto cleanup; + if (error != 0) { + ifa_free(&ia->ia_ifa); + in6_purgeaddr(&ia->ia_ifa); + return (error); + } } /* @@ -1267,7 +1342,7 @@ in6_update_ifa(struct ifnet *ifp, struct if (in6if_do_dad(ifp) && ((ifra->ifra_flags & IN6_IFF_NODAD) == 0) && (ia->ia6_flags & IN6_IFF_TENTATIVE)) { - int mindelay, maxdelay; + int delay, mindelay, maxdelay; delay = 0; if ((flags & IN6_IFAUPDATE_DADDELAY)) { @@ -1298,26 +1373,8 @@ in6_update_ifa(struct ifnet *ifp, struct nd6_dad_start((struct ifaddr *)ia, delay); } - KASSERT(hostIsNew, ("in6_update_ifa: !hostIsNew")); ifa_free(&ia->ia_ifa); return (error); - - unlink: - /* - * XXX: if a change of an existing address failed, keep the entry - * anyway. - */ - if (hostIsNew) { - in6_unlink_ifa(ia, ifp); - ifa_free(&ia->ia_ifa); - } - return (error); - - cleanup: - KASSERT(hostIsNew, ("in6_update_ifa: cleanup: !hostIsNew")); - ifa_free(&ia->ia_ifa); - in6_purgeaddr(&ia->ia_ifa); - return error; } /* @@ -1644,10 +1701,6 @@ in6_ifinit(struct ifnet *ifp, struct in6 ia->ia_flags |= IFA_RTSELF; } - /* Add local address to lltable, if necessary (ex. on p2p link). */ - if (newhost) - in6_ifaddloop(&(ia->ia_ifa)); - return (error); }