162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * xfrm6_input.c: based on net/ipv4/xfrm4_input.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Mitsuru KANDA @USAGI 762306a36Sopenharmony_ci * Kazunori MIYAZAWA @USAGI 862306a36Sopenharmony_ci * Kunihiro Ishiguro <kunihiro@ipinfusion.com> 962306a36Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI 1062306a36Sopenharmony_ci * IPv6 support 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/netfilter.h> 1662306a36Sopenharmony_ci#include <linux/netfilter_ipv6.h> 1762306a36Sopenharmony_ci#include <net/ipv6.h> 1862306a36Sopenharmony_ci#include <net/xfrm.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciint xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi, 2162306a36Sopenharmony_ci struct ip6_tnl *t) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = t; 2462306a36Sopenharmony_ci XFRM_SPI_SKB_CB(skb)->family = AF_INET6; 2562306a36Sopenharmony_ci XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr); 2662306a36Sopenharmony_ci return xfrm_input(skb, nexthdr, spi, 0); 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ciEXPORT_SYMBOL(xfrm6_rcv_spi); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int xfrm6_transport_finish2(struct net *net, struct sock *sk, 3162306a36Sopenharmony_ci struct sk_buff *skb) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci if (xfrm_trans_queue(skb, ip6_rcv_finish)) { 3462306a36Sopenharmony_ci kfree_skb(skb); 3562306a36Sopenharmony_ci return NET_RX_DROP; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ciint xfrm6_transport_finish(struct sk_buff *skb, int async) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct xfrm_offload *xo = xfrm_offload(skb); 4462306a36Sopenharmony_ci int nhlen = skb->data - skb_network_header(skb); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci skb_network_header(skb)[IP6CB(skb)->nhoff] = 4762306a36Sopenharmony_ci XFRM_MODE_SKB_CB(skb)->protocol; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#ifndef CONFIG_NETFILTER 5062306a36Sopenharmony_ci if (!async) 5162306a36Sopenharmony_ci return 1; 5262306a36Sopenharmony_ci#endif 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci __skb_push(skb, nhlen); 5562306a36Sopenharmony_ci ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); 5662306a36Sopenharmony_ci skb_postpush_rcsum(skb, skb_network_header(skb), nhlen); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (xo && (xo->flags & XFRM_GRO)) { 5962306a36Sopenharmony_ci skb_mac_header_rebuild(skb); 6062306a36Sopenharmony_ci skb_reset_transport_header(skb); 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, 6562306a36Sopenharmony_ci dev_net(skb->dev), NULL, skb, skb->dev, NULL, 6662306a36Sopenharmony_ci xfrm6_transport_finish2); 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* If it's a keepalive packet, then just eat it. 7162306a36Sopenharmony_ci * If it's an encapsulated packet, then pass it to the 7262306a36Sopenharmony_ci * IPsec xfrm input. 7362306a36Sopenharmony_ci * Returns 0 if skb passed to xfrm or was dropped. 7462306a36Sopenharmony_ci * Returns >0 if skb should be passed to UDP. 7562306a36Sopenharmony_ci * Returns <0 if skb should be resubmitted (-ret is protocol) 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ciint xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct udp_sock *up = udp_sk(sk); 8062306a36Sopenharmony_ci struct udphdr *uh; 8162306a36Sopenharmony_ci struct ipv6hdr *ip6h; 8262306a36Sopenharmony_ci int len; 8362306a36Sopenharmony_ci int ip6hlen = sizeof(struct ipv6hdr); 8462306a36Sopenharmony_ci __u8 *udpdata; 8562306a36Sopenharmony_ci __be32 *udpdata32; 8662306a36Sopenharmony_ci u16 encap_type; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) 8962306a36Sopenharmony_ci return xfrm4_udp_encap_rcv(sk, skb); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci encap_type = READ_ONCE(up->encap_type); 9262306a36Sopenharmony_ci /* if this is not encapsulated socket, then just return now */ 9362306a36Sopenharmony_ci if (!encap_type) 9462306a36Sopenharmony_ci return 1; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* If this is a paged skb, make sure we pull up 9762306a36Sopenharmony_ci * whatever data we need to look at. */ 9862306a36Sopenharmony_ci len = skb->len - sizeof(struct udphdr); 9962306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct udphdr) + min(len, 8))) 10062306a36Sopenharmony_ci return 1; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* Now we can get the pointers */ 10362306a36Sopenharmony_ci uh = udp_hdr(skb); 10462306a36Sopenharmony_ci udpdata = (__u8 *)uh + sizeof(struct udphdr); 10562306a36Sopenharmony_ci udpdata32 = (__be32 *)udpdata; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci switch (encap_type) { 10862306a36Sopenharmony_ci default: 10962306a36Sopenharmony_ci case UDP_ENCAP_ESPINUDP: 11062306a36Sopenharmony_ci /* Check if this is a keepalive packet. If so, eat it. */ 11162306a36Sopenharmony_ci if (len == 1 && udpdata[0] == 0xff) { 11262306a36Sopenharmony_ci goto drop; 11362306a36Sopenharmony_ci } else if (len > sizeof(struct ip_esp_hdr) && udpdata32[0] != 0) { 11462306a36Sopenharmony_ci /* ESP Packet without Non-ESP header */ 11562306a36Sopenharmony_ci len = sizeof(struct udphdr); 11662306a36Sopenharmony_ci } else 11762306a36Sopenharmony_ci /* Must be an IKE packet.. pass it through */ 11862306a36Sopenharmony_ci return 1; 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci case UDP_ENCAP_ESPINUDP_NON_IKE: 12162306a36Sopenharmony_ci /* Check if this is a keepalive packet. If so, eat it. */ 12262306a36Sopenharmony_ci if (len == 1 && udpdata[0] == 0xff) { 12362306a36Sopenharmony_ci goto drop; 12462306a36Sopenharmony_ci } else if (len > 2 * sizeof(u32) + sizeof(struct ip_esp_hdr) && 12562306a36Sopenharmony_ci udpdata32[0] == 0 && udpdata32[1] == 0) { 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* ESP Packet with Non-IKE marker */ 12862306a36Sopenharmony_ci len = sizeof(struct udphdr) + 2 * sizeof(u32); 12962306a36Sopenharmony_ci } else 13062306a36Sopenharmony_ci /* Must be an IKE packet.. pass it through */ 13162306a36Sopenharmony_ci return 1; 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* At this point we are sure that this is an ESPinUDP packet, 13662306a36Sopenharmony_ci * so we need to remove 'len' bytes from the packet (the UDP 13762306a36Sopenharmony_ci * header and optional ESP marker bytes) and then modify the 13862306a36Sopenharmony_ci * protocol to ESP, and then call into the transform receiver. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci if (skb_unclone(skb, GFP_ATOMIC)) 14162306a36Sopenharmony_ci goto drop; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Now we can update and verify the packet length... */ 14462306a36Sopenharmony_ci ip6h = ipv6_hdr(skb); 14562306a36Sopenharmony_ci ip6h->payload_len = htons(ntohs(ip6h->payload_len) - len); 14662306a36Sopenharmony_ci if (skb->len < ip6hlen + len) { 14762306a36Sopenharmony_ci /* packet is too small!?! */ 14862306a36Sopenharmony_ci goto drop; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* pull the data buffer up to the ESP header and set the 15262306a36Sopenharmony_ci * transport header to point to ESP. Keep UDP on the stack 15362306a36Sopenharmony_ci * for later. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci __skb_pull(skb, len); 15662306a36Sopenharmony_ci skb_reset_transport_header(skb); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* process ESP */ 15962306a36Sopenharmony_ci return xfrm6_rcv_encap(skb, IPPROTO_ESP, 0, encap_type); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cidrop: 16262306a36Sopenharmony_ci kfree_skb(skb); 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ciint xfrm6_rcv_tnl(struct sk_buff *skb, struct ip6_tnl *t) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci return xfrm6_rcv_spi(skb, skb_network_header(skb)[IP6CB(skb)->nhoff], 16962306a36Sopenharmony_ci 0, t); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ciEXPORT_SYMBOL(xfrm6_rcv_tnl); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciint xfrm6_rcv(struct sk_buff *skb) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci return xfrm6_rcv_tnl(skb, NULL); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ciEXPORT_SYMBOL(xfrm6_rcv); 17862306a36Sopenharmony_ciint xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr, 17962306a36Sopenharmony_ci xfrm_address_t *saddr, u8 proto) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 18262306a36Sopenharmony_ci struct xfrm_state *x = NULL; 18362306a36Sopenharmony_ci struct sec_path *sp; 18462306a36Sopenharmony_ci int i = 0; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci sp = secpath_set(skb); 18762306a36Sopenharmony_ci if (!sp) { 18862306a36Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR); 18962306a36Sopenharmony_ci goto drop; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (1 + sp->len == XFRM_MAX_DEPTH) { 19362306a36Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR); 19462306a36Sopenharmony_ci goto drop; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 19862306a36Sopenharmony_ci xfrm_address_t *dst, *src; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci switch (i) { 20162306a36Sopenharmony_ci case 0: 20262306a36Sopenharmony_ci dst = daddr; 20362306a36Sopenharmony_ci src = saddr; 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci case 1: 20662306a36Sopenharmony_ci /* lookup state with wild-card source address */ 20762306a36Sopenharmony_ci dst = daddr; 20862306a36Sopenharmony_ci src = (xfrm_address_t *)&in6addr_any; 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci default: 21162306a36Sopenharmony_ci /* lookup state with wild-card addresses */ 21262306a36Sopenharmony_ci dst = (xfrm_address_t *)&in6addr_any; 21362306a36Sopenharmony_ci src = (xfrm_address_t *)&in6addr_any; 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci x = xfrm_state_lookup_byaddr(net, skb->mark, dst, src, proto, AF_INET6); 21862306a36Sopenharmony_ci if (!x) 21962306a36Sopenharmony_ci continue; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci spin_lock(&x->lock); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if ((!i || (x->props.flags & XFRM_STATE_WILDRECV)) && 22462306a36Sopenharmony_ci likely(x->km.state == XFRM_STATE_VALID) && 22562306a36Sopenharmony_ci !xfrm_state_check_expire(x)) { 22662306a36Sopenharmony_ci spin_unlock(&x->lock); 22762306a36Sopenharmony_ci if (x->type->input(x, skb) > 0) { 22862306a36Sopenharmony_ci /* found a valid state */ 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci } else 23262306a36Sopenharmony_ci spin_unlock(&x->lock); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci xfrm_state_put(x); 23562306a36Sopenharmony_ci x = NULL; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (!x) { 23962306a36Sopenharmony_ci XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES); 24062306a36Sopenharmony_ci xfrm_audit_state_notfound_simple(skb, AF_INET6); 24162306a36Sopenharmony_ci goto drop; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci sp->xvec[sp->len++] = x; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci spin_lock(&x->lock); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci x->curlft.bytes += skb->len; 24962306a36Sopenharmony_ci x->curlft.packets++; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci spin_unlock(&x->lock); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return 1; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cidrop: 25662306a36Sopenharmony_ci return -1; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ciEXPORT_SYMBOL(xfrm6_input_addr); 259