Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: ipv6: add support for rpl sr exthdr

This patch adds rpl source routing receive handling. Everything works
only if sysconf "rpl_seg_enabled" and source routing is enabled. Mostly
the same behaviour as IPv6 segmentation routing. To handle compression
and uncompression a rpl.c file is created which contains the necessary
functionality. The receive handling will also care about IPv6
encapsulated so far it's specified as possible nexthdr in RFC 6554.

Signed-off-by: Alexander Aring <alex.aring@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Alexander Aring and committed by
David S. Miller
8610c7c6 f37c6059

+370 -3
+1
include/linux/ipv6.h
··· 74 74 __u32 addr_gen_mode; 75 75 __s32 disable_policy; 76 76 __s32 ndisc_tclass; 77 + __s32 rpl_seg_enabled; 77 78 78 79 struct ctl_table_header *sysctl_header; 79 80 };
+34
include/net/rpl.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + /* 3 + * RPL implementation 4 + * 5 + * Author: 6 + * (C) 2020 Alexander Aring <alex.aring@gmail.com> 7 + */ 8 + 9 + #ifndef _NET_RPL_H 10 + #define _NET_RPL_H 11 + 12 + #include <linux/rpl.h> 13 + 14 + /* Worst decompression memory usage ipv6 address (16) + pad 7 */ 15 + #define IPV6_RPL_SRH_WORST_SWAP_SIZE (sizeof(struct in6_addr) + 7) 16 + 17 + static inline size_t ipv6_rpl_srh_alloc_size(unsigned char n) 18 + { 19 + return sizeof(struct ipv6_rpl_sr_hdr) + 20 + ((n + 1) * sizeof(struct in6_addr)); 21 + } 22 + 23 + size_t ipv6_rpl_srh_size(unsigned char n, unsigned char cmpri, 24 + unsigned char cmpre); 25 + 26 + void ipv6_rpl_srh_decompress(struct ipv6_rpl_sr_hdr *outhdr, 27 + const struct ipv6_rpl_sr_hdr *inhdr, 28 + const struct in6_addr *daddr, unsigned char n); 29 + 30 + void ipv6_rpl_srh_compress(struct ipv6_rpl_sr_hdr *outhdr, 31 + const struct ipv6_rpl_sr_hdr *inhdr, 32 + const struct in6_addr *daddr, unsigned char n); 33 + 34 + #endif /* _NET_RPL_H */
+2
include/uapi/linux/ipv6.h
··· 40 40 #define IPV6_SRCRT_STRICT 0x01 /* Deprecated; will be removed */ 41 41 #define IPV6_SRCRT_TYPE_0 0 /* Deprecated; will be removed */ 42 42 #define IPV6_SRCRT_TYPE_2 2 /* IPv6 type 2 Routing Header */ 43 + #define IPV6_SRCRT_TYPE_3 3 /* RPL Segment Routing with IPv6 */ 43 44 #define IPV6_SRCRT_TYPE_4 4 /* Segment Routing with IPv6 */ 44 45 45 46 /* ··· 188 187 DEVCONF_DISABLE_POLICY, 189 188 DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, 190 189 DEVCONF_NDISC_TCLASS, 190 + DEVCONF_RPL_SEG_ENABLED, 191 191 DEVCONF_MAX 192 192 }; 193 193
+1 -1
net/ipv6/Makefile
··· 10 10 route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ 11 11 raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \ 12 12 exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \ 13 - udp_offload.o seg6.o fib6_notifier.o 13 + udp_offload.o seg6.o fib6_notifier.o rpl.o 14 14 15 15 ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o 16 16
+10
net/ipv6/addrconf.c
··· 236 236 .enhanced_dad = 1, 237 237 .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, 238 238 .disable_policy = 0, 239 + .rpl_seg_enabled = 0, 239 240 }; 240 241 241 242 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { ··· 291 290 .enhanced_dad = 1, 292 291 .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, 293 292 .disable_policy = 0, 293 + .rpl_seg_enabled = 0, 294 294 }; 295 295 296 296 /* Check if link is ready: is it up and is a valid qdisc available */ ··· 5522 5520 array[DEVCONF_ADDR_GEN_MODE] = cnf->addr_gen_mode; 5523 5521 array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy; 5524 5522 array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass; 5523 + array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled; 5525 5524 } 5526 5525 5527 5526 static inline size_t inet6_ifla6_size(void) ··· 6902 6899 .proc_handler = proc_dointvec_minmax, 6903 6900 .extra1 = (void *)SYSCTL_ZERO, 6904 6901 .extra2 = (void *)&two_five_five, 6902 + }, 6903 + { 6904 + .procname = "rpl_seg_enabled", 6905 + .data = &ipv6_devconf.rpl_seg_enabled, 6906 + .maxlen = sizeof(int), 6907 + .mode = 0644, 6908 + .proc_handler = proc_dointvec, 6905 6909 }, 6906 6910 { 6907 6911 /* sentinel */
+199 -2
net/ipv6/exthdrs.c
··· 48 48 #ifdef CONFIG_IPV6_SEG6_HMAC 49 49 #include <net/seg6_hmac.h> 50 50 #endif 51 + #include <net/rpl.h> 51 52 52 53 #include <linux/uaccess.h> 53 54 ··· 469 468 return -1; 470 469 } 471 470 471 + static int ipv6_rpl_srh_rcv(struct sk_buff *skb) 472 + { 473 + struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr; 474 + struct inet6_skb_parm *opt = IP6CB(skb); 475 + struct net *net = dev_net(skb->dev); 476 + struct inet6_dev *idev; 477 + struct ipv6hdr *oldhdr; 478 + struct in6_addr addr; 479 + unsigned char *buf; 480 + int accept_rpl_seg; 481 + int i, err; 482 + u64 n = 0; 483 + u32 r; 484 + 485 + idev = __in6_dev_get(skb->dev); 486 + 487 + accept_rpl_seg = net->ipv6.devconf_all->rpl_seg_enabled; 488 + if (accept_rpl_seg > idev->cnf.rpl_seg_enabled) 489 + accept_rpl_seg = idev->cnf.rpl_seg_enabled; 490 + 491 + if (!accept_rpl_seg) { 492 + kfree_skb(skb); 493 + return -1; 494 + } 495 + 496 + looped_back: 497 + hdr = (struct ipv6_rpl_sr_hdr *)skb_transport_header(skb); 498 + 499 + if (hdr->segments_left == 0) { 500 + if (hdr->nexthdr == NEXTHDR_IPV6) { 501 + int offset = (hdr->hdrlen + 1) << 3; 502 + 503 + skb_postpull_rcsum(skb, skb_network_header(skb), 504 + skb_network_header_len(skb)); 505 + 506 + if (!pskb_pull(skb, offset)) { 507 + kfree_skb(skb); 508 + return -1; 509 + } 510 + skb_postpull_rcsum(skb, skb_transport_header(skb), 511 + offset); 512 + 513 + skb_reset_network_header(skb); 514 + skb_reset_transport_header(skb); 515 + skb->encapsulation = 0; 516 + 517 + __skb_tunnel_rx(skb, skb->dev, net); 518 + 519 + netif_rx(skb); 520 + return -1; 521 + } 522 + 523 + opt->srcrt = skb_network_header_len(skb); 524 + opt->lastopt = opt->srcrt; 525 + skb->transport_header += (hdr->hdrlen + 1) << 3; 526 + opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb); 527 + 528 + return 1; 529 + } 530 + 531 + if (!pskb_may_pull(skb, sizeof(*hdr))) { 532 + kfree_skb(skb); 533 + return -1; 534 + } 535 + 536 + n = (hdr->hdrlen << 3) - hdr->pad - (16 - hdr->cmpre); 537 + r = do_div(n, (16 - hdr->cmpri)); 538 + /* checks if calculation was without remainder and n fits into 539 + * unsigned char which is segments_left field. Should not be 540 + * higher than that. 541 + */ 542 + if (r || (n + 1) > 255) { 543 + kfree_skb(skb); 544 + return -1; 545 + } 546 + 547 + if (hdr->segments_left > n + 1) { 548 + __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 549 + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 550 + ((&hdr->segments_left) - 551 + skb_network_header(skb))); 552 + return -1; 553 + } 554 + 555 + if (skb_cloned(skb)) { 556 + if (pskb_expand_head(skb, IPV6_RPL_SRH_WORST_SWAP_SIZE, 0, 557 + GFP_ATOMIC)) { 558 + __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), 559 + IPSTATS_MIB_OUTDISCARDS); 560 + kfree_skb(skb); 561 + return -1; 562 + } 563 + } else { 564 + err = skb_cow_head(skb, IPV6_RPL_SRH_WORST_SWAP_SIZE); 565 + if (unlikely(err)) { 566 + kfree_skb(skb); 567 + return -1; 568 + } 569 + } 570 + 571 + hdr = (struct ipv6_rpl_sr_hdr *)skb_transport_header(skb); 572 + 573 + if (!pskb_may_pull(skb, ipv6_rpl_srh_size(n, hdr->cmpri, 574 + hdr->cmpre))) { 575 + kfree_skb(skb); 576 + return -1; 577 + } 578 + 579 + hdr->segments_left--; 580 + i = n - hdr->segments_left; 581 + 582 + buf = kzalloc(ipv6_rpl_srh_alloc_size(n + 1) * 2, GFP_ATOMIC); 583 + if (unlikely(!buf)) { 584 + kfree_skb(skb); 585 + return -1; 586 + } 587 + 588 + ohdr = (struct ipv6_rpl_sr_hdr *)buf; 589 + ipv6_rpl_srh_decompress(ohdr, hdr, &ipv6_hdr(skb)->daddr, n); 590 + chdr = (struct ipv6_rpl_sr_hdr *)(buf + ((ohdr->hdrlen + 1) << 3)); 591 + 592 + if ((ipv6_addr_type(&ipv6_hdr(skb)->daddr) & IPV6_ADDR_MULTICAST) || 593 + (ipv6_addr_type(&ohdr->rpl_segaddr[i]) & IPV6_ADDR_MULTICAST)) { 594 + kfree_skb(skb); 595 + kfree(buf); 596 + return -1; 597 + } 598 + 599 + err = ipv6_chk_rpl_srh_loop(net, ohdr->rpl_segaddr, n + 1); 600 + if (err) { 601 + icmpv6_send(skb, ICMPV6_PARAMPROB, 0, 0); 602 + kfree_skb(skb); 603 + kfree(buf); 604 + return -1; 605 + } 606 + 607 + addr = ipv6_hdr(skb)->daddr; 608 + ipv6_hdr(skb)->daddr = ohdr->rpl_segaddr[i]; 609 + ohdr->rpl_segaddr[i] = addr; 610 + 611 + ipv6_rpl_srh_compress(chdr, ohdr, &ipv6_hdr(skb)->daddr, n); 612 + 613 + oldhdr = ipv6_hdr(skb); 614 + 615 + skb_pull(skb, ((hdr->hdrlen + 1) << 3)); 616 + skb_postpull_rcsum(skb, oldhdr, 617 + sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3)); 618 + skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr)); 619 + skb_reset_network_header(skb); 620 + skb_mac_header_rebuild(skb); 621 + skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 622 + 623 + memmove(ipv6_hdr(skb), oldhdr, sizeof(struct ipv6hdr)); 624 + memcpy(skb_transport_header(skb), chdr, (chdr->hdrlen + 1) << 3); 625 + 626 + ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 627 + skb_postpush_rcsum(skb, ipv6_hdr(skb), 628 + sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3)); 629 + 630 + kfree(buf); 631 + 632 + skb_dst_drop(skb); 633 + 634 + ip6_route_input(skb); 635 + 636 + if (skb_dst(skb)->error) { 637 + dst_input(skb); 638 + return -1; 639 + } 640 + 641 + if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) { 642 + if (ipv6_hdr(skb)->hop_limit <= 1) { 643 + __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); 644 + icmpv6_send(skb, ICMPV6_TIME_EXCEED, 645 + ICMPV6_EXC_HOPLIMIT, 0); 646 + kfree_skb(skb); 647 + return -1; 648 + } 649 + ipv6_hdr(skb)->hop_limit--; 650 + 651 + skb_pull(skb, sizeof(struct ipv6hdr)); 652 + goto looped_back; 653 + } 654 + 655 + dst_input(skb); 656 + 657 + return -1; 658 + } 659 + 472 660 /******************************** 473 661 Routing header. 474 662 ********************************/ ··· 696 506 return -1; 697 507 } 698 508 699 - /* segment routing */ 700 - if (hdr->type == IPV6_SRCRT_TYPE_4) 509 + switch (hdr->type) { 510 + case IPV6_SRCRT_TYPE_4: 511 + /* segment routing */ 701 512 return ipv6_srh_rcv(skb); 513 + case IPV6_SRCRT_TYPE_3: 514 + /* rpl segment routing */ 515 + return ipv6_rpl_srh_rcv(skb); 516 + default: 517 + break; 518 + } 702 519 703 520 looped_back: 704 521 if (hdr->segments_left == 0) {
+123
net/ipv6/rpl.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /** 3 + * Authors: 4 + * (C) 2020 Alexander Aring <alex.aring@gmail.com> 5 + */ 6 + 7 + #include <net/ipv6.h> 8 + #include <net/rpl.h> 9 + 10 + #define IPV6_PFXTAIL_LEN(x) (sizeof(struct in6_addr) - (x)) 11 + 12 + static void ipv6_rpl_addr_decompress(struct in6_addr *dst, 13 + const struct in6_addr *daddr, 14 + const void *post, unsigned char pfx) 15 + { 16 + memcpy(dst, daddr, pfx); 17 + memcpy(&dst->s6_addr[pfx], post, IPV6_PFXTAIL_LEN(pfx)); 18 + } 19 + 20 + static void ipv6_rpl_addr_compress(void *dst, const struct in6_addr *addr, 21 + unsigned char pfx) 22 + { 23 + memcpy(dst, &addr->s6_addr[pfx], IPV6_PFXTAIL_LEN(pfx)); 24 + } 25 + 26 + static void *ipv6_rpl_segdata_pos(const struct ipv6_rpl_sr_hdr *hdr, int i) 27 + { 28 + return (void *)&hdr->rpl_segdata[i * IPV6_PFXTAIL_LEN(hdr->cmpri)]; 29 + } 30 + 31 + size_t ipv6_rpl_srh_size(unsigned char n, unsigned char cmpri, 32 + unsigned char cmpre) 33 + { 34 + return (n * IPV6_PFXTAIL_LEN(cmpri)) + IPV6_PFXTAIL_LEN(cmpre); 35 + } 36 + 37 + void ipv6_rpl_srh_decompress(struct ipv6_rpl_sr_hdr *outhdr, 38 + const struct ipv6_rpl_sr_hdr *inhdr, 39 + const struct in6_addr *daddr, unsigned char n) 40 + { 41 + int i; 42 + 43 + outhdr->nexthdr = inhdr->nexthdr; 44 + outhdr->hdrlen = (((n + 1) * sizeof(struct in6_addr)) >> 3); 45 + outhdr->pad = 0; 46 + outhdr->type = inhdr->type; 47 + outhdr->segments_left = inhdr->segments_left; 48 + outhdr->cmpri = 0; 49 + outhdr->cmpre = 0; 50 + 51 + for (i = 0; i <= n; i++) 52 + ipv6_rpl_addr_decompress(&outhdr->rpl_segaddr[i], daddr, 53 + ipv6_rpl_segdata_pos(inhdr, i), 54 + inhdr->cmpri); 55 + 56 + ipv6_rpl_addr_decompress(&outhdr->rpl_segaddr[n], daddr, 57 + ipv6_rpl_segdata_pos(inhdr, n), 58 + inhdr->cmpre); 59 + } 60 + 61 + static unsigned char ipv6_rpl_srh_calc_cmpri(const struct ipv6_rpl_sr_hdr *inhdr, 62 + const struct in6_addr *daddr, 63 + unsigned char n) 64 + { 65 + unsigned char plen; 66 + int i; 67 + 68 + for (plen = 0; plen < sizeof(*daddr); plen++) { 69 + for (i = 0; i <= n; i++) { 70 + if (daddr->s6_addr[plen] != 71 + inhdr->rpl_segaddr[i].s6_addr[plen]) 72 + return plen; 73 + } 74 + } 75 + 76 + return plen; 77 + } 78 + 79 + static unsigned char ipv6_rpl_srh_calc_cmpre(const struct in6_addr *daddr, 80 + const struct in6_addr *last_segment) 81 + { 82 + unsigned int plen; 83 + 84 + for (plen = 0; plen < sizeof(*daddr); plen++) { 85 + if (daddr->s6_addr[plen] != last_segment->s6_addr[plen]) 86 + break; 87 + } 88 + 89 + return plen; 90 + } 91 + 92 + void ipv6_rpl_srh_compress(struct ipv6_rpl_sr_hdr *outhdr, 93 + const struct ipv6_rpl_sr_hdr *inhdr, 94 + const struct in6_addr *daddr, unsigned char n) 95 + { 96 + unsigned char cmpri, cmpre; 97 + size_t seglen; 98 + int i; 99 + 100 + cmpri = ipv6_rpl_srh_calc_cmpri(inhdr, daddr, n); 101 + cmpre = ipv6_rpl_srh_calc_cmpre(daddr, &inhdr->rpl_segaddr[n]); 102 + 103 + outhdr->nexthdr = inhdr->nexthdr; 104 + seglen = (n * IPV6_PFXTAIL_LEN(cmpri)) + IPV6_PFXTAIL_LEN(cmpre); 105 + outhdr->hdrlen = seglen >> 3; 106 + if (seglen & 0x7) { 107 + outhdr->hdrlen++; 108 + outhdr->pad = 8 - (seglen & 0x7); 109 + } else { 110 + outhdr->pad = 0; 111 + } 112 + outhdr->type = inhdr->type; 113 + outhdr->segments_left = inhdr->segments_left; 114 + outhdr->cmpri = cmpri; 115 + outhdr->cmpre = cmpre; 116 + 117 + for (i = 0; i <= n; i++) 118 + ipv6_rpl_addr_compress(ipv6_rpl_segdata_pos(outhdr, i), 119 + &inhdr->rpl_segaddr[i], cmpri); 120 + 121 + ipv6_rpl_addr_compress(ipv6_rpl_segdata_pos(outhdr, n), 122 + &inhdr->rpl_segaddr[n], cmpre); 123 + }