]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet6/ah_output.c
xnu-2782.30.5.tar.gz
[apple/xnu.git] / bsd / netinet6 / ah_output.c
CommitLineData
b0d623f7 1/*
316670eb 2 * Copyright (c) 2008-2011 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
9bccf70c
A
29/* $FreeBSD: src/sys/netinet6/ah_output.c,v 1.1.2.3 2001/07/03 11:01:49 ume Exp $ */
30/* $KAME: ah_output.c,v 1.30 2001/02/21 00:50:53 itojun Exp $ */
1c79356b
A
31
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
65#define _IP_VHL
1c79356b
A
66
67#include <sys/param.h>
68#include <sys/systm.h>
69#include <sys/malloc.h>
70#include <sys/mbuf.h>
71#include <sys/domain.h>
72#include <sys/protosw.h>
73#include <sys/socket.h>
74#include <sys/socketvar.h>
75#include <sys/errno.h>
76#include <sys/time.h>
77#include <sys/kernel.h>
78#include <sys/syslog.h>
79
80#include <net/if.h>
81#include <net/route.h>
82
83#include <netinet/in.h>
84
85#include <netinet/in_systm.h>
86#include <netinet/ip.h>
87#include <netinet/in_var.h>
88
89#if INET6
90#include <netinet/ip6.h>
91#include <netinet6/ip6_var.h>
92#include <netinet/icmp6.h>
93#endif
94
95#include <netinet6/ipsec.h>
9bccf70c
A
96#if INET6
97#include <netinet6/ipsec6.h>
98#endif
1c79356b 99#include <netinet6/ah.h>
9bccf70c
A
100#if INET6
101#include <netinet6/ah6.h>
102#endif
1c79356b
A
103#include <netkey/key.h>
104#include <netkey/keydb.h>
1c79356b
A
105
106#include <net/net_osdep.h>
107
9bccf70c 108#if INET
91447636 109static struct in_addr *ah4_finaldst(struct mbuf *);
9bccf70c 110#endif
1c79356b 111
2d21ac55
A
112extern lck_mtx_t *sadb_mutex;
113
1c79356b
A
114/*
115 * compute AH header size.
116 * transport mode only. for tunnel mode, we should implement
117 * virtual interface, and control MTU/MSS by the interface MTU.
118 */
119size_t
120ah_hdrsiz(isr)
121 struct ipsecrequest *isr;
122{
1c79356b
A
123
124 /* sanity check */
125 if (isr == NULL)
126 panic("ah_hdrsiz: NULL was passed.\n");
127
128 if (isr->saidx.proto != IPPROTO_AH)
129 panic("unsupported mode passed to ah_hdrsiz");
130
2d21ac55
A
131#if 0
132 {
1c79356b 133
2d21ac55
A
134 lck_mtx_lock(sadb_mutex);
135 const struct ah_algorithm *algo;
136 size_t hdrsiz;
1c79356b 137
2d21ac55
A
138 /*%%%%% this needs to change - no sav in ipsecrequest any more */
139 if (isr->sav == NULL)
140 goto estimate;
141 if (isr->sav->state != SADB_SASTATE_MATURE
142 && isr->sav->state != SADB_SASTATE_DYING)
143 goto estimate;
144
145 /* we need transport mode AH. */
146 algo = ah_algorithm_lookup(isr->sav->alg_auth);
147 if (!algo)
148 goto estimate;
149
150 /*
151 * XXX
152 * right now we don't calcurate the padding size. simply
153 * treat the padding size as constant, for simplicity.
154 *
155 * XXX variable size padding support
156 */
157 hdrsiz = (((*algo->sumsiz)(isr->sav) + 3) & ~(4 - 1));
158 if (isr->sav->flags & SADB_X_EXT_OLD)
159 hdrsiz += sizeof(struct ah);
160 else
161 hdrsiz += sizeof(struct newah);
162
163 lck_mtx_unlock(sadb_mutex);
164 return hdrsiz;
165 }
1c79356b 166
2d21ac55
A
167estimate:
168#endif
1c79356b 169
2d21ac55 170 //lck_mtx_unlock(sadb_mutex);
1c79356b
A
171 /* ASSUMING:
172 * sizeof(struct newah) > sizeof(struct ah).
173 * 16 = (16 + 3) & ~(4 - 1).
174 */
175 return sizeof(struct newah) + 16;
176}
177
9bccf70c 178#if INET
1c79356b
A
179/*
180 * Modify the packet so that it includes the authentication data.
181 * The mbuf passed must start with IPv4 header.
182 *
183 * assumes that the first mbuf contains IPv4 header + option only.
184 * the function does not modify m.
185 */
186int
2d21ac55 187ah4_output(m, sav)
1c79356b 188 struct mbuf *m;
2d21ac55 189 struct secasvar *sav;
1c79356b 190{
9bccf70c 191 const struct ah_algorithm *algo;
1c79356b
A
192 u_int32_t spi;
193 u_char *ahdrpos;
194 u_char *ahsumpos = NULL;
195 size_t hlen = 0; /*IP header+option in bytes*/
196 size_t plen = 0; /*AH payload size in bytes*/
197 size_t ahlen = 0; /*plen + sizeof(ah)*/
198 struct ip *ip;
2d21ac55 199 struct in_addr dst = { 0 };
1c79356b
A
200 struct in_addr *finaldst;
201 int error;
202
203 /* sanity checks */
204 if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
1c79356b
A
205 ip = mtod(m, struct ip *);
206 ipseclog((LOG_DEBUG, "ah4_output: internal error: "
207 "sav->replay is null: %x->%x, SPI=%u\n",
208 (u_int32_t)ntohl(ip->ip_src.s_addr),
209 (u_int32_t)ntohl(ip->ip_dst.s_addr),
210 (u_int32_t)ntohl(sav->spi)));
2d21ac55 211 IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
1c79356b
A
212 m_freem(m);
213 return EINVAL;
214 }
215
9bccf70c
A
216 algo = ah_algorithm_lookup(sav->alg_auth);
217 if (!algo) {
218 ipseclog((LOG_ERR, "ah4_output: unsupported algorithm: "
219 "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
2d21ac55 220 IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
9bccf70c
A
221 m_freem(m);
222 return EINVAL;
223 }
1c79356b
A
224 spi = sav->spi;
225
226 /*
227 * determine the size to grow.
228 */
229 if (sav->flags & SADB_X_EXT_OLD) {
230 /* RFC 1826 */
231 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/
232 ahlen = plen + sizeof(struct ah);
233 } else {
234 /* RFC 2402 */
235 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/
236 ahlen = plen + sizeof(struct newah);
237 }
238
239 /*
240 * grow the mbuf to accomodate AH.
241 */
242 ip = mtod(m, struct ip *);
243#ifdef _IP_VHL
244 hlen = IP_VHL_HL(ip->ip_vhl) << 2;
245#else
246 hlen = ip->ip_hl << 2;
247#endif
248
249 if (m->m_len != hlen)
250 panic("ah4_output: assumption failed (first mbuf length)");
251 if (M_LEADINGSPACE(m->m_next) < ahlen) {
252 struct mbuf *n;
253 MGET(n, M_DONTWAIT, MT_DATA);
254 if (!n) {
255 ipseclog((LOG_DEBUG, "ENOBUFS in ah4_output %d\n",
256 __LINE__));
257 m_freem(m);
258 return ENOBUFS;
259 }
260 n->m_len = ahlen;
261 n->m_next = m->m_next;
262 m->m_next = n;
263 m->m_pkthdr.len += ahlen;
264 ahdrpos = mtod(n, u_char *);
265 } else {
266 m->m_next->m_len += ahlen;
267 m->m_next->m_data -= ahlen;
268 m->m_pkthdr.len += ahlen;
269 ahdrpos = mtod(m->m_next, u_char *);
270 }
271
272 ip = mtod(m, struct ip *); /*just to be sure*/
273
274 /*
275 * initialize AH.
276 */
277 if (sav->flags & SADB_X_EXT_OLD) {
278 struct ah *ahdr;
279
316670eb 280 ahdr = (struct ah *)(void *)ahdrpos;
1c79356b
A
281 ahsumpos = (u_char *)(ahdr + 1);
282 ahdr->ah_len = plen >> 2;
283 ahdr->ah_nxt = ip->ip_p;
284 ahdr->ah_reserve = htons(0);
285 ahdr->ah_spi = spi;
286 bzero(ahdr + 1, plen);
287 } else {
288 struct newah *ahdr;
289
316670eb 290 ahdr = (struct newah *)(void *)ahdrpos;
1c79356b
A
291 ahsumpos = (u_char *)(ahdr + 1);
292 ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */
293 ahdr->ah_nxt = ip->ip_p;
294 ahdr->ah_reserve = htons(0);
295 ahdr->ah_spi = spi;
296 if (sav->replay->count == ~0) {
297 if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
298 /* XXX Is it noisy ? */
299 ipseclog((LOG_WARNING,
300 "replay counter overflowed. %s\n",
301 ipsec_logsastr(sav)));
2d21ac55 302 IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
1c79356b
A
303 m_freem(m);
304 return EINVAL;
305 }
306 }
2d21ac55 307 lck_mtx_lock(sadb_mutex);
1c79356b 308 sav->replay->count++;
2d21ac55 309 lck_mtx_unlock(sadb_mutex);
1c79356b
A
310 /*
311 * XXX sequence number must not be cycled, if the SA is
312 * installed by IKE daemon.
313 */
314 ahdr->ah_seq = htonl(sav->replay->count);
315 bzero(ahdr + 1, plen);
316 }
317
318 /*
319 * modify IPv4 header.
320 */
321 ip->ip_p = IPPROTO_AH;
322 if (ahlen < (IP_MAXPACKET - ntohs(ip->ip_len)))
323 ip->ip_len = htons(ntohs(ip->ip_len) + ahlen);
324 else {
325 ipseclog((LOG_ERR, "IPv4 AH output: size exceeds limit\n"));
2d21ac55 326 IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
1c79356b
A
327 m_freem(m);
328 return EMSGSIZE;
329 }
330
331 /*
332 * If there is source routing option, update destination field in
333 * the IPv4 header to the final destination.
334 * Note that we do not need to update source routing option itself
335 * (as done in IPv4 AH processing -- see ip6_output()), since
336 * source routing option is not part of the ICV computation.
337 */
338 finaldst = ah4_finaldst(m);
339 if (finaldst) {
340 dst.s_addr = ip->ip_dst.s_addr;
341 ip->ip_dst.s_addr = finaldst->s_addr;
342 }
343
344 /*
345 * calcurate the checksum, based on security association
346 * and the algorithm specified.
347 */
9bccf70c 348 error = ah4_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
1c79356b
A
349 if (error) {
350 ipseclog((LOG_ERR,
351 "error after ah4_calccksum, called from ah4_output"));
9bccf70c 352 m_freem(m);
1c79356b 353 m = NULL;
2d21ac55 354 IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
1c79356b
A
355 return error;
356 }
357
358 if (finaldst) {
359 ip = mtod(m, struct ip *); /*just to make sure*/
360 ip->ip_dst.s_addr = dst.s_addr;
361 }
2d21ac55 362 lck_mtx_lock(sadb_stat_mutex);
1c79356b
A
363 ipsecstat.out_success++;
364 ipsecstat.out_ahhist[sav->alg_auth]++;
2d21ac55 365 lck_mtx_unlock(sadb_stat_mutex);
1c79356b
A
366 key_sa_recordxfer(sav, m);
367
368 return 0;
369}
9bccf70c 370#endif
1c79356b
A
371
372/* Calculate AH length */
373int
374ah_hdrlen(sav)
375 struct secasvar *sav;
376{
9bccf70c 377 const struct ah_algorithm *algo;
1c79356b
A
378 int plen, ahlen;
379
9bccf70c
A
380 algo = ah_algorithm_lookup(sav->alg_auth);
381 if (!algo)
382 return 0;
1c79356b
A
383 if (sav->flags & SADB_X_EXT_OLD) {
384 /* RFC 1826 */
385 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/
386 ahlen = plen + sizeof(struct ah);
387 } else {
388 /* RFC 2402 */
389 plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/
390 ahlen = plen + sizeof(struct newah);
391 }
392
393 return(ahlen);
394}
395
396#if INET6
397/*
398 * Fill in the Authentication Header and calculate checksum.
399 */
400int
2d21ac55 401ah6_output(m, nexthdrp, md, sav)
1c79356b
A
402 struct mbuf *m;
403 u_char *nexthdrp;
404 struct mbuf *md;
2d21ac55 405 struct secasvar *sav;
1c79356b
A
406{
407 struct mbuf *mprev;
408 struct mbuf *mah;
9bccf70c 409 const struct ah_algorithm *algo;
1c79356b
A
410 u_int32_t spi;
411 u_char *ahsumpos = NULL;
412 size_t plen; /*AH payload size in bytes*/
413 int error = 0;
414 int ahlen;
415 struct ip6_hdr *ip6;
416
417 if (m->m_len < sizeof(struct ip6_hdr)) {
418 ipseclog((LOG_DEBUG, "ah6_output: first mbuf too short\n"));
419 m_freem(m);
420 return EINVAL;
421 }
422
423 ahlen = ah_hdrlen(sav);
424 if (ahlen == 0)
425 return 0;
426
427 for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
428 ;
429 if (!mprev || mprev->m_next != md) {
430 ipseclog((LOG_DEBUG, "ah6_output: md is not in chain\n"));
431 m_freem(m);
432 return EINVAL;
433 }
434
435 MGET(mah, M_DONTWAIT, MT_DATA);
436 if (!mah) {
437 m_freem(m);
438 return ENOBUFS;
439 }
440 if (ahlen > MLEN) {
441 MCLGET(mah, M_DONTWAIT);
442 if ((mah->m_flags & M_EXT) == 0) {
443 m_free(mah);
444 m_freem(m);
445 return ENOBUFS;
446 }
447 }
448 mah->m_len = ahlen;
449 mah->m_next = md;
450 mprev->m_next = mah;
451 m->m_pkthdr.len += ahlen;
452
453 /* fix plen */
454 if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) {
455 ipseclog((LOG_ERR,
456 "ip6_output: AH with IPv6 jumbogram is not supported\n"));
457 m_freem(m);
458 return EINVAL;
459 }
460 ip6 = mtod(m, struct ip6_hdr *);
461 ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
462
463 if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
464 ipseclog((LOG_DEBUG, "ah6_output: internal error: "
2d21ac55
A
465 "sav->replay is null: SPI=%u\n",
466 (u_int32_t)ntohl(sav->spi)));
467 IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
1c79356b
A
468 m_freem(m);
469 return EINVAL;
470 }
471
9bccf70c
A
472 algo = ah_algorithm_lookup(sav->alg_auth);
473 if (!algo) {
474 ipseclog((LOG_ERR, "ah6_output: unsupported algorithm: "
475 "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
2d21ac55 476 IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
9bccf70c
A
477 m_freem(m);
478 return EINVAL;
479 }
1c79356b
A
480 spi = sav->spi;
481
482 /*
483 * initialize AH.
484 */
485 if (sav->flags & SADB_X_EXT_OLD) {
486 struct ah *ahdr = mtod(mah, struct ah *);
487
488 plen = mah->m_len - sizeof(struct ah);
489 ahsumpos = (u_char *)(ahdr + 1);
490 ahdr->ah_nxt = *nexthdrp;
491 *nexthdrp = IPPROTO_AH;
492 ahdr->ah_len = plen >> 2;
493 ahdr->ah_reserve = htons(0);
494 ahdr->ah_spi = spi;
495 bzero(ahdr + 1, plen);
496 } else {
497 struct newah *ahdr = mtod(mah, struct newah *);
498
499 plen = mah->m_len - sizeof(struct newah);
500 ahsumpos = (u_char *)(ahdr + 1);
501 ahdr->ah_nxt = *nexthdrp;
502 *nexthdrp = IPPROTO_AH;
503 ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */
504 ahdr->ah_reserve = htons(0);
505 ahdr->ah_spi = spi;
506 if (sav->replay->count == ~0) {
507 if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
508 /* XXX Is it noisy ? */
509 ipseclog((LOG_WARNING,
2d21ac55 510 "replay counter overflowed. %s\n",
1c79356b 511 ipsec_logsastr(sav)));
2d21ac55 512 IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
1c79356b
A
513 m_freem(m);
514 return EINVAL;
515 }
516 }
2d21ac55 517 lck_mtx_lock(sadb_mutex);
1c79356b 518 sav->replay->count++;
2d21ac55 519 lck_mtx_unlock(sadb_mutex);
1c79356b
A
520 /*
521 * XXX sequence number must not be cycled, if the SA is
522 * installed by IKE daemon.
523 */
524 ahdr->ah_seq = htonl(sav->replay->count);
525 bzero(ahdr + 1, plen);
526 }
527
528 /*
529 * calcurate the checksum, based on security association
530 * and the algorithm specified.
531 */
9bccf70c 532 error = ah6_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
1c79356b 533 if (error) {
2d21ac55 534 IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
1c79356b
A
535 m_freem(m);
536 } else {
2d21ac55 537 IPSEC_STAT_INCREMENT(ipsec6stat.out_success);
1c79356b
A
538 key_sa_recordxfer(sav, m);
539 }
2d21ac55 540 IPSEC_STAT_INCREMENT(ipsec6stat.out_ahhist[sav->alg_auth]);
1c79356b
A
541
542 return(error);
543}
544#endif
545
9bccf70c 546#if INET
1c79356b
A
547/*
548 * Find the final destination if there is loose/strict source routing option.
549 * Returns NULL if there's no source routing options.
550 * Returns NULL on errors too.
551 * Note that this function will return a pointer INTO the given parameter,
552 * struct mbuf *m.
553 * The mbuf must be pulled up toward, at least, ip option part.
554 */
555static struct in_addr *
556ah4_finaldst(m)
557 struct mbuf *m;
558{
559 struct ip *ip;
560 int optlen;
561 u_char *q;
562 int i;
563 int hlen;
564
565 if (!m)
566 panic("ah4_finaldst: m == NULL");
567 ip = mtod(m, struct ip *);
568#ifdef _IP_VHL
569 hlen = IP_VHL_HL(ip->ip_vhl) << 2;
570#else
571 hlen = ip->ip_hl << 2;
572#endif
573
574 if (m->m_len < hlen) {
575 ipseclog((LOG_DEBUG,
576 "ah4_finaldst: parameter mbuf wrong (not pulled up)\n"));
577 return NULL;
578 }
579
580 if (hlen == sizeof(struct ip))
581 return NULL;
582
583 optlen = hlen - sizeof(struct ip);
584 if (optlen < 0) {
585 ipseclog((LOG_DEBUG, "ah4_finaldst: wrong optlen %d\n",
586 optlen));
587 return NULL;
588 }
589
590 q = (u_char *)(ip + 1);
591 i = 0;
592 while (i < optlen) {
9bccf70c
A
593 if (i + IPOPT_OPTVAL >= optlen)
594 return NULL;
595 if (q[i + IPOPT_OPTVAL] == IPOPT_EOL ||
596 q[i + IPOPT_OPTVAL] == IPOPT_NOP ||
597 i + IPOPT_OLEN < optlen)
598 ;
599 else
600 return NULL;
601
1c79356b
A
602 switch (q[i + IPOPT_OPTVAL]) {
603 case IPOPT_EOL:
604 i = optlen; /* bye */
605 break;
606 case IPOPT_NOP:
607 i++;
608 break;
609 case IPOPT_LSRR:
610 case IPOPT_SSRR:
9bccf70c
A
611 if (q[i + IPOPT_OLEN] < 2 + sizeof(struct in_addr) ||
612 optlen - i < q[i + IPOPT_OLEN]) {
1c79356b
A
613 ipseclog((LOG_ERR,
614 "ip_finaldst: invalid IP option "
615 "(code=%02x len=%02x)\n",
616 q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
617 return NULL;
618 }
619 i += q[i + IPOPT_OLEN] - sizeof(struct in_addr);
316670eb 620 return (struct in_addr *)(void *)(q + i);
1c79356b 621 default:
9bccf70c
A
622 if (q[i + IPOPT_OLEN] < 2 ||
623 optlen - i < q[i + IPOPT_OLEN]) {
1c79356b
A
624 ipseclog((LOG_ERR,
625 "ip_finaldst: invalid IP option "
626 "(code=%02x len=%02x)\n",
627 q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
628 return NULL;
629 }
630 i += q[i + IPOPT_OLEN];
631 break;
632 }
633 }
634 return NULL;
635}
9bccf70c 636#endif