1121472Sume/*	$KAME: ip6opt.c,v 1.13 2003/06/06 10:08:20 suz Exp $	*/
2121472Sume
354696Sshin/*
454696Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
554696Sshin * All rights reserved.
654696Sshin *
754696Sshin * Redistribution and use in source and binary forms, with or without
854696Sshin * modification, are permitted provided that the following conditions
954696Sshin * are met:
1054696Sshin * 1. Redistributions of source code must retain the above copyright
1154696Sshin *    notice, this list of conditions and the following disclaimer.
1254696Sshin * 2. Redistributions in binary form must reproduce the above copyright
1354696Sshin *    notice, this list of conditions and the following disclaimer in the
1454696Sshin *    documentation and/or other materials provided with the distribution.
1554696Sshin * 3. Neither the name of the project nor the names of its contributors
1654696Sshin *    may be used to endorse or promote products derived from this software
1754696Sshin *    without specific prior written permission.
1854696Sshin *
1954696Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2054696Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2154696Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2254696Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2354696Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2454696Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2554696Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2654696Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2754696Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2854696Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2954696Sshin * SUCH DAMAGE.
3054696Sshin */
3154696Sshin
3292986Sobrien#include <sys/cdefs.h>
3392986Sobrien__FBSDID("$FreeBSD$");
3492986Sobrien
3554696Sshin#include <sys/param.h>
3654696Sshin#include <sys/types.h>
3754696Sshin#include <sys/socket.h>
3854696Sshin
3954696Sshin#include <netinet/in.h>
4054696Sshin#include <netinet/ip6.h>
4154696Sshin
4254696Sshin#include <string.h>
4354696Sshin#include <stdio.h>
4454696Sshin
4554696Sshinstatic int ip6optlen(u_int8_t *opt, u_int8_t *lim);
4654696Sshinstatic void inet6_insert_padopt(u_char *p, int len);
4754696Sshin
4854696Sshin/*
4954696Sshin * This function returns the number of bytes required to hold an option
5054696Sshin * when it is stored as ancillary data, including the cmsghdr structure
5154696Sshin * at the beginning, and any padding at the end (to make its size a
5254696Sshin * multiple of 8 bytes).  The argument is the size of the structure
5354696Sshin * defining the option, which must include any pad bytes at the
5454696Sshin * beginning (the value y in the alignment term "xn + y"), the type
5554696Sshin * byte, the length byte, and the option data.
5654696Sshin */
5754696Sshinint
58199188Sumeinet6_option_space(int nbytes)
5954696Sshin{
6054696Sshin	nbytes += 2;	/* we need space for nxt-hdr and length fields */
6154696Sshin	return(CMSG_SPACE((nbytes + 7) & ~7));
6254696Sshin}
6354696Sshin
6454696Sshin/*
6554696Sshin * This function is called once per ancillary data object that will
6654696Sshin * contain either Hop-by-Hop or Destination options.  It returns 0 on
6754696Sshin * success or -1 on an error.
6854696Sshin */
6954696Sshinint
70199188Sumeinet6_option_init(void *bp, struct cmsghdr **cmsgp, int type)
7154696Sshin{
7292889Sobrien	struct cmsghdr *ch = (struct cmsghdr *)bp;
7354696Sshin
7454696Sshin	/* argument validation */
7554696Sshin	if (type != IPV6_HOPOPTS && type != IPV6_DSTOPTS)
7654696Sshin		return(-1);
7754696Sshin
7854696Sshin	ch->cmsg_level = IPPROTO_IPV6;
7954696Sshin	ch->cmsg_type = type;
8054696Sshin	ch->cmsg_len = CMSG_LEN(0);
8154696Sshin
8254696Sshin	*cmsgp = ch;
8354696Sshin	return(0);
8454696Sshin}
8554696Sshin
8654696Sshin/*
8754696Sshin * This function appends a Hop-by-Hop option or a Destination option
8854696Sshin * into an ancillary data object that has been initialized by
8954696Sshin * inet6_option_init().  This function returns 0 if it succeeds or -1 on
9054696Sshin * an error.
9154696Sshin * multx is the value x in the alignment term "xn + y" described
9254696Sshin * earlier.  It must have a value of 1, 2, 4, or 8.
9354696Sshin * plusy is the value y in the alignment term "xn + y" described
9454696Sshin * earlier.  It must have a value between 0 and 7, inclusive.
9554696Sshin */
9654696Sshinint
97199188Sumeinet6_option_append(struct cmsghdr *cmsg, const u_int8_t *typep, int multx,
98199188Sume    int plusy)
9954696Sshin{
10054696Sshin	int padlen, optlen, off;
10192889Sobrien	u_char *bp = (u_char *)cmsg + cmsg->cmsg_len;
10254696Sshin	struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg);
10354696Sshin
10454696Sshin	/* argument validation */
10554696Sshin	if (multx != 1 && multx != 2 && multx != 4 && multx != 8)
10654696Sshin		return(-1);
10754696Sshin	if (plusy < 0 || plusy > 7)
10854696Sshin		return(-1);
10954696Sshin
11054696Sshin	/*
11154696Sshin	 * If this is the first option, allocate space for the
11254696Sshin	 * first 2 bytes(for next header and length fields) of
11354696Sshin	 * the option header.
11454696Sshin	 */
11554696Sshin	if (bp == (u_char *)eh) {
11654696Sshin		bp += 2;
11754696Sshin		cmsg->cmsg_len += 2;
11854696Sshin	}
11954696Sshin
12054696Sshin	/* calculate pad length before the option. */
12154696Sshin	off = bp - (u_char *)eh;
12254696Sshin	padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) -
12354696Sshin		(off % multx);
12454696Sshin	padlen += plusy;
125121472Sume	padlen %= multx;	/* keep the pad as short as possible */
12654696Sshin	/* insert padding */
12754696Sshin	inet6_insert_padopt(bp, padlen);
12854696Sshin	cmsg->cmsg_len += padlen;
12954696Sshin	bp += padlen;
13054696Sshin
13154696Sshin	/* copy the option */
13254696Sshin	if (typep[0] == IP6OPT_PAD1)
13354696Sshin		optlen = 1;
13454696Sshin	else
13554696Sshin		optlen = typep[1] + 2;
13654696Sshin	memcpy(bp, typep, optlen);
13754696Sshin	bp += optlen;
13854696Sshin	cmsg->cmsg_len += optlen;
13954696Sshin
14054696Sshin	/* calculate pad length after the option and insert the padding */
14154696Sshin	off = bp - (u_char *)eh;
14254696Sshin	padlen = ((off + 7) & ~7) - off;
14354696Sshin	inet6_insert_padopt(bp, padlen);
14454696Sshin	bp += padlen;
14554696Sshin	cmsg->cmsg_len += padlen;
14654696Sshin
14754696Sshin	/* update the length field of the ip6 option header */
14854696Sshin	eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1;
14954696Sshin
15054696Sshin	return(0);
15154696Sshin}
15254696Sshin
15354696Sshin/*
15454696Sshin * This function appends a Hop-by-Hop option or a Destination option
15554696Sshin * into an ancillary data object that has been initialized by
15654696Sshin * inet6_option_init().  This function returns a pointer to the 8-bit
15754696Sshin * option type field that starts the option on success, or NULL on an
15854696Sshin * error.
15954696Sshin * The difference between this function and inet6_option_append() is
16054696Sshin * that the latter copies the contents of a previously built option into
16154696Sshin * the ancillary data object while the current function returns a
16254696Sshin * pointer to the space in the data object where the option's TLV must
16354696Sshin * then be built by the caller.
16454696Sshin *
16554696Sshin */
16654696Sshinu_int8_t *
167199188Sumeinet6_option_alloc(struct cmsghdr *cmsg, int datalen, int multx, int plusy)
16854696Sshin{
16954696Sshin	int padlen, off;
17092889Sobrien	u_int8_t *bp = (u_char *)cmsg + cmsg->cmsg_len;
17154696Sshin	u_int8_t *retval;
17254696Sshin	struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg);
17354696Sshin
17454696Sshin	/* argument validation */
17554696Sshin	if (multx != 1 && multx != 2 && multx != 4 && multx != 8)
17654696Sshin		return(NULL);
17754696Sshin	if (plusy < 0 || plusy > 7)
17854696Sshin		return(NULL);
17954696Sshin
18054696Sshin	/*
18154696Sshin	 * If this is the first option, allocate space for the
18254696Sshin	 * first 2 bytes(for next header and length fields) of
18354696Sshin	 * the option header.
18454696Sshin	 */
18554696Sshin	if (bp == (u_char *)eh) {
18654696Sshin		bp += 2;
18754696Sshin		cmsg->cmsg_len += 2;
18854696Sshin	}
18954696Sshin
19054696Sshin	/* calculate pad length before the option. */
19154696Sshin	off = bp - (u_char *)eh;
19254696Sshin	padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) -
19354696Sshin		(off % multx);
19454696Sshin	padlen += plusy;
195121472Sume	padlen %= multx;	/* keep the pad as short as possible */
19654696Sshin	/* insert padding */
19754696Sshin	inet6_insert_padopt(bp, padlen);
19854696Sshin	cmsg->cmsg_len += padlen;
19954696Sshin	bp += padlen;
20054696Sshin
20154696Sshin	/* keep space to store specified length of data */
20254696Sshin	retval = bp;
20354696Sshin	bp += datalen;
20454696Sshin	cmsg->cmsg_len += datalen;
20554696Sshin
20654696Sshin	/* calculate pad length after the option and insert the padding */
20754696Sshin	off = bp - (u_char *)eh;
20854696Sshin	padlen = ((off + 7) & ~7) - off;
20954696Sshin	inet6_insert_padopt(bp, padlen);
21054696Sshin	bp += padlen;
21154696Sshin	cmsg->cmsg_len += padlen;
21254696Sshin
21354696Sshin	/* update the length field of the ip6 option header */
21454696Sshin	eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1;
21554696Sshin
21654696Sshin	return(retval);
21754696Sshin}
21854696Sshin
21954696Sshin/*
22054696Sshin * This function processes the next Hop-by-Hop option or Destination
22154696Sshin * option in an ancillary data object.  If another option remains to be
22254696Sshin * processed, the return value of the function is 0 and *tptrp points to
22354696Sshin * the 8-bit option type field (which is followed by the 8-bit option
22454696Sshin * data length, followed by the option data).  If no more options remain
22554696Sshin * to be processed, the return value is -1 and *tptrp is NULL.  If an
22654696Sshin * error occurs, the return value is -1 and *tptrp is not NULL.
22754696Sshin * (RFC 2292, 6.3.5)
22854696Sshin */
22954696Sshinint
230199188Sumeinet6_option_next(const struct cmsghdr *cmsg, u_int8_t **tptrp)
23154696Sshin{
23254696Sshin	struct ip6_ext *ip6e;
23354696Sshin	int hdrlen, optlen;
23454696Sshin	u_int8_t *lim;
23554696Sshin
23654696Sshin	if (cmsg->cmsg_level != IPPROTO_IPV6 ||
23754696Sshin	    (cmsg->cmsg_type != IPV6_HOPOPTS &&
23854696Sshin	     cmsg->cmsg_type != IPV6_DSTOPTS))
23954696Sshin		return(-1);
24054696Sshin
24154696Sshin	/* message length validation */
24254696Sshin	if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext)))
24354696Sshin		return(-1);
24454696Sshin	ip6e = (struct ip6_ext *)CMSG_DATA(cmsg);
24554696Sshin	hdrlen = (ip6e->ip6e_len + 1) << 3;
24654696Sshin	if (cmsg->cmsg_len < CMSG_SPACE(hdrlen))
24754696Sshin		return(-1);
24854696Sshin
24954696Sshin	/*
25054696Sshin	 * If the caller does not specify the starting point,
25154696Sshin	 * simply return the 1st option.
25254696Sshin	 * Otherwise, search the option list for the next option.
25354696Sshin	 */
25454696Sshin	lim = (u_int8_t *)ip6e + hdrlen;
25554696Sshin	if (*tptrp == NULL)
25654696Sshin		*tptrp = (u_int8_t *)(ip6e + 1);
25754696Sshin	else {
25854696Sshin		if ((optlen = ip6optlen(*tptrp, lim)) == 0)
25954696Sshin			return(-1);
26054696Sshin
26154696Sshin		*tptrp = *tptrp + optlen;
26254696Sshin	}
26354696Sshin	if (*tptrp >= lim) {	/* there is no option */
26454696Sshin		*tptrp = NULL;
26554696Sshin		return(-1);
26654696Sshin	}
26754696Sshin	/*
26854696Sshin	 * Finally, checks if the next option is safely stored in the
26954696Sshin	 * cmsg data.
27054696Sshin	 */
27154696Sshin	if (ip6optlen(*tptrp, lim) == 0)
27254696Sshin		return(-1);
27354696Sshin	else
27454696Sshin		return(0);
27554696Sshin}
27654696Sshin
27754696Sshin/*
27854696Sshin * This function is similar to the inet6_option_next() function,
27954696Sshin * except this function lets the caller specify the option type to be
28054696Sshin * searched for, instead of always returning the next option in the
28154696Sshin * ancillary data object.
28254696Sshin * Note: RFC 2292 says the type of tptrp is u_int8_t *, but we think
28354696Sshin *       it's a typo. The variable should be type of u_int8_t **.
28454696Sshin */
28554696Sshinint
286199188Sumeinet6_option_find(const struct cmsghdr *cmsg, u_int8_t **tptrp, int type)
28754696Sshin{
28854696Sshin	struct ip6_ext *ip6e;
28954696Sshin	int hdrlen, optlen;
29054696Sshin	u_int8_t *optp, *lim;
29154696Sshin
29254696Sshin	if (cmsg->cmsg_level != IPPROTO_IPV6 ||
29354696Sshin	    (cmsg->cmsg_type != IPV6_HOPOPTS &&
29454696Sshin	     cmsg->cmsg_type != IPV6_DSTOPTS))
29554696Sshin		return(-1);
29654696Sshin
29754696Sshin	/* message length validation */
29854696Sshin	if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext)))
29954696Sshin		return(-1);
30054696Sshin	ip6e = (struct ip6_ext *)CMSG_DATA(cmsg);
30154696Sshin	hdrlen = (ip6e->ip6e_len + 1) << 3;
30254696Sshin	if (cmsg->cmsg_len < CMSG_SPACE(hdrlen))
30354696Sshin		return(-1);
30454696Sshin
30554696Sshin	/*
30654696Sshin	 * If the caller does not specify the starting point,
30754696Sshin	 * search from the beginning of the option list.
30854696Sshin	 * Otherwise, search from *the next option* of the specified point.
30954696Sshin	 */
31054696Sshin	lim = (u_int8_t *)ip6e + hdrlen;
31154696Sshin	if (*tptrp == NULL)
31254696Sshin		*tptrp = (u_int8_t *)(ip6e + 1);
31354696Sshin	else {
31454696Sshin		if ((optlen = ip6optlen(*tptrp, lim)) == 0)
31554696Sshin			return(-1);
31654696Sshin
31754696Sshin		*tptrp = *tptrp + optlen;
31854696Sshin	}
31954696Sshin	for (optp = *tptrp; optp < lim; optp += optlen) {
32054696Sshin		if (*optp == type) {
32154696Sshin			*tptrp = optp;
32254696Sshin			return(0);
32354696Sshin		}
32454696Sshin		if ((optlen = ip6optlen(optp, lim)) == 0)
32554696Sshin			return(-1);
32654696Sshin	}
32754696Sshin
32854696Sshin	/* search failed */
32954696Sshin	*tptrp = NULL;
33054696Sshin	return(-1);
33154696Sshin}
33254696Sshin
33354696Sshin/*
33454696Sshin * Calculate the length of a given IPv6 option. Also checks
33554696Sshin * if the option is safely stored in user's buffer according to the
33654696Sshin * calculated length and the limitation of the buffer.
33754696Sshin */
33854696Sshinstatic int
339199188Sumeip6optlen(u_int8_t *opt, u_int8_t *lim)
34054696Sshin{
34154696Sshin	int optlen;
34254696Sshin
34354696Sshin	if (*opt == IP6OPT_PAD1)
34454696Sshin		optlen = 1;
34554696Sshin	else {
34654696Sshin		/* is there enough space to store type and len? */
34754696Sshin		if (opt + 2 > lim)
34854696Sshin			return(0);
34954696Sshin		optlen = *(opt + 1) + 2;
35054696Sshin	}
35154696Sshin	if (opt + optlen <= lim)
35254696Sshin		return(optlen);
35354696Sshin
35454696Sshin	return(0);
35554696Sshin}
35654696Sshin
35754696Sshinstatic void
35854696Sshininet6_insert_padopt(u_char *p, int len)
35954696Sshin{
36054696Sshin	switch(len) {
36154696Sshin	 case 0:
36254696Sshin		 return;
36354696Sshin	 case 1:
36454696Sshin		 p[0] = IP6OPT_PAD1;
36554696Sshin		 return;
36654696Sshin	 default:
36754696Sshin		 p[0] = IP6OPT_PADN;
36854696Sshin		 p[1] = len - 2;
36954696Sshin		 memset(&p[2], 0, len - 2);
37054696Sshin		 return;
37154696Sshin	}
37254696Sshin}
373121472Sume
374121472Sume/*
375148160Sume * The following functions are defined in RFC3542, which is a successor
376148160Sume * of RFC2292.
377121472Sume */
378121472Sume
379121472Sumeint
380121472Sumeinet6_opt_init(void *extbuf, socklen_t extlen)
381121472Sume{
382121472Sume	struct ip6_ext *ext = (struct ip6_ext *)extbuf;
383121472Sume
384121472Sume	if (extlen < 0 || (extlen % 8))
385121472Sume		return(-1);
386121472Sume
387121472Sume	if (ext) {
388121472Sume		if (extlen == 0)
389121472Sume			return(-1);
390121472Sume		ext->ip6e_len = (extlen >> 3) - 1;
391121472Sume	}
392121472Sume
393121472Sume	return(2);		/* sizeof the next and the length fields */
394121472Sume}
395121472Sume
396121472Sumeint
397121472Sumeinet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
398121472Sume		 socklen_t len, u_int8_t align, void **databufp)
399121472Sume{
400121472Sume	int currentlen = offset, padlen = 0;
401121472Sume
402121472Sume	/*
403121472Sume	 * The option type must have a value from 2 to 255, inclusive.
404121472Sume	 * (0 and 1 are reserved for the Pad1 and PadN options, respectively.)
405121472Sume	 */
406122682Sume	if (type < 2)
407121472Sume		return(-1);
408121472Sume
409121472Sume	/*
410121472Sume	 * The option data length must have a value between 0 and 255,
411121472Sume	 * inclusive, and is the length of the option data that follows.
412121472Sume	 */
413121472Sume	if (len < 0 || len > 255)
414121472Sume		return(-1);
415121472Sume
416121472Sume	/*
417121472Sume	 * The align parameter must have a value of 1, 2, 4, or 8.
418121472Sume	 * The align value can not exceed the value of len.
419121472Sume	 */
420121472Sume	if (align != 1 && align != 2 && align != 4 && align != 8)
421121472Sume		return(-1);
422121472Sume	if (align > len)
423121472Sume		return(-1);
424121472Sume
425121472Sume	/* Calculate the padding length. */
426121472Sume	currentlen += 2 + len;	/* 2 means "type + len" */
427121472Sume	if (currentlen % align)
428121472Sume		padlen = align - (currentlen % align);
429121472Sume
430121472Sume	/* The option must fit in the extension header buffer. */
431121472Sume	currentlen += padlen;
432121472Sume	if (extlen &&		/* XXX: right? */
433121472Sume	    currentlen > extlen)
434121472Sume		return(-1);
435121472Sume
436121472Sume	if (extbuf) {
437121472Sume		u_int8_t *optp = (u_int8_t *)extbuf + offset;
438121472Sume
439121472Sume		if (padlen == 1) {
440121472Sume			/* insert a Pad1 option */
441121472Sume			*optp = IP6OPT_PAD1;
442121472Sume			optp++;
443121472Sume		}
444121472Sume		else if (padlen > 0) {
445121472Sume			/* insert a PadN option for alignment */
446121472Sume			*optp++ = IP6OPT_PADN;
447121472Sume			*optp++ = padlen - 2;
448121472Sume			memset(optp, 0, padlen - 2);
449121472Sume			optp += (padlen - 2);
450121472Sume		}
451121472Sume
452121472Sume		*optp++ = type;
453121472Sume		*optp++ = len;
454121472Sume
455121472Sume		*databufp = optp;
456121472Sume	}
457121472Sume
458121472Sume	return(currentlen);
459121472Sume}
460121472Sume
461121472Sumeint
462121472Sumeinet6_opt_finish(void *extbuf, socklen_t extlen, int offset)
463121472Sume{
464242544Seadler	int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;
465121472Sume
466121472Sume	if (extbuf) {
467121472Sume		u_int8_t *padp;
468121472Sume		int padlen = updatelen - offset;
469121472Sume
470121472Sume		if (updatelen > extlen)
471121472Sume			return(-1);
472121472Sume
473121472Sume		padp = (u_int8_t *)extbuf + offset;
474121472Sume		if (padlen == 1)
475121472Sume			*padp = IP6OPT_PAD1;
476121472Sume		else if (padlen > 0) {
477121472Sume			*padp++ = IP6OPT_PADN;
478121472Sume			*padp++ = (padlen - 2);
479121472Sume			memset(padp, 0, padlen - 2);
480121472Sume		}
481121472Sume	}
482121472Sume
483121472Sume	return(updatelen);
484121472Sume}
485121472Sume
486121472Sumeint
487121472Sumeinet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen)
488121472Sume{
489121472Sume
490121472Sume	memcpy((u_int8_t *)databuf + offset, val, vallen);
491121472Sume	return(offset + vallen);
492121472Sume}
493121472Sume
494121472Sumeint
495121472Sumeinet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep,
496121496Sume	       socklen_t *lenp, void **databufp)
497121472Sume{
498121472Sume	u_int8_t *optp, *lim;
499121472Sume	int optlen;
500121472Sume
501121472Sume	/* Validate extlen. XXX: is the variable really necessary?? */
502121472Sume	if (extlen == 0 || (extlen % 8))
503121472Sume		return(-1);
504121472Sume	lim = (u_int8_t *)extbuf + extlen;
505121472Sume
506121472Sume	/*
507121472Sume	 * If this is the first time this function called for this options
508121472Sume	 * header, simply return the 1st option.
509121472Sume	 * Otherwise, search the option list for the next option.
510121472Sume	 */
511121472Sume	if (offset == 0) {
512121472Sume		optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
513121472Sume	}
514121472Sume	else
515121472Sume		optp = (u_int8_t *)extbuf + offset;
516121472Sume
517121472Sume	/* Find the next option skipping any padding options. */
518121472Sume	while(optp < lim) {
519121472Sume		switch(*optp) {
520121472Sume		case IP6OPT_PAD1:
521121472Sume			optp++;
522121472Sume			break;
523121472Sume		case IP6OPT_PADN:
524121472Sume			if ((optlen = ip6optlen(optp, lim)) == 0)
525121472Sume				goto optend;
526121472Sume			optp += optlen;
527121472Sume			break;
528121472Sume		default:	/* found */
529121472Sume			if ((optlen = ip6optlen(optp, lim)) == 0)
530121472Sume				goto optend;
531121472Sume			*typep = *optp;
532121472Sume			*lenp = optlen - 2;
533121472Sume			*databufp = optp + 2;
534121472Sume			return(optp + optlen - (u_int8_t *)extbuf);
535121472Sume		}
536121472Sume	}
537121472Sume
538121472Sume  optend:
539121472Sume	*databufp = NULL; /* for safety */
540121472Sume	return(-1);
541121472Sume}
542121472Sume
543121472Sumeint
544121472Sumeinet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
545121472Sume	       socklen_t *lenp, void **databufp)
546121472Sume{
547121472Sume	u_int8_t *optp, *lim;
548121472Sume	int optlen;
549121472Sume
550121472Sume	/* Validate extlen. XXX: is the variable really necessary?? */
551121472Sume	if (extlen == 0 || (extlen % 8))
552121472Sume		return(-1);
553121472Sume	lim = (u_int8_t *)extbuf + extlen;
554121472Sume
555121472Sume	/*
556121472Sume	 * If this is the first time this function called for this options
557121472Sume	 * header, simply return the 1st option.
558121472Sume	 * Otherwise, search the option list for the next option.
559121472Sume	 */
560121472Sume	if (offset == 0) {
561121472Sume		optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
562121472Sume	}
563121472Sume	else
564121472Sume		optp = (u_int8_t *)extbuf + offset;
565121472Sume
566121472Sume	/* Find the specified option */
567121472Sume	while(optp < lim) {
568121472Sume		if ((optlen = ip6optlen(optp, lim)) == 0)
569121472Sume			goto optend;
570121472Sume
571121472Sume		if (*optp == type) { /* found */
572121472Sume			*lenp = optlen - 2;
573121472Sume			*databufp = optp + 2;
574121472Sume			return(optp + optlen - (u_int8_t *)extbuf);
575121472Sume		}
576121472Sume
577121472Sume		optp += optlen;
578121472Sume	}
579121472Sume
580121472Sume  optend:
581121472Sume	*databufp = NULL; /* for safety */
582121472Sume	return(-1);
583121472Sume}
584121472Sume
585121472Sumeint
586121472Sumeinet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen)
587121472Sume{
588121472Sume
589121472Sume	/* we can't assume alignment here */
590121472Sume	memcpy(val, (u_int8_t *)databuf + offset, vallen);
591121472Sume
592121472Sume	return(offset + vallen);
593121472Sume}
594