patch-2.4.0-test3 linux/net/ipv4/ip_gre.c

Next file: linux/net/ipv4/ip_sockglue.c
Previous file: linux/net/ipv4/ip_fragment.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test2/linux/net/ipv4/ip_gre.c linux/net/ipv4/ip_gre.c
@@ -27,6 +27,7 @@
 #include <linux/in6.h>
 #include <linux/inetdevice.h>
 #include <linux/igmp.h>
+#include <linux/netfilter_ipv4.h>
 
 #include <net/sock.h>
 #include <net/ip.h>
@@ -35,6 +36,7 @@
 #include <net/ipip.h>
 #include <net/arp.h>
 #include <net/checksum.h>
+#include <net/inet_ecn.h>
 
 #ifdef CONFIG_IPV6
 #include <net/ipv6.h>
@@ -118,11 +120,11 @@
 static int ipgre_fb_tunnel_init(struct net_device *dev);
 
 static struct net_device ipgre_fb_tunnel_dev = {
-	"gre%d", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, ipgre_fb_tunnel_init,
+	"gre0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, ipgre_fb_tunnel_init,
 };
 
 static struct ip_tunnel ipgre_fb_tunnel = {
-	NULL, &ipgre_fb_tunnel_dev, {0, }, 0, 0, 0, 0, 0, 0, 0, {"gre%d", }
+	NULL, &ipgre_fb_tunnel_dev, {0, }, 0, 0, 0, 0, 0, 0, 0, {"gre0", }
 };
 
 /* Tunnel hash table */
@@ -529,6 +531,34 @@
 #endif
 }
 
+static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
+{
+	if (INET_ECN_is_ce(iph->tos)) {
+		if (skb->protocol == __constant_htons(ETH_P_IP)) {
+			if (INET_ECN_is_not_ce(skb->nh.iph->tos))
+				IP_ECN_set_ce(skb->nh.iph);
+		} else if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
+			if (INET_ECN_is_not_ce(ip6_get_dsfield(skb->nh.ipv6h)))
+				IP6_ECN_set_ce(skb->nh.ipv6h);
+		}
+	}
+}
+
+static inline u8
+ipgre_ecn_encapsulate(u8 tos, struct iphdr *old_iph, struct sk_buff *skb)
+{
+#ifdef CONFIG_INET_ECN
+	u8 inner = 0;
+	if (skb->protocol == __constant_htons(ETH_P_IP))
+		inner = old_iph->tos;
+	else if (skb->protocol == __constant_htons(ETH_P_IPV6))
+		inner = ip6_get_dsfield((struct ipv6hdr*)old_iph);
+	return INET_ECN_encapsulate(tos, inner);
+#else
+	return tos;
+#endif
+}
+
 int ipgre_rcv(struct sk_buff *skb, unsigned short len)
 {
 	struct iphdr *iph = skb->nh.iph;
@@ -603,6 +633,7 @@
 		nf_conntrack_put(skb->nfct);
 		skb->nfct = NULL;
 #endif
+		ipgre_ecn_decapsulate(iph, skb);
 		netif_rx(skb);
 		read_unlock(&ipgre_lock);
 		return(0);
@@ -616,6 +647,12 @@
 	return(0);
 }
 
+/* Need this wrapper because NF_HOOK takes the function address */
+static inline int do_ip_send(struct sk_buff *skb)
+{
+	return ip_send(skb);
+}
+
 static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;
@@ -631,6 +668,7 @@
 	int    gre_hlen;
 	u32    dst;
 	int    mtu;
+	int    err;
 
 	if (tunnel->recursion++) {
 		tunnel->stat.collisions++;
@@ -782,7 +820,7 @@
 	iph->ihl		=	sizeof(struct iphdr) >> 2;
 	iph->frag_off		=	df;
 	iph->protocol		=	IPPROTO_GRE;
-	iph->tos		=	tos;
+	iph->tos		=	ipgre_ecn_encapsulate(tos, old_iph, skb);
 	iph->daddr		=	rt->rt_dst;
 	iph->saddr		=	rt->rt_src;
 
@@ -827,9 +865,17 @@
 	skb->nfct = NULL;
 #endif
 
+	err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
+		do_ip_send);
+	if(err < 0) {
+		if(net_ratelimit())
+			printk(KERN_ERR "ipgre_tunnel_xmit: ip_send() failed, err=%d\n", -err);
+		skb = NULL;
+		goto tx_error;
+	}
+
 	stats->tx_bytes += skb->len;
 	stats->tx_packets++;
-	ip_send(skb);
 	tunnel->recursion--;
 	return 0;
 
@@ -838,7 +884,8 @@
 
 tx_error:
 	stats->tx_errors++;
-	dev_kfree_skb(skb);
+	if(skb)
+		dev_kfree_skb(skb);
 	tunnel->recursion--;
 	return 0;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)