ip6opt.c revision 121472
156043Syokota/*	$KAME: ip6opt.c,v 1.13 2003/06/06 10:08:20 suz Exp $	*/
256043Syokota
356043Syokota/*
456043Syokota * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
556043Syokota * All rights reserved.
656043Syokota *
756043Syokota * Redistribution and use in source and binary forms, with or without
856043Syokota * modification, are permitted provided that the following conditions
956043Syokota * are met:
1056043Syokota * 1. Redistributions of source code must retain the above copyright
1156043Syokota *    notice, this list of conditions and the following disclaimer.
1256043Syokota * 2. Redistributions in binary form must reproduce the above copyright
1356043Syokota *    notice, this list of conditions and the following disclaimer in the
1456043Syokota *    documentation and/or other materials provided with the distribution.
1556043Syokota * 3. Neither the name of the project nor the names of its contributors
1656043Syokota *    may be used to endorse or promote products derived from this software
1756043Syokota *    without specific prior written permission.
1856043Syokota *
1956043Syokota * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2056043Syokota * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2156043Syokota * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2256043Syokota * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2356043Syokota * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2456043Syokota * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2556043Syokota * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2656043Syokota * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2756043Syokota * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28119420Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29119420Sobrien * SUCH DAMAGE.
30119420Sobrien */
3156043Syokota
3256043Syokota#include <sys/cdefs.h>
3356043Syokota__FBSDID("$FreeBSD: head/lib/libc/net/ip6opt.c 121472 2003-10-24 18:26:30Z ume $");
3456043Syokota
35164033Srwatson#include <sys/param.h>
36181905Sed#include <sys/types.h>
3756043Syokota#include <sys/socket.h>
38199881Sed
3956043Syokota#include <netinet/in.h>
40268366Sray#include <netinet/ip6.h>
4166834Sphk
4266860Sphk#include <string.h>
4356043Syokota#include <stdio.h>
4456043Syokota
4556043Syokotastatic int ip6optlen(u_int8_t *opt, u_int8_t *lim);
4656043Syokotastatic void inet6_insert_padopt(u_char *p, int len);
4756043Syokota
4856043Syokota/*
4956043Syokota * This function returns the number of bytes required to hold an option
5056043Syokota * when it is stored as ancillary data, including the cmsghdr structure
5156043Syokota * at the beginning, and any padding at the end (to make its size a
5256043Syokota * multiple of 8 bytes).  The argument is the size of the structure
53181905Sed * defining the option, which must include any pad bytes at the
54181905Sed * beginning (the value y in the alignment term "xn + y"), the type
5556043Syokota * byte, the length byte, and the option data.
5656043Syokota */
5756043Syokotaint
5856043Syokotainet6_option_space(nbytes)
5956043Syokota	int nbytes;
60181905Sed{
6156043Syokota	nbytes += 2;	/* we need space for nxt-hdr and length fields */
6256043Syokota	return(CMSG_SPACE((nbytes + 7) & ~7));
6356043Syokota}
6456043Syokota
6556043Syokota/*
6656043Syokota * This function is called once per ancillary data object that will
6756043Syokota * contain either Hop-by-Hop or Destination options.  It returns 0 on
6856043Syokota * success or -1 on an error.
6956043Syokota */
7056043Syokotaint
7156043Syokotainet6_option_init(bp, cmsgp, type)
7256043Syokota	void *bp;
7356043Syokota	struct cmsghdr **cmsgp;
7456043Syokota	int type;
7556043Syokota{
7656043Syokota	struct cmsghdr *ch = (struct cmsghdr *)bp;
7756043Syokota
7856043Syokota	/* argument validation */
7956043Syokota	if (type != IPV6_HOPOPTS && type != IPV6_DSTOPTS)
8056043Syokota		return(-1);
8156043Syokota
8256043Syokota	ch->cmsg_level = IPPROTO_IPV6;
8356043Syokota	ch->cmsg_type = type;
8456043Syokota	ch->cmsg_len = CMSG_LEN(0);
8556043Syokota
8656043Syokota	*cmsgp = ch;
8756043Syokota	return(0);
8856043Syokota}
8956043Syokota
9056043Syokota/*
9156043Syokota * This function appends a Hop-by-Hop option or a Destination option
9256043Syokota * into an ancillary data object that has been initialized by
9356043Syokota * inet6_option_init().  This function returns 0 if it succeeds or -1 on
9456043Syokota * an error.
9556043Syokota * multx is the value x in the alignment term "xn + y" described
9656043Syokota * earlier.  It must have a value of 1, 2, 4, or 8.
9756043Syokota * plusy is the value y in the alignment term "xn + y" described
9856043Syokota * earlier.  It must have a value between 0 and 7, inclusive.
9956043Syokota */
10056043Syokotaint
10156043Syokotainet6_option_append(cmsg, typep, multx, plusy)
10256043Syokota	struct cmsghdr *cmsg;
10356043Syokota	const u_int8_t *typep;
10465129Syokota	int multx;
10565129Syokota	int plusy;
10665129Syokota{
10756043Syokota	int padlen, optlen, off;
10865129Syokota	u_char *bp = (u_char *)cmsg + cmsg->cmsg_len;
10965129Syokota	struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg);
11056043Syokota
11156043Syokota	/* argument validation */
11256043Syokota	if (multx != 1 && multx != 2 && multx != 4 && multx != 8)
11356043Syokota		return(-1);
11456043Syokota	if (plusy < 0 || plusy > 7)
11556043Syokota		return(-1);
11656043Syokota	if (typep[0] > 255)
11756043Syokota		return(-1);
11856043Syokota
11956043Syokota	/*
12056043Syokota	 * If this is the first option, allocate space for the
12156043Syokota	 * first 2 bytes(for next header and length fields) of
12256043Syokota	 * the option header.
12356043Syokota	 */
12456043Syokota	if (bp == (u_char *)eh) {
12556043Syokota		bp += 2;
12656043Syokota		cmsg->cmsg_len += 2;
12756043Syokota	}
12856043Syokota
12956043Syokota	/* calculate pad length before the option. */
13056043Syokota	off = bp - (u_char *)eh;
131153072Sru	padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) -
13256043Syokota		(off % multx);
13356043Syokota	padlen += plusy;
13456043Syokota	padlen %= multx;	/* keep the pad as short as possible */
13556043Syokota	/* insert padding */
13656043Syokota	inet6_insert_padopt(bp, padlen);
13756043Syokota	cmsg->cmsg_len += padlen;
13856043Syokota	bp += padlen;
13956043Syokota
14056043Syokota	/* copy the option */
14156043Syokota	if (typep[0] == IP6OPT_PAD1)
142181905Sed		optlen = 1;
14356043Syokota	else
14456043Syokota		optlen = typep[1] + 2;
145181905Sed	memcpy(bp, typep, optlen);
146181905Sed	bp += optlen;
14756043Syokota	cmsg->cmsg_len += optlen;
14856043Syokota
149181905Sed	/* calculate pad length after the option and insert the padding */
150181905Sed	off = bp - (u_char *)eh;
151181905Sed	padlen = ((off + 7) & ~7) - off;
152181905Sed	inet6_insert_padopt(bp, padlen);
153184771Sed	bp += padlen;
154181905Sed	cmsg->cmsg_len += padlen;
155136505Sphk
156181905Sed	/* update the length field of the ip6 option header */
15756043Syokota	eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1;
15856043Syokota
159181905Sed	return(0);
160181905Sed}
161181905Sed
162181905Sed/*
163181905Sed * This function appends a Hop-by-Hop option or a Destination option
164181905Sed * into an ancillary data object that has been initialized by
165181905Sed * inet6_option_init().  This function returns a pointer to the 8-bit
166181905Sed * option type field that starts the option on success, or NULL on an
167181905Sed * error.
168181905Sed * The difference between this function and inet6_option_append() is
169268366Sray * that the latter copies the contents of a previously built option into
170268366Sray * the ancillary data object while the current function returns a
171193018Sed * pointer to the space in the data object where the option's TLV must
172181905Sed * then be built by the caller.
173181905Sed *
174181905Sed */
175177253Srwatsonu_int8_t *
17656043Syokotainet6_option_alloc(cmsg, datalen, multx, plusy)
17756043Syokota	struct cmsghdr *cmsg;
17856043Syokota	int datalen;
17956043Syokota	int multx;
18056043Syokota	int plusy;
18156043Syokota{
18256043Syokota	int padlen, off;
18356043Syokota	u_int8_t *bp = (u_char *)cmsg + cmsg->cmsg_len;
18456043Syokota	u_int8_t *retval;
18556043Syokota	struct ip6_ext *eh = (struct ip6_ext *)CMSG_DATA(cmsg);
18656043Syokota
18756043Syokota	/* argument validation */
18856043Syokota	if (multx != 1 && multx != 2 && multx != 4 && multx != 8)
18956043Syokota		return(NULL);
19056043Syokota	if (plusy < 0 || plusy > 7)
19156043Syokota		return(NULL);
19256043Syokota
193182109Sed	/*
19456043Syokota	 * If this is the first option, allocate space for the
195182109Sed	 * first 2 bytes(for next header and length fields) of
196182109Sed	 * the option header.
19756043Syokota	 */
19856043Syokota	if (bp == (u_char *)eh) {
19956043Syokota		bp += 2;
20056043Syokota		cmsg->cmsg_len += 2;
20156043Syokota	}
20256043Syokota
20356043Syokota	/* calculate pad length before the option. */
20456043Syokota	off = bp - (u_char *)eh;
20556043Syokota	padlen = (((off % multx) + (multx - 1)) & ~(multx - 1)) -
20656043Syokota		(off % multx);
20756043Syokota	padlen += plusy;
20856043Syokota	padlen %= multx;	/* keep the pad as short as possible */
20956043Syokota	/* insert padding */
21056043Syokota	inet6_insert_padopt(bp, padlen);
21156043Syokota	cmsg->cmsg_len += padlen;
21256043Syokota	bp += padlen;
21356043Syokota
214182109Sed	/* keep space to store specified length of data */
21556043Syokota	retval = bp;
21656043Syokota	bp += datalen;
21756043Syokota	cmsg->cmsg_len += datalen;
21856043Syokota
21956043Syokota	/* calculate pad length after the option and insert the padding */
22056043Syokota	off = bp - (u_char *)eh;
22156043Syokota	padlen = ((off + 7) & ~7) - off;
222182109Sed	inet6_insert_padopt(bp, padlen);
223182109Sed	bp += padlen;
224182109Sed	cmsg->cmsg_len += padlen;
22556043Syokota
22656043Syokota	/* update the length field of the ip6 option header */
22756043Syokota	eh->ip6e_len = ((bp - (u_char *)eh) >> 3) - 1;
22856043Syokota
22956043Syokota	return(retval);
23056043Syokota}
23156043Syokota
23256043Syokota/*
23356043Syokota * This function processes the next Hop-by-Hop option or Destination
23456043Syokota * option in an ancillary data object.  If another option remains to be
23556043Syokota * processed, the return value of the function is 0 and *tptrp points to
236181905Sed * the 8-bit option type field (which is followed by the 8-bit option
23756043Syokota * data length, followed by the option data).  If no more options remain
23856043Syokota * to be processed, the return value is -1 and *tptrp is NULL.  If an
23956043Syokota * error occurs, the return value is -1 and *tptrp is not NULL.
24056043Syokota * (RFC 2292, 6.3.5)
24156043Syokota */
24256043Syokotaint
24356043Syokotainet6_option_next(cmsg, tptrp)
24456043Syokota	const struct cmsghdr *cmsg;
245181905Sed	u_int8_t **tptrp;
24656043Syokota{
247181905Sed	struct ip6_ext *ip6e;
24856043Syokota	int hdrlen, optlen;
249182109Sed	u_int8_t *lim;
250182109Sed
25156043Syokota	if (cmsg->cmsg_level != IPPROTO_IPV6 ||
25256043Syokota	    (cmsg->cmsg_type != IPV6_HOPOPTS &&
25356043Syokota	     cmsg->cmsg_type != IPV6_DSTOPTS))
254		return(-1);
255
256	/* message length validation */
257	if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext)))
258		return(-1);
259	ip6e = (struct ip6_ext *)CMSG_DATA(cmsg);
260	hdrlen = (ip6e->ip6e_len + 1) << 3;
261	if (cmsg->cmsg_len < CMSG_SPACE(hdrlen))
262		return(-1);
263
264	/*
265	 * If the caller does not specify the starting point,
266	 * simply return the 1st option.
267	 * Otherwise, search the option list for the next option.
268	 */
269	lim = (u_int8_t *)ip6e + hdrlen;
270	if (*tptrp == NULL)
271		*tptrp = (u_int8_t *)(ip6e + 1);
272	else {
273		if ((optlen = ip6optlen(*tptrp, lim)) == 0)
274			return(-1);
275
276		*tptrp = *tptrp + optlen;
277	}
278	if (*tptrp >= lim) {	/* there is no option */
279		*tptrp = NULL;
280		return(-1);
281	}
282	/*
283	 * Finally, checks if the next option is safely stored in the
284	 * cmsg data.
285	 */
286	if (ip6optlen(*tptrp, lim) == 0)
287		return(-1);
288	else
289		return(0);
290}
291
292/*
293 * This function is similar to the inet6_option_next() function,
294 * except this function lets the caller specify the option type to be
295 * searched for, instead of always returning the next option in the
296 * ancillary data object.
297 * Note: RFC 2292 says the type of tptrp is u_int8_t *, but we think
298 *       it's a typo. The variable should be type of u_int8_t **.
299 */
300int
301inet6_option_find(cmsg, tptrp, type)
302	const struct cmsghdr *cmsg;
303	u_int8_t **tptrp;
304	int type;
305{
306	struct ip6_ext *ip6e;
307	int hdrlen, optlen;
308	u_int8_t *optp, *lim;
309
310	if (cmsg->cmsg_level != IPPROTO_IPV6 ||
311	    (cmsg->cmsg_type != IPV6_HOPOPTS &&
312	     cmsg->cmsg_type != IPV6_DSTOPTS))
313		return(-1);
314
315	/* message length validation */
316	if (cmsg->cmsg_len < CMSG_SPACE(sizeof(struct ip6_ext)))
317		return(-1);
318	ip6e = (struct ip6_ext *)CMSG_DATA(cmsg);
319	hdrlen = (ip6e->ip6e_len + 1) << 3;
320	if (cmsg->cmsg_len < CMSG_SPACE(hdrlen))
321		return(-1);
322
323	/*
324	 * If the caller does not specify the starting point,
325	 * search from the beginning of the option list.
326	 * Otherwise, search from *the next option* of the specified point.
327	 */
328	lim = (u_int8_t *)ip6e + hdrlen;
329	if (*tptrp == NULL)
330		*tptrp = (u_int8_t *)(ip6e + 1);
331	else {
332		if ((optlen = ip6optlen(*tptrp, lim)) == 0)
333			return(-1);
334
335		*tptrp = *tptrp + optlen;
336	}
337	for (optp = *tptrp; optp < lim; optp += optlen) {
338		if (*optp == type) {
339			*tptrp = optp;
340			return(0);
341		}
342		if ((optlen = ip6optlen(optp, lim)) == 0)
343			return(-1);
344	}
345
346	/* search failed */
347	*tptrp = NULL;
348	return(-1);
349}
350
351/*
352 * Calculate the length of a given IPv6 option. Also checks
353 * if the option is safely stored in user's buffer according to the
354 * calculated length and the limitation of the buffer.
355 */
356static int
357ip6optlen(opt, lim)
358	u_int8_t *opt, *lim;
359{
360	int optlen;
361
362	if (*opt == IP6OPT_PAD1)
363		optlen = 1;
364	else {
365		/* is there enough space to store type and len? */
366		if (opt + 2 > lim)
367			return(0);
368		optlen = *(opt + 1) + 2;
369	}
370	if (opt + optlen <= lim)
371		return(optlen);
372
373	return(0);
374}
375
376static void
377inet6_insert_padopt(u_char *p, int len)
378{
379	switch(len) {
380	 case 0:
381		 return;
382	 case 1:
383		 p[0] = IP6OPT_PAD1;
384		 return;
385	 default:
386		 p[0] = IP6OPT_PADN;
387		 p[1] = len - 2;
388		 memset(&p[2], 0, len - 2);
389		 return;
390	}
391}
392
393/*
394 * The following functions are defined in a successor of RFC2292, aka
395 * rfc2292bis.
396 */
397
398int
399inet6_opt_init(void *extbuf, socklen_t extlen)
400{
401	struct ip6_ext *ext = (struct ip6_ext *)extbuf;
402
403	if (extlen < 0 || (extlen % 8))
404		return(-1);
405
406	if (ext) {
407		if (extlen == 0)
408			return(-1);
409		ext->ip6e_len = (extlen >> 3) - 1;
410	}
411
412	return(2);		/* sizeof the next and the length fields */
413}
414
415int
416inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
417		 socklen_t len, u_int8_t align, void **databufp)
418{
419	int currentlen = offset, padlen = 0;
420
421	/*
422	 * The option type must have a value from 2 to 255, inclusive.
423	 * (0 and 1 are reserved for the Pad1 and PadN options, respectively.)
424	 */
425	if (type < 2 || type > 255)
426		return(-1);
427
428	/*
429	 * The option data length must have a value between 0 and 255,
430	 * inclusive, and is the length of the option data that follows.
431	 */
432	if (len < 0 || len > 255)
433		return(-1);
434
435	/*
436	 * The align parameter must have a value of 1, 2, 4, or 8.
437	 * The align value can not exceed the value of len.
438	 */
439	if (align != 1 && align != 2 && align != 4 && align != 8)
440		return(-1);
441	if (align > len)
442		return(-1);
443
444	/* Calculate the padding length. */
445	currentlen += 2 + len;	/* 2 means "type + len" */
446	if (currentlen % align)
447		padlen = align - (currentlen % align);
448
449	/* The option must fit in the extension header buffer. */
450	currentlen += padlen;
451	if (extlen &&		/* XXX: right? */
452	    currentlen > extlen)
453		return(-1);
454
455	if (extbuf) {
456		u_int8_t *optp = (u_int8_t *)extbuf + offset;
457
458		if (padlen == 1) {
459			/* insert a Pad1 option */
460			*optp = IP6OPT_PAD1;
461			optp++;
462		}
463		else if (padlen > 0) {
464			/* insert a PadN option for alignment */
465			*optp++ = IP6OPT_PADN;
466			*optp++ = padlen - 2;
467			memset(optp, 0, padlen - 2);
468			optp += (padlen - 2);
469		}
470
471		*optp++ = type;
472		*optp++ = len;
473
474		*databufp = optp;
475	}
476
477	return(currentlen);
478}
479
480int
481inet6_opt_finish(void *extbuf, socklen_t extlen, int offset)
482{
483	int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;;
484
485	if (extbuf) {
486		u_int8_t *padp;
487		int padlen = updatelen - offset;
488
489		if (updatelen > extlen)
490			return(-1);
491
492		padp = (u_int8_t *)extbuf + offset;
493		if (padlen == 1)
494			*padp = IP6OPT_PAD1;
495		else if (padlen > 0) {
496			*padp++ = IP6OPT_PADN;
497			*padp++ = (padlen - 2);
498			memset(padp, 0, padlen - 2);
499		}
500	}
501
502	return(updatelen);
503}
504
505int
506inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen)
507{
508
509	memcpy((u_int8_t *)databuf + offset, val, vallen);
510	return(offset + vallen);
511}
512
513int
514inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep,
515	       size_t *lenp, void **databufp)
516{
517	u_int8_t *optp, *lim;
518	int optlen;
519
520	/* Validate extlen. XXX: is the variable really necessary?? */
521	if (extlen == 0 || (extlen % 8))
522		return(-1);
523	lim = (u_int8_t *)extbuf + extlen;
524
525	/*
526	 * If this is the first time this function called for this options
527	 * header, simply return the 1st option.
528	 * Otherwise, search the option list for the next option.
529	 */
530	if (offset == 0) {
531		optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
532	}
533	else
534		optp = (u_int8_t *)extbuf + offset;
535
536	/* Find the next option skipping any padding options. */
537	while(optp < lim) {
538		switch(*optp) {
539		case IP6OPT_PAD1:
540			optp++;
541			break;
542		case IP6OPT_PADN:
543			if ((optlen = ip6optlen(optp, lim)) == 0)
544				goto optend;
545			optp += optlen;
546			break;
547		default:	/* found */
548			if ((optlen = ip6optlen(optp, lim)) == 0)
549				goto optend;
550			*typep = *optp;
551			*lenp = optlen - 2;
552			*databufp = optp + 2;
553			return(optp + optlen - (u_int8_t *)extbuf);
554		}
555	}
556
557  optend:
558	*databufp = NULL; /* for safety */
559	return(-1);
560}
561
562int
563inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
564	       socklen_t *lenp, void **databufp)
565{
566	u_int8_t *optp, *lim;
567	int optlen;
568
569	/* Validate extlen. XXX: is the variable really necessary?? */
570	if (extlen == 0 || (extlen % 8))
571		return(-1);
572	lim = (u_int8_t *)extbuf + extlen;
573
574	/*
575	 * If this is the first time this function called for this options
576	 * header, simply return the 1st option.
577	 * Otherwise, search the option list for the next option.
578	 */
579	if (offset == 0) {
580		optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
581	}
582	else
583		optp = (u_int8_t *)extbuf + offset;
584
585	/* Find the specified option */
586	while(optp < lim) {
587		if ((optlen = ip6optlen(optp, lim)) == 0)
588			goto optend;
589
590		if (*optp == type) { /* found */
591			*lenp = optlen - 2;
592			*databufp = optp + 2;
593			return(optp + optlen - (u_int8_t *)extbuf);
594		}
595
596		optp += optlen;
597	}
598
599  optend:
600	*databufp = NULL; /* for safety */
601	return(-1);
602}
603
604int
605inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen)
606{
607
608	/* we can't assume alignment here */
609	memcpy(val, (u_int8_t *)databuf + offset, vallen);
610
611	return(offset + vallen);
612}
613