2 * gbp.h : Group Based Policy
4 * Copyright (c) 2013 Cisco and/or its affiliates.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #include <plugins/gbp/gbp.h>
21 * IP4 destintion address to destination EPG mapping table
23 typedef struct gbp_ip4_to_epg_db_t_
26 * use a simple hash table
29 } gbp_ip4_to_epg_db_t;
31 static gbp_ip4_to_epg_db_t gbp_ip4_to_epg_db;
34 * IP6 destintion address to destination EPG mapping table
36 typedef struct gbp_ip6_to_epg_db_t_
39 * use a memroy hash table
42 } gbp_ip6_to_epg_db_t;
44 static gbp_ip6_to_epg_db_t gbp_ip6_to_epg_db;
47 * Result of a interface to EPG mapping.
48 * multiple Endpoints can occur on the same interface, so this
49 * mapping needs to be reference counted.
51 typedef struct gbp_itf_t_
57 const static gbp_itf_t ITF_INVALID = {
58 .gi_epg = EPG_INVALID,
63 * Interface to source EPG DB - a per-interface vector
65 typedef struct gbp_itf_to_epg_db_t_
68 } gbp_itf_to_epg_db_t;
70 static gbp_itf_to_epg_db_t gbp_itf_to_epg_db;
73 * Pool of GBP endpoints
75 static gbp_endpoint_t *gbp_endpoint_pool;
80 static uword *gbp_endpoint_db;
83 * EPG src,dst pair to ACL mapping table, aka contract DB
85 typedef struct gbp_contract_db_t_
88 * We can form a u64 key from the pair, so use a simple hash table
94 * Since contract DB instance
96 static gbp_contract_db_t gbp_contract_db;
99 gbp_ip_epg_update (const ip46_address_t * ip, epg_id_t epg_id)
102 * we are dealing only with addresses here so this limited
105 if (ip46_address_is_ip4 (ip))
107 hash_set (gbp_ip4_to_epg_db.g4ie_hash, ip->ip4.as_u32, epg_id);
111 hash_set_mem (gbp_ip6_to_epg_db.g6ie_hash, &ip->ip6, epg_id);
116 gbp_ip_epg_delete (const ip46_address_t * ip)
118 if (ip46_address_is_ip4 (ip))
120 hash_unset (gbp_ip4_to_epg_db.g4ie_hash, ip->ip4.as_u32);
124 hash_unset_mem (gbp_ip6_to_epg_db.g6ie_hash, &ip->ip6);
129 gbp_itf_epg_update (u32 sw_if_index, epg_id_t src_epg)
131 vec_validate_init_empty (gbp_itf_to_epg_db.gte_vec,
132 sw_if_index, ITF_INVALID);
134 if (0 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count)
136 vnet_feature_enable_disable ("ip4-unicast", "gbp4",
137 sw_if_index, 1, NULL, 0);
138 vnet_feature_enable_disable ("ip6-unicast", "gbp6",
139 sw_if_index, 1, NULL, 0);
141 gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = src_epg;
142 gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count++;
146 gbp_itf_epg_delete (u32 sw_if_index)
148 if (vec_len (gbp_itf_to_epg_db.gte_vec) <= sw_if_index)
151 if (1 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count)
153 gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = EPG_INVALID;
155 vnet_feature_enable_disable ("ip4-unicast", "gbp4",
156 sw_if_index, 0, NULL, 0);
157 vnet_feature_enable_disable ("ip6-unicast", "gbp6",
158 sw_if_index, 0, NULL, 0);
160 gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count--;
164 gbp_endpoint_update (u32 sw_if_index,
165 const ip46_address_t * ip, epg_id_t epg_id)
167 gbp_endpoint_key_t key = {
169 .gek_sw_if_index = sw_if_index,
171 gbp_endpoint_t *gbpe;
174 p = hash_get_mem (gbp_endpoint_db, &key);
178 gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
182 pool_get (gbp_endpoint_pool, gbpe);
184 gbpe->ge_key = clib_mem_alloc (sizeof (gbp_endpoint_key_t));
185 clib_memcpy (gbpe->ge_key, &key, sizeof (gbp_endpoint_key_t));
187 hash_set_mem (gbp_endpoint_db, gbpe->ge_key, gbpe - gbp_endpoint_pool);
190 gbpe->ge_epg_id = epg_id;
192 gbp_itf_epg_update (gbpe->ge_key->gek_sw_if_index, gbpe->ge_epg_id);
193 gbp_ip_epg_update (&gbpe->ge_key->gek_ip, gbpe->ge_epg_id);
197 gbp_endpoint_delete (u32 sw_if_index, const ip46_address_t * ip)
199 gbp_endpoint_key_t key = {
201 .gek_sw_if_index = sw_if_index,
203 gbp_endpoint_t *gbpe;
206 p = hash_get_mem (gbp_endpoint_db, &key);
210 gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
212 hash_unset_mem (gbp_endpoint_db, gbpe->ge_key);
214 gbp_itf_epg_delete (gbpe->ge_key->gek_sw_if_index);
215 gbp_ip_epg_delete (&gbpe->ge_key->gek_ip);
217 clib_mem_free (gbpe->ge_key);
219 pool_put (gbp_endpoint_pool, gbpe);
224 gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx)
226 gbp_endpoint_t *gbpe;
229 pool_foreach(gbpe, gbp_endpoint_pool,
238 gbp_contract_update (epg_id_t src_epg, epg_id_t dst_epg, u32 acl_index)
240 gbp_contract_key_t key = {
245 hash_set (gbp_contract_db.gc_hash, key.as_u64, acl_index);
249 gbp_contract_delete (epg_id_t src_epg, epg_id_t dst_epg)
251 gbp_contract_key_t key = {
256 hash_unset (gbp_contract_db.gc_hash, key.as_u64);
260 gbp_contract_walk (gbp_contract_cb_t cb, void *ctx)
262 gbp_contract_key_t key;
266 hash_foreach(key.as_u64, acl_index, gbp_contract_db.gc_hash,
268 gbp_contract_t gbpc = {
270 .gc_acl_index = acl_index,
279 static clib_error_t *
280 gbp_endpoint_cli (vlib_main_t * vm,
281 unformat_input_t * input, vlib_cli_command_t * cmd)
283 vnet_main_t *vnm = vnet_get_main ();
284 epg_id_t epg_id = EPG_INVALID;
285 ip46_address_t ip = { };
286 u32 sw_if_index = ~0;
289 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
291 if (unformat (input, "%U", unformat_vnet_sw_interface,
294 else if (unformat (input, "add"))
296 else if (unformat (input, "del"))
298 else if (unformat (input, "epg %d", &epg_id))
300 else if (unformat (input, "ip %U", unformat_ip4_address, &ip.ip4))
302 else if (unformat (input, "ip %U", unformat_ip6_address, &ip.ip6))
308 if (~0 == sw_if_index)
309 return clib_error_return (0, "interface must be specified");
310 if (EPG_INVALID == epg_id)
311 return clib_error_return (0, "EPG-ID must be specified");
312 if (ip46_address_is_zero (&ip))
313 return clib_error_return (0, "IP address must be specified");
316 gbp_endpoint_update (sw_if_index, &ip, epg_id);
318 gbp_endpoint_delete (sw_if_index, &ip);
323 static clib_error_t *
324 gbp_contract_cli (vlib_main_t * vm,
325 unformat_input_t * input, vlib_cli_command_t * cmd)
327 epg_id_t src_epg_id = EPG_INVALID, dst_epg_id = EPG_INVALID;
331 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
333 if (unformat (input, "add"))
335 else if (unformat (input, "del"))
337 else if (unformat (input, "src-epg %d", &src_epg_id))
339 else if (unformat (input, "dst-epg %d", &dst_epg_id))
341 else if (unformat (input, "acl-index %d", &acl_index))
347 if (EPG_INVALID == src_epg_id)
348 return clib_error_return (0, "Source EPG-ID must be specified");
349 if (EPG_INVALID == dst_epg_id)
350 return clib_error_return (0, "Destination EPG-ID must be specified");
354 gbp_contract_update (src_epg_id, dst_epg_id, acl_index);
358 gbp_contract_delete (src_epg_id, dst_epg_id);
365 * Configure a GBP Endpoint
368 * @cliexstart{set gbp endpoint [del] <interface> epg <ID> ip <IP>}
372 VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = {
373 .path = "gbp endpoint",
374 .short_help = "gbp endpoint [del] <interface> epg <ID> ip <IP>",
375 .function = gbp_endpoint_cli,
379 * Configure a GBP Contract
382 * @cliexstart{set gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>}
385 VLIB_CLI_COMMAND (gbp_contract_cli_node, static) = {
386 .path = "gbp contract",
387 .short_help = "gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>",
388 .function = gbp_contract_cli,
393 gbp_endpoint_show_one (gbp_endpoint_t * gbpe, void *ctx)
395 vnet_main_t *vnm = vnet_get_main ();
399 vlib_cli_output (vm, " {%U, %U} -> %d",
400 format_vnet_sw_if_index_name, vnm,
401 gbpe->ge_key->gek_sw_if_index,
402 format_ip46_address, &gbpe->ge_key->gek_ip, IP46_TYPE_ANY,
408 static clib_error_t *
409 gbp_endpoint_show (vlib_main_t * vm,
410 unformat_input_t * input, vlib_cli_command_t * cmd)
412 vnet_main_t *vnm = vnet_get_main ();
413 ip46_address_t ip, *ipp;
417 vlib_cli_output (vm, "Endpoints:");
418 gbp_endpoint_walk (gbp_endpoint_show_one, vm);
420 vlib_cli_output (vm, "\nSource interface to EPG:");
422 vec_foreach_index (sw_if_index, gbp_itf_to_epg_db.gte_vec)
424 if (EPG_INVALID != gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg)
426 vlib_cli_output (vm, " %U -> %d",
427 format_vnet_sw_if_index_name, vnm, sw_if_index,
428 gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg);
432 vlib_cli_output (vm, "\nDestination IP4 to EPG:");
435 hash_foreach (ip.ip4.as_u32, epg_id, gbp_ip4_to_epg_db.g4ie_hash,
437 vlib_cli_output (vm, " %U -> %d", format_ip46_address, &ip,
438 IP46_TYPE_IP4, epg_id);
442 vlib_cli_output (vm, "\nDestination IP6 to EPG:");
445 hash_foreach_mem (ipp, epg_id, gbp_ip6_to_epg_db.g6ie_hash,
447 vlib_cli_output (vm, " %U -> %d", format_ip46_address, ipp,
448 IP46_TYPE_IP6, epg_id);
455 static clib_error_t *
456 gbp_contract_show (vlib_main_t * vm,
457 unformat_input_t * input, vlib_cli_command_t * cmd)
459 gbp_contract_key_t key;
462 vlib_cli_output (vm, "Contracts:");
465 hash_foreach (key.as_u64, epg_id, gbp_contract_db.gc_hash,
467 vlib_cli_output (vm, " {%d,%d} -> %d", key.gck_src,
468 key.gck_dst, epg_id);
476 * Show Group Based Policy Endpoints and derived information
479 * @cliexstart{show gbp endpoint}
483 VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = {
484 .path = "show gbp endpoint",
485 .short_help = "show gbp endpoint\n",
486 .function = gbp_endpoint_show,
491 * Show Group Based Policy Contracts
494 * @cliexstart{show gbp contract}
498 VLIB_CLI_COMMAND (gbp_contract_show_node, static) = {
499 .path = "show gbp contract",
500 .short_help = "show gbp contract\n",
501 .function = gbp_contract_show,
505 #define foreach_gbp \
510 #define _(sym,str) GBP_ERROR_##sym,
516 static char *gbp_error_strings[] = {
517 #define _(sym,string) string,
524 #define _(sym,str) GBP_NEXT_##sym,
531 * per-packet trace data
533 typedef struct gbp_trace_t_
535 /* per-pkt trace data */
542 gbp_inline (vlib_main_t * vm,
543 vlib_node_runtime_t * node, vlib_frame_t * frame, int is_ip6)
545 u32 n_left_from, *from, *to_next;
546 gbp_next_t next_index;
549 n_left_from = frame->n_vectors;
550 from = vlib_frame_vector_args (frame);
552 while (n_left_from > 0)
556 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
558 while (n_left_from > 0 && n_left_to_next > 0)
566 gbp_contract_key_t key0;
577 /* deny by default */
578 next0 = GBP_NEXT_DENY;
580 b0 = vlib_get_buffer (vm, bi0);
582 ip6_0 = vlib_buffer_get_current (b0);
584 ip4_0 = vlib_buffer_get_current (b0);
585 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
588 * determine the src and dst EPG
590 key0.gck_src = gbp_itf_to_epg_db.gte_vec[sw_if_index0].gi_epg;
593 p = hash_get_mem (gbp_ip6_to_epg_db.g6ie_hash,
594 &ip6_0->dst_address);
596 p = hash_get (gbp_ip4_to_epg_db.g4ie_hash,
597 ip4_0->dst_address.as_u32);
604 * If the src and dst are the same, then let it through
606 if (key0.gck_dst == key0.gck_src)
608 vnet_feature_next (sw_if_index0, &next0, b0);
614 * find this src,dst pair in the egp->acl DB
616 p = hash_get (gbp_contract_db.gc_hash, key0.as_u64);
623 * the ACL index stored is NULL, this means any-any so let it pass
625 if (~0 == acl_index0)
627 vnet_feature_next (sw_if_index0, &next0, b0);
632 * TODO tests against the ACL
635 * ACL tables are not available outside of ACL plugin
636 * until then bypass the ACL to next node
638 vnet_feature_next (sw_if_index0, &next0, b0);
644 * no ACL to apply for packets between these two EPGs.
645 * GBP is a whitelist model, so no ACL implies deny, which
646 * is the default result
655 * cannot determine the destinaiotn EPG, so we cannot enforce policy
656 * on this node. permit.
658 vnet_feature_next (sw_if_index0, &next0, b0);
664 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
665 (b0->flags & VLIB_BUFFER_IS_TRACED)))
667 gbp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
668 t->src_epg = key0.gck_src;
669 t->dst_epg = key0.gck_dst;
670 t->acl_index = acl_index0;
673 /* verify speculative enqueue, maybe switch current next frame */
674 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
675 to_next, n_left_to_next,
679 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
682 return frame->n_vectors;
685 /* packet trace format function */
687 format_gbp_trace (u8 * s, va_list * args)
689 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
690 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
691 gbp_trace_t *t = va_arg (*args, gbp_trace_t *);
693 s = format (s, "gbp: src:%d dst:%d acl:%d",
694 t->src_epg, t->dst_epg, t->acl_index);
700 gbp_4 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
702 return (gbp_inline (vm, node, frame, 0));
706 gbp_6 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
708 return (gbp_inline (vm, node, frame, 1));
712 VLIB_REGISTER_NODE (gbp_4_node) = {
715 .vector_size = sizeof (u32),
716 .format_trace = format_gbp_trace,
717 .type = VLIB_NODE_TYPE_INTERNAL,
719 .n_errors = ARRAY_LEN(gbp_error_strings),
720 .error_strings = gbp_error_strings,
722 .n_next_nodes = GBP_N_NEXT,
725 [GBP_NEXT_DENY] = "ip4-drop",
729 VLIB_NODE_FUNCTION_MULTIARCH (gbp_4_node, gbp_4);
731 VNET_FEATURE_INIT (gbp_4_node, static) = {
732 .arc_name = "ip4-unicast",
734 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
737 VLIB_REGISTER_NODE (gbp_6_node) = {
740 .vector_size = sizeof (u32),
741 .format_trace = format_gbp_trace,
742 .type = VLIB_NODE_TYPE_INTERNAL,
744 .n_errors = ARRAY_LEN(gbp_error_strings),
745 .error_strings = gbp_error_strings,
747 .n_next_nodes = GBP_N_NEXT,
750 [GBP_NEXT_DENY] = "ip6-drop",
754 VLIB_NODE_FUNCTION_MULTIARCH (gbp_6_node, gbp_6);
756 VNET_FEATURE_INIT (gbp_6_node, static) = {
757 .arc_name = "ip6-unicast",
759 .runs_after = VNET_FEATURES ("acl-plugin-in-ip6-fa"),
763 static clib_error_t *
764 gbp_init (vlib_main_t * vm)
766 gbp_endpoint_db = hash_create_mem (0,
767 sizeof (gbp_endpoint_key_t),
769 gbp_ip6_to_epg_db.g6ie_hash =
770 hash_create_mem (0, sizeof (ip6_address_t), sizeof (u32));
774 VLIB_INIT_FUNCTION (gbp_init);
777 * fd.io coding-style-patch-verification: ON
780 * eval: (c-set-style "gnu")