diff options
author | vanhu <vanhu@FreeBSD.org> | 2009-06-12 15:44:35 +0000 |
---|---|---|
committer | vanhu <vanhu@FreeBSD.org> | 2009-06-12 15:44:35 +0000 |
commit | 16c1346b9a6c737fd054d4d0644bf5104fcb32aa (patch) | |
tree | e75e977677e2ddd8c5e3a47752c3693ea92b15e3 | |
parent | 76ddf574294a7a39ca62f55ea127233303bcf29e (diff) | |
download | FreeBSD-src-16c1346b9a6c737fd054d4d0644bf5104fcb32aa.zip FreeBSD-src-16c1346b9a6c737fd054d4d0644bf5104fcb32aa.tar.gz |
Added support for NAT-Traversal (RFC 3948) in IPsec stack.
Thanks to (no special order) Emmanuel Dreyfus (manu@netbsd.org), Larry
Baird (lab@gta.com), gnn, bz, and other FreeBSD devs, Julien Vanherzeele
(julien.vanherzeele@netasq.com, for years of bug reporting), the PFSense
team, and all people who used / tried the NAT-T patch for years and
reported bugs, patches, etc...
X-MFC: never
Reviewed by: bz
Approved by: gnn(mentor)
Obtained from: NETASQ
-rw-r--r-- | sys/conf/NOTES | 5 | ||||
-rw-r--r-- | sys/conf/options | 1 | ||||
-rw-r--r-- | sys/net/pfkeyv2.h | 37 | ||||
-rw-r--r-- | sys/netinet/in_proto.c | 2 | ||||
-rw-r--r-- | sys/netinet/udp.h | 19 | ||||
-rw-r--r-- | sys/netinet/udp_usrreq.c | 258 | ||||
-rw-r--r-- | sys/netinet/udp_var.h | 7 | ||||
-rw-r--r-- | sys/netipsec/ipsec_input.c | 9 | ||||
-rw-r--r-- | sys/netipsec/ipsec_output.c | 55 | ||||
-rw-r--r-- | sys/netipsec/key.c | 621 | ||||
-rw-r--r-- | sys/netipsec/key.h | 5 | ||||
-rw-r--r-- | sys/netipsec/keydb.h | 6 | ||||
-rw-r--r-- | sys/sys/mbuf.h | 1 |
13 files changed, 1019 insertions, 7 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 7de53a2..0abfc54 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -534,6 +534,11 @@ options IPSEC #IP security (requires device crypto) # using ipfw(8)'s 'ipsec' keyword, when this option is enabled. # #options IPSEC_FILTERTUNNEL #filter ipsec packets from a tunnel +# +# Set IPSEC_NAT_T to enable NAT-Traversal support. This enables +# optional UDP encapsulation of ESP packets. +# +options IPSEC_NAT_T #NAT-T support, UDP encap of ESP options IPX #IPX/SPX communications protocols diff --git a/sys/conf/options b/sys/conf/options index 883f7ae..3c56a81 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -401,6 +401,7 @@ IPFIREWALL_VERBOSE_LIMIT opt_ipfw.h IPSEC opt_ipsec.h IPSEC_DEBUG opt_ipsec.h IPSEC_FILTERTUNNEL opt_ipsec.h +IPSEC_NAT_T opt_ipsec.h IPSTEALTH IPX KRPC diff --git a/sys/net/pfkeyv2.h b/sys/net/pfkeyv2.h index 6158d88..c45f8b0 100644 --- a/sys/net/pfkeyv2.h +++ b/sys/net/pfkeyv2.h @@ -255,6 +255,34 @@ struct sadb_x_ipsecrequest { */ }; +/* NAT-Traversal type, see RFC 3948 (and drafts). */ +/* sizeof(struct sadb_x_nat_t_type) == 8 */ +struct sadb_x_nat_t_type { + u_int16_t sadb_x_nat_t_type_len; + u_int16_t sadb_x_nat_t_type_exttype; + u_int8_t sadb_x_nat_t_type_type; + u_int8_t sadb_x_nat_t_type_reserved[3]; +}; + +/* NAT-Traversal source or destination port. */ +/* sizeof(struct sadb_x_nat_t_port) == 8 */ +struct sadb_x_nat_t_port { + u_int16_t sadb_x_nat_t_port_len; + u_int16_t sadb_x_nat_t_port_exttype; + u_int16_t sadb_x_nat_t_port_port; + u_int16_t sadb_x_nat_t_port_reserved; +}; + +/* ESP fragmentation size. */ +/* sizeof(struct sadb_x_nat_t_frag) == 8 */ +struct sadb_x_nat_t_frag { + u_int16_t sadb_x_nat_t_frag_len; + u_int16_t sadb_x_nat_t_frag_exttype; + u_int16_t sadb_x_nat_t_frag_fraglen; + u_int16_t sadb_x_nat_t_frag_reserved; +}; + + #define SADB_EXT_RESERVED 0 #define SADB_EXT_SA 1 #define SADB_EXT_LIFETIME_CURRENT 2 @@ -275,7 +303,14 @@ struct sadb_x_ipsecrequest { #define SADB_X_EXT_KMPRIVATE 17 #define SADB_X_EXT_POLICY 18 #define SADB_X_EXT_SA2 19 -#define SADB_EXT_MAX 19 +#define SADB_X_EXT_NAT_T_TYPE 20 +#define SADB_X_EXT_NAT_T_SPORT 21 +#define SADB_X_EXT_NAT_T_DPORT 22 +#define SADB_X_EXT_NAT_T_OA 23 /* Deprecated. */ +#define SADB_X_EXT_NAT_T_OAI 23 /* Peer's NAT_OA for src of SA. */ +#define SADB_X_EXT_NAT_T_OAR 24 /* Peer's NAT_OA for dst of SA. */ +#define SADB_X_EXT_NAT_T_FRAG 25 /* Manual MTU override. */ +#define SADB_EXT_MAX 25 #define SADB_SATYPE_UNSPEC 0 #define SADB_SATYPE_AH 2 diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c index d86cc19..c677c5d 100644 --- a/sys/netinet/in_proto.c +++ b/sys/netinet/in_proto.c @@ -124,7 +124,7 @@ struct protosw inetsw[] = { .pr_flags = PR_ATOMIC|PR_ADDR, .pr_input = udp_input, .pr_ctlinput = udp_ctlinput, - .pr_ctloutput = ip_ctloutput, + .pr_ctloutput = udp_ctloutput, .pr_init = udp_init, #ifdef VIMAGE .pr_destroy = udp_destroy, diff --git a/sys/netinet/udp.h b/sys/netinet/udp.h index 2e8fd8d..6841683 100644 --- a/sys/netinet/udp.h +++ b/sys/netinet/udp.h @@ -45,4 +45,23 @@ struct udphdr { u_short uh_sum; /* udp checksum */ }; +/* + * User-settable options (used with setsockopt). + */ +#define UDP_ENCAP 0x01 + + +/* + * UDP Encapsulation of IPsec Packets options. + */ +/* Encapsulation types. */ +#define UDP_ENCAP_ESPINUDP_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */ +#define UDP_ENCAP_ESPINUDP 2 /* draft-ietf-ipsec-udp-encaps-02+ */ + +/* Default ESP in UDP encapsulation port. */ +#define UDP_ENCAP_ESPINUDP_PORT 500 + +/* Maximum UDP fragment size for ESP over UDP. */ +#define UDP_ENCAP_ESPINUDP_MAXFRAGLEN 552 + #endif diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index fdc96f4..51dda23 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -84,6 +84,7 @@ __FBSDID("$FreeBSD$"); #ifdef IPSEC #include <netipsec/ipsec.h> +#include <netipsec/esp.h> #endif #include <machine/in_cksum.h> @@ -151,6 +152,14 @@ SYSCTL_V_STRUCT(V_NET, vnet_inet, _net_inet_udp, UDPCTL_STATS, stats, static void udp_detach(struct socket *so); static int udp_output(struct inpcb *, struct mbuf *, struct sockaddr *, struct mbuf *, struct thread *); +#ifdef IPSEC +#ifdef IPSEC_NAT_T +#define UF_ESPINUDP_ALL (UF_ESPINUDP_NON_IKE|UF_ESPINUDP) +#ifdef INET +static struct mbuf *udp4_espdecap(struct inpcb *, struct mbuf *, int); +#endif +#endif /* IPSEC_NAT_T */ +#endif /* IPSEC */ static void udp_zone_change(void *tag) @@ -252,6 +261,13 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off, #ifdef INET6 struct sockaddr_in6 udp_in6; #endif +#ifdef IPSEC +#ifdef IPSEC_NAT_T +#ifdef INET + struct udpcb *up; +#endif +#endif +#endif INP_RLOCK_ASSERT(inp); @@ -263,6 +279,17 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off, V_ipsec4stat.in_polvio++; return; } +#ifdef IPSEC_NAT_T +#ifdef INET + up = intoudpcb(inp); + KASSERT(up != NULL, ("%s: udpcb NULL", __func__)); + if (up->u_flags & UF_ESPINUDP_ALL) { /* IPSec UDP encaps. */ + n = udp4_espdecap(inp, n, off); + if (n == NULL) /* Consumed. */ + return; + } +#endif /* INET */ +#endif /* IPSEC_NAT_T */ #endif /* IPSEC */ #ifdef MAC if (mac_inpcb_check_deliver(inp, n) != 0) { @@ -825,6 +852,99 @@ SYSCTL_PROC(_net_inet_udp, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_PRISON, 0, 0, udp_getcred, "S,xucred", "Get the xucred of a UDP connection"); +int +udp_ctloutput(struct socket *so, struct sockopt *sopt) +{ + int error = 0, optval; + struct inpcb *inp; +#ifdef IPSEC_NAT_T + struct udpcb *up; +#endif + + inp = sotoinpcb(so); + KASSERT(inp != NULL, ("%s: inp == NULL", __func__)); + INP_WLOCK(inp); + if (sopt->sopt_level != IPPROTO_UDP) { +#ifdef INET6 + if (INP_CHECK_SOCKAF(so, AF_INET6)) { + INP_WUNLOCK(inp); + error = ip6_ctloutput(so, sopt); + } else { +#endif + INP_WUNLOCK(inp); + error = ip_ctloutput(so, sopt); +#ifdef INET6 + } +#endif + return (error); + } + + switch (sopt->sopt_dir) { + case SOPT_SET: + switch (sopt->sopt_name) { + case UDP_ENCAP: + INP_WUNLOCK(inp); + error = sooptcopyin(sopt, &optval, sizeof optval, + sizeof optval); + if (error) + break; + inp = sotoinpcb(so); + KASSERT(inp != NULL, ("%s: inp == NULL", __func__)); + INP_WLOCK(inp); +#ifdef IPSEC_NAT_T + up = intoudpcb(inp); + KASSERT(up != NULL, ("%s: up == NULL", __func__)); +#endif + switch (optval) { + case 0: + /* Clear all UDP encap. */ +#ifdef IPSEC_NAT_T + up->u_flags &= ~UF_ESPINUDP_ALL; +#endif + break; +#ifdef IPSEC_NAT_T + case UDP_ENCAP_ESPINUDP: + case UDP_ENCAP_ESPINUDP_NON_IKE: + up->u_flags &= ~UF_ESPINUDP_ALL; + if (optval == UDP_ENCAP_ESPINUDP) + up->u_flags |= UF_ESPINUDP; + else if (optval == UDP_ENCAP_ESPINUDP_NON_IKE) + up->u_flags |= UF_ESPINUDP_NON_IKE; + break; +#endif + default: + error = EINVAL; + break; + } + INP_WUNLOCK(inp); + break; + default: + INP_WUNLOCK(inp); + error = ENOPROTOOPT; + break; + } + break; + case SOPT_GET: + switch (sopt->sopt_name) { +#ifdef IPSEC_NAT_T + case UDP_ENCAP: + up = intoudpcb(inp); + KASSERT(up != NULL, ("%s: up == NULL", __func__)); + optval = up->u_flags & UF_ESPINUDP_ALL; + INP_WUNLOCK(inp); + error = sooptcopyout(sopt, &optval, sizeof optval); + break; +#endif + default: + INP_WUNLOCK(inp); + error = ENOPROTOOPT; + break; + } + break; + } + return (error); +} + static int udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct thread *td) @@ -1136,6 +1256,144 @@ release: return (error); } + +#if defined(IPSEC) && defined(IPSEC_NAT_T) +#ifdef INET +/* + * Potentially decap ESP in UDP frame. Check for an ESP header + * and optional marker; if present, strip the UDP header and + * push the result through IPSec. + * + * Returns mbuf to be processed (potentially re-allocated) or + * NULL if consumed and/or processed. + */ +static struct mbuf * +udp4_espdecap(struct inpcb *inp, struct mbuf *m, int off) +{ + INIT_VNET_IPSEC(curvnet); + size_t minlen, payload, skip, iphlen; + caddr_t data; + struct udpcb *up; + struct m_tag *tag; + struct udphdr *udphdr; + struct ip *ip; + + INP_RLOCK_ASSERT(inp); + + /* + * Pull up data so the longest case is contiguous: + * IP/UDP hdr + non ESP marker + ESP hdr. + */ + minlen = off + sizeof(uint64_t) + sizeof(struct esp); + if (minlen > m->m_pkthdr.len) + minlen = m->m_pkthdr.len; + if ((m = m_pullup(m, minlen)) == NULL) { + V_ipsec4stat.in_inval++; + return (NULL); /* Bypass caller processing. */ + } + data = mtod(m, caddr_t); /* Points to ip header. */ + payload = m->m_len - off; /* Size of payload. */ + + if (payload == 1 && data[off] == '\xff') + return (m); /* NB: keepalive packet, no decap. */ + + up = intoudpcb(inp); + KASSERT(up != NULL, ("%s: udpcb NULL", __func__)); + KASSERT((up->u_flags & UF_ESPINUDP_ALL) != 0, + ("u_flags 0x%x", up->u_flags)); + + /* + * Check that the payload is large enough to hold an + * ESP header and compute the amount of data to remove. + * + * NB: the caller has already done a pullup for us. + * XXX can we assume alignment and eliminate bcopys? + */ + if (up->u_flags & UF_ESPINUDP_NON_IKE) { + /* + * draft-ietf-ipsec-nat-t-ike-0[01].txt and + * draft-ietf-ipsec-udp-encaps-(00/)01.txt, ignoring + * possible AH mode non-IKE marker+non-ESP marker + * from draft-ietf-ipsec-udp-encaps-00.txt. + */ + uint64_t marker; + + if (payload <= sizeof(uint64_t) + sizeof(struct esp)) + return (m); /* NB: no decap. */ + bcopy(data + off, &marker, sizeof(uint64_t)); + if (marker != 0) /* Non-IKE marker. */ + return (m); /* NB: no decap. */ + skip = sizeof(uint64_t) + sizeof(struct udphdr); + } else { + uint32_t spi; + + if (payload <= sizeof(struct esp)) { + V_ipsec4stat.in_inval++; + m_freem(m); + return (NULL); /* Discard. */ + } + bcopy(data + off, &spi, sizeof(uint32_t)); + if (spi == 0) /* Non-ESP marker. */ + return (m); /* NB: no decap. */ + skip = sizeof(struct udphdr); + } + + /* + * Setup a PACKET_TAG_IPSEC_NAT_T_PORT tag to remember + * the UDP ports. This is required if we want to select + * the right SPD for multiple hosts behind same NAT. + * + * NB: ports are maintained in network byte order everywhere + * in the NAT-T code. + */ + tag = m_tag_get(PACKET_TAG_IPSEC_NAT_T_PORTS, + 2 * sizeof(uint16_t), M_NOWAIT); + if (tag == NULL) { + V_ipsec4stat.in_nomem++; + m_freem(m); + return (NULL); /* Discard. */ + } + iphlen = off - sizeof(struct udphdr); + udphdr = (struct udphdr *)(data + iphlen); + ((uint16_t *)(tag + 1))[0] = udphdr->uh_sport; + ((uint16_t *)(tag + 1))[1] = udphdr->uh_dport; + m_tag_prepend(m, tag); + + /* + * Remove the UDP header (and possibly the non ESP marker) + * IP header length is iphlen + * Before: + * <--- off ---> + * +----+------+-----+ + * | IP | UDP | ESP | + * +----+------+-----+ + * <-skip-> + * After: + * +----+-----+ + * | IP | ESP | + * +----+-----+ + * <-skip-> + */ + ovbcopy(data, data + skip, iphlen); + m_adj(m, skip); + + ip = mtod(m, struct ip *); + ip->ip_len -= skip; + ip->ip_p = IPPROTO_ESP; + + /* + * We cannot yet update the cksums so clear any + * h/w cksum flags as they are no longer valid. + */ + if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) + m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID|CSUM_PSEUDO_HDR); + + (void) ipsec4_common_input(m, iphlen, ip->ip_p); + return (NULL); /* NB: consumed, bypass processing. */ +} +#endif /* INET */ +#endif /* defined(IPSEC) && defined(IPSEC_NAT_T) */ + static void udp_abort(struct socket *so) { diff --git a/sys/netinet/udp_var.h b/sys/netinet/udp_var.h index 8b045ad..211edff 100644 --- a/sys/netinet/udp_var.h +++ b/sys/netinet/udp_var.h @@ -64,6 +64,12 @@ struct udpcb { #define intoudpcb(ip) ((struct udpcb *)(ip)->inp_ppcb) #define sotoudpcb(so) (intoudpcb(sotoinpcb(so))) + /* IPsec: ESP in UDP tunneling: */ +#define UF_ESPINUDP_NON_IKE 0x00000001 /* w/ non-IKE marker .. */ + /* .. per draft-ietf-ipsec-nat-t-ike-0[01], + * and draft-ietf-ipsec-udp-encaps-(00/)01.txt */ +#define UF_ESPINUDP 0x00000002 /* w/ non-ESP marker. */ + struct udpstat { /* input statistics: */ u_long udps_ipackets; /* total input packets */ @@ -127,6 +133,7 @@ int udp_newudpcb(struct inpcb *); void udp_discardcb(struct udpcb *); void udp_ctlinput(int, struct sockaddr *, void *); +int udp_ctloutput(struct socket *, struct sockopt *); void udp_init(void); #ifdef VIMAGE void udp_destroy(void); diff --git a/sys/netipsec/ipsec_input.c b/sys/netipsec/ipsec_input.c index ce6b060..4ec169b 100644 --- a/sys/netipsec/ipsec_input.c +++ b/sys/netipsec/ipsec_input.c @@ -121,6 +121,9 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto) struct secasvar *sav; u_int32_t spi; int error; +#ifdef IPSEC_NAT_T + struct m_tag *tag; +#endif IPSEC_ISTAT(sproto, V_espstat.esps_input, V_ahstat.ahs_input, V_ipcompstat.ipcomps_input); @@ -175,6 +178,12 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto) m_copydata(m, offsetof(struct ip, ip_dst), sizeof(struct in_addr), (caddr_t) &dst_address.sin.sin_addr); +#ifdef IPSEC_NAT_T + /* Find the source port for NAT-T; see udp*_espdecap. */ + tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL); + if (tag != NULL) + dst_address.sin.sin_port = ((u_int16_t *)(tag + 1))[1]; +#endif /* IPSEC_NAT_T */ break; #endif /* INET */ #ifdef INET6 diff --git a/sys/netipsec/ipsec_output.c b/sys/netipsec/ipsec_output.c index ec6b41e..b953786 100644 --- a/sys/netipsec/ipsec_output.c +++ b/sys/netipsec/ipsec_output.c @@ -84,6 +84,10 @@ #include <machine/in_cksum.h> +#ifdef IPSEC_NAT_T +#include <netinet/udp.h> +#endif + #ifdef DEV_ENC #include <net/if_enc.h> #endif @@ -180,6 +184,57 @@ ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr) ip->ip_len = ntohs(ip->ip_len); ip->ip_off = ntohs(ip->ip_off); +#ifdef IPSEC_NAT_T + /* + * If NAT-T is enabled, now that all IPsec processing is done + * insert UDP encapsulation header after IP header. + */ + if (sav->natt_type) { +#ifdef _IP_VHL + const int hlen = IP_VHL_HL(ip->ip_vhl); +#else + const int hlen = (ip->ip_hl << 2); +#endif + int size, off; + struct mbuf *mi; + struct udphdr *udp; + + size = sizeof(struct udphdr); + if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) { + /* + * draft-ietf-ipsec-nat-t-ike-0[01].txt and + * draft-ietf-ipsec-udp-encaps-(00/)01.txt, + * ignoring possible AH mode + * non-IKE marker + non-ESP marker + * from draft-ietf-ipsec-udp-encaps-00.txt. + */ + size += sizeof(u_int64_t); + } + mi = m_makespace(m, hlen, size, &off); + if (mi == NULL) { + DPRINTF(("%s: m_makespace for udphdr failed\n", + __func__)); + error = ENOBUFS; + goto bad; + } + + udp = (struct udphdr *)(mtod(mi, caddr_t) + off); + if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) + udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT); + else + udp->uh_sport = + KEY_PORTFROMSADDR(&sav->sah->saidx.src); + udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst); + udp->uh_sum = 0; + udp->uh_ulen = htons(m->m_pkthdr.len - hlen); + ip->ip_len = m->m_pkthdr.len; + ip->ip_p = IPPROTO_UDP; + + if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) + *(u_int64_t *)(udp + 1) = 0; + } +#endif /* IPSEC_NAT_T */ + return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); #endif /* INET */ #ifdef INET6 diff --git a/sys/netipsec/key.c b/sys/netipsec/key.c index e79179f..8f81d83 100644 --- a/sys/netipsec/key.c +++ b/sys/netipsec/key.c @@ -221,6 +221,12 @@ static const int minsize[] = { 0, /* SADB_X_EXT_KMPRIVATE */ sizeof(struct sadb_x_policy), /* SADB_X_EXT_POLICY */ sizeof(struct sadb_x_sa2), /* SADB_X_SA2 */ + sizeof(struct sadb_x_nat_t_type),/* SADB_X_EXT_NAT_T_TYPE */ + sizeof(struct sadb_x_nat_t_port),/* SADB_X_EXT_NAT_T_SPORT */ + sizeof(struct sadb_x_nat_t_port),/* SADB_X_EXT_NAT_T_DPORT */ + sizeof(struct sadb_address), /* SADB_X_EXT_NAT_T_OAI */ + sizeof(struct sadb_address), /* SADB_X_EXT_NAT_T_OAR */ + sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */ }; static const int maxsize[] = { sizeof(struct sadb_msg), /* SADB_EXT_RESERVED */ @@ -243,6 +249,12 @@ static const int maxsize[] = { 0, /* SADB_X_EXT_KMPRIVATE */ 0, /* SADB_X_EXT_POLICY */ sizeof(struct sadb_x_sa2), /* SADB_X_SA2 */ + sizeof(struct sadb_x_nat_t_type),/* SADB_X_EXT_NAT_T_TYPE */ + sizeof(struct sadb_x_nat_t_port),/* SADB_X_EXT_NAT_T_SPORT */ + sizeof(struct sadb_x_nat_t_port),/* SADB_X_EXT_NAT_T_DPORT */ + 0, /* SADB_X_EXT_NAT_T_OAI */ + 0, /* SADB_X_EXT_NAT_T_OAR */ + sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */ }; #ifdef SYSCTL_DECL @@ -425,6 +437,13 @@ static struct mbuf *key_setsadbmsg __P((u_int8_t, u_int16_t, u_int8_t, static struct mbuf *key_setsadbsa __P((struct secasvar *)); static struct mbuf *key_setsadbaddr __P((u_int16_t, const struct sockaddr *, u_int8_t, u_int16_t)); +#ifdef IPSEC_NAT_T +static struct mbuf *key_setsadbxport(u_int16_t, u_int16_t); +static struct mbuf *key_setsadbxtype(u_int16_t); +#endif +static void key_porttosaddr(struct sockaddr *, u_int16_t); +#define KEY_PORTTOSADDR(saddr, port) \ + key_porttosaddr((struct sockaddr *)(saddr), (port)) static struct mbuf *key_setsadbxsa2 __P((u_int8_t, u_int32_t, u_int32_t)); static struct mbuf *key_setsadbxpolicy __P((u_int16_t, u_int8_t, u_int32_t)); @@ -1067,12 +1086,21 @@ key_allocsa( struct secasvar *sav; u_int stateidx, arraysize, state; const u_int *saorder_state_valid; + int chkport; IPSEC_ASSERT(dst != NULL, ("null dst address")); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP %s from %s:%u\n", __func__, where, tag)); +#ifdef IPSEC_NAT_T + chkport = (dst->sa.sa_family == AF_INET && + dst->sa.sa_len == sizeof(struct sockaddr_in) && + dst->sin.sin_port != 0); +#else + chkport = 0; +#endif + /* * searching SAD. * XXX: to be checked internal IP header somewhere. Also when @@ -1104,11 +1132,11 @@ key_allocsa( continue; #if 0 /* don't check src */ /* check src address */ - if (key_sockaddrcmp(&src->sa, &sav->sah->saidx.src.sa, 0) != 0) + if (key_sockaddrcmp(&src->sa, &sav->sah->saidx.src.sa, chkport) != 0) continue; #endif /* check dst address */ - if (key_sockaddrcmp(&dst->sa, &sav->sah->saidx.dst.sa, 0) != 0) + if (key_sockaddrcmp(&dst->sa, &sav->sah->saidx.dst.sa, chkport) != 0) continue; sa_addref(sav); goto done; @@ -1798,6 +1826,11 @@ key_spdadd(so, m, mhp) dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY]; + /* + * Note: do not parse SADB_X_EXT_NAT_T_* here: + * we are processing traffic endpoints. + */ + /* make secindex */ /* XXX boundary check against sa_len */ KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, @@ -1931,6 +1964,11 @@ key_spdadd(so, m, mhp) struct sadb_msg *newmsg; int off; + /* + * Note: do not send SADB_X_EXT_NAT_T_* here: + * we are sending traffic endpoints. + */ + /* create new sadb_msg to reply. */ if (lft) { n = key_gather_mbuf(m, mhp, 2, 5, SADB_EXT_RESERVED, @@ -2053,6 +2091,11 @@ key_spddelete(so, m, mhp) dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY]; + /* + * Note: do not parse SADB_X_EXT_NAT_T_* here: + * we are processing traffic endpoints. + */ + /* make secindex */ /* XXX boundary check against sa_len */ KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, @@ -2089,6 +2132,11 @@ key_spddelete(so, m, mhp) struct mbuf *n; struct sadb_msg *newmsg; + /* + * Note: do not send SADB_X_EXT_NAT_T_* here: + * we are sending traffic endpoints. + */ + /* create new sadb_msg to reply. */ n = key_gather_mbuf(m, mhp, 1, 4, SADB_EXT_RESERVED, SADB_X_EXT_POLICY, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); @@ -2435,6 +2483,10 @@ key_setdumpsp(struct secpolicy *sp, u_int8_t type, u_int32_t seq, u_int32_t pid) goto fail; result = m; + /* + * Note: do not send SADB_X_EXT_NAT_T_* here: + * we are sending traffic endpoints. + */ m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, &sp->spidx.src.sa, sp->spidx.prefs, sp->spidx.ul_proto); @@ -2581,6 +2633,11 @@ key_spdexpire(sp) lt->sadb_lifetime_usetime = sp->validtime; m_cat(result, m); + /* + * Note: do not send SADB_X_EXT_NAT_T_* here: + * we are sending traffic endpoints. + */ + /* set sadb_address for source */ m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, &sp->spidx.src.sa, @@ -3034,6 +3091,9 @@ key_setsaval(sav, m, mhp) sav->tdb_encalgxform = NULL; /* encoding algorithm */ sav->tdb_authalgxform = NULL; /* authentication algorithm */ sav->tdb_compalgxform = NULL; /* compression algorithm */ + /* Initialize even if NAT-T not compiled in: */ + sav->natt_type = 0; + sav->natt_esp_frag_len = 0; /* SA */ if (mhp->ext[SADB_EXT_SA] != NULL) { @@ -3343,6 +3403,12 @@ key_setdumpsa(struct secasvar *sav, u_int8_t type, u_int8_t satype, SADB_EXT_ADDRESS_DST, SADB_EXT_ADDRESS_PROXY, SADB_EXT_KEY_AUTH, SADB_EXT_KEY_ENCRYPT, SADB_EXT_IDENTITY_SRC, SADB_EXT_IDENTITY_DST, SADB_EXT_SENSITIVITY, +#ifdef IPSEC_NAT_T + SADB_X_EXT_NAT_T_TYPE, + SADB_X_EXT_NAT_T_SPORT, SADB_X_EXT_NAT_T_DPORT, + SADB_X_EXT_NAT_T_OAI, SADB_X_EXT_NAT_T_OAR, + SADB_X_EXT_NAT_T_FRAG, +#endif }; m = key_setsadbmsg(type, 0, satype, seq, pid, sav->refcnt); @@ -3427,6 +3493,36 @@ key_setdumpsa(struct secasvar *sav, u_int8_t type, u_int8_t satype, goto fail; break; +#ifdef IPSEC_NAT_T + case SADB_X_EXT_NAT_T_TYPE: + m = key_setsadbxtype(sav->natt_type); + if (!m) + goto fail; + break; + + case SADB_X_EXT_NAT_T_DPORT: + m = key_setsadbxport( + KEY_PORTFROMSADDR(&sav->sah->saidx.dst), + SADB_X_EXT_NAT_T_DPORT); + if (!m) + goto fail; + break; + + case SADB_X_EXT_NAT_T_SPORT: + m = key_setsadbxport( + KEY_PORTFROMSADDR(&sav->sah->saidx.src), + SADB_X_EXT_NAT_T_SPORT); + if (!m) + goto fail; + break; + + case SADB_X_EXT_NAT_T_OAI: + case SADB_X_EXT_NAT_T_OAR: + case SADB_X_EXT_NAT_T_FRAG: + /* We do not (yet) support those. */ + continue; +#endif + case SADB_EXT_ADDRESS_PROXY: case SADB_EXT_IDENTITY_SRC: case SADB_EXT_IDENTITY_DST: @@ -3621,6 +3717,116 @@ key_setsadbxsa2(u_int8_t mode, u_int32_t seq, u_int32_t reqid) return m; } +#ifdef IPSEC_NAT_T +/* + * Set a type in sadb_x_nat_t_type. + */ +static struct mbuf * +key_setsadbxtype(u_int16_t type) +{ + struct mbuf *m; + size_t len; + struct sadb_x_nat_t_type *p; + + len = PFKEY_ALIGN8(sizeof(struct sadb_x_nat_t_type)); + + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + return (NULL); + } + + p = mtod(m, struct sadb_x_nat_t_type *); + + bzero(p, len); + p->sadb_x_nat_t_type_len = PFKEY_UNIT64(len); + p->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE; + p->sadb_x_nat_t_type_type = type; + + return (m); +} +/* + * Set a port in sadb_x_nat_t_port. + * In contrast to default RFC 2367 behaviour, port is in network byte order. + */ +static struct mbuf * +key_setsadbxport(u_int16_t port, u_int16_t type) +{ + struct mbuf *m; + size_t len; + struct sadb_x_nat_t_port *p; + + len = PFKEY_ALIGN8(sizeof(struct sadb_x_nat_t_port)); + + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + return (NULL); + } + + p = mtod(m, struct sadb_x_nat_t_port *); + + bzero(p, len); + p->sadb_x_nat_t_port_len = PFKEY_UNIT64(len); + p->sadb_x_nat_t_port_exttype = type; + p->sadb_x_nat_t_port_port = port; + + return (m); +} + +/* + * Get port from sockaddr. Port is in network byte order. + */ +u_int16_t +key_portfromsaddr(struct sockaddr *sa) +{ + INIT_VNET_IPSEC(curvnet); + + switch (sa->sa_family) { +#ifdef INET + case AF_INET: + return ((struct sockaddr_in *)sa)->sin_port; +#endif +#ifdef INET6 + case AF_INET6: + return ((struct sockaddr_in6 *)sa)->sin6_port; +#endif + } + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP %s unexpected address family %d\n", + __func__, sa->sa_family)); + return (0); +} +#endif /* IPSEC_NAT_T */ + +/* + * Set port in struct sockaddr. Port is in network byte order. + */ +static void +key_porttosaddr(struct sockaddr *sa, u_int16_t port) +{ + INIT_VNET_IPSEC(curvnet); + + switch (sa->sa_family) { +#ifdef INET + case AF_INET: + ((struct sockaddr_in *)sa)->sin_port = port; + break; +#endif +#ifdef INET6 + case AF_INET6: + ((struct sockaddr_in6 *)sa)->sin6_port = port; + break; +#endif + default: + ipseclog((LOG_DEBUG, "%s: unexpected address family %d.\n", + __func__, sa->sa_family)); + break; + } +} + /* * set data into sadb_x_policy */ @@ -3818,6 +4024,8 @@ key_cmpsaidx( const struct secasindex *saidx1, int flag) { + int chkport = 0; + /* sanity */ if (saidx0 == NULL && saidx1 == NULL) return 1; @@ -3855,10 +4063,25 @@ key_cmpsaidx( return 0; } - if (key_sockaddrcmp(&saidx0->src.sa, &saidx1->src.sa, 0) != 0) { +#ifdef IPSEC_NAT_T + /* + * If NAT-T is enabled, check ports for tunnel mode. + * Do not check ports if they are set to zero in the SPD. + * Also do not do it for transport mode, as there is no + * port information available in the SP. + */ + if (saidx1->mode == IPSEC_MODE_TUNNEL && + saidx1->src.sa.sa_family == AF_INET && + saidx1->dst.sa.sa_family == AF_INET && + ((const struct sockaddr_in *)(&saidx1->src))->sin_port && + ((const struct sockaddr_in *)(&saidx1->dst))->sin_port) + chkport = 1; +#endif /* IPSEC_NAT_T */ + + if (key_sockaddrcmp(&saidx0->src.sa, &saidx1->src.sa, chkport) != 0) { return 0; } - if (key_sockaddrcmp(&saidx0->dst.sa, &saidx1->dst.sa, 0) != 0) { + if (key_sockaddrcmp(&saidx0->dst.sa, &saidx1->dst.sa, chkport) != 0) { return 0; } } @@ -4492,7 +4715,10 @@ key_getspi(so, m, mhp) return key_senderror(so, m, EINVAL); } - /* make sure if port number is zero. */ + /* + * Make sure the port numbers are zero. + * In case of NAT-T we will update them later if needed. + */ switch (((struct sockaddr *)(src0 + 1))->sa_family) { case AF_INET: if (((struct sockaddr *)(src0 + 1))->sa_len != @@ -4529,6 +4755,43 @@ key_getspi(so, m, mhp) /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); +#ifdef IPSEC_NAT_T + /* + * Handle NAT-T info if present. + * We made sure the port numbers are zero above, so we do + * not have to worry in case we do not update them. + */ + if (mhp->ext[SADB_X_EXT_NAT_T_OAI] != NULL) + ipseclog((LOG_DEBUG, "%s: NAT-T OAi present\n", __func__)); + if (mhp->ext[SADB_X_EXT_NAT_T_OAR] != NULL) + ipseclog((LOG_DEBUG, "%s: NAT-T OAr present\n", __func__)); + + if (mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL && + mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && + mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { + struct sadb_x_nat_t_type *type; + struct sadb_x_nat_t_port *sport, *dport; + + if (mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type) || + mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || + mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { + ipseclog((LOG_DEBUG, "%s: invalid nat-t message " + "passed.\n", __func__)); + return key_senderror(so, m, EINVAL); + } + + sport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_SPORT]; + dport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_DPORT]; + + if (sport) + KEY_PORTTOSADDR(&saidx.src, sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&saidx.dst, dport->sadb_x_nat_t_port_port); + } +#endif + /* SPI allocation */ spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE], &saidx); @@ -4726,6 +4989,11 @@ key_update(so, m, mhp) INIT_VNET_IPSEC(curvnet); struct sadb_sa *sa0; struct sadb_address *src0, *dst0; +#ifdef IPSEC_NAT_T + struct sadb_x_nat_t_type *type; + struct sadb_address *iaddr, *raddr; + struct sadb_x_nat_t_frag *frag; +#endif struct secasindex saidx; struct secashead *sah; struct secasvar *sav; @@ -4784,6 +5052,73 @@ key_update(so, m, mhp) /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); + /* + * Make sure the port numbers are zero. + * In case of NAT-T we will update them later if needed. + */ + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); + +#ifdef IPSEC_NAT_T + /* + * Handle NAT-T info if present. + */ + if (mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL && + mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && + mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { + struct sadb_x_nat_t_port *sport, *dport; + + if (mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type) || + mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || + mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { + ipseclog((LOG_DEBUG, "%s: invalid message.\n", + __func__)); + return key_senderror(so, m, EINVAL); + } + + type = (struct sadb_x_nat_t_type *) + mhp->ext[SADB_X_EXT_NAT_T_TYPE]; + sport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_SPORT]; + dport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_DPORT]; + + if (sport) + KEY_PORTTOSADDR(&saidx.src, + sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&saidx.dst, + dport->sadb_x_nat_t_port_port); + } else { + type = 0; + } + if (mhp->ext[SADB_X_EXT_NAT_T_OAI] != NULL && + mhp->ext[SADB_X_EXT_NAT_T_OAR] != NULL) { + if (mhp->extlen[SADB_X_EXT_NAT_T_OAI] < sizeof(*iaddr) || + mhp->extlen[SADB_X_EXT_NAT_T_OAR] < sizeof(*raddr)) { + ipseclog((LOG_DEBUG, "%s: invalid message\n", + __func__)); + return key_senderror(so, m, EINVAL); + } + iaddr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAI]; + raddr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAR]; + ipseclog((LOG_DEBUG, "%s: NAT-T OAi/r present\n", __func__)); + } else { + iaddr = raddr = NULL; + } + if (mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) { + if (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag)) { + ipseclog((LOG_DEBUG, "%s: invalid message\n", + __func__)); + return key_senderror(so, m, EINVAL); + } + frag = (struct sadb_x_nat_t_frag *) + mhp->ext[SADB_X_EXT_NAT_T_FRAG]; + } else { + frag = 0; + } +#endif + /* get a SA header */ if ((sah = key_getsah(&saidx)) == NULL) { ipseclog((LOG_DEBUG, "%s: no SA index found.\n", __func__)); @@ -4850,6 +5185,25 @@ key_update(so, m, mhp) return key_senderror(so, m, 0); } +#ifdef IPSEC_NAT_T + /* + * Handle more NAT-T info if present, + * now that we have a sav to fill. + */ + if (type) + sav->natt_type = type->sadb_x_nat_t_type_type; + +#if 0 + /* + * In case SADB_X_EXT_NAT_T_FRAG was not given, leave it at 0. + * We should actually check for a minimum MTU here, if we + * want to support it in ip_output. + */ + if (frag) + sav->natt_esp_frag_len = frag->sadb_x_nat_t_frag_fraglen; +#endif +#endif + { struct mbuf *n; @@ -4925,6 +5279,11 @@ key_add(so, m, mhp) INIT_VNET_IPSEC(curvnet); struct sadb_sa *sa0; struct sadb_address *src0, *dst0; +#ifdef IPSEC_NAT_T + struct sadb_x_nat_t_type *type; + struct sadb_address *iaddr, *raddr; + struct sadb_x_nat_t_frag *frag; +#endif struct secasindex saidx; struct secashead *newsah; struct secasvar *newsav; @@ -4983,6 +5342,73 @@ key_add(so, m, mhp) /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); + /* + * Make sure the port numbers are zero. + * In case of NAT-T we will update them later if needed. + */ + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); + +#ifdef IPSEC_NAT_T + /* + * Handle NAT-T info if present. + */ + if (mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL && + mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && + mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { + struct sadb_x_nat_t_port *sport, *dport; + + if (mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type) || + mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || + mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { + ipseclog((LOG_DEBUG, "%s: invalid message.\n", + __func__)); + return key_senderror(so, m, EINVAL); + } + + type = (struct sadb_x_nat_t_type *) + mhp->ext[SADB_X_EXT_NAT_T_TYPE]; + sport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_SPORT]; + dport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_DPORT]; + + if (sport) + KEY_PORTTOSADDR(&saidx.src, + sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&saidx.dst, + dport->sadb_x_nat_t_port_port); + } else { + type = 0; + } + if (mhp->ext[SADB_X_EXT_NAT_T_OAI] != NULL && + mhp->ext[SADB_X_EXT_NAT_T_OAR] != NULL) { + if (mhp->extlen[SADB_X_EXT_NAT_T_OAI] < sizeof(*iaddr) || + mhp->extlen[SADB_X_EXT_NAT_T_OAR] < sizeof(*raddr)) { + ipseclog((LOG_DEBUG, "%s: invalid message\n", + __func__)); + return key_senderror(so, m, EINVAL); + } + iaddr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAI]; + raddr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAR]; + ipseclog((LOG_DEBUG, "%s: NAT-T OAi/r present\n", __func__)); + } else { + iaddr = raddr = NULL; + } + if (mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) { + if (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag)) { + ipseclog((LOG_DEBUG, "%s: invalid message\n", + __func__)); + return key_senderror(so, m, EINVAL); + } + frag = (struct sadb_x_nat_t_frag *) + mhp->ext[SADB_X_EXT_NAT_T_FRAG]; + } else { + frag = 0; + } +#endif + /* get a SA header */ if ((newsah = key_getsah(&saidx)) == NULL) { /* create a new SA header */ @@ -5019,6 +5445,25 @@ key_add(so, m, mhp) return key_senderror(so, m, error); } +#ifdef IPSEC_NAT_T + /* + * Handle more NAT-T info if present, + * now that we have a sav to fill. + */ + if (type) + newsav->natt_type = type->sadb_x_nat_t_type_type; + +#if 0 + /* + * In case SADB_X_EXT_NAT_T_FRAG was not given, leave it at 0. + * We should actually check for a minimum MTU here, if we + * want to support it in ip_output. + */ + if (frag) + newsav->natt_esp_frag_len = frag->sadb_x_nat_t_frag_fraglen; +#endif +#endif + /* * don't call key_freesav() here, as we would like to keep the SA * in the database on success. @@ -5224,6 +5669,42 @@ key_delete(so, m, mhp) /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); + /* + * Make sure the port numbers are zero. + * In case of NAT-T we will update them later if needed. + */ + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); + +#ifdef IPSEC_NAT_T + /* + * Handle NAT-T info if present. + */ + if (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && + mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { + struct sadb_x_nat_t_port *sport, *dport; + + if (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || + mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { + ipseclog((LOG_DEBUG, "%s: invalid message.\n", + __func__)); + return key_senderror(so, m, EINVAL); + } + + sport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_SPORT]; + dport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_DPORT]; + + if (sport) + KEY_PORTTOSADDR(&saidx.src, + sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&saidx.dst, + dport->sadb_x_nat_t_port_port); + } +#endif + /* get a SA header */ SAHTREE_LOCK(); LIST_FOREACH(sah, &V_sahtree, chain) { @@ -5252,6 +5733,7 @@ key_delete(so, m, mhp) struct sadb_msg *newmsg; /* create new sadb_msg to reply. */ + /* XXX-BZ NAT-T extensions? */ n = key_gather_mbuf(m, mhp, 1, 4, SADB_EXT_RESERVED, SADB_EXT_SA, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); if (!n) @@ -5291,6 +5773,43 @@ key_delete_all(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp, /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); + /* + * Make sure the port numbers are zero. + * In case of NAT-T we will update them later if needed. + */ + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); + +#ifdef IPSEC_NAT_T + /* + * Handle NAT-T info if present. + */ + + if (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && + mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { + struct sadb_x_nat_t_port *sport, *dport; + + if (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || + mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { + ipseclog((LOG_DEBUG, "%s: invalid message.\n", + __func__)); + return key_senderror(so, m, EINVAL); + } + + sport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_SPORT]; + dport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_DPORT]; + + if (sport) + KEY_PORTTOSADDR(&saidx.src, + sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&saidx.dst, + dport->sadb_x_nat_t_port_port); + } +#endif + SAHTREE_LOCK(); LIST_FOREACH(sah, &V_sahtree, chain) { if (sah->state == SADB_SASTATE_DEAD) @@ -5327,6 +5846,7 @@ key_delete_all(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp, struct sadb_msg *newmsg; /* create new sadb_msg to reply. */ + /* XXX-BZ NAT-T extensions? */ n = key_gather_mbuf(m, mhp, 1, 3, SADB_EXT_RESERVED, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); if (!n) @@ -5406,6 +5926,43 @@ key_get(so, m, mhp) /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); + /* + * Make sure the port numbers are zero. + * In case of NAT-T we will update them later if needed. + */ + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); + +#ifdef IPSEC_NAT_T + /* + * Handle NAT-T info if present. + */ + + if (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && + mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { + struct sadb_x_nat_t_port *sport, *dport; + + if (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || + mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { + ipseclog((LOG_DEBUG, "%s: invalid message.\n", + __func__)); + return key_senderror(so, m, EINVAL); + } + + sport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_SPORT]; + dport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_DPORT]; + + if (sport) + KEY_PORTTOSADDR(&saidx.src, + sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&saidx.dst, + dport->sadb_x_nat_t_port_port); + } +#endif + /* get a SA header */ SAHTREE_LOCK(); LIST_FOREACH(sah, &V_sahtree, chain) { @@ -5778,6 +6335,11 @@ key_acquire(const struct secasindex *saidx, struct secpolicy *sp) } result = m; + /* + * No SADB_X_EXT_NAT_T_* here: we do not know + * anything related to NAT-T at this time. + */ + /* set sadb_address for saidx's. */ m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, &saidx->src.sa, FULLMASK, IPSEC_ULPROTO_ANY); @@ -6104,6 +6666,43 @@ key_acquire2(so, m, mhp) /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); + /* + * Make sure the port numbers are zero. + * In case of NAT-T we will update them later if needed. + */ + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); + +#ifndef IPSEC_NAT_T + /* + * Handle NAT-T info if present. + */ + + if (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL && + mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) { + struct sadb_x_nat_t_port *sport, *dport; + + if (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) || + mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) { + ipseclog((LOG_DEBUG, "%s: invalid message.\n", + __func__)); + return key_senderror(so, m, EINVAL); + } + + sport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_SPORT]; + dport = (struct sadb_x_nat_t_port *) + mhp->ext[SADB_X_EXT_NAT_T_DPORT]; + + if (sport) + KEY_PORTTOSADDR(&saidx.src, + sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&saidx.dst, + dport->sadb_x_nat_t_port_port); + } +#endif + /* get a SA index */ SAHTREE_LOCK(); LIST_FOREACH(sah, &V_sahtree, chain) { @@ -6426,6 +7025,10 @@ key_expire(struct secasvar *sav) } m_cat(result, m); + /* + * XXX-BZ Handle NAT-T extensions here. + */ + if ((result->m_flags & M_PKTHDR) == 0) { error = EINVAL; goto fail; @@ -7059,6 +7662,14 @@ key_align(m, mhp) case SADB_EXT_SPIRANGE: case SADB_X_EXT_POLICY: case SADB_X_EXT_SA2: +#ifdef IPSEC_NAT_T + case SADB_X_EXT_NAT_T_TYPE: + case SADB_X_EXT_NAT_T_SPORT: + case SADB_X_EXT_NAT_T_DPORT: + case SADB_X_EXT_NAT_T_OAI: + case SADB_X_EXT_NAT_T_OAR: + case SADB_X_EXT_NAT_T_FRAG: +#endif /* duplicate check */ /* * XXX Are there duplication payloads of either diff --git a/sys/netipsec/key.h b/sys/netipsec/key.h index fc38279..e85acd1 100644 --- a/sys/netipsec/key.h +++ b/sys/netipsec/key.h @@ -106,6 +106,11 @@ extern void key_destroy(void); extern void key_sa_recordxfer __P((struct secasvar *, struct mbuf *)); extern void key_sa_routechange __P((struct sockaddr *)); extern void key_sa_stir_iv __P((struct secasvar *)); +#ifdef IPSEC_NAT_T +u_int16_t key_portfromsaddr(struct sockaddr *); +#define KEY_PORTFROMSADDR(saddr) \ + key_portfromsaddr((struct sockaddr *)(saddr)) +#endif #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_IPSEC_SA); diff --git a/sys/netipsec/keydb.h b/sys/netipsec/keydb.h index c9a37e3..35a0122 100644 --- a/sys/netipsec/keydb.h +++ b/sys/netipsec/keydb.h @@ -151,6 +151,12 @@ struct secasvar { struct auth_hash *tdb_authalgxform; /* authentication algorithm */ struct comp_algo *tdb_compalgxform; /* compression algorithm */ u_int64_t tdb_cryptoid; /* crypto session id */ + + /* + * NAT-Traversal. + */ + u_int16_t natt_type; /* IKE/ESP-marker in output. */ + u_int16_t natt_esp_frag_len; /* MTU for payload fragmentation. */ }; #define SECASVAR_LOCK_INIT(_sav) \ diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index 1ffdc1b..18b1488 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -868,6 +868,7 @@ struct mbuf *m_unshare(struct mbuf *, int how); #define PACKET_TAG_RTSOCKFAM 25 /* rtsock sa family */ #define PACKET_TAG_IPOPTIONS 27 /* Saved IP options */ #define PACKET_TAG_CARP 28 /* CARP info */ +#define PACKET_TAG_IPSEC_NAT_T_PORTS 29 /* two uint16_t */ /* Specific cookies and tags. */ |