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; 39d1df6fd8SDavid Lebrun int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 40d1df6fd8SDavid Lebrun int static_headroom; 41d1df6fd8SDavid Lebrun }; 42d1df6fd8SDavid Lebrun 43004d4b27SMathieu Xhonneux struct bpf_lwt_prog { 44004d4b27SMathieu Xhonneux struct bpf_prog *prog; 45004d4b27SMathieu Xhonneux char *name; 46004d4b27SMathieu Xhonneux }; 47004d4b27SMathieu Xhonneux 48d1df6fd8SDavid Lebrun struct seg6_local_lwt { 49d1df6fd8SDavid Lebrun int action; 50d1df6fd8SDavid Lebrun struct ipv6_sr_hdr *srh; 51d1df6fd8SDavid Lebrun int table; 52d1df6fd8SDavid Lebrun struct in_addr nh4; 53d1df6fd8SDavid Lebrun struct in6_addr nh6; 54d1df6fd8SDavid Lebrun int iif; 55d1df6fd8SDavid Lebrun int oif; 56004d4b27SMathieu Xhonneux struct bpf_lwt_prog bpf; 57d1df6fd8SDavid Lebrun 58d1df6fd8SDavid Lebrun int headroom; 59d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 60d1df6fd8SDavid Lebrun }; 61d1df6fd8SDavid Lebrun 62d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) 63d1df6fd8SDavid Lebrun { 64d1df6fd8SDavid Lebrun return (struct seg6_local_lwt *)lwt->data; 65d1df6fd8SDavid Lebrun } 66d1df6fd8SDavid Lebrun 67140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb) 68140f04c3SDavid Lebrun { 69140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 705829d70bSAhmed Abdelsalam int len, srhoff = 0; 71140f04c3SDavid Lebrun 725829d70bSAhmed Abdelsalam if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0) 73140f04c3SDavid Lebrun return NULL; 74140f04c3SDavid Lebrun 755829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + sizeof(*srh))) 765829d70bSAhmed Abdelsalam return NULL; 775829d70bSAhmed Abdelsalam 785829d70bSAhmed Abdelsalam srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 795829d70bSAhmed Abdelsalam 80140f04c3SDavid Lebrun len = (srh->hdrlen + 1) << 3; 81140f04c3SDavid Lebrun 825829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + len)) 83140f04c3SDavid Lebrun return NULL; 84140f04c3SDavid Lebrun 857f91ed8cSAndrea Mayer /* note that pskb_may_pull may change pointers in header; 867f91ed8cSAndrea Mayer * for this reason it is necessary to reload them when needed. 877f91ed8cSAndrea Mayer */ 887f91ed8cSAndrea Mayer srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 897f91ed8cSAndrea Mayer 90*bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, true)) 91140f04c3SDavid Lebrun return NULL; 92140f04c3SDavid Lebrun 93140f04c3SDavid Lebrun return srh; 94140f04c3SDavid Lebrun } 95140f04c3SDavid Lebrun 96140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) 97140f04c3SDavid Lebrun { 98140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 99140f04c3SDavid Lebrun 100140f04c3SDavid Lebrun srh = get_srh(skb); 101140f04c3SDavid Lebrun if (!srh) 102140f04c3SDavid Lebrun return NULL; 103140f04c3SDavid Lebrun 104140f04c3SDavid Lebrun if (srh->segments_left == 0) 105140f04c3SDavid Lebrun return NULL; 106140f04c3SDavid Lebrun 107140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 108140f04c3SDavid Lebrun if (!seg6_hmac_validate_skb(skb)) 109140f04c3SDavid Lebrun return NULL; 110140f04c3SDavid Lebrun #endif 111140f04c3SDavid Lebrun 112140f04c3SDavid Lebrun return srh; 113140f04c3SDavid Lebrun } 114140f04c3SDavid Lebrun 115d7a669ddSDavid Lebrun static bool decap_and_validate(struct sk_buff *skb, int proto) 116d7a669ddSDavid Lebrun { 117d7a669ddSDavid Lebrun struct ipv6_sr_hdr *srh; 118d7a669ddSDavid Lebrun unsigned int off = 0; 119d7a669ddSDavid Lebrun 120d7a669ddSDavid Lebrun srh = get_srh(skb); 121d7a669ddSDavid Lebrun if (srh && srh->segments_left > 0) 122d7a669ddSDavid Lebrun return false; 123d7a669ddSDavid Lebrun 124d7a669ddSDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 125d7a669ddSDavid Lebrun if (srh && !seg6_hmac_validate_skb(skb)) 126d7a669ddSDavid Lebrun return false; 127d7a669ddSDavid Lebrun #endif 128d7a669ddSDavid Lebrun 129d7a669ddSDavid Lebrun if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0) 130d7a669ddSDavid Lebrun return false; 131d7a669ddSDavid Lebrun 132d7a669ddSDavid Lebrun if (!pskb_pull(skb, off)) 133d7a669ddSDavid Lebrun return false; 134d7a669ddSDavid Lebrun 135d7a669ddSDavid Lebrun skb_postpull_rcsum(skb, skb_network_header(skb), off); 136d7a669ddSDavid Lebrun 137d7a669ddSDavid Lebrun skb_reset_network_header(skb); 138d7a669ddSDavid Lebrun skb_reset_transport_header(skb); 13962ebaeaeSYuki Taguchi if (iptunnel_pull_offloads(skb)) 14062ebaeaeSYuki Taguchi return false; 141d7a669ddSDavid Lebrun 142d7a669ddSDavid Lebrun return true; 143d7a669ddSDavid Lebrun } 144d7a669ddSDavid Lebrun 145d7a669ddSDavid Lebrun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr) 146d7a669ddSDavid Lebrun { 147d7a669ddSDavid Lebrun struct in6_addr *addr; 148d7a669ddSDavid Lebrun 149d7a669ddSDavid Lebrun srh->segments_left--; 150d7a669ddSDavid Lebrun addr = srh->segments + srh->segments_left; 151d7a669ddSDavid Lebrun *daddr = *addr; 152d7a669ddSDavid Lebrun } 153d7a669ddSDavid Lebrun 154fd1fef0cSAndrea Mayer static int 155fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr, 156fd1fef0cSAndrea Mayer u32 tbl_id, bool local_delivery) 157d7a669ddSDavid Lebrun { 158d7a669ddSDavid Lebrun struct net *net = dev_net(skb->dev); 159d7a669ddSDavid Lebrun struct ipv6hdr *hdr = ipv6_hdr(skb); 160d7a669ddSDavid Lebrun int flags = RT6_LOOKUP_F_HAS_SADDR; 161d7a669ddSDavid Lebrun struct dst_entry *dst = NULL; 162d7a669ddSDavid Lebrun struct rt6_info *rt; 163d7a669ddSDavid Lebrun struct flowi6 fl6; 164fd1fef0cSAndrea Mayer int dev_flags = 0; 165d7a669ddSDavid Lebrun 166d7a669ddSDavid Lebrun fl6.flowi6_iif = skb->dev->ifindex; 167d7a669ddSDavid Lebrun fl6.daddr = nhaddr ? *nhaddr : hdr->daddr; 168d7a669ddSDavid Lebrun fl6.saddr = hdr->saddr; 169d7a669ddSDavid Lebrun fl6.flowlabel = ip6_flowinfo(hdr); 170d7a669ddSDavid Lebrun fl6.flowi6_mark = skb->mark; 171d7a669ddSDavid Lebrun fl6.flowi6_proto = hdr->nexthdr; 172d7a669ddSDavid Lebrun 173d7a669ddSDavid Lebrun if (nhaddr) 174d7a669ddSDavid Lebrun fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH; 175d7a669ddSDavid Lebrun 176d7a669ddSDavid Lebrun if (!tbl_id) { 177b75cc8f9SDavid Ahern dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags); 178d7a669ddSDavid Lebrun } else { 179d7a669ddSDavid Lebrun struct fib6_table *table; 180d7a669ddSDavid Lebrun 181d7a669ddSDavid Lebrun table = fib6_get_table(net, tbl_id); 182d7a669ddSDavid Lebrun if (!table) 183d7a669ddSDavid Lebrun goto out; 184d7a669ddSDavid Lebrun 185b75cc8f9SDavid Ahern rt = ip6_pol_route(net, table, 0, &fl6, skb, flags); 186d7a669ddSDavid Lebrun dst = &rt->dst; 187d7a669ddSDavid Lebrun } 188d7a669ddSDavid Lebrun 189fd1fef0cSAndrea Mayer /* we want to discard traffic destined for local packet processing, 190fd1fef0cSAndrea Mayer * if @local_delivery is set to false. 191fd1fef0cSAndrea Mayer */ 192fd1fef0cSAndrea Mayer if (!local_delivery) 193fd1fef0cSAndrea Mayer dev_flags |= IFF_LOOPBACK; 194fd1fef0cSAndrea Mayer 195fd1fef0cSAndrea Mayer if (dst && (dst->dev->flags & dev_flags) && !dst->error) { 196d7a669ddSDavid Lebrun dst_release(dst); 197d7a669ddSDavid Lebrun dst = NULL; 198d7a669ddSDavid Lebrun } 199d7a669ddSDavid Lebrun 200d7a669ddSDavid Lebrun out: 201d7a669ddSDavid Lebrun if (!dst) { 202d7a669ddSDavid Lebrun rt = net->ipv6.ip6_blk_hole_entry; 203d7a669ddSDavid Lebrun dst = &rt->dst; 204d7a669ddSDavid Lebrun dst_hold(dst); 205d7a669ddSDavid Lebrun } 206d7a669ddSDavid Lebrun 207d7a669ddSDavid Lebrun skb_dst_drop(skb); 208d7a669ddSDavid Lebrun skb_dst_set(skb, dst); 2091c1e761eSMathieu Xhonneux return dst->error; 210d7a669ddSDavid Lebrun } 211d7a669ddSDavid Lebrun 212fd1fef0cSAndrea Mayer int seg6_lookup_nexthop(struct sk_buff *skb, 213fd1fef0cSAndrea Mayer struct in6_addr *nhaddr, u32 tbl_id) 214fd1fef0cSAndrea Mayer { 215fd1fef0cSAndrea Mayer return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false); 216fd1fef0cSAndrea Mayer } 217fd1fef0cSAndrea Mayer 218140f04c3SDavid Lebrun /* regular endpoint function */ 219140f04c3SDavid Lebrun static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 220140f04c3SDavid Lebrun { 221140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 222140f04c3SDavid Lebrun 223140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 224140f04c3SDavid Lebrun if (!srh) 225140f04c3SDavid Lebrun goto drop; 226140f04c3SDavid Lebrun 227d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 228140f04c3SDavid Lebrun 2291c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 230140f04c3SDavid Lebrun 231140f04c3SDavid Lebrun return dst_input(skb); 232140f04c3SDavid Lebrun 233140f04c3SDavid Lebrun drop: 234140f04c3SDavid Lebrun kfree_skb(skb); 235140f04c3SDavid Lebrun return -EINVAL; 236140f04c3SDavid Lebrun } 237140f04c3SDavid Lebrun 238140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */ 239140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) 240140f04c3SDavid Lebrun { 241140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 242140f04c3SDavid Lebrun 243140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 244140f04c3SDavid Lebrun if (!srh) 245140f04c3SDavid Lebrun goto drop; 246140f04c3SDavid Lebrun 247d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 248140f04c3SDavid Lebrun 2491c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, &slwt->nh6, 0); 250140f04c3SDavid Lebrun 251140f04c3SDavid Lebrun return dst_input(skb); 252140f04c3SDavid Lebrun 253140f04c3SDavid Lebrun drop: 254140f04c3SDavid Lebrun kfree_skb(skb); 255140f04c3SDavid Lebrun return -EINVAL; 256140f04c3SDavid Lebrun } 257140f04c3SDavid Lebrun 258891ef8ddSDavid Lebrun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) 259891ef8ddSDavid Lebrun { 260891ef8ddSDavid Lebrun struct ipv6_sr_hdr *srh; 261891ef8ddSDavid Lebrun 262891ef8ddSDavid Lebrun srh = get_and_validate_srh(skb); 263891ef8ddSDavid Lebrun if (!srh) 264891ef8ddSDavid Lebrun goto drop; 265891ef8ddSDavid Lebrun 266891ef8ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 267891ef8ddSDavid Lebrun 2681c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, slwt->table); 269891ef8ddSDavid Lebrun 270891ef8ddSDavid Lebrun return dst_input(skb); 271891ef8ddSDavid Lebrun 272891ef8ddSDavid Lebrun drop: 273891ef8ddSDavid Lebrun kfree_skb(skb); 274891ef8ddSDavid Lebrun return -EINVAL; 275891ef8ddSDavid Lebrun } 276891ef8ddSDavid Lebrun 277891ef8ddSDavid Lebrun /* decapsulate and forward inner L2 frame on specified interface */ 278891ef8ddSDavid Lebrun static int input_action_end_dx2(struct sk_buff *skb, 279891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 280891ef8ddSDavid Lebrun { 281891ef8ddSDavid Lebrun struct net *net = dev_net(skb->dev); 282891ef8ddSDavid Lebrun struct net_device *odev; 283891ef8ddSDavid Lebrun struct ethhdr *eth; 284891ef8ddSDavid Lebrun 28526776253SPaolo Lungaroni if (!decap_and_validate(skb, IPPROTO_ETHERNET)) 286891ef8ddSDavid Lebrun goto drop; 287891ef8ddSDavid Lebrun 288891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, ETH_HLEN)) 289891ef8ddSDavid Lebrun goto drop; 290891ef8ddSDavid Lebrun 291891ef8ddSDavid Lebrun skb_reset_mac_header(skb); 292891ef8ddSDavid Lebrun eth = (struct ethhdr *)skb->data; 293891ef8ddSDavid Lebrun 294891ef8ddSDavid Lebrun /* To determine the frame's protocol, we assume it is 802.3. This avoids 295891ef8ddSDavid Lebrun * a call to eth_type_trans(), which is not really relevant for our 296891ef8ddSDavid Lebrun * use case. 297891ef8ddSDavid Lebrun */ 298891ef8ddSDavid Lebrun if (!eth_proto_is_802_3(eth->h_proto)) 299891ef8ddSDavid Lebrun goto drop; 300891ef8ddSDavid Lebrun 301891ef8ddSDavid Lebrun odev = dev_get_by_index_rcu(net, slwt->oif); 302891ef8ddSDavid Lebrun if (!odev) 303891ef8ddSDavid Lebrun goto drop; 304891ef8ddSDavid Lebrun 305891ef8ddSDavid Lebrun /* As we accept Ethernet frames, make sure the egress device is of 306891ef8ddSDavid Lebrun * the correct type. 307891ef8ddSDavid Lebrun */ 308891ef8ddSDavid Lebrun if (odev->type != ARPHRD_ETHER) 309891ef8ddSDavid Lebrun goto drop; 310891ef8ddSDavid Lebrun 311891ef8ddSDavid Lebrun if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev)) 312891ef8ddSDavid Lebrun goto drop; 313891ef8ddSDavid Lebrun 314891ef8ddSDavid Lebrun skb_orphan(skb); 315891ef8ddSDavid Lebrun 316891ef8ddSDavid Lebrun if (skb_warn_if_lro(skb)) 317891ef8ddSDavid Lebrun goto drop; 318891ef8ddSDavid Lebrun 319891ef8ddSDavid Lebrun skb_forward_csum(skb); 320891ef8ddSDavid Lebrun 321891ef8ddSDavid Lebrun if (skb->len - ETH_HLEN > odev->mtu) 322891ef8ddSDavid Lebrun goto drop; 323891ef8ddSDavid Lebrun 324891ef8ddSDavid Lebrun skb->dev = odev; 325891ef8ddSDavid Lebrun skb->protocol = eth->h_proto; 326891ef8ddSDavid Lebrun 327891ef8ddSDavid Lebrun return dev_queue_xmit(skb); 328891ef8ddSDavid Lebrun 329891ef8ddSDavid Lebrun drop: 330891ef8ddSDavid Lebrun kfree_skb(skb); 331891ef8ddSDavid Lebrun return -EINVAL; 332891ef8ddSDavid Lebrun } 333891ef8ddSDavid Lebrun 334140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */ 335140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb, 336140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 337140f04c3SDavid Lebrun { 338d7a669ddSDavid Lebrun struct in6_addr *nhaddr = NULL; 339140f04c3SDavid Lebrun 340140f04c3SDavid Lebrun /* this function accepts IPv6 encapsulated packets, with either 341140f04c3SDavid Lebrun * an SRH with SL=0, or no SRH. 342140f04c3SDavid Lebrun */ 343140f04c3SDavid Lebrun 344d7a669ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 345140f04c3SDavid Lebrun goto drop; 346140f04c3SDavid Lebrun 347d7a669ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 348140f04c3SDavid Lebrun goto drop; 349140f04c3SDavid Lebrun 350140f04c3SDavid Lebrun /* The inner packet is not associated to any local interface, 351140f04c3SDavid Lebrun * so we do not call netif_rx(). 352140f04c3SDavid Lebrun * 353140f04c3SDavid Lebrun * If slwt->nh6 is set to ::, then lookup the nexthop for the 354140f04c3SDavid Lebrun * inner packet's DA. Otherwise, use the specified nexthop. 355140f04c3SDavid Lebrun */ 356140f04c3SDavid Lebrun 357d7a669ddSDavid Lebrun if (!ipv6_addr_any(&slwt->nh6)) 358d7a669ddSDavid Lebrun nhaddr = &slwt->nh6; 359140f04c3SDavid Lebrun 360c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 361c71644d0SAndrea Mayer 3621c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, nhaddr, 0); 363140f04c3SDavid Lebrun 364140f04c3SDavid Lebrun return dst_input(skb); 365140f04c3SDavid Lebrun drop: 366140f04c3SDavid Lebrun kfree_skb(skb); 367140f04c3SDavid Lebrun return -EINVAL; 368140f04c3SDavid Lebrun } 369140f04c3SDavid Lebrun 370891ef8ddSDavid Lebrun static int input_action_end_dx4(struct sk_buff *skb, 371891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 372891ef8ddSDavid Lebrun { 373891ef8ddSDavid Lebrun struct iphdr *iph; 374891ef8ddSDavid Lebrun __be32 nhaddr; 375891ef8ddSDavid Lebrun int err; 376891ef8ddSDavid Lebrun 377891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPIP)) 378891ef8ddSDavid Lebrun goto drop; 379891ef8ddSDavid Lebrun 380891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct iphdr))) 381891ef8ddSDavid Lebrun goto drop; 382891ef8ddSDavid Lebrun 383891ef8ddSDavid Lebrun skb->protocol = htons(ETH_P_IP); 384891ef8ddSDavid Lebrun 385891ef8ddSDavid Lebrun iph = ip_hdr(skb); 386891ef8ddSDavid Lebrun 387891ef8ddSDavid Lebrun nhaddr = slwt->nh4.s_addr ?: iph->daddr; 388891ef8ddSDavid Lebrun 389891ef8ddSDavid Lebrun skb_dst_drop(skb); 390891ef8ddSDavid Lebrun 391c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct iphdr)); 392c71644d0SAndrea Mayer 393891ef8ddSDavid Lebrun err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev); 394891ef8ddSDavid Lebrun if (err) 395891ef8ddSDavid Lebrun goto drop; 396891ef8ddSDavid Lebrun 397891ef8ddSDavid Lebrun return dst_input(skb); 398891ef8ddSDavid Lebrun 399891ef8ddSDavid Lebrun drop: 400891ef8ddSDavid Lebrun kfree_skb(skb); 401891ef8ddSDavid Lebrun return -EINVAL; 402891ef8ddSDavid Lebrun } 403891ef8ddSDavid Lebrun 404891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb, 405891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 406891ef8ddSDavid Lebrun { 407891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 408891ef8ddSDavid Lebrun goto drop; 409891ef8ddSDavid Lebrun 410891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 411891ef8ddSDavid Lebrun goto drop; 412891ef8ddSDavid Lebrun 413c71644d0SAndrea Mayer skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 414c71644d0SAndrea Mayer 415fd1fef0cSAndrea Mayer seg6_lookup_any_nexthop(skb, NULL, slwt->table, true); 416891ef8ddSDavid Lebrun 417891ef8ddSDavid Lebrun return dst_input(skb); 418891ef8ddSDavid Lebrun 419891ef8ddSDavid Lebrun drop: 420891ef8ddSDavid Lebrun kfree_skb(skb); 421891ef8ddSDavid Lebrun return -EINVAL; 422891ef8ddSDavid Lebrun } 423891ef8ddSDavid Lebrun 424140f04c3SDavid Lebrun /* push an SRH on top of the current one */ 425140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 426140f04c3SDavid Lebrun { 427140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 428140f04c3SDavid Lebrun int err = -EINVAL; 429140f04c3SDavid Lebrun 430140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 431140f04c3SDavid Lebrun if (!srh) 432140f04c3SDavid Lebrun goto drop; 433140f04c3SDavid Lebrun 434140f04c3SDavid Lebrun err = seg6_do_srh_inline(skb, slwt->srh); 435140f04c3SDavid Lebrun if (err) 436140f04c3SDavid Lebrun goto drop; 437140f04c3SDavid Lebrun 438140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 439140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 440140f04c3SDavid Lebrun 4411c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 442140f04c3SDavid Lebrun 443140f04c3SDavid Lebrun return dst_input(skb); 444140f04c3SDavid Lebrun 445140f04c3SDavid Lebrun drop: 446140f04c3SDavid Lebrun kfree_skb(skb); 447140f04c3SDavid Lebrun return err; 448140f04c3SDavid Lebrun } 449140f04c3SDavid Lebrun 450140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */ 451140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb, 452140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 453140f04c3SDavid Lebrun { 454140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 455140f04c3SDavid Lebrun int err = -EINVAL; 456140f04c3SDavid Lebrun 457140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 458140f04c3SDavid Lebrun if (!srh) 459140f04c3SDavid Lebrun goto drop; 460140f04c3SDavid Lebrun 461d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 462140f04c3SDavid Lebrun 463140f04c3SDavid Lebrun skb_reset_inner_headers(skb); 464140f04c3SDavid Lebrun skb->encapsulation = 1; 465140f04c3SDavid Lebrun 46632d99d0bSDavid Lebrun err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6); 467140f04c3SDavid Lebrun if (err) 468140f04c3SDavid Lebrun goto drop; 469140f04c3SDavid Lebrun 470140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 471140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 472140f04c3SDavid Lebrun 4731c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 474140f04c3SDavid Lebrun 475140f04c3SDavid Lebrun return dst_input(skb); 476140f04c3SDavid Lebrun 477140f04c3SDavid Lebrun drop: 478140f04c3SDavid Lebrun kfree_skb(skb); 479140f04c3SDavid Lebrun return err; 480140f04c3SDavid Lebrun } 481140f04c3SDavid Lebrun 482fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states); 483fe94cc29SMathieu Xhonneux 484486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb) 485486cdf21SMathieu Xhonneux { 486486cdf21SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 487486cdf21SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 488486cdf21SMathieu Xhonneux struct ipv6_sr_hdr *srh = srh_state->srh; 489486cdf21SMathieu Xhonneux 490486cdf21SMathieu Xhonneux if (unlikely(srh == NULL)) 491486cdf21SMathieu Xhonneux return false; 492486cdf21SMathieu Xhonneux 493486cdf21SMathieu Xhonneux if (unlikely(!srh_state->valid)) { 494486cdf21SMathieu Xhonneux if ((srh_state->hdrlen & 7) != 0) 495486cdf21SMathieu Xhonneux return false; 496486cdf21SMathieu Xhonneux 497486cdf21SMathieu Xhonneux srh->hdrlen = (u8)(srh_state->hdrlen >> 3); 498*bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true)) 499486cdf21SMathieu Xhonneux return false; 500486cdf21SMathieu Xhonneux 501486cdf21SMathieu Xhonneux srh_state->valid = true; 502486cdf21SMathieu Xhonneux } 503486cdf21SMathieu Xhonneux 504486cdf21SMathieu Xhonneux return true; 505486cdf21SMathieu Xhonneux } 506486cdf21SMathieu Xhonneux 507004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb, 508004d4b27SMathieu Xhonneux struct seg6_local_lwt *slwt) 509004d4b27SMathieu Xhonneux { 510004d4b27SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 511004d4b27SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 512004d4b27SMathieu Xhonneux struct ipv6_sr_hdr *srh; 513004d4b27SMathieu Xhonneux int ret; 514004d4b27SMathieu Xhonneux 515004d4b27SMathieu Xhonneux srh = get_and_validate_srh(skb); 516486cdf21SMathieu Xhonneux if (!srh) { 517486cdf21SMathieu Xhonneux kfree_skb(skb); 518486cdf21SMathieu Xhonneux return -EINVAL; 519486cdf21SMathieu Xhonneux } 520004d4b27SMathieu Xhonneux advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 521004d4b27SMathieu Xhonneux 522004d4b27SMathieu Xhonneux /* preempt_disable is needed to protect the per-CPU buffer srh_state, 523004d4b27SMathieu Xhonneux * which is also accessed by the bpf_lwt_seg6_* helpers 524004d4b27SMathieu Xhonneux */ 525004d4b27SMathieu Xhonneux preempt_disable(); 526486cdf21SMathieu Xhonneux srh_state->srh = srh; 527004d4b27SMathieu Xhonneux srh_state->hdrlen = srh->hdrlen << 3; 528486cdf21SMathieu Xhonneux srh_state->valid = true; 529004d4b27SMathieu Xhonneux 530004d4b27SMathieu Xhonneux rcu_read_lock(); 531004d4b27SMathieu Xhonneux bpf_compute_data_pointers(skb); 532004d4b27SMathieu Xhonneux ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb); 533004d4b27SMathieu Xhonneux rcu_read_unlock(); 534004d4b27SMathieu Xhonneux 535004d4b27SMathieu Xhonneux switch (ret) { 536004d4b27SMathieu Xhonneux case BPF_OK: 537004d4b27SMathieu Xhonneux case BPF_REDIRECT: 538004d4b27SMathieu Xhonneux break; 539004d4b27SMathieu Xhonneux case BPF_DROP: 540004d4b27SMathieu Xhonneux goto drop; 541004d4b27SMathieu Xhonneux default: 542004d4b27SMathieu Xhonneux pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret); 543004d4b27SMathieu Xhonneux goto drop; 544004d4b27SMathieu Xhonneux } 545004d4b27SMathieu Xhonneux 546486cdf21SMathieu Xhonneux if (srh_state->srh && !seg6_bpf_has_valid_srh(skb)) 547004d4b27SMathieu Xhonneux goto drop; 548004d4b27SMathieu Xhonneux 549486cdf21SMathieu Xhonneux preempt_enable(); 550004d4b27SMathieu Xhonneux if (ret != BPF_REDIRECT) 551004d4b27SMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 552004d4b27SMathieu Xhonneux 553004d4b27SMathieu Xhonneux return dst_input(skb); 554004d4b27SMathieu Xhonneux 555004d4b27SMathieu Xhonneux drop: 556486cdf21SMathieu Xhonneux preempt_enable(); 557004d4b27SMathieu Xhonneux kfree_skb(skb); 558004d4b27SMathieu Xhonneux return -EINVAL; 559004d4b27SMathieu Xhonneux } 560004d4b27SMathieu Xhonneux 561d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = { 562d1df6fd8SDavid Lebrun { 563d1df6fd8SDavid Lebrun .action = SEG6_LOCAL_ACTION_END, 564d1df6fd8SDavid Lebrun .attrs = 0, 565140f04c3SDavid Lebrun .input = input_action_end, 566d1df6fd8SDavid Lebrun }, 567140f04c3SDavid Lebrun { 568140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_X, 569140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH6), 570140f04c3SDavid Lebrun .input = input_action_end_x, 571140f04c3SDavid Lebrun }, 572140f04c3SDavid Lebrun { 573891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_T, 574891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_TABLE), 575891ef8ddSDavid Lebrun .input = input_action_end_t, 576891ef8ddSDavid Lebrun }, 577891ef8ddSDavid Lebrun { 578891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX2, 579891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_OIF), 580891ef8ddSDavid Lebrun .input = input_action_end_dx2, 581891ef8ddSDavid Lebrun }, 582891ef8ddSDavid Lebrun { 583140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX6, 584140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH6), 585140f04c3SDavid Lebrun .input = input_action_end_dx6, 586140f04c3SDavid Lebrun }, 587140f04c3SDavid Lebrun { 588891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX4, 589891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH4), 590891ef8ddSDavid Lebrun .input = input_action_end_dx4, 591891ef8ddSDavid Lebrun }, 592891ef8ddSDavid Lebrun { 593891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DT6, 594891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_TABLE), 595891ef8ddSDavid Lebrun .input = input_action_end_dt6, 596891ef8ddSDavid Lebrun }, 597891ef8ddSDavid Lebrun { 598140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6, 599140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_SRH), 600140f04c3SDavid Lebrun .input = input_action_end_b6, 601140f04c3SDavid Lebrun }, 602140f04c3SDavid Lebrun { 603140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6_ENCAP, 604140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_SRH), 605140f04c3SDavid Lebrun .input = input_action_end_b6_encap, 606140f04c3SDavid Lebrun .static_headroom = sizeof(struct ipv6hdr), 607004d4b27SMathieu Xhonneux }, 608004d4b27SMathieu Xhonneux { 609004d4b27SMathieu Xhonneux .action = SEG6_LOCAL_ACTION_END_BPF, 610004d4b27SMathieu Xhonneux .attrs = (1 << SEG6_LOCAL_BPF), 611004d4b27SMathieu Xhonneux .input = input_action_end_bpf, 612004d4b27SMathieu Xhonneux }, 613004d4b27SMathieu Xhonneux 614d1df6fd8SDavid Lebrun }; 615d1df6fd8SDavid Lebrun 616d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action) 617d1df6fd8SDavid Lebrun { 618d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 619d1df6fd8SDavid Lebrun int i, count; 620d1df6fd8SDavid Lebrun 621709af180SColin Ian King count = ARRAY_SIZE(seg6_action_table); 622d1df6fd8SDavid Lebrun for (i = 0; i < count; i++) { 623d1df6fd8SDavid Lebrun desc = &seg6_action_table[i]; 624d1df6fd8SDavid Lebrun if (desc->action == action) 625d1df6fd8SDavid Lebrun return desc; 626d1df6fd8SDavid Lebrun } 627d1df6fd8SDavid Lebrun 628d1df6fd8SDavid Lebrun return NULL; 629d1df6fd8SDavid Lebrun } 630d1df6fd8SDavid Lebrun 631d1df6fd8SDavid Lebrun static int seg6_local_input(struct sk_buff *skb) 632d1df6fd8SDavid Lebrun { 633d1df6fd8SDavid Lebrun struct dst_entry *orig_dst = skb_dst(skb); 634d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 635d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 636d1df6fd8SDavid Lebrun 6376285217fSDavid Lebrun if (skb->protocol != htons(ETH_P_IPV6)) { 6386285217fSDavid Lebrun kfree_skb(skb); 6396285217fSDavid Lebrun return -EINVAL; 6406285217fSDavid Lebrun } 6416285217fSDavid Lebrun 642d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 643d1df6fd8SDavid Lebrun desc = slwt->desc; 644d1df6fd8SDavid Lebrun 645d1df6fd8SDavid Lebrun return desc->input(skb, slwt); 646d1df6fd8SDavid Lebrun } 647d1df6fd8SDavid Lebrun 648d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { 649d1df6fd8SDavid Lebrun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, 650d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, 651d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, 652d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, 653d1df6fd8SDavid Lebrun .len = sizeof(struct in_addr) }, 654d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, 655d1df6fd8SDavid Lebrun .len = sizeof(struct in6_addr) }, 656d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, 657d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 658004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .type = NLA_NESTED }, 659d1df6fd8SDavid Lebrun }; 660d1df6fd8SDavid Lebrun 6612d9cc60aSDavid Lebrun static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt) 6622d9cc60aSDavid Lebrun { 6632d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 6642d9cc60aSDavid Lebrun int len; 6652d9cc60aSDavid Lebrun 6662d9cc60aSDavid Lebrun srh = nla_data(attrs[SEG6_LOCAL_SRH]); 6672d9cc60aSDavid Lebrun len = nla_len(attrs[SEG6_LOCAL_SRH]); 6682d9cc60aSDavid Lebrun 6692d9cc60aSDavid Lebrun /* SRH must contain at least one segment */ 6702d9cc60aSDavid Lebrun if (len < sizeof(*srh) + sizeof(struct in6_addr)) 6712d9cc60aSDavid Lebrun return -EINVAL; 6722d9cc60aSDavid Lebrun 673*bb986a50SAhmed Abdelsalam if (!seg6_validate_srh(srh, len, false)) 6742d9cc60aSDavid Lebrun return -EINVAL; 6752d9cc60aSDavid Lebrun 6767fa41efaSYueHaibing slwt->srh = kmemdup(srh, len, GFP_KERNEL); 6772d9cc60aSDavid Lebrun if (!slwt->srh) 6782d9cc60aSDavid Lebrun return -ENOMEM; 6792d9cc60aSDavid Lebrun 6802d9cc60aSDavid Lebrun slwt->headroom += len; 6812d9cc60aSDavid Lebrun 6822d9cc60aSDavid Lebrun return 0; 6832d9cc60aSDavid Lebrun } 6842d9cc60aSDavid Lebrun 6852d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt) 6862d9cc60aSDavid Lebrun { 6872d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 6882d9cc60aSDavid Lebrun struct nlattr *nla; 6892d9cc60aSDavid Lebrun int len; 6902d9cc60aSDavid Lebrun 6912d9cc60aSDavid Lebrun srh = slwt->srh; 6922d9cc60aSDavid Lebrun len = (srh->hdrlen + 1) << 3; 6932d9cc60aSDavid Lebrun 6942d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_SRH, len); 6952d9cc60aSDavid Lebrun if (!nla) 6962d9cc60aSDavid Lebrun return -EMSGSIZE; 6972d9cc60aSDavid Lebrun 6982d9cc60aSDavid Lebrun memcpy(nla_data(nla), srh, len); 6992d9cc60aSDavid Lebrun 7002d9cc60aSDavid Lebrun return 0; 7012d9cc60aSDavid Lebrun } 7022d9cc60aSDavid Lebrun 7032d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7042d9cc60aSDavid Lebrun { 7052d9cc60aSDavid Lebrun int len = (a->srh->hdrlen + 1) << 3; 7062d9cc60aSDavid Lebrun 7072d9cc60aSDavid Lebrun if (len != ((b->srh->hdrlen + 1) << 3)) 7082d9cc60aSDavid Lebrun return 1; 7092d9cc60aSDavid Lebrun 7102d9cc60aSDavid Lebrun return memcmp(a->srh, b->srh, len); 7112d9cc60aSDavid Lebrun } 7122d9cc60aSDavid Lebrun 7132d9cc60aSDavid Lebrun static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7142d9cc60aSDavid Lebrun { 7152d9cc60aSDavid Lebrun slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]); 7162d9cc60aSDavid Lebrun 7172d9cc60aSDavid Lebrun return 0; 7182d9cc60aSDavid Lebrun } 7192d9cc60aSDavid Lebrun 7202d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7212d9cc60aSDavid Lebrun { 7222d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table)) 7232d9cc60aSDavid Lebrun return -EMSGSIZE; 7242d9cc60aSDavid Lebrun 7252d9cc60aSDavid Lebrun return 0; 7262d9cc60aSDavid Lebrun } 7272d9cc60aSDavid Lebrun 7282d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7292d9cc60aSDavid Lebrun { 7302d9cc60aSDavid Lebrun if (a->table != b->table) 7312d9cc60aSDavid Lebrun return 1; 7322d9cc60aSDavid Lebrun 7332d9cc60aSDavid Lebrun return 0; 7342d9cc60aSDavid Lebrun } 7352d9cc60aSDavid Lebrun 7362d9cc60aSDavid Lebrun static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7372d9cc60aSDavid Lebrun { 7382d9cc60aSDavid Lebrun memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]), 7392d9cc60aSDavid Lebrun sizeof(struct in_addr)); 7402d9cc60aSDavid Lebrun 7412d9cc60aSDavid Lebrun return 0; 7422d9cc60aSDavid Lebrun } 7432d9cc60aSDavid Lebrun 7442d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7452d9cc60aSDavid Lebrun { 7462d9cc60aSDavid Lebrun struct nlattr *nla; 7472d9cc60aSDavid Lebrun 7482d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr)); 7492d9cc60aSDavid Lebrun if (!nla) 7502d9cc60aSDavid Lebrun return -EMSGSIZE; 7512d9cc60aSDavid Lebrun 7522d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr)); 7532d9cc60aSDavid Lebrun 7542d9cc60aSDavid Lebrun return 0; 7552d9cc60aSDavid Lebrun } 7562d9cc60aSDavid Lebrun 7572d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7582d9cc60aSDavid Lebrun { 7592d9cc60aSDavid Lebrun return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr)); 7602d9cc60aSDavid Lebrun } 7612d9cc60aSDavid Lebrun 7622d9cc60aSDavid Lebrun static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7632d9cc60aSDavid Lebrun { 7642d9cc60aSDavid Lebrun memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]), 7652d9cc60aSDavid Lebrun sizeof(struct in6_addr)); 7662d9cc60aSDavid Lebrun 7672d9cc60aSDavid Lebrun return 0; 7682d9cc60aSDavid Lebrun } 7692d9cc60aSDavid Lebrun 7702d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7712d9cc60aSDavid Lebrun { 7722d9cc60aSDavid Lebrun struct nlattr *nla; 7732d9cc60aSDavid Lebrun 7742d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr)); 7752d9cc60aSDavid Lebrun if (!nla) 7762d9cc60aSDavid Lebrun return -EMSGSIZE; 7772d9cc60aSDavid Lebrun 7782d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr)); 7792d9cc60aSDavid Lebrun 7802d9cc60aSDavid Lebrun return 0; 7812d9cc60aSDavid Lebrun } 7822d9cc60aSDavid Lebrun 7832d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7842d9cc60aSDavid Lebrun { 7852d9cc60aSDavid Lebrun return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr)); 7862d9cc60aSDavid Lebrun } 7872d9cc60aSDavid Lebrun 7882d9cc60aSDavid Lebrun static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7892d9cc60aSDavid Lebrun { 7902d9cc60aSDavid Lebrun slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]); 7912d9cc60aSDavid Lebrun 7922d9cc60aSDavid Lebrun return 0; 7932d9cc60aSDavid Lebrun } 7942d9cc60aSDavid Lebrun 7952d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7962d9cc60aSDavid Lebrun { 7972d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif)) 7982d9cc60aSDavid Lebrun return -EMSGSIZE; 7992d9cc60aSDavid Lebrun 8002d9cc60aSDavid Lebrun return 0; 8012d9cc60aSDavid Lebrun } 8022d9cc60aSDavid Lebrun 8032d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 8042d9cc60aSDavid Lebrun { 8052d9cc60aSDavid Lebrun if (a->iif != b->iif) 8062d9cc60aSDavid Lebrun return 1; 8072d9cc60aSDavid Lebrun 8082d9cc60aSDavid Lebrun return 0; 8092d9cc60aSDavid Lebrun } 8102d9cc60aSDavid Lebrun 8112d9cc60aSDavid Lebrun static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 8122d9cc60aSDavid Lebrun { 8132d9cc60aSDavid Lebrun slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]); 8142d9cc60aSDavid Lebrun 8152d9cc60aSDavid Lebrun return 0; 8162d9cc60aSDavid Lebrun } 8172d9cc60aSDavid Lebrun 8182d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 8192d9cc60aSDavid Lebrun { 8202d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif)) 8212d9cc60aSDavid Lebrun return -EMSGSIZE; 8222d9cc60aSDavid Lebrun 8232d9cc60aSDavid Lebrun return 0; 8242d9cc60aSDavid Lebrun } 8252d9cc60aSDavid Lebrun 8262d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 8272d9cc60aSDavid Lebrun { 8282d9cc60aSDavid Lebrun if (a->oif != b->oif) 8292d9cc60aSDavid Lebrun return 1; 8302d9cc60aSDavid Lebrun 8312d9cc60aSDavid Lebrun return 0; 8322d9cc60aSDavid Lebrun } 8332d9cc60aSDavid Lebrun 834004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256 835004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = { 836004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG] = { .type = NLA_U32, }, 837004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING, 838004d4b27SMathieu Xhonneux .len = MAX_PROG_NAME }, 839004d4b27SMathieu Xhonneux }; 840004d4b27SMathieu Xhonneux 841004d4b27SMathieu Xhonneux static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt) 842004d4b27SMathieu Xhonneux { 843004d4b27SMathieu Xhonneux struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1]; 844004d4b27SMathieu Xhonneux struct bpf_prog *p; 845004d4b27SMathieu Xhonneux int ret; 846004d4b27SMathieu Xhonneux u32 fd; 847004d4b27SMathieu Xhonneux 8488cb08174SJohannes Berg ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX, 8498cb08174SJohannes Berg attrs[SEG6_LOCAL_BPF], 8508cb08174SJohannes Berg bpf_prog_policy, NULL); 851004d4b27SMathieu Xhonneux if (ret < 0) 852004d4b27SMathieu Xhonneux return ret; 853004d4b27SMathieu Xhonneux 854004d4b27SMathieu Xhonneux if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME]) 855004d4b27SMathieu Xhonneux return -EINVAL; 856004d4b27SMathieu Xhonneux 857004d4b27SMathieu Xhonneux slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL); 858004d4b27SMathieu Xhonneux if (!slwt->bpf.name) 859004d4b27SMathieu Xhonneux return -ENOMEM; 860004d4b27SMathieu Xhonneux 861004d4b27SMathieu Xhonneux fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]); 862004d4b27SMathieu Xhonneux p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL); 863004d4b27SMathieu Xhonneux if (IS_ERR(p)) { 864004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 865004d4b27SMathieu Xhonneux return PTR_ERR(p); 866004d4b27SMathieu Xhonneux } 867004d4b27SMathieu Xhonneux 868004d4b27SMathieu Xhonneux slwt->bpf.prog = p; 869004d4b27SMathieu Xhonneux return 0; 870004d4b27SMathieu Xhonneux } 871004d4b27SMathieu Xhonneux 872004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt) 873004d4b27SMathieu Xhonneux { 874004d4b27SMathieu Xhonneux struct nlattr *nest; 875004d4b27SMathieu Xhonneux 876004d4b27SMathieu Xhonneux if (!slwt->bpf.prog) 877004d4b27SMathieu Xhonneux return 0; 878004d4b27SMathieu Xhonneux 879ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF); 880004d4b27SMathieu Xhonneux if (!nest) 881004d4b27SMathieu Xhonneux return -EMSGSIZE; 882004d4b27SMathieu Xhonneux 883004d4b27SMathieu Xhonneux if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id)) 884004d4b27SMathieu Xhonneux return -EMSGSIZE; 885004d4b27SMathieu Xhonneux 886004d4b27SMathieu Xhonneux if (slwt->bpf.name && 887004d4b27SMathieu Xhonneux nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name)) 888004d4b27SMathieu Xhonneux return -EMSGSIZE; 889004d4b27SMathieu Xhonneux 890004d4b27SMathieu Xhonneux return nla_nest_end(skb, nest); 891004d4b27SMathieu Xhonneux } 892004d4b27SMathieu Xhonneux 893004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 894004d4b27SMathieu Xhonneux { 895004d4b27SMathieu Xhonneux if (!a->bpf.name && !b->bpf.name) 896004d4b27SMathieu Xhonneux return 0; 897004d4b27SMathieu Xhonneux 898004d4b27SMathieu Xhonneux if (!a->bpf.name || !b->bpf.name) 899004d4b27SMathieu Xhonneux return 1; 900004d4b27SMathieu Xhonneux 901004d4b27SMathieu Xhonneux return strcmp(a->bpf.name, b->bpf.name); 902004d4b27SMathieu Xhonneux } 903004d4b27SMathieu Xhonneux 904d1df6fd8SDavid Lebrun struct seg6_action_param { 905d1df6fd8SDavid Lebrun int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt); 906d1df6fd8SDavid Lebrun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 907d1df6fd8SDavid Lebrun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); 908d1df6fd8SDavid Lebrun }; 909d1df6fd8SDavid Lebrun 910d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { 9112d9cc60aSDavid Lebrun [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh, 9122d9cc60aSDavid Lebrun .put = put_nla_srh, 9132d9cc60aSDavid Lebrun .cmp = cmp_nla_srh }, 914d1df6fd8SDavid Lebrun 9152d9cc60aSDavid Lebrun [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table, 9162d9cc60aSDavid Lebrun .put = put_nla_table, 9172d9cc60aSDavid Lebrun .cmp = cmp_nla_table }, 918d1df6fd8SDavid Lebrun 9192d9cc60aSDavid Lebrun [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4, 9202d9cc60aSDavid Lebrun .put = put_nla_nh4, 9212d9cc60aSDavid Lebrun .cmp = cmp_nla_nh4 }, 922d1df6fd8SDavid Lebrun 9232d9cc60aSDavid Lebrun [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6, 9242d9cc60aSDavid Lebrun .put = put_nla_nh6, 9252d9cc60aSDavid Lebrun .cmp = cmp_nla_nh6 }, 926d1df6fd8SDavid Lebrun 9272d9cc60aSDavid Lebrun [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif, 9282d9cc60aSDavid Lebrun .put = put_nla_iif, 9292d9cc60aSDavid Lebrun .cmp = cmp_nla_iif }, 930d1df6fd8SDavid Lebrun 9312d9cc60aSDavid Lebrun [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif, 9322d9cc60aSDavid Lebrun .put = put_nla_oif, 9332d9cc60aSDavid Lebrun .cmp = cmp_nla_oif }, 934004d4b27SMathieu Xhonneux 935004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .parse = parse_nla_bpf, 936004d4b27SMathieu Xhonneux .put = put_nla_bpf, 937004d4b27SMathieu Xhonneux .cmp = cmp_nla_bpf }, 938004d4b27SMathieu Xhonneux 939d1df6fd8SDavid Lebrun }; 940d1df6fd8SDavid Lebrun 941d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) 942d1df6fd8SDavid Lebrun { 943d1df6fd8SDavid Lebrun struct seg6_action_param *param; 944d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 945d1df6fd8SDavid Lebrun int i, err; 946d1df6fd8SDavid Lebrun 947d1df6fd8SDavid Lebrun desc = __get_action_desc(slwt->action); 948d1df6fd8SDavid Lebrun if (!desc) 949d1df6fd8SDavid Lebrun return -EINVAL; 950d1df6fd8SDavid Lebrun 951d1df6fd8SDavid Lebrun if (!desc->input) 952d1df6fd8SDavid Lebrun return -EOPNOTSUPP; 953d1df6fd8SDavid Lebrun 954d1df6fd8SDavid Lebrun slwt->desc = desc; 955d1df6fd8SDavid Lebrun slwt->headroom += desc->static_headroom; 956d1df6fd8SDavid Lebrun 957d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 958d1df6fd8SDavid Lebrun if (desc->attrs & (1 << i)) { 959d1df6fd8SDavid Lebrun if (!attrs[i]) 960d1df6fd8SDavid Lebrun return -EINVAL; 961d1df6fd8SDavid Lebrun 962d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 963d1df6fd8SDavid Lebrun 964d1df6fd8SDavid Lebrun err = param->parse(attrs, slwt); 965d1df6fd8SDavid Lebrun if (err < 0) 966d1df6fd8SDavid Lebrun return err; 967d1df6fd8SDavid Lebrun } 968d1df6fd8SDavid Lebrun } 969d1df6fd8SDavid Lebrun 970d1df6fd8SDavid Lebrun return 0; 971d1df6fd8SDavid Lebrun } 972d1df6fd8SDavid Lebrun 973faee6769SAlexander Aring static int seg6_local_build_state(struct net *net, struct nlattr *nla, 974faee6769SAlexander Aring unsigned int family, const void *cfg, 975faee6769SAlexander Aring struct lwtunnel_state **ts, 976d1df6fd8SDavid Lebrun struct netlink_ext_ack *extack) 977d1df6fd8SDavid Lebrun { 978d1df6fd8SDavid Lebrun struct nlattr *tb[SEG6_LOCAL_MAX + 1]; 979d1df6fd8SDavid Lebrun struct lwtunnel_state *newts; 980d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 981d1df6fd8SDavid Lebrun int err; 982d1df6fd8SDavid Lebrun 9836285217fSDavid Lebrun if (family != AF_INET6) 9846285217fSDavid Lebrun return -EINVAL; 9856285217fSDavid Lebrun 9868cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla, 9878cb08174SJohannes Berg seg6_local_policy, extack); 988d1df6fd8SDavid Lebrun 989d1df6fd8SDavid Lebrun if (err < 0) 990d1df6fd8SDavid Lebrun return err; 991d1df6fd8SDavid Lebrun 992d1df6fd8SDavid Lebrun if (!tb[SEG6_LOCAL_ACTION]) 993d1df6fd8SDavid Lebrun return -EINVAL; 994d1df6fd8SDavid Lebrun 995d1df6fd8SDavid Lebrun newts = lwtunnel_state_alloc(sizeof(*slwt)); 996d1df6fd8SDavid Lebrun if (!newts) 997d1df6fd8SDavid Lebrun return -ENOMEM; 998d1df6fd8SDavid Lebrun 999d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(newts); 1000d1df6fd8SDavid Lebrun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); 1001d1df6fd8SDavid Lebrun 1002d1df6fd8SDavid Lebrun err = parse_nla_action(tb, slwt); 1003d1df6fd8SDavid Lebrun if (err < 0) 1004d1df6fd8SDavid Lebrun goto out_free; 1005d1df6fd8SDavid Lebrun 1006d1df6fd8SDavid Lebrun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; 1007d1df6fd8SDavid Lebrun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; 1008d1df6fd8SDavid Lebrun newts->headroom = slwt->headroom; 1009d1df6fd8SDavid Lebrun 1010d1df6fd8SDavid Lebrun *ts = newts; 1011d1df6fd8SDavid Lebrun 1012d1df6fd8SDavid Lebrun return 0; 1013d1df6fd8SDavid Lebrun 1014d1df6fd8SDavid Lebrun out_free: 1015d1df6fd8SDavid Lebrun kfree(slwt->srh); 1016d1df6fd8SDavid Lebrun kfree(newts); 1017d1df6fd8SDavid Lebrun return err; 1018d1df6fd8SDavid Lebrun } 1019d1df6fd8SDavid Lebrun 1020d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt) 1021d1df6fd8SDavid Lebrun { 1022d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1023d1df6fd8SDavid Lebrun 1024d1df6fd8SDavid Lebrun kfree(slwt->srh); 1025004d4b27SMathieu Xhonneux 1026004d4b27SMathieu Xhonneux if (slwt->desc->attrs & (1 << SEG6_LOCAL_BPF)) { 1027004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 1028004d4b27SMathieu Xhonneux bpf_prog_put(slwt->bpf.prog); 1029004d4b27SMathieu Xhonneux } 1030004d4b27SMathieu Xhonneux 1031004d4b27SMathieu Xhonneux return; 1032d1df6fd8SDavid Lebrun } 1033d1df6fd8SDavid Lebrun 1034d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb, 1035d1df6fd8SDavid Lebrun struct lwtunnel_state *lwt) 1036d1df6fd8SDavid Lebrun { 1037d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1038d1df6fd8SDavid Lebrun struct seg6_action_param *param; 1039d1df6fd8SDavid Lebrun int i, err; 1040d1df6fd8SDavid Lebrun 1041d1df6fd8SDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) 1042d1df6fd8SDavid Lebrun return -EMSGSIZE; 1043d1df6fd8SDavid Lebrun 1044d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1045d1df6fd8SDavid Lebrun if (slwt->desc->attrs & (1 << i)) { 1046d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1047d1df6fd8SDavid Lebrun err = param->put(skb, slwt); 1048d1df6fd8SDavid Lebrun if (err < 0) 1049d1df6fd8SDavid Lebrun return err; 1050d1df6fd8SDavid Lebrun } 1051d1df6fd8SDavid Lebrun } 1052d1df6fd8SDavid Lebrun 1053d1df6fd8SDavid Lebrun return 0; 1054d1df6fd8SDavid Lebrun } 1055d1df6fd8SDavid Lebrun 1056d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) 1057d1df6fd8SDavid Lebrun { 1058d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1059d1df6fd8SDavid Lebrun unsigned long attrs; 1060d1df6fd8SDavid Lebrun int nlsize; 1061d1df6fd8SDavid Lebrun 1062d1df6fd8SDavid Lebrun nlsize = nla_total_size(4); /* action */ 1063d1df6fd8SDavid Lebrun 1064d1df6fd8SDavid Lebrun attrs = slwt->desc->attrs; 1065d1df6fd8SDavid Lebrun 1066d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_SRH)) 1067d1df6fd8SDavid Lebrun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); 1068d1df6fd8SDavid Lebrun 1069d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_TABLE)) 1070d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1071d1df6fd8SDavid Lebrun 1072d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_NH4)) 1073d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1074d1df6fd8SDavid Lebrun 1075d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_NH6)) 1076d1df6fd8SDavid Lebrun nlsize += nla_total_size(16); 1077d1df6fd8SDavid Lebrun 1078d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_IIF)) 1079d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1080d1df6fd8SDavid Lebrun 1081d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_OIF)) 1082d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1083d1df6fd8SDavid Lebrun 1084004d4b27SMathieu Xhonneux if (attrs & (1 << SEG6_LOCAL_BPF)) 1085004d4b27SMathieu Xhonneux nlsize += nla_total_size(sizeof(struct nlattr)) + 1086004d4b27SMathieu Xhonneux nla_total_size(MAX_PROG_NAME) + 1087004d4b27SMathieu Xhonneux nla_total_size(4); 1088004d4b27SMathieu Xhonneux 1089d1df6fd8SDavid Lebrun return nlsize; 1090d1df6fd8SDavid Lebrun } 1091d1df6fd8SDavid Lebrun 1092d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a, 1093d1df6fd8SDavid Lebrun struct lwtunnel_state *b) 1094d1df6fd8SDavid Lebrun { 1095d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt_a, *slwt_b; 1096d1df6fd8SDavid Lebrun struct seg6_action_param *param; 1097d1df6fd8SDavid Lebrun int i; 1098d1df6fd8SDavid Lebrun 1099d1df6fd8SDavid Lebrun slwt_a = seg6_local_lwtunnel(a); 1100d1df6fd8SDavid Lebrun slwt_b = seg6_local_lwtunnel(b); 1101d1df6fd8SDavid Lebrun 1102d1df6fd8SDavid Lebrun if (slwt_a->action != slwt_b->action) 1103d1df6fd8SDavid Lebrun return 1; 1104d1df6fd8SDavid Lebrun 1105d1df6fd8SDavid Lebrun if (slwt_a->desc->attrs != slwt_b->desc->attrs) 1106d1df6fd8SDavid Lebrun return 1; 1107d1df6fd8SDavid Lebrun 1108d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1109d1df6fd8SDavid Lebrun if (slwt_a->desc->attrs & (1 << i)) { 1110d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1111d1df6fd8SDavid Lebrun if (param->cmp(slwt_a, slwt_b)) 1112d1df6fd8SDavid Lebrun return 1; 1113d1df6fd8SDavid Lebrun } 1114d1df6fd8SDavid Lebrun } 1115d1df6fd8SDavid Lebrun 1116d1df6fd8SDavid Lebrun return 0; 1117d1df6fd8SDavid Lebrun } 1118d1df6fd8SDavid Lebrun 1119d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = { 1120d1df6fd8SDavid Lebrun .build_state = seg6_local_build_state, 1121d1df6fd8SDavid Lebrun .destroy_state = seg6_local_destroy_state, 1122d1df6fd8SDavid Lebrun .input = seg6_local_input, 1123d1df6fd8SDavid Lebrun .fill_encap = seg6_local_fill_encap, 1124d1df6fd8SDavid Lebrun .get_encap_size = seg6_local_get_encap_size, 1125d1df6fd8SDavid Lebrun .cmp_encap = seg6_local_cmp_encap, 1126d1df6fd8SDavid Lebrun .owner = THIS_MODULE, 1127d1df6fd8SDavid Lebrun }; 1128d1df6fd8SDavid Lebrun 1129d1df6fd8SDavid Lebrun int __init seg6_local_init(void) 1130d1df6fd8SDavid Lebrun { 1131d1df6fd8SDavid Lebrun return lwtunnel_encap_add_ops(&seg6_local_ops, 1132d1df6fd8SDavid Lebrun LWTUNNEL_ENCAP_SEG6_LOCAL); 1133d1df6fd8SDavid Lebrun } 1134d1df6fd8SDavid Lebrun 1135d1df6fd8SDavid Lebrun void seg6_local_exit(void) 1136d1df6fd8SDavid Lebrun { 1137d1df6fd8SDavid Lebrun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); 1138d1df6fd8SDavid Lebrun } 1139