12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2d1df6fd8SDavid Lebrun /* 3d1df6fd8SDavid Lebrun * SR-IPv6 implementation 4d1df6fd8SDavid Lebrun * 5004d4b27SMathieu Xhonneux * Authors: 6d1df6fd8SDavid Lebrun * David Lebrun <david.lebrun@uclouvain.be> 7004d4b27SMathieu Xhonneux * eBPF support: Mathieu Xhonneux <m.xhonneux@gmail.com> 8d1df6fd8SDavid Lebrun */ 9d1df6fd8SDavid Lebrun 10b6459415SJakub Kicinski #include <linux/filter.h> 11d1df6fd8SDavid Lebrun #include <linux/types.h> 12d1df6fd8SDavid Lebrun #include <linux/skbuff.h> 13d1df6fd8SDavid Lebrun #include <linux/net.h> 14d1df6fd8SDavid Lebrun #include <linux/module.h> 15d1df6fd8SDavid Lebrun #include <net/ip.h> 16d1df6fd8SDavid Lebrun #include <net/lwtunnel.h> 17d1df6fd8SDavid Lebrun #include <net/netevent.h> 18d1df6fd8SDavid Lebrun #include <net/netns/generic.h> 19d1df6fd8SDavid Lebrun #include <net/ip6_fib.h> 20d1df6fd8SDavid Lebrun #include <net/route.h> 21d1df6fd8SDavid Lebrun #include <net/seg6.h> 22d1df6fd8SDavid Lebrun #include <linux/seg6.h> 23d1df6fd8SDavid Lebrun #include <linux/seg6_local.h> 24d1df6fd8SDavid Lebrun #include <net/addrconf.h> 25d1df6fd8SDavid Lebrun #include <net/ip6_route.h> 26d1df6fd8SDavid Lebrun #include <net/dst_cache.h> 2762ebaeaeSYuki Taguchi #include <net/ip_tunnels.h> 28d1df6fd8SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 29d1df6fd8SDavid Lebrun #include <net/seg6_hmac.h> 30d1df6fd8SDavid Lebrun #endif 311c1e761eSMathieu Xhonneux #include <net/seg6_local.h> 32891ef8ddSDavid Lebrun #include <linux/etherdevice.h> 33004d4b27SMathieu Xhonneux #include <linux/bpf.h> 347a3f5b0dSRyoga Saito #include <linux/netfilter.h> 35d1df6fd8SDavid Lebrun 36300a0fd8SAndrea Mayer #define SEG6_F_ATTR(i) BIT(i) 37300a0fd8SAndrea Mayer 38d1df6fd8SDavid Lebrun struct seg6_local_lwt; 39d1df6fd8SDavid Lebrun 40cfdf64a0SAndrea Mayer /* callbacks used for customizing the creation and destruction of a behavior */ 41cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops { 42cfdf64a0SAndrea Mayer int (*build_state)(struct seg6_local_lwt *slwt, const void *cfg, 43cfdf64a0SAndrea Mayer struct netlink_ext_ack *extack); 44cfdf64a0SAndrea Mayer void (*destroy_state)(struct seg6_local_lwt *slwt); 45cfdf64a0SAndrea Mayer }; 46cfdf64a0SAndrea Mayer 47d1df6fd8SDavid Lebrun struct seg6_action_desc { 48d1df6fd8SDavid Lebrun int action; 49d1df6fd8SDavid Lebrun unsigned long attrs; 500a3021f1SAndrea Mayer 510a3021f1SAndrea Mayer /* The optattrs field is used for specifying all the optional 520a3021f1SAndrea Mayer * attributes supported by a specific behavior. 530a3021f1SAndrea Mayer * It means that if one of these attributes is not provided in the 540a3021f1SAndrea Mayer * netlink message during the behavior creation, no errors will be 550a3021f1SAndrea Mayer * returned to the userspace. 560a3021f1SAndrea Mayer * 570a3021f1SAndrea Mayer * Each attribute can be only of two types (mutually exclusive): 580a3021f1SAndrea Mayer * 1) required or 2) optional. 590a3021f1SAndrea Mayer * Every user MUST obey to this rule! If you set an attribute as 600a3021f1SAndrea Mayer * required the same attribute CANNOT be set as optional and vice 610a3021f1SAndrea Mayer * versa. 620a3021f1SAndrea Mayer */ 630a3021f1SAndrea Mayer unsigned long optattrs; 640a3021f1SAndrea Mayer 65d1df6fd8SDavid Lebrun int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 66d1df6fd8SDavid Lebrun int static_headroom; 67cfdf64a0SAndrea Mayer 68cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops slwt_ops; 69d1df6fd8SDavid Lebrun }; 70d1df6fd8SDavid Lebrun 71004d4b27SMathieu Xhonneux struct bpf_lwt_prog { 72004d4b27SMathieu Xhonneux struct bpf_prog *prog; 73004d4b27SMathieu Xhonneux char *name; 74004d4b27SMathieu Xhonneux }; 75004d4b27SMathieu Xhonneux 76848f3c0dSAndrea Mayer /* default length values (expressed in bits) for both Locator-Block and 77848f3c0dSAndrea Mayer * Locator-Node Function. 78848f3c0dSAndrea Mayer * 79848f3c0dSAndrea Mayer * Both SEG6_LOCAL_LCBLOCK_DBITS and SEG6_LOCAL_LCNODE_FN_DBITS *must* be: 80848f3c0dSAndrea Mayer * i) greater than 0; 81848f3c0dSAndrea Mayer * ii) evenly divisible by 8. In other terms, the lengths of the 82848f3c0dSAndrea Mayer * Locator-Block and Locator-Node Function must be byte-aligned (we can 83848f3c0dSAndrea Mayer * relax this constraint in the future if really needed). 84848f3c0dSAndrea Mayer * 85848f3c0dSAndrea Mayer * Moreover, a third condition must hold: 86848f3c0dSAndrea Mayer * iii) SEG6_LOCAL_LCBLOCK_DBITS + SEG6_LOCAL_LCNODE_FN_DBITS <= 128. 87848f3c0dSAndrea Mayer * 88848f3c0dSAndrea Mayer * The correctness of SEG6_LOCAL_LCBLOCK_DBITS and SEG6_LOCAL_LCNODE_FN_DBITS 89848f3c0dSAndrea Mayer * values are checked during the kernel compilation. If the compilation stops, 90848f3c0dSAndrea Mayer * check the value of these parameters to see if they meet conditions (i), (ii) 91848f3c0dSAndrea Mayer * and (iii). 92848f3c0dSAndrea Mayer */ 93848f3c0dSAndrea Mayer #define SEG6_LOCAL_LCBLOCK_DBITS 32 94848f3c0dSAndrea Mayer #define SEG6_LOCAL_LCNODE_FN_DBITS 16 95848f3c0dSAndrea Mayer 96848f3c0dSAndrea Mayer /* The following next_csid_chk_{cntr,lcblock,lcblock_fn}_bits macros can be 97848f3c0dSAndrea Mayer * used directly to check whether the lengths (in bits) of Locator-Block and 98848f3c0dSAndrea Mayer * Locator-Node Function are valid according to (i), (ii), (iii). 99848f3c0dSAndrea Mayer */ 100848f3c0dSAndrea Mayer #define next_csid_chk_cntr_bits(blen, flen) \ 101848f3c0dSAndrea Mayer ((blen) + (flen) > 128) 102848f3c0dSAndrea Mayer 103848f3c0dSAndrea Mayer #define next_csid_chk_lcblock_bits(blen) \ 104848f3c0dSAndrea Mayer ({ \ 105848f3c0dSAndrea Mayer typeof(blen) __tmp = blen; \ 106848f3c0dSAndrea Mayer (!__tmp || __tmp > 120 || (__tmp & 0x07)); \ 107848f3c0dSAndrea Mayer }) 108848f3c0dSAndrea Mayer 109848f3c0dSAndrea Mayer #define next_csid_chk_lcnode_fn_bits(flen) \ 110848f3c0dSAndrea Mayer next_csid_chk_lcblock_bits(flen) 111848f3c0dSAndrea Mayer 112*bdf3c0b9SAndrea Mayer #define SEG6_F_LOCAL_FLV_OP(flvname) BIT(SEG6_LOCAL_FLV_OP_##flvname) 113*bdf3c0b9SAndrea Mayer #define SEG6_F_LOCAL_FLV_PSP SEG6_F_LOCAL_FLV_OP(PSP) 114*bdf3c0b9SAndrea Mayer 115*bdf3c0b9SAndrea Mayer /* Supported RFC8986 Flavor operations are reported in this bitmask */ 116*bdf3c0b9SAndrea Mayer #define SEG6_LOCAL_FLV8986_SUPP_OPS SEG6_F_LOCAL_FLV_PSP 117*bdf3c0b9SAndrea Mayer 118848f3c0dSAndrea Mayer /* Supported Flavor operations are reported in this bitmask */ 119*bdf3c0b9SAndrea Mayer #define SEG6_LOCAL_FLV_SUPP_OPS (SEG6_F_LOCAL_FLV_OP(NEXT_CSID) | \ 120*bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV8986_SUPP_OPS) 121848f3c0dSAndrea Mayer 122848f3c0dSAndrea Mayer struct seg6_flavors_info { 123848f3c0dSAndrea Mayer /* Flavor operations */ 124848f3c0dSAndrea Mayer __u32 flv_ops; 125848f3c0dSAndrea Mayer 126848f3c0dSAndrea Mayer /* Locator-Block length, expressed in bits */ 127848f3c0dSAndrea Mayer __u8 lcblock_bits; 128848f3c0dSAndrea Mayer /* Locator-Node Function length, expressed in bits*/ 129848f3c0dSAndrea Mayer __u8 lcnode_func_bits; 130848f3c0dSAndrea Mayer }; 131848f3c0dSAndrea Mayer 132664d6f86SAndrea Mayer enum seg6_end_dt_mode { 133664d6f86SAndrea Mayer DT_INVALID_MODE = -EINVAL, 134664d6f86SAndrea Mayer DT_LEGACY_MODE = 0, 135664d6f86SAndrea Mayer DT_VRF_MODE = 1, 136664d6f86SAndrea Mayer }; 137664d6f86SAndrea Mayer 138664d6f86SAndrea Mayer struct seg6_end_dt_info { 139664d6f86SAndrea Mayer enum seg6_end_dt_mode mode; 140664d6f86SAndrea Mayer 141664d6f86SAndrea Mayer struct net *net; 142664d6f86SAndrea Mayer /* VRF device associated to the routing table used by the SRv6 143664d6f86SAndrea Mayer * End.DT4/DT6 behavior for routing IPv4/IPv6 packets. 144664d6f86SAndrea Mayer */ 145664d6f86SAndrea Mayer int vrf_ifindex; 146664d6f86SAndrea Mayer int vrf_table; 147664d6f86SAndrea Mayer 1488b532109SAndrea Mayer /* tunneled packet family (IPv4 or IPv6). 1498b532109SAndrea Mayer * Protocol and header length are inferred from family. 1508b532109SAndrea Mayer */ 151664d6f86SAndrea Mayer u16 family; 152664d6f86SAndrea Mayer }; 153664d6f86SAndrea Mayer 15494604548SAndrea Mayer struct pcpu_seg6_local_counters { 15594604548SAndrea Mayer u64_stats_t packets; 15694604548SAndrea Mayer u64_stats_t bytes; 15794604548SAndrea Mayer u64_stats_t errors; 15894604548SAndrea Mayer 15994604548SAndrea Mayer struct u64_stats_sync syncp; 16094604548SAndrea Mayer }; 16194604548SAndrea Mayer 16294604548SAndrea Mayer /* This struct groups all the SRv6 Behavior counters supported so far. 16394604548SAndrea Mayer * 16494604548SAndrea Mayer * put_nla_counters() makes use of this data structure to collect all counter 16594604548SAndrea Mayer * values after the per-CPU counter evaluation has been performed. 16694604548SAndrea Mayer * Finally, each counter value (in seg6_local_counters) is stored in the 16794604548SAndrea Mayer * corresponding netlink attribute and sent to user space. 16894604548SAndrea Mayer * 16994604548SAndrea Mayer * NB: we don't want to expose this structure to user space! 17094604548SAndrea Mayer */ 17194604548SAndrea Mayer struct seg6_local_counters { 17294604548SAndrea Mayer __u64 packets; 17394604548SAndrea Mayer __u64 bytes; 17494604548SAndrea Mayer __u64 errors; 17594604548SAndrea Mayer }; 17694604548SAndrea Mayer 17794604548SAndrea Mayer #define seg6_local_alloc_pcpu_counters(__gfp) \ 17894604548SAndrea Mayer __netdev_alloc_pcpu_stats(struct pcpu_seg6_local_counters, \ 17994604548SAndrea Mayer ((__gfp) | __GFP_ZERO)) 18094604548SAndrea Mayer 18194604548SAndrea Mayer #define SEG6_F_LOCAL_COUNTERS SEG6_F_ATTR(SEG6_LOCAL_COUNTERS) 18294604548SAndrea Mayer 183d1df6fd8SDavid Lebrun struct seg6_local_lwt { 184d1df6fd8SDavid Lebrun int action; 185d1df6fd8SDavid Lebrun struct ipv6_sr_hdr *srh; 186d1df6fd8SDavid Lebrun int table; 187d1df6fd8SDavid Lebrun struct in_addr nh4; 188d1df6fd8SDavid Lebrun struct in6_addr nh6; 189d1df6fd8SDavid Lebrun int iif; 190d1df6fd8SDavid Lebrun int oif; 191004d4b27SMathieu Xhonneux struct bpf_lwt_prog bpf; 192664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 193664d6f86SAndrea Mayer struct seg6_end_dt_info dt_info; 194664d6f86SAndrea Mayer #endif 195848f3c0dSAndrea Mayer struct seg6_flavors_info flv_info; 196848f3c0dSAndrea Mayer 19794604548SAndrea Mayer struct pcpu_seg6_local_counters __percpu *pcpu_counters; 198d1df6fd8SDavid Lebrun 199d1df6fd8SDavid Lebrun int headroom; 200d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 2010a3021f1SAndrea Mayer /* unlike the required attrs, we have to track the optional attributes 2020a3021f1SAndrea Mayer * that have been effectively parsed. 2030a3021f1SAndrea Mayer */ 2040a3021f1SAndrea Mayer unsigned long parsed_optattrs; 205d1df6fd8SDavid Lebrun }; 206d1df6fd8SDavid Lebrun 207d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) 208d1df6fd8SDavid Lebrun { 209d1df6fd8SDavid Lebrun return (struct seg6_local_lwt *)lwt->data; 210d1df6fd8SDavid Lebrun } 211d1df6fd8SDavid Lebrun 212140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) 213140f04c3SDavid Lebrun { 214140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 215140f04c3SDavid Lebrun 216fa55a7d7SAndrew Lunn srh = seg6_get_srh(skb, IP6_FH_F_SKIP_RH); 217140f04c3SDavid Lebrun if (!srh) 218140f04c3SDavid Lebrun return NULL; 219140f04c3SDavid Lebrun 220140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 221140f04c3SDavid Lebrun if (!seg6_hmac_validate_skb(skb)) 222140f04c3SDavid Lebrun return NULL; 223140f04c3SDavid Lebrun #endif 224140f04c3SDavid Lebrun 225140f04c3SDavid Lebrun return srh; 226140f04c3SDavid Lebrun } 227140f04c3SDavid Lebrun 228d7a669ddSDavid Lebrun static bool decap_and_validate(struct sk_buff *skb, int proto) 229d7a669ddSDavid Lebrun { 230d7a669ddSDavid Lebrun struct ipv6_sr_hdr *srh; 231d7a669ddSDavid Lebrun unsigned int off = 0; 232d7a669ddSDavid Lebrun 233fa55a7d7SAndrew Lunn srh = seg6_get_srh(skb, 0); 234d7a669ddSDavid Lebrun if (srh && srh->segments_left > 0) 235d7a669ddSDavid Lebrun return false; 236d7a669ddSDavid Lebrun 237d7a669ddSDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 238d7a669ddSDavid Lebrun if (srh && !seg6_hmac_validate_skb(skb)) 239d7a669ddSDavid Lebrun return false; 240d7a669ddSDavid Lebrun #endif 241d7a669ddSDavid Lebrun 242d7a669ddSDavid Lebrun if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0) 243d7a669ddSDavid Lebrun return false; 244d7a669ddSDavid Lebrun 245d7a669ddSDavid Lebrun if (!pskb_pull(skb, off)) 246d7a669ddSDavid Lebrun return false; 247d7a669ddSDavid Lebrun 248d7a669ddSDavid Lebrun skb_postpull_rcsum(skb, skb_network_header(skb), off); 249d7a669ddSDavid Lebrun 250d7a669ddSDavid Lebrun skb_reset_network_header(skb); 251d7a669ddSDavid Lebrun skb_reset_transport_header(skb); 25262ebaeaeSYuki Taguchi if (iptunnel_pull_offloads(skb)) 25362ebaeaeSYuki Taguchi return false; 254d7a669ddSDavid Lebrun 255d7a669ddSDavid Lebrun return true; 256d7a669ddSDavid Lebrun } 257d7a669ddSDavid Lebrun 258d7a669ddSDavid Lebrun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr) 259d7a669ddSDavid Lebrun { 260d7a669ddSDavid Lebrun struct in6_addr *addr; 261d7a669ddSDavid Lebrun 262d7a669ddSDavid Lebrun srh->segments_left--; 263d7a669ddSDavid Lebrun addr = srh->segments + srh->segments_left; 264d7a669ddSDavid Lebrun *daddr = *addr; 265d7a669ddSDavid Lebrun } 266d7a669ddSDavid Lebrun 267fd1fef0cSAndrea Mayer static int 268fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr, 269fd1fef0cSAndrea Mayer u32 tbl_id, bool local_delivery) 270d7a669ddSDavid Lebrun { 271d7a669ddSDavid Lebrun struct net *net = dev_net(skb->dev); 272d7a669ddSDavid Lebrun struct ipv6hdr *hdr = ipv6_hdr(skb); 273d7a669ddSDavid Lebrun int flags = RT6_LOOKUP_F_HAS_SADDR; 274d7a669ddSDavid Lebrun struct dst_entry *dst = NULL; 275d7a669ddSDavid Lebrun struct rt6_info *rt; 276d7a669ddSDavid Lebrun struct flowi6 fl6; 277fd1fef0cSAndrea Mayer int dev_flags = 0; 278d7a669ddSDavid Lebrun 279a3bd2102SAndrea Mayer memset(&fl6, 0, sizeof(fl6)); 280d7a669ddSDavid Lebrun fl6.flowi6_iif = skb->dev->ifindex; 281d7a669ddSDavid Lebrun fl6.daddr = nhaddr ? *nhaddr : hdr->daddr; 282d7a669ddSDavid Lebrun fl6.saddr = hdr->saddr; 283d7a669ddSDavid Lebrun fl6.flowlabel = ip6_flowinfo(hdr); 284d7a669ddSDavid Lebrun fl6.flowi6_mark = skb->mark; 285d7a669ddSDavid Lebrun fl6.flowi6_proto = hdr->nexthdr; 286d7a669ddSDavid Lebrun 287d7a669ddSDavid Lebrun if (nhaddr) 288d7a669ddSDavid Lebrun fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH; 289d7a669ddSDavid Lebrun 290d7a669ddSDavid Lebrun if (!tbl_id) { 291b75cc8f9SDavid Ahern dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags); 292d7a669ddSDavid Lebrun } else { 293d7a669ddSDavid Lebrun struct fib6_table *table; 294d7a669ddSDavid Lebrun 295d7a669ddSDavid Lebrun table = fib6_get_table(net, tbl_id); 296d7a669ddSDavid Lebrun if (!table) 297d7a669ddSDavid Lebrun goto out; 298d7a669ddSDavid Lebrun 299b75cc8f9SDavid Ahern rt = ip6_pol_route(net, table, 0, &fl6, skb, flags); 300d7a669ddSDavid Lebrun dst = &rt->dst; 301d7a669ddSDavid Lebrun } 302d7a669ddSDavid Lebrun 303fd1fef0cSAndrea Mayer /* we want to discard traffic destined for local packet processing, 304fd1fef0cSAndrea Mayer * if @local_delivery is set to false. 305fd1fef0cSAndrea Mayer */ 306fd1fef0cSAndrea Mayer if (!local_delivery) 307fd1fef0cSAndrea Mayer dev_flags |= IFF_LOOPBACK; 308fd1fef0cSAndrea Mayer 309fd1fef0cSAndrea Mayer if (dst && (dst->dev->flags & dev_flags) && !dst->error) { 310d7a669ddSDavid Lebrun dst_release(dst); 311d7a669ddSDavid Lebrun dst = NULL; 312d7a669ddSDavid Lebrun } 313d7a669ddSDavid Lebrun 314d7a669ddSDavid Lebrun out: 315d7a669ddSDavid Lebrun if (!dst) { 316d7a669ddSDavid Lebrun rt = net->ipv6.ip6_blk_hole_entry; 317d7a669ddSDavid Lebrun dst = &rt->dst; 318d7a669ddSDavid Lebrun dst_hold(dst); 319d7a669ddSDavid Lebrun } 320d7a669ddSDavid Lebrun 321d7a669ddSDavid Lebrun skb_dst_drop(skb); 322d7a669ddSDavid Lebrun skb_dst_set(skb, dst); 3231c1e761eSMathieu Xhonneux return dst->error; 324d7a669ddSDavid Lebrun } 325d7a669ddSDavid Lebrun 326fd1fef0cSAndrea Mayer int seg6_lookup_nexthop(struct sk_buff *skb, 327fd1fef0cSAndrea Mayer struct in6_addr *nhaddr, u32 tbl_id) 328fd1fef0cSAndrea Mayer { 329fd1fef0cSAndrea Mayer return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false); 330fd1fef0cSAndrea Mayer } 331fd1fef0cSAndrea Mayer 332848f3c0dSAndrea Mayer static __u8 seg6_flv_lcblock_octects(const struct seg6_flavors_info *finfo) 333848f3c0dSAndrea Mayer { 334848f3c0dSAndrea Mayer return finfo->lcblock_bits >> 3; 335848f3c0dSAndrea Mayer } 336848f3c0dSAndrea Mayer 337848f3c0dSAndrea Mayer static __u8 seg6_flv_lcnode_func_octects(const struct seg6_flavors_info *finfo) 338848f3c0dSAndrea Mayer { 339848f3c0dSAndrea Mayer return finfo->lcnode_func_bits >> 3; 340848f3c0dSAndrea Mayer } 341848f3c0dSAndrea Mayer 342848f3c0dSAndrea Mayer static bool seg6_next_csid_is_arg_zero(const struct in6_addr *addr, 343848f3c0dSAndrea Mayer const struct seg6_flavors_info *finfo) 344848f3c0dSAndrea Mayer { 345848f3c0dSAndrea Mayer __u8 fnc_octects = seg6_flv_lcnode_func_octects(finfo); 346848f3c0dSAndrea Mayer __u8 blk_octects = seg6_flv_lcblock_octects(finfo); 347848f3c0dSAndrea Mayer __u8 arg_octects; 348848f3c0dSAndrea Mayer int i; 349848f3c0dSAndrea Mayer 350848f3c0dSAndrea Mayer arg_octects = 16 - blk_octects - fnc_octects; 351848f3c0dSAndrea Mayer for (i = 0; i < arg_octects; ++i) { 352848f3c0dSAndrea Mayer if (addr->s6_addr[blk_octects + fnc_octects + i] != 0x00) 353848f3c0dSAndrea Mayer return false; 354848f3c0dSAndrea Mayer } 355848f3c0dSAndrea Mayer 356848f3c0dSAndrea Mayer return true; 357848f3c0dSAndrea Mayer } 358848f3c0dSAndrea Mayer 359848f3c0dSAndrea Mayer /* assume that DA.Argument length > 0 */ 360848f3c0dSAndrea Mayer static void seg6_next_csid_advance_arg(struct in6_addr *addr, 361848f3c0dSAndrea Mayer const struct seg6_flavors_info *finfo) 362848f3c0dSAndrea Mayer { 363848f3c0dSAndrea Mayer __u8 fnc_octects = seg6_flv_lcnode_func_octects(finfo); 364848f3c0dSAndrea Mayer __u8 blk_octects = seg6_flv_lcblock_octects(finfo); 365848f3c0dSAndrea Mayer 366848f3c0dSAndrea Mayer /* advance DA.Argument */ 367848f3c0dSAndrea Mayer memmove(&addr->s6_addr[blk_octects], 368848f3c0dSAndrea Mayer &addr->s6_addr[blk_octects + fnc_octects], 369848f3c0dSAndrea Mayer 16 - blk_octects - fnc_octects); 370848f3c0dSAndrea Mayer 371848f3c0dSAndrea Mayer memset(&addr->s6_addr[16 - fnc_octects], 0x00, fnc_octects); 372848f3c0dSAndrea Mayer } 373848f3c0dSAndrea Mayer 374525c65ffSAndrea Mayer static int input_action_end_finish(struct sk_buff *skb, 375525c65ffSAndrea Mayer struct seg6_local_lwt *slwt) 376525c65ffSAndrea Mayer { 377525c65ffSAndrea Mayer seg6_lookup_nexthop(skb, NULL, 0); 378525c65ffSAndrea Mayer 379525c65ffSAndrea Mayer return dst_input(skb); 380525c65ffSAndrea Mayer } 381525c65ffSAndrea Mayer 382848f3c0dSAndrea Mayer static int input_action_end_core(struct sk_buff *skb, 383848f3c0dSAndrea Mayer struct seg6_local_lwt *slwt) 384140f04c3SDavid Lebrun { 385140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 386140f04c3SDavid Lebrun 387140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 388140f04c3SDavid Lebrun if (!srh) 389140f04c3SDavid Lebrun goto drop; 390140f04c3SDavid Lebrun 391d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 392140f04c3SDavid Lebrun 393525c65ffSAndrea Mayer return input_action_end_finish(skb, slwt); 394140f04c3SDavid Lebrun 395140f04c3SDavid Lebrun drop: 396140f04c3SDavid Lebrun kfree_skb(skb); 397140f04c3SDavid Lebrun return -EINVAL; 398140f04c3SDavid Lebrun } 399140f04c3SDavid Lebrun 400848f3c0dSAndrea Mayer static int end_next_csid_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) 401848f3c0dSAndrea Mayer { 402848f3c0dSAndrea Mayer const struct seg6_flavors_info *finfo = &slwt->flv_info; 403848f3c0dSAndrea Mayer struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; 404848f3c0dSAndrea Mayer 405848f3c0dSAndrea Mayer if (seg6_next_csid_is_arg_zero(daddr, finfo)) 406848f3c0dSAndrea Mayer return input_action_end_core(skb, slwt); 407848f3c0dSAndrea Mayer 408848f3c0dSAndrea Mayer /* update DA */ 409848f3c0dSAndrea Mayer seg6_next_csid_advance_arg(daddr, finfo); 410848f3c0dSAndrea Mayer 411525c65ffSAndrea Mayer return input_action_end_finish(skb, slwt); 412848f3c0dSAndrea Mayer } 413848f3c0dSAndrea Mayer 414848f3c0dSAndrea Mayer static bool seg6_next_csid_enabled(__u32 fops) 415848f3c0dSAndrea Mayer { 416848f3c0dSAndrea Mayer return fops & BIT(SEG6_LOCAL_FLV_OP_NEXT_CSID); 417848f3c0dSAndrea Mayer } 418848f3c0dSAndrea Mayer 419*bdf3c0b9SAndrea Mayer /* We describe the packet state in relation to the absence/presence of the SRH 420*bdf3c0b9SAndrea Mayer * and the Segment Left (SL) field. 421*bdf3c0b9SAndrea Mayer * For our purposes, it is not necessary to record the exact value of the SL 422*bdf3c0b9SAndrea Mayer * when the SID List consists of two or more segments. 423*bdf3c0b9SAndrea Mayer */ 424*bdf3c0b9SAndrea Mayer enum seg6_local_pktinfo { 425*bdf3c0b9SAndrea Mayer /* the order really matters! */ 426*bdf3c0b9SAndrea Mayer SEG6_LOCAL_PKTINFO_NOHDR = 0, 427*bdf3c0b9SAndrea Mayer SEG6_LOCAL_PKTINFO_SL_ZERO, 428*bdf3c0b9SAndrea Mayer SEG6_LOCAL_PKTINFO_SL_ONE, 429*bdf3c0b9SAndrea Mayer SEG6_LOCAL_PKTINFO_SL_MORE, 430*bdf3c0b9SAndrea Mayer __SEG6_LOCAL_PKTINFO_MAX, 431*bdf3c0b9SAndrea Mayer }; 432*bdf3c0b9SAndrea Mayer 433*bdf3c0b9SAndrea Mayer #define SEG6_LOCAL_PKTINFO_MAX (__SEG6_LOCAL_PKTINFO_MAX - 1) 434*bdf3c0b9SAndrea Mayer 435*bdf3c0b9SAndrea Mayer static enum seg6_local_pktinfo seg6_get_srh_pktinfo(struct ipv6_sr_hdr *srh) 436*bdf3c0b9SAndrea Mayer { 437*bdf3c0b9SAndrea Mayer __u8 sgl; 438*bdf3c0b9SAndrea Mayer 439*bdf3c0b9SAndrea Mayer if (!srh) 440*bdf3c0b9SAndrea Mayer return SEG6_LOCAL_PKTINFO_NOHDR; 441*bdf3c0b9SAndrea Mayer 442*bdf3c0b9SAndrea Mayer sgl = srh->segments_left; 443*bdf3c0b9SAndrea Mayer if (sgl < 2) 444*bdf3c0b9SAndrea Mayer return SEG6_LOCAL_PKTINFO_SL_ZERO + sgl; 445*bdf3c0b9SAndrea Mayer 446*bdf3c0b9SAndrea Mayer return SEG6_LOCAL_PKTINFO_SL_MORE; 447*bdf3c0b9SAndrea Mayer } 448*bdf3c0b9SAndrea Mayer 449*bdf3c0b9SAndrea Mayer enum seg6_local_flv_action { 450*bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV_ACT_UNSPEC = 0, 451*bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV_ACT_END, 452*bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV_ACT_PSP, 453*bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV_ACT_USP, 454*bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV_ACT_USD, 455*bdf3c0b9SAndrea Mayer __SEG6_LOCAL_FLV_ACT_MAX 456*bdf3c0b9SAndrea Mayer }; 457*bdf3c0b9SAndrea Mayer 458*bdf3c0b9SAndrea Mayer #define SEG6_LOCAL_FLV_ACT_MAX (__SEG6_LOCAL_FLV_ACT_MAX - 1) 459*bdf3c0b9SAndrea Mayer 460*bdf3c0b9SAndrea Mayer /* The action table for RFC8986 flavors (see the flv8986_act_tbl below) 461*bdf3c0b9SAndrea Mayer * contains the actions (i.e. processing operations) to be applied on packets 462*bdf3c0b9SAndrea Mayer * when flavors are configured for an End* behavior. 463*bdf3c0b9SAndrea Mayer * By combining the pkinfo data and from the flavors mask, the macro 464*bdf3c0b9SAndrea Mayer * computes the index used to access the elements (actions) stored in the 465*bdf3c0b9SAndrea Mayer * action table. The index is structured as follows: 466*bdf3c0b9SAndrea Mayer * 467*bdf3c0b9SAndrea Mayer * index 468*bdf3c0b9SAndrea Mayer * _______________/\________________ 469*bdf3c0b9SAndrea Mayer * / \ 470*bdf3c0b9SAndrea Mayer * +----------------+----------------+ 471*bdf3c0b9SAndrea Mayer * | pf | afm | 472*bdf3c0b9SAndrea Mayer * +----------------+----------------+ 473*bdf3c0b9SAndrea Mayer * ph-1 ... p1 p0 fk-1 ... f1 f0 474*bdf3c0b9SAndrea Mayer * MSB LSB 475*bdf3c0b9SAndrea Mayer * 476*bdf3c0b9SAndrea Mayer * where: 477*bdf3c0b9SAndrea Mayer * - 'afm' (adjusted flavor mask) is the mask containing a combination of the 478*bdf3c0b9SAndrea Mayer * RFC8986 flavors currently supported. 'afm' corresponds to the @fm 479*bdf3c0b9SAndrea Mayer * argument of the macro whose value is righ-shifted by 1 bit. By doing so, 480*bdf3c0b9SAndrea Mayer * we discard the SEG6_LOCAL_FLV_OP_UNSPEC flag (bit 0 in @fm) which is 481*bdf3c0b9SAndrea Mayer * never used here; 482*bdf3c0b9SAndrea Mayer * - 'pf' encodes the packet info (pktinfo) regarding the presence/absence of 483*bdf3c0b9SAndrea Mayer * the SRH, SL = 0, etc. 'pf' is set with the value of @pf provided as 484*bdf3c0b9SAndrea Mayer * argument to the macro. 485*bdf3c0b9SAndrea Mayer */ 486*bdf3c0b9SAndrea Mayer #define flv8986_act_tbl_idx(pf, fm) \ 487*bdf3c0b9SAndrea Mayer ((((pf) << bits_per(SEG6_LOCAL_FLV8986_SUPP_OPS)) | \ 488*bdf3c0b9SAndrea Mayer ((fm) & SEG6_LOCAL_FLV8986_SUPP_OPS)) >> SEG6_LOCAL_FLV_OP_PSP) 489*bdf3c0b9SAndrea Mayer 490*bdf3c0b9SAndrea Mayer /* We compute the size of the action table by considering the RFC8986 flavors 491*bdf3c0b9SAndrea Mayer * actually supported by the kernel. In this way, the size is automatically 492*bdf3c0b9SAndrea Mayer * adjusted when new flavors are supported. 493*bdf3c0b9SAndrea Mayer */ 494*bdf3c0b9SAndrea Mayer #define FLV8986_ACT_TBL_SIZE \ 495*bdf3c0b9SAndrea Mayer roundup_pow_of_two(flv8986_act_tbl_idx(SEG6_LOCAL_PKTINFO_MAX, \ 496*bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV8986_SUPP_OPS)) 497*bdf3c0b9SAndrea Mayer 498*bdf3c0b9SAndrea Mayer /* tbl_cfg(act, pf, fm) macro is used to easily configure the action 499*bdf3c0b9SAndrea Mayer * table; it accepts 3 arguments: 500*bdf3c0b9SAndrea Mayer * i) @act, the suffix from SEG6_LOCAL_FLV_ACT_{act} representing 501*bdf3c0b9SAndrea Mayer * the action that should be applied on the packet; 502*bdf3c0b9SAndrea Mayer * ii) @pf, the suffix from SEG6_LOCAL_PKTINFO_{pf} reporting the packet 503*bdf3c0b9SAndrea Mayer * info about the lack/presence of SRH, SRH with SL = 0, etc; 504*bdf3c0b9SAndrea Mayer * iii) @fm, the mask of flavors. 505*bdf3c0b9SAndrea Mayer */ 506*bdf3c0b9SAndrea Mayer #define tbl_cfg(act, pf, fm) \ 507*bdf3c0b9SAndrea Mayer [flv8986_act_tbl_idx(SEG6_LOCAL_PKTINFO_##pf, \ 508*bdf3c0b9SAndrea Mayer (fm))] = SEG6_LOCAL_FLV_ACT_##act 509*bdf3c0b9SAndrea Mayer 510*bdf3c0b9SAndrea Mayer /* shorthand for improving readability */ 511*bdf3c0b9SAndrea Mayer #define F_PSP SEG6_F_LOCAL_FLV_PSP 512*bdf3c0b9SAndrea Mayer 513*bdf3c0b9SAndrea Mayer /* The table contains, for each combination of the pktinfo data and 514*bdf3c0b9SAndrea Mayer * flavors, the action that should be taken on a packet (e.g. 515*bdf3c0b9SAndrea Mayer * "standard" Endpoint processing, Penultimate Segment Pop, etc). 516*bdf3c0b9SAndrea Mayer * 517*bdf3c0b9SAndrea Mayer * By default, table entries not explicitly configured are initialized with the 518*bdf3c0b9SAndrea Mayer * SEG6_LOCAL_FLV_ACT_UNSPEC action, which generally has the effect of 519*bdf3c0b9SAndrea Mayer * discarding the processed packet. 520*bdf3c0b9SAndrea Mayer */ 521*bdf3c0b9SAndrea Mayer static const u8 flv8986_act_tbl[FLV8986_ACT_TBL_SIZE] = { 522*bdf3c0b9SAndrea Mayer /* PSP variant for packet where SRH with SL = 1 */ 523*bdf3c0b9SAndrea Mayer tbl_cfg(PSP, SL_ONE, F_PSP), 524*bdf3c0b9SAndrea Mayer /* End for packet where the SRH with SL > 1*/ 525*bdf3c0b9SAndrea Mayer tbl_cfg(END, SL_MORE, F_PSP), 526*bdf3c0b9SAndrea Mayer }; 527*bdf3c0b9SAndrea Mayer 528*bdf3c0b9SAndrea Mayer #undef F_PSP 529*bdf3c0b9SAndrea Mayer #undef tbl_cfg 530*bdf3c0b9SAndrea Mayer 531*bdf3c0b9SAndrea Mayer /* For each flavor defined in RFC8986 (or a combination of them) an action is 532*bdf3c0b9SAndrea Mayer * performed on the packet. The specific action depends on: 533*bdf3c0b9SAndrea Mayer * - info extracted from the packet (i.e. pktinfo data) regarding the 534*bdf3c0b9SAndrea Mayer * lack/presence of the SRH, and if the SRH is available, on the value of 535*bdf3c0b9SAndrea Mayer * Segment Left field; 536*bdf3c0b9SAndrea Mayer * - the mask of flavors configured for the specific SRv6 End* behavior. 537*bdf3c0b9SAndrea Mayer * 538*bdf3c0b9SAndrea Mayer * The function combines both the pkinfo and the flavors mask to evaluate the 539*bdf3c0b9SAndrea Mayer * corresponding action to be taken on the packet. 540*bdf3c0b9SAndrea Mayer */ 541*bdf3c0b9SAndrea Mayer static enum seg6_local_flv_action 542*bdf3c0b9SAndrea Mayer seg6_local_flv8986_act_lookup(enum seg6_local_pktinfo pinfo, __u32 flvmask) 543*bdf3c0b9SAndrea Mayer { 544*bdf3c0b9SAndrea Mayer unsigned long index; 545*bdf3c0b9SAndrea Mayer 546*bdf3c0b9SAndrea Mayer /* check if the provided mask of flavors is supported */ 547*bdf3c0b9SAndrea Mayer if (unlikely(flvmask & ~SEG6_LOCAL_FLV8986_SUPP_OPS)) 548*bdf3c0b9SAndrea Mayer return SEG6_LOCAL_FLV_ACT_UNSPEC; 549*bdf3c0b9SAndrea Mayer 550*bdf3c0b9SAndrea Mayer index = flv8986_act_tbl_idx(pinfo, flvmask); 551*bdf3c0b9SAndrea Mayer if (unlikely(index >= FLV8986_ACT_TBL_SIZE)) 552*bdf3c0b9SAndrea Mayer return SEG6_LOCAL_FLV_ACT_UNSPEC; 553*bdf3c0b9SAndrea Mayer 554*bdf3c0b9SAndrea Mayer return flv8986_act_tbl[index]; 555*bdf3c0b9SAndrea Mayer } 556*bdf3c0b9SAndrea Mayer 557*bdf3c0b9SAndrea Mayer /* skb->data must be aligned with skb->network_header */ 558*bdf3c0b9SAndrea Mayer static bool seg6_pop_srh(struct sk_buff *skb, int srhoff) 559*bdf3c0b9SAndrea Mayer { 560*bdf3c0b9SAndrea Mayer struct ipv6_sr_hdr *srh; 561*bdf3c0b9SAndrea Mayer struct ipv6hdr *iph; 562*bdf3c0b9SAndrea Mayer __u8 srh_nexthdr; 563*bdf3c0b9SAndrea Mayer int thoff = -1; 564*bdf3c0b9SAndrea Mayer int srhlen; 565*bdf3c0b9SAndrea Mayer int nhlen; 566*bdf3c0b9SAndrea Mayer 567*bdf3c0b9SAndrea Mayer if (unlikely(srhoff < sizeof(*iph) || 568*bdf3c0b9SAndrea Mayer !pskb_may_pull(skb, srhoff + sizeof(*srh)))) 569*bdf3c0b9SAndrea Mayer return false; 570*bdf3c0b9SAndrea Mayer 571*bdf3c0b9SAndrea Mayer srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 572*bdf3c0b9SAndrea Mayer srhlen = ipv6_optlen(srh); 573*bdf3c0b9SAndrea Mayer 574*bdf3c0b9SAndrea Mayer /* we are about to mangle the pkt, let's check if we can write on it */ 575*bdf3c0b9SAndrea Mayer if (unlikely(skb_ensure_writable(skb, srhoff + srhlen))) 576*bdf3c0b9SAndrea Mayer return false; 577*bdf3c0b9SAndrea Mayer 578*bdf3c0b9SAndrea Mayer /* skb_ensure_writable() may change skb pointers; evaluate srh again */ 579*bdf3c0b9SAndrea Mayer srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 580*bdf3c0b9SAndrea Mayer srh_nexthdr = srh->nexthdr; 581*bdf3c0b9SAndrea Mayer 582*bdf3c0b9SAndrea Mayer if (unlikely(!skb_transport_header_was_set(skb))) 583*bdf3c0b9SAndrea Mayer goto pull; 584*bdf3c0b9SAndrea Mayer 585*bdf3c0b9SAndrea Mayer nhlen = skb_network_header_len(skb); 586*bdf3c0b9SAndrea Mayer /* we have to deal with the transport header: it could be set before 587*bdf3c0b9SAndrea Mayer * the SRH, after the SRH, or within it (which is considered wrong, 588*bdf3c0b9SAndrea Mayer * however). 589*bdf3c0b9SAndrea Mayer */ 590*bdf3c0b9SAndrea Mayer if (likely(nhlen <= srhoff)) 591*bdf3c0b9SAndrea Mayer thoff = nhlen; 592*bdf3c0b9SAndrea Mayer else if (nhlen >= srhoff + srhlen) 593*bdf3c0b9SAndrea Mayer /* transport_header is set after the SRH */ 594*bdf3c0b9SAndrea Mayer thoff = nhlen - srhlen; 595*bdf3c0b9SAndrea Mayer else 596*bdf3c0b9SAndrea Mayer /* transport_header falls inside the SRH; hence, we can't 597*bdf3c0b9SAndrea Mayer * restore the transport_header pointer properly after 598*bdf3c0b9SAndrea Mayer * SRH removing operation. 599*bdf3c0b9SAndrea Mayer */ 600*bdf3c0b9SAndrea Mayer return false; 601*bdf3c0b9SAndrea Mayer pull: 602*bdf3c0b9SAndrea Mayer /* we need to pop the SRH: 603*bdf3c0b9SAndrea Mayer * 1) first of all, we pull out everything from IPv6 header up to SRH 604*bdf3c0b9SAndrea Mayer * (included) evaluating also the rcsum; 605*bdf3c0b9SAndrea Mayer * 2) we overwrite (and then remove) the SRH by properly moving the 606*bdf3c0b9SAndrea Mayer * IPv6 along with any extension header that precedes the SRH; 607*bdf3c0b9SAndrea Mayer * 3) At the end, we push back the pulled headers (except for SRH, 608*bdf3c0b9SAndrea Mayer * obviously). 609*bdf3c0b9SAndrea Mayer */ 610*bdf3c0b9SAndrea Mayer skb_pull_rcsum(skb, srhoff + srhlen); 611*bdf3c0b9SAndrea Mayer memmove(skb_network_header(skb) + srhlen, skb_network_header(skb), 612*bdf3c0b9SAndrea Mayer srhoff); 613*bdf3c0b9SAndrea Mayer skb_push(skb, srhoff); 614*bdf3c0b9SAndrea Mayer 615*bdf3c0b9SAndrea Mayer skb_reset_network_header(skb); 616*bdf3c0b9SAndrea Mayer skb_mac_header_rebuild(skb); 617*bdf3c0b9SAndrea Mayer if (likely(thoff >= 0)) 618*bdf3c0b9SAndrea Mayer skb_set_transport_header(skb, thoff); 619*bdf3c0b9SAndrea Mayer 620*bdf3c0b9SAndrea Mayer iph = ipv6_hdr(skb); 621*bdf3c0b9SAndrea Mayer if (iph->nexthdr == NEXTHDR_ROUTING) { 622*bdf3c0b9SAndrea Mayer iph->nexthdr = srh_nexthdr; 623*bdf3c0b9SAndrea Mayer } else { 624*bdf3c0b9SAndrea Mayer /* we must look for the extension header (EXTH, for short) that 625*bdf3c0b9SAndrea Mayer * immediately precedes the SRH we have just removed. 626*bdf3c0b9SAndrea Mayer * Then, we update the value of the EXTH nexthdr with the one 627*bdf3c0b9SAndrea Mayer * contained in the SRH nexthdr. 628*bdf3c0b9SAndrea Mayer */ 629*bdf3c0b9SAndrea Mayer unsigned int off = sizeof(*iph); 630*bdf3c0b9SAndrea Mayer struct ipv6_opt_hdr *hp, _hdr; 631*bdf3c0b9SAndrea Mayer __u8 nexthdr = iph->nexthdr; 632*bdf3c0b9SAndrea Mayer 633*bdf3c0b9SAndrea Mayer for (;;) { 634*bdf3c0b9SAndrea Mayer if (unlikely(!ipv6_ext_hdr(nexthdr) || 635*bdf3c0b9SAndrea Mayer nexthdr == NEXTHDR_NONE)) 636*bdf3c0b9SAndrea Mayer return false; 637*bdf3c0b9SAndrea Mayer 638*bdf3c0b9SAndrea Mayer hp = skb_header_pointer(skb, off, sizeof(_hdr), &_hdr); 639*bdf3c0b9SAndrea Mayer if (unlikely(!hp)) 640*bdf3c0b9SAndrea Mayer return false; 641*bdf3c0b9SAndrea Mayer 642*bdf3c0b9SAndrea Mayer if (hp->nexthdr == NEXTHDR_ROUTING) { 643*bdf3c0b9SAndrea Mayer hp->nexthdr = srh_nexthdr; 644*bdf3c0b9SAndrea Mayer break; 645*bdf3c0b9SAndrea Mayer } 646*bdf3c0b9SAndrea Mayer 647*bdf3c0b9SAndrea Mayer switch (nexthdr) { 648*bdf3c0b9SAndrea Mayer case NEXTHDR_FRAGMENT: 649*bdf3c0b9SAndrea Mayer fallthrough; 650*bdf3c0b9SAndrea Mayer case NEXTHDR_AUTH: 651*bdf3c0b9SAndrea Mayer /* we expect SRH before FRAG and AUTH */ 652*bdf3c0b9SAndrea Mayer return false; 653*bdf3c0b9SAndrea Mayer default: 654*bdf3c0b9SAndrea Mayer off += ipv6_optlen(hp); 655*bdf3c0b9SAndrea Mayer break; 656*bdf3c0b9SAndrea Mayer } 657*bdf3c0b9SAndrea Mayer 658*bdf3c0b9SAndrea Mayer nexthdr = hp->nexthdr; 659*bdf3c0b9SAndrea Mayer } 660*bdf3c0b9SAndrea Mayer } 661*bdf3c0b9SAndrea Mayer 662*bdf3c0b9SAndrea Mayer iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 663*bdf3c0b9SAndrea Mayer 664*bdf3c0b9SAndrea Mayer skb_postpush_rcsum(skb, iph, srhoff); 665*bdf3c0b9SAndrea Mayer 666*bdf3c0b9SAndrea Mayer return true; 667*bdf3c0b9SAndrea Mayer } 668*bdf3c0b9SAndrea Mayer 669*bdf3c0b9SAndrea Mayer /* process the packet on the basis of the RFC8986 flavors set for the given 670*bdf3c0b9SAndrea Mayer * SRv6 End behavior instance. 671*bdf3c0b9SAndrea Mayer */ 672*bdf3c0b9SAndrea Mayer static int end_flv8986_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) 673*bdf3c0b9SAndrea Mayer { 674*bdf3c0b9SAndrea Mayer const struct seg6_flavors_info *finfo = &slwt->flv_info; 675*bdf3c0b9SAndrea Mayer enum seg6_local_flv_action action; 676*bdf3c0b9SAndrea Mayer enum seg6_local_pktinfo pinfo; 677*bdf3c0b9SAndrea Mayer struct ipv6_sr_hdr *srh; 678*bdf3c0b9SAndrea Mayer __u32 flvmask; 679*bdf3c0b9SAndrea Mayer int srhoff; 680*bdf3c0b9SAndrea Mayer 681*bdf3c0b9SAndrea Mayer srh = seg6_get_srh(skb, 0); 682*bdf3c0b9SAndrea Mayer srhoff = srh ? ((unsigned char *)srh - skb->data) : 0; 683*bdf3c0b9SAndrea Mayer pinfo = seg6_get_srh_pktinfo(srh); 684*bdf3c0b9SAndrea Mayer #ifdef CONFIG_IPV6_SEG6_HMAC 685*bdf3c0b9SAndrea Mayer if (srh && !seg6_hmac_validate_skb(skb)) 686*bdf3c0b9SAndrea Mayer goto drop; 687*bdf3c0b9SAndrea Mayer #endif 688*bdf3c0b9SAndrea Mayer flvmask = finfo->flv_ops; 689*bdf3c0b9SAndrea Mayer if (unlikely(flvmask & ~SEG6_LOCAL_FLV8986_SUPP_OPS)) { 690*bdf3c0b9SAndrea Mayer pr_warn_once("seg6local: invalid RFC8986 flavors\n"); 691*bdf3c0b9SAndrea Mayer goto drop; 692*bdf3c0b9SAndrea Mayer } 693*bdf3c0b9SAndrea Mayer 694*bdf3c0b9SAndrea Mayer /* retrieve the action triggered by the combination of pktinfo data and 695*bdf3c0b9SAndrea Mayer * the flavors mask. 696*bdf3c0b9SAndrea Mayer */ 697*bdf3c0b9SAndrea Mayer action = seg6_local_flv8986_act_lookup(pinfo, flvmask); 698*bdf3c0b9SAndrea Mayer switch (action) { 699*bdf3c0b9SAndrea Mayer case SEG6_LOCAL_FLV_ACT_END: 700*bdf3c0b9SAndrea Mayer /* process the packet as the "standard" End behavior */ 701*bdf3c0b9SAndrea Mayer advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 702*bdf3c0b9SAndrea Mayer break; 703*bdf3c0b9SAndrea Mayer case SEG6_LOCAL_FLV_ACT_PSP: 704*bdf3c0b9SAndrea Mayer advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 705*bdf3c0b9SAndrea Mayer 706*bdf3c0b9SAndrea Mayer if (unlikely(!seg6_pop_srh(skb, srhoff))) 707*bdf3c0b9SAndrea Mayer goto drop; 708*bdf3c0b9SAndrea Mayer break; 709*bdf3c0b9SAndrea Mayer case SEG6_LOCAL_FLV_ACT_UNSPEC: 710*bdf3c0b9SAndrea Mayer fallthrough; 711*bdf3c0b9SAndrea Mayer default: 712*bdf3c0b9SAndrea Mayer /* by default, we drop the packet since we could not find a 713*bdf3c0b9SAndrea Mayer * suitable action. 714*bdf3c0b9SAndrea Mayer */ 715*bdf3c0b9SAndrea Mayer goto drop; 716*bdf3c0b9SAndrea Mayer } 717*bdf3c0b9SAndrea Mayer 718*bdf3c0b9SAndrea Mayer return input_action_end_finish(skb, slwt); 719*bdf3c0b9SAndrea Mayer 720*bdf3c0b9SAndrea Mayer drop: 721*bdf3c0b9SAndrea Mayer kfree_skb(skb); 722*bdf3c0b9SAndrea Mayer return -EINVAL; 723*bdf3c0b9SAndrea Mayer } 724*bdf3c0b9SAndrea Mayer 725848f3c0dSAndrea Mayer /* regular endpoint function */ 726848f3c0dSAndrea Mayer static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 727848f3c0dSAndrea Mayer { 728848f3c0dSAndrea Mayer const struct seg6_flavors_info *finfo = &slwt->flv_info; 729*bdf3c0b9SAndrea Mayer __u32 fops = finfo->flv_ops; 730848f3c0dSAndrea Mayer 731*bdf3c0b9SAndrea Mayer if (!fops) 732*bdf3c0b9SAndrea Mayer return input_action_end_core(skb, slwt); 733*bdf3c0b9SAndrea Mayer 734*bdf3c0b9SAndrea Mayer /* check for the presence of NEXT-C-SID since it applies first */ 735*bdf3c0b9SAndrea Mayer if (seg6_next_csid_enabled(fops)) 736848f3c0dSAndrea Mayer return end_next_csid_core(skb, slwt); 737848f3c0dSAndrea Mayer 738*bdf3c0b9SAndrea Mayer /* the specific processing function to be performed on the packet 739*bdf3c0b9SAndrea Mayer * depends on the combination of flavors defined in RFC8986 and some 740*bdf3c0b9SAndrea Mayer * information extracted from the packet, e.g. presence/absence of SRH, 741*bdf3c0b9SAndrea Mayer * Segment Left = 0, etc. 742*bdf3c0b9SAndrea Mayer */ 743*bdf3c0b9SAndrea Mayer return end_flv8986_core(skb, slwt); 744848f3c0dSAndrea Mayer } 745848f3c0dSAndrea Mayer 746140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */ 747140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) 748140f04c3SDavid Lebrun { 749140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 750140f04c3SDavid Lebrun 751140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 752140f04c3SDavid Lebrun if (!srh) 753140f04c3SDavid Lebrun goto drop; 754140f04c3SDavid Lebrun 755d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 756140f04c3SDavid Lebrun 7571c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, &slwt->nh6, 0); 758140f04c3SDavid Lebrun 759140f04c3SDavid Lebrun return dst_input(skb); 760140f04c3SDavid Lebrun 761140f04c3SDavid Lebrun drop: 762140f04c3SDavid Lebrun kfree_skb(skb); 763140f04c3SDavid Lebrun return -EINVAL; 764140f04c3SDavid Lebrun } 765140f04c3SDavid Lebrun 766891ef8ddSDavid Lebrun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) 767891ef8ddSDavid Lebrun { 768891ef8ddSDavid Lebrun struct ipv6_sr_hdr *srh; 769891ef8ddSDavid Lebrun 770891ef8ddSDavid Lebrun srh = get_and_validate_srh(skb); 771891ef8ddSDavid Lebrun if (!srh) 772891ef8ddSDavid Lebrun goto drop; 773891ef8ddSDavid Lebrun 774891ef8ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 775891ef8ddSDavid Lebrun 7761c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, slwt->table); 777891ef8ddSDavid Lebrun 778891ef8ddSDavid Lebrun return dst_input(skb); 779891ef8ddSDavid Lebrun 780891ef8ddSDavid Lebrun drop: 781891ef8ddSDavid Lebrun kfree_skb(skb); 782891ef8ddSDavid Lebrun return -EINVAL; 783891ef8ddSDavid Lebrun } 784891ef8ddSDavid Lebrun 785891ef8ddSDavid Lebrun /* decapsulate and forward inner L2 frame on specified interface */ 786891ef8ddSDavid Lebrun static int input_action_end_dx2(struct sk_buff *skb, 787891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 788891ef8ddSDavid Lebrun { 789891ef8ddSDavid Lebrun struct net *net = dev_net(skb->dev); 790891ef8ddSDavid Lebrun struct net_device *odev; 791891ef8ddSDavid Lebrun struct ethhdr *eth; 792891ef8ddSDavid Lebrun 79326776253SPaolo Lungaroni if (!decap_and_validate(skb, IPPROTO_ETHERNET)) 794891ef8ddSDavid Lebrun goto drop; 795891ef8ddSDavid Lebrun 796891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, ETH_HLEN)) 797891ef8ddSDavid Lebrun goto drop; 798891ef8ddSDavid Lebrun 799891ef8ddSDavid Lebrun skb_reset_mac_header(skb); 800891ef8ddSDavid Lebrun eth = (struct ethhdr *)skb->data; 801891ef8ddSDavid Lebrun 802891ef8ddSDavid Lebrun /* To determine the frame's protocol, we assume it is 802.3. This avoids 803891ef8ddSDavid Lebrun * a call to eth_type_trans(), which is not really relevant for our 804891ef8ddSDavid Lebrun * use case. 805891ef8ddSDavid Lebrun */ 806891ef8ddSDavid Lebrun if (!eth_proto_is_802_3(eth->h_proto)) 807891ef8ddSDavid Lebrun goto drop; 808891ef8ddSDavid Lebrun 809891ef8ddSDavid Lebrun odev = dev_get_by_index_rcu(net, slwt->oif); 810891ef8ddSDavid Lebrun if (!odev) 811891ef8ddSDavid Lebrun goto drop; 812891ef8ddSDavid Lebrun 813891ef8ddSDavid Lebrun /* As we accept Ethernet frames, make sure the egress device is of 814891ef8ddSDavid Lebrun * the correct type. 815891ef8ddSDavid Lebrun */ 816891ef8ddSDavid Lebrun if (odev->type != ARPHRD_ETHER) 817891ef8ddSDavid Lebrun goto drop; 818891ef8ddSDavid Lebrun 819891ef8ddSDavid Lebrun if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev)) 820891ef8ddSDavid Lebrun goto drop; 821891ef8ddSDavid Lebrun 822891ef8ddSDavid Lebrun skb_orphan(skb); 823891ef8ddSDavid Lebrun 824891ef8ddSDavid Lebrun if (skb_warn_if_lro(skb)) 825891ef8ddSDavid Lebrun goto drop; 826891ef8ddSDavid Lebrun 827891ef8ddSDavid Lebrun skb_forward_csum(skb); 828891ef8ddSDavid Lebrun 829891ef8ddSDavid Lebrun if (skb->len - ETH_HLEN > odev->mtu) 830891ef8ddSDavid Lebrun goto drop; 831891ef8ddSDavid Lebrun 832891ef8ddSDavid Lebrun skb->dev = odev; 833891ef8ddSDavid Lebrun skb->protocol = eth->h_proto; 834891ef8ddSDavid Lebrun 835891ef8ddSDavid Lebrun return dev_queue_xmit(skb); 836891ef8ddSDavid Lebrun 837891ef8ddSDavid Lebrun drop: 838891ef8ddSDavid Lebrun kfree_skb(skb); 839891ef8ddSDavid Lebrun return -EINVAL; 840891ef8ddSDavid Lebrun } 841891ef8ddSDavid Lebrun 8427a3f5b0dSRyoga Saito static int input_action_end_dx6_finish(struct net *net, struct sock *sk, 8437a3f5b0dSRyoga Saito struct sk_buff *skb) 8447a3f5b0dSRyoga Saito { 8457a3f5b0dSRyoga Saito struct dst_entry *orig_dst = skb_dst(skb); 8467a3f5b0dSRyoga Saito struct in6_addr *nhaddr = NULL; 8477a3f5b0dSRyoga Saito struct seg6_local_lwt *slwt; 8487a3f5b0dSRyoga Saito 8497a3f5b0dSRyoga Saito slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 8507a3f5b0dSRyoga Saito 8517a3f5b0dSRyoga Saito /* The inner packet is not associated to any local interface, 8527a3f5b0dSRyoga Saito * so we do not call netif_rx(). 8537a3f5b0dSRyoga Saito * 8547a3f5b0dSRyoga Saito * If slwt->nh6 is set to ::, then lookup the nexthop for the 8557a3f5b0dSRyoga Saito * inner packet's DA. Otherwise, use the specified nexthop. 8567a3f5b0dSRyoga Saito */ 8577a3f5b0dSRyoga Saito if (!ipv6_addr_any(&slwt->nh6)) 8587a3f5b0dSRyoga Saito nhaddr = &slwt->nh6; 8597a3f5b0dSRyoga Saito 8607a3f5b0dSRyoga Saito seg6_lookup_nexthop(skb, nhaddr, 0); 8617a3f5b0dSRyoga Saito 8627a3f5b0dSRyoga Saito return dst_input(skb); 8637a3f5b0dSRyoga Saito } 8647a3f5b0dSRyoga Saito 865140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */ 866140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb, 867140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 868140f04c3SDavid Lebrun { 869140f04c3SDavid Lebrun /* this function accepts IPv6 encapsulated packets, with either 870140f04c3SDavid Lebrun * an SRH with SL=0, or no SRH. 871140f04c3SDavid Lebrun */ 872140f04c3SDavid Lebrun 873d7a669ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 874140f04c3SDavid Lebrun goto drop; 875140f04c3SDavid Lebrun 876d7a669ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 877140f04c3SDavid Lebrun goto drop; 878140f04c3SDavid Lebrun 879c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 8807a3f5b0dSRyoga Saito nf_reset_ct(skb); 881c71644d0SAndrea Mayer 8827a3f5b0dSRyoga Saito if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 8837a3f5b0dSRyoga Saito return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, 8847a3f5b0dSRyoga Saito dev_net(skb->dev), NULL, skb, NULL, 8857a3f5b0dSRyoga Saito skb_dst(skb)->dev, input_action_end_dx6_finish); 886140f04c3SDavid Lebrun 8877a3f5b0dSRyoga Saito return input_action_end_dx6_finish(dev_net(skb->dev), NULL, skb); 888140f04c3SDavid Lebrun drop: 889140f04c3SDavid Lebrun kfree_skb(skb); 890140f04c3SDavid Lebrun return -EINVAL; 891140f04c3SDavid Lebrun } 892140f04c3SDavid Lebrun 8937a3f5b0dSRyoga Saito static int input_action_end_dx4_finish(struct net *net, struct sock *sk, 8947a3f5b0dSRyoga Saito struct sk_buff *skb) 895891ef8ddSDavid Lebrun { 8967a3f5b0dSRyoga Saito struct dst_entry *orig_dst = skb_dst(skb); 8977a3f5b0dSRyoga Saito struct seg6_local_lwt *slwt; 898891ef8ddSDavid Lebrun struct iphdr *iph; 899891ef8ddSDavid Lebrun __be32 nhaddr; 900891ef8ddSDavid Lebrun int err; 901891ef8ddSDavid Lebrun 9027a3f5b0dSRyoga Saito slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 903891ef8ddSDavid Lebrun 904891ef8ddSDavid Lebrun iph = ip_hdr(skb); 905891ef8ddSDavid Lebrun 906891ef8ddSDavid Lebrun nhaddr = slwt->nh4.s_addr ?: iph->daddr; 907891ef8ddSDavid Lebrun 908891ef8ddSDavid Lebrun skb_dst_drop(skb); 909891ef8ddSDavid Lebrun 910891ef8ddSDavid Lebrun err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev); 9117a3f5b0dSRyoga Saito if (err) { 9127a3f5b0dSRyoga Saito kfree_skb(skb); 9137a3f5b0dSRyoga Saito return -EINVAL; 9147a3f5b0dSRyoga Saito } 915891ef8ddSDavid Lebrun 916891ef8ddSDavid Lebrun return dst_input(skb); 9177a3f5b0dSRyoga Saito } 918891ef8ddSDavid Lebrun 9197a3f5b0dSRyoga Saito static int input_action_end_dx4(struct sk_buff *skb, 9207a3f5b0dSRyoga Saito struct seg6_local_lwt *slwt) 9217a3f5b0dSRyoga Saito { 9227a3f5b0dSRyoga Saito if (!decap_and_validate(skb, IPPROTO_IPIP)) 9237a3f5b0dSRyoga Saito goto drop; 9247a3f5b0dSRyoga Saito 9257a3f5b0dSRyoga Saito if (!pskb_may_pull(skb, sizeof(struct iphdr))) 9267a3f5b0dSRyoga Saito goto drop; 9277a3f5b0dSRyoga Saito 9287a3f5b0dSRyoga Saito skb->protocol = htons(ETH_P_IP); 9297a3f5b0dSRyoga Saito skb_set_transport_header(skb, sizeof(struct iphdr)); 9307a3f5b0dSRyoga Saito nf_reset_ct(skb); 9317a3f5b0dSRyoga Saito 9327a3f5b0dSRyoga Saito if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 9337a3f5b0dSRyoga Saito return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, 9347a3f5b0dSRyoga Saito dev_net(skb->dev), NULL, skb, NULL, 9357a3f5b0dSRyoga Saito skb_dst(skb)->dev, input_action_end_dx4_finish); 9367a3f5b0dSRyoga Saito 9377a3f5b0dSRyoga Saito return input_action_end_dx4_finish(dev_net(skb->dev), NULL, skb); 938891ef8ddSDavid Lebrun drop: 939891ef8ddSDavid Lebrun kfree_skb(skb); 940891ef8ddSDavid Lebrun return -EINVAL; 941891ef8ddSDavid Lebrun } 942891ef8ddSDavid Lebrun 943664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 944664d6f86SAndrea Mayer static struct net *fib6_config_get_net(const struct fib6_config *fib6_cfg) 945664d6f86SAndrea Mayer { 946664d6f86SAndrea Mayer const struct nl_info *nli = &fib6_cfg->fc_nlinfo; 947664d6f86SAndrea Mayer 948664d6f86SAndrea Mayer return nli->nl_net; 949664d6f86SAndrea Mayer } 950664d6f86SAndrea Mayer 951664d6f86SAndrea Mayer static int __seg6_end_dt_vrf_build(struct seg6_local_lwt *slwt, const void *cfg, 952664d6f86SAndrea Mayer u16 family, struct netlink_ext_ack *extack) 953664d6f86SAndrea Mayer { 954664d6f86SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 955664d6f86SAndrea Mayer int vrf_ifindex; 956664d6f86SAndrea Mayer struct net *net; 957664d6f86SAndrea Mayer 958664d6f86SAndrea Mayer net = fib6_config_get_net(cfg); 959664d6f86SAndrea Mayer 960664d6f86SAndrea Mayer /* note that vrf_table was already set by parse_nla_vrftable() */ 961664d6f86SAndrea Mayer vrf_ifindex = l3mdev_ifindex_lookup_by_table_id(L3MDEV_TYPE_VRF, net, 962664d6f86SAndrea Mayer info->vrf_table); 963664d6f86SAndrea Mayer if (vrf_ifindex < 0) { 964664d6f86SAndrea Mayer if (vrf_ifindex == -EPERM) { 965664d6f86SAndrea Mayer NL_SET_ERR_MSG(extack, 966664d6f86SAndrea Mayer "Strict mode for VRF is disabled"); 967664d6f86SAndrea Mayer } else if (vrf_ifindex == -ENODEV) { 968664d6f86SAndrea Mayer NL_SET_ERR_MSG(extack, 969664d6f86SAndrea Mayer "Table has no associated VRF device"); 970664d6f86SAndrea Mayer } else { 971664d6f86SAndrea Mayer pr_debug("seg6local: SRv6 End.DT* creation error=%d\n", 972664d6f86SAndrea Mayer vrf_ifindex); 973664d6f86SAndrea Mayer } 974664d6f86SAndrea Mayer 975664d6f86SAndrea Mayer return vrf_ifindex; 976664d6f86SAndrea Mayer } 977664d6f86SAndrea Mayer 978664d6f86SAndrea Mayer info->net = net; 979664d6f86SAndrea Mayer info->vrf_ifindex = vrf_ifindex; 980664d6f86SAndrea Mayer 981664d6f86SAndrea Mayer info->family = family; 982664d6f86SAndrea Mayer info->mode = DT_VRF_MODE; 983664d6f86SAndrea Mayer 984664d6f86SAndrea Mayer return 0; 985664d6f86SAndrea Mayer } 986664d6f86SAndrea Mayer 987664d6f86SAndrea Mayer /* The SRv6 End.DT4/DT6 behavior extracts the inner (IPv4/IPv6) packet and 988664d6f86SAndrea Mayer * routes the IPv4/IPv6 packet by looking at the configured routing table. 989664d6f86SAndrea Mayer * 990664d6f86SAndrea Mayer * In the SRv6 End.DT4/DT6 use case, we can receive traffic (IPv6+Segment 991664d6f86SAndrea Mayer * Routing Header packets) from several interfaces and the outer IPv6 992664d6f86SAndrea Mayer * destination address (DA) is used for retrieving the specific instance of the 993664d6f86SAndrea Mayer * End.DT4/DT6 behavior that should process the packets. 994664d6f86SAndrea Mayer * 995664d6f86SAndrea Mayer * However, the inner IPv4/IPv6 packet is not really bound to any receiving 996664d6f86SAndrea Mayer * interface and thus the End.DT4/DT6 sets the VRF (associated with the 997664d6f86SAndrea Mayer * corresponding routing table) as the *receiving* interface. 998664d6f86SAndrea Mayer * In other words, the End.DT4/DT6 processes a packet as if it has been received 999664d6f86SAndrea Mayer * directly by the VRF (and not by one of its slave devices, if any). 1000664d6f86SAndrea Mayer * In this way, the VRF interface is used for routing the IPv4/IPv6 packet in 1001664d6f86SAndrea Mayer * according to the routing table configured by the End.DT4/DT6 instance. 1002664d6f86SAndrea Mayer * 1003664d6f86SAndrea Mayer * This design allows you to get some interesting features like: 1004664d6f86SAndrea Mayer * 1) the statistics on rx packets; 1005664d6f86SAndrea Mayer * 2) the possibility to install a packet sniffer on the receiving interface 1006664d6f86SAndrea Mayer * (the VRF one) for looking at the incoming packets; 1007664d6f86SAndrea Mayer * 3) the possibility to leverage the netfilter prerouting hook for the inner 1008664d6f86SAndrea Mayer * IPv4 packet. 1009664d6f86SAndrea Mayer * 1010664d6f86SAndrea Mayer * This function returns: 1011664d6f86SAndrea Mayer * - the sk_buff* when the VRF rcv handler has processed the packet correctly; 1012664d6f86SAndrea Mayer * - NULL when the skb is consumed by the VRF rcv handler; 1013664d6f86SAndrea Mayer * - a pointer which encodes a negative error number in case of error. 1014664d6f86SAndrea Mayer * Note that in this case, the function takes care of freeing the skb. 1015664d6f86SAndrea Mayer */ 1016664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_rcv(struct sk_buff *skb, u16 family, 1017664d6f86SAndrea Mayer struct net_device *dev) 1018664d6f86SAndrea Mayer { 1019664d6f86SAndrea Mayer /* based on l3mdev_ip_rcv; we are only interested in the master */ 1020664d6f86SAndrea Mayer if (unlikely(!netif_is_l3_master(dev) && !netif_has_l3_rx_handler(dev))) 1021664d6f86SAndrea Mayer goto drop; 1022664d6f86SAndrea Mayer 1023664d6f86SAndrea Mayer if (unlikely(!dev->l3mdev_ops->l3mdev_l3_rcv)) 1024664d6f86SAndrea Mayer goto drop; 1025664d6f86SAndrea Mayer 1026664d6f86SAndrea Mayer /* the decap packet IPv4/IPv6 does not come with any mac header info. 1027664d6f86SAndrea Mayer * We must unset the mac header to allow the VRF device to rebuild it, 1028664d6f86SAndrea Mayer * just in case there is a sniffer attached on the device. 1029664d6f86SAndrea Mayer */ 1030664d6f86SAndrea Mayer skb_unset_mac_header(skb); 1031664d6f86SAndrea Mayer 1032664d6f86SAndrea Mayer skb = dev->l3mdev_ops->l3mdev_l3_rcv(dev, skb, family); 1033664d6f86SAndrea Mayer if (!skb) 1034664d6f86SAndrea Mayer /* the skb buffer was consumed by the handler */ 1035664d6f86SAndrea Mayer return NULL; 1036664d6f86SAndrea Mayer 1037664d6f86SAndrea Mayer /* when a packet is received by a VRF or by one of its slaves, the 1038664d6f86SAndrea Mayer * master device reference is set into the skb. 1039664d6f86SAndrea Mayer */ 1040664d6f86SAndrea Mayer if (unlikely(skb->dev != dev || skb->skb_iif != dev->ifindex)) 1041664d6f86SAndrea Mayer goto drop; 1042664d6f86SAndrea Mayer 1043664d6f86SAndrea Mayer return skb; 1044664d6f86SAndrea Mayer 1045664d6f86SAndrea Mayer drop: 1046664d6f86SAndrea Mayer kfree_skb(skb); 1047664d6f86SAndrea Mayer return ERR_PTR(-EINVAL); 1048664d6f86SAndrea Mayer } 1049664d6f86SAndrea Mayer 1050664d6f86SAndrea Mayer static struct net_device *end_dt_get_vrf_rcu(struct sk_buff *skb, 1051664d6f86SAndrea Mayer struct seg6_end_dt_info *info) 1052664d6f86SAndrea Mayer { 1053664d6f86SAndrea Mayer int vrf_ifindex = info->vrf_ifindex; 1054664d6f86SAndrea Mayer struct net *net = info->net; 1055664d6f86SAndrea Mayer 1056664d6f86SAndrea Mayer if (unlikely(vrf_ifindex < 0)) 1057664d6f86SAndrea Mayer goto error; 1058664d6f86SAndrea Mayer 1059664d6f86SAndrea Mayer if (unlikely(!net_eq(dev_net(skb->dev), net))) 1060664d6f86SAndrea Mayer goto error; 1061664d6f86SAndrea Mayer 1062664d6f86SAndrea Mayer return dev_get_by_index_rcu(net, vrf_ifindex); 1063664d6f86SAndrea Mayer 1064664d6f86SAndrea Mayer error: 1065664d6f86SAndrea Mayer return NULL; 1066664d6f86SAndrea Mayer } 1067664d6f86SAndrea Mayer 1068664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb, 10698b532109SAndrea Mayer struct seg6_local_lwt *slwt, u16 family) 1070664d6f86SAndrea Mayer { 1071664d6f86SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 1072664d6f86SAndrea Mayer struct net_device *vrf; 10738b532109SAndrea Mayer __be16 protocol; 10748b532109SAndrea Mayer int hdrlen; 1075664d6f86SAndrea Mayer 1076664d6f86SAndrea Mayer vrf = end_dt_get_vrf_rcu(skb, info); 1077664d6f86SAndrea Mayer if (unlikely(!vrf)) 1078664d6f86SAndrea Mayer goto drop; 1079664d6f86SAndrea Mayer 10808b532109SAndrea Mayer switch (family) { 10818b532109SAndrea Mayer case AF_INET: 10828b532109SAndrea Mayer protocol = htons(ETH_P_IP); 10838b532109SAndrea Mayer hdrlen = sizeof(struct iphdr); 10848b532109SAndrea Mayer break; 10858b532109SAndrea Mayer case AF_INET6: 10868b532109SAndrea Mayer protocol = htons(ETH_P_IPV6); 10878b532109SAndrea Mayer hdrlen = sizeof(struct ipv6hdr); 10888b532109SAndrea Mayer break; 10898b532109SAndrea Mayer case AF_UNSPEC: 10908b532109SAndrea Mayer fallthrough; 10918b532109SAndrea Mayer default: 10928b532109SAndrea Mayer goto drop; 10938b532109SAndrea Mayer } 10948b532109SAndrea Mayer 10958b532109SAndrea Mayer if (unlikely(info->family != AF_UNSPEC && info->family != family)) { 10968b532109SAndrea Mayer pr_warn_once("seg6local: SRv6 End.DT* family mismatch"); 10978b532109SAndrea Mayer goto drop; 10988b532109SAndrea Mayer } 10998b532109SAndrea Mayer 11008b532109SAndrea Mayer skb->protocol = protocol; 1101664d6f86SAndrea Mayer 1102664d6f86SAndrea Mayer skb_dst_drop(skb); 1103664d6f86SAndrea Mayer 11048b532109SAndrea Mayer skb_set_transport_header(skb, hdrlen); 11057a3f5b0dSRyoga Saito nf_reset_ct(skb); 1106664d6f86SAndrea Mayer 11078b532109SAndrea Mayer return end_dt_vrf_rcv(skb, family, vrf); 1108664d6f86SAndrea Mayer 1109664d6f86SAndrea Mayer drop: 1110664d6f86SAndrea Mayer kfree_skb(skb); 1111664d6f86SAndrea Mayer return ERR_PTR(-EINVAL); 1112664d6f86SAndrea Mayer } 1113664d6f86SAndrea Mayer 1114664d6f86SAndrea Mayer static int input_action_end_dt4(struct sk_buff *skb, 1115664d6f86SAndrea Mayer struct seg6_local_lwt *slwt) 1116664d6f86SAndrea Mayer { 1117664d6f86SAndrea Mayer struct iphdr *iph; 1118664d6f86SAndrea Mayer int err; 1119664d6f86SAndrea Mayer 1120664d6f86SAndrea Mayer if (!decap_and_validate(skb, IPPROTO_IPIP)) 1121664d6f86SAndrea Mayer goto drop; 1122664d6f86SAndrea Mayer 1123664d6f86SAndrea Mayer if (!pskb_may_pull(skb, sizeof(struct iphdr))) 1124664d6f86SAndrea Mayer goto drop; 1125664d6f86SAndrea Mayer 11268b532109SAndrea Mayer skb = end_dt_vrf_core(skb, slwt, AF_INET); 1127664d6f86SAndrea Mayer if (!skb) 1128664d6f86SAndrea Mayer /* packet has been processed and consumed by the VRF */ 1129664d6f86SAndrea Mayer return 0; 1130664d6f86SAndrea Mayer 1131664d6f86SAndrea Mayer if (IS_ERR(skb)) 1132664d6f86SAndrea Mayer return PTR_ERR(skb); 1133664d6f86SAndrea Mayer 1134664d6f86SAndrea Mayer iph = ip_hdr(skb); 1135664d6f86SAndrea Mayer 1136664d6f86SAndrea Mayer err = ip_route_input(skb, iph->daddr, iph->saddr, 0, skb->dev); 1137664d6f86SAndrea Mayer if (unlikely(err)) 1138664d6f86SAndrea Mayer goto drop; 1139664d6f86SAndrea Mayer 1140664d6f86SAndrea Mayer return dst_input(skb); 1141664d6f86SAndrea Mayer 1142664d6f86SAndrea Mayer drop: 1143664d6f86SAndrea Mayer kfree_skb(skb); 1144664d6f86SAndrea Mayer return -EINVAL; 1145664d6f86SAndrea Mayer } 1146664d6f86SAndrea Mayer 1147664d6f86SAndrea Mayer static int seg6_end_dt4_build(struct seg6_local_lwt *slwt, const void *cfg, 1148664d6f86SAndrea Mayer struct netlink_ext_ack *extack) 1149664d6f86SAndrea Mayer { 1150664d6f86SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET, extack); 1151664d6f86SAndrea Mayer } 115220a081b7SAndrea Mayer 115320a081b7SAndrea Mayer static enum 115420a081b7SAndrea Mayer seg6_end_dt_mode seg6_end_dt6_parse_mode(struct seg6_local_lwt *slwt) 115520a081b7SAndrea Mayer { 115620a081b7SAndrea Mayer unsigned long parsed_optattrs = slwt->parsed_optattrs; 115720a081b7SAndrea Mayer bool legacy, vrfmode; 115820a081b7SAndrea Mayer 1159300a0fd8SAndrea Mayer legacy = !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE)); 1160300a0fd8SAndrea Mayer vrfmode = !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE)); 116120a081b7SAndrea Mayer 116220a081b7SAndrea Mayer if (!(legacy ^ vrfmode)) 116320a081b7SAndrea Mayer /* both are absent or present: invalid DT6 mode */ 116420a081b7SAndrea Mayer return DT_INVALID_MODE; 116520a081b7SAndrea Mayer 116620a081b7SAndrea Mayer return legacy ? DT_LEGACY_MODE : DT_VRF_MODE; 116720a081b7SAndrea Mayer } 116820a081b7SAndrea Mayer 116920a081b7SAndrea Mayer static enum seg6_end_dt_mode seg6_end_dt6_get_mode(struct seg6_local_lwt *slwt) 117020a081b7SAndrea Mayer { 117120a081b7SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 117220a081b7SAndrea Mayer 117320a081b7SAndrea Mayer return info->mode; 117420a081b7SAndrea Mayer } 117520a081b7SAndrea Mayer 117620a081b7SAndrea Mayer static int seg6_end_dt6_build(struct seg6_local_lwt *slwt, const void *cfg, 117720a081b7SAndrea Mayer struct netlink_ext_ack *extack) 117820a081b7SAndrea Mayer { 117920a081b7SAndrea Mayer enum seg6_end_dt_mode mode = seg6_end_dt6_parse_mode(slwt); 118020a081b7SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 118120a081b7SAndrea Mayer 118220a081b7SAndrea Mayer switch (mode) { 118320a081b7SAndrea Mayer case DT_LEGACY_MODE: 118420a081b7SAndrea Mayer info->mode = DT_LEGACY_MODE; 118520a081b7SAndrea Mayer return 0; 118620a081b7SAndrea Mayer case DT_VRF_MODE: 118720a081b7SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET6, extack); 118820a081b7SAndrea Mayer default: 118920a081b7SAndrea Mayer NL_SET_ERR_MSG(extack, "table or vrftable must be specified"); 119020a081b7SAndrea Mayer return -EINVAL; 119120a081b7SAndrea Mayer } 119220a081b7SAndrea Mayer } 1193664d6f86SAndrea Mayer #endif 1194664d6f86SAndrea Mayer 1195891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb, 1196891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 1197891ef8ddSDavid Lebrun { 1198891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 1199891ef8ddSDavid Lebrun goto drop; 1200891ef8ddSDavid Lebrun 1201891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 1202891ef8ddSDavid Lebrun goto drop; 1203891ef8ddSDavid Lebrun 120420a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 120520a081b7SAndrea Mayer if (seg6_end_dt6_get_mode(slwt) == DT_LEGACY_MODE) 120620a081b7SAndrea Mayer goto legacy_mode; 120720a081b7SAndrea Mayer 120820a081b7SAndrea Mayer /* DT6_VRF_MODE */ 12098b532109SAndrea Mayer skb = end_dt_vrf_core(skb, slwt, AF_INET6); 121020a081b7SAndrea Mayer if (!skb) 121120a081b7SAndrea Mayer /* packet has been processed and consumed by the VRF */ 121220a081b7SAndrea Mayer return 0; 121320a081b7SAndrea Mayer 121420a081b7SAndrea Mayer if (IS_ERR(skb)) 121520a081b7SAndrea Mayer return PTR_ERR(skb); 121620a081b7SAndrea Mayer 121720a081b7SAndrea Mayer /* note: this time we do not need to specify the table because the VRF 121820a081b7SAndrea Mayer * takes care of selecting the correct table. 121920a081b7SAndrea Mayer */ 122020a081b7SAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, 0, true); 122120a081b7SAndrea Mayer 122220a081b7SAndrea Mayer return dst_input(skb); 122320a081b7SAndrea Mayer 122420a081b7SAndrea Mayer legacy_mode: 122520a081b7SAndrea Mayer #endif 1226c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 1227c71644d0SAndrea Mayer 1228fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, slwt->table, true); 1229891ef8ddSDavid Lebrun 1230891ef8ddSDavid Lebrun return dst_input(skb); 1231891ef8ddSDavid Lebrun 1232891ef8ddSDavid Lebrun drop: 1233891ef8ddSDavid Lebrun kfree_skb(skb); 1234891ef8ddSDavid Lebrun return -EINVAL; 1235891ef8ddSDavid Lebrun } 1236891ef8ddSDavid Lebrun 12378b532109SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 12388b532109SAndrea Mayer static int seg6_end_dt46_build(struct seg6_local_lwt *slwt, const void *cfg, 12398b532109SAndrea Mayer struct netlink_ext_ack *extack) 12408b532109SAndrea Mayer { 12418b532109SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_UNSPEC, extack); 12428b532109SAndrea Mayer } 12438b532109SAndrea Mayer 12448b532109SAndrea Mayer static int input_action_end_dt46(struct sk_buff *skb, 12458b532109SAndrea Mayer struct seg6_local_lwt *slwt) 12468b532109SAndrea Mayer { 12478b532109SAndrea Mayer unsigned int off = 0; 12488b532109SAndrea Mayer int nexthdr; 12498b532109SAndrea Mayer 12508b532109SAndrea Mayer nexthdr = ipv6_find_hdr(skb, &off, -1, NULL, NULL); 12518b532109SAndrea Mayer if (unlikely(nexthdr < 0)) 12528b532109SAndrea Mayer goto drop; 12538b532109SAndrea Mayer 12548b532109SAndrea Mayer switch (nexthdr) { 12558b532109SAndrea Mayer case IPPROTO_IPIP: 12568b532109SAndrea Mayer return input_action_end_dt4(skb, slwt); 12578b532109SAndrea Mayer case IPPROTO_IPV6: 12588b532109SAndrea Mayer return input_action_end_dt6(skb, slwt); 12598b532109SAndrea Mayer } 12608b532109SAndrea Mayer 12618b532109SAndrea Mayer drop: 12628b532109SAndrea Mayer kfree_skb(skb); 12638b532109SAndrea Mayer return -EINVAL; 12648b532109SAndrea Mayer } 12658b532109SAndrea Mayer #endif 12668b532109SAndrea Mayer 1267140f04c3SDavid Lebrun /* push an SRH on top of the current one */ 1268140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1269140f04c3SDavid Lebrun { 1270140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 1271140f04c3SDavid Lebrun int err = -EINVAL; 1272140f04c3SDavid Lebrun 1273140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 1274140f04c3SDavid Lebrun if (!srh) 1275140f04c3SDavid Lebrun goto drop; 1276140f04c3SDavid Lebrun 1277140f04c3SDavid Lebrun err = seg6_do_srh_inline(skb, slwt->srh); 1278140f04c3SDavid Lebrun if (err) 1279140f04c3SDavid Lebrun goto drop; 1280140f04c3SDavid Lebrun 1281140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 1282140f04c3SDavid Lebrun 12831c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 1284140f04c3SDavid Lebrun 1285140f04c3SDavid Lebrun return dst_input(skb); 1286140f04c3SDavid Lebrun 1287140f04c3SDavid Lebrun drop: 1288140f04c3SDavid Lebrun kfree_skb(skb); 1289140f04c3SDavid Lebrun return err; 1290140f04c3SDavid Lebrun } 1291140f04c3SDavid Lebrun 1292140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */ 1293140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb, 1294140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 1295140f04c3SDavid Lebrun { 1296140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 1297140f04c3SDavid Lebrun int err = -EINVAL; 1298140f04c3SDavid Lebrun 1299140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 1300140f04c3SDavid Lebrun if (!srh) 1301140f04c3SDavid Lebrun goto drop; 1302140f04c3SDavid Lebrun 1303d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 1304140f04c3SDavid Lebrun 1305140f04c3SDavid Lebrun skb_reset_inner_headers(skb); 1306140f04c3SDavid Lebrun skb->encapsulation = 1; 1307140f04c3SDavid Lebrun 130832d99d0bSDavid Lebrun err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6); 1309140f04c3SDavid Lebrun if (err) 1310140f04c3SDavid Lebrun goto drop; 1311140f04c3SDavid Lebrun 1312140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 1313140f04c3SDavid Lebrun 13141c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 1315140f04c3SDavid Lebrun 1316140f04c3SDavid Lebrun return dst_input(skb); 1317140f04c3SDavid Lebrun 1318140f04c3SDavid Lebrun drop: 1319140f04c3SDavid Lebrun kfree_skb(skb); 1320140f04c3SDavid Lebrun return err; 1321140f04c3SDavid Lebrun } 1322140f04c3SDavid Lebrun 1323fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states); 1324fe94cc29SMathieu Xhonneux 1325486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb) 1326486cdf21SMathieu Xhonneux { 1327486cdf21SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 1328486cdf21SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 1329486cdf21SMathieu Xhonneux struct ipv6_sr_hdr *srh = srh_state->srh; 1330486cdf21SMathieu Xhonneux 1331486cdf21SMathieu Xhonneux if (unlikely(srh == NULL)) 1332486cdf21SMathieu Xhonneux return false; 1333486cdf21SMathieu Xhonneux 1334486cdf21SMathieu Xhonneux if (unlikely(!srh_state->valid)) { 1335486cdf21SMathieu Xhonneux if ((srh_state->hdrlen & 7) != 0) 1336486cdf21SMathieu Xhonneux return false; 1337486cdf21SMathieu Xhonneux 1338486cdf21SMathieu Xhonneux srh->hdrlen = (u8)(srh_state->hdrlen >> 3); 1339bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true)) 1340486cdf21SMathieu Xhonneux return false; 1341486cdf21SMathieu Xhonneux 1342486cdf21SMathieu Xhonneux srh_state->valid = true; 1343486cdf21SMathieu Xhonneux } 1344486cdf21SMathieu Xhonneux 1345486cdf21SMathieu Xhonneux return true; 1346486cdf21SMathieu Xhonneux } 1347486cdf21SMathieu Xhonneux 1348004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb, 1349004d4b27SMathieu Xhonneux struct seg6_local_lwt *slwt) 1350004d4b27SMathieu Xhonneux { 1351004d4b27SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 1352004d4b27SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 1353004d4b27SMathieu Xhonneux struct ipv6_sr_hdr *srh; 1354004d4b27SMathieu Xhonneux int ret; 1355004d4b27SMathieu Xhonneux 1356004d4b27SMathieu Xhonneux srh = get_and_validate_srh(skb); 1357486cdf21SMathieu Xhonneux if (!srh) { 1358486cdf21SMathieu Xhonneux kfree_skb(skb); 1359486cdf21SMathieu Xhonneux return -EINVAL; 1360486cdf21SMathieu Xhonneux } 1361004d4b27SMathieu Xhonneux advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 1362004d4b27SMathieu Xhonneux 1363004d4b27SMathieu Xhonneux /* preempt_disable is needed to protect the per-CPU buffer srh_state, 1364004d4b27SMathieu Xhonneux * which is also accessed by the bpf_lwt_seg6_* helpers 1365004d4b27SMathieu Xhonneux */ 1366004d4b27SMathieu Xhonneux preempt_disable(); 1367486cdf21SMathieu Xhonneux srh_state->srh = srh; 1368004d4b27SMathieu Xhonneux srh_state->hdrlen = srh->hdrlen << 3; 1369486cdf21SMathieu Xhonneux srh_state->valid = true; 1370004d4b27SMathieu Xhonneux 1371004d4b27SMathieu Xhonneux rcu_read_lock(); 1372004d4b27SMathieu Xhonneux bpf_compute_data_pointers(skb); 1373004d4b27SMathieu Xhonneux ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb); 1374004d4b27SMathieu Xhonneux rcu_read_unlock(); 1375004d4b27SMathieu Xhonneux 1376004d4b27SMathieu Xhonneux switch (ret) { 1377004d4b27SMathieu Xhonneux case BPF_OK: 1378004d4b27SMathieu Xhonneux case BPF_REDIRECT: 1379004d4b27SMathieu Xhonneux break; 1380004d4b27SMathieu Xhonneux case BPF_DROP: 1381004d4b27SMathieu Xhonneux goto drop; 1382004d4b27SMathieu Xhonneux default: 1383004d4b27SMathieu Xhonneux pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret); 1384004d4b27SMathieu Xhonneux goto drop; 1385004d4b27SMathieu Xhonneux } 1386004d4b27SMathieu Xhonneux 1387486cdf21SMathieu Xhonneux if (srh_state->srh && !seg6_bpf_has_valid_srh(skb)) 1388004d4b27SMathieu Xhonneux goto drop; 1389004d4b27SMathieu Xhonneux 1390486cdf21SMathieu Xhonneux preempt_enable(); 1391004d4b27SMathieu Xhonneux if (ret != BPF_REDIRECT) 1392004d4b27SMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 1393004d4b27SMathieu Xhonneux 1394004d4b27SMathieu Xhonneux return dst_input(skb); 1395004d4b27SMathieu Xhonneux 1396004d4b27SMathieu Xhonneux drop: 1397486cdf21SMathieu Xhonneux preempt_enable(); 1398004d4b27SMathieu Xhonneux kfree_skb(skb); 1399004d4b27SMathieu Xhonneux return -EINVAL; 1400004d4b27SMathieu Xhonneux } 1401004d4b27SMathieu Xhonneux 1402d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = { 1403d1df6fd8SDavid Lebrun { 1404d1df6fd8SDavid Lebrun .action = SEG6_LOCAL_ACTION_END, 1405d1df6fd8SDavid Lebrun .attrs = 0, 1406848f3c0dSAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS | 1407848f3c0dSAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_FLAVORS), 1408140f04c3SDavid Lebrun .input = input_action_end, 1409d1df6fd8SDavid Lebrun }, 1410140f04c3SDavid Lebrun { 1411140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_X, 1412300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), 141394604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1414140f04c3SDavid Lebrun .input = input_action_end_x, 1415140f04c3SDavid Lebrun }, 1416140f04c3SDavid Lebrun { 1417891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_T, 1418300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE), 141994604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1420891ef8ddSDavid Lebrun .input = input_action_end_t, 1421891ef8ddSDavid Lebrun }, 1422891ef8ddSDavid Lebrun { 1423891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX2, 1424300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_OIF), 142594604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1426891ef8ddSDavid Lebrun .input = input_action_end_dx2, 1427891ef8ddSDavid Lebrun }, 1428891ef8ddSDavid Lebrun { 1429140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX6, 1430300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), 143194604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1432140f04c3SDavid Lebrun .input = input_action_end_dx6, 1433140f04c3SDavid Lebrun }, 1434140f04c3SDavid Lebrun { 1435891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX4, 1436300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH4), 143794604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1438891ef8ddSDavid Lebrun .input = input_action_end_dx4, 1439891ef8ddSDavid Lebrun }, 1440891ef8ddSDavid Lebrun { 1441664d6f86SAndrea Mayer .action = SEG6_LOCAL_ACTION_END_DT4, 1442300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 144394604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1444664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 1445664d6f86SAndrea Mayer .input = input_action_end_dt4, 1446664d6f86SAndrea Mayer .slwt_ops = { 1447664d6f86SAndrea Mayer .build_state = seg6_end_dt4_build, 1448664d6f86SAndrea Mayer }, 1449664d6f86SAndrea Mayer #endif 1450664d6f86SAndrea Mayer }, 1451664d6f86SAndrea Mayer { 1452891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DT6, 145320a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 145420a081b7SAndrea Mayer .attrs = 0, 145594604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS | 145694604548SAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_TABLE) | 1457300a0fd8SAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 145820a081b7SAndrea Mayer .slwt_ops = { 145920a081b7SAndrea Mayer .build_state = seg6_end_dt6_build, 146020a081b7SAndrea Mayer }, 146120a081b7SAndrea Mayer #else 1462300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE), 146394604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 146420a081b7SAndrea Mayer #endif 1465891ef8ddSDavid Lebrun .input = input_action_end_dt6, 1466891ef8ddSDavid Lebrun }, 1467891ef8ddSDavid Lebrun { 14688b532109SAndrea Mayer .action = SEG6_LOCAL_ACTION_END_DT46, 14698b532109SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 14708b532109SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 14718b532109SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 14728b532109SAndrea Mayer .input = input_action_end_dt46, 14738b532109SAndrea Mayer .slwt_ops = { 14748b532109SAndrea Mayer .build_state = seg6_end_dt46_build, 14758b532109SAndrea Mayer }, 14768b532109SAndrea Mayer #endif 14778b532109SAndrea Mayer }, 14788b532109SAndrea Mayer { 1479140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6, 1480300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH), 148194604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1482140f04c3SDavid Lebrun .input = input_action_end_b6, 1483140f04c3SDavid Lebrun }, 1484140f04c3SDavid Lebrun { 1485140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6_ENCAP, 1486300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH), 148794604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1488140f04c3SDavid Lebrun .input = input_action_end_b6_encap, 1489140f04c3SDavid Lebrun .static_headroom = sizeof(struct ipv6hdr), 1490004d4b27SMathieu Xhonneux }, 1491004d4b27SMathieu Xhonneux { 1492004d4b27SMathieu Xhonneux .action = SEG6_LOCAL_ACTION_END_BPF, 1493300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_BPF), 149494604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1495004d4b27SMathieu Xhonneux .input = input_action_end_bpf, 1496004d4b27SMathieu Xhonneux }, 1497004d4b27SMathieu Xhonneux 1498d1df6fd8SDavid Lebrun }; 1499d1df6fd8SDavid Lebrun 1500d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action) 1501d1df6fd8SDavid Lebrun { 1502d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1503d1df6fd8SDavid Lebrun int i, count; 1504d1df6fd8SDavid Lebrun 1505709af180SColin Ian King count = ARRAY_SIZE(seg6_action_table); 1506d1df6fd8SDavid Lebrun for (i = 0; i < count; i++) { 1507d1df6fd8SDavid Lebrun desc = &seg6_action_table[i]; 1508d1df6fd8SDavid Lebrun if (desc->action == action) 1509d1df6fd8SDavid Lebrun return desc; 1510d1df6fd8SDavid Lebrun } 1511d1df6fd8SDavid Lebrun 1512d1df6fd8SDavid Lebrun return NULL; 1513d1df6fd8SDavid Lebrun } 1514d1df6fd8SDavid Lebrun 151594604548SAndrea Mayer static bool seg6_lwtunnel_counters_enabled(struct seg6_local_lwt *slwt) 151694604548SAndrea Mayer { 151794604548SAndrea Mayer return slwt->parsed_optattrs & SEG6_F_LOCAL_COUNTERS; 151894604548SAndrea Mayer } 151994604548SAndrea Mayer 152094604548SAndrea Mayer static void seg6_local_update_counters(struct seg6_local_lwt *slwt, 152194604548SAndrea Mayer unsigned int len, int err) 152294604548SAndrea Mayer { 152394604548SAndrea Mayer struct pcpu_seg6_local_counters *pcounters; 152494604548SAndrea Mayer 152594604548SAndrea Mayer pcounters = this_cpu_ptr(slwt->pcpu_counters); 152694604548SAndrea Mayer u64_stats_update_begin(&pcounters->syncp); 152794604548SAndrea Mayer 152894604548SAndrea Mayer if (likely(!err)) { 152994604548SAndrea Mayer u64_stats_inc(&pcounters->packets); 153094604548SAndrea Mayer u64_stats_add(&pcounters->bytes, len); 153194604548SAndrea Mayer } else { 153294604548SAndrea Mayer u64_stats_inc(&pcounters->errors); 153394604548SAndrea Mayer } 153494604548SAndrea Mayer 153594604548SAndrea Mayer u64_stats_update_end(&pcounters->syncp); 153694604548SAndrea Mayer } 153794604548SAndrea Mayer 15387a3f5b0dSRyoga Saito static int seg6_local_input_core(struct net *net, struct sock *sk, 15397a3f5b0dSRyoga Saito struct sk_buff *skb) 1540d1df6fd8SDavid Lebrun { 1541d1df6fd8SDavid Lebrun struct dst_entry *orig_dst = skb_dst(skb); 1542d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1543d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 154494604548SAndrea Mayer unsigned int len = skb->len; 154594604548SAndrea Mayer int rc; 1546d1df6fd8SDavid Lebrun 1547d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 1548d1df6fd8SDavid Lebrun desc = slwt->desc; 1549d1df6fd8SDavid Lebrun 155094604548SAndrea Mayer rc = desc->input(skb, slwt); 155194604548SAndrea Mayer 155294604548SAndrea Mayer if (!seg6_lwtunnel_counters_enabled(slwt)) 155394604548SAndrea Mayer return rc; 155494604548SAndrea Mayer 155594604548SAndrea Mayer seg6_local_update_counters(slwt, len, rc); 155694604548SAndrea Mayer 155794604548SAndrea Mayer return rc; 1558d1df6fd8SDavid Lebrun } 1559d1df6fd8SDavid Lebrun 15607a3f5b0dSRyoga Saito static int seg6_local_input(struct sk_buff *skb) 15617a3f5b0dSRyoga Saito { 15627a3f5b0dSRyoga Saito if (skb->protocol != htons(ETH_P_IPV6)) { 15637a3f5b0dSRyoga Saito kfree_skb(skb); 15647a3f5b0dSRyoga Saito return -EINVAL; 15657a3f5b0dSRyoga Saito } 15667a3f5b0dSRyoga Saito 15677a3f5b0dSRyoga Saito if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 15687a3f5b0dSRyoga Saito return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN, 15697a3f5b0dSRyoga Saito dev_net(skb->dev), NULL, skb, skb->dev, NULL, 15707a3f5b0dSRyoga Saito seg6_local_input_core); 15717a3f5b0dSRyoga Saito 15727a3f5b0dSRyoga Saito return seg6_local_input_core(dev_net(skb->dev), NULL, skb); 15737a3f5b0dSRyoga Saito } 15747a3f5b0dSRyoga Saito 1575d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { 1576d1df6fd8SDavid Lebrun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, 1577d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, 1578d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, 1579664d6f86SAndrea Mayer [SEG6_LOCAL_VRFTABLE] = { .type = NLA_U32 }, 1580d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, 1581d1df6fd8SDavid Lebrun .len = sizeof(struct in_addr) }, 1582d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, 1583d1df6fd8SDavid Lebrun .len = sizeof(struct in6_addr) }, 1584d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, 1585d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 1586004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .type = NLA_NESTED }, 158794604548SAndrea Mayer [SEG6_LOCAL_COUNTERS] = { .type = NLA_NESTED }, 1588848f3c0dSAndrea Mayer [SEG6_LOCAL_FLAVORS] = { .type = NLA_NESTED }, 1589d1df6fd8SDavid Lebrun }; 1590d1df6fd8SDavid Lebrun 1591e2a8ecc4SAndrea Mayer static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1592e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 15932d9cc60aSDavid Lebrun { 15942d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 15952d9cc60aSDavid Lebrun int len; 15962d9cc60aSDavid Lebrun 15972d9cc60aSDavid Lebrun srh = nla_data(attrs[SEG6_LOCAL_SRH]); 15982d9cc60aSDavid Lebrun len = nla_len(attrs[SEG6_LOCAL_SRH]); 15992d9cc60aSDavid Lebrun 16002d9cc60aSDavid Lebrun /* SRH must contain at least one segment */ 16012d9cc60aSDavid Lebrun if (len < sizeof(*srh) + sizeof(struct in6_addr)) 16022d9cc60aSDavid Lebrun return -EINVAL; 16032d9cc60aSDavid Lebrun 1604bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, false)) 16052d9cc60aSDavid Lebrun return -EINVAL; 16062d9cc60aSDavid Lebrun 16077fa41efaSYueHaibing slwt->srh = kmemdup(srh, len, GFP_KERNEL); 16082d9cc60aSDavid Lebrun if (!slwt->srh) 16092d9cc60aSDavid Lebrun return -ENOMEM; 16102d9cc60aSDavid Lebrun 16112d9cc60aSDavid Lebrun slwt->headroom += len; 16122d9cc60aSDavid Lebrun 16132d9cc60aSDavid Lebrun return 0; 16142d9cc60aSDavid Lebrun } 16152d9cc60aSDavid Lebrun 16162d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt) 16172d9cc60aSDavid Lebrun { 16182d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 16192d9cc60aSDavid Lebrun struct nlattr *nla; 16202d9cc60aSDavid Lebrun int len; 16212d9cc60aSDavid Lebrun 16222d9cc60aSDavid Lebrun srh = slwt->srh; 16232d9cc60aSDavid Lebrun len = (srh->hdrlen + 1) << 3; 16242d9cc60aSDavid Lebrun 16252d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_SRH, len); 16262d9cc60aSDavid Lebrun if (!nla) 16272d9cc60aSDavid Lebrun return -EMSGSIZE; 16282d9cc60aSDavid Lebrun 16292d9cc60aSDavid Lebrun memcpy(nla_data(nla), srh, len); 16302d9cc60aSDavid Lebrun 16312d9cc60aSDavid Lebrun return 0; 16322d9cc60aSDavid Lebrun } 16332d9cc60aSDavid Lebrun 16342d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 16352d9cc60aSDavid Lebrun { 16362d9cc60aSDavid Lebrun int len = (a->srh->hdrlen + 1) << 3; 16372d9cc60aSDavid Lebrun 16382d9cc60aSDavid Lebrun if (len != ((b->srh->hdrlen + 1) << 3)) 16392d9cc60aSDavid Lebrun return 1; 16402d9cc60aSDavid Lebrun 16412d9cc60aSDavid Lebrun return memcmp(a->srh, b->srh, len); 16422d9cc60aSDavid Lebrun } 16432d9cc60aSDavid Lebrun 1644964adce5SAndrea Mayer static void destroy_attr_srh(struct seg6_local_lwt *slwt) 1645964adce5SAndrea Mayer { 1646964adce5SAndrea Mayer kfree(slwt->srh); 1647964adce5SAndrea Mayer } 1648964adce5SAndrea Mayer 1649e2a8ecc4SAndrea Mayer static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1650e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 16512d9cc60aSDavid Lebrun { 16522d9cc60aSDavid Lebrun slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]); 16532d9cc60aSDavid Lebrun 16542d9cc60aSDavid Lebrun return 0; 16552d9cc60aSDavid Lebrun } 16562d9cc60aSDavid Lebrun 16572d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt) 16582d9cc60aSDavid Lebrun { 16592d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table)) 16602d9cc60aSDavid Lebrun return -EMSGSIZE; 16612d9cc60aSDavid Lebrun 16622d9cc60aSDavid Lebrun return 0; 16632d9cc60aSDavid Lebrun } 16642d9cc60aSDavid Lebrun 16652d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 16662d9cc60aSDavid Lebrun { 16672d9cc60aSDavid Lebrun if (a->table != b->table) 16682d9cc60aSDavid Lebrun return 1; 16692d9cc60aSDavid Lebrun 16702d9cc60aSDavid Lebrun return 0; 16712d9cc60aSDavid Lebrun } 16722d9cc60aSDavid Lebrun 1673664d6f86SAndrea Mayer static struct 1674664d6f86SAndrea Mayer seg6_end_dt_info *seg6_possible_end_dt_info(struct seg6_local_lwt *slwt) 1675664d6f86SAndrea Mayer { 1676664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 1677664d6f86SAndrea Mayer return &slwt->dt_info; 1678664d6f86SAndrea Mayer #else 1679664d6f86SAndrea Mayer return ERR_PTR(-EOPNOTSUPP); 1680664d6f86SAndrea Mayer #endif 1681664d6f86SAndrea Mayer } 1682664d6f86SAndrea Mayer 1683664d6f86SAndrea Mayer static int parse_nla_vrftable(struct nlattr **attrs, 1684e2a8ecc4SAndrea Mayer struct seg6_local_lwt *slwt, 1685e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 1686664d6f86SAndrea Mayer { 1687664d6f86SAndrea Mayer struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt); 1688664d6f86SAndrea Mayer 1689664d6f86SAndrea Mayer if (IS_ERR(info)) 1690664d6f86SAndrea Mayer return PTR_ERR(info); 1691664d6f86SAndrea Mayer 1692664d6f86SAndrea Mayer info->vrf_table = nla_get_u32(attrs[SEG6_LOCAL_VRFTABLE]); 1693664d6f86SAndrea Mayer 1694664d6f86SAndrea Mayer return 0; 1695664d6f86SAndrea Mayer } 1696664d6f86SAndrea Mayer 1697664d6f86SAndrea Mayer static int put_nla_vrftable(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1698664d6f86SAndrea Mayer { 1699664d6f86SAndrea Mayer struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt); 1700664d6f86SAndrea Mayer 1701664d6f86SAndrea Mayer if (IS_ERR(info)) 1702664d6f86SAndrea Mayer return PTR_ERR(info); 1703664d6f86SAndrea Mayer 1704664d6f86SAndrea Mayer if (nla_put_u32(skb, SEG6_LOCAL_VRFTABLE, info->vrf_table)) 1705664d6f86SAndrea Mayer return -EMSGSIZE; 1706664d6f86SAndrea Mayer 1707664d6f86SAndrea Mayer return 0; 1708664d6f86SAndrea Mayer } 1709664d6f86SAndrea Mayer 1710664d6f86SAndrea Mayer static int cmp_nla_vrftable(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1711664d6f86SAndrea Mayer { 1712664d6f86SAndrea Mayer struct seg6_end_dt_info *info_a = seg6_possible_end_dt_info(a); 1713664d6f86SAndrea Mayer struct seg6_end_dt_info *info_b = seg6_possible_end_dt_info(b); 1714664d6f86SAndrea Mayer 1715664d6f86SAndrea Mayer if (info_a->vrf_table != info_b->vrf_table) 1716664d6f86SAndrea Mayer return 1; 1717664d6f86SAndrea Mayer 1718664d6f86SAndrea Mayer return 0; 1719664d6f86SAndrea Mayer } 1720664d6f86SAndrea Mayer 1721e2a8ecc4SAndrea Mayer static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1722e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 17232d9cc60aSDavid Lebrun { 17242d9cc60aSDavid Lebrun memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]), 17252d9cc60aSDavid Lebrun sizeof(struct in_addr)); 17262d9cc60aSDavid Lebrun 17272d9cc60aSDavid Lebrun return 0; 17282d9cc60aSDavid Lebrun } 17292d9cc60aSDavid Lebrun 17302d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt) 17312d9cc60aSDavid Lebrun { 17322d9cc60aSDavid Lebrun struct nlattr *nla; 17332d9cc60aSDavid Lebrun 17342d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr)); 17352d9cc60aSDavid Lebrun if (!nla) 17362d9cc60aSDavid Lebrun return -EMSGSIZE; 17372d9cc60aSDavid Lebrun 17382d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr)); 17392d9cc60aSDavid Lebrun 17402d9cc60aSDavid Lebrun return 0; 17412d9cc60aSDavid Lebrun } 17422d9cc60aSDavid Lebrun 17432d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 17442d9cc60aSDavid Lebrun { 17452d9cc60aSDavid Lebrun return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr)); 17462d9cc60aSDavid Lebrun } 17472d9cc60aSDavid Lebrun 1748e2a8ecc4SAndrea Mayer static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1749e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 17502d9cc60aSDavid Lebrun { 17512d9cc60aSDavid Lebrun memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]), 17522d9cc60aSDavid Lebrun sizeof(struct in6_addr)); 17532d9cc60aSDavid Lebrun 17542d9cc60aSDavid Lebrun return 0; 17552d9cc60aSDavid Lebrun } 17562d9cc60aSDavid Lebrun 17572d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 17582d9cc60aSDavid Lebrun { 17592d9cc60aSDavid Lebrun struct nlattr *nla; 17602d9cc60aSDavid Lebrun 17612d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr)); 17622d9cc60aSDavid Lebrun if (!nla) 17632d9cc60aSDavid Lebrun return -EMSGSIZE; 17642d9cc60aSDavid Lebrun 17652d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr)); 17662d9cc60aSDavid Lebrun 17672d9cc60aSDavid Lebrun return 0; 17682d9cc60aSDavid Lebrun } 17692d9cc60aSDavid Lebrun 17702d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 17712d9cc60aSDavid Lebrun { 17722d9cc60aSDavid Lebrun return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr)); 17732d9cc60aSDavid Lebrun } 17742d9cc60aSDavid Lebrun 1775e2a8ecc4SAndrea Mayer static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1776e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 17772d9cc60aSDavid Lebrun { 17782d9cc60aSDavid Lebrun slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]); 17792d9cc60aSDavid Lebrun 17802d9cc60aSDavid Lebrun return 0; 17812d9cc60aSDavid Lebrun } 17822d9cc60aSDavid Lebrun 17832d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 17842d9cc60aSDavid Lebrun { 17852d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif)) 17862d9cc60aSDavid Lebrun return -EMSGSIZE; 17872d9cc60aSDavid Lebrun 17882d9cc60aSDavid Lebrun return 0; 17892d9cc60aSDavid Lebrun } 17902d9cc60aSDavid Lebrun 17912d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 17922d9cc60aSDavid Lebrun { 17932d9cc60aSDavid Lebrun if (a->iif != b->iif) 17942d9cc60aSDavid Lebrun return 1; 17952d9cc60aSDavid Lebrun 17962d9cc60aSDavid Lebrun return 0; 17972d9cc60aSDavid Lebrun } 17982d9cc60aSDavid Lebrun 1799e2a8ecc4SAndrea Mayer static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1800e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 18012d9cc60aSDavid Lebrun { 18022d9cc60aSDavid Lebrun slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]); 18032d9cc60aSDavid Lebrun 18042d9cc60aSDavid Lebrun return 0; 18052d9cc60aSDavid Lebrun } 18062d9cc60aSDavid Lebrun 18072d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 18082d9cc60aSDavid Lebrun { 18092d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif)) 18102d9cc60aSDavid Lebrun return -EMSGSIZE; 18112d9cc60aSDavid Lebrun 18122d9cc60aSDavid Lebrun return 0; 18132d9cc60aSDavid Lebrun } 18142d9cc60aSDavid Lebrun 18152d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 18162d9cc60aSDavid Lebrun { 18172d9cc60aSDavid Lebrun if (a->oif != b->oif) 18182d9cc60aSDavid Lebrun return 1; 18192d9cc60aSDavid Lebrun 18202d9cc60aSDavid Lebrun return 0; 18212d9cc60aSDavid Lebrun } 18222d9cc60aSDavid Lebrun 1823004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256 1824004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = { 1825004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG] = { .type = NLA_U32, }, 1826004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING, 1827004d4b27SMathieu Xhonneux .len = MAX_PROG_NAME }, 1828004d4b27SMathieu Xhonneux }; 1829004d4b27SMathieu Xhonneux 1830e2a8ecc4SAndrea Mayer static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1831e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 1832004d4b27SMathieu Xhonneux { 1833004d4b27SMathieu Xhonneux struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1]; 1834004d4b27SMathieu Xhonneux struct bpf_prog *p; 1835004d4b27SMathieu Xhonneux int ret; 1836004d4b27SMathieu Xhonneux u32 fd; 1837004d4b27SMathieu Xhonneux 18388cb08174SJohannes Berg ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX, 18398cb08174SJohannes Berg attrs[SEG6_LOCAL_BPF], 18408cb08174SJohannes Berg bpf_prog_policy, NULL); 1841004d4b27SMathieu Xhonneux if (ret < 0) 1842004d4b27SMathieu Xhonneux return ret; 1843004d4b27SMathieu Xhonneux 1844004d4b27SMathieu Xhonneux if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME]) 1845004d4b27SMathieu Xhonneux return -EINVAL; 1846004d4b27SMathieu Xhonneux 1847004d4b27SMathieu Xhonneux slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL); 1848004d4b27SMathieu Xhonneux if (!slwt->bpf.name) 1849004d4b27SMathieu Xhonneux return -ENOMEM; 1850004d4b27SMathieu Xhonneux 1851004d4b27SMathieu Xhonneux fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]); 1852004d4b27SMathieu Xhonneux p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL); 1853004d4b27SMathieu Xhonneux if (IS_ERR(p)) { 1854004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 1855004d4b27SMathieu Xhonneux return PTR_ERR(p); 1856004d4b27SMathieu Xhonneux } 1857004d4b27SMathieu Xhonneux 1858004d4b27SMathieu Xhonneux slwt->bpf.prog = p; 1859004d4b27SMathieu Xhonneux return 0; 1860004d4b27SMathieu Xhonneux } 1861004d4b27SMathieu Xhonneux 1862004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1863004d4b27SMathieu Xhonneux { 1864004d4b27SMathieu Xhonneux struct nlattr *nest; 1865004d4b27SMathieu Xhonneux 1866004d4b27SMathieu Xhonneux if (!slwt->bpf.prog) 1867004d4b27SMathieu Xhonneux return 0; 1868004d4b27SMathieu Xhonneux 1869ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF); 1870004d4b27SMathieu Xhonneux if (!nest) 1871004d4b27SMathieu Xhonneux return -EMSGSIZE; 1872004d4b27SMathieu Xhonneux 1873004d4b27SMathieu Xhonneux if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id)) 1874004d4b27SMathieu Xhonneux return -EMSGSIZE; 1875004d4b27SMathieu Xhonneux 1876004d4b27SMathieu Xhonneux if (slwt->bpf.name && 1877004d4b27SMathieu Xhonneux nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name)) 1878004d4b27SMathieu Xhonneux return -EMSGSIZE; 1879004d4b27SMathieu Xhonneux 1880004d4b27SMathieu Xhonneux return nla_nest_end(skb, nest); 1881004d4b27SMathieu Xhonneux } 1882004d4b27SMathieu Xhonneux 1883004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1884004d4b27SMathieu Xhonneux { 1885004d4b27SMathieu Xhonneux if (!a->bpf.name && !b->bpf.name) 1886004d4b27SMathieu Xhonneux return 0; 1887004d4b27SMathieu Xhonneux 1888004d4b27SMathieu Xhonneux if (!a->bpf.name || !b->bpf.name) 1889004d4b27SMathieu Xhonneux return 1; 1890004d4b27SMathieu Xhonneux 1891004d4b27SMathieu Xhonneux return strcmp(a->bpf.name, b->bpf.name); 1892004d4b27SMathieu Xhonneux } 1893004d4b27SMathieu Xhonneux 1894964adce5SAndrea Mayer static void destroy_attr_bpf(struct seg6_local_lwt *slwt) 1895964adce5SAndrea Mayer { 1896964adce5SAndrea Mayer kfree(slwt->bpf.name); 1897964adce5SAndrea Mayer if (slwt->bpf.prog) 1898964adce5SAndrea Mayer bpf_prog_put(slwt->bpf.prog); 1899964adce5SAndrea Mayer } 1900964adce5SAndrea Mayer 190194604548SAndrea Mayer static const struct 190294604548SAndrea Mayer nla_policy seg6_local_counters_policy[SEG6_LOCAL_CNT_MAX + 1] = { 190394604548SAndrea Mayer [SEG6_LOCAL_CNT_PACKETS] = { .type = NLA_U64 }, 190494604548SAndrea Mayer [SEG6_LOCAL_CNT_BYTES] = { .type = NLA_U64 }, 190594604548SAndrea Mayer [SEG6_LOCAL_CNT_ERRORS] = { .type = NLA_U64 }, 190694604548SAndrea Mayer }; 190794604548SAndrea Mayer 190894604548SAndrea Mayer static int parse_nla_counters(struct nlattr **attrs, 1909e2a8ecc4SAndrea Mayer struct seg6_local_lwt *slwt, 1910e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 191194604548SAndrea Mayer { 191294604548SAndrea Mayer struct pcpu_seg6_local_counters __percpu *pcounters; 191394604548SAndrea Mayer struct nlattr *tb[SEG6_LOCAL_CNT_MAX + 1]; 191494604548SAndrea Mayer int ret; 191594604548SAndrea Mayer 191694604548SAndrea Mayer ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_CNT_MAX, 191794604548SAndrea Mayer attrs[SEG6_LOCAL_COUNTERS], 191894604548SAndrea Mayer seg6_local_counters_policy, NULL); 191994604548SAndrea Mayer if (ret < 0) 192094604548SAndrea Mayer return ret; 192194604548SAndrea Mayer 192294604548SAndrea Mayer /* basic support for SRv6 Behavior counters requires at least: 192394604548SAndrea Mayer * packets, bytes and errors. 192494604548SAndrea Mayer */ 192594604548SAndrea Mayer if (!tb[SEG6_LOCAL_CNT_PACKETS] || !tb[SEG6_LOCAL_CNT_BYTES] || 192694604548SAndrea Mayer !tb[SEG6_LOCAL_CNT_ERRORS]) 192794604548SAndrea Mayer return -EINVAL; 192894604548SAndrea Mayer 192994604548SAndrea Mayer /* counters are always zero initialized */ 193094604548SAndrea Mayer pcounters = seg6_local_alloc_pcpu_counters(GFP_KERNEL); 193194604548SAndrea Mayer if (!pcounters) 193294604548SAndrea Mayer return -ENOMEM; 193394604548SAndrea Mayer 193494604548SAndrea Mayer slwt->pcpu_counters = pcounters; 193594604548SAndrea Mayer 193694604548SAndrea Mayer return 0; 193794604548SAndrea Mayer } 193894604548SAndrea Mayer 193994604548SAndrea Mayer static int seg6_local_fill_nla_counters(struct sk_buff *skb, 194094604548SAndrea Mayer struct seg6_local_counters *counters) 194194604548SAndrea Mayer { 194294604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_PACKETS, counters->packets, 194394604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 194494604548SAndrea Mayer return -EMSGSIZE; 194594604548SAndrea Mayer 194694604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_BYTES, counters->bytes, 194794604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 194894604548SAndrea Mayer return -EMSGSIZE; 194994604548SAndrea Mayer 195094604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_ERRORS, counters->errors, 195194604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 195294604548SAndrea Mayer return -EMSGSIZE; 195394604548SAndrea Mayer 195494604548SAndrea Mayer return 0; 195594604548SAndrea Mayer } 195694604548SAndrea Mayer 195794604548SAndrea Mayer static int put_nla_counters(struct sk_buff *skb, struct seg6_local_lwt *slwt) 195894604548SAndrea Mayer { 195994604548SAndrea Mayer struct seg6_local_counters counters = { 0, 0, 0 }; 196094604548SAndrea Mayer struct nlattr *nest; 196194604548SAndrea Mayer int rc, i; 196294604548SAndrea Mayer 196394604548SAndrea Mayer nest = nla_nest_start(skb, SEG6_LOCAL_COUNTERS); 196494604548SAndrea Mayer if (!nest) 196594604548SAndrea Mayer return -EMSGSIZE; 196694604548SAndrea Mayer 196794604548SAndrea Mayer for_each_possible_cpu(i) { 196894604548SAndrea Mayer struct pcpu_seg6_local_counters *pcounters; 196994604548SAndrea Mayer u64 packets, bytes, errors; 197094604548SAndrea Mayer unsigned int start; 197194604548SAndrea Mayer 197294604548SAndrea Mayer pcounters = per_cpu_ptr(slwt->pcpu_counters, i); 197394604548SAndrea Mayer do { 1974d120d1a6SThomas Gleixner start = u64_stats_fetch_begin(&pcounters->syncp); 197594604548SAndrea Mayer 197694604548SAndrea Mayer packets = u64_stats_read(&pcounters->packets); 197794604548SAndrea Mayer bytes = u64_stats_read(&pcounters->bytes); 197894604548SAndrea Mayer errors = u64_stats_read(&pcounters->errors); 197994604548SAndrea Mayer 1980d120d1a6SThomas Gleixner } while (u64_stats_fetch_retry(&pcounters->syncp, start)); 198194604548SAndrea Mayer 198294604548SAndrea Mayer counters.packets += packets; 198394604548SAndrea Mayer counters.bytes += bytes; 198494604548SAndrea Mayer counters.errors += errors; 198594604548SAndrea Mayer } 198694604548SAndrea Mayer 198794604548SAndrea Mayer rc = seg6_local_fill_nla_counters(skb, &counters); 198894604548SAndrea Mayer if (rc < 0) { 198994604548SAndrea Mayer nla_nest_cancel(skb, nest); 199094604548SAndrea Mayer return rc; 199194604548SAndrea Mayer } 199294604548SAndrea Mayer 199394604548SAndrea Mayer return nla_nest_end(skb, nest); 199494604548SAndrea Mayer } 199594604548SAndrea Mayer 199694604548SAndrea Mayer static int cmp_nla_counters(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 199794604548SAndrea Mayer { 199894604548SAndrea Mayer /* a and b are equal if both have pcpu_counters set or not */ 199994604548SAndrea Mayer return (!!((unsigned long)a->pcpu_counters)) ^ 200094604548SAndrea Mayer (!!((unsigned long)b->pcpu_counters)); 200194604548SAndrea Mayer } 200294604548SAndrea Mayer 200394604548SAndrea Mayer static void destroy_attr_counters(struct seg6_local_lwt *slwt) 200494604548SAndrea Mayer { 200594604548SAndrea Mayer free_percpu(slwt->pcpu_counters); 200694604548SAndrea Mayer } 200794604548SAndrea Mayer 2008848f3c0dSAndrea Mayer static const 2009848f3c0dSAndrea Mayer struct nla_policy seg6_local_flavors_policy[SEG6_LOCAL_FLV_MAX + 1] = { 2010848f3c0dSAndrea Mayer [SEG6_LOCAL_FLV_OPERATION] = { .type = NLA_U32 }, 2011848f3c0dSAndrea Mayer [SEG6_LOCAL_FLV_LCBLOCK_BITS] = { .type = NLA_U8 }, 2012848f3c0dSAndrea Mayer [SEG6_LOCAL_FLV_LCNODE_FN_BITS] = { .type = NLA_U8 }, 2013848f3c0dSAndrea Mayer }; 2014848f3c0dSAndrea Mayer 2015848f3c0dSAndrea Mayer /* check whether the lengths of the Locator-Block and Locator-Node Function 2016848f3c0dSAndrea Mayer * are compatible with the dimension of a C-SID container. 2017848f3c0dSAndrea Mayer */ 2018848f3c0dSAndrea Mayer static int seg6_chk_next_csid_cfg(__u8 block_len, __u8 func_len) 2019848f3c0dSAndrea Mayer { 2020848f3c0dSAndrea Mayer /* Locator-Block and Locator-Node Function cannot exceed 128 bits 2021848f3c0dSAndrea Mayer * (i.e. C-SID container lenghts). 2022848f3c0dSAndrea Mayer */ 2023848f3c0dSAndrea Mayer if (next_csid_chk_cntr_bits(block_len, func_len)) 2024848f3c0dSAndrea Mayer return -EINVAL; 2025848f3c0dSAndrea Mayer 2026848f3c0dSAndrea Mayer /* Locator-Block length must be greater than zero and evenly divisible 2027848f3c0dSAndrea Mayer * by 8. There must be room for a Locator-Node Function, at least. 2028848f3c0dSAndrea Mayer */ 2029848f3c0dSAndrea Mayer if (next_csid_chk_lcblock_bits(block_len)) 2030848f3c0dSAndrea Mayer return -EINVAL; 2031848f3c0dSAndrea Mayer 2032848f3c0dSAndrea Mayer /* Locator-Node Function length must be greater than zero and evenly 2033848f3c0dSAndrea Mayer * divisible by 8. There must be room for the Locator-Block. 2034848f3c0dSAndrea Mayer */ 2035848f3c0dSAndrea Mayer if (next_csid_chk_lcnode_fn_bits(func_len)) 2036848f3c0dSAndrea Mayer return -EINVAL; 2037848f3c0dSAndrea Mayer 2038848f3c0dSAndrea Mayer return 0; 2039848f3c0dSAndrea Mayer } 2040848f3c0dSAndrea Mayer 2041848f3c0dSAndrea Mayer static int seg6_parse_nla_next_csid_cfg(struct nlattr **tb, 2042848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo, 2043848f3c0dSAndrea Mayer struct netlink_ext_ack *extack) 2044848f3c0dSAndrea Mayer { 2045848f3c0dSAndrea Mayer __u8 func_len = SEG6_LOCAL_LCNODE_FN_DBITS; 2046848f3c0dSAndrea Mayer __u8 block_len = SEG6_LOCAL_LCBLOCK_DBITS; 2047848f3c0dSAndrea Mayer int rc; 2048848f3c0dSAndrea Mayer 2049848f3c0dSAndrea Mayer if (tb[SEG6_LOCAL_FLV_LCBLOCK_BITS]) 2050848f3c0dSAndrea Mayer block_len = nla_get_u8(tb[SEG6_LOCAL_FLV_LCBLOCK_BITS]); 2051848f3c0dSAndrea Mayer 2052848f3c0dSAndrea Mayer if (tb[SEG6_LOCAL_FLV_LCNODE_FN_BITS]) 2053848f3c0dSAndrea Mayer func_len = nla_get_u8(tb[SEG6_LOCAL_FLV_LCNODE_FN_BITS]); 2054848f3c0dSAndrea Mayer 2055848f3c0dSAndrea Mayer rc = seg6_chk_next_csid_cfg(block_len, func_len); 2056848f3c0dSAndrea Mayer if (rc < 0) { 2057848f3c0dSAndrea Mayer NL_SET_ERR_MSG(extack, 2058848f3c0dSAndrea Mayer "Invalid Locator Block/Node Function lengths"); 2059848f3c0dSAndrea Mayer return rc; 2060848f3c0dSAndrea Mayer } 2061848f3c0dSAndrea Mayer 2062848f3c0dSAndrea Mayer finfo->lcblock_bits = block_len; 2063848f3c0dSAndrea Mayer finfo->lcnode_func_bits = func_len; 2064848f3c0dSAndrea Mayer 2065848f3c0dSAndrea Mayer return 0; 2066848f3c0dSAndrea Mayer } 2067848f3c0dSAndrea Mayer 2068848f3c0dSAndrea Mayer static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, 2069848f3c0dSAndrea Mayer struct netlink_ext_ack *extack) 2070848f3c0dSAndrea Mayer { 2071848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo = &slwt->flv_info; 2072848f3c0dSAndrea Mayer struct nlattr *tb[SEG6_LOCAL_FLV_MAX + 1]; 2073848f3c0dSAndrea Mayer unsigned long fops; 2074848f3c0dSAndrea Mayer int rc; 2075848f3c0dSAndrea Mayer 2076848f3c0dSAndrea Mayer rc = nla_parse_nested_deprecated(tb, SEG6_LOCAL_FLV_MAX, 2077848f3c0dSAndrea Mayer attrs[SEG6_LOCAL_FLAVORS], 2078848f3c0dSAndrea Mayer seg6_local_flavors_policy, NULL); 2079848f3c0dSAndrea Mayer if (rc < 0) 2080848f3c0dSAndrea Mayer return rc; 2081848f3c0dSAndrea Mayer 2082848f3c0dSAndrea Mayer /* this attribute MUST always be present since it represents the Flavor 2083848f3c0dSAndrea Mayer * operation(s) to be carried out. 2084848f3c0dSAndrea Mayer */ 2085848f3c0dSAndrea Mayer if (!tb[SEG6_LOCAL_FLV_OPERATION]) 2086848f3c0dSAndrea Mayer return -EINVAL; 2087848f3c0dSAndrea Mayer 2088848f3c0dSAndrea Mayer fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]); 2089848f3c0dSAndrea Mayer if (fops & ~SEG6_LOCAL_FLV_SUPP_OPS) { 2090848f3c0dSAndrea Mayer NL_SET_ERR_MSG(extack, "Unsupported Flavor operation(s)"); 2091848f3c0dSAndrea Mayer return -EOPNOTSUPP; 2092848f3c0dSAndrea Mayer } 2093848f3c0dSAndrea Mayer 2094848f3c0dSAndrea Mayer finfo->flv_ops = fops; 2095848f3c0dSAndrea Mayer 2096848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(fops)) { 2097848f3c0dSAndrea Mayer /* Locator-Block and Locator-Node Function lengths can be 2098848f3c0dSAndrea Mayer * provided by the user space. Otherwise, default values are 2099848f3c0dSAndrea Mayer * applied. 2100848f3c0dSAndrea Mayer */ 2101848f3c0dSAndrea Mayer rc = seg6_parse_nla_next_csid_cfg(tb, finfo, extack); 2102848f3c0dSAndrea Mayer if (rc < 0) 2103848f3c0dSAndrea Mayer return rc; 2104848f3c0dSAndrea Mayer } 2105848f3c0dSAndrea Mayer 2106848f3c0dSAndrea Mayer return 0; 2107848f3c0dSAndrea Mayer } 2108848f3c0dSAndrea Mayer 2109848f3c0dSAndrea Mayer static int seg6_fill_nla_next_csid_cfg(struct sk_buff *skb, 2110848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo) 2111848f3c0dSAndrea Mayer { 2112848f3c0dSAndrea Mayer if (nla_put_u8(skb, SEG6_LOCAL_FLV_LCBLOCK_BITS, finfo->lcblock_bits)) 2113848f3c0dSAndrea Mayer return -EMSGSIZE; 2114848f3c0dSAndrea Mayer 2115848f3c0dSAndrea Mayer if (nla_put_u8(skb, SEG6_LOCAL_FLV_LCNODE_FN_BITS, 2116848f3c0dSAndrea Mayer finfo->lcnode_func_bits)) 2117848f3c0dSAndrea Mayer return -EMSGSIZE; 2118848f3c0dSAndrea Mayer 2119848f3c0dSAndrea Mayer return 0; 2120848f3c0dSAndrea Mayer } 2121848f3c0dSAndrea Mayer 2122848f3c0dSAndrea Mayer static int put_nla_flavors(struct sk_buff *skb, struct seg6_local_lwt *slwt) 2123848f3c0dSAndrea Mayer { 2124848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo = &slwt->flv_info; 2125848f3c0dSAndrea Mayer __u32 fops = finfo->flv_ops; 2126848f3c0dSAndrea Mayer struct nlattr *nest; 2127848f3c0dSAndrea Mayer int rc; 2128848f3c0dSAndrea Mayer 2129848f3c0dSAndrea Mayer nest = nla_nest_start(skb, SEG6_LOCAL_FLAVORS); 2130848f3c0dSAndrea Mayer if (!nest) 2131848f3c0dSAndrea Mayer return -EMSGSIZE; 2132848f3c0dSAndrea Mayer 2133848f3c0dSAndrea Mayer if (nla_put_u32(skb, SEG6_LOCAL_FLV_OPERATION, fops)) { 2134848f3c0dSAndrea Mayer rc = -EMSGSIZE; 2135848f3c0dSAndrea Mayer goto err; 2136848f3c0dSAndrea Mayer } 2137848f3c0dSAndrea Mayer 2138848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(fops)) { 2139848f3c0dSAndrea Mayer rc = seg6_fill_nla_next_csid_cfg(skb, finfo); 2140848f3c0dSAndrea Mayer if (rc < 0) 2141848f3c0dSAndrea Mayer goto err; 2142848f3c0dSAndrea Mayer } 2143848f3c0dSAndrea Mayer 2144848f3c0dSAndrea Mayer return nla_nest_end(skb, nest); 2145848f3c0dSAndrea Mayer 2146848f3c0dSAndrea Mayer err: 2147848f3c0dSAndrea Mayer nla_nest_cancel(skb, nest); 2148848f3c0dSAndrea Mayer return rc; 2149848f3c0dSAndrea Mayer } 2150848f3c0dSAndrea Mayer 2151848f3c0dSAndrea Mayer static int seg6_cmp_nla_next_csid_cfg(struct seg6_flavors_info *finfo_a, 2152848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo_b) 2153848f3c0dSAndrea Mayer { 2154848f3c0dSAndrea Mayer if (finfo_a->lcblock_bits != finfo_b->lcblock_bits) 2155848f3c0dSAndrea Mayer return 1; 2156848f3c0dSAndrea Mayer 2157848f3c0dSAndrea Mayer if (finfo_a->lcnode_func_bits != finfo_b->lcnode_func_bits) 2158848f3c0dSAndrea Mayer return 1; 2159848f3c0dSAndrea Mayer 2160848f3c0dSAndrea Mayer return 0; 2161848f3c0dSAndrea Mayer } 2162848f3c0dSAndrea Mayer 2163848f3c0dSAndrea Mayer static int cmp_nla_flavors(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 2164848f3c0dSAndrea Mayer { 2165848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo_a = &a->flv_info; 2166848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo_b = &b->flv_info; 2167848f3c0dSAndrea Mayer 2168848f3c0dSAndrea Mayer if (finfo_a->flv_ops != finfo_b->flv_ops) 2169848f3c0dSAndrea Mayer return 1; 2170848f3c0dSAndrea Mayer 2171848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(finfo_a->flv_ops)) { 2172848f3c0dSAndrea Mayer if (seg6_cmp_nla_next_csid_cfg(finfo_a, finfo_b)) 2173848f3c0dSAndrea Mayer return 1; 2174848f3c0dSAndrea Mayer } 2175848f3c0dSAndrea Mayer 2176848f3c0dSAndrea Mayer return 0; 2177848f3c0dSAndrea Mayer } 2178848f3c0dSAndrea Mayer 2179848f3c0dSAndrea Mayer static int encap_size_flavors(struct seg6_local_lwt *slwt) 2180848f3c0dSAndrea Mayer { 2181848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo = &slwt->flv_info; 2182848f3c0dSAndrea Mayer int nlsize; 2183848f3c0dSAndrea Mayer 2184848f3c0dSAndrea Mayer nlsize = nla_total_size(0) + /* nest SEG6_LOCAL_FLAVORS */ 2185848f3c0dSAndrea Mayer nla_total_size(4); /* SEG6_LOCAL_FLV_OPERATION */ 2186848f3c0dSAndrea Mayer 2187848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(finfo->flv_ops)) 2188848f3c0dSAndrea Mayer nlsize += nla_total_size(1) + /* SEG6_LOCAL_FLV_LCBLOCK_BITS */ 2189848f3c0dSAndrea Mayer nla_total_size(1); /* SEG6_LOCAL_FLV_LCNODE_FN_BITS */ 2190848f3c0dSAndrea Mayer 2191848f3c0dSAndrea Mayer return nlsize; 2192848f3c0dSAndrea Mayer } 2193848f3c0dSAndrea Mayer 2194d1df6fd8SDavid Lebrun struct seg6_action_param { 2195e2a8ecc4SAndrea Mayer int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt, 2196e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack); 2197d1df6fd8SDavid Lebrun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 2198d1df6fd8SDavid Lebrun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); 2199964adce5SAndrea Mayer 2200964adce5SAndrea Mayer /* optional destroy() callback useful for releasing resources which 2201964adce5SAndrea Mayer * have been previously acquired in the corresponding parse() 2202964adce5SAndrea Mayer * function. 2203964adce5SAndrea Mayer */ 2204964adce5SAndrea Mayer void (*destroy)(struct seg6_local_lwt *slwt); 2205d1df6fd8SDavid Lebrun }; 2206d1df6fd8SDavid Lebrun 2207d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { 22082d9cc60aSDavid Lebrun [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh, 22092d9cc60aSDavid Lebrun .put = put_nla_srh, 2210964adce5SAndrea Mayer .cmp = cmp_nla_srh, 2211964adce5SAndrea Mayer .destroy = destroy_attr_srh }, 2212d1df6fd8SDavid Lebrun 22132d9cc60aSDavid Lebrun [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table, 22142d9cc60aSDavid Lebrun .put = put_nla_table, 22152d9cc60aSDavid Lebrun .cmp = cmp_nla_table }, 2216d1df6fd8SDavid Lebrun 22172d9cc60aSDavid Lebrun [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4, 22182d9cc60aSDavid Lebrun .put = put_nla_nh4, 22192d9cc60aSDavid Lebrun .cmp = cmp_nla_nh4 }, 2220d1df6fd8SDavid Lebrun 22212d9cc60aSDavid Lebrun [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6, 22222d9cc60aSDavid Lebrun .put = put_nla_nh6, 22232d9cc60aSDavid Lebrun .cmp = cmp_nla_nh6 }, 2224d1df6fd8SDavid Lebrun 22252d9cc60aSDavid Lebrun [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif, 22262d9cc60aSDavid Lebrun .put = put_nla_iif, 22272d9cc60aSDavid Lebrun .cmp = cmp_nla_iif }, 2228d1df6fd8SDavid Lebrun 22292d9cc60aSDavid Lebrun [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif, 22302d9cc60aSDavid Lebrun .put = put_nla_oif, 22312d9cc60aSDavid Lebrun .cmp = cmp_nla_oif }, 2232004d4b27SMathieu Xhonneux 2233004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .parse = parse_nla_bpf, 2234004d4b27SMathieu Xhonneux .put = put_nla_bpf, 2235964adce5SAndrea Mayer .cmp = cmp_nla_bpf, 2236964adce5SAndrea Mayer .destroy = destroy_attr_bpf }, 2237004d4b27SMathieu Xhonneux 2238664d6f86SAndrea Mayer [SEG6_LOCAL_VRFTABLE] = { .parse = parse_nla_vrftable, 2239664d6f86SAndrea Mayer .put = put_nla_vrftable, 2240664d6f86SAndrea Mayer .cmp = cmp_nla_vrftable }, 2241664d6f86SAndrea Mayer 224294604548SAndrea Mayer [SEG6_LOCAL_COUNTERS] = { .parse = parse_nla_counters, 224394604548SAndrea Mayer .put = put_nla_counters, 224494604548SAndrea Mayer .cmp = cmp_nla_counters, 224594604548SAndrea Mayer .destroy = destroy_attr_counters }, 2246848f3c0dSAndrea Mayer 2247848f3c0dSAndrea Mayer [SEG6_LOCAL_FLAVORS] = { .parse = parse_nla_flavors, 2248848f3c0dSAndrea Mayer .put = put_nla_flavors, 2249848f3c0dSAndrea Mayer .cmp = cmp_nla_flavors }, 2250d1df6fd8SDavid Lebrun }; 2251d1df6fd8SDavid Lebrun 2252964adce5SAndrea Mayer /* call the destroy() callback (if available) for each set attribute in 22530a3021f1SAndrea Mayer * @parsed_attrs, starting from the first attribute up to the @max_parsed 22540a3021f1SAndrea Mayer * (excluded) attribute. 2255964adce5SAndrea Mayer */ 22560a3021f1SAndrea Mayer static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed, 22570a3021f1SAndrea Mayer struct seg6_local_lwt *slwt) 2258964adce5SAndrea Mayer { 2259964adce5SAndrea Mayer struct seg6_action_param *param; 2260964adce5SAndrea Mayer int i; 2261964adce5SAndrea Mayer 2262964adce5SAndrea Mayer /* Every required seg6local attribute is identified by an ID which is 2263964adce5SAndrea Mayer * encoded as a flag (i.e: 1 << ID) in the 'attrs' bitmask; 2264964adce5SAndrea Mayer * 22650a3021f1SAndrea Mayer * We scan the 'parsed_attrs' bitmask, starting from the first attribute 2266964adce5SAndrea Mayer * up to the @max_parsed (excluded) attribute. 2267964adce5SAndrea Mayer * For each set attribute, we retrieve the corresponding destroy() 2268964adce5SAndrea Mayer * callback. If the callback is not available, then we skip to the next 2269964adce5SAndrea Mayer * attribute; otherwise, we call the destroy() callback. 2270964adce5SAndrea Mayer */ 2271ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < max_parsed; ++i) { 2272300a0fd8SAndrea Mayer if (!(parsed_attrs & SEG6_F_ATTR(i))) 2273964adce5SAndrea Mayer continue; 2274964adce5SAndrea Mayer 2275964adce5SAndrea Mayer param = &seg6_action_params[i]; 2276964adce5SAndrea Mayer 2277964adce5SAndrea Mayer if (param->destroy) 2278964adce5SAndrea Mayer param->destroy(slwt); 2279964adce5SAndrea Mayer } 2280964adce5SAndrea Mayer } 2281964adce5SAndrea Mayer 2282964adce5SAndrea Mayer /* release all the resources that may have been acquired during parsing 2283964adce5SAndrea Mayer * operations. 2284964adce5SAndrea Mayer */ 2285964adce5SAndrea Mayer static void destroy_attrs(struct seg6_local_lwt *slwt) 2286964adce5SAndrea Mayer { 22870a3021f1SAndrea Mayer unsigned long attrs = slwt->desc->attrs | slwt->parsed_optattrs; 22880a3021f1SAndrea Mayer 22890a3021f1SAndrea Mayer __destroy_attrs(attrs, SEG6_LOCAL_MAX + 1, slwt); 22900a3021f1SAndrea Mayer } 22910a3021f1SAndrea Mayer 22920a3021f1SAndrea Mayer static int parse_nla_optional_attrs(struct nlattr **attrs, 2293e2a8ecc4SAndrea Mayer struct seg6_local_lwt *slwt, 2294e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 22950a3021f1SAndrea Mayer { 22960a3021f1SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 22970a3021f1SAndrea Mayer unsigned long parsed_optattrs = 0; 22980a3021f1SAndrea Mayer struct seg6_action_param *param; 22990a3021f1SAndrea Mayer int err, i; 23000a3021f1SAndrea Mayer 2301ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; ++i) { 2302300a0fd8SAndrea Mayer if (!(desc->optattrs & SEG6_F_ATTR(i)) || !attrs[i]) 23030a3021f1SAndrea Mayer continue; 23040a3021f1SAndrea Mayer 23050a3021f1SAndrea Mayer /* once here, the i-th attribute is provided by the 23060a3021f1SAndrea Mayer * userspace AND it is identified optional as well. 23070a3021f1SAndrea Mayer */ 23080a3021f1SAndrea Mayer param = &seg6_action_params[i]; 23090a3021f1SAndrea Mayer 2310e2a8ecc4SAndrea Mayer err = param->parse(attrs, slwt, extack); 23110a3021f1SAndrea Mayer if (err < 0) 23120a3021f1SAndrea Mayer goto parse_optattrs_err; 23130a3021f1SAndrea Mayer 23140a3021f1SAndrea Mayer /* current attribute has been correctly parsed */ 2315300a0fd8SAndrea Mayer parsed_optattrs |= SEG6_F_ATTR(i); 23160a3021f1SAndrea Mayer } 23170a3021f1SAndrea Mayer 23180a3021f1SAndrea Mayer /* store in the tunnel state all the optional attributed successfully 23190a3021f1SAndrea Mayer * parsed. 23200a3021f1SAndrea Mayer */ 23210a3021f1SAndrea Mayer slwt->parsed_optattrs = parsed_optattrs; 23220a3021f1SAndrea Mayer 23230a3021f1SAndrea Mayer return 0; 23240a3021f1SAndrea Mayer 23250a3021f1SAndrea Mayer parse_optattrs_err: 23260a3021f1SAndrea Mayer __destroy_attrs(parsed_optattrs, i, slwt); 23270a3021f1SAndrea Mayer 23280a3021f1SAndrea Mayer return err; 2329964adce5SAndrea Mayer } 2330964adce5SAndrea Mayer 2331cfdf64a0SAndrea Mayer /* call the custom constructor of the behavior during its initialization phase 2332cfdf64a0SAndrea Mayer * and after that all its attributes have been parsed successfully. 2333cfdf64a0SAndrea Mayer */ 2334cfdf64a0SAndrea Mayer static int 2335cfdf64a0SAndrea Mayer seg6_local_lwtunnel_build_state(struct seg6_local_lwt *slwt, const void *cfg, 2336cfdf64a0SAndrea Mayer struct netlink_ext_ack *extack) 2337cfdf64a0SAndrea Mayer { 2338cfdf64a0SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 2339cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops *ops; 2340cfdf64a0SAndrea Mayer 2341cfdf64a0SAndrea Mayer ops = &desc->slwt_ops; 2342cfdf64a0SAndrea Mayer if (!ops->build_state) 2343cfdf64a0SAndrea Mayer return 0; 2344cfdf64a0SAndrea Mayer 2345cfdf64a0SAndrea Mayer return ops->build_state(slwt, cfg, extack); 2346cfdf64a0SAndrea Mayer } 2347cfdf64a0SAndrea Mayer 2348cfdf64a0SAndrea Mayer /* call the custom destructor of the behavior which is invoked before the 2349cfdf64a0SAndrea Mayer * tunnel is going to be destroyed. 2350cfdf64a0SAndrea Mayer */ 2351cfdf64a0SAndrea Mayer static void seg6_local_lwtunnel_destroy_state(struct seg6_local_lwt *slwt) 2352cfdf64a0SAndrea Mayer { 2353cfdf64a0SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 2354cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops *ops; 2355cfdf64a0SAndrea Mayer 2356cfdf64a0SAndrea Mayer ops = &desc->slwt_ops; 2357cfdf64a0SAndrea Mayer if (!ops->destroy_state) 2358cfdf64a0SAndrea Mayer return; 2359cfdf64a0SAndrea Mayer 2360cfdf64a0SAndrea Mayer ops->destroy_state(slwt); 2361cfdf64a0SAndrea Mayer } 2362cfdf64a0SAndrea Mayer 2363e2a8ecc4SAndrea Mayer static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt, 2364e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 2365d1df6fd8SDavid Lebrun { 2366d1df6fd8SDavid Lebrun struct seg6_action_param *param; 2367d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 23680a3021f1SAndrea Mayer unsigned long invalid_attrs; 2369d1df6fd8SDavid Lebrun int i, err; 2370d1df6fd8SDavid Lebrun 2371d1df6fd8SDavid Lebrun desc = __get_action_desc(slwt->action); 2372d1df6fd8SDavid Lebrun if (!desc) 2373d1df6fd8SDavid Lebrun return -EINVAL; 2374d1df6fd8SDavid Lebrun 2375d1df6fd8SDavid Lebrun if (!desc->input) 2376d1df6fd8SDavid Lebrun return -EOPNOTSUPP; 2377d1df6fd8SDavid Lebrun 2378d1df6fd8SDavid Lebrun slwt->desc = desc; 2379d1df6fd8SDavid Lebrun slwt->headroom += desc->static_headroom; 2380d1df6fd8SDavid Lebrun 23810a3021f1SAndrea Mayer /* Forcing the desc->optattrs *set* and the desc->attrs *set* to be 23820a3021f1SAndrea Mayer * disjoined, this allow us to release acquired resources by optional 23830a3021f1SAndrea Mayer * attributes and by required attributes independently from each other 23840d770360SAndrea Mayer * without any interference. 23850a3021f1SAndrea Mayer * In other terms, we are sure that we do not release some the acquired 23860a3021f1SAndrea Mayer * resources twice. 23870a3021f1SAndrea Mayer * 23880a3021f1SAndrea Mayer * Note that if an attribute is configured both as required and as 23890a3021f1SAndrea Mayer * optional, it means that the user has messed something up in the 23900a3021f1SAndrea Mayer * seg6_action_table. Therefore, this check is required for SRv6 23910a3021f1SAndrea Mayer * behaviors to work properly. 23920a3021f1SAndrea Mayer */ 23930a3021f1SAndrea Mayer invalid_attrs = desc->attrs & desc->optattrs; 23940a3021f1SAndrea Mayer if (invalid_attrs) { 23950a3021f1SAndrea Mayer WARN_ONCE(1, 23960a3021f1SAndrea Mayer "An attribute cannot be both required AND optional"); 23970a3021f1SAndrea Mayer return -EINVAL; 23980a3021f1SAndrea Mayer } 23990a3021f1SAndrea Mayer 24000a3021f1SAndrea Mayer /* parse the required attributes */ 2401ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { 2402300a0fd8SAndrea Mayer if (desc->attrs & SEG6_F_ATTR(i)) { 2403d1df6fd8SDavid Lebrun if (!attrs[i]) 2404d1df6fd8SDavid Lebrun return -EINVAL; 2405d1df6fd8SDavid Lebrun 2406d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 2407d1df6fd8SDavid Lebrun 2408e2a8ecc4SAndrea Mayer err = param->parse(attrs, slwt, extack); 2409d1df6fd8SDavid Lebrun if (err < 0) 24100a3021f1SAndrea Mayer goto parse_attrs_err; 2411d1df6fd8SDavid Lebrun } 2412d1df6fd8SDavid Lebrun } 2413d1df6fd8SDavid Lebrun 24140a3021f1SAndrea Mayer /* parse the optional attributes, if any */ 2415e2a8ecc4SAndrea Mayer err = parse_nla_optional_attrs(attrs, slwt, extack); 24160a3021f1SAndrea Mayer if (err < 0) 24170a3021f1SAndrea Mayer goto parse_attrs_err; 24180a3021f1SAndrea Mayer 2419d1df6fd8SDavid Lebrun return 0; 2420964adce5SAndrea Mayer 24210a3021f1SAndrea Mayer parse_attrs_err: 2422964adce5SAndrea Mayer /* release any resource that may have been acquired during the i-1 2423964adce5SAndrea Mayer * parse() operations. 2424964adce5SAndrea Mayer */ 24250a3021f1SAndrea Mayer __destroy_attrs(desc->attrs, i, slwt); 2426964adce5SAndrea Mayer 2427964adce5SAndrea Mayer return err; 2428d1df6fd8SDavid Lebrun } 2429d1df6fd8SDavid Lebrun 2430faee6769SAlexander Aring static int seg6_local_build_state(struct net *net, struct nlattr *nla, 2431faee6769SAlexander Aring unsigned int family, const void *cfg, 2432faee6769SAlexander Aring struct lwtunnel_state **ts, 2433d1df6fd8SDavid Lebrun struct netlink_ext_ack *extack) 2434d1df6fd8SDavid Lebrun { 2435d1df6fd8SDavid Lebrun struct nlattr *tb[SEG6_LOCAL_MAX + 1]; 2436d1df6fd8SDavid Lebrun struct lwtunnel_state *newts; 2437d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 2438d1df6fd8SDavid Lebrun int err; 2439d1df6fd8SDavid Lebrun 24406285217fSDavid Lebrun if (family != AF_INET6) 24416285217fSDavid Lebrun return -EINVAL; 24426285217fSDavid Lebrun 24438cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla, 24448cb08174SJohannes Berg seg6_local_policy, extack); 2445d1df6fd8SDavid Lebrun 2446d1df6fd8SDavid Lebrun if (err < 0) 2447d1df6fd8SDavid Lebrun return err; 2448d1df6fd8SDavid Lebrun 2449d1df6fd8SDavid Lebrun if (!tb[SEG6_LOCAL_ACTION]) 2450d1df6fd8SDavid Lebrun return -EINVAL; 2451d1df6fd8SDavid Lebrun 2452d1df6fd8SDavid Lebrun newts = lwtunnel_state_alloc(sizeof(*slwt)); 2453d1df6fd8SDavid Lebrun if (!newts) 2454d1df6fd8SDavid Lebrun return -ENOMEM; 2455d1df6fd8SDavid Lebrun 2456d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(newts); 2457d1df6fd8SDavid Lebrun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); 2458d1df6fd8SDavid Lebrun 2459e2a8ecc4SAndrea Mayer err = parse_nla_action(tb, slwt, extack); 2460d1df6fd8SDavid Lebrun if (err < 0) 2461d1df6fd8SDavid Lebrun goto out_free; 2462d1df6fd8SDavid Lebrun 2463cfdf64a0SAndrea Mayer err = seg6_local_lwtunnel_build_state(slwt, cfg, extack); 2464cfdf64a0SAndrea Mayer if (err < 0) 2465cfdf64a0SAndrea Mayer goto out_destroy_attrs; 2466cfdf64a0SAndrea Mayer 2467d1df6fd8SDavid Lebrun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; 2468d1df6fd8SDavid Lebrun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; 2469d1df6fd8SDavid Lebrun newts->headroom = slwt->headroom; 2470d1df6fd8SDavid Lebrun 2471d1df6fd8SDavid Lebrun *ts = newts; 2472d1df6fd8SDavid Lebrun 2473d1df6fd8SDavid Lebrun return 0; 2474d1df6fd8SDavid Lebrun 2475cfdf64a0SAndrea Mayer out_destroy_attrs: 2476cfdf64a0SAndrea Mayer destroy_attrs(slwt); 2477d1df6fd8SDavid Lebrun out_free: 2478d1df6fd8SDavid Lebrun kfree(newts); 2479d1df6fd8SDavid Lebrun return err; 2480d1df6fd8SDavid Lebrun } 2481d1df6fd8SDavid Lebrun 2482d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt) 2483d1df6fd8SDavid Lebrun { 2484d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 2485d1df6fd8SDavid Lebrun 2486cfdf64a0SAndrea Mayer seg6_local_lwtunnel_destroy_state(slwt); 2487cfdf64a0SAndrea Mayer 2488964adce5SAndrea Mayer destroy_attrs(slwt); 2489004d4b27SMathieu Xhonneux 2490004d4b27SMathieu Xhonneux return; 2491d1df6fd8SDavid Lebrun } 2492d1df6fd8SDavid Lebrun 2493d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb, 2494d1df6fd8SDavid Lebrun struct lwtunnel_state *lwt) 2495d1df6fd8SDavid Lebrun { 2496d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 2497d1df6fd8SDavid Lebrun struct seg6_action_param *param; 24980a3021f1SAndrea Mayer unsigned long attrs; 2499d1df6fd8SDavid Lebrun int i, err; 2500d1df6fd8SDavid Lebrun 2501d1df6fd8SDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) 2502d1df6fd8SDavid Lebrun return -EMSGSIZE; 2503d1df6fd8SDavid Lebrun 25040a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 25050a3021f1SAndrea Mayer 2506ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { 2507300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(i)) { 2508d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 2509d1df6fd8SDavid Lebrun err = param->put(skb, slwt); 2510d1df6fd8SDavid Lebrun if (err < 0) 2511d1df6fd8SDavid Lebrun return err; 2512d1df6fd8SDavid Lebrun } 2513d1df6fd8SDavid Lebrun } 2514d1df6fd8SDavid Lebrun 2515d1df6fd8SDavid Lebrun return 0; 2516d1df6fd8SDavid Lebrun } 2517d1df6fd8SDavid Lebrun 2518d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) 2519d1df6fd8SDavid Lebrun { 2520d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 2521d1df6fd8SDavid Lebrun unsigned long attrs; 2522d1df6fd8SDavid Lebrun int nlsize; 2523d1df6fd8SDavid Lebrun 2524d1df6fd8SDavid Lebrun nlsize = nla_total_size(4); /* action */ 2525d1df6fd8SDavid Lebrun 25260a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 2527d1df6fd8SDavid Lebrun 2528300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_SRH)) 2529d1df6fd8SDavid Lebrun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); 2530d1df6fd8SDavid Lebrun 2531300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE)) 2532d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 2533d1df6fd8SDavid Lebrun 2534300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH4)) 2535d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 2536d1df6fd8SDavid Lebrun 2537300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH6)) 2538d1df6fd8SDavid Lebrun nlsize += nla_total_size(16); 2539d1df6fd8SDavid Lebrun 2540300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_IIF)) 2541d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 2542d1df6fd8SDavid Lebrun 2543300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_OIF)) 2544d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 2545d1df6fd8SDavid Lebrun 2546300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_BPF)) 2547004d4b27SMathieu Xhonneux nlsize += nla_total_size(sizeof(struct nlattr)) + 2548004d4b27SMathieu Xhonneux nla_total_size(MAX_PROG_NAME) + 2549004d4b27SMathieu Xhonneux nla_total_size(4); 2550004d4b27SMathieu Xhonneux 2551300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE)) 2552664d6f86SAndrea Mayer nlsize += nla_total_size(4); 2553664d6f86SAndrea Mayer 255494604548SAndrea Mayer if (attrs & SEG6_F_LOCAL_COUNTERS) 255594604548SAndrea Mayer nlsize += nla_total_size(0) + /* nest SEG6_LOCAL_COUNTERS */ 255694604548SAndrea Mayer /* SEG6_LOCAL_CNT_PACKETS */ 255794604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)) + 255894604548SAndrea Mayer /* SEG6_LOCAL_CNT_BYTES */ 255994604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)) + 256094604548SAndrea Mayer /* SEG6_LOCAL_CNT_ERRORS */ 256194604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)); 256294604548SAndrea Mayer 2563848f3c0dSAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_FLAVORS)) 2564848f3c0dSAndrea Mayer nlsize += encap_size_flavors(slwt); 2565848f3c0dSAndrea Mayer 2566d1df6fd8SDavid Lebrun return nlsize; 2567d1df6fd8SDavid Lebrun } 2568d1df6fd8SDavid Lebrun 2569d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a, 2570d1df6fd8SDavid Lebrun struct lwtunnel_state *b) 2571d1df6fd8SDavid Lebrun { 2572d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt_a, *slwt_b; 2573d1df6fd8SDavid Lebrun struct seg6_action_param *param; 25740a3021f1SAndrea Mayer unsigned long attrs_a, attrs_b; 2575d1df6fd8SDavid Lebrun int i; 2576d1df6fd8SDavid Lebrun 2577d1df6fd8SDavid Lebrun slwt_a = seg6_local_lwtunnel(a); 2578d1df6fd8SDavid Lebrun slwt_b = seg6_local_lwtunnel(b); 2579d1df6fd8SDavid Lebrun 2580d1df6fd8SDavid Lebrun if (slwt_a->action != slwt_b->action) 2581d1df6fd8SDavid Lebrun return 1; 2582d1df6fd8SDavid Lebrun 25830a3021f1SAndrea Mayer attrs_a = slwt_a->desc->attrs | slwt_a->parsed_optattrs; 25840a3021f1SAndrea Mayer attrs_b = slwt_b->desc->attrs | slwt_b->parsed_optattrs; 25850a3021f1SAndrea Mayer 25860a3021f1SAndrea Mayer if (attrs_a != attrs_b) 2587d1df6fd8SDavid Lebrun return 1; 2588d1df6fd8SDavid Lebrun 2589ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { 2590300a0fd8SAndrea Mayer if (attrs_a & SEG6_F_ATTR(i)) { 2591d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 2592d1df6fd8SDavid Lebrun if (param->cmp(slwt_a, slwt_b)) 2593d1df6fd8SDavid Lebrun return 1; 2594d1df6fd8SDavid Lebrun } 2595d1df6fd8SDavid Lebrun } 2596d1df6fd8SDavid Lebrun 2597d1df6fd8SDavid Lebrun return 0; 2598d1df6fd8SDavid Lebrun } 2599d1df6fd8SDavid Lebrun 2600d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = { 2601d1df6fd8SDavid Lebrun .build_state = seg6_local_build_state, 2602d1df6fd8SDavid Lebrun .destroy_state = seg6_local_destroy_state, 2603d1df6fd8SDavid Lebrun .input = seg6_local_input, 2604d1df6fd8SDavid Lebrun .fill_encap = seg6_local_fill_encap, 2605d1df6fd8SDavid Lebrun .get_encap_size = seg6_local_get_encap_size, 2606d1df6fd8SDavid Lebrun .cmp_encap = seg6_local_cmp_encap, 2607d1df6fd8SDavid Lebrun .owner = THIS_MODULE, 2608d1df6fd8SDavid Lebrun }; 2609d1df6fd8SDavid Lebrun 2610d1df6fd8SDavid Lebrun int __init seg6_local_init(void) 2611d1df6fd8SDavid Lebrun { 2612300a0fd8SAndrea Mayer /* If the max total number of defined attributes is reached, then your 2613300a0fd8SAndrea Mayer * kernel build stops here. 2614300a0fd8SAndrea Mayer * 2615300a0fd8SAndrea Mayer * This check is required to avoid arithmetic overflows when processing 2616300a0fd8SAndrea Mayer * behavior attributes and the maximum number of defined attributes 2617300a0fd8SAndrea Mayer * exceeds the allowed value. 2618300a0fd8SAndrea Mayer */ 2619300a0fd8SAndrea Mayer BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsigned long)); 2620300a0fd8SAndrea Mayer 2621848f3c0dSAndrea Mayer /* If the default NEXT-C-SID Locator-Block/Node Function lengths (in 2622848f3c0dSAndrea Mayer * bits) have been changed with invalid values, kernel build stops 2623848f3c0dSAndrea Mayer * here. 2624848f3c0dSAndrea Mayer */ 2625848f3c0dSAndrea Mayer BUILD_BUG_ON(next_csid_chk_cntr_bits(SEG6_LOCAL_LCBLOCK_DBITS, 2626848f3c0dSAndrea Mayer SEG6_LOCAL_LCNODE_FN_DBITS)); 2627848f3c0dSAndrea Mayer BUILD_BUG_ON(next_csid_chk_lcblock_bits(SEG6_LOCAL_LCBLOCK_DBITS)); 2628848f3c0dSAndrea Mayer BUILD_BUG_ON(next_csid_chk_lcnode_fn_bits(SEG6_LOCAL_LCNODE_FN_DBITS)); 2629848f3c0dSAndrea Mayer 2630*bdf3c0b9SAndrea Mayer /* To be memory efficient, we use 'u8' to represent the different 2631*bdf3c0b9SAndrea Mayer * actions related to RFC8986 flavors. If the kernel build stops here, 2632*bdf3c0b9SAndrea Mayer * it means that it is not possible to correctly encode these actions 2633*bdf3c0b9SAndrea Mayer * with the data type chosen for the action table. 2634*bdf3c0b9SAndrea Mayer */ 2635*bdf3c0b9SAndrea Mayer BUILD_BUG_ON(SEG6_LOCAL_FLV_ACT_MAX > (typeof(flv8986_act_tbl[0]))~0U); 2636*bdf3c0b9SAndrea Mayer 2637d1df6fd8SDavid Lebrun return lwtunnel_encap_add_ops(&seg6_local_ops, 2638d1df6fd8SDavid Lebrun LWTUNNEL_ENCAP_SEG6_LOCAL); 2639d1df6fd8SDavid Lebrun } 2640d1df6fd8SDavid Lebrun 2641d1df6fd8SDavid Lebrun void seg6_local_exit(void) 2642d1df6fd8SDavid Lebrun { 2643d1df6fd8SDavid Lebrun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); 2644d1df6fd8SDavid Lebrun } 2645