ipsec_output.c revision 111119
1105197Ssam/* $FreeBSD: head/sys/netipsec/ipsec_output.c 111119 2003-02-19 05:47:46Z imp $ */ 2105197Ssam/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */ 3105197Ssam 4105197Ssam/* 5105197Ssam * IPsec output processing. 6105197Ssam */ 7105197Ssam#include "opt_inet.h" 8105197Ssam#include "opt_inet6.h" 9105197Ssam#include "opt_ipsec.h" 10105197Ssam 11105197Ssam#include <sys/param.h> 12105197Ssam#include <sys/systm.h> 13105197Ssam#include <sys/mbuf.h> 14105197Ssam#include <sys/domain.h> 15105197Ssam#include <sys/protosw.h> 16105197Ssam#include <sys/socket.h> 17105197Ssam#include <sys/errno.h> 18105197Ssam#include <sys/syslog.h> 19105197Ssam 20105197Ssam#include <net/if.h> 21105197Ssam#include <net/route.h> 22105197Ssam 23105197Ssam#include <netinet/in.h> 24105197Ssam#include <netinet/in_systm.h> 25105197Ssam#include <netinet/ip.h> 26105197Ssam#include <netinet/ip_var.h> 27105197Ssam#include <netinet/in_var.h> 28105197Ssam#include <netinet/ip_ecn.h> 29105197Ssam#ifdef INET6 30105197Ssam#include <netinet6/ip6_ecn.h> 31105197Ssam#endif 32105197Ssam 33105197Ssam#include <netinet/ip6.h> 34105197Ssam#ifdef INET6 35105197Ssam#include <netinet6/ip6_var.h> 36105197Ssam#endif 37105197Ssam#include <netinet/in_pcb.h> 38105197Ssam#ifdef INET6 39105197Ssam#include <netinet/icmp6.h> 40105197Ssam#endif 41105197Ssam 42105197Ssam#include <netipsec/ipsec.h> 43105197Ssam#ifdef INET6 44105197Ssam#include <netipsec/ipsec6.h> 45105197Ssam#endif 46105197Ssam#include <netipsec/ah_var.h> 47105197Ssam#include <netipsec/esp_var.h> 48105197Ssam#include <netipsec/ipcomp_var.h> 49105197Ssam 50105197Ssam#include <netipsec/xform.h> 51105197Ssam 52105197Ssam#include <netipsec/key.h> 53105197Ssam#include <netipsec/keydb.h> 54105197Ssam#include <netipsec/key_debug.h> 55105197Ssam 56105197Ssam#include <machine/in_cksum.h> 57105197Ssam 58105197Ssamint 59105197Ssamipsec_process_done(struct mbuf *m, struct ipsecrequest *isr) 60105197Ssam{ 61105197Ssam struct tdb_ident *tdbi; 62105197Ssam struct m_tag *mtag; 63105197Ssam struct secasvar *sav; 64105197Ssam struct secasindex *saidx; 65105197Ssam int error; 66105197Ssam 67105197Ssam#if 0 68105197Ssam SPLASSERT(net, "ipsec_process_done"); 69105197Ssam#endif 70105197Ssam 71105197Ssam KASSERT(m != NULL, ("ipsec_process_done: null mbuf")); 72105197Ssam KASSERT(isr != NULL, ("ipsec_process_done: null ISR")); 73105197Ssam sav = isr->sav; 74105197Ssam KASSERT(sav != NULL, ("ipsec_process_done: null SA")); 75105197Ssam KASSERT(sav->sah != NULL, ("ipsec_process_done: null SAH")); 76105197Ssam 77105197Ssam saidx = &sav->sah->saidx; 78105197Ssam switch (saidx->dst.sa.sa_family) { 79105197Ssam#ifdef INET 80105197Ssam case AF_INET: 81105197Ssam /* Fix the header length, for AH processing. */ 82105197Ssam mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len); 83105197Ssam break; 84105197Ssam#endif /* INET */ 85105197Ssam#ifdef INET6 86105197Ssam case AF_INET6: 87105197Ssam /* Fix the header length, for AH processing. */ 88105197Ssam if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) { 89105197Ssam error = ENXIO; 90105197Ssam goto bad; 91105197Ssam } 92105197Ssam if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) { 93105197Ssam /* No jumbogram support. */ 94105197Ssam error = ENXIO; /*?*/ 95105197Ssam goto bad; 96105197Ssam } 97105197Ssam mtod(m, struct ip6_hdr *)->ip6_plen = 98105197Ssam htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); 99105197Ssam break; 100105197Ssam#endif /* INET6 */ 101105197Ssam default: 102105197Ssam DPRINTF(("ipsec_process_done: unknown protocol family %u\n", 103105197Ssam saidx->dst.sa.sa_family)); 104105197Ssam error = ENXIO; 105105197Ssam goto bad; 106105197Ssam } 107105197Ssam 108105197Ssam /* 109105197Ssam * Add a record of what we've done or what needs to be done to the 110105197Ssam * packet. 111105197Ssam */ 112105197Ssam mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, 113105197Ssam sizeof(struct tdb_ident), M_NOWAIT); 114105197Ssam if (mtag == NULL) { 115105197Ssam DPRINTF(("ipsec_process_done: could not get packet tag\n")); 116105197Ssam error = ENOMEM; 117105197Ssam goto bad; 118105197Ssam } 119105197Ssam 120105197Ssam tdbi = (struct tdb_ident *)(mtag + 1); 121105197Ssam tdbi->dst = saidx->dst; 122105197Ssam tdbi->proto = saidx->proto; 123105197Ssam tdbi->spi = sav->spi; 124105197Ssam m_tag_prepend(m, mtag); 125105197Ssam 126105197Ssam /* 127105197Ssam * If there's another (bundled) SA to apply, do so. 128105197Ssam * Note that this puts a burden on the kernel stack size. 129105197Ssam * If this is a problem we'll need to introduce a queue 130105197Ssam * to set the packet on so we can unwind the stack before 131105197Ssam * doing further processing. 132105197Ssam */ 133105197Ssam if (isr->next) { 134105197Ssam newipsecstat.ips_out_bundlesa++; 135105197Ssam return ipsec4_process_packet(m, isr->next, 0, 0); 136105197Ssam } 137105197Ssam 138105197Ssam /* 139105197Ssam * We're done with IPsec processing, transmit the packet using the 140105197Ssam * appropriate network protocol (IP or IPv6). SPD lookup will be 141105197Ssam * performed again there. 142105197Ssam */ 143105197Ssam switch (saidx->dst.sa.sa_family) { 144105197Ssam#ifdef INET 145105197Ssam struct ip *ip; 146105197Ssam case AF_INET: 147105197Ssam ip = mtod(m, struct ip *); 148105197Ssam ip->ip_len = ntohs(ip->ip_len); 149105197Ssam ip->ip_off = ntohs(ip->ip_off); 150105197Ssam 151105197Ssam return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); 152105197Ssam#endif /* INET */ 153105197Ssam#ifdef INET6 154105197Ssam case AF_INET6: 155105197Ssam /* 156105197Ssam * We don't need massage, IPv6 header fields are always in 157105197Ssam * net endian. 158105197Ssam */ 159105197Ssam return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); 160105197Ssam#endif /* INET6 */ 161105197Ssam } 162105197Ssam panic("ipsec_process_done"); 163105197Ssambad: 164105197Ssam m_freem(m); 165105197Ssam KEY_FREESAV(&sav); 166105197Ssam return (error); 167105197Ssam} 168105197Ssam 169105197Ssamstatic struct ipsecrequest * 170105197Ssamipsec_nextisr( 171105197Ssam struct mbuf *m, 172105197Ssam struct ipsecrequest *isr, 173105197Ssam int af, 174105197Ssam struct secasindex *saidx, 175105197Ssam int *error 176105197Ssam) 177105197Ssam{ 178105197Ssam#define IPSEC_OSTAT(x,y,z) (isr->saidx.proto == IPPROTO_ESP ? (x)++ : \ 179105197Ssam isr->saidx.proto == IPPROTO_AH ? (y)++ : (z)++) 180105197Ssam struct secasvar *sav; 181105197Ssam 182105197Ssam#if 0 183105197Ssam SPLASSERT(net, "ipsec_nextisr"); 184105197Ssam#endif 185105197Ssam KASSERT(af == AF_INET || af == AF_INET6, 186105197Ssam ("ipsec_nextisr: invalid address family %u", af)); 187105197Ssamagain: 188105197Ssam /* 189105197Ssam * Craft SA index to search for proper SA. Note that 190105197Ssam * we only fillin unspecified SA peers for transport 191105197Ssam * mode; for tunnel mode they must already be filled in. 192105197Ssam */ 193105197Ssam *saidx = isr->saidx; 194105197Ssam if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { 195105197Ssam /* Fillin unspecified SA peers only for transport mode */ 196105197Ssam if (af == AF_INET) { 197105197Ssam struct sockaddr_in *sin; 198105197Ssam struct ip *ip = mtod(m, struct ip *); 199105197Ssam 200105197Ssam if (saidx->src.sa.sa_len == 0) { 201105197Ssam sin = &saidx->src.sin; 202105197Ssam sin->sin_len = sizeof(*sin); 203105197Ssam sin->sin_family = AF_INET; 204105197Ssam sin->sin_port = IPSEC_PORT_ANY; 205105197Ssam sin->sin_addr = ip->ip_src; 206105197Ssam } 207105197Ssam if (saidx->dst.sa.sa_len == 0) { 208105197Ssam sin = &saidx->dst.sin; 209105197Ssam sin->sin_len = sizeof(*sin); 210105197Ssam sin->sin_family = AF_INET; 211105197Ssam sin->sin_port = IPSEC_PORT_ANY; 212105197Ssam sin->sin_addr = ip->ip_dst; 213105197Ssam } 214105197Ssam } else { 215105197Ssam struct sockaddr_in6 *sin6; 216105197Ssam struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 217105197Ssam 218105197Ssam if (saidx->src.sin6.sin6_len == 0) { 219105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->src; 220105197Ssam sin6->sin6_len = sizeof(*sin6); 221105197Ssam sin6->sin6_family = AF_INET6; 222105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 223105197Ssam sin6->sin6_addr = ip6->ip6_src; 224105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 225105197Ssam /* fix scope id for comparing SPD */ 226105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 227105197Ssam sin6->sin6_scope_id = 228105197Ssam ntohs(ip6->ip6_src.s6_addr16[1]); 229105197Ssam } 230105197Ssam } 231105197Ssam if (saidx->dst.sin6.sin6_len == 0) { 232105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->dst; 233105197Ssam sin6->sin6_len = sizeof(*sin6); 234105197Ssam sin6->sin6_family = AF_INET6; 235105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 236105197Ssam sin6->sin6_addr = ip6->ip6_dst; 237105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 238105197Ssam /* fix scope id for comparing SPD */ 239105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 240105197Ssam sin6->sin6_scope_id = 241105197Ssam ntohs(ip6->ip6_dst.s6_addr16[1]); 242105197Ssam } 243105197Ssam } 244105197Ssam } 245105197Ssam } 246105197Ssam 247105197Ssam /* 248105197Ssam * Lookup SA and validate it. 249105197Ssam */ 250105197Ssam *error = key_checkrequest(isr, saidx); 251105197Ssam if (*error != 0) { 252105197Ssam /* 253105197Ssam * IPsec processing is required, but no SA found. 254105197Ssam * I assume that key_acquire() had been called 255105197Ssam * to get/establish the SA. Here I discard 256105197Ssam * this packet because it is responsibility for 257105197Ssam * upper layer to retransmit the packet. 258105197Ssam */ 259105197Ssam newipsecstat.ips_out_nosa++; 260105197Ssam goto bad; 261105197Ssam } 262105197Ssam sav = isr->sav; 263105197Ssam if (sav == NULL) { /* XXX valid return */ 264105197Ssam KASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE, 265105197Ssam ("ipsec_nextisr: no SA found, but required; level %u", 266105197Ssam ipsec_get_reqlevel(isr))); 267105197Ssam isr = isr->next; 268105197Ssam if (isr == NULL) { 269105197Ssam /*XXXstatistic??*/ 270105197Ssam *error = EINVAL; /*XXX*/ 271105197Ssam return isr; 272105197Ssam } 273105197Ssam goto again; 274105197Ssam } 275105197Ssam 276105197Ssam /* 277105197Ssam * Check system global policy controls. 278105197Ssam */ 279105197Ssam if ((isr->saidx.proto == IPPROTO_ESP && !esp_enable) || 280105197Ssam (isr->saidx.proto == IPPROTO_AH && !ah_enable) || 281105197Ssam (isr->saidx.proto == IPPROTO_IPCOMP && !ipcomp_enable)) { 282105197Ssam DPRINTF(("ipsec_nextisr: IPsec outbound packet dropped due" 283105197Ssam " to policy (check your sysctls)\n")); 284105197Ssam IPSEC_OSTAT(espstat.esps_pdrops, ahstat.ahs_pdrops, 285105197Ssam ipcompstat.ipcomps_pdrops); 286105197Ssam *error = EHOSTUNREACH; 287105197Ssam goto bad; 288105197Ssam } 289105197Ssam 290105197Ssam /* 291105197Ssam * Sanity check the SA contents for the caller 292105197Ssam * before they invoke the xform output method. 293105197Ssam */ 294105197Ssam if (sav->tdb_xform == NULL) { 295105197Ssam DPRINTF(("ipsec_nextisr: no transform for SA\n")); 296105197Ssam IPSEC_OSTAT(espstat.esps_noxform, ahstat.ahs_noxform, 297105197Ssam ipcompstat.ipcomps_noxform); 298105197Ssam *error = EHOSTUNREACH; 299105197Ssam goto bad; 300105197Ssam } 301105197Ssam return isr; 302105197Ssambad: 303105197Ssam KASSERT(*error != 0, ("ipsec_nextisr: error return w/ no error code")); 304105197Ssam return NULL; 305105197Ssam#undef IPSEC_OSTAT 306105197Ssam} 307105197Ssam 308105197Ssam#ifdef INET 309105197Ssam/* 310105197Ssam * IPsec output logic for IPv4. 311105197Ssam */ 312105197Ssamint 313105197Ssamipsec4_process_packet( 314105197Ssam struct mbuf *m, 315105197Ssam struct ipsecrequest *isr, 316105197Ssam int flags, 317105197Ssam int tunalready) 318105197Ssam{ 319105197Ssam struct secasindex saidx; 320105197Ssam struct secasvar *sav; 321105197Ssam struct ip *ip; 322105197Ssam int s, error, i, off; 323105197Ssam 324105197Ssam KASSERT(m != NULL, ("ipsec4_process_packet: null mbuf")); 325105197Ssam KASSERT(isr != NULL, ("ipsec4_process_packet: null isr")); 326105197Ssam 327105197Ssam s = splnet(); /* insure SA contents don't change */ 328105197Ssam 329105197Ssam isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error); 330105197Ssam if (isr == NULL) 331105197Ssam goto bad; 332105197Ssam 333105197Ssam sav = isr->sav; 334105197Ssam if (!tunalready) { 335105197Ssam union sockaddr_union *dst = &sav->sah->saidx.dst; 336105197Ssam int setdf; 337105197Ssam 338105197Ssam /* 339105197Ssam * Collect IP_DF state from the outer header. 340105197Ssam */ 341105197Ssam if (dst->sa.sa_family == AF_INET) { 342105197Ssam if (m->m_len < sizeof (struct ip) && 343105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 344105197Ssam error = ENOBUFS; 345105197Ssam goto bad; 346105197Ssam } 347105197Ssam ip = mtod(m, struct ip *); 348105197Ssam /* Honor system-wide control of how to handle IP_DF */ 349105197Ssam switch (ip4_ipsec_dfbit) { 350105197Ssam case 0: /* clear in outer header */ 351105197Ssam case 1: /* set in outer header */ 352105197Ssam setdf = ip4_ipsec_dfbit; 353105197Ssam break; 354105197Ssam default: /* propagate to outer header */ 355105197Ssam setdf = ntohs(ip->ip_off & IP_DF); 356105197Ssam break; 357105197Ssam } 358105197Ssam } else { 359105197Ssam ip = NULL; /* keep compiler happy */ 360105197Ssam setdf = 0; 361105197Ssam } 362105197Ssam /* Do the appropriate encapsulation, if necessary */ 363105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 364105197Ssam dst->sa.sa_family != AF_INET || /* PF mismatch */ 365105197Ssam#if 0 366105197Ssam (sav->flags & SADB_X_SAFLAGS_TUNNEL) || /* Tunnel requ'd */ 367105197Ssam sav->tdb_xform->xf_type == XF_IP4 || /* ditto */ 368105197Ssam#endif 369105197Ssam (dst->sa.sa_family == AF_INET && /* Proxy */ 370105197Ssam dst->sin.sin_addr.s_addr != INADDR_ANY && 371105197Ssam dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) { 372105197Ssam struct mbuf *mp; 373105197Ssam 374105197Ssam /* Fix IPv4 header checksum and length */ 375105197Ssam if (m->m_len < sizeof (struct ip) && 376105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 377105197Ssam error = ENOBUFS; 378105197Ssam goto bad; 379105197Ssam } 380105197Ssam ip = mtod(m, struct ip *); 381105197Ssam ip->ip_len = htons(m->m_pkthdr.len); 382105197Ssam ip->ip_sum = 0; 383105197Ssam#ifdef _IP_VHL 384105197Ssam if (ip->ip_vhl == IP_VHL_BORING) 385105197Ssam ip->ip_sum = in_cksum_hdr(ip); 386105197Ssam else 387105197Ssam ip->ip_sum = in_cksum(m, 388105197Ssam _IP_VHL_HL(ip->ip_vhl) << 2); 389105197Ssam#else 390105197Ssam ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 391105197Ssam#endif 392105197Ssam 393105197Ssam /* Encapsulate the packet */ 394105197Ssam error = ipip_output(m, isr, &mp, 0, 0); 395105197Ssam if (mp == NULL && !error) { 396105197Ssam /* Should never happen. */ 397105197Ssam DPRINTF(("ipsec4_process_packet: ipip_output " 398105197Ssam "returns no mbuf and no error!")); 399105197Ssam error = EFAULT; 400105197Ssam } 401105197Ssam if (error) { 402105197Ssam if (mp) 403105197Ssam m_freem(mp); 404105197Ssam goto bad; 405105197Ssam } 406105197Ssam m = mp, mp = NULL; 407105197Ssam /* 408105197Ssam * ipip_output clears IP_DF in the new header. If 409105197Ssam * we need to propagate IP_DF from the outer header, 410105197Ssam * then we have to do it here. 411105197Ssam * 412105197Ssam * XXX shouldn't assume what ipip_output does. 413105197Ssam */ 414105197Ssam if (dst->sa.sa_family == AF_INET && setdf) { 415105197Ssam if (m->m_len < sizeof (struct ip) && 416105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 417105197Ssam error = ENOBUFS; 418105197Ssam goto bad; 419105197Ssam } 420105197Ssam ip = mtod(m, struct ip *); 421105197Ssam ip->ip_off = ntohs(ip->ip_off); 422105197Ssam ip->ip_off |= IP_DF; 423105197Ssam ip->ip_off = htons(ip->ip_off); 424105197Ssam } 425105197Ssam } 426105197Ssam } 427105197Ssam 428105197Ssam /* 429105197Ssam * Dispatch to the appropriate IPsec transform logic. The 430105197Ssam * packet will be returned for transmission after crypto 431105197Ssam * processing, etc. are completed. For encapsulation we 432105197Ssam * bypass this call because of the explicit call done above 433105197Ssam * (necessary to deal with IP_DF handling for IPv4). 434105197Ssam * 435105197Ssam * NB: m & sav are ``passed to caller'' who's reponsible for 436105197Ssam * for reclaiming their resources. 437105197Ssam */ 438105197Ssam if (sav->tdb_xform->xf_type != XF_IP4) { 439105197Ssam ip = mtod(m, struct ip *); 440105197Ssam i = ip->ip_hl << 2; 441105197Ssam off = offsetof(struct ip, ip_p); 442105197Ssam error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 443105197Ssam } else { 444105197Ssam error = ipsec_process_done(m, isr); 445105197Ssam } 446105197Ssam splx(s); 447105197Ssam return error; 448105197Ssambad: 449105197Ssam splx(s); 450105197Ssam if (m) 451105197Ssam m_freem(m); 452105197Ssam return error; 453105197Ssam} 454105197Ssam#endif 455105197Ssam 456105197Ssam#ifdef INET6 457105197Ssam/* 458105197Ssam * Chop IP6 header from the payload. 459105197Ssam */ 460105197Ssamstatic struct mbuf * 461105197Ssamipsec6_splithdr(struct mbuf *m) 462105197Ssam{ 463105197Ssam struct mbuf *mh; 464105197Ssam struct ip6_hdr *ip6; 465105197Ssam int hlen; 466105197Ssam 467105197Ssam KASSERT(m->m_len >= sizeof (struct ip6_hdr), 468105197Ssam ("ipsec6_splithdr: first mbuf too short, len %u", m->m_len)); 469105197Ssam ip6 = mtod(m, struct ip6_hdr *); 470105197Ssam hlen = sizeof(struct ip6_hdr); 471105197Ssam if (m->m_len > hlen) { 472111119Simp MGETHDR(mh, M_DONTWAIT, MT_HEADER); 473105197Ssam if (!mh) { 474105197Ssam m_freem(m); 475105197Ssam return NULL; 476105197Ssam } 477108466Ssam M_MOVE_PKTHDR(mh, m); 478105197Ssam MH_ALIGN(mh, hlen); 479105197Ssam m->m_len -= hlen; 480105197Ssam m->m_data += hlen; 481105197Ssam mh->m_next = m; 482105197Ssam m = mh; 483105197Ssam m->m_len = hlen; 484105197Ssam bcopy((caddr_t)ip6, mtod(m, caddr_t), hlen); 485105197Ssam } else if (m->m_len < hlen) { 486105197Ssam m = m_pullup(m, hlen); 487105197Ssam if (!m) 488105197Ssam return NULL; 489105197Ssam } 490105197Ssam return m; 491105197Ssam} 492105197Ssam 493105197Ssam/* 494105197Ssam * IPsec output logic for IPv6, transport mode. 495105197Ssam */ 496105197Ssamint 497105197Ssamipsec6_output_trans( 498105197Ssam struct ipsec_output_state *state, 499105197Ssam u_char *nexthdrp, 500105197Ssam struct mbuf *mprev, 501105197Ssam struct secpolicy *sp, 502105197Ssam int flags, 503105197Ssam int *tun) 504105197Ssam{ 505105197Ssam struct ipsecrequest *isr; 506105197Ssam struct secasindex saidx; 507105197Ssam int error = 0; 508105197Ssam struct mbuf *m; 509105197Ssam 510105197Ssam KASSERT(state != NULL, ("ipsec6_output: null state")); 511105197Ssam KASSERT(state->m != NULL, ("ipsec6_output: null m")); 512105197Ssam KASSERT(nexthdrp != NULL, ("ipsec6_output: null nexthdrp")); 513105197Ssam KASSERT(mprev != NULL, ("ipsec6_output: null mprev")); 514105197Ssam KASSERT(sp != NULL, ("ipsec6_output: null sp")); 515105197Ssam KASSERT(tun != NULL, ("ipsec6_output: null tun")); 516105197Ssam 517105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 518105197Ssam printf("ipsec6_output_trans: applyed SP\n"); 519105197Ssam kdebug_secpolicy(sp)); 520105197Ssam 521105197Ssam isr = sp->req; 522105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 523105197Ssam /* the rest will be handled by ipsec6_output_tunnel() */ 524105197Ssam *tun = 1; /* need tunnel-mode processing */ 525105197Ssam return 0; 526105197Ssam } 527105197Ssam 528105197Ssam *tun = 0; 529105197Ssam m = state->m; 530105197Ssam 531105197Ssam isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 532105197Ssam if (isr == NULL) { 533105197Ssam#ifdef notdef 534105197Ssam /* XXX should notification be done for all errors ? */ 535105197Ssam /* 536105197Ssam * Notify the fact that the packet is discarded 537105197Ssam * to ourselves. I believe this is better than 538105197Ssam * just silently discarding. (jinmei@kame.net) 539105197Ssam * XXX: should we restrict the error to TCP packets? 540105197Ssam * XXX: should we directly notify sockets via 541105197Ssam * pfctlinputs? 542105197Ssam */ 543105197Ssam icmp6_error(m, ICMP6_DST_UNREACH, 544105197Ssam ICMP6_DST_UNREACH_ADMIN, 0); 545105197Ssam m = NULL; /* NB: icmp6_error frees mbuf */ 546105197Ssam#endif 547105197Ssam goto bad; 548105197Ssam } 549105197Ssam 550105197Ssam return (*isr->sav->tdb_xform->xf_output)(m, isr, NULL, 551105197Ssam sizeof (struct ip6_hdr), 552105197Ssam offsetof(struct ip6_hdr, ip6_nxt)); 553105197Ssambad: 554105197Ssam if (m) 555105197Ssam m_freem(m); 556105197Ssam state->m = NULL; 557105197Ssam return error; 558105197Ssam} 559105197Ssam 560105197Ssamstatic int 561105197Ssamipsec6_encapsulate(struct mbuf *m, struct secasvar *sav) 562105197Ssam{ 563105197Ssam struct ip6_hdr *oip6; 564105197Ssam struct ip6_hdr *ip6; 565105197Ssam size_t plen; 566105197Ssam 567105197Ssam /* can't tunnel between different AFs */ 568105197Ssam if (sav->sah->saidx.src.sa.sa_family != AF_INET6 || 569105197Ssam sav->sah->saidx.dst.sa.sa_family != AF_INET6) { 570105197Ssam m_freem(m); 571105197Ssam return EINVAL; 572105197Ssam } 573105197Ssam KASSERT(m->m_len != sizeof (struct ip6_hdr), 574105197Ssam ("ipsec6_encapsulate: mbuf wrong size; len %u", m->m_len)); 575105197Ssam 576105197Ssam 577105197Ssam /* 578105197Ssam * grow the mbuf to accomodate the new IPv6 header. 579105197Ssam */ 580105197Ssam plen = m->m_pkthdr.len; 581105197Ssam if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) { 582105197Ssam struct mbuf *n; 583111119Simp MGET(n, M_DONTWAIT, MT_DATA); 584105197Ssam if (!n) { 585105197Ssam m_freem(m); 586105197Ssam return ENOBUFS; 587105197Ssam } 588105197Ssam n->m_len = sizeof(struct ip6_hdr); 589105197Ssam n->m_next = m->m_next; 590105197Ssam m->m_next = n; 591105197Ssam m->m_pkthdr.len += sizeof(struct ip6_hdr); 592105197Ssam oip6 = mtod(n, struct ip6_hdr *); 593105197Ssam } else { 594105197Ssam m->m_next->m_len += sizeof(struct ip6_hdr); 595105197Ssam m->m_next->m_data -= sizeof(struct ip6_hdr); 596105197Ssam m->m_pkthdr.len += sizeof(struct ip6_hdr); 597105197Ssam oip6 = mtod(m->m_next, struct ip6_hdr *); 598105197Ssam } 599105197Ssam ip6 = mtod(m, struct ip6_hdr *); 600105197Ssam ovbcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr)); 601105197Ssam 602105197Ssam /* Fake link-local scope-class addresses */ 603105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src)) 604105197Ssam oip6->ip6_src.s6_addr16[1] = 0; 605105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst)) 606105197Ssam oip6->ip6_dst.s6_addr16[1] = 0; 607105197Ssam 608105197Ssam /* construct new IPv6 header. see RFC 2401 5.1.2.2 */ 609105197Ssam /* ECN consideration. */ 610105197Ssam ip6_ecn_ingress(ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow); 611105197Ssam if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr)) 612105197Ssam ip6->ip6_plen = htons(plen); 613105197Ssam else { 614105197Ssam /* ip6->ip6_plen will be updated in ip6_output() */ 615105197Ssam } 616105197Ssam ip6->ip6_nxt = IPPROTO_IPV6; 617105197Ssam sav->sah->saidx.src.sin6.sin6_addr = ip6->ip6_src; 618105197Ssam sav->sah->saidx.dst.sin6.sin6_addr = ip6->ip6_dst; 619105197Ssam ip6->ip6_hlim = IPV6_DEFHLIM; 620105197Ssam 621105197Ssam /* XXX Should ip6_src be updated later ? */ 622105197Ssam 623105197Ssam return 0; 624105197Ssam} 625105197Ssam 626105197Ssam/* 627105197Ssam * IPsec output logic for IPv6, tunnel mode. 628105197Ssam */ 629105197Ssamint 630105197Ssamipsec6_output_tunnel(struct ipsec_output_state *state, struct secpolicy *sp, int flags) 631105197Ssam{ 632105197Ssam struct ip6_hdr *ip6; 633105197Ssam struct ipsecrequest *isr; 634105197Ssam struct secasindex saidx; 635105197Ssam int error; 636105197Ssam struct sockaddr_in6* dst6; 637105197Ssam struct mbuf *m; 638105197Ssam 639105197Ssam KASSERT(state != NULL, ("ipsec6_output: null state")); 640105197Ssam KASSERT(state->m != NULL, ("ipsec6_output: null m")); 641105197Ssam KASSERT(sp != NULL, ("ipsec6_output: null sp")); 642105197Ssam 643105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 644105197Ssam printf("ipsec6_output_tunnel: applyed SP\n"); 645105197Ssam kdebug_secpolicy(sp)); 646105197Ssam 647105197Ssam m = state->m; 648105197Ssam /* 649105197Ssam * transport mode ipsec (before the 1st tunnel mode) is already 650105197Ssam * processed by ipsec6_output_trans(). 651105197Ssam */ 652105197Ssam for (isr = sp->req; isr; isr = isr->next) { 653105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 654105197Ssam break; 655105197Ssam } 656105197Ssam isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 657105197Ssam if (isr == NULL) 658105197Ssam goto bad; 659105197Ssam 660105197Ssam /* 661105197Ssam * There may be the case that SA status will be changed when 662105197Ssam * we are refering to one. So calling splsoftnet(). 663105197Ssam */ 664105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 665105197Ssam /* 666105197Ssam * build IPsec tunnel. 667105197Ssam */ 668105197Ssam /* XXX should be processed with other familiy */ 669105197Ssam if (isr->sav->sah->saidx.src.sa.sa_family != AF_INET6) { 670105197Ssam ipseclog((LOG_ERR, "ipsec6_output_tunnel: " 671105197Ssam "family mismatched between inner and outer, spi=%u\n", 672105197Ssam ntohl(isr->sav->spi))); 673105197Ssam newipsecstat.ips_out_inval++; 674105197Ssam error = EAFNOSUPPORT; 675105197Ssam goto bad; 676105197Ssam } 677105197Ssam 678105197Ssam m = ipsec6_splithdr(m); 679105197Ssam if (!m) { 680105197Ssam newipsecstat.ips_out_nomem++; 681105197Ssam error = ENOMEM; 682105197Ssam goto bad; 683105197Ssam } 684105197Ssam error = ipsec6_encapsulate(m, isr->sav); 685105197Ssam if (error) { 686105197Ssam m = NULL; 687105197Ssam goto bad; 688105197Ssam } 689105197Ssam ip6 = mtod(m, struct ip6_hdr *); 690105197Ssam 691105197Ssam state->ro = &isr->sav->sah->sa_route; 692105197Ssam state->dst = (struct sockaddr *)&state->ro->ro_dst; 693105197Ssam dst6 = (struct sockaddr_in6 *)state->dst; 694105197Ssam if (state->ro->ro_rt 695105197Ssam && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 696105197Ssam || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) { 697105197Ssam RTFREE(state->ro->ro_rt); 698105197Ssam state->ro->ro_rt = NULL; 699105197Ssam } 700105197Ssam if (state->ro->ro_rt == 0) { 701105197Ssam bzero(dst6, sizeof(*dst6)); 702105197Ssam dst6->sin6_family = AF_INET6; 703105197Ssam dst6->sin6_len = sizeof(*dst6); 704105197Ssam dst6->sin6_addr = ip6->ip6_dst; 705105197Ssam rtalloc(state->ro); 706105197Ssam } 707105197Ssam if (state->ro->ro_rt == 0) { 708105197Ssam ip6stat.ip6s_noroute++; 709105197Ssam newipsecstat.ips_out_noroute++; 710105197Ssam error = EHOSTUNREACH; 711105197Ssam goto bad; 712105197Ssam } 713105197Ssam 714105197Ssam /* adjust state->dst if tunnel endpoint is offlink */ 715105197Ssam if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) { 716105197Ssam state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway; 717105197Ssam dst6 = (struct sockaddr_in6 *)state->dst; 718105197Ssam } 719105197Ssam } 720105197Ssam 721105197Ssam m = ipsec6_splithdr(m); 722105197Ssam if (!m) { 723105197Ssam newipsecstat.ips_out_nomem++; 724105197Ssam error = ENOMEM; 725105197Ssam goto bad; 726105197Ssam } 727105197Ssam ip6 = mtod(m, struct ip6_hdr *); 728105197Ssam return (*isr->sav->tdb_xform->xf_output)(m, isr, NULL, 729105197Ssam sizeof (struct ip6_hdr), 730105197Ssam offsetof(struct ip6_hdr, ip6_nxt)); 731105197Ssambad: 732105197Ssam if (m) 733105197Ssam m_freem(m); 734105197Ssam state->m = NULL; 735105197Ssam return error; 736105197Ssam} 737105197Ssam#endif /*INET6*/ 738