patch-2.3.49 linux/net/ipv6/ip6_output.c

Next file: linux/net/ipv6/ipv6_sockglue.c
Previous file: linux/net/ipv6/ip6_input.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.48/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_output.c,v 1.24 2000/01/09 02:19:49 davem Exp $
+ *	$Id: ip6_output.c,v 1.26 2000/03/01 02:58:12 davem Exp $
  *
  *	Based on linux/net/ipv4/ip_output.c
  *
@@ -24,6 +24,7 @@
  *      H. von Brand    :       Added missing #include <linux/string.h>
  */
 
+#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/string.h>
@@ -34,6 +35,9 @@
 #include <linux/in6.h>
 #include <linux/route.h>
 
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
+
 #include <net/sock.h>
 #include <net/snmp.h>
 
@@ -57,11 +61,44 @@
 	spin_unlock_bh(&ip6_id_lock);
 }
 
+static inline int ip6_output_finish(struct sk_buff *skb)
+{
+
+	struct dst_entry *dst = skb->dst;
+	struct hh_cache *hh = dst->hh;
+
+	if (hh) {
+		read_lock_bh(&hh->hh_lock);
+		memcpy(skb->data - 16, hh->hh_data, 16);
+		read_unlock_bh(&hh->hh_lock);
+	        skb_push(skb, hh->hh_len);
+		return hh->hh_output(skb);
+	} else if (dst->neighbour)
+		return dst->neighbour->output(skb);
+
+	kfree_skb(skb);
+	return -EINVAL;
+
+}
+
+/* dev_loopback_xmit for use with netfilter. */
+static int ip6_dev_loopback_xmit(struct sk_buff *newskb)
+{
+	newskb->mac.raw = newskb->data;
+	skb_pull(newskb, newskb->nh.raw - newskb->data);
+	newskb->pkt_type = PACKET_LOOPBACK;
+	newskb->ip_summed = CHECKSUM_UNNECESSARY;
+	BUG_TRAP(newskb->dst);
+
+	netif_rx(newskb);
+	return 0;
+}
+
+
 int ip6_output(struct sk_buff *skb)
 {
 	struct dst_entry *dst = skb->dst;
 	struct net_device *dev = dst->dev;
-	struct hh_cache *hh = dst->hh;
 
 	skb->protocol = __constant_htons(ETH_P_IPV6);
 	skb->dev = dev;
@@ -70,10 +107,15 @@
 		if (!(dev->flags&IFF_LOOPBACK) &&
 		    (skb->sk == NULL || skb->sk->net_pinfo.af_inet6.mc_loop) &&
 		    ipv6_chk_mcast_addr(dev, &skb->nh.ipv6h->daddr)) {
+			struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
 			/* Do not check for IFF_ALLMULTI; multicast routing
 			   is not supported in any case.
 			 */
-			dev_loopback_xmit(skb);
+			if (newskb)
+				NF_HOOK(PF_INET, NF_IP6_POST_ROUTING, newskb, NULL,
+					newskb->dev,
+					ip6_dev_loopback_xmit);
 
 			if (skb->nh.ipv6h->hop_limit == 0) {
 				kfree_skb(skb);
@@ -84,17 +126,51 @@
 		IP6_INC_STATS(Ip6OutMcastPkts);
 	}
 
-	if (hh) {
-		read_lock_bh(&hh->hh_lock);
-		memcpy(skb->data - 16, hh->hh_data, 16);
-		read_unlock_bh(&hh->hh_lock);
-	        skb_push(skb, hh->hh_len);
-		return hh->hh_output(skb);
-	} else if (dst->neighbour)
-		return dst->neighbour->output(skb);
+	return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish);
+}
 
-	kfree_skb(skb);
-	return -EINVAL;
+
+#ifdef CONFIG_NETFILTER
+static int route6_me_harder(struct sk_buff *skb)
+{
+	struct ipv6hdr *iph = skb->nh.ipv6h;
+	struct dst_entry *dst;
+	struct flowi fl;
+
+	fl.proto = iph->nexthdr;
+	fl.fl6_dst = &iph->daddr;
+	fl.fl6_src = &iph->saddr;
+	fl.oif = skb->sk ? skb->sk->bound_dev_if : 0;
+	fl.fl6_flowlabel = 0;
+	fl.uli_u.ports.dport = 0;
+	fl.uli_u.ports.sport = 0;
+
+	dst = ip6_route_output(skb->sk, &fl);
+
+	if (dst->error) {
+		printk(KERN_DEBUG "route6_me_harder: No more route.\n");
+		return -EINVAL;
+	}
+
+	/* Drop old route. */
+	dst_release(skb->dst);
+
+	skb->dst = dst;
+	return 0;
+}
+#endif
+
+static inline int ip6_maybe_reroute(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER
+	if (skb->nfcache & NFC_ALTERED){
+		if (route6_me_harder(skb) != 0){
+			kfree_skb(skb);
+			return -EINVAL;
+		}
+	}
+#endif /* CONFIG_NETFILTER */
+	return skb->dst->output(skb);
 }
 
 /*
@@ -159,7 +235,7 @@
 
 	if (skb->len <= dst->pmtu) {
 		IP6_INC_STATS(Ip6OutRequests);
-		return dst->output(skb);
+		return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
 	}
 
 	printk(KERN_DEBUG "IPv6: sending pkt_too_big to self\n");
@@ -388,7 +464,7 @@
 
 			IP6_INC_STATS(Ip6FragCreates);
 			IP6_INC_STATS(Ip6OutRequests);
-			err = dst->output(skb);
+			err = NF_HOOK(PF_INET6,NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
 			if (err) {
 				kfree_skb(last_skb);
 				return err;
@@ -414,7 +490,7 @@
 	IP6_INC_STATS(Ip6FragCreates);
 	IP6_INC_STATS(Ip6FragOKs);
 	IP6_INC_STATS(Ip6OutRequests);
-	return dst->output(last_skb);
+	return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, last_skb, NULL,dst->dev, ip6_maybe_reroute);
 }
 
 int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
@@ -582,7 +658,7 @@
 
 		if (!err) {
 			IP6_INC_STATS(Ip6OutRequests);
-			err = dst->output(skb);
+			err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
 		} else {
 			err = -EFAULT;
 			kfree_skb(skb);
@@ -636,6 +712,11 @@
 	return 0;
 }
 
+static inline int ip6_forward_finish(struct sk_buff *skb)
+{
+	return skb->dst->output(skb);
+}
+
 int ip6_forward(struct sk_buff *skb)
 {
 	struct dst_entry *dst = skb->dst;
@@ -726,7 +807,7 @@
 	hdr->hop_limit--;
 
 	IP6_INC_STATS_BH(Ip6OutForwDatagrams);
-	return dst->output(skb);
+	return NF_HOOK(PF_INET6,NF_IP6_FORWARD, skb, skb->dev, dst->dev, ip6_forward_finish);
 
 drop:
 	IP6_INC_STATS_BH(Ip6InAddrErrors);

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