1d1df6fd8SDavid Lebrun /* 2d1df6fd8SDavid Lebrun * SR-IPv6 implementation 3d1df6fd8SDavid Lebrun * 4d1df6fd8SDavid Lebrun * Author: 5d1df6fd8SDavid Lebrun * David Lebrun <david.lebrun@uclouvain.be> 6d1df6fd8SDavid Lebrun * 7d1df6fd8SDavid Lebrun * 8d1df6fd8SDavid Lebrun * This program is free software; you can redistribute it and/or 9d1df6fd8SDavid Lebrun * modify it under the terms of the GNU General Public License 10d1df6fd8SDavid Lebrun * as published by the Free Software Foundation; either version 11d1df6fd8SDavid Lebrun * 2 of the License, or (at your option) any later version. 12d1df6fd8SDavid Lebrun */ 13d1df6fd8SDavid Lebrun 14d1df6fd8SDavid Lebrun #include <linux/types.h> 15d1df6fd8SDavid Lebrun #include <linux/skbuff.h> 16d1df6fd8SDavid Lebrun #include <linux/net.h> 17d1df6fd8SDavid Lebrun #include <linux/module.h> 18d1df6fd8SDavid Lebrun #include <net/ip.h> 19d1df6fd8SDavid Lebrun #include <net/lwtunnel.h> 20d1df6fd8SDavid Lebrun #include <net/netevent.h> 21d1df6fd8SDavid Lebrun #include <net/netns/generic.h> 22d1df6fd8SDavid Lebrun #include <net/ip6_fib.h> 23d1df6fd8SDavid Lebrun #include <net/route.h> 24d1df6fd8SDavid Lebrun #include <net/seg6.h> 25d1df6fd8SDavid Lebrun #include <linux/seg6.h> 26d1df6fd8SDavid Lebrun #include <linux/seg6_local.h> 27d1df6fd8SDavid Lebrun #include <net/addrconf.h> 28d1df6fd8SDavid Lebrun #include <net/ip6_route.h> 29d1df6fd8SDavid Lebrun #include <net/dst_cache.h> 30d1df6fd8SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 31d1df6fd8SDavid Lebrun #include <net/seg6_hmac.h> 32d1df6fd8SDavid Lebrun #endif 33d1df6fd8SDavid Lebrun 34d1df6fd8SDavid Lebrun struct seg6_local_lwt; 35d1df6fd8SDavid Lebrun 36d1df6fd8SDavid Lebrun struct seg6_action_desc { 37d1df6fd8SDavid Lebrun int action; 38d1df6fd8SDavid Lebrun unsigned long attrs; 39d1df6fd8SDavid Lebrun int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 40d1df6fd8SDavid Lebrun int static_headroom; 41d1df6fd8SDavid Lebrun }; 42d1df6fd8SDavid Lebrun 43d1df6fd8SDavid Lebrun struct seg6_local_lwt { 44d1df6fd8SDavid Lebrun int action; 45d1df6fd8SDavid Lebrun struct ipv6_sr_hdr *srh; 46d1df6fd8SDavid Lebrun int table; 47d1df6fd8SDavid Lebrun struct in_addr nh4; 48d1df6fd8SDavid Lebrun struct in6_addr nh6; 49d1df6fd8SDavid Lebrun int iif; 50d1df6fd8SDavid Lebrun int oif; 51d1df6fd8SDavid Lebrun 52d1df6fd8SDavid Lebrun int headroom; 53d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 54d1df6fd8SDavid Lebrun }; 55d1df6fd8SDavid Lebrun 56d1df6fd8SDavid Lebrun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) 57d1df6fd8SDavid Lebrun { 58d1df6fd8SDavid Lebrun return (struct seg6_local_lwt *)lwt->data; 59d1df6fd8SDavid Lebrun } 60d1df6fd8SDavid Lebrun 61*140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb) 62*140f04c3SDavid Lebrun { 63*140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 64*140f04c3SDavid Lebrun struct ipv6hdr *hdr; 65*140f04c3SDavid Lebrun int len; 66*140f04c3SDavid Lebrun 67*140f04c3SDavid Lebrun hdr = ipv6_hdr(skb); 68*140f04c3SDavid Lebrun if (hdr->nexthdr != IPPROTO_ROUTING) 69*140f04c3SDavid Lebrun return NULL; 70*140f04c3SDavid Lebrun 71*140f04c3SDavid Lebrun srh = (struct ipv6_sr_hdr *)(hdr + 1); 72*140f04c3SDavid Lebrun len = (srh->hdrlen + 1) << 3; 73*140f04c3SDavid Lebrun 74*140f04c3SDavid Lebrun if (!pskb_may_pull(skb, sizeof(*hdr) + len)) 75*140f04c3SDavid Lebrun return NULL; 76*140f04c3SDavid Lebrun 77*140f04c3SDavid Lebrun if (!seg6_validate_srh(srh, len)) 78*140f04c3SDavid Lebrun return NULL; 79*140f04c3SDavid Lebrun 80*140f04c3SDavid Lebrun return srh; 81*140f04c3SDavid Lebrun } 82*140f04c3SDavid Lebrun 83*140f04c3SDavid Lebrun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) 84*140f04c3SDavid Lebrun { 85*140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 86*140f04c3SDavid Lebrun 87*140f04c3SDavid Lebrun srh = get_srh(skb); 88*140f04c3SDavid Lebrun if (!srh) 89*140f04c3SDavid Lebrun return NULL; 90*140f04c3SDavid Lebrun 91*140f04c3SDavid Lebrun if (srh->segments_left == 0) 92*140f04c3SDavid Lebrun return NULL; 93*140f04c3SDavid Lebrun 94*140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 95*140f04c3SDavid Lebrun if (!seg6_hmac_validate_skb(skb)) 96*140f04c3SDavid Lebrun return NULL; 97*140f04c3SDavid Lebrun #endif 98*140f04c3SDavid Lebrun 99*140f04c3SDavid Lebrun return srh; 100*140f04c3SDavid Lebrun } 101*140f04c3SDavid Lebrun 102*140f04c3SDavid Lebrun /* regular endpoint function */ 103*140f04c3SDavid Lebrun static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) 104*140f04c3SDavid Lebrun { 105*140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 106*140f04c3SDavid Lebrun struct in6_addr *addr; 107*140f04c3SDavid Lebrun 108*140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 109*140f04c3SDavid Lebrun if (!srh) 110*140f04c3SDavid Lebrun goto drop; 111*140f04c3SDavid Lebrun 112*140f04c3SDavid Lebrun srh->segments_left--; 113*140f04c3SDavid Lebrun addr = srh->segments + srh->segments_left; 114*140f04c3SDavid Lebrun 115*140f04c3SDavid Lebrun ipv6_hdr(skb)->daddr = *addr; 116*140f04c3SDavid Lebrun 117*140f04c3SDavid Lebrun skb_dst_drop(skb); 118*140f04c3SDavid Lebrun ip6_route_input(skb); 119*140f04c3SDavid Lebrun 120*140f04c3SDavid Lebrun return dst_input(skb); 121*140f04c3SDavid Lebrun 122*140f04c3SDavid Lebrun drop: 123*140f04c3SDavid Lebrun kfree_skb(skb); 124*140f04c3SDavid Lebrun return -EINVAL; 125*140f04c3SDavid Lebrun } 126*140f04c3SDavid Lebrun 127*140f04c3SDavid Lebrun /* regular endpoint, and forward to specified nexthop */ 128*140f04c3SDavid Lebrun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) 129*140f04c3SDavid Lebrun { 130*140f04c3SDavid Lebrun struct net *net = dev_net(skb->dev); 131*140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 132*140f04c3SDavid Lebrun struct dst_entry *dst; 133*140f04c3SDavid Lebrun struct in6_addr *addr; 134*140f04c3SDavid Lebrun struct ipv6hdr *hdr; 135*140f04c3SDavid Lebrun struct flowi6 fl6; 136*140f04c3SDavid Lebrun int flags; 137*140f04c3SDavid Lebrun 138*140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 139*140f04c3SDavid Lebrun if (!srh) 140*140f04c3SDavid Lebrun goto drop; 141*140f04c3SDavid Lebrun 142*140f04c3SDavid Lebrun srh->segments_left--; 143*140f04c3SDavid Lebrun addr = srh->segments + srh->segments_left; 144*140f04c3SDavid Lebrun 145*140f04c3SDavid Lebrun hdr = ipv6_hdr(skb); 146*140f04c3SDavid Lebrun hdr->daddr = *addr; 147*140f04c3SDavid Lebrun 148*140f04c3SDavid Lebrun skb_dst_drop(skb); 149*140f04c3SDavid Lebrun 150*140f04c3SDavid Lebrun fl6.flowi6_iif = skb->dev->ifindex; 151*140f04c3SDavid Lebrun fl6.daddr = slwt->nh6; 152*140f04c3SDavid Lebrun fl6.saddr = hdr->saddr; 153*140f04c3SDavid Lebrun fl6.flowlabel = ip6_flowinfo(hdr); 154*140f04c3SDavid Lebrun fl6.flowi6_mark = skb->mark; 155*140f04c3SDavid Lebrun fl6.flowi6_proto = hdr->nexthdr; 156*140f04c3SDavid Lebrun 157*140f04c3SDavid Lebrun flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_IFACE | 158*140f04c3SDavid Lebrun RT6_LOOKUP_F_REACHABLE; 159*140f04c3SDavid Lebrun 160*140f04c3SDavid Lebrun dst = ip6_route_input_lookup(net, skb->dev, &fl6, flags); 161*140f04c3SDavid Lebrun if (dst->dev->flags & IFF_LOOPBACK) 162*140f04c3SDavid Lebrun goto drop; 163*140f04c3SDavid Lebrun 164*140f04c3SDavid Lebrun skb_dst_set(skb, dst); 165*140f04c3SDavid Lebrun 166*140f04c3SDavid Lebrun return dst_input(skb); 167*140f04c3SDavid Lebrun 168*140f04c3SDavid Lebrun drop: 169*140f04c3SDavid Lebrun kfree_skb(skb); 170*140f04c3SDavid Lebrun return -EINVAL; 171*140f04c3SDavid Lebrun } 172*140f04c3SDavid Lebrun 173*140f04c3SDavid Lebrun /* decapsulate and forward to specified nexthop */ 174*140f04c3SDavid Lebrun static int input_action_end_dx6(struct sk_buff *skb, 175*140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 176*140f04c3SDavid Lebrun { 177*140f04c3SDavid Lebrun struct net *net = dev_net(skb->dev); 178*140f04c3SDavid Lebrun struct ipv6hdr *inner_hdr; 179*140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 180*140f04c3SDavid Lebrun struct dst_entry *dst; 181*140f04c3SDavid Lebrun unsigned int off = 0; 182*140f04c3SDavid Lebrun struct flowi6 fl6; 183*140f04c3SDavid Lebrun bool use_nh; 184*140f04c3SDavid Lebrun int flags; 185*140f04c3SDavid Lebrun 186*140f04c3SDavid Lebrun /* this function accepts IPv6 encapsulated packets, with either 187*140f04c3SDavid Lebrun * an SRH with SL=0, or no SRH. 188*140f04c3SDavid Lebrun */ 189*140f04c3SDavid Lebrun 190*140f04c3SDavid Lebrun srh = get_srh(skb); 191*140f04c3SDavid Lebrun if (srh && srh->segments_left > 0) 192*140f04c3SDavid Lebrun goto drop; 193*140f04c3SDavid Lebrun 194*140f04c3SDavid Lebrun #ifdef CONFIG_IPV6_SEG6_HMAC 195*140f04c3SDavid Lebrun if (srh && !seg6_hmac_validate_skb(skb)) 196*140f04c3SDavid Lebrun goto drop; 197*140f04c3SDavid Lebrun #endif 198*140f04c3SDavid Lebrun 199*140f04c3SDavid Lebrun if (ipv6_find_hdr(skb, &off, IPPROTO_IPV6, NULL, NULL) < 0) 200*140f04c3SDavid Lebrun goto drop; 201*140f04c3SDavid Lebrun 202*140f04c3SDavid Lebrun if (!pskb_pull(skb, off)) 203*140f04c3SDavid Lebrun goto drop; 204*140f04c3SDavid Lebrun 205*140f04c3SDavid Lebrun skb_postpull_rcsum(skb, skb_network_header(skb), off); 206*140f04c3SDavid Lebrun 207*140f04c3SDavid Lebrun skb_reset_network_header(skb); 208*140f04c3SDavid Lebrun skb_reset_transport_header(skb); 209*140f04c3SDavid Lebrun skb->encapsulation = 0; 210*140f04c3SDavid Lebrun 211*140f04c3SDavid Lebrun inner_hdr = ipv6_hdr(skb); 212*140f04c3SDavid Lebrun 213*140f04c3SDavid Lebrun /* The inner packet is not associated to any local interface, 214*140f04c3SDavid Lebrun * so we do not call netif_rx(). 215*140f04c3SDavid Lebrun * 216*140f04c3SDavid Lebrun * If slwt->nh6 is set to ::, then lookup the nexthop for the 217*140f04c3SDavid Lebrun * inner packet's DA. Otherwise, use the specified nexthop. 218*140f04c3SDavid Lebrun */ 219*140f04c3SDavid Lebrun 220*140f04c3SDavid Lebrun use_nh = !ipv6_addr_any(&slwt->nh6); 221*140f04c3SDavid Lebrun 222*140f04c3SDavid Lebrun skb_dst_drop(skb); 223*140f04c3SDavid Lebrun 224*140f04c3SDavid Lebrun fl6.flowi6_iif = skb->dev->ifindex; 225*140f04c3SDavid Lebrun fl6.daddr = use_nh ? slwt->nh6 : inner_hdr->daddr; 226*140f04c3SDavid Lebrun fl6.saddr = inner_hdr->saddr; 227*140f04c3SDavid Lebrun fl6.flowlabel = ip6_flowinfo(inner_hdr); 228*140f04c3SDavid Lebrun fl6.flowi6_mark = skb->mark; 229*140f04c3SDavid Lebrun fl6.flowi6_proto = inner_hdr->nexthdr; 230*140f04c3SDavid Lebrun 231*140f04c3SDavid Lebrun flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_REACHABLE; 232*140f04c3SDavid Lebrun if (use_nh) 233*140f04c3SDavid Lebrun flags |= RT6_LOOKUP_F_IFACE; 234*140f04c3SDavid Lebrun 235*140f04c3SDavid Lebrun dst = ip6_route_input_lookup(net, skb->dev, &fl6, flags); 236*140f04c3SDavid Lebrun if (dst->dev->flags & IFF_LOOPBACK) 237*140f04c3SDavid Lebrun goto drop; 238*140f04c3SDavid Lebrun 239*140f04c3SDavid Lebrun skb_dst_set(skb, dst); 240*140f04c3SDavid Lebrun 241*140f04c3SDavid Lebrun return dst_input(skb); 242*140f04c3SDavid Lebrun drop: 243*140f04c3SDavid Lebrun kfree_skb(skb); 244*140f04c3SDavid Lebrun return -EINVAL; 245*140f04c3SDavid Lebrun } 246*140f04c3SDavid Lebrun 247*140f04c3SDavid Lebrun /* push an SRH on top of the current one */ 248*140f04c3SDavid Lebrun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 249*140f04c3SDavid Lebrun { 250*140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 251*140f04c3SDavid Lebrun int err = -EINVAL; 252*140f04c3SDavid Lebrun 253*140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 254*140f04c3SDavid Lebrun if (!srh) 255*140f04c3SDavid Lebrun goto drop; 256*140f04c3SDavid Lebrun 257*140f04c3SDavid Lebrun err = seg6_do_srh_inline(skb, slwt->srh); 258*140f04c3SDavid Lebrun if (err) 259*140f04c3SDavid Lebrun goto drop; 260*140f04c3SDavid Lebrun 261*140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 262*140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 263*140f04c3SDavid Lebrun 264*140f04c3SDavid Lebrun skb_dst_drop(skb); 265*140f04c3SDavid Lebrun ip6_route_input(skb); 266*140f04c3SDavid Lebrun 267*140f04c3SDavid Lebrun return dst_input(skb); 268*140f04c3SDavid Lebrun 269*140f04c3SDavid Lebrun drop: 270*140f04c3SDavid Lebrun kfree_skb(skb); 271*140f04c3SDavid Lebrun return err; 272*140f04c3SDavid Lebrun } 273*140f04c3SDavid Lebrun 274*140f04c3SDavid Lebrun /* encapsulate within an outer IPv6 header and a specified SRH */ 275*140f04c3SDavid Lebrun static int input_action_end_b6_encap(struct sk_buff *skb, 276*140f04c3SDavid Lebrun struct seg6_local_lwt *slwt) 277*140f04c3SDavid Lebrun { 278*140f04c3SDavid Lebrun struct ipv6_sr_hdr *srh; 279*140f04c3SDavid Lebrun struct in6_addr *addr; 280*140f04c3SDavid Lebrun int err = -EINVAL; 281*140f04c3SDavid Lebrun 282*140f04c3SDavid Lebrun srh = get_and_validate_srh(skb); 283*140f04c3SDavid Lebrun if (!srh) 284*140f04c3SDavid Lebrun goto drop; 285*140f04c3SDavid Lebrun 286*140f04c3SDavid Lebrun srh->segments_left--; 287*140f04c3SDavid Lebrun addr = srh->segments + srh->segments_left; 288*140f04c3SDavid Lebrun ipv6_hdr(skb)->daddr = *addr; 289*140f04c3SDavid Lebrun 290*140f04c3SDavid Lebrun skb_reset_inner_headers(skb); 291*140f04c3SDavid Lebrun skb->encapsulation = 1; 292*140f04c3SDavid Lebrun 293*140f04c3SDavid Lebrun err = seg6_do_srh_encap(skb, slwt->srh); 294*140f04c3SDavid Lebrun if (err) 295*140f04c3SDavid Lebrun goto drop; 296*140f04c3SDavid Lebrun 297*140f04c3SDavid Lebrun ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 298*140f04c3SDavid Lebrun skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 299*140f04c3SDavid Lebrun 300*140f04c3SDavid Lebrun skb_dst_drop(skb); 301*140f04c3SDavid Lebrun ip6_route_input(skb); 302*140f04c3SDavid Lebrun 303*140f04c3SDavid Lebrun return dst_input(skb); 304*140f04c3SDavid Lebrun 305*140f04c3SDavid Lebrun drop: 306*140f04c3SDavid Lebrun kfree_skb(skb); 307*140f04c3SDavid Lebrun return err; 308*140f04c3SDavid Lebrun } 309*140f04c3SDavid Lebrun 310d1df6fd8SDavid Lebrun static struct seg6_action_desc seg6_action_table[] = { 311d1df6fd8SDavid Lebrun { 312d1df6fd8SDavid Lebrun .action = SEG6_LOCAL_ACTION_END, 313d1df6fd8SDavid Lebrun .attrs = 0, 314*140f04c3SDavid Lebrun .input = input_action_end, 315d1df6fd8SDavid Lebrun }, 316*140f04c3SDavid Lebrun { 317*140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_X, 318*140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH6), 319*140f04c3SDavid Lebrun .input = input_action_end_x, 320*140f04c3SDavid Lebrun }, 321*140f04c3SDavid Lebrun { 322*140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_DX6, 323*140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_NH6), 324*140f04c3SDavid Lebrun .input = input_action_end_dx6, 325*140f04c3SDavid Lebrun }, 326*140f04c3SDavid Lebrun { 327*140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6, 328*140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_SRH), 329*140f04c3SDavid Lebrun .input = input_action_end_b6, 330*140f04c3SDavid Lebrun }, 331*140f04c3SDavid Lebrun { 332*140f04c3SDavid Lebrun .action = SEG6_LOCAL_ACTION_END_B6_ENCAP, 333*140f04c3SDavid Lebrun .attrs = (1 << SEG6_LOCAL_SRH), 334*140f04c3SDavid Lebrun .input = input_action_end_b6_encap, 335*140f04c3SDavid Lebrun .static_headroom = sizeof(struct ipv6hdr), 336*140f04c3SDavid Lebrun } 337d1df6fd8SDavid Lebrun }; 338d1df6fd8SDavid Lebrun 339d1df6fd8SDavid Lebrun static struct seg6_action_desc *__get_action_desc(int action) 340d1df6fd8SDavid Lebrun { 341d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 342d1df6fd8SDavid Lebrun int i, count; 343d1df6fd8SDavid Lebrun 344d1df6fd8SDavid Lebrun count = sizeof(seg6_action_table) / sizeof(struct seg6_action_desc); 345d1df6fd8SDavid Lebrun for (i = 0; i < count; i++) { 346d1df6fd8SDavid Lebrun desc = &seg6_action_table[i]; 347d1df6fd8SDavid Lebrun if (desc->action == action) 348d1df6fd8SDavid Lebrun return desc; 349d1df6fd8SDavid Lebrun } 350d1df6fd8SDavid Lebrun 351d1df6fd8SDavid Lebrun return NULL; 352d1df6fd8SDavid Lebrun } 353d1df6fd8SDavid Lebrun 354d1df6fd8SDavid Lebrun static int seg6_local_input(struct sk_buff *skb) 355d1df6fd8SDavid Lebrun { 356d1df6fd8SDavid Lebrun struct dst_entry *orig_dst = skb_dst(skb); 357d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 358d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 359d1df6fd8SDavid Lebrun 360d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(orig_dst->lwtstate); 361d1df6fd8SDavid Lebrun desc = slwt->desc; 362d1df6fd8SDavid Lebrun 363d1df6fd8SDavid Lebrun return desc->input(skb, slwt); 364d1df6fd8SDavid Lebrun } 365d1df6fd8SDavid Lebrun 366d1df6fd8SDavid Lebrun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { 367d1df6fd8SDavid Lebrun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, 368d1df6fd8SDavid Lebrun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, 369d1df6fd8SDavid Lebrun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, 370d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, 371d1df6fd8SDavid Lebrun .len = sizeof(struct in_addr) }, 372d1df6fd8SDavid Lebrun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, 373d1df6fd8SDavid Lebrun .len = sizeof(struct in6_addr) }, 374d1df6fd8SDavid Lebrun [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, 375d1df6fd8SDavid Lebrun [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, 376d1df6fd8SDavid Lebrun }; 377d1df6fd8SDavid Lebrun 3782d9cc60aSDavid Lebrun static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt) 3792d9cc60aSDavid Lebrun { 3802d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 3812d9cc60aSDavid Lebrun int len; 3822d9cc60aSDavid Lebrun 3832d9cc60aSDavid Lebrun srh = nla_data(attrs[SEG6_LOCAL_SRH]); 3842d9cc60aSDavid Lebrun len = nla_len(attrs[SEG6_LOCAL_SRH]); 3852d9cc60aSDavid Lebrun 3862d9cc60aSDavid Lebrun /* SRH must contain at least one segment */ 3872d9cc60aSDavid Lebrun if (len < sizeof(*srh) + sizeof(struct in6_addr)) 3882d9cc60aSDavid Lebrun return -EINVAL; 3892d9cc60aSDavid Lebrun 3902d9cc60aSDavid Lebrun if (!seg6_validate_srh(srh, len)) 3912d9cc60aSDavid Lebrun return -EINVAL; 3922d9cc60aSDavid Lebrun 3932d9cc60aSDavid Lebrun slwt->srh = kmalloc(len, GFP_KERNEL); 3942d9cc60aSDavid Lebrun if (!slwt->srh) 3952d9cc60aSDavid Lebrun return -ENOMEM; 3962d9cc60aSDavid Lebrun 3972d9cc60aSDavid Lebrun memcpy(slwt->srh, srh, len); 3982d9cc60aSDavid Lebrun 3992d9cc60aSDavid Lebrun slwt->headroom += len; 4002d9cc60aSDavid Lebrun 4012d9cc60aSDavid Lebrun return 0; 4022d9cc60aSDavid Lebrun } 4032d9cc60aSDavid Lebrun 4042d9cc60aSDavid Lebrun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt) 4052d9cc60aSDavid Lebrun { 4062d9cc60aSDavid Lebrun struct ipv6_sr_hdr *srh; 4072d9cc60aSDavid Lebrun struct nlattr *nla; 4082d9cc60aSDavid Lebrun int len; 4092d9cc60aSDavid Lebrun 4102d9cc60aSDavid Lebrun srh = slwt->srh; 4112d9cc60aSDavid Lebrun len = (srh->hdrlen + 1) << 3; 4122d9cc60aSDavid Lebrun 4132d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_SRH, len); 4142d9cc60aSDavid Lebrun if (!nla) 4152d9cc60aSDavid Lebrun return -EMSGSIZE; 4162d9cc60aSDavid Lebrun 4172d9cc60aSDavid Lebrun memcpy(nla_data(nla), srh, len); 4182d9cc60aSDavid Lebrun 4192d9cc60aSDavid Lebrun return 0; 4202d9cc60aSDavid Lebrun } 4212d9cc60aSDavid Lebrun 4222d9cc60aSDavid Lebrun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 4232d9cc60aSDavid Lebrun { 4242d9cc60aSDavid Lebrun int len = (a->srh->hdrlen + 1) << 3; 4252d9cc60aSDavid Lebrun 4262d9cc60aSDavid Lebrun if (len != ((b->srh->hdrlen + 1) << 3)) 4272d9cc60aSDavid Lebrun return 1; 4282d9cc60aSDavid Lebrun 4292d9cc60aSDavid Lebrun return memcmp(a->srh, b->srh, len); 4302d9cc60aSDavid Lebrun } 4312d9cc60aSDavid Lebrun 4322d9cc60aSDavid Lebrun static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt) 4332d9cc60aSDavid Lebrun { 4342d9cc60aSDavid Lebrun slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]); 4352d9cc60aSDavid Lebrun 4362d9cc60aSDavid Lebrun return 0; 4372d9cc60aSDavid Lebrun } 4382d9cc60aSDavid Lebrun 4392d9cc60aSDavid Lebrun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt) 4402d9cc60aSDavid Lebrun { 4412d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table)) 4422d9cc60aSDavid Lebrun return -EMSGSIZE; 4432d9cc60aSDavid Lebrun 4442d9cc60aSDavid Lebrun return 0; 4452d9cc60aSDavid Lebrun } 4462d9cc60aSDavid Lebrun 4472d9cc60aSDavid Lebrun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 4482d9cc60aSDavid Lebrun { 4492d9cc60aSDavid Lebrun if (a->table != b->table) 4502d9cc60aSDavid Lebrun return 1; 4512d9cc60aSDavid Lebrun 4522d9cc60aSDavid Lebrun return 0; 4532d9cc60aSDavid Lebrun } 4542d9cc60aSDavid Lebrun 4552d9cc60aSDavid Lebrun static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt) 4562d9cc60aSDavid Lebrun { 4572d9cc60aSDavid Lebrun memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]), 4582d9cc60aSDavid Lebrun sizeof(struct in_addr)); 4592d9cc60aSDavid Lebrun 4602d9cc60aSDavid Lebrun return 0; 4612d9cc60aSDavid Lebrun } 4622d9cc60aSDavid Lebrun 4632d9cc60aSDavid Lebrun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt) 4642d9cc60aSDavid Lebrun { 4652d9cc60aSDavid Lebrun struct nlattr *nla; 4662d9cc60aSDavid Lebrun 4672d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr)); 4682d9cc60aSDavid Lebrun if (!nla) 4692d9cc60aSDavid Lebrun return -EMSGSIZE; 4702d9cc60aSDavid Lebrun 4712d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr)); 4722d9cc60aSDavid Lebrun 4732d9cc60aSDavid Lebrun return 0; 4742d9cc60aSDavid Lebrun } 4752d9cc60aSDavid Lebrun 4762d9cc60aSDavid Lebrun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 4772d9cc60aSDavid Lebrun { 4782d9cc60aSDavid Lebrun return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr)); 4792d9cc60aSDavid Lebrun } 4802d9cc60aSDavid Lebrun 4812d9cc60aSDavid Lebrun static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt) 4822d9cc60aSDavid Lebrun { 4832d9cc60aSDavid Lebrun memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]), 4842d9cc60aSDavid Lebrun sizeof(struct in6_addr)); 4852d9cc60aSDavid Lebrun 4862d9cc60aSDavid Lebrun return 0; 4872d9cc60aSDavid Lebrun } 4882d9cc60aSDavid Lebrun 4892d9cc60aSDavid Lebrun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt) 4902d9cc60aSDavid Lebrun { 4912d9cc60aSDavid Lebrun struct nlattr *nla; 4922d9cc60aSDavid Lebrun 4932d9cc60aSDavid Lebrun nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr)); 4942d9cc60aSDavid Lebrun if (!nla) 4952d9cc60aSDavid Lebrun return -EMSGSIZE; 4962d9cc60aSDavid Lebrun 4972d9cc60aSDavid Lebrun memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr)); 4982d9cc60aSDavid Lebrun 4992d9cc60aSDavid Lebrun return 0; 5002d9cc60aSDavid Lebrun } 5012d9cc60aSDavid Lebrun 5022d9cc60aSDavid Lebrun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 5032d9cc60aSDavid Lebrun { 5042d9cc60aSDavid Lebrun return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr)); 5052d9cc60aSDavid Lebrun } 5062d9cc60aSDavid Lebrun 5072d9cc60aSDavid Lebrun static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 5082d9cc60aSDavid Lebrun { 5092d9cc60aSDavid Lebrun slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]); 5102d9cc60aSDavid Lebrun 5112d9cc60aSDavid Lebrun return 0; 5122d9cc60aSDavid Lebrun } 5132d9cc60aSDavid Lebrun 5142d9cc60aSDavid Lebrun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 5152d9cc60aSDavid Lebrun { 5162d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif)) 5172d9cc60aSDavid Lebrun return -EMSGSIZE; 5182d9cc60aSDavid Lebrun 5192d9cc60aSDavid Lebrun return 0; 5202d9cc60aSDavid Lebrun } 5212d9cc60aSDavid Lebrun 5222d9cc60aSDavid Lebrun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 5232d9cc60aSDavid Lebrun { 5242d9cc60aSDavid Lebrun if (a->iif != b->iif) 5252d9cc60aSDavid Lebrun return 1; 5262d9cc60aSDavid Lebrun 5272d9cc60aSDavid Lebrun return 0; 5282d9cc60aSDavid Lebrun } 5292d9cc60aSDavid Lebrun 5302d9cc60aSDavid Lebrun static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt) 5312d9cc60aSDavid Lebrun { 5322d9cc60aSDavid Lebrun slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]); 5332d9cc60aSDavid Lebrun 5342d9cc60aSDavid Lebrun return 0; 5352d9cc60aSDavid Lebrun } 5362d9cc60aSDavid Lebrun 5372d9cc60aSDavid Lebrun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt) 5382d9cc60aSDavid Lebrun { 5392d9cc60aSDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif)) 5402d9cc60aSDavid Lebrun return -EMSGSIZE; 5412d9cc60aSDavid Lebrun 5422d9cc60aSDavid Lebrun return 0; 5432d9cc60aSDavid Lebrun } 5442d9cc60aSDavid Lebrun 5452d9cc60aSDavid Lebrun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) 5462d9cc60aSDavid Lebrun { 5472d9cc60aSDavid Lebrun if (a->oif != b->oif) 5482d9cc60aSDavid Lebrun return 1; 5492d9cc60aSDavid Lebrun 5502d9cc60aSDavid Lebrun return 0; 5512d9cc60aSDavid Lebrun } 5522d9cc60aSDavid Lebrun 553d1df6fd8SDavid Lebrun struct seg6_action_param { 554d1df6fd8SDavid Lebrun int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt); 555d1df6fd8SDavid Lebrun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); 556d1df6fd8SDavid Lebrun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); 557d1df6fd8SDavid Lebrun }; 558d1df6fd8SDavid Lebrun 559d1df6fd8SDavid Lebrun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { 5602d9cc60aSDavid Lebrun [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh, 5612d9cc60aSDavid Lebrun .put = put_nla_srh, 5622d9cc60aSDavid Lebrun .cmp = cmp_nla_srh }, 563d1df6fd8SDavid Lebrun 5642d9cc60aSDavid Lebrun [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table, 5652d9cc60aSDavid Lebrun .put = put_nla_table, 5662d9cc60aSDavid Lebrun .cmp = cmp_nla_table }, 567d1df6fd8SDavid Lebrun 5682d9cc60aSDavid Lebrun [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4, 5692d9cc60aSDavid Lebrun .put = put_nla_nh4, 5702d9cc60aSDavid Lebrun .cmp = cmp_nla_nh4 }, 571d1df6fd8SDavid Lebrun 5722d9cc60aSDavid Lebrun [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6, 5732d9cc60aSDavid Lebrun .put = put_nla_nh6, 5742d9cc60aSDavid Lebrun .cmp = cmp_nla_nh6 }, 575d1df6fd8SDavid Lebrun 5762d9cc60aSDavid Lebrun [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif, 5772d9cc60aSDavid Lebrun .put = put_nla_iif, 5782d9cc60aSDavid Lebrun .cmp = cmp_nla_iif }, 579d1df6fd8SDavid Lebrun 5802d9cc60aSDavid Lebrun [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif, 5812d9cc60aSDavid Lebrun .put = put_nla_oif, 5822d9cc60aSDavid Lebrun .cmp = cmp_nla_oif }, 583d1df6fd8SDavid Lebrun }; 584d1df6fd8SDavid Lebrun 585d1df6fd8SDavid Lebrun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) 586d1df6fd8SDavid Lebrun { 587d1df6fd8SDavid Lebrun struct seg6_action_param *param; 588d1df6fd8SDavid Lebrun struct seg6_action_desc *desc; 589d1df6fd8SDavid Lebrun int i, err; 590d1df6fd8SDavid Lebrun 591d1df6fd8SDavid Lebrun desc = __get_action_desc(slwt->action); 592d1df6fd8SDavid Lebrun if (!desc) 593d1df6fd8SDavid Lebrun return -EINVAL; 594d1df6fd8SDavid Lebrun 595d1df6fd8SDavid Lebrun if (!desc->input) 596d1df6fd8SDavid Lebrun return -EOPNOTSUPP; 597d1df6fd8SDavid Lebrun 598d1df6fd8SDavid Lebrun slwt->desc = desc; 599d1df6fd8SDavid Lebrun slwt->headroom += desc->static_headroom; 600d1df6fd8SDavid Lebrun 601d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 602d1df6fd8SDavid Lebrun if (desc->attrs & (1 << i)) { 603d1df6fd8SDavid Lebrun if (!attrs[i]) 604d1df6fd8SDavid Lebrun return -EINVAL; 605d1df6fd8SDavid Lebrun 606d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 607d1df6fd8SDavid Lebrun 608d1df6fd8SDavid Lebrun err = param->parse(attrs, slwt); 609d1df6fd8SDavid Lebrun if (err < 0) 610d1df6fd8SDavid Lebrun return err; 611d1df6fd8SDavid Lebrun } 612d1df6fd8SDavid Lebrun } 613d1df6fd8SDavid Lebrun 614d1df6fd8SDavid Lebrun return 0; 615d1df6fd8SDavid Lebrun } 616d1df6fd8SDavid Lebrun 617d1df6fd8SDavid Lebrun static int seg6_local_build_state(struct nlattr *nla, unsigned int family, 618d1df6fd8SDavid Lebrun const void *cfg, struct lwtunnel_state **ts, 619d1df6fd8SDavid Lebrun struct netlink_ext_ack *extack) 620d1df6fd8SDavid Lebrun { 621d1df6fd8SDavid Lebrun struct nlattr *tb[SEG6_LOCAL_MAX + 1]; 622d1df6fd8SDavid Lebrun struct lwtunnel_state *newts; 623d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt; 624d1df6fd8SDavid Lebrun int err; 625d1df6fd8SDavid Lebrun 626d1df6fd8SDavid Lebrun err = nla_parse_nested(tb, SEG6_LOCAL_MAX, nla, seg6_local_policy, 627d1df6fd8SDavid Lebrun extack); 628d1df6fd8SDavid Lebrun 629d1df6fd8SDavid Lebrun if (err < 0) 630d1df6fd8SDavid Lebrun return err; 631d1df6fd8SDavid Lebrun 632d1df6fd8SDavid Lebrun if (!tb[SEG6_LOCAL_ACTION]) 633d1df6fd8SDavid Lebrun return -EINVAL; 634d1df6fd8SDavid Lebrun 635d1df6fd8SDavid Lebrun newts = lwtunnel_state_alloc(sizeof(*slwt)); 636d1df6fd8SDavid Lebrun if (!newts) 637d1df6fd8SDavid Lebrun return -ENOMEM; 638d1df6fd8SDavid Lebrun 639d1df6fd8SDavid Lebrun slwt = seg6_local_lwtunnel(newts); 640d1df6fd8SDavid Lebrun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); 641d1df6fd8SDavid Lebrun 642d1df6fd8SDavid Lebrun err = parse_nla_action(tb, slwt); 643d1df6fd8SDavid Lebrun if (err < 0) 644d1df6fd8SDavid Lebrun goto out_free; 645d1df6fd8SDavid Lebrun 646d1df6fd8SDavid Lebrun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; 647d1df6fd8SDavid Lebrun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; 648d1df6fd8SDavid Lebrun newts->headroom = slwt->headroom; 649d1df6fd8SDavid Lebrun 650d1df6fd8SDavid Lebrun *ts = newts; 651d1df6fd8SDavid Lebrun 652d1df6fd8SDavid Lebrun return 0; 653d1df6fd8SDavid Lebrun 654d1df6fd8SDavid Lebrun out_free: 655d1df6fd8SDavid Lebrun kfree(slwt->srh); 656d1df6fd8SDavid Lebrun kfree(newts); 657d1df6fd8SDavid Lebrun return err; 658d1df6fd8SDavid Lebrun } 659d1df6fd8SDavid Lebrun 660d1df6fd8SDavid Lebrun static void seg6_local_destroy_state(struct lwtunnel_state *lwt) 661d1df6fd8SDavid Lebrun { 662d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 663d1df6fd8SDavid Lebrun 664d1df6fd8SDavid Lebrun kfree(slwt->srh); 665d1df6fd8SDavid Lebrun } 666d1df6fd8SDavid Lebrun 667d1df6fd8SDavid Lebrun static int seg6_local_fill_encap(struct sk_buff *skb, 668d1df6fd8SDavid Lebrun struct lwtunnel_state *lwt) 669d1df6fd8SDavid Lebrun { 670d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 671d1df6fd8SDavid Lebrun struct seg6_action_param *param; 672d1df6fd8SDavid Lebrun int i, err; 673d1df6fd8SDavid Lebrun 674d1df6fd8SDavid Lebrun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) 675d1df6fd8SDavid Lebrun return -EMSGSIZE; 676d1df6fd8SDavid Lebrun 677d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 678d1df6fd8SDavid Lebrun if (slwt->desc->attrs & (1 << i)) { 679d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 680d1df6fd8SDavid Lebrun err = param->put(skb, slwt); 681d1df6fd8SDavid Lebrun if (err < 0) 682d1df6fd8SDavid Lebrun return err; 683d1df6fd8SDavid Lebrun } 684d1df6fd8SDavid Lebrun } 685d1df6fd8SDavid Lebrun 686d1df6fd8SDavid Lebrun return 0; 687d1df6fd8SDavid Lebrun } 688d1df6fd8SDavid Lebrun 689d1df6fd8SDavid Lebrun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) 690d1df6fd8SDavid Lebrun { 691d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); 692d1df6fd8SDavid Lebrun unsigned long attrs; 693d1df6fd8SDavid Lebrun int nlsize; 694d1df6fd8SDavid Lebrun 695d1df6fd8SDavid Lebrun nlsize = nla_total_size(4); /* action */ 696d1df6fd8SDavid Lebrun 697d1df6fd8SDavid Lebrun attrs = slwt->desc->attrs; 698d1df6fd8SDavid Lebrun 699d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_SRH)) 700d1df6fd8SDavid Lebrun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); 701d1df6fd8SDavid Lebrun 702d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_TABLE)) 703d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 704d1df6fd8SDavid Lebrun 705d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_NH4)) 706d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 707d1df6fd8SDavid Lebrun 708d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_NH6)) 709d1df6fd8SDavid Lebrun nlsize += nla_total_size(16); 710d1df6fd8SDavid Lebrun 711d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_IIF)) 712d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 713d1df6fd8SDavid Lebrun 714d1df6fd8SDavid Lebrun if (attrs & (1 << SEG6_LOCAL_OIF)) 715d1df6fd8SDavid Lebrun nlsize += nla_total_size(4); 716d1df6fd8SDavid Lebrun 717d1df6fd8SDavid Lebrun return nlsize; 718d1df6fd8SDavid Lebrun } 719d1df6fd8SDavid Lebrun 720d1df6fd8SDavid Lebrun static int seg6_local_cmp_encap(struct lwtunnel_state *a, 721d1df6fd8SDavid Lebrun struct lwtunnel_state *b) 722d1df6fd8SDavid Lebrun { 723d1df6fd8SDavid Lebrun struct seg6_local_lwt *slwt_a, *slwt_b; 724d1df6fd8SDavid Lebrun struct seg6_action_param *param; 725d1df6fd8SDavid Lebrun int i; 726d1df6fd8SDavid Lebrun 727d1df6fd8SDavid Lebrun slwt_a = seg6_local_lwtunnel(a); 728d1df6fd8SDavid Lebrun slwt_b = seg6_local_lwtunnel(b); 729d1df6fd8SDavid Lebrun 730d1df6fd8SDavid Lebrun if (slwt_a->action != slwt_b->action) 731d1df6fd8SDavid Lebrun return 1; 732d1df6fd8SDavid Lebrun 733d1df6fd8SDavid Lebrun if (slwt_a->desc->attrs != slwt_b->desc->attrs) 734d1df6fd8SDavid Lebrun return 1; 735d1df6fd8SDavid Lebrun 736d1df6fd8SDavid Lebrun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { 737d1df6fd8SDavid Lebrun if (slwt_a->desc->attrs & (1 << i)) { 738d1df6fd8SDavid Lebrun param = &seg6_action_params[i]; 739d1df6fd8SDavid Lebrun if (param->cmp(slwt_a, slwt_b)) 740d1df6fd8SDavid Lebrun return 1; 741d1df6fd8SDavid Lebrun } 742d1df6fd8SDavid Lebrun } 743d1df6fd8SDavid Lebrun 744d1df6fd8SDavid Lebrun return 0; 745d1df6fd8SDavid Lebrun } 746d1df6fd8SDavid Lebrun 747d1df6fd8SDavid Lebrun static const struct lwtunnel_encap_ops seg6_local_ops = { 748d1df6fd8SDavid Lebrun .build_state = seg6_local_build_state, 749d1df6fd8SDavid Lebrun .destroy_state = seg6_local_destroy_state, 750d1df6fd8SDavid Lebrun .input = seg6_local_input, 751d1df6fd8SDavid Lebrun .fill_encap = seg6_local_fill_encap, 752d1df6fd8SDavid Lebrun .get_encap_size = seg6_local_get_encap_size, 753d1df6fd8SDavid Lebrun .cmp_encap = seg6_local_cmp_encap, 754d1df6fd8SDavid Lebrun .owner = THIS_MODULE, 755d1df6fd8SDavid Lebrun }; 756d1df6fd8SDavid Lebrun 757d1df6fd8SDavid Lebrun int __init seg6_local_init(void) 758d1df6fd8SDavid Lebrun { 759d1df6fd8SDavid Lebrun return lwtunnel_encap_add_ops(&seg6_local_ops, 760d1df6fd8SDavid Lebrun LWTUNNEL_ENCAP_SEG6_LOCAL); 761d1df6fd8SDavid Lebrun } 762d1df6fd8SDavid Lebrun 763d1df6fd8SDavid Lebrun void seg6_local_exit(void) 764d1df6fd8SDavid Lebrun { 765d1df6fd8SDavid Lebrun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); 766d1df6fd8SDavid Lebrun } 767