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 10d1df6fd8SDavid Lebrun #include <linux/types.h> 11d1df6fd8SDavid Lebrun #include <linux/skbuff.h> 12d1df6fd8SDavid Lebrun #include <linux/net.h> 13d1df6fd8SDavid Lebrun #include <linux/module.h> 14d1df6fd8SDavid Lebrun #include <net/ip.h> 15d1df6fd8SDavid Lebrun #include <net/lwtunnel.h> 16d1df6fd8SDavid Lebrun #include <net/netevent.h> 17d1df6fd8SDavid Lebrun #include <net/netns/generic.h> 18d1df6fd8SDavid Lebrun #include <net/ip6_fib.h> 19d1df6fd8SDavid Lebrun #include <net/route.h> 20d1df6fd8SDavid Lebrun #include <net/seg6.h> 21d1df6fd8SDavid Lebrun #include <linux/seg6.h> 22d1df6fd8SDavid Lebrun #include <linux/seg6_local.h> 23d1df6fd8SDavid Lebrun #include <net/addrconf.h> 24d1df6fd8SDavid Lebrun #include <net/ip6_route.h> 25d1df6fd8SDavid Lebrun #include <net/dst_cache.h> 2662ebaeaeSYuki Taguchi #include <net/ip_tunnels.h> 27d1df6fd8SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 28d1df6fd8SDavid Lebrun #include <net/seg6_hmac.h> 29d1df6fd8SDavid Lebrun #endif 301c1e761eSMathieu Xhonneux #include <net/seg6_local.h> 31891ef8ddSDavid Lebrun #include <linux/etherdevice.h> 32004d4b27SMathieu Xhonneux #include <linux/bpf.h> 33*7a3f5b0dSRyoga Saito #include <net/lwtunnel.h> 34*7a3f5b0dSRyoga 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 76664d6f86SAndrea Mayer enum seg6_end_dt_mode { 77664d6f86SAndrea Mayer DT_INVALID_MODE = -EINVAL, 78664d6f86SAndrea Mayer DT_LEGACY_MODE = 0, 79664d6f86SAndrea Mayer DT_VRF_MODE = 1, 80664d6f86SAndrea Mayer }; 81664d6f86SAndrea Mayer 82664d6f86SAndrea Mayer struct seg6_end_dt_info { 83664d6f86SAndrea Mayer enum seg6_end_dt_mode mode; 84664d6f86SAndrea Mayer 85664d6f86SAndrea Mayer struct net *net; 86664d6f86SAndrea Mayer /* VRF device associated to the routing table used by the SRv6 87664d6f86SAndrea Mayer * End.DT4/DT6 behavior for routing IPv4/IPv6 packets. 88664d6f86SAndrea Mayer */ 89664d6f86SAndrea Mayer int vrf_ifindex; 90664d6f86SAndrea Mayer int vrf_table; 91664d6f86SAndrea Mayer 928b532109SAndrea Mayer /* tunneled packet family (IPv4 or IPv6). 938b532109SAndrea Mayer * Protocol and header length are inferred from family. 948b532109SAndrea Mayer */ 95664d6f86SAndrea Mayer u16 family; 96664d6f86SAndrea Mayer }; 97664d6f86SAndrea Mayer 9894604548SAndrea Mayer struct pcpu_seg6_local_counters { 9994604548SAndrea Mayer u64_stats_t packets; 10094604548SAndrea Mayer u64_stats_t bytes; 10194604548SAndrea Mayer u64_stats_t errors; 10294604548SAndrea Mayer 10394604548SAndrea Mayer struct u64_stats_sync syncp; 10494604548SAndrea Mayer }; 10594604548SAndrea Mayer 10694604548SAndrea Mayer /* This struct groups all the SRv6 Behavior counters supported so far. 10794604548SAndrea Mayer * 10894604548SAndrea Mayer * put_nla_counters() makes use of this data structure to collect all counter 10994604548SAndrea Mayer * values after the per-CPU counter evaluation has been performed. 11094604548SAndrea Mayer * Finally, each counter value (in seg6_local_counters) is stored in the 11194604548SAndrea Mayer * corresponding netlink attribute and sent to user space. 11294604548SAndrea Mayer * 11394604548SAndrea Mayer * NB: we don't want to expose this structure to user space! 11494604548SAndrea Mayer */ 11594604548SAndrea Mayer struct seg6_local_counters { 11694604548SAndrea Mayer __u64 packets; 11794604548SAndrea Mayer __u64 bytes; 11894604548SAndrea Mayer __u64 errors; 11994604548SAndrea Mayer }; 12094604548SAndrea Mayer 12194604548SAndrea Mayer #define seg6_local_alloc_pcpu_counters(__gfp) \ 12294604548SAndrea Mayer __netdev_alloc_pcpu_stats(struct pcpu_seg6_local_counters, \ 12394604548SAndrea Mayer ((__gfp) | __GFP_ZERO)) 12494604548SAndrea Mayer 12594604548SAndrea Mayer #define SEG6_F_LOCAL_COUNTERS SEG6_F_ATTR(SEG6_LOCAL_COUNTERS) 12694604548SAndrea Mayer 127d1df6fd8SDavid Lebrun struct seg6_local_lwt { 128d1df6fd8SDavid Lebrun int action; 129d1df6fd8SDavid Lebrun struct ipv6_sr_hdr *srh; 130d1df6fd8SDavid Lebrun int table; 131d1df6fd8SDavid Lebrun struct in_addr nh4; 132d1df6fd8SDavid Lebrun struct in6_addr nh6; 133d1df6fd8SDavid Lebrun int iif; 134d1df6fd8SDavid Lebrun int oif; 135004d4b27SMathieu Xhonneux struct bpf_lwt_prog bpf; 136664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 137664d6f86SAndrea Mayer struct seg6_end_dt_info dt_info; 138664d6f86SAndrea Mayer #endif 13994604548SAndrea Mayer struct pcpu_seg6_local_counters __percpu *pcpu_counters; 140d1df6fd8SDavid Lebrun 141d1df6fd8SDavid Lebrun int headroom; 142d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1430a3021f1SAndrea Mayer /* unlike the required attrs, we have to track the optional attributes 1440a3021f1SAndrea Mayer * that have been effectively parsed. 1450a3021f1SAndrea Mayer */ 1460a3021f1SAndrea Mayer unsigned long parsed_optattrs; 147d1df6fd8SDavid Lebrun }; 148d1df6fd8SDavid Lebrun 149d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) 150d1df6fd8SDavid Lebrun { 151d1df6fd8SDavid Lebrun return (struct seg6_local_lwt *)lwt->data; 152d1df6fd8SDavid Lebrun } 153d1df6fd8SDavid Lebrun 154fbbc5bc2SJulien Massonneau static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb, int flags) 155140f04c3SDavid Lebrun { 156140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 1575829d70bSAhmed Abdelsalam int len, srhoff = 0; 158140f04c3SDavid Lebrun 159fbbc5bc2SJulien Massonneau if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, &flags) < 0) 160140f04c3SDavid Lebrun return NULL; 161140f04c3SDavid Lebrun 1625829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + sizeof(*srh))) 1635829d70bSAhmed Abdelsalam return NULL; 1645829d70bSAhmed Abdelsalam 1655829d70bSAhmed Abdelsalam srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 1665829d70bSAhmed Abdelsalam 167140f04c3SDavid Lebrun len = (srh->hdrlen + 1) << 3; 168140f04c3SDavid Lebrun 1695829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + len)) 170140f04c3SDavid Lebrun return NULL; 171140f04c3SDavid Lebrun 1727f91ed8cSAndrea Mayer /* note that pskb_may_pull may change pointers in header; 1737f91ed8cSAndrea Mayer * for this reason it is necessary to reload them when needed. 1747f91ed8cSAndrea Mayer */ 1757f91ed8cSAndrea Mayer srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 1767f91ed8cSAndrea Mayer 177bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, true)) 178140f04c3SDavid Lebrun return NULL; 179140f04c3SDavid Lebrun 180140f04c3SDavid Lebrun return srh; 181140f04c3SDavid Lebrun } 182140f04c3SDavid Lebrun 183140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) 184140f04c3SDavid Lebrun { 185140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 186140f04c3SDavid Lebrun 187fbbc5bc2SJulien Massonneau srh = get_srh(skb, IP6_FH_F_SKIP_RH); 188140f04c3SDavid Lebrun if (!srh) 189140f04c3SDavid Lebrun return NULL; 190140f04c3SDavid Lebrun 191140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 192140f04c3SDavid Lebrun if (!seg6_hmac_validate_skb(skb)) 193140f04c3SDavid Lebrun return NULL; 194140f04c3SDavid Lebrun #endif 195140f04c3SDavid Lebrun 196140f04c3SDavid Lebrun return srh; 197140f04c3SDavid Lebrun } 198140f04c3SDavid Lebrun 199d7a669ddSDavid Lebrun static bool decap_and_validate(struct sk_buff *skb, int proto) 200d7a669ddSDavid Lebrun { 201d7a669ddSDavid Lebrun struct ipv6_sr_hdr *srh; 202d7a669ddSDavid Lebrun unsigned int off = 0; 203d7a669ddSDavid Lebrun 204fbbc5bc2SJulien Massonneau srh = get_srh(skb, 0); 205d7a669ddSDavid Lebrun if (srh && srh->segments_left > 0) 206d7a669ddSDavid Lebrun return false; 207d7a669ddSDavid Lebrun 208d7a669ddSDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 209d7a669ddSDavid Lebrun if (srh && !seg6_hmac_validate_skb(skb)) 210d7a669ddSDavid Lebrun return false; 211d7a669ddSDavid Lebrun #endif 212d7a669ddSDavid Lebrun 213d7a669ddSDavid Lebrun if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0) 214d7a669ddSDavid Lebrun return false; 215d7a669ddSDavid Lebrun 216d7a669ddSDavid Lebrun if (!pskb_pull(skb, off)) 217d7a669ddSDavid Lebrun return false; 218d7a669ddSDavid Lebrun 219d7a669ddSDavid Lebrun skb_postpull_rcsum(skb, skb_network_header(skb), off); 220d7a669ddSDavid Lebrun 221d7a669ddSDavid Lebrun skb_reset_network_header(skb); 222d7a669ddSDavid Lebrun skb_reset_transport_header(skb); 22362ebaeaeSYuki Taguchi if (iptunnel_pull_offloads(skb)) 22462ebaeaeSYuki Taguchi return false; 225d7a669ddSDavid Lebrun 226d7a669ddSDavid Lebrun return true; 227d7a669ddSDavid Lebrun } 228d7a669ddSDavid Lebrun 229d7a669ddSDavid Lebrun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr) 230d7a669ddSDavid Lebrun { 231d7a669ddSDavid Lebrun struct in6_addr *addr; 232d7a669ddSDavid Lebrun 233d7a669ddSDavid Lebrun srh->segments_left--; 234d7a669ddSDavid Lebrun addr = srh->segments + srh->segments_left; 235d7a669ddSDavid Lebrun *daddr = *addr; 236d7a669ddSDavid Lebrun } 237d7a669ddSDavid Lebrun 238fd1fef0cSAndrea Mayer static int 239fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr, 240fd1fef0cSAndrea Mayer u32 tbl_id, bool local_delivery) 241d7a669ddSDavid Lebrun { 242d7a669ddSDavid Lebrun struct net *net = dev_net(skb->dev); 243d7a669ddSDavid Lebrun struct ipv6hdr *hdr = ipv6_hdr(skb); 244d7a669ddSDavid Lebrun int flags = RT6_LOOKUP_F_HAS_SADDR; 245d7a669ddSDavid Lebrun struct dst_entry *dst = NULL; 246d7a669ddSDavid Lebrun struct rt6_info *rt; 247d7a669ddSDavid Lebrun struct flowi6 fl6; 248fd1fef0cSAndrea Mayer int dev_flags = 0; 249d7a669ddSDavid Lebrun 250d7a669ddSDavid Lebrun fl6.flowi6_iif = skb->dev->ifindex; 251d7a669ddSDavid Lebrun fl6.daddr = nhaddr ? *nhaddr : hdr->daddr; 252d7a669ddSDavid Lebrun fl6.saddr = hdr->saddr; 253d7a669ddSDavid Lebrun fl6.flowlabel = ip6_flowinfo(hdr); 254d7a669ddSDavid Lebrun fl6.flowi6_mark = skb->mark; 255d7a669ddSDavid Lebrun fl6.flowi6_proto = hdr->nexthdr; 256d7a669ddSDavid Lebrun 257d7a669ddSDavid Lebrun if (nhaddr) 258d7a669ddSDavid Lebrun fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH; 259d7a669ddSDavid Lebrun 260d7a669ddSDavid Lebrun if (!tbl_id) { 261b75cc8f9SDavid Ahern dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags); 262d7a669ddSDavid Lebrun } else { 263d7a669ddSDavid Lebrun struct fib6_table *table; 264d7a669ddSDavid Lebrun 265d7a669ddSDavid Lebrun table = fib6_get_table(net, tbl_id); 266d7a669ddSDavid Lebrun if (!table) 267d7a669ddSDavid Lebrun goto out; 268d7a669ddSDavid Lebrun 269b75cc8f9SDavid Ahern rt = ip6_pol_route(net, table, 0, &fl6, skb, flags); 270d7a669ddSDavid Lebrun dst = &rt->dst; 271d7a669ddSDavid Lebrun } 272d7a669ddSDavid Lebrun 273fd1fef0cSAndrea Mayer /* we want to discard traffic destined for local packet processing, 274fd1fef0cSAndrea Mayer * if @local_delivery is set to false. 275fd1fef0cSAndrea Mayer */ 276fd1fef0cSAndrea Mayer if (!local_delivery) 277fd1fef0cSAndrea Mayer dev_flags |= IFF_LOOPBACK; 278fd1fef0cSAndrea Mayer 279fd1fef0cSAndrea Mayer if (dst && (dst->dev->flags & dev_flags) && !dst->error) { 280d7a669ddSDavid Lebrun dst_release(dst); 281d7a669ddSDavid Lebrun dst = NULL; 282d7a669ddSDavid Lebrun } 283d7a669ddSDavid Lebrun 284d7a669ddSDavid Lebrun out: 285d7a669ddSDavid Lebrun if (!dst) { 286d7a669ddSDavid Lebrun rt = net->ipv6.ip6_blk_hole_entry; 287d7a669ddSDavid Lebrun dst = &rt->dst; 288d7a669ddSDavid Lebrun dst_hold(dst); 289d7a669ddSDavid Lebrun } 290d7a669ddSDavid Lebrun 291d7a669ddSDavid Lebrun skb_dst_drop(skb); 292d7a669ddSDavid Lebrun skb_dst_set(skb, dst); 2931c1e761eSMathieu Xhonneux return dst->error; 294d7a669ddSDavid Lebrun } 295d7a669ddSDavid Lebrun 296fd1fef0cSAndrea Mayer int seg6_lookup_nexthop(struct sk_buff *skb, 297fd1fef0cSAndrea Mayer struct in6_addr *nhaddr, u32 tbl_id) 298fd1fef0cSAndrea Mayer { 299fd1fef0cSAndrea Mayer return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false); 300fd1fef0cSAndrea Mayer } 301fd1fef0cSAndrea Mayer 302140f04c3SDavid Lebrun /* regular endpoint function */ 303140f04c3SDavid Lebrun static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 304140f04c3SDavid Lebrun { 305140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 306140f04c3SDavid Lebrun 307140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 308140f04c3SDavid Lebrun if (!srh) 309140f04c3SDavid Lebrun goto drop; 310140f04c3SDavid Lebrun 311d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 312140f04c3SDavid Lebrun 3131c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 314140f04c3SDavid Lebrun 315140f04c3SDavid Lebrun return dst_input(skb); 316140f04c3SDavid Lebrun 317140f04c3SDavid Lebrun drop: 318140f04c3SDavid Lebrun kfree_skb(skb); 319140f04c3SDavid Lebrun return -EINVAL; 320140f04c3SDavid Lebrun } 321140f04c3SDavid Lebrun 322140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */ 323140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) 324140f04c3SDavid Lebrun { 325140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 326140f04c3SDavid Lebrun 327140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 328140f04c3SDavid Lebrun if (!srh) 329140f04c3SDavid Lebrun goto drop; 330140f04c3SDavid Lebrun 331d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 332140f04c3SDavid Lebrun 3331c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, &slwt->nh6, 0); 334140f04c3SDavid Lebrun 335140f04c3SDavid Lebrun return dst_input(skb); 336140f04c3SDavid Lebrun 337140f04c3SDavid Lebrun drop: 338140f04c3SDavid Lebrun kfree_skb(skb); 339140f04c3SDavid Lebrun return -EINVAL; 340140f04c3SDavid Lebrun } 341140f04c3SDavid Lebrun 342891ef8ddSDavid Lebrun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) 343891ef8ddSDavid Lebrun { 344891ef8ddSDavid Lebrun struct ipv6_sr_hdr *srh; 345891ef8ddSDavid Lebrun 346891ef8ddSDavid Lebrun srh = get_and_validate_srh(skb); 347891ef8ddSDavid Lebrun if (!srh) 348891ef8ddSDavid Lebrun goto drop; 349891ef8ddSDavid Lebrun 350891ef8ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 351891ef8ddSDavid Lebrun 3521c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, slwt->table); 353891ef8ddSDavid Lebrun 354891ef8ddSDavid Lebrun return dst_input(skb); 355891ef8ddSDavid Lebrun 356891ef8ddSDavid Lebrun drop: 357891ef8ddSDavid Lebrun kfree_skb(skb); 358891ef8ddSDavid Lebrun return -EINVAL; 359891ef8ddSDavid Lebrun } 360891ef8ddSDavid Lebrun 361891ef8ddSDavid Lebrun /* decapsulate and forward inner L2 frame on specified interface */ 362891ef8ddSDavid Lebrun static int input_action_end_dx2(struct sk_buff *skb, 363891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 364891ef8ddSDavid Lebrun { 365891ef8ddSDavid Lebrun struct net *net = dev_net(skb->dev); 366891ef8ddSDavid Lebrun struct net_device *odev; 367891ef8ddSDavid Lebrun struct ethhdr *eth; 368891ef8ddSDavid Lebrun 36926776253SPaolo Lungaroni if (!decap_and_validate(skb, IPPROTO_ETHERNET)) 370891ef8ddSDavid Lebrun goto drop; 371891ef8ddSDavid Lebrun 372891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, ETH_HLEN)) 373891ef8ddSDavid Lebrun goto drop; 374891ef8ddSDavid Lebrun 375891ef8ddSDavid Lebrun skb_reset_mac_header(skb); 376891ef8ddSDavid Lebrun eth = (struct ethhdr *)skb->data; 377891ef8ddSDavid Lebrun 378891ef8ddSDavid Lebrun /* To determine the frame's protocol, we assume it is 802.3. This avoids 379891ef8ddSDavid Lebrun * a call to eth_type_trans(), which is not really relevant for our 380891ef8ddSDavid Lebrun * use case. 381891ef8ddSDavid Lebrun */ 382891ef8ddSDavid Lebrun if (!eth_proto_is_802_3(eth->h_proto)) 383891ef8ddSDavid Lebrun goto drop; 384891ef8ddSDavid Lebrun 385891ef8ddSDavid Lebrun odev = dev_get_by_index_rcu(net, slwt->oif); 386891ef8ddSDavid Lebrun if (!odev) 387891ef8ddSDavid Lebrun goto drop; 388891ef8ddSDavid Lebrun 389891ef8ddSDavid Lebrun /* As we accept Ethernet frames, make sure the egress device is of 390891ef8ddSDavid Lebrun * the correct type. 391891ef8ddSDavid Lebrun */ 392891ef8ddSDavid Lebrun if (odev->type != ARPHRD_ETHER) 393891ef8ddSDavid Lebrun goto drop; 394891ef8ddSDavid Lebrun 395891ef8ddSDavid Lebrun if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev)) 396891ef8ddSDavid Lebrun goto drop; 397891ef8ddSDavid Lebrun 398891ef8ddSDavid Lebrun skb_orphan(skb); 399891ef8ddSDavid Lebrun 400891ef8ddSDavid Lebrun if (skb_warn_if_lro(skb)) 401891ef8ddSDavid Lebrun goto drop; 402891ef8ddSDavid Lebrun 403891ef8ddSDavid Lebrun skb_forward_csum(skb); 404891ef8ddSDavid Lebrun 405891ef8ddSDavid Lebrun if (skb->len - ETH_HLEN > odev->mtu) 406891ef8ddSDavid Lebrun goto drop; 407891ef8ddSDavid Lebrun 408891ef8ddSDavid Lebrun skb->dev = odev; 409891ef8ddSDavid Lebrun skb->protocol = eth->h_proto; 410891ef8ddSDavid Lebrun 411891ef8ddSDavid Lebrun return dev_queue_xmit(skb); 412891ef8ddSDavid Lebrun 413891ef8ddSDavid Lebrun drop: 414891ef8ddSDavid Lebrun kfree_skb(skb); 415891ef8ddSDavid Lebrun return -EINVAL; 416891ef8ddSDavid Lebrun } 417891ef8ddSDavid Lebrun 418*7a3f5b0dSRyoga Saito static int input_action_end_dx6_finish(struct net *net, struct sock *sk, 419*7a3f5b0dSRyoga Saito struct sk_buff *skb) 420*7a3f5b0dSRyoga Saito { 421*7a3f5b0dSRyoga Saito struct dst_entry *orig_dst = skb_dst(skb); 422*7a3f5b0dSRyoga Saito struct in6_addr *nhaddr = NULL; 423*7a3f5b0dSRyoga Saito struct seg6_local_lwt *slwt; 424*7a3f5b0dSRyoga Saito 425*7a3f5b0dSRyoga Saito slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 426*7a3f5b0dSRyoga Saito 427*7a3f5b0dSRyoga Saito /* The inner packet is not associated to any local interface, 428*7a3f5b0dSRyoga Saito * so we do not call netif_rx(). 429*7a3f5b0dSRyoga Saito * 430*7a3f5b0dSRyoga Saito * If slwt->nh6 is set to ::, then lookup the nexthop for the 431*7a3f5b0dSRyoga Saito * inner packet's DA. Otherwise, use the specified nexthop. 432*7a3f5b0dSRyoga Saito */ 433*7a3f5b0dSRyoga Saito if (!ipv6_addr_any(&slwt->nh6)) 434*7a3f5b0dSRyoga Saito nhaddr = &slwt->nh6; 435*7a3f5b0dSRyoga Saito 436*7a3f5b0dSRyoga Saito seg6_lookup_nexthop(skb, nhaddr, 0); 437*7a3f5b0dSRyoga Saito 438*7a3f5b0dSRyoga Saito return dst_input(skb); 439*7a3f5b0dSRyoga Saito } 440*7a3f5b0dSRyoga Saito 441140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */ 442140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb, 443140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 444140f04c3SDavid Lebrun { 445140f04c3SDavid Lebrun /* this function accepts IPv6 encapsulated packets, with either 446140f04c3SDavid Lebrun * an SRH with SL=0, or no SRH. 447140f04c3SDavid Lebrun */ 448140f04c3SDavid Lebrun 449d7a669ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 450140f04c3SDavid Lebrun goto drop; 451140f04c3SDavid Lebrun 452d7a669ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 453140f04c3SDavid Lebrun goto drop; 454140f04c3SDavid Lebrun 455c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 456*7a3f5b0dSRyoga Saito nf_reset_ct(skb); 457c71644d0SAndrea Mayer 458*7a3f5b0dSRyoga Saito if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 459*7a3f5b0dSRyoga Saito return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, 460*7a3f5b0dSRyoga Saito dev_net(skb->dev), NULL, skb, NULL, 461*7a3f5b0dSRyoga Saito skb_dst(skb)->dev, input_action_end_dx6_finish); 462140f04c3SDavid Lebrun 463*7a3f5b0dSRyoga Saito return input_action_end_dx6_finish(dev_net(skb->dev), NULL, skb); 464140f04c3SDavid Lebrun drop: 465140f04c3SDavid Lebrun kfree_skb(skb); 466140f04c3SDavid Lebrun return -EINVAL; 467140f04c3SDavid Lebrun } 468140f04c3SDavid Lebrun 469*7a3f5b0dSRyoga Saito static int input_action_end_dx4_finish(struct net *net, struct sock *sk, 470*7a3f5b0dSRyoga Saito struct sk_buff *skb) 471891ef8ddSDavid Lebrun { 472*7a3f5b0dSRyoga Saito struct dst_entry *orig_dst = skb_dst(skb); 473*7a3f5b0dSRyoga Saito struct seg6_local_lwt *slwt; 474891ef8ddSDavid Lebrun struct iphdr *iph; 475891ef8ddSDavid Lebrun __be32 nhaddr; 476891ef8ddSDavid Lebrun int err; 477891ef8ddSDavid Lebrun 478*7a3f5b0dSRyoga Saito slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 479891ef8ddSDavid Lebrun 480891ef8ddSDavid Lebrun iph = ip_hdr(skb); 481891ef8ddSDavid Lebrun 482891ef8ddSDavid Lebrun nhaddr = slwt->nh4.s_addr ?: iph->daddr; 483891ef8ddSDavid Lebrun 484891ef8ddSDavid Lebrun skb_dst_drop(skb); 485891ef8ddSDavid Lebrun 486891ef8ddSDavid Lebrun err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev); 487*7a3f5b0dSRyoga Saito if (err) { 488*7a3f5b0dSRyoga Saito kfree_skb(skb); 489*7a3f5b0dSRyoga Saito return -EINVAL; 490*7a3f5b0dSRyoga Saito } 491891ef8ddSDavid Lebrun 492891ef8ddSDavid Lebrun return dst_input(skb); 493*7a3f5b0dSRyoga Saito } 494891ef8ddSDavid Lebrun 495*7a3f5b0dSRyoga Saito static int input_action_end_dx4(struct sk_buff *skb, 496*7a3f5b0dSRyoga Saito struct seg6_local_lwt *slwt) 497*7a3f5b0dSRyoga Saito { 498*7a3f5b0dSRyoga Saito if (!decap_and_validate(skb, IPPROTO_IPIP)) 499*7a3f5b0dSRyoga Saito goto drop; 500*7a3f5b0dSRyoga Saito 501*7a3f5b0dSRyoga Saito if (!pskb_may_pull(skb, sizeof(struct iphdr))) 502*7a3f5b0dSRyoga Saito goto drop; 503*7a3f5b0dSRyoga Saito 504*7a3f5b0dSRyoga Saito skb->protocol = htons(ETH_P_IP); 505*7a3f5b0dSRyoga Saito skb_set_transport_header(skb, sizeof(struct iphdr)); 506*7a3f5b0dSRyoga Saito nf_reset_ct(skb); 507*7a3f5b0dSRyoga Saito 508*7a3f5b0dSRyoga Saito if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 509*7a3f5b0dSRyoga Saito return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, 510*7a3f5b0dSRyoga Saito dev_net(skb->dev), NULL, skb, NULL, 511*7a3f5b0dSRyoga Saito skb_dst(skb)->dev, input_action_end_dx4_finish); 512*7a3f5b0dSRyoga Saito 513*7a3f5b0dSRyoga Saito return input_action_end_dx4_finish(dev_net(skb->dev), NULL, skb); 514891ef8ddSDavid Lebrun drop: 515891ef8ddSDavid Lebrun kfree_skb(skb); 516891ef8ddSDavid Lebrun return -EINVAL; 517891ef8ddSDavid Lebrun } 518891ef8ddSDavid Lebrun 519664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 520664d6f86SAndrea Mayer static struct net *fib6_config_get_net(const struct fib6_config *fib6_cfg) 521664d6f86SAndrea Mayer { 522664d6f86SAndrea Mayer const struct nl_info *nli = &fib6_cfg->fc_nlinfo; 523664d6f86SAndrea Mayer 524664d6f86SAndrea Mayer return nli->nl_net; 525664d6f86SAndrea Mayer } 526664d6f86SAndrea Mayer 527664d6f86SAndrea Mayer static int __seg6_end_dt_vrf_build(struct seg6_local_lwt *slwt, const void *cfg, 528664d6f86SAndrea Mayer u16 family, struct netlink_ext_ack *extack) 529664d6f86SAndrea Mayer { 530664d6f86SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 531664d6f86SAndrea Mayer int vrf_ifindex; 532664d6f86SAndrea Mayer struct net *net; 533664d6f86SAndrea Mayer 534664d6f86SAndrea Mayer net = fib6_config_get_net(cfg); 535664d6f86SAndrea Mayer 536664d6f86SAndrea Mayer /* note that vrf_table was already set by parse_nla_vrftable() */ 537664d6f86SAndrea Mayer vrf_ifindex = l3mdev_ifindex_lookup_by_table_id(L3MDEV_TYPE_VRF, net, 538664d6f86SAndrea Mayer info->vrf_table); 539664d6f86SAndrea Mayer if (vrf_ifindex < 0) { 540664d6f86SAndrea Mayer if (vrf_ifindex == -EPERM) { 541664d6f86SAndrea Mayer NL_SET_ERR_MSG(extack, 542664d6f86SAndrea Mayer "Strict mode for VRF is disabled"); 543664d6f86SAndrea Mayer } else if (vrf_ifindex == -ENODEV) { 544664d6f86SAndrea Mayer NL_SET_ERR_MSG(extack, 545664d6f86SAndrea Mayer "Table has no associated VRF device"); 546664d6f86SAndrea Mayer } else { 547664d6f86SAndrea Mayer pr_debug("seg6local: SRv6 End.DT* creation error=%d\n", 548664d6f86SAndrea Mayer vrf_ifindex); 549664d6f86SAndrea Mayer } 550664d6f86SAndrea Mayer 551664d6f86SAndrea Mayer return vrf_ifindex; 552664d6f86SAndrea Mayer } 553664d6f86SAndrea Mayer 554664d6f86SAndrea Mayer info->net = net; 555664d6f86SAndrea Mayer info->vrf_ifindex = vrf_ifindex; 556664d6f86SAndrea Mayer 557664d6f86SAndrea Mayer info->family = family; 558664d6f86SAndrea Mayer info->mode = DT_VRF_MODE; 559664d6f86SAndrea Mayer 560664d6f86SAndrea Mayer return 0; 561664d6f86SAndrea Mayer } 562664d6f86SAndrea Mayer 563664d6f86SAndrea Mayer /* The SRv6 End.DT4/DT6 behavior extracts the inner (IPv4/IPv6) packet and 564664d6f86SAndrea Mayer * routes the IPv4/IPv6 packet by looking at the configured routing table. 565664d6f86SAndrea Mayer * 566664d6f86SAndrea Mayer * In the SRv6 End.DT4/DT6 use case, we can receive traffic (IPv6+Segment 567664d6f86SAndrea Mayer * Routing Header packets) from several interfaces and the outer IPv6 568664d6f86SAndrea Mayer * destination address (DA) is used for retrieving the specific instance of the 569664d6f86SAndrea Mayer * End.DT4/DT6 behavior that should process the packets. 570664d6f86SAndrea Mayer * 571664d6f86SAndrea Mayer * However, the inner IPv4/IPv6 packet is not really bound to any receiving 572664d6f86SAndrea Mayer * interface and thus the End.DT4/DT6 sets the VRF (associated with the 573664d6f86SAndrea Mayer * corresponding routing table) as the *receiving* interface. 574664d6f86SAndrea Mayer * In other words, the End.DT4/DT6 processes a packet as if it has been received 575664d6f86SAndrea Mayer * directly by the VRF (and not by one of its slave devices, if any). 576664d6f86SAndrea Mayer * In this way, the VRF interface is used for routing the IPv4/IPv6 packet in 577664d6f86SAndrea Mayer * according to the routing table configured by the End.DT4/DT6 instance. 578664d6f86SAndrea Mayer * 579664d6f86SAndrea Mayer * This design allows you to get some interesting features like: 580664d6f86SAndrea Mayer * 1) the statistics on rx packets; 581664d6f86SAndrea Mayer * 2) the possibility to install a packet sniffer on the receiving interface 582664d6f86SAndrea Mayer * (the VRF one) for looking at the incoming packets; 583664d6f86SAndrea Mayer * 3) the possibility to leverage the netfilter prerouting hook for the inner 584664d6f86SAndrea Mayer * IPv4 packet. 585664d6f86SAndrea Mayer * 586664d6f86SAndrea Mayer * This function returns: 587664d6f86SAndrea Mayer * - the sk_buff* when the VRF rcv handler has processed the packet correctly; 588664d6f86SAndrea Mayer * - NULL when the skb is consumed by the VRF rcv handler; 589664d6f86SAndrea Mayer * - a pointer which encodes a negative error number in case of error. 590664d6f86SAndrea Mayer * Note that in this case, the function takes care of freeing the skb. 591664d6f86SAndrea Mayer */ 592664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_rcv(struct sk_buff *skb, u16 family, 593664d6f86SAndrea Mayer struct net_device *dev) 594664d6f86SAndrea Mayer { 595664d6f86SAndrea Mayer /* based on l3mdev_ip_rcv; we are only interested in the master */ 596664d6f86SAndrea Mayer if (unlikely(!netif_is_l3_master(dev) && !netif_has_l3_rx_handler(dev))) 597664d6f86SAndrea Mayer goto drop; 598664d6f86SAndrea Mayer 599664d6f86SAndrea Mayer if (unlikely(!dev->l3mdev_ops->l3mdev_l3_rcv)) 600664d6f86SAndrea Mayer goto drop; 601664d6f86SAndrea Mayer 602664d6f86SAndrea Mayer /* the decap packet IPv4/IPv6 does not come with any mac header info. 603664d6f86SAndrea Mayer * We must unset the mac header to allow the VRF device to rebuild it, 604664d6f86SAndrea Mayer * just in case there is a sniffer attached on the device. 605664d6f86SAndrea Mayer */ 606664d6f86SAndrea Mayer skb_unset_mac_header(skb); 607664d6f86SAndrea Mayer 608664d6f86SAndrea Mayer skb = dev->l3mdev_ops->l3mdev_l3_rcv(dev, skb, family); 609664d6f86SAndrea Mayer if (!skb) 610664d6f86SAndrea Mayer /* the skb buffer was consumed by the handler */ 611664d6f86SAndrea Mayer return NULL; 612664d6f86SAndrea Mayer 613664d6f86SAndrea Mayer /* when a packet is received by a VRF or by one of its slaves, the 614664d6f86SAndrea Mayer * master device reference is set into the skb. 615664d6f86SAndrea Mayer */ 616664d6f86SAndrea Mayer if (unlikely(skb->dev != dev || skb->skb_iif != dev->ifindex)) 617664d6f86SAndrea Mayer goto drop; 618664d6f86SAndrea Mayer 619664d6f86SAndrea Mayer return skb; 620664d6f86SAndrea Mayer 621664d6f86SAndrea Mayer drop: 622664d6f86SAndrea Mayer kfree_skb(skb); 623664d6f86SAndrea Mayer return ERR_PTR(-EINVAL); 624664d6f86SAndrea Mayer } 625664d6f86SAndrea Mayer 626664d6f86SAndrea Mayer static struct net_device *end_dt_get_vrf_rcu(struct sk_buff *skb, 627664d6f86SAndrea Mayer struct seg6_end_dt_info *info) 628664d6f86SAndrea Mayer { 629664d6f86SAndrea Mayer int vrf_ifindex = info->vrf_ifindex; 630664d6f86SAndrea Mayer struct net *net = info->net; 631664d6f86SAndrea Mayer 632664d6f86SAndrea Mayer if (unlikely(vrf_ifindex < 0)) 633664d6f86SAndrea Mayer goto error; 634664d6f86SAndrea Mayer 635664d6f86SAndrea Mayer if (unlikely(!net_eq(dev_net(skb->dev), net))) 636664d6f86SAndrea Mayer goto error; 637664d6f86SAndrea Mayer 638664d6f86SAndrea Mayer return dev_get_by_index_rcu(net, vrf_ifindex); 639664d6f86SAndrea Mayer 640664d6f86SAndrea Mayer error: 641664d6f86SAndrea Mayer return NULL; 642664d6f86SAndrea Mayer } 643664d6f86SAndrea Mayer 644664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb, 6458b532109SAndrea Mayer struct seg6_local_lwt *slwt, u16 family) 646664d6f86SAndrea Mayer { 647664d6f86SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 648664d6f86SAndrea Mayer struct net_device *vrf; 6498b532109SAndrea Mayer __be16 protocol; 6508b532109SAndrea Mayer int hdrlen; 651664d6f86SAndrea Mayer 652664d6f86SAndrea Mayer vrf = end_dt_get_vrf_rcu(skb, info); 653664d6f86SAndrea Mayer if (unlikely(!vrf)) 654664d6f86SAndrea Mayer goto drop; 655664d6f86SAndrea Mayer 6568b532109SAndrea Mayer switch (family) { 6578b532109SAndrea Mayer case AF_INET: 6588b532109SAndrea Mayer protocol = htons(ETH_P_IP); 6598b532109SAndrea Mayer hdrlen = sizeof(struct iphdr); 6608b532109SAndrea Mayer break; 6618b532109SAndrea Mayer case AF_INET6: 6628b532109SAndrea Mayer protocol = htons(ETH_P_IPV6); 6638b532109SAndrea Mayer hdrlen = sizeof(struct ipv6hdr); 6648b532109SAndrea Mayer break; 6658b532109SAndrea Mayer case AF_UNSPEC: 6668b532109SAndrea Mayer fallthrough; 6678b532109SAndrea Mayer default: 6688b532109SAndrea Mayer goto drop; 6698b532109SAndrea Mayer } 6708b532109SAndrea Mayer 6718b532109SAndrea Mayer if (unlikely(info->family != AF_UNSPEC && info->family != family)) { 6728b532109SAndrea Mayer pr_warn_once("seg6local: SRv6 End.DT* family mismatch"); 6738b532109SAndrea Mayer goto drop; 6748b532109SAndrea Mayer } 6758b532109SAndrea Mayer 6768b532109SAndrea Mayer skb->protocol = protocol; 677664d6f86SAndrea Mayer 678664d6f86SAndrea Mayer skb_dst_drop(skb); 679664d6f86SAndrea Mayer 6808b532109SAndrea Mayer skb_set_transport_header(skb, hdrlen); 681*7a3f5b0dSRyoga Saito nf_reset_ct(skb); 682664d6f86SAndrea Mayer 6838b532109SAndrea Mayer return end_dt_vrf_rcv(skb, family, vrf); 684664d6f86SAndrea Mayer 685664d6f86SAndrea Mayer drop: 686664d6f86SAndrea Mayer kfree_skb(skb); 687664d6f86SAndrea Mayer return ERR_PTR(-EINVAL); 688664d6f86SAndrea Mayer } 689664d6f86SAndrea Mayer 690664d6f86SAndrea Mayer static int input_action_end_dt4(struct sk_buff *skb, 691664d6f86SAndrea Mayer struct seg6_local_lwt *slwt) 692664d6f86SAndrea Mayer { 693664d6f86SAndrea Mayer struct iphdr *iph; 694664d6f86SAndrea Mayer int err; 695664d6f86SAndrea Mayer 696664d6f86SAndrea Mayer if (!decap_and_validate(skb, IPPROTO_IPIP)) 697664d6f86SAndrea Mayer goto drop; 698664d6f86SAndrea Mayer 699664d6f86SAndrea Mayer if (!pskb_may_pull(skb, sizeof(struct iphdr))) 700664d6f86SAndrea Mayer goto drop; 701664d6f86SAndrea Mayer 7028b532109SAndrea Mayer skb = end_dt_vrf_core(skb, slwt, AF_INET); 703664d6f86SAndrea Mayer if (!skb) 704664d6f86SAndrea Mayer /* packet has been processed and consumed by the VRF */ 705664d6f86SAndrea Mayer return 0; 706664d6f86SAndrea Mayer 707664d6f86SAndrea Mayer if (IS_ERR(skb)) 708664d6f86SAndrea Mayer return PTR_ERR(skb); 709664d6f86SAndrea Mayer 710664d6f86SAndrea Mayer iph = ip_hdr(skb); 711664d6f86SAndrea Mayer 712664d6f86SAndrea Mayer err = ip_route_input(skb, iph->daddr, iph->saddr, 0, skb->dev); 713664d6f86SAndrea Mayer if (unlikely(err)) 714664d6f86SAndrea Mayer goto drop; 715664d6f86SAndrea Mayer 716664d6f86SAndrea Mayer return dst_input(skb); 717664d6f86SAndrea Mayer 718664d6f86SAndrea Mayer drop: 719664d6f86SAndrea Mayer kfree_skb(skb); 720664d6f86SAndrea Mayer return -EINVAL; 721664d6f86SAndrea Mayer } 722664d6f86SAndrea Mayer 723664d6f86SAndrea Mayer static int seg6_end_dt4_build(struct seg6_local_lwt *slwt, const void *cfg, 724664d6f86SAndrea Mayer struct netlink_ext_ack *extack) 725664d6f86SAndrea Mayer { 726664d6f86SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET, extack); 727664d6f86SAndrea Mayer } 72820a081b7SAndrea Mayer 72920a081b7SAndrea Mayer static enum 73020a081b7SAndrea Mayer seg6_end_dt_mode seg6_end_dt6_parse_mode(struct seg6_local_lwt *slwt) 73120a081b7SAndrea Mayer { 73220a081b7SAndrea Mayer unsigned long parsed_optattrs = slwt->parsed_optattrs; 73320a081b7SAndrea Mayer bool legacy, vrfmode; 73420a081b7SAndrea Mayer 735300a0fd8SAndrea Mayer legacy = !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE)); 736300a0fd8SAndrea Mayer vrfmode = !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE)); 73720a081b7SAndrea Mayer 73820a081b7SAndrea Mayer if (!(legacy ^ vrfmode)) 73920a081b7SAndrea Mayer /* both are absent or present: invalid DT6 mode */ 74020a081b7SAndrea Mayer return DT_INVALID_MODE; 74120a081b7SAndrea Mayer 74220a081b7SAndrea Mayer return legacy ? DT_LEGACY_MODE : DT_VRF_MODE; 74320a081b7SAndrea Mayer } 74420a081b7SAndrea Mayer 74520a081b7SAndrea Mayer static enum seg6_end_dt_mode seg6_end_dt6_get_mode(struct seg6_local_lwt *slwt) 74620a081b7SAndrea Mayer { 74720a081b7SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 74820a081b7SAndrea Mayer 74920a081b7SAndrea Mayer return info->mode; 75020a081b7SAndrea Mayer } 75120a081b7SAndrea Mayer 75220a081b7SAndrea Mayer static int seg6_end_dt6_build(struct seg6_local_lwt *slwt, const void *cfg, 75320a081b7SAndrea Mayer struct netlink_ext_ack *extack) 75420a081b7SAndrea Mayer { 75520a081b7SAndrea Mayer enum seg6_end_dt_mode mode = seg6_end_dt6_parse_mode(slwt); 75620a081b7SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 75720a081b7SAndrea Mayer 75820a081b7SAndrea Mayer switch (mode) { 75920a081b7SAndrea Mayer case DT_LEGACY_MODE: 76020a081b7SAndrea Mayer info->mode = DT_LEGACY_MODE; 76120a081b7SAndrea Mayer return 0; 76220a081b7SAndrea Mayer case DT_VRF_MODE: 76320a081b7SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET6, extack); 76420a081b7SAndrea Mayer default: 76520a081b7SAndrea Mayer NL_SET_ERR_MSG(extack, "table or vrftable must be specified"); 76620a081b7SAndrea Mayer return -EINVAL; 76720a081b7SAndrea Mayer } 76820a081b7SAndrea Mayer } 769664d6f86SAndrea Mayer #endif 770664d6f86SAndrea Mayer 771891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb, 772891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 773891ef8ddSDavid Lebrun { 774891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 775891ef8ddSDavid Lebrun goto drop; 776891ef8ddSDavid Lebrun 777891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 778891ef8ddSDavid Lebrun goto drop; 779891ef8ddSDavid Lebrun 78020a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 78120a081b7SAndrea Mayer if (seg6_end_dt6_get_mode(slwt) == DT_LEGACY_MODE) 78220a081b7SAndrea Mayer goto legacy_mode; 78320a081b7SAndrea Mayer 78420a081b7SAndrea Mayer /* DT6_VRF_MODE */ 7858b532109SAndrea Mayer skb = end_dt_vrf_core(skb, slwt, AF_INET6); 78620a081b7SAndrea Mayer if (!skb) 78720a081b7SAndrea Mayer /* packet has been processed and consumed by the VRF */ 78820a081b7SAndrea Mayer return 0; 78920a081b7SAndrea Mayer 79020a081b7SAndrea Mayer if (IS_ERR(skb)) 79120a081b7SAndrea Mayer return PTR_ERR(skb); 79220a081b7SAndrea Mayer 79320a081b7SAndrea Mayer /* note: this time we do not need to specify the table because the VRF 79420a081b7SAndrea Mayer * takes care of selecting the correct table. 79520a081b7SAndrea Mayer */ 79620a081b7SAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, 0, true); 79720a081b7SAndrea Mayer 79820a081b7SAndrea Mayer return dst_input(skb); 79920a081b7SAndrea Mayer 80020a081b7SAndrea Mayer legacy_mode: 80120a081b7SAndrea Mayer #endif 802c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 803c71644d0SAndrea Mayer 804fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, slwt->table, true); 805891ef8ddSDavid Lebrun 806891ef8ddSDavid Lebrun return dst_input(skb); 807891ef8ddSDavid Lebrun 808891ef8ddSDavid Lebrun drop: 809891ef8ddSDavid Lebrun kfree_skb(skb); 810891ef8ddSDavid Lebrun return -EINVAL; 811891ef8ddSDavid Lebrun } 812891ef8ddSDavid Lebrun 8138b532109SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 8148b532109SAndrea Mayer static int seg6_end_dt46_build(struct seg6_local_lwt *slwt, const void *cfg, 8158b532109SAndrea Mayer struct netlink_ext_ack *extack) 8168b532109SAndrea Mayer { 8178b532109SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_UNSPEC, extack); 8188b532109SAndrea Mayer } 8198b532109SAndrea Mayer 8208b532109SAndrea Mayer static int input_action_end_dt46(struct sk_buff *skb, 8218b532109SAndrea Mayer struct seg6_local_lwt *slwt) 8228b532109SAndrea Mayer { 8238b532109SAndrea Mayer unsigned int off = 0; 8248b532109SAndrea Mayer int nexthdr; 8258b532109SAndrea Mayer 8268b532109SAndrea Mayer nexthdr = ipv6_find_hdr(skb, &off, -1, NULL, NULL); 8278b532109SAndrea Mayer if (unlikely(nexthdr < 0)) 8288b532109SAndrea Mayer goto drop; 8298b532109SAndrea Mayer 8308b532109SAndrea Mayer switch (nexthdr) { 8318b532109SAndrea Mayer case IPPROTO_IPIP: 8328b532109SAndrea Mayer return input_action_end_dt4(skb, slwt); 8338b532109SAndrea Mayer case IPPROTO_IPV6: 8348b532109SAndrea Mayer return input_action_end_dt6(skb, slwt); 8358b532109SAndrea Mayer } 8368b532109SAndrea Mayer 8378b532109SAndrea Mayer drop: 8388b532109SAndrea Mayer kfree_skb(skb); 8398b532109SAndrea Mayer return -EINVAL; 8408b532109SAndrea Mayer } 8418b532109SAndrea Mayer #endif 8428b532109SAndrea Mayer 843140f04c3SDavid Lebrun /* push an SRH on top of the current one */ 844140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 845140f04c3SDavid Lebrun { 846140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 847140f04c3SDavid Lebrun int err = -EINVAL; 848140f04c3SDavid Lebrun 849140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 850140f04c3SDavid Lebrun if (!srh) 851140f04c3SDavid Lebrun goto drop; 852140f04c3SDavid Lebrun 853140f04c3SDavid Lebrun err = seg6_do_srh_inline(skb, slwt->srh); 854140f04c3SDavid Lebrun if (err) 855140f04c3SDavid Lebrun goto drop; 856140f04c3SDavid Lebrun 857140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 858140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 859140f04c3SDavid Lebrun 8601c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 861140f04c3SDavid Lebrun 862140f04c3SDavid Lebrun return dst_input(skb); 863140f04c3SDavid Lebrun 864140f04c3SDavid Lebrun drop: 865140f04c3SDavid Lebrun kfree_skb(skb); 866140f04c3SDavid Lebrun return err; 867140f04c3SDavid Lebrun } 868140f04c3SDavid Lebrun 869140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */ 870140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb, 871140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 872140f04c3SDavid Lebrun { 873140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 874140f04c3SDavid Lebrun int err = -EINVAL; 875140f04c3SDavid Lebrun 876140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 877140f04c3SDavid Lebrun if (!srh) 878140f04c3SDavid Lebrun goto drop; 879140f04c3SDavid Lebrun 880d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 881140f04c3SDavid Lebrun 882140f04c3SDavid Lebrun skb_reset_inner_headers(skb); 883140f04c3SDavid Lebrun skb->encapsulation = 1; 884140f04c3SDavid Lebrun 88532d99d0bSDavid Lebrun err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6); 886140f04c3SDavid Lebrun if (err) 887140f04c3SDavid Lebrun goto drop; 888140f04c3SDavid Lebrun 889140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 890140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 891140f04c3SDavid Lebrun 8921c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 893140f04c3SDavid Lebrun 894140f04c3SDavid Lebrun return dst_input(skb); 895140f04c3SDavid Lebrun 896140f04c3SDavid Lebrun drop: 897140f04c3SDavid Lebrun kfree_skb(skb); 898140f04c3SDavid Lebrun return err; 899140f04c3SDavid Lebrun } 900140f04c3SDavid Lebrun 901fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states); 902fe94cc29SMathieu Xhonneux 903486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb) 904486cdf21SMathieu Xhonneux { 905486cdf21SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 906486cdf21SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 907486cdf21SMathieu Xhonneux struct ipv6_sr_hdr *srh = srh_state->srh; 908486cdf21SMathieu Xhonneux 909486cdf21SMathieu Xhonneux if (unlikely(srh == NULL)) 910486cdf21SMathieu Xhonneux return false; 911486cdf21SMathieu Xhonneux 912486cdf21SMathieu Xhonneux if (unlikely(!srh_state->valid)) { 913486cdf21SMathieu Xhonneux if ((srh_state->hdrlen & 7) != 0) 914486cdf21SMathieu Xhonneux return false; 915486cdf21SMathieu Xhonneux 916486cdf21SMathieu Xhonneux srh->hdrlen = (u8)(srh_state->hdrlen >> 3); 917bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true)) 918486cdf21SMathieu Xhonneux return false; 919486cdf21SMathieu Xhonneux 920486cdf21SMathieu Xhonneux srh_state->valid = true; 921486cdf21SMathieu Xhonneux } 922486cdf21SMathieu Xhonneux 923486cdf21SMathieu Xhonneux return true; 924486cdf21SMathieu Xhonneux } 925486cdf21SMathieu Xhonneux 926004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb, 927004d4b27SMathieu Xhonneux struct seg6_local_lwt *slwt) 928004d4b27SMathieu Xhonneux { 929004d4b27SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 930004d4b27SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 931004d4b27SMathieu Xhonneux struct ipv6_sr_hdr *srh; 932004d4b27SMathieu Xhonneux int ret; 933004d4b27SMathieu Xhonneux 934004d4b27SMathieu Xhonneux srh = get_and_validate_srh(skb); 935486cdf21SMathieu Xhonneux if (!srh) { 936486cdf21SMathieu Xhonneux kfree_skb(skb); 937486cdf21SMathieu Xhonneux return -EINVAL; 938486cdf21SMathieu Xhonneux } 939004d4b27SMathieu Xhonneux advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 940004d4b27SMathieu Xhonneux 941004d4b27SMathieu Xhonneux /* preempt_disable is needed to protect the per-CPU buffer srh_state, 942004d4b27SMathieu Xhonneux * which is also accessed by the bpf_lwt_seg6_* helpers 943004d4b27SMathieu Xhonneux */ 944004d4b27SMathieu Xhonneux preempt_disable(); 945486cdf21SMathieu Xhonneux srh_state->srh = srh; 946004d4b27SMathieu Xhonneux srh_state->hdrlen = srh->hdrlen << 3; 947486cdf21SMathieu Xhonneux srh_state->valid = true; 948004d4b27SMathieu Xhonneux 949004d4b27SMathieu Xhonneux rcu_read_lock(); 950004d4b27SMathieu Xhonneux bpf_compute_data_pointers(skb); 951004d4b27SMathieu Xhonneux ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb); 952004d4b27SMathieu Xhonneux rcu_read_unlock(); 953004d4b27SMathieu Xhonneux 954004d4b27SMathieu Xhonneux switch (ret) { 955004d4b27SMathieu Xhonneux case BPF_OK: 956004d4b27SMathieu Xhonneux case BPF_REDIRECT: 957004d4b27SMathieu Xhonneux break; 958004d4b27SMathieu Xhonneux case BPF_DROP: 959004d4b27SMathieu Xhonneux goto drop; 960004d4b27SMathieu Xhonneux default: 961004d4b27SMathieu Xhonneux pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret); 962004d4b27SMathieu Xhonneux goto drop; 963004d4b27SMathieu Xhonneux } 964004d4b27SMathieu Xhonneux 965486cdf21SMathieu Xhonneux if (srh_state->srh && !seg6_bpf_has_valid_srh(skb)) 966004d4b27SMathieu Xhonneux goto drop; 967004d4b27SMathieu Xhonneux 968486cdf21SMathieu Xhonneux preempt_enable(); 969004d4b27SMathieu Xhonneux if (ret != BPF_REDIRECT) 970004d4b27SMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 971004d4b27SMathieu Xhonneux 972004d4b27SMathieu Xhonneux return dst_input(skb); 973004d4b27SMathieu Xhonneux 974004d4b27SMathieu Xhonneux drop: 975486cdf21SMathieu Xhonneux preempt_enable(); 976004d4b27SMathieu Xhonneux kfree_skb(skb); 977004d4b27SMathieu Xhonneux return -EINVAL; 978004d4b27SMathieu Xhonneux } 979004d4b27SMathieu Xhonneux 980d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = { 981d1df6fd8SDavid Lebrun { 982d1df6fd8SDavid Lebrun .action = SEG6_LOCAL_ACTION_END, 983d1df6fd8SDavid Lebrun .attrs = 0, 98494604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 985140f04c3SDavid Lebrun .input = input_action_end, 986d1df6fd8SDavid Lebrun }, 987140f04c3SDavid Lebrun { 988140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_X, 989300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), 99094604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 991140f04c3SDavid Lebrun .input = input_action_end_x, 992140f04c3SDavid Lebrun }, 993140f04c3SDavid Lebrun { 994891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_T, 995300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE), 99694604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 997891ef8ddSDavid Lebrun .input = input_action_end_t, 998891ef8ddSDavid Lebrun }, 999891ef8ddSDavid Lebrun { 1000891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX2, 1001300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_OIF), 100294604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1003891ef8ddSDavid Lebrun .input = input_action_end_dx2, 1004891ef8ddSDavid Lebrun }, 1005891ef8ddSDavid Lebrun { 1006140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX6, 1007300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), 100894604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1009140f04c3SDavid Lebrun .input = input_action_end_dx6, 1010140f04c3SDavid Lebrun }, 1011140f04c3SDavid Lebrun { 1012891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX4, 1013300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH4), 101494604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1015891ef8ddSDavid Lebrun .input = input_action_end_dx4, 1016891ef8ddSDavid Lebrun }, 1017891ef8ddSDavid Lebrun { 1018664d6f86SAndrea Mayer .action = SEG6_LOCAL_ACTION_END_DT4, 1019300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 102094604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1021664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 1022664d6f86SAndrea Mayer .input = input_action_end_dt4, 1023664d6f86SAndrea Mayer .slwt_ops = { 1024664d6f86SAndrea Mayer .build_state = seg6_end_dt4_build, 1025664d6f86SAndrea Mayer }, 1026664d6f86SAndrea Mayer #endif 1027664d6f86SAndrea Mayer }, 1028664d6f86SAndrea Mayer { 1029891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DT6, 103020a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 103120a081b7SAndrea Mayer .attrs = 0, 103294604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS | 103394604548SAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_TABLE) | 1034300a0fd8SAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 103520a081b7SAndrea Mayer .slwt_ops = { 103620a081b7SAndrea Mayer .build_state = seg6_end_dt6_build, 103720a081b7SAndrea Mayer }, 103820a081b7SAndrea Mayer #else 1039300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE), 104094604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 104120a081b7SAndrea Mayer #endif 1042891ef8ddSDavid Lebrun .input = input_action_end_dt6, 1043891ef8ddSDavid Lebrun }, 1044891ef8ddSDavid Lebrun { 10458b532109SAndrea Mayer .action = SEG6_LOCAL_ACTION_END_DT46, 10468b532109SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 10478b532109SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 10488b532109SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 10498b532109SAndrea Mayer .input = input_action_end_dt46, 10508b532109SAndrea Mayer .slwt_ops = { 10518b532109SAndrea Mayer .build_state = seg6_end_dt46_build, 10528b532109SAndrea Mayer }, 10538b532109SAndrea Mayer #endif 10548b532109SAndrea Mayer }, 10558b532109SAndrea Mayer { 1056140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6, 1057300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH), 105894604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1059140f04c3SDavid Lebrun .input = input_action_end_b6, 1060140f04c3SDavid Lebrun }, 1061140f04c3SDavid Lebrun { 1062140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6_ENCAP, 1063300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH), 106494604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1065140f04c3SDavid Lebrun .input = input_action_end_b6_encap, 1066140f04c3SDavid Lebrun .static_headroom = sizeof(struct ipv6hdr), 1067004d4b27SMathieu Xhonneux }, 1068004d4b27SMathieu Xhonneux { 1069004d4b27SMathieu Xhonneux .action = SEG6_LOCAL_ACTION_END_BPF, 1070300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_BPF), 107194604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 1072004d4b27SMathieu Xhonneux .input = input_action_end_bpf, 1073004d4b27SMathieu Xhonneux }, 1074004d4b27SMathieu Xhonneux 1075d1df6fd8SDavid Lebrun }; 1076d1df6fd8SDavid Lebrun 1077d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action) 1078d1df6fd8SDavid Lebrun { 1079d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1080d1df6fd8SDavid Lebrun int i, count; 1081d1df6fd8SDavid Lebrun 1082709af180SColin Ian King count = ARRAY_SIZE(seg6_action_table); 1083d1df6fd8SDavid Lebrun for (i = 0; i < count; i++) { 1084d1df6fd8SDavid Lebrun desc = &seg6_action_table[i]; 1085d1df6fd8SDavid Lebrun if (desc->action == action) 1086d1df6fd8SDavid Lebrun return desc; 1087d1df6fd8SDavid Lebrun } 1088d1df6fd8SDavid Lebrun 1089d1df6fd8SDavid Lebrun return NULL; 1090d1df6fd8SDavid Lebrun } 1091d1df6fd8SDavid Lebrun 109294604548SAndrea Mayer static bool seg6_lwtunnel_counters_enabled(struct seg6_local_lwt *slwt) 109394604548SAndrea Mayer { 109494604548SAndrea Mayer return slwt->parsed_optattrs & SEG6_F_LOCAL_COUNTERS; 109594604548SAndrea Mayer } 109694604548SAndrea Mayer 109794604548SAndrea Mayer static void seg6_local_update_counters(struct seg6_local_lwt *slwt, 109894604548SAndrea Mayer unsigned int len, int err) 109994604548SAndrea Mayer { 110094604548SAndrea Mayer struct pcpu_seg6_local_counters *pcounters; 110194604548SAndrea Mayer 110294604548SAndrea Mayer pcounters = this_cpu_ptr(slwt->pcpu_counters); 110394604548SAndrea Mayer u64_stats_update_begin(&pcounters->syncp); 110494604548SAndrea Mayer 110594604548SAndrea Mayer if (likely(!err)) { 110694604548SAndrea Mayer u64_stats_inc(&pcounters->packets); 110794604548SAndrea Mayer u64_stats_add(&pcounters->bytes, len); 110894604548SAndrea Mayer } else { 110994604548SAndrea Mayer u64_stats_inc(&pcounters->errors); 111094604548SAndrea Mayer } 111194604548SAndrea Mayer 111294604548SAndrea Mayer u64_stats_update_end(&pcounters->syncp); 111394604548SAndrea Mayer } 111494604548SAndrea Mayer 1115*7a3f5b0dSRyoga Saito static int seg6_local_input_core(struct net *net, struct sock *sk, 1116*7a3f5b0dSRyoga Saito struct sk_buff *skb) 1117d1df6fd8SDavid Lebrun { 1118d1df6fd8SDavid Lebrun struct dst_entry *orig_dst = skb_dst(skb); 1119d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1120d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 112194604548SAndrea Mayer unsigned int len = skb->len; 112294604548SAndrea Mayer int rc; 1123d1df6fd8SDavid Lebrun 1124d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 1125d1df6fd8SDavid Lebrun desc = slwt->desc; 1126d1df6fd8SDavid Lebrun 112794604548SAndrea Mayer rc = desc->input(skb, slwt); 112894604548SAndrea Mayer 112994604548SAndrea Mayer if (!seg6_lwtunnel_counters_enabled(slwt)) 113094604548SAndrea Mayer return rc; 113194604548SAndrea Mayer 113294604548SAndrea Mayer seg6_local_update_counters(slwt, len, rc); 113394604548SAndrea Mayer 113494604548SAndrea Mayer return rc; 1135d1df6fd8SDavid Lebrun } 1136d1df6fd8SDavid Lebrun 1137*7a3f5b0dSRyoga Saito static int seg6_local_input(struct sk_buff *skb) 1138*7a3f5b0dSRyoga Saito { 1139*7a3f5b0dSRyoga Saito if (skb->protocol != htons(ETH_P_IPV6)) { 1140*7a3f5b0dSRyoga Saito kfree_skb(skb); 1141*7a3f5b0dSRyoga Saito return -EINVAL; 1142*7a3f5b0dSRyoga Saito } 1143*7a3f5b0dSRyoga Saito 1144*7a3f5b0dSRyoga Saito if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 1145*7a3f5b0dSRyoga Saito return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN, 1146*7a3f5b0dSRyoga Saito dev_net(skb->dev), NULL, skb, skb->dev, NULL, 1147*7a3f5b0dSRyoga Saito seg6_local_input_core); 1148*7a3f5b0dSRyoga Saito 1149*7a3f5b0dSRyoga Saito return seg6_local_input_core(dev_net(skb->dev), NULL, skb); 1150*7a3f5b0dSRyoga Saito } 1151*7a3f5b0dSRyoga Saito 1152d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { 1153d1df6fd8SDavid Lebrun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, 1154d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, 1155d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, 1156664d6f86SAndrea Mayer [SEG6_LOCAL_VRFTABLE] = { .type = NLA_U32 }, 1157d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, 1158d1df6fd8SDavid Lebrun .len = sizeof(struct in_addr) }, 1159d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, 1160d1df6fd8SDavid Lebrun .len = sizeof(struct in6_addr) }, 1161d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, 1162d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 1163004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .type = NLA_NESTED }, 116494604548SAndrea Mayer [SEG6_LOCAL_COUNTERS] = { .type = NLA_NESTED }, 1165d1df6fd8SDavid Lebrun }; 1166d1df6fd8SDavid Lebrun 11672d9cc60aSDavid Lebrun static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt) 11682d9cc60aSDavid Lebrun { 11692d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 11702d9cc60aSDavid Lebrun int len; 11712d9cc60aSDavid Lebrun 11722d9cc60aSDavid Lebrun srh = nla_data(attrs[SEG6_LOCAL_SRH]); 11732d9cc60aSDavid Lebrun len = nla_len(attrs[SEG6_LOCAL_SRH]); 11742d9cc60aSDavid Lebrun 11752d9cc60aSDavid Lebrun /* SRH must contain at least one segment */ 11762d9cc60aSDavid Lebrun if (len < sizeof(*srh) + sizeof(struct in6_addr)) 11772d9cc60aSDavid Lebrun return -EINVAL; 11782d9cc60aSDavid Lebrun 1179bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, false)) 11802d9cc60aSDavid Lebrun return -EINVAL; 11812d9cc60aSDavid Lebrun 11827fa41efaSYueHaibing slwt->srh = kmemdup(srh, len, GFP_KERNEL); 11832d9cc60aSDavid Lebrun if (!slwt->srh) 11842d9cc60aSDavid Lebrun return -ENOMEM; 11852d9cc60aSDavid Lebrun 11862d9cc60aSDavid Lebrun slwt->headroom += len; 11872d9cc60aSDavid Lebrun 11882d9cc60aSDavid Lebrun return 0; 11892d9cc60aSDavid Lebrun } 11902d9cc60aSDavid Lebrun 11912d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt) 11922d9cc60aSDavid Lebrun { 11932d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 11942d9cc60aSDavid Lebrun struct nlattr *nla; 11952d9cc60aSDavid Lebrun int len; 11962d9cc60aSDavid Lebrun 11972d9cc60aSDavid Lebrun srh = slwt->srh; 11982d9cc60aSDavid Lebrun len = (srh->hdrlen + 1) << 3; 11992d9cc60aSDavid Lebrun 12002d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_SRH, len); 12012d9cc60aSDavid Lebrun if (!nla) 12022d9cc60aSDavid Lebrun return -EMSGSIZE; 12032d9cc60aSDavid Lebrun 12042d9cc60aSDavid Lebrun memcpy(nla_data(nla), srh, len); 12052d9cc60aSDavid Lebrun 12062d9cc60aSDavid Lebrun return 0; 12072d9cc60aSDavid Lebrun } 12082d9cc60aSDavid Lebrun 12092d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 12102d9cc60aSDavid Lebrun { 12112d9cc60aSDavid Lebrun int len = (a->srh->hdrlen + 1) << 3; 12122d9cc60aSDavid Lebrun 12132d9cc60aSDavid Lebrun if (len != ((b->srh->hdrlen + 1) << 3)) 12142d9cc60aSDavid Lebrun return 1; 12152d9cc60aSDavid Lebrun 12162d9cc60aSDavid Lebrun return memcmp(a->srh, b->srh, len); 12172d9cc60aSDavid Lebrun } 12182d9cc60aSDavid Lebrun 1219964adce5SAndrea Mayer static void destroy_attr_srh(struct seg6_local_lwt *slwt) 1220964adce5SAndrea Mayer { 1221964adce5SAndrea Mayer kfree(slwt->srh); 1222964adce5SAndrea Mayer } 1223964adce5SAndrea Mayer 12242d9cc60aSDavid Lebrun static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt) 12252d9cc60aSDavid Lebrun { 12262d9cc60aSDavid Lebrun slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]); 12272d9cc60aSDavid Lebrun 12282d9cc60aSDavid Lebrun return 0; 12292d9cc60aSDavid Lebrun } 12302d9cc60aSDavid Lebrun 12312d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt) 12322d9cc60aSDavid Lebrun { 12332d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table)) 12342d9cc60aSDavid Lebrun return -EMSGSIZE; 12352d9cc60aSDavid Lebrun 12362d9cc60aSDavid Lebrun return 0; 12372d9cc60aSDavid Lebrun } 12382d9cc60aSDavid Lebrun 12392d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 12402d9cc60aSDavid Lebrun { 12412d9cc60aSDavid Lebrun if (a->table != b->table) 12422d9cc60aSDavid Lebrun return 1; 12432d9cc60aSDavid Lebrun 12442d9cc60aSDavid Lebrun return 0; 12452d9cc60aSDavid Lebrun } 12462d9cc60aSDavid Lebrun 1247664d6f86SAndrea Mayer static struct 1248664d6f86SAndrea Mayer seg6_end_dt_info *seg6_possible_end_dt_info(struct seg6_local_lwt *slwt) 1249664d6f86SAndrea Mayer { 1250664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 1251664d6f86SAndrea Mayer return &slwt->dt_info; 1252664d6f86SAndrea Mayer #else 1253664d6f86SAndrea Mayer return ERR_PTR(-EOPNOTSUPP); 1254664d6f86SAndrea Mayer #endif 1255664d6f86SAndrea Mayer } 1256664d6f86SAndrea Mayer 1257664d6f86SAndrea Mayer static int parse_nla_vrftable(struct nlattr **attrs, 1258664d6f86SAndrea Mayer struct seg6_local_lwt *slwt) 1259664d6f86SAndrea Mayer { 1260664d6f86SAndrea Mayer struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt); 1261664d6f86SAndrea Mayer 1262664d6f86SAndrea Mayer if (IS_ERR(info)) 1263664d6f86SAndrea Mayer return PTR_ERR(info); 1264664d6f86SAndrea Mayer 1265664d6f86SAndrea Mayer info->vrf_table = nla_get_u32(attrs[SEG6_LOCAL_VRFTABLE]); 1266664d6f86SAndrea Mayer 1267664d6f86SAndrea Mayer return 0; 1268664d6f86SAndrea Mayer } 1269664d6f86SAndrea Mayer 1270664d6f86SAndrea Mayer static int put_nla_vrftable(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1271664d6f86SAndrea Mayer { 1272664d6f86SAndrea Mayer struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt); 1273664d6f86SAndrea Mayer 1274664d6f86SAndrea Mayer if (IS_ERR(info)) 1275664d6f86SAndrea Mayer return PTR_ERR(info); 1276664d6f86SAndrea Mayer 1277664d6f86SAndrea Mayer if (nla_put_u32(skb, SEG6_LOCAL_VRFTABLE, info->vrf_table)) 1278664d6f86SAndrea Mayer return -EMSGSIZE; 1279664d6f86SAndrea Mayer 1280664d6f86SAndrea Mayer return 0; 1281664d6f86SAndrea Mayer } 1282664d6f86SAndrea Mayer 1283664d6f86SAndrea Mayer static int cmp_nla_vrftable(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1284664d6f86SAndrea Mayer { 1285664d6f86SAndrea Mayer struct seg6_end_dt_info *info_a = seg6_possible_end_dt_info(a); 1286664d6f86SAndrea Mayer struct seg6_end_dt_info *info_b = seg6_possible_end_dt_info(b); 1287664d6f86SAndrea Mayer 1288664d6f86SAndrea Mayer if (info_a->vrf_table != info_b->vrf_table) 1289664d6f86SAndrea Mayer return 1; 1290664d6f86SAndrea Mayer 1291664d6f86SAndrea Mayer return 0; 1292664d6f86SAndrea Mayer } 1293664d6f86SAndrea Mayer 12942d9cc60aSDavid Lebrun static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt) 12952d9cc60aSDavid Lebrun { 12962d9cc60aSDavid Lebrun memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]), 12972d9cc60aSDavid Lebrun sizeof(struct in_addr)); 12982d9cc60aSDavid Lebrun 12992d9cc60aSDavid Lebrun return 0; 13002d9cc60aSDavid Lebrun } 13012d9cc60aSDavid Lebrun 13022d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt) 13032d9cc60aSDavid Lebrun { 13042d9cc60aSDavid Lebrun struct nlattr *nla; 13052d9cc60aSDavid Lebrun 13062d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr)); 13072d9cc60aSDavid Lebrun if (!nla) 13082d9cc60aSDavid Lebrun return -EMSGSIZE; 13092d9cc60aSDavid Lebrun 13102d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr)); 13112d9cc60aSDavid Lebrun 13122d9cc60aSDavid Lebrun return 0; 13132d9cc60aSDavid Lebrun } 13142d9cc60aSDavid Lebrun 13152d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 13162d9cc60aSDavid Lebrun { 13172d9cc60aSDavid Lebrun return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr)); 13182d9cc60aSDavid Lebrun } 13192d9cc60aSDavid Lebrun 13202d9cc60aSDavid Lebrun static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt) 13212d9cc60aSDavid Lebrun { 13222d9cc60aSDavid Lebrun memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]), 13232d9cc60aSDavid Lebrun sizeof(struct in6_addr)); 13242d9cc60aSDavid Lebrun 13252d9cc60aSDavid Lebrun return 0; 13262d9cc60aSDavid Lebrun } 13272d9cc60aSDavid Lebrun 13282d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 13292d9cc60aSDavid Lebrun { 13302d9cc60aSDavid Lebrun struct nlattr *nla; 13312d9cc60aSDavid Lebrun 13322d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr)); 13332d9cc60aSDavid Lebrun if (!nla) 13342d9cc60aSDavid Lebrun return -EMSGSIZE; 13352d9cc60aSDavid Lebrun 13362d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr)); 13372d9cc60aSDavid Lebrun 13382d9cc60aSDavid Lebrun return 0; 13392d9cc60aSDavid Lebrun } 13402d9cc60aSDavid Lebrun 13412d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 13422d9cc60aSDavid Lebrun { 13432d9cc60aSDavid Lebrun return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr)); 13442d9cc60aSDavid Lebrun } 13452d9cc60aSDavid Lebrun 13462d9cc60aSDavid Lebrun static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 13472d9cc60aSDavid Lebrun { 13482d9cc60aSDavid Lebrun slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]); 13492d9cc60aSDavid Lebrun 13502d9cc60aSDavid Lebrun return 0; 13512d9cc60aSDavid Lebrun } 13522d9cc60aSDavid Lebrun 13532d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 13542d9cc60aSDavid Lebrun { 13552d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif)) 13562d9cc60aSDavid Lebrun return -EMSGSIZE; 13572d9cc60aSDavid Lebrun 13582d9cc60aSDavid Lebrun return 0; 13592d9cc60aSDavid Lebrun } 13602d9cc60aSDavid Lebrun 13612d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 13622d9cc60aSDavid Lebrun { 13632d9cc60aSDavid Lebrun if (a->iif != b->iif) 13642d9cc60aSDavid Lebrun return 1; 13652d9cc60aSDavid Lebrun 13662d9cc60aSDavid Lebrun return 0; 13672d9cc60aSDavid Lebrun } 13682d9cc60aSDavid Lebrun 13692d9cc60aSDavid Lebrun static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 13702d9cc60aSDavid Lebrun { 13712d9cc60aSDavid Lebrun slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]); 13722d9cc60aSDavid Lebrun 13732d9cc60aSDavid Lebrun return 0; 13742d9cc60aSDavid Lebrun } 13752d9cc60aSDavid Lebrun 13762d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 13772d9cc60aSDavid Lebrun { 13782d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif)) 13792d9cc60aSDavid Lebrun return -EMSGSIZE; 13802d9cc60aSDavid Lebrun 13812d9cc60aSDavid Lebrun return 0; 13822d9cc60aSDavid Lebrun } 13832d9cc60aSDavid Lebrun 13842d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 13852d9cc60aSDavid Lebrun { 13862d9cc60aSDavid Lebrun if (a->oif != b->oif) 13872d9cc60aSDavid Lebrun return 1; 13882d9cc60aSDavid Lebrun 13892d9cc60aSDavid Lebrun return 0; 13902d9cc60aSDavid Lebrun } 13912d9cc60aSDavid Lebrun 1392004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256 1393004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = { 1394004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG] = { .type = NLA_U32, }, 1395004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING, 1396004d4b27SMathieu Xhonneux .len = MAX_PROG_NAME }, 1397004d4b27SMathieu Xhonneux }; 1398004d4b27SMathieu Xhonneux 1399004d4b27SMathieu Xhonneux static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt) 1400004d4b27SMathieu Xhonneux { 1401004d4b27SMathieu Xhonneux struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1]; 1402004d4b27SMathieu Xhonneux struct bpf_prog *p; 1403004d4b27SMathieu Xhonneux int ret; 1404004d4b27SMathieu Xhonneux u32 fd; 1405004d4b27SMathieu Xhonneux 14068cb08174SJohannes Berg ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX, 14078cb08174SJohannes Berg attrs[SEG6_LOCAL_BPF], 14088cb08174SJohannes Berg bpf_prog_policy, NULL); 1409004d4b27SMathieu Xhonneux if (ret < 0) 1410004d4b27SMathieu Xhonneux return ret; 1411004d4b27SMathieu Xhonneux 1412004d4b27SMathieu Xhonneux if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME]) 1413004d4b27SMathieu Xhonneux return -EINVAL; 1414004d4b27SMathieu Xhonneux 1415004d4b27SMathieu Xhonneux slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL); 1416004d4b27SMathieu Xhonneux if (!slwt->bpf.name) 1417004d4b27SMathieu Xhonneux return -ENOMEM; 1418004d4b27SMathieu Xhonneux 1419004d4b27SMathieu Xhonneux fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]); 1420004d4b27SMathieu Xhonneux p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL); 1421004d4b27SMathieu Xhonneux if (IS_ERR(p)) { 1422004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 1423004d4b27SMathieu Xhonneux return PTR_ERR(p); 1424004d4b27SMathieu Xhonneux } 1425004d4b27SMathieu Xhonneux 1426004d4b27SMathieu Xhonneux slwt->bpf.prog = p; 1427004d4b27SMathieu Xhonneux return 0; 1428004d4b27SMathieu Xhonneux } 1429004d4b27SMathieu Xhonneux 1430004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1431004d4b27SMathieu Xhonneux { 1432004d4b27SMathieu Xhonneux struct nlattr *nest; 1433004d4b27SMathieu Xhonneux 1434004d4b27SMathieu Xhonneux if (!slwt->bpf.prog) 1435004d4b27SMathieu Xhonneux return 0; 1436004d4b27SMathieu Xhonneux 1437ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF); 1438004d4b27SMathieu Xhonneux if (!nest) 1439004d4b27SMathieu Xhonneux return -EMSGSIZE; 1440004d4b27SMathieu Xhonneux 1441004d4b27SMathieu Xhonneux if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id)) 1442004d4b27SMathieu Xhonneux return -EMSGSIZE; 1443004d4b27SMathieu Xhonneux 1444004d4b27SMathieu Xhonneux if (slwt->bpf.name && 1445004d4b27SMathieu Xhonneux nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name)) 1446004d4b27SMathieu Xhonneux return -EMSGSIZE; 1447004d4b27SMathieu Xhonneux 1448004d4b27SMathieu Xhonneux return nla_nest_end(skb, nest); 1449004d4b27SMathieu Xhonneux } 1450004d4b27SMathieu Xhonneux 1451004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1452004d4b27SMathieu Xhonneux { 1453004d4b27SMathieu Xhonneux if (!a->bpf.name && !b->bpf.name) 1454004d4b27SMathieu Xhonneux return 0; 1455004d4b27SMathieu Xhonneux 1456004d4b27SMathieu Xhonneux if (!a->bpf.name || !b->bpf.name) 1457004d4b27SMathieu Xhonneux return 1; 1458004d4b27SMathieu Xhonneux 1459004d4b27SMathieu Xhonneux return strcmp(a->bpf.name, b->bpf.name); 1460004d4b27SMathieu Xhonneux } 1461004d4b27SMathieu Xhonneux 1462964adce5SAndrea Mayer static void destroy_attr_bpf(struct seg6_local_lwt *slwt) 1463964adce5SAndrea Mayer { 1464964adce5SAndrea Mayer kfree(slwt->bpf.name); 1465964adce5SAndrea Mayer if (slwt->bpf.prog) 1466964adce5SAndrea Mayer bpf_prog_put(slwt->bpf.prog); 1467964adce5SAndrea Mayer } 1468964adce5SAndrea Mayer 146994604548SAndrea Mayer static const struct 147094604548SAndrea Mayer nla_policy seg6_local_counters_policy[SEG6_LOCAL_CNT_MAX + 1] = { 147194604548SAndrea Mayer [SEG6_LOCAL_CNT_PACKETS] = { .type = NLA_U64 }, 147294604548SAndrea Mayer [SEG6_LOCAL_CNT_BYTES] = { .type = NLA_U64 }, 147394604548SAndrea Mayer [SEG6_LOCAL_CNT_ERRORS] = { .type = NLA_U64 }, 147494604548SAndrea Mayer }; 147594604548SAndrea Mayer 147694604548SAndrea Mayer static int parse_nla_counters(struct nlattr **attrs, 147794604548SAndrea Mayer struct seg6_local_lwt *slwt) 147894604548SAndrea Mayer { 147994604548SAndrea Mayer struct pcpu_seg6_local_counters __percpu *pcounters; 148094604548SAndrea Mayer struct nlattr *tb[SEG6_LOCAL_CNT_MAX + 1]; 148194604548SAndrea Mayer int ret; 148294604548SAndrea Mayer 148394604548SAndrea Mayer ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_CNT_MAX, 148494604548SAndrea Mayer attrs[SEG6_LOCAL_COUNTERS], 148594604548SAndrea Mayer seg6_local_counters_policy, NULL); 148694604548SAndrea Mayer if (ret < 0) 148794604548SAndrea Mayer return ret; 148894604548SAndrea Mayer 148994604548SAndrea Mayer /* basic support for SRv6 Behavior counters requires at least: 149094604548SAndrea Mayer * packets, bytes and errors. 149194604548SAndrea Mayer */ 149294604548SAndrea Mayer if (!tb[SEG6_LOCAL_CNT_PACKETS] || !tb[SEG6_LOCAL_CNT_BYTES] || 149394604548SAndrea Mayer !tb[SEG6_LOCAL_CNT_ERRORS]) 149494604548SAndrea Mayer return -EINVAL; 149594604548SAndrea Mayer 149694604548SAndrea Mayer /* counters are always zero initialized */ 149794604548SAndrea Mayer pcounters = seg6_local_alloc_pcpu_counters(GFP_KERNEL); 149894604548SAndrea Mayer if (!pcounters) 149994604548SAndrea Mayer return -ENOMEM; 150094604548SAndrea Mayer 150194604548SAndrea Mayer slwt->pcpu_counters = pcounters; 150294604548SAndrea Mayer 150394604548SAndrea Mayer return 0; 150494604548SAndrea Mayer } 150594604548SAndrea Mayer 150694604548SAndrea Mayer static int seg6_local_fill_nla_counters(struct sk_buff *skb, 150794604548SAndrea Mayer struct seg6_local_counters *counters) 150894604548SAndrea Mayer { 150994604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_PACKETS, counters->packets, 151094604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 151194604548SAndrea Mayer return -EMSGSIZE; 151294604548SAndrea Mayer 151394604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_BYTES, counters->bytes, 151494604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 151594604548SAndrea Mayer return -EMSGSIZE; 151694604548SAndrea Mayer 151794604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_ERRORS, counters->errors, 151894604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 151994604548SAndrea Mayer return -EMSGSIZE; 152094604548SAndrea Mayer 152194604548SAndrea Mayer return 0; 152294604548SAndrea Mayer } 152394604548SAndrea Mayer 152494604548SAndrea Mayer static int put_nla_counters(struct sk_buff *skb, struct seg6_local_lwt *slwt) 152594604548SAndrea Mayer { 152694604548SAndrea Mayer struct seg6_local_counters counters = { 0, 0, 0 }; 152794604548SAndrea Mayer struct nlattr *nest; 152894604548SAndrea Mayer int rc, i; 152994604548SAndrea Mayer 153094604548SAndrea Mayer nest = nla_nest_start(skb, SEG6_LOCAL_COUNTERS); 153194604548SAndrea Mayer if (!nest) 153294604548SAndrea Mayer return -EMSGSIZE; 153394604548SAndrea Mayer 153494604548SAndrea Mayer for_each_possible_cpu(i) { 153594604548SAndrea Mayer struct pcpu_seg6_local_counters *pcounters; 153694604548SAndrea Mayer u64 packets, bytes, errors; 153794604548SAndrea Mayer unsigned int start; 153894604548SAndrea Mayer 153994604548SAndrea Mayer pcounters = per_cpu_ptr(slwt->pcpu_counters, i); 154094604548SAndrea Mayer do { 154194604548SAndrea Mayer start = u64_stats_fetch_begin_irq(&pcounters->syncp); 154294604548SAndrea Mayer 154394604548SAndrea Mayer packets = u64_stats_read(&pcounters->packets); 154494604548SAndrea Mayer bytes = u64_stats_read(&pcounters->bytes); 154594604548SAndrea Mayer errors = u64_stats_read(&pcounters->errors); 154694604548SAndrea Mayer 154794604548SAndrea Mayer } while (u64_stats_fetch_retry_irq(&pcounters->syncp, start)); 154894604548SAndrea Mayer 154994604548SAndrea Mayer counters.packets += packets; 155094604548SAndrea Mayer counters.bytes += bytes; 155194604548SAndrea Mayer counters.errors += errors; 155294604548SAndrea Mayer } 155394604548SAndrea Mayer 155494604548SAndrea Mayer rc = seg6_local_fill_nla_counters(skb, &counters); 155594604548SAndrea Mayer if (rc < 0) { 155694604548SAndrea Mayer nla_nest_cancel(skb, nest); 155794604548SAndrea Mayer return rc; 155894604548SAndrea Mayer } 155994604548SAndrea Mayer 156094604548SAndrea Mayer return nla_nest_end(skb, nest); 156194604548SAndrea Mayer } 156294604548SAndrea Mayer 156394604548SAndrea Mayer static int cmp_nla_counters(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 156494604548SAndrea Mayer { 156594604548SAndrea Mayer /* a and b are equal if both have pcpu_counters set or not */ 156694604548SAndrea Mayer return (!!((unsigned long)a->pcpu_counters)) ^ 156794604548SAndrea Mayer (!!((unsigned long)b->pcpu_counters)); 156894604548SAndrea Mayer } 156994604548SAndrea Mayer 157094604548SAndrea Mayer static void destroy_attr_counters(struct seg6_local_lwt *slwt) 157194604548SAndrea Mayer { 157294604548SAndrea Mayer free_percpu(slwt->pcpu_counters); 157394604548SAndrea Mayer } 157494604548SAndrea Mayer 1575d1df6fd8SDavid Lebrun struct seg6_action_param { 1576d1df6fd8SDavid Lebrun int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt); 1577d1df6fd8SDavid Lebrun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 1578d1df6fd8SDavid Lebrun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); 1579964adce5SAndrea Mayer 1580964adce5SAndrea Mayer /* optional destroy() callback useful for releasing resources which 1581964adce5SAndrea Mayer * have been previously acquired in the corresponding parse() 1582964adce5SAndrea Mayer * function. 1583964adce5SAndrea Mayer */ 1584964adce5SAndrea Mayer void (*destroy)(struct seg6_local_lwt *slwt); 1585d1df6fd8SDavid Lebrun }; 1586d1df6fd8SDavid Lebrun 1587d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { 15882d9cc60aSDavid Lebrun [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh, 15892d9cc60aSDavid Lebrun .put = put_nla_srh, 1590964adce5SAndrea Mayer .cmp = cmp_nla_srh, 1591964adce5SAndrea Mayer .destroy = destroy_attr_srh }, 1592d1df6fd8SDavid Lebrun 15932d9cc60aSDavid Lebrun [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table, 15942d9cc60aSDavid Lebrun .put = put_nla_table, 15952d9cc60aSDavid Lebrun .cmp = cmp_nla_table }, 1596d1df6fd8SDavid Lebrun 15972d9cc60aSDavid Lebrun [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4, 15982d9cc60aSDavid Lebrun .put = put_nla_nh4, 15992d9cc60aSDavid Lebrun .cmp = cmp_nla_nh4 }, 1600d1df6fd8SDavid Lebrun 16012d9cc60aSDavid Lebrun [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6, 16022d9cc60aSDavid Lebrun .put = put_nla_nh6, 16032d9cc60aSDavid Lebrun .cmp = cmp_nla_nh6 }, 1604d1df6fd8SDavid Lebrun 16052d9cc60aSDavid Lebrun [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif, 16062d9cc60aSDavid Lebrun .put = put_nla_iif, 16072d9cc60aSDavid Lebrun .cmp = cmp_nla_iif }, 1608d1df6fd8SDavid Lebrun 16092d9cc60aSDavid Lebrun [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif, 16102d9cc60aSDavid Lebrun .put = put_nla_oif, 16112d9cc60aSDavid Lebrun .cmp = cmp_nla_oif }, 1612004d4b27SMathieu Xhonneux 1613004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .parse = parse_nla_bpf, 1614004d4b27SMathieu Xhonneux .put = put_nla_bpf, 1615964adce5SAndrea Mayer .cmp = cmp_nla_bpf, 1616964adce5SAndrea Mayer .destroy = destroy_attr_bpf }, 1617004d4b27SMathieu Xhonneux 1618664d6f86SAndrea Mayer [SEG6_LOCAL_VRFTABLE] = { .parse = parse_nla_vrftable, 1619664d6f86SAndrea Mayer .put = put_nla_vrftable, 1620664d6f86SAndrea Mayer .cmp = cmp_nla_vrftable }, 1621664d6f86SAndrea Mayer 162294604548SAndrea Mayer [SEG6_LOCAL_COUNTERS] = { .parse = parse_nla_counters, 162394604548SAndrea Mayer .put = put_nla_counters, 162494604548SAndrea Mayer .cmp = cmp_nla_counters, 162594604548SAndrea Mayer .destroy = destroy_attr_counters }, 1626d1df6fd8SDavid Lebrun }; 1627d1df6fd8SDavid Lebrun 1628964adce5SAndrea Mayer /* call the destroy() callback (if available) for each set attribute in 16290a3021f1SAndrea Mayer * @parsed_attrs, starting from the first attribute up to the @max_parsed 16300a3021f1SAndrea Mayer * (excluded) attribute. 1631964adce5SAndrea Mayer */ 16320a3021f1SAndrea Mayer static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed, 16330a3021f1SAndrea Mayer struct seg6_local_lwt *slwt) 1634964adce5SAndrea Mayer { 1635964adce5SAndrea Mayer struct seg6_action_param *param; 1636964adce5SAndrea Mayer int i; 1637964adce5SAndrea Mayer 1638964adce5SAndrea Mayer /* Every required seg6local attribute is identified by an ID which is 1639964adce5SAndrea Mayer * encoded as a flag (i.e: 1 << ID) in the 'attrs' bitmask; 1640964adce5SAndrea Mayer * 16410a3021f1SAndrea Mayer * We scan the 'parsed_attrs' bitmask, starting from the first attribute 1642964adce5SAndrea Mayer * up to the @max_parsed (excluded) attribute. 1643964adce5SAndrea Mayer * For each set attribute, we retrieve the corresponding destroy() 1644964adce5SAndrea Mayer * callback. If the callback is not available, then we skip to the next 1645964adce5SAndrea Mayer * attribute; otherwise, we call the destroy() callback. 1646964adce5SAndrea Mayer */ 1647964adce5SAndrea Mayer for (i = 0; i < max_parsed; ++i) { 1648300a0fd8SAndrea Mayer if (!(parsed_attrs & SEG6_F_ATTR(i))) 1649964adce5SAndrea Mayer continue; 1650964adce5SAndrea Mayer 1651964adce5SAndrea Mayer param = &seg6_action_params[i]; 1652964adce5SAndrea Mayer 1653964adce5SAndrea Mayer if (param->destroy) 1654964adce5SAndrea Mayer param->destroy(slwt); 1655964adce5SAndrea Mayer } 1656964adce5SAndrea Mayer } 1657964adce5SAndrea Mayer 1658964adce5SAndrea Mayer /* release all the resources that may have been acquired during parsing 1659964adce5SAndrea Mayer * operations. 1660964adce5SAndrea Mayer */ 1661964adce5SAndrea Mayer static void destroy_attrs(struct seg6_local_lwt *slwt) 1662964adce5SAndrea Mayer { 16630a3021f1SAndrea Mayer unsigned long attrs = slwt->desc->attrs | slwt->parsed_optattrs; 16640a3021f1SAndrea Mayer 16650a3021f1SAndrea Mayer __destroy_attrs(attrs, SEG6_LOCAL_MAX + 1, slwt); 16660a3021f1SAndrea Mayer } 16670a3021f1SAndrea Mayer 16680a3021f1SAndrea Mayer static int parse_nla_optional_attrs(struct nlattr **attrs, 16690a3021f1SAndrea Mayer struct seg6_local_lwt *slwt) 16700a3021f1SAndrea Mayer { 16710a3021f1SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 16720a3021f1SAndrea Mayer unsigned long parsed_optattrs = 0; 16730a3021f1SAndrea Mayer struct seg6_action_param *param; 16740a3021f1SAndrea Mayer int err, i; 16750a3021f1SAndrea Mayer 16760a3021f1SAndrea Mayer for (i = 0; i < SEG6_LOCAL_MAX + 1; ++i) { 1677300a0fd8SAndrea Mayer if (!(desc->optattrs & SEG6_F_ATTR(i)) || !attrs[i]) 16780a3021f1SAndrea Mayer continue; 16790a3021f1SAndrea Mayer 16800a3021f1SAndrea Mayer /* once here, the i-th attribute is provided by the 16810a3021f1SAndrea Mayer * userspace AND it is identified optional as well. 16820a3021f1SAndrea Mayer */ 16830a3021f1SAndrea Mayer param = &seg6_action_params[i]; 16840a3021f1SAndrea Mayer 16850a3021f1SAndrea Mayer err = param->parse(attrs, slwt); 16860a3021f1SAndrea Mayer if (err < 0) 16870a3021f1SAndrea Mayer goto parse_optattrs_err; 16880a3021f1SAndrea Mayer 16890a3021f1SAndrea Mayer /* current attribute has been correctly parsed */ 1690300a0fd8SAndrea Mayer parsed_optattrs |= SEG6_F_ATTR(i); 16910a3021f1SAndrea Mayer } 16920a3021f1SAndrea Mayer 16930a3021f1SAndrea Mayer /* store in the tunnel state all the optional attributed successfully 16940a3021f1SAndrea Mayer * parsed. 16950a3021f1SAndrea Mayer */ 16960a3021f1SAndrea Mayer slwt->parsed_optattrs = parsed_optattrs; 16970a3021f1SAndrea Mayer 16980a3021f1SAndrea Mayer return 0; 16990a3021f1SAndrea Mayer 17000a3021f1SAndrea Mayer parse_optattrs_err: 17010a3021f1SAndrea Mayer __destroy_attrs(parsed_optattrs, i, slwt); 17020a3021f1SAndrea Mayer 17030a3021f1SAndrea Mayer return err; 1704964adce5SAndrea Mayer } 1705964adce5SAndrea Mayer 1706cfdf64a0SAndrea Mayer /* call the custom constructor of the behavior during its initialization phase 1707cfdf64a0SAndrea Mayer * and after that all its attributes have been parsed successfully. 1708cfdf64a0SAndrea Mayer */ 1709cfdf64a0SAndrea Mayer static int 1710cfdf64a0SAndrea Mayer seg6_local_lwtunnel_build_state(struct seg6_local_lwt *slwt, const void *cfg, 1711cfdf64a0SAndrea Mayer struct netlink_ext_ack *extack) 1712cfdf64a0SAndrea Mayer { 1713cfdf64a0SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 1714cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops *ops; 1715cfdf64a0SAndrea Mayer 1716cfdf64a0SAndrea Mayer ops = &desc->slwt_ops; 1717cfdf64a0SAndrea Mayer if (!ops->build_state) 1718cfdf64a0SAndrea Mayer return 0; 1719cfdf64a0SAndrea Mayer 1720cfdf64a0SAndrea Mayer return ops->build_state(slwt, cfg, extack); 1721cfdf64a0SAndrea Mayer } 1722cfdf64a0SAndrea Mayer 1723cfdf64a0SAndrea Mayer /* call the custom destructor of the behavior which is invoked before the 1724cfdf64a0SAndrea Mayer * tunnel is going to be destroyed. 1725cfdf64a0SAndrea Mayer */ 1726cfdf64a0SAndrea Mayer static void seg6_local_lwtunnel_destroy_state(struct seg6_local_lwt *slwt) 1727cfdf64a0SAndrea Mayer { 1728cfdf64a0SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 1729cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops *ops; 1730cfdf64a0SAndrea Mayer 1731cfdf64a0SAndrea Mayer ops = &desc->slwt_ops; 1732cfdf64a0SAndrea Mayer if (!ops->destroy_state) 1733cfdf64a0SAndrea Mayer return; 1734cfdf64a0SAndrea Mayer 1735cfdf64a0SAndrea Mayer ops->destroy_state(slwt); 1736cfdf64a0SAndrea Mayer } 1737cfdf64a0SAndrea Mayer 1738d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) 1739d1df6fd8SDavid Lebrun { 1740d1df6fd8SDavid Lebrun struct seg6_action_param *param; 1741d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 17420a3021f1SAndrea Mayer unsigned long invalid_attrs; 1743d1df6fd8SDavid Lebrun int i, err; 1744d1df6fd8SDavid Lebrun 1745d1df6fd8SDavid Lebrun desc = __get_action_desc(slwt->action); 1746d1df6fd8SDavid Lebrun if (!desc) 1747d1df6fd8SDavid Lebrun return -EINVAL; 1748d1df6fd8SDavid Lebrun 1749d1df6fd8SDavid Lebrun if (!desc->input) 1750d1df6fd8SDavid Lebrun return -EOPNOTSUPP; 1751d1df6fd8SDavid Lebrun 1752d1df6fd8SDavid Lebrun slwt->desc = desc; 1753d1df6fd8SDavid Lebrun slwt->headroom += desc->static_headroom; 1754d1df6fd8SDavid Lebrun 17550a3021f1SAndrea Mayer /* Forcing the desc->optattrs *set* and the desc->attrs *set* to be 17560a3021f1SAndrea Mayer * disjoined, this allow us to release acquired resources by optional 17570a3021f1SAndrea Mayer * attributes and by required attributes independently from each other 17580d770360SAndrea Mayer * without any interference. 17590a3021f1SAndrea Mayer * In other terms, we are sure that we do not release some the acquired 17600a3021f1SAndrea Mayer * resources twice. 17610a3021f1SAndrea Mayer * 17620a3021f1SAndrea Mayer * Note that if an attribute is configured both as required and as 17630a3021f1SAndrea Mayer * optional, it means that the user has messed something up in the 17640a3021f1SAndrea Mayer * seg6_action_table. Therefore, this check is required for SRv6 17650a3021f1SAndrea Mayer * behaviors to work properly. 17660a3021f1SAndrea Mayer */ 17670a3021f1SAndrea Mayer invalid_attrs = desc->attrs & desc->optattrs; 17680a3021f1SAndrea Mayer if (invalid_attrs) { 17690a3021f1SAndrea Mayer WARN_ONCE(1, 17700a3021f1SAndrea Mayer "An attribute cannot be both required AND optional"); 17710a3021f1SAndrea Mayer return -EINVAL; 17720a3021f1SAndrea Mayer } 17730a3021f1SAndrea Mayer 17740a3021f1SAndrea Mayer /* parse the required attributes */ 1775d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1776300a0fd8SAndrea Mayer if (desc->attrs & SEG6_F_ATTR(i)) { 1777d1df6fd8SDavid Lebrun if (!attrs[i]) 1778d1df6fd8SDavid Lebrun return -EINVAL; 1779d1df6fd8SDavid Lebrun 1780d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1781d1df6fd8SDavid Lebrun 1782d1df6fd8SDavid Lebrun err = param->parse(attrs, slwt); 1783d1df6fd8SDavid Lebrun if (err < 0) 17840a3021f1SAndrea Mayer goto parse_attrs_err; 1785d1df6fd8SDavid Lebrun } 1786d1df6fd8SDavid Lebrun } 1787d1df6fd8SDavid Lebrun 17880a3021f1SAndrea Mayer /* parse the optional attributes, if any */ 17890a3021f1SAndrea Mayer err = parse_nla_optional_attrs(attrs, slwt); 17900a3021f1SAndrea Mayer if (err < 0) 17910a3021f1SAndrea Mayer goto parse_attrs_err; 17920a3021f1SAndrea Mayer 1793d1df6fd8SDavid Lebrun return 0; 1794964adce5SAndrea Mayer 17950a3021f1SAndrea Mayer parse_attrs_err: 1796964adce5SAndrea Mayer /* release any resource that may have been acquired during the i-1 1797964adce5SAndrea Mayer * parse() operations. 1798964adce5SAndrea Mayer */ 17990a3021f1SAndrea Mayer __destroy_attrs(desc->attrs, i, slwt); 1800964adce5SAndrea Mayer 1801964adce5SAndrea Mayer return err; 1802d1df6fd8SDavid Lebrun } 1803d1df6fd8SDavid Lebrun 1804faee6769SAlexander Aring static int seg6_local_build_state(struct net *net, struct nlattr *nla, 1805faee6769SAlexander Aring unsigned int family, const void *cfg, 1806faee6769SAlexander Aring struct lwtunnel_state **ts, 1807d1df6fd8SDavid Lebrun struct netlink_ext_ack *extack) 1808d1df6fd8SDavid Lebrun { 1809d1df6fd8SDavid Lebrun struct nlattr *tb[SEG6_LOCAL_MAX + 1]; 1810d1df6fd8SDavid Lebrun struct lwtunnel_state *newts; 1811d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 1812d1df6fd8SDavid Lebrun int err; 1813d1df6fd8SDavid Lebrun 18146285217fSDavid Lebrun if (family != AF_INET6) 18156285217fSDavid Lebrun return -EINVAL; 18166285217fSDavid Lebrun 18178cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla, 18188cb08174SJohannes Berg seg6_local_policy, extack); 1819d1df6fd8SDavid Lebrun 1820d1df6fd8SDavid Lebrun if (err < 0) 1821d1df6fd8SDavid Lebrun return err; 1822d1df6fd8SDavid Lebrun 1823d1df6fd8SDavid Lebrun if (!tb[SEG6_LOCAL_ACTION]) 1824d1df6fd8SDavid Lebrun return -EINVAL; 1825d1df6fd8SDavid Lebrun 1826d1df6fd8SDavid Lebrun newts = lwtunnel_state_alloc(sizeof(*slwt)); 1827d1df6fd8SDavid Lebrun if (!newts) 1828d1df6fd8SDavid Lebrun return -ENOMEM; 1829d1df6fd8SDavid Lebrun 1830d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(newts); 1831d1df6fd8SDavid Lebrun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); 1832d1df6fd8SDavid Lebrun 1833d1df6fd8SDavid Lebrun err = parse_nla_action(tb, slwt); 1834d1df6fd8SDavid Lebrun if (err < 0) 1835d1df6fd8SDavid Lebrun goto out_free; 1836d1df6fd8SDavid Lebrun 1837cfdf64a0SAndrea Mayer err = seg6_local_lwtunnel_build_state(slwt, cfg, extack); 1838cfdf64a0SAndrea Mayer if (err < 0) 1839cfdf64a0SAndrea Mayer goto out_destroy_attrs; 1840cfdf64a0SAndrea Mayer 1841d1df6fd8SDavid Lebrun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; 1842d1df6fd8SDavid Lebrun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; 1843d1df6fd8SDavid Lebrun newts->headroom = slwt->headroom; 1844d1df6fd8SDavid Lebrun 1845d1df6fd8SDavid Lebrun *ts = newts; 1846d1df6fd8SDavid Lebrun 1847d1df6fd8SDavid Lebrun return 0; 1848d1df6fd8SDavid Lebrun 1849cfdf64a0SAndrea Mayer out_destroy_attrs: 1850cfdf64a0SAndrea Mayer destroy_attrs(slwt); 1851d1df6fd8SDavid Lebrun out_free: 1852d1df6fd8SDavid Lebrun kfree(newts); 1853d1df6fd8SDavid Lebrun return err; 1854d1df6fd8SDavid Lebrun } 1855d1df6fd8SDavid Lebrun 1856d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt) 1857d1df6fd8SDavid Lebrun { 1858d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1859d1df6fd8SDavid Lebrun 1860cfdf64a0SAndrea Mayer seg6_local_lwtunnel_destroy_state(slwt); 1861cfdf64a0SAndrea Mayer 1862964adce5SAndrea Mayer destroy_attrs(slwt); 1863004d4b27SMathieu Xhonneux 1864004d4b27SMathieu Xhonneux return; 1865d1df6fd8SDavid Lebrun } 1866d1df6fd8SDavid Lebrun 1867d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb, 1868d1df6fd8SDavid Lebrun struct lwtunnel_state *lwt) 1869d1df6fd8SDavid Lebrun { 1870d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1871d1df6fd8SDavid Lebrun struct seg6_action_param *param; 18720a3021f1SAndrea Mayer unsigned long attrs; 1873d1df6fd8SDavid Lebrun int i, err; 1874d1df6fd8SDavid Lebrun 1875d1df6fd8SDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) 1876d1df6fd8SDavid Lebrun return -EMSGSIZE; 1877d1df6fd8SDavid Lebrun 18780a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 18790a3021f1SAndrea Mayer 1880d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1881300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(i)) { 1882d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1883d1df6fd8SDavid Lebrun err = param->put(skb, slwt); 1884d1df6fd8SDavid Lebrun if (err < 0) 1885d1df6fd8SDavid Lebrun return err; 1886d1df6fd8SDavid Lebrun } 1887d1df6fd8SDavid Lebrun } 1888d1df6fd8SDavid Lebrun 1889d1df6fd8SDavid Lebrun return 0; 1890d1df6fd8SDavid Lebrun } 1891d1df6fd8SDavid Lebrun 1892d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) 1893d1df6fd8SDavid Lebrun { 1894d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1895d1df6fd8SDavid Lebrun unsigned long attrs; 1896d1df6fd8SDavid Lebrun int nlsize; 1897d1df6fd8SDavid Lebrun 1898d1df6fd8SDavid Lebrun nlsize = nla_total_size(4); /* action */ 1899d1df6fd8SDavid Lebrun 19000a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 1901d1df6fd8SDavid Lebrun 1902300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_SRH)) 1903d1df6fd8SDavid Lebrun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); 1904d1df6fd8SDavid Lebrun 1905300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE)) 1906d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1907d1df6fd8SDavid Lebrun 1908300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH4)) 1909d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1910d1df6fd8SDavid Lebrun 1911300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH6)) 1912d1df6fd8SDavid Lebrun nlsize += nla_total_size(16); 1913d1df6fd8SDavid Lebrun 1914300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_IIF)) 1915d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1916d1df6fd8SDavid Lebrun 1917300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_OIF)) 1918d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1919d1df6fd8SDavid Lebrun 1920300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_BPF)) 1921004d4b27SMathieu Xhonneux nlsize += nla_total_size(sizeof(struct nlattr)) + 1922004d4b27SMathieu Xhonneux nla_total_size(MAX_PROG_NAME) + 1923004d4b27SMathieu Xhonneux nla_total_size(4); 1924004d4b27SMathieu Xhonneux 1925300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE)) 1926664d6f86SAndrea Mayer nlsize += nla_total_size(4); 1927664d6f86SAndrea Mayer 192894604548SAndrea Mayer if (attrs & SEG6_F_LOCAL_COUNTERS) 192994604548SAndrea Mayer nlsize += nla_total_size(0) + /* nest SEG6_LOCAL_COUNTERS */ 193094604548SAndrea Mayer /* SEG6_LOCAL_CNT_PACKETS */ 193194604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)) + 193294604548SAndrea Mayer /* SEG6_LOCAL_CNT_BYTES */ 193394604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)) + 193494604548SAndrea Mayer /* SEG6_LOCAL_CNT_ERRORS */ 193594604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)); 193694604548SAndrea Mayer 1937d1df6fd8SDavid Lebrun return nlsize; 1938d1df6fd8SDavid Lebrun } 1939d1df6fd8SDavid Lebrun 1940d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a, 1941d1df6fd8SDavid Lebrun struct lwtunnel_state *b) 1942d1df6fd8SDavid Lebrun { 1943d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt_a, *slwt_b; 1944d1df6fd8SDavid Lebrun struct seg6_action_param *param; 19450a3021f1SAndrea Mayer unsigned long attrs_a, attrs_b; 1946d1df6fd8SDavid Lebrun int i; 1947d1df6fd8SDavid Lebrun 1948d1df6fd8SDavid Lebrun slwt_a = seg6_local_lwtunnel(a); 1949d1df6fd8SDavid Lebrun slwt_b = seg6_local_lwtunnel(b); 1950d1df6fd8SDavid Lebrun 1951d1df6fd8SDavid Lebrun if (slwt_a->action != slwt_b->action) 1952d1df6fd8SDavid Lebrun return 1; 1953d1df6fd8SDavid Lebrun 19540a3021f1SAndrea Mayer attrs_a = slwt_a->desc->attrs | slwt_a->parsed_optattrs; 19550a3021f1SAndrea Mayer attrs_b = slwt_b->desc->attrs | slwt_b->parsed_optattrs; 19560a3021f1SAndrea Mayer 19570a3021f1SAndrea Mayer if (attrs_a != attrs_b) 1958d1df6fd8SDavid Lebrun return 1; 1959d1df6fd8SDavid Lebrun 1960d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1961300a0fd8SAndrea Mayer if (attrs_a & SEG6_F_ATTR(i)) { 1962d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1963d1df6fd8SDavid Lebrun if (param->cmp(slwt_a, slwt_b)) 1964d1df6fd8SDavid Lebrun return 1; 1965d1df6fd8SDavid Lebrun } 1966d1df6fd8SDavid Lebrun } 1967d1df6fd8SDavid Lebrun 1968d1df6fd8SDavid Lebrun return 0; 1969d1df6fd8SDavid Lebrun } 1970d1df6fd8SDavid Lebrun 1971d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = { 1972d1df6fd8SDavid Lebrun .build_state = seg6_local_build_state, 1973d1df6fd8SDavid Lebrun .destroy_state = seg6_local_destroy_state, 1974d1df6fd8SDavid Lebrun .input = seg6_local_input, 1975d1df6fd8SDavid Lebrun .fill_encap = seg6_local_fill_encap, 1976d1df6fd8SDavid Lebrun .get_encap_size = seg6_local_get_encap_size, 1977d1df6fd8SDavid Lebrun .cmp_encap = seg6_local_cmp_encap, 1978d1df6fd8SDavid Lebrun .owner = THIS_MODULE, 1979d1df6fd8SDavid Lebrun }; 1980d1df6fd8SDavid Lebrun 1981d1df6fd8SDavid Lebrun int __init seg6_local_init(void) 1982d1df6fd8SDavid Lebrun { 1983300a0fd8SAndrea Mayer /* If the max total number of defined attributes is reached, then your 1984300a0fd8SAndrea Mayer * kernel build stops here. 1985300a0fd8SAndrea Mayer * 1986300a0fd8SAndrea Mayer * This check is required to avoid arithmetic overflows when processing 1987300a0fd8SAndrea Mayer * behavior attributes and the maximum number of defined attributes 1988300a0fd8SAndrea Mayer * exceeds the allowed value. 1989300a0fd8SAndrea Mayer */ 1990300a0fd8SAndrea Mayer BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsigned long)); 1991300a0fd8SAndrea Mayer 1992d1df6fd8SDavid Lebrun return lwtunnel_encap_add_ops(&seg6_local_ops, 1993d1df6fd8SDavid Lebrun LWTUNNEL_ENCAP_SEG6_LOCAL); 1994d1df6fd8SDavid Lebrun } 1995d1df6fd8SDavid Lebrun 1996d1df6fd8SDavid Lebrun void seg6_local_exit(void) 1997d1df6fd8SDavid Lebrun { 1998d1df6fd8SDavid Lebrun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); 1999d1df6fd8SDavid Lebrun } 2000