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