summaryrefslogtreecommitdiffstats
path: root/sys/netinet6/in6_prefix.c
diff options
context:
space:
mode:
authorshin <shin@FreeBSD.org>1999-11-22 02:45:11 +0000
committershin <shin@FreeBSD.org>1999-11-22 02:45:11 +0000
commitcad2014b2749528351ec5180e88a5929efebbfc4 (patch)
treea147aa319428e26625f19209916c257b71cfd2e1 /sys/netinet6/in6_prefix.c
parent00ea4eac2008e0a2aaa1eda46cc090b7c1741a2d (diff)
downloadFreeBSD-src-cad2014b2749528351ec5180e88a5929efebbfc4.zip
FreeBSD-src-cad2014b2749528351ec5180e88a5929efebbfc4.tar.gz
KAME netinet6 basic part(no IPsec,no V6 Multicast Forwarding, no UDP/TCP
for IPv6 yet) With this patch, you can assigne IPv6 addr automatically, and can reply to IPv6 ping. Reviewed by: freebsd-arch, cvs-committers Obtained from: KAME project
Diffstat (limited to 'sys/netinet6/in6_prefix.c')
-rw-r--r--sys/netinet6/in6_prefix.c1106
1 files changed, 1106 insertions, 0 deletions
diff --git a/sys/netinet6/in6_prefix.c b/sys/netinet6/in6_prefix.c
new file mode 100644
index 0000000..8da07c4
--- /dev/null
+++ b/sys/netinet6/in6_prefix.c
@@ -0,0 +1,1106 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in.c 8.2 (Berkeley) 11/15/93
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sockio.h>
+#include <sys/systm.h>
+#include <sys/syslog.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/in6_prefix.h>
+#include <netinet6/ip6_var.h>
+
+static MALLOC_DEFINE(M_IP6RR, "ip6rr", "IPv6 Router Renumbering Prefix");
+static MALLOC_DEFINE(M_RR_ADDR, "rp_addr", "IPv6 Router Renumbering Ifid");
+
+struct rr_prhead rr_prefix;
+
+#include <net/net_osdep.h>
+
+static int create_ra_entry __P((struct rp_addr **rapp));
+static int add_each_prefix __P((struct socket *so,
+ struct rr_prefix *rpp));
+static void free_rp_entries __P((struct rr_prefix *rpp));
+static int link_stray_ia6s __P((struct rr_prefix *rpp));
+
+/*
+ * Copy bits from src to tgt, from off bit for len bits.
+ * Caller must specify collect tgtsize and srcsize.
+ */
+static void
+bit_copy(char *tgt, u_int tgtsize, char *src, u_int srcsize,
+ u_int off, u_int len)
+{
+ char *sp, *tp;
+
+ /* arg values check */
+ if (srcsize < off || srcsize < (off + len) ||
+ tgtsize < off || tgtsize < (off + len)) {
+ log(LOG_ERR,
+ "in6_prefix.c: bit_copy: invalid args: srcsize %d,\n"
+ "tgtsize %d, off %d, len %d\n", srcsize, tgtsize, off,
+ len);
+ return;
+ }
+
+ /* search start point */
+ for (sp = src, tp = tgt; off >= 8; sp++, tp++)
+ off-=8;
+ /* copy starting bits */
+ if (off) {
+ char setbit;
+ int startbits;
+
+ startbits = min((8 - off), len);
+
+ for (setbit = (0x80 >> off); startbits;
+ setbit >>= 1, startbits--, len--)
+ *tp |= (setbit & *sp);
+ tp++;
+ sp++;
+ }
+ /* copy midium bits */
+ for (; len >= 8; sp++, tp++) {
+ *tp = *sp;
+ len-=8;
+ }
+ /* copy ending bits */
+ if (len) {
+ char setbit;
+
+ for (setbit = 0x80; len; setbit >>= 1, len--)
+ *tp |= (setbit & *sp);
+ }
+}
+
+static struct ifprefix *
+in6_prefixwithifp(struct ifnet *ifp, int plen, struct in6_addr *dst)
+{
+ struct ifprefix *ifpr;
+
+ /* search matched prefix */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list)) {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (plen <= in6_matchlen(dst, IFPR_IN6(ifpr)))
+ break;
+ }
+ return (ifpr);
+}
+
+/*
+ * Search prefix which matches arg prefix as specified in
+ * draft-ietf-ipngwg-router-renum-08.txt
+ */
+static struct rr_prefix *
+search_matched_prefix(struct ifnet *ifp, struct in6_prefixreq *ipr)
+{
+ struct ifprefix *ifpr;
+ struct ifaddr *ifa;
+ struct rr_prefix *rpp;
+
+ /* search matched prefix */
+ ifpr = in6_prefixwithifp(ifp, ipr->ipr_plen,
+ &ipr->ipr_prefix.sin6_addr);
+ if (ifpr != NULL)
+ return ifpr2rp(ifpr);
+
+ /*
+ * search matched addr, and then search prefix
+ * which matches the addr
+ */
+
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (ipr->ipr_plen <=
+ in6_matchlen(&ipr->ipr_prefix.sin6_addr, IFA_IN6(ifa)))
+ break;
+ }
+ if (ifa == NULL)
+ return NULL;
+
+ rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
+ if (rpp != 0)
+ return rpp;
+
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list)) {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr->ifpr_plen <= in6_matchlen(IFA_IN6(ifa),
+ IFPR_IN6(ifpr)))
+ break;
+ }
+ if (ifpr != NULL)
+ log(LOG_ERR, "in6_prefix.c: search_matched_prefix: addr %s"
+ "has no pointer to prefix %s", ip6_sprintf(IFA_IN6(ifa)),
+ ip6_sprintf(IFPR_IN6(ifpr)));
+ return ifpr2rp(ifpr);
+}
+
+/*
+ * Search prefix which matches arg prefix as specified in
+ * draft-ietf-ipngwg-router-renum-08.txt, and mark it if exists.
+ * Return 1 if anything matched, and 0 if nothing matched.
+ */
+static int
+mark_matched_prefixes(u_long cmd, struct ifnet *ifp, struct in6_rrenumreq *irr)
+{
+ struct ifprefix *ifpr;
+ struct ifaddr *ifa;
+ int matchlen, matched = 0;
+
+ /* search matched prefixes */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list)) {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr,
+ IFPR_IN6(ifpr));
+ if (irr->irr_m_minlen > ifpr->ifpr_plen ||
+ irr->irr_m_maxlen < ifpr->ifpr_plen ||
+ irr->irr_m_len > matchlen)
+ continue;
+ matched = 1;
+ ifpr2rp(ifpr)->rp_statef_addmark = 1;
+ if (cmd == SIOCCIFPREFIX_IN6)
+ ifpr2rp(ifpr)->rp_statef_delmark = 1;
+ }
+
+ /*
+ * search matched addr, and then search prefixes
+ * which matche the addr
+ */
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ struct rr_prefix *rpp;
+
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr,
+ IFA_IN6(ifa));
+ if (irr->irr_m_minlen > matchlen ||
+ irr->irr_m_maxlen < matchlen || irr->irr_m_len > matchlen)
+ continue;
+ rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
+ if (rpp != 0) {
+ matched = 1;
+ rpp->rp_statef_addmark = 1;
+ if (cmd == SIOCCIFPREFIX_IN6)
+ rpp->rp_statef_delmark = 1;
+ } else
+ log(LOG_WARNING, "in6_prefix.c: mark_matched_prefixes:"
+ "no back pointer to ifprefix for %s. "
+ "ND autoconfigured addr?",
+ ip6_sprintf(IFA_IN6(ifa)));
+ }
+ return matched;
+}
+
+/*
+ * Mark global prefixes as to be deleted.
+ */
+static void
+delmark_global_prefixes(struct ifnet *ifp, struct in6_rrenumreq *irr)
+{
+ struct ifprefix *ifpr;
+
+ /* search matched prefixes */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list)) {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ /* mark delete global prefix */
+ if (in6_addrscope(RP_IN6(ifpr2rp(ifpr))) ==
+ IPV6_ADDR_SCOPE_GLOBAL)
+ ifpr2rp(ifpr)->rp_statef_delmark = 1;
+ }
+}
+
+/* Unmark prefixes */
+static void
+unmark_prefixes(struct ifnet *ifp)
+{
+ struct ifprefix *ifpr;
+
+ /* unmark all prefix */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list)) {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ /* unmark prefix */
+ ifpr2rp(ifpr)->rp_statef_addmark = 0;
+ ifpr2rp(ifpr)->rp_statef_delmark = 0;
+ }
+}
+
+static void
+init_prefix_ltimes(struct rr_prefix *rpp)
+{
+ if (rpp->rp_pltime == RR_INFINITE_LIFETIME ||
+ rpp->rp_rrf_decrprefd == 0)
+ rpp->rp_preferred = 0;
+ else
+ rpp->rp_preferred = time_second + rpp->rp_pltime;
+ if (rpp->rp_vltime == RR_INFINITE_LIFETIME ||
+ rpp->rp_rrf_decrvalid == 0)
+ rpp->rp_expire = 0;
+ else
+ rpp->rp_expire = time_second + rpp->rp_vltime;
+}
+
+static int
+rr_are_ifid_equal(struct in6_addr *ii1, struct in6_addr *ii2, int ii_len)
+{
+ int ii_bytelen, ii_bitlen;
+ int p_bytelen, p_bitlen;
+
+ /* sanity check */
+ if (1 > ii_len ||
+ ii_len > 124) { /* as RFC2373, prefix is at least 4 bit */
+ log(LOG_ERR, "rr_are_ifid_equal: invalid ifid length(%d)\n",
+ ii_len);
+ return(0);
+ }
+
+ ii_bytelen = ii_len / 8;
+ ii_bitlen = ii_len % 8;
+
+ p_bytelen = sizeof(struct in6_addr) - ii_bytelen - 1;
+ p_bitlen = 8 - ii_bitlen;
+
+ if (bcmp(ii1->s6_addr + p_bytelen + 1, ii2->s6_addr + p_bytelen + 1,
+ ii_bytelen))
+ return(0);
+ if (((ii1->s6_addr[p_bytelen] << p_bitlen) & 0xff) !=
+ ((ii2->s6_addr[p_bytelen] << p_bitlen) & 0xff))
+ return(0);
+
+ return(1);
+}
+
+static struct rp_addr *
+search_ifidwithprefix(struct rr_prefix *rpp, struct in6_addr *ifid)
+{
+ struct rp_addr *rap;
+
+ for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
+ rap = rap->ra_entry.le_next)
+ if (rr_are_ifid_equal(ifid, &rap->ra_ifid,
+ (sizeof(struct in6_addr) << 3) -
+ rpp->rp_plen))
+ break;
+ return rap;
+}
+
+static int
+assigne_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia)
+{
+ int error = 0;
+ struct rp_addr *rap;
+ int s;
+
+ if ((error = create_ra_entry(&rap)) != 0)
+ return error;
+
+ /* copy interface id part */
+ bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
+ (caddr_t)IA6_IN6(ia),
+ sizeof(*IA6_IN6(ia)) << 3, rpp->rp_plen, iilen);
+ /* link to ia, and put into list */
+ rap->ra_addr = ia;
+ /*
+ * Can't point rp2ifpr(rpp) from ia->ia6_ifpr now,
+ * because rpp may be on th stack. should fix it?
+ */
+ s = splnet();
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ splx(s);
+
+ return 0;
+}
+
+int
+in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia)
+{
+ int plen = (sizeof(*IA6_IN6(ia)) << 3) - iilen;
+ struct ifprefix *ifpr;
+ struct rp_addr *rap;
+ int error = 0;
+
+ ifpr = in6_prefixwithifp(ia->ia_ifp, plen, IA6_IN6(ia));
+ if (ifpr == NULL) {
+ struct rr_prefix rp;
+ struct socket so;
+ int pplen = (plen == 128) ? 64 : plen;
+
+ /* allocate a prefix for ia, with default properties */
+
+ /* init rp */
+ bzero(&rp, sizeof(rp));
+ rp.rp_type = IN6_PREFIX_RR;
+ rp.rp_ifp = ia->ia_ifp;
+ rp.rp_plen = pplen;
+ rp.rp_prefix.sin6_len = sizeof(rp.rp_prefix);
+ rp.rp_prefix.sin6_family = AF_INET6;
+ bit_copy((char *)RP_IN6(&rp), sizeof(*RP_IN6(&rp)) << 3,
+ (char *)&ia->ia_addr.sin6_addr,
+ sizeof(ia->ia_addr.sin6_addr) << 3,
+ 0, pplen);
+ rp.rp_vltime = rp.rp_pltime = RR_INFINITE_LIFETIME;
+ rp.rp_raf_onlink = 1;
+ rp.rp_raf_auto = 1;
+ /* Is some FlagMasks for rrf necessary? */
+ rp.rp_rrf_decrvalid = rp.rp_rrf_decrprefd = 0;
+ rp.rp_origin = PR_ORIG_RR; /* can be renumbered */
+
+ /* create ra_entry */
+ error = link_stray_ia6s(&rp);
+ if (error != 0) {
+ free_rp_entries(&rp);
+ return error;
+ }
+
+ /* XXX: init dummy so */
+ bzero(&so, sizeof(so));
+
+ error = add_each_prefix(&so, &rp);
+
+ /* free each rp_addr entry */
+ free_rp_entries(&rp);
+
+ if (error != 0)
+ return error;
+
+ /* search again */
+ ifpr = in6_prefixwithifp(ia->ia_ifp, pplen, IA6_IN6(ia));
+ if (ifpr == NULL)
+ return 0;
+ }
+ rap = search_ifidwithprefix(ifpr2rp(ifpr), IA6_IN6(ia));
+ if (rap != NULL) {
+ if (rap->ra_addr == NULL)
+ rap->ra_addr = ia;
+ else if (rap->ra_addr != ia) {
+ /* There may be some inconsistencies between addrs. */
+ log(LOG_ERR, "ip6_prefix.c: addr %s/%d matched prefix"
+ "has already another ia %p(%s) on its ifid list",
+ ip6_sprintf(IA6_IN6(ia)), plen,
+ rap->ra_addr,
+ ip6_sprintf(IA6_IN6(rap->ra_addr)));
+ return EADDRINUSE /* XXX */;
+ }
+ ia->ia6_ifpr = ifpr;
+ return 0;
+ }
+ error = assigne_ra_entry(ifpr2rp(ifpr), iilen, ia);
+ if (error == 0)
+ ia->ia6_ifpr = ifpr;
+ return (error);
+}
+
+void
+in6_prefix_remove_ifid(int iilen, struct in6_ifaddr *ia)
+{
+ struct rp_addr *rap;
+
+ if (ia->ia6_ifpr == NULL)
+ return;
+ rap = search_ifidwithprefix(ifpr2rp(ia->ia6_ifpr), IA6_IN6(ia));
+ if (rap != NULL) {
+ int s = splnet();
+ LIST_REMOVE(rap, ra_entry);
+ splx(s);
+ free(rap, M_RR_ADDR);
+ }
+}
+
+static void
+add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap)
+{
+ struct in6_ifaddr *ia6;
+ struct in6_aliasreq ifra;
+ int error;
+
+ /* init ifra */
+ bzero(&ifra, sizeof(ifra));
+ strncpy(ifra.ifra_name, if_name(rpp->rp_ifp), sizeof(ifra.ifra_name));
+ ifra.ifra_addr.sin6_family = ifra.ifra_prefixmask.sin6_family =
+ AF_INET6;
+ ifra.ifra_addr.sin6_len = ifra.ifra_prefixmask.sin6_len =
+ sizeof(ifra.ifra_addr);
+ /* copy prefix part */
+ bit_copy((char *)&ifra.ifra_addr.sin6_addr,
+ sizeof(ifra.ifra_addr.sin6_addr) << 3,
+ (char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
+ 0, rpp->rp_plen);
+ /* copy interface id part */
+ bit_copy((char *)&ifra.ifra_addr.sin6_addr,
+ sizeof(ifra.ifra_addr.sin6_addr) << 3,
+ (char *)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
+ rpp->rp_plen, (sizeof(rap->ra_ifid) << 3) - rpp->rp_plen);
+ in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, rpp->rp_plen);
+ /* don't care ifra_flags for now */
+
+ ia6 = in6ifa_ifpwithaddr(rpp->rp_ifp, &ifra.ifra_addr.sin6_addr);
+ if (ia6 != NULL) {
+ if (ia6->ia6_ifpr == NULL) {
+ /* link this addr and the prefix each other */
+ rap->ra_addr = ia6;
+ ia6->ia6_ifpr = rp2ifpr(rpp);
+ return;
+ }
+ if (ia6->ia6_ifpr == rp2ifpr(rpp)) {
+ rap->ra_addr = ia6;
+ return;
+ }
+ /*
+ * The addr is already assigned to other
+ * prefix.
+ * There may be some inconsistencies between
+ * prefixes.
+ * e.g. overraped prefixes with common starting
+ * part and different plefixlen.
+ * Or, completely duplicated prefixes?
+ * log it and return.
+ */
+ log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr"
+ "%s/%d failed because there is already another addr %s/%d",
+ ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen,
+ ip6_sprintf(IA6_IN6(ia6)),
+ in6_mask2len(&ia6->ia_prefixmask.sin6_addr));
+ return;
+ }
+ /* propagate ANYCAST flag if it is set for ancestor addr */
+ if (rap->ra_flags.anycast != 0)
+ ifra.ifra_flags |= IN6_IFF_ANYCAST;
+ error = in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, rpp->rp_ifp,
+ curproc);
+ if (error != 0)
+ log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr"
+ "%s/%d failed because in6_control failed for error %d",
+ ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen,
+ error);
+ return;
+
+ /*
+ * link beween this addr and the prefix will be done
+ * in in6_prefix_add_ifid
+ */
+}
+
+static int
+rrpr_update(struct socket *so, struct rr_prefix *new)
+{
+ struct rr_prefix *rpp;
+ struct ifprefix *ifpr;
+ struct rp_addr *rap;
+ int s;
+
+ /* search existing prefix */
+ for (ifpr = TAILQ_FIRST(&new->rp_ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list)) {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr->ifpr_plen == new->rp_plen &&
+ in6_are_prefix_equal(IFPR_IN6(ifpr), RP_IN6(new),
+ ifpr->ifpr_plen))
+ break;
+ }
+ rpp = ifpr2rp(ifpr);
+ if (rpp != NULL) {
+ /*
+ * We got a prefix which we have seen in the past.
+ */
+ /*
+ * If the origin of the already-installed prefix is more
+ * preferable than the new one, ignore installation request.
+ */
+ if (rpp->rp_origin > new->rp_origin)
+ return(EPERM);
+
+ /* update prefix information */
+ rpp->rp_flags.prf_ra = new->rp_flags.prf_ra;
+ if (rpp->rp_origin >= PR_ORIG_RR)
+ rpp->rp_flags.prf_rr = new->rp_flags.prf_rr;
+ rpp->rp_vltime = new->rp_vltime;
+ rpp->rp_pltime = new->rp_pltime;
+ rpp->rp_expire = new->rp_expire;
+ rpp->rp_preferred = new->rp_preferred;
+ rpp->rp_statef_delmark = 0; /* cancel deletion */
+ /*
+ * Interface id related update.
+ * add rp_addr entries in new into rpp, if they have not
+ * been already included in rpp.
+ */
+ while (!LIST_EMPTY(&new->rp_addrhead))
+ {
+ rap = LIST_FIRST(&new->rp_addrhead);
+ LIST_REMOVE(rap, ra_entry);
+ if (search_ifidwithprefix(rpp, &rap->ra_ifid)
+ != NULL) {
+ free(rap, M_RR_ADDR);
+ continue;
+ }
+ s = splnet();
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ splx(s);
+ }
+ } else {
+ /*
+ * We got a fresh prefix.
+ */
+ /* create new prefix */
+ rpp = (struct rr_prefix *)malloc(sizeof(*rpp), M_IP6RR,
+ M_NOWAIT);
+ if (rpp == NULL) {
+ log(LOG_ERR, "in6_prefix.c: rrpr_update:%d"
+ ": ENOBUFS for rr_prefix", __LINE__);
+ return(ENOBUFS);
+ }
+ /* initilization */
+ *rpp = *new;
+ LIST_INIT(&rpp->rp_addrhead);
+ /* move rp_addr entries of new to rpp */
+ while (!LIST_EMPTY(&new->rp_addrhead))
+ {
+ rap = LIST_FIRST(&new->rp_addrhead);
+ LIST_REMOVE(rap, ra_entry);
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ }
+
+ /* let rp_ifpr.ifpr_prefix point rr_prefix. */
+ rpp->rp_ifpr.ifpr_prefix = (struct sockaddr *)&rpp->rp_prefix;
+ /* link rr_prefix entry to if_prefixhead */
+ {
+ struct ifnet *ifp = rpp->rp_ifp;
+ struct ifprefix *ifpr;
+
+ if ((ifpr = TAILQ_FIRST(&ifp->if_prefixhead))
+ != NULL) {
+ for ( ; TAILQ_NEXT(ifpr, ifpr_list);
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list))
+ continue;
+ TAILQ_NEXT(ifpr, ifpr_list) = rp2ifpr(rpp);
+ } else
+ TAILQ_FIRST(&ifp->if_prefixhead) =
+ rp2ifpr(rpp);
+ rp2ifpr(rpp)->ifpr_type = IN6_PREFIX_RR;
+ }
+ /* link rr_prefix entry to rr_prefix list */
+ s = splnet();
+ LIST_INSERT_HEAD(&rr_prefix, rpp, rp_entry);
+ splx(s);
+ }
+
+ if (!new->rp_raf_auto)
+ return 0;
+
+ /*
+ * Add an address for each interface id, if it is not yet
+ * If it existed but not pointing to the prefix yet,
+ * init the prefix pointer.
+ */
+ for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
+ rap = rap->ra_entry.le_next) {
+ if (rap->ra_addr != NULL) {
+ if (rap->ra_addr->ia6_ifpr == NULL)
+ rap->ra_addr->ia6_ifpr = rp2ifpr(rpp);
+ continue;
+ }
+ add_each_addr(so, rpp, rap);
+ }
+ return 0;
+}
+
+static int
+add_each_prefix(struct socket *so, struct rr_prefix *rpp)
+{
+ init_prefix_ltimes(rpp);
+ return(rrpr_update(so, rpp));
+}
+
+static void
+rp_remove(struct rr_prefix *rpp)
+{
+ int s;
+
+ s = splnet();
+ /* unlink rp_entry from if_prefixhead */
+ {
+ struct ifnet *ifp = rpp->rp_ifp;
+ struct ifprefix *ifpr;
+
+ if ((ifpr = TAILQ_FIRST(&ifp->if_prefixhead)) == rp2ifpr(rpp))
+ TAILQ_FIRST(&ifp->if_prefixhead) =
+ TAILQ_NEXT(ifpr, ifpr_list);
+ else {
+ while (TAILQ_NEXT(ifpr, ifpr_list) != NULL &&
+ (TAILQ_NEXT(ifpr, ifpr_list) != rp2ifpr(rpp)))
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list);
+ if (TAILQ_NEXT(ifpr, ifpr_list))
+ TAILQ_NEXT(ifpr, ifpr_list) =
+ TAILQ_NEXT(rp2ifpr(rpp), ifpr_list);
+ else
+ printf("Couldn't unlink rr_prefix from ifp\n");
+ }
+ }
+ /* unlink rp_entry from rr_prefix list */
+ LIST_REMOVE(rpp, rp_entry);
+ splx(s);
+ free(rpp, M_IP6RR);
+}
+
+static int
+create_ra_entry(struct rp_addr **rapp)
+{
+ *rapp = (struct rp_addr *)malloc(sizeof(struct rp_addr), M_RR_ADDR,
+ M_NOWAIT);
+ if (*rapp == NULL) {
+ log(LOG_ERR, "in6_prefix.c: init_newprefix:%d: ENOBUFS"
+ "for rp_addr", __LINE__);
+ return ENOBUFS;
+ }
+ bzero(*rapp, sizeof(*(*rapp)));
+
+ return 0;
+}
+
+static int
+init_newprefix(struct in6_rrenumreq *irr, struct ifprefix *ifpr,
+ struct rr_prefix *rpp)
+{
+ struct rp_addr *orap;
+
+ /* init rp */
+ bzero(rpp, sizeof(*rpp));
+ rpp->rp_type = IN6_PREFIX_RR;
+ rpp->rp_ifp = ifpr->ifpr_ifp;
+ rpp->rp_plen = ifpr->ifpr_plen;
+ rpp->rp_prefix.sin6_len = sizeof(rpp->rp_prefix);
+ rpp->rp_prefix.sin6_family = AF_INET6;
+ bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
+ (char *)&irr->irr_useprefix.sin6_addr,
+ sizeof(irr->irr_useprefix.sin6_addr) << 3,
+ 0, irr->irr_u_uselen);
+ /* copy keeplen part if necessary as necessary len */
+ if (irr->irr_u_uselen < ifpr->ifpr_plen)
+ bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
+ (char *)IFPR_IN6(ifpr), sizeof(*IFPR_IN6(ifpr)) << 3,
+ irr->irr_u_uselen,
+ min(ifpr->ifpr_plen - irr->irr_u_uselen,
+ irr->irr_u_keeplen));
+ for (orap = (ifpr2rp(ifpr)->rp_addrhead).lh_first; orap != NULL;
+ orap = orap->ra_entry.le_next) {
+ struct rp_addr *rap;
+ int error = 0;
+
+ if ((error = create_ra_entry(&rap)) != 0)
+ return error;
+ rap->ra_ifid = orap->ra_ifid;
+ rap->ra_flags.anycast = (orap->ra_addr != NULL &&
+ (orap->ra_addr->ia_flags &
+ IN6_IFF_ANYCAST) != 0) ? 1 : 0;
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ }
+ rpp->rp_vltime = irr->irr_vltime;
+ rpp->rp_pltime = irr->irr_pltime;
+ rpp->rp_raf_onlink = irr->irr_raf_mask_onlink ? irr->irr_raf_onlink :
+ ifpr2rp(ifpr)->rp_raf_onlink;
+ rpp->rp_raf_auto = irr->irr_raf_mask_auto ? irr->irr_raf_auto :
+ ifpr2rp(ifpr)->rp_raf_auto;
+ /* Is some FlagMasks for rrf necessary? */
+ rpp->rp_rrf = irr->irr_rrf;
+ rpp->rp_origin = irr->irr_origin;
+
+ return 0;
+}
+
+static void
+free_rp_entries(struct rr_prefix *rpp)
+{
+ /*
+ * This func is only called with rpp on stack(not on list).
+ * So no splnet() here
+ */
+ while (!LIST_EMPTY(&rpp->rp_addrhead))
+ {
+ struct rp_addr *rap;
+
+ rap = LIST_FIRST(&rpp->rp_addrhead);
+ LIST_REMOVE(rap, ra_entry);
+ free(rap, M_RR_ADDR);
+ }
+}
+
+static int
+add_useprefixes(struct socket *so, struct ifnet *ifp,
+ struct in6_rrenumreq *irr)
+{
+ struct ifprefix *ifpr, *nextifpr;
+ struct rr_prefix rp;
+ int error = 0;
+
+ /* add prefixes to each of marked prefix */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) {
+ nextifpr = TAILQ_NEXT(ifpr, ifpr_list);
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr2rp(ifpr)->rp_statef_addmark) {
+ if ((error = init_newprefix(irr, ifpr, &rp)) != 0)
+ break;
+ error = add_each_prefix(so, &rp);
+ }
+ }
+ /* free each rp_addr entry */
+ free_rp_entries(&rp);
+
+ return error;
+}
+
+static void
+unprefer_prefix(struct rr_prefix *rpp)
+{
+ struct rp_addr *rap;
+
+ for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
+ rap = rap->ra_entry.le_next) {
+ if (rap->ra_addr == NULL)
+ continue;
+ rap->ra_addr->ia6_lifetime.ia6t_preferred = time_second;
+ rap->ra_addr->ia6_lifetime.ia6t_pltime = 0;
+ }
+}
+
+int
+delete_each_prefix(struct socket *so, struct rr_prefix *rpp, u_char origin)
+{
+ struct in6_aliasreq ifra;
+ int error = 0;
+
+ if (rpp->rp_origin > origin)
+ return(EPERM);
+
+ while (rpp->rp_addrhead.lh_first != NULL) {
+ struct rp_addr *rap;
+ int s;
+
+ s = splnet();
+ rap = LIST_FIRST(&rpp->rp_addrhead);
+ if (rap == NULL)
+ break;
+ LIST_REMOVE(rap, ra_entry);
+ splx(s);
+ if (rap->ra_addr == NULL) {
+ free(rap, M_RR_ADDR);
+ continue;
+ }
+ rap->ra_addr->ia6_ifpr = NULL;
+
+ bzero(&ifra, sizeof(ifra));
+ strncpy(ifra.ifra_name, if_name(rpp->rp_ifp),
+ sizeof(ifra.ifra_name));
+ ifra.ifra_addr = rap->ra_addr->ia_addr;
+ ifra.ifra_dstaddr = rap->ra_addr->ia_dstaddr;
+ ifra.ifra_prefixmask = rap->ra_addr->ia_prefixmask;
+
+ error = in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra,
+ rpp->rp_ifp, curproc);
+ if (error != 0)
+ log(LOG_ERR, "in6_prefix.c: delete_each_prefix:"
+ "deletion of an addr %s/%d failed because"
+ "in6_control failed for error %d",
+ ip6_sprintf(&ifra.ifra_addr.sin6_addr),
+ rpp->rp_plen, error);
+
+ free(rap, M_RR_ADDR);
+ }
+ rp_remove(rpp);
+
+ return error;
+}
+
+static void
+delete_prefixes(struct socket *so, struct ifnet *ifp, u_char origin)
+{
+ struct ifprefix *ifpr, *nextifpr;
+
+ /* delete prefixes marked as tobe deleted */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) {
+ nextifpr = TAILQ_NEXT(ifpr, ifpr_list);
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr2rp(ifpr)->rp_statef_delmark)
+ (void)delete_each_prefix(so, ifpr2rp(ifpr), origin);
+ }
+}
+
+static int
+link_stray_ia6s(struct rr_prefix *rpp)
+{
+ struct ifaddr *ifa;
+
+ for (ifa = rpp->rp_ifp->if_addrlist.tqh_first; ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ struct rp_addr *rap;
+ struct rr_prefix *orpp;
+ int error = 0;
+
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (rpp->rp_plen > in6_matchlen(RP_IN6(rpp), IFA_IN6(ifa)))
+ continue;
+
+ orpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
+ if (orpp != NULL) {
+ if (!in6_are_prefix_equal(RP_IN6(orpp), RP_IN6(rpp),
+ rpp->rp_plen))
+ log(LOG_ERR, "in6_prefix.c: link_stray_ia6s:"
+ "addr %s/%d already linked to a prefix"
+ "and it matches also %s/%d",
+ ip6_sprintf(IFA_IN6(ifa)), orpp->rp_plen,
+ ip6_sprintf(RP_IN6(rpp)),
+ rpp->rp_plen);
+ continue;
+ }
+ if ((error = assigne_ra_entry(rpp,
+ (sizeof(rap->ra_ifid) << 3) -
+ rpp->rp_plen,
+ (struct in6_ifaddr *)ifa)) != 0)
+ return error;
+ }
+ return 0;
+}
+
+int
+in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data,
+ struct ifnet *ifp)
+{
+ struct rr_prefix *rpp, rp_tmp;
+ struct rp_addr *rap;
+ struct in6_prefixreq *ipr = (struct in6_prefixreq *)data;
+ struct in6_rrenumreq *irr = (struct in6_rrenumreq *)data;
+ struct ifaddr *ifa;
+ int error = 0;
+
+ /*
+ * Failsafe for errneous address config program.
+ * Let's hope rrenumd don't make a mistakes.
+ */
+ if (ipr->ipr_origin <= PR_ORIG_RA)
+ ipr->ipr_origin = PR_ORIG_STATIC;
+
+ switch (cmd) {
+ case SIOCSGIFPREFIX_IN6:
+ delmark_global_prefixes(ifp, irr);
+ /* FALL THROUGH */
+ case SIOCAIFPREFIX_IN6:
+ case SIOCCIFPREFIX_IN6:
+ /* check if preferred lifetime > valid lifetime */
+ if (irr->irr_pltime > irr->irr_vltime) {
+ log(LOG_NOTICE,
+ "in6_prefix_ioctl: preferred lifetime"
+ "(%ld) is greater than valid lifetime(%ld)",
+ (u_long)irr->irr_pltime, (u_long)irr->irr_vltime);
+ error = EINVAL;
+ break;
+ }
+ if (mark_matched_prefixes(cmd, ifp, irr)) {
+ if (irr->irr_u_uselen != 0)
+ if ((error = add_useprefixes(so, ifp, irr))
+ != 0)
+ goto failed;
+ if (cmd != SIOCAIFPREFIX_IN6)
+ delete_prefixes(so, ifp, irr->irr_origin);
+ } else
+ return (EADDRNOTAVAIL);
+ failed:
+ unmark_prefixes(ifp);
+ break;
+ case SIOCGIFPREFIX_IN6:
+ rpp = search_matched_prefix(ifp, ipr);
+ if (rpp == NULL || ifp != rpp->rp_ifp)
+ return (EADDRNOTAVAIL);
+
+ ipr->ipr_origin = rpp->rp_origin;
+ ipr->ipr_plen = rpp->rp_plen;
+ ipr->ipr_vltime = rpp->rp_vltime;
+ ipr->ipr_pltime = rpp->rp_pltime;
+ ipr->ipr_flags = rpp->rp_flags;
+ ipr->ipr_prefix = rpp->rp_prefix;
+
+ break;
+ case SIOCSIFPREFIX_IN6:
+ /* check if preferred lifetime > valid lifetime */
+ if (ipr->ipr_pltime > ipr->ipr_vltime) {
+ log(LOG_NOTICE,
+ "in6_prefix_ioctl: preferred lifetime"
+ "(%ld) is greater than valid lifetime(%ld)",
+ (u_long)ipr->ipr_pltime, (u_long)ipr->ipr_vltime);
+ error = EINVAL;
+ break;
+ }
+
+ /* init rp_tmp */
+ bzero((caddr_t)&rp_tmp, sizeof(rp_tmp));
+ rp_tmp.rp_ifp = ifp;
+ rp_tmp.rp_plen = ipr->ipr_plen;
+ rp_tmp.rp_prefix = ipr->ipr_prefix;
+ rp_tmp.rp_vltime = ipr->ipr_vltime;
+ rp_tmp.rp_pltime = ipr->ipr_pltime;
+ rp_tmp.rp_flags = ipr->ipr_flags;
+ rp_tmp.rp_origin = ipr->ipr_origin;
+
+ /* create rp_addr entries, usually at least for lladdr */
+ if ((error = link_stray_ia6s(&rp_tmp)) != 0) {
+ free_rp_entries(&rp_tmp);
+ break;
+ }
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
+ if (ifa != NULL) {
+ if ((error = create_ra_entry(&rap)) != 0) {
+ free_rp_entries(&rp_tmp);
+ break;
+ }
+ /* copy interface id part */
+ bit_copy((caddr_t)&rap->ra_ifid,
+ sizeof(rap->ra_ifid) << 3,
+ (caddr_t)IFA_IN6(ifa),
+ sizeof(*IFA_IN6(ifa)) << 3,
+ rp_tmp.rp_plen,
+ (sizeof(rap->ra_ifid) << 3) - rp_tmp.rp_plen);
+ /* insert into list */
+ LIST_INSERT_HEAD(&rp_tmp.rp_addrhead, rap, ra_entry);
+ }
+
+ error = add_each_prefix(so, &rp_tmp);
+
+ /* free each rp_addr entry */
+ free_rp_entries(&rp_tmp);
+
+ break;
+ case SIOCDIFPREFIX_IN6:
+ rpp = search_matched_prefix(ifp, ipr);
+ if (rpp == NULL || ifp != rpp->rp_ifp)
+ return (EADDRNOTAVAIL);
+
+ error = delete_each_prefix(so, rpp, ipr->ipr_origin);
+ break;
+ }
+ return error;
+}
+
+void
+in6_rr_timer(void *ignored_arg)
+{
+ int s;
+ struct rr_prefix *rpp;
+
+ timeout(in6_rr_timer, (caddr_t)0, ip6_rr_prune * hz);
+
+ s = splnet();
+ /* expire */
+ rpp = LIST_FIRST(&rr_prefix);
+ while (rpp) {
+ if (rpp->rp_expire && rpp->rp_expire < time_second) {
+ struct rr_prefix *next_rpp;
+ struct socket so;
+
+ /* XXX: init dummy so */
+ bzero(&so, sizeof(so));
+
+ next_rpp = LIST_NEXT(rpp, rp_entry);
+ delete_each_prefix(&so, rpp, PR_ORIG_KERNEL);
+ rpp = next_rpp;
+ continue;
+ }
+ if (rpp->rp_preferred && rpp->rp_preferred < time_second)
+ unprefer_prefix(rpp);
+ rpp = LIST_NEXT(rpp, rp_entry);
+ }
+ splx(s);
+}
OpenPOWER on IntegriCloud