162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SR-IPv6 implementation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: 662306a36Sopenharmony_ci * David Lebrun <david.lebrun@uclouvain.be> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/skbuff.h> 1162306a36Sopenharmony_ci#include <linux/net.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <net/ip.h> 1462306a36Sopenharmony_ci#include <net/ip_tunnels.h> 1562306a36Sopenharmony_ci#include <net/lwtunnel.h> 1662306a36Sopenharmony_ci#include <net/netevent.h> 1762306a36Sopenharmony_ci#include <net/netns/generic.h> 1862306a36Sopenharmony_ci#include <net/ip6_fib.h> 1962306a36Sopenharmony_ci#include <net/route.h> 2062306a36Sopenharmony_ci#include <net/seg6.h> 2162306a36Sopenharmony_ci#include <linux/seg6.h> 2262306a36Sopenharmony_ci#include <linux/seg6_iptunnel.h> 2362306a36Sopenharmony_ci#include <net/addrconf.h> 2462306a36Sopenharmony_ci#include <net/ip6_route.h> 2562306a36Sopenharmony_ci#include <net/dst_cache.h> 2662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 2762306a36Sopenharmony_ci#include <net/seg6_hmac.h> 2862306a36Sopenharmony_ci#endif 2962306a36Sopenharmony_ci#include <linux/netfilter.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci int head = 0; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci switch (tuninfo->mode) { 3662306a36Sopenharmony_ci case SEG6_IPTUN_MODE_INLINE: 3762306a36Sopenharmony_ci break; 3862306a36Sopenharmony_ci case SEG6_IPTUN_MODE_ENCAP: 3962306a36Sopenharmony_ci case SEG6_IPTUN_MODE_ENCAP_RED: 4062306a36Sopenharmony_ci head = sizeof(struct ipv6hdr); 4162306a36Sopenharmony_ci break; 4262306a36Sopenharmony_ci case SEG6_IPTUN_MODE_L2ENCAP: 4362306a36Sopenharmony_ci case SEG6_IPTUN_MODE_L2ENCAP_RED: 4462306a36Sopenharmony_ci return 0; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return ((tuninfo->srh->hdrlen + 1) << 3) + head; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct seg6_lwt { 5162306a36Sopenharmony_ci struct dst_cache cache; 5262306a36Sopenharmony_ci struct seg6_iptunnel_encap tuninfo[]; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic inline struct seg6_lwt *seg6_lwt_lwtunnel(struct lwtunnel_state *lwt) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci return (struct seg6_lwt *)lwt->data; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic inline struct seg6_iptunnel_encap * 6162306a36Sopenharmony_ciseg6_encap_lwtunnel(struct lwtunnel_state *lwt) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci return seg6_lwt_lwtunnel(lwt)->tuninfo; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic const struct nla_policy seg6_iptunnel_policy[SEG6_IPTUNNEL_MAX + 1] = { 6762306a36Sopenharmony_ci [SEG6_IPTUNNEL_SRH] = { .type = NLA_BINARY }, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int nla_put_srh(struct sk_buff *skb, int attrtype, 7162306a36Sopenharmony_ci struct seg6_iptunnel_encap *tuninfo) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct seg6_iptunnel_encap *data; 7462306a36Sopenharmony_ci struct nlattr *nla; 7562306a36Sopenharmony_ci int len; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci len = SEG6_IPTUN_ENCAP_SIZE(tuninfo); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci nla = nla_reserve(skb, attrtype, len); 8062306a36Sopenharmony_ci if (!nla) 8162306a36Sopenharmony_ci return -EMSGSIZE; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci data = nla_data(nla); 8462306a36Sopenharmony_ci memcpy(data, tuninfo, len); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void set_tun_src(struct net *net, struct net_device *dev, 9062306a36Sopenharmony_ci struct in6_addr *daddr, struct in6_addr *saddr) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct seg6_pernet_data *sdata = seg6_pernet(net); 9362306a36Sopenharmony_ci struct in6_addr *tun_src; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci rcu_read_lock(); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci tun_src = rcu_dereference(sdata->tun_src); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (!ipv6_addr_any(tun_src)) { 10062306a36Sopenharmony_ci memcpy(saddr, tun_src, sizeof(struct in6_addr)); 10162306a36Sopenharmony_ci } else { 10262306a36Sopenharmony_ci ipv6_dev_get_saddr(net, dev, daddr, IPV6_PREFER_SRC_PUBLIC, 10362306a36Sopenharmony_ci saddr); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci rcu_read_unlock(); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* Compute flowlabel for outer IPv6 header */ 11062306a36Sopenharmony_cistatic __be32 seg6_make_flowlabel(struct net *net, struct sk_buff *skb, 11162306a36Sopenharmony_ci struct ipv6hdr *inner_hdr) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci int do_flowlabel = net->ipv6.sysctl.seg6_flowlabel; 11462306a36Sopenharmony_ci __be32 flowlabel = 0; 11562306a36Sopenharmony_ci u32 hash; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (do_flowlabel > 0) { 11862306a36Sopenharmony_ci hash = skb_get_hash(skb); 11962306a36Sopenharmony_ci hash = rol32(hash, 16); 12062306a36Sopenharmony_ci flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK; 12162306a36Sopenharmony_ci } else if (!do_flowlabel && skb->protocol == htons(ETH_P_IPV6)) { 12262306a36Sopenharmony_ci flowlabel = ip6_flowlabel(inner_hdr); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci return flowlabel; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */ 12862306a36Sopenharmony_ciint seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 13162306a36Sopenharmony_ci struct net *net = dev_net(dst->dev); 13262306a36Sopenharmony_ci struct ipv6hdr *hdr, *inner_hdr; 13362306a36Sopenharmony_ci struct ipv6_sr_hdr *isrh; 13462306a36Sopenharmony_ci int hdrlen, tot_len, err; 13562306a36Sopenharmony_ci __be32 flowlabel; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci hdrlen = (osrh->hdrlen + 1) << 3; 13862306a36Sopenharmony_ci tot_len = hdrlen + sizeof(*hdr); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci err = skb_cow_head(skb, tot_len + skb->mac_len); 14162306a36Sopenharmony_ci if (unlikely(err)) 14262306a36Sopenharmony_ci return err; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci inner_hdr = ipv6_hdr(skb); 14562306a36Sopenharmony_ci flowlabel = seg6_make_flowlabel(net, skb, inner_hdr); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci skb_push(skb, tot_len); 14862306a36Sopenharmony_ci skb_reset_network_header(skb); 14962306a36Sopenharmony_ci skb_mac_header_rebuild(skb); 15062306a36Sopenharmony_ci hdr = ipv6_hdr(skb); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* inherit tc, flowlabel and hlim 15362306a36Sopenharmony_ci * hlim will be decremented in ip6_forward() afterwards and 15462306a36Sopenharmony_ci * decapsulation will overwrite inner hlim with outer hlim 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IPV6)) { 15862306a36Sopenharmony_ci ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)), 15962306a36Sopenharmony_ci flowlabel); 16062306a36Sopenharmony_ci hdr->hop_limit = inner_hdr->hop_limit; 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci ip6_flow_hdr(hdr, 0, flowlabel); 16362306a36Sopenharmony_ci hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb)); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* the control block has been erased, so we have to set the 16862306a36Sopenharmony_ci * iif once again. 16962306a36Sopenharmony_ci * We read the receiving interface index directly from the 17062306a36Sopenharmony_ci * skb->skb_iif as it is done in the IPv4 receiving path (i.e.: 17162306a36Sopenharmony_ci * ip_rcv_core(...)). 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci IP6CB(skb)->iif = skb->skb_iif; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci hdr->nexthdr = NEXTHDR_ROUTING; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci isrh = (void *)hdr + sizeof(*hdr); 17962306a36Sopenharmony_ci memcpy(isrh, osrh, hdrlen); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci isrh->nexthdr = proto; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci hdr->daddr = isrh->segments[isrh->first_segment]; 18462306a36Sopenharmony_ci set_tun_src(net, dst->dev, &hdr->daddr, &hdr->saddr); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 18762306a36Sopenharmony_ci if (sr_has_hmac(isrh)) { 18862306a36Sopenharmony_ci err = seg6_push_hmac(net, &hdr->saddr, isrh); 18962306a36Sopenharmony_ci if (unlikely(err)) 19062306a36Sopenharmony_ci return err; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci#endif 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci skb_postpush_rcsum(skb, hdr, tot_len); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(seg6_do_srh_encap); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* encapsulate an IPv6 packet within an outer IPv6 header with reduced SRH */ 20362306a36Sopenharmony_cistatic int seg6_do_srh_encap_red(struct sk_buff *skb, 20462306a36Sopenharmony_ci struct ipv6_sr_hdr *osrh, int proto) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci __u8 first_seg = osrh->first_segment; 20762306a36Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 20862306a36Sopenharmony_ci struct net *net = dev_net(dst->dev); 20962306a36Sopenharmony_ci struct ipv6hdr *hdr, *inner_hdr; 21062306a36Sopenharmony_ci int hdrlen = ipv6_optlen(osrh); 21162306a36Sopenharmony_ci int red_tlv_offset, tlv_offset; 21262306a36Sopenharmony_ci struct ipv6_sr_hdr *isrh; 21362306a36Sopenharmony_ci bool skip_srh = false; 21462306a36Sopenharmony_ci __be32 flowlabel; 21562306a36Sopenharmony_ci int tot_len, err; 21662306a36Sopenharmony_ci int red_hdrlen; 21762306a36Sopenharmony_ci int tlvs_len; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (first_seg > 0) { 22062306a36Sopenharmony_ci red_hdrlen = hdrlen - sizeof(struct in6_addr); 22162306a36Sopenharmony_ci } else { 22262306a36Sopenharmony_ci /* NOTE: if tag/flags and/or other TLVs are introduced in the 22362306a36Sopenharmony_ci * seg6_iptunnel infrastructure, they should be considered when 22462306a36Sopenharmony_ci * deciding to skip the SRH. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci skip_srh = !sr_has_hmac(osrh); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci red_hdrlen = skip_srh ? 0 : hdrlen; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci tot_len = red_hdrlen + sizeof(struct ipv6hdr); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci err = skb_cow_head(skb, tot_len + skb->mac_len); 23462306a36Sopenharmony_ci if (unlikely(err)) 23562306a36Sopenharmony_ci return err; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci inner_hdr = ipv6_hdr(skb); 23862306a36Sopenharmony_ci flowlabel = seg6_make_flowlabel(net, skb, inner_hdr); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci skb_push(skb, tot_len); 24162306a36Sopenharmony_ci skb_reset_network_header(skb); 24262306a36Sopenharmony_ci skb_mac_header_rebuild(skb); 24362306a36Sopenharmony_ci hdr = ipv6_hdr(skb); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* based on seg6_do_srh_encap() */ 24662306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IPV6)) { 24762306a36Sopenharmony_ci ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)), 24862306a36Sopenharmony_ci flowlabel); 24962306a36Sopenharmony_ci hdr->hop_limit = inner_hdr->hop_limit; 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci ip6_flow_hdr(hdr, 0, flowlabel); 25262306a36Sopenharmony_ci hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb)); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); 25562306a36Sopenharmony_ci IP6CB(skb)->iif = skb->skb_iif; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* no matter if we have to skip the SRH or not, the first segment 25962306a36Sopenharmony_ci * always comes in the pushed IPv6 header. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci hdr->daddr = osrh->segments[first_seg]; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (skip_srh) { 26462306a36Sopenharmony_ci hdr->nexthdr = proto; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci set_tun_src(net, dst->dev, &hdr->daddr, &hdr->saddr); 26762306a36Sopenharmony_ci goto out; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* we cannot skip the SRH, slow path */ 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci hdr->nexthdr = NEXTHDR_ROUTING; 27362306a36Sopenharmony_ci isrh = (void *)hdr + sizeof(struct ipv6hdr); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (unlikely(!first_seg)) { 27662306a36Sopenharmony_ci /* this is a very rare case; we have only one SID but 27762306a36Sopenharmony_ci * we cannot skip the SRH since we are carrying some 27862306a36Sopenharmony_ci * other info. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci memcpy(isrh, osrh, hdrlen); 28162306a36Sopenharmony_ci goto srcaddr; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci tlv_offset = sizeof(*osrh) + (first_seg + 1) * sizeof(struct in6_addr); 28562306a36Sopenharmony_ci red_tlv_offset = tlv_offset - sizeof(struct in6_addr); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci memcpy(isrh, osrh, red_tlv_offset); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci tlvs_len = hdrlen - tlv_offset; 29062306a36Sopenharmony_ci if (unlikely(tlvs_len > 0)) { 29162306a36Sopenharmony_ci const void *s = (const void *)osrh + tlv_offset; 29262306a36Sopenharmony_ci void *d = (void *)isrh + red_tlv_offset; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci memcpy(d, s, tlvs_len); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci --isrh->first_segment; 29862306a36Sopenharmony_ci isrh->hdrlen -= 2; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cisrcaddr: 30162306a36Sopenharmony_ci isrh->nexthdr = proto; 30262306a36Sopenharmony_ci set_tun_src(net, dst->dev, &hdr->daddr, &hdr->saddr); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 30562306a36Sopenharmony_ci if (unlikely(!skip_srh && sr_has_hmac(isrh))) { 30662306a36Sopenharmony_ci err = seg6_push_hmac(net, &hdr->saddr, isrh); 30762306a36Sopenharmony_ci if (unlikely(err)) 30862306a36Sopenharmony_ci return err; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci#endif 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ciout: 31362306a36Sopenharmony_ci hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci skb_postpush_rcsum(skb, hdr, tot_len); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/* insert an SRH within an IPv6 packet, just after the IPv6 header */ 32162306a36Sopenharmony_ciint seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct ipv6hdr *hdr, *oldhdr; 32462306a36Sopenharmony_ci struct ipv6_sr_hdr *isrh; 32562306a36Sopenharmony_ci int hdrlen, err; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci hdrlen = (osrh->hdrlen + 1) << 3; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci err = skb_cow_head(skb, hdrlen + skb->mac_len); 33062306a36Sopenharmony_ci if (unlikely(err)) 33162306a36Sopenharmony_ci return err; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci oldhdr = ipv6_hdr(skb); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci skb_pull(skb, sizeof(struct ipv6hdr)); 33662306a36Sopenharmony_ci skb_postpull_rcsum(skb, skb_network_header(skb), 33762306a36Sopenharmony_ci sizeof(struct ipv6hdr)); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci skb_push(skb, sizeof(struct ipv6hdr) + hdrlen); 34062306a36Sopenharmony_ci skb_reset_network_header(skb); 34162306a36Sopenharmony_ci skb_mac_header_rebuild(skb); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci hdr = ipv6_hdr(skb); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci memmove(hdr, oldhdr, sizeof(*hdr)); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci isrh = (void *)hdr + sizeof(*hdr); 34862306a36Sopenharmony_ci memcpy(isrh, osrh, hdrlen); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci isrh->nexthdr = hdr->nexthdr; 35162306a36Sopenharmony_ci hdr->nexthdr = NEXTHDR_ROUTING; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci isrh->segments[0] = hdr->daddr; 35462306a36Sopenharmony_ci hdr->daddr = isrh->segments[isrh->first_segment]; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 35762306a36Sopenharmony_ci if (sr_has_hmac(isrh)) { 35862306a36Sopenharmony_ci struct net *net = dev_net(skb_dst(skb)->dev); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci err = seg6_push_hmac(net, &hdr->saddr, isrh); 36162306a36Sopenharmony_ci if (unlikely(err)) 36262306a36Sopenharmony_ci return err; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci#endif 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci skb_postpush_rcsum(skb, hdr, sizeof(struct ipv6hdr) + hdrlen); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(seg6_do_srh_inline); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int seg6_do_srh(struct sk_buff *skb) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 37762306a36Sopenharmony_ci struct seg6_iptunnel_encap *tinfo; 37862306a36Sopenharmony_ci int proto, err = 0; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci tinfo = seg6_encap_lwtunnel(dst->lwtstate); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci switch (tinfo->mode) { 38362306a36Sopenharmony_ci case SEG6_IPTUN_MODE_INLINE: 38462306a36Sopenharmony_ci if (skb->protocol != htons(ETH_P_IPV6)) 38562306a36Sopenharmony_ci return -EINVAL; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci err = seg6_do_srh_inline(skb, tinfo->srh); 38862306a36Sopenharmony_ci if (err) 38962306a36Sopenharmony_ci return err; 39062306a36Sopenharmony_ci break; 39162306a36Sopenharmony_ci case SEG6_IPTUN_MODE_ENCAP: 39262306a36Sopenharmony_ci case SEG6_IPTUN_MODE_ENCAP_RED: 39362306a36Sopenharmony_ci err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6); 39462306a36Sopenharmony_ci if (err) 39562306a36Sopenharmony_ci return err; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IPV6)) 39862306a36Sopenharmony_ci proto = IPPROTO_IPV6; 39962306a36Sopenharmony_ci else if (skb->protocol == htons(ETH_P_IP)) 40062306a36Sopenharmony_ci proto = IPPROTO_IPIP; 40162306a36Sopenharmony_ci else 40262306a36Sopenharmony_ci return -EINVAL; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (tinfo->mode == SEG6_IPTUN_MODE_ENCAP) 40562306a36Sopenharmony_ci err = seg6_do_srh_encap(skb, tinfo->srh, proto); 40662306a36Sopenharmony_ci else 40762306a36Sopenharmony_ci err = seg6_do_srh_encap_red(skb, tinfo->srh, proto); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (err) 41062306a36Sopenharmony_ci return err; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci skb_set_inner_transport_header(skb, skb_transport_offset(skb)); 41362306a36Sopenharmony_ci skb_set_inner_protocol(skb, skb->protocol); 41462306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci case SEG6_IPTUN_MODE_L2ENCAP: 41762306a36Sopenharmony_ci case SEG6_IPTUN_MODE_L2ENCAP_RED: 41862306a36Sopenharmony_ci if (!skb_mac_header_was_set(skb)) 41962306a36Sopenharmony_ci return -EINVAL; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (pskb_expand_head(skb, skb->mac_len, 0, GFP_ATOMIC) < 0) 42262306a36Sopenharmony_ci return -ENOMEM; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci skb_mac_header_rebuild(skb); 42562306a36Sopenharmony_ci skb_push(skb, skb->mac_len); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (tinfo->mode == SEG6_IPTUN_MODE_L2ENCAP) 42862306a36Sopenharmony_ci err = seg6_do_srh_encap(skb, tinfo->srh, 42962306a36Sopenharmony_ci IPPROTO_ETHERNET); 43062306a36Sopenharmony_ci else 43162306a36Sopenharmony_ci err = seg6_do_srh_encap_red(skb, tinfo->srh, 43262306a36Sopenharmony_ci IPPROTO_ETHERNET); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (err) 43562306a36Sopenharmony_ci return err; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 43862306a36Sopenharmony_ci break; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 44262306a36Sopenharmony_ci nf_reset_ct(skb); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic int seg6_input_finish(struct net *net, struct sock *sk, 44862306a36Sopenharmony_ci struct sk_buff *skb) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci return dst_input(skb); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic int seg6_input_core(struct net *net, struct sock *sk, 45462306a36Sopenharmony_ci struct sk_buff *skb) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct dst_entry *orig_dst = skb_dst(skb); 45762306a36Sopenharmony_ci struct dst_entry *dst = NULL; 45862306a36Sopenharmony_ci struct seg6_lwt *slwt; 45962306a36Sopenharmony_ci int err; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci err = seg6_do_srh(skb); 46262306a36Sopenharmony_ci if (unlikely(err)) { 46362306a36Sopenharmony_ci kfree_skb(skb); 46462306a36Sopenharmony_ci return err; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci preempt_disable(); 47062306a36Sopenharmony_ci dst = dst_cache_get(&slwt->cache); 47162306a36Sopenharmony_ci preempt_enable(); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (!dst) { 47462306a36Sopenharmony_ci ip6_route_input(skb); 47562306a36Sopenharmony_ci dst = skb_dst(skb); 47662306a36Sopenharmony_ci if (!dst->error) { 47762306a36Sopenharmony_ci preempt_disable(); 47862306a36Sopenharmony_ci dst_cache_set_ip6(&slwt->cache, dst, 47962306a36Sopenharmony_ci &ipv6_hdr(skb)->saddr); 48062306a36Sopenharmony_ci preempt_enable(); 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci } else { 48362306a36Sopenharmony_ci skb_dst_drop(skb); 48462306a36Sopenharmony_ci skb_dst_set(skb, dst); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); 48862306a36Sopenharmony_ci if (unlikely(err)) 48962306a36Sopenharmony_ci return err; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 49262306a36Sopenharmony_ci return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, 49362306a36Sopenharmony_ci dev_net(skb->dev), NULL, skb, NULL, 49462306a36Sopenharmony_ci skb_dst(skb)->dev, seg6_input_finish); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return seg6_input_finish(dev_net(skb->dev), NULL, skb); 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int seg6_input_nf(struct sk_buff *skb) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct net_device *dev = skb_dst(skb)->dev; 50262306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci switch (skb->protocol) { 50562306a36Sopenharmony_ci case htons(ETH_P_IP): 50662306a36Sopenharmony_ci return NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, NULL, 50762306a36Sopenharmony_ci skb, NULL, dev, seg6_input_core); 50862306a36Sopenharmony_ci case htons(ETH_P_IPV6): 50962306a36Sopenharmony_ci return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, NULL, 51062306a36Sopenharmony_ci skb, NULL, dev, seg6_input_core); 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return -EINVAL; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic int seg6_input(struct sk_buff *skb) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 51962306a36Sopenharmony_ci return seg6_input_nf(skb); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return seg6_input_core(dev_net(skb->dev), NULL, skb); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic int seg6_output_core(struct net *net, struct sock *sk, 52562306a36Sopenharmony_ci struct sk_buff *skb) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct dst_entry *orig_dst = skb_dst(skb); 52862306a36Sopenharmony_ci struct dst_entry *dst = NULL; 52962306a36Sopenharmony_ci struct seg6_lwt *slwt; 53062306a36Sopenharmony_ci int err; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci err = seg6_do_srh(skb); 53362306a36Sopenharmony_ci if (unlikely(err)) 53462306a36Sopenharmony_ci goto drop; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci preempt_disable(); 53962306a36Sopenharmony_ci dst = dst_cache_get(&slwt->cache); 54062306a36Sopenharmony_ci preempt_enable(); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (unlikely(!dst)) { 54362306a36Sopenharmony_ci struct ipv6hdr *hdr = ipv6_hdr(skb); 54462306a36Sopenharmony_ci struct flowi6 fl6; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 54762306a36Sopenharmony_ci fl6.daddr = hdr->daddr; 54862306a36Sopenharmony_ci fl6.saddr = hdr->saddr; 54962306a36Sopenharmony_ci fl6.flowlabel = ip6_flowinfo(hdr); 55062306a36Sopenharmony_ci fl6.flowi6_mark = skb->mark; 55162306a36Sopenharmony_ci fl6.flowi6_proto = hdr->nexthdr; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci dst = ip6_route_output(net, NULL, &fl6); 55462306a36Sopenharmony_ci if (dst->error) { 55562306a36Sopenharmony_ci err = dst->error; 55662306a36Sopenharmony_ci dst_release(dst); 55762306a36Sopenharmony_ci goto drop; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci preempt_disable(); 56162306a36Sopenharmony_ci dst_cache_set_ip6(&slwt->cache, dst, &fl6.saddr); 56262306a36Sopenharmony_ci preempt_enable(); 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci skb_dst_drop(skb); 56662306a36Sopenharmony_ci skb_dst_set(skb, dst); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); 56962306a36Sopenharmony_ci if (unlikely(err)) 57062306a36Sopenharmony_ci goto drop; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 57362306a36Sopenharmony_ci return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb, 57462306a36Sopenharmony_ci NULL, skb_dst(skb)->dev, dst_output); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return dst_output(net, sk, skb); 57762306a36Sopenharmony_cidrop: 57862306a36Sopenharmony_ci kfree_skb(skb); 57962306a36Sopenharmony_ci return err; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic int seg6_output_nf(struct net *net, struct sock *sk, struct sk_buff *skb) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct net_device *dev = skb_dst(skb)->dev; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci switch (skb->protocol) { 58762306a36Sopenharmony_ci case htons(ETH_P_IP): 58862306a36Sopenharmony_ci return NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, sk, skb, 58962306a36Sopenharmony_ci NULL, dev, seg6_output_core); 59062306a36Sopenharmony_ci case htons(ETH_P_IPV6): 59162306a36Sopenharmony_ci return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, sk, skb, 59262306a36Sopenharmony_ci NULL, dev, seg6_output_core); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) 60162306a36Sopenharmony_ci return seg6_output_nf(net, sk, skb); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return seg6_output_core(net, sk, skb); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic int seg6_build_state(struct net *net, struct nlattr *nla, 60762306a36Sopenharmony_ci unsigned int family, const void *cfg, 60862306a36Sopenharmony_ci struct lwtunnel_state **ts, 60962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct nlattr *tb[SEG6_IPTUNNEL_MAX + 1]; 61262306a36Sopenharmony_ci struct seg6_iptunnel_encap *tuninfo; 61362306a36Sopenharmony_ci struct lwtunnel_state *newts; 61462306a36Sopenharmony_ci int tuninfo_len, min_size; 61562306a36Sopenharmony_ci struct seg6_lwt *slwt; 61662306a36Sopenharmony_ci int err; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (family != AF_INET && family != AF_INET6) 61962306a36Sopenharmony_ci return -EINVAL; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, SEG6_IPTUNNEL_MAX, nla, 62262306a36Sopenharmony_ci seg6_iptunnel_policy, extack); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (err < 0) 62562306a36Sopenharmony_ci return err; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (!tb[SEG6_IPTUNNEL_SRH]) 62862306a36Sopenharmony_ci return -EINVAL; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci tuninfo = nla_data(tb[SEG6_IPTUNNEL_SRH]); 63162306a36Sopenharmony_ci tuninfo_len = nla_len(tb[SEG6_IPTUNNEL_SRH]); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* tuninfo must contain at least the iptunnel encap structure, 63462306a36Sopenharmony_ci * the SRH and one segment 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_ci min_size = sizeof(*tuninfo) + sizeof(struct ipv6_sr_hdr) + 63762306a36Sopenharmony_ci sizeof(struct in6_addr); 63862306a36Sopenharmony_ci if (tuninfo_len < min_size) 63962306a36Sopenharmony_ci return -EINVAL; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci switch (tuninfo->mode) { 64262306a36Sopenharmony_ci case SEG6_IPTUN_MODE_INLINE: 64362306a36Sopenharmony_ci if (family != AF_INET6) 64462306a36Sopenharmony_ci return -EINVAL; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci case SEG6_IPTUN_MODE_ENCAP: 64862306a36Sopenharmony_ci break; 64962306a36Sopenharmony_ci case SEG6_IPTUN_MODE_L2ENCAP: 65062306a36Sopenharmony_ci break; 65162306a36Sopenharmony_ci case SEG6_IPTUN_MODE_ENCAP_RED: 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci case SEG6_IPTUN_MODE_L2ENCAP_RED: 65462306a36Sopenharmony_ci break; 65562306a36Sopenharmony_ci default: 65662306a36Sopenharmony_ci return -EINVAL; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* verify that SRH is consistent */ 66062306a36Sopenharmony_ci if (!seg6_validate_srh(tuninfo->srh, tuninfo_len - sizeof(*tuninfo), false)) 66162306a36Sopenharmony_ci return -EINVAL; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci newts = lwtunnel_state_alloc(tuninfo_len + sizeof(*slwt)); 66462306a36Sopenharmony_ci if (!newts) 66562306a36Sopenharmony_ci return -ENOMEM; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci slwt = seg6_lwt_lwtunnel(newts); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci err = dst_cache_init(&slwt->cache, GFP_ATOMIC); 67062306a36Sopenharmony_ci if (err) { 67162306a36Sopenharmony_ci kfree(newts); 67262306a36Sopenharmony_ci return err; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci memcpy(&slwt->tuninfo, tuninfo, tuninfo_len); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci newts->type = LWTUNNEL_ENCAP_SEG6; 67862306a36Sopenharmony_ci newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (tuninfo->mode != SEG6_IPTUN_MODE_L2ENCAP) 68162306a36Sopenharmony_ci newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci newts->headroom = seg6_lwt_headroom(tuninfo); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci *ts = newts; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return 0; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic void seg6_destroy_state(struct lwtunnel_state *lwt) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci dst_cache_destroy(&seg6_lwt_lwtunnel(lwt)->cache); 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic int seg6_fill_encap_info(struct sk_buff *skb, 69662306a36Sopenharmony_ci struct lwtunnel_state *lwtstate) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (nla_put_srh(skb, SEG6_IPTUNNEL_SRH, tuninfo)) 70162306a36Sopenharmony_ci return -EMSGSIZE; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return 0; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic int seg6_encap_nlsize(struct lwtunnel_state *lwtstate) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci return nla_total_size(SEG6_IPTUN_ENCAP_SIZE(tuninfo)); 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic int seg6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct seg6_iptunnel_encap *a_hdr = seg6_encap_lwtunnel(a); 71662306a36Sopenharmony_ci struct seg6_iptunnel_encap *b_hdr = seg6_encap_lwtunnel(b); 71762306a36Sopenharmony_ci int len = SEG6_IPTUN_ENCAP_SIZE(a_hdr); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (len != SEG6_IPTUN_ENCAP_SIZE(b_hdr)) 72062306a36Sopenharmony_ci return 1; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci return memcmp(a_hdr, b_hdr, len); 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic const struct lwtunnel_encap_ops seg6_iptun_ops = { 72662306a36Sopenharmony_ci .build_state = seg6_build_state, 72762306a36Sopenharmony_ci .destroy_state = seg6_destroy_state, 72862306a36Sopenharmony_ci .output = seg6_output, 72962306a36Sopenharmony_ci .input = seg6_input, 73062306a36Sopenharmony_ci .fill_encap = seg6_fill_encap_info, 73162306a36Sopenharmony_ci .get_encap_size = seg6_encap_nlsize, 73262306a36Sopenharmony_ci .cmp_encap = seg6_encap_cmp, 73362306a36Sopenharmony_ci .owner = THIS_MODULE, 73462306a36Sopenharmony_ci}; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ciint __init seg6_iptunnel_init(void) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci return lwtunnel_encap_add_ops(&seg6_iptun_ops, LWTUNNEL_ENCAP_SEG6); 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_civoid seg6_iptunnel_exit(void) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci lwtunnel_encap_del_ops(&seg6_iptun_ops, LWTUNNEL_ENCAP_SEG6); 74462306a36Sopenharmony_ci} 745