Skip to content

Commit

Permalink
bridge: allow creating/deleting fdb entries via netlink
Browse files Browse the repository at this point in the history
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>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
stephen hemminger authored and David S. Miller committed Apr 5, 2011
1 parent b078f0d commit 36fd2b6
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 0 deletions.
139 changes: 139 additions & 0 deletions net/bridge/br_fdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -555,3 +555,142 @@ int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)

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;
}
3 changes: 3 additions & 0 deletions net/bridge/br_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions net/bridge/br_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ 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);
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,
Expand Down

0 comments on commit 36fd2b6

Please sign in to comment.