/* Add encapsulation header. * * The top IP header will be constructed per RFC 2401. The following fields * in it shall be filled in by x->type->output: * tot_len * check * * On exit, skb->h will be set to the start of the payload to be processed * by x->type->output and skb->nh will be set to the top IP header. */ static int xfrm4_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) { struct dst_entry *dst = skb->dst; struct xfrm_dst *xdst = (struct xfrm_dst*)dst; struct iphdr *iph, *top_iph; int flags; iph = skb->nh.iph; skb->h.ipiph = iph; skb->nh.raw = skb_push(skb, x->props.header_len); top_iph = skb->nh.iph; top_iph->ihl = 5; top_iph->version = 4; flags = x->props.flags; /* DS disclosed */ if (xdst->route->ops->family == AF_INET) { top_iph->protocol = IPPROTO_IPIP; top_iph->tos = INET_ECN_encapsulate(iph->tos, iph->tos); top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ? 0 : (iph->frag_off & htons(IP_DF)); } #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) else { struct ipv6hdr *ipv6h = (struct ipv6hdr*)iph; top_iph->protocol = IPPROTO_IPV6; top_iph->tos = INET_ECN_encapsulate(iph->tos, ipv6_get_dsfield(ipv6h)); top_iph->frag_off = 0; } #endif if (flags & XFRM_STATE_NOECN) IP_ECN_clear(top_iph); if (!top_iph->frag_off) __ip_select_ident(top_iph, dst->child, 0); top_iph->ttl = dst_metric(dst->child, RTAX_HOPLIMIT); top_iph->saddr = x->props.saddr.a4; top_iph->daddr = x->id.daddr.a4; skb->protocol = htons(ETH_P_IP); memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); return 0; }
/* Add encapsulation header. * * In transport mode, the IP header will be moved forward to make space * for the encapsulation header. * * In tunnel mode, the top IP header will be constructed per RFC 2401. * The following fields in it shall be filled in by x->type->output: * tot_len * check * * On exit, skb->h will be set to the start of the payload to be processed * by x->type->output and skb->nh will be set to the top IP header. */ static void xfrm4_encap(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; struct xfrm_state *x = dst->xfrm; struct iphdr *iph, *top_iph; int flags; iph = skb->nh.iph; skb->h.ipiph = iph; skb->nh.raw = skb_push(skb, x->props.header_len); top_iph = skb->nh.iph; if (!x->props.mode) { skb->h.raw += iph->ihl*4; memmove(top_iph, iph, iph->ihl*4); return; } top_iph->ihl = 5; top_iph->version = 4; /* DS disclosed */ top_iph->tos = INET_ECN_encapsulate(iph->tos, iph->tos); flags = x->props.flags; if (flags & XFRM_STATE_NOECN) IP_ECN_clear(top_iph); top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ? 0 : (iph->frag_off & htons(IP_DF)); if (!top_iph->frag_off) __ip_select_ident(top_iph, dst, 0); top_iph->ttl = dst_metric(dst->child, RTAX_HOPLIMIT); top_iph->saddr = x->props.saddr.a4; top_iph->daddr = x->id.daddr.a4; top_iph->protocol = IPPROTO_IPIP; memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); }
/* Add encapsulation header. * * The top IP header will be constructed per RFC 2401. */ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct iphdr *top_iph; int flags; skb_set_network_header(skb, -x->props.header_len); skb->mac_header = skb->network_header + offsetof(struct iphdr, protocol); skb->transport_header = skb->network_header + sizeof(*top_iph); top_iph = ip_hdr(skb); top_iph->ihl = 5; top_iph->version = 4; top_iph->protocol = xfrm_af2proto(skb_dst(skb)->ops->family); /* DS disclosing depends on XFRM_SA_XFLAG_DONT_ENCAP_DSCP */ if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP) top_iph->tos = 0; else top_iph->tos = XFRM_MODE_SKB_CB(skb)->tos; top_iph->tos = INET_ECN_encapsulate(top_iph->tos, XFRM_MODE_SKB_CB(skb)->tos); flags = x->props.flags; if (flags & XFRM_STATE_NOECN) IP_ECN_clear(top_iph); top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ? 0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF)); top_iph->ttl = ip4_dst_hoplimit(dst->child); top_iph->saddr = x->props.saddr.a4; top_iph->daddr = x->id.daddr.a4; ip_select_ident(dev_net(dst->dev), skb, NULL); return 0; }
/* Add encapsulation header. * * The top IP header will be constructed per RFC 2401. */ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct iphdr *top_iph; int flags; skb_set_network_header(skb, -x->props.header_len); skb->mac_header = skb->network_header + offsetof(struct iphdr, protocol); skb->transport_header = skb->network_header + sizeof(*top_iph); top_iph = ip_hdr(skb); top_iph->ihl = 5; top_iph->version = 4; top_iph->protocol = xfrm_af2proto(skb_dst(skb)->ops->family); /* DS disclosed */ top_iph->tos = INET_ECN_encapsulate(XFRM_MODE_SKB_CB(skb)->tos, XFRM_MODE_SKB_CB(skb)->tos); flags = x->props.flags; if (flags & XFRM_STATE_NOECN) IP_ECN_clear(top_iph); top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ? 0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF)); ip_select_ident(top_iph, dst->child, NULL); top_iph->ttl = dst_metric(dst->child, RTAX_HOPLIMIT); top_iph->saddr = x->props.saddr.a4; top_iph->daddr = x->id.daddr.a4; return 0; }
int esp_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_esp_hdr *esph; struct crypto_tfm *tfm; struct esp_data *esp; struct sk_buff *trailer; struct udphdr *uh = NULL; struct xfrm_encap_tmpl *encap = NULL; int blksize; int clen; int alen; int nfrags; union { struct iphdr iph; char buf[60]; } tmp_iph; /* First, if the skb is not checksummed, complete checksum. */ 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; err = -ENOMEM; /* Strip IP header in transport mode. Save it. */ if (!x->props.mode) { iph = skb->nh.iph; memcpy(&tmp_iph, iph, iph->ihl*4); __skb_pull(skb, iph->ihl*4); } /* Now skb is pure payload to encrypt */ /* Round to block size */ clen = skb->len; esp = x->data; alen = esp->auth.icv_trunc_len; tfm = esp->conf.tfm; blksize = (crypto_tfm_alg_blocksize(tfm) + 3) & ~3; clen = (clen + 2 + blksize-1)&~(blksize-1); if (esp->conf.padlen) clen = (clen + esp->conf.padlen-1)&~(esp->conf.padlen-1); if ((nfrags = skb_cow_data(skb, clen-skb->len+alen, &trailer)) < 0) goto error; /* Fill padding... */ do { int i; for (i=0; i<clen-skb->len - 2; i++) *(u8*)(trailer->tail + i) = i+1; } while (0); *(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2; pskb_put(skb, trailer, clen - skb->len); encap = x->encap; iph = skb->nh.iph; if (x->props.mode) { top_iph = (struct iphdr*)skb_push(skb, x->props.header_len); esph = (struct ip_esp_hdr*)(top_iph+1); if (encap && encap->encap_type) { switch (encap->encap_type) { case UDP_ENCAP_ESPINUDP: uh = (struct udphdr*) esph; esph = (struct ip_esp_hdr*)(uh+1); top_iph->protocol = IPPROTO_UDP; break; default: printk(KERN_INFO "esp_output(): Unhandled encap: %u\n", encap->encap_type); top_iph->protocol = IPPROTO_ESP; break; } } else top_iph->protocol = IPPROTO_ESP; *(u8*)(trailer->tail - 1) = IPPROTO_IPIP; top_iph->ihl = 5; top_iph->version = 4; top_iph->tos = iph->tos; /* DS disclosed */ if (x->props.flags & XFRM_STATE_NOECN) IP_ECN_clear(top_iph); top_iph->tot_len = htons(skb->len + alen); top_iph->frag_off = iph->frag_off&htons(IP_DF); if (!(top_iph->frag_off)) ip_select_ident(top_iph, dst, 0); top_iph->ttl = iph->ttl; /* TTL disclosed */ top_iph->check = 0; top_iph->saddr = x->props.saddr.a4; top_iph->daddr = x->id.daddr.a4; memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); } else { esph = (struct ip_esp_hdr*)skb_push(skb, x->props.header_len); top_iph = (struct iphdr*)skb_push(skb, iph->ihl*4); memcpy(top_iph, &tmp_iph, iph->ihl*4); if (encap && encap->encap_type) { switch (encap->encap_type) { case UDP_ENCAP_ESPINUDP: uh = (struct udphdr*) esph; esph = (struct ip_esp_hdr*)(uh+1); top_iph->protocol = IPPROTO_UDP; break; default: printk(KERN_INFO "esp_output(): Unhandled encap: %u\n", encap->encap_type); top_iph->protocol = IPPROTO_ESP; break; } } else top_iph->protocol = IPPROTO_ESP; iph = &tmp_iph.iph; top_iph->tot_len = htons(skb->len + alen); top_iph->check = 0; top_iph->frag_off = iph->frag_off; *(u8*)(trailer->tail - 1) = iph->protocol; } /* this is non-NULL only with UDP Encapsulation */ if (encap && uh) { uh->source = encap->encap_sport; uh->dest = encap->encap_dport; uh->len = htons(skb->len + alen - sizeof(struct iphdr)); uh->check = 0; } esph->spi = x->id.spi; esph->seq_no = htonl(++x->replay.oseq); if (esp->conf.ivlen) crypto_cipher_set_iv(tfm, esp->conf.ivec, crypto_tfm_alg_ivsize(tfm)); do { struct scatterlist sgbuf[nfrags>MAX_SG_ONSTACK ? 0 : nfrags]; struct scatterlist *sg = sgbuf; if (unlikely(nfrags > MAX_SG_ONSTACK)) { sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC); if (!sg) goto error; } skb_to_sgvec(skb, sg, esph->enc_data+esp->conf.ivlen-skb->data, clen); crypto_cipher_encrypt(tfm, sg, sg, clen); if (unlikely(sg != sgbuf)) kfree(sg); } while (0); if (esp->conf.ivlen) { memcpy(esph->enc_data, esp->conf.ivec, crypto_tfm_alg_ivsize(tfm)); crypto_cipher_get_iv(tfm, esp->conf.ivec, crypto_tfm_alg_ivsize(tfm)); } if (esp->auth.icv_full_len) { esp->auth.icv(esp, skb, (u8*)esph-skb->data, sizeof(struct ip_esp_hdr) + esp->conf.ivlen+clen, trailer->tail); pskb_put(skb, trailer, alen); } ip_send_check(top_iph); skb->nh.raw = skb->data; 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; } return NET_XMIT_BYPASS; error: spin_unlock_bh(&x->lock); error_nolock: kfree_skb(skb); return err; }
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; }