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