18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/** 38c2ecf20Sopenharmony_ci * Authors: 48c2ecf20Sopenharmony_ci * (C) 2020 Alexander Aring <alex.aring@gmail.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/rpl_iptunnel.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <net/dst_cache.h> 108c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 118c2ecf20Sopenharmony_ci#include <net/lwtunnel.h> 128c2ecf20Sopenharmony_ci#include <net/ipv6.h> 138c2ecf20Sopenharmony_ci#include <net/rpl.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistruct rpl_iptunnel_encap { 168c2ecf20Sopenharmony_ci struct ipv6_rpl_sr_hdr srh[0]; 178c2ecf20Sopenharmony_ci}; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct rpl_lwt { 208c2ecf20Sopenharmony_ci struct dst_cache cache; 218c2ecf20Sopenharmony_ci struct rpl_iptunnel_encap tuninfo; 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic inline struct rpl_lwt *rpl_lwt_lwtunnel(struct lwtunnel_state *lwt) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci return (struct rpl_lwt *)lwt->data; 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic inline struct rpl_iptunnel_encap * 308c2ecf20Sopenharmony_cirpl_encap_lwtunnel(struct lwtunnel_state *lwt) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci return &rpl_lwt_lwtunnel(lwt)->tuninfo; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const struct nla_policy rpl_iptunnel_policy[RPL_IPTUNNEL_MAX + 1] = { 368c2ecf20Sopenharmony_ci [RPL_IPTUNNEL_SRH] = { .type = NLA_BINARY }, 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic bool rpl_validate_srh(struct net *net, struct ipv6_rpl_sr_hdr *srh, 408c2ecf20Sopenharmony_ci size_t seglen) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci int err; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if ((srh->hdrlen << 3) != seglen) 458c2ecf20Sopenharmony_ci return false; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* check at least one segment and seglen fit with segments_left */ 488c2ecf20Sopenharmony_ci if (!srh->segments_left || 498c2ecf20Sopenharmony_ci (srh->segments_left * sizeof(struct in6_addr)) != seglen) 508c2ecf20Sopenharmony_ci return false; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (srh->cmpri || srh->cmpre) 538c2ecf20Sopenharmony_ci return false; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci err = ipv6_chk_rpl_srh_loop(net, srh->rpl_segaddr, 568c2ecf20Sopenharmony_ci srh->segments_left); 578c2ecf20Sopenharmony_ci if (err) 588c2ecf20Sopenharmony_ci return false; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (ipv6_addr_type(&srh->rpl_segaddr[srh->segments_left - 1]) & 618c2ecf20Sopenharmony_ci IPV6_ADDR_MULTICAST) 628c2ecf20Sopenharmony_ci return false; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return true; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int rpl_build_state(struct net *net, struct nlattr *nla, 688c2ecf20Sopenharmony_ci unsigned int family, const void *cfg, 698c2ecf20Sopenharmony_ci struct lwtunnel_state **ts, 708c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct nlattr *tb[RPL_IPTUNNEL_MAX + 1]; 738c2ecf20Sopenharmony_ci struct lwtunnel_state *newts; 748c2ecf20Sopenharmony_ci struct ipv6_rpl_sr_hdr *srh; 758c2ecf20Sopenharmony_ci struct rpl_lwt *rlwt; 768c2ecf20Sopenharmony_ci int err, srh_len; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (family != AF_INET6) 798c2ecf20Sopenharmony_ci return -EINVAL; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci err = nla_parse_nested(tb, RPL_IPTUNNEL_MAX, nla, 828c2ecf20Sopenharmony_ci rpl_iptunnel_policy, extack); 838c2ecf20Sopenharmony_ci if (err < 0) 848c2ecf20Sopenharmony_ci return err; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!tb[RPL_IPTUNNEL_SRH]) 878c2ecf20Sopenharmony_ci return -EINVAL; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci srh = nla_data(tb[RPL_IPTUNNEL_SRH]); 908c2ecf20Sopenharmony_ci srh_len = nla_len(tb[RPL_IPTUNNEL_SRH]); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (srh_len < sizeof(*srh)) 938c2ecf20Sopenharmony_ci return -EINVAL; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* verify that SRH is consistent */ 968c2ecf20Sopenharmony_ci if (!rpl_validate_srh(net, srh, srh_len - sizeof(*srh))) 978c2ecf20Sopenharmony_ci return -EINVAL; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci newts = lwtunnel_state_alloc(srh_len + sizeof(*rlwt)); 1008c2ecf20Sopenharmony_ci if (!newts) 1018c2ecf20Sopenharmony_ci return -ENOMEM; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci rlwt = rpl_lwt_lwtunnel(newts); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci err = dst_cache_init(&rlwt->cache, GFP_ATOMIC); 1068c2ecf20Sopenharmony_ci if (err) { 1078c2ecf20Sopenharmony_ci kfree(newts); 1088c2ecf20Sopenharmony_ci return err; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci memcpy(&rlwt->tuninfo.srh, srh, srh_len); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci newts->type = LWTUNNEL_ENCAP_RPL; 1148c2ecf20Sopenharmony_ci newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT; 1158c2ecf20Sopenharmony_ci newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci *ts = newts; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void rpl_destroy_state(struct lwtunnel_state *lwt) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci dst_cache_destroy(&rpl_lwt_lwtunnel(lwt)->cache); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int rpl_do_srh_inline(struct sk_buff *skb, const struct rpl_lwt *rlwt, 1288c2ecf20Sopenharmony_ci const struct ipv6_rpl_sr_hdr *srh) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct ipv6_rpl_sr_hdr *isrh, *csrh; 1318c2ecf20Sopenharmony_ci const struct ipv6hdr *oldhdr; 1328c2ecf20Sopenharmony_ci struct ipv6hdr *hdr; 1338c2ecf20Sopenharmony_ci unsigned char *buf; 1348c2ecf20Sopenharmony_ci size_t hdrlen; 1358c2ecf20Sopenharmony_ci int err; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci oldhdr = ipv6_hdr(skb); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci buf = kcalloc(struct_size(srh, segments.addr, srh->segments_left), 2, GFP_ATOMIC); 1408c2ecf20Sopenharmony_ci if (!buf) 1418c2ecf20Sopenharmony_ci return -ENOMEM; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci isrh = (struct ipv6_rpl_sr_hdr *)buf; 1448c2ecf20Sopenharmony_ci csrh = (struct ipv6_rpl_sr_hdr *)(buf + ((srh->hdrlen + 1) << 3)); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci memcpy(isrh, srh, sizeof(*isrh)); 1478c2ecf20Sopenharmony_ci memcpy(isrh->rpl_segaddr, &srh->rpl_segaddr[1], 1488c2ecf20Sopenharmony_ci (srh->segments_left - 1) * 16); 1498c2ecf20Sopenharmony_ci isrh->rpl_segaddr[srh->segments_left - 1] = oldhdr->daddr; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ipv6_rpl_srh_compress(csrh, isrh, &srh->rpl_segaddr[0], 1528c2ecf20Sopenharmony_ci isrh->segments_left - 1); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci hdrlen = ((csrh->hdrlen + 1) << 3); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci err = skb_cow_head(skb, hdrlen + skb->mac_len); 1578c2ecf20Sopenharmony_ci if (unlikely(err)) { 1588c2ecf20Sopenharmony_ci kfree(buf); 1598c2ecf20Sopenharmony_ci return err; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(struct ipv6hdr)); 1638c2ecf20Sopenharmony_ci skb_postpull_rcsum(skb, skb_network_header(skb), 1648c2ecf20Sopenharmony_ci sizeof(struct ipv6hdr)); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci skb_push(skb, sizeof(struct ipv6hdr) + hdrlen); 1678c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 1688c2ecf20Sopenharmony_ci skb_mac_header_rebuild(skb); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci hdr = ipv6_hdr(skb); 1718c2ecf20Sopenharmony_ci memmove(hdr, oldhdr, sizeof(*hdr)); 1728c2ecf20Sopenharmony_ci isrh = (void *)hdr + sizeof(*hdr); 1738c2ecf20Sopenharmony_ci memcpy(isrh, csrh, hdrlen); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci isrh->nexthdr = hdr->nexthdr; 1768c2ecf20Sopenharmony_ci hdr->nexthdr = NEXTHDR_ROUTING; 1778c2ecf20Sopenharmony_ci hdr->daddr = srh->rpl_segaddr[0]; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 1808c2ecf20Sopenharmony_ci skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci skb_postpush_rcsum(skb, hdr, sizeof(struct ipv6hdr) + hdrlen); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci kfree(buf); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int rpl_do_srh(struct sk_buff *skb, const struct rpl_lwt *rlwt) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 1928c2ecf20Sopenharmony_ci struct rpl_iptunnel_encap *tinfo; 1938c2ecf20Sopenharmony_ci int err = 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (skb->protocol != htons(ETH_P_IPV6)) 1968c2ecf20Sopenharmony_ci return -EINVAL; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci tinfo = rpl_encap_lwtunnel(dst->lwtstate); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci err = rpl_do_srh_inline(skb, rlwt, tinfo->srh); 2018c2ecf20Sopenharmony_ci if (err) 2028c2ecf20Sopenharmony_ci return err; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int rpl_output(struct net *net, struct sock *sk, struct sk_buff *skb) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct dst_entry *orig_dst = skb_dst(skb); 2108c2ecf20Sopenharmony_ci struct dst_entry *dst = NULL; 2118c2ecf20Sopenharmony_ci struct rpl_lwt *rlwt; 2128c2ecf20Sopenharmony_ci int err; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci rlwt = rpl_lwt_lwtunnel(orig_dst->lwtstate); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci err = rpl_do_srh(skb, rlwt); 2178c2ecf20Sopenharmony_ci if (unlikely(err)) 2188c2ecf20Sopenharmony_ci goto drop; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci preempt_disable(); 2218c2ecf20Sopenharmony_ci dst = dst_cache_get(&rlwt->cache); 2228c2ecf20Sopenharmony_ci preempt_enable(); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (unlikely(!dst)) { 2258c2ecf20Sopenharmony_ci struct ipv6hdr *hdr = ipv6_hdr(skb); 2268c2ecf20Sopenharmony_ci struct flowi6 fl6; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 2298c2ecf20Sopenharmony_ci fl6.daddr = hdr->daddr; 2308c2ecf20Sopenharmony_ci fl6.saddr = hdr->saddr; 2318c2ecf20Sopenharmony_ci fl6.flowlabel = ip6_flowinfo(hdr); 2328c2ecf20Sopenharmony_ci fl6.flowi6_mark = skb->mark; 2338c2ecf20Sopenharmony_ci fl6.flowi6_proto = hdr->nexthdr; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci dst = ip6_route_output(net, NULL, &fl6); 2368c2ecf20Sopenharmony_ci if (dst->error) { 2378c2ecf20Sopenharmony_ci err = dst->error; 2388c2ecf20Sopenharmony_ci dst_release(dst); 2398c2ecf20Sopenharmony_ci goto drop; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci preempt_disable(); 2438c2ecf20Sopenharmony_ci dst_cache_set_ip6(&rlwt->cache, dst, &fl6.saddr); 2448c2ecf20Sopenharmony_ci preempt_enable(); 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci skb_dst_drop(skb); 2488c2ecf20Sopenharmony_ci skb_dst_set(skb, dst); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); 2518c2ecf20Sopenharmony_ci if (unlikely(err)) 2528c2ecf20Sopenharmony_ci goto drop; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return dst_output(net, sk, skb); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cidrop: 2578c2ecf20Sopenharmony_ci kfree_skb(skb); 2588c2ecf20Sopenharmony_ci return err; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int rpl_input(struct sk_buff *skb) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct dst_entry *orig_dst = skb_dst(skb); 2648c2ecf20Sopenharmony_ci struct dst_entry *dst = NULL; 2658c2ecf20Sopenharmony_ci struct rpl_lwt *rlwt; 2668c2ecf20Sopenharmony_ci int err; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci rlwt = rpl_lwt_lwtunnel(orig_dst->lwtstate); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci err = rpl_do_srh(skb, rlwt); 2718c2ecf20Sopenharmony_ci if (unlikely(err)) { 2728c2ecf20Sopenharmony_ci kfree_skb(skb); 2738c2ecf20Sopenharmony_ci return err; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci preempt_disable(); 2778c2ecf20Sopenharmony_ci dst = dst_cache_get(&rlwt->cache); 2788c2ecf20Sopenharmony_ci preempt_enable(); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci skb_dst_drop(skb); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (!dst) { 2838c2ecf20Sopenharmony_ci ip6_route_input(skb); 2848c2ecf20Sopenharmony_ci dst = skb_dst(skb); 2858c2ecf20Sopenharmony_ci if (!dst->error) { 2868c2ecf20Sopenharmony_ci preempt_disable(); 2878c2ecf20Sopenharmony_ci dst_cache_set_ip6(&rlwt->cache, dst, 2888c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->saddr); 2898c2ecf20Sopenharmony_ci preempt_enable(); 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci } else { 2928c2ecf20Sopenharmony_ci skb_dst_set(skb, dst); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); 2968c2ecf20Sopenharmony_ci if (unlikely(err)) 2978c2ecf20Sopenharmony_ci return err; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return dst_input(skb); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int nla_put_rpl_srh(struct sk_buff *skb, int attrtype, 3038c2ecf20Sopenharmony_ci struct rpl_iptunnel_encap *tuninfo) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct rpl_iptunnel_encap *data; 3068c2ecf20Sopenharmony_ci struct nlattr *nla; 3078c2ecf20Sopenharmony_ci int len; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci len = RPL_IPTUNNEL_SRH_SIZE(tuninfo->srh); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci nla = nla_reserve(skb, attrtype, len); 3128c2ecf20Sopenharmony_ci if (!nla) 3138c2ecf20Sopenharmony_ci return -EMSGSIZE; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci data = nla_data(nla); 3168c2ecf20Sopenharmony_ci memcpy(data, tuninfo->srh, len); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int rpl_fill_encap_info(struct sk_buff *skb, 3228c2ecf20Sopenharmony_ci struct lwtunnel_state *lwtstate) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct rpl_iptunnel_encap *tuninfo = rpl_encap_lwtunnel(lwtstate); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (nla_put_rpl_srh(skb, RPL_IPTUNNEL_SRH, tuninfo)) 3278c2ecf20Sopenharmony_ci return -EMSGSIZE; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int rpl_encap_nlsize(struct lwtunnel_state *lwtstate) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct rpl_iptunnel_encap *tuninfo = rpl_encap_lwtunnel(lwtstate); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return nla_total_size(RPL_IPTUNNEL_SRH_SIZE(tuninfo->srh)); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int rpl_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct rpl_iptunnel_encap *a_hdr = rpl_encap_lwtunnel(a); 3428c2ecf20Sopenharmony_ci struct rpl_iptunnel_encap *b_hdr = rpl_encap_lwtunnel(b); 3438c2ecf20Sopenharmony_ci int len = RPL_IPTUNNEL_SRH_SIZE(a_hdr->srh); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (len != RPL_IPTUNNEL_SRH_SIZE(b_hdr->srh)) 3468c2ecf20Sopenharmony_ci return 1; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return memcmp(a_hdr, b_hdr, len); 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic const struct lwtunnel_encap_ops rpl_ops = { 3528c2ecf20Sopenharmony_ci .build_state = rpl_build_state, 3538c2ecf20Sopenharmony_ci .destroy_state = rpl_destroy_state, 3548c2ecf20Sopenharmony_ci .output = rpl_output, 3558c2ecf20Sopenharmony_ci .input = rpl_input, 3568c2ecf20Sopenharmony_ci .fill_encap = rpl_fill_encap_info, 3578c2ecf20Sopenharmony_ci .get_encap_size = rpl_encap_nlsize, 3588c2ecf20Sopenharmony_ci .cmp_encap = rpl_encap_cmp, 3598c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3608c2ecf20Sopenharmony_ci}; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ciint __init rpl_init(void) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci int err; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci err = lwtunnel_encap_add_ops(&rpl_ops, LWTUNNEL_ENCAP_RPL); 3678c2ecf20Sopenharmony_ci if (err) 3688c2ecf20Sopenharmony_ci goto out; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci pr_info("RPL Segment Routing with IPv6\n"); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ciout: 3758c2ecf20Sopenharmony_ci return err; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_civoid rpl_exit(void) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci lwtunnel_encap_del_ops(&rpl_ops, LWTUNNEL_ENCAP_RPL); 3818c2ecf20Sopenharmony_ci} 382