File: | netinet/ipsec_output.c |
Warning: | line 177, column 17 The right operand of '<' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: ipsec_output.c,v 1.97 2022/01/02 22:36:04 jsg Exp $ */ | |||
2 | /* | |||
3 | * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) | |||
4 | * | |||
5 | * Copyright (c) 2000-2001 Angelos D. Keromytis. | |||
6 | * | |||
7 | * Permission to use, copy, and modify this software with or without fee | |||
8 | * is hereby granted, provided that this entire notice is included in | |||
9 | * all copies of any software which is or includes a copy or | |||
10 | * modification of this software. | |||
11 | * You may use this code under the GNU public license if you so wish. Please | |||
12 | * contribute changes back to the authors under this freer than GPL license | |||
13 | * so that we may further the use of strong encryption without limitations to | |||
14 | * all. | |||
15 | * | |||
16 | * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR | |||
17 | * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY | |||
18 | * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE | |||
19 | * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR | |||
20 | * PURPOSE. | |||
21 | */ | |||
22 | ||||
23 | #include "pf.h" | |||
24 | ||||
25 | #include <sys/param.h> | |||
26 | #include <sys/systm.h> | |||
27 | #include <sys/mbuf.h> | |||
28 | #include <sys/socket.h> | |||
29 | #include <sys/kernel.h> | |||
30 | #include <sys/timeout.h> | |||
31 | ||||
32 | #include <net/if.h> | |||
33 | #include <net/route.h> | |||
34 | ||||
35 | #include <netinet/in.h> | |||
36 | #include <netinet/ip.h> | |||
37 | #include <netinet/in_pcb.h> | |||
38 | #include <netinet/ip_var.h> | |||
39 | ||||
40 | #if NPF1 > 0 | |||
41 | #include <net/pfvar.h> | |||
42 | #endif | |||
43 | ||||
44 | #include <netinet/udp.h> | |||
45 | #include <netinet/ip_ipip.h> | |||
46 | #include <netinet/ip_ah.h> | |||
47 | #include <netinet/ip_esp.h> | |||
48 | #include <netinet/ip_ipcomp.h> | |||
49 | ||||
50 | #include <crypto/cryptodev.h> | |||
51 | #include <crypto/xform.h> | |||
52 | ||||
53 | #ifdef ENCDEBUG | |||
54 | #define DPRINTF(fmt, args...)do { } while (0) \ | |||
55 | do { \ | |||
56 | if (encdebug) \ | |||
57 | printf("%s: " fmt "\n", __func__, ## args); \ | |||
58 | } while (0) | |||
59 | #else | |||
60 | #define DPRINTF(fmt, args...)do { } while (0) \ | |||
61 | do { } while (0) | |||
62 | #endif | |||
63 | ||||
64 | int udpencap_enable = 1; /* enabled by default */ | |||
65 | int udpencap_port = 4500; /* triggers decapsulation */ | |||
66 | ||||
67 | /* | |||
68 | * Loop over a tdb chain, taking into consideration protocol tunneling. The | |||
69 | * fourth argument is set if the first encapsulation header is already in | |||
70 | * place. | |||
71 | */ | |||
72 | int | |||
73 | ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready) | |||
74 | { | |||
75 | int hlen, off, error; | |||
| ||||
76 | #ifdef INET61 | |||
77 | struct ip6_ext ip6e; | |||
78 | int nxt; | |||
79 | int dstopt = 0; | |||
80 | #endif | |||
81 | ||||
82 | int setdf = 0; | |||
83 | struct ip *ip; | |||
84 | #ifdef INET61 | |||
85 | struct ip6_hdr *ip6; | |||
86 | #endif /* INET6 */ | |||
87 | ||||
88 | #ifdef ENCDEBUG | |||
89 | char buf[INET6_ADDRSTRLEN46]; | |||
90 | #endif | |||
91 | ||||
92 | /* Check that the transform is allowed by the administrator. */ | |||
93 | if ((tdb->tdb_sproto == IPPROTO_ESP50 && !esp_enable) || | |||
94 | (tdb->tdb_sproto == IPPROTO_AH51 && !ah_enable) || | |||
95 | (tdb->tdb_sproto == IPPROTO_IPCOMP108 && !ipcomp_enable)) { | |||
96 | DPRINTF("IPsec outbound packet dropped due to policy "do { } while (0) | |||
97 | "(check your sysctls)")do { } while (0); | |||
98 | error = EHOSTUNREACH65; | |||
99 | goto drop; | |||
100 | } | |||
101 | ||||
102 | /* Sanity check. */ | |||
103 | if (!tdb->tdb_xform) { | |||
104 | DPRINTF("uninitialized TDB")do { } while (0); | |||
105 | error = EHOSTUNREACH65; | |||
106 | goto drop; | |||
107 | } | |||
108 | ||||
109 | /* Check if the SPI is invalid. */ | |||
110 | if (tdb->tdb_flags & TDBF_INVALID0x00010) { | |||
111 | DPRINTF("attempt to use invalid SA %s/%08x/%u",do { } while (0) | |||
112 | ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),do { } while (0) | |||
113 | ntohl(tdb->tdb_spi), tdb->tdb_sproto)do { } while (0); | |||
114 | error = ENXIO6; | |||
115 | goto drop; | |||
116 | } | |||
117 | ||||
118 | /* Check that the network protocol is supported */ | |||
119 | switch (tdb->tdb_dst.sa.sa_family) { | |||
120 | case AF_INET2: | |||
121 | break; | |||
122 | ||||
123 | #ifdef INET61 | |||
124 | case AF_INET624: | |||
125 | break; | |||
126 | #endif /* INET6 */ | |||
127 | ||||
128 | default: | |||
129 | DPRINTF("attempt to use SA %s/%08x/%u for protocol family %d",do { } while (0) | |||
130 | ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),do { } while (0) | |||
131 | ntohl(tdb->tdb_spi), tdb->tdb_sproto,do { } while (0) | |||
132 | tdb->tdb_dst.sa.sa_family)do { } while (0); | |||
133 | error = EPFNOSUPPORT46; | |||
134 | goto drop; | |||
135 | } | |||
136 | ||||
137 | /* | |||
138 | * Register first use if applicable, setup relevant expiration timer. | |||
139 | */ | |||
140 | if (tdb->tdb_first_use == 0) { | |||
141 | tdb->tdb_first_use = gettime(); | |||
142 | if (tdb->tdb_flags & TDBF_FIRSTUSE0x00020) { | |||
143 | if (timeout_add_sec(&tdb->tdb_first_tmo, | |||
144 | tdb->tdb_exp_first_use)) | |||
145 | tdb_ref(tdb); | |||
146 | } | |||
147 | if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE0x00400) { | |||
148 | if (timeout_add_sec(&tdb->tdb_sfirst_tmo, | |||
149 | tdb->tdb_soft_first_use)) | |||
150 | tdb_ref(tdb); | |||
151 | } | |||
152 | } | |||
153 | ||||
154 | /* | |||
155 | * Check for tunneling if we don't have the first header in place. | |||
156 | * When doing Ethernet-over-IP, we are handed an already-encapsulated | |||
157 | * frame, so we don't need to re-encapsulate. | |||
158 | */ | |||
159 | if (tunalready == 0) { | |||
160 | /* | |||
161 | * If the target protocol family is different, we know we'll be | |||
162 | * doing tunneling. | |||
163 | */ | |||
164 | if (af == tdb->tdb_dst.sa.sa_family) { | |||
165 | switch (af) { | |||
166 | case AF_INET2: | |||
167 | hlen = sizeof(struct ip); | |||
168 | break; | |||
169 | #ifdef INET61 | |||
170 | case AF_INET624: | |||
171 | hlen = sizeof(struct ip6_hdr); | |||
172 | break; | |||
173 | #endif /* INET6 */ | |||
174 | } | |||
175 | ||||
176 | /* Bring the network header in the first mbuf. */ | |||
177 | if (m->m_lenm_hdr.mh_len < hlen) { | |||
| ||||
178 | if ((m = m_pullup(m, hlen)) == NULL((void *)0)) { | |||
179 | error = ENOBUFS55; | |||
180 | goto drop; | |||
181 | } | |||
182 | } | |||
183 | ||||
184 | if (af == AF_INET2) { | |||
185 | ip = mtod(m, struct ip *)((struct ip *)((m)->m_hdr.mh_data)); | |||
186 | ||||
187 | /* | |||
188 | * This is not a bridge packet, remember if we | |||
189 | * had IP_DF. | |||
190 | */ | |||
191 | setdf = ip->ip_off & htons(IP_DF)(__uint16_t)(__builtin_constant_p(0x4000) ? (__uint16_t)(((__uint16_t )(0x4000) & 0xffU) << 8 | ((__uint16_t)(0x4000) & 0xff00U) >> 8) : __swap16md(0x4000)); | |||
192 | } | |||
193 | ||||
194 | #ifdef INET61 | |||
195 | if (af == AF_INET624) | |||
196 | ip6 = mtod(m, struct ip6_hdr *)((struct ip6_hdr *)((m)->m_hdr.mh_data)); | |||
197 | #endif /* INET6 */ | |||
198 | } | |||
199 | ||||
200 | /* Do the appropriate encapsulation, if necessary. */ | |||
201 | if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */ | |||
202 | (tdb->tdb_flags & TDBF_TUNNELING0x01000) || /* Tunneling needed */ | |||
203 | (tdb->tdb_xform->xf_type == XF_IP41) || /* ditto */ | |||
204 | ((tdb->tdb_dst.sa.sa_family == AF_INET2) && | |||
205 | (tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY((u_int32_t) (__uint32_t)(__builtin_constant_p((u_int32_t)(0x00000000 )) ? (__uint32_t)(((__uint32_t)((u_int32_t)(0x00000000)) & 0xff) << 24 | ((__uint32_t)((u_int32_t)(0x00000000)) & 0xff00) << 8 | ((__uint32_t)((u_int32_t)(0x00000000)) & 0xff0000) >> 8 | ((__uint32_t)((u_int32_t)(0x00000000) ) & 0xff000000) >> 24) : __swap32md((u_int32_t)(0x00000000 ))))) && | |||
206 | (tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) || | |||
207 | #ifdef INET61 | |||
208 | ((tdb->tdb_dst.sa.sa_family == AF_INET624) && | |||
209 | (!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)((*(const u_int32_t *)(const void *)(&(&tdb->tdb_dst .sin6.sin6_addr)->__u6_addr.__u6_addr8[0]) == 0) && (*(const u_int32_t *)(const void *)(&(&tdb->tdb_dst .sin6.sin6_addr)->__u6_addr.__u6_addr8[4]) == 0) && (*(const u_int32_t *)(const void *)(&(&tdb->tdb_dst .sin6.sin6_addr)->__u6_addr.__u6_addr8[8]) == 0) && (*(const u_int32_t *)(const void *)(&(&tdb->tdb_dst .sin6.sin6_addr)->__u6_addr.__u6_addr8[12]) == 0))) && | |||
210 | (!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr,(__builtin_memcmp((&(&tdb->tdb_dst.sin6.sin6_addr) ->__u6_addr.__u6_addr8[0]), (&(&ip6->ip6_dst)-> __u6_addr.__u6_addr8[0]), (sizeof(struct in6_addr))) == 0) | |||
211 | &ip6->ip6_dst)(__builtin_memcmp((&(&tdb->tdb_dst.sin6.sin6_addr) ->__u6_addr.__u6_addr8[0]), (&(&ip6->ip6_dst)-> __u6_addr.__u6_addr8[0]), (sizeof(struct in6_addr))) == 0))) || | |||
212 | #endif /* INET6 */ | |||
213 | 0) { | |||
214 | /* Fix IPv4 header checksum and length. */ | |||
215 | if (af == AF_INET2) { | |||
216 | if (m->m_lenm_hdr.mh_len < sizeof(struct ip)) | |||
217 | if ((m = m_pullup(m, | |||
218 | sizeof(struct ip))) == NULL((void *)0)) { | |||
219 | error = ENOBUFS55; | |||
220 | goto drop; | |||
221 | } | |||
222 | ||||
223 | ip = mtod(m, struct ip *)((struct ip *)((m)->m_hdr.mh_data)); | |||
224 | ip->ip_len = htons(m->m_pkthdr.len)(__uint16_t)(__builtin_constant_p(m->M_dat.MH.MH_pkthdr.len ) ? (__uint16_t)(((__uint16_t)(m->M_dat.MH.MH_pkthdr.len) & 0xffU) << 8 | ((__uint16_t)(m->M_dat.MH.MH_pkthdr.len ) & 0xff00U) >> 8) : __swap16md(m->M_dat.MH.MH_pkthdr .len)); | |||
225 | ip->ip_sum = 0; | |||
226 | ip->ip_sum = in_cksum(m, ip->ip_hl << 2); | |||
227 | } | |||
228 | ||||
229 | #ifdef INET61 | |||
230 | /* Fix IPv6 header payload length. */ | |||
231 | if (af == AF_INET624) { | |||
232 | if (m->m_lenm_hdr.mh_len < sizeof(struct ip6_hdr)) | |||
233 | if ((m = m_pullup(m, | |||
234 | sizeof(struct ip6_hdr))) == NULL((void *)0)) { | |||
235 | error = ENOBUFS55; | |||
236 | goto drop; | |||
237 | } | |||
238 | ||||
239 | if (m->m_pkthdrM_dat.MH.MH_pkthdr.len - sizeof(*ip6) > | |||
240 | IPV6_MAXPACKET65535) { | |||
241 | /* No jumbogram support. */ | |||
242 | error = ENXIO6; /*?*/ | |||
243 | goto drop; | |||
244 | } | |||
245 | ip6 = mtod(m, struct ip6_hdr *)((struct ip6_hdr *)((m)->m_hdr.mh_data)); | |||
246 | ip6->ip6_plenip6_ctlun.ip6_un1.ip6_un1_plen = htons(m->m_pkthdr.len(__uint16_t)(__builtin_constant_p(m->M_dat.MH.MH_pkthdr.len - sizeof(*ip6)) ? (__uint16_t)(((__uint16_t)(m->M_dat.MH. MH_pkthdr.len - sizeof(*ip6)) & 0xffU) << 8 | ((__uint16_t )(m->M_dat.MH.MH_pkthdr.len - sizeof(*ip6)) & 0xff00U) >> 8) : __swap16md(m->M_dat.MH.MH_pkthdr.len - sizeof (*ip6))) | |||
247 | - sizeof(*ip6))(__uint16_t)(__builtin_constant_p(m->M_dat.MH.MH_pkthdr.len - sizeof(*ip6)) ? (__uint16_t)(((__uint16_t)(m->M_dat.MH. MH_pkthdr.len - sizeof(*ip6)) & 0xffU) << 8 | ((__uint16_t )(m->M_dat.MH.MH_pkthdr.len - sizeof(*ip6)) & 0xff00U) >> 8) : __swap16md(m->M_dat.MH.MH_pkthdr.len - sizeof (*ip6))); | |||
248 | } | |||
249 | #endif /* INET6 */ | |||
250 | ||||
251 | /* Encapsulate -- m may be changed or set to NULL. */ | |||
252 | error = ipip_output(&m, tdb); | |||
253 | if ((m == NULL((void *)0)) && (!error)) | |||
254 | error = EFAULT14; | |||
255 | if (error) | |||
256 | goto drop; | |||
257 | ||||
258 | if (tdb->tdb_dst.sa.sa_family == AF_INET2 && setdf) { | |||
259 | if (m->m_lenm_hdr.mh_len < sizeof(struct ip)) | |||
260 | if ((m = m_pullup(m, | |||
261 | sizeof(struct ip))) == NULL((void *)0)) { | |||
262 | error = ENOBUFS55; | |||
263 | goto drop; | |||
264 | } | |||
265 | ||||
266 | ip = mtod(m, struct ip *)((struct ip *)((m)->m_hdr.mh_data)); | |||
267 | ip->ip_off |= htons(IP_DF)(__uint16_t)(__builtin_constant_p(0x4000) ? (__uint16_t)(((__uint16_t )(0x4000) & 0xffU) << 8 | ((__uint16_t)(0x4000) & 0xff00U) >> 8) : __swap16md(0x4000)); | |||
268 | } | |||
269 | ||||
270 | /* Remember that we appended a tunnel header. */ | |||
271 | mtx_enter(&tdb->tdb_mtx); | |||
272 | tdb->tdb_flags |= TDBF_USEDTUNNEL0x10000; | |||
273 | mtx_leave(&tdb->tdb_mtx); | |||
274 | } | |||
275 | } | |||
276 | ||||
277 | /* | |||
278 | * If this is just an IP-IP TDB and we're told there's already an | |||
279 | * encapsulation header or ipip_output() has encapsulated it, move on. | |||
280 | */ | |||
281 | if (tdb->tdb_xform->xf_type == XF_IP41) | |||
282 | return ipsp_process_done(m, tdb); | |||
283 | ||||
284 | /* Extract some information off the headers. */ | |||
285 | switch (tdb->tdb_dst.sa.sa_family) { | |||
286 | case AF_INET2: | |||
287 | ip = mtod(m, struct ip *)((struct ip *)((m)->m_hdr.mh_data)); | |||
288 | hlen = ip->ip_hl << 2; | |||
289 | off = offsetof(struct ip, ip_p)__builtin_offsetof(struct ip, ip_p); | |||
290 | break; | |||
291 | ||||
292 | #ifdef INET61 | |||
293 | case AF_INET624: | |||
294 | ip6 = mtod(m, struct ip6_hdr *)((struct ip6_hdr *)((m)->m_hdr.mh_data)); | |||
295 | hlen = sizeof(struct ip6_hdr); | |||
296 | off = offsetof(struct ip6_hdr, ip6_nxt)__builtin_offsetof(struct ip6_hdr, ip6_ctlun.ip6_un1.ip6_un1_nxt ); | |||
297 | nxt = ip6->ip6_nxtip6_ctlun.ip6_un1.ip6_un1_nxt; | |||
298 | /* | |||
299 | * chase mbuf chain to find the appropriate place to | |||
300 | * put AH/ESP/IPcomp header. | |||
301 | * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload] | |||
302 | */ | |||
303 | do { | |||
304 | switch (nxt) { | |||
305 | case IPPROTO_AH51: | |||
306 | case IPPROTO_ESP50: | |||
307 | case IPPROTO_IPCOMP108: | |||
308 | /* | |||
309 | * we should not skip security header added | |||
310 | * beforehand. | |||
311 | */ | |||
312 | goto exitip6loop; | |||
313 | ||||
314 | case IPPROTO_HOPOPTS0: | |||
315 | case IPPROTO_DSTOPTS60: | |||
316 | case IPPROTO_ROUTING43: | |||
317 | /* | |||
318 | * if we see 2nd destination option header, | |||
319 | * we should stop there. | |||
320 | */ | |||
321 | if (nxt == IPPROTO_DSTOPTS60 && dstopt) | |||
322 | goto exitip6loop; | |||
323 | ||||
324 | if (nxt == IPPROTO_DSTOPTS60) { | |||
325 | /* | |||
326 | * seen 1st or 2nd destination option. | |||
327 | * next time we see one, it must be 2nd. | |||
328 | */ | |||
329 | dstopt = 1; | |||
330 | } else if (nxt == IPPROTO_ROUTING43) { | |||
331 | /* | |||
332 | * if we see destination option next | |||
333 | * time, it must be dest2. | |||
334 | */ | |||
335 | dstopt = 2; | |||
336 | } | |||
337 | if (m->m_pkthdrM_dat.MH.MH_pkthdr.len < hlen + sizeof(ip6e)) { | |||
338 | error = EINVAL22; | |||
339 | goto drop; | |||
340 | } | |||
341 | /* skip this header */ | |||
342 | m_copydata(m, hlen, sizeof(ip6e), | |||
343 | (caddr_t)&ip6e); | |||
344 | nxt = ip6e.ip6e_nxt; | |||
345 | off = hlen + offsetof(struct ip6_ext, ip6e_nxt)__builtin_offsetof(struct ip6_ext, ip6e_nxt); | |||
346 | /* | |||
347 | * we will never see nxt == IPPROTO_AH | |||
348 | * so it is safe to omit AH case. | |||
349 | */ | |||
350 | hlen += (ip6e.ip6e_len + 1) << 3; | |||
351 | break; | |||
352 | default: | |||
353 | goto exitip6loop; | |||
354 | } | |||
355 | } while (hlen < m->m_pkthdrM_dat.MH.MH_pkthdr.len); | |||
356 | exitip6loop: | |||
357 | break; | |||
358 | #endif /* INET6 */ | |||
359 | default: | |||
360 | error = EPFNOSUPPORT46; | |||
361 | goto drop; | |||
362 | } | |||
363 | ||||
364 | if (m->m_pkthdrM_dat.MH.MH_pkthdr.len < hlen) { | |||
365 | error = EINVAL22; | |||
366 | goto drop; | |||
367 | } | |||
368 | ||||
369 | ipsecstat_add(ipsec_ouncompbytes, m->m_pkthdrM_dat.MH.MH_pkthdr.len); | |||
370 | tdbstat_add(tdb, tdb_ouncompbytes, m->m_pkthdrM_dat.MH.MH_pkthdr.len); | |||
371 | ||||
372 | /* Non expansion policy for IPCOMP */ | |||
373 | if (tdb->tdb_sproto == IPPROTO_IPCOMP108) { | |||
374 | if ((m->m_pkthdrM_dat.MH.MH_pkthdr.len - hlen) < tdb->tdb_compalgxform->minlen) { | |||
375 | /* No need to compress, leave the packet untouched */ | |||
376 | ipcompstat_inc(ipcomps_minlen); | |||
377 | return ipsp_process_done(m, tdb); | |||
378 | } | |||
379 | } | |||
380 | ||||
381 | /* Invoke the IPsec transform. */ | |||
382 | return (*(tdb->tdb_xform->xf_output))(m, tdb, hlen, off); | |||
383 | ||||
384 | drop: | |||
385 | m_freem(m); | |||
386 | return error; | |||
387 | } | |||
388 | ||||
389 | /* | |||
390 | * Called by the IPsec output transform callbacks, to transmit the packet | |||
391 | * or do further processing, as necessary. | |||
392 | */ | |||
393 | int | |||
394 | ipsp_process_done(struct mbuf *m, struct tdb *tdb) | |||
395 | { | |||
396 | struct ip *ip; | |||
397 | #ifdef INET61 | |||
398 | struct ip6_hdr *ip6; | |||
399 | #endif /* INET6 */ | |||
400 | struct tdb *tdbo; | |||
401 | struct tdb_ident *tdbi; | |||
402 | struct m_tag *mtag; | |||
403 | int roff, error; | |||
404 | ||||
405 | NET_ASSERT_LOCKED()do { int _s = rw_status(&netlock); if ((splassert_ctl > 0) && (_s != 0x0001UL && _s != 0x0002UL)) splassert_fail (0x0002UL, _s, __func__); } while (0); | |||
406 | ||||
407 | tdb->tdb_last_used = gettime(); | |||
408 | ||||
409 | if ((tdb->tdb_flags & TDBF_UDPENCAP0x20000) != 0) { | |||
410 | struct mbuf *mi; | |||
411 | struct udphdr *uh; | |||
412 | int iphlen; | |||
413 | ||||
414 | if (!udpencap_enable || !udpencap_port) { | |||
415 | error = ENXIO6; | |||
416 | goto drop; | |||
417 | } | |||
418 | ||||
419 | switch (tdb->tdb_dst.sa.sa_family) { | |||
420 | case AF_INET2: | |||
421 | iphlen = sizeof(struct ip); | |||
422 | break; | |||
423 | #ifdef INET61 | |||
424 | case AF_INET624: | |||
425 | iphlen = sizeof(struct ip6_hdr); | |||
426 | break; | |||
427 | #endif /* INET6 */ | |||
428 | default: | |||
429 | DPRINTF("unknown protocol family (%d)",do { } while (0) | |||
430 | tdb->tdb_dst.sa.sa_family)do { } while (0); | |||
431 | error = EPFNOSUPPORT46; | |||
432 | goto drop; | |||
433 | } | |||
434 | ||||
435 | mi = m_makespace(m, iphlen, sizeof(struct udphdr), &roff); | |||
436 | if (mi == NULL((void *)0)) { | |||
437 | error = ENOMEM12; | |||
438 | goto drop; | |||
439 | } | |||
440 | uh = (struct udphdr *)(mtod(mi, caddr_t)((caddr_t)((mi)->m_hdr.mh_data)) + roff); | |||
441 | uh->uh_sport = uh->uh_dport = htons(udpencap_port)(__uint16_t)(__builtin_constant_p(udpencap_port) ? (__uint16_t )(((__uint16_t)(udpencap_port) & 0xffU) << 8 | ((__uint16_t )(udpencap_port) & 0xff00U) >> 8) : __swap16md(udpencap_port )); | |||
442 | if (tdb->tdb_udpencap_port) | |||
443 | uh->uh_dport = tdb->tdb_udpencap_port; | |||
444 | ||||
445 | uh->uh_ulen = htons(m->m_pkthdr.len - iphlen)(__uint16_t)(__builtin_constant_p(m->M_dat.MH.MH_pkthdr.len - iphlen) ? (__uint16_t)(((__uint16_t)(m->M_dat.MH.MH_pkthdr .len - iphlen) & 0xffU) << 8 | ((__uint16_t)(m-> M_dat.MH.MH_pkthdr.len - iphlen) & 0xff00U) >> 8) : __swap16md(m->M_dat.MH.MH_pkthdr.len - iphlen)); | |||
446 | uh->uh_sum = 0; | |||
447 | #ifdef INET61 | |||
448 | if (tdb->tdb_dst.sa.sa_family == AF_INET624) | |||
449 | m->m_pkthdrM_dat.MH.MH_pkthdr.csum_flags |= M_UDP_CSUM_OUT0x0004; | |||
450 | #endif /* INET6 */ | |||
451 | espstat_inc(esps_udpencout); | |||
452 | } | |||
453 | ||||
454 | switch (tdb->tdb_dst.sa.sa_family) { | |||
455 | case AF_INET2: | |||
456 | /* Fix the header length, for AH processing. */ | |||
457 | ip = mtod(m, struct ip *)((struct ip *)((m)->m_hdr.mh_data)); | |||
458 | ip->ip_len = htons(m->m_pkthdr.len)(__uint16_t)(__builtin_constant_p(m->M_dat.MH.MH_pkthdr.len ) ? (__uint16_t)(((__uint16_t)(m->M_dat.MH.MH_pkthdr.len) & 0xffU) << 8 | ((__uint16_t)(m->M_dat.MH.MH_pkthdr.len ) & 0xff00U) >> 8) : __swap16md(m->M_dat.MH.MH_pkthdr .len)); | |||
459 | if ((tdb->tdb_flags & TDBF_UDPENCAP0x20000) != 0) | |||
460 | ip->ip_p = IPPROTO_UDP17; | |||
461 | break; | |||
462 | ||||
463 | #ifdef INET61 | |||
464 | case AF_INET624: | |||
465 | /* Fix the header length, for AH processing. */ | |||
466 | if (m->m_pkthdrM_dat.MH.MH_pkthdr.len < sizeof(*ip6)) { | |||
467 | error = ENXIO6; | |||
468 | goto drop; | |||
469 | } | |||
470 | if (m->m_pkthdrM_dat.MH.MH_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET65535) { | |||
471 | /* No jumbogram support. */ | |||
472 | error = ENXIO6; | |||
473 | goto drop; | |||
474 | } | |||
475 | ip6 = mtod(m, struct ip6_hdr *)((struct ip6_hdr *)((m)->m_hdr.mh_data)); | |||
476 | ip6->ip6_plenip6_ctlun.ip6_un1.ip6_un1_plen = htons(m->m_pkthdr.len - sizeof(*ip6))(__uint16_t)(__builtin_constant_p(m->M_dat.MH.MH_pkthdr.len - sizeof(*ip6)) ? (__uint16_t)(((__uint16_t)(m->M_dat.MH. MH_pkthdr.len - sizeof(*ip6)) & 0xffU) << 8 | ((__uint16_t )(m->M_dat.MH.MH_pkthdr.len - sizeof(*ip6)) & 0xff00U) >> 8) : __swap16md(m->M_dat.MH.MH_pkthdr.len - sizeof (*ip6))); | |||
477 | if ((tdb->tdb_flags & TDBF_UDPENCAP0x20000) != 0) | |||
478 | ip6->ip6_nxtip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_UDP17; | |||
479 | break; | |||
480 | #endif /* INET6 */ | |||
481 | ||||
482 | default: | |||
483 | DPRINTF("unknown protocol family (%d)",do { } while (0) | |||
484 | tdb->tdb_dst.sa.sa_family)do { } while (0); | |||
485 | error = EPFNOSUPPORT46; | |||
486 | goto drop; | |||
487 | } | |||
488 | ||||
489 | /* | |||
490 | * Add a record of what we've done or what needs to be done to the | |||
491 | * packet. | |||
492 | */ | |||
493 | mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE0x0002, sizeof(struct tdb_ident), | |||
494 | M_NOWAIT0x0002); | |||
495 | if (mtag == NULL((void *)0)) { | |||
496 | DPRINTF("could not allocate packet tag")do { } while (0); | |||
497 | error = ENOMEM12; | |||
498 | goto drop; | |||
499 | } | |||
500 | ||||
501 | tdbi = (struct tdb_ident *)(mtag + 1); | |||
502 | tdbi->dst = tdb->tdb_dst; | |||
503 | tdbi->proto = tdb->tdb_sproto; | |||
504 | tdbi->spi = tdb->tdb_spi; | |||
505 | tdbi->rdomain = tdb->tdb_rdomain; | |||
506 | ||||
507 | m_tag_prepend(m, mtag); | |||
508 | ||||
509 | ipsecstat_pkt(ipsec_opackets, ipsec_obytes, m->m_pkthdrM_dat.MH.MH_pkthdr.len); | |||
510 | tdbstat_pkt(tdb, tdb_opackets, tdb_obytes, m->m_pkthdrM_dat.MH.MH_pkthdr.len); | |||
511 | ||||
512 | /* If there's another (bundled) TDB to apply, do so. */ | |||
513 | tdbo = tdb_ref(tdb->tdb_onext); | |||
514 | if (tdbo != NULL((void *)0)) { | |||
515 | KERNEL_ASSERT_LOCKED()((_kernel_lock_held()) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/netinet/ipsec_output.c" , 515, "_kernel_lock_held()")); | |||
516 | error = ipsp_process_packet(m, tdbo, | |||
517 | tdb->tdb_dst.sa.sa_family, 0); | |||
518 | tdb_unref(tdbo); | |||
519 | return error; | |||
520 | } | |||
521 | ||||
522 | #if NPF1 > 0 | |||
523 | /* Add pf tag if requested. */ | |||
524 | pf_tag_packet(m, tdb->tdb_tag, -1); | |||
525 | pf_pkt_addr_changed(m); | |||
526 | #endif | |||
527 | if (tdb->tdb_rdomain != tdb->tdb_rdomain_post) | |||
528 | m->m_pkthdrM_dat.MH.MH_pkthdr.ph_rtableid = tdb->tdb_rdomain_post; | |||
529 | ||||
530 | /* | |||
531 | * We're done with IPsec processing, transmit the packet using the | |||
532 | * appropriate network protocol (IP or IPv6). SPD lookup will be | |||
533 | * performed again there. | |||
534 | */ | |||
535 | switch (tdb->tdb_dst.sa.sa_family) { | |||
536 | case AF_INET2: | |||
537 | error = ip_output(m, NULL((void *)0), NULL((void *)0), IP_RAWOUTPUT0x2, NULL((void *)0), NULL((void *)0), 0); | |||
538 | break; | |||
539 | #ifdef INET61 | |||
540 | case AF_INET624: | |||
541 | /* | |||
542 | * We don't need massage, IPv6 header fields are always in | |||
543 | * net endian. | |||
544 | */ | |||
545 | error = ip6_output(m, NULL((void *)0), NULL((void *)0), 0, NULL((void *)0), NULL((void *)0)); | |||
546 | break; | |||
547 | #endif /* INET6 */ | |||
548 | default: | |||
549 | error = EPFNOSUPPORT46; | |||
550 | break; | |||
551 | } | |||
552 | return error; | |||
553 | ||||
554 | drop: | |||
555 | m_freem(m); | |||
556 | return error; | |||
557 | } | |||
558 | ||||
559 | ssize_t | |||
560 | ipsec_hdrsz(struct tdb *tdbp) | |||
561 | { | |||
562 | ssize_t adjust; | |||
563 | ||||
564 | switch (tdbp->tdb_sproto) { | |||
565 | case IPPROTO_IPIP4: | |||
566 | adjust = 0; | |||
567 | break; | |||
568 | ||||
569 | case IPPROTO_ESP50: | |||
570 | if (tdbp->tdb_encalgxform == NULL((void *)0)) | |||
571 | return (-1); | |||
572 | ||||
573 | /* Header length */ | |||
574 | adjust = 2 * sizeof(u_int32_t) + tdbp->tdb_ivlen; | |||
575 | if (tdbp->tdb_flags & TDBF_UDPENCAP0x20000) | |||
576 | adjust += sizeof(struct udphdr); | |||
577 | /* Authenticator */ | |||
578 | if (tdbp->tdb_authalgxform != NULL((void *)0)) | |||
579 | adjust += tdbp->tdb_authalgxform->authsize; | |||
580 | /* Padding */ | |||
581 | adjust += MAX(4, tdbp->tdb_encalgxform->blocksize)(((4)>(tdbp->tdb_encalgxform->blocksize))?(4):(tdbp-> tdb_encalgxform->blocksize)); | |||
582 | break; | |||
583 | ||||
584 | case IPPROTO_AH51: | |||
585 | if (tdbp->tdb_authalgxform == NULL((void *)0)) | |||
586 | return (-1); | |||
587 | ||||
588 | adjust = AH_FLENGTH8 + sizeof(u_int32_t); | |||
589 | adjust += tdbp->tdb_authalgxform->authsize; | |||
590 | break; | |||
591 | ||||
592 | default: | |||
593 | return (-1); | |||
594 | } | |||
595 | ||||
596 | if (!(tdbp->tdb_flags & TDBF_TUNNELING0x01000) && | |||
597 | !(tdbp->tdb_flags & TDBF_USEDTUNNEL0x10000)) | |||
598 | return (adjust); | |||
599 | ||||
600 | switch (tdbp->tdb_dst.sa.sa_family) { | |||
601 | case AF_INET2: | |||
602 | adjust += sizeof(struct ip); | |||
603 | break; | |||
604 | #ifdef INET61 | |||
605 | case AF_INET624: | |||
606 | adjust += sizeof(struct ip6_hdr); | |||
607 | break; | |||
608 | #endif /* INET6 */ | |||
609 | } | |||
610 | ||||
611 | return (adjust); | |||
612 | } | |||
613 | ||||
614 | void | |||
615 | ipsec_adjust_mtu(struct mbuf *m, u_int32_t mtu) | |||
616 | { | |||
617 | struct tdb_ident *tdbi; | |||
618 | struct tdb *tdbp; | |||
619 | struct m_tag *mtag; | |||
620 | ssize_t adjust; | |||
621 | ||||
622 | NET_ASSERT_LOCKED()do { int _s = rw_status(&netlock); if ((splassert_ctl > 0) && (_s != 0x0001UL && _s != 0x0002UL)) splassert_fail (0x0002UL, _s, __func__); } while (0); | |||
623 | ||||
624 | for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE0x0002, NULL((void *)0)); mtag; | |||
625 | mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE0x0002, mtag)) { | |||
626 | tdbi = (struct tdb_ident *)(mtag + 1); | |||
627 | tdbp = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst,gettdb_dir((tdbi->rdomain),(tdbi->spi),(&tdbi->dst ),(tdbi->proto),0) | |||
628 | tdbi->proto)gettdb_dir((tdbi->rdomain),(tdbi->spi),(&tdbi->dst ),(tdbi->proto),0); | |||
629 | if (tdbp == NULL((void *)0)) | |||
630 | break; | |||
631 | ||||
632 | if ((adjust = ipsec_hdrsz(tdbp)) == -1) { | |||
633 | tdb_unref(tdbp); | |||
634 | break; | |||
635 | } | |||
636 | ||||
637 | mtu -= adjust; | |||
638 | tdbp->tdb_mtu = mtu; | |||
639 | tdbp->tdb_mtutimeout = gettime() + ip_mtudisc_timeout; | |||
640 | DPRINTF("spi %08x mtu %d adjust %ld mbuf %p",do { } while (0) | |||
641 | ntohl(tdbp->tdb_spi), tdbp->tdb_mtu, adjust, m)do { } while (0); | |||
642 | tdb_unref(tdbp); | |||
643 | } | |||
644 | } |