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