ip: add classifier-based ACLs support on ip punt
[vpp.git] / src / vnet / classify / in_out_acl.c
1 /*
2  * Copyright (c) 2015 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 #include <vnet/ip/ip.h>
16 #include <vnet/classify/vnet_classify.h>
17 #include <vnet/classify/in_out_acl.h>
18 #include <vnet/l2/l2_output.h>
19 #include <vnet/l2/l2_input.h>
20
21 in_out_acl_main_t in_out_acl_main;
22
23 static int
24 vnet_in_out_acl_feature_enable (in_out_acl_main_t *am, u32 sw_if_index,
25                                 in_out_acl_table_id_t tid, int feature_enable,
26                                 int is_output)
27 {
28   const char *arc_name, *feature_name;
29   vnet_feature_config_main_t *fcm;
30   u8 arc;
31   int rv;
32
33   switch (tid)
34     {
35     case IN_OUT_ACL_N_TABLES:
36       return VNET_API_ERROR_NO_SUCH_TABLE;
37     case IN_OUT_ACL_TABLE_L2:
38       if (is_output)
39         l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_ACL,
40                                      feature_enable);
41       else
42         l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_ACL,
43                                     feature_enable);
44       return 0;
45     case IN_OUT_ACL_TABLE_IP4:
46       arc_name = is_output ? "ip4-output" : "ip4-unicast";
47       feature_name = is_output ? "ip4-outacl" : "ip4-inacl";
48       break;
49     case IN_OUT_ACL_TABLE_IP6:
50       arc_name = is_output ? "ip6-output" : "ip6-unicast";
51       feature_name = is_output ? "ip6-outacl" : "ip6-inacl";
52       break;
53     case IN_OUT_ACL_TABLE_IP4_PUNT:
54       if (sw_if_index != 0)
55         return VNET_API_ERROR_INVALID_INTERFACE;
56       arc_name = "ip4-punt";
57       feature_name = "ip4-punt-acl";
58       break;
59     case IN_OUT_ACL_TABLE_IP6_PUNT:
60       if (sw_if_index != 0)
61         return VNET_API_ERROR_INVALID_INTERFACE;
62       arc_name = "ip6-punt";
63       feature_name = "ip6-punt-acl";
64       break;
65     }
66
67   rv = vnet_feature_enable_disable (arc_name, feature_name, sw_if_index,
68                                     feature_enable, 0, 0);
69   if (rv)
70     return rv;
71
72   arc = vnet_get_feature_arc_index (arc_name);
73   fcm = vnet_get_feature_arc_config_main (arc);
74   am->vnet_config_main[is_output][tid] = &fcm->config_main;
75
76   return 0;
77 }
78
79 int
80 vnet_set_in_out_acl_intfc (vlib_main_t *vm, u32 sw_if_index,
81                            u32 ip4_table_index, u32 ip6_table_index,
82                            u32 l2_table_index, u32 ip4_punt_table_index,
83                            u32 ip6_punt_table_index, u32 is_add, u32 is_output)
84 {
85   in_out_acl_main_t *am = &in_out_acl_main;
86   vnet_classify_main_t *vcm = am->vnet_classify_main;
87   u32 acl[IN_OUT_ACL_N_TABLES] = {
88     ip4_table_index,      ip6_table_index,      l2_table_index,
89     ip4_punt_table_index, ip6_punt_table_index,
90   };
91   u32 ti;
92   int rv;
93
94   /* Assume that we've validated sw_if_index in the API layer */
95
96   for (ti = 0; ti < IN_OUT_ACL_N_TABLES; ti++)
97     {
98       if (acl[ti] == ~0)
99         continue;
100
101       if (pool_is_free_index (vcm->tables, acl[ti]))
102         return VNET_API_ERROR_NO_SUCH_TABLE;
103
104       vec_validate_init_empty
105         (am->classify_table_index_by_sw_if_index[is_output][ti], sw_if_index,
106          ~0);
107
108       /* Reject any DEL operation with wrong sw_if_index */
109       if (!is_add &&
110           (acl[ti] !=
111            am->classify_table_index_by_sw_if_index[is_output][ti]
112            [sw_if_index]))
113         {
114           clib_warning
115             ("Non-existent intf_idx=%d with table_index=%d for delete",
116              sw_if_index, acl[ti]);
117           return VNET_API_ERROR_NO_SUCH_TABLE;
118         }
119
120       /* Return ok on ADD operaton if feature is already enabled */
121       if (is_add &&
122           am->classify_table_index_by_sw_if_index[is_output][ti][sw_if_index]
123           != ~0)
124         return 0;
125
126       rv = vnet_in_out_acl_feature_enable (am, sw_if_index, ti, is_add,
127                                            is_output);
128       if (rv)
129         return rv;
130
131       if (is_add)
132         am->classify_table_index_by_sw_if_index[is_output][ti][sw_if_index] =
133           acl[ti];
134       else
135         am->classify_table_index_by_sw_if_index[is_output][ti][sw_if_index] =
136           ~0;
137     }
138
139   return 0;
140 }
141
142 int
143 vnet_set_input_acl_intfc (vlib_main_t * vm, u32 sw_if_index,
144                           u32 ip4_table_index,
145                           u32 ip6_table_index, u32 l2_table_index, u32 is_add)
146 {
147   return vnet_set_in_out_acl_intfc (
148     vm, sw_if_index, ip4_table_index, ip6_table_index, l2_table_index,
149     ~0 /* ip4_punt_table_index */, ~0 /* ip6_punt_table_index */, is_add,
150     IN_OUT_ACL_INPUT_TABLE_GROUP);
151 }
152
153 int
154 vnet_set_output_acl_intfc (vlib_main_t * vm, u32 sw_if_index,
155                            u32 ip4_table_index,
156                            u32 ip6_table_index, u32 l2_table_index,
157                            u32 is_add)
158 {
159   return vnet_set_in_out_acl_intfc (
160     vm, sw_if_index, ip4_table_index, ip6_table_index, l2_table_index,
161     ~0 /* ip4_punt_table_index */, ~0 /* ip6_punt_table_index */, is_add,
162     IN_OUT_ACL_OUTPUT_TABLE_GROUP);
163 }
164
165 static clib_error_t *
166 set_in_out_acl_command_fn (vlib_main_t * vm,
167                            unformat_input_t * input, vlib_cli_command_t * cmd,
168                            u32 is_output)
169 {
170   vnet_main_t *vnm = vnet_get_main ();
171   u32 sw_if_index = ~0;
172   u32 ip4_table_index = ~0;
173   u32 ip6_table_index = ~0;
174   u32 ip4_punt_table_index = ~0;
175   u32 ip6_punt_table_index = ~0;
176   u32 l2_table_index = ~0;
177   u32 is_add = 1;
178   u32 idx_cnt = 0;
179   int rv;
180
181   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
182     {
183       if (unformat (input, "intfc %U", unformat_vnet_sw_interface,
184                     vnm, &sw_if_index))
185         ;
186       else if (unformat (input, "ip4-table %d", &ip4_table_index))
187         idx_cnt++;
188       else if (unformat (input, "ip6-table %d", &ip6_table_index))
189         idx_cnt++;
190       else if (unformat (input, "ip4-punt-table %d", &ip4_punt_table_index))
191         idx_cnt++;
192       else if (unformat (input, "ip6-punt-table %d", &ip6_punt_table_index))
193         idx_cnt++;
194       else if (unformat (input, "l2-table %d", &l2_table_index))
195         idx_cnt++;
196       else if (unformat (input, "del"))
197         is_add = 0;
198       else
199         break;
200     }
201
202   if (sw_if_index == ~0)
203     return clib_error_return (0, "Interface must be specified.");
204
205   if (!idx_cnt)
206     return clib_error_return (0, "Table index should be specified.");
207
208   if (idx_cnt > 1)
209     return clib_error_return (0, "Only one table index per API is allowed.");
210
211   rv = vnet_set_in_out_acl_intfc (
212     vm, sw_if_index, ip4_table_index, ip6_table_index, l2_table_index,
213     ip4_punt_table_index, ip6_punt_table_index, is_add, is_output);
214
215   switch (rv)
216     {
217     case 0:
218       break;
219
220     case VNET_API_ERROR_NO_MATCHING_INTERFACE:
221       return clib_error_return (0, "No such interface");
222
223     case VNET_API_ERROR_NO_SUCH_ENTRY:
224       return clib_error_return (0, "No such classifier table");
225
226     default:
227       return clib_error_return (0, "Error: %d", rv);
228     }
229   return 0;
230 }
231
232 static clib_error_t *
233 set_input_acl_command_fn (vlib_main_t * vm,
234                           unformat_input_t * input, vlib_cli_command_t * cmd)
235 {
236   return set_in_out_acl_command_fn (vm, input, cmd,
237                                     IN_OUT_ACL_INPUT_TABLE_GROUP);
238 }
239
240 static clib_error_t *
241 set_output_acl_command_fn (vlib_main_t * vm,
242                            unformat_input_t * input, vlib_cli_command_t * cmd)
243 {
244   return set_in_out_acl_command_fn (vm, input, cmd,
245                                     IN_OUT_ACL_OUTPUT_TABLE_GROUP);
246 }
247
248 /*
249  * Configure interface to enable/disble input/output ACL features:
250  * intfc - interface name to be configured as input ACL
251  * Ip4-table <index> [del] - enable/disable IP4 input ACL
252  * Ip6-table <index> [del] - enable/disable IP6 input ACL
253  * l2-table <index> [del] - enable/disable Layer2 input ACL
254  *
255  * Note: Only one table index per API call is allowed.
256  *
257  */
258 /* *INDENT-OFF* */
259 VLIB_CLI_COMMAND (set_input_acl_command, static) = {
260   .path = "set interface input acl",
261   .short_help =
262     "set interface input acl intfc <int> [ip4-table <index>]\n"
263     "  [ip6-table <index>] [l2-table <index>] [ip4-punt-table <index>]\n"
264     "  [ip6-punt-table <index> [del]",
265   .function = set_input_acl_command_fn,
266 };
267 VLIB_CLI_COMMAND (set_output_acl_command, static) = {
268     .path = "set interface output acl",
269     .short_help =
270     "set interface output acl intfc <int> [ip4-table <index>]\n"
271     "  [ip6-table <index>] [l2-table <index>] [del]",
272     .function = set_output_acl_command_fn,
273 };
274 /* *INDENT-ON* */
275
276 clib_error_t *
277 in_out_acl_init (vlib_main_t * vm)
278 {
279   in_out_acl_main_t *am = &in_out_acl_main;
280
281   am->vlib_main = vm;
282   am->vnet_main = vnet_get_main ();
283   am->vnet_classify_main = &vnet_classify_main;
284
285   return 0;
286 }
287 /* *INDENT-OFF* */
288 VLIB_INIT_FUNCTION (in_out_acl_init) =
289 {
290   .runs_after = VLIB_INITS("ip_in_out_acl_init"),
291 };
292 /* *INDENT-ON* */
293
294 uword
295 unformat_acl_type (unformat_input_t * input, va_list * args)
296 {
297   u32 *acl_type = va_arg (*args, u32 *);
298   u32 tid = IN_OUT_ACL_N_TABLES;
299
300   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
301     {
302       if (unformat (input, "ip4"))
303         tid = IN_OUT_ACL_TABLE_IP4;
304       else if (unformat (input, "ip6"))
305         tid = IN_OUT_ACL_TABLE_IP6;
306       else if (unformat (input, "l2"))
307         tid = IN_OUT_ACL_TABLE_L2;
308       else
309         break;
310     }
311
312   *acl_type = tid;
313   return 1;
314 }
315
316 u8 *
317 format_vnet_in_out_acl_info (u8 * s, va_list * va)
318 {
319   in_out_acl_main_t *am = va_arg (*va, in_out_acl_main_t *);
320   int sw_if_idx = va_arg (*va, int);
321   u32 tid = va_arg (*va, u32);
322
323   if (tid == ~0)
324     {
325       s = format (s, "%10s%20s\t\t%s", "Intfc idx", "Classify table",
326                   "Interface name");
327       return s;
328     }
329
330   s = format (s, "%10d%20d\t\t%U", sw_if_idx, tid,
331               format_vnet_sw_if_index_name, am->vnet_main, sw_if_idx);
332
333   return s;
334 }
335
336 static clib_error_t *
337 show_in_out_acl_command_fn (vlib_main_t * vm,
338                             unformat_input_t * input,
339                             vlib_cli_command_t * cmd, u32 is_output)
340 {
341   in_out_acl_main_t *am = &in_out_acl_main;
342   u32 type = IN_OUT_ACL_N_TABLES;
343   int i;
344   u32 *vec_tbl;
345
346   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
347     {
348       if (unformat (input, "type %U", unformat_acl_type, &type))
349         ;
350       else
351         break;
352     }
353
354   if (type == IN_OUT_ACL_N_TABLES)
355     return clib_error_return (0, is_output ? "Invalid output ACL table type."
356                               : "Invalid input ACL table type.");
357
358   vec_tbl = am->classify_table_index_by_sw_if_index[is_output][type];
359
360   if (vec_len (vec_tbl))
361     vlib_cli_output (vm, "%U", format_vnet_in_out_acl_info, am, ~0 /* hdr */ ,
362                      ~0);
363   else
364     vlib_cli_output (vm, is_output ? "No output ACL tables configured"
365                      : "No input ACL tables configured");
366
367   for (i = 0; i < vec_len (vec_tbl); i++)
368     {
369       if (vec_elt (vec_tbl, i) == ~0)
370         continue;
371
372       vlib_cli_output (vm, "%U", format_vnet_in_out_acl_info,
373                        am, i, vec_elt (vec_tbl, i));
374     }
375
376   return 0;
377 }
378
379 static clib_error_t *
380 show_inacl_command_fn (vlib_main_t * vm,
381                        unformat_input_t * input, vlib_cli_command_t * cmd)
382 {
383   return show_in_out_acl_command_fn (vm, input, cmd,
384                                      IN_OUT_ACL_INPUT_TABLE_GROUP);
385 }
386
387 static clib_error_t *
388 show_outacl_command_fn (vlib_main_t * vm,
389                         unformat_input_t * input, vlib_cli_command_t * cmd)
390 {
391   return show_in_out_acl_command_fn (vm, input, cmd,
392                                      IN_OUT_ACL_OUTPUT_TABLE_GROUP);
393 }
394
395 /* *INDENT-OFF* */
396 VLIB_CLI_COMMAND (show_inacl_command, static) = {
397     .path = "show inacl",
398     .short_help = "show inacl type [ip4|ip6|l2]",
399     .function = show_inacl_command_fn,
400 };
401 VLIB_CLI_COMMAND (show_outacl_command, static) = {
402     .path = "show outacl",
403     .short_help = "show outacl type [ip4|ip6|l2]",
404     .function = show_outacl_command_fn,
405 };
406 /* *INDENT-ON* */
407
408 /*
409  * fd.io coding-style-patch-verification: ON
410  *
411  * Local Variables:
412  * eval: (c-set-style "gnu")
413  * End:
414  */