mctp: Add neighbour implementation
authorMatt Johnston <matt@codeconstruct.com.au>
Thu, 29 Jul 2021 02:20:47 +0000 (10:20 +0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 29 Jul 2021 14:06:50 +0000 (15:06 +0100)
Add an initial neighbour table implementation, to be used in the route
output path.

Signed-off-by: Matt Johnston <matt@codeconstruct.com.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/mctp.h
include/net/mctpdevice.h
include/net/netns/mctp.h
net/mctp/Makefile
net/mctp/af_mctp.c
net/mctp/device.c
net/mctp/neigh.c [new file with mode: 0644]

index bc36e37..53f035c 100644 (file)
@@ -117,6 +117,31 @@ int mctp_route_add_local(struct mctp_dev *mdev, mctp_eid_t addr);
 int mctp_route_remove_local(struct mctp_dev *mdev, mctp_eid_t addr);
 void mctp_route_remove_dev(struct mctp_dev *mdev);
 
+/* neighbour definitions */
+enum mctp_neigh_source {
+       MCTP_NEIGH_STATIC,
+       MCTP_NEIGH_DISCOVER,
+};
+
+struct mctp_neigh {
+       struct mctp_dev         *dev;
+       mctp_eid_t              eid;
+       enum mctp_neigh_source  source;
+
+       unsigned char           ha[MAX_ADDR_LEN];
+
+       struct list_head        list;
+       struct rcu_head         rcu;
+};
+
+int mctp_neigh_init(void);
+void mctp_neigh_exit(void);
+
+// ret_hwaddr may be NULL, otherwise must have space for MAX_ADDR_LEN
+int mctp_neigh_lookup(struct mctp_dev *dev, mctp_eid_t eid,
+                     void *ret_hwaddr);
+void mctp_neigh_remove_dev(struct mctp_dev *mdev);
+
 int mctp_routes_init(void);
 void mctp_routes_exit(void);
 
index 71a1101..57e773f 100644 (file)
@@ -31,5 +31,6 @@ struct mctp_dev {
 
 struct mctp_dev *mctp_dev_get_rtnl(const struct net_device *dev);
 struct mctp_dev *__mctp_dev_get(const struct net_device *dev);
+struct mctp_dev *mctp_dev_get_rtnl(const struct net_device *dev);
 
 #endif /* __NET_MCTPDEVICE_H */
index 508459b..2f5ebee 100644 (file)
 struct netns_mctp {
        /* Only updated under RTNL, entries freed via RCU */
        struct list_head routes;
+
+       /* neighbour table */
+       struct mutex neigh_lock;
+       struct list_head neighbours;
 };
 
 #endif /* __NETNS_MCTP_H__ */
index b1a330e..0171333 100644 (file)
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_MCTP) += mctp.o
-mctp-objs := af_mctp.o device.o route.o
+mctp-objs := af_mctp.o device.o route.o neigh.o
index 8085f59..58701e6 100644 (file)
@@ -161,6 +161,10 @@ static __init int mctp_init(void)
        if (rc)
                goto err_unreg_proto;
 
+       rc = mctp_neigh_init();
+       if (rc)
+               goto err_unreg_proto;
+
        mctp_device_init();
 
        return 0;
@@ -176,6 +180,7 @@ err_unreg_sock:
 static __exit void mctp_exit(void)
 {
        mctp_device_exit();
+       mctp_neigh_exit();
        mctp_routes_exit();
        proto_unregister(&mctp_proto);
        sock_unregister(PF_MCTP);
index 5f1b18c..aa04959 100644 (file)
@@ -342,6 +342,7 @@ static void mctp_unregister(struct net_device *dev)
        RCU_INIT_POINTER(mdev->dev->mctp_ptr, NULL);
 
        mctp_route_remove_dev(mdev);
+       mctp_neigh_remove_dev(mdev);
        kfree(mdev->addrs);
 
        mctp_dev_destroy(mdev);
diff --git a/net/mctp/neigh.c b/net/mctp/neigh.c
new file mode 100644 (file)
index 0000000..8603f0c
--- /dev/null
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Management Component Transport Protocol (MCTP) - routing
+ * implementation.
+ *
+ * This is currently based on a simple routing table, with no dst cache. The
+ * number of routes should stay fairly small, so the lookup cost is small.
+ *
+ * Copyright (c) 2021 Code Construct
+ * Copyright (c) 2021 Google
+ */
+
+#include <linux/idr.h>
+#include <linux/mctp.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/skbuff.h>
+
+#include <net/mctp.h>
+#include <net/mctpdevice.h>
+#include <net/netlink.h>
+#include <net/sock.h>
+
+static int __always_unused mctp_neigh_add(struct mctp_dev *mdev, mctp_eid_t eid,
+                                         enum mctp_neigh_source source,
+                                         size_t lladdr_len, const void *lladdr)
+{
+       struct net *net = dev_net(mdev->dev);
+       struct mctp_neigh *neigh;
+       int rc;
+
+       mutex_lock(&net->mctp.neigh_lock);
+       if (mctp_neigh_lookup(mdev, eid, NULL) == 0) {
+               rc = -EEXIST;
+               goto out;
+       }
+
+       if (lladdr_len > sizeof(neigh->ha)) {
+               rc = -EINVAL;
+               goto out;
+       }
+
+       neigh = kzalloc(sizeof(*neigh), GFP_KERNEL);
+       if (!neigh) {
+               rc = -ENOMEM;
+               goto out;
+       }
+       INIT_LIST_HEAD(&neigh->list);
+       neigh->dev = mdev;
+       dev_hold(neigh->dev->dev);
+       neigh->eid = eid;
+       neigh->source = source;
+       memcpy(neigh->ha, lladdr, lladdr_len);
+
+       list_add_rcu(&neigh->list, &net->mctp.neighbours);
+       rc = 0;
+out:
+       mutex_unlock(&net->mctp.neigh_lock);
+       return rc;
+}
+
+static void __mctp_neigh_free(struct rcu_head *rcu)
+{
+       struct mctp_neigh *neigh = container_of(rcu, struct mctp_neigh, rcu);
+
+       dev_put(neigh->dev->dev);
+       kfree(neigh);
+}
+
+/* Removes all neighbour entries referring to a device */
+void mctp_neigh_remove_dev(struct mctp_dev *mdev)
+{
+       struct net *net = dev_net(mdev->dev);
+       struct mctp_neigh *neigh, *tmp;
+
+       mutex_lock(&net->mctp.neigh_lock);
+       list_for_each_entry_safe(neigh, tmp, &net->mctp.neighbours, list) {
+               if (neigh->dev == mdev) {
+                       list_del_rcu(&neigh->list);
+                       /* TODO: immediate RTM_DELNEIGH */
+                       call_rcu(&neigh->rcu, __mctp_neigh_free);
+               }
+       }
+
+       mutex_unlock(&net->mctp.neigh_lock);
+}
+
+int mctp_neigh_lookup(struct mctp_dev *mdev, mctp_eid_t eid, void *ret_hwaddr)
+{
+       struct net *net = dev_net(mdev->dev);
+       struct mctp_neigh *neigh;
+       int rc = -EHOSTUNREACH; // TODO: or ENOENT?
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(neigh, &net->mctp.neighbours, list) {
+               if (mdev == neigh->dev && eid == neigh->eid) {
+                       if (ret_hwaddr)
+                               memcpy(ret_hwaddr, neigh->ha,
+                                      sizeof(neigh->ha));
+                       rc = 0;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+       return rc;
+}
+
+/* namespace registration */
+static int __net_init mctp_neigh_net_init(struct net *net)
+{
+       struct netns_mctp *ns = &net->mctp;
+
+       INIT_LIST_HEAD(&ns->neighbours);
+       return 0;
+}
+
+static void __net_exit mctp_neigh_net_exit(struct net *net)
+{
+       struct netns_mctp *ns = &net->mctp;
+       struct mctp_neigh *neigh;
+
+       list_for_each_entry(neigh, &ns->neighbours, list)
+               call_rcu(&neigh->rcu, __mctp_neigh_free);
+}
+
+/* net namespace implementation */
+
+static struct pernet_operations mctp_net_ops = {
+       .init = mctp_neigh_net_init,
+       .exit = mctp_neigh_net_exit,
+};
+
+int __init mctp_neigh_init(void)
+{
+       return register_pernet_subsys(&mctp_net_ops);
+}
+
+void __exit mctp_neigh_exit(void)
+{
+       unregister_pernet_subsys(&mctp_net_ops);
+}