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 1127458575aSAndrea Mayer /* flag indicating that flavors are set up for a given End* behavior */ 1137458575aSAndrea Mayer #define SEG6_F_LOCAL_FLAVORS SEG6_F_ATTR(SEG6_LOCAL_FLAVORS) 1147458575aSAndrea Mayer 115bdf3c0b9SAndrea Mayer #define SEG6_F_LOCAL_FLV_OP(flvname) BIT(SEG6_LOCAL_FLV_OP_##flvname) 1167458575aSAndrea Mayer #define SEG6_F_LOCAL_FLV_NEXT_CSID SEG6_F_LOCAL_FLV_OP(NEXT_CSID) 117bdf3c0b9SAndrea Mayer #define SEG6_F_LOCAL_FLV_PSP SEG6_F_LOCAL_FLV_OP(PSP) 118bdf3c0b9SAndrea Mayer 119bdf3c0b9SAndrea Mayer /* Supported RFC8986 Flavor operations are reported in this bitmask */ 120bdf3c0b9SAndrea Mayer #define SEG6_LOCAL_FLV8986_SUPP_OPS SEG6_F_LOCAL_FLV_PSP 121bdf3c0b9SAndrea Mayer 1227458575aSAndrea Mayer #define SEG6_LOCAL_END_FLV_SUPP_OPS (SEG6_F_LOCAL_FLV_NEXT_CSID | \ 123bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV8986_SUPP_OPS) 1247458575aSAndrea Mayer #define SEG6_LOCAL_END_X_FLV_SUPP_OPS SEG6_F_LOCAL_FLV_NEXT_CSID 125848f3c0dSAndrea Mayer 126848f3c0dSAndrea Mayer struct seg6_flavors_info { 127848f3c0dSAndrea Mayer /* Flavor operations */ 128848f3c0dSAndrea Mayer __u32 flv_ops; 129848f3c0dSAndrea Mayer 130848f3c0dSAndrea Mayer /* Locator-Block length, expressed in bits */ 131848f3c0dSAndrea Mayer __u8 lcblock_bits; 132848f3c0dSAndrea Mayer /* Locator-Node Function length, expressed in bits*/ 133848f3c0dSAndrea Mayer __u8 lcnode_func_bits; 134848f3c0dSAndrea Mayer }; 135848f3c0dSAndrea Mayer 136664d6f86SAndrea Mayer enum seg6_end_dt_mode { 137664d6f86SAndrea Mayer DT_INVALID_MODE = -EINVAL, 138664d6f86SAndrea Mayer DT_LEGACY_MODE = 0, 139664d6f86SAndrea Mayer DT_VRF_MODE = 1, 140664d6f86SAndrea Mayer }; 141664d6f86SAndrea Mayer 142664d6f86SAndrea Mayer struct seg6_end_dt_info { 143664d6f86SAndrea Mayer enum seg6_end_dt_mode mode; 144664d6f86SAndrea Mayer 145664d6f86SAndrea Mayer struct net *net; 146664d6f86SAndrea Mayer /* VRF device associated to the routing table used by the SRv6 147664d6f86SAndrea Mayer * End.DT4/DT6 behavior for routing IPv4/IPv6 packets. 148664d6f86SAndrea Mayer */ 149664d6f86SAndrea Mayer int vrf_ifindex; 150664d6f86SAndrea Mayer int vrf_table; 151664d6f86SAndrea Mayer 1528b532109SAndrea Mayer /* tunneled packet family (IPv4 or IPv6). 1538b532109SAndrea Mayer * Protocol and header length are inferred from family. 1548b532109SAndrea Mayer */ 155664d6f86SAndrea Mayer u16 family; 156664d6f86SAndrea Mayer }; 157664d6f86SAndrea Mayer 15894604548SAndrea Mayer struct pcpu_seg6_local_counters { 15994604548SAndrea Mayer u64_stats_t packets; 16094604548SAndrea Mayer u64_stats_t bytes; 16194604548SAndrea Mayer u64_stats_t errors; 16294604548SAndrea Mayer 16394604548SAndrea Mayer struct u64_stats_sync syncp; 16494604548SAndrea Mayer }; 16594604548SAndrea Mayer 16694604548SAndrea Mayer /* This struct groups all the SRv6 Behavior counters supported so far. 16794604548SAndrea Mayer * 16894604548SAndrea Mayer * put_nla_counters() makes use of this data structure to collect all counter 16994604548SAndrea Mayer * values after the per-CPU counter evaluation has been performed. 17094604548SAndrea Mayer * Finally, each counter value (in seg6_local_counters) is stored in the 17194604548SAndrea Mayer * corresponding netlink attribute and sent to user space. 17294604548SAndrea Mayer * 17394604548SAndrea Mayer * NB: we don't want to expose this structure to user space! 17494604548SAndrea Mayer */ 17594604548SAndrea Mayer struct seg6_local_counters { 17694604548SAndrea Mayer __u64 packets; 17794604548SAndrea Mayer __u64 bytes; 17894604548SAndrea Mayer __u64 errors; 17994604548SAndrea Mayer }; 18094604548SAndrea Mayer 18194604548SAndrea Mayer #define seg6_local_alloc_pcpu_counters(__gfp) \ 18294604548SAndrea Mayer __netdev_alloc_pcpu_stats(struct pcpu_seg6_local_counters, \ 18394604548SAndrea Mayer ((__gfp) | __GFP_ZERO)) 18494604548SAndrea Mayer 18594604548SAndrea Mayer #define SEG6_F_LOCAL_COUNTERS SEG6_F_ATTR(SEG6_LOCAL_COUNTERS) 18694604548SAndrea Mayer 187d1df6fd8SDavid Lebrun struct seg6_local_lwt { 188d1df6fd8SDavid Lebrun int action; 189d1df6fd8SDavid Lebrun struct ipv6_sr_hdr *srh; 190d1df6fd8SDavid Lebrun int table; 191d1df6fd8SDavid Lebrun struct in_addr nh4; 192d1df6fd8SDavid Lebrun struct in6_addr nh6; 193d1df6fd8SDavid Lebrun int iif; 194d1df6fd8SDavid Lebrun int oif; 195004d4b27SMathieu Xhonneux struct bpf_lwt_prog bpf; 196664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 197664d6f86SAndrea Mayer struct seg6_end_dt_info dt_info; 198664d6f86SAndrea Mayer #endif 199848f3c0dSAndrea Mayer struct seg6_flavors_info flv_info; 200848f3c0dSAndrea Mayer 20194604548SAndrea Mayer struct pcpu_seg6_local_counters __percpu *pcpu_counters; 202d1df6fd8SDavid Lebrun 203d1df6fd8SDavid Lebrun int headroom; 204d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 2050a3021f1SAndrea Mayer /* unlike the required attrs, we have to track the optional attributes 2060a3021f1SAndrea Mayer * that have been effectively parsed. 2070a3021f1SAndrea Mayer */ 2080a3021f1SAndrea Mayer unsigned long parsed_optattrs; 209d1df6fd8SDavid Lebrun }; 210d1df6fd8SDavid Lebrun 211d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) 212d1df6fd8SDavid Lebrun { 213d1df6fd8SDavid Lebrun return (struct seg6_local_lwt *)lwt->data; 214d1df6fd8SDavid Lebrun } 215d1df6fd8SDavid Lebrun 216140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) 217140f04c3SDavid Lebrun { 218140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 219140f04c3SDavid Lebrun 220fa55a7d7SAndrew Lunn srh = seg6_get_srh(skb, IP6_FH_F_SKIP_RH); 221140f04c3SDavid Lebrun if (!srh) 222140f04c3SDavid Lebrun return NULL; 223140f04c3SDavid Lebrun 224140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 225140f04c3SDavid Lebrun if (!seg6_hmac_validate_skb(skb)) 226140f04c3SDavid Lebrun return NULL; 227140f04c3SDavid Lebrun #endif 228140f04c3SDavid Lebrun 229140f04c3SDavid Lebrun return srh; 230140f04c3SDavid Lebrun } 231140f04c3SDavid Lebrun 232d7a669ddSDavid Lebrun static bool decap_and_validate(struct sk_buff *skb, int proto) 233d7a669ddSDavid Lebrun { 234d7a669ddSDavid Lebrun struct ipv6_sr_hdr *srh; 235d7a669ddSDavid Lebrun unsigned int off = 0; 236d7a669ddSDavid Lebrun 237fa55a7d7SAndrew Lunn srh = seg6_get_srh(skb, 0); 238d7a669ddSDavid Lebrun if (srh && srh->segments_left > 0) 239d7a669ddSDavid Lebrun return false; 240d7a669ddSDavid Lebrun 241d7a669ddSDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 242d7a669ddSDavid Lebrun if (srh && !seg6_hmac_validate_skb(skb)) 243d7a669ddSDavid Lebrun return false; 244d7a669ddSDavid Lebrun #endif 245d7a669ddSDavid Lebrun 246d7a669ddSDavid Lebrun if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0) 247d7a669ddSDavid Lebrun return false; 248d7a669ddSDavid Lebrun 249d7a669ddSDavid Lebrun if (!pskb_pull(skb, off)) 250d7a669ddSDavid Lebrun return false; 251d7a669ddSDavid Lebrun 252d7a669ddSDavid Lebrun skb_postpull_rcsum(skb, skb_network_header(skb), off); 253d7a669ddSDavid Lebrun 254d7a669ddSDavid Lebrun skb_reset_network_header(skb); 255d7a669ddSDavid Lebrun skb_reset_transport_header(skb); 25662ebaeaeSYuki Taguchi if (iptunnel_pull_offloads(skb)) 25762ebaeaeSYuki Taguchi return false; 258d7a669ddSDavid Lebrun 259d7a669ddSDavid Lebrun return true; 260d7a669ddSDavid Lebrun } 261d7a669ddSDavid Lebrun 262d7a669ddSDavid Lebrun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr) 263d7a669ddSDavid Lebrun { 264d7a669ddSDavid Lebrun struct in6_addr *addr; 265d7a669ddSDavid Lebrun 266d7a669ddSDavid Lebrun srh->segments_left--; 267d7a669ddSDavid Lebrun addr = srh->segments + srh->segments_left; 268d7a669ddSDavid Lebrun *daddr = *addr; 269d7a669ddSDavid Lebrun } 270d7a669ddSDavid Lebrun 271fd1fef0cSAndrea Mayer static int 272fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr, 273fd1fef0cSAndrea Mayer u32 tbl_id, bool local_delivery) 274d7a669ddSDavid Lebrun { 275d7a669ddSDavid Lebrun struct net *net = dev_net(skb->dev); 276d7a669ddSDavid Lebrun struct ipv6hdr *hdr = ipv6_hdr(skb); 277d7a669ddSDavid Lebrun int flags = RT6_LOOKUP_F_HAS_SADDR; 278d7a669ddSDavid Lebrun struct dst_entry *dst = NULL; 279d7a669ddSDavid Lebrun struct rt6_info *rt; 280d7a669ddSDavid Lebrun struct flowi6 fl6; 281fd1fef0cSAndrea Mayer int dev_flags = 0; 282d7a669ddSDavid Lebrun 283a3bd2102SAndrea Mayer memset(&fl6, 0, sizeof(fl6)); 284d7a669ddSDavid Lebrun fl6.flowi6_iif = skb->dev->ifindex; 285d7a669ddSDavid Lebrun fl6.daddr = nhaddr ? *nhaddr : hdr->daddr; 286d7a669ddSDavid Lebrun fl6.saddr = hdr->saddr; 287d7a669ddSDavid Lebrun fl6.flowlabel = ip6_flowinfo(hdr); 288d7a669ddSDavid Lebrun fl6.flowi6_mark = skb->mark; 289d7a669ddSDavid Lebrun fl6.flowi6_proto = hdr->nexthdr; 290d7a669ddSDavid Lebrun 291d7a669ddSDavid Lebrun if (nhaddr) 292d7a669ddSDavid Lebrun fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH; 293d7a669ddSDavid Lebrun 294d7a669ddSDavid Lebrun if (!tbl_id) { 295b75cc8f9SDavid Ahern dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags); 296d7a669ddSDavid Lebrun } else { 297d7a669ddSDavid Lebrun struct fib6_table *table; 298d7a669ddSDavid Lebrun 299d7a669ddSDavid Lebrun table = fib6_get_table(net, tbl_id); 300d7a669ddSDavid Lebrun if (!table) 301d7a669ddSDavid Lebrun goto out; 302d7a669ddSDavid Lebrun 303b75cc8f9SDavid Ahern rt = ip6_pol_route(net, table, 0, &fl6, skb, flags); 304d7a669ddSDavid Lebrun dst = &rt->dst; 305d7a669ddSDavid Lebrun } 306d7a669ddSDavid Lebrun 307fd1fef0cSAndrea Mayer /* we want to discard traffic destined for local packet processing, 308fd1fef0cSAndrea Mayer * if @local_delivery is set to false. 309fd1fef0cSAndrea Mayer */ 310fd1fef0cSAndrea Mayer if (!local_delivery) 311fd1fef0cSAndrea Mayer dev_flags |= IFF_LOOPBACK; 312fd1fef0cSAndrea Mayer 313fd1fef0cSAndrea Mayer if (dst && (dst->dev->flags & dev_flags) && !dst->error) { 314d7a669ddSDavid Lebrun dst_release(dst); 315d7a669ddSDavid Lebrun dst = NULL; 316d7a669ddSDavid Lebrun } 317d7a669ddSDavid Lebrun 318d7a669ddSDavid Lebrun out: 319d7a669ddSDavid Lebrun if (!dst) { 320d7a669ddSDavid Lebrun rt = net->ipv6.ip6_blk_hole_entry; 321d7a669ddSDavid Lebrun dst = &rt->dst; 322d7a669ddSDavid Lebrun dst_hold(dst); 323d7a669ddSDavid Lebrun } 324d7a669ddSDavid Lebrun 325d7a669ddSDavid Lebrun skb_dst_drop(skb); 326d7a669ddSDavid Lebrun skb_dst_set(skb, dst); 3271c1e761eSMathieu Xhonneux return dst->error; 328d7a669ddSDavid Lebrun } 329d7a669ddSDavid Lebrun 330fd1fef0cSAndrea Mayer int seg6_lookup_nexthop(struct sk_buff *skb, 331fd1fef0cSAndrea Mayer struct in6_addr *nhaddr, u32 tbl_id) 332fd1fef0cSAndrea Mayer { 333fd1fef0cSAndrea Mayer return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false); 334fd1fef0cSAndrea Mayer } 335fd1fef0cSAndrea Mayer 336848f3c0dSAndrea Mayer static __u8 seg6_flv_lcblock_octects(const struct seg6_flavors_info *finfo) 337848f3c0dSAndrea Mayer { 338848f3c0dSAndrea Mayer return finfo->lcblock_bits >> 3; 339848f3c0dSAndrea Mayer } 340848f3c0dSAndrea Mayer 341848f3c0dSAndrea Mayer static __u8 seg6_flv_lcnode_func_octects(const struct seg6_flavors_info *finfo) 342848f3c0dSAndrea Mayer { 343848f3c0dSAndrea Mayer return finfo->lcnode_func_bits >> 3; 344848f3c0dSAndrea Mayer } 345848f3c0dSAndrea Mayer 346848f3c0dSAndrea Mayer static bool seg6_next_csid_is_arg_zero(const struct in6_addr *addr, 347848f3c0dSAndrea Mayer const struct seg6_flavors_info *finfo) 348848f3c0dSAndrea Mayer { 349848f3c0dSAndrea Mayer __u8 fnc_octects = seg6_flv_lcnode_func_octects(finfo); 350848f3c0dSAndrea Mayer __u8 blk_octects = seg6_flv_lcblock_octects(finfo); 351848f3c0dSAndrea Mayer __u8 arg_octects; 352848f3c0dSAndrea Mayer int i; 353848f3c0dSAndrea Mayer 354848f3c0dSAndrea Mayer arg_octects = 16 - blk_octects - fnc_octects; 355848f3c0dSAndrea Mayer for (i = 0; i < arg_octects; ++i) { 356848f3c0dSAndrea Mayer if (addr->s6_addr[blk_octects + fnc_octects + i] != 0x00) 357848f3c0dSAndrea Mayer return false; 358848f3c0dSAndrea Mayer } 359848f3c0dSAndrea Mayer 360848f3c0dSAndrea Mayer return true; 361848f3c0dSAndrea Mayer } 362848f3c0dSAndrea Mayer 363848f3c0dSAndrea Mayer /* assume that DA.Argument length > 0 */ 364848f3c0dSAndrea Mayer static void seg6_next_csid_advance_arg(struct in6_addr *addr, 365848f3c0dSAndrea Mayer const struct seg6_flavors_info *finfo) 366848f3c0dSAndrea Mayer { 367848f3c0dSAndrea Mayer __u8 fnc_octects = seg6_flv_lcnode_func_octects(finfo); 368848f3c0dSAndrea Mayer __u8 blk_octects = seg6_flv_lcblock_octects(finfo); 369848f3c0dSAndrea Mayer 370848f3c0dSAndrea Mayer /* advance DA.Argument */ 371848f3c0dSAndrea Mayer memmove(&addr->s6_addr[blk_octects], 372848f3c0dSAndrea Mayer &addr->s6_addr[blk_octects + fnc_octects], 373848f3c0dSAndrea Mayer 16 - blk_octects - fnc_octects); 374848f3c0dSAndrea Mayer 375848f3c0dSAndrea Mayer memset(&addr->s6_addr[16 - fnc_octects], 0x00, fnc_octects); 376848f3c0dSAndrea Mayer } 377848f3c0dSAndrea Mayer 378525c65ffSAndrea Mayer static int input_action_end_finish(struct sk_buff *skb, 379525c65ffSAndrea Mayer struct seg6_local_lwt *slwt) 380525c65ffSAndrea Mayer { 381525c65ffSAndrea Mayer seg6_lookup_nexthop(skb, NULL, 0); 382525c65ffSAndrea Mayer 383525c65ffSAndrea Mayer return dst_input(skb); 384525c65ffSAndrea Mayer } 385525c65ffSAndrea Mayer 386848f3c0dSAndrea Mayer static int input_action_end_core(struct sk_buff *skb, 387848f3c0dSAndrea Mayer struct seg6_local_lwt *slwt) 388140f04c3SDavid Lebrun { 389140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 390140f04c3SDavid Lebrun 391140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 392140f04c3SDavid Lebrun if (!srh) 393140f04c3SDavid Lebrun goto drop; 394140f04c3SDavid Lebrun 395d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 396140f04c3SDavid Lebrun 397525c65ffSAndrea Mayer return input_action_end_finish(skb, slwt); 398140f04c3SDavid Lebrun 399140f04c3SDavid Lebrun drop: 400140f04c3SDavid Lebrun kfree_skb(skb); 401140f04c3SDavid Lebrun return -EINVAL; 402140f04c3SDavid Lebrun } 403140f04c3SDavid Lebrun 404848f3c0dSAndrea Mayer static int end_next_csid_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) 405848f3c0dSAndrea Mayer { 406848f3c0dSAndrea Mayer const struct seg6_flavors_info *finfo = &slwt->flv_info; 407848f3c0dSAndrea Mayer struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; 408848f3c0dSAndrea Mayer 409848f3c0dSAndrea Mayer if (seg6_next_csid_is_arg_zero(daddr, finfo)) 410848f3c0dSAndrea Mayer return input_action_end_core(skb, slwt); 411848f3c0dSAndrea Mayer 412848f3c0dSAndrea Mayer /* update DA */ 413848f3c0dSAndrea Mayer seg6_next_csid_advance_arg(daddr, finfo); 414848f3c0dSAndrea Mayer 415525c65ffSAndrea Mayer return input_action_end_finish(skb, slwt); 416848f3c0dSAndrea Mayer } 417848f3c0dSAndrea Mayer 4187458575aSAndrea Mayer static int input_action_end_x_finish(struct sk_buff *skb, 4197458575aSAndrea Mayer struct seg6_local_lwt *slwt) 4207458575aSAndrea Mayer { 4217458575aSAndrea Mayer seg6_lookup_nexthop(skb, &slwt->nh6, 0); 4227458575aSAndrea Mayer 4237458575aSAndrea Mayer return dst_input(skb); 4247458575aSAndrea Mayer } 4257458575aSAndrea Mayer 4267458575aSAndrea Mayer static int input_action_end_x_core(struct sk_buff *skb, 4277458575aSAndrea Mayer struct seg6_local_lwt *slwt) 4287458575aSAndrea Mayer { 4297458575aSAndrea Mayer struct ipv6_sr_hdr *srh; 4307458575aSAndrea Mayer 4317458575aSAndrea Mayer srh = get_and_validate_srh(skb); 4327458575aSAndrea Mayer if (!srh) 4337458575aSAndrea Mayer goto drop; 4347458575aSAndrea Mayer 4357458575aSAndrea Mayer advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 4367458575aSAndrea Mayer 4377458575aSAndrea Mayer return input_action_end_x_finish(skb, slwt); 4387458575aSAndrea Mayer 4397458575aSAndrea Mayer drop: 4407458575aSAndrea Mayer kfree_skb(skb); 4417458575aSAndrea Mayer return -EINVAL; 4427458575aSAndrea Mayer } 4437458575aSAndrea Mayer 4447458575aSAndrea Mayer static int end_x_next_csid_core(struct sk_buff *skb, 4457458575aSAndrea Mayer struct seg6_local_lwt *slwt) 4467458575aSAndrea Mayer { 4477458575aSAndrea Mayer const struct seg6_flavors_info *finfo = &slwt->flv_info; 4487458575aSAndrea Mayer struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; 4497458575aSAndrea Mayer 4507458575aSAndrea Mayer if (seg6_next_csid_is_arg_zero(daddr, finfo)) 4517458575aSAndrea Mayer return input_action_end_x_core(skb, slwt); 4527458575aSAndrea Mayer 4537458575aSAndrea Mayer /* update DA */ 4547458575aSAndrea Mayer seg6_next_csid_advance_arg(daddr, finfo); 4557458575aSAndrea Mayer 4567458575aSAndrea Mayer return input_action_end_x_finish(skb, slwt); 4577458575aSAndrea Mayer } 4587458575aSAndrea Mayer 459848f3c0dSAndrea Mayer static bool seg6_next_csid_enabled(__u32 fops) 460848f3c0dSAndrea Mayer { 4617458575aSAndrea Mayer return fops & SEG6_F_LOCAL_FLV_NEXT_CSID; 4627458575aSAndrea Mayer } 4637458575aSAndrea Mayer 4647458575aSAndrea Mayer /* Processing of SRv6 End, End.X, and End.T behaviors can be extended through 4657458575aSAndrea Mayer * the flavors framework. These behaviors must report the subset of (flavor) 4667458575aSAndrea Mayer * operations they currently implement. In this way, if a user specifies a 4677458575aSAndrea Mayer * flavor combination that is not supported by a given End* behavior, the 4687458575aSAndrea Mayer * kernel refuses to instantiate the tunnel reporting the error. 4697458575aSAndrea Mayer */ 4707458575aSAndrea Mayer static int seg6_flv_supp_ops_by_action(int action, __u32 *fops) 4717458575aSAndrea Mayer { 4727458575aSAndrea Mayer switch (action) { 4737458575aSAndrea Mayer case SEG6_LOCAL_ACTION_END: 4747458575aSAndrea Mayer *fops = SEG6_LOCAL_END_FLV_SUPP_OPS; 4757458575aSAndrea Mayer break; 4767458575aSAndrea Mayer case SEG6_LOCAL_ACTION_END_X: 4777458575aSAndrea Mayer *fops = SEG6_LOCAL_END_X_FLV_SUPP_OPS; 4787458575aSAndrea Mayer break; 4797458575aSAndrea Mayer default: 4807458575aSAndrea Mayer return -EOPNOTSUPP; 4817458575aSAndrea Mayer } 4827458575aSAndrea Mayer 4837458575aSAndrea Mayer return 0; 484848f3c0dSAndrea Mayer } 485848f3c0dSAndrea Mayer 486bdf3c0b9SAndrea Mayer /* We describe the packet state in relation to the absence/presence of the SRH 487bdf3c0b9SAndrea Mayer * and the Segment Left (SL) field. 488bdf3c0b9SAndrea Mayer * For our purposes, it is not necessary to record the exact value of the SL 489bdf3c0b9SAndrea Mayer * when the SID List consists of two or more segments. 490bdf3c0b9SAndrea Mayer */ 491bdf3c0b9SAndrea Mayer enum seg6_local_pktinfo { 492bdf3c0b9SAndrea Mayer /* the order really matters! */ 493bdf3c0b9SAndrea Mayer SEG6_LOCAL_PKTINFO_NOHDR = 0, 494bdf3c0b9SAndrea Mayer SEG6_LOCAL_PKTINFO_SL_ZERO, 495bdf3c0b9SAndrea Mayer SEG6_LOCAL_PKTINFO_SL_ONE, 496bdf3c0b9SAndrea Mayer SEG6_LOCAL_PKTINFO_SL_MORE, 497bdf3c0b9SAndrea Mayer __SEG6_LOCAL_PKTINFO_MAX, 498bdf3c0b9SAndrea Mayer }; 499bdf3c0b9SAndrea Mayer 500bdf3c0b9SAndrea Mayer #define SEG6_LOCAL_PKTINFO_MAX (__SEG6_LOCAL_PKTINFO_MAX - 1) 501bdf3c0b9SAndrea Mayer 502bdf3c0b9SAndrea Mayer static enum seg6_local_pktinfo seg6_get_srh_pktinfo(struct ipv6_sr_hdr *srh) 503bdf3c0b9SAndrea Mayer { 504bdf3c0b9SAndrea Mayer __u8 sgl; 505bdf3c0b9SAndrea Mayer 506bdf3c0b9SAndrea Mayer if (!srh) 507bdf3c0b9SAndrea Mayer return SEG6_LOCAL_PKTINFO_NOHDR; 508bdf3c0b9SAndrea Mayer 509bdf3c0b9SAndrea Mayer sgl = srh->segments_left; 510bdf3c0b9SAndrea Mayer if (sgl < 2) 511bdf3c0b9SAndrea Mayer return SEG6_LOCAL_PKTINFO_SL_ZERO + sgl; 512bdf3c0b9SAndrea Mayer 513bdf3c0b9SAndrea Mayer return SEG6_LOCAL_PKTINFO_SL_MORE; 514bdf3c0b9SAndrea Mayer } 515bdf3c0b9SAndrea Mayer 516bdf3c0b9SAndrea Mayer enum seg6_local_flv_action { 517bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV_ACT_UNSPEC = 0, 518bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV_ACT_END, 519bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV_ACT_PSP, 520bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV_ACT_USP, 521bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV_ACT_USD, 522bdf3c0b9SAndrea Mayer __SEG6_LOCAL_FLV_ACT_MAX 523bdf3c0b9SAndrea Mayer }; 524bdf3c0b9SAndrea Mayer 525bdf3c0b9SAndrea Mayer #define SEG6_LOCAL_FLV_ACT_MAX (__SEG6_LOCAL_FLV_ACT_MAX - 1) 526bdf3c0b9SAndrea Mayer 527bdf3c0b9SAndrea Mayer /* The action table for RFC8986 flavors (see the flv8986_act_tbl below) 528bdf3c0b9SAndrea Mayer * contains the actions (i.e. processing operations) to be applied on packets 529bdf3c0b9SAndrea Mayer * when flavors are configured for an End* behavior. 530bdf3c0b9SAndrea Mayer * By combining the pkinfo data and from the flavors mask, the macro 531bdf3c0b9SAndrea Mayer * computes the index used to access the elements (actions) stored in the 532bdf3c0b9SAndrea Mayer * action table. The index is structured as follows: 533bdf3c0b9SAndrea Mayer * 534bdf3c0b9SAndrea Mayer * index 535bdf3c0b9SAndrea Mayer * _______________/\________________ 536bdf3c0b9SAndrea Mayer * / \ 537bdf3c0b9SAndrea Mayer * +----------------+----------------+ 538bdf3c0b9SAndrea Mayer * | pf | afm | 539bdf3c0b9SAndrea Mayer * +----------------+----------------+ 540bdf3c0b9SAndrea Mayer * ph-1 ... p1 p0 fk-1 ... f1 f0 541bdf3c0b9SAndrea Mayer * MSB LSB 542bdf3c0b9SAndrea Mayer * 543bdf3c0b9SAndrea Mayer * where: 544bdf3c0b9SAndrea Mayer * - 'afm' (adjusted flavor mask) is the mask containing a combination of the 545bdf3c0b9SAndrea Mayer * RFC8986 flavors currently supported. 'afm' corresponds to the @fm 546bdf3c0b9SAndrea Mayer * argument of the macro whose value is righ-shifted by 1 bit. By doing so, 547bdf3c0b9SAndrea Mayer * we discard the SEG6_LOCAL_FLV_OP_UNSPEC flag (bit 0 in @fm) which is 548bdf3c0b9SAndrea Mayer * never used here; 549bdf3c0b9SAndrea Mayer * - 'pf' encodes the packet info (pktinfo) regarding the presence/absence of 550bdf3c0b9SAndrea Mayer * the SRH, SL = 0, etc. 'pf' is set with the value of @pf provided as 551bdf3c0b9SAndrea Mayer * argument to the macro. 552bdf3c0b9SAndrea Mayer */ 553bdf3c0b9SAndrea Mayer #define flv8986_act_tbl_idx(pf, fm) \ 554bdf3c0b9SAndrea Mayer ((((pf) << bits_per(SEG6_LOCAL_FLV8986_SUPP_OPS)) | \ 555bdf3c0b9SAndrea Mayer ((fm) & SEG6_LOCAL_FLV8986_SUPP_OPS)) >> SEG6_LOCAL_FLV_OP_PSP) 556bdf3c0b9SAndrea Mayer 557bdf3c0b9SAndrea Mayer /* We compute the size of the action table by considering the RFC8986 flavors 558bdf3c0b9SAndrea Mayer * actually supported by the kernel. In this way, the size is automatically 559bdf3c0b9SAndrea Mayer * adjusted when new flavors are supported. 560bdf3c0b9SAndrea Mayer */ 561bdf3c0b9SAndrea Mayer #define FLV8986_ACT_TBL_SIZE \ 562bdf3c0b9SAndrea Mayer roundup_pow_of_two(flv8986_act_tbl_idx(SEG6_LOCAL_PKTINFO_MAX, \ 563bdf3c0b9SAndrea Mayer SEG6_LOCAL_FLV8986_SUPP_OPS)) 564bdf3c0b9SAndrea Mayer 565bdf3c0b9SAndrea Mayer /* tbl_cfg(act, pf, fm) macro is used to easily configure the action 566bdf3c0b9SAndrea Mayer * table; it accepts 3 arguments: 567bdf3c0b9SAndrea Mayer * i) @act, the suffix from SEG6_LOCAL_FLV_ACT_{act} representing 568bdf3c0b9SAndrea Mayer * the action that should be applied on the packet; 569bdf3c0b9SAndrea Mayer * ii) @pf, the suffix from SEG6_LOCAL_PKTINFO_{pf} reporting the packet 570bdf3c0b9SAndrea Mayer * info about the lack/presence of SRH, SRH with SL = 0, etc; 571bdf3c0b9SAndrea Mayer * iii) @fm, the mask of flavors. 572bdf3c0b9SAndrea Mayer */ 573bdf3c0b9SAndrea Mayer #define tbl_cfg(act, pf, fm) \ 574bdf3c0b9SAndrea Mayer [flv8986_act_tbl_idx(SEG6_LOCAL_PKTINFO_##pf, \ 575bdf3c0b9SAndrea Mayer (fm))] = SEG6_LOCAL_FLV_ACT_##act 576bdf3c0b9SAndrea Mayer 577bdf3c0b9SAndrea Mayer /* shorthand for improving readability */ 578bdf3c0b9SAndrea Mayer #define F_PSP SEG6_F_LOCAL_FLV_PSP 579bdf3c0b9SAndrea Mayer 580bdf3c0b9SAndrea Mayer /* The table contains, for each combination of the pktinfo data and 581bdf3c0b9SAndrea Mayer * flavors, the action that should be taken on a packet (e.g. 582bdf3c0b9SAndrea Mayer * "standard" Endpoint processing, Penultimate Segment Pop, etc). 583bdf3c0b9SAndrea Mayer * 584bdf3c0b9SAndrea Mayer * By default, table entries not explicitly configured are initialized with the 585bdf3c0b9SAndrea Mayer * SEG6_LOCAL_FLV_ACT_UNSPEC action, which generally has the effect of 586bdf3c0b9SAndrea Mayer * discarding the processed packet. 587bdf3c0b9SAndrea Mayer */ 588bdf3c0b9SAndrea Mayer static const u8 flv8986_act_tbl[FLV8986_ACT_TBL_SIZE] = { 589bdf3c0b9SAndrea Mayer /* PSP variant for packet where SRH with SL = 1 */ 590bdf3c0b9SAndrea Mayer tbl_cfg(PSP, SL_ONE, F_PSP), 591bdf3c0b9SAndrea Mayer /* End for packet where the SRH with SL > 1*/ 592bdf3c0b9SAndrea Mayer tbl_cfg(END, SL_MORE, F_PSP), 593bdf3c0b9SAndrea Mayer }; 594bdf3c0b9SAndrea Mayer 595bdf3c0b9SAndrea Mayer #undef F_PSP 596bdf3c0b9SAndrea Mayer #undef tbl_cfg 597bdf3c0b9SAndrea Mayer 598bdf3c0b9SAndrea Mayer /* For each flavor defined in RFC8986 (or a combination of them) an action is 599bdf3c0b9SAndrea Mayer * performed on the packet. The specific action depends on: 600bdf3c0b9SAndrea Mayer * - info extracted from the packet (i.e. pktinfo data) regarding the 601bdf3c0b9SAndrea Mayer * lack/presence of the SRH, and if the SRH is available, on the value of 602bdf3c0b9SAndrea Mayer * Segment Left field; 603bdf3c0b9SAndrea Mayer * - the mask of flavors configured for the specific SRv6 End* behavior. 604bdf3c0b9SAndrea Mayer * 605bdf3c0b9SAndrea Mayer * The function combines both the pkinfo and the flavors mask to evaluate the 606bdf3c0b9SAndrea Mayer * corresponding action to be taken on the packet. 607bdf3c0b9SAndrea Mayer */ 608bdf3c0b9SAndrea Mayer static enum seg6_local_flv_action 609bdf3c0b9SAndrea Mayer seg6_local_flv8986_act_lookup(enum seg6_local_pktinfo pinfo, __u32 flvmask) 610bdf3c0b9SAndrea Mayer { 611bdf3c0b9SAndrea Mayer unsigned long index; 612bdf3c0b9SAndrea Mayer 613bdf3c0b9SAndrea Mayer /* check if the provided mask of flavors is supported */ 614bdf3c0b9SAndrea Mayer if (unlikely(flvmask & ~SEG6_LOCAL_FLV8986_SUPP_OPS)) 615bdf3c0b9SAndrea Mayer return SEG6_LOCAL_FLV_ACT_UNSPEC; 616bdf3c0b9SAndrea Mayer 617bdf3c0b9SAndrea Mayer index = flv8986_act_tbl_idx(pinfo, flvmask); 618bdf3c0b9SAndrea Mayer if (unlikely(index >= FLV8986_ACT_TBL_SIZE)) 619bdf3c0b9SAndrea Mayer return SEG6_LOCAL_FLV_ACT_UNSPEC; 620bdf3c0b9SAndrea Mayer 621bdf3c0b9SAndrea Mayer return flv8986_act_tbl[index]; 622bdf3c0b9SAndrea Mayer } 623bdf3c0b9SAndrea Mayer 624bdf3c0b9SAndrea Mayer /* skb->data must be aligned with skb->network_header */ 625bdf3c0b9SAndrea Mayer static bool seg6_pop_srh(struct sk_buff *skb, int srhoff) 626bdf3c0b9SAndrea Mayer { 627bdf3c0b9SAndrea Mayer struct ipv6_sr_hdr *srh; 628bdf3c0b9SAndrea Mayer struct ipv6hdr *iph; 629bdf3c0b9SAndrea Mayer __u8 srh_nexthdr; 630bdf3c0b9SAndrea Mayer int thoff = -1; 631bdf3c0b9SAndrea Mayer int srhlen; 632bdf3c0b9SAndrea Mayer int nhlen; 633bdf3c0b9SAndrea Mayer 634bdf3c0b9SAndrea Mayer if (unlikely(srhoff < sizeof(*iph) || 635bdf3c0b9SAndrea Mayer !pskb_may_pull(skb, srhoff + sizeof(*srh)))) 636bdf3c0b9SAndrea Mayer return false; 637bdf3c0b9SAndrea Mayer 638bdf3c0b9SAndrea Mayer srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 639bdf3c0b9SAndrea Mayer srhlen = ipv6_optlen(srh); 640bdf3c0b9SAndrea Mayer 641bdf3c0b9SAndrea Mayer /* we are about to mangle the pkt, let's check if we can write on it */ 642bdf3c0b9SAndrea Mayer if (unlikely(skb_ensure_writable(skb, srhoff + srhlen))) 643bdf3c0b9SAndrea Mayer return false; 644bdf3c0b9SAndrea Mayer 645bdf3c0b9SAndrea Mayer /* skb_ensure_writable() may change skb pointers; evaluate srh again */ 646bdf3c0b9SAndrea Mayer srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 647bdf3c0b9SAndrea Mayer srh_nexthdr = srh->nexthdr; 648bdf3c0b9SAndrea Mayer 649bdf3c0b9SAndrea Mayer if (unlikely(!skb_transport_header_was_set(skb))) 650bdf3c0b9SAndrea Mayer goto pull; 651bdf3c0b9SAndrea Mayer 652bdf3c0b9SAndrea Mayer nhlen = skb_network_header_len(skb); 653bdf3c0b9SAndrea Mayer /* we have to deal with the transport header: it could be set before 654bdf3c0b9SAndrea Mayer * the SRH, after the SRH, or within it (which is considered wrong, 655bdf3c0b9SAndrea Mayer * however). 656bdf3c0b9SAndrea Mayer */ 657bdf3c0b9SAndrea Mayer if (likely(nhlen <= srhoff)) 658bdf3c0b9SAndrea Mayer thoff = nhlen; 659bdf3c0b9SAndrea Mayer else if (nhlen >= srhoff + srhlen) 660bdf3c0b9SAndrea Mayer /* transport_header is set after the SRH */ 661bdf3c0b9SAndrea Mayer thoff = nhlen - srhlen; 662bdf3c0b9SAndrea Mayer else 663bdf3c0b9SAndrea Mayer /* transport_header falls inside the SRH; hence, we can't 664bdf3c0b9SAndrea Mayer * restore the transport_header pointer properly after 665bdf3c0b9SAndrea Mayer * SRH removing operation. 666bdf3c0b9SAndrea Mayer */ 667bdf3c0b9SAndrea Mayer return false; 668bdf3c0b9SAndrea Mayer pull: 669bdf3c0b9SAndrea Mayer /* we need to pop the SRH: 670bdf3c0b9SAndrea Mayer * 1) first of all, we pull out everything from IPv6 header up to SRH 671bdf3c0b9SAndrea Mayer * (included) evaluating also the rcsum; 672bdf3c0b9SAndrea Mayer * 2) we overwrite (and then remove) the SRH by properly moving the 673bdf3c0b9SAndrea Mayer * IPv6 along with any extension header that precedes the SRH; 674bdf3c0b9SAndrea Mayer * 3) At the end, we push back the pulled headers (except for SRH, 675bdf3c0b9SAndrea Mayer * obviously). 676bdf3c0b9SAndrea Mayer */ 677bdf3c0b9SAndrea Mayer skb_pull_rcsum(skb, srhoff + srhlen); 678bdf3c0b9SAndrea Mayer memmove(skb_network_header(skb) + srhlen, skb_network_header(skb), 679bdf3c0b9SAndrea Mayer srhoff); 680bdf3c0b9SAndrea Mayer skb_push(skb, srhoff); 681bdf3c0b9SAndrea Mayer 682bdf3c0b9SAndrea Mayer skb_reset_network_header(skb); 683bdf3c0b9SAndrea Mayer skb_mac_header_rebuild(skb); 684bdf3c0b9SAndrea Mayer if (likely(thoff >= 0)) 685bdf3c0b9SAndrea Mayer skb_set_transport_header(skb, thoff); 686bdf3c0b9SAndrea Mayer 687bdf3c0b9SAndrea Mayer iph = ipv6_hdr(skb); 688bdf3c0b9SAndrea Mayer if (iph->nexthdr == NEXTHDR_ROUTING) { 689bdf3c0b9SAndrea Mayer iph->nexthdr = srh_nexthdr; 690bdf3c0b9SAndrea Mayer } else { 691bdf3c0b9SAndrea Mayer /* we must look for the extension header (EXTH, for short) that 692bdf3c0b9SAndrea Mayer * immediately precedes the SRH we have just removed. 693bdf3c0b9SAndrea Mayer * Then, we update the value of the EXTH nexthdr with the one 694bdf3c0b9SAndrea Mayer * contained in the SRH nexthdr. 695bdf3c0b9SAndrea Mayer */ 696bdf3c0b9SAndrea Mayer unsigned int off = sizeof(*iph); 697bdf3c0b9SAndrea Mayer struct ipv6_opt_hdr *hp, _hdr; 698bdf3c0b9SAndrea Mayer __u8 nexthdr = iph->nexthdr; 699bdf3c0b9SAndrea Mayer 700bdf3c0b9SAndrea Mayer for (;;) { 701bdf3c0b9SAndrea Mayer if (unlikely(!ipv6_ext_hdr(nexthdr) || 702bdf3c0b9SAndrea Mayer nexthdr == NEXTHDR_NONE)) 703bdf3c0b9SAndrea Mayer return false; 704bdf3c0b9SAndrea Mayer 705bdf3c0b9SAndrea Mayer hp = skb_header_pointer(skb, off, sizeof(_hdr), &_hdr); 706bdf3c0b9SAndrea Mayer if (unlikely(!hp)) 707bdf3c0b9SAndrea Mayer return false; 708bdf3c0b9SAndrea Mayer 709bdf3c0b9SAndrea Mayer if (hp->nexthdr == NEXTHDR_ROUTING) { 710bdf3c0b9SAndrea Mayer hp->nexthdr = srh_nexthdr; 711bdf3c0b9SAndrea Mayer break; 712bdf3c0b9SAndrea Mayer } 713bdf3c0b9SAndrea Mayer 714bdf3c0b9SAndrea Mayer switch (nexthdr) { 715bdf3c0b9SAndrea Mayer case NEXTHDR_FRAGMENT: 716bdf3c0b9SAndrea Mayer fallthrough; 717bdf3c0b9SAndrea Mayer case NEXTHDR_AUTH: 718bdf3c0b9SAndrea Mayer /* we expect SRH before FRAG and AUTH */ 719bdf3c0b9SAndrea Mayer return false; 720bdf3c0b9SAndrea Mayer default: 721bdf3c0b9SAndrea Mayer off += ipv6_optlen(hp); 722bdf3c0b9SAndrea Mayer break; 723bdf3c0b9SAndrea Mayer } 724bdf3c0b9SAndrea Mayer 725bdf3c0b9SAndrea Mayer nexthdr = hp->nexthdr; 726bdf3c0b9SAndrea Mayer } 727bdf3c0b9SAndrea Mayer } 728bdf3c0b9SAndrea Mayer 729bdf3c0b9SAndrea Mayer iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 730bdf3c0b9SAndrea Mayer 731bdf3c0b9SAndrea Mayer skb_postpush_rcsum(skb, iph, srhoff); 732bdf3c0b9SAndrea Mayer 733bdf3c0b9SAndrea Mayer return true; 734bdf3c0b9SAndrea Mayer } 735bdf3c0b9SAndrea Mayer 736bdf3c0b9SAndrea Mayer /* process the packet on the basis of the RFC8986 flavors set for the given 737bdf3c0b9SAndrea Mayer * SRv6 End behavior instance. 738bdf3c0b9SAndrea Mayer */ 739bdf3c0b9SAndrea Mayer static int end_flv8986_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) 740bdf3c0b9SAndrea Mayer { 741bdf3c0b9SAndrea Mayer const struct seg6_flavors_info *finfo = &slwt->flv_info; 742bdf3c0b9SAndrea Mayer enum seg6_local_flv_action action; 743bdf3c0b9SAndrea Mayer enum seg6_local_pktinfo pinfo; 744bdf3c0b9SAndrea Mayer struct ipv6_sr_hdr *srh; 745bdf3c0b9SAndrea Mayer __u32 flvmask; 746bdf3c0b9SAndrea Mayer int srhoff; 747bdf3c0b9SAndrea Mayer 748bdf3c0b9SAndrea Mayer srh = seg6_get_srh(skb, 0); 749bdf3c0b9SAndrea Mayer srhoff = srh ? ((unsigned char *)srh - skb->data) : 0; 750bdf3c0b9SAndrea Mayer pinfo = seg6_get_srh_pktinfo(srh); 751bdf3c0b9SAndrea Mayer #ifdef CONFIG_IPV6_SEG6_HMAC 752bdf3c0b9SAndrea Mayer if (srh && !seg6_hmac_validate_skb(skb)) 753bdf3c0b9SAndrea Mayer goto drop; 754bdf3c0b9SAndrea Mayer #endif 755bdf3c0b9SAndrea Mayer flvmask = finfo->flv_ops; 756bdf3c0b9SAndrea Mayer if (unlikely(flvmask & ~SEG6_LOCAL_FLV8986_SUPP_OPS)) { 757bdf3c0b9SAndrea Mayer pr_warn_once("seg6local: invalid RFC8986 flavors\n"); 758bdf3c0b9SAndrea Mayer goto drop; 759bdf3c0b9SAndrea Mayer } 760bdf3c0b9SAndrea Mayer 761bdf3c0b9SAndrea Mayer /* retrieve the action triggered by the combination of pktinfo data and 762bdf3c0b9SAndrea Mayer * the flavors mask. 763bdf3c0b9SAndrea Mayer */ 764bdf3c0b9SAndrea Mayer action = seg6_local_flv8986_act_lookup(pinfo, flvmask); 765bdf3c0b9SAndrea Mayer switch (action) { 766bdf3c0b9SAndrea Mayer case SEG6_LOCAL_FLV_ACT_END: 767bdf3c0b9SAndrea Mayer /* process the packet as the "standard" End behavior */ 768bdf3c0b9SAndrea Mayer advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 769bdf3c0b9SAndrea Mayer break; 770bdf3c0b9SAndrea Mayer case SEG6_LOCAL_FLV_ACT_PSP: 771bdf3c0b9SAndrea Mayer advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 772bdf3c0b9SAndrea Mayer 773bdf3c0b9SAndrea Mayer if (unlikely(!seg6_pop_srh(skb, srhoff))) 774bdf3c0b9SAndrea Mayer goto drop; 775bdf3c0b9SAndrea Mayer break; 776bdf3c0b9SAndrea Mayer case SEG6_LOCAL_FLV_ACT_UNSPEC: 777bdf3c0b9SAndrea Mayer fallthrough; 778bdf3c0b9SAndrea Mayer default: 779bdf3c0b9SAndrea Mayer /* by default, we drop the packet since we could not find a 780bdf3c0b9SAndrea Mayer * suitable action. 781bdf3c0b9SAndrea Mayer */ 782bdf3c0b9SAndrea Mayer goto drop; 783bdf3c0b9SAndrea Mayer } 784bdf3c0b9SAndrea Mayer 785bdf3c0b9SAndrea Mayer return input_action_end_finish(skb, slwt); 786bdf3c0b9SAndrea Mayer 787bdf3c0b9SAndrea Mayer drop: 788bdf3c0b9SAndrea Mayer kfree_skb(skb); 789bdf3c0b9SAndrea Mayer return -EINVAL; 790bdf3c0b9SAndrea Mayer } 791bdf3c0b9SAndrea Mayer 792848f3c0dSAndrea Mayer /* regular endpoint function */ 793848f3c0dSAndrea Mayer static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 794848f3c0dSAndrea Mayer { 795848f3c0dSAndrea Mayer const struct seg6_flavors_info *finfo = &slwt->flv_info; 796bdf3c0b9SAndrea Mayer __u32 fops = finfo->flv_ops; 797848f3c0dSAndrea Mayer 798bdf3c0b9SAndrea Mayer if (!fops) 799bdf3c0b9SAndrea Mayer return input_action_end_core(skb, slwt); 800bdf3c0b9SAndrea Mayer 801bdf3c0b9SAndrea Mayer /* check for the presence of NEXT-C-SID since it applies first */ 802bdf3c0b9SAndrea Mayer if (seg6_next_csid_enabled(fops)) 803848f3c0dSAndrea Mayer return end_next_csid_core(skb, slwt); 804848f3c0dSAndrea Mayer 805bdf3c0b9SAndrea Mayer /* the specific processing function to be performed on the packet 806bdf3c0b9SAndrea Mayer * depends on the combination of flavors defined in RFC8986 and some 807bdf3c0b9SAndrea Mayer * information extracted from the packet, e.g. presence/absence of SRH, 808bdf3c0b9SAndrea Mayer * Segment Left = 0, etc. 809bdf3c0b9SAndrea Mayer */ 810bdf3c0b9SAndrea Mayer return end_flv8986_core(skb, slwt); 811848f3c0dSAndrea Mayer } 812848f3c0dSAndrea Mayer 813140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */ 814140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) 815140f04c3SDavid Lebrun { 8167458575aSAndrea Mayer const struct seg6_flavors_info *finfo = &slwt->flv_info; 8177458575aSAndrea Mayer __u32 fops = finfo->flv_ops; 818140f04c3SDavid Lebrun 8197458575aSAndrea Mayer /* check for the presence of NEXT-C-SID since it applies first */ 8207458575aSAndrea Mayer if (seg6_next_csid_enabled(fops)) 8217458575aSAndrea Mayer return end_x_next_csid_core(skb, slwt); 822140f04c3SDavid Lebrun 8237458575aSAndrea Mayer return input_action_end_x_core(skb, slwt); 824140f04c3SDavid Lebrun } 825140f04c3SDavid Lebrun 826891ef8ddSDavid Lebrun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) 827891ef8ddSDavid Lebrun { 828891ef8ddSDavid Lebrun struct ipv6_sr_hdr *srh; 829891ef8ddSDavid Lebrun 830891ef8ddSDavid Lebrun srh = get_and_validate_srh(skb); 831891ef8ddSDavid Lebrun if (!srh) 832891ef8ddSDavid Lebrun goto drop; 833891ef8ddSDavid Lebrun 834891ef8ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 835891ef8ddSDavid Lebrun 8361c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, slwt->table); 837891ef8ddSDavid Lebrun 838891ef8ddSDavid Lebrun return dst_input(skb); 839891ef8ddSDavid Lebrun 840891ef8ddSDavid Lebrun drop: 841891ef8ddSDavid Lebrun kfree_skb(skb); 842891ef8ddSDavid Lebrun return -EINVAL; 843891ef8ddSDavid Lebrun } 844891ef8ddSDavid Lebrun 845891ef8ddSDavid Lebrun /* decapsulate and forward inner L2 frame on specified interface */ 846891ef8ddSDavid Lebrun static int input_action_end_dx2(struct sk_buff *skb, 847891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 848891ef8ddSDavid Lebrun { 849891ef8ddSDavid Lebrun struct net *net = dev_net(skb->dev); 850891ef8ddSDavid Lebrun struct net_device *odev; 851891ef8ddSDavid Lebrun struct ethhdr *eth; 852891ef8ddSDavid Lebrun 85326776253SPaolo Lungaroni if (!decap_and_validate(skb, IPPROTO_ETHERNET)) 854891ef8ddSDavid Lebrun goto drop; 855891ef8ddSDavid Lebrun 856891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, ETH_HLEN)) 857891ef8ddSDavid Lebrun goto drop; 858891ef8ddSDavid Lebrun 859891ef8ddSDavid Lebrun skb_reset_mac_header(skb); 860891ef8ddSDavid Lebrun eth = (struct ethhdr *)skb->data; 861891ef8ddSDavid Lebrun 862891ef8ddSDavid Lebrun /* To determine the frame's protocol, we assume it is 802.3. This avoids 863891ef8ddSDavid Lebrun * a call to eth_type_trans(), which is not really relevant for our 864891ef8ddSDavid Lebrun * use case. 865891ef8ddSDavid Lebrun */ 866891ef8ddSDavid Lebrun if (!eth_proto_is_802_3(eth->h_proto)) 867891ef8ddSDavid Lebrun goto drop; 868891ef8ddSDavid Lebrun 869891ef8ddSDavid Lebrun odev = dev_get_by_index_rcu(net, slwt->oif); 870891ef8ddSDavid Lebrun if (!odev) 871891ef8ddSDavid Lebrun goto drop; 872891ef8ddSDavid Lebrun 873891ef8ddSDavid Lebrun /* As we accept Ethernet frames, make sure the egress device is of 874891ef8ddSDavid Lebrun * the correct type. 875891ef8ddSDavid Lebrun */ 876891ef8ddSDavid Lebrun if (odev->type != ARPHRD_ETHER) 877891ef8ddSDavid Lebrun goto drop; 878891ef8ddSDavid Lebrun 879891ef8ddSDavid Lebrun if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev)) 880891ef8ddSDavid Lebrun goto drop; 881891ef8ddSDavid Lebrun 882891ef8ddSDavid Lebrun skb_orphan(skb); 883891ef8ddSDavid Lebrun 884891ef8ddSDavid Lebrun if (skb_warn_if_lro(skb)) 885891ef8ddSDavid Lebrun goto drop; 886891ef8ddSDavid Lebrun 887891ef8ddSDavid Lebrun skb_forward_csum(skb); 888891ef8ddSDavid Lebrun 889891ef8ddSDavid Lebrun if (skb->len - ETH_HLEN > odev->mtu) 890891ef8ddSDavid Lebrun goto drop; 891891ef8ddSDavid Lebrun 892891ef8ddSDavid Lebrun skb->dev = odev; 893891ef8ddSDavid Lebrun skb->protocol = eth->h_proto; 894891ef8ddSDavid Lebrun 895891ef8ddSDavid Lebrun return dev_queue_xmit(skb); 896891ef8ddSDavid Lebrun 897891ef8ddSDavid Lebrun drop: 898891ef8ddSDavid Lebrun kfree_skb(skb); 899891ef8ddSDavid Lebrun return -EINVAL; 900891ef8ddSDavid Lebrun } 901891ef8ddSDavid Lebrun 9027a3f5b0dSRyoga Saito static int input_action_end_dx6_finish(struct net *net, struct sock *sk, 9037a3f5b0dSRyoga Saito struct sk_buff *skb) 9047a3f5b0dSRyoga Saito { 9057a3f5b0dSRyoga Saito struct dst_entry *orig_dst = skb_dst(skb); 9067a3f5b0dSRyoga Saito struct in6_addr *nhaddr = NULL; 9077a3f5b0dSRyoga Saito struct seg6_local_lwt *slwt; 9087a3f5b0dSRyoga Saito 9097a3f5b0dSRyoga Saito slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 9107a3f5b0dSRyoga Saito 9117a3f5b0dSRyoga Saito /* The inner packet is not associated to any local interface, 9127a3f5b0dSRyoga Saito * so we do not call netif_rx(). 9137a3f5b0dSRyoga Saito * 9147a3f5b0dSRyoga Saito * If slwt->nh6 is set to ::, then lookup the nexthop for the 9157a3f5b0dSRyoga Saito * inner packet's DA. Otherwise, use the specified nexthop. 9167a3f5b0dSRyoga Saito */ 9177a3f5b0dSRyoga Saito if (!ipv6_addr_any(&slwt->nh6)) 9187a3f5b0dSRyoga Saito nhaddr = &slwt->nh6; 9197a3f5b0dSRyoga Saito 9207a3f5b0dSRyoga Saito seg6_lookup_nexthop(skb, nhaddr, 0); 9217a3f5b0dSRyoga Saito 9227a3f5b0dSRyoga Saito return dst_input(skb); 9237a3f5b0dSRyoga Saito } 9247a3f5b0dSRyoga Saito 925140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */ 926140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb, 927140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 928140f04c3SDavid Lebrun { 929140f04c3SDavid Lebrun /* this function accepts IPv6 encapsulated packets, with either 930140f04c3SDavid Lebrun * an SRH with SL=0, or no SRH. 931140f04c3SDavid Lebrun */ 932140f04c3SDavid Lebrun 933d7a669ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 934140f04c3SDavid Lebrun goto drop; 935140f04c3SDavid Lebrun 936d7a669ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 937140f04c3SDavid Lebrun goto drop; 938140f04c3SDavid Lebrun 939c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 9407a3f5b0dSRyoga Saito nf_reset_ct(skb); 941c71644d0SAndrea Mayer 9427a3f5b0dSRyoga Saito if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 9437a3f5b0dSRyoga Saito return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, 944*d62df86cSJianguo Wu dev_net(skb->dev), NULL, skb, skb->dev, 945*d62df86cSJianguo Wu NULL, input_action_end_dx6_finish); 946140f04c3SDavid Lebrun 9477a3f5b0dSRyoga Saito return input_action_end_dx6_finish(dev_net(skb->dev), NULL, skb); 948140f04c3SDavid Lebrun drop: 949140f04c3SDavid Lebrun kfree_skb(skb); 950140f04c3SDavid Lebrun return -EINVAL; 951140f04c3SDavid Lebrun } 952140f04c3SDavid Lebrun 9537a3f5b0dSRyoga Saito static int input_action_end_dx4_finish(struct net *net, struct sock *sk, 9547a3f5b0dSRyoga Saito struct sk_buff *skb) 955891ef8ddSDavid Lebrun { 9567a3f5b0dSRyoga Saito struct dst_entry *orig_dst = skb_dst(skb); 9577a3f5b0dSRyoga Saito struct seg6_local_lwt *slwt; 958891ef8ddSDavid Lebrun struct iphdr *iph; 959891ef8ddSDavid Lebrun __be32 nhaddr; 960891ef8ddSDavid Lebrun int err; 961891ef8ddSDavid Lebrun 9627a3f5b0dSRyoga Saito slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 963891ef8ddSDavid Lebrun 964891ef8ddSDavid Lebrun iph = ip_hdr(skb); 965891ef8ddSDavid Lebrun 966891ef8ddSDavid Lebrun nhaddr = slwt->nh4.s_addr ?: iph->daddr; 967891ef8ddSDavid Lebrun 968891ef8ddSDavid Lebrun skb_dst_drop(skb); 969891ef8ddSDavid Lebrun 970891ef8ddSDavid Lebrun err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev); 9717a3f5b0dSRyoga Saito if (err) { 9727a3f5b0dSRyoga Saito kfree_skb(skb); 9737a3f5b0dSRyoga Saito return -EINVAL; 9747a3f5b0dSRyoga Saito } 975891ef8ddSDavid Lebrun 976891ef8ddSDavid Lebrun return dst_input(skb); 9777a3f5b0dSRyoga Saito } 978891ef8ddSDavid Lebrun 9797a3f5b0dSRyoga Saito static int input_action_end_dx4(struct sk_buff *skb, 9807a3f5b0dSRyoga Saito struct seg6_local_lwt *slwt) 9817a3f5b0dSRyoga Saito { 9827a3f5b0dSRyoga Saito if (!decap_and_validate(skb, IPPROTO_IPIP)) 9837a3f5b0dSRyoga Saito goto drop; 9847a3f5b0dSRyoga Saito 9857a3f5b0dSRyoga Saito if (!pskb_may_pull(skb, sizeof(struct iphdr))) 9867a3f5b0dSRyoga Saito goto drop; 9877a3f5b0dSRyoga Saito 9887a3f5b0dSRyoga Saito skb->protocol = htons(ETH_P_IP); 9897a3f5b0dSRyoga Saito skb_set_transport_header(skb, sizeof(struct iphdr)); 9907a3f5b0dSRyoga Saito nf_reset_ct(skb); 9917a3f5b0dSRyoga Saito 9927a3f5b0dSRyoga Saito if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 9937a3f5b0dSRyoga Saito return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, 994*d62df86cSJianguo Wu dev_net(skb->dev), NULL, skb, skb->dev, 995*d62df86cSJianguo Wu NULL, input_action_end_dx4_finish); 9967a3f5b0dSRyoga Saito 9977a3f5b0dSRyoga Saito return input_action_end_dx4_finish(dev_net(skb->dev), NULL, skb); 998891ef8ddSDavid Lebrun drop: 999891ef8ddSDavid Lebrun kfree_skb(skb); 1000891ef8ddSDavid Lebrun return -EINVAL; 1001891ef8ddSDavid Lebrun } 1002891ef8ddSDavid Lebrun 1003664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 1004664d6f86SAndrea Mayer static struct net *fib6_config_get_net(const struct fib6_config *fib6_cfg) 1005664d6f86SAndrea Mayer { 1006664d6f86SAndrea Mayer const struct nl_info *nli = &fib6_cfg->fc_nlinfo; 1007664d6f86SAndrea Mayer 1008664d6f86SAndrea Mayer return nli->nl_net; 1009664d6f86SAndrea Mayer } 1010664d6f86SAndrea Mayer 1011664d6f86SAndrea Mayer static int __seg6_end_dt_vrf_build(struct seg6_local_lwt *slwt, const void *cfg, 1012664d6f86SAndrea Mayer u16 family, struct netlink_ext_ack *extack) 1013664d6f86SAndrea Mayer { 1014664d6f86SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 1015664d6f86SAndrea Mayer int vrf_ifindex; 1016664d6f86SAndrea Mayer struct net *net; 1017664d6f86SAndrea Mayer 1018664d6f86SAndrea Mayer net = fib6_config_get_net(cfg); 1019664d6f86SAndrea Mayer 1020664d6f86SAndrea Mayer /* note that vrf_table was already set by parse_nla_vrftable() */ 1021664d6f86SAndrea Mayer vrf_ifindex = l3mdev_ifindex_lookup_by_table_id(L3MDEV_TYPE_VRF, net, 1022664d6f86SAndrea Mayer info->vrf_table); 1023664d6f86SAndrea Mayer if (vrf_ifindex < 0) { 1024664d6f86SAndrea Mayer if (vrf_ifindex == -EPERM) { 1025664d6f86SAndrea Mayer NL_SET_ERR_MSG(extack, 1026664d6f86SAndrea Mayer "Strict mode for VRF is disabled"); 1027664d6f86SAndrea Mayer } else if (vrf_ifindex == -ENODEV) { 1028664d6f86SAndrea Mayer NL_SET_ERR_MSG(extack, 1029664d6f86SAndrea Mayer "Table has no associated VRF device"); 1030664d6f86SAndrea Mayer } else { 1031664d6f86SAndrea Mayer pr_debug("seg6local: SRv6 End.DT* creation error=%d\n", 1032664d6f86SAndrea Mayer vrf_ifindex); 1033664d6f86SAndrea Mayer } 1034664d6f86SAndrea Mayer 1035664d6f86SAndrea Mayer return vrf_ifindex; 1036664d6f86SAndrea Mayer } 1037664d6f86SAndrea Mayer 1038664d6f86SAndrea Mayer info->net = net; 1039664d6f86SAndrea Mayer info->vrf_ifindex = vrf_ifindex; 1040664d6f86SAndrea Mayer 1041664d6f86SAndrea Mayer info->family = family; 1042664d6f86SAndrea Mayer info->mode = DT_VRF_MODE; 1043664d6f86SAndrea Mayer 1044664d6f86SAndrea Mayer return 0; 1045664d6f86SAndrea Mayer } 1046664d6f86SAndrea Mayer 1047664d6f86SAndrea Mayer /* The SRv6 End.DT4/DT6 behavior extracts the inner (IPv4/IPv6) packet and 1048664d6f86SAndrea Mayer * routes the IPv4/IPv6 packet by looking at the configured routing table. 1049664d6f86SAndrea Mayer * 1050664d6f86SAndrea Mayer * In the SRv6 End.DT4/DT6 use case, we can receive traffic (IPv6+Segment 1051664d6f86SAndrea Mayer * Routing Header packets) from several interfaces and the outer IPv6 1052664d6f86SAndrea Mayer * destination address (DA) is used for retrieving the specific instance of the 1053664d6f86SAndrea Mayer * End.DT4/DT6 behavior that should process the packets. 1054664d6f86SAndrea Mayer * 1055664d6f86SAndrea Mayer * However, the inner IPv4/IPv6 packet is not really bound to any receiving 1056664d6f86SAndrea Mayer * interface and thus the End.DT4/DT6 sets the VRF (associated with the 1057664d6f86SAndrea Mayer * corresponding routing table) as the *receiving* interface. 1058664d6f86SAndrea Mayer * In other words, the End.DT4/DT6 processes a packet as if it has been received 1059664d6f86SAndrea Mayer * directly by the VRF (and not by one of its slave devices, if any). 1060664d6f86SAndrea Mayer * In this way, the VRF interface is used for routing the IPv4/IPv6 packet in 1061664d6f86SAndrea Mayer * according to the routing table configured by the End.DT4/DT6 instance. 1062664d6f86SAndrea Mayer * 1063664d6f86SAndrea Mayer * This design allows you to get some interesting features like: 1064664d6f86SAndrea Mayer * 1) the statistics on rx packets; 1065664d6f86SAndrea Mayer * 2) the possibility to install a packet sniffer on the receiving interface 1066664d6f86SAndrea Mayer * (the VRF one) for looking at the incoming packets; 1067664d6f86SAndrea Mayer * 3) the possibility to leverage the netfilter prerouting hook for the inner 1068664d6f86SAndrea Mayer * IPv4 packet. 1069664d6f86SAndrea Mayer * 1070664d6f86SAndrea Mayer * This function returns: 1071664d6f86SAndrea Mayer * - the sk_buff* when the VRF rcv handler has processed the packet correctly; 1072664d6f86SAndrea Mayer * - NULL when the skb is consumed by the VRF rcv handler; 1073664d6f86SAndrea Mayer * - a pointer which encodes a negative error number in case of error. 1074664d6f86SAndrea Mayer * Note that in this case, the function takes care of freeing the skb. 1075664d6f86SAndrea Mayer */ 1076664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_rcv(struct sk_buff *skb, u16 family, 1077664d6f86SAndrea Mayer struct net_device *dev) 1078664d6f86SAndrea Mayer { 1079664d6f86SAndrea Mayer /* based on l3mdev_ip_rcv; we are only interested in the master */ 1080664d6f86SAndrea Mayer if (unlikely(!netif_is_l3_master(dev) && !netif_has_l3_rx_handler(dev))) 1081664d6f86SAndrea Mayer goto drop; 1082664d6f86SAndrea Mayer 1083664d6f86SAndrea Mayer if (unlikely(!dev->l3mdev_ops->l3mdev_l3_rcv)) 1084664d6f86SAndrea Mayer goto drop; 1085664d6f86SAndrea Mayer 1086664d6f86SAndrea Mayer /* the decap packet IPv4/IPv6 does not come with any mac header info. 1087664d6f86SAndrea Mayer * We must unset the mac header to allow the VRF device to rebuild it, 1088664d6f86SAndrea Mayer * just in case there is a sniffer attached on the device. 1089664d6f86SAndrea Mayer */ 1090664d6f86SAndrea Mayer skb_unset_mac_header(skb); 1091664d6f86SAndrea Mayer 1092664d6f86SAndrea Mayer skb = dev->l3mdev_ops->l3mdev_l3_rcv(dev, skb, family); 1093664d6f86SAndrea Mayer if (!skb) 1094664d6f86SAndrea Mayer /* the skb buffer was consumed by the handler */ 1095664d6f86SAndrea Mayer return NULL; 1096664d6f86SAndrea Mayer 1097664d6f86SAndrea Mayer /* when a packet is received by a VRF or by one of its slaves, the 1098664d6f86SAndrea Mayer * master device reference is set into the skb. 1099664d6f86SAndrea Mayer */ 1100664d6f86SAndrea Mayer if (unlikely(skb->dev != dev || skb->skb_iif != dev->ifindex)) 1101664d6f86SAndrea Mayer goto drop; 1102664d6f86SAndrea Mayer 1103664d6f86SAndrea Mayer return skb; 1104664d6f86SAndrea Mayer 1105664d6f86SAndrea Mayer drop: 1106664d6f86SAndrea Mayer kfree_skb(skb); 1107664d6f86SAndrea Mayer return ERR_PTR(-EINVAL); 1108664d6f86SAndrea Mayer } 1109664d6f86SAndrea Mayer 1110664d6f86SAndrea Mayer static struct net_device *end_dt_get_vrf_rcu(struct sk_buff *skb, 1111664d6f86SAndrea Mayer struct seg6_end_dt_info *info) 1112664d6f86SAndrea Mayer { 1113664d6f86SAndrea Mayer int vrf_ifindex = info->vrf_ifindex; 1114664d6f86SAndrea Mayer struct net *net = info->net; 1115664d6f86SAndrea Mayer 1116664d6f86SAndrea Mayer if (unlikely(vrf_ifindex < 0)) 1117664d6f86SAndrea Mayer goto error; 1118664d6f86SAndrea Mayer 1119664d6f86SAndrea Mayer if (unlikely(!net_eq(dev_net(skb->dev), net))) 1120664d6f86SAndrea Mayer goto error; 1121664d6f86SAndrea Mayer 1122664d6f86SAndrea Mayer return dev_get_by_index_rcu(net, vrf_ifindex); 1123664d6f86SAndrea Mayer 1124664d6f86SAndrea Mayer error: 1125664d6f86SAndrea Mayer return NULL; 1126664d6f86SAndrea Mayer } 1127664d6f86SAndrea Mayer 1128664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb, 11298b532109SAndrea Mayer struct seg6_local_lwt *slwt, u16 family) 1130664d6f86SAndrea Mayer { 1131664d6f86SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 1132664d6f86SAndrea Mayer struct net_device *vrf; 11338b532109SAndrea Mayer __be16 protocol; 11348b532109SAndrea Mayer int hdrlen; 1135664d6f86SAndrea Mayer 1136664d6f86SAndrea Mayer vrf = end_dt_get_vrf_rcu(skb, info); 1137664d6f86SAndrea Mayer if (unlikely(!vrf)) 1138664d6f86SAndrea Mayer goto drop; 1139664d6f86SAndrea Mayer 11408b532109SAndrea Mayer switch (family) { 11418b532109SAndrea Mayer case AF_INET: 11428b532109SAndrea Mayer protocol = htons(ETH_P_IP); 11438b532109SAndrea Mayer hdrlen = sizeof(struct iphdr); 11448b532109SAndrea Mayer break; 11458b532109SAndrea Mayer case AF_INET6: 11468b532109SAndrea Mayer protocol = htons(ETH_P_IPV6); 11478b532109SAndrea Mayer hdrlen = sizeof(struct ipv6hdr); 11488b532109SAndrea Mayer break; 11498b532109SAndrea Mayer case AF_UNSPEC: 11508b532109SAndrea Mayer fallthrough; 11518b532109SAndrea Mayer default: 11528b532109SAndrea Mayer goto drop; 11538b532109SAndrea Mayer } 11548b532109SAndrea Mayer 11558b532109SAndrea Mayer if (unlikely(info->family != AF_UNSPEC && info->family != family)) { 11568b532109SAndrea Mayer pr_warn_once("seg6local: SRv6 End.DT* family mismatch"); 11578b532109SAndrea Mayer goto drop; 11588b532109SAndrea Mayer } 11598b532109SAndrea Mayer 11608b532109SAndrea Mayer skb->protocol = protocol; 1161664d6f86SAndrea Mayer 1162664d6f86SAndrea Mayer skb_dst_drop(skb); 1163664d6f86SAndrea Mayer 11648b532109SAndrea Mayer skb_set_transport_header(skb, hdrlen); 11657a3f5b0dSRyoga Saito nf_reset_ct(skb); 1166664d6f86SAndrea Mayer 11678b532109SAndrea Mayer return end_dt_vrf_rcv(skb, family, vrf); 1168664d6f86SAndrea Mayer 1169664d6f86SAndrea Mayer drop: 1170664d6f86SAndrea Mayer kfree_skb(skb); 1171664d6f86SAndrea Mayer return ERR_PTR(-EINVAL); 1172664d6f86SAndrea Mayer } 1173664d6f86SAndrea Mayer 1174664d6f86SAndrea Mayer static int input_action_end_dt4(struct sk_buff *skb, 1175664d6f86SAndrea Mayer struct seg6_local_lwt *slwt) 1176664d6f86SAndrea Mayer { 1177664d6f86SAndrea Mayer struct iphdr *iph; 1178664d6f86SAndrea Mayer int err; 1179664d6f86SAndrea Mayer 1180664d6f86SAndrea Mayer if (!decap_and_validate(skb, IPPROTO_IPIP)) 1181664d6f86SAndrea Mayer goto drop; 1182664d6f86SAndrea Mayer 1183664d6f86SAndrea Mayer if (!pskb_may_pull(skb, sizeof(struct iphdr))) 1184664d6f86SAndrea Mayer goto drop; 1185664d6f86SAndrea Mayer 11868b532109SAndrea Mayer skb = end_dt_vrf_core(skb, slwt, AF_INET); 1187664d6f86SAndrea Mayer if (!skb) 1188664d6f86SAndrea Mayer /* packet has been processed and consumed by the VRF */ 1189664d6f86SAndrea Mayer return 0; 1190664d6f86SAndrea Mayer 1191664d6f86SAndrea Mayer if (IS_ERR(skb)) 1192664d6f86SAndrea Mayer return PTR_ERR(skb); 1193664d6f86SAndrea Mayer 1194664d6f86SAndrea Mayer iph = ip_hdr(skb); 1195664d6f86SAndrea Mayer 1196664d6f86SAndrea Mayer err = ip_route_input(skb, iph->daddr, iph->saddr, 0, skb->dev); 1197664d6f86SAndrea Mayer if (unlikely(err)) 1198664d6f86SAndrea Mayer goto drop; 1199664d6f86SAndrea Mayer 1200664d6f86SAndrea Mayer return dst_input(skb); 1201664d6f86SAndrea Mayer 1202664d6f86SAndrea Mayer drop: 1203664d6f86SAndrea Mayer kfree_skb(skb); 1204664d6f86SAndrea Mayer return -EINVAL; 1205664d6f86SAndrea Mayer } 1206664d6f86SAndrea Mayer 1207664d6f86SAndrea Mayer static int seg6_end_dt4_build(struct seg6_local_lwt *slwt, const void *cfg, 1208664d6f86SAndrea Mayer struct netlink_ext_ack *extack) 1209664d6f86SAndrea Mayer { 1210664d6f86SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET, extack); 1211664d6f86SAndrea Mayer } 121220a081b7SAndrea Mayer 121320a081b7SAndrea Mayer static enum 121420a081b7SAndrea Mayer seg6_end_dt_mode seg6_end_dt6_parse_mode(struct seg6_local_lwt *slwt) 121520a081b7SAndrea Mayer { 121620a081b7SAndrea Mayer unsigned long parsed_optattrs = slwt->parsed_optattrs; 121720a081b7SAndrea Mayer bool legacy, vrfmode; 121820a081b7SAndrea Mayer 1219300a0fd8SAndrea Mayer legacy = !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE)); 1220300a0fd8SAndrea Mayer vrfmode = !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE)); 122120a081b7SAndrea Mayer 122220a081b7SAndrea Mayer if (!(legacy ^ vrfmode)) 122320a081b7SAndrea Mayer /* both are absent or present: invalid DT6 mode */ 122420a081b7SAndrea Mayer return DT_INVALID_MODE; 122520a081b7SAndrea Mayer 122620a081b7SAndrea Mayer return legacy ? DT_LEGACY_MODE : DT_VRF_MODE; 122720a081b7SAndrea Mayer } 122820a081b7SAndrea Mayer 122920a081b7SAndrea Mayer static enum seg6_end_dt_mode seg6_end_dt6_get_mode(struct seg6_local_lwt *slwt) 123020a081b7SAndrea Mayer { 123120a081b7SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 123220a081b7SAndrea Mayer 123320a081b7SAndrea Mayer return info->mode; 123420a081b7SAndrea Mayer } 123520a081b7SAndrea Mayer 123620a081b7SAndrea Mayer static int seg6_end_dt6_build(struct seg6_local_lwt *slwt, const void *cfg, 123720a081b7SAndrea Mayer struct netlink_ext_ack *extack) 123820a081b7SAndrea Mayer { 123920a081b7SAndrea Mayer enum seg6_end_dt_mode mode = seg6_end_dt6_parse_mode(slwt); 124020a081b7SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 124120a081b7SAndrea Mayer 124220a081b7SAndrea Mayer switch (mode) { 124320a081b7SAndrea Mayer case DT_LEGACY_MODE: 124420a081b7SAndrea Mayer info->mode = DT_LEGACY_MODE; 124520a081b7SAndrea Mayer return 0; 124620a081b7SAndrea Mayer case DT_VRF_MODE: 124720a081b7SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET6, extack); 124820a081b7SAndrea Mayer default: 124920a081b7SAndrea Mayer NL_SET_ERR_MSG(extack, "table or vrftable must be specified"); 125020a081b7SAndrea Mayer return -EINVAL; 125120a081b7SAndrea Mayer } 125220a081b7SAndrea Mayer } 1253664d6f86SAndrea Mayer #endif 1254664d6f86SAndrea Mayer 1255891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb, 1256891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 1257891ef8ddSDavid Lebrun { 1258891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 1259891ef8ddSDavid Lebrun goto drop; 1260891ef8ddSDavid Lebrun 1261891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 1262891ef8ddSDavid Lebrun goto drop; 1263891ef8ddSDavid Lebrun 126420a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 126520a081b7SAndrea Mayer if (seg6_end_dt6_get_mode(slwt) == DT_LEGACY_MODE) 126620a081b7SAndrea Mayer goto legacy_mode; 126720a081b7SAndrea Mayer 126820a081b7SAndrea Mayer /* DT6_VRF_MODE */ 12698b532109SAndrea Mayer skb = end_dt_vrf_core(skb, slwt, AF_INET6); 127020a081b7SAndrea Mayer if (!skb) 127120a081b7SAndrea Mayer /* packet has been processed and consumed by the VRF */ 127220a081b7SAndrea Mayer return 0; 127320a081b7SAndrea Mayer 127420a081b7SAndrea Mayer if (IS_ERR(skb)) 127520a081b7SAndrea Mayer return PTR_ERR(skb); 127620a081b7SAndrea Mayer 127720a081b7SAndrea Mayer /* note: this time we do not need to specify the table because the VRF 127820a081b7SAndrea Mayer * takes care of selecting the correct table. 127920a081b7SAndrea Mayer */ 128020a081b7SAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, 0, true); 128120a081b7SAndrea Mayer 128220a081b7SAndrea Mayer return dst_input(skb); 128320a081b7SAndrea Mayer 128420a081b7SAndrea Mayer legacy_mode: 128520a081b7SAndrea Mayer #endif 1286c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 1287c71644d0SAndrea Mayer 1288fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, slwt->table, true); 1289891ef8ddSDavid Lebrun 1290891ef8ddSDavid Lebrun return dst_input(skb); 1291891ef8ddSDavid Lebrun 1292891ef8ddSDavid Lebrun drop: 1293891ef8ddSDavid Lebrun kfree_skb(skb); 1294891ef8ddSDavid Lebrun return -EINVAL; 1295891ef8ddSDavid Lebrun } 1296891ef8ddSDavid Lebrun 12978b532109SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 12988b532109SAndrea Mayer static int seg6_end_dt46_build(struct seg6_local_lwt *slwt, const void *cfg, 12998b532109SAndrea Mayer struct netlink_ext_ack *extack) 13008b532109SAndrea Mayer { 13018b532109SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_UNSPEC, extack); 13028b532109SAndrea Mayer } 13038b532109SAndrea Mayer 13048b532109SAndrea Mayer static int input_action_end_dt46(struct sk_buff *skb, 13058b532109SAndrea Mayer struct seg6_local_lwt *slwt) 13068b532109SAndrea Mayer { 13078b532109SAndrea Mayer unsigned int off = 0; 13088b532109SAndrea Mayer int nexthdr; 13098b532109SAndrea Mayer 13108b532109SAndrea Mayer nexthdr = ipv6_find_hdr(skb, &off, -1, NULL, NULL); 13118b532109SAndrea Mayer if (unlikely(nexthdr < 0)) 13128b532109SAndrea Mayer goto drop; 13138b532109SAndrea Mayer 13148b532109SAndrea Mayer switch (nexthdr) { 13158b532109SAndrea Mayer case IPPROTO_IPIP: 13168b532109SAndrea Mayer return input_action_end_dt4(skb, slwt); 13178b532109SAndrea Mayer case IPPROTO_IPV6: 13188b532109SAndrea Mayer return input_action_end_dt6(skb, slwt); 13198b532109SAndrea Mayer } 13208b532109SAndrea Mayer 13218b532109SAndrea Mayer drop: 13228b532109SAndrea Mayer kfree_skb(skb); 13238b532109SAndrea Mayer return -EINVAL; 13248b532109SAndrea Mayer } 13258b532109SAndrea Mayer #endif 13268b532109SAndrea Mayer 1327140f04c3SDavid Lebrun /* push an SRH on top of the current one */ 1328140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1329140f04c3SDavid Lebrun { 1330140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 1331140f04c3SDavid Lebrun int err = -EINVAL; 1332140f04c3SDavid Lebrun 1333140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 1334140f04c3SDavid Lebrun if (!srh) 1335140f04c3SDavid Lebrun goto drop; 1336140f04c3SDavid Lebrun 1337140f04c3SDavid Lebrun err = seg6_do_srh_inline(skb, slwt->srh); 1338140f04c3SDavid Lebrun if (err) 1339140f04c3SDavid Lebrun goto drop; 1340140f04c3SDavid Lebrun 1341140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 1342140f04c3SDavid Lebrun 13431c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 1344140f04c3SDavid Lebrun 1345140f04c3SDavid Lebrun return dst_input(skb); 1346140f04c3SDavid Lebrun 1347140f04c3SDavid Lebrun drop: 1348140f04c3SDavid Lebrun kfree_skb(skb); 1349140f04c3SDavid Lebrun return err; 1350140f04c3SDavid Lebrun } 1351140f04c3SDavid Lebrun 1352140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */ 1353140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb, 1354140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 1355140f04c3SDavid Lebrun { 1356140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 1357140f04c3SDavid Lebrun int err = -EINVAL; 1358140f04c3SDavid Lebrun 1359140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 1360140f04c3SDavid Lebrun if (!srh) 1361140f04c3SDavid Lebrun goto drop; 1362140f04c3SDavid Lebrun 1363d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 1364140f04c3SDavid Lebrun 1365140f04c3SDavid Lebrun skb_reset_inner_headers(skb); 1366140f04c3SDavid Lebrun skb->encapsulation = 1; 1367140f04c3SDavid Lebrun 136832d99d0bSDavid Lebrun err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6); 1369140f04c3SDavid Lebrun if (err) 1370140f04c3SDavid Lebrun goto drop; 1371140f04c3SDavid Lebrun 1372140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 1373140f04c3SDavid Lebrun 13741c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 1375140f04c3SDavid Lebrun 1376140f04c3SDavid Lebrun return dst_input(skb); 1377140f04c3SDavid Lebrun 1378140f04c3SDavid Lebrun drop: 1379140f04c3SDavid Lebrun kfree_skb(skb); 1380140f04c3SDavid Lebrun return err; 1381140f04c3SDavid Lebrun } 1382140f04c3SDavid Lebrun 1383fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states); 1384fe94cc29SMathieu Xhonneux 1385486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb) 1386486cdf21SMathieu Xhonneux { 1387486cdf21SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 1388486cdf21SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 1389486cdf21SMathieu Xhonneux struct ipv6_sr_hdr *srh = srh_state->srh; 1390486cdf21SMathieu Xhonneux 1391486cdf21SMathieu Xhonneux if (unlikely(srh == NULL)) 1392486cdf21SMathieu Xhonneux return false; 1393486cdf21SMathieu Xhonneux 1394486cdf21SMathieu Xhonneux if (unlikely(!srh_state->valid)) { 1395486cdf21SMathieu Xhonneux if ((srh_state->hdrlen & 7) != 0) 1396486cdf21SMathieu Xhonneux return false; 1397486cdf21SMathieu Xhonneux 1398486cdf21SMathieu Xhonneux srh->hdrlen = (u8)(srh_state->hdrlen >> 3); 1399bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true)) 1400486cdf21SMathieu Xhonneux return false; 1401486cdf21SMathieu Xhonneux 1402486cdf21SMathieu Xhonneux srh_state->valid = true; 1403486cdf21SMathieu Xhonneux } 1404486cdf21SMathieu Xhonneux 1405486cdf21SMathieu Xhonneux return true; 1406486cdf21SMathieu Xhonneux } 1407486cdf21SMathieu Xhonneux 1408004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb, 1409004d4b27SMathieu Xhonneux struct seg6_local_lwt *slwt) 1410004d4b27SMathieu Xhonneux { 1411004d4b27SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 1412004d4b27SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 1413004d4b27SMathieu Xhonneux struct ipv6_sr_hdr *srh; 1414004d4b27SMathieu Xhonneux int ret; 1415004d4b27SMathieu Xhonneux 1416004d4b27SMathieu Xhonneux srh = get_and_validate_srh(skb); 1417486cdf21SMathieu Xhonneux if (!srh) { 1418486cdf21SMathieu Xhonneux kfree_skb(skb); 1419486cdf21SMathieu Xhonneux return -EINVAL; 1420486cdf21SMathieu Xhonneux } 1421004d4b27SMathieu Xhonneux advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 1422004d4b27SMathieu Xhonneux 1423004d4b27SMathieu Xhonneux /* preempt_disable is needed to protect the per-CPU buffer srh_state, 1424004d4b27SMathieu Xhonneux * which is also accessed by the bpf_lwt_seg6_* helpers 1425004d4b27SMathieu Xhonneux */ 1426004d4b27SMathieu Xhonneux preempt_disable(); 1427486cdf21SMathieu Xhonneux srh_state->srh = srh; 1428004d4b27SMathieu Xhonneux srh_state->hdrlen = srh->hdrlen << 3; 1429486cdf21SMathieu Xhonneux srh_state->valid = true; 1430004d4b27SMathieu Xhonneux 1431004d4b27SMathieu Xhonneux rcu_read_lock(); 1432004d4b27SMathieu Xhonneux bpf_compute_data_pointers(skb); 1433004d4b27SMathieu Xhonneux ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb); 1434004d4b27SMathieu Xhonneux rcu_read_unlock(); 1435004d4b27SMathieu Xhonneux 1436004d4b27SMathieu Xhonneux switch (ret) { 1437004d4b27SMathieu Xhonneux case BPF_OK: 1438004d4b27SMathieu Xhonneux case BPF_REDIRECT: 1439004d4b27SMathieu Xhonneux break; 1440004d4b27SMathieu Xhonneux case BPF_DROP: 1441004d4b27SMathieu Xhonneux goto drop; 1442004d4b27SMathieu Xhonneux default: 1443004d4b27SMathieu Xhonneux pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret); 1444004d4b27SMathieu Xhonneux goto drop; 1445004d4b27SMathieu Xhonneux } 1446004d4b27SMathieu Xhonneux 1447486cdf21SMathieu Xhonneux if (srh_state->srh && !seg6_bpf_has_valid_srh(skb)) 1448004d4b27SMathieu Xhonneux goto drop; 1449004d4b27SMathieu Xhonneux 1450486cdf21SMathieu Xhonneux preempt_enable(); 1451004d4b27SMathieu Xhonneux if (ret != BPF_REDIRECT) 1452004d4b27SMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 1453004d4b27SMathieu Xhonneux 1454004d4b27SMathieu Xhonneux return dst_input(skb); 1455004d4b27SMathieu Xhonneux 1456004d4b27SMathieu Xhonneux drop: 1457486cdf21SMathieu Xhonneux preempt_enable(); 1458004d4b27SMathieu Xhonneux kfree_skb(skb); 1459004d4b27SMathieu Xhonneux return -EINVAL; 1460004d4b27SMathieu Xhonneux } 1461004d4b27SMathieu Xhonneux 1462d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = { 1463d1df6fd8SDavid Lebrun { 1464d1df6fd8SDavid Lebrun .action = SEG6_LOCAL_ACTION_END, 1465d1df6fd8SDavid Lebrun .attrs = 0, 1466848f3c0dSAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS | 14677458575aSAndrea Mayer SEG6_F_LOCAL_FLAVORS, 1468140f04c3SDavid Lebrun .input = input_action_end, 1469d1df6fd8SDavid Lebrun }, 1470140f04c3SDavid Lebrun { 1471140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_X, 1472300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), 14737458575aSAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS | 14747458575aSAndrea Mayer SEG6_F_LOCAL_FLAVORS, 1475140f04c3SDavid Lebrun .input = input_action_end_x, 1476140f04c3SDavid Lebrun }, 1477140f04c3SDavid Lebrun { 1478891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_T, 1479300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE), 148094604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1481891ef8ddSDavid Lebrun .input = input_action_end_t, 1482891ef8ddSDavid Lebrun }, 1483891ef8ddSDavid Lebrun { 1484891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX2, 1485300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_OIF), 148694604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1487891ef8ddSDavid Lebrun .input = input_action_end_dx2, 1488891ef8ddSDavid Lebrun }, 1489891ef8ddSDavid Lebrun { 1490140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX6, 1491300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), 149294604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1493140f04c3SDavid Lebrun .input = input_action_end_dx6, 1494140f04c3SDavid Lebrun }, 1495140f04c3SDavid Lebrun { 1496891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX4, 1497300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH4), 149894604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1499891ef8ddSDavid Lebrun .input = input_action_end_dx4, 1500891ef8ddSDavid Lebrun }, 1501891ef8ddSDavid Lebrun { 1502664d6f86SAndrea Mayer .action = SEG6_LOCAL_ACTION_END_DT4, 1503300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 150494604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1505664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 1506664d6f86SAndrea Mayer .input = input_action_end_dt4, 1507664d6f86SAndrea Mayer .slwt_ops = { 1508664d6f86SAndrea Mayer .build_state = seg6_end_dt4_build, 1509664d6f86SAndrea Mayer }, 1510664d6f86SAndrea Mayer #endif 1511664d6f86SAndrea Mayer }, 1512664d6f86SAndrea Mayer { 1513891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DT6, 151420a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 151520a081b7SAndrea Mayer .attrs = 0, 151694604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS | 151794604548SAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_TABLE) | 1518300a0fd8SAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 151920a081b7SAndrea Mayer .slwt_ops = { 152020a081b7SAndrea Mayer .build_state = seg6_end_dt6_build, 152120a081b7SAndrea Mayer }, 152220a081b7SAndrea Mayer #else 1523300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE), 152494604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 152520a081b7SAndrea Mayer #endif 1526891ef8ddSDavid Lebrun .input = input_action_end_dt6, 1527891ef8ddSDavid Lebrun }, 1528891ef8ddSDavid Lebrun { 15298b532109SAndrea Mayer .action = SEG6_LOCAL_ACTION_END_DT46, 15308b532109SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 15318b532109SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 15328b532109SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 15338b532109SAndrea Mayer .input = input_action_end_dt46, 15348b532109SAndrea Mayer .slwt_ops = { 15358b532109SAndrea Mayer .build_state = seg6_end_dt46_build, 15368b532109SAndrea Mayer }, 15378b532109SAndrea Mayer #endif 15388b532109SAndrea Mayer }, 15398b532109SAndrea Mayer { 1540140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6, 1541300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH), 154294604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1543140f04c3SDavid Lebrun .input = input_action_end_b6, 1544140f04c3SDavid Lebrun }, 1545140f04c3SDavid Lebrun { 1546140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6_ENCAP, 1547300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH), 154894604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1549140f04c3SDavid Lebrun .input = input_action_end_b6_encap, 1550140f04c3SDavid Lebrun .static_headroom = sizeof(struct ipv6hdr), 1551004d4b27SMathieu Xhonneux }, 1552004d4b27SMathieu Xhonneux { 1553004d4b27SMathieu Xhonneux .action = SEG6_LOCAL_ACTION_END_BPF, 1554300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_BPF), 155594604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1556004d4b27SMathieu Xhonneux .input = input_action_end_bpf, 1557004d4b27SMathieu Xhonneux }, 1558004d4b27SMathieu Xhonneux 1559d1df6fd8SDavid Lebrun }; 1560d1df6fd8SDavid Lebrun 1561d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action) 1562d1df6fd8SDavid Lebrun { 1563d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1564d1df6fd8SDavid Lebrun int i, count; 1565d1df6fd8SDavid Lebrun 1566709af180SColin Ian King count = ARRAY_SIZE(seg6_action_table); 1567d1df6fd8SDavid Lebrun for (i = 0; i < count; i++) { 1568d1df6fd8SDavid Lebrun desc = &seg6_action_table[i]; 1569d1df6fd8SDavid Lebrun if (desc->action == action) 1570d1df6fd8SDavid Lebrun return desc; 1571d1df6fd8SDavid Lebrun } 1572d1df6fd8SDavid Lebrun 1573d1df6fd8SDavid Lebrun return NULL; 1574d1df6fd8SDavid Lebrun } 1575d1df6fd8SDavid Lebrun 157694604548SAndrea Mayer static bool seg6_lwtunnel_counters_enabled(struct seg6_local_lwt *slwt) 157794604548SAndrea Mayer { 157894604548SAndrea Mayer return slwt->parsed_optattrs & SEG6_F_LOCAL_COUNTERS; 157994604548SAndrea Mayer } 158094604548SAndrea Mayer 158194604548SAndrea Mayer static void seg6_local_update_counters(struct seg6_local_lwt *slwt, 158294604548SAndrea Mayer unsigned int len, int err) 158394604548SAndrea Mayer { 158494604548SAndrea Mayer struct pcpu_seg6_local_counters *pcounters; 158594604548SAndrea Mayer 158694604548SAndrea Mayer pcounters = this_cpu_ptr(slwt->pcpu_counters); 158794604548SAndrea Mayer u64_stats_update_begin(&pcounters->syncp); 158894604548SAndrea Mayer 158994604548SAndrea Mayer if (likely(!err)) { 159094604548SAndrea Mayer u64_stats_inc(&pcounters->packets); 159194604548SAndrea Mayer u64_stats_add(&pcounters->bytes, len); 159294604548SAndrea Mayer } else { 159394604548SAndrea Mayer u64_stats_inc(&pcounters->errors); 159494604548SAndrea Mayer } 159594604548SAndrea Mayer 159694604548SAndrea Mayer u64_stats_update_end(&pcounters->syncp); 159794604548SAndrea Mayer } 159894604548SAndrea Mayer 15997a3f5b0dSRyoga Saito static int seg6_local_input_core(struct net *net, struct sock *sk, 16007a3f5b0dSRyoga Saito struct sk_buff *skb) 1601d1df6fd8SDavid Lebrun { 1602d1df6fd8SDavid Lebrun struct dst_entry *orig_dst = skb_dst(skb); 1603d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1604d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 160594604548SAndrea Mayer unsigned int len = skb->len; 160694604548SAndrea Mayer int rc; 1607d1df6fd8SDavid Lebrun 1608d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 1609d1df6fd8SDavid Lebrun desc = slwt->desc; 1610d1df6fd8SDavid Lebrun 161194604548SAndrea Mayer rc = desc->input(skb, slwt); 161294604548SAndrea Mayer 161394604548SAndrea Mayer if (!seg6_lwtunnel_counters_enabled(slwt)) 161494604548SAndrea Mayer return rc; 161594604548SAndrea Mayer 161694604548SAndrea Mayer seg6_local_update_counters(slwt, len, rc); 161794604548SAndrea Mayer 161894604548SAndrea Mayer return rc; 1619d1df6fd8SDavid Lebrun } 1620d1df6fd8SDavid Lebrun 16217a3f5b0dSRyoga Saito static int seg6_local_input(struct sk_buff *skb) 16227a3f5b0dSRyoga Saito { 16237a3f5b0dSRyoga Saito if (skb->protocol != htons(ETH_P_IPV6)) { 16247a3f5b0dSRyoga Saito kfree_skb(skb); 16257a3f5b0dSRyoga Saito return -EINVAL; 16267a3f5b0dSRyoga Saito } 16277a3f5b0dSRyoga Saito 16287a3f5b0dSRyoga Saito if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 16297a3f5b0dSRyoga Saito return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN, 16307a3f5b0dSRyoga Saito dev_net(skb->dev), NULL, skb, skb->dev, NULL, 16317a3f5b0dSRyoga Saito seg6_local_input_core); 16327a3f5b0dSRyoga Saito 16337a3f5b0dSRyoga Saito return seg6_local_input_core(dev_net(skb->dev), NULL, skb); 16347a3f5b0dSRyoga Saito } 16357a3f5b0dSRyoga Saito 1636d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { 1637d1df6fd8SDavid Lebrun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, 1638d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, 1639d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, 1640664d6f86SAndrea Mayer [SEG6_LOCAL_VRFTABLE] = { .type = NLA_U32 }, 1641d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, 1642d1df6fd8SDavid Lebrun .len = sizeof(struct in_addr) }, 1643d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, 1644d1df6fd8SDavid Lebrun .len = sizeof(struct in6_addr) }, 1645d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, 1646d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 1647004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .type = NLA_NESTED }, 164894604548SAndrea Mayer [SEG6_LOCAL_COUNTERS] = { .type = NLA_NESTED }, 1649848f3c0dSAndrea Mayer [SEG6_LOCAL_FLAVORS] = { .type = NLA_NESTED }, 1650d1df6fd8SDavid Lebrun }; 1651d1df6fd8SDavid Lebrun 1652e2a8ecc4SAndrea Mayer static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1653e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 16542d9cc60aSDavid Lebrun { 16552d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 16562d9cc60aSDavid Lebrun int len; 16572d9cc60aSDavid Lebrun 16582d9cc60aSDavid Lebrun srh = nla_data(attrs[SEG6_LOCAL_SRH]); 16592d9cc60aSDavid Lebrun len = nla_len(attrs[SEG6_LOCAL_SRH]); 16602d9cc60aSDavid Lebrun 16612d9cc60aSDavid Lebrun /* SRH must contain at least one segment */ 16622d9cc60aSDavid Lebrun if (len < sizeof(*srh) + sizeof(struct in6_addr)) 16632d9cc60aSDavid Lebrun return -EINVAL; 16642d9cc60aSDavid Lebrun 1665bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, false)) 16662d9cc60aSDavid Lebrun return -EINVAL; 16672d9cc60aSDavid Lebrun 16687fa41efaSYueHaibing slwt->srh = kmemdup(srh, len, GFP_KERNEL); 16692d9cc60aSDavid Lebrun if (!slwt->srh) 16702d9cc60aSDavid Lebrun return -ENOMEM; 16712d9cc60aSDavid Lebrun 16722d9cc60aSDavid Lebrun slwt->headroom += len; 16732d9cc60aSDavid Lebrun 16742d9cc60aSDavid Lebrun return 0; 16752d9cc60aSDavid Lebrun } 16762d9cc60aSDavid Lebrun 16772d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt) 16782d9cc60aSDavid Lebrun { 16792d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 16802d9cc60aSDavid Lebrun struct nlattr *nla; 16812d9cc60aSDavid Lebrun int len; 16822d9cc60aSDavid Lebrun 16832d9cc60aSDavid Lebrun srh = slwt->srh; 16842d9cc60aSDavid Lebrun len = (srh->hdrlen + 1) << 3; 16852d9cc60aSDavid Lebrun 16862d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_SRH, len); 16872d9cc60aSDavid Lebrun if (!nla) 16882d9cc60aSDavid Lebrun return -EMSGSIZE; 16892d9cc60aSDavid Lebrun 16902d9cc60aSDavid Lebrun memcpy(nla_data(nla), srh, len); 16912d9cc60aSDavid Lebrun 16922d9cc60aSDavid Lebrun return 0; 16932d9cc60aSDavid Lebrun } 16942d9cc60aSDavid Lebrun 16952d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 16962d9cc60aSDavid Lebrun { 16972d9cc60aSDavid Lebrun int len = (a->srh->hdrlen + 1) << 3; 16982d9cc60aSDavid Lebrun 16992d9cc60aSDavid Lebrun if (len != ((b->srh->hdrlen + 1) << 3)) 17002d9cc60aSDavid Lebrun return 1; 17012d9cc60aSDavid Lebrun 17022d9cc60aSDavid Lebrun return memcmp(a->srh, b->srh, len); 17032d9cc60aSDavid Lebrun } 17042d9cc60aSDavid Lebrun 1705964adce5SAndrea Mayer static void destroy_attr_srh(struct seg6_local_lwt *slwt) 1706964adce5SAndrea Mayer { 1707964adce5SAndrea Mayer kfree(slwt->srh); 1708964adce5SAndrea Mayer } 1709964adce5SAndrea Mayer 1710e2a8ecc4SAndrea Mayer static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1711e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 17122d9cc60aSDavid Lebrun { 17132d9cc60aSDavid Lebrun slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]); 17142d9cc60aSDavid Lebrun 17152d9cc60aSDavid Lebrun return 0; 17162d9cc60aSDavid Lebrun } 17172d9cc60aSDavid Lebrun 17182d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt) 17192d9cc60aSDavid Lebrun { 17202d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table)) 17212d9cc60aSDavid Lebrun return -EMSGSIZE; 17222d9cc60aSDavid Lebrun 17232d9cc60aSDavid Lebrun return 0; 17242d9cc60aSDavid Lebrun } 17252d9cc60aSDavid Lebrun 17262d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 17272d9cc60aSDavid Lebrun { 17282d9cc60aSDavid Lebrun if (a->table != b->table) 17292d9cc60aSDavid Lebrun return 1; 17302d9cc60aSDavid Lebrun 17312d9cc60aSDavid Lebrun return 0; 17322d9cc60aSDavid Lebrun } 17332d9cc60aSDavid Lebrun 1734664d6f86SAndrea Mayer static struct 1735664d6f86SAndrea Mayer seg6_end_dt_info *seg6_possible_end_dt_info(struct seg6_local_lwt *slwt) 1736664d6f86SAndrea Mayer { 1737664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 1738664d6f86SAndrea Mayer return &slwt->dt_info; 1739664d6f86SAndrea Mayer #else 1740664d6f86SAndrea Mayer return ERR_PTR(-EOPNOTSUPP); 1741664d6f86SAndrea Mayer #endif 1742664d6f86SAndrea Mayer } 1743664d6f86SAndrea Mayer 1744664d6f86SAndrea Mayer static int parse_nla_vrftable(struct nlattr **attrs, 1745e2a8ecc4SAndrea Mayer struct seg6_local_lwt *slwt, 1746e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 1747664d6f86SAndrea Mayer { 1748664d6f86SAndrea Mayer struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt); 1749664d6f86SAndrea Mayer 1750664d6f86SAndrea Mayer if (IS_ERR(info)) 1751664d6f86SAndrea Mayer return PTR_ERR(info); 1752664d6f86SAndrea Mayer 1753664d6f86SAndrea Mayer info->vrf_table = nla_get_u32(attrs[SEG6_LOCAL_VRFTABLE]); 1754664d6f86SAndrea Mayer 1755664d6f86SAndrea Mayer return 0; 1756664d6f86SAndrea Mayer } 1757664d6f86SAndrea Mayer 1758664d6f86SAndrea Mayer static int put_nla_vrftable(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1759664d6f86SAndrea Mayer { 1760664d6f86SAndrea Mayer struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt); 1761664d6f86SAndrea Mayer 1762664d6f86SAndrea Mayer if (IS_ERR(info)) 1763664d6f86SAndrea Mayer return PTR_ERR(info); 1764664d6f86SAndrea Mayer 1765664d6f86SAndrea Mayer if (nla_put_u32(skb, SEG6_LOCAL_VRFTABLE, info->vrf_table)) 1766664d6f86SAndrea Mayer return -EMSGSIZE; 1767664d6f86SAndrea Mayer 1768664d6f86SAndrea Mayer return 0; 1769664d6f86SAndrea Mayer } 1770664d6f86SAndrea Mayer 1771664d6f86SAndrea Mayer static int cmp_nla_vrftable(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1772664d6f86SAndrea Mayer { 1773664d6f86SAndrea Mayer struct seg6_end_dt_info *info_a = seg6_possible_end_dt_info(a); 1774664d6f86SAndrea Mayer struct seg6_end_dt_info *info_b = seg6_possible_end_dt_info(b); 1775664d6f86SAndrea Mayer 1776664d6f86SAndrea Mayer if (info_a->vrf_table != info_b->vrf_table) 1777664d6f86SAndrea Mayer return 1; 1778664d6f86SAndrea Mayer 1779664d6f86SAndrea Mayer return 0; 1780664d6f86SAndrea Mayer } 1781664d6f86SAndrea Mayer 1782e2a8ecc4SAndrea Mayer static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1783e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 17842d9cc60aSDavid Lebrun { 17852d9cc60aSDavid Lebrun memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]), 17862d9cc60aSDavid Lebrun sizeof(struct in_addr)); 17872d9cc60aSDavid Lebrun 17882d9cc60aSDavid Lebrun return 0; 17892d9cc60aSDavid Lebrun } 17902d9cc60aSDavid Lebrun 17912d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt) 17922d9cc60aSDavid Lebrun { 17932d9cc60aSDavid Lebrun struct nlattr *nla; 17942d9cc60aSDavid Lebrun 17952d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr)); 17962d9cc60aSDavid Lebrun if (!nla) 17972d9cc60aSDavid Lebrun return -EMSGSIZE; 17982d9cc60aSDavid Lebrun 17992d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr)); 18002d9cc60aSDavid Lebrun 18012d9cc60aSDavid Lebrun return 0; 18022d9cc60aSDavid Lebrun } 18032d9cc60aSDavid Lebrun 18042d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 18052d9cc60aSDavid Lebrun { 18062d9cc60aSDavid Lebrun return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr)); 18072d9cc60aSDavid Lebrun } 18082d9cc60aSDavid Lebrun 1809e2a8ecc4SAndrea Mayer static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1810e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 18112d9cc60aSDavid Lebrun { 18122d9cc60aSDavid Lebrun memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]), 18132d9cc60aSDavid Lebrun sizeof(struct in6_addr)); 18142d9cc60aSDavid Lebrun 18152d9cc60aSDavid Lebrun return 0; 18162d9cc60aSDavid Lebrun } 18172d9cc60aSDavid Lebrun 18182d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 18192d9cc60aSDavid Lebrun { 18202d9cc60aSDavid Lebrun struct nlattr *nla; 18212d9cc60aSDavid Lebrun 18222d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr)); 18232d9cc60aSDavid Lebrun if (!nla) 18242d9cc60aSDavid Lebrun return -EMSGSIZE; 18252d9cc60aSDavid Lebrun 18262d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr)); 18272d9cc60aSDavid Lebrun 18282d9cc60aSDavid Lebrun return 0; 18292d9cc60aSDavid Lebrun } 18302d9cc60aSDavid Lebrun 18312d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 18322d9cc60aSDavid Lebrun { 18332d9cc60aSDavid Lebrun return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr)); 18342d9cc60aSDavid Lebrun } 18352d9cc60aSDavid Lebrun 1836e2a8ecc4SAndrea Mayer static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1837e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 18382d9cc60aSDavid Lebrun { 18392d9cc60aSDavid Lebrun slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]); 18402d9cc60aSDavid Lebrun 18412d9cc60aSDavid Lebrun return 0; 18422d9cc60aSDavid Lebrun } 18432d9cc60aSDavid Lebrun 18442d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 18452d9cc60aSDavid Lebrun { 18462d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif)) 18472d9cc60aSDavid Lebrun return -EMSGSIZE; 18482d9cc60aSDavid Lebrun 18492d9cc60aSDavid Lebrun return 0; 18502d9cc60aSDavid Lebrun } 18512d9cc60aSDavid Lebrun 18522d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 18532d9cc60aSDavid Lebrun { 18542d9cc60aSDavid Lebrun if (a->iif != b->iif) 18552d9cc60aSDavid Lebrun return 1; 18562d9cc60aSDavid Lebrun 18572d9cc60aSDavid Lebrun return 0; 18582d9cc60aSDavid Lebrun } 18592d9cc60aSDavid Lebrun 1860e2a8ecc4SAndrea Mayer static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1861e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 18622d9cc60aSDavid Lebrun { 18632d9cc60aSDavid Lebrun slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]); 18642d9cc60aSDavid Lebrun 18652d9cc60aSDavid Lebrun return 0; 18662d9cc60aSDavid Lebrun } 18672d9cc60aSDavid Lebrun 18682d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 18692d9cc60aSDavid Lebrun { 18702d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif)) 18712d9cc60aSDavid Lebrun return -EMSGSIZE; 18722d9cc60aSDavid Lebrun 18732d9cc60aSDavid Lebrun return 0; 18742d9cc60aSDavid Lebrun } 18752d9cc60aSDavid Lebrun 18762d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 18772d9cc60aSDavid Lebrun { 18782d9cc60aSDavid Lebrun if (a->oif != b->oif) 18792d9cc60aSDavid Lebrun return 1; 18802d9cc60aSDavid Lebrun 18812d9cc60aSDavid Lebrun return 0; 18822d9cc60aSDavid Lebrun } 18832d9cc60aSDavid Lebrun 1884004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256 1885004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = { 1886004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG] = { .type = NLA_U32, }, 1887004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING, 1888004d4b27SMathieu Xhonneux .len = MAX_PROG_NAME }, 1889004d4b27SMathieu Xhonneux }; 1890004d4b27SMathieu Xhonneux 1891e2a8ecc4SAndrea Mayer static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1892e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 1893004d4b27SMathieu Xhonneux { 1894004d4b27SMathieu Xhonneux struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1]; 1895004d4b27SMathieu Xhonneux struct bpf_prog *p; 1896004d4b27SMathieu Xhonneux int ret; 1897004d4b27SMathieu Xhonneux u32 fd; 1898004d4b27SMathieu Xhonneux 18998cb08174SJohannes Berg ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX, 19008cb08174SJohannes Berg attrs[SEG6_LOCAL_BPF], 19018cb08174SJohannes Berg bpf_prog_policy, NULL); 1902004d4b27SMathieu Xhonneux if (ret < 0) 1903004d4b27SMathieu Xhonneux return ret; 1904004d4b27SMathieu Xhonneux 1905004d4b27SMathieu Xhonneux if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME]) 1906004d4b27SMathieu Xhonneux return -EINVAL; 1907004d4b27SMathieu Xhonneux 1908004d4b27SMathieu Xhonneux slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL); 1909004d4b27SMathieu Xhonneux if (!slwt->bpf.name) 1910004d4b27SMathieu Xhonneux return -ENOMEM; 1911004d4b27SMathieu Xhonneux 1912004d4b27SMathieu Xhonneux fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]); 1913004d4b27SMathieu Xhonneux p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL); 1914004d4b27SMathieu Xhonneux if (IS_ERR(p)) { 1915004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 1916004d4b27SMathieu Xhonneux return PTR_ERR(p); 1917004d4b27SMathieu Xhonneux } 1918004d4b27SMathieu Xhonneux 1919004d4b27SMathieu Xhonneux slwt->bpf.prog = p; 1920004d4b27SMathieu Xhonneux return 0; 1921004d4b27SMathieu Xhonneux } 1922004d4b27SMathieu Xhonneux 1923004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1924004d4b27SMathieu Xhonneux { 1925004d4b27SMathieu Xhonneux struct nlattr *nest; 1926004d4b27SMathieu Xhonneux 1927004d4b27SMathieu Xhonneux if (!slwt->bpf.prog) 1928004d4b27SMathieu Xhonneux return 0; 1929004d4b27SMathieu Xhonneux 1930ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF); 1931004d4b27SMathieu Xhonneux if (!nest) 1932004d4b27SMathieu Xhonneux return -EMSGSIZE; 1933004d4b27SMathieu Xhonneux 1934004d4b27SMathieu Xhonneux if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id)) 1935004d4b27SMathieu Xhonneux return -EMSGSIZE; 1936004d4b27SMathieu Xhonneux 1937004d4b27SMathieu Xhonneux if (slwt->bpf.name && 1938004d4b27SMathieu Xhonneux nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name)) 1939004d4b27SMathieu Xhonneux return -EMSGSIZE; 1940004d4b27SMathieu Xhonneux 1941004d4b27SMathieu Xhonneux return nla_nest_end(skb, nest); 1942004d4b27SMathieu Xhonneux } 1943004d4b27SMathieu Xhonneux 1944004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1945004d4b27SMathieu Xhonneux { 1946004d4b27SMathieu Xhonneux if (!a->bpf.name && !b->bpf.name) 1947004d4b27SMathieu Xhonneux return 0; 1948004d4b27SMathieu Xhonneux 1949004d4b27SMathieu Xhonneux if (!a->bpf.name || !b->bpf.name) 1950004d4b27SMathieu Xhonneux return 1; 1951004d4b27SMathieu Xhonneux 1952004d4b27SMathieu Xhonneux return strcmp(a->bpf.name, b->bpf.name); 1953004d4b27SMathieu Xhonneux } 1954004d4b27SMathieu Xhonneux 1955964adce5SAndrea Mayer static void destroy_attr_bpf(struct seg6_local_lwt *slwt) 1956964adce5SAndrea Mayer { 1957964adce5SAndrea Mayer kfree(slwt->bpf.name); 1958964adce5SAndrea Mayer if (slwt->bpf.prog) 1959964adce5SAndrea Mayer bpf_prog_put(slwt->bpf.prog); 1960964adce5SAndrea Mayer } 1961964adce5SAndrea Mayer 196294604548SAndrea Mayer static const struct 196394604548SAndrea Mayer nla_policy seg6_local_counters_policy[SEG6_LOCAL_CNT_MAX + 1] = { 196494604548SAndrea Mayer [SEG6_LOCAL_CNT_PACKETS] = { .type = NLA_U64 }, 196594604548SAndrea Mayer [SEG6_LOCAL_CNT_BYTES] = { .type = NLA_U64 }, 196694604548SAndrea Mayer [SEG6_LOCAL_CNT_ERRORS] = { .type = NLA_U64 }, 196794604548SAndrea Mayer }; 196894604548SAndrea Mayer 196994604548SAndrea Mayer static int parse_nla_counters(struct nlattr **attrs, 1970e2a8ecc4SAndrea Mayer struct seg6_local_lwt *slwt, 1971e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 197294604548SAndrea Mayer { 197394604548SAndrea Mayer struct pcpu_seg6_local_counters __percpu *pcounters; 197494604548SAndrea Mayer struct nlattr *tb[SEG6_LOCAL_CNT_MAX + 1]; 197594604548SAndrea Mayer int ret; 197694604548SAndrea Mayer 197794604548SAndrea Mayer ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_CNT_MAX, 197894604548SAndrea Mayer attrs[SEG6_LOCAL_COUNTERS], 197994604548SAndrea Mayer seg6_local_counters_policy, NULL); 198094604548SAndrea Mayer if (ret < 0) 198194604548SAndrea Mayer return ret; 198294604548SAndrea Mayer 198394604548SAndrea Mayer /* basic support for SRv6 Behavior counters requires at least: 198494604548SAndrea Mayer * packets, bytes and errors. 198594604548SAndrea Mayer */ 198694604548SAndrea Mayer if (!tb[SEG6_LOCAL_CNT_PACKETS] || !tb[SEG6_LOCAL_CNT_BYTES] || 198794604548SAndrea Mayer !tb[SEG6_LOCAL_CNT_ERRORS]) 198894604548SAndrea Mayer return -EINVAL; 198994604548SAndrea Mayer 199094604548SAndrea Mayer /* counters are always zero initialized */ 199194604548SAndrea Mayer pcounters = seg6_local_alloc_pcpu_counters(GFP_KERNEL); 199294604548SAndrea Mayer if (!pcounters) 199394604548SAndrea Mayer return -ENOMEM; 199494604548SAndrea Mayer 199594604548SAndrea Mayer slwt->pcpu_counters = pcounters; 199694604548SAndrea Mayer 199794604548SAndrea Mayer return 0; 199894604548SAndrea Mayer } 199994604548SAndrea Mayer 200094604548SAndrea Mayer static int seg6_local_fill_nla_counters(struct sk_buff *skb, 200194604548SAndrea Mayer struct seg6_local_counters *counters) 200294604548SAndrea Mayer { 200394604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_PACKETS, counters->packets, 200494604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 200594604548SAndrea Mayer return -EMSGSIZE; 200694604548SAndrea Mayer 200794604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_BYTES, counters->bytes, 200894604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 200994604548SAndrea Mayer return -EMSGSIZE; 201094604548SAndrea Mayer 201194604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_ERRORS, counters->errors, 201294604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 201394604548SAndrea Mayer return -EMSGSIZE; 201494604548SAndrea Mayer 201594604548SAndrea Mayer return 0; 201694604548SAndrea Mayer } 201794604548SAndrea Mayer 201894604548SAndrea Mayer static int put_nla_counters(struct sk_buff *skb, struct seg6_local_lwt *slwt) 201994604548SAndrea Mayer { 202094604548SAndrea Mayer struct seg6_local_counters counters = { 0, 0, 0 }; 202194604548SAndrea Mayer struct nlattr *nest; 202294604548SAndrea Mayer int rc, i; 202394604548SAndrea Mayer 202494604548SAndrea Mayer nest = nla_nest_start(skb, SEG6_LOCAL_COUNTERS); 202594604548SAndrea Mayer if (!nest) 202694604548SAndrea Mayer return -EMSGSIZE; 202794604548SAndrea Mayer 202894604548SAndrea Mayer for_each_possible_cpu(i) { 202994604548SAndrea Mayer struct pcpu_seg6_local_counters *pcounters; 203094604548SAndrea Mayer u64 packets, bytes, errors; 203194604548SAndrea Mayer unsigned int start; 203294604548SAndrea Mayer 203394604548SAndrea Mayer pcounters = per_cpu_ptr(slwt->pcpu_counters, i); 203494604548SAndrea Mayer do { 2035d120d1a6SThomas Gleixner start = u64_stats_fetch_begin(&pcounters->syncp); 203694604548SAndrea Mayer 203794604548SAndrea Mayer packets = u64_stats_read(&pcounters->packets); 203894604548SAndrea Mayer bytes = u64_stats_read(&pcounters->bytes); 203994604548SAndrea Mayer errors = u64_stats_read(&pcounters->errors); 204094604548SAndrea Mayer 2041d120d1a6SThomas Gleixner } while (u64_stats_fetch_retry(&pcounters->syncp, start)); 204294604548SAndrea Mayer 204394604548SAndrea Mayer counters.packets += packets; 204494604548SAndrea Mayer counters.bytes += bytes; 204594604548SAndrea Mayer counters.errors += errors; 204694604548SAndrea Mayer } 204794604548SAndrea Mayer 204894604548SAndrea Mayer rc = seg6_local_fill_nla_counters(skb, &counters); 204994604548SAndrea Mayer if (rc < 0) { 205094604548SAndrea Mayer nla_nest_cancel(skb, nest); 205194604548SAndrea Mayer return rc; 205294604548SAndrea Mayer } 205394604548SAndrea Mayer 205494604548SAndrea Mayer return nla_nest_end(skb, nest); 205594604548SAndrea Mayer } 205694604548SAndrea Mayer 205794604548SAndrea Mayer static int cmp_nla_counters(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 205894604548SAndrea Mayer { 205994604548SAndrea Mayer /* a and b are equal if both have pcpu_counters set or not */ 206094604548SAndrea Mayer return (!!((unsigned long)a->pcpu_counters)) ^ 206194604548SAndrea Mayer (!!((unsigned long)b->pcpu_counters)); 206294604548SAndrea Mayer } 206394604548SAndrea Mayer 206494604548SAndrea Mayer static void destroy_attr_counters(struct seg6_local_lwt *slwt) 206594604548SAndrea Mayer { 206694604548SAndrea Mayer free_percpu(slwt->pcpu_counters); 206794604548SAndrea Mayer } 206894604548SAndrea Mayer 2069848f3c0dSAndrea Mayer static const 2070848f3c0dSAndrea Mayer struct nla_policy seg6_local_flavors_policy[SEG6_LOCAL_FLV_MAX + 1] = { 2071848f3c0dSAndrea Mayer [SEG6_LOCAL_FLV_OPERATION] = { .type = NLA_U32 }, 2072848f3c0dSAndrea Mayer [SEG6_LOCAL_FLV_LCBLOCK_BITS] = { .type = NLA_U8 }, 2073848f3c0dSAndrea Mayer [SEG6_LOCAL_FLV_LCNODE_FN_BITS] = { .type = NLA_U8 }, 2074848f3c0dSAndrea Mayer }; 2075848f3c0dSAndrea Mayer 2076848f3c0dSAndrea Mayer /* check whether the lengths of the Locator-Block and Locator-Node Function 2077848f3c0dSAndrea Mayer * are compatible with the dimension of a C-SID container. 2078848f3c0dSAndrea Mayer */ 2079848f3c0dSAndrea Mayer static int seg6_chk_next_csid_cfg(__u8 block_len, __u8 func_len) 2080848f3c0dSAndrea Mayer { 2081848f3c0dSAndrea Mayer /* Locator-Block and Locator-Node Function cannot exceed 128 bits 2082848f3c0dSAndrea Mayer * (i.e. C-SID container lenghts). 2083848f3c0dSAndrea Mayer */ 2084848f3c0dSAndrea Mayer if (next_csid_chk_cntr_bits(block_len, func_len)) 2085848f3c0dSAndrea Mayer return -EINVAL; 2086848f3c0dSAndrea Mayer 2087848f3c0dSAndrea Mayer /* Locator-Block length must be greater than zero and evenly divisible 2088848f3c0dSAndrea Mayer * by 8. There must be room for a Locator-Node Function, at least. 2089848f3c0dSAndrea Mayer */ 2090848f3c0dSAndrea Mayer if (next_csid_chk_lcblock_bits(block_len)) 2091848f3c0dSAndrea Mayer return -EINVAL; 2092848f3c0dSAndrea Mayer 2093848f3c0dSAndrea Mayer /* Locator-Node Function length must be greater than zero and evenly 2094848f3c0dSAndrea Mayer * divisible by 8. There must be room for the Locator-Block. 2095848f3c0dSAndrea Mayer */ 2096848f3c0dSAndrea Mayer if (next_csid_chk_lcnode_fn_bits(func_len)) 2097848f3c0dSAndrea Mayer return -EINVAL; 2098848f3c0dSAndrea Mayer 2099848f3c0dSAndrea Mayer return 0; 2100848f3c0dSAndrea Mayer } 2101848f3c0dSAndrea Mayer 2102848f3c0dSAndrea Mayer static int seg6_parse_nla_next_csid_cfg(struct nlattr **tb, 2103848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo, 2104848f3c0dSAndrea Mayer struct netlink_ext_ack *extack) 2105848f3c0dSAndrea Mayer { 2106848f3c0dSAndrea Mayer __u8 func_len = SEG6_LOCAL_LCNODE_FN_DBITS; 2107848f3c0dSAndrea Mayer __u8 block_len = SEG6_LOCAL_LCBLOCK_DBITS; 2108848f3c0dSAndrea Mayer int rc; 2109848f3c0dSAndrea Mayer 2110848f3c0dSAndrea Mayer if (tb[SEG6_LOCAL_FLV_LCBLOCK_BITS]) 2111848f3c0dSAndrea Mayer block_len = nla_get_u8(tb[SEG6_LOCAL_FLV_LCBLOCK_BITS]); 2112848f3c0dSAndrea Mayer 2113848f3c0dSAndrea Mayer if (tb[SEG6_LOCAL_FLV_LCNODE_FN_BITS]) 2114848f3c0dSAndrea Mayer func_len = nla_get_u8(tb[SEG6_LOCAL_FLV_LCNODE_FN_BITS]); 2115848f3c0dSAndrea Mayer 2116848f3c0dSAndrea Mayer rc = seg6_chk_next_csid_cfg(block_len, func_len); 2117848f3c0dSAndrea Mayer if (rc < 0) { 2118848f3c0dSAndrea Mayer NL_SET_ERR_MSG(extack, 2119848f3c0dSAndrea Mayer "Invalid Locator Block/Node Function lengths"); 2120848f3c0dSAndrea Mayer return rc; 2121848f3c0dSAndrea Mayer } 2122848f3c0dSAndrea Mayer 2123848f3c0dSAndrea Mayer finfo->lcblock_bits = block_len; 2124848f3c0dSAndrea Mayer finfo->lcnode_func_bits = func_len; 2125848f3c0dSAndrea Mayer 2126848f3c0dSAndrea Mayer return 0; 2127848f3c0dSAndrea Mayer } 2128848f3c0dSAndrea Mayer 2129848f3c0dSAndrea Mayer static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, 2130848f3c0dSAndrea Mayer struct netlink_ext_ack *extack) 2131848f3c0dSAndrea Mayer { 2132848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo = &slwt->flv_info; 2133848f3c0dSAndrea Mayer struct nlattr *tb[SEG6_LOCAL_FLV_MAX + 1]; 21347458575aSAndrea Mayer int action = slwt->action; 21357458575aSAndrea Mayer __u32 fops, supp_fops; 2136848f3c0dSAndrea Mayer int rc; 2137848f3c0dSAndrea Mayer 2138848f3c0dSAndrea Mayer rc = nla_parse_nested_deprecated(tb, SEG6_LOCAL_FLV_MAX, 2139848f3c0dSAndrea Mayer attrs[SEG6_LOCAL_FLAVORS], 2140848f3c0dSAndrea Mayer seg6_local_flavors_policy, NULL); 2141848f3c0dSAndrea Mayer if (rc < 0) 2142848f3c0dSAndrea Mayer return rc; 2143848f3c0dSAndrea Mayer 2144848f3c0dSAndrea Mayer /* this attribute MUST always be present since it represents the Flavor 2145848f3c0dSAndrea Mayer * operation(s) to be carried out. 2146848f3c0dSAndrea Mayer */ 2147848f3c0dSAndrea Mayer if (!tb[SEG6_LOCAL_FLV_OPERATION]) 2148848f3c0dSAndrea Mayer return -EINVAL; 2149848f3c0dSAndrea Mayer 2150848f3c0dSAndrea Mayer fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]); 21517458575aSAndrea Mayer rc = seg6_flv_supp_ops_by_action(action, &supp_fops); 21527458575aSAndrea Mayer if (rc < 0 || (fops & ~supp_fops)) { 2153848f3c0dSAndrea Mayer NL_SET_ERR_MSG(extack, "Unsupported Flavor operation(s)"); 2154848f3c0dSAndrea Mayer return -EOPNOTSUPP; 2155848f3c0dSAndrea Mayer } 2156848f3c0dSAndrea Mayer 2157848f3c0dSAndrea Mayer finfo->flv_ops = fops; 2158848f3c0dSAndrea Mayer 2159848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(fops)) { 2160848f3c0dSAndrea Mayer /* Locator-Block and Locator-Node Function lengths can be 2161848f3c0dSAndrea Mayer * provided by the user space. Otherwise, default values are 2162848f3c0dSAndrea Mayer * applied. 2163848f3c0dSAndrea Mayer */ 2164848f3c0dSAndrea Mayer rc = seg6_parse_nla_next_csid_cfg(tb, finfo, extack); 2165848f3c0dSAndrea Mayer if (rc < 0) 2166848f3c0dSAndrea Mayer return rc; 2167848f3c0dSAndrea Mayer } 2168848f3c0dSAndrea Mayer 2169848f3c0dSAndrea Mayer return 0; 2170848f3c0dSAndrea Mayer } 2171848f3c0dSAndrea Mayer 2172848f3c0dSAndrea Mayer static int seg6_fill_nla_next_csid_cfg(struct sk_buff *skb, 2173848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo) 2174848f3c0dSAndrea Mayer { 2175848f3c0dSAndrea Mayer if (nla_put_u8(skb, SEG6_LOCAL_FLV_LCBLOCK_BITS, finfo->lcblock_bits)) 2176848f3c0dSAndrea Mayer return -EMSGSIZE; 2177848f3c0dSAndrea Mayer 2178848f3c0dSAndrea Mayer if (nla_put_u8(skb, SEG6_LOCAL_FLV_LCNODE_FN_BITS, 2179848f3c0dSAndrea Mayer finfo->lcnode_func_bits)) 2180848f3c0dSAndrea Mayer return -EMSGSIZE; 2181848f3c0dSAndrea Mayer 2182848f3c0dSAndrea Mayer return 0; 2183848f3c0dSAndrea Mayer } 2184848f3c0dSAndrea Mayer 2185848f3c0dSAndrea Mayer static int put_nla_flavors(struct sk_buff *skb, struct seg6_local_lwt *slwt) 2186848f3c0dSAndrea Mayer { 2187848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo = &slwt->flv_info; 2188848f3c0dSAndrea Mayer __u32 fops = finfo->flv_ops; 2189848f3c0dSAndrea Mayer struct nlattr *nest; 2190848f3c0dSAndrea Mayer int rc; 2191848f3c0dSAndrea Mayer 2192848f3c0dSAndrea Mayer nest = nla_nest_start(skb, SEG6_LOCAL_FLAVORS); 2193848f3c0dSAndrea Mayer if (!nest) 2194848f3c0dSAndrea Mayer return -EMSGSIZE; 2195848f3c0dSAndrea Mayer 2196848f3c0dSAndrea Mayer if (nla_put_u32(skb, SEG6_LOCAL_FLV_OPERATION, fops)) { 2197848f3c0dSAndrea Mayer rc = -EMSGSIZE; 2198848f3c0dSAndrea Mayer goto err; 2199848f3c0dSAndrea Mayer } 2200848f3c0dSAndrea Mayer 2201848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(fops)) { 2202848f3c0dSAndrea Mayer rc = seg6_fill_nla_next_csid_cfg(skb, finfo); 2203848f3c0dSAndrea Mayer if (rc < 0) 2204848f3c0dSAndrea Mayer goto err; 2205848f3c0dSAndrea Mayer } 2206848f3c0dSAndrea Mayer 2207848f3c0dSAndrea Mayer return nla_nest_end(skb, nest); 2208848f3c0dSAndrea Mayer 2209848f3c0dSAndrea Mayer err: 2210848f3c0dSAndrea Mayer nla_nest_cancel(skb, nest); 2211848f3c0dSAndrea Mayer return rc; 2212848f3c0dSAndrea Mayer } 2213848f3c0dSAndrea Mayer 2214848f3c0dSAndrea Mayer static int seg6_cmp_nla_next_csid_cfg(struct seg6_flavors_info *finfo_a, 2215848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo_b) 2216848f3c0dSAndrea Mayer { 2217848f3c0dSAndrea Mayer if (finfo_a->lcblock_bits != finfo_b->lcblock_bits) 2218848f3c0dSAndrea Mayer return 1; 2219848f3c0dSAndrea Mayer 2220848f3c0dSAndrea Mayer if (finfo_a->lcnode_func_bits != finfo_b->lcnode_func_bits) 2221848f3c0dSAndrea Mayer return 1; 2222848f3c0dSAndrea Mayer 2223848f3c0dSAndrea Mayer return 0; 2224848f3c0dSAndrea Mayer } 2225848f3c0dSAndrea Mayer 2226848f3c0dSAndrea Mayer static int cmp_nla_flavors(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 2227848f3c0dSAndrea Mayer { 2228848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo_a = &a->flv_info; 2229848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo_b = &b->flv_info; 2230848f3c0dSAndrea Mayer 2231848f3c0dSAndrea Mayer if (finfo_a->flv_ops != finfo_b->flv_ops) 2232848f3c0dSAndrea Mayer return 1; 2233848f3c0dSAndrea Mayer 2234848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(finfo_a->flv_ops)) { 2235848f3c0dSAndrea Mayer if (seg6_cmp_nla_next_csid_cfg(finfo_a, finfo_b)) 2236848f3c0dSAndrea Mayer return 1; 2237848f3c0dSAndrea Mayer } 2238848f3c0dSAndrea Mayer 2239848f3c0dSAndrea Mayer return 0; 2240848f3c0dSAndrea Mayer } 2241848f3c0dSAndrea Mayer 2242848f3c0dSAndrea Mayer static int encap_size_flavors(struct seg6_local_lwt *slwt) 2243848f3c0dSAndrea Mayer { 2244848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo = &slwt->flv_info; 2245848f3c0dSAndrea Mayer int nlsize; 2246848f3c0dSAndrea Mayer 2247848f3c0dSAndrea Mayer nlsize = nla_total_size(0) + /* nest SEG6_LOCAL_FLAVORS */ 2248848f3c0dSAndrea Mayer nla_total_size(4); /* SEG6_LOCAL_FLV_OPERATION */ 2249848f3c0dSAndrea Mayer 2250848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(finfo->flv_ops)) 2251848f3c0dSAndrea Mayer nlsize += nla_total_size(1) + /* SEG6_LOCAL_FLV_LCBLOCK_BITS */ 2252848f3c0dSAndrea Mayer nla_total_size(1); /* SEG6_LOCAL_FLV_LCNODE_FN_BITS */ 2253848f3c0dSAndrea Mayer 2254848f3c0dSAndrea Mayer return nlsize; 2255848f3c0dSAndrea Mayer } 2256848f3c0dSAndrea Mayer 2257d1df6fd8SDavid Lebrun struct seg6_action_param { 2258e2a8ecc4SAndrea Mayer int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt, 2259e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack); 2260d1df6fd8SDavid Lebrun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 2261d1df6fd8SDavid Lebrun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); 2262964adce5SAndrea Mayer 2263964adce5SAndrea Mayer /* optional destroy() callback useful for releasing resources which 2264964adce5SAndrea Mayer * have been previously acquired in the corresponding parse() 2265964adce5SAndrea Mayer * function. 2266964adce5SAndrea Mayer */ 2267964adce5SAndrea Mayer void (*destroy)(struct seg6_local_lwt *slwt); 2268d1df6fd8SDavid Lebrun }; 2269d1df6fd8SDavid Lebrun 2270d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { 22712d9cc60aSDavid Lebrun [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh, 22722d9cc60aSDavid Lebrun .put = put_nla_srh, 2273964adce5SAndrea Mayer .cmp = cmp_nla_srh, 2274964adce5SAndrea Mayer .destroy = destroy_attr_srh }, 2275d1df6fd8SDavid Lebrun 22762d9cc60aSDavid Lebrun [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table, 22772d9cc60aSDavid Lebrun .put = put_nla_table, 22782d9cc60aSDavid Lebrun .cmp = cmp_nla_table }, 2279d1df6fd8SDavid Lebrun 22802d9cc60aSDavid Lebrun [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4, 22812d9cc60aSDavid Lebrun .put = put_nla_nh4, 22822d9cc60aSDavid Lebrun .cmp = cmp_nla_nh4 }, 2283d1df6fd8SDavid Lebrun 22842d9cc60aSDavid Lebrun [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6, 22852d9cc60aSDavid Lebrun .put = put_nla_nh6, 22862d9cc60aSDavid Lebrun .cmp = cmp_nla_nh6 }, 2287d1df6fd8SDavid Lebrun 22882d9cc60aSDavid Lebrun [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif, 22892d9cc60aSDavid Lebrun .put = put_nla_iif, 22902d9cc60aSDavid Lebrun .cmp = cmp_nla_iif }, 2291d1df6fd8SDavid Lebrun 22922d9cc60aSDavid Lebrun [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif, 22932d9cc60aSDavid Lebrun .put = put_nla_oif, 22942d9cc60aSDavid Lebrun .cmp = cmp_nla_oif }, 2295004d4b27SMathieu Xhonneux 2296004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .parse = parse_nla_bpf, 2297004d4b27SMathieu Xhonneux .put = put_nla_bpf, 2298964adce5SAndrea Mayer .cmp = cmp_nla_bpf, 2299964adce5SAndrea Mayer .destroy = destroy_attr_bpf }, 2300004d4b27SMathieu Xhonneux 2301664d6f86SAndrea Mayer [SEG6_LOCAL_VRFTABLE] = { .parse = parse_nla_vrftable, 2302664d6f86SAndrea Mayer .put = put_nla_vrftable, 2303664d6f86SAndrea Mayer .cmp = cmp_nla_vrftable }, 2304664d6f86SAndrea Mayer 230594604548SAndrea Mayer [SEG6_LOCAL_COUNTERS] = { .parse = parse_nla_counters, 230694604548SAndrea Mayer .put = put_nla_counters, 230794604548SAndrea Mayer .cmp = cmp_nla_counters, 230894604548SAndrea Mayer .destroy = destroy_attr_counters }, 2309848f3c0dSAndrea Mayer 2310848f3c0dSAndrea Mayer [SEG6_LOCAL_FLAVORS] = { .parse = parse_nla_flavors, 2311848f3c0dSAndrea Mayer .put = put_nla_flavors, 2312848f3c0dSAndrea Mayer .cmp = cmp_nla_flavors }, 2313d1df6fd8SDavid Lebrun }; 2314d1df6fd8SDavid Lebrun 2315964adce5SAndrea Mayer /* call the destroy() callback (if available) for each set attribute in 23160a3021f1SAndrea Mayer * @parsed_attrs, starting from the first attribute up to the @max_parsed 23170a3021f1SAndrea Mayer * (excluded) attribute. 2318964adce5SAndrea Mayer */ 23190a3021f1SAndrea Mayer static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed, 23200a3021f1SAndrea Mayer struct seg6_local_lwt *slwt) 2321964adce5SAndrea Mayer { 2322964adce5SAndrea Mayer struct seg6_action_param *param; 2323964adce5SAndrea Mayer int i; 2324964adce5SAndrea Mayer 2325964adce5SAndrea Mayer /* Every required seg6local attribute is identified by an ID which is 2326964adce5SAndrea Mayer * encoded as a flag (i.e: 1 << ID) in the 'attrs' bitmask; 2327964adce5SAndrea Mayer * 23280a3021f1SAndrea Mayer * We scan the 'parsed_attrs' bitmask, starting from the first attribute 2329964adce5SAndrea Mayer * up to the @max_parsed (excluded) attribute. 2330964adce5SAndrea Mayer * For each set attribute, we retrieve the corresponding destroy() 2331964adce5SAndrea Mayer * callback. If the callback is not available, then we skip to the next 2332964adce5SAndrea Mayer * attribute; otherwise, we call the destroy() callback. 2333964adce5SAndrea Mayer */ 2334ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < max_parsed; ++i) { 2335300a0fd8SAndrea Mayer if (!(parsed_attrs & SEG6_F_ATTR(i))) 2336964adce5SAndrea Mayer continue; 2337964adce5SAndrea Mayer 2338964adce5SAndrea Mayer param = &seg6_action_params[i]; 2339964adce5SAndrea Mayer 2340964adce5SAndrea Mayer if (param->destroy) 2341964adce5SAndrea Mayer param->destroy(slwt); 2342964adce5SAndrea Mayer } 2343964adce5SAndrea Mayer } 2344964adce5SAndrea Mayer 2345964adce5SAndrea Mayer /* release all the resources that may have been acquired during parsing 2346964adce5SAndrea Mayer * operations. 2347964adce5SAndrea Mayer */ 2348964adce5SAndrea Mayer static void destroy_attrs(struct seg6_local_lwt *slwt) 2349964adce5SAndrea Mayer { 23500a3021f1SAndrea Mayer unsigned long attrs = slwt->desc->attrs | slwt->parsed_optattrs; 23510a3021f1SAndrea Mayer 23520a3021f1SAndrea Mayer __destroy_attrs(attrs, SEG6_LOCAL_MAX + 1, slwt); 23530a3021f1SAndrea Mayer } 23540a3021f1SAndrea Mayer 23550a3021f1SAndrea Mayer static int parse_nla_optional_attrs(struct nlattr **attrs, 2356e2a8ecc4SAndrea Mayer struct seg6_local_lwt *slwt, 2357e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 23580a3021f1SAndrea Mayer { 23590a3021f1SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 23600a3021f1SAndrea Mayer unsigned long parsed_optattrs = 0; 23610a3021f1SAndrea Mayer struct seg6_action_param *param; 23620a3021f1SAndrea Mayer int err, i; 23630a3021f1SAndrea Mayer 2364ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; ++i) { 2365300a0fd8SAndrea Mayer if (!(desc->optattrs & SEG6_F_ATTR(i)) || !attrs[i]) 23660a3021f1SAndrea Mayer continue; 23670a3021f1SAndrea Mayer 23680a3021f1SAndrea Mayer /* once here, the i-th attribute is provided by the 23690a3021f1SAndrea Mayer * userspace AND it is identified optional as well. 23700a3021f1SAndrea Mayer */ 23710a3021f1SAndrea Mayer param = &seg6_action_params[i]; 23720a3021f1SAndrea Mayer 2373e2a8ecc4SAndrea Mayer err = param->parse(attrs, slwt, extack); 23740a3021f1SAndrea Mayer if (err < 0) 23750a3021f1SAndrea Mayer goto parse_optattrs_err; 23760a3021f1SAndrea Mayer 23770a3021f1SAndrea Mayer /* current attribute has been correctly parsed */ 2378300a0fd8SAndrea Mayer parsed_optattrs |= SEG6_F_ATTR(i); 23790a3021f1SAndrea Mayer } 23800a3021f1SAndrea Mayer 23810a3021f1SAndrea Mayer /* store in the tunnel state all the optional attributed successfully 23820a3021f1SAndrea Mayer * parsed. 23830a3021f1SAndrea Mayer */ 23840a3021f1SAndrea Mayer slwt->parsed_optattrs = parsed_optattrs; 23850a3021f1SAndrea Mayer 23860a3021f1SAndrea Mayer return 0; 23870a3021f1SAndrea Mayer 23880a3021f1SAndrea Mayer parse_optattrs_err: 23890a3021f1SAndrea Mayer __destroy_attrs(parsed_optattrs, i, slwt); 23900a3021f1SAndrea Mayer 23910a3021f1SAndrea Mayer return err; 2392964adce5SAndrea Mayer } 2393964adce5SAndrea Mayer 2394cfdf64a0SAndrea Mayer /* call the custom constructor of the behavior during its initialization phase 2395cfdf64a0SAndrea Mayer * and after that all its attributes have been parsed successfully. 2396cfdf64a0SAndrea Mayer */ 2397cfdf64a0SAndrea Mayer static int 2398cfdf64a0SAndrea Mayer seg6_local_lwtunnel_build_state(struct seg6_local_lwt *slwt, const void *cfg, 2399cfdf64a0SAndrea Mayer struct netlink_ext_ack *extack) 2400cfdf64a0SAndrea Mayer { 2401cfdf64a0SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 2402cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops *ops; 2403cfdf64a0SAndrea Mayer 2404cfdf64a0SAndrea Mayer ops = &desc->slwt_ops; 2405cfdf64a0SAndrea Mayer if (!ops->build_state) 2406cfdf64a0SAndrea Mayer return 0; 2407cfdf64a0SAndrea Mayer 2408cfdf64a0SAndrea Mayer return ops->build_state(slwt, cfg, extack); 2409cfdf64a0SAndrea Mayer } 2410cfdf64a0SAndrea Mayer 2411cfdf64a0SAndrea Mayer /* call the custom destructor of the behavior which is invoked before the 2412cfdf64a0SAndrea Mayer * tunnel is going to be destroyed. 2413cfdf64a0SAndrea Mayer */ 2414cfdf64a0SAndrea Mayer static void seg6_local_lwtunnel_destroy_state(struct seg6_local_lwt *slwt) 2415cfdf64a0SAndrea Mayer { 2416cfdf64a0SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 2417cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops *ops; 2418cfdf64a0SAndrea Mayer 2419cfdf64a0SAndrea Mayer ops = &desc->slwt_ops; 2420cfdf64a0SAndrea Mayer if (!ops->destroy_state) 2421cfdf64a0SAndrea Mayer return; 2422cfdf64a0SAndrea Mayer 2423cfdf64a0SAndrea Mayer ops->destroy_state(slwt); 2424cfdf64a0SAndrea Mayer } 2425cfdf64a0SAndrea Mayer 2426e2a8ecc4SAndrea Mayer static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt, 2427e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 2428d1df6fd8SDavid Lebrun { 2429d1df6fd8SDavid Lebrun struct seg6_action_param *param; 2430d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 24310a3021f1SAndrea Mayer unsigned long invalid_attrs; 2432d1df6fd8SDavid Lebrun int i, err; 2433d1df6fd8SDavid Lebrun 2434d1df6fd8SDavid Lebrun desc = __get_action_desc(slwt->action); 2435d1df6fd8SDavid Lebrun if (!desc) 2436d1df6fd8SDavid Lebrun return -EINVAL; 2437d1df6fd8SDavid Lebrun 2438d1df6fd8SDavid Lebrun if (!desc->input) 2439d1df6fd8SDavid Lebrun return -EOPNOTSUPP; 2440d1df6fd8SDavid Lebrun 2441d1df6fd8SDavid Lebrun slwt->desc = desc; 2442d1df6fd8SDavid Lebrun slwt->headroom += desc->static_headroom; 2443d1df6fd8SDavid Lebrun 24440a3021f1SAndrea Mayer /* Forcing the desc->optattrs *set* and the desc->attrs *set* to be 24450a3021f1SAndrea Mayer * disjoined, this allow us to release acquired resources by optional 24460a3021f1SAndrea Mayer * attributes and by required attributes independently from each other 24470d770360SAndrea Mayer * without any interference. 24480a3021f1SAndrea Mayer * In other terms, we are sure that we do not release some the acquired 24490a3021f1SAndrea Mayer * resources twice. 24500a3021f1SAndrea Mayer * 24510a3021f1SAndrea Mayer * Note that if an attribute is configured both as required and as 24520a3021f1SAndrea Mayer * optional, it means that the user has messed something up in the 24530a3021f1SAndrea Mayer * seg6_action_table. Therefore, this check is required for SRv6 24540a3021f1SAndrea Mayer * behaviors to work properly. 24550a3021f1SAndrea Mayer */ 24560a3021f1SAndrea Mayer invalid_attrs = desc->attrs & desc->optattrs; 24570a3021f1SAndrea Mayer if (invalid_attrs) { 24580a3021f1SAndrea Mayer WARN_ONCE(1, 24590a3021f1SAndrea Mayer "An attribute cannot be both required AND optional"); 24600a3021f1SAndrea Mayer return -EINVAL; 24610a3021f1SAndrea Mayer } 24620a3021f1SAndrea Mayer 24630a3021f1SAndrea Mayer /* parse the required attributes */ 2464ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { 2465300a0fd8SAndrea Mayer if (desc->attrs & SEG6_F_ATTR(i)) { 2466d1df6fd8SDavid Lebrun if (!attrs[i]) 2467d1df6fd8SDavid Lebrun return -EINVAL; 2468d1df6fd8SDavid Lebrun 2469d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 2470d1df6fd8SDavid Lebrun 2471e2a8ecc4SAndrea Mayer err = param->parse(attrs, slwt, extack); 2472d1df6fd8SDavid Lebrun if (err < 0) 24730a3021f1SAndrea Mayer goto parse_attrs_err; 2474d1df6fd8SDavid Lebrun } 2475d1df6fd8SDavid Lebrun } 2476d1df6fd8SDavid Lebrun 24770a3021f1SAndrea Mayer /* parse the optional attributes, if any */ 2478e2a8ecc4SAndrea Mayer err = parse_nla_optional_attrs(attrs, slwt, extack); 24790a3021f1SAndrea Mayer if (err < 0) 24800a3021f1SAndrea Mayer goto parse_attrs_err; 24810a3021f1SAndrea Mayer 2482d1df6fd8SDavid Lebrun return 0; 2483964adce5SAndrea Mayer 24840a3021f1SAndrea Mayer parse_attrs_err: 2485964adce5SAndrea Mayer /* release any resource that may have been acquired during the i-1 2486964adce5SAndrea Mayer * parse() operations. 2487964adce5SAndrea Mayer */ 24880a3021f1SAndrea Mayer __destroy_attrs(desc->attrs, i, slwt); 2489964adce5SAndrea Mayer 2490964adce5SAndrea Mayer return err; 2491d1df6fd8SDavid Lebrun } 2492d1df6fd8SDavid Lebrun 2493faee6769SAlexander Aring static int seg6_local_build_state(struct net *net, struct nlattr *nla, 2494faee6769SAlexander Aring unsigned int family, const void *cfg, 2495faee6769SAlexander Aring struct lwtunnel_state **ts, 2496d1df6fd8SDavid Lebrun struct netlink_ext_ack *extack) 2497d1df6fd8SDavid Lebrun { 2498d1df6fd8SDavid Lebrun struct nlattr *tb[SEG6_LOCAL_MAX + 1]; 2499d1df6fd8SDavid Lebrun struct lwtunnel_state *newts; 2500d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 2501d1df6fd8SDavid Lebrun int err; 2502d1df6fd8SDavid Lebrun 25036285217fSDavid Lebrun if (family != AF_INET6) 25046285217fSDavid Lebrun return -EINVAL; 25056285217fSDavid Lebrun 25068cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla, 25078cb08174SJohannes Berg seg6_local_policy, extack); 2508d1df6fd8SDavid Lebrun 2509d1df6fd8SDavid Lebrun if (err < 0) 2510d1df6fd8SDavid Lebrun return err; 2511d1df6fd8SDavid Lebrun 2512d1df6fd8SDavid Lebrun if (!tb[SEG6_LOCAL_ACTION]) 2513d1df6fd8SDavid Lebrun return -EINVAL; 2514d1df6fd8SDavid Lebrun 2515d1df6fd8SDavid Lebrun newts = lwtunnel_state_alloc(sizeof(*slwt)); 2516d1df6fd8SDavid Lebrun if (!newts) 2517d1df6fd8SDavid Lebrun return -ENOMEM; 2518d1df6fd8SDavid Lebrun 2519d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(newts); 2520d1df6fd8SDavid Lebrun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); 2521d1df6fd8SDavid Lebrun 2522e2a8ecc4SAndrea Mayer err = parse_nla_action(tb, slwt, extack); 2523d1df6fd8SDavid Lebrun if (err < 0) 2524d1df6fd8SDavid Lebrun goto out_free; 2525d1df6fd8SDavid Lebrun 2526cfdf64a0SAndrea Mayer err = seg6_local_lwtunnel_build_state(slwt, cfg, extack); 2527cfdf64a0SAndrea Mayer if (err < 0) 2528cfdf64a0SAndrea Mayer goto out_destroy_attrs; 2529cfdf64a0SAndrea Mayer 2530d1df6fd8SDavid Lebrun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; 2531d1df6fd8SDavid Lebrun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; 2532d1df6fd8SDavid Lebrun newts->headroom = slwt->headroom; 2533d1df6fd8SDavid Lebrun 2534d1df6fd8SDavid Lebrun *ts = newts; 2535d1df6fd8SDavid Lebrun 2536d1df6fd8SDavid Lebrun return 0; 2537d1df6fd8SDavid Lebrun 2538cfdf64a0SAndrea Mayer out_destroy_attrs: 2539cfdf64a0SAndrea Mayer destroy_attrs(slwt); 2540d1df6fd8SDavid Lebrun out_free: 2541d1df6fd8SDavid Lebrun kfree(newts); 2542d1df6fd8SDavid Lebrun return err; 2543d1df6fd8SDavid Lebrun } 2544d1df6fd8SDavid Lebrun 2545d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt) 2546d1df6fd8SDavid Lebrun { 2547d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 2548d1df6fd8SDavid Lebrun 2549cfdf64a0SAndrea Mayer seg6_local_lwtunnel_destroy_state(slwt); 2550cfdf64a0SAndrea Mayer 2551964adce5SAndrea Mayer destroy_attrs(slwt); 2552004d4b27SMathieu Xhonneux 2553004d4b27SMathieu Xhonneux return; 2554d1df6fd8SDavid Lebrun } 2555d1df6fd8SDavid Lebrun 2556d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb, 2557d1df6fd8SDavid Lebrun struct lwtunnel_state *lwt) 2558d1df6fd8SDavid Lebrun { 2559d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 2560d1df6fd8SDavid Lebrun struct seg6_action_param *param; 25610a3021f1SAndrea Mayer unsigned long attrs; 2562d1df6fd8SDavid Lebrun int i, err; 2563d1df6fd8SDavid Lebrun 2564d1df6fd8SDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) 2565d1df6fd8SDavid Lebrun return -EMSGSIZE; 2566d1df6fd8SDavid Lebrun 25670a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 25680a3021f1SAndrea Mayer 2569ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { 2570300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(i)) { 2571d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 2572d1df6fd8SDavid Lebrun err = param->put(skb, slwt); 2573d1df6fd8SDavid Lebrun if (err < 0) 2574d1df6fd8SDavid Lebrun return err; 2575d1df6fd8SDavid Lebrun } 2576d1df6fd8SDavid Lebrun } 2577d1df6fd8SDavid Lebrun 2578d1df6fd8SDavid Lebrun return 0; 2579d1df6fd8SDavid Lebrun } 2580d1df6fd8SDavid Lebrun 2581d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) 2582d1df6fd8SDavid Lebrun { 2583d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 2584d1df6fd8SDavid Lebrun unsigned long attrs; 2585d1df6fd8SDavid Lebrun int nlsize; 2586d1df6fd8SDavid Lebrun 2587d1df6fd8SDavid Lebrun nlsize = nla_total_size(4); /* action */ 2588d1df6fd8SDavid Lebrun 25890a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 2590d1df6fd8SDavid Lebrun 2591300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_SRH)) 2592d1df6fd8SDavid Lebrun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); 2593d1df6fd8SDavid Lebrun 2594300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE)) 2595d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 2596d1df6fd8SDavid Lebrun 2597300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH4)) 2598d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 2599d1df6fd8SDavid Lebrun 2600300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH6)) 2601d1df6fd8SDavid Lebrun nlsize += nla_total_size(16); 2602d1df6fd8SDavid Lebrun 2603300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_IIF)) 2604d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 2605d1df6fd8SDavid Lebrun 2606300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_OIF)) 2607d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 2608d1df6fd8SDavid Lebrun 2609300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_BPF)) 2610004d4b27SMathieu Xhonneux nlsize += nla_total_size(sizeof(struct nlattr)) + 2611004d4b27SMathieu Xhonneux nla_total_size(MAX_PROG_NAME) + 2612004d4b27SMathieu Xhonneux nla_total_size(4); 2613004d4b27SMathieu Xhonneux 2614300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE)) 2615664d6f86SAndrea Mayer nlsize += nla_total_size(4); 2616664d6f86SAndrea Mayer 261794604548SAndrea Mayer if (attrs & SEG6_F_LOCAL_COUNTERS) 261894604548SAndrea Mayer nlsize += nla_total_size(0) + /* nest SEG6_LOCAL_COUNTERS */ 261994604548SAndrea Mayer /* SEG6_LOCAL_CNT_PACKETS */ 262094604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)) + 262194604548SAndrea Mayer /* SEG6_LOCAL_CNT_BYTES */ 262294604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)) + 262394604548SAndrea Mayer /* SEG6_LOCAL_CNT_ERRORS */ 262494604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)); 262594604548SAndrea Mayer 2626848f3c0dSAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_FLAVORS)) 2627848f3c0dSAndrea Mayer nlsize += encap_size_flavors(slwt); 2628848f3c0dSAndrea Mayer 2629d1df6fd8SDavid Lebrun return nlsize; 2630d1df6fd8SDavid Lebrun } 2631d1df6fd8SDavid Lebrun 2632d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a, 2633d1df6fd8SDavid Lebrun struct lwtunnel_state *b) 2634d1df6fd8SDavid Lebrun { 2635d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt_a, *slwt_b; 2636d1df6fd8SDavid Lebrun struct seg6_action_param *param; 26370a3021f1SAndrea Mayer unsigned long attrs_a, attrs_b; 2638d1df6fd8SDavid Lebrun int i; 2639d1df6fd8SDavid Lebrun 2640d1df6fd8SDavid Lebrun slwt_a = seg6_local_lwtunnel(a); 2641d1df6fd8SDavid Lebrun slwt_b = seg6_local_lwtunnel(b); 2642d1df6fd8SDavid Lebrun 2643d1df6fd8SDavid Lebrun if (slwt_a->action != slwt_b->action) 2644d1df6fd8SDavid Lebrun return 1; 2645d1df6fd8SDavid Lebrun 26460a3021f1SAndrea Mayer attrs_a = slwt_a->desc->attrs | slwt_a->parsed_optattrs; 26470a3021f1SAndrea Mayer attrs_b = slwt_b->desc->attrs | slwt_b->parsed_optattrs; 26480a3021f1SAndrea Mayer 26490a3021f1SAndrea Mayer if (attrs_a != attrs_b) 2650d1df6fd8SDavid Lebrun return 1; 2651d1df6fd8SDavid Lebrun 2652ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { 2653300a0fd8SAndrea Mayer if (attrs_a & SEG6_F_ATTR(i)) { 2654d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 2655d1df6fd8SDavid Lebrun if (param->cmp(slwt_a, slwt_b)) 2656d1df6fd8SDavid Lebrun return 1; 2657d1df6fd8SDavid Lebrun } 2658d1df6fd8SDavid Lebrun } 2659d1df6fd8SDavid Lebrun 2660d1df6fd8SDavid Lebrun return 0; 2661d1df6fd8SDavid Lebrun } 2662d1df6fd8SDavid Lebrun 2663d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = { 2664d1df6fd8SDavid Lebrun .build_state = seg6_local_build_state, 2665d1df6fd8SDavid Lebrun .destroy_state = seg6_local_destroy_state, 2666d1df6fd8SDavid Lebrun .input = seg6_local_input, 2667d1df6fd8SDavid Lebrun .fill_encap = seg6_local_fill_encap, 2668d1df6fd8SDavid Lebrun .get_encap_size = seg6_local_get_encap_size, 2669d1df6fd8SDavid Lebrun .cmp_encap = seg6_local_cmp_encap, 2670d1df6fd8SDavid Lebrun .owner = THIS_MODULE, 2671d1df6fd8SDavid Lebrun }; 2672d1df6fd8SDavid Lebrun 2673d1df6fd8SDavid Lebrun int __init seg6_local_init(void) 2674d1df6fd8SDavid Lebrun { 2675300a0fd8SAndrea Mayer /* If the max total number of defined attributes is reached, then your 2676300a0fd8SAndrea Mayer * kernel build stops here. 2677300a0fd8SAndrea Mayer * 2678300a0fd8SAndrea Mayer * This check is required to avoid arithmetic overflows when processing 2679300a0fd8SAndrea Mayer * behavior attributes and the maximum number of defined attributes 2680300a0fd8SAndrea Mayer * exceeds the allowed value. 2681300a0fd8SAndrea Mayer */ 2682300a0fd8SAndrea Mayer BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsigned long)); 2683300a0fd8SAndrea Mayer 26847458575aSAndrea Mayer /* Check whether the number of defined flavors exceeds the maximum 26857458575aSAndrea Mayer * allowed value. 26867458575aSAndrea Mayer */ 26877458575aSAndrea Mayer BUILD_BUG_ON(SEG6_LOCAL_FLV_OP_MAX + 1 > BITS_PER_TYPE(__u32)); 26887458575aSAndrea Mayer 2689848f3c0dSAndrea Mayer /* If the default NEXT-C-SID Locator-Block/Node Function lengths (in 2690848f3c0dSAndrea Mayer * bits) have been changed with invalid values, kernel build stops 2691848f3c0dSAndrea Mayer * here. 2692848f3c0dSAndrea Mayer */ 2693848f3c0dSAndrea Mayer BUILD_BUG_ON(next_csid_chk_cntr_bits(SEG6_LOCAL_LCBLOCK_DBITS, 2694848f3c0dSAndrea Mayer SEG6_LOCAL_LCNODE_FN_DBITS)); 2695848f3c0dSAndrea Mayer BUILD_BUG_ON(next_csid_chk_lcblock_bits(SEG6_LOCAL_LCBLOCK_DBITS)); 2696848f3c0dSAndrea Mayer BUILD_BUG_ON(next_csid_chk_lcnode_fn_bits(SEG6_LOCAL_LCNODE_FN_DBITS)); 2697848f3c0dSAndrea Mayer 2698bdf3c0b9SAndrea Mayer /* To be memory efficient, we use 'u8' to represent the different 2699bdf3c0b9SAndrea Mayer * actions related to RFC8986 flavors. If the kernel build stops here, 2700bdf3c0b9SAndrea Mayer * it means that it is not possible to correctly encode these actions 2701bdf3c0b9SAndrea Mayer * with the data type chosen for the action table. 2702bdf3c0b9SAndrea Mayer */ 2703bdf3c0b9SAndrea Mayer BUILD_BUG_ON(SEG6_LOCAL_FLV_ACT_MAX > (typeof(flv8986_act_tbl[0]))~0U); 2704bdf3c0b9SAndrea Mayer 2705d1df6fd8SDavid Lebrun return lwtunnel_encap_add_ops(&seg6_local_ops, 2706d1df6fd8SDavid Lebrun LWTUNNEL_ENCAP_SEG6_LOCAL); 2707d1df6fd8SDavid Lebrun } 2708d1df6fd8SDavid Lebrun 2709d1df6fd8SDavid Lebrun void seg6_local_exit(void) 2710d1df6fd8SDavid Lebrun { 2711d1df6fd8SDavid Lebrun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); 2712d1df6fd8SDavid Lebrun } 2713