Commit cc0142de authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://kernel.bkbits.net/davem/net-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 00d412d2 cab9315c
...@@ -320,6 +320,21 @@ static inline void list_splice_init(struct list_head *list, ...@@ -320,6 +320,21 @@ static inline void list_splice_init(struct list_head *list,
for (pos = (head)->next, n = pos->next; pos != (head); \ for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next) pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
/**
* list_for_each_entry_rcu - iterate over rcu list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_rcu(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
prefetch(pos->member.next); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member), \
({ smp_read_barrier_depends(); 0;}), \
prefetch(pos->member.next))
/* /*
* Double linked lists with a single pointer list head. * Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is * Mostly useful for hash tables where the two pointer list head is
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#ifndef _NET_IF_INET6_H #ifndef _NET_IF_INET6_H
#define _NET_IF_INET6_H #define _NET_IF_INET6_H
#include <net/snmp.h>
#define IF_RA_RCVD 0x20 #define IF_RA_RCVD 0x20
#define IF_RS_SENT 0x10 #define IF_RS_SENT 0x10
...@@ -152,6 +154,11 @@ struct ipv6_devconf ...@@ -152,6 +154,11 @@ struct ipv6_devconf
void *sysctl; void *sysctl;
}; };
struct ipv6_devstat {
struct proc_dir_entry *proc_dir_entry;
DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6);
};
struct inet6_dev struct inet6_dev
{ {
struct net_device *dev; struct net_device *dev;
...@@ -185,6 +192,7 @@ struct inet6_dev ...@@ -185,6 +192,7 @@ struct inet6_dev
struct neigh_parms *nd_parms; struct neigh_parms *nd_parms;
struct inet6_dev *next; struct inet6_dev *next;
struct ipv6_devconf cnf; struct ipv6_devconf cnf;
struct ipv6_devstat stats;
}; };
extern struct ipv6_devconf ipv6_devconf; extern struct ipv6_devconf ipv6_devconf;
......
...@@ -106,24 +106,48 @@ struct frag_hdr { ...@@ -106,24 +106,48 @@ struct frag_hdr {
/* sysctls */ /* sysctls */
extern int sysctl_ipv6_bindv6only; extern int sysctl_ipv6_bindv6only;
/* MIBs */
DECLARE_SNMP_STAT(struct ipv6_mib, ipv6_statistics); DECLARE_SNMP_STAT(struct ipv6_mib, ipv6_statistics);
#define IP6_INC_STATS(field) SNMP_INC_STATS(ipv6_statistics, field) #define IP6_INC_STATS(field) SNMP_INC_STATS(ipv6_statistics, field)
#define IP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(ipv6_statistics, field) #define IP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(ipv6_statistics, field)
#define IP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(ipv6_statistics, field) #define IP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(ipv6_statistics, field)
DECLARE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics); DECLARE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics);
#define ICMP6_INC_STATS(field) SNMP_INC_STATS(icmpv6_statistics, field) #define ICMP6_INC_STATS(idev, field) ({ \
#define ICMP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(icmpv6_statistics, field) struct inet6_dev *_idev = (idev); \
#define ICMP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(icmpv6_statistics, field) if (likely(_idev != NULL)) \
#define ICMP6_STATS_PTR_BH(field) \ SNMP_INC_STATS(idev->stats.icmpv6, field); \
(& \ SNMP_INC_STATS(icmpv6_statistics, field); \
((per_cpu_ptr(icmpv6_statistics[0], smp_processor_id()))-> \ })
field)) #define ICMP6_INC_STATS_BH(idev, field) ({ \
struct inet6_dev *_idev = (idev); \
if (likely(_idev != NULL)) \
SNMP_INC_STATS_BH((_idev)->stats.icmpv6, field); \
SNMP_INC_STATS_BH(icmpv6_statistics, field); \
})
#define ICMP6_INC_STATS_USER(idev, field) ({ \
struct inet6_dev *_idev = (idev); \
if (likely(_idev != NULL)) \
SNMP_INC_STATS_USER(_idev->stats.icmpv6, field); \
SNMP_INC_STATS_USER(icmpv6_statistics, field); \
})
#define ICMP6_INC_STATS_OFFSET_BH(idev, field, offset) ({ \
struct inet6_dev *_idev = idev; \
__typeof__(offset) _offset = (offset); \
if (likely(_idev != NULL)) \
SNMP_INC_STATS_OFFSET_BH(_idev->stats.icmpv6, field, _offset); \
SNMP_INC_STATS_OFFSET_BH(icmpv6_statistics, field, _offset); \
})
DECLARE_SNMP_STAT(struct udp_mib, udp_stats_in6); DECLARE_SNMP_STAT(struct udp_mib, udp_stats_in6);
#define UDP6_INC_STATS(field) SNMP_INC_STATS(udp_stats_in6, field) #define UDP6_INC_STATS(field) SNMP_INC_STATS(udp_stats_in6, field)
#define UDP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(udp_stats_in6, field) #define UDP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(udp_stats_in6, field)
#define UDP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(udp_stats_in6, field) #define UDP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(udp_stats_in6, field)
extern atomic_t inet6_sock_nr; extern atomic_t inet6_sock_nr;
int snmp6_register_dev(struct inet6_dev *idev);
int snmp6_unregister_dev(struct inet6_dev *idev);
int snmp6_mib_init(void *ptr[2], size_t mibsize);
void snmp6_mib_free(void *ptr[2]);
struct ip6_ra_chain struct ip6_ra_chain
{ {
struct ip6_ra_chain *next; struct ip6_ra_chain *next;
......
...@@ -304,6 +304,8 @@ struct linux_mib ...@@ -304,6 +304,8 @@ struct linux_mib
#define SNMP_INC_STATS_BH(mib, field) \ #define SNMP_INC_STATS_BH(mib, field) \
(per_cpu_ptr(mib[0], smp_processor_id())->field++) (per_cpu_ptr(mib[0], smp_processor_id())->field++)
#define SNMP_INC_STATS_OFFSET_BH(mib, field, offset) \
((*((&per_cpu_ptr(mib[0], smp_processor_id())->field) + (offset)))++)
#define SNMP_INC_STATS_USER(mib, field) \ #define SNMP_INC_STATS_USER(mib, field) \
(per_cpu_ptr(mib[1], smp_processor_id())->field++) (per_cpu_ptr(mib[1], smp_processor_id())->field++)
#define SNMP_INC_STATS(mib, field) \ #define SNMP_INC_STATS(mib, field) \
......
...@@ -74,27 +74,20 @@ static int __br_dev_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -74,27 +74,20 @@ static int __br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
int br_dev_xmit(struct sk_buff *skb, struct net_device *dev) int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{ {
struct net_bridge *br;
int ret; int ret;
br = dev->priv; rcu_read_lock();
read_lock(&br->lock);
ret = __br_dev_xmit(skb, dev); ret = __br_dev_xmit(skb, dev);
read_unlock(&br->lock); rcu_read_unlock();
return ret; return ret;
} }
static int br_dev_open(struct net_device *dev) static int br_dev_open(struct net_device *dev)
{ {
struct net_bridge *br;
netif_start_queue(dev); netif_start_queue(dev);
br = dev->priv; br_stp_enable_bridge(dev->priv);
write_lock(&br->lock);
br_stp_enable_bridge(br);
write_unlock(&br->lock);
return 0; return 0;
} }
...@@ -105,12 +98,7 @@ static void br_dev_set_multicast_list(struct net_device *dev) ...@@ -105,12 +98,7 @@ static void br_dev_set_multicast_list(struct net_device *dev)
static int br_dev_stop(struct net_device *dev) static int br_dev_stop(struct net_device *dev)
{ {
struct net_bridge *br; br_stp_disable_bridge(dev->priv);
br = dev->priv;
write_lock(&br->lock);
br_stp_disable_bridge(br);
write_unlock(&br->lock);
netif_stop_queue(dev); netif_stop_queue(dev);
......
...@@ -21,7 +21,8 @@ ...@@ -21,7 +21,8 @@
#include <linux/netfilter_bridge.h> #include <linux/netfilter_bridge.h>
#include "br_private.h" #include "br_private.h"
static inline int should_deliver(struct net_bridge_port *p, struct sk_buff *skb) static inline int should_deliver(const struct net_bridge_port *p,
const struct sk_buff *skb)
{ {
if (skb->dev == p->dev || if (skb->dev == p->dev ||
p->state != BR_STATE_FORWARDING) p->state != BR_STATE_FORWARDING)
...@@ -52,7 +53,7 @@ int br_forward_finish(struct sk_buff *skb) ...@@ -52,7 +53,7 @@ int br_forward_finish(struct sk_buff *skb)
return 0; return 0;
} }
static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb) static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
{ {
skb->dev = to->dev; skb->dev = to->dev;
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
...@@ -62,7 +63,7 @@ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb) ...@@ -62,7 +63,7 @@ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
br_forward_finish); br_forward_finish);
} }
static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb) static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
{ {
struct net_device *indev; struct net_device *indev;
...@@ -73,8 +74,8 @@ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb) ...@@ -73,8 +74,8 @@ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
br_forward_finish); br_forward_finish);
} }
/* called under bridge lock */ /* called with rcu_read_lock */
void br_deliver(struct net_bridge_port *to, struct sk_buff *skb) void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
{ {
if (should_deliver(to, skb)) { if (should_deliver(to, skb)) {
__br_deliver(to, skb); __br_deliver(to, skb);
...@@ -84,8 +85,8 @@ void br_deliver(struct net_bridge_port *to, struct sk_buff *skb) ...@@ -84,8 +85,8 @@ void br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
kfree_skb(skb); kfree_skb(skb);
} }
/* called under bridge lock */ /* called with rcu_read_lock */
void br_forward(struct net_bridge_port *to, struct sk_buff *skb) void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
{ {
if (should_deliver(to, skb)) { if (should_deliver(to, skb)) {
__br_forward(to, skb); __br_forward(to, skb);
...@@ -97,7 +98,8 @@ void br_forward(struct net_bridge_port *to, struct sk_buff *skb) ...@@ -97,7 +98,8 @@ void br_forward(struct net_bridge_port *to, struct sk_buff *skb)
/* called under bridge lock */ /* called under bridge lock */
static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone, static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
void (*__packet_hook)(struct net_bridge_port *p, struct sk_buff *skb)) void (*__packet_hook)(const struct net_bridge_port *p,
struct sk_buff *skb))
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
struct net_bridge_port *prev; struct net_bridge_port *prev;
...@@ -115,8 +117,7 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone, ...@@ -115,8 +117,7 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
prev = NULL; prev = NULL;
p = br->port_list; list_for_each_entry_rcu(p, &br->port_list, list) {
while (p != NULL) {
if (should_deliver(p, skb)) { if (should_deliver(p, skb)) {
if (prev != NULL) { if (prev != NULL) {
struct sk_buff *skb2; struct sk_buff *skb2;
...@@ -132,8 +133,6 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone, ...@@ -132,8 +133,6 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
prev = p; prev = p;
} }
p = p->next;
} }
if (prev != NULL) { if (prev != NULL) {
...@@ -144,7 +143,8 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone, ...@@ -144,7 +143,8 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
kfree_skb(skb); kfree_skb(skb);
} }
/* called under bridge lock */
/* called with rcu_read_lock */
void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone) void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone)
{ {
br_flood(br, skb, clone, __br_deliver); br_flood(br, skb, clone, __br_deliver);
......
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
#include <linux/inetdevice.h> #include <linux/inetdevice.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/brlock.h>
#include <net/sock.h> #include <net/sock.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "br_private.h" #include "br_private.h"
...@@ -38,45 +38,39 @@ static int br_initial_port_cost(struct net_device *dev) ...@@ -38,45 +38,39 @@ static int br_initial_port_cost(struct net_device *dev)
return 100; return 100;
} }
/* called under BR_NETPROTO_LOCK and bridge lock */ static void destroy_nbp(void *arg)
static int __br_del_if(struct net_bridge *br, struct net_device *dev)
{ {
struct net_bridge_port *p; struct net_bridge_port *p = arg;
struct net_bridge_port **pptr; dev_put(p->dev);
kfree(p);
}
if ((p = dev->br_port) == NULL) /* called under bridge lock */
return -EINVAL; static void del_nbp(struct net_bridge_port *p)
{
struct net_device *dev = p->dev;
br_stp_disable_port(p); br_stp_disable_port(p);
dev_set_promiscuity(dev, -1); dev_set_promiscuity(dev, -1);
dev->br_port = NULL; dev->br_port = NULL;
pptr = &br->port_list; list_del_rcu(&p->list);
while (*pptr != NULL) {
if (*pptr == p) {
*pptr = p->next;
break;
}
pptr = &((*pptr)->next); br_fdb_delete_by_port(p->br, p);
}
br_fdb_delete_by_port(br, p); call_rcu(&p->rcu, destroy_nbp, p);
kfree(p);
dev_put(dev);
return 0;
} }
static void del_ifs(struct net_bridge *br) static void del_ifs(struct net_bridge *br)
{ {
br_write_lock_bh(BR_NETPROTO_LOCK); struct list_head *p, *n;
write_lock(&br->lock);
while (br->port_list != NULL) spin_lock_bh(&br->lock);
__br_del_if(br, br->port_list->dev); list_for_each_safe(p, n, &br->port_list) {
write_unlock(&br->lock); del_nbp(list_entry(p, struct net_bridge_port, list));
br_write_unlock_bh(BR_NETPROTO_LOCK); }
spin_unlock_bh(&br->lock);
} }
static struct net_bridge *new_nb(const char *name) static struct net_bridge *new_nb(const char *name)
...@@ -98,7 +92,8 @@ static struct net_bridge *new_nb(const char *name) ...@@ -98,7 +92,8 @@ static struct net_bridge *new_nb(const char *name)
ether_setup(dev); ether_setup(dev);
br_dev_setup(dev); br_dev_setup(dev);
br->lock = RW_LOCK_UNLOCKED; br->lock = SPIN_LOCK_UNLOCKED;
INIT_LIST_HEAD(&br->port_list);
br->hash_lock = RW_LOCK_UNLOCKED; br->hash_lock = RW_LOCK_UNLOCKED;
br->bridge_id.prio[0] = 0x80; br->bridge_id.prio[0] = 0x80;
...@@ -155,8 +150,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, struct net_device ...@@ -155,8 +150,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, struct net_device
br_init_port(p); br_init_port(p);
p->state = BR_STATE_DISABLED; p->state = BR_STATE_DISABLED;
p->next = br->port_list; list_add_rcu(&p->list, &br->port_list);
br->port_list = p;
return p; return p;
} }
...@@ -218,9 +212,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -218,9 +212,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
return -ELOOP; return -ELOOP;
dev_hold(dev); dev_hold(dev);
write_lock_bh(&br->lock); spin_lock_bh(&br->lock);
if ((p = new_nbp(br, dev)) == NULL) { if ((p = new_nbp(br, dev)) == NULL) {
write_unlock_bh(&br->lock); spin_unlock_bh(&br->lock);
dev_put(dev); dev_put(dev);
return -EXFULL; return -EXFULL;
} }
...@@ -231,21 +225,24 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -231,21 +225,24 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
br_fdb_insert(br, p, dev->dev_addr, 1); br_fdb_insert(br, p, dev->dev_addr, 1);
if ((br->dev.flags & IFF_UP) && (dev->flags & IFF_UP)) if ((br->dev.flags & IFF_UP) && (dev->flags & IFF_UP))
br_stp_enable_port(p); br_stp_enable_port(p);
write_unlock_bh(&br->lock); spin_unlock_bh(&br->lock);
return 0; return 0;
} }
int br_del_if(struct net_bridge *br, struct net_device *dev) int br_del_if(struct net_bridge *br, struct net_device *dev)
{ {
int retval; struct net_bridge_port *p;
int retval = 0;
br_write_lock_bh(BR_NETPROTO_LOCK); spin_lock_bh(&br->lock);
write_lock(&br->lock); if ((p = dev->br_port) == NULL || p->br != br)
retval = __br_del_if(br, dev); retval = -EINVAL;
br_stp_recalculate_bridge_id(br); else {
write_unlock(&br->lock); del_nbp(p);
br_write_unlock_bh(BR_NETPROTO_LOCK); br_stp_recalculate_bridge_id(br);
}
spin_unlock_bh(&br->lock);
return retval; return retval;
} }
...@@ -269,13 +266,11 @@ void br_get_port_ifindices(struct net_bridge *br, int *ifindices) ...@@ -269,13 +266,11 @@ void br_get_port_ifindices(struct net_bridge *br, int *ifindices)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
read_lock(&br->lock); rcu_read_lock();
p = br->port_list; list_for_each_entry_rcu(p, &br->port_list, list) {
while (p != NULL) {
ifindices[p->port_no] = p->dev->ifindex; ifindices[p->port_no] = p->dev->ifindex;
p = p->next;
} }
read_unlock(&br->lock); rcu_read_unlock();
} }
......
...@@ -59,15 +59,16 @@ int br_handle_frame_finish(struct sk_buff *skb) ...@@ -59,15 +59,16 @@ int br_handle_frame_finish(struct sk_buff *skb)
dest = skb->mac.ethernet->h_dest; dest = skb->mac.ethernet->h_dest;
rcu_read_lock();
p = skb->dev->br_port; p = skb->dev->br_port;
if (p == NULL) smp_read_barrier_depends();
goto err_nolock;
br = p->br; if (p == NULL || p->state == BR_STATE_DISABLED) {
read_lock(&br->lock); kfree(skb);
if (skb->dev->br_port == NULL) goto out;
goto err; }
br = p->br;
passedup = 0; passedup = 0;
if (br->dev.flags & IFF_PROMISC) { if (br->dev.flags & IFF_PROMISC) {
struct sk_buff *skb2; struct sk_buff *skb2;
...@@ -105,35 +106,20 @@ int br_handle_frame_finish(struct sk_buff *skb) ...@@ -105,35 +106,20 @@ int br_handle_frame_finish(struct sk_buff *skb)
br_flood_forward(br, skb, 0); br_flood_forward(br, skb, 0);
out: out:
read_unlock(&br->lock); rcu_read_unlock();
return 0;
err:
read_unlock(&br->lock);
err_nolock:
kfree_skb(skb);
return 0; return 0;
} }
int br_handle_frame(struct sk_buff *skb) int br_handle_frame(struct sk_buff *skb)
{ {
struct net_bridge *br;
unsigned char *dest; unsigned char *dest;
struct net_bridge_port *p; struct net_bridge_port *p;
dest = skb->mac.ethernet->h_dest; dest = skb->mac.ethernet->h_dest;
rcu_read_lock();
p = skb->dev->br_port; p = skb->dev->br_port;
if (p == NULL) if (p == NULL || p->state == BR_STATE_DISABLED)
goto err_nolock;
br = p->br;
read_lock(&br->lock);
if (skb->dev->br_port == NULL)
goto err;
if (!(br->dev.flags & IFF_UP) ||
p->state == BR_STATE_DISABLED)
goto err; goto err;
if (skb->mac.ethernet->h_source[0] & 1) if (skb->mac.ethernet->h_source[0] & 1)
...@@ -141,39 +127,30 @@ int br_handle_frame(struct sk_buff *skb) ...@@ -141,39 +127,30 @@ int br_handle_frame(struct sk_buff *skb)
if (p->state == BR_STATE_LEARNING || if (p->state == BR_STATE_LEARNING ||
p->state == BR_STATE_FORWARDING) p->state == BR_STATE_FORWARDING)
br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0); br_fdb_insert(p->br, p, skb->mac.ethernet->h_source, 0);
if (br->stp_enabled && if (p->br->stp_enabled &&
!memcmp(dest, bridge_ula, 5) && !memcmp(dest, bridge_ula, 5) &&
!(dest[5] & 0xF0)) !(dest[5] & 0xF0)) {
goto handle_special_frame; if (!dest[5])
br_stp_handle_bpdu(skb);
goto err;
}
if (p->state == BR_STATE_FORWARDING) { if (p->state == BR_STATE_FORWARDING) {
if (br_should_route_hook && br_should_route_hook(&skb)) { if (br_should_route_hook && br_should_route_hook(&skb)) {
read_unlock(&br->lock); rcu_read_unlock();
return -1; return -1;
} }
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish); br_handle_frame_finish);
read_unlock(&br->lock); rcu_read_unlock();
return 0; return 0;
} }
err: err:
read_unlock(&br->lock); rcu_read_unlock();
err_nolock:
kfree_skb(skb);
return 0;
handle_special_frame:
if (!dest[5]) {
br_stp_handle_bpdu(skb);
read_unlock(&br->lock);
return 0;
}
kfree_skb(skb); kfree_skb(skb);
read_unlock(&br->lock);
return 0; return 0;
} }
...@@ -68,8 +68,8 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -68,8 +68,8 @@ static int br_ioctl_device(struct net_bridge *br,
{ {
struct __bridge_info b; struct __bridge_info b;
read_lock(&br->lock);
memset(&b, 0, sizeof(struct __bridge_info)); memset(&b, 0, sizeof(struct __bridge_info));
rcu_read_lock();
memcpy(&b.designated_root, &br->designated_root, 8); memcpy(&b.designated_root, &br->designated_root, 8);
memcpy(&b.bridge_id, &br->bridge_id, 8); memcpy(&b.bridge_id, &br->bridge_id, 8);
b.root_path_cost = br->root_path_cost; b.root_path_cost = br->root_path_cost;
...@@ -89,7 +89,7 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -89,7 +89,7 @@ static int br_ioctl_device(struct net_bridge *br,
b.tcn_timer_value = timer_residue(&br->tcn_timer); b.tcn_timer_value = timer_residue(&br->tcn_timer);
b.topology_change_timer_value = timer_residue(&br->topology_change_timer); b.topology_change_timer_value = timer_residue(&br->topology_change_timer);
b.gc_timer_value = timer_residue(&br->gc_timer); b.gc_timer_value = timer_residue(&br->gc_timer);
read_unlock(&br->lock); rcu_read_unlock();
if (copy_to_user((void *)arg0, &b, sizeof(b))) if (copy_to_user((void *)arg0, &b, sizeof(b)))
return -EFAULT; return -EFAULT;
...@@ -116,27 +116,27 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -116,27 +116,27 @@ static int br_ioctl_device(struct net_bridge *br,
} }
case BRCTL_SET_BRIDGE_FORWARD_DELAY: case BRCTL_SET_BRIDGE_FORWARD_DELAY:
write_lock(&br->lock); spin_lock_bh(&br->lock);
br->bridge_forward_delay = user_to_ticks(arg0); br->bridge_forward_delay = user_to_ticks(arg0);
if (br_is_root_bridge(br)) if (br_is_root_bridge(br))
br->forward_delay = br->bridge_forward_delay; br->forward_delay = br->bridge_forward_delay;
write_unlock(&br->lock); spin_unlock_bh(&br->lock);
return 0; return 0;
case BRCTL_SET_BRIDGE_HELLO_TIME: case BRCTL_SET_BRIDGE_HELLO_TIME:
write_lock(&br->lock); spin_lock_bh(&br->lock);
br->bridge_hello_time = user_to_ticks(arg0); br->bridge_hello_time = user_to_ticks(arg0);
if (br_is_root_bridge(br)) if (br_is_root_bridge(br))
br->hello_time = br->bridge_hello_time; br->hello_time = br->bridge_hello_time;
write_unlock(&br->lock); spin_unlock_bh(&br->lock);
return 0; return 0;
case BRCTL_SET_BRIDGE_MAX_AGE: case BRCTL_SET_BRIDGE_MAX_AGE:
write_lock(&br->lock); spin_lock_bh(&br->lock);
br->bridge_max_age = user_to_ticks(arg0); br->bridge_max_age = user_to_ticks(arg0);
if (br_is_root_bridge(br)) if (br_is_root_bridge(br))
br->max_age = br->bridge_max_age; br->max_age = br->bridge_max_age;
write_unlock(&br->lock); spin_unlock_bh(&br->lock);
return 0; return 0;
case BRCTL_SET_AGEING_TIME: case BRCTL_SET_AGEING_TIME:
...@@ -152,9 +152,9 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -152,9 +152,9 @@ static int br_ioctl_device(struct net_bridge *br,
struct __port_info p; struct __port_info p;
struct net_bridge_port *pt; struct net_bridge_port *pt;
read_lock(&br->lock); rcu_read_lock();
if ((pt = br_get_port(br, arg1)) == NULL) { if ((pt = br_get_port(br, arg1)) == NULL) {
read_unlock(&br->lock); rcu_read_unlock();
return -EINVAL; return -EINVAL;
} }
...@@ -172,7 +172,7 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -172,7 +172,7 @@ static int br_ioctl_device(struct net_bridge *br,
p.forward_delay_timer_value = timer_residue(&pt->forward_delay_timer); p.forward_delay_timer_value = timer_residue(&pt->forward_delay_timer);
p.hold_timer_value = timer_residue(&pt->hold_timer); p.hold_timer_value = timer_residue(&pt->hold_timer);
read_unlock(&br->lock); rcu_read_unlock();
if (copy_to_user((void *)arg0, &p, sizeof(p))) if (copy_to_user((void *)arg0, &p, sizeof(p)))
return -EFAULT; return -EFAULT;
...@@ -185,9 +185,9 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -185,9 +185,9 @@ static int br_ioctl_device(struct net_bridge *br,
return 0; return 0;
case BRCTL_SET_BRIDGE_PRIORITY: case BRCTL_SET_BRIDGE_PRIORITY:
write_lock(&br->lock); spin_lock_bh(&br->lock);
br_stp_set_bridge_priority(br, arg0); br_stp_set_bridge_priority(br, arg0);
write_unlock(&br->lock); spin_unlock_bh(&br->lock);
return 0; return 0;
case BRCTL_SET_PORT_PRIORITY: case BRCTL_SET_PORT_PRIORITY:
...@@ -195,12 +195,12 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -195,12 +195,12 @@ static int br_ioctl_device(struct net_bridge *br,
struct net_bridge_port *p; struct net_bridge_port *p;
int ret = 0; int ret = 0;
write_lock(&br->lock); spin_lock_bh(&br->lock);
if ((p = br_get_port(br, arg0)) == NULL) if ((p = br_get_port(br, arg0)) == NULL)
ret = -EINVAL; ret = -EINVAL;
else else
br_stp_set_port_priority(p, arg1); br_stp_set_port_priority(p, arg1);
write_unlock(&br->lock); spin_unlock_bh(&br->lock);
return ret; return ret;
} }
...@@ -209,12 +209,12 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -209,12 +209,12 @@ static int br_ioctl_device(struct net_bridge *br,
struct net_bridge_port *p; struct net_bridge_port *p;
int ret = 0; int ret = 0;
write_lock(&br->lock); spin_lock_bh(&br->lock);
if ((p = br_get_port(br, arg0)) == NULL) if ((p = br_get_port(br, arg0)) == NULL)
ret = -EINVAL; ret = -EINVAL;
else else
br_stp_set_path_cost(p, arg1); br_stp_set_path_cost(p, arg1);
write_unlock(&br->lock); spin_unlock_bh(&br->lock);
return ret; return ret;
} }
......
...@@ -41,10 +41,10 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v ...@@ -41,10 +41,10 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
switch (event) switch (event)
{ {
case NETDEV_CHANGEADDR: case NETDEV_CHANGEADDR:
write_lock_bh(&br->lock); spin_lock_bh(&br->lock);
br_fdb_changeaddr(p, dev->dev_addr); br_fdb_changeaddr(p, dev->dev_addr);
br_stp_recalculate_bridge_id(br); br_stp_recalculate_bridge_id(br);
write_unlock_bh(&br->lock); spin_unlock_bh(&br->lock);
break; break;
case NETDEV_GOING_DOWN: case NETDEV_GOING_DOWN:
...@@ -53,17 +53,17 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v ...@@ -53,17 +53,17 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
case NETDEV_DOWN: case NETDEV_DOWN:
if (br->dev.flags & IFF_UP) { if (br->dev.flags & IFF_UP) {
write_lock_bh(&br->lock); spin_lock_bh(&br->lock);
br_stp_disable_port(p); br_stp_disable_port(p);
write_unlock_bh(&br->lock); spin_unlock_bh(&br->lock);
} }
break; break;
case NETDEV_UP: case NETDEV_UP:
if (!(br->dev.flags & IFF_UP)) { if (!(br->dev.flags & IFF_UP)) {
write_lock_bh(&br->lock); spin_lock_bh(&br->lock);
br_stp_enable_port(p); br_stp_enable_port(p);
write_unlock_bh(&br->lock); spin_unlock_bh(&br->lock);
} }
break; break;
......
...@@ -55,9 +55,9 @@ struct net_bridge_fdb_entry ...@@ -55,9 +55,9 @@ struct net_bridge_fdb_entry
struct net_bridge_port struct net_bridge_port
{ {
struct net_bridge_port *next;
struct net_bridge *br; struct net_bridge *br;
struct net_device *dev; struct net_device *dev;
struct list_head list;
int port_no; int port_no;
/* STP */ /* STP */
...@@ -75,12 +75,14 @@ struct net_bridge_port ...@@ -75,12 +75,14 @@ struct net_bridge_port
struct br_timer forward_delay_timer; struct br_timer forward_delay_timer;
struct br_timer hold_timer; struct br_timer hold_timer;
struct br_timer message_age_timer; struct br_timer message_age_timer;
struct rcu_head rcu;
}; };
struct net_bridge struct net_bridge
{ {
rwlock_t lock; spinlock_t lock;
struct net_bridge_port *port_list; struct list_head port_list;
struct net_device dev; struct net_device dev;
struct net_device_stats statistics; struct net_device_stats statistics;
rwlock_t hash_lock; rwlock_t hash_lock;
...@@ -137,10 +139,10 @@ extern void br_fdb_insert(struct net_bridge *br, ...@@ -137,10 +139,10 @@ extern void br_fdb_insert(struct net_bridge *br,
int is_local); int is_local);
/* br_forward.c */ /* br_forward.c */
extern void br_deliver(struct net_bridge_port *to, extern void br_deliver(const struct net_bridge_port *to,
struct sk_buff *skb); struct sk_buff *skb);
extern int br_dev_queue_push_xmit(struct sk_buff *skb); extern int br_dev_queue_push_xmit(struct sk_buff *skb);
extern void br_forward(struct net_bridge_port *to, extern void br_forward(const struct net_bridge_port *to,
struct sk_buff *skb); struct sk_buff *skb);
extern int br_forward_finish(struct sk_buff *skb); extern int br_forward_finish(struct sk_buff *skb);
extern void br_flood_deliver(struct net_bridge *br, extern void br_flood_deliver(struct net_bridge *br,
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
/* called under ioctl_lock or bridge lock */ /* called under bridge lock */
int br_is_root_bridge(struct net_bridge *br) int br_is_root_bridge(struct net_bridge *br)
{ {
return !memcmp(&br->bridge_id, &br->designated_root, 8); return !memcmp(&br->bridge_id, &br->designated_root, 8);
...@@ -35,17 +35,14 @@ int br_is_designated_port(struct net_bridge_port *p) ...@@ -35,17 +35,14 @@ int br_is_designated_port(struct net_bridge_port *p)
(p->designated_port == p->port_id); (p->designated_port == p->port_id);
} }
/* called under ioctl_lock or bridge lock */ /* called under bridge lock */
struct net_bridge_port *br_get_port(struct net_bridge *br, int port_no) struct net_bridge_port *br_get_port(struct net_bridge *br, int port_no)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->port_no == port_no) if (p->port_no == port_no)
return p; return p;
p = p->next;
} }
return NULL; return NULL;
...@@ -109,12 +106,10 @@ static void br_root_selection(struct net_bridge *br) ...@@ -109,12 +106,10 @@ static void br_root_selection(struct net_bridge *br)
root_port = 0; root_port = 0;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (br_should_become_root_port(p, root_port)) if (br_should_become_root_port(p, root_port))
root_port = p->port_no; root_port = p->port_no;
p = p->next;
} }
br->root_port = root_port; br->root_port = root_port;
...@@ -241,13 +236,11 @@ static void br_designated_port_selection(struct net_bridge *br) ...@@ -241,13 +236,11 @@ static void br_designated_port_selection(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED && if (p->state != BR_STATE_DISABLED &&
br_should_become_designated_port(p)) br_should_become_designated_port(p))
br_become_designated_port(p); br_become_designated_port(p);
p = p->next;
} }
} }
...@@ -313,13 +306,10 @@ void br_config_bpdu_generation(struct net_bridge *br) ...@@ -313,13 +306,10 @@ void br_config_bpdu_generation(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED && if (p->state != BR_STATE_DISABLED &&
br_is_designated_port(p)) br_is_designated_port(p))
br_transmit_config(p); br_transmit_config(p);
p = p->next;
} }
} }
...@@ -391,8 +381,7 @@ void br_port_state_selection(struct net_bridge *br) ...@@ -391,8 +381,7 @@ void br_port_state_selection(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED) { if (p->state != BR_STATE_DISABLED) {
if (p->port_no == br->root_port) { if (p->port_no == br->root_port) {
p->config_pending = 0; p->config_pending = 0;
...@@ -407,8 +396,6 @@ void br_port_state_selection(struct net_bridge *br) ...@@ -407,8 +396,6 @@ void br_port_state_selection(struct net_bridge *br)
br_make_blocking(p); br_make_blocking(p);
} }
} }
p = p->next;
} }
} }
...@@ -419,18 +406,13 @@ static void br_topology_change_acknowledge(struct net_bridge_port *p) ...@@ -419,18 +406,13 @@ static void br_topology_change_acknowledge(struct net_bridge_port *p)
br_transmit_config(p); br_transmit_config(p);
} }
/* lock-safe */ /* called under bridge lock */
void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
{ {
struct net_bridge *br; struct net_bridge *br;
int was_root; int was_root;
if (p->state == BR_STATE_DISABLED)
return;
br = p->br; br = p->br;
read_lock(&br->lock);
was_root = br_is_root_bridge(br); was_root = br_is_root_bridge(br);
if (br_supersedes_port_info(p, bpdu)) { if (br_supersedes_port_info(p, bpdu)) {
br_record_config_information(p, bpdu); br_record_config_information(p, bpdu);
...@@ -455,21 +437,16 @@ void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *b ...@@ -455,21 +437,16 @@ void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *b
} else if (br_is_designated_port(p)) { } else if (br_is_designated_port(p)) {
br_reply(p); br_reply(p);
} }
read_unlock(&br->lock);
} }
/* lock-safe */ /* called under bridge lock */
void br_received_tcn_bpdu(struct net_bridge_port *p) void br_received_tcn_bpdu(struct net_bridge_port *p)
{ {
read_lock(&p->br->lock); if (br_is_designated_port(p)) {
if (p->state != BR_STATE_DISABLED &&
br_is_designated_port(p)) {
printk(KERN_INFO "%s: received tcn bpdu on port %i(%s)\n", printk(KERN_INFO "%s: received tcn bpdu on port %i(%s)\n",
p->br->dev.name, p->port_no, p->dev->name); p->br->dev.name, p->port_no, p->dev->name);
br_topology_change_detection(p->br); br_topology_change_detection(p->br);
br_topology_change_acknowledge(p); br_topology_change_acknowledge(p);
} }
read_unlock(&p->br->lock);
} }
...@@ -132,18 +132,23 @@ void br_send_tcn_bpdu(struct net_bridge_port *p) ...@@ -132,18 +132,23 @@ void br_send_tcn_bpdu(struct net_bridge_port *p)
static unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; static unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
/* called under bridge lock */ /* NO locks */
void br_stp_handle_bpdu(struct sk_buff *skb) void br_stp_handle_bpdu(struct sk_buff *skb)
{ {
unsigned char *buf; unsigned char *buf;
struct net_bridge_port *p; struct net_bridge_port *p;
struct net_bridge *br;
buf = skb->mac.raw + 14; buf = skb->mac.raw + 14;
p = skb->dev->br_port; p = skb->dev->br_port;
if (!p->br->stp_enabled || memcmp(buf, header, 6)) { br = p->br;
kfree_skb(skb);
return; spin_lock_bh(&br->lock);
} if (p->state == BR_STATE_DISABLED
|| !(br->dev.flags & IFF_UP)
|| !br->stp_enabled
|| memcmp(buf, header, 6))
goto out;
if (buf[6] == BPDU_TYPE_CONFIG) { if (buf[6] == BPDU_TYPE_CONFIG) {
struct br_config_bpdu bpdu; struct br_config_bpdu bpdu;
...@@ -178,16 +183,14 @@ void br_stp_handle_bpdu(struct sk_buff *skb) ...@@ -178,16 +183,14 @@ void br_stp_handle_bpdu(struct sk_buff *skb)
bpdu.hello_time = br_get_ticks(buf+34); bpdu.hello_time = br_get_ticks(buf+34);
bpdu.forward_delay = br_get_ticks(buf+36); bpdu.forward_delay = br_get_ticks(buf+36);
kfree_skb(skb);
br_received_config_bpdu(p, &bpdu); br_received_config_bpdu(p, &bpdu);
return; goto out;
} }
if (buf[6] == BPDU_TYPE_TCN) { if (buf[6] == BPDU_TYPE_TCN) {
br_received_tcn_bpdu(p); br_received_tcn_bpdu(p);
kfree_skb(skb); goto out;
return;
} }
out:
kfree_skb(skb); spin_unlock_bh(&br->lock);
} }
...@@ -44,6 +44,7 @@ void br_stp_enable_bridge(struct net_bridge *br) ...@@ -44,6 +44,7 @@ void br_stp_enable_bridge(struct net_bridge *br)
struct net_bridge_port *p; struct net_bridge_port *p;
struct timer_list *timer = &br->tick; struct timer_list *timer = &br->tick;
spin_lock_bh(&br->lock);
init_timer(timer); init_timer(timer);
timer->data = (unsigned long) br; timer->data = (unsigned long) br;
timer->function = br_tick; timer->function = br_tick;
...@@ -53,22 +54,21 @@ void br_stp_enable_bridge(struct net_bridge *br) ...@@ -53,22 +54,21 @@ void br_stp_enable_bridge(struct net_bridge *br)
br_timer_set(&br->hello_timer, jiffies); br_timer_set(&br->hello_timer, jiffies);
br_config_bpdu_generation(br); br_config_bpdu_generation(br);
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->dev->flags & IFF_UP) if (p->dev->flags & IFF_UP)
br_stp_enable_port(p); br_stp_enable_port(p);
p = p->next;
} }
br_timer_set(&br->gc_timer, jiffies); br_timer_set(&br->gc_timer, jiffies);
spin_unlock_bh(&br->lock);
} }
/* called under bridge lock */ /* NO locks held */
void br_stp_disable_bridge(struct net_bridge *br) void br_stp_disable_bridge(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
spin_lock_bh(&br->lock);
br->topology_change = 0; br->topology_change = 0;
br->topology_change_detected = 0; br->topology_change_detected = 0;
br_timer_clear(&br->hello_timer); br_timer_clear(&br->hello_timer);
...@@ -77,13 +77,11 @@ void br_stp_disable_bridge(struct net_bridge *br) ...@@ -77,13 +77,11 @@ void br_stp_disable_bridge(struct net_bridge *br)
br_timer_clear(&br->gc_timer); br_timer_clear(&br->gc_timer);
br_fdb_cleanup(br); br_fdb_cleanup(br);
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED) if (p->state != BR_STATE_DISABLED)
br_stp_disable_port(p); br_stp_disable_port(p);
p = p->next;
} }
spin_unlock_bh(&br->lock);
del_timer_sync(&br->tick); del_timer_sync(&br->tick);
} }
...@@ -133,15 +131,13 @@ static void br_stp_change_bridge_id(struct net_bridge *br, unsigned char *addr) ...@@ -133,15 +131,13 @@ static void br_stp_change_bridge_id(struct net_bridge *br, unsigned char *addr)
memcpy(br->bridge_id.addr, addr, ETH_ALEN); memcpy(br->bridge_id.addr, addr, ETH_ALEN);
memcpy(br->dev.dev_addr, addr, ETH_ALEN); memcpy(br->dev.dev_addr, addr, ETH_ALEN);
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN)) if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN))
memcpy(p->designated_bridge.addr, addr, ETH_ALEN); memcpy(p->designated_bridge.addr, addr, ETH_ALEN);
if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN)) if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN))
memcpy(p->designated_root.addr, addr, ETH_ALEN); memcpy(p->designated_root.addr, addr, ETH_ALEN);
p = p->next;
} }
br_configuration_update(br); br_configuration_update(br);
...@@ -160,13 +156,11 @@ void br_stp_recalculate_bridge_id(struct net_bridge *br) ...@@ -160,13 +156,11 @@ void br_stp_recalculate_bridge_id(struct net_bridge *br)
addr = br_mac_zero; addr = br_mac_zero;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (addr == br_mac_zero || if (addr == br_mac_zero ||
memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0) memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)
addr = p->dev->dev_addr; addr = p->dev->dev_addr;
p = p->next;
} }
if (memcmp(br->bridge_id.addr, addr, ETH_ALEN)) if (memcmp(br->bridge_id.addr, addr, ETH_ALEN))
...@@ -181,15 +175,13 @@ void br_stp_set_bridge_priority(struct net_bridge *br, int newprio) ...@@ -181,15 +175,13 @@ void br_stp_set_bridge_priority(struct net_bridge *br, int newprio)
wasroot = br_is_root_bridge(br); wasroot = br_is_root_bridge(br);
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED && if (p->state != BR_STATE_DISABLED &&
br_is_designated_port(p)) { br_is_designated_port(p)) {
p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF; p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF;
p->designated_bridge.prio[1] = newprio & 0xFF; p->designated_bridge.prio[1] = newprio & 0xFF;
} }
p = p->next;
} }
br->bridge_id.prio[0] = (newprio >> 8) & 0xFF; br->bridge_id.prio[0] = (newprio >> 8) & 0xFF;
......
...@@ -32,13 +32,10 @@ static int br_is_designated_for_some_port(struct net_bridge *br) ...@@ -32,13 +32,10 @@ static int br_is_designated_for_some_port(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED && if (p->state != BR_STATE_DISABLED &&
!memcmp(&p->designated_bridge, &br->bridge_id, 8)) !memcmp(&p->designated_bridge, &br->bridge_id, 8))
return 1; return 1;
p = p->next;
} }
return 0; return 0;
...@@ -162,12 +159,9 @@ static void br_check_timers(struct net_bridge *br) ...@@ -162,12 +159,9 @@ static void br_check_timers(struct net_bridge *br)
br_topology_change_timer_expired(br); br_topology_change_timer_expired(br);
} }
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED) if (p->state != BR_STATE_DISABLED)
br_check_port_timers(p); br_check_port_timers(p);
p = p->next;
} }
} }
...@@ -175,10 +169,10 @@ void br_tick(unsigned long __data) ...@@ -175,10 +169,10 @@ void br_tick(unsigned long __data)
{ {
struct net_bridge *br = (struct net_bridge *)__data; struct net_bridge *br = (struct net_bridge *)__data;
read_lock(&br->lock); if (spin_trylock_bh(&br->lock)) {
br_check_timers(br); br_check_timers(br);
read_unlock(&br->lock); spin_unlock_bh(&br->lock);
}
br->tick.expires = jiffies + 1; br->tick.expires = jiffies + 1;
add_timer(&br->tick); add_timer(&br->tick);
} }
...@@ -300,6 +300,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev) ...@@ -300,6 +300,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)
printk("Freeing alive inet6 device %p\n", idev); printk("Freeing alive inet6 device %p\n", idev);
return; return;
} }
snmp6_unregister_dev(idev);
inet6_dev_count--; inet6_dev_count--;
kfree(idev); kfree(idev);
} }
...@@ -332,6 +333,15 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) ...@@ -332,6 +333,15 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
/* We refer to the device */ /* We refer to the device */
dev_hold(dev); dev_hold(dev);
if (snmp6_register_dev(ndev) < 0) {
ADBG((KERN_WARNING
"%s(): cannot create /proc/net/dev_snmp6/%s\n",
__FUNCTION__, dev->name));
neigh_parms_release(&nd_tbl, ndev->nd_parms);
in6_dev_finish_destroy(ndev);
return NULL;
}
#ifdef CONFIG_IPV6_PRIVACY #ifdef CONFIG_IPV6_PRIVACY
get_random_bytes(ndev->rndid, sizeof(ndev->rndid)); get_random_bytes(ndev->rndid, sizeof(ndev->rndid));
get_random_bytes(ndev->entropy, sizeof(ndev->entropy)); get_random_bytes(ndev->entropy, sizeof(ndev->entropy));
......
...@@ -631,79 +631,72 @@ inet6_unregister_protosw(struct inet_protosw *p) ...@@ -631,79 +631,72 @@ inet6_unregister_protosw(struct inet_protosw *p)
inet_unregister_protosw(p); inet_unregister_protosw(p);
} }
static int __init init_ipv6_mibs(void) int
snmp6_mib_init(void *ptr[2], size_t mibsize)
{ {
int i; int i;
ipv6_statistics[0] = kmalloc_percpu(sizeof (struct ipv6_mib), if (ptr == NULL)
GFP_KERNEL); return -EINVAL;
if (!ipv6_statistics[0])
goto err_ip_mib0; ptr[0] = kmalloc_percpu(mibsize, GFP_KERNEL);
ipv6_statistics[1] = kmalloc_percpu(sizeof (struct ipv6_mib), if (!ptr[0])
GFP_KERNEL); goto err0;
if (!ipv6_statistics[1])
goto err_ip_mib1; ptr[1] = kmalloc_percpu(mibsize, GFP_KERNEL);
if (!ptr[1])
icmpv6_statistics[0] = kmalloc_percpu(sizeof (struct icmpv6_mib), goto err1;
GFP_KERNEL);
if (!icmpv6_statistics[0]) /* Zero percpu version of the mibs */
goto err_icmp_mib0;
icmpv6_statistics[1] = kmalloc_percpu(sizeof (struct icmpv6_mib),
GFP_KERNEL);
if (!icmpv6_statistics[1])
goto err_icmp_mib1;
udp_stats_in6[0] = kmalloc_percpu(sizeof (struct udp_mib),
GFP_KERNEL);
if (!udp_stats_in6[0])
goto err_udp_mib0;
udp_stats_in6[1] = kmalloc_percpu(sizeof (struct udp_mib),
GFP_KERNEL);
if (!udp_stats_in6[1])
goto err_udp_mib1;
/* Zero all percpu versions of the mibs */
for (i = 0; i < NR_CPUS; i++) { for (i = 0; i < NR_CPUS; i++) {
if (cpu_possible(i)) { if (cpu_possible(i)) {
memset(per_cpu_ptr(ipv6_statistics[0], i), 0, memset(per_cpu_ptr(ptr[0], i), 0, mibsize);
sizeof (struct ipv6_mib)); memset(per_cpu_ptr(ptr[1], i), 0, mibsize);
memset(per_cpu_ptr(ipv6_statistics[1], i), 0,
sizeof (struct ipv6_mib));
memset(per_cpu_ptr(icmpv6_statistics[0], i), 0,
sizeof (struct icmpv6_mib));
memset(per_cpu_ptr(icmpv6_statistics[1], i), 0,
sizeof (struct icmpv6_mib));
memset(per_cpu_ptr(udp_stats_in6[0], i), 0,
sizeof (struct udp_mib));
memset(per_cpu_ptr(udp_stats_in6[1], i), 0,
sizeof (struct udp_mib));
} }
} }
return 0; return 0;
err_udp_mib1: err1:
kfree_percpu(udp_stats_in6[0]); kfree_percpu(ptr[0]);
err_udp_mib0: ptr[0] = NULL;
kfree_percpu(icmpv6_statistics[1]); err0:
err_icmp_mib1: return -ENOMEM;
kfree_percpu(icmpv6_statistics[0]); }
err_icmp_mib0:
kfree_percpu(ipv6_statistics[1]); void
err_ip_mib1: snmp6_mib_free(void *ptr[2])
kfree_percpu(ipv6_statistics[0]); {
err_ip_mib0: if (ptr == NULL)
return;
kfree_percpu(ptr[0]);
kfree_percpu(ptr[1]);
ptr[0] = ptr[1] = NULL;
}
static int __init init_ipv6_mibs(void)
{
if (snmp6_mib_init((void **)ipv6_statistics, sizeof (struct ipv6_mib)) < 0)
goto err_ip_mib;
if (snmp6_mib_init((void **)icmpv6_statistics, sizeof (struct icmpv6_mib)) < 0)
goto err_icmp_mib;
if (snmp6_mib_init((void **)udp_stats_in6, sizeof (struct udp_mib)) < 0)
goto err_udp_mib;
return 0;
err_udp_mib:
snmp6_mib_free((void **)icmpv6_statistics);
err_icmp_mib:
snmp6_mib_free((void **)ipv6_statistics);
err_ip_mib:
return -ENOMEM; return -ENOMEM;
} }
static void cleanup_ipv6_mibs(void) static void cleanup_ipv6_mibs(void)
{ {
kfree_percpu(ipv6_statistics[0]); snmp6_mib_free((void **)ipv6_statistics);
kfree_percpu(ipv6_statistics[1]); snmp6_mib_free((void **)icmpv6_statistics);
kfree_percpu(icmpv6_statistics[0]); snmp6_mib_free((void **)udp_stats_in6);
kfree_percpu(icmpv6_statistics[1]);
kfree_percpu(udp_stats_in6[0]);
kfree_percpu(udp_stats_in6[1]);
} }
extern int ipv6_misc_proc_init(void); extern int ipv6_misc_proc_init(void);
...@@ -819,6 +812,7 @@ static int __init inet6_init(void) ...@@ -819,6 +812,7 @@ static int __init inet6_init(void)
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
proc_anycast6_fail: proc_anycast6_fail:
proc_net_remove("snmp6"); proc_net_remove("snmp6");
proc_net_remove("dev_snmp6");
proc_net_remove("sockstat6"); proc_net_remove("sockstat6");
proc_misc6_fail: proc_misc6_fail:
proc_net_remove("udp6"); proc_net_remove("udp6");
...@@ -854,6 +848,7 @@ static void inet6_exit(void) ...@@ -854,6 +848,7 @@ static void inet6_exit(void)
proc_net_remove("tcp6"); proc_net_remove("tcp6");
proc_net_remove("udp6"); proc_net_remove("udp6");
proc_net_remove("sockstat6"); proc_net_remove("sockstat6");
proc_net_remove("dev_snmp6");
proc_net_remove("snmp6"); proc_net_remove("snmp6");
proc_net_remove("anycast6"); proc_net_remove("anycast6");
#endif #endif
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
* yoshfuji : ensure to sent parameter problem for * yoshfuji : ensure to sent parameter problem for
* fragments. * fragments.
* YOSHIFUJI Hideaki @USAGI: added sysctl for icmp rate limit. * YOSHIFUJI Hideaki @USAGI: added sysctl for icmp rate limit.
* Randy Dunlap and
* YOSHIFUJI Hideaki @USAGI: Per-interface statistics support
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -247,6 +249,7 @@ static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset) ...@@ -247,6 +249,7 @@ static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
struct net_device *dev) struct net_device *dev)
{ {
struct inet6_dev *idev;
struct ipv6hdr *hdr = skb->nh.ipv6h; struct ipv6hdr *hdr = skb->nh.ipv6h;
struct sock *sk = icmpv6_socket->sk; struct sock *sk = icmpv6_socket->sk;
struct in6_addr *saddr = NULL; struct in6_addr *saddr = NULL;
...@@ -351,11 +354,16 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, ...@@ -351,11 +354,16 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
msg.len = len; msg.len = len;
idev = in6_dev_get(skb->dev);
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1, ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
MSG_DONTWAIT); MSG_DONTWAIT);
if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
ICMP6_STATS_PTR_BH(Icmp6OutDestUnreachs) [type-ICMPV6_DEST_UNREACH]++; ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6OutDestUnreachs, type - ICMPV6_DEST_UNREACH);
ICMP6_INC_STATS_BH(Icmp6OutMsgs); ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
out: out:
icmpv6_xmit_unlock(); icmpv6_xmit_unlock();
} }
...@@ -363,6 +371,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, ...@@ -363,6 +371,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
static void icmpv6_echo_reply(struct sk_buff *skb) static void icmpv6_echo_reply(struct sk_buff *skb)
{ {
struct sock *sk = icmpv6_socket->sk; struct sock *sk = icmpv6_socket->sk;
struct inet6_dev *idev;
struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw; struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
struct in6_addr *saddr; struct in6_addr *saddr;
struct icmpv6_msg msg; struct icmpv6_msg msg;
...@@ -394,14 +403,19 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -394,14 +403,19 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
fl.fl_icmp_type = ICMPV6_ECHO_REPLY; fl.fl_icmp_type = ICMPV6_ECHO_REPLY;
fl.fl_icmp_code = 0; fl.fl_icmp_code = 0;
idev = in6_dev_get(skb->dev);
icmpv6_xmit_lock(); icmpv6_xmit_lock();
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1, ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1,
MSG_DONTWAIT); MSG_DONTWAIT);
ICMP6_INC_STATS_BH(Icmp6OutEchoReplies); ICMP6_INC_STATS_BH(idev, Icmp6OutEchoReplies);
ICMP6_INC_STATS_BH(Icmp6OutMsgs); ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs);
icmpv6_xmit_unlock(); icmpv6_xmit_unlock();
if (likely(idev != NULL))
in6_dev_put(idev);
} }
static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info) static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info)
...@@ -464,12 +478,13 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) ...@@ -464,12 +478,13 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
{ {
struct sk_buff *skb = *pskb; struct sk_buff *skb = *pskb;
struct net_device *dev = skb->dev; struct net_device *dev = skb->dev;
struct inet6_dev *idev = __in6_dev_get(dev);
struct in6_addr *saddr, *daddr; struct in6_addr *saddr, *daddr;
struct ipv6hdr *orig_hdr; struct ipv6hdr *orig_hdr;
struct icmp6hdr *hdr; struct icmp6hdr *hdr;
int type; int type;
ICMP6_INC_STATS_BH(Icmp6InMsgs); ICMP6_INC_STATS_BH(idev, Icmp6InMsgs);
saddr = &skb->nh.ipv6h->saddr; saddr = &skb->nh.ipv6h->saddr;
daddr = &skb->nh.ipv6h->daddr; daddr = &skb->nh.ipv6h->daddr;
...@@ -517,9 +532,9 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) ...@@ -517,9 +532,9 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
type = hdr->icmp6_type; type = hdr->icmp6_type;
if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
ICMP6_STATS_PTR_BH(Icmp6InDestUnreachs)[type-ICMPV6_DEST_UNREACH]++; ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6InDestUnreachs, type - ICMPV6_DEST_UNREACH);
else if (type >= ICMPV6_ECHO_REQUEST && type <= NDISC_REDIRECT) else if (type >= ICMPV6_ECHO_REQUEST && type <= NDISC_REDIRECT)
ICMP6_STATS_PTR_BH(Icmp6InEchos)[type-ICMPV6_ECHO_REQUEST]++; ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6InEchos, type - ICMPV6_ECHO_REQUEST);
switch (type) { switch (type) {
case ICMPV6_ECHO_REQUEST: case ICMPV6_ECHO_REQUEST:
...@@ -597,7 +612,7 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) ...@@ -597,7 +612,7 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
return 0; return 0;
discard_it: discard_it:
ICMP6_INC_STATS_BH(Icmp6InErrors); ICMP6_INC_STATS_BH(idev, Icmp6InErrors);
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
} }
......
...@@ -34,5 +34,6 @@ EXPORT_SYMBOL(ipv6_get_saddr); ...@@ -34,5 +34,6 @@ EXPORT_SYMBOL(ipv6_get_saddr);
EXPORT_SYMBOL(ipv6_chk_addr); EXPORT_SYMBOL(ipv6_chk_addr);
EXPORT_SYMBOL(in6addr_any); EXPORT_SYMBOL(in6addr_any);
EXPORT_SYMBOL(in6addr_loopback); EXPORT_SYMBOL(in6addr_loopback);
EXPORT_SYMBOL(in6_dev_finish_destroy);
EXPORT_SYMBOL(xfrm6_rcv); EXPORT_SYMBOL(xfrm6_rcv);
EXPORT_SYMBOL(xfrm6_clear_mutable_options); EXPORT_SYMBOL(xfrm6_clear_mutable_options);
...@@ -1253,6 +1253,7 @@ static void mld_sendpack(struct sk_buff *skb) ...@@ -1253,6 +1253,7 @@ static void mld_sendpack(struct sk_buff *skb)
struct ipv6hdr *pip6 = skb->nh.ipv6h; struct ipv6hdr *pip6 = skb->nh.ipv6h;
struct mld2_report *pmr = (struct mld2_report *)skb->h.raw; struct mld2_report *pmr = (struct mld2_report *)skb->h.raw;
int payload_len, mldlen; int payload_len, mldlen;
struct inet6_dev *idev = in6_dev_get(skb->dev);
payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h - payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h -
sizeof(struct ipv6hdr); sizeof(struct ipv6hdr);
...@@ -1262,7 +1263,9 @@ static void mld_sendpack(struct sk_buff *skb) ...@@ -1262,7 +1263,9 @@ static void mld_sendpack(struct sk_buff *skb)
pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen, pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen,
IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0)); IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0));
dev_queue_xmit(skb); dev_queue_xmit(skb);
ICMP6_INC_STATS(Icmp6OutMsgs); ICMP6_INC_STATS(idev,Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
} }
static int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel) static int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel)
...@@ -1520,6 +1523,7 @@ static void mld_send_cr(struct inet6_dev *idev) ...@@ -1520,6 +1523,7 @@ static void mld_send_cr(struct inet6_dev *idev)
static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
{ {
struct sock *sk = igmp6_socket->sk; struct sock *sk = igmp6_socket->sk;
struct inet6_dev *idev;
struct sk_buff *skb; struct sk_buff *skb;
struct icmp6hdr *hdr; struct icmp6hdr *hdr;
struct in6_addr *snd_addr; struct in6_addr *snd_addr;
...@@ -1577,12 +1581,17 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) ...@@ -1577,12 +1581,17 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
IPPROTO_ICMPV6, IPPROTO_ICMPV6,
csum_partial((__u8 *) hdr, len, 0)); csum_partial((__u8 *) hdr, len, 0));
idev = in6_dev_get(skb->dev);
dev_queue_xmit(skb); dev_queue_xmit(skb);
if (type == ICMPV6_MGM_REDUCTION) if (type == ICMPV6_MGM_REDUCTION)
ICMP6_INC_STATS(Icmp6OutGroupMembReductions); ICMP6_INC_STATS(idev, Icmp6OutGroupMembReductions);
else else
ICMP6_INC_STATS(Icmp6OutGroupMembResponses); ICMP6_INC_STATS(idev, Icmp6OutGroupMembResponses);
ICMP6_INC_STATS(Icmp6OutMsgs); ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
return; return;
out: out:
......
...@@ -415,6 +415,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -415,6 +415,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
{ {
static struct in6_addr tmpaddr; static struct in6_addr tmpaddr;
struct inet6_ifaddr *ifp; struct inet6_ifaddr *ifp;
struct inet6_dev *idev;
struct flowi fl; struct flowi fl;
struct rt6_info *rt = NULL; struct rt6_info *rt = NULL;
struct dst_entry* dst; struct dst_entry* dst;
...@@ -497,10 +498,14 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -497,10 +498,14 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
dst_clone(dst); dst_clone(dst);
skb->dst = dst; skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb); dst_output(skb);
ICMP6_INC_STATS(Icmp6OutNeighborAdvertisements); ICMP6_INC_STATS(idev, Icmp6OutNeighborAdvertisements);
ICMP6_INC_STATS(Icmp6OutMsgs); ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
} }
void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
...@@ -510,6 +515,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, ...@@ -510,6 +515,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
struct flowi fl; struct flowi fl;
struct rt6_info *rt = NULL; struct rt6_info *rt = NULL;
struct dst_entry* dst; struct dst_entry* dst;
struct inet6_dev *idev;
struct sock *sk = ndisc_socket->sk; struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb; struct sk_buff *skb;
struct nd_msg *msg; struct nd_msg *msg;
...@@ -576,10 +582,14 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, ...@@ -576,10 +582,14 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
/* send it! */ /* send it! */
dst_clone(dst); dst_clone(dst);
skb->dst = dst; skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb); dst_output(skb);
ICMP6_INC_STATS(Icmp6OutNeighborSolicits); ICMP6_INC_STATS(idev, Icmp6OutNeighborSolicits);
ICMP6_INC_STATS(Icmp6OutMsgs); ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
} }
void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
...@@ -588,6 +598,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, ...@@ -588,6 +598,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
struct flowi fl; struct flowi fl;
struct rt6_info *rt = NULL; struct rt6_info *rt = NULL;
struct dst_entry* dst; struct dst_entry* dst;
struct inet6_dev *idev;
struct sock *sk = ndisc_socket->sk; struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb; struct sk_buff *skb;
struct icmp6hdr *hdr; struct icmp6hdr *hdr;
...@@ -644,10 +655,14 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, ...@@ -644,10 +655,14 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
/* send it! */ /* send it! */
dst_clone(dst); dst_clone(dst);
skb->dst = dst; skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb); dst_output(skb);
ICMP6_INC_STATS(Icmp6OutRouterSolicits); ICMP6_INC_STATS(idev, Icmp6OutRouterSolicits);
ICMP6_INC_STATS(Icmp6OutMsgs); ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
} }
...@@ -1271,6 +1286,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, ...@@ -1271,6 +1286,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
struct net_device *dev; struct net_device *dev;
struct rt6_info *rt; struct rt6_info *rt;
struct dst_entry *dst; struct dst_entry *dst;
struct inet6_dev *idev;
struct flowi fl; struct flowi fl;
u8 *opt; u8 *opt;
int rd_len; int rd_len;
...@@ -1379,10 +1395,14 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, ...@@ -1379,10 +1395,14 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
csum_partial((u8 *) icmph, len, 0)); csum_partial((u8 *) icmph, len, 0));
skb->dst = dst; skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb); dst_output(skb);
ICMP6_INC_STATS(Icmp6OutRedirects); ICMP6_INC_STATS(idev, Icmp6OutRedirects);
ICMP6_INC_STATS(Icmp6OutMsgs); ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
} }
static void pndisc_redo(struct sk_buff *skb) static void pndisc_redo(struct sk_buff *skb)
......
...@@ -10,12 +10,14 @@ ...@@ -10,12 +10,14 @@
* Version: $Id: proc.c,v 1.17 2002/02/01 22:01:04 davem Exp $ * Version: $Id: proc.c,v 1.17 2002/02/01 22:01:04 davem Exp $
* *
* Authors: David S. Miller (davem@caip.rutgers.edu) * Authors: David S. Miller (davem@caip.rutgers.edu)
* YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version * as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
*/ */
#include <linux/config.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/net.h> #include <linux/net.h>
...@@ -28,6 +30,10 @@ ...@@ -28,6 +30,10 @@
#include <net/transp_v6.h> #include <net/transp_v6.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_net_devsnmp6;
#endif
static int fold_prot_inuse(struct proto *proto) static int fold_prot_inuse(struct proto *proto)
{ {
int res = 0; int res = 0;
...@@ -53,14 +59,16 @@ static int sockstat6_seq_show(struct seq_file *seq, void *v) ...@@ -53,14 +59,16 @@ static int sockstat6_seq_show(struct seq_file *seq, void *v)
} }
static struct snmp6_item struct snmp6_item
{ {
char *name; char *name;
void **mib;
int offset; int offset;
} snmp6_list[] = { };
#define SNMP6_SENTINEL { .name = NULL, .offset = 0 }
static struct snmp6_item snmp6_ipv6_list[] = {
/* ipv6 mib according to draft-ietf-ipngwg-ipv6-mib-04 */ /* ipv6 mib according to draft-ietf-ipngwg-ipv6-mib-04 */
#define SNMP6_GEN(x) { #x , (void **)ipv6_statistics, offsetof(struct ipv6_mib, x) } #define SNMP6_GEN(x) { .name = #x , .offset = offsetof(struct ipv6_mib, x) }
SNMP6_GEN(Ip6InReceives), SNMP6_GEN(Ip6InReceives),
SNMP6_GEN(Ip6InHdrErrors), SNMP6_GEN(Ip6InHdrErrors),
SNMP6_GEN(Ip6InTooBigErrors), SNMP6_GEN(Ip6InTooBigErrors),
...@@ -84,6 +92,10 @@ static struct snmp6_item ...@@ -84,6 +92,10 @@ static struct snmp6_item
SNMP6_GEN(Ip6InMcastPkts), SNMP6_GEN(Ip6InMcastPkts),
SNMP6_GEN(Ip6OutMcastPkts), SNMP6_GEN(Ip6OutMcastPkts),
#undef SNMP6_GEN #undef SNMP6_GEN
SNMP6_SENTINEL
};
static struct snmp6_item snmp6_icmp6_list[] = {
/* icmpv6 mib according to draft-ietf-ipngwg-ipv6-icmp-mib-02 /* icmpv6 mib according to draft-ietf-ipngwg-ipv6-icmp-mib-02
Exceptions: {In|Out}AdminProhibs are removed, because I see Exceptions: {In|Out}AdminProhibs are removed, because I see
...@@ -94,7 +106,7 @@ static struct snmp6_item ...@@ -94,7 +106,7 @@ static struct snmp6_item
OutRouterAdvertisements too. OutRouterAdvertisements too.
OutGroupMembQueries too. OutGroupMembQueries too.
*/ */
#define SNMP6_GEN(x) { #x , (void **)icmpv6_statistics, offsetof(struct icmpv6_mib, x) } #define SNMP6_GEN(x) { .name = #x , .offset = offsetof(struct icmpv6_mib, x) }
SNMP6_GEN(Icmp6InMsgs), SNMP6_GEN(Icmp6InMsgs),
SNMP6_GEN(Icmp6InErrors), SNMP6_GEN(Icmp6InErrors),
SNMP6_GEN(Icmp6InDestUnreachs), SNMP6_GEN(Icmp6InDestUnreachs),
...@@ -124,12 +136,17 @@ static struct snmp6_item ...@@ -124,12 +136,17 @@ static struct snmp6_item
SNMP6_GEN(Icmp6OutGroupMembResponses), SNMP6_GEN(Icmp6OutGroupMembResponses),
SNMP6_GEN(Icmp6OutGroupMembReductions), SNMP6_GEN(Icmp6OutGroupMembReductions),
#undef SNMP6_GEN #undef SNMP6_GEN
#define SNMP6_GEN(x) { "Udp6" #x , (void **)udp_stats_in6, offsetof(struct udp_mib, Udp##x) } SNMP6_SENTINEL
};
static struct snmp6_item snmp6_udp6_list[] = {
#define SNMP6_GEN(x) { .name = "Udp6" #x , .offset = offsetof(struct udp_mib, Udp##x) }
SNMP6_GEN(InDatagrams), SNMP6_GEN(InDatagrams),
SNMP6_GEN(NoPorts), SNMP6_GEN(NoPorts),
SNMP6_GEN(InErrors), SNMP6_GEN(InErrors),
SNMP6_GEN(OutDatagrams) SNMP6_GEN(OutDatagrams),
#undef SNMP6_GEN #undef SNMP6_GEN
SNMP6_SENTINEL
}; };
static unsigned long static unsigned long
...@@ -151,18 +168,30 @@ fold_field(void *mib[], int offt) ...@@ -151,18 +168,30 @@ fold_field(void *mib[], int offt)
return res; return res;
} }
static int snmp6_seq_show(struct seq_file *seq, void *v) static inline void
snmp6_seq_show_item(struct seq_file *seq, void **mib, struct snmp6_item *itemlist)
{ {
int i; int i;
for (i=0; itemlist[i].name; i++)
seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name,
fold_field(mib, itemlist[i].offset));
}
for (i=0; i<sizeof(snmp6_list)/sizeof(snmp6_list[0]); i++) static int snmp6_seq_show(struct seq_file *seq, void *v)
seq_printf(seq, "%-32s\t%lu\n", snmp6_list[i].name, {
fold_field(snmp6_list[i].mib, snmp6_list[i].offset)); struct inet6_dev *idev = (struct inet6_dev *)seq->private;
if (idev) {
seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex);
snmp6_seq_show_item(seq, (void **)idev->stats.icmpv6, snmp6_icmp6_list);
} else {
snmp6_seq_show_item(seq, (void **)ipv6_statistics, snmp6_ipv6_list);
snmp6_seq_show_item(seq, (void **)icmpv6_statistics, snmp6_icmp6_list);
snmp6_seq_show_item(seq, (void **)udp_stats_in6, snmp6_udp6_list);
}
return 0; return 0;
} }
static int sockstat6_seq_open(struct inode *inode, struct file *file) static int sockstat6_seq_open(struct inode *inode, struct file *file)
{ {
return single_open(file, sockstat6_seq_show, NULL); return single_open(file, sockstat6_seq_show, NULL);
...@@ -177,7 +206,7 @@ static struct file_operations sockstat6_seq_fops = { ...@@ -177,7 +206,7 @@ static struct file_operations sockstat6_seq_fops = {
static int snmp6_seq_open(struct inode *inode, struct file *file) static int snmp6_seq_open(struct inode *inode, struct file *file)
{ {
return single_open(file, snmp6_seq_show, NULL); return single_open(file, snmp6_seq_show, PDE(inode)->data);
} }
static struct file_operations snmp6_seq_fops = { static struct file_operations snmp6_seq_fops = {
...@@ -187,6 +216,57 @@ static struct file_operations snmp6_seq_fops = { ...@@ -187,6 +216,57 @@ static struct file_operations snmp6_seq_fops = {
.release = single_release, .release = single_release,
}; };
int snmp6_register_dev(struct inet6_dev *idev)
{
int err = -ENOMEM;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *p;
#endif
if (!idev || !idev->dev)
return -EINVAL;
if (snmp6_mib_init((void **)idev->stats.icmpv6, sizeof(struct icmpv6_mib)) < 0)
goto err_icmp;
#ifdef CONFIG_PROC_FS
if (!proc_net_devsnmp6) {
err = -ENOENT;
goto err_proc;
}
p = create_proc_entry(idev->dev->name, S_IRUGO, proc_net_devsnmp6);
if (!p)
goto err_proc;
p->data = idev;
p->proc_fops = &snmp6_seq_fops;
idev->stats.proc_dir_entry = p;
#endif
return 0;
#ifdef CONFIG_PROC_FS
err_proc:
snmp6_mib_free((void **)idev->stats.icmpv6);
#endif
err_icmp:
return err;
}
int snmp6_unregister_dev(struct inet6_dev *idev)
{
#ifdef CONFIG_PROC_FS
if (!proc_net_devsnmp6)
return -ENOENT;
if (!idev || !idev->stats.proc_dir_entry)
return -EINVAL;
remove_proc_entry(idev->stats.proc_dir_entry->name,
proc_net_devsnmp6);
#endif
snmp6_mib_free((void **)idev->stats.icmpv6);
return 0;
}
int __init ipv6_misc_proc_init(void) int __init ipv6_misc_proc_init(void)
{ {
int rc = 0; int rc = 0;
...@@ -197,6 +277,9 @@ int __init ipv6_misc_proc_init(void) ...@@ -197,6 +277,9 @@ int __init ipv6_misc_proc_init(void)
goto proc_snmp6_fail; goto proc_snmp6_fail;
else else
p->proc_fops = &snmp6_seq_fops; p->proc_fops = &snmp6_seq_fops;
proc_net_devsnmp6 = proc_mkdir("dev_snmp6", proc_net);
if (!proc_net_devsnmp6)
goto proc_dev_snmp6_fail;
p = create_proc_entry("sockstat6", S_IRUGO, proc_net); p = create_proc_entry("sockstat6", S_IRUGO, proc_net);
if (!p) if (!p)
goto proc_sockstat6_fail; goto proc_sockstat6_fail;
...@@ -206,6 +289,8 @@ int __init ipv6_misc_proc_init(void) ...@@ -206,6 +289,8 @@ int __init ipv6_misc_proc_init(void)
return rc; return rc;
proc_sockstat6_fail: proc_sockstat6_fail:
remove_proc_entry("dev_snmp6", proc_net);
proc_dev_snmp6_fail:
remove_proc_entry("snmp6", proc_net); remove_proc_entry("snmp6", proc_net);
proc_snmp6_fail: proc_snmp6_fail:
rc = -ENOMEM; rc = -ENOMEM;
......
...@@ -751,7 +751,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -751,7 +751,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
sk = tcp_v6_lookup(&hdr->daddr, th->dest, &hdr->saddr, th->source, skb->dev->ifindex); sk = tcp_v6_lookup(&hdr->daddr, th->dest, &hdr->saddr, th->source, skb->dev->ifindex);
if (sk == NULL) { if (sk == NULL) {
ICMP6_INC_STATS_BH(Icmp6InErrors); ICMP6_INC_STATS_BH(__in6_dev_get(skb->dev), Icmp6InErrors);
return; return;
} }
......
...@@ -92,6 +92,7 @@ extern struct notifier_block sctp_inetaddr_notifier; ...@@ -92,6 +92,7 @@ extern struct notifier_block sctp_inetaddr_notifier;
void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int type, int code, int offset, __u32 info) int type, int code, int offset, __u32 info)
{ {
struct inet6_dev *idev;
struct ipv6hdr *iph = (struct ipv6hdr *)skb->data; struct ipv6hdr *iph = (struct ipv6hdr *)skb->data;
struct sctphdr *sh = (struct sctphdr *)(skb->data + offset); struct sctphdr *sh = (struct sctphdr *)(skb->data + offset);
struct sock *sk; struct sock *sk;
...@@ -102,6 +103,8 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -102,6 +103,8 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
char *saveip, *savesctp; char *saveip, *savesctp;
int err; int err;
idev = in6_dev_get(skb->dev);
/* Fix up skb to look at the embedded net header. */ /* Fix up skb to look at the embedded net header. */
saveip = skb->nh.raw; saveip = skb->nh.raw;
savesctp = skb->h.raw; savesctp = skb->h.raw;
...@@ -112,8 +115,8 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -112,8 +115,8 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
skb->nh.raw = saveip; skb->nh.raw = saveip;
skb->h.raw = savesctp; skb->h.raw = savesctp;
if (!sk) { if (!sk) {
ICMP6_INC_STATS_BH(Icmp6InErrors); ICMP6_INC_STATS_BH(idev, Icmp6InErrors);
return; goto out;
} }
/* Warning: The sock lock is held. Remember to call /* Warning: The sock lock is held. Remember to call
...@@ -139,6 +142,9 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -139,6 +142,9 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
out_unlock: out_unlock:
sctp_err_finish(sk, ep, asoc); sctp_err_finish(sk, ep, asoc);
out:
if (likely(idev != NULL))
in6_dev_put(idev);
} }
/* Based on tcp_v6_xmit() in tcp_ipv6.c. */ /* Based on tcp_v6_xmit() in tcp_ipv6.c. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment