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