Blame view
net/ipv6/exthdrs.c
20.6 KB
1da177e4c
|
1 2 3 4 5 6 7 8 9 |
/* * Extension Header handling for IPv6 * Linux INET6 implementation * * Authors: * Pedro Roque <roque@di.fc.ul.pt> * Andi Kleen <ak@muc.de> * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> * |
1da177e4c
|
10 11 12 13 14 15 16 |
* This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ /* Changes: |
1ab1457c4
|
17 |
* yoshfuji : ensure not to overrun while parsing |
1da177e4c
|
18 19 20 21 22 23 24 25 26 27 |
* tlv options. * Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs(). * YOSHIFUJI Hideaki @USAGI Register inbound extension header * handlers as inet6_protocol{}. */ #include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> #include <linux/sockios.h> |
1da177e4c
|
28 29 30 31 |
#include <linux/net.h> #include <linux/netdevice.h> #include <linux/in6.h> #include <linux/icmpv6.h> |
5a0e3ad6a
|
32 |
#include <linux/slab.h> |
bc3b2d7fb
|
33 |
#include <linux/export.h> |
1da177e4c
|
34 |
|
352e512c3
|
35 |
#include <net/dst.h> |
1da177e4c
|
36 37 38 39 40 41 42 43 44 45 |
#include <net/sock.h> #include <net/snmp.h> #include <net/ipv6.h> #include <net/protocol.h> #include <net/transp_v6.h> #include <net/rawv6.h> #include <net/ndisc.h> #include <net/ip6_route.h> #include <net/addrconf.h> |
07a936260
|
46 |
#if IS_ENABLED(CONFIG_IPV6_MIP6) |
65d4ed922
|
47 48 |
#include <net/xfrm.h> #endif |
1da177e4c
|
49 50 51 52 53 54 |
#include <asm/uaccess.h> /* * Parsing tlv encoded headers. * |
a50feda54
|
55 56 |
* Parsing function "func" returns true, if parsing succeed * and false, if it failed. |
1da177e4c
|
57 58 59 60 61 |
* It MUST NOT touch skb->h. */ struct tlvtype_proc { int type; |
a50feda54
|
62 |
bool (*func)(struct sk_buff *skb, int offset); |
1da177e4c
|
63 64 65 66 67 68 69 |
}; /********************* Generic functions *********************/ /* An unknown option is detected, decide what to do */ |
a50feda54
|
70 |
static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff) |
1da177e4c
|
71 |
{ |
d56f90a7c
|
72 |
switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) { |
1da177e4c
|
73 |
case 0: /* ignore */ |
a50feda54
|
74 |
return true; |
1da177e4c
|
75 76 77 78 79 80 81 82 |
case 1: /* drop packet */ break; case 3: /* Send ICMP if not a multicast address and drop packet */ /* Actually, it is redundant check. icmp_send will recheck in any case. */ |
0660e03f6
|
83 |
if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) |
1da177e4c
|
84 85 86 |
break; case 2: /* send ICMP PARM PROB regardless and drop packet */ icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff); |
a50feda54
|
87 |
return false; |
3ff50b799
|
88 |
} |
1da177e4c
|
89 90 |
kfree_skb(skb); |
a50feda54
|
91 |
return false; |
1da177e4c
|
92 93 94 |
} /* Parse tlv encoded option header (hop-by-hop or destination) */ |
a50feda54
|
95 |
static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb) |
1da177e4c
|
96 |
{ |
a50feda54
|
97 |
const struct tlvtype_proc *curr; |
d56f90a7c
|
98 |
const unsigned char *nh = skb_network_header(skb); |
cfe1fc775
|
99 |
int off = skb_network_header_len(skb); |
9c70220b7
|
100 |
int len = (skb_transport_header(skb)[1] + 1) << 3; |
9b905fe68
|
101 |
int padlen = 0; |
1da177e4c
|
102 |
|
ea2ae17d6
|
103 |
if (skb_transport_offset(skb) + len > skb_headlen(skb)) |
1da177e4c
|
104 105 106 107 108 109 |
goto bad; off += 2; len -= 2; while (len > 0) { |
d56f90a7c
|
110 |
int optlen = nh[off + 1] + 2; |
c1412fce7
|
111 |
int i; |
1da177e4c
|
112 |
|
d56f90a7c
|
113 |
switch (nh[off]) { |
1de5a71c3
|
114 |
case IPV6_TLV_PAD1: |
1da177e4c
|
115 |
optlen = 1; |
9b905fe68
|
116 117 118 |
padlen++; if (padlen > 7) goto bad; |
1da177e4c
|
119 120 121 |
break; case IPV6_TLV_PADN: |
c1412fce7
|
122 123 124 125 126 |
/* RFC 2460 states that the purpose of PadN is * to align the containing header to multiples * of 8. 7 is therefore the highest valid value. * See also RFC 4942, Section 2.1.9.5. */ |
9b905fe68
|
127 128 |
padlen += optlen; if (padlen > 7) |
c1412fce7
|
129 130 131 132 133 134 135 136 137 |
goto bad; /* RFC 4942 recommends receiving hosts to * actively check PadN payload to contain * only zeroes. */ for (i = 2; i < optlen; i++) { if (nh[off + i] != 0) goto bad; } |
1da177e4c
|
138 139 140 141 142 143 |
break; default: /* Other TLV code so scan list */ if (optlen > len) goto bad; for (curr=procs; curr->type >= 0; curr++) { |
d56f90a7c
|
144 |
if (curr->type == nh[off]) { |
1ab1457c4
|
145 146 |
/* type specific length/alignment checks will be performed in the |
1da177e4c
|
147 |
func(). */ |
a50feda54
|
148 149 |
if (curr->func(skb, off) == false) return false; |
1da177e4c
|
150 151 152 153 |
break; } } if (curr->type < 0) { |
e5bbef20e
|
154 |
if (ip6_tlvopt_unknown(skb, off) == 0) |
a50feda54
|
155 |
return false; |
1da177e4c
|
156 |
} |
9b905fe68
|
157 |
padlen = 0; |
1da177e4c
|
158 159 160 161 162 |
break; } off += optlen; len -= optlen; } |
9b905fe68
|
163 |
|
1da177e4c
|
164 |
if (len == 0) |
a50feda54
|
165 |
return true; |
1da177e4c
|
166 167 |
bad: kfree_skb(skb); |
a50feda54
|
168 |
return false; |
1da177e4c
|
169 170 171 172 173 |
} /***************************** Destination options header. *****************************/ |
07a936260
|
174 |
#if IS_ENABLED(CONFIG_IPV6_MIP6) |
a50feda54
|
175 |
static bool ipv6_dest_hao(struct sk_buff *skb, int optoff) |
a831f5bbc
|
176 |
{ |
a831f5bbc
|
177 178 |
struct ipv6_destopt_hao *hao; struct inet6_skb_parm *opt = IP6CB(skb); |
0660e03f6
|
179 |
struct ipv6hdr *ipv6h = ipv6_hdr(skb); |
a831f5bbc
|
180 181 182 183 184 185 186 187 188 189 |
struct in6_addr tmp_addr; int ret; if (opt->dsthao) { LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated "); goto discard; } opt->dsthao = opt->dst1; opt->dst1 = 0; |
d56f90a7c
|
190 |
hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff); |
a831f5bbc
|
191 192 193 194 195 196 197 198 199 200 |
if (hao->length != 16) { LIMIT_NETDEBUG( KERN_DEBUG "hao invalid option length = %d ", hao->length); goto discard; } if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) { LIMIT_NETDEBUG( |
5b095d989
|
201 202 |
KERN_DEBUG "hao is not an unicast addr: %pI6 ", &hao->addr); |
a831f5bbc
|
203 204 205 206 207 208 209 210 211 |
goto discard; } ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr, (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS); if (unlikely(ret < 0)) goto discard; if (skb_cloned(skb)) { |
65c884666
|
212 |
if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) |
a831f5bbc
|
213 |
goto discard; |
a831f5bbc
|
214 |
/* update all variable using below by copied skbuff */ |
65c884666
|
215 |
hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + |
d56f90a7c
|
216 |
optoff); |
65c884666
|
217 |
ipv6h = ipv6_hdr(skb); |
a831f5bbc
|
218 219 220 221 |
} if (skb->ip_summed == CHECKSUM_COMPLETE) skb->ip_summed = CHECKSUM_NONE; |
4e3fd7a06
|
222 223 224 |
tmp_addr = ipv6h->saddr; ipv6h->saddr = hao->addr; hao->addr = tmp_addr; |
a831f5bbc
|
225 |
|
b7aa0bf70
|
226 |
if (skb->tstamp.tv64 == 0) |
a831f5bbc
|
227 |
__net_timestamp(skb); |
a50feda54
|
228 |
return true; |
a831f5bbc
|
229 230 231 |
discard: kfree_skb(skb); |
a50feda54
|
232 |
return false; |
a831f5bbc
|
233 234 |
} #endif |
a50feda54
|
235 |
static const struct tlvtype_proc tlvprocdestopt_lst[] = { |
07a936260
|
236 |
#if IS_ENABLED(CONFIG_IPV6_MIP6) |
a831f5bbc
|
237 238 239 240 241 |
{ .type = IPV6_TLV_HAO, .func = ipv6_dest_hao, }, #endif |
1da177e4c
|
242 243 |
{-1, NULL} }; |
e5bbef20e
|
244 |
static int ipv6_destopt_rcv(struct sk_buff *skb) |
1da177e4c
|
245 |
{ |
1da177e4c
|
246 |
struct inet6_skb_parm *opt = IP6CB(skb); |
07a936260
|
247 |
#if IS_ENABLED(CONFIG_IPV6_MIP6) |
a831f5bbc
|
248 249 |
__u16 dstbuf; #endif |
897dc80b9
|
250 |
struct dst_entry *dst = skb_dst(skb); |
1da177e4c
|
251 |
|
ea2ae17d6
|
252 253 |
if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) || !pskb_may_pull(skb, (skb_transport_offset(skb) + |
9c70220b7
|
254 |
((skb_transport_header(skb)[1] + 1) << 3)))) { |
897dc80b9
|
255 |
IP6_INC_STATS_BH(dev_net(dst->dev), ip6_dst_idev(dst), |
a11d206d0
|
256 |
IPSTATS_MIB_INHDRERRORS); |
1da177e4c
|
257 258 259 |
kfree_skb(skb); return -1; } |
cfe1fc775
|
260 |
opt->lastopt = opt->dst1 = skb_network_header_len(skb); |
07a936260
|
261 |
#if IS_ENABLED(CONFIG_IPV6_MIP6) |
a831f5bbc
|
262 263 |
dstbuf = opt->dst1; #endif |
1da177e4c
|
264 |
|
e5bbef20e
|
265 |
if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) { |
b0e380b1d
|
266 |
skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3; |
dc435e6da
|
267 |
opt = IP6CB(skb); |
07a936260
|
268 |
#if IS_ENABLED(CONFIG_IPV6_MIP6) |
a831f5bbc
|
269 270 |
opt->nhoff = dstbuf; #else |
951dbc8ac
|
271 |
opt->nhoff = opt->dst1; |
a831f5bbc
|
272 |
#endif |
1da177e4c
|
273 274 |
return 1; } |
483a47d2f
|
275 276 |
IP6_INC_STATS_BH(dev_net(dst->dev), ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS); |
1da177e4c
|
277 278 |
return -1; } |
1da177e4c
|
279 280 281 |
/******************************** Routing header. ********************************/ |
f6bc7d9e4
|
282 |
/* called with rcu_read_lock() */ |
e5bbef20e
|
283 |
static int ipv6_rthdr_rcv(struct sk_buff *skb) |
1da177e4c
|
284 |
{ |
1da177e4c
|
285 |
struct inet6_skb_parm *opt = IP6CB(skb); |
65d4ed922
|
286 |
struct in6_addr *addr = NULL; |
1da177e4c
|
287 |
struct in6_addr daddr; |
0bcbc9262
|
288 |
struct inet6_dev *idev; |
1da177e4c
|
289 |
int n, i; |
1da177e4c
|
290 291 |
struct ipv6_rt_hdr *hdr; struct rt0_hdr *rthdr; |
483a47d2f
|
292 293 |
struct net *net = dev_net(skb->dev); int accept_source_route = net->ipv6.devconf_all->accept_source_route; |
0bcbc9262
|
294 |
|
f6bc7d9e4
|
295 296 297 |
idev = __in6_dev_get(skb->dev); if (idev && accept_source_route > idev->cnf.accept_source_route) accept_source_route = idev->cnf.accept_source_route; |
0bcbc9262
|
298 |
|
ea2ae17d6
|
299 300 |
if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) || !pskb_may_pull(skb, (skb_transport_offset(skb) + |
9c70220b7
|
301 |
((skb_transport_header(skb)[1] + 1) << 3)))) { |
adf30907d
|
302 |
IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), |
a11d206d0
|
303 |
IPSTATS_MIB_INHDRERRORS); |
1da177e4c
|
304 305 306 |
kfree_skb(skb); return -1; } |
9c70220b7
|
307 |
hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb); |
1da177e4c
|
308 |
|
0660e03f6
|
309 |
if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) || |
1da177e4c
|
310 |
skb->pkt_type != PACKET_HOST) { |
adf30907d
|
311 |
IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), |
a11d206d0
|
312 |
IPSTATS_MIB_INADDRERRORS); |
1da177e4c
|
313 314 315 316 317 318 |
kfree_skb(skb); return -1; } looped_back: if (hdr->segments_left == 0) { |
65d4ed922
|
319 |
switch (hdr->type) { |
07a936260
|
320 |
#if IS_ENABLED(CONFIG_IPV6_MIP6) |
65d4ed922
|
321 322 323 324 325 |
case IPV6_SRCRT_TYPE_2: /* Silently discard type 2 header unless it was * processed by own */ if (!addr) { |
adf30907d
|
326 |
IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), |
a11d206d0
|
327 |
IPSTATS_MIB_INADDRERRORS); |
65d4ed922
|
328 329 330 331 332 333 334 335 |
kfree_skb(skb); return -1; } break; #endif default: break; } |
cfe1fc775
|
336 |
opt->lastopt = opt->srcrt = skb_network_header_len(skb); |
b0e380b1d
|
337 |
skb->transport_header += (hdr->hdrlen + 1) << 3; |
1da177e4c
|
338 339 |
opt->dst0 = opt->dst1; opt->dst1 = 0; |
d56f90a7c
|
340 |
opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb); |
1da177e4c
|
341 342 |
return 1; } |
65d4ed922
|
343 |
switch (hdr->type) { |
07a936260
|
344 |
#if IS_ENABLED(CONFIG_IPV6_MIP6) |
65d4ed922
|
345 |
case IPV6_SRCRT_TYPE_2: |
c382bb9d3
|
346 347 |
if (accept_source_route < 0) goto unknown_rh; |
65d4ed922
|
348 349 |
/* Silently discard invalid RTH type 2 */ if (hdr->hdrlen != 2 || hdr->segments_left != 1) { |
adf30907d
|
350 |
IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), |
a11d206d0
|
351 |
IPSTATS_MIB_INHDRERRORS); |
65d4ed922
|
352 353 354 355 356 |
kfree_skb(skb); return -1; } break; #endif |
c382bb9d3
|
357 358 |
default: goto unknown_rh; |
1da177e4c
|
359 |
} |
1da177e4c
|
360 361 362 363 364 365 366 367 368 |
/* * This is the routing header forwarding algorithm from * RFC 2460, page 16. */ n = hdr->hdrlen >> 1; if (hdr->segments_left > n) { |
adf30907d
|
369 |
IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), |
a11d206d0
|
370 |
IPSTATS_MIB_INHDRERRORS); |
d56f90a7c
|
371 372 373 |
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, ((&hdr->segments_left) - skb_network_header(skb))); |
1da177e4c
|
374 375 376 377 378 379 380 |
return -1; } /* We are about to mangle packet header. Be careful! Do not damage packets queued somewhere. */ if (skb_cloned(skb)) { |
1da177e4c
|
381 |
/* the copy is a forwarded packet */ |
65c884666
|
382 |
if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { |
adf30907d
|
383 |
IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), |
a11d206d0
|
384 385 |
IPSTATS_MIB_OUTDISCARDS); kfree_skb(skb); |
1da177e4c
|
386 387 |
return -1; } |
65c884666
|
388 |
hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb); |
1da177e4c
|
389 |
} |
84fa7933a
|
390 |
if (skb->ip_summed == CHECKSUM_COMPLETE) |
1da177e4c
|
391 392 393 394 395 396 397 |
skb->ip_summed = CHECKSUM_NONE; i = n - --hdr->segments_left; rthdr = (struct rt0_hdr *) hdr; addr = rthdr->addr; addr += i - 1; |
65d4ed922
|
398 |
switch (hdr->type) { |
07a936260
|
399 |
#if IS_ENABLED(CONFIG_IPV6_MIP6) |
65d4ed922
|
400 401 |
case IPV6_SRCRT_TYPE_2: if (xfrm6_input_addr(skb, (xfrm_address_t *)addr, |
0660e03f6
|
402 |
(xfrm_address_t *)&ipv6_hdr(skb)->saddr, |
65d4ed922
|
403 |
IPPROTO_ROUTING) < 0) { |
adf30907d
|
404 |
IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), |
a11d206d0
|
405 |
IPSTATS_MIB_INADDRERRORS); |
65d4ed922
|
406 407 408 |
kfree_skb(skb); return -1; } |
adf30907d
|
409 410 |
if (!ipv6_chk_home_addr(dev_net(skb_dst(skb)->dev), addr)) { IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), |
a11d206d0
|
411 |
IPSTATS_MIB_INADDRERRORS); |
65d4ed922
|
412 413 414 415 416 417 418 419 |
kfree_skb(skb); return -1; } break; #endif default: break; } |
1da177e4c
|
420 |
if (ipv6_addr_is_multicast(addr)) { |
adf30907d
|
421 |
IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), |
a11d206d0
|
422 |
IPSTATS_MIB_INADDRERRORS); |
1da177e4c
|
423 424 425 |
kfree_skb(skb); return -1; } |
4e3fd7a06
|
426 427 428 |
daddr = *addr; *addr = ipv6_hdr(skb)->daddr; ipv6_hdr(skb)->daddr = daddr; |
1da177e4c
|
429 |
|
adf30907d
|
430 |
skb_dst_drop(skb); |
1da177e4c
|
431 |
ip6_route_input(skb); |
adf30907d
|
432 |
if (skb_dst(skb)->error) { |
d56f90a7c
|
433 |
skb_push(skb, skb->data - skb_network_header(skb)); |
1da177e4c
|
434 435 436 |
dst_input(skb); return -1; } |
adf30907d
|
437 |
if (skb_dst(skb)->dev->flags&IFF_LOOPBACK) { |
0660e03f6
|
438 |
if (ipv6_hdr(skb)->hop_limit <= 1) { |
adf30907d
|
439 |
IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), |
a11d206d0
|
440 |
IPSTATS_MIB_INHDRERRORS); |
1da177e4c
|
441 |
icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, |
3ffe533c8
|
442 |
0); |
1da177e4c
|
443 444 445 |
kfree_skb(skb); return -1; } |
0660e03f6
|
446 |
ipv6_hdr(skb)->hop_limit--; |
1da177e4c
|
447 448 |
goto looped_back; } |
d56f90a7c
|
449 |
skb_push(skb, skb->data - skb_network_header(skb)); |
1da177e4c
|
450 451 |
dst_input(skb); return -1; |
c382bb9d3
|
452 453 |
unknown_rh: |
adf30907d
|
454 |
IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_INHDRERRORS); |
c382bb9d3
|
455 456 457 |
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb_network_header(skb)); return -1; |
1da177e4c
|
458 |
} |
41135cc83
|
459 |
static const struct inet6_protocol rthdr_protocol = { |
1da177e4c
|
460 |
.handler = ipv6_rthdr_rcv, |
2207afc8b
|
461 |
.flags = INET6_PROTO_NOPOLICY, |
8ca896cfd
|
462 |
}; |
41135cc83
|
463 |
static const struct inet6_protocol destopt_protocol = { |
248b238dc
|
464 |
.handler = ipv6_destopt_rcv, |
2207afc8b
|
465 |
.flags = INET6_PROTO_NOPOLICY, |
8ca896cfd
|
466 |
}; |
41135cc83
|
467 |
static const struct inet6_protocol nodata_protocol = { |
248b238dc
|
468 469 470 471 472 |
.handler = dst_discard, .flags = INET6_PROTO_NOPOLICY, }; int __init ipv6_exthdrs_init(void) |
1da177e4c
|
473 |
{ |
248b238dc
|
474 |
int ret; |
3336288a9
|
475 476 |
ret = inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING); if (ret) |
c6b641a4c
|
477 |
goto out; |
3336288a9
|
478 |
|
248b238dc
|
479 480 481 482 483 484 485 486 487 488 |
ret = inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS); if (ret) goto out_rthdr; ret = inet6_add_protocol(&nodata_protocol, IPPROTO_NONE); if (ret) goto out_destopt; out: return ret; |
248b238dc
|
489 490 |
out_destopt: inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS); |
3336288a9
|
491 492 |
out_rthdr: inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING); |
248b238dc
|
493 |
goto out; |
1da177e4c
|
494 |
}; |
248b238dc
|
495 496 497 498 499 500 |
void ipv6_exthdrs_exit(void) { inet6_del_protocol(&nodata_protocol, IPPROTO_NONE); inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS); inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING); } |
1da177e4c
|
501 502 503 |
/********************************** Hop-by-hop options. **********************************/ |
e76b2b256
|
504 |
/* |
adf30907d
|
505 |
* Note: we cannot rely on skb_dst(skb) before we assign it in ip6_route_input(). |
e76b2b256
|
506 507 508 |
*/ static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb) { |
adf30907d
|
509 |
return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) : __in6_dev_get(skb->dev); |
e76b2b256
|
510 |
} |
2570a4f54
|
511 512 513 514 |
static inline struct net *ipv6_skb_net(struct sk_buff *skb) { return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev); } |
1da177e4c
|
515 |
/* Router Alert as of RFC 2711 */ |
a50feda54
|
516 |
static bool ipv6_hop_ra(struct sk_buff *skb, int optoff) |
1da177e4c
|
517 |
{ |
d56f90a7c
|
518 |
const unsigned char *nh = skb_network_header(skb); |
a80ff03e0
|
519 |
|
d56f90a7c
|
520 |
if (nh[optoff + 1] == 2) { |
dd3332bfc
|
521 522 |
IP6CB(skb)->flags |= IP6SKB_ROUTERALERT; memcpy(&IP6CB(skb)->ra, nh + optoff + 2, sizeof(IP6CB(skb)->ra)); |
a50feda54
|
523 |
return true; |
1da177e4c
|
524 |
} |
64ce20730
|
525 526 |
LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d ", |
d56f90a7c
|
527 |
nh[optoff + 1]); |
1da177e4c
|
528 |
kfree_skb(skb); |
a50feda54
|
529 |
return false; |
1da177e4c
|
530 531 532 |
} /* Jumbo payload */ |
a50feda54
|
533 |
static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff) |
1da177e4c
|
534 |
{ |
d56f90a7c
|
535 |
const unsigned char *nh = skb_network_header(skb); |
2570a4f54
|
536 |
struct net *net = ipv6_skb_net(skb); |
1da177e4c
|
537 |
u32 pkt_len; |
d56f90a7c
|
538 |
if (nh[optoff + 1] != 4 || (optoff & 3) != 2) { |
64ce20730
|
539 540 |
LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d ", |
d56f90a7c
|
541 |
nh[optoff+1]); |
483a47d2f
|
542 |
IP6_INC_STATS_BH(net, ipv6_skb_idev(skb), |
a11d206d0
|
543 |
IPSTATS_MIB_INHDRERRORS); |
1da177e4c
|
544 545 |
goto drop; } |
d56f90a7c
|
546 |
pkt_len = ntohl(*(__be32 *)(nh + optoff + 2)); |
1da177e4c
|
547 |
if (pkt_len <= IPV6_MAXPLEN) { |
483a47d2f
|
548 549 |
IP6_INC_STATS_BH(net, ipv6_skb_idev(skb), IPSTATS_MIB_INHDRERRORS); |
1da177e4c
|
550 |
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2); |
a50feda54
|
551 |
return false; |
1da177e4c
|
552 |
} |
0660e03f6
|
553 |
if (ipv6_hdr(skb)->payload_len) { |
483a47d2f
|
554 555 |
IP6_INC_STATS_BH(net, ipv6_skb_idev(skb), IPSTATS_MIB_INHDRERRORS); |
1da177e4c
|
556 |
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff); |
a50feda54
|
557 |
return false; |
1da177e4c
|
558 559 560 |
} if (pkt_len > skb->len - sizeof(struct ipv6hdr)) { |
483a47d2f
|
561 562 |
IP6_INC_STATS_BH(net, ipv6_skb_idev(skb), IPSTATS_MIB_INTRUNCATEDPKTS); |
1da177e4c
|
563 564 |
goto drop; } |
42ca89c18
|
565 566 567 |
if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) goto drop; |
a50feda54
|
568 |
return true; |
1da177e4c
|
569 570 571 |
drop: kfree_skb(skb); |
a50feda54
|
572 |
return false; |
1da177e4c
|
573 |
} |
a50feda54
|
574 |
static const struct tlvtype_proc tlvprochopopt_lst[] = { |
1da177e4c
|
575 576 577 578 579 580 581 582 583 584 |
{ .type = IPV6_TLV_ROUTERALERT, .func = ipv6_hop_ra, }, { .type = IPV6_TLV_JUMBO, .func = ipv6_hop_jumbo, }, { -1, } }; |
e5bbef20e
|
585 |
int ipv6_parse_hopopts(struct sk_buff *skb) |
1da177e4c
|
586 |
{ |
951dbc8ac
|
587 |
struct inet6_skb_parm *opt = IP6CB(skb); |
ec6700958
|
588 |
/* |
d56f90a7c
|
589 |
* skb_network_header(skb) is equal to skb->data, and |
cfe1fc775
|
590 |
* skb_network_header_len(skb) is always equal to |
ec6700958
|
591 592 593 594 |
* sizeof(struct ipv6hdr) by definition of * hop-by-hop options. */ if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) || |
9c70220b7
|
595 596 |
!pskb_may_pull(skb, (sizeof(struct ipv6hdr) + ((skb_transport_header(skb)[1] + 1) << 3)))) { |
ec6700958
|
597 598 599 |
kfree_skb(skb); return -1; } |
951dbc8ac
|
600 |
opt->hop = sizeof(struct ipv6hdr); |
e5bbef20e
|
601 |
if (ip6_parse_tlv(tlvprochopopt_lst, skb)) { |
b0e380b1d
|
602 |
skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3; |
dc435e6da
|
603 |
opt = IP6CB(skb); |
951dbc8ac
|
604 |
opt->nhoff = sizeof(struct ipv6hdr); |
b809739a1
|
605 |
return 1; |
951dbc8ac
|
606 |
} |
1da177e4c
|
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 |
return -1; } /* * Creating outbound headers. * * "build" functions work when skb is filled from head to tail (datagram) * "push" functions work when headers are added from tail to head (tcp) * * In both cases we assume, that caller reserved enough room * for headers. */ static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto, struct ipv6_rt_hdr *opt, struct in6_addr **addr_p) { struct rt0_hdr *phdr, *ihdr; int hops; ihdr = (struct rt0_hdr *) opt; |
1ab1457c4
|
628 |
|
1da177e4c
|
629 630 631 632 633 634 635 636 |
phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3); memcpy(phdr, ihdr, sizeof(struct rt0_hdr)); hops = ihdr->rt_hdr.hdrlen >> 1; if (hops > 1) memcpy(phdr->addr, ihdr->addr + 1, (hops - 1) * sizeof(struct in6_addr)); |
4e3fd7a06
|
637 |
phdr->addr[hops - 1] = **addr_p; |
1da177e4c
|
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 |
*addr_p = ihdr->addr; phdr->rt_hdr.nexthdr = *proto; *proto = NEXTHDR_ROUTING; } static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt) { struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt)); memcpy(h, opt, ipv6_optlen(opt)); h->nexthdr = *proto; *proto = type; } void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto, struct in6_addr **daddr) { |
333fad536
|
657 |
if (opt->srcrt) { |
1da177e4c
|
658 |
ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); |
333fad536
|
659 660 661 662 663 664 665 |
/* * IPV6_RTHDRDSTOPTS is ignored * unless IPV6_RTHDR is set (RFC3542). */ if (opt->dst0opt) ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt); } |
1da177e4c
|
666 667 668 |
if (opt->hopopt) ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); } |
7159039a1
|
669 |
EXPORT_SYMBOL(ipv6_push_nfrag_opts); |
1da177e4c
|
670 671 672 673 674 675 676 677 678 679 680 681 682 |
void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto) { if (opt->dst1opt) ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt); } struct ipv6_txoptions * ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) { struct ipv6_txoptions *opt2; opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC); if (opt2) { |
ac3c8172f
|
683 |
long dif = (char *)opt2 - (char *)opt; |
1da177e4c
|
684 685 |
memcpy(opt2, opt, opt->tot_len); if (opt2->hopopt) |
ac3c8172f
|
686 |
*((char **)&opt2->hopopt) += dif; |
1da177e4c
|
687 |
if (opt2->dst0opt) |
ac3c8172f
|
688 |
*((char **)&opt2->dst0opt) += dif; |
1da177e4c
|
689 |
if (opt2->dst1opt) |
ac3c8172f
|
690 |
*((char **)&opt2->dst1opt) += dif; |
1da177e4c
|
691 |
if (opt2->srcrt) |
ac3c8172f
|
692 |
*((char **)&opt2->srcrt) += dif; |
1da177e4c
|
693 694 695 |
} return opt2; } |
3cf3dc6c2
|
696 |
EXPORT_SYMBOL_GPL(ipv6_dup_options); |
333fad536
|
697 698 699 700 701 702 703 704 705 706 |
static int ipv6_renew_option(void *ohdr, struct ipv6_opt_hdr __user *newopt, int newoptlen, int inherit, struct ipv6_opt_hdr **hdr, char **p) { if (inherit) { if (ohdr) { memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr)); *hdr = (struct ipv6_opt_hdr *)*p; |
e3192690a
|
707 |
*p += CMSG_ALIGN(ipv6_optlen(*hdr)); |
333fad536
|
708 709 710 711 712 713 |
} } else { if (newopt) { if (copy_from_user(*p, newopt, newoptlen)) return -EFAULT; *hdr = (struct ipv6_opt_hdr *)*p; |
e3192690a
|
714 |
if (ipv6_optlen(*hdr) > newoptlen) |
333fad536
|
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 |
return -EINVAL; *p += CMSG_ALIGN(newoptlen); } } return 0; } struct ipv6_txoptions * ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, int newtype, struct ipv6_opt_hdr __user *newopt, int newoptlen) { int tot_len = 0; char *p; struct ipv6_txoptions *opt2; int err; |
99c7bc013
|
731 732 733 734 735 736 737 738 739 740 |
if (opt) { if (newtype != IPV6_HOPOPTS && opt->hopopt) tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt)); if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt) tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt)); if (newtype != IPV6_RTHDR && opt->srcrt) tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt)); if (newtype != IPV6_DSTOPTS && opt->dst1opt) tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt)); } |
333fad536
|
741 742 743 744 745 |
if (newopt && newoptlen) tot_len += CMSG_ALIGN(newoptlen); if (!tot_len) return NULL; |
8b8aa4b5a
|
746 |
tot_len += sizeof(*opt2); |
333fad536
|
747 748 749 750 751 752 753 754 |
opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC); if (!opt2) return ERR_PTR(-ENOBUFS); memset(opt2, 0, tot_len); opt2->tot_len = tot_len; p = (char *)(opt2 + 1); |
99c7bc013
|
755 |
err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen, |
333fad536
|
756 757 758 759 |
newtype != IPV6_HOPOPTS, &opt2->hopopt, &p); if (err) goto out; |
99c7bc013
|
760 |
err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen, |
333fad536
|
761 762 763 764 |
newtype != IPV6_RTHDRDSTOPTS, &opt2->dst0opt, &p); if (err) goto out; |
99c7bc013
|
765 |
err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen, |
333fad536
|
766 |
newtype != IPV6_RTHDR, |
99c7bc013
|
767 |
(struct ipv6_opt_hdr **)&opt2->srcrt, &p); |
333fad536
|
768 769 |
if (err) goto out; |
99c7bc013
|
770 |
err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen, |
333fad536
|
771 772 773 774 775 776 777 778 779 780 781 782 |
newtype != IPV6_DSTOPTS, &opt2->dst1opt, &p); if (err) goto out; opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) + (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) + (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0); opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0); return opt2; out: |
8b8aa4b5a
|
783 |
sock_kfree_s(sk, opt2, opt2->tot_len); |
333fad536
|
784 785 |
return ERR_PTR(err); } |
df9890c31
|
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 |
struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, struct ipv6_txoptions *opt) { /* * ignore the dest before srcrt unless srcrt is being included. * --yoshfuji */ if (opt && opt->dst0opt && !opt->srcrt) { if (opt_space != opt) { memcpy(opt_space, opt, sizeof(*opt_space)); opt = opt_space; } opt->opt_nflen -= ipv6_optlen(opt->dst0opt); opt->dst0opt = NULL; } return opt; } |
a495f8364
|
804 |
EXPORT_SYMBOL_GPL(ipv6_fixup_options); |
df9890c31
|
805 |
|
20c59de2e
|
806 807 808 809 |
/** * fl6_update_dst - update flowi destination address with info given * by srcrt option, if any. * |
4c9483b2f
|
810 |
* @fl6: flowi6 for which daddr is to be updated |
20c59de2e
|
811 |
* @opt: struct ipv6_txoptions in which to look for srcrt opt |
4c9483b2f
|
812 |
* @orig: copy of original daddr address if modified |
20c59de2e
|
813 814 |
* * Returns NULL if no txoptions or no srcrt, otherwise returns orig |
4c9483b2f
|
815 |
* and initial value of fl6->daddr set in orig |
20c59de2e
|
816 |
*/ |
4c9483b2f
|
817 |
struct in6_addr *fl6_update_dst(struct flowi6 *fl6, |
20c59de2e
|
818 819 820 821 822 |
const struct ipv6_txoptions *opt, struct in6_addr *orig) { if (!opt || !opt->srcrt) return NULL; |
4e3fd7a06
|
823 824 |
*orig = fl6->daddr; fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr; |
20c59de2e
|
825 826 |
return orig; } |
20c59de2e
|
827 |
EXPORT_SYMBOL_GPL(fl6_update_dst); |