xref: /freebsd/lib/libc/net/rthdr.c (revision 8fb3f3f68288ae2b1b53dd65e3dd673d83c80f4c)
105a244b4SYoshinobu Inoue /*
205a244b4SYoshinobu Inoue  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
305a244b4SYoshinobu Inoue  * All rights reserved.
405a244b4SYoshinobu Inoue  *
505a244b4SYoshinobu Inoue  * Redistribution and use in source and binary forms, with or without
605a244b4SYoshinobu Inoue  * modification, are permitted provided that the following conditions
705a244b4SYoshinobu Inoue  * are met:
805a244b4SYoshinobu Inoue  * 1. Redistributions of source code must retain the above copyright
905a244b4SYoshinobu Inoue  *    notice, this list of conditions and the following disclaimer.
1005a244b4SYoshinobu Inoue  * 2. Redistributions in binary form must reproduce the above copyright
1105a244b4SYoshinobu Inoue  *    notice, this list of conditions and the following disclaimer in the
1205a244b4SYoshinobu Inoue  *    documentation and/or other materials provided with the distribution.
1305a244b4SYoshinobu Inoue  * 3. Neither the name of the project nor the names of its contributors
1405a244b4SYoshinobu Inoue  *    may be used to endorse or promote products derived from this software
1505a244b4SYoshinobu Inoue  *    without specific prior written permission.
1605a244b4SYoshinobu Inoue  *
1705a244b4SYoshinobu Inoue  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1805a244b4SYoshinobu Inoue  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1905a244b4SYoshinobu Inoue  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2005a244b4SYoshinobu Inoue  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2105a244b4SYoshinobu Inoue  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2205a244b4SYoshinobu Inoue  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2305a244b4SYoshinobu Inoue  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2405a244b4SYoshinobu Inoue  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2505a244b4SYoshinobu Inoue  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2605a244b4SYoshinobu Inoue  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2705a244b4SYoshinobu Inoue  * SUCH DAMAGE.
2805a244b4SYoshinobu Inoue  *
2905a244b4SYoshinobu Inoue  * $FreeBSD$
3005a244b4SYoshinobu Inoue  */
3105a244b4SYoshinobu Inoue 
3205a244b4SYoshinobu Inoue #include <sys/param.h>
3305a244b4SYoshinobu Inoue #include <sys/types.h>
3405a244b4SYoshinobu Inoue #include <sys/socket.h>
3505a244b4SYoshinobu Inoue 
3605a244b4SYoshinobu Inoue #include <netinet/in.h>
3705a244b4SYoshinobu Inoue #include <netinet/ip6.h>
3805a244b4SYoshinobu Inoue 
3905a244b4SYoshinobu Inoue #include <string.h>
4005a244b4SYoshinobu Inoue #include <stdio.h>
4105a244b4SYoshinobu Inoue 
4205a244b4SYoshinobu Inoue size_t
4305a244b4SYoshinobu Inoue inet6_rthdr_space(type, seg)
4405a244b4SYoshinobu Inoue     int type, seg;
4505a244b4SYoshinobu Inoue {
4605a244b4SYoshinobu Inoue     switch(type) {
4705a244b4SYoshinobu Inoue      case IPV6_RTHDR_TYPE_0:
4805a244b4SYoshinobu Inoue 	 if (seg < 1 || seg > 23)
4905a244b4SYoshinobu Inoue 	     return(0);
5005a244b4SYoshinobu Inoue 	 return(CMSG_SPACE(sizeof(struct in6_addr) * (seg - 1)
5105a244b4SYoshinobu Inoue 			   + sizeof(struct ip6_rthdr0)));
5205a244b4SYoshinobu Inoue      default:
5305a244b4SYoshinobu Inoue #ifdef DEBUG
5405a244b4SYoshinobu Inoue 	 fprintf(stderr, "inet6_rthdr_space: unknown type(%d)\n", type);
5505a244b4SYoshinobu Inoue #endif
5605a244b4SYoshinobu Inoue 	 return(0);
5705a244b4SYoshinobu Inoue     }
5805a244b4SYoshinobu Inoue }
5905a244b4SYoshinobu Inoue 
6005a244b4SYoshinobu Inoue struct cmsghdr *
6105a244b4SYoshinobu Inoue inet6_rthdr_init(bp, type)
6205a244b4SYoshinobu Inoue     void *bp;
6305a244b4SYoshinobu Inoue     int type;
6405a244b4SYoshinobu Inoue {
658fb3f3f6SDavid E. O'Brien     struct cmsghdr *ch = (struct cmsghdr *)bp;
668fb3f3f6SDavid E. O'Brien     struct ip6_rthdr *rthdr;
677d0d8dc3SYoshinobu Inoue 
687d0d8dc3SYoshinobu Inoue     rthdr = (struct ip6_rthdr *)CMSG_DATA(ch);
6905a244b4SYoshinobu Inoue 
7005a244b4SYoshinobu Inoue     ch->cmsg_level = IPPROTO_IPV6;
7105a244b4SYoshinobu Inoue     ch->cmsg_type = IPV6_RTHDR;
7205a244b4SYoshinobu Inoue 
7305a244b4SYoshinobu Inoue     switch(type) {
7405a244b4SYoshinobu Inoue      case IPV6_RTHDR_TYPE_0:
7505a244b4SYoshinobu Inoue 	 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) - sizeof(struct in6_addr));
7605a244b4SYoshinobu Inoue 	 bzero(rthdr, sizeof(struct ip6_rthdr0));
7705a244b4SYoshinobu Inoue 	 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
7805a244b4SYoshinobu Inoue 	 return(ch);
7905a244b4SYoshinobu Inoue      default:
8005a244b4SYoshinobu Inoue #ifdef DEBUG
8105a244b4SYoshinobu Inoue 	 fprintf(stderr, "inet6_rthdr_init: unknown type(%d)\n", type);
8205a244b4SYoshinobu Inoue #endif
8305a244b4SYoshinobu Inoue 	 return(NULL);
8405a244b4SYoshinobu Inoue     }
8505a244b4SYoshinobu Inoue }
8605a244b4SYoshinobu Inoue 
8705a244b4SYoshinobu Inoue int
8805a244b4SYoshinobu Inoue inet6_rthdr_add(cmsg, addr, flags)
8905a244b4SYoshinobu Inoue     struct cmsghdr *cmsg;
9005a244b4SYoshinobu Inoue     const struct in6_addr *addr;
9105a244b4SYoshinobu Inoue     u_int flags;
9205a244b4SYoshinobu Inoue {
938fb3f3f6SDavid E. O'Brien     struct ip6_rthdr *rthdr;
947d0d8dc3SYoshinobu Inoue 
957d0d8dc3SYoshinobu Inoue     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
9605a244b4SYoshinobu Inoue 
9705a244b4SYoshinobu Inoue     switch(rthdr->ip6r_type) {
9805a244b4SYoshinobu Inoue      case IPV6_RTHDR_TYPE_0:
9905a244b4SYoshinobu Inoue      {
10005a244b4SYoshinobu Inoue 	 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
10105a244b4SYoshinobu Inoue 	 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
10205a244b4SYoshinobu Inoue #ifdef DEBUG
10305a244b4SYoshinobu Inoue 	     fprintf(stderr, "inet6_rthdr_add: unsupported flag(%d)\n", flags);
10405a244b4SYoshinobu Inoue #endif
10505a244b4SYoshinobu Inoue 	     return(-1);
10605a244b4SYoshinobu Inoue 	 }
10705a244b4SYoshinobu Inoue 	 if (rt0->ip6r0_segleft == 23) {
10805a244b4SYoshinobu Inoue #ifdef DEBUG
10905a244b4SYoshinobu Inoue 	     fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
11005a244b4SYoshinobu Inoue #endif
11105a244b4SYoshinobu Inoue 	     return(-1);
11205a244b4SYoshinobu Inoue 	 }
11305a244b4SYoshinobu Inoue 	 if (flags == IPV6_RTHDR_STRICT) {
11405a244b4SYoshinobu Inoue 	     int c, b;
11505a244b4SYoshinobu Inoue 	     c = rt0->ip6r0_segleft / 8;
11605a244b4SYoshinobu Inoue 	     b = rt0->ip6r0_segleft % 8;
11705a244b4SYoshinobu Inoue 	     rt0->ip6r0_slmap[c] |= (1 << (7 - b));
11805a244b4SYoshinobu Inoue 	 }
11905a244b4SYoshinobu Inoue 	 rt0->ip6r0_segleft++;
12005a244b4SYoshinobu Inoue 	 bcopy(addr, (caddr_t)rt0 + ((rt0->ip6r0_len + 1) << 3),
12105a244b4SYoshinobu Inoue 	       sizeof(struct in6_addr));
12205a244b4SYoshinobu Inoue 	 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
12305a244b4SYoshinobu Inoue 	 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
12405a244b4SYoshinobu Inoue 	 break;
12505a244b4SYoshinobu Inoue      }
12605a244b4SYoshinobu Inoue      default:
12705a244b4SYoshinobu Inoue #ifdef DEBUG
12805a244b4SYoshinobu Inoue 	 fprintf(stderr, "inet6_rthdr_add: unknown type(%d)\n",
12905a244b4SYoshinobu Inoue 		 rthdr->ip6r_type);
13005a244b4SYoshinobu Inoue #endif
13105a244b4SYoshinobu Inoue 	 return(-1);
13205a244b4SYoshinobu Inoue     }
13305a244b4SYoshinobu Inoue 
13405a244b4SYoshinobu Inoue     return(0);
13505a244b4SYoshinobu Inoue }
13605a244b4SYoshinobu Inoue 
13705a244b4SYoshinobu Inoue int
13805a244b4SYoshinobu Inoue inet6_rthdr_lasthop(cmsg, flags)
13905a244b4SYoshinobu Inoue     struct cmsghdr *cmsg;
14005a244b4SYoshinobu Inoue     unsigned int flags;
14105a244b4SYoshinobu Inoue {
1428fb3f3f6SDavid E. O'Brien     struct ip6_rthdr *rthdr;
1437d0d8dc3SYoshinobu Inoue 
1447d0d8dc3SYoshinobu Inoue     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
14505a244b4SYoshinobu Inoue 
14605a244b4SYoshinobu Inoue     switch(rthdr->ip6r_type) {
14705a244b4SYoshinobu Inoue      case IPV6_RTHDR_TYPE_0:
14805a244b4SYoshinobu Inoue      {
14905a244b4SYoshinobu Inoue 	 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
15005a244b4SYoshinobu Inoue 	 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT) {
15105a244b4SYoshinobu Inoue #ifdef DEBUG
15205a244b4SYoshinobu Inoue 	     fprintf(stderr, "inet6_rthdr_lasthop: unsupported flag(%d)\n", flags);
15305a244b4SYoshinobu Inoue #endif
15405a244b4SYoshinobu Inoue 	     return(-1);
15505a244b4SYoshinobu Inoue 	 }
15605a244b4SYoshinobu Inoue 	 if (rt0->ip6r0_segleft > 23) {
15705a244b4SYoshinobu Inoue #ifdef DEBUG
15805a244b4SYoshinobu Inoue 	     fprintf(stderr, "inet6_rthdr_add: segment overflow\n");
15905a244b4SYoshinobu Inoue #endif
16005a244b4SYoshinobu Inoue 	     return(-1);
16105a244b4SYoshinobu Inoue 	 }
16205a244b4SYoshinobu Inoue 	 if (flags == IPV6_RTHDR_STRICT) {
16305a244b4SYoshinobu Inoue 	     int c, b;
16405a244b4SYoshinobu Inoue 	     c = rt0->ip6r0_segleft / 8;
16505a244b4SYoshinobu Inoue 	     b = rt0->ip6r0_segleft % 8;
16605a244b4SYoshinobu Inoue 	     rt0->ip6r0_slmap[c] |= (1 << (7 - b));
16705a244b4SYoshinobu Inoue 	 }
16805a244b4SYoshinobu Inoue 	 break;
16905a244b4SYoshinobu Inoue      }
17005a244b4SYoshinobu Inoue      default:
17105a244b4SYoshinobu Inoue #ifdef DEBUG
17205a244b4SYoshinobu Inoue 	 fprintf(stderr, "inet6_rthdr_lasthop: unknown type(%d)\n",
17305a244b4SYoshinobu Inoue 		 rthdr->ip6r_type);
17405a244b4SYoshinobu Inoue #endif
17505a244b4SYoshinobu Inoue 	 return(-1);
17605a244b4SYoshinobu Inoue     }
17705a244b4SYoshinobu Inoue 
17805a244b4SYoshinobu Inoue     return(0);
17905a244b4SYoshinobu Inoue }
18005a244b4SYoshinobu Inoue 
18105a244b4SYoshinobu Inoue #if 0
18205a244b4SYoshinobu Inoue int
18305a244b4SYoshinobu Inoue inet6_rthdr_reverse(in, out)
18405a244b4SYoshinobu Inoue     const struct cmsghdr *in;
18505a244b4SYoshinobu Inoue     struct cmsghdr *out;
18605a244b4SYoshinobu Inoue {
18705a244b4SYoshinobu Inoue #ifdef DEBUG
18805a244b4SYoshinobu Inoue     fprintf(stderr, "inet6_rthdr_reverse: not implemented yet\n");
18905a244b4SYoshinobu Inoue #endif
19005a244b4SYoshinobu Inoue     return -1;
19105a244b4SYoshinobu Inoue }
19205a244b4SYoshinobu Inoue #endif
19305a244b4SYoshinobu Inoue 
19405a244b4SYoshinobu Inoue int
19505a244b4SYoshinobu Inoue inet6_rthdr_segments(cmsg)
19605a244b4SYoshinobu Inoue     const struct cmsghdr *cmsg;
19705a244b4SYoshinobu Inoue {
1988fb3f3f6SDavid E. O'Brien     struct ip6_rthdr *rthdr;
1997d0d8dc3SYoshinobu Inoue 
2007d0d8dc3SYoshinobu Inoue     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
20105a244b4SYoshinobu Inoue 
20205a244b4SYoshinobu Inoue     switch(rthdr->ip6r_type) {
20305a244b4SYoshinobu Inoue     case IPV6_RTHDR_TYPE_0:
20405a244b4SYoshinobu Inoue       {
20505a244b4SYoshinobu Inoue 	struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
20605a244b4SYoshinobu Inoue 
20705a244b4SYoshinobu Inoue 	if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
20805a244b4SYoshinobu Inoue #ifdef DEBUG
20905a244b4SYoshinobu Inoue 	    fprintf(stderr, "inet6_rthdr_segments: invalid size(%d)\n",
21005a244b4SYoshinobu Inoue 		rt0->ip6r0_len);
21105a244b4SYoshinobu Inoue #endif
21205a244b4SYoshinobu Inoue 	    return -1;
21305a244b4SYoshinobu Inoue 	}
21405a244b4SYoshinobu Inoue 
21505a244b4SYoshinobu Inoue 	return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
21605a244b4SYoshinobu Inoue       }
21705a244b4SYoshinobu Inoue 
21805a244b4SYoshinobu Inoue     default:
21905a244b4SYoshinobu Inoue #ifdef DEBUG
22005a244b4SYoshinobu Inoue 	fprintf(stderr, "inet6_rthdr_segments: unknown type(%d)\n",
22105a244b4SYoshinobu Inoue 	    rthdr->ip6r_type);
22205a244b4SYoshinobu Inoue #endif
22305a244b4SYoshinobu Inoue 	return -1;
22405a244b4SYoshinobu Inoue     }
22505a244b4SYoshinobu Inoue }
22605a244b4SYoshinobu Inoue 
22705a244b4SYoshinobu Inoue struct in6_addr *
22805a244b4SYoshinobu Inoue inet6_rthdr_getaddr(cmsg, index)
22905a244b4SYoshinobu Inoue     struct cmsghdr *cmsg;
23005a244b4SYoshinobu Inoue     int index;
23105a244b4SYoshinobu Inoue {
2328fb3f3f6SDavid E. O'Brien     struct ip6_rthdr *rthdr;
2337d0d8dc3SYoshinobu Inoue 
2347d0d8dc3SYoshinobu Inoue     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
23505a244b4SYoshinobu Inoue 
23605a244b4SYoshinobu Inoue     switch(rthdr->ip6r_type) {
23705a244b4SYoshinobu Inoue     case IPV6_RTHDR_TYPE_0:
23805a244b4SYoshinobu Inoue       {
23905a244b4SYoshinobu Inoue 	struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
24005a244b4SYoshinobu Inoue 	int naddr;
24105a244b4SYoshinobu Inoue 
24205a244b4SYoshinobu Inoue 	if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
24305a244b4SYoshinobu Inoue #ifdef DEBUG
24405a244b4SYoshinobu Inoue 	    fprintf(stderr, "inet6_rthdr_getaddr: invalid size(%d)\n",
24505a244b4SYoshinobu Inoue 		rt0->ip6r0_len);
24605a244b4SYoshinobu Inoue #endif
24705a244b4SYoshinobu Inoue 	    return NULL;
24805a244b4SYoshinobu Inoue 	}
24905a244b4SYoshinobu Inoue 	naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
25005a244b4SYoshinobu Inoue 	if (index <= 0 || naddr < index) {
25105a244b4SYoshinobu Inoue #ifdef DEBUG
25205a244b4SYoshinobu Inoue 	    fprintf(stderr, "inet6_rthdr_getaddr: invalid index(%d)\n", index);
25305a244b4SYoshinobu Inoue #endif
25405a244b4SYoshinobu Inoue 	    return NULL;
25505a244b4SYoshinobu Inoue 	}
25605a244b4SYoshinobu Inoue 	return &rt0->ip6r0_addr[index - 1];
25705a244b4SYoshinobu Inoue       }
25805a244b4SYoshinobu Inoue 
25905a244b4SYoshinobu Inoue     default:
26005a244b4SYoshinobu Inoue #ifdef DEBUG
26105a244b4SYoshinobu Inoue 	fprintf(stderr, "inet6_rthdr_getaddr: unknown type(%d)\n",
26205a244b4SYoshinobu Inoue 	    rthdr->ip6r_type);
26305a244b4SYoshinobu Inoue #endif
26405a244b4SYoshinobu Inoue 	return NULL;
26505a244b4SYoshinobu Inoue     }
26605a244b4SYoshinobu Inoue }
26705a244b4SYoshinobu Inoue 
26805a244b4SYoshinobu Inoue int
26905a244b4SYoshinobu Inoue inet6_rthdr_getflags(cmsg, index)
27005a244b4SYoshinobu Inoue     const struct cmsghdr *cmsg;
27105a244b4SYoshinobu Inoue     int index;
27205a244b4SYoshinobu Inoue {
2738fb3f3f6SDavid E. O'Brien     struct ip6_rthdr *rthdr;
2747d0d8dc3SYoshinobu Inoue 
2757d0d8dc3SYoshinobu Inoue     rthdr = (struct ip6_rthdr *)CMSG_DATA(cmsg);
27605a244b4SYoshinobu Inoue 
27705a244b4SYoshinobu Inoue     switch(rthdr->ip6r_type) {
27805a244b4SYoshinobu Inoue     case IPV6_RTHDR_TYPE_0:
27905a244b4SYoshinobu Inoue       {
28005a244b4SYoshinobu Inoue 	struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)rthdr;
28105a244b4SYoshinobu Inoue 	int naddr;
28205a244b4SYoshinobu Inoue 
28305a244b4SYoshinobu Inoue 	if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len) {
28405a244b4SYoshinobu Inoue #ifdef DEBUG
28505a244b4SYoshinobu Inoue 	    fprintf(stderr, "inet6_rthdr_getflags: invalid size(%d)\n",
28605a244b4SYoshinobu Inoue 		rt0->ip6r0_len);
28705a244b4SYoshinobu Inoue #endif
28805a244b4SYoshinobu Inoue 	    return -1;
28905a244b4SYoshinobu Inoue 	}
29005a244b4SYoshinobu Inoue 	naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
29105a244b4SYoshinobu Inoue 	if (index < 0 || naddr < index) {
29205a244b4SYoshinobu Inoue #ifdef DEBUG
29305a244b4SYoshinobu Inoue 	    fprintf(stderr, "inet6_rthdr_getflags: invalid index(%d)\n", index);
29405a244b4SYoshinobu Inoue #endif
29505a244b4SYoshinobu Inoue 	    return -1;
29605a244b4SYoshinobu Inoue 	}
29705a244b4SYoshinobu Inoue 	if (rt0->ip6r0_slmap[index / 8] & (0x80 >> (index % 8)))
29805a244b4SYoshinobu Inoue 	    return IPV6_RTHDR_STRICT;
29905a244b4SYoshinobu Inoue 	else
30005a244b4SYoshinobu Inoue 	    return IPV6_RTHDR_LOOSE;
30105a244b4SYoshinobu Inoue       }
30205a244b4SYoshinobu Inoue 
30305a244b4SYoshinobu Inoue     default:
30405a244b4SYoshinobu Inoue #ifdef DEBUG
30505a244b4SYoshinobu Inoue 	fprintf(stderr, "inet6_rthdr_getflags: unknown type(%d)\n",
30605a244b4SYoshinobu Inoue 	    rthdr->ip6r_type);
30705a244b4SYoshinobu Inoue #endif
30805a244b4SYoshinobu Inoue 	return -1;
30905a244b4SYoshinobu Inoue     }
31005a244b4SYoshinobu Inoue }
311