tap_inject.c revision 0757a32b
1/*
2 * Copyright 2016 Intel Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *   http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "tap_inject.h"
18
19#include <vnet/mfib/mfib_table.h>
20#include <vnet/ip/ip.h>
21#include <vnet/ip/lookup.h>
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
29static tap_inject_main_t tap_inject_main;
30extern dpo_type_t tap_inject_dpo_type;
31
32tap_inject_main_t *
33tap_inject_get_main (void)
34{
35  return &tap_inject_main;
36}
37
38void
39tap_inject_insert_tap (u32 sw_if_index, u32 tap_fd, u32 tap_if_index)
40{
41  tap_inject_main_t * im = tap_inject_get_main ();
42
43  vec_validate_init_empty (im->sw_if_index_to_tap_fd, sw_if_index, ~0);
44  vec_validate_init_empty (im->sw_if_index_to_tap_if_index, sw_if_index, ~0);
45
46  vec_validate_init_empty (im->tap_fd_to_sw_if_index, tap_fd, ~0);
47
48  im->sw_if_index_to_tap_fd[sw_if_index] = tap_fd;
49  im->sw_if_index_to_tap_if_index[sw_if_index] = tap_if_index;
50
51  im->tap_fd_to_sw_if_index[tap_fd] = sw_if_index;
52
53  hash_set (im->tap_if_index_to_sw_if_index, tap_if_index, sw_if_index);
54}
55
56void
57tap_inject_delete_tap (u32 sw_if_index)
58{
59  tap_inject_main_t * im = tap_inject_get_main ();
60  u32 tap_fd = im->sw_if_index_to_tap_fd[sw_if_index];
61  u32 tap_if_index = im->sw_if_index_to_tap_if_index[sw_if_index];
62
63  im->sw_if_index_to_tap_if_index[sw_if_index] = ~0;
64  im->sw_if_index_to_tap_fd[sw_if_index] = ~0;
65  im->tap_fd_to_sw_if_index[tap_fd] = ~0;
66
67  hash_unset (im->tap_if_index_to_sw_if_index, tap_if_index);
68}
69
70u32
71tap_inject_lookup_tap_fd (u32 sw_if_index)
72{
73  tap_inject_main_t * im = tap_inject_get_main ();
74
75  vec_validate_init_empty (im->sw_if_index_to_tap_fd, sw_if_index, ~0);
76  return im->sw_if_index_to_tap_fd[sw_if_index];
77}
78
79u32
80tap_inject_lookup_sw_if_index_from_tap_fd (u32 tap_fd)
81{
82  tap_inject_main_t * im = tap_inject_get_main ();
83
84  vec_validate_init_empty (im->tap_fd_to_sw_if_index, tap_fd, ~0);
85  return im->tap_fd_to_sw_if_index[tap_fd];
86}
87
88u32
89tap_inject_lookup_sw_if_index_from_tap_if_index (u32 tap_if_index)
90{
91  tap_inject_main_t * im = tap_inject_get_main ();
92  uword * sw_if_index;
93
94  sw_if_index = hash_get (im->tap_if_index_to_sw_if_index, tap_if_index);
95  return sw_if_index ? *(u32 *)sw_if_index : ~0;
96}
97
98/* *INDENT-OFF* */
99VLIB_PLUGIN_REGISTER () = {
100    // .version = VPP_BUILD_VER, FIXME
101    .description = "router",
102};
103/* *INDENT-ON* */
104
105
106static void
107tap_inject_disable (void)
108{
109  tap_inject_main_t * im = tap_inject_get_main ();
110
111  im->flags &= ~TAP_INJECT_F_ENABLED;
112
113  clib_warning ("tap-inject is not actually disabled.");
114}
115
116static clib_error_t *
117tap_inject_enable (void)
118{
119  vlib_main_t * vm = vlib_get_main ();
120  tap_inject_main_t * im = tap_inject_get_main ();
121
122  if (tap_inject_is_enabled ())
123    return 0;
124
125  tap_inject_enable_netlink ();
126
127  /* Only enable netlink? */
128  if (im->flags & TAP_INJECT_F_CONFIG_NETLINK)
129    {
130      im->flags |= TAP_INJECT_F_ENABLED;
131      return 0;
132    }
133
134  /* Register ARP and ICMP6 as neighbor nodes. */
135  ethernet_register_input_type (vm, ETHERNET_TYPE_ARP, im->neighbor_node_index);
136  ip6_register_protocol (IP_PROTOCOL_ICMP6, im->neighbor_node_index);
137
138  /* Register remaining protocols. */
139  ip4_register_protocol (IP_PROTOCOL_ICMP, im->tx_node_index);
140
141  ip4_register_protocol (IP_PROTOCOL_OSPF, im->tx_node_index);
142  ip4_register_protocol (IP_PROTOCOL_TCP, im->tx_node_index);
143  ip4_register_protocol (IP_PROTOCOL_UDP, im->tx_node_index);
144
145  ip6_register_protocol (IP_PROTOCOL_OSPF, im->tx_node_index);
146  ip6_register_protocol (IP_PROTOCOL_TCP, im->tx_node_index);
147  ip6_register_protocol (IP_PROTOCOL_UDP, im->tx_node_index);
148
149#if FIB_VERSION == 1
150  /* Add IPv4 multicast route. */
151  {
152    ip4_add_del_route_args_t a;
153    ip_adjacency_t add_adj;
154    u32 next_node_index;
155
156    memset (&a, 0, sizeof (a));
157    memset (&add_adj, 0, sizeof (add_adj));
158
159    a.add_adj = &add_adj;
160    a.n_add_adj = 1;
161
162    a.flags = IP4_ROUTE_FLAG_TABLE_ID | IP4_ROUTE_FLAG_ADD;
163    a.table_index_or_table_id = 0;
164    a.dst_address.as_u32 = 0x000000E0; /* 224.0.0.0 */
165    a.dst_address_length = 24;
166    a.adj_index = ~0;
167
168    next_node_index = vlib_node_add_next (vm, ip4_lookup_node.index,
169                                          im->tx_node_index);
170
171    add_adj.explicit_fib_index = ~0;
172    add_adj.rewrite_header.node_index = ip4_rewrite_node.index;
173    add_adj.lookup_next_index = next_node_index;
174    add_adj.if_address_index = ~0;
175
176    ip4_add_del_route (&ip4_main, &a);
177  }
178#else
179  {
180    dpo_id_t dpo = DPO_INVALID;
181
182    const mfib_prefix_t pfx_224_0_0_0 = {
183        .fp_len = 24,
184        .fp_proto = FIB_PROTOCOL_IP4,
185        .fp_grp_addr = {
186            .ip4.as_u32 = clib_host_to_net_u32(0xe0000000),
187        },
188        .fp_src_addr = {
189            .ip4.as_u32 = 0,
190        },
191    };
192
193    dpo_set(&dpo, tap_inject_dpo_type, DPO_PROTO_IP4, ~0);
194
195    index_t repi = replicate_create(1, DPO_PROTO_IP4);
196    replicate_set_bucket(repi, 0, &dpo);
197
198    mfib_table_entry_special_add(0,
199                                 &pfx_224_0_0_0,
200                                 MFIB_SOURCE_API,
201                                 MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF,
202                                 repi);
203
204    dpo_reset(&dpo);
205  }
206#endif /* FIB_VERSION == 1 */
207
208  im->flags |= TAP_INJECT_F_ENABLED;
209
210  return 0;
211}
212
213static uword
214tap_inject_iface_isr (vlib_main_t * vm, vlib_node_runtime_t * node,
215                      vlib_frame_t * f)
216{
217  tap_inject_main_t * im = tap_inject_get_main ();
218  vnet_hw_interface_t * hw;
219  u32 * hw_if_index;
220  clib_error_t * err = 0;
221
222  vec_foreach (hw_if_index, im->interfaces_to_enable)
223    {
224      hw = vnet_get_hw_interface (vnet_get_main (), *hw_if_index);
225
226      if (hw->hw_class_index == ethernet_hw_interface_class.index)
227        {
228          err = tap_inject_tap_connect (hw);
229          if (err)
230            break;
231        }
232    }
233
234  vec_foreach (hw_if_index, im->interfaces_to_disable)
235    tap_inject_tap_disconnect (*hw_if_index);
236
237  vec_free (im->interfaces_to_enable);
238  vec_free (im->interfaces_to_disable);
239
240  return err ? -1 : 0;
241}
242
243VLIB_REGISTER_NODE (tap_inject_iface_isr_node, static) = {
244  .function = tap_inject_iface_isr,
245  .name = "tap-inject-iface-isr",
246  .type = VLIB_NODE_TYPE_INPUT,
247  .state = VLIB_NODE_STATE_INTERRUPT,
248  .vector_size = sizeof (u32),
249};
250
251
252static clib_error_t *
253tap_inject_interface_add_del (struct vnet_main_t * vnet_main, u32 hw_if_index,
254                              u32 add)
255{
256  vlib_main_t * vm = vlib_get_main ();
257  tap_inject_main_t * im = tap_inject_get_main ();
258
259  if (!tap_inject_is_config_enabled ())
260    return 0;
261
262  tap_inject_enable ();
263
264  if (add)
265    vec_add1 (im->interfaces_to_enable, hw_if_index);
266  else
267    vec_add1 (im->interfaces_to_disable, hw_if_index);
268
269  vlib_node_set_interrupt_pending (vm, tap_inject_iface_isr_node.index);
270
271  return 0;
272}
273
274VNET_HW_INTERFACE_ADD_DEL_FUNCTION (tap_inject_interface_add_del);
275
276
277static clib_error_t *
278tap_inject_enable_disable_all_interfaces (int enable)
279{
280  vnet_main_t * vnet_main = vnet_get_main ();
281  tap_inject_main_t * im = tap_inject_get_main ();
282  vnet_hw_interface_t * interfaces;
283  vnet_hw_interface_t * hw;
284  u32 ** indices;
285
286  if (enable)
287    tap_inject_enable ();
288  else
289    tap_inject_disable ();
290
291  /* Collect all the interface indices. */
292  interfaces = vnet_main->interface_main.hw_interfaces;
293  indices = enable ? &im->interfaces_to_enable : &im->interfaces_to_disable;
294  pool_foreach (hw, interfaces, vec_add1 (*indices, hw - interfaces));
295
296  if (tap_inject_iface_isr (vlib_get_main (), 0, 0))
297    return clib_error_return (0, "tap-inject interface add del isr failed");
298
299  return 0;
300}
301
302static clib_error_t *
303tap_inject_cli (vlib_main_t * vm, unformat_input_t * input,
304                 vlib_cli_command_t * cmd)
305{
306  tap_inject_main_t * im = tap_inject_get_main ();
307
308  if (cmd->function_arg)
309    {
310      clib_error_t * err;
311
312      if (tap_inject_is_config_disabled ())
313        return clib_error_return (0,
314            "tap-inject is disabled in config, thus cannot be enabled.");
315
316      /* Enable */
317      err = tap_inject_enable_disable_all_interfaces (1);
318      if (err)
319        {
320          tap_inject_enable_disable_all_interfaces (0);
321          return err;
322        }
323
324      im->flags |= TAP_INJECT_F_CONFIG_ENABLE;
325    }
326  else
327    {
328      /* Disable */
329      tap_inject_enable_disable_all_interfaces (0);
330      im->flags &= ~TAP_INJECT_F_CONFIG_ENABLE;
331    }
332
333  return 0;
334}
335
336VLIB_CLI_COMMAND (tap_inject_enable_cmd, static) = {
337  .path = "enable tap-inject",
338  .short_help = "enable tap-inject",
339  .function = tap_inject_cli,
340  .function_arg = 1,
341};
342
343VLIB_CLI_COMMAND (tap_inject_disable_cmd, static) = {
344  .path = "disable tap-inject",
345  .short_help = "disable tap-inject",
346  .function = tap_inject_cli,
347  .function_arg = 0,
348};
349
350
351static clib_error_t *
352show_tap_inject (vlib_main_t * vm, unformat_input_t * input,
353                 vlib_cli_command_t * cmd)
354{
355  vnet_main_t * vnet_main = vnet_get_main ();
356  tap_inject_main_t * im = tap_inject_get_main ();
357  u32 k, v;
358
359  if (tap_inject_is_config_disabled ())
360    {
361      vlib_cli_output (vm, "tap-inject is disabled in config.\n");
362      return 0;
363    }
364
365  if (!tap_inject_is_enabled ())
366    {
367      vlib_cli_output (vm, "tap-inject is not enabled.\n");
368      return 0;
369    }
370
371  hash_foreach (k, v, im->tap_if_index_to_sw_if_index, {
372    vlib_cli_output (vm, "%U -> %U",
373            format_vnet_sw_interface_name, vnet_main,
374            vnet_get_sw_interface (vnet_main, v),
375            format_tap_inject_tap_name, k);
376  });
377
378  return 0;
379}
380
381VLIB_CLI_COMMAND (show_tap_inject_cmd, static) = {
382  .path = "show tap-inject",
383  .short_help = "show tap-inject",
384  .function = show_tap_inject,
385};
386
387
388static clib_error_t *
389tap_inject_config (vlib_main_t * vm, unformat_input_t * input)
390{
391  tap_inject_main_t * im = tap_inject_get_main ();
392
393  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
394    {
395      if (unformat (input, "enable"))
396        im->flags |= TAP_INJECT_F_CONFIG_ENABLE;
397
398      else if (unformat (input, "disable"))
399        im->flags |= TAP_INJECT_F_CONFIG_DISABLE;
400
401      else if (unformat (input, "netlink-only"))
402        im->flags |= TAP_INJECT_F_CONFIG_NETLINK;
403
404      else
405        return clib_error_return (0, "syntax error `%U'",
406                                  format_unformat_error, input);
407    }
408
409  if (tap_inject_is_config_enabled () && tap_inject_is_config_disabled ())
410    return clib_error_return (0,
411              "tap-inject cannot be both enabled and disabled.");
412
413  return 0;
414}
415
416VLIB_CONFIG_FUNCTION (tap_inject_config, "tap-inject");
417