1*2874c5fdSThomas 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> 26d1df6fd8SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 27d1df6fd8SDavid Lebrun #include <net/seg6_hmac.h> 28d1df6fd8SDavid Lebrun #endif 291c1e761eSMathieu Xhonneux #include <net/seg6_local.h> 30891ef8ddSDavid Lebrun #include <linux/etherdevice.h> 31004d4b27SMathieu Xhonneux #include <linux/bpf.h> 32d1df6fd8SDavid Lebrun 33d1df6fd8SDavid Lebrun struct seg6_local_lwt; 34d1df6fd8SDavid Lebrun 35d1df6fd8SDavid Lebrun struct seg6_action_desc { 36d1df6fd8SDavid Lebrun int action; 37d1df6fd8SDavid Lebrun unsigned long attrs; 38d1df6fd8SDavid Lebrun int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 39d1df6fd8SDavid Lebrun int static_headroom; 40d1df6fd8SDavid Lebrun }; 41d1df6fd8SDavid Lebrun 42004d4b27SMathieu Xhonneux struct bpf_lwt_prog { 43004d4b27SMathieu Xhonneux struct bpf_prog *prog; 44004d4b27SMathieu Xhonneux char *name; 45004d4b27SMathieu Xhonneux }; 46004d4b27SMathieu Xhonneux 47d1df6fd8SDavid Lebrun struct seg6_local_lwt { 48d1df6fd8SDavid Lebrun int action; 49d1df6fd8SDavid Lebrun struct ipv6_sr_hdr *srh; 50d1df6fd8SDavid Lebrun int table; 51d1df6fd8SDavid Lebrun struct in_addr nh4; 52d1df6fd8SDavid Lebrun struct in6_addr nh6; 53d1df6fd8SDavid Lebrun int iif; 54d1df6fd8SDavid Lebrun int oif; 55004d4b27SMathieu Xhonneux struct bpf_lwt_prog bpf; 56d1df6fd8SDavid Lebrun 57d1df6fd8SDavid Lebrun int headroom; 58d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 59d1df6fd8SDavid Lebrun }; 60d1df6fd8SDavid Lebrun 61d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) 62d1df6fd8SDavid Lebrun { 63d1df6fd8SDavid Lebrun return (struct seg6_local_lwt *)lwt->data; 64d1df6fd8SDavid Lebrun } 65d1df6fd8SDavid Lebrun 66140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb) 67140f04c3SDavid Lebrun { 68140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 695829d70bSAhmed Abdelsalam int len, srhoff = 0; 70140f04c3SDavid Lebrun 715829d70bSAhmed Abdelsalam if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0) 72140f04c3SDavid Lebrun return NULL; 73140f04c3SDavid Lebrun 745829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + sizeof(*srh))) 755829d70bSAhmed Abdelsalam return NULL; 765829d70bSAhmed Abdelsalam 775829d70bSAhmed Abdelsalam srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 785829d70bSAhmed Abdelsalam 79140f04c3SDavid Lebrun len = (srh->hdrlen + 1) << 3; 80140f04c3SDavid Lebrun 815829d70bSAhmed Abdelsalam if (!pskb_may_pull(skb, srhoff + len)) 82140f04c3SDavid Lebrun return NULL; 83140f04c3SDavid Lebrun 84140f04c3SDavid Lebrun if (!seg6_validate_srh(srh, len)) 85140f04c3SDavid Lebrun return NULL; 86140f04c3SDavid Lebrun 87140f04c3SDavid Lebrun return srh; 88140f04c3SDavid Lebrun } 89140f04c3SDavid Lebrun 90140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) 91140f04c3SDavid Lebrun { 92140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 93140f04c3SDavid Lebrun 94140f04c3SDavid Lebrun srh = get_srh(skb); 95140f04c3SDavid Lebrun if (!srh) 96140f04c3SDavid Lebrun return NULL; 97140f04c3SDavid Lebrun 98140f04c3SDavid Lebrun if (srh->segments_left == 0) 99140f04c3SDavid Lebrun return NULL; 100140f04c3SDavid Lebrun 101140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 102140f04c3SDavid Lebrun if (!seg6_hmac_validate_skb(skb)) 103140f04c3SDavid Lebrun return NULL; 104140f04c3SDavid Lebrun #endif 105140f04c3SDavid Lebrun 106140f04c3SDavid Lebrun return srh; 107140f04c3SDavid Lebrun } 108140f04c3SDavid Lebrun 109d7a669ddSDavid Lebrun static bool decap_and_validate(struct sk_buff *skb, int proto) 110d7a669ddSDavid Lebrun { 111d7a669ddSDavid Lebrun struct ipv6_sr_hdr *srh; 112d7a669ddSDavid Lebrun unsigned int off = 0; 113d7a669ddSDavid Lebrun 114d7a669ddSDavid Lebrun srh = get_srh(skb); 115d7a669ddSDavid Lebrun if (srh && srh->segments_left > 0) 116d7a669ddSDavid Lebrun return false; 117d7a669ddSDavid Lebrun 118d7a669ddSDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 119d7a669ddSDavid Lebrun if (srh && !seg6_hmac_validate_skb(skb)) 120d7a669ddSDavid Lebrun return false; 121d7a669ddSDavid Lebrun #endif 122d7a669ddSDavid Lebrun 123d7a669ddSDavid Lebrun if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0) 124d7a669ddSDavid Lebrun return false; 125d7a669ddSDavid Lebrun 126d7a669ddSDavid Lebrun if (!pskb_pull(skb, off)) 127d7a669ddSDavid Lebrun return false; 128d7a669ddSDavid Lebrun 129d7a669ddSDavid Lebrun skb_postpull_rcsum(skb, skb_network_header(skb), off); 130d7a669ddSDavid Lebrun 131d7a669ddSDavid Lebrun skb_reset_network_header(skb); 132d7a669ddSDavid Lebrun skb_reset_transport_header(skb); 133d7a669ddSDavid Lebrun skb->encapsulation = 0; 134d7a669ddSDavid Lebrun 135d7a669ddSDavid Lebrun return true; 136d7a669ddSDavid Lebrun } 137d7a669ddSDavid Lebrun 138d7a669ddSDavid Lebrun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr) 139d7a669ddSDavid Lebrun { 140d7a669ddSDavid Lebrun struct in6_addr *addr; 141d7a669ddSDavid Lebrun 142d7a669ddSDavid Lebrun srh->segments_left--; 143d7a669ddSDavid Lebrun addr = srh->segments + srh->segments_left; 144d7a669ddSDavid Lebrun *daddr = *addr; 145d7a669ddSDavid Lebrun } 146d7a669ddSDavid Lebrun 1471c1e761eSMathieu Xhonneux int seg6_lookup_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr, 148d7a669ddSDavid Lebrun u32 tbl_id) 149d7a669ddSDavid Lebrun { 150d7a669ddSDavid Lebrun struct net *net = dev_net(skb->dev); 151d7a669ddSDavid Lebrun struct ipv6hdr *hdr = ipv6_hdr(skb); 152d7a669ddSDavid Lebrun int flags = RT6_LOOKUP_F_HAS_SADDR; 153d7a669ddSDavid Lebrun struct dst_entry *dst = NULL; 154d7a669ddSDavid Lebrun struct rt6_info *rt; 155d7a669ddSDavid Lebrun struct flowi6 fl6; 156d7a669ddSDavid Lebrun 157d7a669ddSDavid Lebrun fl6.flowi6_iif = skb->dev->ifindex; 158d7a669ddSDavid Lebrun fl6.daddr = nhaddr ? *nhaddr : hdr->daddr; 159d7a669ddSDavid Lebrun fl6.saddr = hdr->saddr; 160d7a669ddSDavid Lebrun fl6.flowlabel = ip6_flowinfo(hdr); 161d7a669ddSDavid Lebrun fl6.flowi6_mark = skb->mark; 162d7a669ddSDavid Lebrun fl6.flowi6_proto = hdr->nexthdr; 163d7a669ddSDavid Lebrun 164d7a669ddSDavid Lebrun if (nhaddr) 165d7a669ddSDavid Lebrun fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH; 166d7a669ddSDavid Lebrun 167d7a669ddSDavid Lebrun if (!tbl_id) { 168b75cc8f9SDavid Ahern dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags); 169d7a669ddSDavid Lebrun } else { 170d7a669ddSDavid Lebrun struct fib6_table *table; 171d7a669ddSDavid Lebrun 172d7a669ddSDavid Lebrun table = fib6_get_table(net, tbl_id); 173d7a669ddSDavid Lebrun if (!table) 174d7a669ddSDavid Lebrun goto out; 175d7a669ddSDavid Lebrun 176b75cc8f9SDavid Ahern rt = ip6_pol_route(net, table, 0, &fl6, skb, flags); 177d7a669ddSDavid Lebrun dst = &rt->dst; 178d7a669ddSDavid Lebrun } 179d7a669ddSDavid Lebrun 180d7a669ddSDavid Lebrun if (dst && dst->dev->flags & IFF_LOOPBACK && !dst->error) { 181d7a669ddSDavid Lebrun dst_release(dst); 182d7a669ddSDavid Lebrun dst = NULL; 183d7a669ddSDavid Lebrun } 184d7a669ddSDavid Lebrun 185d7a669ddSDavid Lebrun out: 186d7a669ddSDavid Lebrun if (!dst) { 187d7a669ddSDavid Lebrun rt = net->ipv6.ip6_blk_hole_entry; 188d7a669ddSDavid Lebrun dst = &rt->dst; 189d7a669ddSDavid Lebrun dst_hold(dst); 190d7a669ddSDavid Lebrun } 191d7a669ddSDavid Lebrun 192d7a669ddSDavid Lebrun skb_dst_drop(skb); 193d7a669ddSDavid Lebrun skb_dst_set(skb, dst); 1941c1e761eSMathieu Xhonneux return dst->error; 195d7a669ddSDavid Lebrun } 196d7a669ddSDavid Lebrun 197140f04c3SDavid Lebrun /* regular endpoint function */ 198140f04c3SDavid Lebrun static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 199140f04c3SDavid Lebrun { 200140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 201140f04c3SDavid Lebrun 202140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 203140f04c3SDavid Lebrun if (!srh) 204140f04c3SDavid Lebrun goto drop; 205140f04c3SDavid Lebrun 206d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 207140f04c3SDavid Lebrun 2081c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 209140f04c3SDavid Lebrun 210140f04c3SDavid Lebrun return dst_input(skb); 211140f04c3SDavid Lebrun 212140f04c3SDavid Lebrun drop: 213140f04c3SDavid Lebrun kfree_skb(skb); 214140f04c3SDavid Lebrun return -EINVAL; 215140f04c3SDavid Lebrun } 216140f04c3SDavid Lebrun 217140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */ 218140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) 219140f04c3SDavid Lebrun { 220140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 221140f04c3SDavid Lebrun 222140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 223140f04c3SDavid Lebrun if (!srh) 224140f04c3SDavid Lebrun goto drop; 225140f04c3SDavid Lebrun 226d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 227140f04c3SDavid Lebrun 2281c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, &slwt->nh6, 0); 229140f04c3SDavid Lebrun 230140f04c3SDavid Lebrun return dst_input(skb); 231140f04c3SDavid Lebrun 232140f04c3SDavid Lebrun drop: 233140f04c3SDavid Lebrun kfree_skb(skb); 234140f04c3SDavid Lebrun return -EINVAL; 235140f04c3SDavid Lebrun } 236140f04c3SDavid Lebrun 237891ef8ddSDavid Lebrun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) 238891ef8ddSDavid Lebrun { 239891ef8ddSDavid Lebrun struct ipv6_sr_hdr *srh; 240891ef8ddSDavid Lebrun 241891ef8ddSDavid Lebrun srh = get_and_validate_srh(skb); 242891ef8ddSDavid Lebrun if (!srh) 243891ef8ddSDavid Lebrun goto drop; 244891ef8ddSDavid Lebrun 245891ef8ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 246891ef8ddSDavid Lebrun 2471c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, slwt->table); 248891ef8ddSDavid Lebrun 249891ef8ddSDavid Lebrun return dst_input(skb); 250891ef8ddSDavid Lebrun 251891ef8ddSDavid Lebrun drop: 252891ef8ddSDavid Lebrun kfree_skb(skb); 253891ef8ddSDavid Lebrun return -EINVAL; 254891ef8ddSDavid Lebrun } 255891ef8ddSDavid Lebrun 256891ef8ddSDavid Lebrun /* decapsulate and forward inner L2 frame on specified interface */ 257891ef8ddSDavid Lebrun static int input_action_end_dx2(struct sk_buff *skb, 258891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 259891ef8ddSDavid Lebrun { 260891ef8ddSDavid Lebrun struct net *net = dev_net(skb->dev); 261891ef8ddSDavid Lebrun struct net_device *odev; 262891ef8ddSDavid Lebrun struct ethhdr *eth; 263891ef8ddSDavid Lebrun 264891ef8ddSDavid Lebrun if (!decap_and_validate(skb, NEXTHDR_NONE)) 265891ef8ddSDavid Lebrun goto drop; 266891ef8ddSDavid Lebrun 267891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, ETH_HLEN)) 268891ef8ddSDavid Lebrun goto drop; 269891ef8ddSDavid Lebrun 270891ef8ddSDavid Lebrun skb_reset_mac_header(skb); 271891ef8ddSDavid Lebrun eth = (struct ethhdr *)skb->data; 272891ef8ddSDavid Lebrun 273891ef8ddSDavid Lebrun /* To determine the frame's protocol, we assume it is 802.3. This avoids 274891ef8ddSDavid Lebrun * a call to eth_type_trans(), which is not really relevant for our 275891ef8ddSDavid Lebrun * use case. 276891ef8ddSDavid Lebrun */ 277891ef8ddSDavid Lebrun if (!eth_proto_is_802_3(eth->h_proto)) 278891ef8ddSDavid Lebrun goto drop; 279891ef8ddSDavid Lebrun 280891ef8ddSDavid Lebrun odev = dev_get_by_index_rcu(net, slwt->oif); 281891ef8ddSDavid Lebrun if (!odev) 282891ef8ddSDavid Lebrun goto drop; 283891ef8ddSDavid Lebrun 284891ef8ddSDavid Lebrun /* As we accept Ethernet frames, make sure the egress device is of 285891ef8ddSDavid Lebrun * the correct type. 286891ef8ddSDavid Lebrun */ 287891ef8ddSDavid Lebrun if (odev->type != ARPHRD_ETHER) 288891ef8ddSDavid Lebrun goto drop; 289891ef8ddSDavid Lebrun 290891ef8ddSDavid Lebrun if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev)) 291891ef8ddSDavid Lebrun goto drop; 292891ef8ddSDavid Lebrun 293891ef8ddSDavid Lebrun skb_orphan(skb); 294891ef8ddSDavid Lebrun 295891ef8ddSDavid Lebrun if (skb_warn_if_lro(skb)) 296891ef8ddSDavid Lebrun goto drop; 297891ef8ddSDavid Lebrun 298891ef8ddSDavid Lebrun skb_forward_csum(skb); 299891ef8ddSDavid Lebrun 300891ef8ddSDavid Lebrun if (skb->len - ETH_HLEN > odev->mtu) 301891ef8ddSDavid Lebrun goto drop; 302891ef8ddSDavid Lebrun 303891ef8ddSDavid Lebrun skb->dev = odev; 304891ef8ddSDavid Lebrun skb->protocol = eth->h_proto; 305891ef8ddSDavid Lebrun 306891ef8ddSDavid Lebrun return dev_queue_xmit(skb); 307891ef8ddSDavid Lebrun 308891ef8ddSDavid Lebrun drop: 309891ef8ddSDavid Lebrun kfree_skb(skb); 310891ef8ddSDavid Lebrun return -EINVAL; 311891ef8ddSDavid Lebrun } 312891ef8ddSDavid Lebrun 313140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */ 314140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb, 315140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 316140f04c3SDavid Lebrun { 317d7a669ddSDavid Lebrun struct in6_addr *nhaddr = NULL; 318140f04c3SDavid Lebrun 319140f04c3SDavid Lebrun /* this function accepts IPv6 encapsulated packets, with either 320140f04c3SDavid Lebrun * an SRH with SL=0, or no SRH. 321140f04c3SDavid Lebrun */ 322140f04c3SDavid Lebrun 323d7a669ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 324140f04c3SDavid Lebrun goto drop; 325140f04c3SDavid Lebrun 326d7a669ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 327140f04c3SDavid Lebrun goto drop; 328140f04c3SDavid Lebrun 329140f04c3SDavid Lebrun /* The inner packet is not associated to any local interface, 330140f04c3SDavid Lebrun * so we do not call netif_rx(). 331140f04c3SDavid Lebrun * 332140f04c3SDavid Lebrun * If slwt->nh6 is set to ::, then lookup the nexthop for the 333140f04c3SDavid Lebrun * inner packet's DA. Otherwise, use the specified nexthop. 334140f04c3SDavid Lebrun */ 335140f04c3SDavid Lebrun 336d7a669ddSDavid Lebrun if (!ipv6_addr_any(&slwt->nh6)) 337d7a669ddSDavid Lebrun nhaddr = &slwt->nh6; 338140f04c3SDavid Lebrun 3391c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, nhaddr, 0); 340140f04c3SDavid Lebrun 341140f04c3SDavid Lebrun return dst_input(skb); 342140f04c3SDavid Lebrun drop: 343140f04c3SDavid Lebrun kfree_skb(skb); 344140f04c3SDavid Lebrun return -EINVAL; 345140f04c3SDavid Lebrun } 346140f04c3SDavid Lebrun 347891ef8ddSDavid Lebrun static int input_action_end_dx4(struct sk_buff *skb, 348891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 349891ef8ddSDavid Lebrun { 350891ef8ddSDavid Lebrun struct iphdr *iph; 351891ef8ddSDavid Lebrun __be32 nhaddr; 352891ef8ddSDavid Lebrun int err; 353891ef8ddSDavid Lebrun 354891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPIP)) 355891ef8ddSDavid Lebrun goto drop; 356891ef8ddSDavid Lebrun 357891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct iphdr))) 358891ef8ddSDavid Lebrun goto drop; 359891ef8ddSDavid Lebrun 360891ef8ddSDavid Lebrun skb->protocol = htons(ETH_P_IP); 361891ef8ddSDavid Lebrun 362891ef8ddSDavid Lebrun iph = ip_hdr(skb); 363891ef8ddSDavid Lebrun 364891ef8ddSDavid Lebrun nhaddr = slwt->nh4.s_addr ?: iph->daddr; 365891ef8ddSDavid Lebrun 366891ef8ddSDavid Lebrun skb_dst_drop(skb); 367891ef8ddSDavid Lebrun 368891ef8ddSDavid Lebrun err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev); 369891ef8ddSDavid Lebrun if (err) 370891ef8ddSDavid Lebrun goto drop; 371891ef8ddSDavid Lebrun 372891ef8ddSDavid Lebrun return dst_input(skb); 373891ef8ddSDavid Lebrun 374891ef8ddSDavid Lebrun drop: 375891ef8ddSDavid Lebrun kfree_skb(skb); 376891ef8ddSDavid Lebrun return -EINVAL; 377891ef8ddSDavid Lebrun } 378891ef8ddSDavid Lebrun 379891ef8ddSDavid Lebrun static int input_action_end_dt6(struct sk_buff *skb, 380891ef8ddSDavid Lebrun struct seg6_local_lwt *slwt) 381891ef8ddSDavid Lebrun { 382891ef8ddSDavid Lebrun if (!decap_and_validate(skb, IPPROTO_IPV6)) 383891ef8ddSDavid Lebrun goto drop; 384891ef8ddSDavid Lebrun 385891ef8ddSDavid Lebrun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 386891ef8ddSDavid Lebrun goto drop; 387891ef8ddSDavid Lebrun 3881c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, slwt->table); 389891ef8ddSDavid Lebrun 390891ef8ddSDavid Lebrun return dst_input(skb); 391891ef8ddSDavid Lebrun 392891ef8ddSDavid Lebrun drop: 393891ef8ddSDavid Lebrun kfree_skb(skb); 394891ef8ddSDavid Lebrun return -EINVAL; 395891ef8ddSDavid Lebrun } 396891ef8ddSDavid Lebrun 397140f04c3SDavid Lebrun /* push an SRH on top of the current one */ 398140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 399140f04c3SDavid Lebrun { 400140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 401140f04c3SDavid Lebrun int err = -EINVAL; 402140f04c3SDavid Lebrun 403140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 404140f04c3SDavid Lebrun if (!srh) 405140f04c3SDavid Lebrun goto drop; 406140f04c3SDavid Lebrun 407140f04c3SDavid Lebrun err = seg6_do_srh_inline(skb, slwt->srh); 408140f04c3SDavid Lebrun if (err) 409140f04c3SDavid Lebrun goto drop; 410140f04c3SDavid Lebrun 411140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 412140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 413140f04c3SDavid Lebrun 4141c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 415140f04c3SDavid Lebrun 416140f04c3SDavid Lebrun return dst_input(skb); 417140f04c3SDavid Lebrun 418140f04c3SDavid Lebrun drop: 419140f04c3SDavid Lebrun kfree_skb(skb); 420140f04c3SDavid Lebrun return err; 421140f04c3SDavid Lebrun } 422140f04c3SDavid Lebrun 423140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */ 424140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb, 425140f04c3SDavid Lebrun 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 434d7a669ddSDavid Lebrun advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 435140f04c3SDavid Lebrun 436140f04c3SDavid Lebrun skb_reset_inner_headers(skb); 437140f04c3SDavid Lebrun skb->encapsulation = 1; 438140f04c3SDavid Lebrun 43932d99d0bSDavid Lebrun err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6); 440140f04c3SDavid Lebrun if (err) 441140f04c3SDavid Lebrun goto drop; 442140f04c3SDavid Lebrun 443140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 444140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 445140f04c3SDavid Lebrun 4461c1e761eSMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 447140f04c3SDavid Lebrun 448140f04c3SDavid Lebrun return dst_input(skb); 449140f04c3SDavid Lebrun 450140f04c3SDavid Lebrun drop: 451140f04c3SDavid Lebrun kfree_skb(skb); 452140f04c3SDavid Lebrun return err; 453140f04c3SDavid Lebrun } 454140f04c3SDavid Lebrun 455fe94cc29SMathieu Xhonneux DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states); 456fe94cc29SMathieu Xhonneux 457486cdf21SMathieu Xhonneux bool seg6_bpf_has_valid_srh(struct sk_buff *skb) 458486cdf21SMathieu Xhonneux { 459486cdf21SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 460486cdf21SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 461486cdf21SMathieu Xhonneux struct ipv6_sr_hdr *srh = srh_state->srh; 462486cdf21SMathieu Xhonneux 463486cdf21SMathieu Xhonneux if (unlikely(srh == NULL)) 464486cdf21SMathieu Xhonneux return false; 465486cdf21SMathieu Xhonneux 466486cdf21SMathieu Xhonneux if (unlikely(!srh_state->valid)) { 467486cdf21SMathieu Xhonneux if ((srh_state->hdrlen & 7) != 0) 468486cdf21SMathieu Xhonneux return false; 469486cdf21SMathieu Xhonneux 470486cdf21SMathieu Xhonneux srh->hdrlen = (u8)(srh_state->hdrlen >> 3); 471486cdf21SMathieu Xhonneux if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3)) 472486cdf21SMathieu Xhonneux return false; 473486cdf21SMathieu Xhonneux 474486cdf21SMathieu Xhonneux srh_state->valid = true; 475486cdf21SMathieu Xhonneux } 476486cdf21SMathieu Xhonneux 477486cdf21SMathieu Xhonneux return true; 478486cdf21SMathieu Xhonneux } 479486cdf21SMathieu Xhonneux 480004d4b27SMathieu Xhonneux static int input_action_end_bpf(struct sk_buff *skb, 481004d4b27SMathieu Xhonneux struct seg6_local_lwt *slwt) 482004d4b27SMathieu Xhonneux { 483004d4b27SMathieu Xhonneux struct seg6_bpf_srh_state *srh_state = 484004d4b27SMathieu Xhonneux this_cpu_ptr(&seg6_bpf_srh_states); 485004d4b27SMathieu Xhonneux struct ipv6_sr_hdr *srh; 486004d4b27SMathieu Xhonneux int ret; 487004d4b27SMathieu Xhonneux 488004d4b27SMathieu Xhonneux srh = get_and_validate_srh(skb); 489486cdf21SMathieu Xhonneux if (!srh) { 490486cdf21SMathieu Xhonneux kfree_skb(skb); 491486cdf21SMathieu Xhonneux return -EINVAL; 492486cdf21SMathieu Xhonneux } 493004d4b27SMathieu Xhonneux advance_nextseg(srh, &ipv6_hdr(skb)->daddr); 494004d4b27SMathieu Xhonneux 495004d4b27SMathieu Xhonneux /* preempt_disable is needed to protect the per-CPU buffer srh_state, 496004d4b27SMathieu Xhonneux * which is also accessed by the bpf_lwt_seg6_* helpers 497004d4b27SMathieu Xhonneux */ 498004d4b27SMathieu Xhonneux preempt_disable(); 499486cdf21SMathieu Xhonneux srh_state->srh = srh; 500004d4b27SMathieu Xhonneux srh_state->hdrlen = srh->hdrlen << 3; 501486cdf21SMathieu Xhonneux srh_state->valid = true; 502004d4b27SMathieu Xhonneux 503004d4b27SMathieu Xhonneux rcu_read_lock(); 504004d4b27SMathieu Xhonneux bpf_compute_data_pointers(skb); 505004d4b27SMathieu Xhonneux ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb); 506004d4b27SMathieu Xhonneux rcu_read_unlock(); 507004d4b27SMathieu Xhonneux 508004d4b27SMathieu Xhonneux switch (ret) { 509004d4b27SMathieu Xhonneux case BPF_OK: 510004d4b27SMathieu Xhonneux case BPF_REDIRECT: 511004d4b27SMathieu Xhonneux break; 512004d4b27SMathieu Xhonneux case BPF_DROP: 513004d4b27SMathieu Xhonneux goto drop; 514004d4b27SMathieu Xhonneux default: 515004d4b27SMathieu Xhonneux pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret); 516004d4b27SMathieu Xhonneux goto drop; 517004d4b27SMathieu Xhonneux } 518004d4b27SMathieu Xhonneux 519486cdf21SMathieu Xhonneux if (srh_state->srh && !seg6_bpf_has_valid_srh(skb)) 520004d4b27SMathieu Xhonneux goto drop; 521004d4b27SMathieu Xhonneux 522486cdf21SMathieu Xhonneux preempt_enable(); 523004d4b27SMathieu Xhonneux if (ret != BPF_REDIRECT) 524004d4b27SMathieu Xhonneux seg6_lookup_nexthop(skb, NULL, 0); 525004d4b27SMathieu Xhonneux 526004d4b27SMathieu Xhonneux return dst_input(skb); 527004d4b27SMathieu Xhonneux 528004d4b27SMathieu Xhonneux drop: 529486cdf21SMathieu Xhonneux preempt_enable(); 530004d4b27SMathieu Xhonneux kfree_skb(skb); 531004d4b27SMathieu Xhonneux return -EINVAL; 532004d4b27SMathieu Xhonneux } 533004d4b27SMathieu Xhonneux 534d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = { 535d1df6fd8SDavid Lebrun { 536d1df6fd8SDavid Lebrun .action = SEG6_LOCAL_ACTION_END, 537d1df6fd8SDavid Lebrun .attrs = 0, 538140f04c3SDavid Lebrun .input = input_action_end, 539d1df6fd8SDavid Lebrun }, 540140f04c3SDavid Lebrun { 541140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_X, 542140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH6), 543140f04c3SDavid Lebrun .input = input_action_end_x, 544140f04c3SDavid Lebrun }, 545140f04c3SDavid Lebrun { 546891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_T, 547891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_TABLE), 548891ef8ddSDavid Lebrun .input = input_action_end_t, 549891ef8ddSDavid Lebrun }, 550891ef8ddSDavid Lebrun { 551891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX2, 552891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_OIF), 553891ef8ddSDavid Lebrun .input = input_action_end_dx2, 554891ef8ddSDavid Lebrun }, 555891ef8ddSDavid Lebrun { 556140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX6, 557140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH6), 558140f04c3SDavid Lebrun .input = input_action_end_dx6, 559140f04c3SDavid Lebrun }, 560140f04c3SDavid Lebrun { 561891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX4, 562891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH4), 563891ef8ddSDavid Lebrun .input = input_action_end_dx4, 564891ef8ddSDavid Lebrun }, 565891ef8ddSDavid Lebrun { 566891ef8ddSDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DT6, 567891ef8ddSDavid Lebrun .attrs = (1 << SEG6_LOCAL_TABLE), 568891ef8ddSDavid Lebrun .input = input_action_end_dt6, 569891ef8ddSDavid Lebrun }, 570891ef8ddSDavid Lebrun { 571140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6, 572140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_SRH), 573140f04c3SDavid Lebrun .input = input_action_end_b6, 574140f04c3SDavid Lebrun }, 575140f04c3SDavid Lebrun { 576140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6_ENCAP, 577140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_SRH), 578140f04c3SDavid Lebrun .input = input_action_end_b6_encap, 579140f04c3SDavid Lebrun .static_headroom = sizeof(struct ipv6hdr), 580004d4b27SMathieu Xhonneux }, 581004d4b27SMathieu Xhonneux { 582004d4b27SMathieu Xhonneux .action = SEG6_LOCAL_ACTION_END_BPF, 583004d4b27SMathieu Xhonneux .attrs = (1 << SEG6_LOCAL_BPF), 584004d4b27SMathieu Xhonneux .input = input_action_end_bpf, 585004d4b27SMathieu Xhonneux }, 586004d4b27SMathieu Xhonneux 587d1df6fd8SDavid Lebrun }; 588d1df6fd8SDavid Lebrun 589d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action) 590d1df6fd8SDavid Lebrun { 591d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 592d1df6fd8SDavid Lebrun int i, count; 593d1df6fd8SDavid Lebrun 594709af180SColin Ian King count = ARRAY_SIZE(seg6_action_table); 595d1df6fd8SDavid Lebrun for (i = 0; i < count; i++) { 596d1df6fd8SDavid Lebrun desc = &seg6_action_table[i]; 597d1df6fd8SDavid Lebrun if (desc->action == action) 598d1df6fd8SDavid Lebrun return desc; 599d1df6fd8SDavid Lebrun } 600d1df6fd8SDavid Lebrun 601d1df6fd8SDavid Lebrun return NULL; 602d1df6fd8SDavid Lebrun } 603d1df6fd8SDavid Lebrun 604d1df6fd8SDavid Lebrun static int seg6_local_input(struct sk_buff *skb) 605d1df6fd8SDavid Lebrun { 606d1df6fd8SDavid Lebrun struct dst_entry *orig_dst = skb_dst(skb); 607d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 608d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 609d1df6fd8SDavid Lebrun 6106285217fSDavid Lebrun if (skb->protocol != htons(ETH_P_IPV6)) { 6116285217fSDavid Lebrun kfree_skb(skb); 6126285217fSDavid Lebrun return -EINVAL; 6136285217fSDavid Lebrun } 6146285217fSDavid Lebrun 615d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 616d1df6fd8SDavid Lebrun desc = slwt->desc; 617d1df6fd8SDavid Lebrun 618d1df6fd8SDavid Lebrun return desc->input(skb, slwt); 619d1df6fd8SDavid Lebrun } 620d1df6fd8SDavid Lebrun 621d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { 622d1df6fd8SDavid Lebrun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, 623d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, 624d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, 625d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, 626d1df6fd8SDavid Lebrun .len = sizeof(struct in_addr) }, 627d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, 628d1df6fd8SDavid Lebrun .len = sizeof(struct in6_addr) }, 629d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, 630d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 631004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .type = NLA_NESTED }, 632d1df6fd8SDavid Lebrun }; 633d1df6fd8SDavid Lebrun 6342d9cc60aSDavid Lebrun static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt) 6352d9cc60aSDavid Lebrun { 6362d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 6372d9cc60aSDavid Lebrun int len; 6382d9cc60aSDavid Lebrun 6392d9cc60aSDavid Lebrun srh = nla_data(attrs[SEG6_LOCAL_SRH]); 6402d9cc60aSDavid Lebrun len = nla_len(attrs[SEG6_LOCAL_SRH]); 6412d9cc60aSDavid Lebrun 6422d9cc60aSDavid Lebrun /* SRH must contain at least one segment */ 6432d9cc60aSDavid Lebrun if (len < sizeof(*srh) + sizeof(struct in6_addr)) 6442d9cc60aSDavid Lebrun return -EINVAL; 6452d9cc60aSDavid Lebrun 6462d9cc60aSDavid Lebrun if (!seg6_validate_srh(srh, len)) 6472d9cc60aSDavid Lebrun return -EINVAL; 6482d9cc60aSDavid Lebrun 6497fa41efaSYueHaibing slwt->srh = kmemdup(srh, len, GFP_KERNEL); 6502d9cc60aSDavid Lebrun if (!slwt->srh) 6512d9cc60aSDavid Lebrun return -ENOMEM; 6522d9cc60aSDavid Lebrun 6532d9cc60aSDavid Lebrun slwt->headroom += len; 6542d9cc60aSDavid Lebrun 6552d9cc60aSDavid Lebrun return 0; 6562d9cc60aSDavid Lebrun } 6572d9cc60aSDavid Lebrun 6582d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt) 6592d9cc60aSDavid Lebrun { 6602d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 6612d9cc60aSDavid Lebrun struct nlattr *nla; 6622d9cc60aSDavid Lebrun int len; 6632d9cc60aSDavid Lebrun 6642d9cc60aSDavid Lebrun srh = slwt->srh; 6652d9cc60aSDavid Lebrun len = (srh->hdrlen + 1) << 3; 6662d9cc60aSDavid Lebrun 6672d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_SRH, len); 6682d9cc60aSDavid Lebrun if (!nla) 6692d9cc60aSDavid Lebrun return -EMSGSIZE; 6702d9cc60aSDavid Lebrun 6712d9cc60aSDavid Lebrun memcpy(nla_data(nla), srh, len); 6722d9cc60aSDavid Lebrun 6732d9cc60aSDavid Lebrun return 0; 6742d9cc60aSDavid Lebrun } 6752d9cc60aSDavid Lebrun 6762d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 6772d9cc60aSDavid Lebrun { 6782d9cc60aSDavid Lebrun int len = (a->srh->hdrlen + 1) << 3; 6792d9cc60aSDavid Lebrun 6802d9cc60aSDavid Lebrun if (len != ((b->srh->hdrlen + 1) << 3)) 6812d9cc60aSDavid Lebrun return 1; 6822d9cc60aSDavid Lebrun 6832d9cc60aSDavid Lebrun return memcmp(a->srh, b->srh, len); 6842d9cc60aSDavid Lebrun } 6852d9cc60aSDavid Lebrun 6862d9cc60aSDavid Lebrun static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt) 6872d9cc60aSDavid Lebrun { 6882d9cc60aSDavid Lebrun slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]); 6892d9cc60aSDavid Lebrun 6902d9cc60aSDavid Lebrun return 0; 6912d9cc60aSDavid Lebrun } 6922d9cc60aSDavid Lebrun 6932d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt) 6942d9cc60aSDavid Lebrun { 6952d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table)) 6962d9cc60aSDavid Lebrun return -EMSGSIZE; 6972d9cc60aSDavid Lebrun 6982d9cc60aSDavid Lebrun return 0; 6992d9cc60aSDavid Lebrun } 7002d9cc60aSDavid Lebrun 7012d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7022d9cc60aSDavid Lebrun { 7032d9cc60aSDavid Lebrun if (a->table != b->table) 7042d9cc60aSDavid Lebrun return 1; 7052d9cc60aSDavid Lebrun 7062d9cc60aSDavid Lebrun return 0; 7072d9cc60aSDavid Lebrun } 7082d9cc60aSDavid Lebrun 7092d9cc60aSDavid Lebrun static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7102d9cc60aSDavid Lebrun { 7112d9cc60aSDavid Lebrun memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]), 7122d9cc60aSDavid Lebrun sizeof(struct in_addr)); 7132d9cc60aSDavid Lebrun 7142d9cc60aSDavid Lebrun return 0; 7152d9cc60aSDavid Lebrun } 7162d9cc60aSDavid Lebrun 7172d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7182d9cc60aSDavid Lebrun { 7192d9cc60aSDavid Lebrun struct nlattr *nla; 7202d9cc60aSDavid Lebrun 7212d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr)); 7222d9cc60aSDavid Lebrun if (!nla) 7232d9cc60aSDavid Lebrun return -EMSGSIZE; 7242d9cc60aSDavid Lebrun 7252d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr)); 7262d9cc60aSDavid Lebrun 7272d9cc60aSDavid Lebrun return 0; 7282d9cc60aSDavid Lebrun } 7292d9cc60aSDavid Lebrun 7302d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7312d9cc60aSDavid Lebrun { 7322d9cc60aSDavid Lebrun return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr)); 7332d9cc60aSDavid Lebrun } 7342d9cc60aSDavid Lebrun 7352d9cc60aSDavid Lebrun static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7362d9cc60aSDavid Lebrun { 7372d9cc60aSDavid Lebrun memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]), 7382d9cc60aSDavid Lebrun sizeof(struct in6_addr)); 7392d9cc60aSDavid Lebrun 7402d9cc60aSDavid Lebrun return 0; 7412d9cc60aSDavid Lebrun } 7422d9cc60aSDavid Lebrun 7432d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7442d9cc60aSDavid Lebrun { 7452d9cc60aSDavid Lebrun struct nlattr *nla; 7462d9cc60aSDavid Lebrun 7472d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr)); 7482d9cc60aSDavid Lebrun if (!nla) 7492d9cc60aSDavid Lebrun return -EMSGSIZE; 7502d9cc60aSDavid Lebrun 7512d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr)); 7522d9cc60aSDavid Lebrun 7532d9cc60aSDavid Lebrun return 0; 7542d9cc60aSDavid Lebrun } 7552d9cc60aSDavid Lebrun 7562d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7572d9cc60aSDavid Lebrun { 7582d9cc60aSDavid Lebrun return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr)); 7592d9cc60aSDavid Lebrun } 7602d9cc60aSDavid Lebrun 7612d9cc60aSDavid Lebrun static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7622d9cc60aSDavid Lebrun { 7632d9cc60aSDavid Lebrun slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]); 7642d9cc60aSDavid Lebrun 7652d9cc60aSDavid Lebrun return 0; 7662d9cc60aSDavid Lebrun } 7672d9cc60aSDavid Lebrun 7682d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7692d9cc60aSDavid Lebrun { 7702d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif)) 7712d9cc60aSDavid Lebrun return -EMSGSIZE; 7722d9cc60aSDavid Lebrun 7732d9cc60aSDavid Lebrun return 0; 7742d9cc60aSDavid Lebrun } 7752d9cc60aSDavid Lebrun 7762d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 7772d9cc60aSDavid Lebrun { 7782d9cc60aSDavid Lebrun if (a->iif != b->iif) 7792d9cc60aSDavid Lebrun return 1; 7802d9cc60aSDavid Lebrun 7812d9cc60aSDavid Lebrun return 0; 7822d9cc60aSDavid Lebrun } 7832d9cc60aSDavid Lebrun 7842d9cc60aSDavid Lebrun static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 7852d9cc60aSDavid Lebrun { 7862d9cc60aSDavid Lebrun slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]); 7872d9cc60aSDavid Lebrun 7882d9cc60aSDavid Lebrun return 0; 7892d9cc60aSDavid Lebrun } 7902d9cc60aSDavid Lebrun 7912d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 7922d9cc60aSDavid Lebrun { 7932d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif)) 7942d9cc60aSDavid Lebrun return -EMSGSIZE; 7952d9cc60aSDavid Lebrun 7962d9cc60aSDavid Lebrun return 0; 7972d9cc60aSDavid Lebrun } 7982d9cc60aSDavid Lebrun 7992d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 8002d9cc60aSDavid Lebrun { 8012d9cc60aSDavid Lebrun if (a->oif != b->oif) 8022d9cc60aSDavid Lebrun return 1; 8032d9cc60aSDavid Lebrun 8042d9cc60aSDavid Lebrun return 0; 8052d9cc60aSDavid Lebrun } 8062d9cc60aSDavid Lebrun 807004d4b27SMathieu Xhonneux #define MAX_PROG_NAME 256 808004d4b27SMathieu Xhonneux static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = { 809004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG] = { .type = NLA_U32, }, 810004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING, 811004d4b27SMathieu Xhonneux .len = MAX_PROG_NAME }, 812004d4b27SMathieu Xhonneux }; 813004d4b27SMathieu Xhonneux 814004d4b27SMathieu Xhonneux static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt) 815004d4b27SMathieu Xhonneux { 816004d4b27SMathieu Xhonneux struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1]; 817004d4b27SMathieu Xhonneux struct bpf_prog *p; 818004d4b27SMathieu Xhonneux int ret; 819004d4b27SMathieu Xhonneux u32 fd; 820004d4b27SMathieu Xhonneux 8218cb08174SJohannes Berg ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX, 8228cb08174SJohannes Berg attrs[SEG6_LOCAL_BPF], 8238cb08174SJohannes Berg bpf_prog_policy, NULL); 824004d4b27SMathieu Xhonneux if (ret < 0) 825004d4b27SMathieu Xhonneux return ret; 826004d4b27SMathieu Xhonneux 827004d4b27SMathieu Xhonneux if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME]) 828004d4b27SMathieu Xhonneux return -EINVAL; 829004d4b27SMathieu Xhonneux 830004d4b27SMathieu Xhonneux slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL); 831004d4b27SMathieu Xhonneux if (!slwt->bpf.name) 832004d4b27SMathieu Xhonneux return -ENOMEM; 833004d4b27SMathieu Xhonneux 834004d4b27SMathieu Xhonneux fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]); 835004d4b27SMathieu Xhonneux p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL); 836004d4b27SMathieu Xhonneux if (IS_ERR(p)) { 837004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 838004d4b27SMathieu Xhonneux return PTR_ERR(p); 839004d4b27SMathieu Xhonneux } 840004d4b27SMathieu Xhonneux 841004d4b27SMathieu Xhonneux slwt->bpf.prog = p; 842004d4b27SMathieu Xhonneux return 0; 843004d4b27SMathieu Xhonneux } 844004d4b27SMathieu Xhonneux 845004d4b27SMathieu Xhonneux static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt) 846004d4b27SMathieu Xhonneux { 847004d4b27SMathieu Xhonneux struct nlattr *nest; 848004d4b27SMathieu Xhonneux 849004d4b27SMathieu Xhonneux if (!slwt->bpf.prog) 850004d4b27SMathieu Xhonneux return 0; 851004d4b27SMathieu Xhonneux 852ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF); 853004d4b27SMathieu Xhonneux if (!nest) 854004d4b27SMathieu Xhonneux return -EMSGSIZE; 855004d4b27SMathieu Xhonneux 856004d4b27SMathieu Xhonneux if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id)) 857004d4b27SMathieu Xhonneux return -EMSGSIZE; 858004d4b27SMathieu Xhonneux 859004d4b27SMathieu Xhonneux if (slwt->bpf.name && 860004d4b27SMathieu Xhonneux nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name)) 861004d4b27SMathieu Xhonneux return -EMSGSIZE; 862004d4b27SMathieu Xhonneux 863004d4b27SMathieu Xhonneux return nla_nest_end(skb, nest); 864004d4b27SMathieu Xhonneux } 865004d4b27SMathieu Xhonneux 866004d4b27SMathieu Xhonneux static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 867004d4b27SMathieu Xhonneux { 868004d4b27SMathieu Xhonneux if (!a->bpf.name && !b->bpf.name) 869004d4b27SMathieu Xhonneux return 0; 870004d4b27SMathieu Xhonneux 871004d4b27SMathieu Xhonneux if (!a->bpf.name || !b->bpf.name) 872004d4b27SMathieu Xhonneux return 1; 873004d4b27SMathieu Xhonneux 874004d4b27SMathieu Xhonneux return strcmp(a->bpf.name, b->bpf.name); 875004d4b27SMathieu Xhonneux } 876004d4b27SMathieu Xhonneux 877d1df6fd8SDavid Lebrun struct seg6_action_param { 878d1df6fd8SDavid Lebrun int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt); 879d1df6fd8SDavid Lebrun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 880d1df6fd8SDavid Lebrun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); 881d1df6fd8SDavid Lebrun }; 882d1df6fd8SDavid Lebrun 883d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { 8842d9cc60aSDavid Lebrun [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh, 8852d9cc60aSDavid Lebrun .put = put_nla_srh, 8862d9cc60aSDavid Lebrun .cmp = cmp_nla_srh }, 887d1df6fd8SDavid Lebrun 8882d9cc60aSDavid Lebrun [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table, 8892d9cc60aSDavid Lebrun .put = put_nla_table, 8902d9cc60aSDavid Lebrun .cmp = cmp_nla_table }, 891d1df6fd8SDavid Lebrun 8922d9cc60aSDavid Lebrun [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4, 8932d9cc60aSDavid Lebrun .put = put_nla_nh4, 8942d9cc60aSDavid Lebrun .cmp = cmp_nla_nh4 }, 895d1df6fd8SDavid Lebrun 8962d9cc60aSDavid Lebrun [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6, 8972d9cc60aSDavid Lebrun .put = put_nla_nh6, 8982d9cc60aSDavid Lebrun .cmp = cmp_nla_nh6 }, 899d1df6fd8SDavid Lebrun 9002d9cc60aSDavid Lebrun [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif, 9012d9cc60aSDavid Lebrun .put = put_nla_iif, 9022d9cc60aSDavid Lebrun .cmp = cmp_nla_iif }, 903d1df6fd8SDavid Lebrun 9042d9cc60aSDavid Lebrun [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif, 9052d9cc60aSDavid Lebrun .put = put_nla_oif, 9062d9cc60aSDavid Lebrun .cmp = cmp_nla_oif }, 907004d4b27SMathieu Xhonneux 908004d4b27SMathieu Xhonneux [SEG6_LOCAL_BPF] = { .parse = parse_nla_bpf, 909004d4b27SMathieu Xhonneux .put = put_nla_bpf, 910004d4b27SMathieu Xhonneux .cmp = cmp_nla_bpf }, 911004d4b27SMathieu Xhonneux 912d1df6fd8SDavid Lebrun }; 913d1df6fd8SDavid Lebrun 914d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) 915d1df6fd8SDavid Lebrun { 916d1df6fd8SDavid Lebrun struct seg6_action_param *param; 917d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 918d1df6fd8SDavid Lebrun int i, err; 919d1df6fd8SDavid Lebrun 920d1df6fd8SDavid Lebrun desc = __get_action_desc(slwt->action); 921d1df6fd8SDavid Lebrun if (!desc) 922d1df6fd8SDavid Lebrun return -EINVAL; 923d1df6fd8SDavid Lebrun 924d1df6fd8SDavid Lebrun if (!desc->input) 925d1df6fd8SDavid Lebrun return -EOPNOTSUPP; 926d1df6fd8SDavid Lebrun 927d1df6fd8SDavid Lebrun slwt->desc = desc; 928d1df6fd8SDavid Lebrun slwt->headroom += desc->static_headroom; 929d1df6fd8SDavid Lebrun 930d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 931d1df6fd8SDavid Lebrun if (desc->attrs & (1 << i)) { 932d1df6fd8SDavid Lebrun if (!attrs[i]) 933d1df6fd8SDavid Lebrun return -EINVAL; 934d1df6fd8SDavid Lebrun 935d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 936d1df6fd8SDavid Lebrun 937d1df6fd8SDavid Lebrun err = param->parse(attrs, slwt); 938d1df6fd8SDavid Lebrun if (err < 0) 939d1df6fd8SDavid Lebrun return err; 940d1df6fd8SDavid Lebrun } 941d1df6fd8SDavid Lebrun } 942d1df6fd8SDavid Lebrun 943d1df6fd8SDavid Lebrun return 0; 944d1df6fd8SDavid Lebrun } 945d1df6fd8SDavid Lebrun 946d1df6fd8SDavid Lebrun static int seg6_local_build_state(struct nlattr *nla, unsigned int family, 947d1df6fd8SDavid Lebrun const void *cfg, struct lwtunnel_state **ts, 948d1df6fd8SDavid Lebrun struct netlink_ext_ack *extack) 949d1df6fd8SDavid Lebrun { 950d1df6fd8SDavid Lebrun struct nlattr *tb[SEG6_LOCAL_MAX + 1]; 951d1df6fd8SDavid Lebrun struct lwtunnel_state *newts; 952d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 953d1df6fd8SDavid Lebrun int err; 954d1df6fd8SDavid Lebrun 9556285217fSDavid Lebrun if (family != AF_INET6) 9566285217fSDavid Lebrun return -EINVAL; 9576285217fSDavid Lebrun 9588cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla, 9598cb08174SJohannes Berg seg6_local_policy, extack); 960d1df6fd8SDavid Lebrun 961d1df6fd8SDavid Lebrun if (err < 0) 962d1df6fd8SDavid Lebrun return err; 963d1df6fd8SDavid Lebrun 964d1df6fd8SDavid Lebrun if (!tb[SEG6_LOCAL_ACTION]) 965d1df6fd8SDavid Lebrun return -EINVAL; 966d1df6fd8SDavid Lebrun 967d1df6fd8SDavid Lebrun newts = lwtunnel_state_alloc(sizeof(*slwt)); 968d1df6fd8SDavid Lebrun if (!newts) 969d1df6fd8SDavid Lebrun return -ENOMEM; 970d1df6fd8SDavid Lebrun 971d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(newts); 972d1df6fd8SDavid Lebrun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); 973d1df6fd8SDavid Lebrun 974d1df6fd8SDavid Lebrun err = parse_nla_action(tb, slwt); 975d1df6fd8SDavid Lebrun if (err < 0) 976d1df6fd8SDavid Lebrun goto out_free; 977d1df6fd8SDavid Lebrun 978d1df6fd8SDavid Lebrun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; 979d1df6fd8SDavid Lebrun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; 980d1df6fd8SDavid Lebrun newts->headroom = slwt->headroom; 981d1df6fd8SDavid Lebrun 982d1df6fd8SDavid Lebrun *ts = newts; 983d1df6fd8SDavid Lebrun 984d1df6fd8SDavid Lebrun return 0; 985d1df6fd8SDavid Lebrun 986d1df6fd8SDavid Lebrun out_free: 987d1df6fd8SDavid Lebrun kfree(slwt->srh); 988d1df6fd8SDavid Lebrun kfree(newts); 989d1df6fd8SDavid Lebrun return err; 990d1df6fd8SDavid Lebrun } 991d1df6fd8SDavid Lebrun 992d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt) 993d1df6fd8SDavid Lebrun { 994d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 995d1df6fd8SDavid Lebrun 996d1df6fd8SDavid Lebrun kfree(slwt->srh); 997004d4b27SMathieu Xhonneux 998004d4b27SMathieu Xhonneux if (slwt->desc->attrs & (1 << SEG6_LOCAL_BPF)) { 999004d4b27SMathieu Xhonneux kfree(slwt->bpf.name); 1000004d4b27SMathieu Xhonneux bpf_prog_put(slwt->bpf.prog); 1001004d4b27SMathieu Xhonneux } 1002004d4b27SMathieu Xhonneux 1003004d4b27SMathieu Xhonneux return; 1004d1df6fd8SDavid Lebrun } 1005d1df6fd8SDavid Lebrun 1006d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb, 1007d1df6fd8SDavid Lebrun struct lwtunnel_state *lwt) 1008d1df6fd8SDavid Lebrun { 1009d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1010d1df6fd8SDavid Lebrun struct seg6_action_param *param; 1011d1df6fd8SDavid Lebrun int i, err; 1012d1df6fd8SDavid Lebrun 1013d1df6fd8SDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) 1014d1df6fd8SDavid Lebrun return -EMSGSIZE; 1015d1df6fd8SDavid Lebrun 1016d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1017d1df6fd8SDavid Lebrun if (slwt->desc->attrs & (1 << i)) { 1018d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1019d1df6fd8SDavid Lebrun err = param->put(skb, slwt); 1020d1df6fd8SDavid Lebrun if (err < 0) 1021d1df6fd8SDavid Lebrun return err; 1022d1df6fd8SDavid Lebrun } 1023d1df6fd8SDavid Lebrun } 1024d1df6fd8SDavid Lebrun 1025d1df6fd8SDavid Lebrun return 0; 1026d1df6fd8SDavid Lebrun } 1027d1df6fd8SDavid Lebrun 1028d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) 1029d1df6fd8SDavid Lebrun { 1030d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 1031d1df6fd8SDavid Lebrun unsigned long attrs; 1032d1df6fd8SDavid Lebrun int nlsize; 1033d1df6fd8SDavid Lebrun 1034d1df6fd8SDavid Lebrun nlsize = nla_total_size(4); /* action */ 1035d1df6fd8SDavid Lebrun 1036d1df6fd8SDavid Lebrun attrs = slwt->desc->attrs; 1037d1df6fd8SDavid Lebrun 1038d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_SRH)) 1039d1df6fd8SDavid Lebrun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); 1040d1df6fd8SDavid Lebrun 1041d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_TABLE)) 1042d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1043d1df6fd8SDavid Lebrun 1044d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_NH4)) 1045d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1046d1df6fd8SDavid Lebrun 1047d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_NH6)) 1048d1df6fd8SDavid Lebrun nlsize += nla_total_size(16); 1049d1df6fd8SDavid Lebrun 1050d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_IIF)) 1051d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1052d1df6fd8SDavid Lebrun 1053d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_OIF)) 1054d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 1055d1df6fd8SDavid Lebrun 1056004d4b27SMathieu Xhonneux if (attrs & (1 << SEG6_LOCAL_BPF)) 1057004d4b27SMathieu Xhonneux nlsize += nla_total_size(sizeof(struct nlattr)) + 1058004d4b27SMathieu Xhonneux nla_total_size(MAX_PROG_NAME) + 1059004d4b27SMathieu Xhonneux nla_total_size(4); 1060004d4b27SMathieu Xhonneux 1061d1df6fd8SDavid Lebrun return nlsize; 1062d1df6fd8SDavid Lebrun } 1063d1df6fd8SDavid Lebrun 1064d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a, 1065d1df6fd8SDavid Lebrun struct lwtunnel_state *b) 1066d1df6fd8SDavid Lebrun { 1067d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt_a, *slwt_b; 1068d1df6fd8SDavid Lebrun struct seg6_action_param *param; 1069d1df6fd8SDavid Lebrun int i; 1070d1df6fd8SDavid Lebrun 1071d1df6fd8SDavid Lebrun slwt_a = seg6_local_lwtunnel(a); 1072d1df6fd8SDavid Lebrun slwt_b = seg6_local_lwtunnel(b); 1073d1df6fd8SDavid Lebrun 1074d1df6fd8SDavid Lebrun if (slwt_a->action != slwt_b->action) 1075d1df6fd8SDavid Lebrun return 1; 1076d1df6fd8SDavid Lebrun 1077d1df6fd8SDavid Lebrun if (slwt_a->desc->attrs != slwt_b->desc->attrs) 1078d1df6fd8SDavid Lebrun return 1; 1079d1df6fd8SDavid Lebrun 1080d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 1081d1df6fd8SDavid Lebrun if (slwt_a->desc->attrs & (1 << i)) { 1082d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 1083d1df6fd8SDavid Lebrun if (param->cmp(slwt_a, slwt_b)) 1084d1df6fd8SDavid Lebrun return 1; 1085d1df6fd8SDavid Lebrun } 1086d1df6fd8SDavid Lebrun } 1087d1df6fd8SDavid Lebrun 1088d1df6fd8SDavid Lebrun return 0; 1089d1df6fd8SDavid Lebrun } 1090d1df6fd8SDavid Lebrun 1091d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = { 1092d1df6fd8SDavid Lebrun .build_state = seg6_local_build_state, 1093d1df6fd8SDavid Lebrun .destroy_state = seg6_local_destroy_state, 1094d1df6fd8SDavid Lebrun .input = seg6_local_input, 1095d1df6fd8SDavid Lebrun .fill_encap = seg6_local_fill_encap, 1096d1df6fd8SDavid Lebrun .get_encap_size = seg6_local_get_encap_size, 1097d1df6fd8SDavid Lebrun .cmp_encap = seg6_local_cmp_encap, 1098d1df6fd8SDavid Lebrun .owner = THIS_MODULE, 1099d1df6fd8SDavid Lebrun }; 1100d1df6fd8SDavid Lebrun 1101d1df6fd8SDavid Lebrun int __init seg6_local_init(void) 1102d1df6fd8SDavid Lebrun { 1103d1df6fd8SDavid Lebrun return lwtunnel_encap_add_ops(&seg6_local_ops, 1104d1df6fd8SDavid Lebrun LWTUNNEL_ENCAP_SEG6_LOCAL); 1105d1df6fd8SDavid Lebrun } 1106d1df6fd8SDavid Lebrun 1107d1df6fd8SDavid Lebrun void seg6_local_exit(void) 1108d1df6fd8SDavid Lebrun { 1109d1df6fd8SDavid Lebrun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); 1110d1df6fd8SDavid Lebrun } 1111