FILE
seg6_iptunnel.c
File preview with syntax highlighting.
linux-6.19.14/net/ipv6/seg6_iptunnel.c
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * SR-IPv6 implementation 4 * 5 * Author: 6 * David Lebrun <david.lebrun@uclouvain.be> 7 */ 8 9#include <linux/types.h> 10#include <linux/skbuff.h> 11#include <linux/net.h> 12#include <linux/module.h> 13#include <net/ip.h> 14#include <net/ip_tunnels.h> 15#include <net/lwtunnel.h> 16#include <net/netevent.h> 17#include <net/netns/generic.h> 18#include <net/ip6_fib.h> 19#include <net/route.h> 20#include <net/seg6.h> 21#include <linux/seg6.h> 22#include <linux/seg6_iptunnel.h> 23#include <net/addrconf.h> 24#include <net/ip6_route.h> 25#include <net/dst_cache.h> 26#ifdef CONFIG_IPV6_SEG6_HMAC 27#include <net/seg6_hmac.h> 28#endif 29#include <linux/netfilter.h> 30 31static size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo) 32{ 33 int head = 0; 34 35 switch (tuninfo->mode) { 36 case SEG6_IPTUN_MODE_INLINE: 37 break; 38 case SEG6_IPTUN_MODE_ENCAP: 39 case SEG6_IPTUN_MODE_ENCAP_RED: 40 head = sizeof(struct ipv6hdr); 41 break; 42 case SEG6_IPTUN_MODE_L2ENCAP: 43 case SEG6_IPTUN_MODE_L2ENCAP_RED: 44 return 0; 45 } 46 47 return ((tuninfo->srh->hdrlen + 1) << 3) + head; 48} 49 50struct seg6_lwt { 51 struct dst_cache cache_input; 52 struct dst_cache cache_output; 53 struct seg6_iptunnel_encap tuninfo[]; 54}; 55 56static inline struct seg6_lwt *seg6_lwt_lwtunnel(struct lwtunnel_state *lwt) 57{ 58 return (struct seg6_lwt *)lwt->data; 59} 60 61static inline struct seg6_iptunnel_encap * 62seg6_encap_lwtunnel(struct lwtunnel_state *lwt) 63{ 64 return seg6_lwt_lwtunnel(lwt)->tuninfo; 65} 66 67static const struct nla_policy seg6_iptunnel_policy[SEG6_IPTUNNEL_MAX + 1] = { 68 [SEG6_IPTUNNEL_SRH] = { .type = NLA_BINARY }, 69}; 70 71static int nla_put_srh(struct sk_buff *skb, int attrtype, 72 struct seg6_iptunnel_encap *tuninfo) 73{ 74 struct seg6_iptunnel_encap *data; 75 struct nlattr *nla; 76 int len; 77 78 len = SEG6_IPTUN_ENCAP_SIZE(tuninfo); 79 80 nla = nla_reserve(skb, attrtype, len); 81 if (!nla) 82 return -EMSGSIZE; 83 84 data = nla_data(nla); 85 memcpy(data, tuninfo, len); 86 87 return 0; 88} 89 90static void set_tun_src(struct net *net, struct net_device *dev, 91 struct in6_addr *daddr, struct in6_addr *saddr) 92{ 93 struct seg6_pernet_data *sdata = seg6_pernet(net); 94 struct in6_addr *tun_src; 95 96 rcu_read_lock(); 97 98 tun_src = rcu_dereference(sdata->tun_src); 99 100 if (!ipv6_addr_any(tun_src)) { 101 memcpy(saddr, tun_src, sizeof(struct in6_addr)); 102 } else { 103 ipv6_dev_get_saddr(net, dev, daddr, IPV6_PREFER_SRC_PUBLIC, 104 saddr); 105 } 106 107 rcu_read_unlock(); 108} 109 110/* Compute flowlabel for outer IPv6 header */ 111static __be32 seg6_make_flowlabel(struct net *net, struct sk_buff *skb, 112 struct ipv6hdr *inner_hdr) 113{ 114 int do_flowlabel = net->ipv6.sysctl.seg6_flowlabel; 115 __be32 flowlabel = 0; 116 u32 hash; 117 118 if (do_flowlabel > 0) { 119 hash = skb_get_hash(skb); 120 hash = rol32(hash, 16); 121 flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK; 122 } else if (!do_flowlabel && skb->protocol == htons(ETH_P_IPV6)) { 123 flowlabel = ip6_flowlabel(inner_hdr); 124 } 125 return flowlabel; 126} 127 128static int __seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, 129 int proto, struct dst_entry *cache_dst) 130{ 131 struct dst_entry *dst = skb_dst(skb); 132 struct net_device *dev = dst_dev(dst); 133 struct net *net = dev_net(dev); 134 struct ipv6hdr *hdr, *inner_hdr; 135 struct ipv6_sr_hdr *isrh; 136 int hdrlen, tot_len, err; 137 __be32 flowlabel; 138 139 hdrlen = (osrh->hdrlen + 1) << 3; 140 tot_len = hdrlen + sizeof(*hdr); 141 142 err = skb_cow_head(skb, tot_len + dst_dev_overhead(cache_dst, skb)); 143 if (unlikely(err)) 144 return err; 145 146 inner_hdr = ipv6_hdr(skb); 147 flowlabel = seg6_make_flowlabel(net, skb, inner_hdr); 148 149 skb_push(skb, tot_len); 150 skb_reset_network_header(skb); 151 skb_mac_header_rebuild(skb); 152 hdr = ipv6_hdr(skb); 153 154 /* inherit tc, flowlabel and hlim 155 * hlim will be decremented in ip6_forward() afterwards and 156 * decapsulation will overwrite inner hlim with outer hlim 157 */ 158 159 if (skb->protocol == htons(ETH_P_IPV6)) { 160 ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)), 161 flowlabel); 162 hdr->hop_limit = inner_hdr->hop_limit; 163 } else { 164 ip6_flow_hdr(hdr, 0, flowlabel); 165 hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb)); 166 167 memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); 168 169 /* the control block has been erased, so we have to set the 170 * iif once again. 171 * We read the receiving interface index directly from the 172 * skb->skb_iif as it is done in the IPv4 receiving path (i.e.: 173 * ip_rcv_core(...)). 174 */ 175 IP6CB(skb)->iif = skb->skb_iif; 176 } 177 178 hdr->nexthdr = NEXTHDR_ROUTING; 179 180 isrh = (void *)hdr + sizeof(*hdr); 181 memcpy(isrh, osrh, hdrlen); 182 183 isrh->nexthdr = proto; 184 185 hdr->daddr = isrh->segments[isrh->first_segment]; 186 set_tun_src(net, dev, &hdr->daddr, &hdr->saddr); 187 188#ifdef CONFIG_IPV6_SEG6_HMAC 189 if (sr_has_hmac(isrh)) { 190 err = seg6_push_hmac(net, &hdr->saddr, isrh); 191 if (unlikely(err)) 192 return err; 193 } 194#endif 195 196 hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 197 198 skb_postpush_rcsum(skb, hdr, tot_len); 199 200 return 0; 201} 202 203/* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */ 204int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto) 205{ 206 return __seg6_do_srh_encap(skb, osrh, proto, NULL); 207} 208EXPORT_SYMBOL_GPL(seg6_do_srh_encap); 209 210/* encapsulate an IPv6 packet within an outer IPv6 header with reduced SRH */ 211static int seg6_do_srh_encap_red(struct sk_buff *skb, 212 struct ipv6_sr_hdr *osrh, int proto, 213 struct dst_entry *cache_dst) 214{ 215 __u8 first_seg = osrh->first_segment; 216 struct dst_entry *dst = skb_dst(skb); 217 struct net_device *dev = dst_dev(dst); 218 struct net *net = dev_net(dev); 219 struct ipv6hdr *hdr, *inner_hdr; 220 int hdrlen = ipv6_optlen(osrh); 221 int red_tlv_offset, tlv_offset; 222 struct ipv6_sr_hdr *isrh; 223 bool skip_srh = false; 224 __be32 flowlabel; 225 int tot_len, err; 226 int red_hdrlen; 227 int tlvs_len; 228 229 if (first_seg > 0) { 230 red_hdrlen = hdrlen - sizeof(struct in6_addr); 231 } else { 232 /* NOTE: if tag/flags and/or other TLVs are introduced in the 233 * seg6_iptunnel infrastructure, they should be considered when 234 * deciding to skip the SRH. 235 */ 236 skip_srh = !sr_has_hmac(osrh); 237 238 red_hdrlen = skip_srh ? 0 : hdrlen; 239 } 240 241 tot_len = red_hdrlen + sizeof(struct ipv6hdr); 242 243 err = skb_cow_head(skb, tot_len + dst_dev_overhead(cache_dst, skb)); 244 if (unlikely(err)) 245 return err; 246 247 inner_hdr = ipv6_hdr(skb); 248 flowlabel = seg6_make_flowlabel(net, skb, inner_hdr); 249 250 skb_push(skb, tot_len); 251 skb_reset_network_header(skb); 252 skb_mac_header_rebuild(skb); 253 hdr = ipv6_hdr(skb); 254 255 /* based on seg6_do_srh_encap() */ 256 if (skb->protocol == htons(ETH_P_IPV6)) { 257 ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)), 258 flowlabel); 259 hdr->hop_limit = inner_hdr->hop_limit; 260 } else { 261 ip6_flow_hdr(hdr, 0, flowlabel); 262 hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb)); 263 264 memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); 265 IP6CB(skb)->iif = skb->skb_iif; 266 } 267 268 /* no matter if we have to skip the SRH or not, the first segment 269 * always comes in the pushed IPv6 header. 270 */ 271 hdr->daddr = osrh->segments[first_seg]; 272 273 if (skip_srh) { 274 hdr->nexthdr = proto; 275 276 set_tun_src(net, dev, &hdr->daddr, &hdr->saddr); 277 goto out; 278 } 279 280 /* we cannot skip the SRH, slow path */ 281 282 hdr->nexthdr = NEXTHDR_ROUTING; 283 isrh = (void *)hdr + sizeof(struct ipv6hdr); 284 285 if (unlikely(!first_seg)) { 286 /* this is a very rare case; we have only one SID but 287 * we cannot skip the SRH since we are carrying some 288 * other info. 289 */ 290 memcpy(isrh, osrh, hdrlen); 291 goto srcaddr; 292 } 293 294 tlv_offset = sizeof(*osrh) + (first_seg + 1) * sizeof(struct in6_addr); 295 red_tlv_offset = tlv_offset - sizeof(struct in6_addr); 296 297 memcpy(isrh, osrh, red_tlv_offset); 298 299 tlvs_len = hdrlen - tlv_offset; 300 if (unlikely(tlvs_len > 0)) { 301 const void *s = (const void *)osrh + tlv_offset; 302 void *d = (void *)isrh + red_tlv_offset; 303 304 memcpy(d, s, tlvs_len); 305 } 306 307 --isrh->first_segment; 308 isrh->hdrlen -= 2; 309 310srcaddr: 311 isrh->nexthdr = proto; 312 set_tun_src(net, dev, &hdr->daddr, &hdr->saddr); 313 314#ifdef CONFIG_IPV6_SEG6_HMAC 315 if (unlikely(!skip_srh && sr_has_hmac(isrh))) { 316 err = seg6_push_hmac(net, &hdr->saddr, isrh); 317 if (unlikely(err)) 318 return err; 319 } 320#endif 321 322out: 323 hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 324 325 skb_postpush_rcsum(skb, hdr, tot_len); 326 327 return 0; 328} 329 330static int __seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, 331 struct dst_entry *cache_dst) 332{ 333 struct ipv6hdr *hdr, *oldhdr; 334 struct ipv6_sr_hdr *isrh; 335 int hdrlen, err; 336 337 hdrlen = (osrh->hdrlen + 1) << 3; 338 339 err = skb_cow_head(skb, hdrlen + dst_dev_overhead(cache_dst, skb)); 340 if (unlikely(err)) 341 return err; 342 343 oldhdr = ipv6_hdr(skb); 344 345 skb_pull(skb, sizeof(struct ipv6hdr)); 346 skb_postpull_rcsum(skb, skb_network_header(skb), 347 sizeof(struct ipv6hdr)); 348 349 skb_push(skb, sizeof(struct ipv6hdr) + hdrlen); 350 skb_reset_network_header(skb); 351 skb_mac_header_rebuild(skb); 352 353 hdr = ipv6_hdr(skb); 354 355 memmove(hdr, oldhdr, sizeof(*hdr)); 356 357 isrh = (void *)hdr + sizeof(*hdr); 358 memcpy(isrh, osrh, hdrlen); 359 360 isrh->nexthdr = hdr->nexthdr; 361 hdr->nexthdr = NEXTHDR_ROUTING; 362 363 isrh->segments[0] = hdr->daddr; 364 hdr->daddr = isrh->segments[isrh->first_segment]; 365 366#ifdef CONFIG_IPV6_SEG6_HMAC 367 if (sr_has_hmac(isrh)) { 368 struct net *net = skb_dst_dev_net(skb); 369 370 err = seg6_push_hmac(net, &hdr->saddr, isrh); 371 if (unlikely(err)) 372 return err; 373 } 374#endif 375 376 hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 377 378 skb_postpush_rcsum(skb, hdr, sizeof(struct ipv6hdr) + hdrlen); 379 380 return 0; 381} 382 383static int seg6_do_srh(struct sk_buff *skb, struct dst_entry *cache_dst) 384{ 385 struct dst_entry *dst = skb_dst(skb); 386 struct seg6_iptunnel_encap *tinfo; 387 int proto, err = 0; 388 389 tinfo = seg6_encap_lwtunnel(dst->lwtstate); 390 391 switch (tinfo->mode) { 392 case SEG6_IPTUN_MODE_INLINE: 393 if (skb->protocol != htons(ETH_P_IPV6)) 394 return -EINVAL; 395 396 err = __seg6_do_srh_inline(skb, tinfo->srh, cache_dst); 397 if (err) 398 return err; 399 break; 400 case SEG6_IPTUN_MODE_ENCAP: 401 case SEG6_IPTUN_MODE_ENCAP_RED: 402 err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6); 403 if (err) 404 return err; 405 406 if (skb->protocol == htons(ETH_P_IPV6)) 407 proto = IPPROTO_IPV6; 408 else if (skb->protocol == htons(ETH_P_IP)) 409 proto = IPPROTO_IPIP; 410 else 411 return -EINVAL; 412 413 if (tinfo->mode == SEG6_IPTUN_MODE_ENCAP) 414 err = __seg6_do_srh_encap(skb, tinfo->srh, 415 proto, cache_dst); 416 else 417 err = seg6_do_srh_encap_red(skb, tinfo->srh, 418 proto, cache_dst); 419 420 if (err) 421 return err; 422 423 skb_set_inner_transport_header(skb, skb_transport_offset(skb)); 424 skb_set_inner_protocol(skb, skb->protocol); 425 skb->protocol = htons(ETH_P_IPV6); 426 break; 427 case SEG6_IPTUN_MODE_L2ENCAP: 428 case SEG6_IPTUN_MODE_L2ENCAP_RED: 429 if (!skb_mac_header_was_set(skb)) 430 return -EINVAL; 431 432 if (pskb_expand_head(skb, skb->mac_len, 0, GFP_ATOMIC) < 0) 433 return -ENOMEM; 434 435 skb_mac_header_rebuild(skb); 436 skb_push(skb, skb->mac_len); 437 438 if (tinfo->mode == SEG6_IPTUN_MODE_L2ENCAP) 439 err = __seg6_do_srh_encap(skb, tinfo->srh, 440 IPPROTO_ETHERNET, 441 cache_dst); 442 else 443 err = seg6_do_srh_encap_red(skb, tinfo->srh, 444 IPPROTO_ETHERNET, 445 cache_dst); 446 447 if (err) 448 return err; 449 450 skb->protocol = htons(ETH_P_IPV6); 451 break; 452 } 453 454 skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 455 nf_reset_ct(skb); 456 457 return 0; 458} 459 460/* insert an SRH within an IPv6 packet, just after the IPv6 header */ 461int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) 462{ 463 return __seg6_do_srh_inline(skb, osrh, NULL); 464} 465EXPORT_SYMBOL_GPL(seg6_do_srh_inline); 466 467static int seg6_input_finish(struct net *net, struct sock *sk, 468 struct sk_buff *skb) 469{ 470 return dst_input(skb); 471} 472 473static int seg6_input_core(struct net *net, struct sock *sk, 474 struct sk_buff *skb) 475{ 476 struct dst_entry *orig_dst = skb_dst(skb); 477 struct dst_entry *dst = NULL; 478 struct lwtunnel_state *lwtst; 479 struct seg6_lwt *slwt; 480 int err; 481 482 /* We cannot dereference "orig_dst" once ip6_route_input() or 483 * skb_dst_drop() is called. However, in order to detect a dst loop, we 484 * need the address of its lwtstate. So, save the address of lwtstate 485 * now and use it later as a comparison. 486 */ 487 lwtst = orig_dst->lwtstate; 488 489 slwt = seg6_lwt_lwtunnel(lwtst); 490 491 local_bh_disable(); 492 dst = dst_cache_get(&slwt->cache_input); 493 local_bh_enable(); 494 495 err = seg6_do_srh(skb, dst); 496 if (unlikely(err)) { 497 dst_release(dst); 498 goto drop; 499 } 500 501 if (!dst) { 502 ip6_route_input(skb); 503 dst = skb_dst(skb); 504 505 /* cache only if we don't create a dst reference loop */ 506 if (!dst->error && lwtst != dst->lwtstate) { 507 local_bh_disable(); 508 dst_cache_set_ip6(&slwt->cache_input, dst, 509 &ipv6_hdr(skb)->saddr); 510 local_bh_enable(); 511 } 512 513 err = skb_cow_head(skb, LL_RESERVED_SPACE(dst_dev(dst))); 514 if (unlikely(err)) 515 goto drop; 516 } else { 517 skb_dst_drop(skb); 518 skb_dst_set(skb, dst); 519 } 520 521 if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 522 return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, 523 dev_net(skb->dev), NULL, skb, NULL, 524 skb_dst_dev(skb), seg6_input_finish); 525 526 return seg6_input_finish(dev_net(skb->dev), NULL, skb); 527drop: 528 kfree_skb(skb); 529 return err; 530} 531 532static int seg6_input_nf(struct sk_buff *skb) 533{ 534 struct net_device *dev = skb_dst_dev(skb); 535 struct net *net = dev_net(skb->dev); 536 537 switch (skb->protocol) { 538 case htons(ETH_P_IP): 539 return NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, NULL, 540 skb, NULL, dev, seg6_input_core); 541 case htons(ETH_P_IPV6): 542 return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, NULL, 543 skb, NULL, dev, seg6_input_core); 544 } 545 546 return -EINVAL; 547} 548 549static int seg6_input(struct sk_buff *skb) 550{ 551 if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 552 return seg6_input_nf(skb); 553 554 return seg6_input_core(dev_net(skb->dev), NULL, skb); 555} 556 557static int seg6_output_core(struct net *net, struct sock *sk, 558 struct sk_buff *skb) 559{ 560 struct dst_entry *orig_dst = skb_dst(skb); 561 struct dst_entry *dst = NULL; 562 struct seg6_lwt *slwt; 563 int err; 564 565 slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); 566 567 local_bh_disable(); 568 dst = dst_cache_get(&slwt->cache_output); 569 local_bh_enable(); 570 571 err = seg6_do_srh(skb, dst); 572 if (unlikely(err)) 573 goto drop; 574 575 if (unlikely(!dst)) { 576 struct ipv6hdr *hdr = ipv6_hdr(skb); 577 struct flowi6 fl6; 578 579 memset(&fl6, 0, sizeof(fl6)); 580 fl6.daddr = hdr->daddr; 581 fl6.saddr = hdr->saddr; 582 fl6.flowlabel = ip6_flowinfo(hdr); 583 fl6.flowi6_mark = skb->mark; 584 fl6.flowi6_proto = hdr->nexthdr; 585 586 dst = ip6_route_output(net, NULL, &fl6); 587 if (dst->error) { 588 err = dst->error; 589 goto drop; 590 } 591 592 /* cache only if we don't create a dst reference loop */ 593 if (orig_dst->lwtstate != dst->lwtstate) { 594 local_bh_disable(); 595 dst_cache_set_ip6(&slwt->cache_output, dst, &fl6.saddr); 596 local_bh_enable(); 597 } 598 599 err = skb_cow_head(skb, LL_RESERVED_SPACE(dst_dev(dst))); 600 if (unlikely(err)) 601 goto drop; 602 } 603 604 skb_dst_drop(skb); 605 skb_dst_set(skb, dst); 606 607 if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 608 return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb, 609 NULL, dst_dev(dst), dst_output); 610 611 return dst_output(net, sk, skb); 612drop: 613 dst_release(dst); 614 kfree_skb(skb); 615 return err; 616} 617 618static int seg6_output_nf(struct net *net, struct sock *sk, struct sk_buff *skb) 619{ 620 struct net_device *dev = skb_dst_dev(skb); 621 622 switch (skb->protocol) { 623 case htons(ETH_P_IP): 624 return NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, sk, skb, 625 NULL, dev, seg6_output_core); 626 case htons(ETH_P_IPV6): 627 return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, sk, skb, 628 NULL, dev, seg6_output_core); 629 } 630 631 return -EINVAL; 632} 633 634static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb) 635{ 636 if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 637 return seg6_output_nf(net, sk, skb); 638 639 return seg6_output_core(net, sk, skb); 640} 641 642static int seg6_build_state(struct net *net, struct nlattr *nla, 643 unsigned int family, const void *cfg, 644 struct lwtunnel_state **ts, 645 struct netlink_ext_ack *extack) 646{ 647 struct nlattr *tb[SEG6_IPTUNNEL_MAX + 1]; 648 struct seg6_iptunnel_encap *tuninfo; 649 struct lwtunnel_state *newts; 650 int tuninfo_len, min_size; 651 struct seg6_lwt *slwt; 652 int err; 653 654 if (family != AF_INET && family != AF_INET6) 655 return -EINVAL; 656 657 err = nla_parse_nested_deprecated(tb, SEG6_IPTUNNEL_MAX, nla, 658 seg6_iptunnel_policy, extack); 659 660 if (err < 0) 661 return err; 662 663 if (!tb[SEG6_IPTUNNEL_SRH]) 664 return -EINVAL; 665 666 tuninfo = nla_data(tb[SEG6_IPTUNNEL_SRH]); 667 tuninfo_len = nla_len(tb[SEG6_IPTUNNEL_SRH]); 668 669 /* tuninfo must contain at least the iptunnel encap structure, 670 * the SRH and one segment 671 */ 672 min_size = sizeof(*tuninfo) + sizeof(struct ipv6_sr_hdr) + 673 sizeof(struct in6_addr); 674 if (tuninfo_len < min_size) 675 return -EINVAL; 676 677 switch (tuninfo->mode) { 678 case SEG6_IPTUN_MODE_INLINE: 679 if (family != AF_INET6) 680 return -EINVAL; 681 682 break; 683 case SEG6_IPTUN_MODE_ENCAP: 684 break; 685 case SEG6_IPTUN_MODE_L2ENCAP: 686 break; 687 case SEG6_IPTUN_MODE_ENCAP_RED: 688 break; 689 case SEG6_IPTUN_MODE_L2ENCAP_RED: 690 break; 691 default: 692 return -EINVAL; 693 } 694 695 /* verify that SRH is consistent */ 696 if (!seg6_validate_srh(tuninfo->srh, tuninfo_len - sizeof(*tuninfo), false)) 697 return -EINVAL; 698 699 newts = lwtunnel_state_alloc(tuninfo_len + sizeof(*slwt)); 700 if (!newts) 701 return -ENOMEM; 702 703 slwt = seg6_lwt_lwtunnel(newts); 704 705 err = dst_cache_init(&slwt->cache_input, GFP_ATOMIC); 706 if (err) 707 goto err_free_newts; 708 709 err = dst_cache_init(&slwt->cache_output, GFP_ATOMIC); 710 if (err) 711 goto err_destroy_input; 712 713 memcpy(&slwt->tuninfo, tuninfo, tuninfo_len); 714 715 newts->type = LWTUNNEL_ENCAP_SEG6; 716 newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT; 717 718 if (tuninfo->mode != SEG6_IPTUN_MODE_L2ENCAP) 719 newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT; 720 721 newts->headroom = seg6_lwt_headroom(tuninfo); 722 723 *ts = newts; 724 725 return 0; 726 727err_destroy_input: 728 dst_cache_destroy(&slwt->cache_input); 729err_free_newts: 730 kfree(newts); 731 return err; 732} 733 734static void seg6_destroy_state(struct lwtunnel_state *lwt) 735{ 736 struct seg6_lwt *slwt = seg6_lwt_lwtunnel(lwt); 737 738 dst_cache_destroy(&slwt->cache_input); 739 dst_cache_destroy(&slwt->cache_output); 740} 741 742static int seg6_fill_encap_info(struct sk_buff *skb, 743 struct lwtunnel_state *lwtstate) 744{ 745 struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate); 746 747 if (nla_put_srh(skb, SEG6_IPTUNNEL_SRH, tuninfo)) 748 return -EMSGSIZE; 749 750 return 0; 751} 752 753static int seg6_encap_nlsize(struct lwtunnel_state *lwtstate) 754{ 755 struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate); 756 757 return nla_total_size(SEG6_IPTUN_ENCAP_SIZE(tuninfo)); 758} 759 760static int seg6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) 761{ 762 struct seg6_iptunnel_encap *a_hdr = seg6_encap_lwtunnel(a); 763 struct seg6_iptunnel_encap *b_hdr = seg6_encap_lwtunnel(b); 764 int len = SEG6_IPTUN_ENCAP_SIZE(a_hdr); 765 766 if (len != SEG6_IPTUN_ENCAP_SIZE(b_hdr)) 767 return 1; 768 769 return memcmp(a_hdr, b_hdr, len); 770} 771 772static const struct lwtunnel_encap_ops seg6_iptun_ops = { 773 .build_state = seg6_build_state, 774 .destroy_state = seg6_destroy_state, 775 .output = seg6_output, 776 .input = seg6_input, 777 .fill_encap = seg6_fill_encap_info, 778 .get_encap_size = seg6_encap_nlsize, 779 .cmp_encap = seg6_encap_cmp, 780 .owner = THIS_MODULE, 781}; 782 783int __init seg6_iptunnel_init(void) 784{ 785 return lwtunnel_encap_add_ops(&seg6_iptun_ops, LWTUNNEL_ENCAP_SEG6); 786} 787 788void seg6_iptunnel_exit(void) 789{ 790 lwtunnel_encap_del_ops(&seg6_iptun_ops, LWTUNNEL_ENCAP_SEG6); 791}
~/
Read-only file preview.