diff options
Diffstat (limited to 'net/mpls/af_mpls.c')
-rw-r--r-- | net/mpls/af_mpls.c | 197 |
1 files changed, 154 insertions, 43 deletions
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 1f93a5978f2a..8c5707db53c5 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c | |||
@@ -15,6 +15,10 @@ | |||
15 | #include <net/ip_fib.h> | 15 | #include <net/ip_fib.h> |
16 | #include <net/netevent.h> | 16 | #include <net/netevent.h> |
17 | #include <net/netns/generic.h> | 17 | #include <net/netns/generic.h> |
18 | #if IS_ENABLED(CONFIG_IPV6) | ||
19 | #include <net/ipv6.h> | ||
20 | #include <net/addrconf.h> | ||
21 | #endif | ||
18 | #include "internal.h" | 22 | #include "internal.h" |
19 | 23 | ||
20 | #define LABEL_NOT_SPECIFIED (1<<20) | 24 | #define LABEL_NOT_SPECIFIED (1<<20) |
@@ -23,11 +27,23 @@ | |||
23 | /* This maximum ha length copied from the definition of struct neighbour */ | 27 | /* This maximum ha length copied from the definition of struct neighbour */ |
24 | #define MAX_VIA_ALEN (ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))) | 28 | #define MAX_VIA_ALEN (ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))) |
25 | 29 | ||
30 | enum mpls_payload_type { | ||
31 | MPT_UNSPEC, /* IPv4 or IPv6 */ | ||
32 | MPT_IPV4 = 4, | ||
33 | MPT_IPV6 = 6, | ||
34 | |||
35 | /* Other types not implemented: | ||
36 | * - Pseudo-wire with or without control word (RFC4385) | ||
37 | * - GAL (RFC5586) | ||
38 | */ | ||
39 | }; | ||
40 | |||
26 | struct mpls_route { /* next hop label forwarding entry */ | 41 | struct mpls_route { /* next hop label forwarding entry */ |
27 | struct net_device __rcu *rt_dev; | 42 | struct net_device __rcu *rt_dev; |
28 | struct rcu_head rt_rcu; | 43 | struct rcu_head rt_rcu; |
29 | u32 rt_label[MAX_NEW_LABELS]; | 44 | u32 rt_label[MAX_NEW_LABELS]; |
30 | u8 rt_protocol; /* routing protocol that set this entry */ | 45 | u8 rt_protocol; /* routing protocol that set this entry */ |
46 | u8 rt_payload_type; | ||
31 | u8 rt_labels; | 47 | u8 rt_labels; |
32 | u8 rt_via_alen; | 48 | u8 rt_via_alen; |
33 | u8 rt_via_table; | 49 | u8 rt_via_table; |
@@ -58,10 +74,11 @@ static inline struct mpls_dev *mpls_dev_get(const struct net_device *dev) | |||
58 | return rcu_dereference_rtnl(dev->mpls_ptr); | 74 | return rcu_dereference_rtnl(dev->mpls_ptr); |
59 | } | 75 | } |
60 | 76 | ||
61 | static bool mpls_output_possible(const struct net_device *dev) | 77 | bool mpls_output_possible(const struct net_device *dev) |
62 | { | 78 | { |
63 | return dev && (dev->flags & IFF_UP) && netif_carrier_ok(dev); | 79 | return dev && (dev->flags & IFF_UP) && netif_carrier_ok(dev); |
64 | } | 80 | } |
81 | EXPORT_SYMBOL_GPL(mpls_output_possible); | ||
65 | 82 | ||
66 | static unsigned int mpls_rt_header_size(const struct mpls_route *rt) | 83 | static unsigned int mpls_rt_header_size(const struct mpls_route *rt) |
67 | { | 84 | { |
@@ -69,13 +86,14 @@ static unsigned int mpls_rt_header_size(const struct mpls_route *rt) | |||
69 | return rt->rt_labels * sizeof(struct mpls_shim_hdr); | 86 | return rt->rt_labels * sizeof(struct mpls_shim_hdr); |
70 | } | 87 | } |
71 | 88 | ||
72 | static unsigned int mpls_dev_mtu(const struct net_device *dev) | 89 | unsigned int mpls_dev_mtu(const struct net_device *dev) |
73 | { | 90 | { |
74 | /* The amount of data the layer 2 frame can hold */ | 91 | /* The amount of data the layer 2 frame can hold */ |
75 | return dev->mtu; | 92 | return dev->mtu; |
76 | } | 93 | } |
94 | EXPORT_SYMBOL_GPL(mpls_dev_mtu); | ||
77 | 95 | ||
78 | static bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu) | 96 | bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu) |
79 | { | 97 | { |
80 | if (skb->len <= mtu) | 98 | if (skb->len <= mtu) |
81 | return false; | 99 | return false; |
@@ -85,20 +103,13 @@ static bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu) | |||
85 | 103 | ||
86 | return true; | 104 | return true; |
87 | } | 105 | } |
106 | EXPORT_SYMBOL_GPL(mpls_pkt_too_big); | ||
88 | 107 | ||
89 | static bool mpls_egress(struct mpls_route *rt, struct sk_buff *skb, | 108 | static bool mpls_egress(struct mpls_route *rt, struct sk_buff *skb, |
90 | struct mpls_entry_decoded dec) | 109 | struct mpls_entry_decoded dec) |
91 | { | 110 | { |
92 | /* RFC4385 and RFC5586 encode other packets in mpls such that | 111 | enum mpls_payload_type payload_type; |
93 | * they don't conflict with the ip version number, making | 112 | bool success = false; |
94 | * decoding by examining the ip version correct in everything | ||
95 | * except for the strangest cases. | ||
96 | * | ||
97 | * The strange cases if we choose to support them will require | ||
98 | * manual configuration. | ||
99 | */ | ||
100 | struct iphdr *hdr4; | ||
101 | bool success = true; | ||
102 | 113 | ||
103 | /* The IPv4 code below accesses through the IPv4 header | 114 | /* The IPv4 code below accesses through the IPv4 header |
104 | * checksum, which is 12 bytes into the packet. | 115 | * checksum, which is 12 bytes into the packet. |
@@ -113,23 +124,32 @@ static bool mpls_egress(struct mpls_route *rt, struct sk_buff *skb, | |||
113 | if (!pskb_may_pull(skb, 12)) | 124 | if (!pskb_may_pull(skb, 12)) |
114 | return false; | 125 | return false; |
115 | 126 | ||
116 | /* Use ip_hdr to find the ip protocol version */ | 127 | payload_type = rt->rt_payload_type; |
117 | hdr4 = ip_hdr(skb); | 128 | if (payload_type == MPT_UNSPEC) |
118 | if (hdr4->version == 4) { | 129 | payload_type = ip_hdr(skb)->version; |
130 | |||
131 | switch (payload_type) { | ||
132 | case MPT_IPV4: { | ||
133 | struct iphdr *hdr4 = ip_hdr(skb); | ||
119 | skb->protocol = htons(ETH_P_IP); | 134 | skb->protocol = htons(ETH_P_IP); |
120 | csum_replace2(&hdr4->check, | 135 | csum_replace2(&hdr4->check, |
121 | htons(hdr4->ttl << 8), | 136 | htons(hdr4->ttl << 8), |
122 | htons(dec.ttl << 8)); | 137 | htons(dec.ttl << 8)); |
123 | hdr4->ttl = dec.ttl; | 138 | hdr4->ttl = dec.ttl; |
139 | success = true; | ||
140 | break; | ||
124 | } | 141 | } |
125 | else if (hdr4->version == 6) { | 142 | case MPT_IPV6: { |
126 | struct ipv6hdr *hdr6 = ipv6_hdr(skb); | 143 | struct ipv6hdr *hdr6 = ipv6_hdr(skb); |
127 | skb->protocol = htons(ETH_P_IPV6); | 144 | skb->protocol = htons(ETH_P_IPV6); |
128 | hdr6->hop_limit = dec.ttl; | 145 | hdr6->hop_limit = dec.ttl; |
146 | success = true; | ||
147 | break; | ||
148 | } | ||
149 | case MPT_UNSPEC: | ||
150 | break; | ||
129 | } | 151 | } |
130 | else | 152 | |
131 | /* version 0 and version 1 are used by pseudo wires */ | ||
132 | success = false; | ||
133 | return success; | 153 | return success; |
134 | } | 154 | } |
135 | 155 | ||
@@ -248,16 +268,17 @@ static const struct nla_policy rtm_mpls_policy[RTA_MAX+1] = { | |||
248 | }; | 268 | }; |
249 | 269 | ||
250 | struct mpls_route_config { | 270 | struct mpls_route_config { |
251 | u32 rc_protocol; | 271 | u32 rc_protocol; |
252 | u32 rc_ifindex; | 272 | u32 rc_ifindex; |
253 | u16 rc_via_table; | 273 | u16 rc_via_table; |
254 | u16 rc_via_alen; | 274 | u16 rc_via_alen; |
255 | u8 rc_via[MAX_VIA_ALEN]; | 275 | u8 rc_via[MAX_VIA_ALEN]; |
256 | u32 rc_label; | 276 | u32 rc_label; |
257 | u32 rc_output_labels; | 277 | u32 rc_output_labels; |
258 | u32 rc_output_label[MAX_NEW_LABELS]; | 278 | u32 rc_output_label[MAX_NEW_LABELS]; |
259 | u32 rc_nlflags; | 279 | u32 rc_nlflags; |
260 | struct nl_info rc_nlinfo; | 280 | enum mpls_payload_type rc_payload_type; |
281 | struct nl_info rc_nlinfo; | ||
261 | }; | 282 | }; |
262 | 283 | ||
263 | static struct mpls_route *mpls_rt_alloc(size_t alen) | 284 | static struct mpls_route *mpls_rt_alloc(size_t alen) |
@@ -286,7 +307,7 @@ static void mpls_notify_route(struct net *net, unsigned index, | |||
286 | struct mpls_route *rt = new ? new : old; | 307 | struct mpls_route *rt = new ? new : old; |
287 | unsigned nlm_flags = (old && new) ? NLM_F_REPLACE : 0; | 308 | unsigned nlm_flags = (old && new) ? NLM_F_REPLACE : 0; |
288 | /* Ignore reserved labels for now */ | 309 | /* Ignore reserved labels for now */ |
289 | if (rt && (index >= 16)) | 310 | if (rt && (index >= MPLS_LABEL_FIRST_UNRESERVED)) |
290 | rtmsg_lfib(event, index, rt, nlh, net, portid, nlm_flags); | 311 | rtmsg_lfib(event, index, rt, nlh, net, portid, nlm_flags); |
291 | } | 312 | } |
292 | 313 | ||
@@ -320,13 +341,96 @@ static unsigned find_free_label(struct net *net) | |||
320 | 341 | ||
321 | platform_label = rtnl_dereference(net->mpls.platform_label); | 342 | platform_label = rtnl_dereference(net->mpls.platform_label); |
322 | platform_labels = net->mpls.platform_labels; | 343 | platform_labels = net->mpls.platform_labels; |
323 | for (index = 16; index < platform_labels; index++) { | 344 | for (index = MPLS_LABEL_FIRST_UNRESERVED; index < platform_labels; |
345 | index++) { | ||
324 | if (!rtnl_dereference(platform_label[index])) | 346 | if (!rtnl_dereference(platform_label[index])) |
325 | return index; | 347 | return index; |
326 | } | 348 | } |
327 | return LABEL_NOT_SPECIFIED; | 349 | return LABEL_NOT_SPECIFIED; |
328 | } | 350 | } |
329 | 351 | ||
352 | #if IS_ENABLED(CONFIG_INET) | ||
353 | static struct net_device *inet_fib_lookup_dev(struct net *net, void *addr) | ||
354 | { | ||
355 | struct net_device *dev; | ||
356 | struct rtable *rt; | ||
357 | struct in_addr daddr; | ||
358 | |||
359 | memcpy(&daddr, addr, sizeof(struct in_addr)); | ||
360 | rt = ip_route_output(net, daddr.s_addr, 0, 0, 0); | ||
361 | if (IS_ERR(rt)) | ||
362 | return ERR_CAST(rt); | ||
363 | |||
364 | dev = rt->dst.dev; | ||
365 | dev_hold(dev); | ||
366 | |||
367 | ip_rt_put(rt); | ||
368 | |||
369 | return dev; | ||
370 | } | ||
371 | #else | ||
372 | static struct net_device *inet_fib_lookup_dev(struct net *net, void *addr) | ||
373 | { | ||
374 | return ERR_PTR(-EAFNOSUPPORT); | ||
375 | } | ||
376 | #endif | ||
377 | |||
378 | #if IS_ENABLED(CONFIG_IPV6) | ||
379 | static struct net_device *inet6_fib_lookup_dev(struct net *net, void *addr) | ||
380 | { | ||
381 | struct net_device *dev; | ||
382 | struct dst_entry *dst; | ||
383 | struct flowi6 fl6; | ||
384 | int err; | ||
385 | |||
386 | if (!ipv6_stub) | ||
387 | return ERR_PTR(-EAFNOSUPPORT); | ||
388 | |||
389 | memset(&fl6, 0, sizeof(fl6)); | ||
390 | memcpy(&fl6.daddr, addr, sizeof(struct in6_addr)); | ||
391 | err = ipv6_stub->ipv6_dst_lookup(net, NULL, &dst, &fl6); | ||
392 | if (err) | ||
393 | return ERR_PTR(err); | ||
394 | |||
395 | dev = dst->dev; | ||
396 | dev_hold(dev); | ||
397 | dst_release(dst); | ||
398 | |||
399 | return dev; | ||
400 | } | ||
401 | #else | ||
402 | static struct net_device *inet6_fib_lookup_dev(struct net *net, void *addr) | ||
403 | { | ||
404 | return ERR_PTR(-EAFNOSUPPORT); | ||
405 | } | ||
406 | #endif | ||
407 | |||
408 | static struct net_device *find_outdev(struct net *net, | ||
409 | struct mpls_route_config *cfg) | ||
410 | { | ||
411 | struct net_device *dev = NULL; | ||
412 | |||
413 | if (!cfg->rc_ifindex) { | ||
414 | switch (cfg->rc_via_table) { | ||
415 | case NEIGH_ARP_TABLE: | ||
416 | dev = inet_fib_lookup_dev(net, cfg->rc_via); | ||
417 | break; | ||
418 | case NEIGH_ND_TABLE: | ||
419 | dev = inet6_fib_lookup_dev(net, cfg->rc_via); | ||
420 | break; | ||
421 | case NEIGH_LINK_TABLE: | ||
422 | break; | ||
423 | } | ||
424 | } else { | ||
425 | dev = dev_get_by_index(net, cfg->rc_ifindex); | ||
426 | } | ||
427 | |||
428 | if (!dev) | ||
429 | return ERR_PTR(-ENODEV); | ||
430 | |||
431 | return dev; | ||
432 | } | ||
433 | |||
330 | static int mpls_route_add(struct mpls_route_config *cfg) | 434 | static int mpls_route_add(struct mpls_route_config *cfg) |
331 | { | 435 | { |
332 | struct mpls_route __rcu **platform_label; | 436 | struct mpls_route __rcu **platform_label; |
@@ -345,8 +449,8 @@ static int mpls_route_add(struct mpls_route_config *cfg) | |||
345 | index = find_free_label(net); | 449 | index = find_free_label(net); |
346 | } | 450 | } |
347 | 451 | ||
348 | /* The first 16 labels are reserved, and may not be set */ | 452 | /* Reserved labels may not be set */ |
349 | if (index < 16) | 453 | if (index < MPLS_LABEL_FIRST_UNRESERVED) |
350 | goto errout; | 454 | goto errout; |
351 | 455 | ||
352 | /* The full 20 bit range may not be supported. */ | 456 | /* The full 20 bit range may not be supported. */ |
@@ -357,10 +461,12 @@ static int mpls_route_add(struct mpls_route_config *cfg) | |||
357 | if (cfg->rc_output_labels > MAX_NEW_LABELS) | 461 | if (cfg->rc_output_labels > MAX_NEW_LABELS) |
358 | goto errout; | 462 | goto errout; |
359 | 463 | ||
360 | err = -ENODEV; | 464 | dev = find_outdev(net, cfg); |
361 | dev = dev_get_by_index(net, cfg->rc_ifindex); | 465 | if (IS_ERR(dev)) { |
362 | if (!dev) | 466 | err = PTR_ERR(dev); |
467 | dev = NULL; | ||
363 | goto errout; | 468 | goto errout; |
469 | } | ||
364 | 470 | ||
365 | /* Ensure this is a supported device */ | 471 | /* Ensure this is a supported device */ |
366 | err = -EINVAL; | 472 | err = -EINVAL; |
@@ -401,6 +507,7 @@ static int mpls_route_add(struct mpls_route_config *cfg) | |||
401 | rt->rt_label[i] = cfg->rc_output_label[i]; | 507 | rt->rt_label[i] = cfg->rc_output_label[i]; |
402 | rt->rt_protocol = cfg->rc_protocol; | 508 | rt->rt_protocol = cfg->rc_protocol; |
403 | RCU_INIT_POINTER(rt->rt_dev, dev); | 509 | RCU_INIT_POINTER(rt->rt_dev, dev); |
510 | rt->rt_payload_type = cfg->rc_payload_type; | ||
404 | rt->rt_via_table = cfg->rc_via_table; | 511 | rt->rt_via_table = cfg->rc_via_table; |
405 | memcpy(rt->rt_via, cfg->rc_via, cfg->rc_via_alen); | 512 | memcpy(rt->rt_via, cfg->rc_via, cfg->rc_via_alen); |
406 | 513 | ||
@@ -423,8 +530,8 @@ static int mpls_route_del(struct mpls_route_config *cfg) | |||
423 | 530 | ||
424 | index = cfg->rc_label; | 531 | index = cfg->rc_label; |
425 | 532 | ||
426 | /* The first 16 labels are reserved, and may not be removed */ | 533 | /* Reserved labels may not be removed */ |
427 | if (index < 16) | 534 | if (index < MPLS_LABEL_FIRST_UNRESERVED) |
428 | goto errout; | 535 | goto errout; |
429 | 536 | ||
430 | /* The full 20 bit range may not be supported */ | 537 | /* The full 20 bit range may not be supported */ |
@@ -626,6 +733,7 @@ int nla_put_labels(struct sk_buff *skb, int attrtype, | |||
626 | 733 | ||
627 | return 0; | 734 | return 0; |
628 | } | 735 | } |
736 | EXPORT_SYMBOL_GPL(nla_put_labels); | ||
629 | 737 | ||
630 | int nla_get_labels(const struct nlattr *nla, | 738 | int nla_get_labels(const struct nlattr *nla, |
631 | u32 max_labels, u32 *labels, u32 label[]) | 739 | u32 max_labels, u32 *labels, u32 label[]) |
@@ -671,6 +779,7 @@ int nla_get_labels(const struct nlattr *nla, | |||
671 | *labels = nla_labels; | 779 | *labels = nla_labels; |
672 | return 0; | 780 | return 0; |
673 | } | 781 | } |
782 | EXPORT_SYMBOL_GPL(nla_get_labels); | ||
674 | 783 | ||
675 | static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, | 784 | static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, |
676 | struct mpls_route_config *cfg) | 785 | struct mpls_route_config *cfg) |
@@ -740,8 +849,8 @@ static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
740 | &cfg->rc_label)) | 849 | &cfg->rc_label)) |
741 | goto errout; | 850 | goto errout; |
742 | 851 | ||
743 | /* The first 16 labels are reserved, and may not be set */ | 852 | /* Reserved labels may not be set */ |
744 | if (cfg->rc_label < 16) | 853 | if (cfg->rc_label < MPLS_LABEL_FIRST_UNRESERVED) |
745 | goto errout; | 854 | goto errout; |
746 | 855 | ||
747 | break; | 856 | break; |
@@ -866,8 +975,8 @@ static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb) | |||
866 | ASSERT_RTNL(); | 975 | ASSERT_RTNL(); |
867 | 976 | ||
868 | index = cb->args[0]; | 977 | index = cb->args[0]; |
869 | if (index < 16) | 978 | if (index < MPLS_LABEL_FIRST_UNRESERVED) |
870 | index = 16; | 979 | index = MPLS_LABEL_FIRST_UNRESERVED; |
871 | 980 | ||
872 | platform_label = rtnl_dereference(net->mpls.platform_label); | 981 | platform_label = rtnl_dereference(net->mpls.platform_label); |
873 | platform_labels = net->mpls.platform_labels; | 982 | platform_labels = net->mpls.platform_labels; |
@@ -953,6 +1062,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) | |||
953 | goto nort0; | 1062 | goto nort0; |
954 | RCU_INIT_POINTER(rt0->rt_dev, lo); | 1063 | RCU_INIT_POINTER(rt0->rt_dev, lo); |
955 | rt0->rt_protocol = RTPROT_KERNEL; | 1064 | rt0->rt_protocol = RTPROT_KERNEL; |
1065 | rt0->rt_payload_type = MPT_IPV4; | ||
956 | rt0->rt_via_table = NEIGH_LINK_TABLE; | 1066 | rt0->rt_via_table = NEIGH_LINK_TABLE; |
957 | memcpy(rt0->rt_via, lo->dev_addr, lo->addr_len); | 1067 | memcpy(rt0->rt_via, lo->dev_addr, lo->addr_len); |
958 | } | 1068 | } |
@@ -963,6 +1073,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) | |||
963 | goto nort2; | 1073 | goto nort2; |
964 | RCU_INIT_POINTER(rt2->rt_dev, lo); | 1074 | RCU_INIT_POINTER(rt2->rt_dev, lo); |
965 | rt2->rt_protocol = RTPROT_KERNEL; | 1075 | rt2->rt_protocol = RTPROT_KERNEL; |
1076 | rt2->rt_payload_type = MPT_IPV6; | ||
966 | rt2->rt_via_table = NEIGH_LINK_TABLE; | 1077 | rt2->rt_via_table = NEIGH_LINK_TABLE; |
967 | memcpy(rt2->rt_via, lo->dev_addr, lo->addr_len); | 1078 | memcpy(rt2->rt_via, lo->dev_addr, lo->addr_len); |
968 | } | 1079 | } |