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