1/*- 2 * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28#include "opt_inet.h" 29#include "opt_inet6.h" 30#include "opt_ipsec.h" 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/kernel.h> 35#include <sys/lock.h> 36#include <sys/mbuf.h> 37#include <sys/protosw.h> 38#include <sys/socket.h> 39#include <sys/sockopt.h> 40#include <sys/sysctl.h> 41 42#include <netinet/in.h> 43#include <netinet/in_pcb.h> 44#include <netinet/in_systm.h> 45#include <netinet/ip.h> 46#include <netinet/ip6.h> 47#include <netinet/ip_var.h> 48#include <netinet/tcp.h> 49#include <netinet/udp.h> 50#include <netinet/udp_var.h> 51 52#include <netinet6/ip6_var.h> 53 54#include <net/vnet.h> 55 56#include <netipsec/ipsec.h> 57#include <netipsec/esp.h> 58#include <netipsec/esp_var.h> 59#include <netipsec/xform.h> 60 61#include <netipsec/key.h> 62#include <netipsec/key_debug.h> 63#include <netipsec/ipsec_support.h> 64#include <machine/in_cksum.h> 65 66/* 67 * Handle UDP_ENCAP socket option. Always return with released INP_WLOCK. 68 */ 69int 70udp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt) 71{ 72 struct udpcb *up; 73 int error, optval; 74 75 INP_WLOCK_ASSERT(inp); 76 if (sopt->sopt_name != UDP_ENCAP) { 77 INP_WUNLOCK(inp); 78 return (ENOPROTOOPT); 79 } 80 81 up = intoudpcb(inp); 82 if (sopt->sopt_dir == SOPT_GET) { 83 if (up->u_flags & UF_ESPINUDP) 84 optval = UDP_ENCAP_ESPINUDP; 85 else 86 optval = 0; 87 INP_WUNLOCK(inp); 88 return (sooptcopyout(sopt, &optval, sizeof(optval))); 89 } 90 INP_WUNLOCK(inp); 91 92 error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval)); 93 if (error != 0) 94 return (error); 95 96 INP_WLOCK(inp); 97 switch (optval) { 98 case 0: 99 up->u_flags &= ~UF_ESPINUDP; 100 break; 101 case UDP_ENCAP_ESPINUDP: 102 up->u_flags |= UF_ESPINUDP; 103 break; 104 default: 105 error = EINVAL; 106 } 107 INP_WUNLOCK(inp); 108 return (error); 109} 110 111/* 112 * Potentially decap ESP in UDP frame. Check for an ESP header. 113 * If present, strip the UDP header and push the result through IPSec. 114 * 115 * Returns error if mbuf consumed and/or processed, otherwise 0. 116 */ 117int 118udp_ipsec_input(struct mbuf *m, int off, int af) 119{ 120 union sockaddr_union dst; 121 struct secasvar *sav; 122 struct udphdr *udp; 123 uint32_t spi; 124 int hlen; 125 126 /* 127 * Just return if packet doesn't have enough data. 128 * We need at least [IP header + UDP header + ESP header]. 129 * NAT-Keepalive packet has only one byte of payload, so it 130 * by default will not be processed. 131 */ 132 if (m->m_pkthdr.len < off + sizeof(struct esp)) 133 return (0); 134 135 m_copydata(m, off, sizeof(uint32_t), (caddr_t)&spi); 136 if (spi == 0) /* Non-ESP marker. */ 137 return (0); 138 139 /* 140 * Find SA and check that it is configured for UDP 141 * encapsulation. 142 */ 143 bzero(&dst, sizeof(dst)); 144 dst.sa.sa_family = af; 145 switch (af) { 146#ifdef INET 147 case AF_INET: { 148 struct ip *ip; 149 150 dst.sin.sin_len = sizeof(struct sockaddr_in); 151 ip = mtod(m, struct ip *); 152 ip->ip_p = IPPROTO_ESP; 153 off = offsetof(struct ip, ip_p); 154 hlen = ip->ip_hl << 2; 155 dst.sin.sin_addr = ip->ip_dst; 156 break; 157 } 158#endif 159#ifdef INET6 160 case AF_INET6: { 161 struct ip6_hdr *ip6; 162 163 dst.sin6.sin6_len = sizeof(struct sockaddr_in6); 164 ip6 = mtod(m, struct ip6_hdr *); 165 ip6->ip6_nxt = IPPROTO_ESP; 166 off = offsetof(struct ip6_hdr, ip6_nxt); 167 hlen = sizeof(struct ip6_hdr); 168 dst.sin6.sin6_addr = ip6->ip6_dst; 169 break; 170 } 171#endif 172 default: 173 ESPSTAT_INC(esps_nopf); 174 m_freem(m); 175 return (EPFNOSUPPORT); 176 } 177 178 sav = key_allocsa(&dst, IPPROTO_ESP, spi); 179 if (sav == NULL) { 180 ESPSTAT_INC(esps_notdb); 181 m_freem(m); 182 return (ENOENT); 183 } 184 udp = mtodo(m, hlen); 185 if (sav->natt == NULL || 186 sav->natt->sport != udp->uh_sport || 187 sav->natt->dport != udp->uh_dport) { 188 /* XXXAE: should we check source address? */ 189 ESPSTAT_INC(esps_notdb); 190 key_freesav(&sav); 191 m_freem(m); 192 return (ENOENT); 193 } 194 /* 195 * Remove the UDP header 196 * Before: 197 * <--- off ---> 198 * +----+------+-----+ 199 * | IP | UDP | ESP | 200 * +----+------+-----+ 201 * <-skip-> 202 * After: 203 * +----+-----+ 204 * | IP | ESP | 205 * +----+-----+ 206 * <-skip-> 207 */ 208 m_striphdr(m, hlen, sizeof(*udp)); 209 210 /* 211 * We cannot yet update the cksums so clear any h/w cksum flags 212 * as they are no longer valid. 213 */ 214 switch (af) { 215#ifdef INET 216 case AF_INET: 217 if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) 218 m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID | CSUM_PSEUDO_HDR); 219 break; 220#endif /* INET */ 221#ifdef INET6 222 case AF_INET6: 223 if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID_IPV6) 224 m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR); 225 break; 226#endif /* INET6 */ 227 default: 228 ESPSTAT_INC(esps_nopf); 229 m_freem(m); 230 return (EPFNOSUPPORT); 231 } 232 233 /* 234 * We can update ip_len and ip_sum here, but ipsec4_input_cb() 235 * will do this anyway, so don't touch them here. 236 */ 237 ESPSTAT_INC(esps_input); 238 (*sav->tdb_xform->xf_input)(m, sav, hlen, off); 239 return (EINPROGRESS); /* Consumed by IPsec. */ 240} 241 242int 243udp_ipsec_output(struct mbuf *m, struct secasvar *sav) 244{ 245 struct udphdr *udp; 246 struct mbuf *n; 247 int hlen, off; 248 249 IPSEC_ASSERT(sav->natt != NULL, ("UDP encapsulation isn't required.")); 250 251 switch (sav->sah->saidx.dst.sa.sa_family) { 252#ifdef INET 253 case AF_INET: { 254 struct ip *ip; 255 ip = mtod(m, struct ip *); 256 hlen = ip->ip_hl << 2; 257 break; 258 } 259#endif 260#ifdef INET6 261 case AF_INET6: 262 hlen = sizeof(struct ip6_hdr); 263 break; 264#endif 265 default: 266 ESPSTAT_INC(esps_nopf); 267 return (EAFNOSUPPORT); 268 } 269 270 n = m_makespace(m, hlen, sizeof(*udp), &off); 271 if (n == NULL) { 272 DPRINTF(("%s: m_makespace for udphdr failed\n", __func__)); 273 return (ENOBUFS); 274 } 275 276 udp = mtodo(n, off); 277 udp->uh_dport = sav->natt->dport; 278 udp->uh_sport = sav->natt->sport; 279 udp->uh_sum = 0; 280 udp->uh_ulen = htons(m->m_pkthdr.len - hlen); 281 282 switch (sav->sah->saidx.dst.sa.sa_family) { 283#ifdef INET 284 case AF_INET: { 285 struct ip *ip; 286 287 ip = mtod(m, struct ip *); 288 ip->ip_len = htons(m->m_pkthdr.len); 289 ip->ip_p = IPPROTO_UDP; 290 break; 291 } 292#endif 293#ifdef INET6 294 case AF_INET6: { 295 struct ip6_hdr *ip6; 296 297 ip6 = mtod(m, struct ip6_hdr *); 298 KASSERT(ip6->ip6_nxt == IPPROTO_ESP, 299 ("unexpected next header type %d", ip6->ip6_nxt)); 300 ip6->ip6_plen = htons(m->m_pkthdr.len); 301 ip6->ip6_nxt = IPPROTO_UDP; 302 udp->uh_sum = in6_cksum_pseudo(ip6, 303 m->m_pkthdr.len - hlen, ip6->ip6_nxt, 0); 304 m->m_pkthdr.csum_flags = CSUM_UDP_IPV6; 305 m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); 306 break; 307 } 308#endif 309 default: 310 ESPSTAT_INC(esps_nopf); 311 return (EAFNOSUPPORT); 312 } 313 314 return (0); 315} 316 317void 318udp_ipsec_adjust_cksum(struct mbuf *m, struct secasvar *sav, int proto, 319 int skip) 320{ 321 uint16_t cksum, off; 322 323 IPSEC_ASSERT(sav->natt != NULL, ("NAT-T isn't required")); 324 IPSEC_ASSERT(proto == IPPROTO_UDP || proto == IPPROTO_TCP, 325 ("unexpected protocol %u", proto)); 326 327 if (proto == IPPROTO_UDP) 328 off = offsetof(struct udphdr, uh_sum); 329 else 330 off = offsetof(struct tcphdr, th_sum); 331 332 if (V_natt_cksum_policy == 0) { /* auto */ 333 if (sav->natt->cksum != 0) { 334 /* Incrementally recompute. */ 335 m_copydata(m, skip + off, sizeof(cksum), 336 (caddr_t)&cksum); 337 /* Do not adjust UDP checksum if it is zero. */ 338 if (proto == IPPROTO_UDP && cksum == 0) 339 return; 340 cksum = in_addword(cksum, sav->natt->cksum); 341 } else { 342 /* No OA from IKEd. */ 343 if (proto == IPPROTO_TCP) { 344 /* Ignore for TCP. */ 345 m->m_pkthdr.csum_data = 0xffff; 346 switch (sav->sah->saidx.dst.sa.sa_family) { 347#ifdef INET 348 case AF_INET: 349 m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID | 350 CSUM_PSEUDO_HDR); 351 break; 352#endif 353#ifdef INET6 354 case AF_INET6: 355 m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID_IPV6 | 356 CSUM_PSEUDO_HDR); 357 break; 358#endif 359 default: 360 break; 361 } 362 return; 363 } 364 cksum = 0; /* Reset for UDP. */ 365 } 366 m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum); 367 } else { /* Fully recompute */ 368 switch (sav->sah->saidx.dst.sa.sa_family) { 369#ifdef INET 370 case AF_INET: { 371 struct ip *ip; 372 373 ip = mtod(m, struct ip *); 374 cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 375 htons(m->m_pkthdr.len - skip + proto)); 376 m_copyback(m, skip + off, sizeof(cksum), 377 (caddr_t)&cksum); 378 m->m_pkthdr.csum_flags = 379 (proto == IPPROTO_UDP) ? CSUM_UDP : CSUM_TCP; 380 m->m_pkthdr.csum_data = off; 381 in_delayed_cksum(m); 382 m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 383 break; 384 } 385#endif 386#ifdef INET6 387 case AF_INET6: { 388 struct ip6_hdr *ip6; 389 390 ip6 = mtod(m, struct ip6_hdr *); 391 cksum = in6_cksum_pseudo(ip6, m->m_pkthdr.len - skip, 392 proto, 0); 393 m_copyback(m, skip + off, sizeof(cksum), 394 (caddr_t)&cksum); 395 m->m_pkthdr.csum_flags = 396 (proto == IPPROTO_UDP) ? CSUM_UDP_IPV6 : CSUM_TCP_IPV6; 397 m->m_pkthdr.csum_data = off; 398 in6_delayed_cksum(m, 399 m->m_pkthdr.len - sizeof(struct ip6_hdr), 400 sizeof(struct ip6_hdr)); 401 m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6; 402 break; 403 } 404#endif 405 default: 406 break; 407 } 408 } 409} 410