xref: /openbsd-src/lib/libc/net/rthdr.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: rthdr.c,v 1.11 2015/09/14 11:01:47 guenther 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
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 suppported */
54 	}
55 }
56 DEF_WEAK(inet6_rth_space);
57 
58 void *
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
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
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((void *)rth0_out, (void *)rth0_in,
124 			((rth0_in->ip6r0_len) + 1) << 3);
125 		rth0_out->ip6r0_segleft = segments;
126 
127 		/* reverse the addresses */
128 		for (i = 0; i < segments / 2; i++) {
129 			struct in6_addr addr_tmp, *addr1, *addr2;
130 
131 			addr1 = (struct in6_addr *)(rth0_out + 1) + i;
132 			addr2 = (struct in6_addr *)(rth0_out + 1) +
133 			    (segments - i - 1);
134 			addr_tmp = *addr1;
135 			*addr1 = *addr2;
136 			*addr2 = addr_tmp;
137 		}
138 
139 		break;
140 	default:
141 		return (-1);	/* type not supported */
142 	}
143 
144 	return (0);
145 }
146 
147 int
148 inet6_rth_segments(const void *bp)
149 {
150 	struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
151 	struct ip6_rthdr0 *rh0;
152 	int addrs;
153 
154 	switch (rh->ip6r_type) {
155 	case IPV6_RTHDR_TYPE_0:
156 		rh0 = (struct ip6_rthdr0 *)bp;
157 
158 		/*
159 		 * Validation for a type-0 routing header.
160 		 * Is this too strict?
161 		 */
162 		if ((rh0->ip6r0_len % 2) != 0 ||
163 		    (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
164 			return (-1);
165 
166 		return (addrs);
167 	default:
168 		return (-1);	/* unknown type */
169 	}
170 }
171 
172 struct in6_addr *
173 inet6_rth_getaddr(const void *bp, int idx)
174 {
175 	struct ip6_rthdr *rh = (struct ip6_rthdr *)bp;
176 	struct ip6_rthdr0 *rh0;
177 	int addrs;
178 
179 	switch (rh->ip6r_type) {
180 	case IPV6_RTHDR_TYPE_0:
181 		 rh0 = (struct ip6_rthdr0 *)bp;
182 
183 		/*
184 		 * Validation for a type-0 routing header.
185 		 * Is this too strict?
186 		 */
187 		if ((rh0->ip6r0_len % 2) != 0 ||
188 		    (addrs = (rh0->ip6r0_len >> 1)) < rh0->ip6r0_segleft)
189 			return (NULL);
190 
191 		if (idx < 0 || addrs <= idx)
192 			return (NULL);
193 
194 		return (((struct in6_addr *)(rh0 + 1)) + idx);
195 	default:
196 		return (NULL);	/* unknown type */
197 		break;
198 	}
199 }
200