diff options
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/Makefile | 14 | ||||
-rw-r--r-- | net/ipv6/addrconf.c | 1293 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 757 | ||||
-rw-r--r-- | net/ipv6/datagram.c | 138 | ||||
-rw-r--r-- | net/ipv6/exthdrs.c | 39 | ||||
-rw-r--r-- | net/ipv6/icmp.c | 233 | ||||
-rw-r--r-- | net/ipv6/ip6_fib.c | 927 | ||||
-rw-r--r-- | net/ipv6/ip6_fw.c | 378 | ||||
-rw-r--r-- | net/ipv6/ip6_input.c | 408 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 629 | ||||
-rw-r--r-- | net/ipv6/ipv6_input.c | 437 | ||||
-rw-r--r-- | net/ipv6/ipv6_output.c | 1003 | ||||
-rw-r--r-- | net/ipv6/ipv6_route.c | 2056 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 120 | ||||
-rw-r--r-- | net/ipv6/mcast.c | 409 | ||||
-rw-r--r-- | net/ipv6/ndisc.c | 1789 | ||||
-rw-r--r-- | net/ipv6/proc.c | 143 | ||||
-rw-r--r-- | net/ipv6/protocol.c | 43 | ||||
-rw-r--r-- | net/ipv6/raw.c | 336 | ||||
-rw-r--r-- | net/ipv6/reassembly.c | 85 | ||||
-rw-r--r-- | net/ipv6/route.c | 1599 | ||||
-rw-r--r-- | net/ipv6/sit.c | 255 | ||||
-rw-r--r-- | net/ipv6/sysctl_net_ipv6.c | 96 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 989 | ||||
-rw-r--r-- | net/ipv6/udp.c | 494 |
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) |