xref: /OK3568_Linux_fs/kernel/net/ipv6/rpl_iptunnel.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /**
3*4882a593Smuzhiyun  * Authors:
4*4882a593Smuzhiyun  * (C) 2020 Alexander Aring <alex.aring@gmail.com>
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <linux/rpl_iptunnel.h>
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <net/dst_cache.h>
10*4882a593Smuzhiyun #include <net/ip6_route.h>
11*4882a593Smuzhiyun #include <net/lwtunnel.h>
12*4882a593Smuzhiyun #include <net/ipv6.h>
13*4882a593Smuzhiyun #include <net/rpl.h>
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun struct rpl_iptunnel_encap {
16*4882a593Smuzhiyun 	struct ipv6_rpl_sr_hdr srh[0];
17*4882a593Smuzhiyun };
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun struct rpl_lwt {
20*4882a593Smuzhiyun 	struct dst_cache cache;
21*4882a593Smuzhiyun 	struct rpl_iptunnel_encap tuninfo;
22*4882a593Smuzhiyun };
23*4882a593Smuzhiyun 
rpl_lwt_lwtunnel(struct lwtunnel_state * lwt)24*4882a593Smuzhiyun static inline struct rpl_lwt *rpl_lwt_lwtunnel(struct lwtunnel_state *lwt)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun 	return (struct rpl_lwt *)lwt->data;
27*4882a593Smuzhiyun }
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun static inline struct rpl_iptunnel_encap *
rpl_encap_lwtunnel(struct lwtunnel_state * lwt)30*4882a593Smuzhiyun rpl_encap_lwtunnel(struct lwtunnel_state *lwt)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun 	return &rpl_lwt_lwtunnel(lwt)->tuninfo;
33*4882a593Smuzhiyun }
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun static const struct nla_policy rpl_iptunnel_policy[RPL_IPTUNNEL_MAX + 1] = {
36*4882a593Smuzhiyun 	[RPL_IPTUNNEL_SRH]	= { .type = NLA_BINARY },
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun 
rpl_validate_srh(struct net * net,struct ipv6_rpl_sr_hdr * srh,size_t seglen)39*4882a593Smuzhiyun static bool rpl_validate_srh(struct net *net, struct ipv6_rpl_sr_hdr *srh,
40*4882a593Smuzhiyun 			     size_t seglen)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun 	int err;
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	if ((srh->hdrlen << 3) != seglen)
45*4882a593Smuzhiyun 		return false;
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	/* check at least one segment and seglen fit with segments_left */
48*4882a593Smuzhiyun 	if (!srh->segments_left ||
49*4882a593Smuzhiyun 	    (srh->segments_left * sizeof(struct in6_addr)) != seglen)
50*4882a593Smuzhiyun 		return false;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	if (srh->cmpri || srh->cmpre)
53*4882a593Smuzhiyun 		return false;
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	err = ipv6_chk_rpl_srh_loop(net, srh->rpl_segaddr,
56*4882a593Smuzhiyun 				    srh->segments_left);
57*4882a593Smuzhiyun 	if (err)
58*4882a593Smuzhiyun 		return false;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	if (ipv6_addr_type(&srh->rpl_segaddr[srh->segments_left - 1]) &
61*4882a593Smuzhiyun 	    IPV6_ADDR_MULTICAST)
62*4882a593Smuzhiyun 		return false;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	return true;
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
rpl_build_state(struct net * net,struct nlattr * nla,unsigned int family,const void * cfg,struct lwtunnel_state ** ts,struct netlink_ext_ack * extack)67*4882a593Smuzhiyun static int rpl_build_state(struct net *net, struct nlattr *nla,
68*4882a593Smuzhiyun 			   unsigned int family, const void *cfg,
69*4882a593Smuzhiyun 			   struct lwtunnel_state **ts,
70*4882a593Smuzhiyun 			   struct netlink_ext_ack *extack)
71*4882a593Smuzhiyun {
72*4882a593Smuzhiyun 	struct nlattr *tb[RPL_IPTUNNEL_MAX + 1];
73*4882a593Smuzhiyun 	struct lwtunnel_state *newts;
74*4882a593Smuzhiyun 	struct ipv6_rpl_sr_hdr *srh;
75*4882a593Smuzhiyun 	struct rpl_lwt *rlwt;
76*4882a593Smuzhiyun 	int err, srh_len;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	if (family != AF_INET6)
79*4882a593Smuzhiyun 		return -EINVAL;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	err = nla_parse_nested(tb, RPL_IPTUNNEL_MAX, nla,
82*4882a593Smuzhiyun 			       rpl_iptunnel_policy, extack);
83*4882a593Smuzhiyun 	if (err < 0)
84*4882a593Smuzhiyun 		return err;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	if (!tb[RPL_IPTUNNEL_SRH])
87*4882a593Smuzhiyun 		return -EINVAL;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	srh = nla_data(tb[RPL_IPTUNNEL_SRH]);
90*4882a593Smuzhiyun 	srh_len = nla_len(tb[RPL_IPTUNNEL_SRH]);
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	if (srh_len < sizeof(*srh))
93*4882a593Smuzhiyun 		return -EINVAL;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	/* verify that SRH is consistent */
96*4882a593Smuzhiyun 	if (!rpl_validate_srh(net, srh, srh_len - sizeof(*srh)))
97*4882a593Smuzhiyun 		return -EINVAL;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	newts = lwtunnel_state_alloc(srh_len + sizeof(*rlwt));
100*4882a593Smuzhiyun 	if (!newts)
101*4882a593Smuzhiyun 		return -ENOMEM;
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	rlwt = rpl_lwt_lwtunnel(newts);
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	err = dst_cache_init(&rlwt->cache, GFP_ATOMIC);
106*4882a593Smuzhiyun 	if (err) {
107*4882a593Smuzhiyun 		kfree(newts);
108*4882a593Smuzhiyun 		return err;
109*4882a593Smuzhiyun 	}
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	memcpy(&rlwt->tuninfo.srh, srh, srh_len);
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	newts->type = LWTUNNEL_ENCAP_RPL;
114*4882a593Smuzhiyun 	newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT;
115*4882a593Smuzhiyun 	newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	*ts = newts;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	return 0;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun 
rpl_destroy_state(struct lwtunnel_state * lwt)122*4882a593Smuzhiyun static void rpl_destroy_state(struct lwtunnel_state *lwt)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun 	dst_cache_destroy(&rpl_lwt_lwtunnel(lwt)->cache);
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
rpl_do_srh_inline(struct sk_buff * skb,const struct rpl_lwt * rlwt,const struct ipv6_rpl_sr_hdr * srh)127*4882a593Smuzhiyun static int rpl_do_srh_inline(struct sk_buff *skb, const struct rpl_lwt *rlwt,
128*4882a593Smuzhiyun 			     const struct ipv6_rpl_sr_hdr *srh)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun 	struct ipv6_rpl_sr_hdr *isrh, *csrh;
131*4882a593Smuzhiyun 	const struct ipv6hdr *oldhdr;
132*4882a593Smuzhiyun 	struct ipv6hdr *hdr;
133*4882a593Smuzhiyun 	unsigned char *buf;
134*4882a593Smuzhiyun 	size_t hdrlen;
135*4882a593Smuzhiyun 	int err;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	oldhdr = ipv6_hdr(skb);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	buf = kcalloc(struct_size(srh, segments.addr, srh->segments_left), 2, GFP_ATOMIC);
140*4882a593Smuzhiyun 	if (!buf)
141*4882a593Smuzhiyun 		return -ENOMEM;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	isrh = (struct ipv6_rpl_sr_hdr *)buf;
144*4882a593Smuzhiyun 	csrh = (struct ipv6_rpl_sr_hdr *)(buf + ((srh->hdrlen + 1) << 3));
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	memcpy(isrh, srh, sizeof(*isrh));
147*4882a593Smuzhiyun 	memcpy(isrh->rpl_segaddr, &srh->rpl_segaddr[1],
148*4882a593Smuzhiyun 	       (srh->segments_left - 1) * 16);
149*4882a593Smuzhiyun 	isrh->rpl_segaddr[srh->segments_left - 1] = oldhdr->daddr;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	ipv6_rpl_srh_compress(csrh, isrh, &srh->rpl_segaddr[0],
152*4882a593Smuzhiyun 			      isrh->segments_left - 1);
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	hdrlen = ((csrh->hdrlen + 1) << 3);
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	err = skb_cow_head(skb, hdrlen + skb->mac_len);
157*4882a593Smuzhiyun 	if (unlikely(err)) {
158*4882a593Smuzhiyun 		kfree(buf);
159*4882a593Smuzhiyun 		return err;
160*4882a593Smuzhiyun 	}
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	skb_pull(skb, sizeof(struct ipv6hdr));
163*4882a593Smuzhiyun 	skb_postpull_rcsum(skb, skb_network_header(skb),
164*4882a593Smuzhiyun 			   sizeof(struct ipv6hdr));
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	skb_push(skb, sizeof(struct ipv6hdr) + hdrlen);
167*4882a593Smuzhiyun 	skb_reset_network_header(skb);
168*4882a593Smuzhiyun 	skb_mac_header_rebuild(skb);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	hdr = ipv6_hdr(skb);
171*4882a593Smuzhiyun 	memmove(hdr, oldhdr, sizeof(*hdr));
172*4882a593Smuzhiyun 	isrh = (void *)hdr + sizeof(*hdr);
173*4882a593Smuzhiyun 	memcpy(isrh, csrh, hdrlen);
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	isrh->nexthdr = hdr->nexthdr;
176*4882a593Smuzhiyun 	hdr->nexthdr = NEXTHDR_ROUTING;
177*4882a593Smuzhiyun 	hdr->daddr = srh->rpl_segaddr[0];
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
180*4882a593Smuzhiyun 	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	skb_postpush_rcsum(skb, hdr, sizeof(struct ipv6hdr) + hdrlen);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	kfree(buf);
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	return 0;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
rpl_do_srh(struct sk_buff * skb,const struct rpl_lwt * rlwt)189*4882a593Smuzhiyun static int rpl_do_srh(struct sk_buff *skb, const struct rpl_lwt *rlwt)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun 	struct dst_entry *dst = skb_dst(skb);
192*4882a593Smuzhiyun 	struct rpl_iptunnel_encap *tinfo;
193*4882a593Smuzhiyun 	int err = 0;
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	if (skb->protocol != htons(ETH_P_IPV6))
196*4882a593Smuzhiyun 		return -EINVAL;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	tinfo = rpl_encap_lwtunnel(dst->lwtstate);
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	err = rpl_do_srh_inline(skb, rlwt, tinfo->srh);
201*4882a593Smuzhiyun 	if (err)
202*4882a593Smuzhiyun 		return err;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	return 0;
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun 
rpl_output(struct net * net,struct sock * sk,struct sk_buff * skb)207*4882a593Smuzhiyun static int rpl_output(struct net *net, struct sock *sk, struct sk_buff *skb)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun 	struct dst_entry *orig_dst = skb_dst(skb);
210*4882a593Smuzhiyun 	struct dst_entry *dst = NULL;
211*4882a593Smuzhiyun 	struct rpl_lwt *rlwt;
212*4882a593Smuzhiyun 	int err;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	rlwt = rpl_lwt_lwtunnel(orig_dst->lwtstate);
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	err = rpl_do_srh(skb, rlwt);
217*4882a593Smuzhiyun 	if (unlikely(err))
218*4882a593Smuzhiyun 		goto drop;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	preempt_disable();
221*4882a593Smuzhiyun 	dst = dst_cache_get(&rlwt->cache);
222*4882a593Smuzhiyun 	preempt_enable();
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	if (unlikely(!dst)) {
225*4882a593Smuzhiyun 		struct ipv6hdr *hdr = ipv6_hdr(skb);
226*4882a593Smuzhiyun 		struct flowi6 fl6;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 		memset(&fl6, 0, sizeof(fl6));
229*4882a593Smuzhiyun 		fl6.daddr = hdr->daddr;
230*4882a593Smuzhiyun 		fl6.saddr = hdr->saddr;
231*4882a593Smuzhiyun 		fl6.flowlabel = ip6_flowinfo(hdr);
232*4882a593Smuzhiyun 		fl6.flowi6_mark = skb->mark;
233*4882a593Smuzhiyun 		fl6.flowi6_proto = hdr->nexthdr;
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 		dst = ip6_route_output(net, NULL, &fl6);
236*4882a593Smuzhiyun 		if (dst->error) {
237*4882a593Smuzhiyun 			err = dst->error;
238*4882a593Smuzhiyun 			dst_release(dst);
239*4882a593Smuzhiyun 			goto drop;
240*4882a593Smuzhiyun 		}
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 		preempt_disable();
243*4882a593Smuzhiyun 		dst_cache_set_ip6(&rlwt->cache, dst, &fl6.saddr);
244*4882a593Smuzhiyun 		preempt_enable();
245*4882a593Smuzhiyun 	}
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	skb_dst_drop(skb);
248*4882a593Smuzhiyun 	skb_dst_set(skb, dst);
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
251*4882a593Smuzhiyun 	if (unlikely(err))
252*4882a593Smuzhiyun 		goto drop;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	return dst_output(net, sk, skb);
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun drop:
257*4882a593Smuzhiyun 	kfree_skb(skb);
258*4882a593Smuzhiyun 	return err;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun 
rpl_input(struct sk_buff * skb)261*4882a593Smuzhiyun static int rpl_input(struct sk_buff *skb)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun 	struct dst_entry *orig_dst = skb_dst(skb);
264*4882a593Smuzhiyun 	struct dst_entry *dst = NULL;
265*4882a593Smuzhiyun 	struct rpl_lwt *rlwt;
266*4882a593Smuzhiyun 	int err;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	rlwt = rpl_lwt_lwtunnel(orig_dst->lwtstate);
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	err = rpl_do_srh(skb, rlwt);
271*4882a593Smuzhiyun 	if (unlikely(err)) {
272*4882a593Smuzhiyun 		kfree_skb(skb);
273*4882a593Smuzhiyun 		return err;
274*4882a593Smuzhiyun 	}
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	preempt_disable();
277*4882a593Smuzhiyun 	dst = dst_cache_get(&rlwt->cache);
278*4882a593Smuzhiyun 	preempt_enable();
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	skb_dst_drop(skb);
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	if (!dst) {
283*4882a593Smuzhiyun 		ip6_route_input(skb);
284*4882a593Smuzhiyun 		dst = skb_dst(skb);
285*4882a593Smuzhiyun 		if (!dst->error) {
286*4882a593Smuzhiyun 			preempt_disable();
287*4882a593Smuzhiyun 			dst_cache_set_ip6(&rlwt->cache, dst,
288*4882a593Smuzhiyun 					  &ipv6_hdr(skb)->saddr);
289*4882a593Smuzhiyun 			preempt_enable();
290*4882a593Smuzhiyun 		}
291*4882a593Smuzhiyun 	} else {
292*4882a593Smuzhiyun 		skb_dst_set(skb, dst);
293*4882a593Smuzhiyun 	}
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
296*4882a593Smuzhiyun 	if (unlikely(err))
297*4882a593Smuzhiyun 		return err;
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	return dst_input(skb);
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun 
nla_put_rpl_srh(struct sk_buff * skb,int attrtype,struct rpl_iptunnel_encap * tuninfo)302*4882a593Smuzhiyun static int nla_put_rpl_srh(struct sk_buff *skb, int attrtype,
303*4882a593Smuzhiyun 			   struct rpl_iptunnel_encap *tuninfo)
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun 	struct rpl_iptunnel_encap *data;
306*4882a593Smuzhiyun 	struct nlattr *nla;
307*4882a593Smuzhiyun 	int len;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	len = RPL_IPTUNNEL_SRH_SIZE(tuninfo->srh);
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	nla = nla_reserve(skb, attrtype, len);
312*4882a593Smuzhiyun 	if (!nla)
313*4882a593Smuzhiyun 		return -EMSGSIZE;
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 	data = nla_data(nla);
316*4882a593Smuzhiyun 	memcpy(data, tuninfo->srh, len);
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	return 0;
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun 
rpl_fill_encap_info(struct sk_buff * skb,struct lwtunnel_state * lwtstate)321*4882a593Smuzhiyun static int rpl_fill_encap_info(struct sk_buff *skb,
322*4882a593Smuzhiyun 			       struct lwtunnel_state *lwtstate)
323*4882a593Smuzhiyun {
324*4882a593Smuzhiyun 	struct rpl_iptunnel_encap *tuninfo = rpl_encap_lwtunnel(lwtstate);
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	if (nla_put_rpl_srh(skb, RPL_IPTUNNEL_SRH, tuninfo))
327*4882a593Smuzhiyun 		return -EMSGSIZE;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	return 0;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun 
rpl_encap_nlsize(struct lwtunnel_state * lwtstate)332*4882a593Smuzhiyun static int rpl_encap_nlsize(struct lwtunnel_state *lwtstate)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun 	struct rpl_iptunnel_encap *tuninfo = rpl_encap_lwtunnel(lwtstate);
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	return nla_total_size(RPL_IPTUNNEL_SRH_SIZE(tuninfo->srh));
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun 
rpl_encap_cmp(struct lwtunnel_state * a,struct lwtunnel_state * b)339*4882a593Smuzhiyun static int rpl_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
340*4882a593Smuzhiyun {
341*4882a593Smuzhiyun 	struct rpl_iptunnel_encap *a_hdr = rpl_encap_lwtunnel(a);
342*4882a593Smuzhiyun 	struct rpl_iptunnel_encap *b_hdr = rpl_encap_lwtunnel(b);
343*4882a593Smuzhiyun 	int len = RPL_IPTUNNEL_SRH_SIZE(a_hdr->srh);
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	if (len != RPL_IPTUNNEL_SRH_SIZE(b_hdr->srh))
346*4882a593Smuzhiyun 		return 1;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	return memcmp(a_hdr, b_hdr, len);
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun static const struct lwtunnel_encap_ops rpl_ops = {
352*4882a593Smuzhiyun 	.build_state	= rpl_build_state,
353*4882a593Smuzhiyun 	.destroy_state	= rpl_destroy_state,
354*4882a593Smuzhiyun 	.output		= rpl_output,
355*4882a593Smuzhiyun 	.input		= rpl_input,
356*4882a593Smuzhiyun 	.fill_encap	= rpl_fill_encap_info,
357*4882a593Smuzhiyun 	.get_encap_size	= rpl_encap_nlsize,
358*4882a593Smuzhiyun 	.cmp_encap	= rpl_encap_cmp,
359*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
360*4882a593Smuzhiyun };
361*4882a593Smuzhiyun 
rpl_init(void)362*4882a593Smuzhiyun int __init rpl_init(void)
363*4882a593Smuzhiyun {
364*4882a593Smuzhiyun 	int err;
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	err = lwtunnel_encap_add_ops(&rpl_ops, LWTUNNEL_ENCAP_RPL);
367*4882a593Smuzhiyun 	if (err)
368*4882a593Smuzhiyun 		goto out;
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	pr_info("RPL Segment Routing with IPv6\n");
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	return 0;
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun out:
375*4882a593Smuzhiyun 	return err;
376*4882a593Smuzhiyun }
377*4882a593Smuzhiyun 
rpl_exit(void)378*4882a593Smuzhiyun void rpl_exit(void)
379*4882a593Smuzhiyun {
380*4882a593Smuzhiyun 	lwtunnel_encap_del_ops(&rpl_ops, LWTUNNEL_ENCAP_RPL);
381*4882a593Smuzhiyun }
382