#include <net/inet_ecn.h>
#include <net/ip.h>
#include <net/xfrm.h>
+#include <net/icmp.h>
/* Add encapsulation header.
*
if (!top_iph->frag_off)
__ip_select_ident(top_iph, dst, 0);
- /* TTL disclosed */
- top_iph->ttl = iph->ttl;
+ top_iph->ttl = dst_path_metric(dst, RTAX_HOPLIMIT);
top_iph->saddr = x->props.saddr.a4;
top_iph->daddr = x->id.daddr.a4;
memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
}
-int xfrm4_output(struct sk_buff **pskb)
+static int xfrm4_tunnel_check_size(struct sk_buff *skb)
+{
+ int mtu, ret = 0;
+ struct dst_entry *dst;
+ struct iphdr *iph = skb->nh.iph;
+
+ if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE)
+ goto out;
+
+ IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
+
+ if (!(iph->frag_off & htons(IP_DF)))
+ goto out;
+
+ dst = skb->dst;
+ mtu = dst_pmtu(dst) - dst->header_len - dst->trailer_len;
+ if (skb->len > mtu) {
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+ ret = -EMSGSIZE;
+ }
+out:
+ return ret;
+}
+
+int xfrm4_output(struct sk_buff *skb)
{
- struct sk_buff *skb = *pskb;
struct dst_entry *dst = skb->dst;
struct xfrm_state *x = dst->xfrm;
int err;
if (skb->ip_summed == CHECKSUM_HW) {
- err = skb_checksum_help(pskb, 0);
- skb = *pskb;
+ err = skb_checksum_help(skb, 0);
if (err)
goto error_nolock;
}
- spin_lock_bh(&x->lock);
- err = xfrm_state_check(x, skb);
- if (err)
- goto error;
-
if (x->props.mode) {
err = xfrm4_tunnel_check_size(skb);
if (err)
- goto error;
+ goto error_nolock;
}
+ spin_lock_bh(&x->lock);
+ err = xfrm_state_check(x, skb);
+ if (err)
+ goto error;
+
xfrm4_encap(skb);
- err = x->type->output(pskb);
- skb = *pskb;
+ err = x->type->output(skb);
if (err)
goto error;