diff options
-rw-r--r-- | sys/netipsec/key.c | 60 | ||||
-rw-r--r-- | sys/netipsec/key.h | 4 | ||||
-rw-r--r-- | sys/netipsec/key_debug.c | 24 | ||||
-rw-r--r-- | sys/netipsec/xform_ipcomp.c | 163 |
4 files changed, 233 insertions, 18 deletions
diff --git a/sys/netipsec/key.c b/sys/netipsec/key.c index f13d42c..17980d6 100644 --- a/sys/netipsec/key.c +++ b/sys/netipsec/key.c @@ -1153,6 +1153,66 @@ done: return sav; } +struct secasvar * +key_allocsa_tunnel(union sockaddr_union *src, union sockaddr_union *dst, + u_int proto, const char* where, int tag) +{ + struct secashead *sah; + struct secasvar *sav; + u_int stateidx, arraysize, state; + const u_int *saorder_state_valid; + + IPSEC_ASSERT(src != NULL, ("null src address")); + IPSEC_ASSERT(dst != NULL, ("null dst address")); + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP %s from %s:%u\n", __func__, where, tag)); + + SAHTREE_LOCK(); + if (V_key_preferred_oldsa) { + saorder_state_valid = saorder_state_valid_prefer_old; + arraysize = _ARRAYLEN(saorder_state_valid_prefer_old); + } else { + saorder_state_valid = saorder_state_valid_prefer_new; + arraysize = _ARRAYLEN(saorder_state_valid_prefer_new); + } + LIST_FOREACH(sah, &V_sahtree, chain) { + /* search valid state */ + for (stateidx = 0; stateidx < arraysize; stateidx++) { + state = saorder_state_valid[stateidx]; + LIST_FOREACH(sav, &sah->savtree[state], chain) { + /* sanity check */ + KEY_CHKSASTATE(sav->state, state, __func__); + /* do not return entries w/ unusable state */ + if (sav->state != SADB_SASTATE_MATURE && + sav->state != SADB_SASTATE_DYING) + continue; + if (IPSEC_MODE_TUNNEL != sav->sah->saidx.mode) + continue; + if (proto != sav->sah->saidx.proto) + continue; + /* check src address */ + if (key_sockaddrcmp(&src->sa, + &sav->sah->saidx.src.sa, 0) != 0) + continue; + /* check dst address */ + if (key_sockaddrcmp(&dst->sa, + &sav->sah->saidx.dst.sa, 0) != 0) + continue; + sa_addref(sav); + goto done; + } + } + } + sav = NULL; +done: + SAHTREE_UNLOCK(); + + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP %s return SA:%p; refcnt %u\n", __func__, + sav, sav ? sav->refcnt : 0)); + return (sav); +} + /* * Must be called after calling key_allocsp(). * For both the packet without socket and key_freeso(). diff --git a/sys/netipsec/key.h b/sys/netipsec/key.h index 39fd1b2..7564de5 100644 --- a/sys/netipsec/key.h +++ b/sys/netipsec/key.h @@ -76,11 +76,15 @@ extern void _key_freesp(struct secpolicy **, const char*, int); extern struct secasvar *key_allocsa(union sockaddr_union *, u_int, u_int32_t, const char*, int); +extern struct secasvar *key_allocsa_tunnel(union sockaddr_union *, + union sockaddr_union *, u_int, const char*, int); extern void key_addrefsa(struct secasvar *, const char*, int); extern void key_freesav(struct secasvar **, const char*, int); #define KEY_ALLOCSA(dst, proto, spi) \ key_allocsa(dst, proto, spi, __FILE__, __LINE__) +#define KEY_ALLOCSA_TUNNEL(src, dst, proto) \ + key_allocsa_tunnel(src, dst, proto, __FILE__, __LINE__) #define KEY_ADDREFSA(sav) \ key_addrefsa(sav, __FILE__, __LINE__) #define KEY_FREESAV(psav) \ diff --git a/sys/netipsec/key_debug.c b/sys/netipsec/key_debug.c index ceb9453..03eeeb0 100644 --- a/sys/netipsec/key_debug.c +++ b/sys/netipsec/key_debug.c @@ -506,6 +506,8 @@ kdebug_secpolicy(struct secpolicy *sp) void kdebug_secpolicyindex(struct secpolicyindex *spidx) { + char buf[INET6_ADDRSTRLEN]; + /* sanity check */ if (spidx == NULL) panic("%s: NULL pointer was passed.\n", __func__); @@ -513,19 +515,15 @@ kdebug_secpolicyindex(struct secpolicyindex *spidx) printf("secpolicyindex{ dir=%u prefs=%u prefd=%u ul_proto=%u\n", spidx->dir, spidx->prefs, spidx->prefd, spidx->ul_proto); - ipsec_hexdump((caddr_t)&spidx->src, - ((struct sockaddr *)&spidx->src)->sa_len); - printf("\n"); - ipsec_hexdump((caddr_t)&spidx->dst, - ((struct sockaddr *)&spidx->dst)->sa_len); - printf("}\n"); - - return; + printf("%s -> ", ipsec_address(&spidx->src, buf, sizeof(buf))); + printf("%s }\n", ipsec_address(&spidx->dst, buf, sizeof(buf))); } void kdebug_secasindex(struct secasindex *saidx) { + char buf[INET6_ADDRSTRLEN]; + /* sanity check */ if (saidx == NULL) panic("%s: NULL pointer was passed.\n", __func__); @@ -533,14 +531,8 @@ kdebug_secasindex(struct secasindex *saidx) printf("secasindex{ mode=%u proto=%u\n", saidx->mode, saidx->proto); - ipsec_hexdump((caddr_t)&saidx->src, - ((struct sockaddr *)&saidx->src)->sa_len); - printf("\n"); - ipsec_hexdump((caddr_t)&saidx->dst, - ((struct sockaddr *)&saidx->dst)->sa_len); - printf("\n"); - - return; + printf("%s -> ", ipsec_address(&saidx->src, buf, sizeof(buf))); + printf("%s }\n", ipsec_address(&saidx->dst, buf, sizeof(buf))); } static void diff --git a/sys/netipsec/xform_ipcomp.c b/sys/netipsec/xform_ipcomp.c index 605fff9..5c7b569 100644 --- a/sys/netipsec/xform_ipcomp.c +++ b/sys/netipsec/xform_ipcomp.c @@ -47,7 +47,9 @@ #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_var.h> +#include <netinet/ip_encap.h> +#include <net/netisr.h> #include <net/vnet.h> #include <netipsec/ipsec.h> @@ -55,6 +57,8 @@ #ifdef INET6 #include <netinet/ip6.h> +#include <netinet6/ip6_var.h> +#include <netinet6/ip6protosw.h> #include <netipsec/ipsec6.h> #endif @@ -62,7 +66,6 @@ #include <netipsec/ipcomp_var.h> #include <netipsec/key.h> -#include <netipsec/key_debug.h> #include <opencrypto/cryptodev.h> #include <opencrypto/deflate.h> @@ -99,6 +102,70 @@ ipcomp_algorithm_lookup(int alg) } /* + * RFC 3173 p 2.2. Non-Expansion Policy: + * If the total size of a compressed payload and the IPComp header, as + * defined in section 3, is not smaller than the size of the original + * payload, the IP datagram MUST be sent in the original non-compressed + * form. + * + * When we use IPComp in tunnel mode, for small packets we will receive + * encapsulated IP-IP datagrams without any compression and without IPComp + * header. + */ +static int +ipcomp_encapcheck(union sockaddr_union *src, union sockaddr_union *dst) +{ + struct secasvar *sav; + + sav = KEY_ALLOCSA_TUNNEL(src, dst, IPPROTO_IPCOMP); + if (sav == NULL) + return (0); + KEY_FREESAV(&sav); + + if (src->sa.sa_family == AF_INET) + return (sizeof(struct in_addr) << 4); + else + return (sizeof(struct in6_addr) << 4); +} + +static int +ipcomp6_nonexp_input(struct mbuf **mp, int *offp, int proto) +{ + int isr; + + switch (proto) { +#ifdef INET + case IPPROTO_IPV4: + isr = NETISR_IP; + break; +#endif +#ifdef INET6 + case IPPROTO_IPV6: + isr = NETISR_IPV6; + break; +#endif + default: + IPCOMPSTAT_INC(ipcomps_nopf); + m_freem(*mp); + return (IPPROTO_DONE); + } + m_adj(*mp, *offp); + IPCOMPSTAT_ADD(ipcomps_ibytes, (*mp)->m_pkthdr.len); + IPCOMPSTAT_INC(ipcomps_input); + netisr_dispatch(isr, *mp); + return (IPPROTO_DONE); +} + +static void +ipcomp_nonexp_input(struct mbuf *m, int hlen) +{ + int off; + + off = hlen; + ipcomp6_nonexp_input(&m, &off, AF_INET); +} + +/* * ipcomp_init() is called when an CPI is being set up. */ static int @@ -628,11 +695,103 @@ static struct xformsw ipcomp_xformsw = { ipcomp_output }; +#ifdef INET +static const struct encaptab *ipe4_cookie = NULL; +extern struct domain inetdomain; +static struct protosw ipcomp4_protosw = { + .pr_type = SOCK_RAW, + .pr_domain = &inetdomain, + .pr_protocol = 0 /* IPPROTO_IPV[46] */, + .pr_flags = PR_ATOMIC | PR_ADDR | PR_LASTHDR, + .pr_input = ipcomp_nonexp_input, + .pr_output = (pr_output_t *)rip_output, + .pr_ctloutput = rip_ctloutput, + .pr_usrreqs = &rip_usrreqs +}; + +static int +ipcomp4_nonexp_encapcheck(const struct mbuf *m, int off, int proto, + void *arg __unused) +{ + union sockaddr_union src, dst; + const struct ip *ip; + + if (V_ipcomp_enable == 0) + return (0); + if (proto != IPPROTO_IPV4 && proto != IPPROTO_IPV6) + return (0); + bzero(&src, sizeof(src)); + bzero(&dst, sizeof(dst)); + src.sa.sa_family = dst.sa.sa_family = AF_INET; + src.sin.sin_len = dst.sin.sin_len = sizeof(struct sockaddr_in); + ip = mtod(m, const struct ip *); + src.sin.sin_addr = ip->ip_src; + dst.sin.sin_addr = ip->ip_dst; + return (ipcomp_encapcheck(&src, &dst)); +} +#endif +#ifdef INET6 +static const struct encaptab *ipe6_cookie = NULL; +extern struct domain inet6domain; +static struct ip6protosw ipcomp6_protosw = { + .pr_type = SOCK_RAW, + .pr_domain = &inet6domain, + .pr_protocol = 0 /* IPPROTO_IPV[46] */, + .pr_flags = PR_ATOMIC | PR_ADDR | PR_LASTHDR, + .pr_input = ipcomp6_nonexp_input, + .pr_output = rip6_output, + .pr_ctloutput = rip6_ctloutput, + .pr_usrreqs = &rip6_usrreqs +}; + +static int +ipcomp6_nonexp_encapcheck(const struct mbuf *m, int off, int proto, + void *arg __unused) +{ + union sockaddr_union src, dst; + const struct ip6_hdr *ip6; + + if (V_ipcomp_enable == 0) + return (0); + if (proto != IPPROTO_IPV4 && proto != IPPROTO_IPV6) + return (0); + bzero(&src, sizeof(src)); + bzero(&dst, sizeof(dst)); + src.sa.sa_family = dst.sa.sa_family = AF_INET; + src.sin6.sin6_len = dst.sin6.sin6_len = sizeof(struct sockaddr_in6); + ip6 = mtod(m, const struct ip6_hdr *); + src.sin6.sin6_addr = ip6->ip6_src; + dst.sin6.sin6_addr = ip6->ip6_dst; + if (IN6_IS_SCOPE_LINKLOCAL(&src.sin6.sin6_addr)) { + /* XXX: sa6_recoverscope() */ + src.sin6.sin6_scope_id = + ntohs(src.sin6.sin6_addr.s6_addr16[1]); + src.sin6.sin6_addr.s6_addr16[1] = 0; + } + if (IN6_IS_SCOPE_LINKLOCAL(&dst.sin6.sin6_addr)) { + /* XXX: sa6_recoverscope() */ + dst.sin6.sin6_scope_id = + ntohs(dst.sin6.sin6_addr.s6_addr16[1]); + dst.sin6.sin6_addr.s6_addr16[1] = 0; + } + return (ipcomp_encapcheck(&src, &dst)); +} +#endif + static void ipcomp_attach(void) { +#ifdef INET + ipe4_cookie = encap_attach_func(AF_INET, -1, + ipcomp4_nonexp_encapcheck, &ipcomp4_protosw, NULL); +#endif +#ifdef INET6 + ipe6_cookie = encap_attach_func(AF_INET6, -1, + ipcomp6_nonexp_encapcheck, (void *)&ipcomp6_protosw, NULL); +#endif xform_register(&ipcomp_xformsw); } -SYSINIT(ipcomp_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ipcomp_attach, NULL); +SYSINIT(ipcomp_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, + ipcomp_attach, NULL); |