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
inet6_rth_space(int type,int segments)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 *
inet6_rth_init(void * bp,socklen_t bp_len,int type,int segments)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
inet6_rth_add(void * bp,const struct in6_addr * addr)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
inet6_rth_reverse(const void * in,void * out)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
inet6_rth_segments(const void * bp)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 *
inet6_rth_getaddr(const void * bp,int index)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