14c047107SPierre Pfister/*
24c047107SPierre Pfister * Copyright (c) 2016 Cisco and/or its affiliates.
34c047107SPierre Pfister * Licensed under the Apache License, Version 2.0 (the "License");
44c047107SPierre Pfister * you may not use this file except in compliance with the License.
54c047107SPierre Pfister * You may obtain a copy of the License at:
64c047107SPierre Pfister *
74c047107SPierre Pfister *     http://www.apache.org/licenses/LICENSE-2.0
84c047107SPierre Pfister *
94c047107SPierre Pfister * Unless required by applicable law or agreed to in writing, software
104c047107SPierre Pfister * distributed under the License is distributed on an "AS IS" BASIS,
114c047107SPierre Pfister * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124c047107SPierre Pfister * See the License for the specific language governing permissions and
134c047107SPierre Pfister * limitations under the License.
144c047107SPierre Pfister */
154c047107SPierre Pfister
164c047107SPierre Pfister#include <librtnl/mapper.h>
174c047107SPierre Pfister#include <librtnl/netns.h>
184c047107SPierre Pfister
194c047107SPierre Pfister#include <vnet/ip/ip.h>
204c047107SPierre Pfister#include <vnet/ip/lookup.h>
21fbaa56c3SJeff Shaw#include <vnet/fib/fib.h>
22fbaa56c3SJeff Shaw
234c047107SPierre Pfistertypedef struct {
244c047107SPierre Pfister  int linux_ifindex;
254c047107SPierre Pfister  u32 sw_if_index;
264c047107SPierre Pfister} mapper_map_t;
274c047107SPierre Pfister
284c047107SPierre Pfistertypedef struct {
294c047107SPierre Pfister  char nsname[RTNL_NETNS_NAMELEN + 1];
304c047107SPierre Pfister  mapper_map_t *mappings;
314c047107SPierre Pfister  u32 netns_handle; //Used to receive notifications
324c047107SPierre Pfister  u32 v4fib_index; //One fib index for the namespace
334c047107SPierre Pfister  u32 v6fib_index;
344c047107SPierre Pfister} mapper_ns_t;
354c047107SPierre Pfister
364c047107SPierre Pfistertypedef struct {
374c047107SPierre Pfister  mapper_ns_t *namespaces;
384c047107SPierre Pfister} mapper_main_t;
394c047107SPierre Pfister
404c047107SPierre Pfisterstatic mapper_main_t mapper_main;
414c047107SPierre Pfister
424c047107SPierre Pfistermapper_map_t *mapper_get_by_ifindex(mapper_ns_t *ns, int ifindex)
434c047107SPierre Pfister{
444c047107SPierre Pfister  mapper_map_t *map;
454c047107SPierre Pfister  pool_foreach(map, ns->mappings, {
464c047107SPierre Pfister      if (ifindex == map->linux_ifindex)
474c047107SPierre Pfister        return map;
484c047107SPierre Pfister  });
494c047107SPierre Pfister  return NULL;
504c047107SPierre Pfister}
514c047107SPierre Pfister
524c047107SPierre Pfisterint mapper_add_del_route(mapper_ns_t *ns, ns_route_t *route, int del)
534c047107SPierre Pfister{
544c047107SPierre Pfister  mapper_main_t *mm = &mapper_main;
554c047107SPierre Pfister  clib_warning("NS %d %s %U", ns - mm->namespaces, del?"del":"add", format_ns_route, route);
564c047107SPierre Pfister
574c047107SPierre Pfister  mapper_map_t *map = mapper_get_by_ifindex(ns, route->oif);
584c047107SPierre Pfister  if (!map)
594c047107SPierre Pfister    return 0;
604c047107SPierre Pfister
614c047107SPierre Pfister  if (route->rtm.rtm_family == AF_INET6) {
624c047107SPierre Pfister
634c047107SPierre Pfister    //Filter-out multicast
644c047107SPierre Pfister    if (route->rtm.rtm_dst_len >= 8 && route->dst[0] == 0xff)
654c047107SPierre Pfister      return 0;
664c047107SPierre Pfister
67fbaa56c3SJeff Shaw    fib_prefix_t prefix;
68fbaa56c3SJeff Shaw    ip46_address_t nh;
69fbaa56c3SJeff Shaw
70fbaa56c3SJeff Shaw    memset (&prefix, 0, sizeof (prefix));
71fbaa56c3SJeff Shaw    prefix.fp_len = route->rtm.rtm_dst_len;
72fbaa56c3SJeff Shaw    prefix.fp_proto = FIB_PROTOCOL_IP6;
73fbaa56c3SJeff Shaw    clib_memcpy (&prefix.fp_addr.ip6, route->dst, sizeof (prefix.fp_addr.ip6));
74fbaa56c3SJeff Shaw
75fbaa56c3SJeff Shaw    memset (&nh, 0, sizeof (nh));
76fbaa56c3SJeff Shaw    clib_memcpy (&nh.ip6, route->gateway, sizeof (nh.ip6));
77fbaa56c3SJeff Shaw
78fbaa56c3SJeff Shaw    fib_table_entry_path_add (ns->v6fib_index, &prefix, FIB_SOURCE_API,
79fbaa56c3SJeff Shaw                              FIB_ENTRY_FLAG_NONE, prefix.fp_proto,
80fbaa56c3SJeff Shaw                              &nh, map->sw_if_index, ns->v6fib_index,
81ffd58251SHongjun Ni                              0 /* weight */,
82ce5ab876SChad Wang                              (fib_mpls_label_t *) MPLS_LABEL_INVALID,
83fbaa56c3SJeff Shaw                              FIB_ROUTE_PATH_FLAG_NONE);
844c047107SPierre Pfister  } else {
85fbaa56c3SJeff Shaw    fib_prefix_t prefix;
86fbaa56c3SJeff Shaw    ip46_address_t nh;
87fbaa56c3SJeff Shaw
88fbaa56c3SJeff Shaw    memset (&prefix, 0, sizeof (prefix));
89fbaa56c3SJeff Shaw    prefix.fp_len = route->rtm.rtm_dst_len;
90fbaa56c3SJeff Shaw    prefix.fp_proto = FIB_PROTOCOL_IP4;
91fbaa56c3SJeff Shaw    clib_memcpy (&prefix.fp_addr.ip4, route->dst, sizeof (prefix.fp_addr.ip4));
92fbaa56c3SJeff Shaw
93fbaa56c3SJeff Shaw    memset (&nh, 0, sizeof (nh));
94fbaa56c3SJeff Shaw    clib_memcpy (&nh.ip4, route->gateway, sizeof (nh.ip4));
95fbaa56c3SJeff Shaw
96fbaa56c3SJeff Shaw    fib_table_entry_path_add (ns->v4fib_index, &prefix, FIB_SOURCE_API,
97fbaa56c3SJeff Shaw                              FIB_ENTRY_FLAG_NONE, prefix.fp_proto,
98fbaa56c3SJeff Shaw                              &nh, map->sw_if_index, ns->v4fib_index,
99ffd58251SHongjun Ni                              0 /* weight */,
100ce5ab876SChad Wang                              (fib_mpls_label_t *) MPLS_LABEL_INVALID,
101fbaa56c3SJeff Shaw                              FIB_ROUTE_PATH_FLAG_NONE);
1024c047107SPierre Pfister  }
1034c047107SPierre Pfister
1044c047107SPierre Pfister  return 0;
1054c047107SPierre Pfister}
1064c047107SPierre Pfister
1074c047107SPierre Pfisterstatic void
1084c047107SPierre Pfistermapper_netns_notify_cb(void *obj, netns_type_t type,
1094c047107SPierre Pfister                       u32 flags, uword opaque)
1104c047107SPierre Pfister{
1114c047107SPierre Pfister  mapper_main_t *mm = &mapper_main;
1124c047107SPierre Pfister  mapper_ns_t *ns = &mm->namespaces[(u32) opaque];
1134c047107SPierre Pfister  ASSERT(!pool_is_free_index(mm->namespaces, (u32) opaque));
1144c047107SPierre Pfister  if (type != NETNS_TYPE_ROUTE)
1154c047107SPierre Pfister    return; //For now...
1164c047107SPierre Pfister
1174c047107SPierre Pfister  ns_route_t *route = obj;
1184c047107SPierre Pfister  if (flags & NETNS_F_DEL) {
1194c047107SPierre Pfister    mapper_add_del_route(ns, route, 1);
1204c047107SPierre Pfister  } else if (flags & NETNS_F_ADD) {
1214c047107SPierre Pfister    mapper_add_del_route(ns, route, 0);
1224c047107SPierre Pfister  }
1234c047107SPierre Pfister}
1244c047107SPierre Pfister
1254c047107SPierre Pfistervoid
1264c047107SPierre Pfistermapper_delmap(mapper_ns_t*ns, mapper_map_t *map)
1274c047107SPierre Pfister{
1284c047107SPierre Pfister  ns_route_t *route;
1294c047107SPierre Pfister  netns_t *netns = netns_getns(ns->netns_handle);
1304c047107SPierre Pfister  pool_foreach(route, netns->routes, {
1314c047107SPierre Pfister      if (route->oif == map->linux_ifindex)
1324c047107SPierre Pfister        mapper_add_del_route(ns, route, 1);
1334c047107SPierre Pfister  });
1344c047107SPierre Pfister  pool_put(ns->mappings, map);
1354c047107SPierre Pfister}
1364c047107SPierre Pfister
1374c047107SPierre Pfistermapper_map_t *
1384c047107SPierre Pfistermapper_getmap(mapper_ns_t*ns, u32 sw_if_index,
1394c047107SPierre Pfister              int linux_ifindex, int create)
1404c047107SPierre Pfister{
1414c047107SPierre Pfister  mapper_map_t *map;
1424c047107SPierre Pfister  pool_foreach(map, ns->mappings, {
1434c047107SPierre Pfister      if (linux_ifindex == map->linux_ifindex) {
1444c047107SPierre Pfister        if (sw_if_index != map->sw_if_index)
1454c047107SPierre Pfister          return NULL; //Cannot have multiple mapping with the same ifindex
1464c047107SPierre Pfister        else
1474c047107SPierre Pfister          return map;
1484c047107SPierre Pfister      }
1494c047107SPierre Pfister  });
1504c047107SPierre Pfister
1514c047107SPierre Pfister  if (!create)
1524c047107SPierre Pfister    return NULL;
1534c047107SPierre Pfister
1544c047107SPierre Pfister  pool_get(ns->mappings, map);
1554c047107SPierre Pfister  map->linux_ifindex = linux_ifindex;
1564c047107SPierre Pfister  map->sw_if_index = sw_if_index;
1574c047107SPierre Pfister  ip6_main.fib_index_by_sw_if_index[sw_if_index] = ns->v6fib_index;
1584c047107SPierre Pfister  ip4_main.fib_index_by_sw_if_index[sw_if_index] = ns->v4fib_index;
1594c047107SPierre Pfister
1604c047107SPierre Pfister  //Load available routes
1614c047107SPierre Pfister  ns_route_t *route;
1624c047107SPierre Pfister  netns_t *netns = netns_getns(ns->netns_handle);
1634c047107SPierre Pfister  pool_foreach(route, netns->routes, {
1644c047107SPierre Pfister      if (route->oif == map->linux_ifindex)
1654c047107SPierre Pfister        mapper_add_del_route(ns, route, 0);
1664c047107SPierre Pfister  });
1674c047107SPierre Pfister  return map;
1684c047107SPierre Pfister}
1694c047107SPierre Pfister
1704c047107SPierre Pfisteru32
1714c047107SPierre Pfistermapper_get_ns(char *nsname)
1724c047107SPierre Pfister{
1734c047107SPierre Pfister  mapper_main_t *mm = &mapper_main;
1744c047107SPierre Pfister  mapper_ns_t *ns;
1754c047107SPierre Pfister  pool_foreach(ns, mm->namespaces, {
1764c047107SPierre Pfister      if (!strcmp(nsname, ns->nsname))
1774c047107SPierre Pfister        return ns - mm->namespaces;
1784c047107SPierre Pfister  });
1794c047107SPierre Pfister  return ~0;
1804c047107SPierre Pfister}
1814c047107SPierre Pfister
1824c047107SPierre Pfisterint
1834c047107SPierre Pfistermapper_add_del(u32 nsindex, int linux_ifindex,
1844c047107SPierre Pfister               u32 sw_if_index, int del)
1854c047107SPierre Pfister{
1864c047107SPierre Pfister  mapper_main_t *mm = &mapper_main;
1874c047107SPierre Pfister  //ip6_main_t *im6 = &ip6_main;
1884c047107SPierre Pfister  mapper_ns_t *ns = &mm->namespaces[nsindex];
1894c047107SPierre Pfister  mapper_map_t *map;
1904c047107SPierre Pfister  //vnet_sw_interface_t *iface = vnet_get_sw_interface(vnet_get_main(), sw_if_index);
1914c047107SPierre Pfister
1924c047107SPierre Pfister  if (pool_is_free(mm->namespaces, ns))
1934c047107SPierre Pfister    return -1;
1944c047107SPierre Pfister
1954c047107SPierre Pfister  /*if (!del) {
1964c047107SPierre Pfister    if ((iface->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) &&
1974c047107SPierre Pfister        im6->fib_index_by_sw_if_index[sw_if_index] != ~0) {
1984c047107SPierre Pfister      //A custom fib index will be used...
1994c047107SPierre Pfister      clib_warning("Cannot add interface with a custom fib index (current is %d)",
2004c047107SPierre Pfister                   im6->fib_index_by_sw_if_index[sw_if_index]);
2014c047107SPierre Pfister      return -1;
2024c047107SPierre Pfister    }
2034c047107SPierre Pfister  }*/
2044c047107SPierre Pfister
2054c047107SPierre Pfister  if (!(map = mapper_getmap(ns, sw_if_index, linux_ifindex, !del)))
2064c047107SPierre Pfister    return -1;
2074c047107SPierre Pfister
2084c047107SPierre Pfister  if (del)
2094c047107SPierre Pfister    mapper_delmap(ns, map);
2104c047107SPierre Pfister
2114c047107SPierre Pfister  return 0;
2124c047107SPierre Pfister}
2134c047107SPierre Pfister
2144c047107SPierre Pfisterint
2154c047107SPierre Pfistermapper_add_ns(char *nsname, u32 v4fib_index, u32 v6fib_index, u32 *nsindex)
2164c047107SPierre Pfister{
2174c047107SPierre Pfister  mapper_main_t *mm = &mapper_main;
2184c047107SPierre Pfister  mapper_ns_t *ns;
2194c047107SPierre Pfister  if (mapper_get_ns(nsname) != ~0)
2204c047107SPierre Pfister    return -1; //Already exists
2214c047107SPierre Pfister
2224c047107SPierre Pfister  pool_get(mm->namespaces, ns);
2234c047107SPierre Pfister  strcpy(ns->nsname, nsname);
2244c047107SPierre Pfister  ns->v4fib_index = v4fib_index;
2254c047107SPierre Pfister  ns->v6fib_index = v6fib_index;
2264c047107SPierre Pfister  ns->mappings = 0;
2274c047107SPierre Pfister
2284c047107SPierre Pfister  netns_sub_t sub;
2294c047107SPierre Pfister  sub.notify = mapper_netns_notify_cb;
2304c047107SPierre Pfister  sub.opaque = (uword)(ns - mm->namespaces);
2314c047107SPierre Pfister  if ((ns->netns_handle = netns_open(ns->nsname, &sub)) == ~0) {
2324c047107SPierre Pfister    pool_put(mm->namespaces, ns);
2334c047107SPierre Pfister    return -1;
2344c047107SPierre Pfister  }
2354c047107SPierre Pfister  *nsindex = ns - mm->namespaces;
2364c047107SPierre Pfister  return 0;
2374c047107SPierre Pfister}
2384c047107SPierre Pfister
2394c047107SPierre Pfisterint
2404c047107SPierre Pfistermapper_del_ns(u32 nsindex)
2414c047107SPierre Pfister{
2424c047107SPierre Pfister  mapper_main_t *mm = &mapper_main;
2434c047107SPierre Pfister  mapper_ns_t *ns = &mm->namespaces[nsindex];
2444c047107SPierre Pfister  if (pool_is_free(mm->namespaces, ns))
2454c047107SPierre Pfister    return -1;
2464c047107SPierre Pfister
2474c047107SPierre Pfister  //Remove all existing mappings
2484c047107SPierre Pfister  int i, *indexes = 0;
2494c047107SPierre Pfister  pool_foreach_index(i, ns->mappings, {
2504c047107SPierre Pfister    vec_add1(indexes, i);
2514c047107SPierre Pfister  });
2524c047107SPierre Pfister  vec_foreach_index(i, indexes) {
2534c047107SPierre Pfister    mapper_delmap(ns, &ns->mappings[indexes[i]]);
2544c047107SPierre Pfister  }
2554c047107SPierre Pfister  vec_free(indexes);
2564c047107SPierre Pfister
2574c047107SPierre Pfister  netns_close(ns->netns_handle);
2584c047107SPierre Pfister  pool_put(mm->namespaces, ns);
2594c047107SPierre Pfister  return 0;
2604c047107SPierre Pfister}
2614c047107SPierre Pfister
2624c047107SPierre Pfisterclib_error_t *
2634c047107SPierre Pfistermapper_init (vlib_main_t * vm)
2644c047107SPierre Pfister{
2654c047107SPierre Pfister  mapper_main_t *mm = &mapper_main;
2664c047107SPierre Pfister  mm->namespaces = 0;
2674c047107SPierre Pfister  return 0;
2684c047107SPierre Pfister}
2694c047107SPierre Pfister
2704c047107SPierre PfisterVLIB_INIT_FUNCTION (mapper_init);
271