summaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/Makefile14
-rw-r--r--net/ipv6/addrconf.c1293
-rw-r--r--net/ipv6/af_inet6.c757
-rw-r--r--net/ipv6/datagram.c138
-rw-r--r--net/ipv6/exthdrs.c39
-rw-r--r--net/ipv6/icmp.c233
-rw-r--r--net/ipv6/ip6_fib.c927
-rw-r--r--net/ipv6/ip6_fw.c378
-rw-r--r--net/ipv6/ip6_input.c408
-rw-r--r--net/ipv6/ip6_output.c629
-rw-r--r--net/ipv6/ipv6_input.c437
-rw-r--r--net/ipv6/ipv6_output.c1003
-rw-r--r--net/ipv6/ipv6_route.c2056
-rw-r--r--net/ipv6/ipv6_sockglue.c120
-rw-r--r--net/ipv6/mcast.c409
-rw-r--r--net/ipv6/ndisc.c1789
-rw-r--r--net/ipv6/proc.c143
-rw-r--r--net/ipv6/protocol.c43
-rw-r--r--net/ipv6/raw.c336
-rw-r--r--net/ipv6/reassembly.c85
-rw-r--r--net/ipv6/route.c1599
-rw-r--r--net/ipv6/sit.c255
-rw-r--r--net/ipv6/sysctl_net_ipv6.c96
-rw-r--r--net/ipv6/tcp_ipv6.c989
-rw-r--r--net/ipv6/udp.c494
25 files changed, 7578 insertions, 7092 deletions
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index e11c5eee7..15ef93ac6 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -8,12 +8,18 @@
O_TARGET := ipv6.o
-O_OBJS := af_inet6.o ipv6_output.o ipv6_input.o addrconf.o sit.o \
- ipv6_route.o ipv6_sockglue.o ndisc.o udp.o raw.o \
- protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
- exthdrs.o sysctl_net_ipv6.o datagram.o
+IPV6_OBJS := af_inet6.o ip6_output.o ip6_input.o addrconf.o sit.o \
+ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \
+ protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
+ exthdrs.o sysctl_net_ipv6.o datagram.o proc.o
MOD_LIST_NAME := IPV6_MODULES
M_OBJS := $(O_TARGET)
+#ifeq ($(CONFIG_IPV6_FIREWALL),y)
+# IPV6_OBJS += ip6_fw.o
+#endif
+
+O_OBJS := $(IPV6_OBJS)
+
include $(TOPDIR)/Rules.make
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 6b7ec0ad4..9173a7760 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5,12 +5,14 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
+ * $Id: addrconf.c,v 1.18 1997/04/16 05:58:03 davem Exp $
*
* 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.
*/
+
/*
* Changes:
*
@@ -18,6 +20,7 @@
* <chexum@bankinf.banki.hu>
*/
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -27,6 +30,7 @@
#include <linux/in6.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
+#include <linux/route.h>
#include <linux/proc_fs.h>
#include <net/sock.h>
@@ -35,30 +39,37 @@
#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/sit.h>
#include <asm/uaccess.h>
-#define HASH_SIZE 16
+/* Set to 3 to get tracing... */
+#define ACONF_DEBUG 2
+
+#if ACONF_DEBUG >= 3
+#define ADBG(x) printk x
+#else
+#define ADBG(x)
+#endif
+
/*
* Configured unicast address list
*/
-struct inet6_ifaddr *inet6_addr_lst[HASH_SIZE];
+struct inet6_ifaddr *inet6_addr_lst[IN6_ADDR_HSIZE];
/*
* Hash list of configured multicast addresses
*/
-struct ipv6_mc_list *inet6_mcast_lst[HASH_SIZE];
+struct ifmcaddr6 *inet6_mcast_lst[IN6_ADDR_HSIZE];
/*
* AF_INET6 device list
*/
-struct inet6_dev *inet6_dev_lst;
-int in6_ifnum = 0;
+struct inet6_dev *inet6_dev_lst[IN6_ADDR_HSIZE];
-atomic_t addr_list_lock = 0;
+static atomic_t addr_list_lock = ATOMIC_INIT(0);
void addrconf_verify(unsigned long);
@@ -67,15 +78,11 @@ static struct timer_list addr_chk_timer = {
0, 0, addrconf_verify
};
-
-int DupAddrDetectTransmits = 1;
-
-/*
- * /proc/sys switch for autoconf (enabled by default)
- */
-int addrconf_sys_autoconf = 1;
+static int addrconf_ifdown(struct device *dev);
static void addrconf_dad_start(struct inet6_ifaddr *ifp);
+static void addrconf_dad_timer(unsigned long data);
+static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
static void addrconf_rs_timer(unsigned long data);
int ipv6_addr_type(struct in6_addr *addr)
@@ -89,58 +96,41 @@ int ipv6_addr_type(struct in6_addr *addr)
* 0x4/3
*/
- if ((st & __constant_htonl(0xE0000000)) ==
- __constant_htonl(0x40000000))
- {
+ if ((st & __constant_htonl(0xE0000000)) == __constant_htonl(0x40000000))
return IPV6_ADDR_UNICAST;
- }
- if ((st & __constant_htonl(0xFF000000)) ==
- __constant_htonl(0xFF000000))
- {
+ if ((st & __constant_htonl(0xFF000000)) == __constant_htonl(0xFF000000)) {
int type = IPV6_ADDR_MULTICAST;
- switch((st >> 16) & 0x0f)
- {
- case 0x01:
+ switch((st & __constant_htonl(0x00FF0000))) {
+ case __constant_htonl(0x00010000):
type |= IPV6_ADDR_LOOPBACK;
break;
- case 0x02:
+
+ case __constant_htonl(0x00020000):
type |= IPV6_ADDR_LINKLOCAL;
break;
- case 0x05:
+
+ case __constant_htonl(0x00050000):
type |= IPV6_ADDR_SITELOCAL;
break;
- }
+ };
return type;
}
- if ((st & __constant_htonl(0xFFC00000)) ==
- __constant_htonl(0xFE800000))
- {
+ if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFE800000))
return (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST);
- }
- if ((st & __constant_htonl(0xFFC00000)) ==
- __constant_htonl(0xFEC00000))
- {
+ if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFEC00000))
return (IPV6_ADDR_SITELOCAL | IPV6_ADDR_UNICAST);
- }
- if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0)
- {
- if (addr->s6_addr32[2] == 0)
- {
+ if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) {
+ if (addr->s6_addr32[2] == 0) {
if (addr->in6_u.u6_addr32[3] == 0)
- {
return IPV6_ADDR_ANY;
- }
if (addr->s6_addr32[3] == __constant_htonl(0x00000001))
- {
- return (IPV6_ADDR_LOOPBACK |
- IPV6_ADDR_UNICAST);
- }
+ return (IPV6_ADDR_LOOPBACK | IPV6_ADDR_UNICAST);
return (IPV6_ADDR_COMPATv4 | IPV6_ADDR_UNICAST);
}
@@ -152,81 +142,64 @@ int ipv6_addr_type(struct in6_addr *addr)
return IPV6_ADDR_RESERVED;
}
-struct inet6_dev * ipv6_add_dev(struct device *dev)
+static struct inet6_dev * ipv6_add_dev(struct device *dev)
{
- struct inet6_dev *dev6;
-
- /*
- * called by netdev notifier from a syscall
- */
- dev6 = (struct inet6_dev *) kmalloc(sizeof(struct inet6_dev),
- GFP_ATOMIC);
+ struct inet6_dev *ndev, **bptr, *iter;
+ int hash;
- if (dev6 == NULL)
- return NULL;
+ ndev = kmalloc(sizeof(struct inet6_dev), gfp_any());
- memset(dev6, 0, sizeof(struct inet6_dev));
- dev6->dev = dev;
- dev6->if_index = ++in6_ifnum;
+ if (ndev) {
+ memset(ndev, 0, sizeof(struct inet6_dev));
- /*
- * insert at head.
- */
+ ndev->dev = dev;
+ hash = ipv6_devindex_hash(dev->ifindex);
+ bptr = &inet6_dev_lst[hash];
+ iter = *bptr;
- dev6->next = inet6_dev_lst;
- inet6_dev_lst = dev6;
+ for (; iter; iter = iter->next)
+ bptr = &iter->next;
- return dev6;
-}
-
-struct inet6_dev * ipv6_dev_by_index(int index)
-{
- struct inet6_dev *in6_dev;
-
- for (in6_dev = inet6_dev_lst; in6_dev; in6_dev = in6_dev->next)
- {
- if (in6_dev->if_index == index)
- return in6_dev;
+ *bptr = ndev;
}
-
- return NULL;
+ return ndev;
}
void addrconf_forwarding_on(void)
{
- struct inet6_dev *in6_dev;
- struct in6_addr maddr;
+ struct inet6_dev *idev;
+ int i;
- for (in6_dev = inet6_dev_lst; in6_dev; in6_dev = in6_dev->next)
- {
- printk(KERN_DEBUG "dev %s\n", in6_dev->dev->name);
+ for (i = 0; i < IN6_ADDR_HSIZE; i++) {
+ for (idev = inet6_dev_lst[i]; idev; idev = idev->next) {
+#if ACONF_DEBUG >= 2
+ printk(KERN_DEBUG "dev %s\n", idev->dev->name);
+#endif
- if (in6_dev->dev->type == ARPHRD_ETHER)
- {
- printk(KERN_DEBUG "joining all-routers\n");
- in6_dev->router = 1;
- ipv6_addr_all_routers(&maddr);
- ipv6_dev_mc_inc(in6_dev->dev, &maddr);
- }
- }
+ if (idev->dev->type == ARPHRD_ETHER) {
+ struct in6_addr maddr;
- if (last_resort_rt && (last_resort_rt->rt_flags & RTI_ALLONLINK))
- {
- rt_release(last_resort_rt);
- last_resort_rt = NULL;
+#if ACONF_DEBUG >= 2
+ printk(KERN_DEBUG "joining all-routers\n");
+#endif
+ idev->router = 1;
+ ipv6_addr_all_routers(&maddr);
+ ipv6_dev_mc_inc(idev->dev, &maddr);
+ }
+ }
}
}
struct inet6_dev * ipv6_get_idev(struct device *dev)
{
- struct inet6_dev *in6_dev;
+ struct inet6_dev *idev;
+ int hash;
- for (in6_dev = inet6_dev_lst; in6_dev; in6_dev = in6_dev->next)
- {
- if (in6_dev->dev == dev)
- {
- return in6_dev;
- }
+ hash = ipv6_devindex_hash(dev->ifindex);
+
+ for (idev = inet6_dev_lst[hash]; idev; idev = idev->next) {
+ if (idev->dev == dev)
+ return idev;
}
return NULL;
}
@@ -234,45 +207,34 @@ struct inet6_dev * ipv6_get_idev(struct device *dev)
struct inet6_ifaddr * ipv6_add_addr(struct inet6_dev *idev,
struct in6_addr *addr, int scope)
{
- struct inet6_ifaddr * ifaddr;
+ struct inet6_ifaddr *ifa;
int hash;
- unsigned long flags;
- save_flags(flags);
- cli();
+ ifa = kmalloc(sizeof(struct inet6_ifaddr), gfp_any());
- ifaddr = (struct inet6_ifaddr *) kmalloc(sizeof(struct inet6_ifaddr),
- GFP_ATOMIC);
-
- if (ifaddr == NULL)
- {
- printk(KERN_DEBUG "ipv6_add_addr: malloc failed\n");
- restore_flags(flags);
+ if (ifa == NULL) {
+ ADBG(("ipv6_add_addr: malloc failed\n"));
return NULL;
}
- memset(ifaddr, 0, sizeof(struct inet6_ifaddr));
- memcpy(&ifaddr->addr, addr, sizeof(struct in6_addr));
+ memset(ifa, 0, sizeof(struct inet6_ifaddr));
+ memcpy(&ifa->addr, addr, sizeof(struct in6_addr));
- ifaddr->scope = scope;
- ifaddr->idev = idev;
-
-
- /* add to list */
+ init_timer(&ifa->timer);
+ ifa->scope = scope;
+ ifa->idev = idev;
+ /* Add to list. */
hash = ipv6_addr_hash(addr);
- ifaddr->lst_next = inet6_addr_lst[hash];
- inet6_addr_lst[hash] = ifaddr;
-
+ ifa->lst_next = inet6_addr_lst[hash];
+ inet6_addr_lst[hash] = ifa;
- /* add to inet6_dev unicast addr list */
- ifaddr->if_next = idev->addr_list;
- idev->addr_list = ifaddr;
+ /* Add to inet6_dev unicast addr list. */
+ ifa->if_next = idev->addr_list;
+ idev->addr_list = ifa;
- restore_flags(flags);
- return ifaddr;
-
+ return ifa;
}
void ipv6_del_addr(struct inet6_ifaddr *ifp)
@@ -280,8 +242,7 @@ void ipv6_del_addr(struct inet6_ifaddr *ifp)
struct inet6_ifaddr *iter, **back;
int hash;
- if (addr_list_lock)
- {
+ if (atomic_read(&addr_list_lock)) {
ifp->flags |= ADDR_INVALID;
return;
}
@@ -291,10 +252,8 @@ void ipv6_del_addr(struct inet6_ifaddr *ifp)
iter = inet6_addr_lst[hash];
back = &inet6_addr_lst[hash];
- for (; iter; iter = iter->lst_next)
- {
- if (iter == ifp)
- {
+ for (; iter; iter = iter->lst_next) {
+ if (iter == ifp) {
*back = ifp->lst_next;
ifp->lst_next = NULL;
break;
@@ -305,10 +264,8 @@ void ipv6_del_addr(struct inet6_ifaddr *ifp)
iter = ifp->idev->addr_list;
back = &ifp->idev->addr_list;
- for (; iter; iter = iter->if_next)
- {
- if (iter == ifp)
- {
+ for (; iter; iter = iter->if_next) {
+ if (iter == ifp) {
*back = ifp->if_next;
ifp->if_next = NULL;
break;
@@ -327,30 +284,26 @@ void ipv6_del_addr(struct inet6_ifaddr *ifp)
* an address of the attached interface
* iii) don't use deprecated addresses
*
- * at the moment i believe only iii) is missing.
+ * at the moment I believe only iii) is missing.
*/
-struct inet6_ifaddr * ipv6_get_saddr(struct rt6_info *rt, struct in6_addr *daddr)
+struct inet6_ifaddr * ipv6_get_saddr(struct dst_entry *dst,
+ struct in6_addr *daddr)
{
int scope;
- struct inet6_ifaddr * ifp = NULL;
- struct inet6_dev * i6dev;
- struct inet6_ifaddr * match = NULL;
+ struct inet6_ifaddr *ifp = NULL;
+ struct inet6_ifaddr *match = NULL;
struct device *dev = NULL;
+ struct rt6_info *rt;
int i;
+ rt = (struct rt6_info *) dst;
if (rt)
- {
- dev = rt->rt_dev;
- }
+ dev = rt->rt6i_dev;
atomic_inc(&addr_list_lock);
- scope = ipv6_addr_type(daddr);
-
- scope &= IPV6_ADDR_SCOPE_MASK;
-
- if (rt && (rt->rt_flags & RTI_ALLONLINK))
- {
+ scope = ipv6_addr_scope(daddr);
+ if (rt && (rt->rt6i_flags & RTF_ALLONLINK)) {
/*
* route for the "all destinations on link" rule
* when no routers are present
@@ -363,30 +316,23 @@ struct inet6_ifaddr * ipv6_get_saddr(struct rt6_info *rt, struct in6_addr *daddr
* search dev and walk through dev addresses
*/
- if (dev)
- {
+ if (dev) {
+ struct inet6_dev *idev;
+ int hash;
+
if (dev->flags & IFF_LOOPBACK)
- {
scope = IFA_HOST;
- }
- for (i6dev = inet6_dev_lst; i6dev; i6dev=i6dev->next)
- {
- if (i6dev->dev == dev)
- {
- for (ifp=i6dev->addr_list; ifp;
- ifp=ifp->if_next)
- {
- if (ifp->scope == scope)
- {
+ hash = ipv6_devindex_hash(dev->ifindex);
+ for (idev = inet6_dev_lst[hash]; idev; idev=idev->next) {
+ if (idev->dev == dev) {
+ for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
+ if (ifp->scope == scope) {
if (!(ifp->flags & ADDR_STATUS))
- {
goto out;
- }
+
if (!(ifp->flags & ADDR_INVALID))
- {
match = ifp;
- }
}
}
break;
@@ -395,37 +341,27 @@ struct inet6_ifaddr * ipv6_get_saddr(struct rt6_info *rt, struct in6_addr *daddr
}
if (scope == IFA_LINK)
- {
goto out;
- }
/*
* dev == NULL or search failed for specified dev
*/
- for (i=0; i < HASH_SIZE; i++)
- {
- for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next)
- {
- if (ifp->scope == scope)
- {
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
+ if (ifp->scope == scope) {
if (!(ifp->flags & ADDR_STATUS))
- {
goto out;
- }
+
if (!(ifp->flags & ADDR_INVALID))
- {
match = ifp;
- }
}
}
}
- out:
+out:
if (ifp == NULL && match)
- {
ifp = match;
- }
atomic_dec(&addr_list_lock);
return ifp;
}
@@ -433,14 +369,14 @@ struct inet6_ifaddr * ipv6_get_saddr(struct rt6_info *rt, struct in6_addr *daddr
struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev)
{
struct inet6_ifaddr *ifp;
- struct inet6_dev *i6dev;
-
- for (i6dev = inet6_dev_lst; i6dev; i6dev=i6dev->next)
- {
- if (i6dev->dev == dev)
- {
- for (ifp=i6dev->addr_list; ifp; ifp=ifp->if_next)
- {
+ struct inet6_dev *idev;
+ int hash;
+
+ hash = ipv6_devindex_hash(dev->ifindex);
+
+ for (idev = inet6_dev_lst[hash]; idev; idev=idev->next) {
+ if (idev->dev == dev) {
+ for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
if (ifp->scope == IFA_LINK)
return ifp;
}
@@ -464,154 +400,15 @@ struct inet6_ifaddr * ipv6_chk_addr(struct in6_addr *addr)
atomic_inc(&addr_list_lock);
hash = ipv6_addr_hash(addr);
-
- for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next)
- {
+ for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
if (ipv6_addr_cmp(&ifp->addr, addr) == 0)
- {
break;
- }
}
atomic_dec(&addr_list_lock);
return ifp;
}
-static void sit_route_add(struct inet6_dev *idev)
-{
- struct in6_rtmsg rtmsg;
- struct device *dev = idev->dev;
- int err;
-
- rtmsg.rtmsg_type = RTMSG_NEWROUTE;
-
- memset(&rtmsg.rtmsg_dst, 0, sizeof(struct in6_addr));
- memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
-
- if (dev->pa_dstaddr == 0)
- {
- /* prefix length - 96 bytes "::d.d.d.d" */
- rtmsg.rtmsg_prefixlen = 96;
- rtmsg.rtmsg_metric = 1;
- rtmsg.rtmsg_flags = RTF_NEXTHOP|RTF_UP;
- }
- else
- {
- rtmsg.rtmsg_prefixlen = 10;
- rtmsg.rtmsg_dst.s6_addr32[0] = __constant_htonl(0xfe800000);
- rtmsg.rtmsg_dst.s6_addr32[3] = dev->pa_dstaddr;
- rtmsg.rtmsg_metric = 1;
- rtmsg.rtmsg_flags = RTF_NEXTHOP|RTF_UP;
- }
-
- rtmsg.rtmsg_ifindex = idev->if_index;
-
- err = ipv6_route_add(&rtmsg);
-
- if (err)
- {
- printk(KERN_DEBUG "sit_route_add: error in route_add\n");
- }
-}
-
-static void init_loopback(struct device *dev)
-{
- struct in6_addr addr;
- struct inet6_dev *idev;
- struct inet6_ifaddr * ifp;
- struct in6_rtmsg rtmsg;
- int err;
-
- /* ::1 */
-
- memset(&addr, 0, sizeof(struct in6_addr));
- addr.s6_addr[15] = 1;
-
- idev = ipv6_add_dev(dev);
-
- if (idev == NULL)
- {
- printk(KERN_DEBUG "init loopback: add_dev failed\n");
- return;
- }
-
- ifp = ipv6_add_addr(idev, &addr, IFA_HOST);
-
- if (ifp == NULL)
- {
- printk(KERN_DEBUG "init_loopback: add_addr failed\n");
- return;
- }
-
- ifp->flags |= ADDR_PERMANENT;
-
- memcpy(&rtmsg.rtmsg_dst, &addr, sizeof(struct in6_addr));
- memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
-
- rtmsg.rtmsg_prefixlen = 128;
- rtmsg.rtmsg_metric = 1;
- rtmsg.rtmsg_ifindex = idev->if_index;
-
- rtmsg.rtmsg_flags = RTF_NEXTHOP|RTF_HOST|RTF_UP;
-
- err = ipv6_route_add(&rtmsg);
-
- if (err)
- {
- printk(KERN_DEBUG "init_loopback: error in route_add\n");
- }
-
- /* add route for ::127.0.0.1 */
-}
-
-static void addrconf_eth_config(struct device *dev)
-{
- struct in6_addr addr;
- struct in6_addr maddr;
- struct inet6_ifaddr * ifp;
- struct inet6_dev * idev;
-
- memset(&addr, 0, sizeof(struct in6_addr));
-
- /* generate link local address*/
- addr.s6_addr[0] = 0xFE;
- addr.s6_addr[1] = 0x80;
-
- memcpy(addr.s6_addr + (sizeof(struct in6_addr) - dev->addr_len),
- dev->dev_addr, dev->addr_len);
-
- idev = ipv6_add_dev(dev);
-
- if (idev == NULL)
- return;
-
- ifp = ipv6_add_addr(idev, &addr, IFA_LINK);
-
- if (ifp == NULL)
- return;
-
- ifp->flags |= (DAD_INCOMPLETE | ADDR_PERMANENT);
- ifp->prefix_len = 10;
-
- /* join to all nodes multicast group */
- ipv6_addr_all_nodes(&maddr);
- ipv6_dev_mc_inc(dev, &maddr);
-
- if (ipv6_forwarding)
- {
- idev->router = 1;
- ipv6_addr_all_routers(&maddr);
- ipv6_dev_mc_inc(dev, &maddr);
- }
-
- /* join to solicited addr multicast group */
- addrconf_addr_solict_mult(&addr, &maddr);
- ipv6_dev_mc_inc(dev, &maddr);
-
- /* start dad */
- addrconf_dad_start(ifp);
-}
-
void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
{
struct prefix_info *pinfo;
@@ -623,9 +420,8 @@ void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
pinfo = (struct prefix_info *) opt;
- if (len < sizeof(struct prefix_info))
- {
- printk(KERN_DEBUG "addrconf: prefix option too short\n");
+ if (len < sizeof(struct prefix_info)) {
+ ADBG(("addrconf: prefix option too short\n"));
return;
}
@@ -636,17 +432,13 @@ void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
addr_type = ipv6_addr_type(&pinfo->prefix);
if (addr_type & IPV6_ADDR_LINKLOCAL)
- {
return;
- }
valid_lft = ntohl(pinfo->valid);
prefered_lft = ntohl(pinfo->prefered);
- if (prefered_lft > valid_lft)
- {
- printk(KERN_WARNING
- "addrconf: prefix option has invalid lifetime\n");
+ if (prefered_lft > valid_lft) {
+ printk(KERN_WARNING "addrconf: prefix option has invalid lifetime\n");
return;
}
@@ -655,11 +447,7 @@ void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
* delete it
*/
- if (last_resort_rt && (last_resort_rt->rt_flags & RTI_ALLONLINK))
- {
- rt_release(last_resort_rt);
- last_resort_rt = NULL;
- }
+ rt6_purge_dflt_routers(RTF_ALLONLINK);
/*
* Two things going on here:
@@ -669,178 +457,86 @@ void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
rt_expires = jiffies + valid_lft * HZ;
if (rt_expires < jiffies)
- {
rt_expires = ~0;
- }
- rt = fibv6_lookup(&pinfo->prefix, dev, RTI_DYNAMIC|RTI_GATEWAY);
-
- if (rt)
- {
- if (pinfo->onlink == 0 || valid_lft == 0)
- {
- /*
- * delete route
- */
- fib6_del_rt(rt);
+ rt = rt6_lookup(&pinfo->prefix, NULL, dev, RTF_LINKRT);
+
+ if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
+ if (pinfo->onlink == 0 || valid_lft == 0) {
+ ip6_del_rt(rt);
rt = NULL;
+ } else {
+ rt->rt6i_expires = rt_expires;
}
- else
- {
- rt->rt_expires = rt_expires;
- }
- }
- else if (pinfo->onlink && valid_lft)
- {
+ } else if (pinfo->onlink && valid_lft) {
struct in6_rtmsg rtmsg;
- struct inet6_dev *idev;
+ int err;
+
+ memset(&rtmsg, 0, sizeof(rtmsg));
printk(KERN_DEBUG "adding on link route\n");
- ipv6_addr_copy(&rtmsg.rtmsg_dst, &pinfo->prefix);
- memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
- rtmsg.rtmsg_prefixlen = pinfo->prefix_len;
- rtmsg.rtmsg_metric = 1;
-
- if ((idev = ipv6_get_idev(dev)))
- {
- rtmsg.rtmsg_ifindex = idev->if_index;
- }
- rtmsg.rtmsg_flags = RTF_UP | RTF_ADDRCONF;
- rtmsg.rtmsg_info = rt_expires;
+ ipv6_addr_copy(&rtmsg.rtmsg_dst, &pinfo->prefix);
+ rtmsg.rtmsg_dst_len = pinfo->prefix_len;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+ rtmsg.rtmsg_flags = RTF_UP | RTF_ADDRCONF;
+ rtmsg.rtmsg_info = rt_expires;
- ipv6_route_add(&rtmsg);
+ ip6_route_add(&rtmsg, &err);
}
- if (pinfo->autoconf && addrconf_sys_autoconf)
- {
+ if (pinfo->autoconf && ipv6_config.autoconf) {
struct inet6_ifaddr * ifp;
struct in6_addr addr;
int plen;
plen = pinfo->prefix_len >> 3;
- if (plen + dev->addr_len == sizeof(struct in6_addr))
- {
+ if (plen + dev->addr_len == sizeof(struct in6_addr)) {
memcpy(&addr, &pinfo->prefix, plen);
memcpy(addr.s6_addr + plen, dev->dev_addr,
dev->addr_len);
- }
- else
- {
- printk(KERN_DEBUG
- "addrconf: prefix_len invalid\n");
+ } else {
+ ADBG(("addrconf: prefix_len invalid\n"));
return;
}
ifp = ipv6_chk_addr(&addr);
- if (ifp == NULL && valid_lft)
- {
- /* create */
-
- struct inet6_dev *in6_dev;
-
- in6_dev = ipv6_get_idev(dev);
+ if (ifp == NULL && valid_lft) {
+ struct inet6_dev *in6_dev = ipv6_get_idev(dev);
if (in6_dev == NULL)
- {
- printk(KERN_DEBUG
- "addrconf: device not configured\n");
- }
+ ADBG(("addrconf: device not configured\n"));
ifp = ipv6_add_addr(in6_dev, &addr,
addr_type & IPV6_ADDR_SCOPE_MASK);
- if (dev->flags & IFF_MULTICAST)
- {
+ if (dev->flags & IFF_MULTICAST) {
struct in6_addr maddr;
- /* join to solicited addr multicast group */
+ /* Join to solicited addr multicast group. */
addrconf_addr_solict_mult(&addr, &maddr);
ipv6_dev_mc_inc(dev, &maddr);
}
- ifp->flags |= DAD_INCOMPLETE;
ifp->prefix_len = pinfo->prefix_len;
addrconf_dad_start(ifp);
-
}
- if (ifp && valid_lft == 0)
- {
+ if (ifp && valid_lft == 0) {
ipv6_del_addr(ifp);
ifp = NULL;
}
- if (ifp)
- {
+ if (ifp) {
ifp->valid_lft = valid_lft;
ifp->prefered_lft = prefered_lft;
ifp->tstamp = jiffies;
}
}
-
-}
-
-static int addrconf_ifdown(struct device *dev)
-{
- struct inet6_dev *idev, **bidev;
- struct inet6_ifaddr *ifa, **bifa;
- int i;
-
- start_bh_atomic();
-
- bidev = &inet6_dev_lst;
-
- for (idev = inet6_dev_lst; idev; idev = idev->next)
- {
- if (idev->dev == dev)
- {
- *bidev = idev->next;
- break;
- }
- bidev = &idev;
- }
-
- if (idev == NULL)
- {
- printk(KERN_DEBUG "addrconf_ifdown: device not found\n");
- end_bh_atomic();
- return -ENODEV;
- }
-
- /*
- * FIXME: clear multicast group membership
- */
-
- /*
- * clean addr_list
- */
-
- for (i=0; i<16; i++)
- {
- bifa = &inet6_addr_lst[i];
-
- for (ifa=inet6_addr_lst[i]; ifa; )
- {
- if (ifa->idev == idev)
- {
- *bifa = ifa->lst_next;
- del_timer(&ifa->timer);
- kfree(ifa);
- ifa = *bifa;
- continue;
- }
- bifa = &ifa;
- ifa = ifa->lst_next;
- }
- }
-
- kfree(idev);
- end_bh_atomic();
- return 0;
}
/*
@@ -852,67 +548,36 @@ int addrconf_set_dstaddr(void *arg)
{
struct in6_ifreq ireq;
struct device *dev;
- int err;
+ int err = -EINVAL;
- err = copy_from_user(&ireq, arg, sizeof(struct in6_ifreq));
-
- if (err)
- return -EFAULT;
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) {
+ err = -EFAULT;
+ goto err_exit;
+ }
- dev = dev_get(ireq.devname);
+ dev = dev_get_by_index(ireq.ifr6_ifindex);
- if (dev->type == ARPHRD_SIT)
- {
+ if (dev == NULL) {
+ err = -ENODEV;
+ goto err_exit;
+ }
+
+ if (dev->type == ARPHRD_SIT) {
struct device *dev;
- if (!(ipv6_addr_type(&ireq.addr) & IPV6_ADDR_COMPATv4))
- {
+ if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4))
return -EADDRNOTAVAIL;
- }
- dev = sit_add_tunnel(ireq.addr.s6_addr32[3]);
+ dev = sit_add_tunnel(ireq.ifr6_addr.s6_addr32[3]);
if (dev == NULL)
- return -ENODEV;
-
- return 0;
- }
-
- return -EINVAL;
-}
-
-/*
- * Obtain if_index from device name
- */
-int addrconf_get_ifindex(void *arg)
-{
- struct ifreq ifr;
- int res = -ENODEV;
-
- if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
- {
- res = -EFAULT;
- }
- else
- {
- struct inet6_dev *idev;
-
- for (idev = inet6_dev_lst; idev; idev=idev->next)
- {
- if (!strncmp(ifr.ifr_name, idev->dev->name, IFNAMSIZ))
- {
- res = 0;
- ifr.ifr_ifindex = idev->if_index;
- if (copy_to_user(arg, &ifr, sizeof(ifr)))
- {
- res = -EFAULT;
- }
- break;
- }
- }
+ err = -ENODEV;
+ else
+ err = 0;
}
- return res;
+err_exit:
+ return err;
}
/*
@@ -920,61 +585,118 @@ int addrconf_get_ifindex(void *arg)
*/
int addrconf_add_ifaddr(void *arg)
{
- struct inet6_dev *in6_dev;
+ struct inet6_dev *idev;
struct in6_ifreq ireq;
struct inet6_ifaddr *ifp;
struct device *dev;
- int addr_type;
- int err;
+ int scope;
if (!suser())
return -EPERM;
- err = copy_from_user(&ireq, arg, sizeof(struct in6_ifreq));
- if (err)
+ if(copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
return -EFAULT;
- dev = dev_get(ireq.devname);
-
- if (dev == NULL)
+ if((dev = dev_get_by_index(ireq.ifr6_ifindex)) == NULL)
return -EINVAL;
- in6_dev = ipv6_get_idev(dev);
-
- if (in6_dev == NULL)
+ if ((idev = ipv6_get_idev(dev)) == NULL)
return -EINVAL;
- addr_type = ipv6_addr_type(&ireq.addr);
- addr_type &= IPV6_ADDR_SCOPE_MASK;
-
- ifp = ipv6_add_addr(in6_dev, &ireq.addr, addr_type);
+ scope = ipv6_addr_scope(&ireq.ifr6_addr);
- if (ifp == NULL)
+ if((ifp = ipv6_add_addr(idev, &ireq.ifr6_addr, scope)) == NULL)
return -ENOMEM;
ifp->prefix_len = 128;
- if (dev->flags & IFF_MULTICAST)
- {
+ if (dev->flags & IFF_MULTICAST) {
struct in6_addr maddr;
- /* join to solicited addr multicast group */
- addrconf_addr_solict_mult(&ireq.addr, &maddr);
+ /* Join to solicited addr multicast group. */
+ addrconf_addr_solict_mult(&ireq.ifr6_addr, &maddr);
ipv6_dev_mc_inc(dev, &maddr);
}
-
- ifp->prefix_len = ireq.prefix_len;
+ ifp->prefix_len = ireq.ifr6_prefixlen;
ifp->flags |= ADDR_PERMANENT;
if (!(dev->flags & (IFF_NOARP|IFF_LOOPBACK)))
- {
- ifp->flags |= DAD_INCOMPLETE;
addrconf_dad_start(ifp);
- }
+ else
+ ip6_rt_addr_add(&ifp->addr, dev);
+
return 0;
}
+static void sit_route_add(struct device *dev)
+{
+ struct in6_rtmsg rtmsg;
+ struct rt6_info *rt;
+ int err;
+
+ ADBG(("sit_route_add(%s): ", dev->name));
+ memset(&rtmsg, 0, sizeof(rtmsg));
+
+ rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+
+ if (dev->pa_dstaddr == 0) {
+ ADBG(("pa_dstaddr=0, "));
+ /* prefix length - 96 bytes "::d.d.d.d" */
+ rtmsg.rtmsg_dst_len = 96;
+ rtmsg.rtmsg_flags = RTF_NONEXTHOP|RTF_UP;
+ } else {
+ ADBG(("pa_dstaddr=%08x, ", dev->pa_dstaddr));
+ rtmsg.rtmsg_dst_len = 10;
+ rtmsg.rtmsg_dst.s6_addr32[0] = __constant_htonl(0xfe800000);
+ rtmsg.rtmsg_dst.s6_addr32[3] = dev->pa_dstaddr;
+ rtmsg.rtmsg_gateway.s6_addr32[3]= dev->pa_dstaddr;
+ rtmsg.rtmsg_flags = RTF_UP;
+ }
+
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+ ADBG(("doing ip6_route_add()\n"));
+ rt = ip6_route_add(&rtmsg, &err);
+
+ if (err) {
+#if ACONF_DEBUG >= 1
+ printk(KERN_DEBUG "sit_route_add: error %d in route_add\n", err);
+#endif
+ }
+
+ ADBG(("sit_route_add(cont): "));
+ if (dev->pa_dstaddr) {
+ struct rt6_info *mrt;
+
+ ADBG(("pa_dstaddr != 0, "));
+ rt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_gateway);
+ if (rt->rt6i_nexthop == NULL) {
+ ADBG(("can't get neighbour\n"));
+ printk(KERN_DEBUG "sit_route: get_neigh failed\n");
+ }
+
+ /*
+ * Add multicast route.
+ */
+ ADBG(("add MULT, "));
+ ipv6_addr_set(&rtmsg.rtmsg_dst, __constant_htonl(0xFF000000), 0, 0, 0);
+
+ rtmsg.rtmsg_dst_len = 8;
+ rtmsg.rtmsg_flags = RTF_UP;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+
+ memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
+ ADBG(("doing ip6_route_add()\n"));
+ mrt = ip6_route_add(&rtmsg, &err);
+
+ if (mrt)
+ mrt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+ } else {
+ ADBG(("pa_dstaddr==0\n"));
+ }
+}
+
static void sit_add_v4_addrs(struct inet6_dev *idev)
{
struct inet6_ifaddr * ifp;
@@ -984,26 +706,20 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
memset(&addr, 0, sizeof(struct in6_addr));
- if (idev->dev->pa_dstaddr)
- {
+ if (idev->dev->pa_dstaddr) {
addr.s6_addr32[0] = __constant_htonl(0xfe800000);
scope = IFA_LINK;
- }
- else
- {
+ } else {
scope = IPV6_ADDR_COMPATv4;
}
- for (dev = dev_base; dev != NULL; dev = dev->next)
- {
- if (dev->family == AF_INET && (dev->flags & IFF_UP))
- {
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ if (dev->family == AF_INET && (dev->flags & IFF_UP)) {
int flag = scope;
addr.s6_addr32[3] = dev->pa_addr;
- if (dev->flags & IFF_LOOPBACK)
- {
+ if (dev->flags & IFF_LOOPBACK) {
if (idev->dev->pa_dstaddr)
continue;
@@ -1011,15 +727,94 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
}
ifp = ipv6_add_addr(idev, &addr, flag);
-
+
if (ifp == NULL)
continue;
ifp->flags |= ADDR_PERMANENT;
+ ip6_rt_addr_add(&ifp->addr, dev);
}
}
}
+static void init_loopback(struct device *dev)
+{
+ struct in6_addr addr;
+ struct inet6_dev *idev;
+ struct inet6_ifaddr * ifp;
+ int err;
+
+ /* ::1 */
+
+ memset(&addr, 0, sizeof(struct in6_addr));
+ addr.s6_addr[15] = 1;
+
+ idev = ipv6_add_dev(dev);
+
+ if (idev == NULL) {
+ printk(KERN_DEBUG "init loopback: add_dev failed\n");
+ return;
+ }
+
+ ifp = ipv6_add_addr(idev, &addr, IFA_HOST);
+
+ if (ifp == NULL) {
+ printk(KERN_DEBUG "init_loopback: add_addr failed\n");
+ return;
+ }
+
+ ifp->flags |= ADDR_PERMANENT;
+
+ err = ip6_rt_addr_add(&addr, dev);
+ if (err)
+ printk(KERN_DEBUG "init_loopback: error in route_add\n");
+}
+
+static void addrconf_eth_config(struct device *dev)
+{
+ struct in6_addr addr;
+ struct in6_addr maddr;
+ struct inet6_ifaddr * ifp;
+ struct inet6_dev * idev;
+
+ memset(&addr, 0, sizeof(struct in6_addr));
+
+ /* Generate link local address. */
+ addr.s6_addr[0] = 0xFE;
+ addr.s6_addr[1] = 0x80;
+
+ memcpy(addr.s6_addr + (sizeof(struct in6_addr) - dev->addr_len),
+ dev->dev_addr, dev->addr_len);
+
+ idev = ipv6_add_dev(dev);
+ if (idev == NULL)
+ return;
+
+ ifp = ipv6_add_addr(idev, &addr, IFA_LINK);
+ if (ifp == NULL)
+ return;
+
+ ifp->flags = ADDR_PERMANENT;
+ ifp->prefix_len = 10;
+
+ /* Join to all nodes multicast group. */
+ ipv6_addr_all_nodes(&maddr);
+ ipv6_dev_mc_inc(dev, &maddr);
+
+ if (ipv6_config.forwarding) {
+ idev->router = 1;
+ ipv6_addr_all_routers(&maddr);
+ ipv6_dev_mc_inc(dev, &maddr);
+ }
+
+ /* Join to solicited addr multicast group. */
+ addrconf_addr_solict_mult(&addr, &maddr);
+ ipv6_dev_mc_inc(dev, &maddr);
+
+ /* Start duplicate address detection. */
+ addrconf_dad_start(ifp);
+}
+
int addrconf_notify(struct notifier_block *this, unsigned long event,
void * data)
{
@@ -1050,7 +845,7 @@ int addrconf_notify(struct notifier_block *this, unsigned long event,
* route.
*/
- sit_route_add(idev);
+ sit_route_add(dev);
break;
case ARPHRD_LOOPBACK:
@@ -1058,12 +853,12 @@ int addrconf_notify(struct notifier_block *this, unsigned long event,
break;
case ARPHRD_ETHER:
-
printk(KERN_DEBUG "Configuring eth interface\n");
addrconf_eth_config(dev);
break;
- }
- rt6_sndmsg(RTMSG_NEWDEVICE, NULL, NULL, 0, dev, 0, 0);
+ };
+
+ rt6_sndmsg(RTMSG_NEWDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
break;
case NETDEV_DOWN:
@@ -1071,104 +866,72 @@ int addrconf_notify(struct notifier_block *this, unsigned long event,
* Remove all addresses from this interface
* and take the interface out of the list.
*/
- if (addrconf_ifdown(dev) == 0)
- {
+ if (addrconf_ifdown(dev) == 0) {
+#if 0
rt6_ifdown(dev);
- rt6_sndmsg(RTMSG_DELDEVICE, NULL, NULL, 0, dev, 0, 0);
+#endif
+ rt6_sndmsg(RTMSG_DELDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
}
break;
- }
+ };
return NOTIFY_OK;
}
-static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
-{
- struct in6_rtmsg rtmsg;
- struct device *dev;
- int err;
-
-
- if (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)
- {
- struct in6_addr all_routers;
-
- /*
- * 1) configure a link route for this interface
- * 2) send a (delayed) router solicitation
- */
-
- memcpy(&rtmsg.rtmsg_dst, &ifp->addr, sizeof(struct in6_addr));
- memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
-
- dev = ifp->idev->dev;
- rtmsg.rtmsg_prefixlen = ifp->prefix_len;
- rtmsg.rtmsg_metric = 1;
- rtmsg.rtmsg_ifindex = ifp->idev->if_index;
-
- rtmsg.rtmsg_flags = RTF_UP;
-
- err = ipv6_route_add(&rtmsg);
-
- if (err)
- {
- printk(KERN_DEBUG "dad_complete: error in route_add\n");
- }
+static int addrconf_ifdown(struct device *dev)
+{
+ struct inet6_dev *idev, **bidev;
+ struct inet6_ifaddr *ifa, **bifa;
+ int i, hash;
- if (ipv6_forwarding == 0)
- {
- ipv6_addr_set(&all_routers,
- __constant_htonl(0xff020000U), 0, 0,
- __constant_htonl(0x2U));
+ start_bh_atomic();
- /*
- * If a host as already performed a random delay
- * [...] as part of DAD [...] there is no need
- * to delay again before sending the first RS
- */
- ndisc_send_rs(ifp->idev->dev, &ifp->addr,
- &all_routers);
+ hash = ipv6_devindex_hash(dev->ifindex);
+ bidev = &inet6_dev_lst[hash];
- ifp->probes = 1;
- ifp->timer.function = addrconf_rs_timer;
- ifp->timer.expires = (jiffies +
- RTR_SOLICITATION_INTERVAL);
- ifp->idev->if_flags |= IF_RS_SENT;
- add_timer(&ifp->timer);
+ for (idev = inet6_dev_lst[hash]; idev; idev = idev->next) {
+ if (idev->dev == dev) {
+ *bidev = idev->next;
+ break;
}
+ bidev = &idev->next;
}
-}
+ if (idev == NULL) {
+ printk(KERN_DEBUG "addrconf_ifdown: device not found\n");
+ end_bh_atomic();
+ return -ENODEV;
+ }
-static void addrconf_dad_timer(unsigned long data)
-{
- struct inet6_ifaddr *ifp;
- struct in6_addr unspec;
- struct in6_addr mcaddr;
+ /*
+ * FIXME: clear multicast group membership
+ */
- ifp = (struct inet6_ifaddr *) data;
+ /*
+ * clean addr_list
+ */
- if (ifp->probes-- == 0)
- {
- /*
- * DAD was successful
- */
+ for (i=0; i<16; i++) {
+ bifa = &inet6_addr_lst[i];
- ifp->flags &= ~DAD_INCOMPLETE;
- addrconf_dad_completed(ifp);
- return;
+ for (ifa=inet6_addr_lst[i]; ifa; ) {
+ if (ifa->idev == idev) {
+ *bifa = ifa->lst_next;
+ del_timer(&ifa->timer);
+ kfree(ifa);
+ ifa = *bifa;
+ continue;
+ }
+ ifa = ifa->lst_next;
+ bifa = &ifa->lst_next;
+ }
}
- /* send a neighbour solicitation for our addr */
- memset(&unspec, 0, sizeof(unspec));
- addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
-
- ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
-
- ifp->timer.expires = jiffies + RETRANS_TIMER;
- add_timer(&ifp->timer);
+ kfree(idev);
+ end_bh_atomic();
+ return 0;
}
static void addrconf_rs_timer(unsigned long data)
@@ -1177,11 +940,10 @@ static void addrconf_rs_timer(unsigned long data)
ifp = (struct inet6_ifaddr *) data;
- if (ipv6_forwarding)
+ if (ipv6_config.forwarding)
return;
- if (ifp->idev->if_flags & IF_RA_RCVD)
- {
+ if (ifp->idev->if_flags & IF_RA_RCVD) {
/*
* Announcement received after solicitation
* was sent
@@ -1189,8 +951,7 @@ static void addrconf_rs_timer(unsigned long data)
return;
}
- if (ifp->probes++ <= MAX_RTR_SOLICITATIONS)
- {
+ if (ifp->probes++ <= ipv6_config.rtr_solicits) {
struct in6_addr all_routers;
ipv6_addr_set(&all_routers,
@@ -1199,54 +960,77 @@ static void addrconf_rs_timer(unsigned long data)
ndisc_send_rs(ifp->idev->dev, &ifp->addr,
&all_routers);
-
ifp->timer.function = addrconf_rs_timer;
- ifp->timer.expires = jiffies + RTR_SOLICITATION_INTERVAL;
+ ifp->timer.expires = (jiffies +
+ ipv6_config.rtr_solicit_interval);
add_timer(&ifp->timer);
- }
- else
- {
+ } else {
+ struct in6_rtmsg rtmsg;
+ int err;
+
+#if ACONF_DEBUG >= 2
printk(KERN_DEBUG "%s: no IPv6 routers present\n",
ifp->idev->dev->name);
+#endif
- if (!default_rt_list && !last_resort_rt)
- {
- struct rt6_info *rt;
+ memset(&rtmsg, 0, sizeof(struct in6_rtmsg));
+ rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+ rtmsg.rtmsg_flags = (RTF_ALLONLINK | RTF_ADDRCONF |
+ RTF_DEFAULT | RTF_UP);
- /*
- * create a last resort route with all
- * destinations on link
- */
- rt = kmalloc(sizeof(struct rt6_info), GFP_ATOMIC);
-
- if (rt)
- {
- memset(rt, 0, sizeof(struct rt6_info));
- rt->rt_dev = ifp->idev->dev;
- rt->rt_ref = 1;
- rt->rt_flags = (RTI_ALLONLINK | RTF_UP);
- last_resort_rt = rt;
- }
- }
+ rtmsg.rtmsg_ifindex = ifp->idev->dev->ifindex;
+
+ ip6_route_add(&rtmsg, &err);
}
}
+/*
+ * Duplicate Address Detection
+ */
static void addrconf_dad_start(struct inet6_ifaddr *ifp)
{
static int rand_seed = 1;
- int rand_num;
+ struct device *dev;
+ unsigned long rand_num;
+
+ dev = ifp->idev->dev;
+
+ if (dev->flags & IFF_MULTICAST) {
+ struct in6_rtmsg rtmsg;
+ struct rt6_info *mrt;
+ int err;
+
+ memset(&rtmsg, 0, sizeof(rtmsg));
+ ipv6_addr_set(&rtmsg.rtmsg_dst,
+ __constant_htonl(0xFF000000), 0, 0, 0);
+
+ rtmsg.rtmsg_dst_len = 8;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+
+ rtmsg.rtmsg_flags = RTF_UP;
- if (rand_seed)
- {
+ mrt = ip6_route_add(&rtmsg, &err);
+
+ if (err)
+ printk(KERN_DEBUG "dad_start: mcast route add failed\n");
+ else
+ mrt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+ }
+
+ if (rand_seed) {
rand_seed = 0;
nd_rand_seed = ifp->addr.s6_addr32[3];
}
init_timer(&ifp->timer);
- ifp->probes = DupAddrDetectTransmits;
- rand_num = ipv6_random() % MAX_RTR_SOLICITATION_DELAY;
+ ifp->probes = ipv6_config.dad_transmits;
+ ifp->flags |= DAD_INCOMPLETE;
+
+ rand_num = ipv6_random() % ipv6_config.rtr_solicit_delay;
ifp->timer.function = addrconf_dad_timer;
ifp->timer.data = (unsigned long) ifp;
@@ -1255,6 +1039,97 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp)
add_timer(&ifp->timer);
}
+static void addrconf_dad_timer(unsigned long data)
+{
+ struct inet6_ifaddr *ifp;
+ struct in6_addr unspec;
+ struct in6_addr mcaddr;
+
+ ifp = (struct inet6_ifaddr *) data;
+
+ if (ifp->probes == 0) {
+ /*
+ * DAD was successful
+ */
+
+ ifp->flags &= ~DAD_INCOMPLETE;
+ addrconf_dad_completed(ifp);
+ return;
+ }
+
+ ifp->probes--;
+
+ /* send a neighbour solicitation for our addr */
+ memset(&unspec, 0, sizeof(unspec));
+ addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
+
+ ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
+
+ ifp->timer.expires = jiffies + ipv6_config.rtr_solicit_interval;
+ add_timer(&ifp->timer);
+}
+
+static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
+{
+ struct device *dev;
+ int err;
+
+ dev = ifp->idev->dev;
+
+ if (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) {
+ struct in6_rtmsg rtmsg;
+ struct in6_addr all_routers;
+
+ /*
+ * 1) configure a link route for this interface
+ * 2) send a (delayed) router solicitation
+ */
+
+ memset(&rtmsg, 0, sizeof(rtmsg));
+
+ memcpy(&rtmsg.rtmsg_dst, &ifp->addr, sizeof(struct in6_addr));
+
+ rtmsg.rtmsg_dst_len = ifp->prefix_len;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+
+ rtmsg.rtmsg_flags = RTF_UP;
+
+ ip6_route_add(&rtmsg, &err);
+
+ if (err)
+ printk(KERN_DEBUG "dad_complete: error in route_add\n");
+
+ if (ipv6_config.forwarding == 0) {
+ ipv6_addr_set(&all_routers,
+ __constant_htonl(0xff020000U), 0, 0,
+ __constant_htonl(0x2U));
+
+ /*
+ * If a host as already performed a random delay
+ * [...] as part of DAD [...] there is no need
+ * to delay again before sending the first RS
+ */
+ ndisc_send_rs(ifp->idev->dev, &ifp->addr,
+ &all_routers);
+
+ ifp->probes = 1;
+ ifp->timer.function = addrconf_rs_timer;
+ ifp->timer.expires = (jiffies +
+ ipv6_config.rtr_solicit_interval);
+ ifp->idev->if_flags |= IF_RS_SENT;
+ add_timer(&ifp->timer);
+ }
+ }
+
+ /*
+ * configure the address for reception
+ */
+
+ ip6_rt_addr_add(&ifp->addr, dev);
+}
+
+#ifdef CONFIG_PROC_FS
static int iface_proc_info(char *buffer, char **start, off_t offset,
int length, int dummy)
{
@@ -1262,13 +1137,11 @@ static int iface_proc_info(char *buffer, char **start, off_t offset,
int i;
int len = 0;
- for (i=0; i < HASH_SIZE; i++)
- for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next)
- {
+ for (i=0; i < IN6_ADDR_HSIZE; i++)
+ for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
int j;
- for (j=0; j<16; j++)
- {
+ for (j=0; j<16; j++) {
sprintf(buffer + len, "%02x",
ifp->addr.s6_addr[j]);
len += 2;
@@ -1276,7 +1149,7 @@ static int iface_proc_info(char *buffer, char **start, off_t offset,
len += sprintf(buffer + len,
" %02x %02x %02x %02x %8s\n",
- ifp->idev->if_index,
+ ifp->idev->dev->ifindex,
ifp->prefix_len,
ifp->scope,
ifp->flags,
@@ -1299,7 +1172,7 @@ struct proc_dir_entry iface_proc_entry =
0, NULL,
&iface_proc_info
};
-
+#endif /* CONFIG_PROC_FS */
/*
* Periodic address status verification
@@ -1311,29 +1184,23 @@ void addrconf_verify(unsigned long foo)
unsigned long now = jiffies;
int i;
- for (i=0; i < HASH_SIZE; i++)
- {
- for (ifp=inet6_addr_lst[i]; ifp;)
- {
- if (!(ifp->flags & ADDR_PERMANENT))
- {
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ for (ifp=inet6_addr_lst[i]; ifp;) {
+ if (!(ifp->flags & ADDR_PERMANENT)) {
struct inet6_ifaddr *bp;
unsigned long age;
age = (now - ifp->tstamp) / HZ;
if (age > ifp->prefered_lft)
- {
ifp->flags |= ADDR_DEPRECATED;
- }
bp = ifp;
ifp=ifp->lst_next;
if (age > bp->valid_lft)
- {
ipv6_del_addr(bp);
- }
+
continue;
}
ifp=ifp->lst_next;
@@ -1344,18 +1211,25 @@ void addrconf_verify(unsigned long foo)
add_timer(&addr_chk_timer);
}
+/*
+ * Init / cleanup code
+ */
+
void addrconf_init()
{
struct device *dev;
- /* init addr hash list */
- memset(inet6_addr_lst, 0, 16 * sizeof(struct inet6_ifaddr *));
+ /*
+ * init address and device hash lists
+ */
+
+ memset(inet6_addr_lst, 0, IN6_ADDR_HSIZE * sizeof(struct inet6_ifaddr *));
- memset(inet6_mcast_lst, 0, 16 * sizeof(struct ipv6_mc_list *));
+ memset(inet6_mcast_lst, 0, IN6_ADDR_HSIZE * sizeof(struct ifmcaddr6 *));
- inet6_dev_lst = NULL;
+ memset(inet6_dev_lst, 0, IN6_ADDR_HSIZE * sizeof(struct inet6_dev *));
- /*
+ /*
* Init loopback device
*/
@@ -1374,16 +1248,19 @@ void addrconf_init()
if (dev && (dev->flags & IFF_UP))
addrconf_eth_config(dev);
- proc_register_dynamic(&proc_net, &iface_proc_entry);
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&iface_proc_entry);
+#endif
addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY;
add_timer(&addr_chk_timer);
}
+#ifdef MODULE
void addrconf_cleanup(void)
{
- struct inet6_dev *idev, *bidev;
- struct inet6_ifaddr *ifa, *bifa;
+ struct inet6_dev *idev;
+ struct inet6_ifaddr *ifa;
int i;
del_timer(&addr_chk_timer);
@@ -1392,32 +1269,32 @@ void addrconf_cleanup(void)
* clean dev list.
*/
- for (idev = inet6_dev_lst; idev; )
- {
- bidev = idev;
- idev = idev->next;
- kfree(bidev);
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ for (idev = inet6_dev_lst[i]; idev; ) {
+ struct inet6_dev *back;
+
+ back = idev;
+ idev = idev->next;
+ kfree(back);
+ }
}
/*
* clean addr_list
*/
- for (i=0; i<16; i++)
- {
- for (ifa=inet6_addr_lst[i]; ifa; )
- {
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ for (ifa=inet6_addr_lst[i]; ifa; ) {
+ struct inet6_ifaddr *bifa;
+
bifa = ifa;
ifa = ifa->lst_next;
kfree(bifa);
}
}
- proc_unregister(&proc_net, iface_proc_entry.low_ino);
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(iface_proc_entry.low_ino);
+#endif
}
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */
+#endif /* MODULE */
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 2609e5294..0f6bbf4de 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -7,7 +7,7 @@
*
* Adapted from linux/net/ipv4/af_inet.c
*
- * $Id: af_inet6.c,v 1.13 1996/10/31 19:47:17 roque Exp $
+ * $Id: af_inet6.c,v 1.16 1997/03/18 18:24:26 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -35,293 +35,182 @@
#include <linux/proc_fs.h>
#include <linux/stat.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-
#include <linux/inet.h>
#include <linux/netdevice.h>
+#include <linux/icmpv6.h>
+
#include <net/ip.h>
#include <net/ipv6.h>
-#include <net/protocol.h>
-#include <net/arp.h>
-#include <net/rarp.h>
-#include <net/route.h>
-#include <net/tcp.h>
#include <net/udp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/raw.h>
-#include <net/icmp.h>
-#include <linux/icmpv6.h>
+#include <net/tcp.h>
+#include <net/sit.h>
+#include <net/protocol.h>
#include <net/inet_common.h>
#include <net/transp_v6.h>
-#include <net/ndisc.h>
-#include <net/ipv6_route.h>
-#include <net/sit.h>
-#include <linux/ip_fw.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
-/*
- * Default callbacks for user INET sockets. These just wake up
- * the user owning the socket.
- */
-
-static void def_callback1(struct sock *sk)
-{
- if(!sk->dead)
- wake_up_interruptible(sk->sleep);
-}
-
-static void def_callback2(struct sock *sk,int len)
-{
- if(!sk->dead)
- {
- wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket, 1);
- }
-}
+#include <asm/uaccess.h>
+#include <asm/system.h>
-static void def_callback3(struct sock *sk)
-{
- long wmem;
-
- wmem = (long) sk->wmem_alloc;
+extern struct proto_ops inet6_stream_ops;
+extern struct proto_ops inet6_dgram_ops;
- if (wmem < 0) {
- printk(KERN_DEBUG "bug wmem_alloc < 0\n");
- sk->wmem_alloc = 0;
- }
-
- if(!sk->dead && sk->wmem_alloc*2 <= sk->sndbuf)
- {
- wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket, 2);
- }
-}
+/* IPv6 procfs goodies... */
-struct sock * rawv6_sock_array[SOCK_ARRAY_SIZE];
+#ifdef CONFIG_PROC_FS
+extern int raw6_get_info(char *, char **, off_t, int, int);
+extern int tcp6_get_info(char *, char **, off_t, int, int);
+extern int udp6_get_info(char *, char **, off_t, int, int);
+extern int afinet6_get_info(char *, char **, off_t, int, int);
+#endif
static int inet6_create(struct socket *sock, int protocol)
{
struct sock *sk;
struct proto *prot;
- int err;
sk = sk_alloc(GFP_KERNEL);
if (sk == NULL)
- return(-ENOBUFS);
-
- /* Efficient way to set most fields to zero */
- memset(sk,0,sizeof(*sk));
-
- /*
- * Note for tcp that also wiped the dummy_th block for us.
- */
-
- switch(sock->type)
- {
- case SOCK_STREAM:
- case SOCK_SEQPACKET:
- if (protocol && protocol != IPPROTO_TCP)
- {
- kfree_s((void *)sk, sizeof(*sk));
- return(-EPROTONOSUPPORT);
- }
- protocol = IPPROTO_TCP;
- sk->no_check = TCP_NO_CHECK;
- prot = &tcpv6_prot;
- break;
-
- case SOCK_DGRAM:
- if (protocol && protocol != IPPROTO_UDP)
- {
- kfree_s((void *)sk, sizeof(*sk));
- return(-EPROTONOSUPPORT);
- }
- protocol = IPPROTO_UDP;
- sk->no_check = UDP_NO_CHECK;
- prot=&udpv6_prot;
- break;
-
- case SOCK_RAW:
- if (!suser())
- {
- kfree_s((void *)sk, sizeof(*sk));
- return(-EPERM);
- }
- if (!protocol)
- {
- kfree_s((void *)sk, sizeof(*sk));
- return(-EPROTONOSUPPORT);
- }
- prot = &rawv6_prot;
- sk->reuse = 1;
- sk->num = protocol;
- break;
- default:
- kfree_s((void *)sk, sizeof(*sk));
- return(-ESOCKTNOSUPPORT);
+ goto do_oom;
+
+ /* Note for tcp that also wiped the dummy_th block for us. */
+ if(sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET) {
+ if (protocol && protocol != IPPROTO_TCP)
+ goto free_and_noproto;
+ protocol = IPPROTO_TCP;
+ sk->no_check = TCP_NO_CHECK;
+ prot = &tcpv6_prot;
+ sock->ops = &inet6_stream_ops;
+ } else if(sock->type == SOCK_DGRAM) {
+ if (protocol && protocol != IPPROTO_UDP)
+ goto free_and_noproto;
+ protocol = IPPROTO_UDP;
+ sk->no_check = UDP_NO_CHECK;
+ prot=&udpv6_prot;
+ sock->ops = &inet6_dgram_ops;
+ } else if(sock->type == SOCK_RAW) {
+ if (!suser())
+ goto free_and_badperm;
+ if (!protocol)
+ goto free_and_noproto;
+ prot = &rawv6_prot;
+ sock->ops = &inet6_dgram_ops;
+ sk->reuse = 1;
+ sk->num = protocol;
+ } else {
+ goto free_and_badtype;
}
+
+ sock_init_data(sock, sk);
- sk->socket = sock;
-
- sk->family = AF_INET6;
- sk->type = sock->type;
- sk->protocol = protocol;
- sk->allocation = GFP_KERNEL;
- sk->sndbuf = SK_WMEM_MAX;
- sk->rcvbuf = SK_RMEM_MAX;
- sk->priority = 1;
-
- sk->prot = prot;
- sk->backlog_rcv = prot->backlog_rcv;
-
- sk->sleep = sock->wait;
- sock->data =(void *) sk;
-
- sk->state = TCP_CLOSE;
-
- skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->receive_queue);
- skb_queue_head_init(&sk->back_log);
+ sk->zapped = 0;
+ sk->family = AF_INET6;
+ sk->protocol = protocol;
- sk->timer.data = (unsigned long)sk;
- sk->timer.function = &net_timer;
- init_timer(&sk->timer);
+ sk->prot = prot;
+ sk->backlog_rcv = prot->backlog_rcv;
- sk->state_change = def_callback1;
- sk->data_ready = def_callback2;
- sk->write_space = def_callback3;
- sk->error_report = def_callback1;
+ sk->timer.data = (unsigned long)sk;
+ sk->timer.function = &net_timer;
- sk->net_pinfo.af_inet6.hop_limit = ipv6_hop_limit;
+ sk->net_pinfo.af_inet6.hop_limit = ipv6_config.hop_limit;
sk->net_pinfo.af_inet6.mcast_hops = IPV6_DEFAULT_MCASTHOPS;
sk->net_pinfo.af_inet6.mc_loop = 1;
- /*
- * init the ipv4 part of the socket since
- * we can have sockets using v6 API for ipv4
+ /* Init the ipv4 part of the socket since we can have sockets
+ * using v6 API for ipv4.
*/
+ sk->ip_ttl = 64;
- sk->ip_ttl=64;
-
-#ifdef CONFIG_IP_MULTICAST
- sk->ip_mc_loop=1;
- sk->ip_mc_ttl=1;
- *sk->ip_mc_name=0;
- sk->ip_mc_list=NULL;
-#endif
-
+ sk->ip_mc_loop = 1;
+ sk->ip_mc_ttl = 1;
+ sk->ip_mc_index = 0;
+ sk->ip_mc_list = NULL;
if (sk->type==SOCK_RAW && protocol==IPPROTO_RAW)
sk->ip_hdrincl=1;
- if (sk->num)
- {
- /*
- * It assumes that any protocol which allows
+ if (sk->num) {
+ /* It assumes that any protocol which allows
* the user to assign a number at socket
- * creation time automatically
- * shares.
+ * creation time automatically shares.
*/
-
- inet_put_sock(sk->num, sk);
sk->dummy_th.source = ntohs(sk->num);
+ if(sk->prot->hash)
+ sk->prot->hash(sk);
+ add_to_prot_sklist(sk);
}
- if (sk->prot->init)
- {
- err = sk->prot->init(sk);
- if (err != 0)
- {
+ if (sk->prot->init) {
+ int err = sk->prot->init(sk);
+ if (err != 0) {
destroy_sock(sk);
return(err);
}
}
MOD_INC_USE_COUNT;
return(0);
+
+free_and_badtype:
+ sk_free(sk);
+ return -ESOCKTNOSUPPORT;
+free_and_badperm:
+ sk_free(sk);
+ return -EPERM;
+free_and_noproto:
+ sk_free(sk);
+ return -EPROTONOSUPPORT;
+do_oom:
+ return -ENOBUFS;
}
static int inet6_dup(struct socket *newsock, struct socket *oldsock)
{
- return(inet6_create(newsock,
- ((struct sock *)(oldsock->data))->protocol));
+ return(inet6_create(newsock, oldsock->sk->protocol));
}
-
-/*
- * bind for INET6 API
- */
-
-static int inet6_bind(struct socket *sock, struct sockaddr *uaddr,
- int addr_len)
+/* bind for INET6 API */
+static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr;
- struct sock *sk=(struct sock *)sock->data, *sk2;
+ struct sock *sk = sock->sk;
__u32 v4addr = 0;
unsigned short snum = 0;
int addr_type = 0;
- /*
- * If the socket has its own bind function then use it.
- */
-
+ /* If the socket has its own bind function then use it. */
if(sk->prot->bind)
return sk->prot->bind(sk, uaddr, addr_len);
- /* check this error. */
- if (sk->state != TCP_CLOSE)
- return(-EINVAL);
-
- if(addr_len < sizeof(struct sockaddr_in6))
+ /* Check these errors (active socket, bad address length, double bind). */
+ if ((sk->state != TCP_CLOSE) ||
+ (addr_len < sizeof(struct sockaddr_in6)) ||
+ (sk->num != 0))
return -EINVAL;
- if(sock->type != SOCK_RAW)
- {
- if (sk->num != 0)
- return(-EINVAL);
-
- snum = ntohs(addr->sin6_port);
-
- if (snum == 0)
- snum = get_new_socknum(sk->prot, 0);
-
- if (snum < PROT_SOCK && !suser())
- return(-EACCES);
- }
+ snum = ntohs(addr->sin6_port);
+ if (snum == 0)
+ snum = sk->prot->good_socknum();
+ if (snum < PROT_SOCK && !suser())
+ return(-EACCES);
addr_type = ipv6_addr_type(&addr->sin6_addr);
-
if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM)
- {
return(-EINVAL);
- }
-
- /*
- * check if the address belongs to the host
- */
- if (addr_type == IPV6_ADDR_MAPPED)
- {
+ /* Check if the address belongs to the host. */
+ if (addr_type == IPV6_ADDR_MAPPED) {
v4addr = addr->sin6_addr.s6_addr32[3];
-
- if (ip_chk_addr(v4addr) != IS_MYADDR)
+ if (__ip_chk_addr(v4addr) != IS_MYADDR)
return(-EADDRNOTAVAIL);
- }
- else
- {
- if (addr_type != IPV6_ADDR_ANY)
- {
- /*
- * ipv4 addr of the socket is invalid.
- * only the unpecified and mapped address
- * have a v4 equivalent.
+ } else {
+ if (addr_type != IPV6_ADDR_ANY) {
+ /* ipv4 addr of the socket is invalid. Only the
+ * unpecified and mapped address have a v4 equivalent.
*/
-
v4addr = LOOPBACK4_IPV6;
-
- if (!(addr_type & IPV6_ADDR_MULTICAST))
- {
+ if (!(addr_type & IPV6_ADDR_MULTICAST)) {
if (ipv6_chk_addr(&addr->sin6_addr) == NULL)
return(-EADDRNOTAVAIL);
}
@@ -338,82 +227,16 @@ static int inet6_bind(struct socket *sock, struct sockaddr *uaddr,
memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr,
sizeof(struct in6_addr));
- if(sock->type != SOCK_RAW)
- {
- /* Make sure we are allowed to bind here. */
- cli();
- for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
- sk2 != NULL; sk2 = sk2->next)
- {
- /*
- * Hash collision or real match ?
- */
-
- if (sk2->num != snum)
- continue;
-
- /*
- * Either bind on the port is wildcard means
- * they will overlap and thus be in error.
- * We use the sk2 v4 address to test the
- * other socket since addr_any in av4 implies
- * addr_any in v6
- */
-
- if (addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr))
- {
- /*
- * Allow only if both are setting reuse.
- */
- if(sk2->reuse && sk->reuse && sk2->state!=TCP_LISTEN)
- continue;
- sti();
- return(-EADDRINUSE);
- }
-
- /*
- * Two binds match ?
- */
-
- if (ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
- &sk2->net_pinfo.af_inet6.rcv_saddr))
-
- continue;
- /*
- * Reusable port ?
- */
-
- if (!sk->reuse)
- {
- sti();
- return(-EADDRINUSE);
- }
-
- /*
- * Reuse ?
- */
-
- if (!sk2->reuse || sk2->state==TCP_LISTEN)
- {
- sti();
- return(-EADDRINUSE);
- }
- }
- sti();
+ /* Make sure we are allowed to bind here. */
+ if(sk->prot->verify_bind(sk, snum))
+ return -EADDRINUSE;
- inet_remove_sock(sk);
-
- /*
- if(sock->type==SOCK_DGRAM)
- udp_cache_zap();
- if(sock->type==SOCK_STREAM)
- tcp_cache_zap();
- */
- inet_put_sock(snum, sk);
- sk->dummy_th.source = ntohs(sk->num);
- sk->dummy_th.dest = 0;
- sk->daddr = 0;
- }
+ sk->num = snum;
+ sk->dummy_th.source = ntohs(sk->num);
+ sk->dummy_th.dest = 0;
+ sk->daddr = 0;
+ sk->prot->rehash(sk);
+ add_to_prot_sklist(sk);
return(0);
}
@@ -440,39 +263,32 @@ static int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
struct sock *sk;
sin->sin6_family = AF_INET6;
- sk = (struct sock *) sock->data;
- if (peer)
- {
+ sk = sock->sk;
+ if (peer) {
if (!tcp_connected(sk->state))
return(-ENOTCONN);
sin->sin6_port = sk->dummy_th.dest;
memcpy(&sin->sin6_addr, &sk->net_pinfo.af_inet6.daddr,
sizeof(struct in6_addr));
- }
- else
- {
- if (ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr) ==
- IPV6_ADDR_ANY)
+ } else {
+ if (ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr) == IPV6_ADDR_ANY)
memcpy(&sin->sin6_addr,
&sk->net_pinfo.af_inet6.saddr,
sizeof(struct in6_addr));
-
else
memcpy(&sin->sin6_addr,
&sk->net_pinfo.af_inet6.rcv_saddr,
sizeof(struct in6_addr));
sin->sin6_port = sk->dummy_th.source;
-
}
-
*uaddr_len = sizeof(*sin);
return(0);
}
static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
- struct sock *sk=(struct sock *)sock->data;
+ struct sock *sk = sock->sk;
int err;
int pid;
@@ -484,7 +300,7 @@ static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
if(err)
return err;
- /* see inet_fcntl */
+ /* see sock_no_fcntl */
if (current->pid != pid && current->pgrp != -pid && !suser())
return -EPERM;
sk->proc = pid;
@@ -545,18 +361,10 @@ static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCGIFMAP:
case SIOCSIFSLAVE:
case SIOCGIFSLAVE:
+ case SIOGIFINDEX:
return(dev_ioctl(cmd,(void *) arg));
- return -EINVAL;
-
- case SIOGIFINDEX:
- /*
- * This one will be moved to the generic device
- * layer in the near future
- */
- return addrconf_get_ifindex((void *) arg);
-
case SIOCSIFADDR:
return addrconf_add_ifaddr((void *) arg);
case SIOCSIFDSTADDR:
@@ -574,276 +382,112 @@ static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return(0);
}
-/*
- * This routine must find a socket given a TCP or UDP header.
- * Everything is assumed to be in net order.
- *
- * We give priority to more closely bound ports: if some socket
- * is bound to a particular foreign address, it will get the packet
- * rather than somebody listening to any address..
- */
-
-struct sock *inet6_get_sock(struct proto *prot,
- struct in6_addr *loc_addr,
- struct in6_addr *rmt_addr,
- unsigned short loc_port,
- unsigned short rmt_port)
-{
- struct sock *s;
- struct sock *result = NULL;
- int badness = -1;
- unsigned short hnum;
- struct ipv6_pinfo *np;
- hnum = ntohs(loc_port);
-
- /*
- * SOCK_ARRAY_SIZE must be a power of two. This will work better
- * than a prime unless 3 or more sockets end up using the same
- * array entry. This should not be a problem because most
- * well known sockets don't overlap that much, and for
- * the other ones, we can just be careful about picking our
- * socket number when we choose an arbitrary one.
- */
-
- for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
- s != NULL; s = s->next)
- {
- int score = 0;
-
- if ((s->num != hnum) || s->family != AF_INET6)
- continue;
-
- if(s->dead && (s->state == TCP_CLOSE))
- {
- printk(KERN_DEBUG "dead or closed socket\n");
- continue;
- }
-
- np = &s->net_pinfo.af_inet6;
-
- /* remote port matches? */
-
- if (s->dummy_th.dest) {
- if (s->dummy_th.dest != rmt_port)
- {
- continue;
- }
- score++;
- }
-
- /* local address matches? */
-
- if (!ipv6_addr_any(&np->rcv_saddr))
- {
- if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr))
- {
- continue;
- }
- score++;
- }
-
- /* remote address matches? */
- if (!ipv6_addr_any(&np->daddr))
- {
- if (ipv6_addr_cmp(&np->daddr, rmt_addr))
- {
- continue;
- }
- score++;
- }
-
- /* perfect match? */
- if (score == 3)
- return s;
- /* no, check if this is the best so far.. */
- if (score <= badness)
- continue;
- result = s;
- badness = score;
- }
- return result;
-}
-
-static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
-{
- struct ipv6_mc_socklist *mc;
-
- for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next)
- {
- if (ipv6_addr_cmp(&mc->addr, addr) == 0)
- return 1;
- }
-
- return 0;
-}
-
-/*
- * Deliver a datagram to raw sockets.
- */
-
-struct sock *inet6_get_sock_raw(struct sock *sk, unsigned short num,
- struct in6_addr *loc_addr,
- struct in6_addr *rmt_addr)
-
-{
- struct sock *s;
- struct ipv6_pinfo *np;
- int addr_type = 0;
-
- s=sk;
-
- addr_type = ipv6_addr_type(loc_addr);
-
- for(; s != NULL; s = s->next)
- {
- if (s->num != num)
- continue;
-
- if(s->dead && (s->state == TCP_CLOSE))
- continue;
-
- np = &s->net_pinfo.af_inet6;
-
- if (!ipv6_addr_any(&np->daddr) &&
- ipv6_addr_cmp(&np->daddr, rmt_addr))
- {
- continue;
- }
-
- if (!ipv6_addr_any(&np->rcv_saddr))
- {
- if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
- return(s);
-
- if ((addr_type & IPV6_ADDR_MULTICAST) &&
- inet6_mc_check(s, loc_addr))
- return (s);
-
- continue;
- }
-
- return(s);
- }
- return(NULL);
-}
-
-/*
- * inet6_get_sock_mcast for UDP sockets.
- */
-
-struct sock *inet6_get_sock_mcast(struct sock *sk,
- unsigned short num, unsigned short rmt_port,
- struct in6_addr *loc_addr,
- struct in6_addr *rmt_addr)
-{
- struct sock *s;
- struct ipv6_pinfo *np;
-
- s=sk;
-
- for(; s != NULL; s = s->next)
- {
- if (s->num != num)
- continue;
-
- if(s->dead && (s->state == TCP_CLOSE))
- continue;
-
- np = &s->net_pinfo.af_inet6;
-
- if (s->dummy_th.dest) {
- if (s->dummy_th.dest != rmt_port)
- {
- continue;
- }
- }
-
- if (!ipv6_addr_any(&np->daddr) &&
- ipv6_addr_cmp(&np->daddr, rmt_addr))
- {
- continue;
- }
-
-
- if (!ipv6_addr_any(&np->rcv_saddr))
- {
- if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
- return(s);
- }
-
- if (!inet6_mc_check(s, loc_addr))
- {
- continue;
- }
+struct proto_ops inet6_stream_ops = {
+ AF_INET6,
- return(s);
- }
- return(NULL);
-}
-
+ inet6_dup,
+ inet6_release,
+ inet6_bind,
+ inet_stream_connect, /* ok */
+ inet6_socketpair, /* a do nothing */
+ inet_accept, /* ok */
+ inet6_getname,
+ inet_poll, /* ok */
+ inet6_ioctl, /* must change */
+ inet_listen, /* ok */
+ inet_shutdown, /* ok */
+ inet_setsockopt, /* ok */
+ inet_getsockopt, /* ok */
+ sock_no_fcntl, /* ok */
+ inet_sendmsg, /* ok */
+ inet_recvmsg /* ok */
+};
-static struct proto_ops inet6_proto_ops = {
+struct proto_ops inet6_dgram_ops = {
AF_INET6,
- inet6_create,
inet6_dup,
inet6_release,
inet6_bind,
- inet_connect, /* ok */
+ inet_dgram_connect, /* ok */
inet6_socketpair, /* a do nothing */
inet_accept, /* ok */
inet6_getname,
- inet_select, /* ok */
+ datagram_poll, /* ok */
inet6_ioctl, /* must change */
- inet_listen, /* ok */
+ sock_no_listen, /* ok */
inet_shutdown, /* ok */
inet_setsockopt, /* ok */
inet_getsockopt, /* ok */
- inet_fcntl, /* ok */
+ sock_no_fcntl, /* ok */
inet_sendmsg, /* ok */
inet_recvmsg /* ok */
};
+struct net_proto_family inet6_family_ops = {
+ AF_INET6,
+ inet6_create
+};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_raw6 = {
+ PROC_NET_RAW6, 4, "raw6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ raw6_get_info
+};
+static struct proc_dir_entry proc_net_tcp6 = {
+ PROC_NET_TCP6, 4, "tcp6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ tcp6_get_info
+};
+static struct proc_dir_entry proc_net_udp6 = {
+ PROC_NET_RAW6, 4, "udp6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ udp6_get_info
+};
+static struct proc_dir_entry proc_net_sockstat6 = {
+ PROC_NET_SOCKSTAT6, 9, "sockstat6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ afinet6_get_info
+};
+#endif /* CONFIG_PROC_FS */
+
#ifdef MODULE
int init_module(void)
#else
void inet6_proto_init(struct net_proto *pro)
#endif
{
- int i;
+ struct sk_buff *dummy_skb;
- printk(KERN_INFO "IPv6 v0.1 for NET3.037\n");
+ printk(KERN_INFO "IPv6 v0.2 for NET3.037\n");
- sock_register(inet6_proto_ops.family, &inet6_proto_ops);
-
- for(i = 0; i < SOCK_ARRAY_SIZE; i++)
+ if (sizeof(struct ipv6_options) > sizeof(dummy_skb->cb))
{
- rawv6_sock_array[i] = NULL;
- }
+ printk(KERN_CRIT "inet6_proto_init: size fault\n");
+#ifdef MODULE
+ return -EINVAL;
+#else
+ return;
+#endif
+ }
+ (void) sock_register(&inet6_family_ops);
+
/*
* ipngwg API draft makes clear that the correct semantics
* for TCP and UDP is to consider one TCP and UDP instance
* in a host availiable by both INET and INET6 APIs and
- * hable to communicate via both network protocols.
+ * able to communicate via both network protocols.
*/
-
- tcpv6_prot.inuse = 0;
- tcpv6_prot.highestinuse = 0;
- tcpv6_prot.sock_array = tcp_sock_array;
- udpv6_prot.inuse = 0;
- udpv6_prot.highestinuse = 0;
- udpv6_prot.sock_array = udp_sock_array;
-
- rawv6_prot.inuse = 0;
- rawv6_prot.highestinuse = 0;
- rawv6_prot.sock_array = rawv6_sock_array;
-
ipv6_init();
- icmpv6_init(&inet6_proto_ops);
- ndisc_init(&inet6_proto_ops);
+ icmpv6_init(&inet6_family_ops);
addrconf_init();
@@ -856,6 +500,14 @@ void inet6_proto_init(struct net_proto *pro)
tcpv6_init();
+ /* Create /proc/foo6 entries. */
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_raw6);
+ proc_net_register(&proc_net_tcp6);
+ proc_net_register(&proc_net_udp6);
+ proc_net_register(&proc_net_sockstat6);
+#endif
+
#ifdef MODULE
return 0;
#endif
@@ -867,6 +519,11 @@ void cleanup_module(void)
sit_cleanup();
ipv6_cleanup();
sock_unregister(AF_INET6);
-}
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(proc_net_raw6.low_ino);
+ proc_net_unregister(proc_net_tcp6.low_ino);
+ proc_net_unregister(proc_net_udp6.low_ino);
+ proc_net_unregister(proc_net_sockstat6.low_ino);
#endif
-
+}
+#endif /* MODULE */
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 03a58e843..a898f6008 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: datagram.c,v 1.3 1996/10/11 16:03:05 roque Exp $
+ * $Id: datagram.c,v 1.10 1997/04/14 05:39:42 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,90 +19,51 @@
#include <linux/sockios.h>
#include <linux/in6.h>
#include <linux/ipv6.h>
+#include <linux/route.h>
#include <net/ipv6.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
#include <net/addrconf.h>
#include <net/transp_v6.h>
-
int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
{
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
- struct ipv6_options *opt = (struct ipv6_options *) skb->proto_priv;
- struct cmsghdr *cmsg = msg->msg_control;
- int len = msg->msg_controllen;
-
- msg->msg_controllen = 0;
+ struct ipv6_options *opt = (struct ipv6_options *) skb->cb;
- if (np->rxinfo && (len >= sizeof(struct cmsghdr) +
- sizeof(struct in6_pktinfo)))
- {
- struct in6_pktinfo *src_info;
- struct inet6_dev *in6_dev;
-
- cmsg->cmsg_len = (sizeof(struct cmsghdr) +
- sizeof(struct in6_pktinfo));
- cmsg->cmsg_level = SOL_IPV6;
- cmsg->cmsg_type = IPV6_RXINFO;
-
- src_info = (struct in6_pktinfo *) cmsg->cmsg_data;
- in6_dev = ipv6_get_idev(skb->dev);
-
- if (in6_dev == NULL)
- {
- printk(KERN_DEBUG "recv_ctl: unknown device\n");
- return -ENODEV;
- }
+ if (np->rxinfo) {
+ struct in6_pktinfo src_info;
- src_info->ipi6_ifindex = in6_dev->if_index;
- ipv6_addr_copy(&src_info->ipi6_addr,
- &skb->ipv6_hdr->daddr);
+ src_info.ipi6_ifindex = skb->dev->ifindex;
+ ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
+ put_cmsg(msg, SOL_IPV6, IPV6_RXINFO, sizeof(src_info), &src_info);
+ }
- len -= cmsg->cmsg_len;
- msg->msg_controllen += cmsg->cmsg_len;
- cmsg = (struct cmsghdr *)((u8*) cmsg + cmsg->cmsg_len);
+ if (np->rxhlim) {
+ int hlim = skb->nh.ipv6h->hop_limit;
+ put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
}
- if (opt->srcrt)
- {
+ if (opt->srcrt) {
int hdrlen = sizeof(struct rt0_hdr) + (opt->srcrt->hdrlen << 3);
- if (len >= sizeof(struct cmsghdr) + hdrlen)
- {
- struct rt0_hdr *rt0;
-
- cmsg->cmsg_len = sizeof(struct cmsghdr) + hdrlen;
- cmsg->cmsg_level = SOL_IPV6;
- cmsg->cmsg_type = IPV6_RXINFO;
-
- rt0 = (struct rt0_hdr *) cmsg->cmsg_data;
- memcpy(rt0, opt->srcrt, hdrlen);
-
- len -= cmsg->cmsg_len;
- msg->msg_controllen += cmsg->cmsg_len;
- cmsg = (struct cmsghdr *)((u8*) cmsg + cmsg->cmsg_len);
- }
+ put_cmsg(msg, SOL_IPV6, IPV6_RXSRCRT, hdrlen, opt->srcrt);
}
return 0;
}
-
int datagram_send_ctl(struct msghdr *msg, struct device **src_dev,
- struct in6_addr **src_addr, struct ipv6_options *opt)
+ struct in6_addr **src_addr, struct ipv6_options *opt,
+ int *hlimit)
{
- struct inet6_dev *in6_dev = NULL;
struct in6_pktinfo *src_info;
struct cmsghdr *cmsg;
struct ipv6_rt_hdr *rthdr;
int len;
- int err = -EINVAL;
+ int err = 0;
- for (cmsg = msg->msg_control; cmsg; cmsg = cmsg_nxthdr(msg, cmsg))
- {
- if (cmsg->cmsg_level != SOL_IPV6)
- {
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_IPV6) {
printk(KERN_DEBUG "cmsg_level %d\n", cmsg->cmsg_level);
continue;
}
@@ -111,50 +72,43 @@ int datagram_send_ctl(struct msghdr *msg, struct device **src_dev,
case IPV6_TXINFO:
if (cmsg->cmsg_len < (sizeof(struct cmsghdr) +
- sizeof(struct in6_pktinfo)))
- {
+ sizeof(struct in6_pktinfo))) {
+ err = -EINVAL;
goto exit_f;
}
src_info = (struct in6_pktinfo *) cmsg->cmsg_data;
- if (src_info->ipi6_ifindex)
- {
- in6_dev = ipv6_dev_by_index(src_info->ipi6_ifindex);
- if (in6_dev == NULL)
- {
- goto exit_f;
- }
+ if (src_info->ipi6_ifindex) {
+ int index = src_info->ipi6_ifindex;
- *src_dev = in6_dev->dev;
+ *src_dev = dev_get_by_index(index);
}
- if (!ipv6_addr_any(&src_info->ipi6_addr))
- {
+ if (!ipv6_addr_any(&src_info->ipi6_addr)) {
struct inet6_ifaddr *ifp;
ifp = ipv6_chk_addr(&src_info->ipi6_addr);
- if ( ifp == NULL)
- {
+ if (ifp == NULL) {
+ err = -EINVAL;
goto exit_f;
}
*src_addr = &src_info->ipi6_addr;
- err = 0;
}
break;
- case SCM_SRCRT:
+ case IPV6_RXSRCRT:
len = cmsg->cmsg_len;
len -= sizeof(struct cmsghdr);
/* validate option length */
- if (len < sizeof(struct ipv6_rt_hdr))
- {
+ if (len < sizeof(struct ipv6_rt_hdr)) {
+ err = -EINVAL;
goto exit_f;
}
@@ -163,34 +117,48 @@ int datagram_send_ctl(struct msghdr *msg, struct device **src_dev,
/*
* TYPE 0
*/
- if (rthdr->type)
- {
+ if (rthdr->type) {
+ err = -EINVAL;
goto exit_f;
}
- if (((rthdr->hdrlen + 1) << 3) < len)
- {
+ if (((rthdr->hdrlen + 1) << 3) < len) {
+ err = -EINVAL;
goto exit_f;
}
/* segments left must also match */
- if ((rthdr->hdrlen >> 1) != rthdr->segments_left)
- {
+ if ((rthdr->hdrlen >> 1) != rthdr->segments_left) {
+ err = -EINVAL;
goto exit_f;
}
opt->opt_nflen += ((rthdr->hdrlen + 1) << 3);
opt->srcrt = rthdr;
- err = 0;
break;
+
+ case IPV6_HOPLIMIT:
+
+ len = cmsg->cmsg_len;
+ len -= sizeof(struct cmsghdr);
+
+ if (len < sizeof(int)) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ *hlimit = *((int *) cmsg->cmsg_data);
+ break;
+
default:
printk(KERN_DEBUG "invalid cmsg type: %d\n",
cmsg->cmsg_type);
+ err = -EINVAL;
break;
- }
+ };
}
- exit_f:
+exit_f:
return err;
}
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 6c5c8ab7e..b2380fb78 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: exthdrs.c,v 1.7 1996/09/12 18:44:18 roque Exp $
+ * $Id: exthdrs.c,v 1.4 1997/03/18 18:24:29 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -31,13 +31,13 @@
#include <net/transp_v6.h>
#include <net/rawv6.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
/*
* inbound
*/
-
+#if 0
int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
__u8 *nhptr, struct ipv6_options *opt)
{
@@ -53,11 +53,10 @@ int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
struct ipv6_rt_hdr *hdr = (struct ipv6_rt_hdr *) skb->h.raw;
struct rt0_hdr *rthdr;
- if (hdr->segments_left == 0)
- {
+ if (hdr->segments_left == 0) {
struct ipv6_options *opt;
- opt = (struct ipv6_options *) skb->proto_priv;
+ opt = (struct ipv6_options *) skb->cb;
opt->srcrt = hdr;
skb->h.raw += (hdr->hdrlen + 1) << 3;
@@ -65,13 +64,12 @@ int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
}
if (hdr->type != IPV6_SRCRT_TYPE_0 || hdr->hdrlen & 0x01 ||
- hdr->hdrlen > 46)
- {
+ hdr->hdrlen > 46) {
/*
* Discard
*/
- pos = (__u8 *) hdr - (__u8 *) skb->ipv6_hdr + 2;
+ pos = (__u8 *) hdr - (__u8 *) skb->nh.ipv6h + 2;
if (hdr->type)
pos += 2;
@@ -90,9 +88,8 @@ int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
n = hdr->hdrlen >> 1;
- if (hdr->segments_left > n)
- {
- pos = (__u8 *) hdr - (__u8 *) skb->ipv6_hdr + 2;
+ if (hdr->segments_left > n) {
+ pos = (__u8 *) hdr - (__u8 *) skb->nh.ipv6h + 2;
pos += 3;
@@ -109,15 +106,14 @@ int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
addr_type = ipv6_addr_type(addr);
- if (addr_type == IPV6_ADDR_MULTICAST)
- {
+ if (addr_type == IPV6_ADDR_MULTICAST) {
kfree_skb(skb, FREE_READ);
return 0;
}
ipv6_addr_copy(&daddr, addr);
- ipv6_addr_copy(addr, &skb->ipv6_hdr->daddr);
- ipv6_addr_copy(&skb->ipv6_hdr->daddr, &daddr);
+ ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);
+ ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);
/*
* Check Strick Source Route
@@ -126,9 +122,7 @@ int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
bit_map = ntohl(rthdr->bitmap);
if ((bit_map & (1 << i)) == IPV6_SRCRT_STRICT)
- {
strict = 1;
- }
ipv6_forward(skb, dev, (strict ? IP6_FW_STRICT : 0) | IP6_FW_SRCRT);
@@ -154,10 +148,8 @@ int ipv6opt_bld_rthdr(struct sk_buff *skb, struct ipv6_options *opt,
hops = ihdr->rt_hdr.hdrlen >> 1;
if (hops > 1)
- {
memcpy(phdr->addr, ihdr->addr + 1,
(hops - 1) * sizeof(struct in6_addr));
- }
ipv6_addr_copy(phdr->addr + (hops - 1), addr);
@@ -165,9 +157,4 @@ int ipv6opt_bld_rthdr(struct sk_buff *skb, struct ipv6_options *opt,
return NEXTHDR_ROUTING;
}
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */
+#endif
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index f959189c6..37bd7f814 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -5,6 +5,8 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
+ * $Id: icmp.c,v 1.8 1997/03/18 18:24:30 davem Exp $
+ *
* Based on net/ipv4/icmp.c
*
* RFC 1885
@@ -28,17 +30,9 @@
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
-#include <linux/major.h>
#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
#include <linux/skbuff.h>
#include <linux/inet.h>
@@ -49,15 +43,13 @@
#include <net/sock.h>
#include <net/ipv6.h>
+#include <net/checksum.h>
#include <net/protocol.h>
-#include <net/route.h>
-#include <net/ndisc.h>
#include <net/raw.h>
-#include <net/inet_common.h>
+#include <net/rawv6.h>
#include <net/transp_v6.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
-#include <net/rawv6.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -66,7 +58,8 @@
* ICMP socket for flow control.
*/
-static struct socket icmpv6_socket;
+struct inode icmpv6_inode;
+struct socket *icmpv6_socket=&icmpv6_inode.u.socket_i;
int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
struct in6_addr *saddr, struct in6_addr *daddr,
@@ -87,7 +80,7 @@ static struct inet6_protocol icmpv6_protocol =
struct icmpv6_msg {
- struct icmpv6hdr icmph;
+ struct icmp6hdr icmph;
__u8 *data;
struct in6_addr *daddr;
int len;
@@ -105,7 +98,7 @@ static int icmpv6_getfrag(const void *data, struct in6_addr *saddr,
char *buff, unsigned int offset, unsigned int len)
{
struct icmpv6_msg *msg = (struct icmpv6_msg *) data;
- struct icmpv6hdr *icmph;
+ struct icmp6hdr *icmph;
__u32 csum;
/*
@@ -114,26 +107,25 @@ static int icmpv6_getfrag(const void *data, struct in6_addr *saddr,
* on an echo reply. (those are the rules on RFC 1883)
*/
- if (offset)
- {
+ if (offset) {
csum = csum_partial_copy((void *) msg->data +
- offset - sizeof(struct icmpv6hdr),
+ offset - sizeof(struct icmp6hdr),
buff, len, msg->csum);
msg->csum = csum;
return 0;
}
csum = csum_partial_copy((void *) &msg->icmph, buff,
- sizeof(struct icmpv6hdr), msg->csum);
+ sizeof(struct icmp6hdr), msg->csum);
csum = csum_partial_copy((void *) msg->data,
- buff + sizeof(struct icmpv6hdr),
- len - sizeof(struct icmpv6hdr), csum);
+ buff + sizeof(struct icmp6hdr),
+ len - sizeof(struct icmp6hdr), csum);
- icmph = (struct icmpv6hdr *) buff;
+ icmph = (struct icmp6hdr *) buff;
- icmph->checksum = csum_ipv6_magic(saddr, msg->daddr, msg->len,
- IPPROTO_ICMPV6, csum);
+ icmph->icmp6_cksum = csum_ipv6_magic(saddr, msg->daddr, msg->len,
+ IPPROTO_ICMPV6, csum);
return 0;
}
@@ -145,7 +137,7 @@ static int icmpv6_getfrag(const void *data, struct in6_addr *saddr,
*/
static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
{
- char *buff = (char *) skb->ipv6_hdr;
+ char *buff = skb->nh.raw;
return ( ( *(buff + offset) & 0xC0 ) == 0x80 );
}
@@ -157,11 +149,12 @@ static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
struct device *dev)
{
- struct ipv6hdr *hdr = skb->ipv6_hdr;
- struct sock *sk = (struct sock *) icmpv6_socket.data;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct sock *sk = icmpv6_socket->sk;
struct in6_addr *saddr = NULL;
struct device *src_dev = NULL;
struct icmpv6_msg msg;
+ struct flowi fl;
int addr_type = 0;
int optlen;
int len;
@@ -170,9 +163,8 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
* sanity check pointer in case of parameter problem
*/
- if (type == ICMPV6_PARAMETER_PROB &&
- (info > (skb->tail - ((unsigned char *) hdr))))
- {
+ if (type == ICMPV6_PARAMPROB &&
+ (info > (skb->tail - ((unsigned char *) hdr)))) {
printk(KERN_DEBUG "icmpv6_send: bug! pointer > skb\n");
return;
}
@@ -187,23 +179,18 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
addr_type = ipv6_addr_type(&hdr->daddr);
if (ipv6_chk_addr(&hdr->daddr))
- {
saddr = &hdr->daddr;
- }
/*
* Dest addr check
*/
- if ((addr_type & IPV6_ADDR_MULTICAST || skb->pkt_type != PACKET_HOST))
- {
+ if ((addr_type & IPV6_ADDR_MULTICAST || skb->pkt_type != PACKET_HOST)) {
if (type != ICMPV6_PKT_TOOBIG &&
- !(type == ICMPV6_PARAMETER_PROB &&
+ !(type == ICMPV6_PARAMPROB &&
code == ICMPV6_UNK_OPTION &&
(opt_unrec(skb, info))))
- {
return;
- }
saddr = NULL;
}
@@ -215,16 +202,13 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
*/
if (addr_type & IPV6_ADDR_LINKLOCAL)
- {
src_dev = skb->dev;
- }
/*
* Must not send if we know that source is Anycast also.
* for now we don't know that.
*/
- if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST))
- {
+ if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
printk(KERN_DEBUG "icmpv6_send: addr_any/mcast source\n");
return;
}
@@ -234,12 +218,12 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
* getfrag_t callback.
*/
- msg.icmph.type = type;
- msg.icmph.code = code;
- msg.icmph.checksum = 0;
+ msg.icmph.icmp6_type = type;
+ msg.icmph.icmp6_code = code;
+ msg.icmph.icmp6_cksum = 0;
msg.icmph.icmp6_pointer = htonl(info);
- msg.data = (__u8 *) skb->ipv6_hdr;
+ msg.data = skb->nh.raw;
msg.csum = 0;
msg.daddr = &hdr->saddr;
/*
@@ -251,31 +235,37 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
optlen = 0;
len = min(skb->tail - ((unsigned char *) hdr),
- 576 - sizeof(struct ipv6hdr) - sizeof(struct icmpv6hdr)
+ 576 - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr)
- optlen);
- if (len < 0)
- {
+ if (len < 0) {
printk(KERN_DEBUG "icmp: len problem\n");
return;
}
- len += sizeof(struct icmpv6hdr);
+ len += sizeof(struct icmp6hdr);
msg.len = len;
+ fl.proto = IPPROTO_ICMPV6;
+ fl.nl_u.ip6_u.daddr = &hdr->saddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.dev = src_dev;
+ fl.uli_u.icmpt.type = type;
+ fl.uli_u.icmpt.code = code;
- ipv6_build_xmit(sk, icmpv6_getfrag, &msg, &hdr->saddr, len,
- saddr, src_dev, NULL, IPPROTO_ICMPV6, 1);
+ ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
+ MSG_DONTWAIT);
}
static void icmpv6_echo_reply(struct sk_buff *skb)
{
- struct sock *sk = (struct sock *) icmpv6_socket.data;
- struct ipv6hdr *hdr = skb->ipv6_hdr;
- struct icmpv6hdr *icmph = (struct icmpv6hdr *) skb->h.raw;
+ struct sock *sk = icmpv6_socket->sk;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
struct in6_addr *saddr;
struct icmpv6_msg msg;
+ struct flowi fl;
unsigned char *data;
int len;
@@ -287,11 +277,11 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
saddr = NULL;
len = skb->tail - data;
- len += sizeof(struct icmpv6hdr);
+ len += sizeof(struct icmp6hdr);
- msg.icmph.type = ICMPV6_ECHO_REPLY;
- msg.icmph.code = 0;
- msg.icmph.checksum = 0;
+ msg.icmph.icmp6_type = ICMPV6_ECHO_REPLY;
+ msg.icmph.icmp6_code = 0;
+ msg.icmph.icmp6_cksum = 0;
msg.icmph.icmp6_identifier = icmph->icmp6_identifier;
msg.icmph.icmp6_sequence = icmph->icmp6_sequence;
@@ -299,9 +289,16 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
msg.csum = 0;
msg.len = len;
msg.daddr = &hdr->saddr;
-
- ipv6_build_xmit(sk, icmpv6_getfrag, &msg, &hdr->saddr, len, saddr,
- skb->dev, NULL, IPPROTO_ICMPV6, 1);
+
+ fl.proto = IPPROTO_ICMPV6;
+ fl.nl_u.ip6_u.daddr = &hdr->saddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.dev = skb->dev;
+ fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY;
+ fl.uli_u.icmpt.code = 0;
+
+ ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
+ MSG_DONTWAIT);
}
static __inline__ int ipv6_ext_hdr(u8 nexthdr)
@@ -338,56 +335,53 @@ static void icmpv6_notify(int type, int code, unsigned char *buff, int len,
pbuff = (char *) (hdr + 1);
len -= sizeof(struct ipv6hdr);
- while (ipv6_ext_hdr(nexthdr))
- {
+ while (ipv6_ext_hdr(nexthdr)) {
int hdrlen;
if (nexthdr == NEXTHDR_NONE)
return;
nexthdr = *pbuff;
+
+ /* Header length is size in 8-octet units, not
+ * including the first 8 octets.
+ */
hdrlen = *(pbuff+1);
+ hdrlen = (hdrlen + 1) << 3;
- if (((hdrlen + 1) << 3) > len)
+ if (hdrlen > len)
return;
+ /* Now this is right. */
pbuff += hdrlen;
len -= hdrlen;
}
- hash = nexthdr & (MAX_INET_PROTOS -1);
+ hash = nexthdr & (MAX_INET_PROTOS - 1);
for (ipprot = (struct inet6_protocol *) inet6_protos[hash];
ipprot != NULL;
- ipprot=(struct inet6_protocol *)ipprot->next)
- {
+ ipprot=(struct inet6_protocol *)ipprot->next) {
if (ipprot->protocol != nexthdr)
continue;
if (ipprot->err_handler)
- {
ipprot->err_handler(type, code, pbuff, info,
saddr, daddr, ipprot);
- }
return;
}
/* delivery to upper layer protocols failed. try raw sockets */
- sk = rawv6_prot.sock_array[hash];
+ sk = raw_v6_htable[hash];
if (sk == NULL)
- {
return;
- }
- while ((sk = inet6_get_sock_raw(sk, nexthdr, daddr, saddr)))
- {
+ while((sk = raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
rawv6_err(sk, type, code, pbuff, saddr, daddr);
sk = sk->next;
}
-
- return;
}
/*
@@ -400,32 +394,29 @@ int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
int redo, struct inet6_protocol *protocol)
{
struct ipv6hdr *orig_hdr;
- struct icmpv6hdr *hdr = (struct icmpv6hdr *) skb->h.raw;
+ struct icmp6hdr *hdr = (struct icmp6hdr *) skb->h.raw;
int ulen;
- /* perform checksum */
-
-
+ /* Perform checksum. */
switch (skb->ip_summed) {
case CHECKSUM_NONE:
skb->csum = csum_partial((char *)hdr, len, 0);
case CHECKSUM_HW:
if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6,
- skb->csum))
- {
+ skb->csum)) {
printk(KERN_DEBUG "icmpv6 checksum failed\n");
goto discard_it;
}
default:
/* CHECKSUM_UNNECESSARY */
- }
+ };
/*
* length of original packet carried in skb
*/
ulen = skb->tail - (unsigned char *) (hdr + 1);
- switch (hdr->type) {
+ switch (hdr->icmp6_type) {
case ICMPV6_ECHO_REQUEST:
icmpv6_echo_reply(skb);
@@ -438,20 +429,19 @@ int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
case ICMPV6_PKT_TOOBIG:
orig_hdr = (struct ipv6hdr *) (hdr + 1);
if (ulen >= sizeof(struct ipv6hdr))
- {
- rt6_handle_pmtu(&orig_hdr->daddr,
- ntohl(hdr->icmp6_mtu));
- }
+ rt6_pmtu_discovery(&orig_hdr->daddr, dev,
+ ntohl(hdr->icmp6_mtu));
/*
- * Drop through to notify
+ * Drop through to notify
*/
case ICMPV6_DEST_UNREACH:
- case ICMPV6_TIME_EXCEEDED:
- case ICMPV6_PARAMETER_PROB:
+ case ICMPV6_TIME_EXCEED:
+ case ICMPV6_PARAMPROB:
- icmpv6_notify(hdr->type, hdr->code, (char *) (hdr + 1), ulen,
+ icmpv6_notify(hdr->icmp6_type, hdr->icmp6_code,
+ (char *) (hdr + 1), ulen,
saddr, daddr, protocol);
break;
@@ -463,55 +453,67 @@ int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
ndisc_rcv(skb, dev, saddr, daddr, opt, len);
break;
- case ICMPV6_MEMBERSHIP_QUERY:
- case ICMPV6_MEMBERSHIP_REPORT:
- case ICMPV6_MEMBERSHIP_REDUCTION:
- /* forward the packet to the igmp module */
+ case ICMPV6_MGM_QUERY:
+ igmp6_event_query(skb, hdr, len);
+ break;
+
+ case ICMPV6_MGM_REPORT:
+ igmp6_event_report(skb, hdr, len);
+ break;
+
+ case ICMPV6_MGM_REDUCTION:
break;
default:
printk(KERN_DEBUG "icmpv6: msg of unkown type\n");
/* informational */
- if (hdr->type & 0x80)
- {
+ if (hdr->icmp6_type & 0x80)
goto discard_it;
- }
/*
* error of unkown type.
* must pass to upper level
*/
- icmpv6_notify(hdr->type, hdr->code, (char *) (hdr + 1), ulen,
+ icmpv6_notify(hdr->icmp6_type, hdr->icmp6_code,
+ (char *) (hdr + 1), ulen,
saddr, daddr, protocol);
- }
-
- discard_it:
+ };
+discard_it:
kfree_skb(skb, FREE_READ);
return 0;
}
-void icmpv6_init(struct proto_ops *ops)
+void icmpv6_init(struct net_proto_family *ops)
{
struct sock *sk;
int err;
- icmpv6_socket.type=SOCK_RAW;
- icmpv6_socket.ops=ops;
+ icmpv6_inode.i_mode = S_IFSOCK;
+ icmpv6_inode.i_sock = 1;
+ icmpv6_inode.i_uid = 0;
+ icmpv6_inode.i_gid = 0;
- if((err=ops->create(&icmpv6_socket, IPPROTO_ICMPV6))<0)
+ icmpv6_socket->inode = &icmpv6_inode;
+ icmpv6_socket->state = SS_UNCONNECTED;
+ icmpv6_socket->type=SOCK_RAW;
+
+ if((err=ops->create(icmpv6_socket, IPPROTO_ICMPV6))<0)
printk(KERN_DEBUG
- "Failed to create the ICMP control socket.\n");
+ "Failed to create the ICMP6 control socket.\n");
MOD_DEC_USE_COUNT;
- sk = icmpv6_socket.data;
+ sk = icmpv6_socket->sk;
sk->allocation = GFP_ATOMIC;
sk->num = 256; /* Don't receive any data */
inet6_add_protocol(&icmpv6_protocol);
+
+ ndisc_init(ops);
+ igmp6_init(ops);
}
static struct icmp6_err {
@@ -533,8 +535,7 @@ int icmpv6_err_convert(int type, int code, int *err)
switch (type) {
case ICMPV6_DEST_UNREACH:
- if (code <= ICMPV6_PORT_UNREACH)
- {
+ if (code <= ICMPV6_PORT_UNREACH) {
*err = tab_unreach[code].err;
fatal = tab_unreach[code].fatal;
}
@@ -544,7 +545,7 @@ int icmpv6_err_convert(int type, int code, int *err)
*err = EMSGSIZE;
break;
- case ICMPV6_PARAMETER_PROB:
+ case ICMPV6_PARAMPROB:
*err = EPROTO;
fatal = 1;
break;
@@ -552,9 +553,3 @@ int icmpv6_err_convert(int type, int code, int *err)
return fatal;
}
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o icmp.o icmp.c"
- * End:
- */
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
new file mode 100644
index 000000000..25b34465c
--- /dev/null
+++ b/net/ipv6/ip6_fib.c
@@ -0,0 +1,927 @@
+/*
+ * Linux INET6 implementation
+ * Forwarding Information Database
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: ip6_fib.c,v 1.7 1997/04/12 04:32:46 davem Exp $
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+#include <net/netlink.h>
+
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+
+#define RT_DEBUG 2
+
+struct rt6_statistics rt6_stats;
+
+/*
+ * A routing update causes an increase of the serial number on the
+ * afected subtree. This allows for cached routes to be asynchronously
+ * tested when modifications are made to the destination cache as a
+ * result of redirects, path MTU changes, etc.
+ */
+
+static __u32 rt_sernum = 0;
+
+static void fib6_run_gc(unsigned long);
+
+static struct timer_list ip6_fib_timer = {
+ NULL, NULL,
+ 0,
+ 0,
+ fib6_run_gc
+};
+
+/*
+ * Auxiliary address test functions for the radix tree.
+ *
+ * These assume a 32bit processor (although it will work on
+ * 64bit processors)
+ */
+
+/*
+ * compare "prefix length" bits of an address
+ */
+
+static __inline__ int addr_match(void *token1, void *token2, int prefixlen)
+{
+ __u32 *a1 = token1;
+ __u32 *a2 = token2;
+ int pdw;
+ int pbi;
+
+ pdw = prefixlen >> 0x05; /* num of whole __u32 in prefix */
+ pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */
+
+ if (pdw)
+ if (memcmp(a1, a2, pdw << 2))
+ return 0;
+
+ if (pbi) {
+ __u32 w1, w2;
+ __u32 mask;
+
+ w1 = a1[pdw];
+ w2 = a2[pdw];
+
+ mask = htonl((0xffffffff) << (0x20 - pbi));
+
+ if ((w1 ^ w2) & mask)
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * test bit
+ */
+
+static __inline__ int addr_bit_set(void *token, int fn_bit)
+{
+ int dw;
+ __u32 b1;
+ __u32 mask;
+ int bit = fn_bit;
+ __u32 *addr = token;
+
+ dw = bit >> 0x05;
+
+ b1 = addr[dw];
+
+ bit = ~bit;
+ bit &= 0x1f;
+ mask = htonl(1 << bit);
+ return (b1 & mask);
+}
+
+
+
+/*
+ * find the first different bit between two addresses
+ * length of address must be a multiple of 32bits
+ */
+
+static __inline__ int addr_diff(void *token1, void *token2, int addrlen)
+{
+ __u32 *a1 = token1;
+ __u32 *a2 = token2;
+ int i;
+
+ addrlen >>= 2;
+
+ for (i = 0; i < addrlen; i++) {
+ __u32 b1, b2;
+ __u32 xb;
+
+ b1 = a1[i];
+ b2 = a2[i];
+
+ xb = b1 ^ b2;
+
+ if (xb) {
+ int res = 0;
+ int j=31;
+
+ xb = ntohl(xb);
+
+ while (test_bit(j, &xb) == 0) {
+ res++;
+ j--;
+ }
+
+ return (i * 32 + res);
+ }
+ }
+
+ /*
+ * we should *never* get to this point since that
+ * would mean the addrs are equal
+ */
+
+ return -1;
+}
+
+static __inline__ struct fib6_node * node_alloc(void)
+{
+ struct fib6_node *fn;
+
+ if ((fn = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC))) {
+ memset(fn, 0, sizeof(struct fib6_node));
+ rt6_stats.fib_nodes++;
+ }
+
+ return fn;
+}
+
+static __inline__ void node_free(struct fib6_node * fn)
+{
+ rt6_stats.fib_nodes--;
+ kfree(fn);
+}
+
+/*
+ * Routing Table
+ *
+ * return the apropriate node for a routing tree "add" operation
+ * by either creating and inserting or by returning an existing
+ * node.
+ */
+
+static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
+ int addrlen, int plen,
+ unsigned long offset,
+ struct rt6_info *rt)
+
+{
+ struct fib6_node *fn;
+ struct fib6_node *pn = NULL;
+ struct fib6_node *in;
+ struct fib6_node *ln;
+ struct rt6key *key;
+ __u32 bit;
+ __u32 dir = 0;
+ __u32 sernum = ++rt_sernum;
+
+ /* insert node in tree */
+
+ fn = root;
+
+ if (plen == 0)
+ return fn;
+
+ for (;;) {
+ if (fn == NULL) {
+ ln = node_alloc();
+
+ if (ln == NULL)
+ return NULL;
+ ln->fn_bit = plen;
+
+ ln->parent = pn;
+ ln->fn_sernum = sernum;
+ rt->rt6i_node = ln;
+
+ if (dir)
+ pn->right = ln;
+ else
+ pn->left = ln;
+
+ return ln;
+ }
+
+ key = (struct rt6key *)((u8 *)fn->leaf + offset);
+
+ if (addr_match(&key->addr, addr, fn->fn_bit)) {
+ if (plen == fn->fn_bit) {
+ /* clean up an intermediate node */
+ if ((fn->fn_flags & RTN_RTINFO) == 0) {
+ rt6_release(fn->leaf);
+ fn->leaf = NULL;
+ }
+
+ fn->fn_sernum = sernum;
+
+ return fn;
+ }
+
+ if (plen > fn->fn_bit) {
+ /* Walk down on tree. */
+ fn->fn_sernum = sernum;
+ dir = addr_bit_set(addr, fn->fn_bit);
+ pn = fn;
+ fn = dir ? fn->right: fn->left;
+
+ continue;
+ }
+ }
+
+ /*
+ * split since we don't have a common prefix anymore or
+ * we have a less significant route.
+ * we've to insert an intermediate node on the list
+ * this new node will point to the one we need to create
+ * and the current
+ */
+
+ pn = fn->parent;
+
+ /* find 1st bit in difference between the 2 addrs */
+ bit = addr_diff(addr, &key->addr, addrlen);
+
+
+ /*
+ * (intermediate)
+ * / \
+ * (new leaf node) (old node)
+ */
+ if (plen > bit) {
+ in = node_alloc();
+
+ if (in == NULL)
+ return NULL;
+
+ /*
+ * new intermediate node.
+ * RTN_RTINFO will
+ * be off since that an address that chooses one of
+ * the branches would not match less specific routes
+ * int the other branch
+ */
+
+ in->fn_bit = bit;
+
+ in->parent = pn;
+ in->leaf = rt;
+
+ in->fn_sernum = sernum;
+ atomic_inc(&rt->rt6i_ref);
+
+ /* leaf node */
+ ln = node_alloc();
+
+ if (ln == NULL) {
+ node_free(in);
+ return NULL;
+ }
+
+ /* update parent pointer */
+ if (dir)
+ pn->right = in;
+ else
+ pn->left = in;
+
+ ln->fn_bit = plen;
+
+ ln->parent = in;
+ fn->parent = in;
+
+ ln->fn_sernum = sernum;
+
+ if (addr_bit_set(addr, bit)) {
+ in->right = ln;
+ in->left = fn;
+ } else {
+ in->left = ln;
+ in->right = fn;
+ }
+
+ return ln;
+ }
+
+ /*
+ * (new leaf node)
+ * / \
+ * (old node) NULL
+ */
+
+ ln = node_alloc();
+
+ if (ln == NULL)
+ return NULL;
+
+ ln->fn_bit = plen;
+
+ ln->parent = pn;
+
+ ln->fn_sernum = sernum;
+
+ if (dir)
+ pn->right = ln;
+ else
+ pn->left = ln;
+
+
+ if (addr_bit_set(&key->addr, plen))
+ ln->right = fn;
+ else
+ ln->left = fn;
+
+ fn->parent = ln;
+
+ return ln;
+ }
+
+ return NULL;
+}
+
+/*
+ * Insert routing information in a node.
+ */
+
+static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt)
+{
+ struct rt6_info *iter = NULL;
+ struct rt6_info **ins;
+
+ rt->rt6i_node = fn;
+ ins = &fn->leaf;
+
+ for (iter = fn->leaf; iter; iter=iter->u.next) {
+ /*
+ * Search for duplicates
+ */
+
+ if (iter->rt6i_metric == rt->rt6i_metric) {
+ /*
+ * Same priority level
+ */
+
+ if ((iter->rt6i_dev == rt->rt6i_dev) &&
+ (iter->rt6i_flowr == rt->rt6i_flowr) &&
+ (ipv6_addr_cmp(&iter->rt6i_gateway,
+ &rt->rt6i_gateway) == 0))
+ return -EEXIST;
+ }
+
+ if (iter->rt6i_metric > rt->rt6i_metric)
+ break;
+
+ ins = &iter->u.next;
+ }
+
+ /*
+ * insert node
+ */
+
+ *ins = rt;
+ rt->u.next = iter;
+ atomic_inc(&rt->rt6i_ref);
+ rt6_stats.fib_rt_entries++;
+
+ if ((fn->fn_flags & RTN_RTINFO) == 0) {
+ rt6_stats.fib_route_nodes++;
+ fn->fn_flags |= RTN_RTINFO;
+ }
+
+ return 0;
+}
+
+static __inline__ void fib6_start_gc(struct rt6_info *rt)
+{
+ if ((ip6_fib_timer.expires == 0) &&
+ (rt->rt6i_flags & (RTF_ADDRCONF | RTF_CACHE))) {
+ ip6_fib_timer.expires = jiffies + ipv6_config.rt_gc_period;
+ add_timer(&ip6_fib_timer);
+ }
+}
+
+/*
+ * Add routing information to the routing tree.
+ * <destination addr>/<source addr>
+ * with source addr info in sub-trees
+ */
+
+int fib6_add(struct fib6_node *root, struct rt6_info *rt)
+{
+ struct fib6_node *fn;
+ int err = -ENOMEM;
+ unsigned long offset;
+
+ offset = (u8*) &rt->rt6i_dst - (u8*) rt;
+ fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
+ rt->rt6i_dst.plen, offset, rt);
+
+ if (fn == NULL) {
+#if RT_DEBUG >= 2
+ printk(KERN_DEBUG "fib6_add: fn == NULL\n");
+#endif
+ goto out;
+ }
+
+ if (rt->rt6i_src.plen) {
+ struct fib6_node *sn;
+
+#if RT_DEBUG >= 2
+ printk(KERN_DEBUG "fib6_add: src.len > 0\n");
+#endif
+
+ if (fn->subtree == NULL) {
+ struct fib6_node *sfn;
+
+ if (fn->leaf == NULL) {
+ fn->leaf = rt;
+ atomic_inc(&rt->rt6i_ref);
+ }
+
+ sfn = node_alloc();
+
+ if (sfn == NULL)
+ goto out;
+
+ sfn->parent = fn;
+ sfn->leaf = &ip6_null_entry;
+ sfn->fn_flags = RTN_ROOT;
+ sfn->fn_sernum = ++rt_sernum;
+
+ fn->subtree = sfn;
+ }
+
+ offset = (u8*) &rt->rt6i_src - (u8*) rt;
+
+ sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
+ sizeof(struct in6_addr), rt->rt6i_src.plen,
+ offset, rt);
+
+ if (sn == NULL)
+ goto out;
+
+ fn = sn;
+ }
+
+ err = fib6_add_rt2node(fn, rt);
+
+ if (err == 0)
+ fib6_start_gc(rt);
+out:
+ return err;
+}
+
+/*
+ * Routing tree lookup
+ *
+ */
+
+struct lookup_args {
+ unsigned long offset; /* key offset on rt6_info */
+ struct in6_addr *addr; /* search key */
+};
+
+static struct fib6_node * fib6_lookup_1(struct fib6_node *root,
+ struct lookup_args *args)
+{
+ struct fib6_node *fn;
+ int dir;
+
+ /*
+ * Descend on a tree
+ */
+
+ fn = root;
+
+ for (;;) {
+ struct fib6_node *next;
+
+ dir = addr_bit_set(args->addr, fn->fn_bit);
+
+ next = dir ? fn->right : fn->left;
+
+ if (next) {
+ fn = next;
+ continue;
+ }
+
+ break;
+ }
+
+ while ((fn->fn_flags & RTN_ROOT) == 0) {
+ if (fn->subtree) {
+ struct fib6_node *st;
+ struct lookup_args *narg;
+
+ narg = args + 1;
+
+ if (narg->addr) {
+ st = fib6_lookup_1(fn->subtree, narg);
+
+ if (!(st->fn_flags & RTN_ROOT))
+ {
+ return st;
+ }
+ }
+ }
+
+ if (fn->fn_flags & RTN_RTINFO) {
+ struct rt6key *key;
+
+ key = (struct rt6key *) ((u8 *) fn->leaf +
+ args->offset);
+
+ if (addr_match(&key->addr, args->addr, key->plen))
+ return fn;
+ }
+
+ fn = fn->parent;
+ }
+
+ return NULL;
+}
+
+struct fib6_node * fib6_lookup(struct fib6_node *root, struct in6_addr *daddr,
+ struct in6_addr *saddr)
+{
+ struct lookup_args args[2];
+ struct rt6_info *rt = NULL;
+ struct fib6_node *fn;
+
+ args[0].offset = (u8*) &rt->rt6i_dst - (u8*) rt;
+ args[0].addr = daddr;
+
+ args[1].offset = (u8*) &rt->rt6i_src - (u8*) rt;
+ args[1].addr = saddr;
+
+ fn = fib6_lookup_1(root, args);
+
+ if (fn == NULL)
+ fn = root;
+
+ return fn;
+}
+
+/*
+ * Deletion
+ *
+ */
+
+static struct rt6_info * fib6_find_prefix(struct fib6_node *fn)
+{
+ while(fn) {
+ if(fn->left)
+ return fn->left->leaf;
+
+ if(fn->right)
+ return fn->right->leaf;
+
+ fn = fn->subtree;
+ }
+ return NULL;
+}
+
+/*
+ * called to trim the tree of intermediate nodes when possible
+ */
+
+static void fib6_del_2(struct fib6_node *fn)
+{
+ struct rt6_info *rt;
+
+ fn->fn_flags &= ~RTN_RTINFO;
+ rt6_stats.fib_route_nodes--;
+
+ if (fn->fn_flags & RTN_TL_ROOT)
+ return;
+
+ do {
+ struct fib6_node *pn, *child;
+ int children = 0;
+
+ child = NULL;
+
+ if (fn->left) {
+ children++;
+ child = fn->left;
+ }
+
+ if (fn->right) {
+ children++;
+ child = fn->right;
+ }
+
+ if (children > 1 || (fn->fn_flags & RTN_RTINFO))
+ break;
+
+ if (fn->subtree)
+ goto stree_node;
+
+ pn = fn->parent;
+
+ if ((fn->fn_flags & RTN_ROOT) == 0) {
+ if (pn->left == fn)
+ pn->left = child;
+ else
+ pn->right = child;
+
+ if (child)
+ child->parent = pn;
+
+ if (fn->leaf)
+ rt6_release(fn->leaf);
+ } else {
+ if (children)
+ break;
+
+ pn->subtree = NULL;
+ }
+
+ node_free(fn);
+ fn = pn;
+
+ } while (!(fn->fn_flags & RTN_TL_ROOT));
+
+ return;
+
+stree_node:
+
+ rt6_release(fn->leaf);
+ rt = fib6_find_prefix(fn);
+
+ if (rt == NULL)
+ panic("fib6_del_2: inconsistent tree\n");
+
+ atomic_inc(&rt->rt6i_ref);
+ fn->leaf = rt;
+}
+
+static struct fib6_node * fib6_del_1(struct rt6_info *rt)
+{
+ struct fib6_node *fn;
+
+ fn = rt->rt6i_node;
+
+ if (fn) {
+ struct rt6_info **back;
+ struct rt6_info *lf;
+
+ back = &fn->leaf;
+
+ for(lf = fn->leaf; lf; lf=lf->u.next) {
+ if (rt == lf) {
+ /*
+ * Delete this entry.
+ */
+
+ *back = lf->u.next;
+ rt6_release(lf);
+ return fn;
+ }
+ back = &lf->u.next;
+ }
+ }
+
+ return NULL;
+}
+
+int fib6_del(struct rt6_info *rt)
+{
+ struct fib6_node *fn;
+
+ fn = fib6_del_1(rt);
+
+ if (fn == NULL)
+ return -ENOENT;
+
+ if (fn->leaf == NULL)
+ fib6_del_2(fn);
+
+ return 0;
+}
+
+/*
+ * Tree transversal function
+ *
+ */
+
+void fib6_walk_tree(struct fib6_node *root, f_pnode func, void *arg,
+ int filter)
+{
+ struct fib6_node *fn;
+
+ fn = root;
+
+ do {
+ if (!(fn->fn_flags & RTN_TAG)) {
+ fn->fn_flags |= RTN_TAG;
+
+ if (fn->left) {
+ fn = fn->left;
+ continue;
+ }
+ }
+
+ fn->fn_flags &= ~RTN_TAG;
+
+ if (fn->right) {
+ fn = fn->right;
+ continue;
+ }
+
+ do {
+ struct fib6_node *node;
+
+ if (fn->fn_flags & RTN_ROOT)
+ break;
+ node = fn;
+ fn = fn->parent;
+
+ if (!(node->fn_flags & RTN_TAG)) {
+ if (node->subtree) {
+ fib6_walk_tree(node->subtree, func,
+ arg, filter);
+ }
+
+ if (!filter ||
+ (node->fn_flags & RTN_RTINFO))
+ (*func)(node, arg);
+ }
+
+ } while (!(fn->fn_flags & RTN_TAG));
+
+ } while (!(fn->fn_flags & RTN_ROOT) || (fn->fn_flags & RTN_TAG));
+}
+
+/*
+ * Garbage collection
+ */
+
+static int fib6_gc_node(struct fib6_node *fn, int timeout)
+{
+ struct rt6_info *rt, **back;
+ int more = 0;
+ unsigned long now = jiffies;
+
+ back = &fn->leaf;
+
+ for (rt = fn->leaf; rt;) {
+ if ((rt->rt6i_flags & RTF_CACHE) && atomic_read(&rt->rt6i_use) == 0) {
+ if (now - rt->rt6i_tstamp > timeout) {
+ struct rt6_info *old;
+
+ old = rt;
+
+ rt = rt->u.next;
+
+ *back = rt;
+
+ old->rt6i_node = NULL;
+ rt6_release(old);
+ rt6_stats.fib_rt_entries--;
+ continue;
+ }
+ more++;
+ }
+
+ /*
+ * check addrconf expiration here.
+ */
+ back = &rt->u.next;
+ rt = rt->u.next;
+ }
+
+ return more;
+}
+
+struct fib6_gc_args {
+ unsigned long timeout;
+ int more;
+};
+
+static void fib6_garbage_collect(struct fib6_node *fn, void *p_arg)
+{
+ struct fib6_gc_args * args = (struct fib6_gc_args *) p_arg;
+
+ if (fn->fn_flags & RTN_RTINFO) {
+ int more;
+
+ more = fib6_gc_node(fn, args->timeout);
+
+ if (fn->leaf) {
+ args->more += more;
+ return;
+ }
+
+ rt6_stats.fib_route_nodes--;
+ fn->fn_flags &= ~RTN_RTINFO;
+ }
+
+ /*
+ * tree nodes (with no routing information)
+ */
+
+ if (!fn->subtree && !(fn->fn_flags & RTN_TL_ROOT)) {
+ int children = 0;
+ struct fib6_node *chld = NULL;
+
+ if (fn->left) {
+ children++;
+ chld = fn->left;
+ }
+
+ if (fn->right) {
+ children++;
+ chld = fn->right;
+ }
+
+ if ((fn->fn_flags & RTN_ROOT)) {
+ if (children == 0) {
+ struct fib6_node *pn;
+
+ pn = fn->parent;
+ pn->subtree = NULL;
+
+ node_free(fn);
+ }
+ return;
+ }
+
+ if (children <= 1) {
+ struct fib6_node *pn = fn->parent;
+
+ if (pn->left == fn)
+ pn->left = chld;
+ else
+ pn->right = chld;
+
+ if (chld)
+ chld->parent = pn;
+
+ if (fn->leaf)
+ rt6_release(fn->leaf);
+
+ node_free(fn);
+
+ return;
+ }
+ }
+
+ if (fn->leaf == NULL) {
+ struct rt6_info *nrt;
+
+ nrt = fib6_find_prefix(fn);
+
+ if (nrt == NULL)
+ panic("fib6: inconsistent tree\n");
+
+ atomic_inc(&nrt->rt6i_ref);
+ fn->leaf = nrt;
+ }
+}
+
+static void fib6_run_gc(unsigned long dummy)
+{
+ struct fib6_gc_args arg = {
+ ipv6_config.rt_cache_timeout,
+ 0
+ };
+
+ fib6_walk_tree(&ip6_routing_table, fib6_garbage_collect, &arg, 0);
+
+ if (arg.more) {
+ ip6_fib_timer.expires = jiffies + ipv6_config.rt_gc_period;
+ add_timer(&ip6_fib_timer);
+ } else {
+ ip6_fib_timer.expires = 0;
+ }
+}
diff --git a/net/ipv6/ip6_fw.c b/net/ipv6/ip6_fw.c
new file mode 100644
index 000000000..f6e7f8da4
--- /dev/null
+++ b/net/ipv6/ip6_fw.c
@@ -0,0 +1,378 @@
+/*
+ * IPv6 Firewall
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: ip6_fw.c,v 1.4 1997/03/18 18:24:34 davem Exp $
+ *
+ * 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+#include <linux/udp.h>
+
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/ip6_fw.h>
+#include <net/netlink.h>
+
+static unsigned long ip6_fw_rule_cnt;
+static struct ip6_fw_rule ip6_fw_rule_list = {
+ {0},
+ NULL, NULL,
+ {0},
+ IP6_FW_REJECT
+};
+
+static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args);
+
+struct flow_rule_ops ip6_fw_ops = {
+ ip6_fw_accept
+};
+
+
+static struct rt6_info ip6_fw_null_entry = {
+ {{NULL, 0, 0, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL,
+ ip6_pkt_discard, ip6_pkt_discard, NULL}},
+ NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0UL,
+ 0, &ip6_fw_rule_list, {{{{0}}}, 128}, {{{{0}}}, 128}
+};
+
+static struct fib6_node ip6_fw_fib = {
+ NULL, NULL, NULL, NULL,
+ &ip6_fw_null_entry,
+ 0, RTN_ROOT|RTN_TL_ROOT, 0
+};
+
+static void ip6_rule_add(struct ip6_fw_rule *rl)
+{
+ struct ip6_fw_rule *next;
+
+ start_bh_atomic();
+ ip6_fw_rule_cnt++;
+ next = &ip6_fw_rule_list;
+ rl->next = next;
+ rl->prev = next->prev;
+ rl->prev->next = rl;
+ next->prev = rl;
+ end_bh_atomic();
+}
+
+static void ip6_rule_del(struct ip6_fw_rule *rl)
+{
+ struct ip6_fw_rule *next, *prev;
+
+ start_bh_atomic();
+ ip6_fw_rule_cnt--;
+ next = rl->next;
+ prev = rl->prev;
+ next->prev = prev;
+ prev->next = next;
+ end_bh_atomic();
+}
+
+static __inline__ struct ip6_fw_rule * ip6_fwrule_alloc(void)
+{
+ struct ip6_fw_rule *rl;
+
+ rl = kmalloc(sizeof(struct ip6_fw_rule), GFP_ATOMIC);
+
+ memset(rl, 0, sizeof(struct ip6_fw_rule));
+
+ if (rl)
+ rl->flowr.ops = &ip6_fw_ops;
+
+ return rl;
+}
+
+static __inline__ void ip6_fwrule_free(struct ip6_fw_rule * rl)
+{
+ kfree(rl);
+}
+
+static __inline__ int port_match(int rl_port, int fl_port)
+{
+ int res = 0;
+ if (rl_port == 0 || (rl_port == fl_port))
+ res = 1;
+ return res;
+}
+
+static int ip6_fw_accept_trans(struct ip6_fw_rule *rl,
+ struct fl_acc_args *args)
+{
+ int res = FLOWR_NODECISION;
+ int proto = 0;
+ int sport = 0;
+ int dport = 0;
+
+ switch (args->type) {
+ case FL_ARG_FORWARD:
+ {
+ struct sk_buff *skb = args->fl_u.skb;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ int len;
+
+ len = skb->len - sizeof(struct ipv6hdr);
+
+ proto = hdr->nexthdr;
+
+ switch (proto) {
+ case IPPROTO_TCP:
+ {
+ struct tcphdr *th;
+
+ if (len < sizeof(struct tcphdr)) {
+ res = FLOWR_ERROR;
+ goto out;
+ }
+ th = (struct tcphdr *)(hdr + 1);
+ sport = th->source;
+ dport = th->dest;
+ break;
+ }
+ case IPPROTO_UDP:
+ {
+ struct udphdr *uh;
+
+ if (len < sizeof(struct udphdr)) {
+ res = FLOWR_ERROR;
+ goto out;
+ }
+ uh = (struct udphdr *)(hdr + 1);
+ sport = uh->source;
+ dport = uh->dest;
+ break;
+ }
+ default:
+ goto out;
+ };
+ break;
+ }
+
+ case FL_ARG_ORIGIN:
+ {
+ proto = args->fl_u.fl_o.flow->proto;
+
+ if (proto == IPPROTO_ICMPV6) {
+ goto out;
+ } else {
+ sport = args->fl_u.fl_o.flow->uli_u.ports.sport;
+ dport = args->fl_u.fl_o.flow->uli_u.ports.dport;
+ }
+ break;
+ }
+
+ if (proto == rl->info.proto &&
+ port_match(args->fl_u.fl_o.flow->uli_u.ports.sport, sport) &&
+ port_match(args->fl_u.fl_o.flow->uli_u.ports.dport, dport)) {
+ if (rl->policy & IP6_FW_REJECT)
+ res = FLOWR_SELECT;
+ else
+ res = FLOWR_CLEAR;
+ }
+
+ default:
+#if IP6_FW_DEBUG >= 1
+ printk(KERN_DEBUG "ip6_fw_accept: unknown arg type\n");
+#endif
+ goto out;
+ };
+
+out:
+ return res;
+}
+
+static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args)
+{
+ struct rt6_info *rt;
+ struct ip6_fw_rule *rl;
+ int proto;
+ int res = FLOWR_NODECISION;
+
+ rt = (struct rt6_info *) dst;
+ rl = (struct ip6_fw_rule *) rt->rt6i_flowr;
+
+ proto = rl->info.proto;
+
+ switch (proto) {
+ case 0:
+ if (rl->policy & IP6_FW_REJECT)
+ res = FLOWR_SELECT;
+ else
+ res = FLOWR_CLEAR;
+ break;
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ res = ip6_fw_accept_trans(rl, args);
+ break;
+ case IPPROTO_ICMPV6:
+ };
+
+ return res;
+}
+
+static struct dst_entry * ip6_fw_dup(struct dst_entry *frule,
+ struct dst_entry *rt,
+ struct fl_acc_args *args)
+{
+ struct ip6_fw_rule *rl;
+ struct rt6_info *nrt;
+ struct rt6_info *frt;
+
+ frt = (struct rt6_info *) frule;
+
+ rl = (struct ip6_fw_rule *) frt->rt6i_flowr;
+
+ nrt = ip6_rt_copy((struct rt6_info *) rt);
+
+ if (nrt) {
+ nrt->u.dst.input = frule->input;
+ nrt->u.dst.output = frule->output;
+
+ nrt->rt6i_flowr = flow_clone(frt->rt6i_flowr);
+
+ nrt->rt6i_flags |= RTF_CACHE;
+ nrt->rt6i_tstamp = jiffies;
+ }
+
+ return (struct dst_entry *) nrt;
+}
+
+int ip6_fw_reject(struct sk_buff *skb)
+{
+#if IP6_FW_DEBUG >= 1
+ printk(KERN_DEBUG "packet rejected: \n");
+#endif
+
+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADM_PROHIBITED, 0,
+ skb->dev);
+ /*
+ * send it via netlink, as (rule, skb)
+ */
+
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+int ip6_fw_discard(struct sk_buff *skb)
+{
+ printk(KERN_DEBUG "ip6_fw: BUG fw_reject called\n");
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+int ip6_fw_msg_add(struct ip6_fw_msg *msg)
+{
+ struct in6_rtmsg rtmsg;
+ struct ip6_fw_rule *rl;
+ struct rt6_info *rt;
+ int err;
+
+ ipv6_addr_copy(&rtmsg.rtmsg_dst, &msg->dst);
+ ipv6_addr_copy(&rtmsg.rtmsg_src, &msg->src);
+ rtmsg.rtmsg_dst_len = msg->dst_len;
+ rtmsg.rtmsg_src_len = msg->src_len;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_FW;
+
+ rl = ip6_fwrule_alloc();
+
+ if (rl == NULL)
+ return -ENOMEM;
+
+ rl->policy = msg->policy;
+ rl->info.proto = msg->proto;
+ rl->info.uli_u.data = msg->u.data;
+
+ rtmsg.rtmsg_flags = RTF_NONEXTHOP|RTF_POLICY;
+ rt = ip6_route_add(&rtmsg, &err);
+
+ if (rt == NULL) {
+ ip6_fwrule_free(rl);
+ return -ENOMEM;
+ }
+
+ rt->u.dst.error = -EPERM;
+
+ if (msg->policy == IP6_FW_ACCEPT) {
+ /*
+ * Accept rules are never selected
+ * (i.e. packets use normal forwarding)
+ */
+ rt->u.dst.input = ip6_fw_discard;
+ rt->u.dst.output = ip6_fw_discard;
+ } else {
+ rt->u.dst.input = ip6_fw_reject;
+ rt->u.dst.output = ip6_fw_reject;
+ }
+
+ ip6_rule_add(rl);
+
+ rt->rt6i_flowr = flow_clone((struct flow_rule *)rl);
+
+ return 0;
+}
+
+static int ip6_fw_msgrcv(int unit, struct sk_buff *skb)
+{
+ int count = 0;
+
+ while (skb->len) {
+ struct ip6_fw_msg *msg;
+
+ if (skb->len < sizeof(struct ip6_fw_msg)) {
+ count = -EINVAL;
+ break;
+ }
+
+ msg = (struct ip6_fw_msg *) skb->data;
+ skb_pull(skb, sizeof(struct ip6_fw_msg));
+ count += sizeof(struct ip6_fw_msg);
+
+ switch (msg->action) {
+ case IP6_FW_MSG_ADD:
+ ip6_fw_msg_add(msg);
+ break;
+ case IP6_FW_MSG_DEL:
+ break;
+ default:
+ return -EINVAL;
+ };
+ }
+
+ return count;
+}
+
+static void ip6_fw_destroy(struct flow_rule *rl)
+{
+ ip6_fwrule_free((struct ip6_fw_rule *)rl);
+}
+
+#ifdef MODULE
+#define ip6_fw_init module_init
+#endif
+
+void ip6_fw_init(void)
+{
+ netlink_attach(NETLINK_IP6_FW, ip6_fw_msgrcv);
+}
+
+#ifdef MODULE
+void module_cleanup(void)
+{
+ netlink_detach(NETLINK_IP6_FW);
+}
+#endif
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
new file mode 100644
index 000000000..c5e21417d
--- /dev/null
+++ b/net/ipv6/ip6_input.c
@@ -0,0 +1,408 @@
+/*
+ * IPv6 input
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ * Ian P. Morris <I.P.Morris@soton.ac.uk>
+ *
+ * $Id: ip6_input.c,v 1.4 1997/03/18 18:24:35 davem Exp $
+ *
+ * Based in linux/net/ipv4/ip_input.c
+ *
+ * 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+#include <linux/icmpv6.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/transp_v6.h>
+#include <net/rawv6.h>
+#include <net/ndisc.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev,
+ __u8 *nhptr, struct ipv6_options *opt);
+
+struct hdrtype_proc {
+ u8 type;
+ int (*func) (struct sk_buff **, struct device *dev, __u8 *ptr,
+ struct ipv6_options *opt);
+} hdrproc_lst[] = {
+
+ /*
+ TODO
+
+ {NEXTHDR_HOP, ipv6_hop_by_hop}
+ {NEXTHDR_ROUTING, ipv6_routing_header},
+ */
+ {NEXTHDR_FRAGMENT, ipv6_reassembly},
+
+ {NEXTHDR_DEST, ipv6_dest_opt},
+ /*
+ {NEXTHDR_AUTH, ipv6_auth_hdr},
+ {NEXTHDR_ESP, ipv6_esp_hdr},
+ */
+ {NEXTHDR_MAX, NULL}
+};
+
+/* New header structures */
+
+
+struct ipv6_tlvtype {
+ u8 type;
+ u8 len;
+};
+
+struct ipv6_destopt_hdr {
+ u8 nexthdr;
+ u8 hdrlen;
+};
+
+
+struct tlvtype_proc {
+ u8 type;
+ int (*func) (struct sk_buff *, struct device *dev, __u8 *ptr,
+ struct ipv6_options *opt);
+ /*
+ * these functions do NOT update skb->h.raw
+ */
+
+} tlvprocdestopt_lst[] = {
+ {255, NULL}
+};
+
+static int ip6_dstopt_unknown(struct sk_buff *skb, struct ipv6_tlvtype *hdr)
+{
+ struct in6_addr *daddr;
+ int pos;
+
+ /*
+ * unkown destination option type
+ */
+
+ pos = (__u8 *) skb->h.raw - (__u8 *) skb->nh.raw;
+
+ /* I think this is correct please check - IPM */
+
+ switch ((hdr->type & 0xC0) >> 6) {
+ case 0: /* ignore */
+ skb->h.raw += hdr->len+2;
+ return 1;
+
+ case 1: /* drop packet */
+ break;
+
+ case 2: /* send ICMP PARM PROB regardless and drop packet */
+ icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_UNK_OPTION,
+ pos, skb->dev);
+ break;
+
+ case 3: /* Send ICMP if not a multicast address and drop packet */
+ daddr = &skb->nh.ipv6h->daddr;
+ if (!(ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST))
+ icmpv6_send(skb, ICMPV6_PARAMPROB,
+ ICMPV6_UNK_OPTION, pos, skb->dev);
+ };
+
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
+ struct device *dev, __u8 *nhptr,
+ struct ipv6_options *opt, void *lastopt)
+{
+ struct ipv6_tlvtype *hdr;
+ struct tlvtype_proc *curr;
+
+ while ((hdr=(struct ipv6_tlvtype *)skb->h.raw) != lastopt) {
+ switch (hdr->type & 0x3F) {
+ case 0: /* TLV encoded Pad1 */
+ skb->h.raw++;
+ break;
+
+ case 1: /* TLV encoded PadN */
+ skb->h.raw += hdr->len+2;
+ break;
+
+ default: /* Other TLV code so scan list */
+ for (curr=procs; curr->type != 255; curr++) {
+ if (curr->type == (hdr->type & 0x3F)) {
+ curr->func(skb, dev, nhptr, opt);
+ skb->h.raw += hdr->len+2;
+ break;
+ }
+ }
+ if (curr->type==255) {
+ if (ip6_dstopt_unknown(skb, hdr) == 0)
+ return 0;
+ }
+ break;
+ }
+ }
+ return 1;
+}
+
+static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev,
+ __u8 *nhptr, struct ipv6_options *opt)
+{
+ struct sk_buff *skb=*skb_ptr;
+ struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
+ int res = 0;
+
+ if (ip6_parse_tlv(tlvprocdestopt_lst, skb, dev, nhptr, opt,
+ skb->h.raw+hdr->hdrlen))
+ res = hdr->nexthdr;
+
+ return res;
+}
+
+
+int ipv6_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct ipv6hdr *hdr;
+ int pkt_len;
+
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ hdr = skb->nh.ipv6h;
+
+ if (skb->len < sizeof(struct ipv6hdr) || hdr->version != 6)
+ goto err;
+
+ pkt_len = ntohs(hdr->payload_len);
+
+ if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
+ goto err;
+
+ skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+
+ ip6_route_input(skb);
+
+ return 0;
+err:
+ ipv6_statistics.Ip6InHdrErrors++;
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+/*
+ * 0 - deliver
+ * 1 - block
+ */
+static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
+{
+ struct icmp6hdr *icmph;
+ struct raw6_opt *opt;
+
+ opt = &sk->tp_pinfo.tp_raw;
+ icmph = (struct icmp6hdr *) (skb->nh.ipv6h + 1);
+ return test_bit(icmph->icmp6_type, &opt->filter);
+}
+
+/*
+ * demultiplex raw sockets.
+ * (should consider queueing the skb in the sock receive_queue
+ * without calling rawv6.c)
+ */
+static struct sock * ipv6_raw_deliver(struct sk_buff *skb,
+ struct ipv6_options *opt,
+ int nexthdr, int len)
+{
+ struct in6_addr *saddr;
+ struct in6_addr *daddr;
+ struct sock *sk, *sk2;
+ __u8 hash;
+
+ saddr = &skb->nh.ipv6h->saddr;
+ daddr = saddr + 1;
+
+ hash = nexthdr & (MAX_INET_PROTOS - 1);
+
+ sk = raw_v6_htable[hash];
+
+ /*
+ * The first socket found will be delivered after
+ * delivery to transport protocols.
+ */
+
+ if (sk == NULL)
+ return NULL;
+
+ sk = raw_v6_lookup(sk, nexthdr, daddr, saddr);
+
+ if (sk) {
+ sk2 = sk;
+
+ while ((sk2 = raw_v6_lookup(sk2->next, nexthdr, daddr, saddr))) {
+ struct sk_buff *buff;
+
+ if (nexthdr == IPPROTO_ICMPV6 &&
+ icmpv6_filter(sk2, skb))
+ continue;
+
+ buff = skb_clone(skb, GFP_ATOMIC);
+ buff->sk = sk2;
+ rawv6_rcv(buff, skb->dev, saddr, daddr, opt, len);
+ }
+ }
+
+ if (sk && nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk, skb))
+ sk = NULL;
+
+ return sk;
+}
+
+/*
+ * Deliver the packet to the host
+ */
+
+int ip6_input(struct sk_buff *skb)
+{
+ struct ipv6_options *opt = (struct ipv6_options *) skb->cb;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct inet6_protocol *ipprot;
+ struct hdrtype_proc *hdrt;
+ struct sock *raw_sk;
+ __u8 *nhptr;
+ int nexthdr;
+ int found = 0;
+ u8 hash;
+ int len;
+
+ skb->h.raw += sizeof(struct ipv6hdr);
+
+ /*
+ * Parse extension headers
+ */
+
+ nexthdr = hdr->nexthdr;
+ nhptr = &hdr->nexthdr;
+
+ /*
+ * check for extension headers
+ */
+
+st_loop:
+
+ for (hdrt=hdrproc_lst; hdrt->type != NEXTHDR_MAX; hdrt++) {
+ if (hdrt->type == nexthdr) {
+ if ((nexthdr = hdrt->func(&skb, skb->dev, nhptr, opt))) {
+ nhptr = skb->h.raw;
+ hdr = skb->nh.ipv6h;
+ goto st_loop;
+ }
+ return 0;
+ }
+ }
+
+ len = skb->tail - skb->h.raw;
+
+ raw_sk = ipv6_raw_deliver(skb, opt, nexthdr, len);
+
+ hash = nexthdr & (MAX_INET_PROTOS - 1);
+ for (ipprot = (struct inet6_protocol *) inet6_protos[hash];
+ ipprot != NULL;
+ ipprot = (struct inet6_protocol *) ipprot->next) {
+ struct sk_buff *buff = skb;
+
+ if (ipprot->protocol != nexthdr)
+ continue;
+
+ if (ipprot->copy || raw_sk)
+ buff = skb_clone(skb, GFP_ATOMIC);
+
+
+ ipprot->handler(buff, skb->dev, &hdr->saddr, &hdr->daddr,
+ opt, len, 0, ipprot);
+ found = 1;
+ }
+
+ if (raw_sk) {
+ skb->sk = raw_sk;
+ rawv6_rcv(skb, skb->dev, &hdr->saddr, &hdr->daddr, opt, len);
+ found = 1;
+ }
+
+ /*
+ * not found: send ICMP parameter problem back
+ */
+
+ if (!found) {
+ unsigned long offset;
+#if IP6_DEBUG >= 2
+ printk(KERN_DEBUG "proto not found %d\n", nexthdr);
+#endif
+ offset = nhptr - (u8*) hdr;
+ icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_UNK_NEXTHDR,
+ offset, skb->dev);
+ kfree_skb(skb, FREE_READ);
+ }
+
+ return 0;
+}
+
+int ip6_mc_input(struct sk_buff *skb)
+{
+ struct ipv6hdr *hdr;
+ int deliver = 0;
+ int discard = 1;
+
+ hdr = skb->nh.ipv6h;
+ if (ipv6_chk_mcast_addr(skb->dev, &hdr->daddr))
+ deliver = 1;
+
+#if 0
+ if (ipv6_config.multicast_route) {
+ int addr_type;
+
+ addr_type = ipv6_addr_type(&hdr->daddr);
+
+ if (!(addr_type & (IPV6_ADDR_LOOPBACK | IPV6_ADDR_LINKLOCAL))) {
+ struct sk_buff *skb2;
+ struct dst_entry *dst;
+
+ dst = skb->dst;
+
+ if (deliver) {
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ } else {
+ discard = 0;
+ skb2 = skb;
+ }
+
+ dst->output(skb2);
+ }
+ }
+#endif
+
+ if (deliver) {
+ discard = 0;
+ ip6_input(skb);
+ }
+
+ if (discard)
+ kfree_skb(skb, FREE_READ);
+
+ return 0;
+}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
new file mode 100644
index 000000000..6c14747eb
--- /dev/null
+++ b/net/ipv6/ip6_output.c
@@ -0,0 +1,629 @@
+/*
+ * IPv6 output functions
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: ip6_output.c,v 1.3 1997/03/18 18:24:37 davem Exp $
+ *
+ * Based on linux/net/ipv4/ip_output.c
+ *
+ * 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/route.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/protocol.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+static u32 ipv6_fragmentation_id = 1;
+
+static void ipv6_build_mac_hdr(struct sk_buff *skb, struct dst_entry *dst,
+ int len)
+{
+ struct device *dev;
+
+
+ dev = dst->dev;
+
+ skb->arp = 1;
+
+ if (dev->hard_header) {
+ int mac;
+
+#if 0
+ if (dst->hh)
+ hh_copy_header(dst->hh, skb);
+#endif
+ mac = dev->hard_header(skb, dev, ETH_P_IPV6, NULL, NULL, len);
+
+ if (mac < 0)
+ skb->arp = 0;
+ }
+
+ skb->mac.raw = skb->data;
+}
+
+/*
+ * xmit an sk_buff (used by TCP)
+ * sk can be NULL (for sending RESETs)
+ */
+
+int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
+ struct ipv6_options *opt)
+{
+ struct ipv6_pinfo *np = NULL;
+ struct dst_entry *dst = NULL;
+ struct ipv6hdr *hdr;
+ int seg_len;
+
+ hdr = skb->nh.ipv6h;
+
+ if (sk)
+ np = &sk->net_pinfo.af_inet6;
+
+ if (np && np->dst) {
+ /*
+ * dst_check returns NULL if route is no longer valid
+ */
+ dst = dst_check(&dst, np->dst_cookie);
+ }
+
+ if (dst == NULL) {
+ dst = ip6_route_output(sk, fl);
+
+ if (dst->error) {
+ /*
+ * NETUNREACH usually
+ */
+ return dst->error;
+ }
+ }
+
+ skb->dst = dst_clone(dst);
+ skb->dev = dst->dev;
+ seg_len = skb->tail - ((unsigned char *) hdr);
+
+ /*
+ * Link Layer headers
+ */
+
+ skb->protocol = __constant_htons(ETH_P_IPV6);
+ hdr = skb->nh.ipv6h;
+
+ ipv6_build_mac_hdr(skb, dst, seg_len);
+
+
+ /*
+ * Fill in the IPv6 header
+ */
+
+ hdr->version = 6;
+ hdr->priority = np ? np->priority : 0;
+
+ if (np)
+ memcpy(hdr->flow_lbl, (void *) &np->flow_lbl, 3);
+ else
+ memset(hdr->flow_lbl, 0, 3);
+
+ hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
+ hdr->nexthdr = fl->proto;
+ hdr->hop_limit = np ? np->hop_limit : ipv6_config.hop_limit;
+
+ ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
+ ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
+
+ ipv6_statistics.Ip6OutRequests++;
+ dst->output(skb);
+
+ if (sk)
+ ip6_dst_store(sk, dst);
+ else
+ dst_release(dst);
+
+ return 0;
+}
+
+/*
+ * To avoid extra problems ND packets are send through this
+ * routine. It's code duplication but i really want to avoid
+ * extra checks since ipv6_build_header is used by TCP (which
+ * is for us performace critical)
+ */
+
+int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct device *dev,
+ struct in6_addr *saddr, struct in6_addr *daddr,
+ int proto, int len)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6hdr *hdr;
+ int totlen;
+
+ skb->protocol = __constant_htons(ETH_P_IPV6);
+ skb->dev = dev;
+
+ totlen = len + sizeof(struct ipv6hdr);
+
+ skb->mac.raw = skb->data;
+
+ hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
+ skb->nh.ipv6h = hdr;
+
+ hdr->version = 6;
+ hdr->priority = np->priority & 0x0f;
+ memset(hdr->flow_lbl, 0, 3);
+
+ hdr->payload_len = htons(len);
+ hdr->nexthdr = proto;
+ hdr->hop_limit = np->hop_limit;
+
+ ipv6_addr_copy(&hdr->saddr, saddr);
+ ipv6_addr_copy(&hdr->daddr, daddr);
+
+ return 0;
+}
+
+static void ip6_bld_1(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
+ int hlimit, unsigned short pktlength)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6hdr *hdr;
+
+ skb->nh.raw = skb_put(skb, sizeof(struct ipv6hdr));
+ hdr = skb->nh.ipv6h;
+
+ hdr->version = 6;
+ hdr->priority = np->priority;
+
+ memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
+
+ hdr->payload_len = htons(pktlength - sizeof(struct ipv6hdr));
+
+ /*
+ * FIXME: hop limit has default UNI/MCAST and
+ * msgctl settings
+ */
+ hdr->hop_limit = hlimit;
+
+ ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
+ ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
+}
+
+static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag,
+ const void *data, struct dst_entry *dst,
+ struct flowi *fl, struct ipv6_options *opt,
+ int hlimit, int flags, unsigned short length)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6hdr *hdr;
+ struct sk_buff *last_skb;
+ struct frag_hdr *fhdr;
+ int unfrag_len;
+ int payl_len;
+ int frag_len;
+ int last_len;
+ int nfrags;
+ int fhdr_dist;
+ int err;
+
+ /*
+ * Fragmentation
+ *
+ * Extension header order:
+ * Hop-by-hop -> Routing -> Fragment -> rest (...)
+ *
+ * We must build the non-fragmented part that
+ * will be in every packet... this also means
+ * that other extension headers (Dest, Auth, etc)
+ * must be considered in the data to be fragmented
+ */
+
+ unfrag_len = sizeof(struct ipv6hdr) + sizeof(struct frag_hdr);
+ payl_len = length;
+
+ if (opt) {
+ unfrag_len += opt->opt_nflen;
+ payl_len += opt->opt_flen;
+ }
+
+ nfrags = payl_len / ((dst->pmtu - unfrag_len) & ~0x7);
+
+ /*
+ * Length of fragmented part on every packet but
+ * the last must be an:
+ * "integer multiple of 8 octects".
+ */
+
+ frag_len = (dst->pmtu - unfrag_len) & ~0x7;
+
+ /*
+ * We must send from end to start because of
+ * UDP/ICMP checksums. We do a funny trick:
+ * fill the last skb first with the fixed
+ * header (and its data) and then use it
+ * to create the following segments and send it
+ * in the end. If the peer is checking the M_flag
+ * to trigger the reassembly code then this
+ * might be a good idea.
+ */
+
+ last_len = payl_len - (nfrags * frag_len);
+
+ if (last_len == 0) {
+ last_len = frag_len;
+ nfrags--;
+ }
+
+ last_skb = sock_alloc_send_skb(sk, unfrag_len + frag_len +
+ dst->dev->hard_header_len + 15,
+ 0, flags & MSG_DONTWAIT, &err);
+
+ if (last_skb == NULL)
+ return err;
+
+ last_skb->dst = dst_clone(dst);
+ last_skb->dev = dst->dev;
+ last_skb->protocol = htons(ETH_P_IPV6);
+ last_skb->when = jiffies;
+ last_skb->arp = 0;
+
+ /*
+ * build the mac header...
+ */
+ if (dst->dev->hard_header_len) {
+ skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15);
+ ipv6_build_mac_hdr(last_skb, dst, unfrag_len + frag_len);
+ }
+
+ hdr = (struct ipv6hdr *) skb_put(last_skb, sizeof(struct ipv6hdr));
+ last_skb->nh.ipv6h = hdr;
+
+ hdr->version = 6;
+ hdr->priority = np->priority;
+
+ memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
+ hdr->payload_len = htons(unfrag_len + frag_len - sizeof(struct ipv6hdr));
+
+ hdr->hop_limit = hlimit;
+
+ hdr->nexthdr = NEXTHDR_FRAGMENT;
+
+ ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
+ ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
+
+#if 0
+ if (opt && opt->srcrt) {
+ hdr->nexthdr = ipv6opt_bld_rthdr(last_skb, opt, daddr,
+ NEXTHDR_FRAGMENT);
+ }
+#endif
+
+ fhdr = (struct frag_hdr *) skb_put(last_skb, sizeof(struct frag_hdr));
+ memset(fhdr, 0, sizeof(struct frag_hdr));
+
+ fhdr->nexthdr = fl->proto;
+ fhdr->frag_off = ntohs(nfrags * frag_len);
+ fhdr->identification = ipv6_fragmentation_id++;
+
+ fhdr_dist = (unsigned char *) fhdr - last_skb->data;
+
+ err = getfrag(data, &hdr->saddr, last_skb->tail, nfrags * frag_len,
+ last_len);
+
+ if (!err) {
+ while (nfrags--) {
+ struct sk_buff *skb;
+
+ struct frag_hdr *fhdr2;
+
+ printk(KERN_DEBUG "sending frag %d\n", nfrags);
+ skb = skb_copy(last_skb, sk->allocation);
+
+ if (skb == NULL)
+ return -ENOMEM;
+
+ fhdr2 = (struct frag_hdr *) (skb->data + fhdr_dist);
+
+ /* more flag on */
+ fhdr2->frag_off = ntohs(nfrags * frag_len + 1);
+
+ /*
+ * FIXME:
+ * if (nfrags == 0)
+ * put rest of headers
+ */
+
+ err = getfrag(data, &hdr->saddr,skb_put(skb, frag_len),
+ nfrags * frag_len, frag_len);
+
+ if (err) {
+ kfree_skb(skb, FREE_WRITE);
+ break;
+ }
+
+ ipv6_statistics.Ip6OutRequests++;
+ dst->output(skb);
+ }
+ }
+
+ if (err) {
+ kfree_skb(last_skb, FREE_WRITE);
+ return -EFAULT;
+ }
+
+ printk(KERN_DEBUG "sending last frag \n");
+
+ hdr->payload_len = htons(unfrag_len + last_len -
+ sizeof(struct ipv6hdr));
+
+ /*
+ * update last_skb to reflect the getfrag we did
+ * on start.
+ */
+
+ last_skb->tail += last_len;
+ last_skb->len += last_len;
+
+ /*
+ * toss the mac header out and rebuild it.
+ * needed because of the different frame length.
+ * ie: not needed for an ethernet.
+ */
+
+ if (dst->dev->type != ARPHRD_ETHER && last_len != frag_len) {
+ skb_pull(last_skb, (unsigned char *)last_skb->nh.ipv6h -
+ last_skb->data);
+ ipv6_build_mac_hdr(last_skb, dst, unfrag_len + last_len);
+ }
+
+ ipv6_statistics.Ip6OutRequests++;
+ dst->output(last_skb);
+
+ return 0;
+}
+
+int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
+ struct flowi *fl, unsigned short length,
+ struct ipv6_options *opt, int hlimit, int flags)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct in6_addr *final_dst = NULL;
+ struct dst_entry *dst;
+ int pktlength;
+ int err = 0;
+
+ if (opt && opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+ final_dst = fl->nl_u.ip6_u.daddr;
+ fl->nl_u.ip6_u.daddr = rt0->addr;
+ }
+
+ dst = NULL;
+
+ if (np->dst)
+ dst = dst_check(&np->dst, np->dst_cookie);
+
+ if (dst == NULL)
+ dst = ip6_route_output(sk, fl);
+
+ if (dst->error) {
+ ipv6_statistics.Ip6OutNoRoutes++;
+ err = -ENETUNREACH;
+ goto out;
+ }
+
+ if (fl->nl_u.ip6_u.saddr == NULL) {
+ struct inet6_ifaddr *ifa;
+
+ ifa = ipv6_get_saddr(dst, fl->nl_u.ip6_u.daddr);
+
+ if (ifa == NULL) {
+#if IP6_DEBUG >= 2
+ printk(KERN_DEBUG "ip6_build_xmit: "
+ "no availiable source address\n");
+#endif
+ err = -ENETUNREACH;
+ goto out;
+ }
+ fl->nl_u.ip6_u.saddr = &ifa->addr;
+ }
+
+ pktlength = length;
+
+ if (hlimit < 0)
+ hlimit = np->hop_limit;
+
+ if (!sk->ip_hdrincl) {
+ pktlength += sizeof(struct ipv6hdr);
+ if (opt)
+ pktlength += opt->opt_flen + opt->opt_nflen;
+ }
+
+ if (pktlength <= dst->pmtu) {
+ struct sk_buff *skb;
+ struct ipv6hdr *hdr;
+ struct device *dev;
+
+ skb = sock_alloc_send_skb(sk, pktlength + 15 +
+ dst->dev->hard_header_len, 0,
+ flags & MSG_DONTWAIT, &err);
+
+ if (skb == NULL) {
+ ipv6_statistics.Ip6OutDiscards++;
+ goto out;
+ }
+
+ dev = dst->dev;
+ skb->dst = dst_clone(dst);
+
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_IPV6);
+ skb->when = jiffies;
+ skb->arp = 0;
+
+ if (dev && dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ ipv6_build_mac_hdr(skb, dst, pktlength);
+ }
+
+ hdr = (struct ipv6hdr *) skb->tail;
+ skb->nh.ipv6h = hdr;
+
+ if (!sk->ip_hdrincl) {
+ ip6_bld_1(sk, skb, fl, hlimit, pktlength);
+#if 0
+ if (opt && opt->srcrt) {
+ hdr->nexthdr = ipv6opt_bld_rthdr(skb, opt,
+ final_dst,
+ fl->proto);
+ }
+ else
+#endif
+ hdr->nexthdr = fl->proto;
+ }
+
+ skb_put(skb, length);
+ err = getfrag(data, &hdr->saddr,
+ ((char *) hdr) + (pktlength - length),
+ 0, length);
+
+ if (!err) {
+ ipv6_statistics.Ip6OutRequests++;
+ dst->output(skb);
+ } else {
+ err = -EFAULT;
+ kfree_skb(skb, FREE_WRITE);
+ }
+ } else {
+ if (sk->ip_hdrincl)
+ return -EMSGSIZE;
+
+ err = ip6_frag_xmit(sk, getfrag, data, dst, fl, opt, hlimit,
+ flags, pktlength);
+ }
+
+ /*
+ * cleanup
+ */
+ out:
+
+ if (np->dst)
+ ip6_dst_store(sk, dst);
+ else
+ dst_release(dst);
+
+ return err;
+}
+
+int ip6_forward(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb->dst;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ int size;
+
+ /*
+ * check hop-by-hop options present
+ */
+#if 0
+ if (hdr->nexthdr == NEXTHDR_HOP)
+ {
+ }
+#endif
+ /*
+ * check and decrement ttl
+ */
+ if (hdr->hop_limit <= 1) {
+ icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
+ 0, skb->dev);
+
+ kfree_skb(skb, FREE_READ);
+ return -ETIMEDOUT;
+ }
+
+ hdr->hop_limit--;
+
+ if (skb->dev == dst->dev && dst->neighbour) {
+ struct in6_addr *target = NULL;
+ struct rt6_info *rt;
+ struct nd_neigh *ndn = (struct nd_neigh *) dst->neighbour;
+
+ /*
+ * incoming and outgoing devices are the same
+ * send a redirect.
+ */
+
+ rt = (struct rt6_info *) dst;
+ if ((rt->rt6i_flags & RTF_GATEWAY))
+ target = &ndn->ndn_addr;
+ else
+ target = &hdr->daddr;
+
+ ndisc_send_redirect(skb, dst->neighbour, target);
+ }
+
+ size = sizeof(struct ipv6hdr) + ntohs(hdr->payload_len);
+
+ if (size > dst->pmtu) {
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev);
+ kfree_skb(skb, FREE_READ);
+ return -EMSGSIZE;
+ }
+
+ skb->dev = dst->dev;
+
+ /*
+ * Rebuild the mac header
+ */
+ if (skb_headroom(skb) < dst->dev->hard_header_len) {
+ struct sk_buff *buff;
+
+ buff = alloc_skb(dst->dev->hard_header_len + skb->len + 15,
+ GFP_ATOMIC);
+
+ if (buff == NULL) {
+ kfree_skb(skb, FREE_WRITE);
+ return -ENOMEM;
+ }
+
+ skb_reserve(buff, (dst->dev->hard_header_len + 15) & ~15);
+
+ buff->protocol = __constant_htons(ETH_P_IPV6);
+ buff->h.raw = skb_put(buff, size);
+ buff->dst = dst_clone(dst);
+ buff->dev = dst->dev;
+
+ memcpy(buff->h.raw, hdr, size);
+ buff->nh.ipv6h = (struct ipv6hdr *) buff->h.raw;
+ kfree_skb(skb, FREE_READ);
+ skb = buff;
+ } else {
+ skb_pull(skb, skb->nh.raw - skb->data);
+ }
+
+ ipv6_build_mac_hdr(skb, dst, size);
+
+ if (dst->neighbour)
+ ndisc_event_send(dst->neighbour, skb);
+
+ ipv6_statistics.Ip6ForwDatagrams++;
+ dst->output(skb);
+
+ return 0;
+}
diff --git a/net/ipv6/ipv6_input.c b/net/ipv6/ipv6_input.c
deleted file mode 100644
index 64a9d79f0..000000000
--- a/net/ipv6/ipv6_input.c
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * IPv6 input
- * Linux INET6 implementation
- *
- * Authors:
- * Pedro Roque <roque@di.fc.ul.pt>
- * Ian P. Morris <I.P.Morris@soton.ac.uk>
- *
- * Based in linux/net/ipv4/ip_input.c
- *
- * $Id: ipv6_input.c,v 1.13 1996/10/11 16:03:06 roque Exp $
- *
- * 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.
- */
-
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/sched.h>
-#include <linux/net.h>
-#include <linux/netdevice.h>
-#include <linux/in6.h>
-#include <linux/icmpv6.h>
-
-#include <net/sock.h>
-#include <net/snmp.h>
-
-#include <net/ipv6.h>
-#include <net/protocol.h>
-#include <net/transp_v6.h>
-#include <net/rawv6.h>
-#include <net/ndisc.h>
-#include <net/ipv6_route.h>
-#include <net/addrconf.h>
-
-/*
- * Header processing function list
- * We process headers in order (as per RFC)
- * If the processing function returns 0 the packet is considered
- * delivered else it returns the value of the nexthdr.
- * The ptr field of the function points to the previous nexthdr field.
- * This is allows the processing function to change it if it's sematics
- * is: return a new packet without this header (like fragmentation).
- * When a next_header value is not within the list
- * the inet protocol list is searched (i.e. to deliver to
- * TCP for instance)
- */
-
-static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev, __u8 *nhptr,
- struct ipv6_options *opt);
-
-
-struct hdrtype_proc {
- u8 type;
- int (*func) (struct sk_buff **, struct device *dev, __u8 *ptr,
- struct ipv6_options *opt);
-} hdrproc_lst[] = {
- /*
- TODO
-
- {NEXTHDR_HOP, ipv6_hop_by_hop}
- */
- {NEXTHDR_ROUTING, ipv6_routing_header},
- {NEXTHDR_FRAGMENT, ipv6_reassembly},
-
- {NEXTHDR_DEST, ipv6_dest_opt},
- /*
- {NEXTHDR_AUTH, ipv6_auth_hdr},
- {NEXTHDR_ESP, ipv6_esp_hdr},
- */
- {NEXTHDR_MAX, NULL}
-};
-
-/* New header structures */
-
-
-struct ipv6_tlvtype {
- u8 type;
- u8 len;
-};
-
-struct ipv6_destopt_hdr {
- u8 nexthdr;
- u8 hdrlen;
-};
-
-
-struct tlvtype_proc {
- u8 type;
- int (*func) (struct sk_buff *, struct device *dev, __u8 *ptr,
- struct ipv6_options *opt);
-
- /* these functions do NOT update skb->h.raw */
-
-} tlvprocdestopt_lst[] = {
- {255, NULL}
-};
-
-
-static int parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
- struct device *dev, __u8 *nhptr, struct ipv6_options *opt,
- void *lastopt)
-{
- struct ipv6_tlvtype *hdr;
- struct tlvtype_proc *curr;
- int pos;
-
- while ((hdr=(struct ipv6_tlvtype *)skb->h.raw) != lastopt)
- switch (hdr->type & 0x3F)
- {
- case 0: /* TLV encoded Pad1 */
- skb->h.raw++;
- break;
-
- case 1: /* TLV encoded PadN */
- skb->h.raw += hdr->len+2;
- break;
-
- default: /* Other TLV code so scan list */
- for (curr=procs; curr->type != 255; curr++)
- if (curr->type == (hdr->type & 0x3F))
- {
- curr->func(skb, dev, nhptr, opt);
- skb->h.raw += hdr->len+2;
- break;
- }
-
- if (curr->type==255)
- {
- /* unkown type */
- pos= (__u8 *) skb->h.raw - (__u8 *) skb->ipv6_hdr;
- /* I think this is correct please check - IPM */
-
- switch ((hdr->type & 0xC0) >> 6) {
- case 0: /* ignore */
- skb->h.raw += hdr->len+2;
- break;
-
- case 1: /* drop packet */
- kfree_skb(skb, FREE_READ);
- return 0;
-
- case 2: /* send ICMP PARM PROB regardless and
- drop packet */
- icmpv6_send(skb, ICMPV6_PARAMETER_PROB,
- 2, pos, dev);
- kfree_skb(skb, FREE_READ);
- return 0;
-
- case 3: /* Send ICMP if not a multicast address
- and drop packet */
- if (!(ipv6_addr_type(&(skb->ipv6_hdr->daddr)) & IPV6_ADDR_MULTICAST) )
- icmpv6_send(skb, ICMPV6_PARAMETER_PROB, 2, pos, dev);
- kfree_skb(skb, FREE_READ);
- return 0;
- }
- }
- break;
- }
-
- return 1;
-}
-
-
-
-static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev, __u8 *nhptr,
- struct ipv6_options *opt)
-{
- struct sk_buff *skb=*skb_ptr;
- struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
-
- if (parse_tlv(tlvprocdestopt_lst, skb, dev, nhptr, opt,skb->h.raw+hdr->hdrlen))
- return hdr->nexthdr;
- else
- return 0;
-}
-
-
-
-/*
- * 0 - deliver
- * 1 - block
- */
-static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
-{
- struct icmpv6hdr *icmph;
- struct raw6_opt *opt;
-
- opt = &sk->tp_pinfo.tp_raw;
- icmph = (struct icmpv6hdr *) (skb->ipv6_hdr + 1);
- return test_bit(icmph->type, &opt->filter);
-}
-
-/*
- * demultiplex raw sockets.
- * (should consider queueing the skb in the sock receive_queue
- * without calling rawv6.c)
- */
-static struct sock * ipv6_raw_deliver(struct sk_buff *skb,
- struct device *dev,
- struct ipv6_options *opt,
- __u16 nexthdr,
- __u16 len,
- struct in6_addr *saddr,
- struct in6_addr *daddr)
-{
- struct sock *sk, *sk2;
- __u8 hash;
-
- hash = nexthdr & (SOCK_ARRAY_SIZE-1);
-
- sk = rawv6_prot.sock_array[hash];
-
-
- /*
- * The first socket found will be delivered after
- * delivery to transport protocols.
- */
-
- if (sk == NULL)
- return NULL;
-
- sk = inet6_get_sock_raw(sk, nexthdr, daddr, saddr);
-
- if (sk)
- {
- sk2 = sk;
-
- while ((sk2 = inet6_get_sock_raw(sk2->next, nexthdr,
- daddr, saddr)))
- {
- struct sk_buff *buff;
-
- if (nexthdr == IPPROTO_ICMPV6 &&
- icmpv6_filter(sk2, skb))
- {
- continue;
- }
- buff = skb_clone(skb, GFP_ATOMIC);
- buff->sk = sk2;
- rawv6_rcv(buff, dev, saddr, daddr, opt, len);
- }
- }
-
- if (sk && nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk, skb))
- {
- sk = NULL;
- }
-
- return sk;
-}
-
-int ipv6_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
-{
- struct inet6_ifaddr *ifp;
- struct ipv6_options *opt = (struct ipv6_options *) skb->proto_priv;
- struct ipv6hdr *hdr;
- u8 hash;
- u8 addr_type;
- struct inet6_protocol *ipprot;
- struct sock *raw_sk;
- int found = 0;
- int nexthdr = 0;
- __u8 *nhptr;
- int pkt_len;
-
- hdr = skb->ipv6_hdr = (struct ipv6hdr *) skb->h.raw;
-
- if (skb->len < sizeof(struct ipv6hdr) || hdr->version != 6)
- {
- ipv6_statistics.Ip6InHdrErrors++;
- printk(KERN_DEBUG "ipv6_rcv: broken header\n");
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- pkt_len = ntohs(hdr->payload_len);
-
- if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
- {
- printk(KERN_DEBUG "ipv6_rcv: invalid payload length\n");
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
-
- /* check daddr */
-
- /* Accounting & Firewall check */
-
- addr_type = ipv6_addr_type(&hdr->daddr);
-
- if (addr_type & IPV6_ADDR_MULTICAST)
- {
- /*
- * if mcast address is not for one of our groups
- * either pass it to mcast router or discard it
- */
-
- if (ipv6_chk_mcast_addr(dev, &hdr->daddr) == 0)
- {
- /* something like:
- if (acting_as_router)
- ipv6_mcast_route(skb, ...)
- else
- */
- kfree_skb(skb, FREE_READ);
- return 0;
- }
- }
-
- if (addr_type & IPV6_ADDR_MULTICAST ||
- (ifp = ipv6_chk_addr(&hdr->daddr)))
- {
-
- /* loop in a cicle parsing nexthdrs */
-
- skb->h.raw += sizeof(struct ipv6hdr);
-
- /* extension header processing must update skb->h.raw */
-
- nexthdr = hdr->nexthdr;
- nhptr = &hdr->nexthdr;
-
-
- while(1)
- {
- struct hdrtype_proc *hdrt;
-
- /* check for extension header */
-
- for (hdrt=hdrproc_lst; hdrt->type != NEXTHDR_MAX; hdrt++)
- {
- if (hdrt->type == nexthdr)
- {
- if ((nexthdr = hdrt->func(&skb, dev, nhptr, opt)))
- {
- nhptr = skb->h.raw;
- hdr = skb->ipv6_hdr;
- continue;
- }
- return 0;
- }
- }
- break;
-
- }
-
- /*
- * deliver to raw sockets
- * should we deliver raw after or before parsing
- * extension headers ?
- * delivering after means we do reassembly of datagrams
- * in ip.
- */
-
- pkt_len = skb->tail - skb->h.raw;
-
- raw_sk = ipv6_raw_deliver(skb, dev, opt, nexthdr, pkt_len,
- &hdr->saddr, &hdr->daddr);
-
- /* check inet6_protocol list */
-
- hash = nexthdr & (MAX_INET_PROTOS -1);
- for (ipprot = (struct inet6_protocol *) inet6_protos[hash];
- ipprot != NULL;
- ipprot = (struct inet6_protocol *) ipprot->next)
- {
- struct sk_buff *buff = skb;
-
- if (ipprot->protocol != nexthdr)
- continue;
-
- if (ipprot->copy || raw_sk)
- buff = skb_clone(skb, GFP_ATOMIC);
-
-
- ipprot->handler(buff, dev,
- &hdr->saddr, &hdr->daddr,
- opt, pkt_len,
- 0, ipprot);
- found = 1;
- }
-
- if (raw_sk)
- {
- skb->sk = raw_sk;
- rawv6_rcv(skb, dev, &hdr->saddr, &hdr->daddr, opt,
- htons(hdr->payload_len));
- found = 1;
- }
-
- /* not found: send ICMP parameter problem back */
-
- if (!found)
- {
- printk(KERN_DEBUG "proto not found %d\n", nexthdr);
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
- }
-
- }
- else
- {
- if (ipv6_forwarding)
- {
- if (addr_type & IPV6_ADDR_LINKLOCAL)
- {
- printk(KERN_DEBUG
- "link local pkt to forward\n");
- kfree_skb(skb, FREE_READ);
- return 0;
- }
- ipv6_forward(skb, dev, 0);
- }
- else
- {
- printk(KERN_WARNING "IPV6: packet to forward -"
- "host not configured as router\n");
- kfree_skb(skb, FREE_READ);
- }
- }
-
- return 0;
-}
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/ipv6_output.c b/net/ipv6/ipv6_output.c
deleted file mode 100644
index 7f82dba03..000000000
--- a/net/ipv6/ipv6_output.c
+++ /dev/null
@@ -1,1003 +0,0 @@
-/*
- * IPv6 output functions
- * Linux INET6 implementation
- *
- * Authors:
- * Pedro Roque <roque@di.fc.ul.pt>
- *
- * Based on linux/net/ipv4/ip_output.c
- *
- * $Id: ipv6_output.c,v 1.19 1996/10/16 18:34:16 roque Exp $
- *
- * 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.
- */
-
-/*
- * Changes:
- *
- * Andi Kleen : exception handling
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/sched.h>
-#include <linux/net.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/in6.h>
-
-#include <net/sock.h>
-#include <net/snmp.h>
-
-#include <net/ipv6.h>
-#include <net/ndisc.h>
-#include <net/protocol.h>
-#include <net/transp_v6.h>
-#include <net/ipv6_route.h>
-#include <net/addrconf.h>
-
-static u32 ipv6_fragmentation_id = 1;
-int ipv6_forwarding = 0; /* default: host */
-
-static int __inline__ ipv6_build_mac_header(struct sk_buff *skb,
- struct device *dev,
- struct neighbour *neigh,
- int len)
-{
- int mac;
- int hdrlen = 0;
-
- skb->arp = 1;
- skb->nexthop = neigh;
-
-
- if (dev->hard_header_len)
- {
- skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
-
- if (neigh && (neigh->flags & NCF_HHVALID))
- {
- /*
- * Cached hardware header
- */
-
- memcpy(skb_push(skb, dev->hard_header_len),
- neigh->hh_data, dev->hard_header_len);
-
- return dev->hard_header_len;
- }
-
- if (dev->hard_header)
- {
- mac = dev->hard_header(skb, dev, ETH_P_IPV6,
- NULL, NULL, len);
-
- if (mac < 0)
- {
- hdrlen = -mac;
- skb->arp = 0;
- }
- else
- {
- hdrlen = mac;
- }
- }
- else
- hdrlen = dev->hard_header_len;
- }
-
- return hdrlen;
-}
-
-void ipv6_redo_mac_hdr(struct sk_buff *skb, struct neighbour *neigh, int len)
-{
- struct device *dev = neigh->dev;
- int mac;
-
- skb->dev = dev;
- skb->nexthop = neigh;
- skb->arp = 1;
-
- skb_pull(skb, (unsigned char *) skb->ipv6_hdr - skb->data);
-
- /*
- * neighbour cache should have the ether address
- * cached... use it
- */
-
- if (dev->hard_header)
- {
- if (neigh && (neigh->flags & NCF_HHVALID))
- {
- /*
- * Cached hardware header
- */
-
- memcpy(skb_push(skb, dev->hard_header_len),
- neigh->hh_data, dev->hard_header_len);
- return;
- }
-
- mac = dev->hard_header(skb, dev, ETH_P_IPV6,
- NULL, NULL, len);
-
- if (mac < 0)
- {
- skb->arp = 0;
- }
-
- }
-}
-
-void default_output_method(struct sk_buff *skb, struct rt6_info *rt)
-{
- struct sock *sk = skb->sk;
- struct device *dev = skb->dev;
-
- if (dev->flags & IFF_UP)
- {
- /*
- * If we have an owner use its priority setting,
- * otherwise use NORMAL
- */
-
- if (sk != NULL)
- {
- dev_queue_xmit(skb, dev, sk->priority);
- }
- else
- {
- dev_queue_xmit(skb, dev, SOPRI_NORMAL);
- }
- }
- else
- {
- if(sk)
- sk->err = ENETDOWN;
-
- ipv6_statistics.Ip6OutDiscards++;
-
- kfree_skb(skb, FREE_WRITE);
- }
-}
-
-/*
- * xmit an sk_buff (used by TCP)
- * sk can be NULL (for sending RESETs)
- */
-int ipv6_xmit(struct sock *sk, struct sk_buff *skb, struct in6_addr *saddr,
- struct in6_addr *daddr, struct ipv6_options *opt, int proto)
-{
- struct ipv6hdr *hdr;
- struct dest_entry *dc;
- struct ipv6_pinfo *np = NULL;
- struct device *dev = skb->dev;
- int seg_len;
- int addr_type;
- int rt_flags = 0;
-
-
- addr_type = ipv6_addr_type(daddr);
-
- if (addr_type & (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_SITELOCAL))
- {
- /*
- * force device match on route lookup
- */
-
- rt_flags |= RTI_DEVRT;
- }
-
- if (skb->localroute)
- {
- rt_flags |= RTI_GATEWAY;
- }
-
- hdr = skb->ipv6_hdr;
-
-
- if (sk)
- {
- np = &sk->net_pinfo.af_inet6;
- }
-
- if (np && np->dest)
- {
- dc = ipv6_dst_check(np->dest, daddr, np->dc_sernum, rt_flags);
- }
- else
- {
- dc = ipv6_dst_route(daddr, dev, rt_flags);
- }
-
- if (dc == NULL)
- {
- ipv6_statistics.Ip6OutNoRoutes++;
- return(-ENETUNREACH);
- }
-
- dev = dc->rt.rt_dev;
-
- if (saddr == NULL)
- {
- struct inet6_ifaddr *ifa;
-
- ifa = ipv6_get_saddr((struct rt6_info *) dc, daddr);
-
- if (ifa == NULL)
- {
- printk(KERN_DEBUG
- "ipv6_xmit: get_saddr failed\n");
- return -ENETUNREACH;
- }
-
- saddr = &ifa->addr;
-
- if (np)
- {
- ipv6_addr_copy(&np->saddr, saddr);
- }
- }
-
- seg_len = skb->tail - ((unsigned char *) hdr);
-
- /*
- * Link Layer headers
- */
-
- skb->sk = sk;
- skb->protocol = __constant_htons(ETH_P_IPV6);
- skb->free = 1;
- skb->dev = dev;
-
- ipv6_redo_mac_hdr(skb, dc->dc_nexthop, seg_len);
-
- /*
- * Fill in the IPv6 header
- */
-
- hdr->version = 6;
- hdr->priority = np ? np->priority : 0;
-
- if (np)
- memcpy(hdr->flow_lbl, (void *) &np->flow_lbl, 3);
- else
- memset(hdr->flow_lbl, 0, 3);
-
- hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
- hdr->nexthdr = proto;
- hdr->hop_limit = np ? np->hop_limit : ipv6_hop_limit;
-
- memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
- memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-
-
- /*
- * Options
- */
-
-
- /*
- * Output the packet
- */
-
- ipv6_statistics.Ip6OutRequests++;
-
- if (dc->rt.rt_output_method)
- {
- (*dc->rt.rt_output_method)(skb, (struct rt6_info *) dc);
- }
- else
- default_output_method(skb, (struct rt6_info *) dc);
-
- /*
- * Update serial number of cached dest_entry or
- * release destination cache entry
- */
-
- if (np)
- {
- np->dest = dc;
- if (dc->rt.fib_node)
- {
- np->dc_sernum = dc->rt.fib_node->fn_sernum;
- }
- }
- else
- {
- ipv6_dst_unlock(dc);
- }
-
- return 0;
-}
-
-/*
- * To avoid extra problems ND packets are send through this
- * routine. It's code duplication but i really want to avoid
- * extra checks since ipv6_build_header is used by TCP (which
- * is for us performace critical)
- */
-
-int ipv6_bld_hdr_2(struct sock *sk, struct sk_buff *skb, struct device *dev,
- struct neighbour *neigh,
- struct in6_addr *saddr, struct in6_addr *daddr,
- int proto, int len)
-{
- struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
- struct ipv6hdr *hdr;
- int hdrlen = 0;
-
- skb->dev = dev;
-
- /* build MAC header */
- hdrlen += ipv6_build_mac_header(skb, dev, neigh, len);
-
- /* build fixed IPv6 header */
-
- if (proto == IPPROTO_RAW)
- return hdrlen;
-
-
- hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
- skb->ipv6_hdr = hdr;
-
- hdr->version = 6;
- hdr->priority = np->priority & 0x0f;
-
- memset(hdr->flow_lbl, 0, 3);
-
- hdr->hop_limit = np->hop_limit;
-
- if (saddr == NULL)
- {
- printk(KERN_DEBUG "bug: bld_hdr called with no saddr\n");
- return -ENETUNREACH;
- }
-
- memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
- memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-
- hdrlen += sizeof(struct ipv6hdr);
-
- hdr->nexthdr = proto;
-
- return hdrlen;
-}
-
-void ipv6_queue_xmit(struct sock *sk, struct device *dev, struct sk_buff *skb,
- int free)
-{
- struct ipv6hdr *hdr;
- u32 seg_len;
-
- hdr = skb->ipv6_hdr;
- skb->sk = sk;
- skb->protocol = __constant_htons(ETH_P_IPV6);
- skb->free=1;
-
- seg_len = skb->tail - ((unsigned char *) hdr);
-
- hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
-
- if (dev == NULL)
- {
- printk(KERN_DEBUG "ipv6_queue_xmit: unknown device\n");
- return;
- }
-
- skb->dev = dev;
-
- ipv6_statistics.Ip6OutRequests++;
-
-
- /*
- * Multicast loopback
- */
-
- if (dev->flags & IFF_UP)
- {
- /*
- * If we have an owner use its priority setting,
- * otherwise use NORMAL
- */
-
- if (sk != NULL)
- {
- dev_queue_xmit(skb, dev, sk->priority);
- }
- else
- {
- dev_queue_xmit(skb, dev, SOPRI_NORMAL);
- }
- }
- else
- {
- if(sk)
- sk->err = ENETDOWN;
-
- ipv6_statistics.Ip6OutDiscards++;
-
- kfree_skb(skb, FREE_WRITE);
- }
-
-}
-
-
-int ipv6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
- struct in6_addr *dest, unsigned short int length,
- struct in6_addr *saddr, struct device *dev,
- struct ipv6_options *opt, int proto,
- int noblock)
-{
- rt6_output_method_t output_method = default_output_method;
- int hlimit;
- struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
- struct dest_entry *dc = NULL;
- struct in6_addr *daddr = dest;
- struct ipv6hdr *hdr;
- struct neighbour *neigh;
- int addr_type;
- int pktlength;
- int pmtu = 0;
- int rt_flags = 0;
- int error;
-
- if (opt && opt->srcrt)
- {
- struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
- daddr = rt0->addr;
- }
-
- addr_type = ipv6_addr_type(daddr);
- if (addr_type & IPV6_ADDR_MULTICAST)
- {
- hlimit = np->mcast_hops;
- if (dev == NULL)
- {
- dev = np->mc_if;
- }
- }
- else
- hlimit = np->hop_limit;
-
- if (addr_type & (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_SITELOCAL |
- IPV6_ADDR_MULTICAST))
- {
- /*
- * force device match on route lookup
- */
-
- rt_flags |= RTI_DEVRT;
- }
-
- if (sk->localroute)
- {
- rt_flags |= RTI_GATEWAY;
- }
-
- if (np->dest)
- {
- np->dest = ipv6_dst_check(np->dest, daddr, np->dc_sernum,
- rt_flags);
-
- dc = np->dest;
-
- if (dc && dc->rt.fib_node)
- {
- np->dc_sernum = dc->rt.fib_node->fn_sernum;
- }
- else
- {
- printk(KERN_WARNING "dc entry not in table\n");
- }
- }
- else
- {
- dc = ipv6_dst_route(daddr, dev, rt_flags);
- }
-
- if (dc == NULL)
- {
- if ((addr_type & IPV6_ADDR_MULTICAST) && dev)
- {
- neigh = NULL;
- pmtu = dev->mtu;
- }
- else
- {
- ipv6_statistics.Ip6OutNoRoutes++;
- return(-ENETUNREACH);
- }
- }
- else
- {
- neigh = dc->dc_nexthop;
- dev = neigh->dev;
-
- if (dc->rt.rt_output_method)
- {
- output_method = dc->rt.rt_output_method;
- }
-
- if (dc->dc_flags & DCF_PMTU)
- pmtu = dc->dc_pmtu;
- else
- pmtu = dev->mtu;
- }
-
-
- if (saddr == NULL)
- {
- struct inet6_ifaddr *ifa;
-
- ifa = ipv6_get_saddr((struct rt6_info *) dc, daddr);
-
- if (ifa == NULL)
- {
- printk(KERN_DEBUG
- "ipv6_build_xmit: get_saddr failed\n");
- return -ENETUNREACH;
- }
-
- saddr = &ifa->addr;
- }
-
- if (dc && np->dest == NULL)
- {
- ipv6_dst_unlock(dc);
- }
-
- pktlength = length;
-
- if (!sk->ip_hdrincl)
- {
- pktlength += sizeof(struct ipv6hdr);
- if (opt)
- {
- pktlength += opt->opt_flen + opt->opt_nflen;
- }
- }
-
-
- dev_lock_list();
-
- /*
- * reminder: don't allow fragmentation for IPPROTO_RAW
- */
-
-
- if (pktlength <= pmtu)
- {
- struct sk_buff *skb =
- sock_alloc_send_skb(sk, pktlength+15+
- dev->hard_header_len,
- 0, noblock, &error);
-
- if (skb == NULL)
- {
- ipv6_statistics.Ip6OutDiscards++;
- dev_unlock_list();
- return error;
-
- }
-
- skb->dev=dev;
- skb->protocol = htons(ETH_P_IPV6);
- skb->free=1;
- skb->when=jiffies;
- skb->sk=sk;
- skb->arp=0;
-
- /* build the mac header... */
- ipv6_build_mac_header(skb, dev, neigh, pktlength);
-
- hdr = (struct ipv6hdr *) skb->tail;
-
- if (!sk->ip_hdrincl)
- {
- skb_put(skb, sizeof(struct ipv6hdr));
- skb->ipv6_hdr = hdr;
-
- hdr->version = 6;
- hdr->priority = np->priority;
-
- memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
-
- hdr->payload_len = htons(pktlength -
- sizeof(struct ipv6hdr));
-
- hdr->hop_limit = hlimit;
-
- memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
- memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-
- if (opt && opt->srcrt)
- {
- hdr->nexthdr = ipv6opt_bld_rthdr(skb, opt,
- dest, proto);
-
- }
- else
- hdr->nexthdr = proto;
- }
-
- skb_put(skb, length);
- error = getfrag(data, &hdr->saddr,
- ((char *) hdr) + (pktlength - length),
- 0, length);
-
- if (!error)
- {
- ipv6_statistics.Ip6OutRequests++;
- (*output_method)(skb, (struct rt6_info *) dc);
- } else
- {
- error = -EFAULT;
- kfree_skb(skb, FREE_WRITE);
- }
-
- dev_unlock_list();
- return error;
- }
- else
- {
- /*
- * Fragmentation
- */
-
- /*
- * Extension header order:
- * Hop-by-hop -> Routing -> Fragment -> rest (...)
- *
- * We must build the non-fragmented part that
- * will be in every packet... this also means
- * that other extension headers (Dest, Auth, etc)
- * must be considered in the data to be fragmented
- */
-
- struct sk_buff *last_skb;
- struct frag_hdr *fhdr;
- int unfrag_len;
- int payl_len;
- int frag_len;
- int last_len;
- int nfrags;
- int err;
- int fhdr_dist;
- __u32 id;
-
- if (sk->ip_hdrincl)
- {
- return -EMSGSIZE;
- }
-
- id = ipv6_fragmentation_id++;
-
- unfrag_len = sizeof(struct ipv6hdr) + sizeof(struct frag_hdr);
- payl_len = length;
-
- if (opt)
- {
- unfrag_len += opt->opt_nflen;
- payl_len += opt->opt_flen;
- }
-
- nfrags = payl_len / ((pmtu - unfrag_len) & ~0x7);
-
- /*
- * Length of fragmented part on every packet but
- * the last must be an:
- * "integer multiple of 8 octects".
- */
-
- frag_len = (pmtu - unfrag_len) & ~0x7;
-
- /*
- * We must send from end to start because of
- * UDP/ICMP checksums. We do a funny trick:
- * fill the last skb first with the fixed
- * header (and its data) and then use it
- * to create the following segments and send it
- * in the end. If the peer is checking the M_flag
- * to trigger the reassembly code then this
- * might be a good idea.
- */
-
- last_len = payl_len - (nfrags * frag_len);
-
- if (last_len == 0)
- {
- last_len = frag_len;
- nfrags--;
- }
-
- last_skb = sock_alloc_send_skb(sk, unfrag_len + frag_len +
- dev->hard_header_len + 15,
- 0, noblock, &err);
-
- if (last_skb == NULL)
- {
- dev_unlock_list();
- return err;
- }
-
- last_skb->dev=dev;
- last_skb->protocol = htons(ETH_P_IPV6);
- last_skb->free=1;
- last_skb->when=jiffies;
- last_skb->sk=sk;
- last_skb->arp=0;
-
- /*
- * build the mac header...
- */
- ipv6_build_mac_header(last_skb, dev, neigh,
- unfrag_len + frag_len);
-
- hdr = (struct ipv6hdr *) skb_put(last_skb,
- sizeof(struct ipv6hdr));
- last_skb->ipv6_hdr = hdr;
-
- hdr->version = 6;
- hdr->priority = np->priority;
-
- memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
- hdr->payload_len = htons(unfrag_len + frag_len -
- sizeof(struct ipv6hdr));
-
- hdr->hop_limit = hlimit;
-
- hdr->nexthdr = NEXTHDR_FRAGMENT;
-
- memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
- memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-
- if (opt && opt->srcrt)
- {
- hdr->nexthdr = ipv6opt_bld_rthdr(last_skb, opt, dest,
- NEXTHDR_FRAGMENT);
- }
-
- fhdr = (struct frag_hdr *)
- skb_put(last_skb, sizeof(struct frag_hdr));
-
- memset(fhdr, 0, sizeof(struct frag_hdr));
-
- fhdr->nexthdr = proto;
- fhdr->frag_off = ntohs(nfrags * frag_len);
- fhdr->identification = id;
-
- fhdr_dist = (unsigned char *) fhdr - last_skb->data;
-
- error = getfrag(data, &hdr->saddr, last_skb->tail,
- nfrags * frag_len, last_len);
-
- if (!error)
- {
- while (nfrags--)
- {
- struct sk_buff *skb;
-
- struct frag_hdr *fhdr2;
-
- printk(KERN_DEBUG "sending frag %d\n", nfrags);
- skb = skb_copy(last_skb, sk->allocation);
-
- fhdr2 = (struct frag_hdr *)
- (skb->data + fhdr_dist);
-
- /* more flag on */
- fhdr2->frag_off = ntohs(nfrags * frag_len + 1);
-
- /*
- * FIXME:
- * if (nfrags == 0)
- * put rest of headers
- */
-
- error = getfrag(data, &hdr->saddr,
- skb_put(skb, frag_len),
- nfrags * frag_len, frag_len);
-
- if (error)
- {
- kfree_skb(skb, FREE_WRITE);
- break;
- }
-
- ipv6_statistics.Ip6OutRequests++;
- (*output_method)(skb, (struct rt6_info *) dc);
- }
- }
-
- if (error)
- {
- kfree_skb(last_skb, FREE_WRITE);
- dev_unlock_list();
- return -EFAULT;
- }
-
- printk(KERN_DEBUG "sending last frag \n");
-
- hdr->payload_len = htons(unfrag_len + last_len -
- sizeof(struct ipv6hdr));
-
- /*
- * update last_skb to reflect the getfrag we did
- * on start.
- */
- last_skb->tail += last_len;
- last_skb->len += last_len;
-
- /*
- * toss the mac header out and rebuild it.
- * needed because of the different frame length.
- * ie: not needed for an ethernet.
- */
-
- if (dev->type != ARPHRD_ETHER && last_len != frag_len)
- {
- ipv6_redo_mac_hdr(last_skb, neigh,
- unfrag_len + last_len);
- }
-
- ipv6_statistics.Ip6OutRequests++;
- (*output_method)(last_skb, (struct rt6_info *) dc);
-
- dev_unlock_list();
- return 0;
- }
- return -1;
-}
-
-static int pri_values[4] =
-{
- SOPRI_BACKGROUND,
- SOPRI_NORMAL,
- SOPRI_NORMAL,
- SOPRI_INTERACTIVE
-};
-
-void ipv6_forward(struct sk_buff *skb, struct device *dev, int flags)
-{
- struct neighbour *neigh;
- struct dest_entry *dest;
- int priority;
- int rt_flags;
- int size;
- int pmtu;
-
- if (skb->ipv6_hdr->hop_limit <= 1)
- {
- icmpv6_send(skb, ICMPV6_TIME_EXCEEDED, ICMPV6_EXC_HOPLIMIT,
- 0, dev);
-
- kfree_skb(skb, FREE_READ);
- return;
- }
-
- skb->ipv6_hdr->hop_limit--;
-
- if (ipv6_addr_type(&skb->ipv6_hdr->saddr) & IPV6_ADDR_LINKLOCAL)
- {
- printk(KERN_DEBUG "ipv6_forward: link local source addr\n");
- icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_NOT_NEIGHBOUR,
- 0, dev);
- kfree_skb(skb, FREE_READ);
- return;
- }
-
- rt_flags = RTF_MODIFIED;
-
- if ((flags & IP6_FW_STRICT))
- {
- rt_flags |= RTF_GATEWAY;
- }
-
- dest = ipv6_dst_route(&skb->ipv6_hdr->daddr, NULL, rt_flags);
-
- if (dest == NULL)
- {
- int code;
-
- if (flags & IP6_FW_STRICT)
- code = ICMPV6_NOT_NEIGHBOUR;
- else
- code = ICMPV6_NOROUTE;
-
- icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0, dev);
-
- kfree_skb(skb, FREE_READ);
- return;
- }
-
- neigh = dest->dc_nexthop;
-
- if (neigh->dev == dev && (dev->flags & IFF_MULTICAST) &&
- !(flags & IP6_FW_SRCRT))
- {
- struct in6_addr *target = NULL;
-
- /*
- * outgoing device equal to incoming device
- * send a redirect
- */
-
- if ((dest->dc_flags & RTF_GATEWAY))
- {
- target = &neigh->addr;
- }
- else
- {
- target = &skb->ipv6_hdr->daddr;
- }
-
- ndisc_send_redirect(skb, neigh, target);
- }
-
- pmtu = neigh->dev->mtu;
-
- size = sizeof(struct ipv6hdr) + ntohs(skb->ipv6_hdr->payload_len);
-
- if (size > pmtu)
- {
- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, pmtu, dev);
- kfree_skb(skb, FREE_READ);
- return;
- }
-
- ipv6_dst_unlock(dest);
-
- if (skb_headroom(skb) < neigh->dev->hard_header_len)
- {
- struct sk_buff *buff;
-
- buff = alloc_skb(neigh->dev->hard_header_len + skb->len + 15,
- GFP_ATOMIC);
-
- if (buff == NULL)
- {
- return;
- }
-
- skb_reserve(buff, (neigh->dev->hard_header_len + 15) & ~15);
-
- buff->protocol = __constant_htons(ETH_P_IPV6);
- buff->free = 1;
- buff->h.raw = skb_put(buff, size);
-
- memcpy(buff->h.raw, skb->ipv6_hdr, size);
- buff->ipv6_hdr = (struct ipv6hdr *) buff->h.raw;
- kfree_skb(skb, FREE_READ);
- skb = buff;
- }
-
- ipv6_redo_mac_hdr(skb, neigh, size);
-
- priority = skb->ipv6_hdr->priority;
-
- priority = (priority & 0x7) >> 1;
- priority = pri_values[priority];
-
- if (dev->flags & IFF_UP)
- {
- dev_queue_xmit(skb, neigh->dev, priority);
- }
- else
- {
- ipv6_statistics.Ip6OutDiscards++;
- kfree_skb(skb, FREE_READ);
- }
-}
-
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/ipv6_route.c b/net/ipv6/ipv6_route.c
deleted file mode 100644
index e68990a0f..000000000
--- a/net/ipv6/ipv6_route.c
+++ /dev/null
@@ -1,2056 +0,0 @@
-/*
- * IPv6 routing table
- * Linux INET6 implementation
- *
- * Authors:
- * Pedro Roque <roque@di.fc.ul.pt>
- *
- * 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.
- */
-
-/*
- * Changes:
- *
- * Masaki Hirabaru : Fix for /proc info > pagesize
- * <masaki@merit.edu>
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/sched.h>
-#include <linux/net.h>
-#include <linux/route.h>
-#include <linux/netdevice.h>
-#include <linux/in6.h>
-
-#ifdef CONFIG_PROC_FS
-#include <linux/proc_fs.h>
-#endif
-
-#include <net/tcp.h>
-#include <net/sock.h>
-#include <net/snmp.h>
-
-#include <net/ipv6.h>
-#include <net/ndisc.h>
-#include <net/protocol.h>
-#include <net/ipv6_route.h>
-#include <net/addrconf.h>
-
-#include <net/netlink.h>
-
-#include <asm/uaccess.h>
-
-/*
- * Routing Table
- *
- * simplified version of a radix tree
- *
- * - every node shares it's acestors prefix
- * - the tree is ordered from less to most specific mask
- * - default routes are handled apart
- *
- * this facilitates recursion a lot
- */
-
-static struct rt6_info null_entry = {
- NULL, NULL,
- {{{0}}},
- 0, 1,
- NULL, NULL,
- 0, 0, RTF_REJECT
-};
-
-struct fib6_node routing_table = {
- NULL, NULL, NULL, &null_entry,
- 0, RTN_ROOT, 0
-};
-
-struct rt6_info *default_rt_list = NULL;
-struct rt6_info *loopback_rt = NULL;
-
-/*
- * last_resort_rt - no routers present.
- * Assume all destinations on link.
- */
-struct rt6_info *last_resort_rt = NULL;
-
-static struct rt6_req request_queue = {
- 0, NULL, &request_queue, &request_queue
-};
-
-
-/*
- * A routing update causes an increase of the serial number on the
- * afected subtree. This allows for cached routes to be asynchronously
- * tested when modifications are made to the destination cache as a
- * result of redirects, path MTU changes, etc.
- */
-
-static __u32 rt_sernum = 0;
-
-static atomic_t rt6_lock = 0;
-static int rt6_bh_mask = 0;
-
-#define RT_BH_REQUEST 1
-#define RT_BH_GC 2
-
-static void __rt6_run_bh(void);
-
-typedef void (*f_pnode)(struct fib6_node *fn, void *);
-
-static void rt6_walk_tree(f_pnode func, void * arg, int filter);
-static void rt6_rt_timeout(struct fib6_node *fn, void *arg);
-static int rt6_msgrcv(int unit, struct sk_buff *skb);
-
-struct rt6_statistics rt6_stats = {
- 1, 0, 1, 1, 0
-};
-
-static atomic_t rt_clients = 0;
-
-void rt6_timer_handler(unsigned long data);
-
-struct timer_list rt6_gc_timer = {
- NULL,
- NULL,
- 0,
- 0,
- rt6_timer_handler
-};
-
-static __inline__ void rt6_run_bh(void)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- if (rt6_lock == 0 && rt6_bh_mask)
- {
- __rt6_run_bh();
- }
- restore_flags(flags);
-}
-
-/*
- * request queue operations
- * FIFO queue/dequeue
- */
-static __inline__ void rtreq_queue(struct rt6_req * req)
-{
- unsigned long flags;
- struct rt6_req *next = &request_queue;
-
- save_flags(flags);
- cli();
-
- req->prev = next->prev;
- req->prev->next = req;
- next->prev = req;
- req->next = next;
- restore_flags(flags);
-}
-
-static __inline__ struct rt6_req * rtreq_dequeue(void)
-{
- struct rt6_req *next = &request_queue;
- struct rt6_req *head;
-
- head = next->next;
-
- if (head == next)
- {
- return NULL;
- }
-
- head->next->prev = head->prev;
- next->next = head->next;
-
- head->next = NULL;
- head->prev = NULL;
-
- return head;
-}
-
-/*
- * compare "prefix length" bits of an address
- */
-static __inline__ int addr_match(struct in6_addr *a1, struct in6_addr *a2,
- int prefixlen)
-{
- int pdw;
- int pbi;
-
- pdw = prefixlen >> 0x05; /* num of whole __u32 in prefix */
- pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */
-
- if (pdw)
- {
- if (memcmp(a1, a2, pdw << 2))
- return 0;
- }
-
- if (pbi)
- {
- __u32 w1, w2;
- __u32 mask;
-
- w1 = a1->s6_addr32[pdw];
- w2 = a2->s6_addr32[pdw];
-
- mask = htonl((0xffffffff) << (0x20 - pbi));
-
- if ((w1 ^ w2) & mask)
- return 0;
- }
-
- return 1;
-}
-
-/*
- * test bit. range [0-127]
- */
-
-static __inline__ int addr_bit_set(struct in6_addr *addr, int fn_bit)
-{
- int dw;
- __u32 b1;
- __u32 mask;
- int bit = fn_bit;
-
- dw = bit >> 0x05;
-
- b1 = addr->s6_addr32[dw];
-
- bit = ~bit;
- bit &= 0x1f;
- mask = htonl(1 << bit);
- return (b1 & mask);
-}
-
-static __inline__ int addr_bit_equal(struct in6_addr *a1, struct in6_addr *a2,
- int fn_bit)
-{
- int dw;
- __u32 b1, b2;
- __u32 mask;
- int bit = fn_bit;
-
- dw = bit >> 0x05;
-
- b1 = a1->s6_addr32[dw];
- b2 = a2->s6_addr32[dw];
-
- bit = ~bit;
- bit &= 0x1f;
- mask = htonl(1 << bit);
- return !((b1 ^ b2) & mask);
-}
-
-/*
- * find the first different bit between two addresses
- */
-static __inline__ int addr_diff(struct in6_addr *a1, struct in6_addr *a2)
-{
- int i;
-
- for (i = 0; i<4; i++)
- {
- __u32 b1, b2;
- __u32 xb;
-
- b1 = a1->s6_addr32[i];
- b2 = a2->s6_addr32[i];
-
- xb = b1 ^ b2;
-
- if (xb)
- {
- int res = 0;
- int j=31;
-
- xb = ntohl(xb);
-
- while (test_bit(j, &xb) == 0)
- {
- res++;
- j--;
- }
-
- return (i * 32 + res);
- }
- }
-
- /*
- * bit values are in range [0-127]
- * 128 is an ilegal value as we should *never* get to
- * this point since that would mean the addrs are equal
- */
- return 128;
-}
-
-/*
- * add a rt to a node that may already contain routes
- * sort routes in ascending metric order so that fib lookup
- * returns the smallest metric by default
- */
-
-static __inline__ void fib6_add_rt2node(struct fib6_node *fn,
- struct rt6_info *rt)
-{
- struct rt6_info *iter, **back;
-
- rt->fib_node = fn;
- back = &fn->leaf;
-
- for (iter = fn->leaf; iter; iter=iter->next)
- {
- if (iter->rt_metric > rt->rt_metric)
- {
- break;
- }
-
- back = &iter->next;
- }
-
- rt->next = iter;
- *back = rt;
-}
-
-/*
- * Routing Table
- */
-
-static int fib6_add_1(struct rt6_info *rt)
-{
- struct fib6_node *fn;
- struct fib6_node *pn = NULL;
- struct fib6_node *in;
- struct fib6_node *ln;
- struct in6_addr *addr;
- __u32 bit;
- __u32 dir = 0;
- __u32 sernum = ++rt_sernum;
- int pbit = rt->rt_prefixlen - 1;
-
- addr = &rt->rt_dst;
-
- /* insert node in tree */
-
- fn = &routing_table;
-
- for (;;)
- {
- if (fn == NULL)
- {
- ln = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
- if (ln == NULL)
- return (-ENOMEM);
-
- memset(ln, 0, sizeof(struct fib6_node));
- ln->fn_bit = pbit;
- ln->fn_flags = RTN_BACKTRACK;
-
- ln->parent = pn;
- ln->leaf = rt;
- ln->fn_sernum = sernum;
- rt->fib_node = ln;
-
- atomic_inc(&rt->rt_ref);
-
- if (dir)
- pn->right = ln;
- else
- pn->left = ln;
-
- rt6_stats.fib_nodes++;
- rt6_stats.fib_route_nodes++;
- rt6_stats.fib_rt_entries++;
-
- return(0);
- }
-
- if (addr_match(&fn->leaf->rt_dst, addr, fn->fn_bit))
- {
- if (pbit == fn->fn_bit && pbit &&
- addr_bit_equal(addr, &fn->leaf->rt_dst,
- rt->rt_prefixlen))
- {
- /* clean up an intermediate node */
- if ((fn->fn_flags & RTN_BACKTRACK) == 0)
- {
- rt_release(fn->leaf);
- fn->leaf = NULL;
- fn->fn_flags |= RTN_BACKTRACK;
- }
-
- fib6_add_rt2node(fn, rt);
- fn->fn_sernum = sernum;
- atomic_inc(&rt->rt_ref);
-
- rt6_stats.fib_route_nodes++;
- rt6_stats.fib_rt_entries++;
-
- return 0;
- }
-
- if (pbit > fn->fn_bit || pbit == 0)
- {
- /* walk down on tree */
-
- fn->fn_sernum = sernum;
-
- dir = addr_bit_set(addr, fn->fn_bit);
- pn = fn;
- fn = dir ? fn->right: fn->left;
-
- continue;
- }
- }
-
- /*
- * split since we don't have a common prefix anymore or
- * we have a less significant route.
- * we've to insert an intermediate node on the list
- * this new node will point to the one we need to create
- * and the current
- */
-
- pn = fn->parent;
-
- /* find 1st bit in difference between the 2 addrs */
- bit = addr_diff(addr, &fn->leaf->rt_dst);
-
-
- /*
- * (intermediate)
- * / \
- * (new leaf node) (old node)
- */
- if (rt->rt_prefixlen > bit)
- {
- in = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
- if (in == NULL)
- return (-ENOMEM);
-
- memset(in, 0, sizeof(struct fib6_node));
-
- /*
- * new intermediate node.
- * RTN_BACKTRACK will
- * be off since that an address that chooses one of
- * the branches would not match less specific routes
- * int the other branch
- */
-
- in->fn_bit = bit;
- in->parent = pn;
- in->leaf = rt;
- in->fn_sernum = sernum;
- atomic_inc(&rt->rt_ref);
-
- /* leaf node */
- ln = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
- if (ln == NULL)
- {
- kfree(in);
- return (-ENOMEM);
- }
-
- /* update parent pointer */
- if (dir)
- pn->right = in;
- else
- pn->left = in;
-
- memset(ln, 0, sizeof(struct fib6_node));
- ln->fn_bit = pbit;
- ln->fn_flags = RTN_BACKTRACK;
-
- ln->parent = in;
- fn->parent = in;
-
- ln->leaf = rt;
- ln->fn_sernum = sernum;
- atomic_inc(&rt->rt_ref);
-
- rt->fib_node = ln;
-
- if (addr_bit_set(addr, bit))
- {
- in->right = ln;
- in->left = fn;
- }
- else
- {
- in->left = ln;
- in->right = fn;
- }
-
- rt6_stats.fib_nodes += 2;
- rt6_stats.fib_route_nodes++;
- rt6_stats.fib_rt_entries++;
-
- return 0;
- }
-
- /*
- * (new leaf node)
- * / \
- * (old node) NULL
- */
-
- ln = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
- if (ln == NULL)
- return (-ENOMEM);
-
- memset(ln, 0, sizeof(struct fib6_node));
- ln->fn_bit = pbit;
- ln->fn_flags = RTN_BACKTRACK;
-
-
- ln->parent = pn;
- ln->leaf = rt;
- ln->fn_sernum = sernum;
- atomic_inc(&rt->rt_ref);
-
- rt->fib_node = ln;
-
- if (dir)
- pn->right = ln;
- else
- pn->left = ln;
-
-
- if (addr_bit_set(&fn->leaf->rt_dst, pbit))
- ln->right = fn;
- else
- ln->left = fn;
-
- fn->parent = ln;
-
- rt6_stats.fib_nodes++;
- rt6_stats.fib_route_nodes++;
- rt6_stats.fib_rt_entries++;
-
- return(0);
- }
-
- return (-1);
-}
-
-static struct rt6_info * fib6_lookup_1(struct in6_addr *addr, int flags)
-{
- struct fib6_node *fn, *next;
- int dir;
-
- fn = &routing_table;
-
- for (;;)
- {
- dir = addr_bit_set(addr, fn->fn_bit);
-
- next = dir ? fn->right: fn->left;
-
- if (next)
- {
- fn = next;
- continue;
- }
-
- break;
- }
-
-
- while ((fn->fn_flags & RTN_ROOT) == 0)
- {
- if (fn->fn_flags & RTN_BACKTRACK)
- {
- if (addr_match(&fn->leaf->rt_dst, addr,
- fn->leaf->rt_prefixlen))
- {
- struct rt6_info *rt;
-
- for (rt = fn->leaf; rt; rt = rt->next)
- {
- if ((rt->rt_flags & flags) == 0)
- return rt;
- }
- }
- }
-
- fn = fn->parent;
- }
-
- return NULL;
-}
-
-
-
-/*
- * called to trim the tree of intermediate nodes when possible
- */
-
-static void fib6_del_3(struct fib6_node *fn)
-{
- int children = 0;
- int dir = 0;
- int bit;
-
- /*
- * 0 or one children:
- * delete the node
- *
- * 2 children:
- * move the bit down
- */
-
- if (fn->left)
- {
- children++;
- dir = 0;
- }
-
- if (fn->right)
- {
- children++;
- dir = 1;
- }
-
- if (children < 2)
- {
- struct fib6_node *child;
- struct fib6_node *pn;
-
- child = dir ? fn->right : fn->left;
-
- if (fn->parent->left == fn)
- {
- fn->parent->left = child;
- }
- else
- {
- fn->parent->right = child;
- }
-
- if (child)
- {
- child->parent = fn->parent;
- }
-
- /*
- * try to collapse on top
- */
- pn = fn->parent;
- fn->parent = NULL;
-
- if ((pn->fn_flags & (RTN_BACKTRACK | RTN_ROOT)) == 0)
- {
- if (pn->leaf)
- {
- rt_release(pn->leaf);
- pn->leaf = NULL;
- }
- fib6_del_3(pn);
- }
-
- if (fn->fn_flags & RTN_BACKTRACK)
- {
- rt6_stats.fib_route_nodes--;
- }
- rt6_stats.fib_nodes--;
- kfree(fn);
- return;
- }
-
- bit = addr_diff(&fn->left->leaf->rt_dst, &fn->right->leaf->rt_dst);
-
- fn->fn_bit = bit;
- fn->fn_flags &= ~RTN_BACKTRACK;
-
- fn->leaf = fn->left->leaf;
- atomic_inc(&fn->leaf->rt_ref);
-
- rt6_stats.fib_route_nodes--;
-}
-
-static struct fib6_node * fib6_del_2(struct in6_addr *addr, __u32 prefixlen,
- struct in6_addr *gw, struct device *dev)
-{
- struct fib6_node *fn;
-
- for (fn = &routing_table; fn;)
- {
- int dir;
-
- if ((fn->fn_flags & RTN_BACKTRACK) &&
- prefixlen == fn->leaf->rt_prefixlen &&
- addr_match(&fn->leaf->rt_dst, addr, fn->leaf->rt_prefixlen)
- )
- {
- break;
- }
-
- dir = addr_bit_set(addr, fn->fn_bit);
-
- fn = dir ? fn->right: fn->left;
- }
-
- /*
- * if route tree node found
- * search among it's entries
- */
-
- if (fn)
- {
- struct rt6_info *back = NULL;
- struct rt6_info *lf;
-
- for(lf = fn->leaf; lf; lf=lf->next)
- {
- if ((gw && (ipv6_addr_cmp(addr, &lf->rt_dst) == 0)) ||
- (dev && dev == lf->rt_dev))
- {
- /* delete this entry */
- if (back == NULL)
- fn->leaf = lf->next;
- else
- back->next = lf->next;
-
- lf->fib_node = NULL;
- rt_release(lf);
- return fn;
- }
- back = lf;
- }
- }
-
- return NULL;
-}
-
-static struct fib6_node * fib6_del_rt_2(struct rt6_info *rt)
-{
- struct fib6_node *fn;
- struct in6_addr *addr = &rt->rt_dst;
- int prefixlen = rt->rt_prefixlen;
-
- for (fn = &routing_table; fn;)
- {
- int dir;
-
- if ((fn->fn_flags & RTN_BACKTRACK) &&
- prefixlen == fn->leaf->rt_prefixlen &&
- addr_match(&fn->leaf->rt_dst, addr, fn->leaf->rt_prefixlen)
- )
- {
- break;
- }
-
- dir = addr_bit_set(addr, fn->fn_bit);
-
- fn = dir ? fn->right: fn->left;
- }
-
- /*
- * if route tree node found
- * search among its entries
- */
-
- if (fn)
- {
- struct rt6_info **back;
- struct rt6_info *lf;
-
- back = &fn->leaf;
-
- for(lf = fn->leaf; lf; lf=lf->next)
- {
- if (rt == lf)
- {
- /*
- * delete this entry
- */
-
- *back = lf->next;
- rt_release(lf);
- return fn;
- }
- back = &lf->next;
- }
- }
-
- return NULL;
-}
-
-int fib6_del_1(struct in6_addr *addr, __u32 prefixlen, struct in6_addr *gw,
- struct device *dev)
-{
- struct fib6_node *fn;
-
- fn = fib6_del_2(addr, prefixlen, gw, dev);
-
- if (fn == NULL)
- return -ENOENT;
-
- if (fn->leaf == NULL)
- {
- fib6_del_3(fn);
- }
-
- return 0;
-}
-
-int fib6_del_rt(struct rt6_info *rt)
-{
- struct fib6_node *fn;
-
- fn = fib6_del_rt_2(rt);
-
- if (fn == NULL)
- return -ENOENT;
-
- if (fn->leaf == NULL)
- {
- fib6_del_3(fn);
- }
-
- return 0;
-}
-
-static void fib6_flush_1(struct fib6_node *fn, void *p_arg)
-{
- struct rt6_info *rt;
-
- for (rt = fn->leaf; rt;)
- {
- struct rt6_info *itr;
-
- itr = rt;
- rt = rt->next;
- itr->fib_node = NULL;
- rt_release(itr);
- }
-
- if (fn->fn_flags & RTN_BACKTRACK)
- {
- rt6_stats.fib_route_nodes--;
- }
- rt6_stats.fib_nodes--;
- kfree(fn);
-}
-
-void fib6_flush(void)
-{
- rt6_walk_tree(fib6_flush_1, NULL, RT6_FILTER_NONE);
-}
-
-int ipv6_route_add(struct in6_rtmsg *rtmsg)
-{
- struct rt6_info *rt;
- struct device * dev = NULL;
- struct inet6_dev *idev;
- struct rt6_req *request;
- int flags = rtmsg->rtmsg_flags;
-
- idev = ipv6_dev_by_index(rtmsg->rtmsg_ifindex);
- if (idev)
- {
- dev = idev->dev;
- }
-
- rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
- GFP_ATOMIC);
-
- rt6_stats.fib_rt_alloc++;
-
- memset(rt, 0, sizeof(struct rt6_info));
-
- memcpy(&rt->rt_dst, &rtmsg->rtmsg_dst, sizeof(struct in6_addr));
- rt->rt_prefixlen = rtmsg->rtmsg_prefixlen;
-
- if (rt->rt_prefixlen == 0)
- {
- printk(KERN_DEBUG "ip6_fib: zero length route not allowed\n");
- return -EINVAL;
- }
-
- if (flags & (RTF_GATEWAY | RTF_NEXTHOP))
- {
- /* check to see if its an acceptable gateway */
- if (flags & RTF_GATEWAY)
- {
- struct rt6_info *gw_rt;
-
- gw_rt = fibv6_lookup(&rtmsg->rtmsg_gateway, dev,
- RTI_GATEWAY);
-
- if (gw_rt == NULL)
- {
- return -EHOSTUNREACH;
- }
-
- dev = gw_rt->rt_dev;
- }
-
- rt->rt_nexthop = ndisc_get_neigh(dev, &rtmsg->rtmsg_gateway);
-
- if (rt->rt_nexthop == NULL)
- {
- printk(KERN_DEBUG "ipv6_route_add: no nexthop\n");
- kfree(rt);
- return -EINVAL;
- }
-
- rt->rt_dev = dev;
-
- if (loopback_rt == NULL && (dev->flags & IFF_LOOPBACK))
- {
- loopback_rt = rt;
- }
-
- }
- else
- {
- if (dev == NULL)
- {
- printk(KERN_DEBUG "ipv6_route_add: NULL dev\n");
- kfree(rt);
- return -EINVAL;
- }
-
- rt->rt_dev = dev;
- rt->rt_nexthop = NULL;
- }
-
- rt->rt_metric = rtmsg->rtmsg_metric;
- rt->rt_flags = rtmsg->rtmsg_flags;
-
- if (rt->rt_flags & RTF_ADDRCONF)
- {
- rt->rt_expires = rtmsg->rtmsg_info;
- }
-
- request = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
- if (request == NULL)
- {
- printk(KERN_WARNING "ipv6_route_add: kmalloc failed\n");
- return -ENOMEM;
- }
-
- request->operation = RT_OPER_ADD;
- request->ptr = rt;
- request->next = request->prev = NULL;
- rtreq_queue(request);
- rt6_bh_mask |= RT_BH_REQUEST;
-
- rt6_run_bh();
-
- return 0;
-}
-
-int ipv6_route_del(struct in6_rtmsg *rtmsg)
-{
- struct rt6_info * rt;
- int res = -ENOENT;
-
- atomic_inc(&rt6_lock);
-
- rt = fib6_lookup_1(&rtmsg->rtmsg_dst, 0);
-
- if (rt && (rt->rt_prefixlen == rtmsg->rtmsg_prefixlen))
- {
- int test;
-
- start_bh_atomic();
-
- test = (rt6_lock == 1);
-
- if (test)
- {
- res = fib6_del_rt(rt);
- }
- end_bh_atomic();
-
- if (!test)
- {
- struct rt6_req *request;
-
- request = kmalloc(sizeof(struct rt6_req), GFP_KERNEL);
-
- if (!request)
- {
- res = -ENOMEM;
- goto out;
- }
- request->operation = RT_OPER_DEL;
- request->ptr = rt;
- request->next = request->prev = NULL;
- rtreq_queue(request);
- rt6_bh_mask |= RT_BH_REQUEST;
- res = 0;
- }
- }
- out:
- atomic_dec(&rt6_lock);
- rt6_run_bh();
- return res;
-}
-
-/*
- * search the routing table
- * the flags parameter restricts the search to entries where
- * the flag is *not* set
- */
-struct rt6_info * fibv6_lookup(struct in6_addr *addr, struct device *src_dev,
- int flags)
-{
- struct rt6_info *rt;
-
- atomic_inc(&rt6_lock);
-
- if ((rt = fib6_lookup_1(addr, flags)))
- {
- if (src_dev)
- {
- struct rt6_info *sprt;
-
- for (sprt=rt; sprt; sprt=sprt->next)
- {
- if (sprt->rt_dev == src_dev)
- {
- rt = sprt;
- goto out;
- }
- }
-
- if (flags & RTI_DEVRT)
- {
- rt = NULL;
- }
- }
-
- goto out;
- }
-
- if (!(flags & RTI_GATEWAY))
- {
- if ((rt = dflt_rt_lookup()))
- {
- goto out;
- }
-
- rt = last_resort_rt;
- }
- out:
- atomic_dec(&rt6_lock);
- return rt;
-}
-
-/*
- * Destination Cache
- */
-
-struct dest_entry * ipv6_dst_route(struct in6_addr * daddr,
- struct device *src_dev,
- int flags)
-{
- struct dest_entry * dc = NULL;
- struct rt6_info * rt;
-
- atomic_inc(&rt6_lock);
-
- rt = fibv6_lookup(daddr, src_dev, flags);
-
- if (rt == NULL)
- {
- goto exit;
- }
-
- if (rt->rt_nexthop)
- {
- /*
- * We can use the generic route
- * (warning: the pmtu value maybe invalid)
- */
-
- dc = (struct dest_entry *) rt;
- atomic_inc(&rt->rt_use);
- }
- else
- {
- struct rt6_req *request;
-
- if (ipv6_chk_addr(daddr) && !(rt->rt_dev->flags & IFF_LOOPBACK))
- {
- rt = loopback_rt;
-
- if (rt == NULL)
- {
- goto exit;
- }
- }
-
- /*
- * dynamicly allocate a new route
- */
-
- dc = (struct dest_entry *) kmalloc(sizeof(struct dest_entry),
- GFP_ATOMIC);
-
- if (dc == NULL)
- {
- printk(KERN_WARNING "dst_route: kmalloc failed\n");
- goto exit;
- }
-
- rt6_stats.fib_rt_alloc++;
- rt6_stats.fib_dc_alloc++;
-
- memset(dc, 0, sizeof(struct dest_entry));
-
- memcpy(&dc->dc_addr, daddr, sizeof(struct in6_addr));
- dc->rt.rt_prefixlen = 128;
- dc->dc_usecnt = 1;
- dc->rt.rt_metric = rt->rt_metric;
-
- dc->dc_flags = (rt->rt_flags | RTF_HOST | RTI_DYNAMIC |
- RTI_DCACHE | DCF_PMTU);
-
- dc->dc_pmtu = rt->rt_dev->mtu;
- dc->rt.rt_dev = rt->rt_dev;
- dc->rt.rt_output_method = rt->rt_output_method;
- dc->dc_tstamp = jiffies;
- /* add it to the request queue */
-
- request = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-
- if (request == NULL)
- {
- printk(KERN_WARNING "dst_route: kmalloc failed\n");
- dc = NULL;
- goto exit;
- }
-
- dc->dc_nexthop = ndisc_get_neigh(rt->rt_dev, daddr);
-
- rt6_bh_mask |= RT_BH_REQUEST;
-
- request->operation = RT_OPER_ADD;
- request->ptr = (struct rt6_info *) dc;
- request->next = request->prev = NULL;
- rtreq_queue(request);
- }
-
- atomic_inc(&rt_clients);
-
- exit:
-
- atomic_dec(&rt6_lock);
- rt6_run_bh();
-
- return dc;
-}
-
-/*
- * check cache entry for vality...
- * this needs to be done as a inline func that calls
- * ipv6_slow_dst_check if entry is invalid
- */
-
-struct dest_entry * ipv6_dst_check(struct dest_entry *dc,
- struct in6_addr *daddr,
- __u32 sernum, int flags)
-{
- int uptodate = 0;
-
- /*
- * destination cache becomes invalid when routing
- * changes or a more specific dynamic entry is
- * created.
- * if route is removed from table fib_node will
- * become NULL
- */
-
- if (dc->rt.fib_node && (dc->rt.fib_node->fn_sernum == sernum))
- uptodate = 1;
-
- if (uptodate && ((dc->dc_flags & DCF_INVALID) == 0))
- {
- if (dc->dc_nexthop && !(dc->dc_nexthop->flags & NCF_NOARP))
- {
- ndisc_event_send(dc->dc_nexthop, NULL);
- }
- return dc;
- }
-
- /* route for destination may have changed */
-
- ipv6_dst_unlock(dc);
-
- return ipv6_dst_route(daddr, NULL, flags);
-}
-
-void ipv6_dst_unlock(struct dest_entry *dc)
-{
- /*
- * decrement counter and mark entry for deletion
- * if counter reaches 0. we delay deletions in hope
- * we can reuse cache entries.
- */
-
- atomic_dec(&dc->dc_usecnt);
-
- if (dc->dc_usecnt == 0)
- {
-
- if (dc->dc_flags & RTI_DCACHE)
- {
- /*
- * update last usage tstamp
- */
-
- dc->dc_tstamp = jiffies;
- rt6_bh_mask |= RT_BH_GC;
- }
-
- if (dc->rt.rt_ref == 0)
- {
- /*
- * entry out of the routing table
- * pending to be released on last deref
- */
-
- if (dc->dc_nexthop)
- {
- ndisc_dec_neigh(dc->dc_nexthop);
- }
-
- if (dc->dc_flags & RTI_DCACHE)
- {
- rt6_stats.fib_dc_alloc--;
- }
-
- rt6_stats.fib_rt_alloc--;
- kfree(dc);
- }
-
- }
-
- atomic_dec(&rt_clients);
-}
-
-/*
- * Received a packet too big icmp that lowers the mtu for this
- * address. If the route for the destination is genric we create
- * a new route with the apropriate MTU info. The route_add
- * procedure will update the serial number on the generic routes
- * belonging to the afected tree forcing clients to request a route
- * lookup.
- */
-void rt6_handle_pmtu(struct in6_addr *addr, int pmtu)
-{
- struct rt6_info *rt;
- struct rt6_req *req;
- struct dest_entry *dc;
-
- printk(KERN_DEBUG "rt6_handle_pmtu\n");
-
- if (pmtu < 0 || pmtu > 65536)
- {
- printk(KERN_DEBUG "invalid MTU value\n");
- return;
- }
-
- rt = fibv6_lookup(addr, NULL, 0);
-
- if (rt == NULL)
- {
- printk(KERN_DEBUG "rt6_handle_pmtu: route not found\n");
- return;
- }
-
- if (rt->rt_flags & RTI_DCACHE)
- {
- /*
- * we do have a destination cache entry for this
- * address.
- */
-
- dc = (struct dest_entry *) rt;
-
- /*
- * fixme: some sanity checks are likely to be needed
- * here
- */
-
- dc->dc_pmtu = pmtu;
- dc->dc_flags |= DCF_PMTU;
- return;
- }
-
- req = (struct rt6_req *) kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-
- /* now add the new destination cache entry */
-
- dc = (struct dest_entry *) kmalloc(sizeof(struct dest_entry),
- GFP_ATOMIC);
-
- rt6_stats.fib_rt_alloc++;
- rt6_stats.fib_dc_alloc++;
-
- memset(dc, 0, sizeof(struct dest_entry));
-
- memcpy(&dc->dc_addr, addr, sizeof(struct in6_addr));
- dc->rt.rt_prefixlen = 128;
- dc->rt.rt_metric = rt->rt_metric;
-
- dc->dc_flags = (rt->rt_flags | RTI_DYNAMIC | RTI_DCACHE | DCF_PMTU |
- RTF_HOST);
-
- dc->dc_pmtu = pmtu;
- dc->dc_tstamp = jiffies;
-
- dc->dc_nexthop = rt->rt_nexthop;
- atomic_inc(&dc->dc_nexthop->refcnt);
-
- dc->rt.rt_dev = rt->rt_dev;
- dc->rt.rt_output_method = rt->rt_output_method;
-
- req->operation = RT_OPER_ADD;
- req->ptr = (struct rt6_info *) dc;
- req->next = req->prev = NULL;
-
- rtreq_queue(req);
-
- rt6_bh_mask |= RT_BH_REQUEST;
-
- rt6_run_bh();
-}
-
-/*
- * Redirect received: target is nexthop for dest
- */
-struct rt6_info * ipv6_rt_redirect(struct device *dev, struct in6_addr *dest,
- struct in6_addr *target, int on_link)
-
-{
- struct rt6_info *rt;
- struct rt6_req *req;
- int metric;
-
- rt = fibv6_lookup(dest, dev, 0);
-
- if (rt == NULL)
- {
- printk(KERN_WARNING "rt_redirect: unable to locate route\n");
- return NULL;
- }
-
- metric = rt->rt_metric;
-
- if ((rt->rt_flags & RTF_HOST) == 0)
- {
- /* Need to create an host route for this address */
-
- rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
- GFP_ATOMIC);
- memset(rt, 0, sizeof(struct rt6_info));
- ipv6_addr_copy(&rt->rt_dst, dest);
- rt->rt_prefixlen = 128;
- rt->rt_flags = RTF_HOST | RTF_UP;
- rt->rt_dev = dev;
-
- /*
- * clone rt->rt_output_method ?
- */
-
- rt->rt_metric = metric;
-
- rt6_stats.fib_rt_alloc++;
-
- req = (struct rt6_req *) kmalloc(sizeof(struct rt6_req),
- GFP_ATOMIC);
- req->operation = RT_OPER_ADD;
- req->ptr = rt;
- req->next = req->prev = NULL;
-
- rtreq_queue(req);
- rt6_bh_mask |= RT_BH_REQUEST;
- }
- else
- {
- rt->rt_flags |= RTF_MODIFIED;
- }
-
- rt->rt_flags |= RTF_DYNAMIC;
-
- if (on_link)
- {
- rt->rt_flags &= ~RTF_GATEWAY;
- }
- else
- {
- rt->rt_flags |= RTF_GATEWAY;
- }
-
- if (rt->rt_nexthop)
- {
- if (ipv6_addr_cmp(&rt->rt_nexthop->addr, target) == 0)
- {
- atomic_inc(&rt->rt_nexthop->refcnt);
- goto exit;
- }
- else
- {
- ndisc_dec_neigh(rt->rt_nexthop);
- }
- }
-
- rt->rt_nexthop = ndisc_get_neigh(dev, target);
-
- exit:
- rt6_run_bh();
- return rt;
-}
-
-static int dcache_gc_node(struct fib6_node *fn, int timeout)
-{
- struct rt6_info *rt, *back;
- int more = 0;
- unsigned long now = jiffies;
-
- back = NULL;
-
- for (rt = fn->leaf; rt;)
- {
- if ((rt->rt_flags & RTI_DCACHE) && rt->rt_use == 0)
- {
- struct dest_entry *dc;
-
- dc = (struct dest_entry *) rt;
-
- if (now - dc->dc_tstamp > timeout)
- {
- struct rt6_info *old;
-
- old = rt;
-
- rt = rt->next;
-
- if (back == NULL)
- {
- fn->leaf = rt;
- }
- else
- {
- back->next = rt;
- }
-
- old->fib_node = NULL;
- rt_release(old);
- rt6_stats.fib_rt_entries--;
- continue;
- }
- else
- {
- more++;
- }
- }
-
- back = rt;
- rt = rt->next;
- }
-
- if (fn->leaf == NULL)
- {
- return -1;
- }
- return more;
-}
-
-struct dc_gc_args {
- unsigned long timeout;
- int more;
-};
-
-static void dc_garbage_collect(struct fib6_node *fn, void *p_arg)
-{
- struct dc_gc_args * args = (struct dc_gc_args *) p_arg;
-
- if (fn->fn_flags & RTN_BACKTRACK)
- {
- if (fn->fn_bit == 127)
- {
- int more;
-
- more = dcache_gc_node(fn, args->timeout);
-
- if (more == -1)
- {
- if (fn->parent->left == fn)
- fn->parent->left = NULL;
- else
- fn->parent->right = NULL;
-
- kfree(fn);
-
- rt6_stats.fib_nodes--;
- rt6_stats.fib_route_nodes--;
-
- return;
- }
- args->more += more;
- }
- }
- else if (!(fn->fn_flags & RTN_ROOT))
- {
- int children = 0;
- struct fib6_node *chld = NULL;
-
- if (fn->left)
- {
- children++;
- chld = fn->left;
- }
-
- if (fn->right)
- {
- children++;
- chld = fn->right;
- }
-
- if (children <= 1)
- {
- struct fib6_node *pn = fn->parent;
-
- if (pn->left == fn)
- {
- pn->left = chld;
- }
- else
- {
- pn->right = chld;
- }
-
- if (chld)
- {
- chld->parent = pn;
- }
-
- rt_release(fn->leaf);
-
- rt6_stats.fib_nodes--;
- kfree(fn);
- }
- }
-}
-
-/*
- * called with ints off
- */
-
-static void __rt6_run_bh(void)
-{
- static last_gc_run = 0;
-
- if (rt6_bh_mask & RT_BH_REQUEST)
- {
- struct rt6_req *request;
-
- while ((request = rtreq_dequeue()))
- {
- struct rt6_info *rt;
-
- rt = request->ptr;
-
- switch (request->operation) {
- case RT_OPER_ADD:
- fib6_add_1(rt);
- break;
-
- case RT_OPER_DEL:
- fib6_del_rt(rt);
- break;
-
- default:
- printk(KERN_WARNING
- "rt6_run_bh: bad request in queue\n");
- }
-
- kfree(request);
- }
-
- rt6_bh_mask &= ~RT_BH_REQUEST;
- }
-
- if (rt6_bh_mask & RT_BH_GC)
- {
- if (jiffies - last_gc_run > DC_TIME_RUN)
- {
- struct dc_gc_args args;
-
- if (rt6_stats.fib_dc_alloc >= DC_WATER_MARK)
- args.timeout = DC_SHORT_TIMEOUT;
- else
- args.timeout = DC_LONG_TIMEOUT;
-
- args.more = 0;
- rt6_walk_tree(dc_garbage_collect, &args, RT6_FILTER_NONE);
-
- last_gc_run = jiffies;
-
- if (!args.more)
- {
- rt6_bh_mask &= ~RT_BH_GC;
- }
- }
- }
-}
-
-/*
- * Timer for expiring routes learned via addrconf and stale DC
- * entries when there is no network actuvity
- */
-
-void rt6_timer_handler(unsigned long data)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- if (rt6_lock == 0)
- {
- if (rt_clients == 0 && rt6_bh_mask)
- {
- __rt6_run_bh();
- }
-
- /*
- * route expiry
- */
-
- rt6_walk_tree(rt6_rt_timeout, NULL, RT6_FILTER_RTNODES);
- }
-
- restore_flags(flags);
-
- rt6_gc_timer.expires = jiffies + 4 * DC_LONG_TIMEOUT;
- add_timer(&rt6_gc_timer);
-}
-
-/*
- * Check if routes should be timed out.
- * Called from rt6_walk_tree for every node.
- */
-
-static void rt6_rt_timeout(struct fib6_node *fn, void *arg)
-{
- struct rt6_info *rt;
- unsigned long now = jiffies;
-
- for (rt = fn->leaf; rt; rt = rt->next)
- {
- if ((rt->rt_flags & RTF_ADDRCONF) && now > rt->rt_expires)
- {
- struct rt6_req *req;
-
- /*
- * request route deletion. routes will only
- * be deleted after walk_tree completes
- */
-
- req = (struct rt6_req *) kmalloc(sizeof(struct rt6_req),
- GFP_ATOMIC);
- req->operation = RT_OPER_DEL;
- req->ptr = rt;
- req->next = req->prev = NULL;
- }
- }
-}
-
-static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg)
-{
- struct sk_buff *skb;
-
- skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
- if (skb == NULL)
- return;
-
- skb->free = 1;
-
- memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg,
- sizeof(struct in6_rtmsg));
-
- if (netlink_post(NETLINK_ROUTE6, skb))
- {
- kfree_skb(skb, FREE_WRITE);
- }
-}
-
-int ipv6_route_ioctl(unsigned int cmd, void *arg)
-{
- struct in6_rtmsg rtmsg;
- int err;
-
- switch(cmd)
- {
- case SIOCADDRT: /* Add a route */
- case SIOCDELRT: /* Delete a route */
- if (!suser())
- return -EPERM;
- err = copy_from_user(&rtmsg, arg,
- sizeof(struct in6_rtmsg));
- if (err)
- return -EFAULT;
-
- err = (cmd == SIOCDELRT) ? ipv6_route_del(&rtmsg) :
- ipv6_route_add(&rtmsg);
-
- if (err == 0)
- {
- rt6_sndrtmsg(&rtmsg);
- }
- return err;
- }
-
- return -EINVAL;
-}
-
-static void rt6_ifdown_scan(struct fib6_node *fn, void *arg)
-{
- struct rt6_info *rt;
- struct device *dev = (struct device *) arg;
-
- for (rt = fn->leaf; rt; rt=rt->next)
- {
- if (((rt->rt_flags & RTI_DCACHE) == 0) && rt->rt_dev == dev)
- {
- struct rt6_req *req;
-
- req = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
- req->operation = RT_OPER_DEL;
- req->ptr = rt;
- req->next = req->prev = NULL;
- rt6_bh_mask |= RT_BH_REQUEST;
- }
- }
-}
-
-void rt6_ifdown(struct device *dev)
-{
- rt6_walk_tree(rt6_ifdown_scan, (void *) dev, RT6_FILTER_RTNODES);
-}
-
-static void rt6_walk_tree(f_pnode func, void * arg, int filter)
-{
- struct fib6_node *fn;
- /*
- * adquire lock
- * this warranties that the operation will be atomic with
- * respect to the garbage collect routine that also does
- * a tree transversal and tags nodes with the RTN_TAG flag
- */
- atomic_inc(&rt6_lock);
-
- fn = &routing_table;
-
- do {
- if (!(fn->fn_flags & RTN_TAG))
- {
- fn->fn_flags |= RTN_TAG;
-
- if (fn->left)
- {
- fn = fn->left;
- continue;
- }
- }
-
- fn->fn_flags &= ~RTN_TAG;
-
- if (fn->right)
- {
- fn = fn->right;
- continue;
- }
-
- do {
- struct fib6_node *node;
-
- if (fn->fn_flags & RTN_ROOT)
- break;
- node = fn;
- fn = fn->parent;
-
- if (!(node->fn_flags & RTN_TAG) &&
- (!filter || (node->fn_flags & RTN_BACKTRACK)))
- {
- (*func)(node, arg);
- }
-
- } while (!(fn->fn_flags & RTN_TAG));
-
- } while (!(fn->fn_flags & RTN_ROOT) || (fn->fn_flags & RTN_TAG));
-
- atomic_dec(&rt6_lock);
-}
-
-#ifdef CONFIG_PROC_FS
-#define RT6_INFO_LEN (32 + 2 + 32 + 2 + 2 + 2 + 4 + 8 + 7 + 1)
-
-struct rt6_proc_arg {
- char *buffer;
- int offset;
- int length;
- int skip;
- int len;
-};
-
-static void rt6_info_node(struct fib6_node *fn, void *p_arg)
-{
- struct rt6_info *rt;
- struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
-
- for (rt = fn->leaf; rt; rt = rt->next)
- {
- int i;
-
- if (arg->skip < arg->offset / RT6_INFO_LEN)
- {
- arg->skip++;
- continue;
- }
-
- if (arg->len >= arg->length)
- return;
-
- for (i=0; i<16; i++)
- {
- sprintf(arg->buffer + arg->len, "%02x",
- rt->rt_dst.s6_addr[i]);
- arg->len += 2;
- }
- arg->len += sprintf(arg->buffer + arg->len, " %02x ",
- rt->rt_prefixlen);
- if (rt->rt_nexthop)
- {
- for (i=0; i<16; i++)
- {
- sprintf(arg->buffer + arg->len, "%02x",
- rt->rt_nexthop->addr.s6_addr[i]);
- arg->len += 2;
- }
- }
- else
- {
- sprintf(arg->buffer + arg->len,
- "00000000000000000000000000000000");
- arg->len += 32;
- }
- arg->len += sprintf(arg->buffer + arg->len,
- " %02x %02x %02x %04x %8s\n",
- rt->rt_metric, rt->rt_use,
- rt->rt_ref, rt->rt_flags,
- rt->rt_dev ? rt->rt_dev->name : "");
- }
-}
-
-static int rt6_proc_info(char *buffer, char **start, off_t offset, int length,
- int dummy)
-{
- struct rt6_proc_arg arg;
- struct fib6_node sfn;
- arg.buffer = buffer;
- arg.offset = offset;
- arg.length = length;
- arg.skip = 0;
- arg.len = 0;
-
- rt6_walk_tree(rt6_info_node, &arg, RT6_FILTER_RTNODES);
-
- sfn.leaf = default_rt_list;
- rt6_info_node(&sfn, &arg);
-
- sfn.leaf = last_resort_rt;
- rt6_info_node(&sfn, &arg);
-
- *start = buffer;
-
- if (offset)
- *start += offset % RT6_INFO_LEN;
-
- arg.len -= offset % RT6_INFO_LEN;
-
- if (arg.len > length)
- arg.len = length;
-
- return arg.len;
-}
-
-
-static int rt6_proc_stats(char *buffer, char **start, off_t offset, int length,
- int dummy)
-{
- int len;
-
- len = sprintf(buffer, "%04x %04x %04x %04x %04x\n",
- rt6_stats.fib_nodes, rt6_stats.fib_route_nodes,
- rt6_stats.fib_rt_alloc, rt6_stats.fib_rt_entries,
- rt6_stats.fib_dc_alloc);
-
- len -= offset;
-
- if (len > length)
- len = length;
-
- *start = buffer + offset;
-
- return len;
-}
-
-#endif /* CONFIG_PROC_FS */
-
-/*
- * init/cleanup code
- *
- */
-
-void ipv6_route_init(void)
-{
-#ifdef CONFIG_PROC_FS
- proc_net_register(&(struct proc_dir_entry) {
- PROC_NET_RT6, 10, "ipv6_route",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- rt6_proc_info
- });
- proc_net_register(&(struct proc_dir_entry) {
- PROC_NET_RT6_STATS, 9, "rt6_stats",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- rt6_proc_stats
- });
-
-#endif
- rt6_gc_timer.expires = jiffies + 4 * DC_LONG_TIMEOUT;
- add_timer(&rt6_gc_timer);
- netlink_attach(NETLINK_ROUTE6, rt6_msgrcv);
-}
-
-#ifdef MODULE
-void ipv6_route_cleanup(void)
-{
- proc_net_unregister(PROC_NET_RT6);
- proc_net_unregister(PROC_NET_RT6_STATS);
- netlink_detach(NETLINK_ROUTE6);
- del_timer(&rt6_gc_timer);
- fib6_flush();
-}
-#endif
-
-/*
- * NETLINK interface
- * routing socket moral equivalent
- */
-
-static int rt6_msgrcv(int unit, struct sk_buff *skb)
-{
- int count = 0;
- struct in6_rtmsg *rtmsg;
-
- while (skb->len)
- {
- if (skb->len < sizeof(struct in6_rtmsg))
- {
- count = -EINVAL;
- goto out;
- }
-
- rtmsg = (struct in6_rtmsg *) skb->data;
- skb_pull(skb, sizeof(struct in6_rtmsg));
- count += sizeof(struct in6_rtmsg);
-
- switch (rtmsg->rtmsg_type) {
- case RTMSG_NEWROUTE:
- ipv6_route_add(rtmsg);
- break;
- case RTMSG_DELROUTE:
- ipv6_route_del(rtmsg);
- break;
- default:
- count = -EINVAL;
- goto out;
- }
- }
-
- out:
- kfree_skb(skb, FREE_READ);
- return count;
-}
-
-void rt6_sndmsg(__u32 type, struct in6_addr *dst, struct in6_addr *gw,
- __u16 plen, struct device *dev, __u16 metric, __u16 flags)
-{
- struct sk_buff *skb;
- struct in6_rtmsg *msg;
- int ifindex = 0;
-
- skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
- if (skb == NULL)
- return;
-
- skb->free = 1;
-
- msg = (struct in6_rtmsg *) skb_put(skb, sizeof(struct in6_rtmsg));
-
- msg->rtmsg_type = type;
-
- if (dst)
- {
- ipv6_addr_copy(&msg->rtmsg_dst, dst);
- }
- else
- memset(&msg->rtmsg_dst, 0, sizeof(struct in6_addr));
-
- if (gw)
- {
- ipv6_addr_copy(&msg->rtmsg_gateway, gw);
- }
- else
- memset(&msg->rtmsg_gateway, 0, sizeof(struct in6_addr));
-
- msg->rtmsg_prefixlen = plen;
- msg->rtmsg_metric = metric;
-
- if (dev)
- {
- struct inet6_dev *idev;
-
- idev = ipv6_get_idev(dev);
- if (idev)
- {
- ifindex = idev->if_index;
- }
- }
-
- msg->rtmsg_ifindex = ifindex;
-
- msg->rtmsg_flags = flags;
-
- if (netlink_post(NETLINK_ROUTE6, skb))
- {
- kfree_skb(skb, FREE_WRITE);
- }
-}
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 7ae830876..88920bb73 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -7,14 +7,21 @@
*
* Based on linux/net/ipv4/ip_sockglue.c
*
- * $Id: ipv6_sockglue.c,v 1.12 1996/10/29 22:45:53 roque Exp $
+ * $Id: ipv6_sockglue.c,v 1.11 1997/04/20 09:44:33 davem Exp $
*
* 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.
+ *
+ * FIXME: Make the setsockopt code POSIX compliant: That is
+ *
+ * o Return -EINVAL for setsockopt of short lengths
+ * o Truncate getsockopt returns
+ * o Return an optlen of the truncated length if need be
*/
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -34,7 +41,7 @@
#include <net/ndisc.h>
#include <net/protocol.h>
#include <net/transp_v6.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/inet_common.h>
#include <net/sit.h>
@@ -46,7 +53,7 @@
struct ipv6_mib ipv6_statistics={0, };
struct packet_type ipv6_packet_type =
{
- 0,
+ __constant_htons(ETH_P_IPV6),
NULL, /* All devices */
ipv6_rcv,
NULL,
@@ -67,17 +74,14 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
{
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
int val, err;
- int retv = -EOPNOTSUPP;
+ int retv = -ENOPROTOOPT;
if(level!=SOL_IPV6)
goto out;
- if (optval == NULL)
- {
+ if (optval == NULL) {
val=0;
- }
- else
- {
+ } else {
err = get_user(val, (int *) optval);
if(err)
return err;
@@ -87,42 +91,33 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
switch (optname) {
case IPV6_ADDRFORM:
- if (val == PF_INET)
- {
+ if (val == PF_INET) {
if (sk->protocol != IPPROTO_UDP &&
sk->protocol != IPPROTO_TCP)
- {
goto out;
- }
- if (sk->state != TCP_ESTABLISHED)
- {
+ if (sk->state != TCP_ESTABLISHED) {
retv = ENOTCONN;
goto out;
}
- if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED))
- {
+ if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
retv = -EADDRNOTAVAIL;
goto out;
}
- if (sk->protocol == IPPROTO_TCP)
- {
+ if (sk->protocol == IPPROTO_TCP) {
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
sk->prot = &tcp_prot;
tp->af_specific = &ipv4_specific;
- }
- else
- {
+ sk->socket->ops = &inet_stream_ops;
+ } else {
sk->prot = &udp_prot;
+ sk->socket->ops = &inet_dgram_ops;
}
- sk->socket->ops = &inet_proto_ops;
retv = 0;
- }
- else
- {
+ } else {
retv = -EINVAL;
}
break;
@@ -132,13 +127,15 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
retv = 0;
break;
+ case IPV6_HOPLIMIT:
+ np->rxhlim = val;
+ retv = 0;
+ break;
+
case IPV6_UNICAST_HOPS:
if (val > 255)
- {
retv = -EINVAL;
- }
- else
- {
+ else {
np->hop_limit = val;
retv = 0;
}
@@ -146,11 +143,8 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
case IPV6_MULTICAST_HOPS:
if (val > 255)
- {
retv = -EINVAL;
- }
- else
- {
+ else {
np->mcast_hops = val;
retv = 0;
}
@@ -168,23 +162,19 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
if(err)
return -EFAULT;
- if (ipv6_addr_any(&addr))
- {
- np->mc_if = NULL;
- }
- else
- {
+ if (ipv6_addr_any(&addr)) {
+ np->oif = NULL;
+ } else {
struct inet6_ifaddr *ifp;
ifp = ipv6_chk_addr(&addr);
- if (ifp == NULL)
- {
+ if (ifp == NULL) {
retv = -EADDRNOTAVAIL;
break;
}
- np->mc_if = ifp->idev->dev;
+ np->oif = ifp->idev->dev;
}
retv = 0;
break;
@@ -200,8 +190,8 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
if(err)
return -EFAULT;
- if (mreq.ipv6mr_ifindex == 0)
- {
+ if (mreq.ipv6mr_ifindex == 0) {
+#if 0
struct in6_addr mcast;
struct dest_entry *dc;
@@ -214,34 +204,22 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
dev = dc->rt.rt_dev;
ipv6_dst_unlock(dc);
}
- }
- else
- {
- struct inet6_dev *idev;
-
- if ((idev = ipv6_dev_by_index(mreq.ipv6mr_ifindex)))
- {
- dev = idev->dev;
- }
+#endif
+ } else {
+ dev = dev_get_by_index(mreq.ipv6mr_ifindex);
}
if (dev == NULL)
- {
return -ENODEV;
- }
if (optname == IPV6_ADD_MEMBERSHIP)
- {
retv = ipv6_sock_mc_join(sk, dev, &mreq.ipv6mr_multiaddr);
- }
else
- {
retv = ipv6_sock_mc_drop(sk, dev, &mreq.ipv6mr_multiaddr);
- }
- }
}
+ };
- out:
+out:
return retv;
}
@@ -251,7 +229,7 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval,
return 0;
}
-#ifdef MODULE
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
/*
* sysctl registration functions defined in sysctl_net_ipv6.c
@@ -263,17 +241,15 @@ extern void ipv6_sysctl_unregister(void);
void ipv6_init(void)
{
- ipv6_packet_type.type = ntohs(ETH_P_IPV6);
-
dev_add_pack(&ipv6_packet_type);
-#ifdef MODULE
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
ipv6_sysctl_register();
#endif
register_netdevice_notifier(&ipv6_dev_notf);
- ipv6_route_init();
+ ip6_route_init();
}
#ifdef MODULE
@@ -281,15 +257,13 @@ void ipv6_cleanup(void)
{
unregister_netdevice_notifier(&ipv6_dev_notf);
dev_remove_pack(&ipv6_packet_type);
+#ifdef CONFIG_SYSCTL
ipv6_sysctl_unregister();
- ipv6_route_cleanup();
+#endif
+ ip6_route_cleanup();
ndisc_cleanup();
addrconf_cleanup();
}
#endif
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O6 -m486 -c ipv6_sockglue.c"
- * End:
- */
+
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 14ba9ef5f..573f1f611 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -5,6 +5,8 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
+ * $Id: mcast.c,v 1.8 1997/04/12 04:32:48 davem Exp $
+ *
* Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c
*
* This program is free software; you can redistribute it and/or
@@ -13,6 +15,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define __NO_VERSION__
+#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -22,6 +26,7 @@
#include <linux/in6.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
+#include <linux/route.h>
#include <net/sock.h>
#include <net/snmp.h>
@@ -30,13 +35,32 @@
#include <net/protocol.h>
#include <net/if_inet6.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
#include <net/addrconf.h>
+#include <net/checksum.h>
+
+/* Set to 3 to get tracing... */
+#define MCAST_DEBUG 2
+
+#if MCAST_DEBUG >= 3
+#define MDBG(x) printk x
+#else
+#define MDBG(x)
+#endif
+
+static struct inode igmp6_inode;
+static struct socket *igmp6_socket=&igmp6_inode.u.socket_i;
+
+static void igmp6_join_group(struct ifmcaddr6 *ma);
+static void igmp6_leave_group(struct ifmcaddr6 *ma);
+void igmp6_timer_handler(unsigned long data);
+
+#define IGMP6_UNSOLICITED_IVAL (10*HZ)
/*
* socket join on multicast group
*/
+
int ipv6_sock_mc_join(struct sock *sk, struct device *dev,
struct in6_addr *addr)
{
@@ -44,21 +68,25 @@ int ipv6_sock_mc_join(struct sock *sk, struct device *dev,
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
int err;
+ MDBG(("ipv6_sock_mc_join(%s) addr[", dev ? dev->name : "[NULL]"));
+ MDBG(("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+ addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+ addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+ addr->s6_addr16[6], addr->s6_addr16[7]));
if (!(ipv6_addr_type(addr) & IPV6_ADDR_MULTICAST))
return -EINVAL;
if(!(dev->flags & IFF_MULTICAST))
return -EADDRNOTAVAIL;
- mc_lst = (struct ipv6_mc_socklist *)
- kmalloc(sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
+ mc_lst = kmalloc(sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
if (mc_lst == NULL)
return -ENOMEM;
mc_lst->next = NULL;
memcpy(&mc_lst->addr, addr, sizeof(struct in6_addr));
- mc_lst->dev = dev;
+ mc_lst->dev = dev;
/*
* now add/increase the group membership on the device
@@ -66,8 +94,7 @@ int ipv6_sock_mc_join(struct sock *sk, struct device *dev,
err = ipv6_dev_mc_inc(dev, addr);
- if (err)
- {
+ if (err) {
kfree(mc_lst);
return err;
}
@@ -84,7 +111,30 @@ int ipv6_sock_mc_join(struct sock *sk, struct device *dev,
int ipv6_sock_mc_drop(struct sock *sk, struct device *dev,
struct in6_addr *addr)
{
- return 0;
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6_mc_socklist *mc_lst, **lnk;
+
+ lnk = &np->ipv6_mc_list;
+
+ MDBG(("ipv6_sock_mc_drop(%s) addr[", dev ? dev->name : "[NULL]"));
+ MDBG(("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+ addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+ addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+ addr->s6_addr16[6], addr->s6_addr16[7]));
+
+ for (mc_lst = *lnk ; mc_lst; mc_lst = mc_lst->next) {
+ if (mc_lst->dev == dev &&
+ ipv6_addr_cmp(&mc_lst->addr, addr) == 0) {
+ *lnk = mc_lst->next;
+ ipv6_dev_mc_dec(mc_lst->dev, &mc_lst->addr);
+ kfree(mc_lst);
+
+ return 0;
+ }
+ lnk = &mc_lst->next;
+ }
+
+ return -ENOENT;
}
void ipv6_sock_mc_close(struct sock *sk)
@@ -92,14 +142,15 @@ void ipv6_sock_mc_close(struct sock *sk)
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct ipv6_mc_socklist *mc_lst;
- for (mc_lst = np->ipv6_mc_list; mc_lst; )
- {
+ for (mc_lst = np->ipv6_mc_list; mc_lst; ) {
struct ipv6_mc_socklist *back;
/*
* leave group
*/
+ ipv6_dev_mc_dec(mc_lst->dev, &mc_lst->addr);
+
back = mc_lst;
mc_lst = mc_lst->next;
kfree(back);
@@ -111,53 +162,59 @@ void ipv6_sock_mc_close(struct sock *sk)
*/
int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
{
- struct ipv6_mc_list *mc;
- struct inet6_dev *i6dev;
+ struct ifmcaddr6 *mc;
+ struct inet6_dev *idev;
char buf[6];
- u8 hash;
-
- for (i6dev = inet6_dev_lst; i6dev; i6dev=i6dev->next)
- if (i6dev->dev == dev)
+ int hash;
+
+ MDBG(("ipv6_dev_mc_inc(%s) addr[", dev ? dev->name : "[NULL]"));
+ MDBG(("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+ addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+ addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+ addr->s6_addr16[6], addr->s6_addr16[7]));
+ hash = ipv6_devindex_hash(dev->ifindex);
+
+ for (idev = inet6_dev_lst[hash]; idev; idev=idev->next)
+ if (idev->dev == dev)
break;
-
- if (i6dev == NULL)
- {
+
+ if (idev == NULL) {
printk(KERN_DEBUG "ipv6_dev_mc_inc: device not found\n");
return -EINVAL;
}
- for (mc = i6dev->mc_list; mc; mc = mc->if_next)
- if (ipv6_addr_cmp(&mc->addr, addr) == 0)
- {
- atomic_inc(&mc->users);
+ hash = ipv6_addr_hash(addr);
+
+ for (mc = inet6_mcast_lst[hash]; mc; mc = mc->next) {
+ if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0) {
+ atomic_inc(&mc->mca_users);
return 0;
}
+ }
/*
* not found: create a new one.
*/
- mc = (struct ipv6_mc_list *) kmalloc(sizeof(struct ipv6_mc_list),
- GFP_ATOMIC);
+ mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC);
if (mc == NULL)
- {
return -ENOMEM;
- }
- memset(mc, 0, sizeof(struct ipv6_mc_list));
+ MDBG(("create new ipv6 MC entry, "));
+ memset(mc, 0, sizeof(struct ifmcaddr6));
+ mc->mca_timer.function = igmp6_timer_handler;
+ mc->mca_timer.data = (unsigned long) mc;
- memcpy(&mc->addr, addr, sizeof(struct in6_addr));
+ memcpy(&mc->mca_addr, addr, sizeof(struct in6_addr));
mc->dev = dev;
- mc->users = 1;
-
- hash = ipv6_addr_hash(addr);
+ atomic_set(&mc->mca_users, 1);
mc->next = inet6_mcast_lst[hash];
inet6_mcast_lst[hash] = mc;
-
- mc->if_next = i6dev->mc_list;
- i6dev->mc_list = mc;
+
+ mc->if_next = idev->mc_list;
+ idev->mc_list = mc;
/*
* multicast mapping is defined in IPv6-over-foo documents
@@ -166,27 +223,67 @@ int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
switch (dev->type) {
case ARPHRD_ETHER:
ipv6_mc_map(addr, buf);
+ MDBG(("ARPHRD_ETHER[%02x:%02x:%02x:%02x:%02x:%02x] dev_mc_add()\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]));
dev_mc_add(dev, buf, ETH_ALEN, 0);
break;
-
+
default:
printk(KERN_DEBUG "dev_mc_inc: unkown device type\n");
- }
-
+ };
- /*
- * FIXME: ICMP report handling
- */
+ igmp6_join_group(mc);
return 0;
}
+static void ipv6_mca_remove(struct device *dev, struct ifmcaddr6 *ma)
+{
+ struct inet6_dev *idev;
+
+ idev = ipv6_get_idev(dev);
+
+ if (idev) {
+ struct ifmcaddr6 *iter, **lnk;
+
+ lnk = &idev->mc_list;
+
+ for (iter = *lnk; iter; iter = iter->if_next) {
+ if (iter == ma) {
+ *lnk = iter->if_next;
+ break;
+ }
+ lnk = &iter->if_next;
+ }
+ }
+}
+
/*
* device multicast group del
*/
int ipv6_dev_mc_dec(struct device *dev, struct in6_addr *addr)
{
- return 0;
+ struct ifmcaddr6 *ma, **lnk;
+ int hash;
+
+ hash = ipv6_addr_hash(addr);
+
+ lnk = &inet6_mcast_lst[hash];
+
+ for (ma = inet6_mcast_lst[hash]; ma; ma = ma->next) {
+ if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0) {
+ if (atomic_dec_and_test(&ma->mca_users)) {
+ igmp6_leave_group(ma);
+ *lnk = ma->next;
+ ipv6_mca_remove(ma->dev, ma);
+ kfree(ma);
+ }
+ return 0;
+ }
+ lnk = &ma->next;
+ }
+
+ return -ENOENT;
}
/*
@@ -194,17 +291,15 @@ int ipv6_dev_mc_dec(struct device *dev, struct in6_addr *addr)
*/
int ipv6_chk_mcast_addr(struct device *dev, struct in6_addr *addr)
{
- struct ipv6_mc_list *mc;
- u8 hash;
+ struct ifmcaddr6 *mc;
+ int hash;
hash = ipv6_addr_hash(addr);
- for (mc = inet6_mcast_lst[hash]; mc; mc=mc->next)
- if ((mc->dev == dev) &&
- ipv6_addr_cmp(&mc->addr, addr) == 0)
- {
+ for (mc = inet6_mcast_lst[hash]; mc; mc=mc->next) {
+ if ((mc->dev == dev) && ipv6_addr_cmp(&mc->mca_addr, addr) == 0)
return 1;
- }
+ }
return 0;
}
@@ -213,8 +308,216 @@ int ipv6_chk_mcast_addr(struct device *dev, struct in6_addr *addr)
* IGMP handling (alias multicast ICMPv6 messages)
*/
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o mcast.o mcast.c"
- * End:
- */
+static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
+{
+ unsigned long delay;
+
+ ma->mca_flags |= MAF_TIMER_RUNNING;
+
+ delay = ipv6_random() % resptime;
+ ma->mca_timer.expires = jiffies + delay;
+ add_timer(&ma->mca_timer);
+}
+
+int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
+{
+ struct ifmcaddr6 *ma;
+ struct in6_addr *addrp;
+ unsigned long resptime;
+
+ if (len < sizeof(struct icmp6hdr) + sizeof(struct ipv6hdr))
+ return -EINVAL;
+
+ resptime = hdr->icmp6_maxdelay;
+
+ addrp = (struct in6_addr *) (hdr + 1);
+
+ if (ipv6_addr_any(addrp)) {
+ struct inet6_dev *idev;
+
+ idev = ipv6_get_idev(skb->dev);
+
+ if (idev == NULL)
+ return 0;
+
+ for (ma = idev->mc_list; ma; ma=ma->if_next)
+ igmp6_group_queried(ma, resptime);
+ } else {
+ int hash = ipv6_addr_hash(addrp);
+
+ for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
+ if (ma->dev == skb->dev &&
+ ipv6_addr_cmp(addrp, &ma->mca_addr) == 0) {
+ igmp6_group_queried(ma, resptime);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
+{
+ struct ifmcaddr6 *ma;
+ struct in6_addr *addrp;
+ struct device *dev;
+ int hash;
+
+ if (len < sizeof(struct icmp6hdr) + sizeof(struct ipv6hdr))
+ return -EINVAL;
+
+ addrp = (struct in6_addr *) (hdr + 1);
+
+ dev = skb->dev;
+
+ /*
+ * Cancel the timer for this group
+ */
+
+ hash = ipv6_addr_hash(addrp);
+
+ for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
+ if ((ma->dev == dev) && ipv6_addr_cmp(&ma->mca_addr, addrp) == 0) {
+ if (ma->mca_flags & MAF_TIMER_RUNNING) {
+ del_timer(&ma->mca_timer);
+ ma->mca_flags &= ~MAF_TIMER_RUNNING;
+ }
+
+ ma->mca_flags &= ~MAF_LAST_REPORTER;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void igmp6_send(struct in6_addr *addr, struct device *dev, int type)
+{
+ struct sock *sk = igmp6_socket->sk;
+ struct sk_buff *skb;
+ struct icmp6hdr *hdr;
+ struct inet6_ifaddr *ifp;
+ struct in6_addr *addrp;
+ int err, len, plen;
+
+ len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+
+ plen = sizeof(struct ipv6hdr) + len;
+
+ skb = sock_alloc_send_skb(sk, dev->hard_header_len + plen, 0, 0, &err);
+
+ if (skb == NULL)
+ return;
+
+ if (dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ unsigned char ha[MAX_ADDR_LEN];
+ ipv6_mc_map(addr, ha);
+ dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, plen);
+ skb->arp = 1;
+ }
+ }
+
+ ifp = ipv6_get_lladdr(dev);
+
+ if (ifp == NULL) {
+#if MCAST_DEBUG >= 1
+ printk(KERN_DEBUG "igmp6: %s no linklocal address\n",
+ dev->name);
+#endif
+ return;
+ }
+
+ ip6_nd_hdr(sk, skb, dev, &ifp->addr, addr, IPPROTO_ICMPV6, len);
+
+ /*
+ * need hop-by-hop router alert option.
+ */
+
+ hdr = (struct icmp6hdr *) skb_put(skb, sizeof(struct icmp6hdr));
+ memset(hdr, 0, sizeof(struct icmp6hdr));
+ hdr->icmp6_type = type;
+
+ addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr));
+ ipv6_addr_copy(addrp, addr);
+
+ hdr->icmp6_cksum = csum_ipv6_magic(&ifp->addr, addr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) hdr, len, 0));
+
+ dev_queue_xmit(skb);
+}
+
+static void igmp6_join_group(struct ifmcaddr6 *ma)
+{
+ unsigned long delay;
+ int addr_type;
+
+ addr_type = ipv6_addr_type(&ma->mca_addr);
+
+ if ((addr_type & IPV6_ADDR_LINKLOCAL))
+ return;
+
+ igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
+
+ delay = ipv6_random() % IGMP6_UNSOLICITED_IVAL;
+ ma->mca_timer.expires = jiffies + delay;
+
+ add_timer(&ma->mca_timer);
+ ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER;
+}
+
+static void igmp6_leave_group(struct ifmcaddr6 *ma)
+{
+ int addr_type;
+
+ addr_type = ipv6_addr_type(&ma->mca_addr);
+
+ if ((addr_type & IPV6_ADDR_LINKLOCAL))
+ return;
+
+ if (ma->mca_flags & MAF_LAST_REPORTER)
+ igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REDUCTION);
+
+ if (ma->mca_flags & MAF_TIMER_RUNNING)
+ del_timer(&ma->mca_timer);
+}
+
+void igmp6_timer_handler(unsigned long data)
+{
+ struct ifmcaddr6 *ma = (struct ifmcaddr6 *) data;
+
+ ma->mca_flags |= MAF_LAST_REPORTER;
+ igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
+ ma->mca_flags &= ~MAF_TIMER_RUNNING;
+}
+
+void igmp6_init(struct net_proto_family *ops)
+{
+ struct sock *sk;
+ int err;
+
+ igmp6_inode.i_mode = S_IFSOCK;
+ igmp6_inode.i_sock = 1;
+ igmp6_inode.i_uid = 0;
+ igmp6_inode.i_gid = 0;
+
+ igmp6_socket->inode = &igmp6_inode;
+ igmp6_socket->state = SS_UNCONNECTED;
+ igmp6_socket->type = SOCK_RAW;
+
+ if((err=ops->create(igmp6_socket, IPPROTO_ICMPV6))<0)
+ printk(KERN_DEBUG
+ "Failed to create the IGMP6 control socket.\n");
+
+ MOD_DEC_USE_COUNT;
+
+ sk = igmp6_socket->sk;
+ sk->allocation = GFP_ATOMIC;
+ sk->num = 256; /* Don't receive any data */
+
+ sk->net_pinfo.af_inet6.hop_limit = 1;
+}
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 31d50a5b5..3a1704f37 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -6,7 +6,7 @@
* Pedro Roque <roque@di.fc.ul.pt>
* Mike Shaver <shaver@ingenia.com>
*
- * $Id: ndisc.c,v 1.28 1996/10/11 16:03:06 roque Exp $
+ * $Id: ndisc.c,v 1.14 1997/04/12 04:32:51 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -23,18 +23,14 @@
* Janos Farkas : kmalloc failure checks
*/
-/*
- * Interface:
- *
- * ndisc_lookup will be called from eth.c on dev->(re)build_header
- *
- * ndisc_rcv
- * ndisc_validate is called by higher layers when they know a neighbour
- * is reachable.
- *
- * Manages neighbour cache
- *
- */
+/* Set to 3 to get tracing... */
+#define ND_DEBUG 2
+
+#if ND_DEBUG >= 3
+#define NDBG(x) printk x
+#else
+#define NDBG(x)
+#endif
#define __NO_VERSION__
#include <linux/module.h>
@@ -46,7 +42,8 @@
#include <linux/sched.h>
#include <linux/net.h>
#include <linux/in6.h>
-#include <linux/netdevice.h>
+#include <linux/route.h>
+
#include <linux/if_arp.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
@@ -57,38 +54,44 @@
#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
+
#include <net/checksum.h>
#include <linux/proc_fs.h>
#define NCACHE_NUM_BUCKETS 32
-static struct socket ndisc_socket;
+static struct inode ndisc_inode;
+static struct socket *ndisc_socket=&ndisc_inode.u.socket_i;
unsigned long nd_rand_seed = 152L;
struct ndisc_statistics nd_stats;
-static struct neighbour *neighbours[NCACHE_NUM_BUCKETS];
+static struct neigh_table nd_tbl;
+
+unsigned int ndisc_hash(void *primary_key);
+int ndisc_eth_resolv(unsigned char *h_dest, struct sk_buff *skb);
+
+static struct neigh_ops nd_neigh_ops = {
+ ETH_P_IPV6,
+ ndisc_hash,
+ ndisc_eth_resolv,
+ NULL
+};
+
static struct timer_list ndisc_timer;
static struct timer_list ndisc_gc_timer;
-static atomic_t ndisc_lock = 0;
-
/*
* Protocol variables
*/
-int nd_max_multicast_solicit = 3;
-int nd_max_unicast_solicit = 3;
-int nd_retrans_timer = RETRANS_TIMER;
-int nd_reachable_time = RECHABLE_TIME;
-int nd_base_reachable_time = RECHABLE_TIME;
-int nd_delay_first_probe = 5 * HZ;
-int nd_gc_interval = 5 * HZ;
+unsigned long nd_reachable_time = RECHABLE_TIME;
+int nd_gc_interval = 5 * HZ;
/*
* garbage collection timeout must be greater than reachable time
@@ -99,20 +102,10 @@ int nd_gc_interval = 5 * HZ;
int nd_gc_staletime = 3 * RECHABLE_TIME;
-static struct neighbour ndisc_insert_queue = {
- {{{0,}}}, 0, 0, NULL, 0,
- {0,}, NULL, {0,}, 0, 0, 0, 0, 0,
- &ndisc_insert_queue,
- &ndisc_insert_queue
-};
-
-static int ndisc_ins_queue_len = 0;
-
-int ndisc_event_timer(struct neighbour *neigh);
-static void ndisc_bh_insert(void);
+static int ndisc_event_timer(struct nd_neigh *ndn);
-int ipv6_random(void)
+unsigned long ipv6_random(void)
{
nd_rand_seed=nd_rand_seed*69069L+1;
return nd_rand_seed^jiffies;
@@ -122,26 +115,22 @@ static __inline__ unsigned long rand_reach_time(void)
{
unsigned long val;
- val = ipv6_random() % (MAX_RANDOM_FACTOR * nd_base_reachable_time);
- if (val < (MIN_RANDOM_FACTOR * nd_base_reachable_time))
- {
- val += (MIN_RANDOM_FACTOR * nd_base_reachable_time);
- }
+ val = ipv6_random() % (MAX_RANDOM_FACTOR *
+ ipv6_config.nd_base_reachable_time);
+
+ if (val < (MIN_RANDOM_FACTOR * ipv6_config.nd_base_reachable_time))
+ val+= (MIN_RANDOM_FACTOR * ipv6_config.nd_base_reachable_time);
return val;
}
-void ndisc_verify_reachability(struct neighbour * neigh);
-
-/*
- * (inline) support functions
- */
-
-static __inline__ __u32 ndisc_hash(struct in6_addr *addr)
+unsigned int ndisc_hash(void *primary_key)
{
-
+ struct in6_addr *addr = (struct in6_addr *) primary_key;
__u32 hash_val;
+ addr = (struct in6_addr *) primary_key;
+
hash_val = addr->s6_addr32[2] ^ addr->s6_addr32[3];
hash_val ^= hash_val >> 16;
@@ -149,239 +138,117 @@ static __inline__ __u32 ndisc_hash(struct in6_addr *addr)
return (hash_val & (NCACHE_NUM_BUCKETS - 1));
}
+static int ndisc_gc_func(struct neighbour *neigh, void *arg);
-static __inline__ void ndisc_neigh_queue(struct neighbour *neigh)
-{
- struct neighbour *next = &ndisc_insert_queue;
-
- ndisc_ins_queue_len++;
-
- neigh->prev = next->prev;
- neigh->prev->next = neigh;
- next->prev = neigh;
- neigh->next = next;
-}
-
-static __inline__ struct neighbour * ndisc_dequeue(void)
-{
- struct neighbour *next = &ndisc_insert_queue;
- struct neighbour *head;
-
- ndisc_ins_queue_len--;
-
- head = next->next;
-
- if (head == next)
- {
- return NULL;
- }
-
- head->next->prev = head->prev;
- next->next = head->next;
-
- head->next = NULL;
- head->prev = NULL;
-
- return head;
-}
-
-static __inline__ void ndisc_release_lock(void)
+static void ndisc_periodic_timer(unsigned long arg)
{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- ndisc_lock--;
-
- if (ndisc_lock == 0 && ndisc_ins_queue_len)
- {
- ndisc_bh_insert();
- }
-
- restore_flags(flags);
-}
-
-static void ndisc_insert_neigh(struct neighbour *neigh)
-{
-
- struct neighbour * bucket;
- __u32 hash_val = ndisc_hash(&neigh->addr);
-
- bucket = neighbours[hash_val];
-
- if (!bucket)
- {
- neighbours[hash_val] = neigh;
- return;
- }
-
- for (; bucket->next; bucket = bucket->next)
- ;
-
- bucket->next = neigh;
- neigh->prev = bucket;
-}
-
-static __inline__ struct neighbour *
-ndisc_retrieve_neigh(struct device *dev, struct in6_addr *addr)
-{
-
- struct neighbour * iter;
- iter = neighbours[ndisc_hash(addr)];
-
- for (; iter; iter = iter->next)
- {
- if (dev == iter->dev && ipv6_addr_cmp(addr, &iter->addr) == 0)
- return iter;
- }
- return NULL;
-}
-
-static void ndisc_unlink_neigh(struct neighbour * neigh)
-{
- if (neigh->prev)
- neigh->prev->next = neigh->next;
- else
- {
- int hash = ndisc_hash(&neigh->addr);
- neighbours[hash] = neigh->next;
- }
-
- if (neigh->next)
- neigh->next->prev = neigh->prev;
-}
-
-static void ndisc_release_neigh(struct neighbour * neigh)
-{
- struct sk_buff *skb;
-
- while((skb=skb_dequeue(&neigh->arp_queue)))
- {
- dev_kfree_skb(skb, FREE_WRITE);
- }
-
- if (neigh->refcnt == 0)
- {
- ndisc_unlink_neigh(neigh);
- kfree(neigh);
- }
-}
-
-static void ndisc_bh_insert(void)
-{
- struct neighbour *neigh;
-
- while((neigh = ndisc_dequeue()))
- {
- ndisc_insert_neigh(neigh);
- }
-}
-
-
-static void ndisc_garbage_collect(unsigned long arg)
-{
- struct neighbour * neigh;
static unsigned long last_rand = 0;
- unsigned long now = jiffies;
- unsigned long flags;
- int i = 0;
-
-
+ unsigned long now = jiffies;
+
/*
* periodicly compute ReachableTime from random function
*/
- if (now - last_rand > REACH_RANDOM_INTERVAL)
- {
+
+ if ((now - last_rand) > REACH_RANDOM_INTERVAL) {
last_rand = now;
nd_reachable_time = rand_reach_time();
}
- save_flags(flags);
- cli();
+ neigh_table_lock(&nd_tbl);
- if (ndisc_lock)
- {
- restore_flags(flags);
+ start_bh_atomic();
+ if (atomic_read(&nd_tbl.tbl_lock) == 1) {
+ ntbl_walk_table(&nd_tbl, ndisc_gc_func, 0, 0, NULL);
+ ndisc_gc_timer.expires = now + nd_gc_interval;
+ } else {
+#if ND_DEBUG >= 2
+ printk(KERN_DEBUG "ndisc_gc delayed: table locked\n");
+#endif
ndisc_gc_timer.expires = now + HZ;
- add_timer(&ndisc_gc_timer);
- return;
}
-
- for (; i < NCACHE_NUM_BUCKETS; i++)
- for (neigh = neighbours[i]; neigh;)
- {
- /*
- * Release unused entries
- */
- if (neigh->refcnt == 0 &&
- ((neigh->nud_state == NUD_FAILED) ||
- ((neigh->nud_state == NUD_REACHABLE) &&
- (neigh->tstamp <= (now - nd_gc_staletime))
- )
- )
- )
- {
- struct neighbour *prev;
-
- prev = neigh;
- neigh = neigh->next;
- ndisc_release_neigh(prev);
- continue;
- }
- neigh = neigh->next;
- }
+ end_bh_atomic();
+
+ neigh_table_unlock(&nd_tbl);
+
+ add_timer(&ndisc_gc_timer);
+}
- restore_flags(flags);
+static int ndisc_gc_func(struct neighbour *neigh, void *arg)
+{
+ struct nd_neigh *ndn = (struct nd_neigh *) neigh;
+ unsigned long now = jiffies;
- ndisc_gc_timer.expires = now + nd_gc_interval;
- add_timer(&ndisc_gc_timer);
+ if (atomic_read(&ndn->ndn_refcnt) == 0) {
+ switch (ndn->ndn_nud_state) {
+
+ case NUD_REACHABLE:
+ case NUD_STALE:
+ if (now - ndn->ndn_tstamp < nd_gc_staletime)
+ break;
+ case NUD_FAILED:
+ return 1;
+ default:
+ };
+ }
+ return 0;
}
-static __inline__ void ndisc_add_timer(struct neighbour *neigh, int timer)
+static __inline__ void ndisc_add_timer(struct nd_neigh *ndn, int timer)
{
unsigned long now = jiffies;
- unsigned long tval;
+ unsigned long tval = ~0UL;
- neigh->expires = now + timer;
- tval = del_timer(&ndisc_timer);
+ ndn->ndn_expires = now + timer;
+
+ if (del_timer(&ndisc_timer))
+ tval = ndisc_timer.expires;
- if (tval)
- {
- tval = min(tval, neigh->expires);
- }
- else
- tval = neigh->expires;
+ tval = min(tval, ndn->ndn_expires);
ndisc_timer.expires = tval;
add_timer(&ndisc_timer);
}
-static void ndisc_del_timer(struct neighbour *neigh)
+static void ndisc_del_timer(struct nd_neigh *ndn)
{
- unsigned long tval;
+ unsigned long tval = ~0UL;
+ unsigned long neigh_val;
- if (!(neigh->nud_state & NUD_IN_TIMER))
- return;
+ if (del_timer(&ndisc_timer))
+ tval = ndisc_timer.expires;
- tval = del_timer(&ndisc_timer);
-
- if (tval == neigh->expires)
- {
+ neigh_val = ndn->ndn_expires;
+ ndn->ndn_expires = 0;
+
+ if (tval == neigh_val) {
int i;
tval = ~0UL;
+ neigh_table_lock(&nd_tbl);
+
/* need to search the entire neighbour cache */
- for (i=0; i < NCACHE_NUM_BUCKETS; i++)
- {
- for (neigh = neighbours[i]; neigh; neigh=neigh->next)
- if (neigh->nud_state & NUD_IN_TIMER)
- {
- tval = min(tval, neigh->expires);
- }
- }
+ for (i=0; i < nd_tbl.tbl_size; i++) {
+ struct neighbour *neigh, *head;
+ head = nd_tbl.hash_buckets[i];
+
+ if ((neigh = head) == NULL)
+ continue;
+
+ do {
+ struct nd_neigh *n;
+
+ n = (struct nd_neigh *) neigh;
+ if ((n->ndn_nud_state & NUD_IN_TIMER) &&
+ n->ndn_expires)
+ tval = min(tval, n->ndn_expires);
+
+ neigh = neigh->next;
+
+ } while (neigh != head);
+ }
+ neigh_table_unlock(&nd_tbl);
}
if (tval == ~(0UL))
@@ -391,53 +258,79 @@ static void ndisc_del_timer(struct neighbour *neigh)
add_timer(&ndisc_timer);
}
-static struct neighbour * ndisc_new_neigh(struct device *dev,
- struct in6_addr *addr)
+static int ndisc_forced_gc(struct neighbour *neigh, void *arg)
+{
+ struct nd_neigh *ndn = (struct nd_neigh *) neigh;
+
+ if (atomic_read(&ndn->ndn_refcnt) == 0) {
+ if (ndn->ndn_nud_state & NUD_IN_TIMER)
+ ndisc_del_timer(ndn);
+
+ return 1;
+ }
+ return 0;
+}
+
+static struct nd_neigh * ndisc_new_neigh(struct device *dev,
+ struct in6_addr *addr)
{
- struct neighbour *neigh;
- unsigned long flags;
+ struct nd_neigh *ndn;
- neigh = (struct neighbour *) kmalloc(sizeof(struct neighbour),
- GFP_ATOMIC);
+ NDBG(("ndisc_new_neigh("));
+ if(dev)
+ NDBG(("%s,", dev->name));
+ else
+ NDBG(("[NULL],"));
+ NDBG(("[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]): ",
+ addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+ addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+ addr->s6_addr16[6], addr->s6_addr16[7]));
+
+ ndn = (struct nd_neigh *) neigh_alloc(sizeof(struct nd_neigh),
+ &nd_neigh_ops);
+ if (ndn == NULL) {
+
+#if ND_DEBUG >= 2
+ printk(KERN_DEBUG "neigh_alloc: out of memory\n");
+#endif
- if (neigh == NULL)
- {
- printk(KERN_DEBUG "ndisc: kmalloc failure\n");
+ start_bh_atomic();
+ if (atomic_read(&nd_tbl.tbl_lock) == 1) {
+#if ND_DEBUG >= 2
+ printk(KERN_DEBUG "ndisc_alloc: forcing gc\n");
+#endif
+ ntbl_walk_table(&nd_tbl, ndisc_forced_gc, 0, 0, NULL);
+ }
+
+ end_bh_atomic();
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "ndisc_alloc failed\n");
+#endif
return NULL;
}
nd_stats.allocs++;
- memset(neigh, 0, sizeof (struct neighbour));
- skb_queue_head_init(&neigh->arp_queue);
-
- ipv6_addr_copy(&neigh->addr, addr);
- neigh->len = 128;
- neigh->type = ipv6_addr_type(addr);
- neigh->dev = dev;
- neigh->tstamp = jiffies;
+ ipv6_addr_copy(&ndn->ndn_addr, addr);
+ ndn->ndn_plen = 128;
+ ndn->ndn_type = ipv6_addr_type(addr);
+ ndn->ndn_dev = dev;
+ ndn->ndn_tstamp = jiffies;
- if (dev->type == ARPHRD_LOOPBACK || dev->type == ARPHRD_SIT)
- {
- neigh->flags |= NCF_NOARP;
+ if ((ndn->ndn_type & IPV6_ADDR_MULTICAST)) {
+ NDBG(("MULTICAST(NCF_NOARP) "));
+ ndn->ndn_flags |= NCF_NOARP;
}
- save_flags(flags);
- cli();
-
- if (ndisc_lock == 0)
- {
- /* Add to the cache. */
- ndisc_insert_neigh(neigh);
- }
- else
- {
- ndisc_neigh_queue(neigh);
+ if (dev->type == ARPHRD_LOOPBACK || dev->type == ARPHRD_SIT) {
+ NDBG(("%s(NCF_NOARP) ",
+ (dev->type==ARPHRD_LOOPBACK) ? "LOOPBACK" : "SIT"));
+ ndn->ndn_flags |= NCF_NOARP;
}
- restore_flags(flags);
-
- return neigh;
+ neigh_insert(&nd_tbl, (struct neighbour *) ndn);
+ NDBG(("returning ndn(%p)\n", ndn));
+ return ndn;
}
/*
@@ -448,33 +341,34 @@ static struct neighbour * ndisc_new_neigh(struct device *dev,
struct neighbour * ndisc_get_neigh(struct device *dev, struct in6_addr *addr)
{
- struct neighbour *neigh;
+ struct nd_neigh *neigh;
/*
* neighbour cache:
* cached information about nexthop and addr resolution
*/
- if (dev == NULL)
- {
- printk(KERN_DEBUG "ncache_get_neigh: NULL device\n");
+ if (dev == NULL) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "ndisc_get_neigh: NULL device\n");
+#endif
return NULL;
}
- atomic_inc(&ndisc_lock);
-
- neigh = ndisc_retrieve_neigh(dev, addr);
-
- ndisc_release_lock();
+ neigh_table_lock(&nd_tbl);
- if (neigh == NULL)
- {
+ neigh = (struct nd_neigh *) neigh_lookup(&nd_tbl, (void *) addr,
+ sizeof(struct in6_addr), dev);
+ if (neigh == NULL) {
neigh = ndisc_new_neigh(dev, addr);
+
+ if (neigh == NULL)
+ return NULL;
}
- atomic_inc(&neigh->refcnt);
-
- return neigh;
+ neigh_table_unlock(&nd_tbl);
+
+ return neighbour_clone((struct neighbour *) neigh);
}
/*
@@ -483,69 +377,54 @@ struct neighbour * ndisc_get_neigh(struct device *dev, struct in6_addr *addr)
* 1 - Address Resolution unfinished / packet queued
*/
-int ndisc_eth_resolv(unsigned char *h_dest, struct device *dev,
- struct sk_buff *skb)
+int ndisc_eth_resolv(unsigned char *h_dest, struct sk_buff *skb)
{
- struct neighbour *neigh;
-
- neigh = skb->nexthop;
+ struct nd_neigh *ndn = NULL;
- if (neigh == NULL)
- {
- int addr_type;
-
- addr_type = ipv6_addr_type(&skb->ipv6_hdr->daddr);
-
- if (addr_type & IPV6_ADDR_MULTICAST)
- {
- ipv6_mc_map(&skb->ipv6_hdr->daddr, h_dest);
- return 0;
- }
+ if (skb->dst)
+ ndn = (struct nd_neigh *) skb->dst->neighbour;
+ if (ndn == NULL) {
+#if ND_DEBUG >= 2
printk(KERN_DEBUG "ndisc_eth_resolv: nexthop is NULL\n");
+#endif
goto discard;
}
- if (skb->pkt_type == PACKET_NDISC)
- goto ndisc_pkt;
-
- switch (neigh->nud_state) {
+ if ((ndn->ndn_type & IPV6_ADDR_MULTICAST)) {
+ struct in6_addr *daddr;
+
+ daddr = &skb->nh.ipv6h->daddr;
+ ipv6_mc_map(daddr, h_dest);
+ return 0;
+ }
+
+ switch (ndn->ndn_nud_state) {
case NUD_FAILED:
case NUD_NONE:
- ndisc_event_send(neigh, skb);
+ ndisc_event_send((struct neighbour *)ndn, skb);
case NUD_INCOMPLETE:
- if (skb_queue_len(&neigh->arp_queue) >= NDISC_QUEUE_LEN)
- {
+ if (skb_queue_len(&ndn->neigh.arp_queue) >= NDISC_QUEUE_LEN) {
struct sk_buff *buff;
- buff = neigh->arp_queue.prev;
+ buff = ndn->neigh.arp_queue.prev;
skb_unlink(buff);
dev_kfree_skb(buff, FREE_WRITE);
}
- skb_queue_head(&neigh->arp_queue, skb);
+ skb_queue_head(&ndn->neigh.arp_queue, skb);
return 1;
default:
- ndisc_event_send(neigh, skb);
- }
-
- ndisc_pkt:
+ ndisc_event_send((struct neighbour *)ndn, skb);
+ };
- if (neigh->h_dest == NULL)
- {
- printk(KERN_DEBUG "neigh->h_dest is NULL\n");
- goto discard;
- }
-
- memcpy(h_dest, neigh->h_dest, dev->addr_len);
-
- if ((neigh->flags & NCF_HHVALID) == 0)
- {
- /*
- * copy header to hh_data and move h_dest pointer
- * this is strictly media dependent.
- */
+ if ((ndn->ndn_flags & NTF_COMPLETE) == 0) {
+#if ND_DEBUG >=1
+ /* This shouldn't happen */
+ printk(KERN_DEBUG "ND: using incomplete entry\n");
+#endif
}
+ memcpy(h_dest, ndn->ndn_ha, skb->dev->addr_len);
return 0;
discard:
@@ -554,54 +433,78 @@ int ndisc_eth_resolv(unsigned char *h_dest, struct device *dev,
return 1;
}
+/*
+ * Send a Neighbour Advertisement
+ */
-/* Send the actual Neighbour Advertisement */
-
-void ndisc_send_na(struct device *dev, struct neighbour *neigh,
- struct in6_addr *daddr,
- struct in6_addr *solicited_addr,
+void ndisc_send_na(struct device *dev, struct nd_neigh *ndn,
+ struct in6_addr *daddr, struct in6_addr *solicited_addr,
int router, int solicited, int override, int inc_opt)
{
- struct sock *sk = (struct sock *)ndisc_socket.data;
+ struct sock *sk = ndisc_socket->sk;
struct nd_msg *msg;
int len, opt_len;
struct sk_buff *skb;
int err;
+ NDBG(("ndisc_send_na("));
+ if(dev)
+ NDBG(("%s,", dev->name));
+ else
+ NDBG(("[NULL]"));
+ NDBG(("%p): ", ndn));
+ if(daddr)
+ NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+ daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+ daddr->s6_addr16[6], daddr->s6_addr16[7]));
+ if(solicited_addr)
+ NDBG(("solicit_addr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ solicited_addr->s6_addr16[0], solicited_addr->s6_addr16[1],
+ solicited_addr->s6_addr16[2], solicited_addr->s6_addr16[3],
+ solicited_addr->s6_addr16[4], solicited_addr->s6_addr16[5],
+ solicited_addr->s6_addr16[6], solicited_addr->s6_addr16[7]));
+ NDBG(("rtr(%d)sol(%d)ovr(%d)iopt(%d)\n", router, solicited, override, inc_opt));
+
opt_len = ((dev->addr_len + 1) >> 3) + 1;
- len = sizeof(struct icmpv6hdr) + sizeof(struct in6_addr);
+ len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+#if ND_DEBUG >=1
+ if (dev == NULL) {
+ printk(KERN_DEBUG "send_na: null device\n");
+ return;
+ }
+#endif
if (inc_opt)
- {
len += opt_len << 3;
- }
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
- if (skb == NULL)
- {
+ if (skb == NULL) {
printk(KERN_DEBUG "send_na: alloc skb failed\n");
return;
}
-
- skb->free=1;
-
- if (ipv6_bld_hdr_2(sk, skb, dev, neigh, solicited_addr, daddr,
- IPPROTO_ICMPV6, len) < 0)
- {
- kfree_skb(skb, FREE_WRITE);
- printk(KERN_DEBUG
- "ndisc_send_na: ipv6_build_header returned < 0\n");
- return;
+ /*
+ * build the MAC header
+ */
+
+ if (dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ dev->hard_header(skb, dev, ETH_P_IPV6, ndn->ndn_ha,
+ NULL, len);
+ skb->arp = 1;
+ }
}
- skb->pkt_type = PACKET_NDISC;
-
+ ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len);
+
msg = (struct nd_msg *) skb_put(skb, len);
- msg->icmph.type = NDISC_NEIGHBOUR_ADVERTISEMENT;
- msg->icmph.code = 0;
- msg->icmph.checksum = 0;
+ msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
+ msg->icmph.icmp6_code = 0;
+ msg->icmph.icmp6_cksum = 0;
msg->icmph.icmp6_unused = 0;
msg->icmph.icmp6_router = router;
@@ -611,152 +514,192 @@ void ndisc_send_na(struct device *dev, struct neighbour *neigh,
/* Set the target address. */
ipv6_addr_copy(&msg->target, solicited_addr);
- if (inc_opt)
- {
+ if (inc_opt) {
/* Set the source link-layer address option. */
msg->opt.opt_type = ND_OPT_TARGET_LL_ADDR;
msg->opt.opt_len = opt_len;
memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
- if ((opt_len << 3) - (2 + dev->addr_len))
- {
+ if ((opt_len << 3) - (2 + dev->addr_len)) {
memset(msg->opt.link_addr + dev->addr_len, 0,
(opt_len << 3) - (2 + dev->addr_len));
}
}
/* checksum */
- msg->icmph.checksum = csum_ipv6_magic(solicited_addr, daddr, len,
- IPPROTO_ICMPV6,
- csum_partial((__u8 *) msg,
- len, 0));
+ msg->icmph.icmp6_cksum = csum_ipv6_magic(solicited_addr, daddr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) msg,
+ len, 0));
- ipv6_queue_xmit(sk, skb->dev, skb, 1);
+ dev_queue_xmit(skb);
}
void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
struct in6_addr *solicit,
struct in6_addr *daddr, struct in6_addr *saddr)
{
- struct sock *sk = (struct sock *) ndisc_socket.data;
+ unsigned char ha[MAX_ADDR_LEN];
+ struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
struct nd_msg *msg;
- int len, opt_len;
+ int len, opt_len;
+ void *h_dest;
int err;
+ NDBG(("ndisc_send_ns(%s,%p): ", (dev ? dev->name : "[NULL]"), neigh));
+ if(daddr)
+ NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+ daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+ daddr->s6_addr16[6], daddr->s6_addr16[7]));
+ if(saddr)
+ NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+ saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+ saddr->s6_addr16[6], saddr->s6_addr16[7]));
+ if(solicit)
+ NDBG(("solicit[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ solicit->s6_addr16[0], solicit->s6_addr16[1],
+ solicit->s6_addr16[2], solicit->s6_addr16[3],
+ solicit->s6_addr16[4], solicit->s6_addr16[5],
+ solicit->s6_addr16[6], solicit->s6_addr16[7]));
+ NDBG(("\n"));
+
/* length of addr in 8 octet groups.*/
opt_len = ((dev->addr_len + 1) >> 3) + 1;
- len = sizeof(struct icmpv6hdr) + sizeof(struct in6_addr) +
+ len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr) +
(opt_len << 3);
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
- if (skb == NULL)
- {
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+ if (skb == NULL) {
+#if ND_DEBUG >= 1
printk(KERN_DEBUG "send_ns: alloc skb failed\n");
+#endif
return;
}
- skb->free=1;
skb->pkt_type = PACKET_NDISC;
- if (saddr == NULL)
- {
+ if (saddr == NULL) {
struct inet6_ifaddr *ifa;
/* use link local address */
ifa = ipv6_get_lladdr(dev);
if (ifa)
- {
saddr = &ifa->addr;
- }
}
- if(ipv6_addr_type(daddr) == IPV6_ADDR_MULTICAST)
- {
- nd_stats.snt_probes_mcast++;
+ if ((ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST)) {
+ nd_stats.snt_probes_mcast++;
+ ipv6_mc_map(daddr, ha);
+ h_dest = ha;
+ } else {
+ if (neigh == NULL) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "send_ns: ucast destination "
+ "with null neighbour\n");
+#endif
+ return;
+ }
+ h_dest = neigh->ha;
+ nd_stats.snt_probes_ucast++;
}
- else
- {
- nd_stats.snt_probes_ucast++;
+
+ if (dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ dev->hard_header(skb, dev, ETH_P_IPV6, h_dest, NULL,
+ len);
+ skb->arp = 1;
+ }
}
- if (ipv6_bld_hdr_2(sk, skb, dev, neigh, saddr, daddr, IPPROTO_ICMPV6,
- len) < 0 )
- {
- kfree_skb(skb, FREE_WRITE);
- printk(KERN_DEBUG
- "ndisc_send_ns: ipv6_build_header returned < 0\n");
- return;
- }
+ ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
- msg = (struct nd_msg *)skb_put(skb, len);
- msg->icmph.type = NDISC_NEIGHBOUR_SOLICITATION;
- msg->icmph.code = 0;
- msg->icmph.checksum = 0;
- msg->icmph.icmp6_unused = 0;
+ msg = (struct nd_msg *)skb_put(skb, len);
+ msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
+ msg->icmph.icmp6_code = 0;
+ msg->icmph.icmp6_cksum = 0;
+ msg->icmph.icmp6_unused = 0;
- /* Set the target address. */
- ipv6_addr_copy(&msg->target, solicit);
+ /* Set the target address. */
+ ipv6_addr_copy(&msg->target, solicit);
- /* Set the source link-layer address option. */
- msg->opt.opt_type = ND_OPT_SOURCE_LL_ADDR;
- msg->opt.opt_len = opt_len;
+ /* Set the source link-layer address option. */
+ msg->opt.opt_type = ND_OPT_SOURCE_LL_ADDR;
+ msg->opt.opt_len = opt_len;
- memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
+ memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
- if ((opt_len << 3) - (2 + dev->addr_len))
- {
+ if ((opt_len << 3) - (2 + dev->addr_len)) {
memset(msg->opt.link_addr + dev->addr_len, 0,
(opt_len << 3) - (2 + dev->addr_len));
}
/* checksum */
- msg->icmph.checksum = csum_ipv6_magic(&skb->ipv6_hdr->saddr,
- daddr, len,
- IPPROTO_ICMPV6,
- csum_partial((__u8 *) msg,
- len, 0));
+ msg->icmph.icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,
+ daddr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) msg,
+ len, 0));
/* send it! */
- ipv6_queue_xmit(sk, skb->dev, skb, 1);
+ dev_queue_xmit(skb);
}
void ndisc_send_rs(struct device *dev, struct in6_addr *saddr,
struct in6_addr *daddr)
{
- struct sock *sk = (struct sock *) ndisc_socket.data;
+ struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
- struct icmpv6hdr *hdr;
+ struct icmp6hdr *hdr;
__u8 * opt;
int len, opt_len;
int err;
+ NDBG(("ndisc_send_rs(%s): ", (dev ? dev->name : "[NULL]")));
+ if(daddr)
+ NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+ daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+ daddr->s6_addr16[6], daddr->s6_addr16[7]));
+ if(saddr)
+ NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+ saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+ saddr->s6_addr16[6], saddr->s6_addr16[7]));
+ NDBG(("\n"));
+
/* length of addr in 8 octet groups.*/
opt_len = ((dev->addr_len + 1) >> 3) + 1;
- len = sizeof(struct icmpv6hdr) + (opt_len << 3);
+ len = sizeof(struct icmp6hdr) + (opt_len << 3);
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
- if (skb == NULL)
- {
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+ if (skb == NULL) {
printk(KERN_DEBUG "send_ns: alloc skb failed\n");
return;
}
- skb->free=1;
+ if (dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ unsigned char ha[MAX_ADDR_LEN];
- if (ipv6_bld_hdr_2(sk, skb, dev, NULL, saddr, daddr, IPPROTO_ICMPV6,
- len) < 0 )
- {
- kfree_skb(skb, FREE_WRITE);
- printk(KERN_DEBUG
- "ndisc_send_ns: ipv6_build_header returned < 0\n");
- return;
- }
+ ipv6_mc_map(daddr, ha);
+ dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, len);
+ skb->arp = 1;
+ }
+ }
+
+ ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
- hdr = (struct icmpv6hdr *) skb_put(skb, len);
- hdr->type = NDISC_ROUTER_SOLICITATION;
- hdr->code = 0;
- hdr->checksum = 0;
+ hdr = (struct icmp6hdr *) skb_put(skb, len);
+ hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
+ hdr->icmp6_code = 0;
+ hdr->icmp6_cksum = 0;
hdr->icmp6_unused = 0;
opt = (u8*) (hdr + 1);
@@ -767,27 +710,25 @@ void ndisc_send_rs(struct device *dev, struct in6_addr *saddr,
memcpy(opt + 2, dev->dev_addr, dev->addr_len);
- if ((opt_len << 3) - (2 + dev->addr_len))
- {
+ if ((opt_len << 3) - (2 + dev->addr_len)) {
memset(opt + 2 + dev->addr_len, 0,
(opt_len << 3) - (2 + dev->addr_len));
}
/* checksum */
- hdr->checksum = csum_ipv6_magic(&skb->ipv6_hdr->saddr, daddr, len,
- IPPROTO_ICMPV6,
- csum_partial((__u8 *) hdr, len, 0));
+ hdr->icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) hdr, len, 0));
/* send it! */
- ipv6_queue_xmit(sk, skb->dev, skb, 1);
+ dev_queue_xmit(skb);
}
-static int ndisc_store_hwaddr(struct device *dev, __u8 *opt, int opt_len,
- __u8 *h_addr, int option)
+static int ndisc_store_hwaddr(struct nd_neigh *ndn, __u8 *opt, int opt_len,
+ int option)
{
- while (*opt != option && opt_len)
- {
+ while (*opt != option && opt_len) {
int len;
len = opt[1] << 3;
@@ -802,9 +743,8 @@ static int ndisc_store_hwaddr(struct device *dev, __u8 *opt, int opt_len,
opt_len -= len;
}
- if (*opt == option)
- {
- memcpy(h_addr, opt + 2, dev->addr_len);
+ if (*opt == option) {
+ memcpy(ndn->neigh.ha, opt + 2, ndn->ndn_dev->addr_len);
return 0;
}
@@ -816,63 +756,46 @@ static int ndisc_store_hwaddr(struct device *dev, __u8 *opt, int opt_len,
static void ndisc_timer_handler(unsigned long arg)
{
unsigned long now = jiffies;
- struct neighbour * neigh;
unsigned long ntimer = ~0UL;
int i;
- atomic_inc(&ndisc_lock);
-
- for (i=0; i < NCACHE_NUM_BUCKETS; i++)
- {
- for (neigh = neighbours[i]; neigh;)
- {
- if (neigh->nud_state & NUD_IN_TIMER)
- {
- int time;
-
- if (neigh->expires <= now)
- {
- time = ndisc_event_timer(neigh);
- }
- else
- time = neigh->expires - now;
-
- if (time == 0)
- {
- unsigned long flags;
+ neigh_table_lock(&nd_tbl);
+
+ for (i=0; i < nd_tbl.tbl_size; i++) {
+ struct nd_neigh *ndn, *head;
- save_flags(flags);
- cli();
+ head = (struct nd_neigh *) nd_tbl.hash_buckets[i];
- if (ndisc_lock == 1)
- {
- struct neighbour *old = neigh;
+ if ((ndn = head) == NULL)
+ continue;
- neigh = neigh->next;
- ndisc_release_neigh(old);
- restore_flags(flags);
- continue;
- }
+ do {
+ if (ndn->ndn_nud_state & NUD_IN_TIMER) {
+ unsigned long time;
- restore_flags(flags);
- }
+ time = ndn->ndn_expires - now;
- ntimer = min(ntimer, time);
+ if ((long) time <= 0)
+ time = ndisc_event_timer(ndn);
+
+ if (time)
+ ntimer = min(ntimer, time);
}
- neigh = neigh->next;
- }
+ ndn = (struct nd_neigh *) ndn->neigh.next;
+
+ } while (ndn != head);
}
- if (ntimer != (~0UL))
- {
- ndisc_timer.expires = jiffies + ntimer;
+ if (ntimer != (~0UL)) {
+ ndisc_timer.expires = now + ntimer;
add_timer(&ndisc_timer);
}
- ndisc_release_lock();
+
+ neigh_table_unlock(&nd_tbl);
}
-int ndisc_event_timer(struct neighbour *neigh)
+static int ndisc_event_timer(struct nd_neigh *ndn)
{
struct in6_addr *daddr;
struct in6_addr *target;
@@ -880,375 +803,248 @@ int ndisc_event_timer(struct neighbour *neigh)
struct device *dev;
int max_probes;
- if (neigh->nud_state == NUD_DELAY)
- {
- neigh->nud_state = NUD_PROBE;
- }
+ if (ndn->ndn_nud_state == NUD_DELAY)
+ ndn->ndn_nud_state = NUD_PROBE;
- max_probes = (neigh->nud_state == NUD_PROBE ? nd_max_unicast_solicit:
- nd_max_multicast_solicit);
+ max_probes = (ndn->ndn_nud_state == NUD_PROBE ?
+ ipv6_config.nd_max_ucast_solicit:
+ ipv6_config.nd_max_mcast_solicit);
- if (neigh->probes == max_probes)
- {
+ if (ndn->ndn_probes == max_probes) {
struct sk_buff *skb;
- neigh->nud_state = NUD_FAILED;
- neigh->flags |= NCF_INVALID;
+ ndn->ndn_nud_state = NUD_FAILED;
+ ndn->ndn_flags &= ~NTF_COMPLETE;
nd_stats.res_failed++;
- while((skb=skb_dequeue(&neigh->arp_queue)))
- {
+ while((skb=skb_dequeue(&ndn->neigh.arp_queue))) {
/*
* "The sender MUST return an ICMP
* destination unreachable"
*/
icmpv6_send(skb, ICMPV6_DEST_UNREACH,
- ICMPV6_ADDR_UNREACH, 0, neigh->dev);
+ ICMPV6_ADDR_UNREACH, 0, ndn->ndn_dev);
dev_kfree_skb(skb, FREE_WRITE);
}
return 0;
}
-
- neigh->probes++;
- dev = neigh->dev;
- target = &neigh->addr;
+ ndn->ndn_probes++;
- if (neigh->nud_state == NUD_INCOMPLETE)
- {
- addrconf_addr_solict_mult(&neigh->addr, &mcaddr);
- daddr = &mcaddr;
- neigh = NULL;
- }
- else
- {
- daddr = &neigh->addr;
+ dev = ndn->ndn_dev;
+ target = &ndn->ndn_addr;
+
+ if (ndn->ndn_nud_state == NUD_INCOMPLETE) {
+ addrconf_addr_solict_mult(&ndn->ndn_addr, &mcaddr);
+ daddr = &mcaddr;
+ ndn = NULL;
+ } else {
+ daddr = &ndn->ndn_addr;
}
- ndisc_send_ns(dev, neigh, target, daddr, NULL);
+ ndisc_send_ns(dev, (struct neighbour *) ndn, target, daddr, NULL);
- return nd_retrans_timer;
+ return ipv6_config.nd_retrans_time;
}
void ndisc_event_send(struct neighbour *neigh, struct sk_buff *skb)
{
- unsigned long now = jiffies;
+ struct nd_neigh *ndn = (struct nd_neigh *) neigh;
struct in6_addr daddr;
+ unsigned long now = jiffies;
struct in6_addr *saddr = NULL;
- switch (neigh->nud_state) {
+ if ((ndn->ndn_flags & NCF_NOARP))
+ return;
+
+ switch (ndn->ndn_nud_state) {
case NUD_FAILED:
- neigh->probes = 0;
+ ndn->ndn_probes = 0;
case NUD_NONE:
-
- if (skb && !skb->stamp.tv_sec)
- {
+ if (skb && !skb->stamp.tv_sec) {
/*
* skb->stamp allows us to know if we are
* originating the skb or forwarding it.
* (it is set on netif_rx)
*/
- saddr = &skb->ipv6_hdr->saddr;
+ saddr = &skb->nh.ipv6h->saddr;
}
- neigh->nud_state = NUD_INCOMPLETE;
- addrconf_addr_solict_mult(&neigh->addr, &daddr);
- ndisc_send_ns(neigh->dev, NULL, &neigh->addr, &daddr, saddr);
- ndisc_add_timer(neigh, nd_retrans_timer);
+ ndn->ndn_nud_state = NUD_INCOMPLETE;
+ addrconf_addr_solict_mult(&ndn->ndn_addr, &daddr);
+ ndisc_send_ns(ndn->ndn_dev, NULL, &ndn->ndn_addr, &daddr,
+ saddr);
+ ndisc_add_timer(ndn, ipv6_config.nd_retrans_time);
break;
case NUD_REACHABLE:
- if (now - neigh->tstamp < nd_reachable_time)
+ if ((now - ndn->ndn_tstamp) < nd_reachable_time)
break;
case NUD_STALE:
- neigh->nud_state = NUD_DELAY;
- ndisc_add_timer(neigh, nd_delay_first_probe);
+ ndn->ndn_nud_state = NUD_DELAY;
+ ndisc_add_timer(ndn, ipv6_config.nd_delay_probe_time);
}
}
/*
* Received a neighbour announce
*/
-void ndisc_event_na(struct neighbour *neigh, unsigned char * opt, int opt_len,
+void ndisc_event_na(struct nd_neigh *ndn, unsigned char *opt, int opt_len,
int solicited, int override)
{
struct sk_buff *skb;
- if (neigh->nud_state == NUD_NONE)
- {
- neigh->nud_state = NUD_INCOMPLETE;
- }
+ NDBG(("ndisc_event_na(%p,%p,%d,%d,%d)\n", ndn, opt, opt_len,
+ solicited, override));
- if (neigh->nud_state == NUD_INCOMPLETE || override)
- {
+ if (ndn->ndn_nud_state == NUD_NONE)
+ ndn->ndn_nud_state = NUD_INCOMPLETE;
- if (opt_len == 0)
- {
+ if (ndn->ndn_nud_state == NUD_INCOMPLETE || override) {
+ if (opt_len == 0) {
printk(KERN_DEBUG "no opt on NA\n");
- }
- else
- {
- /* record hardware address */
-
- neigh->h_dest = neigh->hh_data;
- neigh->flags &= ~NCF_HHVALID;
+ } else {
+ /* Record hardware address. */
+ ndn->ndn_flags |= NTF_COMPLETE;
- if (ndisc_store_hwaddr(neigh->dev, opt, opt_len,
- neigh->h_dest,
- ND_OPT_TARGET_LL_ADDR))
- {
+ if (ndisc_store_hwaddr(ndn, opt, opt_len,
+ ND_OPT_TARGET_LL_ADDR)) {
+#if ND_DEBUG >= 2
printk(KERN_DEBUG
"event_na: invalid TARGET_LL_ADDR\n");
- neigh->h_dest = NULL;
- neigh->nud_state = NUD_NONE;
+#endif
+ ndn->ndn_flags &= ~NTF_COMPLETE;
+ ndn->ndn_nud_state = NUD_NONE;
return;
}
}
}
+ if (solicited || override || ndn->ndn_nud_state == NUD_INCOMPLETE) {
+ ndn->ndn_probes = 0;
+ ndn->ndn_tstamp = jiffies;
- if (solicited || override || neigh->nud_state == NUD_INCOMPLETE)
- {
-
- neigh->probes = 0;
- neigh->tstamp = jiffies;
-
- if (neigh->nud_state & NUD_IN_TIMER)
- {
- ndisc_del_timer(neigh);
- }
+ if (ndn->ndn_nud_state & NUD_IN_TIMER)
+ ndisc_del_timer(ndn);
if (solicited)
- {
- neigh->nud_state = NUD_REACHABLE;
- }
+ ndn->ndn_nud_state = NUD_REACHABLE;
else
- {
- neigh->nud_state = NUD_STALE;
- }
+ ndn->ndn_nud_state = NUD_STALE;
}
- while ((skb=skb_dequeue(&neigh->arp_queue)))
- {
- int priority = SOPRI_NORMAL;
-
- if (skb->sk)
- priority = skb->sk->priority;
-
- dev_queue_xmit(skb, neigh->dev, priority);
- }
+ while ((skb=skb_dequeue(&ndn->neigh.arp_queue)))
+ dev_queue_xmit(skb);
}
-static void ndisc_event_ns(struct in6_addr *saddr, struct sk_buff *skb)
+static struct nd_neigh * ndisc_event_ns(struct in6_addr *saddr,
+ struct sk_buff *skb)
{
- struct neighbour *neigh;
+ struct nd_neigh *ndn;
u8 *opt;
int len;
+ NDBG(("ndisc_event_ns: "));
+ if(saddr)
+ NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+ saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+ saddr->s6_addr16[6], saddr->s6_addr16[7]));
+ NDBG(("\n"));
+
opt = skb->h.raw;
- opt += sizeof(struct icmpv6hdr) + sizeof(struct in6_addr);
+ opt += sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
len = skb->tail - opt;
- neigh = ndisc_retrieve_neigh(skb->dev, saddr);
-
- if (neigh == NULL)
- {
- neigh = ndisc_new_neigh(skb->dev, saddr);
- }
-
- switch(neigh->nud_state) {
- case NUD_REACHABLE:
- case NUD_STALE:
- case NUD_DELAY:
- if (*opt != ND_OPT_SOURCE_LL_ADDR ||
- len != neigh->dev->addr_len ||
- memcmp(neigh->h_dest, opt + 2, len))
- {
- break;
- }
-
- if (neigh->nud_state & NUD_IN_TIMER)
- {
- ndisc_del_timer(neigh);
- }
- default:
- neigh->flags &= ~NCF_HHVALID;
- neigh->h_dest = neigh->hh_data;
-
- if (ndisc_store_hwaddr(neigh->dev, opt, len,
- neigh->h_dest,
- ND_OPT_SOURCE_LL_ADDR))
- {
- printk(KERN_DEBUG
- "event_ns: invalid SOURCE_LL_ADDR\n");
- neigh->h_dest = NULL;
- neigh->nud_state = NUD_NONE;
- return;
- }
-
- neigh->nud_state = NUD_STALE;
- neigh->tstamp = jiffies;
- neigh->probes = 0;
- }
-
-}
-
-static struct rt6_info *ndisc_get_dflt_router(struct device *dev,
- struct in6_addr *addr)
-{
- struct rt6_info *iter;
-
- for (iter = default_rt_list; iter; iter=iter->next)
- {
- if (dev == iter->rt_dev &&
- ipv6_addr_cmp(&iter->rt_dst, addr) == 0)
- {
- return iter;
- }
- }
- return NULL;
-}
+ neigh_table_lock(&nd_tbl);
+
+ ndn = (struct nd_neigh *) neigh_lookup(&nd_tbl, saddr,
+ sizeof(struct in6_addr),
+ skb->dev);
-static void ndisc_add_dflt_router(struct rt6_info *rt)
-{
- struct rt6_info *iter;
+ if (ndn == NULL)
+ ndn = ndisc_new_neigh(skb->dev, saddr);
- rt->rt_ref++;
- rt->fib_node = &routing_table;
- rt6_stats.fib_rt_alloc++;
+ neigh_table_unlock(&nd_tbl);
- if (default_rt_list == NULL)
- {
- default_rt_list = rt;
- return;
- }
+ if (ndn == NULL)
+ return NULL;
- for (iter = default_rt_list; iter->next; iter=iter->next)
- ;
+ switch(ndn->ndn_nud_state) {
+ case NUD_REACHABLE:
+ case NUD_STALE:
+ case NUD_DELAY:
+ if (*opt != ND_OPT_SOURCE_LL_ADDR ||
+ len != ndn->ndn_dev->addr_len ||
+ memcmp(ndn->neigh.ha, opt + 2, len))
+ break;
- iter->next = rt;
-}
+ if (ndn->ndn_nud_state & NUD_IN_TIMER)
+ ndisc_del_timer(ndn);
-static void ndisc_del_dflt_router(struct rt6_info *rt)
-{
- struct rt6_info *iter, *back;
+ /* FALLTHROUGH */
+ default:
+ ndn->ndn_flags |= NTF_COMPLETE;
+
+ if (ndisc_store_hwaddr(ndn, opt, len, ND_OPT_SOURCE_LL_ADDR)) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG
+ "event_ns: invalid SOURCE_LL_ADDR\n");
+#endif
- if (rt == default_rt_list)
- {
- default_rt_list = rt->next;
- }
- else
- {
- back = NULL;
- for (iter = default_rt_list; iter; iter=iter->next)
- {
- if (iter == rt)
- {
- back->next = rt->next;
- break;
- }
- back = iter;
+ ndn->ndn_flags &= ~NTF_COMPLETE;
+ ndn->ndn_nud_state = NUD_NONE;
+ return ndn;
}
- }
- rt->fib_node = NULL;
- rt_release(rt);
-}
+ ndn->ndn_nud_state = NUD_STALE;
+ ndn->ndn_tstamp = jiffies;
+ ndn->ndn_probes = 0;
+ };
-static void ndisc_purge_dflt_routers(void)
-{
- struct rt6_info *iter, *rt;
-
- for (iter = default_rt_list; iter; )
- {
- rt = iter;
- iter=iter->next;
- rt_release(rt);
- }
- default_rt_list = NULL;
+ return ndn;
}
-static void ndisc_ll_addr_update(struct neighbour *neigh, u8* opt, int len,
+
+static void ndisc_ll_addr_update(struct nd_neigh *ndn, u8* opt, int len,
int type)
{
- switch(neigh->nud_state) {
+ switch(ndn->ndn_nud_state) {
case NUD_REACHABLE:
case NUD_STALE:
case NUD_DELAY:
- if (len == neigh->dev->addr_len &&
- memcmp(neigh->h_dest, opt + 2, len) == 0)
- {
+ if (len == ndn->ndn_dev->addr_len &&
+ memcmp(ndn->neigh.ha, opt + 2, len) == 0)
break;
- }
- if (neigh->nud_state & NUD_IN_TIMER)
- {
- ndisc_del_timer(neigh);
- }
+ if (ndn->ndn_nud_state & NUD_IN_TIMER)
+ ndisc_del_timer(ndn);
default:
- neigh->flags &= ~NCF_HHVALID;
- neigh->h_dest = neigh->hh_data;
+ ndn->ndn_flags |= NTF_COMPLETE;
- if (ndisc_store_hwaddr(neigh->dev, opt, len, neigh->h_dest,
- type))
- {
+ if (ndisc_store_hwaddr(ndn, opt, len, type)) {
+#if ND_DEBUG >=1
printk(KERN_DEBUG "NDISC: invalid LL_ADDR\n");
- neigh->h_dest = NULL;
- neigh->nud_state = NUD_NONE;
+#endif
+ ndn->ndn_flags &= ~NTF_COMPLETE;
+ ndn->ndn_nud_state = NUD_NONE;
break;
}
- neigh->nud_state = NUD_STALE;
- neigh->tstamp = jiffies;
- neigh->probes = 0;
- }
-
-}
-
-struct rt6_info * dflt_rt_lookup(void)
-{
- struct rt6_info *match = NULL;
- struct rt6_info *rt;
- int score = -1;
- unsigned long now = jiffies;
-
- for (rt = default_rt_list; rt; rt=rt->next)
- {
- struct neighbour *neigh = rt->rt_nexthop;
-
- if (score < 0)
- {
- score = 0;
- match = rt;
- }
-
- if (neigh->nud_state == NUD_REACHABLE)
- {
- if (score < 1)
- {
- score = 1;
- match = rt;
- }
-
- if (now - neigh->tstamp < nd_reachable_time)
- {
- return rt;
- }
- }
-
- }
-
- return match;
+ ndn->ndn_nud_state = NUD_STALE;
+ ndn->ndn_tstamp = jiffies;
+ ndn->ndn_probes = 0;
+ };
}
static void ndisc_router_discovery(struct sk_buff *skb)
{
struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw;
- struct neighbour *neigh;
+ struct nd_neigh *ndn;
struct inet6_dev *in6_dev;
struct rt6_info *rt;
int lifetime;
@@ -1256,11 +1052,12 @@ static void ndisc_router_discovery(struct sk_buff *skb)
__u8 * opt = (__u8 *)(ra_msg + 1);
+ NDBG(("ndisc_router_discovery(%p)\n", skb));
+
optlen = (skb->tail - skb->h.raw) - sizeof(struct ra_msg);
- if (skb->ipv6_hdr->hop_limit != 255)
- {
- printk(KERN_WARNING
+ if (skb->nh.ipv6h->hop_limit != 255) {
+ printk(KERN_INFO
"NDISC: fake router advertisment received\n");
return;
}
@@ -1270,14 +1067,12 @@ static void ndisc_router_discovery(struct sk_buff *skb)
*/
in6_dev = ipv6_get_idev(skb->dev);
- if (in6_dev == NULL)
- {
+ if (in6_dev == NULL) {
printk(KERN_DEBUG "RA: can't find in6 device\n");
return;
}
- if (in6_dev->if_flags & IF_RS_SENT)
- {
+ if (in6_dev->if_flags & IF_RS_SENT) {
/*
* flag that an RA was received after an RS was sent
* out on this interface.
@@ -1287,81 +1082,57 @@ static void ndisc_router_discovery(struct sk_buff *skb)
lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
- rt = ndisc_get_dflt_router(skb->dev, &skb->ipv6_hdr->saddr);
+ rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
- if (rt && lifetime == 0)
- {
- ndisc_del_dflt_router(rt);
+ if (rt && lifetime == 0) {
+ ip6_del_rt(rt);
rt = NULL;
}
- if (rt == NULL && lifetime)
- {
- printk(KERN_DEBUG "ndisc_rdisc: new default router\n");
-
- rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
- GFP_ATOMIC);
- if (rt)
- {
- neigh = ndisc_retrieve_neigh(skb->dev,
- &skb->ipv6_hdr->saddr);
-
- if (neigh == NULL)
- {
- neigh = ndisc_new_neigh(skb->dev,
- &skb->ipv6_hdr->saddr);
- }
+ if (rt == NULL && lifetime) {
+#if ND_DEBUG >= 2
+ printk(KERN_DEBUG "ndisc_rdisc: adding default router\n");
+#endif
- if (neigh)
- {
- atomic_inc(&neigh->refcnt);
- neigh->flags |= NCF_ROUTER;
-
- memset(rt, 0, sizeof(struct rt6_info));
+ rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
- ipv6_addr_copy(&rt->rt_dst,
- &skb->ipv6_hdr->saddr);
- rt->rt_metric = 1;
- rt->rt_flags = RTF_GATEWAY | RTF_DYNAMIC;
- rt->rt_dev = skb->dev;
- rt->rt_nexthop = neigh;
+ if (rt == NULL) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "route_add failed\n");
+#endif
+ return;
+ }
- ndisc_add_dflt_router(rt);
- }
- else
- {
- kfree(rt);
- }
+ ndn = (struct nd_neigh *) rt->rt6i_nexthop;
+ if (ndn == NULL) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "nd: add default router: null "
+ "neighbour\n");
+#endif
+ return;
}
+ ndn->ndn_flags |= NCF_ROUTER;
}
if (rt)
- {
- rt->rt_expires = jiffies + (HZ * lifetime);
- }
+ rt->rt6i_expires = jiffies + (HZ * lifetime);
if (ra_msg->icmph.icmp6_hop_limit)
- {
- ipv6_hop_limit = ra_msg->icmph.icmp6_hop_limit;
- }
+ ipv6_config.hop_limit = ra_msg->icmph.icmp6_hop_limit;
/*
* Update Reachable Time and Retrans Timer
*/
if (ra_msg->retrans_timer)
- {
- nd_retrans_timer = ntohl(ra_msg->retrans_timer);
- }
+ ipv6_config.nd_retrans_time = ntohl(ra_msg->retrans_timer);
- if (ra_msg->reachable_time)
- {
+ if (ra_msg->reachable_time) {
__u32 rtime = ntohl(ra_msg->reachable_time);
- if (rtime != nd_base_reachable_time)
- {
- nd_base_reachable_time = rtime;
- nd_gc_staletime = 3 * nd_base_reachable_time;
+ if (rtime != ipv6_config.nd_base_reachable_time) {
+ ipv6_config.nd_base_reachable_time = rtime;
+ nd_gc_staletime = 3 * rtime;
nd_reachable_time = rand_reach_time();
}
@@ -1376,22 +1147,22 @@ static void ndisc_router_discovery(struct sk_buff *skb)
len = (opt[1] << 3);
- if (len == 0)
- {
+ if (len == 0) {
printk(KERN_DEBUG "RA: opt has 0 len\n");
break;
}
switch(*opt) {
case ND_OPT_SOURCE_LL_ADDR:
-
+
if (rt == NULL)
break;
- neigh = rt->rt_nexthop;
+ ndn = (struct nd_neigh *) rt->rt6i_nexthop;
- ndisc_ll_addr_update(neigh, opt, len,
- ND_OPT_SOURCE_LL_ADDR);
+ if (ndn)
+ ndisc_ll_addr_update(ndn, opt, len,
+ ND_OPT_SOURCE_LL_ADDR);
break;
case ND_OPT_PREFIX_INFO:
@@ -1399,17 +1170,17 @@ static void ndisc_router_discovery(struct sk_buff *skb)
break;
case ND_OPT_MTU:
-
- if (rt)
- {
+ if (rt) {
int mtu;
struct device *dev;
mtu = htonl(*(__u32 *)(opt+4));
- dev = rt->rt_nexthop->dev;
+ dev = rt->rt6i_dev;
+
+ if (dev == NULL)
+ break;
- if (mtu < 576)
- {
+ if (mtu < 576) {
printk(KERN_DEBUG "NDISC: router "
"announcement with mtu = %d\n",
mtu);
@@ -1417,13 +1188,9 @@ static void ndisc_router_discovery(struct sk_buff *skb)
}
if (dev->change_mtu)
- {
dev->change_mtu(dev, mtu);
- }
else
- {
dev->mtu = mtu;
- }
}
break;
@@ -1433,109 +1200,100 @@ static void ndisc_router_discovery(struct sk_buff *skb)
break;
default:
printk(KERN_DEBUG "unkown option in RA\n");
- }
+ };
optlen -= len;
opt += len;
}
-
}
void ndisc_forwarding_on(void)
{
+
/*
- * forwarding was turned on
+ * Forwarding was turned on.
*/
- ndisc_purge_dflt_routers();
+ rt6_purge_dflt_routers(0);
}
void ndisc_forwarding_off(void)
{
/*
- * forwarding was turned off
+ * Forwarding was turned off.
*/
}
static void ndisc_redirect_rcv(struct sk_buff *skb)
{
- struct icmpv6hdr *icmph;
+ struct icmp6hdr *icmph;
struct in6_addr *dest;
struct in6_addr *target; /* new first hop to destination */
- struct neighbour *neigh;
+ struct nd_neigh *ndn;
struct rt6_info *rt;
int on_link = 0;
int optlen;
u8 * opt;
- if (skb->ipv6_hdr->hop_limit != 255)
- {
+ NDBG(("ndisc_redirect_rcv(%p)\n", skb));
+
+ if (skb->nh.ipv6h->hop_limit != 255) {
printk(KERN_WARNING
"NDISC: fake ICMP redirect received\n");
return;
}
- if (!(ipv6_addr_type(&skb->ipv6_hdr->saddr) & IPV6_ADDR_LINKLOCAL))
- {
+ if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
printk(KERN_WARNING
"ICMP redirect: source address is not linklocal\n");
return;
}
optlen = skb->tail - skb->h.raw;
- optlen -= sizeof(struct icmpv6hdr) + 2 * sizeof(struct in6_addr);
+ optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
- if (optlen < 0)
- {
+ if (optlen < 0) {
printk(KERN_WARNING "ICMP redirect: packet too small\n");
return;
}
- icmph = (struct icmpv6hdr *) skb->h.raw;
+ icmph = (struct icmp6hdr *) skb->h.raw;
target = (struct in6_addr *) (icmph + 1);
dest = target + 1;
- if (ipv6_addr_type(dest) & IPV6_ADDR_MULTICAST)
- {
+ if (ipv6_addr_type(dest) & IPV6_ADDR_MULTICAST) {
printk(KERN_WARNING "ICMP redirect for multicast addr\n");
return;
}
- if (ipv6_addr_cmp(dest, target) == 0)
- {
+ if (ipv6_addr_cmp(dest, target) == 0) {
on_link = 1;
- }
- else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL))
- {
+ } else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL)) {
printk(KERN_WARNING
"ICMP redirect: target address is not linklocal\n");
return;
}
/* passed validation tests */
+ rt = rt6_redirect(dest, &skb->nh.ipv6h->saddr, target, skb->dev,
+ on_link);
- rt = ipv6_rt_redirect(skb->dev, dest, target, on_link);
-
- if (rt == NULL)
- {
+ if (rt == NULL) {
printk(KERN_WARNING "ICMP redirect: no route to host\n");
return;
}
- neigh = rt->rt_nexthop;
+ ndn = (struct nd_neigh *) rt->rt6i_nexthop;
opt = (u8 *) (dest + 1);
- while (optlen > 0)
- {
+ while (optlen > 0) {
int len;
len = (opt[1] << 3);
if (*opt == ND_OPT_TARGET_LL_ADDR)
- {
- ndisc_ll_addr_update(neigh, opt, len,
+ ndisc_ll_addr_update(ndn, opt, len,
ND_OPT_TARGET_LL_ADDR);
- }
opt += len;
optlen -= len;
@@ -1545,12 +1303,14 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
struct in6_addr *target)
{
- struct sock *sk = (struct sock *) ndisc_socket.data;
- int len = sizeof(struct icmpv6hdr) + 2 * sizeof(struct in6_addr);
+ struct sock *sk = ndisc_socket->sk;
+ int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
struct sk_buff *buff;
+ struct nd_neigh *ndn = (struct nd_neigh *) neigh;
struct inet6_ifaddr *ifp;
- struct icmpv6hdr *icmph;
+ struct icmp6hdr *icmph;
struct in6_addr *addrp;
+ struct device *dev;
struct rt6_info *rt;
int ta_len = 0;
u8 *opt;
@@ -1558,53 +1318,68 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
int err;
int hlen;
- rt = fibv6_lookup(&skb->ipv6_hdr->saddr, skb->dev, 0);
-
- if (rt->rt_flags & RTF_GATEWAY)
- {
+ dev = skb->dev;
+ rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev, 0);
+
+ if (rt == NULL || rt->u.dst.error) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "ndisc_send_redirect: hostunreach\n");
+#endif
+ return;
+ }
+
+ if (rt->rt6i_flags & RTF_GATEWAY) {
+#if ND_DEBUG >= 1
printk(KERN_DEBUG "ndisc_send_redirect: not a neighbour\n");
+#endif
return;
}
- if (neigh->nud_state == NUD_REACHABLE)
- {
- ta_len = ((neigh->dev->addr_len + 1) >> 3) + 1;
+ if (ndn->ndn_nud_state == NUD_REACHABLE) {
+ ta_len = ((dev->addr_len + 1) >> 3) + 1;
len += (ta_len << 3);
}
- rd_len = min(536 - len, ntohs(skb->ipv6_hdr->payload_len) + 8);
+ rd_len = min(536 - len, ntohs(skb->nh.ipv6h->payload_len) + 8);
rd_len &= ~0x7;
len += rd_len;
- ifp = ipv6_get_lladdr(skb->dev);
+ ifp = ipv6_get_lladdr(dev);
- if (ifp == NULL)
- {
+ if (ifp == NULL) {
+#if ND_DEBUG >= 1
printk(KERN_DEBUG "redirect: no link_local addr for dev\n");
+#endif
return;
}
- buff = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
-
- if (buff == NULL)
- {
+ buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+ if (buff == NULL) {
+#if ND_DEBUG >= 2
printk(KERN_DEBUG "ndisc_send_redirect: alloc_skb failed\n");
+#endif
return;
}
-
hlen = 0;
- if (skb->dev->hard_header_len)
- {
- hlen = (skb->dev->hard_header_len + 15) & ~15;
- }
- skb_reserve(buff, hlen + sizeof(struct ipv6hdr));
+ if (dev->hard_header_len) {
+ skb_reserve(buff, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ dev->hard_header(buff, dev, ETH_P_IPV6, ndn->ndn_ha,
+ NULL, len);
+ buff->arp = 1;
+ }
+ }
- icmph = (struct icmpv6hdr *) skb_put(buff, len);
+ ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr,
+ IPPROTO_ICMPV6, len);
- memset(icmph, 0, sizeof(struct icmpv6hdr));
- icmph->type = NDISC_REDIRECT;
+ icmph = (struct icmp6hdr *) skb_put(buff, len);
+
+ memset(icmph, 0, sizeof(struct icmp6hdr));
+ icmph->icmp6_type = NDISC_REDIRECT;
/*
* copy target and destination addresses
@@ -1613,7 +1388,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
addrp = (struct in6_addr *)(icmph + 1);
ipv6_addr_copy(addrp, target);
addrp++;
- ipv6_addr_copy(addrp, &skb->ipv6_hdr->daddr);
+ ipv6_addr_copy(addrp, &skb->nh.ipv6h->daddr);
opt = (u8*) (addrp + 1);
@@ -1621,14 +1396,13 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
* include target_address option
*/
- if (ta_len)
- {
+ if (ta_len) {
int zb;
*(opt++) = ND_OPT_TARGET_LL_ADDR;
*(opt++) = ta_len;
- memcpy(opt, neigh->h_dest, neigh->dev->addr_len);
+ memcpy(opt, neigh->ha, neigh->dev->addr_len);
opt += neigh->dev->addr_len;
/*
@@ -1637,8 +1411,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
*/
zb = (neigh->dev->addr_len + 2) & 0x7;
- if (zb)
- {
+ if (zb) {
int comp;
comp = 8 - zb;
@@ -1656,30 +1429,33 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
*(opt++) = (rd_len >> 3);
opt += 6;
- memcpy(opt, &skb->ipv6_hdr, rd_len - 8);
+ memcpy(opt, &skb->nh.ipv6h, rd_len - 8);
- icmph->checksum = csum_ipv6_magic(&ifp->addr, &skb->ipv6_hdr->saddr,
- len, IPPROTO_ICMPV6,
- csum_partial((u8 *) icmph, len, 0));
+ icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
+ len, IPPROTO_ICMPV6,
+ csum_partial((u8 *) icmph, len, 0));
- ipv6_xmit(sk, buff, &ifp->addr, &skb->ipv6_hdr->saddr, NULL, IPPROTO_ICMPV6);
+ dev_queue_xmit(buff);
}
/* Called by upper layers to validate neighbour cache entries. */
void ndisc_validate(struct neighbour *neigh)
{
- if (neigh->nud_state == NUD_INCOMPLETE)
+ struct nd_neigh *ndn = (struct nd_neigh *) neigh;
+
+ if (neigh == NULL)
+ return;
+
+ if (ndn->ndn_nud_state == NUD_INCOMPLETE)
return;
- if (neigh->nud_state == NUD_DELAY)
- {
- ndisc_del_timer(neigh);
- }
+ if (ndn->ndn_nud_state == NUD_DELAY)
+ ndisc_del_timer(ndn);
nd_stats.rcv_upper_conf++;
- neigh->nud_state = NUD_REACHABLE;
- neigh->tstamp = jiffies;
+ ndn->ndn_nud_state = NUD_REACHABLE;
+ ndn->ndn_tstamp = jiffies;
}
int ndisc_rcv(struct sk_buff *skb, struct device *dev,
@@ -1687,29 +1463,33 @@ int ndisc_rcv(struct sk_buff *skb, struct device *dev,
struct ipv6_options *opt, unsigned short len)
{
struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
- struct neighbour *neigh;
+ struct nd_neigh *ndn;
struct inet6_ifaddr *ifp;
- switch (msg->icmph.type) {
+ NDBG(("ndisc_rcv(type=%d) ", msg->icmph.icmp6_type));
+ switch (msg->icmph.icmp6_type) {
case NDISC_NEIGHBOUR_SOLICITATION:
- if ((ifp = ipv6_chk_addr(&msg->target)))
- {
+ NDBG(("NS "));
+ if ((ifp = ipv6_chk_addr(&msg->target))) {
int addr_type;
- if (ifp->flags & DAD_INCOMPLETE)
- {
+ if (ifp->flags & DAD_INCOMPLETE) {
/*
- * DAD failed
+ * DAD failed
+ */
+
+ /* XXX Check if this came in over same interface
+ * XXX we just sent an NS from! That is valid! -DaveM
*/
- printk(KERN_DEBUG "duplicate address\n");
+ printk(KERN_DEBUG "%s: duplicate address\n",
+ ifp->idev->dev->name);
del_timer(&ifp->timer);
return 0;
}
addr_type = ipv6_addr_type(saddr);
- if (addr_type & IPV6_ADDR_UNICAST)
- {
+ if (addr_type & IPV6_ADDR_UNICAST) {
int inc;
/*
@@ -1718,129 +1498,137 @@ int ndisc_rcv(struct sk_buff *skb, struct device *dev,
*/
nd_stats.rcv_probes_ucast++;
- ndisc_event_ns(saddr, skb);
- /* answer solicitation */
- neigh = ndisc_retrieve_neigh(dev, saddr);
+ ndn = ndisc_event_ns(saddr, skb);
+
+ if (ndn == NULL)
+ return 0;
inc = ipv6_addr_type(daddr);
inc &= IPV6_ADDR_MULTICAST;
- ndisc_send_na(dev, neigh, saddr, &ifp->addr,
+ ndisc_send_na(dev, ndn, saddr, &ifp->addr,
ifp->idev->router, 1, inc, inc);
- }
- else
- {
+ } else {
+#if ND_DEBUG >= 1
/* FIXME */
printk(KERN_DEBUG "ns: non unicast saddr\n");
+#endif
}
}
break;
case NDISC_NEIGHBOUR_ADVERTISEMENT:
-
- neigh = ndisc_retrieve_neigh(skb->dev, &msg->target);
- if (neigh)
- {
- if (neigh->flags & NCF_ROUTER)
- {
- if (msg->icmph.icmp6_router == 0)
- {
+ NDBG(("NA "));
+ neigh_table_lock(&nd_tbl);
+ ndn = (struct nd_neigh *)
+ neigh_lookup(&nd_tbl, (void *) &msg->target,
+ sizeof(struct in6_addr), skb->dev);
+ neigh_table_unlock(&nd_tbl);
+
+ if (ndn) {
+ if (ndn->ndn_flags & NCF_ROUTER) {
+ if (msg->icmph.icmp6_router == 0) {
/*
* Change: router to host
*/
-
+#if 0
struct rt6_info *rt;
rt = ndisc_get_dflt_router(skb->dev,
saddr);
if (rt)
- {
ndisc_del_dflt_router(rt);
- }
+#endif
}
- }
- else
- {
+ } else {
if (msg->icmph.icmp6_router)
- {
- neigh->flags |= NCF_ROUTER;
- }
+ ndn->ndn_flags |= NCF_ROUTER;
}
- ndisc_event_na(neigh, (unsigned char *) &msg->opt,
+ ndisc_event_na(ndn, (unsigned char *) &msg->opt,
skb->tail - (u8 *)&msg->opt /*opt_len*/,
msg->icmph.icmp6_solicited,
msg->icmph.icmp6_override);
}
break;
- }
+ };
- if (ipv6_forwarding == 0)
- {
- switch (msg->icmph.type) {
+ if (ipv6_config.forwarding == 0) {
+ switch (msg->icmph.icmp6_type) {
case NDISC_ROUTER_ADVERTISEMENT:
- ndisc_router_discovery(skb);
+ NDBG(("RA "));
+ if (ipv6_config.accept_ra)
+ ndisc_router_discovery(skb);
break;
case NDISC_REDIRECT:
- ndisc_redirect_rcv(skb);
+ NDBG(("REDIR "));
+ if (ipv6_config.accept_redirects)
+ ndisc_redirect_rcv(skb);
break;
- }
+ };
}
return 0;
}
+#ifdef CONFIG_PROC_FS
int ndisc_get_info(char *buffer, char **start, off_t offset, int length,
int dummy)
{
- struct neighbour *neigh;
unsigned long now = jiffies;
int len = 0;
int i;
- atomic_inc(&ndisc_lock);
+ neigh_table_lock(&nd_tbl);
- for (i = 0; i < NCACHE_NUM_BUCKETS; i++)
- {
- for(neigh = neighbours[i]; neigh; neigh=neigh->next)
- {
+ for (i = 0; i < nd_tbl.tbl_size; i++) {
+ struct neighbour *neigh, *head;
+ head = nd_tbl.hash_buckets[i];
+
+ if ((neigh = head) == NULL)
+ continue;
+
+ do {
+ struct nd_neigh *ndn = (struct nd_neigh *) neigh;
int j;
- for (j=0; j<16; j++)
- {
+ for (j=0; j<16; j++) {
sprintf(buffer + len, "%02x",
- neigh->addr.s6_addr[j]);
+ ndn->ndn_addr.s6_addr[j]);
len += 2;
}
len += sprintf(buffer + len,
- " %02x %02x %08lx %08lx %04x %04x ",
- i,
- neigh->nud_state,
- neigh->expires - now,
- now - neigh->tstamp,
- neigh->refcnt,
- neigh->flags);
-
- if (neigh->h_dest)
- {
- for (j=0; j< neigh->dev->addr_len; j++)
- {
+ " %02x %02x %02x %02x %08lx %08lx %08lx %04x %04x %04lx %8s ", i,
+ ndn->ndn_plen,
+ ndn->ndn_type,
+ ndn->ndn_nud_state,
+ ndn->ndn_expires ? ndn->ndn_expires - now : 0,
+ now - ndn->ndn_tstamp,
+ nd_reachable_time,
+ nd_gc_staletime,
+ atomic_read(&ndn->ndn_refcnt),
+ ndn->ndn_flags,
+ ndn->ndn_dev ? ndn->ndn_dev->name : "NULLDEV");
+
+ if ((ndn->ndn_flags & NTF_COMPLETE)) {
+ for (j=0; j< neigh->dev->addr_len; j++) {
sprintf(buffer + len, "%02x",
- neigh->h_dest[j]);
+ neigh->ha[j]);
len += 2;
}
- }
- else
+ } else {
len += sprintf(buffer + len, "000000000000");
+ }
len += sprintf(buffer + len, "\n");
-
- }
+
+ neigh = neigh->next;
+ } while (neigh != head);
}
- ndisc_release_lock();
-
+ neigh_table_unlock(&nd_tbl);
+
*start = buffer + offset;
len -= offset;
@@ -1852,42 +1640,44 @@ int ndisc_get_info(char *buffer, char **start, off_t offset, int length,
struct proc_dir_entry ndisc_proc_entry =
{
- 0, 11, "ndisc_cache",
+ PROC_NET_NDISC, 5, "ndisc",
S_IFREG | S_IRUGO, 1, 0, 0,
0, NULL,
&ndisc_get_info
};
+#endif /* CONFIG_PROC_FS */
-void ndisc_init(struct proto_ops *ops)
+void ndisc_init(struct net_proto_family *ops)
{
struct sock *sk;
- int i = 0;
int err;
- /*
- * Init ndisc_socket
- */
- ndisc_socket.type=SOCK_RAW;
- ndisc_socket.ops=ops;
+ ndisc_inode.i_mode = S_IFSOCK;
+ ndisc_inode.i_sock = 1;
+ ndisc_inode.i_uid = 0;
+ ndisc_inode.i_gid = 0;
+
+ ndisc_socket->inode = &ndisc_inode;
+ ndisc_socket->state = SS_UNCONNECTED;
+ ndisc_socket->type=SOCK_RAW;
- if((err=ops->create(&ndisc_socket, IPPROTO_ICMPV6))<0)
+ if((err=ops->create(ndisc_socket, IPPROTO_ICMPV6))<0)
printk(KERN_DEBUG
"Failed to create the NDISC control socket.\n");
MOD_DEC_USE_COUNT;
- sk = ndisc_socket.data;
+ sk = ndisc_socket->sk;
sk->allocation = GFP_ATOMIC;
sk->net_pinfo.af_inet6.hop_limit = 255;
sk->net_pinfo.af_inet6.priority = 15;
- sk->num = 256; /* Don't receive any data */
+ sk->num = 256;
/*
- * Initialize the neighbours hash buckets.
+ * Initialize the neighbour table
*/
-
- for (; i < NCACHE_NUM_BUCKETS; i++)
- neighbours[i] = NULL;
+
+ neigh_table_init(&nd_tbl, &nd_neigh_ops, NCACHE_NUM_BUCKETS);
/* General ND state machine timer. */
init_timer(&ndisc_timer);
@@ -1897,31 +1687,24 @@ void ndisc_init(struct proto_ops *ops)
/* ND GC timer */
init_timer(&ndisc_gc_timer);
- ndisc_gc_timer.function = ndisc_garbage_collect;
+ ndisc_gc_timer.function = ndisc_periodic_timer;
ndisc_gc_timer.data = 0L;
ndisc_gc_timer.expires = jiffies + nd_gc_interval;
add_timer(&ndisc_gc_timer);
-#ifdef CONFIG_IPV6_MODULE
- ndisc_eth_hook = ndisc_eth_resolv;
- proc_register_dynamic(&proc_net, &ndisc_proc_entry);
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&ndisc_proc_entry);
#endif
}
-#ifdef CONFIG_IPV6_MODULE
+#ifdef MODULE
void ndisc_cleanup(void)
{
- ndisc_eth_hook = NULL;
- proc_unregister(&proc_net, ndisc_proc_entry.low_ino);
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(ndisc_proc_entry.low_ino);
+#endif
del_timer(&ndisc_gc_timer);
del_timer(&ndisc_timer);
}
#endif
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o ndisc.o ndisc.c"
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c
new file mode 100644
index 000000000..b9b811e35
--- /dev/null
+++ b/net/ipv6/proc.c
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ *
+ * This file implements the various access functions for the
+ * PROC file system. This is very similar to the IPv4 version,
+ * except it reports the sockets in the INET6 address family.
+ *
+ * Version: $Id: proc.c,v 1.4 1997/04/20 22:50:44 schenk Exp $
+ *
+ * Authors: David S. Miller (davem@caip.rutgers.edu)
+ *
+ * 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.
+ */
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <net/sock.h>
+#include <net/transp_v6.h>
+
+/* This is the main implementation workhorse of all these routines. */
+static int get__netinfo6(struct proto *pro, char *buffer, int format, char **start,
+ off_t offset, int length)
+{
+ struct sock *sp;
+ struct tcp_opt *tp;
+ int timer_active, timer_active1, timer_active2;
+ unsigned long timer_expires;
+ struct in6_addr *dest, *src;
+ unsigned short destp, srcp;
+ int len = 0, i = 0;
+ off_t pos = 0;
+ off_t begin;
+ char tmpbuf[150];
+
+ if(offset < 149)
+ len += sprintf(buffer, "%-148s\n",
+ " sl " /* 6 */
+ "local_address " /* 38 */
+ "remote_address " /* 38 */
+ "st tx_queue rx_queue tr tm->when retrnsmt" /* 41 */
+ " uid timeout inode"); /* 21 */
+ /*----*/
+ /*144 */
+
+ pos = 149;
+ SOCKHASH_LOCK();
+ sp = pro->sklist_next;
+ while(sp != (struct sock *)pro) {
+ pos += 149;
+ if(pos < offset)
+ goto next;
+ tp = &(sp->tp_pinfo.af_tcp);
+ dest = &sp->net_pinfo.af_inet6.daddr;
+ src = &sp->net_pinfo.af_inet6.rcv_saddr;
+ destp = ntohs(sp->dummy_th.dest);
+ srcp = ntohs(sp->dummy_th.source);
+
+ timer_active1 = del_timer(&tp->retransmit_timer);
+ timer_active2 = del_timer(&sp->timer);
+ if(!timer_active1) tp->retransmit_timer.expires = 0;
+ if(!timer_active2) sp->timer.expires = 0;
+ timer_active = 0;
+ timer_expires = (unsigned) -1;
+ if(timer_active1 && tp->retransmit_timer.expires < timer_expires) {
+ timer_active = timer_active1;
+ timer_expires = tp->retransmit_timer.expires;
+ }
+ if(timer_active2 && sp->timer.expires < timer_expires) {
+ timer_active = timer_active2;
+ timer_expires = sp->timer.expires;
+ }
+ sprintf(tmpbuf, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
+ "%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
+ i,
+ src->s6_addr32[0], src->s6_addr32[1],
+ src->s6_addr32[2], src->s6_addr32[3], srcp,
+ dest->s6_addr32[0], dest->s6_addr32[1],
+ dest->s6_addr32[2], dest->s6_addr32[3], destp,
+ sp->state,
+ format==0?sp->write_seq-tp->snd_una:atomic_read(&sp->wmem_alloc),
+ format==0?tp->rcv_nxt-sp->copied_seq:atomic_read(&sp->rmem_alloc),
+ timer_active, timer_expires-jiffies,
+ tp->retransmits,
+ sp->socket ? sp->socket->inode->i_uid:0,
+ timer_active?sp->timeout:0,
+ sp->socket ? sp->socket->inode->i_ino:0);
+
+ if(timer_active1) add_timer(&tp->retransmit_timer);
+ if(timer_active2) add_timer(&sp->timer);
+ len += sprintf(buffer+len, "%-148s\n", tmpbuf);
+ if(len >= length)
+ break;
+ next:
+ sp = sp->sklist_next;
+ i++;
+ }
+ SOCKHASH_UNLOCK();
+
+ begin = len - (pos - offset);
+ *start = buffer + begin;
+ len -= begin;
+ if(len > length)
+ len = length;
+ return len;
+}
+
+/* These get exported and registered with procfs in af_inet6.c at init time. */
+int tcp6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ return get__netinfo6(&tcpv6_prot, buffer, 0, start, offset, length);
+}
+
+int udp6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ return get__netinfo6(&udpv6_prot, buffer, 1, start, offset, length);
+}
+
+int raw6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ return get__netinfo6(&rawv6_prot, buffer, 1, start, offset, length);
+}
+
+int afinet6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len = 0;
+ len += sprintf(buffer+len, "TCP6: inuse %d highest %d\n",
+ tcpv6_prot.inuse, tcpv6_prot.highestinuse);
+ len += sprintf(buffer+len, "UDP6: inuse %d highest %d\n",
+ udpv6_prot.inuse, udpv6_prot.highestinuse);
+ len += sprintf(buffer+len, "RAW6: inuse %d highest %d\n",
+ rawv6_prot.inuse, rawv6_prot.highestinuse);
+ *start = buffer + offset;
+ len -= offset;
+ if(len > length)
+ len = length;
+ return len;
+}
diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c
index 7ba6f5be1..3ec242adb 100644
--- a/net/ipv6/protocol.c
+++ b/net/ipv6/protocol.c
@@ -1,3 +1,20 @@
+/*
+ * 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.
+ *
+ * AF_INET6 protocol dispatch tables.
+ *
+ * Version: $Id: protocol.c,v 1.5 1997/03/18 18:24:44 davem Exp $
+ *
+ * Authors: Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * 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.
+ */
+
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -27,8 +44,7 @@ struct inet6_protocol *inet6_get_protocol(unsigned char prot)
struct inet6_protocol *p;
hash = prot & (MAX_INET_PROTOS - 1);
- for (p = inet6_protos[hash] ; p != NULL; p=p->next)
- {
+ for (p = inet6_protos[hash] ; p != NULL; p=p->next) {
if (p->protocol == prot)
return((struct inet6_protocol *) p);
}
@@ -41,7 +57,7 @@ void inet6_add_protocol(struct inet6_protocol *prot)
struct inet6_protocol *p2;
hash = prot->protocol & (MAX_INET_PROTOS - 1);
- prot ->next = inet6_protos[hash];
+ prot->next = inet6_protos[hash];
inet6_protos[hash] = prot;
prot->copy = 0;
@@ -50,10 +66,8 @@ void inet6_add_protocol(struct inet6_protocol *prot)
*/
p2 = (struct inet6_protocol *) prot->next;
- while(p2 != NULL)
- {
- if (p2->protocol == prot->protocol)
- {
+ while(p2 != NULL) {
+ if (p2->protocol == prot->protocol) {
prot->copy = 1;
break;
}
@@ -72,22 +86,19 @@ int inet6_del_protocol(struct inet6_protocol *prot)
unsigned char hash;
hash = prot->protocol & (MAX_INET_PROTOS - 1);
- if (prot == inet6_protos[hash])
- {
+ if (prot == inet6_protos[hash]) {
inet6_protos[hash] = (struct inet6_protocol *) inet6_protos[hash]->next;
return(0);
}
p = (struct inet6_protocol *) inet6_protos[hash];
- while(p != NULL)
- {
+ while(p != NULL) {
/*
* We have to worry if the protocol being deleted is
* the last one on the list, then we may need to reset
* someone's copied bit.
*/
- if (p->next != NULL && p->next == prot)
- {
+ if (p->next != NULL && p->next == prot) {
/*
* if we are the last one with this protocol and
* there is a previous one, reset its copy bit.
@@ -104,9 +115,3 @@ int inet6_del_protocol(struct inet6_protocol *prot)
}
return(-1);
}
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o protocol.o protocol.c"
- * End:
- */
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index bab6514d6..303649705 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -7,7 +7,7 @@
*
* Adapted from linux/net/ipv4/raw.c
*
- * $Id: raw.c,v 1.5 1996/10/29 22:45:53 roque Exp $
+ * $Id: raw.c,v 1.12 1997/04/01 02:23:34 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -29,34 +29,170 @@
#include <net/sock.h>
#include <net/snmp.h>
-#include <net/ip.h>
-#include <net/udp.h>
-
#include <net/ipv6.h>
#include <net/ndisc.h>
#include <net/protocol.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/transp_v6.h>
+#include <net/rawv6.h>
+
#include <asm/uaccess.h>
+struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
+
+static void raw_v6_hash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+
+ num &= (RAWV6_HTABLE_SIZE - 1);
+ skp = &raw_v6_htable[num];
+ SOCKHASH_LOCK();
+ sk->next = *skp;
+ *skp = sk;
+ sk->hashent = num;
+ SOCKHASH_UNLOCK();
+}
+
+static void raw_v6_unhash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+
+ num &= (RAWV6_HTABLE_SIZE - 1);
+ skp = &raw_v6_htable[num];
+
+ SOCKHASH_LOCK();
+ while(*skp != NULL) {
+ if(*skp == sk) {
+ *skp = sk->next;
+ break;
+ }
+ skp = &((*skp)->next);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static void raw_v6_rehash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+ int oldnum = sk->hashent;
+
+ num &= (RAWV6_HTABLE_SIZE - 1);
+ skp = &raw_v6_htable[oldnum];
+
+ SOCKHASH_LOCK();
+ while(*skp != NULL) {
+ if(*skp == sk) {
+ *skp = sk->next;
+ break;
+ }
+ skp = &((*skp)->next);
+ }
+ sk->next = raw_v6_htable[num];
+ raw_v6_htable[num] = sk;
+ sk->hashent = num;
+ SOCKHASH_UNLOCK();
+}
+
+static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+ struct ipv6_mc_socklist *mc;
+
+ for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+ if (ipv6_addr_cmp(&mc->addr, addr) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Grumble... icmp and ip_input want to get at this... */
+struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
+ struct in6_addr *loc_addr, struct in6_addr *rmt_addr)
+{
+ struct sock *s = sk;
+ int addr_type = ipv6_addr_type(loc_addr);
+
+ for(s = sk; s; s = s->next) {
+ if((s->num == num) &&
+ !(s->dead && (s->state == TCP_CLOSE))) {
+ struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
+
+ if (!ipv6_addr_any(&np->daddr) &&
+ ipv6_addr_cmp(&np->daddr, rmt_addr))
+ continue;
+
+ if (!ipv6_addr_any(&np->rcv_saddr)) {
+ if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
+ return(s);
+ if ((addr_type & IPV6_ADDR_MULTICAST) &&
+ inet6_mc_check(s, loc_addr))
+ return (s);
+ continue;
+ }
+ return(s);
+ }
+ }
+ return NULL;
+}
+
+/* This cleans up af_inet6 a bit. -DaveM */
+static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
+ __u32 v4addr = 0;
+ int addr_type;
+
+ /* Check these errors. */
+ if (sk->state != TCP_CLOSE || (addr_len < sizeof(struct sockaddr_in6)))
+ return -EINVAL;
+
+ addr_type = ipv6_addr_type(&addr->sin6_addr);
+
+ /* Check if the address belongs to the host. */
+ if (addr_type == IPV6_ADDR_MAPPED) {
+ v4addr = addr->sin6_addr.s6_addr32[3];
+ if (__ip_chk_addr(v4addr) != IS_MYADDR)
+ return(-EADDRNOTAVAIL);
+ } else {
+ if (addr_type != IPV6_ADDR_ANY) {
+ /* ipv4 addr of the socket is invalid. Only the
+ * unpecified and mapped address have a v4 equivalent.
+ */
+ v4addr = LOOPBACK4_IPV6;
+ if (!(addr_type & IPV6_ADDR_MULTICAST)) {
+ if (ipv6_chk_addr(&addr->sin6_addr) == NULL)
+ return(-EADDRNOTAVAIL);
+ }
+ }
+ }
+
+ sk->rcv_saddr = v4addr;
+ sk->saddr = v4addr;
+ memcpy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr,
+ sizeof(struct in6_addr));
+ if (!(addr_type & IPV6_ADDR_MULTICAST))
+ memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr,
+ sizeof(struct in6_addr));
+ return 0;
+}
+
void rawv6_err(struct sock *sk, int type, int code, unsigned char *buff,
struct in6_addr *saddr, struct in6_addr *daddr)
{
if (sk == NULL)
return;
-
}
static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb)
{
/* Charge it to the socket. */
-
- if (sock_queue_rcv_skb(sk,skb)<0)
- {
+ if (sock_queue_rcv_skb(sk,skb)<0) {
/* ip_statistics.IpInDiscards++; */
- skb->sk=NULL;
kfree_skb(skb, FREE_READ);
return 0;
}
@@ -69,7 +205,7 @@ static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb)
* This is next to useless...
* if we demultiplex in network layer we don't need the extra call
* just to queue the skb...
- * maybe we could have the network decide uppon an hint if it
+ * maybe we could have the network decide uppon a hint if it
* should call raw_rcv for demultiplexing
*/
int rawv6_rcv(struct sk_buff *skb, struct device *dev,
@@ -81,11 +217,9 @@ int rawv6_rcv(struct sk_buff *skb, struct device *dev,
sk = skb->sk;
if (sk->ip_hdrincl)
- {
- skb->h.raw = (unsigned char *) skb->ipv6_hdr;
- }
+ skb->h.raw = skb->nh.raw;
- if (sk->users) {
+ if (sk->sock_readers) {
__skb_queue_tail(&sk->back_log, skb);
return 0;
}
@@ -131,26 +265,16 @@ int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
return err;
/* Copy the address. */
- if (sin6)
- {
+ if (sin6) {
sin6->sin6_family = AF_INET6;
- memcpy(&sin6->sin6_addr, &skb->ipv6_hdr->saddr,
+ memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
sizeof(struct in6_addr));
*addr_len = sizeof(struct sockaddr_in6);
}
- if (msg->msg_control)
- {
- int err;
-
- err = datagram_recv_ctl(sk, msg, skb);
-
- if (err < 0)
- {
- copied = err;
- }
- }
+ if (msg->msg_controllen)
+ datagram_recv_ctl(sk, msg, skb);
skb_free_datagram(sk, skb);
return (copied);
@@ -186,8 +310,7 @@ static int rawv6_frag_cksum(const void *data, struct in6_addr *addr,
hdr->cksum = csum_partial_copy_fromiovecend(buff, hdr->iov, offset,
len, hdr->cksum);
- if (offset == 0)
- {
+ if (offset == 0) {
struct sock *sk;
struct raw6_opt *opt;
struct in6_addr *daddr;
@@ -196,26 +319,19 @@ static int rawv6_frag_cksum(const void *data, struct in6_addr *addr,
opt = &sk->tp_pinfo.tp_raw;
if (hdr->daddr)
- {
daddr = hdr->daddr;
- }
else
- {
daddr = addr + 1;
- }
hdr->cksum = csum_ipv6_magic(addr, daddr, hdr->len,
hdr->proto, hdr->cksum);
- if (opt->offset < len)
- {
+ if (opt->offset < len) {
__u16 *csum;
csum = (__u16 *) (buff + opt->offset);
*csum = hdr->cksum;
- }
- else
- {
+ } else {
/*
* FIXME
* signal an error to user via sk->err
@@ -227,8 +343,7 @@ static int rawv6_frag_cksum(const void *data, struct in6_addr *addr,
}
-static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
- int noblock, int flags)
+static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
{
struct ipv6_options opt_space;
struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name;
@@ -236,25 +351,26 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
struct ipv6_options *opt = NULL;
struct device *dev = NULL;
struct in6_addr *saddr = NULL;
+ struct flowi fl;
int addr_len = msg->msg_namelen;
struct in6_addr *daddr;
struct raw6_opt *raw_opt;
+ int hlimit = -1;
u16 proto;
int err;
/* Mirror BSD error message compatibility */
- if (flags & MSG_OOB)
+ if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
- if (flags & ~MSG_DONTROUTE)
+ if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
return(-EINVAL);
/*
* Get and verify the address.
*/
- if (sin6)
- {
+ if (sin6) {
if (addr_len < sizeof(struct sockaddr_in6))
return(-EINVAL);
@@ -272,14 +388,11 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
daddr = &sin6->sin6_addr;
- if (np->dest && ipv6_addr_cmp(daddr, &np->daddr))
- {
- ipv6_dst_unlock(np->dest);
- np->dest = NULL;
+ if (np->dst && ipv6_addr_cmp(daddr, &np->daddr)) {
+ dst_release(np->dst);
+ np->dst = NULL;
}
- }
- else
- {
+ } else {
if (sk->state != TCP_ESTABLISHED)
return(-EINVAL);
@@ -287,8 +400,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
daddr = &(sk->net_pinfo.af_inet6.daddr);
}
- if (ipv6_addr_any(daddr))
- {
+ if (ipv6_addr_any(daddr)) {
/*
* unspecfied destination address
* treated as error... is this correct ?
@@ -302,14 +414,12 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
if (len + (sk->ip_hdrincl ? 0 : sizeof(struct ipv6hdr)) > 65535)
return -EMSGSIZE;
- if (msg->msg_control)
- {
+ if (msg->msg_controllen) {
opt = &opt_space;
memset(opt, 0, sizeof(struct ipv6_options));
- err = datagram_send_ctl(msg, &dev, &saddr, opt);
- if (err < 0)
- {
+ err = datagram_send_ctl(msg, &dev, &saddr, opt, &hlimit);
+ if (err < 0) {
printk(KERN_DEBUG "invalid msg_control\n");
return err;
}
@@ -317,9 +427,14 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
raw_opt = &sk->tp_pinfo.tp_raw;
+ fl.proto = proto;
+ fl.nl_u.ip6_u.daddr = daddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.dev = dev;
+ fl.uli_u.icmpt.type = 0;
+ fl.uli_u.icmpt.code = 0;
- if (raw_opt->checksum)
- {
+ if (raw_opt->checksum) {
struct rawv6_fakehdr hdr;
hdr.iov = msg->msg_iov;
@@ -329,22 +444,15 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
hdr.proto = proto;
if (opt && opt->srcrt)
- {
hdr.daddr = daddr;
- }
else
- {
hdr.daddr = NULL;
- }
- err = ipv6_build_xmit(sk, rawv6_frag_cksum, &hdr, daddr, len,
- saddr, dev, opt, proto, noblock);
- }
- else
- {
- err = ipv6_build_xmit(sk, rawv6_getfrag, msg->msg_iov, daddr,
- len, saddr, dev, opt, proto,
- noblock);
+ err = ip6_build_xmit(sk, rawv6_frag_cksum, &hdr, &fl, len,
+ opt, hlimit, msg->msg_flags);
+ } else {
+ err = ip6_build_xmit(sk, rawv6_getfrag, msg->msg_iov, &fl, len,
+ opt, hlimit, msg->msg_flags);
}
return err<0?err:len;
@@ -376,8 +484,7 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname,
struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
int val, err;
- switch(level)
- {
+ switch(level) {
case SOL_RAW:
break;
@@ -392,7 +499,7 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname,
default:
return ipv6_setsockopt(sk, level, optname, optval,
optlen);
- }
+ };
if (optval == NULL)
return(-EINVAL);
@@ -401,15 +508,11 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname,
if(err)
return err;
- switch (optname)
- {
+ switch (optname) {
case IPV6_CHECKSUM:
- if (val < 0)
- {
+ if (val < 0) {
opt->checksum = 0;
- }
- else
- {
+ } else {
opt->checksum = 1;
opt->offset = val;
}
@@ -428,11 +531,10 @@ static void rawv6_close(struct sock *sk, unsigned long timeout)
sk->state = TCP_CLOSE;
- if (np->dest)
- {
- ipv6_dst_unlock(np->dest);
- }
+ if (np->dst)
+ dst_release(np->dst);
+ ipv6_sock_mc_close(sk);
destroy_sock(sk);
}
@@ -442,33 +544,33 @@ static int rawv6_init_sk(struct sock *sk)
}
struct proto rawv6_prot = {
- rawv6_close,
- udpv6_connect,
- NULL,
- NULL,
- NULL,
- NULL,
- datagram_select,
- NULL,
- rawv6_init_sk,
- NULL,
- NULL,
- rawv6_setsockopt,
- ipv6_getsockopt, /* FIXME */
- rawv6_sendmsg,
- rawv6_recvmsg,
- NULL, /* No special bind */
- rawv6_rcv_skb,
- 128,
- 0,
- "RAW",
- 0, 0,
- NULL
+ (struct sock *)&rawv6_prot, /* sklist_next */
+ (struct sock *)&rawv6_prot, /* sklist_prev */
+ rawv6_close, /* close */
+ udpv6_connect, /* connect */
+ NULL, /* accept */
+ NULL, /* retransmit */
+ NULL, /* write_wakeup */
+ NULL, /* read_wakeup */
+ datagram_poll, /* poll */
+ NULL, /* ioctl */
+ rawv6_init_sk, /* init */
+ NULL, /* destroy */
+ NULL, /* shutdown */
+ rawv6_setsockopt, /* setsockopt */
+ ipv6_getsockopt, /* getsockopt - FIXME */
+ rawv6_sendmsg, /* sendmsg */
+ rawv6_recvmsg, /* recvmsg */
+ rawv6_bind, /* bind */
+ rawv6_rcv_skb, /* backlog_rcv */
+ raw_v6_hash, /* hash */
+ raw_v6_unhash, /* unhash */
+ raw_v6_rehash, /* rehash */
+ NULL, /* good_socknum */
+ NULL, /* verify_bind */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "RAW", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
};
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o rawv6.o rawv6.c"
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index e76dcc17c..35aa41b95 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -5,6 +5,8 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
+ * $Id: reassembly.c,v 1.7 1997/03/18 18:24:47 davem Exp $
+ *
* Based on: net/ipv4/ip_fragment.c
*
* This program is free software; you can redistribute it and/or
@@ -32,7 +34,6 @@
#include <net/transp_v6.h>
#include <net/rawv6.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
#include <net/addrconf.h>
@@ -57,10 +58,11 @@ static int reasm_frag(struct frag_queue *fq, struct sk_buff **skb,
__u8 *nhptr,
struct frag_hdr *fhdr)
{
- __u32 expires;
+ __u32 expires = jiffies + IPV6_FRAG_TIMEOUT;
int nh;
- expires = del_timer(&fq->timer);
+ if (del_timer(&fq->timer))
+ expires = fq->timer.expires;
/*
* We queue the packet even if it's the last.
@@ -72,18 +74,16 @@ static int reasm_frag(struct frag_queue *fq, struct sk_buff **skb,
*/
reasm_queue(fq, *skb, fhdr);
- if ((fhdr->frag_off & __constant_htons(0x0001)) == 0)
- {
+ if ((fhdr->frag_off & __constant_htons(0x0001)) == 0) {
fq->last_in = 1;
fq->nhptr = nhptr;
}
- if (fq->last_in)
- {
+ if (fq->last_in) {
if ((nh = reasm_frag_1(fq, skb)))
return nh;
}
-
+
fq->timer.expires = expires;
add_timer(&fq->timer);
@@ -96,17 +96,13 @@ int ipv6_reassembly(struct sk_buff **skb, struct device *dev, __u8 *nhptr,
struct frag_hdr *fhdr = (struct frag_hdr *) ((*skb)->h.raw);
struct frag_queue *fq;
- for (fq = ipv6_frag_queue.next; fq != &ipv6_frag_queue; fq = fq->next)
- {
+ for (fq = ipv6_frag_queue.next; fq != &ipv6_frag_queue; fq = fq->next) {
if (fq->id == fhdr->identification)
- {
return reasm_frag(fq, skb, nhptr,fhdr);
- }
}
create_frag_entry(*skb, dev, nhptr, fhdr);
-
return 0;
}
@@ -115,8 +111,7 @@ static void fq_free(struct frag_queue *fq)
{
struct ipv6_frag *fp, *back;
- for(fp = fq->fragments; fp; )
- {
+ for(fp = fq->fragments; fp; ) {
kfree_skb(fp->skb, FREE_READ);
back = fp;
fp=fp->next;
@@ -129,7 +124,6 @@ static void fq_free(struct frag_queue *fq)
fq->prev = fq->next = NULL;
kfree(fq);
-
}
static void frag_expire(unsigned long data)
@@ -143,13 +137,12 @@ static void frag_expire(unsigned long data)
frag = fq->fragments;
- if (frag == NULL)
- {
+ if (frag == NULL) {
printk(KERN_DEBUG "invalid fragment queue\n");
return;
}
- icmpv6_send(frag->skb, ICMPV6_TIME_EXCEEDED, ICMPV6_EXC_FRAGTIME, 0,
+ icmpv6_send(frag->skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0,
frag->skb->dev);
fq_free(fq);
@@ -165,8 +158,7 @@ static void create_frag_entry(struct sk_buff *skb, struct device *dev,
fq = (struct frag_queue *) kmalloc(sizeof(struct frag_queue),
GFP_ATOMIC);
- if (fq == NULL)
- {
+ if (fq == NULL) {
kfree_skb(skb, FREE_READ);
return;
}
@@ -185,8 +177,7 @@ static void create_frag_entry(struct sk_buff *skb, struct device *dev,
fq->nexthdr = fhdr->nexthdr;
- if ((fhdr->frag_off & __constant_htons(0x0001)) == 0)
- {
+ if ((fhdr->frag_off & __constant_htons(0x0001)) == 0) {
fq->last_in = 1;
fq->nhptr = nhptr;
}
@@ -209,16 +200,14 @@ static void reasm_queue(struct frag_queue *fq, struct sk_buff *skb,
nfp = (struct ipv6_frag *) kmalloc(sizeof(struct ipv6_frag),
GFP_ATOMIC);
- if (nfp == NULL)
- {
+ if (nfp == NULL) {
kfree_skb(skb, FREE_READ);
return;
}
-
nfp->offset = ntohs(fhdr->frag_off) & ~0x7;
- nfp->len = (ntohs(skb->ipv6_hdr->payload_len) -
- ((u8 *) (fhdr + 1) - (u8 *) (skb->ipv6_hdr + 1)));
+ nfp->len = (ntohs(skb->nh.ipv6h->payload_len) -
+ ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
nfp->skb = skb;
@@ -228,18 +217,14 @@ static void reasm_queue(struct frag_queue *fq, struct sk_buff *skb,
bptr = &fq->fragments;
-
- for (fp = fq->fragments; fp; fp=fp->next)
- {
+ for (fp = fq->fragments; fp; fp=fp->next) {
if (nfp->offset <= fp->offset)
break;
bptr = &fp->next;
}
- if (fp && fp->offset == nfp->offset)
- {
- if (fp->len != nfp->len)
- {
+ if (fp && fp->offset == nfp->offset) {
+ if (fp->len != nfp->len) {
/* this cannot happen */
printk(KERN_DEBUG "reasm_queue: dup with wrong len\n");
}
@@ -250,7 +235,6 @@ static void reasm_queue(struct frag_queue *fq, struct sk_buff *skb,
return;
}
-
*bptr = nfp;
nfp->next = fp;
}
@@ -270,9 +254,7 @@ static int reasm_frag_1(struct frag_queue *fq, struct sk_buff **skb_in)
__u16 copy;
int nh;
-
- for(fp = fq->fragments; fp; fp=fp->next)
- {
+ for(fp = fq->fragments; fp; fp=fp->next) {
if (offset != fp->offset)
return 0;
@@ -286,15 +268,14 @@ static int reasm_frag_1(struct frag_queue *fq, struct sk_buff **skb_in)
* this means we have all fragments.
*/
- unfrag_len = (u8 *) (tail->fhdr) - (u8 *) (tail->skb->ipv6_hdr + 1);
+ unfrag_len = (u8 *) (tail->fhdr) - (u8 *) (tail->skb->nh.ipv6h + 1);
payload_len = (unfrag_len + tail->offset +
(tail->skb->tail - (__u8 *) (tail->fhdr + 1)));
printk(KERN_DEBUG "reasm: payload len = %d\n", payload_len);
- if ((skb = dev_alloc_skb(sizeof(struct ipv6hdr) + payload_len))==NULL)
- {
+ if ((skb = dev_alloc_skb(sizeof(struct ipv6hdr) + payload_len))==NULL) {
printk(KERN_DEBUG "reasm_frag: no memory for reassembly\n");
fq_free(fq);
return 1;
@@ -302,20 +283,18 @@ static int reasm_frag_1(struct frag_queue *fq, struct sk_buff **skb_in)
copy = unfrag_len + sizeof(struct ipv6hdr);
- skb->ipv6_hdr = (struct ipv6hdr *) skb->data;
+ skb->nh.ipv6h = (struct ipv6hdr *) skb->data;
- skb->free = 1;
skb->dev = fq->dev;
-
nh = fq->nexthdr;
*(fq->nhptr) = nh;
- memcpy(skb_put(skb, copy), tail->skb->ipv6_hdr, copy);
+ memcpy(skb_put(skb, copy), tail->skb->nh.ipv6h, copy);
skb->h.raw = skb->tail;
- skb->ipv6_hdr->payload_len = ntohs(payload_len);
+ skb->nh.ipv6h->payload_len = ntohs(payload_len);
*skb_in = skb;
@@ -323,8 +302,7 @@ static int reasm_frag_1(struct frag_queue *fq, struct sk_buff **skb_in)
* FIXME: If we don't have a checksum we ought to be able
* to defragment and checksum in this pass. [AC]
*/
- for(fp = fq->fragments; fp; )
- {
+ for(fp = fq->fragments; fp; ) {
struct ipv6_frag *back;
memcpy(skb_put(skb, fp->len), (__u8*)(fp->fhdr + 1), fp->len);
@@ -343,12 +321,3 @@ static int reasm_frag_1(struct frag_queue *fq, struct sk_buff **skb_in)
return nh;
}
-
-
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o reassembly.o reassembly.c"
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
new file mode 100644
index 000000000..d04464e26
--- /dev/null
+++ b/net/ipv6/route.c
@@ -0,0 +1,1599 @@
+/*
+ * Linux INET6 implementation
+ * FIB front-end.
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: route.c,v 1.11 1997/04/16 05:58:05 davem Exp $
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#include <net/snmp.h>
+#include <net/ipv6.h>
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+#include <net/netlink.h>
+
+#include <asm/uaccess.h>
+
+#undef CONFIG_RT6_POLICY
+
+/* Set to 3 to get tracing. */
+#define RT6_DEBUG 2
+
+#if RT6_DEBUG >= 3
+#define RDBG(x) printk x
+#else
+#define RDBG(x)
+#endif
+
+static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
+static struct dst_entry *ip6_dst_reroute(struct dst_entry *dst,
+ struct sk_buff *skb);
+
+static int ip6_pkt_discard(struct sk_buff *skb);
+
+struct dst_ops ip6_dst_ops = {
+ AF_INET6,
+ ip6_dst_check,
+ ip6_dst_reroute,
+ NULL
+};
+
+struct rt6_info ip6_null_entry = {
+ {{NULL, ATOMIC_INIT(0), ATOMIC_INIT(0), NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL,
+ ip6_pkt_discard, ip6_pkt_discard, &ip6_dst_ops}},
+ NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0UL,
+ 0, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128}
+};
+
+struct fib6_node ip6_routing_table = {
+ NULL, NULL, NULL, NULL,
+ &ip6_null_entry,
+ 0, RTN_ROOT|RTN_TL_ROOT, 0
+};
+
+#ifdef CONFIG_RT6_POLICY
+int ip6_rt_policy = 0;
+
+struct pol_chain *rt6_pol_list = NULL;
+
+
+static int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb);
+static int rt6_flow_match_out(struct rt6_info *rt, struct sock *sk);
+
+static struct rt6_info *rt6_flow_lookup(struct rt6_info *rt,
+ struct in6_addr *daddr,
+ struct in6_addr *saddr,
+ struct fl_acc_args *args);
+
+#else
+#define ip6_rt_policy (0)
+#endif
+
+static atomic_t rt6_tbl_lock = ATOMIC_INIT(0);
+static int rt6_bh_mask = 0;
+
+#define RT_BH_REQUEST 1
+#define RT_BH_GC 2
+
+static void __rt6_run_bh(void);
+
+/*
+ * request queue operations
+ * FIFO queue/dequeue
+ */
+
+static struct rt6_req request_queue = {
+ 0, NULL, &request_queue, &request_queue
+};
+
+static __inline__ void rtreq_queue(struct rt6_req * req)
+{
+ unsigned long flags;
+ struct rt6_req *next = &request_queue;
+
+ save_flags(flags);
+ cli();
+
+ req->prev = next->prev;
+ req->prev->next = req;
+ next->prev = req;
+ req->next = next;
+ restore_flags(flags);
+}
+
+static __inline__ struct rt6_req * rtreq_dequeue(void)
+{
+ struct rt6_req *next = &request_queue;
+ struct rt6_req *head;
+
+ head = next->next;
+
+ if (head == next)
+ return NULL;
+
+ head->next->prev = head->prev;
+ next->next = head->next;
+
+ head->next = NULL;
+ head->prev = NULL;
+
+ return head;
+}
+
+void rtreq_add(struct rt6_info *rt, int operation)
+{
+ struct rt6_req *rtreq;
+
+ rtreq = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
+
+ if (rtreq == NULL)
+ return;
+
+ memset(rtreq, 0, sizeof(struct rt6_req));
+
+ rtreq->operation = operation;
+ rtreq->ptr = rt;
+ rtreq_queue(rtreq);
+
+ rt6_bh_mask |= RT_BH_REQUEST;
+}
+
+static __inline__ void rt6_lock(void)
+{
+ atomic_inc(&rt6_tbl_lock);
+}
+
+static __inline__ void rt6_unlock(void)
+{
+ if (atomic_dec_and_test(&rt6_tbl_lock) && rt6_bh_mask) {
+ start_bh_atomic();
+ __rt6_run_bh();
+ end_bh_atomic();
+ }
+}
+
+/*
+ * Route lookup
+ */
+
+static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt,
+ struct device *dev,
+ int strict)
+{
+ struct rt6_info *sprt;
+
+ RDBG(("rt6_device_match: (%p,%p,%d) ", rt, dev, strict));
+ if (dev) {
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
+ if (sprt->rt6i_dev == dev) {
+ RDBG(("match --> %p\n", sprt));
+ return sprt;
+ }
+ }
+
+ if (strict) {
+ RDBG(("nomatch & STRICT --> ip6_null_entry\n"));
+ return &ip6_null_entry;
+ }
+ }
+ RDBG(("!dev or (no match and !strict) --> rt(%p)\n", rt));
+ return rt;
+}
+
+/*
+ * pointer to the last default router chosen
+ */
+static struct rt6_info *rt6_dflt_pointer = NULL;
+
+static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, struct device *dev)
+{
+ struct rt6_info *match = NULL;
+ struct rt6_info *sprt;
+ int mpri = 0;
+
+ RDBG(("rt6_best_dflt(%p,%p): ", rt, dev));
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
+ struct nd_neigh *ndn;
+
+ RDBG(("sprt(%p): ", sprt));
+ if ((ndn = (struct nd_neigh *) sprt->rt6i_nexthop)) {
+ int m = -1;
+
+ RDBG(("nxthop(%p,%d) ", ndn, ndn->ndn_nud_state));
+ switch (ndn->ndn_nud_state) {
+ case NUD_REACHABLE:
+ RDBG(("NUD_REACHABLE "));
+ if (sprt != rt6_dflt_pointer) {
+ rt = sprt;
+ RDBG(("sprt!=dflt_ptr -> %p\n",
+ sprt));
+ goto out;
+ }
+ RDBG(("m=2, "));
+ m = 2;
+ break;
+
+ case NUD_DELAY:
+ RDBG(("NUD_DELAY, m=1, "));
+ m = 1;
+ break;
+
+ case NUD_STALE:
+ RDBG(("NUD_STALE, m=1, "));
+ m = 1;
+ break;
+ };
+
+ if (dev && sprt->rt6i_dev == dev) {
+ RDBG(("dev&&sprt->rt6i_dev==dev(%p), m+=2, ", dev));
+ m += 2;
+ }
+
+ if (m >= mpri) {
+ RDBG(("m>=mpri setmatch, "));
+ mpri = m;
+ match = sprt;
+ }
+ }
+ }
+
+ if (match) {
+ RDBG(("match, set rt, "));
+ rt = match;
+ } else {
+ /*
+ * No default routers are known to be reachable.
+ * SHOULD round robin
+ */
+ RDBG(("!match, trying rt6_dflt_pointer, "));
+ if (rt6_dflt_pointer) {
+ struct rt6_info *next;
+
+ if ((next = rt6_dflt_pointer->u.next) &&
+ next->u.dst.error == 0)
+ rt = next;
+ }
+ }
+
+out:
+ rt6_dflt_pointer = rt;
+ RDBG(("returning %p, dflt_ptr set\n", rt));
+ return rt;
+}
+
+struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr,
+ struct device *dev, int flags)
+{
+ struct fib6_node *fn;
+ struct rt6_info *rt;
+
+ RDBG(("rt6_lookup(%p,%p,%p,%x) from %p\n",
+ daddr, saddr, dev, flags, __builtin_return_address(0)));
+ rt6_lock();
+ fn = fib6_lookup(&ip6_routing_table, daddr, saddr);
+
+ rt = rt6_device_match(fn->leaf, dev, 0);
+ rt6_unlock();
+ return rt;
+}
+
+static struct rt6_info *rt6_cow(struct rt6_info *rt, struct in6_addr *daddr,
+ struct in6_addr *saddr)
+{
+ /*
+ * Clone the route.
+ */
+
+ rt = ip6_rt_copy(rt);
+
+ if (rt) {
+ ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
+
+ rt->rt6i_dst.plen = 128;
+ rt->rt6i_flags |= RTF_CACHE;
+
+ if (rt->rt6i_src.plen) {
+ ipv6_addr_copy(&rt->rt6i_src.addr, saddr);
+ rt->rt6i_src.plen = 128;
+ }
+
+ rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, daddr);
+
+ rtreq_add(rt, RT_OPER_ADD);
+ } else {
+ rt = &ip6_null_entry;
+ }
+ return rt;
+}
+
+#ifdef CONFIG_RT6_POLICY
+static __inline__ struct rt6_info *rt6_flow_lookup_in(struct rt6_info *rt,
+ struct sk_buff *skb)
+{
+ struct in6_addr *daddr, *saddr;
+ struct fl_acc_args arg;
+
+ arg.type = FL_ARG_FORWARD;
+ arg.fl_u.skb = skb;
+
+ saddr = &skb->nh.ipv6h->saddr;
+ daddr = &skb->nh.ipv6h->daddr;
+
+ return rt6_flow_lookup(rt, daddr, saddr, &arg);
+}
+
+static __inline__ struct rt6_info *rt6_flow_lookup_out(struct rt6_info *rt,
+ struct sock *sk,
+ struct flowi *fl)
+{
+ struct fl_acc_args arg;
+
+ arg.type = FL_ARG_ORIGIN;
+ arg.fl_u.fl_o.sk = sk;
+ arg.fl_u.fl_o.flow = fl;
+
+ return rt6_flow_lookup(rt, fl->nl_u.ip6_u.daddr, fl->nl_u.ip6_u.saddr,
+ &arg);
+}
+
+#endif
+
+void ip6_route_input(struct sk_buff *skb)
+{
+ struct fib6_node *fn;
+ struct rt6_info *rt;
+ struct dst_entry *dst;
+
+ RDBG(("ip6_route_input(%p) from %p\n", skb, __builtin_return_address(0)));
+ rt6_lock();
+ fn = fib6_lookup(&ip6_routing_table, &skb->nh.ipv6h->daddr,
+ &skb->nh.ipv6h->saddr);
+
+ rt = fn->leaf;
+
+ if ((rt->rt6i_flags & RTF_CACHE)) {
+ if (ip6_rt_policy == 0) {
+ rt = rt6_device_match(rt, skb->dev, 0);
+ goto out;
+ }
+
+#ifdef CONFIG_RT6_POLICY
+ if ((rt->rt6i_flags & RTF_FLOW)) {
+ struct rt6_info *sprt;
+
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
+ if (rt6_flow_match_in(sprt, skb)) {
+ rt = sprt;
+ goto out;
+ }
+ }
+ }
+#endif
+ }
+
+ rt = rt6_device_match(rt, skb->dev, 0);
+
+ if (ip6_rt_policy == 0) {
+ if (!rt->rt6i_nexthop && rt->rt6i_dev &&
+ ((rt->rt6i_flags & RTF_NONEXTHOP) == 0)) {
+ rt = rt6_cow(rt, &skb->nh.ipv6h->daddr,
+ &skb->nh.ipv6h->saddr);
+ }
+ } else {
+#ifdef CONFIG_RT6_POLICY
+ rt = rt6_flow_lookup_in(rt, skb);
+#endif
+ }
+
+out:
+ dst = dst_clone((struct dst_entry *) rt);
+ rt6_unlock();
+
+ skb->dst = dst;
+ dst->input(skb);
+}
+
+struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl)
+{
+ struct fib6_node *fn;
+ struct rt6_info *rt;
+ struct dst_entry *dst;
+ int strict;
+
+ RDBG(("ip6_route_output(%p,%p) from(%p)", sk, fl,
+ __builtin_return_address(0)));
+ strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & IPV6_ADDR_MULTICAST;
+
+ rt6_lock();
+#if RT6_DEBUG >= 3
+ RDBG(("lkup("));
+ if(fl->nl_u.ip6_u.daddr) {
+ struct in6_addr *addr = fl->nl_u.ip6_u.daddr;
+ int i;
+ RDBG(("daddr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+ if(fl->nl_u.ip6_u.saddr) {
+ struct in6_addr *addr = fl->nl_u.ip6_u.saddr;
+ int i;
+ RDBG(("saddr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+#endif
+ fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr,
+ fl->nl_u.ip6_u.saddr);
+
+ RDBG(("-->(%p[%s])) ", fn, fn == &ip6_routing_table ? "ROOT" : "!ROOT"));
+
+ rt = fn->leaf;
+
+ if ((rt->rt6i_flags & RTF_CACHE)) {
+ RDBG(("RTF_CACHE "));
+ if (ip6_rt_policy == 0) {
+ rt = rt6_device_match(rt, fl->dev, strict);
+ RDBG(("devmatch(%p) ", rt));
+ goto out;
+ }
+
+#ifdef CONFIG_RT6_POLICY
+ if ((rt->rt6i_flags & RTF_FLOW)) {
+ struct rt6_info *sprt;
+
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
+ if (rt6_flow_match_out(sprt, sk)) {
+ rt = sprt;
+ goto out;
+ }
+ }
+ }
+#endif
+ }
+ RDBG(("!RTF_CACHE "));
+ if (rt->rt6i_flags & RTF_DEFAULT) {
+ RDBG(("RTF_DEFAULT "));
+ if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF) {
+ rt = rt6_best_dflt(rt, fl->dev);
+ RDBG(("best_dflt(%p) ", rt));
+ }
+ } else {
+ rt = rt6_device_match(rt, fl->dev, strict);
+ RDBG(("!RTF_DEFAULT devmatch(%p) ", rt));
+ }
+
+ if (ip6_rt_policy == 0) {
+ if (!rt->rt6i_nexthop && rt->rt6i_dev &&
+ ((rt->rt6i_flags & RTF_NONEXTHOP) == 0)) {
+ rt = rt6_cow(rt, fl->nl_u.ip6_u.daddr,
+ fl->nl_u.ip6_u.saddr);
+ RDBG(("(!nhop&&rt6i_dev&&!RTF_NONEXTHOP) cow(%p) ", rt));
+ }
+ } else {
+#ifdef CONFIG_RT6_POLICY
+ rt = rt6_flow_lookup_out(rt, sk, fl);
+#endif
+ }
+
+out:
+ dst = dst_clone((struct dst_entry *) rt);
+ rt6_unlock();
+ RDBG(("dclone/ret(%p)\n", dst));
+ return dst;
+}
+
+
+void rt6_ins(struct rt6_info *rt)
+{
+ start_bh_atomic();
+ if (atomic_read(&rt6_tbl_lock) == 1)
+ fib6_add(&ip6_routing_table, rt);
+ else
+ rtreq_add(rt, RT_OPER_ADD);
+ end_bh_atomic();
+}
+
+/*
+ * Destination cache support functions
+ */
+
+struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
+{
+ struct rt6_info *rt;
+
+ RDBG(("ip6dstchk(%p,%08x)[%p]\n", dst, cookie,
+ __builtin_return_address(0)));
+
+ rt = (struct rt6_info *) dst;
+
+ if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
+ if (rt->rt6i_nexthop)
+ ndisc_event_send(rt->rt6i_nexthop, NULL);
+
+ return dst;
+ }
+
+ dst_release(dst);
+ return NULL;
+}
+
+struct dst_entry *ip6_dst_reroute(struct dst_entry *dst, struct sk_buff *skb)
+{
+ /*
+ * FIXME
+ */
+ RDBG(("ip6_dst_reroute(%p,%p)[%p] (AIEEE)\n", dst, skb,
+ __builtin_return_address(0)));
+ return NULL;
+}
+
+/*
+ *
+ */
+
+struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err)
+{
+ struct rt6_info *rt;
+ struct device *dev = NULL;
+ int addr_type;
+
+ RDBG(("ip6_route_add(%p)[%p] ", rtmsg, __builtin_return_address(0)));
+ *err = 0;
+
+ rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
+
+ if (rt == NULL) {
+ RDBG(("dalloc fails, "));
+ *err = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * default... this should be chosen according to route flags
+ */
+
+#if RT6_DEBUG >= 3
+ {
+ struct in6_addr *addr = &rtmsg->rtmsg_dst;
+ int i;
+
+ RDBG(("daddr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ addr = &rtmsg->rtmsg_src;
+ RDBG(("saddr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+#endif
+
+ addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst);
+
+ if (addr_type & IPV6_ADDR_MULTICAST) {
+ RDBG(("MCAST, "));
+ rt->u.dst.input = ip6_mc_input;
+ } else {
+ RDBG(("!MCAST "));
+ rt->u.dst.input = ip6_forward;
+ }
+
+ rt->u.dst.output = dev_queue_xmit;
+
+ if (rtmsg->rtmsg_ifindex)
+ dev = dev_get_by_index(rtmsg->rtmsg_ifindex);
+ if(dev)
+ RDBG(("d[%s] ", dev->name));
+
+ ipv6_addr_copy(&rt->rt6i_dst.addr, &rtmsg->rtmsg_dst);
+ rt->rt6i_dst.plen = rtmsg->rtmsg_dst_len;
+
+ /* XXX Figure out what really is supposed to be happening here -DaveM */
+ ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src);
+ rt->rt6i_src.plen = rtmsg->rtmsg_src_len;
+
+ if ((rt->rt6i_src.plen = rtmsg->rtmsg_src_len)) {
+ RDBG(("splen, "));
+ ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src);
+ } else {
+ RDBG(("!splen, "));
+ }
+ /* XXX */
+
+ if (rtmsg->rtmsg_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
+ struct rt6_info *grt;
+ struct in6_addr *gw_addr;
+ u32 flags = 0;
+
+ RDBG(("RTF_GATEWAY, "));
+ /*
+ * 1. gateway route lookup
+ * 2. ndisc_get_neigh
+ */
+
+ gw_addr = &rtmsg->rtmsg_gateway;
+
+#if RT6_DEBUG >= 3
+ {
+ struct in6_addr *addr = gw_addr;
+ int i;
+
+ RDBG(("gwaddr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+#endif
+
+ if ((rtmsg->rtmsg_flags & RTF_GATEWAY) &&
+ (rtmsg->rtmsg_flags & RTF_ADDRCONF) == 0) {
+ RDBG(("RTF_GATEWAY && !RTF_ADDRCONF, "));
+ if (dev)
+ flags |= RTF_LINKRT;
+
+ grt = rt6_lookup(gw_addr, NULL, dev, flags);
+
+ if (grt == NULL)
+ {
+ RDBG(("!grt, "));
+ *err = -EHOSTUNREACH;
+ goto out;
+ }
+ dev = grt->rt6i_dev;
+ RDBG(("grt(d=%s), ", dev ? dev->name : "NULL"));
+ }
+
+ rt->rt6i_nexthop = ndisc_get_neigh(dev, gw_addr);
+
+ if (rt->rt6i_nexthop == NULL) {
+ RDBG(("!nxthop, "));
+ *err = -ENOMEM;
+ goto out;
+ }
+ RDBG(("nxthop, "));
+ }
+
+ if (dev == NULL) {
+ RDBG(("!dev, "));
+ *err = -ENODEV;
+ goto out;
+ }
+
+ rt->rt6i_metric = rtmsg->rtmsg_metric;
+
+ rt->rt6i_dev = dev;
+ rt->u.dst.pmtu = dev->mtu;
+ rt->rt6i_flags = rtmsg->rtmsg_flags;
+
+ RDBG(("rt6ins(%p) ", rt));
+
+ rt6_lock();
+ rt6_ins(rt);
+ rt6_unlock();
+
+out:
+ if (*err) {
+ RDBG(("dfree(%p) ", rt));
+ dst_free((struct dst_entry *) rt);
+ rt = NULL;
+ }
+ RDBG(("ret(%p)\n", rt));
+ return rt;
+}
+
+int ip6_del_rt(struct rt6_info *rt)
+{
+ rt6_lock();
+
+ start_bh_atomic();
+
+ rt6_dflt_pointer = NULL;
+
+ if (atomic_read(&rt6_tbl_lock) == 1)
+ fib6_del(rt);
+ else
+ rtreq_add(rt, RT_OPER_DEL);
+ end_bh_atomic();
+ rt6_unlock();
+ return 0;
+}
+
+int ip6_route_del(struct in6_rtmsg *rtmsg)
+{
+ return 0;
+}
+
+
+/*
+ * bottom handler, runs with atomic_bh protection
+ */
+void __rt6_run_bh(void)
+{
+ struct rt6_req *rtreq;
+
+ while ((rtreq = rtreq_dequeue())) {
+ switch (rtreq->operation) {
+ case RT_OPER_ADD:
+ fib6_add(&ip6_routing_table, rtreq->ptr);
+ break;
+ case RT_OPER_DEL:
+ fib6_del(rtreq->ptr);
+ break;
+ };
+ kfree(rtreq);
+ }
+ rt6_bh_mask = 0;
+}
+
+/*
+ * NETLINK interface
+ * routing socket moral equivalent
+ */
+
+static int rt6_msgrcv(int unit, struct sk_buff *skb)
+{
+ int count = 0;
+ struct in6_rtmsg *rtmsg;
+ int err;
+
+ while (skb->len) {
+ if (skb->len < sizeof(struct in6_rtmsg)) {
+ count = -EINVAL;
+ goto out;
+ }
+
+ rtmsg = (struct in6_rtmsg *) skb->data;
+ skb_pull(skb, sizeof(struct in6_rtmsg));
+ count += sizeof(struct in6_rtmsg);
+
+ switch (rtmsg->rtmsg_type) {
+ case RTMSG_NEWROUTE:
+ ip6_route_add(rtmsg, &err);
+ break;
+ case RTMSG_DELROUTE:
+ ip6_route_del(rtmsg);
+ break;
+ default:
+ count = -EINVAL;
+ goto out;
+ };
+ }
+
+out:
+ kfree_skb(skb, FREE_READ);
+ return count;
+}
+
+static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg,
+ sizeof(struct in6_rtmsg));
+
+ if (netlink_post(NETLINK_ROUTE6, skb))
+ kfree_skb(skb, FREE_WRITE);
+}
+
+void rt6_sndmsg(int type, struct in6_addr *dst, struct in6_addr *src,
+ struct in6_addr *gw, struct device *dev,
+ int dstlen, int srclen, int metric, __u32 flags)
+{
+ struct sk_buff *skb;
+ struct in6_rtmsg *msg;
+
+ skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ msg = (struct in6_rtmsg *) skb_put(skb, sizeof(struct in6_rtmsg));
+
+ memset(msg, 0, sizeof(struct in6_rtmsg));
+
+ msg->rtmsg_type = type;
+
+ if (dst)
+ ipv6_addr_copy(&msg->rtmsg_dst, dst);
+
+ if (src) {
+ ipv6_addr_copy(&msg->rtmsg_src, src);
+ msg->rtmsg_src_len = srclen;
+ }
+
+ if (gw)
+ ipv6_addr_copy(&msg->rtmsg_gateway, gw);
+
+ msg->rtmsg_dst_len = dstlen;
+ msg->rtmsg_metric = metric;
+
+ if (dev)
+ msg->rtmsg_ifindex = dev->ifindex;
+
+ msg->rtmsg_flags = flags;
+
+ if (netlink_post(NETLINK_ROUTE6, skb))
+ kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ * Handle redirects
+ */
+struct rt6_info *rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr,
+ struct in6_addr *target, struct device *dev,
+ int on_link)
+{
+ struct rt6_info *rt, *tgtr, *nrt;
+
+ RDBG(("rt6_redirect(%s)[%p]: ",
+ dev ? dev->name : "NULL",
+ __builtin_return_address(0)));
+ rt = rt6_lookup(dest, NULL, dev, 0);
+
+ if (rt == NULL || rt->u.dst.error) {
+ RDBG(("!rt\n"));
+ printk(KERN_DEBUG "rt6_redirect: no route to destination\n");
+ return NULL;
+ }
+
+ if (rt->rt6i_flags & RTF_GATEWAY) {
+ /*
+ * This can happen due to misconfiguration
+ * if we are dealing with an "on link" redirect.
+ */
+ RDBG(("RTF_GATEWAY\n"));
+ printk(KERN_DEBUG "rt6_redirect: destination not directly "
+ "connected\n");
+ return NULL;
+ }
+ RDBG(("tgt_lkup, "));
+ tgtr = rt6_lookup(target, NULL, dev, 0);
+
+ if (tgtr == NULL || tgtr->u.dst.error) {
+ /*
+ * duh?! no route to redirect target.
+ * How where we talking to it in the first place ?
+ */
+ RDBG(("!tgtr||dsterr\n"));
+ printk(KERN_DEBUG "rt6_redirect: no route to target\n");
+ return NULL;
+ }
+
+ if ((tgtr->rt6i_flags & RTF_GATEWAY) &&
+ ipv6_addr_cmp(dest, &tgtr->rt6i_gateway) == 0) {
+ RDBG(("tgt RTF_GATEWAY && dstmatch, dup\n"));
+ /*
+ * Check if we already have the right route.
+ */
+#if RT6_DEBUG >= 1
+ printk(KERN_DEBUG "rt6_redirect: duplicate\n");
+#endif
+ return NULL;
+ }
+
+ /*
+ * RFC 1970 specifies that redirects should only be
+ * accepted if they come from the nexthop to the target.
+ * Due to the way default routers are chosen, this notion
+ * is a bit fuzzy and one might need to check all default
+ * routers.
+ */
+
+ if (ipv6_addr_cmp(saddr, &tgtr->rt6i_gateway)) {
+ RDBG(("saddr/tgt->gway match, "));
+ if (tgtr->rt6i_flags & RTF_DEFAULT) {
+ tgtr = ip6_routing_table.leaf;
+
+ for (; tgtr; tgtr = tgtr->u.next) {
+ if (!ipv6_addr_cmp(saddr, &tgtr->rt6i_gateway)) {
+ RDBG(("found srcok, "));
+ goto source_ok;
+ }
+ }
+ }
+ RDBG(("!dflt||!srcok, "));
+ printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
+ "for redirect target\n");
+ }
+
+source_ok:
+
+ /*
+ * We have finally decided to accept it.
+ */
+ RDBG(("srcok: "));
+ if ((tgtr->rt6i_flags & RTF_HOST)) {
+ /*
+ * Already a host route.
+ *
+ */
+ RDBG(("hralready, "));
+ if (tgtr->rt6i_nexthop) {
+ RDBG(("nrel(nxthop) "));
+ neigh_release(tgtr->rt6i_nexthop);
+ }
+ /*
+ * purge hh_cache
+ */
+ tgtr->rt6i_flags |= RTF_MODIFIED | RTF_CACHE;
+ ipv6_addr_copy(&tgtr->rt6i_gateway, dest);
+ tgtr->rt6i_nexthop = ndisc_get_neigh(tgtr->rt6i_dev, dest);
+ RDBG(("hhpurge, getnewneigh, ret(%p)\n", tgtr));
+ return tgtr;
+ }
+
+ nrt = ip6_rt_copy(tgtr);
+ nrt->rt6i_flags = RTF_GATEWAY|RTF_HOST|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
+
+ ipv6_addr_copy(&nrt->rt6i_dst.addr, target);
+ nrt->rt6i_dst.plen = 128;
+
+ ipv6_addr_copy(&nrt->rt6i_gateway, dest);
+ nrt->rt6i_nexthop = ndisc_get_neigh(nrt->rt6i_dev, dest);
+ nrt->rt6i_dev = dev;
+ nrt->u.dst.pmtu = dev->mtu;
+
+ RDBG(("rt6_ins(%p)\n", nrt));
+
+ rt6_lock();
+ rt6_ins(nrt);
+ rt6_unlock();
+
+ return nrt;
+}
+
+/*
+ * Handle ICMP "packet too big" messages
+ * i.e. Path MTU discovery
+ */
+
+void rt6_pmtu_discovery(struct in6_addr *addr, struct device *dev, int pmtu)
+{
+ struct rt6_info *rt;
+
+ if (pmtu < 576 || pmtu > 65536) {
+#if RT6_DEBUG >= 1
+ printk(KERN_DEBUG "rt6_pmtu_discovery: invalid MTU value %d\n",
+ pmtu);
+#endif
+ return;
+ }
+
+ rt = rt6_lookup(addr, NULL, dev, 0);
+
+ if (rt == NULL || rt->u.dst.error) {
+#if RT6_DEBUG >= 2
+ printk(KERN_DEBUG "rt6_pmtu_discovery: no route to host\n");
+#endif
+ return;
+ }
+
+ if (rt->rt6i_flags & RTF_HOST) {
+ /*
+ * host route
+ */
+ rt->u.dst.pmtu = pmtu;
+ rt->rt6i_flags |= RTF_MODIFIED;
+
+ return;
+ }
+
+ rt = ip6_rt_copy(rt);
+ ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
+ rt->rt6i_dst.plen = 128;
+
+ rt->rt6i_flags |= (RTF_HOST | RTF_DYNAMIC | RTF_CACHE);
+
+ rt6_lock();
+ rt6_ins(rt);
+ rt6_unlock();
+}
+
+/*
+ * Misc support functions
+ */
+
+struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
+{
+ struct rt6_info *rt;
+
+ rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
+
+ if (rt) {
+ rt->u.dst.input = ort->u.dst.input;
+ rt->u.dst.output = ort->u.dst.output;
+
+ rt->u.dst.pmtu = ort->u.dst.pmtu;
+ rt->rt6i_dev = ort->rt6i_dev;
+
+ ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
+ rt->rt6i_keylen = ort->rt6i_keylen;
+ rt->rt6i_flags = ort->rt6i_flags;
+ rt->rt6i_metric = ort->rt6i_metric;
+
+ memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
+ memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
+ }
+ return rt;
+}
+
+struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct device *dev)
+{
+ struct rt6_info *rt;
+ struct fib6_node *fn;
+
+ RDBG(("rt6_get_dflt_router(%p,%p)[%p]", addr, dev,
+ __builtin_return_address(0)));
+#if RT6_DEBUG >= 3
+ {
+ int i;
+
+ RDBG(("addr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+#endif
+ RDBG(("\n"));
+ rt6_lock();
+
+ fn = &ip6_routing_table;
+
+ for (rt = fn->leaf; rt; rt=rt->u.next) {
+ if (dev == rt->rt6i_dev &&
+ ipv6_addr_cmp(&rt->rt6i_dst.addr, addr) == 0)
+ break;
+ }
+
+ rt6_unlock();
+ return rt;
+}
+
+struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr,
+ struct device *dev)
+{
+ struct in6_rtmsg rtmsg;
+ struct rt6_info *rt;
+ int err;
+
+ RDBG(("rt6_add_dflt_router(%p,%p)[%p] ", gwaddr, dev,
+ __builtin_return_address(0)));
+#if RT6_DEBUG >= 3
+ {
+ struct in6_addr *addr = gwaddr;
+ int i;
+
+ RDBG(("gwaddr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+#endif
+ RDBG(("\n"));
+
+ memset(&rtmsg, 0, sizeof(struct in6_rtmsg));
+ rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+ ipv6_addr_copy(&rtmsg.rtmsg_gateway, gwaddr);
+ rtmsg.rtmsg_metric = 1024;
+ rtmsg.rtmsg_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | RTF_UP;
+
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+
+ rt = ip6_route_add(&rtmsg, &err);
+
+ if (err) {
+ printk(KERN_DEBUG "rt6_add_dflt: ip6_route_add error %d\n",
+ err);
+ }
+ return rt;
+}
+
+void rt6_purge_dflt_routers(int last_resort)
+{
+ struct rt6_info *rt;
+ struct fib6_node *fn;
+ u32 flags;
+
+ RDBG(("rt6_purge_dflt_routers(%d)[%p]\n", last_resort,
+ __builtin_return_address(0)));
+ fn = &ip6_routing_table;
+
+ rt6_dflt_pointer = NULL;
+
+ if (last_resort)
+ flags = RTF_ALLONLINK;
+ else
+ flags = RTF_DEFAULT | RTF_ADDRCONF;
+
+ for (rt = fn->leaf; rt; ) {
+ if ((rt->rt6i_flags & flags)) {
+ struct rt6_info *drt;
+#if RT6_DEBUG >= 2
+ printk(KERN_DEBUG "rt6_purge_dflt: deleting entry\n");
+#endif
+ drt = rt;
+ rt = rt->u.next;
+ ip6_del_rt(drt);
+ continue;
+ }
+ rt = rt->u.next;
+ }
+}
+
+int ipv6_route_ioctl(unsigned int cmd, void *arg)
+{
+ struct in6_rtmsg rtmsg;
+ int err;
+
+ RDBG(("ipv6_route_ioctl(%d,%p)\n", cmd, arg));
+ switch(cmd) {
+ case SIOCADDRT: /* Add a route */
+ case SIOCDELRT: /* Delete a route */
+ if (!suser())
+ return -EPERM;
+ err = copy_from_user(&rtmsg, arg,
+ sizeof(struct in6_rtmsg));
+ if (err)
+ return -EFAULT;
+
+ switch (cmd) {
+ case SIOCADDRT:
+ ip6_route_add(&rtmsg, &err);
+ break;
+ case SIOCDELRT:
+ err = ip6_route_del(&rtmsg);
+ break;
+ default:
+ err = -EINVAL;
+ };
+
+ if (err == 0)
+ rt6_sndrtmsg(&rtmsg);
+ return err;
+ };
+
+ return -EINVAL;
+}
+
+/*
+ * Drop the packet on the floor
+ */
+
+int ip6_pkt_discard(struct sk_buff *skb)
+{
+ ipv6_statistics.Ip6OutNoRoutes++;
+ kfree_skb(skb, FREE_WRITE);
+ return 0;
+}
+
+/*
+ * Add address
+ */
+
+int ip6_rt_addr_add(struct in6_addr *addr, struct device *dev)
+{
+ struct rt6_info *rt;
+
+ RDBG(("ip6_rt_addr_add(%p,%p)[%p]\n", addr, dev,
+ __builtin_return_address(0)));
+#if RT6_DEBUG >= 3
+ {
+ int i;
+
+ RDBG(("addr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+#endif
+ RDBG(("\n"));
+
+ rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
+ if (rt == NULL)
+ return -ENOMEM;
+
+ memset(rt, 0, sizeof(struct rt6_info));
+
+ rt->u.dst.input = ip6_input;
+ rt->u.dst.output = dev_queue_xmit;
+ rt->rt6i_dev = dev_get("lo");
+ rt->u.dst.pmtu = rt->rt6i_dev->mtu;
+
+ rt->rt6i_flags = RTF_HOST | RTF_LOCAL | RTF_UP | RTF_NONEXTHOP;
+
+ ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
+ rt->rt6i_dst.plen = 128;
+
+ rt6_lock();
+ rt6_ins(rt);
+ rt6_unlock();
+
+ return 0;
+}
+
+#ifdef CONFIG_RT6_POLICY
+
+static int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb)
+{
+ struct flow_filter *frule;
+ struct pkt_filter *filter;
+ int res = 1;
+
+ if ((frule = rt->rt6i_filter) == NULL)
+ goto out;
+
+ if (frule->type != FLR_INPUT) {
+ res = 0;
+ goto out;
+ }
+
+ for (filter = frule->u.filter; filter; filter = filter->next) {
+ __u32 *word;
+
+ word = (__u32 *) skb->h.raw;
+ word += filter->offset;
+
+ if ((*word ^ filter->value) & filter->mask) {
+ res = 0;
+ break;
+ }
+ }
+
+out:
+ return res;
+}
+
+static int rt6_flow_match_out(struct rt6_info *rt, struct sock *sk)
+{
+ struct flow_filter *frule;
+ int res = 1;
+
+ if ((frule = rt->rt6i_filter) == NULL)
+ goto out;
+
+ if (frule->type != FLR_INPUT) {
+ res = 0;
+ goto out;
+ }
+
+ if (frule->u.sk != sk)
+ res = 0;
+out:
+ return res;
+}
+
+static struct rt6_info *rt6_flow_lookup(struct rt6_info *rt,
+ struct in6_addr *daddr,
+ struct in6_addr *saddr,
+ struct fl_acc_args *args)
+{
+ struct flow_rule *frule;
+ struct rt6_info *nrt = NULL;
+ struct pol_chain *pol;
+
+ for (pol = rt6_pol_list; pol; pol = pol->next) {
+ struct fib6_node *fn;
+ struct rt6_info *sprt;
+
+ fn = fib6_lookup(pol->rules, daddr, saddr);
+
+ do {
+ for (sprt = fn->leaf; sprt; sprt=sprt->u.next) {
+ int res;
+
+ frule = sprt->rt6i_flowr;
+#if RT6_DEBUG >= 2
+ if (frule == NULL) {
+ printk(KERN_DEBUG "NULL flowr\n");
+ goto error;
+ }
+#endif
+ res = frule->ops->accept(rt, sprt, args, &nrt);
+
+ switch (res) {
+ case FLOWR_SELECT:
+ goto found;
+ case FLOWR_CLEAR:
+ goto next_policy;
+ case FLOWR_NODECISION:
+ break;
+ default:
+ goto error;
+ };
+ }
+
+ fn = fn->parent;
+
+ } while ((fn->fn_flags & RTN_TL_ROOT) == 0);
+
+ next_policy:
+ }
+
+error:
+ return &ip6_null_entry;
+
+found:
+
+ if (nrt == NULL)
+ goto error;
+
+ nrt->rt6i_flags |= RTF_CACHE;
+ rt6_ins(nrt);
+
+ return nrt;
+}
+#endif
+
+/*
+ * /proc
+ */
+
+#ifdef CONFIG_PROC_FS
+
+#define RT6_INFO_LEN (32 + 4 + 32 + 4 + 32 + 40 + 5 + 1)
+
+struct rt6_proc_arg {
+ char *buffer;
+ int offset;
+ int length;
+ int skip;
+ int len;
+};
+
+static void rt6_info_node(struct fib6_node *fn, void *p_arg)
+{
+ struct rt6_info *rt;
+ struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
+
+ for (rt = fn->leaf; rt; rt = rt->u.next) {
+ int i;
+
+ if (arg->skip < arg->offset / RT6_INFO_LEN) {
+ arg->skip++;
+ continue;
+ }
+
+ if (arg->len >= arg->length)
+ return;
+
+ for (i=0; i<16; i++) {
+ sprintf(arg->buffer + arg->len, "%02x",
+ rt->rt6i_dst.addr.s6_addr[i]);
+ arg->len += 2;
+ }
+ arg->len += sprintf(arg->buffer + arg->len, " %02x ",
+ rt->rt6i_dst.plen);
+
+ for (i=0; i<16; i++) {
+ sprintf(arg->buffer + arg->len, "%02x",
+ rt->rt6i_src.addr.s6_addr[i]);
+ arg->len += 2;
+ }
+ arg->len += sprintf(arg->buffer + arg->len, " %02x ",
+ rt->rt6i_src.plen);
+
+ if (rt->rt6i_nexthop) {
+ for (i=0; i<16; i++) {
+ struct nd_neigh *ndn;
+
+ ndn = (struct nd_neigh *) rt->rt6i_nexthop;
+ sprintf(arg->buffer + arg->len, "%02x",
+ ndn->ndn_addr.s6_addr[i]);
+ arg->len += 2;
+ }
+ } else {
+ sprintf(arg->buffer + arg->len,
+ "00000000000000000000000000000000");
+ arg->len += 32;
+ }
+ arg->len += sprintf(arg->buffer + arg->len,
+ " %08lx %08x %08x %08lx %8s\n",
+ rt->rt6i_metric, rt->rt6i_use,
+ rt->rt6i_ref, rt->rt6i_flags,
+ rt->rt6i_dev ? rt->rt6i_dev->name : "");
+ }
+}
+
+static int rt6_proc_info(char *buffer, char **start, off_t offset, int length,
+ int dummy)
+{
+ struct rt6_proc_arg arg;
+ arg.buffer = buffer;
+ arg.offset = offset;
+ arg.length = length;
+ arg.skip = 0;
+ arg.len = 0;
+
+ fib6_walk_tree(&ip6_routing_table, rt6_info_node, &arg,
+ RT6_FILTER_RTNODES);
+
+ rt6_info_node(&ip6_routing_table, &arg);
+
+ *start = buffer;
+ if (offset)
+ *start += offset % RT6_INFO_LEN;
+
+ arg.len -= offset % RT6_INFO_LEN;
+
+ if(arg.len > length)
+ arg.len = length;
+ if(arg.len < 0)
+ arg.len = 0;
+
+ return arg.len;
+}
+
+#define PTR_SZ (sizeof(void *) * 2)
+#define FI_LINE_SZ (2 * (PTR_SZ) + 7 + 32 + 4 + 32 + 4)
+
+static void rt6_tree_node(struct fib6_node *fn, void *p_arg)
+{
+ struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
+ struct rt6_info *rt;
+ char f;
+ int i;
+
+ rt = fn->leaf;
+
+ if (arg->skip < arg->offset / FI_LINE_SZ) {
+ arg->skip++;
+ return;
+ }
+
+ if (arg->len + FI_LINE_SZ >= arg->length)
+ return;
+
+ f = (fn->fn_flags & RTN_RTINFO) ? 'r' : 'n';
+ arg->len += sprintf(arg->buffer + arg->len, "%p %p %02x %c ",
+ fn, fn->parent, fn->fn_bit, f);
+
+ for (i=0; i<16; i++) {
+ sprintf(arg->buffer + arg->len, "%02x",
+ rt->rt6i_dst.addr.s6_addr[i]);
+ arg->len += 2;
+ }
+ arg->len += sprintf(arg->buffer + arg->len, " %02x ",
+ rt->rt6i_dst.plen);
+
+ for (i=0; i<16; i++) {
+ sprintf(arg->buffer + arg->len, "%02x",
+ rt->rt6i_src.addr.s6_addr[i]);
+ arg->len += 2;
+ }
+ arg->len += sprintf(arg->buffer + arg->len, " %02x\n",
+ rt->rt6i_src.plen);
+
+}
+
+static int rt6_proc_tree(char *buffer, char **start, off_t offset, int length,
+ int dummy)
+{
+ struct rt6_proc_arg arg;
+ arg.buffer = buffer;
+ arg.offset = offset;
+ arg.length = length;
+ arg.skip = 0;
+ arg.len = 0;
+
+ fib6_walk_tree(&ip6_routing_table, rt6_tree_node, &arg, 0);
+
+ *start = buffer;
+ if (offset)
+ *start += offset % RT6_INFO_LEN;
+
+ arg.len -= offset % RT6_INFO_LEN;
+
+ if(arg.len > length)
+ arg.len = length;
+ if(arg.len < 0)
+ arg.len = 0;
+
+ return arg.len;
+}
+
+extern struct rt6_statistics rt6_stats;
+
+static int rt6_proc_stats(char *buffer, char **start, off_t offset, int length,
+ int dummy)
+{
+ int len;
+
+ len = sprintf(buffer, "%04x %04x %04x %04x %04x\n",
+ rt6_stats.fib_nodes, rt6_stats.fib_route_nodes,
+ rt6_stats.fib_rt_alloc, rt6_stats.fib_rt_entries,
+ rt6_stats.fib_rt_cache);
+
+ len -= offset;
+
+ if (len > length)
+ len = length;
+ if(len < 0)
+ len = 0;
+
+ *start = buffer + offset;
+
+ return len;
+}
+
+static struct proc_dir_entry proc_rt6_info = {
+ PROC_NET_RT6, 10, "ipv6_route",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rt6_proc_info
+};
+static struct proc_dir_entry proc_rt6_stats = {
+ PROC_NET_RT6_STATS, 9, "rt6_stats",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rt6_proc_stats
+};
+static struct proc_dir_entry proc_rt6_tree = {
+ PROC_NET_RT6_TREE, 7, "ip6_fib",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rt6_proc_tree
+};
+#endif /* CONFIG_PROC_FS */
+
+void ip6_route_init(void)
+{
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_rt6_info);
+ proc_net_register(&proc_rt6_stats);
+ proc_net_register(&proc_rt6_tree);
+#endif
+ netlink_attach(NETLINK_ROUTE6, rt6_msgrcv);
+}
+
+#ifdef MODULE
+void ip6_route_cleanup(void)
+{
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_RT6);
+ proc_net_unregister(PROC_NET_RT6_TREE);
+ proc_net_unregister(PROC_NET_RT6_STATS);
+#endif
+ netlink_detach(NETLINK_ROUTE6);
+#if 0
+ fib6_flush();
+#endif
+}
+#endif /* MODULE */
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index f96b62229..4b072889c 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -5,6 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
+ * $Id: sit.c,v 1.13 1997/03/18 18:24:50 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -30,7 +31,6 @@
#include <net/protocol.h>
#include <net/transp_v6.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
#include <net/addrconf.h>
#include <net/ip.h>
#include <net/udp.h>
@@ -51,23 +51,15 @@ static void sit_mtu_cache_gc(void);
static int sit_xmit(struct sk_buff *skb,
struct device *dev);
-static int sit_rcv(struct sk_buff *skb,
- struct device *dev,
- struct options *opt,
- __u32 daddr, unsigned short len,
- __u32 saddr, int redo,
- struct inet_protocol * protocol);
+static int sit_rcv(struct sk_buff *skb, unsigned short len);
+static void sit_err(struct sk_buff *skb, unsigned char *dp);
static int sit_open(struct device *dev);
static int sit_close(struct device *dev);
-static struct enet_statistics * sit_get_stats(struct device *dev);
+static struct net_device_stats *sit_get_stats(struct device *dev);
-static void sit_err(int type, int code,
- unsigned char *buff, __u32 info,
- __u32 daddr, __u32 saddr,
- struct inet_protocol *protocol,
- int len);
+extern void udp_err(struct sk_buff *, unsigned char *);
static struct inet_protocol sit_protocol = {
sit_rcv,
@@ -126,10 +118,8 @@ static struct sit_mtu_info * sit_mtu_lookup(__u32 addr)
hash = sit_addr_hash(addr);
- for(iter = sit_mtu_cache[hash]; iter; iter=iter->next)
- {
- if (iter->addr == addr)
- {
+ for(iter = sit_mtu_cache[hash]; iter; iter=iter->next) {
+ if (iter->addr == addr) {
iter->tstamp = jiffies;
break;
}
@@ -139,8 +129,7 @@ static struct sit_mtu_info * sit_mtu_lookup(__u32 addr)
* run garbage collector
*/
- if (jiffies - sit_gc_last_run > SIT_GC_FREQUENCY)
- {
+ if (jiffies - sit_gc_last_run > SIT_GC_FREQUENCY) {
sit_mtu_cache_gc();
sit_gc_last_run = jiffies;
}
@@ -154,26 +143,19 @@ static void sit_mtu_cache_gc(void)
unsigned long now = jiffies;
int i;
- for (i=0; i < SIT_NUM_BUCKETS; i++)
- {
+ for (i=0; i < SIT_NUM_BUCKETS; i++) {
back = NULL;
- for (iter = sit_mtu_cache[i]; iter;)
- {
- if (now - iter->tstamp > SIT_GC_TIMEOUT)
- {
+ for (iter = sit_mtu_cache[i]; iter;) {
+ if (now - iter->tstamp > SIT_GC_TIMEOUT) {
struct sit_mtu_info *old;
old = iter;
iter = iter->next;
if (back)
- {
back->next = iter;
- }
else
- {
sit_mtu_cache[i] = iter;
- }
kfree(old);
continue;
@@ -194,12 +176,12 @@ static int sit_init_dev(struct device *dev)
dev->hard_start_xmit = sit_xmit;
dev->get_stats = sit_get_stats;
- dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+ dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
- memset(dev->priv, 0, sizeof(struct enet_statistics));
+ memset(dev->priv, 0, sizeof(struct net_device_stats));
for (i = 0; i < DEV_NUMBUFFS; i++)
@@ -208,7 +190,7 @@ static int sit_init_dev(struct device *dev)
dev->hard_header = NULL;
dev->rebuild_header = NULL;
dev->set_mac_address = NULL;
- dev->header_cache_bind = NULL;
+ dev->hard_header_cache = NULL;
dev->header_cache_update= NULL;
dev->type = ARPHRD_SIT;
@@ -216,7 +198,7 @@ static int sit_init_dev(struct device *dev)
dev->hard_header_len = MAX_HEADER;
dev->mtu = 1500 - sizeof(struct iphdr);
dev->addr_len = 0;
- dev->tx_queue_len = 2;
+ dev->tx_queue_len = 0;
memset(dev->broadcast, 0, MAX_ADDR_LEN);
memset(dev->dev_addr, 0, MAX_ADDR_LEN);
@@ -238,12 +220,12 @@ static int sit_init_vif(struct device *dev)
int i;
dev->flags = IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST;
- dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+ dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
- memset(dev->priv, 0, sizeof(struct enet_statistics));
+ memset(dev->priv, 0, sizeof(struct net_device_stats));
for (i = 0; i < DEV_NUMBUFFS; i++)
skb_queue_head_init(&dev->buffs[i]);
@@ -261,7 +243,6 @@ static int sit_close(struct device *dev)
return 0;
}
-
int sit_init(void)
{
int i;
@@ -269,9 +250,7 @@ int sit_init(void)
/* register device */
if (register_netdev(&sit_device) != 0)
- {
return -EIO;
- }
inet_add_protocol(&sit_protocol);
@@ -322,9 +301,8 @@ struct device *sit_add_tunnel(__u32 dstaddr)
void sit_cleanup(void)
{
struct sit_vif *vif;
-
- for (vif = vif_list; vif;)
- {
+
+ for (vif = vif_list; vif;) {
struct device *dev = vif->dev;
struct sit_vif *cur;
@@ -343,29 +321,26 @@ void sit_cleanup(void)
}
-
-
/*
* receive IPv4 ICMP messages
*/
-static void sit_err(int type, int code, unsigned char *buff, __u32 info,
- __u32 daddr, __u32 saddr, struct inet_protocol *protocol,
- int len)
-
+static void sit_err(struct sk_buff *skb, unsigned char *dp)
{
- if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
- {
- struct sit_mtu_info *minfo;
+ struct iphdr *iph = (struct iphdr*)dp;
+ int type = skb->h.icmph->type;
+ int code = skb->h.icmph->code;
- info -= sizeof(struct iphdr);
+ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+ struct sit_mtu_info *minfo;
+ unsigned short info = skb->h.icmph->un.frag.mtu - sizeof(struct iphdr);
- minfo = sit_mtu_lookup(daddr);
+ minfo = sit_mtu_lookup(iph->daddr);
- printk(KERN_DEBUG "sit: %08lx pmtu = %ul\n", ntohl(saddr),
+ printk(KERN_DEBUG "sit: %08lx pmtu = %ul\n", ntohl(iph->saddr),
info);
- if (minfo == NULL)
- {
+
+ if (minfo == NULL) {
minfo = kmalloc(sizeof(struct sit_mtu_info),
GFP_ATOMIC);
@@ -373,46 +348,40 @@ static void sit_err(int type, int code, unsigned char *buff, __u32 info,
return;
start_bh_atomic();
- sit_cache_insert(daddr, info);
+ sit_cache_insert(iph->daddr, info);
end_bh_atomic();
- }
- else
- {
+ } else {
minfo->mtu = info;
}
}
}
-static int sit_rcv(struct sk_buff *skb, struct device *idev,
- struct options *opt,
- __u32 daddr, unsigned short len,
- __u32 saddr, int redo, struct inet_protocol * protocol)
+static int sit_rcv(struct sk_buff *skb, unsigned short len)
{
- struct enet_statistics *stats;
+ struct net_device_stats *stats;
struct device *dev = NULL;
struct sit_vif *vif;
+ __u32 saddr = skb->nh.iph->saddr;
- skb->h.raw = skb_pull(skb, skb->h.raw - skb->data);
+ skb->h.raw = skb->nh.raw = skb_pull(skb, skb->h.raw - skb->data);
+
skb->protocol = __constant_htons(ETH_P_IPV6);
- for (vif = vif_list; vif; vif = vif->next)
- {
- if (saddr == vif->dev->pa_dstaddr)
- {
+ for (vif = vif_list; vif; vif = vif->next) {
+ if (saddr == vif->dev->pa_dstaddr) {
dev = vif->dev;
break;
}
}
if (dev == NULL)
- {
dev = &sit_device;
- }
skb->dev = dev;
skb->ip_summed = CHECKSUM_NONE;
- stats = (struct enet_statistics *)dev->priv;
+ stats = (struct net_device_stats *)dev->priv;
+ stats->rx_bytes += len;
stats->rx_packets++;
ipv6_rcv(skb, dev, NULL);
@@ -421,145 +390,100 @@ static int sit_rcv(struct sk_buff *skb, struct device *idev,
static int sit_xmit(struct sk_buff *skb, struct device *dev)
{
- struct enet_statistics *stats;
+ struct net_device_stats *stats;
struct sit_mtu_info *minfo;
struct in6_addr *addr6;
- unsigned long flags;
struct rtable *rt;
struct iphdr *iph;
__u32 saddr;
__u32 daddr;
- __u32 raddr;
int addr_type;
int mtu;
- int len;
+ int headroom;
/*
* Make sure we are not busy (check lock variable)
*/
- stats = (struct enet_statistics *)dev->priv;
- save_flags(flags);
- cli();
- if (dev->tbusy != 0)
- {
- restore_flags(flags);
- printk(KERN_DEBUG "sit_xmit: busy\n");
- return(1);
- }
- dev->tbusy = 1;
- restore_flags(flags);
+ stats = (struct net_device_stats *)dev->priv;
daddr = dev->pa_dstaddr;
- if (daddr == 0)
- {
- struct neighbour *neigh;
+ if (daddr == 0) {
+ struct nd_neigh *neigh = NULL;
- neigh = skb->nexthop;
- if (neigh == NULL)
- {
+ if (skb->dst)
+ neigh = (struct nd_neigh *) skb->dst->neighbour;
+
+ if (neigh == NULL) {
printk(KERN_DEBUG "sit: nexthop == NULL\n");
goto on_error;
}
- addr6 = &neigh->addr;
+ addr6 = &neigh->ndn_addr;
addr_type = ipv6_addr_type(addr6);
- if (addr_type == IPV6_ADDR_ANY)
- {
- addr6 = &skb->ipv6_hdr->daddr;
+ if (addr_type == IPV6_ADDR_ANY) {
+ addr6 = &skb->nh.ipv6h->daddr;
addr_type = ipv6_addr_type(addr6);
}
- if ((addr_type & IPV6_ADDR_COMPATv4) == 0)
- {
+ if ((addr_type & IPV6_ADDR_COMPATv4) == 0) {
printk(KERN_DEBUG "sit_xmit: non v4 address\n");
goto on_error;
}
daddr = addr6->s6_addr32[3];
}
- len = skb->tail - (skb->data + sizeof(struct ipv6hdr));
-
- if (skb->sk)
- {
- atomic_sub(skb->truesize, &skb->sk->wmem_alloc);
- }
-
- skb->sk = NULL;
-
- iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr));
-
- skb->protocol = htons(ETH_P_IP);
-
- /* get route */
-
- rt = ip_rt_route(daddr, skb->localroute);
-
- if (rt == NULL)
- {
+ if (ip_route_output(&rt, daddr, 0, 0, NULL)) {
printk(KERN_DEBUG "sit: no route to host\n");
goto on_error;
}
minfo = sit_mtu_lookup(daddr);
+ /* IP should calculate pmtu correctly,
+ * let's check it...
+ */
+#if 0
if (minfo)
mtu = minfo->mtu;
else
- mtu = rt->rt_dev->mtu;
+#endif
+ mtu = rt->u.dst.pmtu;
- if (mtu > 576 && len > mtu)
- {
+ if (mtu > 576 && skb->tail - (skb->data + sizeof(struct ipv6hdr)) > mtu) {
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
+ ip_rt_put(rt);
goto on_error;
}
- saddr = rt->rt_src;
- skb->dev = rt->rt_dev;
- raddr = rt->rt_gateway;
-
- if (raddr == 0)
- raddr = daddr;
-
- /* now for the device header */
+ headroom = ((rt->u.dst.dev->hard_header_len+15)&~15)+sizeof(struct iphdr);
- skb->arp = 1;
-
- if (skb->dev->hard_header_len)
- {
- int mac;
-
- if (skb->data - skb->head < skb->dev->hard_header_len)
- {
- printk(KERN_DEBUG "sit: space at head < dev header\n");
+ if (skb_headroom(skb) < headroom || skb_shared(skb)) {
+ struct sk_buff *new_skb = skb_realloc_headroom(skb, headroom);
+ if (!new_skb) {
+ ip_rt_put(rt);
goto on_error;
}
-
- if (skb->dev->hard_header)
- {
- mac = skb->dev->hard_header(skb, skb->dev, ETH_P_IP,
- NULL, NULL, len);
-
- if (mac < 0)
- skb->arp = 0;
-
- skb->raddr = raddr;
- }
-
+ dev_kfree_skb(skb, FREE_WRITE);
+ skb = new_skb;
}
+
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
- ip_rt_put(rt);
+ iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr));
+ skb->nh.iph = iph;
+ saddr = rt->rt_src;
+ dst_release(skb->dst);
+ skb->dst = &rt->u.dst;
iph->version = 4;
iph->ihl = 5;
iph->tos = 0; /* tos set to 0... */
if (mtu > 576)
- {
iph->frag_off = htons(IP_DF);
- }
else
iph->frag_off = 0;
@@ -567,33 +491,24 @@ static int sit_xmit(struct sk_buff *skb, struct device *dev)
iph->saddr = saddr;
iph->daddr = daddr;
iph->protocol = IPPROTO_IPV6;
- skb->ip_hdr = iph;
-
+ iph->tot_len = htons(skb->len);
+ iph->id = htons(ip_id_count++);
ip_send_check(iph);
- ip_queue_xmit(NULL, skb->dev, skb, 1);
+ ip_send(skb);
+ stats->tx_bytes += skb->len;
stats->tx_packets++;
- dev->tbusy=0;
return 0;
- on_error:
- kfree_skb(skb, FREE_WRITE);
- dev->tbusy=0;
+on_error:
+ dev_kfree_skb(skb, FREE_WRITE);
stats->tx_errors++;
return 0;
}
-static struct enet_statistics *sit_get_stats(struct device *dev)
+static struct net_device_stats *sit_get_stats(struct device *dev)
{
- return((struct enet_statistics*) dev->priv);
+ return((struct net_device_stats *) dev->priv);
}
-
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o sit.o sit.c"
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index ce7bb4681..212bcbc3e 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -4,45 +4,114 @@
#include <linux/mm.h>
#include <linux/sysctl.h>
+#include <linux/config.h>
#include <linux/in6.h>
#include <linux/ipv6.h>
#include <net/ndisc.h>
#include <net/ipv6.h>
#include <net/addrconf.h>
+struct ipv6_config ipv6_config =
+{
+ 0, /* forwarding */
+ IPV6_DEFAULT_HOPLIMIT, /* hop limit */
+ 1, /* accept RAs */
+ 1, /* accept redirects */
+
+ 3, /* nd_max_mcast_solicit */
+ 3, /* nd_max_ucast_solicit */
+ RETRANS_TIMER, /* nd_retrans_time */
+ RECHABLE_TIME, /* nd_base_reach_time */
+ (5 * HZ), /* nd_delay_probe_time */
+
+ 1, /* autoconfiguration */
+ 1, /* dad transmits */
+ MAX_RTR_SOLICITATIONS, /* router solicits */
+ RTR_SOLICITATION_INTERVAL, /* rtr solicit interval */
+ MAX_RTR_SOLICITATION_DELAY, /* rtr solicit delay */
+
+ 60*HZ, /* rt cache timeout */
+ 30*HZ, /* rt gc period */
+};
-int ipv6_hop_limit = IPV6_DEFAULT_HOPLIMIT;
+#ifdef CONFIG_SYSCTL
int ipv6_sysctl_forwarding(ctl_table *ctl, int write, struct file * filp,
void *buffer, size_t *lenp)
{
- int val = ipv6_forwarding;
+ int val = ipv6_config.forwarding;
int retv;
retv = proc_dointvec(ctl, write, filp, buffer, lenp);
- if (write)
- {
- if (ipv6_forwarding && val == 0) {
+ if (write) {
+ if (ipv6_config.forwarding && val == 0) {
printk(KERN_DEBUG "sysctl: IPv6 forwarding enabled\n");
ndisc_forwarding_on();
addrconf_forwarding_on();
}
- if (ipv6_forwarding == 0 && val) {
+ if (ipv6_config.forwarding == 0 && val)
ndisc_forwarding_off();
- }
}
return retv;
}
ctl_table ipv6_table[] = {
- {NET_IPV6_FORWARDING, "ipv6_forwarding",
- &ipv6_forwarding, sizeof(int), 0644, NULL,
+ {NET_IPV6_FORWARDING, "forwarding",
+ &ipv6_config.forwarding, sizeof(int), 0644, NULL,
&ipv6_sysctl_forwarding},
- {NET_IPV6_HOPLIMIT, "ipv6_hop_limit",
- &ipv6_hop_limit, sizeof(int), 0644, NULL,
+ {NET_IPV6_HOPLIMIT, "hop_limit",
+ &ipv6_config.hop_limit, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ACCEPT_RA, "accept_ra",
+ &ipv6_config.accept_ra, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ACCEPT_REDIRECTS, "accept_redirects",
+ &ipv6_config.accept_redirects, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ND_MAX_MCAST_SOLICIT, "nd_max_mcast_solicit",
+ &ipv6_config.nd_max_mcast_solicit, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ND_MAX_UCAST_SOLICIT, "nd_max_ucast_solicit",
+ &ipv6_config.nd_max_ucast_solicit, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ND_RETRANS_TIME, "nd_retrans_time",
+ &ipv6_config.nd_retrans_time, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ND_REACHABLE_TIME, "nd_base_reachble_time",
+ &ipv6_config.nd_base_reachable_time, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ND_DELAY_PROBE_TIME, "nd_delay_first_probe_time",
+ &ipv6_config.nd_delay_probe_time, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_AUTOCONF, "autoconf",
+ &ipv6_config.autoconf, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_DAD_TRANSMITS, "dad_transmits",
+ &ipv6_config.dad_transmits, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_RTR_SOLICITS, "router_solicitations",
+ &ipv6_config.rtr_solicits, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_RTR_SOLICIT_INTERVAL, "router_solicitation_interval",
+ &ipv6_config.rtr_solicit_interval, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_RTR_SOLICIT_DELAY, "router_solicitation_delay",
+ &ipv6_config.rtr_solicit_delay, sizeof(int), 0644, NULL,
&proc_dointvec},
{0}
@@ -73,6 +142,9 @@ void ipv6_sysctl_unregister(void)
{
unregister_sysctl_table(ipv6_sysctl_header);
}
+#endif /* MODULE */
+
+#endif /* CONFIG_SYSCTL */
+
-#endif
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index bb03b34dd..5151013a7 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: tcp_ipv6.c,v 1.15 1996/10/29 22:45:53 roque Exp $
+ * $Id: tcp_ipv6.c,v 1.27 1997/04/22 02:53:20 davem Exp $
*
* Based on:
* linux/net/ipv4/tcp.c
@@ -37,24 +37,216 @@
#include <net/ipv6.h>
#include <net/transp_v6.h>
#include <net/addrconf.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <asm/uaccess.h>
-static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr,
- struct tcphdr *th, struct proto *prot,
- struct ipv6_options *opt,
- struct device *dev, int pri, int hop_limit);
+extern int sysctl_tcp_sack;
+extern int sysctl_tcp_timestamps;
+extern int sysctl_tcp_window_scaling;
-static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
- struct sk_buff *skb);
+static void tcp_v6_send_reset(struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ struct tcphdr *th, struct proto *prot,
+ struct ipv6_options *opt,
+ struct device *dev, int pri, int hop_limit);
-static int tcp_v6_backlog_rcv(struct sock *sk, struct sk_buff *skb);
-static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb);
+static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
+ struct sk_buff *skb);
+
+static int tcp_v6_backlog_rcv(struct sock *sk, struct sk_buff *skb);
+static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb);
+static void tcp_v6_xmit(struct sk_buff *skb);
static struct tcp_func ipv6_mapped;
static struct tcp_func ipv6_specific;
+/* I have no idea if this is a good hash for v6 or not. -DaveM */
+static __inline__ int tcp_v6_hashfn(struct in6_addr *laddr, u16 lport,
+ struct in6_addr *faddr, u16 fport)
+{
+ int hashent = (lport ^ fport);
+
+ hashent ^= (laddr->s6_addr32[0] ^ laddr->s6_addr32[1]);
+ hashent ^= (faddr->s6_addr32[0] ^ faddr->s6_addr32[1]);
+ hashent ^= (faddr->s6_addr32[2] ^ faddr->s6_addr32[3]);
+ return (hashent & ((TCP_HTABLE_SIZE/2) - 1));
+}
+
+static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
+{
+ struct in6_addr *laddr = &sk->net_pinfo.af_inet6.rcv_saddr;
+ struct in6_addr *faddr = &sk->net_pinfo.af_inet6.daddr;
+ __u16 lport = sk->num;
+ __u16 fport = sk->dummy_th.dest;
+ return tcp_v6_hashfn(laddr, lport, faddr, fport);
+}
+
+/* Grrr, addr_type already calculated by caller, but I don't want
+ * to add some silly "cookie" argument to this method just for that.
+ */
+static int tcp_v6_verify_bind(struct sock *sk, unsigned short snum)
+{
+ struct sock *sk2;
+ int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+ int retval = 0, sk_reuse = sk->reuse;
+
+ SOCKHASH_LOCK();
+ sk2 = tcp_bound_hash[tcp_sk_bhashfn(sk)];
+ for(; sk2 != NULL; sk2 = sk2->bind_next) {
+ if((sk2->num == snum) && (sk2 != sk)) {
+ unsigned char state = sk2->state;
+ int sk2_reuse = sk2->reuse;
+ if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
+ if((!sk2_reuse) ||
+ (!sk_reuse) ||
+ (state == TCP_LISTEN)) {
+ retval = 1;
+ break;
+ }
+ } else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+ &sk2->net_pinfo.af_inet6.rcv_saddr)) {
+ if((!sk_reuse) ||
+ (!sk2_reuse) ||
+ (state == TCP_LISTEN)) {
+ retval = 1;
+ break;
+ }
+ }
+ }
+ }
+ SOCKHASH_UNLOCK();
+
+ return retval;
+}
+
+static void tcp_v6_hash(struct sock *sk)
+{
+ unsigned char state;
+
+ SOCKHASH_LOCK();
+ state = sk->state;
+ if(state != TCP_CLOSE) {
+ struct sock **skp;
+
+ if(state == TCP_LISTEN)
+ skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+ else
+ skp = &tcp_established_hash[tcp_v6_sk_hashfn(sk)];
+ if((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ tcp_sk_bindify(sk);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static void tcp_v6_unhash(struct sock *sk)
+{
+ SOCKHASH_LOCK();
+ if(sk->pprev) {
+ if(sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ tcp_sk_unbindify(sk);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static void tcp_v6_rehash(struct sock *sk)
+{
+ unsigned char state;
+
+ SOCKHASH_LOCK();
+ state = sk->state;
+ if(sk->pprev) {
+ if(sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ tcp_sk_unbindify(sk);
+ }
+ if(state != TCP_CLOSE) {
+ struct sock **skp;
+
+ if(state == TCP_LISTEN) {
+ skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+ } else {
+ int hash = tcp_v6_sk_hashfn(sk);
+ if(state == TCP_TIME_WAIT)
+ hash += (TCP_HTABLE_SIZE/2);
+ skp = &tcp_established_hash[hash];
+ }
+ if((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ tcp_sk_bindify(sk);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum)
+{
+ struct sock *sk;
+ struct sock *result = NULL;
+
+ sk = tcp_listening_hash[tcp_lhashfn(hnum)];
+ for(; sk; sk = sk->next) {
+ if((sk->num == hnum) && (sk->family == AF_INET6)) {
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ if(!ipv6_addr_any(&np->rcv_saddr)) {
+ if(!ipv6_addr_cmp(&np->rcv_saddr, daddr))
+ return sk; /* Best possible match. */
+ } else if(!result)
+ result = sk;
+ }
+ }
+ return result;
+}
+
+/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
+ * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
+ */
+static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
+ struct in6_addr *saddr, u16 sport,
+ struct in6_addr *daddr, u16 dport)
+{
+ unsigned short hnum = ntohs(dport);
+ struct sock *sk;
+ int hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
+
+ /* Optimize here for direct hit, only listening connections can
+ * have wildcards anyways. It is assumed that this code only
+ * gets called from within NET_BH.
+ */
+ for(sk = tcp_established_hash[hash]; sk; sk = sk->next)
+ /* For IPV6 do the cheaper port and family tests first. */
+ if(sk->num == hnum && /* local port */
+ sk->family == AF_INET6 && /* address family */
+ sk->dummy_th.dest == sport && /* remote port */
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) &&
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
+ goto hit; /* You sunk my battleship! */
+
+ /* Must check for a TIME_WAIT'er before going to listener hash. */
+ for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next)
+ if(sk->num == hnum && /* local port */
+ sk->family == AF_INET6 && /* address family */
+ sk->dummy_th.dest == sport && /* remote port */
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) &&
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
+ goto hit;
+
+ sk = tcp_v6_lookup_listener(daddr, hnum);
+hit:
+ return sk;
+}
+
+#define tcp_v6_lookup(sa, sp, da, dp) __tcp_v6_lookup((0),(sa),(sp),(da),(dp))
+
static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len,
struct in6_addr *saddr,
struct in6_addr *daddr,
@@ -68,15 +260,12 @@ static __u32 tcp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
__u32 si;
__u32 di;
- if (skb->protocol == __constant_htons(ETH_P_IPV6))
- {
- si = skb->ipv6_hdr->saddr.s6_addr32[3];
- di = skb->ipv6_hdr->daddr.s6_addr32[3];
- }
- else
- {
- si = skb->saddr;
- di = skb->daddr;
+ if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
+ si = skb->nh.ipv6h->saddr.s6_addr32[3];
+ di = skb->nh.ipv6h->daddr.s6_addr32[3];
+ } else {
+ si = skb->nh.iph->saddr;
+ di = skb->nh.iph->daddr;
}
return secure_tcp_sequence_number(di, si,
@@ -90,14 +279,15 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
- struct dest_entry *dc;
struct inet6_ifaddr *ifa;
+ struct in6_addr *saddr = NULL;
+ struct flowi fl;
+ struct dst_entry *dst;
struct tcphdr *th;
- __u8 *ptr;
struct sk_buff *buff;
struct sk_buff *skb1;
- int addr_type;
int tmp;
+ int addr_type;
if (sk->state != TCP_CLOSE)
return(-EISCONN);
@@ -125,9 +315,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
addr_type = ipv6_addr_type(&usin->sin6_addr);
if(addr_type & IPV6_ADDR_MULTICAST)
- {
return -ENETUNREACH;
- }
/*
* connect to self not allowed
@@ -135,9 +323,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
if (ipv6_addr_cmp(&usin->sin6_addr, &np->saddr) == 0 &&
usin->sin6_port == sk->dummy_th.source)
- {
return (-EINVAL);
- }
memcpy(&np->daddr, &usin->sin6_addr, sizeof(struct in6_addr));
@@ -145,24 +331,22 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
* TCP over IPv4
*/
- if (addr_type == IPV6_ADDR_MAPPED)
- {
+ if (addr_type == IPV6_ADDR_MAPPED) {
struct sockaddr_in sin;
int err;
- printk(KERN_DEBUG "connect: ipv4 mapped\n");
+ SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
sin.sin_family = AF_INET;
sin.sin_port = usin->sin6_port;
sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
sk->tp_pinfo.af_tcp.af_specific = &ipv6_mapped;
- sk->backlog_rcv = tcp_v4_backlog_rcv;
+ sk->backlog_rcv = tcp_v4_do_rcv;
err = tcp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
- if (err)
- {
+ if (err) {
sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
sk->backlog_rcv = tcp_v6_backlog_rcv;
}
@@ -170,31 +354,49 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
return err;
}
- dc = ipv6_dst_route(&np->daddr, NULL, (sk->localroute ? RTI_GATEWAY : 0));
+ if (!ipv6_addr_any(&np->rcv_saddr))
+ saddr = &np->rcv_saddr;
+
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.dev = NULL;
+ fl.uli_u.ports.dport = usin->sin6_port;
+ fl.uli_u.ports.sport = sk->dummy_th.source;
+
+ dst = ip6_route_output(sk, &fl);
- if (dc == NULL)
- {
- return -ENETUNREACH;
+ if (dst->error) {
+ dst_release(dst);
+ return dst->error;
}
- np->dest = dc;
- np->dc_sernum = (dc->rt.fib_node ? dc->rt.fib_node->fn_sernum : 0);
+ ip6_dst_store(sk, dst);
- ifa = ipv6_get_saddr((struct rt6_info *)dc, &np->daddr);
+ np->oif = dst->dev;
- if (ifa == NULL)
- {
- return -ENETUNREACH;
+ if (saddr == NULL) {
+ ifa = ipv6_get_saddr(dst, &np->daddr);
+
+ if (ifa == NULL)
+ return -ENETUNREACH;
+
+ saddr = &ifa->addr;
+
+ /* set the source address */
+ ipv6_addr_copy(&np->rcv_saddr, saddr);
+ ipv6_addr_copy(&np->saddr, saddr);
}
-
+ /* FIXME: Need to do tcp_v6_unique_address() here! -DaveM */
+
/*
* Init variables
*/
lock_sock(sk);
- sk->dummy_th.dest = usin->sin6_port;
+ sk->dummy_th.dest = usin->sin6_port;
sk->write_seq = secure_tcp_sequence_number(np->saddr.s6_addr32[3],
np->daddr.s6_addr32[3],
sk->dummy_th.source,
@@ -214,20 +416,11 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
buff = sock_wmalloc(sk, MAX_SYN_SIZE, 0, GFP_KERNEL);
if (buff == NULL)
- {
return(-ENOMEM);
- }
+
lock_sock(sk);
- buff->sk = sk;
- buff->free = 0;
- buff->localroute = sk->localroute;
-
- tmp = tcp_v6_build_header(sk, buff);
- /* set the source address */
-
- memcpy(&np->saddr, &ifa->addr, sizeof(struct in6_addr));
- memcpy(&np->rcv_saddr, &ifa->addr, sizeof(struct in6_addr));
+ tcp_v6_build_header(sk, buff);
/* build the tcp header */
th = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
@@ -241,60 +434,57 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
th->ack = 0;
th->window = 2;
th->syn = 1;
- th->doff = 6;
-
- sk->window_clamp=0;
- if ((dc->dc_flags & DCF_PMTU))
- sk->mtu = dc->dc_pmtu;
- else
- sk->mtu = dc->rt.rt_dev->mtu;
+ tp->window_clamp = 0;
+ sk->mtu = dst->pmtu;
sk->mss = sk->mtu - sizeof(struct ipv6hdr) - sizeof(struct tcphdr);
/*
* Put in the TCP options to say MTU.
*/
- ptr = skb_put(buff,4);
- ptr[0] = 2;
- ptr[1] = 4;
- ptr[2] = (sk->mss) >> 8;
- ptr[3] = (sk->mss) & 0xff;
- buff->csum = csum_partial(ptr, 4, 0);
+ tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_sack,
+ sysctl_tcp_timestamps,
+ sysctl_tcp_window_scaling?tp->rcv_wscale:0);
+ th->doff = sizeof(*th)/4 + (tmp>>2);
+ buff->csum = 0;
+ tcp_v6_send_check(sk, th, sizeof(struct tcphdr) + tmp, buff);
- tcp_v6_send_check(sk, th, sizeof(struct tcphdr) + 4, buff);
-
tcp_set_state(sk, TCP_SYN_SENT);
-
+
+ /* Socket identity change complete, no longer
+ * in TCP_CLOSE, so rehash.
+ */
+ sk->prot->rehash(sk);
+
/* FIXME: should use dcache->rtt if availiable */
tp->rto = TCP_TIMEOUT_INIT;
tcp_init_xmit_timers(sk);
- sk->retransmits = 0;
+ tp->retransmits = 0;
skb_queue_tail(&sk->write_queue, buff);
- sk->packets_out++;
+ tp->packets_out++;
buff->when = jiffies;
skb1 = skb_clone(buff, GFP_KERNEL);
- sk->wmem_alloc += skb1->truesize;
+ skb_set_owner_w(skb1, sk);
- tmp = ipv6_xmit(sk, skb1, &np->saddr, &np->daddr, NULL, IPPROTO_TCP);
+ tcp_v6_xmit(skb1);
/* Timer for repeating the SYN until an answer */
tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
tcp_statistics.TcpActiveOpens++;
tcp_statistics.TcpOutSegs++;
-
+
release_sock(sk);
-
- return(tmp);
+
+ return(0);
}
-static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg,
- int len, int nonblock, int flags)
+static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
{
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
int retval = -EINVAL;
@@ -303,7 +493,7 @@ static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg,
* Do sanity checking for sendmsg/sendto/send
*/
- if (flags & ~(MSG_OOB|MSG_DONTROUTE))
+ if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT))
goto out;
if (msg->msg_name) {
struct sockaddr_in6 *addr=(struct sockaddr_in6 *)msg->msg_name;
@@ -326,7 +516,7 @@ static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg,
lock_sock(sk);
retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov,
- len, nonblock, flags);
+ msg->msg_flags);
release_sock(sk);
@@ -344,123 +534,117 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info,
int err;
int opening;
- sk = inet6_get_sock(&tcpv6_prot, daddr, saddr, th->source, th->dest);
+ sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source);
if (sk == NULL)
- {
return;
- }
np = &sk->net_pinfo.af_inet6;
- if (type == ICMPV6_PKT_TOOBIG)
- {
+ if (type == ICMPV6_PKT_TOOBIG) {
/* icmp should have updated the destination cache entry */
- np->dest = ipv6_dst_check(np->dest, &np->daddr, np->dc_sernum,
- 0);
+ dst_check(&np->dst, np->dst_cookie);
- np->dc_sernum = (np->dest->rt.fib_node ?
- np->dest->rt.fib_node->fn_sernum : 0);
+ if (np->dst == NULL) {
+ struct flowi fl;
+ struct dst_entry *dst;
+
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = &np->saddr;
+ fl.dev = np->oif;
+ fl.uli_u.ports.dport = sk->dummy_th.dest;
+ fl.uli_u.ports.sport = sk->dummy_th.source;
- if (np->dest->dc_flags & DCF_PMTU)
- sk->mtu = np->dest->dc_pmtu;
+ dst = ip6_route_output(sk, &fl);
- sk->mtu = (sk->mtu - sizeof(struct ipv6hdr) -
- sizeof(struct tcphdr));
+ ip6_dst_store(sk, dst);
+ }
+
+ if (np->dst->error)
+ sk->err_soft = np->dst->error;
+ else
+ sk->mtu = np->dst->pmtu;
return;
}
opening = (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV);
- if (icmpv6_err_convert(type, code, &err) || opening)
- {
+ if (icmpv6_err_convert(type, code, &err) || opening) {
sk->err = err;
- if (opening)
- {
+
+ if (opening) {
tcp_statistics.TcpAttemptFails++;
tcp_set_state(sk,TCP_CLOSE);
sk->error_report(sk);
}
- }
- else
+ } else {
sk->err_soft = err;
+ }
}
static void tcp_v6_send_synack(struct sock *sk, struct open_request *req)
{
- struct tcp_v6_open_req *af_req = (struct tcp_v6_open_req *) req;
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
struct sk_buff * skb;
struct tcphdr *th;
- unsigned char *ptr;
- struct dest_entry *dc;
- int mss;
+ struct dst_entry *dst;
+ struct flowi fl;
+ int tmp;
skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
-
if (skb == NULL)
- {
return;
- }
- skb_reserve(skb, (MAX_HEADER + 15) & ~15);
- skb->ipv6_hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr;
+ fl.nl_u.ip6_u.saddr = &req->af.v6_req.loc_addr;
+ fl.dev = req->af.v6_req.dev;
+ fl.uli_u.ports.dport = req->rmt_port;
+ fl.uli_u.ports.sport = sk->dummy_th.source;
- dc = ipv6_dst_route(&af_req->rmt_addr, af_req->dev, 0);
+ dst = ip6_route_output(sk, &fl);
+ if (dst->error) {
+ kfree_skb(skb, FREE_WRITE);
+ dst_release(dst);
+ return;
+ }
- skb->dev = af_req->dev;
-
- if (dc)
- {
- if (dc->dc_flags & DCF_PMTU)
- mss = dc->dc_pmtu;
- else
- mss = dc->dc_nexthop->dev->mtu;
- mss -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
+ skb->dev = dst->dev;
+ skb_reserve(skb, (skb->dev->hard_header_len + 15) & ~15);
+ skb->nh.ipv6h = (struct ipv6hdr *) skb_put(skb,sizeof(struct ipv6hdr));
- ipv6_dst_unlock(dc);
- }
- else
- mss = 516;
+ skb->h.th = th = (struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
- th =(struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
- skb->h.th = th;
+ /* Yuck, make this header setup more efficient... -DaveM */
memset(th, 0, sizeof(struct tcphdr));
-
th->syn = 1;
th->ack = 1;
-
th->source = sk->dummy_th.source;
th->dest = req->rmt_port;
-
skb->seq = req->snt_isn;
skb->end_seq = skb->seq + 1;
-
th->seq = ntohl(skb->seq);
th->ack_seq = htonl(req->rcv_isn + 1);
th->doff = sizeof(*th)/4 + 1;
th->window = ntohs(tp->rcv_wnd);
- ptr = skb_put(skb, TCPOLEN_MSS);
- ptr[0] = TCPOPT_MSS;
- ptr[1] = TCPOLEN_MSS;
- ptr[2] = (mss >> 8) & 0xff;
- ptr[3] = mss & 0xff;
- skb->csum = csum_partial(ptr, TCPOLEN_MSS, 0);
+ tmp = tcp_syn_build_options(skb, sk->mss, req->sack_ok, req->tstamp_ok,
+ (req->snd_wscale)?tp->rcv_wscale:0);
+ th->doff = sizeof(*th)/4 + (tmp>>2);
+ th->check = tcp_v6_check(th, sizeof(*th) + tmp,
+ &req->af.v6_req.loc_addr, &req->af.v6_req.rmt_addr,
+ csum_partial((char *)th, sizeof(*th)+tmp, skb->csum));
- th->check = tcp_v6_check(th, sizeof(*th) + TCPOLEN_MSS, &af_req->loc_addr,
- &af_req->rmt_addr,
- csum_partial((char *)th, sizeof(*th), skb->csum));
+ ip6_dst_store(sk, dst);
+ ip6_xmit(sk, skb, &fl, req->af.v6_req.opt);
+ dst_release(dst);
- ipv6_xmit(sk, skb, &af_req->loc_addr, &af_req->rmt_addr, af_req->opt,
- IPPROTO_TCP);
-
tcp_statistics.TcpOutSegs++;
-
}
static void tcp_v6_or_free(struct open_request *req)
@@ -475,71 +659,55 @@ static struct or_calltable or_ipv6 = {
static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr,
__u32 isn)
{
- struct tcp_v6_open_req *af_req;
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
struct open_request *req;
+ __u16 req_mss;
/* If the socket is dead, don't accept the connection. */
- if (sk->dead)
- {
- if(sk->debug)
- {
- printk("Reset on %p: Connect on dead socket.\n",sk);
- }
+ if (sk->dead) {
+ SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n", sk);
tcp_statistics.TcpAttemptFails++;
- return -ENOTCONN;
+ return -ENOTCONN;
}
if (skb->protocol == __constant_htons(ETH_P_IP))
- {
return tcp_v4_conn_request(sk, skb, ptr, isn);
- }
/*
* There are no SYN attacks on IPv6, yet...
*/
- if (sk->ack_backlog >= sk->max_ack_backlog)
- {
+ if (sk->ack_backlog >= sk->max_ack_backlog) {
printk(KERN_DEBUG "droping syn ack:%d max:%d\n",
sk->ack_backlog, sk->max_ack_backlog);
tcp_statistics.TcpAttemptFails++;
goto exit;
}
- af_req = kmalloc(sizeof(struct tcp_v6_open_req), GFP_ATOMIC);
-
- if (af_req == NULL)
- {
+ req = tcp_openreq_alloc();
+ if (req == NULL) {
tcp_statistics.TcpAttemptFails++;
goto exit;
}
sk->ack_backlog++;
- req = (struct open_request *) af_req;
-
- memset(af_req, 0, sizeof(struct tcp_v6_open_req));
req->rcv_isn = skb->seq;
req->snt_isn = isn;
- /* mss */
- req->mss = tcp_parse_options(skb->h.th);
-
- if (!req->mss)
- {
- req->mss = 536;
- }
-
+ tcp_parse_options(skb->h.th,tp);
+ req_mss = tp->in_mss;
+ if (!req_mss)
+ req_mss = 536;
+ req->mss = req_mss;
req->rmt_port = skb->h.th->source;
-
- ipv6_addr_copy(&af_req->rmt_addr, &skb->ipv6_hdr->saddr);
- ipv6_addr_copy(&af_req->loc_addr, &skb->ipv6_hdr->daddr);
-
- /* FIXME: options */
-
- /* keep incoming device so that link locals have meaning */
- af_req->dev = skb->dev;
+ ipv6_addr_copy(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr);
+ ipv6_addr_copy(&req->af.v6_req.loc_addr, &skb->nh.ipv6h->daddr);
+ req->af.v6_req.opt = NULL; /* FIXME: options */
+ req->af.v6_req.dev = skb->dev; /* So that link locals have meaning */
req->class = &or_ipv6;
+ req->retrans = 0;
+ req->sk = NULL;
tcp_v6_send_synack(sk, req);
@@ -549,7 +717,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr,
sk->data_ready(sk, 0);
- exit:
+exit:
kfree_skb(skb, FREE_READ);
return 0;
}
@@ -561,31 +729,29 @@ static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
th->check = 0;
th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,
- csum_partial((char *)th, sizeof(*th),
+ csum_partial((char *)th, th->doff<<2,
skb->csum));
}
static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
struct open_request *req)
{
- struct tcp_v6_open_req *af_req = (struct tcp_v6_open_req *) req;
struct ipv6_pinfo *np;
- struct dest_entry *dc;
+ struct dst_entry *dst;
+ struct flowi fl;
struct tcp_opt *newtp;
struct sock *newsk;
-
- if (skb->protocol == __constant_htons(ETH_P_IP))
- {
- /*
- * v6 mapped
+ if (skb->protocol == __constant_htons(ETH_P_IP)) {
+ /*
+ * v6 mapped
*/
-
+
newsk = tcp_v4_syn_recv_sock(sk, skb, req);
if (newsk == NULL)
return NULL;
-
+
np = &newsk->net_pinfo.af_inet6;
ipv6_addr_set(&np->daddr, 0, 0, __constant_htonl(0x0000FFFF),
@@ -597,31 +763,31 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
ipv6_addr_copy(&np->rcv_saddr, &np->saddr);
newsk->tp_pinfo.af_tcp.af_specific = &ipv6_mapped;
- newsk->backlog_rcv = tcp_v4_backlog_rcv;
+ newsk->backlog_rcv = tcp_v4_do_rcv;
return newsk;
}
- newsk = (struct sock *) kmalloc(sizeof(struct sock), GFP_ATOMIC);
+ newsk = sk_alloc(GFP_ATOMIC);
if (newsk == NULL)
- {
return NULL;
- }
memcpy(newsk, sk, sizeof(*newsk));
+
+ /* Or else we die! -DaveM */
+ newsk->sklist_next = NULL;
+
newsk->opt = NULL;
- newsk->ip_route_cache = NULL;
+ newsk->dst_cache = NULL;
skb_queue_head_init(&newsk->write_queue);
skb_queue_head_init(&newsk->receive_queue);
skb_queue_head_init(&newsk->out_of_order_queue);
-
+ skb_queue_head_init(&newsk->error_queue);
+
/*
* Unused
*/
- newsk->send_head = NULL;
- newsk->send_tail = NULL;
-
newtp = &(newsk->tp_pinfo.af_tcp);
np = &newsk->net_pinfo.af_inet6;
@@ -634,17 +800,16 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newsk->prot->init(newsk);
- newsk->cong_count = 0;
- newsk->ssthresh = 0;
+ newtp->snd_cwnd_cnt = 0;
+#if 0 /* Don't mess up the initialization we did in the init routine! */
+ newtp->snd_ssthresh = 0;
+#endif
newtp->backoff = 0;
- newsk->blog = 0;
- newsk->intr = 0;
newsk->proc = 0;
newsk->done = 0;
- newsk->partial = NULL;
newsk->pair = NULL;
- newsk->wmem_alloc = 0;
- newsk->rmem_alloc = 0;
+ atomic_set(&newsk->wmem_alloc, 0);
+ atomic_set(&newsk->rmem_alloc, 0);
newsk->localroute = sk->localroute;
newsk->max_unacked = MAX_WINDOW - TCP_WINDOW_DIFF;
@@ -653,24 +818,23 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newsk->shutdown = 0;
newsk->ack_backlog = 0;
- newsk->fin_seq = req->rcv_isn;
+ newtp->fin_seq = req->rcv_isn;
newsk->syn_seq = req->rcv_isn;
newsk->state = TCP_SYN_RECV;
newsk->timeout = 0;
- newsk->ip_xmit_timeout = 0;
newsk->write_seq = req->snt_isn;
newtp->snd_wnd = ntohs(skb->h.th->window);
- newsk->max_window = newtp->snd_wnd;
+ newtp->max_window = newtp->snd_wnd;
newtp->snd_wl1 = req->rcv_isn;
newtp->snd_wl2 = newsk->write_seq;
newtp->snd_una = newsk->write_seq++;
newtp->snd_nxt = newsk->write_seq;
newsk->urg_data = 0;
- newsk->packets_out = 0;
- newsk->retransmits = 0;
+ newtp->packets_out = 0;
+ newtp->retransmits = 0;
newsk->linger=0;
newsk->destroy = 0;
init_timer(&newsk->timer);
@@ -681,40 +845,65 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newsk->dummy_th.source = sk->dummy_th.source;
newsk->dummy_th.dest = req->rmt_port;
-
+ newsk->sock_readers=0;
+
newtp->rcv_nxt = req->rcv_isn + 1;
newtp->rcv_wup = req->rcv_isn + 1;
newsk->copied_seq = req->rcv_isn + 1;
newsk->socket = NULL;
- ipv6_addr_copy(&np->daddr, &af_req->rmt_addr);
- ipv6_addr_copy(&np->saddr, &af_req->loc_addr);
- ipv6_addr_copy(&np->rcv_saddr, &af_req->loc_addr);
-
+ ipv6_addr_copy(&np->daddr, &req->af.v6_req.rmt_addr);
+ ipv6_addr_copy(&np->saddr, &req->af.v6_req.loc_addr);
+ ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr);
+ np->oif = req->af.v6_req.dev;
+
/*
- * options / mss
+ * options / mss / route cache
*/
-
- dc = ipv6_dst_route(&af_req->rmt_addr, af_req->dev, 0);
- np->dest = dc;
- if (np->dest && (np->dest->dc_flags & DCF_PMTU))
- newsk->mtu = np->dest->dc_pmtu;
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = &np->saddr;
+ fl.dev = np->oif;
+ fl.uli_u.ports.dport = newsk->dummy_th.dest;
+ fl.uli_u.ports.sport = newsk->dummy_th.source;
+
+ dst = ip6_route_output(newsk, &fl);
+
+ ip6_dst_store(newsk, dst);
+
+ newtp->sack_ok = req->sack_ok;
+ newtp->tstamp_ok = req->tstamp_ok;
+ newtp->snd_wscale = req->snd_wscale;
+ newtp->ts_recent = req->ts_recent;
+ if (newtp->tstamp_ok) {
+ newtp->tcp_header_len = sizeof(struct tcphdr) + 12; /* FIXME: define the contant. */
+ newsk->dummy_th.doff += 3;
+ } else {
+ newtp->tcp_header_len = sizeof(struct tcphdr);
+ }
+
+ if (dst->error)
+ newsk->mtu = req->af.v6_req.dev->mtu;
else
- newsk->mtu = af_req->dev->mtu;
+ newsk->mtu = dst->pmtu;
+
+ newsk->mss = min(req->mss+sizeof(struct tcphdr)-newtp->tcp_header_len,
+ (newsk->mtu - sizeof(struct ipv6hdr) - newtp->tcp_header_len));
+ /* XXX tp->window_clamp??? -DaveM */
- newsk->mss = min(req->mss, (newsk->mtu - sizeof(struct ipv6hdr) -
- sizeof(struct tcphdr)));
-
newsk->daddr = LOOPBACK4_IPV6;
newsk->saddr = LOOPBACK4_IPV6;
newsk->rcv_saddr= LOOPBACK4_IPV6;
-
- inet_put_sock(newsk->num, newsk);
+ newsk->prot->hash(newsk);
+ add_to_prot_sklist(newsk);
return newsk;
+}
+static void tcp_v6_reply_reset(struct sk_buff *skb)
+{
}
static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr,
@@ -724,6 +913,7 @@ static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr,
{
struct sk_buff *buff;
struct tcphdr *t1;
+ struct flowi fl;
if(th->rst)
return;
@@ -737,9 +927,7 @@ static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr,
if (buff == NULL)
return;
- buff->sk = NULL;
buff->dev = dev;
- buff->localroute = 0;
tcp_v6_build_header(NULL, buff);
@@ -755,12 +943,9 @@ static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr,
t1->doff = sizeof(*t1)/4;
t1->rst = 1;
- if(th->ack)
- {
+ if(th->ack) {
t1->seq = th->ack_seq;
- }
- else
- {
+ } else {
t1->ack = 1;
if(!th->syn)
t1->ack_seq = th->seq;
@@ -773,91 +958,69 @@ static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr,
t1->check = csum_ipv6_magic(saddr, daddr, sizeof(*t1), IPPROTO_TCP,
buff->csum);
-
- ipv6_xmit(NULL, buff, saddr, daddr, NULL, IPPROTO_TCP);
-
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = daddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.dev = dev;
+ fl.uli_u.ports.dport = th->dest;
+ fl.uli_u.ports.sport = th->source;
+
+ ip6_xmit(NULL, buff, &fl, NULL);
tcp_statistics.TcpOutSegs++;
}
struct sock *tcp_v6_check_req(struct sock *sk, struct sk_buff *skb)
{
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
- struct open_request *req;
-
+ struct open_request *req = tp->syn_wait_queue;
- /*
- * assumption: the socket is not in use.
+ /* assumption: the socket is not in use.
* as we checked the user count on tcp_rcv and we're
* running from a soft interrupt.
*/
-
- req = tp->syn_wait_queue;
-
-
if (!req)
- {
return sk;
- }
-
- do {
- struct tcp_v6_open_req *af_req;
-
- af_req = (struct tcp_v6_open_req *) req;
- if (!ipv6_addr_cmp(&af_req->rmt_addr, &skb->ipv6_hdr->saddr) &&
- !ipv6_addr_cmp(&af_req->loc_addr, &skb->ipv6_hdr->daddr) &&
- req->rmt_port == skb->h.th->source)
- {
+ while(req) {
+ if (!ipv6_addr_cmp(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr) &&
+ !ipv6_addr_cmp(&req->af.v6_req.loc_addr, &skb->nh.ipv6h->daddr) &&
+ req->rmt_port == skb->h.th->source) {
u32 flg;
-
- if (req->sk)
- {
+
+ if (req->sk) {
printk(KERN_DEBUG "BUG: syn_recv:"
"socket exists\n");
break;
}
- /* match */
-
- /*
- * Check for syn retransmission
- */
+ /* Check for syn retransmission */
flg = *(((u32 *)skb->h.th) + 3);
flg &= __constant_htonl(0x002f0000);
-
+
if ((flg == __constant_htonl(0x00020000)) &&
- (!after(skb->seq, req->rcv_isn)))
- {
- /*
- * retransmited syn
+ (!after(skb->seq, req->rcv_isn))) {
+ /* retransmited syn
* FIXME: must send an ack
*/
return NULL;
}
- atomic_sub(skb->truesize, &sk->rmem_alloc);
+ skb_orphan(skb);
sk = tp->af_specific->syn_recv_sock(sk, skb, req);
tcp_dec_slow_timer(TCP_SLT_SYNACK);
if (sk == NULL)
- {
return NULL;
- }
- atomic_add(skb->truesize, &sk->rmem_alloc);
+ skb_set_owner_r(skb, sk);
req->expires = 0UL;
req->sk = sk;
- skb->sk = sk;
break;
}
-
req = req->dl_next;
- } while (req != tp->syn_wait_queue);
-
-
+ }
return sk;
-
}
int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
@@ -879,41 +1042,37 @@ int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
sk = skb->sk;
- if (!redo)
- {
-
+ if (!redo) {
if (skb->pkt_type != PACKET_HOST)
goto discard_it;
/*
* Pull up the IP header.
*/
-
+
skb_pull(skb, skb->h.raw - skb->data);
/*
* Try to use the device checksum if provided.
*/
-
- switch (skb->ip_summed)
- {
- case CHECKSUM_NONE:
- skb->csum = csum_partial((char *)th, len, 0);
- case CHECKSUM_HW:
- if (tcp_v6_check(th,len,saddr,daddr,skb->csum))
- {
- printk(KERN_DEBUG "tcp csum failed\n");
- goto discard_it;
- }
- default:
- /* CHECKSUM_UNNECESSARY */
- }
- sk = inet6_get_sock(&tcpv6_prot, daddr, saddr,
- th->dest, th->source);
+ switch (skb->ip_summed) {
+ case CHECKSUM_NONE:
+ skb->csum = csum_partial((char *)th, len, 0);
+ case CHECKSUM_HW:
+ if (tcp_v6_check(th,len,saddr,daddr,skb->csum)) {
+ printk(KERN_DEBUG "tcp csum failed\n");
+ goto discard_it;
+ }
+ default:
+ /* CHECKSUM_UNNECESSARY */
+ };
+
+ tcp_statistics.TcpInSegs++;
+
+ sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest);
- if (!sk)
- {
+ if (!sk) {
printk(KERN_DEBUG "socket not found\n");
goto no_tcp_socket;
}
@@ -923,17 +1082,14 @@ int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4;
skb->ack_seq = ntohl(th->ack_seq);
- skb->acked = 0;
skb->used = 0;
- skb->free = 1;
- }
+ }
/*
- * We may need to add it to the backlog here.
+ * We may need to add it to the backlog here.
*/
- if (sk->users)
- {
+ if (sk->sock_readers) {
__skb_queue_tail(&sk->back_log, skb);
return(0);
}
@@ -942,45 +1098,38 @@ int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
* Signal NDISC that the connection is making
* "forward progress"
*/
- if (sk->state != TCP_LISTEN)
- {
+ if (sk->state != TCP_LISTEN) {
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
if (after(skb->seq, tp->rcv_nxt) ||
- after(skb->ack_seq, tp->snd_una))
- {
- if (np->dest)
- ndisc_validate(np->dest->dc_nexthop);
+ after(skb->ack_seq, tp->snd_una)) {
+ if (np->dst)
+ ndisc_validate(np->dst->neighbour);
}
}
- if (!sk->prot)
- {
+ if (!sk->prot) {
printk(KERN_DEBUG "tcp_rcv: sk->prot == NULL\n");
return(0);
}
- atomic_add(skb->truesize, &sk->rmem_alloc);
+ skb_set_owner_r(skb, sk);
- if (sk->state == TCP_ESTABLISHED)
- {
- tcp_rcv_established(sk, skb, th, len);
+ if (sk->state == TCP_ESTABLISHED) {
+ if (tcp_rcv_established(sk, skb, th, len))
+ goto no_tcp_socket;
return 0;
}
-
- if (sk->state == TCP_LISTEN)
- {
+
+ if (sk->state == TCP_LISTEN) {
/*
* find possible connection requests
*/
sk = tcp_v6_check_req(sk, skb);
if (sk == NULL)
- {
goto discard_it;
- }
-
}
if (tcp_rcv_state_process(sk, skb, th, opt, len) == 0)
@@ -989,12 +1138,12 @@ int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
no_tcp_socket:
/*
- * No such TCB. If th->rst is 0 send a reset
+ * No such TCB. If th->rst is 0 send a reset
* (checked in tcp_send_reset)
*/
- tcp_v6_send_reset(daddr, saddr, th, &tcpv6_prot, opt, dev,
- skb->ipv6_hdr->priority, 255);
+ tcp_v6_send_reset(daddr, saddr, th, &tcpv6_prot, opt, dev,
+ skb->nh.ipv6h->priority, 255);
discard_it:
@@ -1004,37 +1153,38 @@ discard_it:
kfree_skb(skb, FREE_READ);
return 0;
-
}
static int tcp_v6_rebuild_header(struct sock *sk, struct sk_buff *skb)
{
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-
- if (np->dest)
- {
- np->dest = ipv6_dst_check(np->dest, &np->daddr,
- np->dc_sernum, 0);
-
- }
- else
- {
- np->dest = ipv6_dst_route(&np->daddr, NULL, 0);
+
+ if (np->dst)
+ dst_check(&np->dst, np->dst_cookie);
+
+ if (np->dst == NULL) {
+ struct flowi fl;
+ struct dst_entry *dst;
+
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = &np->saddr;
+ fl.dev = np->oif;
+ fl.uli_u.ports.dport = sk->dummy_th.dest;
+ fl.uli_u.ports.sport = sk->dummy_th.source;
+
+ dst = ip6_route_output(sk, &fl);
+ ip6_dst_store(sk, dst);
}
- if (!np->dest)
- {
+ if (np->dst->error) {
/*
* lost route to destination
*/
- return -1;
+ return -EHOSTUNREACH;
}
-
- np->dc_sernum = (np->dest->rt.fib_node ?
- np->dest->rt.fib_node->fn_sernum : 0);
- ipv6_redo_mac_hdr(skb, np->dest->dc_nexthop,
- skb->tail - (u8*) skb->ipv6_hdr);
+ skb_pull(skb, skb->nh.raw - skb->data);
return 0;
}
@@ -1043,9 +1193,9 @@ static int tcp_v6_backlog_rcv(struct sock *sk, struct sk_buff *skb)
int res;
res = tcp_v6_rcv(skb, skb->dev,
- &skb->ipv6_hdr->saddr, &skb->ipv6_hdr->daddr,
- (struct ipv6_options *) skb->proto_priv,
- skb->len, 1,
+ &skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
+ (struct ipv6_options *) skb->cb,
+ skb->len, 1,
(struct inet6_protocol *) sk->pair);
return res;
}
@@ -1054,20 +1204,16 @@ static struct sock * tcp_v6_get_sock(struct sk_buff *skb, struct tcphdr *th)
{
struct in6_addr *saddr;
struct in6_addr *daddr;
- struct sock *sk;
- saddr = &skb->ipv6_hdr->saddr;
- daddr = &skb->ipv6_hdr->daddr;
-
- sk = inet6_get_sock(&tcpv6_prot, daddr, saddr, th->source, th->dest);
-
- return sk;
+ saddr = &skb->nh.ipv6h->saddr;
+ daddr = &skb->nh.ipv6h->daddr;
+ return tcp_v6_lookup(saddr, th->source, daddr, th->dest);
}
-
+
static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb)
{
skb_reserve(skb, (MAX_HEADER + 15) & ~15);
- skb->ipv6_hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
+ skb->nh.raw = skb_put(skb, sizeof(struct ipv6hdr));
/*
* FIXME: reserve space for option headers
@@ -1077,32 +1223,37 @@ static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb)
return 0;
}
-static void tcp_v6_xmit(struct sock *sk, struct device *dev, struct sk_buff *skb,
- int free)
+static void tcp_v6_xmit(struct sk_buff *skb)
{
+ struct sock *sk = skb->sk;
struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6;
+ struct flowi fl;
int err;
- err = ipv6_xmit(sk, skb, &np->saddr, &np->daddr, NULL, IPPROTO_TCP);
-
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = &np->saddr;
+ fl.dev = np->oif;
+ fl.uli_u.ports.sport = sk->dummy_th.source;
+ fl.uli_u.ports.dport = sk->dummy_th.dest;
+
+ err = ip6_xmit(sk, skb, &fl, np->opt);
+
/*
* FIXME: check error handling.
*/
sk->err_soft = err;
}
-
-
static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
{
struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) uaddr;
-
+
sin6->sin6_family = AF_INET6;
memcpy(&sin6->sin6_addr, &np->daddr, sizeof(struct in6_addr));
sin6->sin6_port = sk->dummy_th.dest;
-
}
static struct tcp_func ipv6_specific = {
@@ -1117,6 +1268,7 @@ static struct tcp_func ipv6_specific = {
ipv6_setsockopt,
ipv6_getsockopt,
v6_addr2sockaddr,
+ tcp_v6_reply_reset,
sizeof(struct sockaddr_in6)
};
@@ -1136,6 +1288,7 @@ static struct tcp_func ipv6_mapped = {
ipv6_setsockopt,
ipv6_getsockopt,
v6_addr2sockaddr,
+ tcp_v6_reply_reset,
sizeof(struct sockaddr_in6)
};
@@ -1156,8 +1309,8 @@ static int tcp_v6_init_sock(struct sock *sk)
tp->rcv_wnd = 8192;
/* start with only sending one packet at a time. */
- sk->cong_window = 1;
- sk->ssthresh = 0x7fffffff;
+ tp->snd_cwnd = 1;
+ tp->snd_ssthresh = 0x7fffffff;
sk->priority = 1;
sk->state = TCP_CLOSE;
@@ -1165,21 +1318,22 @@ static int tcp_v6_init_sock(struct sock *sk)
/* this is how many unacked bytes we will accept for this socket. */
sk->max_unacked = 2048; /* needs to be at most 2 full packets. */
sk->max_ack_backlog = SOMAXCONN;
-
+
sk->mtu = 576;
sk->mss = 516;
sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
-
/*
- * Speed up by setting some standard state for the dummy_th
- * if TCP uses it (maybe move to tcp_init later)
+ * Speed up by setting some standard state for the dummy_th.
*/
-
- sk->dummy_th.ack=1;
+ sk->dummy_th.ack=1;
sk->dummy_th.doff=sizeof(struct tcphdr)>>2;
+ /* Init SYN queue. */
+ tp->syn_wait_queue = NULL;
+ tp->syn_wait_last = &tp->syn_wait_queue;
+
sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
return 0;
@@ -1191,70 +1345,67 @@ static int tcp_v6_destroy_sock(struct sock *sk)
struct sk_buff *skb;
tcp_clear_xmit_timers(sk);
-
+
if (sk->keepopen)
- {
tcp_dec_slow_timer(TCP_SLT_KEEPALIVE);
- }
/*
- * Cleanup up the write buffer.
+ * Cleanup up the write buffer.
*/
-
- while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
- IS_SKB(skb);
- skb->free = 1;
+
+ while((skb = skb_dequeue(&sk->write_queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
- }
/*
* Cleans up our, hopefuly empty, out_of_order_queue
*/
- while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL) {
- IS_SKB(skb);
+ while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL)
kfree_skb(skb, FREE_READ);
- }
/*
* Release destination entry
*/
- if (np->dest)
- {
- ipv6_dst_unlock(np->dest);
- }
+ if (np->dst)
+ dst_release(np->dst);
return 0;
}
-
struct proto tcpv6_prot = {
- tcp_close,
- tcp_v6_connect,
- tcp_accept,
- NULL,
- tcp_write_wakeup,
- tcp_read_wakeup,
- tcp_select,
- tcp_ioctl,
- tcp_v6_init_sock,
- tcp_v6_destroy_sock,
- tcp_shutdown,
- tcp_setsockopt,
- tcp_getsockopt,
- tcp_v6_sendmsg,
- tcp_recvmsg,
- NULL, /* No special bind() */
- tcp_v6_backlog_rcv,
- 128,
- 0,
- "TCPv6",
- 0, 0,
- NULL
+ (struct sock *)&tcpv6_prot, /* sklist_next */
+ (struct sock *)&tcpv6_prot, /* sklist_prev */
+ tcp_close, /* close */
+ tcp_v6_connect, /* connect */
+ tcp_accept, /* accept */
+ NULL, /* retransmit */
+ tcp_write_wakeup, /* write_wakeup */
+ tcp_read_wakeup, /* read_wakeup */
+ tcp_poll, /* poll */
+ tcp_ioctl, /* ioctl */
+ tcp_v6_init_sock, /* init */
+ tcp_v6_destroy_sock, /* destroy */
+ tcp_shutdown, /* shutdown */
+ tcp_setsockopt, /* setsockopt */
+ tcp_getsockopt, /* getsockopt */
+ tcp_v6_sendmsg, /* sendmsg */
+ tcp_recvmsg, /* recvmsg */
+ NULL, /* bind */
+ tcp_v6_backlog_rcv, /* backlog_rcv */
+ tcp_v6_hash, /* hash */
+ tcp_v6_unhash, /* unhash */
+ tcp_v6_rehash, /* rehash */
+ tcp_good_socknum, /* good_socknum */
+ tcp_v6_verify_bind, /* verify_bind */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "TCPv6", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
};
-static struct inet6_protocol tcpv6_protocol =
+static struct inet6_protocol tcpv6_protocol =
{
tcp_v6_rcv, /* TCP handler */
tcp_v6_err, /* TCP error control */
@@ -1265,16 +1416,8 @@ static struct inet6_protocol tcpv6_protocol =
"TCPv6" /* name */
};
-
void tcpv6_init(void)
{
/* register inet6 protocol */
inet6_add_protocol(&tcpv6_protocol);
}
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o tcp_ipv6.o tcp_ipv6.c"
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 380122210..1f0fb8ce5 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -7,7 +7,7 @@
*
* Based on linux/ipv4/udp.c
*
- * $Id: udp.c,v 1.6 1996/10/16 18:34:16 roque Exp $
+ * $Id: udp.c,v 1.16 1997/04/11 22:22:57 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -34,7 +34,7 @@
#include <net/ndisc.h>
#include <net/protocol.h>
#include <net/transp_v6.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/ip.h>
#include <net/udp.h>
@@ -43,6 +43,139 @@
struct udp_mib udp_stats_in6;
+/* Grrr, addr_type already calculated by caller, but I don't want
+ * to add some silly "cookie" argument to this method just for that.
+ */
+static int udp_v6_verify_bind(struct sock *sk, unsigned short snum)
+{
+ struct sock *sk2;
+ int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+ int retval = 0, sk_reuse = sk->reuse;
+
+ SOCKHASH_LOCK();
+ for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
+ if((sk2->num == snum) && (sk2 != sk)) {
+ unsigned char state = sk2->state;
+ int sk2_reuse = sk2->reuse;
+ if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
+ if((!sk2_reuse) ||
+ (!sk_reuse) ||
+ (state == TCP_LISTEN)) {
+ retval = 1;
+ break;
+ }
+ } else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+ &sk2->net_pinfo.af_inet6.rcv_saddr)) {
+ if((!sk_reuse) ||
+ (!sk2_reuse) ||
+ (state == TCP_LISTEN)) {
+ retval = 1;
+ break;
+ }
+ }
+ }
+ }
+ SOCKHASH_UNLOCK();
+ return retval;
+}
+
+static void udp_v6_hash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+
+ num &= (UDP_HTABLE_SIZE - 1);
+ skp = &udp_hash[num];
+
+ SOCKHASH_LOCK();
+ sk->next = *skp;
+ *skp = sk;
+ sk->hashent = num;
+ SOCKHASH_UNLOCK();
+}
+
+static void udp_v6_unhash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+
+ num &= (UDP_HTABLE_SIZE - 1);
+ skp = &udp_hash[num];
+
+ SOCKHASH_LOCK();
+ while(*skp != NULL) {
+ if(*skp == sk) {
+ *skp = sk->next;
+ break;
+ }
+ skp = &((*skp)->next);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static void udp_v6_rehash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+ int oldnum = sk->hashent;
+
+ num &= (UDP_HTABLE_SIZE - 1);
+ skp = &udp_hash[oldnum];
+
+ SOCKHASH_LOCK();
+ while(*skp != NULL) {
+ if(*skp == sk) {
+ *skp = sk->next;
+ break;
+ }
+ skp = &((*skp)->next);
+ }
+ sk->next = udp_hash[num];
+ udp_hash[num] = sk;
+ sk->hashent = num;
+ SOCKHASH_UNLOCK();
+}
+
+static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
+ struct in6_addr *daddr, u16 dport)
+{
+ struct sock *sk, *result = NULL;
+ unsigned short hnum = ntohs(dport);
+ int badness = -1;
+
+ for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
+ if((sk->num == hnum) &&
+ (sk->family == AF_INET6) &&
+ !(sk->dead && (sk->state == TCP_CLOSE))) {
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ int score = 0;
+ if(sk->dummy_th.dest) {
+ if(sk->dummy_th.dest != sport)
+ continue;
+ score++;
+ }
+ if(!ipv6_addr_any(&np->rcv_saddr)) {
+ if(ipv6_addr_cmp(&np->rcv_saddr, daddr))
+ continue;
+ score++;
+ }
+ if(!ipv6_addr_any(&np->daddr)) {
+ if(ipv6_addr_cmp(&np->daddr, saddr))
+ continue;
+ score++;
+ }
+ if(score == 3) {
+ result = sk;
+ break;
+ } else if(score > badness) {
+ result = sk;
+ badness = score;
+ }
+ }
+ }
+ return result;
+}
+
/*
*
*/
@@ -51,9 +184,10 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
struct in6_addr *daddr;
- struct dest_entry *dest;
+ struct dst_entry *dst;
struct ipv6_pinfo *np;
struct inet6_ifaddr *ifa;
+ struct flowi fl;
int addr_type;
if (addr_len < sizeof(*usin))
@@ -65,8 +199,7 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
addr_type = ipv6_addr_type(&usin->sin6_addr);
np = &sk->net_pinfo.af_inet6;
- if (addr_type == IPV6_ADDR_ANY)
- {
+ if (addr_type == IPV6_ADDR_ANY) {
/*
* connect to self
*/
@@ -75,8 +208,7 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
daddr = &usin->sin6_addr;
- if (addr_type == IPV6_ADDR_MAPPED)
- {
+ if (addr_type == IPV6_ADDR_MAPPED) {
struct sockaddr_in sin;
int err;
@@ -86,22 +218,18 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
err = udp_connect(sk, (struct sockaddr*) &sin, sizeof(sin));
if (err < 0)
- {
return err;
- }
ipv6_addr_copy(&np->daddr, daddr);
- if(ipv6_addr_any(&np->saddr))
- {
+ if(ipv6_addr_any(&np->saddr)) {
ipv6_addr_set(&np->saddr, 0, 0,
__constant_htonl(0x0000ffff),
sk->saddr);
}
- if(ipv6_addr_any(&np->rcv_saddr))
- {
+ if(ipv6_addr_any(&np->rcv_saddr)) {
ipv6_addr_set(&np->rcv_saddr, 0, 0,
__constant_htonl(0x0000ffff),
sk->rcv_saddr);
@@ -111,35 +239,41 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
ipv6_addr_copy(&np->daddr, daddr);
+ sk->dummy_th.dest = usin->sin6_port;
+
/*
* Check for a route to destination an obtain the
* destination cache for it.
*/
- dest = ipv6_dst_route(daddr, NULL, sk->localroute ? RTI_GATEWAY : 0);
-
- np->dest = dest;
+ fl.proto = IPPROTO_UDP;
+ fl.nl_u.ip6_u.daddr = daddr;
+ fl.nl_u.ip6_u.saddr = NULL;
+ fl.dev = NULL;
+ fl.uli_u.ports.dport = sk->dummy_th.dest;
+ fl.uli_u.ports.sport = sk->dummy_th.source;
+
+ dst = ip6_route_output(sk, &fl);
+
+ if (dst->error) {
+ dst_release(dst);
+ return dst->error;
+ }
- if (dest == NULL)
- return -ENETUNREACH;
+ ip6_dst_store(sk, dst);
/* get the source adddress used in the apropriate device */
- ifa = ipv6_get_saddr((struct rt6_info *) dest, daddr);
+ ifa = ipv6_get_saddr(dst, daddr);
if(ipv6_addr_any(&np->saddr))
- {
ipv6_addr_copy(&np->saddr, &ifa->addr);
- }
- if(ipv6_addr_any(&np->rcv_saddr))
- {
+ if(ipv6_addr_any(&np->rcv_saddr)) {
ipv6_addr_copy(&np->rcv_saddr, &ifa->addr);
sk->rcv_saddr = 0xffffffff;
}
- sk->dummy_th.dest = usin->sin6_port;
-
sk->state = TCP_ESTABLISHED;
return(0);
@@ -152,11 +286,11 @@ static void udpv6_close(struct sock *sk, unsigned long timeout)
lock_sock(sk);
sk->state = TCP_CLOSE;
- if (np->dest)
- {
- ipv6_dst_unlock(np->dest);
- }
+ if (np->dst)
+ dst_release(np->dst);
+ ipv6_sock_mc_close(sk);
+ udp_v6_unhash(sk);
release_sock(sk);
destroy_sock(sk);
}
@@ -173,7 +307,6 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
int truesize;
struct sk_buff *skb;
int err;
-
/*
* Check any passed addresses
@@ -191,11 +324,11 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
if(skb==NULL)
return err;
- truesize = skb->tail - skb->h.raw - sizeof(struct udphdr);
+ truesize=ntohs(((struct udphdr *)skb->h.raw)->len) - sizeof(struct udphdr);
copied=truesize;
- if(copied>len)
- {
+
+ if(copied>len) {
copied=len;
msg->msg_flags|=MSG_TRUNC;
}
@@ -212,8 +345,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
sk->stamp=skb->stamp;
/* Copy the address. */
- if (msg->msg_name)
- {
+ if (msg->msg_name) {
struct sockaddr_in6 *sin6;
sin6 = (struct sockaddr_in6 *) msg->msg_name;
@@ -221,27 +353,15 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
sin6->sin6_family = AF_INET6;
sin6->sin6_port = skb->h.uh->source;
- if (skb->protocol == __constant_htons(ETH_P_IP))
- {
+ if (skb->protocol == __constant_htons(ETH_P_IP)) {
ipv6_addr_set(&sin6->sin6_addr, 0, 0,
- __constant_htonl(0xffff), skb->daddr);
- }
- else
- {
- memcpy(&sin6->sin6_addr, &skb->ipv6_hdr->saddr,
+ __constant_htonl(0xffff), skb->nh.iph->saddr);
+ } else {
+ memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
sizeof(struct in6_addr));
- if (msg->msg_control)
- {
- int err;
-
- err = datagram_recv_ctl(sk, msg, skb);
-
- if (err < 0)
- {
- copied = err;
- }
- }
+ if (msg->msg_controllen)
+ datagram_recv_ctl(sk, msg, skb);
}
}
@@ -259,24 +379,22 @@ void udpv6_err(int type, int code, unsigned char *buff, __u32 info,
uh = (struct udphdr *) buff;
- sk = inet6_get_sock(&udpv6_prot, daddr, saddr, uh->source, uh->dest);
+ sk = udp_v6_lookup(daddr, uh->dest, saddr, uh->source);
- if (sk == NULL)
- {
- printk(KERN_DEBUG "icmp for unkown sock\n");
+ if (sk == NULL) {
+ printk(KERN_DEBUG "icmp for unknown sock\n");
return;
}
- if (icmpv6_err_convert(type, code, &err))
- {
+ if (icmpv6_err_convert(type, code, &err)) {
if(sk->bsdism && sk->state!=TCP_ESTABLISHED)
return;
sk->err = err;
sk->error_report(sk);
- }
- else
+ } else {
sk->err_soft = err;
+ }
}
static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
@@ -294,6 +412,74 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
return 0;
}
+static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+ struct ipv6_mc_socklist *mc;
+
+ for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+ if (ipv6_addr_cmp(&mc->addr, addr) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct sock *udp_v6_mcast_next(struct sock *sk,
+ u16 loc_port, struct in6_addr *loc_addr,
+ u16 rmt_port, struct in6_addr *rmt_addr)
+{
+ struct sock *s = sk;
+ unsigned short num = ntohs(loc_port);
+ for(; s; s = s->next) {
+ if((s->num == num) &&
+ !(s->dead && (s->state == TCP_CLOSE))) {
+ struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
+ if(s->dummy_th.dest) {
+ if(s->dummy_th.dest != rmt_port)
+ continue;
+ }
+ if(!ipv6_addr_any(&np->daddr) &&
+ ipv6_addr_cmp(&np->daddr, rmt_addr))
+ continue;
+
+ if(!ipv6_addr_any(&np->rcv_saddr)) {
+ if(ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
+ return s;
+ }
+ if(!inet6_mc_check(s, loc_addr))
+ continue;
+ return s;
+ }
+ }
+ return NULL;
+}
+
+static void udpv6_mcast_deliver(struct udphdr *uh,
+ struct in6_addr *saddr, struct in6_addr *daddr,
+ struct sk_buff *skb)
+{
+ struct sock *sk, *sk2;
+
+ sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
+ sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr);
+ if(sk) {
+ sk2 = sk;
+ while((sk2 = udp_v6_mcast_next(sk2->next,
+ uh->dest, saddr,
+ uh->source, daddr))) {
+ struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC);
+ if(sock_queue_rcv_skb(sk, buff) < 0) {
+ buff->sk = NULL;
+ kfree_skb(buff, FREE_READ);
+ }
+ }
+ }
+ if(!sk || sock_queue_rcv_skb(sk, skb) < 0) {
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ }
+}
+
int udpv6_rcv(struct sk_buff *skb, struct device *dev,
struct in6_addr *saddr, struct in6_addr *daddr,
struct ipv6_options *opt, unsigned short len,
@@ -314,16 +500,14 @@ int udpv6_rcv(struct sk_buff *skb, struct device *dev,
ulen = ntohs(uh->len);
- if (ulen > len || len < sizeof(*uh))
- {
+ if (ulen > len || len < sizeof(*uh)) {
printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len);
udp_stats_in6.UdpInErrors++;
kfree_skb(skb, FREE_READ);
return(0);
}
- if (uh->check == 0)
- {
+ if (uh->check == 0) {
printk(KERN_DEBUG "IPv6: udp checksum is 0\n");
goto discard;
}
@@ -332,53 +516,19 @@ int udpv6_rcv(struct sk_buff *skb, struct device *dev,
case CHECKSUM_NONE:
skb->csum = csum_partial((char*)uh, len, 0);
case CHECKSUM_HW:
- if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, skb->csum))
- {
+ if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, skb->csum)) {
printk(KERN_DEBUG "IPv6: udp checksum error\n");
goto discard;
}
- }
+ };
len = ulen;
/*
* Multicast receive code
*/
- if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST)
- {
- struct sock *sk2;
- int lport;
-
- lport = ntohs(uh->dest);
- sk = udpv6_prot.sock_array[lport & (SOCK_ARRAY_SIZE-1)];
-
- sk = inet6_get_sock_mcast(sk, lport, uh->source,
- daddr, saddr);
-
- if (sk)
- {
- sk2 = sk;
-
- while ((sk2 = inet6_get_sock_mcast(sk2->next, lport,
- uh->source,
- daddr, saddr)))
- {
- struct sk_buff *buff;
-
- buff = skb_clone(skb, GFP_ATOMIC);
-
- if (sock_queue_rcv_skb(sk, buff) < 0)
- {
- buff->sk = NULL;
- kfree_skb(buff, FREE_READ);
- }
- }
- }
- if (!sk || sock_queue_rcv_skb(sk, skb) < 0)
- {
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
- }
+ if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {
+ udpv6_mcast_deliver(uh, saddr, daddr, skb);
return 0;
}
@@ -389,14 +539,12 @@ int udpv6_rcv(struct sk_buff *skb, struct device *dev,
* for sock caches... i'll skip this for now.
*/
- sk = inet6_get_sock(&udpv6_prot, daddr, saddr, uh->dest, uh->source);
+ sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest);
- if (sk == NULL)
- {
+ if (sk == NULL) {
udp_stats_in6.UdpNoPorts++;
- icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH,
- 0, dev);
+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev);
kfree_skb(skb, FREE_READ);
return(0);
@@ -404,18 +552,14 @@ int udpv6_rcv(struct sk_buff *skb, struct device *dev,
/* deliver */
- if (sk->users)
- {
+ if (sk->sock_readers)
__skb_queue_tail(&sk->back_log, skb);
- }
else
- {
udpv6_queue_rcv_skb(sk, skb);
- }
return(0);
- discard:
+discard:
udp_stats_in6.UdpInErrors++;
kfree_skb(skb, FREE_READ);
return(0);
@@ -448,12 +592,9 @@ static int udpv6_getfrag(const void *data, struct in6_addr *addr,
dst = buff;
- if (offset)
- {
+ if (offset) {
offset -= sizeof(struct udphdr);
- }
- else
- {
+ } else {
dst += sizeof(struct udphdr);
final = 1;
clen -= sizeof(struct udphdr);
@@ -462,19 +603,15 @@ static int udpv6_getfrag(const void *data, struct in6_addr *addr,
udh->wcheck = csum_partial_copy_fromiovecend(dst, udh->iov, offset,
clen, udh->wcheck);
- if (final)
- {
+ if (final) {
struct in6_addr *daddr;
udh->wcheck = csum_partial((char *)udh, sizeof(struct udphdr),
udh->wcheck);
- if (udh->daddr)
- {
+ if (udh->daddr) {
daddr = udh->daddr;
- }
- else
- {
+ } else {
/*
* use packet destination address
* this should improve cache locality
@@ -492,29 +629,28 @@ static int udpv6_getfrag(const void *data, struct in6_addr *addr,
return 0;
}
-static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen,
- int noblock, int flags)
+static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
{
-
struct ipv6_options opt_space;
struct udpv6fakehdr udh;
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name;
struct ipv6_options *opt = NULL;
struct device *dev = NULL;
+ struct flowi fl;
int addr_len = msg->msg_namelen;
struct in6_addr *daddr;
struct in6_addr *saddr = NULL;
int len = ulen + sizeof(struct udphdr);
int addr_type;
- int err;
+ int hlimit = -1;
+ int err;
- if (flags & ~MSG_DONTROUTE)
+ if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
return(-EINVAL);
- if (sin6)
- {
+ if (sin6) {
if (addr_len < sizeof(*sin6))
return(-EINVAL);
@@ -527,14 +663,11 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen,
udh.uh.dest = sin6->sin6_port;
daddr = &sin6->sin6_addr;
- if (np->dest && ipv6_addr_cmp(daddr, &np->daddr))
- {
- ipv6_dst_unlock(np->dest);
- np->dest = NULL;
+ if (np->dst && ipv6_addr_cmp(daddr, &np->daddr)) {
+ dst_release(np->dst);
+ np->dst = NULL;
}
- }
- else
- {
+ } else {
if (sk->state != TCP_ESTABLISHED)
return(-EINVAL);
@@ -544,34 +677,29 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen,
addr_type = ipv6_addr_type(daddr);
- if (addr_type == IPV6_ADDR_MAPPED)
- {
+ if (addr_type == IPV6_ADDR_MAPPED) {
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = daddr->s6_addr32[3];
- return udp_sendmsg(sk, msg, len, noblock, flags);
+ return udp_sendmsg(sk, msg, len);
}
udh.daddr = NULL;
- if (msg->msg_control)
- {
+ if (msg->msg_controllen) {
opt = &opt_space;
memset(opt, 0, sizeof(struct ipv6_options));
- err = datagram_send_ctl(msg, &dev, &saddr, opt);
- if (err < 0)
- {
+ err = datagram_send_ctl(msg, &dev, &saddr, opt, &hlimit);
+ if (err < 0) {
printk(KERN_DEBUG "invalid msg_control\n");
return err;
}
if (opt->srcrt)
- {
udh.daddr = daddr;
- }
}
udh.uh.source = sk->dummy_th.source;
@@ -580,10 +708,17 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen,
udh.iov = msg->msg_iov;
udh.wcheck = 0;
udh.pl_len = len;
-
- err = ipv6_build_xmit(sk, udpv6_getfrag, &udh, daddr, len,
- saddr, dev, opt, IPPROTO_UDP, noblock);
-
+
+ fl.proto = IPPROTO_UDP;
+ fl.nl_u.ip6_u.daddr = daddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.dev = dev;
+ fl.uli_u.ports.dport = udh.uh.dest;
+ fl.uli_u.ports.sport = udh.uh.source;
+
+ err = ip6_build_xmit(sk, udpv6_getfrag, &udh, &fl, len, opt, hlimit,
+ msg->msg_flags);
+
if (err < 0)
return err;
@@ -604,28 +739,35 @@ static struct inet6_protocol udpv6_protocol =
struct proto udpv6_prot = {
- udpv6_close,
- udpv6_connect,
- NULL,
- NULL,
- NULL,
- NULL,
- datagram_select,
- udp_ioctl,
- NULL,
- NULL,
- NULL,
- ipv6_setsockopt,
- ipv6_getsockopt,
- udpv6_sendmsg,
- udpv6_recvmsg,
- NULL, /* No special bind function */
- udpv6_queue_rcv_skb,
- 128,
- 0,
- "UDP",
- 0, 0,
- NULL
+ (struct sock *)&udpv6_prot, /* sklist_next */
+ (struct sock *)&udpv6_prot, /* sklist_prev */
+ udpv6_close, /* close */
+ udpv6_connect, /* connect */
+ NULL, /* accept */
+ NULL, /* retransmit */
+ NULL, /* write_wakeup */
+ NULL, /* read_wakeup */
+ datagram_poll, /* poll */
+ udp_ioctl, /* ioctl */
+ NULL, /* init */
+ NULL, /* destroy */
+ NULL, /* shutdown */
+ ipv6_setsockopt, /* setsockopt */
+ ipv6_getsockopt, /* getsockopt */
+ udpv6_sendmsg, /* sendmsg */
+ udpv6_recvmsg, /* recvmsg */
+ NULL, /* bind */
+ udpv6_queue_rcv_skb, /* backlog_rcv */
+ udp_v6_hash, /* hash */
+ udp_v6_unhash, /* unhash */
+ udp_v6_rehash, /* rehash */
+ udp_good_socknum, /* good_socknum */
+ udp_v6_verify_bind, /* verify_bind */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "UDP", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
};
void udpv6_init(void)