xref: /illumos-gate/usr/src/lib/libsocket/inet/inet6_rthdr.c (revision 797f979d1fe26bfb1cdeb3e7a86ed24c0b654200)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This code is conformant to revision 7 of 2292bis.  Some of these functions
31  * were provided (named inet6_rthdr_) in a very similar form in RFC 2292.
32  * The RFC 2292 variants are not supported.
33  */
34 
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <netinet/ip6.h>
43 #include <unistd.h>
44 #include <errno.h>
45 
46 #define	MAX_RTHDR0_SEGMENTS 127
47 
48 /*
49  * Return amount of space needed to hold N segments for the specified
50  * routing type. Does NOT include space for cmsghdr.
51  */
52 socklen_t
53 inet6_rth_space(int type, int segments)
54 {
55 	if (type != IPV6_RTHDR_TYPE_0 || segments < 0 ||
56 	    segments > MAX_RTHDR0_SEGMENTS)
57 		return (0);
58 
59 	return (sizeof (struct ip6_rthdr0) +
60 	    segments * sizeof (struct in6_addr));
61 }
62 
63 /*
64  * Initializes rthdr structure. Verifies the segments against the length of
65  * the buffer.
66  * Note that a routing header can only hold 127 segments since the length field
67  * in the header is just a byte.
68  */
69 void *
70 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
71 {
72 	struct ip6_rthdr0 *rthdr;
73 
74 	if (type != IPV6_RTHDR_TYPE_0 || segments < 0 ||
75 	    segments > MAX_RTHDR0_SEGMENTS)
76 		return (NULL);
77 
78 	if (bp_len < sizeof (struct ip6_rthdr0) +
79 	    segments * sizeof (struct in6_addr))
80 		return (NULL);
81 
82 	rthdr = (struct ip6_rthdr0 *)bp;
83 	rthdr->ip6r0_nxt = 0;
84 	rthdr->ip6r0_len = (segments * 2);
85 	rthdr->ip6r0_type = type;
86 	rthdr->ip6r0_segleft = 0;	/* Incremented by rthdr_add */
87 	*(uint32_t *)&rthdr->ip6r0_reserved = 0;
88 	return (bp);
89 }
90 
91 /*
92  * Add one more address to the routing header. Fails when there is no more
93  * room.
94  */
95 int
96 inet6_rth_add(void *bp, const struct in6_addr *addr)
97 {
98 	struct ip6_rthdr0 *rthdr;
99 	struct in6_addr *addrs;
100 
101 	rthdr = (struct ip6_rthdr0 *)bp;
102 	if ((rthdr->ip6r0_segleft + 1) * 2 > rthdr->ip6r0_len) {
103 		/* Not room for one more */
104 		return (-1);
105 	}
106 	addrs = (struct in6_addr *)((char *)rthdr + sizeof (*rthdr));
107 	addrs[rthdr->ip6r0_segleft++] = *addr;
108 	return (0);
109 }
110 
111 /*
112  * Reverse a source route. Both arguments can point to the same buffer.
113  */
114 int
115 inet6_rth_reverse(const void *in, void *out)
116 {
117 	struct ip6_rthdr0 *rtin, *rtout;
118 	int i, segments;
119 	struct in6_addr tmp;
120 	struct in6_addr *rtout_addrs;
121 	struct in6_addr *rtin_addrs;
122 
123 	rtin = (struct ip6_rthdr0 *)in;
124 	rtout = (struct ip6_rthdr0 *)out;
125 
126 	if (rtout->ip6r0_type != 0 || rtin->ip6r0_type != 0 ||
127 	    rtout->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2 ||
128 	    rtin->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2 ||
129 	    rtout->ip6r0_len != rtin->ip6r0_len)
130 		return (-1);
131 
132 	segments = rtin->ip6r0_len / 2;
133 	rtout_addrs = (struct in6_addr *)((char *)rtout + sizeof (*rtout));
134 	rtin_addrs = (struct in6_addr *)((char *)rtin + sizeof (*rtin));
135 	for (i = 0; i < (segments + 1)/2; i++) {
136 		tmp = rtin_addrs[i];
137 		rtout_addrs[i] = rtin_addrs[segments - 1 - i];
138 		rtout_addrs[segments - 1 - i] = tmp;
139 	}
140 	rtout->ip6r0_segleft = segments;
141 	return (0);
142 }
143 
144 /*
145  * Return the number of segments in the routing header.
146  */
147 int
148 inet6_rth_segments(const void *bp)
149 {
150 	struct ip6_rthdr0 *rthdr;
151 
152 	rthdr = (struct ip6_rthdr0 *)bp;
153 	if (rthdr->ip6r0_type == 0) {
154 		if (rthdr->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2) {
155 			return (-1);
156 		} else {
157 			return (rthdr->ip6r0_len / 2);
158 		}
159 	} else {
160 		return (-1);
161 	}
162 }
163 
164 /*
165  * Return a pointer to an element in the source route.
166  * This uses the C convention for index [0, size-1].
167  */
168 struct in6_addr *
169 inet6_rth_getaddr(const void *bp, int index)
170 {
171 	struct ip6_rthdr0 *rthdr;
172 	struct in6_addr *rv;
173 
174 	rthdr = (struct ip6_rthdr0 *)bp;
175 	if (index >= rthdr->ip6r0_len/2 || index < 0)
176 		return (NULL);
177 
178 	rv = (struct in6_addr *)((char *)rthdr + sizeof (*rthdr));
179 	return (&rv[index]);
180 }
181