diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-03-07 15:45:24 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-03-07 15:45:24 +0000 |
commit | 9f9f3e6e8548a596697778337110a423c384b6f3 (patch) | |
tree | 5dd4b290ef532cf5ecb058e1a92cd3435afeac8c /net | |
parent | d5c9a365ee7d2fded249aa5abfc5e89587583029 (diff) |
Merge with Linux 2.3.49.
Diffstat (limited to 'net')
34 files changed, 650 insertions, 448 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index d2fac6697..5f7b12541 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -5,7 +5,7 @@ * Authors: * Lennert Buytenhek <buytenh@gnu.org> * - * $Id: br_device.c,v 1.2 2000/02/24 19:48:06 davem Exp $ + * $Id: br_device.c,v 1.3 2000/03/01 02:58:09 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -13,7 +13,6 @@ * 2 of the License, or (at your option) any later version. */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/netdevice.h> #include <linux/if_bridge.h> diff --git a/net/core/dev.c b/net/core/dev.c index 638ab6432..b09b3b9a4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -76,6 +76,7 @@ #include <linux/etherdevice.h> #include <linux/notifier.h> #include <linux/skbuff.h> +#include <linux/brlock.h> #include <net/sock.h> #include <linux/rtnetlink.h> #include <net/slhc.h> @@ -129,7 +130,6 @@ const char *if_port_text[] = { static struct packet_type *ptype_base[16]; /* 16 way hashed list */ static struct packet_type *ptype_all = NULL; /* Taps */ -static rwlock_t ptype_lock = RW_LOCK_UNLOCKED; /* * Our notifier list @@ -181,7 +181,7 @@ void dev_add_pack(struct packet_type *pt) { int hash; - write_lock_bh(&ptype_lock); + br_write_lock_bh(BR_NETPROTO_LOCK); #ifdef CONFIG_NET_FASTROUTE /* Hack to detect packet socket */ @@ -199,7 +199,7 @@ void dev_add_pack(struct packet_type *pt) pt->next = ptype_base[hash]; ptype_base[hash] = pt; } - write_unlock_bh(&ptype_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); } @@ -211,7 +211,7 @@ void dev_remove_pack(struct packet_type *pt) { struct packet_type **pt1; - write_lock_bh(&ptype_lock); + br_write_lock_bh(BR_NETPROTO_LOCK); if (pt->type == htons(ETH_P_ALL)) { netdev_nit--; @@ -227,11 +227,11 @@ void dev_remove_pack(struct packet_type *pt) if (pt->data) netdev_fastroute_obstacles--; #endif - write_unlock_bh(&ptype_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); return; } } - write_unlock_bh(&ptype_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt); } @@ -581,7 +581,7 @@ void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) struct packet_type *ptype; get_fast_time(&skb->stamp); - read_lock(&ptype_lock); + br_read_lock(BR_NETPROTO_LOCK); for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) { /* Never send packets back to the socket @@ -615,7 +615,7 @@ void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) ptype->func(skb2, skb->dev, ptype); } } - read_unlock(&ptype_lock); + br_read_unlock(BR_NETPROTO_LOCK); } /* @@ -863,8 +863,8 @@ static void deliver_to_old_ones(struct packet_type *pt, struct sk_buff *skb, int } /* Reparent skb to master device. This function is called - * only from net_rx_action under ptype_lock. It is misuse - * of ptype_lock, but it is OK for now. + * only from net_rx_action under BR_NETPROTO_LOCK. It is misuse + * of BR_NETPROTO_LOCK, but it is OK for now. */ static __inline__ void skb_bond(struct sk_buff *skb) { @@ -924,9 +924,9 @@ static void net_tx_action(struct softirq_action *h) void net_call_rx_atomic(void (*fn)(void)) { - write_lock_bh(&ptype_lock); + br_write_lock_bh(BR_NETPROTO_LOCK); fn(); - write_unlock_bh(&ptype_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); } #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) @@ -936,11 +936,13 @@ void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; static void __inline__ handle_bridge(struct sk_buff *skb, struct packet_type *pt_prev) { - if (pt_prev) - deliver_to_old_ones(pt_prev, skb, 0); - else { - atomic_inc(&skb->users); - pt_prev->func(skb, skb->dev, pt_prev); + if (pt_prev) { + if (!pt_prev->data) + deliver_to_old_ones(pt_prev, skb, 0); + else { + atomic_inc(&skb->users); + pt_prev->func(skb, skb->dev, pt_prev); + } } br_handle_frame_hook(skb); @@ -954,7 +956,7 @@ static void net_rx_action(struct softirq_action *h) unsigned long start_time = jiffies; int bugdet = netdev_max_backlog; - read_lock(&ptype_lock); + br_read_lock(BR_NETPROTO_LOCK); for (;;) { struct sk_buff *skb; @@ -1034,7 +1036,7 @@ static void net_rx_action(struct softirq_action *h) if (bugdet-- < 0 || jiffies - start_time > 1) goto softnet_break; } - read_unlock(&ptype_lock); + br_read_unlock(BR_NETPROTO_LOCK); local_irq_disable(); if (queue->throttle) { @@ -1050,7 +1052,7 @@ static void net_rx_action(struct softirq_action *h) return; softnet_break: - read_unlock(&ptype_lock); + br_read_unlock(BR_NETPROTO_LOCK); local_irq_disable(); netdev_rx_stat[this_cpu].time_squeeze++; @@ -1391,9 +1393,9 @@ int netdev_set_master(struct net_device *slave, struct net_device *master) dev_hold(master); } - write_lock_bh(&ptype_lock); + br_write_lock_bh(BR_NETPROTO_LOCK); slave->master = master; - write_unlock_bh(&ptype_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); if (old) dev_put(old); @@ -1516,7 +1518,7 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) case SIOCGIFFLAGS: /* Get interface flags */ ifr->ifr_flags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI|IFF_RUNNING)) |(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI)); - if (netif_running(dev)) + if (netif_running(dev) && netif_carrier_ok(dev)) ifr->ifr_flags |= IFF_RUNNING; return 0; @@ -2129,6 +2131,7 @@ int __init net_dev_init(void) if (dev->rebuild_header == NULL) dev->rebuild_header = default_rebuild_header; dev_init_scheduler(dev); + set_bit(__LINK_STATE_PRESENT, &dev->state); } } diff --git a/net/core/netfilter.c b/net/core/netfilter.c index bf734a60e..8b04435c3 100644 --- a/net/core/netfilter.c +++ b/net/core/netfilter.c @@ -5,6 +5,8 @@ * way. * * Rusty Russell (C)1998 -- This code is GPL. + * + * February 2000: Modified by James Morris to have 1 queue per protocol. */ #include <linux/config.h> #include <linux/netfilter.h> @@ -16,7 +18,7 @@ #include <linux/interrupt.h> #include <linux/if.h> #include <linux/netdevice.h> -#include <linux/spinlock.h> +#include <linux/brlock.h> #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> @@ -32,41 +34,31 @@ #define NFDEBUG(format, args...) #endif -/* Each queued (to userspace) skbuff has one of these. */ -struct nf_info -{ - /* The ops struct which sent us to userspace. */ - struct nf_hook_ops *elem; - - /* If we're sent to userspace, this keeps housekeeping info */ - int pf; - unsigned long mark; - unsigned int hook; - struct net_device *indev, *outdev; - int (*okfn)(struct sk_buff *); -}; - -static rwlock_t nf_lock = RW_LOCK_UNLOCKED; +/* Sockopts only registered and called from user context, so + BR_NETPROTO_LOCK would be overkill. Also, [gs]etsockopt calls may + sleep. */ static DECLARE_MUTEX(nf_sockopt_mutex); struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS]; static LIST_HEAD(nf_sockopts); -static LIST_HEAD(nf_interested); + +/* + * A queue handler may be registered for each protocol. Each is protected by + * long term mutex. The handler must provide an an outfn() to accept packets + * for queueing and must reinject all packets it receives, no matter what. + */ +static struct nf_queue_handler_t { + nf_queue_outfn_t outfn; + void *data; +} queue_handler[NPROTO]; int nf_register_hook(struct nf_hook_ops *reg) { struct list_head *i; -#ifdef CONFIG_NETFILTER_DEBUG - if (reg->pf<0 || reg->pf>=NPROTO || reg->hooknum >= NF_MAX_HOOKS) { - NFDEBUG("nf_register_hook: bad vals: pf=%i, hooknum=%u.\n", - reg->pf, reg->hooknum); - return -EINVAL; - } -#endif NFDEBUG("nf_register_hook: pf=%i hook=%u.\n", reg->pf, reg->hooknum); - - write_lock_bh(&nf_lock); + + br_write_lock_bh(BR_NETPROTO_LOCK); for (i = nf_hooks[reg->pf][reg->hooknum].next; i != &nf_hooks[reg->pf][reg->hooknum]; i = i->next) { @@ -74,22 +66,15 @@ int nf_register_hook(struct nf_hook_ops *reg) break; } list_add(®->list, i->prev); - write_unlock_bh(&nf_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); return 0; } void nf_unregister_hook(struct nf_hook_ops *reg) { -#ifdef CONFIG_NETFILTER_DEBUG - if (reg->pf<0 || reg->pf>=NPROTO || reg->hooknum >= NF_MAX_HOOKS) { - NFDEBUG("nf_unregister_hook: bad vals: pf=%i, hooknum=%u.\n", - reg->pf, reg->hooknum); - return; - } -#endif - write_lock_bh(&nf_lock); + br_write_lock_bh(BR_NETPROTO_LOCK); list_del(®->list); - write_unlock_bh(&nf_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); } /* Do exclusive ranges overlap? */ @@ -105,22 +90,6 @@ int nf_register_sockopt(struct nf_sockopt_ops *reg) struct list_head *i; int ret = 0; -#ifdef CONFIG_NETFILTER_DEBUG - if (reg->pf<0 || reg->pf>=NPROTO) { - NFDEBUG("nf_register_sockopt: bad val: pf=%i.\n", reg->pf); - return -EINVAL; - } - if (reg->set_optmin > reg->set_optmax) { - NFDEBUG("nf_register_sockopt: bad set val: min=%i max=%i.\n", - reg->set_optmin, reg->set_optmax); - return -EINVAL; - } - if (reg->get_optmin > reg->get_optmax) { - NFDEBUG("nf_register_sockopt: bad get val: min=%i max=%i.\n", - reg->get_optmin, reg->get_optmax); - return -EINVAL; - } -#endif if (down_interruptible(&nf_sockopt_mutex) != 0) return -EINTR; @@ -149,12 +118,6 @@ out: void nf_unregister_sockopt(struct nf_sockopt_ops *reg) { -#ifdef CONFIG_NETFILTER_DEBUG - if (reg->pf<0 || reg->pf>=NPROTO) { - NFDEBUG("nf_register_sockopt: bad val: pf=%i.\n", reg->pf); - return; - } -#endif /* No point being interruptible: we're probably in cleanup_module() */ down(&nf_sockopt_mutex); list_del(®->list); @@ -167,6 +130,33 @@ void nf_unregister_sockopt(struct nf_sockopt_ops *reg) #include <net/tcp.h> #include <linux/netfilter_ipv4.h> +static void debug_print_hooks_ip(unsigned int nf_debug) +{ + if (nf_debug & (1 << NF_IP_PRE_ROUTING)) { + printk("PRE_ROUTING "); + nf_debug ^= (1 << NF_IP_PRE_ROUTING); + } + if (nf_debug & (1 << NF_IP_LOCAL_IN)) { + printk("LOCAL_IN "); + nf_debug ^= (1 << NF_IP_LOCAL_IN); + } + if (nf_debug & (1 << NF_IP_FORWARD)) { + printk("FORWARD "); + nf_debug ^= (1 << NF_IP_FORWARD); + } + if (nf_debug & (1 << NF_IP_LOCAL_OUT)) { + printk("LOCAL_OUT "); + nf_debug ^= (1 << NF_IP_LOCAL_OUT); + } + if (nf_debug & (1 << NF_IP_POST_ROUTING)) { + printk("POST_ROUTING "); + nf_debug ^= (1 << NF_IP_POST_ROUTING); + } + if (nf_debug) + printk("Crap bits: 0x%04X", nf_debug); + printk("\n"); +} + void nf_dump_skb(int pf, struct sk_buff *skb) { printk("skb: pf=%i %s dev=%s len=%u\n", @@ -257,7 +247,7 @@ void nf_debug_ip_finish_output2(struct sk_buff *skb) { /* If it's owned, it must have gone through the * NF_IP_LOCAL_OUT and NF_IP_POST_ROUTING. - * Otherwise, must have gone through NF_IP_RAW_INPUT, + * Otherwise, must have gone through * NF_IP_PRE_ROUTING, NF_IP_FORWARD and NF_IP_POST_ROUTING. */ if (skb->sk) { @@ -269,9 +259,6 @@ void nf_debug_ip_finish_output2(struct sk_buff *skb) } } else { if (skb->nf_debug != ((1 << NF_IP_PRE_ROUTING) -#ifdef CONFIG_IP_NETFILTER_RAW_INPUT - | (1 << NF_IP_RAW_INPUT) -#endif | (1 << NF_IP_FORWARD) | (1 << NF_IP_POST_ROUTING))) { printk("ip_finish_output: bad unowned skb = %p: ",skb); @@ -280,29 +267,8 @@ void nf_debug_ip_finish_output2(struct sk_buff *skb) } } } - - #endif /*CONFIG_NETFILTER_DEBUG*/ -void nf_cacheflush(int pf, unsigned int hook, const void *packet, - const struct net_device *indev, const struct net_device *outdev, - __u32 packetcount, __u32 bytecount) -{ - struct list_head *i; - - read_lock_bh(&nf_lock); - for (i = nf_hooks[pf][hook].next; - i != &nf_hooks[pf][hook]; - i = i->next) { - if (((struct nf_hook_ops *)i)->flush) - ((struct nf_hook_ops *)i)->flush(packet, indev, - outdev, - packetcount, - bytecount); - } - read_unlock_bh(&nf_lock); -} - /* Call get/setsockopt() */ static int nf_sockopt(struct sock *sk, int pf, int val, char *opt, int *len, int get) @@ -360,15 +326,12 @@ static unsigned int nf_iterate(struct list_head *head, struct nf_hook_ops *elem = (struct nf_hook_ops *)*i; switch (elem->hook(hook, skb, indev, outdev, okfn)) { case NF_QUEUE: - NFDEBUG("nf_iterate: NF_QUEUE for %p.\n", *skb); return NF_QUEUE; case NF_STOLEN: - NFDEBUG("nf_iterate: NF_STOLEN for %p.\n", *skb); return NF_STOLEN; case NF_DROP: - NFDEBUG("nf_iterate: NF_DROP for %p.\n", *skb); return NF_DROP; #ifdef CONFIG_NETFILTER_DEBUG @@ -384,6 +347,38 @@ static unsigned int nf_iterate(struct list_head *head, return NF_ACCEPT; } +int nf_register_queue_handler(int pf, nf_queue_outfn_t outfn, void *data) +{ + int ret; + + br_write_lock_bh(BR_NETPROTO_LOCK); + if (queue_handler[pf].outfn) + ret = -EBUSY; + else { + queue_handler[pf].outfn = outfn; + queue_handler[pf].data = data; + ret = 0; + } + br_write_unlock_bh(BR_NETPROTO_LOCK); + + return ret; +} + +/* The caller must flush their queue before this */ +int nf_unregister_queue_handler(int pf) +{ + NFDEBUG("Unregistering Netfilter queue handler for pf=%d\n", pf); + br_write_lock_bh(BR_NETPROTO_LOCK); + queue_handler[pf].outfn = NULL; + queue_handler[pf].data = NULL; + br_write_unlock_bh(BR_NETPROTO_LOCK); + return 0; +} + +/* + * Any packet that leaves via this function must come back + * through nf_reinject(). + */ static void nf_queue(struct sk_buff *skb, struct list_head *elem, int pf, unsigned int hook, @@ -391,61 +386,43 @@ static void nf_queue(struct sk_buff *skb, struct net_device *outdev, int (*okfn)(struct sk_buff *)) { - struct list_head *i; + int status; + struct nf_info *info; - struct nf_info *info = kmalloc(sizeof(*info), GFP_ATOMIC); + if (!queue_handler[pf].outfn) { + NFDEBUG("nf_queue: noone wants the packet, dropping it.\n"); + kfree_skb(skb); + return; + } + + info = kmalloc(sizeof(*info), GFP_ATOMIC); if (!info) { - NFDEBUG("nf_hook: OOM.\n"); + if (net_ratelimit()) + printk(KERN_ERR "OOM queueing packet %p\n", + skb); kfree_skb(skb); return; } - /* Can't do struct assignments with arrays in them. Damn. */ - info->elem = (struct nf_hook_ops *)elem; - info->mark = skb->nfmark; - info->pf = pf; - info->hook = hook; - info->okfn = okfn; - info->indev = indev; - info->outdev = outdev; - skb->nfmark = (unsigned long)info; + *info = (struct nf_info) { + (struct nf_hook_ops *)elem, pf, hook, indev, outdev, okfn }; /* Bump dev refs so they don't vanish while packet is out */ if (indev) dev_hold(indev); if (outdev) dev_hold(outdev); - for (i = nf_interested.next; i != &nf_interested; i = i->next) { - struct nf_interest *recip = (struct nf_interest *)i; - - if ((recip->hookmask & (1 << info->hook)) - && info->pf == recip->pf - && (!recip->mark || info->mark == recip->mark) - && (!recip->reason || skb->nfreason == recip->reason)) { - /* FIXME: Andi says: use netlink. Hmmm... --RR */ - if (skb_queue_len(&recip->wake->skbq) >= 100) { - NFDEBUG("nf_hook: queue to long.\n"); - goto free_discard; - } - /* Hand it to userspace for collection */ - skb_queue_tail(&recip->wake->skbq, skb); - NFDEBUG("Waking up pf=%i hook=%u mark=%lu reason=%u\n", - pf, hook, skb->nfmark, skb->nfreason); - wake_up_interruptible(&recip->wake->sleep); - - return; - } + status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data); + if (status < 0) { + /* James M doesn't say fuck enough. */ + if (indev) dev_put(indev); + if (outdev) dev_put(outdev); + kfree_s(info, sizeof(*info)); + kfree_skb(skb); + return; } - NFDEBUG("nf_hook: noone wants the packet.\n"); - - free_discard: - if (indev) dev_put(indev); - if (outdev) dev_put(outdev); - - kfree_s(info, sizeof(*info)); - kfree_skb(skb); } -/* nf_hook() doesn't have lock, so may give false positive. */ +/* We have BR_NETPROTO_LOCK here */ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, @@ -455,21 +432,6 @@ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb, unsigned int verdict; int ret = 0; -#ifdef CONFIG_NETFILTER_DEBUG - if (pf < 0 || pf >= NPROTO || hook >= NF_MAX_HOOKS) { - NFDEBUG("nf_hook: bad vals: pf=%i, hook=%u.\n", - pf, hook); - kfree_skb(skb); - return -EINVAL; /* -ECODERFUCKEDUP ?*/ - } - - if (skb->nf_debug & (1 << hook)) { - NFDEBUG("nf_hook: hook %i already set.\n", hook); - nf_dump_skb(pf, skb); - } - skb->nf_debug |= (1 << hook); -#endif - read_lock_bh(&nf_lock); elem = &nf_hooks[pf][hook]; verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev, outdev, &elem, okfn); @@ -477,7 +439,6 @@ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb, NFDEBUG("nf_hook: Verdict = QUEUE.\n"); nf_queue(skb, elem, pf, hook, indev, outdev, okfn); } - read_unlock_bh(&nf_lock); switch (verdict) { case NF_ACCEPT: @@ -493,84 +454,41 @@ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb, return ret; } -struct nf_waitinfo { - unsigned int verdict; - struct task_struct *owner; -}; - -/* For netfilter device. */ -void nf_register_interest(struct nf_interest *interest) +void nf_reinject(struct sk_buff *skb, struct nf_info *info, + unsigned int verdict) { - /* First in, best dressed. */ - write_lock_bh(&nf_lock); - list_add(&interest->list, &nf_interested); - write_unlock_bh(&nf_lock); -} - -void nf_unregister_interest(struct nf_interest *interest) -{ - struct sk_buff *skb; - - write_lock_bh(&nf_lock); - list_del(&interest->list); - write_unlock_bh(&nf_lock); - - /* Blow away any queued skbs; this is overzealous. */ - while ((skb = skb_dequeue(&interest->wake->skbq)) != NULL) - nf_reinject(skb, 0, NF_DROP); -} - -void nf_getinfo(const struct sk_buff *skb, - struct net_device **indev, - struct net_device **outdev, - unsigned long *mark) -{ - const struct nf_info *info = (const struct nf_info *)skb->nfmark; - - *indev = info->indev; - *outdev = info->outdev; - *mark = info->mark; -} - -void nf_reinject(struct sk_buff *skb, unsigned long mark, unsigned int verdict) -{ - struct nf_info *info = (struct nf_info *)skb->nfmark; struct list_head *elem = &info->elem->list; struct list_head *i; - read_lock_bh(&nf_lock); - + /* We don't have BR_NETPROTO_LOCK here */ + br_read_lock_bh(BR_NETPROTO_LOCK); for (i = nf_hooks[info->pf][info->hook].next; i != elem; i = i->next) { if (i == &nf_hooks[info->pf][info->hook]) { /* The module which sent it to userspace is gone. */ + NFDEBUG("%s: module disappeared, dropping packet.\n", + __FUNCTION__); verdict = NF_DROP; break; } } - /* Continue traversal iff userspace said ok, and devices still - exist... */ + /* Continue traversal iff userspace said ok... */ if (verdict == NF_ACCEPT) { - skb->nfmark = mark; verdict = nf_iterate(&nf_hooks[info->pf][info->hook], &skb, info->hook, info->indev, info->outdev, &elem, info->okfn); } - if (verdict == NF_QUEUE) { - nf_queue(skb, elem, info->pf, info->hook, - info->indev, info->outdev, info->okfn); - } - read_unlock_bh(&nf_lock); - switch (verdict) { case NF_ACCEPT: - local_bh_disable(); info->okfn(skb); - local_bh_enable(); break; + case NF_QUEUE: + nf_queue(skb, elem, info->pf, info->hook, + info->indev, info->outdev, info->okfn); + case NF_DROP: kfree_skb(skb); break; @@ -579,51 +497,17 @@ void nf_reinject(struct sk_buff *skb, unsigned long mark, unsigned int verdict) /* Release those devices we held, or Alexey will kill me. */ if (info->indev) dev_put(info->indev); if (info->outdev) dev_put(info->outdev); - + kfree_s(info, sizeof(*info)); return; } -/* FIXME: Before cache is ever used, this must be implemented for real. */ -void nf_invalidate_cache(int pf) -{ -} - -#ifdef CONFIG_NETFILTER_DEBUG - -void debug_print_hooks_ip(unsigned int nf_debug) -{ - if (nf_debug & (1 << NF_IP_PRE_ROUTING)) { - printk("PRE_ROUTING "); - nf_debug ^= (1 << NF_IP_PRE_ROUTING); - } - if (nf_debug & (1 << NF_IP_LOCAL_IN)) { - printk("LOCAL_IN "); - nf_debug ^= (1 << NF_IP_LOCAL_IN); - } - if (nf_debug & (1 << NF_IP_FORWARD)) { - printk("FORWARD "); - nf_debug ^= (1 << NF_IP_FORWARD); - } - if (nf_debug & (1 << NF_IP_LOCAL_OUT)) { - printk("LOCAL_OUT "); - nf_debug ^= (1 << NF_IP_LOCAL_OUT); - } - if (nf_debug & (1 << NF_IP_POST_ROUTING)) { - printk("POST_ROUTING "); - nf_debug ^= (1 << NF_IP_POST_ROUTING); - } - if (nf_debug) - printk("Crap bits: 0x%04X", nf_debug); - printk("\n"); -} -#endif /* CONFIG_NETFILTER_DEBUG */ - void __init netfilter_init(void) { int i, h; - for (i = 0; i < NPROTO; i++) + for (i = 0; i < NPROTO; i++) { for (h = 0; h < NF_MAX_HOOKS; h++) INIT_LIST_HEAD(&nf_hooks[i][h]); + } } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index c549162a9..8749dfb0d 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -171,7 +171,7 @@ static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, r->ifi_flags = dev->flags; r->ifi_change = change; - if (! netif_running(dev)) + if (!netif_running(dev) || !netif_carrier_ok(dev)) r->ifi_flags &= ~IFF_RUNNING; else r->ifi_flags |= IFF_RUNNING; diff --git a/net/core/sock.c b/net/core/sock.c index c5781c6e3..21f15b5e7 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -7,7 +7,7 @@ * handler for protocols to use and generic option handler. * * - * Version: $Id: sock.c,v 1.89 2000/01/18 08:24:13 davem Exp $ + * Version: $Id: sock.c,v 1.90 2000/02/27 19:48:11 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -526,7 +526,20 @@ int sock_getsockopt(struct socket *sock, int level, int optname, if(copy_to_user((void*)optval, &sk->peercred, len)) return -EFAULT; goto lenout; - + + case SO_PEERNAME: + { + char address[128]; + + if (sock->ops->getname(sock, (struct sockaddr *)address, &lv, 2)) + return -ENOTCONN; + if (lv < len) + return -EINVAL; + if(copy_to_user((void*)optval, address, len)) + return -EFAULT; + goto lenout; + } + default: return(-ENOPROTOOPT); } diff --git a/net/decnet/TODO b/net/decnet/TODO index c5e7f5cd7..0f5c3a649 100644 --- a/net/decnet/TODO +++ b/net/decnet/TODO @@ -16,10 +16,6 @@ Steve's quick list of things that need finishing off: o sendmsg() in the raw socket layer (yes, its for sending routing messages) - o Better filtering of traffic in raw sockets. Aside from receiving routing - messages, there really doesn't seem to be a lot else that raw sockets - could be useful for... suggestions on a postcard please :-) - o Fix /proc for raw sockets o Lots of testing with real applications diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 04728d5d8..7860597ab 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -106,6 +106,7 @@ Version 0.0.6 2.1.110 07-aug-98 Eduardo Marcelo Serrat #include <linux/netdevice.h> #include <linux/inet.h> #include <linux/route.h> +#include <linux/netfilter.h> #include <net/sock.h> #include <asm/segment.h> #include <asm/system.h> @@ -1005,6 +1006,12 @@ static int dn_accept(struct socket *sock, struct socket *newsock, int flags) memcpy(&newsk->protinfo.dn.addr, &sk->protinfo.dn.addr, sizeof(struct sockaddr_dn)); + /* + * If we are listening on a wild socket, we don't want + * the newly created socket on the wrong hash queue. + */ + newsk->protinfo.dn.addr.sdn_flags &= ~SDF_WILD; + skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &newsk->protinfo.dn.addr, &type)); skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &newsk->protinfo.dn.peer, &type)); *(dn_address *)newsk->protinfo.dn.peer.sdn_add.a_addr = cb->src; @@ -1300,9 +1307,6 @@ static int __dn_setsockopt(struct socket *sock, int level,int optname, char *opt struct dn_scp *scp = &sk->protinfo.dn; struct optdata_dn opt; struct accessdata_dn acc; -#ifdef CONFIG_DECNET_FW - char tmp_fw[MAX(sizeof(struct dn_fwtest),sizeof(struct dn_fwnew))]; -#endif int err; if (optlen && !optval) @@ -1404,34 +1408,15 @@ static int __dn_setsockopt(struct socket *sock, int level,int optname, char *opt dn_nsp_send_disc(sk, 0x38, 0, GFP_KERNEL); break; -#ifdef CONFIG_DECNET_FW - case DN_FW_APPEND: - case DN_FW_REPLACE: - case DN_FW_DELETE: - case DN_FW_DELETE_NUM: - case DN_FW_INSERT: - case DN_FW_FLUSH: - case DN_FW_ZERO: - case DN_FW_CHECK: - case DN_FW_CREATECHAIN: - case DN_FW_DELETECHAIN: - case DN_FW_POLICY: - - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - if ((optlen > sizeof(tmp_fw)) || (optlen < 1)) - return -EINVAL; - if (copy_from_user(&tmp_fw, optval, optlen)) - return -EFAULT; - err = dn_fw_ctl(optname, &tmp_fw, optlen); - return err; -#endif default: +#ifdef CONFIG_NETFILTER + return nf_setsockopt(sk, PF_DECnet, optname, optval, optlen); +#endif case DSO_LINKINFO: case DSO_STREAM: case DSO_SEQPACKET: - return -EOPNOTSUPP; + return -ENOPROTOOPT; } return 0; @@ -1511,12 +1496,22 @@ static int __dn_getsockopt(struct socket *sock, int level,int optname, char *opt return -EFAULT; break; + default: +#ifdef CONFIG_NETFILTER + { + int val, len = *optlen; + val = nf_getsockopt(sk, PF_DECnet, optname, + optval, &len); + if (val >= 0) + val = put_user(len, optlen); + return val; + } +#endif case DSO_STREAM: case DSO_SEQPACKET: case DSO_CONACCEPT: case DSO_CONREJECT: - default: - return -EOPNOTSUPP; + return -ENOPROTOOPT; } return 0; @@ -1975,7 +1970,7 @@ static struct packet_type dn_dix_packet_type = __constant_htons(ETH_P_DNA_RT), NULL, /* All devices */ dn_route_rcv, - NULL, + (void*)1, NULL, }; diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index eb50c8c54..b2c6b2051 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -432,7 +432,7 @@ void dn_neigh_pointopoint_hello(struct sk_buff *skb) /* * Ethernet router hello message received */ -void dn_neigh_router_hello(struct sk_buff *skb) +int dn_neigh_router_hello(struct sk_buff *skb) { struct rtnode_hello_message *msg = (struct rtnode_hello_message *)skb->data; @@ -485,12 +485,13 @@ void dn_neigh_router_hello(struct sk_buff *skb) } kfree_skb(skb); + return 0; } /* * Endnode hello message received */ -void dn_neigh_endnode_hello(struct sk_buff *skb) +int dn_neigh_endnode_hello(struct sk_buff *skb) { struct endnode_hello_message *msg = (struct endnode_hello_message *)skb->data; struct neighbour *neigh; @@ -523,6 +524,7 @@ void dn_neigh_endnode_hello(struct sk_buff *skb) } kfree_skb(skb); + return 0; } diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c index 9cb0c6394..66c72b4bd 100644 --- a/net/decnet/dn_nsp_in.c +++ b/net/decnet/dn_nsp_in.c @@ -320,7 +320,7 @@ static void dn_nsp_disc_conf(struct sock *sk, struct sk_buff *skb) struct dn_scp *scp = &sk->protinfo.dn; unsigned short reason; - if (skb->len != 2) + if (skb->len < 2) goto out; reason = dn_ntohs(*(__u16 *)skb->data); diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c index 3b487617d..e4d0adad3 100644 --- a/net/decnet/dn_nsp_out.c +++ b/net/decnet/dn_nsp_out.c @@ -19,6 +19,7 @@ * Moved output state machine into one function * Steve Whitehouse: New output state machine * Paul Koning: Connect Confirm message fix. + * Eduardo Serrat: Fix to stop dn_nsp_do_disc() sending malformed packets. */ /****************************************************************************** @@ -510,7 +511,7 @@ static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg, int ddl, unsigned char *dd, __u16 rem, __u16 loc) { struct sk_buff *skb = NULL; - int size = 7 + (ddl ? (ddl + 1) : 0); + int size = 8 + ddl; unsigned char *msg; if ((dst == NULL) || (rem == 0)) { @@ -530,9 +531,9 @@ static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg, msg += 2; *(__u16 *)msg = dn_htons(reason); msg += 2; + *msg++ = ddl; if (ddl) { - *msg++ = ddl; memcpy(msg, dd, ddl); } diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index cc2ffeeef..27ff3a10f 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -30,6 +30,9 @@ * my copying the IPv4 routing code. The * hooks here are modified and will continue * to evolve for a while. + * Steve Whitehouse : Real SMP at last :-) Also new netfilter + * stuff. Look out raw sockets your days + * are numbered! */ /****************************************************************************** @@ -359,9 +362,22 @@ drop_it: return 0; } +static int dn_route_discard(struct sk_buff *skb) +{ + kfree_skb(skb); + return 0; +} + +static int dn_route_ptp_hello(struct sk_buff *skb) +{ + dn_dev_hello(skb); + dn_neigh_pointopoint_hello(skb); + return 0; +} + int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) { - struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb; + struct dn_skb_cb *cb; unsigned char flags = 0; int padlen = 0; __u16 len = dn_ntohs(*(__u16 *)skb->data); @@ -370,8 +386,8 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type if (dn == NULL) goto dump_it; - cb->stamp = jiffies; - cb->iif = dev->ifindex; + if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) + goto out; skb_pull(skb, 2); @@ -382,6 +398,10 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type flags = *skb->data; + cb = (struct dn_skb_cb *)skb->cb; + cb->stamp = jiffies; + cb->iif = dev->ifindex; + /* * If we have padding, remove it. */ @@ -426,24 +446,20 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type switch(flags & DN_RT_CNTL_MSK) { case DN_RT_PKT_HELO: - dn_dev_hello(skb); - dn_neigh_pointopoint_hello(skb); - return 0; + NF_HOOK(PF_DECnet, NF_DN_HELLO, skb, skb->dev, NULL, dn_route_ptp_hello); + goto out; case DN_RT_PKT_L1RT: case DN_RT_PKT_L2RT: -#ifdef CONFIG_DECNET_ROUTER - return dn_fib_rt_message(skb); -#else - break; -#endif /* CONFIG_DECNET_ROUTER */ + NF_HOOK(PF_DECnet, NF_DN_ROUTE, skb, skb->dev, NULL, dn_route_discard); + goto out; case DN_RT_PKT_ERTH: - dn_neigh_router_hello(skb); - return 0; + NF_HOOK(PF_DECnet, NF_DN_HELLO, skb, skb->dev, NULL, dn_neigh_router_hello); + goto out; case DN_RT_PKT_EEDH: - dn_neigh_endnode_hello(skb); - return 0; + NF_HOOK(PF_DECnet, NF_DN_HELLO, skb, skb->dev, NULL, dn_neigh_endnode_hello); + goto out; } } else { if (dn->parms.state != DN_DEV_S_RU) @@ -461,6 +477,7 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type dump_it: kfree_skb(skb); +out: return 0; } diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index c01d447b1..d7da63f4e 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -3,7 +3,7 @@ * * Alan Cox, <alan@redhat.com> * - * Version: $Id: icmp.c,v 1.64 2000/02/09 11:16:40 davem Exp $ + * Version: $Id: icmp.c,v 1.65 2000/02/22 23:54:25 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -801,9 +801,10 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len) /* * This can't change while we are doing it. + * Callers have obtained BR_NETPROTO_LOCK so + * we are OK. */ - read_lock(&inet_protocol_lock); ipprot = (struct inet_protocol *) inet_protos[hash]; while(ipprot != NULL) { struct inet_protocol *nextip; @@ -822,7 +823,6 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len) ipprot = nextip; } - read_unlock(&inet_protocol_lock); } diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 23389d249..0c755dbcd 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -5,7 +5,7 @@ * * The Internet Protocol (IP) module. * - * Version: $Id: ip_input.c,v 1.45 2000/01/16 05:11:22 davem Exp $ + * Version: $Id: ip_input.c,v 1.46 2000/02/22 23:54:26 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -241,7 +241,6 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb) if(raw_sk != NULL) raw_sk = raw_v4_input(skb, iph, hash); - read_lock(&inet_protocol_lock); ipprot = (struct inet_protocol *) inet_protos[hash]; flag = 0; if(ipprot != NULL) { @@ -254,13 +253,11 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb) ret = ipprot->handler(skb, (ntohs(iph->tot_len) - (iph->ihl * 4))); - read_unlock(&inet_protocol_lock); return ret; } else { flag = ip_run_ipprot(skb, iph, ipprot, (raw_sk != NULL)); } } - read_unlock(&inet_protocol_lock); /* All protocols checked. * If this packet was a broadcast, we may *not* reply to it, since that diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c index 2b61e6466..4839764e8 100644 --- a/net/ipv4/protocol.c +++ b/net/ipv4/protocol.c @@ -5,7 +5,7 @@ * * INET protocol dispatch tables. * - * Version: $Id: protocol.c,v 1.10 1999/08/20 11:05:55 davem Exp $ + * Version: $Id: protocol.c,v 1.11 2000/02/22 23:54:26 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -35,6 +35,7 @@ #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/timer.h> +#include <linux/brlock.h> #include <net/ip.h> #include <net/protocol.h> #include <net/tcp.h> @@ -116,8 +117,6 @@ struct inet_protocol *inet_protos[MAX_INET_PROTOS] = NULL }; -rwlock_t inet_protocol_lock = RW_LOCK_UNLOCKED; - /* * Add a protocol handler to the hash tables */ @@ -128,7 +127,7 @@ void inet_add_protocol(struct inet_protocol *prot) struct inet_protocol *p2; hash = prot->protocol & (MAX_INET_PROTOS - 1); - write_lock_bh(&inet_protocol_lock); + br_write_lock_bh(BR_NETPROTO_LOCK); prot ->next = inet_protos[hash]; inet_protos[hash] = prot; prot->copy = 0; @@ -147,7 +146,7 @@ void inet_add_protocol(struct inet_protocol *prot) } p2 = (struct inet_protocol *) p2->next; } - write_unlock_bh(&inet_protocol_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); } /* @@ -161,11 +160,11 @@ int inet_del_protocol(struct inet_protocol *prot) unsigned char hash; hash = prot->protocol & (MAX_INET_PROTOS - 1); - write_lock_bh(&inet_protocol_lock); + br_write_lock_bh(BR_NETPROTO_LOCK); if (prot == inet_protos[hash]) { inet_protos[hash] = (struct inet_protocol *) inet_protos[hash]->next; - write_unlock_bh(&inet_protocol_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); return(0); } @@ -186,7 +185,7 @@ int inet_del_protocol(struct inet_protocol *prot) if (p->copy == 0 && lp != NULL) lp->copy = 0; p->next = prot->next; - write_unlock_bh(&inet_protocol_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); return(0); } if (p->next != NULL && p->next->protocol == prot->protocol) @@ -194,6 +193,6 @@ int inet_del_protocol(struct inet_protocol *prot) p = (struct inet_protocol *) p->next; } - write_unlock_bh(&inet_protocol_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); return(-1); } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 88483d516..d431c682c 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.188 2000/02/08 21:27:14 davem Exp $ + * Version: $Id: tcp_input.c,v 1.189 2000/02/27 19:52:55 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -534,6 +534,9 @@ static void tcp_reset(struct sock *sk) sk->err = ECONNRESET; } + if (!sk->dead) + sk->error_report(sk); + tcp_done(sk); } @@ -1660,7 +1663,12 @@ static void tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th) if (!sk->dead) { sk->state_change(sk); - sock_wake_async(sk->socket, 1, POLL_HUP); + + /* Do not send POLL_HUP for half duplex close. */ + if (sk->shutdown == SHUTDOWN_MASK || sk->state == TCP_CLOSE) + sock_wake_async(sk->socket, 1, POLL_HUP); + else + sock_wake_async(sk->socket, 1, POLL_IN); } } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index c052d2eb8..e188b4997 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -5,7 +5,7 @@ * * The User Datagram Protocol (UDP). * - * Version: $Id: udp.c,v 1.79 2000/01/18 08:24:20 davem Exp $ + * Version: $Id: udp.c,v 1.80 2000/02/27 19:51:43 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -798,6 +798,7 @@ int udp_disconnect(struct sock *sk, int flags) sk->rcv_saddr = 0; sk->daddr = 0; sk->dport = 0; + sk->bound_dev_if = 0; sk_dst_reset(sk); return 0; } diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 09b9c1a11..919abf4f9 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -7,7 +7,10 @@ * * Adapted from linux/net/ipv4/af_inet.c * - * $Id: af_inet6.c,v 1.54 2000/02/12 23:34:45 davem Exp $ + * $Id: af_inet6.c,v 1.55 2000/02/27 19:51:47 davem Exp $ + * + * Fixes: + * Hideaki YOSHIFUJI : sin6_scope_id support * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -220,9 +223,8 @@ static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if(sk->prot->bind) return sk->prot->bind(sk, uaddr, addr_len); - if (addr_len < sizeof(struct sockaddr_in6)) + if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; - addr_type = ipv6_addr_type(&addr->sin6_addr); if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM) return -EINVAL; @@ -258,6 +260,22 @@ static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) return -EINVAL; } + if (addr_type & IPV6_ADDR_LINKLOCAL) { + if (addr_len >= sizeof(struct sockaddr_in6) && + addr->sin6_scope_id) { + /* Override any existing binding, if another one + * is supplied by user. + */ + sk->bound_dev_if = addr->sin6_scope_id; + } + + /* Binding to link-local address requires an interface */ + if (sk->bound_dev_if == 0) { + release_sock(sk); + return -EINVAL; + } + } + sk->rcv_saddr = v4addr; sk->saddr = v4addr; @@ -338,6 +356,7 @@ static int inet6_getname(struct socket *sock, struct sockaddr *uaddr, sin->sin6_family = AF_INET6; sin->sin6_flowinfo = 0; + sin->sin6_scope_id = 0; if (peer) { if (!sk->dport) return -ENOTCONN; @@ -360,7 +379,9 @@ static int inet6_getname(struct socket *sock, struct sockaddr *uaddr, sin->sin6_port = sk->sport; } - *uaddr_len = sizeof(*sin); + if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) + sin->sin6_scope_id = sk->bound_dev_if; + *uaddr_len = sizeof(*sin); return(0); } diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index c561d318d..844ea8228 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.18 1999/08/20 11:06:17 davem Exp $ + * $Id: datagram.c,v 1.19 2000/02/27 19:51:47 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -134,14 +134,20 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) sin->sin6_family = AF_INET6; sin->sin6_flowinfo = 0; sin->sin6_port = serr->port; + sin->sin6_scope_id = 0; if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) { memcpy(&sin->sin6_addr, skb->nh.raw + serr->addr_offset, 16); if (sk->net_pinfo.af_inet6.sndflow) sin->sin6_flowinfo = *(u32*)(skb->nh.raw + serr->addr_offset - 24) & IPV6_FLOWINFO_MASK; - } else + if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) { + struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; + sin->sin6_scope_id = opt->iif; + } + } else { ipv6_addr_set(&sin->sin6_addr, 0, 0, __constant_htonl(0xffff), *(u32*)(skb->nh.raw + serr->addr_offset)); + } } memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err)); @@ -154,6 +160,10 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) memcpy(&sin->sin6_addr, &skb->nh.ipv6h->saddr, 16); if (sk->net_pinfo.af_inet6.rxopt.all) datagram_recv_ctl(sk, msg, skb); + if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) { + struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; + sin->sin6_scope_id = opt->iif; + } } else { ipv6_addr_set(&sin->sin6_addr, 0, 0, __constant_htonl(0xffff), diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index f1c211532..ffb0787e8 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: icmp.c,v 1.26 2000/01/19 04:06:19 davem Exp $ + * $Id: icmp.c,v 1.27 2000/02/22 23:54:28 davem Exp $ * * Based on net/ipv4/icmp.c * @@ -477,7 +477,6 @@ static void icmpv6_notify(struct sk_buff *skb, hash = nexthdr & (MAX_INET_PROTOS - 1); - read_lock(&inet6_protocol_lock); for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; ipprot != NULL; ipprot=(struct inet6_protocol *)ipprot->next) { @@ -487,7 +486,6 @@ static void icmpv6_notify(struct sk_buff *skb, if (ipprot->err_handler) ipprot->err_handler(skb, hdr, NULL, type, code, pb, info); } - read_unlock(&inet6_protocol_lock); read_lock(&raw_v6_lock); if ((sk = raw_v6_htable[hash]) != NULL) { diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 709443749..6c6ae227f 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -6,7 +6,7 @@ * Pedro Roque <roque@di.fc.ul.pt> * Ian P. Morris <I.P.Morris@soton.ac.uk> * - * $Id: ip6_input.c,v 1.15 2000/01/09 02:19:54 davem Exp $ + * $Id: ip6_input.c,v 1.17 2000/02/27 19:42:53 davem Exp $ * * Based in linux/net/ipv4/ip_input.c * @@ -26,6 +26,9 @@ #include <linux/in6.h> #include <linux/icmpv6.h> +#include <linux/netfilter.h> +#include <linux/netfilter_ipv6.h> + #include <net/sock.h> #include <net/snmp.h> @@ -38,6 +41,16 @@ #include <net/addrconf.h> + +static inline int ip6_rcv_finish( struct sk_buff *skb) +{ + + if (skb->dst == NULL) + ip6_route_input(skb); + + return skb->dst->input(skb); +} + int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) { struct ipv6hdr *hdr; @@ -77,12 +90,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt return 0; } } - - if (skb->dst == NULL) - ip6_route_input(skb); - - return skb->dst->input(skb); - + return NF_HOOK(PF_INET6,NF_IP6_PRE_ROUTING, skb, dev, NULL, ip6_rcv_finish); truncated: IP6_INC_STATS_BH(Ip6InTruncatedPkts); err: @@ -97,7 +105,8 @@ out: * Deliver the packet to the host */ -int ip6_input(struct sk_buff *skb) + +static inline int ip6_input_finish(struct sk_buff *skb) { struct ipv6hdr *hdr = skb->nh.ipv6h; struct inet6_protocol *ipprot; @@ -147,7 +156,6 @@ int ip6_input(struct sk_buff *skb) raw_sk = ipv6_raw_deliver(skb, nexthdr, len); hash = nexthdr & (MAX_INET_PROTOS - 1); - read_lock(&inet6_protocol_lock); for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; ipprot != NULL; ipprot = (struct inet6_protocol *) ipprot->next) { @@ -163,7 +171,6 @@ int ip6_input(struct sk_buff *skb) ipprot->handler(buff, len); found = 1; } - read_unlock(&inet6_protocol_lock); if (raw_sk) { rawv6_rcv(raw_sk, skb, len); @@ -182,6 +189,12 @@ int ip6_input(struct sk_buff *skb) return 0; } + +int ip6_input(struct sk_buff *skb) +{ + return NF_HOOK(PF_INET6,NF_IP6_LOCAL_IN, skb, skb->dev, NULL, ip6_input_finish); +} + int ip6_mc_input(struct sk_buff *skb) { struct ipv6hdr *hdr; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index d902692bd..4d124d558 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ip6_output.c,v 1.24 2000/01/09 02:19:49 davem Exp $ + * $Id: ip6_output.c,v 1.26 2000/03/01 02:58:12 davem Exp $ * * Based on linux/net/ipv4/ip_output.c * @@ -24,6 +24,7 @@ * H. von Brand : Added missing #include <linux/string.h> */ +#include <linux/config.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/string.h> @@ -34,6 +35,9 @@ #include <linux/in6.h> #include <linux/route.h> +#include <linux/netfilter.h> +#include <linux/netfilter_ipv6.h> + #include <net/sock.h> #include <net/snmp.h> @@ -57,11 +61,44 @@ static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *f spin_unlock_bh(&ip6_id_lock); } +static inline int ip6_output_finish(struct sk_buff *skb) +{ + + struct dst_entry *dst = skb->dst; + struct hh_cache *hh = dst->hh; + + if (hh) { + read_lock_bh(&hh->hh_lock); + memcpy(skb->data - 16, hh->hh_data, 16); + read_unlock_bh(&hh->hh_lock); + skb_push(skb, hh->hh_len); + return hh->hh_output(skb); + } else if (dst->neighbour) + return dst->neighbour->output(skb); + + kfree_skb(skb); + return -EINVAL; + +} + +/* dev_loopback_xmit for use with netfilter. */ +static int ip6_dev_loopback_xmit(struct sk_buff *newskb) +{ + newskb->mac.raw = newskb->data; + skb_pull(newskb, newskb->nh.raw - newskb->data); + newskb->pkt_type = PACKET_LOOPBACK; + newskb->ip_summed = CHECKSUM_UNNECESSARY; + BUG_TRAP(newskb->dst); + + netif_rx(newskb); + return 0; +} + + int ip6_output(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; struct net_device *dev = dst->dev; - struct hh_cache *hh = dst->hh; skb->protocol = __constant_htons(ETH_P_IPV6); skb->dev = dev; @@ -70,10 +107,15 @@ int ip6_output(struct sk_buff *skb) if (!(dev->flags&IFF_LOOPBACK) && (skb->sk == NULL || skb->sk->net_pinfo.af_inet6.mc_loop) && ipv6_chk_mcast_addr(dev, &skb->nh.ipv6h->daddr)) { + struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); + /* Do not check for IFF_ALLMULTI; multicast routing is not supported in any case. */ - dev_loopback_xmit(skb); + if (newskb) + NF_HOOK(PF_INET, NF_IP6_POST_ROUTING, newskb, NULL, + newskb->dev, + ip6_dev_loopback_xmit); if (skb->nh.ipv6h->hop_limit == 0) { kfree_skb(skb); @@ -84,17 +126,51 @@ int ip6_output(struct sk_buff *skb) IP6_INC_STATS(Ip6OutMcastPkts); } - if (hh) { - read_lock_bh(&hh->hh_lock); - memcpy(skb->data - 16, hh->hh_data, 16); - read_unlock_bh(&hh->hh_lock); - skb_push(skb, hh->hh_len); - return hh->hh_output(skb); - } else if (dst->neighbour) - return dst->neighbour->output(skb); + return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish); +} - kfree_skb(skb); - return -EINVAL; + +#ifdef CONFIG_NETFILTER +static int route6_me_harder(struct sk_buff *skb) +{ + struct ipv6hdr *iph = skb->nh.ipv6h; + struct dst_entry *dst; + struct flowi fl; + + fl.proto = iph->nexthdr; + fl.fl6_dst = &iph->daddr; + fl.fl6_src = &iph->saddr; + fl.oif = skb->sk ? skb->sk->bound_dev_if : 0; + fl.fl6_flowlabel = 0; + fl.uli_u.ports.dport = 0; + fl.uli_u.ports.sport = 0; + + dst = ip6_route_output(skb->sk, &fl); + + if (dst->error) { + printk(KERN_DEBUG "route6_me_harder: No more route.\n"); + return -EINVAL; + } + + /* Drop old route. */ + dst_release(skb->dst); + + skb->dst = dst; + return 0; +} +#endif + +static inline int ip6_maybe_reroute(struct sk_buff *skb) +{ +#ifdef CONFIG_NETFILTER + if (skb->nfcache & NFC_ALTERED){ + if (route6_me_harder(skb) != 0){ + kfree_skb(skb); + return -EINVAL; + } + } +#endif /* CONFIG_NETFILTER */ + return skb->dst->output(skb); } /* @@ -159,7 +235,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, if (skb->len <= dst->pmtu) { IP6_INC_STATS(Ip6OutRequests); - return dst->output(skb); + return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); } printk(KERN_DEBUG "IPv6: sending pkt_too_big to self\n"); @@ -388,7 +464,7 @@ static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag, IP6_INC_STATS(Ip6FragCreates); IP6_INC_STATS(Ip6OutRequests); - err = dst->output(skb); + err = NF_HOOK(PF_INET6,NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); if (err) { kfree_skb(last_skb); return err; @@ -414,7 +490,7 @@ static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag, IP6_INC_STATS(Ip6FragCreates); IP6_INC_STATS(Ip6FragOKs); IP6_INC_STATS(Ip6OutRequests); - return dst->output(last_skb); + return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, last_skb, NULL,dst->dev, ip6_maybe_reroute); } int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data, @@ -582,7 +658,7 @@ int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data, if (!err) { IP6_INC_STATS(Ip6OutRequests); - err = dst->output(skb); + err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); } else { err = -EFAULT; kfree_skb(skb); @@ -636,6 +712,11 @@ int ip6_call_ra_chain(struct sk_buff *skb, int sel) return 0; } +static inline int ip6_forward_finish(struct sk_buff *skb) +{ + return skb->dst->output(skb); +} + int ip6_forward(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; @@ -726,7 +807,7 @@ int ip6_forward(struct sk_buff *skb) hdr->hop_limit--; IP6_INC_STATS_BH(Ip6OutForwDatagrams); - return dst->output(skb); + return NF_HOOK(PF_INET6,NF_IP6_FORWARD, skb, skb->dev, dst->dev, ip6_forward_finish); drop: IP6_INC_STATS_BH(Ip6InAddrErrors); diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 9f13435fa..87c9f1eb4 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -7,7 +7,7 @@ * * Based on linux/net/ipv4/ip_sockglue.c * - * $Id: ipv6_sockglue.c,v 1.32 2000/01/31 01:21:25 davem Exp $ + * $Id: ipv6_sockglue.c,v 1.33 2000/02/27 19:42:54 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,6 +35,7 @@ #include <linux/if_arp.h> #include <linux/init.h> #include <linux/sysctl.h> +#include <linux/netfilter.h> #include <net/sock.h> #include <net/snmp.h> @@ -371,6 +372,14 @@ done: case IPV6_FLOWLABEL_MGR: retv = ipv6_flowlabel_opt(sk, optval, optlen); break; + +#ifdef CONFIG_NETFILTER + default: + retv = nf_setsockopt(sk, PF_INET6, optname, optval, + optlen); + break; +#endif + } release_sock(sk); @@ -450,7 +459,17 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval, break; } default: +#ifdef CONFIG_NETFILTER + lock_sock(sk); + val = nf_getsockopt(sk, PF_INET6, optname, optval, + &len); + release_sock(sk); + if (val >= 0) + val = put_user(len, optlen); + return val; +#else return -EINVAL; +#endif } len=min(sizeof(int),len); if(put_user(len, optlen)) diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c index c30e751ed..accc87cbe 100644 --- a/net/ipv6/protocol.c +++ b/net/ipv6/protocol.c @@ -5,7 +5,7 @@ * * PF_INET6 protocol dispatch tables. * - * Version: $Id: protocol.c,v 1.7 1999/08/20 11:06:26 davem Exp $ + * Version: $Id: protocol.c,v 1.8 2000/02/22 23:54:29 davem Exp $ * * Authors: Pedro Roque <roque@di.fc.ul.pt> * @@ -24,6 +24,7 @@ #include <linux/in6.h> #include <linux/netdevice.h> #include <linux/if_arp.h> +#include <linux/brlock.h> #include <net/sock.h> #include <net/snmp.h> @@ -37,15 +38,13 @@ struct inet6_protocol *inet6_protos[MAX_INET_PROTOS] = NULL }; -rwlock_t inet6_protocol_lock = RW_LOCK_UNLOCKED; - void inet6_add_protocol(struct inet6_protocol *prot) { unsigned char hash; struct inet6_protocol *p2; hash = prot->protocol & (MAX_INET_PROTOS - 1); - write_lock_bh(&inet6_protocol_lock); + br_write_lock_bh(BR_NETPROTO_LOCK); prot->next = inet6_protos[hash]; inet6_protos[hash] = prot; prot->copy = 0; @@ -62,7 +61,7 @@ void inet6_add_protocol(struct inet6_protocol *prot) } p2 = (struct inet6_protocol *) p2->next; } - write_unlock_bh(&inet6_protocol_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); } /* @@ -76,10 +75,10 @@ int inet6_del_protocol(struct inet6_protocol *prot) unsigned char hash; hash = prot->protocol & (MAX_INET_PROTOS - 1); - write_lock_bh(&inet6_protocol_lock); + br_write_lock_bh(BR_NETPROTO_LOCK); if (prot == inet6_protos[hash]) { inet6_protos[hash] = (struct inet6_protocol *) inet6_protos[hash]->next; - write_unlock_bh(&inet6_protocol_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); return(0); } @@ -98,7 +97,7 @@ int inet6_del_protocol(struct inet6_protocol *prot) if (p->copy == 0 && lp != NULL) lp->copy = 0; p->next = prot->next; - write_unlock_bh(&inet6_protocol_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); return(0); } if (p->next != NULL && p->next->protocol == prot->protocol) @@ -106,6 +105,6 @@ int inet6_del_protocol(struct inet6_protocol *prot) p = (struct inet6_protocol *) p->next; } - write_unlock_bh(&inet6_protocol_lock); + br_write_unlock_bh(BR_NETPROTO_LOCK); return(-1); } diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 574bc165c..bb4ecb551 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -7,7 +7,10 @@ * * Adapted from linux/net/ipv4/raw.c * - * $Id: raw.c,v 1.33 2000/01/18 08:24:22 davem Exp $ + * $Id: raw.c,v 1.34 2000/02/27 19:51:48 davem Exp $ + * + * Fixes: + * Hideaki YOSHIFUJI : sin6_scope_id support * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -183,9 +186,8 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) int addr_type; int err; - if (addr_len < sizeof(struct sockaddr_in6)) + if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; - addr_type = ipv6_addr_type(&addr->sin6_addr); /* Raw sockets are IPv6 only */ @@ -198,6 +200,20 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (sk->state != TCP_CLOSE) goto out; + if (addr_type & IPV6_ADDR_LINKLOCAL) { + if (addr_len >= sizeof(struct sockaddr_in6) && + addr->sin6_scope_id) { + /* Override any existing binding, if another one + * is supplied by user. + */ + sk->bound_dev_if = addr->sin6_scope_id; + } + + /* Binding to link-local address requires an interface */ + if (sk->bound_dev_if == 0) + goto out; + } + /* Check if the address belongs to the host. */ if (addr_type != IPV6_ADDR_ANY) { /* ipv4 addr of the socket is invalid. Only the @@ -325,6 +341,11 @@ int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr, sizeof(struct in6_addr)); sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; + if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { + struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; + sin6->sin6_scope_id = opt->iif; + } } if (sk->net_pinfo.af_inet6.rxopt.all) @@ -429,14 +450,15 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len) */ fl.fl6_flowlabel = 0; + fl.oif = 0; if (sin6) { - if (addr_len < sizeof(struct sockaddr_in6)) - return(-EINVAL); + if (addr_len < SIN6_LEN_RFC2133) + return -EINVAL; if (sin6->sin6_family && sin6->sin6_family != AF_INET6) return(-EINVAL); - + /* port is the proto value [0..255] carried in nexthdr */ proto = ntohs(sin6->sin6_port); @@ -457,11 +479,15 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len) } } - /* Otherwise it will be difficult to maintain sk->dst_cache. */ if (sk->state == TCP_ESTABLISHED && !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr)) daddr = &sk->net_pinfo.af_inet6.daddr; + + if (addr_len >= sizeof(struct sockaddr_in6) && + sin6->sin6_scope_id && + ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL) + fl.oif = sin6->sin6_scope_id; } else { if (sk->state != TCP_ESTABLISHED) return(-EINVAL); @@ -479,7 +505,8 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len) return(-EINVAL); } - fl.oif = sk->bound_dev_if; + if (fl.oif == 0) + fl.oif = sk->bound_dev_if; fl.fl6_src = NULL; if (msg->msg_controllen) { diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 47dcf8ce0..f47b4a103 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,13 +5,16 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: tcp_ipv6.c,v 1.119 2000/01/31 01:21:26 davem Exp $ + * $Id: tcp_ipv6.c,v 1.120 2000/02/27 19:51:49 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c * linux/net/ipv4/tcp_input.c * linux/net/ipv4/tcp_output.c * + * Fixes: + * Hideaki YOSHIFUJI : sin6_scope_id support + * * 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 @@ -509,8 +512,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_type; int err; - if (addr_len < sizeof(struct sockaddr_in6)) - return(-EINVAL); + if (addr_len < SIN6_LEN_RFC2133) + return -EINVAL; if (usin->sin6_family != AF_INET6) return(-EAFNOSUPPORT); @@ -540,6 +543,24 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, if(addr_type & IPV6_ADDR_MULTICAST) return -ENETUNREACH; + if (addr_type&IPV6_ADDR_LINKLOCAL) { + if (addr_len >= sizeof(struct sockaddr_in6) && + usin->sin6_scope_id) { + /* If interface is set while binding, indices + * must coincide. + */ + if (sk->bound_dev_if && + sk->bound_dev_if != usin->sin6_scope_id) + return -EINVAL; + + sk->bound_dev_if = usin->sin6_scope_id; + } + + /* Connect to link-local address requires an interface */ + if (sk->bound_dev_if == 0) + return -EINVAL; + } + if (tp->ts_recent_stamp && ipv6_addr_cmp(&np->daddr, &usin->sin6_addr)) { tp->ts_recent = 0; tp->ts_recent_stamp = 0; @@ -605,15 +626,6 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, goto failure; } - if (fl.oif == 0 && addr_type&IPV6_ADDR_LINKLOCAL) { - /* Ough! This guy tries to connect to link local - * address and did not specify interface. - * Actually we should kick him out, but - * we will be patient :) --ANK - */ - sk->bound_dev_if = dst->dev->ifindex; - } - ip6_dst_store(sk, dst, NULL); if (saddr == NULL) { @@ -1723,6 +1735,9 @@ static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr) sin6->sin6_port = sk->dport; /* We do not store received flowlabel for TCP */ sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; + if (sk->bound_dev_if && ipv6_addr_type(&sin6->sin6_addr)&IPV6_ADDR_LINKLOCAL) + sin6->sin6_scope_id = sk->bound_dev_if; } static int tcp_v6_remember_stamp(struct sock *sk) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index a5984354b..fed8e3aa2 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -7,7 +7,10 @@ * * Based on linux/ipv4/udp.c * - * $Id: udp.c,v 1.50 2000/01/18 08:24:24 davem Exp $ + * $Id: udp.c,v 1.51 2000/02/27 19:51:51 davem Exp $ + * + * Fixes: + * Hideaki YOSHIFUJI : sin6_scope_id support * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -218,7 +221,7 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) goto ipv4_connected; } - if (addr_len < sizeof(*usin)) + if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; if (usin->sin6_family != AF_INET6) @@ -278,6 +281,21 @@ ipv4_connected: return 0; } + if (addr_type&IPV6_ADDR_LINKLOCAL) { + if (addr_len >= sizeof(struct sockaddr_in6) && + usin->sin6_scope_id) { + if (sk->bound_dev_if && sk->bound_dev_if != usin->sin6_scope_id) { + fl6_sock_release(flowlabel); + return -EINVAL; + } + sk->bound_dev_if = usin->sin6_scope_id; + } + + /* Connect to link-local address requires an interface */ + if (sk->bound_dev_if == 0) + return -EINVAL; + } + ipv6_addr_copy(&np->daddr, daddr); np->flow_label = fl.fl6_flowlabel; @@ -392,6 +410,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, sin6->sin6_family = AF_INET6; sin6->sin6_port = skb->h.uh->source; sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; if (skb->protocol == __constant_htons(ETH_P_IP)) { ipv6_addr_set(&sin6->sin6_addr, 0, 0, @@ -404,6 +423,10 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, if (sk->net_pinfo.af_inet6.rxopt.all) datagram_recv_ctl(sk, msg, skb); + if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { + struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; + sin6->sin6_scope_id = opt->iif; + } } } err = copied; @@ -746,12 +769,13 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen) return -EMSGSIZE; fl.fl6_flowlabel = 0; + fl.oif = 0; if (sin6) { if (sin6->sin6_family == AF_INET) return udp_sendmsg(sk, msg, ulen); - if (addr_len < sizeof(*sin6)) + if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; if (sin6->sin6_family && sin6->sin6_family != AF_INET6) @@ -777,6 +801,11 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen) if (sk->state == TCP_ESTABLISHED && !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr)) daddr = &sk->net_pinfo.af_inet6.daddr; + + if (addr_len >= sizeof(struct sockaddr_in6) && + sin6->sin6_scope_id && + ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL) + fl.oif = sin6->sin6_scope_id; } else { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; @@ -802,7 +831,8 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen) } udh.daddr = NULL; - fl.oif = sk->bound_dev_if; + if (!fl.oif) + fl.oif = sk->bound_dev_if; fl.fl6_src = NULL; if (msg->msg_controllen) { diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index 60427ef6e..9db90b0b8 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -2352,13 +2352,10 @@ static struct proto_ops SOCKOPS_WRAPPED(ipx_dgram_ops) = { #include <linux/smp_lock.h> SOCKOPS_WRAP(ipx_dgram, PF_IPX); - -/* Called by protocol.c on kernel start up */ - static struct packet_type ipx_8023_packet_type = { - 0, /* MUTTER ntohs(ETH_P_802_3),*/ + __constant_htons(ETH_P_802_3), NULL, /* All devices */ ipx_rcv, NULL, @@ -2367,7 +2364,7 @@ static struct packet_type ipx_8023_packet_type = static struct packet_type ipx_dix_packet_type = { - 0, /* MUTTER ntohs(ETH_P_IPX),*/ + __constant_htons(ETH_P_IPX), NULL, /* All devices */ ipx_rcv, NULL, @@ -2389,16 +2386,18 @@ extern void destroy_8023_client(struct datalink_proto *); static unsigned char ipx_8022_type = 0xE0; static unsigned char ipx_snap_id[5] = { 0x0, 0x0, 0x0, 0x81, 0x37 }; + + +/* Called by protocols.c on kernel start up */ + void ipx_proto_init(struct net_proto *pro) { (void) sock_register(&ipx_family_ops); pEII_datalink = make_EII_client(); - ipx_dix_packet_type.type = htons(ETH_P_IPX); dev_add_pack(&ipx_dix_packet_type); p8023_datalink = make_8023_client(); - ipx_8023_packet_type.type = htons(ETH_P_802_3); dev_add_pack(&ipx_8023_packet_type); if((p8022_datalink = register_8022_client(ipx_8022_type,ipx_rcv)) == NULL) diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c index 94345259d..9968e0e95 100644 --- a/net/irda/irda_device.c +++ b/net/irda/irda_device.c @@ -46,6 +46,7 @@ #include <asm/segment.h> #include <asm/uaccess.h> #include <asm/dma.h> +#include <asm/io.h> #include <net/pkt_sched.h> diff --git a/net/irda/iriap_event.c b/net/irda/iriap_event.c index eb8463b73..c073f5baf 100644 --- a/net/irda/iriap_event.c +++ b/net/irda/iriap_event.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Thu Aug 21 00:02:07 1997 - * Modified at: Sat Dec 25 21:09:47 1999 + * Modified at: Wed Mar 1 11:28:34 2000 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>, + * Copyright (c) 1997, 1999-2000 Dag Brattli <dagb@cs.uit.no>, * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -294,7 +294,7 @@ static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event, switch (event) { case IAP_RECV_F_LST: - iriap_send_ack(self); + /*iriap_send_ack(self);*/ /*LM_Idle_request(idle); */ iriap_next_call_state(self, S_WAIT_FOR_CALL); diff --git a/net/irda/parameters.c b/net/irda/parameters.c index 19f76eae4..263c2e2a4 100644 --- a/net/irda/parameters.c +++ b/net/irda/parameters.c @@ -356,21 +356,23 @@ int irda_param_pack(__u8 *buf, char *fmt, ...) for (p = fmt; *p != '\0'; p++) { switch (*p) { case 'b': /* 8 bits unsigned byte */ - buf[n++] = va_arg(args, __u8); + buf[n++] = (__u8)va_arg(args, int); break; case 's': /* 16 bits unsigned short */ - arg.s = va_arg(args, __u16); + arg.s = (__u16)va_arg(args, int); put_unaligned(arg.s, (__u16 *)(buf+n)); n+=2; break; case 'i': /* 32 bits unsigned integer */ arg.i = va_arg(args, __u32); put_unaligned(arg.i, (__u32 *)(buf+n)); n+=4; break; +#if 0 case 'c': /* \0 terminated string */ arg.c = va_arg(args, char *); strcpy(buf+n, arg.c); n += strlen(arg.c) + 1; break; +#endif default: va_end(args); return -1; diff --git a/net/netsyms.c b/net/netsyms.c index d03c0a1b1..16ff31dd0 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -502,6 +502,7 @@ EXPORT_SYMBOL(dev_remove_pack); EXPORT_SYMBOL(dev_get); EXPORT_SYMBOL(dev_alloc); EXPORT_SYMBOL(dev_alloc_name); +EXPORT_SYMBOL(__netdev_watchdog_up); #ifdef CONFIG_KMOD EXPORT_SYMBOL(dev_load); #endif @@ -580,10 +581,9 @@ EXPORT_SYMBOL(nf_register_hook); EXPORT_SYMBOL(nf_unregister_hook); EXPORT_SYMBOL(nf_register_sockopt); EXPORT_SYMBOL(nf_unregister_sockopt); -EXPORT_SYMBOL(nf_getinfo); EXPORT_SYMBOL(nf_reinject); -EXPORT_SYMBOL(nf_register_interest); -EXPORT_SYMBOL(nf_unregister_interest); +EXPORT_SYMBOL(nf_register_queue_handler); +EXPORT_SYMBOL(nf_unregister_queue_handler); EXPORT_SYMBOL(nf_hook_slow); EXPORT_SYMBOL(nf_hooks); #endif diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 8ee8ab54f..d0c0539aa 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -146,16 +146,17 @@ static void dev_watchdog(unsigned long arg) spin_lock(&dev->xmit_lock); if (dev->qdisc != &noop_qdisc) { - if (netif_queue_stopped(dev) && - (jiffies - dev->trans_start) > dev->watchdog_timeo) { - printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", dev->name); - dev->tx_timeout(dev); + if (netif_device_present(dev) && + netif_running(dev) && + netif_carrier_ok(dev)) { + if (netif_queue_stopped(dev) && + (jiffies - dev->trans_start) > dev->watchdog_timeo) { + printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", dev->name); + dev->tx_timeout(dev); + } + if (!mod_timer(&dev->watchdog_timer, jiffies + dev->watchdog_timeo)) + dev_hold(dev); } - if (!del_timer(&dev->watchdog_timer)) - dev_hold(dev); - - dev->watchdog_timer.expires = jiffies + dev->watchdog_timeo; - add_timer(&dev->watchdog_timer); } spin_unlock(&dev->xmit_lock); @@ -169,33 +170,31 @@ static void dev_watchdog_init(struct net_device *dev) dev->watchdog_timer.function = dev_watchdog; } -static void dev_watchdog_up(struct net_device *dev) +void __netdev_watchdog_up(struct net_device *dev) { - spin_lock_bh(&dev->xmit_lock); - if (dev->tx_timeout) { if (dev->watchdog_timeo <= 0) dev->watchdog_timeo = 5*HZ; - if (!del_timer(&dev->watchdog_timer)) + if (!mod_timer(&dev->watchdog_timer, jiffies + dev->watchdog_timeo)) dev_hold(dev); - dev->watchdog_timer.expires = jiffies + dev->watchdog_timeo; - add_timer(&dev->watchdog_timer); } +} + +static void dev_watchdog_up(struct net_device *dev) +{ + spin_lock_bh(&dev->xmit_lock); + __netdev_watchdog_up(dev); spin_unlock_bh(&dev->xmit_lock); } static void dev_watchdog_down(struct net_device *dev) { spin_lock_bh(&dev->xmit_lock); - - if (dev->tx_timeout) { - if (del_timer(&dev->watchdog_timer)) - __dev_put(dev); - } + if (del_timer(&dev->watchdog_timer)) + __dev_put(dev); spin_unlock_bh(&dev->xmit_lock); } - /* "NOOP" scheduler: the best scheduler, recommended for all interfaces under all circumstances. It is difficult to invent anything faster or cheaper. diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index 84deb1ce4..d8c117247 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -11,6 +11,7 @@ * Changes: * J Hadi Salim <hadi@nortel.com> 980914: computation fixes * Alexey Makarenko <makar@phoenix.kharkov.ua> 990814: qave on idle link was calculated incorrectly. + * J Hadi Salim <hadi@nortelnetworks.com> 980816: ECN support */ #include <linux/config.h> @@ -39,6 +40,9 @@ #include <net/sock.h> #include <net/pkt_sched.h> +#define RED_ECN_ECT 0x02 +#define RED_ECN_CE 0x01 + /* Random Early Detection (RED) algorithm. ======================================= @@ -138,6 +142,7 @@ struct red_sched_data u32 qth_max; /* Max average length threshold: A scaled */ u32 Rmask; u32 Scell_max; + unsigned char flags; char Wlog; /* log(W) */ char Plog; /* random number bits */ char Scell_log; @@ -149,8 +154,48 @@ struct red_sched_data u32 qR; /* Cached random number */ psched_time_t qidlestart; /* Start of idle period */ + struct tc_red_xstats st; }; +static int red_ecn_mark(struct sk_buff *skb) +{ + if (skb->nh.raw + 20 < skb->tail) + return 0; + + switch (skb->protocol) { + case __constant_htons(ETH_P_IP): + { + u8 tos = skb->nh.iph->tos; + + if (!(tos & RED_ECN_ECT)) + return 0; + + if (!(tos & RED_ECN_CE)) { + u32 check = skb->nh.iph->check; + + check += __constant_htons(0xFFFE); + skb->nh.iph->check = check + (check>>16); + + skb->nh.iph->tos = tos | RED_ECN_CE; + } + return 1; + } + + case __constant_htons(ETH_P_IPV6): + { + u32 label = *(u32*)skb->nh.raw; + + if (!(label & __constant_htonl(RED_ECN_ECT<<20))) + return 0; + label |= __constant_htonl(RED_ECN_CE<<20); + return 1; + } + + default: + return 0; + } +} + static int red_enqueue(struct sk_buff *skb, struct Qdisc* sch) { @@ -221,7 +266,9 @@ enqueue: sch->stats.backlog += skb->len; sch->stats.bytes += skb->len; sch->stats.packets++; - return 0; + return NET_XMIT_SUCCESS; + } else { + q->st.pdrop++; } kfree_skb(skb); sch->stats.drops++; @@ -231,10 +278,14 @@ enqueue: q->qcount = -1; sch->stats.overlimits++; mark: - kfree_skb(skb); - sch->stats.drops++; - return NET_XMIT_CN; + if (!(q->flags&TC_RED_ECN) || !red_ecn_mark(skb)) { + q->st.early++; + goto drop; + } + q->st.marked++; + goto enqueue; } + if (++q->qcount) { /* The formula used below causes questions. @@ -261,6 +312,11 @@ mark: } q->qR = net_random()&q->Rmask; goto enqueue; + +drop: + kfree_skb(skb); + sch->stats.drops++; + return NET_XMIT_CN; } static int @@ -300,6 +356,7 @@ red_drop(struct Qdisc* sch) if (skb) { sch->stats.backlog -= skb->len; sch->stats.drops++; + q->st.other++; kfree_skb(skb); return 1; } @@ -336,6 +393,7 @@ static int red_change(struct Qdisc *sch, struct rtattr *opt) ctl = RTA_DATA(tb[TCA_RED_PARMS-1]); sch_tree_lock(sch); + q->flags = ctl->flags; q->Wlog = ctl->Wlog; q->Plog = ctl->Plog; q->Rmask = ctl->Plog < 32 ? ((1<<ctl->Plog) - 1) : ~0UL; @@ -367,6 +425,15 @@ static int red_init(struct Qdisc* sch, struct rtattr *opt) #ifdef CONFIG_RTNETLINK +int red_copy_xstats(struct sk_buff *skb, struct tc_red_xstats *st) +{ + RTA_PUT(skb, TCA_XSTATS, sizeof(*st), st); + return 0; + +rtattr_failure: + return 1; +} + static int red_dump(struct Qdisc *sch, struct sk_buff *skb) { struct red_sched_data *q = (struct red_sched_data *)sch->data; @@ -382,9 +449,13 @@ static int red_dump(struct Qdisc *sch, struct sk_buff *skb) opt.Wlog = q->Wlog; opt.Plog = q->Plog; opt.Scell_log = q->Scell_log; + opt.flags = q->flags; RTA_PUT(skb, TCA_RED_PARMS, sizeof(opt), &opt); rta->rta_len = skb->tail - b; + if (red_copy_xstats(skb, &q->st)) + goto rtattr_failure; + return skb->len; rtattr_failure: diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 7b3c63e87..a57c2a06d 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -8,7 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: af_unix.c,v 1.88 2000/01/18 08:24:28 davem Exp $ + * Version: $Id: af_unix.c,v 1.89 2000/02/27 19:52:50 davem Exp $ * * Fixes: * Linus Torvalds : Assorted bug cures. @@ -1556,8 +1556,10 @@ static int unix_shutdown(struct socket *sock, int mode) other->shutdown |= peer_mode; unix_state_wunlock(other); other->state_change(other); - if (peer_mode&RCV_SHUTDOWN) - sock_wake_async(sk->socket,1,POLL_HUP); + if (peer_mode == SHUTDOWN_MASK) + sock_wake_async(other->socket,1,POLL_HUP); + else if (peer_mode & RCV_SHUTDOWN) + sock_wake_async(other->socket,1,POLL_IN); } if (other) sock_put(other); |