--- zzzz-none-000/linux-3.10.107/net/xfrm/xfrm_input.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/net/xfrm/xfrm_input.c 2021-02-04 17:41:59.000000000 +0000 @@ -13,9 +13,91 @@ #include #include #include +#include +#include + +#ifdef CONFIG_AVM_PA +#include +static struct avm_pa_dev_info xfrm_in_dev_info; +#endif static struct kmem_cache *secpath_cachep __read_mostly; +static DEFINE_SPINLOCK(xfrm_input_afinfo_lock); +static struct xfrm_input_afinfo __rcu *xfrm_input_afinfo[NPROTO]; + +int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo) +{ + int err = 0; + + if (unlikely(afinfo == NULL)) + return -EINVAL; + if (unlikely(afinfo->family >= NPROTO)) + return -EAFNOSUPPORT; + spin_lock_bh(&xfrm_input_afinfo_lock); + if (unlikely(xfrm_input_afinfo[afinfo->family] != NULL)) + err = -EEXIST; + else + rcu_assign_pointer(xfrm_input_afinfo[afinfo->family], afinfo); + spin_unlock_bh(&xfrm_input_afinfo_lock); + return err; +} +EXPORT_SYMBOL(xfrm_input_register_afinfo); + +int xfrm_input_unregister_afinfo(struct xfrm_input_afinfo *afinfo) +{ + int err = 0; + + if (unlikely(afinfo == NULL)) + return -EINVAL; + if (unlikely(afinfo->family >= NPROTO)) + return -EAFNOSUPPORT; + spin_lock_bh(&xfrm_input_afinfo_lock); + if (likely(xfrm_input_afinfo[afinfo->family] != NULL)) { + if (unlikely(xfrm_input_afinfo[afinfo->family] != afinfo)) + err = -EINVAL; + else + RCU_INIT_POINTER(xfrm_input_afinfo[afinfo->family], NULL); + } + spin_unlock_bh(&xfrm_input_afinfo_lock); + synchronize_rcu(); + return err; +} +EXPORT_SYMBOL(xfrm_input_unregister_afinfo); + +static struct xfrm_input_afinfo *xfrm_input_get_afinfo(unsigned int family) +{ + struct xfrm_input_afinfo *afinfo; + + if (unlikely(family >= NPROTO)) + return NULL; + rcu_read_lock(); + afinfo = rcu_dereference(xfrm_input_afinfo[family]); + if (unlikely(!afinfo)) + rcu_read_unlock(); + return afinfo; +} + +static void xfrm_input_put_afinfo(struct xfrm_input_afinfo *afinfo) +{ + rcu_read_unlock(); +} + +static int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, u8 protocol, + int err) +{ + int ret; + struct xfrm_input_afinfo *afinfo = xfrm_input_get_afinfo(family); + + if (!afinfo) + return -EAFNOSUPPORT; + + ret = afinfo->callback(skb, protocol, err); + xfrm_input_put_afinfo(afinfo); + + return ret; +} + void __secpath_destroy(struct sec_path *sp) { int i; @@ -67,7 +149,7 @@ case IPPROTO_COMP: if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr))) return -EINVAL; - *spi = htonl(ntohs(*(__be16*)(skb_transport_header(skb) + 2))); + *spi = htonl(ntohs(*(__be16 *)(skb_transport_header(skb) + 2))); *seq = 0; return 0; default: @@ -77,8 +159,8 @@ if (!pskb_may_pull(skb, hlen)) return -EINVAL; - *spi = *(__be32*)(skb_transport_header(skb) + offset); - *seq = *(__be32*)(skb_transport_header(skb) + offset_seq); + *spi = net_hdr_word(skb_transport_header(skb) + offset); + *seq = net_hdr_word(skb_transport_header(skb) + offset_seq); return 0; } @@ -108,9 +190,10 @@ int err; __be32 seq; __be32 seq_hi; - struct xfrm_state *x; + struct xfrm_state *x = NULL; xfrm_address_t *daddr; struct xfrm_mode *inner_mode; + u32 mark = skb->mark; unsigned int family; int decaps = 0; int async = 0; @@ -120,9 +203,26 @@ async = 1; x = xfrm_input_state(skb); seq = XFRM_SKB_CB(skb)->seq.input.low; + family = x->outer_mode->afinfo->family; goto resume; } + daddr = (xfrm_address_t *)(skb_network_header(skb) + + XFRM_SPI_SKB_CB(skb)->daddroff); + family = XFRM_SPI_SKB_CB(skb)->family; + + /* if tunnel is present override skb->mark value with tunnel i_key */ + if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4) { + switch (family) { + case AF_INET: + mark = be32_to_cpu(XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4->parms.i_key); + break; + case AF_INET6: + mark = be32_to_cpu(XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6->parms.i_key); + break; + } + } + /* Allocate new secpath or COW existing one. */ if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { struct sec_path *sp; @@ -137,10 +237,6 @@ skb->sp = sp; } - daddr = (xfrm_address_t *)(skb_network_header(skb) + - XFRM_SPI_SKB_CB(skb)->daddroff); - family = XFRM_SPI_SKB_CB(skb)->family; - seq = 0; if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); @@ -153,7 +249,7 @@ goto drop; } - x = xfrm_state_lookup(net, skb->mark, daddr, spi, nexthdr, family); + x = xfrm_state_lookup(net, mark, daddr, spi, nexthdr, family); if (x == NULL) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES); xfrm_audit_state_notfound(skb, family, spi, seq); @@ -163,8 +259,13 @@ skb->sp->xvec[skb->sp->len++] = x; spin_lock(&x->lock); + if (unlikely(x->km.state != XFRM_STATE_VALID)) { - XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEINVALID); + if (x->km.state == XFRM_STATE_ACQ) + XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR); + else + XFRM_INC_STATS(net, + LINUX_MIB_XFRMINSTATEINVALID); goto drop_unlock; } @@ -185,19 +286,32 @@ spin_unlock(&x->lock); + if (xfrm_tunnel_check(skb, x, family)) { + XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); + goto drop; + } + seq_hi = htonl(xfrm_replay_seqhi(x, seq)); XFRM_SKB_CB(skb)->seq.input.low = seq; XFRM_SKB_CB(skb)->seq.input.hi = seq_hi; skb_dst_force(skb); + dev_hold(skb->dev); + +#ifdef CONFIG_AVM_PA + avm_pa_add_local_session(skb, NULL); + /* xfrm session is better, allow to take over (if pid is registered) */ + avm_pa_add_xfrm_session(&xfrm_in_dev_info, skb, x); +#endif nexthdr = x->type->input(x, skb); if (nexthdr == -EINPROGRESS) return 0; - resume: + dev_put(skb->dev); + spin_lock(&x->lock); if (nexthdr <= 0) { if (nexthdr == -EBADMSG) { @@ -230,8 +344,10 @@ if (x->sel.family == AF_UNSPEC) { inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol); - if (inner_mode == NULL) + if (inner_mode == NULL) { + XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); goto drop; + } } if (inner_mode->input(x, skb)) { @@ -239,6 +355,23 @@ goto drop; } +#ifdef CONFIG_AVM_PA + /* Now decrypted and past inner_mode->input() which fixes up + * skb offsets for the inner headers. Try to accelerate the + * other half. Reset ingress_pid_handle to allow for a second + * session. + */ + AVM_PKT_INFO(skb)->ingress_pid_handle = 0; + /* The packet is wildy different now and we are going to + * open a separate session for the decrypted packet. Retry + * acceleration even someone said to not accelerate + * the encrypted packet. + */ + AVM_PKT_INFO(skb)->do_not_accelerate = 0; + if (avm_pa_dev_receive(&xfrm_in_dev_info, skb) == AVM_PA_RX_STOLEN) + return 0; +#endif + if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) { decaps = 1; break; @@ -258,7 +391,11 @@ } } while (!err); - nf_reset(skb); + err = xfrm_rcv_cb(skb, family, x->type->proto, 0); + if (err) + goto drop; + + nf_reset_no_generic_ct(skb); if (decaps) { skb_dst_drop(skb); @@ -271,6 +408,7 @@ drop_unlock: spin_unlock(&x->lock); drop: + xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1); kfree_skb(skb); return 0; } @@ -282,10 +420,60 @@ } EXPORT_SYMBOL(xfrm_input_resume); +#ifdef CONFIG_AVM_PA +static void xfrm_in_transmit(void *arg, struct sk_buff *skb) +{ + int ret; + struct xfrm_state *x = arg; + + if (skb->protocol == htons(ETH_P_IP)) { + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; + XFRM_SPI_SKB_CB(skb)->family = AF_INET; + XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); + } else if (skb->protocol == htons(ETH_P_IPV6)) { + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; + XFRM_SPI_SKB_CB(skb)->family = AF_INET6; + XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr); + } else { + net_err_ratelimited("%s: unhandlet protocol %d\n", + __func__, skb->protocol); + kfree_skb(skb); + return; + } + + /* esp_input expects skb->data at esp header because normally + * packets come via ip_local_deliver_finish() + */ + __skb_pull(skb, skb_transport_offset(skb)); + ret = xfrm_input(skb, x->id.proto, 0, 0); + + if (unlikely(ret != 0)) + net_err_ratelimited("%s failed: %d\n", __func__, ret); +} + +static void __init xfrm_input_avm_pa_register(void) +{ + struct avm_pa_pid_cfg cfg = {0}; + int ret; + + snprintf(cfg.name, sizeof(cfg.name), "xfrm_in"); + cfg.framing = avm_pa_framing_ipdev; + cfg.default_mtu = 0xffff; + cfg.tx_func = xfrm_in_transmit; + ret = avm_pa_dev_pid_register(&xfrm_in_dev_info, &cfg); + if (ret < 0) + pr_err("%s: failed to register avm_pa pid %s: %d\n", + __func__, cfg.name, ret); +} +#endif + void __init xfrm_input_init(void) { secpath_cachep = kmem_cache_create("secpath_cache", sizeof(struct sec_path), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); +#ifdef CONFIG_AVM_PA + xfrm_input_avm_pa_register(); +#endif }