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,
for (pos = (head)->next, n = pos->next; pos != (head); \
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.
* Mostly useful for hash tables where the two pointer list head is
......
......@@ -15,6 +15,8 @@
#ifndef _NET_IF_INET6_H
#define _NET_IF_INET6_H
#include <net/snmp.h>
#define IF_RA_RCVD 0x20
#define IF_RS_SENT 0x10
......@@ -152,6 +154,11 @@ struct ipv6_devconf
void *sysctl;
};
struct ipv6_devstat {
struct proc_dir_entry *proc_dir_entry;
DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6);
};
struct inet6_dev
{
struct net_device *dev;
......@@ -185,6 +192,7 @@ struct inet6_dev
struct neigh_parms *nd_parms;
struct inet6_dev *next;
struct ipv6_devconf cnf;
struct ipv6_devstat stats;
};
extern struct ipv6_devconf ipv6_devconf;
......
......@@ -106,24 +106,48 @@ struct frag_hdr {
/* sysctls */
extern int sysctl_ipv6_bindv6only;
/* MIBs */
DECLARE_SNMP_STAT(struct ipv6_mib, ipv6_statistics);
#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_USER(field) SNMP_INC_STATS_USER(ipv6_statistics, field)
DECLARE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics);
#define ICMP6_INC_STATS(field) SNMP_INC_STATS(icmpv6_statistics, field)
#define ICMP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(icmpv6_statistics, field)
#define ICMP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(icmpv6_statistics, field)
#define ICMP6_STATS_PTR_BH(field) \
(& \
((per_cpu_ptr(icmpv6_statistics[0], smp_processor_id()))-> \
field))
#define ICMP6_INC_STATS(idev, field) ({ \
struct inet6_dev *_idev = (idev); \
if (likely(_idev != NULL)) \
SNMP_INC_STATS(idev->stats.icmpv6, field); \
SNMP_INC_STATS(icmpv6_statistics, 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);
#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_USER(field) SNMP_INC_STATS_USER(udp_stats_in6, field)
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 *next;
......
......@@ -304,6 +304,8 @@ struct linux_mib
#define SNMP_INC_STATS_BH(mib, 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) \
(per_cpu_ptr(mib[1], smp_processor_id())->field++)
#define SNMP_INC_STATS(mib, field) \
......
......@@ -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)
{
struct net_bridge *br;
int ret;
br = dev->priv;
read_lock(&br->lock);
rcu_read_lock();
ret = __br_dev_xmit(skb, dev);
read_unlock(&br->lock);
rcu_read_unlock();
return ret;
}
static int br_dev_open(struct net_device *dev)
{
struct net_bridge *br;
netif_start_queue(dev);
br = dev->priv;
write_lock(&br->lock);
br_stp_enable_bridge(br);
write_unlock(&br->lock);
br_stp_enable_bridge(dev->priv);
return 0;
}
......@@ -105,12 +98,7 @@ static void br_dev_set_multicast_list(struct net_device *dev)
static int br_dev_stop(struct net_device *dev)
{
struct net_bridge *br;
br = dev->priv;
write_lock(&br->lock);
br_stp_disable_bridge(br);
write_unlock(&br->lock);
br_stp_disable_bridge(dev->priv);
netif_stop_queue(dev);
......
......@@ -21,7 +21,8 @@
#include <linux/netfilter_bridge.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 ||
p->state != BR_STATE_FORWARDING)
......@@ -52,7 +53,7 @@ int br_forward_finish(struct sk_buff *skb)
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;
#ifdef CONFIG_NETFILTER_DEBUG
......@@ -62,7 +63,7 @@ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
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;
......@@ -73,8 +74,8 @@ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
br_forward_finish);
}
/* called under bridge lock */
void br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
/* called with rcu_read_lock */
void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
{
if (should_deliver(to, skb)) {
__br_deliver(to, skb);
......@@ -84,8 +85,8 @@ void br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
kfree_skb(skb);
}
/* called under bridge lock */
void br_forward(struct net_bridge_port *to, struct sk_buff *skb)
/* called with rcu_read_lock */
void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
{
if (should_deliver(to, skb)) {
__br_forward(to, skb);
......@@ -97,7 +98,8 @@ void br_forward(struct net_bridge_port *to, struct sk_buff *skb)
/* called under bridge lock */
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 *prev;
......@@ -115,8 +117,7 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
prev = NULL;
p = br->port_list;
while (p != NULL) {
list_for_each_entry_rcu(p, &br->port_list, list) {
if (should_deliver(p, skb)) {
if (prev != NULL) {
struct sk_buff *skb2;
......@@ -132,8 +133,6 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
prev = p;
}
p = p->next;
}
if (prev != NULL) {
......@@ -144,7 +143,8 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
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)
{
br_flood(br, skb, clone, __br_deliver);
......
......@@ -18,8 +18,8 @@
#include <linux/if_bridge.h>
#include <linux/inetdevice.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rtnetlink.h>
#include <linux/brlock.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include "br_private.h"
......@@ -38,45 +38,39 @@ static int br_initial_port_cost(struct net_device *dev)
return 100;
}
/* called under BR_NETPROTO_LOCK and bridge lock */
static int __br_del_if(struct net_bridge *br, struct net_device *dev)
static void destroy_nbp(void *arg)
{
struct net_bridge_port *p;
struct net_bridge_port **pptr;
struct net_bridge_port *p = arg;
dev_put(p->dev);
kfree(p);
}
if ((p = dev->br_port) == NULL)
return -EINVAL;
/* called under bridge lock */
static void del_nbp(struct net_bridge_port *p)
{
struct net_device *dev = p->dev;
br_stp_disable_port(p);
dev_set_promiscuity(dev, -1);
dev->br_port = NULL;
pptr = &br->port_list;
while (*pptr != NULL) {
if (*pptr == p) {
*pptr = p->next;
break;
}
list_del_rcu(&p->list);
pptr = &((*pptr)->next);
}
br_fdb_delete_by_port(p->br, p);
br_fdb_delete_by_port(br, p);
kfree(p);
dev_put(dev);
return 0;
call_rcu(&p->rcu, destroy_nbp, p);
}
static void del_ifs(struct net_bridge *br)
{
br_write_lock_bh(BR_NETPROTO_LOCK);
write_lock(&br->lock);
while (br->port_list != NULL)
__br_del_if(br, br->port_list->dev);
write_unlock(&br->lock);
br_write_unlock_bh(BR_NETPROTO_LOCK);
struct list_head *p, *n;
spin_lock_bh(&br->lock);
list_for_each_safe(p, n, &br->port_list) {
del_nbp(list_entry(p, struct net_bridge_port, list));
}
spin_unlock_bh(&br->lock);
}
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);
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->bridge_id.prio[0] = 0x80;
......@@ -155,8 +150,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, struct net_device
br_init_port(p);
p->state = BR_STATE_DISABLED;
p->next = br->port_list;
br->port_list = p;
list_add_rcu(&p->list, &br->port_list);
return p;
}
......@@ -218,9 +212,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
return -ELOOP;
dev_hold(dev);
write_lock_bh(&br->lock);
spin_lock_bh(&br->lock);
if ((p = new_nbp(br, dev)) == NULL) {
write_unlock_bh(&br->lock);
spin_unlock_bh(&br->lock);
dev_put(dev);
return -EXFULL;
}
......@@ -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);
if ((br->dev.flags & IFF_UP) && (dev->flags & IFF_UP))
br_stp_enable_port(p);
write_unlock_bh(&br->lock);
spin_unlock_bh(&br->lock);
return 0;
}
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);
write_lock(&br->lock);
retval = __br_del_if(br, dev);
br_stp_recalculate_bridge_id(br);
write_unlock(&br->lock);
br_write_unlock_bh(BR_NETPROTO_LOCK);
spin_lock_bh(&br->lock);
if ((p = dev->br_port) == NULL || p->br != br)
retval = -EINVAL;
else {
del_nbp(p);
br_stp_recalculate_bridge_id(br);
}
spin_unlock_bh(&br->lock);
return retval;
}
......@@ -269,13 +266,11 @@ void br_get_port_ifindices(struct net_bridge *br, int *ifindices)
{
struct net_bridge_port *p;
read_lock(&br->lock);
p = br->port_list;
while (p != NULL) {
rcu_read_lock();
list_for_each_entry_rcu(p, &br->port_list, list) {
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)
dest = skb->mac.ethernet->h_dest;
rcu_read_lock();
p = skb->dev->br_port;
if (p == NULL)
goto err_nolock;
smp_read_barrier_depends();
br = p->br;
read_lock(&br->lock);
if (skb->dev->br_port == NULL)
goto err;
if (p == NULL || p->state == BR_STATE_DISABLED) {
kfree(skb);
goto out;
}
br = p->br;
passedup = 0;
if (br->dev.flags & IFF_PROMISC) {
struct sk_buff *skb2;
......@@ -105,35 +106,20 @@ int br_handle_frame_finish(struct sk_buff *skb)
br_flood_forward(br, skb, 0);
out:
read_unlock(&br->lock);
return 0;
err:
read_unlock(&br->lock);
err_nolock:
kfree_skb(skb);
rcu_read_unlock();
return 0;
}
int br_handle_frame(struct sk_buff *skb)
{
struct net_bridge *br;
unsigned char *dest;
struct net_bridge_port *p;
dest = skb->mac.ethernet->h_dest;
rcu_read_lock();
p = skb->dev->br_port;
if (p == NULL)
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)
if (p == NULL || p->state == BR_STATE_DISABLED)
goto err;
if (skb->mac.ethernet->h_source[0] & 1)
......@@ -141,39 +127,30 @@ int br_handle_frame(struct sk_buff *skb)
if (p->state == BR_STATE_LEARNING ||
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) &&
!(dest[5] & 0xF0))
goto handle_special_frame;
!(dest[5] & 0xF0)) {
if (!dest[5])
br_stp_handle_bpdu(skb);
goto err;
}
if (p->state == BR_STATE_FORWARDING) {
if (br_should_route_hook && br_should_route_hook(&skb)) {
read_unlock(&br->lock);
rcu_read_unlock();
return -1;
}
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish);
read_unlock(&br->lock);
rcu_read_unlock();
return 0;
}
err:
read_unlock(&br->lock);
err_nolock:
kfree_skb(skb);
return 0;
handle_special_frame:
if (!dest[5]) {
br_stp_handle_bpdu(skb);
read_unlock(&br->lock);
return 0;
}
rcu_read_unlock();
kfree_skb(skb);
read_unlock(&br->lock);
return 0;
}
......@@ -68,8 +68,8 @@ static int br_ioctl_device(struct net_bridge *br,
{
struct __bridge_info b;
read_lock(&br->lock);
memset(&b, 0, sizeof(struct __bridge_info));
rcu_read_lock();
memcpy(&b.designated_root, &br->designated_root, 8);
memcpy(&b.bridge_id, &br->bridge_id, 8);
b.root_path_cost = br->root_path_cost;
......@@ -89,7 +89,7 @@ static int br_ioctl_device(struct net_bridge *br,
b.tcn_timer_value = timer_residue(&br->tcn_timer);
b.topology_change_timer_value = timer_residue(&br->topology_change_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)))
return -EFAULT;
......@@ -116,27 +116,27 @@ static int br_ioctl_device(struct net_bridge *br,
}
case BRCTL_SET_BRIDGE_FORWARD_DELAY:
write_lock(&br->lock);
spin_lock_bh(&br->lock);
br->bridge_forward_delay = user_to_ticks(arg0);
if (br_is_root_bridge(br))
br->forward_delay = br->bridge_forward_delay;
write_unlock(&br->lock);
spin_unlock_bh(&br->lock);
return 0;
case BRCTL_SET_BRIDGE_HELLO_TIME:
write_lock(&br->lock);
spin_lock_bh(&br->lock);
br->bridge_hello_time = user_to_ticks(arg0);
if (br_is_root_bridge(br))
br->hello_time = br->bridge_hello_time;
write_unlock(&br->lock);
spin_unlock_bh(&br->lock);
return 0;
case BRCTL_SET_BRIDGE_MAX_AGE:
write_lock(&br->lock);
spin_lock_bh(&br->lock);
br->bridge_max_age = user_to_ticks(arg0);
if (br_is_root_bridge(br))
br->max_age = br->bridge_max_age;
write_unlock(&br->lock);
spin_unlock_bh(&br->lock);
return 0;
case BRCTL_SET_AGEING_TIME:
......@@ -152,9 +152,9 @@ static int br_ioctl_device(struct net_bridge *br,
struct __port_info p;
struct net_bridge_port *pt;
read_lock(&br->lock);
rcu_read_lock();
if ((pt = br_get_port(br, arg1)) == NULL) {
read_unlock(&br->lock);
rcu_read_unlock();
return -EINVAL;
}
......@@ -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.hold_timer_value = timer_residue(&pt->hold_timer);
read_unlock(&br->lock);
rcu_read_unlock();
if (copy_to_user((void *)arg0, &p, sizeof(p)))
return -EFAULT;
......@@ -185,9 +185,9 @@ static int br_ioctl_device(struct net_bridge *br,
return 0;
case BRCTL_SET_BRIDGE_PRIORITY:
write_lock(&br->lock);
spin_lock_bh(&br->lock);
br_stp_set_bridge_priority(br, arg0);
write_unlock(&br->lock);
spin_unlock_bh(&br->lock);
return 0;
case BRCTL_SET_PORT_PRIORITY:
......@@ -195,12 +195,12 @@ static int br_ioctl_device(struct net_bridge *br,
struct net_bridge_port *p;
int ret = 0;
write_lock(&br->lock);
spin_lock_bh(&br->lock);
if ((p = br_get_port(br, arg0)) == NULL)
ret = -EINVAL;
else
br_stp_set_port_priority(p, arg1);
write_unlock(&br->lock);
spin_unlock_bh(&br->lock);
return ret;
}
......@@ -209,12 +209,12 @@ static int br_ioctl_device(struct net_bridge *br,
struct net_bridge_port *p;
int ret = 0;
write_lock(&br->lock);
spin_lock_bh(&br->lock);
if ((p = br_get_port(br, arg0)) == NULL)
ret = -EINVAL;
else
br_stp_set_path_cost(p, arg1);
write_unlock(&br->lock);
spin_unlock_bh(&br->lock);
return ret;
}
......
......@@ -41,10 +41,10 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
switch (event)
{
case NETDEV_CHANGEADDR:
write_lock_bh(&br->lock);
spin_lock_bh(&br->lock);
br_fdb_changeaddr(p, dev->dev_addr);
br_stp_recalculate_bridge_id(br);
write_unlock_bh(&br->lock);
spin_unlock_bh(&br->lock);
break;
case NETDEV_GOING_DOWN:
......@@ -53,17 +53,17 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
case NETDEV_DOWN:
if (br->dev.flags & IFF_UP) {
write_lock_bh(&br->lock);
spin_lock_bh(&br->lock);
br_stp_disable_port(p);
write_unlock_bh(&br->lock);
spin_unlock_bh(&br->lock);
}
break;
case NETDEV_UP:
if (!(br->dev.flags & IFF_UP)) {
write_lock_bh(&br->lock);
spin_lock_bh(&br->lock);
br_stp_enable_port(p);
write_unlock_bh(&br->lock);
spin_unlock_bh(&br->lock);
}
break;
......
......@@ -55,9 +55,9 @@ struct net_bridge_fdb_entry
struct net_bridge_port
{
struct net_bridge_port *next;
struct net_bridge *br;
struct net_device *dev;
struct list_head list;
int port_no;
/* STP */
......@@ -75,12 +75,14 @@ struct net_bridge_port
struct br_timer forward_delay_timer;
struct br_timer hold_timer;
struct br_timer message_age_timer;
struct rcu_head rcu;
};
struct net_bridge
{
rwlock_t lock;
struct net_bridge_port *port_list;
spinlock_t lock;
struct list_head port_list;
struct net_device dev;
struct net_device_stats statistics;
rwlock_t hash_lock;
......@@ -137,10 +139,10 @@ extern void br_fdb_insert(struct net_bridge *br,
int is_local);
/* 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);
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);
extern int br_forward_finish(struct sk_buff *skb);
extern void br_flood_deliver(struct net_bridge *br,
......
......@@ -22,7 +22,7 @@
/* called under ioctl_lock or bridge lock */
/* called under bridge lock */
int br_is_root_bridge(struct net_bridge *br)
{
return !memcmp(&br->bridge_id, &br->designated_root, 8);
......@@ -35,17 +35,14 @@ int br_is_designated_port(struct net_bridge_port *p)
(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 *p;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->port_no == port_no)
return p;
p = p->next;
}
return NULL;
......@@ -109,12 +106,10 @@ static void br_root_selection(struct net_bridge *br)
root_port = 0;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (br_should_become_root_port(p, root_port))
root_port = p->port_no;
p = p->next;
}
br->root_port = root_port;
......@@ -241,13 +236,11 @@ static void br_designated_port_selection(struct net_bridge *br)
{
struct net_bridge_port *p;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED &&
br_should_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)
{
struct net_bridge_port *p;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED &&
br_is_designated_port(p))
br_transmit_config(p);
p = p->next;
}
}
......@@ -391,8 +381,7 @@ void br_port_state_selection(struct net_bridge *br)
{
struct net_bridge_port *p;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED) {
if (p->port_no == br->root_port) {
p->config_pending = 0;
......@@ -407,8 +396,6 @@ void br_port_state_selection(struct net_bridge *br)
br_make_blocking(p);
}
}
p = p->next;
}
}
......@@ -419,18 +406,13 @@ static void br_topology_change_acknowledge(struct net_bridge_port *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)
{
struct net_bridge *br;
int was_root;
if (p->state == BR_STATE_DISABLED)
return;
br = p->br;
read_lock(&br->lock);
was_root = br_is_root_bridge(br);
if (br_supersedes_port_info(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
} else if (br_is_designated_port(p)) {
br_reply(p);
}
read_unlock(&br->lock);
}
/* lock-safe */
/* called under bridge lock */
void br_received_tcn_bpdu(struct net_bridge_port *p)
{
read_lock(&p->br->lock);
if (p->state != BR_STATE_DISABLED &&
br_is_designated_port(p)) {
if (br_is_designated_port(p)) {
printk(KERN_INFO "%s: received tcn bpdu on port %i(%s)\n",
p->br->dev.name, p->port_no, p->dev->name);
br_topology_change_detection(p->br);
br_topology_change_acknowledge(p);
}
read_unlock(&p->br->lock);
}
......@@ -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};
/* called under bridge lock */
/* NO locks */
void br_stp_handle_bpdu(struct sk_buff *skb)
{
unsigned char *buf;
struct net_bridge_port *p;
struct net_bridge *br;
buf = skb->mac.raw + 14;
p = skb->dev->br_port;
if (!p->br->stp_enabled || memcmp(buf, header, 6)) {
kfree_skb(skb);
return;
}
br = p->br;
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) {
struct br_config_bpdu bpdu;
......@@ -178,16 +183,14 @@ void br_stp_handle_bpdu(struct sk_buff *skb)
bpdu.hello_time = br_get_ticks(buf+34);
bpdu.forward_delay = br_get_ticks(buf+36);
kfree_skb(skb);
br_received_config_bpdu(p, &bpdu);
return;
goto out;
}
if (buf[6] == BPDU_TYPE_TCN) {
br_received_tcn_bpdu(p);
kfree_skb(skb);
return;
goto out;
}
kfree_skb(skb);
out:
spin_unlock_bh(&br->lock);
}
......@@ -44,6 +44,7 @@ void br_stp_enable_bridge(struct net_bridge *br)
struct net_bridge_port *p;
struct timer_list *timer = &br->tick;
spin_lock_bh(&br->lock);
init_timer(timer);
timer->data = (unsigned long) br;
timer->function = br_tick;
......@@ -53,22 +54,21 @@ void br_stp_enable_bridge(struct net_bridge *br)
br_timer_set(&br->hello_timer, jiffies);
br_config_bpdu_generation(br);
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->dev->flags & IFF_UP)
br_stp_enable_port(p);
p = p->next;
}
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)
{
struct net_bridge_port *p;
spin_lock_bh(&br->lock);
br->topology_change = 0;
br->topology_change_detected = 0;
br_timer_clear(&br->hello_timer);
......@@ -77,13 +77,11 @@ void br_stp_disable_bridge(struct net_bridge *br)
br_timer_clear(&br->gc_timer);
br_fdb_cleanup(br);
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED)
br_stp_disable_port(p);
p = p->next;
}
spin_unlock_bh(&br->lock);
del_timer_sync(&br->tick);
}
......@@ -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->dev.dev_addr, addr, ETH_ALEN);
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN))
memcpy(p->designated_bridge.addr, addr, ETH_ALEN);
if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN))
memcpy(p->designated_root.addr, addr, ETH_ALEN);
p = p->next;
}
br_configuration_update(br);
......@@ -160,13 +156,11 @@ void br_stp_recalculate_bridge_id(struct net_bridge *br)
addr = br_mac_zero;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (addr == br_mac_zero ||
memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)
addr = p->dev->dev_addr;
p = p->next;
}
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)
wasroot = br_is_root_bridge(br);
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED &&
br_is_designated_port(p)) {
p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF;
p->designated_bridge.prio[1] = newprio & 0xFF;
}
p = p->next;
}
br->bridge_id.prio[0] = (newprio >> 8) & 0xFF;
......
......@@ -32,13 +32,10 @@ static int br_is_designated_for_some_port(struct net_bridge *br)
{
struct net_bridge_port *p;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED &&
!memcmp(&p->designated_bridge, &br->bridge_id, 8))
return 1;
p = p->next;
}
return 0;
......@@ -162,12 +159,9 @@ static void br_check_timers(struct net_bridge *br)
br_topology_change_timer_expired(br);
}
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED)
br_check_port_timers(p);
p = p->next;
}
}
......@@ -175,10 +169,10 @@ void br_tick(unsigned long __data)
{
struct net_bridge *br = (struct net_bridge *)__data;
read_lock(&br->lock);
br_check_timers(br);
read_unlock(&br->lock);
if (spin_trylock_bh(&br->lock)) {
br_check_timers(br);
spin_unlock_bh(&br->lock);
}
br->tick.expires = jiffies + 1;
add_timer(&br->tick);
}
......@@ -300,6 +300,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)
printk("Freeing alive inet6 device %p\n", idev);
return;
}
snmp6_unregister_dev(idev);
inet6_dev_count--;
kfree(idev);
}
......@@ -332,6 +333,15 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
/* We refer to the device */
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
get_random_bytes(ndev->rndid, sizeof(ndev->rndid));
get_random_bytes(ndev->entropy, sizeof(ndev->entropy));
......
......@@ -631,79 +631,72 @@ inet6_unregister_protosw(struct inet_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;
ipv6_statistics[0] = kmalloc_percpu(sizeof (struct ipv6_mib),
GFP_KERNEL);
if (!ipv6_statistics[0])
goto err_ip_mib0;
ipv6_statistics[1] = kmalloc_percpu(sizeof (struct ipv6_mib),
GFP_KERNEL);
if (!ipv6_statistics[1])
goto err_ip_mib1;
icmpv6_statistics[0] = kmalloc_percpu(sizeof (struct icmpv6_mib),
GFP_KERNEL);
if (!icmpv6_statistics[0])
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 */
if (ptr == NULL)
return -EINVAL;
ptr[0] = kmalloc_percpu(mibsize, GFP_KERNEL);
if (!ptr[0])
goto err0;
ptr[1] = kmalloc_percpu(mibsize, GFP_KERNEL);
if (!ptr[1])
goto err1;
/* Zero percpu version of the mibs */
for (i = 0; i < NR_CPUS; i++) {
if (cpu_possible(i)) {
memset(per_cpu_ptr(ipv6_statistics[0], i), 0,
sizeof (struct ipv6_mib));
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));
memset(per_cpu_ptr(ptr[0], i), 0, mibsize);
memset(per_cpu_ptr(ptr[1], i), 0, mibsize);
}
}
return 0;
err_udp_mib1:
kfree_percpu(udp_stats_in6[0]);
err_udp_mib0:
kfree_percpu(icmpv6_statistics[1]);
err_icmp_mib1:
kfree_percpu(icmpv6_statistics[0]);
err_icmp_mib0:
kfree_percpu(ipv6_statistics[1]);
err_ip_mib1:
kfree_percpu(ipv6_statistics[0]);
err_ip_mib0:
err1:
kfree_percpu(ptr[0]);
ptr[0] = NULL;
err0:
return -ENOMEM;
}
void
snmp6_mib_free(void *ptr[2])
{
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;
}
static void cleanup_ipv6_mibs(void)
{
kfree_percpu(ipv6_statistics[0]);
kfree_percpu(ipv6_statistics[1]);
kfree_percpu(icmpv6_statistics[0]);
kfree_percpu(icmpv6_statistics[1]);
kfree_percpu(udp_stats_in6[0]);
kfree_percpu(udp_stats_in6[1]);
snmp6_mib_free((void **)ipv6_statistics);
snmp6_mib_free((void **)icmpv6_statistics);
snmp6_mib_free((void **)udp_stats_in6);
}
extern int ipv6_misc_proc_init(void);
......@@ -819,6 +812,7 @@ static int __init inet6_init(void)
#ifdef CONFIG_PROC_FS
proc_anycast6_fail:
proc_net_remove("snmp6");
proc_net_remove("dev_snmp6");
proc_net_remove("sockstat6");
proc_misc6_fail:
proc_net_remove("udp6");
......@@ -854,6 +848,7 @@ static void inet6_exit(void)
proc_net_remove("tcp6");
proc_net_remove("udp6");
proc_net_remove("sockstat6");
proc_net_remove("dev_snmp6");
proc_net_remove("snmp6");
proc_net_remove("anycast6");
#endif
......
......@@ -26,6 +26,8 @@
* yoshfuji : ensure to sent parameter problem for
* fragments.
* YOSHIFUJI Hideaki @USAGI: added sysctl for icmp rate limit.
* Randy Dunlap and
* YOSHIFUJI Hideaki @USAGI: Per-interface statistics support
*/
#include <linux/module.h>
......@@ -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,
struct net_device *dev)
{
struct inet6_dev *idev;
struct ipv6hdr *hdr = skb->nh.ipv6h;
struct sock *sk = icmpv6_socket->sk;
struct in6_addr *saddr = NULL;
......@@ -351,11 +354,16 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
msg.len = len;
idev = in6_dev_get(skb->dev);
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
MSG_DONTWAIT);
if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
ICMP6_STATS_PTR_BH(Icmp6OutDestUnreachs) [type-ICMPV6_DEST_UNREACH]++;
ICMP6_INC_STATS_BH(Icmp6OutMsgs);
ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6OutDestUnreachs, type - ICMPV6_DEST_UNREACH);
ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
out:
icmpv6_xmit_unlock();
}
......@@ -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)
{
struct sock *sk = icmpv6_socket->sk;
struct inet6_dev *idev;
struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
struct in6_addr *saddr;
struct icmpv6_msg msg;
......@@ -394,14 +403,19 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
fl.fl_icmp_type = ICMPV6_ECHO_REPLY;
fl.fl_icmp_code = 0;
idev = in6_dev_get(skb->dev);
icmpv6_xmit_lock();
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1,
MSG_DONTWAIT);
ICMP6_INC_STATS_BH(Icmp6OutEchoReplies);
ICMP6_INC_STATS_BH(Icmp6OutMsgs);
ICMP6_INC_STATS_BH(idev, Icmp6OutEchoReplies);
ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs);
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)
......@@ -464,12 +478,13 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
{
struct sk_buff *skb = *pskb;
struct net_device *dev = skb->dev;
struct inet6_dev *idev = __in6_dev_get(dev);
struct in6_addr *saddr, *daddr;
struct ipv6hdr *orig_hdr;
struct icmp6hdr *hdr;
int type;
ICMP6_INC_STATS_BH(Icmp6InMsgs);
ICMP6_INC_STATS_BH(idev, Icmp6InMsgs);
saddr = &skb->nh.ipv6h->saddr;
daddr = &skb->nh.ipv6h->daddr;
......@@ -517,9 +532,9 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
type = hdr->icmp6_type;
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)
ICMP6_STATS_PTR_BH(Icmp6InEchos)[type-ICMPV6_ECHO_REQUEST]++;
ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6InEchos, type - ICMPV6_ECHO_REQUEST);
switch (type) {
case ICMPV6_ECHO_REQUEST:
......@@ -597,7 +612,7 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
return 0;
discard_it:
ICMP6_INC_STATS_BH(Icmp6InErrors);
ICMP6_INC_STATS_BH(idev, Icmp6InErrors);
kfree_skb(skb);
return 0;
}
......
......@@ -34,5 +34,6 @@ EXPORT_SYMBOL(ipv6_get_saddr);
EXPORT_SYMBOL(ipv6_chk_addr);
EXPORT_SYMBOL(in6addr_any);
EXPORT_SYMBOL(in6addr_loopback);
EXPORT_SYMBOL(in6_dev_finish_destroy);
EXPORT_SYMBOL(xfrm6_rcv);
EXPORT_SYMBOL(xfrm6_clear_mutable_options);
......@@ -1253,6 +1253,7 @@ static void mld_sendpack(struct sk_buff *skb)
struct ipv6hdr *pip6 = skb->nh.ipv6h;
struct mld2_report *pmr = (struct mld2_report *)skb->h.raw;
int payload_len, mldlen;
struct inet6_dev *idev = in6_dev_get(skb->dev);
payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h -
sizeof(struct ipv6hdr);
......@@ -1262,7 +1263,9 @@ static void mld_sendpack(struct sk_buff *skb)
pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen,
IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0));
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)
......@@ -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)
{
struct sock *sk = igmp6_socket->sk;
struct inet6_dev *idev;
struct sk_buff *skb;
struct icmp6hdr *hdr;
struct in6_addr *snd_addr;
......@@ -1577,12 +1581,17 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
IPPROTO_ICMPV6,
csum_partial((__u8 *) hdr, len, 0));
idev = in6_dev_get(skb->dev);
dev_queue_xmit(skb);
if (type == ICMPV6_MGM_REDUCTION)
ICMP6_INC_STATS(Icmp6OutGroupMembReductions);
ICMP6_INC_STATS(idev, Icmp6OutGroupMembReductions);
else
ICMP6_INC_STATS(Icmp6OutGroupMembResponses);
ICMP6_INC_STATS(Icmp6OutMsgs);
ICMP6_INC_STATS(idev, Icmp6OutGroupMembResponses);
ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
return;
out:
......
......@@ -415,6 +415,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
{
static struct in6_addr tmpaddr;
struct inet6_ifaddr *ifp;
struct inet6_dev *idev;
struct flowi fl;
struct rt6_info *rt = NULL;
struct dst_entry* dst;
......@@ -497,10 +498,14 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
dst_clone(dst);
skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb);
ICMP6_INC_STATS(Icmp6OutNeighborAdvertisements);
ICMP6_INC_STATS(Icmp6OutMsgs);
ICMP6_INC_STATS(idev, Icmp6OutNeighborAdvertisements);
ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
}
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 rt6_info *rt = NULL;
struct dst_entry* dst;
struct inet6_dev *idev;
struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
struct nd_msg *msg;
......@@ -576,10 +582,14 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
/* send it! */
dst_clone(dst);
skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb);
ICMP6_INC_STATS(Icmp6OutNeighborSolicits);
ICMP6_INC_STATS(Icmp6OutMsgs);
ICMP6_INC_STATS(idev, Icmp6OutNeighborSolicits);
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,
......@@ -588,6 +598,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
struct flowi fl;
struct rt6_info *rt = NULL;
struct dst_entry* dst;
struct inet6_dev *idev;
struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
struct icmp6hdr *hdr;
......@@ -644,10 +655,14 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
/* send it! */
dst_clone(dst);
skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb);
ICMP6_INC_STATS(Icmp6OutRouterSolicits);
ICMP6_INC_STATS(Icmp6OutMsgs);
ICMP6_INC_STATS(idev, Icmp6OutRouterSolicits);
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,
struct net_device *dev;
struct rt6_info *rt;
struct dst_entry *dst;
struct inet6_dev *idev;
struct flowi fl;
u8 *opt;
int rd_len;
......@@ -1379,10 +1395,14 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
csum_partial((u8 *) icmph, len, 0));
skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb);
ICMP6_INC_STATS(Icmp6OutRedirects);
ICMP6_INC_STATS(Icmp6OutMsgs);
ICMP6_INC_STATS(idev, Icmp6OutRedirects);
ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
}
static void pndisc_redo(struct sk_buff *skb)
......
......@@ -10,12 +10,14 @@
* Version: $Id: proc.c,v 1.17 2002/02/01 22:01:04 davem Exp $
*
* 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
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/socket.h>
#include <linux/net.h>
......@@ -28,6 +30,10 @@
#include <net/transp_v6.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)
{
int res = 0;
......@@ -53,14 +59,16 @@ static int sockstat6_seq_show(struct seq_file *seq, void *v)
}
static struct snmp6_item
struct snmp6_item
{
char *name;
void **mib;
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 */
#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(Ip6InHdrErrors),
SNMP6_GEN(Ip6InTooBigErrors),
......@@ -84,6 +92,10 @@ static struct snmp6_item
SNMP6_GEN(Ip6InMcastPkts),
SNMP6_GEN(Ip6OutMcastPkts),
#undef SNMP6_GEN
SNMP6_SENTINEL
};
static struct snmp6_item snmp6_icmp6_list[] = {
/* icmpv6 mib according to draft-ietf-ipngwg-ipv6-icmp-mib-02
Exceptions: {In|Out}AdminProhibs are removed, because I see
......@@ -94,7 +106,7 @@ static struct snmp6_item
OutRouterAdvertisements 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(Icmp6InErrors),
SNMP6_GEN(Icmp6InDestUnreachs),
......@@ -124,12 +136,17 @@ static struct snmp6_item
SNMP6_GEN(Icmp6OutGroupMembResponses),
SNMP6_GEN(Icmp6OutGroupMembReductions),
#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(NoPorts),
SNMP6_GEN(InErrors),
SNMP6_GEN(OutDatagrams)
SNMP6_GEN(OutDatagrams),
#undef SNMP6_GEN
SNMP6_SENTINEL
};
static unsigned long
......@@ -151,18 +168,30 @@ fold_field(void *mib[], int offt)
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;
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++)
seq_printf(seq, "%-32s\t%lu\n", snmp6_list[i].name,
fold_field(snmp6_list[i].mib, snmp6_list[i].offset));
static int snmp6_seq_show(struct seq_file *seq, void *v)
{
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;
}
static int sockstat6_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, sockstat6_seq_show, NULL);
......@@ -177,7 +206,7 @@ static struct file_operations sockstat6_seq_fops = {
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 = {
......@@ -187,6 +216,57 @@ static struct file_operations snmp6_seq_fops = {
.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 rc = 0;
......@@ -197,6 +277,9 @@ int __init ipv6_misc_proc_init(void)
goto proc_snmp6_fail;
else
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);
if (!p)
goto proc_sockstat6_fail;
......@@ -206,6 +289,8 @@ int __init ipv6_misc_proc_init(void)
return rc;
proc_sockstat6_fail:
remove_proc_entry("dev_snmp6", proc_net);
proc_dev_snmp6_fail:
remove_proc_entry("snmp6", proc_net);
proc_snmp6_fail:
rc = -ENOMEM;
......
......@@ -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);
if (sk == NULL) {
ICMP6_INC_STATS_BH(Icmp6InErrors);
ICMP6_INC_STATS_BH(__in6_dev_get(skb->dev), Icmp6InErrors);
return;
}
......
......@@ -92,6 +92,7 @@ extern struct notifier_block sctp_inetaddr_notifier;
void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int type, int code, int offset, __u32 info)
{
struct inet6_dev *idev;
struct ipv6hdr *iph = (struct ipv6hdr *)skb->data;
struct sctphdr *sh = (struct sctphdr *)(skb->data + offset);
struct sock *sk;
......@@ -102,6 +103,8 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
char *saveip, *savesctp;
int err;
idev = in6_dev_get(skb->dev);
/* Fix up skb to look at the embedded net header. */
saveip = skb->nh.raw;
savesctp = skb->h.raw;
......@@ -112,8 +115,8 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
skb->nh.raw = saveip;
skb->h.raw = savesctp;
if (!sk) {
ICMP6_INC_STATS_BH(Icmp6InErrors);
return;
ICMP6_INC_STATS_BH(idev, Icmp6InErrors);
goto out;
}
/* 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,
out_unlock:
sctp_err_finish(sk, ep, asoc);
out:
if (likely(idev != NULL))
in6_dev_put(idev);
}
/* 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