From owner-freebsd-net@FreeBSD.ORG Thu Sep 14 13:30:39 2006 Return-Path: X-Original-To: freebsd-net@freebsd.org Delivered-To: freebsd-net@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 67F0F16A40F for ; Thu, 14 Sep 2006 13:30:39 +0000 (UTC) (envelope-from lab@gta.com) Received: from gta.com (gta-edge-199-20.gta.com [199.120.225.20]) by mx1.FreeBSD.org (Postfix) with SMTP id C8CF443D53 for ; Thu, 14 Sep 2006 13:30:35 +0000 (GMT) (envelope-from lab@gta.com) Received: (qmail 85724 invoked by uid 1000); 14 Sep 2006 13:30:34 -0000 Date: Thu, 14 Sep 2006 09:30:34 -0400 From: Larry Baird To: freebsd-net@freebsd.org Message-ID: <20060914093034.A83805@gta.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="NzB8fVQJ5HfG6fxh" Content-Disposition: inline User-Agent: Mutt/1.2.5i Subject: FAST_IPSEC NAT-T support X-BeenThere: freebsd-net@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Networking and TCP/IP with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 14 Sep 2006 13:30:39 -0000 --NzB8fVQJ5HfG6fxh Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Please find attached two patches for adding FAST_IPSEC NAT-T support to FreeBSD 6.x. The patch "freebsd6-fastipsec-natt.diff" is dependent upon Yvan's IPSEC NAT-T patch "freebsd6-natt.diff" which can be found at http://ipsec-tools.cvs.sourceforge.net/ipsec-tools/htdocs/. The second patch "freebsd6-ipsec-fastipsec-natt.diff" is a cumulative patch combining both patches together. -- ------------------------------------------------------------------------ Larry Baird | http://www.gta.com Global Technology Associates, Inc. | Orlando, FL Email: lab@gta.com | TEL 407-380-0220, FAX 407-380-6080 --NzB8fVQJ5HfG6fxh Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="freebsd6-fastipsec-natt.diff" Index: netinet/udp_usrreq.c =================================================================== --- netinet/udp_usrreq.c (.../6.x-IPSEC-NATT) (revision 8180) +++ netinet/udp_usrreq.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -1170,7 +1170,7 @@ m_tag_prepend(n, tag); #ifdef FAST_IPSEC - ipsec4_common_input(n, iphdrlen); + ipsec4_common_input(n, iphdrlen, ip->ip_p); #else /* IPSEC */ esp4_input(n, iphdrlen); #endif Index: netipsec/ipsec.c =================================================================== --- netipsec/ipsec.c (.../6.x-IPSEC-NATT) (revision 8180) +++ netipsec/ipsec.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -1808,15 +1808,15 @@ /* Return a printable string for the IPv4 address. */ static char * -inet_ntoa4(struct in_addr ina) +inet_ntoa4(const struct sockaddr_in *sin) { - static char buf[4][4 * sizeof "123" + 4]; - unsigned char *ucp = (unsigned char *) &ina; + static char buf[4][4 * sizeof "123" + 4 + 10]; + const unsigned char *ucp = (const unsigned char *)&sin->sin_addr; static int i = 3; i = (i + 1) % 4; - sprintf(buf[i], "%d.%d.%d.%d", ucp[0] & 0xff, ucp[1] & 0xff, - ucp[2] & 0xff, ucp[3] & 0xff); + sprintf(buf[i], "%d.%d.%d.%d[%u]", ucp[0] & 0xff, ucp[1] & 0xff, + ucp[2] & 0xff, ucp[3] & 0xff, ntohs(sin->sin_port)); return (buf[i]); } @@ -1827,7 +1827,7 @@ switch (sa->sa.sa_family) { #if INET case AF_INET: - return inet_ntoa4(sa->sin.sin_addr); + return inet_ntoa4(&sa->sin); #endif /* INET */ #if INET6 Index: netipsec/keydb.h =================================================================== --- netipsec/keydb.h (.../6.x-IPSEC-NATT) (revision 8180) +++ netipsec/keydb.h (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -117,6 +117,12 @@ struct secashead *sah; /* back pointer to the secashead */ /* + * NAT-Traversal + */ + u_int16_t natt_type; + u_int16_t esp_frag; + + /* * NB: Fields with a tdb_ prefix are part of the "glue" used * to interface to the OpenBSD crypto support. This was done * to distinguish this code from the mainline KAME code. Index: netipsec/ipsec_input.c =================================================================== --- netipsec/ipsec_input.c (.../6.x-IPSEC-NATT) (revision 8180) +++ netipsec/ipsec_input.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -110,6 +110,9 @@ struct secasvar *sav; u_int32_t spi; int error; +#ifdef IPSEC_NAT_T + struct m_tag *tag; +#endif IPSEC_ISTAT(sproto, espstat.esps_input, ahstat.ahs_input, ipcompstat.ipcomps_input); @@ -160,6 +163,13 @@ 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 */ + if ((tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL)) + != NULL) { + dst_address.sin.sin_port = ((u_int16_t *)(tag + 1))[1]; + } +#endif /* IPSEC_NAT_T */ break; #endif /* INET */ #ifdef INET6 @@ -179,7 +189,7 @@ } /* NB: only pass dst since key_allocsa follows RFC2401 */ - sav = KEY_ALLOCSA(&dst_address, sproto, spi); + sav = KEY_ALLOCSA( &dst_address, sproto, spi); if (sav == NULL) { DPRINTF(("%s: no key association found for SA %s/%08lx/%u\n", __func__, ipsec_address(&dst_address), Index: netipsec/ipsec_output.c =================================================================== --- netipsec/ipsec_output.c (.../6.x-IPSEC-NATT) (revision 8180) +++ netipsec/ipsec_output.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -81,6 +81,10 @@ #include +#ifdef IPSEC_NAT_T +#include +#endif + int ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr) { @@ -173,6 +177,51 @@ 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 != 0) { + int size = sizeof(struct udphdr); +#ifdef _IP_VHL + int hlen = IP_VHL_HL(ip->ip_vhl); +#else + int hlen = (ip->ip_hl << 2); +#endif + int off; + struct mbuf *mi; + struct udphdr *udp; + + if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) + size += sizeof(u_int64_t); + + if ( (mi = m_makespace(m, hlen, size, &off)) == NULL ) { + 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 *marker = (u_int64_t *)(udp + 1); + *marker = 0; + } + } +#endif /* IPSEC_NAT_T */ + return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); #endif /* INET */ #ifdef INET6 Index: netipsec/key.c =================================================================== --- netipsec/key.c (.../6.x-IPSEC-NATT) (revision 8180) +++ netipsec/key.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -210,6 +210,11 @@ 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_OA */ + sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */ }; static const int maxsize[] = { sizeof(struct sadb_msg), /* SADB_EXT_RESERVED */ @@ -232,6 +237,11 @@ 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_OA */ + sizeof(struct sadb_x_nat_t_frag), /* SADB_X_EXT_NAT_T_FRAG */ }; static int ipsec_esp_keymin = 256; @@ -393,6 +403,10 @@ const struct sadb_msghdr *)); static int key_spddump __P((struct socket *, struct mbuf *, const struct sadb_msghdr *)); +#ifdef IPSEC_NAT_T +static int key_nat_map(struct socket *, struct mbuf *, + const struct sadb_msghdr *); +#endif static struct mbuf *key_setdumpsp __P((struct secpolicy *, u_int8_t, u_int32_t, u_int32_t)); static u_int key_getspreqmsglen __P((struct secpolicy *)); @@ -418,6 +432,13 @@ 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 __P((u_int16_t, u_int16_t)); +static struct mbuf *key_setsadbxtype __P((u_int16_t)); +#endif +static void key_porttosaddr __P((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)); @@ -1042,12 +1063,20 @@ struct secasvar *sav; u_int stateidx, arraysize, state; const u_int *saorder_state_valid; + int chkport = 0; 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 + if (dst->sa.sa_family == AF_INET && + dst->sa.sa_len == sizeof(struct sockaddr_in) && + dst->sin.sin_port != 0) + chkport = 1; +#endif + /* * searching SAD. * XXX: to be checked internal IP header somewhere. Also when @@ -1079,11 +1108,11 @@ 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; @@ -1847,6 +1876,52 @@ return key_senderror(so, m, error); } +#ifndef IPSEC_NAT_T + for (isr = newsp->req; isr; isr = isr->next) { + struct sockaddr *sa; + + /* + * port spec is not permitted for tunnel mode + */ + if (isr->saidx.mode == IPSEC_MODE_TUNNEL && src0 && dst0) { + sa = (struct sockaddr *)(src0 + 1); + switch (sa->sa_family) { + case AF_INET: + if (((struct sockaddr_in *)sa)->sin_port) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + break; + case AF_INET6: + if (((struct sockaddr_in6 *)sa)->sin6_port) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + break; + default: + break; + } + sa = (struct sockaddr *)(dst0 + 1); + switch (sa->sa_family) { + case AF_INET: + if (((struct sockaddr_in *)sa)->sin_port) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + break; + case AF_INET6: + if (((struct sockaddr_in6 *)sa)->sin6_port) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + break; + default: + break; + } + } + } +#endif /* !IPSEC_NAT_T */ + if ((newsp->id = key_getnewspid()) == 0) { _key_delsp(newsp); return key_senderror(so, m, ENOBUFS); @@ -2355,6 +2430,71 @@ return key_sendup_mbuf(so, m, KEY_SENDUP_ALL); } +#ifdef IPSEC_NAT_T +/* + * SADB_X_NAT_T_NEW_MAPPING + */ +static int +key_nat_map(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; +{ + struct sadb_x_nat_t_type *type; + struct sadb_x_nat_t_port *sport; + struct sadb_x_nat_t_port *dport; + struct sadb_address *addr; + struct sadb_x_nat_t_frag *frag; + + /* sanity check */ + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) + panic("key_nat_map: NULL pointer is passed."); + + 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) { + ipseclog((LOG_DEBUG, "key_nat_map: invalid message.\n")); + return key_senderror(so, m, EINVAL); + } + 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, "key_nat_map: invalid message.\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) { + ipseclog((LOG_DEBUG, "key_nat_map: invalid message\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) { + ipseclog((LOG_DEBUG, "key_nat_map: invalid message\n")); + 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]; + addr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OA]; + frag = (struct sadb_x_nat_t_frag *) mhp->ext[SADB_X_EXT_NAT_T_FRAG]; + + printf("sadb_nat_map: type %d, sport = %d, dport = %d\n", + type->sadb_x_nat_t_type_type, + sport->sadb_x_nat_t_port_port, + dport->sadb_x_nat_t_port_port); + + /* + * XXX handle that, it should also contain a SA, or anything + * that enable to update the SA information. + */ + + return 0; +} +#endif /* IPSEC_NAT_T */ + /* * SADB_SPDDUMP processing * receive @@ -2984,6 +3124,10 @@ sav->lft_c = NULL; sav->lft_h = NULL; sav->lft_s = NULL; +#ifdef IPSEC_NAT_T + sav->natt_type = 0; + sav->esp_frag = 0; +#endif sav->tdb_xform = NULL; /* transform */ sav->tdb_encalgxform = NULL; /* encoding algorithm */ sav->tdb_authalgxform = NULL; /* authentication algorithm */ @@ -3294,6 +3438,11 @@ 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_OA, + SADB_X_EXT_NAT_T_FRAG, +#endif }; m = key_setsadbmsg(type, 0, satype, seq, pid, sav->refcnt); @@ -3370,6 +3519,31 @@ p = sav->lft_s; break; +#ifdef IPSEC_NAT_T + case SADB_X_EXT_NAT_T_TYPE: + if ((m = key_setsadbxtype(sav->natt_type)) == NULL) + goto fail; + break; + + case SADB_X_EXT_NAT_T_DPORT: + if ((m = key_setsadbxport(KEY_PORTFROMSADDR + (&sav->sah->saidx.dst), + SADB_X_EXT_NAT_T_DPORT)) == NULL) + goto fail; + break; + + case SADB_X_EXT_NAT_T_SPORT: + if ((m = key_setsadbxport(KEY_PORTFROMSADDR + (&sav->sah->saidx.src), + SADB_X_EXT_NAT_T_SPORT)) == NULL) + goto fail; + break; + + case SADB_X_EXT_NAT_T_OA: + case SADB_X_EXT_NAT_T_FRAG: + continue; +#endif + case SADB_EXT_ADDRESS_PROXY: case SADB_EXT_IDENTITY_SRC: case SADB_EXT_IDENTITY_DST: @@ -3588,7 +3762,134 @@ return m; } +#ifdef IPSEC_NAT_T /* + * set a type in sadb_x_nat_t_type + */ +static struct mbuf * +key_setsadbxtype(type) + 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. port is in network order + */ +static struct mbuf * +key_setsadbxport(port, type) + 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 order + */ +u_int16_t +key_portfromsaddr(saddr) + struct sockaddr *saddr; +{ + u_int16_t port; + + switch (saddr->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)saddr; + + port = sin->sin_port; + break; + } +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr; + + port = sin6->sin6_port; + break; + } +#endif + default: + printf("key_portfromsaddr: unexpected address family\n"); + port = 0; + break; + } + + return port; +} +#endif /* IPSEC_NAT_T */ + +/* + * Set port is struct sockaddr. port is in network order + */ +static void +key_porttosaddr(saddr, port) + struct sockaddr *saddr; + u_int16_t port; +{ + switch (saddr->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)saddr; + + sin->sin_port = port; + break; + } +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr; + + sin6->sin6_port = port; + break; + } +#endif + default: + printf("key_porttosaddr: unexpected address family %d\n", + saddr->sa_family); + break; + } + + return; +} + +/* * set data into sadb_x_policy */ static struct mbuf * @@ -3738,6 +4039,8 @@ const struct secasindex *saidx1, int flag) { + int chkport = 0; + /* sanity */ if (saidx0 == NULL && saidx1 == NULL) return 1; @@ -3761,13 +4064,30 @@ /* CMP_MODE_REQID, CMP_REQID, CMP_HEAD */ if (flag == CMP_MODE_REQID ||flag == CMP_REQID) { +#ifdef IPSEC_NAT_T /* + * If NAT-T is enabled, check ports for tunnel mode. + * Don't do it for transport mode, as there is no + * port information available in the SP. + * XXX also don't check ports if they are set to zero in the SPD: + * This means we bave a non-generated SPD, which can't know UDP ports. + */ + if (saidx1->mode == IPSEC_MODE_TUNNEL && + ((const struct sockaddr_in *)(&saidx1->src))->sin_port && + ((const struct sockaddr_in *)(&saidx1->dst))->sin_port ) + chkport = 1; +#endif /* IPSEC_NAT_T */ + /* * 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) return 0; } +#ifdef IPSEC_NAT_T + else + chkport = 1; +#endif if (flag == CMP_MODE_REQID) { if (saidx0->mode != IPSEC_MODE_ANY @@ -3775,10 +4095,10 @@ return 0; } - if (key_sockaddrcmp(&saidx0->src.sa, &saidx1->src.sa, 0) != 0) { + 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; } } @@ -4398,13 +4718,17 @@ if (((struct sockaddr *)(src0 + 1))->sa_len != sizeof(struct sockaddr_in)) return key_senderror(so, m, EINVAL); +#ifndef IPSEC_NAT_T ((struct sockaddr_in *)(src0 + 1))->sin_port = 0; +#endif break; case AF_INET6: if (((struct sockaddr *)(src0 + 1))->sa_len != sizeof(struct sockaddr_in6)) return key_senderror(so, m, EINVAL); +#ifndef IPSEC_NAT_T ((struct sockaddr_in6 *)(src0 + 1))->sin6_port = 0; +#endif break; default: ; /*???*/ @@ -4414,13 +4738,17 @@ if (((struct sockaddr *)(dst0 + 1))->sa_len != sizeof(struct sockaddr_in)) return key_senderror(so, m, EINVAL); +#ifndef IPSEC_NAT_T ((struct sockaddr_in *)(dst0 + 1))->sin_port = 0; +#endif break; case AF_INET6: if (((struct sockaddr *)(dst0 + 1))->sa_len != sizeof(struct sockaddr_in6)) return key_senderror(so, m, EINVAL); +#ifndef IPSEC_NAT_T ((struct sockaddr_in6 *)(dst0 + 1))->sin6_port = 0; +#endif break; default: ; /*???*/ @@ -4429,6 +4757,12 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); + /* If not using NAT-T, make sure port numbers are set to zero. */ +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* SPI allocation */ spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE], &saidx); @@ -4684,6 +5018,12 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); + /* If not using NAT-T, make sure if port number is zero. */ +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA header */ if ((sah = key_getsah(&saidx)) == NULL) { ipseclog((LOG_DEBUG, "%s: no SA index found.\n", __func__)); @@ -4750,6 +5090,68 @@ return key_senderror(so, m, 0); } +#ifdef IPSEC_NAT_T + /* + * Handle NAT-T info if present + */ + if (mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) + printf("update: NAT-T OA present\n"); + + 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; + struct sadb_x_nat_t_port *dport; + struct sadb_address *addr; + struct sadb_x_nat_t_frag *frag; + + 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, "key_update: " + "invalid message.\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) { + ipseclog((LOG_DEBUG, "key_update: invalid message\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) { + ipseclog((LOG_DEBUG, "key_update: invalid message\n")); + 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]; + addr = (struct sadb_address *) + mhp->ext[SADB_X_EXT_NAT_T_OA]; + frag = (struct sadb_x_nat_t_frag *) + mhp->ext[SADB_X_EXT_NAT_T_FRAG]; + + if (type) + sav->natt_type = type->sadb_x_nat_t_type_type; + if (sport) + KEY_PORTTOSADDR(&sav->sah->saidx.src, + sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&sav->sah->saidx.dst, + dport->sadb_x_nat_t_port_port); + if (frag) + sav->esp_frag = frag->sadb_x_nat_t_frag_fraglen; + else + sav->esp_frag = IP_MAXPACKET; + } +#endif /* IPSEC_NAT_T */ + { struct mbuf *n; @@ -4882,6 +5284,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA header */ if ((newsah = key_getsah(&saidx)) == NULL) { /* create a new SA header */ @@ -4918,7 +5325,69 @@ return key_senderror(so, m, error); } +#ifdef IPSEC_NAT_T /* + * Handle NAT-T info if present + */ + if (mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) + printf("add: NAT-T OA present\n"); + + 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; + struct sadb_x_nat_t_port *dport; + struct sadb_address *addr; + struct sadb_x_nat_t_frag *frag; + + 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, "key_add: " + "invalid message.\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) { + ipseclog((LOG_DEBUG, "key_add: invalid message\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) { + ipseclog((LOG_DEBUG, "key_update: invalid message\n")); + 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]; + addr = (struct sadb_address *) + mhp->ext[SADB_X_EXT_NAT_T_OA]; + frag = (struct sadb_x_nat_t_frag *) + mhp->ext[SADB_X_EXT_NAT_T_FRAG]; + + if (type) + newsav->natt_type = type->sadb_x_nat_t_type_type; + if (sport) + KEY_PORTTOSADDR(&newsav->sah->saidx.src, + sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&newsav->sah->saidx.dst, + dport->sadb_x_nat_t_port_port); + if (frag) + newsav->esp_frag = frag->sadb_x_nat_t_frag_fraglen; + else + newsav->esp_frag = IP_MAXPACKET; + } +#endif + + /* * don't call key_freesav() here, as we would like to keep the SA * in the database on success. */ @@ -5118,6 +5587,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA header */ SAHTREE_LOCK(); LIST_FOREACH(sah, &sahtree, chain) { @@ -5187,6 +5661,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + SAHTREE_LOCK(); LIST_FOREACH(sah, &sahtree, chain) { if (sah->state == SADB_SASTATE_DEAD) @@ -5301,6 +5780,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA header */ SAHTREE_LOCK(); LIST_FOREACH(sah, &sahtree, chain) { @@ -5988,6 +6472,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA index */ SAHTREE_LOCK(); LIST_FOREACH(sah, &sahtree, chain) { @@ -6596,6 +7085,11 @@ key_spdadd, /* SADB_X_SPDSETIDX */ NULL, /* SADB_X_SPDEXPIRE */ key_spddelete2, /* SADB_X_SPDDELETE2 */ +#ifdef IPSEC_NAT_T + key_nat_map, /* SADB_X_NAT_T_NEW_MAPPING */ +#else + NULL, +#endif }; /* @@ -6932,6 +7426,13 @@ 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_OA: + case SADB_X_EXT_NAT_T_FRAG: +#endif /* duplicate check */ /* * XXX Are there duplication payloads of either Index: netipsec/key.h =================================================================== --- netipsec/key.h (.../6.x-IPSEC-NATT) (revision 8180) +++ netipsec/key.h (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -99,6 +99,10 @@ 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 __P((struct sockaddr *)); +#define KEY_PORTFROMSADDR(saddr) key_portfromsaddr((struct sockaddr *)(saddr)) +#endif #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_IPSEC_SA); --NzB8fVQJ5HfG6fxh Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="freebsd6-ipsec-fastipsec-natt.diff" Index: conf/options =================================================================== --- conf/options (.../6.x) (revision 8180) +++ conf/options (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -352,6 +352,7 @@ INET6 opt_inet6.h IPSEC opt_ipsec.h IPSEC_ESP opt_ipsec.h +IPSEC_NAT_T opt_ipsec.h IPSEC_DEBUG opt_ipsec.h IPSEC_FILTERGIF opt_ipsec.h FAST_IPSEC opt_ipsec.h Index: netinet/udp_var.h =================================================================== --- netinet/udp_var.h (.../6.x) (revision 8180) +++ netinet/udp_var.h (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -100,6 +100,7 @@ extern int log_in_vain; void udp_ctlinput(int, struct sockaddr *, void *); +int udp_ctloutput(struct socket *, struct sockopt *sopt); void udp_init(void); void udp_input(struct mbuf *, int); Index: netinet/udp.h =================================================================== --- netinet/udp.h (.../6.x) (revision 8180) +++ netinet/udp.h (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -44,4 +44,17 @@ u_short uh_sum; /* udp checksum */ }; +/* socket options for UDP */ +#define UDP_ENCAP 100 + +/* 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 encapsulation port */ +#define UDP_ENCAP_ESPINUDP_PORT 500 + +/* Maximum UDP fragment size for ESP over UDP */ +#define UDP_ENCAP_ESPINUDP_MAXFRAGLEN 552 + #endif Index: netinet/in_pcb.h =================================================================== --- netinet/in_pcb.h (.../6.x) (revision 8180) +++ netinet/in_pcb.h (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -298,6 +298,11 @@ #define IN6P_RFC2292 0x40000000 /* used RFC2292 API on the socket */ #define IN6P_MTU 0x80000000 /* receive path MTU */ +/* XXX should move to an UDP control block */ +#define INP_ESPINUDP 0x1000 /* ESP over UDP for NAT-T */ +#define INP_ESPINUDP_NON_IKE 0x2000 /* ESP over UDP for NAT-T */ +#define INP_ESPINUDP_ALL (INP_ESPINUDP|INP_ESPINUDP_NON_IKE) + #define INP_CONTROLOPTS (INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR|\ INP_RECVIF|INP_RECVTTL|\ IN6P_PKTINFO|IN6P_HOPLIMIT|IN6P_HOPOPTS|\ Index: netinet/ip_output.c =================================================================== --- netinet/ip_output.c (.../6.x) (revision 8180) +++ netinet/ip_output.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -58,6 +58,10 @@ #include #include +#ifdef IPSEC_NAT_T +#include +#endif + #include static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "internet multicast options"); Index: netinet/in_proto.c =================================================================== --- netinet/in_proto.c (.../6.x) (revision 8180) +++ netinet/in_proto.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -122,7 +122,7 @@ .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, .pr_usrreqs = &udp_usrreqs }, Index: netinet/udp_usrreq.c =================================================================== --- netinet/udp_usrreq.c (.../6.x) (revision 8180) +++ netinet/udp_usrreq.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -78,10 +78,12 @@ #ifdef FAST_IPSEC #include +#include #endif /*FAST_IPSEC*/ #ifdef IPSEC #include +#include #endif /*IPSEC*/ #include @@ -128,6 +130,11 @@ static int udp_detach(struct socket *so); static int udp_output(struct inpcb *, struct mbuf *, struct sockaddr *, struct mbuf *, struct thread *); +#ifdef INET +#ifdef IPSEC_NAT_T +static int udp4_espinudp (struct mbuf *, int, struct sockaddr *, struct socket *); +#endif +#endif static void udp_zone_change(void *tag) @@ -464,6 +471,41 @@ return; } #endif /*IPSEC || FAST_IPSEC*/ +#ifdef IPSEC_NAT_T + /* Handle ESP over UDP */ + if (last->inp_flags & INP_ESPINUDP_ALL) { + struct sockaddr_in src; + struct sockaddr *sa = (struct sockaddr *)(&src); + size_t minlen; + + bzero(&src, sizeof(src)); + src.sin_family = AF_INET; + src.sin_len = sizeof(struct sockaddr_in); + bcopy(&ip->ip_src, &src.sin_addr, sizeof(src.sin_addr)); + src.sin_port = udp_in->sin_port; + + /* + * Collapse the mbuf chain if the first mbuf is too short + * The longest case is: UDP + non ESP marker + ESP + */ + minlen = off + sizeof(struct udphdr) + sizeof(u_int64_t) + sizeof(struct esp); + if (minlen > n->m_pkthdr.len) + minlen = n->m_pkthdr.len; + + if ((n = m_pullup(n, minlen)) == NULL) { + printf("udp_append: m_pullup failed\n"); + m_freem(n); + return; + } + + if (udp4_espinudp(n, off, sa, last->inp_socket) != 0) { + m_freem(n); + return; + } + + /* Normal UDP processing will take place */ + } +#endif #ifdef MAC if (mac_check_inpcb_deliver(last, n) != 0) { m_freem(n); @@ -702,6 +744,82 @@ CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_PRISON, 0, 0, udp_getcred, "S,xucred", "Get the xucred of a UDP connection"); + +int +udp_ctloutput(so, sopt) + struct socket *so; + struct sockopt *sopt; +{ + int error, optval, s; + struct inpcb *inp; + int family; + + error = 0; + family = so->so_proto->pr_domain->dom_family; + + s = splnet(); + if (sopt->sopt_level != IPPROTO_UDP) { +#ifdef INET6 + if (INP_CHECK_SOCKAF(so, AF_INET6)) + error = ip6_ctloutput(so, sopt); + else +#endif /* INET6 */ + error = ip_ctloutput(so, sopt); + splx(s); + return (error); + } + inp = sotoinpcb(so); + + switch (sopt->sopt_dir) { + case SOPT_SET: + switch (sopt->sopt_name) { + case UDP_ENCAP: + error = sooptcopyin(sopt, &optval, sizeof optval, + sizeof optval); + if (error) + break; + + switch(optval){ +#ifdef IPSEC_NAT_T + case 0: + inp->inp_flags &= ~INP_ESPINUDP_ALL; + break; + + case UDP_ENCAP_ESPINUDP: + inp->inp_flags |= INP_ESPINUDP; + break; + + case UDP_ENCAP_ESPINUDP_NON_IKE: + inp->inp_flags |= INP_ESPINUDP_NON_IKE; + break; +#endif + + default: + error = EINVAL; + goto end; + break; + } + break; + + default: + error = ENOPROTOOPT; + goto end; + break; + } + break; + + default: + error = EINVAL; + goto end; + break; + } + +end: + splx(s); + return error; +} + + static int udp_output(inp, m, addr, control, td) register struct inpcb *inp; @@ -923,6 +1041,146 @@ return (error); } +#ifdef INET +#ifdef IPSEC_NAT_T +/* + * Returns: + * 1 if the packet was processed + * 0 if normal UDP processing should take place + */ +static int +udp4_espinudp(m, off, src, so) + struct mbuf *m; + int off; + struct sockaddr *src; + struct socket *so; +{ + size_t len; + caddr_t data; + struct inpcb *inp; + size_t skip = 0; + size_t minlen; + size_t iphdrlen; + struct m_tag *tag; + struct ip *ip; + struct udphdr *udphdr; + u_int16_t sport, dport; + struct mbuf *n; + + /* + * Cannot collapse the mbuf chain here, must have been done in + * calling function + * The longest case is: UDP + non ESP marker + ESP + */ + minlen = off + sizeof(u_int64_t) + sizeof(struct esp); + if (minlen > m->m_pkthdr.len) + minlen = m->m_pkthdr.len; + + if (m->m_len < minlen) + return 0; + + len = m->m_len - off; + data = mtod(m, caddr_t) + off; + inp = sotoinpcb(so); + + /* Ignore keepalive packets */ + if ((len == 1) && (data[0] == '\xff')) { + return 1; + } + + /* + * Check that the payload is long enough to hold + * an ESP header and compute the length of encapsulation + * header to remove + */ + if (inp->inp_flags & INP_ESPINUDP) { + u_int32_t *st = (u_int32_t *)data; + + if ((len <= sizeof(struct esp)) || (*st == 0)) + return 0; /* Normal UDP processing */ + + skip = sizeof(struct udphdr); + } + + if (inp->inp_flags & INP_ESPINUDP_NON_IKE) { + u_int64_t *st = (u_int64_t *)data; + + if ((len <= sizeof(u_int64_t) + sizeof(struct esp)) + || (*st != 0)) + return 0; /* Normal UDP processing */ + + skip = sizeof(struct udphdr) + sizeof(u_int64_t); + } + + /* + * Get the UDP ports. They are handled in network + * order everywhere in IPSEC_NAT_T code. + */ + udphdr = (struct udphdr *)(data - skip); + sport = udphdr->uh_sport; + dport = udphdr->uh_dport; + + /* + * Remove the UDP header (and possibly the non ESP marker) + * IP header lendth is iphdrlen + * Before: + * <--- off ---> + * +----+------+-----+ + * | IP | UDP | ESP | + * +----+------+-----+ + * <-skip-> + * After: + * +----+-----+ + * | IP | ESP | + * +----+-----+ + * <-skip-> + */ + iphdrlen = off - sizeof(struct udphdr); + ovbcopy(mtod(m, caddr_t), mtod(m, caddr_t) + skip, iphdrlen); + m_adj(m, skip); + + ip = mtod(m, struct ip *); + ip->ip_len = htons(ntohs(ip->ip_len) - skip); + ip->ip_p = IPPROTO_ESP; + + /* + * Copy the mbuf to avoid multiple free, as both + * esp4_input (which we call) and udp_input (which + * called us) free the mbuf. + */ + if ((n = m_dup(m, M_DONTWAIT)) == NULL) { + printf("udp4_espinudp: m_dup failed\n"); + return 0; + } + + /* + * Add a PACKET_TAG_IPSEC_NAT_T_PORT tag to remember + * the source UDP port. This is required if we want + * to select the right SPD for multiple hosts behind + * same NAT + */ + if ((tag = m_tag_get(PACKET_TAG_IPSEC_NAT_T_PORTS, + sizeof(sport) + sizeof(dport), M_DONTWAIT)) == NULL) { + printf("udp4_espinudp: m_tag_get failed\n"); + m_freem(n); + return 0; + } + ((u_int16_t *)(tag + 1))[0] = sport; + ((u_int16_t *)(tag + 1))[1] = dport; + m_tag_prepend(n, tag); + +#ifdef FAST_IPSEC + ipsec4_common_input(n, iphdrlen, ip->ip_p); +#else /* IPSEC */ + esp4_input(n, iphdrlen); +#endif + + /* We handled it, it shoudln't be handled by UDP */ + return 1; +} +#endif +#endif + u_long udp_sendspace = 9216; /* really max datagram size */ /* 40 1K datagrams */ SYSCTL_INT(_net_inet_udp, UDPCTL_MAXDGRAM, maxdgram, CTLFLAG_RW, Index: net/pfkeyv2.h =================================================================== --- net/pfkeyv2.h (.../6.x) (revision 8180) +++ net/pfkeyv2.h (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -75,7 +75,8 @@ #define SADB_X_SPDSETIDX 20 #define SADB_X_SPDEXPIRE 21 #define SADB_X_SPDDELETE2 22 /* by policy id */ -#define SADB_MAX 22 +#define SADB_X_NAT_T_NEW_MAPPING 23 +#define SADB_MAX 23 struct sadb_msg { u_int8_t sadb_msg_version; @@ -255,6 +256,34 @@ */ }; +/* NAT traversal type, see RFC 3948 */ +/* 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 +304,12 @@ #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 +#define SADB_X_EXT_NAT_T_FRAG 24 +#define SADB_EXT_MAX 24 #define SADB_SATYPE_UNSPEC 0 #define SADB_SATYPE_AH 2 Index: netinet6/ipcomp_input.c =================================================================== --- netinet6/ipcomp_input.c (.../6.x) (revision 8180) +++ netinet6/ipcomp_input.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -36,6 +36,7 @@ #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_ipsec.h" #include #include @@ -100,6 +101,11 @@ int error; size_t newlen, olen; struct secasvar *sav = NULL; + u_int16_t sport = 0; + u_int16_t dport = 0; +#ifdef IPSEC_NAT_T + struct m_tag *tag = NULL; +#endif if (m->m_pkthdr.len < off + sizeof(struct ipcomp)) { ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed " @@ -108,6 +114,14 @@ goto fail; } +#ifdef IPSEC_NAT_T + /* find the source port for NAT-T */ + if ((tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL)) != NULL) { + sport = ((u_int16_t *)(tag + 1))[0]; + dport = ((u_int16_t *)(tag + 1))[1]; + } +#endif + md = m_pulldown(m, off, sizeof(*ipcomp), NULL); if (!md) { m = NULL; /* already freed */ @@ -129,7 +143,7 @@ if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) { sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, - (caddr_t)&ip->ip_dst, IPPROTO_IPCOMP, htonl(cpi)); + (caddr_t)&ip->ip_dst, IPPROTO_IPCOMP, htonl(cpi), sport, dport); if (sav != NULL && (sav->state == SADB_SASTATE_MATURE || sav->state == SADB_SASTATE_DYING)) { @@ -273,7 +287,7 @@ if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) { sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src, - (caddr_t)&ip6->ip6_dst, IPPROTO_IPCOMP, htonl(cpi)); + (caddr_t)&ip6->ip6_dst, IPPROTO_IPCOMP, htonl(cpi), 0, 0); if (sav != NULL && (sav->state == SADB_SASTATE_MATURE || sav->state == SADB_SASTATE_DYING)) { Index: netinet6/esp_output.c =================================================================== --- netinet6/esp_output.c (.../6.x) (revision 8180) +++ netinet6/esp_output.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -32,6 +32,7 @@ #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_ipsec.h" /* * RFC1827/2406 Encapsulated Security Payload. @@ -56,6 +57,10 @@ #include #include +#ifdef IPSEC_NAT_T +#include +#endif + #ifdef INET6 #include #include @@ -139,6 +144,17 @@ hdrsiz = sizeof(struct newesp) + ivlen + 9 + authlen; } +#ifdef IPSEC_NAT_T + /* + * If NAT-T is enabled, add the space for UDP encapsulation + */ + if (sav->natt_type != 0) { + hdrsiz += sizeof(struct udphdr); + if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) + hdrsiz += sizeof(u_int64_t); + } +#endif + return hdrsiz; estimate: @@ -149,8 +165,15 @@ * 9 = (maximum padding length without random padding length) * + (Pad Length field) + (Next Header field). * 16 = maximum ICV we support. + * sizeof(u_int64_t) = non IKE marker (NAT-T) + * sizeof(struct udphdr) = UDP encapsulation (NAT-T) */ +#ifdef IPSEC_NAT_T + return sizeof(struct newesp) + esp_max_ivlen() + 9 + 16 + + sizeof(u_int64_t) + sizeof(struct udphdr); +#else return sizeof(struct newesp) + esp_max_ivlen() + 9 + 16; +#endif } /* @@ -196,6 +219,9 @@ size_t extendsiz; int error = 0; struct ipsecstat *stat; +#ifdef IPSEC_NAT_T + struct udphdr *udp = NULL; +#endif switch (af) { #ifdef INET @@ -334,10 +360,25 @@ espoff = m->m_pkthdr.len - plen; +#ifdef IPSEC_NAT_T + if (sav->natt_type != 0) { + esphlen += sizeof(struct udphdr); + espoff += sizeof(struct udphdr); + + if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) { + /* NON-IKE marker */ + esphlen += sizeof(u_int64_t); + espoff += sizeof(u_int64_t); + } + } +#endif + /* * grow the mbuf to accomodate ESP header. * before: IP ... payload - * after: IP ... ESP IV payload + * after (without NAT-T): IP ... ESP IV payload + * after (with older NAT-T): IP ... UDP non-IKE-marker ESP IV payload + * after (with newer NAT-T): IP ... UDP ESP IV payload */ if (M_LEADINGSPACE(md) < esphlen || (md->m_flags & M_EXT) != 0) { MGET(n, M_DONTWAIT, MT_DATA); @@ -358,6 +399,21 @@ esp = mtod(md, struct esp *); } +#ifdef IPSEC_NAT_T + if (sav->natt_type != 0) { + udp = (struct udphdr *)esp; + esp = (struct esp *)(udp + 1); + + if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) { + u_int64_t *data = (u_int64_t *)esp; + + *data = 0; /* NON-IKE marker */ + esp = (struct esp *)(data + 1); + } + } +#endif + + nxt = *nexthdrp; *nexthdrp = IPPROTO_ESP; switch (af) { @@ -523,6 +579,27 @@ break; } +#ifdef IPSEC_NAT_T + if (sav->natt_type != 0) { + *nexthdrp = IPPROTO_UDP; + + /* + * Create the UDP encapsulation header for NAT-T + * uh_len is set later, when the size is known. + */ + 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; + } else { + *nexthdrp = IPPROTO_ESP; + } +#endif + /* initialize esp trailer. */ esptail = (struct esptail *) (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail)); @@ -666,6 +743,18 @@ } } +#ifdef IPSEC_NAT_T + if (sav->natt_type != 0) { + struct ip *ip; + ip = mtod(m, struct ip *); +#ifdef _IP_VHL + udp->uh_ulen = htons(ntohs(ip->ip_len) - (IP_VHL_HL(ip->ip_vhl) << 2)); +#else + udp->uh_ulen = htons(ntohs(ip->ip_len) - (ip->ip_hl << 2)); +#endif + } +#endif + noantireplay: if (!m) { ipseclog((LOG_ERR, Index: netinet6/esp_input.c =================================================================== --- netinet6/esp_input.c (.../6.x) (revision 8180) +++ netinet6/esp_input.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -36,6 +36,7 @@ #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_ipsec.h" #include #include @@ -116,6 +117,11 @@ int ivlen; size_t hlen; size_t esplen; + u_int16_t sport = 0; + u_int16_t dport = 0; +#ifdef IPSEC_NAT_T + struct m_tag *tag = NULL; +#endif /* sanity check for alignment. */ if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) { @@ -135,6 +141,14 @@ } } +#ifdef IPSEC_NAT_T + /* find the source port for NAT_T */ + if ((tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL)) != NULL) { + sport = ((u_int16_t *)(tag + 1))[0]; + dport = ((u_int16_t *)(tag + 1))[1]; + } +#endif + ip = mtod(m, struct ip *); esp = (struct esp *)(((u_int8_t *)ip) + off); #ifdef _IP_VHL @@ -148,7 +162,7 @@ if ((sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst, - IPPROTO_ESP, spi)) == 0) { + IPPROTO_ESP, spi, sport, dport)) == 0) { ipseclog((LOG_WARNING, "IPv4 ESP input: no key association found for spi %u\n", (u_int32_t)ntohl(spi))); @@ -509,7 +523,7 @@ if ((sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst, - IPPROTO_ESP, spi)) == 0) { + IPPROTO_ESP, spi, 0, 0)) == 0) { ipseclog((LOG_WARNING, "IPv6 ESP input: no key association found for spi %u\n", (u_int32_t)ntohl(spi))); @@ -951,7 +965,7 @@ sav = key_allocsa(AF_INET6, (caddr_t)&sa6_src->sin6_addr, (caddr_t)&sa6_dst->sin6_addr, - IPPROTO_ESP, espp->esp_spi); + IPPROTO_ESP, espp->esp_spi, 0, 0); if (sav) { if (sav->state == SADB_SASTATE_MATURE || sav->state == SADB_SASTATE_DYING) Index: netinet6/ah_input.c =================================================================== --- netinet6/ah_input.c (.../6.x) (revision 8180) +++ netinet6/ah_input.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -36,6 +36,7 @@ #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_ipsec.h" #include #include @@ -113,6 +114,11 @@ u_int16_t nxt; size_t hlen; size_t stripsiz = 0; + u_int16_t sport = 0; + u_int16_t dport = 0; +#ifdef IPSEC_NAT_T + struct m_tag *tag = NULL; +#endif #ifndef PULLDOWN_TEST if (m->m_len < off + sizeof(struct newah)) { @@ -125,6 +131,14 @@ } } +#ifdef IPSEC_NAT_T + /* find the source port for NAT-T */ + if ((tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL)) != NULL) { + sport = ((u_int16_t *)(tag + 1))[0]; + dport = ((u_int16_t *)(tag + 1))[1]; + } +#endif + ip = mtod(m, struct ip *); ah = (struct ah *)(((caddr_t)ip) + off); #else @@ -149,7 +163,7 @@ if ((sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst, - IPPROTO_AH, spi)) == 0) { + IPPROTO_AH, spi, sport, dport)) == 0) { ipseclog((LOG_WARNING, "IPv4 AH input: no key association found for spi %u\n", (u_int32_t)ntohl(spi))); @@ -599,7 +613,7 @@ if ((sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst, - IPPROTO_AH, spi)) == 0) { + IPPROTO_AH, spi, 0, 0)) == 0) { ipseclog((LOG_WARNING, "IPv6 AH input: no key association found for spi %u\n", (u_int32_t)ntohl(spi))); @@ -998,7 +1012,7 @@ sav = key_allocsa(AF_INET6, (caddr_t)&sa6_src->sin6_addr, (caddr_t)&sa6_dst->sin6_addr, - IPPROTO_AH, ahp->ah_spi); + IPPROTO_AH, ahp->ah_spi, 0, 0); if (sav) { if (sav->state == SADB_SASTATE_MATURE || sav->state == SADB_SASTATE_DYING) Index: netkey/keydb.h =================================================================== --- netkey/keydb.h (.../6.x) (revision 8180) +++ netkey/keydb.h (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -114,6 +114,10 @@ pid_t pid; /* message's pid */ struct secashead *sah; /* back pointer to the secashead */ + /* NAT-Traversal + */ + u_int16_t natt_type; + u_int16_t esp_frag; u_int32_t id; /* SA id */ }; Index: netkey/key.c =================================================================== --- netkey/key.c (.../6.x) (revision 8180) +++ netkey/key.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -194,6 +194,11 @@ 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_OA */ + sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */ }; static const int maxsize[] = { sizeof(struct sadb_msg), /* SADB_EXT_RESERVED */ @@ -216,6 +221,11 @@ 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_OA */ + sizeof(struct sadb_x_nat_t_frag), /* SADB_X_EXT_NAT_T_FRAG */ }; static int ipsec_esp_keymin = 256; @@ -384,6 +394,10 @@ const struct sadb_msghdr *); static int key_spddump(struct socket *, struct mbuf *, const struct sadb_msghdr *); +#ifdef IPSEC_NAT_T +static int key_nat_map(struct socket *, struct mbuf *, + const struct sadb_msghdr *); +#endif static struct mbuf *key_setdumpsp(struct secpolicy *, u_int8_t, u_int32_t, u_int32_t); static u_int key_getspreqmsglen(struct secpolicy *); @@ -406,6 +420,13 @@ static struct mbuf *key_setsadbsa(struct secasvar *); static struct mbuf *key_setsadbaddr(u_int16_t, struct sockaddr *, u_int8_t, u_int16_t); +#ifdef IPSEC_NAT_T +static struct mbuf *key_setsadbxport __P((u_int16_t, u_int16_t)); +static struct mbuf *key_setsadbxtype __P((u_int16_t)); +#endif +static void key_porttosaddr __P((struct sockaddr *, u_int16_t)); +#define KEY_PORTTOSADDR(saddr, port) \ + key_porttosaddr((struct sockaddr *)(saddr), (port)) #if 0 static struct mbuf *key_setsadbident(u_int16_t, u_int16_t, caddr_t, int, u_int64_t); @@ -927,10 +948,11 @@ * keep source address in IPsec SA. We see a tricky situation here. */ struct secasvar * -key_allocsa(family, src, dst, proto, spi) +key_allocsa(family, src, dst, proto, spi, sport, dport) u_int family, proto; caddr_t src, dst; u_int32_t spi; + u_int16_t sport, dport; { struct secasvar *sav, *match; u_int stateidx, state, tmpidx, matchidx; @@ -941,11 +963,17 @@ int s; const u_int *saorder_state_valid; int arraysize; + int chkport = 0; /* sanity check */ if (src == NULL || dst == NULL) panic("key_allocsa: NULL pointer is passed."); +#ifdef IPSEC_NAT_T + if ((sport != 0) && (dport != 0)) + chkport = 1; +#endif + /* * when both systems employ similar strategy to use a SA. * the search order is important even in the inbound case. @@ -1004,8 +1032,11 @@ switch (family) { case AF_INET: bcopy(src, &sin.sin_addr, sizeof(sin.sin_addr)); +#ifdef IPSEC_NAT_T + sin.sin_port = sport; +#endif if (key_sockaddrcmp((struct sockaddr*)&sin, - (struct sockaddr *)&sav->sah->saidx.src, 0) != 0) + (struct sockaddr *)&sav->sah->saidx.src, chkport) != 0) continue; break; @@ -1013,10 +1044,13 @@ case AF_INET6: bcopy(src, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); sin6.sin6_scope_id = 0; +#ifdef IPSEC_NAT_T + sin6.sin6_port = sport; +#endif if (sa6_recoverscope(&sin6)) continue; if (key_sockaddrcmp((struct sockaddr *)&sin6, - (struct sockaddr *)&sav->sah->saidx.src, 0) != 0) + (struct sockaddr *)&sav->sah->saidx.src, chkport) != 0) continue; break; #endif @@ -1032,8 +1066,11 @@ switch (family) { case AF_INET: bcopy(dst, &sin.sin_addr, sizeof(sin.sin_addr)); +#ifdef IPSEC_NAT_T + sin.sin_port = dport; +#endif if (key_sockaddrcmp((struct sockaddr*)&sin, - (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0) + (struct sockaddr *)&sav->sah->saidx.dst, chkport) != 0) continue; break; @@ -1041,10 +1078,13 @@ case AF_INET6: bcopy(dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); sin6.sin6_scope_id = 0; +#ifdef IPSEC_NAT_T + sin6.sin6_port = dport; +#endif if (sa6_recoverscope(&sin6)) continue; if (key_sockaddrcmp((struct sockaddr *)&sin6, - (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0) + (struct sockaddr *)&sav->sah->saidx.dst, chkport) != 0) continue; break; #endif @@ -1873,6 +1913,7 @@ } } +#ifndef IPSEC_NAT_T for (isr = newsp->req; isr; isr = isr->next) { struct sockaddr *sa; @@ -1916,6 +1957,7 @@ } } } +#endif /* !IPSEC_NAT_T */ /* * bark if we have different address family on tunnel address @@ -2475,6 +2517,72 @@ return 0; } +#ifdef IPSEC_NAT_T +/* + * SADB_X_NAT_T_NEW_MAPPING + */ +static int +key_nat_map(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; +{ + struct sadb_x_nat_t_type *type; + struct sadb_x_nat_t_port *sport; + struct sadb_x_nat_t_port *dport; + struct sadb_address *addr; + struct sadb_x_nat_t_frag *frag; + + /* sanity check */ + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) + panic("key_nat_map: NULL pointer is passed."); + + 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) { + ipseclog((LOG_DEBUG, "key_nat_map: invalid message.\n")); + return key_senderror(so, m, EINVAL); + } + 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, "key_nat_map: invalid message.\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) { + ipseclog((LOG_DEBUG, "key_nat_map: invalid message\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) { + ipseclog((LOG_DEBUG, "key_nat_map: invalid message\n")); + 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]; + addr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OA]; + frag = (struct sadb_x_nat_t_frag *) mhp->ext[SADB_X_EXT_NAT_T_FRAG]; + + printf("sadb_nat_map: type %d, sport = %d, dport = %d\n", + type->sadb_x_nat_t_type_type, + sport->sadb_x_nat_t_port_port, + dport->sadb_x_nat_t_port_port); + + /* + * XXX handle that, it should also contain a SA, or anything + * that enable to update the SA information. + */ + + return 0; +} +#endif /* IPSEC_NAT_T */ + + static struct mbuf * key_setdumpsp(sp, type, seq, pid) struct secpolicy *sp; @@ -3025,6 +3133,10 @@ sav->lft_c = NULL; sav->lft_h = NULL; sav->lft_s = NULL; +#ifdef IPSEC_NAT_T + sav->natt_type = 0; + sav->esp_frag = 0; +#endif /* SA */ if (mhp->ext[SADB_EXT_SA] != NULL) { @@ -3491,6 +3603,11 @@ 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_OA, + SADB_X_EXT_NAT_T_FRAG, +#endif }; m = key_setsadbmsg(type, 0, satype, seq, pid, sav->refcnt); @@ -3567,6 +3684,31 @@ p = sav->lft_s; break; +#ifdef IPSEC_NAT_T + case SADB_X_EXT_NAT_T_TYPE: + if ((m = key_setsadbxtype(sav->natt_type)) == NULL) + goto fail; + break; + + case SADB_X_EXT_NAT_T_DPORT: + if ((m = key_setsadbxport(KEY_PORTFROMSADDR + (&sav->sah->saidx.dst), + SADB_X_EXT_NAT_T_DPORT)) == NULL) + goto fail; + break; + + case SADB_X_EXT_NAT_T_SPORT: + if ((m = key_setsadbxport(KEY_PORTFROMSADDR + (&sav->sah->saidx.src), + SADB_X_EXT_NAT_T_SPORT)) == NULL) + goto fail; + break; + + case SADB_X_EXT_NAT_T_OA: + case SADB_X_EXT_NAT_T_FRAG: + continue; +#endif + case SADB_EXT_ADDRESS_PROXY: case SADB_EXT_IDENTITY_SRC: case SADB_EXT_IDENTITY_DST: @@ -3825,7 +3967,134 @@ return m; } +#ifdef IPSEC_NAT_T /* + * set a type in sadb_x_nat_t_type + */ +static struct mbuf * +key_setsadbxtype(type) + 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. port is in network order + */ +static struct mbuf * +key_setsadbxport(port, type) + 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 order + */ +u_int16_t +key_portfromsaddr(saddr) + struct sockaddr *saddr; +{ + u_int16_t port; + + switch (saddr->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)saddr; + + port = sin->sin_port; + break; + } +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr; + + port = sin6->sin6_port; + break; + } +#endif + default: + printf("key_portfromsaddr: unexpected address family\n"); + port = 0; + break; + } + + return port; +} +#endif /* IPSEC_NAT_T */ + +/* + * Set port is struct sockaddr. port is in network order + */ +static void +key_porttosaddr(saddr, port) + struct sockaddr *saddr; + u_int16_t port; +{ + switch (saddr->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)saddr; + + sin->sin_port = port; + break; + } +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr; + + sin6->sin6_port = port; + break; + } +#endif + default: + printf("key_porttosaddr: unexpected address family %d\n", + saddr->sa_family); + break; + } + + return; +} + +/* * set data into sadb_lifetime */ static struct mbuf * @@ -4015,6 +4284,8 @@ struct secasindex *saidx0, *saidx1; int flag; { + int chkport = 0; + /* sanity */ if (saidx0 == NULL && saidx1 == NULL) return 1; @@ -4037,13 +4308,30 @@ /* CMP_MODE_REQID, CMP_HEAD */ if (flag == CMP_MODE_REQID) { +#ifdef IPSEC_NAT_T /* + * If NAT-T is enabled, check ports for tunnel mode. + * Don't do it for transport mode, as there is no + * port information available in the SP. + * XXX also don't check ports if they are set to zero in the SPD: + * This means we bave a non-generated SPD, which can't know UDP ports. + */ + if (saidx1->mode == IPSEC_MODE_TUNNEL && + satosin(&saidx1->src)->sin_port && + satosin(&saidx1->dst)->sin_port ) + chkport = 1; +#endif + /* * 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) return 0; } +#ifdef IPSEC_NAT_T + else + chkport = 1; +#endif if (flag == CMP_MODE_REQID) { if (saidx0->mode != IPSEC_MODE_ANY && @@ -4052,11 +4340,11 @@ } if (key_sockaddrcmp((struct sockaddr *)&saidx0->src, - (struct sockaddr *)&saidx1->src, 0) != 0) { + (struct sockaddr *)&saidx1->src, chkport) != 0) { return 0; } if (key_sockaddrcmp((struct sockaddr *)&saidx0->dst, - (struct sockaddr *)&saidx1->dst, 0) != 0) { + (struct sockaddr *)&saidx1->dst, chkport) != 0) { return 0; } } @@ -4704,19 +4992,23 @@ return key_senderror(so, m, EINVAL); } - /* make sure if port number is zero. */ + /* make sure if port number is zero if NAT-T support is NOT compiled. */ switch (((struct sockaddr *)(src0 + 1))->sa_family) { case AF_INET: if (((struct sockaddr *)(src0 + 1))->sa_len != sizeof(struct sockaddr_in)) return key_senderror(so, m, EINVAL); +#ifndef IPSEC_NAT_T ((struct sockaddr_in *)(src0 + 1))->sin_port = 0; +#endif break; case AF_INET6: if (((struct sockaddr *)(src0 + 1))->sa_len != sizeof(struct sockaddr_in6)) return key_senderror(so, m, EINVAL); +#ifndef IPSEC_NAT_T ((struct sockaddr_in6 *)(src0 + 1))->sin6_port = 0; +#endif break; default: ; /*???*/ @@ -4726,13 +5018,17 @@ if (((struct sockaddr *)(dst0 + 1))->sa_len != sizeof(struct sockaddr_in)) return key_senderror(so, m, EINVAL); +#ifndef IPSEC_NAT_T ((struct sockaddr_in *)(dst0 + 1))->sin_port = 0; +#endif break; case AF_INET6: if (((struct sockaddr *)(dst0 + 1))->sa_len != sizeof(struct sockaddr_in6)) return key_senderror(so, m, EINVAL); +#ifndef IPSEC_NAT_T ((struct sockaddr_in6 *)(dst0 + 1))->sin6_port = 0; +#endif break; default: ; /*???*/ @@ -4741,6 +5037,12 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); + /* If not using NAT-T, make sure port numbers are set to zero. */ +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* SPI allocation */ spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE], &saidx); @@ -4994,6 +5296,12 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); + /* If not using NAT-T, make sure if port number is zero. */ +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA header */ if ((sah = key_getsah(&saidx)) == NULL) { ipseclog((LOG_DEBUG, "key_update: no SA index found.\n")); @@ -5060,6 +5368,68 @@ return key_senderror(so, m, error); } +#ifdef IPSEC_NAT_T + /* + * Handle NAT-T info if present + */ + if (mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) + printf("update: NAT-T OA present\n"); + + 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; + struct sadb_x_nat_t_port *dport; + struct sadb_address *addr; + struct sadb_x_nat_t_frag *frag; + + 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, "key_update: " + "invalid message.\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) { + ipseclog((LOG_DEBUG, "key_update: invalid message\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) { + ipseclog((LOG_DEBUG, "key_update: invalid message\n")); + 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]; + addr = (struct sadb_address *) + mhp->ext[SADB_X_EXT_NAT_T_OA]; + frag = (struct sadb_x_nat_t_frag *) + mhp->ext[SADB_X_EXT_NAT_T_FRAG]; + + if (type) + sav->natt_type = type->sadb_x_nat_t_type_type; + if (sport) + KEY_PORTTOSADDR(&sav->sah->saidx.src, + sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&sav->sah->saidx.dst, + dport->sadb_x_nat_t_port_port); + if (frag) + sav->esp_frag = frag->sadb_x_nat_t_frag_fraglen; + else + sav->esp_frag = IP_MAXPACKET; + } +#endif /* IPSEC_NAT_T */ + { struct mbuf *n; @@ -5189,6 +5559,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA header */ if ((newsah = key_getsah(&saidx)) == NULL) { /* create a new SA header */ @@ -5222,7 +5597,69 @@ return key_senderror(so, m, error); } +#ifdef IPSEC_NAT_T /* + * Handle NAT-T info if present + */ + if (mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) + printf("add: NAT-T OA present\n"); + + 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; + struct sadb_x_nat_t_port *dport; + struct sadb_address *addr; + struct sadb_x_nat_t_frag *frag; + + 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, "key_add: " + "invalid message.\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) { + ipseclog((LOG_DEBUG, "key_add: invalid message\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) { + ipseclog((LOG_DEBUG, "key_update: invalid message\n")); + 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]; + addr = (struct sadb_address *) + mhp->ext[SADB_X_EXT_NAT_T_OA]; + frag = (struct sadb_x_nat_t_frag *) + mhp->ext[SADB_X_EXT_NAT_T_FRAG]; + + if (type) + newsav->natt_type = type->sadb_x_nat_t_type_type; + if (sport) + KEY_PORTTOSADDR(&newsav->sah->saidx.src, + sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&newsav->sah->saidx.dst, + dport->sadb_x_nat_t_port_port); + if (frag) + newsav->esp_frag = frag->sadb_x_nat_t_frag_fraglen; + else + newsav->esp_frag = IP_MAXPACKET; + } +#endif + + /* * don't call key_freesav() here, as we would like to keep the SA * in the database on success. */ @@ -5416,6 +5853,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA header */ LIST_FOREACH(sah, &sahtree, chain) { if (sah->state == SADB_SASTATE_DEAD) @@ -5483,6 +5925,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + LIST_FOREACH(sah, &sahtree, chain) { if (sah->state == SADB_SASTATE_DEAD) continue; @@ -5592,6 +6039,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA header */ LIST_FOREACH(sah, &sahtree, chain) { if (sah->state == SADB_SASTATE_DEAD) @@ -6272,6 +6724,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA index */ LIST_FOREACH(sah, &sahtree, chain) { if (sah->state == SADB_SASTATE_DEAD) @@ -6875,6 +7332,11 @@ key_spdadd, /* SADB_X_SPDSETIDX */ NULL, /* SADB_X_SPDEXPIRE */ key_spddelete2, /* SADB_X_SPDDELETE2 */ +#ifdef IPSEC_NAT_T + key_nat_map, /* SADB_X_NAT_T_NEW_MAPPING */ +#else + NULL, +#endif }; /* @@ -7227,6 +7689,13 @@ 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_OA: + case SADB_X_EXT_NAT_T_FRAG: +#endif /* duplicate check */ /* * XXX Are there duplication payloads of either Index: netkey/key.h =================================================================== --- netkey/key.h (.../6.x) (revision 8180) +++ netkey/key.h (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -58,7 +58,8 @@ struct sockaddr *, struct sockaddr *, struct sockaddr *); extern int key_checkrequest (struct ipsecrequest *isr, struct secasindex *); -extern struct secasvar *key_allocsa(u_int, caddr_t, caddr_t, u_int, u_int32_t); +extern struct secasvar *key_allocsa(u_int, caddr_t, caddr_t, u_int, u_int32_t, + u_int16_t, u_int16_t); extern void key_freesp(struct secpolicy *); extern void key_freesav(struct secasvar *); extern struct secpolicy *key_newsp(u_int32_t); @@ -78,6 +79,10 @@ extern void key_sa_recordxfer(struct secasvar *, struct mbuf *); extern void key_sa_routechange(struct sockaddr *); extern void key_sa_stir_iv(struct secasvar *); +#ifdef IPSEC_NAT_T +u_int16_t key_portfromsaddr __P((struct sockaddr *)); +#define KEY_PORTFROMSADDR(saddr) key_portfromsaddr((struct sockaddr *)(saddr)) +#endif /* to keep compatibility with FAST_IPSEC */ #define KEY_ALLOCSA(dst, proto, spi) \ Index: netipsec/ipsec.c =================================================================== --- netipsec/ipsec.c (.../6.x) (revision 8180) +++ netipsec/ipsec.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -1808,15 +1808,15 @@ /* Return a printable string for the IPv4 address. */ static char * -inet_ntoa4(struct in_addr ina) +inet_ntoa4(const struct sockaddr_in *sin) { - static char buf[4][4 * sizeof "123" + 4]; - unsigned char *ucp = (unsigned char *) &ina; + static char buf[4][4 * sizeof "123" + 4 + 10]; + const unsigned char *ucp = (const unsigned char *)&sin->sin_addr; static int i = 3; i = (i + 1) % 4; - sprintf(buf[i], "%d.%d.%d.%d", ucp[0] & 0xff, ucp[1] & 0xff, - ucp[2] & 0xff, ucp[3] & 0xff); + sprintf(buf[i], "%d.%d.%d.%d[%u]", ucp[0] & 0xff, ucp[1] & 0xff, + ucp[2] & 0xff, ucp[3] & 0xff, ntohs(sin->sin_port)); return (buf[i]); } @@ -1827,7 +1827,7 @@ switch (sa->sa.sa_family) { #if INET case AF_INET: - return inet_ntoa4(sa->sin.sin_addr); + return inet_ntoa4(&sa->sin); #endif /* INET */ #if INET6 Index: netipsec/keydb.h =================================================================== --- netipsec/keydb.h (.../6.x) (revision 8180) +++ netipsec/keydb.h (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -117,6 +117,12 @@ struct secashead *sah; /* back pointer to the secashead */ /* + * NAT-Traversal + */ + u_int16_t natt_type; + u_int16_t esp_frag; + + /* * NB: Fields with a tdb_ prefix are part of the "glue" used * to interface to the OpenBSD crypto support. This was done * to distinguish this code from the mainline KAME code. Index: netipsec/ipsec_input.c =================================================================== --- netipsec/ipsec_input.c (.../6.x) (revision 8180) +++ netipsec/ipsec_input.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -110,6 +110,9 @@ struct secasvar *sav; u_int32_t spi; int error; +#ifdef IPSEC_NAT_T + struct m_tag *tag; +#endif IPSEC_ISTAT(sproto, espstat.esps_input, ahstat.ahs_input, ipcompstat.ipcomps_input); @@ -160,6 +163,13 @@ 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 */ + if ((tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL)) + != NULL) { + dst_address.sin.sin_port = ((u_int16_t *)(tag + 1))[1]; + } +#endif /* IPSEC_NAT_T */ break; #endif /* INET */ #ifdef INET6 @@ -179,7 +189,7 @@ } /* NB: only pass dst since key_allocsa follows RFC2401 */ - sav = KEY_ALLOCSA(&dst_address, sproto, spi); + sav = KEY_ALLOCSA( &dst_address, sproto, spi); if (sav == NULL) { DPRINTF(("%s: no key association found for SA %s/%08lx/%u\n", __func__, ipsec_address(&dst_address), Index: netipsec/ipsec_output.c =================================================================== --- netipsec/ipsec_output.c (.../6.x) (revision 8180) +++ netipsec/ipsec_output.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -81,6 +81,10 @@ #include +#ifdef IPSEC_NAT_T +#include +#endif + int ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr) { @@ -173,6 +177,51 @@ 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 != 0) { + int size = sizeof(struct udphdr); +#ifdef _IP_VHL + int hlen = IP_VHL_HL(ip->ip_vhl); +#else + int hlen = (ip->ip_hl << 2); +#endif + int off; + struct mbuf *mi; + struct udphdr *udp; + + if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) + size += sizeof(u_int64_t); + + if ( (mi = m_makespace(m, hlen, size, &off)) == NULL ) { + 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 *marker = (u_int64_t *)(udp + 1); + *marker = 0; + } + } +#endif /* IPSEC_NAT_T */ + return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); #endif /* INET */ #ifdef INET6 Index: netipsec/key.c =================================================================== --- netipsec/key.c (.../6.x) (revision 8180) +++ netipsec/key.c (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -210,6 +210,11 @@ 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_OA */ + sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */ }; static const int maxsize[] = { sizeof(struct sadb_msg), /* SADB_EXT_RESERVED */ @@ -232,6 +237,11 @@ 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_OA */ + sizeof(struct sadb_x_nat_t_frag), /* SADB_X_EXT_NAT_T_FRAG */ }; static int ipsec_esp_keymin = 256; @@ -393,6 +403,10 @@ const struct sadb_msghdr *)); static int key_spddump __P((struct socket *, struct mbuf *, const struct sadb_msghdr *)); +#ifdef IPSEC_NAT_T +static int key_nat_map(struct socket *, struct mbuf *, + const struct sadb_msghdr *); +#endif static struct mbuf *key_setdumpsp __P((struct secpolicy *, u_int8_t, u_int32_t, u_int32_t)); static u_int key_getspreqmsglen __P((struct secpolicy *)); @@ -418,6 +432,13 @@ 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 __P((u_int16_t, u_int16_t)); +static struct mbuf *key_setsadbxtype __P((u_int16_t)); +#endif +static void key_porttosaddr __P((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)); @@ -1042,12 +1063,20 @@ struct secasvar *sav; u_int stateidx, arraysize, state; const u_int *saorder_state_valid; + int chkport = 0; 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 + if (dst->sa.sa_family == AF_INET && + dst->sa.sa_len == sizeof(struct sockaddr_in) && + dst->sin.sin_port != 0) + chkport = 1; +#endif + /* * searching SAD. * XXX: to be checked internal IP header somewhere. Also when @@ -1079,11 +1108,11 @@ 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; @@ -1847,6 +1876,52 @@ return key_senderror(so, m, error); } +#ifndef IPSEC_NAT_T + for (isr = newsp->req; isr; isr = isr->next) { + struct sockaddr *sa; + + /* + * port spec is not permitted for tunnel mode + */ + if (isr->saidx.mode == IPSEC_MODE_TUNNEL && src0 && dst0) { + sa = (struct sockaddr *)(src0 + 1); + switch (sa->sa_family) { + case AF_INET: + if (((struct sockaddr_in *)sa)->sin_port) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + break; + case AF_INET6: + if (((struct sockaddr_in6 *)sa)->sin6_port) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + break; + default: + break; + } + sa = (struct sockaddr *)(dst0 + 1); + switch (sa->sa_family) { + case AF_INET: + if (((struct sockaddr_in *)sa)->sin_port) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + break; + case AF_INET6: + if (((struct sockaddr_in6 *)sa)->sin6_port) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + break; + default: + break; + } + } + } +#endif /* !IPSEC_NAT_T */ + if ((newsp->id = key_getnewspid()) == 0) { _key_delsp(newsp); return key_senderror(so, m, ENOBUFS); @@ -2355,6 +2430,71 @@ return key_sendup_mbuf(so, m, KEY_SENDUP_ALL); } +#ifdef IPSEC_NAT_T +/* + * SADB_X_NAT_T_NEW_MAPPING + */ +static int +key_nat_map(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; +{ + struct sadb_x_nat_t_type *type; + struct sadb_x_nat_t_port *sport; + struct sadb_x_nat_t_port *dport; + struct sadb_address *addr; + struct sadb_x_nat_t_frag *frag; + + /* sanity check */ + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) + panic("key_nat_map: NULL pointer is passed."); + + 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) { + ipseclog((LOG_DEBUG, "key_nat_map: invalid message.\n")); + return key_senderror(so, m, EINVAL); + } + 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, "key_nat_map: invalid message.\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) { + ipseclog((LOG_DEBUG, "key_nat_map: invalid message\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) { + ipseclog((LOG_DEBUG, "key_nat_map: invalid message\n")); + 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]; + addr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OA]; + frag = (struct sadb_x_nat_t_frag *) mhp->ext[SADB_X_EXT_NAT_T_FRAG]; + + printf("sadb_nat_map: type %d, sport = %d, dport = %d\n", + type->sadb_x_nat_t_type_type, + sport->sadb_x_nat_t_port_port, + dport->sadb_x_nat_t_port_port); + + /* + * XXX handle that, it should also contain a SA, or anything + * that enable to update the SA information. + */ + + return 0; +} +#endif /* IPSEC_NAT_T */ + /* * SADB_SPDDUMP processing * receive @@ -2984,6 +3124,10 @@ sav->lft_c = NULL; sav->lft_h = NULL; sav->lft_s = NULL; +#ifdef IPSEC_NAT_T + sav->natt_type = 0; + sav->esp_frag = 0; +#endif sav->tdb_xform = NULL; /* transform */ sav->tdb_encalgxform = NULL; /* encoding algorithm */ sav->tdb_authalgxform = NULL; /* authentication algorithm */ @@ -3294,6 +3438,11 @@ 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_OA, + SADB_X_EXT_NAT_T_FRAG, +#endif }; m = key_setsadbmsg(type, 0, satype, seq, pid, sav->refcnt); @@ -3370,6 +3519,31 @@ p = sav->lft_s; break; +#ifdef IPSEC_NAT_T + case SADB_X_EXT_NAT_T_TYPE: + if ((m = key_setsadbxtype(sav->natt_type)) == NULL) + goto fail; + break; + + case SADB_X_EXT_NAT_T_DPORT: + if ((m = key_setsadbxport(KEY_PORTFROMSADDR + (&sav->sah->saidx.dst), + SADB_X_EXT_NAT_T_DPORT)) == NULL) + goto fail; + break; + + case SADB_X_EXT_NAT_T_SPORT: + if ((m = key_setsadbxport(KEY_PORTFROMSADDR + (&sav->sah->saidx.src), + SADB_X_EXT_NAT_T_SPORT)) == NULL) + goto fail; + break; + + case SADB_X_EXT_NAT_T_OA: + case SADB_X_EXT_NAT_T_FRAG: + continue; +#endif + case SADB_EXT_ADDRESS_PROXY: case SADB_EXT_IDENTITY_SRC: case SADB_EXT_IDENTITY_DST: @@ -3588,7 +3762,134 @@ return m; } +#ifdef IPSEC_NAT_T /* + * set a type in sadb_x_nat_t_type + */ +static struct mbuf * +key_setsadbxtype(type) + 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. port is in network order + */ +static struct mbuf * +key_setsadbxport(port, type) + 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 order + */ +u_int16_t +key_portfromsaddr(saddr) + struct sockaddr *saddr; +{ + u_int16_t port; + + switch (saddr->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)saddr; + + port = sin->sin_port; + break; + } +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr; + + port = sin6->sin6_port; + break; + } +#endif + default: + printf("key_portfromsaddr: unexpected address family\n"); + port = 0; + break; + } + + return port; +} +#endif /* IPSEC_NAT_T */ + +/* + * Set port is struct sockaddr. port is in network order + */ +static void +key_porttosaddr(saddr, port) + struct sockaddr *saddr; + u_int16_t port; +{ + switch (saddr->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)saddr; + + sin->sin_port = port; + break; + } +#ifdef INET6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr; + + sin6->sin6_port = port; + break; + } +#endif + default: + printf("key_porttosaddr: unexpected address family %d\n", + saddr->sa_family); + break; + } + + return; +} + +/* * set data into sadb_x_policy */ static struct mbuf * @@ -3738,6 +4039,8 @@ const struct secasindex *saidx1, int flag) { + int chkport = 0; + /* sanity */ if (saidx0 == NULL && saidx1 == NULL) return 1; @@ -3761,13 +4064,30 @@ /* CMP_MODE_REQID, CMP_REQID, CMP_HEAD */ if (flag == CMP_MODE_REQID ||flag == CMP_REQID) { +#ifdef IPSEC_NAT_T /* + * If NAT-T is enabled, check ports for tunnel mode. + * Don't do it for transport mode, as there is no + * port information available in the SP. + * XXX also don't check ports if they are set to zero in the SPD: + * This means we bave a non-generated SPD, which can't know UDP ports. + */ + if (saidx1->mode == IPSEC_MODE_TUNNEL && + ((const struct sockaddr_in *)(&saidx1->src))->sin_port && + ((const struct sockaddr_in *)(&saidx1->dst))->sin_port ) + chkport = 1; +#endif /* IPSEC_NAT_T */ + /* * 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) return 0; } +#ifdef IPSEC_NAT_T + else + chkport = 1; +#endif if (flag == CMP_MODE_REQID) { if (saidx0->mode != IPSEC_MODE_ANY @@ -3775,10 +4095,10 @@ return 0; } - if (key_sockaddrcmp(&saidx0->src.sa, &saidx1->src.sa, 0) != 0) { + 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; } } @@ -4398,13 +4718,17 @@ if (((struct sockaddr *)(src0 + 1))->sa_len != sizeof(struct sockaddr_in)) return key_senderror(so, m, EINVAL); +#ifndef IPSEC_NAT_T ((struct sockaddr_in *)(src0 + 1))->sin_port = 0; +#endif break; case AF_INET6: if (((struct sockaddr *)(src0 + 1))->sa_len != sizeof(struct sockaddr_in6)) return key_senderror(so, m, EINVAL); +#ifndef IPSEC_NAT_T ((struct sockaddr_in6 *)(src0 + 1))->sin6_port = 0; +#endif break; default: ; /*???*/ @@ -4414,13 +4738,17 @@ if (((struct sockaddr *)(dst0 + 1))->sa_len != sizeof(struct sockaddr_in)) return key_senderror(so, m, EINVAL); +#ifndef IPSEC_NAT_T ((struct sockaddr_in *)(dst0 + 1))->sin_port = 0; +#endif break; case AF_INET6: if (((struct sockaddr *)(dst0 + 1))->sa_len != sizeof(struct sockaddr_in6)) return key_senderror(so, m, EINVAL); +#ifndef IPSEC_NAT_T ((struct sockaddr_in6 *)(dst0 + 1))->sin6_port = 0; +#endif break; default: ; /*???*/ @@ -4429,6 +4757,12 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); + /* If not using NAT-T, make sure port numbers are set to zero. */ +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* SPI allocation */ spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE], &saidx); @@ -4684,6 +5018,12 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); + /* If not using NAT-T, make sure if port number is zero. */ +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA header */ if ((sah = key_getsah(&saidx)) == NULL) { ipseclog((LOG_DEBUG, "%s: no SA index found.\n", __func__)); @@ -4750,6 +5090,68 @@ return key_senderror(so, m, 0); } +#ifdef IPSEC_NAT_T + /* + * Handle NAT-T info if present + */ + if (mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) + printf("update: NAT-T OA present\n"); + + 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; + struct sadb_x_nat_t_port *dport; + struct sadb_address *addr; + struct sadb_x_nat_t_frag *frag; + + 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, "key_update: " + "invalid message.\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) { + ipseclog((LOG_DEBUG, "key_update: invalid message\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) { + ipseclog((LOG_DEBUG, "key_update: invalid message\n")); + 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]; + addr = (struct sadb_address *) + mhp->ext[SADB_X_EXT_NAT_T_OA]; + frag = (struct sadb_x_nat_t_frag *) + mhp->ext[SADB_X_EXT_NAT_T_FRAG]; + + if (type) + sav->natt_type = type->sadb_x_nat_t_type_type; + if (sport) + KEY_PORTTOSADDR(&sav->sah->saidx.src, + sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&sav->sah->saidx.dst, + dport->sadb_x_nat_t_port_port); + if (frag) + sav->esp_frag = frag->sadb_x_nat_t_frag_fraglen; + else + sav->esp_frag = IP_MAXPACKET; + } +#endif /* IPSEC_NAT_T */ + { struct mbuf *n; @@ -4882,6 +5284,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA header */ if ((newsah = key_getsah(&saidx)) == NULL) { /* create a new SA header */ @@ -4918,7 +5325,69 @@ return key_senderror(so, m, error); } +#ifdef IPSEC_NAT_T /* + * Handle NAT-T info if present + */ + if (mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) + printf("add: NAT-T OA present\n"); + + 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; + struct sadb_x_nat_t_port *dport; + struct sadb_address *addr; + struct sadb_x_nat_t_frag *frag; + + 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, "key_add: " + "invalid message.\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) { + ipseclog((LOG_DEBUG, "key_add: invalid message\n")); + return key_senderror(so, m, EINVAL); + } + + if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) && + (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) { + ipseclog((LOG_DEBUG, "key_update: invalid message\n")); + 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]; + addr = (struct sadb_address *) + mhp->ext[SADB_X_EXT_NAT_T_OA]; + frag = (struct sadb_x_nat_t_frag *) + mhp->ext[SADB_X_EXT_NAT_T_FRAG]; + + if (type) + newsav->natt_type = type->sadb_x_nat_t_type_type; + if (sport) + KEY_PORTTOSADDR(&newsav->sah->saidx.src, + sport->sadb_x_nat_t_port_port); + if (dport) + KEY_PORTTOSADDR(&newsav->sah->saidx.dst, + dport->sadb_x_nat_t_port_port); + if (frag) + newsav->esp_frag = frag->sadb_x_nat_t_frag_fraglen; + else + newsav->esp_frag = IP_MAXPACKET; + } +#endif + + /* * don't call key_freesav() here, as we would like to keep the SA * in the database on success. */ @@ -5118,6 +5587,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA header */ SAHTREE_LOCK(); LIST_FOREACH(sah, &sahtree, chain) { @@ -5187,6 +5661,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + SAHTREE_LOCK(); LIST_FOREACH(sah, &sahtree, chain) { if (sah->state == SADB_SASTATE_DEAD) @@ -5301,6 +5780,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA header */ SAHTREE_LOCK(); LIST_FOREACH(sah, &sahtree, chain) { @@ -5988,6 +6472,11 @@ /* XXX boundary check against sa_len */ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); +#ifndef IPSEC_NAT_T + KEY_PORTTOSADDR(&saidx.src, 0); + KEY_PORTTOSADDR(&saidx.dst, 0); +#endif + /* get a SA index */ SAHTREE_LOCK(); LIST_FOREACH(sah, &sahtree, chain) { @@ -6596,6 +7085,11 @@ key_spdadd, /* SADB_X_SPDSETIDX */ NULL, /* SADB_X_SPDEXPIRE */ key_spddelete2, /* SADB_X_SPDDELETE2 */ +#ifdef IPSEC_NAT_T + key_nat_map, /* SADB_X_NAT_T_NEW_MAPPING */ +#else + NULL, +#endif }; /* @@ -6932,6 +7426,13 @@ 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_OA: + case SADB_X_EXT_NAT_T_FRAG: +#endif /* duplicate check */ /* * XXX Are there duplication payloads of either Index: netipsec/key.h =================================================================== --- netipsec/key.h (.../6.x) (revision 8180) +++ netipsec/key.h (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -99,6 +99,10 @@ 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 __P((struct sockaddr *)); +#define KEY_PORTFROMSADDR(saddr) key_portfromsaddr((struct sockaddr *)(saddr)) +#endif #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_IPSEC_SA); Index: sys/mbuf.h =================================================================== --- sys/mbuf.h (.../6.x) (revision 8180) +++ sys/mbuf.h (.../6.x-FAST_IPSEC-NATT) (revision 8180) @@ -778,6 +778,7 @@ #define PACKET_TAG_PF_TRANSLATE_LOCALHOST 26 /* PF translate localhost */ #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 */ /* Packet tag routines. */ struct m_tag *m_tag_alloc(u_int32_t, int, int, int); --NzB8fVQJ5HfG6fxh--