]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet6/ah_input.c
xnu-2422.1.72.tar.gz
[apple/xnu.git] / bsd / netinet6 / ah_input.c
CommitLineData
b0d623f7 1/*
39236c6e 2 * Copyright (c) 2008-2013 Apple Inc. All rights reserved.
b0d623f7
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
55e303ae
A
29/* $FreeBSD: src/sys/netinet6/ah_input.c,v 1.1.2.6 2002/04/28 05:40:26 suz Exp $ */
30/* $KAME: ah_input.c,v 1.67 2002/01/07 11:39:56 kjc Exp $ */
9bccf70c 31
1c79356b
A
32/*
33 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the project nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61/*
62 * RFC1826/2402 authentication header.
63 */
64
1c79356b
A
65#include <sys/param.h>
66#include <sys/systm.h>
67#include <sys/malloc.h>
68#include <sys/mbuf.h>
316670eb 69#include <sys/mcache.h>
1c79356b
A
70#include <sys/domain.h>
71#include <sys/protosw.h>
72#include <sys/socket.h>
73#include <sys/errno.h>
74#include <sys/time.h>
75#include <sys/kernel.h>
76#include <sys/syslog.h>
77
78#include <net/if.h>
79#include <net/route.h>
1c79356b 80#include <kern/cpu_number.h>
91447636 81#include <kern/locks.h>
1c79356b
A
82
83#include <netinet/in.h>
84#include <netinet/in_systm.h>
85#include <netinet/in_var.h>
86#include <netinet/ip.h>
87#include <netinet/ip_var.h>
88#include <netinet/ip_ecn.h>
9bccf70c
A
89#include <netinet/in_pcb.h>
90#if INET6
91#include <netinet6/ip6_ecn.h>
92#endif
1c79356b
A
93
94#if INET6
95#include <netinet/ip6.h>
96#include <netinet6/ip6_var.h>
9bccf70c 97#include <netinet6/in6_pcb.h>
1c79356b 98#include <netinet/icmp6.h>
9bccf70c 99#include <netinet6/ip6protosw.h>
1c79356b
A
100#endif
101
102#include <netinet6/ipsec.h>
9bccf70c
A
103#if INET6
104#include <netinet6/ipsec6.h>
105#endif
1c79356b 106#include <netinet6/ah.h>
9bccf70c
A
107#if INET6
108#include <netinet6/ah6.h>
109#endif
1c79356b
A
110#include <netkey/key.h>
111#include <netkey/keydb.h>
9bccf70c 112#if IPSEC_DEBUG
1c79356b 113#include <netkey/key_debug.h>
9bccf70c
A
114#else
115#define KEYDEBUG(lev,arg)
116#endif
1c79356b 117
2d21ac55
A
118#include <net/kpi_protocol.h>
119#include <netinet/kpi_ipfilter_var.h>
6d2010ae 120#include <mach/sdt.h>
1c79356b
A
121
122#include <net/net_osdep.h>
123
124#define IPLEN_FLIPPED
125
126#if INET
1c79356b
A
127void
128ah4_input(struct mbuf *m, int off)
129{
130 struct ip *ip;
131 struct ah *ah;
132 u_int32_t spi;
9bccf70c 133 const struct ah_algorithm *algo;
1c79356b
A
134 size_t siz;
135 size_t siz1;
136 u_char *cksum;
137 struct secasvar *sav = NULL;
138 u_int16_t nxt;
139 size_t hlen;
9bccf70c 140 size_t stripsiz = 0;
2d21ac55 141 sa_family_t ifamily;
1c79356b
A
142
143#ifndef PULLDOWN_TEST
144 if (m->m_len < off + sizeof(struct newah)) {
145 m = m_pullup(m, off + sizeof(struct newah));
146 if (!m) {
147 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
148 "dropping the packet for simplicity\n"));
2d21ac55 149 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
1c79356b
A
150 goto fail;
151 }
152 }
153
316670eb
A
154 /* Expect 32-bit aligned data pointer on strict-align platforms */
155 MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);
156
1c79356b 157 ip = mtod(m, struct ip *);
316670eb 158 ah = (struct ah *)(void *)(((caddr_t)ip) + off);
1c79356b 159#else
316670eb
A
160 /* Expect 32-bit aligned data pointer on strict-align platforms */
161 MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);
162
1c79356b
A
163 ip = mtod(m, struct ip *);
164 IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
165 if (ah == NULL) {
166 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
167 "dropping the packet for simplicity\n"));
2d21ac55 168 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
1c79356b
A
169 goto fail;
170 }
171#endif
172 nxt = ah->ah_nxt;
173#ifdef _IP_VHL
174 hlen = IP_VHL_HL(ip->ip_vhl) << 2;
175#else
176 hlen = ip->ip_hl << 2;
177#endif
178
179 /* find the sassoc. */
180 spi = ah->ah_spi;
181
182 if ((sav = key_allocsa(AF_INET,
183 (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
184 IPPROTO_AH, spi)) == 0) {
185 ipseclog((LOG_WARNING,
186 "IPv4 AH input: no key association found for spi %u\n",
187 (u_int32_t)ntohl(spi)));
2d21ac55 188 IPSEC_STAT_INCREMENT(ipsecstat.in_nosa);
1c79356b
A
189 goto fail;
190 }
191 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
39236c6e
A
192 printf("DP ah4_input called to allocate SA:0x%llx\n",
193 (uint64_t)VM_KERNEL_ADDRPERM(sav)));
1c79356b
A
194 if (sav->state != SADB_SASTATE_MATURE
195 && sav->state != SADB_SASTATE_DYING) {
196 ipseclog((LOG_DEBUG,
197 "IPv4 AH input: non-mature/dying SA found for spi %u\n",
198 (u_int32_t)ntohl(spi)));
2d21ac55 199 IPSEC_STAT_INCREMENT(ipsecstat.in_badspi);
1c79356b
A
200 goto fail;
201 }
9bccf70c
A
202
203 algo = ah_algorithm_lookup(sav->alg_auth);
204 if (!algo) {
1c79356b 205 ipseclog((LOG_DEBUG, "IPv4 AH input: "
9bccf70c 206 "unsupported authentication algorithm for spi %u\n",
1c79356b 207 (u_int32_t)ntohl(spi)));
2d21ac55 208 IPSEC_STAT_INCREMENT(ipsecstat.in_badspi);
1c79356b
A
209 goto fail;
210 }
211
1c79356b
A
212 siz = (*algo->sumsiz)(sav);
213 siz1 = ((siz + 3) & ~(4 - 1));
214
215 /*
216 * sanity checks for header, 1.
217 */
218 {
219 int sizoff;
220
221 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
222
9bccf70c
A
223 /*
224 * Here, we do not do "siz1 == siz". This is because the way
225 * RFC240[34] section 2 is written. They do not require truncation
226 * to 96 bits.
227 * For example, Microsoft IPsec stack attaches 160 bits of
228 * authentication data for both hmac-md5 and hmac-sha1. For hmac-sha1,
229 * 32 bits of padding is attached.
230 *
231 * There are two downsides to this specification.
232 * They have no real harm, however, they leave us fuzzy feeling.
233 * - if we attach more than 96 bits of authentication data onto AH,
234 * we will never notice about possible modification by rogue
235 * intermediate nodes.
236 * Since extra bits in AH checksum is never used, this constitutes
237 * no real issue, however, it is wacky.
238 * - even if the peer attaches big authentication data, we will never
239 * notice the difference, since longer authentication data will just
240 * work.
241 *
242 * We may need some clarification in the spec.
243 */
244 if (siz1 < siz) {
245 ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input "
246 "(%lu, should be at least %lu): %s\n",
b0d623f7 247 (u_int32_t)siz1, (u_int32_t)siz,
9bccf70c 248 ipsec4_logpacketstr(ip, spi)));
2d21ac55 249 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
9bccf70c
A
250 goto fail;
251 }
1c79356b
A
252 if ((ah->ah_len << 2) - sizoff != siz1) {
253 ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input "
9bccf70c 254 "(%d should be %lu): %s\n",
b0d623f7 255 (ah->ah_len << 2) - sizoff, (u_int32_t)siz1,
9bccf70c 256 ipsec4_logpacketstr(ip, spi)));
2d21ac55 257 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
1c79356b
A
258 goto fail;
259 }
260
261#ifndef PULLDOWN_TEST
262 if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) {
263 m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1);
264 if (!m) {
265 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
2d21ac55 266 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
1c79356b
A
267 goto fail;
268 }
316670eb
A
269 /* Expect 32-bit aligned data ptr on strict-align platforms */
270 MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);
1c79356b
A
271
272 ip = mtod(m, struct ip *);
316670eb 273 ah = (struct ah *)(void *)(((caddr_t)ip) + off);
1c79356b
A
274 }
275#else
276 IP6_EXTHDR_GET(ah, struct ah *, m, off,
277 sizeof(struct ah) + sizoff + siz1);
278 if (ah == NULL) {
279 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
2d21ac55 280 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
1c79356b
A
281 goto fail;
282 }
283#endif
284 }
285
286 /*
287 * check for sequence number.
288 */
289 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
290 if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
291 ; /*okey*/
292 else {
2d21ac55 293 IPSEC_STAT_INCREMENT(ipsecstat.in_ahreplay);
1c79356b
A
294 ipseclog((LOG_WARNING,
295 "replay packet in IPv4 AH input: %s %s\n",
296 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
297 goto fail;
298 }
299 }
300
301 /*
302 * alright, it seems sane. now we are going to check the
303 * cryptographic checksum.
304 */
305 cksum = _MALLOC(siz1, M_TEMP, M_NOWAIT);
306 if (!cksum) {
307 ipseclog((LOG_DEBUG, "IPv4 AH input: "
308 "couldn't alloc temporary region for cksum\n"));
2d21ac55 309 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
1c79356b
A
310 goto fail;
311 }
312
1c79356b
A
313 /*
314 * some of IP header fields are flipped to the host endian.
315 * convert them back to network endian. VERY stupid.
316 */
1c79356b 317 ip->ip_len = htons(ip->ip_len + hlen);
1c79356b 318 ip->ip_off = htons(ip->ip_off);
9bccf70c
A
319 if (ah4_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) {
320 FREE(cksum, M_TEMP);
2d21ac55 321 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
1c79356b
A
322 goto fail;
323 }
2d21ac55 324 IPSEC_STAT_INCREMENT(ipsecstat.in_ahhist[sav->alg_auth]);
1c79356b
A
325 /*
326 * flip them back.
327 */
1c79356b 328 ip->ip_len = ntohs(ip->ip_len) - hlen;
1c79356b 329 ip->ip_off = ntohs(ip->ip_off);
1c79356b
A
330
331 {
332 caddr_t sumpos = NULL;
333
334 if (sav->flags & SADB_X_EXT_OLD) {
335 /* RFC 1826 */
336 sumpos = (caddr_t)(ah + 1);
337 } else {
338 /* RFC 2402 */
339 sumpos = (caddr_t)(((struct newah *)ah) + 1);
340 }
341
342 if (bcmp(sumpos, cksum, siz) != 0) {
343 ipseclog((LOG_WARNING,
344 "checksum mismatch in IPv4 AH input: %s %s\n",
345 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
9bccf70c 346 FREE(cksum, M_TEMP);
2d21ac55 347 IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthfail);
1c79356b
A
348 goto fail;
349 }
350 }
351
9bccf70c 352 FREE(cksum, M_TEMP);
1c79356b
A
353
354 m->m_flags |= M_AUTHIPHDR;
355 m->m_flags |= M_AUTHIPDGM;
356
357#if 0
358 /*
359 * looks okey, but we need more sanity check.
360 * XXX should elaborate.
361 */
362 if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) {
363 struct ip *nip;
364 size_t sizoff;
365
366 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
367
368 if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) {
369 m = m_pullup(m, off + sizeof(struct ah)
370 + sizoff + siz1 + hlen);
371 if (!m) {
372 ipseclog((LOG_DEBUG,
373 "IPv4 AH input: can't pullup\n"));
2d21ac55 374 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
1c79356b
A
375 goto fail;
376 }
377 }
378
379 nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1);
380 if (nip->ip_src.s_addr != ip->ip_src.s_addr
381 || nip->ip_dst.s_addr != ip->ip_dst.s_addr) {
382 m->m_flags &= ~M_AUTHIPHDR;
383 m->m_flags &= ~M_AUTHIPDGM;
384 }
385 }
386#if INET6
387 else if (ah->ah_nxt == IPPROTO_IPV6) {
388 m->m_flags &= ~M_AUTHIPHDR;
389 m->m_flags &= ~M_AUTHIPDGM;
390 }
391#endif /*INET6*/
392#endif /*0*/
393
394 if (m->m_flags & M_AUTHIPHDR
395 && m->m_flags & M_AUTHIPDGM) {
396#if 0
397 ipseclog((LOG_DEBUG,
398 "IPv4 AH input: authentication succeess\n"));
399#endif
2d21ac55 400 IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthsucc);
1c79356b
A
401 } else {
402 ipseclog((LOG_WARNING,
403 "authentication failed in IPv4 AH input: %s %s\n",
404 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
2d21ac55 405 IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthfail);
1c79356b
A
406 goto fail;
407 }
408
409 /*
410 * update sequence number.
411 */
412 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
413 if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
2d21ac55 414 IPSEC_STAT_INCREMENT(ipsecstat.in_ahreplay);
1c79356b
A
415 goto fail;
416 }
417 }
418
419 /* was it transmitted over the IPsec tunnel SA? */
9bccf70c
A
420 if (sav->flags & SADB_X_EXT_OLD) {
421 /* RFC 1826 */
422 stripsiz = sizeof(struct ah) + siz1;
423 } else {
424 /* RFC 2402 */
425 stripsiz = sizeof(struct newah) + siz1;
426 }
2d21ac55 427 if (ipsec4_tunnel_validate(m, off + stripsiz, nxt, sav, &ifamily)) {
6d2010ae
A
428 ifaddr_t ifa;
429 struct sockaddr_storage addr;
430
1c79356b
A
431 /*
432 * strip off all the headers that precedes AH.
433 * IP xx AH IP' payload -> IP' payload
434 *
435 * XXX more sanity checks
436 * XXX relationship with gif?
437 */
1c79356b 438 u_int8_t tos;
2d21ac55
A
439
440 if (ifamily == AF_INET6) {
441 ipseclog((LOG_NOTICE, "ipsec tunnel protocol mismatch "
442 "in IPv4 AH input: %s\n", ipsec_logsastr(sav)));
443 goto fail;
444 }
1c79356b 445 tos = ip->ip_tos;
1c79356b
A
446 m_adj(m, off + stripsiz);
447 if (m->m_len < sizeof(*ip)) {
448 m = m_pullup(m, sizeof(*ip));
449 if (!m) {
2d21ac55 450 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
1c79356b
A
451 goto fail;
452 }
453 }
454 ip = mtod(m, struct ip *);
455 /* ECN consideration. */
456 ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos);
457 if (!key_checktunnelsanity(sav, AF_INET,
458 (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) {
459 ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
460 "in IPv4 AH input: %s %s\n",
461 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
2d21ac55 462 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
1c79356b
A
463 goto fail;
464 }
465
1c79356b
A
466#if 1
467 /*
468 * Should the inner packet be considered authentic?
469 * My current answer is: NO.
470 *
471 * host1 -- gw1 === gw2 -- host2
472 * In this case, gw2 can trust the authenticity of the
473 * outer packet, but NOT inner. Packet may be altered
474 * between host1 and gw1.
475 *
476 * host1 -- gw1 === host2
477 * This case falls into the same scenario as above.
478 *
479 * host1 === host2
480 * This case is the only case when we may be able to leave
481 * M_AUTHIPHDR and M_AUTHIPDGM set.
482 * However, if host1 is wrongly configured, and allows
483 * attacker to inject some packet with src=host1 and
484 * dst=host2, you are in risk.
485 */
486 m->m_flags &= ~M_AUTHIPHDR;
487 m->m_flags &= ~M_AUTHIPDGM;
488#endif
489
490 key_sa_recordxfer(sav, m);
9bccf70c
A
491 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
492 ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) {
2d21ac55 493 IPSEC_STAT_INCREMENT(ipsecstat.in_nomem);
9bccf70c
A
494 goto fail;
495 }
6d2010ae
A
496
497 if (ip_doscopedroute) {
498 struct sockaddr_in *ipaddr;
499
500 bzero(&addr, sizeof(addr));
501 ipaddr = (__typeof__(ipaddr))&addr;
502 ipaddr->sin_family = AF_INET;
503 ipaddr->sin_len = sizeof(*ipaddr);
504 ipaddr->sin_addr = ip->ip_dst;
505
506 // update the receiving interface address based on the inner address
507 ifa = ifa_ifwithaddr((struct sockaddr *)&addr);
508 if (ifa) {
509 m->m_pkthdr.rcvif = ifa->ifa_ifp;
510 IFA_REMREF(ifa);
511 }
512 }
513 if (proto_input(PF_INET, m) != 0)
514 goto fail;
1c79356b
A
515 nxt = IPPROTO_DONE;
516 } else {
517 /*
518 * strip off AH.
1c79356b 519 */
1c79356b
A
520
521 ip = mtod(m, struct ip *);
522#ifndef PULLDOWN_TEST
9bccf70c
A
523 /*
524 * We do deep-copy since KAME requires that
525 * the packet is placed in a single external mbuf.
526 */
1c79356b
A
527 ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off);
528 m->m_data += stripsiz;
529 m->m_len -= stripsiz;
530 m->m_pkthdr.len -= stripsiz;
531#else
532 /*
533 * even in m_pulldown case, we need to strip off AH so that
534 * we can compute checksum for multiple AH correctly.
535 */
536 if (m->m_len >= stripsiz + off) {
537 ovbcopy((caddr_t)ip, ((caddr_t)ip) + stripsiz, off);
538 m->m_data += stripsiz;
539 m->m_len -= stripsiz;
540 m->m_pkthdr.len -= stripsiz;
541 } else {
542 /*
543 * this comes with no copy if the boundary is on
544 * cluster
545 */
546 struct mbuf *n;
547
548 n = m_split(m, off, M_DONTWAIT);
549 if (n == NULL) {
550 /* m is retained by m_split */
551 goto fail;
552 }
553 m_adj(n, stripsiz);
1c79356b
A
554 /* m_cat does not update m_pkthdr.len */
555 m->m_pkthdr.len += n->m_pkthdr.len;
55e303ae 556 m_cat(m, n);
1c79356b
A
557 }
558#endif
559
560 if (m->m_len < sizeof(*ip)) {
561 m = m_pullup(m, sizeof(*ip));
562 if (m == NULL) {
2d21ac55 563 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
1c79356b
A
564 goto fail;
565 }
566 }
567 ip = mtod(m, struct ip *);
568#ifdef IPLEN_FLIPPED
569 ip->ip_len = ip->ip_len - stripsiz;
570#else
571 ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz);
572#endif
573 ip->ip_p = nxt;
574 /* forget about IP hdr checksum, the check has already been passed */
575
576 key_sa_recordxfer(sav, m);
9bccf70c 577 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
2d21ac55 578 IPSEC_STAT_INCREMENT(ipsecstat.in_nomem);
9bccf70c
A
579 goto fail;
580 }
1c79356b 581
6d2010ae
A
582 DTRACE_IP6(receive, struct mbuf *, m, struct inpcb *, NULL,
583 struct ip *, ip, struct ifnet *, m->m_pkthdr.rcvif,
584 struct ip *, ip, struct ip6_hdr *, NULL);
585
9bccf70c
A
586 if (nxt != IPPROTO_DONE) {
587 if ((ip_protox[nxt]->pr_flags & PR_LASTHDR) != 0 &&
588 ipsec4_in_reject(m, NULL)) {
2d21ac55 589 IPSEC_STAT_INCREMENT(ipsecstat.in_polvio);
9bccf70c
A
590 goto fail;
591 }
91447636 592 ip_proto_dispatch_in(m, off, nxt, 0);
9bccf70c 593 } else
1c79356b
A
594 m_freem(m);
595 m = NULL;
596 }
597
598 if (sav) {
599 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
39236c6e
A
600 printf("DP ah4_input call free SA:0x%llx\n",
601 (uint64_t)VM_KERNEL_ADDRPERM(sav)));
2d21ac55 602 key_freesav(sav, KEY_SADB_UNLOCKED);
1c79356b 603 }
2d21ac55 604 IPSEC_STAT_INCREMENT(ipsecstat.in_success);
1c79356b
A
605 return;
606
607fail:
608 if (sav) {
609 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
39236c6e
A
610 printf("DP ah4_input call free SA:0x%llx\n",
611 (uint64_t)VM_KERNEL_ADDRPERM(sav)));
2d21ac55 612 key_freesav(sav, KEY_SADB_UNLOCKED);
1c79356b
A
613 }
614 if (m)
615 m_freem(m);
616 return;
617}
618#endif /* INET */
619
620#if INET6
621int
6d2010ae 622ah6_input(struct mbuf **mp, int *offp, int proto)
1c79356b 623{
6d2010ae 624#pragma unused(proto)
1c79356b
A
625 struct mbuf *m = *mp;
626 int off = *offp;
627 struct ip6_hdr *ip6;
628 struct ah *ah;
629 u_int32_t spi;
9bccf70c 630 const struct ah_algorithm *algo;
1c79356b
A
631 size_t siz;
632 size_t siz1;
633 u_char *cksum;
634 struct secasvar *sav = NULL;
635 u_int16_t nxt;
9bccf70c 636 size_t stripsiz = 0;
1c79356b 637
91447636 638
1c79356b 639#ifndef PULLDOWN_TEST
2d21ac55 640 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), {return IPPROTO_DONE;});
316670eb 641 ah = (struct ah *)(void *)(mtod(m, caddr_t) + off);
1c79356b
A
642#else
643 IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
644 if (ah == NULL) {
645 ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n"));
9bccf70c 646 ipsec6stat.in_inval++;
1c79356b
A
647 return IPPROTO_DONE;
648 }
649#endif
316670eb
A
650 /* Expect 32-bit aligned data pointer on strict-align platforms */
651 MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);
652
1c79356b
A
653 ip6 = mtod(m, struct ip6_hdr *);
654 nxt = ah->ah_nxt;
655
656 /* find the sassoc. */
657 spi = ah->ah_spi;
658
659 if (ntohs(ip6->ip6_plen) == 0) {
660 ipseclog((LOG_ERR, "IPv6 AH input: "
661 "AH with IPv6 jumbogram is not supported.\n"));
2d21ac55 662 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
1c79356b
A
663 goto fail;
664 }
665
666 if ((sav = key_allocsa(AF_INET6,
667 (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
668 IPPROTO_AH, spi)) == 0) {
669 ipseclog((LOG_WARNING,
670 "IPv6 AH input: no key association found for spi %u\n",
671 (u_int32_t)ntohl(spi)));
2d21ac55 672 IPSEC_STAT_INCREMENT(ipsec6stat.in_nosa);
1c79356b
A
673 goto fail;
674 }
675 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
39236c6e
A
676 printf("DP ah6_input called to allocate SA:0x%llx\n",
677 (uint64_t)VM_KERNEL_ADDRPERM(sav)));
1c79356b
A
678 if (sav->state != SADB_SASTATE_MATURE
679 && sav->state != SADB_SASTATE_DYING) {
680 ipseclog((LOG_DEBUG,
681 "IPv6 AH input: non-mature/dying SA found for spi %u; ",
682 (u_int32_t)ntohl(spi)));
2d21ac55 683 IPSEC_STAT_INCREMENT(ipsec6stat.in_badspi);
1c79356b
A
684 goto fail;
685 }
9bccf70c
A
686
687 algo = ah_algorithm_lookup(sav->alg_auth);
688 if (!algo) {
1c79356b 689 ipseclog((LOG_DEBUG, "IPv6 AH input: "
9bccf70c 690 "unsupported authentication algorithm for spi %u\n",
1c79356b 691 (u_int32_t)ntohl(spi)));
2d21ac55 692 IPSEC_STAT_INCREMENT(ipsec6stat.in_badspi);
1c79356b
A
693 goto fail;
694 }
695
1c79356b
A
696 siz = (*algo->sumsiz)(sav);
697 siz1 = ((siz + 3) & ~(4 - 1));
698
699 /*
700 * sanity checks for header, 1.
701 */
702 {
703 int sizoff;
704
705 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
706
9bccf70c
A
707 /*
708 * Here, we do not do "siz1 == siz". See ah4_input() for complete
709 * description.
710 */
711 if (siz1 < siz) {
712 ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input "
713 "(%lu, should be at least %lu): %s\n",
b0d623f7 714 (u_int32_t)siz1, (u_int32_t)siz,
9bccf70c 715 ipsec6_logpacketstr(ip6, spi)));
2d21ac55 716 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
9bccf70c
A
717 goto fail;
718 }
1c79356b
A
719 if ((ah->ah_len << 2) - sizoff != siz1) {
720 ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input "
9bccf70c 721 "(%d should be %lu): %s\n",
b0d623f7 722 (ah->ah_len << 2) - sizoff, (u_int32_t)siz1,
9bccf70c 723 ipsec6_logpacketstr(ip6, spi)));
2d21ac55 724 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
1c79356b
A
725 goto fail;
726 }
727#ifndef PULLDOWN_TEST
91447636 728 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1,
2d21ac55 729 {return IPPROTO_DONE;});
1c79356b
A
730#else
731 IP6_EXTHDR_GET(ah, struct ah *, m, off,
732 sizeof(struct ah) + sizoff + siz1);
733 if (ah == NULL) {
734 ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part"));
2d21ac55 735 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
1c79356b
A
736 m = NULL;
737 goto fail;
738 }
739#endif
740 }
741
742 /*
743 * check for sequence number.
744 */
745 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
746 if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
747 ; /*okey*/
748 else {
2d21ac55 749 IPSEC_STAT_INCREMENT(ipsec6stat.in_ahreplay);
1c79356b
A
750 ipseclog((LOG_WARNING,
751 "replay packet in IPv6 AH input: %s %s\n",
752 ipsec6_logpacketstr(ip6, spi),
753 ipsec_logsastr(sav)));
754 goto fail;
755 }
756 }
757
758 /*
759 * alright, it seems sane. now we are going to check the
760 * cryptographic checksum.
761 */
762 cksum = _MALLOC(siz1, M_TEMP, M_NOWAIT);
763 if (!cksum) {
764 ipseclog((LOG_DEBUG, "IPv6 AH input: "
765 "couldn't alloc temporary region for cksum\n"));
2d21ac55 766 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
1c79356b
A
767 goto fail;
768 }
769
9bccf70c
A
770 if (ah6_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) {
771 FREE(cksum, M_TEMP);
2d21ac55 772 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
1c79356b
A
773 goto fail;
774 }
2d21ac55 775 IPSEC_STAT_INCREMENT(ipsec6stat.in_ahhist[sav->alg_auth]);
1c79356b
A
776
777 {
778 caddr_t sumpos = NULL;
779
780 if (sav->flags & SADB_X_EXT_OLD) {
781 /* RFC 1826 */
782 sumpos = (caddr_t)(ah + 1);
783 } else {
784 /* RFC 2402 */
785 sumpos = (caddr_t)(((struct newah *)ah) + 1);
786 }
787
788 if (bcmp(sumpos, cksum, siz) != 0) {
789 ipseclog((LOG_WARNING,
790 "checksum mismatch in IPv6 AH input: %s %s\n",
791 ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
9bccf70c 792 FREE(cksum, M_TEMP);
2d21ac55 793 IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthfail);
1c79356b
A
794 goto fail;
795 }
796 }
797
9bccf70c 798 FREE(cksum, M_TEMP);
1c79356b
A
799
800 m->m_flags |= M_AUTHIPHDR;
801 m->m_flags |= M_AUTHIPDGM;
802
803#if 0
804 /*
805 * looks okey, but we need more sanity check.
806 * XXX should elaborate.
807 */
808 if (ah->ah_nxt == IPPROTO_IPV6) {
809 struct ip6_hdr *nip6;
810 size_t sizoff;
811
812 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
813
814 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1
91447636 815 + sizeof(struct ip6_hdr),
2d21ac55 816 {return IPPROTO_DONE;});
1c79356b
A
817
818 nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1);
819 if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src)
820 || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) {
821 m->m_flags &= ~M_AUTHIPHDR;
822 m->m_flags &= ~M_AUTHIPDGM;
823 }
824 } else if (ah->ah_nxt == IPPROTO_IPIP) {
825 m->m_flags &= ~M_AUTHIPHDR;
826 m->m_flags &= ~M_AUTHIPDGM;
827 } else if (ah->ah_nxt == IPPROTO_IP) {
828 m->m_flags &= ~M_AUTHIPHDR;
829 m->m_flags &= ~M_AUTHIPDGM;
830 }
831#endif
832
833 if (m->m_flags & M_AUTHIPHDR
834 && m->m_flags & M_AUTHIPDGM) {
835#if 0
836 ipseclog((LOG_DEBUG,
837 "IPv6 AH input: authentication succeess\n"));
838#endif
2d21ac55 839 IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthsucc);
1c79356b
A
840 } else {
841 ipseclog((LOG_WARNING,
842 "authentication failed in IPv6 AH input: %s %s\n",
843 ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
2d21ac55 844 IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthfail);
1c79356b
A
845 goto fail;
846 }
847
848 /*
849 * update sequence number.
850 */
851 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
852 if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
2d21ac55 853 IPSEC_STAT_INCREMENT(ipsec6stat.in_ahreplay);
1c79356b
A
854 goto fail;
855 }
856 }
857
858 /* was it transmitted over the IPsec tunnel SA? */
9bccf70c
A
859 if (sav->flags & SADB_X_EXT_OLD) {
860 /* RFC 1826 */
861 stripsiz = sizeof(struct ah) + siz1;
862 } else {
863 /* RFC 2402 */
864 stripsiz = sizeof(struct newah) + siz1;
865 }
866 if (ipsec6_tunnel_validate(m, off + stripsiz, nxt, sav)) {
6d2010ae
A
867 ifaddr_t ifa;
868 struct sockaddr_storage addr;
869
1c79356b
A
870 /*
871 * strip off all the headers that precedes AH.
872 * IP6 xx AH IP6' payload -> IP6' payload
873 *
874 * XXX more sanity checks
875 * XXX relationship with gif?
876 */
1c79356b
A
877 u_int32_t flowinfo; /*net endian*/
878
879 flowinfo = ip6->ip6_flow;
1c79356b
A
880 m_adj(m, off + stripsiz);
881 if (m->m_len < sizeof(*ip6)) {
882 /*
883 * m_pullup is prohibited in KAME IPv6 input processing
884 * but there's no other way!
885 */
886 m = m_pullup(m, sizeof(*ip6));
887 if (!m) {
2d21ac55 888 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
1c79356b
A
889 goto fail;
890 }
891 }
892 ip6 = mtod(m, struct ip6_hdr *);
893 /* ECN consideration. */
894 ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow);
895 if (!key_checktunnelsanity(sav, AF_INET6,
896 (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) {
897 ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
898 "in IPv6 AH input: %s %s\n",
899 ipsec6_logpacketstr(ip6, spi),
900 ipsec_logsastr(sav)));
2d21ac55 901 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
1c79356b
A
902 goto fail;
903 }
904
1c79356b
A
905#if 1
906 /*
907 * should the inner packet be considered authentic?
908 * see comment in ah4_input().
909 */
910 m->m_flags &= ~M_AUTHIPHDR;
911 m->m_flags &= ~M_AUTHIPDGM;
912#endif
913
914 key_sa_recordxfer(sav, m);
9bccf70c
A
915 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
916 ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) {
2d21ac55 917 IPSEC_STAT_INCREMENT(ipsec6stat.in_nomem);
9bccf70c
A
918 goto fail;
919 }
6d2010ae
A
920
921 if (ip6_doscopedroute) {
922 struct sockaddr_in6 *ip6addr;
923
924 bzero(&addr, sizeof(addr));
925 ip6addr = (__typeof__(ip6addr))&addr;
926 ip6addr->sin6_family = AF_INET6;
927 ip6addr->sin6_len = sizeof(*ip6addr);
928 ip6addr->sin6_addr = ip6->ip6_dst;
929
930 // update the receiving interface address based on the inner address
931 ifa = ifa_ifwithaddr((struct sockaddr *)&addr);
932 if (ifa) {
933 m->m_pkthdr.rcvif = ifa->ifa_ifp;
934 IFA_REMREF(ifa);
935 }
936 }
937
938 if (proto_input(PF_INET6, m) != 0)
939 goto fail;
1c79356b
A
940 nxt = IPPROTO_DONE;
941 } else {
942 /*
943 * strip off AH.
1c79356b 944 */
1c79356b
A
945 char *prvnxtp;
946
947 /*
948 * Copy the value of the next header field of AH to the
949 * next header field of the previous header.
950 * This is necessary because AH will be stripped off below.
951 */
952 prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
953 *prvnxtp = nxt;
954
1c79356b
A
955 ip6 = mtod(m, struct ip6_hdr *);
956#ifndef PULLDOWN_TEST
9bccf70c
A
957 /*
958 * We do deep-copy since KAME requires that
959 * the packet is placed in a single mbuf.
960 */
1c79356b
A
961 ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
962 m->m_data += stripsiz;
963 m->m_len -= stripsiz;
964 m->m_pkthdr.len -= stripsiz;
965#else
966 /*
967 * even in m_pulldown case, we need to strip off AH so that
968 * we can compute checksum for multiple AH correctly.
969 */
970 if (m->m_len >= stripsiz + off) {
971 ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
972 m->m_data += stripsiz;
973 m->m_len -= stripsiz;
974 m->m_pkthdr.len -= stripsiz;
975 } else {
976 /*
977 * this comes with no copy if the boundary is on
978 * cluster
979 */
980 struct mbuf *n;
981
982 n = m_split(m, off, M_DONTWAIT);
983 if (n == NULL) {
984 /* m is retained by m_split */
985 goto fail;
986 }
987 m_adj(n, stripsiz);
1c79356b
A
988 /* m_cat does not update m_pkthdr.len */
989 m->m_pkthdr.len += n->m_pkthdr.len;
55e303ae 990 m_cat(m, n);
1c79356b
A
991 }
992#endif
993 ip6 = mtod(m, struct ip6_hdr *);
994 /* XXX jumbogram */
995 ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
996
997 key_sa_recordxfer(sav, m);
9bccf70c 998 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
2d21ac55 999 IPSEC_STAT_INCREMENT(ipsec6stat.in_nomem);
9bccf70c
A
1000 goto fail;
1001 }
1c79356b
A
1002 }
1003
1004 *offp = off;
1005 *mp = m;
1006
1007 if (sav) {
1008 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
39236c6e
A
1009 printf("DP ah6_input call free SA:0x%llx\n",
1010 (uint64_t)VM_KERNEL_ADDRPERM(sav)));
2d21ac55 1011 key_freesav(sav, KEY_SADB_UNLOCKED);
1c79356b 1012 }
2d21ac55 1013 IPSEC_STAT_INCREMENT(ipsec6stat.in_success);
1c79356b
A
1014 return nxt;
1015
1016fail:
1017 if (sav) {
1018 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
39236c6e
A
1019 printf("DP ah6_input call free SA:0x%llx\n",
1020 (uint64_t)VM_KERNEL_ADDRPERM(sav)));
2d21ac55 1021 key_freesav(sav, KEY_SADB_UNLOCKED);
1c79356b
A
1022 }
1023 if (m)
1024 m_freem(m);
1025 return IPPROTO_DONE;
1026}
9bccf70c
A
1027
1028void
1029ah6_ctlinput(cmd, sa, d)
1030 int cmd;
1031 struct sockaddr *sa;
1032 void *d;
1033{
1034 const struct newah *ahp;
1035 struct newah ah;
1036 struct secasvar *sav;
1037 struct ip6_hdr *ip6;
1038 struct mbuf *m;
1039 struct ip6ctlparam *ip6cp = NULL;
1040 int off;
55e303ae 1041 struct sockaddr_in6 *sa6_src, *sa6_dst;
9bccf70c
A
1042
1043 if (sa->sa_family != AF_INET6 ||
1044 sa->sa_len != sizeof(struct sockaddr_in6))
1045 return;
1046 if ((unsigned)cmd >= PRC_NCMDS)
1047 return;
1048
1049 /* if the parameter is from icmp6, decode it. */
1050 if (d != NULL) {
1051 ip6cp = (struct ip6ctlparam *)d;
1052 m = ip6cp->ip6c_m;
1053 ip6 = ip6cp->ip6c_ip6;
1054 off = ip6cp->ip6c_off;
1055 } else {
1056 m = NULL;
1057 ip6 = NULL;
1058 }
1059
1060 if (ip6) {
1061 /*
1062 * XXX: We assume that when ip6 is non NULL,
1063 * M and OFF are valid.
1064 */
1065
1066 /* check if we can safely examine src and dst ports */
1067 if (m->m_pkthdr.len < off + sizeof(ah))
1068 return;
1069
1070 if (m->m_len < off + sizeof(ah)) {
1071 /*
1072 * this should be rare case,
1073 * so we compromise on this copy...
1074 */
1075 m_copydata(m, off, sizeof(ah), (caddr_t)&ah);
1076 ahp = &ah;
1077 } else
316670eb 1078 ahp = (struct newah *)(void *)(mtod(m, caddr_t) + off);
9bccf70c
A
1079
1080 if (cmd == PRC_MSGSIZE) {
1081 int valid = 0;
1082
1083 /*
1084 * Check to see if we have a valid SA corresponding to
1085 * the address in the ICMP message payload.
1086 */
55e303ae 1087 sa6_src = ip6cp->ip6c_src;
316670eb 1088 sa6_dst = (struct sockaddr_in6 *)(void *)sa;
9bccf70c 1089 sav = key_allocsa(AF_INET6,
55e303ae
A
1090 (caddr_t)&sa6_src->sin6_addr,
1091 (caddr_t)&sa6_dst->sin6_addr,
9bccf70c
A
1092 IPPROTO_AH, ahp->ah_spi);
1093 if (sav) {
1094 if (sav->state == SADB_SASTATE_MATURE ||
1095 sav->state == SADB_SASTATE_DYING)
1096 valid++;
2d21ac55 1097 key_freesav(sav, KEY_SADB_UNLOCKED);
9bccf70c
A
1098 }
1099
1100 /* XXX Further validation? */
1101
1102 /*
1103 * Depending on the value of "valid" and routing table
1104 * size (mtudisc_{hi,lo}wat), we will:
1105 * - recalcurate the new MTU and create the
1106 * corresponding routing entry, or
1107 * - ignore the MTU change notification.
1108 */
1109 icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
1110 }
1111
1112 /* we normally notify single pcb here */
1113 } else {
1114 /* we normally notify any pcb here */
1115 }
1116}
1c79356b 1117#endif /* INET6 */