1d1df6fd8SDavid Lebrun /* 2d1df6fd8SDavid Lebrun * SR-IPv6 implementation 3d1df6fd8SDavid Lebrun * 4004d4b27SMathieu Xhonneux * Authors: 5d1df6fd8SDavid Lebrun * David Lebrun <david.lebrun@uclouvain.be> 6004d4b27SMathieu Xhonneux * eBPF support: Mathieu Xhonneux <m.xhonneux@gmail.com> 7d1df6fd8SDavid Lebrun * 8d1df6fd8SDavid Lebrun * 9d1df6fd8SDavid Lebrun * This program is free software; you can redistribute it and/or 10d1df6fd8SDavid Lebrun * modify it under the terms of the GNU General Public License 11d1df6fd8SDavid Lebrun * as published by the Free Software Foundation; either version 12d1df6fd8SDavid Lebrun * 2 of the License, or (at your option) any later version. 13d1df6fd8SDavid Lebrun */ 14d1df6fd8SDavid Lebrun 15d1df6fd8SDavid Lebrun #include <linux/types.h> 16d1df6fd8SDavid Lebrun #include <linux/skbuff.h> 17d1df6fd8SDavid Lebrun #include <linux/net.h> 18d1df6fd8SDavid Lebrun #include <linux/module.h> 19d1df6fd8SDavid Lebrun #include <net/ip.h> 20d1df6fd8SDavid Lebrun #include <net/lwtunnel.h> 21d1df6fd8SDavid Lebrun #include <net/netevent.h> 22d1df6fd8SDavid Lebrun #include <net/netns/generic.h> 23d1df6fd8SDavid Lebrun #include <net/ip6_fib.h> 24d1df6fd8SDavid Lebrun #include <net/route.h> 25d1df6fd8SDavid Lebrun #include <net/seg6.h> 26d1df6fd8SDavid Lebrun #include <linux/seg6.h> 27d1df6fd8SDavid Lebrun #include <linux/seg6_local.h> 28d1df6fd8SDavid Lebrun #include <net/addrconf.h> 29d1df6fd8SDavid Lebrun #include <net/ip6_route.h> 30d1df6fd8SDavid Lebrun #include <net/dst_cache.h> 31d1df6fd8SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 32d1df6fd8SDavid Lebrun #include <net/seg6_hmac.h> 33d1df6fd8SDavid Lebrun #endif 341c1e761eSMathieu Xhonneux #include <net/seg6_local.h> 35891ef8ddSDavid Lebrun #include <linux/etherdevice.h> 36004d4b27SMathieu Xhonneux #include <linux/bpf.h> 37d1df6fd8SDavid Lebrun 38d1df6fd8SDavid Lebrun struct seg6_local_lwt; 39d1df6fd8SDavid Lebrun 40d1df6fd8SDavid Lebrun struct seg6_action_desc { 41d1df6fd8SDavid Lebrun int action; 42d1df6fd8SDavid Lebrun unsigned long attrs; 43d1df6fd8SDavid Lebrun int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 44d1df6fd8SDavid Lebrun int static_headroom; 45d1df6fd8SDavid Lebrun }; 46d1df6fd8SDavid Lebrun 47004d4b27SMathieu Xhonneux struct bpf_lwt_prog { 48004d4b27SMathieu Xhonneux struct bpf_prog *prog; 49004d4b27SMathieu Xhonneux char *name; 50004d4b27SMathieu Xhonneux }; 51004d4b27SMathieu Xhonneux 52d1df6fd8SDavid Lebrun struct seg6_local_lwt { 53d1df6fd8SDavid Lebrun int action; 54d1df6fd8SDavid Lebrun struct ipv6_sr_hdr *srh; 55d1df6fd8SDavid Lebrun int table; 56d1df6fd8SDavid Lebrun struct in_addr nh4; 57d1df6fd8SDavid Lebrun struct in6_addr nh6; 58d1df6fd8SDavid Lebrun int iif; 59d1df6fd8SDavid Lebrun int oif; 60004d4b27SMathieu Xhonneux struct bpf_lwt_prog bpf; 61d1df6fd8SDavid Lebrun 62d1df6fd8SDavid Lebrun int headroom; 63d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 64d1df6fd8SDavid Lebrun }; 65d1df6fd8SDavid Lebrun 66d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) 67d1df6fd8SDavid Lebrun { 68d1df6fd8SDavid Lebrun return (struct seg6_local_lwt *)lwt->data; 69d1df6fd8SDavid Lebrun } 70d1df6fd8SDavid Lebrun 71140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb) 72140f04c3SDavid Lebrun { 73140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 745829d70bSAhmed Abdelsalam int len, srhoff = 0; 75140f04c3SDavid Lebrun 765829d70bSAhmed Abdelsalam if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0) 77140f04c3SDavid Lebrun return NULL; 78140f04c3SDavid Lebrun 795829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + sizeof(*srh))) 805829d70bSAhmed Abdelsalam return NULL; 815829d70bSAhmed Abdelsalam 825829d70bSAhmed Abdelsalam srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 835829d70bSAhmed Abdelsalam 84140f04c3SDavid Lebrun len = (srh->hdrlen + 1) << 3; 85140f04c3SDavid Lebrun 865829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + len)) 87140f04c3SDavid Lebrun return NULL; 88140f04c3SDavid Lebrun 89140f04c3SDavid Lebrun if (!seg6_validate_srh(srh, len)) 90140f04c3SDavid Lebrun return NULL; 91140f04c3SDavid Lebrun 92140f04c3SDavid Lebrun return srh; 93140f04c3SDavid Lebrun } 94140f04c3SDavid Lebrun 95140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) 96140f04c3SDavid Lebrun { 97140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 98140f04c3SDavid Lebrun 99140f04c3SDavid Lebrun srh = get_srh(skb); 100140f04c3SDavid Lebrun if (!srh) 101140f04c3SDavid Lebrun return NULL; 102140f04c3SDavid Lebrun 103140f04c3SDavid Lebrun if (srh->segments_left == 0) 104140f04c3SDavid Lebrun return NULL; 105140f04c3SDavid Lebrun 106140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 107140f04c3SDavid Lebrun if (!seg6_hmac_validate_skb(skb)) 108140f04c3SDavid Lebrun return NULL; 109140f04c3SDavid Lebrun #endif 110140f04c3SDavid Lebrun 111140f04c3SDavid Lebrun return srh; 112140f04c3SDavid Lebrun } 113140f04c3SDavid Lebrun 114d7a669ddSDavid Lebrun static bool decap_and_validate(struct sk_buff *skb, int proto) 115d7a669ddSDavid Lebrun { 116d7a669ddSDavid Lebrun struct ipv6_sr_hdr *srh; 117d7a669ddSDavid Lebrun unsigned int off = 0; 118d7a669ddSDavid Lebrun 119d7a669ddSDavid Lebrun srh = get_srh(skb); 120d7a669ddSDavid Lebrun if (srh && srh->segments_left > 0) 121d7a669ddSDavid Lebrun return false; 122d7a669ddSDavid Lebrun 123d7a669ddSDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 124d7a669ddSDavid Lebrun if (srh && !seg6_hmac_validate_skb(skb)) 125d7a669ddSDavid Lebrun return false; 126d7a669ddSDavid Lebrun #endif 127d7a669ddSDavid Lebrun 128d7a669ddSDavid Lebrun if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0) 129d7a669ddSDavid Lebrun return false; 130d7a669ddSDavid Lebrun 131d7a669ddSDavid Lebrun if (!pskb_pull(skb, off)) 132d7a669ddSDavid Lebrun return false; 133d7a669ddSDavid Lebrun 134d7a669ddSDavid Lebrun skb_postpull_rcsum(skb, skb_network_header(skb), off); 135d7a669ddSDavid Lebrun 136d7a669ddSDavid Lebrun skb_reset_network_header(skb); 137d7a669ddSDavid Lebrun skb_reset_transport_header(skb); 138d7a669ddSDavid Lebrun skb->encapsulation = 0; 139d7a669ddSDavid Lebrun 140d7a669ddSDavid Lebrun return true; 141d7a669ddSDavid Lebrun } 142d7a669ddSDavid Lebrun 143d7a669ddSDavid Lebrun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr) 144d7a669ddSDavid Lebrun { 145d7a669ddSDavid Lebrun struct in6_addr *addr; 146d7a669ddSDavid Lebrun 147d7a669ddSDavid Lebrun srh->segments_left--; 148d7a669ddSDavid Lebrun addr = srh->segments + srh->segments_left; 149d7a669ddSDavid Lebrun *daddr = *addr; 150d7a669ddSDavid Lebrun } 151d7a669ddSDavid Lebrun 1521c1e761eSMathieu Xhonneux int seg6_lookup_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr, 153d7a669ddSDavid Lebrun u32 tbl_id) 154d7a669ddSDavid Lebrun { 155d7a669ddSDavid Lebrun struct net *net = dev_net(skb->dev); 156d7a669ddSDavid Lebrun struct ipv6hdr *hdr = ipv6_hdr(skb); 157d7a669ddSDavid Lebrun int flags = RT6_LOOKUP_F_HAS_SADDR; 158d7a669ddSDavid Lebrun struct dst_entry *dst = NULL; 159d7a669ddSDavid Lebrun struct rt6_info *rt; 160d7a669ddSDavid Lebrun struct flowi6 fl6; 161d7a669ddSDavid Lebrun 162d7a669ddSDavid Lebrun fl6.flowi6_iif = skb->dev->ifindex; 163d7a669ddSDavid Lebrun fl6.daddr = nhaddr ? *nhaddr : hdr->daddr; 164d7a669ddSDavid Lebrun fl6.saddr = hdr->saddr; 165d7a669ddSDavid Lebrun fl6.flowlabel = ip6_flowinfo(hdr); 166d7a669ddSDavid Lebrun fl6.flowi6_mark = skb->mark; 167d7a669ddSDavid Lebrun fl6.flowi6_proto = hdr->nexthdr; 168d7a669ddSDavid Lebrun 169d7a669ddSDavid Lebrun if (nhaddr) 170d7a669ddSDavid Lebrun fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH; 171d7a669ddSDavid Lebrun 172d7a669ddSDavid Lebrun if (!tbl_id) { 173b75cc8f9SDavid Ahern dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags); 174d7a669ddSDavid Lebrun } else { 175d7a669ddSDavid Lebrun struct fib6_table *table; 176d7a669ddSDavid Lebrun 177d7a669ddSDavid Lebrun table = fib6_get_table(net, tbl_id); 178d7a669ddSDavid Lebrun if (!table) 179d7a669ddSDavid Lebrun goto out; 180d7a669ddSDavid Lebrun 181b75cc8f9SDavid Ahern rt = ip6_pol_route(net, table, 0, &fl6, skb, flags); 182d7a669ddSDavid Lebrun dst = &rt->dst; 183d7a669ddSDavid Lebrun } 184d7a669ddSDavid Lebrun 185d7a669ddSDavid Lebrun if (dst && dst->dev->flags & IFF_LOOPBACK && !dst->error) { 186d7a669ddSDavid Lebrun dst_release(dst); 187d7a669ddSDavid Lebrun dst = NULL; 188d7a669ddSDavid Lebrun } 189d7a669ddSDavid Lebrun 190d7a669ddSDavid Lebrun out: 191d7a669ddSDavid Lebrun if (!dst) { 192d7a669ddSDavid Lebrun rt = net->ipv6.ip6_blk_hole_entry; 193d7a669ddSDavid Lebrun dst = &rt->dst; 194d7a669ddSDavid Lebrun dst_hold(dst); 195d7a669ddSDavid Lebrun } 196d7a669ddSDavid Lebrun 197d7a669ddSDavid Lebrun skb_dst_drop(skb); 198d7a669ddSDavid Lebrun skb_dst_set(skb, dst); 1991c1e761eSMathieu Xhonneux return dst->error; 200d7a669ddSDavid Lebrun } 201d7a669ddSDavid Lebrun 202140f04c3SDavid Lebrun /* regular endpoint function */ 203140f04c3SDavid Lebrun static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 204140f04c3SDavid Lebrun { 205140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 206140f04c3SDavid Lebrun 207140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 208140f04c3SDavid Lebrun if (!srh) 209140f04c3SDavid Lebrun goto drop; 210140f04c3SDavid Lebrun 211d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 212140f04c3SDavid Lebrun 2131c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 214140f04c3SDavid Lebrun 215140f04c3SDavid Lebrun return dst_input(skb); 216140f04c3SDavid Lebrun 217140f04c3SDavid Lebrun drop: 218140f04c3SDavid Lebrun kfree_skb(skb); 219140f04c3SDavid Lebrun return -EINVAL; 220140f04c3SDavid Lebrun } 221140f04c3SDavid Lebrun 222140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */ 223140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) 224140f04c3SDavid Lebrun { 225140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 226140f04c3SDavid Lebrun 227140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 228140f04c3SDavid Lebrun if (!srh) 229140f04c3SDavid Lebrun goto drop; 230140f04c3SDavid Lebrun 231d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 232140f04c3SDavid Lebrun 2331c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, &slwt->nh6, 0); 234140f04c3SDavid Lebrun 235140f04c3SDavid Lebrun return dst_input(skb); 236140f04c3SDavid Lebrun 237140f04c3SDavid Lebrun drop: 238140f04c3SDavid Lebrun kfree_skb(skb); 239140f04c3SDavid Lebrun return -EINVAL; 240140f04c3SDavid Lebrun } 241140f04c3SDavid Lebrun 242891ef8ddSDavid Lebrun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) 243891ef8ddSDavid Lebrun { 244891ef8ddSDavid Lebrun struct ipv6_sr_hdr *srh; 245891ef8ddSDavid Lebrun 246891ef8ddSDavid Lebrun srh = get_and_validate_srh(skb); 247891ef8ddSDavid Lebrun if (!srh) 248891ef8ddSDavid Lebrun goto drop; 249891ef8ddSDavid Lebrun 250891ef8ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 251891ef8ddSDavid Lebrun 2521c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, slwt->table); 253891ef8ddSDavid Lebrun 254891ef8ddSDavid Lebrun return dst_input(skb); 255891ef8ddSDavid Lebrun 256891ef8ddSDavid Lebrun drop: 257891ef8ddSDavid Lebrun kfree_skb(skb); 258891ef8ddSDavid Lebrun return -EINVAL; 259891ef8ddSDavid Lebrun } 260891ef8ddSDavid Lebrun 261891ef8ddSDavid Lebrun /* decapsulate and forward inner L2 frame on specified interface */ 262891ef8ddSDavid Lebrun static int input_action_end_dx2(struct sk_buff *skb, 263891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 264891ef8ddSDavid Lebrun { 265891ef8ddSDavid Lebrun struct net *net = dev_net(skb->dev); 266891ef8ddSDavid Lebrun struct net_device *odev; 267891ef8ddSDavid Lebrun struct ethhdr *eth; 268891ef8ddSDavid Lebrun 269891ef8ddSDavid Lebrun if (!decap_and_validate(skb, NEXTHDR_NONE)) 270891ef8ddSDavid Lebrun goto drop; 271891ef8ddSDavid Lebrun 272891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, ETH_HLEN)) 273891ef8ddSDavid Lebrun goto drop; 274891ef8ddSDavid Lebrun 275891ef8ddSDavid Lebrun skb_reset_mac_header(skb); 276891ef8ddSDavid Lebrun eth = (struct ethhdr *)skb->data; 277891ef8ddSDavid Lebrun 278891ef8ddSDavid Lebrun /* To determine the frame's protocol, we assume it is 802.3. This avoids 279891ef8ddSDavid Lebrun * a call to eth_type_trans(), which is not really relevant for our 280891ef8ddSDavid Lebrun * use case. 281891ef8ddSDavid Lebrun */ 282891ef8ddSDavid Lebrun if (!eth_proto_is_802_3(eth->h_proto)) 283891ef8ddSDavid Lebrun goto drop; 284891ef8ddSDavid Lebrun 285891ef8ddSDavid Lebrun odev = dev_get_by_index_rcu(net, slwt->oif); 286891ef8ddSDavid Lebrun if (!odev) 287891ef8ddSDavid Lebrun goto drop; 288891ef8ddSDavid Lebrun 289891ef8ddSDavid Lebrun /* As we accept Ethernet frames, make sure the egress device is of 290891ef8ddSDavid Lebrun * the correct type. 291891ef8ddSDavid Lebrun */ 292891ef8ddSDavid Lebrun if (odev->type != ARPHRD_ETHER) 293891ef8ddSDavid Lebrun goto drop; 294891ef8ddSDavid Lebrun 295891ef8ddSDavid Lebrun if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev)) 296891ef8ddSDavid Lebrun goto drop; 297891ef8ddSDavid Lebrun 298891ef8ddSDavid Lebrun skb_orphan(skb); 299891ef8ddSDavid Lebrun 300891ef8ddSDavid Lebrun if (skb_warn_if_lro(skb)) 301891ef8ddSDavid Lebrun goto drop; 302891ef8ddSDavid Lebrun 303891ef8ddSDavid Lebrun skb_forward_csum(skb); 304891ef8ddSDavid Lebrun 305891ef8ddSDavid Lebrun if (skb->len - ETH_HLEN > odev->mtu) 306891ef8ddSDavid Lebrun goto drop; 307891ef8ddSDavid Lebrun 308891ef8ddSDavid Lebrun skb->dev = odev; 309891ef8ddSDavid Lebrun skb->protocol = eth->h_proto; 310891ef8ddSDavid Lebrun 311891ef8ddSDavid Lebrun return dev_queue_xmit(skb); 312891ef8ddSDavid Lebrun 313891ef8ddSDavid Lebrun drop: 314891ef8ddSDavid Lebrun kfree_skb(skb); 315891ef8ddSDavid Lebrun return -EINVAL; 316891ef8ddSDavid Lebrun } 317891ef8ddSDavid Lebrun 318140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */ 319140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb, 320140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 321140f04c3SDavid Lebrun { 322d7a669ddSDavid Lebrun struct in6_addr *nhaddr = NULL; 323140f04c3SDavid Lebrun 324140f04c3SDavid Lebrun /* this function accepts IPv6 encapsulated packets, with either 325140f04c3SDavid Lebrun * an SRH with SL=0, or no SRH. 326140f04c3SDavid Lebrun */ 327140f04c3SDavid Lebrun 328d7a669ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 329140f04c3SDavid Lebrun goto drop; 330140f04c3SDavid Lebrun 331d7a669ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 332140f04c3SDavid Lebrun goto drop; 333140f04c3SDavid Lebrun 334140f04c3SDavid Lebrun /* The inner packet is not associated to any local interface, 335140f04c3SDavid Lebrun * so we do not call netif_rx(). 336140f04c3SDavid Lebrun * 337140f04c3SDavid Lebrun * If slwt->nh6 is set to ::, then lookup the nexthop for the 338140f04c3SDavid Lebrun * inner packet's DA. Otherwise, use the specified nexthop. 339140f04c3SDavid Lebrun */ 340140f04c3SDavid Lebrun 341d7a669ddSDavid Lebrun if (!ipv6_addr_any(&slwt->nh6)) 342d7a669ddSDavid Lebrun nhaddr = &slwt->nh6; 343140f04c3SDavid Lebrun 3441c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, nhaddr, 0); 345140f04c3SDavid Lebrun 346140f04c3SDavid Lebrun return dst_input(skb); 347140f04c3SDavid Lebrun drop: 348140f04c3SDavid Lebrun kfree_skb(skb); 349140f04c3SDavid Lebrun return -EINVAL; 350140f04c3SDavid Lebrun } 351140f04c3SDavid Lebrun 352891ef8ddSDavid Lebrun static int input_action_end_dx4(struct sk_buff *skb, 353891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 354891ef8ddSDavid Lebrun { 355891ef8ddSDavid Lebrun struct iphdr *iph; 356891ef8ddSDavid Lebrun __be32 nhaddr; 357891ef8ddSDavid Lebrun int err; 358891ef8ddSDavid Lebrun 359891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPIP)) 360891ef8ddSDavid Lebrun goto drop; 361891ef8ddSDavid Lebrun 362891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct iphdr))) 363891ef8ddSDavid Lebrun goto drop; 364891ef8ddSDavid Lebrun 365891ef8ddSDavid Lebrun skb->protocol = htons(ETH_P_IP); 366891ef8ddSDavid Lebrun 367891ef8ddSDavid Lebrun iph = ip_hdr(skb); 368891ef8ddSDavid Lebrun 369891ef8ddSDavid Lebrun nhaddr = slwt->nh4.s_addr ?: iph->daddr; 370891ef8ddSDavid Lebrun 371891ef8ddSDavid Lebrun skb_dst_drop(skb); 372891ef8ddSDavid Lebrun 373891ef8ddSDavid Lebrun err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev); 374891ef8ddSDavid Lebrun if (err) 375891ef8ddSDavid Lebrun goto drop; 376891ef8ddSDavid Lebrun 377891ef8ddSDavid Lebrun return dst_input(skb); 378891ef8ddSDavid Lebrun 379891ef8ddSDavid Lebrun drop: 380891ef8ddSDavid Lebrun kfree_skb(skb); 381891ef8ddSDavid Lebrun return -EINVAL; 382891ef8ddSDavid Lebrun } 383891ef8ddSDavid Lebrun 384891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb, 385891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 386891ef8ddSDavid Lebrun { 387891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 388891ef8ddSDavid Lebrun goto drop; 389891ef8ddSDavid Lebrun 390891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 391891ef8ddSDavid Lebrun goto drop; 392891ef8ddSDavid Lebrun 3931c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, slwt->table); 394891ef8ddSDavid Lebrun 395891ef8ddSDavid Lebrun return dst_input(skb); 396891ef8ddSDavid Lebrun 397891ef8ddSDavid Lebrun drop: 398891ef8ddSDavid Lebrun kfree_skb(skb); 399891ef8ddSDavid Lebrun return -EINVAL; 400891ef8ddSDavid Lebrun } 401891ef8ddSDavid Lebrun 402140f04c3SDavid Lebrun /* push an SRH on top of the current one */ 403140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 404140f04c3SDavid Lebrun { 405140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 406140f04c3SDavid Lebrun int err = -EINVAL; 407140f04c3SDavid Lebrun 408140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 409140f04c3SDavid Lebrun if (!srh) 410140f04c3SDavid Lebrun goto drop; 411140f04c3SDavid Lebrun 412140f04c3SDavid Lebrun err = seg6_do_srh_inline(skb, slwt->srh); 413140f04c3SDavid Lebrun if (err) 414140f04c3SDavid Lebrun goto drop; 415140f04c3SDavid Lebrun 416140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 417140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 418140f04c3SDavid Lebrun 4191c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 420140f04c3SDavid Lebrun 421140f04c3SDavid Lebrun return dst_input(skb); 422140f04c3SDavid Lebrun 423140f04c3SDavid Lebrun drop: 424140f04c3SDavid Lebrun kfree_skb(skb); 425140f04c3SDavid Lebrun return err; 426140f04c3SDavid Lebrun } 427140f04c3SDavid Lebrun 428140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */ 429140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb, 430140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 431140f04c3SDavid Lebrun { 432140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 433140f04c3SDavid Lebrun int err = -EINVAL; 434140f04c3SDavid Lebrun 435140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 436140f04c3SDavid Lebrun if (!srh) 437140f04c3SDavid Lebrun goto drop; 438140f04c3SDavid Lebrun 439d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 440140f04c3SDavid Lebrun 441140f04c3SDavid Lebrun skb_reset_inner_headers(skb); 442140f04c3SDavid Lebrun skb->encapsulation = 1; 443140f04c3SDavid Lebrun 44432d99d0bSDavid Lebrun err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6); 445140f04c3SDavid Lebrun if (err) 446140f04c3SDavid Lebrun goto drop; 447140f04c3SDavid Lebrun 448140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 449140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 450140f04c3SDavid Lebrun 4511c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 452140f04c3SDavid Lebrun 453140f04c3SDavid Lebrun return dst_input(skb); 454140f04c3SDavid Lebrun 455140f04c3SDavid Lebrun drop: 456140f04c3SDavid Lebrun kfree_skb(skb); 457140f04c3SDavid Lebrun return err; 458140f04c3SDavid Lebrun } 459140f04c3SDavid Lebrun 460fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states); 461fe94cc29SMathieu Xhonneux 462486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb) 463486cdf21SMathieu Xhonneux { 464486cdf21SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 465486cdf21SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 466486cdf21SMathieu Xhonneux struct ipv6_sr_hdr *srh = srh_state->srh; 467486cdf21SMathieu Xhonneux 468486cdf21SMathieu Xhonneux if (unlikely(srh == NULL)) 469486cdf21SMathieu Xhonneux return false; 470486cdf21SMathieu Xhonneux 471486cdf21SMathieu Xhonneux if (unlikely(!srh_state->valid)) { 472486cdf21SMathieu Xhonneux if ((srh_state->hdrlen & 7) != 0) 473486cdf21SMathieu Xhonneux return false; 474486cdf21SMathieu Xhonneux 475486cdf21SMathieu Xhonneux srh->hdrlen = (u8)(srh_state->hdrlen >> 3); 476486cdf21SMathieu Xhonneux if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3)) 477486cdf21SMathieu Xhonneux return false; 478486cdf21SMathieu Xhonneux 479486cdf21SMathieu Xhonneux srh_state->valid = true; 480486cdf21SMathieu Xhonneux } 481486cdf21SMathieu Xhonneux 482486cdf21SMathieu Xhonneux return true; 483486cdf21SMathieu Xhonneux } 484486cdf21SMathieu Xhonneux 485004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb, 486004d4b27SMathieu Xhonneux struct seg6_local_lwt *slwt) 487004d4b27SMathieu Xhonneux { 488004d4b27SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 489004d4b27SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 490004d4b27SMathieu Xhonneux struct ipv6_sr_hdr *srh; 491004d4b27SMathieu Xhonneux int ret; 492004d4b27SMathieu Xhonneux 493004d4b27SMathieu Xhonneux srh = get_and_validate_srh(skb); 494486cdf21SMathieu Xhonneux if (!srh) { 495486cdf21SMathieu Xhonneux kfree_skb(skb); 496486cdf21SMathieu Xhonneux return -EINVAL; 497486cdf21SMathieu Xhonneux } 498004d4b27SMathieu Xhonneux advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 499004d4b27SMathieu Xhonneux 500004d4b27SMathieu Xhonneux /* preempt_disable is needed to protect the per-CPU buffer srh_state, 501004d4b27SMathieu Xhonneux * which is also accessed by the bpf_lwt_seg6_* helpers 502004d4b27SMathieu Xhonneux */ 503004d4b27SMathieu Xhonneux preempt_disable(); 504486cdf21SMathieu Xhonneux srh_state->srh = srh; 505004d4b27SMathieu Xhonneux srh_state->hdrlen = srh->hdrlen << 3; 506486cdf21SMathieu Xhonneux srh_state->valid = true; 507004d4b27SMathieu Xhonneux 508004d4b27SMathieu Xhonneux rcu_read_lock(); 509004d4b27SMathieu Xhonneux bpf_compute_data_pointers(skb); 510004d4b27SMathieu Xhonneux ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb); 511004d4b27SMathieu Xhonneux rcu_read_unlock(); 512004d4b27SMathieu Xhonneux 513004d4b27SMathieu Xhonneux switch (ret) { 514004d4b27SMathieu Xhonneux case BPF_OK: 515004d4b27SMathieu Xhonneux case BPF_REDIRECT: 516004d4b27SMathieu Xhonneux break; 517004d4b27SMathieu Xhonneux case BPF_DROP: 518004d4b27SMathieu Xhonneux goto drop; 519004d4b27SMathieu Xhonneux default: 520004d4b27SMathieu Xhonneux pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret); 521004d4b27SMathieu Xhonneux goto drop; 522004d4b27SMathieu Xhonneux } 523004d4b27SMathieu Xhonneux 524486cdf21SMathieu Xhonneux if (srh_state->srh && !seg6_bpf_has_valid_srh(skb)) 525004d4b27SMathieu Xhonneux goto drop; 526004d4b27SMathieu Xhonneux 527486cdf21SMathieu Xhonneux preempt_enable(); 528004d4b27SMathieu Xhonneux if (ret != BPF_REDIRECT) 529004d4b27SMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 530004d4b27SMathieu Xhonneux 531004d4b27SMathieu Xhonneux return dst_input(skb); 532004d4b27SMathieu Xhonneux 533004d4b27SMathieu Xhonneux drop: 534486cdf21SMathieu Xhonneux preempt_enable(); 535004d4b27SMathieu Xhonneux kfree_skb(skb); 536004d4b27SMathieu Xhonneux return -EINVAL; 537004d4b27SMathieu Xhonneux } 538004d4b27SMathieu Xhonneux 539d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = { 540d1df6fd8SDavid Lebrun { 541d1df6fd8SDavid Lebrun .action = SEG6_LOCAL_ACTION_END, 542d1df6fd8SDavid Lebrun .attrs = 0, 543140f04c3SDavid Lebrun .input = input_action_end, 544d1df6fd8SDavid Lebrun }, 545140f04c3SDavid Lebrun { 546140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_X, 547140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH6), 548140f04c3SDavid Lebrun .input = input_action_end_x, 549140f04c3SDavid Lebrun }, 550140f04c3SDavid Lebrun { 551891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_T, 552891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_TABLE), 553891ef8ddSDavid Lebrun .input = input_action_end_t, 554891ef8ddSDavid Lebrun }, 555891ef8ddSDavid Lebrun { 556891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX2, 557891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_OIF), 558891ef8ddSDavid Lebrun .input = input_action_end_dx2, 559891ef8ddSDavid Lebrun }, 560891ef8ddSDavid Lebrun { 561140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX6, 562140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH6), 563140f04c3SDavid Lebrun .input = input_action_end_dx6, 564140f04c3SDavid Lebrun }, 565140f04c3SDavid Lebrun { 566891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX4, 567891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH4), 568891ef8ddSDavid Lebrun .input = input_action_end_dx4, 569891ef8ddSDavid Lebrun }, 570891ef8ddSDavid Lebrun { 571891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DT6, 572891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_TABLE), 573891ef8ddSDavid Lebrun .input = input_action_end_dt6, 574891ef8ddSDavid Lebrun }, 575891ef8ddSDavid Lebrun { 576140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6, 577140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_SRH), 578140f04c3SDavid Lebrun .input = input_action_end_b6, 579140f04c3SDavid Lebrun }, 580140f04c3SDavid Lebrun { 581140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6_ENCAP, 582140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_SRH), 583140f04c3SDavid Lebrun .input = input_action_end_b6_encap, 584140f04c3SDavid Lebrun .static_headroom = sizeof(struct ipv6hdr), 585004d4b27SMathieu Xhonneux }, 586004d4b27SMathieu Xhonneux { 587004d4b27SMathieu Xhonneux .action = SEG6_LOCAL_ACTION_END_BPF, 588004d4b27SMathieu Xhonneux .attrs = (1 << SEG6_LOCAL_BPF), 589004d4b27SMathieu Xhonneux .input = input_action_end_bpf, 590004d4b27SMathieu Xhonneux }, 591004d4b27SMathieu Xhonneux 592d1df6fd8SDavid Lebrun }; 593d1df6fd8SDavid Lebrun 594d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action) 595d1df6fd8SDavid Lebrun { 596d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 597d1df6fd8SDavid Lebrun int i, count; 598d1df6fd8SDavid Lebrun 599709af180SColin Ian King count = ARRAY_SIZE(seg6_action_table); 600d1df6fd8SDavid Lebrun for (i = 0; i < count; i++) { 601d1df6fd8SDavid Lebrun desc = &seg6_action_table[i]; 602d1df6fd8SDavid Lebrun if (desc->action == action) 603d1df6fd8SDavid Lebrun return desc; 604d1df6fd8SDavid Lebrun } 605d1df6fd8SDavid Lebrun 606d1df6fd8SDavid Lebrun return NULL; 607d1df6fd8SDavid Lebrun } 608d1df6fd8SDavid Lebrun 609d1df6fd8SDavid Lebrun static int seg6_local_input(struct sk_buff *skb) 610d1df6fd8SDavid Lebrun { 611d1df6fd8SDavid Lebrun struct dst_entry *orig_dst = skb_dst(skb); 612d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 613d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 614d1df6fd8SDavid Lebrun 6156285217fSDavid Lebrun if (skb->protocol != htons(ETH_P_IPV6)) { 6166285217fSDavid Lebrun kfree_skb(skb); 6176285217fSDavid Lebrun return -EINVAL; 6186285217fSDavid Lebrun } 6196285217fSDavid Lebrun 620d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 621d1df6fd8SDavid Lebrun desc = slwt->desc; 622d1df6fd8SDavid Lebrun 623d1df6fd8SDavid Lebrun return desc->input(skb, slwt); 624d1df6fd8SDavid Lebrun } 625d1df6fd8SDavid Lebrun 626d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { 627d1df6fd8SDavid Lebrun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, 628d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, 629d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, 630d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, 631d1df6fd8SDavid Lebrun .len = sizeof(struct in_addr) }, 632d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, 633d1df6fd8SDavid Lebrun .len = sizeof(struct in6_addr) }, 634d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, 635d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 636004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .type = NLA_NESTED }, 637d1df6fd8SDavid Lebrun }; 638d1df6fd8SDavid Lebrun 6392d9cc60aSDavid Lebrun static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt) 6402d9cc60aSDavid Lebrun { 6412d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 6422d9cc60aSDavid Lebrun int len; 6432d9cc60aSDavid Lebrun 6442d9cc60aSDavid Lebrun srh = nla_data(attrs[SEG6_LOCAL_SRH]); 6452d9cc60aSDavid Lebrun len = nla_len(attrs[SEG6_LOCAL_SRH]); 6462d9cc60aSDavid Lebrun 6472d9cc60aSDavid Lebrun /* SRH must contain at least one segment */ 6482d9cc60aSDavid Lebrun if (len < sizeof(*srh) + sizeof(struct in6_addr)) 6492d9cc60aSDavid Lebrun return -EINVAL; 6502d9cc60aSDavid Lebrun 6512d9cc60aSDavid Lebrun if (!seg6_validate_srh(srh, len)) 6522d9cc60aSDavid Lebrun return -EINVAL; 6532d9cc60aSDavid Lebrun 6547fa41efaSYueHaibing slwt->srh = kmemdup(srh, len, GFP_KERNEL); 6552d9cc60aSDavid Lebrun if (!slwt->srh) 6562d9cc60aSDavid Lebrun return -ENOMEM; 6572d9cc60aSDavid Lebrun 6582d9cc60aSDavid Lebrun slwt->headroom += len; 6592d9cc60aSDavid Lebrun 6602d9cc60aSDavid Lebrun return 0; 6612d9cc60aSDavid Lebrun } 6622d9cc60aSDavid Lebrun 6632d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt) 6642d9cc60aSDavid Lebrun { 6652d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 6662d9cc60aSDavid Lebrun struct nlattr *nla; 6672d9cc60aSDavid Lebrun int len; 6682d9cc60aSDavid Lebrun 6692d9cc60aSDavid Lebrun srh = slwt->srh; 6702d9cc60aSDavid Lebrun len = (srh->hdrlen + 1) << 3; 6712d9cc60aSDavid Lebrun 6722d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_SRH, len); 6732d9cc60aSDavid Lebrun if (!nla) 6742d9cc60aSDavid Lebrun return -EMSGSIZE; 6752d9cc60aSDavid Lebrun 6762d9cc60aSDavid Lebrun memcpy(nla_data(nla), srh, len); 6772d9cc60aSDavid Lebrun 6782d9cc60aSDavid Lebrun return 0; 6792d9cc60aSDavid Lebrun } 6802d9cc60aSDavid Lebrun 6812d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 6822d9cc60aSDavid Lebrun { 6832d9cc60aSDavid Lebrun int len = (a->srh->hdrlen + 1) << 3; 6842d9cc60aSDavid Lebrun 6852d9cc60aSDavid Lebrun if (len != ((b->srh->hdrlen + 1) << 3)) 6862d9cc60aSDavid Lebrun return 1; 6872d9cc60aSDavid Lebrun 6882d9cc60aSDavid Lebrun return memcmp(a->srh, b->srh, len); 6892d9cc60aSDavid Lebrun } 6902d9cc60aSDavid Lebrun 6912d9cc60aSDavid Lebrun static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt) 6922d9cc60aSDavid Lebrun { 6932d9cc60aSDavid Lebrun slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]); 6942d9cc60aSDavid Lebrun 6952d9cc60aSDavid Lebrun return 0; 6962d9cc60aSDavid Lebrun } 6972d9cc60aSDavid Lebrun 6982d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt) 6992d9cc60aSDavid Lebrun { 7002d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table)) 7012d9cc60aSDavid Lebrun return -EMSGSIZE; 7022d9cc60aSDavid Lebrun 7032d9cc60aSDavid Lebrun return 0; 7042d9cc60aSDavid Lebrun } 7052d9cc60aSDavid Lebrun 7062d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7072d9cc60aSDavid Lebrun { 7082d9cc60aSDavid Lebrun if (a->table != b->table) 7092d9cc60aSDavid Lebrun return 1; 7102d9cc60aSDavid Lebrun 7112d9cc60aSDavid Lebrun return 0; 7122d9cc60aSDavid Lebrun } 7132d9cc60aSDavid Lebrun 7142d9cc60aSDavid Lebrun static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7152d9cc60aSDavid Lebrun { 7162d9cc60aSDavid Lebrun memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]), 7172d9cc60aSDavid Lebrun sizeof(struct in_addr)); 7182d9cc60aSDavid Lebrun 7192d9cc60aSDavid Lebrun return 0; 7202d9cc60aSDavid Lebrun } 7212d9cc60aSDavid Lebrun 7222d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7232d9cc60aSDavid Lebrun { 7242d9cc60aSDavid Lebrun struct nlattr *nla; 7252d9cc60aSDavid Lebrun 7262d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr)); 7272d9cc60aSDavid Lebrun if (!nla) 7282d9cc60aSDavid Lebrun return -EMSGSIZE; 7292d9cc60aSDavid Lebrun 7302d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr)); 7312d9cc60aSDavid Lebrun 7322d9cc60aSDavid Lebrun return 0; 7332d9cc60aSDavid Lebrun } 7342d9cc60aSDavid Lebrun 7352d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7362d9cc60aSDavid Lebrun { 7372d9cc60aSDavid Lebrun return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr)); 7382d9cc60aSDavid Lebrun } 7392d9cc60aSDavid Lebrun 7402d9cc60aSDavid Lebrun static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7412d9cc60aSDavid Lebrun { 7422d9cc60aSDavid Lebrun memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]), 7432d9cc60aSDavid Lebrun sizeof(struct in6_addr)); 7442d9cc60aSDavid Lebrun 7452d9cc60aSDavid Lebrun return 0; 7462d9cc60aSDavid Lebrun } 7472d9cc60aSDavid Lebrun 7482d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7492d9cc60aSDavid Lebrun { 7502d9cc60aSDavid Lebrun struct nlattr *nla; 7512d9cc60aSDavid Lebrun 7522d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr)); 7532d9cc60aSDavid Lebrun if (!nla) 7542d9cc60aSDavid Lebrun return -EMSGSIZE; 7552d9cc60aSDavid Lebrun 7562d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr)); 7572d9cc60aSDavid Lebrun 7582d9cc60aSDavid Lebrun return 0; 7592d9cc60aSDavid Lebrun } 7602d9cc60aSDavid Lebrun 7612d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7622d9cc60aSDavid Lebrun { 7632d9cc60aSDavid Lebrun return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr)); 7642d9cc60aSDavid Lebrun } 7652d9cc60aSDavid Lebrun 7662d9cc60aSDavid Lebrun static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7672d9cc60aSDavid Lebrun { 7682d9cc60aSDavid Lebrun slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]); 7692d9cc60aSDavid Lebrun 7702d9cc60aSDavid Lebrun return 0; 7712d9cc60aSDavid Lebrun } 7722d9cc60aSDavid Lebrun 7732d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7742d9cc60aSDavid Lebrun { 7752d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif)) 7762d9cc60aSDavid Lebrun return -EMSGSIZE; 7772d9cc60aSDavid Lebrun 7782d9cc60aSDavid Lebrun return 0; 7792d9cc60aSDavid Lebrun } 7802d9cc60aSDavid Lebrun 7812d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7822d9cc60aSDavid Lebrun { 7832d9cc60aSDavid Lebrun if (a->iif != b->iif) 7842d9cc60aSDavid Lebrun return 1; 7852d9cc60aSDavid Lebrun 7862d9cc60aSDavid Lebrun return 0; 7872d9cc60aSDavid Lebrun } 7882d9cc60aSDavid Lebrun 7892d9cc60aSDavid Lebrun static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7902d9cc60aSDavid Lebrun { 7912d9cc60aSDavid Lebrun slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]); 7922d9cc60aSDavid Lebrun 7932d9cc60aSDavid Lebrun return 0; 7942d9cc60aSDavid Lebrun } 7952d9cc60aSDavid Lebrun 7962d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7972d9cc60aSDavid Lebrun { 7982d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif)) 7992d9cc60aSDavid Lebrun return -EMSGSIZE; 8002d9cc60aSDavid Lebrun 8012d9cc60aSDavid Lebrun return 0; 8022d9cc60aSDavid Lebrun } 8032d9cc60aSDavid Lebrun 8042d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 8052d9cc60aSDavid Lebrun { 8062d9cc60aSDavid Lebrun if (a->oif != b->oif) 8072d9cc60aSDavid Lebrun return 1; 8082d9cc60aSDavid Lebrun 8092d9cc60aSDavid Lebrun return 0; 8102d9cc60aSDavid Lebrun } 8112d9cc60aSDavid Lebrun 812004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256 813004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = { 814004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG] = { .type = NLA_U32, }, 815004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING, 816004d4b27SMathieu Xhonneux .len = MAX_PROG_NAME }, 817004d4b27SMathieu Xhonneux }; 818004d4b27SMathieu Xhonneux 819004d4b27SMathieu Xhonneux static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt) 820004d4b27SMathieu Xhonneux { 821004d4b27SMathieu Xhonneux struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1]; 822004d4b27SMathieu Xhonneux struct bpf_prog *p; 823004d4b27SMathieu Xhonneux int ret; 824004d4b27SMathieu Xhonneux u32 fd; 825004d4b27SMathieu Xhonneux 826004d4b27SMathieu Xhonneux ret = nla_parse_nested(tb, SEG6_LOCAL_BPF_PROG_MAX, 827004d4b27SMathieu Xhonneux attrs[SEG6_LOCAL_BPF], bpf_prog_policy, NULL); 828004d4b27SMathieu Xhonneux if (ret < 0) 829004d4b27SMathieu Xhonneux return ret; 830004d4b27SMathieu Xhonneux 831004d4b27SMathieu Xhonneux if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME]) 832004d4b27SMathieu Xhonneux return -EINVAL; 833004d4b27SMathieu Xhonneux 834004d4b27SMathieu Xhonneux slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL); 835004d4b27SMathieu Xhonneux if (!slwt->bpf.name) 836004d4b27SMathieu Xhonneux return -ENOMEM; 837004d4b27SMathieu Xhonneux 838004d4b27SMathieu Xhonneux fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]); 839004d4b27SMathieu Xhonneux p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL); 840004d4b27SMathieu Xhonneux if (IS_ERR(p)) { 841004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 842004d4b27SMathieu Xhonneux return PTR_ERR(p); 843004d4b27SMathieu Xhonneux } 844004d4b27SMathieu Xhonneux 845004d4b27SMathieu Xhonneux slwt->bpf.prog = p; 846004d4b27SMathieu Xhonneux return 0; 847004d4b27SMathieu Xhonneux } 848004d4b27SMathieu Xhonneux 849004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt) 850004d4b27SMathieu Xhonneux { 851004d4b27SMathieu Xhonneux struct nlattr *nest; 852004d4b27SMathieu Xhonneux 853004d4b27SMathieu Xhonneux if (!slwt->bpf.prog) 854004d4b27SMathieu Xhonneux return 0; 855004d4b27SMathieu Xhonneux 856*ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF); 857004d4b27SMathieu Xhonneux if (!nest) 858004d4b27SMathieu Xhonneux return -EMSGSIZE; 859004d4b27SMathieu Xhonneux 860004d4b27SMathieu Xhonneux if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id)) 861004d4b27SMathieu Xhonneux return -EMSGSIZE; 862004d4b27SMathieu Xhonneux 863004d4b27SMathieu Xhonneux if (slwt->bpf.name && 864004d4b27SMathieu Xhonneux nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name)) 865004d4b27SMathieu Xhonneux return -EMSGSIZE; 866004d4b27SMathieu Xhonneux 867004d4b27SMathieu Xhonneux return nla_nest_end(skb, nest); 868004d4b27SMathieu Xhonneux } 869004d4b27SMathieu Xhonneux 870004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 871004d4b27SMathieu Xhonneux { 872004d4b27SMathieu Xhonneux if (!a->bpf.name && !b->bpf.name) 873004d4b27SMathieu Xhonneux return 0; 874004d4b27SMathieu Xhonneux 875004d4b27SMathieu Xhonneux if (!a->bpf.name || !b->bpf.name) 876004d4b27SMathieu Xhonneux return 1; 877004d4b27SMathieu Xhonneux 878004d4b27SMathieu Xhonneux return strcmp(a->bpf.name, b->bpf.name); 879004d4b27SMathieu Xhonneux } 880004d4b27SMathieu Xhonneux 881d1df6fd8SDavid Lebrun struct seg6_action_param { 882d1df6fd8SDavid Lebrun int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt); 883d1df6fd8SDavid Lebrun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 884d1df6fd8SDavid Lebrun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); 885d1df6fd8SDavid Lebrun }; 886d1df6fd8SDavid Lebrun 887d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { 8882d9cc60aSDavid Lebrun [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh, 8892d9cc60aSDavid Lebrun .put = put_nla_srh, 8902d9cc60aSDavid Lebrun .cmp = cmp_nla_srh }, 891d1df6fd8SDavid Lebrun 8922d9cc60aSDavid Lebrun [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table, 8932d9cc60aSDavid Lebrun .put = put_nla_table, 8942d9cc60aSDavid Lebrun .cmp = cmp_nla_table }, 895d1df6fd8SDavid Lebrun 8962d9cc60aSDavid Lebrun [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4, 8972d9cc60aSDavid Lebrun .put = put_nla_nh4, 8982d9cc60aSDavid Lebrun .cmp = cmp_nla_nh4 }, 899d1df6fd8SDavid Lebrun 9002d9cc60aSDavid Lebrun [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6, 9012d9cc60aSDavid Lebrun .put = put_nla_nh6, 9022d9cc60aSDavid Lebrun .cmp = cmp_nla_nh6 }, 903d1df6fd8SDavid Lebrun 9042d9cc60aSDavid Lebrun [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif, 9052d9cc60aSDavid Lebrun .put = put_nla_iif, 9062d9cc60aSDavid Lebrun .cmp = cmp_nla_iif }, 907d1df6fd8SDavid Lebrun 9082d9cc60aSDavid Lebrun [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif, 9092d9cc60aSDavid Lebrun .put = put_nla_oif, 9102d9cc60aSDavid Lebrun .cmp = cmp_nla_oif }, 911004d4b27SMathieu Xhonneux 912004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .parse = parse_nla_bpf, 913004d4b27SMathieu Xhonneux .put = put_nla_bpf, 914004d4b27SMathieu Xhonneux .cmp = cmp_nla_bpf }, 915004d4b27SMathieu Xhonneux 916d1df6fd8SDavid Lebrun }; 917d1df6fd8SDavid Lebrun 918d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) 919d1df6fd8SDavid Lebrun { 920d1df6fd8SDavid Lebrun struct seg6_action_param *param; 921d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 922d1df6fd8SDavid Lebrun int i, err; 923d1df6fd8SDavid Lebrun 924d1df6fd8SDavid Lebrun desc = __get_action_desc(slwt->action); 925d1df6fd8SDavid Lebrun if (!desc) 926d1df6fd8SDavid Lebrun return -EINVAL; 927d1df6fd8SDavid Lebrun 928d1df6fd8SDavid Lebrun if (!desc->input) 929d1df6fd8SDavid Lebrun return -EOPNOTSUPP; 930d1df6fd8SDavid Lebrun 931d1df6fd8SDavid Lebrun slwt->desc = desc; 932d1df6fd8SDavid Lebrun slwt->headroom += desc->static_headroom; 933d1df6fd8SDavid Lebrun 934d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 935d1df6fd8SDavid Lebrun if (desc->attrs & (1 << i)) { 936d1df6fd8SDavid Lebrun if (!attrs[i]) 937d1df6fd8SDavid Lebrun return -EINVAL; 938d1df6fd8SDavid Lebrun 939d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 940d1df6fd8SDavid Lebrun 941d1df6fd8SDavid Lebrun err = param->parse(attrs, slwt); 942d1df6fd8SDavid Lebrun if (err < 0) 943d1df6fd8SDavid Lebrun return err; 944d1df6fd8SDavid Lebrun } 945d1df6fd8SDavid Lebrun } 946d1df6fd8SDavid Lebrun 947d1df6fd8SDavid Lebrun return 0; 948d1df6fd8SDavid Lebrun } 949d1df6fd8SDavid Lebrun 950d1df6fd8SDavid Lebrun static int seg6_local_build_state(struct nlattr *nla, unsigned int family, 951d1df6fd8SDavid Lebrun const void *cfg, struct lwtunnel_state **ts, 952d1df6fd8SDavid Lebrun struct netlink_ext_ack *extack) 953d1df6fd8SDavid Lebrun { 954d1df6fd8SDavid Lebrun struct nlattr *tb[SEG6_LOCAL_MAX + 1]; 955d1df6fd8SDavid Lebrun struct lwtunnel_state *newts; 956d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 957d1df6fd8SDavid Lebrun int err; 958d1df6fd8SDavid Lebrun 9596285217fSDavid Lebrun if (family != AF_INET6) 9606285217fSDavid Lebrun return -EINVAL; 9616285217fSDavid Lebrun 962d1df6fd8SDavid Lebrun err = nla_parse_nested(tb, SEG6_LOCAL_MAX, nla, seg6_local_policy, 963d1df6fd8SDavid Lebrun extack); 964d1df6fd8SDavid Lebrun 965d1df6fd8SDavid Lebrun if (err < 0) 966d1df6fd8SDavid Lebrun return err; 967d1df6fd8SDavid Lebrun 968d1df6fd8SDavid Lebrun if (!tb[SEG6_LOCAL_ACTION]) 969d1df6fd8SDavid Lebrun return -EINVAL; 970d1df6fd8SDavid Lebrun 971d1df6fd8SDavid Lebrun newts = lwtunnel_state_alloc(sizeof(*slwt)); 972d1df6fd8SDavid Lebrun if (!newts) 973d1df6fd8SDavid Lebrun return -ENOMEM; 974d1df6fd8SDavid Lebrun 975d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(newts); 976d1df6fd8SDavid Lebrun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); 977d1df6fd8SDavid Lebrun 978d1df6fd8SDavid Lebrun err = parse_nla_action(tb, slwt); 979d1df6fd8SDavid Lebrun if (err < 0) 980d1df6fd8SDavid Lebrun goto out_free; 981d1df6fd8SDavid Lebrun 982d1df6fd8SDavid Lebrun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; 983d1df6fd8SDavid Lebrun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; 984d1df6fd8SDavid Lebrun newts->headroom = slwt->headroom; 985d1df6fd8SDavid Lebrun 986d1df6fd8SDavid Lebrun *ts = newts; 987d1df6fd8SDavid Lebrun 988d1df6fd8SDavid Lebrun return 0; 989d1df6fd8SDavid Lebrun 990d1df6fd8SDavid Lebrun out_free: 991d1df6fd8SDavid Lebrun kfree(slwt->srh); 992d1df6fd8SDavid Lebrun kfree(newts); 993d1df6fd8SDavid Lebrun return err; 994d1df6fd8SDavid Lebrun } 995d1df6fd8SDavid Lebrun 996d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt) 997d1df6fd8SDavid Lebrun { 998d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 999d1df6fd8SDavid Lebrun 1000d1df6fd8SDavid Lebrun kfree(slwt->srh); 1001004d4b27SMathieu Xhonneux 1002004d4b27SMathieu Xhonneux if (slwt->desc->attrs & (1 << SEG6_LOCAL_BPF)) { 1003004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 1004004d4b27SMathieu Xhonneux bpf_prog_put(slwt->bpf.prog); 1005004d4b27SMathieu Xhonneux } 1006004d4b27SMathieu Xhonneux 1007004d4b27SMathieu Xhonneux return; 1008d1df6fd8SDavid Lebrun } 1009d1df6fd8SDavid Lebrun 1010d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb, 1011d1df6fd8SDavid Lebrun struct lwtunnel_state *lwt) 1012d1df6fd8SDavid Lebrun { 1013d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1014d1df6fd8SDavid Lebrun struct seg6_action_param *param; 1015d1df6fd8SDavid Lebrun int i, err; 1016d1df6fd8SDavid Lebrun 1017d1df6fd8SDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) 1018d1df6fd8SDavid Lebrun return -EMSGSIZE; 1019d1df6fd8SDavid Lebrun 1020d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1021d1df6fd8SDavid Lebrun if (slwt->desc->attrs & (1 << i)) { 1022d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1023d1df6fd8SDavid Lebrun err = param->put(skb, slwt); 1024d1df6fd8SDavid Lebrun if (err < 0) 1025d1df6fd8SDavid Lebrun return err; 1026d1df6fd8SDavid Lebrun } 1027d1df6fd8SDavid Lebrun } 1028d1df6fd8SDavid Lebrun 1029d1df6fd8SDavid Lebrun return 0; 1030d1df6fd8SDavid Lebrun } 1031d1df6fd8SDavid Lebrun 1032d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) 1033d1df6fd8SDavid Lebrun { 1034d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1035d1df6fd8SDavid Lebrun unsigned long attrs; 1036d1df6fd8SDavid Lebrun int nlsize; 1037d1df6fd8SDavid Lebrun 1038d1df6fd8SDavid Lebrun nlsize = nla_total_size(4); /* action */ 1039d1df6fd8SDavid Lebrun 1040d1df6fd8SDavid Lebrun attrs = slwt->desc->attrs; 1041d1df6fd8SDavid Lebrun 1042d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_SRH)) 1043d1df6fd8SDavid Lebrun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); 1044d1df6fd8SDavid Lebrun 1045d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_TABLE)) 1046d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1047d1df6fd8SDavid Lebrun 1048d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_NH4)) 1049d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1050d1df6fd8SDavid Lebrun 1051d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_NH6)) 1052d1df6fd8SDavid Lebrun nlsize += nla_total_size(16); 1053d1df6fd8SDavid Lebrun 1054d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_IIF)) 1055d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1056d1df6fd8SDavid Lebrun 1057d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_OIF)) 1058d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1059d1df6fd8SDavid Lebrun 1060004d4b27SMathieu Xhonneux if (attrs & (1 << SEG6_LOCAL_BPF)) 1061004d4b27SMathieu Xhonneux nlsize += nla_total_size(sizeof(struct nlattr)) + 1062004d4b27SMathieu Xhonneux nla_total_size(MAX_PROG_NAME) + 1063004d4b27SMathieu Xhonneux nla_total_size(4); 1064004d4b27SMathieu Xhonneux 1065d1df6fd8SDavid Lebrun return nlsize; 1066d1df6fd8SDavid Lebrun } 1067d1df6fd8SDavid Lebrun 1068d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a, 1069d1df6fd8SDavid Lebrun struct lwtunnel_state *b) 1070d1df6fd8SDavid Lebrun { 1071d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt_a, *slwt_b; 1072d1df6fd8SDavid Lebrun struct seg6_action_param *param; 1073d1df6fd8SDavid Lebrun int i; 1074d1df6fd8SDavid Lebrun 1075d1df6fd8SDavid Lebrun slwt_a = seg6_local_lwtunnel(a); 1076d1df6fd8SDavid Lebrun slwt_b = seg6_local_lwtunnel(b); 1077d1df6fd8SDavid Lebrun 1078d1df6fd8SDavid Lebrun if (slwt_a->action != slwt_b->action) 1079d1df6fd8SDavid Lebrun return 1; 1080d1df6fd8SDavid Lebrun 1081d1df6fd8SDavid Lebrun if (slwt_a->desc->attrs != slwt_b->desc->attrs) 1082d1df6fd8SDavid Lebrun return 1; 1083d1df6fd8SDavid Lebrun 1084d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1085d1df6fd8SDavid Lebrun if (slwt_a->desc->attrs & (1 << i)) { 1086d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1087d1df6fd8SDavid Lebrun if (param->cmp(slwt_a, slwt_b)) 1088d1df6fd8SDavid Lebrun return 1; 1089d1df6fd8SDavid Lebrun } 1090d1df6fd8SDavid Lebrun } 1091d1df6fd8SDavid Lebrun 1092d1df6fd8SDavid Lebrun return 0; 1093d1df6fd8SDavid Lebrun } 1094d1df6fd8SDavid Lebrun 1095d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = { 1096d1df6fd8SDavid Lebrun .build_state = seg6_local_build_state, 1097d1df6fd8SDavid Lebrun .destroy_state = seg6_local_destroy_state, 1098d1df6fd8SDavid Lebrun .input = seg6_local_input, 1099d1df6fd8SDavid Lebrun .fill_encap = seg6_local_fill_encap, 1100d1df6fd8SDavid Lebrun .get_encap_size = seg6_local_get_encap_size, 1101d1df6fd8SDavid Lebrun .cmp_encap = seg6_local_cmp_encap, 1102d1df6fd8SDavid Lebrun .owner = THIS_MODULE, 1103d1df6fd8SDavid Lebrun }; 1104d1df6fd8SDavid Lebrun 1105d1df6fd8SDavid Lebrun int __init seg6_local_init(void) 1106d1df6fd8SDavid Lebrun { 1107d1df6fd8SDavid Lebrun return lwtunnel_encap_add_ops(&seg6_local_ops, 1108d1df6fd8SDavid Lebrun LWTUNNEL_ENCAP_SEG6_LOCAL); 1109d1df6fd8SDavid Lebrun } 1110d1df6fd8SDavid Lebrun 1111d1df6fd8SDavid Lebrun void seg6_local_exit(void) 1112d1df6fd8SDavid Lebrun { 1113d1df6fd8SDavid Lebrun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); 1114d1df6fd8SDavid Lebrun } 1115