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 __FBSDID("$FreeBSD$"); 29 30 #include "opt_inet.h" 31 #include "opt_ipsec.h" 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/kernel.h> 36 #include <sys/lock.h> 37 #include <sys/mbuf.h> 38 #include <sys/protosw.h> 39 #include <sys/socket.h> 40 #include <sys/sockopt.h> 41 #include <sys/sysctl.h> 42 43 #include <netinet/in.h> 44 #include <netinet/in_pcb.h> 45 #include <netinet/in_systm.h> 46 #include <netinet/ip.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 <net/vnet.h> 53 54 #include <netipsec/ipsec.h> 55 #include <netipsec/esp.h> 56 #include <netipsec/esp_var.h> 57 #include <netipsec/xform.h> 58 59 #include <netipsec/key.h> 60 #include <netipsec/key_debug.h> 61 #include <netipsec/ipsec_support.h> 62 #include <machine/in_cksum.h> 63 64 /* 65 * Handle UDP_ENCAP socket option. Always return with released INP_WLOCK. 66 */ 67 int 68 udp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt) 69 { 70 struct udpcb *up; 71 int error, optval; 72 73 INP_WLOCK_ASSERT(inp); 74 if (sopt->sopt_name != UDP_ENCAP) { 75 INP_WUNLOCK(inp); 76 return (ENOPROTOOPT); 77 } 78 79 up = intoudpcb(inp); 80 if (sopt->sopt_dir == SOPT_GET) { 81 if (up->u_flags & UF_ESPINUDP) 82 optval = UDP_ENCAP_ESPINUDP; 83 else 84 optval = 0; 85 INP_WUNLOCK(inp); 86 return (sooptcopyout(sopt, &optval, sizeof(optval))); 87 } 88 INP_WUNLOCK(inp); 89 90 error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval)); 91 if (error != 0) 92 return (error); 93 94 INP_WLOCK(inp); 95 switch (optval) { 96 case 0: 97 up->u_flags &= ~UF_ESPINUDP; 98 break; 99 case UDP_ENCAP_ESPINUDP: 100 up->u_flags |= UF_ESPINUDP; 101 break; 102 default: 103 error = EINVAL; 104 } 105 INP_WUNLOCK(inp); 106 return (error); 107 } 108 109 /* 110 * Potentially decap ESP in UDP frame. Check for an ESP header. 111 * If present, strip the UDP header and push the result through IPSec. 112 * 113 * Returns error if mbuf consumed and/or processed, otherwise 0. 114 */ 115 int 116 udp_ipsec_input(struct mbuf *m, int off, int af) 117 { 118 union sockaddr_union dst; 119 struct secasvar *sav; 120 struct udphdr *udp; 121 struct ip *ip; 122 uint32_t spi; 123 int error, hlen; 124 125 /* 126 * Just return if packet doesn't have enough data. 127 * We need at least [IP header + UDP header + ESP header]. 128 * NAT-Keepalive packet has only one byte of payload, so it 129 * by default will not be processed. 130 */ 131 if (m->m_pkthdr.len < off + sizeof(struct esp)) 132 return (0); 133 134 m_copydata(m, off, sizeof(uint32_t), (caddr_t)&spi); 135 if (spi == 0) /* Non-ESP marker. */ 136 return (0); 137 138 /* 139 * Find SA and check that it is configured for UDP 140 * encapsulation. 141 */ 142 bzero(&dst, sizeof(dst)); 143 dst.sa.sa_family = af; 144 switch (af) { 145 #ifdef INET 146 case AF_INET: 147 dst.sin.sin_len = sizeof(struct sockaddr_in); 148 ip = mtod(m, struct ip *); 149 ip->ip_p = IPPROTO_ESP; 150 off = offsetof(struct ip, ip_p); 151 hlen = ip->ip_hl << 2; 152 dst.sin.sin_addr = ip->ip_dst; 153 break; 154 #endif 155 #ifdef INET6 156 case AF_INET6: 157 /* Not yet */ 158 /* FALLTHROUGH */ 159 #endif 160 default: 161 ESPSTAT_INC(esps_nopf); 162 m_freem(m); 163 return (EPFNOSUPPORT); 164 } 165 166 sav = key_allocsa(&dst, IPPROTO_ESP, spi); 167 if (sav == NULL) { 168 ESPSTAT_INC(esps_notdb); 169 m_freem(m); 170 return (ENOENT); 171 } 172 udp = mtodo(m, hlen); 173 if (sav->natt == NULL || 174 sav->natt->sport != udp->uh_sport || 175 sav->natt->dport != udp->uh_dport) { 176 /* XXXAE: should we check source address? */ 177 ESPSTAT_INC(esps_notdb); 178 key_freesav(&sav); 179 m_freem(m); 180 return (ENOENT); 181 } 182 /* 183 * Remove the UDP header 184 * Before: 185 * <--- off ---> 186 * +----+------+-----+ 187 * | IP | UDP | ESP | 188 * +----+------+-----+ 189 * <-skip-> 190 * After: 191 * +----+-----+ 192 * | IP | ESP | 193 * +----+-----+ 194 * <-skip-> 195 */ 196 m_striphdr(m, hlen, sizeof(*udp)); 197 /* 198 * We cannot yet update the cksums so clear any h/w cksum flags 199 * as they are no longer valid. 200 */ 201 if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) 202 m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID | CSUM_PSEUDO_HDR); 203 /* 204 * We can update ip_len and ip_sum here, but ipsec4_input_cb() 205 * will do this anyway, so don't touch them here. 206 */ 207 ESPSTAT_INC(esps_input); 208 error = (*sav->tdb_xform->xf_input)(m, sav, hlen, off); 209 if (error != 0) 210 key_freesav(&sav); 211 212 return (EINPROGRESS); /* Consumed by IPsec. */ 213 } 214 215 int 216 udp_ipsec_output(struct mbuf *m, struct secasvar *sav) 217 { 218 struct udphdr *udp; 219 struct mbuf *n; 220 struct ip *ip; 221 int hlen, off; 222 223 IPSEC_ASSERT(sav->natt != NULL, ("UDP encapsulation isn't required.")); 224 225 if (sav->sah->saidx.dst.sa.sa_family == AF_INET6) 226 return (EAFNOSUPPORT); 227 228 ip = mtod(m, struct ip *); 229 hlen = ip->ip_hl << 2; 230 n = m_makespace(m, hlen, sizeof(*udp), &off); 231 if (n == NULL) { 232 DPRINTF(("%s: m_makespace for udphdr failed\n", __func__)); 233 return (ENOBUFS); 234 } 235 236 udp = mtodo(n, off); 237 udp->uh_dport = sav->natt->dport; 238 udp->uh_sport = sav->natt->sport; 239 udp->uh_sum = 0; 240 udp->uh_ulen = htons(m->m_pkthdr.len - hlen); 241 242 ip = mtod(m, struct ip *); 243 ip->ip_len = htons(m->m_pkthdr.len); 244 ip->ip_p = IPPROTO_UDP; 245 return (0); 246 } 247 248 void 249 udp_ipsec_adjust_cksum(struct mbuf *m, struct secasvar *sav, int proto, 250 int skip) 251 { 252 struct ip *ip; 253 uint16_t cksum, off; 254 255 IPSEC_ASSERT(sav->natt != NULL, ("NAT-T isn't required")); 256 IPSEC_ASSERT(proto == IPPROTO_UDP || proto == IPPROTO_TCP, 257 ("unexpected protocol %u", proto)); 258 259 if (proto == IPPROTO_UDP) 260 off = offsetof(struct udphdr, uh_sum); 261 else 262 off = offsetof(struct tcphdr, th_sum); 263 264 if (V_natt_cksum_policy == 0) { /* auto */ 265 if (sav->natt->cksum != 0) { 266 /* Incrementally recompute. */ 267 m_copydata(m, skip + off, sizeof(cksum), 268 (caddr_t)&cksum); 269 /* Do not adjust UDP checksum if it is zero. */ 270 if (proto == IPPROTO_UDP && cksum == 0) 271 return; 272 cksum = in_addword(cksum, sav->natt->cksum); 273 } else { 274 /* No OA from IKEd. */ 275 if (proto == IPPROTO_TCP) { 276 /* Ignore for TCP. */ 277 m->m_pkthdr.csum_data = 0xffff; 278 m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID | 279 CSUM_PSEUDO_HDR); 280 return; 281 } 282 cksum = 0; /* Reset for UDP. */ 283 } 284 m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum); 285 } else { /* Fully recompute */ 286 ip = mtod(m, struct ip *); 287 cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 288 htons(m->m_pkthdr.len - skip + proto)); 289 m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum); 290 m->m_pkthdr.csum_flags = 291 (proto == IPPROTO_UDP) ? CSUM_UDP: CSUM_TCP; 292 m->m_pkthdr.csum_data = off; 293 in_delayed_cksum(m); 294 m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 295 } 296 } 297 298