gbp_route_domain.c revision 3bab8f9c
1/*
2 * Copyright (c) 2018 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 <plugins/gbp/gbp_route_domain.h>
17#include <plugins/gbp/gbp_endpoint.h>
18
19#include <vnet/dpo/dvr_dpo.h>
20#include <vnet/fib/fib_table.h>
21#include <vnet/ip/ip_neighbor.h>
22
23/**
24 * A fixed MAC address to use as the source MAC for packets L3 switched
25 * onto the routed uu-fwd interfaces.
26 * Magic values - origin lost to the mists of time...
27 */
28/* *INDENT-OFF* */
29const static mac_address_t GBP_ROUTED_SRC_MAC = {
30  .bytes = {
31    0x0, 0x22, 0xBD, 0xF8, 0x19, 0xFF,
32  }
33};
34
35const static mac_address_t GBP_ROUTED_DST_MAC = {
36  .bytes = {
37    00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
38  }
39};
40/* *INDENT-ON* */
41
42/**
43 * Pool of GBP route_domains
44 */
45gbp_route_domain_t *gbp_route_domain_pool;
46
47/**
48 * DB of route_domains
49 */
50typedef struct gbp_route_domain_db_t
51{
52  uword *gbd_by_rd_id;
53} gbp_route_domain_db_t;
54
55static gbp_route_domain_db_t gbp_route_domain_db;
56static fib_source_t gbp_fib_source;
57
58/**
59 * logger
60 */
61vlib_log_class_t grd_logger;
62
63#define GBP_BD_DBG(...)                           \
64    vlib_log_debug (grd_logger, __VA_ARGS__);
65
66index_t
67gbp_route_domain_index (const gbp_route_domain_t * grd)
68{
69  return (grd - gbp_route_domain_pool);
70}
71
72gbp_route_domain_t *
73gbp_route_domain_get (index_t i)
74{
75  return (pool_elt_at_index (gbp_route_domain_pool, i));
76}
77
78static void
79gbp_route_domain_lock (index_t i)
80{
81  gbp_route_domain_t *grd;
82
83  grd = gbp_route_domain_get (i);
84  grd->grd_locks++;
85}
86
87index_t
88gbp_route_domain_find (u32 rd_id)
89{
90  uword *p;
91
92  p = hash_get (gbp_route_domain_db.gbd_by_rd_id, rd_id);
93
94  if (NULL != p)
95    return p[0];
96
97  return (INDEX_INVALID);
98}
99
100index_t
101gbp_route_domain_find_and_lock (u32 rd_id)
102{
103  index_t grdi;
104
105  grdi = gbp_route_domain_find (rd_id);
106
107  if (INDEX_INVALID != grdi)
108    {
109      gbp_route_domain_lock (grdi);
110    }
111  return (grdi);
112}
113
114static void
115gbp_route_domain_db_add (gbp_route_domain_t * grd)
116{
117  index_t grdi = grd - gbp_route_domain_pool;
118
119  hash_set (gbp_route_domain_db.gbd_by_rd_id, grd->grd_id, grdi);
120}
121
122static void
123gbp_route_domain_db_remove (gbp_route_domain_t * grd)
124{
125  hash_unset (gbp_route_domain_db.gbd_by_rd_id, grd->grd_id);
126}
127
128int
129gbp_route_domain_add_and_lock (u32 rd_id,
130			       gbp_scope_t scope,
131			       u32 ip4_table_id,
132			       u32 ip6_table_id,
133			       u32 ip4_uu_sw_if_index, u32 ip6_uu_sw_if_index)
134{
135  gbp_route_domain_t *grd;
136  index_t grdi;
137
138  grdi = gbp_route_domain_find (rd_id);
139
140  if (INDEX_INVALID == grdi)
141    {
142      fib_protocol_t fproto;
143
144      pool_get_zero (gbp_route_domain_pool, grd);
145
146      grd->grd_id = rd_id;
147      grd->grd_scope = scope;
148      grd->grd_table_id[FIB_PROTOCOL_IP4] = ip4_table_id;
149      grd->grd_table_id[FIB_PROTOCOL_IP6] = ip6_table_id;
150      grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP4] = ip4_uu_sw_if_index;
151      grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP6] = ip6_uu_sw_if_index;
152
153      FOR_EACH_FIB_IP_PROTOCOL (fproto)
154      {
155	grd->grd_fib_index[fproto] =
156	  fib_table_find_or_create_and_lock (fproto,
157					     grd->grd_table_id[fproto],
158					     gbp_fib_source);
159
160	if (~0 != grd->grd_uu_sw_if_index[fproto])
161	  {
162	    ethernet_header_t *eth;
163	    u8 *rewrite;
164
165	    rewrite = NULL;
166	    vec_validate (rewrite, sizeof (*eth) - 1);
167	    eth = (ethernet_header_t *) rewrite;
168
169	    eth->type = clib_host_to_net_u16 ((fproto == FIB_PROTOCOL_IP4 ?
170					       ETHERNET_TYPE_IP4 :
171					       ETHERNET_TYPE_IP6));
172
173	    mac_address_to_bytes (gbp_route_domain_get_local_mac (),
174				  eth->src_address);
175	    mac_address_to_bytes (gbp_route_domain_get_remote_mac (),
176				  eth->dst_address);
177
178	    /*
179	     * create an adjacency out of the uu-fwd interfaces that will
180	     * be used when adding subnet routes.
181	     */
182	    grd->grd_adj[fproto] =
183	      adj_nbr_add_or_lock_w_rewrite (fproto,
184					     fib_proto_to_link (fproto),
185					     &ADJ_BCAST_ADDR,
186					     grd->grd_uu_sw_if_index[fproto],
187					     rewrite);
188	  }
189	else
190	  {
191	    grd->grd_adj[fproto] = INDEX_INVALID;
192	  }
193      }
194
195      gbp_route_domain_db_add (grd);
196    }
197  else
198    {
199      grd = gbp_route_domain_get (grdi);
200    }
201
202  grd->grd_locks++;
203  GBP_BD_DBG ("add: %U", format_gbp_route_domain, grd);
204
205  return (0);
206}
207
208void
209gbp_route_domain_unlock (index_t index)
210{
211  gbp_route_domain_t *grd;
212
213  grd = gbp_route_domain_get (index);
214
215  grd->grd_locks--;
216
217  if (0 == grd->grd_locks)
218    {
219      fib_protocol_t fproto;
220
221      GBP_BD_DBG ("destroy: %U", format_gbp_route_domain, grd);
222
223      FOR_EACH_FIB_IP_PROTOCOL (fproto)
224      {
225	fib_table_unlock (grd->grd_fib_index[fproto], fproto, gbp_fib_source);
226	if (INDEX_INVALID != grd->grd_adj[fproto])
227	  adj_unlock (grd->grd_adj[fproto]);
228      }
229
230      gbp_route_domain_db_remove (grd);
231
232      pool_put (gbp_route_domain_pool, grd);
233    }
234}
235
236u32
237gbp_route_domain_get_rd_id (index_t grdi)
238{
239  gbp_route_domain_t *grd;
240
241  grd = gbp_route_domain_get (grdi);
242
243  return (grd->grd_id);
244}
245
246gbp_scope_t
247gbp_route_domain_get_scope (index_t grdi)
248{
249  gbp_route_domain_t *grd;
250
251  grd = gbp_route_domain_get (grdi);
252
253  return (grd->grd_scope);
254}
255
256int
257gbp_route_domain_delete (u32 rd_id)
258{
259  index_t grdi;
260
261  GBP_BD_DBG ("del: %d", rd_id);
262  grdi = gbp_route_domain_find (rd_id);
263
264  if (INDEX_INVALID != grdi)
265    {
266      GBP_BD_DBG ("del: %U", format_gbp_route_domain,
267		  gbp_route_domain_get (grdi));
268      gbp_route_domain_unlock (grdi);
269
270      return (0);
271    }
272
273  return (VNET_API_ERROR_NO_SUCH_ENTRY);
274}
275
276const mac_address_t *
277gbp_route_domain_get_local_mac (void)
278{
279  return (&GBP_ROUTED_SRC_MAC);
280}
281
282const mac_address_t *
283gbp_route_domain_get_remote_mac (void)
284{
285  return (&GBP_ROUTED_DST_MAC);
286}
287
288void
289gbp_route_domain_walk (gbp_route_domain_cb_t cb, void *ctx)
290{
291  gbp_route_domain_t *gbpe;
292
293  /* *INDENT-OFF* */
294  pool_foreach(gbpe, gbp_route_domain_pool,
295  {
296    if (!cb(gbpe, ctx))
297      break;
298  });
299  /* *INDENT-ON* */
300}
301
302static clib_error_t *
303gbp_route_domain_cli (vlib_main_t * vm,
304		      unformat_input_t * input, vlib_cli_command_t * cmd)
305{
306  vnet_main_t *vnm = vnet_get_main ();
307  u32 ip4_uu_sw_if_index = ~0;
308  u32 ip6_uu_sw_if_index = ~0;
309  u32 ip4_table_id = ~0;
310  u32 ip6_table_id = ~0;
311  u32 scope = ~0;
312  u32 rd_id = ~0;
313  u8 add = 1;
314
315  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
316    {
317      if (unformat (input, "ip4-uu %U", unformat_vnet_sw_interface,
318		    vnm, &ip4_uu_sw_if_index))
319	;
320      else if (unformat (input, "ip6-uu %U", unformat_vnet_sw_interface,
321			 vnm, &ip6_uu_sw_if_index))
322	;
323      else if (unformat (input, "ip4-table-id %d", &ip4_table_id))
324	;
325      else if (unformat (input, "ip6-table-id %d", &ip6_table_id))
326	;
327      else if (unformat (input, "add"))
328	add = 1;
329      else if (unformat (input, "del"))
330	add = 0;
331      else if (unformat (input, "rd %d", &rd_id))
332	;
333      else if (unformat (input, "scope %d", &scope))
334	;
335      else
336	break;
337    }
338
339  if (~0 == rd_id)
340    return clib_error_return (0, "RD-ID must be specified");
341
342  if (add)
343    {
344      if (~0 == ip4_table_id)
345	return clib_error_return (0, "IP4 table-ID must be specified");
346      if (~0 == ip6_table_id)
347	return clib_error_return (0, "IP6 table-ID must be specified");
348
349      gbp_route_domain_add_and_lock (rd_id, scope,
350				     ip4_table_id,
351				     ip6_table_id,
352				     ip4_uu_sw_if_index, ip6_uu_sw_if_index);
353    }
354  else
355    gbp_route_domain_delete (rd_id);
356
357  return (NULL);
358}
359
360/*?
361 * Configure a GBP route-domain
362 *
363 * @cliexpar
364 * @cliexstart{gbp route-domain [del] rd <ID> ip4-table-id <ID> ip6-table-id <ID> [ip4-uu <interface>] [ip6-uu <interface>]}
365 * @cliexend
366 ?*/
367/* *INDENT-OFF* */
368VLIB_CLI_COMMAND (gbp_route_domain_cli_node, static) = {
369  .path = "gbp route-domain",
370  .short_help = "gbp route-domain [del] rd <ID> ip4-table-id <ID> ip6-table-id <ID> [ip4-uu <interface>] [ip6-uu <interface>]",
371  .function = gbp_route_domain_cli,
372};
373
374u8 *
375format_gbp_route_domain (u8 * s, va_list * args)
376{
377  gbp_route_domain_t *grd = va_arg (*args, gbp_route_domain_t*);
378  vnet_main_t *vnm = vnet_get_main ();
379
380  if (NULL != grd)
381    s = format (s, "[%d] rd:%d ip4-uu:%U ip6-uu:%U locks:%d",
382                grd - gbp_route_domain_pool,
383                grd->grd_id,
384                format_vnet_sw_if_index_name, vnm, grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP4],
385                format_vnet_sw_if_index_name, vnm, grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP6],
386                grd->grd_locks);
387  else
388    s = format (s, "NULL");
389
390  return (s);
391}
392
393static int
394gbp_route_domain_show_one (gbp_route_domain_t *gb, void *ctx)
395{
396  vlib_main_t *vm;
397
398  vm = ctx;
399  vlib_cli_output (vm, "  %U",format_gbp_route_domain, gb);
400
401  return (1);
402}
403
404static clib_error_t *
405gbp_route_domain_show (vlib_main_t * vm,
406		   unformat_input_t * input, vlib_cli_command_t * cmd)
407{
408  vlib_cli_output (vm, "Route-Domains:");
409  gbp_route_domain_walk (gbp_route_domain_show_one, vm);
410
411  return (NULL);
412}
413
414/*?
415 * Show Group Based Policy Route_Domains and derived information
416 *
417 * @cliexpar
418 * @cliexstart{show gbp route_domain}
419 * @cliexend
420 ?*/
421/* *INDENT-OFF* */
422VLIB_CLI_COMMAND (gbp_route_domain_show_node, static) = {
423  .path = "show gbp route-domain",
424  .short_help = "show gbp route-domain\n",
425  .function = gbp_route_domain_show,
426};
427/* *INDENT-ON* */
428
429static clib_error_t *
430gbp_route_domain_init (vlib_main_t * vm)
431{
432  grd_logger = vlib_log_register_class ("gbp", "rd");
433  gbp_fib_source = fib_source_allocate ("gbp-rd",
434					FIB_SOURCE_PRIORITY_HI,
435					FIB_SOURCE_BH_DROP);
436
437  return (NULL);
438}
439
440VLIB_INIT_FUNCTION (gbp_route_domain_init);
441
442/*
443 * fd.io coding-style-patch-verification: ON
444 *
445 * Local Variables:
446 * eval: (c-set-style "gnu")
447 * End:
448 */
449