18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SR-IPv6 implementation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: 68c2ecf20Sopenharmony_ci * David Lebrun <david.lebrun@uclouvain.be> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 118c2ecf20Sopenharmony_ci#include <linux/net.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <net/ip.h> 148c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h> 158c2ecf20Sopenharmony_ci#include <net/lwtunnel.h> 168c2ecf20Sopenharmony_ci#include <net/netevent.h> 178c2ecf20Sopenharmony_ci#include <net/netns/generic.h> 188c2ecf20Sopenharmony_ci#include <net/ip6_fib.h> 198c2ecf20Sopenharmony_ci#include <net/route.h> 208c2ecf20Sopenharmony_ci#include <net/seg6.h> 218c2ecf20Sopenharmony_ci#include <linux/seg6.h> 228c2ecf20Sopenharmony_ci#include <linux/seg6_iptunnel.h> 238c2ecf20Sopenharmony_ci#include <net/addrconf.h> 248c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 258c2ecf20Sopenharmony_ci#include <net/dst_cache.h> 268c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 278c2ecf20Sopenharmony_ci#include <net/seg6_hmac.h> 288c2ecf20Sopenharmony_ci#endif 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci int head = 0; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci switch (tuninfo->mode) { 358c2ecf20Sopenharmony_ci case SEG6_IPTUN_MODE_INLINE: 368c2ecf20Sopenharmony_ci break; 378c2ecf20Sopenharmony_ci case SEG6_IPTUN_MODE_ENCAP: 388c2ecf20Sopenharmony_ci head = sizeof(struct ipv6hdr); 398c2ecf20Sopenharmony_ci break; 408c2ecf20Sopenharmony_ci case SEG6_IPTUN_MODE_L2ENCAP: 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return ((tuninfo->srh->hdrlen + 1) << 3) + head; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct seg6_lwt { 488c2ecf20Sopenharmony_ci struct dst_cache cache; 498c2ecf20Sopenharmony_ci struct seg6_iptunnel_encap tuninfo[]; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic inline struct seg6_lwt *seg6_lwt_lwtunnel(struct lwtunnel_state *lwt) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci return (struct seg6_lwt *)lwt->data; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic inline struct seg6_iptunnel_encap * 588c2ecf20Sopenharmony_ciseg6_encap_lwtunnel(struct lwtunnel_state *lwt) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci return seg6_lwt_lwtunnel(lwt)->tuninfo; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic const struct nla_policy seg6_iptunnel_policy[SEG6_IPTUNNEL_MAX + 1] = { 648c2ecf20Sopenharmony_ci [SEG6_IPTUNNEL_SRH] = { .type = NLA_BINARY }, 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int nla_put_srh(struct sk_buff *skb, int attrtype, 688c2ecf20Sopenharmony_ci struct seg6_iptunnel_encap *tuninfo) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct seg6_iptunnel_encap *data; 718c2ecf20Sopenharmony_ci struct nlattr *nla; 728c2ecf20Sopenharmony_ci int len; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci len = SEG6_IPTUN_ENCAP_SIZE(tuninfo); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci nla = nla_reserve(skb, attrtype, len); 778c2ecf20Sopenharmony_ci if (!nla) 788c2ecf20Sopenharmony_ci return -EMSGSIZE; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci data = nla_data(nla); 818c2ecf20Sopenharmony_ci memcpy(data, tuninfo, len); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void set_tun_src(struct net *net, struct net_device *dev, 878c2ecf20Sopenharmony_ci struct in6_addr *daddr, struct in6_addr *saddr) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct seg6_pernet_data *sdata = seg6_pernet(net); 908c2ecf20Sopenharmony_ci struct in6_addr *tun_src; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci rcu_read_lock(); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci tun_src = rcu_dereference(sdata->tun_src); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (!ipv6_addr_any(tun_src)) { 978c2ecf20Sopenharmony_ci memcpy(saddr, tun_src, sizeof(struct in6_addr)); 988c2ecf20Sopenharmony_ci } else { 998c2ecf20Sopenharmony_ci ipv6_dev_get_saddr(net, dev, daddr, IPV6_PREFER_SRC_PUBLIC, 1008c2ecf20Sopenharmony_ci saddr); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci rcu_read_unlock(); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* Compute flowlabel for outer IPv6 header */ 1078c2ecf20Sopenharmony_cistatic __be32 seg6_make_flowlabel(struct net *net, struct sk_buff *skb, 1088c2ecf20Sopenharmony_ci struct ipv6hdr *inner_hdr) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci int do_flowlabel = net->ipv6.sysctl.seg6_flowlabel; 1118c2ecf20Sopenharmony_ci __be32 flowlabel = 0; 1128c2ecf20Sopenharmony_ci u32 hash; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (do_flowlabel > 0) { 1158c2ecf20Sopenharmony_ci hash = skb_get_hash(skb); 1168c2ecf20Sopenharmony_ci hash = rol32(hash, 16); 1178c2ecf20Sopenharmony_ci flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK; 1188c2ecf20Sopenharmony_ci } else if (!do_flowlabel && skb->protocol == htons(ETH_P_IPV6)) { 1198c2ecf20Sopenharmony_ci flowlabel = ip6_flowlabel(inner_hdr); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci return flowlabel; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */ 1258c2ecf20Sopenharmony_ciint seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 1288c2ecf20Sopenharmony_ci struct net *net = dev_net(dst->dev); 1298c2ecf20Sopenharmony_ci struct ipv6hdr *hdr, *inner_hdr; 1308c2ecf20Sopenharmony_ci struct ipv6_sr_hdr *isrh; 1318c2ecf20Sopenharmony_ci int hdrlen, tot_len, err; 1328c2ecf20Sopenharmony_ci __be32 flowlabel; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci hdrlen = (osrh->hdrlen + 1) << 3; 1358c2ecf20Sopenharmony_ci tot_len = hdrlen + sizeof(*hdr); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci err = skb_cow_head(skb, tot_len + skb->mac_len); 1388c2ecf20Sopenharmony_ci if (unlikely(err)) 1398c2ecf20Sopenharmony_ci return err; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci inner_hdr = ipv6_hdr(skb); 1428c2ecf20Sopenharmony_ci flowlabel = seg6_make_flowlabel(net, skb, inner_hdr); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci skb_push(skb, tot_len); 1458c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 1468c2ecf20Sopenharmony_ci skb_mac_header_rebuild(skb); 1478c2ecf20Sopenharmony_ci hdr = ipv6_hdr(skb); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* inherit tc, flowlabel and hlim 1508c2ecf20Sopenharmony_ci * hlim will be decremented in ip6_forward() afterwards and 1518c2ecf20Sopenharmony_ci * decapsulation will overwrite inner hlim with outer hlim 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IPV6)) { 1558c2ecf20Sopenharmony_ci ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)), 1568c2ecf20Sopenharmony_ci flowlabel); 1578c2ecf20Sopenharmony_ci hdr->hop_limit = inner_hdr->hop_limit; 1588c2ecf20Sopenharmony_ci } else { 1598c2ecf20Sopenharmony_ci ip6_flow_hdr(hdr, 0, flowlabel); 1608c2ecf20Sopenharmony_ci hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb)); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* the control block has been erased, so we have to set the 1658c2ecf20Sopenharmony_ci * iif once again. 1668c2ecf20Sopenharmony_ci * We read the receiving interface index directly from the 1678c2ecf20Sopenharmony_ci * skb->skb_iif as it is done in the IPv4 receiving path (i.e.: 1688c2ecf20Sopenharmony_ci * ip_rcv_core(...)). 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci IP6CB(skb)->iif = skb->skb_iif; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci hdr->nexthdr = NEXTHDR_ROUTING; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci isrh = (void *)hdr + sizeof(*hdr); 1768c2ecf20Sopenharmony_ci memcpy(isrh, osrh, hdrlen); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci isrh->nexthdr = proto; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci hdr->daddr = isrh->segments[isrh->first_segment]; 1818c2ecf20Sopenharmony_ci set_tun_src(net, dst->dev, &hdr->daddr, &hdr->saddr); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 1848c2ecf20Sopenharmony_ci if (sr_has_hmac(isrh)) { 1858c2ecf20Sopenharmony_ci err = seg6_push_hmac(net, &hdr->saddr, isrh); 1868c2ecf20Sopenharmony_ci if (unlikely(err)) 1878c2ecf20Sopenharmony_ci return err; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci#endif 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci skb_postpush_rcsum(skb, hdr, tot_len); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(seg6_do_srh_encap); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* insert an SRH within an IPv6 packet, just after the IPv6 header */ 2008c2ecf20Sopenharmony_ciint seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct ipv6hdr *hdr, *oldhdr; 2038c2ecf20Sopenharmony_ci struct ipv6_sr_hdr *isrh; 2048c2ecf20Sopenharmony_ci int hdrlen, err; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci hdrlen = (osrh->hdrlen + 1) << 3; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci err = skb_cow_head(skb, hdrlen + skb->mac_len); 2098c2ecf20Sopenharmony_ci if (unlikely(err)) 2108c2ecf20Sopenharmony_ci return err; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci oldhdr = ipv6_hdr(skb); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(struct ipv6hdr)); 2158c2ecf20Sopenharmony_ci skb_postpull_rcsum(skb, skb_network_header(skb), 2168c2ecf20Sopenharmony_ci sizeof(struct ipv6hdr)); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci skb_push(skb, sizeof(struct ipv6hdr) + hdrlen); 2198c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 2208c2ecf20Sopenharmony_ci skb_mac_header_rebuild(skb); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci hdr = ipv6_hdr(skb); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci memmove(hdr, oldhdr, sizeof(*hdr)); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci isrh = (void *)hdr + sizeof(*hdr); 2278c2ecf20Sopenharmony_ci memcpy(isrh, osrh, hdrlen); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci isrh->nexthdr = hdr->nexthdr; 2308c2ecf20Sopenharmony_ci hdr->nexthdr = NEXTHDR_ROUTING; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci isrh->segments[0] = hdr->daddr; 2338c2ecf20Sopenharmony_ci hdr->daddr = isrh->segments[isrh->first_segment]; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 2368c2ecf20Sopenharmony_ci if (sr_has_hmac(isrh)) { 2378c2ecf20Sopenharmony_ci struct net *net = dev_net(skb_dst(skb)->dev); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci err = seg6_push_hmac(net, &hdr->saddr, isrh); 2408c2ecf20Sopenharmony_ci if (unlikely(err)) 2418c2ecf20Sopenharmony_ci return err; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci#endif 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci skb_postpush_rcsum(skb, hdr, sizeof(struct ipv6hdr) + hdrlen); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(seg6_do_srh_inline); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int seg6_do_srh(struct sk_buff *skb) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 2568c2ecf20Sopenharmony_ci struct seg6_iptunnel_encap *tinfo; 2578c2ecf20Sopenharmony_ci int proto, err = 0; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci tinfo = seg6_encap_lwtunnel(dst->lwtstate); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci switch (tinfo->mode) { 2628c2ecf20Sopenharmony_ci case SEG6_IPTUN_MODE_INLINE: 2638c2ecf20Sopenharmony_ci if (skb->protocol != htons(ETH_P_IPV6)) 2648c2ecf20Sopenharmony_ci return -EINVAL; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci err = seg6_do_srh_inline(skb, tinfo->srh); 2678c2ecf20Sopenharmony_ci if (err) 2688c2ecf20Sopenharmony_ci return err; 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci case SEG6_IPTUN_MODE_ENCAP: 2718c2ecf20Sopenharmony_ci err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6); 2728c2ecf20Sopenharmony_ci if (err) 2738c2ecf20Sopenharmony_ci return err; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IPV6)) 2768c2ecf20Sopenharmony_ci proto = IPPROTO_IPV6; 2778c2ecf20Sopenharmony_ci else if (skb->protocol == htons(ETH_P_IP)) 2788c2ecf20Sopenharmony_ci proto = IPPROTO_IPIP; 2798c2ecf20Sopenharmony_ci else 2808c2ecf20Sopenharmony_ci return -EINVAL; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci err = seg6_do_srh_encap(skb, tinfo->srh, proto); 2838c2ecf20Sopenharmony_ci if (err) 2848c2ecf20Sopenharmony_ci return err; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci skb_set_inner_transport_header(skb, skb_transport_offset(skb)); 2878c2ecf20Sopenharmony_ci skb_set_inner_protocol(skb, skb->protocol); 2888c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case SEG6_IPTUN_MODE_L2ENCAP: 2918c2ecf20Sopenharmony_ci if (!skb_mac_header_was_set(skb)) 2928c2ecf20Sopenharmony_ci return -EINVAL; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (pskb_expand_head(skb, skb->mac_len, 0, GFP_ATOMIC) < 0) 2958c2ecf20Sopenharmony_ci return -ENOMEM; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci skb_mac_header_rebuild(skb); 2988c2ecf20Sopenharmony_ci skb_push(skb, skb->mac_len); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci err = seg6_do_srh_encap(skb, tinfo->srh, IPPROTO_ETHERNET); 3018c2ecf20Sopenharmony_ci if (err) 3028c2ecf20Sopenharmony_ci return err; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci skb_set_transport_header(skb, sizeof(struct ipv6hdr)); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int seg6_input(struct sk_buff *skb) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct dst_entry *orig_dst = skb_dst(skb); 3168c2ecf20Sopenharmony_ci struct dst_entry *dst = NULL; 3178c2ecf20Sopenharmony_ci struct seg6_lwt *slwt; 3188c2ecf20Sopenharmony_ci int err; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci err = seg6_do_srh(skb); 3218c2ecf20Sopenharmony_ci if (unlikely(err)) { 3228c2ecf20Sopenharmony_ci kfree_skb(skb); 3238c2ecf20Sopenharmony_ci return err; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci preempt_disable(); 3298c2ecf20Sopenharmony_ci dst = dst_cache_get(&slwt->cache); 3308c2ecf20Sopenharmony_ci preempt_enable(); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci skb_dst_drop(skb); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (!dst) { 3358c2ecf20Sopenharmony_ci ip6_route_input(skb); 3368c2ecf20Sopenharmony_ci dst = skb_dst(skb); 3378c2ecf20Sopenharmony_ci if (!dst->error) { 3388c2ecf20Sopenharmony_ci preempt_disable(); 3398c2ecf20Sopenharmony_ci dst_cache_set_ip6(&slwt->cache, dst, 3408c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->saddr); 3418c2ecf20Sopenharmony_ci preempt_enable(); 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci } else { 3448c2ecf20Sopenharmony_ci skb_dst_set(skb, dst); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); 3488c2ecf20Sopenharmony_ci if (unlikely(err)) 3498c2ecf20Sopenharmony_ci return err; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return dst_input(skb); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct dst_entry *orig_dst = skb_dst(skb); 3578c2ecf20Sopenharmony_ci struct dst_entry *dst = NULL; 3588c2ecf20Sopenharmony_ci struct seg6_lwt *slwt; 3598c2ecf20Sopenharmony_ci int err = -EINVAL; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci err = seg6_do_srh(skb); 3628c2ecf20Sopenharmony_ci if (unlikely(err)) 3638c2ecf20Sopenharmony_ci goto drop; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci preempt_disable(); 3688c2ecf20Sopenharmony_ci dst = dst_cache_get(&slwt->cache); 3698c2ecf20Sopenharmony_ci preempt_enable(); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (unlikely(!dst)) { 3728c2ecf20Sopenharmony_ci struct ipv6hdr *hdr = ipv6_hdr(skb); 3738c2ecf20Sopenharmony_ci struct flowi6 fl6; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 3768c2ecf20Sopenharmony_ci fl6.daddr = hdr->daddr; 3778c2ecf20Sopenharmony_ci fl6.saddr = hdr->saddr; 3788c2ecf20Sopenharmony_ci fl6.flowlabel = ip6_flowinfo(hdr); 3798c2ecf20Sopenharmony_ci fl6.flowi6_mark = skb->mark; 3808c2ecf20Sopenharmony_ci fl6.flowi6_proto = hdr->nexthdr; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci dst = ip6_route_output(net, NULL, &fl6); 3838c2ecf20Sopenharmony_ci if (dst->error) { 3848c2ecf20Sopenharmony_ci err = dst->error; 3858c2ecf20Sopenharmony_ci dst_release(dst); 3868c2ecf20Sopenharmony_ci goto drop; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci preempt_disable(); 3908c2ecf20Sopenharmony_ci dst_cache_set_ip6(&slwt->cache, dst, &fl6.saddr); 3918c2ecf20Sopenharmony_ci preempt_enable(); 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci skb_dst_drop(skb); 3958c2ecf20Sopenharmony_ci skb_dst_set(skb, dst); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); 3988c2ecf20Sopenharmony_ci if (unlikely(err)) 3998c2ecf20Sopenharmony_ci goto drop; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return dst_output(net, sk, skb); 4028c2ecf20Sopenharmony_cidrop: 4038c2ecf20Sopenharmony_ci kfree_skb(skb); 4048c2ecf20Sopenharmony_ci return err; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int seg6_build_state(struct net *net, struct nlattr *nla, 4088c2ecf20Sopenharmony_ci unsigned int family, const void *cfg, 4098c2ecf20Sopenharmony_ci struct lwtunnel_state **ts, 4108c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct nlattr *tb[SEG6_IPTUNNEL_MAX + 1]; 4138c2ecf20Sopenharmony_ci struct seg6_iptunnel_encap *tuninfo; 4148c2ecf20Sopenharmony_ci struct lwtunnel_state *newts; 4158c2ecf20Sopenharmony_ci int tuninfo_len, min_size; 4168c2ecf20Sopenharmony_ci struct seg6_lwt *slwt; 4178c2ecf20Sopenharmony_ci int err; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (family != AF_INET && family != AF_INET6) 4208c2ecf20Sopenharmony_ci return -EINVAL; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, SEG6_IPTUNNEL_MAX, nla, 4238c2ecf20Sopenharmony_ci seg6_iptunnel_policy, extack); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (err < 0) 4268c2ecf20Sopenharmony_ci return err; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (!tb[SEG6_IPTUNNEL_SRH]) 4298c2ecf20Sopenharmony_ci return -EINVAL; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci tuninfo = nla_data(tb[SEG6_IPTUNNEL_SRH]); 4328c2ecf20Sopenharmony_ci tuninfo_len = nla_len(tb[SEG6_IPTUNNEL_SRH]); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* tuninfo must contain at least the iptunnel encap structure, 4358c2ecf20Sopenharmony_ci * the SRH and one segment 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_ci min_size = sizeof(*tuninfo) + sizeof(struct ipv6_sr_hdr) + 4388c2ecf20Sopenharmony_ci sizeof(struct in6_addr); 4398c2ecf20Sopenharmony_ci if (tuninfo_len < min_size) 4408c2ecf20Sopenharmony_ci return -EINVAL; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci switch (tuninfo->mode) { 4438c2ecf20Sopenharmony_ci case SEG6_IPTUN_MODE_INLINE: 4448c2ecf20Sopenharmony_ci if (family != AF_INET6) 4458c2ecf20Sopenharmony_ci return -EINVAL; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci case SEG6_IPTUN_MODE_ENCAP: 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case SEG6_IPTUN_MODE_L2ENCAP: 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci default: 4538c2ecf20Sopenharmony_ci return -EINVAL; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* verify that SRH is consistent */ 4578c2ecf20Sopenharmony_ci if (!seg6_validate_srh(tuninfo->srh, tuninfo_len - sizeof(*tuninfo), false)) 4588c2ecf20Sopenharmony_ci return -EINVAL; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci newts = lwtunnel_state_alloc(tuninfo_len + sizeof(*slwt)); 4618c2ecf20Sopenharmony_ci if (!newts) 4628c2ecf20Sopenharmony_ci return -ENOMEM; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci slwt = seg6_lwt_lwtunnel(newts); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci err = dst_cache_init(&slwt->cache, GFP_ATOMIC); 4678c2ecf20Sopenharmony_ci if (err) { 4688c2ecf20Sopenharmony_ci kfree(newts); 4698c2ecf20Sopenharmony_ci return err; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci memcpy(&slwt->tuninfo, tuninfo, tuninfo_len); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci newts->type = LWTUNNEL_ENCAP_SEG6; 4758c2ecf20Sopenharmony_ci newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (tuninfo->mode != SEG6_IPTUN_MODE_L2ENCAP) 4788c2ecf20Sopenharmony_ci newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci newts->headroom = seg6_lwt_headroom(tuninfo); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci *ts = newts; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return 0; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic void seg6_destroy_state(struct lwtunnel_state *lwt) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci dst_cache_destroy(&seg6_lwt_lwtunnel(lwt)->cache); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int seg6_fill_encap_info(struct sk_buff *skb, 4938c2ecf20Sopenharmony_ci struct lwtunnel_state *lwtstate) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (nla_put_srh(skb, SEG6_IPTUNNEL_SRH, tuninfo)) 4988c2ecf20Sopenharmony_ci return -EMSGSIZE; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return 0; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic int seg6_encap_nlsize(struct lwtunnel_state *lwtstate) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return nla_total_size(SEG6_IPTUN_ENCAP_SIZE(tuninfo)); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int seg6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct seg6_iptunnel_encap *a_hdr = seg6_encap_lwtunnel(a); 5138c2ecf20Sopenharmony_ci struct seg6_iptunnel_encap *b_hdr = seg6_encap_lwtunnel(b); 5148c2ecf20Sopenharmony_ci int len = SEG6_IPTUN_ENCAP_SIZE(a_hdr); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (len != SEG6_IPTUN_ENCAP_SIZE(b_hdr)) 5178c2ecf20Sopenharmony_ci return 1; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return memcmp(a_hdr, b_hdr, len); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic const struct lwtunnel_encap_ops seg6_iptun_ops = { 5238c2ecf20Sopenharmony_ci .build_state = seg6_build_state, 5248c2ecf20Sopenharmony_ci .destroy_state = seg6_destroy_state, 5258c2ecf20Sopenharmony_ci .output = seg6_output, 5268c2ecf20Sopenharmony_ci .input = seg6_input, 5278c2ecf20Sopenharmony_ci .fill_encap = seg6_fill_encap_info, 5288c2ecf20Sopenharmony_ci .get_encap_size = seg6_encap_nlsize, 5298c2ecf20Sopenharmony_ci .cmp_encap = seg6_encap_cmp, 5308c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5318c2ecf20Sopenharmony_ci}; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ciint __init seg6_iptunnel_init(void) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci return lwtunnel_encap_add_ops(&seg6_iptun_ops, LWTUNNEL_ENCAP_SEG6); 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_civoid seg6_iptunnel_exit(void) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci lwtunnel_encap_del_ops(&seg6_iptun_ops, LWTUNNEL_ENCAP_SEG6); 5418c2ecf20Sopenharmony_ci} 542