From 9dab5c9bc05a959d61465604ac2e19a3e2cb9a69 Mon Sep 17 00:00:00 2001 From: ume Date: Thu, 30 Oct 2003 15:29:17 +0000 Subject: [PATCH] add management part of address selection policy described in RFC3484. Obtained from: KAME --- sys/netinet6/in6.c | 8 ++ sys/netinet6/in6.h | 3 + sys/netinet6/in6_src.c | 202 +++++++++++++++++++++++++++++++++++++++ sys/netinet6/in6_var.h | 13 +++ sys/netinet6/ip6_input.c | 1 + 5 files changed, 227 insertions(+) diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index ea01b1f3761a..46ec23aa3273 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -346,6 +346,14 @@ in6_control(so, cmd, data, ifp, td) 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 af3711dcfb7a..5f3da0ed259b 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -593,6 +593,8 @@ 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 */ + #define IPV6CTL_MAXFRAGS 41 /* max fragments */ /* New entries should be added here from current IPV6CTL_MAXID value. */ @@ -626,6 +628,7 @@ void in6_sin_2_v4mapsin6 __P((struct sockaddr_in *sin, struct sockaddr_in6 *sin6)); void in6_sin6_2_sin_in_sock __P((struct sockaddr *nam)); void in6_sin_2_v4mapsin6_in_sock __P((struct sockaddr **nam)); +extern void addrsel_policy_init __P((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 4780f0f79a74..cf10c936e2c4 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -75,6 +75,8 @@ #include #include #include +#include +#include #include #include @@ -97,6 +99,22 @@ #include +static struct mtx addrsel_lock; +#define ADDRSEL_LOCK_INIT() mtx_init(&addrsel_lock, "addrsel_lock", NULL, MTX_DEF) +#define ADDRSEL_LOCK() mtx_lock(&addrsel_lock) +#define ADDRSEL_UNLOCK() mtx_unlock(&addrsel_lock) +#define ADDRSEL_LOCK_ASSERT() mtx_assert(&addrsel_lock, MA_OWNED) + +#define ADDR_LABEL_NOTAPP (-1) +struct in6_addrpolicy defaultaddrpolicy; + +static void init_policy_queue __P((void)); +static int add_addrsel_policyent __P((struct in6_addrpolicy *)); +static int delete_addrsel_policyent __P((struct in6_addrpolicy *)); +static int walk_addrsel_policy __P((int (*)(struct in6_addrpolicy *, void *), + void *)); +static int dump_addrsel_policyent __P((struct in6_addrpolicy *, void *)); + /* * Return an IPv6 address, which is the most appropriate for a given * destination and user specified options. @@ -528,3 +546,187 @@ in6_clearscope(addr) if (IN6_IS_SCOPE_LINKLOCAL(addr) || IN6_IS_ADDR_MC_INTFACELOCAL(addr)) addr->s6_addr16[1] = 0; } + +void +addrsel_policy_init() +{ + ADDRSEL_LOCK_INIT(); + + 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(cmd, data) + 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() +{ + TAILQ_INIT(&addrsel_policytab); +} + +static int +add_addrsel_policyent(newpolicy) + struct in6_addrpolicy *newpolicy; +{ + struct addrsel_policyent *new, *pol; + + ADDRSEL_LOCK(); + + /* 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? */ + } + } + + MALLOC(new, struct addrsel_policyent *, sizeof(*new), M_IFADDR, + M_WAITOK); + bzero(new, sizeof(*new)); + + /* XXX: should validate entry */ + new->ape_policy = *newpolicy; + + TAILQ_INSERT_TAIL(&addrsel_policytab, new, ape_entry); + ADDRSEL_UNLOCK(); + + return (0); +} + +static int +delete_addrsel_policyent(key) + struct in6_addrpolicy *key; +{ + struct addrsel_policyent *pol; + + ADDRSEL_LOCK(); + + /* 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); + ADDRSEL_UNLOCK(); + + return (0); +} + +static int +walk_addrsel_policy(callback, w) + int (*callback) __P((struct in6_addrpolicy *, void *)); + void *w; +{ + struct addrsel_policyent *pol; + int error = 0; + + ADDRSEL_LOCK(); + for (pol = TAILQ_FIRST(&addrsel_policytab); pol; + pol = TAILQ_NEXT(pol, ape_entry)) { + if ((error = (*callback)(&pol->ape_policy, w)) != 0) + return (error); + } + ADDRSEL_UNLOCK(); + + return (error); +} + +static int +dump_addrsel_policyent(pol, arg) + 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 b481b04d485f..bc06d0f0b331 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -118,6 +118,15 @@ struct in6_ifaddr { struct nd_prefix *ia6_ndpr; }; +/* 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). */ @@ -432,6 +441,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 */ @@ -604,6 +616,7 @@ int in6_embedscope __P((struct in6_addr *, const struct sockaddr_in6 *, int in6_recoverscope __P((struct sockaddr_in6 *, const struct in6_addr *, struct ifnet *)); void in6_clearscope __P((struct in6_addr *)); +int in6_src_ioctl __P((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 adc9e871b488..c02d76e8d5e0 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -198,6 +198,7 @@ ip6_init() mtx_init(&ip6intrq.ifq_mtx, "ip6_inq", NULL, MTX_DEF); netisr_register(NETISR_IPV6, ip6_input, &ip6intrq); scope6_init(); + addrsel_policy_init(); nd6_init(); frag6_init(); #ifndef RANDOM_IP_ID