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