xref: /openbsd-src/lib/libc/net/ip6opt.c (revision ab8e345121805a995660ff2435c0ac6c1977dd80)
1*ab8e3451Sderaadt /*	$OpenBSD: ip6opt.c,v 1.10 2020/01/22 07:52:37 deraadt Exp $	*/
26b532452Sitojun /*	$KAME: ip6opt.c,v 1.18 2005/06/15 07:11:35 keiichi Exp $	*/
34228fd5fSitojun 
44228fd5fSitojun /*
54228fd5fSitojun  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
64228fd5fSitojun  * All rights reserved.
74228fd5fSitojun  *
84228fd5fSitojun  * Redistribution and use in source and binary forms, with or without
94228fd5fSitojun  * modification, are permitted provided that the following conditions
104228fd5fSitojun  * are met:
114228fd5fSitojun  * 1. Redistributions of source code must retain the above copyright
124228fd5fSitojun  *    notice, this list of conditions and the following disclaimer.
134228fd5fSitojun  * 2. Redistributions in binary form must reproduce the above copyright
144228fd5fSitojun  *    notice, this list of conditions and the following disclaimer in the
154228fd5fSitojun  *    documentation and/or other materials provided with the distribution.
164228fd5fSitojun  * 3. Neither the name of the project nor the names of its contributors
174228fd5fSitojun  *    may be used to endorse or promote products derived from this software
184228fd5fSitojun  *    without specific prior written permission.
194228fd5fSitojun  *
204228fd5fSitojun  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
214228fd5fSitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
224228fd5fSitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
234228fd5fSitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
244228fd5fSitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
254228fd5fSitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
264228fd5fSitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
274228fd5fSitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
284228fd5fSitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
294228fd5fSitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
304228fd5fSitojun  * SUCH DAMAGE.
314228fd5fSitojun  */
324228fd5fSitojun 
334228fd5fSitojun #include <sys/types.h>
344228fd5fSitojun #include <sys/socket.h>
354228fd5fSitojun 
364228fd5fSitojun #include <netinet/in.h>
374228fd5fSitojun #include <netinet/ip6.h>
384228fd5fSitojun 
394228fd5fSitojun #include <string.h>
404228fd5fSitojun #include <stdio.h>
414228fd5fSitojun 
424228fd5fSitojun static int ip6optlen(u_int8_t *opt, u_int8_t *lim);
434228fd5fSitojun 
444228fd5fSitojun /*
454228fd5fSitojun  * Calculate the length of a given IPv6 option. Also checks
464228fd5fSitojun  * if the option is safely stored in user's buffer according to the
474228fd5fSitojun  * calculated length and the limitation of the buffer.
484228fd5fSitojun  */
494228fd5fSitojun static int
ip6optlen(u_int8_t * opt,u_int8_t * lim)50db5b349cSotto ip6optlen(u_int8_t *opt, u_int8_t *lim)
514228fd5fSitojun {
524228fd5fSitojun 	int optlen;
534228fd5fSitojun 
544228fd5fSitojun 	if (*opt == IP6OPT_PAD1)
554228fd5fSitojun 		optlen = 1;
564228fd5fSitojun 	else {
574228fd5fSitojun 		/* is there enough space to store type and len? */
584228fd5fSitojun 		if (opt + 2 > lim)
594228fd5fSitojun 			return (0);
604228fd5fSitojun 		optlen = *(opt + 1) + 2;
614228fd5fSitojun 	}
624228fd5fSitojun 	if (opt + optlen <= lim)
634228fd5fSitojun 		return (optlen);
644228fd5fSitojun 
654228fd5fSitojun 	return (0);
664228fd5fSitojun }
674228fd5fSitojun 
686b532452Sitojun /*
696b532452Sitojun  * The following functions are defined in RFC3542, which is a successor
706b532452Sitojun  * of RFC2292.
716b532452Sitojun  */
726b532452Sitojun 
736b532452Sitojun int
inet6_opt_init(void * extbuf,socklen_t extlen)746b532452Sitojun inet6_opt_init(void *extbuf, socklen_t extlen)
756b532452Sitojun {
766b532452Sitojun 	struct ip6_ext *ext = (struct ip6_ext *)extbuf;
776b532452Sitojun 
786b532452Sitojun 	if (ext) {
795c02bc5fSmpi 		if (extlen <= 0 || (extlen % 8))
806b532452Sitojun 			return (-1);
816b532452Sitojun 		ext->ip6e_len = (extlen >> 3) - 1;
826b532452Sitojun 	}
836b532452Sitojun 
846b532452Sitojun 	return (2);		/* sizeof the next and the length fields */
856b532452Sitojun }
866b532452Sitojun 
876b532452Sitojun int
inet6_opt_append(void * extbuf,socklen_t extlen,int offset,u_int8_t type,socklen_t len,u_int8_t align,void ** databufp)886b532452Sitojun inet6_opt_append(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
896b532452Sitojun 		 socklen_t len, u_int8_t align, void **databufp)
906b532452Sitojun {
916b532452Sitojun 	int currentlen = offset, padlen = 0;
926b532452Sitojun 
936b532452Sitojun 	/*
946b532452Sitojun 	 * The option type must have a value from 2 to 255, inclusive.
956b532452Sitojun 	 * (0 and 1 are reserved for the Pad1 and PadN options, respectively.)
966b532452Sitojun 	 */
976b532452Sitojun #if 0 /* always false */
986b532452Sitojun 	if (type < 2 || type > 255)
996b532452Sitojun #else
1006b532452Sitojun 	if (type < 2)
1016b532452Sitojun #endif
1026b532452Sitojun 		return (-1);
1036b532452Sitojun 
1046b532452Sitojun 	/*
1056b532452Sitojun 	 * The option data length must have a value between 0 and 255,
1066b532452Sitojun 	 * inclusive, and is the length of the option data that follows.
1076b532452Sitojun 	 */
108ad502734Smillert 	if (len > 255)
1096b532452Sitojun 		return (-1);
1106b532452Sitojun 
1116b532452Sitojun 	/*
1126b532452Sitojun 	 * The align parameter must have a value of 1, 2, 4, or 8.
1136b532452Sitojun 	 * The align value can not exceed the value of len.
1146b532452Sitojun 	 */
1156b532452Sitojun 	if (align != 1 && align != 2 && align != 4 && align != 8)
1166b532452Sitojun 		return (-1);
1176b532452Sitojun 	if (align > len)
1186b532452Sitojun 		return (-1);
1196b532452Sitojun 
1206b532452Sitojun 	/* Calculate the padding length. */
1216b532452Sitojun 	currentlen += 2 + len;	/* 2 means "type + len" */
1226b532452Sitojun 	if (currentlen % align)
1236b532452Sitojun 		padlen = align - (currentlen % align);
1246b532452Sitojun 
1256b532452Sitojun 	/* The option must fit in the extension header buffer. */
1266b532452Sitojun 	currentlen += padlen;
1276b532452Sitojun 	if (extlen &&		/* XXX: right? */
1286b532452Sitojun 	    currentlen > extlen)
1296b532452Sitojun 		return (-1);
1306b532452Sitojun 
1316b532452Sitojun 	if (extbuf) {
1326b532452Sitojun 		u_int8_t *optp = (u_int8_t *)extbuf + offset;
1336b532452Sitojun 
1346b532452Sitojun 		if (padlen == 1) {
1356b532452Sitojun 			/* insert a Pad1 option */
1366b532452Sitojun 			*optp = IP6OPT_PAD1;
1376b532452Sitojun 			optp++;
1386b532452Sitojun 		} else if (padlen > 0) {
1396b532452Sitojun 			/* insert a PadN option for alignment */
1406b532452Sitojun 			*optp++ = IP6OPT_PADN;
1416b532452Sitojun 			*optp++ = padlen - 2;
1426b532452Sitojun 			memset(optp, 0, padlen - 2);
1436b532452Sitojun 			optp += (padlen - 2);
1446b532452Sitojun 		}
1456b532452Sitojun 
1466b532452Sitojun 		*optp++ = type;
1476b532452Sitojun 		*optp++ = len;
1486b532452Sitojun 
1496b532452Sitojun 		*databufp = optp;
1506b532452Sitojun 	}
1516b532452Sitojun 
1526b532452Sitojun 	return (currentlen);
1536b532452Sitojun }
1546b532452Sitojun 
1556b532452Sitojun int
inet6_opt_finish(void * extbuf,socklen_t extlen,int offset)1566b532452Sitojun inet6_opt_finish(void *extbuf, socklen_t extlen, int offset)
1576b532452Sitojun {
158*ab8e3451Sderaadt 	int updatelen = offset > 0 ? (1 + ((offset - 1) | 7)) : 0;
1596b532452Sitojun 
1606b532452Sitojun 	if (extbuf) {
1616b532452Sitojun 		u_int8_t *padp;
1626b532452Sitojun 		int padlen = updatelen - offset;
1636b532452Sitojun 
1646b532452Sitojun 		if (updatelen > extlen)
1656b532452Sitojun 			return (-1);
1666b532452Sitojun 
1676b532452Sitojun 		padp = (u_int8_t *)extbuf + offset;
1686b532452Sitojun 		if (padlen == 1)
1696b532452Sitojun 			*padp = IP6OPT_PAD1;
1706b532452Sitojun 		else if (padlen > 0) {
1716b532452Sitojun 			*padp++ = IP6OPT_PADN;
1726b532452Sitojun 			*padp++ = (padlen - 2);
1736b532452Sitojun 			memset(padp, 0, padlen - 2);
1746b532452Sitojun 		}
1756b532452Sitojun 	}
1766b532452Sitojun 
1776b532452Sitojun 	return (updatelen);
1786b532452Sitojun }
1796b532452Sitojun 
1806b532452Sitojun int
inet6_opt_set_val(void * databuf,int offset,void * val,socklen_t vallen)1816b532452Sitojun inet6_opt_set_val(void *databuf, int offset, void *val, socklen_t vallen)
1826b532452Sitojun {
1836b532452Sitojun 
1846b532452Sitojun 	memcpy((u_int8_t *)databuf + offset, val, vallen);
1856b532452Sitojun 	return (offset + vallen);
1866b532452Sitojun }
1876b532452Sitojun 
1886b532452Sitojun int
inet6_opt_next(void * extbuf,socklen_t extlen,int offset,u_int8_t * typep,socklen_t * lenp,void ** databufp)1896b532452Sitojun inet6_opt_next(void *extbuf, socklen_t extlen, int offset, u_int8_t *typep,
1906b532452Sitojun 	       socklen_t *lenp, void **databufp)
1916b532452Sitojun {
1926b532452Sitojun 	u_int8_t *optp, *lim;
1936b532452Sitojun 	int optlen;
1946b532452Sitojun 
1956b532452Sitojun 	/* Validate extlen. XXX: is the variable really necessary?? */
1966b532452Sitojun 	if (extlen == 0 || (extlen % 8))
1976b532452Sitojun 		return (-1);
1986b532452Sitojun 	lim = (u_int8_t *)extbuf + extlen;
1996b532452Sitojun 
2006b532452Sitojun 	/*
2016b532452Sitojun 	 * If this is the first time this function called for this options
2026b532452Sitojun 	 * header, simply return the 1st option.
2036b532452Sitojun 	 * Otherwise, search the option list for the next option.
2046b532452Sitojun 	 */
2056b532452Sitojun 	if (offset == 0)
2066b532452Sitojun 		optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
2076b532452Sitojun 	else
2086b532452Sitojun 		optp = (u_int8_t *)extbuf + offset;
2096b532452Sitojun 
2106b532452Sitojun 	/* Find the next option skipping any padding options. */
2116b532452Sitojun 	while (optp < lim) {
2126b532452Sitojun 		switch(*optp) {
2136b532452Sitojun 		case IP6OPT_PAD1:
2146b532452Sitojun 			optp++;
2156b532452Sitojun 			break;
2166b532452Sitojun 		case IP6OPT_PADN:
2176b532452Sitojun 			if ((optlen = ip6optlen(optp, lim)) == 0)
2186b532452Sitojun 				goto optend;
2196b532452Sitojun 			optp += optlen;
2206b532452Sitojun 			break;
2216b532452Sitojun 		default:	/* found */
2226b532452Sitojun 			if ((optlen = ip6optlen(optp, lim)) == 0)
2236b532452Sitojun 				goto optend;
2246b532452Sitojun 			*typep = *optp;
2256b532452Sitojun 			*lenp = optlen - 2;
2266b532452Sitojun 			*databufp = optp + 2;
2276b532452Sitojun 			return (optp + optlen - (u_int8_t *)extbuf);
2286b532452Sitojun 		}
2296b532452Sitojun 	}
2306b532452Sitojun 
2316b532452Sitojun   optend:
2326b532452Sitojun 	*databufp = NULL; /* for safety */
2336b532452Sitojun 	return (-1);
2346b532452Sitojun }
2356b532452Sitojun 
2366b532452Sitojun int
inet6_opt_find(void * extbuf,socklen_t extlen,int offset,u_int8_t type,socklen_t * lenp,void ** databufp)2376b532452Sitojun inet6_opt_find(void *extbuf, socklen_t extlen, int offset, u_int8_t type,
2386b532452Sitojun 	       socklen_t *lenp, void **databufp)
2396b532452Sitojun {
2406b532452Sitojun 	u_int8_t *optp, *lim;
2416b532452Sitojun 	int optlen;
2426b532452Sitojun 
2436b532452Sitojun 	/* Validate extlen. XXX: is the variable really necessary?? */
2446b532452Sitojun 	if (extlen == 0 || (extlen % 8))
2456b532452Sitojun 		return (-1);
2466b532452Sitojun 	lim = (u_int8_t *)extbuf + extlen;
2476b532452Sitojun 
2486b532452Sitojun 	/*
2496b532452Sitojun 	 * If this is the first time this function called for this options
2506b532452Sitojun 	 * header, simply return the 1st option.
2516b532452Sitojun 	 * Otherwise, search the option list for the next option.
2526b532452Sitojun 	 */
2536b532452Sitojun 	if (offset == 0)
2546b532452Sitojun 		optp = (u_int8_t *)((struct ip6_hbh *)extbuf + 1);
2556b532452Sitojun 	else
2566b532452Sitojun 		optp = (u_int8_t *)extbuf + offset;
2576b532452Sitojun 
2586b532452Sitojun 	/* Find the specified option */
2596b532452Sitojun 	while (optp < lim) {
2606b532452Sitojun 		if ((optlen = ip6optlen(optp, lim)) == 0)
2616b532452Sitojun 			goto optend;
2626b532452Sitojun 
2636b532452Sitojun 		if (*optp == type) { /* found */
2646b532452Sitojun 			*lenp = optlen - 2;
2656b532452Sitojun 			*databufp = optp + 2;
2666b532452Sitojun 			return (optp + optlen - (u_int8_t *)extbuf);
2676b532452Sitojun 		}
2686b532452Sitojun 
2696b532452Sitojun 		optp += optlen;
2706b532452Sitojun 	}
2716b532452Sitojun 
2726b532452Sitojun   optend:
2736b532452Sitojun 	*databufp = NULL; /* for safety */
2746b532452Sitojun 	return (-1);
2756b532452Sitojun }
2766b532452Sitojun 
2776b532452Sitojun int
inet6_opt_get_val(void * databuf,int offset,void * val,socklen_t vallen)2786b532452Sitojun inet6_opt_get_val(void *databuf, int offset, void *val, socklen_t vallen)
2796b532452Sitojun {
2806b532452Sitojun 
2816b532452Sitojun 	/* we can't assume alignment here */
2826b532452Sitojun 	memcpy(val, (u_int8_t *)databuf + offset, vallen);
2836b532452Sitojun 
2846b532452Sitojun 	return (offset + vallen);
2856b532452Sitojun }
286