1 /* $OpenBSD: rthdr.c,v 1.13 2022/12/27 17:10:06 jmc Exp $ */
2 /* $KAME: rthdr.c,v 1.22 2006/02/09 08:18:58 keiichi Exp $ */
3
4 /*
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <sys/types.h>
34 #include <sys/socket.h>
35
36 #include <netinet/in.h>
37 #include <netinet/ip6.h>
38
39 #include <string.h>
40 #include <stdio.h>
41
42 /*
43 * RFC3542 (2292bis) API
44 */
45
46 socklen_t
inet6_rth_space(int type,int segments)47 inet6_rth_space(int type, int segments)
48 {
49 switch (type) {
50 case IPV6_RTHDR_TYPE_0:
51 return (((segments * 2) + 1) << 3);
52 default:
53 return (0); /* type not supported */
54 }
55 }
56 DEF_WEAK(inet6_rth_space);
57
58 void *
inet6_rth_init(void * bp,socklen_t bp_len,int type,int segments)59 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
60 {
61 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
62 struct ip6_rthdr0 *rth0;
63
64 switch (type) {
65 case IPV6_RTHDR_TYPE_0:
66 /* length validation */
67 if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments))
68 return (NULL);
69
70 memset(bp, 0, bp_len);
71 rth0 = (struct ip6_rthdr0 *)rth;
72 rth0->ip6r0_len = segments * 2;
73 rth0->ip6r0_type = IPV6_RTHDR_TYPE_0;
74 rth0->ip6r0_segleft = 0;
75 rth0->ip6r0_reserved = 0;
76 break;
77 default:
78 return (NULL); /* type not supported */
79 }
80
81 return (bp);
82 }
83
84 int
inet6_rth_add(void * bp,const struct in6_addr * addr)85 inet6_rth_add(void *bp, const struct in6_addr *addr)
86 {
87 struct ip6_rthdr *rth = (struct ip6_rthdr *)bp;
88 struct ip6_rthdr0 *rth0;
89 struct in6_addr *nextaddr;
90
91 switch (rth->ip6r_type) {
92 case IPV6_RTHDR_TYPE_0:
93 rth0 = (struct ip6_rthdr0 *)rth;
94 nextaddr = (struct in6_addr *)(rth0 + 1) + rth0->ip6r0_segleft;
95 *nextaddr = *addr;
96 rth0->ip6r0_segleft++;
97 break;
98 default:
99 return (-1); /* type not supported */
100 }
101
102 return (0);
103 }
104
105 int
inet6_rth_reverse(const void * in,void * out)106 inet6_rth_reverse(const void *in, void *out)
107 {
108 struct ip6_rthdr *rth_in = (struct ip6_rthdr *)in;
109 struct ip6_rthdr0 *rth0_in, *rth0_out;
110 int i, segments;
111
112 switch (rth_in->ip6r_type) {
113 case IPV6_RTHDR_TYPE_0:
114 rth0_in = (struct ip6_rthdr0 *)in;
115 rth0_out = (struct ip6_rthdr0 *)out;
116
117 /* parameter validation XXX too paranoid? */
118 if (rth0_in->ip6r0_len % 2)
119 return (-1);
120 segments = rth0_in->ip6r0_len / 2;
121
122 /* we can't use memcpy here, since in and out may overlap */
123 memmove(rth0_out, rth0_in, ((rth0_in->ip6r0_len) + 1) << 3);
124 rth0_out->ip6r0_segleft = segments;
125
126 /* reverse the addresses */
127 for (i = 0; i < segments / 2; i++) {
128 struct in6_addr addr_tmp, *addr1, *addr2;
129
130 addr1 = (struct in6_addr *)(rth0_out + 1) + i;
131 addr2 = (struct in6_addr *)(rth0_out + 1) +
132 (segments - i - 1);
133 addr_tmp = *addr1;
134 *addr1 = *addr2;
135 *addr2 = addr_tmp;
136 }
137
138 break;
139 default:
140 return (-1); /* type not supported */
141 }
142
143 return (0);
144 }
145
146 int
inet6_rth_segments(const void * bp)147 inet6_rth_segments(const void *bp)
148 {
149 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
150 struct ip6_rthdr0 *rh0;
151 int addrs;
152
153 switch (rh->ip6r_type) {
154 case IPV6_RTHDR_TYPE_0:
155 rh0 = (struct ip6_rthdr0 *)bp;
156
157 /*
158 * Validation for a type-0 routing header.
159 * Is this too strict?
160 */
161 if ((rh0->ip6r0_len % 2) != 0 ||
162 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
163 return (-1);
164
165 return (addrs);
166 default:
167 return (-1); /* unknown type */
168 }
169 }
170
171 struct in6_addr *
inet6_rth_getaddr(const void * bp,int idx)172 inet6_rth_getaddr(const void *bp, int idx)
173 {
174 struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
175 struct ip6_rthdr0 *rh0;
176 int addrs;
177
178 switch (rh->ip6r_type) {
179 case IPV6_RTHDR_TYPE_0:
180 rh0 = (struct ip6_rthdr0 *)bp;
181
182 /*
183 * Validation for a type-0 routing header.
184 * Is this too strict?
185 */
186 if ((rh0->ip6r0_len % 2) != 0 ||
187 (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
188 return (NULL);
189
190 if (idx < 0 || addrs <= idx)
191 return (NULL);
192
193 return (((struct in6_addr *)(rh0 + 1)) + idx);
194 default:
195 return (NULL); /* unknown type */
196 break;
197 }
198 }
199