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
48269454Smarcel#ifndef IPV6_2292HOPOPTS
49269454Smarcel#define	IPV6_2292HOPOPTS	22
50269454Smarcel#endif
51269454Smarcel#ifndef IPV6_2292DSTOPTS
52269454Smarcel#define	IPV6_2292DSTOPTS	23
53269454Smarcel#endif
54269454Smarcel
55269454Smarcel#define	is_ipv6_hopopts(x)	\
56269454Smarcel	((x) == IPV6_HOPOPTS || (x) == IPV6_2292HOPOPTS)
57269454Smarcel#define	is_ipv6_dstopts(x)	\
58269454Smarcel	((x) == IPV6_DSTOPTS || (x) == IPV6_2292DSTOPTS)
59269454Smarcel
6054696Sshin/*
6154696Sshin * This function returns the number of bytes required to hold an option
6254696Sshin * when it is stored as ancillary data, including the cmsghdr structure
6354696Sshin * at the beginning, and any padding at the end (to make its size a
6454696Sshin * multiple of 8 bytes).  The argument is the size of the structure
6554696Sshin * defining the option, which must include any pad bytes at the
6654696Sshin * beginning (the value y in the alignment term "xn + y"), the type
6754696Sshin * byte, the length byte, and the option data.
6854696Sshin */
6954696Sshinint
70199188Sumeinet6_option_space(int nbytes)
7154696Sshin{
7254696Sshin	nbytes += 2;	/* we need space for nxt-hdr and length fields */
7354696Sshin	return(CMSG_SPACE((nbytes + 7) & ~7));
7454696Sshin}
7554696Sshin
7654696Sshin/*
7754696Sshin * This function is called once per ancillary data object that will
7854696Sshin * contain either Hop-by-Hop or Destination options.  It returns 0 on
7954696Sshin * success or -1 on an error.
8054696Sshin */
8154696Sshinint
82199188Sumeinet6_option_init(void *bp, struct cmsghdr **cmsgp, int type)
8354696Sshin{
8492889Sobrien	struct cmsghdr *ch = (struct cmsghdr *)bp;
8554696Sshin
8654696Sshin	/* argument validation */
87269454Smarcel	if (!is_ipv6_hopopts(type) && !is_ipv6_dstopts(type))
8854696Sshin		return(-1);
89269454Smarcel
9054696Sshin	ch->cmsg_level = IPPROTO_IPV6;
9154696Sshin	ch->cmsg_type = type;
9254696Sshin	ch->cmsg_len = CMSG_LEN(0);
9354696Sshin
9454696Sshin	*cmsgp = ch;
9554696Sshin	return(0);
9654696Sshin}
9754696Sshin
9854696Sshin/*
9954696Sshin * This function appends a Hop-by-Hop option or a Destination option
10054696Sshin * into an ancillary data object that has been initialized by
10154696Sshin * inet6_option_init().  This function returns 0 if it succeeds or -1 on
10254696Sshin * an error.
10354696Sshin * multx is the value x in the alignment term "xn + y" described
10454696Sshin * earlier.  It must have a value of 1, 2, 4, or 8.
10554696Sshin * plusy is the value y in the alignment term "xn + y" described
10654696Sshin * earlier.  It must have a value between 0 and 7, inclusive.
10754696Sshin */
10854696Sshinint
109199188Sumeinet6_option_append(struct cmsghdr *cmsg, const u_int8_t *typep, int multx,
110199188Sume    int plusy)
11154696Sshin{
11254696Sshin	int padlen, optlen, off;
11392889Sobrien	u_char *bp = (u_char *)cmsg + cmsg->cmsg_len;
11454696Sshin	struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg);
11554696Sshin
11654696Sshin	/* argument validation */
11754696Sshin	if (multx != 1 && multx != 2 && multx != 4 && multx != 8)
11854696Sshin		return(-1);
11954696Sshin	if (plusy < 0 || plusy > 7)
12054696Sshin		return(-1);
12154696Sshin
12254696Sshin	/*
12354696Sshin	 * If this is the first option, allocate space for the
12454696Sshin	 * first 2 bytes(for next header and length fields) of
12554696Sshin	 * the option header.
12654696Sshin	 */
12754696Sshin	if (bp == (u_char *)eh) {
12854696Sshin		bp += 2;
12954696Sshin		cmsg->cmsg_len += 2;
13054696Sshin	}
13154696Sshin
13254696Sshin	/* calculate pad length before the option. */
13354696Sshin	off = bp - (u_char *)eh;
13454696Sshin	padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) -
13554696Sshin		(off % multx);
13654696Sshin	padlen += plusy;
137121472Sume	padlen %= multx;	/* keep the pad as short as possible */
13854696Sshin	/* insert padding */
13954696Sshin	inet6_insert_padopt(bp, padlen);
14054696Sshin	cmsg->cmsg_len += padlen;
14154696Sshin	bp += padlen;
14254696Sshin
14354696Sshin	/* copy the option */
14454696Sshin	if (typep[0] == IP6OPT_PAD1)
14554696Sshin		optlen = 1;
14654696Sshin	else
14754696Sshin		optlen = typep[1] + 2;
14854696Sshin	memcpy(bp, typep, optlen);
14954696Sshin	bp += optlen;
15054696Sshin	cmsg->cmsg_len += optlen;
15154696Sshin
15254696Sshin	/* calculate pad length after the option and insert the padding */
15354696Sshin	off = bp - (u_char *)eh;
15454696Sshin	padlen = ((off + 7) & ~7) - off;
15554696Sshin	inet6_insert_padopt(bp, padlen);
15654696Sshin	bp += padlen;
15754696Sshin	cmsg->cmsg_len += padlen;
15854696Sshin
15954696Sshin	/* update the length field of the ip6 option header */
16054696Sshin	eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1;
16154696Sshin
16254696Sshin	return(0);
16354696Sshin}
16454696Sshin
16554696Sshin/*
16654696Sshin * This function appends a Hop-by-Hop option or a Destination option
16754696Sshin * into an ancillary data object that has been initialized by
16854696Sshin * inet6_option_init().  This function returns a pointer to the 8-bit
16954696Sshin * option type field that starts the option on success, or NULL on an
17054696Sshin * error.
17154696Sshin * The difference between this function and inet6_option_append() is
17254696Sshin * that the latter copies the contents of a previously built option into
17354696Sshin * the ancillary data object while the current function returns a
17454696Sshin * pointer to the space in the data object where the option's TLV must
17554696Sshin * then be built by the caller.
17654696Sshin *
17754696Sshin */
17854696Sshinu_int8_t *
179199188Sumeinet6_option_alloc(struct cmsghdr *cmsg, int datalen, int multx, int plusy)
18054696Sshin{
18154696Sshin	int padlen, off;
18292889Sobrien	u_int8_t *bp = (u_char *)cmsg + cmsg->cmsg_len;
18354696Sshin	u_int8_t *retval;
18454696Sshin	struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg);
18554696Sshin
18654696Sshin	/* argument validation */
18754696Sshin	if (multx != 1 && multx != 2 && multx != 4 && multx != 8)
18854696Sshin		return(NULL);
18954696Sshin	if (plusy < 0 || plusy > 7)
19054696Sshin		return(NULL);
19154696Sshin
19254696Sshin	/*
19354696Sshin	 * If this is the first option, allocate space for the
19454696Sshin	 * first 2 bytes(for next header and length fields) of
19554696Sshin	 * the option header.
19654696Sshin	 */
19754696Sshin	if (bp == (u_char *)eh) {
19854696Sshin		bp += 2;
19954696Sshin		cmsg->cmsg_len += 2;
20054696Sshin	}
20154696Sshin
20254696Sshin	/* calculate pad length before the option. */
20354696Sshin	off = bp - (u_char *)eh;
20454696Sshin	padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) -
20554696Sshin		(off % multx);
20654696Sshin	padlen += plusy;
207121472Sume	padlen %= multx;	/* keep the pad as short as possible */
20854696Sshin	/* insert padding */
20954696Sshin	inet6_insert_padopt(bp, padlen);
21054696Sshin	cmsg->cmsg_len += padlen;
21154696Sshin	bp += padlen;
21254696Sshin
21354696Sshin	/* keep space to store specified length of data */
21454696Sshin	retval = bp;
21554696Sshin	bp += datalen;
21654696Sshin	cmsg->cmsg_len += datalen;
21754696Sshin
21854696Sshin	/* calculate pad length after the option and insert the padding */
21954696Sshin	off = bp - (u_char *)eh;
22054696Sshin	padlen = ((off + 7) & ~7) - off;
22154696Sshin	inet6_insert_padopt(bp, padlen);
22254696Sshin	bp += padlen;
22354696Sshin	cmsg->cmsg_len += padlen;
22454696Sshin
22554696Sshin	/* update the length field of the ip6 option header */
22654696Sshin	eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1;
22754696Sshin
22854696Sshin	return(retval);
22954696Sshin}
23054696Sshin
23154696Sshin/*
23254696Sshin * This function processes the next Hop-by-Hop option or Destination
23354696Sshin * option in an ancillary data object.  If another option remains to be
23454696Sshin * processed, the return value of the function is 0 and *tptrp points to
23554696Sshin * the 8-bit option type field (which is followed by the 8-bit option
23654696Sshin * data length, followed by the option data).  If no more options remain
23754696Sshin * to be processed, the return value is -1 and *tptrp is NULL.  If an
23854696Sshin * error occurs, the return value is -1 and *tptrp is not NULL.
23954696Sshin * (RFC 2292, 6.3.5)
24054696Sshin */
24154696Sshinint
242199188Sumeinet6_option_next(const struct cmsghdr *cmsg, u_int8_t **tptrp)
24354696Sshin{
24454696Sshin	struct ip6_ext *ip6e;
24554696Sshin	int hdrlen, optlen;
24654696Sshin	u_int8_t *lim;
24754696Sshin
24854696Sshin	if (cmsg->cmsg_level != IPPROTO_IPV6 ||
249269454Smarcel	    (!is_ipv6_hopopts(cmsg->cmsg_type) &&
250269454Smarcel	     !is_ipv6_dstopts(cmsg->cmsg_type)))
25154696Sshin		return(-1);
25254696Sshin
25354696Sshin	/* message length validation */
25454696Sshin	if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext)))
25554696Sshin		return(-1);
25654696Sshin	ip6e = (struct ip6_ext *)CMSG_DATA(cmsg);
25754696Sshin	hdrlen = (ip6e->ip6e_len + 1) << 3;
25854696Sshin	if (cmsg->cmsg_len < CMSG_SPACE(hdrlen))
25954696Sshin		return(-1);
26054696Sshin
26154696Sshin	/*
26254696Sshin	 * If the caller does not specify the starting point,
26354696Sshin	 * simply return the 1st option.
26454696Sshin	 * Otherwise, search the option list for the next option.
26554696Sshin	 */
26654696Sshin	lim = (u_int8_t *)ip6e + hdrlen;
26754696Sshin	if (*tptrp == NULL)
26854696Sshin		*tptrp = (u_int8_t *)(ip6e + 1);
26954696Sshin	else {
27054696Sshin		if ((optlen = ip6optlen(*tptrp, lim)) == 0)
27154696Sshin			return(-1);
27254696Sshin
27354696Sshin		*tptrp = *tptrp + optlen;
27454696Sshin	}
27554696Sshin	if (*tptrp >= lim) {	/* there is no option */
27654696Sshin		*tptrp = NULL;
27754696Sshin		return(-1);
27854696Sshin	}
27954696Sshin	/*
28054696Sshin	 * Finally, checks if the next option is safely stored in the
28154696Sshin	 * cmsg data.
28254696Sshin	 */
28354696Sshin	if (ip6optlen(*tptrp, lim) == 0)
28454696Sshin		return(-1);
28554696Sshin	else
28654696Sshin		return(0);
28754696Sshin}
28854696Sshin
28954696Sshin/*
29054696Sshin * This function is similar to the inet6_option_next() function,
29154696Sshin * except this function lets the caller specify the option type to be
29254696Sshin * searched for, instead of always returning the next option in the
29354696Sshin * ancillary data object.
29454696Sshin * Note: RFC 2292 says the type of tptrp is u_int8_t *, but we think
29554696Sshin *       it's a typo. The variable should be type of u_int8_t **.
29654696Sshin */
29754696Sshinint
298199188Sumeinet6_option_find(const struct cmsghdr *cmsg, u_int8_t **tptrp, int type)
29954696Sshin{
30054696Sshin	struct ip6_ext *ip6e;
30154696Sshin	int hdrlen, optlen;
30254696Sshin	u_int8_t *optp, *lim;
30354696Sshin
30454696Sshin	if (cmsg->cmsg_level != IPPROTO_IPV6 ||
305269454Smarcel	    (!is_ipv6_hopopts(cmsg->cmsg_type) &&
306269454Smarcel	     !is_ipv6_dstopts(cmsg->cmsg_type)))
30754696Sshin		return(-1);
30854696Sshin
30954696Sshin	/* message length validation */
31054696Sshin	if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext)))
31154696Sshin		return(-1);
31254696Sshin	ip6e = (struct ip6_ext *)CMSG_DATA(cmsg);
31354696Sshin	hdrlen = (ip6e->ip6e_len + 1) << 3;
31454696Sshin	if (cmsg->cmsg_len < CMSG_SPACE(hdrlen))
31554696Sshin		return(-1);
31654696Sshin
31754696Sshin	/*
31854696Sshin	 * If the caller does not specify the starting point,
31954696Sshin	 * search from the beginning of the option list.
32054696Sshin	 * Otherwise, search from *the next option* of the specified point.
32154696Sshin	 */
32254696Sshin	lim = (u_int8_t *)ip6e + hdrlen;
32354696Sshin	if (*tptrp == NULL)
32454696Sshin		*tptrp = (u_int8_t *)(ip6e + 1);
32554696Sshin	else {
32654696Sshin		if ((optlen = ip6optlen(*tptrp, lim)) == 0)
32754696Sshin			return(-1);
32854696Sshin
32954696Sshin		*tptrp = *tptrp + optlen;
33054696Sshin	}
33154696Sshin	for (optp = *tptrp; optp < lim; optp += optlen) {
33254696Sshin		if (*optp == type) {
33354696Sshin			*tptrp = optp;
33454696Sshin			return(0);
33554696Sshin		}
33654696Sshin		if ((optlen = ip6optlen(optp, lim)) == 0)
33754696Sshin			return(-1);
33854696Sshin	}
33954696Sshin
34054696Sshin	/* search failed */
34154696Sshin	*tptrp = NULL;
34254696Sshin	return(-1);
34354696Sshin}
34454696Sshin
34554696Sshin/*
34654696Sshin * Calculate the length of a given IPv6 option. Also checks
34754696Sshin * if the option is safely stored in user's buffer according to the
34854696Sshin * calculated length and the limitation of the buffer.
34954696Sshin */
35054696Sshinstatic int
351199188Sumeip6optlen(u_int8_t *opt, u_int8_t *lim)
35254696Sshin{
35354696Sshin	int optlen;
35454696Sshin
35554696Sshin	if (*opt == IP6OPT_PAD1)
35654696Sshin		optlen = 1;
35754696Sshin	else {
35854696Sshin		/* is there enough space to store type and len? */
35954696Sshin		if (opt + 2 > lim)
36054696Sshin			return(0);
36154696Sshin		optlen = *(opt + 1) + 2;
36254696Sshin	}
36354696Sshin	if (opt + optlen <= lim)
36454696Sshin		return(optlen);
36554696Sshin
36654696Sshin	return(0);
36754696Sshin}
36854696Sshin
36954696Sshinstatic void
37054696Sshininet6_insert_padopt(u_char *p, int len)
37154696Sshin{
37254696Sshin	switch(len) {
37354696Sshin	 case 0:
37454696Sshin		 return;
37554696Sshin	 case 1:
37654696Sshin		 p[0] = IP6OPT_PAD1;
37754696Sshin		 return;
37854696Sshin	 default:
37954696Sshin		 p[0] = IP6OPT_PADN;
38054696Sshin		 p[1] = len - 2;
38154696Sshin		 memset(&p[2], 0, len - 2);
38254696Sshin		 return;
38354696Sshin	}
38454696Sshin}
385121472Sume
386121472Sume/*
387148160Sume * The following functions are defined in RFC3542, which is a successor
388148160Sume * of RFC2292.
389121472Sume */
390121472Sume
391121472Sumeint
392121472Sumeinet6_opt_init(void *extbuf, socklen_t extlen)
393121472Sume{
394121472Sume	struct ip6_ext *ext = (struct ip6_ext *)extbuf;
395121472Sume
396121472Sume	if (extlen < 0 || (extlen % 8))
397121472Sume		return(-1);
398121472Sume
399121472Sume	if (ext) {
400121472Sume		if (extlen == 0)
401121472Sume			return(-1);
402121472Sume		ext->ip6e_len = (extlen >> 3) - 1;
403121472Sume	}
404121472Sume
405121472Sume	return(2);		/* sizeof the next and the length fields */
406121472Sume}
407121472Sume
408121472Sumeint
409121472Sumeinet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
410121472Sume		 socklen_t len, u_int8_t align, void **databufp)
411121472Sume{
412121472Sume	int currentlen = offset, padlen = 0;
413121472Sume
414121472Sume	/*
415121472Sume	 * The option type must have a value from 2 to 255, inclusive.
416121472Sume	 * (0 and 1 are reserved for the Pad1 and PadN options, respectively.)
417121472Sume	 */
418122682Sume	if (type < 2)
419121472Sume		return(-1);
420121472Sume
421121472Sume	/*
422121472Sume	 * The option data length must have a value between 0 and 255,
423121472Sume	 * inclusive, and is the length of the option data that follows.
424121472Sume	 */
425121472Sume	if (len < 0 || len > 255)
426121472Sume		return(-1);
427121472Sume
428121472Sume	/*
429121472Sume	 * The align parameter must have a value of 1, 2, 4, or 8.
430121472Sume	 * The align value can not exceed the value of len.
431121472Sume	 */
432121472Sume	if (align != 1 && align != 2 && align != 4 && align != 8)
433121472Sume		return(-1);
434121472Sume	if (align > len)
435121472Sume		return(-1);
436121472Sume
437121472Sume	/* Calculate the padding length. */
438121472Sume	currentlen += 2 + len;	/* 2 means "type + len" */
439121472Sume	if (currentlen % align)
440121472Sume		padlen = align - (currentlen % align);
441121472Sume
442121472Sume	/* The option must fit in the extension header buffer. */
443121472Sume	currentlen += padlen;
444121472Sume	if (extlen &&		/* XXX: right? */
445121472Sume	    currentlen > extlen)
446121472Sume		return(-1);
447121472Sume
448121472Sume	if (extbuf) {
449121472Sume		u_int8_t *optp = (u_int8_t *)extbuf + offset;
450121472Sume
451121472Sume		if (padlen == 1) {
452121472Sume			/* insert a Pad1 option */
453121472Sume			*optp = IP6OPT_PAD1;
454121472Sume			optp++;
455121472Sume		}
456121472Sume		else if (padlen > 0) {
457121472Sume			/* insert a PadN option for alignment */
458121472Sume			*optp++ = IP6OPT_PADN;
459121472Sume			*optp++ = padlen - 2;
460121472Sume			memset(optp, 0, padlen - 2);
461121472Sume			optp += (padlen - 2);
462121472Sume		}
463121472Sume
464121472Sume		*optp++ = type;
465121472Sume		*optp++ = len;
466121472Sume
467121472Sume		*databufp = optp;
468121472Sume	}
469121472Sume
470121472Sume	return(currentlen);
471121472Sume}
472121472Sume
473121472Sumeint
474121472Sumeinet6_opt_finish(void *extbuf, socklen_t extlen, int offset)
475121472Sume{
476241844Seadler	int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;
477121472Sume
478121472Sume	if (extbuf) {
479121472Sume		u_int8_t *padp;
480121472Sume		int padlen = updatelen - offset;
481121472Sume
482121472Sume		if (updatelen > extlen)
483121472Sume			return(-1);
484121472Sume
485121472Sume		padp = (u_int8_t *)extbuf + offset;
486121472Sume		if (padlen == 1)
487121472Sume			*padp = IP6OPT_PAD1;
488121472Sume		else if (padlen > 0) {
489121472Sume			*padp++ = IP6OPT_PADN;
490121472Sume			*padp++ = (padlen - 2);
491121472Sume			memset(padp, 0, padlen - 2);
492121472Sume		}
493121472Sume	}
494121472Sume
495121472Sume	return(updatelen);
496121472Sume}
497121472Sume
498121472Sumeint
499121472Sumeinet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen)
500121472Sume{
501121472Sume
502121472Sume	memcpy((u_int8_t *)databuf + offset, val, vallen);
503121472Sume	return(offset + vallen);
504121472Sume}
505121472Sume
506121472Sumeint
507121472Sumeinet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep,
508121496Sume	       socklen_t *lenp, void **databufp)
509121472Sume{
510121472Sume	u_int8_t *optp, *lim;
511121472Sume	int optlen;
512121472Sume
513121472Sume	/* Validate extlen. XXX: is the variable really necessary?? */
514121472Sume	if (extlen == 0 || (extlen % 8))
515121472Sume		return(-1);
516121472Sume	lim = (u_int8_t *)extbuf + extlen;
517121472Sume
518121472Sume	/*
519121472Sume	 * If this is the first time this function called for this options
520121472Sume	 * header, simply return the 1st option.
521121472Sume	 * Otherwise, search the option list for the next option.
522121472Sume	 */
523121472Sume	if (offset == 0) {
524121472Sume		optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
525121472Sume	}
526121472Sume	else
527121472Sume		optp = (u_int8_t *)extbuf + offset;
528121472Sume
529121472Sume	/* Find the next option skipping any padding options. */
530121472Sume	while(optp < lim) {
531121472Sume		switch(*optp) {
532121472Sume		case IP6OPT_PAD1:
533121472Sume			optp++;
534121472Sume			break;
535121472Sume		case IP6OPT_PADN:
536121472Sume			if ((optlen = ip6optlen(optp, lim)) == 0)
537121472Sume				goto optend;
538121472Sume			optp += optlen;
539121472Sume			break;
540121472Sume		default:	/* found */
541121472Sume			if ((optlen = ip6optlen(optp, lim)) == 0)
542121472Sume				goto optend;
543121472Sume			*typep = *optp;
544121472Sume			*lenp = optlen - 2;
545121472Sume			*databufp = optp + 2;
546121472Sume			return(optp + optlen - (u_int8_t *)extbuf);
547121472Sume		}
548121472Sume	}
549121472Sume
550121472Sume  optend:
551121472Sume	*databufp = NULL; /* for safety */
552121472Sume	return(-1);
553121472Sume}
554121472Sume
555121472Sumeint
556121472Sumeinet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
557121472Sume	       socklen_t *lenp, void **databufp)
558121472Sume{
559121472Sume	u_int8_t *optp, *lim;
560121472Sume	int optlen;
561121472Sume
562121472Sume	/* Validate extlen. XXX: is the variable really necessary?? */
563121472Sume	if (extlen == 0 || (extlen % 8))
564121472Sume		return(-1);
565121472Sume	lim = (u_int8_t *)extbuf + extlen;
566121472Sume
567121472Sume	/*
568121472Sume	 * If this is the first time this function called for this options
569121472Sume	 * header, simply return the 1st option.
570121472Sume	 * Otherwise, search the option list for the next option.
571121472Sume	 */
572121472Sume	if (offset == 0) {
573121472Sume		optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
574121472Sume	}
575121472Sume	else
576121472Sume		optp = (u_int8_t *)extbuf + offset;
577121472Sume
578121472Sume	/* Find the specified option */
579121472Sume	while(optp < lim) {
580121472Sume		if ((optlen = ip6optlen(optp, lim)) == 0)
581121472Sume			goto optend;
582121472Sume
583121472Sume		if (*optp == type) { /* found */
584121472Sume			*lenp = optlen - 2;
585121472Sume			*databufp = optp + 2;
586121472Sume			return(optp + optlen - (u_int8_t *)extbuf);
587121472Sume		}
588121472Sume
589121472Sume		optp += optlen;
590121472Sume	}
591121472Sume
592121472Sume  optend:
593121472Sume	*databufp = NULL; /* for safety */
594121472Sume	return(-1);
595121472Sume}
596121472Sume
597121472Sumeint
598121472Sumeinet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen)
599121472Sume{
600121472Sume
601121472Sume	/* we can't assume alignment here */
602121472Sume	memcpy(val, (u_int8_t *)databuf + offset, vallen);
603121472Sume
604121472Sume	return(offset + vallen);
605121472Sume}
606