From owner-svn-src-projects@freebsd.org Sat Dec 10 14:20:53 2016 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 2640DC70950 for ; Sat, 10 Dec 2016 14:20:53 +0000 (UTC) (envelope-from ae@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id E2C35125E; Sat, 10 Dec 2016 14:20:52 +0000 (UTC) (envelope-from ae@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id uBAEKqf5084760; Sat, 10 Dec 2016 14:20:52 GMT (envelope-from ae@FreeBSD.org) Received: (from ae@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id uBAEKpC3084749; Sat, 10 Dec 2016 14:20:51 GMT (envelope-from ae@FreeBSD.org) Message-Id: <201612101420.uBAEKpC3084749@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ae set sender to ae@FreeBSD.org using -f From: "Andrey V. Elsukov" Date: Sat, 10 Dec 2016 14:20:51 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r309808 - in projects/ipsec/sys: conf netinet netipsec X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 10 Dec 2016 14:20:53 -0000 Author: ae Date: Sat Dec 10 14:20:51 2016 New Revision: 309808 URL: https://svnweb.freebsd.org/changeset/base/309808 Log: Rework NAT-T support. Remove UDP_ENCAP_ESPINUDP_NON_IKE UDP encapsulation support. This type was introduced in draft-ietf-ipsec-udp-encaps-00 and then was removed in draft-ietf-ipsec-udp-encaps-02. All these drafts were already expired almost 15 years ago. Now we support only one encapsulation type described in the RFC3948. NAT-T support is enabled by default in IPSEC kernel, IPSEC_NAT_T option is removed. Move UDP encapsulation releated code into netipsec/udpencap.c. Add udp_ipsec_input() function. Use it from UDP input path to check that datagram needs decapsulation. Add udp_ipsec_output() function to encapsulate outbound IP+ESP datagram into UDP. Add udp_ipsec_adjust_cksum() function to update TCP/UDP checksum for decapsulated and decrypted inbound packet for transport mode SA. Introduce net.inet.ipsec.natt_cksum_policy sysctl variable. It controls how udp_ipsec_adjust_cksum() updates TCP/UDP checksum: 0: use incremental checksum update using information about original addresses from IKE. 1: fully recalculate checksum; 2: for UDP reset checksum to zero, for TCP set mbuf's csum_flags to mark checksum as valid. Due to changes in SADB now each SA has unique SPI. Thus even when several peers are behind the same NAT, they all will have different SPI and we don't need to keep ports in the SAIDX. Remove all such ports checks and all XXX comments related to NAT-T from key.c. Instead add big comment that describes how NAT-T support implemented and where the kernel expects NAT-T extension headers. Introduce "struct secnatt {}" that contains all information related to NAT-T, allocate it by demand if IKEd has determined presence of NAT. Add support for SADB_X_EXT_NAT_T_OAI and SADB_X_EXT_NAT_T_OAR PF_KEY messages. Added: projects/ipsec/sys/netipsec/udpencap.c (contents, props changed) Modified: projects/ipsec/sys/conf/NOTES projects/ipsec/sys/conf/files projects/ipsec/sys/conf/options projects/ipsec/sys/netinet/udp.h projects/ipsec/sys/netinet/udp_usrreq.c projects/ipsec/sys/netipsec/ipsec.c projects/ipsec/sys/netipsec/ipsec.h projects/ipsec/sys/netipsec/ipsec_input.c projects/ipsec/sys/netipsec/ipsec_output.c projects/ipsec/sys/netipsec/key.c projects/ipsec/sys/netipsec/keydb.h Modified: projects/ipsec/sys/conf/NOTES ============================================================================== --- projects/ipsec/sys/conf/NOTES Sat Dec 10 13:30:18 2016 (r309807) +++ projects/ipsec/sys/conf/NOTES Sat Dec 10 14:20:51 2016 (r309808) @@ -625,11 +625,6 @@ options TCP_OFFLOAD # TCP offload supp # your kernel configuration options IPSEC #IP security (requires device crypto) #options IPSEC_DEBUG #debug for IP security -# -# 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 # # SMB/CIFS requester Modified: projects/ipsec/sys/conf/files ============================================================================== --- projects/ipsec/sys/conf/files Sat Dec 10 13:30:18 2016 (r309807) +++ projects/ipsec/sys/conf/files Sat Dec 10 14:20:51 2016 (r309808) @@ -4111,6 +4111,7 @@ netipsec/ipsec_output.c optional ipsec netipsec/key.c optional ipsec inet | ipsec inet6 netipsec/key_debug.c optional ipsec inet | ipsec inet6 netipsec/keysock.c optional ipsec inet | ipsec inet6 +netipsec/udpencap.c optional ipsec inet netipsec/xform_ah.c optional ipsec inet | ipsec inet6 netipsec/xform_esp.c optional ipsec inet | ipsec inet6 netipsec/xform_ipcomp.c optional ipsec inet | ipsec inet6 Modified: projects/ipsec/sys/conf/options ============================================================================== --- projects/ipsec/sys/conf/options Sat Dec 10 13:30:18 2016 (r309807) +++ projects/ipsec/sys/conf/options Sat Dec 10 14:20:51 2016 (r309808) @@ -427,7 +427,6 @@ IPFIREWALL_VERBOSE opt_ipfw.h IPFIREWALL_VERBOSE_LIMIT opt_ipfw.h IPSEC opt_ipsec.h IPSEC_DEBUG opt_ipsec.h -IPSEC_NAT_T opt_ipsec.h IPSTEALTH KRPC LIBALIAS Modified: projects/ipsec/sys/netinet/udp.h ============================================================================== --- projects/ipsec/sys/netinet/udp.h Sat Dec 10 13:30:18 2016 (r309807) +++ projects/ipsec/sys/netinet/udp.h Sat Dec 10 14:20:51 2016 (r309808) @@ -58,7 +58,7 @@ struct udphdr { */ /* 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+ */ +#define UDP_ENCAP_ESPINUDP 2 /* RFC3948 */ /* Default ESP in UDP encapsulation port. */ #define UDP_ENCAP_ESPINUDP_PORT 500 Modified: projects/ipsec/sys/netinet/udp_usrreq.c ============================================================================== --- projects/ipsec/sys/netinet/udp_usrreq.c Sat Dec 10 13:30:18 2016 (r309807) +++ projects/ipsec/sys/netinet/udp_usrreq.c Sat Dec 10 14:20:51 2016 (r309808) @@ -172,15 +172,6 @@ static int udp_output(struct inpcb *, st struct mbuf *, struct thread *); #endif -#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) { @@ -345,15 +336,11 @@ udp_append(struct inpcb *inp, struct ip m_freem(n); return (0); } -#ifdef IPSEC_NAT_T - 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 (0); + if (up->u_flags & UF_ESPINUDP) {/* IPSec UDP encaps. */ + if (udp_ipsec_input(n, off, AF_INET) != 0) + return (0); /* Consumed. */ } -#endif /* IPSEC_NAT_T */ #endif /* IPSEC */ #ifdef MAC if (mac_inpcb_check_deliver(inp, n) != 0) { @@ -1021,42 +1008,11 @@ udp_ctloutput(struct socket *so, struct switch (sopt->sopt_dir) { case SOPT_SET: switch (sopt->sopt_name) { +#ifdef IPSEC 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); + error = udp_ipsec_pcbctl(inp, sopt); break; +#endif case UDPLITE_SEND_CSCOV: case UDPLITE_RECV_CSCOV: if (!isudplite) { @@ -1093,13 +1049,9 @@ udp_ctloutput(struct socket *so, struct break; case SOPT_GET: switch (sopt->sopt_name) { -#ifdef IPSEC_NAT_T +#ifdef IPSEC 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); + error = udp_ipsec_pcbctl(inp, sopt); break; #endif case UDPLITE_SEND_CSCOV: @@ -1584,142 +1536,6 @@ release: return (error); } - -#if defined(IPSEC) && defined(IPSEC_NAT_T) -/* - * 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) -{ - 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) { - IPSECSTAT_INC(ips_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)) { - IPSECSTAT_INC(ips_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) { - IPSECSTAT_INC(ips_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 = htons(ntohs(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) ipsec_common_input(m, iphlen, offsetof(struct ip, ip_p), - AF_INET, ip->ip_p); - return (NULL); /* NB: consumed, bypass processing. */ -} -#endif /* defined(IPSEC) && defined(IPSEC_NAT_T) */ - static void udp_abort(struct socket *so) { Modified: projects/ipsec/sys/netipsec/ipsec.c ============================================================================== --- projects/ipsec/sys/netipsec/ipsec.c Sat Dec 10 13:30:18 2016 (r309807) +++ projects/ipsec/sys/netipsec/ipsec.c Sat Dec 10 14:20:51 2016 (r309808) @@ -136,11 +136,17 @@ static VNET_DEFINE(struct secpolicy, def * 0 take anything */ VNET_DEFINE(int, crypto_support) = CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE; +/* + * TCP/UDP checksum handling policy for transport mode NAT-T (RFC3948) + * + * 0 - incrementally recompute. + * 1 - fully recompute TCP/UDP checksum. + * 2 - for UDP reset checksum to zero; for TCP mark csum_flags as valid. + */ +VNET_DEFINE(int, natt_cksum_policy) = 0; FEATURE(ipsec, "Internet Protocol Security (IPsec)"); -#ifdef IPSEC_NAT_T FEATURE(ipsec_natt, "UDP Encapsulation of IPsec ESP Packets ('NAT-T')"); -#endif SYSCTL_DECL(_net_inet_ipsec); @@ -180,7 +186,10 @@ SYSCTL_INT(_net_inet_ipsec, OID_AUTO, cr "Crypto driver selection."); SYSCTL_INT(_net_inet_ipsec, OID_AUTO, check_policy_history, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(check_policy_history), 0, - "Use strict check of inbound packets to security policy compliance"); + "Use strict check of inbound packets to security policy compliance."); +SYSCTL_INT(_net_inet_ipsec, OID_AUTO, natt_cksum_policy, + CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(natt_cksum_policy), 0, + "Method to fix TCP/UDP checksum for transport mode IPsec after NAT."); SYSCTL_VNET_PCPUSTAT(_net_inet_ipsec, OID_AUTO, ipsecstats, struct ipsecstat, ipsec4stat, "IPsec IPv4 statistics."); Modified: projects/ipsec/sys/netipsec/ipsec.h ============================================================================== --- projects/ipsec/sys/netipsec/ipsec.h Sat Dec 10 13:30:18 2016 (r309807) +++ projects/ipsec/sys/netipsec/ipsec.h Sat Dec 10 14:20:51 2016 (r309808) @@ -276,6 +276,7 @@ VNET_DECLARE(int, ip4_ipsec_dfbit); VNET_DECLARE(int, ip4_ipsec_ecn); VNET_DECLARE(int, ip4_esp_randpad); VNET_DECLARE(int, crypto_support); +VNET_DECLARE(int, natt_cksum_policy); #define IPSECSTAT_INC(name) \ VNET_PCPUSTAT_ADD(struct ipsecstat, ipsec4stat, name, 1) @@ -288,6 +289,7 @@ VNET_DECLARE(int, crypto_support); #define V_ip4_ipsec_ecn VNET(ip4_ipsec_ecn) #define V_ip4_esp_randpad VNET(ip4_esp_randpad) #define V_crypto_support VNET(crypto_support) +#define V_natt_cksum_policy VNET(natt_cksum_policy) #define ipseclog(x) do { if (V_ipsec_debug) log x; } while (0) /* for openbsd compatibility */ @@ -320,6 +322,11 @@ int tcp_ipsec_pcbctl(struct inpcb *, str int tcp_ipsec_input(struct mbuf *, struct tcphdr *, u_char *); int tcp_ipsec_output(struct mbuf *, struct tcphdr *, u_char *); +int udp_ipsec_pcbctl(struct inpcb *, struct sockopt *); +int udp_ipsec_input(struct mbuf *, int, int); +void udp_ipsec_adjust_cksum(struct mbuf *, struct secasvar *, int, int); +int udp_ipsec_output(struct mbuf **, struct secasvar *); + int ipsec_chkreplay(uint32_t, struct secasvar *); int ipsec_updatereplay(uint32_t, struct secasvar *); int ipsec_updateid(struct secasvar *, uint64_t *, uint64_t *); Modified: projects/ipsec/sys/netipsec/ipsec_input.c ============================================================================== --- projects/ipsec/sys/netipsec/ipsec_input.c Sat Dec 10 13:30:18 2016 (r309807) +++ projects/ipsec/sys/netipsec/ipsec_input.c Sat Dec 10 14:20:51 2016 (r309808) @@ -122,11 +122,6 @@ ipsec_common_input(struct mbuf *m, int s struct secasvar *sav; uint32_t spi; int error; -#ifdef INET -#ifdef IPSEC_NAT_T - struct m_tag *tag; -#endif -#endif IPSEC_ISTAT(sproto, input); @@ -178,12 +173,6 @@ ipsec_common_input(struct mbuf *m, int s 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 @@ -336,7 +325,6 @@ ipsec4_common_input_cb(struct mbuf *m, s if (skip != 0) { /* * Fix IPv4 header - * XXXGL: do we need this entire block? */ if (m->m_len < skip && (m = m_pullup(m, skip)) == NULL) { DPRINTF(("%s: processing failed for SA %s/%08lx\n", @@ -355,6 +343,14 @@ ipsec4_common_input_cb(struct mbuf *m, s ip = mtod(m, struct ip *); } prot = ip->ip_p; + /* + * Check that we have NAT-T enabled and apply transport mode + * decapsulation NAT procedure (RFC3948). + * Do this before invoking into the PFIL. + */ + if (sav->natt != NULL && + (prot == IPPROTO_UDP || prot == IPPROTO_TCP)) + udp_ipsec_adjust_cksum(m, sav, prot, skip); IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET, IPSEC_ENC_BEFORE); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0) Modified: projects/ipsec/sys/netipsec/ipsec_output.c ============================================================================== --- projects/ipsec/sys/netipsec/ipsec_output.c Sat Dec 10 13:30:18 2016 (r309807) +++ projects/ipsec/sys/netipsec/ipsec_output.c Sat Dec 10 14:20:51 2016 (r309808) @@ -217,7 +217,9 @@ ipsec4_perform_request(struct mbuf *m, s } goto bad; } - + /* + * XXXAE: most likely ip_sum at this point is wrong. + */ IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET, IPSEC_ENC_BEFORE); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0) goto bad; @@ -614,69 +616,25 @@ ipsec_process_done(struct mbuf *m, struc key_freesp(&sp), sp = NULL; /* Release reference to SP */ /* + * Do UDP encapsulation if SA requires it. + */ + if (sav->natt != NULL) { + error = udp_ipsec_output(&m, sav); + if (error != 0) + goto bad; + } + /* * We're done with IPsec processing, transmit the packet using the * appropriate network protocol (IP or IPv6). */ switch (saidx->dst.sa.sa_family) { #ifdef INET case AF_INET: -#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) { - struct ip *ip = mtod(m, struct ip *); - const int hlen = (ip->ip_hl << 2); - 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.sa); - udp->uh_dport = key_portfromsaddr( - &sav->sah->saidx.dst.sa); - udp->uh_sum = 0; - udp->uh_ulen = htons(m->m_pkthdr.len - hlen); - ip->ip_len = htons(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 */ key_freesav(&sav); return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); #endif /* INET */ #ifdef INET6 case AF_INET6: - /* - * We don't need massage, IPv6 header fields are always in - * net endian. - */ key_freesav(&sav); return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); #endif /* INET6 */ Modified: projects/ipsec/sys/netipsec/key.c ============================================================================== --- projects/ipsec/sys/netipsec/key.c Sat Dec 10 13:30:18 2016 (r309807) +++ projects/ipsec/sys/netipsec/key.c Sat Dec 10 14:20:51 2016 (r309808) @@ -70,6 +70,7 @@ #include #include #include +#include #ifdef INET6 #include @@ -89,6 +90,7 @@ #endif #include +#include #include /* randomness */ @@ -468,6 +470,8 @@ do { \ (idx)->reqid = (r); \ bcopy((s), &(idx)->src, ((const struct sockaddr *)(s))->sa_len); \ bcopy((d), &(idx)->dst, ((const struct sockaddr *)(d))->sa_len); \ + key_porttosaddr(&(idx)->src.sa, 0); \ + key_porttosaddr(&(idx)->dst.sa, 0); \ } while (0) /* key statistics */ @@ -529,10 +533,8 @@ static struct mbuf *key_setsadbmsg(u_int static struct mbuf *key_setsadbsa(struct secasvar *); static struct mbuf *key_setsadbaddr(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 struct mbuf *key_setsadbxsa2(u_int8_t, u_int32_t, u_int32_t); static struct mbuf *key_setsadbxpolicy(u_int16_t, u_int8_t, u_int32_t, u_int32_t); @@ -894,13 +896,11 @@ key_allocsa(union sockaddr_union *dst, u { SAHTREE_RLOCK_TRACKER; struct secasvar *sav; - int chkport; IPSEC_ASSERT(proto == IPPROTO_ESP || proto == IPPROTO_AH || proto == IPPROTO_IPCOMP, ("unexpected security protocol %u", proto)); - chkport = 0; SAHTREE_RLOCK(); LIST_FOREACH(sav, SAVHASH_HASH(spi), spihash) { if (sav->spi == spi) @@ -911,21 +911,10 @@ key_allocsa(union sockaddr_union *dst, u * impossible to have SPI duplicates in the SAVHASH. */ if (sav != NULL) { -#ifdef IPSEC_NAT_T - /* - * Really only check ports when this is a NAT-T - * SA. Otherwise other lookups providing ports - * might suffer. - */ - chkport = (sav->natt_type != 0 && - dst->sa.sa_family == AF_INET && - dst->sa.sa_len == sizeof(struct sockaddr_in) && - dst->sin.sin_port != 0); -#endif if (sav->state != SADB_SASTATE_LARVAL && sav->sah->saidx.proto == proto && - key_sockaddrcmp(&dst->sa, &sav->sah->saidx.dst.sa, - chkport) == 0) + key_sockaddrcmp(&dst->sa, + &sav->sah->saidx.dst.sa, 0) == 0) SAV_ADDREF(sav); else sav = NULL; @@ -1718,11 +1707,6 @@ key_spdadd(struct socket *so, struct mbu 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. - */ - /* check the direciton */ switch (xpl0->sadb_x_policy_dir) { case IPSEC_DIR_INBOUND: @@ -1810,11 +1794,6 @@ key_spdadd(struct socket *so, struct mbu 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, @@ -1952,12 +1931,6 @@ key_spddelete(struct socket *so, struct ipseclog((LOG_DEBUG, "%s: invalid policy type.\n", __func__)); return key_senderror(so, m, EINVAL); } - - /* - * Note: do not parse SADB_X_EXT_NAT_T_* here: - * we are processing traffic endpoints. - */ - if (key_checksockaddrs((struct sockaddr *)(src0 + 1), (struct sockaddr *)(dst0 + 1)) != 0 || src0->sadb_address_proto != dst0->sadb_address_proto) { @@ -1992,11 +1965,6 @@ key_spddelete(struct socket *so, struct 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); @@ -2372,10 +2340,6 @@ key_setdumpsp(struct secpolicy *sp, u_in 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); @@ -2514,11 +2478,6 @@ key_spdexpire(struct secpolicy *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, @@ -2811,6 +2770,10 @@ key_cleansav(struct secasvar *sav) free(sav->key_enc, M_IPSEC_MISC); sav->key_enc = NULL; } + if (sav->natt != NULL) { + free(sav->natt, M_IPSEC_MISC); + sav->natt = NULL; + } if (sav->replay != NULL) { free(sav->replay, M_IPSEC_MISC); sav->replay = NULL; @@ -3016,10 +2979,6 @@ key_setsaval(struct secasvar *sav, const if (error != 0) goto fail; - error = key_setnatt(sav, mhp); - if (error != 0) - goto fail; - /* SA */ if (!SADB_CHECKHDR(mhp, SADB_EXT_SA)) { if (SADB_CHECKLEN(mhp, SADB_EXT_SA)) { @@ -3194,6 +3153,11 @@ key_setsaval(struct secasvar *sav, const goto fail; } + /* Handle NAT-T headers */ + error = key_setnatt(sav, mhp); + if (error != 0) + goto fail; + /* Initialize lifetime for CURRENT */ sav->firstused = 0; sav->created = time_second; @@ -3224,12 +3188,10 @@ key_setdumpsa(struct secasvar *sav, uint 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); @@ -3316,35 +3278,53 @@ key_setdumpsa(struct secasvar *sav, uint goto fail; break; -#ifdef IPSEC_NAT_T case SADB_X_EXT_NAT_T_TYPE: - m = key_setsadbxtype(sav->natt_type); + if (sav->natt == NULL) + continue; + m = key_setsadbxtype(UDP_ENCAP_ESPINUDP); if (!m) goto fail; break; case SADB_X_EXT_NAT_T_DPORT: - m = key_setsadbxport( - key_portfromsaddr(&sav->sah->saidx.dst.sa), + if (sav->natt == NULL) + continue; + m = key_setsadbxport(sav->natt->dport, 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.sa), + if (sav->natt == NULL) + continue; + m = key_setsadbxport(sav->natt->sport, SADB_X_EXT_NAT_T_SPORT); if (!m) goto fail; break; case SADB_X_EXT_NAT_T_OAI: + if (sav->natt == NULL || + (sav->natt->flags & IPSEC_NATT_F_OAI) == 0) + continue; + m = key_setsadbaddr(SADB_X_EXT_NAT_T_OAI, + &sav->natt->oai.sa, FULLMASK, IPSEC_ULPROTO_ANY); + if (!m) + goto fail; + break; case SADB_X_EXT_NAT_T_OAR: + if (sav->natt == NULL || + (sav->natt->flags & IPSEC_NATT_F_OAR) == 0) + continue; + m = key_setsadbaddr(SADB_X_EXT_NAT_T_OAR, + &sav->natt->oar.sa, FULLMASK, IPSEC_ULPROTO_ANY); + if (!m) + goto fail; + break; 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: @@ -3532,7 +3512,6 @@ key_setsadbxsa2(u_int8_t mode, u_int32_t return m; } -#ifdef IPSEC_NAT_T /* * Set a type in sadb_x_nat_t_type. */ @@ -3586,7 +3565,6 @@ key_setsadbxport(u_int16_t port, u_int16 return (m); } -#endif /* IPSEC_NAT_T */ /* * Get port from sockaddr. Port is in network byte order. @@ -3735,7 +3713,6 @@ static int key_cmpsaidx(const struct secasindex *saidx0, const struct secasindex *saidx1, int flag) { - int chkport = 0; /* sanity */ if (saidx0 == NULL && saidx1 == NULL) @@ -3752,19 +3729,21 @@ key_cmpsaidx(const struct secasindex *sa return 0; if (saidx0->reqid != saidx1->reqid) return 0; - if (bcmp(&saidx0->src, &saidx1->src, saidx0->src.sa.sa_len) != 0 || - bcmp(&saidx0->dst, &saidx1->dst, saidx0->dst.sa.sa_len) != 0) + if (bcmp(&saidx0->src, &saidx1->src, + saidx0->src.sa.sa_len) != 0 || + bcmp(&saidx0->dst, &saidx1->dst, + saidx0->dst.sa.sa_len) != 0) return 0; } else { /* CMP_MODE_REQID, CMP_REQID, CMP_HEAD */ - if (flag == CMP_MODE_REQID - ||flag == CMP_REQID) { + if (flag == CMP_MODE_REQID || flag == CMP_REQID) { /* * If reqid of SPD is non-zero, unique SA is required. * The result must be of same reqid in this case. */ - if (saidx1->reqid != 0 && saidx0->reqid != saidx1->reqid) + if (saidx1->reqid != 0 && + saidx0->reqid != saidx1->reqid) return 0; } @@ -3774,29 +3753,10 @@ key_cmpsaidx(const struct secasindex *sa return 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 native transport mode, as there - * is no port information available in the SP. - */ - if ((saidx1->mode == IPSEC_MODE_TUNNEL || - (saidx1->mode == IPSEC_MODE_TRANSPORT && - saidx1->proto == IPPROTO_ESP)) && - 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) { + if (key_sockaddrcmp(&saidx0->src.sa, &saidx1->src.sa, 0) != 0) return 0; - } - if (key_sockaddrcmp(&saidx0->dst.sa, &saidx1->dst.sa, chkport) != 0) { + if (key_sockaddrcmp(&saidx0->dst.sa, &saidx1->dst.sa, 0) != 0) return 0; - } } return 1; @@ -4543,12 +4503,6 @@ key_getspi(struct socket *so, struct mbu goto fail; } 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.sa, 0); - key_porttosaddr(&saidx.dst.sa, 0); /* SPI allocation */ spi = key_do_getnewspi( @@ -4874,13 +4828,6 @@ key_update(struct socket *so, struct mbu return key_senderror(so, m, error); } 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.sa, 0); - key_porttosaddr(&saidx.dst.sa, 0); - sav = key_getsavbyspi(sa0->sadb_sa_spi); if (sav == NULL) { ipseclog((LOG_DEBUG, "%s: no SA found for SPI %u\n", @@ -4898,10 +4845,7 @@ key_update(struct socket *so, struct mbu key_freesav(&sav); return key_senderror(so, m, EINVAL); } - /* - * XXXAE: saidx should match with SA. Use CMP_MODE_REQID since we - * didn't set ports for NAT-T yet and exactly match may fail. - */ + /* saidx should match with SA. */ if (key_cmpsaidx(&sav->sah->saidx, &saidx, CMP_MODE_REQID) == 0) { ipseclog((LOG_DEBUG, "%s: saidx mismatched for SPI %u", __func__, ntohl(sav->spi))); @@ -5088,17 +5032,11 @@ key_add(struct socket *so, struct mbuf * return key_senderror(so, m, error); } 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.sa, 0); - key_porttosaddr(&saidx.dst.sa, 0); - spi = sa0->sadb_sa_spi; /* - * XXX: For TCP-MD5 SAs we don't use SPI. - * Check the uniqueness using secasindex. + * For TCP-MD5 SAs we don't use SPI. Check the uniqueness using + * secasindex. + * XXXAE: IPComp seems also doesn't use SPI. */ if (proto == IPPROTO_TCP) { sav = key_getsav_tcpmd5(&saidx, &spi); @@ -5151,12 +5089,46 @@ key_add(struct socket *so, struct mbuf * } } +/* + * NAT-T support. + * IKEd may request the use ESP in UDP encapsulation when it detects the + * presence of NAT. It uses NAT-T extension headers for such SAs to specify + * parameters needed for encapsulation and decapsulation. These PF_KEY + * extension headers are not standardized, so this comment addresses our + * implementation. + * SADB_X_EXT_NAT_T_TYPE specifies type of encapsulation, we support only + * UDP_ENCAP_ESPINUDP as described in RFC3948. + * SADB_X_EXT_NAT_T_SPORT/DPORT specifies source and destination ports for + * UDP header. We use these ports in UDP encapsulation procedure, also we + * can check them in UDP decapsulation procedure. + * SADB_X_EXT_NAT_T_OA[IR] specifies original address of initiator or + * responder. These addresses can be used for transport mode to adjust + * checksum after decapsulation and decryption. Since original IP addresses + * used by peer usually different (we detected presence of NAT), TCP/UDP + * pseudo header checksum and IP header checksum was calculated using original + * addresses. After decapsulation and decryption we need to adjust checksum + * to have correct datagram. + * + * We expect presence of NAT-T extension headers only in SADB_ADD and + * SADB_UPDATE messages. We report NAT-T extension headers in replies + * to SADB_ADD, SADB_UPDATE, SADB_GET, and SADB_DUMP messages. + */ static int key_setnatt(struct secasvar *sav, const struct sadb_msghdr *mhp) { -#ifdef IPSEC_NAT_T - struct sadb_x_nat_t_port *sport, *dport; + struct sadb_x_nat_t_port *port; struct sadb_x_nat_t_type *type; + struct sadb_address *oai, *oar; + struct sockaddr *sa; + uint32_t addr; + uint16_t cksum; + + IPSEC_ASSERT(sav->natt == NULL, ("natt is already initialized")); + /* + * Ignore NAT-T headers if sproto isn't ESP. + */ + if (sav->sah->saidx.proto != IPPROTO_ESP) + return (0); if (!SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_TYPE) && !SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_SPORT) && @@ -5169,53 +5141,122 @@ key_setnatt(struct secasvar *sav, const __func__)); return (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]; - - sav->natt_type = type->sadb_x_nat_t_type_type; - key_porttosaddr(&sav->sah->saidx.src.sa, - sport->sadb_x_nat_t_port_port); - key_porttosaddr(&sav->sah->saidx.dst.sa, - dport->sadb_x_nat_t_port_port); } else return (0); - if (!SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_OAI) && - !SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_OAR)) { - if (SADB_CHECKLEN(mhp, SADB_X_EXT_NAT_T_OAI) || - SADB_CHECKLEN(mhp, SADB_X_EXT_NAT_T_OAR)) { + + type = (struct sadb_x_nat_t_type *)mhp->ext[SADB_X_EXT_NAT_T_TYPE]; + if (type->sadb_x_nat_t_type_type != UDP_ENCAP_ESPINUDP) { + ipseclog((LOG_DEBUG, "%s: unsupported NAT-T type %u.\n", + __func__, type->sadb_x_nat_t_type_type)); + return (EINVAL); + } + /* + * Allocate storage for NAT-T config. + * On error it will be released by key_cleansav(). + */ + sav->natt = malloc(sizeof(struct secnatt), M_IPSEC_MISC, + M_NOWAIT | M_ZERO); + if (sav->natt == NULL) { + PFKEYSTAT_INC(in_nomem); + ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); + return (ENOBUFS); + } + port = (struct sadb_x_nat_t_port *)mhp->ext[SADB_X_EXT_NAT_T_SPORT]; + if (port->sadb_x_nat_t_port_port == 0) { + ipseclog((LOG_DEBUG, "%s: invalid NAT-T sport specified.\n", + __func__)); + return (EINVAL); + } + sav->natt->sport = port->sadb_x_nat_t_port_port; + port = (struct sadb_x_nat_t_port *)mhp->ext[SADB_X_EXT_NAT_T_DPORT]; + if (port->sadb_x_nat_t_port_port == 0) { + ipseclog((LOG_DEBUG, "%s: invalid NAT-T dport specified.\n", + __func__)); + return (EINVAL); + } + sav->natt->dport = port->sadb_x_nat_t_port_port; + + /* + * SADB_X_EXT_NAT_T_OAI and SADB_X_EXT_NAT_T_OAR are optional + * and needed only for transport mode IPsec. + * Usually NAT translates only one address, but it is possible, *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***