summaryrefslogtreecommitdiffstats
path: root/utils/extras/devices/rtnetlink/netns.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/extras/devices/rtnetlink/netns.c')
-rw-r--r--utils/extras/devices/rtnetlink/netns.c787
1 files changed, 787 insertions, 0 deletions
diff --git a/utils/extras/devices/rtnetlink/netns.c b/utils/extras/devices/rtnetlink/netns.c
new file mode 100644
index 000000000..19adb469a
--- /dev/null
+++ b/utils/extras/devices/rtnetlink/netns.c
@@ -0,0 +1,787 @@
+/*
+ * Copyright (c) 2016-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/ip/format.h>
+#include <stddef.h>
+
+#include "netns.h"
+
+/* Enable some RTA values debug */
+//#define RTNL_CHECK
+
+#define is_nonzero(x) \
+ ({ \
+ u8 __is_zero_zero[sizeof(x)] = {}; \
+ memcmp(__is_zero_zero, &x, sizeof(x)); \
+ })
+
+typedef struct {
+ u8 type; //Attribute identifier
+ u8 unique; //Part of the values uniquely identifying an entry
+ u16 offset; //Offset where stored in struct
+ u16 size; //Length of the attribute
+} rtnl_mapping_t;
+
+#define ns_foreach_ifla \
+ _(IFLA_ADDRESS, hwaddr) \
+ _(IFLA_BROADCAST, broadcast) \
+ _(IFLA_IFNAME, ifname) \
+ _(IFLA_MASTER, master) \
+ _(IFLA_MTU, mtu) \
+ _(IFLA_QDISC, qdisc)
+
+static rtnl_mapping_t ns_ifmap[] = {
+#define _(t, e) \
+ { \
+ .type = t, \
+ .offset = offsetof(ns_link_t, e), \
+ .size = sizeof(((ns_link_t*)0)->e) \
+ },
+ ns_foreach_ifla
+#undef _
+ { .type = 0 }
+};
+
+u8 *format_ns_link (u8 *s, va_list *args)
+{
+ ns_link_t *l = va_arg(*args, ns_link_t *);
+ s = format(s, "%s index %u", l->ifname, l->ifi.ifi_index);
+ return s;
+}
+
+#define ns_foreach_rta \
+ _(RTA_DST, dst, 1) \
+ _(RTA_SRC, src, 1) \
+ _(RTA_GATEWAY, gateway, 1) \
+ _(RTA_IIF, iif, 1) \
+ _(RTA_OIF, oif, 1) \
+ _(RTA_PREFSRC, prefsrc, 0) \
+ _(RTA_TABLE, table, 0) \
+ _(RTA_PRIORITY, priority, 0) \
+ _(RTA_CACHEINFO, cacheinfo, 0) \
+ _(RTA_ENCAP, encap, 1)
+
+static rtnl_mapping_t ns_routemap[] = {
+#define _(t, e, u) \
+ { \
+ .type = t, .unique = u, \
+ .offset = offsetof(ns_route_t, e), \
+ .size = sizeof(((ns_route_t*)0)->e) \
+ },
+ ns_foreach_rta
+#undef _
+ { .type = 0 }
+};
+
+u8 *format_ns_route (u8 *s, va_list *args)
+{
+ ns_route_t *r = va_arg(*args, ns_route_t *);
+ void *format_ip = r->rtm.rtm_family == AF_INET ? format_ip4_address : format_ip6_address;
+ s = format(s, "%U/%d", format_ip, r->dst, r->rtm.rtm_dst_len);
+ if (r->rtm.rtm_src_len)
+ s = format(s, " from %U/%d", format_ip, r->src, r->rtm.rtm_src_len);
+ if (is_nonzero(r->gateway))
+ s = format(s, " via %U", format_ip, r->gateway);
+ if (r->iif)
+ s = format(s, " iif %d", r->iif);
+ if (r->oif)
+ s = format(s, " oif %d", r->oif);
+ if (is_nonzero(r->prefsrc))
+ s = format(s, " src %U", format_ip, r->prefsrc);
+ if (r->table)
+ s = format(s, " table %d", r->table);
+ if (r->priority)
+ s = format(s, " priority %u", r->priority);
+ return s;
+}
+
+#define ns_foreach_ifaddr \
+ _(IFA_ADDRESS, addr, 1) \
+ _(IFA_LOCAL, local, 1) \
+ _(IFA_LABEL, label, 0) \
+ _(IFA_BROADCAST, broadcast, 0) \
+ _(IFA_ANYCAST, anycast, 0) \
+ _(IFA_CACHEINFO, cacheinfo, 0)
+
+static rtnl_mapping_t ns_addrmap[] = {
+#define _(t, e, u) \
+ { \
+ .type = t, .unique = u, \
+ .offset = offsetof(ns_addr_t, e), \
+ .size = sizeof(((ns_addr_t*)0)->e) \
+ },
+ ns_foreach_ifaddr
+#undef _
+ { .type = 0 }
+};
+
+u8 *format_ns_addr (u8 *s, va_list *args)
+{
+ ns_addr_t *a = va_arg(*args, ns_addr_t *);
+ void *format_ip = a->ifaddr.ifa_family == AF_INET ? format_ip4_address : format_ip6_address;
+ s = format(s, "%U/%d", format_ip, a->addr, a->ifaddr.ifa_prefixlen);
+ if (is_nonzero(a->label))
+ s = format(s, " dev %s", a->label);
+ if (is_nonzero(a->broadcast))
+ s = format(s, " broadcast %U", format_ip, a->broadcast);
+ if (is_nonzero(a->anycast))
+ s = format(s, " anycast %U", format_ip, a->anycast);
+ if (is_nonzero(a->local))
+ s = format(s, " local %U", format_ip, a->local);
+ return s;
+}
+
+#ifndef NDA_RTA
+#define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+#endif
+
+#define ns_foreach_neigh \
+ _(NDA_DST, dst, 1) \
+ _(NDA_LLADDR, lladdr, 0) \
+ _(NDA_PROBES, probes, 0) \
+ _(NDA_CACHEINFO, cacheinfo, 0)
+
+static rtnl_mapping_t ns_neighmap[] = {
+#define _(t, e, u) \
+ { \
+ .type = t, .unique = u, \
+ .offset = offsetof(ns_neigh_t, e), \
+ .size = sizeof(((ns_neigh_t*)0)->e) \
+ },
+ ns_foreach_neigh
+#undef _
+ { .type = 0 }
+};
+
+u8 *format_ns_neigh (u8 *s, va_list *args)
+{
+ ns_neigh_t *n = va_arg(*args, ns_neigh_t *);
+ void *format_ip = n->nd.ndm_family == AF_INET ? format_ip4_address : format_ip6_address;
+ s = format(s, "%U", format_ip, n->dst);
+ if (is_nonzero(n->lladdr))
+ s = format(s, " lladdr %U", format_ethernet_address, n->lladdr);
+ if (n->probes)
+ s = format(s, " probes %d", n->probes);
+ return s;
+}
+
+typedef struct {
+ void (*notify)(void *obj, netns_type_t type, u32 flags, uword opaque);
+ uword opaque;
+ u32 netns_index;
+} netns_handle_t;
+
+typedef struct {
+ netns_t netns;
+ u32 rtnl_handle;
+ u32 subscriber_count;
+} netns_p;
+
+typedef struct {
+ netns_p *netnss;
+ netns_handle_t *handles;
+} netns_main_t;
+
+netns_main_t netns_main;
+
+static int
+rtnl_parse_rtattr(struct rtattr *db[], size_t max,
+ struct rtattr *rta, size_t len) {
+ for(; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
+ if (rta->rta_type <= max)
+ db[rta->rta_type] = rta;
+#ifdef RTNL_CHECK
+ else
+ clib_warning("RTA type too high: %d", rta->rta_type);
+#endif
+ }
+
+ if(len) {
+ clib_warning("rattr lenght mistmatch %d %d len",
+ (int) len, (int) rta->rta_len);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Debug function to display when
+ * we receive an RTA that I forgot in
+ * the mapping table (there are so many of them).
+ */
+#ifdef RTNL_CHECK
+static void
+rtnl_entry_check(struct rtattr *rtas[],
+ size_t rta_len,
+ rtnl_mapping_t map[],
+ char *logstr)
+{
+ int i;
+ for (i=0; i<rta_len; i++) {
+ if (!rtas[i])
+ continue;
+
+ rtnl_mapping_t *m = map;
+ for (m = map; m->type; m++) {
+ if (m->type == rtas[i]->rta_type)
+ break;
+ }
+ if (!m->type)
+ clib_warning("Unknown RTA type %d (%s)", rtas[i]->rta_type, logstr);
+ }
+}
+#endif
+
+/*
+ * Check if the provided entry matches the parsed and unique rtas
+ */
+static int
+rtnl_entry_match(void *entry,
+ struct rtattr *rtas[],
+ rtnl_mapping_t map[])
+{
+ u8 zero[1024] = {};
+ for ( ;map->type != 0; map++) {
+ struct rtattr *rta = rtas[map->type];
+ size_t rta_len = rta?RTA_PAYLOAD(rta):0;
+ if (!map->unique)
+ continue;
+
+ if (rta && RTA_PAYLOAD(rta) > map->size) {
+ clib_warning("rta (type=%d len=%d) too long (max %d)",
+ rta->rta_type, rta->rta_len, map->size);
+ return -1;
+ }
+
+ if ((rta && memcmp(RTA_DATA(rta), entry + map->offset, rta_len)) ||
+ memcmp(entry + map->offset + rta_len, zero, map->size - rta_len)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+rtnl_entry_set(void *entry,
+ struct rtattr *rtas[],
+ rtnl_mapping_t map[],
+ int init)
+{
+ for (; map->type != 0; map++) {
+
+ struct rtattr *rta = rtas[map->type];
+
+ if(map->type == RTA_ENCAP && rta) {
+ /*Data of RTA_ENCAP is a pointer to rta attributes for MPLS*/
+ rta = (struct rtattr*)RTA_DATA(rta);
+ if (RTA_PAYLOAD(rta) > map->size) {
+ clib_warning("rta (type=%d len=%d) too long (max %d)", rta->rta_type, rta->rta_len, map->size);
+ return -1;
+ }
+ memcpy(entry + map->offset, RTA_DATA(rta), map->size);
+ memset(entry + map->offset + map->size, 0, 0);
+ } else if (rta) {
+ if (RTA_PAYLOAD(rta) > map->size) {
+ clib_warning("rta (type=%d len=%d) too long (max %d)", rta->rta_type, rta->rta_len, map->size);
+ return -1;
+ }
+ memcpy(entry + map->offset, RTA_DATA(rta), RTA_PAYLOAD(rta));
+ memset(entry + map->offset + RTA_PAYLOAD(rta), 0, map->size - RTA_PAYLOAD(rta));
+ } else if (init) {
+ memset(entry + map->offset, 0, map->size);
+ }
+ }
+ return 0;
+}
+
+void
+netns_notify(netns_p *ns, void *obj, netns_type_t type, u32 flags)
+{
+ netns_main_t *nm = &netns_main;
+ netns_handle_t *h;
+ pool_foreach(h, nm->handles, {
+ if (h->netns_index == (ns - nm->netnss) && h->notify)
+ h->notify(obj, type, flags, h->opaque);
+ });
+}
+
+static_always_inline int
+mask_match(void *a, void *b, void *mask, size_t len)
+{
+ u8 *va = (u8 *) a;
+ u8 *vb = (u8 *) b;
+ u8 *vm = (u8 *) mask;
+ while (len--) {
+ if ((va[len] ^ vb[len]) & vm[len])
+ return 0;
+ }
+ return 1;
+}
+
+static ns_link_t *
+ns_get_link(netns_p *ns, struct ifinfomsg *ifi, struct rtattr *rtas[])
+{
+ ns_link_t *link;
+ pool_foreach(link, ns->netns.links, {
+ if(ifi->ifi_index == link->ifi.ifi_index)
+ return link;
+ });
+ return NULL;
+}
+
+static int
+ns_rcv_link(netns_p *ns, struct nlmsghdr *hdr)
+{
+ ns_link_t *link;
+ struct ifinfomsg *ifi;
+ struct rtattr *rtas[IFLA_MAX + 1] = {};
+ size_t datalen = hdr->nlmsg_len - NLMSG_ALIGN(sizeof(*hdr));
+
+ if(datalen < sizeof(*ifi))
+ return -1;
+
+ ifi = NLMSG_DATA(hdr);
+ if((datalen > NLMSG_ALIGN(sizeof(*ifi))) &&
+ rtnl_parse_rtattr(rtas, IFLA_MAX, IFLA_RTA(ifi),
+ IFLA_PAYLOAD(hdr))) {
+ return -1;
+ }
+#ifdef RTNL_CHECK
+ rtnl_entry_check(rtas, IFLA_MAX + 1, ns_ifmap, "link");
+#endif
+
+ link = ns_get_link(ns, ifi, rtas);
+
+ if (hdr->nlmsg_type == RTM_DELLINK) {
+ if (!link)
+ return -1;
+ pool_put(ns->netns.links, link);
+ netns_notify(ns, link, NETNS_TYPE_LINK, NETNS_F_DEL);
+ return 0;
+ }
+
+ if (!link) {
+ pool_get(ns->netns.links, link);
+ rtnl_entry_set(link, rtas, ns_ifmap, 1);
+ } else {
+ rtnl_entry_set(link, rtas, ns_ifmap, 0);
+ }
+
+ link->ifi = *ifi;
+ link->last_updated = vlib_time_now(vlib_get_main());
+ netns_notify(ns, link, NETNS_TYPE_LINK, NETNS_F_ADD);
+ return 0;
+}
+
+static ns_route_t *
+ns_get_route(netns_p *ns, struct rtmsg *rtm, struct rtattr *rtas[])
+{
+ ns_route_t *route;
+
+ //This describes the values which uniquely identify a route
+ struct rtmsg msg = {
+ .rtm_family = 0xff,
+ .rtm_dst_len = 0xff,
+ .rtm_src_len = 0xff,
+ .rtm_table = 0xff,
+ .rtm_protocol = 0xff,
+ .rtm_type = 0xff
+ };
+
+ pool_foreach(route, ns->netns.routes, {
+ if(mask_match(&route->rtm, rtm, &msg, sizeof(struct rtmsg)) &&
+ rtnl_entry_match(route, rtas, ns_routemap))
+ return route;
+ });
+ return NULL;
+}
+
+static int
+ns_rcv_route(netns_p *ns, struct nlmsghdr *hdr)
+{
+ ns_route_t *route;
+ struct rtmsg *rtm;
+ struct rtattr *rtas[RTA_MAX + 1] = {};
+ size_t datalen = hdr->nlmsg_len - NLMSG_ALIGN(sizeof(*hdr));
+
+ if(datalen < sizeof(*rtm))
+ return -1;
+
+ rtm = NLMSG_DATA(hdr);
+ if((datalen > NLMSG_ALIGN(sizeof(*rtm))) &&
+ rtnl_parse_rtattr(rtas, RTA_MAX, RTM_RTA(rtm),
+ RTM_PAYLOAD(hdr))) {
+ return -1;
+ }
+#ifdef RTNL_CHECK
+ rtnl_entry_check(rtas, RTA_MAX + 1, ns_routemap, "route");
+#endif
+ route = ns_get_route(ns, rtm, rtas);
+
+ if (hdr->nlmsg_type == RTM_DELROUTE) {
+ if (!route)
+ return -1;
+ pool_put(ns->netns.routes, route);
+ netns_notify(ns, route, NETNS_TYPE_ROUTE, NETNS_F_DEL);
+ return 0;
+ }
+
+ if (!route) {
+ pool_get(ns->netns.routes, route);
+ memset(route, 0, sizeof(*route));
+ rtnl_entry_set(route, rtas, ns_routemap, 1);
+ } else {
+ rtnl_entry_set(route, rtas, ns_routemap, 0);
+ }
+
+ route->rtm = *rtm;
+ route->last_updated = vlib_time_now(vlib_get_main());
+ netns_notify(ns, route, NETNS_TYPE_ROUTE, NETNS_F_ADD);
+ return 0;
+}
+
+static ns_addr_t *
+ns_get_addr(netns_p *ns, struct ifaddrmsg *ifaddr, struct rtattr *rtas[])
+{
+ ns_addr_t *addr;
+
+ //This describes the values which uniquely identify a route
+ struct ifaddrmsg msg = {
+ .ifa_family = 0xff,
+ .ifa_prefixlen = 0xff,
+ };
+
+ pool_foreach(addr, ns->netns.addresses, {
+ if(mask_match(&addr->ifaddr, ifaddr, &msg, sizeof(struct ifaddrmsg)) &&
+ rtnl_entry_match(addr, rtas, ns_addrmap))
+ return addr;
+ });
+ return NULL;
+}
+
+static int
+ns_rcv_addr(netns_p *ns, struct nlmsghdr *hdr)
+{
+ ns_addr_t *addr;
+ struct ifaddrmsg *ifaddr;
+ struct rtattr *rtas[IFA_MAX + 1] = {};
+ size_t datalen = hdr->nlmsg_len - NLMSG_ALIGN(sizeof(*hdr));
+
+ if(datalen < sizeof(*ifaddr))
+ return -1;
+
+ ifaddr = NLMSG_DATA(hdr);
+ if((datalen > NLMSG_ALIGN(sizeof(*ifaddr))) &&
+ rtnl_parse_rtattr(rtas, IFA_MAX, IFA_RTA(ifaddr),
+ IFA_PAYLOAD(hdr))) {
+ return -1;
+ }
+#ifdef RTNL_CHECK
+ rtnl_entry_check(rtas, IFA_MAX + 1, ns_addrmap, "addr");
+#endif
+ addr = ns_get_addr(ns, ifaddr, rtas);
+
+ if (hdr->nlmsg_type == RTM_DELADDR) {
+ if (!addr)
+ return -1;
+ pool_put(ns->netns.addresses, addr);
+ netns_notify(ns, addr, NETNS_TYPE_ADDR, NETNS_F_DEL);
+ return 0;
+ }
+
+ if (!addr) {
+ pool_get(ns->netns.addresses, addr);
+ memset(addr, 0, sizeof(*addr));
+ rtnl_entry_set(addr, rtas, ns_addrmap, 1);
+ } else {
+ rtnl_entry_set(addr, rtas, ns_addrmap, 0);
+ }
+
+ addr->ifaddr = *ifaddr;
+ addr->last_updated = vlib_time_now(vlib_get_main());
+ netns_notify(ns, addr, NETNS_TYPE_ADDR, NETNS_F_ADD);
+ return 0;
+}
+
+static ns_neigh_t *
+ns_get_neigh(netns_p *ns, struct ndmsg *nd, struct rtattr *rtas[])
+{
+ ns_neigh_t *neigh;
+
+ //This describes the values which uniquely identify a route
+ struct ndmsg msg = {
+ .ndm_family = 0xff,
+ .ndm_ifindex = 0xff,
+ };
+
+ pool_foreach(neigh, ns->netns.neighbors, {
+ if(mask_match(&neigh->nd, nd, &msg, sizeof(&msg)) &&
+ rtnl_entry_match(neigh, rtas, ns_neighmap))
+ return neigh;
+ });
+ return NULL;
+}
+
+static int
+ns_rcv_neigh(netns_p *ns, struct nlmsghdr *hdr)
+{
+ ns_neigh_t *neigh;
+ struct ndmsg *nd;
+ struct rtattr *rtas[NDA_MAX + 1] = {};
+ size_t datalen = hdr->nlmsg_len - NLMSG_ALIGN(sizeof(*hdr));
+
+ if(datalen < sizeof(*nd))
+ return -1;
+
+ nd = NLMSG_DATA(hdr);
+ if((datalen > NLMSG_ALIGN(sizeof(*nd))) &&
+ rtnl_parse_rtattr(rtas, NDA_MAX, NDA_RTA(nd),
+ NDA_PAYLOAD(hdr))) {
+ return -1;
+ }
+#ifdef RTNL_CHECK
+ rtnl_entry_check(rtas, NDA_MAX + 1, ns_neighmap, "nd");
+#endif
+ neigh = ns_get_neigh(ns, nd, rtas);
+
+ if (hdr->nlmsg_type == RTM_DELNEIGH) {
+ if (!neigh)
+ return -1;
+ pool_put(ns->netns.neighbors, neigh);
+ netns_notify(ns, neigh, NETNS_TYPE_NEIGH, NETNS_F_DEL);
+ return 0;
+ }
+
+ if (!neigh) {
+ pool_get(ns->netns.neighbors, neigh);
+ memset(neigh, 0, sizeof(*neigh));
+ rtnl_entry_set(neigh, rtas, ns_neighmap, 1);
+ } else {
+ rtnl_entry_set(neigh, rtas, ns_neighmap, 0);
+ }
+
+ neigh->nd = *nd;
+ neigh->last_updated = vlib_time_now(vlib_get_main());
+ netns_notify(ns, neigh, NETNS_TYPE_NEIGH, NETNS_F_ADD);
+ return 0;
+}
+
+#define ns_object_foreach \
+ _(neighbors, NETNS_TYPE_NEIGH) \
+ _(routes, NETNS_TYPE_ROUTE) \
+ _(addresses, NETNS_TYPE_ADDR) \
+ _(links, NETNS_TYPE_LINK)
+
+static void
+ns_recv_error(rtnl_error_t err, uword o)
+{
+ //An error was received. Reset everything.
+ netns_p *ns = &netns_main.netnss[o];
+ u32 *indexes = 0;
+ u32 *i = 0;
+
+#define _(pool, type) \
+ pool_foreach_index(*i, ns->netns.pool, { \
+ vec_add1(indexes, *i); \
+ }) \
+ vec_foreach(i, indexes) { \
+ pool_put_index(ns->netns.pool, *i); \
+ netns_notify(ns, &ns->netns.pool[*i], type, NETNS_F_DEL); \
+ } \
+ vec_reset_length(indexes);
+
+ ns_object_foreach
+
+#undef _
+ vec_free(indexes);
+}
+
+static void
+ns_recv_rtnl(struct nlmsghdr *hdr, uword o)
+{
+ netns_p *ns = &netns_main.netnss[o];
+ switch (hdr->nlmsg_type) {
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ ns_rcv_route(ns, hdr);
+ break;
+ case RTM_NEWLINK:
+ case RTM_DELLINK:
+ ns_rcv_link(ns, hdr);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ns_rcv_addr(ns, hdr);
+ break;
+ case RTM_NEWNEIGH:
+ case RTM_DELNEIGH:
+ ns_rcv_neigh(ns, hdr);
+ break;
+ default:
+ clib_warning("unknown rtnl type %d", hdr->nlmsg_type);
+ break;
+ }
+}
+
+static void
+netns_destroy(netns_p *ns)
+{
+ netns_main_t *nm = &netns_main;
+ rtnl_stream_close(ns->rtnl_handle);
+ pool_put(nm->netnss, ns);
+ pool_free(ns->netns.links);
+ pool_free(ns->netns.addresses);
+ pool_free(ns->netns.routes);
+ pool_free(ns->netns.neighbors);
+}
+
+static netns_p *
+netns_get(char *name)
+{
+ netns_main_t *nm = &netns_main;
+ netns_p *ns;
+ pool_foreach(ns, nm->netnss, {
+ if (!strcmp(name, ns->netns.name))
+ return ns;
+ });
+
+ if (strlen(name) > RTNL_NETNS_NAMELEN)
+ return NULL;
+
+ pool_get(nm->netnss, ns);
+ rtnl_stream_t s = {
+ .recv_message = ns_recv_rtnl,
+ .error = ns_recv_error,
+ .opaque = (uword)(ns - nm->netnss),
+ };
+ strcpy(s.name, name);
+
+ u32 handle;
+ if ((handle = rtnl_stream_open(&s)) == ~0) {
+ pool_put(nm->netnss, ns);
+ return NULL;
+ }
+
+ strcpy(ns->netns.name, name);
+ ns->netns.addresses = 0;
+ ns->netns.links = 0;
+ ns->netns.neighbors = 0;
+ ns->netns.routes = 0;
+ ns->subscriber_count = 0;
+ ns->rtnl_handle = handle;
+ return ns;
+}
+
+u32 netns_open(char *name, netns_sub_t *sub)
+{
+ netns_main_t *nm = &netns_main;
+ netns_p *ns;
+ netns_handle_t *p;
+ if (!(ns = netns_get(name)))
+ return ~0;
+
+ pool_get(nm->handles, p);
+ p->netns_index = ns - nm->netnss;
+ p->notify = sub->notify;
+ p->opaque = sub->opaque;
+ ns->subscriber_count++;
+ return p - nm->handles;
+}
+
+netns_t *netns_getns(u32 handle)
+{
+ netns_main_t *nm = &netns_main;
+ netns_handle_t *h = pool_elt_at_index(nm->handles, handle);
+ netns_p *ns = pool_elt_at_index(nm->netnss, h->netns_index);
+ return &ns->netns;
+}
+
+void netns_close(u32 handle)
+{
+ netns_main_t *nm = &netns_main;
+ netns_handle_t *h = pool_elt_at_index(nm->handles, handle);
+ netns_p *ns = pool_elt_at_index(nm->netnss, h->netns_index);
+ pool_put(h, nm->handles);
+ ns->subscriber_count--;
+ if (!ns->subscriber_count)
+ netns_destroy(ns);
+}
+
+void netns_callme(u32 handle, char del)
+{
+ netns_main_t *nm = &netns_main;
+ netns_handle_t *h = pool_elt_at_index(nm->handles, handle);
+ netns_p *ns = pool_elt_at_index(nm->netnss, h->netns_index);
+ u32 i = 0;
+ if (!h->notify)
+ return;
+
+#define _(pool, type) \
+ pool_foreach_index(i, ns->netns.pool, { \
+ h->notify(&ns->netns.pool[i], type, \
+ del?NETNS_F_DEL:NETNS_F_ADD, h->opaque); \
+ });
+
+ ns_object_foreach
+#undef _
+
+ }
+
+u8 *format_ns_object(u8 *s, va_list *args)
+{
+ netns_type_t t = va_arg(*args, netns_type_t);
+ void *o = va_arg(*args, void *);
+ switch (t) {
+ case NETNS_TYPE_ADDR:
+ return format(s, "addr %U", format_ns_addr, o);
+ case NETNS_TYPE_ROUTE:
+ return format(s, "route %U", format_ns_route, o);
+ case NETNS_TYPE_LINK:
+ return format(s, "link %U", format_ns_link, o);
+ case NETNS_TYPE_NEIGH:
+ return format(s, "neigh %U", format_ns_neigh, o);
+ }
+ return s;
+}
+
+u8 *format_ns_flags(u8 *s, va_list *args)
+{
+ u32 flags = va_arg(*args, u32);
+ if (flags & NETNS_F_ADD)
+ s = format(s, "add");
+ else if (flags & NETNS_F_DEL)
+ s = format(s, "del");
+ else
+ s = format(s, "mod");
+ return s;
+}
+
+clib_error_t *
+netns_init (vlib_main_t * vm)
+{
+ netns_main_t *nm = &netns_main;
+ nm->netnss = 0;
+ nm->handles = 0;
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (netns_init);