IPIP modifications to conform to RFC2003 (a bit)

David Woodhouse (D.W.Woodhouse@nortel.co.uk)
Wed, 20 Aug 1997 15:35:57 +0100


This is a multipart MIME message.

--===_0_Wed_Aug_20_15:23:30_BST_1997
Content-Type: text/plain; charset=us-ascii

Here's a patch to 2.1.51 with the following modifications to the IPIP tunnel
drivers:

1. TTL is no longer copied from the inner packet to the host packet (RFC2003)
(This may be overridden if a module with the option 'copyttl=1')
2. Source address set to the local address of the interface through which the
IPIP packet is sent out. (RFC2003)
3. A vague attempt at ICMP handling is done. Source quenches are forwarded to
machines trying to send packets through the tunnel (RFC 2003 says "SHOULD
NOT", but it works, and I can't see a better way to do it), and Dest
Unreachables are translated and forwarded, too. Path MTU is not yet done.
4. IPIP decapsulation now keeps stats for the number of packets received by
each tunnel device.
5. Decapsulated packets now have skb->dev set to the tunnel device from which
they should have come. I think this fixes the firewalling input rules.
6. ip_forward.c modified slightly to implement checks to prevent IPIP looping.
I had a slightly more paranoid version of this originally, but backed down to
this to simplify it and improve performance. IPIP loops that slip through this
are vaguely possible, but hardly likely.
7. As an experiment, what I call IPIP transparent routing has been
implemented. This notes when IPIP packets for decapsulation are due to be
immediately re-encapsulated and sent out again, and simply rewrites the
destination address before forwarding the packet, instead of stripping and
repackaging it. This means that machines can act as IPIP routers and route
IPIP packets which are sent to them without having to have a valid IP address
on the client network. The operation skb->h.ipiph.ttl++; would have much the
same effect, but not avoid so much system overhead.

As mentioned in (6), IPIP loops are just about possible, but fairly unlikely.
When trying to set up a loop to test the code, I had great problems
understanding how it could _accidentally_ get done that way.

The IPIP code, as it stands with these modifications, is now capable of
transporting a VPN over a totally separate host network with total
transparency to the client VPN. Likewise, Internet traffic can be routed over
a private backbone network while maintaining the illusion of a direct route
solely through publically addressable routers.

I believe that this functionality is worth the slight risk that it can be
misconfigured to cause IPIP loops which fall through the sanity checks
provided.

IPIP Looping:

The simplest form of IPIP loop is when packets for the tunnel endpoint are
routed back through the device. The tunnel driver has always checked for this.

Next, it is possible that outgoing packets are put into another IPIP tunnel
by an intervening machine and sent back to the original tunneller, like so:
1. Router X has packet for machine A, puts it into IPIP wrapper and sends it
out to machine B, via router Y.
2. Router Y sees said packet for machine B, puts it into IPIP wrapper and
sends it to machine A, via router X.
1. Router X sees a slightly larger packet for machine A...
This is fairly unlikely, but will be caught by one of the checks in
ip_forward(), which makes sure the source address of the encapsulated packet
is not a local address before allowing it to be forwarded out through the
tunnel device.

There are checks against more unlikely routing loops, too, but I get a bit
confused when I try to get my head round the configurations that would cause
them. It's possible that someone could manage to get a loop going, but I
reckon they'd send themselves insane before managing to bring the routers down.

(Cue someone telling me how to do it easily - Sod's Law :)

Well, I've talked about it for long enough - here's the patch...

--===_0_Wed_Aug_20_15:23:30_BST_1997
Content-Type: application/octet-stream
Content-Description: tunnel-diff-clean

diff -u --recursive --new-file linux-2.1.51/drivers/net/tunnel.c linux/drivers/net/tunnel.c
--- linux-2.1.51/drivers/net/tunnel.c Thu Apr 24 03:01:20 1997
+++ linux/drivers/net/tunnel.c Wed Aug 20 13:42:50 1997
@@ -63,26 +63,11 @@
#include <linux/socket.h>
#include <linux/in.h>
#include <net/ip.h>
+#include <net/icmp.h>
#include <linux/if_arp.h>
+#include <linux/if_tunnel.h>
#include <linux/init.h>

-/*#define TUNNEL_DEBUG*/
-
-/*
- * Our header is a simple IP packet with no options
- */
-
-#define tunnel_hlen sizeof(struct iphdr)
-
-/*
- * Okay, this needs to be high enough that we can fit a "standard"
- * ethernet header and an IP tunnel header into the outgoing packet.
- * [36 bytes]
- */
-
-#define TUNL_HLEN (((ETH_HLEN+15)&~15)+tunnel_hlen)
-
-
static int tunnel_open(struct device *dev)
{
MOD_INC_USE_COUNT;
@@ -114,6 +99,12 @@
}
#endif

+static int ttlcopy=0;
+
+#ifdef MODULE
+MODULE_PARM(ttlcopy,"i");
+#endif
+
/*
* This function assumes it is being called from dev_queue_xmit()
* and that skb is filled properly by that function.
@@ -121,24 +112,69 @@

static int tunnel_xmit(struct sk_buff *skb, struct device *dev)
{
- struct net_device_stats *stats; /* This device's statistics */
+ struct tunlpriv *priv; /* This device's statistics */
struct rtable *rt; /* Route to the other host */
struct device *tdev; /* Device to other host */
struct iphdr *iph; /* Our new IP header */
int max_headroom; /* The extra header space needed */

- stats = (struct net_device_stats *)dev->priv;
+ priv = (struct tunlpriv *)dev->priv;
+ iph = skb->nh.iph;

+ if (atomic_read(&priv->quench))
+ {
+ /* We've been told to shut up by a host inside the tunnel.
+ * We don't really care who we tell to shut up - we'll
+ * just pass the message to the next host to use the tunnel.
+ * According to RFC2003, we "SHOULD NOT" do this, but...
+ * Alternative congestion control methods, anyone?
+ */
+
+ atomic_dec(&priv->quench);
+#ifdef TUNNEL_DEBUG
+ printk ("ICMP Source Quench sent to %d.%d.%d.%d\n",MKDIGITS(ntohl(iph->saddr)));
+#endif
+ icmp_send(skb, ICMP_SOURCE_QUENCH, 0, 0);
+ }
+
+
+ if (priv->lastlost)
+ {
+
+ /* We've received an ICMP message from within the tunnel
+ * telling us that a packet was lost.
+ */
+
+ if ((priv->lastlost < priv->firstgood &&
+ priv->firstgood + 200 < jiffies))
+ {
+ /* The last transmitted packet wasn't reported lost within
+ * two seconds. We think the interface is up now.
+ */
+
+ priv->lastlost = 0;
+ }
+ else
+ {
+ /* We think the tunnel is still down. */
+
+#ifdef TUNNEL_DEBUG
+ printk ("ICMP Net Unreachable sent to %d.%d.%d.%d\n", MKDIGITS(ntohl(iph->saddr)));
+#endif
+ icmp_send(skb,ICMP_DEST_UNREACH,ICMP_NET_UNREACH, 0);
+ }
+ }
+
+
/*
* First things first. Look up the destination address in the
* routing tables
*/
- iph = skb->nh.iph;

- if (ip_route_output(&rt, dev->pa_dstaddr, dev->pa_addr, RT_TOS(iph->tos), NULL)) {
+ if (ip_route_output(&rt, dev->pa_dstaddr, 0, RT_TOS(iph->tos), NULL)) {
/* No route to host */
printk ( KERN_INFO "%s: Can't reach target gateway!\n", dev->name);
- stats->tx_errors++;
+ priv->stats.tx_errors++;
dev_kfree_skb(skb, FREE_WRITE);
return 0;
}
@@ -148,7 +184,7 @@
/* Tunnel to tunnel? -- I don't think so. */
printk ( KERN_INFO "%s: Packet targetted at myself!\n" , dev->name);
ip_rt_put(rt);
- stats->tx_errors++;
+ priv->stats.tx_errors++;
dev_kfree_skb(skb, FREE_WRITE);
return 0;
}
@@ -164,7 +200,7 @@
struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
if (!new_skb) {
ip_rt_put(rt);
- stats->tx_dropped++;
+ priv->stats.tx_dropped++;
dev_kfree_skb(skb, FREE_WRITE);
return 0;
}
@@ -172,6 +208,15 @@
skb = new_skb;
}

+
+ /* Record statistics */
+
+ priv->stats.tx_bytes+=skb->len;
+ priv->stats.tx_packets++;
+ dev->trans_start = jiffies;
+ if (!priv->firstgood) priv->firstgood = dev->trans_start;
+
+
skb->nh.iph = (struct iphdr *) skb_push(skb, tunnel_hlen);
dst_release(skb->dst);
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
@@ -185,28 +230,25 @@
iph = skb->nh.iph;
iph->version = 4;
iph->tos = skb->h.ipiph->tos;
- iph->ttl = skb->h.ipiph->ttl;
+ iph->ttl = priv->ttl?priv->ttl:skb->h.ipiph->ttl;
iph->frag_off = 0;
iph->daddr = dev->pa_dstaddr;
- iph->saddr = dev->pa_addr;
+ iph->saddr = tdev->pa_addr;
iph->protocol = IPPROTO_IPIP;
iph->ihl = 5;
iph->tot_len = htons(skb->len);
iph->id = htons(ip_id_count++); /* Race condition here? */
ip_send_check(iph);

- stats->tx_bytes+=skb->len;
-
- ip_send(skb);
+ /* Send packet and return */

- /* Record statistics and return */
- stats->tx_packets++;
+ ip_send(skb);
return 0;
}

static struct net_device_stats *tunnel_get_stats(struct device *dev)
{
- return((struct net_device_stats*) dev->priv);
+ return &((struct tunlpriv *)dev->priv)->stats;
}

/*
@@ -220,7 +262,7 @@
static int tun_msg=0;
if(!tun_msg)
{
- printk ( KERN_INFO "tunnel: version v0.2b2\n" );
+ printk ( KERN_INFO "tunnel: version v0.2b3\n" );
tun_msg=1;
}

@@ -229,10 +271,12 @@
dev->stop = tunnel_close;
dev->hard_start_xmit = tunnel_xmit;
dev->get_stats = tunnel_get_stats;
- dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
+ dev->priv = kmalloc(sizeof(struct tunlpriv), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
- memset(dev->priv, 0, sizeof(struct net_device_stats));
+ memset(dev->priv, 0, sizeof(struct tunlpriv));
+ ((struct tunlpriv *)dev->priv)->magic = TUNNEL_MAGIC;
+ ((struct tunlpriv *)dev->priv)->ttl = ttlcopy?0:ip_statistics.IpDefaultTTL;

/* Initialize the tunnel device structure */

@@ -298,7 +342,7 @@
void cleanup_module(void)
{
unregister_netdev(&dev_tunnel);
- kfree_s(dev_tunnel.priv,sizeof(struct net_device_stats));
+ kfree_s(dev_tunnel.priv,sizeof(struct tunlpriv));
dev_tunnel.priv=NULL;
}
#endif /* MODULE */
diff -u --recursive --new-file linux-2.1.51/include/linux/if_tunnel.h linux/include/linux/if_tunnel.h
--- linux-2.1.51/include/linux/if_tunnel.h Thu Jan 1 01:00:00 1970
+++ linux/include/linux/if_tunnel.h Wed Aug 20 13:11:11 1997
@@ -0,0 +1,59 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the Ethernet IEEE 802.3 interface.
+ *
+ * Version: @(#)if_tunnel.h 1.0 18/08/97
+ *
+ * Author: David Woodhouse <dwmw2@cam.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IF_TUNNEL_H
+#define _LINUX_IF_TUNNEL_H
+
+#undef TUNNEL_DEBUG
+
+#define TUNNEL_MAGIC 0x3d6bf2e4
+
+struct tunlpriv {
+ int magic;
+ struct net_device_stats stats;
+ atomic_t quench;
+ u_long lastlost;
+ u_long firstgood;
+ u_char ttl;
+ u_short mtu;
+};
+
+/* Useful macro for printing IP addresses */
+
+#define MKDIGITS(x) (int)(x >> 24), \
+ (int)(x >> 16 & 0xff), \
+ (int)(x >> 8 & 0xff), \
+ (int)(x & 0xff)
+
+/*
+ * Our header is a simple IP packet with no options
+ */
+
+#define tunnel_hlen sizeof(struct iphdr)
+
+
+/*
+ * Okay, this needs to be high enough that we can fit a "standard"
+ * ethernet header and an IP tunnel header into the outgoing packet.
+ * [36 bytes]
+ */
+
+#define TUNL_HLEN (((ETH_HLEN+15)&~15)+tunnel_hlen)
+
+
+
+#endif /* _LINUX_IF_TUNNEL_H */
diff -u --recursive --new-file linux-2.1.51/net/ipv4/Config.in linux/net/ipv4/Config.in
--- linux-2.1.51/net/ipv4/Config.in Thu Jun 26 20:33:41 1997
+++ linux/net/ipv4/Config.in Wed Aug 20 10:44:46 1997
@@ -19,7 +19,10 @@
fi
bool 'IP: accounting' CONFIG_IP_ACCT
bool 'IP: optimize as router not host' CONFIG_IP_ROUTER
-tristate 'IP: tunneling' CONFIG_NET_IPIP
+tristate 'IP: tunnelling' CONFIG_NET_IPIP
+if [ "$CONFIG_NET_IPIP" != "n" ]; then
+ bool 'IPIP: Transparently route IPIP packets' CONFIG_IPIP_ROUTING
+fi
if [ "$CONFIG_IP_MULTICAST" = "y" ]; then
bool 'IP: multicast routing' CONFIG_IP_MROUTE
fi
diff -u --recursive --new-file linux-2.1.51/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- linux-2.1.51/net/ipv4/ip_forward.c Fri Apr 4 17:52:28 1997
+++ linux/net/ipv4/ip_forward.c Wed Aug 20 15:06:49 1997
@@ -35,6 +35,7 @@
#include <linux/udp.h>
#include <linux/firewall.h>
#include <linux/ip_fw.h>
+#include <linux/if_arp.h>
#ifdef CONFIG_IP_MASQUERADE
#include <net/ip_masq.h>
#endif
@@ -236,8 +237,40 @@
}
#endif

+ if (dev2->type == ARPHRD_TUNNEL)
+ {
+ /* It's going out through an IPIP tunnel.
+ * Check it's not looping, because nasty things
+ * happen when IPIP loops.
+ * I had a more comprehensive check here, but replaced
+ * it with the check specified in RFC2003.
+ */
+
+ struct iphdr *iph=skb->nh.iph;
+
+ if (iph->protocol == IPPROTO_IPIP)
+ {
+ if (iph->saddr == dev2->pa_dstaddr ||
+ ip_fib_chk_addr(iph->saddr) | RTF_LOCAL ||
+ ip_fib_chk_addr(skb->h.ipiph->saddr) | RTF_LOCAL)
+ {
+#ifndef TUNNEL_DEBUG
+ if (net_ratelimit())
+#endif
+ {
+ printk(KERN_WARNING
+ "Possible IPIP loop detected, packet dropped.\n");
+ }
+
+ kfree_skb(skb,FREE_WRITE);
+ return -1;
+ }
+ }
+ }
+
+
ip_statistics.IpForwDatagrams++;
-
+
if (opt->optlen)
ip_forward_options(skb);

diff -u --recursive --new-file linux-2.1.51/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- linux-2.1.51/net/ipv4/ipip.c Sun Aug 3 22:04:43 1997
+++ linux/net/ipv4/ipip.c Wed Aug 20 13:11:56 1997
@@ -5,12 +5,15 @@
* Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95
*
* Fixes:
- * Alan Cox : Merged and made usable non modular (its so tiny its silly as
- * a module taking up 2 pages).
- * Alan Cox : Fixed bug with 1.3.18 and IPIP not working (now needs to set skb->h.iph)
- * to keep ip_forward happy.
- * Alan Cox : More fixes for 1.3.21, and firewall fix. Maybe this will work soon 8).
+ * Alan Cox : Merged and made usable non modular (its so tiny
+ * its silly as a module taking up 2 pages).
+ * Alan Cox : Fixed bug with 1.3.18 and IPIP not working (now
+ * needs to set skb->h.iph to keep ip_forward happy).
+ * Alan Cox : More fixes for 1.3.21, and firewall fix.
+ * Maybe this will work soon 8).
* Kai Schulte : Fixed #defines for IP_FIREWALL->FIREWALL
+ * David Woodhouse : Perform some basic ICMP handling.
+ * IPIP Routing without decapsulation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -30,6 +33,7 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
+#include <linux/if_tunnel.h>
#include <linux/mroute.h>

#include <net/datalink.h>
@@ -39,12 +43,66 @@
#include <net/protocol.h>
#include <net/ipip.h>

+
void ipip_err(struct sk_buff *skb, unsigned char *dp)
{
- /* NI */
- return;
+ /* We are the Knights Who Say NI */
+
+ struct iphdr *iph = (struct iphdr *)dp;
+ struct iphdr *ipiph = (struct iphdr *)(dp+(iph->ihl << 2));
+ int type = skb->h.icmph->type;
+ /* int code = skb->h.icmph->code; */
+ struct rtable *rt;
+ struct tunlpriv *priv;
+ struct device *dev=NULL;
+
+ if ((void *)&ipiph->daddr > (void *)skb->tail)
+ {
+ /* We can look up the offending tunl device by its dest. endpoint */
+
+ for (dev = dev_base; dev; dev=dev->next)
+ {
+ if (dev->type == ARPHRD_TUNNEL && dev->pa_dstaddr == iph->daddr)
+ break;
+ }
+ }
+ else if (!ip_route_output(&rt, ipiph->daddr, 0, RT_TOS(ipiph->tos), NULL))
+ {
+ dev = rt->u.dst.dev;
+ ip_rt_put(rt);
+ }
+
+ if (!dev)
+ return;
+
+ priv=(struct tunlpriv *)dev->priv;
+
+ if (dev->type != ARPHRD_TUNNEL || priv->magic != TUNNEL_MAGIC)
+ {
+#ifndef TUNNEL_DEBUG
+ if (net_ratelimit())
+#endif
+ printk ("IPIP: ICMP packet received for non-tunnel %s\n",dev->name);
+ return;
+ }
+
+ switch (type)
+ {
+ case ICMP_SOURCE_QUENCH:
+ atomic_inc(&priv->quench);
+ break;
+
+ case ICMP_TIME_EXCEEDED:
+ case ICMP_PROT_UNREACH:
+ case ICMP_DEST_UNREACH:
+ priv->lastlost = jiffies;
+ priv->firstgood = 0;
+ priv->stats.tx_carrier_errors++;
+ break;
+ }
}

+
/*
* The IPIP protocol driver.
*
@@ -58,16 +116,18 @@
{
struct device *dev;
struct iphdr *iph;
-
+ struct rtable *rt;
+ int delta;
+
#ifdef TUNNEL_DEBUG
printk("ipip_rcv: got a packet!\n");
#endif
+
/*
* Discard the original IP header
*/

- skb->mac.raw = skb->data;
- skb_pull(skb, skb->h.raw - skb->nh.raw);
+ skb_pull(skb, (delta = skb->h.raw - skb->nh.raw));

/*
* Adjust pointers
@@ -75,40 +135,133 @@

iph = skb->nh.iph;
skb->nh.iph = skb->h.ipiph;
- memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));

/*
- * If you want to add LZ compressed IP or things like that here,
- * and in drivers/net/tunnel.c are the places to add.
+ * Is it draconic? I do not think so. --ANK
*/

+ if (!ip_route_output(&rt, skb->nh.iph->saddr, iph->daddr,
+ RT_TOS(iph->tos), NULL))
+ {
+ dev = rt->u.dst.dev;
+ ip_rt_put(rt);
+ }
+ else
+ {
+ /* No route to the tunnelling host */
+#ifndef TUNNEL_DEBUG
+ if (net_ratelimit())
+#endif
+ {
+ printk(KERN_INFO "IPIP packet received with payload from "
+ "unreachable host %d.%d.%d.%d\n",
+ MKDIGITS(ntohl(skb->nh.iph->saddr)));
+ }
+ dev = NULL;
+ }
+
+ if (dev == NULL)
+ {
+ kfree_skb(skb, FREE_READ);
+ return -EINVAL;
+ }
+
+ if (dev->type == ARPHRD_TUNNEL)
+ {
+ struct tunlpriv *priv=(struct tunlpriv *)dev->priv;
+
+ /* Increase the no. of packets received on the interface */
+ if (priv->magic == TUNNEL_MAGIC)
+ {
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += skb->len;
+ dev->last_rx = jiffies;
+ }
+#ifdef CONFIG_IPIP_ROUTING
+ if (IS_ROUTER)
+ {
+ /* If it's going back out through a tunnel device, we
+ * don't bother to unpackage it. Just send it on.
+ *
+ * This allows for IPIP routing through a completely
+ * private host network, without either the end-points having
+ * stupidly large routing tables and numbers of tunnel devices
+ * or un-encapsulating and re-encapsulating at every hop, which
+ * wastes public IP addresses and gives out details of the
+ * private network structure.
+ * It may, however, confuse the tunnel devices at each end,
+ * because their packets are getting redirected in transit.
+ * Most people won't want to do this.
+ */
+
+ if (!ip_route_output(&rt, skb->nh.iph->daddr, iph->daddr,
+ RT_TOS(iph->tos), NULL))
+ {
+ /* We know where to send packets for this host. */
+
+ if (rt->u.dst.dev->type == ARPHRD_TUNNEL &&
+ rt->u.dst.dev->pa_dstaddr)
+ {
+ /* It's going straight back into another tunnel.
+ * Don't bother with extracting it and repackaging.
+ */
+ __u32 newdaddr = rt->u.dst.dev->pa_dstaddr;
+
+ ip_rt_put(rt);
+ if (!ip_route_output(&rt, rt->u.dst.dev->pa_dstaddr,
+ iph->daddr,
+ RT_TOS(iph->tos), NULL))
+ {
+ /* We even know where the endpoint of the tunnel is */
+ dev = rt->u.dst.dev;
+#ifdef TUNNEL_DEBUG
+ printk("Sending on IPIP routed packet through dev %s\n"
+ , dev->name);
+#endif
+ skb->nh.iph=iph;
+ skb_push(skb, delta);
+
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ ip_rt_put(rt);
+
+ iph->daddr=newdaddr;
+
+ ip_send_check(iph);
+ netif_rx(skb);
+
+ return(0);
+ }
+ }
+ ip_rt_put(rt);
+ }
+ }
+#endif
+ }
+ else
+ {
+#ifndef TUNNEL_DEBUG
+ if (net_ratelimit())
+#endif
+ {
+ printk(KERN_INFO "IPIP packet received from non-tunnel "
+ "host %d.%d.%d.%d\n",
+ MKDIGITS(ntohl(skb->nh.iph->saddr)));
+ }
+ kfree_skb(skb, FREE_READ);
+ return -EINVAL;
+ }
+
+ /* Finally - the actual decapsulation... */
+
+ skb->mac.raw = skb->data;
+
+ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+
skb->protocol = htons(ETH_P_IP);
skb->ip_summed = 0;
skb->pkt_type = PACKET_HOST;
-
- /*
- * Is it draconic? I do not think so. --ANK
- */
- dev = ip_dev_find_tunnel(iph->daddr, iph->saddr);
- if (dev == NULL) {
-#ifdef CONFIG_IP_MROUTE
- int vif;
-
- if (!MULTICAST(skb->nh.iph->daddr) ||
- !ipv4_config.multicast_route ||
- LOCAL_MCAST(skb->nh.iph->daddr) ||
- (vif=ip_mr_find_tunnel(iph->daddr, iph->saddr)) < 0)
- {
-#endif
- kfree_skb(skb, FREE_READ);
- return -EINVAL;
-#ifdef CONFIG_IP_MROUTE
- }
- IPCB(skb)->flags |= IPSKB_TUNNELED;
- IPCB(skb)->vif = vif;
- dev = skb->dev;
-#endif
- }
+
skb->dev = dev;
dst_release(skb->dst);
skb->dst = NULL;
@@ -146,3 +299,12 @@
}

#endif
+
+
+
+
+
+
+
+
+
diff -u --recursive --new-file linux-2.1.51/net/netsyms.c linux/net/netsyms.c
--- linux-2.1.51/net/netsyms.c Tue Jun 17 00:36:02 1997
+++ linux/net/netsyms.c Wed Aug 20 10:44:46 1997
@@ -150,6 +150,8 @@

#ifdef CONFIG_INET
/* Internet layer registration */
+EXPORT_SYMBOL(net_ratelimit);
+EXPORT_SYMBOL(ip_statistics);
EXPORT_SYMBOL(inet_add_protocol);
EXPORT_SYMBOL(inet_del_protocol);
EXPORT_SYMBOL(rarp_ioctl_hook);

--===_0_Wed_Aug_20_15:23:30_BST_1997
Content-Type: text/plain; charset=us-ascii

David Woodhouse, CB3 9AN http://dwmw2.robinson.cam.ac.uk/
dwmw2@cam.ac.uk Tel: 0976 658355
D.W.Woodhouse@nortel.co.uk Tel: 01279 402332

--===_0_Wed_Aug_20_15:23:30_BST_1997--