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