vrrp: fix vrrp_garp_or_na_send()'s memory leak
[vpp.git] / src / plugins / urpf / urpf.c
1 /*
2  * Copyright (c) 2020 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 <urpf/urpf.h>
17
18 #include <vnet/fib/fib_table.h>
19
20 static const char *urpf_feat_arcs[N_AF][VLIB_N_DIR] =
21 {
22   [AF_IP4] = {
23     [VLIB_RX] = "ip4-unicast",
24     [VLIB_TX] = "ip4-output",
25   },
26   [AF_IP6] = {
27     [VLIB_RX] = "ip6-unicast",
28     [VLIB_TX] = "ip6-output",
29   },
30 };
31
32 static const char *urpf_feats[N_AF][VLIB_N_DIR][URPF_N_MODES] =
33 {
34   [AF_IP4] = {
35     [VLIB_RX] = {
36       [URPF_MODE_STRICT] = "ip4-rx-urpf-strict",
37       [URPF_MODE_LOOSE] = "ip4-rx-urpf-loose",
38     },
39     [VLIB_TX] = {
40       [URPF_MODE_STRICT] = "ip4-tx-urpf-strict",
41       [URPF_MODE_LOOSE] = "ip4-tx-urpf-loose",
42     },
43   },
44   [AF_IP6] = {
45     [VLIB_RX] = {
46       [URPF_MODE_STRICT] = "ip6-rx-urpf-strict",
47       [URPF_MODE_LOOSE] = "ip6-rx-urpf-loose",
48     },
49     [VLIB_TX] = {
50       [URPF_MODE_STRICT] = "ip6-tx-urpf-strict",
51       [URPF_MODE_LOOSE] = "ip6-tx-urpf-loose",
52     },
53   },
54 };
55
56 /**
57  * Per-af, per-direction, per-interface uRPF configs
58  */
59
60 urpf_data_t *urpf_cfgs[N_AF][VLIB_N_DIR];
61
62 u8 *
63 format_urpf_mode (u8 * s, va_list * a)
64 {
65   urpf_mode_t mode = va_arg (*a, int);
66
67   switch (mode)
68     {
69 #define _(a,b)                                  \
70     case URPF_MODE_##a:                         \
71       return (format (s, "%s", b));
72       foreach_urpf_mode
73 #undef _
74     }
75
76   return (format (s, "unknown"));
77 }
78
79 static uword
80 unformat_urpf_mode (unformat_input_t * input, va_list * args)
81 {
82   urpf_mode_t *mode = va_arg (*args, urpf_mode_t *);
83
84   if (0)
85     ;
86 #define _(a,b)                                                  \
87   else if (unformat (input, b))                                 \
88     {                                                           \
89     *mode = URPF_MODE_##a;                                      \
90     return (1);                                                 \
91     }
92   foreach_urpf_mode
93 #undef _
94     return 0;
95 }
96
97 int
98 urpf_update (urpf_mode_t mode, u32 sw_if_index, ip_address_family_t af,
99              vlib_dir_t dir, u32 table_id)
100 {
101   fib_protocol_t proto;
102   u32 fib_index;
103   if (table_id != ~0)
104     {
105       proto = ip_address_family_to_fib_proto (af);
106       fib_index = fib_table_find (proto, table_id);
107       if (fib_index == (~0))
108         return VNET_API_ERROR_INVALID_VALUE;
109     }
110   else
111     {
112       bool is_ip4 = (AF_IP4 == af);
113       u32 *fib_index_by_sw_if_index = is_ip4 ?
114                                               ip4_main.fib_index_by_sw_if_index :
115                                               ip6_main.fib_index_by_sw_if_index;
116
117       fib_index = fib_index_by_sw_if_index[sw_if_index];
118     }
119   urpf_data_t old;
120   urpf_mode_t off = URPF_MODE_OFF;
121   urpf_data_t empty = { .fib_index = 0, .mode = off };
122   vec_validate_init_empty (urpf_cfgs[af][dir], sw_if_index, empty);
123   old = urpf_cfgs[af][dir][sw_if_index];
124
125   urpf_data_t data = { .fib_index = fib_index,
126                        .mode = mode,
127                        .fib_index_is_custom = (table_id != ~0) };
128   urpf_cfgs[af][dir][sw_if_index] = data;
129   if (data.mode != old.mode || data.fib_index != old.fib_index)
130     {
131       if (URPF_MODE_OFF != old.mode)
132         /* disable what we have */
133         vnet_feature_enable_disable (urpf_feat_arcs[af][dir],
134                                      urpf_feats[af][dir][old.mode],
135                                      sw_if_index, 0, 0, 0);
136
137       if (URPF_MODE_OFF != data.mode)
138         /* enable what's new */
139         vnet_feature_enable_disable (urpf_feat_arcs[af][dir],
140                                      urpf_feats[af][dir][data.mode],
141                                      sw_if_index, 1, 0, 0);
142     }
143   /* else - no change to existing config */
144   return 0;
145 }
146
147 static void
148 urpf_table_bind_v4 (ip4_main_t *im, uword opaque, u32 sw_if_index,
149                     u32 new_fib_index, u32 old_fib_index)
150 {
151   vlib_dir_t dir;
152   urpf_data_t empty = { .fib_index = 0, .mode = URPF_MODE_OFF };
153   FOREACH_VLIB_DIR (dir)
154   {
155     vec_validate_init_empty (urpf_cfgs[AF_IP4][dir], sw_if_index, empty);
156     if (!urpf_cfgs[AF_IP4][dir][sw_if_index].fib_index_is_custom)
157       {
158         urpf_cfgs[AF_IP4][dir][sw_if_index].fib_index = new_fib_index;
159       }
160   }
161 }
162
163 static void
164 urpf_table_bind_v6 (ip6_main_t *im, uword opaque, u32 sw_if_index,
165                     u32 new_fib_index, u32 old_fib_index)
166 {
167   vlib_dir_t dir;
168   urpf_data_t empty = { .fib_index = 0, .mode = URPF_MODE_OFF };
169   FOREACH_VLIB_DIR (dir)
170   {
171     vec_validate_init_empty (urpf_cfgs[AF_IP6][dir], sw_if_index, empty);
172     if (!urpf_cfgs[AF_IP6][dir][sw_if_index].fib_index_is_custom)
173       {
174         urpf_cfgs[AF_IP6][dir][sw_if_index].fib_index = new_fib_index;
175       }
176   }
177 }
178
179 static clib_error_t *
180 urpf_init (vlib_main_t *vm)
181 {
182   ip4_table_bind_callback_t cb4 = {
183     .function = urpf_table_bind_v4,
184   };
185   vec_add1 (ip4_main.table_bind_callbacks, cb4);
186
187   ip6_table_bind_callback_t cb6 = {
188     .function = urpf_table_bind_v6,
189   };
190   vec_add1 (ip6_main.table_bind_callbacks, cb6);
191   return (NULL);
192 }
193
194 VLIB_INIT_FUNCTION (urpf_init);
195
196 static clib_error_t *
197 urpf_cli_update (vlib_main_t * vm,
198                  unformat_input_t * input, vlib_cli_command_t * cmd)
199 {
200   unformat_input_t _line_input, *line_input = &_line_input;
201   vnet_main_t *vnm = vnet_get_main ();
202   clib_error_t *error = NULL;
203   ip_address_family_t af;
204   urpf_mode_t mode;
205   u32 sw_if_index;
206   vlib_dir_t dir;
207   u32 table_id;
208
209   sw_if_index = ~0;
210   af = AF_IP4;
211   dir = VLIB_RX;
212   mode = URPF_MODE_STRICT;
213   table_id = ~0;
214
215   if (!unformat_user (input, unformat_line_input, line_input))
216     return 0;
217
218   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
219     {
220       if (unformat (line_input, "%U",
221                     unformat_vnet_sw_interface, vnm, &sw_if_index))
222         ;
223       else if (unformat (line_input, "%U", unformat_urpf_mode, &mode))
224         ;
225       else if (unformat (line_input, "table %d", &table_id))
226         ;
227       else if (unformat (line_input, "%U", unformat_ip_address_family, &af))
228         ;
229       else if (unformat (line_input, "%U", unformat_vlib_rx_tx, &dir))
230         ;
231       else
232         {
233           error = unformat_parse_error (line_input);
234           goto done;
235         }
236     }
237
238   if (~0 == sw_if_index)
239     {
240       error = clib_error_return (0, "unknown interface `%U'",
241                                  format_unformat_error, line_input);
242       goto done;
243     }
244
245   int rv = 0;
246   rv = urpf_update (mode, sw_if_index, af, dir, table_id);
247   if (rv)
248     {
249       error = clib_error_return (0, "unknown table id");
250       goto done;
251     }
252 done:
253   unformat_free (line_input);
254
255   return error;
256 }
257
258 /*?
259  * This command configures uRPF on an interface.
260  * Two flavours are supported (the default is strict):
261  * - loose: accept ingress packet if there is a route to reach the source
262  * - strict: accept ingress packet if it arrived on an interface which
263  *          the route to the source uses. i.e. an interface that the source
264  *          is reachable via.
265  *
266  * @cliexpar
267  * @parblock
268  * Example of graph node before range checking is enabled:
269  * @cliexstart{show vlib graph ip4-rx-urpf-strict}
270  *            Name                      Next                    Previous
271  * ip4-rx-urpf-strict         ip4-drop [0]
272  * @cliexend
273  *
274  * Example of how to enable unicast source checking on an interface:
275  * @cliexcmd{set urpf ip4 rx GigabitEthernet2/0/0 loose}
276  *
277  * Example of graph node after range checking is enabled:
278  * @cliexstart{show vlib graph ip4-rx-urpf-loose}
279  *       Name                    Next                  Previous
280  * ip4-rx-urpf-loose          ip4-drop [0]        ip4-input-no-checksum
281  *                    ip4-source-and-port-range-       ip4-input
282  * @cliexend
283  *
284  * Example of how to display the feature enabled on an interface:
285  * @cliexstart{show ip interface features GigabitEthernet2/0/0}
286  * IP feature paths configured on GigabitEthernet2/0/0...
287  *
288  * ipv4 unicast:
289  *   ip4-rx-urpf-loose
290  *   ip4-lookup
291  *
292  * ipv4 multicast:
293  *   ip4-lookup-multicast
294  *
295  * ipv4 multicast:
296  *   interface-output
297  *
298  * ipv6 unicast:
299  *   ip6-lookup
300  *
301  * ipv6 multicast:
302  *   ip6-lookup
303  *
304  * ipv6 multicast:
305  *   interface-output
306  * @cliexend
307  *
308  * Example of how to disable unicast source checking on an interface:
309  * @cliexcmd{set urpf ip4 off GigabitEthernet2/0/0}
310  * @endparblock
311 ?*/
312 VLIB_CLI_COMMAND (set_interface_ip_source_check_command, static) = {
313   .path = "set urpf",
314   .function = urpf_cli_update,
315   .short_help = "set urpf [ip4|ip6] [rx|tx] [off|strict|loose] "
316                 "<INTERFACE> [table <table>]",
317 };
318
319 static clib_error_t *
320 urpf_cli_accept (vlib_main_t * vm,
321                  unformat_input_t * input, vlib_cli_command_t * cmd)
322 {
323   unformat_input_t _line_input, *line_input = &_line_input;
324   clib_error_t *error = NULL;
325   fib_prefix_t fpfx;
326   ip_prefix_t pfx;
327   u32 table_id, is_add, fib_index;
328
329   is_add = 1;
330   table_id = 0;
331
332   /* Get a line of input. */
333   if (!unformat_user (input, unformat_line_input, line_input))
334     return 0;
335
336   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
337     {
338       if (unformat (line_input, "table %d", &table_id))
339         ;
340       else if (unformat (line_input, "del"))
341         is_add = 0;
342       else if (unformat (line_input, "add"))
343         is_add = 1;
344       else if (unformat (line_input, "%U", unformat_ip_prefix, &pfx))
345         ;
346       else
347         {
348           error = unformat_parse_error (line_input);
349           goto done;
350         }
351     }
352
353   ip_prefix_to_fib_prefix (&pfx, &fpfx);
354
355   fib_index = fib_table_find (fpfx.fp_proto, table_id);
356
357   if (~0 == fib_index)
358     {
359       error = clib_error_return (0, "Nonexistent table id %d", table_id);
360       goto done;
361     }
362
363   if (is_add)
364     fib_table_entry_special_add (fib_index,
365                                  &fpfx,
366                                  FIB_SOURCE_URPF_EXEMPT, FIB_ENTRY_FLAG_DROP);
367   else
368     fib_table_entry_special_remove (fib_index, &fpfx, FIB_SOURCE_URPF_EXEMPT);
369
370 done:
371   unformat_free (line_input);
372
373   return (error);
374 }
375
376 /*?
377  * Add an exemption for a prefix to pass the Unicast Reverse Path
378  * Forwarding (uRPF) loose check. This is for testing purposes only.
379  * If the '<em>table</em>' is not enter it is defaulted to 0. Default
380  * is to '<em>add</em>'. VPP always performs a loose uRPF check for
381  * for-us traffic.
382  *
383  * @cliexpar
384  * Example of how to add a uRPF exception to a FIB table to pass the
385  * loose RPF tests:
386  * @cliexcmd{set urpf-accept table 7 10.0.0.0/8 add}
387 ?*/
388 VLIB_CLI_COMMAND (urpf_accept_command, static) = {
389   .path = "set urpf-accept",
390   .function = urpf_cli_accept,
391   .short_help = "urpf-accept [table <table-id>] [add|del] <PREFIX>",
392 };
393
394 /*
395  * fd.io coding-style-patch-verification: ON
396  *
397  * Local Variables:
398  * eval: (c-set-style "gnu")
399  * End:
400  */