* [PATCH 0/7] bridge enhancements for net-next @ 2011-04-05 0:03 Stephen Hemminger 2011-04-05 0:03 ` [PATCH 1/7] bridge: change arguments to fdb_create Stephen Hemminger ` (7 more replies) 0 siblings, 8 replies; 12+ messages in thread From: Stephen Hemminger @ 2011-04-05 0:03 UTC (permalink / raw) To: David S. Miller; +Cc: netdev These patches add more netlink support for bridge. It is possible to do basic configuration bridge with just netlink. Later enhancements will add statistics and parameters. The intention is to switch to pure netlink in future and support RSTP and deprecate the old ioctl, sysfs and STP code. ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/7] bridge: change arguments to fdb_create 2011-04-05 0:03 [PATCH 0/7] bridge enhancements for net-next Stephen Hemminger @ 2011-04-05 0:03 ` Stephen Hemminger 2011-04-05 0:03 ` [PATCH 2/7] bridge: track last used time in forwarding table Stephen Hemminger ` (6 subsequent siblings) 7 siblings, 0 replies; 12+ messages in thread From: Stephen Hemminger @ 2011-04-05 0:03 UTC (permalink / raw) To: David S. Miller; +Cc: netdev [-- Attachment #1: br-fdb-reorg.patch --] [-- Type: text/plain, Size: 1772 bytes --] Later patch provides ability to create non-local static entry. To make this easier move the updating of the flag values to after the code that creates entry. Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> --- a/net/bridge/br_fdb.c 2011-03-21 09:04:33.000000000 -0700 +++ b/net/bridge/br_fdb.c 2011-03-21 10:36:09.359041121 -0700 @@ -320,8 +320,7 @@ static inline struct net_bridge_fdb_entr static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, struct net_bridge_port *source, - const unsigned char *addr, - int is_local) + const unsigned char *addr) { struct net_bridge_fdb_entry *fdb; @@ -329,10 +328,9 @@ static struct net_bridge_fdb_entry *fdb_ if (fdb) { memcpy(fdb->addr.addr, addr, ETH_ALEN); fdb->dst = source; - fdb->is_local = is_local; - fdb->is_static = is_local; + fdb->is_local = 0; + fdb->is_static = 0; fdb->ageing_timer = jiffies; - hlist_add_head_rcu(&fdb->hlist, head); } return fdb; @@ -360,12 +358,15 @@ static int fdb_insert(struct net_bridge fdb_delete(fdb); } - if (!fdb_create(head, source, addr, 1)) + fdb = fdb_create(head, source, addr); + if (!fdb) return -ENOMEM; + fdb->is_local = fdb->is_static = 1; return 0; } +/* Add entry for local address of interface */ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, const unsigned char *addr) { @@ -407,8 +408,9 @@ void br_fdb_update(struct net_bridge *br } } else { spin_lock(&br->hash_lock); - if (!fdb_find(head, addr)) - fdb_create(head, source, addr, 0); + if (likely(!fdb_find(head, addr))) + fdb_create(head, source, addr); + /* else we lose race and someone else inserts * it first, don't bother updating */ ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 2/7] bridge: track last used time in forwarding table 2011-04-05 0:03 [PATCH 0/7] bridge enhancements for net-next Stephen Hemminger 2011-04-05 0:03 ` [PATCH 1/7] bridge: change arguments to fdb_create Stephen Hemminger @ 2011-04-05 0:03 ` Stephen Hemminger 2011-04-05 0:03 ` [PATCH 3/7] bridge: split rcu and no-rcu cases of fdb lookup Stephen Hemminger ` (5 subsequent siblings) 7 siblings, 0 replies; 12+ messages in thread From: Stephen Hemminger @ 2011-04-05 0:03 UTC (permalink / raw) To: David S. Miller; +Cc: netdev [-- Attachment #1: br-fdb-used.patch --] [-- Type: text/plain, Size: 2660 bytes --] Adds tracking the last used time in forwarding table. Rename ageing_timer to updated to better describe it. Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> --- net/bridge/br_fdb.c | 10 +++++----- net/bridge/br_input.c | 5 +++-- net/bridge/br_private.h | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) --- a/net/bridge/br_fdb.c 2011-04-03 09:39:13.000000000 -0700 +++ b/net/bridge/br_fdb.c 2011-04-03 09:39:21.221199041 -0700 @@ -62,7 +62,7 @@ static inline int has_expired(const stru const struct net_bridge_fdb_entry *fdb) { return !fdb->is_static && - time_before_eq(fdb->ageing_timer + hold_time(br), jiffies); + time_before_eq(fdb->updated + hold_time(br), jiffies); } static inline int br_mac_hash(const unsigned char *mac) @@ -140,7 +140,7 @@ void br_fdb_cleanup(unsigned long _data) unsigned long this_timer; if (f->is_static) continue; - this_timer = f->ageing_timer + delay; + this_timer = f->updated + delay; if (time_before_eq(this_timer, jiffies)) fdb_delete(f); else if (time_before(this_timer, next_timer)) @@ -293,7 +293,7 @@ int br_fdb_fillbuf(struct net_bridge *br fe->is_local = f->is_local; if (!f->is_static) - fe->ageing_timer_value = jiffies_to_clock_t(jiffies - f->ageing_timer); + fe->ageing_timer_value = jiffies_to_clock_t(jiffies - f->updated); ++fe; ++num; } @@ -330,7 +330,7 @@ static struct net_bridge_fdb_entry *fdb_ fdb->dst = source; fdb->is_local = 0; fdb->is_static = 0; - fdb->ageing_timer = jiffies; + fdb->updated = fdb->used = jiffies; hlist_add_head_rcu(&fdb->hlist, head); } return fdb; @@ -404,7 +404,7 @@ void br_fdb_update(struct net_bridge *br } else { /* fastpath: update of existing entry */ fdb->dst = source; - fdb->ageing_timer = jiffies; + fdb->updated = jiffies; } } else { spin_lock(&br->hash_lock); --- a/net/bridge/br_input.c 2011-04-01 11:30:16.000000000 -0700 +++ b/net/bridge/br_input.c 2011-04-03 09:39:21.221199041 -0700 @@ -98,9 +98,10 @@ int br_handle_frame_finish(struct sk_buf } if (skb) { - if (dst) + if (dst) { + dst->used = jiffies; br_forward(dst->dst, skb, skb2); - else + } else br_flood_forward(br, skb, skb2); } --- a/net/bridge/br_private.h 2011-04-01 11:30:16.000000000 -0700 +++ b/net/bridge/br_private.h 2011-04-03 09:39:21.221199041 -0700 @@ -64,7 +64,8 @@ struct net_bridge_fdb_entry struct net_bridge_port *dst; struct rcu_head rcu; - unsigned long ageing_timer; + unsigned long updated; + unsigned long used; mac_addr addr; unsigned char is_local; unsigned char is_static; ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 3/7] bridge: split rcu and no-rcu cases of fdb lookup 2011-04-05 0:03 [PATCH 0/7] bridge enhancements for net-next Stephen Hemminger 2011-04-05 0:03 ` [PATCH 1/7] bridge: change arguments to fdb_create Stephen Hemminger 2011-04-05 0:03 ` [PATCH 2/7] bridge: track last used time in forwarding table Stephen Hemminger @ 2011-04-05 0:03 ` Stephen Hemminger 2011-04-05 0:03 ` [PATCH 4/7] bridge: add netlink notification on forward entry changes Stephen Hemminger ` (4 subsequent siblings) 7 siblings, 0 replies; 12+ messages in thread From: Stephen Hemminger @ 2011-04-05 0:03 UTC (permalink / raw) To: David S. Miller; +Cc: netdev [-- Attachment #1: br-fdb-norcu.patch --] [-- Type: text/plain, Size: 1349 bytes --] In some cases, look up of forward database entry is done with RCU; and for others no RCU is needed because of locking. Split the two cases into two differnt loops (and take off inline). Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> --- a/net/bridge/br_fdb.c 2011-03-21 10:36:51.109460181 -0700 +++ b/net/bridge/br_fdb.c 2011-03-21 10:37:11.872443564 -0700 @@ -305,8 +305,21 @@ int br_fdb_fillbuf(struct net_bridge *br return num; } -static inline struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, - const unsigned char *addr) +static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, + const unsigned char *addr) +{ + struct hlist_node *h; + struct net_bridge_fdb_entry *fdb; + + hlist_for_each_entry(fdb, h, head, hlist) { + if (!compare_ether_addr(fdb->addr.addr, addr)) + return fdb; + } + return NULL; +} + +static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head, + const unsigned char *addr) { struct hlist_node *h; struct net_bridge_fdb_entry *fdb; @@ -393,7 +406,7 @@ void br_fdb_update(struct net_bridge *br source->state == BR_STATE_FORWARDING)) return; - fdb = fdb_find(head, addr); + fdb = fdb_find_rcu(head, addr); if (likely(fdb)) { /* attempt to update an entry for a local interface */ if (unlikely(fdb->is_local)) { ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 4/7] bridge: add netlink notification on forward entry changes 2011-04-05 0:03 [PATCH 0/7] bridge enhancements for net-next Stephen Hemminger ` (2 preceding siblings ...) 2011-04-05 0:03 ` [PATCH 3/7] bridge: split rcu and no-rcu cases of fdb lookup Stephen Hemminger @ 2011-04-05 0:03 ` Stephen Hemminger 2011-04-05 0:03 ` [PATCH 5/7] bridge: allow creating/deleting fdb entries via netlink Stephen Hemminger ` (3 subsequent siblings) 7 siblings, 0 replies; 12+ messages in thread From: Stephen Hemminger @ 2011-04-05 0:03 UTC (permalink / raw) To: David S. Miller; +Cc: netdev [-- Attachment #1: br-fdb-notify.patch --] [-- Type: text/plain, Size: 5256 bytes --] This allows applications to query and monitor bridge forwarding table in the same method used for neighbor table. The forward table entries are returned in same structure format as used by the ioctl. If more information is desired in future, the netlink method is extensible. Example (using bridge extensions to iproute2) # br monitor Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> --- net/bridge/br_fdb.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++ net/bridge/br_netlink.c | 1 net/bridge/br_private.h | 1 3 files changed, 127 insertions(+) --- a/net/bridge/br_fdb.c 2011-03-21 10:37:11.872443564 -0700 +++ b/net/bridge/br_fdb.c 2011-03-21 13:00:35.370484883 -0700 @@ -28,6 +28,7 @@ static struct kmem_cache *br_fdb_cache __read_mostly; static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, const unsigned char *addr); +static void fdb_notify(const struct net_bridge_fdb_entry *, int); static u32 fdb_salt __read_mostly; @@ -81,6 +82,7 @@ static void fdb_rcu_free(struct rcu_head static inline void fdb_delete(struct net_bridge_fdb_entry *f) { + fdb_notify(f, RTM_DELNEIGH); hlist_del_rcu(&f->hlist); call_rcu(&f->rcu, fdb_rcu_free); } @@ -345,6 +347,7 @@ static struct net_bridge_fdb_entry *fdb_ fdb->is_static = 0; fdb->updated = fdb->used = jiffies; hlist_add_head_rcu(&fdb->hlist, head); + fdb_notify(fdb, RTM_NEWNEIGH); } return fdb; } @@ -430,3 +433,125 @@ void br_fdb_update(struct net_bridge *br spin_unlock(&br->hash_lock); } } + +static int fdb_to_nud(const struct net_bridge_fdb_entry *fdb) +{ + if (fdb->is_local) + return NUD_PERMANENT; + else if (fdb->is_static) + return NUD_NOARP; + else if (has_expired(fdb->dst->br, fdb)) + return NUD_STALE; + else + return NUD_REACHABLE; +} + +static int fdb_fill_info(struct sk_buff *skb, + const struct net_bridge_fdb_entry *fdb, + u32 pid, u32 seq, int type, unsigned int flags) +{ + unsigned long now = jiffies; + struct nda_cacheinfo ci; + struct nlmsghdr *nlh; + struct ndmsg *ndm; + + nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags); + if (nlh == NULL) + return -EMSGSIZE; + + + ndm = nlmsg_data(nlh); + ndm->ndm_family = AF_BRIDGE; + ndm->ndm_pad1 = 0; + ndm->ndm_pad2 = 0; + ndm->ndm_flags = 0; + ndm->ndm_type = 0; + ndm->ndm_ifindex = fdb->dst->dev->ifindex; + ndm->ndm_state = fdb_to_nud(fdb); + + NLA_PUT(skb, NDA_LLADDR, ETH_ALEN, &fdb->addr); + + ci.ndm_used = jiffies_to_clock_t(now - fdb->used); + ci.ndm_confirmed = 0; + ci.ndm_updated = jiffies_to_clock_t(now - fdb->updated); + ci.ndm_refcnt = 0; + NLA_PUT(skb, NDA_CACHEINFO, sizeof(ci), &ci); + + return nlmsg_end(skb, nlh); + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} + +static inline size_t fdb_nlmsg_size(void) +{ + return NLMSG_ALIGN(sizeof(struct ndmsg)) + + nla_total_size(ETH_ALEN) /* NDA_LLADDR */ + + nla_total_size(sizeof(struct nda_cacheinfo)); +} + +static void fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) +{ + struct net *net = dev_net(fdb->dst->dev); + struct sk_buff *skb; + int err = -ENOBUFS; + + skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC); + if (skb == NULL) + goto errout; + + err = fdb_fill_info(skb, fdb, 0, 0, type, 0); + if (err < 0) { + /* -EMSGSIZE implies BUG in fdb_nlmsg_size() */ + WARN_ON(err == -EMSGSIZE); + kfree_skb(skb); + goto errout; + } + rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); + return; +errout: + if (err < 0) + rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); +} + +/* Dump information about entries, in response to GETNEIGH */ +int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + int idx = 0; + + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + struct net_bridge *br = netdev_priv(dev); + int i; + + if (!(dev->priv_flags & IFF_EBRIDGE)) + continue; + + for (i = 0; i < BR_HASH_SIZE; i++) { + struct hlist_node *h; + struct net_bridge_fdb_entry *f; + + hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) { + if (idx < cb->args[0]) + goto skip; + + if (fdb_fill_info(skb, f, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + RTM_NEWNEIGH, + NLM_F_MULTI) < 0) + break; +skip: + ++idx; + } + } + } + rcu_read_unlock(); + + cb->args[0] = idx; + + return skb->len; +} --- a/net/bridge/br_private.h 2011-03-21 10:36:16.608199014 -0700 +++ b/net/bridge/br_private.h 2011-03-21 13:00:19.106207704 -0700 @@ -354,6 +354,7 @@ extern int br_fdb_insert(struct net_brid extern void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, const unsigned char *addr); +extern int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb); /* br_forward.c */ extern void br_deliver(const struct net_bridge_port *to, --- a/net/bridge/br_netlink.c 2011-03-21 10:33:38.838610211 -0700 +++ b/net/bridge/br_netlink.c 2011-03-21 13:00:19.090207432 -0700 @@ -196,6 +196,7 @@ int __init br_netlink_init(void) /* Only the first call to __rtnl_register can fail */ __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL); + __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump); return 0; } ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 5/7] bridge: allow creating/deleting fdb entries via netlink 2011-04-05 0:03 [PATCH 0/7] bridge enhancements for net-next Stephen Hemminger ` (3 preceding siblings ...) 2011-04-05 0:03 ` [PATCH 4/7] bridge: add netlink notification on forward entry changes Stephen Hemminger @ 2011-04-05 0:03 ` Stephen Hemminger 2011-10-05 19:06 ` Kevin Wilson 2011-04-05 0:03 ` [PATCH 6/7] bridge: allow creating bridge devices with netlink Stephen Hemminger ` (2 subsequent siblings) 7 siblings, 1 reply; 12+ messages in thread From: Stephen Hemminger @ 2011-04-05 0:03 UTC (permalink / raw) To: David S. Miller; +Cc: netdev [-- Attachment #1: br-fdb-newneigh.patch --] [-- Type: text/plain, Size: 5105 bytes --] Use RTM_NEWNEIGH and RTM_DELNEIGH to allow updating of entries in bridge forwarding table. This allows manipulating static entries which is not possible with existing tools. Example (using bridge extensions to iproute2) # br fdb add 00:02:03:04:05:06 dev eth0 Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> --- net/bridge/br_fdb.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++ net/bridge/br_netlink.c | 3 + net/bridge/br_private.h | 2 3 files changed, 144 insertions(+) --- a/net/bridge/br_fdb.c 2011-03-22 10:25:00.329008182 -0700 +++ b/net/bridge/br_fdb.c 2011-03-22 10:25:01.057042585 -0700 @@ -555,3 +555,142 @@ skip: return skb->len; } + +/* Create new static fdb entry */ +static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, + __u16 state) +{ + struct net_bridge *br = source->br; + struct hlist_head *head = &br->hash[br_mac_hash(addr)]; + struct net_bridge_fdb_entry *fdb; + + fdb = fdb_find(head, addr); + if (fdb) + return -EEXIST; + + fdb = fdb_create(head, source, addr); + if (!fdb) + return -ENOMEM; + + if (state & NUD_PERMANENT) + fdb->is_local = fdb->is_static = 1; + else if (state & NUD_NOARP) + fdb->is_static = 1; + return 0; +} + +/* Add new permanent fdb entry with RTM_NEWNEIGH */ +int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +{ + struct net *net = sock_net(skb->sk); + struct ndmsg *ndm; + struct nlattr *tb[NDA_MAX+1]; + struct net_device *dev; + struct net_bridge_port *p; + const __u8 *addr; + int err; + + ASSERT_RTNL(); + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); + if (err < 0) + return err; + + ndm = nlmsg_data(nlh); + if (ndm->ndm_ifindex == 0) { + pr_info("bridge: RTM_NEWNEIGH with invalid ifindex\n"); + return -EINVAL; + } + + dev = __dev_get_by_index(net, ndm->ndm_ifindex); + if (dev == NULL) { + pr_info("bridge: RTM_NEWNEIGH with unknown ifindex\n"); + return -ENODEV; + } + + if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) { + pr_info("bridge: RTM_NEWNEIGH with invalid address\n"); + return -EINVAL; + } + + addr = nla_data(tb[NDA_LLADDR]); + if (!is_valid_ether_addr(addr)) { + pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n"); + return -EINVAL; + } + + p = br_port_get_rtnl(dev); + if (p == NULL) { + pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n", + dev->name); + return -EINVAL; + } + + spin_lock_bh(&p->br->hash_lock); + err = fdb_add_entry(p, addr, ndm->ndm_state); + spin_unlock_bh(&p->br->hash_lock); + + return err; +} + +static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr) +{ + struct net_bridge *br = p->br; + struct hlist_head *head = &br->hash[br_mac_hash(addr)]; + struct net_bridge_fdb_entry *fdb; + + fdb = fdb_find(head, addr); + if (!fdb) + return -ENOENT; + + fdb_delete(fdb); + return 0; +} + +/* Remove neighbor entry with RTM_DELNEIGH */ +int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +{ + struct net *net = sock_net(skb->sk); + struct ndmsg *ndm; + struct net_bridge_port *p; + struct nlattr *llattr; + const __u8 *addr; + struct net_device *dev; + int err; + + ASSERT_RTNL(); + if (nlmsg_len(nlh) < sizeof(*ndm)) + return -EINVAL; + + ndm = nlmsg_data(nlh); + if (ndm->ndm_ifindex == 0) { + pr_info("bridge: RTM_DELNEIGH with invalid ifindex\n"); + return -EINVAL; + } + + dev = __dev_get_by_index(net, ndm->ndm_ifindex); + if (dev == NULL) { + pr_info("bridge: RTM_DELNEIGH with unknown ifindex\n"); + return -ENODEV; + } + + llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR); + if (llattr == NULL || nla_len(llattr) != ETH_ALEN) { + pr_info("bridge: RTM_DELNEIGH with invalid address\n"); + return -EINVAL; + } + + addr = nla_data(llattr); + + p = br_port_get_rtnl(dev); + if (p == NULL) { + pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n", + dev->name); + return -EINVAL; + } + + spin_lock_bh(&p->br->hash_lock); + err = fdb_delete_by_addr(p, addr); + spin_unlock_bh(&p->br->hash_lock); + + return err; +} --- a/net/bridge/br_netlink.c 2011-03-22 10:25:00.329008182 -0700 +++ b/net/bridge/br_netlink.c 2011-03-22 10:25:01.057042585 -0700 @@ -196,6 +196,9 @@ int __init br_netlink_init(void) /* Only the first call to __rtnl_register can fail */ __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL); + + __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, br_fdb_add, NULL); + __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL); __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump); return 0; --- a/net/bridge/br_private.h 2011-03-22 10:25:00.329008182 -0700 +++ b/net/bridge/br_private.h 2011-03-22 10:25:01.057042585 -0700 @@ -355,6 +355,8 @@ extern void br_fdb_update(struct net_bri struct net_bridge_port *source, const unsigned char *addr); extern int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb); +extern int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); +extern int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); /* br_forward.c */ extern void br_deliver(const struct net_bridge_port *to, ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 5/7] bridge: allow creating/deleting fdb entries via netlink 2011-04-05 0:03 ` [PATCH 5/7] bridge: allow creating/deleting fdb entries via netlink Stephen Hemminger @ 2011-10-05 19:06 ` Kevin Wilson 2011-10-05 19:13 ` Stephen Hemminger 0 siblings, 1 reply; 12+ messages in thread From: Kevin Wilson @ 2011-10-05 19:06 UTC (permalink / raw) To: netdev Hello all, I would appreciate if someone can elaborate about "bridge extensions to iproute2" mentioned here. I downloaded latest iproute2 git tree and did not find it there. googling for it did not gave much info about it. I will appreciate if someone can tell who develop it, what is the status, site, repository tree, etc. rgs, Kevin On Tue, Apr 5, 2011 at 3:03 AM, Stephen Hemminger <shemminger@vyatta.com> wrote: > Use RTM_NEWNEIGH and RTM_DELNEIGH to allow updating of entries > in bridge forwarding table. This allows manipulating static entries > which is not possible with existing tools. > > Example (using bridge extensions to iproute2) > # br fdb add 00:02:03:04:05:06 dev eth0 > > Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> > > --- > net/bridge/br_fdb.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++ > net/bridge/br_netlink.c | 3 + > net/bridge/br_private.h | 2 > 3 files changed, 144 insertions(+) > > --- a/net/bridge/br_fdb.c 2011-03-22 10:25:00.329008182 -0700 > +++ b/net/bridge/br_fdb.c 2011-03-22 10:25:01.057042585 -0700 > @@ -555,3 +555,142 @@ skip: > > return skb->len; > } > + > +/* Create new static fdb entry */ > +static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, > + __u16 state) > +{ > + struct net_bridge *br = source->br; > + struct hlist_head *head = &br->hash[br_mac_hash(addr)]; > + struct net_bridge_fdb_entry *fdb; > + > + fdb = fdb_find(head, addr); > + if (fdb) > + return -EEXIST; > + > + fdb = fdb_create(head, source, addr); > + if (!fdb) > + return -ENOMEM; > + > + if (state & NUD_PERMANENT) > + fdb->is_local = fdb->is_static = 1; > + else if (state & NUD_NOARP) > + fdb->is_static = 1; > + return 0; > +} > + > +/* Add new permanent fdb entry with RTM_NEWNEIGH */ > +int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) > +{ > + struct net *net = sock_net(skb->sk); > + struct ndmsg *ndm; > + struct nlattr *tb[NDA_MAX+1]; > + struct net_device *dev; > + struct net_bridge_port *p; > + const __u8 *addr; > + int err; > + > + ASSERT_RTNL(); > + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); > + if (err < 0) > + return err; > + > + ndm = nlmsg_data(nlh); > + if (ndm->ndm_ifindex == 0) { > + pr_info("bridge: RTM_NEWNEIGH with invalid ifindex\n"); > + return -EINVAL; > + } > + > + dev = __dev_get_by_index(net, ndm->ndm_ifindex); > + if (dev == NULL) { > + pr_info("bridge: RTM_NEWNEIGH with unknown ifindex\n"); > + return -ENODEV; > + } > + > + if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) { > + pr_info("bridge: RTM_NEWNEIGH with invalid address\n"); > + return -EINVAL; > + } > + > + addr = nla_data(tb[NDA_LLADDR]); > + if (!is_valid_ether_addr(addr)) { > + pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n"); > + return -EINVAL; > + } > + > + p = br_port_get_rtnl(dev); > + if (p == NULL) { > + pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n", > + dev->name); > + return -EINVAL; > + } > + > + spin_lock_bh(&p->br->hash_lock); > + err = fdb_add_entry(p, addr, ndm->ndm_state); > + spin_unlock_bh(&p->br->hash_lock); > + > + return err; > +} > + > +static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr) > +{ > + struct net_bridge *br = p->br; > + struct hlist_head *head = &br->hash[br_mac_hash(addr)]; > + struct net_bridge_fdb_entry *fdb; > + > + fdb = fdb_find(head, addr); > + if (!fdb) > + return -ENOENT; > + > + fdb_delete(fdb); > + return 0; > +} > + > +/* Remove neighbor entry with RTM_DELNEIGH */ > +int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) > +{ > + struct net *net = sock_net(skb->sk); > + struct ndmsg *ndm; > + struct net_bridge_port *p; > + struct nlattr *llattr; > + const __u8 *addr; > + struct net_device *dev; > + int err; > + > + ASSERT_RTNL(); > + if (nlmsg_len(nlh) < sizeof(*ndm)) > + return -EINVAL; > + > + ndm = nlmsg_data(nlh); > + if (ndm->ndm_ifindex == 0) { > + pr_info("bridge: RTM_DELNEIGH with invalid ifindex\n"); > + return -EINVAL; > + } > + > + dev = __dev_get_by_index(net, ndm->ndm_ifindex); > + if (dev == NULL) { > + pr_info("bridge: RTM_DELNEIGH with unknown ifindex\n"); > + return -ENODEV; > + } > + > + llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR); > + if (llattr == NULL || nla_len(llattr) != ETH_ALEN) { > + pr_info("bridge: RTM_DELNEIGH with invalid address\n"); > + return -EINVAL; > + } > + > + addr = nla_data(llattr); > + > + p = br_port_get_rtnl(dev); > + if (p == NULL) { > + pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n", > + dev->name); > + return -EINVAL; > + } > + > + spin_lock_bh(&p->br->hash_lock); > + err = fdb_delete_by_addr(p, addr); > + spin_unlock_bh(&p->br->hash_lock); > + > + return err; > +} > --- a/net/bridge/br_netlink.c 2011-03-22 10:25:00.329008182 -0700 > +++ b/net/bridge/br_netlink.c 2011-03-22 10:25:01.057042585 -0700 > @@ -196,6 +196,9 @@ int __init br_netlink_init(void) > > /* Only the first call to __rtnl_register can fail */ > __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL); > + > + __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, br_fdb_add, NULL); > + __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL); > __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump); > > return 0; > --- a/net/bridge/br_private.h 2011-03-22 10:25:00.329008182 -0700 > +++ b/net/bridge/br_private.h 2011-03-22 10:25:01.057042585 -0700 > @@ -355,6 +355,8 @@ extern void br_fdb_update(struct net_bri > struct net_bridge_port *source, > const unsigned char *addr); > extern int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb); > +extern int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); > +extern int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); > > /* br_forward.c */ > extern void br_deliver(const struct net_bridge_port *to, > > > -- > To unsubscribe from this list: send the line "unsubscribe netdev" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 5/7] bridge: allow creating/deleting fdb entries via netlink 2011-10-05 19:06 ` Kevin Wilson @ 2011-10-05 19:13 ` Stephen Hemminger 2011-10-05 19:36 ` Ben Hutchings 0 siblings, 1 reply; 12+ messages in thread From: Stephen Hemminger @ 2011-10-05 19:13 UTC (permalink / raw) To: Kevin Wilson; +Cc: netdev On Wed, 5 Oct 2011 21:06:54 +0200 Kevin Wilson <wkevils@gmail.com> wrote: > Hello all, > I would appreciate if someone can elaborate about "bridge extensions > to iproute2" mentioned here. > I downloaded latest iproute2 git tree and did not find it there. > googling for it did not gave much info about it. > I will appreciate if someone can tell who develop it, what is the > status, site, repository tree, etc. > > rgs, > Kevin The patch to handle this was posted, but is not committed to the tree yet. -- >From 8e3d00d0602420dadc3d23877a4995ea3d7496c2 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger <shemminger@vyatta.com> Date: Tue, 4 Oct 2011 09:31:58 -0700 Subject: [PATCH] Add support for bridging control. This adds a new 'bridge' command which is the bridging equivalent of the ip command. --- Makefile | 2 +- br/.gitignore | 1 + br/Makefile | 14 +++ br/br_common.h | 13 +++ br/bridge.c | 104 ++++++++++++++++++++++ br/fdb.c | 245 +++++++++++++++++++++++++++++++++++++++++++++++++++++ br/link.c | 142 +++++++++++++++++++++++++++++++ br/monitor.c | 138 ++++++++++++++++++++++++++++++ man/man8/bridge.8 | 177 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 835 insertions(+), 1 deletions(-) create mode 100644 br/.gitignore create mode 100644 br/Makefile create mode 100644 br/br_common.h create mode 100644 br/bridge.c create mode 100644 br/fdb.c create mode 100644 br/link.c create mode 100644 br/monitor.c create mode 100644 man/man8/bridge.8 diff --git a/Makefile b/Makefile index d1ace1f..f1d360a 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall CFLAGS = $(CCOPTS) -I../include $(DEFINES) YACCFLAGS = -d -t -v -SUBDIRS=lib ip tc misc netem genl +SUBDIRS=lib ip tc br misc netem genl LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a LDLIBS += $(LIBNETLINK) diff --git a/br/.gitignore b/br/.gitignore new file mode 100644 index 0000000..7096907 --- /dev/null +++ b/br/.gitignore @@ -0,0 +1 @@ +bridge diff --git a/br/Makefile b/br/Makefile new file mode 100644 index 0000000..9a6743e --- /dev/null +++ b/br/Makefile @@ -0,0 +1,14 @@ +BROBJ = bridge.o fdb.o monitor.o link.o + +include ../Config + +all: bridge + +bridge: $(BROBJ) $(LIBNETLINK) + +install: all + install -m 0755 bridge $(DESTDIR)$(SBINDIR) + +clean: + rm -f $(BROBJ) bridge + diff --git a/br/br_common.h b/br/br_common.h new file mode 100644 index 0000000..ec1671d --- /dev/null +++ b/br/br_common.h @@ -0,0 +1,13 @@ +extern int print_linkinfo(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg); +extern int print_fdb(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); + +extern int do_fdb(int argc, char **argv); +extern int do_monitor(int argc, char **argv); + +extern int show_stats; +extern int show_detail; +extern int timestamp; +extern struct rtnl_handle rth; diff --git a/br/bridge.c b/br/bridge.c new file mode 100644 index 0000000..9e5f69c --- /dev/null +++ b/br/bridge.c @@ -0,0 +1,104 @@ +/* + * Get/set/delete bridge with netlink + * + * Authors: Stephen Hemminger <shemminger@vyatta.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <string.h> + +#include "SNAPSHOT.h" +#include "utils.h" +#include "br_common.h" + +struct rtnl_handle rth = { .fd = -1 }; +int resolve_hosts; +int show_stats; +int show_details; +int timestamp; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, +"Usage: br [ OPTIONS ] OBJECT { COMMAND | help }\n" +"where OBJECT := { fdb | monitor }\n" +" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails]\n" ); + exit(-1); +} + +static int do_help(int argc, char **argv) +{ + usage(); +} + + +static const struct cmd { + const char *cmd; + int (*func)(int argc, char **argv); +} cmds[] = { + { "fdb", do_fdb }, + { "monitor", do_monitor }, + { "help", do_help }, + { 0 } +}; + +static int do_cmd(const char *argv0, int argc, char **argv) +{ + const struct cmd *c; + + for (c = cmds; c->cmd; ++c) { + if (matches(argv0, c->cmd) == 0) + return c->func(argc-1, argv+1); + } + + fprintf(stderr, "Object \"%s\" is unknown, try \"br help\".\n", argv0); + return -1; +} + +int +main(int argc, char **argv) +{ + while (argc > 1) { + char *opt = argv[1]; + if (strcmp(opt,"--") == 0) { + argc--; argv++; + break; + } + if (opt[0] != '-') + break; + if (opt[1] == '-') + opt++; + + if (matches(opt, "-help") == 0) { + usage(); + } else if (matches(opt, "-Version") == 0) { + printf("br utility, 0.0\n"); + exit(0); + } else if (matches(opt, "-stats") == 0 || + matches(opt, "-statistics") == 0) { + ++show_stats; + } else if (matches(opt, "-details") == 0) { + ++show_details; + } else if (matches(opt, "-timestamp") == 0) { + ++timestamp; + } else { + fprintf(stderr, "Option \"%s\" is unknown, try \"br -help\".\n", opt); + exit(-1); + } + argc--; argv++; + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + if (argc > 1) + return do_cmd(argv[1], argc-1, argv+1); + + rtnl_close(&rth); + usage(); +} diff --git a/br/fdb.c b/br/fdb.c new file mode 100644 index 0000000..d849f97 --- /dev/null +++ b/br/fdb.c @@ -0,0 +1,245 @@ +/* + * Get/set/delete fdb table with netlink + * + * Authors: Stephen Hemminger <shemminger@vyatta.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <net/if.h> +#include <netinet/in.h> +#include <linux/if_bridge.h> +#include <linux/if_ether.h> +#include <linux/neighbour.h> +#include <string.h> + +#include "libnetlink.h" +#include "br_common.h" +#include "utils.h" + +int filter_index; + +static void usage(void) +{ + fprintf(stderr, "Usage: br fdb { add | del | replace } ADDR dev DEV\n"); + fprintf(stderr, " br fdb {show} [ dev DEV ]\n"); + exit(-1); +} + +static const char *state_n2a(unsigned s) +{ + static char buf[32]; + + if (s & NUD_PERMANENT) + return "local"; + + if (s & NUD_NOARP) + return "static"; + + if (s & NUD_STALE) + return "stale"; + + if (s & NUD_REACHABLE) + return ""; + + sprintf(buf, "state=%#x", s); + return buf; +} + +static char *fmt_time(char *b, size_t l, unsigned long tick) +{ + static int hz; + + if (hz == 0) + hz = __get_user_hz(); + + snprintf(b, l, "%lu.%02lu", tick / hz, ((tick % hz) * hz) / 100); + return b; +} + +int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = arg; + struct ndmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[NDA_MAX+1]; + const __u8 *addr = NULL; + char b1[32]; + + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (r->ndm_family != AF_BRIDGE) + return 0; + + if (filter_index && filter_index != r->ndm_ifindex) + return 0; + + parse_rtattr(tb, NDA_MAX, NDA_RTA(r), + n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + if (n->nlmsg_type == RTM_DELNEIGH) + fprintf(fp, "Deleted "); + + if (tb[NDA_LLADDR]) + addr = RTA_DATA(tb[NDA_LLADDR]); + else { + fprintf(stderr, "missing lladdr\n"); + return -1; + } + + fprintf(fp, "%s\t%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t%s", + ll_index_to_name(r->ndm_ifindex), + addr[0], addr[1], addr[2], + addr[3], addr[4], addr[5], + state_n2a(r->ndm_state)); + + if (show_stats && tb[NDA_CACHEINFO]) { + struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); + + fprintf(fp, "\t%8s", fmt_time(b1, sizeof(b1), ci->ndm_updated)); + fprintf(fp, " %8s", fmt_time(b1, sizeof(b1), ci->ndm_used)); + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +static int fdb_show(int argc, char **argv) +{ + char *filter_dev = NULL; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (filter_dev) + duparg("dev", *argv); + filter_dev = *argv; + } + argc--; argv++; + } + + if (filter_dev) { + if ((filter_index = if_nametoindex(filter_dev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev); + return -1; + } + } + + if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETNEIGH) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + printf("port\tmac addr\t\tflags%s\n", + show_stats ? "\t updated used" : ""); + + if (rtnl_dump_filter(&rth, print_fdb, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + return 0; +} + +static int fdb_modify(int cmd, int flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req; + char *addr = NULL; + char *d = NULL; + char abuf[ETH_ALEN]; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.ndm.ndm_family = PF_BRIDGE; + req.ndm.ndm_state = NUD_NOARP; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else if (strcmp(*argv, "local") == 0) { + req.ndm.ndm_state = NUD_PERMANENT; + } else if (strcmp(*argv, "temp") == 0) { + req.ndm.ndm_state = NUD_REACHABLE; + } else { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) { + NEXT_ARG(); + } + if (addr) + duparg2("to", *argv); + addr = *argv; + } + argc--; argv++; + } + + if (d == NULL || addr == NULL) { + fprintf(stderr, "Device and address are required arguments.\n"); + exit(-1); + } + + if (sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + abuf, abuf+1, abuf+2, + abuf+3, abuf+4, abuf+5) != 6) { + fprintf(stderr, "Invalid mac address %s\n", addr); + exit(-1); + } + + addattr_l(&req.n, sizeof(req), NDA_LLADDR, abuf, ETH_ALEN); + + req.ndm.ndm_ifindex = ll_name_to_index(d); + if (req.ndm.ndm_ifindex == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + return 0; +} + +int do_fdb(int argc, char **argv) +{ + ll_init_map(&rth); + + if (argc > 0) { + if (matches(*argv, "add") == 0) + return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1); + if (matches(*argv, "change") == 0) + return fdb_modify(RTM_NEWNEIGH, NLM_F_REPLACE, argc-1, argv+1); + + if (matches(*argv, "replace") == 0) + return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return fdb_modify(RTM_DELNEIGH, 0, argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return fdb_show(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return fdb_show(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv); + exit(-1); +} diff --git a/br/link.c b/br/link.c new file mode 100644 index 0000000..1b9541d --- /dev/null +++ b/br/link.c @@ -0,0 +1,142 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <linux/if.h> +#include <linux/if_bridge.h> +#include <string.h> + +#include "utils.h" +#include "br_common.h" + +static const char *port_states[] = { + [BR_STATE_DISABLED] = "disabled", + [BR_STATE_LISTENING] = "listening", + [BR_STATE_LEARNING] = "learning", + [BR_STATE_FORWARDING] = "forwarding", + [BR_STATE_BLOCKING] = "blocking", +}; + +extern char *if_indextoname (unsigned int __ifindex, char *__ifname); + +static void print_link_flags(FILE *fp, unsigned flags) +{ + fprintf(fp, "<"); + if (flags & IFF_UP && !(flags & IFF_RUNNING)) + fprintf(fp, "NO-CARRIER%s", flags ? "," : ""); + flags &= ~IFF_RUNNING; +#define _PF(f) if (flags&IFF_##f) { \ + flags &= ~IFF_##f ; \ + fprintf(fp, #f "%s", flags ? "," : ""); } + _PF(LOOPBACK); + _PF(BROADCAST); + _PF(POINTOPOINT); + _PF(MULTICAST); + _PF(NOARP); + _PF(ALLMULTI); + _PF(PROMISC); + _PF(MASTER); + _PF(SLAVE); + _PF(DEBUG); + _PF(DYNAMIC); + _PF(AUTOMEDIA); + _PF(PORTSEL); + _PF(NOTRAILERS); + _PF(UP); + _PF(LOWER_UP); + _PF(DORMANT); + _PF(ECHO); +#undef _PF + if (flags) + fprintf(fp, "%x", flags); + fprintf(fp, "> "); +} + +static const char *oper_states[] = { + "UNKNOWN", "NOTPRESENT", "DOWN", "LOWERLAYERDOWN", + "TESTING", "DORMANT", "UP" +}; + +static void print_operstate(FILE *f, __u8 state) +{ + if (state >= sizeof(oper_states)/sizeof(oper_states[0])) + fprintf(f, "state %#x ", state); + else + fprintf(f, "state %s ", oper_states[state]); +} + +int print_linkinfo(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = arg; + int len = n->nlmsg_len; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr * tb[IFLA_MAX+1]; + char b1[IFNAMSIZ]; + + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) { + fprintf(stderr, "Message too short!\n"); + return -1; + } + + if (!(ifi->ifi_family == AF_BRIDGE || ifi->ifi_family == AF_UNSPEC)) + return 0; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + + if (tb[IFLA_IFNAME] == NULL) { + fprintf(stderr, "BUG: nil ifname\n"); + return -1; + } + + if (n->nlmsg_type == RTM_DELLINK) + fprintf(fp, "Deleted "); + + fprintf(fp, "%d: %s ", ifi->ifi_index, + tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>"); + + if (tb[IFLA_OPERSTATE]) + print_operstate(fp, *(__u8 *)RTA_DATA(tb[IFLA_OPERSTATE])); + + if (tb[IFLA_LINK]) { + SPRINT_BUF(b1); + int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]); + + if (iflink == 0) + fprintf(fp, "@NONE: "); + else { + fprintf(fp, "@%s: ", + if_indextoname(iflink, b1)); + } + } else { + fprintf(fp, ": "); + } + + print_link_flags(fp, ifi->ifi_flags); + + if (tb[IFLA_MTU]) + fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU])); + + if (tb[IFLA_MASTER]) { + fprintf(fp, "master %s ", + if_indextoname(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1)); + } + + if (tb[IFLA_PROTINFO]) { + uint8_t state = *(uint8_t *)RTA_DATA(tb[IFLA_PROTINFO]); + if (state <= BR_STATE_BLOCKING) + fprintf(fp, "state %s", port_states[state]); + else + fprintf(fp, "state (%d)", state); + } + + + fprintf(fp, "\n"); + fflush(fp); + return 0; +} diff --git a/br/monitor.c b/br/monitor.c new file mode 100644 index 0000000..37468e6 --- /dev/null +++ b/br/monitor.c @@ -0,0 +1,138 @@ +/* + * brmonitor.c "br monitor" + * + * 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. + * + * Authors: Stephen Hemminger <shemminger@vyatta.com> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <net/if.h> +#include <netinet/in.h> +#include <linux/if_bridge.h> +#include <linux/neighbour.h> +#include <string.h> + +#include "utils.h" +#include "br_common.h" + + +static void usage(void) __attribute__((noreturn)); +int prefix_banner; + +static void usage(void) +{ + fprintf(stderr, "Usage: br monitor\n"); + exit(-1); +} + +static int show_mark(FILE *fp, const struct nlmsghdr *n) +{ + char *tstr; + time_t secs = ((__u32*)NLMSG_DATA(n))[0]; + long usecs = ((__u32*)NLMSG_DATA(n))[1]; + tstr = asctime(localtime(&secs)); + tstr[strlen(tstr)-1] = 0; + fprintf(fp, "Timestamp: %s %lu us\n", tstr, usecs); + return 0; +} + +int accept_msg(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = arg; + + if (timestamp) + print_timestamp(fp); + + switch (n->nlmsg_type) { + case RTM_NEWLINK: + case RTM_DELLINK: + if (prefix_banner) + fprintf(fp, "[LINK]"); + + return print_linkinfo(who, n, arg); + + case RTM_NEWNEIGH: + case RTM_DELNEIGH: + if (prefix_banner) + fprintf(fp, "[NEIGH]"); + return print_fdb(who, n, arg); + + case 15: + return show_mark(fp, n); + + default: + return 0; + } + + +} + +int do_monitor(int argc, char **argv) +{ + char *file = NULL; + unsigned groups = ~RTMGRP_TC; + int llink=0; + int lneigh=0; + + rtnl_close(&rth); + + while (argc > 0) { + if (matches(*argv, "file") == 0) { + NEXT_ARG(); + file = *argv; + } else if (matches(*argv, "link") == 0) { + llink=1; + groups = 0; + } else if (matches(*argv, "fdb") == 0) { + lneigh = 1; + groups = 0; + } else if (strcmp(*argv, "all") == 0) { + groups = ~RTMGRP_TC; + prefix_banner=1; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, "Argument \"%s\" is unknown, try \"br monitor help\".\n", *argv); + exit(-1); + } + argc--; argv++; + } + + if (llink) + groups |= nl_mgrp(RTNLGRP_LINK); + + if (lneigh) { + groups |= nl_mgrp(RTNLGRP_NEIGH); + } + + if (file) { + FILE *fp; + fp = fopen(file, "r"); + if (fp == NULL) { + perror("Cannot fopen"); + exit(-1); + } + return rtnl_from_file(fp, accept_msg, stdout); + } + + if (rtnl_open(&rth, groups) < 0) + exit(1); + ll_init_map(&rth); + + if (rtnl_listen(&rth, accept_msg, stdout) < 0) + exit(2); + + return 0; +} + diff --git a/man/man8/bridge.8 b/man/man8/bridge.8 new file mode 100644 index 0000000..8a5d21e --- /dev/null +++ b/man/man8/bridge.8 @@ -0,0 +1,177 @@ +.TH BRIDGE 8 "4 October 2011" "iproute2" "Linux" +.SH NAME +bridge \- show / manipulate bridge addresses and devices +.SH SYNOPSIS + +.ad l +.in +8 +.ti -8 +.B bridge +.RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OBJECT " := { " +.BR fdb " | " monitor " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-s\fR[\fItatistics\fR] + +.ti -8 +.BR "bridge fdb" " { " add " | " del " | " change " | " replace " } " +.I LLADDR +.B dev +.IR DEV " { " +.BR local " | " temp " }" + +.ti -8 +.BR "bridge fdb" " [ " show " ] [ " +.B dev +.IR DEV " ]" + +.ti -8 +.BR "bridge monitor" " [ " all " | " neigh " | " link " ]" + +.SH OPTIONS + +.TP +.BR "\-V" , " -Version" +print the version of the +.B ip +utility and exit. + +.TP +.BR "\-s" , " \-stats", " \-statistics" +output more information. If the option +appears twice or more, the amount of information increases. +As a rule, the information is statistics or some time values. + + +.SH BRIDGE - COMMAND SYNTAX + +.SS +.I OBJECT + +.TP +.B fdb +- Forwarding Database entry. + +.SS +.I COMMAND + +Specifies the action to perform on the object. +The set of possible actions depends on the object type. +As a rule, it is possible to +.BR "add" , " delete" +and +.B show +(or +.B list +) objects, but some objects do not allow all of these operations +or have some additional commands. The +.B help +command is available for all objects. It prints +out a list of available commands and argument syntax conventions. +.sp +If no command is given, some default command is assumed. +Usually it is +.B list +or, if the objects of this class cannot be listed, +.BR "help" . + +.SH bridge fdb - forwarding database management + +.B fdb +objects contain known ethernet addresses fona link. + +.P +The corresponding commands display fdb entries, add new entries, +and delete old ones. + +.SS bridge fdb add - add a new neighbour entry +.SS bridge fdb change - change an existing entry +.SS bridge fdb replace - add a new entry or change an existing one + +These commands create new neighbour records or update existing ones. + +.TP +.BI "ADDRESS" +the Ethernet MAC address. + +.TP +.BI dev " NAME" +the interface to which this address is associated. + +.TP +.in +8 +.B local +- the address is associated with a local interface on the system +and is never forwarded. +.sp + +.B temp +- the address is a dynamic entry, and will be removed if not used. +.sp + +.in -8 + +.SS bridge fdb delete - delete a forwarding database entry +This command removes an existing fdb entry. + +.PP +The arguments are the same as with +.BR "bridge fdb add" , + +.SS bridge fdb show - list forwarding entries. + +This commands displays current forwarding table. + +.PP +With the +.B -statistics +option, the command becomes verbose. It prints out the last updated +and last used time for each entry. + +.SH bridge monitor - state monitoring + +The +.B bridge +utility can monitor the state of devices and addresses +continuously. This option has a slightly different format. +Namely, the +.B monitor +command is the first in the command line and then the object list follows: + +.BR "bridge monitor" " [ " all " |" +.IR LISTofOBJECTS " ]" + +.I OBJECT-LIST +is the list of object types that we want to monitor. +It may contain +.BR link ", and " fdb "." +If no +.B file +argument is given, +.B ip +opens RTNETLINK, listens on it and dumps state changes in the format +described in previous sections. + +.P +If a file name is given, it does not listen on RTNETLINK, +but opens the file containing RTNETLINK messages saved in binary format +and dumps them. Such a history file can be generated with the + +.SH HISTORY +.B bridge +was written by Stephen Hemminger and uses kernel facilities added in Linux 3.0 +.SH SEE ALSO +.BR ip (8) +.br +.RB "Please direct bugreports and patches to: " <netdev@vger.kernel.org> + +.SH AUTHOR +Original Manpage by Stephen Hemminger -- 1.7.6.3 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 5/7] bridge: allow creating/deleting fdb entries via netlink 2011-10-05 19:13 ` Stephen Hemminger @ 2011-10-05 19:36 ` Ben Hutchings 0 siblings, 0 replies; 12+ messages in thread From: Ben Hutchings @ 2011-10-05 19:36 UTC (permalink / raw) To: Stephen Hemminger; +Cc: Kevin Wilson, netdev On Wed, 2011-10-05 at 12:13 -0700, Stephen Hemminger wrote: > On Wed, 5 Oct 2011 21:06:54 +0200 > Kevin Wilson <wkevils@gmail.com> wrote: > > > Hello all, > > I would appreciate if someone can elaborate about "bridge extensions > > to iproute2" mentioned here. > > I downloaded latest iproute2 git tree and did not find it there. > > googling for it did not gave much info about it. > > I will appreciate if someone can tell who develop it, what is the > > status, site, repository tree, etc. > > > > rgs, > > Kevin > > The patch to handle this was posted, but is not committed to the tree yet. [...] > +static void usage(void) > +{ > + fprintf(stderr, > +"Usage: br [ OPTIONS ] OBJECT { COMMAND | help }\n" [...] This and the sub-command usage functions still report the command name as 'br'. Ben. -- Ben Hutchings, Staff Engineer, Solarflare Not speaking for my employer; that's the marketing department's job. They asked us to note that Solarflare product names are trademarked. ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 6/7] bridge: allow creating bridge devices with netlink 2011-04-05 0:03 [PATCH 0/7] bridge enhancements for net-next Stephen Hemminger ` (4 preceding siblings ...) 2011-04-05 0:03 ` [PATCH 5/7] bridge: allow creating/deleting fdb entries via netlink Stephen Hemminger @ 2011-04-05 0:03 ` Stephen Hemminger 2011-04-05 0:03 ` [PATCH 7/7] bridge: range check STP parameters Stephen Hemminger 2011-04-05 0:23 ` [PATCH 0/7] bridge enhancements for net-next David Miller 7 siblings, 0 replies; 12+ messages in thread From: Stephen Hemminger @ 2011-04-05 0:03 UTC (permalink / raw) To: David S. Miller; +Cc: netdev [-- Attachment #1: br-newlink.patch --] [-- Type: text/plain, Size: 8289 bytes --] Add netlink device ops to allow creating bridge device via netlink. This works in a manner similar to vlan, macvlan and bonding. Example: # ip link add link dev br0 type bridge # ip link del dev br0 The change required rearranging initializtion code to deal with being called by create link. Most of the initialization happens in br_dev_setup, but allocation of stats is done in ndo_init callback to deal with allocation failure. Sysfs setup has to wait until after the network device kobject is registered. Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> --- net/bridge/br.c | 1 net/bridge/br_device.c | 41 +++++++++++++++++++++++ net/bridge/br_if.c | 83 ++---------------------------------------------- net/bridge/br_netlink.c | 55 +++++++++++++++++++++++++++---- net/bridge/br_notify.c | 6 +++ 5 files changed, 100 insertions(+), 86 deletions(-) --- a/net/bridge/br_device.c 2011-03-22 10:14:12.669553975 -0700 +++ b/net/bridge/br_device.c 2011-03-22 10:25:05.369243058 -0700 @@ -74,6 +74,17 @@ out: return NETDEV_TX_OK; } +static int br_dev_init(struct net_device *dev) +{ + struct net_bridge *br = netdev_priv(dev); + + br->stats = alloc_percpu(struct br_cpu_netstats); + if (!br->stats) + return -ENOMEM; + + return 0; +} + static int br_dev_open(struct net_device *dev) { struct net_bridge *br = netdev_priv(dev); @@ -334,6 +345,7 @@ static const struct ethtool_ops br_ethto static const struct net_device_ops br_netdev_ops = { .ndo_open = br_dev_open, .ndo_stop = br_dev_stop, + .ndo_init = br_dev_init, .ndo_start_xmit = br_dev_xmit, .ndo_get_stats64 = br_get_stats64, .ndo_set_mac_address = br_set_mac_address, @@ -357,18 +369,47 @@ static void br_dev_free(struct net_devic free_netdev(dev); } +static struct device_type br_type = { + .name = "bridge", +}; + void br_dev_setup(struct net_device *dev) { + struct net_bridge *br = netdev_priv(dev); + random_ether_addr(dev->dev_addr); ether_setup(dev); dev->netdev_ops = &br_netdev_ops; dev->destructor = br_dev_free; SET_ETHTOOL_OPS(dev, &br_ethtool_ops); + SET_NETDEV_DEVTYPE(dev, &br_type); dev->tx_queue_len = 0; dev->priv_flags = IFF_EBRIDGE; dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | NETIF_F_GSO_MASK | NETIF_F_NO_CSUM | NETIF_F_LLTX | NETIF_F_NETNS_LOCAL | NETIF_F_GSO | NETIF_F_HW_VLAN_TX; + + br->dev = dev; + spin_lock_init(&br->lock); + INIT_LIST_HEAD(&br->port_list); + spin_lock_init(&br->hash_lock); + + br->bridge_id.prio[0] = 0x80; + br->bridge_id.prio[1] = 0x00; + + memcpy(br->group_addr, br_group_address, ETH_ALEN); + + br->feature_mask = dev->features; + br->stp_enabled = BR_NO_STP; + br->designated_root = br->bridge_id; + br->bridge_max_age = br->max_age = 20 * HZ; + br->bridge_hello_time = br->hello_time = 2 * HZ; + br->bridge_forward_delay = br->forward_delay = 15 * HZ; + br->ageing_time = 300 * HZ; + + br_netfilter_rtable_init(br); + br_stp_timer_init(br); + br_multicast_init(br); } --- a/net/bridge/br_netlink.c 2011-03-22 10:25:01.057042585 -0700 +++ b/net/bridge/br_netlink.c 2011-03-22 10:25:05.369243058 -0700 @@ -12,9 +12,11 @@ #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/etherdevice.h> #include <net/rtnetlink.h> #include <net/net_namespace.h> #include <net/sock.h> + #include "br_private.h" static inline size_t br_nlmsg_size(void) @@ -188,24 +190,61 @@ static int br_rtm_setlink(struct sk_buff return 0; } +static int br_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + if (tb[IFLA_ADDRESS]) { + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) + return -EINVAL; + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + return -EADDRNOTAVAIL; + } + + return 0; +} + +static struct rtnl_link_ops br_link_ops __read_mostly = { + .kind = "bridge", + .priv_size = sizeof(struct net_bridge), + .setup = br_dev_setup, + .validate = br_validate, +}; int __init br_netlink_init(void) { - if (__rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo)) - return -ENOBUFS; + int err; - /* Only the first call to __rtnl_register can fail */ - __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL); + err = rtnl_link_register(&br_link_ops); + if (err < 0) + goto err1; - __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, br_fdb_add, NULL); - __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL); - __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump); + err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo); + if (err) + goto err2; + err = __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL); + if (err) + goto err3; + err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, br_fdb_add, NULL); + if (err) + goto err3; + err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL); + if (err) + goto err3; + err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump); + if (err) + goto err3; return 0; + +err3: + rtnl_unregister_all(PF_BRIDGE); +err2: + rtnl_link_unregister(&br_link_ops); +err1: + return err; } void __exit br_netlink_fini(void) { + rtnl_link_unregister(&br_link_ops); rtnl_unregister_all(PF_BRIDGE); } - --- a/net/bridge/br_if.c 2011-03-22 10:24:50.420524900 -0700 +++ b/net/bridge/br_if.c 2011-03-22 10:25:05.369243058 -0700 @@ -175,56 +175,6 @@ static void del_br(struct net_bridge *br unregister_netdevice_queue(br->dev, head); } -static struct net_device *new_bridge_dev(struct net *net, const char *name) -{ - struct net_bridge *br; - struct net_device *dev; - - dev = alloc_netdev(sizeof(struct net_bridge), name, - br_dev_setup); - - if (!dev) - return NULL; - dev_net_set(dev, net); - - br = netdev_priv(dev); - br->dev = dev; - - br->stats = alloc_percpu(struct br_cpu_netstats); - if (!br->stats) { - free_netdev(dev); - return NULL; - } - - spin_lock_init(&br->lock); - INIT_LIST_HEAD(&br->port_list); - spin_lock_init(&br->hash_lock); - - br->bridge_id.prio[0] = 0x80; - br->bridge_id.prio[1] = 0x00; - - memcpy(br->group_addr, br_group_address, ETH_ALEN); - - br->feature_mask = dev->features; - br->stp_enabled = BR_NO_STP; - br->designated_root = br->bridge_id; - br->root_path_cost = 0; - br->root_port = 0; - br->bridge_max_age = br->max_age = 20 * HZ; - br->bridge_hello_time = br->hello_time = 2 * HZ; - br->bridge_forward_delay = br->forward_delay = 15 * HZ; - br->topology_change = 0; - br->topology_change_detected = 0; - br->ageing_time = 300 * HZ; - - br_netfilter_rtable_init(br); - - br_stp_timer_init(br); - br_multicast_init(br); - - return dev; -} - /* find an available port number */ static int find_portno(struct net_bridge *br) { @@ -277,42 +227,19 @@ static struct net_bridge_port *new_nbp(s return p; } -static struct device_type br_type = { - .name = "bridge", -}; - int br_add_bridge(struct net *net, const char *name) { struct net_device *dev; - int ret; - dev = new_bridge_dev(net, name); + dev = alloc_netdev(sizeof(struct net_bridge), name, + br_dev_setup); + if (!dev) return -ENOMEM; - rtnl_lock(); - if (strchr(dev->name, '%')) { - ret = dev_alloc_name(dev, dev->name); - if (ret < 0) - goto out_free; - } - - SET_NETDEV_DEVTYPE(dev, &br_type); + dev_net_set(dev, net); - ret = register_netdevice(dev); - if (ret) - goto out_free; - - ret = br_sysfs_addbr(dev); - if (ret) - unregister_netdevice(dev); - out: - rtnl_unlock(); - return ret; - -out_free: - free_netdev(dev); - goto out; + return register_netdev(dev); } int br_del_bridge(struct net *net, const char *name) --- a/net/bridge/br.c 2011-03-22 10:13:27.074313779 -0700 +++ b/net/bridge/br.c 2011-03-22 10:25:05.369243058 -0700 @@ -104,3 +104,4 @@ module_init(br_init) module_exit(br_deinit) MODULE_LICENSE("GPL"); MODULE_VERSION(BR_VERSION); +MODULE_ALIAS_RTNL_LINK("bridge"); --- a/net/bridge/br_notify.c 2011-03-22 10:13:27.090305095 -0700 +++ b/net/bridge/br_notify.c 2011-03-22 10:25:05.369243058 -0700 @@ -36,6 +36,12 @@ static int br_device_event(struct notifi struct net_bridge *br; int err; + /* register of bridge completed, add sysfs entries */ + if ((dev->priv_flags && IFF_EBRIDGE) && event == NETDEV_REGISTER) { + br_sysfs_addbr(dev); + return NOTIFY_DONE; + } + /* not a port of a bridge */ p = br_port_get_rtnl(dev); if (!p) ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 7/7] bridge: range check STP parameters 2011-04-05 0:03 [PATCH 0/7] bridge enhancements for net-next Stephen Hemminger ` (5 preceding siblings ...) 2011-04-05 0:03 ` [PATCH 6/7] bridge: allow creating bridge devices with netlink Stephen Hemminger @ 2011-04-05 0:03 ` Stephen Hemminger 2011-04-05 0:23 ` [PATCH 0/7] bridge enhancements for net-next David Miller 7 siblings, 0 replies; 12+ messages in thread From: Stephen Hemminger @ 2011-04-05 0:03 UTC (permalink / raw) To: David S. Miller, Sasikanth V; +Cc: netdev [-- Attachment #1: br-param.patch --] [-- Type: text/plain, Size: 13447 bytes --] Apply restrictions on STP parameters based 802.1D 1998 standard. * Fixes missing locking in set path cost ioctl * Uses common code for both ioctl and sysfs This is based on an earlier patch Sasikanth V but with overhaul. Note: 1. It does NOT enforce the restriction on the relationship max_age and forward delay or hello time because in existing implementation these are set as independant operations. 2. If STP is disabled, there is no restriction on forward delay 3. No restriction on holding time because users use Linux code to act as hub or be sticky. 4. Although standard allow 0-255, Linux only allows 0-63 for port priority because more bits are reserved for port number. Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> --- net/bridge/br_ioctl.c | 40 ++++++++---------------------------- net/bridge/br_private.h | 13 ++++++++--- net/bridge/br_private_stp.h | 13 +++++++++++ net/bridge/br_stp.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ net/bridge/br_stp_if.c | 21 +++++++++++++++---- net/bridge/br_sysfs_br.c | 39 ++--------------------------------- net/bridge/br_sysfs_if.c | 26 +++++++---------------- 7 files changed, 107 insertions(+), 93 deletions(-) --- a/net/bridge/br_ioctl.c 2011-04-04 15:46:46.822291708 -0700 +++ b/net/bridge/br_ioctl.c 2011-04-04 16:34:29.164630581 -0700 @@ -181,40 +181,19 @@ static int old_dev_ioctl(struct net_devi if (!capable(CAP_NET_ADMIN)) return -EPERM; - spin_lock_bh(&br->lock); - br->bridge_forward_delay = clock_t_to_jiffies(args[1]); - if (br_is_root_bridge(br)) - br->forward_delay = br->bridge_forward_delay; - spin_unlock_bh(&br->lock); - return 0; + return br_set_forward_delay(br, args[1]); case BRCTL_SET_BRIDGE_HELLO_TIME: - { - unsigned long t = clock_t_to_jiffies(args[1]); if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (t < HZ) - return -EINVAL; - - spin_lock_bh(&br->lock); - br->bridge_hello_time = t; - if (br_is_root_bridge(br)) - br->hello_time = br->bridge_hello_time; - spin_unlock_bh(&br->lock); - return 0; - } + return br_set_hello_time(br, args[1]); case BRCTL_SET_BRIDGE_MAX_AGE: if (!capable(CAP_NET_ADMIN)) return -EPERM; - spin_lock_bh(&br->lock); - br->bridge_max_age = clock_t_to_jiffies(args[1]); - if (br_is_root_bridge(br)) - br->max_age = br->bridge_max_age; - spin_unlock_bh(&br->lock); - return 0; + return br_set_max_age(br, args[1]); case BRCTL_SET_AGEING_TIME: if (!capable(CAP_NET_ADMIN)) @@ -275,19 +254,16 @@ static int old_dev_ioctl(struct net_devi case BRCTL_SET_PORT_PRIORITY: { struct net_bridge_port *p; - int ret = 0; + int ret; if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (args[2] >= (1<<(16-BR_PORT_BITS))) - return -ERANGE; - spin_lock_bh(&br->lock); if ((p = br_get_port(br, args[1])) == NULL) ret = -EINVAL; else - br_stp_set_port_priority(p, args[2]); + ret = br_stp_set_port_priority(p, args[2]); spin_unlock_bh(&br->lock); return ret; } @@ -295,15 +271,17 @@ static int old_dev_ioctl(struct net_devi case BRCTL_SET_PATH_COST: { struct net_bridge_port *p; - int ret = 0; + int ret; if (!capable(CAP_NET_ADMIN)) return -EPERM; + spin_lock_bh(&br->lock); if ((p = br_get_port(br, args[1])) == NULL) ret = -EINVAL; else - br_stp_set_path_cost(p, args[2]); + ret = br_stp_set_path_cost(p, args[2]); + spin_unlock_bh(&br->lock); return ret; } --- a/net/bridge/br_private.h 2011-04-04 15:39:32.777614097 -0700 +++ b/net/bridge/br_private.h 2011-04-04 16:39:55.816195002 -0700 @@ -495,6 +495,11 @@ extern struct net_bridge_port *br_get_po extern void br_init_port(struct net_bridge_port *p); extern void br_become_designated_port(struct net_bridge_port *p); +extern int br_set_forward_delay(struct net_bridge *br, unsigned long x); +extern int br_set_hello_time(struct net_bridge *br, unsigned long x); +extern int br_set_max_age(struct net_bridge *br, unsigned long x); + + /* br_stp_if.c */ extern void br_stp_enable_bridge(struct net_bridge *br); extern void br_stp_disable_bridge(struct net_bridge *br); @@ -505,10 +510,10 @@ extern bool br_stp_recalculate_bridge_id extern void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *a); extern void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio); -extern void br_stp_set_port_priority(struct net_bridge_port *p, - u8 newprio); -extern void br_stp_set_path_cost(struct net_bridge_port *p, - u32 path_cost); +extern int br_stp_set_port_priority(struct net_bridge_port *p, + unsigned long newprio); +extern int br_stp_set_path_cost(struct net_bridge_port *p, + unsigned long path_cost); extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id); /* br_stp_bpdu.c */ --- a/net/bridge/br_private_stp.h 2011-04-04 15:40:02.125930146 -0700 +++ b/net/bridge/br_private_stp.h 2011-04-04 16:21:05.960073263 -0700 @@ -16,6 +16,19 @@ #define BPDU_TYPE_CONFIG 0 #define BPDU_TYPE_TCN 0x80 +/* IEEE 802.1D-1998 timer values */ +#define BR_MIN_HELLO_TIME (1*HZ) +#define BR_MAX_HELLO_TIME (10*HZ) + +#define BR_MIN_FORWARD_DELAY (2*HZ) +#define BR_MAX_FORWARD_DELAY (30*HZ) + +#define BR_MIN_MAX_AGE (6*HZ) +#define BR_MAX_MAX_AGE (40*HZ) + +#define BR_MIN_PATH_COST 1 +#define BR_MAX_PATH_COST 65535 + struct br_config_bpdu { unsigned topology_change:1; --- a/net/bridge/br_stp.c 2011-04-04 15:47:54.707023927 -0700 +++ b/net/bridge/br_stp.c 2011-04-04 16:33:49.908201916 -0700 @@ -484,3 +484,51 @@ void br_received_tcn_bpdu(struct net_bri br_topology_change_acknowledge(p); } } + +/* Change bridge STP parameter */ +int br_set_hello_time(struct net_bridge *br, unsigned long val) +{ + unsigned long t = clock_t_to_jiffies(val); + + if (t < BR_MIN_HELLO_TIME || t > BR_MAX_HELLO_TIME) + return -ERANGE; + + spin_lock_bh(&br->lock); + br->bridge_hello_time = t; + if (br_is_root_bridge(br)) + br->hello_time = br->bridge_hello_time; + spin_unlock_bh(&br->lock); + return 0; +} + +int br_set_max_age(struct net_bridge *br, unsigned long val) +{ + unsigned long t = clock_t_to_jiffies(val); + + if (t < BR_MIN_MAX_AGE || t > BR_MAX_MAX_AGE) + return -ERANGE; + + spin_lock_bh(&br->lock); + br->bridge_max_age = t; + if (br_is_root_bridge(br)) + br->max_age = br->bridge_max_age; + spin_unlock_bh(&br->lock); + return 0; + +} + +int br_set_forward_delay(struct net_bridge *br, unsigned long val) +{ + unsigned long t = clock_t_to_jiffies(val); + + if (br->stp_enabled != BR_NO_STP && + (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY)) + return -ERANGE; + + spin_lock_bh(&br->lock); + br->bridge_forward_delay = t; + if (br_is_root_bridge(br)) + br->forward_delay = br->bridge_forward_delay; + spin_unlock_bh(&br->lock); + return 0; +} --- a/net/bridge/br_stp_if.c 2011-04-04 16:11:56.538282617 -0700 +++ b/net/bridge/br_stp_if.c 2011-04-04 16:33:49.888201699 -0700 @@ -20,7 +20,7 @@ /* Port id is composed of priority and port number. - * NB: least significant bits of priority are dropped to + * NB: some bits of priority are dropped to * make room for more ports. */ static inline port_id br_make_port_id(__u8 priority, __u16 port_no) @@ -29,6 +29,8 @@ static inline port_id br_make_port_id(__ | (port_no & ((1<<BR_PORT_BITS)-1)); } +#define BR_MAX_PORT_PRIORITY ((u16)~0 >> BR_PORT_BITS) + /* called under bridge lock */ void br_init_port(struct net_bridge_port *p) { @@ -255,10 +257,14 @@ void br_stp_set_bridge_priority(struct n } /* called under bridge lock */ -void br_stp_set_port_priority(struct net_bridge_port *p, u8 newprio) +int br_stp_set_port_priority(struct net_bridge_port *p, unsigned long newprio) { - port_id new_port_id = br_make_port_id(newprio, p->port_no); + port_id new_port_id; + + if (newprio > BR_MAX_PORT_PRIORITY) + return -ERANGE; + new_port_id = br_make_port_id(newprio, p->port_no); if (br_is_designated_port(p)) p->designated_port = new_port_id; @@ -269,14 +275,21 @@ void br_stp_set_port_priority(struct net br_become_designated_port(p); br_port_state_selection(p->br); } + + return 0; } /* called under bridge lock */ -void br_stp_set_path_cost(struct net_bridge_port *p, u32 path_cost) +int br_stp_set_path_cost(struct net_bridge_port *p, unsigned long path_cost) { + if (path_cost < BR_MIN_PATH_COST || + path_cost > BR_MAX_PATH_COST) + return -ERANGE; + p->path_cost = path_cost; br_configuration_update(p->br); br_port_state_selection(p->br); + return 0; } ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id) --- a/net/bridge/br_sysfs_br.c 2011-04-04 15:56:51.112815302 -0700 +++ b/net/bridge/br_sysfs_br.c 2011-04-04 15:59:07.710269828 -0700 @@ -43,9 +43,7 @@ static ssize_t store_bridge_parm(struct if (endp == buf) return -EINVAL; - spin_lock_bh(&br->lock); err = (*set)(br, val); - spin_unlock_bh(&br->lock); return err ? err : len; } @@ -57,20 +55,11 @@ static ssize_t show_forward_delay(struct return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay)); } -static int set_forward_delay(struct net_bridge *br, unsigned long val) -{ - unsigned long delay = clock_t_to_jiffies(val); - br->forward_delay = delay; - if (br_is_root_bridge(br)) - br->bridge_forward_delay = delay; - return 0; -} - static ssize_t store_forward_delay(struct device *d, struct device_attribute *attr, const char *buf, size_t len) { - return store_bridge_parm(d, buf, len, set_forward_delay); + return store_bridge_parm(d, buf, len, br_set_forward_delay); } static DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR, show_forward_delay, store_forward_delay); @@ -82,24 +71,11 @@ static ssize_t show_hello_time(struct de jiffies_to_clock_t(to_bridge(d)->hello_time)); } -static int set_hello_time(struct net_bridge *br, unsigned long val) -{ - unsigned long t = clock_t_to_jiffies(val); - - if (t < HZ) - return -EINVAL; - - br->hello_time = t; - if (br_is_root_bridge(br)) - br->bridge_hello_time = t; - return 0; -} - static ssize_t store_hello_time(struct device *d, struct device_attribute *attr, const char *buf, size_t len) { - return store_bridge_parm(d, buf, len, set_hello_time); + return store_bridge_parm(d, buf, len, br_set_hello_time); } static DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time, store_hello_time); @@ -111,19 +87,10 @@ static ssize_t show_max_age(struct devic jiffies_to_clock_t(to_bridge(d)->max_age)); } -static int set_max_age(struct net_bridge *br, unsigned long val) -{ - unsigned long t = clock_t_to_jiffies(val); - br->max_age = t; - if (br_is_root_bridge(br)) - br->bridge_max_age = t; - return 0; -} - static ssize_t store_max_age(struct device *d, struct device_attribute *attr, const char *buf, size_t len) { - return store_bridge_parm(d, buf, len, set_max_age); + return store_bridge_parm(d, buf, len, br_set_max_age); } static DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age); --- a/net/bridge/br_sysfs_if.c 2011-04-04 16:11:07.277767864 -0700 +++ b/net/bridge/br_sysfs_if.c 2011-04-04 16:32:56.123614502 -0700 @@ -23,7 +23,7 @@ struct brport_attribute { struct attribute attr; ssize_t (*show)(struct net_bridge_port *, char *); - ssize_t (*store)(struct net_bridge_port *, unsigned long); + int (*store)(struct net_bridge_port *, unsigned long); }; #define BRPORT_ATTR(_name,_mode,_show,_store) \ @@ -38,27 +38,17 @@ static ssize_t show_path_cost(struct net { return sprintf(buf, "%d\n", p->path_cost); } -static ssize_t store_path_cost(struct net_bridge_port *p, unsigned long v) -{ - br_stp_set_path_cost(p, v); - return 0; -} + static BRPORT_ATTR(path_cost, S_IRUGO | S_IWUSR, - show_path_cost, store_path_cost); + show_path_cost, br_stp_set_path_cost); static ssize_t show_priority(struct net_bridge_port *p, char *buf) { return sprintf(buf, "%d\n", p->priority); } -static ssize_t store_priority(struct net_bridge_port *p, unsigned long v) -{ - if (v >= (1<<(16-BR_PORT_BITS))) - return -ERANGE; - br_stp_set_port_priority(p, v); - return 0; -} + static BRPORT_ATTR(priority, S_IRUGO | S_IWUSR, - show_priority, store_priority); + show_priority, br_stp_set_port_priority); static ssize_t show_designated_root(struct net_bridge_port *p, char *buf) { @@ -136,7 +126,7 @@ static ssize_t show_hold_timer(struct ne } static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL); -static ssize_t store_flush(struct net_bridge_port *p, unsigned long v) +static int store_flush(struct net_bridge_port *p, unsigned long v) { br_fdb_delete_by_port(p->br, p, 0); // Don't delete local entry return 0; @@ -148,7 +138,7 @@ static ssize_t show_hairpin_mode(struct int hairpin_mode = (p->flags & BR_HAIRPIN_MODE) ? 1 : 0; return sprintf(buf, "%d\n", hairpin_mode); } -static ssize_t store_hairpin_mode(struct net_bridge_port *p, unsigned long v) +static int store_hairpin_mode(struct net_bridge_port *p, unsigned long v) { if (v) p->flags |= BR_HAIRPIN_MODE; @@ -165,7 +155,7 @@ static ssize_t show_multicast_router(str return sprintf(buf, "%d\n", p->multicast_router); } -static ssize_t store_multicast_router(struct net_bridge_port *p, +static int store_multicast_router(struct net_bridge_port *p, unsigned long v) { return br_multicast_set_port_router(p, v); ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 0/7] bridge enhancements for net-next 2011-04-05 0:03 [PATCH 0/7] bridge enhancements for net-next Stephen Hemminger ` (6 preceding siblings ...) 2011-04-05 0:03 ` [PATCH 7/7] bridge: range check STP parameters Stephen Hemminger @ 2011-04-05 0:23 ` David Miller 7 siblings, 0 replies; 12+ messages in thread From: David Miller @ 2011-04-05 0:23 UTC (permalink / raw) To: shemminger; +Cc: netdev From: Stephen Hemminger <shemminger@vyatta.com> Date: Mon, 04 Apr 2011 17:03:26 -0700 > These patches add more netlink support for bridge. > It is possible to do basic configuration bridge with just netlink. > Later enhancements will add statistics and parameters. > > The intention is to switch to pure netlink in future and support > RSTP and deprecate the old ioctl, sysfs and STP code. Looks good, all applied, thanks Stephen. ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2011-10-05 19:36 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-04-05 0:03 [PATCH 0/7] bridge enhancements for net-next Stephen Hemminger 2011-04-05 0:03 ` [PATCH 1/7] bridge: change arguments to fdb_create Stephen Hemminger 2011-04-05 0:03 ` [PATCH 2/7] bridge: track last used time in forwarding table Stephen Hemminger 2011-04-05 0:03 ` [PATCH 3/7] bridge: split rcu and no-rcu cases of fdb lookup Stephen Hemminger 2011-04-05 0:03 ` [PATCH 4/7] bridge: add netlink notification on forward entry changes Stephen Hemminger 2011-04-05 0:03 ` [PATCH 5/7] bridge: allow creating/deleting fdb entries via netlink Stephen Hemminger 2011-10-05 19:06 ` Kevin Wilson 2011-10-05 19:13 ` Stephen Hemminger 2011-10-05 19:36 ` Ben Hutchings 2011-04-05 0:03 ` [PATCH 6/7] bridge: allow creating bridge devices with netlink Stephen Hemminger 2011-04-05 0:03 ` [PATCH 7/7] bridge: range check STP parameters Stephen Hemminger 2011-04-05 0:23 ` [PATCH 0/7] bridge enhancements for net-next David Miller
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).