netns.c revision 2ab698c9
1/*
2 * Copyright (c) 2016 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <librtnl/netns.h>
17
18#include <vnet/ip/format.h>
19#include <vnet/ethernet/ethernet.h>
20
21#include <stddef.h>
22
23/* Enable some RTA values debug */
24//#define RTNL_CHECK
25
26#define is_nonzero(x)                           \
27  ({                                            \
28    u8 __is_zero_zero[sizeof(x)] = {};          \
29    memcmp(__is_zero_zero, &x, sizeof(x));      \
30  })
31
32typedef struct {
33  u8 type;      //Attribute identifier
34  u8 unique;    //Part of the values uniquely identifying an entry
35  u16 offset;   //Offset where stored in struct
36  u16 size;     //Length of the attribute
37} rtnl_mapping_t;
38
39#define ns_foreach_ifla                         \
40  _(IFLA_ADDRESS, hwaddr)                       \
41  _(IFLA_BROADCAST, broadcast)                  \
42  _(IFLA_IFNAME, ifname)                        \
43  _(IFLA_MASTER, master)                        \
44  _(IFLA_MTU, mtu)                              \
45  _(IFLA_QDISC, qdisc)
46
47static rtnl_mapping_t ns_ifmap[] = {
48#define _(t, e)                                 \
49  {                                             \
50    .type = t,                                  \
51    .offset = offsetof(ns_link_t, e),           \
52    .size = sizeof(((ns_link_t*)0)->e)          \
53  },
54  ns_foreach_ifla
55#undef _
56  { .type = 0 }
57};
58
59u8 *format_ns_link (u8 *s, va_list *args)
60{
61  ns_link_t *l = va_arg(*args, ns_link_t *);
62  s = format(s, "%s index %u", l->ifname, l->ifi.ifi_index);
63  return s;
64}
65
66#define ns_foreach_rta                          \
67  _(RTA_DST, dst, 1)                            \
68  _(RTA_SRC, src, 1)                            \
69  _(RTA_VIA, via, 1)                            \
70  _(RTA_GATEWAY, gateway, 1)                    \
71  _(RTA_IIF, iif, 1)                            \
72  _(RTA_OIF, oif, 1)                            \
73  _(RTA_PREFSRC, prefsrc, 0)                    \
74  _(RTA_TABLE, table, 0)                        \
75  _(RTA_PRIORITY, priority, 0)                  \
76  _(RTA_CACHEINFO, cacheinfo, 0)                \
77  _(RTA_ENCAP, encap, 1)
78
79static rtnl_mapping_t ns_routemap[] = {
80#define _(t, e, u)                              \
81  {                                             \
82    .type = t, .unique = u,                     \
83    .offset = offsetof(ns_route_t, e),          \
84    .size = sizeof(((ns_route_t*)0)->e)         \
85  },
86  ns_foreach_rta
87#undef _
88  { .type = 0 }
89};
90
91u8 *format_ns_route (u8 *s, va_list *args)
92{
93  ns_route_t *r = va_arg(*args, ns_route_t *);
94  void *format_ip = r->rtm.rtm_family == AF_INET ? format_ip4_address : format_ip6_address;
95  s = format(s, "%U/%d", format_ip, r->dst, r->rtm.rtm_dst_len);
96  if (r->rtm.rtm_src_len)
97    s = format(s, " from %U/%d", format_ip, r->src, r->rtm.rtm_src_len);
98  if (is_nonzero(r->gateway))
99    s = format(s, " via %U", format_ip, r->gateway);
100  if (r->iif)
101    s = format(s, " iif %d", r->iif);
102  if (r->oif)
103    s = format(s, " oif %d", r->oif);
104  if (is_nonzero(r->prefsrc))
105    s = format(s, " src %U", format_ip, r->prefsrc);
106  if (r->table)
107    s = format(s, " table %d", r->table);
108  if (r->priority)
109    s = format(s, " priority %u", r->priority);
110  return s;
111}
112
113#define ns_foreach_ifaddr                       \
114  _(IFA_ADDRESS, addr, 1)                       \
115  _(IFA_LOCAL, local, 1)                        \
116  _(IFA_LABEL, label, 0)                        \
117  _(IFA_BROADCAST, broadcast, 0)                \
118  _(IFA_ANYCAST, anycast, 0)                    \
119  _(IFA_CACHEINFO, cacheinfo, 0)
120
121static rtnl_mapping_t ns_addrmap[] = {
122#define _(t, e, u)                              \
123  {                                             \
124    .type = t, .unique = u,                     \
125    .offset = offsetof(ns_addr_t, e),           \
126    .size = sizeof(((ns_addr_t*)0)->e)          \
127  },
128  ns_foreach_ifaddr
129#undef _
130  { .type = 0 }
131};
132
133u8 *format_ns_addr (u8 *s, va_list *args)
134{
135  ns_addr_t *a = va_arg(*args, ns_addr_t *);
136  void *format_ip = a->ifaddr.ifa_family == AF_INET ? format_ip4_address : format_ip6_address;
137  s = format(s, "%U/%d", format_ip, a->addr, a->ifaddr.ifa_prefixlen);
138  if (is_nonzero(a->label))
139    s = format(s, " dev %s", a->label);
140  if (is_nonzero(a->broadcast))
141    s = format(s, " broadcast %U", format_ip, a->broadcast);
142  if (is_nonzero(a->anycast))
143    s = format(s, " anycast %U", format_ip, a->anycast);
144  if (is_nonzero(a->local))
145    s = format(s, " local %U", format_ip, a->local);
146  return s;
147}
148
149#ifndef NDA_RTA
150#define NDA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
151#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
152#endif
153
154#define ns_foreach_neigh                        \
155  _(NDA_DST, dst, 1)                            \
156  _(NDA_LLADDR, lladdr, 0)                      \
157  _(NDA_PROBES, probes, 0)                      \
158  _(NDA_CACHEINFO, cacheinfo, 0)
159
160static rtnl_mapping_t ns_neighmap[] = {
161#define _(t, e, u)                              \
162  {                                             \
163    .type = t, .unique = u,                     \
164    .offset = offsetof(ns_neigh_t, e),          \
165    .size = sizeof(((ns_neigh_t*)0)->e)         \
166  },
167  ns_foreach_neigh
168#undef _
169  { .type = 0 }
170};
171
172u8 *format_ns_neigh (u8 *s, va_list *args)
173{
174  ns_neigh_t *n = va_arg(*args, ns_neigh_t *);
175  void *format_ip = n->nd.ndm_family == AF_INET ? format_ip4_address : format_ip6_address;
176  s = format(s, "%U", format_ip, n->dst);
177  if (is_nonzero(n->lladdr))
178    s = format(s, " lladdr %U", format_ethernet_address, n->lladdr);
179  if (n->probes)
180    s = format(s, " probes %d", n->probes);
181  return s;
182}
183
184typedef struct {
185  void (*notify)(void *obj, netns_type_t type, u32 flags, uword opaque);
186  uword opaque;
187  u32 netns_index;
188} netns_handle_t;
189
190typedef struct {
191  netns_t netns;
192  u32 rtnl_handle;
193  u32 subscriber_count;
194} netns_p;
195
196typedef struct {
197  netns_p *netnss;
198  netns_handle_t *handles;
199} netns_main_t;
200
201netns_main_t netns_main;
202
203static int
204rtnl_parse_rtattr(struct rtattr *db[], size_t max,
205                  struct rtattr *rta, size_t len) {
206  for(; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
207    if (rta->rta_type <= max)
208      db[rta->rta_type] = rta;
209#ifdef RTNL_CHECK
210    else
211      clib_warning("RTA type too high: %d", rta->rta_type);
212#endif
213  }
214
215  if(len) {
216    clib_warning("rattr lenght mistmatch %d %d len",
217                 (int) len, (int) rta->rta_len);
218    return -1;
219  }
220  return 0;
221}
222
223/*
224 * Debug function to display when
225 * we receive an RTA that I forgot in
226 * the mapping table (there are so many of them).
227 */
228#ifdef RTNL_CHECK
229static void
230rtnl_entry_check(struct rtattr *rtas[],
231                 size_t rta_len,
232                 rtnl_mapping_t map[],
233                 char *logstr)
234{
235  int i;
236  for (i=0; i<rta_len; i++) {
237    if (!rtas[i])
238      continue;
239
240    rtnl_mapping_t *m = map;
241    for (m = map; m->type; m++) {
242      if (m->type == rtas[i]->rta_type)
243        break;
244    }
245    if (!m->type)
246      clib_warning("Unknown RTA type %d (%s)", rtas[i]->rta_type, logstr);
247  }
248}
249#endif
250
251/*
252 * Check if the provided entry matches the parsed and unique rtas
253 */
254static int
255rtnl_entry_match(void *entry,
256                 struct rtattr *rtas[],
257                 rtnl_mapping_t map[])
258{
259  u8 zero[1024] = {};
260  for ( ;map->type != 0; map++) {
261    struct rtattr *rta = rtas[map->type];
262    size_t rta_len = rta?RTA_PAYLOAD(rta):0;
263    if (!map->unique)
264      continue;
265
266    if (rta && RTA_PAYLOAD(rta) > map->size) {
267      clib_warning("rta (type=%d len=%d) too long (max %d)",
268                   rta->rta_type, rta->rta_len, map->size);
269      return -1;
270    }
271
272    if ((rta && memcmp(RTA_DATA(rta), entry + map->offset, rta_len)) ||
273        memcmp(entry + map->offset + rta_len, zero, map->size - rta_len)) {
274      return 0;
275    }
276  }
277  return 1;
278}
279
280static int
281rtnl_entry_set(void *entry,
282               struct rtattr *rtas[],
283               rtnl_mapping_t map[],
284               int init)
285{
286  for (; map->type != 0; map++) {
287
288    struct rtattr *rta = rtas[map->type];
289
290    if(map->type == RTA_ENCAP && rta) {
291      /*Data of RTA_ENCAP is a pointer to rta attributes for MPLS*/
292      rta = (struct rtattr*)RTA_DATA(rta);
293      if (RTA_PAYLOAD(rta) > map->size) {
294        clib_warning("rta (type=%d len=%d) too long (max %d)", rta->rta_type, rta->rta_len, map->size);
295        return -1;
296      }
297      memcpy(entry + map->offset, RTA_DATA(rta), map->size);
298      memset(entry + map->offset + map->size, 0, 0);
299    } else if (rta) {
300      if (RTA_PAYLOAD(rta) > map->size) {
301        clib_warning("rta (type=%d len=%d) too long (max %d)", rta->rta_type, rta->rta_len, map->size);
302        return -1;
303      }
304      memcpy(entry + map->offset, RTA_DATA(rta), RTA_PAYLOAD(rta));
305      memset(entry + map->offset + RTA_PAYLOAD(rta), 0, map->size - RTA_PAYLOAD(rta));
306    } else if (init) {
307      memset(entry + map->offset, 0, map->size);
308    }
309  }
310  return 0;
311}
312
313void
314netns_notify(netns_p *ns, void *obj, netns_type_t type, u32 flags)
315{
316  netns_main_t *nm = &netns_main;
317  netns_handle_t *h;
318  pool_foreach(h, nm->handles, {
319      if (h->netns_index == (ns - nm->netnss) &&  h->notify)
320        h->notify(obj, type, flags, h->opaque);
321    });
322}
323
324static_always_inline int
325mask_match(void *a, void *b, void *mask, size_t len)
326{
327  u8 *va = (u8 *) a;
328  u8 *vb = (u8 *) b;
329  u8 *vm = (u8 *) mask;
330  while (len--) {
331    if ((va[len] ^ vb[len]) & vm[len])
332      return 0;
333  }
334  return 1;
335}
336
337static ns_link_t *
338ns_get_link(netns_p *ns, struct ifinfomsg *ifi, struct rtattr *rtas[])
339{
340  ns_link_t *link;
341  pool_foreach(link, ns->netns.links, {
342      if(ifi->ifi_index == link->ifi.ifi_index)
343        return link;
344    });
345  return NULL;
346}
347
348static int
349ns_rcv_link(netns_p *ns, struct nlmsghdr *hdr)
350{
351  ns_link_t *link;
352  struct ifinfomsg *ifi;
353  struct rtattr *rtas[IFLA_MAX + 1] = {};
354  size_t datalen = hdr->nlmsg_len - NLMSG_ALIGN(sizeof(*hdr));
355
356  if(datalen < sizeof(*ifi))
357    return -1;
358
359  ifi = NLMSG_DATA(hdr);
360  if((datalen > NLMSG_ALIGN(sizeof(*ifi))) &&
361     rtnl_parse_rtattr(rtas, IFLA_MAX, IFLA_RTA(ifi),
362                       IFLA_PAYLOAD(hdr))) {
363    return -1;
364  }
365#ifdef RTNL_CHECK
366  rtnl_entry_check(rtas, IFLA_MAX + 1, ns_ifmap, "link");
367#endif
368
369  link = ns_get_link(ns, ifi, rtas);
370
371  if (hdr->nlmsg_type == RTM_DELLINK) {
372    if (!link)
373      return -1;
374    pool_put(ns->netns.links, link);
375    netns_notify(ns, link, NETNS_TYPE_LINK, NETNS_F_DEL);
376    return 0;
377  }
378
379  if (!link) {
380    pool_get(ns->netns.links, link);
381    rtnl_entry_set(link, rtas, ns_ifmap, 1);
382  } else {
383    rtnl_entry_set(link, rtas, ns_ifmap, 0);
384  }
385
386  link->ifi = *ifi;
387  link->last_updated = vlib_time_now(vlib_get_main());
388  netns_notify(ns, link, NETNS_TYPE_LINK, NETNS_F_ADD);
389  return 0;
390}
391
392static ns_route_t *
393ns_get_route(netns_p *ns, struct rtmsg *rtm, struct rtattr *rtas[])
394{
395  ns_route_t *route;
396
397  //This describes the values which uniquely identify a route
398  struct rtmsg msg = {
399    .rtm_family = 0xff,
400    .rtm_dst_len = 0xff,
401    .rtm_src_len = 0xff,
402    .rtm_table = 0xff,
403    .rtm_protocol = 0xff,
404    .rtm_type = 0xff
405  };
406
407  pool_foreach(route, ns->netns.routes, {
408      if(mask_match(&route->rtm, rtm, &msg, sizeof(struct rtmsg)) &&
409         rtnl_entry_match(route, rtas, ns_routemap))
410        return route;
411    });
412  return NULL;
413}
414
415static int
416ns_rcv_route(netns_p *ns, struct nlmsghdr *hdr)
417{
418  ns_route_t *route;
419  struct rtmsg *rtm;
420  struct rtattr *rtas[RTA_MAX + 1] = {};
421  size_t datalen = hdr->nlmsg_len - NLMSG_ALIGN(sizeof(*hdr));
422
423  if(datalen < sizeof(*rtm))
424    return -1;
425
426  rtm = NLMSG_DATA(hdr);
427  if((datalen > NLMSG_ALIGN(sizeof(*rtm))) &&
428     rtnl_parse_rtattr(rtas, RTA_MAX, RTM_RTA(rtm),
429                       RTM_PAYLOAD(hdr))) {
430    return -1;
431  }
432#ifdef RTNL_CHECK
433  rtnl_entry_check(rtas, RTA_MAX + 1, ns_routemap, "route");
434#endif
435  route = ns_get_route(ns, rtm, rtas);
436
437  if (hdr->nlmsg_type == RTM_DELROUTE) {
438    if (!route)
439      return -1;
440    pool_put(ns->netns.routes, route);
441    netns_notify(ns, route, NETNS_TYPE_ROUTE, NETNS_F_DEL);
442    return 0;
443  }
444
445  if (!route) {
446    pool_get(ns->netns.routes, route);
447    memset(route, 0, sizeof(*route));
448    rtnl_entry_set(route, rtas, ns_routemap, 1);
449  } else {
450    rtnl_entry_set(route, rtas, ns_routemap, 0);
451  }
452
453  route->rtm = *rtm;
454  route->last_updated = vlib_time_now(vlib_get_main());
455  netns_notify(ns, route, NETNS_TYPE_ROUTE, NETNS_F_ADD);
456  return 0;
457}
458
459static ns_addr_t *
460ns_get_addr(netns_p *ns, struct ifaddrmsg *ifaddr, struct rtattr *rtas[])
461{
462  ns_addr_t *addr;
463
464  //This describes the values which uniquely identify a route
465  struct ifaddrmsg msg = {
466    .ifa_family = 0xff,
467    .ifa_prefixlen = 0xff,
468  };
469
470  pool_foreach(addr, ns->netns.addresses, {
471      if(mask_match(&addr->ifaddr, ifaddr, &msg, sizeof(struct ifaddrmsg)) &&
472         rtnl_entry_match(addr, rtas, ns_addrmap))
473        return addr;
474    });
475  return NULL;
476}
477
478static int
479ns_rcv_addr(netns_p *ns, struct nlmsghdr *hdr)
480{
481  ns_addr_t *addr;
482  struct ifaddrmsg *ifaddr;
483  struct rtattr *rtas[IFA_MAX + 1] = {};
484  size_t datalen = hdr->nlmsg_len - NLMSG_ALIGN(sizeof(*hdr));
485
486  if(datalen < sizeof(*ifaddr))
487    return -1;
488
489  ifaddr = NLMSG_DATA(hdr);
490  if((datalen > NLMSG_ALIGN(sizeof(*ifaddr))) &&
491     rtnl_parse_rtattr(rtas, IFA_MAX, IFA_RTA(ifaddr),
492                       IFA_PAYLOAD(hdr))) {
493    return -1;
494  }
495#ifdef RTNL_CHECK
496  rtnl_entry_check(rtas, IFA_MAX + 1, ns_addrmap, "addr");
497#endif
498  addr = ns_get_addr(ns, ifaddr, rtas);
499
500  if (hdr->nlmsg_type == RTM_DELADDR) {
501    if (!addr)
502      return -1;
503    pool_put(ns->netns.addresses, addr);
504    netns_notify(ns, addr, NETNS_TYPE_ADDR, NETNS_F_DEL);
505    return 0;
506  }
507
508  if (!addr) {
509    pool_get(ns->netns.addresses, addr);
510    memset(addr, 0, sizeof(*addr));
511    rtnl_entry_set(addr, rtas, ns_addrmap, 1);
512  } else {
513    rtnl_entry_set(addr, rtas, ns_addrmap, 0);
514  }
515
516  addr->ifaddr = *ifaddr;
517  addr->last_updated = vlib_time_now(vlib_get_main());
518  netns_notify(ns, addr, NETNS_TYPE_ADDR, NETNS_F_ADD);
519  return 0;
520}
521
522static ns_neigh_t *
523ns_get_neigh(netns_p *ns, struct ndmsg *nd, struct rtattr *rtas[])
524{
525  ns_neigh_t *neigh;
526
527  //This describes the values which uniquely identify a route
528  struct ndmsg msg = {
529    .ndm_family = 0xff,
530    .ndm_ifindex = 0xff,
531  };
532
533  pool_foreach(neigh, ns->netns.neighbors, {
534      if(mask_match(&neigh->nd, nd, &msg, sizeof(&msg)) &&
535         rtnl_entry_match(neigh, rtas, ns_neighmap))
536        return neigh;
537    });
538  return NULL;
539}
540
541static int
542ns_rcv_neigh(netns_p *ns, struct nlmsghdr *hdr)
543{
544  ns_neigh_t *neigh;
545  struct ndmsg *nd;
546  struct rtattr *rtas[NDA_MAX + 1] = {};
547  size_t datalen = hdr->nlmsg_len - NLMSG_ALIGN(sizeof(*hdr));
548
549  if(datalen < sizeof(*nd))
550    return -1;
551
552  nd = NLMSG_DATA(hdr);
553  if((datalen > NLMSG_ALIGN(sizeof(*nd))) &&
554     rtnl_parse_rtattr(rtas, NDA_MAX, NDA_RTA(nd),
555                       NDA_PAYLOAD(hdr))) {
556    return -1;
557  }
558#ifdef RTNL_CHECK
559  rtnl_entry_check(rtas, NDA_MAX + 1, ns_neighmap, "nd");
560#endif
561  neigh = ns_get_neigh(ns, nd, rtas);
562
563  if (hdr->nlmsg_type == RTM_DELNEIGH) {
564    if (!neigh)
565      return -1;
566    pool_put(ns->netns.neighbors, neigh);
567    netns_notify(ns, neigh, NETNS_TYPE_NEIGH, NETNS_F_DEL);
568    return 0;
569  }
570
571  if (!neigh) {
572    pool_get(ns->netns.neighbors, neigh);
573    memset(neigh, 0, sizeof(*neigh));
574    rtnl_entry_set(neigh, rtas, ns_neighmap, 1);
575  } else {
576    rtnl_entry_set(neigh, rtas, ns_neighmap, 0);
577  }
578
579  neigh->nd = *nd;
580  neigh->last_updated = vlib_time_now(vlib_get_main());
581  netns_notify(ns, neigh, NETNS_TYPE_NEIGH, NETNS_F_ADD);
582  return 0;
583}
584
585#define ns_object_foreach                       \
586  _(neighbors, NETNS_TYPE_NEIGH)                \
587  _(routes, NETNS_TYPE_ROUTE)                   \
588  _(addresses, NETNS_TYPE_ADDR)                 \
589  _(links, NETNS_TYPE_LINK)
590
591static void
592ns_recv_error(rtnl_error_t err, uword o)
593{
594  //An error was received. Reset everything.
595  netns_p *ns = &netns_main.netnss[o];
596  u32 *indexes = 0;
597  u32 *i = 0;
598
599#define _(pool, type)                                           \
600  pool_foreach_index(*i, ns->netns.pool, {                      \
601      vec_add1(indexes, *i);                                    \
602    })                                                          \
603    vec_foreach(i, indexes) {                                   \
604    pool_put_index(ns->netns.pool, *i);                         \
605    netns_notify(ns, &ns->netns.pool[*i], type, NETNS_F_DEL);   \
606  }                                                             \
607  vec_reset_length(indexes);
608
609  ns_object_foreach
610
611#undef _
612    vec_free(indexes);
613}
614
615static void
616ns_recv_rtnl(struct nlmsghdr *hdr, uword o)
617{
618  netns_p *ns = &netns_main.netnss[o];
619  switch (hdr->nlmsg_type) {
620  case RTM_NEWROUTE:
621  case RTM_DELROUTE:
622    ns_rcv_route(ns, hdr);
623    break;
624  case RTM_NEWLINK:
625  case RTM_DELLINK:
626    ns_rcv_link(ns, hdr);
627    break;
628  case RTM_NEWADDR:
629  case RTM_DELADDR:
630    ns_rcv_addr(ns, hdr);
631    break;
632  case RTM_NEWNEIGH:
633  case RTM_DELNEIGH:
634    ns_rcv_neigh(ns, hdr);
635    break;
636  default:
637    clib_warning("unknown rtnl type %d", hdr->nlmsg_type);
638    break;
639  }
640}
641
642static void
643netns_destroy(netns_p *ns)
644{
645  netns_main_t *nm = &netns_main;
646  rtnl_stream_close(ns->rtnl_handle);
647  pool_put(nm->netnss, ns);
648  pool_free(ns->netns.links);
649  pool_free(ns->netns.addresses);
650  pool_free(ns->netns.routes);
651  pool_free(ns->netns.neighbors);
652}
653
654static netns_p *
655netns_get(char *name)
656{
657  netns_main_t *nm = &netns_main;
658  netns_p *ns;
659  pool_foreach(ns, nm->netnss, {
660      if (!strcmp(name, ns->netns.name))
661        return ns;
662    });
663
664  if (strlen(name) > RTNL_NETNS_NAMELEN)
665    return NULL;
666
667  pool_get(nm->netnss, ns);
668  rtnl_stream_t s = {
669    .recv_message = ns_recv_rtnl,
670    .error = ns_recv_error,
671    .opaque = (uword)(ns - nm->netnss),
672  };
673  strcpy(s.name, name);
674
675  u32 handle;
676  if ((handle = rtnl_stream_open(&s)) == ~0) {
677    pool_put(nm->netnss, ns);
678    return NULL;
679  }
680
681  strcpy(ns->netns.name, name);
682  ns->netns.addresses = 0;
683  ns->netns.links = 0;
684  ns->netns.neighbors = 0;
685  ns->netns.routes = 0;
686  ns->subscriber_count = 0;
687  ns->rtnl_handle = handle;
688  return ns;
689}
690
691u32 netns_open(char *name, netns_sub_t *sub)
692{
693  netns_main_t *nm = &netns_main;
694  netns_p *ns;
695  netns_handle_t *p;
696  if (!(ns = netns_get(name)))
697    return ~0;
698
699  pool_get(nm->handles, p);
700  p->netns_index = ns - nm->netnss;
701  p->notify = sub->notify;
702  p->opaque = sub->opaque;
703  ns->subscriber_count++;
704  return p - nm->handles;
705}
706
707netns_t *netns_getns(u32 handle)
708{
709  netns_main_t *nm = &netns_main;
710  netns_handle_t *h = pool_elt_at_index(nm->handles, handle);
711  netns_p *ns = pool_elt_at_index(nm->netnss, h->netns_index);
712  return &ns->netns;
713}
714
715void netns_close(u32 handle)
716{
717  netns_main_t *nm = &netns_main;
718  netns_handle_t *h = pool_elt_at_index(nm->handles, handle);
719  netns_p *ns = pool_elt_at_index(nm->netnss, h->netns_index);
720  pool_put(h, nm->handles);
721  ns->subscriber_count--;
722  if (!ns->subscriber_count)
723    netns_destroy(ns);
724}
725
726void netns_callme(u32 handle, char del)
727{
728  netns_main_t *nm = &netns_main;
729  netns_handle_t *h = pool_elt_at_index(nm->handles, handle);
730  netns_p *ns = pool_elt_at_index(nm->netnss, h->netns_index);
731  u32 i = 0;
732  if (!h->notify)
733    return;
734
735#define _(pool, type)                                           \
736  pool_foreach_index(i, ns->netns.pool, {                       \
737      h->notify(&ns->netns.pool[i], type,                       \
738                del?NETNS_F_DEL:NETNS_F_ADD, h->opaque);        \
739    });
740
741  ns_object_foreach
742#undef _
743
744    }
745
746u8 *format_ns_object(u8 *s, va_list *args)
747{
748  netns_type_t t = va_arg(*args, netns_type_t);
749  void *o = va_arg(*args, void *);
750  switch (t) {
751  case NETNS_TYPE_ADDR:
752    return format(s, "addr %U", format_ns_addr, o);
753  case NETNS_TYPE_ROUTE:
754    return format(s, "route %U", format_ns_route, o);
755  case NETNS_TYPE_LINK:
756    return format(s, "link %U", format_ns_link, o);
757  case NETNS_TYPE_NEIGH:
758    return format(s, "neigh %U", format_ns_neigh, o);
759  }
760  return s;
761}
762
763u8 *format_ns_flags(u8 *s, va_list *args)
764{
765  u32 flags = va_arg(*args, u32);
766  if (flags & NETNS_F_ADD)
767    s = format(s, "add");
768  else if (flags & NETNS_F_DEL)
769    s = format(s, "del");
770  else
771    s = format(s, "mod");
772  return s;
773}
774
775clib_error_t *
776netns_init (vlib_main_t * vm)
777{
778  netns_main_t *nm = &netns_main;
779  nm->netnss = 0;
780  nm->handles = 0;
781  return 0;
782}
783
784VLIB_INIT_FUNCTION (netns_init);
785