#define NTF_PROXY      0x08    /* == ATF_PUBL */
 #define NTF_ROUTER     0x80
 
+#define NTF_SELF       0x02
+#define NTF_MASTER     0x04
+
 /*
  *     Neighbor Cache Entry States.
  */
 
 #include <net/netprio_cgroup.h>
 
 #include <linux/netdev_features.h>
+#include <linux/neighbour.h>
 
 struct netpoll_info;
 struct device;
  *     feature set might be less than what was returned by ndo_fix_features()).
  *     Must return >0 or -errno if it changed dev->features itself.
  *
+ * int (*ndo_fdb_add)(struct ndmsg *ndm, struct net_device *dev,
+ *                   unsigned char *addr, u16 flags)
+ *     Adds an FDB entry to dev for addr.
+ * int (*ndo_fdb_del)(struct ndmsg *ndm, struct net_device *dev,
+ *                   unsigned char *addr)
+ *     Deletes the FDB entry from dev coresponding to addr.
+ * int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb,
+ *                    struct net_device *dev, int idx)
+ *     Used to add FDB entries to dump requests. Implementers should add
+ *     entries to skb and update idx with the number of entries.
  */
 struct net_device_ops {
        int                     (*ndo_init)(struct net_device *dev);
                                                    netdev_features_t features);
        int                     (*ndo_neigh_construct)(struct neighbour *n);
        void                    (*ndo_neigh_destroy)(struct neighbour *n);
+
+       int                     (*ndo_fdb_add)(struct ndmsg *ndm,
+                                              struct net_device *dev,
+                                              unsigned char *addr,
+                                              u16 flags);
+       int                     (*ndo_fdb_del)(struct ndmsg *ndm,
+                                              struct net_device *dev,
+                                              unsigned char *addr);
+       int                     (*ndo_fdb_dump)(struct sk_buff *skb,
+                                               struct netlink_callback *cb,
+                                               struct net_device *dev,
+                                               int idx);
 };
 
 /*
 
        return table;
 }
 
+extern int ndo_dflt_fdb_dump(struct sk_buff *skb,
+                            struct netlink_callback *cb,
+                            struct net_device *dev,
+                            int idx);
 #endif /* __KERNEL__ */
 
 
 
        .ndo_add_slave           = br_add_slave,
        .ndo_del_slave           = br_del_slave,
        .ndo_fix_features        = br_fix_features,
+       .ndo_fdb_add             = br_fdb_add,
+       .ndo_fdb_del             = br_fdb_delete,
+       .ndo_fdb_dump            = br_fdb_dump,
 };
 
 static void br_dev_free(struct net_device *dev)
 
 }
 
 /* Dump information about entries, in response to GETNEIGH */
-int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
+int br_fdb_dump(struct sk_buff *skb,
+               struct netlink_callback *cb,
+               struct net_device *dev,
+               int idx)
 {
-       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;
+       struct net_bridge *br = netdev_priv(dev);
+       int i;
 
-               for (i = 0; i < BR_HASH_SIZE; i++) {
-                       struct hlist_node *h;
-                       struct net_bridge_fdb_entry *f;
+       if (!(dev->priv_flags & IFF_EBRIDGE))
+               goto out;
 
-                       hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) {
-                               if (idx < cb->args[0])
-                                       goto skip;
+       for (i = 0; i < BR_HASH_SIZE; i++) {
+               struct hlist_node *h;
+               struct net_bridge_fdb_entry *f;
 
-                               if (fdb_fill_info(skb, br, f,
-                                                 NETLINK_CB(cb->skb).pid,
-                                                 cb->nlh->nlmsg_seq,
-                                                 RTM_NEWNEIGH,
-                                                 NLM_F_MULTI) < 0)
-                                       break;
+               hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) {
+                       if (idx < cb->args[0])
+                               goto skip;
+
+                       if (fdb_fill_info(skb, br, f,
+                                         NETLINK_CB(cb->skb).pid,
+                                         cb->nlh->nlmsg_seq,
+                                         RTM_NEWNEIGH,
+                                         NLM_F_MULTI) < 0)
+                               break;
 skip:
-                               ++idx;
-                       }
+                       ++idx;
                }
        }
-       rcu_read_unlock();
-
-       cb->args[0] = idx;
 
-       return skb->len;
+out:
+       return idx;
 }
 
 /* Update (create or replace) forwarding database entry */
 }
 
 /* Add new permanent fdb entry with RTM_NEWNEIGH */
-int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+int br_fdb_add(struct ndmsg *ndm, struct net_device *dev,
+              unsigned char *addr, u16 nlh_flags)
 {
-       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;
-       }
+       int err = 0;
 
        if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) {
                pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state);
                rcu_read_unlock();
        } else {
                spin_lock_bh(&p->br->hash_lock);
-               err = fdb_add_entry(p, addr, ndm->ndm_state, nlh->nlmsg_flags);
+               err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags);
                spin_unlock_bh(&p->br->hash_lock);
        }
 
        return err;
 }
 
-static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr)
+static int fdb_delete_by_addr(struct net_bridge_port *p, u8 *addr)
 {
        struct net_bridge *br = p->br;
        struct hlist_head *head = &br->hash[br_mac_hash(addr)];
 }
 
 /* Remove neighbor entry with RTM_DELNEIGH */
-int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev,
+                 unsigned char *addr)
 {
-       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",
 
                              br_rtm_setlink, NULL, NULL);
        if (err)
                goto err3;
-       err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH,
-                             br_fdb_add, NULL, NULL);
-       if (err)
-               goto err3;
-       err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH,
-                             br_fdb_delete, NULL, NULL);
-       if (err)
-               goto err3;
-       err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH,
-                             NULL, br_fdb_dump, NULL);
-       if (err)
-               goto err3;
 
        return 0;
 
 
 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);
+
+extern int br_fdb_delete(struct ndmsg *ndm,
+                        struct net_device *dev,
+                        unsigned char *addr);
+extern int br_fdb_add(struct ndmsg *nlh,
+                     struct net_device *dev,
+                     unsigned char *addr,
+                     u16 nlh_flags);
+extern int br_fdb_dump(struct sk_buff *skb,
+                      struct netlink_callback *cb,
+                      struct net_device *dev,
+                      int idx);
 
 /* br_forward.c */
 extern void br_deliver(const struct net_bridge_port *to,
 
 #include <linux/security.h>
 #include <linux/mutex.h>
 #include <linux/if_addr.h>
+#include <linux/if_bridge.h>
 #include <linux/pci.h>
+#include <linux/etherdevice.h>
 
 #include <asm/uaccess.h>
 
                rtnl_set_sk_err(net, RTNLGRP_LINK, err);
 }
 
+static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+       struct net *net = sock_net(skb->sk);
+       struct net_device *master = NULL;
+       struct ndmsg *ndm;
+       struct nlattr *tb[NDA_MAX+1];
+       struct net_device *dev;
+       u8 *addr;
+       int err;
+
+       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("PF_BRIDGE: RTM_NEWNEIGH with invalid ifindex\n");
+               return -EINVAL;
+       }
+
+       dev = __dev_get_by_index(net, ndm->ndm_ifindex);
+       if (dev == NULL) {
+               pr_info("PF_BRIDGE: RTM_NEWNEIGH with unknown ifindex\n");
+               return -ENODEV;
+       }
+
+       if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) {
+               pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid address\n");
+               return -EINVAL;
+       }
+
+       addr = nla_data(tb[NDA_LLADDR]);
+       if (!is_valid_ether_addr(addr)) {
+               pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid ether address\n");
+               return -EINVAL;
+       }
+
+       err = -EOPNOTSUPP;
+
+       /* Support fdb on master device the net/bridge default case */
+       if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) &&
+           (dev->priv_flags & IFF_BRIDGE_PORT)) {
+               master = dev->master;
+               err = master->netdev_ops->ndo_fdb_add(ndm, dev, addr,
+                                                     nlh->nlmsg_flags);
+               if (err)
+                       goto out;
+               else
+                       ndm->ndm_flags &= ~NTF_MASTER;
+       }
+
+       /* Embedded bridge, macvlan, and any other device support */
+       if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_add) {
+               err = dev->netdev_ops->ndo_fdb_add(ndm, dev, addr,
+                                                  nlh->nlmsg_flags);
+
+               if (!err)
+                       ndm->ndm_flags &= ~NTF_SELF;
+       }
+out:
+       return err;
+}
+
+static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+       struct net *net = sock_net(skb->sk);
+       struct ndmsg *ndm;
+       struct nlattr *llattr;
+       struct net_device *dev;
+       int err = -EINVAL;
+       __u8 *addr;
+
+       if (nlmsg_len(nlh) < sizeof(*ndm))
+               return -EINVAL;
+
+       ndm = nlmsg_data(nlh);
+       if (ndm->ndm_ifindex == 0) {
+               pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid ifindex\n");
+               return -EINVAL;
+       }
+
+       dev = __dev_get_by_index(net, ndm->ndm_ifindex);
+       if (dev == NULL) {
+               pr_info("PF_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("PF_BRIGDE: RTM_DELNEIGH with invalid address\n");
+               return -EINVAL;
+       }
+
+       addr = nla_data(llattr);
+       err = -EOPNOTSUPP;
+
+       /* Support fdb on master device the net/bridge default case */
+       if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) &&
+           (dev->priv_flags & IFF_BRIDGE_PORT)) {
+               struct net_device *master = dev->master;
+
+               if (master->netdev_ops->ndo_fdb_del)
+                       err = master->netdev_ops->ndo_fdb_del(ndm, dev, addr);
+
+               if (err)
+                       goto out;
+               else
+                       ndm->ndm_flags &= ~NTF_MASTER;
+       }
+
+       /* Embedded bridge, macvlan, and any other device support */
+       if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_del) {
+               err = dev->netdev_ops->ndo_fdb_del(ndm, dev, addr);
+
+               if (!err)
+                       ndm->ndm_flags &= ~NTF_SELF;
+       }
+out:
+       return err;
+}
+
+static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       int idx = 0;
+       struct net *net = sock_net(skb->sk);
+       struct net_device *dev;
+
+       rcu_read_lock();
+       for_each_netdev_rcu(net, dev) {
+               if (dev->priv_flags & IFF_BRIDGE_PORT) {
+                       struct net_device *master = dev->master;
+                       const struct net_device_ops *ops = master->netdev_ops;
+
+                       if (ops->ndo_fdb_dump)
+                               idx = ops->ndo_fdb_dump(skb, cb, dev, idx);
+               }
+
+               if (dev->netdev_ops->ndo_fdb_dump)
+                       idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, idx);
+       }
+       rcu_read_unlock();
+
+       cb->args[0] = idx;
+       return skb->len;
+}
+
 /* Protected by RTNL sempahore.  */
 static struct rtattr **rta_buf;
 static int rtattr_max;
 
        rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, NULL);
        rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, NULL);
+
+       rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, NULL);
+       rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, NULL);
+       rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL);
 }