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 96d1df6fd8SDavid Lebrun struct seg6_local_lwt { 97d1df6fd8SDavid Lebrun int action; 98d1df6fd8SDavid Lebrun struct ipv6_sr_hdr *srh; 99d1df6fd8SDavid Lebrun int table; 100d1df6fd8SDavid Lebrun struct in_addr nh4; 101d1df6fd8SDavid Lebrun struct in6_addr nh6; 102d1df6fd8SDavid Lebrun int iif; 103d1df6fd8SDavid Lebrun int oif; 104004d4b27SMathieu Xhonneux struct bpf_lwt_prog bpf; 105664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 106664d6f86SAndrea Mayer struct seg6_end_dt_info dt_info; 107664d6f86SAndrea Mayer #endif 108d1df6fd8SDavid Lebrun 109d1df6fd8SDavid Lebrun int headroom; 110d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1110a3021f1SAndrea Mayer /* unlike the required attrs, we have to track the optional attributes 1120a3021f1SAndrea Mayer * that have been effectively parsed. 1130a3021f1SAndrea Mayer */ 1140a3021f1SAndrea Mayer unsigned long parsed_optattrs; 115d1df6fd8SDavid Lebrun }; 116d1df6fd8SDavid Lebrun 117d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) 118d1df6fd8SDavid Lebrun { 119d1df6fd8SDavid Lebrun return (struct seg6_local_lwt *)lwt->data; 120d1df6fd8SDavid Lebrun } 121d1df6fd8SDavid Lebrun 122fbbc5bc2SJulien Massonneau static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb, int flags) 123140f04c3SDavid Lebrun { 124140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 1255829d70bSAhmed Abdelsalam int len, srhoff = 0; 126140f04c3SDavid Lebrun 127fbbc5bc2SJulien Massonneau if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, &flags) < 0) 128140f04c3SDavid Lebrun return NULL; 129140f04c3SDavid Lebrun 1305829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + sizeof(*srh))) 1315829d70bSAhmed Abdelsalam return NULL; 1325829d70bSAhmed Abdelsalam 1335829d70bSAhmed Abdelsalam srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 1345829d70bSAhmed Abdelsalam 135140f04c3SDavid Lebrun len = (srh->hdrlen + 1) << 3; 136140f04c3SDavid Lebrun 1375829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + len)) 138140f04c3SDavid Lebrun return NULL; 139140f04c3SDavid Lebrun 1407f91ed8cSAndrea Mayer /* note that pskb_may_pull may change pointers in header; 1417f91ed8cSAndrea Mayer * for this reason it is necessary to reload them when needed. 1427f91ed8cSAndrea Mayer */ 1437f91ed8cSAndrea Mayer srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 1447f91ed8cSAndrea Mayer 145bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, true)) 146140f04c3SDavid Lebrun return NULL; 147140f04c3SDavid Lebrun 148140f04c3SDavid Lebrun return srh; 149140f04c3SDavid Lebrun } 150140f04c3SDavid Lebrun 151140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) 152140f04c3SDavid Lebrun { 153140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 154140f04c3SDavid Lebrun 155fbbc5bc2SJulien Massonneau srh = get_srh(skb, IP6_FH_F_SKIP_RH); 156140f04c3SDavid Lebrun if (!srh) 157140f04c3SDavid Lebrun return NULL; 158140f04c3SDavid Lebrun 159140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 160140f04c3SDavid Lebrun if (!seg6_hmac_validate_skb(skb)) 161140f04c3SDavid Lebrun return NULL; 162140f04c3SDavid Lebrun #endif 163140f04c3SDavid Lebrun 164140f04c3SDavid Lebrun return srh; 165140f04c3SDavid Lebrun } 166140f04c3SDavid Lebrun 167d7a669ddSDavid Lebrun static bool decap_and_validate(struct sk_buff *skb, int proto) 168d7a669ddSDavid Lebrun { 169d7a669ddSDavid Lebrun struct ipv6_sr_hdr *srh; 170d7a669ddSDavid Lebrun unsigned int off = 0; 171d7a669ddSDavid Lebrun 172fbbc5bc2SJulien Massonneau srh = get_srh(skb, 0); 173d7a669ddSDavid Lebrun if (srh && srh->segments_left > 0) 174d7a669ddSDavid Lebrun return false; 175d7a669ddSDavid Lebrun 176d7a669ddSDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 177d7a669ddSDavid Lebrun if (srh && !seg6_hmac_validate_skb(skb)) 178d7a669ddSDavid Lebrun return false; 179d7a669ddSDavid Lebrun #endif 180d7a669ddSDavid Lebrun 181d7a669ddSDavid Lebrun if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0) 182d7a669ddSDavid Lebrun return false; 183d7a669ddSDavid Lebrun 184d7a669ddSDavid Lebrun if (!pskb_pull(skb, off)) 185d7a669ddSDavid Lebrun return false; 186d7a669ddSDavid Lebrun 187d7a669ddSDavid Lebrun skb_postpull_rcsum(skb, skb_network_header(skb), off); 188d7a669ddSDavid Lebrun 189d7a669ddSDavid Lebrun skb_reset_network_header(skb); 190d7a669ddSDavid Lebrun skb_reset_transport_header(skb); 19162ebaeaeSYuki Taguchi if (iptunnel_pull_offloads(skb)) 19262ebaeaeSYuki Taguchi return false; 193d7a669ddSDavid Lebrun 194d7a669ddSDavid Lebrun return true; 195d7a669ddSDavid Lebrun } 196d7a669ddSDavid Lebrun 197d7a669ddSDavid Lebrun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr) 198d7a669ddSDavid Lebrun { 199d7a669ddSDavid Lebrun struct in6_addr *addr; 200d7a669ddSDavid Lebrun 201d7a669ddSDavid Lebrun srh->segments_left--; 202d7a669ddSDavid Lebrun addr = srh->segments + srh->segments_left; 203d7a669ddSDavid Lebrun *daddr = *addr; 204d7a669ddSDavid Lebrun } 205d7a669ddSDavid Lebrun 206fd1fef0cSAndrea Mayer static int 207fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr, 208fd1fef0cSAndrea Mayer u32 tbl_id, bool local_delivery) 209d7a669ddSDavid Lebrun { 210d7a669ddSDavid Lebrun struct net *net = dev_net(skb->dev); 211d7a669ddSDavid Lebrun struct ipv6hdr *hdr = ipv6_hdr(skb); 212d7a669ddSDavid Lebrun int flags = RT6_LOOKUP_F_HAS_SADDR; 213d7a669ddSDavid Lebrun struct dst_entry *dst = NULL; 214d7a669ddSDavid Lebrun struct rt6_info *rt; 215d7a669ddSDavid Lebrun struct flowi6 fl6; 216fd1fef0cSAndrea Mayer int dev_flags = 0; 217d7a669ddSDavid Lebrun 218d7a669ddSDavid Lebrun fl6.flowi6_iif = skb->dev->ifindex; 219d7a669ddSDavid Lebrun fl6.daddr = nhaddr ? *nhaddr : hdr->daddr; 220d7a669ddSDavid Lebrun fl6.saddr = hdr->saddr; 221d7a669ddSDavid Lebrun fl6.flowlabel = ip6_flowinfo(hdr); 222d7a669ddSDavid Lebrun fl6.flowi6_mark = skb->mark; 223d7a669ddSDavid Lebrun fl6.flowi6_proto = hdr->nexthdr; 224d7a669ddSDavid Lebrun 225d7a669ddSDavid Lebrun if (nhaddr) 226d7a669ddSDavid Lebrun fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH; 227d7a669ddSDavid Lebrun 228d7a669ddSDavid Lebrun if (!tbl_id) { 229b75cc8f9SDavid Ahern dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags); 230d7a669ddSDavid Lebrun } else { 231d7a669ddSDavid Lebrun struct fib6_table *table; 232d7a669ddSDavid Lebrun 233d7a669ddSDavid Lebrun table = fib6_get_table(net, tbl_id); 234d7a669ddSDavid Lebrun if (!table) 235d7a669ddSDavid Lebrun goto out; 236d7a669ddSDavid Lebrun 237b75cc8f9SDavid Ahern rt = ip6_pol_route(net, table, 0, &fl6, skb, flags); 238d7a669ddSDavid Lebrun dst = &rt->dst; 239d7a669ddSDavid Lebrun } 240d7a669ddSDavid Lebrun 241fd1fef0cSAndrea Mayer /* we want to discard traffic destined for local packet processing, 242fd1fef0cSAndrea Mayer * if @local_delivery is set to false. 243fd1fef0cSAndrea Mayer */ 244fd1fef0cSAndrea Mayer if (!local_delivery) 245fd1fef0cSAndrea Mayer dev_flags |= IFF_LOOPBACK; 246fd1fef0cSAndrea Mayer 247fd1fef0cSAndrea Mayer if (dst && (dst->dev->flags & dev_flags) && !dst->error) { 248d7a669ddSDavid Lebrun dst_release(dst); 249d7a669ddSDavid Lebrun dst = NULL; 250d7a669ddSDavid Lebrun } 251d7a669ddSDavid Lebrun 252d7a669ddSDavid Lebrun out: 253d7a669ddSDavid Lebrun if (!dst) { 254d7a669ddSDavid Lebrun rt = net->ipv6.ip6_blk_hole_entry; 255d7a669ddSDavid Lebrun dst = &rt->dst; 256d7a669ddSDavid Lebrun dst_hold(dst); 257d7a669ddSDavid Lebrun } 258d7a669ddSDavid Lebrun 259d7a669ddSDavid Lebrun skb_dst_drop(skb); 260d7a669ddSDavid Lebrun skb_dst_set(skb, dst); 2611c1e761eSMathieu Xhonneux return dst->error; 262d7a669ddSDavid Lebrun } 263d7a669ddSDavid Lebrun 264fd1fef0cSAndrea Mayer int seg6_lookup_nexthop(struct sk_buff *skb, 265fd1fef0cSAndrea Mayer struct in6_addr *nhaddr, u32 tbl_id) 266fd1fef0cSAndrea Mayer { 267fd1fef0cSAndrea Mayer return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false); 268fd1fef0cSAndrea Mayer } 269fd1fef0cSAndrea Mayer 270140f04c3SDavid Lebrun /* regular endpoint function */ 271140f04c3SDavid Lebrun static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 272140f04c3SDavid Lebrun { 273140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 274140f04c3SDavid Lebrun 275140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 276140f04c3SDavid Lebrun if (!srh) 277140f04c3SDavid Lebrun goto drop; 278140f04c3SDavid Lebrun 279d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 280140f04c3SDavid Lebrun 2811c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 282140f04c3SDavid Lebrun 283140f04c3SDavid Lebrun return dst_input(skb); 284140f04c3SDavid Lebrun 285140f04c3SDavid Lebrun drop: 286140f04c3SDavid Lebrun kfree_skb(skb); 287140f04c3SDavid Lebrun return -EINVAL; 288140f04c3SDavid Lebrun } 289140f04c3SDavid Lebrun 290140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */ 291140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) 292140f04c3SDavid Lebrun { 293140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 294140f04c3SDavid Lebrun 295140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 296140f04c3SDavid Lebrun if (!srh) 297140f04c3SDavid Lebrun goto drop; 298140f04c3SDavid Lebrun 299d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 300140f04c3SDavid Lebrun 3011c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, &slwt->nh6, 0); 302140f04c3SDavid Lebrun 303140f04c3SDavid Lebrun return dst_input(skb); 304140f04c3SDavid Lebrun 305140f04c3SDavid Lebrun drop: 306140f04c3SDavid Lebrun kfree_skb(skb); 307140f04c3SDavid Lebrun return -EINVAL; 308140f04c3SDavid Lebrun } 309140f04c3SDavid Lebrun 310891ef8ddSDavid Lebrun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) 311891ef8ddSDavid Lebrun { 312891ef8ddSDavid Lebrun struct ipv6_sr_hdr *srh; 313891ef8ddSDavid Lebrun 314891ef8ddSDavid Lebrun srh = get_and_validate_srh(skb); 315891ef8ddSDavid Lebrun if (!srh) 316891ef8ddSDavid Lebrun goto drop; 317891ef8ddSDavid Lebrun 318891ef8ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 319891ef8ddSDavid Lebrun 3201c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, slwt->table); 321891ef8ddSDavid Lebrun 322891ef8ddSDavid Lebrun return dst_input(skb); 323891ef8ddSDavid Lebrun 324891ef8ddSDavid Lebrun drop: 325891ef8ddSDavid Lebrun kfree_skb(skb); 326891ef8ddSDavid Lebrun return -EINVAL; 327891ef8ddSDavid Lebrun } 328891ef8ddSDavid Lebrun 329891ef8ddSDavid Lebrun /* decapsulate and forward inner L2 frame on specified interface */ 330891ef8ddSDavid Lebrun static int input_action_end_dx2(struct sk_buff *skb, 331891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 332891ef8ddSDavid Lebrun { 333891ef8ddSDavid Lebrun struct net *net = dev_net(skb->dev); 334891ef8ddSDavid Lebrun struct net_device *odev; 335891ef8ddSDavid Lebrun struct ethhdr *eth; 336891ef8ddSDavid Lebrun 33726776253SPaolo Lungaroni if (!decap_and_validate(skb, IPPROTO_ETHERNET)) 338891ef8ddSDavid Lebrun goto drop; 339891ef8ddSDavid Lebrun 340891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, ETH_HLEN)) 341891ef8ddSDavid Lebrun goto drop; 342891ef8ddSDavid Lebrun 343891ef8ddSDavid Lebrun skb_reset_mac_header(skb); 344891ef8ddSDavid Lebrun eth = (struct ethhdr *)skb->data; 345891ef8ddSDavid Lebrun 346891ef8ddSDavid Lebrun /* To determine the frame's protocol, we assume it is 802.3. This avoids 347891ef8ddSDavid Lebrun * a call to eth_type_trans(), which is not really relevant for our 348891ef8ddSDavid Lebrun * use case. 349891ef8ddSDavid Lebrun */ 350891ef8ddSDavid Lebrun if (!eth_proto_is_802_3(eth->h_proto)) 351891ef8ddSDavid Lebrun goto drop; 352891ef8ddSDavid Lebrun 353891ef8ddSDavid Lebrun odev = dev_get_by_index_rcu(net, slwt->oif); 354891ef8ddSDavid Lebrun if (!odev) 355891ef8ddSDavid Lebrun goto drop; 356891ef8ddSDavid Lebrun 357891ef8ddSDavid Lebrun /* As we accept Ethernet frames, make sure the egress device is of 358891ef8ddSDavid Lebrun * the correct type. 359891ef8ddSDavid Lebrun */ 360891ef8ddSDavid Lebrun if (odev->type != ARPHRD_ETHER) 361891ef8ddSDavid Lebrun goto drop; 362891ef8ddSDavid Lebrun 363891ef8ddSDavid Lebrun if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev)) 364891ef8ddSDavid Lebrun goto drop; 365891ef8ddSDavid Lebrun 366891ef8ddSDavid Lebrun skb_orphan(skb); 367891ef8ddSDavid Lebrun 368891ef8ddSDavid Lebrun if (skb_warn_if_lro(skb)) 369891ef8ddSDavid Lebrun goto drop; 370891ef8ddSDavid Lebrun 371891ef8ddSDavid Lebrun skb_forward_csum(skb); 372891ef8ddSDavid Lebrun 373891ef8ddSDavid Lebrun if (skb->len - ETH_HLEN > odev->mtu) 374891ef8ddSDavid Lebrun goto drop; 375891ef8ddSDavid Lebrun 376891ef8ddSDavid Lebrun skb->dev = odev; 377891ef8ddSDavid Lebrun skb->protocol = eth->h_proto; 378891ef8ddSDavid Lebrun 379891ef8ddSDavid Lebrun return dev_queue_xmit(skb); 380891ef8ddSDavid Lebrun 381891ef8ddSDavid Lebrun drop: 382891ef8ddSDavid Lebrun kfree_skb(skb); 383891ef8ddSDavid Lebrun return -EINVAL; 384891ef8ddSDavid Lebrun } 385891ef8ddSDavid Lebrun 386140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */ 387140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb, 388140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 389140f04c3SDavid Lebrun { 390d7a669ddSDavid Lebrun struct in6_addr *nhaddr = NULL; 391140f04c3SDavid Lebrun 392140f04c3SDavid Lebrun /* this function accepts IPv6 encapsulated packets, with either 393140f04c3SDavid Lebrun * an SRH with SL=0, or no SRH. 394140f04c3SDavid Lebrun */ 395140f04c3SDavid Lebrun 396d7a669ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 397140f04c3SDavid Lebrun goto drop; 398140f04c3SDavid Lebrun 399d7a669ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 400140f04c3SDavid Lebrun goto drop; 401140f04c3SDavid Lebrun 402140f04c3SDavid Lebrun /* The inner packet is not associated to any local interface, 403140f04c3SDavid Lebrun * so we do not call netif_rx(). 404140f04c3SDavid Lebrun * 405140f04c3SDavid Lebrun * If slwt->nh6 is set to ::, then lookup the nexthop for the 406140f04c3SDavid Lebrun * inner packet's DA. Otherwise, use the specified nexthop. 407140f04c3SDavid Lebrun */ 408140f04c3SDavid Lebrun 409d7a669ddSDavid Lebrun if (!ipv6_addr_any(&slwt->nh6)) 410d7a669ddSDavid Lebrun nhaddr = &slwt->nh6; 411140f04c3SDavid Lebrun 412c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 413c71644d0SAndrea Mayer 4141c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, nhaddr, 0); 415140f04c3SDavid Lebrun 416140f04c3SDavid Lebrun return dst_input(skb); 417140f04c3SDavid Lebrun drop: 418140f04c3SDavid Lebrun kfree_skb(skb); 419140f04c3SDavid Lebrun return -EINVAL; 420140f04c3SDavid Lebrun } 421140f04c3SDavid Lebrun 422891ef8ddSDavid Lebrun static int input_action_end_dx4(struct sk_buff *skb, 423891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 424891ef8ddSDavid Lebrun { 425891ef8ddSDavid Lebrun struct iphdr *iph; 426891ef8ddSDavid Lebrun __be32 nhaddr; 427891ef8ddSDavid Lebrun int err; 428891ef8ddSDavid Lebrun 429891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPIP)) 430891ef8ddSDavid Lebrun goto drop; 431891ef8ddSDavid Lebrun 432891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct iphdr))) 433891ef8ddSDavid Lebrun goto drop; 434891ef8ddSDavid Lebrun 435891ef8ddSDavid Lebrun skb->protocol = htons(ETH_P_IP); 436891ef8ddSDavid Lebrun 437891ef8ddSDavid Lebrun iph = ip_hdr(skb); 438891ef8ddSDavid Lebrun 439891ef8ddSDavid Lebrun nhaddr = slwt->nh4.s_addr ?: iph->daddr; 440891ef8ddSDavid Lebrun 441891ef8ddSDavid Lebrun skb_dst_drop(skb); 442891ef8ddSDavid Lebrun 443c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct iphdr)); 444c71644d0SAndrea Mayer 445891ef8ddSDavid Lebrun err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev); 446891ef8ddSDavid Lebrun if (err) 447891ef8ddSDavid Lebrun goto drop; 448891ef8ddSDavid Lebrun 449891ef8ddSDavid Lebrun return dst_input(skb); 450891ef8ddSDavid Lebrun 451891ef8ddSDavid Lebrun drop: 452891ef8ddSDavid Lebrun kfree_skb(skb); 453891ef8ddSDavid Lebrun return -EINVAL; 454891ef8ddSDavid Lebrun } 455891ef8ddSDavid Lebrun 456664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 457664d6f86SAndrea Mayer static struct net *fib6_config_get_net(const struct fib6_config *fib6_cfg) 458664d6f86SAndrea Mayer { 459664d6f86SAndrea Mayer const struct nl_info *nli = &fib6_cfg->fc_nlinfo; 460664d6f86SAndrea Mayer 461664d6f86SAndrea Mayer return nli->nl_net; 462664d6f86SAndrea Mayer } 463664d6f86SAndrea Mayer 464664d6f86SAndrea Mayer static int __seg6_end_dt_vrf_build(struct seg6_local_lwt *slwt, const void *cfg, 465664d6f86SAndrea Mayer u16 family, struct netlink_ext_ack *extack) 466664d6f86SAndrea Mayer { 467664d6f86SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 468664d6f86SAndrea Mayer int vrf_ifindex; 469664d6f86SAndrea Mayer struct net *net; 470664d6f86SAndrea Mayer 471664d6f86SAndrea Mayer net = fib6_config_get_net(cfg); 472664d6f86SAndrea Mayer 473664d6f86SAndrea Mayer /* note that vrf_table was already set by parse_nla_vrftable() */ 474664d6f86SAndrea Mayer vrf_ifindex = l3mdev_ifindex_lookup_by_table_id(L3MDEV_TYPE_VRF, net, 475664d6f86SAndrea Mayer info->vrf_table); 476664d6f86SAndrea Mayer if (vrf_ifindex < 0) { 477664d6f86SAndrea Mayer if (vrf_ifindex == -EPERM) { 478664d6f86SAndrea Mayer NL_SET_ERR_MSG(extack, 479664d6f86SAndrea Mayer "Strict mode for VRF is disabled"); 480664d6f86SAndrea Mayer } else if (vrf_ifindex == -ENODEV) { 481664d6f86SAndrea Mayer NL_SET_ERR_MSG(extack, 482664d6f86SAndrea Mayer "Table has no associated VRF device"); 483664d6f86SAndrea Mayer } else { 484664d6f86SAndrea Mayer pr_debug("seg6local: SRv6 End.DT* creation error=%d\n", 485664d6f86SAndrea Mayer vrf_ifindex); 486664d6f86SAndrea Mayer } 487664d6f86SAndrea Mayer 488664d6f86SAndrea Mayer return vrf_ifindex; 489664d6f86SAndrea Mayer } 490664d6f86SAndrea Mayer 491664d6f86SAndrea Mayer info->net = net; 492664d6f86SAndrea Mayer info->vrf_ifindex = vrf_ifindex; 493664d6f86SAndrea Mayer 494664d6f86SAndrea Mayer switch (family) { 495664d6f86SAndrea Mayer case AF_INET: 496664d6f86SAndrea Mayer info->proto = htons(ETH_P_IP); 497664d6f86SAndrea Mayer info->hdrlen = sizeof(struct iphdr); 498664d6f86SAndrea Mayer break; 49920a081b7SAndrea Mayer case AF_INET6: 50020a081b7SAndrea Mayer info->proto = htons(ETH_P_IPV6); 50120a081b7SAndrea Mayer info->hdrlen = sizeof(struct ipv6hdr); 50220a081b7SAndrea Mayer break; 503664d6f86SAndrea Mayer default: 504664d6f86SAndrea Mayer return -EINVAL; 505664d6f86SAndrea Mayer } 506664d6f86SAndrea Mayer 507664d6f86SAndrea Mayer info->family = family; 508664d6f86SAndrea Mayer info->mode = DT_VRF_MODE; 509664d6f86SAndrea Mayer 510664d6f86SAndrea Mayer return 0; 511664d6f86SAndrea Mayer } 512664d6f86SAndrea Mayer 513664d6f86SAndrea Mayer /* The SRv6 End.DT4/DT6 behavior extracts the inner (IPv4/IPv6) packet and 514664d6f86SAndrea Mayer * routes the IPv4/IPv6 packet by looking at the configured routing table. 515664d6f86SAndrea Mayer * 516664d6f86SAndrea Mayer * In the SRv6 End.DT4/DT6 use case, we can receive traffic (IPv6+Segment 517664d6f86SAndrea Mayer * Routing Header packets) from several interfaces and the outer IPv6 518664d6f86SAndrea Mayer * destination address (DA) is used for retrieving the specific instance of the 519664d6f86SAndrea Mayer * End.DT4/DT6 behavior that should process the packets. 520664d6f86SAndrea Mayer * 521664d6f86SAndrea Mayer * However, the inner IPv4/IPv6 packet is not really bound to any receiving 522664d6f86SAndrea Mayer * interface and thus the End.DT4/DT6 sets the VRF (associated with the 523664d6f86SAndrea Mayer * corresponding routing table) as the *receiving* interface. 524664d6f86SAndrea Mayer * In other words, the End.DT4/DT6 processes a packet as if it has been received 525664d6f86SAndrea Mayer * directly by the VRF (and not by one of its slave devices, if any). 526664d6f86SAndrea Mayer * In this way, the VRF interface is used for routing the IPv4/IPv6 packet in 527664d6f86SAndrea Mayer * according to the routing table configured by the End.DT4/DT6 instance. 528664d6f86SAndrea Mayer * 529664d6f86SAndrea Mayer * This design allows you to get some interesting features like: 530664d6f86SAndrea Mayer * 1) the statistics on rx packets; 531664d6f86SAndrea Mayer * 2) the possibility to install a packet sniffer on the receiving interface 532664d6f86SAndrea Mayer * (the VRF one) for looking at the incoming packets; 533664d6f86SAndrea Mayer * 3) the possibility to leverage the netfilter prerouting hook for the inner 534664d6f86SAndrea Mayer * IPv4 packet. 535664d6f86SAndrea Mayer * 536664d6f86SAndrea Mayer * This function returns: 537664d6f86SAndrea Mayer * - the sk_buff* when the VRF rcv handler has processed the packet correctly; 538664d6f86SAndrea Mayer * - NULL when the skb is consumed by the VRF rcv handler; 539664d6f86SAndrea Mayer * - a pointer which encodes a negative error number in case of error. 540664d6f86SAndrea Mayer * Note that in this case, the function takes care of freeing the skb. 541664d6f86SAndrea Mayer */ 542664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_rcv(struct sk_buff *skb, u16 family, 543664d6f86SAndrea Mayer struct net_device *dev) 544664d6f86SAndrea Mayer { 545664d6f86SAndrea Mayer /* based on l3mdev_ip_rcv; we are only interested in the master */ 546664d6f86SAndrea Mayer if (unlikely(!netif_is_l3_master(dev) && !netif_has_l3_rx_handler(dev))) 547664d6f86SAndrea Mayer goto drop; 548664d6f86SAndrea Mayer 549664d6f86SAndrea Mayer if (unlikely(!dev->l3mdev_ops->l3mdev_l3_rcv)) 550664d6f86SAndrea Mayer goto drop; 551664d6f86SAndrea Mayer 552664d6f86SAndrea Mayer /* the decap packet IPv4/IPv6 does not come with any mac header info. 553664d6f86SAndrea Mayer * We must unset the mac header to allow the VRF device to rebuild it, 554664d6f86SAndrea Mayer * just in case there is a sniffer attached on the device. 555664d6f86SAndrea Mayer */ 556664d6f86SAndrea Mayer skb_unset_mac_header(skb); 557664d6f86SAndrea Mayer 558664d6f86SAndrea Mayer skb = dev->l3mdev_ops->l3mdev_l3_rcv(dev, skb, family); 559664d6f86SAndrea Mayer if (!skb) 560664d6f86SAndrea Mayer /* the skb buffer was consumed by the handler */ 561664d6f86SAndrea Mayer return NULL; 562664d6f86SAndrea Mayer 563664d6f86SAndrea Mayer /* when a packet is received by a VRF or by one of its slaves, the 564664d6f86SAndrea Mayer * master device reference is set into the skb. 565664d6f86SAndrea Mayer */ 566664d6f86SAndrea Mayer if (unlikely(skb->dev != dev || skb->skb_iif != dev->ifindex)) 567664d6f86SAndrea Mayer goto drop; 568664d6f86SAndrea Mayer 569664d6f86SAndrea Mayer return skb; 570664d6f86SAndrea Mayer 571664d6f86SAndrea Mayer drop: 572664d6f86SAndrea Mayer kfree_skb(skb); 573664d6f86SAndrea Mayer return ERR_PTR(-EINVAL); 574664d6f86SAndrea Mayer } 575664d6f86SAndrea Mayer 576664d6f86SAndrea Mayer static struct net_device *end_dt_get_vrf_rcu(struct sk_buff *skb, 577664d6f86SAndrea Mayer struct seg6_end_dt_info *info) 578664d6f86SAndrea Mayer { 579664d6f86SAndrea Mayer int vrf_ifindex = info->vrf_ifindex; 580664d6f86SAndrea Mayer struct net *net = info->net; 581664d6f86SAndrea Mayer 582664d6f86SAndrea Mayer if (unlikely(vrf_ifindex < 0)) 583664d6f86SAndrea Mayer goto error; 584664d6f86SAndrea Mayer 585664d6f86SAndrea Mayer if (unlikely(!net_eq(dev_net(skb->dev), net))) 586664d6f86SAndrea Mayer goto error; 587664d6f86SAndrea Mayer 588664d6f86SAndrea Mayer return dev_get_by_index_rcu(net, vrf_ifindex); 589664d6f86SAndrea Mayer 590664d6f86SAndrea Mayer error: 591664d6f86SAndrea Mayer return NULL; 592664d6f86SAndrea Mayer } 593664d6f86SAndrea Mayer 594664d6f86SAndrea Mayer static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb, 595664d6f86SAndrea Mayer struct seg6_local_lwt *slwt) 596664d6f86SAndrea Mayer { 597664d6f86SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 598664d6f86SAndrea Mayer struct net_device *vrf; 599664d6f86SAndrea Mayer 600664d6f86SAndrea Mayer vrf = end_dt_get_vrf_rcu(skb, info); 601664d6f86SAndrea Mayer if (unlikely(!vrf)) 602664d6f86SAndrea Mayer goto drop; 603664d6f86SAndrea Mayer 604664d6f86SAndrea Mayer skb->protocol = info->proto; 605664d6f86SAndrea Mayer 606664d6f86SAndrea Mayer skb_dst_drop(skb); 607664d6f86SAndrea Mayer 608664d6f86SAndrea Mayer skb_set_transport_header(skb, info->hdrlen); 609664d6f86SAndrea Mayer 610664d6f86SAndrea Mayer return end_dt_vrf_rcv(skb, info->family, vrf); 611664d6f86SAndrea Mayer 612664d6f86SAndrea Mayer drop: 613664d6f86SAndrea Mayer kfree_skb(skb); 614664d6f86SAndrea Mayer return ERR_PTR(-EINVAL); 615664d6f86SAndrea Mayer } 616664d6f86SAndrea Mayer 617664d6f86SAndrea Mayer static int input_action_end_dt4(struct sk_buff *skb, 618664d6f86SAndrea Mayer struct seg6_local_lwt *slwt) 619664d6f86SAndrea Mayer { 620664d6f86SAndrea Mayer struct iphdr *iph; 621664d6f86SAndrea Mayer int err; 622664d6f86SAndrea Mayer 623664d6f86SAndrea Mayer if (!decap_and_validate(skb, IPPROTO_IPIP)) 624664d6f86SAndrea Mayer goto drop; 625664d6f86SAndrea Mayer 626664d6f86SAndrea Mayer if (!pskb_may_pull(skb, sizeof(struct iphdr))) 627664d6f86SAndrea Mayer goto drop; 628664d6f86SAndrea Mayer 629664d6f86SAndrea Mayer skb = end_dt_vrf_core(skb, slwt); 630664d6f86SAndrea Mayer if (!skb) 631664d6f86SAndrea Mayer /* packet has been processed and consumed by the VRF */ 632664d6f86SAndrea Mayer return 0; 633664d6f86SAndrea Mayer 634664d6f86SAndrea Mayer if (IS_ERR(skb)) 635664d6f86SAndrea Mayer return PTR_ERR(skb); 636664d6f86SAndrea Mayer 637664d6f86SAndrea Mayer iph = ip_hdr(skb); 638664d6f86SAndrea Mayer 639664d6f86SAndrea Mayer err = ip_route_input(skb, iph->daddr, iph->saddr, 0, skb->dev); 640664d6f86SAndrea Mayer if (unlikely(err)) 641664d6f86SAndrea Mayer goto drop; 642664d6f86SAndrea Mayer 643664d6f86SAndrea Mayer return dst_input(skb); 644664d6f86SAndrea Mayer 645664d6f86SAndrea Mayer drop: 646664d6f86SAndrea Mayer kfree_skb(skb); 647664d6f86SAndrea Mayer return -EINVAL; 648664d6f86SAndrea Mayer } 649664d6f86SAndrea Mayer 650664d6f86SAndrea Mayer static int seg6_end_dt4_build(struct seg6_local_lwt *slwt, const void *cfg, 651664d6f86SAndrea Mayer struct netlink_ext_ack *extack) 652664d6f86SAndrea Mayer { 653664d6f86SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET, extack); 654664d6f86SAndrea Mayer } 65520a081b7SAndrea Mayer 65620a081b7SAndrea Mayer static enum 65720a081b7SAndrea Mayer seg6_end_dt_mode seg6_end_dt6_parse_mode(struct seg6_local_lwt *slwt) 65820a081b7SAndrea Mayer { 65920a081b7SAndrea Mayer unsigned long parsed_optattrs = slwt->parsed_optattrs; 66020a081b7SAndrea Mayer bool legacy, vrfmode; 66120a081b7SAndrea Mayer 662300a0fd8SAndrea Mayer legacy = !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE)); 663300a0fd8SAndrea Mayer vrfmode = !!(parsed_optattrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE)); 66420a081b7SAndrea Mayer 66520a081b7SAndrea Mayer if (!(legacy ^ vrfmode)) 66620a081b7SAndrea Mayer /* both are absent or present: invalid DT6 mode */ 66720a081b7SAndrea Mayer return DT_INVALID_MODE; 66820a081b7SAndrea Mayer 66920a081b7SAndrea Mayer return legacy ? DT_LEGACY_MODE : DT_VRF_MODE; 67020a081b7SAndrea Mayer } 67120a081b7SAndrea Mayer 67220a081b7SAndrea Mayer static enum seg6_end_dt_mode seg6_end_dt6_get_mode(struct seg6_local_lwt *slwt) 67320a081b7SAndrea Mayer { 67420a081b7SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 67520a081b7SAndrea Mayer 67620a081b7SAndrea Mayer return info->mode; 67720a081b7SAndrea Mayer } 67820a081b7SAndrea Mayer 67920a081b7SAndrea Mayer static int seg6_end_dt6_build(struct seg6_local_lwt *slwt, const void *cfg, 68020a081b7SAndrea Mayer struct netlink_ext_ack *extack) 68120a081b7SAndrea Mayer { 68220a081b7SAndrea Mayer enum seg6_end_dt_mode mode = seg6_end_dt6_parse_mode(slwt); 68320a081b7SAndrea Mayer struct seg6_end_dt_info *info = &slwt->dt_info; 68420a081b7SAndrea Mayer 68520a081b7SAndrea Mayer switch (mode) { 68620a081b7SAndrea Mayer case DT_LEGACY_MODE: 68720a081b7SAndrea Mayer info->mode = DT_LEGACY_MODE; 68820a081b7SAndrea Mayer return 0; 68920a081b7SAndrea Mayer case DT_VRF_MODE: 69020a081b7SAndrea Mayer return __seg6_end_dt_vrf_build(slwt, cfg, AF_INET6, extack); 69120a081b7SAndrea Mayer default: 69220a081b7SAndrea Mayer NL_SET_ERR_MSG(extack, "table or vrftable must be specified"); 69320a081b7SAndrea Mayer return -EINVAL; 69420a081b7SAndrea Mayer } 69520a081b7SAndrea Mayer } 696664d6f86SAndrea Mayer #endif 697664d6f86SAndrea Mayer 698891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb, 699891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 700891ef8ddSDavid Lebrun { 701891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 702891ef8ddSDavid Lebrun goto drop; 703891ef8ddSDavid Lebrun 704891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 705891ef8ddSDavid Lebrun goto drop; 706891ef8ddSDavid Lebrun 70720a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 70820a081b7SAndrea Mayer if (seg6_end_dt6_get_mode(slwt) == DT_LEGACY_MODE) 70920a081b7SAndrea Mayer goto legacy_mode; 71020a081b7SAndrea Mayer 71120a081b7SAndrea Mayer /* DT6_VRF_MODE */ 71220a081b7SAndrea Mayer skb = end_dt_vrf_core(skb, slwt); 71320a081b7SAndrea Mayer if (!skb) 71420a081b7SAndrea Mayer /* packet has been processed and consumed by the VRF */ 71520a081b7SAndrea Mayer return 0; 71620a081b7SAndrea Mayer 71720a081b7SAndrea Mayer if (IS_ERR(skb)) 71820a081b7SAndrea Mayer return PTR_ERR(skb); 71920a081b7SAndrea Mayer 72020a081b7SAndrea Mayer /* note: this time we do not need to specify the table because the VRF 72120a081b7SAndrea Mayer * takes care of selecting the correct table. 72220a081b7SAndrea Mayer */ 72320a081b7SAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, 0, true); 72420a081b7SAndrea Mayer 72520a081b7SAndrea Mayer return dst_input(skb); 72620a081b7SAndrea Mayer 72720a081b7SAndrea Mayer legacy_mode: 72820a081b7SAndrea Mayer #endif 729c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 730c71644d0SAndrea Mayer 731fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, slwt->table, true); 732891ef8ddSDavid Lebrun 733891ef8ddSDavid Lebrun return dst_input(skb); 734891ef8ddSDavid Lebrun 735891ef8ddSDavid Lebrun drop: 736891ef8ddSDavid Lebrun kfree_skb(skb); 737891ef8ddSDavid Lebrun return -EINVAL; 738891ef8ddSDavid Lebrun } 739891ef8ddSDavid Lebrun 740140f04c3SDavid Lebrun /* push an SRH on top of the current one */ 741140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 742140f04c3SDavid Lebrun { 743140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 744140f04c3SDavid Lebrun int err = -EINVAL; 745140f04c3SDavid Lebrun 746140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 747140f04c3SDavid Lebrun if (!srh) 748140f04c3SDavid Lebrun goto drop; 749140f04c3SDavid Lebrun 750140f04c3SDavid Lebrun err = seg6_do_srh_inline(skb, slwt->srh); 751140f04c3SDavid Lebrun if (err) 752140f04c3SDavid Lebrun goto drop; 753140f04c3SDavid Lebrun 754140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 755140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 756140f04c3SDavid Lebrun 7571c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 758140f04c3SDavid Lebrun 759140f04c3SDavid Lebrun return dst_input(skb); 760140f04c3SDavid Lebrun 761140f04c3SDavid Lebrun drop: 762140f04c3SDavid Lebrun kfree_skb(skb); 763140f04c3SDavid Lebrun return err; 764140f04c3SDavid Lebrun } 765140f04c3SDavid Lebrun 766140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */ 767140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb, 768140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 769140f04c3SDavid Lebrun { 770140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 771140f04c3SDavid Lebrun int err = -EINVAL; 772140f04c3SDavid Lebrun 773140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 774140f04c3SDavid Lebrun if (!srh) 775140f04c3SDavid Lebrun goto drop; 776140f04c3SDavid Lebrun 777d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 778140f04c3SDavid Lebrun 779140f04c3SDavid Lebrun skb_reset_inner_headers(skb); 780140f04c3SDavid Lebrun skb->encapsulation = 1; 781140f04c3SDavid Lebrun 78232d99d0bSDavid Lebrun err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6); 783140f04c3SDavid Lebrun if (err) 784140f04c3SDavid Lebrun goto drop; 785140f04c3SDavid Lebrun 786140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 787140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 788140f04c3SDavid Lebrun 7891c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 790140f04c3SDavid Lebrun 791140f04c3SDavid Lebrun return dst_input(skb); 792140f04c3SDavid Lebrun 793140f04c3SDavid Lebrun drop: 794140f04c3SDavid Lebrun kfree_skb(skb); 795140f04c3SDavid Lebrun return err; 796140f04c3SDavid Lebrun } 797140f04c3SDavid Lebrun 798fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states); 799fe94cc29SMathieu Xhonneux 800486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb) 801486cdf21SMathieu Xhonneux { 802486cdf21SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 803486cdf21SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 804486cdf21SMathieu Xhonneux struct ipv6_sr_hdr *srh = srh_state->srh; 805486cdf21SMathieu Xhonneux 806486cdf21SMathieu Xhonneux if (unlikely(srh == NULL)) 807486cdf21SMathieu Xhonneux return false; 808486cdf21SMathieu Xhonneux 809486cdf21SMathieu Xhonneux if (unlikely(!srh_state->valid)) { 810486cdf21SMathieu Xhonneux if ((srh_state->hdrlen & 7) != 0) 811486cdf21SMathieu Xhonneux return false; 812486cdf21SMathieu Xhonneux 813486cdf21SMathieu Xhonneux srh->hdrlen = (u8)(srh_state->hdrlen >> 3); 814bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true)) 815486cdf21SMathieu Xhonneux return false; 816486cdf21SMathieu Xhonneux 817486cdf21SMathieu Xhonneux srh_state->valid = true; 818486cdf21SMathieu Xhonneux } 819486cdf21SMathieu Xhonneux 820486cdf21SMathieu Xhonneux return true; 821486cdf21SMathieu Xhonneux } 822486cdf21SMathieu Xhonneux 823004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb, 824004d4b27SMathieu Xhonneux struct seg6_local_lwt *slwt) 825004d4b27SMathieu Xhonneux { 826004d4b27SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 827004d4b27SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 828004d4b27SMathieu Xhonneux struct ipv6_sr_hdr *srh; 829004d4b27SMathieu Xhonneux int ret; 830004d4b27SMathieu Xhonneux 831004d4b27SMathieu Xhonneux srh = get_and_validate_srh(skb); 832486cdf21SMathieu Xhonneux if (!srh) { 833486cdf21SMathieu Xhonneux kfree_skb(skb); 834486cdf21SMathieu Xhonneux return -EINVAL; 835486cdf21SMathieu Xhonneux } 836004d4b27SMathieu Xhonneux advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 837004d4b27SMathieu Xhonneux 838004d4b27SMathieu Xhonneux /* preempt_disable is needed to protect the per-CPU buffer srh_state, 839004d4b27SMathieu Xhonneux * which is also accessed by the bpf_lwt_seg6_* helpers 840004d4b27SMathieu Xhonneux */ 841004d4b27SMathieu Xhonneux preempt_disable(); 842486cdf21SMathieu Xhonneux srh_state->srh = srh; 843004d4b27SMathieu Xhonneux srh_state->hdrlen = srh->hdrlen << 3; 844486cdf21SMathieu Xhonneux srh_state->valid = true; 845004d4b27SMathieu Xhonneux 846004d4b27SMathieu Xhonneux rcu_read_lock(); 847004d4b27SMathieu Xhonneux bpf_compute_data_pointers(skb); 848004d4b27SMathieu Xhonneux ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb); 849004d4b27SMathieu Xhonneux rcu_read_unlock(); 850004d4b27SMathieu Xhonneux 851004d4b27SMathieu Xhonneux switch (ret) { 852004d4b27SMathieu Xhonneux case BPF_OK: 853004d4b27SMathieu Xhonneux case BPF_REDIRECT: 854004d4b27SMathieu Xhonneux break; 855004d4b27SMathieu Xhonneux case BPF_DROP: 856004d4b27SMathieu Xhonneux goto drop; 857004d4b27SMathieu Xhonneux default: 858004d4b27SMathieu Xhonneux pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret); 859004d4b27SMathieu Xhonneux goto drop; 860004d4b27SMathieu Xhonneux } 861004d4b27SMathieu Xhonneux 862486cdf21SMathieu Xhonneux if (srh_state->srh && !seg6_bpf_has_valid_srh(skb)) 863004d4b27SMathieu Xhonneux goto drop; 864004d4b27SMathieu Xhonneux 865486cdf21SMathieu Xhonneux preempt_enable(); 866004d4b27SMathieu Xhonneux if (ret != BPF_REDIRECT) 867004d4b27SMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 868004d4b27SMathieu Xhonneux 869004d4b27SMathieu Xhonneux return dst_input(skb); 870004d4b27SMathieu Xhonneux 871004d4b27SMathieu Xhonneux drop: 872486cdf21SMathieu Xhonneux preempt_enable(); 873004d4b27SMathieu Xhonneux kfree_skb(skb); 874004d4b27SMathieu Xhonneux return -EINVAL; 875004d4b27SMathieu Xhonneux } 876004d4b27SMathieu Xhonneux 877d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = { 878d1df6fd8SDavid Lebrun { 879d1df6fd8SDavid Lebrun .action = SEG6_LOCAL_ACTION_END, 880d1df6fd8SDavid Lebrun .attrs = 0, 881140f04c3SDavid Lebrun .input = input_action_end, 882d1df6fd8SDavid Lebrun }, 883140f04c3SDavid Lebrun { 884140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_X, 885300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), 886140f04c3SDavid Lebrun .input = input_action_end_x, 887140f04c3SDavid Lebrun }, 888140f04c3SDavid Lebrun { 889891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_T, 890300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE), 891891ef8ddSDavid Lebrun .input = input_action_end_t, 892891ef8ddSDavid Lebrun }, 893891ef8ddSDavid Lebrun { 894891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX2, 895300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_OIF), 896891ef8ddSDavid Lebrun .input = input_action_end_dx2, 897891ef8ddSDavid Lebrun }, 898891ef8ddSDavid Lebrun { 899140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX6, 900300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH6), 901140f04c3SDavid Lebrun .input = input_action_end_dx6, 902140f04c3SDavid Lebrun }, 903140f04c3SDavid Lebrun { 904891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX4, 905300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_NH4), 906891ef8ddSDavid Lebrun .input = input_action_end_dx4, 907891ef8ddSDavid Lebrun }, 908891ef8ddSDavid Lebrun { 909664d6f86SAndrea Mayer .action = SEG6_LOCAL_ACTION_END_DT4, 910300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 911664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 912664d6f86SAndrea Mayer .input = input_action_end_dt4, 913664d6f86SAndrea Mayer .slwt_ops = { 914664d6f86SAndrea Mayer .build_state = seg6_end_dt4_build, 915664d6f86SAndrea Mayer }, 916664d6f86SAndrea Mayer #endif 917664d6f86SAndrea Mayer }, 918664d6f86SAndrea Mayer { 919891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DT6, 92020a081b7SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 92120a081b7SAndrea Mayer .attrs = 0, 922300a0fd8SAndrea Mayer .optattrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE) | 923300a0fd8SAndrea Mayer SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE), 92420a081b7SAndrea Mayer .slwt_ops = { 92520a081b7SAndrea Mayer .build_state = seg6_end_dt6_build, 92620a081b7SAndrea Mayer }, 92720a081b7SAndrea Mayer #else 928300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_TABLE), 92920a081b7SAndrea Mayer #endif 930891ef8ddSDavid Lebrun .input = input_action_end_dt6, 931891ef8ddSDavid Lebrun }, 932891ef8ddSDavid Lebrun { 933140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6, 934300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH), 935140f04c3SDavid Lebrun .input = input_action_end_b6, 936140f04c3SDavid Lebrun }, 937140f04c3SDavid Lebrun { 938140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6_ENCAP, 939300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH), 940140f04c3SDavid Lebrun .input = input_action_end_b6_encap, 941140f04c3SDavid Lebrun .static_headroom = sizeof(struct ipv6hdr), 942004d4b27SMathieu Xhonneux }, 943004d4b27SMathieu Xhonneux { 944004d4b27SMathieu Xhonneux .action = SEG6_LOCAL_ACTION_END_BPF, 945300a0fd8SAndrea Mayer .attrs = SEG6_F_ATTR(SEG6_LOCAL_BPF), 946004d4b27SMathieu Xhonneux .input = input_action_end_bpf, 947004d4b27SMathieu Xhonneux }, 948004d4b27SMathieu Xhonneux 949d1df6fd8SDavid Lebrun }; 950d1df6fd8SDavid Lebrun 951d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action) 952d1df6fd8SDavid Lebrun { 953d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 954d1df6fd8SDavid Lebrun int i, count; 955d1df6fd8SDavid Lebrun 956709af180SColin Ian King count = ARRAY_SIZE(seg6_action_table); 957d1df6fd8SDavid Lebrun for (i = 0; i < count; i++) { 958d1df6fd8SDavid Lebrun desc = &seg6_action_table[i]; 959d1df6fd8SDavid Lebrun if (desc->action == action) 960d1df6fd8SDavid Lebrun return desc; 961d1df6fd8SDavid Lebrun } 962d1df6fd8SDavid Lebrun 963d1df6fd8SDavid Lebrun return NULL; 964d1df6fd8SDavid Lebrun } 965d1df6fd8SDavid Lebrun 966d1df6fd8SDavid Lebrun static int seg6_local_input(struct sk_buff *skb) 967d1df6fd8SDavid Lebrun { 968d1df6fd8SDavid Lebrun struct dst_entry *orig_dst = skb_dst(skb); 969d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 970d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 971d1df6fd8SDavid Lebrun 9726285217fSDavid Lebrun if (skb->protocol != htons(ETH_P_IPV6)) { 9736285217fSDavid Lebrun kfree_skb(skb); 9746285217fSDavid Lebrun return -EINVAL; 9756285217fSDavid Lebrun } 9766285217fSDavid Lebrun 977d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 978d1df6fd8SDavid Lebrun desc = slwt->desc; 979d1df6fd8SDavid Lebrun 980d1df6fd8SDavid Lebrun return desc->input(skb, slwt); 981d1df6fd8SDavid Lebrun } 982d1df6fd8SDavid Lebrun 983d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { 984d1df6fd8SDavid Lebrun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, 985d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, 986d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, 987664d6f86SAndrea Mayer [SEG6_LOCAL_VRFTABLE] = { .type = NLA_U32 }, 988d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, 989d1df6fd8SDavid Lebrun .len = sizeof(struct in_addr) }, 990d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, 991d1df6fd8SDavid Lebrun .len = sizeof(struct in6_addr) }, 992d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, 993d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 994004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .type = NLA_NESTED }, 995d1df6fd8SDavid Lebrun }; 996d1df6fd8SDavid Lebrun 9972d9cc60aSDavid Lebrun static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt) 9982d9cc60aSDavid Lebrun { 9992d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 10002d9cc60aSDavid Lebrun int len; 10012d9cc60aSDavid Lebrun 10022d9cc60aSDavid Lebrun srh = nla_data(attrs[SEG6_LOCAL_SRH]); 10032d9cc60aSDavid Lebrun len = nla_len(attrs[SEG6_LOCAL_SRH]); 10042d9cc60aSDavid Lebrun 10052d9cc60aSDavid Lebrun /* SRH must contain at least one segment */ 10062d9cc60aSDavid Lebrun if (len < sizeof(*srh) + sizeof(struct in6_addr)) 10072d9cc60aSDavid Lebrun return -EINVAL; 10082d9cc60aSDavid Lebrun 1009bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, false)) 10102d9cc60aSDavid Lebrun return -EINVAL; 10112d9cc60aSDavid Lebrun 10127fa41efaSYueHaibing slwt->srh = kmemdup(srh, len, GFP_KERNEL); 10132d9cc60aSDavid Lebrun if (!slwt->srh) 10142d9cc60aSDavid Lebrun return -ENOMEM; 10152d9cc60aSDavid Lebrun 10162d9cc60aSDavid Lebrun slwt->headroom += len; 10172d9cc60aSDavid Lebrun 10182d9cc60aSDavid Lebrun return 0; 10192d9cc60aSDavid Lebrun } 10202d9cc60aSDavid Lebrun 10212d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt) 10222d9cc60aSDavid Lebrun { 10232d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 10242d9cc60aSDavid Lebrun struct nlattr *nla; 10252d9cc60aSDavid Lebrun int len; 10262d9cc60aSDavid Lebrun 10272d9cc60aSDavid Lebrun srh = slwt->srh; 10282d9cc60aSDavid Lebrun len = (srh->hdrlen + 1) << 3; 10292d9cc60aSDavid Lebrun 10302d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_SRH, len); 10312d9cc60aSDavid Lebrun if (!nla) 10322d9cc60aSDavid Lebrun return -EMSGSIZE; 10332d9cc60aSDavid Lebrun 10342d9cc60aSDavid Lebrun memcpy(nla_data(nla), srh, len); 10352d9cc60aSDavid Lebrun 10362d9cc60aSDavid Lebrun return 0; 10372d9cc60aSDavid Lebrun } 10382d9cc60aSDavid Lebrun 10392d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 10402d9cc60aSDavid Lebrun { 10412d9cc60aSDavid Lebrun int len = (a->srh->hdrlen + 1) << 3; 10422d9cc60aSDavid Lebrun 10432d9cc60aSDavid Lebrun if (len != ((b->srh->hdrlen + 1) << 3)) 10442d9cc60aSDavid Lebrun return 1; 10452d9cc60aSDavid Lebrun 10462d9cc60aSDavid Lebrun return memcmp(a->srh, b->srh, len); 10472d9cc60aSDavid Lebrun } 10482d9cc60aSDavid Lebrun 1049964adce5SAndrea Mayer static void destroy_attr_srh(struct seg6_local_lwt *slwt) 1050964adce5SAndrea Mayer { 1051964adce5SAndrea Mayer kfree(slwt->srh); 1052964adce5SAndrea Mayer } 1053964adce5SAndrea Mayer 10542d9cc60aSDavid Lebrun static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt) 10552d9cc60aSDavid Lebrun { 10562d9cc60aSDavid Lebrun slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]); 10572d9cc60aSDavid Lebrun 10582d9cc60aSDavid Lebrun return 0; 10592d9cc60aSDavid Lebrun } 10602d9cc60aSDavid Lebrun 10612d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt) 10622d9cc60aSDavid Lebrun { 10632d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table)) 10642d9cc60aSDavid Lebrun return -EMSGSIZE; 10652d9cc60aSDavid Lebrun 10662d9cc60aSDavid Lebrun return 0; 10672d9cc60aSDavid Lebrun } 10682d9cc60aSDavid Lebrun 10692d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 10702d9cc60aSDavid Lebrun { 10712d9cc60aSDavid Lebrun if (a->table != b->table) 10722d9cc60aSDavid Lebrun return 1; 10732d9cc60aSDavid Lebrun 10742d9cc60aSDavid Lebrun return 0; 10752d9cc60aSDavid Lebrun } 10762d9cc60aSDavid Lebrun 1077664d6f86SAndrea Mayer static struct 1078664d6f86SAndrea Mayer seg6_end_dt_info *seg6_possible_end_dt_info(struct seg6_local_lwt *slwt) 1079664d6f86SAndrea Mayer { 1080664d6f86SAndrea Mayer #ifdef CONFIG_NET_L3_MASTER_DEV 1081664d6f86SAndrea Mayer return &slwt->dt_info; 1082664d6f86SAndrea Mayer #else 1083664d6f86SAndrea Mayer return ERR_PTR(-EOPNOTSUPP); 1084664d6f86SAndrea Mayer #endif 1085664d6f86SAndrea Mayer } 1086664d6f86SAndrea Mayer 1087664d6f86SAndrea Mayer static int parse_nla_vrftable(struct nlattr **attrs, 1088664d6f86SAndrea Mayer struct seg6_local_lwt *slwt) 1089664d6f86SAndrea Mayer { 1090664d6f86SAndrea Mayer struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt); 1091664d6f86SAndrea Mayer 1092664d6f86SAndrea Mayer if (IS_ERR(info)) 1093664d6f86SAndrea Mayer return PTR_ERR(info); 1094664d6f86SAndrea Mayer 1095664d6f86SAndrea Mayer info->vrf_table = nla_get_u32(attrs[SEG6_LOCAL_VRFTABLE]); 1096664d6f86SAndrea Mayer 1097664d6f86SAndrea Mayer return 0; 1098664d6f86SAndrea Mayer } 1099664d6f86SAndrea Mayer 1100664d6f86SAndrea Mayer static int put_nla_vrftable(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1101664d6f86SAndrea Mayer { 1102664d6f86SAndrea Mayer struct seg6_end_dt_info *info = seg6_possible_end_dt_info(slwt); 1103664d6f86SAndrea Mayer 1104664d6f86SAndrea Mayer if (IS_ERR(info)) 1105664d6f86SAndrea Mayer return PTR_ERR(info); 1106664d6f86SAndrea Mayer 1107664d6f86SAndrea Mayer if (nla_put_u32(skb, SEG6_LOCAL_VRFTABLE, info->vrf_table)) 1108664d6f86SAndrea Mayer return -EMSGSIZE; 1109664d6f86SAndrea Mayer 1110664d6f86SAndrea Mayer return 0; 1111664d6f86SAndrea Mayer } 1112664d6f86SAndrea Mayer 1113664d6f86SAndrea Mayer static int cmp_nla_vrftable(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1114664d6f86SAndrea Mayer { 1115664d6f86SAndrea Mayer struct seg6_end_dt_info *info_a = seg6_possible_end_dt_info(a); 1116664d6f86SAndrea Mayer struct seg6_end_dt_info *info_b = seg6_possible_end_dt_info(b); 1117664d6f86SAndrea Mayer 1118664d6f86SAndrea Mayer if (info_a->vrf_table != info_b->vrf_table) 1119664d6f86SAndrea Mayer return 1; 1120664d6f86SAndrea Mayer 1121664d6f86SAndrea Mayer return 0; 1122664d6f86SAndrea Mayer } 1123664d6f86SAndrea Mayer 11242d9cc60aSDavid Lebrun static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt) 11252d9cc60aSDavid Lebrun { 11262d9cc60aSDavid Lebrun memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]), 11272d9cc60aSDavid Lebrun sizeof(struct in_addr)); 11282d9cc60aSDavid Lebrun 11292d9cc60aSDavid Lebrun return 0; 11302d9cc60aSDavid Lebrun } 11312d9cc60aSDavid Lebrun 11322d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt) 11332d9cc60aSDavid Lebrun { 11342d9cc60aSDavid Lebrun struct nlattr *nla; 11352d9cc60aSDavid Lebrun 11362d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr)); 11372d9cc60aSDavid Lebrun if (!nla) 11382d9cc60aSDavid Lebrun return -EMSGSIZE; 11392d9cc60aSDavid Lebrun 11402d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr)); 11412d9cc60aSDavid Lebrun 11422d9cc60aSDavid Lebrun return 0; 11432d9cc60aSDavid Lebrun } 11442d9cc60aSDavid Lebrun 11452d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 11462d9cc60aSDavid Lebrun { 11472d9cc60aSDavid Lebrun return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr)); 11482d9cc60aSDavid Lebrun } 11492d9cc60aSDavid Lebrun 11502d9cc60aSDavid Lebrun static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt) 11512d9cc60aSDavid Lebrun { 11522d9cc60aSDavid Lebrun memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]), 11532d9cc60aSDavid Lebrun sizeof(struct in6_addr)); 11542d9cc60aSDavid Lebrun 11552d9cc60aSDavid Lebrun return 0; 11562d9cc60aSDavid Lebrun } 11572d9cc60aSDavid Lebrun 11582d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 11592d9cc60aSDavid Lebrun { 11602d9cc60aSDavid Lebrun struct nlattr *nla; 11612d9cc60aSDavid Lebrun 11622d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr)); 11632d9cc60aSDavid Lebrun if (!nla) 11642d9cc60aSDavid Lebrun return -EMSGSIZE; 11652d9cc60aSDavid Lebrun 11662d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr)); 11672d9cc60aSDavid Lebrun 11682d9cc60aSDavid Lebrun return 0; 11692d9cc60aSDavid Lebrun } 11702d9cc60aSDavid Lebrun 11712d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 11722d9cc60aSDavid Lebrun { 11732d9cc60aSDavid Lebrun return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr)); 11742d9cc60aSDavid Lebrun } 11752d9cc60aSDavid Lebrun 11762d9cc60aSDavid Lebrun static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 11772d9cc60aSDavid Lebrun { 11782d9cc60aSDavid Lebrun slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]); 11792d9cc60aSDavid Lebrun 11802d9cc60aSDavid Lebrun return 0; 11812d9cc60aSDavid Lebrun } 11822d9cc60aSDavid Lebrun 11832d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 11842d9cc60aSDavid Lebrun { 11852d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif)) 11862d9cc60aSDavid Lebrun return -EMSGSIZE; 11872d9cc60aSDavid Lebrun 11882d9cc60aSDavid Lebrun return 0; 11892d9cc60aSDavid Lebrun } 11902d9cc60aSDavid Lebrun 11912d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 11922d9cc60aSDavid Lebrun { 11932d9cc60aSDavid Lebrun if (a->iif != b->iif) 11942d9cc60aSDavid Lebrun return 1; 11952d9cc60aSDavid Lebrun 11962d9cc60aSDavid Lebrun return 0; 11972d9cc60aSDavid Lebrun } 11982d9cc60aSDavid Lebrun 11992d9cc60aSDavid Lebrun static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 12002d9cc60aSDavid Lebrun { 12012d9cc60aSDavid Lebrun slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]); 12022d9cc60aSDavid Lebrun 12032d9cc60aSDavid Lebrun return 0; 12042d9cc60aSDavid Lebrun } 12052d9cc60aSDavid Lebrun 12062d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 12072d9cc60aSDavid Lebrun { 12082d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif)) 12092d9cc60aSDavid Lebrun return -EMSGSIZE; 12102d9cc60aSDavid Lebrun 12112d9cc60aSDavid Lebrun return 0; 12122d9cc60aSDavid Lebrun } 12132d9cc60aSDavid Lebrun 12142d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 12152d9cc60aSDavid Lebrun { 12162d9cc60aSDavid Lebrun if (a->oif != b->oif) 12172d9cc60aSDavid Lebrun return 1; 12182d9cc60aSDavid Lebrun 12192d9cc60aSDavid Lebrun return 0; 12202d9cc60aSDavid Lebrun } 12212d9cc60aSDavid Lebrun 1222004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256 1223004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = { 1224004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG] = { .type = NLA_U32, }, 1225004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING, 1226004d4b27SMathieu Xhonneux .len = MAX_PROG_NAME }, 1227004d4b27SMathieu Xhonneux }; 1228004d4b27SMathieu Xhonneux 1229004d4b27SMathieu Xhonneux static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt) 1230004d4b27SMathieu Xhonneux { 1231004d4b27SMathieu Xhonneux struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1]; 1232004d4b27SMathieu Xhonneux struct bpf_prog *p; 1233004d4b27SMathieu Xhonneux int ret; 1234004d4b27SMathieu Xhonneux u32 fd; 1235004d4b27SMathieu Xhonneux 12368cb08174SJohannes Berg ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX, 12378cb08174SJohannes Berg attrs[SEG6_LOCAL_BPF], 12388cb08174SJohannes Berg bpf_prog_policy, NULL); 1239004d4b27SMathieu Xhonneux if (ret < 0) 1240004d4b27SMathieu Xhonneux return ret; 1241004d4b27SMathieu Xhonneux 1242004d4b27SMathieu Xhonneux if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME]) 1243004d4b27SMathieu Xhonneux return -EINVAL; 1244004d4b27SMathieu Xhonneux 1245004d4b27SMathieu Xhonneux slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL); 1246004d4b27SMathieu Xhonneux if (!slwt->bpf.name) 1247004d4b27SMathieu Xhonneux return -ENOMEM; 1248004d4b27SMathieu Xhonneux 1249004d4b27SMathieu Xhonneux fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]); 1250004d4b27SMathieu Xhonneux p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL); 1251004d4b27SMathieu Xhonneux if (IS_ERR(p)) { 1252004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 1253004d4b27SMathieu Xhonneux return PTR_ERR(p); 1254004d4b27SMathieu Xhonneux } 1255004d4b27SMathieu Xhonneux 1256004d4b27SMathieu Xhonneux slwt->bpf.prog = p; 1257004d4b27SMathieu Xhonneux return 0; 1258004d4b27SMathieu Xhonneux } 1259004d4b27SMathieu Xhonneux 1260004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt) 1261004d4b27SMathieu Xhonneux { 1262004d4b27SMathieu Xhonneux struct nlattr *nest; 1263004d4b27SMathieu Xhonneux 1264004d4b27SMathieu Xhonneux if (!slwt->bpf.prog) 1265004d4b27SMathieu Xhonneux return 0; 1266004d4b27SMathieu Xhonneux 1267ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF); 1268004d4b27SMathieu Xhonneux if (!nest) 1269004d4b27SMathieu Xhonneux return -EMSGSIZE; 1270004d4b27SMathieu Xhonneux 1271004d4b27SMathieu Xhonneux if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id)) 1272004d4b27SMathieu Xhonneux return -EMSGSIZE; 1273004d4b27SMathieu Xhonneux 1274004d4b27SMathieu Xhonneux if (slwt->bpf.name && 1275004d4b27SMathieu Xhonneux nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name)) 1276004d4b27SMathieu Xhonneux return -EMSGSIZE; 1277004d4b27SMathieu Xhonneux 1278004d4b27SMathieu Xhonneux return nla_nest_end(skb, nest); 1279004d4b27SMathieu Xhonneux } 1280004d4b27SMathieu Xhonneux 1281004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 1282004d4b27SMathieu Xhonneux { 1283004d4b27SMathieu Xhonneux if (!a->bpf.name && !b->bpf.name) 1284004d4b27SMathieu Xhonneux return 0; 1285004d4b27SMathieu Xhonneux 1286004d4b27SMathieu Xhonneux if (!a->bpf.name || !b->bpf.name) 1287004d4b27SMathieu Xhonneux return 1; 1288004d4b27SMathieu Xhonneux 1289004d4b27SMathieu Xhonneux return strcmp(a->bpf.name, b->bpf.name); 1290004d4b27SMathieu Xhonneux } 1291004d4b27SMathieu Xhonneux 1292964adce5SAndrea Mayer static void destroy_attr_bpf(struct seg6_local_lwt *slwt) 1293964adce5SAndrea Mayer { 1294964adce5SAndrea Mayer kfree(slwt->bpf.name); 1295964adce5SAndrea Mayer if (slwt->bpf.prog) 1296964adce5SAndrea Mayer bpf_prog_put(slwt->bpf.prog); 1297964adce5SAndrea Mayer } 1298964adce5SAndrea Mayer 1299d1df6fd8SDavid Lebrun struct seg6_action_param { 1300d1df6fd8SDavid Lebrun int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt); 1301d1df6fd8SDavid Lebrun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 1302d1df6fd8SDavid Lebrun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); 1303964adce5SAndrea Mayer 1304964adce5SAndrea Mayer /* optional destroy() callback useful for releasing resources which 1305964adce5SAndrea Mayer * have been previously acquired in the corresponding parse() 1306964adce5SAndrea Mayer * function. 1307964adce5SAndrea Mayer */ 1308964adce5SAndrea Mayer void (*destroy)(struct seg6_local_lwt *slwt); 1309d1df6fd8SDavid Lebrun }; 1310d1df6fd8SDavid Lebrun 1311d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { 13122d9cc60aSDavid Lebrun [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh, 13132d9cc60aSDavid Lebrun .put = put_nla_srh, 1314964adce5SAndrea Mayer .cmp = cmp_nla_srh, 1315964adce5SAndrea Mayer .destroy = destroy_attr_srh }, 1316d1df6fd8SDavid Lebrun 13172d9cc60aSDavid Lebrun [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table, 13182d9cc60aSDavid Lebrun .put = put_nla_table, 13192d9cc60aSDavid Lebrun .cmp = cmp_nla_table }, 1320d1df6fd8SDavid Lebrun 13212d9cc60aSDavid Lebrun [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4, 13222d9cc60aSDavid Lebrun .put = put_nla_nh4, 13232d9cc60aSDavid Lebrun .cmp = cmp_nla_nh4 }, 1324d1df6fd8SDavid Lebrun 13252d9cc60aSDavid Lebrun [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6, 13262d9cc60aSDavid Lebrun .put = put_nla_nh6, 13272d9cc60aSDavid Lebrun .cmp = cmp_nla_nh6 }, 1328d1df6fd8SDavid Lebrun 13292d9cc60aSDavid Lebrun [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif, 13302d9cc60aSDavid Lebrun .put = put_nla_iif, 13312d9cc60aSDavid Lebrun .cmp = cmp_nla_iif }, 1332d1df6fd8SDavid Lebrun 13332d9cc60aSDavid Lebrun [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif, 13342d9cc60aSDavid Lebrun .put = put_nla_oif, 13352d9cc60aSDavid Lebrun .cmp = cmp_nla_oif }, 1336004d4b27SMathieu Xhonneux 1337004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .parse = parse_nla_bpf, 1338004d4b27SMathieu Xhonneux .put = put_nla_bpf, 1339964adce5SAndrea Mayer .cmp = cmp_nla_bpf, 1340964adce5SAndrea Mayer .destroy = destroy_attr_bpf }, 1341004d4b27SMathieu Xhonneux 1342664d6f86SAndrea Mayer [SEG6_LOCAL_VRFTABLE] = { .parse = parse_nla_vrftable, 1343664d6f86SAndrea Mayer .put = put_nla_vrftable, 1344664d6f86SAndrea Mayer .cmp = cmp_nla_vrftable }, 1345664d6f86SAndrea Mayer 1346d1df6fd8SDavid Lebrun }; 1347d1df6fd8SDavid Lebrun 1348964adce5SAndrea Mayer /* call the destroy() callback (if available) for each set attribute in 13490a3021f1SAndrea Mayer * @parsed_attrs, starting from the first attribute up to the @max_parsed 13500a3021f1SAndrea Mayer * (excluded) attribute. 1351964adce5SAndrea Mayer */ 13520a3021f1SAndrea Mayer static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed, 13530a3021f1SAndrea Mayer struct seg6_local_lwt *slwt) 1354964adce5SAndrea Mayer { 1355964adce5SAndrea Mayer struct seg6_action_param *param; 1356964adce5SAndrea Mayer int i; 1357964adce5SAndrea Mayer 1358964adce5SAndrea Mayer /* Every required seg6local attribute is identified by an ID which is 1359964adce5SAndrea Mayer * encoded as a flag (i.e: 1 << ID) in the 'attrs' bitmask; 1360964adce5SAndrea Mayer * 13610a3021f1SAndrea Mayer * We scan the 'parsed_attrs' bitmask, starting from the first attribute 1362964adce5SAndrea Mayer * up to the @max_parsed (excluded) attribute. 1363964adce5SAndrea Mayer * For each set attribute, we retrieve the corresponding destroy() 1364964adce5SAndrea Mayer * callback. If the callback is not available, then we skip to the next 1365964adce5SAndrea Mayer * attribute; otherwise, we call the destroy() callback. 1366964adce5SAndrea Mayer */ 1367964adce5SAndrea Mayer for (i = 0; i < max_parsed; ++i) { 1368300a0fd8SAndrea Mayer if (!(parsed_attrs & SEG6_F_ATTR(i))) 1369964adce5SAndrea Mayer continue; 1370964adce5SAndrea Mayer 1371964adce5SAndrea Mayer param = &seg6_action_params[i]; 1372964adce5SAndrea Mayer 1373964adce5SAndrea Mayer if (param->destroy) 1374964adce5SAndrea Mayer param->destroy(slwt); 1375964adce5SAndrea Mayer } 1376964adce5SAndrea Mayer } 1377964adce5SAndrea Mayer 1378964adce5SAndrea Mayer /* release all the resources that may have been acquired during parsing 1379964adce5SAndrea Mayer * operations. 1380964adce5SAndrea Mayer */ 1381964adce5SAndrea Mayer static void destroy_attrs(struct seg6_local_lwt *slwt) 1382964adce5SAndrea Mayer { 13830a3021f1SAndrea Mayer unsigned long attrs = slwt->desc->attrs | slwt->parsed_optattrs; 13840a3021f1SAndrea Mayer 13850a3021f1SAndrea Mayer __destroy_attrs(attrs, SEG6_LOCAL_MAX + 1, slwt); 13860a3021f1SAndrea Mayer } 13870a3021f1SAndrea Mayer 13880a3021f1SAndrea Mayer static int parse_nla_optional_attrs(struct nlattr **attrs, 13890a3021f1SAndrea Mayer struct seg6_local_lwt *slwt) 13900a3021f1SAndrea Mayer { 13910a3021f1SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 13920a3021f1SAndrea Mayer unsigned long parsed_optattrs = 0; 13930a3021f1SAndrea Mayer struct seg6_action_param *param; 13940a3021f1SAndrea Mayer int err, i; 13950a3021f1SAndrea Mayer 13960a3021f1SAndrea Mayer for (i = 0; i < SEG6_LOCAL_MAX + 1; ++i) { 1397300a0fd8SAndrea Mayer if (!(desc->optattrs & SEG6_F_ATTR(i)) || !attrs[i]) 13980a3021f1SAndrea Mayer continue; 13990a3021f1SAndrea Mayer 14000a3021f1SAndrea Mayer /* once here, the i-th attribute is provided by the 14010a3021f1SAndrea Mayer * userspace AND it is identified optional as well. 14020a3021f1SAndrea Mayer */ 14030a3021f1SAndrea Mayer param = &seg6_action_params[i]; 14040a3021f1SAndrea Mayer 14050a3021f1SAndrea Mayer err = param->parse(attrs, slwt); 14060a3021f1SAndrea Mayer if (err < 0) 14070a3021f1SAndrea Mayer goto parse_optattrs_err; 14080a3021f1SAndrea Mayer 14090a3021f1SAndrea Mayer /* current attribute has been correctly parsed */ 1410300a0fd8SAndrea Mayer parsed_optattrs |= SEG6_F_ATTR(i); 14110a3021f1SAndrea Mayer } 14120a3021f1SAndrea Mayer 14130a3021f1SAndrea Mayer /* store in the tunnel state all the optional attributed successfully 14140a3021f1SAndrea Mayer * parsed. 14150a3021f1SAndrea Mayer */ 14160a3021f1SAndrea Mayer slwt->parsed_optattrs = parsed_optattrs; 14170a3021f1SAndrea Mayer 14180a3021f1SAndrea Mayer return 0; 14190a3021f1SAndrea Mayer 14200a3021f1SAndrea Mayer parse_optattrs_err: 14210a3021f1SAndrea Mayer __destroy_attrs(parsed_optattrs, i, slwt); 14220a3021f1SAndrea Mayer 14230a3021f1SAndrea Mayer return err; 1424964adce5SAndrea Mayer } 1425964adce5SAndrea Mayer 1426cfdf64a0SAndrea Mayer /* call the custom constructor of the behavior during its initialization phase 1427cfdf64a0SAndrea Mayer * and after that all its attributes have been parsed successfully. 1428cfdf64a0SAndrea Mayer */ 1429cfdf64a0SAndrea Mayer static int 1430cfdf64a0SAndrea Mayer seg6_local_lwtunnel_build_state(struct seg6_local_lwt *slwt, const void *cfg, 1431cfdf64a0SAndrea Mayer struct netlink_ext_ack *extack) 1432cfdf64a0SAndrea Mayer { 1433cfdf64a0SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 1434cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops *ops; 1435cfdf64a0SAndrea Mayer 1436cfdf64a0SAndrea Mayer ops = &desc->slwt_ops; 1437cfdf64a0SAndrea Mayer if (!ops->build_state) 1438cfdf64a0SAndrea Mayer return 0; 1439cfdf64a0SAndrea Mayer 1440cfdf64a0SAndrea Mayer return ops->build_state(slwt, cfg, extack); 1441cfdf64a0SAndrea Mayer } 1442cfdf64a0SAndrea Mayer 1443cfdf64a0SAndrea Mayer /* call the custom destructor of the behavior which is invoked before the 1444cfdf64a0SAndrea Mayer * tunnel is going to be destroyed. 1445cfdf64a0SAndrea Mayer */ 1446cfdf64a0SAndrea Mayer static void seg6_local_lwtunnel_destroy_state(struct seg6_local_lwt *slwt) 1447cfdf64a0SAndrea Mayer { 1448cfdf64a0SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 1449cfdf64a0SAndrea Mayer struct seg6_local_lwtunnel_ops *ops; 1450cfdf64a0SAndrea Mayer 1451cfdf64a0SAndrea Mayer ops = &desc->slwt_ops; 1452cfdf64a0SAndrea Mayer if (!ops->destroy_state) 1453cfdf64a0SAndrea Mayer return; 1454cfdf64a0SAndrea Mayer 1455cfdf64a0SAndrea Mayer ops->destroy_state(slwt); 1456cfdf64a0SAndrea Mayer } 1457cfdf64a0SAndrea Mayer 1458d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) 1459d1df6fd8SDavid Lebrun { 1460d1df6fd8SDavid Lebrun struct seg6_action_param *param; 1461d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 14620a3021f1SAndrea Mayer unsigned long invalid_attrs; 1463d1df6fd8SDavid Lebrun int i, err; 1464d1df6fd8SDavid Lebrun 1465d1df6fd8SDavid Lebrun desc = __get_action_desc(slwt->action); 1466d1df6fd8SDavid Lebrun if (!desc) 1467d1df6fd8SDavid Lebrun return -EINVAL; 1468d1df6fd8SDavid Lebrun 1469d1df6fd8SDavid Lebrun if (!desc->input) 1470d1df6fd8SDavid Lebrun return -EOPNOTSUPP; 1471d1df6fd8SDavid Lebrun 1472d1df6fd8SDavid Lebrun slwt->desc = desc; 1473d1df6fd8SDavid Lebrun slwt->headroom += desc->static_headroom; 1474d1df6fd8SDavid Lebrun 14750a3021f1SAndrea Mayer /* Forcing the desc->optattrs *set* and the desc->attrs *set* to be 14760a3021f1SAndrea Mayer * disjoined, this allow us to release acquired resources by optional 14770a3021f1SAndrea Mayer * attributes and by required attributes independently from each other 1478*0d770360SAndrea Mayer * without any interference. 14790a3021f1SAndrea Mayer * In other terms, we are sure that we do not release some the acquired 14800a3021f1SAndrea Mayer * resources twice. 14810a3021f1SAndrea Mayer * 14820a3021f1SAndrea Mayer * Note that if an attribute is configured both as required and as 14830a3021f1SAndrea Mayer * optional, it means that the user has messed something up in the 14840a3021f1SAndrea Mayer * seg6_action_table. Therefore, this check is required for SRv6 14850a3021f1SAndrea Mayer * behaviors to work properly. 14860a3021f1SAndrea Mayer */ 14870a3021f1SAndrea Mayer invalid_attrs = desc->attrs & desc->optattrs; 14880a3021f1SAndrea Mayer if (invalid_attrs) { 14890a3021f1SAndrea Mayer WARN_ONCE(1, 14900a3021f1SAndrea Mayer "An attribute cannot be both required AND optional"); 14910a3021f1SAndrea Mayer return -EINVAL; 14920a3021f1SAndrea Mayer } 14930a3021f1SAndrea Mayer 14940a3021f1SAndrea Mayer /* parse the required attributes */ 1495d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1496300a0fd8SAndrea Mayer if (desc->attrs & SEG6_F_ATTR(i)) { 1497d1df6fd8SDavid Lebrun if (!attrs[i]) 1498d1df6fd8SDavid Lebrun return -EINVAL; 1499d1df6fd8SDavid Lebrun 1500d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1501d1df6fd8SDavid Lebrun 1502d1df6fd8SDavid Lebrun err = param->parse(attrs, slwt); 1503d1df6fd8SDavid Lebrun if (err < 0) 15040a3021f1SAndrea Mayer goto parse_attrs_err; 1505d1df6fd8SDavid Lebrun } 1506d1df6fd8SDavid Lebrun } 1507d1df6fd8SDavid Lebrun 15080a3021f1SAndrea Mayer /* parse the optional attributes, if any */ 15090a3021f1SAndrea Mayer err = parse_nla_optional_attrs(attrs, slwt); 15100a3021f1SAndrea Mayer if (err < 0) 15110a3021f1SAndrea Mayer goto parse_attrs_err; 15120a3021f1SAndrea Mayer 1513d1df6fd8SDavid Lebrun return 0; 1514964adce5SAndrea Mayer 15150a3021f1SAndrea Mayer parse_attrs_err: 1516964adce5SAndrea Mayer /* release any resource that may have been acquired during the i-1 1517964adce5SAndrea Mayer * parse() operations. 1518964adce5SAndrea Mayer */ 15190a3021f1SAndrea Mayer __destroy_attrs(desc->attrs, i, slwt); 1520964adce5SAndrea Mayer 1521964adce5SAndrea Mayer return err; 1522d1df6fd8SDavid Lebrun } 1523d1df6fd8SDavid Lebrun 1524faee6769SAlexander Aring static int seg6_local_build_state(struct net *net, struct nlattr *nla, 1525faee6769SAlexander Aring unsigned int family, const void *cfg, 1526faee6769SAlexander Aring struct lwtunnel_state **ts, 1527d1df6fd8SDavid Lebrun struct netlink_ext_ack *extack) 1528d1df6fd8SDavid Lebrun { 1529d1df6fd8SDavid Lebrun struct nlattr *tb[SEG6_LOCAL_MAX + 1]; 1530d1df6fd8SDavid Lebrun struct lwtunnel_state *newts; 1531d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 1532d1df6fd8SDavid Lebrun int err; 1533d1df6fd8SDavid Lebrun 15346285217fSDavid Lebrun if (family != AF_INET6) 15356285217fSDavid Lebrun return -EINVAL; 15366285217fSDavid Lebrun 15378cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla, 15388cb08174SJohannes Berg seg6_local_policy, extack); 1539d1df6fd8SDavid Lebrun 1540d1df6fd8SDavid Lebrun if (err < 0) 1541d1df6fd8SDavid Lebrun return err; 1542d1df6fd8SDavid Lebrun 1543d1df6fd8SDavid Lebrun if (!tb[SEG6_LOCAL_ACTION]) 1544d1df6fd8SDavid Lebrun return -EINVAL; 1545d1df6fd8SDavid Lebrun 1546d1df6fd8SDavid Lebrun newts = lwtunnel_state_alloc(sizeof(*slwt)); 1547d1df6fd8SDavid Lebrun if (!newts) 1548d1df6fd8SDavid Lebrun return -ENOMEM; 1549d1df6fd8SDavid Lebrun 1550d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(newts); 1551d1df6fd8SDavid Lebrun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); 1552d1df6fd8SDavid Lebrun 1553d1df6fd8SDavid Lebrun err = parse_nla_action(tb, slwt); 1554d1df6fd8SDavid Lebrun if (err < 0) 1555d1df6fd8SDavid Lebrun goto out_free; 1556d1df6fd8SDavid Lebrun 1557cfdf64a0SAndrea Mayer err = seg6_local_lwtunnel_build_state(slwt, cfg, extack); 1558cfdf64a0SAndrea Mayer if (err < 0) 1559cfdf64a0SAndrea Mayer goto out_destroy_attrs; 1560cfdf64a0SAndrea Mayer 1561d1df6fd8SDavid Lebrun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; 1562d1df6fd8SDavid Lebrun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; 1563d1df6fd8SDavid Lebrun newts->headroom = slwt->headroom; 1564d1df6fd8SDavid Lebrun 1565d1df6fd8SDavid Lebrun *ts = newts; 1566d1df6fd8SDavid Lebrun 1567d1df6fd8SDavid Lebrun return 0; 1568d1df6fd8SDavid Lebrun 1569cfdf64a0SAndrea Mayer out_destroy_attrs: 1570cfdf64a0SAndrea Mayer destroy_attrs(slwt); 1571d1df6fd8SDavid Lebrun out_free: 1572d1df6fd8SDavid Lebrun kfree(newts); 1573d1df6fd8SDavid Lebrun return err; 1574d1df6fd8SDavid Lebrun } 1575d1df6fd8SDavid Lebrun 1576d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt) 1577d1df6fd8SDavid Lebrun { 1578d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1579d1df6fd8SDavid Lebrun 1580cfdf64a0SAndrea Mayer seg6_local_lwtunnel_destroy_state(slwt); 1581cfdf64a0SAndrea Mayer 1582964adce5SAndrea Mayer destroy_attrs(slwt); 1583004d4b27SMathieu Xhonneux 1584004d4b27SMathieu Xhonneux return; 1585d1df6fd8SDavid Lebrun } 1586d1df6fd8SDavid Lebrun 1587d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb, 1588d1df6fd8SDavid Lebrun struct lwtunnel_state *lwt) 1589d1df6fd8SDavid Lebrun { 1590d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1591d1df6fd8SDavid Lebrun struct seg6_action_param *param; 15920a3021f1SAndrea Mayer unsigned long attrs; 1593d1df6fd8SDavid Lebrun int i, err; 1594d1df6fd8SDavid Lebrun 1595d1df6fd8SDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) 1596d1df6fd8SDavid Lebrun return -EMSGSIZE; 1597d1df6fd8SDavid Lebrun 15980a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 15990a3021f1SAndrea Mayer 1600d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1601300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(i)) { 1602d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1603d1df6fd8SDavid Lebrun err = param->put(skb, slwt); 1604d1df6fd8SDavid Lebrun if (err < 0) 1605d1df6fd8SDavid Lebrun return err; 1606d1df6fd8SDavid Lebrun } 1607d1df6fd8SDavid Lebrun } 1608d1df6fd8SDavid Lebrun 1609d1df6fd8SDavid Lebrun return 0; 1610d1df6fd8SDavid Lebrun } 1611d1df6fd8SDavid Lebrun 1612d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) 1613d1df6fd8SDavid Lebrun { 1614d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1615d1df6fd8SDavid Lebrun unsigned long attrs; 1616d1df6fd8SDavid Lebrun int nlsize; 1617d1df6fd8SDavid Lebrun 1618d1df6fd8SDavid Lebrun nlsize = nla_total_size(4); /* action */ 1619d1df6fd8SDavid Lebrun 16200a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 1621d1df6fd8SDavid Lebrun 1622300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_SRH)) 1623d1df6fd8SDavid Lebrun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); 1624d1df6fd8SDavid Lebrun 1625300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_TABLE)) 1626d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1627d1df6fd8SDavid Lebrun 1628300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH4)) 1629d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1630d1df6fd8SDavid Lebrun 1631300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_NH6)) 1632d1df6fd8SDavid Lebrun nlsize += nla_total_size(16); 1633d1df6fd8SDavid Lebrun 1634300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_IIF)) 1635d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1636d1df6fd8SDavid Lebrun 1637300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_OIF)) 1638d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1639d1df6fd8SDavid Lebrun 1640300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_BPF)) 1641004d4b27SMathieu Xhonneux nlsize += nla_total_size(sizeof(struct nlattr)) + 1642004d4b27SMathieu Xhonneux nla_total_size(MAX_PROG_NAME) + 1643004d4b27SMathieu Xhonneux nla_total_size(4); 1644004d4b27SMathieu Xhonneux 1645300a0fd8SAndrea Mayer if (attrs & SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE)) 1646664d6f86SAndrea Mayer nlsize += nla_total_size(4); 1647664d6f86SAndrea Mayer 1648d1df6fd8SDavid Lebrun return nlsize; 1649d1df6fd8SDavid Lebrun } 1650d1df6fd8SDavid Lebrun 1651d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a, 1652d1df6fd8SDavid Lebrun struct lwtunnel_state *b) 1653d1df6fd8SDavid Lebrun { 1654d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt_a, *slwt_b; 1655d1df6fd8SDavid Lebrun struct seg6_action_param *param; 16560a3021f1SAndrea Mayer unsigned long attrs_a, attrs_b; 1657d1df6fd8SDavid Lebrun int i; 1658d1df6fd8SDavid Lebrun 1659d1df6fd8SDavid Lebrun slwt_a = seg6_local_lwtunnel(a); 1660d1df6fd8SDavid Lebrun slwt_b = seg6_local_lwtunnel(b); 1661d1df6fd8SDavid Lebrun 1662d1df6fd8SDavid Lebrun if (slwt_a->action != slwt_b->action) 1663d1df6fd8SDavid Lebrun return 1; 1664d1df6fd8SDavid Lebrun 16650a3021f1SAndrea Mayer attrs_a = slwt_a->desc->attrs | slwt_a->parsed_optattrs; 16660a3021f1SAndrea Mayer attrs_b = slwt_b->desc->attrs | slwt_b->parsed_optattrs; 16670a3021f1SAndrea Mayer 16680a3021f1SAndrea Mayer if (attrs_a != attrs_b) 1669d1df6fd8SDavid Lebrun return 1; 1670d1df6fd8SDavid Lebrun 1671d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1672300a0fd8SAndrea Mayer if (attrs_a & SEG6_F_ATTR(i)) { 1673d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1674d1df6fd8SDavid Lebrun if (param->cmp(slwt_a, slwt_b)) 1675d1df6fd8SDavid Lebrun return 1; 1676d1df6fd8SDavid Lebrun } 1677d1df6fd8SDavid Lebrun } 1678d1df6fd8SDavid Lebrun 1679d1df6fd8SDavid Lebrun return 0; 1680d1df6fd8SDavid Lebrun } 1681d1df6fd8SDavid Lebrun 1682d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = { 1683d1df6fd8SDavid Lebrun .build_state = seg6_local_build_state, 1684d1df6fd8SDavid Lebrun .destroy_state = seg6_local_destroy_state, 1685d1df6fd8SDavid Lebrun .input = seg6_local_input, 1686d1df6fd8SDavid Lebrun .fill_encap = seg6_local_fill_encap, 1687d1df6fd8SDavid Lebrun .get_encap_size = seg6_local_get_encap_size, 1688d1df6fd8SDavid Lebrun .cmp_encap = seg6_local_cmp_encap, 1689d1df6fd8SDavid Lebrun .owner = THIS_MODULE, 1690d1df6fd8SDavid Lebrun }; 1691d1df6fd8SDavid Lebrun 1692d1df6fd8SDavid Lebrun int __init seg6_local_init(void) 1693d1df6fd8SDavid Lebrun { 1694300a0fd8SAndrea Mayer /* If the max total number of defined attributes is reached, then your 1695300a0fd8SAndrea Mayer * kernel build stops here. 1696300a0fd8SAndrea Mayer * 1697300a0fd8SAndrea Mayer * This check is required to avoid arithmetic overflows when processing 1698300a0fd8SAndrea Mayer * behavior attributes and the maximum number of defined attributes 1699300a0fd8SAndrea Mayer * exceeds the allowed value. 1700300a0fd8SAndrea Mayer */ 1701300a0fd8SAndrea Mayer BUILD_BUG_ON(SEG6_LOCAL_MAX + 1 > BITS_PER_TYPE(unsigned long)); 1702300a0fd8SAndrea Mayer 1703d1df6fd8SDavid Lebrun return lwtunnel_encap_add_ops(&seg6_local_ops, 1704d1df6fd8SDavid Lebrun LWTUNNEL_ENCAP_SEG6_LOCAL); 1705d1df6fd8SDavid Lebrun } 1706d1df6fd8SDavid Lebrun 1707d1df6fd8SDavid Lebrun void seg6_local_exit(void) 1708d1df6fd8SDavid Lebrun { 1709d1df6fd8SDavid Lebrun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); 1710d1df6fd8SDavid Lebrun } 1711