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