mapper.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/mapper.h>
17#include <librtnl/netns.h>
18
19#include <vnet/ip/ip.h>
20#include <vnet/ip/lookup.h>
21
22#ifdef ip6_add_del_route_next_hop
23#define FIB_VERSION 1
24#else
25#include <vnet/fib/fib.h>
26#define FIB_VERSION 2
27#endif
28
29typedef struct {
30  int linux_ifindex;
31  u32 sw_if_index;
32} mapper_map_t;
33
34typedef struct {
35  char nsname[RTNL_NETNS_NAMELEN + 1];
36  mapper_map_t *mappings;
37  u32 netns_handle; //Used to receive notifications
38  u32 v4fib_index; //One fib index for the namespace
39  u32 v6fib_index;
40} mapper_ns_t;
41
42typedef struct {
43  mapper_ns_t *namespaces;
44} mapper_main_t;
45
46static mapper_main_t mapper_main;
47
48mapper_map_t *mapper_get_by_ifindex(mapper_ns_t *ns, int ifindex)
49{
50  mapper_map_t *map;
51  pool_foreach(map, ns->mappings, {
52      if (ifindex == map->linux_ifindex)
53        return map;
54  });
55  return NULL;
56}
57
58int mapper_add_del_route(mapper_ns_t *ns, ns_route_t *route, int del)
59{
60  mapper_main_t *mm = &mapper_main;
61  clib_warning("NS %d %s %U", ns - mm->namespaces, del?"del":"add", format_ns_route, route);
62
63  mapper_map_t *map = mapper_get_by_ifindex(ns, route->oif);
64  if (!map)
65    return 0;
66
67  if (route->rtm.rtm_family == AF_INET6) {
68
69    //Filter-out multicast
70    if (route->rtm.rtm_dst_len >= 8 && route->dst[0] == 0xff)
71      return 0;
72
73#if FIB_VERSION == 1
74    struct ip6_main_t *im = &ip6_main;
75    ip6_add_del_route_next_hop(im, //ip6_main
76                               del?IP6_ROUTE_FLAG_DEL:IP6_ROUTE_FLAG_ADD,  //flags (not del)
77                               (ip6_address_t *)&route->dst[0], //Dst addr
78                               route->rtm.rtm_dst_len, //Plen
79                               (ip6_address_t *)&route->gateway[0], //next-hop
80                               map->sw_if_index, //sw_if_index
81                               0, //weight
82                               ~0, //adj_index
83                               ns->v6fib_index);
84#else
85    fib_prefix_t prefix;
86    ip46_address_t nh;
87
88    memset (&prefix, 0, sizeof (prefix));
89    prefix.fp_len = route->rtm.rtm_dst_len;
90    prefix.fp_proto = FIB_PROTOCOL_IP6;
91    clib_memcpy (&prefix.fp_addr.ip6, route->dst, sizeof (prefix.fp_addr.ip6));
92
93    memset (&nh, 0, sizeof (nh));
94    clib_memcpy (&nh.ip6, route->gateway, sizeof (nh.ip6));
95
96    fib_table_entry_path_add (ns->v6fib_index, &prefix, FIB_SOURCE_API,
97                              FIB_ENTRY_FLAG_NONE, prefix.fp_proto,
98                              &nh, map->sw_if_index, ns->v6fib_index,
99                              0 /* weight */,
100                              (fib_mpls_label_t *) MPLS_LABEL_INVALID,
101                              FIB_ROUTE_PATH_FLAG_NONE);
102#endif /* FIB_VERSION == 1 */
103  } else {
104#if FIB_VERSION == 1
105    struct ip4_main_t *im = &ip4_main;
106    ip4_add_del_route_next_hop(im, //ip4_main
107                               del?IP4_ROUTE_FLAG_DEL:IP4_ROUTE_FLAG_ADD,  //flags (not del)
108                               (ip4_address_t *)&route->dst[0], //Dst addr
109                               route->rtm.rtm_dst_len, //Plen
110                               (ip4_address_t *)&route->gateway[0], //next-hop
111                               map->sw_if_index, //sw_if_index
112                               0, //weight
113                               ~0, //adj_index
114                               ns->v4fib_index);
115#else
116    fib_prefix_t prefix;
117    ip46_address_t nh;
118
119    memset (&prefix, 0, sizeof (prefix));
120    prefix.fp_len = route->rtm.rtm_dst_len;
121    prefix.fp_proto = FIB_PROTOCOL_IP4;
122    clib_memcpy (&prefix.fp_addr.ip4, route->dst, sizeof (prefix.fp_addr.ip4));
123
124    memset (&nh, 0, sizeof (nh));
125    clib_memcpy (&nh.ip4, route->gateway, sizeof (nh.ip4));
126
127    fib_table_entry_path_add (ns->v4fib_index, &prefix, FIB_SOURCE_API,
128                              FIB_ENTRY_FLAG_NONE, prefix.fp_proto,
129                              &nh, map->sw_if_index, ns->v4fib_index,
130                              0 /* weight */,
131                              (fib_mpls_label_t *) MPLS_LABEL_INVALID,
132                              FIB_ROUTE_PATH_FLAG_NONE);
133#endif /* FIB_VERSION == 1 */
134  }
135
136  return 0;
137}
138
139static void
140mapper_netns_notify_cb(void *obj, netns_type_t type,
141                       u32 flags, uword opaque)
142{
143  mapper_main_t *mm = &mapper_main;
144  mapper_ns_t *ns = &mm->namespaces[(u32) opaque];
145  ASSERT(!pool_is_free_index(mm->namespaces, (u32) opaque));
146  if (type != NETNS_TYPE_ROUTE)
147    return; //For now...
148
149  ns_route_t *route = obj;
150  if (flags & NETNS_F_DEL) {
151    mapper_add_del_route(ns, route, 1);
152  } else if (flags & NETNS_F_ADD) {
153    mapper_add_del_route(ns, route, 0);
154  }
155}
156
157void
158mapper_delmap(mapper_ns_t*ns, mapper_map_t *map)
159{
160  ns_route_t *route;
161  netns_t *netns = netns_getns(ns->netns_handle);
162  pool_foreach(route, netns->routes, {
163      if (route->oif == map->linux_ifindex)
164        mapper_add_del_route(ns, route, 1);
165  });
166  pool_put(ns->mappings, map);
167}
168
169mapper_map_t *
170mapper_getmap(mapper_ns_t*ns, u32 sw_if_index,
171              int linux_ifindex, int create)
172{
173  mapper_map_t *map;
174  pool_foreach(map, ns->mappings, {
175      if (linux_ifindex == map->linux_ifindex) {
176        if (sw_if_index != map->sw_if_index)
177          return NULL; //Cannot have multiple mapping with the same ifindex
178        else
179          return map;
180      }
181  });
182
183  if (!create)
184    return NULL;
185
186  pool_get(ns->mappings, map);
187  map->linux_ifindex = linux_ifindex;
188  map->sw_if_index = sw_if_index;
189  ip6_main.fib_index_by_sw_if_index[sw_if_index] = ns->v6fib_index;
190  ip4_main.fib_index_by_sw_if_index[sw_if_index] = ns->v4fib_index;
191
192  //Load available routes
193  ns_route_t *route;
194  netns_t *netns = netns_getns(ns->netns_handle);
195  pool_foreach(route, netns->routes, {
196      if (route->oif == map->linux_ifindex)
197        mapper_add_del_route(ns, route, 0);
198  });
199  return map;
200}
201
202u32
203mapper_get_ns(char *nsname)
204{
205  mapper_main_t *mm = &mapper_main;
206  mapper_ns_t *ns;
207  pool_foreach(ns, mm->namespaces, {
208      if (!strcmp(nsname, ns->nsname))
209        return ns - mm->namespaces;
210  });
211  return ~0;
212}
213
214int
215mapper_add_del(u32 nsindex, int linux_ifindex,
216               u32 sw_if_index, int del)
217{
218  mapper_main_t *mm = &mapper_main;
219  //ip6_main_t *im6 = &ip6_main;
220  mapper_ns_t *ns = &mm->namespaces[nsindex];
221  mapper_map_t *map;
222  //vnet_sw_interface_t *iface = vnet_get_sw_interface(vnet_get_main(), sw_if_index);
223
224  if (pool_is_free(mm->namespaces, ns))
225    return -1;
226
227  /*if (!del) {
228    if ((iface->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) &&
229        im6->fib_index_by_sw_if_index[sw_if_index] != ~0) {
230      //A custom fib index will be used...
231      clib_warning("Cannot add interface with a custom fib index (current is %d)",
232                   im6->fib_index_by_sw_if_index[sw_if_index]);
233      return -1;
234    }
235  }*/
236
237  if (!(map = mapper_getmap(ns, sw_if_index, linux_ifindex, !del)))
238    return -1;
239
240  if (del)
241    mapper_delmap(ns, map);
242
243  return 0;
244}
245
246int
247mapper_add_ns(char *nsname, u32 v4fib_index, u32 v6fib_index, u32 *nsindex)
248{
249  mapper_main_t *mm = &mapper_main;
250  mapper_ns_t *ns;
251  if (mapper_get_ns(nsname) != ~0)
252    return -1; //Already exists
253
254  pool_get(mm->namespaces, ns);
255  strcpy(ns->nsname, nsname);
256  ns->v4fib_index = v4fib_index;
257  ns->v6fib_index = v6fib_index;
258  ns->mappings = 0;
259
260  netns_sub_t sub;
261  sub.notify = mapper_netns_notify_cb;
262  sub.opaque = (uword)(ns - mm->namespaces);
263  if ((ns->netns_handle = netns_open(ns->nsname, &sub)) == ~0) {
264    pool_put(mm->namespaces, ns);
265    return -1;
266  }
267  *nsindex = ns - mm->namespaces;
268  return 0;
269}
270
271int
272mapper_del_ns(u32 nsindex)
273{
274  mapper_main_t *mm = &mapper_main;
275  mapper_ns_t *ns = &mm->namespaces[nsindex];
276  if (pool_is_free(mm->namespaces, ns))
277    return -1;
278
279  //Remove all existing mappings
280  int i, *indexes = 0;
281  pool_foreach_index(i, ns->mappings, {
282    vec_add1(indexes, i);
283  });
284  vec_foreach_index(i, indexes) {
285    mapper_delmap(ns, &ns->mappings[indexes[i]]);
286  }
287  vec_free(indexes);
288
289  netns_close(ns->netns_handle);
290  pool_put(mm->namespaces, ns);
291  return 0;
292}
293
294clib_error_t *
295mapper_init (vlib_main_t * vm)
296{
297  mapper_main_t *mm = &mapper_main;
298  mm->namespaces = 0;
299  return 0;
300}
301
302VLIB_INIT_FUNCTION (mapper_init);
303