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 112848f3c0dSAndrea Mayer /* Supported Flavor operations are reported in this bitmask */ 113848f3c0dSAndrea Mayer #define SEG6_LOCAL_FLV_SUPP_OPS (BIT(SEG6_LOCAL_FLV_OP_NEXT_CSID)) 114848f3c0dSAndrea Mayer 115848f3c0dSAndrea Mayer struct seg6_flavors_info { 116848f3c0dSAndrea Mayer /* Flavor operations */ 117848f3c0dSAndrea Mayer __u32 flv_ops; 118848f3c0dSAndrea Mayer 119848f3c0dSAndrea Mayer /* Locator-Block length, expressed in bits */ 120848f3c0dSAndrea Mayer __u8 lcblock_bits; 121848f3c0dSAndrea Mayer /* Locator-Node Function length, expressed in bits*/ 122848f3c0dSAndrea Mayer __u8 lcnode_func_bits; 123848f3c0dSAndrea Mayer }; 124848f3c0dSAndrea Mayer 125664d6f86SAndrea Mayer enum seg6_end_dt_mode { 126664d6f86SAndrea Mayer DT_INVALID_MODE = -EINVAL, 127664d6f86SAndrea Mayer DT_LEGACY_MODE = 0, 128664d6f86SAndrea Mayer DT_VRF_MODE = 1, 129664d6f86SAndrea Mayer }; 130664d6f86SAndrea Mayer 131664d6f86SAndrea Mayer struct seg6_end_dt_info { 132664d6f86SAndrea Mayer enum seg6_end_dt_mode mode; 133664d6f86SAndrea Mayer 134664d6f86SAndrea Mayer struct net *net; 135664d6f86SAndrea Mayer /* VRF device associated to the routing table used by the SRv6 136664d6f86SAndrea Mayer * End.DT4/DT6 behavior for routing IPv4/IPv6 packets. 137664d6f86SAndrea Mayer */ 138664d6f86SAndrea Mayer int vrf_ifindex; 139664d6f86SAndrea Mayer int vrf_table; 140664d6f86SAndrea Mayer 1418b532109SAndrea Mayer /* tunneled packet family (IPv4 or IPv6). 1428b532109SAndrea Mayer * Protocol and header length are inferred from family. 1438b532109SAndrea Mayer */ 144664d6f86SAndrea Mayer u16 family; 145664d6f86SAndrea Mayer }; 146664d6f86SAndrea Mayer 14794604548SAndrea Mayer struct pcpu_seg6_local_counters { 14894604548SAndrea Mayer u64_stats_t packets; 14994604548SAndrea Mayer u64_stats_t bytes; 15094604548SAndrea Mayer u64_stats_t errors; 15194604548SAndrea Mayer 15294604548SAndrea Mayer struct u64_stats_sync syncp; 15394604548SAndrea Mayer }; 15494604548SAndrea Mayer 15594604548SAndrea Mayer /* This struct groups all the SRv6 Behavior counters supported so far. 15694604548SAndrea Mayer * 15794604548SAndrea Mayer * put_nla_counters() makes use of this data structure to collect all counter 15894604548SAndrea Mayer * values after the per-CPU counter evaluation has been performed. 15994604548SAndrea Mayer * Finally, each counter value (in seg6_local_counters) is stored in the 16094604548SAndrea Mayer * corresponding netlink attribute and sent to user space. 16194604548SAndrea Mayer * 16294604548SAndrea Mayer * NB: we don't want to expose this structure to user space! 16394604548SAndrea Mayer */ 16494604548SAndrea Mayer struct seg6_local_counters { 16594604548SAndrea Mayer __u64 packets; 16694604548SAndrea Mayer __u64 bytes; 16794604548SAndrea Mayer __u64 errors; 16894604548SAndrea Mayer }; 16994604548SAndrea Mayer 17094604548SAndrea Mayer #define seg6_local_alloc_pcpu_counters(__gfp) \ 17194604548SAndrea Mayer __netdev_alloc_pcpu_stats(struct pcpu_seg6_local_counters, \ 17294604548SAndrea Mayer ((__gfp) | __GFP_ZERO)) 17394604548SAndrea Mayer 17494604548SAndrea Mayer #define SEG6_F_LOCAL_COUNTERS SEG6_F_ATTR(SEG6_LOCAL_COUNTERS) 17594604548SAndrea Mayer 176d1df6fd8SDavid Lebrun struct seg6_local_lwt { 177d1df6fd8SDavid Lebrun int action; 178d1df6fd8SDavid Lebrun struct ipv6_sr_hdr *srh; 179d1df6fd8SDavid Lebrun int table; 180d1df6fd8SDavid Lebrun struct in_addr nh4; 181d1df6fd8SDavid Lebrun struct in6_addr nh6; 182d1df6fd8SDavid Lebrun int iif; 183d1df6fd8SDavid Lebrun int oif; 184004d4b27SMathieu Xhonneux struct bpf_lwt_prog bpf; 185664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 186664d6f86SAndrea Mayer struct seg6_end_dt_info dt_info; 187664d6f86SAndrea Mayer #endif 188848f3c0dSAndrea Mayer struct seg6_flavors_info flv_info; 189848f3c0dSAndrea Mayer 19094604548SAndrea Mayer struct pcpu_seg6_local_counters __percpu *pcpu_counters; 191d1df6fd8SDavid Lebrun 192d1df6fd8SDavid Lebrun int headroom; 193d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1940a3021f1SAndrea Mayer /* unlike the required attrs, we have to track the optional attributes 1950a3021f1SAndrea Mayer * that have been effectively parsed. 1960a3021f1SAndrea Mayer */ 1970a3021f1SAndrea Mayer unsigned long parsed_optattrs; 198d1df6fd8SDavid Lebrun }; 199d1df6fd8SDavid Lebrun 200d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) 201d1df6fd8SDavid Lebrun { 202d1df6fd8SDavid Lebrun return (struct seg6_local_lwt *)lwt->data; 203d1df6fd8SDavid Lebrun } 204d1df6fd8SDavid Lebrun 205140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) 206140f04c3SDavid Lebrun { 207140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 208140f04c3SDavid Lebrun 209fa55a7d7SAndrew Lunn srh = seg6_get_srh(skb, IP6_FH_F_SKIP_RH); 210140f04c3SDavid Lebrun if (!srh) 211140f04c3SDavid Lebrun return NULL; 212140f04c3SDavid Lebrun 213140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 214140f04c3SDavid Lebrun if (!seg6_hmac_validate_skb(skb)) 215140f04c3SDavid Lebrun return NULL; 216140f04c3SDavid Lebrun #endif 217140f04c3SDavid Lebrun 218140f04c3SDavid Lebrun return srh; 219140f04c3SDavid Lebrun } 220140f04c3SDavid Lebrun 221d7a669ddSDavid Lebrun static bool decap_and_validate(struct sk_buff *skb, int proto) 222d7a669ddSDavid Lebrun { 223d7a669ddSDavid Lebrun struct ipv6_sr_hdr *srh; 224d7a669ddSDavid Lebrun unsigned int off = 0; 225d7a669ddSDavid Lebrun 226fa55a7d7SAndrew Lunn srh = seg6_get_srh(skb, 0); 227d7a669ddSDavid Lebrun if (srh && srh->segments_left > 0) 228d7a669ddSDavid Lebrun return false; 229d7a669ddSDavid Lebrun 230d7a669ddSDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 231d7a669ddSDavid Lebrun if (srh && !seg6_hmac_validate_skb(skb)) 232d7a669ddSDavid Lebrun return false; 233d7a669ddSDavid Lebrun #endif 234d7a669ddSDavid Lebrun 235d7a669ddSDavid Lebrun if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0) 236d7a669ddSDavid Lebrun return false; 237d7a669ddSDavid Lebrun 238d7a669ddSDavid Lebrun if (!pskb_pull(skb, off)) 239d7a669ddSDavid Lebrun return false; 240d7a669ddSDavid Lebrun 241d7a669ddSDavid Lebrun skb_postpull_rcsum(skb, skb_network_header(skb), off); 242d7a669ddSDavid Lebrun 243d7a669ddSDavid Lebrun skb_reset_network_header(skb); 244d7a669ddSDavid Lebrun skb_reset_transport_header(skb); 24562ebaeaeSYuki Taguchi if (iptunnel_pull_offloads(skb)) 24662ebaeaeSYuki Taguchi return false; 247d7a669ddSDavid Lebrun 248d7a669ddSDavid Lebrun return true; 249d7a669ddSDavid Lebrun } 250d7a669ddSDavid Lebrun 251d7a669ddSDavid Lebrun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr) 252d7a669ddSDavid Lebrun { 253d7a669ddSDavid Lebrun struct in6_addr *addr; 254d7a669ddSDavid Lebrun 255d7a669ddSDavid Lebrun srh->segments_left--; 256d7a669ddSDavid Lebrun addr = srh->segments + srh->segments_left; 257d7a669ddSDavid Lebrun *daddr = *addr; 258d7a669ddSDavid Lebrun } 259d7a669ddSDavid Lebrun 260fd1fef0cSAndrea Mayer static int 261fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr, 262fd1fef0cSAndrea Mayer u32 tbl_id, bool local_delivery) 263d7a669ddSDavid Lebrun { 264d7a669ddSDavid Lebrun struct net *net = dev_net(skb->dev); 265d7a669ddSDavid Lebrun struct ipv6hdr *hdr = ipv6_hdr(skb); 266d7a669ddSDavid Lebrun int flags = RT6_LOOKUP_F_HAS_SADDR; 267d7a669ddSDavid Lebrun struct dst_entry *dst = NULL; 268d7a669ddSDavid Lebrun struct rt6_info *rt; 269d7a669ddSDavid Lebrun struct flowi6 fl6; 270fd1fef0cSAndrea Mayer int dev_flags = 0; 271d7a669ddSDavid Lebrun 272a3bd2102SAndrea Mayer memset(&fl6, 0, sizeof(fl6)); 273d7a669ddSDavid Lebrun fl6.flowi6_iif = skb->dev->ifindex; 274d7a669ddSDavid Lebrun fl6.daddr = nhaddr ? *nhaddr : hdr->daddr; 275d7a669ddSDavid Lebrun fl6.saddr = hdr->saddr; 276d7a669ddSDavid Lebrun fl6.flowlabel = ip6_flowinfo(hdr); 277d7a669ddSDavid Lebrun fl6.flowi6_mark = skb->mark; 278d7a669ddSDavid Lebrun fl6.flowi6_proto = hdr->nexthdr; 279d7a669ddSDavid Lebrun 280d7a669ddSDavid Lebrun if (nhaddr) 281d7a669ddSDavid Lebrun fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH; 282d7a669ddSDavid Lebrun 283d7a669ddSDavid Lebrun if (!tbl_id) { 284b75cc8f9SDavid Ahern dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags); 285d7a669ddSDavid Lebrun } else { 286d7a669ddSDavid Lebrun struct fib6_table *table; 287d7a669ddSDavid Lebrun 288d7a669ddSDavid Lebrun table = fib6_get_table(net, tbl_id); 289d7a669ddSDavid Lebrun if (!table) 290d7a669ddSDavid Lebrun goto out; 291d7a669ddSDavid Lebrun 292b75cc8f9SDavid Ahern rt = ip6_pol_route(net, table, 0, &fl6, skb, flags); 293d7a669ddSDavid Lebrun dst = &rt->dst; 294d7a669ddSDavid Lebrun } 295d7a669ddSDavid Lebrun 296fd1fef0cSAndrea Mayer /* we want to discard traffic destined for local packet processing, 297fd1fef0cSAndrea Mayer * if @local_delivery is set to false. 298fd1fef0cSAndrea Mayer */ 299fd1fef0cSAndrea Mayer if (!local_delivery) 300fd1fef0cSAndrea Mayer dev_flags |= IFF_LOOPBACK; 301fd1fef0cSAndrea Mayer 302fd1fef0cSAndrea Mayer if (dst && (dst->dev->flags & dev_flags) && !dst->error) { 303d7a669ddSDavid Lebrun dst_release(dst); 304d7a669ddSDavid Lebrun dst = NULL; 305d7a669ddSDavid Lebrun } 306d7a669ddSDavid Lebrun 307d7a669ddSDavid Lebrun out: 308d7a669ddSDavid Lebrun if (!dst) { 309d7a669ddSDavid Lebrun rt = net->ipv6.ip6_blk_hole_entry; 310d7a669ddSDavid Lebrun dst = &rt->dst; 311d7a669ddSDavid Lebrun dst_hold(dst); 312d7a669ddSDavid Lebrun } 313d7a669ddSDavid Lebrun 314d7a669ddSDavid Lebrun skb_dst_drop(skb); 315d7a669ddSDavid Lebrun skb_dst_set(skb, dst); 3161c1e761eSMathieu Xhonneux return dst->error; 317d7a669ddSDavid Lebrun } 318d7a669ddSDavid Lebrun 319fd1fef0cSAndrea Mayer int seg6_lookup_nexthop(struct sk_buff *skb, 320fd1fef0cSAndrea Mayer struct in6_addr *nhaddr, u32 tbl_id) 321fd1fef0cSAndrea Mayer { 322fd1fef0cSAndrea Mayer return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false); 323fd1fef0cSAndrea Mayer } 324fd1fef0cSAndrea Mayer 325848f3c0dSAndrea Mayer static __u8 seg6_flv_lcblock_octects(const struct seg6_flavors_info *finfo) 326848f3c0dSAndrea Mayer { 327848f3c0dSAndrea Mayer return finfo->lcblock_bits >> 3; 328848f3c0dSAndrea Mayer } 329848f3c0dSAndrea Mayer 330848f3c0dSAndrea Mayer static __u8 seg6_flv_lcnode_func_octects(const struct seg6_flavors_info *finfo) 331848f3c0dSAndrea Mayer { 332848f3c0dSAndrea Mayer return finfo->lcnode_func_bits >> 3; 333848f3c0dSAndrea Mayer } 334848f3c0dSAndrea Mayer 335848f3c0dSAndrea Mayer static bool seg6_next_csid_is_arg_zero(const struct in6_addr *addr, 336848f3c0dSAndrea Mayer const struct seg6_flavors_info *finfo) 337848f3c0dSAndrea Mayer { 338848f3c0dSAndrea Mayer __u8 fnc_octects = seg6_flv_lcnode_func_octects(finfo); 339848f3c0dSAndrea Mayer __u8 blk_octects = seg6_flv_lcblock_octects(finfo); 340848f3c0dSAndrea Mayer __u8 arg_octects; 341848f3c0dSAndrea Mayer int i; 342848f3c0dSAndrea Mayer 343848f3c0dSAndrea Mayer arg_octects = 16 - blk_octects - fnc_octects; 344848f3c0dSAndrea Mayer for (i = 0; i < arg_octects; ++i) { 345848f3c0dSAndrea Mayer if (addr->s6_addr[blk_octects + fnc_octects + i] != 0x00) 346848f3c0dSAndrea Mayer return false; 347848f3c0dSAndrea Mayer } 348848f3c0dSAndrea Mayer 349848f3c0dSAndrea Mayer return true; 350848f3c0dSAndrea Mayer } 351848f3c0dSAndrea Mayer 352848f3c0dSAndrea Mayer /* assume that DA.Argument length > 0 */ 353848f3c0dSAndrea Mayer static void seg6_next_csid_advance_arg(struct in6_addr *addr, 354848f3c0dSAndrea Mayer const struct seg6_flavors_info *finfo) 355848f3c0dSAndrea Mayer { 356848f3c0dSAndrea Mayer __u8 fnc_octects = seg6_flv_lcnode_func_octects(finfo); 357848f3c0dSAndrea Mayer __u8 blk_octects = seg6_flv_lcblock_octects(finfo); 358848f3c0dSAndrea Mayer 359848f3c0dSAndrea Mayer /* advance DA.Argument */ 360848f3c0dSAndrea Mayer memmove(&addr->s6_addr[blk_octects], 361848f3c0dSAndrea Mayer &addr->s6_addr[blk_octects + fnc_octects], 362848f3c0dSAndrea Mayer 16 - blk_octects - fnc_octects); 363848f3c0dSAndrea Mayer 364848f3c0dSAndrea Mayer memset(&addr->s6_addr[16 - fnc_octects], 0x00, fnc_octects); 365848f3c0dSAndrea Mayer } 366848f3c0dSAndrea Mayer 367*525c65ffSAndrea Mayer static int input_action_end_finish(struct sk_buff *skb, 368*525c65ffSAndrea Mayer struct seg6_local_lwt *slwt) 369*525c65ffSAndrea Mayer { 370*525c65ffSAndrea Mayer seg6_lookup_nexthop(skb, NULL, 0); 371*525c65ffSAndrea Mayer 372*525c65ffSAndrea Mayer return dst_input(skb); 373*525c65ffSAndrea Mayer } 374*525c65ffSAndrea Mayer 375848f3c0dSAndrea Mayer static int input_action_end_core(struct sk_buff *skb, 376848f3c0dSAndrea Mayer struct seg6_local_lwt *slwt) 377140f04c3SDavid Lebrun { 378140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 379140f04c3SDavid Lebrun 380140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 381140f04c3SDavid Lebrun if (!srh) 382140f04c3SDavid Lebrun goto drop; 383140f04c3SDavid Lebrun 384d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 385140f04c3SDavid Lebrun 386*525c65ffSAndrea Mayer return input_action_end_finish(skb, slwt); 387140f04c3SDavid Lebrun 388140f04c3SDavid Lebrun drop: 389140f04c3SDavid Lebrun kfree_skb(skb); 390140f04c3SDavid Lebrun return -EINVAL; 391140f04c3SDavid Lebrun } 392140f04c3SDavid Lebrun 393848f3c0dSAndrea Mayer static int end_next_csid_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) 394848f3c0dSAndrea Mayer { 395848f3c0dSAndrea Mayer const struct seg6_flavors_info *finfo = &slwt->flv_info; 396848f3c0dSAndrea Mayer struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; 397848f3c0dSAndrea Mayer 398848f3c0dSAndrea Mayer if (seg6_next_csid_is_arg_zero(daddr, finfo)) 399848f3c0dSAndrea Mayer return input_action_end_core(skb, slwt); 400848f3c0dSAndrea Mayer 401848f3c0dSAndrea Mayer /* update DA */ 402848f3c0dSAndrea Mayer seg6_next_csid_advance_arg(daddr, finfo); 403848f3c0dSAndrea Mayer 404*525c65ffSAndrea Mayer return input_action_end_finish(skb, slwt); 405848f3c0dSAndrea Mayer } 406848f3c0dSAndrea Mayer 407848f3c0dSAndrea Mayer static bool seg6_next_csid_enabled(__u32 fops) 408848f3c0dSAndrea Mayer { 409848f3c0dSAndrea Mayer return fops & BIT(SEG6_LOCAL_FLV_OP_NEXT_CSID); 410848f3c0dSAndrea Mayer } 411848f3c0dSAndrea Mayer 412848f3c0dSAndrea Mayer /* regular endpoint function */ 413848f3c0dSAndrea Mayer static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 414848f3c0dSAndrea Mayer { 415848f3c0dSAndrea Mayer const struct seg6_flavors_info *finfo = &slwt->flv_info; 416848f3c0dSAndrea Mayer 417848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(finfo->flv_ops)) 418848f3c0dSAndrea Mayer return end_next_csid_core(skb, slwt); 419848f3c0dSAndrea Mayer 420848f3c0dSAndrea Mayer return input_action_end_core(skb, slwt); 421848f3c0dSAndrea Mayer } 422848f3c0dSAndrea Mayer 423140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */ 424140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) 425140f04c3SDavid Lebrun { 426140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 427140f04c3SDavid Lebrun 428140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 429140f04c3SDavid Lebrun if (!srh) 430140f04c3SDavid Lebrun goto drop; 431140f04c3SDavid Lebrun 432d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 433140f04c3SDavid Lebrun 4341c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, &slwt->nh6, 0); 435140f04c3SDavid Lebrun 436140f04c3SDavid Lebrun return dst_input(skb); 437140f04c3SDavid Lebrun 438140f04c3SDavid Lebrun drop: 439140f04c3SDavid Lebrun kfree_skb(skb); 440140f04c3SDavid Lebrun return -EINVAL; 441140f04c3SDavid Lebrun } 442140f04c3SDavid Lebrun 443891ef8ddSDavid Lebrun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) 444891ef8ddSDavid Lebrun { 445891ef8ddSDavid Lebrun struct ipv6_sr_hdr *srh; 446891ef8ddSDavid Lebrun 447891ef8ddSDavid Lebrun srh = get_and_validate_srh(skb); 448891ef8ddSDavid Lebrun if (!srh) 449891ef8ddSDavid Lebrun goto drop; 450891ef8ddSDavid Lebrun 451891ef8ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 452891ef8ddSDavid Lebrun 4531c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, slwt->table); 454891ef8ddSDavid Lebrun 455891ef8ddSDavid Lebrun return dst_input(skb); 456891ef8ddSDavid Lebrun 457891ef8ddSDavid Lebrun drop: 458891ef8ddSDavid Lebrun kfree_skb(skb); 459891ef8ddSDavid Lebrun return -EINVAL; 460891ef8ddSDavid Lebrun } 461891ef8ddSDavid Lebrun 462891ef8ddSDavid Lebrun /* decapsulate and forward inner L2 frame on specified interface */ 463891ef8ddSDavid Lebrun static int input_action_end_dx2(struct sk_buff *skb, 464891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 465891ef8ddSDavid Lebrun { 466891ef8ddSDavid Lebrun struct net *net = dev_net(skb->dev); 467891ef8ddSDavid Lebrun struct net_device *odev; 468891ef8ddSDavid Lebrun struct ethhdr *eth; 469891ef8ddSDavid Lebrun 47026776253SPaolo Lungaroni if (!decap_and_validate(skb, IPPROTO_ETHERNET)) 471891ef8ddSDavid Lebrun goto drop; 472891ef8ddSDavid Lebrun 473891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, ETH_HLEN)) 474891ef8ddSDavid Lebrun goto drop; 475891ef8ddSDavid Lebrun 476891ef8ddSDavid Lebrun skb_reset_mac_header(skb); 477891ef8ddSDavid Lebrun eth = (struct ethhdr *)skb->data; 478891ef8ddSDavid Lebrun 479891ef8ddSDavid Lebrun /* To determine the frame's protocol, we assume it is 802.3. This avoids 480891ef8ddSDavid Lebrun * a call to eth_type_trans(), which is not really relevant for our 481891ef8ddSDavid Lebrun * use case. 482891ef8ddSDavid Lebrun */ 483891ef8ddSDavid Lebrun if (!eth_proto_is_802_3(eth->h_proto)) 484891ef8ddSDavid Lebrun goto drop; 485891ef8ddSDavid Lebrun 486891ef8ddSDavid Lebrun odev = dev_get_by_index_rcu(net, slwt->oif); 487891ef8ddSDavid Lebrun if (!odev) 488891ef8ddSDavid Lebrun goto drop; 489891ef8ddSDavid Lebrun 490891ef8ddSDavid Lebrun /* As we accept Ethernet frames, make sure the egress device is of 491891ef8ddSDavid Lebrun * the correct type. 492891ef8ddSDavid Lebrun */ 493891ef8ddSDavid Lebrun if (odev->type != ARPHRD_ETHER) 494891ef8ddSDavid Lebrun goto drop; 495891ef8ddSDavid Lebrun 496891ef8ddSDavid Lebrun if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev)) 497891ef8ddSDavid Lebrun goto drop; 498891ef8ddSDavid Lebrun 499891ef8ddSDavid Lebrun skb_orphan(skb); 500891ef8ddSDavid Lebrun 501891ef8ddSDavid Lebrun if (skb_warn_if_lro(skb)) 502891ef8ddSDavid Lebrun goto drop; 503891ef8ddSDavid Lebrun 504891ef8ddSDavid Lebrun skb_forward_csum(skb); 505891ef8ddSDavid Lebrun 506891ef8ddSDavid Lebrun if (skb->len - ETH_HLEN > odev->mtu) 507891ef8ddSDavid Lebrun goto drop; 508891ef8ddSDavid Lebrun 509891ef8ddSDavid Lebrun skb->dev = odev; 510891ef8ddSDavid Lebrun skb->protocol = eth->h_proto; 511891ef8ddSDavid Lebrun 512891ef8ddSDavid Lebrun return dev_queue_xmit(skb); 513891ef8ddSDavid Lebrun 514891ef8ddSDavid Lebrun drop: 515891ef8ddSDavid Lebrun kfree_skb(skb); 516891ef8ddSDavid Lebrun return -EINVAL; 517891ef8ddSDavid Lebrun } 518891ef8ddSDavid Lebrun 5197a3f5b0dSRyoga Saito static int input_action_end_dx6_finish(struct net *net, struct sock *sk, 5207a3f5b0dSRyoga Saito struct sk_buff *skb) 5217a3f5b0dSRyoga Saito { 5227a3f5b0dSRyoga Saito struct dst_entry *orig_dst = skb_dst(skb); 5237a3f5b0dSRyoga Saito struct in6_addr *nhaddr = NULL; 5247a3f5b0dSRyoga Saito struct seg6_local_lwt *slwt; 5257a3f5b0dSRyoga Saito 5267a3f5b0dSRyoga Saito slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 5277a3f5b0dSRyoga Saito 5287a3f5b0dSRyoga Saito /* The inner packet is not associated to any local interface, 5297a3f5b0dSRyoga Saito * so we do not call netif_rx(). 5307a3f5b0dSRyoga Saito * 5317a3f5b0dSRyoga Saito * If slwt->nh6 is set to ::, then lookup the nexthop for the 5327a3f5b0dSRyoga Saito * inner packet's DA. Otherwise, use the specified nexthop. 5337a3f5b0dSRyoga Saito */ 5347a3f5b0dSRyoga Saito if (!ipv6_addr_any(&slwt->nh6)) 5357a3f5b0dSRyoga Saito nhaddr = &slwt->nh6; 5367a3f5b0dSRyoga Saito 5377a3f5b0dSRyoga Saito seg6_lookup_nexthop(skb, nhaddr, 0); 5387a3f5b0dSRyoga Saito 5397a3f5b0dSRyoga Saito return dst_input(skb); 5407a3f5b0dSRyoga Saito } 5417a3f5b0dSRyoga Saito 542140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */ 543140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb, 544140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 545140f04c3SDavid Lebrun { 546140f04c3SDavid Lebrun /* this function accepts IPv6 encapsulated packets, with either 547140f04c3SDavid Lebrun * an SRH with SL=0, or no SRH. 548140f04c3SDavid Lebrun */ 549140f04c3SDavid Lebrun 550d7a669ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 551140f04c3SDavid Lebrun goto drop; 552140f04c3SDavid Lebrun 553d7a669ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 554140f04c3SDavid Lebrun goto drop; 555140f04c3SDavid Lebrun 556c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 5577a3f5b0dSRyoga Saito nf_reset_ct(skb); 558c71644d0SAndrea Mayer 5597a3f5b0dSRyoga Saito if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 5607a3f5b0dSRyoga Saito return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, 5617a3f5b0dSRyoga Saito dev_net(skb->dev), NULL, skb, NULL, 5627a3f5b0dSRyoga Saito skb_dst(skb)->dev, input_action_end_dx6_finish); 563140f04c3SDavid Lebrun 5647a3f5b0dSRyoga Saito return input_action_end_dx6_finish(dev_net(skb->dev), NULL, skb); 565140f04c3SDavid Lebrun drop: 566140f04c3SDavid Lebrun kfree_skb(skb); 567140f04c3SDavid Lebrun return -EINVAL; 568140f04c3SDavid Lebrun } 569140f04c3SDavid Lebrun 5707a3f5b0dSRyoga Saito static int input_action_end_dx4_finish(struct net *net, struct sock *sk, 5717a3f5b0dSRyoga Saito struct sk_buff *skb) 572891ef8ddSDavid Lebrun { 5737a3f5b0dSRyoga Saito struct dst_entry *orig_dst = skb_dst(skb); 5747a3f5b0dSRyoga Saito struct seg6_local_lwt *slwt; 575891ef8ddSDavid Lebrun struct iphdr *iph; 576891ef8ddSDavid Lebrun __be32 nhaddr; 577891ef8ddSDavid Lebrun int err; 578891ef8ddSDavid Lebrun 5797a3f5b0dSRyoga Saito slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 580891ef8ddSDavid Lebrun 581891ef8ddSDavid Lebrun iph = ip_hdr(skb); 582891ef8ddSDavid Lebrun 583891ef8ddSDavid Lebrun nhaddr = slwt->nh4.s_addr ?: iph->daddr; 584891ef8ddSDavid Lebrun 585891ef8ddSDavid Lebrun skb_dst_drop(skb); 586891ef8ddSDavid Lebrun 587891ef8ddSDavid Lebrun err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev); 5887a3f5b0dSRyoga Saito if (err) { 5897a3f5b0dSRyoga Saito kfree_skb(skb); 5907a3f5b0dSRyoga Saito return -EINVAL; 5917a3f5b0dSRyoga Saito } 592891ef8ddSDavid Lebrun 593891ef8ddSDavid Lebrun return dst_input(skb); 5947a3f5b0dSRyoga Saito } 595891ef8ddSDavid Lebrun 5967a3f5b0dSRyoga Saito static int input_action_end_dx4(struct sk_buff *skb, 5977a3f5b0dSRyoga Saito struct seg6_local_lwt *slwt) 5987a3f5b0dSRyoga Saito { 5997a3f5b0dSRyoga Saito if (!decap_and_validate(skb, IPPROTO_IPIP)) 6007a3f5b0dSRyoga Saito goto drop; 6017a3f5b0dSRyoga Saito 6027a3f5b0dSRyoga Saito if (!pskb_may_pull(skb, sizeof(struct iphdr))) 6037a3f5b0dSRyoga Saito goto drop; 6047a3f5b0dSRyoga Saito 6057a3f5b0dSRyoga Saito skb->protocol = htons(ETH_P_IP); 6067a3f5b0dSRyoga Saito skb_set_transport_header(skb, sizeof(struct iphdr)); 6077a3f5b0dSRyoga Saito nf_reset_ct(skb); 6087a3f5b0dSRyoga Saito 6097a3f5b0dSRyoga Saito if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 6107a3f5b0dSRyoga Saito return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, 6117a3f5b0dSRyoga Saito dev_net(skb->dev), NULL, skb, NULL, 6127a3f5b0dSRyoga Saito skb_dst(skb)->dev, input_action_end_dx4_finish); 6137a3f5b0dSRyoga Saito 6147a3f5b0dSRyoga Saito return input_action_end_dx4_finish(dev_net(skb->dev), NULL, skb); 615891ef8ddSDavid Lebrun drop: 616891ef8ddSDavid Lebrun kfree_skb(skb); 617891ef8ddSDavid Lebrun return -EINVAL; 618891ef8ddSDavid Lebrun } 619891ef8ddSDavid Lebrun 620664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 621664d6f86SAndrea Mayer static struct net *fib6_config_get_net(const struct fib6_config *fib6_cfg) 622664d6f86SAndrea Mayer { 623664d6f86SAndrea Mayer const struct nl_info *nli = &fib6_cfg->fc_nlinfo; 624664d6f86SAndrea Mayer 625664d6f86SAndrea Mayer return nli->nl_net; 626664d6f86SAndrea Mayer } 627664d6f86SAndrea Mayer 628664d6f86SAndrea Mayer static int __seg6_end_dt_vrf_build(struct seg6_local_lwt *slwt, const void *cfg, 629664d6f86SAndrea Mayer u16 family, struct netlink_ext_ack *extack) 630664d6f86SAndrea Mayer { 631664d6f86SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 632664d6f86SAndrea Mayer int vrf_ifindex; 633664d6f86SAndrea Mayer struct net *net; 634664d6f86SAndrea Mayer 635664d6f86SAndrea Mayer net = fib6_config_get_net(cfg); 636664d6f86SAndrea Mayer 637664d6f86SAndrea Mayer /* note that vrf_table was already set by parse_nla_vrftable() */ 638664d6f86SAndrea Mayer vrf_ifindex = l3mdev_ifindex_lookup_by_table_id(L3MDEV_TYPE_VRF, net, 639664d6f86SAndrea Mayer info->vrf_table); 640664d6f86SAndrea Mayer if (vrf_ifindex < 0) { 641664d6f86SAndrea Mayer if (vrf_ifindex == -EPERM) { 642664d6f86SAndrea Mayer NL_SET_ERR_MSG(extack, 643664d6f86SAndrea Mayer "Strict mode for VRF is disabled"); 644664d6f86SAndrea Mayer } else if (vrf_ifindex == -ENODEV) { 645664d6f86SAndrea Mayer NL_SET_ERR_MSG(extack, 646664d6f86SAndrea Mayer "Table has no associated VRF device"); 647664d6f86SAndrea Mayer } else { 648664d6f86SAndrea Mayer pr_debug("seg6local: SRv6 End.DT* creation error=%d\n", 649664d6f86SAndrea Mayer vrf_ifindex); 650664d6f86SAndrea Mayer } 651664d6f86SAndrea Mayer 652664d6f86SAndrea Mayer return vrf_ifindex; 653664d6f86SAndrea Mayer } 654664d6f86SAndrea Mayer 655664d6f86SAndrea Mayer info->net = net; 656664d6f86SAndrea Mayer info->vrf_ifindex = vrf_ifindex; 657664d6f86SAndrea Mayer 658664d6f86SAndrea Mayer info->family = family; 659664d6f86SAndrea Mayer info->mode = DT_VRF_MODE; 660664d6f86SAndrea Mayer 661664d6f86SAndrea Mayer return 0; 662664d6f86SAndrea Mayer } 663664d6f86SAndrea Mayer 664664d6f86SAndrea Mayer /* The SRv6 End.DT4/DT6 behavior extracts the inner (IPv4/IPv6) packet and 665664d6f86SAndrea Mayer * routes the IPv4/IPv6 packet by looking at the configured routing table. 666664d6f86SAndrea Mayer * 667664d6f86SAndrea Mayer * In the SRv6 End.DT4/DT6 use case, we can receive traffic (IPv6+Segment 668664d6f86SAndrea Mayer * Routing Header packets) from several interfaces and the outer IPv6 669664d6f86SAndrea Mayer * destination address (DA) is used for retrieving the specific instance of the 670664d6f86SAndrea Mayer * End.DT4/DT6 behavior that should process the packets. 671664d6f86SAndrea Mayer * 672664d6f86SAndrea Mayer * However, the inner IPv4/IPv6 packet is not really bound to any receiving 673664d6f86SAndrea Mayer * interface and thus the End.DT4/DT6 sets the VRF (associated with the 674664d6f86SAndrea Mayer * corresponding routing table) as the *receiving* interface. 675664d6f86SAndrea Mayer * In other words, the End.DT4/DT6 processes a packet as if it has been received 676664d6f86SAndrea Mayer * directly by the VRF (and not by one of its slave devices, if any). 677664d6f86SAndrea Mayer * In this way, the VRF interface is used for routing the IPv4/IPv6 packet in 678664d6f86SAndrea Mayer * according to the routing table configured by the End.DT4/DT6 instance. 679664d6f86SAndrea Mayer * 680664d6f86SAndrea Mayer * This design allows you to get some interesting features like: 681664d6f86SAndrea Mayer * 1) the statistics on rx packets; 682664d6f86SAndrea Mayer * 2) the possibility to install a packet sniffer on the receiving interface 683664d6f86SAndrea Mayer * (the VRF one) for looking at the incoming packets; 684664d6f86SAndrea Mayer * 3) the possibility to leverage the netfilter prerouting hook for the inner 685664d6f86SAndrea Mayer * IPv4 packet. 686664d6f86SAndrea Mayer * 687664d6f86SAndrea Mayer * This function returns: 688664d6f86SAndrea Mayer * - the sk_buff* when the VRF rcv handler has processed the packet correctly; 689664d6f86SAndrea Mayer * - NULL when the skb is consumed by the VRF rcv handler; 690664d6f86SAndrea Mayer * - a pointer which encodes a negative error number in case of error. 691664d6f86SAndrea Mayer * Note that in this case, the function takes care of freeing the skb. 692664d6f86SAndrea Mayer */ 693664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_rcv(struct sk_buff *skb, u16 family, 694664d6f86SAndrea Mayer struct net_device *dev) 695664d6f86SAndrea Mayer { 696664d6f86SAndrea Mayer /* based on l3mdev_ip_rcv; we are only interested in the master */ 697664d6f86SAndrea Mayer if (unlikely(!netif_is_l3_master(dev) && !netif_has_l3_rx_handler(dev))) 698664d6f86SAndrea Mayer goto drop; 699664d6f86SAndrea Mayer 700664d6f86SAndrea Mayer if (unlikely(!dev->l3mdev_ops->l3mdev_l3_rcv)) 701664d6f86SAndrea Mayer goto drop; 702664d6f86SAndrea Mayer 703664d6f86SAndrea Mayer /* the decap packet IPv4/IPv6 does not come with any mac header info. 704664d6f86SAndrea Mayer * We must unset the mac header to allow the VRF device to rebuild it, 705664d6f86SAndrea Mayer * just in case there is a sniffer attached on the device. 706664d6f86SAndrea Mayer */ 707664d6f86SAndrea Mayer skb_unset_mac_header(skb); 708664d6f86SAndrea Mayer 709664d6f86SAndrea Mayer skb = dev->l3mdev_ops->l3mdev_l3_rcv(dev, skb, family); 710664d6f86SAndrea Mayer if (!skb) 711664d6f86SAndrea Mayer /* the skb buffer was consumed by the handler */ 712664d6f86SAndrea Mayer return NULL; 713664d6f86SAndrea Mayer 714664d6f86SAndrea Mayer /* when a packet is received by a VRF or by one of its slaves, the 715664d6f86SAndrea Mayer * master device reference is set into the skb. 716664d6f86SAndrea Mayer */ 717664d6f86SAndrea Mayer if (unlikely(skb->dev != dev || skb->skb_iif != dev->ifindex)) 718664d6f86SAndrea Mayer goto drop; 719664d6f86SAndrea Mayer 720664d6f86SAndrea Mayer return skb; 721664d6f86SAndrea Mayer 722664d6f86SAndrea Mayer drop: 723664d6f86SAndrea Mayer kfree_skb(skb); 724664d6f86SAndrea Mayer return ERR_PTR(-EINVAL); 725664d6f86SAndrea Mayer } 726664d6f86SAndrea Mayer 727664d6f86SAndrea Mayer static struct net_device *end_dt_get_vrf_rcu(struct sk_buff *skb, 728664d6f86SAndrea Mayer struct seg6_end_dt_info *info) 729664d6f86SAndrea Mayer { 730664d6f86SAndrea Mayer int vrf_ifindex = info->vrf_ifindex; 731664d6f86SAndrea Mayer struct net *net = info->net; 732664d6f86SAndrea Mayer 733664d6f86SAndrea Mayer if (unlikely(vrf_ifindex < 0)) 734664d6f86SAndrea Mayer goto error; 735664d6f86SAndrea Mayer 736664d6f86SAndrea Mayer if (unlikely(!net_eq(dev_net(skb->dev), net))) 737664d6f86SAndrea Mayer goto error; 738664d6f86SAndrea Mayer 739664d6f86SAndrea Mayer return dev_get_by_index_rcu(net, vrf_ifindex); 740664d6f86SAndrea Mayer 741664d6f86SAndrea Mayer error: 742664d6f86SAndrea Mayer return NULL; 743664d6f86SAndrea Mayer } 744664d6f86SAndrea Mayer 745664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb, 7468b532109SAndrea Mayer struct seg6_local_lwt *slwt, u16 family) 747664d6f86SAndrea Mayer { 748664d6f86SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 749664d6f86SAndrea Mayer struct net_device *vrf; 7508b532109SAndrea Mayer __be16 protocol; 7518b532109SAndrea Mayer int hdrlen; 752664d6f86SAndrea Mayer 753664d6f86SAndrea Mayer vrf = end_dt_get_vrf_rcu(skb, info); 754664d6f86SAndrea Mayer if (unlikely(!vrf)) 755664d6f86SAndrea Mayer goto drop; 756664d6f86SAndrea Mayer 7578b532109SAndrea Mayer switch (family) { 7588b532109SAndrea Mayer case AF_INET: 7598b532109SAndrea Mayer protocol = htons(ETH_P_IP); 7608b532109SAndrea Mayer hdrlen = sizeof(struct iphdr); 7618b532109SAndrea Mayer break; 7628b532109SAndrea Mayer case AF_INET6: 7638b532109SAndrea Mayer protocol = htons(ETH_P_IPV6); 7648b532109SAndrea Mayer hdrlen = sizeof(struct ipv6hdr); 7658b532109SAndrea Mayer break; 7668b532109SAndrea Mayer case AF_UNSPEC: 7678b532109SAndrea Mayer fallthrough; 7688b532109SAndrea Mayer default: 7698b532109SAndrea Mayer goto drop; 7708b532109SAndrea Mayer } 7718b532109SAndrea Mayer 7728b532109SAndrea Mayer if (unlikely(info->family != AF_UNSPEC && info->family != family)) { 7738b532109SAndrea Mayer pr_warn_once("seg6local: SRv6 End.DT* family mismatch"); 7748b532109SAndrea Mayer goto drop; 7758b532109SAndrea Mayer } 7768b532109SAndrea Mayer 7778b532109SAndrea Mayer skb->protocol = protocol; 778664d6f86SAndrea Mayer 779664d6f86SAndrea Mayer skb_dst_drop(skb); 780664d6f86SAndrea Mayer 7818b532109SAndrea Mayer skb_set_transport_header(skb, hdrlen); 7827a3f5b0dSRyoga Saito nf_reset_ct(skb); 783664d6f86SAndrea Mayer 7848b532109SAndrea Mayer return end_dt_vrf_rcv(skb, family, vrf); 785664d6f86SAndrea Mayer 786664d6f86SAndrea Mayer drop: 787664d6f86SAndrea Mayer kfree_skb(skb); 788664d6f86SAndrea Mayer return ERR_PTR(-EINVAL); 789664d6f86SAndrea Mayer } 790664d6f86SAndrea Mayer 791664d6f86SAndrea Mayer static int input_action_end_dt4(struct sk_buff *skb, 792664d6f86SAndrea Mayer struct seg6_local_lwt *slwt) 793664d6f86SAndrea Mayer { 794664d6f86SAndrea Mayer struct iphdr *iph; 795664d6f86SAndrea Mayer int err; 796664d6f86SAndrea Mayer 797664d6f86SAndrea Mayer if (!decap_and_validate(skb, IPPROTO_IPIP)) 798664d6f86SAndrea Mayer goto drop; 799664d6f86SAndrea Mayer 800664d6f86SAndrea Mayer if (!pskb_may_pull(skb, sizeof(struct iphdr))) 801664d6f86SAndrea Mayer goto drop; 802664d6f86SAndrea Mayer 8038b532109SAndrea Mayer skb = end_dt_vrf_core(skb, slwt, AF_INET); 804664d6f86SAndrea Mayer if (!skb) 805664d6f86SAndrea Mayer /* packet has been processed and consumed by the VRF */ 806664d6f86SAndrea Mayer return 0; 807664d6f86SAndrea Mayer 808664d6f86SAndrea Mayer if (IS_ERR(skb)) 809664d6f86SAndrea Mayer return PTR_ERR(skb); 810664d6f86SAndrea Mayer 811664d6f86SAndrea Mayer iph = ip_hdr(skb); 812664d6f86SAndrea Mayer 813664d6f86SAndrea Mayer err = ip_route_input(skb, iph->daddr, iph->saddr, 0, skb->dev); 814664d6f86SAndrea Mayer if (unlikely(err)) 815664d6f86SAndrea Mayer goto drop; 816664d6f86SAndrea Mayer 817664d6f86SAndrea Mayer return dst_input(skb); 818664d6f86SAndrea Mayer 819664d6f86SAndrea Mayer drop: 820664d6f86SAndrea Mayer kfree_skb(skb); 821664d6f86SAndrea Mayer return -EINVAL; 822664d6f86SAndrea Mayer } 823664d6f86SAndrea Mayer 824664d6f86SAndrea Mayer static int seg6_end_dt4_build(struct seg6_local_lwt *slwt, const void *cfg, 825664d6f86SAndrea Mayer struct netlink_ext_ack *extack) 826664d6f86SAndrea Mayer { 827664d6f86SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET, extack); 828664d6f86SAndrea Mayer } 82920a081b7SAndrea Mayer 83020a081b7SAndrea Mayer static enum 83120a081b7SAndrea Mayer seg6_end_dt_mode seg6_end_dt6_parse_mode(struct seg6_local_lwt *slwt) 83220a081b7SAndrea Mayer { 83320a081b7SAndrea Mayer unsigned long parsed_optattrs = slwt->parsed_optattrs; 83420a081b7SAndrea Mayer bool legacy, vrfmode; 83520a081b7SAndrea Mayer 836300a0fd8SAndrea Mayer legacy = !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE)); 837300a0fd8SAndrea Mayer vrfmode = !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE)); 83820a081b7SAndrea Mayer 83920a081b7SAndrea Mayer if (!(legacy ^ vrfmode)) 84020a081b7SAndrea Mayer /* both are absent or present: invalid DT6 mode */ 84120a081b7SAndrea Mayer return DT_INVALID_MODE; 84220a081b7SAndrea Mayer 84320a081b7SAndrea Mayer return legacy ? DT_LEGACY_MODE : DT_VRF_MODE; 84420a081b7SAndrea Mayer } 84520a081b7SAndrea Mayer 84620a081b7SAndrea Mayer static enum seg6_end_dt_mode seg6_end_dt6_get_mode(struct seg6_local_lwt *slwt) 84720a081b7SAndrea Mayer { 84820a081b7SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 84920a081b7SAndrea Mayer 85020a081b7SAndrea Mayer return info->mode; 85120a081b7SAndrea Mayer } 85220a081b7SAndrea Mayer 85320a081b7SAndrea Mayer static int seg6_end_dt6_build(struct seg6_local_lwt *slwt, const void *cfg, 85420a081b7SAndrea Mayer struct netlink_ext_ack *extack) 85520a081b7SAndrea Mayer { 85620a081b7SAndrea Mayer enum seg6_end_dt_mode mode = seg6_end_dt6_parse_mode(slwt); 85720a081b7SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 85820a081b7SAndrea Mayer 85920a081b7SAndrea Mayer switch (mode) { 86020a081b7SAndrea Mayer case DT_LEGACY_MODE: 86120a081b7SAndrea Mayer info->mode = DT_LEGACY_MODE; 86220a081b7SAndrea Mayer return 0; 86320a081b7SAndrea Mayer case DT_VRF_MODE: 86420a081b7SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET6, extack); 86520a081b7SAndrea Mayer default: 86620a081b7SAndrea Mayer NL_SET_ERR_MSG(extack, "table or vrftable must be specified"); 86720a081b7SAndrea Mayer return -EINVAL; 86820a081b7SAndrea Mayer } 86920a081b7SAndrea Mayer } 870664d6f86SAndrea Mayer #endif 871664d6f86SAndrea Mayer 872891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb, 873891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 874891ef8ddSDavid Lebrun { 875891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 876891ef8ddSDavid Lebrun goto drop; 877891ef8ddSDavid Lebrun 878891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 879891ef8ddSDavid Lebrun goto drop; 880891ef8ddSDavid Lebrun 88120a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 88220a081b7SAndrea Mayer if (seg6_end_dt6_get_mode(slwt) == DT_LEGACY_MODE) 88320a081b7SAndrea Mayer goto legacy_mode; 88420a081b7SAndrea Mayer 88520a081b7SAndrea Mayer /* DT6_VRF_MODE */ 8868b532109SAndrea Mayer skb = end_dt_vrf_core(skb, slwt, AF_INET6); 88720a081b7SAndrea Mayer if (!skb) 88820a081b7SAndrea Mayer /* packet has been processed and consumed by the VRF */ 88920a081b7SAndrea Mayer return 0; 89020a081b7SAndrea Mayer 89120a081b7SAndrea Mayer if (IS_ERR(skb)) 89220a081b7SAndrea Mayer return PTR_ERR(skb); 89320a081b7SAndrea Mayer 89420a081b7SAndrea Mayer /* note: this time we do not need to specify the table because the VRF 89520a081b7SAndrea Mayer * takes care of selecting the correct table. 89620a081b7SAndrea Mayer */ 89720a081b7SAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, 0, true); 89820a081b7SAndrea Mayer 89920a081b7SAndrea Mayer return dst_input(skb); 90020a081b7SAndrea Mayer 90120a081b7SAndrea Mayer legacy_mode: 90220a081b7SAndrea Mayer #endif 903c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 904c71644d0SAndrea Mayer 905fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, slwt->table, true); 906891ef8ddSDavid Lebrun 907891ef8ddSDavid Lebrun return dst_input(skb); 908891ef8ddSDavid Lebrun 909891ef8ddSDavid Lebrun drop: 910891ef8ddSDavid Lebrun kfree_skb(skb); 911891ef8ddSDavid Lebrun return -EINVAL; 912891ef8ddSDavid Lebrun } 913891ef8ddSDavid Lebrun 9148b532109SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 9158b532109SAndrea Mayer static int seg6_end_dt46_build(struct seg6_local_lwt *slwt, const void *cfg, 9168b532109SAndrea Mayer struct netlink_ext_ack *extack) 9178b532109SAndrea Mayer { 9188b532109SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_UNSPEC, extack); 9198b532109SAndrea Mayer } 9208b532109SAndrea Mayer 9218b532109SAndrea Mayer static int input_action_end_dt46(struct sk_buff *skb, 9228b532109SAndrea Mayer struct seg6_local_lwt *slwt) 9238b532109SAndrea Mayer { 9248b532109SAndrea Mayer unsigned int off = 0; 9258b532109SAndrea Mayer int nexthdr; 9268b532109SAndrea Mayer 9278b532109SAndrea Mayer nexthdr = ipv6_find_hdr(skb, &off, -1, NULL, NULL); 9288b532109SAndrea Mayer if (unlikely(nexthdr < 0)) 9298b532109SAndrea Mayer goto drop; 9308b532109SAndrea Mayer 9318b532109SAndrea Mayer switch (nexthdr) { 9328b532109SAndrea Mayer case IPPROTO_IPIP: 9338b532109SAndrea Mayer return input_action_end_dt4(skb, slwt); 9348b532109SAndrea Mayer case IPPROTO_IPV6: 9358b532109SAndrea Mayer return input_action_end_dt6(skb, slwt); 9368b532109SAndrea Mayer } 9378b532109SAndrea Mayer 9388b532109SAndrea Mayer drop: 9398b532109SAndrea Mayer kfree_skb(skb); 9408b532109SAndrea Mayer return -EINVAL; 9418b532109SAndrea Mayer } 9428b532109SAndrea Mayer #endif 9438b532109SAndrea Mayer 944140f04c3SDavid Lebrun /* push an SRH on top of the current one */ 945140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 946140f04c3SDavid Lebrun { 947140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 948140f04c3SDavid Lebrun int err = -EINVAL; 949140f04c3SDavid Lebrun 950140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 951140f04c3SDavid Lebrun if (!srh) 952140f04c3SDavid Lebrun goto drop; 953140f04c3SDavid Lebrun 954140f04c3SDavid Lebrun err = seg6_do_srh_inline(skb, slwt->srh); 955140f04c3SDavid Lebrun if (err) 956140f04c3SDavid Lebrun goto drop; 957140f04c3SDavid Lebrun 958140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 959140f04c3SDavid Lebrun 9601c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 961140f04c3SDavid Lebrun 962140f04c3SDavid Lebrun return dst_input(skb); 963140f04c3SDavid Lebrun 964140f04c3SDavid Lebrun drop: 965140f04c3SDavid Lebrun kfree_skb(skb); 966140f04c3SDavid Lebrun return err; 967140f04c3SDavid Lebrun } 968140f04c3SDavid Lebrun 969140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */ 970140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb, 971140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 972140f04c3SDavid Lebrun { 973140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 974140f04c3SDavid Lebrun int err = -EINVAL; 975140f04c3SDavid Lebrun 976140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 977140f04c3SDavid Lebrun if (!srh) 978140f04c3SDavid Lebrun goto drop; 979140f04c3SDavid Lebrun 980d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 981140f04c3SDavid Lebrun 982140f04c3SDavid Lebrun skb_reset_inner_headers(skb); 983140f04c3SDavid Lebrun skb->encapsulation = 1; 984140f04c3SDavid Lebrun 98532d99d0bSDavid Lebrun err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6); 986140f04c3SDavid Lebrun if (err) 987140f04c3SDavid Lebrun goto drop; 988140f04c3SDavid Lebrun 989140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 990140f04c3SDavid Lebrun 9911c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 992140f04c3SDavid Lebrun 993140f04c3SDavid Lebrun return dst_input(skb); 994140f04c3SDavid Lebrun 995140f04c3SDavid Lebrun drop: 996140f04c3SDavid Lebrun kfree_skb(skb); 997140f04c3SDavid Lebrun return err; 998140f04c3SDavid Lebrun } 999140f04c3SDavid Lebrun 1000fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states); 1001fe94cc29SMathieu Xhonneux 1002486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb) 1003486cdf21SMathieu Xhonneux { 1004486cdf21SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 1005486cdf21SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 1006486cdf21SMathieu Xhonneux struct ipv6_sr_hdr *srh = srh_state->srh; 1007486cdf21SMathieu Xhonneux 1008486cdf21SMathieu Xhonneux if (unlikely(srh == NULL)) 1009486cdf21SMathieu Xhonneux return false; 1010486cdf21SMathieu Xhonneux 1011486cdf21SMathieu Xhonneux if (unlikely(!srh_state->valid)) { 1012486cdf21SMathieu Xhonneux if ((srh_state->hdrlen & 7) != 0) 1013486cdf21SMathieu Xhonneux return false; 1014486cdf21SMathieu Xhonneux 1015486cdf21SMathieu Xhonneux srh->hdrlen = (u8)(srh_state->hdrlen >> 3); 1016bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true)) 1017486cdf21SMathieu Xhonneux return false; 1018486cdf21SMathieu Xhonneux 1019486cdf21SMathieu Xhonneux srh_state->valid = true; 1020486cdf21SMathieu Xhonneux } 1021486cdf21SMathieu Xhonneux 1022486cdf21SMathieu Xhonneux return true; 1023486cdf21SMathieu Xhonneux } 1024486cdf21SMathieu Xhonneux 1025004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb, 1026004d4b27SMathieu Xhonneux struct seg6_local_lwt *slwt) 1027004d4b27SMathieu Xhonneux { 1028004d4b27SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 1029004d4b27SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 1030004d4b27SMathieu Xhonneux struct ipv6_sr_hdr *srh; 1031004d4b27SMathieu Xhonneux int ret; 1032004d4b27SMathieu Xhonneux 1033004d4b27SMathieu Xhonneux srh = get_and_validate_srh(skb); 1034486cdf21SMathieu Xhonneux if (!srh) { 1035486cdf21SMathieu Xhonneux kfree_skb(skb); 1036486cdf21SMathieu Xhonneux return -EINVAL; 1037486cdf21SMathieu Xhonneux } 1038004d4b27SMathieu Xhonneux advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 1039004d4b27SMathieu Xhonneux 1040004d4b27SMathieu Xhonneux /* preempt_disable is needed to protect the per-CPU buffer srh_state, 1041004d4b27SMathieu Xhonneux * which is also accessed by the bpf_lwt_seg6_* helpers 1042004d4b27SMathieu Xhonneux */ 1043004d4b27SMathieu Xhonneux preempt_disable(); 1044486cdf21SMathieu Xhonneux srh_state->srh = srh; 1045004d4b27SMathieu Xhonneux srh_state->hdrlen = srh->hdrlen << 3; 1046486cdf21SMathieu Xhonneux srh_state->valid = true; 1047004d4b27SMathieu Xhonneux 1048004d4b27SMathieu Xhonneux rcu_read_lock(); 1049004d4b27SMathieu Xhonneux bpf_compute_data_pointers(skb); 1050004d4b27SMathieu Xhonneux ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb); 1051004d4b27SMathieu Xhonneux rcu_read_unlock(); 1052004d4b27SMathieu Xhonneux 1053004d4b27SMathieu Xhonneux switch (ret) { 1054004d4b27SMathieu Xhonneux case BPF_OK: 1055004d4b27SMathieu Xhonneux case BPF_REDIRECT: 1056004d4b27SMathieu Xhonneux break; 1057004d4b27SMathieu Xhonneux case BPF_DROP: 1058004d4b27SMathieu Xhonneux goto drop; 1059004d4b27SMathieu Xhonneux default: 1060004d4b27SMathieu Xhonneux pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret); 1061004d4b27SMathieu Xhonneux goto drop; 1062004d4b27SMathieu Xhonneux } 1063004d4b27SMathieu Xhonneux 1064486cdf21SMathieu Xhonneux if (srh_state->srh && !seg6_bpf_has_valid_srh(skb)) 1065004d4b27SMathieu Xhonneux goto drop; 1066004d4b27SMathieu Xhonneux 1067486cdf21SMathieu Xhonneux preempt_enable(); 1068004d4b27SMathieu Xhonneux if (ret != BPF_REDIRECT) 1069004d4b27SMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 1070004d4b27SMathieu Xhonneux 1071004d4b27SMathieu Xhonneux return dst_input(skb); 1072004d4b27SMathieu Xhonneux 1073004d4b27SMathieu Xhonneux drop: 1074486cdf21SMathieu Xhonneux preempt_enable(); 1075004d4b27SMathieu Xhonneux kfree_skb(skb); 1076004d4b27SMathieu Xhonneux return -EINVAL; 1077004d4b27SMathieu Xhonneux } 1078004d4b27SMathieu Xhonneux 1079d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = { 1080d1df6fd8SDavid Lebrun { 1081d1df6fd8SDavid Lebrun .action = SEG6_LOCAL_ACTION_END, 1082d1df6fd8SDavid Lebrun .attrs = 0, 1083848f3c0dSAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS | 1084848f3c0dSAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_FLAVORS), 1085140f04c3SDavid Lebrun .input = input_action_end, 1086d1df6fd8SDavid Lebrun }, 1087140f04c3SDavid Lebrun { 1088140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_X, 1089300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), 109094604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1091140f04c3SDavid Lebrun .input = input_action_end_x, 1092140f04c3SDavid Lebrun }, 1093140f04c3SDavid Lebrun { 1094891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_T, 1095300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE), 109694604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1097891ef8ddSDavid Lebrun .input = input_action_end_t, 1098891ef8ddSDavid Lebrun }, 1099891ef8ddSDavid Lebrun { 1100891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX2, 1101300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_OIF), 110294604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1103891ef8ddSDavid Lebrun .input = input_action_end_dx2, 1104891ef8ddSDavid Lebrun }, 1105891ef8ddSDavid Lebrun { 1106140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX6, 1107300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), 110894604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1109140f04c3SDavid Lebrun .input = input_action_end_dx6, 1110140f04c3SDavid Lebrun }, 1111140f04c3SDavid Lebrun { 1112891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX4, 1113300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH4), 111494604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1115891ef8ddSDavid Lebrun .input = input_action_end_dx4, 1116891ef8ddSDavid Lebrun }, 1117891ef8ddSDavid Lebrun { 1118664d6f86SAndrea Mayer .action = SEG6_LOCAL_ACTION_END_DT4, 1119300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 112094604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1121664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 1122664d6f86SAndrea Mayer .input = input_action_end_dt4, 1123664d6f86SAndrea Mayer .slwt_ops = { 1124664d6f86SAndrea Mayer .build_state = seg6_end_dt4_build, 1125664d6f86SAndrea Mayer }, 1126664d6f86SAndrea Mayer #endif 1127664d6f86SAndrea Mayer }, 1128664d6f86SAndrea Mayer { 1129891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DT6, 113020a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 113120a081b7SAndrea Mayer .attrs = 0, 113294604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS | 113394604548SAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_TABLE) | 1134300a0fd8SAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 113520a081b7SAndrea Mayer .slwt_ops = { 113620a081b7SAndrea Mayer .build_state = seg6_end_dt6_build, 113720a081b7SAndrea Mayer }, 113820a081b7SAndrea Mayer #else 1139300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE), 114094604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 114120a081b7SAndrea Mayer #endif 1142891ef8ddSDavid Lebrun .input = input_action_end_dt6, 1143891ef8ddSDavid Lebrun }, 1144891ef8ddSDavid Lebrun { 11458b532109SAndrea Mayer .action = SEG6_LOCAL_ACTION_END_DT46, 11468b532109SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 11478b532109SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 11488b532109SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 11498b532109SAndrea Mayer .input = input_action_end_dt46, 11508b532109SAndrea Mayer .slwt_ops = { 11518b532109SAndrea Mayer .build_state = seg6_end_dt46_build, 11528b532109SAndrea Mayer }, 11538b532109SAndrea Mayer #endif 11548b532109SAndrea Mayer }, 11558b532109SAndrea Mayer { 1156140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6, 1157300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH), 115894604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1159140f04c3SDavid Lebrun .input = input_action_end_b6, 1160140f04c3SDavid Lebrun }, 1161140f04c3SDavid Lebrun { 1162140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6_ENCAP, 1163300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH), 116494604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1165140f04c3SDavid Lebrun .input = input_action_end_b6_encap, 1166140f04c3SDavid Lebrun .static_headroom = sizeof(struct ipv6hdr), 1167004d4b27SMathieu Xhonneux }, 1168004d4b27SMathieu Xhonneux { 1169004d4b27SMathieu Xhonneux .action = SEG6_LOCAL_ACTION_END_BPF, 1170300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_BPF), 117194604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1172004d4b27SMathieu Xhonneux .input = input_action_end_bpf, 1173004d4b27SMathieu Xhonneux }, 1174004d4b27SMathieu Xhonneux 1175d1df6fd8SDavid Lebrun }; 1176d1df6fd8SDavid Lebrun 1177d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action) 1178d1df6fd8SDavid Lebrun { 1179d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1180d1df6fd8SDavid Lebrun int i, count; 1181d1df6fd8SDavid Lebrun 1182709af180SColin Ian King count = ARRAY_SIZE(seg6_action_table); 1183d1df6fd8SDavid Lebrun for (i = 0; i < count; i++) { 1184d1df6fd8SDavid Lebrun desc = &seg6_action_table[i]; 1185d1df6fd8SDavid Lebrun if (desc->action == action) 1186d1df6fd8SDavid Lebrun return desc; 1187d1df6fd8SDavid Lebrun } 1188d1df6fd8SDavid Lebrun 1189d1df6fd8SDavid Lebrun return NULL; 1190d1df6fd8SDavid Lebrun } 1191d1df6fd8SDavid Lebrun 119294604548SAndrea Mayer static bool seg6_lwtunnel_counters_enabled(struct seg6_local_lwt *slwt) 119394604548SAndrea Mayer { 119494604548SAndrea Mayer return slwt->parsed_optattrs & SEG6_F_LOCAL_COUNTERS; 119594604548SAndrea Mayer } 119694604548SAndrea Mayer 119794604548SAndrea Mayer static void seg6_local_update_counters(struct seg6_local_lwt *slwt, 119894604548SAndrea Mayer unsigned int len, int err) 119994604548SAndrea Mayer { 120094604548SAndrea Mayer struct pcpu_seg6_local_counters *pcounters; 120194604548SAndrea Mayer 120294604548SAndrea Mayer pcounters = this_cpu_ptr(slwt->pcpu_counters); 120394604548SAndrea Mayer u64_stats_update_begin(&pcounters->syncp); 120494604548SAndrea Mayer 120594604548SAndrea Mayer if (likely(!err)) { 120694604548SAndrea Mayer u64_stats_inc(&pcounters->packets); 120794604548SAndrea Mayer u64_stats_add(&pcounters->bytes, len); 120894604548SAndrea Mayer } else { 120994604548SAndrea Mayer u64_stats_inc(&pcounters->errors); 121094604548SAndrea Mayer } 121194604548SAndrea Mayer 121294604548SAndrea Mayer u64_stats_update_end(&pcounters->syncp); 121394604548SAndrea Mayer } 121494604548SAndrea Mayer 12157a3f5b0dSRyoga Saito static int seg6_local_input_core(struct net *net, struct sock *sk, 12167a3f5b0dSRyoga Saito struct sk_buff *skb) 1217d1df6fd8SDavid Lebrun { 1218d1df6fd8SDavid Lebrun struct dst_entry *orig_dst = skb_dst(skb); 1219d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1220d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 122194604548SAndrea Mayer unsigned int len = skb->len; 122294604548SAndrea Mayer int rc; 1223d1df6fd8SDavid Lebrun 1224d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 1225d1df6fd8SDavid Lebrun desc = slwt->desc; 1226d1df6fd8SDavid Lebrun 122794604548SAndrea Mayer rc = desc->input(skb, slwt); 122894604548SAndrea Mayer 122994604548SAndrea Mayer if (!seg6_lwtunnel_counters_enabled(slwt)) 123094604548SAndrea Mayer return rc; 123194604548SAndrea Mayer 123294604548SAndrea Mayer seg6_local_update_counters(slwt, len, rc); 123394604548SAndrea Mayer 123494604548SAndrea Mayer return rc; 1235d1df6fd8SDavid Lebrun } 1236d1df6fd8SDavid Lebrun 12377a3f5b0dSRyoga Saito static int seg6_local_input(struct sk_buff *skb) 12387a3f5b0dSRyoga Saito { 12397a3f5b0dSRyoga Saito if (skb->protocol != htons(ETH_P_IPV6)) { 12407a3f5b0dSRyoga Saito kfree_skb(skb); 12417a3f5b0dSRyoga Saito return -EINVAL; 12427a3f5b0dSRyoga Saito } 12437a3f5b0dSRyoga Saito 12447a3f5b0dSRyoga Saito if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 12457a3f5b0dSRyoga Saito return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN, 12467a3f5b0dSRyoga Saito dev_net(skb->dev), NULL, skb, skb->dev, NULL, 12477a3f5b0dSRyoga Saito seg6_local_input_core); 12487a3f5b0dSRyoga Saito 12497a3f5b0dSRyoga Saito return seg6_local_input_core(dev_net(skb->dev), NULL, skb); 12507a3f5b0dSRyoga Saito } 12517a3f5b0dSRyoga Saito 1252d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { 1253d1df6fd8SDavid Lebrun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, 1254d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, 1255d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, 1256664d6f86SAndrea Mayer [SEG6_LOCAL_VRFTABLE] = { .type = NLA_U32 }, 1257d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, 1258d1df6fd8SDavid Lebrun .len = sizeof(struct in_addr) }, 1259d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, 1260d1df6fd8SDavid Lebrun .len = sizeof(struct in6_addr) }, 1261d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, 1262d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 1263004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .type = NLA_NESTED }, 126494604548SAndrea Mayer [SEG6_LOCAL_COUNTERS] = { .type = NLA_NESTED }, 1265848f3c0dSAndrea Mayer [SEG6_LOCAL_FLAVORS] = { .type = NLA_NESTED }, 1266d1df6fd8SDavid Lebrun }; 1267d1df6fd8SDavid Lebrun 1268e2a8ecc4SAndrea Mayer static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1269e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 12702d9cc60aSDavid Lebrun { 12712d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 12722d9cc60aSDavid Lebrun int len; 12732d9cc60aSDavid Lebrun 12742d9cc60aSDavid Lebrun srh = nla_data(attrs[SEG6_LOCAL_SRH]); 12752d9cc60aSDavid Lebrun len = nla_len(attrs[SEG6_LOCAL_SRH]); 12762d9cc60aSDavid Lebrun 12772d9cc60aSDavid Lebrun /* SRH must contain at least one segment */ 12782d9cc60aSDavid Lebrun if (len < sizeof(*srh) + sizeof(struct in6_addr)) 12792d9cc60aSDavid Lebrun return -EINVAL; 12802d9cc60aSDavid Lebrun 1281bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, false)) 12822d9cc60aSDavid Lebrun return -EINVAL; 12832d9cc60aSDavid Lebrun 12847fa41efaSYueHaibing slwt->srh = kmemdup(srh, len, GFP_KERNEL); 12852d9cc60aSDavid Lebrun if (!slwt->srh) 12862d9cc60aSDavid Lebrun return -ENOMEM; 12872d9cc60aSDavid Lebrun 12882d9cc60aSDavid Lebrun slwt->headroom += len; 12892d9cc60aSDavid Lebrun 12902d9cc60aSDavid Lebrun return 0; 12912d9cc60aSDavid Lebrun } 12922d9cc60aSDavid Lebrun 12932d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt) 12942d9cc60aSDavid Lebrun { 12952d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 12962d9cc60aSDavid Lebrun struct nlattr *nla; 12972d9cc60aSDavid Lebrun int len; 12982d9cc60aSDavid Lebrun 12992d9cc60aSDavid Lebrun srh = slwt->srh; 13002d9cc60aSDavid Lebrun len = (srh->hdrlen + 1) << 3; 13012d9cc60aSDavid Lebrun 13022d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_SRH, len); 13032d9cc60aSDavid Lebrun if (!nla) 13042d9cc60aSDavid Lebrun return -EMSGSIZE; 13052d9cc60aSDavid Lebrun 13062d9cc60aSDavid Lebrun memcpy(nla_data(nla), srh, len); 13072d9cc60aSDavid Lebrun 13082d9cc60aSDavid Lebrun return 0; 13092d9cc60aSDavid Lebrun } 13102d9cc60aSDavid Lebrun 13112d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 13122d9cc60aSDavid Lebrun { 13132d9cc60aSDavid Lebrun int len = (a->srh->hdrlen + 1) << 3; 13142d9cc60aSDavid Lebrun 13152d9cc60aSDavid Lebrun if (len != ((b->srh->hdrlen + 1) << 3)) 13162d9cc60aSDavid Lebrun return 1; 13172d9cc60aSDavid Lebrun 13182d9cc60aSDavid Lebrun return memcmp(a->srh, b->srh, len); 13192d9cc60aSDavid Lebrun } 13202d9cc60aSDavid Lebrun 1321964adce5SAndrea Mayer static void destroy_attr_srh(struct seg6_local_lwt *slwt) 1322964adce5SAndrea Mayer { 1323964adce5SAndrea Mayer kfree(slwt->srh); 1324964adce5SAndrea Mayer } 1325964adce5SAndrea Mayer 1326e2a8ecc4SAndrea Mayer static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1327e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 13282d9cc60aSDavid Lebrun { 13292d9cc60aSDavid Lebrun slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]); 13302d9cc60aSDavid Lebrun 13312d9cc60aSDavid Lebrun return 0; 13322d9cc60aSDavid Lebrun } 13332d9cc60aSDavid Lebrun 13342d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt) 13352d9cc60aSDavid Lebrun { 13362d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table)) 13372d9cc60aSDavid Lebrun return -EMSGSIZE; 13382d9cc60aSDavid Lebrun 13392d9cc60aSDavid Lebrun return 0; 13402d9cc60aSDavid Lebrun } 13412d9cc60aSDavid Lebrun 13422d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 13432d9cc60aSDavid Lebrun { 13442d9cc60aSDavid Lebrun if (a->table != b->table) 13452d9cc60aSDavid Lebrun return 1; 13462d9cc60aSDavid Lebrun 13472d9cc60aSDavid Lebrun return 0; 13482d9cc60aSDavid Lebrun } 13492d9cc60aSDavid Lebrun 1350664d6f86SAndrea Mayer static struct 1351664d6f86SAndrea Mayer seg6_end_dt_info *seg6_possible_end_dt_info(struct seg6_local_lwt *slwt) 1352664d6f86SAndrea Mayer { 1353664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 1354664d6f86SAndrea Mayer return &slwt->dt_info; 1355664d6f86SAndrea Mayer #else 1356664d6f86SAndrea Mayer return ERR_PTR(-EOPNOTSUPP); 1357664d6f86SAndrea Mayer #endif 1358664d6f86SAndrea Mayer } 1359664d6f86SAndrea Mayer 1360664d6f86SAndrea Mayer static int parse_nla_vrftable(struct nlattr **attrs, 1361e2a8ecc4SAndrea Mayer struct seg6_local_lwt *slwt, 1362e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 1363664d6f86SAndrea Mayer { 1364664d6f86SAndrea Mayer struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt); 1365664d6f86SAndrea Mayer 1366664d6f86SAndrea Mayer if (IS_ERR(info)) 1367664d6f86SAndrea Mayer return PTR_ERR(info); 1368664d6f86SAndrea Mayer 1369664d6f86SAndrea Mayer info->vrf_table = nla_get_u32(attrs[SEG6_LOCAL_VRFTABLE]); 1370664d6f86SAndrea Mayer 1371664d6f86SAndrea Mayer return 0; 1372664d6f86SAndrea Mayer } 1373664d6f86SAndrea Mayer 1374664d6f86SAndrea Mayer static int put_nla_vrftable(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1375664d6f86SAndrea Mayer { 1376664d6f86SAndrea Mayer struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt); 1377664d6f86SAndrea Mayer 1378664d6f86SAndrea Mayer if (IS_ERR(info)) 1379664d6f86SAndrea Mayer return PTR_ERR(info); 1380664d6f86SAndrea Mayer 1381664d6f86SAndrea Mayer if (nla_put_u32(skb, SEG6_LOCAL_VRFTABLE, info->vrf_table)) 1382664d6f86SAndrea Mayer return -EMSGSIZE; 1383664d6f86SAndrea Mayer 1384664d6f86SAndrea Mayer return 0; 1385664d6f86SAndrea Mayer } 1386664d6f86SAndrea Mayer 1387664d6f86SAndrea Mayer static int cmp_nla_vrftable(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1388664d6f86SAndrea Mayer { 1389664d6f86SAndrea Mayer struct seg6_end_dt_info *info_a = seg6_possible_end_dt_info(a); 1390664d6f86SAndrea Mayer struct seg6_end_dt_info *info_b = seg6_possible_end_dt_info(b); 1391664d6f86SAndrea Mayer 1392664d6f86SAndrea Mayer if (info_a->vrf_table != info_b->vrf_table) 1393664d6f86SAndrea Mayer return 1; 1394664d6f86SAndrea Mayer 1395664d6f86SAndrea Mayer return 0; 1396664d6f86SAndrea Mayer } 1397664d6f86SAndrea Mayer 1398e2a8ecc4SAndrea Mayer static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1399e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 14002d9cc60aSDavid Lebrun { 14012d9cc60aSDavid Lebrun memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]), 14022d9cc60aSDavid Lebrun sizeof(struct in_addr)); 14032d9cc60aSDavid Lebrun 14042d9cc60aSDavid Lebrun return 0; 14052d9cc60aSDavid Lebrun } 14062d9cc60aSDavid Lebrun 14072d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt) 14082d9cc60aSDavid Lebrun { 14092d9cc60aSDavid Lebrun struct nlattr *nla; 14102d9cc60aSDavid Lebrun 14112d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr)); 14122d9cc60aSDavid Lebrun if (!nla) 14132d9cc60aSDavid Lebrun return -EMSGSIZE; 14142d9cc60aSDavid Lebrun 14152d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr)); 14162d9cc60aSDavid Lebrun 14172d9cc60aSDavid Lebrun return 0; 14182d9cc60aSDavid Lebrun } 14192d9cc60aSDavid Lebrun 14202d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 14212d9cc60aSDavid Lebrun { 14222d9cc60aSDavid Lebrun return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr)); 14232d9cc60aSDavid Lebrun } 14242d9cc60aSDavid Lebrun 1425e2a8ecc4SAndrea Mayer static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1426e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 14272d9cc60aSDavid Lebrun { 14282d9cc60aSDavid Lebrun memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]), 14292d9cc60aSDavid Lebrun sizeof(struct in6_addr)); 14302d9cc60aSDavid Lebrun 14312d9cc60aSDavid Lebrun return 0; 14322d9cc60aSDavid Lebrun } 14332d9cc60aSDavid Lebrun 14342d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 14352d9cc60aSDavid Lebrun { 14362d9cc60aSDavid Lebrun struct nlattr *nla; 14372d9cc60aSDavid Lebrun 14382d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr)); 14392d9cc60aSDavid Lebrun if (!nla) 14402d9cc60aSDavid Lebrun return -EMSGSIZE; 14412d9cc60aSDavid Lebrun 14422d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr)); 14432d9cc60aSDavid Lebrun 14442d9cc60aSDavid Lebrun return 0; 14452d9cc60aSDavid Lebrun } 14462d9cc60aSDavid Lebrun 14472d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 14482d9cc60aSDavid Lebrun { 14492d9cc60aSDavid Lebrun return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr)); 14502d9cc60aSDavid Lebrun } 14512d9cc60aSDavid Lebrun 1452e2a8ecc4SAndrea Mayer static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1453e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 14542d9cc60aSDavid Lebrun { 14552d9cc60aSDavid Lebrun slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]); 14562d9cc60aSDavid Lebrun 14572d9cc60aSDavid Lebrun return 0; 14582d9cc60aSDavid Lebrun } 14592d9cc60aSDavid Lebrun 14602d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 14612d9cc60aSDavid Lebrun { 14622d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif)) 14632d9cc60aSDavid Lebrun return -EMSGSIZE; 14642d9cc60aSDavid Lebrun 14652d9cc60aSDavid Lebrun return 0; 14662d9cc60aSDavid Lebrun } 14672d9cc60aSDavid Lebrun 14682d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 14692d9cc60aSDavid Lebrun { 14702d9cc60aSDavid Lebrun if (a->iif != b->iif) 14712d9cc60aSDavid Lebrun return 1; 14722d9cc60aSDavid Lebrun 14732d9cc60aSDavid Lebrun return 0; 14742d9cc60aSDavid Lebrun } 14752d9cc60aSDavid Lebrun 1476e2a8ecc4SAndrea Mayer static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1477e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 14782d9cc60aSDavid Lebrun { 14792d9cc60aSDavid Lebrun slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]); 14802d9cc60aSDavid Lebrun 14812d9cc60aSDavid Lebrun return 0; 14822d9cc60aSDavid Lebrun } 14832d9cc60aSDavid Lebrun 14842d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 14852d9cc60aSDavid Lebrun { 14862d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif)) 14872d9cc60aSDavid Lebrun return -EMSGSIZE; 14882d9cc60aSDavid Lebrun 14892d9cc60aSDavid Lebrun return 0; 14902d9cc60aSDavid Lebrun } 14912d9cc60aSDavid Lebrun 14922d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 14932d9cc60aSDavid Lebrun { 14942d9cc60aSDavid Lebrun if (a->oif != b->oif) 14952d9cc60aSDavid Lebrun return 1; 14962d9cc60aSDavid Lebrun 14972d9cc60aSDavid Lebrun return 0; 14982d9cc60aSDavid Lebrun } 14992d9cc60aSDavid Lebrun 1500004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256 1501004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = { 1502004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG] = { .type = NLA_U32, }, 1503004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING, 1504004d4b27SMathieu Xhonneux .len = MAX_PROG_NAME }, 1505004d4b27SMathieu Xhonneux }; 1506004d4b27SMathieu Xhonneux 1507e2a8ecc4SAndrea Mayer static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1508e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 1509004d4b27SMathieu Xhonneux { 1510004d4b27SMathieu Xhonneux struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1]; 1511004d4b27SMathieu Xhonneux struct bpf_prog *p; 1512004d4b27SMathieu Xhonneux int ret; 1513004d4b27SMathieu Xhonneux u32 fd; 1514004d4b27SMathieu Xhonneux 15158cb08174SJohannes Berg ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX, 15168cb08174SJohannes Berg attrs[SEG6_LOCAL_BPF], 15178cb08174SJohannes Berg bpf_prog_policy, NULL); 1518004d4b27SMathieu Xhonneux if (ret < 0) 1519004d4b27SMathieu Xhonneux return ret; 1520004d4b27SMathieu Xhonneux 1521004d4b27SMathieu Xhonneux if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME]) 1522004d4b27SMathieu Xhonneux return -EINVAL; 1523004d4b27SMathieu Xhonneux 1524004d4b27SMathieu Xhonneux slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL); 1525004d4b27SMathieu Xhonneux if (!slwt->bpf.name) 1526004d4b27SMathieu Xhonneux return -ENOMEM; 1527004d4b27SMathieu Xhonneux 1528004d4b27SMathieu Xhonneux fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]); 1529004d4b27SMathieu Xhonneux p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL); 1530004d4b27SMathieu Xhonneux if (IS_ERR(p)) { 1531004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 1532004d4b27SMathieu Xhonneux return PTR_ERR(p); 1533004d4b27SMathieu Xhonneux } 1534004d4b27SMathieu Xhonneux 1535004d4b27SMathieu Xhonneux slwt->bpf.prog = p; 1536004d4b27SMathieu Xhonneux return 0; 1537004d4b27SMathieu Xhonneux } 1538004d4b27SMathieu Xhonneux 1539004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1540004d4b27SMathieu Xhonneux { 1541004d4b27SMathieu Xhonneux struct nlattr *nest; 1542004d4b27SMathieu Xhonneux 1543004d4b27SMathieu Xhonneux if (!slwt->bpf.prog) 1544004d4b27SMathieu Xhonneux return 0; 1545004d4b27SMathieu Xhonneux 1546ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF); 1547004d4b27SMathieu Xhonneux if (!nest) 1548004d4b27SMathieu Xhonneux return -EMSGSIZE; 1549004d4b27SMathieu Xhonneux 1550004d4b27SMathieu Xhonneux if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id)) 1551004d4b27SMathieu Xhonneux return -EMSGSIZE; 1552004d4b27SMathieu Xhonneux 1553004d4b27SMathieu Xhonneux if (slwt->bpf.name && 1554004d4b27SMathieu Xhonneux nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name)) 1555004d4b27SMathieu Xhonneux return -EMSGSIZE; 1556004d4b27SMathieu Xhonneux 1557004d4b27SMathieu Xhonneux return nla_nest_end(skb, nest); 1558004d4b27SMathieu Xhonneux } 1559004d4b27SMathieu Xhonneux 1560004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1561004d4b27SMathieu Xhonneux { 1562004d4b27SMathieu Xhonneux if (!a->bpf.name && !b->bpf.name) 1563004d4b27SMathieu Xhonneux return 0; 1564004d4b27SMathieu Xhonneux 1565004d4b27SMathieu Xhonneux if (!a->bpf.name || !b->bpf.name) 1566004d4b27SMathieu Xhonneux return 1; 1567004d4b27SMathieu Xhonneux 1568004d4b27SMathieu Xhonneux return strcmp(a->bpf.name, b->bpf.name); 1569004d4b27SMathieu Xhonneux } 1570004d4b27SMathieu Xhonneux 1571964adce5SAndrea Mayer static void destroy_attr_bpf(struct seg6_local_lwt *slwt) 1572964adce5SAndrea Mayer { 1573964adce5SAndrea Mayer kfree(slwt->bpf.name); 1574964adce5SAndrea Mayer if (slwt->bpf.prog) 1575964adce5SAndrea Mayer bpf_prog_put(slwt->bpf.prog); 1576964adce5SAndrea Mayer } 1577964adce5SAndrea Mayer 157894604548SAndrea Mayer static const struct 157994604548SAndrea Mayer nla_policy seg6_local_counters_policy[SEG6_LOCAL_CNT_MAX + 1] = { 158094604548SAndrea Mayer [SEG6_LOCAL_CNT_PACKETS] = { .type = NLA_U64 }, 158194604548SAndrea Mayer [SEG6_LOCAL_CNT_BYTES] = { .type = NLA_U64 }, 158294604548SAndrea Mayer [SEG6_LOCAL_CNT_ERRORS] = { .type = NLA_U64 }, 158394604548SAndrea Mayer }; 158494604548SAndrea Mayer 158594604548SAndrea Mayer static int parse_nla_counters(struct nlattr **attrs, 1586e2a8ecc4SAndrea Mayer struct seg6_local_lwt *slwt, 1587e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 158894604548SAndrea Mayer { 158994604548SAndrea Mayer struct pcpu_seg6_local_counters __percpu *pcounters; 159094604548SAndrea Mayer struct nlattr *tb[SEG6_LOCAL_CNT_MAX + 1]; 159194604548SAndrea Mayer int ret; 159294604548SAndrea Mayer 159394604548SAndrea Mayer ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_CNT_MAX, 159494604548SAndrea Mayer attrs[SEG6_LOCAL_COUNTERS], 159594604548SAndrea Mayer seg6_local_counters_policy, NULL); 159694604548SAndrea Mayer if (ret < 0) 159794604548SAndrea Mayer return ret; 159894604548SAndrea Mayer 159994604548SAndrea Mayer /* basic support for SRv6 Behavior counters requires at least: 160094604548SAndrea Mayer * packets, bytes and errors. 160194604548SAndrea Mayer */ 160294604548SAndrea Mayer if (!tb[SEG6_LOCAL_CNT_PACKETS] || !tb[SEG6_LOCAL_CNT_BYTES] || 160394604548SAndrea Mayer !tb[SEG6_LOCAL_CNT_ERRORS]) 160494604548SAndrea Mayer return -EINVAL; 160594604548SAndrea Mayer 160694604548SAndrea Mayer /* counters are always zero initialized */ 160794604548SAndrea Mayer pcounters = seg6_local_alloc_pcpu_counters(GFP_KERNEL); 160894604548SAndrea Mayer if (!pcounters) 160994604548SAndrea Mayer return -ENOMEM; 161094604548SAndrea Mayer 161194604548SAndrea Mayer slwt->pcpu_counters = pcounters; 161294604548SAndrea Mayer 161394604548SAndrea Mayer return 0; 161494604548SAndrea Mayer } 161594604548SAndrea Mayer 161694604548SAndrea Mayer static int seg6_local_fill_nla_counters(struct sk_buff *skb, 161794604548SAndrea Mayer struct seg6_local_counters *counters) 161894604548SAndrea Mayer { 161994604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_PACKETS, counters->packets, 162094604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 162194604548SAndrea Mayer return -EMSGSIZE; 162294604548SAndrea Mayer 162394604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_BYTES, counters->bytes, 162494604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 162594604548SAndrea Mayer return -EMSGSIZE; 162694604548SAndrea Mayer 162794604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_ERRORS, counters->errors, 162894604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 162994604548SAndrea Mayer return -EMSGSIZE; 163094604548SAndrea Mayer 163194604548SAndrea Mayer return 0; 163294604548SAndrea Mayer } 163394604548SAndrea Mayer 163494604548SAndrea Mayer static int put_nla_counters(struct sk_buff *skb, struct seg6_local_lwt *slwt) 163594604548SAndrea Mayer { 163694604548SAndrea Mayer struct seg6_local_counters counters = { 0, 0, 0 }; 163794604548SAndrea Mayer struct nlattr *nest; 163894604548SAndrea Mayer int rc, i; 163994604548SAndrea Mayer 164094604548SAndrea Mayer nest = nla_nest_start(skb, SEG6_LOCAL_COUNTERS); 164194604548SAndrea Mayer if (!nest) 164294604548SAndrea Mayer return -EMSGSIZE; 164394604548SAndrea Mayer 164494604548SAndrea Mayer for_each_possible_cpu(i) { 164594604548SAndrea Mayer struct pcpu_seg6_local_counters *pcounters; 164694604548SAndrea Mayer u64 packets, bytes, errors; 164794604548SAndrea Mayer unsigned int start; 164894604548SAndrea Mayer 164994604548SAndrea Mayer pcounters = per_cpu_ptr(slwt->pcpu_counters, i); 165094604548SAndrea Mayer do { 1651d120d1a6SThomas Gleixner start = u64_stats_fetch_begin(&pcounters->syncp); 165294604548SAndrea Mayer 165394604548SAndrea Mayer packets = u64_stats_read(&pcounters->packets); 165494604548SAndrea Mayer bytes = u64_stats_read(&pcounters->bytes); 165594604548SAndrea Mayer errors = u64_stats_read(&pcounters->errors); 165694604548SAndrea Mayer 1657d120d1a6SThomas Gleixner } while (u64_stats_fetch_retry(&pcounters->syncp, start)); 165894604548SAndrea Mayer 165994604548SAndrea Mayer counters.packets += packets; 166094604548SAndrea Mayer counters.bytes += bytes; 166194604548SAndrea Mayer counters.errors += errors; 166294604548SAndrea Mayer } 166394604548SAndrea Mayer 166494604548SAndrea Mayer rc = seg6_local_fill_nla_counters(skb, &counters); 166594604548SAndrea Mayer if (rc < 0) { 166694604548SAndrea Mayer nla_nest_cancel(skb, nest); 166794604548SAndrea Mayer return rc; 166894604548SAndrea Mayer } 166994604548SAndrea Mayer 167094604548SAndrea Mayer return nla_nest_end(skb, nest); 167194604548SAndrea Mayer } 167294604548SAndrea Mayer 167394604548SAndrea Mayer static int cmp_nla_counters(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 167494604548SAndrea Mayer { 167594604548SAndrea Mayer /* a and b are equal if both have pcpu_counters set or not */ 167694604548SAndrea Mayer return (!!((unsigned long)a->pcpu_counters)) ^ 167794604548SAndrea Mayer (!!((unsigned long)b->pcpu_counters)); 167894604548SAndrea Mayer } 167994604548SAndrea Mayer 168094604548SAndrea Mayer static void destroy_attr_counters(struct seg6_local_lwt *slwt) 168194604548SAndrea Mayer { 168294604548SAndrea Mayer free_percpu(slwt->pcpu_counters); 168394604548SAndrea Mayer } 168494604548SAndrea Mayer 1685848f3c0dSAndrea Mayer static const 1686848f3c0dSAndrea Mayer struct nla_policy seg6_local_flavors_policy[SEG6_LOCAL_FLV_MAX + 1] = { 1687848f3c0dSAndrea Mayer [SEG6_LOCAL_FLV_OPERATION] = { .type = NLA_U32 }, 1688848f3c0dSAndrea Mayer [SEG6_LOCAL_FLV_LCBLOCK_BITS] = { .type = NLA_U8 }, 1689848f3c0dSAndrea Mayer [SEG6_LOCAL_FLV_LCNODE_FN_BITS] = { .type = NLA_U8 }, 1690848f3c0dSAndrea Mayer }; 1691848f3c0dSAndrea Mayer 1692848f3c0dSAndrea Mayer /* check whether the lengths of the Locator-Block and Locator-Node Function 1693848f3c0dSAndrea Mayer * are compatible with the dimension of a C-SID container. 1694848f3c0dSAndrea Mayer */ 1695848f3c0dSAndrea Mayer static int seg6_chk_next_csid_cfg(__u8 block_len, __u8 func_len) 1696848f3c0dSAndrea Mayer { 1697848f3c0dSAndrea Mayer /* Locator-Block and Locator-Node Function cannot exceed 128 bits 1698848f3c0dSAndrea Mayer * (i.e. C-SID container lenghts). 1699848f3c0dSAndrea Mayer */ 1700848f3c0dSAndrea Mayer if (next_csid_chk_cntr_bits(block_len, func_len)) 1701848f3c0dSAndrea Mayer return -EINVAL; 1702848f3c0dSAndrea Mayer 1703848f3c0dSAndrea Mayer /* Locator-Block length must be greater than zero and evenly divisible 1704848f3c0dSAndrea Mayer * by 8. There must be room for a Locator-Node Function, at least. 1705848f3c0dSAndrea Mayer */ 1706848f3c0dSAndrea Mayer if (next_csid_chk_lcblock_bits(block_len)) 1707848f3c0dSAndrea Mayer return -EINVAL; 1708848f3c0dSAndrea Mayer 1709848f3c0dSAndrea Mayer /* Locator-Node Function length must be greater than zero and evenly 1710848f3c0dSAndrea Mayer * divisible by 8. There must be room for the Locator-Block. 1711848f3c0dSAndrea Mayer */ 1712848f3c0dSAndrea Mayer if (next_csid_chk_lcnode_fn_bits(func_len)) 1713848f3c0dSAndrea Mayer return -EINVAL; 1714848f3c0dSAndrea Mayer 1715848f3c0dSAndrea Mayer return 0; 1716848f3c0dSAndrea Mayer } 1717848f3c0dSAndrea Mayer 1718848f3c0dSAndrea Mayer static int seg6_parse_nla_next_csid_cfg(struct nlattr **tb, 1719848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo, 1720848f3c0dSAndrea Mayer struct netlink_ext_ack *extack) 1721848f3c0dSAndrea Mayer { 1722848f3c0dSAndrea Mayer __u8 func_len = SEG6_LOCAL_LCNODE_FN_DBITS; 1723848f3c0dSAndrea Mayer __u8 block_len = SEG6_LOCAL_LCBLOCK_DBITS; 1724848f3c0dSAndrea Mayer int rc; 1725848f3c0dSAndrea Mayer 1726848f3c0dSAndrea Mayer if (tb[SEG6_LOCAL_FLV_LCBLOCK_BITS]) 1727848f3c0dSAndrea Mayer block_len = nla_get_u8(tb[SEG6_LOCAL_FLV_LCBLOCK_BITS]); 1728848f3c0dSAndrea Mayer 1729848f3c0dSAndrea Mayer if (tb[SEG6_LOCAL_FLV_LCNODE_FN_BITS]) 1730848f3c0dSAndrea Mayer func_len = nla_get_u8(tb[SEG6_LOCAL_FLV_LCNODE_FN_BITS]); 1731848f3c0dSAndrea Mayer 1732848f3c0dSAndrea Mayer rc = seg6_chk_next_csid_cfg(block_len, func_len); 1733848f3c0dSAndrea Mayer if (rc < 0) { 1734848f3c0dSAndrea Mayer NL_SET_ERR_MSG(extack, 1735848f3c0dSAndrea Mayer "Invalid Locator Block/Node Function lengths"); 1736848f3c0dSAndrea Mayer return rc; 1737848f3c0dSAndrea Mayer } 1738848f3c0dSAndrea Mayer 1739848f3c0dSAndrea Mayer finfo->lcblock_bits = block_len; 1740848f3c0dSAndrea Mayer finfo->lcnode_func_bits = func_len; 1741848f3c0dSAndrea Mayer 1742848f3c0dSAndrea Mayer return 0; 1743848f3c0dSAndrea Mayer } 1744848f3c0dSAndrea Mayer 1745848f3c0dSAndrea Mayer static int parse_nla_flavors(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1746848f3c0dSAndrea Mayer struct netlink_ext_ack *extack) 1747848f3c0dSAndrea Mayer { 1748848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo = &slwt->flv_info; 1749848f3c0dSAndrea Mayer struct nlattr *tb[SEG6_LOCAL_FLV_MAX + 1]; 1750848f3c0dSAndrea Mayer unsigned long fops; 1751848f3c0dSAndrea Mayer int rc; 1752848f3c0dSAndrea Mayer 1753848f3c0dSAndrea Mayer rc = nla_parse_nested_deprecated(tb, SEG6_LOCAL_FLV_MAX, 1754848f3c0dSAndrea Mayer attrs[SEG6_LOCAL_FLAVORS], 1755848f3c0dSAndrea Mayer seg6_local_flavors_policy, NULL); 1756848f3c0dSAndrea Mayer if (rc < 0) 1757848f3c0dSAndrea Mayer return rc; 1758848f3c0dSAndrea Mayer 1759848f3c0dSAndrea Mayer /* this attribute MUST always be present since it represents the Flavor 1760848f3c0dSAndrea Mayer * operation(s) to be carried out. 1761848f3c0dSAndrea Mayer */ 1762848f3c0dSAndrea Mayer if (!tb[SEG6_LOCAL_FLV_OPERATION]) 1763848f3c0dSAndrea Mayer return -EINVAL; 1764848f3c0dSAndrea Mayer 1765848f3c0dSAndrea Mayer fops = nla_get_u32(tb[SEG6_LOCAL_FLV_OPERATION]); 1766848f3c0dSAndrea Mayer if (fops & ~SEG6_LOCAL_FLV_SUPP_OPS) { 1767848f3c0dSAndrea Mayer NL_SET_ERR_MSG(extack, "Unsupported Flavor operation(s)"); 1768848f3c0dSAndrea Mayer return -EOPNOTSUPP; 1769848f3c0dSAndrea Mayer } 1770848f3c0dSAndrea Mayer 1771848f3c0dSAndrea Mayer finfo->flv_ops = fops; 1772848f3c0dSAndrea Mayer 1773848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(fops)) { 1774848f3c0dSAndrea Mayer /* Locator-Block and Locator-Node Function lengths can be 1775848f3c0dSAndrea Mayer * provided by the user space. Otherwise, default values are 1776848f3c0dSAndrea Mayer * applied. 1777848f3c0dSAndrea Mayer */ 1778848f3c0dSAndrea Mayer rc = seg6_parse_nla_next_csid_cfg(tb, finfo, extack); 1779848f3c0dSAndrea Mayer if (rc < 0) 1780848f3c0dSAndrea Mayer return rc; 1781848f3c0dSAndrea Mayer } 1782848f3c0dSAndrea Mayer 1783848f3c0dSAndrea Mayer return 0; 1784848f3c0dSAndrea Mayer } 1785848f3c0dSAndrea Mayer 1786848f3c0dSAndrea Mayer static int seg6_fill_nla_next_csid_cfg(struct sk_buff *skb, 1787848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo) 1788848f3c0dSAndrea Mayer { 1789848f3c0dSAndrea Mayer if (nla_put_u8(skb, SEG6_LOCAL_FLV_LCBLOCK_BITS, finfo->lcblock_bits)) 1790848f3c0dSAndrea Mayer return -EMSGSIZE; 1791848f3c0dSAndrea Mayer 1792848f3c0dSAndrea Mayer if (nla_put_u8(skb, SEG6_LOCAL_FLV_LCNODE_FN_BITS, 1793848f3c0dSAndrea Mayer finfo->lcnode_func_bits)) 1794848f3c0dSAndrea Mayer return -EMSGSIZE; 1795848f3c0dSAndrea Mayer 1796848f3c0dSAndrea Mayer return 0; 1797848f3c0dSAndrea Mayer } 1798848f3c0dSAndrea Mayer 1799848f3c0dSAndrea Mayer static int put_nla_flavors(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1800848f3c0dSAndrea Mayer { 1801848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo = &slwt->flv_info; 1802848f3c0dSAndrea Mayer __u32 fops = finfo->flv_ops; 1803848f3c0dSAndrea Mayer struct nlattr *nest; 1804848f3c0dSAndrea Mayer int rc; 1805848f3c0dSAndrea Mayer 1806848f3c0dSAndrea Mayer nest = nla_nest_start(skb, SEG6_LOCAL_FLAVORS); 1807848f3c0dSAndrea Mayer if (!nest) 1808848f3c0dSAndrea Mayer return -EMSGSIZE; 1809848f3c0dSAndrea Mayer 1810848f3c0dSAndrea Mayer if (nla_put_u32(skb, SEG6_LOCAL_FLV_OPERATION, fops)) { 1811848f3c0dSAndrea Mayer rc = -EMSGSIZE; 1812848f3c0dSAndrea Mayer goto err; 1813848f3c0dSAndrea Mayer } 1814848f3c0dSAndrea Mayer 1815848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(fops)) { 1816848f3c0dSAndrea Mayer rc = seg6_fill_nla_next_csid_cfg(skb, finfo); 1817848f3c0dSAndrea Mayer if (rc < 0) 1818848f3c0dSAndrea Mayer goto err; 1819848f3c0dSAndrea Mayer } 1820848f3c0dSAndrea Mayer 1821848f3c0dSAndrea Mayer return nla_nest_end(skb, nest); 1822848f3c0dSAndrea Mayer 1823848f3c0dSAndrea Mayer err: 1824848f3c0dSAndrea Mayer nla_nest_cancel(skb, nest); 1825848f3c0dSAndrea Mayer return rc; 1826848f3c0dSAndrea Mayer } 1827848f3c0dSAndrea Mayer 1828848f3c0dSAndrea Mayer static int seg6_cmp_nla_next_csid_cfg(struct seg6_flavors_info *finfo_a, 1829848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo_b) 1830848f3c0dSAndrea Mayer { 1831848f3c0dSAndrea Mayer if (finfo_a->lcblock_bits != finfo_b->lcblock_bits) 1832848f3c0dSAndrea Mayer return 1; 1833848f3c0dSAndrea Mayer 1834848f3c0dSAndrea Mayer if (finfo_a->lcnode_func_bits != finfo_b->lcnode_func_bits) 1835848f3c0dSAndrea Mayer return 1; 1836848f3c0dSAndrea Mayer 1837848f3c0dSAndrea Mayer return 0; 1838848f3c0dSAndrea Mayer } 1839848f3c0dSAndrea Mayer 1840848f3c0dSAndrea Mayer static int cmp_nla_flavors(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1841848f3c0dSAndrea Mayer { 1842848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo_a = &a->flv_info; 1843848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo_b = &b->flv_info; 1844848f3c0dSAndrea Mayer 1845848f3c0dSAndrea Mayer if (finfo_a->flv_ops != finfo_b->flv_ops) 1846848f3c0dSAndrea Mayer return 1; 1847848f3c0dSAndrea Mayer 1848848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(finfo_a->flv_ops)) { 1849848f3c0dSAndrea Mayer if (seg6_cmp_nla_next_csid_cfg(finfo_a, finfo_b)) 1850848f3c0dSAndrea Mayer return 1; 1851848f3c0dSAndrea Mayer } 1852848f3c0dSAndrea Mayer 1853848f3c0dSAndrea Mayer return 0; 1854848f3c0dSAndrea Mayer } 1855848f3c0dSAndrea Mayer 1856848f3c0dSAndrea Mayer static int encap_size_flavors(struct seg6_local_lwt *slwt) 1857848f3c0dSAndrea Mayer { 1858848f3c0dSAndrea Mayer struct seg6_flavors_info *finfo = &slwt->flv_info; 1859848f3c0dSAndrea Mayer int nlsize; 1860848f3c0dSAndrea Mayer 1861848f3c0dSAndrea Mayer nlsize = nla_total_size(0) + /* nest SEG6_LOCAL_FLAVORS */ 1862848f3c0dSAndrea Mayer nla_total_size(4); /* SEG6_LOCAL_FLV_OPERATION */ 1863848f3c0dSAndrea Mayer 1864848f3c0dSAndrea Mayer if (seg6_next_csid_enabled(finfo->flv_ops)) 1865848f3c0dSAndrea Mayer nlsize += nla_total_size(1) + /* SEG6_LOCAL_FLV_LCBLOCK_BITS */ 1866848f3c0dSAndrea Mayer nla_total_size(1); /* SEG6_LOCAL_FLV_LCNODE_FN_BITS */ 1867848f3c0dSAndrea Mayer 1868848f3c0dSAndrea Mayer return nlsize; 1869848f3c0dSAndrea Mayer } 1870848f3c0dSAndrea Mayer 1871d1df6fd8SDavid Lebrun struct seg6_action_param { 1872e2a8ecc4SAndrea Mayer int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt, 1873e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack); 1874d1df6fd8SDavid Lebrun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 1875d1df6fd8SDavid Lebrun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); 1876964adce5SAndrea Mayer 1877964adce5SAndrea Mayer /* optional destroy() callback useful for releasing resources which 1878964adce5SAndrea Mayer * have been previously acquired in the corresponding parse() 1879964adce5SAndrea Mayer * function. 1880964adce5SAndrea Mayer */ 1881964adce5SAndrea Mayer void (*destroy)(struct seg6_local_lwt *slwt); 1882d1df6fd8SDavid Lebrun }; 1883d1df6fd8SDavid Lebrun 1884d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { 18852d9cc60aSDavid Lebrun [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh, 18862d9cc60aSDavid Lebrun .put = put_nla_srh, 1887964adce5SAndrea Mayer .cmp = cmp_nla_srh, 1888964adce5SAndrea Mayer .destroy = destroy_attr_srh }, 1889d1df6fd8SDavid Lebrun 18902d9cc60aSDavid Lebrun [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table, 18912d9cc60aSDavid Lebrun .put = put_nla_table, 18922d9cc60aSDavid Lebrun .cmp = cmp_nla_table }, 1893d1df6fd8SDavid Lebrun 18942d9cc60aSDavid Lebrun [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4, 18952d9cc60aSDavid Lebrun .put = put_nla_nh4, 18962d9cc60aSDavid Lebrun .cmp = cmp_nla_nh4 }, 1897d1df6fd8SDavid Lebrun 18982d9cc60aSDavid Lebrun [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6, 18992d9cc60aSDavid Lebrun .put = put_nla_nh6, 19002d9cc60aSDavid Lebrun .cmp = cmp_nla_nh6 }, 1901d1df6fd8SDavid Lebrun 19022d9cc60aSDavid Lebrun [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif, 19032d9cc60aSDavid Lebrun .put = put_nla_iif, 19042d9cc60aSDavid Lebrun .cmp = cmp_nla_iif }, 1905d1df6fd8SDavid Lebrun 19062d9cc60aSDavid Lebrun [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif, 19072d9cc60aSDavid Lebrun .put = put_nla_oif, 19082d9cc60aSDavid Lebrun .cmp = cmp_nla_oif }, 1909004d4b27SMathieu Xhonneux 1910004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .parse = parse_nla_bpf, 1911004d4b27SMathieu Xhonneux .put = put_nla_bpf, 1912964adce5SAndrea Mayer .cmp = cmp_nla_bpf, 1913964adce5SAndrea Mayer .destroy = destroy_attr_bpf }, 1914004d4b27SMathieu Xhonneux 1915664d6f86SAndrea Mayer [SEG6_LOCAL_VRFTABLE] = { .parse = parse_nla_vrftable, 1916664d6f86SAndrea Mayer .put = put_nla_vrftable, 1917664d6f86SAndrea Mayer .cmp = cmp_nla_vrftable }, 1918664d6f86SAndrea Mayer 191994604548SAndrea Mayer [SEG6_LOCAL_COUNTERS] = { .parse = parse_nla_counters, 192094604548SAndrea Mayer .put = put_nla_counters, 192194604548SAndrea Mayer .cmp = cmp_nla_counters, 192294604548SAndrea Mayer .destroy = destroy_attr_counters }, 1923848f3c0dSAndrea Mayer 1924848f3c0dSAndrea Mayer [SEG6_LOCAL_FLAVORS] = { .parse = parse_nla_flavors, 1925848f3c0dSAndrea Mayer .put = put_nla_flavors, 1926848f3c0dSAndrea Mayer .cmp = cmp_nla_flavors }, 1927d1df6fd8SDavid Lebrun }; 1928d1df6fd8SDavid Lebrun 1929964adce5SAndrea Mayer /* call the destroy() callback (if available) for each set attribute in 19300a3021f1SAndrea Mayer * @parsed_attrs, starting from the first attribute up to the @max_parsed 19310a3021f1SAndrea Mayer * (excluded) attribute. 1932964adce5SAndrea Mayer */ 19330a3021f1SAndrea Mayer static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed, 19340a3021f1SAndrea Mayer struct seg6_local_lwt *slwt) 1935964adce5SAndrea Mayer { 1936964adce5SAndrea Mayer struct seg6_action_param *param; 1937964adce5SAndrea Mayer int i; 1938964adce5SAndrea Mayer 1939964adce5SAndrea Mayer /* Every required seg6local attribute is identified by an ID which is 1940964adce5SAndrea Mayer * encoded as a flag (i.e: 1 << ID) in the 'attrs' bitmask; 1941964adce5SAndrea Mayer * 19420a3021f1SAndrea Mayer * We scan the 'parsed_attrs' bitmask, starting from the first attribute 1943964adce5SAndrea Mayer * up to the @max_parsed (excluded) attribute. 1944964adce5SAndrea Mayer * For each set attribute, we retrieve the corresponding destroy() 1945964adce5SAndrea Mayer * callback. If the callback is not available, then we skip to the next 1946964adce5SAndrea Mayer * attribute; otherwise, we call the destroy() callback. 1947964adce5SAndrea Mayer */ 1948ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < max_parsed; ++i) { 1949300a0fd8SAndrea Mayer if (!(parsed_attrs & SEG6_F_ATTR(i))) 1950964adce5SAndrea Mayer continue; 1951964adce5SAndrea Mayer 1952964adce5SAndrea Mayer param = &seg6_action_params[i]; 1953964adce5SAndrea Mayer 1954964adce5SAndrea Mayer if (param->destroy) 1955964adce5SAndrea Mayer param->destroy(slwt); 1956964adce5SAndrea Mayer } 1957964adce5SAndrea Mayer } 1958964adce5SAndrea Mayer 1959964adce5SAndrea Mayer /* release all the resources that may have been acquired during parsing 1960964adce5SAndrea Mayer * operations. 1961964adce5SAndrea Mayer */ 1962964adce5SAndrea Mayer static void destroy_attrs(struct seg6_local_lwt *slwt) 1963964adce5SAndrea Mayer { 19640a3021f1SAndrea Mayer unsigned long attrs = slwt->desc->attrs | slwt->parsed_optattrs; 19650a3021f1SAndrea Mayer 19660a3021f1SAndrea Mayer __destroy_attrs(attrs, SEG6_LOCAL_MAX + 1, slwt); 19670a3021f1SAndrea Mayer } 19680a3021f1SAndrea Mayer 19690a3021f1SAndrea Mayer static int parse_nla_optional_attrs(struct nlattr **attrs, 1970e2a8ecc4SAndrea Mayer struct seg6_local_lwt *slwt, 1971e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 19720a3021f1SAndrea Mayer { 19730a3021f1SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 19740a3021f1SAndrea Mayer unsigned long parsed_optattrs = 0; 19750a3021f1SAndrea Mayer struct seg6_action_param *param; 19760a3021f1SAndrea Mayer int err, i; 19770a3021f1SAndrea Mayer 1978ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; ++i) { 1979300a0fd8SAndrea Mayer if (!(desc->optattrs & SEG6_F_ATTR(i)) || !attrs[i]) 19800a3021f1SAndrea Mayer continue; 19810a3021f1SAndrea Mayer 19820a3021f1SAndrea Mayer /* once here, the i-th attribute is provided by the 19830a3021f1SAndrea Mayer * userspace AND it is identified optional as well. 19840a3021f1SAndrea Mayer */ 19850a3021f1SAndrea Mayer param = &seg6_action_params[i]; 19860a3021f1SAndrea Mayer 1987e2a8ecc4SAndrea Mayer err = param->parse(attrs, slwt, extack); 19880a3021f1SAndrea Mayer if (err < 0) 19890a3021f1SAndrea Mayer goto parse_optattrs_err; 19900a3021f1SAndrea Mayer 19910a3021f1SAndrea Mayer /* current attribute has been correctly parsed */ 1992300a0fd8SAndrea Mayer parsed_optattrs |= SEG6_F_ATTR(i); 19930a3021f1SAndrea Mayer } 19940a3021f1SAndrea Mayer 19950a3021f1SAndrea Mayer /* store in the tunnel state all the optional attributed successfully 19960a3021f1SAndrea Mayer * parsed. 19970a3021f1SAndrea Mayer */ 19980a3021f1SAndrea Mayer slwt->parsed_optattrs = parsed_optattrs; 19990a3021f1SAndrea Mayer 20000a3021f1SAndrea Mayer return 0; 20010a3021f1SAndrea Mayer 20020a3021f1SAndrea Mayer parse_optattrs_err: 20030a3021f1SAndrea Mayer __destroy_attrs(parsed_optattrs, i, slwt); 20040a3021f1SAndrea Mayer 20050a3021f1SAndrea Mayer return err; 2006964adce5SAndrea Mayer } 2007964adce5SAndrea Mayer 2008cfdf64a0SAndrea Mayer /* call the custom constructor of the behavior during its initialization phase 2009cfdf64a0SAndrea Mayer * and after that all its attributes have been parsed successfully. 2010cfdf64a0SAndrea Mayer */ 2011cfdf64a0SAndrea Mayer static int 2012cfdf64a0SAndrea Mayer seg6_local_lwtunnel_build_state(struct seg6_local_lwt *slwt, const void *cfg, 2013cfdf64a0SAndrea Mayer struct netlink_ext_ack *extack) 2014cfdf64a0SAndrea Mayer { 2015cfdf64a0SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 2016cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops *ops; 2017cfdf64a0SAndrea Mayer 2018cfdf64a0SAndrea Mayer ops = &desc->slwt_ops; 2019cfdf64a0SAndrea Mayer if (!ops->build_state) 2020cfdf64a0SAndrea Mayer return 0; 2021cfdf64a0SAndrea Mayer 2022cfdf64a0SAndrea Mayer return ops->build_state(slwt, cfg, extack); 2023cfdf64a0SAndrea Mayer } 2024cfdf64a0SAndrea Mayer 2025cfdf64a0SAndrea Mayer /* call the custom destructor of the behavior which is invoked before the 2026cfdf64a0SAndrea Mayer * tunnel is going to be destroyed. 2027cfdf64a0SAndrea Mayer */ 2028cfdf64a0SAndrea Mayer static void seg6_local_lwtunnel_destroy_state(struct seg6_local_lwt *slwt) 2029cfdf64a0SAndrea Mayer { 2030cfdf64a0SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 2031cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops *ops; 2032cfdf64a0SAndrea Mayer 2033cfdf64a0SAndrea Mayer ops = &desc->slwt_ops; 2034cfdf64a0SAndrea Mayer if (!ops->destroy_state) 2035cfdf64a0SAndrea Mayer return; 2036cfdf64a0SAndrea Mayer 2037cfdf64a0SAndrea Mayer ops->destroy_state(slwt); 2038cfdf64a0SAndrea Mayer } 2039cfdf64a0SAndrea Mayer 2040e2a8ecc4SAndrea Mayer static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt, 2041e2a8ecc4SAndrea Mayer struct netlink_ext_ack *extack) 2042d1df6fd8SDavid Lebrun { 2043d1df6fd8SDavid Lebrun struct seg6_action_param *param; 2044d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 20450a3021f1SAndrea Mayer unsigned long invalid_attrs; 2046d1df6fd8SDavid Lebrun int i, err; 2047d1df6fd8SDavid Lebrun 2048d1df6fd8SDavid Lebrun desc = __get_action_desc(slwt->action); 2049d1df6fd8SDavid Lebrun if (!desc) 2050d1df6fd8SDavid Lebrun return -EINVAL; 2051d1df6fd8SDavid Lebrun 2052d1df6fd8SDavid Lebrun if (!desc->input) 2053d1df6fd8SDavid Lebrun return -EOPNOTSUPP; 2054d1df6fd8SDavid Lebrun 2055d1df6fd8SDavid Lebrun slwt->desc = desc; 2056d1df6fd8SDavid Lebrun slwt->headroom += desc->static_headroom; 2057d1df6fd8SDavid Lebrun 20580a3021f1SAndrea Mayer /* Forcing the desc->optattrs *set* and the desc->attrs *set* to be 20590a3021f1SAndrea Mayer * disjoined, this allow us to release acquired resources by optional 20600a3021f1SAndrea Mayer * attributes and by required attributes independently from each other 20610d770360SAndrea Mayer * without any interference. 20620a3021f1SAndrea Mayer * In other terms, we are sure that we do not release some the acquired 20630a3021f1SAndrea Mayer * resources twice. 20640a3021f1SAndrea Mayer * 20650a3021f1SAndrea Mayer * Note that if an attribute is configured both as required and as 20660a3021f1SAndrea Mayer * optional, it means that the user has messed something up in the 20670a3021f1SAndrea Mayer * seg6_action_table. Therefore, this check is required for SRv6 20680a3021f1SAndrea Mayer * behaviors to work properly. 20690a3021f1SAndrea Mayer */ 20700a3021f1SAndrea Mayer invalid_attrs = desc->attrs & desc->optattrs; 20710a3021f1SAndrea Mayer if (invalid_attrs) { 20720a3021f1SAndrea Mayer WARN_ONCE(1, 20730a3021f1SAndrea Mayer "An attribute cannot be both required AND optional"); 20740a3021f1SAndrea Mayer return -EINVAL; 20750a3021f1SAndrea Mayer } 20760a3021f1SAndrea Mayer 20770a3021f1SAndrea Mayer /* parse the required attributes */ 2078ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { 2079300a0fd8SAndrea Mayer if (desc->attrs & SEG6_F_ATTR(i)) { 2080d1df6fd8SDavid Lebrun if (!attrs[i]) 2081d1df6fd8SDavid Lebrun return -EINVAL; 2082d1df6fd8SDavid Lebrun 2083d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 2084d1df6fd8SDavid Lebrun 2085e2a8ecc4SAndrea Mayer err = param->parse(attrs, slwt, extack); 2086d1df6fd8SDavid Lebrun if (err < 0) 20870a3021f1SAndrea Mayer goto parse_attrs_err; 2088d1df6fd8SDavid Lebrun } 2089d1df6fd8SDavid Lebrun } 2090d1df6fd8SDavid Lebrun 20910a3021f1SAndrea Mayer /* parse the optional attributes, if any */ 2092e2a8ecc4SAndrea Mayer err = parse_nla_optional_attrs(attrs, slwt, extack); 20930a3021f1SAndrea Mayer if (err < 0) 20940a3021f1SAndrea Mayer goto parse_attrs_err; 20950a3021f1SAndrea Mayer 2096d1df6fd8SDavid Lebrun return 0; 2097964adce5SAndrea Mayer 20980a3021f1SAndrea Mayer parse_attrs_err: 2099964adce5SAndrea Mayer /* release any resource that may have been acquired during the i-1 2100964adce5SAndrea Mayer * parse() operations. 2101964adce5SAndrea Mayer */ 21020a3021f1SAndrea Mayer __destroy_attrs(desc->attrs, i, slwt); 2103964adce5SAndrea Mayer 2104964adce5SAndrea Mayer return err; 2105d1df6fd8SDavid Lebrun } 2106d1df6fd8SDavid Lebrun 2107faee6769SAlexander Aring static int seg6_local_build_state(struct net *net, struct nlattr *nla, 2108faee6769SAlexander Aring unsigned int family, const void *cfg, 2109faee6769SAlexander Aring struct lwtunnel_state **ts, 2110d1df6fd8SDavid Lebrun struct netlink_ext_ack *extack) 2111d1df6fd8SDavid Lebrun { 2112d1df6fd8SDavid Lebrun struct nlattr *tb[SEG6_LOCAL_MAX + 1]; 2113d1df6fd8SDavid Lebrun struct lwtunnel_state *newts; 2114d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 2115d1df6fd8SDavid Lebrun int err; 2116d1df6fd8SDavid Lebrun 21176285217fSDavid Lebrun if (family != AF_INET6) 21186285217fSDavid Lebrun return -EINVAL; 21196285217fSDavid Lebrun 21208cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla, 21218cb08174SJohannes Berg seg6_local_policy, extack); 2122d1df6fd8SDavid Lebrun 2123d1df6fd8SDavid Lebrun if (err < 0) 2124d1df6fd8SDavid Lebrun return err; 2125d1df6fd8SDavid Lebrun 2126d1df6fd8SDavid Lebrun if (!tb[SEG6_LOCAL_ACTION]) 2127d1df6fd8SDavid Lebrun return -EINVAL; 2128d1df6fd8SDavid Lebrun 2129d1df6fd8SDavid Lebrun newts = lwtunnel_state_alloc(sizeof(*slwt)); 2130d1df6fd8SDavid Lebrun if (!newts) 2131d1df6fd8SDavid Lebrun return -ENOMEM; 2132d1df6fd8SDavid Lebrun 2133d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(newts); 2134d1df6fd8SDavid Lebrun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); 2135d1df6fd8SDavid Lebrun 2136e2a8ecc4SAndrea Mayer err = parse_nla_action(tb, slwt, extack); 2137d1df6fd8SDavid Lebrun if (err < 0) 2138d1df6fd8SDavid Lebrun goto out_free; 2139d1df6fd8SDavid Lebrun 2140cfdf64a0SAndrea Mayer err = seg6_local_lwtunnel_build_state(slwt, cfg, extack); 2141cfdf64a0SAndrea Mayer if (err < 0) 2142cfdf64a0SAndrea Mayer goto out_destroy_attrs; 2143cfdf64a0SAndrea Mayer 2144d1df6fd8SDavid Lebrun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; 2145d1df6fd8SDavid Lebrun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; 2146d1df6fd8SDavid Lebrun newts->headroom = slwt->headroom; 2147d1df6fd8SDavid Lebrun 2148d1df6fd8SDavid Lebrun *ts = newts; 2149d1df6fd8SDavid Lebrun 2150d1df6fd8SDavid Lebrun return 0; 2151d1df6fd8SDavid Lebrun 2152cfdf64a0SAndrea Mayer out_destroy_attrs: 2153cfdf64a0SAndrea Mayer destroy_attrs(slwt); 2154d1df6fd8SDavid Lebrun out_free: 2155d1df6fd8SDavid Lebrun kfree(newts); 2156d1df6fd8SDavid Lebrun return err; 2157d1df6fd8SDavid Lebrun } 2158d1df6fd8SDavid Lebrun 2159d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt) 2160d1df6fd8SDavid Lebrun { 2161d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 2162d1df6fd8SDavid Lebrun 2163cfdf64a0SAndrea Mayer seg6_local_lwtunnel_destroy_state(slwt); 2164cfdf64a0SAndrea Mayer 2165964adce5SAndrea Mayer destroy_attrs(slwt); 2166004d4b27SMathieu Xhonneux 2167004d4b27SMathieu Xhonneux return; 2168d1df6fd8SDavid Lebrun } 2169d1df6fd8SDavid Lebrun 2170d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb, 2171d1df6fd8SDavid Lebrun struct lwtunnel_state *lwt) 2172d1df6fd8SDavid Lebrun { 2173d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 2174d1df6fd8SDavid Lebrun struct seg6_action_param *param; 21750a3021f1SAndrea Mayer unsigned long attrs; 2176d1df6fd8SDavid Lebrun int i, err; 2177d1df6fd8SDavid Lebrun 2178d1df6fd8SDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) 2179d1df6fd8SDavid Lebrun return -EMSGSIZE; 2180d1df6fd8SDavid Lebrun 21810a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 21820a3021f1SAndrea Mayer 2183ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { 2184300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(i)) { 2185d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 2186d1df6fd8SDavid Lebrun err = param->put(skb, slwt); 2187d1df6fd8SDavid Lebrun if (err < 0) 2188d1df6fd8SDavid Lebrun return err; 2189d1df6fd8SDavid Lebrun } 2190d1df6fd8SDavid Lebrun } 2191d1df6fd8SDavid Lebrun 2192d1df6fd8SDavid Lebrun return 0; 2193d1df6fd8SDavid Lebrun } 2194d1df6fd8SDavid Lebrun 2195d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) 2196d1df6fd8SDavid Lebrun { 2197d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 2198d1df6fd8SDavid Lebrun unsigned long attrs; 2199d1df6fd8SDavid Lebrun int nlsize; 2200d1df6fd8SDavid Lebrun 2201d1df6fd8SDavid Lebrun nlsize = nla_total_size(4); /* action */ 2202d1df6fd8SDavid Lebrun 22030a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 2204d1df6fd8SDavid Lebrun 2205300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_SRH)) 2206d1df6fd8SDavid Lebrun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); 2207d1df6fd8SDavid Lebrun 2208300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE)) 2209d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 2210d1df6fd8SDavid Lebrun 2211300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH4)) 2212d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 2213d1df6fd8SDavid Lebrun 2214300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH6)) 2215d1df6fd8SDavid Lebrun nlsize += nla_total_size(16); 2216d1df6fd8SDavid Lebrun 2217300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_IIF)) 2218d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 2219d1df6fd8SDavid Lebrun 2220300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_OIF)) 2221d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 2222d1df6fd8SDavid Lebrun 2223300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_BPF)) 2224004d4b27SMathieu Xhonneux nlsize += nla_total_size(sizeof(struct nlattr)) + 2225004d4b27SMathieu Xhonneux nla_total_size(MAX_PROG_NAME) + 2226004d4b27SMathieu Xhonneux nla_total_size(4); 2227004d4b27SMathieu Xhonneux 2228300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE)) 2229664d6f86SAndrea Mayer nlsize += nla_total_size(4); 2230664d6f86SAndrea Mayer 223194604548SAndrea Mayer if (attrs & SEG6_F_LOCAL_COUNTERS) 223294604548SAndrea Mayer nlsize += nla_total_size(0) + /* nest SEG6_LOCAL_COUNTERS */ 223394604548SAndrea Mayer /* SEG6_LOCAL_CNT_PACKETS */ 223494604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)) + 223594604548SAndrea Mayer /* SEG6_LOCAL_CNT_BYTES */ 223694604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)) + 223794604548SAndrea Mayer /* SEG6_LOCAL_CNT_ERRORS */ 223894604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)); 223994604548SAndrea Mayer 2240848f3c0dSAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_FLAVORS)) 2241848f3c0dSAndrea Mayer nlsize += encap_size_flavors(slwt); 2242848f3c0dSAndrea Mayer 2243d1df6fd8SDavid Lebrun return nlsize; 2244d1df6fd8SDavid Lebrun } 2245d1df6fd8SDavid Lebrun 2246d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a, 2247d1df6fd8SDavid Lebrun struct lwtunnel_state *b) 2248d1df6fd8SDavid Lebrun { 2249d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt_a, *slwt_b; 2250d1df6fd8SDavid Lebrun struct seg6_action_param *param; 22510a3021f1SAndrea Mayer unsigned long attrs_a, attrs_b; 2252d1df6fd8SDavid Lebrun int i; 2253d1df6fd8SDavid Lebrun 2254d1df6fd8SDavid Lebrun slwt_a = seg6_local_lwtunnel(a); 2255d1df6fd8SDavid Lebrun slwt_b = seg6_local_lwtunnel(b); 2256d1df6fd8SDavid Lebrun 2257d1df6fd8SDavid Lebrun if (slwt_a->action != slwt_b->action) 2258d1df6fd8SDavid Lebrun return 1; 2259d1df6fd8SDavid Lebrun 22600a3021f1SAndrea Mayer attrs_a = slwt_a->desc->attrs | slwt_a->parsed_optattrs; 22610a3021f1SAndrea Mayer attrs_b = slwt_b->desc->attrs | slwt_b->parsed_optattrs; 22620a3021f1SAndrea Mayer 22630a3021f1SAndrea Mayer if (attrs_a != attrs_b) 2264d1df6fd8SDavid Lebrun return 1; 2265d1df6fd8SDavid Lebrun 2266ac0dbed9SNick Desaulniers for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { 2267300a0fd8SAndrea Mayer if (attrs_a & SEG6_F_ATTR(i)) { 2268d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 2269d1df6fd8SDavid Lebrun if (param->cmp(slwt_a, slwt_b)) 2270d1df6fd8SDavid Lebrun return 1; 2271d1df6fd8SDavid Lebrun } 2272d1df6fd8SDavid Lebrun } 2273d1df6fd8SDavid Lebrun 2274d1df6fd8SDavid Lebrun return 0; 2275d1df6fd8SDavid Lebrun } 2276d1df6fd8SDavid Lebrun 2277d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = { 2278d1df6fd8SDavid Lebrun .build_state = seg6_local_build_state, 2279d1df6fd8SDavid Lebrun .destroy_state = seg6_local_destroy_state, 2280d1df6fd8SDavid Lebrun .input = seg6_local_input, 2281d1df6fd8SDavid Lebrun .fill_encap = seg6_local_fill_encap, 2282d1df6fd8SDavid Lebrun .get_encap_size = seg6_local_get_encap_size, 2283d1df6fd8SDavid Lebrun .cmp_encap = seg6_local_cmp_encap, 2284d1df6fd8SDavid Lebrun .owner = THIS_MODULE, 2285d1df6fd8SDavid Lebrun }; 2286d1df6fd8SDavid Lebrun 2287d1df6fd8SDavid Lebrun int __init seg6_local_init(void) 2288d1df6fd8SDavid Lebrun { 2289300a0fd8SAndrea Mayer /* If the max total number of defined attributes is reached, then your 2290300a0fd8SAndrea Mayer * kernel build stops here. 2291300a0fd8SAndrea Mayer * 2292300a0fd8SAndrea Mayer * This check is required to avoid arithmetic overflows when processing 2293300a0fd8SAndrea Mayer * behavior attributes and the maximum number of defined attributes 2294300a0fd8SAndrea Mayer * exceeds the allowed value. 2295300a0fd8SAndrea Mayer */ 2296300a0fd8SAndrea Mayer BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsigned long)); 2297300a0fd8SAndrea Mayer 2298848f3c0dSAndrea Mayer /* If the default NEXT-C-SID Locator-Block/Node Function lengths (in 2299848f3c0dSAndrea Mayer * bits) have been changed with invalid values, kernel build stops 2300848f3c0dSAndrea Mayer * here. 2301848f3c0dSAndrea Mayer */ 2302848f3c0dSAndrea Mayer BUILD_BUG_ON(next_csid_chk_cntr_bits(SEG6_LOCAL_LCBLOCK_DBITS, 2303848f3c0dSAndrea Mayer SEG6_LOCAL_LCNODE_FN_DBITS)); 2304848f3c0dSAndrea Mayer BUILD_BUG_ON(next_csid_chk_lcblock_bits(SEG6_LOCAL_LCBLOCK_DBITS)); 2305848f3c0dSAndrea Mayer BUILD_BUG_ON(next_csid_chk_lcnode_fn_bits(SEG6_LOCAL_LCNODE_FN_DBITS)); 2306848f3c0dSAndrea Mayer 2307d1df6fd8SDavid Lebrun return lwtunnel_encap_add_ops(&seg6_local_ops, 2308d1df6fd8SDavid Lebrun LWTUNNEL_ENCAP_SEG6_LOCAL); 2309d1df6fd8SDavid Lebrun } 2310d1df6fd8SDavid Lebrun 2311d1df6fd8SDavid Lebrun void seg6_local_exit(void) 2312d1df6fd8SDavid Lebrun { 2313d1df6fd8SDavid Lebrun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); 2314d1df6fd8SDavid Lebrun } 2315