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 34d1df6fd8SDavid Lebrun struct seg6_local_lwt; 35d1df6fd8SDavid Lebrun 36d1df6fd8SDavid Lebrun struct seg6_action_desc { 37d1df6fd8SDavid Lebrun int action; 38d1df6fd8SDavid Lebrun unsigned long attrs; 39*0a3021f1SAndrea Mayer 40*0a3021f1SAndrea Mayer /* The optattrs field is used for specifying all the optional 41*0a3021f1SAndrea Mayer * attributes supported by a specific behavior. 42*0a3021f1SAndrea Mayer * It means that if one of these attributes is not provided in the 43*0a3021f1SAndrea Mayer * netlink message during the behavior creation, no errors will be 44*0a3021f1SAndrea Mayer * returned to the userspace. 45*0a3021f1SAndrea Mayer * 46*0a3021f1SAndrea Mayer * Each attribute can be only of two types (mutually exclusive): 47*0a3021f1SAndrea Mayer * 1) required or 2) optional. 48*0a3021f1SAndrea Mayer * Every user MUST obey to this rule! If you set an attribute as 49*0a3021f1SAndrea Mayer * required the same attribute CANNOT be set as optional and vice 50*0a3021f1SAndrea Mayer * versa. 51*0a3021f1SAndrea Mayer */ 52*0a3021f1SAndrea Mayer unsigned long optattrs; 53*0a3021f1SAndrea Mayer 54d1df6fd8SDavid Lebrun int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 55d1df6fd8SDavid Lebrun int static_headroom; 56d1df6fd8SDavid Lebrun }; 57d1df6fd8SDavid Lebrun 58004d4b27SMathieu Xhonneux struct bpf_lwt_prog { 59004d4b27SMathieu Xhonneux struct bpf_prog *prog; 60004d4b27SMathieu Xhonneux char *name; 61004d4b27SMathieu Xhonneux }; 62004d4b27SMathieu Xhonneux 63d1df6fd8SDavid Lebrun struct seg6_local_lwt { 64d1df6fd8SDavid Lebrun int action; 65d1df6fd8SDavid Lebrun struct ipv6_sr_hdr *srh; 66d1df6fd8SDavid Lebrun int table; 67d1df6fd8SDavid Lebrun struct in_addr nh4; 68d1df6fd8SDavid Lebrun struct in6_addr nh6; 69d1df6fd8SDavid Lebrun int iif; 70d1df6fd8SDavid Lebrun int oif; 71004d4b27SMathieu Xhonneux struct bpf_lwt_prog bpf; 72d1df6fd8SDavid Lebrun 73d1df6fd8SDavid Lebrun int headroom; 74d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 75*0a3021f1SAndrea Mayer /* unlike the required attrs, we have to track the optional attributes 76*0a3021f1SAndrea Mayer * that have been effectively parsed. 77*0a3021f1SAndrea Mayer */ 78*0a3021f1SAndrea Mayer unsigned long parsed_optattrs; 79d1df6fd8SDavid Lebrun }; 80d1df6fd8SDavid Lebrun 81d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) 82d1df6fd8SDavid Lebrun { 83d1df6fd8SDavid Lebrun return (struct seg6_local_lwt *)lwt->data; 84d1df6fd8SDavid Lebrun } 85d1df6fd8SDavid Lebrun 86140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb) 87140f04c3SDavid Lebrun { 88140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 895829d70bSAhmed Abdelsalam int len, srhoff = 0; 90140f04c3SDavid Lebrun 915829d70bSAhmed Abdelsalam if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0) 92140f04c3SDavid Lebrun return NULL; 93140f04c3SDavid Lebrun 945829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + sizeof(*srh))) 955829d70bSAhmed Abdelsalam return NULL; 965829d70bSAhmed Abdelsalam 975829d70bSAhmed Abdelsalam srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 985829d70bSAhmed Abdelsalam 99140f04c3SDavid Lebrun len = (srh->hdrlen + 1) << 3; 100140f04c3SDavid Lebrun 1015829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + len)) 102140f04c3SDavid Lebrun return NULL; 103140f04c3SDavid Lebrun 1047f91ed8cSAndrea Mayer /* note that pskb_may_pull may change pointers in header; 1057f91ed8cSAndrea Mayer * for this reason it is necessary to reload them when needed. 1067f91ed8cSAndrea Mayer */ 1077f91ed8cSAndrea Mayer srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 1087f91ed8cSAndrea Mayer 109bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, true)) 110140f04c3SDavid Lebrun return NULL; 111140f04c3SDavid Lebrun 112140f04c3SDavid Lebrun return srh; 113140f04c3SDavid Lebrun } 114140f04c3SDavid Lebrun 115140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) 116140f04c3SDavid Lebrun { 117140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 118140f04c3SDavid Lebrun 119140f04c3SDavid Lebrun srh = get_srh(skb); 120140f04c3SDavid Lebrun if (!srh) 121140f04c3SDavid Lebrun return NULL; 122140f04c3SDavid Lebrun 123140f04c3SDavid Lebrun if (srh->segments_left == 0) 124140f04c3SDavid Lebrun return NULL; 125140f04c3SDavid Lebrun 126140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 127140f04c3SDavid Lebrun if (!seg6_hmac_validate_skb(skb)) 128140f04c3SDavid Lebrun return NULL; 129140f04c3SDavid Lebrun #endif 130140f04c3SDavid Lebrun 131140f04c3SDavid Lebrun return srh; 132140f04c3SDavid Lebrun } 133140f04c3SDavid Lebrun 134d7a669ddSDavid Lebrun static bool decap_and_validate(struct sk_buff *skb, int proto) 135d7a669ddSDavid Lebrun { 136d7a669ddSDavid Lebrun struct ipv6_sr_hdr *srh; 137d7a669ddSDavid Lebrun unsigned int off = 0; 138d7a669ddSDavid Lebrun 139d7a669ddSDavid Lebrun srh = get_srh(skb); 140d7a669ddSDavid Lebrun if (srh && srh->segments_left > 0) 141d7a669ddSDavid Lebrun return false; 142d7a669ddSDavid Lebrun 143d7a669ddSDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 144d7a669ddSDavid Lebrun if (srh && !seg6_hmac_validate_skb(skb)) 145d7a669ddSDavid Lebrun return false; 146d7a669ddSDavid Lebrun #endif 147d7a669ddSDavid Lebrun 148d7a669ddSDavid Lebrun if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0) 149d7a669ddSDavid Lebrun return false; 150d7a669ddSDavid Lebrun 151d7a669ddSDavid Lebrun if (!pskb_pull(skb, off)) 152d7a669ddSDavid Lebrun return false; 153d7a669ddSDavid Lebrun 154d7a669ddSDavid Lebrun skb_postpull_rcsum(skb, skb_network_header(skb), off); 155d7a669ddSDavid Lebrun 156d7a669ddSDavid Lebrun skb_reset_network_header(skb); 157d7a669ddSDavid Lebrun skb_reset_transport_header(skb); 15862ebaeaeSYuki Taguchi if (iptunnel_pull_offloads(skb)) 15962ebaeaeSYuki Taguchi return false; 160d7a669ddSDavid Lebrun 161d7a669ddSDavid Lebrun return true; 162d7a669ddSDavid Lebrun } 163d7a669ddSDavid Lebrun 164d7a669ddSDavid Lebrun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr) 165d7a669ddSDavid Lebrun { 166d7a669ddSDavid Lebrun struct in6_addr *addr; 167d7a669ddSDavid Lebrun 168d7a669ddSDavid Lebrun srh->segments_left--; 169d7a669ddSDavid Lebrun addr = srh->segments + srh->segments_left; 170d7a669ddSDavid Lebrun *daddr = *addr; 171d7a669ddSDavid Lebrun } 172d7a669ddSDavid Lebrun 173fd1fef0cSAndrea Mayer static int 174fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr, 175fd1fef0cSAndrea Mayer u32 tbl_id, bool local_delivery) 176d7a669ddSDavid Lebrun { 177d7a669ddSDavid Lebrun struct net *net = dev_net(skb->dev); 178d7a669ddSDavid Lebrun struct ipv6hdr *hdr = ipv6_hdr(skb); 179d7a669ddSDavid Lebrun int flags = RT6_LOOKUP_F_HAS_SADDR; 180d7a669ddSDavid Lebrun struct dst_entry *dst = NULL; 181d7a669ddSDavid Lebrun struct rt6_info *rt; 182d7a669ddSDavid Lebrun struct flowi6 fl6; 183fd1fef0cSAndrea Mayer int dev_flags = 0; 184d7a669ddSDavid Lebrun 185d7a669ddSDavid Lebrun fl6.flowi6_iif = skb->dev->ifindex; 186d7a669ddSDavid Lebrun fl6.daddr = nhaddr ? *nhaddr : hdr->daddr; 187d7a669ddSDavid Lebrun fl6.saddr = hdr->saddr; 188d7a669ddSDavid Lebrun fl6.flowlabel = ip6_flowinfo(hdr); 189d7a669ddSDavid Lebrun fl6.flowi6_mark = skb->mark; 190d7a669ddSDavid Lebrun fl6.flowi6_proto = hdr->nexthdr; 191d7a669ddSDavid Lebrun 192d7a669ddSDavid Lebrun if (nhaddr) 193d7a669ddSDavid Lebrun fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH; 194d7a669ddSDavid Lebrun 195d7a669ddSDavid Lebrun if (!tbl_id) { 196b75cc8f9SDavid Ahern dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags); 197d7a669ddSDavid Lebrun } else { 198d7a669ddSDavid Lebrun struct fib6_table *table; 199d7a669ddSDavid Lebrun 200d7a669ddSDavid Lebrun table = fib6_get_table(net, tbl_id); 201d7a669ddSDavid Lebrun if (!table) 202d7a669ddSDavid Lebrun goto out; 203d7a669ddSDavid Lebrun 204b75cc8f9SDavid Ahern rt = ip6_pol_route(net, table, 0, &fl6, skb, flags); 205d7a669ddSDavid Lebrun dst = &rt->dst; 206d7a669ddSDavid Lebrun } 207d7a669ddSDavid Lebrun 208fd1fef0cSAndrea Mayer /* we want to discard traffic destined for local packet processing, 209fd1fef0cSAndrea Mayer * if @local_delivery is set to false. 210fd1fef0cSAndrea Mayer */ 211fd1fef0cSAndrea Mayer if (!local_delivery) 212fd1fef0cSAndrea Mayer dev_flags |= IFF_LOOPBACK; 213fd1fef0cSAndrea Mayer 214fd1fef0cSAndrea Mayer if (dst && (dst->dev->flags & dev_flags) && !dst->error) { 215d7a669ddSDavid Lebrun dst_release(dst); 216d7a669ddSDavid Lebrun dst = NULL; 217d7a669ddSDavid Lebrun } 218d7a669ddSDavid Lebrun 219d7a669ddSDavid Lebrun out: 220d7a669ddSDavid Lebrun if (!dst) { 221d7a669ddSDavid Lebrun rt = net->ipv6.ip6_blk_hole_entry; 222d7a669ddSDavid Lebrun dst = &rt->dst; 223d7a669ddSDavid Lebrun dst_hold(dst); 224d7a669ddSDavid Lebrun } 225d7a669ddSDavid Lebrun 226d7a669ddSDavid Lebrun skb_dst_drop(skb); 227d7a669ddSDavid Lebrun skb_dst_set(skb, dst); 2281c1e761eSMathieu Xhonneux return dst->error; 229d7a669ddSDavid Lebrun } 230d7a669ddSDavid Lebrun 231fd1fef0cSAndrea Mayer int seg6_lookup_nexthop(struct sk_buff *skb, 232fd1fef0cSAndrea Mayer struct in6_addr *nhaddr, u32 tbl_id) 233fd1fef0cSAndrea Mayer { 234fd1fef0cSAndrea Mayer return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false); 235fd1fef0cSAndrea Mayer } 236fd1fef0cSAndrea Mayer 237140f04c3SDavid Lebrun /* regular endpoint function */ 238140f04c3SDavid Lebrun static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 239140f04c3SDavid Lebrun { 240140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 241140f04c3SDavid Lebrun 242140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 243140f04c3SDavid Lebrun if (!srh) 244140f04c3SDavid Lebrun goto drop; 245140f04c3SDavid Lebrun 246d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 247140f04c3SDavid Lebrun 2481c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 249140f04c3SDavid Lebrun 250140f04c3SDavid Lebrun return dst_input(skb); 251140f04c3SDavid Lebrun 252140f04c3SDavid Lebrun drop: 253140f04c3SDavid Lebrun kfree_skb(skb); 254140f04c3SDavid Lebrun return -EINVAL; 255140f04c3SDavid Lebrun } 256140f04c3SDavid Lebrun 257140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */ 258140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) 259140f04c3SDavid Lebrun { 260140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 261140f04c3SDavid Lebrun 262140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 263140f04c3SDavid Lebrun if (!srh) 264140f04c3SDavid Lebrun goto drop; 265140f04c3SDavid Lebrun 266d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 267140f04c3SDavid Lebrun 2681c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, &slwt->nh6, 0); 269140f04c3SDavid Lebrun 270140f04c3SDavid Lebrun return dst_input(skb); 271140f04c3SDavid Lebrun 272140f04c3SDavid Lebrun drop: 273140f04c3SDavid Lebrun kfree_skb(skb); 274140f04c3SDavid Lebrun return -EINVAL; 275140f04c3SDavid Lebrun } 276140f04c3SDavid Lebrun 277891ef8ddSDavid Lebrun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) 278891ef8ddSDavid Lebrun { 279891ef8ddSDavid Lebrun struct ipv6_sr_hdr *srh; 280891ef8ddSDavid Lebrun 281891ef8ddSDavid Lebrun srh = get_and_validate_srh(skb); 282891ef8ddSDavid Lebrun if (!srh) 283891ef8ddSDavid Lebrun goto drop; 284891ef8ddSDavid Lebrun 285891ef8ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 286891ef8ddSDavid Lebrun 2871c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, slwt->table); 288891ef8ddSDavid Lebrun 289891ef8ddSDavid Lebrun return dst_input(skb); 290891ef8ddSDavid Lebrun 291891ef8ddSDavid Lebrun drop: 292891ef8ddSDavid Lebrun kfree_skb(skb); 293891ef8ddSDavid Lebrun return -EINVAL; 294891ef8ddSDavid Lebrun } 295891ef8ddSDavid Lebrun 296891ef8ddSDavid Lebrun /* decapsulate and forward inner L2 frame on specified interface */ 297891ef8ddSDavid Lebrun static int input_action_end_dx2(struct sk_buff *skb, 298891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 299891ef8ddSDavid Lebrun { 300891ef8ddSDavid Lebrun struct net *net = dev_net(skb->dev); 301891ef8ddSDavid Lebrun struct net_device *odev; 302891ef8ddSDavid Lebrun struct ethhdr *eth; 303891ef8ddSDavid Lebrun 30426776253SPaolo Lungaroni if (!decap_and_validate(skb, IPPROTO_ETHERNET)) 305891ef8ddSDavid Lebrun goto drop; 306891ef8ddSDavid Lebrun 307891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, ETH_HLEN)) 308891ef8ddSDavid Lebrun goto drop; 309891ef8ddSDavid Lebrun 310891ef8ddSDavid Lebrun skb_reset_mac_header(skb); 311891ef8ddSDavid Lebrun eth = (struct ethhdr *)skb->data; 312891ef8ddSDavid Lebrun 313891ef8ddSDavid Lebrun /* To determine the frame's protocol, we assume it is 802.3. This avoids 314891ef8ddSDavid Lebrun * a call to eth_type_trans(), which is not really relevant for our 315891ef8ddSDavid Lebrun * use case. 316891ef8ddSDavid Lebrun */ 317891ef8ddSDavid Lebrun if (!eth_proto_is_802_3(eth->h_proto)) 318891ef8ddSDavid Lebrun goto drop; 319891ef8ddSDavid Lebrun 320891ef8ddSDavid Lebrun odev = dev_get_by_index_rcu(net, slwt->oif); 321891ef8ddSDavid Lebrun if (!odev) 322891ef8ddSDavid Lebrun goto drop; 323891ef8ddSDavid Lebrun 324891ef8ddSDavid Lebrun /* As we accept Ethernet frames, make sure the egress device is of 325891ef8ddSDavid Lebrun * the correct type. 326891ef8ddSDavid Lebrun */ 327891ef8ddSDavid Lebrun if (odev->type != ARPHRD_ETHER) 328891ef8ddSDavid Lebrun goto drop; 329891ef8ddSDavid Lebrun 330891ef8ddSDavid Lebrun if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev)) 331891ef8ddSDavid Lebrun goto drop; 332891ef8ddSDavid Lebrun 333891ef8ddSDavid Lebrun skb_orphan(skb); 334891ef8ddSDavid Lebrun 335891ef8ddSDavid Lebrun if (skb_warn_if_lro(skb)) 336891ef8ddSDavid Lebrun goto drop; 337891ef8ddSDavid Lebrun 338891ef8ddSDavid Lebrun skb_forward_csum(skb); 339891ef8ddSDavid Lebrun 340891ef8ddSDavid Lebrun if (skb->len - ETH_HLEN > odev->mtu) 341891ef8ddSDavid Lebrun goto drop; 342891ef8ddSDavid Lebrun 343891ef8ddSDavid Lebrun skb->dev = odev; 344891ef8ddSDavid Lebrun skb->protocol = eth->h_proto; 345891ef8ddSDavid Lebrun 346891ef8ddSDavid Lebrun return dev_queue_xmit(skb); 347891ef8ddSDavid Lebrun 348891ef8ddSDavid Lebrun drop: 349891ef8ddSDavid Lebrun kfree_skb(skb); 350891ef8ddSDavid Lebrun return -EINVAL; 351891ef8ddSDavid Lebrun } 352891ef8ddSDavid Lebrun 353140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */ 354140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb, 355140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 356140f04c3SDavid Lebrun { 357d7a669ddSDavid Lebrun struct in6_addr *nhaddr = NULL; 358140f04c3SDavid Lebrun 359140f04c3SDavid Lebrun /* this function accepts IPv6 encapsulated packets, with either 360140f04c3SDavid Lebrun * an SRH with SL=0, or no SRH. 361140f04c3SDavid Lebrun */ 362140f04c3SDavid Lebrun 363d7a669ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 364140f04c3SDavid Lebrun goto drop; 365140f04c3SDavid Lebrun 366d7a669ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 367140f04c3SDavid Lebrun goto drop; 368140f04c3SDavid Lebrun 369140f04c3SDavid Lebrun /* The inner packet is not associated to any local interface, 370140f04c3SDavid Lebrun * so we do not call netif_rx(). 371140f04c3SDavid Lebrun * 372140f04c3SDavid Lebrun * If slwt->nh6 is set to ::, then lookup the nexthop for the 373140f04c3SDavid Lebrun * inner packet's DA. Otherwise, use the specified nexthop. 374140f04c3SDavid Lebrun */ 375140f04c3SDavid Lebrun 376d7a669ddSDavid Lebrun if (!ipv6_addr_any(&slwt->nh6)) 377d7a669ddSDavid Lebrun nhaddr = &slwt->nh6; 378140f04c3SDavid Lebrun 379c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 380c71644d0SAndrea Mayer 3811c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, nhaddr, 0); 382140f04c3SDavid Lebrun 383140f04c3SDavid Lebrun return dst_input(skb); 384140f04c3SDavid Lebrun drop: 385140f04c3SDavid Lebrun kfree_skb(skb); 386140f04c3SDavid Lebrun return -EINVAL; 387140f04c3SDavid Lebrun } 388140f04c3SDavid Lebrun 389891ef8ddSDavid Lebrun static int input_action_end_dx4(struct sk_buff *skb, 390891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 391891ef8ddSDavid Lebrun { 392891ef8ddSDavid Lebrun struct iphdr *iph; 393891ef8ddSDavid Lebrun __be32 nhaddr; 394891ef8ddSDavid Lebrun int err; 395891ef8ddSDavid Lebrun 396891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPIP)) 397891ef8ddSDavid Lebrun goto drop; 398891ef8ddSDavid Lebrun 399891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct iphdr))) 400891ef8ddSDavid Lebrun goto drop; 401891ef8ddSDavid Lebrun 402891ef8ddSDavid Lebrun skb->protocol = htons(ETH_P_IP); 403891ef8ddSDavid Lebrun 404891ef8ddSDavid Lebrun iph = ip_hdr(skb); 405891ef8ddSDavid Lebrun 406891ef8ddSDavid Lebrun nhaddr = slwt->nh4.s_addr ?: iph->daddr; 407891ef8ddSDavid Lebrun 408891ef8ddSDavid Lebrun skb_dst_drop(skb); 409891ef8ddSDavid Lebrun 410c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct iphdr)); 411c71644d0SAndrea Mayer 412891ef8ddSDavid Lebrun err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev); 413891ef8ddSDavid Lebrun if (err) 414891ef8ddSDavid Lebrun goto drop; 415891ef8ddSDavid Lebrun 416891ef8ddSDavid Lebrun return dst_input(skb); 417891ef8ddSDavid Lebrun 418891ef8ddSDavid Lebrun drop: 419891ef8ddSDavid Lebrun kfree_skb(skb); 420891ef8ddSDavid Lebrun return -EINVAL; 421891ef8ddSDavid Lebrun } 422891ef8ddSDavid Lebrun 423891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb, 424891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 425891ef8ddSDavid Lebrun { 426891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 427891ef8ddSDavid Lebrun goto drop; 428891ef8ddSDavid Lebrun 429891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 430891ef8ddSDavid Lebrun goto drop; 431891ef8ddSDavid Lebrun 432c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 433c71644d0SAndrea Mayer 434fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, slwt->table, true); 435891ef8ddSDavid Lebrun 436891ef8ddSDavid Lebrun return dst_input(skb); 437891ef8ddSDavid Lebrun 438891ef8ddSDavid Lebrun drop: 439891ef8ddSDavid Lebrun kfree_skb(skb); 440891ef8ddSDavid Lebrun return -EINVAL; 441891ef8ddSDavid Lebrun } 442891ef8ddSDavid Lebrun 443140f04c3SDavid Lebrun /* push an SRH on top of the current one */ 444140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 445140f04c3SDavid Lebrun { 446140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 447140f04c3SDavid Lebrun int err = -EINVAL; 448140f04c3SDavid Lebrun 449140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 450140f04c3SDavid Lebrun if (!srh) 451140f04c3SDavid Lebrun goto drop; 452140f04c3SDavid Lebrun 453140f04c3SDavid Lebrun err = seg6_do_srh_inline(skb, slwt->srh); 454140f04c3SDavid Lebrun if (err) 455140f04c3SDavid Lebrun goto drop; 456140f04c3SDavid Lebrun 457140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 458140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 459140f04c3SDavid Lebrun 4601c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 461140f04c3SDavid Lebrun 462140f04c3SDavid Lebrun return dst_input(skb); 463140f04c3SDavid Lebrun 464140f04c3SDavid Lebrun drop: 465140f04c3SDavid Lebrun kfree_skb(skb); 466140f04c3SDavid Lebrun return err; 467140f04c3SDavid Lebrun } 468140f04c3SDavid Lebrun 469140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */ 470140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb, 471140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 472140f04c3SDavid Lebrun { 473140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 474140f04c3SDavid Lebrun int err = -EINVAL; 475140f04c3SDavid Lebrun 476140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 477140f04c3SDavid Lebrun if (!srh) 478140f04c3SDavid Lebrun goto drop; 479140f04c3SDavid Lebrun 480d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 481140f04c3SDavid Lebrun 482140f04c3SDavid Lebrun skb_reset_inner_headers(skb); 483140f04c3SDavid Lebrun skb->encapsulation = 1; 484140f04c3SDavid Lebrun 48532d99d0bSDavid Lebrun err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6); 486140f04c3SDavid Lebrun if (err) 487140f04c3SDavid Lebrun goto drop; 488140f04c3SDavid Lebrun 489140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 490140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 491140f04c3SDavid Lebrun 4921c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 493140f04c3SDavid Lebrun 494140f04c3SDavid Lebrun return dst_input(skb); 495140f04c3SDavid Lebrun 496140f04c3SDavid Lebrun drop: 497140f04c3SDavid Lebrun kfree_skb(skb); 498140f04c3SDavid Lebrun return err; 499140f04c3SDavid Lebrun } 500140f04c3SDavid Lebrun 501fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states); 502fe94cc29SMathieu Xhonneux 503486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb) 504486cdf21SMathieu Xhonneux { 505486cdf21SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 506486cdf21SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 507486cdf21SMathieu Xhonneux struct ipv6_sr_hdr *srh = srh_state->srh; 508486cdf21SMathieu Xhonneux 509486cdf21SMathieu Xhonneux if (unlikely(srh == NULL)) 510486cdf21SMathieu Xhonneux return false; 511486cdf21SMathieu Xhonneux 512486cdf21SMathieu Xhonneux if (unlikely(!srh_state->valid)) { 513486cdf21SMathieu Xhonneux if ((srh_state->hdrlen & 7) != 0) 514486cdf21SMathieu Xhonneux return false; 515486cdf21SMathieu Xhonneux 516486cdf21SMathieu Xhonneux srh->hdrlen = (u8)(srh_state->hdrlen >> 3); 517bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true)) 518486cdf21SMathieu Xhonneux return false; 519486cdf21SMathieu Xhonneux 520486cdf21SMathieu Xhonneux srh_state->valid = true; 521486cdf21SMathieu Xhonneux } 522486cdf21SMathieu Xhonneux 523486cdf21SMathieu Xhonneux return true; 524486cdf21SMathieu Xhonneux } 525486cdf21SMathieu Xhonneux 526004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb, 527004d4b27SMathieu Xhonneux struct seg6_local_lwt *slwt) 528004d4b27SMathieu Xhonneux { 529004d4b27SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 530004d4b27SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 531004d4b27SMathieu Xhonneux struct ipv6_sr_hdr *srh; 532004d4b27SMathieu Xhonneux int ret; 533004d4b27SMathieu Xhonneux 534004d4b27SMathieu Xhonneux srh = get_and_validate_srh(skb); 535486cdf21SMathieu Xhonneux if (!srh) { 536486cdf21SMathieu Xhonneux kfree_skb(skb); 537486cdf21SMathieu Xhonneux return -EINVAL; 538486cdf21SMathieu Xhonneux } 539004d4b27SMathieu Xhonneux advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 540004d4b27SMathieu Xhonneux 541004d4b27SMathieu Xhonneux /* preempt_disable is needed to protect the per-CPU buffer srh_state, 542004d4b27SMathieu Xhonneux * which is also accessed by the bpf_lwt_seg6_* helpers 543004d4b27SMathieu Xhonneux */ 544004d4b27SMathieu Xhonneux preempt_disable(); 545486cdf21SMathieu Xhonneux srh_state->srh = srh; 546004d4b27SMathieu Xhonneux srh_state->hdrlen = srh->hdrlen << 3; 547486cdf21SMathieu Xhonneux srh_state->valid = true; 548004d4b27SMathieu Xhonneux 549004d4b27SMathieu Xhonneux rcu_read_lock(); 550004d4b27SMathieu Xhonneux bpf_compute_data_pointers(skb); 551004d4b27SMathieu Xhonneux ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb); 552004d4b27SMathieu Xhonneux rcu_read_unlock(); 553004d4b27SMathieu Xhonneux 554004d4b27SMathieu Xhonneux switch (ret) { 555004d4b27SMathieu Xhonneux case BPF_OK: 556004d4b27SMathieu Xhonneux case BPF_REDIRECT: 557004d4b27SMathieu Xhonneux break; 558004d4b27SMathieu Xhonneux case BPF_DROP: 559004d4b27SMathieu Xhonneux goto drop; 560004d4b27SMathieu Xhonneux default: 561004d4b27SMathieu Xhonneux pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret); 562004d4b27SMathieu Xhonneux goto drop; 563004d4b27SMathieu Xhonneux } 564004d4b27SMathieu Xhonneux 565486cdf21SMathieu Xhonneux if (srh_state->srh && !seg6_bpf_has_valid_srh(skb)) 566004d4b27SMathieu Xhonneux goto drop; 567004d4b27SMathieu Xhonneux 568486cdf21SMathieu Xhonneux preempt_enable(); 569004d4b27SMathieu Xhonneux if (ret != BPF_REDIRECT) 570004d4b27SMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 571004d4b27SMathieu Xhonneux 572004d4b27SMathieu Xhonneux return dst_input(skb); 573004d4b27SMathieu Xhonneux 574004d4b27SMathieu Xhonneux drop: 575486cdf21SMathieu Xhonneux preempt_enable(); 576004d4b27SMathieu Xhonneux kfree_skb(skb); 577004d4b27SMathieu Xhonneux return -EINVAL; 578004d4b27SMathieu Xhonneux } 579004d4b27SMathieu Xhonneux 580d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = { 581d1df6fd8SDavid Lebrun { 582d1df6fd8SDavid Lebrun .action = SEG6_LOCAL_ACTION_END, 583d1df6fd8SDavid Lebrun .attrs = 0, 584140f04c3SDavid Lebrun .input = input_action_end, 585d1df6fd8SDavid Lebrun }, 586140f04c3SDavid Lebrun { 587140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_X, 588140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH6), 589140f04c3SDavid Lebrun .input = input_action_end_x, 590140f04c3SDavid Lebrun }, 591140f04c3SDavid Lebrun { 592891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_T, 593891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_TABLE), 594891ef8ddSDavid Lebrun .input = input_action_end_t, 595891ef8ddSDavid Lebrun }, 596891ef8ddSDavid Lebrun { 597891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX2, 598891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_OIF), 599891ef8ddSDavid Lebrun .input = input_action_end_dx2, 600891ef8ddSDavid Lebrun }, 601891ef8ddSDavid Lebrun { 602140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX6, 603140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH6), 604140f04c3SDavid Lebrun .input = input_action_end_dx6, 605140f04c3SDavid Lebrun }, 606140f04c3SDavid Lebrun { 607891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX4, 608891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH4), 609891ef8ddSDavid Lebrun .input = input_action_end_dx4, 610891ef8ddSDavid Lebrun }, 611891ef8ddSDavid Lebrun { 612891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DT6, 613891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_TABLE), 614891ef8ddSDavid Lebrun .input = input_action_end_dt6, 615891ef8ddSDavid Lebrun }, 616891ef8ddSDavid Lebrun { 617140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6, 618140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_SRH), 619140f04c3SDavid Lebrun .input = input_action_end_b6, 620140f04c3SDavid Lebrun }, 621140f04c3SDavid Lebrun { 622140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6_ENCAP, 623140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_SRH), 624140f04c3SDavid Lebrun .input = input_action_end_b6_encap, 625140f04c3SDavid Lebrun .static_headroom = sizeof(struct ipv6hdr), 626004d4b27SMathieu Xhonneux }, 627004d4b27SMathieu Xhonneux { 628004d4b27SMathieu Xhonneux .action = SEG6_LOCAL_ACTION_END_BPF, 629004d4b27SMathieu Xhonneux .attrs = (1 << SEG6_LOCAL_BPF), 630004d4b27SMathieu Xhonneux .input = input_action_end_bpf, 631004d4b27SMathieu Xhonneux }, 632004d4b27SMathieu Xhonneux 633d1df6fd8SDavid Lebrun }; 634d1df6fd8SDavid Lebrun 635d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action) 636d1df6fd8SDavid Lebrun { 637d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 638d1df6fd8SDavid Lebrun int i, count; 639d1df6fd8SDavid Lebrun 640709af180SColin Ian King count = ARRAY_SIZE(seg6_action_table); 641d1df6fd8SDavid Lebrun for (i = 0; i < count; i++) { 642d1df6fd8SDavid Lebrun desc = &seg6_action_table[i]; 643d1df6fd8SDavid Lebrun if (desc->action == action) 644d1df6fd8SDavid Lebrun return desc; 645d1df6fd8SDavid Lebrun } 646d1df6fd8SDavid Lebrun 647d1df6fd8SDavid Lebrun return NULL; 648d1df6fd8SDavid Lebrun } 649d1df6fd8SDavid Lebrun 650d1df6fd8SDavid Lebrun static int seg6_local_input(struct sk_buff *skb) 651d1df6fd8SDavid Lebrun { 652d1df6fd8SDavid Lebrun struct dst_entry *orig_dst = skb_dst(skb); 653d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 654d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 655d1df6fd8SDavid Lebrun 6566285217fSDavid Lebrun if (skb->protocol != htons(ETH_P_IPV6)) { 6576285217fSDavid Lebrun kfree_skb(skb); 6586285217fSDavid Lebrun return -EINVAL; 6596285217fSDavid Lebrun } 6606285217fSDavid Lebrun 661d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 662d1df6fd8SDavid Lebrun desc = slwt->desc; 663d1df6fd8SDavid Lebrun 664d1df6fd8SDavid Lebrun return desc->input(skb, slwt); 665d1df6fd8SDavid Lebrun } 666d1df6fd8SDavid Lebrun 667d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { 668d1df6fd8SDavid Lebrun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, 669d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, 670d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, 671d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, 672d1df6fd8SDavid Lebrun .len = sizeof(struct in_addr) }, 673d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, 674d1df6fd8SDavid Lebrun .len = sizeof(struct in6_addr) }, 675d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, 676d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 677004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .type = NLA_NESTED }, 678d1df6fd8SDavid Lebrun }; 679d1df6fd8SDavid Lebrun 6802d9cc60aSDavid Lebrun static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt) 6812d9cc60aSDavid Lebrun { 6822d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 6832d9cc60aSDavid Lebrun int len; 6842d9cc60aSDavid Lebrun 6852d9cc60aSDavid Lebrun srh = nla_data(attrs[SEG6_LOCAL_SRH]); 6862d9cc60aSDavid Lebrun len = nla_len(attrs[SEG6_LOCAL_SRH]); 6872d9cc60aSDavid Lebrun 6882d9cc60aSDavid Lebrun /* SRH must contain at least one segment */ 6892d9cc60aSDavid Lebrun if (len < sizeof(*srh) + sizeof(struct in6_addr)) 6902d9cc60aSDavid Lebrun return -EINVAL; 6912d9cc60aSDavid Lebrun 692bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, false)) 6932d9cc60aSDavid Lebrun return -EINVAL; 6942d9cc60aSDavid Lebrun 6957fa41efaSYueHaibing slwt->srh = kmemdup(srh, len, GFP_KERNEL); 6962d9cc60aSDavid Lebrun if (!slwt->srh) 6972d9cc60aSDavid Lebrun return -ENOMEM; 6982d9cc60aSDavid Lebrun 6992d9cc60aSDavid Lebrun slwt->headroom += len; 7002d9cc60aSDavid Lebrun 7012d9cc60aSDavid Lebrun return 0; 7022d9cc60aSDavid Lebrun } 7032d9cc60aSDavid Lebrun 7042d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7052d9cc60aSDavid Lebrun { 7062d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 7072d9cc60aSDavid Lebrun struct nlattr *nla; 7082d9cc60aSDavid Lebrun int len; 7092d9cc60aSDavid Lebrun 7102d9cc60aSDavid Lebrun srh = slwt->srh; 7112d9cc60aSDavid Lebrun len = (srh->hdrlen + 1) << 3; 7122d9cc60aSDavid Lebrun 7132d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_SRH, len); 7142d9cc60aSDavid Lebrun if (!nla) 7152d9cc60aSDavid Lebrun return -EMSGSIZE; 7162d9cc60aSDavid Lebrun 7172d9cc60aSDavid Lebrun memcpy(nla_data(nla), srh, len); 7182d9cc60aSDavid Lebrun 7192d9cc60aSDavid Lebrun return 0; 7202d9cc60aSDavid Lebrun } 7212d9cc60aSDavid Lebrun 7222d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7232d9cc60aSDavid Lebrun { 7242d9cc60aSDavid Lebrun int len = (a->srh->hdrlen + 1) << 3; 7252d9cc60aSDavid Lebrun 7262d9cc60aSDavid Lebrun if (len != ((b->srh->hdrlen + 1) << 3)) 7272d9cc60aSDavid Lebrun return 1; 7282d9cc60aSDavid Lebrun 7292d9cc60aSDavid Lebrun return memcmp(a->srh, b->srh, len); 7302d9cc60aSDavid Lebrun } 7312d9cc60aSDavid Lebrun 732964adce5SAndrea Mayer static void destroy_attr_srh(struct seg6_local_lwt *slwt) 733964adce5SAndrea Mayer { 734964adce5SAndrea Mayer kfree(slwt->srh); 735964adce5SAndrea Mayer } 736964adce5SAndrea Mayer 7372d9cc60aSDavid Lebrun static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7382d9cc60aSDavid Lebrun { 7392d9cc60aSDavid Lebrun slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]); 7402d9cc60aSDavid Lebrun 7412d9cc60aSDavid Lebrun return 0; 7422d9cc60aSDavid Lebrun } 7432d9cc60aSDavid Lebrun 7442d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7452d9cc60aSDavid Lebrun { 7462d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table)) 7472d9cc60aSDavid Lebrun return -EMSGSIZE; 7482d9cc60aSDavid Lebrun 7492d9cc60aSDavid Lebrun return 0; 7502d9cc60aSDavid Lebrun } 7512d9cc60aSDavid Lebrun 7522d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7532d9cc60aSDavid Lebrun { 7542d9cc60aSDavid Lebrun if (a->table != b->table) 7552d9cc60aSDavid Lebrun return 1; 7562d9cc60aSDavid Lebrun 7572d9cc60aSDavid Lebrun return 0; 7582d9cc60aSDavid Lebrun } 7592d9cc60aSDavid Lebrun 7602d9cc60aSDavid Lebrun static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7612d9cc60aSDavid Lebrun { 7622d9cc60aSDavid Lebrun memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]), 7632d9cc60aSDavid Lebrun sizeof(struct in_addr)); 7642d9cc60aSDavid Lebrun 7652d9cc60aSDavid Lebrun return 0; 7662d9cc60aSDavid Lebrun } 7672d9cc60aSDavid Lebrun 7682d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7692d9cc60aSDavid Lebrun { 7702d9cc60aSDavid Lebrun struct nlattr *nla; 7712d9cc60aSDavid Lebrun 7722d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr)); 7732d9cc60aSDavid Lebrun if (!nla) 7742d9cc60aSDavid Lebrun return -EMSGSIZE; 7752d9cc60aSDavid Lebrun 7762d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr)); 7772d9cc60aSDavid Lebrun 7782d9cc60aSDavid Lebrun return 0; 7792d9cc60aSDavid Lebrun } 7802d9cc60aSDavid Lebrun 7812d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7822d9cc60aSDavid Lebrun { 7832d9cc60aSDavid Lebrun return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr)); 7842d9cc60aSDavid Lebrun } 7852d9cc60aSDavid Lebrun 7862d9cc60aSDavid Lebrun static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7872d9cc60aSDavid Lebrun { 7882d9cc60aSDavid Lebrun memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]), 7892d9cc60aSDavid Lebrun sizeof(struct in6_addr)); 7902d9cc60aSDavid Lebrun 7912d9cc60aSDavid Lebrun return 0; 7922d9cc60aSDavid Lebrun } 7932d9cc60aSDavid Lebrun 7942d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7952d9cc60aSDavid Lebrun { 7962d9cc60aSDavid Lebrun struct nlattr *nla; 7972d9cc60aSDavid Lebrun 7982d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr)); 7992d9cc60aSDavid Lebrun if (!nla) 8002d9cc60aSDavid Lebrun return -EMSGSIZE; 8012d9cc60aSDavid Lebrun 8022d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr)); 8032d9cc60aSDavid Lebrun 8042d9cc60aSDavid Lebrun return 0; 8052d9cc60aSDavid Lebrun } 8062d9cc60aSDavid Lebrun 8072d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 8082d9cc60aSDavid Lebrun { 8092d9cc60aSDavid Lebrun return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr)); 8102d9cc60aSDavid Lebrun } 8112d9cc60aSDavid Lebrun 8122d9cc60aSDavid Lebrun static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 8132d9cc60aSDavid Lebrun { 8142d9cc60aSDavid Lebrun slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]); 8152d9cc60aSDavid Lebrun 8162d9cc60aSDavid Lebrun return 0; 8172d9cc60aSDavid Lebrun } 8182d9cc60aSDavid Lebrun 8192d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 8202d9cc60aSDavid Lebrun { 8212d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif)) 8222d9cc60aSDavid Lebrun return -EMSGSIZE; 8232d9cc60aSDavid Lebrun 8242d9cc60aSDavid Lebrun return 0; 8252d9cc60aSDavid Lebrun } 8262d9cc60aSDavid Lebrun 8272d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 8282d9cc60aSDavid Lebrun { 8292d9cc60aSDavid Lebrun if (a->iif != b->iif) 8302d9cc60aSDavid Lebrun return 1; 8312d9cc60aSDavid Lebrun 8322d9cc60aSDavid Lebrun return 0; 8332d9cc60aSDavid Lebrun } 8342d9cc60aSDavid Lebrun 8352d9cc60aSDavid Lebrun static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 8362d9cc60aSDavid Lebrun { 8372d9cc60aSDavid Lebrun slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]); 8382d9cc60aSDavid Lebrun 8392d9cc60aSDavid Lebrun return 0; 8402d9cc60aSDavid Lebrun } 8412d9cc60aSDavid Lebrun 8422d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 8432d9cc60aSDavid Lebrun { 8442d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif)) 8452d9cc60aSDavid Lebrun return -EMSGSIZE; 8462d9cc60aSDavid Lebrun 8472d9cc60aSDavid Lebrun return 0; 8482d9cc60aSDavid Lebrun } 8492d9cc60aSDavid Lebrun 8502d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 8512d9cc60aSDavid Lebrun { 8522d9cc60aSDavid Lebrun if (a->oif != b->oif) 8532d9cc60aSDavid Lebrun return 1; 8542d9cc60aSDavid Lebrun 8552d9cc60aSDavid Lebrun return 0; 8562d9cc60aSDavid Lebrun } 8572d9cc60aSDavid Lebrun 858004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256 859004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = { 860004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG] = { .type = NLA_U32, }, 861004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING, 862004d4b27SMathieu Xhonneux .len = MAX_PROG_NAME }, 863004d4b27SMathieu Xhonneux }; 864004d4b27SMathieu Xhonneux 865004d4b27SMathieu Xhonneux static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt) 866004d4b27SMathieu Xhonneux { 867004d4b27SMathieu Xhonneux struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1]; 868004d4b27SMathieu Xhonneux struct bpf_prog *p; 869004d4b27SMathieu Xhonneux int ret; 870004d4b27SMathieu Xhonneux u32 fd; 871004d4b27SMathieu Xhonneux 8728cb08174SJohannes Berg ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX, 8738cb08174SJohannes Berg attrs[SEG6_LOCAL_BPF], 8748cb08174SJohannes Berg bpf_prog_policy, NULL); 875004d4b27SMathieu Xhonneux if (ret < 0) 876004d4b27SMathieu Xhonneux return ret; 877004d4b27SMathieu Xhonneux 878004d4b27SMathieu Xhonneux if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME]) 879004d4b27SMathieu Xhonneux return -EINVAL; 880004d4b27SMathieu Xhonneux 881004d4b27SMathieu Xhonneux slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL); 882004d4b27SMathieu Xhonneux if (!slwt->bpf.name) 883004d4b27SMathieu Xhonneux return -ENOMEM; 884004d4b27SMathieu Xhonneux 885004d4b27SMathieu Xhonneux fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]); 886004d4b27SMathieu Xhonneux p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL); 887004d4b27SMathieu Xhonneux if (IS_ERR(p)) { 888004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 889004d4b27SMathieu Xhonneux return PTR_ERR(p); 890004d4b27SMathieu Xhonneux } 891004d4b27SMathieu Xhonneux 892004d4b27SMathieu Xhonneux slwt->bpf.prog = p; 893004d4b27SMathieu Xhonneux return 0; 894004d4b27SMathieu Xhonneux } 895004d4b27SMathieu Xhonneux 896004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt) 897004d4b27SMathieu Xhonneux { 898004d4b27SMathieu Xhonneux struct nlattr *nest; 899004d4b27SMathieu Xhonneux 900004d4b27SMathieu Xhonneux if (!slwt->bpf.prog) 901004d4b27SMathieu Xhonneux return 0; 902004d4b27SMathieu Xhonneux 903ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF); 904004d4b27SMathieu Xhonneux if (!nest) 905004d4b27SMathieu Xhonneux return -EMSGSIZE; 906004d4b27SMathieu Xhonneux 907004d4b27SMathieu Xhonneux if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id)) 908004d4b27SMathieu Xhonneux return -EMSGSIZE; 909004d4b27SMathieu Xhonneux 910004d4b27SMathieu Xhonneux if (slwt->bpf.name && 911004d4b27SMathieu Xhonneux nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name)) 912004d4b27SMathieu Xhonneux return -EMSGSIZE; 913004d4b27SMathieu Xhonneux 914004d4b27SMathieu Xhonneux return nla_nest_end(skb, nest); 915004d4b27SMathieu Xhonneux } 916004d4b27SMathieu Xhonneux 917004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 918004d4b27SMathieu Xhonneux { 919004d4b27SMathieu Xhonneux if (!a->bpf.name && !b->bpf.name) 920004d4b27SMathieu Xhonneux return 0; 921004d4b27SMathieu Xhonneux 922004d4b27SMathieu Xhonneux if (!a->bpf.name || !b->bpf.name) 923004d4b27SMathieu Xhonneux return 1; 924004d4b27SMathieu Xhonneux 925004d4b27SMathieu Xhonneux return strcmp(a->bpf.name, b->bpf.name); 926004d4b27SMathieu Xhonneux } 927004d4b27SMathieu Xhonneux 928964adce5SAndrea Mayer static void destroy_attr_bpf(struct seg6_local_lwt *slwt) 929964adce5SAndrea Mayer { 930964adce5SAndrea Mayer kfree(slwt->bpf.name); 931964adce5SAndrea Mayer if (slwt->bpf.prog) 932964adce5SAndrea Mayer bpf_prog_put(slwt->bpf.prog); 933964adce5SAndrea Mayer } 934964adce5SAndrea Mayer 935d1df6fd8SDavid Lebrun struct seg6_action_param { 936d1df6fd8SDavid Lebrun int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt); 937d1df6fd8SDavid Lebrun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 938d1df6fd8SDavid Lebrun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); 939964adce5SAndrea Mayer 940964adce5SAndrea Mayer /* optional destroy() callback useful for releasing resources which 941964adce5SAndrea Mayer * have been previously acquired in the corresponding parse() 942964adce5SAndrea Mayer * function. 943964adce5SAndrea Mayer */ 944964adce5SAndrea Mayer void (*destroy)(struct seg6_local_lwt *slwt); 945d1df6fd8SDavid Lebrun }; 946d1df6fd8SDavid Lebrun 947d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { 9482d9cc60aSDavid Lebrun [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh, 9492d9cc60aSDavid Lebrun .put = put_nla_srh, 950964adce5SAndrea Mayer .cmp = cmp_nla_srh, 951964adce5SAndrea Mayer .destroy = destroy_attr_srh }, 952d1df6fd8SDavid Lebrun 9532d9cc60aSDavid Lebrun [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table, 9542d9cc60aSDavid Lebrun .put = put_nla_table, 9552d9cc60aSDavid Lebrun .cmp = cmp_nla_table }, 956d1df6fd8SDavid Lebrun 9572d9cc60aSDavid Lebrun [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4, 9582d9cc60aSDavid Lebrun .put = put_nla_nh4, 9592d9cc60aSDavid Lebrun .cmp = cmp_nla_nh4 }, 960d1df6fd8SDavid Lebrun 9612d9cc60aSDavid Lebrun [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6, 9622d9cc60aSDavid Lebrun .put = put_nla_nh6, 9632d9cc60aSDavid Lebrun .cmp = cmp_nla_nh6 }, 964d1df6fd8SDavid Lebrun 9652d9cc60aSDavid Lebrun [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif, 9662d9cc60aSDavid Lebrun .put = put_nla_iif, 9672d9cc60aSDavid Lebrun .cmp = cmp_nla_iif }, 968d1df6fd8SDavid Lebrun 9692d9cc60aSDavid Lebrun [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif, 9702d9cc60aSDavid Lebrun .put = put_nla_oif, 9712d9cc60aSDavid Lebrun .cmp = cmp_nla_oif }, 972004d4b27SMathieu Xhonneux 973004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .parse = parse_nla_bpf, 974004d4b27SMathieu Xhonneux .put = put_nla_bpf, 975964adce5SAndrea Mayer .cmp = cmp_nla_bpf, 976964adce5SAndrea Mayer .destroy = destroy_attr_bpf }, 977004d4b27SMathieu Xhonneux 978d1df6fd8SDavid Lebrun }; 979d1df6fd8SDavid Lebrun 980964adce5SAndrea Mayer /* call the destroy() callback (if available) for each set attribute in 981*0a3021f1SAndrea Mayer * @parsed_attrs, starting from the first attribute up to the @max_parsed 982*0a3021f1SAndrea Mayer * (excluded) attribute. 983964adce5SAndrea Mayer */ 984*0a3021f1SAndrea Mayer static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed, 985*0a3021f1SAndrea Mayer struct seg6_local_lwt *slwt) 986964adce5SAndrea Mayer { 987964adce5SAndrea Mayer struct seg6_action_param *param; 988964adce5SAndrea Mayer int i; 989964adce5SAndrea Mayer 990964adce5SAndrea Mayer /* Every required seg6local attribute is identified by an ID which is 991964adce5SAndrea Mayer * encoded as a flag (i.e: 1 << ID) in the 'attrs' bitmask; 992964adce5SAndrea Mayer * 993*0a3021f1SAndrea Mayer * We scan the 'parsed_attrs' bitmask, starting from the first attribute 994964adce5SAndrea Mayer * up to the @max_parsed (excluded) attribute. 995964adce5SAndrea Mayer * For each set attribute, we retrieve the corresponding destroy() 996964adce5SAndrea Mayer * callback. If the callback is not available, then we skip to the next 997964adce5SAndrea Mayer * attribute; otherwise, we call the destroy() callback. 998964adce5SAndrea Mayer */ 999964adce5SAndrea Mayer for (i = 0; i < max_parsed; ++i) { 1000*0a3021f1SAndrea Mayer if (!(parsed_attrs & (1 << i))) 1001964adce5SAndrea Mayer continue; 1002964adce5SAndrea Mayer 1003964adce5SAndrea Mayer param = &seg6_action_params[i]; 1004964adce5SAndrea Mayer 1005964adce5SAndrea Mayer if (param->destroy) 1006964adce5SAndrea Mayer param->destroy(slwt); 1007964adce5SAndrea Mayer } 1008964adce5SAndrea Mayer } 1009964adce5SAndrea Mayer 1010964adce5SAndrea Mayer /* release all the resources that may have been acquired during parsing 1011964adce5SAndrea Mayer * operations. 1012964adce5SAndrea Mayer */ 1013964adce5SAndrea Mayer static void destroy_attrs(struct seg6_local_lwt *slwt) 1014964adce5SAndrea Mayer { 1015*0a3021f1SAndrea Mayer unsigned long attrs = slwt->desc->attrs | slwt->parsed_optattrs; 1016*0a3021f1SAndrea Mayer 1017*0a3021f1SAndrea Mayer __destroy_attrs(attrs, SEG6_LOCAL_MAX + 1, slwt); 1018*0a3021f1SAndrea Mayer } 1019*0a3021f1SAndrea Mayer 1020*0a3021f1SAndrea Mayer static int parse_nla_optional_attrs(struct nlattr **attrs, 1021*0a3021f1SAndrea Mayer struct seg6_local_lwt *slwt) 1022*0a3021f1SAndrea Mayer { 1023*0a3021f1SAndrea Mayer struct seg6_action_desc *desc = slwt->desc; 1024*0a3021f1SAndrea Mayer unsigned long parsed_optattrs = 0; 1025*0a3021f1SAndrea Mayer struct seg6_action_param *param; 1026*0a3021f1SAndrea Mayer int err, i; 1027*0a3021f1SAndrea Mayer 1028*0a3021f1SAndrea Mayer for (i = 0; i < SEG6_LOCAL_MAX + 1; ++i) { 1029*0a3021f1SAndrea Mayer if (!(desc->optattrs & (1 << i)) || !attrs[i]) 1030*0a3021f1SAndrea Mayer continue; 1031*0a3021f1SAndrea Mayer 1032*0a3021f1SAndrea Mayer /* once here, the i-th attribute is provided by the 1033*0a3021f1SAndrea Mayer * userspace AND it is identified optional as well. 1034*0a3021f1SAndrea Mayer */ 1035*0a3021f1SAndrea Mayer param = &seg6_action_params[i]; 1036*0a3021f1SAndrea Mayer 1037*0a3021f1SAndrea Mayer err = param->parse(attrs, slwt); 1038*0a3021f1SAndrea Mayer if (err < 0) 1039*0a3021f1SAndrea Mayer goto parse_optattrs_err; 1040*0a3021f1SAndrea Mayer 1041*0a3021f1SAndrea Mayer /* current attribute has been correctly parsed */ 1042*0a3021f1SAndrea Mayer parsed_optattrs |= (1 << i); 1043*0a3021f1SAndrea Mayer } 1044*0a3021f1SAndrea Mayer 1045*0a3021f1SAndrea Mayer /* store in the tunnel state all the optional attributed successfully 1046*0a3021f1SAndrea Mayer * parsed. 1047*0a3021f1SAndrea Mayer */ 1048*0a3021f1SAndrea Mayer slwt->parsed_optattrs = parsed_optattrs; 1049*0a3021f1SAndrea Mayer 1050*0a3021f1SAndrea Mayer return 0; 1051*0a3021f1SAndrea Mayer 1052*0a3021f1SAndrea Mayer parse_optattrs_err: 1053*0a3021f1SAndrea Mayer __destroy_attrs(parsed_optattrs, i, slwt); 1054*0a3021f1SAndrea Mayer 1055*0a3021f1SAndrea Mayer return err; 1056964adce5SAndrea Mayer } 1057964adce5SAndrea Mayer 1058d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) 1059d1df6fd8SDavid Lebrun { 1060d1df6fd8SDavid Lebrun struct seg6_action_param *param; 1061d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 1062*0a3021f1SAndrea Mayer unsigned long invalid_attrs; 1063d1df6fd8SDavid Lebrun int i, err; 1064d1df6fd8SDavid Lebrun 1065d1df6fd8SDavid Lebrun desc = __get_action_desc(slwt->action); 1066d1df6fd8SDavid Lebrun if (!desc) 1067d1df6fd8SDavid Lebrun return -EINVAL; 1068d1df6fd8SDavid Lebrun 1069d1df6fd8SDavid Lebrun if (!desc->input) 1070d1df6fd8SDavid Lebrun return -EOPNOTSUPP; 1071d1df6fd8SDavid Lebrun 1072d1df6fd8SDavid Lebrun slwt->desc = desc; 1073d1df6fd8SDavid Lebrun slwt->headroom += desc->static_headroom; 1074d1df6fd8SDavid Lebrun 1075*0a3021f1SAndrea Mayer /* Forcing the desc->optattrs *set* and the desc->attrs *set* to be 1076*0a3021f1SAndrea Mayer * disjoined, this allow us to release acquired resources by optional 1077*0a3021f1SAndrea Mayer * attributes and by required attributes independently from each other 1078*0a3021f1SAndrea Mayer * without any interfarence. 1079*0a3021f1SAndrea Mayer * In other terms, we are sure that we do not release some the acquired 1080*0a3021f1SAndrea Mayer * resources twice. 1081*0a3021f1SAndrea Mayer * 1082*0a3021f1SAndrea Mayer * Note that if an attribute is configured both as required and as 1083*0a3021f1SAndrea Mayer * optional, it means that the user has messed something up in the 1084*0a3021f1SAndrea Mayer * seg6_action_table. Therefore, this check is required for SRv6 1085*0a3021f1SAndrea Mayer * behaviors to work properly. 1086*0a3021f1SAndrea Mayer */ 1087*0a3021f1SAndrea Mayer invalid_attrs = desc->attrs & desc->optattrs; 1088*0a3021f1SAndrea Mayer if (invalid_attrs) { 1089*0a3021f1SAndrea Mayer WARN_ONCE(1, 1090*0a3021f1SAndrea Mayer "An attribute cannot be both required AND optional"); 1091*0a3021f1SAndrea Mayer return -EINVAL; 1092*0a3021f1SAndrea Mayer } 1093*0a3021f1SAndrea Mayer 1094*0a3021f1SAndrea Mayer /* parse the required attributes */ 1095d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1096d1df6fd8SDavid Lebrun if (desc->attrs & (1 << i)) { 1097d1df6fd8SDavid Lebrun if (!attrs[i]) 1098d1df6fd8SDavid Lebrun return -EINVAL; 1099d1df6fd8SDavid Lebrun 1100d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1101d1df6fd8SDavid Lebrun 1102d1df6fd8SDavid Lebrun err = param->parse(attrs, slwt); 1103d1df6fd8SDavid Lebrun if (err < 0) 1104*0a3021f1SAndrea Mayer goto parse_attrs_err; 1105d1df6fd8SDavid Lebrun } 1106d1df6fd8SDavid Lebrun } 1107d1df6fd8SDavid Lebrun 1108*0a3021f1SAndrea Mayer /* parse the optional attributes, if any */ 1109*0a3021f1SAndrea Mayer err = parse_nla_optional_attrs(attrs, slwt); 1110*0a3021f1SAndrea Mayer if (err < 0) 1111*0a3021f1SAndrea Mayer goto parse_attrs_err; 1112*0a3021f1SAndrea Mayer 1113d1df6fd8SDavid Lebrun return 0; 1114964adce5SAndrea Mayer 1115*0a3021f1SAndrea Mayer parse_attrs_err: 1116964adce5SAndrea Mayer /* release any resource that may have been acquired during the i-1 1117964adce5SAndrea Mayer * parse() operations. 1118964adce5SAndrea Mayer */ 1119*0a3021f1SAndrea Mayer __destroy_attrs(desc->attrs, i, slwt); 1120964adce5SAndrea Mayer 1121964adce5SAndrea Mayer return err; 1122d1df6fd8SDavid Lebrun } 1123d1df6fd8SDavid Lebrun 1124faee6769SAlexander Aring static int seg6_local_build_state(struct net *net, struct nlattr *nla, 1125faee6769SAlexander Aring unsigned int family, const void *cfg, 1126faee6769SAlexander Aring struct lwtunnel_state **ts, 1127d1df6fd8SDavid Lebrun struct netlink_ext_ack *extack) 1128d1df6fd8SDavid Lebrun { 1129d1df6fd8SDavid Lebrun struct nlattr *tb[SEG6_LOCAL_MAX + 1]; 1130d1df6fd8SDavid Lebrun struct lwtunnel_state *newts; 1131d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 1132d1df6fd8SDavid Lebrun int err; 1133d1df6fd8SDavid Lebrun 11346285217fSDavid Lebrun if (family != AF_INET6) 11356285217fSDavid Lebrun return -EINVAL; 11366285217fSDavid Lebrun 11378cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla, 11388cb08174SJohannes Berg seg6_local_policy, extack); 1139d1df6fd8SDavid Lebrun 1140d1df6fd8SDavid Lebrun if (err < 0) 1141d1df6fd8SDavid Lebrun return err; 1142d1df6fd8SDavid Lebrun 1143d1df6fd8SDavid Lebrun if (!tb[SEG6_LOCAL_ACTION]) 1144d1df6fd8SDavid Lebrun return -EINVAL; 1145d1df6fd8SDavid Lebrun 1146d1df6fd8SDavid Lebrun newts = lwtunnel_state_alloc(sizeof(*slwt)); 1147d1df6fd8SDavid Lebrun if (!newts) 1148d1df6fd8SDavid Lebrun return -ENOMEM; 1149d1df6fd8SDavid Lebrun 1150d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(newts); 1151d1df6fd8SDavid Lebrun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); 1152d1df6fd8SDavid Lebrun 1153d1df6fd8SDavid Lebrun err = parse_nla_action(tb, slwt); 1154d1df6fd8SDavid Lebrun if (err < 0) 1155d1df6fd8SDavid Lebrun goto out_free; 1156d1df6fd8SDavid Lebrun 1157d1df6fd8SDavid Lebrun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; 1158d1df6fd8SDavid Lebrun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; 1159d1df6fd8SDavid Lebrun newts->headroom = slwt->headroom; 1160d1df6fd8SDavid Lebrun 1161d1df6fd8SDavid Lebrun *ts = newts; 1162d1df6fd8SDavid Lebrun 1163d1df6fd8SDavid Lebrun return 0; 1164d1df6fd8SDavid Lebrun 1165d1df6fd8SDavid Lebrun out_free: 1166d1df6fd8SDavid Lebrun kfree(newts); 1167d1df6fd8SDavid Lebrun return err; 1168d1df6fd8SDavid Lebrun } 1169d1df6fd8SDavid Lebrun 1170d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt) 1171d1df6fd8SDavid Lebrun { 1172d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1173d1df6fd8SDavid Lebrun 1174964adce5SAndrea Mayer destroy_attrs(slwt); 1175004d4b27SMathieu Xhonneux 1176004d4b27SMathieu Xhonneux return; 1177d1df6fd8SDavid Lebrun } 1178d1df6fd8SDavid Lebrun 1179d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb, 1180d1df6fd8SDavid Lebrun struct lwtunnel_state *lwt) 1181d1df6fd8SDavid Lebrun { 1182d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1183d1df6fd8SDavid Lebrun struct seg6_action_param *param; 1184*0a3021f1SAndrea Mayer unsigned long attrs; 1185d1df6fd8SDavid Lebrun int i, err; 1186d1df6fd8SDavid Lebrun 1187d1df6fd8SDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) 1188d1df6fd8SDavid Lebrun return -EMSGSIZE; 1189d1df6fd8SDavid Lebrun 1190*0a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 1191*0a3021f1SAndrea Mayer 1192d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1193*0a3021f1SAndrea Mayer if (attrs & (1 << i)) { 1194d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1195d1df6fd8SDavid Lebrun err = param->put(skb, slwt); 1196d1df6fd8SDavid Lebrun if (err < 0) 1197d1df6fd8SDavid Lebrun return err; 1198d1df6fd8SDavid Lebrun } 1199d1df6fd8SDavid Lebrun } 1200d1df6fd8SDavid Lebrun 1201d1df6fd8SDavid Lebrun return 0; 1202d1df6fd8SDavid Lebrun } 1203d1df6fd8SDavid Lebrun 1204d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) 1205d1df6fd8SDavid Lebrun { 1206d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1207d1df6fd8SDavid Lebrun unsigned long attrs; 1208d1df6fd8SDavid Lebrun int nlsize; 1209d1df6fd8SDavid Lebrun 1210d1df6fd8SDavid Lebrun nlsize = nla_total_size(4); /* action */ 1211d1df6fd8SDavid Lebrun 1212*0a3021f1SAndrea Mayer attrs = slwt->desc->attrs | slwt->parsed_optattrs; 1213d1df6fd8SDavid Lebrun 1214d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_SRH)) 1215d1df6fd8SDavid Lebrun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); 1216d1df6fd8SDavid Lebrun 1217d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_TABLE)) 1218d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1219d1df6fd8SDavid Lebrun 1220d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_NH4)) 1221d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1222d1df6fd8SDavid Lebrun 1223d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_NH6)) 1224d1df6fd8SDavid Lebrun nlsize += nla_total_size(16); 1225d1df6fd8SDavid Lebrun 1226d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_IIF)) 1227d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1228d1df6fd8SDavid Lebrun 1229d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_OIF)) 1230d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1231d1df6fd8SDavid Lebrun 1232004d4b27SMathieu Xhonneux if (attrs & (1 << SEG6_LOCAL_BPF)) 1233004d4b27SMathieu Xhonneux nlsize += nla_total_size(sizeof(struct nlattr)) + 1234004d4b27SMathieu Xhonneux nla_total_size(MAX_PROG_NAME) + 1235004d4b27SMathieu Xhonneux nla_total_size(4); 1236004d4b27SMathieu Xhonneux 1237d1df6fd8SDavid Lebrun return nlsize; 1238d1df6fd8SDavid Lebrun } 1239d1df6fd8SDavid Lebrun 1240d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a, 1241d1df6fd8SDavid Lebrun struct lwtunnel_state *b) 1242d1df6fd8SDavid Lebrun { 1243d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt_a, *slwt_b; 1244d1df6fd8SDavid Lebrun struct seg6_action_param *param; 1245*0a3021f1SAndrea Mayer unsigned long attrs_a, attrs_b; 1246d1df6fd8SDavid Lebrun int i; 1247d1df6fd8SDavid Lebrun 1248d1df6fd8SDavid Lebrun slwt_a = seg6_local_lwtunnel(a); 1249d1df6fd8SDavid Lebrun slwt_b = seg6_local_lwtunnel(b); 1250d1df6fd8SDavid Lebrun 1251d1df6fd8SDavid Lebrun if (slwt_a->action != slwt_b->action) 1252d1df6fd8SDavid Lebrun return 1; 1253d1df6fd8SDavid Lebrun 1254*0a3021f1SAndrea Mayer attrs_a = slwt_a->desc->attrs | slwt_a->parsed_optattrs; 1255*0a3021f1SAndrea Mayer attrs_b = slwt_b->desc->attrs | slwt_b->parsed_optattrs; 1256*0a3021f1SAndrea Mayer 1257*0a3021f1SAndrea Mayer if (attrs_a != attrs_b) 1258d1df6fd8SDavid Lebrun return 1; 1259d1df6fd8SDavid Lebrun 1260d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1261*0a3021f1SAndrea Mayer if (attrs_a & (1 << i)) { 1262d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1263d1df6fd8SDavid Lebrun if (param->cmp(slwt_a, slwt_b)) 1264d1df6fd8SDavid Lebrun return 1; 1265d1df6fd8SDavid Lebrun } 1266d1df6fd8SDavid Lebrun } 1267d1df6fd8SDavid Lebrun 1268d1df6fd8SDavid Lebrun return 0; 1269d1df6fd8SDavid Lebrun } 1270d1df6fd8SDavid Lebrun 1271d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = { 1272d1df6fd8SDavid Lebrun .build_state = seg6_local_build_state, 1273d1df6fd8SDavid Lebrun .destroy_state = seg6_local_destroy_state, 1274d1df6fd8SDavid Lebrun .input = seg6_local_input, 1275d1df6fd8SDavid Lebrun .fill_encap = seg6_local_fill_encap, 1276d1df6fd8SDavid Lebrun .get_encap_size = seg6_local_get_encap_size, 1277d1df6fd8SDavid Lebrun .cmp_encap = seg6_local_cmp_encap, 1278d1df6fd8SDavid Lebrun .owner = THIS_MODULE, 1279d1df6fd8SDavid Lebrun }; 1280d1df6fd8SDavid Lebrun 1281d1df6fd8SDavid Lebrun int __init seg6_local_init(void) 1282d1df6fd8SDavid Lebrun { 1283d1df6fd8SDavid Lebrun return lwtunnel_encap_add_ops(&seg6_local_ops, 1284d1df6fd8SDavid Lebrun LWTUNNEL_ENCAP_SEG6_LOCAL); 1285d1df6fd8SDavid Lebrun } 1286d1df6fd8SDavid Lebrun 1287d1df6fd8SDavid Lebrun void seg6_local_exit(void) 1288d1df6fd8SDavid Lebrun { 1289d1df6fd8SDavid Lebrun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); 1290d1df6fd8SDavid Lebrun } 1291