int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb) { int err; struct ip_comp_hdr *ipch; struct ipcomp_data *ipcd = x->data; if (skb->len < ipcd->threshold) { goto out_ok; } if (skb_linearize_cow(skb)) goto out_ok; err = ipcomp_compress(x, skb); if (err) { goto out_ok; } ipch = ip_comp_hdr(skb); ipch->nexthdr = *skb_mac_header(skb); ipch->flags = 0; ipch->cpi = htons((u16 )ntohl(x->id.spi)); *skb_mac_header(skb) = IPPROTO_COMP; out_ok: skb_push(skb, -skb_network_offset(skb)); return 0; }
static int ipcomp_output(struct sk_buff *skb) { int err; struct dst_entry *dst = skb->dst; struct xfrm_state *x = dst->xfrm; struct iphdr *iph, *top_iph; struct ip_comp_hdr *ipch; struct ipcomp_data *ipcd = x->data; union { struct iphdr iph; char buf[60]; } tmp_iph; int hdr_len = 0; if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) { err = -EINVAL; goto error_nolock; } spin_lock_bh(&x->lock); err = xfrm_check_output(x, skb, AF_INET); if (err) goto error; /* Don't bother compressing */ if (!x->props.mode) { iph = skb->nh.iph; hdr_len = iph->ihl * 4; } if ((skb->len - hdr_len) < ipcd->threshold) { if (x->props.mode) { ipcomp_tunnel_encap(x, skb); iph = skb->nh.iph; iph->protocol = IPPROTO_IPIP; ip_send_check(iph); } goto out_ok; } if (x->props.mode) ipcomp_tunnel_encap(x, skb); if ((skb_is_nonlinear(skb) || skb_cloned(skb)) && skb_linearize(skb, GFP_ATOMIC) != 0) { err = -ENOMEM; goto error; } err = ipcomp_compress(x, skb); if (err) { if (err == -EMSGSIZE) { if (x->props.mode) { iph = skb->nh.iph; iph->protocol = IPPROTO_IPIP; ip_send_check(iph); } goto out_ok; } goto error; } /* Install ipcomp header, convert into ipcomp datagram. */ iph = skb->nh.iph; memcpy(&tmp_iph, iph, iph->ihl * 4); top_iph = (struct iphdr *)skb_push(skb, sizeof(struct ip_comp_hdr)); memcpy(top_iph, &tmp_iph, iph->ihl * 4); iph = top_iph; if (x->props.mode && (x->props.flags & XFRM_STATE_NOECN)) IP_ECN_clear(iph); iph->tot_len = htons(skb->len); iph->protocol = IPPROTO_COMP; iph->check = 0; ipch = (struct ip_comp_hdr *)((char *)iph + iph->ihl * 4); ipch->nexthdr = x->props.mode ? IPPROTO_IPIP : tmp_iph.iph.protocol; ipch->flags = 0; ipch->cpi = htons((u16 )ntohl(x->id.spi)); ip_send_check(iph); skb->nh.raw = skb->data; out_ok: x->curlft.bytes += skb->len; x->curlft.packets++; spin_unlock_bh(&x->lock); if ((skb->dst = dst_pop(dst)) == NULL) { err = -EHOSTUNREACH; goto error_nolock; } err = NET_XMIT_BYPASS; out_exit: return err; error: spin_unlock_bh(&x->lock); error_nolock: kfree_skb(skb); goto out_exit; }