From: Peter Avalos Date: Fri, 26 Dec 2008 03:37:08 +0000 (-0500) Subject: Add the management part of address selection policy described in RFC X-Git-Tag: v2.3.0~193^2~3 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/02809d295c49f5405783640030bc2fd7597a386e Add the management part of address selection policy described in RFC 3484. Obtained-from: KAME via FreeBSD Reviewed-by: sephe & hasso --- diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 4e249f9ea5..e227c06a21 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -394,6 +394,14 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, return (mrt6_ioctl(cmd, data)); } + switch(cmd) { + case SIOCAADDRCTL_POLICY: + case SIOCDADDRCTL_POLICY: + if (!privileged) + return (EPERM); + return (in6_src_ioctl(cmd, data)); + } + if (ifp == NULL) return (EOPNOTSUPP); diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index a741aee569..eef8da0d26 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -613,9 +613,11 @@ struct ip6_mtuinfo { #define IPV6CTL_AUTO_LINKLOCAL 35 /* automatic link-local addr assign */ #define IPV6CTL_RIP6STATS 36 /* raw_ip6 stats */ +#define IPV6CTL_ADDRCTLPOLICY 38 /* get/set address selection policy */ + /* New entries should be added here from current IPV6CTL_MAXID value. */ /* to define items, should talk with KAME guys first, for *BSD compatibility */ -#define IPV6CTL_MAXID 37 +#define IPV6CTL_MAXID 42 #endif /* !_XOPEN_SOURCE */ @@ -646,6 +648,7 @@ void in6_sin6_2_sin (struct sockaddr_in *sin, struct sockaddr_in6 *sin6); void in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6); void in6_sin6_2_sin_in_sock (struct sockaddr *nam); void in6_sin_2_v4mapsin6_in_sock (struct sockaddr **nam); +void addrsel_policy_init (void); #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) #define sin6tosa(sin6) ((struct sockaddr *)(sin6)) diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index a6e09048d3..eff45baee2 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -77,6 +77,8 @@ #include #include #include +#include +#include #include #include #include @@ -102,6 +104,17 @@ #include "use_loop.h" +#define ADDR_LABEL_NOTAPP (-1) +struct in6_addrpolicy defaultaddrpolicy; + +static void init_policy_queue(void); +static int add_addrsel_policyent(struct in6_addrpolicy *); +static int delete_addrsel_policyent(struct in6_addrpolicy *); +static int walk_addrsel_policy(int (*)(struct in6_addrpolicy *, void *), + void *); +static int dump_addrsel_policyent(struct in6_addrpolicy *, void *); + + /* * Return an IPv6 address, which is the most appropriate for a given * destination and user specified options. @@ -610,3 +623,169 @@ in6_clearscope(struct in6_addr *addr) if (IN6_IS_SCOPE_LINKLOCAL(addr)) addr->s6_addr16[1] = 0; } + +void +addrsel_policy_init(void) +{ + + init_policy_queue(); + + /* initialize the "last resort" policy */ + bzero(&defaultaddrpolicy, sizeof(defaultaddrpolicy)); + defaultaddrpolicy.label = ADDR_LABEL_NOTAPP; +} + +/* + * Subroutines to manage the address selection policy table via sysctl. + */ +struct walkarg { + struct sysctl_req *w_req; +}; + +static int in6_src_sysctl(SYSCTL_HANDLER_ARGS); +SYSCTL_DECL(_net_inet6_ip6); +SYSCTL_NODE(_net_inet6_ip6, IPV6CTL_ADDRCTLPOLICY, addrctlpolicy, + CTLFLAG_RD, in6_src_sysctl, ""); + +static int +in6_src_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct walkarg w; + + if (req->newptr) + return EPERM; + + bzero(&w, sizeof(w)); + w.w_req = req; + + return (walk_addrsel_policy(dump_addrsel_policyent, &w)); +} + +int +in6_src_ioctl(u_long cmd, caddr_t data) +{ + int i; + struct in6_addrpolicy ent0; + + if (cmd != SIOCAADDRCTL_POLICY && cmd != SIOCDADDRCTL_POLICY) + return (EOPNOTSUPP); /* check for safety */ + + ent0 = *(struct in6_addrpolicy *)data; + + if (ent0.label == ADDR_LABEL_NOTAPP) + return (EINVAL); + /* check if the prefix mask is consecutive. */ + if (in6_mask2len(&ent0.addrmask.sin6_addr, NULL) < 0) + return (EINVAL); + /* clear trailing garbages (if any) of the prefix address. */ + for (i = 0; i < 4; i++) { + ent0.addr.sin6_addr.s6_addr32[i] &= + ent0.addrmask.sin6_addr.s6_addr32[i]; + } + ent0.use = 0; + + switch (cmd) { + case SIOCAADDRCTL_POLICY: + return (add_addrsel_policyent(&ent0)); + case SIOCDADDRCTL_POLICY: + return (delete_addrsel_policyent(&ent0)); + } + + return (0); /* XXX: compromise compilers */ +} + +/* + * The followings are implementation of the policy table using a + * simple tail queue. + * XXX such details should be hidden. + * XXX implementation using binary tree should be more efficient. + */ +struct addrsel_policyent { + TAILQ_ENTRY(addrsel_policyent) ape_entry; + struct in6_addrpolicy ape_policy; +}; + +TAILQ_HEAD(addrsel_policyhead, addrsel_policyent); + +struct addrsel_policyhead addrsel_policytab; + +static void +init_policy_queue(void) +{ + TAILQ_INIT(&addrsel_policytab); +} + +static int +add_addrsel_policyent(struct in6_addrpolicy *newpolicy) +{ + struct addrsel_policyent *new, *pol; + + /* duplication check */ + for (pol = TAILQ_FIRST(&addrsel_policytab); pol; + pol = TAILQ_NEXT(pol, ape_entry)) { + if (SA6_ARE_ADDR_EQUAL(&newpolicy->addr, + &pol->ape_policy.addr) && + SA6_ARE_ADDR_EQUAL(&newpolicy->addrmask, + &pol->ape_policy.addrmask)) { + return (EEXIST); /* or override it? */ + } + } + + new = kmalloc(sizeof(*new), M_IFADDR, M_WAITOK | M_ZERO); + + /* XXX: should validate entry */ + new->ape_policy = *newpolicy; + + TAILQ_INSERT_TAIL(&addrsel_policytab, new, ape_entry); + + return (0); +} + +static int +delete_addrsel_policyent(struct in6_addrpolicy *key) +{ + struct addrsel_policyent *pol; + + /* search for the entry in the table */ + for (pol = TAILQ_FIRST(&addrsel_policytab); pol; + pol = TAILQ_NEXT(pol, ape_entry)) { + if (SA6_ARE_ADDR_EQUAL(&key->addr, &pol->ape_policy.addr) && + SA6_ARE_ADDR_EQUAL(&key->addrmask, + &pol->ape_policy.addrmask)) { + break; + } + } + if (pol == NULL) + return (ESRCH); + + TAILQ_REMOVE(&addrsel_policytab, pol, ape_entry); + kfree(pol, M_IFADDR); + + return (0); +} + +static int +walk_addrsel_policy(int(*callback)(struct in6_addrpolicy *, void *), void *w) +{ + struct addrsel_policyent *pol; + int error = 0; + + for (pol = TAILQ_FIRST(&addrsel_policytab); pol; + pol = TAILQ_NEXT(pol, ape_entry)) { + if ((error = (*callback)(&pol->ape_policy, w)) != 0) + return (error); + } + + return (error); +} + +static int +dump_addrsel_policyent(struct in6_addrpolicy *pol, void *arg) +{ + int error = 0; + struct walkarg *w = arg; + + error = SYSCTL_OUT(w->w_req, pol, sizeof(*pol)); + + return (error); +} diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index 68ca18d33c..91342b345b 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -134,6 +134,15 @@ struct in6_ifaddr { */ }; +/* control structure to manage address selection policy */ +struct in6_addrpolicy { + struct sockaddr_in6 addr; /* prefix address */ + struct sockaddr_in6 addrmask; /* prefix mask */ + int preced; /* precedence */ + int label; /* matching label */ + u_quad_t use; /* statistics */ +}; + /* * IPv6 interface statistics, as defined in RFC2465 Ipv6IfStatsEntry (p12). */ @@ -445,6 +454,9 @@ struct in6_rrenumreq { #define SIOCGETMIFCNT_IN6 _IOWR('u', 107, \ struct sioc_mif_req6) /* get pkt cnt per if */ +#define SIOCAADDRCTL_POLICY _IOW('u', 108, struct in6_addrpolicy) +#define SIOCDADDRCTL_POLICY _IOW('u', 109, struct in6_addrpolicy) + #define IN6_IFF_ANYCAST 0x01 /* anycast address */ #define IN6_IFF_TENTATIVE 0x02 /* tentative address */ #define IN6_IFF_DUPLICATED 0x04 /* DAD detected duplicate */ @@ -630,6 +642,7 @@ int in6_embedscope (struct in6_addr *, const struct sockaddr_in6 *, int in6_recoverscope (struct sockaddr_in6 *, const struct in6_addr *, struct ifnet *); void in6_clearscope (struct in6_addr *); +int in6_src_ioctl (u_long, caddr_t); #endif /* _KERNEL */ #endif /* _NETINET6_IN6_VAR_H_ */ diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index ab0b7fa221..c253b80257 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -200,6 +200,7 @@ ip6_init(void) netisr_register(NETISR_IPV6, cpu0_portfn, ip6_input, NETISR_FLAG_NOTMPSAFE); scope6_init(); + addrsel_policy_init(); nd6_init(); frag6_init(); /*