1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * SR-IPv6 implementation
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Authors:
6*4882a593Smuzhiyun * David Lebrun <david.lebrun@uclouvain.be>
7*4882a593Smuzhiyun * eBPF support: Mathieu Xhonneux <m.xhonneux@gmail.com>
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/types.h>
11*4882a593Smuzhiyun #include <linux/skbuff.h>
12*4882a593Smuzhiyun #include <linux/net.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <net/ip.h>
15*4882a593Smuzhiyun #include <net/lwtunnel.h>
16*4882a593Smuzhiyun #include <net/netevent.h>
17*4882a593Smuzhiyun #include <net/netns/generic.h>
18*4882a593Smuzhiyun #include <net/ip6_fib.h>
19*4882a593Smuzhiyun #include <net/route.h>
20*4882a593Smuzhiyun #include <net/seg6.h>
21*4882a593Smuzhiyun #include <linux/seg6.h>
22*4882a593Smuzhiyun #include <linux/seg6_local.h>
23*4882a593Smuzhiyun #include <net/addrconf.h>
24*4882a593Smuzhiyun #include <net/ip6_route.h>
25*4882a593Smuzhiyun #include <net/dst_cache.h>
26*4882a593Smuzhiyun #include <net/ip_tunnels.h>
27*4882a593Smuzhiyun #ifdef CONFIG_IPV6_SEG6_HMAC
28*4882a593Smuzhiyun #include <net/seg6_hmac.h>
29*4882a593Smuzhiyun #endif
30*4882a593Smuzhiyun #include <net/seg6_local.h>
31*4882a593Smuzhiyun #include <linux/etherdevice.h>
32*4882a593Smuzhiyun #include <linux/bpf.h>
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun struct seg6_local_lwt;
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun struct seg6_action_desc {
37*4882a593Smuzhiyun int action;
38*4882a593Smuzhiyun unsigned long attrs;
39*4882a593Smuzhiyun int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
40*4882a593Smuzhiyun int static_headroom;
41*4882a593Smuzhiyun };
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun struct bpf_lwt_prog {
44*4882a593Smuzhiyun struct bpf_prog *prog;
45*4882a593Smuzhiyun char *name;
46*4882a593Smuzhiyun };
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun struct seg6_local_lwt {
49*4882a593Smuzhiyun int action;
50*4882a593Smuzhiyun struct ipv6_sr_hdr *srh;
51*4882a593Smuzhiyun int table;
52*4882a593Smuzhiyun struct in_addr nh4;
53*4882a593Smuzhiyun struct in6_addr nh6;
54*4882a593Smuzhiyun int iif;
55*4882a593Smuzhiyun int oif;
56*4882a593Smuzhiyun struct bpf_lwt_prog bpf;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun int headroom;
59*4882a593Smuzhiyun struct seg6_action_desc *desc;
60*4882a593Smuzhiyun };
61*4882a593Smuzhiyun
seg6_local_lwtunnel(struct lwtunnel_state * lwt)62*4882a593Smuzhiyun static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun return (struct seg6_local_lwt *)lwt->data;
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
get_srh(struct sk_buff * skb)67*4882a593Smuzhiyun static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun struct ipv6_sr_hdr *srh;
70*4882a593Smuzhiyun int len, srhoff = 0;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
73*4882a593Smuzhiyun return NULL;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun if (!pskb_may_pull(skb, srhoff + sizeof(*srh)))
76*4882a593Smuzhiyun return NULL;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun len = (srh->hdrlen + 1) << 3;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun if (!pskb_may_pull(skb, srhoff + len))
83*4882a593Smuzhiyun return NULL;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun /* note that pskb_may_pull may change pointers in header;
86*4882a593Smuzhiyun * for this reason it is necessary to reload them when needed.
87*4882a593Smuzhiyun */
88*4882a593Smuzhiyun srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun if (!seg6_validate_srh(srh, len, true))
91*4882a593Smuzhiyun return NULL;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun return srh;
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun
get_and_validate_srh(struct sk_buff * skb)96*4882a593Smuzhiyun static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun struct ipv6_sr_hdr *srh;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun srh = get_srh(skb);
101*4882a593Smuzhiyun if (!srh)
102*4882a593Smuzhiyun return NULL;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun if (srh->segments_left == 0)
105*4882a593Smuzhiyun return NULL;
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun #ifdef CONFIG_IPV6_SEG6_HMAC
108*4882a593Smuzhiyun if (!seg6_hmac_validate_skb(skb))
109*4882a593Smuzhiyun return NULL;
110*4882a593Smuzhiyun #endif
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun return srh;
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
decap_and_validate(struct sk_buff * skb,int proto)115*4882a593Smuzhiyun static bool decap_and_validate(struct sk_buff *skb, int proto)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun struct ipv6_sr_hdr *srh;
118*4882a593Smuzhiyun unsigned int off = 0;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun srh = get_srh(skb);
121*4882a593Smuzhiyun if (srh && srh->segments_left > 0)
122*4882a593Smuzhiyun return false;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun #ifdef CONFIG_IPV6_SEG6_HMAC
125*4882a593Smuzhiyun if (srh && !seg6_hmac_validate_skb(skb))
126*4882a593Smuzhiyun return false;
127*4882a593Smuzhiyun #endif
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0)
130*4882a593Smuzhiyun return false;
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun if (!pskb_pull(skb, off))
133*4882a593Smuzhiyun return false;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun skb_postpull_rcsum(skb, skb_network_header(skb), off);
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun skb_reset_network_header(skb);
138*4882a593Smuzhiyun skb_reset_transport_header(skb);
139*4882a593Smuzhiyun if (iptunnel_pull_offloads(skb))
140*4882a593Smuzhiyun return false;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun return true;
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
advance_nextseg(struct ipv6_sr_hdr * srh,struct in6_addr * daddr)145*4882a593Smuzhiyun static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun struct in6_addr *addr;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun srh->segments_left--;
150*4882a593Smuzhiyun addr = srh->segments + srh->segments_left;
151*4882a593Smuzhiyun *daddr = *addr;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun static int
seg6_lookup_any_nexthop(struct sk_buff * skb,struct in6_addr * nhaddr,u32 tbl_id,bool local_delivery)155*4882a593Smuzhiyun seg6_lookup_any_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr,
156*4882a593Smuzhiyun u32 tbl_id, bool local_delivery)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun struct net *net = dev_net(skb->dev);
159*4882a593Smuzhiyun struct ipv6hdr *hdr = ipv6_hdr(skb);
160*4882a593Smuzhiyun int flags = RT6_LOOKUP_F_HAS_SADDR;
161*4882a593Smuzhiyun struct dst_entry *dst = NULL;
162*4882a593Smuzhiyun struct rt6_info *rt;
163*4882a593Smuzhiyun struct flowi6 fl6;
164*4882a593Smuzhiyun int dev_flags = 0;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun fl6.flowi6_iif = skb->dev->ifindex;
167*4882a593Smuzhiyun fl6.daddr = nhaddr ? *nhaddr : hdr->daddr;
168*4882a593Smuzhiyun fl6.saddr = hdr->saddr;
169*4882a593Smuzhiyun fl6.flowlabel = ip6_flowinfo(hdr);
170*4882a593Smuzhiyun fl6.flowi6_mark = skb->mark;
171*4882a593Smuzhiyun fl6.flowi6_proto = hdr->nexthdr;
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun if (nhaddr)
174*4882a593Smuzhiyun fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun if (!tbl_id) {
177*4882a593Smuzhiyun dst = ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags);
178*4882a593Smuzhiyun } else {
179*4882a593Smuzhiyun struct fib6_table *table;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun table = fib6_get_table(net, tbl_id);
182*4882a593Smuzhiyun if (!table)
183*4882a593Smuzhiyun goto out;
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun rt = ip6_pol_route(net, table, 0, &fl6, skb, flags);
186*4882a593Smuzhiyun dst = &rt->dst;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun /* we want to discard traffic destined for local packet processing,
190*4882a593Smuzhiyun * if @local_delivery is set to false.
191*4882a593Smuzhiyun */
192*4882a593Smuzhiyun if (!local_delivery)
193*4882a593Smuzhiyun dev_flags |= IFF_LOOPBACK;
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun if (dst && (dst->dev->flags & dev_flags) && !dst->error) {
196*4882a593Smuzhiyun dst_release(dst);
197*4882a593Smuzhiyun dst = NULL;
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun out:
201*4882a593Smuzhiyun if (!dst) {
202*4882a593Smuzhiyun rt = net->ipv6.ip6_blk_hole_entry;
203*4882a593Smuzhiyun dst = &rt->dst;
204*4882a593Smuzhiyun dst_hold(dst);
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun skb_dst_drop(skb);
208*4882a593Smuzhiyun skb_dst_set(skb, dst);
209*4882a593Smuzhiyun return dst->error;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
seg6_lookup_nexthop(struct sk_buff * skb,struct in6_addr * nhaddr,u32 tbl_id)212*4882a593Smuzhiyun int seg6_lookup_nexthop(struct sk_buff *skb,
213*4882a593Smuzhiyun struct in6_addr *nhaddr, u32 tbl_id)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun return seg6_lookup_any_nexthop(skb, nhaddr, tbl_id, false);
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun /* regular endpoint function */
input_action_end(struct sk_buff * skb,struct seg6_local_lwt * slwt)219*4882a593Smuzhiyun static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun struct ipv6_sr_hdr *srh;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun srh = get_and_validate_srh(skb);
224*4882a593Smuzhiyun if (!srh)
225*4882a593Smuzhiyun goto drop;
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun seg6_lookup_nexthop(skb, NULL, 0);
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun return dst_input(skb);
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun drop:
234*4882a593Smuzhiyun kfree_skb(skb);
235*4882a593Smuzhiyun return -EINVAL;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun /* regular endpoint, and forward to specified nexthop */
input_action_end_x(struct sk_buff * skb,struct seg6_local_lwt * slwt)239*4882a593Smuzhiyun static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt)
240*4882a593Smuzhiyun {
241*4882a593Smuzhiyun struct ipv6_sr_hdr *srh;
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun srh = get_and_validate_srh(skb);
244*4882a593Smuzhiyun if (!srh)
245*4882a593Smuzhiyun goto drop;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun seg6_lookup_nexthop(skb, &slwt->nh6, 0);
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun return dst_input(skb);
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun drop:
254*4882a593Smuzhiyun kfree_skb(skb);
255*4882a593Smuzhiyun return -EINVAL;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun
input_action_end_t(struct sk_buff * skb,struct seg6_local_lwt * slwt)258*4882a593Smuzhiyun static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt)
259*4882a593Smuzhiyun {
260*4882a593Smuzhiyun struct ipv6_sr_hdr *srh;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun srh = get_and_validate_srh(skb);
263*4882a593Smuzhiyun if (!srh)
264*4882a593Smuzhiyun goto drop;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun seg6_lookup_nexthop(skb, NULL, slwt->table);
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun return dst_input(skb);
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun drop:
273*4882a593Smuzhiyun kfree_skb(skb);
274*4882a593Smuzhiyun return -EINVAL;
275*4882a593Smuzhiyun }
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun /* decapsulate and forward inner L2 frame on specified interface */
input_action_end_dx2(struct sk_buff * skb,struct seg6_local_lwt * slwt)278*4882a593Smuzhiyun static int input_action_end_dx2(struct sk_buff *skb,
279*4882a593Smuzhiyun struct seg6_local_lwt *slwt)
280*4882a593Smuzhiyun {
281*4882a593Smuzhiyun struct net *net = dev_net(skb->dev);
282*4882a593Smuzhiyun struct net_device *odev;
283*4882a593Smuzhiyun struct ethhdr *eth;
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun if (!decap_and_validate(skb, IPPROTO_ETHERNET))
286*4882a593Smuzhiyun goto drop;
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun if (!pskb_may_pull(skb, ETH_HLEN))
289*4882a593Smuzhiyun goto drop;
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun skb_reset_mac_header(skb);
292*4882a593Smuzhiyun eth = (struct ethhdr *)skb->data;
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun /* To determine the frame's protocol, we assume it is 802.3. This avoids
295*4882a593Smuzhiyun * a call to eth_type_trans(), which is not really relevant for our
296*4882a593Smuzhiyun * use case.
297*4882a593Smuzhiyun */
298*4882a593Smuzhiyun if (!eth_proto_is_802_3(eth->h_proto))
299*4882a593Smuzhiyun goto drop;
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun odev = dev_get_by_index_rcu(net, slwt->oif);
302*4882a593Smuzhiyun if (!odev)
303*4882a593Smuzhiyun goto drop;
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun /* As we accept Ethernet frames, make sure the egress device is of
306*4882a593Smuzhiyun * the correct type.
307*4882a593Smuzhiyun */
308*4882a593Smuzhiyun if (odev->type != ARPHRD_ETHER)
309*4882a593Smuzhiyun goto drop;
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev))
312*4882a593Smuzhiyun goto drop;
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun skb_orphan(skb);
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun if (skb_warn_if_lro(skb))
317*4882a593Smuzhiyun goto drop;
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun skb_forward_csum(skb);
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun if (skb->len - ETH_HLEN > odev->mtu)
322*4882a593Smuzhiyun goto drop;
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun skb->dev = odev;
325*4882a593Smuzhiyun skb->protocol = eth->h_proto;
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun return dev_queue_xmit(skb);
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun drop:
330*4882a593Smuzhiyun kfree_skb(skb);
331*4882a593Smuzhiyun return -EINVAL;
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun /* decapsulate and forward to specified nexthop */
input_action_end_dx6(struct sk_buff * skb,struct seg6_local_lwt * slwt)335*4882a593Smuzhiyun static int input_action_end_dx6(struct sk_buff *skb,
336*4882a593Smuzhiyun struct seg6_local_lwt *slwt)
337*4882a593Smuzhiyun {
338*4882a593Smuzhiyun struct in6_addr *nhaddr = NULL;
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun /* this function accepts IPv6 encapsulated packets, with either
341*4882a593Smuzhiyun * an SRH with SL=0, or no SRH.
342*4882a593Smuzhiyun */
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun if (!decap_and_validate(skb, IPPROTO_IPV6))
345*4882a593Smuzhiyun goto drop;
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
348*4882a593Smuzhiyun goto drop;
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun /* The inner packet is not associated to any local interface,
351*4882a593Smuzhiyun * so we do not call netif_rx().
352*4882a593Smuzhiyun *
353*4882a593Smuzhiyun * If slwt->nh6 is set to ::, then lookup the nexthop for the
354*4882a593Smuzhiyun * inner packet's DA. Otherwise, use the specified nexthop.
355*4882a593Smuzhiyun */
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun if (!ipv6_addr_any(&slwt->nh6))
358*4882a593Smuzhiyun nhaddr = &slwt->nh6;
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun skb_set_transport_header(skb, sizeof(struct ipv6hdr));
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun seg6_lookup_nexthop(skb, nhaddr, 0);
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun return dst_input(skb);
365*4882a593Smuzhiyun drop:
366*4882a593Smuzhiyun kfree_skb(skb);
367*4882a593Smuzhiyun return -EINVAL;
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun
input_action_end_dx4(struct sk_buff * skb,struct seg6_local_lwt * slwt)370*4882a593Smuzhiyun static int input_action_end_dx4(struct sk_buff *skb,
371*4882a593Smuzhiyun struct seg6_local_lwt *slwt)
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun struct iphdr *iph;
374*4882a593Smuzhiyun __be32 nhaddr;
375*4882a593Smuzhiyun int err;
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun if (!decap_and_validate(skb, IPPROTO_IPIP))
378*4882a593Smuzhiyun goto drop;
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun if (!pskb_may_pull(skb, sizeof(struct iphdr)))
381*4882a593Smuzhiyun goto drop;
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun skb->protocol = htons(ETH_P_IP);
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun iph = ip_hdr(skb);
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun nhaddr = slwt->nh4.s_addr ?: iph->daddr;
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun skb_dst_drop(skb);
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun skb_set_transport_header(skb, sizeof(struct iphdr));
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev);
394*4882a593Smuzhiyun if (err)
395*4882a593Smuzhiyun goto drop;
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun return dst_input(skb);
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun drop:
400*4882a593Smuzhiyun kfree_skb(skb);
401*4882a593Smuzhiyun return -EINVAL;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun
input_action_end_dt6(struct sk_buff * skb,struct seg6_local_lwt * slwt)404*4882a593Smuzhiyun static int input_action_end_dt6(struct sk_buff *skb,
405*4882a593Smuzhiyun struct seg6_local_lwt *slwt)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun if (!decap_and_validate(skb, IPPROTO_IPV6))
408*4882a593Smuzhiyun goto drop;
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
411*4882a593Smuzhiyun goto drop;
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun skb_set_transport_header(skb, sizeof(struct ipv6hdr));
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun seg6_lookup_any_nexthop(skb, NULL, slwt->table, true);
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun return dst_input(skb);
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun drop:
420*4882a593Smuzhiyun kfree_skb(skb);
421*4882a593Smuzhiyun return -EINVAL;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun /* push an SRH on top of the current one */
input_action_end_b6(struct sk_buff * skb,struct seg6_local_lwt * slwt)425*4882a593Smuzhiyun static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
426*4882a593Smuzhiyun {
427*4882a593Smuzhiyun struct ipv6_sr_hdr *srh;
428*4882a593Smuzhiyun int err = -EINVAL;
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun srh = get_and_validate_srh(skb);
431*4882a593Smuzhiyun if (!srh)
432*4882a593Smuzhiyun goto drop;
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun err = seg6_do_srh_inline(skb, slwt->srh);
435*4882a593Smuzhiyun if (err)
436*4882a593Smuzhiyun goto drop;
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun skb_set_transport_header(skb, sizeof(struct ipv6hdr));
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun seg6_lookup_nexthop(skb, NULL, 0);
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun return dst_input(skb);
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun drop:
445*4882a593Smuzhiyun kfree_skb(skb);
446*4882a593Smuzhiyun return err;
447*4882a593Smuzhiyun }
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun /* encapsulate within an outer IPv6 header and a specified SRH */
input_action_end_b6_encap(struct sk_buff * skb,struct seg6_local_lwt * slwt)450*4882a593Smuzhiyun static int input_action_end_b6_encap(struct sk_buff *skb,
451*4882a593Smuzhiyun struct seg6_local_lwt *slwt)
452*4882a593Smuzhiyun {
453*4882a593Smuzhiyun struct ipv6_sr_hdr *srh;
454*4882a593Smuzhiyun int err = -EINVAL;
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun srh = get_and_validate_srh(skb);
457*4882a593Smuzhiyun if (!srh)
458*4882a593Smuzhiyun goto drop;
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun skb_reset_inner_headers(skb);
463*4882a593Smuzhiyun skb->encapsulation = 1;
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6);
466*4882a593Smuzhiyun if (err)
467*4882a593Smuzhiyun goto drop;
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun skb_set_transport_header(skb, sizeof(struct ipv6hdr));
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun seg6_lookup_nexthop(skb, NULL, 0);
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun return dst_input(skb);
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun drop:
476*4882a593Smuzhiyun kfree_skb(skb);
477*4882a593Smuzhiyun return err;
478*4882a593Smuzhiyun }
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states);
481*4882a593Smuzhiyun
seg6_bpf_has_valid_srh(struct sk_buff * skb)482*4882a593Smuzhiyun bool seg6_bpf_has_valid_srh(struct sk_buff *skb)
483*4882a593Smuzhiyun {
484*4882a593Smuzhiyun struct seg6_bpf_srh_state *srh_state =
485*4882a593Smuzhiyun this_cpu_ptr(&seg6_bpf_srh_states);
486*4882a593Smuzhiyun struct ipv6_sr_hdr *srh = srh_state->srh;
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun if (unlikely(srh == NULL))
489*4882a593Smuzhiyun return false;
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun if (unlikely(!srh_state->valid)) {
492*4882a593Smuzhiyun if ((srh_state->hdrlen & 7) != 0)
493*4882a593Smuzhiyun return false;
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun srh->hdrlen = (u8)(srh_state->hdrlen >> 3);
496*4882a593Smuzhiyun if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true))
497*4882a593Smuzhiyun return false;
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun srh_state->valid = true;
500*4882a593Smuzhiyun }
501*4882a593Smuzhiyun
502*4882a593Smuzhiyun return true;
503*4882a593Smuzhiyun }
504*4882a593Smuzhiyun
input_action_end_bpf(struct sk_buff * skb,struct seg6_local_lwt * slwt)505*4882a593Smuzhiyun static int input_action_end_bpf(struct sk_buff *skb,
506*4882a593Smuzhiyun struct seg6_local_lwt *slwt)
507*4882a593Smuzhiyun {
508*4882a593Smuzhiyun struct seg6_bpf_srh_state *srh_state =
509*4882a593Smuzhiyun this_cpu_ptr(&seg6_bpf_srh_states);
510*4882a593Smuzhiyun struct ipv6_sr_hdr *srh;
511*4882a593Smuzhiyun int ret;
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun srh = get_and_validate_srh(skb);
514*4882a593Smuzhiyun if (!srh) {
515*4882a593Smuzhiyun kfree_skb(skb);
516*4882a593Smuzhiyun return -EINVAL;
517*4882a593Smuzhiyun }
518*4882a593Smuzhiyun advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun /* preempt_disable is needed to protect the per-CPU buffer srh_state,
521*4882a593Smuzhiyun * which is also accessed by the bpf_lwt_seg6_* helpers
522*4882a593Smuzhiyun */
523*4882a593Smuzhiyun preempt_disable();
524*4882a593Smuzhiyun srh_state->srh = srh;
525*4882a593Smuzhiyun srh_state->hdrlen = srh->hdrlen << 3;
526*4882a593Smuzhiyun srh_state->valid = true;
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun rcu_read_lock();
529*4882a593Smuzhiyun bpf_compute_data_pointers(skb);
530*4882a593Smuzhiyun ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb);
531*4882a593Smuzhiyun rcu_read_unlock();
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun switch (ret) {
534*4882a593Smuzhiyun case BPF_OK:
535*4882a593Smuzhiyun case BPF_REDIRECT:
536*4882a593Smuzhiyun break;
537*4882a593Smuzhiyun case BPF_DROP:
538*4882a593Smuzhiyun goto drop;
539*4882a593Smuzhiyun default:
540*4882a593Smuzhiyun pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret);
541*4882a593Smuzhiyun goto drop;
542*4882a593Smuzhiyun }
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun if (srh_state->srh && !seg6_bpf_has_valid_srh(skb))
545*4882a593Smuzhiyun goto drop;
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun preempt_enable();
548*4882a593Smuzhiyun if (ret != BPF_REDIRECT)
549*4882a593Smuzhiyun seg6_lookup_nexthop(skb, NULL, 0);
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun return dst_input(skb);
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun drop:
554*4882a593Smuzhiyun preempt_enable();
555*4882a593Smuzhiyun kfree_skb(skb);
556*4882a593Smuzhiyun return -EINVAL;
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun static struct seg6_action_desc seg6_action_table[] = {
560*4882a593Smuzhiyun {
561*4882a593Smuzhiyun .action = SEG6_LOCAL_ACTION_END,
562*4882a593Smuzhiyun .attrs = 0,
563*4882a593Smuzhiyun .input = input_action_end,
564*4882a593Smuzhiyun },
565*4882a593Smuzhiyun {
566*4882a593Smuzhiyun .action = SEG6_LOCAL_ACTION_END_X,
567*4882a593Smuzhiyun .attrs = (1 << SEG6_LOCAL_NH6),
568*4882a593Smuzhiyun .input = input_action_end_x,
569*4882a593Smuzhiyun },
570*4882a593Smuzhiyun {
571*4882a593Smuzhiyun .action = SEG6_LOCAL_ACTION_END_T,
572*4882a593Smuzhiyun .attrs = (1 << SEG6_LOCAL_TABLE),
573*4882a593Smuzhiyun .input = input_action_end_t,
574*4882a593Smuzhiyun },
575*4882a593Smuzhiyun {
576*4882a593Smuzhiyun .action = SEG6_LOCAL_ACTION_END_DX2,
577*4882a593Smuzhiyun .attrs = (1 << SEG6_LOCAL_OIF),
578*4882a593Smuzhiyun .input = input_action_end_dx2,
579*4882a593Smuzhiyun },
580*4882a593Smuzhiyun {
581*4882a593Smuzhiyun .action = SEG6_LOCAL_ACTION_END_DX6,
582*4882a593Smuzhiyun .attrs = (1 << SEG6_LOCAL_NH6),
583*4882a593Smuzhiyun .input = input_action_end_dx6,
584*4882a593Smuzhiyun },
585*4882a593Smuzhiyun {
586*4882a593Smuzhiyun .action = SEG6_LOCAL_ACTION_END_DX4,
587*4882a593Smuzhiyun .attrs = (1 << SEG6_LOCAL_NH4),
588*4882a593Smuzhiyun .input = input_action_end_dx4,
589*4882a593Smuzhiyun },
590*4882a593Smuzhiyun {
591*4882a593Smuzhiyun .action = SEG6_LOCAL_ACTION_END_DT6,
592*4882a593Smuzhiyun .attrs = (1 << SEG6_LOCAL_TABLE),
593*4882a593Smuzhiyun .input = input_action_end_dt6,
594*4882a593Smuzhiyun },
595*4882a593Smuzhiyun {
596*4882a593Smuzhiyun .action = SEG6_LOCAL_ACTION_END_B6,
597*4882a593Smuzhiyun .attrs = (1 << SEG6_LOCAL_SRH),
598*4882a593Smuzhiyun .input = input_action_end_b6,
599*4882a593Smuzhiyun },
600*4882a593Smuzhiyun {
601*4882a593Smuzhiyun .action = SEG6_LOCAL_ACTION_END_B6_ENCAP,
602*4882a593Smuzhiyun .attrs = (1 << SEG6_LOCAL_SRH),
603*4882a593Smuzhiyun .input = input_action_end_b6_encap,
604*4882a593Smuzhiyun .static_headroom = sizeof(struct ipv6hdr),
605*4882a593Smuzhiyun },
606*4882a593Smuzhiyun {
607*4882a593Smuzhiyun .action = SEG6_LOCAL_ACTION_END_BPF,
608*4882a593Smuzhiyun .attrs = (1 << SEG6_LOCAL_BPF),
609*4882a593Smuzhiyun .input = input_action_end_bpf,
610*4882a593Smuzhiyun },
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun };
613*4882a593Smuzhiyun
__get_action_desc(int action)614*4882a593Smuzhiyun static struct seg6_action_desc *__get_action_desc(int action)
615*4882a593Smuzhiyun {
616*4882a593Smuzhiyun struct seg6_action_desc *desc;
617*4882a593Smuzhiyun int i, count;
618*4882a593Smuzhiyun
619*4882a593Smuzhiyun count = ARRAY_SIZE(seg6_action_table);
620*4882a593Smuzhiyun for (i = 0; i < count; i++) {
621*4882a593Smuzhiyun desc = &seg6_action_table[i];
622*4882a593Smuzhiyun if (desc->action == action)
623*4882a593Smuzhiyun return desc;
624*4882a593Smuzhiyun }
625*4882a593Smuzhiyun
626*4882a593Smuzhiyun return NULL;
627*4882a593Smuzhiyun }
628*4882a593Smuzhiyun
seg6_local_input(struct sk_buff * skb)629*4882a593Smuzhiyun static int seg6_local_input(struct sk_buff *skb)
630*4882a593Smuzhiyun {
631*4882a593Smuzhiyun struct dst_entry *orig_dst = skb_dst(skb);
632*4882a593Smuzhiyun struct seg6_action_desc *desc;
633*4882a593Smuzhiyun struct seg6_local_lwt *slwt;
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun if (skb->protocol != htons(ETH_P_IPV6)) {
636*4882a593Smuzhiyun kfree_skb(skb);
637*4882a593Smuzhiyun return -EINVAL;
638*4882a593Smuzhiyun }
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
641*4882a593Smuzhiyun desc = slwt->desc;
642*4882a593Smuzhiyun
643*4882a593Smuzhiyun return desc->input(skb, slwt);
644*4882a593Smuzhiyun }
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = {
647*4882a593Smuzhiyun [SEG6_LOCAL_ACTION] = { .type = NLA_U32 },
648*4882a593Smuzhiyun [SEG6_LOCAL_SRH] = { .type = NLA_BINARY },
649*4882a593Smuzhiyun [SEG6_LOCAL_TABLE] = { .type = NLA_U32 },
650*4882a593Smuzhiyun [SEG6_LOCAL_NH4] = { .type = NLA_BINARY,
651*4882a593Smuzhiyun .len = sizeof(struct in_addr) },
652*4882a593Smuzhiyun [SEG6_LOCAL_NH6] = { .type = NLA_BINARY,
653*4882a593Smuzhiyun .len = sizeof(struct in6_addr) },
654*4882a593Smuzhiyun [SEG6_LOCAL_IIF] = { .type = NLA_U32 },
655*4882a593Smuzhiyun [SEG6_LOCAL_OIF] = { .type = NLA_U32 },
656*4882a593Smuzhiyun [SEG6_LOCAL_BPF] = { .type = NLA_NESTED },
657*4882a593Smuzhiyun };
658*4882a593Smuzhiyun
parse_nla_srh(struct nlattr ** attrs,struct seg6_local_lwt * slwt)659*4882a593Smuzhiyun static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt)
660*4882a593Smuzhiyun {
661*4882a593Smuzhiyun struct ipv6_sr_hdr *srh;
662*4882a593Smuzhiyun int len;
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun srh = nla_data(attrs[SEG6_LOCAL_SRH]);
665*4882a593Smuzhiyun len = nla_len(attrs[SEG6_LOCAL_SRH]);
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun /* SRH must contain at least one segment */
668*4882a593Smuzhiyun if (len < sizeof(*srh) + sizeof(struct in6_addr))
669*4882a593Smuzhiyun return -EINVAL;
670*4882a593Smuzhiyun
671*4882a593Smuzhiyun if (!seg6_validate_srh(srh, len, false))
672*4882a593Smuzhiyun return -EINVAL;
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun slwt->srh = kmemdup(srh, len, GFP_KERNEL);
675*4882a593Smuzhiyun if (!slwt->srh)
676*4882a593Smuzhiyun return -ENOMEM;
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun slwt->headroom += len;
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun return 0;
681*4882a593Smuzhiyun }
682*4882a593Smuzhiyun
put_nla_srh(struct sk_buff * skb,struct seg6_local_lwt * slwt)683*4882a593Smuzhiyun static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt)
684*4882a593Smuzhiyun {
685*4882a593Smuzhiyun struct ipv6_sr_hdr *srh;
686*4882a593Smuzhiyun struct nlattr *nla;
687*4882a593Smuzhiyun int len;
688*4882a593Smuzhiyun
689*4882a593Smuzhiyun srh = slwt->srh;
690*4882a593Smuzhiyun len = (srh->hdrlen + 1) << 3;
691*4882a593Smuzhiyun
692*4882a593Smuzhiyun nla = nla_reserve(skb, SEG6_LOCAL_SRH, len);
693*4882a593Smuzhiyun if (!nla)
694*4882a593Smuzhiyun return -EMSGSIZE;
695*4882a593Smuzhiyun
696*4882a593Smuzhiyun memcpy(nla_data(nla), srh, len);
697*4882a593Smuzhiyun
698*4882a593Smuzhiyun return 0;
699*4882a593Smuzhiyun }
700*4882a593Smuzhiyun
cmp_nla_srh(struct seg6_local_lwt * a,struct seg6_local_lwt * b)701*4882a593Smuzhiyun static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
702*4882a593Smuzhiyun {
703*4882a593Smuzhiyun int len = (a->srh->hdrlen + 1) << 3;
704*4882a593Smuzhiyun
705*4882a593Smuzhiyun if (len != ((b->srh->hdrlen + 1) << 3))
706*4882a593Smuzhiyun return 1;
707*4882a593Smuzhiyun
708*4882a593Smuzhiyun return memcmp(a->srh, b->srh, len);
709*4882a593Smuzhiyun }
710*4882a593Smuzhiyun
parse_nla_table(struct nlattr ** attrs,struct seg6_local_lwt * slwt)711*4882a593Smuzhiyun static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt)
712*4882a593Smuzhiyun {
713*4882a593Smuzhiyun slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]);
714*4882a593Smuzhiyun
715*4882a593Smuzhiyun return 0;
716*4882a593Smuzhiyun }
717*4882a593Smuzhiyun
put_nla_table(struct sk_buff * skb,struct seg6_local_lwt * slwt)718*4882a593Smuzhiyun static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt)
719*4882a593Smuzhiyun {
720*4882a593Smuzhiyun if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table))
721*4882a593Smuzhiyun return -EMSGSIZE;
722*4882a593Smuzhiyun
723*4882a593Smuzhiyun return 0;
724*4882a593Smuzhiyun }
725*4882a593Smuzhiyun
cmp_nla_table(struct seg6_local_lwt * a,struct seg6_local_lwt * b)726*4882a593Smuzhiyun static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
727*4882a593Smuzhiyun {
728*4882a593Smuzhiyun if (a->table != b->table)
729*4882a593Smuzhiyun return 1;
730*4882a593Smuzhiyun
731*4882a593Smuzhiyun return 0;
732*4882a593Smuzhiyun }
733*4882a593Smuzhiyun
parse_nla_nh4(struct nlattr ** attrs,struct seg6_local_lwt * slwt)734*4882a593Smuzhiyun static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt)
735*4882a593Smuzhiyun {
736*4882a593Smuzhiyun memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]),
737*4882a593Smuzhiyun sizeof(struct in_addr));
738*4882a593Smuzhiyun
739*4882a593Smuzhiyun return 0;
740*4882a593Smuzhiyun }
741*4882a593Smuzhiyun
put_nla_nh4(struct sk_buff * skb,struct seg6_local_lwt * slwt)742*4882a593Smuzhiyun static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt)
743*4882a593Smuzhiyun {
744*4882a593Smuzhiyun struct nlattr *nla;
745*4882a593Smuzhiyun
746*4882a593Smuzhiyun nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr));
747*4882a593Smuzhiyun if (!nla)
748*4882a593Smuzhiyun return -EMSGSIZE;
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr));
751*4882a593Smuzhiyun
752*4882a593Smuzhiyun return 0;
753*4882a593Smuzhiyun }
754*4882a593Smuzhiyun
cmp_nla_nh4(struct seg6_local_lwt * a,struct seg6_local_lwt * b)755*4882a593Smuzhiyun static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
756*4882a593Smuzhiyun {
757*4882a593Smuzhiyun return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr));
758*4882a593Smuzhiyun }
759*4882a593Smuzhiyun
parse_nla_nh6(struct nlattr ** attrs,struct seg6_local_lwt * slwt)760*4882a593Smuzhiyun static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt)
761*4882a593Smuzhiyun {
762*4882a593Smuzhiyun memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]),
763*4882a593Smuzhiyun sizeof(struct in6_addr));
764*4882a593Smuzhiyun
765*4882a593Smuzhiyun return 0;
766*4882a593Smuzhiyun }
767*4882a593Smuzhiyun
put_nla_nh6(struct sk_buff * skb,struct seg6_local_lwt * slwt)768*4882a593Smuzhiyun static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
769*4882a593Smuzhiyun {
770*4882a593Smuzhiyun struct nlattr *nla;
771*4882a593Smuzhiyun
772*4882a593Smuzhiyun nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr));
773*4882a593Smuzhiyun if (!nla)
774*4882a593Smuzhiyun return -EMSGSIZE;
775*4882a593Smuzhiyun
776*4882a593Smuzhiyun memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr));
777*4882a593Smuzhiyun
778*4882a593Smuzhiyun return 0;
779*4882a593Smuzhiyun }
780*4882a593Smuzhiyun
cmp_nla_nh6(struct seg6_local_lwt * a,struct seg6_local_lwt * b)781*4882a593Smuzhiyun static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
782*4882a593Smuzhiyun {
783*4882a593Smuzhiyun return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr));
784*4882a593Smuzhiyun }
785*4882a593Smuzhiyun
parse_nla_iif(struct nlattr ** attrs,struct seg6_local_lwt * slwt)786*4882a593Smuzhiyun static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt)
787*4882a593Smuzhiyun {
788*4882a593Smuzhiyun slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]);
789*4882a593Smuzhiyun
790*4882a593Smuzhiyun return 0;
791*4882a593Smuzhiyun }
792*4882a593Smuzhiyun
put_nla_iif(struct sk_buff * skb,struct seg6_local_lwt * slwt)793*4882a593Smuzhiyun static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt)
794*4882a593Smuzhiyun {
795*4882a593Smuzhiyun if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif))
796*4882a593Smuzhiyun return -EMSGSIZE;
797*4882a593Smuzhiyun
798*4882a593Smuzhiyun return 0;
799*4882a593Smuzhiyun }
800*4882a593Smuzhiyun
cmp_nla_iif(struct seg6_local_lwt * a,struct seg6_local_lwt * b)801*4882a593Smuzhiyun static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
802*4882a593Smuzhiyun {
803*4882a593Smuzhiyun if (a->iif != b->iif)
804*4882a593Smuzhiyun return 1;
805*4882a593Smuzhiyun
806*4882a593Smuzhiyun return 0;
807*4882a593Smuzhiyun }
808*4882a593Smuzhiyun
parse_nla_oif(struct nlattr ** attrs,struct seg6_local_lwt * slwt)809*4882a593Smuzhiyun static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt)
810*4882a593Smuzhiyun {
811*4882a593Smuzhiyun slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]);
812*4882a593Smuzhiyun
813*4882a593Smuzhiyun return 0;
814*4882a593Smuzhiyun }
815*4882a593Smuzhiyun
put_nla_oif(struct sk_buff * skb,struct seg6_local_lwt * slwt)816*4882a593Smuzhiyun static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt)
817*4882a593Smuzhiyun {
818*4882a593Smuzhiyun if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif))
819*4882a593Smuzhiyun return -EMSGSIZE;
820*4882a593Smuzhiyun
821*4882a593Smuzhiyun return 0;
822*4882a593Smuzhiyun }
823*4882a593Smuzhiyun
cmp_nla_oif(struct seg6_local_lwt * a,struct seg6_local_lwt * b)824*4882a593Smuzhiyun static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
825*4882a593Smuzhiyun {
826*4882a593Smuzhiyun if (a->oif != b->oif)
827*4882a593Smuzhiyun return 1;
828*4882a593Smuzhiyun
829*4882a593Smuzhiyun return 0;
830*4882a593Smuzhiyun }
831*4882a593Smuzhiyun
832*4882a593Smuzhiyun #define MAX_PROG_NAME 256
833*4882a593Smuzhiyun static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = {
834*4882a593Smuzhiyun [SEG6_LOCAL_BPF_PROG] = { .type = NLA_U32, },
835*4882a593Smuzhiyun [SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING,
836*4882a593Smuzhiyun .len = MAX_PROG_NAME },
837*4882a593Smuzhiyun };
838*4882a593Smuzhiyun
parse_nla_bpf(struct nlattr ** attrs,struct seg6_local_lwt * slwt)839*4882a593Smuzhiyun static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt)
840*4882a593Smuzhiyun {
841*4882a593Smuzhiyun struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1];
842*4882a593Smuzhiyun struct bpf_prog *p;
843*4882a593Smuzhiyun int ret;
844*4882a593Smuzhiyun u32 fd;
845*4882a593Smuzhiyun
846*4882a593Smuzhiyun ret = nla_parse_nested_deprecated(tb, SEG6_LOCAL_BPF_PROG_MAX,
847*4882a593Smuzhiyun attrs[SEG6_LOCAL_BPF],
848*4882a593Smuzhiyun bpf_prog_policy, NULL);
849*4882a593Smuzhiyun if (ret < 0)
850*4882a593Smuzhiyun return ret;
851*4882a593Smuzhiyun
852*4882a593Smuzhiyun if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME])
853*4882a593Smuzhiyun return -EINVAL;
854*4882a593Smuzhiyun
855*4882a593Smuzhiyun slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL);
856*4882a593Smuzhiyun if (!slwt->bpf.name)
857*4882a593Smuzhiyun return -ENOMEM;
858*4882a593Smuzhiyun
859*4882a593Smuzhiyun fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]);
860*4882a593Smuzhiyun p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL);
861*4882a593Smuzhiyun if (IS_ERR(p)) {
862*4882a593Smuzhiyun kfree(slwt->bpf.name);
863*4882a593Smuzhiyun return PTR_ERR(p);
864*4882a593Smuzhiyun }
865*4882a593Smuzhiyun
866*4882a593Smuzhiyun slwt->bpf.prog = p;
867*4882a593Smuzhiyun return 0;
868*4882a593Smuzhiyun }
869*4882a593Smuzhiyun
put_nla_bpf(struct sk_buff * skb,struct seg6_local_lwt * slwt)870*4882a593Smuzhiyun static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt)
871*4882a593Smuzhiyun {
872*4882a593Smuzhiyun struct nlattr *nest;
873*4882a593Smuzhiyun
874*4882a593Smuzhiyun if (!slwt->bpf.prog)
875*4882a593Smuzhiyun return 0;
876*4882a593Smuzhiyun
877*4882a593Smuzhiyun nest = nla_nest_start_noflag(skb, SEG6_LOCAL_BPF);
878*4882a593Smuzhiyun if (!nest)
879*4882a593Smuzhiyun return -EMSGSIZE;
880*4882a593Smuzhiyun
881*4882a593Smuzhiyun if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id))
882*4882a593Smuzhiyun return -EMSGSIZE;
883*4882a593Smuzhiyun
884*4882a593Smuzhiyun if (slwt->bpf.name &&
885*4882a593Smuzhiyun nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name))
886*4882a593Smuzhiyun return -EMSGSIZE;
887*4882a593Smuzhiyun
888*4882a593Smuzhiyun return nla_nest_end(skb, nest);
889*4882a593Smuzhiyun }
890*4882a593Smuzhiyun
cmp_nla_bpf(struct seg6_local_lwt * a,struct seg6_local_lwt * b)891*4882a593Smuzhiyun static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
892*4882a593Smuzhiyun {
893*4882a593Smuzhiyun if (!a->bpf.name && !b->bpf.name)
894*4882a593Smuzhiyun return 0;
895*4882a593Smuzhiyun
896*4882a593Smuzhiyun if (!a->bpf.name || !b->bpf.name)
897*4882a593Smuzhiyun return 1;
898*4882a593Smuzhiyun
899*4882a593Smuzhiyun return strcmp(a->bpf.name, b->bpf.name);
900*4882a593Smuzhiyun }
901*4882a593Smuzhiyun
902*4882a593Smuzhiyun struct seg6_action_param {
903*4882a593Smuzhiyun int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt);
904*4882a593Smuzhiyun int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
905*4882a593Smuzhiyun int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b);
906*4882a593Smuzhiyun };
907*4882a593Smuzhiyun
908*4882a593Smuzhiyun static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = {
909*4882a593Smuzhiyun [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh,
910*4882a593Smuzhiyun .put = put_nla_srh,
911*4882a593Smuzhiyun .cmp = cmp_nla_srh },
912*4882a593Smuzhiyun
913*4882a593Smuzhiyun [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table,
914*4882a593Smuzhiyun .put = put_nla_table,
915*4882a593Smuzhiyun .cmp = cmp_nla_table },
916*4882a593Smuzhiyun
917*4882a593Smuzhiyun [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4,
918*4882a593Smuzhiyun .put = put_nla_nh4,
919*4882a593Smuzhiyun .cmp = cmp_nla_nh4 },
920*4882a593Smuzhiyun
921*4882a593Smuzhiyun [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6,
922*4882a593Smuzhiyun .put = put_nla_nh6,
923*4882a593Smuzhiyun .cmp = cmp_nla_nh6 },
924*4882a593Smuzhiyun
925*4882a593Smuzhiyun [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif,
926*4882a593Smuzhiyun .put = put_nla_iif,
927*4882a593Smuzhiyun .cmp = cmp_nla_iif },
928*4882a593Smuzhiyun
929*4882a593Smuzhiyun [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif,
930*4882a593Smuzhiyun .put = put_nla_oif,
931*4882a593Smuzhiyun .cmp = cmp_nla_oif },
932*4882a593Smuzhiyun
933*4882a593Smuzhiyun [SEG6_LOCAL_BPF] = { .parse = parse_nla_bpf,
934*4882a593Smuzhiyun .put = put_nla_bpf,
935*4882a593Smuzhiyun .cmp = cmp_nla_bpf },
936*4882a593Smuzhiyun
937*4882a593Smuzhiyun };
938*4882a593Smuzhiyun
parse_nla_action(struct nlattr ** attrs,struct seg6_local_lwt * slwt)939*4882a593Smuzhiyun static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt)
940*4882a593Smuzhiyun {
941*4882a593Smuzhiyun struct seg6_action_param *param;
942*4882a593Smuzhiyun struct seg6_action_desc *desc;
943*4882a593Smuzhiyun int i, err;
944*4882a593Smuzhiyun
945*4882a593Smuzhiyun desc = __get_action_desc(slwt->action);
946*4882a593Smuzhiyun if (!desc)
947*4882a593Smuzhiyun return -EINVAL;
948*4882a593Smuzhiyun
949*4882a593Smuzhiyun if (!desc->input)
950*4882a593Smuzhiyun return -EOPNOTSUPP;
951*4882a593Smuzhiyun
952*4882a593Smuzhiyun slwt->desc = desc;
953*4882a593Smuzhiyun slwt->headroom += desc->static_headroom;
954*4882a593Smuzhiyun
955*4882a593Smuzhiyun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
956*4882a593Smuzhiyun if (desc->attrs & (1 << i)) {
957*4882a593Smuzhiyun if (!attrs[i])
958*4882a593Smuzhiyun return -EINVAL;
959*4882a593Smuzhiyun
960*4882a593Smuzhiyun param = &seg6_action_params[i];
961*4882a593Smuzhiyun
962*4882a593Smuzhiyun err = param->parse(attrs, slwt);
963*4882a593Smuzhiyun if (err < 0)
964*4882a593Smuzhiyun return err;
965*4882a593Smuzhiyun }
966*4882a593Smuzhiyun }
967*4882a593Smuzhiyun
968*4882a593Smuzhiyun return 0;
969*4882a593Smuzhiyun }
970*4882a593Smuzhiyun
seg6_local_build_state(struct net * net,struct nlattr * nla,unsigned int family,const void * cfg,struct lwtunnel_state ** ts,struct netlink_ext_ack * extack)971*4882a593Smuzhiyun static int seg6_local_build_state(struct net *net, struct nlattr *nla,
972*4882a593Smuzhiyun unsigned int family, const void *cfg,
973*4882a593Smuzhiyun struct lwtunnel_state **ts,
974*4882a593Smuzhiyun struct netlink_ext_ack *extack)
975*4882a593Smuzhiyun {
976*4882a593Smuzhiyun struct nlattr *tb[SEG6_LOCAL_MAX + 1];
977*4882a593Smuzhiyun struct lwtunnel_state *newts;
978*4882a593Smuzhiyun struct seg6_local_lwt *slwt;
979*4882a593Smuzhiyun int err;
980*4882a593Smuzhiyun
981*4882a593Smuzhiyun if (family != AF_INET6)
982*4882a593Smuzhiyun return -EINVAL;
983*4882a593Smuzhiyun
984*4882a593Smuzhiyun err = nla_parse_nested_deprecated(tb, SEG6_LOCAL_MAX, nla,
985*4882a593Smuzhiyun seg6_local_policy, extack);
986*4882a593Smuzhiyun
987*4882a593Smuzhiyun if (err < 0)
988*4882a593Smuzhiyun return err;
989*4882a593Smuzhiyun
990*4882a593Smuzhiyun if (!tb[SEG6_LOCAL_ACTION])
991*4882a593Smuzhiyun return -EINVAL;
992*4882a593Smuzhiyun
993*4882a593Smuzhiyun newts = lwtunnel_state_alloc(sizeof(*slwt));
994*4882a593Smuzhiyun if (!newts)
995*4882a593Smuzhiyun return -ENOMEM;
996*4882a593Smuzhiyun
997*4882a593Smuzhiyun slwt = seg6_local_lwtunnel(newts);
998*4882a593Smuzhiyun slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]);
999*4882a593Smuzhiyun
1000*4882a593Smuzhiyun err = parse_nla_action(tb, slwt);
1001*4882a593Smuzhiyun if (err < 0)
1002*4882a593Smuzhiyun goto out_free;
1003*4882a593Smuzhiyun
1004*4882a593Smuzhiyun newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL;
1005*4882a593Smuzhiyun newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT;
1006*4882a593Smuzhiyun newts->headroom = slwt->headroom;
1007*4882a593Smuzhiyun
1008*4882a593Smuzhiyun *ts = newts;
1009*4882a593Smuzhiyun
1010*4882a593Smuzhiyun return 0;
1011*4882a593Smuzhiyun
1012*4882a593Smuzhiyun out_free:
1013*4882a593Smuzhiyun kfree(slwt->srh);
1014*4882a593Smuzhiyun kfree(newts);
1015*4882a593Smuzhiyun return err;
1016*4882a593Smuzhiyun }
1017*4882a593Smuzhiyun
seg6_local_destroy_state(struct lwtunnel_state * lwt)1018*4882a593Smuzhiyun static void seg6_local_destroy_state(struct lwtunnel_state *lwt)
1019*4882a593Smuzhiyun {
1020*4882a593Smuzhiyun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
1021*4882a593Smuzhiyun
1022*4882a593Smuzhiyun kfree(slwt->srh);
1023*4882a593Smuzhiyun
1024*4882a593Smuzhiyun if (slwt->desc->attrs & (1 << SEG6_LOCAL_BPF)) {
1025*4882a593Smuzhiyun kfree(slwt->bpf.name);
1026*4882a593Smuzhiyun bpf_prog_put(slwt->bpf.prog);
1027*4882a593Smuzhiyun }
1028*4882a593Smuzhiyun
1029*4882a593Smuzhiyun return;
1030*4882a593Smuzhiyun }
1031*4882a593Smuzhiyun
seg6_local_fill_encap(struct sk_buff * skb,struct lwtunnel_state * lwt)1032*4882a593Smuzhiyun static int seg6_local_fill_encap(struct sk_buff *skb,
1033*4882a593Smuzhiyun struct lwtunnel_state *lwt)
1034*4882a593Smuzhiyun {
1035*4882a593Smuzhiyun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
1036*4882a593Smuzhiyun struct seg6_action_param *param;
1037*4882a593Smuzhiyun int i, err;
1038*4882a593Smuzhiyun
1039*4882a593Smuzhiyun if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action))
1040*4882a593Smuzhiyun return -EMSGSIZE;
1041*4882a593Smuzhiyun
1042*4882a593Smuzhiyun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
1043*4882a593Smuzhiyun if (slwt->desc->attrs & (1 << i)) {
1044*4882a593Smuzhiyun param = &seg6_action_params[i];
1045*4882a593Smuzhiyun err = param->put(skb, slwt);
1046*4882a593Smuzhiyun if (err < 0)
1047*4882a593Smuzhiyun return err;
1048*4882a593Smuzhiyun }
1049*4882a593Smuzhiyun }
1050*4882a593Smuzhiyun
1051*4882a593Smuzhiyun return 0;
1052*4882a593Smuzhiyun }
1053*4882a593Smuzhiyun
seg6_local_get_encap_size(struct lwtunnel_state * lwt)1054*4882a593Smuzhiyun static int seg6_local_get_encap_size(struct lwtunnel_state *lwt)
1055*4882a593Smuzhiyun {
1056*4882a593Smuzhiyun struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
1057*4882a593Smuzhiyun unsigned long attrs;
1058*4882a593Smuzhiyun int nlsize;
1059*4882a593Smuzhiyun
1060*4882a593Smuzhiyun nlsize = nla_total_size(4); /* action */
1061*4882a593Smuzhiyun
1062*4882a593Smuzhiyun attrs = slwt->desc->attrs;
1063*4882a593Smuzhiyun
1064*4882a593Smuzhiyun if (attrs & (1 << SEG6_LOCAL_SRH))
1065*4882a593Smuzhiyun nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3);
1066*4882a593Smuzhiyun
1067*4882a593Smuzhiyun if (attrs & (1 << SEG6_LOCAL_TABLE))
1068*4882a593Smuzhiyun nlsize += nla_total_size(4);
1069*4882a593Smuzhiyun
1070*4882a593Smuzhiyun if (attrs & (1 << SEG6_LOCAL_NH4))
1071*4882a593Smuzhiyun nlsize += nla_total_size(4);
1072*4882a593Smuzhiyun
1073*4882a593Smuzhiyun if (attrs & (1 << SEG6_LOCAL_NH6))
1074*4882a593Smuzhiyun nlsize += nla_total_size(16);
1075*4882a593Smuzhiyun
1076*4882a593Smuzhiyun if (attrs & (1 << SEG6_LOCAL_IIF))
1077*4882a593Smuzhiyun nlsize += nla_total_size(4);
1078*4882a593Smuzhiyun
1079*4882a593Smuzhiyun if (attrs & (1 << SEG6_LOCAL_OIF))
1080*4882a593Smuzhiyun nlsize += nla_total_size(4);
1081*4882a593Smuzhiyun
1082*4882a593Smuzhiyun if (attrs & (1 << SEG6_LOCAL_BPF))
1083*4882a593Smuzhiyun nlsize += nla_total_size(sizeof(struct nlattr)) +
1084*4882a593Smuzhiyun nla_total_size(MAX_PROG_NAME) +
1085*4882a593Smuzhiyun nla_total_size(4);
1086*4882a593Smuzhiyun
1087*4882a593Smuzhiyun return nlsize;
1088*4882a593Smuzhiyun }
1089*4882a593Smuzhiyun
seg6_local_cmp_encap(struct lwtunnel_state * a,struct lwtunnel_state * b)1090*4882a593Smuzhiyun static int seg6_local_cmp_encap(struct lwtunnel_state *a,
1091*4882a593Smuzhiyun struct lwtunnel_state *b)
1092*4882a593Smuzhiyun {
1093*4882a593Smuzhiyun struct seg6_local_lwt *slwt_a, *slwt_b;
1094*4882a593Smuzhiyun struct seg6_action_param *param;
1095*4882a593Smuzhiyun int i;
1096*4882a593Smuzhiyun
1097*4882a593Smuzhiyun slwt_a = seg6_local_lwtunnel(a);
1098*4882a593Smuzhiyun slwt_b = seg6_local_lwtunnel(b);
1099*4882a593Smuzhiyun
1100*4882a593Smuzhiyun if (slwt_a->action != slwt_b->action)
1101*4882a593Smuzhiyun return 1;
1102*4882a593Smuzhiyun
1103*4882a593Smuzhiyun if (slwt_a->desc->attrs != slwt_b->desc->attrs)
1104*4882a593Smuzhiyun return 1;
1105*4882a593Smuzhiyun
1106*4882a593Smuzhiyun for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
1107*4882a593Smuzhiyun if (slwt_a->desc->attrs & (1 << i)) {
1108*4882a593Smuzhiyun param = &seg6_action_params[i];
1109*4882a593Smuzhiyun if (param->cmp(slwt_a, slwt_b))
1110*4882a593Smuzhiyun return 1;
1111*4882a593Smuzhiyun }
1112*4882a593Smuzhiyun }
1113*4882a593Smuzhiyun
1114*4882a593Smuzhiyun return 0;
1115*4882a593Smuzhiyun }
1116*4882a593Smuzhiyun
1117*4882a593Smuzhiyun static const struct lwtunnel_encap_ops seg6_local_ops = {
1118*4882a593Smuzhiyun .build_state = seg6_local_build_state,
1119*4882a593Smuzhiyun .destroy_state = seg6_local_destroy_state,
1120*4882a593Smuzhiyun .input = seg6_local_input,
1121*4882a593Smuzhiyun .fill_encap = seg6_local_fill_encap,
1122*4882a593Smuzhiyun .get_encap_size = seg6_local_get_encap_size,
1123*4882a593Smuzhiyun .cmp_encap = seg6_local_cmp_encap,
1124*4882a593Smuzhiyun .owner = THIS_MODULE,
1125*4882a593Smuzhiyun };
1126*4882a593Smuzhiyun
seg6_local_init(void)1127*4882a593Smuzhiyun int __init seg6_local_init(void)
1128*4882a593Smuzhiyun {
1129*4882a593Smuzhiyun return lwtunnel_encap_add_ops(&seg6_local_ops,
1130*4882a593Smuzhiyun LWTUNNEL_ENCAP_SEG6_LOCAL);
1131*4882a593Smuzhiyun }
1132*4882a593Smuzhiyun
seg6_local_exit(void)1133*4882a593Smuzhiyun void seg6_local_exit(void)
1134*4882a593Smuzhiyun {
1135*4882a593Smuzhiyun lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL);
1136*4882a593Smuzhiyun }
1137