1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.
24  * All rights reserved.  Use is subject to license terms.
25  */
26 
27 /*
28  * This code is conformant to revision 7 of 2292bis.  Some of these functions
29  * were provided (named inet6_rthdr_) in a very similar form in RFC 2292.
30  * The RFC 2292 variants are not supported.
31  */
32 
33 #include <stdio.h>
34 #include <ctype.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <netinet/ip6.h>
41 #include <unistd.h>
42 #include <errno.h>
43 
44 #define	MAX_RTHDR0_SEGMENTS 127
45 
46 /*
47  * Return amount of space needed to hold N segments for the specified
48  * routing type. Does NOT include space for cmsghdr.
49  */
50 socklen_t
inet6_rth_space(int type,int segments)51 inet6_rth_space(int type, int segments)
52 {
53 	if (type != IPV6_RTHDR_TYPE_0 || segments < 0 ||
54 	    segments > MAX_RTHDR0_SEGMENTS)
55 		return (0);
56 
57 	return (sizeof (struct ip6_rthdr0) +
58 	    segments * sizeof (struct in6_addr));
59 }
60 
61 /*
62  * Initializes rthdr structure. Verifies the segments against the length of
63  * the buffer.
64  * Note that a routing header can only hold 127 segments since the length field
65  * in the header is just a byte.
66  */
67 void *
inet6_rth_init(void * bp,socklen_t bp_len,int type,int segments)68 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
69 {
70 	struct ip6_rthdr0 *rthdr;
71 
72 	if (type != IPV6_RTHDR_TYPE_0 || segments < 0 ||
73 	    segments > MAX_RTHDR0_SEGMENTS)
74 		return (NULL);
75 
76 	if (bp_len < sizeof (struct ip6_rthdr0) +
77 	    segments * sizeof (struct in6_addr))
78 		return (NULL);
79 
80 	rthdr = (struct ip6_rthdr0 *)bp;
81 	rthdr->ip6r0_nxt = 0;
82 	rthdr->ip6r0_len = (segments * 2);
83 	rthdr->ip6r0_type = type;
84 	rthdr->ip6r0_segleft = 0;	/* Incremented by rthdr_add */
85 	*(uint32_t *)&rthdr->ip6r0_reserved = 0;
86 	return (bp);
87 }
88 
89 /*
90  * Add one more address to the routing header. Fails when there is no more
91  * room.
92  */
93 int
inet6_rth_add(void * bp,const struct in6_addr * addr)94 inet6_rth_add(void *bp, const struct in6_addr *addr)
95 {
96 	struct ip6_rthdr0 *rthdr;
97 	struct in6_addr *addrs;
98 
99 	rthdr = (struct ip6_rthdr0 *)bp;
100 	if ((rthdr->ip6r0_segleft + 1) * 2 > rthdr->ip6r0_len) {
101 		/* Not room for one more */
102 		return (-1);
103 	}
104 	addrs = (struct in6_addr *)((char *)rthdr + sizeof (*rthdr));
105 	addrs[rthdr->ip6r0_segleft++] = *addr;
106 	return (0);
107 }
108 
109 /*
110  * Reverse a source route. Both arguments can point to the same buffer.
111  */
112 int
inet6_rth_reverse(const void * in,void * out)113 inet6_rth_reverse(const void *in, void *out)
114 {
115 	struct ip6_rthdr0 *rtin, *rtout;
116 	int i, segments;
117 	struct in6_addr tmp;
118 	struct in6_addr *rtout_addrs;
119 	struct in6_addr *rtin_addrs;
120 
121 	rtin = (struct ip6_rthdr0 *)in;
122 	rtout = (struct ip6_rthdr0 *)out;
123 
124 	if (rtout->ip6r0_type != 0 || rtin->ip6r0_type != 0 ||
125 	    rtout->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2 ||
126 	    rtin->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2 ||
127 	    rtout->ip6r0_len != rtin->ip6r0_len)
128 		return (-1);
129 
130 	segments = rtin->ip6r0_len / 2;
131 	rtout_addrs = (struct in6_addr *)((char *)rtout + sizeof (*rtout));
132 	rtin_addrs = (struct in6_addr *)((char *)rtin + sizeof (*rtin));
133 	for (i = 0; i < (segments + 1)/2; i++) {
134 		tmp = rtin_addrs[i];
135 		rtout_addrs[i] = rtin_addrs[segments - 1 - i];
136 		rtout_addrs[segments - 1 - i] = tmp;
137 	}
138 	rtout->ip6r0_segleft = segments;
139 	return (0);
140 }
141 
142 /*
143  * Return the number of segments in the routing header.
144  */
145 int
inet6_rth_segments(const void * bp)146 inet6_rth_segments(const void *bp)
147 {
148 	struct ip6_rthdr0 *rthdr;
149 
150 	rthdr = (struct ip6_rthdr0 *)bp;
151 	if (rthdr->ip6r0_type == 0) {
152 		if (rthdr->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2) {
153 			return (-1);
154 		} else {
155 			return (rthdr->ip6r0_len / 2);
156 		}
157 	} else {
158 		return (-1);
159 	}
160 }
161 
162 /*
163  * Return a pointer to an element in the source route.
164  * This uses the C convention for index [0, size-1].
165  */
166 struct in6_addr *
inet6_rth_getaddr(const void * bp,int index)167 inet6_rth_getaddr(const void *bp, int index)
168 {
169 	struct ip6_rthdr0 *rthdr;
170 	struct in6_addr *rv;
171 
172 	rthdr = (struct ip6_rthdr0 *)bp;
173 	if (index >= rthdr->ip6r0_len/2 || index < 0)
174 		return (NULL);
175 
176 	rv = (struct in6_addr *)((char *)rthdr + sizeof (*rthdr));
177 	return (&rv[index]);
178 }
179