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