summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuiz Otavio O Souza <luiz@netgate.com>2016-05-11 16:54:15 -0500
committerLuiz Otavio O Souza <luiz@netgate.com>2016-05-12 11:07:52 -0500
commit384ae63efc9d676414c45591c9b6095197919513 (patch)
treee08803f9dcc0c02eac29021c91980c5546985777
parent37b749f4ba04bd2cfbe3472b597c0caab8e862fa (diff)
downloadFreeBSD-src-384ae63efc9d676414c45591c9b6095197919513.zip
FreeBSD-src-384ae63efc9d676414c45591c9b6095197919513.tar.gz
MFC r298535, r298536 and r298549:
Handle non-compressed packets for IPComp in tunnel mode. RFC3173 says that the IP datagram MUST be sent in the original non-compressed form, when the total size of a compressed payload and the IPComp header is not smaller than the size of the original payload. In tunnel mode for small packets IPComp will send encapsulated IP datagrams without IPComp header. Add ip_encap handler for IPPROTO_IPV4 and IPPROTO_IPV6 to handle these datagrams. The handler does lookup for SA related to IPComp protocol and given from mbuf source and destination addresses as tunnel endpoints. It decapsulates packets only when corresponding SA is found. Reported by: gnn Reviewed by: gnn Differential Revision: https://reviews.freebsd.org/D6062 r298536: Use ipsec_address() function to print IP addresses. r298549: Fix build for NOINET and NOINET6 kernels. Use own protosw structures for both address families. Check proto in encapcheck function and use -1 as proto argument in encap_attach_func(), both address families can have IPPROTO_IPV4 and IPPROTO_IPV6 protocols. Reported by: bz TAG: IPSEC-HEAD (cherry picked from commit a1d2523e7f503ed719420848cc61de12bdf8ab4f)
-rw-r--r--sys/netipsec/key.c60
-rw-r--r--sys/netipsec/key.h4
-rw-r--r--sys/netipsec/key_debug.c24
-rw-r--r--sys/netipsec/xform_ipcomp.c163
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);
OpenPOWER on IntegriCloud