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