tap_inject.c revision 22dc5563
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#include <vnet/fib/fib.h>
23#include <vnet/osi/osi.h>
24
25static tap_inject_main_t tap_inject_main;
26extern dpo_type_t tap_inject_dpo_type;
27
28tap_inject_main_t *
29tap_inject_get_main (void)
30{
31  return &tap_inject_main;
32}
33
34void
35tap_inject_insert_tap (u32 sw_if_index, u32 tap_fd, u32 tap_if_index)
36{
37  tap_inject_main_t * im = tap_inject_get_main ();
38
39  vec_validate_init_empty (im->sw_if_index_to_tap_fd, sw_if_index, ~0);
40  vec_validate_init_empty (im->sw_if_index_to_tap_if_index, sw_if_index, ~0);
41
42  vec_validate_init_empty (im->tap_fd_to_sw_if_index, tap_fd, ~0);
43
44  im->sw_if_index_to_tap_fd[sw_if_index] = tap_fd;
45  im->sw_if_index_to_tap_if_index[sw_if_index] = tap_if_index;
46
47  im->tap_fd_to_sw_if_index[tap_fd] = sw_if_index;
48
49  hash_set (im->tap_if_index_to_sw_if_index, tap_if_index, sw_if_index);
50}
51
52void
53tap_inject_delete_tap (u32 sw_if_index)
54{
55  tap_inject_main_t * im = tap_inject_get_main ();
56  u32 tap_fd = im->sw_if_index_to_tap_fd[sw_if_index];
57  u32 tap_if_index = im->sw_if_index_to_tap_if_index[sw_if_index];
58
59  im->sw_if_index_to_tap_if_index[sw_if_index] = ~0;
60  im->sw_if_index_to_tap_fd[sw_if_index] = ~0;
61  im->tap_fd_to_sw_if_index[tap_fd] = ~0;
62
63  hash_unset (im->tap_if_index_to_sw_if_index, tap_if_index);
64}
65
66u32
67tap_inject_lookup_tap_fd (u32 sw_if_index)
68{
69  tap_inject_main_t * im = tap_inject_get_main ();
70
71  vec_validate_init_empty (im->sw_if_index_to_tap_fd, sw_if_index, ~0);
72  return im->sw_if_index_to_tap_fd[sw_if_index];
73}
74
75u32
76tap_inject_lookup_sw_if_index_from_tap_fd (u32 tap_fd)
77{
78  tap_inject_main_t * im = tap_inject_get_main ();
79
80  vec_validate_init_empty (im->tap_fd_to_sw_if_index, tap_fd, ~0);
81  return im->tap_fd_to_sw_if_index[tap_fd];
82}
83
84u32
85tap_inject_lookup_sw_if_index_from_tap_if_index (u32 tap_if_index)
86{
87  tap_inject_main_t * im = tap_inject_get_main ();
88  uword * sw_if_index;
89
90  sw_if_index = hash_get (im->tap_if_index_to_sw_if_index, tap_if_index);
91  return sw_if_index ? *(u32 *)sw_if_index : ~0;
92}
93
94/* *INDENT-OFF* */
95VLIB_PLUGIN_REGISTER () = {
96    // .version = VPP_BUILD_VER, FIXME
97    .description = "router",
98};
99/* *INDENT-ON* */
100
101
102static void
103tap_inject_disable (void)
104{
105  tap_inject_main_t * im = tap_inject_get_main ();
106
107  im->flags &= ~TAP_INJECT_F_ENABLED;
108
109  clib_warning ("tap-inject is not actually disabled.");
110}
111
112static clib_error_t *
113tap_inject_enable (void)
114{
115  vlib_main_t * vm = vlib_get_main ();
116  tap_inject_main_t * im = tap_inject_get_main ();
117
118  if (tap_inject_is_enabled ())
119    return 0;
120
121  tap_inject_enable_netlink ();
122
123  /* Only enable netlink? */
124  if (im->flags & TAP_INJECT_F_CONFIG_NETLINK)
125    {
126      im->flags |= TAP_INJECT_F_ENABLED;
127      return 0;
128    }
129
130  /* Register ARP and ICMP6 as neighbor nodes. */
131  ethernet_register_input_type (vm, ETHERNET_TYPE_ARP, im->neighbor_node_index);
132  ip6_register_protocol (IP_PROTOCOL_ICMP6, im->neighbor_node_index);
133
134  /* Register remaining protocols. */
135  ip4_register_protocol (IP_PROTOCOL_ICMP, im->tx_node_index);
136
137  ip4_register_protocol (IP_PROTOCOL_OSPF, im->tx_node_index);
138  ip4_register_protocol (IP_PROTOCOL_TCP, im->tx_node_index);
139  ip4_register_protocol (IP_PROTOCOL_UDP, im->tx_node_index);
140
141  ip6_register_protocol (IP_PROTOCOL_OSPF, im->tx_node_index);
142  ip6_register_protocol (IP_PROTOCOL_TCP, im->tx_node_index);
143  ip6_register_protocol (IP_PROTOCOL_UDP, im->tx_node_index);
144  /* Registering ISIS to OSI node. */
145  osi_register_input_protocol (OSI_PROTOCOL_isis, im->tx_node_index);
146
147  {
148    dpo_id_t dpo = DPO_INVALID;
149
150    const mfib_prefix_t pfx_224_0_0_0 = {
151        .fp_len = 24,
152        .fp_proto = FIB_PROTOCOL_IP4,
153        .fp_grp_addr = {
154            .ip4.as_u32 = clib_host_to_net_u32(0xe0000000),
155        },
156        .fp_src_addr = {
157            .ip4.as_u32 = 0,
158        },
159    };
160
161    dpo_set(&dpo, tap_inject_dpo_type, DPO_PROTO_IP4, ~0);
162
163    index_t repi = replicate_create(1, DPO_PROTO_IP4);
164    replicate_set_bucket(repi, 0, &dpo);
165
166    mfib_table_entry_special_add(0,
167                                 &pfx_224_0_0_0,
168                                 MFIB_SOURCE_API,
169                                 MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF,
170                                 repi);
171
172    dpo_reset(&dpo);
173  }
174
175  im->flags |= TAP_INJECT_F_ENABLED;
176
177  return 0;
178}
179
180static uword
181tap_inject_iface_isr (vlib_main_t * vm, vlib_node_runtime_t * node,
182                      vlib_frame_t * f)
183{
184  tap_inject_main_t * im = tap_inject_get_main ();
185  vnet_hw_interface_t * hw;
186  u32 * hw_if_index;
187  clib_error_t * err = 0;
188
189  vec_foreach (hw_if_index, im->interfaces_to_enable)
190    {
191      hw = vnet_get_hw_interface (vnet_get_main (), *hw_if_index);
192
193      if (hw->hw_class_index == ethernet_hw_interface_class.index)
194        {
195          err = tap_inject_tap_connect (hw);
196          if (err)
197            break;
198        }
199    }
200
201  vec_foreach (hw_if_index, im->interfaces_to_disable)
202    tap_inject_tap_disconnect (*hw_if_index);
203
204  vec_free (im->interfaces_to_enable);
205  vec_free (im->interfaces_to_disable);
206
207  return err ? -1 : 0;
208}
209
210VLIB_REGISTER_NODE (tap_inject_iface_isr_node, static) = {
211  .function = tap_inject_iface_isr,
212  .name = "tap-inject-iface-isr",
213  .type = VLIB_NODE_TYPE_INPUT,
214  .state = VLIB_NODE_STATE_INTERRUPT,
215  .vector_size = sizeof (u32),
216};
217
218
219static clib_error_t *
220tap_inject_interface_add_del (struct vnet_main_t * vnet_main, u32 hw_if_index,
221                              u32 add)
222{
223  vlib_main_t * vm = vlib_get_main ();
224  tap_inject_main_t * im = tap_inject_get_main ();
225
226  if (!tap_inject_is_config_enabled ())
227    return 0;
228
229  tap_inject_enable ();
230
231  if (add)
232    vec_add1 (im->interfaces_to_enable, hw_if_index);
233  else
234    vec_add1 (im->interfaces_to_disable, hw_if_index);
235
236  vlib_node_set_interrupt_pending (vm, tap_inject_iface_isr_node.index);
237
238  return 0;
239}
240
241VNET_HW_INTERFACE_ADD_DEL_FUNCTION (tap_inject_interface_add_del);
242
243
244static clib_error_t *
245tap_inject_enable_disable_all_interfaces (int enable)
246{
247  vnet_main_t * vnet_main = vnet_get_main ();
248  tap_inject_main_t * im = tap_inject_get_main ();
249  vnet_hw_interface_t * interfaces;
250  vnet_hw_interface_t * hw;
251  u32 ** indices;
252
253  if (enable)
254    tap_inject_enable ();
255  else
256    tap_inject_disable ();
257
258  /* Collect all the interface indices. */
259  interfaces = vnet_main->interface_main.hw_interfaces;
260  indices = enable ? &im->interfaces_to_enable : &im->interfaces_to_disable;
261  pool_foreach (hw, interfaces, vec_add1 (*indices, hw - interfaces));
262
263  if (tap_inject_iface_isr (vlib_get_main (), 0, 0))
264    return clib_error_return (0, "tap-inject interface add del isr failed");
265
266  return 0;
267}
268
269static clib_error_t *
270tap_inject_cli (vlib_main_t * vm, unformat_input_t * input,
271                 vlib_cli_command_t * cmd)
272{
273  tap_inject_main_t * im = tap_inject_get_main ();
274
275  if (cmd->function_arg)
276    {
277      clib_error_t * err;
278
279      if (tap_inject_is_config_disabled ())
280        return clib_error_return (0,
281            "tap-inject is disabled in config, thus cannot be enabled.");
282
283      /* Enable */
284      err = tap_inject_enable_disable_all_interfaces (1);
285      if (err)
286        {
287          tap_inject_enable_disable_all_interfaces (0);
288          return err;
289        }
290
291      im->flags |= TAP_INJECT_F_CONFIG_ENABLE;
292    }
293  else
294    {
295      /* Disable */
296      tap_inject_enable_disable_all_interfaces (0);
297      im->flags &= ~TAP_INJECT_F_CONFIG_ENABLE;
298    }
299
300  return 0;
301}
302
303VLIB_CLI_COMMAND (tap_inject_enable_cmd, static) = {
304  .path = "enable tap-inject",
305  .short_help = "enable tap-inject",
306  .function = tap_inject_cli,
307  .function_arg = 1,
308};
309
310VLIB_CLI_COMMAND (tap_inject_disable_cmd, static) = {
311  .path = "disable tap-inject",
312  .short_help = "disable tap-inject",
313  .function = tap_inject_cli,
314  .function_arg = 0,
315};
316
317
318static clib_error_t *
319show_tap_inject (vlib_main_t * vm, unformat_input_t * input,
320                 vlib_cli_command_t * cmd)
321{
322  vnet_main_t * vnet_main = vnet_get_main ();
323  tap_inject_main_t * im = tap_inject_get_main ();
324  u32 k, v;
325
326  if (tap_inject_is_config_disabled ())
327    {
328      vlib_cli_output (vm, "tap-inject is disabled in config.\n");
329      return 0;
330    }
331
332  if (!tap_inject_is_enabled ())
333    {
334      vlib_cli_output (vm, "tap-inject is not enabled.\n");
335      return 0;
336    }
337
338  hash_foreach (k, v, im->tap_if_index_to_sw_if_index, {
339    vlib_cli_output (vm, "%U -> %U",
340            format_vnet_sw_interface_name, vnet_main,
341            vnet_get_sw_interface (vnet_main, v),
342            format_tap_inject_tap_name, k);
343  });
344
345  return 0;
346}
347
348VLIB_CLI_COMMAND (show_tap_inject_cmd, static) = {
349  .path = "show tap-inject",
350  .short_help = "show tap-inject",
351  .function = show_tap_inject,
352};
353
354
355static clib_error_t *
356tap_inject_config (vlib_main_t * vm, unformat_input_t * input)
357{
358  tap_inject_main_t * im = tap_inject_get_main ();
359
360  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
361    {
362      if (unformat (input, "enable"))
363        im->flags |= TAP_INJECT_F_CONFIG_ENABLE;
364
365      else if (unformat (input, "disable"))
366        im->flags |= TAP_INJECT_F_CONFIG_DISABLE;
367
368      else if (unformat (input, "netlink-only"))
369        im->flags |= TAP_INJECT_F_CONFIG_NETLINK;
370
371      else
372        return clib_error_return (0, "syntax error `%U'",
373                                  format_unformat_error, input);
374    }
375
376  if (tap_inject_is_config_enabled () && tap_inject_is_config_disabled ())
377    return clib_error_return (0,
378              "tap-inject cannot be both enabled and disabled.");
379
380  return 0;
381}
382
383VLIB_CONFIG_FUNCTION (tap_inject_config, "tap-inject");
384