--- zzzz-none-000/linux-3.10.107/net/xfrm/xfrm_output.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/net/xfrm/xfrm_output.c 2021-02-04 17:41:59.000000000 +0000 @@ -19,7 +19,12 @@ #include #include -static int xfrm_output2(struct sk_buff *skb); +#ifdef CONFIG_AVM_PA +#include +static struct avm_pa_dev_info xfrm_out_dev_info; +#endif + +static int xfrm_output2(struct net *net, struct sock *sk, struct sk_buff *skb); static int xfrm_skb_check_space(struct sk_buff *skb) { @@ -38,6 +43,18 @@ return pskb_expand_head(skb, nhead, ntail, GFP_ATOMIC); } +/* Children define the path of the packet through the + * Linux networking. Thus, destinations are stackable. + */ + +static struct dst_entry *skb_dst_pop(struct sk_buff *skb) +{ + struct dst_entry *child = dst_clone(skb_dst(skb)->child); + + skb_dst_drop(skb); + return child; +} + static int xfrm_output_one(struct sk_buff *skb, int err) { struct dst_entry *dst = skb_dst(skb); @@ -89,7 +106,7 @@ err = x->type->output(x, skb); if (err == -EINPROGRESS) - goto out_exit; + goto out; resume: if (err) { @@ -107,31 +124,62 @@ x = dst->xfrm; } while (x && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL)); - err = 0; + return 0; -out_exit: - return err; error: spin_unlock_bh(&x->lock); error_nolock: kfree_skb(skb); - goto out_exit; +out: + return err; } int xfrm_output_resume(struct sk_buff *skb, int err) { + struct net *net = xs_net(skb_dst(skb)->xfrm); + while (likely((err = xfrm_output_one(skb, err)) == 0)) { - nf_reset(skb); + nf_reset_no_generic_ct(skb); - err = skb_dst(skb)->ops->local_out(skb); - if (unlikely(err != 1)) - goto out; +#ifdef CONFIG_AVM_PA + /* Now encrypted, try to accelerate the other half. + * Reset ingress_pid_handle to allow for a secondary session. + */ + if (!skb_dst(skb)->xfrm) { + 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 encrypted packet. Retry + * acceleration even someone said to not accelerate + * the decrypted packet. + */ + AVM_PKT_INFO(skb)->do_not_accelerate = 0; + /* Record local_out_pid_handle, this will enable the general + * call to avm_pa_local_out_receive() down the road + * to open a session. + */ + AVM_PKT_INFO(skb)->local_out_pid_handle = xfrm_out_dev_info.pid_handle; + } + /* We want to invert the likely indication because avm_pa + * very likely accelerates the packet. We take the opportunity + * and do a static call optimization for __ip_local_out as well. + */ + if (skb_dst(skb)->ops->local_out == __ip_local_out) { + err = __ip_local_out(net, skb->sk, skb); + if (likely(err != 1)) /* likely accelerated */ + goto out; + } else +#endif + { + err = skb_dst(skb)->ops->local_out(net, skb->sk, skb); + if (unlikely(err != 1)) + goto out; + } if (!skb_dst(skb)->xfrm) - return dst_output(skb); + return dst_output(net, skb->sk, skb); err = nf_hook(skb_dst(skb)->ops->family, - NF_INET_POST_ROUTING, skb, + NF_INET_POST_ROUTING, net, skb->sk, skb, NULL, skb_dst(skb)->dev, xfrm_output2); if (unlikely(err != 1)) goto out; @@ -145,33 +193,33 @@ } EXPORT_SYMBOL_GPL(xfrm_output_resume); -static int xfrm_output2(struct sk_buff *skb) +static int xfrm_output2(struct net *net, struct sock *sk, struct sk_buff *skb) { return xfrm_output_resume(skb, 1); } -static int xfrm_output_gso(struct sk_buff *skb) +static int xfrm_output_gso(struct net *net, struct sock *sk, struct sk_buff *skb) { struct sk_buff *segs; + BUILD_BUG_ON(sizeof(*IPCB(skb)) > SKB_SGO_CB_OFFSET); + BUILD_BUG_ON(sizeof(*IP6CB(skb)) > SKB_SGO_CB_OFFSET); segs = skb_gso_segment(skb, 0); kfree_skb(skb); if (IS_ERR(segs)) return PTR_ERR(segs); + if (segs == NULL) + return -EINVAL; do { struct sk_buff *nskb = segs->next; int err; segs->next = NULL; - err = xfrm_output2(segs); + err = xfrm_output2(net, sk, segs); if (unlikely(err)) { - while ((segs = nskb)) { - nskb = segs->next; - segs->next = NULL; - kfree_skb(segs); - } + kfree_skb_list(nskb); return err; } @@ -181,13 +229,17 @@ return 0; } -int xfrm_output(struct sk_buff *skb) +int xfrm_output(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); int err; +#ifdef CONFIG_AVM_PA + avm_pa_add_xfrm_session(&xfrm_out_dev_info, skb, skb_dst(skb)->xfrm); +#endif + if (skb_is_gso(skb)) - return xfrm_output_gso(skb); + return xfrm_output_gso(net, sk, skb); if (skb->ip_summed == CHECKSUM_PARTIAL) { err = skb_checksum_help(skb); @@ -198,8 +250,9 @@ } } - return xfrm_output2(skb); + return xfrm_output2(net, sk, skb); } +EXPORT_SYMBOL_GPL(xfrm_output); int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb) { @@ -214,6 +267,58 @@ return -EAFNOSUPPORT; return inner_mode->afinfo->extract_output(x, skb); } - -EXPORT_SYMBOL_GPL(xfrm_output); EXPORT_SYMBOL_GPL(xfrm_inner_extract_output); + +void xfrm_local_error(struct sk_buff *skb, int mtu) +{ + unsigned int proto; + struct xfrm_state_afinfo *afinfo; + + if (skb->protocol == htons(ETH_P_IP)) + proto = AF_INET; + else if (skb->protocol == htons(ETH_P_IPV6)) + proto = AF_INET6; + else + return; + + afinfo = xfrm_state_get_afinfo(proto); + if (!afinfo) + return; + + afinfo->local_error(skb, mtu); + xfrm_state_put_afinfo(afinfo); +} +EXPORT_SYMBOL_GPL(xfrm_local_error); + +#ifdef CONFIG_AVM_PA +static void xfrm_out_transmit(void *arg, struct sk_buff *skb) +{ + int ret; + + ret = xfrm_output(NULL, skb); + if (unlikely(ret < 0)) + net_err_ratelimited("%s failed: %d\n", __func__, ret); +} + +static void __init xfrm_output_avm_pa_register(void) +{ + struct avm_pa_pid_cfg cfg = {0}; + int ret; + + snprintf(cfg.name, sizeof(cfg.name), "xfrm_out"); + cfg.framing = avm_pa_framing_ipdev; + cfg.default_mtu = 0xffff; + cfg.tx_func = xfrm_out_transmit; + ret = avm_pa_dev_pid_register(&xfrm_out_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_output_init(void) +{ +#ifdef CONFIG_AVM_PA + xfrm_output_avm_pa_register(); +#endif +}