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