aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteffen Klassert <steffen.klassert@secunet.com>2022-04-26 09:17:16 +0200
committerSteffen Klassert <steffen.klassert@secunet.com>2022-05-11 13:12:02 +0200
commit85e63f3c91cea1a8cbf0ff6df7903ad2c1a261b2 (patch)
tree3c47741379925a9022ac48ddffa32554828f2030
parentee50346903b5e117a22e95e3aa355dac39680646 (diff)
downloadlinux-stk-nft-bulk-v5.15.tar.gz
nft: Add black to red codepathnft-bulk-v5.15
-rw-r--r--include/net/netfilter/nf_flow_table.h12
-rw-r--r--include/net/xfrm.h4
-rw-r--r--net/netfilter/nf_flow_table_ip.c140
-rw-r--r--net/netfilter/nft_flow_offload.c5
-rw-r--r--net/xfrm/xfrm_input.c365
5 files changed, 395 insertions, 131 deletions
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
index 1287228eeb50e1..7931cea2d57447 100644
--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -17,17 +17,13 @@ struct flow_offload;
enum flow_offload_tuple_dir;
struct nft_bulk_cb {
- /* This is non-zero if the packet cannot be merged with the new skb. */
- u16 flush;
-
- /* Used in ipv6_gro_receive() and foo-over-udp */
- u16 proto;
+ struct sk_buff *last;
+ struct flow_offload_tuple_rhash *tuplehash;
/* This is non-zero if the packet may be of the same flow. */
- u8 same_flow:1;
+// u8 same_flow:1;
+
- struct sk_buff *last;
- struct flow_offload_tuple_rhash *tuplehash;
};
#define NFT_BULK_CB(skb) ((struct nft_bulk_cb *)(skb)->cb)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 1434912fdf7d88..0ef49bd6daf90b 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -25,6 +25,7 @@
#include <net/ip6_fib.h>
#include <net/flow.h>
#include <net/gro_cells.h>
+#include <net/netfilter/nf_flow_table.h>
#include <linux/interrupt.h>
@@ -578,6 +579,7 @@ int xfrm_unregister_km(struct xfrm_mgr *km);
struct xfrm_tunnel_skb_cb {
union {
+ struct nft_bulk_cb ncb;
struct inet_skb_parm h4;
struct inet6_skb_parm h6;
} header;
@@ -655,6 +657,7 @@ struct xfrm_spi_skb_cb {
unsigned int daddroff;
unsigned int family;
__be32 seq;
+ __be32 spi;
};
#define XFRM_SPI_SKB_CB(__skb) ((struct xfrm_spi_skb_cb *)&((__skb)->cb[0]))
@@ -1567,6 +1570,7 @@ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu);
int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload);
int xfrm_init_state(struct xfrm_state *x);
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
+int xfrm_input_list(struct sk_buff **skb, int nexthdr, __be32 spi, int encap_type);
int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
int xfrm_trans_queue_net(struct net *net, struct sk_buff *skb,
int (*finish)(struct net *, struct sock *,
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index 80a2b30d4ce4c5..b59396cbf5134a 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -18,6 +18,7 @@
#include <net/netfilter/nf_conntrack_acct.h>
#include <net/xfrm.h>
#include <net/pkt_sched.h>
+#include <net/esp.h>
/* For layer 4 checksum field offset. */
#include <linux/tcp.h>
#include <linux/udp.h>
@@ -176,7 +177,7 @@ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
struct iphdr *iph;
if (!pskb_may_pull(skb, sizeof(*iph) + offset))
- return -1;
+ return 0;
iph = (struct iphdr *)(skb_network_header(skb) + offset);
thoff = (iph->ihl * 4);
@@ -194,15 +195,25 @@ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
case IPPROTO_UDP:
*hdrsize = sizeof(struct udphdr);
break;
+ case IPPROTO_ESP:
+ *hdrsize = sizeof(struct ip_esp_hdr);
+ break;
default:
- return -1;
+ return 0;
}
if (iph->ttl <= 1)
- return -1;
+ return 0;
if (!pskb_may_pull(skb, thoff + *hdrsize))
- return -1;
+ return 0;
+
+ if ((iph->protocol == IPPROTO_ESP)) {
+ skb_pull(skb, offset);
+ skb_reset_network_header(skb);
+ skb_set_transport_header(skb, thoff);
+ return 2;
+ }
iph = (struct iphdr *)(skb_network_header(skb) + offset);
ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
@@ -216,7 +227,7 @@ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
tuple->iifidx = dev->ifindex;
nf_flow_tuple_encap(skb, tuple);
- return 0;
+ return 1;
}
/* Based on ip_exceeds_mtu(). */
@@ -326,14 +337,88 @@ static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,
return NF_STOLEN;
}
+/*
int nft_bulk_receive_list(struct sk_buff *p, struct sk_buff *skb)
{
NFT_BULK_CB(p)->last->next = skb;
NFT_BULK_CB(p)->last = skb;
- NFT_BULK_CB(skb)->same_flow = 1;
return 0;
}
+*/
+
+static int nft_esp_bulk_receive(struct list_head *head, struct sk_buff *skb)
+{
+ const struct iphdr *iph;
+ struct sk_buff *p;
+ struct xfrm_state *x;
+ struct sec_path *sp;
+ __be32 daddr;
+ __be32 spi;
+
+ if (xfrm_offload(skb))
+ return -EINVAL;
+
+ iph = ip_hdr(skb);
+ daddr = iph->daddr;
+
+ BUG_ON(iph->protocol != IPPROTO_ESP);
+
+ spi = ip_esp_hdr(skb)->spi;
+
+ XFRM_SPI_SKB_CB(skb)->family = AF_INET;
+ XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
+ XFRM_SPI_SKB_CB(skb)->seq = ip_esp_hdr(skb)->seq_no;
+ XFRM_SPI_SKB_CB(skb)->spi = spi;
+
+ list_for_each_entry(p, head, list) {
+
+ if (daddr != ip_hdr(p)->daddr) {
+ continue;
+ }
+
+ if (spi != ip_esp_hdr(p)->spi) {
+ continue;
+ }
+
+ goto found;
+ }
+
+ goto out;
+
+found:
+ if (NFT_BULK_CB(p)->last == p)
+ skb_shinfo(p)->frag_list = skb;
+ else
+ NFT_BULK_CB(p)->last->next = skb;
+
+ NFT_BULK_CB(p)->last = skb;
+ skb_pull(skb, sizeof(*iph));
+ /* XXX: Copy or alloc new one? */
+ __skb_ext_copy(skb, p);
+
+ return 0;
+out:
+ /* First skb */
+ NFT_BULK_CB(skb)->last = skb;
+ list_add_tail(&skb->list, head);
+
+ x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
+ (xfrm_address_t *)&daddr,
+ spi, IPPROTO_ESP, AF_INET);
+ if (!x)
+ return -ENOENT;
+
+ sp = secpath_set(skb);
+ if (!sp)
+ return -ENOMEM;
+
+ sp->xvec[sp->len++] = x;
+ skb_pull(skb, sizeof(*iph));
+
+ return 0;
+}
+
static void nft_bulk_receive(struct list_head *head, struct sk_buff *skb)
{
@@ -364,25 +449,19 @@ static void nft_bulk_receive(struct list_head *head, struct sk_buff *skb)
struct iphdr *iph2;
__be32 daddr2;
- if (!NFT_BULK_CB(p)->same_flow)
- continue;
-
dst2 = skb_dst(p);
rt2 = (struct rtable *)dst2;
if (dst->dev != dst2->dev) {
- NFT_BULK_CB(p)->same_flow = 0;
continue;
}
iph2 = ip_hdr(p);
daddr2 = rt_nexthop(rt2, iph2->daddr);
if (daddr != daddr2) {
- NFT_BULK_CB(p)->same_flow = 0;
continue;
}
if (x != dst_xfrm(dst2)) {
- NFT_BULK_CB(p)->same_flow = 0;
continue;
}
@@ -398,13 +477,11 @@ found:
NFT_BULK_CB(p)->last->next = skb;
NFT_BULK_CB(p)->last = skb;
- NFT_BULK_CB(skb)->same_flow = 1;
return;
out:
/* First skb */
NFT_BULK_CB(skb)->last = skb;
- NFT_BULK_CB(skb)->same_flow = 1;
list_add_tail(&skb->list, head);
return;
@@ -423,6 +500,7 @@ __nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
unsigned int thoff, mtu;
struct iphdr *iph;
struct dst_entry *dst;
+ int ret;
skb_reset_network_header(skb);
if (!skb_transport_header_was_set(skb))
@@ -433,8 +511,9 @@ __nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
!nf_flow_skb_encap_protocol(skb, htons(ETH_P_IP), &offset))
return 0;
- if (nf_flow_tuple_ip(skb, state->in, &tuple, &hdrsize, offset) < 0)
- return 0;
+ ret = nf_flow_tuple_ip(skb, state->in, &tuple, &hdrsize, offset);
+ if (ret != 1)
+ return ret;
tuplehash = flow_offload_lookup(flow_table, &tuple);
if (tuplehash == NULL)
@@ -686,6 +765,7 @@ nf_flow_offload_ip_hook_list(void *priv, struct sk_buff *unused,
struct sk_buff *skb, *n;
struct list_head bulk_list;
struct list_head acc_list;
+ struct list_head esp_list;
struct list_head *bulk_head;
struct list_head *head = state->skb_list;
struct neighbour *neigh;
@@ -695,6 +775,7 @@ nf_flow_offload_ip_hook_list(void *priv, struct sk_buff *unused,
INIT_LIST_HEAD(&bulk_list);
INIT_LIST_HEAD(&acc_list);
+ INIT_LIST_HEAD(&esp_list);
bulk_head = per_cpu_ptr(flow_table->bulk_list, cpu);
@@ -706,12 +787,39 @@ nf_flow_offload_ip_hook_list(void *priv, struct sk_buff *unused,
list_add_tail(&skb->list, &acc_list);
else if (ret == 1)
list_add_tail(&skb->list, &bulk_list);
+ else if (ret == 2)
+ list_add_tail(&skb->list, &esp_list);
/* ret == -1: Packet dropped! */
else if (ret == -1)
kfree_skb(skb);
}
+ list_for_each_entry_safe(skb, n, &esp_list, list) {
+ skb_list_del_init(skb);
+ memset(skb->cb, 0, sizeof(struct nft_bulk_cb));
+ ret = nft_esp_bulk_receive(bulk_head, skb);
+ if (ret)
+ list_add_tail(&skb->list, &acc_list);
+ }
+
+ list_for_each_entry_safe(skb, n, bulk_head, list) {
+
+ list_del_init(&skb->list);
+
+ skb->next = skb_shinfo(skb)->frag_list;
+ skb_shinfo(skb)->frag_list = NULL;
+
+ ret = xfrm_input_list(&skb, IPPROTO_ESP, 0, -2);
+// if (ret) {
+// if (ret == 1)
+// kfree_skb_list(skb);
+// continue;
+// }
+ }
+
+ /*XXX: fwd policy check */
+
list_splice_init(&acc_list, head);
list_for_each_entry_safe(skb, n, &bulk_list, list) {
diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
index a13796fa45992e..ac4dc3d76bd1db 100644
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -256,8 +256,9 @@ static int nft_flow_route(const struct nft_pktinfo *pkt,
static bool nft_flow_offload_skip(struct sk_buff *skb, int family)
{
- if (skb_sec_path(skb))
- return true;
+ /* FIXME: */
+// if (skb_sec_path(skb))
+// return true;
if (family == NFPROTO_IPV4) {
const struct ip_options *opt;
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 3df0861d4390f6..ab69d7df917693 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -456,128 +456,52 @@ static int xfrm_inner_mode_input(struct xfrm_state *x,
return -EOPNOTSUPP;
}
-int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
+static int xfrm_input_loop(struct net *net, struct sk_buff *skb, struct xfrm_state *x, __be32 spi, int nexthdr, int encap_type)
{
const struct xfrm_state_afinfo *afinfo;
- struct net *net = dev_net(skb->dev);
const struct xfrm_mode *inner_mode;
- int err;
+ xfrm_address_t *daddr;
+ unsigned int family;
+ struct sec_path *sp;
__be32 seq;
__be32 seq_hi;
- struct xfrm_state *x = NULL;
- xfrm_address_t *daddr;
- u32 mark = skb->mark;
- unsigned int family = AF_UNSPEC;
- int decaps = 0;
+ int err = 0;
int async = 0;
bool xfrm_gro = false;
bool crypto_done = false;
struct xfrm_offload *xo = xfrm_offload(skb);
- struct sec_path *sp;
-
- if (encap_type < 0) {
- x = xfrm_input_state(skb);
-
- if (unlikely(x->km.state != XFRM_STATE_VALID)) {
- if (x->km.state == XFRM_STATE_ACQ)
- XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR);
- else
- XFRM_INC_STATS(net,
- LINUX_MIB_XFRMINSTATEINVALID);
-
- if (encap_type == -1)
- dev_put(skb->dev);
- goto drop;
- }
+ if (encap_type == -1) {
+ async = 1;
family = x->outer_mode.family;
+ seq = XFRM_SKB_CB(skb)->seq.input.low;
+ goto resume;
+ }
+
+ seq = XFRM_SPI_SKB_CB(skb)->seq;
- /* An encap_type of -1 indicates async resumption. */
- if (encap_type == -1) {
- async = 1;
- seq = XFRM_SKB_CB(skb)->seq.input.low;
- goto resume;
- }
-
- /* encap_type < -1 indicates a GRO call. */
+ /* encap_type < -1 indicates a GRO call. */
+ if (encap_type < -1) {
encap_type = 0;
- seq = XFRM_SPI_SKB_CB(skb)->seq;
-
- if (xo && (xo->flags & CRYPTO_DONE)) {
- crypto_done = true;
- family = XFRM_SPI_SKB_CB(skb)->family;
-
- if (!(xo->status & CRYPTO_SUCCESS)) {
- if (xo->status &
- (CRYPTO_TRANSPORT_AH_AUTH_FAILED |
- CRYPTO_TRANSPORT_ESP_AUTH_FAILED |
- CRYPTO_TUNNEL_AH_AUTH_FAILED |
- CRYPTO_TUNNEL_ESP_AUTH_FAILED)) {
-
- xfrm_audit_state_icvfail(x, skb,
- x->type->proto);
- x->stats.integrity_failed++;
- XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR);
- goto drop;
- }
-
- if (xo->status & CRYPTO_INVALID_PROTOCOL) {
- XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR);
- goto drop;
- }
-
- XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
- goto drop;
- }
-
- if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
- XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
- goto drop;
- }
- }
-
+ family = x->outer_mode.family;
goto lock;
}
- family = XFRM_SPI_SKB_CB(skb)->family;
-
- /* if tunnel is present override skb->mark value with tunnel i_key */
- switch (family) {
- case AF_INET:
- if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4)
- mark = be32_to_cpu(XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4->parms.i_key);
- break;
- case AF_INET6:
- if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6)
- mark = be32_to_cpu(XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6->parms.i_key);
- break;
- }
-
- sp = secpath_set(skb);
- if (!sp) {
- XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR);
- goto drop;
- }
-
- seq = 0;
- if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
- secpath_reset(skb);
- XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
- goto drop;
- }
-
daddr = (xfrm_address_t *)(skb_network_header(skb) +
XFRM_SPI_SKB_CB(skb)->daddroff);
+
+ family = XFRM_SPI_SKB_CB(skb)->family;
+
do {
sp = skb_sec_path(skb);
- if (sp->len == XFRM_MAX_DEPTH) {
+ if (sp && sp->len == XFRM_MAX_DEPTH) {
secpath_reset(skb);
XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
goto drop;
}
- x = xfrm_state_lookup(net, mark, daddr, spi, nexthdr, family);
+ x = xfrm_state_lookup(net, skb->mark, daddr, spi, nexthdr, family);
if (x == NULL) {
secpath_reset(skb);
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
@@ -587,7 +511,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
skb->mark = xfrm_smark_get(skb->mark, x);
- sp->xvec[sp->len++] = x;
+ if (sp)
+ sp->xvec[sp->len++] = x;
skb_dst_force(skb);
if (!skb_dst(skb)) {
@@ -689,10 +614,8 @@ resume:
goto drop;
}
- if (x->outer_mode.flags & XFRM_MODE_FLAG_TUNNEL) {
- decaps = 1;
+ if (x->outer_mode.flags & XFRM_MODE_FLAG_TUNNEL)
break;
- }
/*
* We need the inner address. However, we only get here for
@@ -715,12 +638,11 @@ resume:
nf_reset_ct(skb);
- if (decaps) {
+ if (x->outer_mode.flags & XFRM_MODE_FLAG_TUNNEL) {
sp = skb_sec_path(skb);
if (sp)
sp->olen = 0;
skb_dst_drop(skb);
- gro_cells_receive(&gro_cells, skb);
return 0;
} else {
xo = xfrm_offload(skb);
@@ -738,16 +660,249 @@ resume:
if (sp)
sp->olen = 0;
skb_dst_drop(skb);
- gro_cells_receive(&gro_cells, skb);
return err;
}
-
- return err;
}
+ return 0;
+
drop_unlock:
spin_unlock(&x->lock);
drop:
+ return -EINVAL;
+}
+int xfrm_input_list(struct sk_buff **skbp, int nexthdr, __be32 spi, int encap_type)
+{
+ struct sk_buff *skb2, *nskb, *pskb = NULL;
+ struct sk_buff *skb = *skbp;
+ struct net *net = dev_net(skb->dev);
+ int err;
+ __be32 seq;
+ struct xfrm_state *x = NULL;
+ unsigned int family = AF_UNSPEC;
+ bool crypto_done = false;
+ struct xfrm_offload *xo = xfrm_offload(skb);
+ struct list_head head;
+
+ x = xfrm_input_state(skb);
+
+ if (unlikely(x->km.state != XFRM_STATE_VALID)) {
+ if (x->km.state == XFRM_STATE_ACQ)
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR);
+ else
+ XFRM_INC_STATS(net,
+ LINUX_MIB_XFRMINSTATEINVALID);
+
+ if (encap_type == -1)
+ dev_put(skb->dev);
+ goto drop;
+ }
+
+ family = x->outer_mode.family;
+
+ /* An encap_type of -1 indicates async resumption. */
+ if (encap_type == -1) {
+ seq = XFRM_SKB_CB(skb)->seq.input.low;
+ goto loop;
+ }
+
+ seq = XFRM_SPI_SKB_CB(skb)->seq;
+
+ if (xo && (xo->flags & CRYPTO_DONE)) {
+ crypto_done = true;
+ family = XFRM_SPI_SKB_CB(skb)->family;
+
+ if (!(xo->status & CRYPTO_SUCCESS)) {
+ if (xo->status &
+ (CRYPTO_TRANSPORT_AH_AUTH_FAILED |
+ CRYPTO_TRANSPORT_ESP_AUTH_FAILED |
+ CRYPTO_TUNNEL_AH_AUTH_FAILED |
+ CRYPTO_TUNNEL_ESP_AUTH_FAILED)) {
+
+ xfrm_audit_state_icvfail(x, skb,
+ x->type->proto);
+ x->stats.integrity_failed++;
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR);
+ goto drop;
+ }
+
+ if (xo->status & CRYPTO_INVALID_PROTOCOL) {
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR);
+ goto drop;
+ }
+
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
+ goto drop;
+ }
+
+ /* XXX: Do we need this here? */
+ if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
+ goto drop;
+ }
+ }
+
+ family = XFRM_SPI_SKB_CB(skb)->family;
+
+ /* HERE */
+loop:
+ INIT_LIST_HEAD(&head);
+ skb_list_walk_safe(skb, skb2, nskb) {
+
+
+ skb_mark_not_on_list(skb2);
+
+ err = xfrm_input_loop(net, skb2, x, nexthdr, spi, encap_type);
+// if (!err) {
+// skb2->next = nskb;
+// } else if (err != -EINPROGRESS) {
+// skb2->next = nskb;
+// kfree_skb_list(skb2);
+// return err;
+// } else {
+// if (skb == skb2)
+// skb = nskb;
+// else
+// pskb->next = nskb;
+//
+// continue;
+// }
+//
+// pskb = skb2;
+
+ if (err) {
+ if (err != -EINPROGRESS)
+ kfree_skb(skb2);
+ continue;
+ }
+
+ list_add_tail(&skb2->list, &head);
+ }
+
+ /* XXX: Recursive call! */
+ netif_receive_skb_list(&head);
+ return 0;
+
+drop:
+ xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1);
+ kfree_skb_list(skb);
+ return 0;
+}
+EXPORT_SYMBOL(xfrm_input_list);
+
+int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
+{
+ struct net *net = dev_net(skb->dev);
+ int err;
+ __be32 seq;
+ struct xfrm_state *x = NULL;
+ u32 mark = skb->mark;
+ unsigned int family = AF_UNSPEC;
+ int async = 0;
+ bool crypto_done = false;
+ struct xfrm_offload *xo = xfrm_offload(skb);
+ struct sec_path *sp;
+
+ if (encap_type < 0) {
+ x = xfrm_input_state(skb);
+
+ if (unlikely(x->km.state != XFRM_STATE_VALID)) {
+ if (x->km.state == XFRM_STATE_ACQ)
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR);
+ else
+ XFRM_INC_STATS(net,
+ LINUX_MIB_XFRMINSTATEINVALID);
+
+ if (encap_type == -1)
+ dev_put(skb->dev);
+ goto drop;
+ }
+
+ family = x->outer_mode.family;
+
+ /* An encap_type of -1 indicates async resumption. */
+ if (encap_type == -1) {
+ async = 1;
+ seq = XFRM_SKB_CB(skb)->seq.input.low;
+ goto loop;
+ }
+
+ seq = XFRM_SPI_SKB_CB(skb)->seq;
+
+ if (xo && (xo->flags & CRYPTO_DONE)) {
+ crypto_done = true;
+ family = XFRM_SPI_SKB_CB(skb)->family;
+
+ if (!(xo->status & CRYPTO_SUCCESS)) {
+ if (xo->status &
+ (CRYPTO_TRANSPORT_AH_AUTH_FAILED |
+ CRYPTO_TRANSPORT_ESP_AUTH_FAILED |
+ CRYPTO_TUNNEL_AH_AUTH_FAILED |
+ CRYPTO_TUNNEL_ESP_AUTH_FAILED)) {
+
+ xfrm_audit_state_icvfail(x, skb,
+ x->type->proto);
+ x->stats.integrity_failed++;
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR);
+ goto drop;
+ }
+
+ if (xo->status & CRYPTO_INVALID_PROTOCOL) {
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR);
+ goto drop;
+ }
+
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
+ goto drop;
+ }
+
+ if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
+ goto drop;
+ }
+ }
+
+ goto loop;
+ }
+
+ family = XFRM_SPI_SKB_CB(skb)->family;
+
+ /* if tunnel is present override skb->mark value with tunnel i_key */
+ switch (family) {
+ case AF_INET:
+ if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4)
+ mark = be32_to_cpu(XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4->parms.i_key);
+ break;
+ case AF_INET6:
+ if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6)
+ mark = be32_to_cpu(XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6->parms.i_key);
+ break;
+ }
+
+ sp = secpath_set(skb);
+ if (!sp) {
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR);
+ goto drop;
+ }
+
+ seq = 0;
+ if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
+ secpath_reset(skb);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
+ goto drop;
+ }
+
+ XFRM_SPI_SKB_CB(skb)->seq = seq;
+loop:
+ err = xfrm_input_loop(net, skb, x, spi, nexthdr, encap_type);
+ if (err)
+ goto drop;
+
+ gro_cells_receive(&gro_cells, skb);
+
+ return err;
+
+drop:
xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1);
kfree_skb(skb);
return 0;