diff -Nru a/include/linux/netdevice.h b/include/linux/netdevice.h
--- a/include/linux/netdevice.h	Tue Jun 15 22:21:03 2004
+++ b/include/linux/netdevice.h	Tue Jun 15 22:21:03 2004
@@ -215,6 +215,8 @@
  */
 #define LL_RESERVED_SPACE(dev) \
 	(((dev)->hard_header_len&~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
+#define LL_RESERVED_SPACE_EXTRA(dev,extra) \
+	((((dev)->hard_header_len+extra)&~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
 
 /* These flag bits are private to the generic network queueing
  * layer, they may not be explicitly referenced by any other
@@ -941,7 +943,7 @@
 extern unsigned long	netdev_fc_xoff;
 extern atomic_t netdev_dropping;
 extern int		netdev_set_master(struct net_device *dev, struct net_device *master);
-extern struct sk_buff * skb_checksum_help(struct sk_buff *skb);
+extern int skb_checksum_help(struct sk_buff **pskb, int inward);
 #ifdef CONFIG_NET_FASTROUTE
 extern int		netdev_fastroute;
 extern int		netdev_fastroute_obstacles;
diff -Nru a/include/linux/netfilter.h b/include/linux/netfilter.h
--- a/include/linux/netfilter.h	Tue Jun 15 22:20:40 2004
+++ b/include/linux/netfilter.h	Tue Jun 15 22:20:40 2004
@@ -64,11 +64,11 @@
 	/* Non-inclusive ranges: use 0/0/NULL to never get called. */
 	int set_optmin;
 	int set_optmax;
-	int (*set)(struct sock *sk, int optval, void *user, unsigned int len);
+	int (*set)(struct sock *sk, int optval, void __user *user, unsigned int len);
 
 	int get_optmin;
 	int get_optmax;
-	int (*get)(struct sock *sk, int optval, void *user, int *len);
+	int (*get)(struct sock *sk, int optval, void __user *user, int *len);
 
 	/* Number of users inside set() or get(). */
 	unsigned int use;
@@ -156,9 +156,9 @@
 		 int (*okfn)(struct sk_buff *), int thresh);
 
 /* Call setsockopt() */
-int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+int nf_setsockopt(struct sock *sk, int pf, int optval, char __user *opt, 
 		  int len);
-int nf_getsockopt(struct sock *sk, int pf, int optval, char *opt,
+int nf_getsockopt(struct sock *sk, int pf, int optval, char __user *opt,
 		  int *len);
 
 /* Packet queuing */
@@ -171,6 +171,12 @@
 			struct nf_info *info,
 			unsigned int verdict);
 
+extern inline struct ipt_target *
+ipt_find_target_lock(const char *name, int *error, struct semaphore *mutex);
+extern inline struct ip6t_target *
+ip6t_find_target_lock(const char *name, int *error, struct semaphore *mutex);
+extern inline struct arpt_target *
+arpt_find_target_lock(const char *name, int *error, struct semaphore *mutex);
 extern void (*ip_ct_attach)(struct sk_buff *, struct nf_ct_info *);
 
 #ifdef CONFIG_NETFILTER_DEBUG
diff -Nru a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h
--- a/include/linux/netfilter_arp/arp_tables.h	Tue Jun 15 22:20:38 2004
+++ b/include/linux/netfilter_arp/arp_tables.h	Tue Jun 15 22:20:38 2004
@@ -205,7 +205,7 @@
 	/* Number of counters (must be equal to current number of entries). */
 	unsigned int num_counters;
 	/* The old entries' counters. */
-	struct arpt_counters *counters;
+	struct arpt_counters __user *counters;
 
 	/* The entries (hang off end: not really an array). */
 	struct arpt_entry entries[0];
diff -Nru a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h
--- a/include/linux/netfilter_ipv4/ip_tables.h	Tue Jun 15 22:20:41 2004
+++ b/include/linux/netfilter_ipv4/ip_tables.h	Tue Jun 15 22:20:41 2004
@@ -252,7 +252,7 @@
 	/* Number of counters (must be equal to current number of entries). */
 	unsigned int num_counters;
 	/* The old entries' counters. */
-	struct ipt_counters *counters;
+	struct ipt_counters __user *counters;
 
 	/* The entries (hang off end: not really an array). */
 	struct ipt_entry entries[0];
@@ -283,6 +283,8 @@
 	struct ipt_entry entrytable[0];
 };
 
+extern struct semaphore ipt_mutex;
+
 /* Standard return verdict, or do jump. */
 #define IPT_STANDARD_TARGET ""
 /* Error verdict. */
@@ -334,6 +336,7 @@
 /*
  *	Main firewall chains definitions and global var's definitions.
  */
+static DECLARE_MUTEX(ipt_mutex);
 #ifdef __KERNEL__
 
 #include <linux/init.h>
@@ -405,6 +408,11 @@
 	/* Set this to THIS_MODULE. */
 	struct module *me;
 };
+
+extern struct ipt_target *
+ipt_find_target_lock(const char *name, int *error, struct semaphore *mutex);
+extern struct arpt_target *
+arpt_find_target_lock(const char *name, int *error, struct semaphore *mutex);
 
 extern int ipt_register_target(struct ipt_target *target);
 extern void ipt_unregister_target(struct ipt_target *target);
diff -Nru a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h
--- a/include/linux/netfilter_ipv6/ip6_tables.h	Tue Jun 15 22:21:03 2004
+++ b/include/linux/netfilter_ipv6/ip6_tables.h	Tue Jun 15 22:21:03 2004
@@ -106,6 +106,8 @@
 	u_int64_t pcnt, bcnt;			/* Packet and byte counters */
 };
 
+static DECLARE_MUTEX(ip6t_mutex);
+
 /* Values for "flag" field in struct ip6t_ip6 (general ip6 structure). */
 #define IP6T_F_PROTO		0x01	/* Set if rule cares about upper 
 					   protocols */
@@ -258,7 +260,7 @@
 	/* Number of counters (must be equal to current number of entries). */
 	unsigned int num_counters;
 	/* The old entries' counters. */
-	struct ip6t_counters *counters;
+	struct ip6t_counters __user *counters;
 
 	/* The entries (hang off end: not really an array). */
 	struct ip6t_entry entries[0];
diff -Nru a/include/net/dst.h b/include/net/dst.h
--- a/include/net/dst.h	Tue Jun 15 22:21:02 2004
+++ b/include/net/dst.h	Tue Jun 15 22:21:02 2004
@@ -67,7 +67,7 @@
 	struct xfrm_state	*xfrm;
 
 	int			(*input)(struct sk_buff*);
-	int			(*output)(struct sk_buff*);
+	int			(*output)(struct sk_buff**);
 
 #ifdef CONFIG_NET_CLS_ROUTE
 	__u32			tclassid;
@@ -219,7 +220,7 @@
 	int err;
 
 	for (;;) {
-		err = skb->dst->output(skb);
+		err = skb->dst->output(&skb);
 
 		if (likely(err == 0))
 			return err;
diff -Nru a/include/net/ip.h b/include/net/ip.h
--- a/include/net/ip.h	Tue Jun 15 22:20:42 2004
+++ b/include/net/ip.h	Tue Jun 15 22:20:42 2004
@@ -92,8 +92,8 @@
 			       struct packet_type *pt);
 extern int		ip_local_deliver(struct sk_buff *skb);
 extern int		ip_mr_input(struct sk_buff *skb);
-extern int		ip_output(struct sk_buff *skb);
-extern int		ip_mc_output(struct sk_buff *skb);
+extern int		ip_output(struct sk_buff **pskb);
+extern int		ip_mc_output(struct sk_buff **pskb);
 extern int		ip_fragment(struct sk_buff *skb, int (*out)(struct sk_buff*));
 extern int		ip_do_nat(struct sk_buff *skb);
 extern void		ip_send_check(struct iphdr *ip);
diff -Nru a/include/net/ip6_route.h b/include/net/ip6_route.h
--- a/include/net/ip6_route.h	Tue Jun 15 22:20:38 2004
+++ b/include/net/ip6_route.h	Tue Jun 15 22:20:38 2004
@@ -65,7 +65,7 @@
 extern struct dst_entry *ndisc_dst_alloc(struct net_device *dev,
 					 struct neighbour *neigh,
 					 struct in6_addr *addr,
-					 int (*output)(struct sk_buff *));
+					 int (*output)(struct sk_buff **));
 extern int ndisc_dst_gc(int *more);
 extern void fib6_force_start_gc(void);
 
diff -Nru a/include/net/ipv6.h b/include/net/ipv6.h
--- a/include/net/ipv6.h	Tue Jun 15 22:20:43 2004
+++ b/include/net/ipv6.h	Tue Jun 15 22:20:43 2004
@@ -355,8 +355,7 @@
  *	skb processing functions
  */
 
-extern int			ip6_output(struct sk_buff *skb);
-extern int			ip6_output2(struct sk_buff *skb);
+extern int			ip6_output(struct sk_buff **pskb);
 extern int			ip6_forward(struct sk_buff *skb);
 extern int			ip6_input(struct sk_buff *skb);
 extern int			ip6_mc_input(struct sk_buff *skb);
diff -Nru a/include/net/xfrm.h b/include/net/xfrm.h
--- a/include/net/xfrm.h	Tue Jun 15 22:20:44 2004
+++ b/include/net/xfrm.h	Tue Jun 15 22:20:44 2004
@@ -216,7 +216,7 @@
 	void			(*destructor)(struct xfrm_state *);
 	int			(*input)(struct xfrm_state *, struct xfrm_decap_state *, struct sk_buff *skb);
 	int			(*post_input)(struct xfrm_state *, struct xfrm_decap_state *, struct sk_buff *skb);
-	int			(*output)(struct sk_buff *skb);
+	int			(*output)(struct sk_buff **pskb);
 	/* Estimate maximal size of result of transformation of a dgram */
 	u32			(*get_max_size)(struct xfrm_state *, int size);
 };
diff -Nru a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
--- a/net/bridge/br_netfilter.c	Tue Jun 15 22:21:03 2004
+++ b/net/bridge/br_netfilter.c	Tue Jun 15 22:21:03 2004
@@ -165,7 +165,7 @@
 		skb_pull(skb, VLAN_HLEN);
 		skb->nh.raw += VLAN_HLEN;
 	}
-	skb->dst->output(skb);
+	skb->dst->output(&skb);
 	return 0;
 }
 
diff -Nru a/net/compat.c b/net/compat.c
--- a/net/compat.c	Tue Jun 15 22:20:41 2004
+++ b/net/compat.c	Tue Jun 15 22:20:41 2004
@@ -322,10 +319,10 @@
 };
 
 static int do_netfilter_replace(int fd, int level, int optname,
-				char *optval, int optlen)
+				char __user *optval, int optlen)
 {
-	struct compat_ipt_replace *urepl = (struct compat_ipt_replace *)optval;
-	struct ipt_replace *repl_nat;
+	struct compat_ipt_replace __user *urepl;
+	struct ipt_replace __user *repl_nat;
 	char name[IPT_TABLE_MAXNAMELEN];
 	u32 origsize, tmp32, num_counters;
 	unsigned int repl_nat_size;
@@ -333,6 +330,7 @@
 	int i;
 	compat_uptr_t ucntrs;
 
+	urepl = (struct compat_ipt_replace __user *)optval;
 	if (get_user(origsize, &urepl->size))
 		return -EFAULT;
 
@@ -399,7 +397,7 @@
 
 
 	ret = sys_setsockopt(fd, level, optname,
-			     (char *)repl_nat, repl_nat_size);
+			     (char __user *)repl_nat, repl_nat_size);
 
 out:
 	return ret;
diff -Nru a/net/core/dev.c b/net/core/dev.c
--- a/net/core/dev.c	Tue Jun 15 22:20:40 2004
+++ b/net/core/dev.c	Tue Jun 15 22:20:40 2004
@@ -1170,28 +1180,46 @@
 	rcu_read_unlock();
 }
 
-/* Calculate csum in the case, when packet is misrouted.
- * If it failed by some reason, ignore and send skb with wrong
- * checksum.
+/*
+ * Invalidate hardware checksum when packet is to be mangled, and
+ * complete checksum manually on outgoing path.
  */
-struct sk_buff *skb_checksum_help(struct sk_buff *skb)
+int skb_checksum_help(struct sk_buff **pskb, int inward)
 {
 	unsigned int csum;
-	int offset = skb->h.raw - skb->data;
+	int ret = 0, offset = (*pskb)->h.raw - (*pskb)->data;
+
+	if (inward) {
+		(*pskb)->ip_summed = CHECKSUM_NONE;
+		goto out;
+	}
+
+	if (skb_shared(*pskb)  || skb_cloned(*pskb)) {
+		struct sk_buff *newskb = skb_copy(*pskb, GFP_ATOMIC);
+		if (!newskb) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		if ((*pskb)->sk)
+			skb_set_owner_w(newskb, (*pskb)->sk);
+		kfree_skb(*pskb);
+		*pskb = newskb;
+	}
 
-	if (offset > (int)skb->len)
+	if (offset > (int)(*pskb)->len)
 		BUG();
-	csum = skb_checksum(skb, offset, skb->len-offset, 0);
+	csum = skb_checksum(*pskb, offset, (*pskb)->len-offset, 0);
 
-	offset = skb->tail - skb->h.raw;
+	offset = (*pskb)->tail - (*pskb)->h.raw;
 	if (offset <= 0)
 		BUG();
-	if (skb->csum + 2 > offset)
+	if ((*pskb)->csum + 2 > offset)
 		BUG();
 
-	*(u16*)(skb->h.raw + skb->csum) = csum_fold(csum);
-	skb->ip_summed = CHECKSUM_NONE;
-	return skb;
+	*(u16*)((*pskb)->h.raw + (*pskb)->csum) = csum_fold(csum);
+	(*pskb)->ip_summed = CHECKSUM_NONE;
+out:	
+	return ret;
 }
 
 #ifdef CONFIG_HIGHMEM
@@ -1316,10 +1344,9 @@
 	if (skb->ip_summed == CHECKSUM_HW &&
 	    (!(dev->features & (NETIF_F_HW_CSUM | NETIF_F_NO_CSUM)) &&
 	     (!(dev->features & NETIF_F_IP_CSUM) ||
-	      skb->protocol != htons(ETH_P_IP)))) {
-		if ((skb = skb_checksum_help(skb)) == NULL)
-			goto out;
-	}
+	      skb->protocol != htons(ETH_P_IP))))
+	      	if (skb_checksum_help(&skb, 0))
+	      		goto out_kfree_skb;
 
 	/* Grab device queue */
 	spin_lock_bh(&dev->queue_lock);
diff -Nru a/net/core/dst.c b/net/core/dst.c
--- a/net/core/dst.c	Tue Jun 15 22:20:41 2004
+++ b/net/core/dst.c	Tue Jun 15 22:20:41 2004
@@ -100,15 +100,15 @@
 	spin_unlock(&dst_lock);
 }
 
-static int dst_discard(struct sk_buff *skb)
+static int dst_discard_in(struct sk_buff *skb)
 {
 	kfree_skb(skb);
 	return 0;
 }
 
-static int dst_blackhole(struct sk_buff *skb)
+static int dst_discard_out(struct sk_buff **pskb)
 {
-	kfree_skb(skb);
+	kfree_skb(*pskb);
 	return 0;
 }
 
@@ -128,8 +128,8 @@
 	dst->ops = ops;
 	dst->lastuse = jiffies;
 	dst->path = dst;
-	dst->input = dst_discard;
-	dst->output = dst_blackhole;
+	dst->input = dst_discard_in;
+	dst->output = dst_discard_out;
 #if RT_CACHE_DEBUG >= 2 
 	atomic_inc(&dst_total);
 #endif
@@ -143,8 +143,8 @@
 	   protocol module is unloaded.
 	 */
 	if (dst->dev == NULL || !(dst->dev->flags&IFF_UP)) {
-		dst->input = dst_discard;
-		dst->output = dst_blackhole;
+		dst->input = dst_discard_in;
+		dst->output = dst_discard_out;
 	}
 	dst->obsolete = 2;
 }
@@ -228,20 +228,22 @@
 				   _race_ _condition_.
 				 */
 				if (event!=NETDEV_DOWN &&
-				    dst->output == dst_blackhole) {
+				    dst->output == dst_discard_out) {
 					dst->dev = &loopback_dev;
-					dev_put(dev);
 					dev_hold(&loopback_dev);
-					dst->output = dst_discard;
+					dev_put(dev);
+					dst->output = dst_discard_out;
 					if (dst->neighbour && dst->neighbour->dev == dev) {
 						dst->neighbour->dev = &loopback_dev;
 						dev_put(dev);
 						dev_hold(&loopback_dev);
 					}
 				} else {
-					dst->input = dst_discard;
-					dst->output = dst_blackhole;
+					dst->input = dst_discard_in;
+					dst->output = dst_discard_out;
 				}
+				
+					
 			}
 		}
 		spin_unlock_bh(&dst_lock);
diff -Nru a/net/core/netfilter.c b/net/core/netfilter.c
--- a/net/core/netfilter.c	Tue Jun 15 22:20:40 2004
+++ b/net/core/netfilter.c	Tue Jun 15 22:20:40 2004
@@ -286,7 +286,7 @@
 
 /* Call get/setsockopt() */
 static int nf_sockopt(struct sock *sk, int pf, int val, 
-		      char *opt, int *len, int get)
+		      char __user *opt, int *len, int get)
 {
 	struct list_head *i;
 	struct nf_sockopt_ops *ops;
@@ -329,13 +329,13 @@
 	return ret;
 }
 
-int nf_setsockopt(struct sock *sk, int pf, int val, char *opt,
+int nf_setsockopt(struct sock *sk, int pf, int val, char __user *opt,
 		  int len)
 {
 	return nf_sockopt(sk, pf, val, opt, &len, 0);
 }
 
-int nf_getsockopt(struct sock *sk, int pf, int val, char *opt, int *len)
+int nf_getsockopt(struct sock *sk, int pf, int val, char __user *opt, int *len)
 {
 	return nf_sockopt(sk, pf, val, opt, len, 1);
 }
@@ -503,14 +503,6 @@
 	struct list_head *elem;
 	unsigned int verdict;
 	int ret = 0;
-
-	if (skb->ip_summed == CHECKSUM_HW) {
-		if (outdev == NULL) {
-			skb->ip_summed = CHECKSUM_NONE;
-		} else {
-			skb_checksum_help(skb);
-		}
-	}
 
 	/* We may already have this, but read-locks nest anyway */
 	rcu_read_lock();
diff -Nru a/net/decnet/dn_route.c b/net/decnet/dn_route.c
--- a/net/decnet/dn_route.c	Tue Jun 15 22:21:03 2004
+++ b/net/decnet/dn_route.c	Tue Jun 15 22:21:03 2004
@@ -684,8 +684,9 @@
 	return NET_RX_DROP;
 }
 
-static int dn_output(struct sk_buff *skb)
+static int dn_output(struct sk_buff **pskb)
 {
+	struct sk_buff *skb = *pskb;
 	struct dst_entry *dst = skb->dst;
 	struct dn_route *rt = (struct dn_route *)dst;
 	struct net_device *dev = dst->dev;
@@ -796,6 +797,11 @@
 	return NET_RX_BAD;
 }
 
+static int dn_rt_bug_out(struct sk_buff **pskb)
+{
+	return dn_rt_bug(*pskb);
+}
+
 static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res)
 {
 	struct dn_fib_info *fi = res->fi;
@@ -1387,7 +1393,7 @@
 	rt->u.dst.neighbour = neigh;
 	rt->u.dst.dev = out_dev;
 	rt->u.dst.lastuse = jiffies;
-	rt->u.dst.output = dn_rt_bug;
+	rt->u.dst.output = dn_rt_bug_out;
 	switch(res.type) {
 		case RTN_UNICAST:
 			rt->u.dst.input = dn_forward;
diff -Nru a/net/ipv4/ah4.c b/net/ipv4/ah4.c
--- a/net/ipv4/ah4.c	Tue Jun 15 22:20:44 2004
+++ b/net/ipv4/ah4.c	Tue Jun 15 22:20:44 2004
@@ -54,10 +54,10 @@
 	return 0;
 }
 
-static int ah_output(struct sk_buff *skb)
+static int ah_output(struct sk_buff **pskb)
 {
 	int err;
-	struct dst_entry *dst = skb->dst;
+	struct dst_entry *dst = (*pskb)->dst;
 	struct xfrm_state *x  = dst->xfrm;
 	struct iphdr *iph, *top_iph;
 	struct ip_auth_hdr *ah;
@@ -67,23 +67,24 @@
 		char 		buf[60];
 	} tmp_iph;
 
-	if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
-		err = -EINVAL;
-		goto error_nolock;
+	if ((*pskb)->ip_summed == CHECKSUM_HW) {
+		err = skb_checksum_help(pskb, 0);
+		if (err)
+			goto error_nolock;
 	}
 
 	spin_lock_bh(&x->lock);
-	err = xfrm_check_output(x, skb, AF_INET);
+	err = xfrm_check_output(x, *pskb, AF_INET);
 	if (err)
 		goto error;
 
-	iph = skb->nh.iph;
+	iph = (*pskb)->nh.iph;
 	if (x->props.mode) {
-		top_iph = (struct iphdr*)skb_push(skb, x->props.header_len);
+		top_iph = (struct iphdr*)skb_push(*pskb, x->props.header_len);
 		top_iph->ihl = 5;
 		top_iph->version = 4;
 		top_iph->tos = 0;
-		top_iph->tot_len = htons(skb->len);
+		top_iph->tot_len = htons((*pskb)->len);
 		top_iph->frag_off = 0;
 		if (!(iph->frag_off&htons(IP_DF)))
 			__ip_select_ident(top_iph, dst, 0);
@@ -95,12 +96,12 @@
 		ah = (struct ip_auth_hdr*)(top_iph+1);
 		ah->nexthdr = IPPROTO_IPIP;
 	} else {
-		memcpy(&tmp_iph, skb->data, iph->ihl*4);
-		top_iph = (struct iphdr*)skb_push(skb, x->props.header_len);
+		memcpy(&tmp_iph, (*pskb)->data, iph->ihl*4);
+		top_iph = (struct iphdr*)skb_push(*pskb, x->props.header_len);
 		memcpy(top_iph, &tmp_iph, iph->ihl*4);
 		iph = &tmp_iph.iph;
 		top_iph->tos = 0;
-		top_iph->tot_len = htons(skb->len);
+		top_iph->tot_len = htons((*pskb)->len);
 		top_iph->frag_off = 0;
 		top_iph->ttl = 0;
 		top_iph->protocol = IPPROTO_AH;
@@ -120,14 +121,14 @@
 	ah->reserved = 0;
 	ah->spi = x->id.spi;
 	ah->seq_no = htonl(++x->replay.oseq);
-	ahp->icv(ahp, skb, ah->auth_data);
+	ahp->icv(ahp, *pskb, ah->auth_data);
 	top_iph->tos = iph->tos;
 	top_iph->ttl = iph->ttl;
 	if (x->props.mode) {
 		if (x->props.flags & XFRM_STATE_NOECN)
 			IP_ECN_clear(top_iph);
 		top_iph->frag_off = iph->frag_off&~htons(IP_MF|IP_OFFSET);
-		memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+		memset(&(IPCB(*pskb)->opt), 0, sizeof(struct ip_options));
 	} else {
 		top_iph->frag_off = iph->frag_off;
 		top_iph->daddr = iph->daddr;
@@ -136,12 +137,12 @@
 	}
 	ip_send_check(top_iph);
 
-	skb->nh.raw = skb->data;
+	(*pskb)->nh.raw = (*pskb)->data;
 
-	x->curlft.bytes += skb->len;
+	x->curlft.bytes += (*pskb)->len;
 	x->curlft.packets++;
 	spin_unlock_bh(&x->lock);
-	if ((skb->dst = dst_pop(dst)) == NULL) {
+	if (((*pskb)->dst = dst_pop(dst)) == NULL) {
 		err = -EHOSTUNREACH;
 		goto error_nolock;
 	}
@@ -150,7 +151,7 @@
 error:
 	spin_unlock_bh(&x->lock);
 error_nolock:
-	kfree_skb(skb);
+	kfree_skb(*pskb);
 	return err;
 }
 
diff -Nru a/net/ipv4/esp4.c b/net/ipv4/esp4.c
--- a/net/ipv4/esp4.c	Tue Jun 15 22:20:41 2004
+++ b/net/ipv4/esp4.c	Tue Jun 15 22:20:41 2004
@@ -20,10 +20,10 @@
 	__u8		proto;
 };
 
-int esp_output(struct sk_buff *skb)
+int esp_output(struct sk_buff **pskb)
 {
 	int err;
-	struct dst_entry *dst = skb->dst;
+	struct dst_entry *dst = (*pskb)->dst;
 	struct xfrm_state *x  = dst->xfrm;
 	struct iphdr *iph, *top_iph;
 	struct ip_esp_hdr *esph;
@@ -42,28 +42,28 @@
 		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;
+	if ((*pskb)->ip_summed == CHECKSUM_HW) {
+		err = skb_checksum_help(pskb, 0);
+		if (err)
+			goto error_nolock;
 	}
 
 	spin_lock_bh(&x->lock);
-	err = xfrm_check_output(x, skb, AF_INET);
+	err = xfrm_check_output(x, *pskb, AF_INET);
 	if (err)
 		goto error;
 	err = -ENOMEM;
 
 	/* Strip IP header in transport mode. Save it. */
 	if (!x->props.mode) {
-		iph = skb->nh.iph;
+		iph = (*pskb)->nh.iph;
 		memcpy(&tmp_iph, iph, iph->ihl*4);
-		__skb_pull(skb, iph->ihl*4);
+		__skb_pull(*pskb, iph->ihl*4);
 	}
 	/* Now skb is pure payload to encrypt */
 
 	/* Round to block size */
-	clen = skb->len;
+	clen = (*pskb)->len;
 
 	esp = x->data;
 	alen = esp->auth.icv_trunc_len;
@@ -73,23 +73,23 @@
 	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)
+	if ((nfrags = skb_cow_data(*pskb, clen-(*pskb)->len+alen, &trailer)) < 0)
 		goto error;
 
 	/* Fill padding... */
 	do {
 		int i;
-		for (i=0; i<clen-skb->len - 2; i++)
+		for (i=0; i<clen-(*pskb)->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);
+	*(u8*)(trailer->tail + clen-(*pskb)->len - 2) = (clen - (*pskb)->len)-2;
+	pskb_put(*pskb, trailer, clen - (*pskb)->len);
 
 	encap = x->encap;
 
-	iph = skb->nh.iph;
+	iph = (*pskb)->nh.iph;
 	if (x->props.mode) {
-		top_iph = (struct iphdr*)skb_push(skb, x->props.header_len);
+		top_iph = (struct iphdr*)skb_push(*pskb, x->props.header_len);
 		esph = (struct ip_esp_hdr*)(top_iph+1);
 		if (encap && encap->encap_type) {
 			switch (encap->encap_type) {
@@ -121,7 +121,7 @@
 		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->tot_len = htons((*pskb)->len + alen);
 		top_iph->frag_off = iph->frag_off&htons(IP_DF);
 		if (!(top_iph->frag_off))
 			ip_select_ident(top_iph, dst, 0);
@@ -129,10 +129,10 @@
 		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));
+		memset(&(IPCB(*pskb)->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);
+		esph = (struct ip_esp_hdr*)skb_push(*pskb, x->props.header_len);
+		top_iph = (struct iphdr*)skb_push(*pskb, iph->ihl*4);
 		memcpy(top_iph, &tmp_iph, iph->ihl*4);
 		if (encap && encap->encap_type) {
 			switch (encap->encap_type) {
@@ -159,7 +159,7 @@
 		} else
 			top_iph->protocol = IPPROTO_ESP;
 		iph = &tmp_iph.iph;
-		top_iph->tot_len = htons(skb->len + alen);
+		top_iph->tot_len = htons((*pskb)->len + alen);
 		top_iph->check = 0;
 		top_iph->frag_off = iph->frag_off;
 		*(u8*)(trailer->tail - 1) = iph->protocol;
@@ -169,7 +169,7 @@
 	if (encap && uh) {
 		uh->source = encap->encap_sport;
 		uh->dest = encap->encap_dport;
-		uh->len = htons(skb->len + alen - sizeof(struct iphdr));
+		uh->len = htons((*pskb)->len + alen - sizeof(struct iphdr));
 		uh->check = 0;
 	}
 
@@ -188,7 +188,7 @@
 			if (!sg)
 				goto error;
 		}
-		skb_to_sgvec(skb, sg, esph->enc_data+esp->conf.ivlen-skb->data, clen);
+		skb_to_sgvec(*pskb, sg, esph->enc_data+esp->conf.ivlen-(*pskb)->data, clen);
 		crypto_cipher_encrypt(tfm, sg, sg, clen);
 		if (unlikely(sg != sgbuf))
 			kfree(sg);
@@ -200,19 +200,19 @@
 	}
 
 	if (esp->auth.icv_full_len) {
-		esp->auth.icv(esp, skb, (u8*)esph-skb->data,
+		esp->auth.icv(esp, *pskb, (u8*)esph-(*pskb)->data,
 		              sizeof(struct ip_esp_hdr) + esp->conf.ivlen+clen, trailer->tail);
-		pskb_put(skb, trailer, alen);
+		pskb_put(*pskb, trailer, alen);
 	}
 
 	ip_send_check(top_iph);
 
-	skb->nh.raw = skb->data;
+	(*pskb)->nh.raw = (*pskb)->data;
 
-	x->curlft.bytes += skb->len;
+	x->curlft.bytes += (*pskb)->len;
 	x->curlft.packets++;
 	spin_unlock_bh(&x->lock);
-	if ((skb->dst = dst_pop(dst)) == NULL) {
+	if (((*pskb)->dst = dst_pop(dst)) == NULL) {
 		err = -EHOSTUNREACH;
 		goto error_nolock;
 	}
@@ -221,7 +221,7 @@
 error:
 	spin_unlock_bh(&x->lock);
 error_nolock:
-	kfree_skb(skb);
+	kfree_skb(*pskb);
 	return err;
 }
 
diff -Nru a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
--- a/net/ipv4/ip_output.c	Tue Jun 15 22:21:04 2004
+++ b/net/ipv4/ip_output.c	Tue Jun 15 22:21:04 2004
@@ -223,8 +223,9 @@
 		       ip_finish_output2);
 }
 
-int ip_mc_output(struct sk_buff *skb)
+int ip_mc_output(struct sk_buff **pskb)
 {
+	struct sk_buff *skb = *pskb;
 	struct sock *sk = skb->sk;
 	struct rtable *rt = (struct rtable*)skb->dst;
 	struct net_device *dev = rt->u.dst.dev;
@@ -283,9 +284,11 @@
 		return ip_finish_output(skb);
 }
 
-int ip_output(struct sk_buff *skb)
+int ip_output(struct sk_buff **pskb)
 {
-	IP_INC_STATS(IpOutRequests);
+	struct sk_buff *skb = *pskb;
+
+	IP_INC_STATS(IpOutRequests);
 
 	if ((skb->len > dst_pmtu(skb->dst) || skb_shinfo(skb)->frag_list) &&
 	    !skb_shinfo(skb)->tso_size)
@@ -567,7 +570,7 @@
 #ifdef CONFIG_BRIDGE_NETFILTER
 	/* for bridged IP traffic encapsulated inside f.e. a vlan header,
 	 * we need to make room for the encapsulating header */
-	ll_rs = LL_RESERVED_SPACE(rt->u.dst.dev + nf_bridge_pad(skb));
+	ll_rs = LL_RESERVED_SPACE_EXTRA(rt->u.dst.dev, nf_bridge_pad(skb));
 	mtu -= nf_bridge_pad(skb);
 #else
 	ll_rs = LL_RESERVED_SPACE(rt->u.dst.dev);
diff -Nru a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
--- a/net/ipv4/ipcomp.c	Tue Jun 15 22:20:44 2004
+++ b/net/ipv4/ipcomp.c	Tue Jun 15 22:20:44 2004
@@ -143,10 +143,10 @@
 	skb->nh.raw = skb->data;
 }
 
-static int ipcomp_output(struct sk_buff *skb)
+static int ipcomp_output(struct sk_buff **pskb)
 {
 	int err;
-	struct dst_entry *dst = skb->dst;
+	struct dst_entry *dst = (*pskb)->dst;
 	struct xfrm_state *x = dst->xfrm;
 	struct iphdr *iph, *top_iph;
 	struct ip_comp_hdr *ipch;
@@ -157,25 +157,26 @@
 	} tmp_iph;
 	int hdr_len = 0;
 
-	if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
-		err = -EINVAL;
-		goto error_nolock;
+	if ((*pskb)->ip_summed == CHECKSUM_HW) {
+		err = skb_checksum_help(pskb, 0);
+		if (err)
+			goto error_nolock;
 	}
 
 	spin_lock_bh(&x->lock);
-	err = xfrm_check_output(x, skb, AF_INET);
+	err = xfrm_check_output(x, *pskb, AF_INET);
 	if (err)
 		goto error;
 
 	/* Don't bother compressing */
 	if (!x->props.mode) {
-		iph = skb->nh.iph;
+		iph = (*pskb)->nh.iph;
 		hdr_len = iph->ihl * 4;
 	}
-	if ((skb->len - hdr_len) < ipcd->threshold) {
+	if (((*pskb)->len - hdr_len) < ipcd->threshold) {
 		if (x->props.mode) {
-			ipcomp_tunnel_encap(x, skb);
-			iph = skb->nh.iph;
+			ipcomp_tunnel_encap(x, *pskb);
+			iph = (*pskb)->nh.iph;
 			iph->protocol = IPPROTO_IPIP;
 			ip_send_check(iph);
 		}
@@ -183,19 +184,19 @@
 	}
 
 	if (x->props.mode) 
-		ipcomp_tunnel_encap(x, skb);
+		ipcomp_tunnel_encap(x, *pskb);
 
-	if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
-	    skb_linearize(skb, GFP_ATOMIC) != 0) {
+	if ((skb_is_nonlinear(*pskb) || skb_cloned(*pskb)) &&
+	    skb_linearize(*pskb, GFP_ATOMIC) != 0) {
 	    	err = -ENOMEM;
 	    	goto error;
 	}
 	
-	err = ipcomp_compress(x, skb);
+	err = ipcomp_compress(x, *pskb);
 	if (err) {
 		if (err == -EMSGSIZE) {
 			if (x->props.mode) {
-				iph = skb->nh.iph;
+				iph = (*pskb)->nh.iph;
 				iph->protocol = IPPROTO_IPIP;
 				ip_send_check(iph);
 			}
@@ -205,14 +206,14 @@
 	}
 
 	/* Install ipcomp header, convert into ipcomp datagram. */
-	iph = skb->nh.iph;
+	iph = (*pskb)->nh.iph;
 	memcpy(&tmp_iph, iph, iph->ihl * 4);
-	top_iph = (struct iphdr *)skb_push(skb, sizeof(struct ip_comp_hdr));
+	top_iph = (struct iphdr *)skb_push(*pskb, 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->tot_len = htons((*pskb)->len);
 	iph->protocol = IPPROTO_COMP;
 	iph->check = 0;
 	ipch = (struct ip_comp_hdr *)((char *)iph + iph->ihl * 4);
@@ -220,14 +221,14 @@
 	ipch->flags = 0;
 	ipch->cpi = htons((u16 )ntohl(x->id.spi));
 	ip_send_check(iph);
-	skb->nh.raw = skb->data;
+	(*pskb)->nh.raw = (*pskb)->data;
 
 out_ok:
-	x->curlft.bytes += skb->len;
+	x->curlft.bytes += (*pskb)->len;
 	x->curlft.packets++;
 	spin_unlock_bh(&x->lock);
 	
-	if ((skb->dst = dst_pop(dst)) == NULL) {
+	if (((*pskb)->dst = dst_pop(dst)) == NULL) {
 		err = -EHOSTUNREACH;
 		goto error_nolock;
 	}
@@ -238,7 +239,7 @@
 error:
 	spin_unlock_bh(&x->lock);
 error_nolock:
-	kfree_skb(skb);
+	kfree_skb(*pskb);
 	goto out_exit;
 }
 
@@ -339,6 +340,7 @@
 	struct ipcomp_data *ipcd = x->data;
 	if (!ipcd)
 		return;
+	xfrm_state_delete_tunnel(x);
 	ipcomp_free_data(ipcd);
 	kfree(ipcd);
 }
diff -Nru a/net/ipv4/ipip.c b/net/ipv4/ipip.c
--- a/net/ipv4/ipip.c	Tue Jun 15 22:21:05 2004
+++ b/net/ipv4/ipip.c	Tue Jun 15 22:21:05 2004
@@ -479,6 +479,7 @@
 	read_lock(&ipip_lock);
 	if ((tunnel = ipip_tunnel_lookup(iph->saddr, iph->daddr)) != NULL) {
 		if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+			read_unlock(&ipip_lock);
 			kfree_skb(skb);
 			return 0;
 		}
diff -Nru a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
--- a/net/ipv4/netfilter/arp_tables.c	Tue Jun 15 22:20:43 2004
+++ b/net/ipv4/netfilter/arp_tables.c	Tue Jun 15 22:20:43 2004
@@ -179,11 +179,10 @@
 		return 0;
 	}
 
-	/* Look for ifname matches; this should unroll nicely. */
-	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
-		ret |= (((const unsigned long *)indev)[i]
-			^ ((const unsigned long *)arpinfo->iniface)[i])
-			& ((const unsigned long *)arpinfo->iniface_mask)[i];
+	/* Look for ifname matches.  */
+	for (i = 0, ret = 0; i < IFNAMSIZ; i++) {
+		ret |= (indev[i] ^ arpinfo->iniface[i])
+			& arpinfo->iniface_mask[i];
 	}
 
 	if (FWINV(ret != 0, ARPT_INV_VIA_IN)) {
@@ -388,12 +387,12 @@
 }
 #endif
 
-static inline struct arpt_table *find_table_lock(const char *name, int *error, struct semaphore *mutex)
+static inline struct arpt_table *arpt_find_table_lock(const char *name, int *error, struct semaphore *mutex)
 {
 	return find_inlist_lock(&arpt_tables, name, "arptable_", error, mutex);
 }
 
-static inline struct arpt_target *find_target_lock(const char *name, int *error, struct semaphore *mutex)
+struct arpt_target *arpt_find_target_lock(const char *name, int *error, struct semaphore *mutex)
 {
 	return find_inlist_lock(&arpt_target, name, "arpt_", error, mutex);
 }
@@ -543,7 +542,7 @@
 	}
 
 	t = arpt_get_target(e);
-	target = find_target_lock(t->u.user.name, &ret, &arpt_mutex);
+	target = arpt_find_target_lock(t->u.user.name, &ret, &arpt_mutex);
 	if (!target) {
 		duprintf("check_entry: `%s' not found\n", t->u.user.name);
 		goto out;
@@ -778,7 +777,7 @@
 
 static int copy_entries_to_user(unsigned int total_size,
 				struct arpt_table *table,
-				void *userptr)
+				void __user *userptr)
 {
 	unsigned int off, num, countersize;
 	struct arpt_entry *e;
@@ -838,12 +837,12 @@
 }
 
 static int get_entries(const struct arpt_get_entries *entries,
-		       struct arpt_get_entries *uptr)
+		       struct arpt_get_entries __user *uptr)
 {
 	int ret;
 	struct arpt_table *t;
 
-	t = find_table_lock(entries->name, &ret, &arpt_mutex);
+	t = arpt_find_table_lock(entries->name, &ret, &arpt_mutex);
 	if (t) {
 		duprintf("t->private->number = %u\n",
 			 t->private->number);
@@ -864,7 +863,7 @@
 	return ret;
 }
 
-static int do_replace(void *user, unsigned int len)
+static int do_replace(void __user *user, unsigned int len)
 {
 	int ret;
 	struct arpt_replace tmp;
@@ -909,7 +908,7 @@
 
 	duprintf("arp_tables: Translated table\n");
 
-	t = find_table_lock(tmp.name, &ret, &arpt_mutex);
+	t = arpt_find_table_lock(tmp.name, &ret, &arpt_mutex);
 	if (!t)
 		goto free_newinfo_counters_untrans;
 
@@ -980,7 +979,7 @@
 	return 0;
 }
 
-static int do_add_counters(void *user, unsigned int len)
+static int do_add_counters(void __user *user, unsigned int len)
 {
 	unsigned int i;
 	struct arpt_counters_info tmp, *paddc;
@@ -1002,7 +1001,7 @@
 		goto free;
 	}
 
-	t = find_table_lock(tmp.name, &ret, &arpt_mutex);
+	t = arpt_find_table_lock(tmp.name, &ret, &arpt_mutex);
 	if (!t)
 		goto free;
 
@@ -1027,7 +1026,7 @@
 	return ret;
 }
 
-static int do_arpt_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
+static int do_arpt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
 {
 	int ret;
 
@@ -1051,7 +1050,7 @@
 	return ret;
 }
 
-static int do_arpt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
+static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
 {
 	int ret;
 
@@ -1075,7 +1074,7 @@
 			break;
 		}
 		name[ARPT_TABLE_MAXNAMELEN-1] = '\0';
-		t = find_table_lock(name, &ret, &arpt_mutex);
+		t = arpt_find_table_lock(name, &ret, &arpt_mutex);
 		if (t) {
 			struct arpt_getinfo info;
 
@@ -1323,6 +1322,7 @@
 EXPORT_SYMBOL(arpt_register_table);
 EXPORT_SYMBOL(arpt_unregister_table);
 EXPORT_SYMBOL(arpt_do_table);
+EXPORT_SYMBOL(arpt_find_target_lock);
 EXPORT_SYMBOL(arpt_register_target);
 EXPORT_SYMBOL(arpt_unregister_target);
 
diff -Nru a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c
--- a/net/ipv4/netfilter/ip_conntrack_core.c	Tue Jun 15 22:20:35 2004
+++ b/net/ipv4/netfilter/ip_conntrack_core.c	Tue Jun 15 22:20:35 2004
@@ -324,8 +324,9 @@
 		ip_conntrack_destroyed(ct);
 
 	WRITE_LOCK(&ip_conntrack_lock);
-	/* Delete us from our own list to prevent corruption later */
-	list_del(&ct->sibling_list);
+	/* Make sure don't leave any orphaned expectations lying around */
+	if (ct->expecting)
+		remove_expectations(ct, 1);
 
 	/* Delete our master expectation */
 	if (ct->master) {
@@ -920,7 +921,7 @@
 }
 
 struct ip_conntrack_expect *
-ip_conntrack_expect_alloc()
+ip_conntrack_expect_alloc(void)
 {
 	struct ip_conntrack_expect *new;
 	
@@ -1127,10 +1128,8 @@
 	DUMP_TUPLE(newreply);
 
 	conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply;
-	if (!conntrack->master)
-		conntrack->helper = LIST_FIND(&helpers, helper_cmp,
-					      struct ip_conntrack_helper *,
-					      newreply);
+	if (!conntrack->master && list_empty(&conntrack->sibling_list))
+		conntrack->helper = ip_ct_find_helper(newreply);
 	WRITE_UNLOCK(&ip_conntrack_lock);
 
 	return 1;
@@ -1300,7 +1299,7 @@
 /* Reversing the socket's dst/src point of view gives us the reply
    mapping. */
 static int
-getorigdst(struct sock *sk, int optval, void *user, int *len)
+getorigdst(struct sock *sk, int optval, void __user *user, int *len)
 {
 	struct inet_opt *inet = inet_sk(sk);
 	struct ip_conntrack_tuple_hash *h;
diff -Nru a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
--- a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c	Tue Jun 15 22:21:05 2004
+++ b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c	Tue Jun 15 22:21:05 2004
@@ -177,6 +177,8 @@
 
 	if (skb_copy_bits(skb, skb->nh.iph->ihl * 4, &tcph, sizeof(tcph)) != 0)
 		return -1;
+	if (skb->len < skb->nh.iph->ihl * 4 + tcph.doff * 4)
+		return -1;
 
 	/* If only reply is a RST, we can consider ourselves not to
 	   have an established connection: this is a fairly common
diff -Nru a/net/ipv4/netfilter/ip_fw_compat.c b/net/ipv4/netfilter/ip_fw_compat.c
--- a/net/ipv4/netfilter/ip_fw_compat.c	Tue Jun 15 22:20:39 2004
+++ b/net/ipv4/netfilter/ip_fw_compat.c	Tue Jun 15 22:20:39 2004
@@ -69,7 +69,8 @@
 	/* Assume worse case: any hook could change packet */
 	(*pskb)->nfcache |= NFC_UNKNOWN | NFC_ALTERED;
 	if ((*pskb)->ip_summed == CHECKSUM_HW)
-		(*pskb)->ip_summed = CHECKSUM_NONE;
+		if (skb_checksum_help(pskb, (out == NULL)))
+			return NF_DROP;
 
 	switch (hooknum) {
 	case NF_IP_PRE_ROUTING:
@@ -181,7 +182,7 @@
 
 extern int ip_fw_ctl(int optval, void *m, unsigned int len);
 
-static int sock_fn(struct sock *sk, int optval, void *user, unsigned int len)
+static int sock_fn(struct sock *sk, int optval, void __user *user, unsigned int len)
 {
 	/* MAX of:
 	   2.2: sizeof(struct ip_fwtest) (~14x4 + 3x4 = 17x4)
diff -Nru a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c
--- a/net/ipv4/netfilter/ip_nat_standalone.c	Tue Jun 15 22:20:43 2004
+++ b/net/ipv4/netfilter/ip_nat_standalone.c	Tue Jun 15 22:20:43 2004
@@ -85,7 +85,8 @@
 
 	/* If we had a hardware checksum before, it's now invalid */
 	if ((*pskb)->ip_summed == CHECKSUM_HW)
-		(*pskb)->ip_summed = CHECKSUM_NONE;
+		if (skb_checksum_help(pskb, (out == NULL)))
+			return NF_DROP;
 
 	ct = ip_conntrack_get(*pskb, &ctinfo);
 	/* Can't track?  It's not due to stress, or conntrack would
diff -Nru a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
--- a/net/ipv4/netfilter/ip_tables.c	Tue Jun 15 22:20:38 2004
+++ b/net/ipv4/netfilter/ip_tables.c	Tue Jun 15 22:20:38 2004
@@ -61,9 +61,6 @@
 #endif
 #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
 
-/* Mutex protects lists (only traversed in user context). */
-static DECLARE_MUTEX(ipt_mutex);
-
 /* Must have mutex */
 #define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ipt_mutex) != 0)
 #define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ipt_mutex) != 0)
@@ -418,7 +415,7 @@
 {
 	void *ret;
 
-#if 0
+#if 0 
 	duprintf("find_inlist: searching for `%s' in %s.\n",
 		 name, head == &ipt_target ? "ipt_target"
 		 : head == &ipt_match ? "ipt_match"
@@ -461,7 +458,7 @@
 #endif
 
 static inline struct ipt_table *
-find_table_lock(const char *name, int *error, struct semaphore *mutex)
+ipt_find_table_lock(const char *name, int *error, struct semaphore *mutex)
 {
 	return find_inlist_lock(&ipt_tables, name, "iptable_", error, mutex);
 }
@@ -472,8 +469,8 @@
 	return find_inlist_lock(&ipt_match, name, "ipt_", error, mutex);
 }
 
-static inline struct ipt_target *
-find_target_lock(const char *name, int *error, struct semaphore *mutex)
+struct ipt_target *
+ipt_find_target_lock(const char *name, int *error, struct semaphore *mutex)
 {
 	return find_inlist_lock(&ipt_target, name, "ipt_", error, mutex);
 }
@@ -688,7 +685,7 @@
 		goto cleanup_matches;
 
 	t = ipt_get_target(e);
-	target = find_target_lock(t->u.user.name, &ret, &ipt_mutex);
+	target = ipt_find_target_lock(t->u.user.name, &ret, &ipt_mutex);
 	if (!target) {
 		duprintf("check_entry: `%s' not found\n", t->u.user.name);
 		goto cleanup_matches;
@@ -942,7 +939,7 @@
 static int
 copy_entries_to_user(unsigned int total_size,
 		     struct ipt_table *table,
-		     void *userptr)
+		     void __user *userptr)
 {
 	unsigned int off, num, countersize;
 	struct ipt_entry *e;
@@ -1020,12 +1017,12 @@
 
 static int
 get_entries(const struct ipt_get_entries *entries,
-	    struct ipt_get_entries *uptr)
+	    struct ipt_get_entries __user *uptr)
 {
 	int ret;
 	struct ipt_table *t;
 
-	t = find_table_lock(entries->name, &ret, &ipt_mutex);
+	t = ipt_find_table_lock(entries->name, &ret, &ipt_mutex);
 	if (t) {
 		duprintf("t->private->number = %u\n",
 			 t->private->number);
@@ -1047,7 +1044,7 @@
 }
 
 static int
-do_replace(void *user, unsigned int len)
+do_replace(void __user *user, unsigned int len)
 {
 	int ret;
 	struct ipt_replace tmp;
@@ -1092,7 +1089,7 @@
 
 	duprintf("ip_tables: Translated table\n");
 
-	t = find_table_lock(tmp.name, &ret, &ipt_mutex);
+	t = ipt_find_table_lock(tmp.name, &ret, &ipt_mutex);
 	if (!t)
 		goto free_newinfo_counters_untrans;
 
@@ -1173,7 +1170,7 @@
 }
 
 static int
-do_add_counters(void *user, unsigned int len)
+do_add_counters(void __user *user, unsigned int len)
 {
 	unsigned int i;
 	struct ipt_counters_info tmp, *paddc;
@@ -1195,7 +1192,7 @@
 		goto free;
 	}
 
-	t = find_table_lock(tmp.name, &ret, &ipt_mutex);
+	t = ipt_find_table_lock(tmp.name, &ret, &ipt_mutex);
 	if (!t)
 		goto free;
 
@@ -1221,7 +1218,7 @@
 }
 
 static int
-do_ipt_set_ctl(struct sock *sk,	int cmd, void *user, unsigned int len)
+do_ipt_set_ctl(struct sock *sk,	int cmd, void __user *user, unsigned int len)
 {
 	int ret;
 
@@ -1246,7 +1243,7 @@
 }
 
 static int
-do_ipt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
+do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
 {
 	int ret;
 
@@ -1270,7 +1267,7 @@
 			break;
 		}
 		name[IPT_TABLE_MAXNAMELEN-1] = '\0';
-		t = find_table_lock(name, &ret, &ipt_mutex);
+		t = ipt_find_table_lock(name, &ret, &ipt_mutex);
 		if (t) {
 			struct ipt_getinfo info;
 
@@ -1855,6 +1852,7 @@
 EXPORT_SYMBOL(ipt_do_table);
 EXPORT_SYMBOL(ipt_register_target);
 EXPORT_SYMBOL(ipt_unregister_target);
+EXPORT_SYMBOL(ipt_find_target_lock);
 
 module_init(init);
 module_exit(fini);
diff -Nru a/net/ipv4/netfilter/ipt_ECN.c b/net/ipv4/netfilter/ipt_ECN.c
--- a/net/ipv4/netfilter/ipt_ECN.c	Tue Jun 15 22:21:04 2004
+++ b/net/ipv4/netfilter/ipt_ECN.c	Tue Jun 15 22:21:04 2004
@@ -50,7 +50,7 @@
 
 /* Return 0 if there was an error. */
 static inline int
-set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo)
+set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo, int inward)
 {
 	struct tcphdr tcph;
 	u_int16_t diffs[2];
@@ -74,11 +74,15 @@
 		if (!skb_ip_make_writable(pskb,
 					  (*pskb)->nh.iph->ihl*4+sizeof(tcph)))
 			return 0;
-		tcph.check = csum_fold(csum_partial((char *)diffs,
-		                                    sizeof(diffs),
-		                                    tcph.check^0xFFFF));
+		if ((*pskb)->ip_summed != CHECKSUM_HW)
+			tcph.check = csum_fold(csum_partial((char *)diffs,
+					       sizeof(diffs),
+					       tcph.check^0xFFFF));
 		memcpy((*pskb)->data + (*pskb)->nh.iph->ihl*4,
 		       &tcph, sizeof(tcph));
+		if ((*pskb)->ip_summed == CHECKSUM_HW)
+			if (skb_checksum_help(pskb, inward))
+				return 0;
 		(*pskb)->nfcache |= NFC_ALTERED;
 	}
 	return 1;
@@ -100,7 +104,7 @@
 
 	if (einfo->operation & (IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR)
 	    && (*pskb)->nh.iph->protocol == IPPROTO_TCP)
-		if (!set_ect_tcp(pskb, einfo))
+		if (!set_ect_tcp(pskb, einfo, (out == NULL)))
 			return NF_DROP;
 
 	return IPT_CONTINUE;
diff -Nru a/net/ipv4/netfilter/ipt_TCPMSS.c b/net/ipv4/netfilter/ipt_TCPMSS.c
--- a/net/ipv4/netfilter/ipt_TCPMSS.c	Tue Jun 15 22:20:40 2004
+++ b/net/ipv4/netfilter/ipt_TCPMSS.c	Tue Jun 15 22:20:40 2004
@@ -186,8 +186,9 @@
 	       newmss);
 
  retmodified:
-	/* If we had a hardware checksum before, it's now invalid */
-	(*pskb)->ip_summed = CHECKSUM_NONE;
+	/* We never hw checksum SYN packets.  */
+	BUG_ON((*pskb)->ip_summed == CHECKSUM_HW);
+
 	(*pskb)->nfcache |= NFC_UNKNOWN | NFC_ALTERED;
 	return IPT_CONTINUE;
 }
diff -Nru a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c
--- a/net/ipv4/netfilter/ipt_recent.c	Tue Jun 15 22:20:37 2004
+++ b/net/ipv4/netfilter/ipt_recent.c	Tue Jun 15 22:20:37 2004
@@ -175,7 +175,7 @@
  * clear         -- Flush table, remove all entries
  */
 
-static int ip_recent_ctrl(struct file *file, const char *input, unsigned long size, void *data)
+static int ip_recent_ctrl(struct file *file, const char __user *input, unsigned long size, void *data)
 {
 	static const u_int32_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
 	u_int32_t val;
diff -Nru a/net/ipv4/route.c b/net/ipv4/route.c
--- a/net/ipv4/route.c	Tue Jun 15 22:21:05 2004
+++ b/net/ipv4/route.c	Tue Jun 15 22:21:05 2004
@@ -1339,8 +1359,10 @@
 		dst_set_expires(&rt->u.dst, 0);
 }
 
-static int ip_rt_bug(struct sk_buff *skb)
+static int ip_rt_bug(struct sk_buff **pskb)
 {
+	struct sk_buff *skb = *pskb;
+
 	printk(KERN_DEBUG "ip_rt_bug: %u.%u.%u.%u -> %u.%u.%u.%u, %s\n",
 		NIPQUAD(skb->nh.iph->saddr), NIPQUAD(skb->nh.iph->daddr),
 		skb->dev ? skb->dev->name : "?");
diff -Nru a/net/ipv4/xfrm4_tunnel.c b/net/ipv4/xfrm4_tunnel.c
--- a/net/ipv4/xfrm4_tunnel.c	Tue Jun 15 22:20:37 2004
+++ b/net/ipv4/xfrm4_tunnel.c	Tue Jun 15 22:20:37 2004
@@ -33,8 +33,9 @@
 	return ret;
 }
 
-static int ipip_output(struct sk_buff *skb)
+static int ipip_output(struct sk_buff **pskb)
 {
+	struct sk_buff *skb = *pskb;
 	struct dst_entry *dst = skb->dst;
 	struct xfrm_state *x = dst->xfrm;
 	struct iphdr *iph, *top_iph;
diff -Nru a/net/ipv6/ah6.c b/net/ipv6/ah6.c
--- a/net/ipv6/ah6.c	Tue Jun 15 22:20:42 2004
+++ b/net/ipv6/ah6.c	Tue Jun 15 22:20:42 2004
@@ -144,11 +144,11 @@
 	return nexthdr;
 }
 
-int ah6_output(struct sk_buff *skb)
+int ah6_output(struct sk_buff **pskb)
 {
 	int err;
 	int hdr_len = sizeof(struct ipv6hdr);
-	struct dst_entry *dst = skb->dst;
+	struct dst_entry *dst = (*pskb)->dst;
 	struct xfrm_state *x  = dst->xfrm;
 	struct ipv6hdr *iph = NULL;
 	struct ip_auth_hdr *ah;
@@ -156,54 +156,55 @@
 	u16 nh_offset = 0;
 	u8 nexthdr;
 
-	if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
-		err = -EINVAL;
-		goto error_nolock;
+	if ((*pskb)->ip_summed == CHECKSUM_HW) {
+		err = skb_checksum_help(pskb, 0);
+		if (err)
+			goto error_nolock;
 	}
 
 	spin_lock_bh(&x->lock);
-	err = xfrm_check_output(x, skb, AF_INET6);
+	err = xfrm_check_output(x, *pskb, AF_INET6);
 	if (err)
 		goto error;
 
 	if (x->props.mode) {
-		iph = skb->nh.ipv6h;
-		skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, x->props.header_len);
-		skb->nh.ipv6h->version = 6;
-		skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
-		skb->nh.ipv6h->nexthdr = IPPROTO_AH;
-		ipv6_addr_copy(&skb->nh.ipv6h->saddr,
+		iph = (*pskb)->nh.ipv6h;
+		(*pskb)->nh.ipv6h = (struct ipv6hdr*)skb_push(*pskb, x->props.header_len);
+		(*pskb)->nh.ipv6h->version = 6;
+		(*pskb)->nh.ipv6h->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
+		(*pskb)->nh.ipv6h->nexthdr = IPPROTO_AH;
+		ipv6_addr_copy(&(*pskb)->nh.ipv6h->saddr,
 			       (struct in6_addr *) &x->props.saddr);
-		ipv6_addr_copy(&skb->nh.ipv6h->daddr,
+		ipv6_addr_copy(&(*pskb)->nh.ipv6h->daddr,
 			       (struct in6_addr *) &x->id.daddr);
-		ah = (struct ip_auth_hdr*)(skb->nh.ipv6h+1);
+		ah = (struct ip_auth_hdr*)((*pskb)->nh.ipv6h+1);
 		ah->nexthdr = IPPROTO_IPV6;
 	} else {
-		hdr_len = skb->h.raw - skb->nh.raw;
+		hdr_len = (*pskb)->h.raw - (*pskb)->nh.raw;
 		iph = kmalloc(hdr_len, GFP_ATOMIC);
 		if (!iph) {
 			err = -ENOMEM;
 			goto error;
 		}
-		memcpy(iph, skb->data, hdr_len);
-		skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, x->props.header_len);
-		memcpy(skb->nh.ipv6h, iph, hdr_len);
-		nexthdr = ipv6_clear_mutable_options(skb, &nh_offset, XFRM_POLICY_OUT);
+		memcpy(iph, (*pskb)->data, hdr_len);
+		(*pskb)->nh.ipv6h = (struct ipv6hdr*)skb_push(*pskb, x->props.header_len);
+		memcpy((*pskb)->nh.ipv6h, iph, hdr_len);
+		nexthdr = ipv6_clear_mutable_options(*pskb, &nh_offset, XFRM_POLICY_OUT);
 		if (nexthdr == 0)
-			goto error;
+			goto error_free_iph;
 
-		skb->nh.raw[nh_offset] = IPPROTO_AH;
-		skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
-		ah = (struct ip_auth_hdr*)(skb->nh.raw+hdr_len);
-		skb->h.raw = (unsigned char*) ah;
+		(*pskb)->nh.raw[nh_offset] = IPPROTO_AH;
+		(*pskb)->nh.ipv6h->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
+		ah = (struct ip_auth_hdr*)((*pskb)->nh.raw+hdr_len);
+		(*pskb)->h.raw = (unsigned char*) ah;
 		ah->nexthdr = nexthdr;
 	}
 
-	skb->nh.ipv6h->priority    = 0;
-	skb->nh.ipv6h->flow_lbl[0] = 0;
-	skb->nh.ipv6h->flow_lbl[1] = 0;
-	skb->nh.ipv6h->flow_lbl[2] = 0;
-	skb->nh.ipv6h->hop_limit    = 0;
+	(*pskb)->nh.ipv6h->priority    = 0;
+	(*pskb)->nh.ipv6h->flow_lbl[0] = 0;
+	(*pskb)->nh.ipv6h->flow_lbl[1] = 0;
+	(*pskb)->nh.ipv6h->flow_lbl[2] = 0;
+	(*pskb)->nh.ipv6h->hop_limit    = 0;
 
 	ahp = x->data;
 	ah->hdrlen  = (XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + 
@@ -212,37 +213,39 @@
 	ah->reserved = 0;
 	ah->spi = x->id.spi;
 	ah->seq_no = htonl(++x->replay.oseq);
-	ahp->icv(ahp, skb, ah->auth_data);
+	ahp->icv(ahp, *pskb, ah->auth_data);
 
 	if (x->props.mode) {
-		skb->nh.ipv6h->hop_limit   = iph->hop_limit;
-		skb->nh.ipv6h->priority    = iph->priority; 	
-		skb->nh.ipv6h->flow_lbl[0] = iph->flow_lbl[0];
-		skb->nh.ipv6h->flow_lbl[1] = iph->flow_lbl[1];
-		skb->nh.ipv6h->flow_lbl[2] = iph->flow_lbl[2];
+		(*pskb)->nh.ipv6h->hop_limit   = iph->hop_limit;
+		(*pskb)->nh.ipv6h->priority    = iph->priority; 	
+		(*pskb)->nh.ipv6h->flow_lbl[0] = iph->flow_lbl[0];
+		(*pskb)->nh.ipv6h->flow_lbl[1] = iph->flow_lbl[1];
+		(*pskb)->nh.ipv6h->flow_lbl[2] = iph->flow_lbl[2];
 		if (x->props.flags & XFRM_STATE_NOECN)
-			IP6_ECN_clear(skb->nh.ipv6h);
+			IP6_ECN_clear((*pskb)->nh.ipv6h);
 	} else {
-		memcpy(skb->nh.ipv6h, iph, hdr_len);
-		skb->nh.raw[nh_offset] = IPPROTO_AH;
-		skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
+		memcpy((*pskb)->nh.ipv6h, iph, hdr_len);
+		(*pskb)->nh.raw[nh_offset] = IPPROTO_AH;
+		(*pskb)->nh.ipv6h->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
 		kfree (iph);
 	}
 
-	skb->nh.raw = skb->data;
+	(*pskb)->nh.raw = (*pskb)->data;
 
-	x->curlft.bytes += skb->len;
+	x->curlft.bytes += (*pskb)->len;
 	x->curlft.packets++;
 	spin_unlock_bh(&x->lock);
-	if ((skb->dst = dst_pop(dst)) == NULL) {
+	if (((*pskb)->dst = dst_pop(dst)) == NULL) {
 		err = -EHOSTUNREACH;
 		goto error_nolock;
 	}
 	return NET_XMIT_BYPASS;
+error_free_iph:
+	kfree(iph);
 error:
 	spin_unlock_bh(&x->lock);
 error_nolock:
-	kfree_skb(skb);
+	kfree_skb(*pskb);
 	return err;
 }
 
@@ -360,7 +363,7 @@
 	struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+offset);
 	struct xfrm_state *x;
 
-	if (type != ICMPV6_DEST_UNREACH ||
+	if (type != ICMPV6_DEST_UNREACH &&
 	    type != ICMPV6_PKT_TOOBIG)
 		return;
 
diff -Nru a/net/ipv6/esp6.c b/net/ipv6/esp6.c
--- a/net/ipv6/esp6.c	Tue Jun 15 22:20:42 2004
+++ b/net/ipv6/esp6.c	Tue Jun 15 22:20:42 2004
@@ -40,11 +40,11 @@
 
 #define MAX_SG_ONSTACK 4
 
-int esp6_output(struct sk_buff *skb)
+int esp6_output(struct sk_buff **pskb)
 {
 	int err;
 	int hdr_len = 0;
-	struct dst_entry *dst = skb->dst;
+	struct dst_entry *dst = (*pskb)->dst;
 	struct xfrm_state *x  = dst->xfrm;
 	struct ipv6hdr *iph = NULL, *top_iph;
 	struct ipv6_esp_hdr *esph;
@@ -58,14 +58,14 @@
 	u8 *prevhdr;
 	u8 nexthdr = 0;
 
-	/* 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;
+	if ((*pskb)->ip_summed == CHECKSUM_HW) {
+		err = skb_checksum_help(pskb, 0);
+		if (err)
+			goto error_nolock;
 	}
 
 	spin_lock_bh(&x->lock);
-	err = xfrm_check_output(x, skb, AF_INET6);
+	err = xfrm_check_output(x, *pskb, AF_INET6);
 	if (err)
 		goto error;
 	err = -ENOMEM;
@@ -73,7 +73,7 @@
 	/* Strip IP header in transport mode. Save it. */
 
 	if (!x->props.mode) {
-		hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
+		hdr_len = ip6_find_1stfragopt(*pskb, &prevhdr);
 		nexthdr = *prevhdr;
 		*prevhdr = IPPROTO_ESP;
 		iph = kmalloc(hdr_len, GFP_ATOMIC);
@@ -81,14 +81,14 @@
 			err = -ENOMEM;
 			goto error;
 		}
-		memcpy(iph, skb->nh.raw, hdr_len);
-		__skb_pull(skb, hdr_len);
+		memcpy(iph, (*pskb)->nh.raw, hdr_len);
+		__skb_pull(*pskb, hdr_len);
 	}
 
 	/* Now skb is pure payload to encrypt */
 
 	/* Round to block size */
-	clen = skb->len;
+	clen = (*pskb)->len;
 
 	esp = x->data;
 	alen = esp->auth.icv_trunc_len;
@@ -98,7 +98,7 @@
 	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) {
+	if ((nfrags = skb_cow_data(*pskb, clen-(*pskb)->len+alen, &trailer)) < 0) {
 		if (!x->props.mode && iph) kfree(iph);
 		goto error;
 	}
@@ -106,15 +106,15 @@
 	/* Fill padding... */
 	do {
 		int i;
-		for (i=0; i<clen-skb->len - 2; i++)
+		for (i=0; i<clen-(*pskb)->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);
+	*(u8*)(trailer->tail + clen-(*pskb)->len - 2) = (clen - (*pskb)->len)-2;
+	pskb_put(*pskb, trailer, clen - (*pskb)->len);
 
 	if (x->props.mode) {
-		iph = skb->nh.ipv6h;
-		top_iph = (struct ipv6hdr*)skb_push(skb, x->props.header_len);
+		iph = (*pskb)->nh.ipv6h;
+		top_iph = (struct ipv6hdr*)skb_push(*pskb, x->props.header_len);
 		esph = (struct ipv6_esp_hdr*)(top_iph+1);
 		*(u8*)(trailer->tail - 1) = IPPROTO_IPV6;
 		top_iph->version = 6;
@@ -125,19 +125,19 @@
 		if (x->props.flags & XFRM_STATE_NOECN)
 			IP6_ECN_clear(top_iph);
 		top_iph->nexthdr = IPPROTO_ESP;
-		top_iph->payload_len = htons(skb->len + alen - sizeof(struct ipv6hdr));
+		top_iph->payload_len = htons((*pskb)->len + alen - sizeof(struct ipv6hdr));
 		top_iph->hop_limit = iph->hop_limit;
 		ipv6_addr_copy(&top_iph->saddr,
 			       (struct in6_addr *)&x->props.saddr);
 		ipv6_addr_copy(&top_iph->daddr,
 			       (struct in6_addr *)&x->id.daddr);
 	} else { 
-		esph = (struct ipv6_esp_hdr*)skb_push(skb, x->props.header_len);
-		skb->h.raw = (unsigned char*)esph;
-		top_iph = (struct ipv6hdr*)skb_push(skb, hdr_len);
+		esph = (struct ipv6_esp_hdr*)skb_push(*pskb, x->props.header_len);
+		(*pskb)->h.raw = (unsigned char*)esph;
+		top_iph = (struct ipv6hdr*)skb_push(*pskb, hdr_len);
 		memcpy(top_iph, iph, hdr_len);
 		kfree(iph);
-		top_iph->payload_len = htons(skb->len + alen - sizeof(struct ipv6hdr));
+		top_iph->payload_len = htons((*pskb)->len + alen - sizeof(struct ipv6hdr));
 		*(u8*)(trailer->tail - 1) = nexthdr;
 	}
 
@@ -156,7 +156,7 @@
 			if (!sg)
 				goto error;
 		}
-		skb_to_sgvec(skb, sg, esph->enc_data+esp->conf.ivlen-skb->data, clen);
+		skb_to_sgvec(*pskb, sg, esph->enc_data+esp->conf.ivlen-(*pskb)->data, clen);
 		crypto_cipher_encrypt(tfm, sg, sg, clen);
 		if (unlikely(sg != sgbuf))
 			kfree(sg);
@@ -168,17 +168,17 @@
 	}
 
 	if (esp->auth.icv_full_len) {
-		esp->auth.icv(esp, skb, (u8*)esph-skb->data,
+		esp->auth.icv(esp, *pskb, (u8*)esph-(*pskb)->data,
 			sizeof(struct ipv6_esp_hdr) + esp->conf.ivlen+clen, trailer->tail);
-		pskb_put(skb, trailer, alen);
+		pskb_put(*pskb, trailer, alen);
 	}
 
-	skb->nh.raw = skb->data;
+	(*pskb)->nh.raw = (*pskb)->data;
 
-	x->curlft.bytes += skb->len;
+	x->curlft.bytes += (*pskb)->len;
 	x->curlft.packets++;
 	spin_unlock_bh(&x->lock);
-	if ((skb->dst = dst_pop(dst)) == NULL) {
+	if (((*pskb)->dst = dst_pop(dst)) == NULL) {
 		err = -EHOSTUNREACH;
 		goto error_nolock;
 	}
@@ -187,7 +187,7 @@
 error:
 	spin_unlock_bh(&x->lock);
 error_nolock:
-	kfree_skb(skb);
+	kfree_skb(*pskb);
 	return err;
 }
 
@@ -324,7 +324,7 @@
 	struct ipv6_esp_hdr *esph = (struct ipv6_esp_hdr*)(skb->data+offset);
 	struct xfrm_state *x;
 
-	if (type != ICMPV6_DEST_UNREACH ||
+	if (type != ICMPV6_DEST_UNREACH && 
 	    type != ICMPV6_PKT_TOOBIG)
 		return;
 
diff -Nru a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
--- a/net/ipv6/ip6_output.c	Tue Jun 15 22:20:36 2004
+++ b/net/ipv6/ip6_output.c	Tue Jun 15 22:20:36 2004
@@ -55,7 +55,7 @@
 #include <net/icmp.h>
 #include <net/xfrm.h>
 
-static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*));
+static int ip6_fragment(struct sk_buff **pskb, int (*output)(struct sk_buff**));
 
 static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr)
 {
@@ -107,8 +107,9 @@
 }
 
 
-int ip6_output2(struct sk_buff *skb)
+static int ip6_output2(struct sk_buff **pskb)
 {
+	struct sk_buff *skb = *pskb;
 	struct dst_entry *dst = skb->dst;
 	struct net_device *dev = dst->dev;
 
@@ -132,24 +133,26 @@
 					ip6_dev_loopback_xmit);
 
 			if (skb->nh.ipv6h->hop_limit == 0) {
-				IP6_INC_STATS(Ip6OutDiscards);
+				IP6_INC_STATS(Ip6OutDiscards);
 				kfree_skb(skb);
 				return 0;
 			}
 		}
 
-		IP6_INC_STATS(Ip6OutMcastPkts);
+		IP6_INC_STATS(Ip6OutMcastPkts);
 	}
 
 	return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish);
 }
 
-int ip6_output(struct sk_buff *skb)
+int ip6_output(struct sk_buff **pskb)
 {
+	struct sk_buff *skb = *pskb;
+
 	if ((skb->len > dst_pmtu(skb->dst) || skb_shinfo(skb)->frag_list))
-		return ip6_fragment(skb, ip6_output2);
+		return ip6_fragment(pskb, ip6_output2);
 	else
-		return ip6_output2(skb);
+		return ip6_output2(pskb);
 }
 
 #ifdef CONFIG_NETFILTER
@@ -513,11 +516,11 @@
 	return offset;
 }
 
-static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
+static int ip6_fragment(struct sk_buff **pskb, int (*output)(struct sk_buff**))
 {
 	struct net_device *dev;
+	struct sk_buff *frag, *skb = *pskb;
 	struct rt6_info *rt = (struct rt6_info*)skb->dst;
-	struct sk_buff *frag;
 	struct ipv6hdr *tmp_hdr;
 	struct frag_hdr *fh;
 	unsigned int mtu, hlen, left, len;
@@ -604,11 +607,13 @@
 				frag->nh.ipv6h->payload_len = htons(frag->len - sizeof(struct ipv6hdr));
 				ip6_copy_metadata(frag, skb);
 			}
-			err = output(skb);
-
-			if (err || !frag)
+			err = output(pskb);
+			if (err || !frag) {
+				if (unlikely(skb != *pskb))
+					skb = *pskb;
 				break;
-
+			}
+			
 			skb = frag;
 			frag = skb->next;
 			skb->next = NULL;
@@ -719,19 +724,19 @@
 		 *	Put this fragment into the sending queue.
 		 */
 
-		IP6_INC_STATS(Ip6FragCreates);
+		IP6_INC_STATS(Ip6FragCreates);
 
-		err = output(frag);
+		err = output(&frag);
 		if (err)
 			goto fail;
 	}
 	kfree_skb(skb);
-	IP6_INC_STATS(Ip6FragOKs);
+	IP6_INC_STATS(Ip6FragOKs);
 	return err;
 
 fail:
 	kfree_skb(skb); 
-	IP6_INC_STATS(Ip6FragFails);
+	IP6_INC_STATS(Ip6FragFails);
 	return err;
 }
 
diff -Nru a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
--- a/net/ipv6/ipcomp6.c	Tue Jun 15 22:20:40 2004
+++ b/net/ipv6/ipcomp6.c	Tue Jun 15 22:20:40 2004
@@ -118,10 +118,10 @@
 	return err;
 }
 
-static int ipcomp6_output(struct sk_buff *skb)
+static int ipcomp6_output(struct sk_buff **pskb)
 {
 	int err;
-	struct dst_entry *dst = skb->dst;
+	struct dst_entry *dst = (*pskb)->dst;
 	struct xfrm_state *x = dst->xfrm;
 	struct ipv6hdr *tmp_iph = NULL, *iph, *top_iph;
 	int hdr_len = 0;
@@ -132,54 +132,55 @@
 	int plen, dlen;
 	u8 *start, *scratch = ipcd->scratch;
 
-	if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
-		err = -EINVAL;
-		goto error_nolock;
+	if ((*pskb)->ip_summed == CHECKSUM_HW) {
+		err = skb_checksum_help(pskb, 0);
+		if (err)
+			goto error_nolock;
 	}
 
 	spin_lock_bh(&x->lock);
 
-	err = xfrm_check_output(x, skb, AF_INET6);
+	err = xfrm_check_output(x, *pskb, AF_INET6);
 	if (err)
 		goto error;
 
 	if (x->props.mode) {
 		hdr_len = sizeof(struct ipv6hdr);
 		nexthdr = IPPROTO_IPV6;
-		iph = skb->nh.ipv6h;
-		top_iph = (struct ipv6hdr *)skb_push(skb, sizeof(struct ipv6hdr));
+		iph = (*pskb)->nh.ipv6h;
+		top_iph = (struct ipv6hdr *)skb_push(*pskb, sizeof(struct ipv6hdr));
 		top_iph->version = 6;
 		top_iph->priority = iph->priority;
 		top_iph->flow_lbl[0] = iph->flow_lbl[0];
 		top_iph->flow_lbl[1] = iph->flow_lbl[1];
 		top_iph->flow_lbl[2] = iph->flow_lbl[2];
 		top_iph->nexthdr = IPPROTO_IPV6; /* initial */
-		top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
+		top_iph->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
 		top_iph->hop_limit = iph->hop_limit;
 		memcpy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr, sizeof(struct in6_addr));
 		memcpy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr, sizeof(struct in6_addr));
-		skb->nh.raw = skb->data; /* == top_iph */
-		skb->h.raw = skb->nh.raw + hdr_len;
+		(*pskb)->nh.raw = (*pskb)->data; /* == top_iph */
+		(*pskb)->h.raw = (*pskb)->nh.raw + hdr_len;
 	} else {
-		hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
+		hdr_len = ip6_find_1stfragopt(*pskb, &prevhdr);
 		nexthdr = *prevhdr;
 	}
 
 	/* check whether datagram len is larger than threshold */
-	if ((skb->len - hdr_len) < ipcd->threshold) {
+	if (((*pskb)->len - hdr_len) < ipcd->threshold) {
 		goto out_ok;
 	}
 
-	if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
-		skb_linearize(skb, GFP_ATOMIC) != 0) {
+	if ((skb_is_nonlinear(*pskb) || skb_cloned(*pskb)) &&
+		skb_linearize(*pskb, GFP_ATOMIC) != 0) {
 		err = -ENOMEM;
 		goto error;
 	}
 
 	/* compression */
-	plen = skb->len - hdr_len;
+	plen = (*pskb)->len - hdr_len;
 	dlen = IPCOMP_SCRATCH_SIZE;
-	start = skb->data + hdr_len;
+	start = (*pskb)->data + hdr_len;
 
 	err = crypto_comp_compress(ipcd->tfm, start, plen, scratch, &dlen);
 	if (err) {
@@ -189,7 +190,7 @@
 		goto out_ok;
 	}
 	memcpy(start, scratch, dlen);
-	pskb_trim(skb, hdr_len+dlen);
+	pskb_trim(*pskb, hdr_len+dlen);
 
 	/* insert ipcomp header and replace datagram */
 	tmp_iph = kmalloc(hdr_len, GFP_ATOMIC);
@@ -197,16 +198,16 @@
 		err = -ENOMEM;
 		goto error;
 	}
-	memcpy(tmp_iph, skb->nh.raw, hdr_len);
-	top_iph = (struct ipv6hdr*)skb_push(skb, sizeof(struct ipv6_comp_hdr));
+	memcpy(tmp_iph, (*pskb)->nh.raw, hdr_len);
+	top_iph = (struct ipv6hdr*)skb_push(*pskb, sizeof(struct ipv6_comp_hdr));
 	memcpy(top_iph, tmp_iph, hdr_len);
 	kfree(tmp_iph);
 
 	if (x->props.mode && (x->props.flags & XFRM_STATE_NOECN))
 		IP6_ECN_clear(top_iph);
-	top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
-	skb->nh.raw = skb->data; /* top_iph */
-	ip6_find_1stfragopt(skb, &prevhdr); 
+	top_iph->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
+	(*pskb)->nh.raw = (*pskb)->data; /* top_iph */
+	ip6_find_1stfragopt(*pskb, &prevhdr); 
 	*prevhdr = IPPROTO_COMP;
 
 	ipch = (struct ipv6_comp_hdr *)((unsigned char *)top_iph + hdr_len);
@@ -214,13 +215,13 @@
 	ipch->flags = 0;
 	ipch->cpi = htons((u16 )ntohl(x->id.spi));
 
-	skb->h.raw = (unsigned char*)ipch;
+	(*pskb)->h.raw = (unsigned char*)ipch;
 out_ok:
-	x->curlft.bytes += skb->len;
+	x->curlft.bytes += (*pskb)->len;
 	x->curlft.packets++;
 	spin_unlock_bh(&x->lock);
 
-	if ((skb->dst = dst_pop(dst)) == NULL) {
+	if (((*pskb)->dst = dst_pop(dst)) == NULL) {
 		err = -EHOSTUNREACH;
 		goto error_nolock;
 	}
@@ -231,7 +232,7 @@
 error:
 	spin_unlock_bh(&x->lock);
 error_nolock:
-	kfree_skb(skb);
+	kfree_skb(*pskb);
 	goto out_exit;
 }
 
diff -Nru a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
--- a/net/ipv6/ndisc.c	Tue Jun 15 22:20:41 2004
+++ b/net/ipv6/ndisc.c	Tue Jun 15 22:20:41 2004
@@ -395,7 +395,7 @@
 
 	ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, src_addr, daddr);
 
-	dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output2);
+	dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output);
 	if (!dst)
 		return;
 
@@ -486,7 +486,7 @@
 
 	ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr);
 
-	dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output2);
+	dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output);
 	if (!dst)
 		return;
 
@@ -562,7 +562,7 @@
 
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr);
 
-	dst = ndisc_dst_alloc(dev, NULL, daddr, ip6_output2);
+	dst = ndisc_dst_alloc(dev, NULL, daddr, ip6_output);
 	if (!dst)
 		return;
 
diff -Nru a/net/ipv6/route.c b/net/ipv6/route.c
--- a/net/ipv6/route.c	Tue Jun 15 22:20:43 2004
+++ b/net/ipv6/route.c	Tue Jun 15 22:20:43 2004
@@ -83,9 +83,12 @@
 static struct rt6_info * ip6_rt_copy(struct rt6_info *ort);
 static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
 static struct dst_entry *ip6_negative_advice(struct dst_entry *);
+
+
 static int		 ip6_dst_gc(void);
 
 static int		ip6_pkt_discard(struct sk_buff *skb);
+static int		ip6_pkt_discard_out(struct sk_buff **pskb);
 static void		ip6_link_failure(struct sk_buff *skb);
 static void		ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
 
@@ -111,7 +116,7 @@
 			.error		= -ENETUNREACH,
 			.metrics	= { [RTAX_HOPLIMIT - 1] = 255, },
 			.input		= ip6_pkt_discard,
-			.output		= ip6_pkt_discard,
+			.output		= ip6_pkt_discard_out,
 			.ops		= &ip6_dst_ops,
 			.path		= (struct dst_entry*)&ip6_null_entry,
 		}
@@ -554,37 +575,43 @@
 
 	if (mtu < dst_pmtu(dst) && rt6->rt6i_dst.plen == 128) {
 		rt6->rt6i_flags |= RTF_MODIFIED;
+		if (mtu < IPV6_MIN_MTU)
+			mtu = IPV6_MIN_MTU;
 		dst->metrics[RTAX_MTU-1] = mtu;
 	}
 }
 
 /* Protected by rt6_lock.  */
 static struct dst_entry *ndisc_dst_gc_list;
+static int ipv6_get_mtu(struct net_device *dev);
+static inline unsigned int ipv6_advmss(unsigned int mtu);
 
 struct dst_entry *ndisc_dst_alloc(struct net_device *dev, 
 				  struct neighbour *neigh,
 				  struct in6_addr *addr,
-				  int (*output)(struct sk_buff *))
+				  int (*output)(struct sk_buff **))
 {
 	struct rt6_info *rt = ip6_dst_alloc();
 
 	if (unlikely(rt == NULL))
 		goto out;
 
-	if (dev)
-		dev_hold(dev);
+	dev_hold(dev);
 	if (neigh)
 		neigh_hold(neigh);
 	else
 		neigh = ndisc_get_neigh(dev, addr);
 
 	rt->rt6i_dev	  = dev;
+	
 	rt->rt6i_nexthop  = neigh;
 	rt->rt6i_expires  = 0;
 	rt->rt6i_flags    = RTF_LOCAL;
 	rt->rt6i_metric   = 0;
 	atomic_set(&rt->u.dst.__refcnt, 1);
 	rt->u.dst.metrics[RTAX_HOPLIMIT-1] = 255;
+	
+	
 	rt->u.dst.output  = output;
 
 	write_lock_bh(&rt6_lock);
@@ -767,7 +793,7 @@
 			dev_put(dev);
 		dev = &loopback_dev;
 		dev_hold(dev);
-		rt->u.dst.output = ip6_pkt_discard;
+		rt->u.dst.output = ip6_pkt_discard_out;
 		rt->u.dst.input = ip6_pkt_discard;
 		rt->u.dst.error = -ENETUNREACH;
 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
@@ -1257,12 +1287,17 @@
 
 int ip6_pkt_discard(struct sk_buff *skb)
 {
-	IP6_INC_STATS(Ip6OutNoRoutes);
+	IP6_INC_STATS(Ip6OutNoRoutes);
 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_NOROUTE, 0, skb->dev);
 	kfree_skb(skb);
 	return 0;
 }
 
+int ip6_pkt_discard_out(struct sk_buff **pskb)
+{
+	return ip6_pkt_discard(*pskb);
+}
+
 /*
  *	Add address
  */
diff -Nru a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
--- a/net/ipv6/netfilter/ip6_tables.c	Tue Jun 15 22:20:58 2004
+++ b/net/ipv6/netfilter/ip6_tables.c	Tue Jun 15 22:20:58 2004
@@ -66,8 +66,6 @@
 #endif
 #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
 
-/* Mutex protects lists (only traversed in user context). */
-static DECLARE_MUTEX(ip6t_mutex);
 
 /* Must have mutex */
 #define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
@@ -544,7 +542,7 @@
 #endif
 
 static inline struct ip6t_table *
-find_table_lock(const char *name, int *error, struct semaphore *mutex)
+ip6t_find_table_lock(const char *name, int *error, struct semaphore *mutex)
 {
 	return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex);
 }
@@ -555,8 +553,8 @@
 	return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex);
 }
 
-static inline struct ip6t_target *
-find_target_lock(const char *name, int *error, struct semaphore *mutex)
+struct ip6t_target *
+ip6t_find_target_lock(const char *name, int *error, struct semaphore *mutex)
 {
 	return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex);
 }
@@ -771,7 +769,7 @@
 		goto cleanup_matches;
 
 	t = ip6t_get_target(e);
-	target = find_target_lock(t->u.user.name, &ret, &ip6t_mutex);
+	target = ip6t_find_target_lock(t->u.user.name, &ret, &ip6t_mutex);
 	if (!target) {
 		duprintf("check_entry: `%s' not found\n", t->u.user.name);
 		goto cleanup_matches;
@@ -1028,7 +1026,7 @@
 static int
 copy_entries_to_user(unsigned int total_size,
 		     struct ip6t_table *table,
-		     void *userptr)
+		     void __user *userptr)
 {
 	unsigned int off, num, countersize;
 	struct ip6t_entry *e;
@@ -1106,12 +1104,12 @@
 
 static int
 get_entries(const struct ip6t_get_entries *entries,
-	    struct ip6t_get_entries *uptr)
+	    struct ip6t_get_entries __user *uptr)
 {
 	int ret;
 	struct ip6t_table *t;
 
-	t = find_table_lock(entries->name, &ret, &ip6t_mutex);
+	t = ip6t_find_table_lock(entries->name, &ret, &ip6t_mutex);
 	if (t) {
 		duprintf("t->private->number = %u\n",
 			 t->private->number);
@@ -1133,7 +1131,7 @@
 }
 
 static int
-do_replace(void *user, unsigned int len)
+do_replace(void __user *user, unsigned int len)
 {
 	int ret;
 	struct ip6t_replace tmp;
@@ -1174,7 +1172,7 @@
 
 	duprintf("ip_tables: Translated table\n");
 
-	t = find_table_lock(tmp.name, &ret, &ip6t_mutex);
+	t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
 	if (!t)
 		goto free_newinfo_counters_untrans;
 
@@ -1254,7 +1252,7 @@
 }
 
 static int
-do_add_counters(void *user, unsigned int len)
+do_add_counters(void __user *user, unsigned int len)
 {
 	unsigned int i;
 	struct ip6t_counters_info tmp, *paddc;
@@ -1276,7 +1274,7 @@
 		goto free;
 	}
 
-	t = find_table_lock(tmp.name, &ret, &ip6t_mutex);
+	t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
 	if (!t)
 		goto free;
 
@@ -1302,7 +1300,7 @@
 }
 
 static int
-do_ip6t_set_ctl(struct sock *sk,	int cmd, void *user, unsigned int len)
+do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
 {
 	int ret;
 
@@ -1327,7 +1325,7 @@
 }
 
 static int
-do_ip6t_get_ctl(struct sock *sk, int cmd, void *user, int *len)
+do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
 {
 	int ret;
 
@@ -1351,7 +1349,7 @@
 			break;
 		}
 		name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
-		t = find_table_lock(name, &ret, &ip6t_mutex);
+		t = ip6t_find_table_lock(name, &ret, &ip6t_mutex);
 		if (t) {
 			struct ip6t_getinfo info;
 
@@ -1964,6 +1962,7 @@
 EXPORT_SYMBOL(ip6t_register_table);
 EXPORT_SYMBOL(ip6t_unregister_table);
 EXPORT_SYMBOL(ip6t_do_table);
+EXPORT_SYMBOL(ip6t_find_target_lock);
 EXPORT_SYMBOL(ip6t_register_match);
 EXPORT_SYMBOL(ip6t_unregister_match);
 EXPORT_SYMBOL(ip6t_register_target);
