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> 33d1df6fd8SDavid Lebrun 34300a0fd8SAndrea Mayer #define SEG6_F_ATTR(i) BIT(i) 35300a0fd8SAndrea Mayer 36d1df6fd8SDavid Lebrun struct seg6_local_lwt; 37d1df6fd8SDavid Lebrun 38cfdf64a0SAndrea Mayer /* callbacks used for customizing the creation and destruction of a behavior */ 39cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops { 40cfdf64a0SAndrea Mayer int (*build_state)(struct seg6_local_lwt *slwt, const void *cfg, 41cfdf64a0SAndrea Mayer struct netlink_ext_ack *extack); 42cfdf64a0SAndrea Mayer void (*destroy_state)(struct seg6_local_lwt *slwt); 43cfdf64a0SAndrea Mayer }; 44cfdf64a0SAndrea Mayer 45d1df6fd8SDavid Lebrun struct seg6_action_desc { 46d1df6fd8SDavid Lebrun int action; 47d1df6fd8SDavid Lebrun unsigned long attrs; 480a3021f1SAndrea Mayer 490a3021f1SAndrea Mayer /* The optattrs field is used for specifying all the optional 500a3021f1SAndrea Mayer * attributes supported by a specific behavior. 510a3021f1SAndrea Mayer * It means that if one of these attributes is not provided in the 520a3021f1SAndrea Mayer * netlink message during the behavior creation, no errors will be 530a3021f1SAndrea Mayer * returned to the userspace. 540a3021f1SAndrea Mayer * 550a3021f1SAndrea Mayer * Each attribute can be only of two types (mutually exclusive): 560a3021f1SAndrea Mayer * 1) required or 2) optional. 570a3021f1SAndrea Mayer * Every user MUST obey to this rule! If you set an attribute as 580a3021f1SAndrea Mayer * required the same attribute CANNOT be set as optional and vice 590a3021f1SAndrea Mayer * versa. 600a3021f1SAndrea Mayer */ 610a3021f1SAndrea Mayer unsigned long optattrs; 620a3021f1SAndrea Mayer 63d1df6fd8SDavid Lebrun int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 64d1df6fd8SDavid Lebrun int static_headroom; 65cfdf64a0SAndrea Mayer 66cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops slwt_ops; 67d1df6fd8SDavid Lebrun }; 68d1df6fd8SDavid Lebrun 69004d4b27SMathieu Xhonneux struct bpf_lwt_prog { 70004d4b27SMathieu Xhonneux struct bpf_prog *prog; 71004d4b27SMathieu Xhonneux char *name; 72004d4b27SMathieu Xhonneux }; 73004d4b27SMathieu Xhonneux 74664d6f86SAndrea Mayer enum seg6_end_dt_mode { 75664d6f86SAndrea Mayer DT_INVALID_MODE = -EINVAL, 76664d6f86SAndrea Mayer DT_LEGACY_MODE = 0, 77664d6f86SAndrea Mayer DT_VRF_MODE = 1, 78664d6f86SAndrea Mayer }; 79664d6f86SAndrea Mayer 80664d6f86SAndrea Mayer struct seg6_end_dt_info { 81664d6f86SAndrea Mayer enum seg6_end_dt_mode mode; 82664d6f86SAndrea Mayer 83664d6f86SAndrea Mayer struct net *net; 84664d6f86SAndrea Mayer /* VRF device associated to the routing table used by the SRv6 85664d6f86SAndrea Mayer * End.DT4/DT6 behavior for routing IPv4/IPv6 packets. 86664d6f86SAndrea Mayer */ 87664d6f86SAndrea Mayer int vrf_ifindex; 88664d6f86SAndrea Mayer int vrf_table; 89664d6f86SAndrea Mayer 90664d6f86SAndrea Mayer /* tunneled packet proto and family (IPv4 or IPv6) */ 91664d6f86SAndrea Mayer __be16 proto; 92664d6f86SAndrea Mayer u16 family; 93664d6f86SAndrea Mayer int hdrlen; 94664d6f86SAndrea Mayer }; 95664d6f86SAndrea Mayer 96*94604548SAndrea Mayer struct pcpu_seg6_local_counters { 97*94604548SAndrea Mayer u64_stats_t packets; 98*94604548SAndrea Mayer u64_stats_t bytes; 99*94604548SAndrea Mayer u64_stats_t errors; 100*94604548SAndrea Mayer 101*94604548SAndrea Mayer struct u64_stats_sync syncp; 102*94604548SAndrea Mayer }; 103*94604548SAndrea Mayer 104*94604548SAndrea Mayer /* This struct groups all the SRv6 Behavior counters supported so far. 105*94604548SAndrea Mayer * 106*94604548SAndrea Mayer * put_nla_counters() makes use of this data structure to collect all counter 107*94604548SAndrea Mayer * values after the per-CPU counter evaluation has been performed. 108*94604548SAndrea Mayer * Finally, each counter value (in seg6_local_counters) is stored in the 109*94604548SAndrea Mayer * corresponding netlink attribute and sent to user space. 110*94604548SAndrea Mayer * 111*94604548SAndrea Mayer * NB: we don't want to expose this structure to user space! 112*94604548SAndrea Mayer */ 113*94604548SAndrea Mayer struct seg6_local_counters { 114*94604548SAndrea Mayer __u64 packets; 115*94604548SAndrea Mayer __u64 bytes; 116*94604548SAndrea Mayer __u64 errors; 117*94604548SAndrea Mayer }; 118*94604548SAndrea Mayer 119*94604548SAndrea Mayer #define seg6_local_alloc_pcpu_counters(__gfp) \ 120*94604548SAndrea Mayer __netdev_alloc_pcpu_stats(struct pcpu_seg6_local_counters, \ 121*94604548SAndrea Mayer ((__gfp) | __GFP_ZERO)) 122*94604548SAndrea Mayer 123*94604548SAndrea Mayer #define SEG6_F_LOCAL_COUNTERS SEG6_F_ATTR(SEG6_LOCAL_COUNTERS) 124*94604548SAndrea Mayer 125d1df6fd8SDavid Lebrun struct seg6_local_lwt { 126d1df6fd8SDavid Lebrun int action; 127d1df6fd8SDavid Lebrun struct ipv6_sr_hdr *srh; 128d1df6fd8SDavid Lebrun int table; 129d1df6fd8SDavid Lebrun struct in_addr nh4; 130d1df6fd8SDavid Lebrun struct in6_addr nh6; 131d1df6fd8SDavid Lebrun int iif; 132d1df6fd8SDavid Lebrun int oif; 133004d4b27SMathieu Xhonneux struct bpf_lwt_prog bpf; 134664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 135664d6f86SAndrea Mayer struct seg6_end_dt_info dt_info; 136664d6f86SAndrea Mayer #endif 137*94604548SAndrea Mayer struct pcpu_seg6_local_counters __percpu *pcpu_counters; 138d1df6fd8SDavid Lebrun 139d1df6fd8SDavid Lebrun int headroom; 140d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1410a3021f1SAndrea Mayer /* unlike the required attrs, we have to track the optional attributes 1420a3021f1SAndrea Mayer * that have been effectively parsed. 1430a3021f1SAndrea Mayer */ 1440a3021f1SAndrea Mayer unsigned long parsed_optattrs; 145d1df6fd8SDavid Lebrun }; 146d1df6fd8SDavid Lebrun 147d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) 148d1df6fd8SDavid Lebrun { 149d1df6fd8SDavid Lebrun return (struct seg6_local_lwt *)lwt->data; 150d1df6fd8SDavid Lebrun } 151d1df6fd8SDavid Lebrun 152fbbc5bc2SJulien Massonneau static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb, int flags) 153140f04c3SDavid Lebrun { 154140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 1555829d70bSAhmed Abdelsalam int len, srhoff = 0; 156140f04c3SDavid Lebrun 157fbbc5bc2SJulien Massonneau if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, &flags) < 0) 158140f04c3SDavid Lebrun return NULL; 159140f04c3SDavid Lebrun 1605829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + sizeof(*srh))) 1615829d70bSAhmed Abdelsalam return NULL; 1625829d70bSAhmed Abdelsalam 1635829d70bSAhmed Abdelsalam srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 1645829d70bSAhmed Abdelsalam 165140f04c3SDavid Lebrun len = (srh->hdrlen + 1) << 3; 166140f04c3SDavid Lebrun 1675829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + len)) 168140f04c3SDavid Lebrun return NULL; 169140f04c3SDavid Lebrun 1707f91ed8cSAndrea Mayer /* note that pskb_may_pull may change pointers in header; 1717f91ed8cSAndrea Mayer * for this reason it is necessary to reload them when needed. 1727f91ed8cSAndrea Mayer */ 1737f91ed8cSAndrea Mayer srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 1747f91ed8cSAndrea Mayer 175bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, true)) 176140f04c3SDavid Lebrun return NULL; 177140f04c3SDavid Lebrun 178140f04c3SDavid Lebrun return srh; 179140f04c3SDavid Lebrun } 180140f04c3SDavid Lebrun 181140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) 182140f04c3SDavid Lebrun { 183140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 184140f04c3SDavid Lebrun 185fbbc5bc2SJulien Massonneau srh = get_srh(skb, IP6_FH_F_SKIP_RH); 186140f04c3SDavid Lebrun if (!srh) 187140f04c3SDavid Lebrun return NULL; 188140f04c3SDavid Lebrun 189140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 190140f04c3SDavid Lebrun if (!seg6_hmac_validate_skb(skb)) 191140f04c3SDavid Lebrun return NULL; 192140f04c3SDavid Lebrun #endif 193140f04c3SDavid Lebrun 194140f04c3SDavid Lebrun return srh; 195140f04c3SDavid Lebrun } 196140f04c3SDavid Lebrun 197d7a669ddSDavid Lebrun static bool decap_and_validate(struct sk_buff *skb, int proto) 198d7a669ddSDavid Lebrun { 199d7a669ddSDavid Lebrun struct ipv6_sr_hdr *srh; 200d7a669ddSDavid Lebrun unsigned int off = 0; 201d7a669ddSDavid Lebrun 202fbbc5bc2SJulien Massonneau srh = get_srh(skb, 0); 203d7a669ddSDavid Lebrun if (srh && srh->segments_left > 0) 204d7a669ddSDavid Lebrun return false; 205d7a669ddSDavid Lebrun 206d7a669ddSDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 207d7a669ddSDavid Lebrun if (srh && !seg6_hmac_validate_skb(skb)) 208d7a669ddSDavid Lebrun return false; 209d7a669ddSDavid Lebrun #endif 210d7a669ddSDavid Lebrun 211d7a669ddSDavid Lebrun if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0) 212d7a669ddSDavid Lebrun return false; 213d7a669ddSDavid Lebrun 214d7a669ddSDavid Lebrun if (!pskb_pull(skb, off)) 215d7a669ddSDavid Lebrun return false; 216d7a669ddSDavid Lebrun 217d7a669ddSDavid Lebrun skb_postpull_rcsum(skb, skb_network_header(skb), off); 218d7a669ddSDavid Lebrun 219d7a669ddSDavid Lebrun skb_reset_network_header(skb); 220d7a669ddSDavid Lebrun skb_reset_transport_header(skb); 22162ebaeaeSYuki Taguchi if (iptunnel_pull_offloads(skb)) 22262ebaeaeSYuki Taguchi return false; 223d7a669ddSDavid Lebrun 224d7a669ddSDavid Lebrun return true; 225d7a669ddSDavid Lebrun } 226d7a669ddSDavid Lebrun 227d7a669ddSDavid Lebrun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr) 228d7a669ddSDavid Lebrun { 229d7a669ddSDavid Lebrun struct in6_addr *addr; 230d7a669ddSDavid Lebrun 231d7a669ddSDavid Lebrun srh->segments_left--; 232d7a669ddSDavid Lebrun addr = srh->segments + srh->segments_left; 233d7a669ddSDavid Lebrun *daddr = *addr; 234d7a669ddSDavid Lebrun } 235d7a669ddSDavid Lebrun 236fd1fef0cSAndrea Mayer static int 237fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr, 238fd1fef0cSAndrea Mayer u32 tbl_id, bool local_delivery) 239d7a669ddSDavid Lebrun { 240d7a669ddSDavid Lebrun struct net *net = dev_net(skb->dev); 241d7a669ddSDavid Lebrun struct ipv6hdr *hdr = ipv6_hdr(skb); 242d7a669ddSDavid Lebrun int flags = RT6_LOOKUP_F_HAS_SADDR; 243d7a669ddSDavid Lebrun struct dst_entry *dst = NULL; 244d7a669ddSDavid Lebrun struct rt6_info *rt; 245d7a669ddSDavid Lebrun struct flowi6 fl6; 246fd1fef0cSAndrea Mayer int dev_flags = 0; 247d7a669ddSDavid Lebrun 248d7a669ddSDavid Lebrun fl6.flowi6_iif = skb->dev->ifindex; 249d7a669ddSDavid Lebrun fl6.daddr = nhaddr ? *nhaddr : hdr->daddr; 250d7a669ddSDavid Lebrun fl6.saddr = hdr->saddr; 251d7a669ddSDavid Lebrun fl6.flowlabel = ip6_flowinfo(hdr); 252d7a669ddSDavid Lebrun fl6.flowi6_mark = skb->mark; 253d7a669ddSDavid Lebrun fl6.flowi6_proto = hdr->nexthdr; 254d7a669ddSDavid Lebrun 255d7a669ddSDavid Lebrun if (nhaddr) 256d7a669ddSDavid Lebrun fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH; 257d7a669ddSDavid Lebrun 258d7a669ddSDavid Lebrun if (!tbl_id) { 259b75cc8f9SDavid Ahern dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags); 260d7a669ddSDavid Lebrun } else { 261d7a669ddSDavid Lebrun struct fib6_table *table; 262d7a669ddSDavid Lebrun 263d7a669ddSDavid Lebrun table = fib6_get_table(net, tbl_id); 264d7a669ddSDavid Lebrun if (!table) 265d7a669ddSDavid Lebrun goto out; 266d7a669ddSDavid Lebrun 267b75cc8f9SDavid Ahern rt = ip6_pol_route(net, table, 0, &fl6, skb, flags); 268d7a669ddSDavid Lebrun dst = &rt->dst; 269d7a669ddSDavid Lebrun } 270d7a669ddSDavid Lebrun 271fd1fef0cSAndrea Mayer /* we want to discard traffic destined for local packet processing, 272fd1fef0cSAndrea Mayer * if @local_delivery is set to false. 273fd1fef0cSAndrea Mayer */ 274fd1fef0cSAndrea Mayer if (!local_delivery) 275fd1fef0cSAndrea Mayer dev_flags |= IFF_LOOPBACK; 276fd1fef0cSAndrea Mayer 277fd1fef0cSAndrea Mayer if (dst && (dst->dev->flags & dev_flags) && !dst->error) { 278d7a669ddSDavid Lebrun dst_release(dst); 279d7a669ddSDavid Lebrun dst = NULL; 280d7a669ddSDavid Lebrun } 281d7a669ddSDavid Lebrun 282d7a669ddSDavid Lebrun out: 283d7a669ddSDavid Lebrun if (!dst) { 284d7a669ddSDavid Lebrun rt = net->ipv6.ip6_blk_hole_entry; 285d7a669ddSDavid Lebrun dst = &rt->dst; 286d7a669ddSDavid Lebrun dst_hold(dst); 287d7a669ddSDavid Lebrun } 288d7a669ddSDavid Lebrun 289d7a669ddSDavid Lebrun skb_dst_drop(skb); 290d7a669ddSDavid Lebrun skb_dst_set(skb, dst); 2911c1e761eSMathieu Xhonneux return dst->error; 292d7a669ddSDavid Lebrun } 293d7a669ddSDavid Lebrun 294fd1fef0cSAndrea Mayer int seg6_lookup_nexthop(struct sk_buff *skb, 295fd1fef0cSAndrea Mayer struct in6_addr *nhaddr, u32 tbl_id) 296fd1fef0cSAndrea Mayer { 297fd1fef0cSAndrea Mayer return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false); 298fd1fef0cSAndrea Mayer } 299fd1fef0cSAndrea Mayer 300140f04c3SDavid Lebrun /* regular endpoint function */ 301140f04c3SDavid Lebrun static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 302140f04c3SDavid Lebrun { 303140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 304140f04c3SDavid Lebrun 305140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 306140f04c3SDavid Lebrun if (!srh) 307140f04c3SDavid Lebrun goto drop; 308140f04c3SDavid Lebrun 309d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 310140f04c3SDavid Lebrun 3111c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 312140f04c3SDavid Lebrun 313140f04c3SDavid Lebrun return dst_input(skb); 314140f04c3SDavid Lebrun 315140f04c3SDavid Lebrun drop: 316140f04c3SDavid Lebrun kfree_skb(skb); 317140f04c3SDavid Lebrun return -EINVAL; 318140f04c3SDavid Lebrun } 319140f04c3SDavid Lebrun 320140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */ 321140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) 322140f04c3SDavid Lebrun { 323140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 324140f04c3SDavid Lebrun 325140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 326140f04c3SDavid Lebrun if (!srh) 327140f04c3SDavid Lebrun goto drop; 328140f04c3SDavid Lebrun 329d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 330140f04c3SDavid Lebrun 3311c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, &slwt->nh6, 0); 332140f04c3SDavid Lebrun 333140f04c3SDavid Lebrun return dst_input(skb); 334140f04c3SDavid Lebrun 335140f04c3SDavid Lebrun drop: 336140f04c3SDavid Lebrun kfree_skb(skb); 337140f04c3SDavid Lebrun return -EINVAL; 338140f04c3SDavid Lebrun } 339140f04c3SDavid Lebrun 340891ef8ddSDavid Lebrun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) 341891ef8ddSDavid Lebrun { 342891ef8ddSDavid Lebrun struct ipv6_sr_hdr *srh; 343891ef8ddSDavid Lebrun 344891ef8ddSDavid Lebrun srh = get_and_validate_srh(skb); 345891ef8ddSDavid Lebrun if (!srh) 346891ef8ddSDavid Lebrun goto drop; 347891ef8ddSDavid Lebrun 348891ef8ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 349891ef8ddSDavid Lebrun 3501c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, slwt->table); 351891ef8ddSDavid Lebrun 352891ef8ddSDavid Lebrun return dst_input(skb); 353891ef8ddSDavid Lebrun 354891ef8ddSDavid Lebrun drop: 355891ef8ddSDavid Lebrun kfree_skb(skb); 356891ef8ddSDavid Lebrun return -EINVAL; 357891ef8ddSDavid Lebrun } 358891ef8ddSDavid Lebrun 359891ef8ddSDavid Lebrun /* decapsulate and forward inner L2 frame on specified interface */ 360891ef8ddSDavid Lebrun static int input_action_end_dx2(struct sk_buff *skb, 361891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 362891ef8ddSDavid Lebrun { 363891ef8ddSDavid Lebrun struct net *net = dev_net(skb->dev); 364891ef8ddSDavid Lebrun struct net_device *odev; 365891ef8ddSDavid Lebrun struct ethhdr *eth; 366891ef8ddSDavid Lebrun 36726776253SPaolo Lungaroni if (!decap_and_validate(skb, IPPROTO_ETHERNET)) 368891ef8ddSDavid Lebrun goto drop; 369891ef8ddSDavid Lebrun 370891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, ETH_HLEN)) 371891ef8ddSDavid Lebrun goto drop; 372891ef8ddSDavid Lebrun 373891ef8ddSDavid Lebrun skb_reset_mac_header(skb); 374891ef8ddSDavid Lebrun eth = (struct ethhdr *)skb->data; 375891ef8ddSDavid Lebrun 376891ef8ddSDavid Lebrun /* To determine the frame's protocol, we assume it is 802.3. This avoids 377891ef8ddSDavid Lebrun * a call to eth_type_trans(), which is not really relevant for our 378891ef8ddSDavid Lebrun * use case. 379891ef8ddSDavid Lebrun */ 380891ef8ddSDavid Lebrun if (!eth_proto_is_802_3(eth->h_proto)) 381891ef8ddSDavid Lebrun goto drop; 382891ef8ddSDavid Lebrun 383891ef8ddSDavid Lebrun odev = dev_get_by_index_rcu(net, slwt->oif); 384891ef8ddSDavid Lebrun if (!odev) 385891ef8ddSDavid Lebrun goto drop; 386891ef8ddSDavid Lebrun 387891ef8ddSDavid Lebrun /* As we accept Ethernet frames, make sure the egress device is of 388891ef8ddSDavid Lebrun * the correct type. 389891ef8ddSDavid Lebrun */ 390891ef8ddSDavid Lebrun if (odev->type != ARPHRD_ETHER) 391891ef8ddSDavid Lebrun goto drop; 392891ef8ddSDavid Lebrun 393891ef8ddSDavid Lebrun if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev)) 394891ef8ddSDavid Lebrun goto drop; 395891ef8ddSDavid Lebrun 396891ef8ddSDavid Lebrun skb_orphan(skb); 397891ef8ddSDavid Lebrun 398891ef8ddSDavid Lebrun if (skb_warn_if_lro(skb)) 399891ef8ddSDavid Lebrun goto drop; 400891ef8ddSDavid Lebrun 401891ef8ddSDavid Lebrun skb_forward_csum(skb); 402891ef8ddSDavid Lebrun 403891ef8ddSDavid Lebrun if (skb->len - ETH_HLEN > odev->mtu) 404891ef8ddSDavid Lebrun goto drop; 405891ef8ddSDavid Lebrun 406891ef8ddSDavid Lebrun skb->dev = odev; 407891ef8ddSDavid Lebrun skb->protocol = eth->h_proto; 408891ef8ddSDavid Lebrun 409891ef8ddSDavid Lebrun return dev_queue_xmit(skb); 410891ef8ddSDavid Lebrun 411891ef8ddSDavid Lebrun drop: 412891ef8ddSDavid Lebrun kfree_skb(skb); 413891ef8ddSDavid Lebrun return -EINVAL; 414891ef8ddSDavid Lebrun } 415891ef8ddSDavid Lebrun 416140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */ 417140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb, 418140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 419140f04c3SDavid Lebrun { 420d7a669ddSDavid Lebrun struct in6_addr *nhaddr = NULL; 421140f04c3SDavid Lebrun 422140f04c3SDavid Lebrun /* this function accepts IPv6 encapsulated packets, with either 423140f04c3SDavid Lebrun * an SRH with SL=0, or no SRH. 424140f04c3SDavid Lebrun */ 425140f04c3SDavid Lebrun 426d7a669ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 427140f04c3SDavid Lebrun goto drop; 428140f04c3SDavid Lebrun 429d7a669ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 430140f04c3SDavid Lebrun goto drop; 431140f04c3SDavid Lebrun 432140f04c3SDavid Lebrun /* The inner packet is not associated to any local interface, 433140f04c3SDavid Lebrun * so we do not call netif_rx(). 434140f04c3SDavid Lebrun * 435140f04c3SDavid Lebrun * If slwt->nh6 is set to ::, then lookup the nexthop for the 436140f04c3SDavid Lebrun * inner packet's DA. Otherwise, use the specified nexthop. 437140f04c3SDavid Lebrun */ 438140f04c3SDavid Lebrun 439d7a669ddSDavid Lebrun if (!ipv6_addr_any(&slwt->nh6)) 440d7a669ddSDavid Lebrun nhaddr = &slwt->nh6; 441140f04c3SDavid Lebrun 442c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 443c71644d0SAndrea Mayer 4441c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, nhaddr, 0); 445140f04c3SDavid Lebrun 446140f04c3SDavid Lebrun return dst_input(skb); 447140f04c3SDavid Lebrun drop: 448140f04c3SDavid Lebrun kfree_skb(skb); 449140f04c3SDavid Lebrun return -EINVAL; 450140f04c3SDavid Lebrun } 451140f04c3SDavid Lebrun 452891ef8ddSDavid Lebrun static int input_action_end_dx4(struct sk_buff *skb, 453891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 454891ef8ddSDavid Lebrun { 455891ef8ddSDavid Lebrun struct iphdr *iph; 456891ef8ddSDavid Lebrun __be32 nhaddr; 457891ef8ddSDavid Lebrun int err; 458891ef8ddSDavid Lebrun 459891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPIP)) 460891ef8ddSDavid Lebrun goto drop; 461891ef8ddSDavid Lebrun 462891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct iphdr))) 463891ef8ddSDavid Lebrun goto drop; 464891ef8ddSDavid Lebrun 465891ef8ddSDavid Lebrun skb->protocol = htons(ETH_P_IP); 466891ef8ddSDavid Lebrun 467891ef8ddSDavid Lebrun iph = ip_hdr(skb); 468891ef8ddSDavid Lebrun 469891ef8ddSDavid Lebrun nhaddr = slwt->nh4.s_addr ?: iph->daddr; 470891ef8ddSDavid Lebrun 471891ef8ddSDavid Lebrun skb_dst_drop(skb); 472891ef8ddSDavid Lebrun 473c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct iphdr)); 474c71644d0SAndrea Mayer 475891ef8ddSDavid Lebrun err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev); 476891ef8ddSDavid Lebrun if (err) 477891ef8ddSDavid Lebrun goto drop; 478891ef8ddSDavid Lebrun 479891ef8ddSDavid Lebrun return dst_input(skb); 480891ef8ddSDavid Lebrun 481891ef8ddSDavid Lebrun drop: 482891ef8ddSDavid Lebrun kfree_skb(skb); 483891ef8ddSDavid Lebrun return -EINVAL; 484891ef8ddSDavid Lebrun } 485891ef8ddSDavid Lebrun 486664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 487664d6f86SAndrea Mayer static struct net *fib6_config_get_net(const struct fib6_config *fib6_cfg) 488664d6f86SAndrea Mayer { 489664d6f86SAndrea Mayer const struct nl_info *nli = &fib6_cfg->fc_nlinfo; 490664d6f86SAndrea Mayer 491664d6f86SAndrea Mayer return nli->nl_net; 492664d6f86SAndrea Mayer } 493664d6f86SAndrea Mayer 494664d6f86SAndrea Mayer static int __seg6_end_dt_vrf_build(struct seg6_local_lwt *slwt, const void *cfg, 495664d6f86SAndrea Mayer u16 family, struct netlink_ext_ack *extack) 496664d6f86SAndrea Mayer { 497664d6f86SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 498664d6f86SAndrea Mayer int vrf_ifindex; 499664d6f86SAndrea Mayer struct net *net; 500664d6f86SAndrea Mayer 501664d6f86SAndrea Mayer net = fib6_config_get_net(cfg); 502664d6f86SAndrea Mayer 503664d6f86SAndrea Mayer /* note that vrf_table was already set by parse_nla_vrftable() */ 504664d6f86SAndrea Mayer vrf_ifindex = l3mdev_ifindex_lookup_by_table_id(L3MDEV_TYPE_VRF, net, 505664d6f86SAndrea Mayer info->vrf_table); 506664d6f86SAndrea Mayer if (vrf_ifindex < 0) { 507664d6f86SAndrea Mayer if (vrf_ifindex == -EPERM) { 508664d6f86SAndrea Mayer NL_SET_ERR_MSG(extack, 509664d6f86SAndrea Mayer "Strict mode for VRF is disabled"); 510664d6f86SAndrea Mayer } else if (vrf_ifindex == -ENODEV) { 511664d6f86SAndrea Mayer NL_SET_ERR_MSG(extack, 512664d6f86SAndrea Mayer "Table has no associated VRF device"); 513664d6f86SAndrea Mayer } else { 514664d6f86SAndrea Mayer pr_debug("seg6local: SRv6 End.DT* creation error=%d\n", 515664d6f86SAndrea Mayer vrf_ifindex); 516664d6f86SAndrea Mayer } 517664d6f86SAndrea Mayer 518664d6f86SAndrea Mayer return vrf_ifindex; 519664d6f86SAndrea Mayer } 520664d6f86SAndrea Mayer 521664d6f86SAndrea Mayer info->net = net; 522664d6f86SAndrea Mayer info->vrf_ifindex = vrf_ifindex; 523664d6f86SAndrea Mayer 524664d6f86SAndrea Mayer switch (family) { 525664d6f86SAndrea Mayer case AF_INET: 526664d6f86SAndrea Mayer info->proto = htons(ETH_P_IP); 527664d6f86SAndrea Mayer info->hdrlen = sizeof(struct iphdr); 528664d6f86SAndrea Mayer break; 52920a081b7SAndrea Mayer case AF_INET6: 53020a081b7SAndrea Mayer info->proto = htons(ETH_P_IPV6); 53120a081b7SAndrea Mayer info->hdrlen = sizeof(struct ipv6hdr); 53220a081b7SAndrea Mayer break; 533664d6f86SAndrea Mayer default: 534664d6f86SAndrea Mayer return -EINVAL; 535664d6f86SAndrea Mayer } 536664d6f86SAndrea Mayer 537664d6f86SAndrea Mayer info->family = family; 538664d6f86SAndrea Mayer info->mode = DT_VRF_MODE; 539664d6f86SAndrea Mayer 540664d6f86SAndrea Mayer return 0; 541664d6f86SAndrea Mayer } 542664d6f86SAndrea Mayer 543664d6f86SAndrea Mayer /* The SRv6 End.DT4/DT6 behavior extracts the inner (IPv4/IPv6) packet and 544664d6f86SAndrea Mayer * routes the IPv4/IPv6 packet by looking at the configured routing table. 545664d6f86SAndrea Mayer * 546664d6f86SAndrea Mayer * In the SRv6 End.DT4/DT6 use case, we can receive traffic (IPv6+Segment 547664d6f86SAndrea Mayer * Routing Header packets) from several interfaces and the outer IPv6 548664d6f86SAndrea Mayer * destination address (DA) is used for retrieving the specific instance of the 549664d6f86SAndrea Mayer * End.DT4/DT6 behavior that should process the packets. 550664d6f86SAndrea Mayer * 551664d6f86SAndrea Mayer * However, the inner IPv4/IPv6 packet is not really bound to any receiving 552664d6f86SAndrea Mayer * interface and thus the End.DT4/DT6 sets the VRF (associated with the 553664d6f86SAndrea Mayer * corresponding routing table) as the *receiving* interface. 554664d6f86SAndrea Mayer * In other words, the End.DT4/DT6 processes a packet as if it has been received 555664d6f86SAndrea Mayer * directly by the VRF (and not by one of its slave devices, if any). 556664d6f86SAndrea Mayer * In this way, the VRF interface is used for routing the IPv4/IPv6 packet in 557664d6f86SAndrea Mayer * according to the routing table configured by the End.DT4/DT6 instance. 558664d6f86SAndrea Mayer * 559664d6f86SAndrea Mayer * This design allows you to get some interesting features like: 560664d6f86SAndrea Mayer * 1) the statistics on rx packets; 561664d6f86SAndrea Mayer * 2) the possibility to install a packet sniffer on the receiving interface 562664d6f86SAndrea Mayer * (the VRF one) for looking at the incoming packets; 563664d6f86SAndrea Mayer * 3) the possibility to leverage the netfilter prerouting hook for the inner 564664d6f86SAndrea Mayer * IPv4 packet. 565664d6f86SAndrea Mayer * 566664d6f86SAndrea Mayer * This function returns: 567664d6f86SAndrea Mayer * - the sk_buff* when the VRF rcv handler has processed the packet correctly; 568664d6f86SAndrea Mayer * - NULL when the skb is consumed by the VRF rcv handler; 569664d6f86SAndrea Mayer * - a pointer which encodes a negative error number in case of error. 570664d6f86SAndrea Mayer * Note that in this case, the function takes care of freeing the skb. 571664d6f86SAndrea Mayer */ 572664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_rcv(struct sk_buff *skb, u16 family, 573664d6f86SAndrea Mayer struct net_device *dev) 574664d6f86SAndrea Mayer { 575664d6f86SAndrea Mayer /* based on l3mdev_ip_rcv; we are only interested in the master */ 576664d6f86SAndrea Mayer if (unlikely(!netif_is_l3_master(dev) && !netif_has_l3_rx_handler(dev))) 577664d6f86SAndrea Mayer goto drop; 578664d6f86SAndrea Mayer 579664d6f86SAndrea Mayer if (unlikely(!dev->l3mdev_ops->l3mdev_l3_rcv)) 580664d6f86SAndrea Mayer goto drop; 581664d6f86SAndrea Mayer 582664d6f86SAndrea Mayer /* the decap packet IPv4/IPv6 does not come with any mac header info. 583664d6f86SAndrea Mayer * We must unset the mac header to allow the VRF device to rebuild it, 584664d6f86SAndrea Mayer * just in case there is a sniffer attached on the device. 585664d6f86SAndrea Mayer */ 586664d6f86SAndrea Mayer skb_unset_mac_header(skb); 587664d6f86SAndrea Mayer 588664d6f86SAndrea Mayer skb = dev->l3mdev_ops->l3mdev_l3_rcv(dev, skb, family); 589664d6f86SAndrea Mayer if (!skb) 590664d6f86SAndrea Mayer /* the skb buffer was consumed by the handler */ 591664d6f86SAndrea Mayer return NULL; 592664d6f86SAndrea Mayer 593664d6f86SAndrea Mayer /* when a packet is received by a VRF or by one of its slaves, the 594664d6f86SAndrea Mayer * master device reference is set into the skb. 595664d6f86SAndrea Mayer */ 596664d6f86SAndrea Mayer if (unlikely(skb->dev != dev || skb->skb_iif != dev->ifindex)) 597664d6f86SAndrea Mayer goto drop; 598664d6f86SAndrea Mayer 599664d6f86SAndrea Mayer return skb; 600664d6f86SAndrea Mayer 601664d6f86SAndrea Mayer drop: 602664d6f86SAndrea Mayer kfree_skb(skb); 603664d6f86SAndrea Mayer return ERR_PTR(-EINVAL); 604664d6f86SAndrea Mayer } 605664d6f86SAndrea Mayer 606664d6f86SAndrea Mayer static struct net_device *end_dt_get_vrf_rcu(struct sk_buff *skb, 607664d6f86SAndrea Mayer struct seg6_end_dt_info *info) 608664d6f86SAndrea Mayer { 609664d6f86SAndrea Mayer int vrf_ifindex = info->vrf_ifindex; 610664d6f86SAndrea Mayer struct net *net = info->net; 611664d6f86SAndrea Mayer 612664d6f86SAndrea Mayer if (unlikely(vrf_ifindex < 0)) 613664d6f86SAndrea Mayer goto error; 614664d6f86SAndrea Mayer 615664d6f86SAndrea Mayer if (unlikely(!net_eq(dev_net(skb->dev), net))) 616664d6f86SAndrea Mayer goto error; 617664d6f86SAndrea Mayer 618664d6f86SAndrea Mayer return dev_get_by_index_rcu(net, vrf_ifindex); 619664d6f86SAndrea Mayer 620664d6f86SAndrea Mayer error: 621664d6f86SAndrea Mayer return NULL; 622664d6f86SAndrea Mayer } 623664d6f86SAndrea Mayer 624664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb, 625664d6f86SAndrea Mayer struct seg6_local_lwt *slwt) 626664d6f86SAndrea Mayer { 627664d6f86SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 628664d6f86SAndrea Mayer struct net_device *vrf; 629664d6f86SAndrea Mayer 630664d6f86SAndrea Mayer vrf = end_dt_get_vrf_rcu(skb, info); 631664d6f86SAndrea Mayer if (unlikely(!vrf)) 632664d6f86SAndrea Mayer goto drop; 633664d6f86SAndrea Mayer 634664d6f86SAndrea Mayer skb->protocol = info->proto; 635664d6f86SAndrea Mayer 636664d6f86SAndrea Mayer skb_dst_drop(skb); 637664d6f86SAndrea Mayer 638664d6f86SAndrea Mayer skb_set_transport_header(skb, info->hdrlen); 639664d6f86SAndrea Mayer 640664d6f86SAndrea Mayer return end_dt_vrf_rcv(skb, info->family, vrf); 641664d6f86SAndrea Mayer 642664d6f86SAndrea Mayer drop: 643664d6f86SAndrea Mayer kfree_skb(skb); 644664d6f86SAndrea Mayer return ERR_PTR(-EINVAL); 645664d6f86SAndrea Mayer } 646664d6f86SAndrea Mayer 647664d6f86SAndrea Mayer static int input_action_end_dt4(struct sk_buff *skb, 648664d6f86SAndrea Mayer struct seg6_local_lwt *slwt) 649664d6f86SAndrea Mayer { 650664d6f86SAndrea Mayer struct iphdr *iph; 651664d6f86SAndrea Mayer int err; 652664d6f86SAndrea Mayer 653664d6f86SAndrea Mayer if (!decap_and_validate(skb, IPPROTO_IPIP)) 654664d6f86SAndrea Mayer goto drop; 655664d6f86SAndrea Mayer 656664d6f86SAndrea Mayer if (!pskb_may_pull(skb, sizeof(struct iphdr))) 657664d6f86SAndrea Mayer goto drop; 658664d6f86SAndrea Mayer 659664d6f86SAndrea Mayer skb = end_dt_vrf_core(skb, slwt); 660664d6f86SAndrea Mayer if (!skb) 661664d6f86SAndrea Mayer /* packet has been processed and consumed by the VRF */ 662664d6f86SAndrea Mayer return 0; 663664d6f86SAndrea Mayer 664664d6f86SAndrea Mayer if (IS_ERR(skb)) 665664d6f86SAndrea Mayer return PTR_ERR(skb); 666664d6f86SAndrea Mayer 667664d6f86SAndrea Mayer iph = ip_hdr(skb); 668664d6f86SAndrea Mayer 669664d6f86SAndrea Mayer err = ip_route_input(skb, iph->daddr, iph->saddr, 0, skb->dev); 670664d6f86SAndrea Mayer if (unlikely(err)) 671664d6f86SAndrea Mayer goto drop; 672664d6f86SAndrea Mayer 673664d6f86SAndrea Mayer return dst_input(skb); 674664d6f86SAndrea Mayer 675664d6f86SAndrea Mayer drop: 676664d6f86SAndrea Mayer kfree_skb(skb); 677664d6f86SAndrea Mayer return -EINVAL; 678664d6f86SAndrea Mayer } 679664d6f86SAndrea Mayer 680664d6f86SAndrea Mayer static int seg6_end_dt4_build(struct seg6_local_lwt *slwt, const void *cfg, 681664d6f86SAndrea Mayer struct netlink_ext_ack *extack) 682664d6f86SAndrea Mayer { 683664d6f86SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET, extack); 684664d6f86SAndrea Mayer } 68520a081b7SAndrea Mayer 68620a081b7SAndrea Mayer static enum 68720a081b7SAndrea Mayer seg6_end_dt_mode seg6_end_dt6_parse_mode(struct seg6_local_lwt *slwt) 68820a081b7SAndrea Mayer { 68920a081b7SAndrea Mayer unsigned long parsed_optattrs = slwt->parsed_optattrs; 69020a081b7SAndrea Mayer bool legacy, vrfmode; 69120a081b7SAndrea Mayer 692300a0fd8SAndrea Mayer legacy = !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE)); 693300a0fd8SAndrea Mayer vrfmode = !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE)); 69420a081b7SAndrea Mayer 69520a081b7SAndrea Mayer if (!(legacy ^ vrfmode)) 69620a081b7SAndrea Mayer /* both are absent or present: invalid DT6 mode */ 69720a081b7SAndrea Mayer return DT_INVALID_MODE; 69820a081b7SAndrea Mayer 69920a081b7SAndrea Mayer return legacy ? DT_LEGACY_MODE : DT_VRF_MODE; 70020a081b7SAndrea Mayer } 70120a081b7SAndrea Mayer 70220a081b7SAndrea Mayer static enum seg6_end_dt_mode seg6_end_dt6_get_mode(struct seg6_local_lwt *slwt) 70320a081b7SAndrea Mayer { 70420a081b7SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 70520a081b7SAndrea Mayer 70620a081b7SAndrea Mayer return info->mode; 70720a081b7SAndrea Mayer } 70820a081b7SAndrea Mayer 70920a081b7SAndrea Mayer static int seg6_end_dt6_build(struct seg6_local_lwt *slwt, const void *cfg, 71020a081b7SAndrea Mayer struct netlink_ext_ack *extack) 71120a081b7SAndrea Mayer { 71220a081b7SAndrea Mayer enum seg6_end_dt_mode mode = seg6_end_dt6_parse_mode(slwt); 71320a081b7SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 71420a081b7SAndrea Mayer 71520a081b7SAndrea Mayer switch (mode) { 71620a081b7SAndrea Mayer case DT_LEGACY_MODE: 71720a081b7SAndrea Mayer info->mode = DT_LEGACY_MODE; 71820a081b7SAndrea Mayer return 0; 71920a081b7SAndrea Mayer case DT_VRF_MODE: 72020a081b7SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET6, extack); 72120a081b7SAndrea Mayer default: 72220a081b7SAndrea Mayer NL_SET_ERR_MSG(extack, "table or vrftable must be specified"); 72320a081b7SAndrea Mayer return -EINVAL; 72420a081b7SAndrea Mayer } 72520a081b7SAndrea Mayer } 726664d6f86SAndrea Mayer #endif 727664d6f86SAndrea Mayer 728891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb, 729891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 730891ef8ddSDavid Lebrun { 731891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 732891ef8ddSDavid Lebrun goto drop; 733891ef8ddSDavid Lebrun 734891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 735891ef8ddSDavid Lebrun goto drop; 736891ef8ddSDavid Lebrun 73720a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 73820a081b7SAndrea Mayer if (seg6_end_dt6_get_mode(slwt) == DT_LEGACY_MODE) 73920a081b7SAndrea Mayer goto legacy_mode; 74020a081b7SAndrea Mayer 74120a081b7SAndrea Mayer /* DT6_VRF_MODE */ 74220a081b7SAndrea Mayer skb = end_dt_vrf_core(skb, slwt); 74320a081b7SAndrea Mayer if (!skb) 74420a081b7SAndrea Mayer /* packet has been processed and consumed by the VRF */ 74520a081b7SAndrea Mayer return 0; 74620a081b7SAndrea Mayer 74720a081b7SAndrea Mayer if (IS_ERR(skb)) 74820a081b7SAndrea Mayer return PTR_ERR(skb); 74920a081b7SAndrea Mayer 75020a081b7SAndrea Mayer /* note: this time we do not need to specify the table because the VRF 75120a081b7SAndrea Mayer * takes care of selecting the correct table. 75220a081b7SAndrea Mayer */ 75320a081b7SAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, 0, true); 75420a081b7SAndrea Mayer 75520a081b7SAndrea Mayer return dst_input(skb); 75620a081b7SAndrea Mayer 75720a081b7SAndrea Mayer legacy_mode: 75820a081b7SAndrea Mayer #endif 759c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 760c71644d0SAndrea Mayer 761fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, slwt->table, true); 762891ef8ddSDavid Lebrun 763891ef8ddSDavid Lebrun return dst_input(skb); 764891ef8ddSDavid Lebrun 765891ef8ddSDavid Lebrun drop: 766891ef8ddSDavid Lebrun kfree_skb(skb); 767891ef8ddSDavid Lebrun return -EINVAL; 768891ef8ddSDavid Lebrun } 769891ef8ddSDavid Lebrun 770140f04c3SDavid Lebrun /* push an SRH on top of the current one */ 771140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 772140f04c3SDavid Lebrun { 773140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 774140f04c3SDavid Lebrun int err = -EINVAL; 775140f04c3SDavid Lebrun 776140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 777140f04c3SDavid Lebrun if (!srh) 778140f04c3SDavid Lebrun goto drop; 779140f04c3SDavid Lebrun 780140f04c3SDavid Lebrun err = seg6_do_srh_inline(skb, slwt->srh); 781140f04c3SDavid Lebrun if (err) 782140f04c3SDavid Lebrun goto drop; 783140f04c3SDavid Lebrun 784140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 785140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 786140f04c3SDavid Lebrun 7871c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 788140f04c3SDavid Lebrun 789140f04c3SDavid Lebrun return dst_input(skb); 790140f04c3SDavid Lebrun 791140f04c3SDavid Lebrun drop: 792140f04c3SDavid Lebrun kfree_skb(skb); 793140f04c3SDavid Lebrun return err; 794140f04c3SDavid Lebrun } 795140f04c3SDavid Lebrun 796140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */ 797140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb, 798140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 799140f04c3SDavid Lebrun { 800140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 801140f04c3SDavid Lebrun int err = -EINVAL; 802140f04c3SDavid Lebrun 803140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 804140f04c3SDavid Lebrun if (!srh) 805140f04c3SDavid Lebrun goto drop; 806140f04c3SDavid Lebrun 807d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 808140f04c3SDavid Lebrun 809140f04c3SDavid Lebrun skb_reset_inner_headers(skb); 810140f04c3SDavid Lebrun skb->encapsulation = 1; 811140f04c3SDavid Lebrun 81232d99d0bSDavid Lebrun err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6); 813140f04c3SDavid Lebrun if (err) 814140f04c3SDavid Lebrun goto drop; 815140f04c3SDavid Lebrun 816140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 817140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 818140f04c3SDavid Lebrun 8191c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 820140f04c3SDavid Lebrun 821140f04c3SDavid Lebrun return dst_input(skb); 822140f04c3SDavid Lebrun 823140f04c3SDavid Lebrun drop: 824140f04c3SDavid Lebrun kfree_skb(skb); 825140f04c3SDavid Lebrun return err; 826140f04c3SDavid Lebrun } 827140f04c3SDavid Lebrun 828fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states); 829fe94cc29SMathieu Xhonneux 830486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb) 831486cdf21SMathieu Xhonneux { 832486cdf21SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 833486cdf21SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 834486cdf21SMathieu Xhonneux struct ipv6_sr_hdr *srh = srh_state->srh; 835486cdf21SMathieu Xhonneux 836486cdf21SMathieu Xhonneux if (unlikely(srh == NULL)) 837486cdf21SMathieu Xhonneux return false; 838486cdf21SMathieu Xhonneux 839486cdf21SMathieu Xhonneux if (unlikely(!srh_state->valid)) { 840486cdf21SMathieu Xhonneux if ((srh_state->hdrlen & 7) != 0) 841486cdf21SMathieu Xhonneux return false; 842486cdf21SMathieu Xhonneux 843486cdf21SMathieu Xhonneux srh->hdrlen = (u8)(srh_state->hdrlen >> 3); 844bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true)) 845486cdf21SMathieu Xhonneux return false; 846486cdf21SMathieu Xhonneux 847486cdf21SMathieu Xhonneux srh_state->valid = true; 848486cdf21SMathieu Xhonneux } 849486cdf21SMathieu Xhonneux 850486cdf21SMathieu Xhonneux return true; 851486cdf21SMathieu Xhonneux } 852486cdf21SMathieu Xhonneux 853004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb, 854004d4b27SMathieu Xhonneux struct seg6_local_lwt *slwt) 855004d4b27SMathieu Xhonneux { 856004d4b27SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 857004d4b27SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 858004d4b27SMathieu Xhonneux struct ipv6_sr_hdr *srh; 859004d4b27SMathieu Xhonneux int ret; 860004d4b27SMathieu Xhonneux 861004d4b27SMathieu Xhonneux srh = get_and_validate_srh(skb); 862486cdf21SMathieu Xhonneux if (!srh) { 863486cdf21SMathieu Xhonneux kfree_skb(skb); 864486cdf21SMathieu Xhonneux return -EINVAL; 865486cdf21SMathieu Xhonneux } 866004d4b27SMathieu Xhonneux advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 867004d4b27SMathieu Xhonneux 868004d4b27SMathieu Xhonneux /* preempt_disable is needed to protect the per-CPU buffer srh_state, 869004d4b27SMathieu Xhonneux * which is also accessed by the bpf_lwt_seg6_* helpers 870004d4b27SMathieu Xhonneux */ 871004d4b27SMathieu Xhonneux preempt_disable(); 872486cdf21SMathieu Xhonneux srh_state->srh = srh; 873004d4b27SMathieu Xhonneux srh_state->hdrlen = srh->hdrlen << 3; 874486cdf21SMathieu Xhonneux srh_state->valid = true; 875004d4b27SMathieu Xhonneux 876004d4b27SMathieu Xhonneux rcu_read_lock(); 877004d4b27SMathieu Xhonneux bpf_compute_data_pointers(skb); 878004d4b27SMathieu Xhonneux ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb); 879004d4b27SMathieu Xhonneux rcu_read_unlock(); 880004d4b27SMathieu Xhonneux 881004d4b27SMathieu Xhonneux switch (ret) { 882004d4b27SMathieu Xhonneux case BPF_OK: 883004d4b27SMathieu Xhonneux case BPF_REDIRECT: 884004d4b27SMathieu Xhonneux break; 885004d4b27SMathieu Xhonneux case BPF_DROP: 886004d4b27SMathieu Xhonneux goto drop; 887004d4b27SMathieu Xhonneux default: 888004d4b27SMathieu Xhonneux pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret); 889004d4b27SMathieu Xhonneux goto drop; 890004d4b27SMathieu Xhonneux } 891004d4b27SMathieu Xhonneux 892486cdf21SMathieu Xhonneux if (srh_state->srh && !seg6_bpf_has_valid_srh(skb)) 893004d4b27SMathieu Xhonneux goto drop; 894004d4b27SMathieu Xhonneux 895486cdf21SMathieu Xhonneux preempt_enable(); 896004d4b27SMathieu Xhonneux if (ret != BPF_REDIRECT) 897004d4b27SMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 898004d4b27SMathieu Xhonneux 899004d4b27SMathieu Xhonneux return dst_input(skb); 900004d4b27SMathieu Xhonneux 901004d4b27SMathieu Xhonneux drop: 902486cdf21SMathieu Xhonneux preempt_enable(); 903004d4b27SMathieu Xhonneux kfree_skb(skb); 904004d4b27SMathieu Xhonneux return -EINVAL; 905004d4b27SMathieu Xhonneux } 906004d4b27SMathieu Xhonneux 907d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = { 908d1df6fd8SDavid Lebrun { 909d1df6fd8SDavid Lebrun .action = SEG6_LOCAL_ACTION_END, 910d1df6fd8SDavid Lebrun .attrs = 0, 911*94604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 912140f04c3SDavid Lebrun .input = input_action_end, 913d1df6fd8SDavid Lebrun }, 914140f04c3SDavid Lebrun { 915140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_X, 916300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), 917*94604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 918140f04c3SDavid Lebrun .input = input_action_end_x, 919140f04c3SDavid Lebrun }, 920140f04c3SDavid Lebrun { 921891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_T, 922300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE), 923*94604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 924891ef8ddSDavid Lebrun .input = input_action_end_t, 925891ef8ddSDavid Lebrun }, 926891ef8ddSDavid Lebrun { 927891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX2, 928300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_OIF), 929*94604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 930891ef8ddSDavid Lebrun .input = input_action_end_dx2, 931891ef8ddSDavid Lebrun }, 932891ef8ddSDavid Lebrun { 933140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX6, 934300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), 935*94604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 936140f04c3SDavid Lebrun .input = input_action_end_dx6, 937140f04c3SDavid Lebrun }, 938140f04c3SDavid Lebrun { 939891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX4, 940300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH4), 941*94604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 942891ef8ddSDavid Lebrun .input = input_action_end_dx4, 943891ef8ddSDavid Lebrun }, 944891ef8ddSDavid Lebrun { 945664d6f86SAndrea Mayer .action = SEG6_LOCAL_ACTION_END_DT4, 946300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 947*94604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 948664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 949664d6f86SAndrea Mayer .input = input_action_end_dt4, 950664d6f86SAndrea Mayer .slwt_ops = { 951664d6f86SAndrea Mayer .build_state = seg6_end_dt4_build, 952664d6f86SAndrea Mayer }, 953664d6f86SAndrea Mayer #endif 954664d6f86SAndrea Mayer }, 955664d6f86SAndrea Mayer { 956891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DT6, 95720a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 95820a081b7SAndrea Mayer .attrs = 0, 959*94604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS | 960*94604548SAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_TABLE) | 961300a0fd8SAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 96220a081b7SAndrea Mayer .slwt_ops = { 96320a081b7SAndrea Mayer .build_state = seg6_end_dt6_build, 96420a081b7SAndrea Mayer }, 96520a081b7SAndrea Mayer #else 966300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE), 967*94604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 96820a081b7SAndrea Mayer #endif 969891ef8ddSDavid Lebrun .input = input_action_end_dt6, 970891ef8ddSDavid Lebrun }, 971891ef8ddSDavid Lebrun { 972140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6, 973300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH), 974*94604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 975140f04c3SDavid Lebrun .input = input_action_end_b6, 976140f04c3SDavid Lebrun }, 977140f04c3SDavid Lebrun { 978140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6_ENCAP, 979300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH), 980*94604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 981140f04c3SDavid Lebrun .input = input_action_end_b6_encap, 982140f04c3SDavid Lebrun .static_headroom = sizeof(struct ipv6hdr), 983004d4b27SMathieu Xhonneux }, 984004d4b27SMathieu Xhonneux { 985004d4b27SMathieu Xhonneux .action = SEG6_LOCAL_ACTION_END_BPF, 986300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_BPF), 987*94604548SAndrea Mayer .optattrs = SEG6_F_LOCAL_COUNTERS, 988004d4b27SMathieu Xhonneux .input = input_action_end_bpf, 989004d4b27SMathieu Xhonneux }, 990004d4b27SMathieu Xhonneux 991d1df6fd8SDavid Lebrun }; 992d1df6fd8SDavid Lebrun 993d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action) 994d1df6fd8SDavid Lebrun { 995d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 996d1df6fd8SDavid Lebrun int i, count; 997d1df6fd8SDavid Lebrun 998709af180SColin Ian King count = ARRAY_SIZE(seg6_action_table); 999d1df6fd8SDavid Lebrun for (i = 0; i < count; i++) { 1000d1df6fd8SDavid Lebrun desc = &seg6_action_table[i]; 1001d1df6fd8SDavid Lebrun if (desc->action == action) 1002d1df6fd8SDavid Lebrun return desc; 1003d1df6fd8SDavid Lebrun } 1004d1df6fd8SDavid Lebrun 1005d1df6fd8SDavid Lebrun return NULL; 1006d1df6fd8SDavid Lebrun } 1007d1df6fd8SDavid Lebrun 1008*94604548SAndrea Mayer static bool seg6_lwtunnel_counters_enabled(struct seg6_local_lwt *slwt) 1009*94604548SAndrea Mayer { 1010*94604548SAndrea Mayer return slwt->parsed_optattrs & SEG6_F_LOCAL_COUNTERS; 1011*94604548SAndrea Mayer } 1012*94604548SAndrea Mayer 1013*94604548SAndrea Mayer static void seg6_local_update_counters(struct seg6_local_lwt *slwt, 1014*94604548SAndrea Mayer unsigned int len, int err) 1015*94604548SAndrea Mayer { 1016*94604548SAndrea Mayer struct pcpu_seg6_local_counters *pcounters; 1017*94604548SAndrea Mayer 1018*94604548SAndrea Mayer pcounters = this_cpu_ptr(slwt->pcpu_counters); 1019*94604548SAndrea Mayer u64_stats_update_begin(&pcounters->syncp); 1020*94604548SAndrea Mayer 1021*94604548SAndrea Mayer if (likely(!err)) { 1022*94604548SAndrea Mayer u64_stats_inc(&pcounters->packets); 1023*94604548SAndrea Mayer u64_stats_add(&pcounters->bytes, len); 1024*94604548SAndrea Mayer } else { 1025*94604548SAndrea Mayer u64_stats_inc(&pcounters->errors); 1026*94604548SAndrea Mayer } 1027*94604548SAndrea Mayer 1028*94604548SAndrea Mayer u64_stats_update_end(&pcounters->syncp); 1029*94604548SAndrea Mayer } 1030*94604548SAndrea Mayer 1031d1df6fd8SDavid Lebrun static int seg6_local_input(struct sk_buff *skb) 1032d1df6fd8SDavid Lebrun { 1033d1df6fd8SDavid Lebrun struct dst_entry *orig_dst = skb_dst(skb); 1034d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1035d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 1036*94604548SAndrea Mayer unsigned int len = skb->len; 1037*94604548SAndrea Mayer int rc; 1038d1df6fd8SDavid Lebrun 10396285217fSDavid Lebrun if (skb->protocol != htons(ETH_P_IPV6)) { 10406285217fSDavid Lebrun kfree_skb(skb); 10416285217fSDavid Lebrun return -EINVAL; 10426285217fSDavid Lebrun } 10436285217fSDavid Lebrun 1044d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 1045d1df6fd8SDavid Lebrun desc = slwt->desc; 1046d1df6fd8SDavid Lebrun 1047*94604548SAndrea Mayer rc = desc->input(skb, slwt); 1048*94604548SAndrea Mayer 1049*94604548SAndrea Mayer if (!seg6_lwtunnel_counters_enabled(slwt)) 1050*94604548SAndrea Mayer return rc; 1051*94604548SAndrea Mayer 1052*94604548SAndrea Mayer seg6_local_update_counters(slwt, len, rc); 1053*94604548SAndrea Mayer 1054*94604548SAndrea Mayer return rc; 1055d1df6fd8SDavid Lebrun } 1056d1df6fd8SDavid Lebrun 1057d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { 1058d1df6fd8SDavid Lebrun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, 1059d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, 1060d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, 1061664d6f86SAndrea Mayer [SEG6_LOCAL_VRFTABLE] = { .type = NLA_U32 }, 1062d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, 1063d1df6fd8SDavid Lebrun .len = sizeof(struct in_addr) }, 1064d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, 1065d1df6fd8SDavid Lebrun .len = sizeof(struct in6_addr) }, 1066d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, 1067d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 1068004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .type = NLA_NESTED }, 1069*94604548SAndrea Mayer [SEG6_LOCAL_COUNTERS] = { .type = NLA_NESTED }, 1070d1df6fd8SDavid Lebrun }; 1071d1df6fd8SDavid Lebrun 10722d9cc60aSDavid Lebrun static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt) 10732d9cc60aSDavid Lebrun { 10742d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 10752d9cc60aSDavid Lebrun int len; 10762d9cc60aSDavid Lebrun 10772d9cc60aSDavid Lebrun srh = nla_data(attrs[SEG6_LOCAL_SRH]); 10782d9cc60aSDavid Lebrun len = nla_len(attrs[SEG6_LOCAL_SRH]); 10792d9cc60aSDavid Lebrun 10802d9cc60aSDavid Lebrun /* SRH must contain at least one segment */ 10812d9cc60aSDavid Lebrun if (len < sizeof(*srh) + sizeof(struct in6_addr)) 10822d9cc60aSDavid Lebrun return -EINVAL; 10832d9cc60aSDavid Lebrun 1084bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, false)) 10852d9cc60aSDavid Lebrun return -EINVAL; 10862d9cc60aSDavid Lebrun 10877fa41efaSYueHaibing slwt->srh = kmemdup(srh, len, GFP_KERNEL); 10882d9cc60aSDavid Lebrun if (!slwt->srh) 10892d9cc60aSDavid Lebrun return -ENOMEM; 10902d9cc60aSDavid Lebrun 10912d9cc60aSDavid Lebrun slwt->headroom += len; 10922d9cc60aSDavid Lebrun 10932d9cc60aSDavid Lebrun return 0; 10942d9cc60aSDavid Lebrun } 10952d9cc60aSDavid Lebrun 10962d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt) 10972d9cc60aSDavid Lebrun { 10982d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 10992d9cc60aSDavid Lebrun struct nlattr *nla; 11002d9cc60aSDavid Lebrun int len; 11012d9cc60aSDavid Lebrun 11022d9cc60aSDavid Lebrun srh = slwt->srh; 11032d9cc60aSDavid Lebrun len = (srh->hdrlen + 1) << 3; 11042d9cc60aSDavid Lebrun 11052d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_SRH, len); 11062d9cc60aSDavid Lebrun if (!nla) 11072d9cc60aSDavid Lebrun return -EMSGSIZE; 11082d9cc60aSDavid Lebrun 11092d9cc60aSDavid Lebrun memcpy(nla_data(nla), srh, len); 11102d9cc60aSDavid Lebrun 11112d9cc60aSDavid Lebrun return 0; 11122d9cc60aSDavid Lebrun } 11132d9cc60aSDavid Lebrun 11142d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 11152d9cc60aSDavid Lebrun { 11162d9cc60aSDavid Lebrun int len = (a->srh->hdrlen + 1) << 3; 11172d9cc60aSDavid Lebrun 11182d9cc60aSDavid Lebrun if (len != ((b->srh->hdrlen + 1) << 3)) 11192d9cc60aSDavid Lebrun return 1; 11202d9cc60aSDavid Lebrun 11212d9cc60aSDavid Lebrun return memcmp(a->srh, b->srh, len); 11222d9cc60aSDavid Lebrun } 11232d9cc60aSDavid Lebrun 1124964adce5SAndrea Mayer static void destroy_attr_srh(struct seg6_local_lwt *slwt) 1125964adce5SAndrea Mayer { 1126964adce5SAndrea Mayer kfree(slwt->srh); 1127964adce5SAndrea Mayer } 1128964adce5SAndrea Mayer 11292d9cc60aSDavid Lebrun static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt) 11302d9cc60aSDavid Lebrun { 11312d9cc60aSDavid Lebrun slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]); 11322d9cc60aSDavid Lebrun 11332d9cc60aSDavid Lebrun return 0; 11342d9cc60aSDavid Lebrun } 11352d9cc60aSDavid Lebrun 11362d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt) 11372d9cc60aSDavid Lebrun { 11382d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table)) 11392d9cc60aSDavid Lebrun return -EMSGSIZE; 11402d9cc60aSDavid Lebrun 11412d9cc60aSDavid Lebrun return 0; 11422d9cc60aSDavid Lebrun } 11432d9cc60aSDavid Lebrun 11442d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 11452d9cc60aSDavid Lebrun { 11462d9cc60aSDavid Lebrun if (a->table != b->table) 11472d9cc60aSDavid Lebrun return 1; 11482d9cc60aSDavid Lebrun 11492d9cc60aSDavid Lebrun return 0; 11502d9cc60aSDavid Lebrun } 11512d9cc60aSDavid Lebrun 1152664d6f86SAndrea Mayer static struct 1153664d6f86SAndrea Mayer seg6_end_dt_info *seg6_possible_end_dt_info(struct seg6_local_lwt *slwt) 1154664d6f86SAndrea Mayer { 1155664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 1156664d6f86SAndrea Mayer return &slwt->dt_info; 1157664d6f86SAndrea Mayer #else 1158664d6f86SAndrea Mayer return ERR_PTR(-EOPNOTSUPP); 1159664d6f86SAndrea Mayer #endif 1160664d6f86SAndrea Mayer } 1161664d6f86SAndrea Mayer 1162664d6f86SAndrea Mayer static int parse_nla_vrftable(struct nlattr **attrs, 1163664d6f86SAndrea Mayer struct seg6_local_lwt *slwt) 1164664d6f86SAndrea Mayer { 1165664d6f86SAndrea Mayer struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt); 1166664d6f86SAndrea Mayer 1167664d6f86SAndrea Mayer if (IS_ERR(info)) 1168664d6f86SAndrea Mayer return PTR_ERR(info); 1169664d6f86SAndrea Mayer 1170664d6f86SAndrea Mayer info->vrf_table = nla_get_u32(attrs[SEG6_LOCAL_VRFTABLE]); 1171664d6f86SAndrea Mayer 1172664d6f86SAndrea Mayer return 0; 1173664d6f86SAndrea Mayer } 1174664d6f86SAndrea Mayer 1175664d6f86SAndrea Mayer static int put_nla_vrftable(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1176664d6f86SAndrea Mayer { 1177664d6f86SAndrea Mayer struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt); 1178664d6f86SAndrea Mayer 1179664d6f86SAndrea Mayer if (IS_ERR(info)) 1180664d6f86SAndrea Mayer return PTR_ERR(info); 1181664d6f86SAndrea Mayer 1182664d6f86SAndrea Mayer if (nla_put_u32(skb, SEG6_LOCAL_VRFTABLE, info->vrf_table)) 1183664d6f86SAndrea Mayer return -EMSGSIZE; 1184664d6f86SAndrea Mayer 1185664d6f86SAndrea Mayer return 0; 1186664d6f86SAndrea Mayer } 1187664d6f86SAndrea Mayer 1188664d6f86SAndrea Mayer static int cmp_nla_vrftable(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1189664d6f86SAndrea Mayer { 1190664d6f86SAndrea Mayer struct seg6_end_dt_info *info_a = seg6_possible_end_dt_info(a); 1191664d6f86SAndrea Mayer struct seg6_end_dt_info *info_b = seg6_possible_end_dt_info(b); 1192664d6f86SAndrea Mayer 1193664d6f86SAndrea Mayer if (info_a->vrf_table != info_b->vrf_table) 1194664d6f86SAndrea Mayer return 1; 1195664d6f86SAndrea Mayer 1196664d6f86SAndrea Mayer return 0; 1197664d6f86SAndrea Mayer } 1198664d6f86SAndrea Mayer 11992d9cc60aSDavid Lebrun static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt) 12002d9cc60aSDavid Lebrun { 12012d9cc60aSDavid Lebrun memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]), 12022d9cc60aSDavid Lebrun sizeof(struct in_addr)); 12032d9cc60aSDavid Lebrun 12042d9cc60aSDavid Lebrun return 0; 12052d9cc60aSDavid Lebrun } 12062d9cc60aSDavid Lebrun 12072d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt) 12082d9cc60aSDavid Lebrun { 12092d9cc60aSDavid Lebrun struct nlattr *nla; 12102d9cc60aSDavid Lebrun 12112d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr)); 12122d9cc60aSDavid Lebrun if (!nla) 12132d9cc60aSDavid Lebrun return -EMSGSIZE; 12142d9cc60aSDavid Lebrun 12152d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr)); 12162d9cc60aSDavid Lebrun 12172d9cc60aSDavid Lebrun return 0; 12182d9cc60aSDavid Lebrun } 12192d9cc60aSDavid Lebrun 12202d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 12212d9cc60aSDavid Lebrun { 12222d9cc60aSDavid Lebrun return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr)); 12232d9cc60aSDavid Lebrun } 12242d9cc60aSDavid Lebrun 12252d9cc60aSDavid Lebrun static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt) 12262d9cc60aSDavid Lebrun { 12272d9cc60aSDavid Lebrun memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]), 12282d9cc60aSDavid Lebrun sizeof(struct in6_addr)); 12292d9cc60aSDavid Lebrun 12302d9cc60aSDavid Lebrun return 0; 12312d9cc60aSDavid Lebrun } 12322d9cc60aSDavid Lebrun 12332d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 12342d9cc60aSDavid Lebrun { 12352d9cc60aSDavid Lebrun struct nlattr *nla; 12362d9cc60aSDavid Lebrun 12372d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr)); 12382d9cc60aSDavid Lebrun if (!nla) 12392d9cc60aSDavid Lebrun return -EMSGSIZE; 12402d9cc60aSDavid Lebrun 12412d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr)); 12422d9cc60aSDavid Lebrun 12432d9cc60aSDavid Lebrun return 0; 12442d9cc60aSDavid Lebrun } 12452d9cc60aSDavid Lebrun 12462d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 12472d9cc60aSDavid Lebrun { 12482d9cc60aSDavid Lebrun return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr)); 12492d9cc60aSDavid Lebrun } 12502d9cc60aSDavid Lebrun 12512d9cc60aSDavid Lebrun static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 12522d9cc60aSDavid Lebrun { 12532d9cc60aSDavid Lebrun slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]); 12542d9cc60aSDavid Lebrun 12552d9cc60aSDavid Lebrun return 0; 12562d9cc60aSDavid Lebrun } 12572d9cc60aSDavid Lebrun 12582d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 12592d9cc60aSDavid Lebrun { 12602d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif)) 12612d9cc60aSDavid Lebrun return -EMSGSIZE; 12622d9cc60aSDavid Lebrun 12632d9cc60aSDavid Lebrun return 0; 12642d9cc60aSDavid Lebrun } 12652d9cc60aSDavid Lebrun 12662d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 12672d9cc60aSDavid Lebrun { 12682d9cc60aSDavid Lebrun if (a->iif != b->iif) 12692d9cc60aSDavid Lebrun return 1; 12702d9cc60aSDavid Lebrun 12712d9cc60aSDavid Lebrun return 0; 12722d9cc60aSDavid Lebrun } 12732d9cc60aSDavid Lebrun 12742d9cc60aSDavid Lebrun static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 12752d9cc60aSDavid Lebrun { 12762d9cc60aSDavid Lebrun slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]); 12772d9cc60aSDavid Lebrun 12782d9cc60aSDavid Lebrun return 0; 12792d9cc60aSDavid Lebrun } 12802d9cc60aSDavid Lebrun 12812d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 12822d9cc60aSDavid Lebrun { 12832d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif)) 12842d9cc60aSDavid Lebrun return -EMSGSIZE; 12852d9cc60aSDavid Lebrun 12862d9cc60aSDavid Lebrun return 0; 12872d9cc60aSDavid Lebrun } 12882d9cc60aSDavid Lebrun 12892d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 12902d9cc60aSDavid Lebrun { 12912d9cc60aSDavid Lebrun if (a->oif != b->oif) 12922d9cc60aSDavid Lebrun return 1; 12932d9cc60aSDavid Lebrun 12942d9cc60aSDavid Lebrun return 0; 12952d9cc60aSDavid Lebrun } 12962d9cc60aSDavid Lebrun 1297004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256 1298004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = { 1299004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG] = { .type = NLA_U32, }, 1300004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING, 1301004d4b27SMathieu Xhonneux .len = MAX_PROG_NAME }, 1302004d4b27SMathieu Xhonneux }; 1303004d4b27SMathieu Xhonneux 1304004d4b27SMathieu Xhonneux static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt) 1305004d4b27SMathieu Xhonneux { 1306004d4b27SMathieu Xhonneux struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1]; 1307004d4b27SMathieu Xhonneux struct bpf_prog *p; 1308004d4b27SMathieu Xhonneux int ret; 1309004d4b27SMathieu Xhonneux u32 fd; 1310004d4b27SMathieu Xhonneux 13118cb08174SJohannes Berg ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX, 13128cb08174SJohannes Berg attrs[SEG6_LOCAL_BPF], 13138cb08174SJohannes Berg bpf_prog_policy, NULL); 1314004d4b27SMathieu Xhonneux if (ret < 0) 1315004d4b27SMathieu Xhonneux return ret; 1316004d4b27SMathieu Xhonneux 1317004d4b27SMathieu Xhonneux if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME]) 1318004d4b27SMathieu Xhonneux return -EINVAL; 1319004d4b27SMathieu Xhonneux 1320004d4b27SMathieu Xhonneux slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL); 1321004d4b27SMathieu Xhonneux if (!slwt->bpf.name) 1322004d4b27SMathieu Xhonneux return -ENOMEM; 1323004d4b27SMathieu Xhonneux 1324004d4b27SMathieu Xhonneux fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]); 1325004d4b27SMathieu Xhonneux p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL); 1326004d4b27SMathieu Xhonneux if (IS_ERR(p)) { 1327004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 1328004d4b27SMathieu Xhonneux return PTR_ERR(p); 1329004d4b27SMathieu Xhonneux } 1330004d4b27SMathieu Xhonneux 1331004d4b27SMathieu Xhonneux slwt->bpf.prog = p; 1332004d4b27SMathieu Xhonneux return 0; 1333004d4b27SMathieu Xhonneux } 1334004d4b27SMathieu Xhonneux 1335004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1336004d4b27SMathieu Xhonneux { 1337004d4b27SMathieu Xhonneux struct nlattr *nest; 1338004d4b27SMathieu Xhonneux 1339004d4b27SMathieu Xhonneux if (!slwt->bpf.prog) 1340004d4b27SMathieu Xhonneux return 0; 1341004d4b27SMathieu Xhonneux 1342ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF); 1343004d4b27SMathieu Xhonneux if (!nest) 1344004d4b27SMathieu Xhonneux return -EMSGSIZE; 1345004d4b27SMathieu Xhonneux 1346004d4b27SMathieu Xhonneux if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id)) 1347004d4b27SMathieu Xhonneux return -EMSGSIZE; 1348004d4b27SMathieu Xhonneux 1349004d4b27SMathieu Xhonneux if (slwt->bpf.name && 1350004d4b27SMathieu Xhonneux nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name)) 1351004d4b27SMathieu Xhonneux return -EMSGSIZE; 1352004d4b27SMathieu Xhonneux 1353004d4b27SMathieu Xhonneux return nla_nest_end(skb, nest); 1354004d4b27SMathieu Xhonneux } 1355004d4b27SMathieu Xhonneux 1356004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1357004d4b27SMathieu Xhonneux { 1358004d4b27SMathieu Xhonneux if (!a->bpf.name && !b->bpf.name) 1359004d4b27SMathieu Xhonneux return 0; 1360004d4b27SMathieu Xhonneux 1361004d4b27SMathieu Xhonneux if (!a->bpf.name || !b->bpf.name) 1362004d4b27SMathieu Xhonneux return 1; 1363004d4b27SMathieu Xhonneux 1364004d4b27SMathieu Xhonneux return strcmp(a->bpf.name, b->bpf.name); 1365004d4b27SMathieu Xhonneux } 1366004d4b27SMathieu Xhonneux 1367964adce5SAndrea Mayer static void destroy_attr_bpf(struct seg6_local_lwt *slwt) 1368964adce5SAndrea Mayer { 1369964adce5SAndrea Mayer kfree(slwt->bpf.name); 1370964adce5SAndrea Mayer if (slwt->bpf.prog) 1371964adce5SAndrea Mayer bpf_prog_put(slwt->bpf.prog); 1372964adce5SAndrea Mayer } 1373964adce5SAndrea Mayer 1374*94604548SAndrea Mayer static const struct 1375*94604548SAndrea Mayer nla_policy seg6_local_counters_policy[SEG6_LOCAL_CNT_MAX + 1] = { 1376*94604548SAndrea Mayer [SEG6_LOCAL_CNT_PACKETS] = { .type = NLA_U64 }, 1377*94604548SAndrea Mayer [SEG6_LOCAL_CNT_BYTES] = { .type = NLA_U64 }, 1378*94604548SAndrea Mayer [SEG6_LOCAL_CNT_ERRORS] = { .type = NLA_U64 }, 1379*94604548SAndrea Mayer }; 1380*94604548SAndrea Mayer 1381*94604548SAndrea Mayer static int parse_nla_counters(struct nlattr **attrs, 1382*94604548SAndrea Mayer struct seg6_local_lwt *slwt) 1383*94604548SAndrea Mayer { 1384*94604548SAndrea Mayer struct pcpu_seg6_local_counters __percpu *pcounters; 1385*94604548SAndrea Mayer struct nlattr *tb[SEG6_LOCAL_CNT_MAX + 1]; 1386*94604548SAndrea Mayer int ret; 1387*94604548SAndrea Mayer 1388*94604548SAndrea Mayer ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_CNT_MAX, 1389*94604548SAndrea Mayer attrs[SEG6_LOCAL_COUNTERS], 1390*94604548SAndrea Mayer seg6_local_counters_policy, NULL); 1391*94604548SAndrea Mayer if (ret < 0) 1392*94604548SAndrea Mayer return ret; 1393*94604548SAndrea Mayer 1394*94604548SAndrea Mayer /* basic support for SRv6 Behavior counters requires at least: 1395*94604548SAndrea Mayer * packets, bytes and errors. 1396*94604548SAndrea Mayer */ 1397*94604548SAndrea Mayer if (!tb[SEG6_LOCAL_CNT_PACKETS] || !tb[SEG6_LOCAL_CNT_BYTES] || 1398*94604548SAndrea Mayer !tb[SEG6_LOCAL_CNT_ERRORS]) 1399*94604548SAndrea Mayer return -EINVAL; 1400*94604548SAndrea Mayer 1401*94604548SAndrea Mayer /* counters are always zero initialized */ 1402*94604548SAndrea Mayer pcounters = seg6_local_alloc_pcpu_counters(GFP_KERNEL); 1403*94604548SAndrea Mayer if (!pcounters) 1404*94604548SAndrea Mayer return -ENOMEM; 1405*94604548SAndrea Mayer 1406*94604548SAndrea Mayer slwt->pcpu_counters = pcounters; 1407*94604548SAndrea Mayer 1408*94604548SAndrea Mayer return 0; 1409*94604548SAndrea Mayer } 1410*94604548SAndrea Mayer 1411*94604548SAndrea Mayer static int seg6_local_fill_nla_counters(struct sk_buff *skb, 1412*94604548SAndrea Mayer struct seg6_local_counters *counters) 1413*94604548SAndrea Mayer { 1414*94604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_PACKETS, counters->packets, 1415*94604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 1416*94604548SAndrea Mayer return -EMSGSIZE; 1417*94604548SAndrea Mayer 1418*94604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_BYTES, counters->bytes, 1419*94604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 1420*94604548SAndrea Mayer return -EMSGSIZE; 1421*94604548SAndrea Mayer 1422*94604548SAndrea Mayer if (nla_put_u64_64bit(skb, SEG6_LOCAL_CNT_ERRORS, counters->errors, 1423*94604548SAndrea Mayer SEG6_LOCAL_CNT_PAD)) 1424*94604548SAndrea Mayer return -EMSGSIZE; 1425*94604548SAndrea Mayer 1426*94604548SAndrea Mayer return 0; 1427*94604548SAndrea Mayer } 1428*94604548SAndrea Mayer 1429*94604548SAndrea Mayer static int put_nla_counters(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1430*94604548SAndrea Mayer { 1431*94604548SAndrea Mayer struct seg6_local_counters counters = { 0, 0, 0 }; 1432*94604548SAndrea Mayer struct nlattr *nest; 1433*94604548SAndrea Mayer int rc, i; 1434*94604548SAndrea Mayer 1435*94604548SAndrea Mayer nest = nla_nest_start(skb, SEG6_LOCAL_COUNTERS); 1436*94604548SAndrea Mayer if (!nest) 1437*94604548SAndrea Mayer return -EMSGSIZE; 1438*94604548SAndrea Mayer 1439*94604548SAndrea Mayer for_each_possible_cpu(i) { 1440*94604548SAndrea Mayer struct pcpu_seg6_local_counters *pcounters; 1441*94604548SAndrea Mayer u64 packets, bytes, errors; 1442*94604548SAndrea Mayer unsigned int start; 1443*94604548SAndrea Mayer 1444*94604548SAndrea Mayer pcounters = per_cpu_ptr(slwt->pcpu_counters, i); 1445*94604548SAndrea Mayer do { 1446*94604548SAndrea Mayer start = u64_stats_fetch_begin_irq(&pcounters->syncp); 1447*94604548SAndrea Mayer 1448*94604548SAndrea Mayer packets = u64_stats_read(&pcounters->packets); 1449*94604548SAndrea Mayer bytes = u64_stats_read(&pcounters->bytes); 1450*94604548SAndrea Mayer errors = u64_stats_read(&pcounters->errors); 1451*94604548SAndrea Mayer 1452*94604548SAndrea Mayer } while (u64_stats_fetch_retry_irq(&pcounters->syncp, start)); 1453*94604548SAndrea Mayer 1454*94604548SAndrea Mayer counters.packets += packets; 1455*94604548SAndrea Mayer counters.bytes += bytes; 1456*94604548SAndrea Mayer counters.errors += errors; 1457*94604548SAndrea Mayer } 1458*94604548SAndrea Mayer 1459*94604548SAndrea Mayer rc = seg6_local_fill_nla_counters(skb, &counters); 1460*94604548SAndrea Mayer if (rc < 0) { 1461*94604548SAndrea Mayer nla_nest_cancel(skb, nest); 1462*94604548SAndrea Mayer return rc; 1463*94604548SAndrea Mayer } 1464*94604548SAndrea Mayer 1465*94604548SAndrea Mayer return nla_nest_end(skb, nest); 1466*94604548SAndrea Mayer } 1467*94604548SAndrea Mayer 1468*94604548SAndrea Mayer static int cmp_nla_counters(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1469*94604548SAndrea Mayer { 1470*94604548SAndrea Mayer /* a and b are equal if both have pcpu_counters set or not */ 1471*94604548SAndrea Mayer return (!!((unsigned long)a->pcpu_counters)) ^ 1472*94604548SAndrea Mayer (!!((unsigned long)b->pcpu_counters)); 1473*94604548SAndrea Mayer } 1474*94604548SAndrea Mayer 1475*94604548SAndrea Mayer static void destroy_attr_counters(struct seg6_local_lwt *slwt) 1476*94604548SAndrea Mayer { 1477*94604548SAndrea Mayer free_percpu(slwt->pcpu_counters); 1478*94604548SAndrea Mayer } 1479*94604548SAndrea Mayer 1480d1df6fd8SDavid Lebrun struct seg6_action_param { 1481d1df6fd8SDavid Lebrun int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt); 1482d1df6fd8SDavid Lebrun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 1483d1df6fd8SDavid Lebrun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); 1484964adce5SAndrea Mayer 1485964adce5SAndrea Mayer /* optional destroy() callback useful for releasing resources which 1486964adce5SAndrea Mayer * have been previously acquired in the corresponding parse() 1487964adce5SAndrea Mayer * function. 1488964adce5SAndrea Mayer */ 1489964adce5SAndrea Mayer void (*destroy)(struct seg6_local_lwt *slwt); 1490d1df6fd8SDavid Lebrun }; 1491d1df6fd8SDavid Lebrun 1492d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { 14932d9cc60aSDavid Lebrun [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh, 14942d9cc60aSDavid Lebrun .put = put_nla_srh, 1495964adce5SAndrea Mayer .cmp = cmp_nla_srh, 1496964adce5SAndrea Mayer .destroy = destroy_attr_srh }, 1497d1df6fd8SDavid Lebrun 14982d9cc60aSDavid Lebrun [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table, 14992d9cc60aSDavid Lebrun .put = put_nla_table, 15002d9cc60aSDavid Lebrun .cmp = cmp_nla_table }, 1501d1df6fd8SDavid Lebrun 15022d9cc60aSDavid Lebrun [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4, 15032d9cc60aSDavid Lebrun .put = put_nla_nh4, 15042d9cc60aSDavid Lebrun .cmp = cmp_nla_nh4 }, 1505d1df6fd8SDavid Lebrun 15062d9cc60aSDavid Lebrun [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6, 15072d9cc60aSDavid Lebrun .put = put_nla_nh6, 15082d9cc60aSDavid Lebrun .cmp = cmp_nla_nh6 }, 1509d1df6fd8SDavid Lebrun 15102d9cc60aSDavid Lebrun [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif, 15112d9cc60aSDavid Lebrun .put = put_nla_iif, 15122d9cc60aSDavid Lebrun .cmp = cmp_nla_iif }, 1513d1df6fd8SDavid Lebrun 15142d9cc60aSDavid Lebrun [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif, 15152d9cc60aSDavid Lebrun .put = put_nla_oif, 15162d9cc60aSDavid Lebrun .cmp = cmp_nla_oif }, 1517004d4b27SMathieu Xhonneux 1518004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .parse = parse_nla_bpf, 1519004d4b27SMathieu Xhonneux .put = put_nla_bpf, 1520964adce5SAndrea Mayer .cmp = cmp_nla_bpf, 1521964adce5SAndrea Mayer .destroy = destroy_attr_bpf }, 1522004d4b27SMathieu Xhonneux 1523664d6f86SAndrea Mayer [SEG6_LOCAL_VRFTABLE] = { .parse = parse_nla_vrftable, 1524664d6f86SAndrea Mayer .put = put_nla_vrftable, 1525664d6f86SAndrea Mayer .cmp = cmp_nla_vrftable }, 1526664d6f86SAndrea Mayer 1527*94604548SAndrea Mayer [SEG6_LOCAL_COUNTERS] = { .parse = parse_nla_counters, 1528*94604548SAndrea Mayer .put = put_nla_counters, 1529*94604548SAndrea Mayer .cmp = cmp_nla_counters, 1530*94604548SAndrea Mayer .destroy = destroy_attr_counters }, 1531d1df6fd8SDavid Lebrun }; 1532d1df6fd8SDavid Lebrun 1533964adce5SAndrea Mayer /* call the destroy() callback (if available) for each set attribute in 15340a3021f1SAndrea Mayer * @parsed_attrs, starting from the first attribute up to the @max_parsed 15350a3021f1SAndrea Mayer * (excluded) attribute. 1536964adce5SAndrea Mayer */ 15370a3021f1SAndrea Mayer static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed, 15380a3021f1SAndrea Mayer struct seg6_local_lwt *slwt) 1539964adce5SAndrea Mayer { 1540964adce5SAndrea Mayer struct seg6_action_param *param; 1541964adce5SAndrea Mayer int i; 1542964adce5SAndrea Mayer 1543964adce5SAndrea Mayer /* Every required seg6local attribute is identified by an ID which is 1544964adce5SAndrea Mayer * encoded as a flag (i.e: 1 << ID) in the 'attrs' bitmask; 1545964adce5SAndrea Mayer * 15460a3021f1SAndrea Mayer * We scan the 'parsed_attrs' bitmask, starting from the first attribute 1547964adce5SAndrea Mayer * up to the @max_parsed (excluded) attribute. 1548964adce5SAndrea Mayer * For each set attribute, we retrieve the corresponding destroy() 1549964adce5SAndrea Mayer * callback. If the callback is not available, then we skip to the next 1550964adce5SAndrea Mayer * attribute; otherwise, we call the destroy() callback. 1551964adce5SAndrea Mayer */ 1552964adce5SAndrea Mayer for (i = 0; i < max_parsed; ++i) { 1553300a0fd8SAndrea Mayer if (!(parsed_attrs & SEG6_F_ATTR(i))) 1554964adce5SAndrea Mayer continue; 1555964adce5SAndrea Mayer 1556964adce5SAndrea Mayer param = &seg6_action_params[i]; 1557964adce5SAndrea Mayer 1558964adce5SAndrea Mayer if (param->destroy) 1559964adce5SAndrea Mayer param->destroy(slwt); 1560964adce5SAndrea Mayer } 1561964adce5SAndrea Mayer } 1562964adce5SAndrea Mayer 1563964adce5SAndrea Mayer /* release all the resources that may have been acquired during parsing 1564964adce5SAndrea Mayer * operations. 1565964adce5SAndrea Mayer */ 1566964adce5SAndrea Mayer static void destroy_attrs(struct seg6_local_lwt *slwt) 1567964adce5SAndrea Mayer { 15680a3021f1SAndrea Mayer unsigned long attrs = slwt->desc->attrs | slwt->parsed_optattrs; 15690a3021f1SAndrea Mayer 15700a3021f1SAndrea Mayer __destroy_attrs(attrs, SEG6_LOCAL_MAX + 1, slwt); 15710a3021f1SAndrea Mayer } 15720a3021f1SAndrea Mayer 15730a3021f1SAndrea Mayer static int parse_nla_optional_attrs(struct nlattr **attrs, 15740a3021f1SAndrea Mayer struct seg6_local_lwt *slwt) 15750a3021f1SAndrea Mayer { 15760a3021f1SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 15770a3021f1SAndrea Mayer unsigned long parsed_optattrs = 0; 15780a3021f1SAndrea Mayer struct seg6_action_param *param; 15790a3021f1SAndrea Mayer int err, i; 15800a3021f1SAndrea Mayer 15810a3021f1SAndrea Mayer for (i = 0; i < SEG6_LOCAL_MAX + 1; ++i) { 1582300a0fd8SAndrea Mayer if (!(desc->optattrs & SEG6_F_ATTR(i)) || !attrs[i]) 15830a3021f1SAndrea Mayer continue; 15840a3021f1SAndrea Mayer 15850a3021f1SAndrea Mayer /* once here, the i-th attribute is provided by the 15860a3021f1SAndrea Mayer * userspace AND it is identified optional as well. 15870a3021f1SAndrea Mayer */ 15880a3021f1SAndrea Mayer param = &seg6_action_params[i]; 15890a3021f1SAndrea Mayer 15900a3021f1SAndrea Mayer err = param->parse(attrs, slwt); 15910a3021f1SAndrea Mayer if (err < 0) 15920a3021f1SAndrea Mayer goto parse_optattrs_err; 15930a3021f1SAndrea Mayer 15940a3021f1SAndrea Mayer /* current attribute has been correctly parsed */ 1595300a0fd8SAndrea Mayer parsed_optattrs |= SEG6_F_ATTR(i); 15960a3021f1SAndrea Mayer } 15970a3021f1SAndrea Mayer 15980a3021f1SAndrea Mayer /* store in the tunnel state all the optional attributed successfully 15990a3021f1SAndrea Mayer * parsed. 16000a3021f1SAndrea Mayer */ 16010a3021f1SAndrea Mayer slwt->parsed_optattrs = parsed_optattrs; 16020a3021f1SAndrea Mayer 16030a3021f1SAndrea Mayer return 0; 16040a3021f1SAndrea Mayer 16050a3021f1SAndrea Mayer parse_optattrs_err: 16060a3021f1SAndrea Mayer __destroy_attrs(parsed_optattrs, i, slwt); 16070a3021f1SAndrea Mayer 16080a3021f1SAndrea Mayer return err; 1609964adce5SAndrea Mayer } 1610964adce5SAndrea Mayer 1611cfdf64a0SAndrea Mayer /* call the custom constructor of the behavior during its initialization phase 1612cfdf64a0SAndrea Mayer * and after that all its attributes have been parsed successfully. 1613cfdf64a0SAndrea Mayer */ 1614cfdf64a0SAndrea Mayer static int 1615cfdf64a0SAndrea Mayer seg6_local_lwtunnel_build_state(struct seg6_local_lwt *slwt, const void *cfg, 1616cfdf64a0SAndrea Mayer struct netlink_ext_ack *extack) 1617cfdf64a0SAndrea Mayer { 1618cfdf64a0SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 1619cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops *ops; 1620cfdf64a0SAndrea Mayer 1621cfdf64a0SAndrea Mayer ops = &desc->slwt_ops; 1622cfdf64a0SAndrea Mayer if (!ops->build_state) 1623cfdf64a0SAndrea Mayer return 0; 1624cfdf64a0SAndrea Mayer 1625cfdf64a0SAndrea Mayer return ops->build_state(slwt, cfg, extack); 1626cfdf64a0SAndrea Mayer } 1627cfdf64a0SAndrea Mayer 1628cfdf64a0SAndrea Mayer /* call the custom destructor of the behavior which is invoked before the 1629cfdf64a0SAndrea Mayer * tunnel is going to be destroyed. 1630cfdf64a0SAndrea Mayer */ 1631cfdf64a0SAndrea Mayer static void seg6_local_lwtunnel_destroy_state(struct seg6_local_lwt *slwt) 1632cfdf64a0SAndrea Mayer { 1633cfdf64a0SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 1634cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops *ops; 1635cfdf64a0SAndrea Mayer 1636cfdf64a0SAndrea Mayer ops = &desc->slwt_ops; 1637cfdf64a0SAndrea Mayer if (!ops->destroy_state) 1638cfdf64a0SAndrea Mayer return; 1639cfdf64a0SAndrea Mayer 1640cfdf64a0SAndrea Mayer ops->destroy_state(slwt); 1641cfdf64a0SAndrea Mayer } 1642cfdf64a0SAndrea Mayer 1643d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) 1644d1df6fd8SDavid Lebrun { 1645d1df6fd8SDavid Lebrun struct seg6_action_param *param; 1646d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 16470a3021f1SAndrea Mayer unsigned long invalid_attrs; 1648d1df6fd8SDavid Lebrun int i, err; 1649d1df6fd8SDavid Lebrun 1650d1df6fd8SDavid Lebrun desc = __get_action_desc(slwt->action); 1651d1df6fd8SDavid Lebrun if (!desc) 1652d1df6fd8SDavid Lebrun return -EINVAL; 1653d1df6fd8SDavid Lebrun 1654d1df6fd8SDavid Lebrun if (!desc->input) 1655d1df6fd8SDavid Lebrun return -EOPNOTSUPP; 1656d1df6fd8SDavid Lebrun 1657d1df6fd8SDavid Lebrun slwt->desc = desc; 1658d1df6fd8SDavid Lebrun slwt->headroom += desc->static_headroom; 1659d1df6fd8SDavid Lebrun 16600a3021f1SAndrea Mayer /* Forcing the desc->optattrs *set* and the desc->attrs *set* to be 16610a3021f1SAndrea Mayer * disjoined, this allow us to release acquired resources by optional 16620a3021f1SAndrea Mayer * attributes and by required attributes independently from each other 16630d770360SAndrea Mayer * without any interference. 16640a3021f1SAndrea Mayer * In other terms, we are sure that we do not release some the acquired 16650a3021f1SAndrea Mayer * resources twice. 16660a3021f1SAndrea Mayer * 16670a3021f1SAndrea Mayer * Note that if an attribute is configured both as required and as 16680a3021f1SAndrea Mayer * optional, it means that the user has messed something up in the 16690a3021f1SAndrea Mayer * seg6_action_table. Therefore, this check is required for SRv6 16700a3021f1SAndrea Mayer * behaviors to work properly. 16710a3021f1SAndrea Mayer */ 16720a3021f1SAndrea Mayer invalid_attrs = desc->attrs & desc->optattrs; 16730a3021f1SAndrea Mayer if (invalid_attrs) { 16740a3021f1SAndrea Mayer WARN_ONCE(1, 16750a3021f1SAndrea Mayer "An attribute cannot be both required AND optional"); 16760a3021f1SAndrea Mayer return -EINVAL; 16770a3021f1SAndrea Mayer } 16780a3021f1SAndrea Mayer 16790a3021f1SAndrea Mayer /* parse the required attributes */ 1680d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1681300a0fd8SAndrea Mayer if (desc->attrs & SEG6_F_ATTR(i)) { 1682d1df6fd8SDavid Lebrun if (!attrs[i]) 1683d1df6fd8SDavid Lebrun return -EINVAL; 1684d1df6fd8SDavid Lebrun 1685d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1686d1df6fd8SDavid Lebrun 1687d1df6fd8SDavid Lebrun err = param->parse(attrs, slwt); 1688d1df6fd8SDavid Lebrun if (err < 0) 16890a3021f1SAndrea Mayer goto parse_attrs_err; 1690d1df6fd8SDavid Lebrun } 1691d1df6fd8SDavid Lebrun } 1692d1df6fd8SDavid Lebrun 16930a3021f1SAndrea Mayer /* parse the optional attributes, if any */ 16940a3021f1SAndrea Mayer err = parse_nla_optional_attrs(attrs, slwt); 16950a3021f1SAndrea Mayer if (err < 0) 16960a3021f1SAndrea Mayer goto parse_attrs_err; 16970a3021f1SAndrea Mayer 1698d1df6fd8SDavid Lebrun return 0; 1699964adce5SAndrea Mayer 17000a3021f1SAndrea Mayer parse_attrs_err: 1701964adce5SAndrea Mayer /* release any resource that may have been acquired during the i-1 1702964adce5SAndrea Mayer * parse() operations. 1703964adce5SAndrea Mayer */ 17040a3021f1SAndrea Mayer __destroy_attrs(desc->attrs, i, slwt); 1705964adce5SAndrea Mayer 1706964adce5SAndrea Mayer return err; 1707d1df6fd8SDavid Lebrun } 1708d1df6fd8SDavid Lebrun 1709faee6769SAlexander Aring static int seg6_local_build_state(struct net *net, struct nlattr *nla, 1710faee6769SAlexander Aring unsigned int family, const void *cfg, 1711faee6769SAlexander Aring struct lwtunnel_state **ts, 1712d1df6fd8SDavid Lebrun struct netlink_ext_ack *extack) 1713d1df6fd8SDavid Lebrun { 1714d1df6fd8SDavid Lebrun struct nlattr *tb[SEG6_LOCAL_MAX + 1]; 1715d1df6fd8SDavid Lebrun struct lwtunnel_state *newts; 1716d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 1717d1df6fd8SDavid Lebrun int err; 1718d1df6fd8SDavid Lebrun 17196285217fSDavid Lebrun if (family != AF_INET6) 17206285217fSDavid Lebrun return -EINVAL; 17216285217fSDavid Lebrun 17228cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla, 17238cb08174SJohannes Berg seg6_local_policy, extack); 1724d1df6fd8SDavid Lebrun 1725d1df6fd8SDavid Lebrun if (err < 0) 1726d1df6fd8SDavid Lebrun return err; 1727d1df6fd8SDavid Lebrun 1728d1df6fd8SDavid Lebrun if (!tb[SEG6_LOCAL_ACTION]) 1729d1df6fd8SDavid Lebrun return -EINVAL; 1730d1df6fd8SDavid Lebrun 1731d1df6fd8SDavid Lebrun newts = lwtunnel_state_alloc(sizeof(*slwt)); 1732d1df6fd8SDavid Lebrun if (!newts) 1733d1df6fd8SDavid Lebrun return -ENOMEM; 1734d1df6fd8SDavid Lebrun 1735d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(newts); 1736d1df6fd8SDavid Lebrun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); 1737d1df6fd8SDavid Lebrun 1738d1df6fd8SDavid Lebrun err = parse_nla_action(tb, slwt); 1739d1df6fd8SDavid Lebrun if (err < 0) 1740d1df6fd8SDavid Lebrun goto out_free; 1741d1df6fd8SDavid Lebrun 1742cfdf64a0SAndrea Mayer err = seg6_local_lwtunnel_build_state(slwt, cfg, extack); 1743cfdf64a0SAndrea Mayer if (err < 0) 1744cfdf64a0SAndrea Mayer goto out_destroy_attrs; 1745cfdf64a0SAndrea Mayer 1746d1df6fd8SDavid Lebrun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; 1747d1df6fd8SDavid Lebrun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; 1748d1df6fd8SDavid Lebrun newts->headroom = slwt->headroom; 1749d1df6fd8SDavid Lebrun 1750d1df6fd8SDavid Lebrun *ts = newts; 1751d1df6fd8SDavid Lebrun 1752d1df6fd8SDavid Lebrun return 0; 1753d1df6fd8SDavid Lebrun 1754cfdf64a0SAndrea Mayer out_destroy_attrs: 1755cfdf64a0SAndrea Mayer destroy_attrs(slwt); 1756d1df6fd8SDavid Lebrun out_free: 1757d1df6fd8SDavid Lebrun kfree(newts); 1758d1df6fd8SDavid Lebrun return err; 1759d1df6fd8SDavid Lebrun } 1760d1df6fd8SDavid Lebrun 1761d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt) 1762d1df6fd8SDavid Lebrun { 1763d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1764d1df6fd8SDavid Lebrun 1765cfdf64a0SAndrea Mayer seg6_local_lwtunnel_destroy_state(slwt); 1766cfdf64a0SAndrea Mayer 1767964adce5SAndrea Mayer destroy_attrs(slwt); 1768004d4b27SMathieu Xhonneux 1769004d4b27SMathieu Xhonneux return; 1770d1df6fd8SDavid Lebrun } 1771d1df6fd8SDavid Lebrun 1772d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb, 1773d1df6fd8SDavid Lebrun struct lwtunnel_state *lwt) 1774d1df6fd8SDavid Lebrun { 1775d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1776d1df6fd8SDavid Lebrun struct seg6_action_param *param; 17770a3021f1SAndrea Mayer unsigned long attrs; 1778d1df6fd8SDavid Lebrun int i, err; 1779d1df6fd8SDavid Lebrun 1780d1df6fd8SDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) 1781d1df6fd8SDavid Lebrun return -EMSGSIZE; 1782d1df6fd8SDavid Lebrun 17830a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 17840a3021f1SAndrea Mayer 1785d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1786300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(i)) { 1787d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1788d1df6fd8SDavid Lebrun err = param->put(skb, slwt); 1789d1df6fd8SDavid Lebrun if (err < 0) 1790d1df6fd8SDavid Lebrun return err; 1791d1df6fd8SDavid Lebrun } 1792d1df6fd8SDavid Lebrun } 1793d1df6fd8SDavid Lebrun 1794d1df6fd8SDavid Lebrun return 0; 1795d1df6fd8SDavid Lebrun } 1796d1df6fd8SDavid Lebrun 1797d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) 1798d1df6fd8SDavid Lebrun { 1799d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1800d1df6fd8SDavid Lebrun unsigned long attrs; 1801d1df6fd8SDavid Lebrun int nlsize; 1802d1df6fd8SDavid Lebrun 1803d1df6fd8SDavid Lebrun nlsize = nla_total_size(4); /* action */ 1804d1df6fd8SDavid Lebrun 18050a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 1806d1df6fd8SDavid Lebrun 1807300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_SRH)) 1808d1df6fd8SDavid Lebrun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); 1809d1df6fd8SDavid Lebrun 1810300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE)) 1811d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1812d1df6fd8SDavid Lebrun 1813300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH4)) 1814d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1815d1df6fd8SDavid Lebrun 1816300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH6)) 1817d1df6fd8SDavid Lebrun nlsize += nla_total_size(16); 1818d1df6fd8SDavid Lebrun 1819300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_IIF)) 1820d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1821d1df6fd8SDavid Lebrun 1822300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_OIF)) 1823d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1824d1df6fd8SDavid Lebrun 1825300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_BPF)) 1826004d4b27SMathieu Xhonneux nlsize += nla_total_size(sizeof(struct nlattr)) + 1827004d4b27SMathieu Xhonneux nla_total_size(MAX_PROG_NAME) + 1828004d4b27SMathieu Xhonneux nla_total_size(4); 1829004d4b27SMathieu Xhonneux 1830300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE)) 1831664d6f86SAndrea Mayer nlsize += nla_total_size(4); 1832664d6f86SAndrea Mayer 1833*94604548SAndrea Mayer if (attrs & SEG6_F_LOCAL_COUNTERS) 1834*94604548SAndrea Mayer nlsize += nla_total_size(0) + /* nest SEG6_LOCAL_COUNTERS */ 1835*94604548SAndrea Mayer /* SEG6_LOCAL_CNT_PACKETS */ 1836*94604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)) + 1837*94604548SAndrea Mayer /* SEG6_LOCAL_CNT_BYTES */ 1838*94604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)) + 1839*94604548SAndrea Mayer /* SEG6_LOCAL_CNT_ERRORS */ 1840*94604548SAndrea Mayer nla_total_size_64bit(sizeof(__u64)); 1841*94604548SAndrea Mayer 1842d1df6fd8SDavid Lebrun return nlsize; 1843d1df6fd8SDavid Lebrun } 1844d1df6fd8SDavid Lebrun 1845d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a, 1846d1df6fd8SDavid Lebrun struct lwtunnel_state *b) 1847d1df6fd8SDavid Lebrun { 1848d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt_a, *slwt_b; 1849d1df6fd8SDavid Lebrun struct seg6_action_param *param; 18500a3021f1SAndrea Mayer unsigned long attrs_a, attrs_b; 1851d1df6fd8SDavid Lebrun int i; 1852d1df6fd8SDavid Lebrun 1853d1df6fd8SDavid Lebrun slwt_a = seg6_local_lwtunnel(a); 1854d1df6fd8SDavid Lebrun slwt_b = seg6_local_lwtunnel(b); 1855d1df6fd8SDavid Lebrun 1856d1df6fd8SDavid Lebrun if (slwt_a->action != slwt_b->action) 1857d1df6fd8SDavid Lebrun return 1; 1858d1df6fd8SDavid Lebrun 18590a3021f1SAndrea Mayer attrs_a = slwt_a->desc->attrs | slwt_a->parsed_optattrs; 18600a3021f1SAndrea Mayer attrs_b = slwt_b->desc->attrs | slwt_b->parsed_optattrs; 18610a3021f1SAndrea Mayer 18620a3021f1SAndrea Mayer if (attrs_a != attrs_b) 1863d1df6fd8SDavid Lebrun return 1; 1864d1df6fd8SDavid Lebrun 1865d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1866300a0fd8SAndrea Mayer if (attrs_a & SEG6_F_ATTR(i)) { 1867d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1868d1df6fd8SDavid Lebrun if (param->cmp(slwt_a, slwt_b)) 1869d1df6fd8SDavid Lebrun return 1; 1870d1df6fd8SDavid Lebrun } 1871d1df6fd8SDavid Lebrun } 1872d1df6fd8SDavid Lebrun 1873d1df6fd8SDavid Lebrun return 0; 1874d1df6fd8SDavid Lebrun } 1875d1df6fd8SDavid Lebrun 1876d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = { 1877d1df6fd8SDavid Lebrun .build_state = seg6_local_build_state, 1878d1df6fd8SDavid Lebrun .destroy_state = seg6_local_destroy_state, 1879d1df6fd8SDavid Lebrun .input = seg6_local_input, 1880d1df6fd8SDavid Lebrun .fill_encap = seg6_local_fill_encap, 1881d1df6fd8SDavid Lebrun .get_encap_size = seg6_local_get_encap_size, 1882d1df6fd8SDavid Lebrun .cmp_encap = seg6_local_cmp_encap, 1883d1df6fd8SDavid Lebrun .owner = THIS_MODULE, 1884d1df6fd8SDavid Lebrun }; 1885d1df6fd8SDavid Lebrun 1886d1df6fd8SDavid Lebrun int __init seg6_local_init(void) 1887d1df6fd8SDavid Lebrun { 1888300a0fd8SAndrea Mayer /* If the max total number of defined attributes is reached, then your 1889300a0fd8SAndrea Mayer * kernel build stops here. 1890300a0fd8SAndrea Mayer * 1891300a0fd8SAndrea Mayer * This check is required to avoid arithmetic overflows when processing 1892300a0fd8SAndrea Mayer * behavior attributes and the maximum number of defined attributes 1893300a0fd8SAndrea Mayer * exceeds the allowed value. 1894300a0fd8SAndrea Mayer */ 1895300a0fd8SAndrea Mayer BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsigned long)); 1896300a0fd8SAndrea Mayer 1897d1df6fd8SDavid Lebrun return lwtunnel_encap_add_ops(&seg6_local_ops, 1898d1df6fd8SDavid Lebrun LWTUNNEL_ENCAP_SEG6_LOCAL); 1899d1df6fd8SDavid Lebrun } 1900d1df6fd8SDavid Lebrun 1901d1df6fd8SDavid Lebrun void seg6_local_exit(void) 1902d1df6fd8SDavid Lebrun { 1903d1df6fd8SDavid Lebrun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); 1904d1df6fd8SDavid Lebrun } 1905