From 2dd6852d8109e39d15a5c60f7ba58f1abcf9e455 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Thu, 16 Feb 2017 03:38:59 -0800 Subject: Consolidate DHCP v4 and V6 implementation. No functional change intended The DHCP proxy and VSS information maintained by VPP is the same for v4 and v6, so we can manage this state using the same code. Packet handling is cleary different, so this is kept separate. Change-Id: I10f10cc1f7f19debcd4c4b099c6de64e56bb0c69 Signed-off-by: Neale Ranns --- src/vnet/dhcp/dhcp_proxy.c | 275 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 src/vnet/dhcp/dhcp_proxy.c (limited to 'src/vnet/dhcp/dhcp_proxy.c') diff --git a/src/vnet/dhcp/dhcp_proxy.c b/src/vnet/dhcp/dhcp_proxy.c new file mode 100644 index 00000000..da2deea6 --- /dev/null +++ b/src/vnet/dhcp/dhcp_proxy.c @@ -0,0 +1,275 @@ +/* + * proxy_node.c: common dhcp v4 and v6 proxy node processing + * + * Copyright (c) 2013 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +/** + * @brief Shard 4/6 instance of DHCP main + */ +dhcp_proxy_main_t dhcp_proxy_main; + +void +dhcp_proxy_walk (fib_protocol_t proto, + dhcp_proxy_walk_fn_t fn, + void *ctx) +{ + dhcp_proxy_main_t * dpm = &dhcp_proxy_main; + dhcp_server_t * server; + u32 server_index, i; + + vec_foreach_index (i, dpm->dhcp_server_index_by_rx_fib_index[proto]) + { + server_index = dpm->dhcp_server_index_by_rx_fib_index[proto][i]; + if (~0 == server_index) + continue; + + server = pool_elt_at_index (dpm->dhcp_servers[proto], server_index); + + if (!fn(server, ctx)) + break; + } +} + +void +dhcp_vss_walk (fib_protocol_t proto, + dhcp_vss_walk_fn_t fn, + void *ctx) +{ + dhcp_proxy_main_t * dpm = &dhcp_proxy_main; + dhcp_vss_t * vss; + u32 vss_index, i; + fib_table_t *fib; + + + vec_foreach_index (i, dpm->vss_index_by_rx_fib_index[proto]) + { + vss_index = dpm->vss_index_by_rx_fib_index[proto][i]; + if (~0 == vss_index) + continue; + + vss = pool_elt_at_index (dpm->vss[proto], vss_index); + + fib = fib_table_get(i, proto); + + if (!fn(vss, fib->ft_table_id, ctx)) + break; + } +} + +int +dhcp_proxy_server_del (fib_protocol_t proto, + u32 rx_fib_index) +{ + dhcp_proxy_main_t * dpm = &dhcp_proxy_main; + dhcp_server_t * server = 0; + int rc = 0; + + server = dhcp_get_server(dpm, rx_fib_index, proto); + + if (NULL == server) + { + rc = VNET_API_ERROR_NO_SUCH_ENTRY; + } + else + { + /* Use the default server again. */ + dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] = ~0; + + fib_table_unlock (server->server_fib_index, proto); + + pool_put (dpm->dhcp_servers[proto], server); + } + + return (rc); +} + +int +dhcp_proxy_server_add (fib_protocol_t proto, + ip46_address_t *addr, + ip46_address_t *src_address, + u32 rx_fib_index, + u32 server_table_id) +{ + dhcp_proxy_main_t * dpm = &dhcp_proxy_main; + dhcp_server_t * server = 0; + int new = 0; + + server = dhcp_get_server(dpm, rx_fib_index, proto); + + if (NULL == server) + { + vec_validate_init_empty(dpm->dhcp_server_index_by_rx_fib_index[proto], + rx_fib_index, + ~0); + + pool_get (dpm->dhcp_servers[proto], server); + memset (server, 0, sizeof (*server)); + new = 1; + + dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] = + server - dpm->dhcp_servers[proto]; + + server->rx_fib_index = rx_fib_index; + server->server_fib_index = + fib_table_find_or_create_and_lock(proto, server_table_id); + } + else + { + /* modify, may need to swap server FIBs */ + u32 tmp_index; + + tmp_index = fib_table_find(proto, server_table_id); + + if (tmp_index != server->server_fib_index) + { + tmp_index = server->server_fib_index; + + /* certainly swapping if the fib doesn't exist */ + server->server_fib_index = + fib_table_find_or_create_and_lock(proto, server_table_id); + fib_table_unlock (tmp_index, proto); + } + } + + server->dhcp_server = *addr; + server->dhcp_src_address = *src_address; + + return (new); +} + +typedef struct dhcp4_proxy_dump_walk_ctx_t_ +{ + fib_protocol_t proto; + void *opaque; + u32 context; +} dhcp_proxy_dump_walk_cxt_t; + +static int +dhcp_proxy_dump_walk (dhcp_server_t *server, + void *arg) +{ + dhcp_proxy_dump_walk_cxt_t *ctx = arg; + fib_table_t *s_fib, *r_fib; + dhcp_vss_t *v; + + v = dhcp_get_vss_info(&dhcp_proxy_main, + server->rx_fib_index, + ctx->proto); + + s_fib = fib_table_get(server->server_fib_index, ctx->proto); + r_fib = fib_table_get(server->rx_fib_index, ctx->proto); + + dhcp_send_details(ctx->proto, + ctx->opaque, + ctx->context, + &server->dhcp_server, + &server->dhcp_src_address, + s_fib->ft_table_id, + r_fib->ft_table_id, + (v ? v->fib_id : 0), + (v ? v->oui : 0)); + + return (1); +} + +void +dhcp_proxy_dump (fib_protocol_t proto, + void *opaque, + u32 context) +{ + dhcp_proxy_dump_walk_cxt_t ctx = { + .proto = proto, + .opaque = opaque, + .context = context, + }; + dhcp_proxy_walk(proto, dhcp_proxy_dump_walk, &ctx); +} + +int +dhcp_vss_show_walk (dhcp_vss_t *vss, + u32 rx_table_id, + void *ctx) +{ + vlib_main_t * vm = ctx; + + vlib_cli_output (vm, "%=6d%=6d%=12d", + rx_table_id, + vss->oui, + vss->fib_id); + + return (1); +} + +int dhcp_proxy_set_vss (fib_protocol_t proto, + u32 tbl_id, + u32 oui, + u32 fib_id, + int is_del) +{ + dhcp_proxy_main_t *dm = &dhcp_proxy_main; + dhcp_vss_t *v = NULL; + u32 rx_fib_index; + int rc = 0; + + rx_fib_index = fib_table_find_or_create_and_lock(proto, tbl_id); + v = dhcp_get_vss_info(dm, rx_fib_index, proto); + + if (NULL != v) + { + if (is_del) + { + /* release the lock held on the table when the VSS + * info was created */ + fib_table_unlock (rx_fib_index, proto); + + pool_put (dm->vss[proto], v); + dm->vss_index_by_rx_fib_index[proto][rx_fib_index] = ~0; + } + else + { + /* this is a modify */ + v->fib_id = fib_id; + v->oui = oui; + } + } + else + { + if (is_del) + rc = VNET_API_ERROR_NO_SUCH_ENTRY; + else + { + /* create a new entry */ + vec_validate_init_empty(dm->vss_index_by_rx_fib_index[proto], + rx_fib_index, ~0); + + /* hold a lock on the table whilst the VSS info exist */ + fib_table_lock (rx_fib_index, proto); + + pool_get (dm->vss[proto], v); + v->fib_id = fib_id; + v->oui = oui; + dm->vss_index_by_rx_fib_index[proto][rx_fib_index] = + v - dm->vss[proto]; + } + } + + /* Release the lock taken during the create_or_lock at the start */ + fib_table_unlock (rx_fib_index, proto); + + return (rc); +} -- cgit 1.2.3-korg From 358425b86cb231efd2330e5aeb077cad38c2efad Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Mon, 20 Feb 2017 09:42:36 -0800 Subject: DHCPv6 - Be consistent with the use of MFIB index as the RX FIB index for DHCPv6. For the same table ID, the unicast-FIB index is not necessarily the same value as the multicast-FIB index, since features (like LISP, SR) can create unicast-tables, and thus affect only the index of the unicast FIBs Change-Id: Ibfa334d7eda822f742c241b7ce69a6271b4753a9 Signed-off-by: Neale Ranns --- src/vnet/dhcp/dhcp6_proxy_node.c | 9 +++-- src/vnet/dhcp/dhcp_proxy.c | 84 +++++++++++++++++++++++++++++++++------- src/vnet/dhcp/dhcp_proxy.h | 1 - src/vnet/interface_api.c | 4 +- 4 files changed, 78 insertions(+), 20 deletions(-) (limited to 'src/vnet/dhcp/dhcp_proxy.c') diff --git a/src/vnet/dhcp/dhcp6_proxy_node.c b/src/vnet/dhcp/dhcp6_proxy_node.c index ed44977d..58674209 100644 --- a/src/vnet/dhcp/dhcp6_proxy_node.c +++ b/src/vnet/dhcp/dhcp6_proxy_node.c @@ -226,7 +226,7 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm, /* Send to DHCPV6 server via the configured FIB */ rx_sw_if_index = sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - rx_fib_idx = im->fib_index_by_sw_if_index [rx_sw_if_index]; + rx_fib_idx = im->mfib_index_by_sw_if_index [rx_sw_if_index]; server = dhcp_get_server(dpm, rx_fib_idx, FIB_PROTOCOL_IP6); if (PREDICT_FALSE (NULL == server)) @@ -587,7 +587,7 @@ dhcpv6_proxy_to_client_input (vlib_main_t * vm, //Advance buffer to start of encapsulated DHCPv6 message vlib_buffer_advance (b0, sizeof(*r0)); - client_fib_idx = im->fib_index_by_sw_if_index[sw_if_index]; + client_fib_idx = im->mfib_index_by_sw_if_index[sw_if_index]; server = dhcp_get_server(dm, client_fib_idx, FIB_PROTOCOL_IP6); if (NULL == server) @@ -894,7 +894,8 @@ static u8 * format_dhcp6_proxy_server (u8 * s, va_list * args) { dhcp_server_t * server = va_arg (*args, dhcp_server_t *); - ip6_fib_t * rx_fib, * server_fib; + ip6_fib_t *server_fib; + ip6_mfib_t *rx_fib; if (NULL == server) { @@ -904,7 +905,7 @@ format_dhcp6_proxy_server (u8 * s, va_list * args) } server_fib = ip6_fib_get(server->server_fib_index); - rx_fib = ip6_fib_get(server->rx_fib_index); + rx_fib = ip6_mfib_get(server->rx_fib_index); s = format (s, "%=40U%=40U%=14u%=14u", diff --git a/src/vnet/dhcp/dhcp_proxy.c b/src/vnet/dhcp/dhcp_proxy.c index da2deea6..8e31c3db 100644 --- a/src/vnet/dhcp/dhcp_proxy.c +++ b/src/vnet/dhcp/dhcp_proxy.c @@ -17,12 +17,55 @@ #include #include +#include /** * @brief Shard 4/6 instance of DHCP main */ dhcp_proxy_main_t dhcp_proxy_main; +static void +dhcp_proxy_rx_table_lock (fib_protocol_t proto, + u32 fib_index) +{ + if (FIB_PROTOCOL_IP4 == proto) + fib_table_lock(fib_index, proto); + else + mfib_table_lock(fib_index, proto); +} + +static void +dhcp_proxy_rx_table_unlock (fib_protocol_t proto, + u32 fib_index) +{ + if (FIB_PROTOCOL_IP4 == proto) + fib_table_unlock(fib_index, proto); + else + mfib_table_unlock(fib_index, proto); +} + +static u32 +dhcp_proxy_rx_table_get_table_id (fib_protocol_t proto, + u32 fib_index) +{ + if (FIB_PROTOCOL_IP4 == proto) + { + fib_table_t *fib; + + fib = fib_table_get(fib_index, proto); + + return (fib->ft_table_id); + } + else + { + mfib_table_t *mfib; + + mfib = mfib_table_get(fib_index, proto); + + return (mfib->mft_table_id); + } +} + void dhcp_proxy_walk (fib_protocol_t proto, dhcp_proxy_walk_fn_t fn, @@ -51,11 +94,11 @@ dhcp_vss_walk (fib_protocol_t proto, void *ctx) { dhcp_proxy_main_t * dpm = &dhcp_proxy_main; + mfib_table_t *mfib; dhcp_vss_t * vss; u32 vss_index, i; fib_table_t *fib; - vec_foreach_index (i, dpm->vss_index_by_rx_fib_index[proto]) { vss_index = dpm->vss_index_by_rx_fib_index[proto][i]; @@ -64,10 +107,20 @@ dhcp_vss_walk (fib_protocol_t proto, vss = pool_elt_at_index (dpm->vss[proto], vss_index); - fib = fib_table_get(i, proto); + if (FIB_PROTOCOL_IP4 == proto) + { + fib = fib_table_get(i, proto); - if (!fn(vss, fib->ft_table_id, ctx)) - break; + if (!fn(vss, fib->ft_table_id, ctx)) + break; + } + else + { + mfib = mfib_table_get(i, proto); + + if (!fn(vss, mfib->mft_table_id, ctx)) + break; + } } } @@ -164,7 +217,8 @@ dhcp_proxy_dump_walk (dhcp_server_t *server, void *arg) { dhcp_proxy_dump_walk_cxt_t *ctx = arg; - fib_table_t *s_fib, *r_fib; + fib_table_t *s_fib; + u32 rx_table_id; dhcp_vss_t *v; v = dhcp_get_vss_info(&dhcp_proxy_main, @@ -172,7 +226,8 @@ dhcp_proxy_dump_walk (dhcp_server_t *server, ctx->proto); s_fib = fib_table_get(server->server_fib_index, ctx->proto); - r_fib = fib_table_get(server->rx_fib_index, ctx->proto); + rx_table_id = dhcp_proxy_rx_table_get_table_id(server->rx_fib_index, + ctx->proto); dhcp_send_details(ctx->proto, ctx->opaque, @@ -180,7 +235,7 @@ dhcp_proxy_dump_walk (dhcp_server_t *server, &server->dhcp_server, &server->dhcp_src_address, s_fib->ft_table_id, - r_fib->ft_table_id, + rx_table_id, (v ? v->fib_id : 0), (v ? v->oui : 0)); @@ -226,7 +281,10 @@ int dhcp_proxy_set_vss (fib_protocol_t proto, u32 rx_fib_index; int rc = 0; - rx_fib_index = fib_table_find_or_create_and_lock(proto, tbl_id); + if (proto == FIB_PROTOCOL_IP4) + rx_fib_index = fib_table_find_or_create_and_lock(proto, tbl_id); + else + rx_fib_index = mfib_table_find_or_create_and_lock(proto, tbl_id); v = dhcp_get_vss_info(dm, rx_fib_index, proto); if (NULL != v) @@ -235,7 +293,7 @@ int dhcp_proxy_set_vss (fib_protocol_t proto, { /* release the lock held on the table when the VSS * info was created */ - fib_table_unlock (rx_fib_index, proto); + dhcp_proxy_rx_table_unlock (proto, rx_fib_index); pool_put (dm->vss[proto], v); dm->vss_index_by_rx_fib_index[proto][rx_fib_index] = ~0; @@ -258,18 +316,18 @@ int dhcp_proxy_set_vss (fib_protocol_t proto, rx_fib_index, ~0); /* hold a lock on the table whilst the VSS info exist */ - fib_table_lock (rx_fib_index, proto); - pool_get (dm->vss[proto], v); v->fib_id = fib_id; v->oui = oui; + dm->vss_index_by_rx_fib_index[proto][rx_fib_index] = v - dm->vss[proto]; + dhcp_proxy_rx_table_lock (proto, rx_fib_index); } } /* Release the lock taken during the create_or_lock at the start */ - fib_table_unlock (rx_fib_index, proto); - + dhcp_proxy_rx_table_unlock (proto, rx_fib_index); + return (rc); } diff --git a/src/vnet/dhcp/dhcp_proxy.h b/src/vnet/dhcp/dhcp_proxy.h index 4586d883..708e92f3 100644 --- a/src/vnet/dhcp/dhcp_proxy.h +++ b/src/vnet/dhcp/dhcp_proxy.h @@ -104,7 +104,6 @@ typedef struct { /* hash lookup specific vrf_id -> option 82 vss suboption */ u32 *vss_index_by_rx_fib_index[DHCP_N_PROTOS]; - } dhcp_proxy_main_t; extern dhcp_proxy_main_t dhcp_proxy_main; diff --git a/src/vnet/interface_api.c b/src/vnet/interface_api.c index f94928b6..bfd2af31 100644 --- a/src/vnet/interface_api.c +++ b/src/vnet/interface_api.c @@ -29,6 +29,7 @@ #include #include #include +#include #define vl_typedefs /* define message structures */ #include @@ -340,9 +341,9 @@ vl_api_sw_interface_set_table_t_handler (vl_api_sw_interface_set_table_t * mp) fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, table_id); - vec_validate (ip6_main.fib_index_by_sw_if_index, sw_if_index); ip6_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; + fib_index = mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, table_id); vec_validate (ip6_main.mfib_index_by_sw_if_index, sw_if_index); @@ -362,7 +363,6 @@ vl_api_sw_interface_set_table_t_handler (vl_api_sw_interface_set_table_t * mp) fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, table_id); - vec_validate (ip4_main.fib_index_by_sw_if_index, sw_if_index); ip4_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; -- cgit 1.2.3-korg From 3466c30261950823828d1dad0d2fb170ee2f9aaf Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Thu, 16 Feb 2017 07:45:03 -0800 Subject: DHCP Multiple Servers (VPP-602, VPP-605) Multiple DHCP (4 and/or 6) servers can be added and removed through multiple calls to the 'set dhcp server' API. All 4/6/ discover/solicit messages will then be replicated to all servers in the list. The expectation is that the servers/system is configured in such a way that this is viable. If VSS information is providied for the clinet VRF which also has multiple servers configured, then the same VSS information is sent to each server. Likewise the source address of packets sent to from VPP to each server is the same. Change-Id: I3287cb084c84b3f612b78bc69cfcb5b9c1f8934d Signed-off-by: Neale Ranns --- src/scripts/vnet/dhcp/proxy | 3 +- src/vat/api_format.c | 56 ++++-- src/vnet/dhcp/client.c | 2 +- src/vnet/dhcp/dhcp.api | 12 +- src/vnet/dhcp/dhcp4_packet.h | 5 + src/vnet/dhcp/dhcp4_proxy_node.c | 151 +++++++++++++---- src/vnet/dhcp/dhcp6_proxy_node.c | 158 ++++++++++++----- src/vnet/dhcp/dhcp_api.c | 66 +++++--- src/vnet/dhcp/dhcp_proxy.c | 129 +++++++------- src/vnet/dhcp/dhcp_proxy.h | 85 +++++++--- src/vnet/ip/ip6_packet.h | 2 + src/vnet/pg/input.c | 6 + test/test_dhcp.py | 356 ++++++++++++++++++++++++++++++++++----- 13 files changed, 796 insertions(+), 235 deletions(-) (limited to 'src/vnet/dhcp/dhcp_proxy.c') diff --git a/src/scripts/vnet/dhcp/proxy b/src/scripts/vnet/dhcp/proxy index c709d87d..42dff2a0 100644 --- a/src/scripts/vnet/dhcp/proxy +++ b/src/scripts/vnet/dhcp/proxy @@ -14,7 +14,8 @@ set int ip addr loop0 2001::1/64 set int ip addr loop0 2001:1::1/64 set dhcp proxy server 10.255.0.1 src-address 10.0.0.1 server-fib-id 0 rx-fib-id 0 -set dhcp proxy server 10.255.0.2 src-address 10.0.1.1 server-fib-id 1 rx-fib-id 1 +set dhcp proxy server 10.255.0.2 src-address 10.0.0.1 server-fib-id 0 rx-fib-id 0 +set dhcp proxy server 10.255.1.2 src-address 10.0.1.1 server-fib-id 1 rx-fib-id 1 set dhcpv6 proxy server 3001::1 src-address 2001::1 server-fib-id 0 rx-fib-id 0 set dhcpv6 proxy server 3002::1 src-address 2001:1::1 server-fib-id 1 rx-fib-id 1 diff --git a/src/vat/api_format.c b/src/vat/api_format.c index 0b60b910..b5943f03 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -7573,23 +7573,35 @@ static void vl_api_dhcp_proxy_details_t_handler (vl_api_dhcp_proxy_details_t * mp) { vat_main_t *vam = &vat_main; + u32 i, count = mp->count; + vl_api_dhcp_server_t *s; if (mp->is_ipv6) print (vam->ofp, - "RX Table-ID %d, Server Table-ID %d, Server Address %U, Source Address %U, VSS FIB-ID %d, VSS OUI %d", + "RX Table-ID %d, Source Address %U, VSS FIB-ID %d, VSS OUI %d", ntohl (mp->rx_vrf_id), - ntohl (mp->server_vrf_id), - format_ip6_address, mp->dhcp_server, format_ip6_address, mp->dhcp_src_address, ntohl (mp->vss_oui), ntohl (mp->vss_fib_id)); else print (vam->ofp, - "RX Table-ID %d, Server Table-ID %d, Server Address %U, Source Address %U, VSS FIB-ID %d, VSS OUI %d", + "RX Table-ID %d, Source Address %U, VSS FIB-ID %d, VSS OUI %d", ntohl (mp->rx_vrf_id), - ntohl (mp->server_vrf_id), - format_ip4_address, mp->dhcp_server, format_ip4_address, mp->dhcp_src_address, ntohl (mp->vss_oui), ntohl (mp->vss_fib_id)); + + for (i = 0; i < count; i++) + { + s = &mp->servers[i]; + + if (mp->is_ipv6) + print (vam->ofp, + " Server Table-ID %d, Server Address %U", + ntohl (s->server_vrf_id), format_ip6_address, s->dhcp_server); + else + print (vam->ofp, + " Server Table-ID %d, Server Address %U", + ntohl (s->server_vrf_id), format_ip4_address, s->dhcp_server); + } } static void vl_api_dhcp_proxy_details_t_handler_json @@ -7597,8 +7609,10 @@ static void vl_api_dhcp_proxy_details_t_handler_json { vat_main_t *vam = &vat_main; vat_json_node_t *node = NULL; + u32 i, count = mp->count; struct in_addr ip4; struct in6_addr ip6; + vl_api_dhcp_server_t *s; if (VAT_JSON_ARRAY != vam->json_tree.type) { @@ -7609,24 +7623,38 @@ static void vl_api_dhcp_proxy_details_t_handler_json vat_json_init_object (node); vat_json_object_add_uint (node, "rx-table-id", ntohl (mp->rx_vrf_id)); - vat_json_object_add_uint (node, "server-table-id", - ntohl (mp->server_vrf_id)); + vat_json_object_add_uint (node, "vss-fib-id", ntohl (mp->vss_fib_id)); + vat_json_object_add_uint (node, "vss-oui", ntohl (mp->vss_oui)); + if (mp->is_ipv6) { - clib_memcpy (&ip6, &mp->dhcp_server, sizeof (ip6)); - vat_json_object_add_ip6 (node, "server_address", ip6); clib_memcpy (&ip6, &mp->dhcp_src_address, sizeof (ip6)); vat_json_object_add_ip6 (node, "src_address", ip6); } else { - clib_memcpy (&ip4, &mp->dhcp_server, sizeof (ip4)); - vat_json_object_add_ip4 (node, "server_address", ip4); clib_memcpy (&ip4, &mp->dhcp_src_address, sizeof (ip4)); vat_json_object_add_ip4 (node, "src_address", ip4); } - vat_json_object_add_uint (node, "vss-fib-id", ntohl (mp->vss_fib_id)); - vat_json_object_add_uint (node, "vss-oui", ntohl (mp->vss_oui)); + + for (i = 0; i < count; i++) + { + s = &mp->servers[i]; + + vat_json_object_add_uint (node, "server-table-id", + ntohl (s->server_vrf_id)); + + if (mp->is_ipv6) + { + clib_memcpy (&ip4, &s->dhcp_server, sizeof (ip4)); + vat_json_object_add_ip4 (node, "src_address", ip4); + } + else + { + clib_memcpy (&ip6, &s->dhcp_server, sizeof (ip6)); + vat_json_object_add_ip6 (node, "server_address", ip6); + } + } } static int diff --git a/src/vnet/dhcp/client.c b/src/vnet/dhcp/client.c index d34c5a64..29749a33 100644 --- a/src/vnet/dhcp/client.c +++ b/src/vnet/dhcp/client.c @@ -366,7 +366,7 @@ send_dhcp_pkt (dhcp_client_main_t * dcm, dhcp_client_t * c, o = (dhcp_option_t * )dhcp->options; /* Send option 53, the DHCP message type */ - o->option = 53; + o->option = DHCP_PACKET_OPTION_MSG_TYPE; o->length = 1; o->data[0] = type; o = (dhcp_option_t *) (((uword) o) + (o->length + 2)); diff --git a/src/vnet/dhcp/dhcp.api b/src/vnet/dhcp/dhcp.api index 8daadd8c..2db85a79 100644 --- a/src/vnet/dhcp/dhcp.api +++ b/src/vnet/dhcp/dhcp.api @@ -137,19 +137,25 @@ define dhcp_proxy_dump u8 is_ip6; }; +typeonly manual_print manual_endian define dhcp_server +{ + u32 server_vrf_id; + u8 dhcp_server[16]; +}; + /** \brief Tell client about a DHCP completion event @param client_index - opaque cookie to identify the sender */ -define dhcp_proxy_details +manual_endian manual_print define dhcp_proxy_details { u32 context; u32 rx_vrf_id; - u32 server_vrf_id; u32 vss_oui; u32 vss_fib_id; u8 is_ipv6; - u8 dhcp_server[16]; u8 dhcp_src_address[16]; + u8 count; + vl_api_dhcp_server_t servers[count]; }; /* diff --git a/src/vnet/dhcp/dhcp4_packet.h b/src/vnet/dhcp/dhcp4_packet.h index 28c4b156..07829f48 100644 --- a/src/vnet/dhcp/dhcp4_packet.h +++ b/src/vnet/dhcp/dhcp4_packet.h @@ -55,6 +55,11 @@ typedef enum { DHCP_PACKET_ACK=5, } dhcp_packet_type_t; +typedef enum dhcp_packet_option_t_ +{ + DHCP_PACKET_OPTION_MSG_TYPE = 53, +} dhcp_packet_option_t; + /* charming antique: 99.130.83.99 is the dhcp magic cookie */ #define DHCP_MAGIC (clib_host_to_net_u32(0x63825363)) diff --git a/src/vnet/dhcp/dhcp4_proxy_node.c b/src/vnet/dhcp/dhcp4_proxy_node.c index 88a99249..1c84881a 100644 --- a/src/vnet/dhcp/dhcp4_proxy_node.c +++ b/src/vnet/dhcp/dhcp4_proxy_node.c @@ -135,18 +135,17 @@ dhcp_proxy_to_server_input (vlib_main_t * vm, u32 original_sw_if_index = 0; u8 *end = NULL; u32 fib_index; - dhcp_server_t * server; + dhcp_proxy_t *proxy; + dhcp_server_t *server; u32 rx_sw_if_index; dhcp_option_t *o; u32 len = 0; vlib_buffer_free_list_t *fl; + u8 is_discover = 0; bi0 = from[0]; - to_next[0] = bi0; from += 1; - to_next += 1; n_left_from -= 1; - n_left_to_next -= 1; b0 = vlib_get_buffer (vm, bi0); @@ -172,16 +171,17 @@ dhcp_proxy_to_server_input (vlib_main_t * vm, rx_sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_RX]; fib_index = im->fib_index_by_sw_if_index [rx_sw_if_index]; - server = dhcp_get_server(dpm, fib_index, FIB_PROTOCOL_IP4); - - if (PREDICT_FALSE (NULL == server)) + proxy = dhcp_get_proxy(dpm, fib_index, FIB_PROTOCOL_IP4); + + if (PREDICT_FALSE (NULL == proxy)) { error0 = DHCP_PROXY_ERROR_NO_SERVER; next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_DROP; pkts_no_server++; goto do_trace; } - + + server = &proxy->dhcp_servers[0]; vlib_buffer_advance (b0, -(sizeof(*ip0))); ip0 = vlib_buffer_get_current (b0); @@ -198,7 +198,7 @@ dhcp_proxy_to_server_input (vlib_main_t * vm, sum0 = ip0->checksum; old0 = ip0->src_address.as_u32; - new0 = server->dhcp_src_address.ip4.as_u32; + new0 = proxy->dhcp_src_address.ip4.as_u32; ip0->src_address.as_u32 = new0; sum0 = ip_csum_update (sum0, old0, new0, ip4_header_t /* structure */, @@ -209,7 +209,7 @@ dhcp_proxy_to_server_input (vlib_main_t * vm, vnet_buffer(b0)->sw_if_index[VLIB_TX] = server->server_fib_index; - h0->gateway_ip_address.as_u32 = server->dhcp_src_address.ip4.as_u32; + h0->gateway_ip_address.as_u32 = proxy->dhcp_src_address.ip4.as_u32; pkts_to_server++; o = (dhcp_option_t *) h0->options; @@ -220,7 +220,16 @@ dhcp_proxy_to_server_input (vlib_main_t * vm, end = b0->data + b0->current_data + b0->current_length; /* TLVs are not performance-friendly... */ while (o->option != 0xFF /* end of options */ && (u8 *)o < end) + { + if (DHCP_PACKET_OPTION_MSG_TYPE == o->option) + { + if (DHCP_PACKET_DISCOVER == o->data[0]) + { + is_discover = 1; + } + } o = (dhcp_option_t *) (((uword) o) + (o->length + 2)); + } fl = vlib_buffer_get_free_list (vm, b0->free_list_index); // start write at (option*)o, some packets have padding @@ -340,6 +349,65 @@ dhcp_proxy_to_server_input (vlib_main_t * vm, next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP; + /* + * If we have multiple servers configured and this is the + * client's discover message, then send copies to each of + * those servers + */ + if (is_discover && vec_len(proxy->dhcp_servers) > 1) + { + u32 ii; + + for (ii = 1; ii < vec_len(proxy->dhcp_servers); ii++) + { + vlib_buffer_t *c0; + u32 ci0; + + c0 = vlib_buffer_copy(vm, b0); + ci0 = vlib_get_buffer_index(vm, c0); + server = &proxy->dhcp_servers[ii]; + + ip0 = vlib_buffer_get_current (c0); + + sum0 = ip0->checksum; + old0 = ip0->dst_address.as_u32; + new0 = server->dhcp_server.ip4.as_u32; + ip0->dst_address.as_u32 = server->dhcp_server.ip4.as_u32; + sum0 = ip_csum_update (sum0, old0, new0, + ip4_header_t /* structure */, + dst_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + to_next[0] = ci0; + to_next += 1; + n_left_to_next -= 1; + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + ci0, next0); + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + dhcp_proxy_trace_t *tr; + + tr = vlib_add_trace (vm, node, c0, sizeof (*tr)); + tr->which = 0; /* to server */ + tr->error = error0; + tr->original_sw_if_index = original_sw_if_index; + tr->sw_if_index = sw_if_index; + if (next0 == DHCP_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP) + tr->trace_ip4_address.as_u32 = server->dhcp_server.ip4.as_u32; + } + + if (PREDICT_FALSE(0 == n_left_to_next)) + { + vlib_put_next_frame (vm, node, next_index, + n_left_to_next); + vlib_get_next_frame (vm, node, next_index, + to_next, n_left_to_next); + } + } + } do_trace: if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) { @@ -350,10 +418,15 @@ dhcp_proxy_to_server_input (vlib_main_t * vm, tr->original_sw_if_index = original_sw_if_index; tr->sw_if_index = sw_if_index; if (next0 == DHCP_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP) - tr->trace_ip4_address.as_u32 = server->dhcp_server.ip4.as_u32; + tr->trace_ip4_address.as_u32 = + proxy->dhcp_servers[0].dhcp_server.ip4.as_u32; } do_enqueue: + to_next[0] = bi0; + to_next += 1; + n_left_to_next -= 1; + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, bi0, next0); @@ -437,7 +510,8 @@ dhcp_proxy_to_client_input (vlib_main_t * vm, u32 error0 = (u32)~0; vnet_sw_interface_t *swif; u32 fib_index; - dhcp_server_t * server; + dhcp_proxy_t *proxy; + dhcp_server_t *server; u32 original_sw_if_index = (u32) ~0; ip4_address_t relay_addr = { .as_u32 = 0, @@ -547,20 +621,26 @@ dhcp_proxy_to_client_input (vlib_main_t * vm, } fib_index = im->fib_index_by_sw_if_index [sw_if_index]; - server = dhcp_get_server(dpm, fib_index, FIB_PROTOCOL_IP4); + proxy = dhcp_get_proxy(dpm, fib_index, FIB_PROTOCOL_IP4); - if (PREDICT_FALSE (NULL == server)) + if (PREDICT_FALSE (NULL == proxy)) { error0 = DHCP_PROXY_ERROR_NO_SERVER; goto drop_packet; } - if (ip0->src_address.as_u32 != server->dhcp_server.ip4.as_u32) - { - error0 = DHCP_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS; - goto drop_packet; + vec_foreach(server, proxy->dhcp_servers) + { + if (ip0->src_address.as_u32 == server->dhcp_server.ip4.as_u32) + { + goto server_found; + } } + error0 = DHCP_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS; + goto drop_packet; + + server_found: vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index; swif = vnet_get_sw_interface (vnm, sw_if_index); @@ -709,9 +789,8 @@ dhcp4_proxy_set_server (ip46_address_t *addr, if (is_del) { - rc = dhcp_proxy_server_del (FIB_PROTOCOL_IP4, rx_fib_index); - - if (0 == rc) + if (dhcp_proxy_server_del (FIB_PROTOCOL_IP4, rx_fib_index, + addr, server_table_id)) { fib_table_entry_special_remove(rx_fib_index, &all_1s, @@ -809,29 +888,35 @@ VLIB_CLI_COMMAND (dhcp_proxy_set_command, static) = { static u8 * format_dhcp4_proxy_server (u8 * s, va_list * args) { - dhcp_server_t * server = va_arg (*args, dhcp_server_t *); + dhcp_proxy_t *proxy = va_arg (*args, dhcp_proxy_t *); ip4_fib_t * rx_fib, * server_fib; + dhcp_server_t *server; - if (server == 0) + if (proxy == 0) { - s = format (s, "%=16s%=16s%=14s%=14s", "Server", "Src Address", - "Server FIB", "RX FIB"); + s = format (s, "%=14s%=16s%s", "RX FIB", "Src Address", + "Servers FIB,Address"); return s; } - server_fib = ip4_fib_get(server->server_fib_index); - rx_fib = ip4_fib_get(server->rx_fib_index); + rx_fib = ip4_fib_get(proxy->rx_fib_index); + + s = format (s, "%=14u%=16U", + rx_fib->table_id, + format_ip46_address, &proxy->dhcp_src_address, IP46_TYPE_ANY); - s = format (s, "%=16U%=16U%=14u%=14u", - format_ip46_address, &server->dhcp_server, IP46_TYPE_ANY, - format_ip46_address, &server->dhcp_src_address, IP46_TYPE_ANY, - server_fib->table_id, - rx_fib->table_id); + vec_foreach(server, proxy->dhcp_servers) + { + server_fib = ip4_fib_get(server->server_fib_index); + s = format (s, "%u,%U ", + server_fib->table_id, + format_ip46_address, &server->dhcp_server, IP46_TYPE_ANY); + } return s; } static int -dhcp4_proxy_show_walk (dhcp_server_t *server, +dhcp4_proxy_show_walk (dhcp_proxy_t *server, void *ctx) { vlib_main_t * vm = ctx; diff --git a/src/vnet/dhcp/dhcp6_proxy_node.c b/src/vnet/dhcp/dhcp6_proxy_node.c index 58674209..524cb095 100644 --- a/src/vnet/dhcp/dhcp6_proxy_node.c +++ b/src/vnet/dhcp/dhcp6_proxy_node.c @@ -140,7 +140,8 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm, ip6_main_t * im = &ip6_main; ip6_address_t * src; int bogus_length; - dhcp_server_t * server; + dhcp_proxy_t *proxy; + dhcp_server_t *server; u32 rx_fib_idx = 0, server_fib_idx = 0; next_index = node->cached_next_index; @@ -176,13 +177,11 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm, u8 client_src_mac[6]; vlib_buffer_free_list_t *fl; dhcp_vss_t *vss; + u8 is_solicit = 0; bi0 = from[0]; - to_next[0] = bi0; from += 1; - to_next += 1; n_left_from -= 1; - n_left_to_next -= 1; b0 = vlib_get_buffer (vm, bi0); @@ -227,9 +226,9 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm, /* Send to DHCPV6 server via the configured FIB */ rx_sw_if_index = sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_RX]; rx_fib_idx = im->mfib_index_by_sw_if_index [rx_sw_if_index]; - server = dhcp_get_server(dpm, rx_fib_idx, FIB_PROTOCOL_IP6); + proxy = dhcp_get_proxy(dpm, rx_fib_idx, FIB_PROTOCOL_IP6); - if (PREDICT_FALSE (NULL == server)) + if (PREDICT_FALSE (NULL == proxy)) { error0 = DHCPV6_PROXY_ERROR_NO_SERVER; next0 = DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_DROP; @@ -237,6 +236,7 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm, goto do_trace; } + server = &proxy->dhcp_servers[0]; server_fib_idx = server->server_fib_index; vnet_buffer(b0)->sw_if_index[VLIB_TX] = server_fib_idx; @@ -371,18 +371,19 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm, ip1->payload_length = u1->length; ip1->protocol = PROTO_UDP; ip1->hop_limit = HOP_COUNT_LIMIT; - src = (server->dhcp_server.ip6.as_u64[0] || - server->dhcp_server.ip6.as_u64[1]) ? - &server->dhcp_server.ip6 : &all_dhcpv6_server_address; + src = ((server->dhcp_server.ip6.as_u64[0] || + server->dhcp_server.ip6.as_u64[1]) ? + &server->dhcp_server.ip6 : + &all_dhcpv6_server_address); copy_ip6_address(&ip1->dst_address, src); ia0 = ip6_interface_first_global_or_site_address (&ip6_main, vnet_buffer(b0)->sw_if_index[VLIB_RX]); - src = (server->dhcp_src_address.ip6.as_u64[0] || - server->dhcp_src_address.ip6.as_u64[1]) ? - &server->dhcp_src_address.ip6 : ia0; + src = (proxy->dhcp_src_address.ip6.as_u64[0] || + proxy->dhcp_src_address.ip6.as_u64[1]) ? + &proxy->dhcp_src_address.ip6 : ia0; if (ia0 == 0) { error0 = DHCPV6_PROXY_ERROR_NO_SRC_ADDRESS; @@ -400,6 +401,66 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm, next0 = DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP; + is_solicit = (DHCPV6_MSG_SOLICIT == h0->u.msg_type); + + /* + * If we have multiple servers configured and this is the + * client's discover message, then send copies to each of + * those servers + */ + if (is_solicit && vec_len(proxy->dhcp_servers) > 1) + { + u32 ii; + + for (ii = 1; ii < vec_len(proxy->dhcp_servers); ii++) + { + vlib_buffer_t *c0; + u32 ci0; + + c0 = vlib_buffer_copy(vm, b0); + ci0 = vlib_get_buffer_index(vm, c0); + server = &proxy->dhcp_servers[ii]; + + ip0 = vlib_buffer_get_current (c0); + + src = ((server->dhcp_server.ip6.as_u64[0] || + server->dhcp_server.ip6.as_u64[1]) ? + &server->dhcp_server.ip6 : + &all_dhcpv6_server_address); + copy_ip6_address(&ip1->dst_address, src); + + to_next[0] = ci0; + to_next += 1; + n_left_to_next -= 1; + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + ci0, next0); + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + dhcpv6_proxy_trace_t *tr; + + tr = vlib_add_trace (vm, node, c0, sizeof (*tr)); + tr->which = 0; /* to server */ + tr->error = error0; + tr->original_sw_if_index = rx_sw_if_index; + tr->sw_if_index = sw_if_index; + if (next0 == DHCPV6_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP) + copy_ip6_address((ip6_address_t *)&tr->packet_data[0], + &server->dhcp_server.ip6); + } + + if (PREDICT_FALSE(0 == n_left_to_next)) + { + vlib_put_next_frame (vm, node, next_index, + n_left_to_next); + vlib_get_next_frame (vm, node, next_index, + to_next, n_left_to_next); + } + } + } + do_trace: if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) { @@ -414,6 +475,10 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm, } do_enqueue: + to_next[0] = bi0; + to_next += 1; + n_left_to_next -= 1; + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, bi0, next0); @@ -475,7 +540,8 @@ dhcpv6_proxy_to_client_input (vlib_main_t * vm, u32 n_left_from, * from; ethernet_main_t *em = ethernet_get_main (vm); dhcp_proxy_main_t * dm = &dhcp_proxy_main; - dhcp_server_t * server; + dhcp_proxy_t *proxy; + dhcp_server_t *server; vnet_main_t * vnm = vnet_get_main(); int bogus_length; @@ -588,9 +654,9 @@ dhcpv6_proxy_to_client_input (vlib_main_t * vm, vlib_buffer_advance (b0, sizeof(*r0)); client_fib_idx = im->mfib_index_by_sw_if_index[sw_if_index]; - server = dhcp_get_server(dm, client_fib_idx, FIB_PROTOCOL_IP6); + proxy = dhcp_get_proxy(dm, client_fib_idx, FIB_PROTOCOL_IP6); - if (NULL == server) + if (NULL == proxy) { error0 = DHCPV6_PROXY_ERROR_NO_SERVER; goto drop_packet; @@ -599,15 +665,21 @@ dhcpv6_proxy_to_client_input (vlib_main_t * vm, server_fib_idx = im->fib_index_by_sw_if_index [vnet_buffer(b0)->sw_if_index[VLIB_RX]]; - if (server_fib_idx != server->server_fib_index || - ip0->src_address.as_u64[0] != server->dhcp_server.ip6.as_u64[0] || - ip0->src_address.as_u64[1] != server->dhcp_server.ip6.as_u64[1]) - { - //drop packet if not from server with configured address or FIB - error0 = DHCPV6_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS; - goto drop_packet; - } + vec_foreach(server, proxy->dhcp_servers) + { + if (server_fib_idx == server->server_fib_index && + ip0->src_address.as_u64[0] == server->dhcp_server.ip6.as_u64[0] && + ip0->src_address.as_u64[1] == server->dhcp_server.ip6.as_u64[1]) + { + goto server_found; + } + } + + //drop packet if not from server with configured address or FIB + error0 = DHCPV6_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS; + goto drop_packet; + server_found: vnet_buffer (b0)->sw_if_index[VLIB_TX] = original_sw_if_index = sw_if_index; @@ -773,9 +845,8 @@ dhcp6_proxy_set_server (ip46_address_t *addr, if (is_del) { - rc = dhcp_proxy_server_del (FIB_PROTOCOL_IP6, rx_fib_index); - - if (0 == rc) + if (dhcp_proxy_server_del (FIB_PROTOCOL_IP6, rx_fib_index, + addr, server_table_id)) { mfib_table_entry_delete(rx_fib_index, &all_dhcp_servers, @@ -893,43 +964,50 @@ VLIB_CLI_COMMAND (dhcpv6_proxy_set_command, static) = { static u8 * format_dhcp6_proxy_server (u8 * s, va_list * args) { - dhcp_server_t * server = va_arg (*args, dhcp_server_t *); + dhcp_proxy_t * proxy = va_arg (*args, dhcp_proxy_t *); ip6_fib_t *server_fib; + dhcp_server_t *server; ip6_mfib_t *rx_fib; - if (NULL == server) + if (proxy == 0) { - s = format (s, "%=40s%=40s%=14s%=14s", "Server Address", "Source Address", - "Server FIB", "RX FIB"); + s = format (s, "%=14s%=16s%s", "RX FIB", "Src Address", + "Servers FIB,Address"); return s; } - server_fib = ip6_fib_get(server->server_fib_index); - rx_fib = ip6_mfib_get(server->rx_fib_index); + rx_fib = ip6_mfib_get(proxy->rx_fib_index); + + s = format (s, "%=14u%=16U", + rx_fib->table_id, + format_ip46_address, &proxy->dhcp_src_address, IP46_TYPE_ANY); + vec_foreach(server, proxy->dhcp_servers) + { + server_fib = ip6_fib_get(server->server_fib_index); + s = format (s, "%u,%U ", + server_fib->table_id, + format_ip46_address, &server->dhcp_server, IP46_TYPE_ANY); + } - s = format (s, "%=40U%=40U%=14u%=14u", - format_ip46_address, &server->dhcp_server, IP46_TYPE_ANY, - format_ip46_address, &server->dhcp_src_address, IP46_TYPE_ANY, - server_fib->table_id, rx_fib->table_id); return s; } static int -dhcp6_proxy_show_walk (dhcp_server_t *server, +dhcp6_proxy_show_walk (dhcp_proxy_t *proxy, void *ctx) { vlib_main_t * vm = ctx; - vlib_cli_output (vm, "%U", format_dhcp6_proxy_server, server); + vlib_cli_output (vm, "%U", format_dhcp6_proxy_server, proxy); return (1); } static clib_error_t * dhcpv6_proxy_show_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) + unformat_input_t * input, + vlib_cli_command_t * cmd) { vlib_cli_output (vm, "%U", format_dhcp6_proxy_server, NULL /* header line */); diff --git a/src/vnet/dhcp/dhcp_api.c b/src/vnet/dhcp/dhcp_api.c index ce34f6a4..e9c757e8 100644 --- a/src/vnet/dhcp/dhcp_api.c +++ b/src/vnet/dhcp/dhcp_api.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -113,46 +114,73 @@ vl_api_dhcp_proxy_dump_t_handler (vl_api_dhcp_proxy_dump_t * mp) if (q == 0) return; - dhcp_proxy_dump ((mp->is_ip6 == 0 ? + dhcp_proxy_dump ((mp->is_ip6 == 1 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4), q, mp->context); } void dhcp_send_details (fib_protocol_t proto, - void *opaque, - u32 context, - const ip46_address_t * server, - const ip46_address_t * src, - u32 server_fib_id, - u32 rx_fib_id, u32 vss_fib_id, u32 vss_oui) + void *opaque, u32 context, dhcp_proxy_t * proxy) { vl_api_dhcp_proxy_details_t *mp; unix_shared_memory_queue_t *q = opaque; - - mp = vl_msg_api_alloc (sizeof (*mp)); + vl_api_dhcp_server_t *v_server; + dhcp_server_t *server; + fib_table_t *s_fib; + dhcp_vss_t *vss; + u32 count; + size_t n; + + count = vec_len (proxy->dhcp_servers); + n = sizeof (*mp) + (count * sizeof (vl_api_dhcp_server_t)); + mp = vl_msg_api_alloc (n); if (!mp) return; - memset (mp, 0, sizeof (*mp)); + memset (mp, 0, n); mp->_vl_msg_id = ntohs (VL_API_DHCP_PROXY_DETAILS); mp->context = context; - - mp->rx_vrf_id = htonl (rx_fib_id); - mp->server_vrf_id = htonl (server_fib_id); - mp->vss_oui = htonl (vss_oui); - mp->vss_fib_id = htonl (vss_fib_id); + mp->count = count; mp->is_ipv6 = (proto == FIB_PROTOCOL_IP6); + mp->rx_vrf_id = + htonl (dhcp_proxy_rx_table_get_table_id (proto, proxy->rx_fib_index)); + + vss = dhcp_get_vss_info (&dhcp_proxy_main, proxy->rx_fib_index, proto); + + if (NULL != vss) + { + mp->vss_oui = htonl (vss->oui); + mp->vss_fib_id = htonl (vss->fib_id); + } + + vec_foreach_index (count, proxy->dhcp_servers) + { + server = &proxy->dhcp_servers[count]; + v_server = &mp->servers[count]; + + s_fib = fib_table_get (server->server_fib_index, proto); + + v_server->server_vrf_id = htonl (s_fib->ft_table_id); + + if (mp->is_ipv6) + { + memcpy (v_server->dhcp_server, &server->dhcp_server.ip6, 16); + } + else + { + /* put the address in the first bytes */ + memcpy (v_server->dhcp_server, &server->dhcp_server.ip4, 4); + } + } if (mp->is_ipv6) { - memcpy (mp->dhcp_server, server, 16); - memcpy (mp->dhcp_src_address, src, 16); + memcpy (mp->dhcp_src_address, &proxy->dhcp_src_address.ip6, 16); } else { /* put the address in the first bytes */ - memcpy (mp->dhcp_server, &server->ip4, 4); - memcpy (mp->dhcp_src_address, &src->ip4, 4); + memcpy (mp->dhcp_src_address, &proxy->dhcp_src_address.ip4, 4); } vl_msg_api_send_shmem (q, (u8 *) & mp); } diff --git a/src/vnet/dhcp/dhcp_proxy.c b/src/vnet/dhcp/dhcp_proxy.c index 8e31c3db..ba7f354e 100644 --- a/src/vnet/dhcp/dhcp_proxy.c +++ b/src/vnet/dhcp/dhcp_proxy.c @@ -44,7 +44,7 @@ dhcp_proxy_rx_table_unlock (fib_protocol_t proto, mfib_table_unlock(fib_index, proto); } -static u32 + u32 dhcp_proxy_rx_table_get_table_id (fib_protocol_t proto, u32 fib_index) { @@ -72,7 +72,7 @@ dhcp_proxy_walk (fib_protocol_t proto, void *ctx) { dhcp_proxy_main_t * dpm = &dhcp_proxy_main; - dhcp_server_t * server; + dhcp_proxy_t * server; u32 server_index, i; vec_foreach_index (i, dpm->dhcp_server_index_by_rx_fib_index[proto]) @@ -124,31 +124,68 @@ dhcp_vss_walk (fib_protocol_t proto, } } +static u32 +dhcp_proxy_server_find (dhcp_proxy_t *proxy, + fib_protocol_t proto, + ip46_address_t *addr, + u32 server_table_id) +{ + dhcp_server_t *server; + u32 ii, fib_index; + + vec_foreach_index(ii, proxy->dhcp_servers) + { + server = &proxy->dhcp_servers[ii]; + fib_index = fib_table_find(proto, server_table_id); + + if (ip46_address_is_equal(&server->dhcp_server, + addr) && + (server->server_fib_index == fib_index)) + { + return (ii); + } + } + return (~0); +} + int dhcp_proxy_server_del (fib_protocol_t proto, - u32 rx_fib_index) + u32 rx_fib_index, + ip46_address_t *addr, + u32 server_table_id) { dhcp_proxy_main_t * dpm = &dhcp_proxy_main; - dhcp_server_t * server = 0; - int rc = 0; + dhcp_proxy_t *proxy = 0; - server = dhcp_get_server(dpm, rx_fib_index, proto); + proxy = dhcp_get_proxy(dpm, rx_fib_index, proto); - if (NULL == server) - { - rc = VNET_API_ERROR_NO_SUCH_ENTRY; - } - else + if (NULL != proxy) { - /* Use the default server again. */ - dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] = ~0; + dhcp_server_t *server; + u32 index; - fib_table_unlock (server->server_fib_index, proto); + index = dhcp_proxy_server_find(proxy, proto, addr, server_table_id); - pool_put (dpm->dhcp_servers[proto], server); + if (~0 != index) + { + server = &proxy->dhcp_servers[index]; + fib_table_unlock (server->server_fib_index, proto); + + vec_del1(proxy->dhcp_servers, index); + + if (0 == vec_len(proxy->dhcp_servers)) + { + /* no servers left, delete the proxy config */ + dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] = ~0; + vec_free(proxy->dhcp_servers); + pool_put (dpm->dhcp_servers[proto], proxy); + return (1); + } + } } - return (rc); + /* the proxy still exists */ + return (0); } int @@ -159,48 +196,42 @@ dhcp_proxy_server_add (fib_protocol_t proto, u32 server_table_id) { dhcp_proxy_main_t * dpm = &dhcp_proxy_main; - dhcp_server_t * server = 0; + dhcp_proxy_t * proxy = 0; int new = 0; - server = dhcp_get_server(dpm, rx_fib_index, proto); + proxy = dhcp_get_proxy(dpm, rx_fib_index, proto); - if (NULL == server) + if (NULL == proxy) { vec_validate_init_empty(dpm->dhcp_server_index_by_rx_fib_index[proto], rx_fib_index, ~0); - pool_get (dpm->dhcp_servers[proto], server); - memset (server, 0, sizeof (*server)); + pool_get (dpm->dhcp_servers[proto], proxy); + memset (proxy, 0, sizeof (*proxy)); new = 1; dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] = - server - dpm->dhcp_servers[proto]; + proxy - dpm->dhcp_servers[proto]; - server->rx_fib_index = rx_fib_index; - server->server_fib_index = - fib_table_find_or_create_and_lock(proto, server_table_id); + proxy->dhcp_src_address = *src_address; + proxy->rx_fib_index = rx_fib_index; } else { - /* modify, may need to swap server FIBs */ - u32 tmp_index; - - tmp_index = fib_table_find(proto, server_table_id); - - if (tmp_index != server->server_fib_index) + if (~0 != dhcp_proxy_server_find(proxy, proto, addr, server_table_id)) { - tmp_index = server->server_fib_index; - - /* certainly swapping if the fib doesn't exist */ - server->server_fib_index = - fib_table_find_or_create_and_lock(proto, server_table_id); - fib_table_unlock (tmp_index, proto); + return (new); } } - server->dhcp_server = *addr; - server->dhcp_src_address = *src_address; + dhcp_server_t server = { + .dhcp_server = *addr, + .server_fib_index = fib_table_find_or_create_and_lock(proto, + server_table_id), + }; + + vec_add1(proxy->dhcp_servers, server); return (new); } @@ -213,31 +244,15 @@ typedef struct dhcp4_proxy_dump_walk_ctx_t_ } dhcp_proxy_dump_walk_cxt_t; static int -dhcp_proxy_dump_walk (dhcp_server_t *server, +dhcp_proxy_dump_walk (dhcp_proxy_t *proxy, void *arg) { dhcp_proxy_dump_walk_cxt_t *ctx = arg; - fib_table_t *s_fib; - u32 rx_table_id; - dhcp_vss_t *v; - - v = dhcp_get_vss_info(&dhcp_proxy_main, - server->rx_fib_index, - ctx->proto); - - s_fib = fib_table_get(server->server_fib_index, ctx->proto); - rx_table_id = dhcp_proxy_rx_table_get_table_id(server->rx_fib_index, - ctx->proto); dhcp_send_details(ctx->proto, ctx->opaque, ctx->context, - &server->dhcp_server, - &server->dhcp_src_address, - s_fib->ft_table_id, - rx_table_id, - (v ? v->fib_id : 0), - (v ? v->oui : 0)); + proxy); return (1); } diff --git a/src/vnet/dhcp/dhcp_proxy.h b/src/vnet/dhcp/dhcp_proxy.h index 708e92f3..ef2bc0a1 100644 --- a/src/vnet/dhcp/dhcp_proxy.h +++ b/src/vnet/dhcp/dhcp_proxy.h @@ -58,32 +58,58 @@ typedef struct dhcp_vss_t_ { } dhcp_vss_t; /** - * @brief A DHCP proxy server represenation + * @brief A representation of a single DHCP Server within a given VRF config */ -typedef struct dhcp_server_t_ { +typedef struct dhcp_server_t_ +{ /** * @brief The address of the DHCP server to which to relay the client's * messages */ ip46_address_t dhcp_server; - /** - * @brief The source address to use in relayed messaes - */ - ip46_address_t dhcp_src_address; - /** * @brief The FIB index (not the external Table-ID) in which the server * is reachable. */ u32 server_fib_index; +} dhcp_server_t; + +/** + * @brief A DHCP proxy represenation fpr per-client VRF config + */ +typedef struct dhcp_proxy_t_ { + /** + * @brief The set of DHCP servers to which messages are relayed. + * If multiple servers are configured then discover/solict messages + * are relayed to each. A cookie is maintained for the relay, and only + * one message is replayed to the client, based on the presence of the + * cookie. + * The expectation is there are only 1 or 2 servers, hence no fancy DB. + */ + dhcp_server_t *dhcp_servers; + + /** + * @brief Hash table of pending requets key'd on the clients MAC address + */ + uword *dhcp_pending; + + /** + * @brief A lock for the pending request DB. + */ + int lock; + + /** + * @brief The source address to use in relayed messaes + */ + ip46_address_t dhcp_src_address; /** * @brief The FIB index (not the external Table-ID) in which the client * is resides. */ u32 rx_fib_index; -} dhcp_server_t; +} dhcp_proxy_t; #define DHCP_N_PROTOS (FIB_PROTOCOL_IP6 + 1) @@ -92,7 +118,7 @@ typedef struct dhcp_server_t_ { */ typedef struct { /* Pool of DHCP servers */ - dhcp_server_t *dhcp_servers[DHCP_N_PROTOS]; + dhcp_proxy_t *dhcp_servers[DHCP_N_PROTOS]; /* Pool of selected DHCP server. Zero is the default server */ u32 * dhcp_server_index_by_rx_fib_index[DHCP_N_PROTOS]; @@ -114,12 +140,7 @@ extern dhcp_proxy_main_t dhcp_proxy_main; void dhcp_send_details (fib_protocol_t proto, void *opaque, u32 context, - const ip46_address_t *server, - const ip46_address_t *src, - u32 server_fib_id, - u32 rx_fib_id, - u32 vss_fib_id, - u32 vss_oui); + dhcp_proxy_t *proxy); /** * @brief Show (on CLI) a VSS config during a show walk @@ -157,16 +178,22 @@ int dhcp_proxy_server_add(fib_protocol_t proto, /** * @brief Delete a DHCP proxy config - * @return 0 is deleted, otherwise an error code + * @return 1 if the proxy is deleted, 0 otherwise */ int dhcp_proxy_server_del(fib_protocol_t proto, - u32 rx_fib_index); + u32 rx_fib_index, + ip46_address_t *addr, + u32 server_table_id); + +u32 +dhcp_proxy_rx_table_get_table_id (fib_protocol_t proto, + u32 fib_index); /** * @brief Callback function invoked for each DHCP proxy entry * return 0 to break the walk, non-zero otherwise. */ -typedef int (*dhcp_proxy_walk_fn_t)(dhcp_server_t *server, +typedef int (*dhcp_proxy_walk_fn_t)(dhcp_proxy_t *server, void *ctx); /** @@ -191,6 +218,18 @@ void dhcp_vss_walk(fib_protocol_t proto, dhcp_vss_walk_fn_t fn, void *ctx); +/** + * @brief Lock a proxy object to prevent simultaneous access of its + * pending store + */ +void dhcp_proxy_lock (dhcp_proxy_t *server); + +/** + * @brief Lock a proxy object to prevent simultaneous access of its + * pending store + */ +void dhcp_proxy_unlock (dhcp_proxy_t *server); + /** * @brief Get the VSS data for the FIB index */ @@ -215,12 +254,12 @@ dhcp_get_vss_info (dhcp_proxy_main_t *dm, /** * @brief Get the DHCP proxy server data for the FIB index */ -static inline dhcp_server_t * -dhcp_get_server (dhcp_proxy_main_t *dm, - u32 rx_fib_index, - fib_protocol_t proto) +static inline dhcp_proxy_t * +dhcp_get_proxy (dhcp_proxy_main_t *dm, + u32 rx_fib_index, + fib_protocol_t proto) { - dhcp_server_t *s = NULL; + dhcp_proxy_t *s = NULL; if (vec_len(dm->dhcp_server_index_by_rx_fib_index[proto]) > rx_fib_index && dm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] != ~0) diff --git a/src/vnet/ip/ip6_packet.h b/src/vnet/ip/ip6_packet.h index 6eabeef1..9bf19edb 100644 --- a/src/vnet/ip/ip6_packet.h +++ b/src/vnet/ip/ip6_packet.h @@ -79,6 +79,8 @@ typedef CLIB_PACKED (union { #define ip46_address_reset(ip46) ((ip46)->as_u64[0] = (ip46)->as_u64[1] = 0) #define ip46_address_cmp(ip46_1, ip46_2) (memcmp(ip46_1, ip46_2, sizeof(*ip46_1))) #define ip46_address_is_zero(ip46) (((ip46)->as_u64[0] == 0) && ((ip46)->as_u64[1] == 0)) +#define ip46_address_is_equal(a1, a2) (((a1)->as_u64[0] == (a2)->as_u64[0]) \ + && ((a1)->as_u64[1] == (a2)->as_u64[1])) always_inline void ip46_from_addr_buf (u32 is_ipv6, u8 * buf, ip46_address_t * ip) diff --git a/src/vnet/pg/input.c b/src/vnet/pg/input.c index 4a65b024..2649798b 100644 --- a/src/vnet/pg/input.c +++ b/src/vnet/pg/input.c @@ -1373,6 +1373,7 @@ typedef struct u32 stream_index; u32 packet_length; + u32 sw_if_index; /* Use pre data for packet data. */ vlib_buffer_t buffer; @@ -1399,6 +1400,7 @@ format_pg_input_trace (u8 * s, va_list * va) s = format (s, "stream %d", t->stream_index); s = format (s, ", %d bytes", t->packet_length); + s = format (s, ", %d sw_if_index", t->sw_if_index); s = format (s, "\n%U%U", format_white_space, indent, format_vlib_buffer, &t->buffer); @@ -1458,6 +1460,9 @@ pg_input_trace (pg_main_t * pg, t0->packet_length = vlib_buffer_length_in_chain (vm, b0); t1->packet_length = vlib_buffer_length_in_chain (vm, b1); + t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; + clib_memcpy (&t0->buffer, b0, sizeof (b0[0]) - sizeof (b0->pre_data)); clib_memcpy (&t1->buffer, b1, sizeof (b1[0]) - sizeof (b1->pre_data)); @@ -1484,6 +1489,7 @@ pg_input_trace (pg_main_t * pg, t0->stream_index = stream_index; t0->packet_length = vlib_buffer_length_in_chain (vm, b0); + t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; clib_memcpy (&t0->buffer, b0, sizeof (b0[0]) - sizeof (b0->pre_data)); clib_memcpy (t0->buffer.pre_data, b0->data, sizeof (t0->buffer.pre_data)); diff --git a/test/test_dhcp.py b/test/test_dhcp.py index a09c9bdb..89667d3d 100644 --- a/test/test_dhcp.py +++ b/test/test_dhcp.py @@ -2,8 +2,10 @@ import unittest import socket +import struct from framework import VppTestCase, VppTestRunner +from vpp_neighbor import VppNeighbor from scapy.layers.l2 import Ether, getmacbyip from scapy.layers.inet import IP, UDP, ICMP @@ -11,7 +13,7 @@ from scapy.layers.inet6 import IPv6, in6_getnsmac, in6_mactoifaceid from scapy.layers.dhcp import DHCP, BOOTP, DHCPTypes from scapy.layers.dhcp6 import DHCP6, DHCP6_Solicit, DHCP6_RelayForward, \ DHCP6_RelayReply, DHCP6_Advertise, DHCP6OptRelayMsg, DHCP6OptIfaceId, \ - DHCP6OptStatusCode, DHCP6OptVSS, DHCP6OptClientLinkLayerAddr + DHCP6OptStatusCode, DHCP6OptVSS, DHCP6OptClientLinkLayerAddr, DHCP6_Request from socket import AF_INET, AF_INET6 from scapy.utils import inet_pton, inet_ntop from scapy.utils6 import in6_ptop @@ -140,7 +142,7 @@ class TestDHCP(VppTestCase): return data - def verify_dhcp_offer(self, pkt, intf): + def verify_dhcp_offer(self, pkt, intf, fib_id=0, oui=0): ether = pkt[Ether] self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff") self.assertEqual(ether.src, intf.local_mac) @@ -162,15 +164,22 @@ class TestDHCP(VppTestCase): is_offer = True self.assertTrue(is_offer) - data = self.validate_relay_options(pkt, intf, intf.local_ip4, 0, 0) + data = self.validate_relay_options(pkt, intf, intf.local_ip4, + fib_id, oui) + + def verify_dhcp_discover(self, pkt, intf, src_intf=None, fib_id=0, oui=0, + dst_mac=None, dst_ip=None): + if not dst_mac: + dst_mac = intf.remote_mac + if not dst_ip: + dst_ip = intf.remote_ip4 - def verify_dhcp_discover(self, pkt, intf, src_intf=None, fib_id=0, oui=0): ether = pkt[Ether] - self.assertEqual(ether.dst, intf.remote_mac) + self.assertEqual(ether.dst, dst_mac) self.assertEqual(ether.src, intf.local_mac) ip = pkt[IP] - self.assertEqual(ip.dst, intf.remote_ip4) + self.assertEqual(ip.dst, dst_ip) self.assertEqual(ip.src, intf.local_ip4) udp = pkt[UDP] @@ -195,13 +204,20 @@ class TestDHCP(VppTestCase): def verify_dhcp6_solicit(self, pkt, intf, peer_ip, peer_mac, fib_id=0, - oui=0): + oui=0, + dst_mac=None, + dst_ip=None): + if not dst_mac: + dst_mac = intf.remote_mac + if not dst_ip: + dst_ip = in6_ptop(intf.remote_ip6) + ether = pkt[Ether] - self.assertEqual(ether.dst, intf.remote_mac) + self.assertEqual(ether.dst, dst_mac) self.assertEqual(ether.src, intf.local_mac) ip = pkt[IPv6] - self.assertEqual(in6_ptop(ip.dst), in6_ptop(intf.remote_ip6)) + self.assertEqual(in6_ptop(ip.dst), dst_ip) self.assertEqual(in6_ptop(ip.src), in6_ptop(intf.local_ip6)) udp = pkt[UDP] @@ -447,6 +463,128 @@ class TestDHCP(VppTestCase): self.pg_enable_capture(self.pg_interfaces) self.pg_start() + rx = self.pg1.get_capture(1) + rx = rx[0] + self.verify_dhcp_discover(rx, self.pg1, src_intf=self.pg3, + fib_id=1, oui=4) + + # + # Add a second DHCP server in VRF 1 + # expect clients messages to be relay to both configured servers + # + self.pg1.generate_remote_hosts(2) + server_addr2 = socket.inet_pton(AF_INET, self.pg1.remote_hosts[1].ip4) + + self.vapi.dhcp_proxy_config(server_addr2, + src_addr, + rx_table_id=1, + server_table_id=1, + is_add=1) + + # + # We'll need an ARP entry for the server to send it packets + # + arp_entry = VppNeighbor(self, + self.pg1.sw_if_index, + self.pg1.remote_hosts[1].mac, + self.pg1.remote_hosts[1].ip4) + arp_entry.add_vpp_config() + + # + # Send a discover from the client. expect two relayed messages + # The frist packet is sent to the second server + # We're not enforcing that here, it's just the way it is. + # + self.pg3.add_stream(pkts_disc_vrf1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(2) + + option_82 = self.verify_dhcp_discover( + rx[0], self.pg1, + src_intf=self.pg3, + dst_mac=self.pg1.remote_hosts[1].mac, + dst_ip=self.pg1.remote_hosts[1].ip4, + fib_id=1, oui=4) + self.verify_dhcp_discover(rx[1], self.pg1, src_intf=self.pg3, + fib_id=1, oui=4) + + # + # Send both packets back. Client gets both. + # + p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4) / + UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) / + BOOTP(op=1) / + DHCP(options=[('message-type', 'offer'), + ('relay_agent_Information', option_82), + ('end')])) + p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_hosts[1].ip4, dst=self.pg1.local_ip4) / + UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) / + BOOTP(op=1) / + DHCP(options=[('message-type', 'offer'), + ('relay_agent_Information', option_82), + ('end')])) + pkts = [p1, p2] + + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg3.get_capture(2) + + self.verify_dhcp_offer(rx[0], self.pg3, fib_id=1, oui=4) + self.verify_dhcp_offer(rx[1], self.pg3, fib_id=1, oui=4) + + # + # Ensure offers from non-servers are dropeed + # + p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src="8.8.8.8", dst=self.pg1.local_ip4) / + UDP(sport=DHCP4_SERVER_PORT, dport=DHCP4_SERVER_PORT) / + BOOTP(op=1) / + DHCP(options=[('message-type', 'offer'), + ('relay_agent_Information', option_82), + ('end')])) + self.send_and_assert_no_replies(self.pg1, p2, + "DHCP offer from non-server") + + # + # Ensure only the discover is sent to multiple servers + # + p_req_vrf1 = (Ether(dst="ff:ff:ff:ff:ff:ff", + src=self.pg3.remote_mac) / + IP(src="0.0.0.0", dst="255.255.255.255") / + UDP(sport=DHCP4_CLIENT_PORT, + dport=DHCP4_SERVER_PORT) / + BOOTP(op=1) / + DHCP(options=[('message-type', 'request'), + ('end')])) + + self.pg3.add_stream(p_req_vrf1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(1) + + # + # Remove the second DHCP server + # + self.vapi.dhcp_proxy_config(server_addr2, + src_addr, + rx_table_id=1, + server_table_id=1, + is_add=0) + + # + # Test we can still relay with the first + # + self.pg3.add_stream(pkts_disc_vrf1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = self.pg1.get_capture(1) rx = rx[0] self.verify_dhcp_discover(rx, self.pg1, src_intf=self.pg3, @@ -472,7 +610,7 @@ class TestDHCP(VppTestCase): self.vapi.dhcp_proxy_config(server_addr, src_addr, rx_table_id=1, - server_table_id=11, + server_table_id=1, is_add=0) self.send_and_assert_no_replies(self.pg2, pkts_disc_vrf0, @@ -500,18 +638,16 @@ class TestDHCP(VppTestCase): UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_CLIENT_PORT) / DHCP6_Solicit()) - pkts_solicit_vrf0 = [p_solicit_vrf0] p_solicit_vrf1 = (Ether(dst=dmac, src=self.pg3.remote_mac) / IPv6(src=dhcp_solicit_src_vrf1, dst=dhcp_solicit_dst) / UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_CLIENT_PORT) / DHCP6_Solicit()) - pkts_solicit_vrf1 = [p_solicit_vrf1] - self.send_and_assert_no_replies(self.pg2, pkts_solicit_vrf0, + self.send_and_assert_no_replies(self.pg2, p_solicit_vrf0, "DHCP with no configuration") - self.send_and_assert_no_replies(self.pg3, pkts_solicit_vrf1, + self.send_and_assert_no_replies(self.pg3, p_solicit_vrf1, "DHCP with no configuration") # @@ -525,9 +661,9 @@ class TestDHCP(VppTestCase): server_table_id=0, is_ipv6=1) - self.send_and_assert_no_replies(self.pg2, pkts_solicit_vrf0, + self.send_and_assert_no_replies(self.pg2, p_solicit_vrf0, "DHCP with no configuration") - self.send_and_assert_no_replies(self.pg3, pkts_solicit_vrf1, + self.send_and_assert_no_replies(self.pg3, p_solicit_vrf1, "DHCP with no configuration") # @@ -538,13 +674,13 @@ class TestDHCP(VppTestCase): # # Now the DHCP requests are relayed to the server # - self.pg2.add_stream(pkts_solicit_vrf0) + self.pg2.add_stream(p_solicit_vrf0) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg0.get_capture(1) - rx = rx[0] - self.verify_dhcp6_solicit(rx, self.pg0, + + self.verify_dhcp6_solicit(rx[0], self.pg0, dhcp_solicit_src_vrf0, self.pg2.remote_mac) @@ -557,8 +693,7 @@ class TestDHCP(VppTestCase): IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) / UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) / DHCP6_Advertise()) - pkts_adv_vrf0 = [p_adv_vrf0] - self.send_and_assert_no_replies(self.pg2, pkts_adv_vrf0, + self.send_and_assert_no_replies(self.pg2, p_adv_vrf0, "DHCP6 not a relay reply") # 2 - no relay message option @@ -567,8 +702,7 @@ class TestDHCP(VppTestCase): UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) / DHCP6_RelayReply() / DHCP6_Advertise()) - pkts_adv_vrf0 = [p_adv_vrf0] - self.send_and_assert_no_replies(self.pg2, pkts_adv_vrf0, + self.send_and_assert_no_replies(self.pg2, p_adv_vrf0, "DHCP not a relay message") # 3 - no circuit ID @@ -578,8 +712,7 @@ class TestDHCP(VppTestCase): DHCP6_RelayReply() / DHCP6OptRelayMsg(optlen=0) / DHCP6_Advertise()) - pkts_adv_vrf0 = [p_adv_vrf0] - self.send_and_assert_no_replies(self.pg2, pkts_adv_vrf0, + self.send_and_assert_no_replies(self.pg2, p_adv_vrf0, "DHCP6 no circuit ID") # 4 - wrong circuit ID p_adv_vrf0 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / @@ -589,8 +722,7 @@ class TestDHCP(VppTestCase): DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x05') / DHCP6OptRelayMsg(optlen=0) / DHCP6_Advertise()) - pkts_adv_vrf0 = [p_adv_vrf0] - self.send_and_assert_no_replies(self.pg2, pkts_adv_vrf0, + self.send_and_assert_no_replies(self.pg2, p_adv_vrf0, "DHCP6 wrong circuit ID") # @@ -611,8 +743,8 @@ class TestDHCP(VppTestCase): self.pg_start() rx = self.pg2.get_capture(1) - rx = rx[0] - self.verify_dhcp6_advert(rx, self.pg2, "::") + + self.verify_dhcp6_advert(rx[0], self.pg2, "::") # # Send the relay response (the advertisement) @@ -632,8 +764,8 @@ class TestDHCP(VppTestCase): self.pg_start() rx = self.pg2.get_capture(1) - rx = rx[0] - self.verify_dhcp6_advert(rx, self.pg2, dhcp_solicit_src_vrf0) + + self.verify_dhcp6_advert(rx[0], self.pg2, dhcp_solicit_src_vrf0) # # Add all the config for VRF 1 @@ -648,13 +780,13 @@ class TestDHCP(VppTestCase): # # VRF 1 solicit # - self.pg3.add_stream(pkts_solicit_vrf1) + self.pg3.add_stream(p_solicit_vrf1) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) - rx = rx[0] - self.verify_dhcp6_solicit(rx, self.pg1, + + self.verify_dhcp6_solicit(rx[0], self.pg1, dhcp_solicit_src_vrf1, self.pg3.remote_mac) @@ -676,21 +808,21 @@ class TestDHCP(VppTestCase): self.pg_start() rx = self.pg3.get_capture(1) - rx = rx[0] - self.verify_dhcp6_advert(rx, self.pg3, dhcp_solicit_src_vrf1) + + self.verify_dhcp6_advert(rx[0], self.pg3, dhcp_solicit_src_vrf1) # # Add VSS config # table=1, fib=id=1, oui=4 self.vapi.dhcp_proxy_set_vss(1, 1, 4, is_ip6=1) - self.pg3.add_stream(pkts_solicit_vrf1) + self.pg3.add_stream(p_solicit_vrf1) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) - rx = rx[0] - self.verify_dhcp6_solicit(rx, self.pg1, + + self.verify_dhcp6_solicit(rx[0], self.pg1, dhcp_solicit_src_vrf1, self.pg3.remote_mac, fib_id=1, @@ -702,27 +834,163 @@ class TestDHCP(VppTestCase): # self.vapi.dhcp_proxy_set_vss(1, 1, 4, is_ip6=1, is_add=0) - self.pg3.add_stream(pkts_solicit_vrf1) + self.pg3.add_stream(p_solicit_vrf1) self.pg_enable_capture(self.pg_interfaces) self.pg_start() rx = self.pg1.get_capture(1) - rx = rx[0] - self.verify_dhcp6_solicit(rx, self.pg1, + + self.verify_dhcp6_solicit(rx[0], self.pg1, dhcp_solicit_src_vrf1, self.pg3.remote_mac) # - # Cleanup + # Add a second DHCP server in VRF 1 + # expect clients messages to be relay to both configured servers # - self.vapi.dhcp_proxy_config(server_addr_vrf1, + self.pg1.generate_remote_hosts(2) + server_addr2 = socket.inet_pton(AF_INET6, self.pg1.remote_hosts[1].ip6) + + self.vapi.dhcp_proxy_config(server_addr2, + src_addr_vrf1, + rx_table_id=1, + server_table_id=1, + is_ipv6=1) + + # + # We'll need an ND entry for the server to send it packets + # + nd_entry = VppNeighbor(self, + self.pg1.sw_if_index, + self.pg1.remote_hosts[1].mac, + self.pg1.remote_hosts[1].ip6, + af=AF_INET6) + nd_entry.add_vpp_config() + + # + # Send a discover from the client. expect two relayed messages + # The frist packet is sent to the second server + # We're not enforcing that here, it's just the way it is. + # + self.pg3.add_stream(p_solicit_vrf1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(2) + + self.verify_dhcp6_solicit(rx[0], self.pg1, + dhcp_solicit_src_vrf1, + self.pg3.remote_mac) + self.verify_dhcp6_solicit(rx[1], self.pg1, + dhcp_solicit_src_vrf1, + self.pg3.remote_mac, + dst_mac=self.pg1.remote_hosts[1].mac, + dst_ip=self.pg1.remote_hosts[1].ip6) + + # + # Send both packets back. Client gets both. + # + p1 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IPv6(dst=self.pg1.local_ip6, src=self.pg1.remote_ip6) / + UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) / + DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) / + DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') / + DHCP6OptRelayMsg(optlen=0) / + DHCP6_Advertise(trid=1) / + DHCP6OptStatusCode(statuscode=0)) + p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_hosts[1].mac) / + IPv6(dst=self.pg1.local_ip6, src=self.pg1._remote_hosts[1].ip6) / + UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) / + DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) / + DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') / + DHCP6OptRelayMsg(optlen=0) / + DHCP6_Advertise(trid=1) / + DHCP6OptStatusCode(statuscode=0)) + + pkts = [p1, p2] + + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg3.get_capture(2) + + self.verify_dhcp6_advert(rx[0], self.pg3, dhcp_solicit_src_vrf1) + self.verify_dhcp6_advert(rx[1], self.pg3, dhcp_solicit_src_vrf1) + + # + # Ensure only solicit messages are duplicated + # + p_request_vrf1 = (Ether(dst=dmac, src=self.pg3.remote_mac) / + IPv6(src=dhcp_solicit_src_vrf1, + dst=dhcp_solicit_dst) / + UDP(sport=DHCP6_SERVER_PORT, + dport=DHCP6_CLIENT_PORT) / + DHCP6_Request()) + + self.pg3.add_stream(p_request_vrf1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(1) + + # + # Test we drop DHCP packets from addresses that are not configured as + # DHCP servers + # + p2 = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_hosts[1].mac) / + IPv6(dst=self.pg1.local_ip6, src="3001::1") / + UDP(sport=DHCP6_SERVER_PORT, dport=DHCP6_SERVER_PORT) / + DHCP6_RelayReply(peeraddr=dhcp_solicit_src_vrf1) / + DHCP6OptIfaceId(optlen=4, ifaceid='\x00\x00\x00\x04') / + DHCP6OptRelayMsg(optlen=0) / + DHCP6_Advertise(trid=1) / + DHCP6OptStatusCode(statuscode=0)) + self.send_and_assert_no_replies(self.pg1, p2, + "DHCP6 not from server") + + # + # Remove the second DHCP server + # + self.vapi.dhcp_proxy_config(server_addr2, src_addr_vrf1, rx_table_id=1, server_table_id=1, is_ipv6=1, is_add=0) + + # + # Test we can still relay with the first + # + self.pg3.add_stream(p_solicit_vrf1) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture(1) + + self.verify_dhcp6_solicit(rx[0], self.pg1, + dhcp_solicit_src_vrf1, + self.pg3.remote_mac) + + # + # Cleanup + # self.vapi.dhcp_proxy_config(server_addr_vrf1, src_addr_vrf1, + rx_table_id=1, + server_table_id=1, + is_ipv6=1, + is_add=0) + self.vapi.dhcp_proxy_config(server_addr_vrf0, + src_addr_vrf0, + rx_table_id=0, + server_table_id=0, + is_ipv6=1, + is_add=0) + + # duplicate delete + self.vapi.dhcp_proxy_config(server_addr_vrf0, + src_addr_vrf0, rx_table_id=0, server_table_id=0, is_ipv6=1, -- cgit 1.2.3-korg From 1500254bee11355bbd69cc1dd9705be4f002f2bd Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Sun, 10 Sep 2017 04:39:11 -0700 Subject: FIB table add/delete API part 2; - this adds the code to create an IP and MPLS table via the API. - but the enforcement that the table must be created before it is used is still missing, this is so that CSIT can pass. Change-Id: Id124d884ade6cb7da947225200e3bb193454c555 Signed-off-by: Neale Ranns --- src/plugins/nat/nat.c | 17 +- src/plugins/nat/nat64.c | 13 +- src/vnet/classify/vnet_classify.c | 16 +- src/vnet/dhcp/dhcp4_proxy_node.c | 9 +- src/vnet/dhcp/dhcp6_proxy_node.c | 9 +- src/vnet/dhcp/dhcp_proxy.c | 19 ++- src/vnet/dpo/lookup_dpo.c | 20 ++- src/vnet/dpo/mpls_label_dpo.c | 12 +- src/vnet/ethernet/arp.c | 127 +++++++++++---- src/vnet/fib/fib_api.h | 1 - src/vnet/fib/fib_entry.c | 15 +- src/vnet/fib/fib_entry.h | 1 + src/vnet/fib/fib_entry_src_mpls.c | 7 +- src/vnet/fib/fib_table.c | 43 +++-- src/vnet/fib/fib_table.h | 32 +++- src/vnet/fib/fib_test.c | 27 ++-- src/vnet/fib/ip4_fib.c | 41 +++-- src/vnet/fib/ip4_fib.h | 5 +- src/vnet/fib/ip6_fib.c | 41 +++-- src/vnet/fib/ip6_fib.h | 5 +- src/vnet/fib/mpls_fib.c | 16 +- src/vnet/fib/mpls_fib.h | 5 +- src/vnet/interface_api.c | 177 ++++++++++++++++---- src/vnet/ip/ip.h | 7 + src/vnet/ip/ip4.h | 13 ++ src/vnet/ip/ip4_forward.c | 101 +----------- src/vnet/ip/ip4_source_and_port_range_check.c | 11 +- src/vnet/ip/ip6.h | 13 ++ src/vnet/ip/ip6_forward.c | 103 +----------- src/vnet/ip/ip6_neighbor.c | 108 +++++++++---- src/vnet/ip/ip_api.c | 122 +++++++++++--- src/vnet/ip/lookup.c | 225 ++++++++++++++++++++++++++ src/vnet/lisp-gpe/interface.c | 11 +- src/vnet/lisp-gpe/lisp_gpe_fwd_entry.c | 9 +- src/vnet/lisp-gpe/lisp_gpe_sub_interface.c | 11 +- src/vnet/mfib/ip4_mfib.c | 12 +- src/vnet/mfib/ip4_mfib.h | 5 +- src/vnet/mfib/ip6_mfib.c | 12 +- src/vnet/mfib/ip6_mfib.h | 5 +- src/vnet/mfib/mfib_entry.c | 11 ++ src/vnet/mfib/mfib_entry.h | 2 + src/vnet/mfib/mfib_table.c | 88 ++++++++-- src/vnet/mfib/mfib_table.h | 29 +++- src/vnet/mfib/mfib_test.c | 11 +- src/vnet/mfib/mfib_types.h | 8 +- src/vnet/mpls/interface.c | 26 ++- src/vnet/mpls/mpls.c | 76 ++++++++- src/vnet/mpls/mpls.h | 16 +- src/vnet/mpls/mpls_api.c | 66 ++++++-- src/vnet/srv6/sr_policy_rewrite.c | 6 +- src/vnet/srv6/sr_steering.c | 6 +- src/vpp/api/api.c | 5 +- src/vpp/api/custom_dump.c | 3 - test/test_dhcp.py | 24 ++- test/test_gre.py | 8 +- test/test_ip4.py | 11 +- test/test_ip4_vrf_multi_instance.py | 4 +- test/test_ip6.py | 7 +- test/test_ip6_vrf_multi_instance.py | 4 +- test/test_ip_mcast.py | 98 ++++++++++- test/test_mpls.py | 48 +++++- test/test_nat.py | 13 ++ test/test_neighbor.py | 66 +++++++- test/vpp_ip_route.py | 73 +++++++++ test/vpp_papi_provider.py | 46 ++++-- 65 files changed, 1643 insertions(+), 538 deletions(-) (limited to 'src/vnet/dhcp/dhcp_proxy.c') diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index aa7ef10a..8aecac6d 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -167,7 +167,8 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id) ap->addr = *addr; if (vrf_id != ~0) ap->fib_index = - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id); + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, + FIB_SOURCE_PLUGIN_HI); else ap->fib_index = ~0; #define _(N, i, n, s) \ @@ -625,7 +626,8 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, return VNET_API_ERROR_INVALID_VALUE; fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, - vrf_id); + vrf_id, + FIB_SOURCE_PLUGIN_HI); /* Find external address in allocated addresses and reserve port for address and port pair mapping when dynamic translations enabled */ @@ -754,7 +756,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, if (!m) return VNET_API_ERROR_NO_SUCH_ENTRY; - fib_table_unlock (m->fib_index, FIB_PROTOCOL_IP4); + fib_table_unlock (m->fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_HI); /* Free external address port */ if (!sm->static_mapping_only) @@ -874,7 +876,8 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) } if (a->fib_index != ~0) - fib_table_unlock(a->fib_index, FIB_PROTOCOL_IP4); + fib_table_unlock(a->fib_index, FIB_PROTOCOL_IP4, + FIB_SOURCE_PLUGIN_HI); /* Delete sessions using address */ if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports) @@ -2151,10 +2154,12 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) sm->max_translations_per_user = max_translations_per_user; sm->outside_vrf_id = outside_vrf_id; sm->outside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, - outside_vrf_id); + outside_vrf_id, + FIB_SOURCE_PLUGIN_HI); sm->inside_vrf_id = inside_vrf_id; sm->inside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, - inside_vrf_id); + inside_vrf_id, + FIB_SOURCE_PLUGIN_HI); sm->static_mapping_only = static_mapping_only; sm->static_mapping_connection_tracking = static_mapping_connection_tracking; diff --git a/src/plugins/nat/nat64.c b/src/plugins/nat/nat64.c index b04901fa..bfcfa9b3 100644 --- a/src/plugins/nat/nat64.c +++ b/src/plugins/nat/nat64.c @@ -107,7 +107,8 @@ nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add) a->fib_index = 0; if (vrf_id != ~0) a->fib_index = - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id); + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id, + FIB_SOURCE_PLUGIN_HI); #define _(N, i, n, s) \ clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535); foreach_snat_protocol @@ -119,7 +120,8 @@ nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add) return VNET_API_ERROR_NO_SUCH_ENTRY; if (a->fib_index) - fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP6); + fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP6, + FIB_SOURCE_PLUGIN_HI); #define _(N, id, n, s) \ clib_bitmap_free (a->busy_##n##_port_bitmap); @@ -353,8 +355,8 @@ nat64_add_del_static_bib_entry (ip6_address_t * in_addr, { nat64_main_t *nm = &nat64_main; nat64_db_bib_entry_t *bibe; - u32 fib_index = - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id); + u32 fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id, + FIB_SOURCE_PLUGIN_HI); snat_protocol_t p = ip_proto_to_snat_proto (proto); ip46_address_t addr; int i; @@ -644,7 +646,8 @@ nat64_add_del_prefix (ip6_address_t * prefix, u8 plen, u32 vrf_id, u8 is_add) { vec_add2 (nm->pref64, p, 1); p->fib_index = - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id); + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id, + FIB_SOURCE_PLUGIN_HI); p->vrf_id = vrf_id; } diff --git a/src/vnet/classify/vnet_classify.c b/src/vnet/classify/vnet_classify.c index 879fba3c..57d86748 100644 --- a/src/vnet/classify/vnet_classify.c +++ b/src/vnet/classify/vnet_classify.c @@ -368,10 +368,10 @@ vnet_classify_entry_claim_resource (vnet_classify_entry_t *e) switch (e->action) { case CLASSIFY_ACTION_SET_IP4_FIB_INDEX: - fib_table_lock (e->metadata, FIB_PROTOCOL_IP4); + fib_table_lock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY); break; case CLASSIFY_ACTION_SET_IP6_FIB_INDEX: - fib_table_lock (e->metadata, FIB_PROTOCOL_IP6); + fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY); break; } } @@ -382,10 +382,10 @@ vnet_classify_entry_release_resource (vnet_classify_entry_t *e) switch (e->action) { case CLASSIFY_ACTION_SET_IP4_FIB_INDEX: - fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4); + fib_table_unlock (e->metadata, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY); break; case CLASSIFY_ACTION_SET_IP6_FIB_INDEX: - fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6); + fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY); break; } } @@ -2096,9 +2096,13 @@ int vnet_classify_add_del_session (vnet_classify_main_t * cm, e->flags = 0; e->action = action; if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX) - e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, metadata); + e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, + metadata, + FIB_SOURCE_CLASSIFY); else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX) - e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, metadata); + e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, + metadata, + FIB_SOURCE_CLASSIFY); else e->metadata = 0; diff --git a/src/vnet/dhcp/dhcp4_proxy_node.c b/src/vnet/dhcp/dhcp4_proxy_node.c index 1b59cdea..339a7885 100644 --- a/src/vnet/dhcp/dhcp4_proxy_node.c +++ b/src/vnet/dhcp/dhcp4_proxy_node.c @@ -785,7 +785,8 @@ dhcp4_proxy_set_server (ip46_address_t *addr, return VNET_API_ERROR_INVALID_SRC_ADDRESS; rx_fib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4, - rx_table_id); + rx_table_id, + FIB_SOURCE_DHCP); if (is_del) { @@ -795,7 +796,7 @@ dhcp4_proxy_set_server (ip46_address_t *addr, fib_table_entry_special_remove(rx_fib_index, &all_1s, FIB_SOURCE_DHCP); - fib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP4); + fib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_DHCP); } } else @@ -808,10 +809,10 @@ dhcp4_proxy_set_server (ip46_address_t *addr, &all_1s, FIB_SOURCE_DHCP, FIB_ENTRY_FLAG_LOCAL); - fib_table_lock (rx_fib_index, FIB_PROTOCOL_IP4); + fib_table_lock (rx_fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_DHCP); } } - fib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP4); + fib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_DHCP); return (rc); } diff --git a/src/vnet/dhcp/dhcp6_proxy_node.c b/src/vnet/dhcp/dhcp6_proxy_node.c index 9c2f5220..ce7a8fca 100644 --- a/src/vnet/dhcp/dhcp6_proxy_node.c +++ b/src/vnet/dhcp/dhcp6_proxy_node.c @@ -841,7 +841,8 @@ dhcp6_proxy_set_server (ip46_address_t *addr, return VNET_API_ERROR_INVALID_SRC_ADDRESS; rx_fib_index = mfib_table_find_or_create_and_lock(FIB_PROTOCOL_IP6, - rx_table_id); + rx_table_id, + MFIB_SOURCE_DHCP); if (is_del) { @@ -851,7 +852,7 @@ dhcp6_proxy_set_server (ip46_address_t *addr, mfib_table_entry_delete(rx_fib_index, &all_dhcp_servers, MFIB_SOURCE_DHCP); - mfib_table_unlock(rx_fib_index, FIB_PROTOCOL_IP6); + mfib_table_unlock(rx_fib_index, FIB_PROTOCOL_IP6, MFIB_SOURCE_DHCP); } } else @@ -885,11 +886,11 @@ dhcp6_proxy_set_server (ip46_address_t *addr, MFIB_SOURCE_DHCP, MFIB_RPF_ID_NONE, MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF); - mfib_table_lock(rx_fib_index, FIB_PROTOCOL_IP6); + mfib_table_lock(rx_fib_index, FIB_PROTOCOL_IP6, MFIB_SOURCE_DHCP); } } - mfib_table_unlock(rx_fib_index, FIB_PROTOCOL_IP6); + mfib_table_unlock(rx_fib_index, FIB_PROTOCOL_IP6, MFIB_SOURCE_DHCP); return (rc); } diff --git a/src/vnet/dhcp/dhcp_proxy.c b/src/vnet/dhcp/dhcp_proxy.c index ba7f354e..1784906b 100644 --- a/src/vnet/dhcp/dhcp_proxy.c +++ b/src/vnet/dhcp/dhcp_proxy.c @@ -29,9 +29,9 @@ dhcp_proxy_rx_table_lock (fib_protocol_t proto, u32 fib_index) { if (FIB_PROTOCOL_IP4 == proto) - fib_table_lock(fib_index, proto); + fib_table_lock(fib_index, proto, FIB_SOURCE_DHCP); else - mfib_table_lock(fib_index, proto); + mfib_table_lock(fib_index, proto, MFIB_SOURCE_DHCP); } static void @@ -39,9 +39,9 @@ dhcp_proxy_rx_table_unlock (fib_protocol_t proto, u32 fib_index) { if (FIB_PROTOCOL_IP4 == proto) - fib_table_unlock(fib_index, proto); + fib_table_unlock(fib_index, proto, FIB_SOURCE_DHCP); else - mfib_table_unlock(fib_index, proto); + mfib_table_unlock(fib_index, proto, MFIB_SOURCE_DHCP); } u32 @@ -169,7 +169,7 @@ dhcp_proxy_server_del (fib_protocol_t proto, if (~0 != index) { server = &proxy->dhcp_servers[index]; - fib_table_unlock (server->server_fib_index, proto); + fib_table_unlock (server->server_fib_index, proto, FIB_SOURCE_DHCP); vec_del1(proxy->dhcp_servers, index); @@ -228,7 +228,8 @@ dhcp_proxy_server_add (fib_protocol_t proto, dhcp_server_t server = { .dhcp_server = *addr, .server_fib_index = fib_table_find_or_create_and_lock(proto, - server_table_id), + server_table_id, + FIB_SOURCE_DHCP), }; vec_add1(proxy->dhcp_servers, server); @@ -297,9 +298,11 @@ int dhcp_proxy_set_vss (fib_protocol_t proto, int rc = 0; if (proto == FIB_PROTOCOL_IP4) - rx_fib_index = fib_table_find_or_create_and_lock(proto, tbl_id); + rx_fib_index = fib_table_find_or_create_and_lock(proto, tbl_id, + FIB_SOURCE_DHCP); else - rx_fib_index = mfib_table_find_or_create_and_lock(proto, tbl_id); + rx_fib_index = mfib_table_find_or_create_and_lock(proto, tbl_id, + MFIB_SOURCE_DHCP); v = dhcp_get_vss_info(dm, rx_fib_index, proto); if (NULL != v) diff --git a/src/vnet/dpo/lookup_dpo.c b/src/vnet/dpo/lookup_dpo.c index 26363a2f..af189eda 100644 --- a/src/vnet/dpo/lookup_dpo.c +++ b/src/vnet/dpo/lookup_dpo.c @@ -135,11 +135,15 @@ lookup_dpo_add_or_lock_w_fib_index (fib_node_index_t fib_index, { if (LOOKUP_UNICAST == cast) { - fib_table_lock(fib_index, dpo_proto_to_fib(proto)); + fib_table_lock(fib_index, + dpo_proto_to_fib(proto), + FIB_SOURCE_RR); } else { - mfib_table_lock(fib_index, dpo_proto_to_fib(proto)); + mfib_table_lock(fib_index, + dpo_proto_to_fib(proto), + MFIB_SOURCE_RR); } } lookup_dpo_add_or_lock_i(fib_index, proto, cast, input, table_config, dpo); @@ -161,13 +165,15 @@ lookup_dpo_add_or_lock_w_table_id (u32 table_id, { fib_index = fib_table_find_or_create_and_lock(dpo_proto_to_fib(proto), - table_id); + table_id, + FIB_SOURCE_RR); } else { fib_index = mfib_table_find_or_create_and_lock(dpo_proto_to_fib(proto), - table_id); + table_id, + MFIB_SOURCE_RR); } } @@ -238,12 +244,14 @@ lookup_dpo_unlock (dpo_id_t *dpo) if (LOOKUP_UNICAST == lkd->lkd_cast) { fib_table_unlock(lkd->lkd_fib_index, - dpo_proto_to_fib(lkd->lkd_proto)); + dpo_proto_to_fib(lkd->lkd_proto), + FIB_SOURCE_RR); } else { mfib_table_unlock(lkd->lkd_fib_index, - dpo_proto_to_fib(lkd->lkd_proto)); + dpo_proto_to_fib(lkd->lkd_proto), + MFIB_SOURCE_RR); } } pool_put(lookup_dpo_pool, lkd); diff --git a/src/vnet/dpo/mpls_label_dpo.c b/src/vnet/dpo/mpls_label_dpo.c index b178a902..2a6e7dd5 100644 --- a/src/vnet/dpo/mpls_label_dpo.c +++ b/src/vnet/dpo/mpls_label_dpo.c @@ -105,10 +105,18 @@ format_mpls_label_dpo (u8 *s, va_list *args) mpls_label_dpo_t *mld; u32 ii; - mld = mpls_label_dpo_get(index); - s = format(s, "mpls-label:[%d]:", index); + if (pool_is_free_index(mpls_label_dpo_pool, index)) + { + /* + * the packet trace can be printed after the DPO has been deleted + */ + return (s); + } + + mld = mpls_label_dpo_get(index); + for (ii = 0; ii < mld->mld_n_labels; ii++) { hdr.label_exp_s_ttl = diff --git a/src/vnet/ethernet/arp.c b/src/vnet/ethernet/arp.c index c84ff47b..08e91373 100644 --- a/src/vnet/ethernet/arp.c +++ b/src/vnet/ethernet/arp.c @@ -522,6 +522,24 @@ arp_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai) } } +static void +arp_adj_fib_add (ethernet_arp_ip4_entry_t * e, uint32_t fib_index) +{ + fib_prefix_t pfx = { + .fp_len = 32, + .fp_proto = FIB_PROTOCOL_IP4, + .fp_addr.ip4 = e->ip4_address, + }; + + e->fib_entry_index = + fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ, + FIB_ENTRY_FLAG_ATTACHED, + DPO_PROTO_IP4, &pfx.fp_addr, + e->sw_if_index, ~0, 1, NULL, + FIB_ROUTE_PATH_FLAG_NONE); + fib_table_lock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_ADJ); +} + int vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm, vnet_arp_set_ip4_over_ethernet_rpc_args_t @@ -576,21 +594,9 @@ vnet_arp_set_ip4_over_ethernet_internal (vnet_main_t * vnm, if (!is_no_fib_entry) { - fib_prefix_t pfx = { - .fp_len = 32, - .fp_proto = FIB_PROTOCOL_IP4, - .fp_addr.ip4 = a->ip4, - }; - u32 fib_index; - - fib_index = - ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index); - e->fib_entry_index = - fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ, - FIB_ENTRY_FLAG_ATTACHED, - DPO_PROTO_IP4, &pfx.fp_addr, - e->sw_if_index, ~0, 1, NULL, - FIB_ROUTE_PATH_FLAG_NONE); + arp_adj_fib_add (e, + ip4_fib_table_get_index_for_sw_if_index + (e->sw_if_index)); } else { @@ -1561,6 +1567,65 @@ arp_add_del_interface_address (ip4_main_t * im, } } +void +arp_adj_fib_remove (ethernet_arp_ip4_entry_t * e, uint32_t fib_index) +{ + if (FIB_NODE_INDEX_INVALID != e->fib_entry_index) + { + fib_prefix_t pfx = { + .fp_len = 32, + .fp_proto = FIB_PROTOCOL_IP4, + .fp_addr.ip4 = e->ip4_address, + }; + u32 fib_index; + + fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index); + + fib_table_entry_path_remove (fib_index, &pfx, + FIB_SOURCE_ADJ, + DPO_PROTO_IP4, + &pfx.fp_addr, + e->sw_if_index, ~0, 1, + FIB_ROUTE_PATH_FLAG_NONE); + fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_ADJ); + } +} + +static void +arp_table_bind (ip4_main_t * im, + uword opaque, + u32 sw_if_index, u32 new_fib_index, u32 old_fib_index) +{ + ethernet_arp_main_t *am = ðernet_arp_main; + ethernet_arp_interface_t *eai; + ethernet_arp_ip4_entry_t *e; + hash_pair_t *pair; + + /* + * the IP table that the interface is bound to has changed. + * reinstall all the adj fibs. + */ + + if (vec_len (am->ethernet_arp_by_sw_if_index) <= sw_if_index) + return; + + eai = &am->ethernet_arp_by_sw_if_index[sw_if_index]; + + /* *INDENT-OFF* */ + hash_foreach_pair (pair, eai->arp_entries, + ({ + e = pool_elt_at_index(am->ip4_entry_pool, + pair->value[0]); + /* + * remove the adj-fib from the old table and add to the new + */ + arp_adj_fib_remove(e, old_fib_index); + arp_adj_fib_add(e, new_fib_index); + })); + /* *INDENT-ON* */ + +} + static clib_error_t * ethernet_arp_init (vlib_main_t * vm) { @@ -1606,6 +1671,11 @@ ethernet_arp_init (vlib_main_t * vm) cb.function_opaque = 0; vec_add1 (im->add_del_interface_address_callbacks, cb); + ip4_table_bind_callback_t cbt; + cbt.function = arp_table_bind; + cbt.function_opaque = 0; + vec_add1 (im->table_bind_callbacks, cbt); + return 0; } @@ -1616,24 +1686,9 @@ arp_entry_free (ethernet_arp_interface_t * eai, ethernet_arp_ip4_entry_t * e) { ethernet_arp_main_t *am = ðernet_arp_main; - if (FIB_NODE_INDEX_INVALID != e->fib_entry_index) - { - fib_prefix_t pfx = { - .fp_len = 32, - .fp_proto = FIB_PROTOCOL_IP4, - .fp_addr.ip4 = e->ip4_address, - }; - u32 fib_index; - - fib_index = ip4_fib_table_get_index_for_sw_if_index (e->sw_if_index); - - fib_table_entry_path_remove (fib_index, &pfx, - FIB_SOURCE_ADJ, - DPO_PROTO_IP4, - &pfx.fp_addr, - e->sw_if_index, ~0, 1, - FIB_ROUTE_PATH_FLAG_NONE); - } + arp_adj_fib_remove (e, + ip4_fib_table_get_index_for_sw_if_index + (e->sw_if_index)); hash_unset (eai->arp_entries, e->ip4_address.as_u32); pool_put (am->ip4_entry_pool, e); } @@ -1693,7 +1748,11 @@ vnet_arp_flush_ip4_over_ethernet_internal (vnet_main_t * vnm, * does in response to interface events. unset is only done * by the control plane. */ - if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC) + if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_STATIC) + { + e->flags &= ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC; + } + else if (e->flags & ETHERNET_ARP_IP4_ENTRY_FLAG_DYNAMIC) { arp_entry_free (eai, e); } diff --git a/src/vnet/fib/fib_api.h b/src/vnet/fib/fib_api.h index d07d6cae..f5a107ca 100644 --- a/src/vnet/fib/fib_api.h +++ b/src/vnet/fib/fib_api.h @@ -23,7 +23,6 @@ add_del_route_check (fib_protocol_t table_proto, u32 next_hop_sw_if_index, dpo_proto_t next_hop_table_proto, u32 next_hop_table_id, - u8 create_missing_tables, u8 is_rpf_id, u32 * fib_index, u32 * next_hop_fib_index); diff --git a/src/vnet/fib/fib_entry.c b/src/vnet/fib/fib_entry.c index 2027f2be..4cb6cf60 100644 --- a/src/vnet/fib/fib_entry.c +++ b/src/vnet/fib/fib_entry.c @@ -89,6 +89,17 @@ fib_entry_get_default_chain_type (const fib_entry_t *fib_entry) return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4); } +u8 * +format_fib_source (u8 * s, va_list * args) +{ + fib_source_t source = va_arg (*args, int); + + s = format (s, "\n src:%s ", + fib_source_names[source]); + + return (s); +} + u8 * format_fib_entry (u8 * s, va_list * args) { @@ -114,8 +125,8 @@ format_fib_entry (u8 * s, va_list * args) FOR_EACH_SRC_ADDED(fib_entry, src, source, ({ - s = format (s, "\n src:%s ", - fib_source_names[source]); + s = format (s, "\n src:%U ", + format_fib_source, source); s = fib_entry_src_format(fib_entry, source, s); s = format (s, " refs:%d ", src->fes_ref_count); if (FIB_ENTRY_FLAG_NONE != src->fes_entry_flags) { diff --git a/src/vnet/fib/fib_entry.h b/src/vnet/fib/fib_entry.h index 93b8016d..2f6e37fe 100644 --- a/src/vnet/fib/fib_entry.h +++ b/src/vnet/fib/fib_entry.h @@ -431,6 +431,7 @@ typedef struct fib_entry_t_ { #define FIB_ENTRY_FORMAT_DETAIL2 (0x2) extern u8 *format_fib_entry (u8 * s, va_list * args); +extern u8 *format_fib_source (u8 * s, va_list * args); extern fib_node_index_t fib_entry_create_special(u32 fib_index, const fib_prefix_t *prefix, diff --git a/src/vnet/fib/fib_entry_src_mpls.c b/src/vnet/fib/fib_entry_src_mpls.c index a616458f..6fdd5c0a 100644 --- a/src/vnet/fib/fib_entry_src_mpls.c +++ b/src/vnet/fib/fib_entry_src_mpls.c @@ -94,7 +94,9 @@ fib_entry_src_mpls_set_data (fib_entry_src_t *src, fib_table_entry_delete_index(src->mpls.fesm_lfes[eos], FIB_SOURCE_SPECIAL); } - fib_table_unlock(MPLS_FIB_DEFAULT_TABLE_ID, FIB_PROTOCOL_MPLS); + fib_table_unlock(MPLS_FIB_DEFAULT_TABLE_ID, + FIB_PROTOCOL_MPLS, + FIB_SOURCE_MPLS); src->mpls.fesm_label = label; } else @@ -113,7 +115,8 @@ fib_entry_src_mpls_set_data (fib_entry_src_t *src, { fib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_MPLS, - MPLS_FIB_DEFAULT_TABLE_ID); + MPLS_FIB_DEFAULT_TABLE_ID, + FIB_SOURCE_MPLS); } else { diff --git a/src/vnet/fib/fib_table.c b/src/vnet/fib/fib_table.c index 6b6cc5cb..75d15628 100644 --- a/src/vnet/fib/fib_table.c +++ b/src/vnet/fib/fib_table.c @@ -1039,7 +1039,8 @@ fib_table_find (fib_protocol_t proto, u32 fib_table_find_or_create_and_lock (fib_protocol_t proto, - u32 table_id) + u32 table_id, + fib_source_t src) { fib_table_t *fib_table; fib_node_index_t fi; @@ -1047,13 +1048,13 @@ fib_table_find_or_create_and_lock (fib_protocol_t proto, switch (proto) { case FIB_PROTOCOL_IP4: - fi = ip4_fib_table_find_or_create_and_lock(table_id); + fi = ip4_fib_table_find_or_create_and_lock(table_id, src); break; case FIB_PROTOCOL_IP6: - fi = ip6_fib_table_find_or_create_and_lock(table_id); + fi = ip6_fib_table_find_or_create_and_lock(table_id, src); break; case FIB_PROTOCOL_MPLS: - fi = mpls_fib_table_find_or_create_and_lock(table_id); + fi = mpls_fib_table_find_or_create_and_lock(table_id, src); break; default: return (~0); @@ -1070,6 +1071,7 @@ fib_table_find_or_create_and_lock (fib_protocol_t proto, u32 fib_table_create_and_lock (fib_protocol_t proto, + fib_source_t src, const char *const fmt, ...) { @@ -1082,13 +1084,13 @@ fib_table_create_and_lock (fib_protocol_t proto, switch (proto) { case FIB_PROTOCOL_IP4: - fi = ip4_fib_table_create_and_lock(); + fi = ip4_fib_table_create_and_lock(src); break; case FIB_PROTOCOL_IP6: - fi = ip6_fib_table_create_and_lock(); + fi = ip6_fib_table_create_and_lock(src); break; case FIB_PROTOCOL_MPLS: - fi = mpls_fib_table_create_and_lock(); + fi = mpls_fib_table_create_and_lock(src); break; default: return (~0); @@ -1143,26 +1145,43 @@ fib_table_walk (u32 fib_index, void fib_table_unlock (u32 fib_index, - fib_protocol_t proto) + fib_protocol_t proto, + fib_source_t source) { fib_table_t *fib_table; fib_table = fib_table_get(fib_index, proto); - fib_table->ft_locks--; + fib_table->ft_locks[source]--; + fib_table->ft_locks[FIB_TABLE_TOTAL_LOCKS]--; - if (0 == fib_table->ft_locks) + if (0 == fib_table->ft_locks[source]) { + /* + * The source no longer needs the table. flush any routes + * from it just in case + */ + fib_table_flush(fib_index, proto, source); + } + + if (0 == fib_table->ft_locks[FIB_TABLE_TOTAL_LOCKS]) + { + /* + * no more locak from any source - kill it + */ fib_table_destroy(fib_table); } } + void fib_table_lock (u32 fib_index, - fib_protocol_t proto) + fib_protocol_t proto, + fib_source_t source) { fib_table_t *fib_table; fib_table = fib_table_get(fib_index, proto); - fib_table->ft_locks++; + fib_table->ft_locks[source]++; + fib_table->ft_locks[FIB_TABLE_TOTAL_LOCKS]++; } u32 diff --git a/src/vnet/fib/fib_table.h b/src/vnet/fib/fib_table.h index 579740e9..6b7011b3 100644 --- a/src/vnet/fib/fib_table.h +++ b/src/vnet/fib/fib_table.h @@ -22,6 +22,12 @@ #include #include +/** + * Keep a lock per-source and a total + */ +#define FIB_TABLE_N_LOCKS (FIB_SOURCE_MAX+1) +#define FIB_TABLE_TOTAL_LOCKS FIB_SOURCE_MAX + /** * @brief * A protocol Independent FIB table @@ -34,9 +40,9 @@ typedef struct fib_table_t_ fib_protocol_t ft_proto; /** - * number of locks on the table + * per-source number of locks on the table */ - u16 ft_locks; + u16 ft_locks[FIB_TABLE_N_LOCKS]; /** * Table ID (hash key) for this FIB. @@ -628,9 +634,13 @@ extern u32 fib_table_find(fib_protocol_t proto, u32 table_id); * * @return fib_index * The index of the FIB + * + * @param source + * The ID of the client/source. */ extern u32 fib_table_find_or_create_and_lock(fib_protocol_t proto, - u32 table_id); + u32 table_id, + fib_source_t source); /** * @brief @@ -643,10 +653,14 @@ extern u32 fib_table_find_or_create_and_lock(fib_protocol_t proto, * @param fmt * A string to describe the table * + * @param source + * The ID of the client/source. + * * @return fib_index * The index of the FIB */ extern u32 fib_table_create_and_lock(fib_protocol_t proto, + fib_source_t source, const char *const fmt, ...); @@ -704,9 +718,13 @@ extern void fib_table_set_flow_hash_config(u32 fib_index, * * @paran proto * The protocol of the FIB (and thus the entries therein) + * + * @param source + * The ID of the client/source. */ extern void fib_table_unlock(u32 fib_index, - fib_protocol_t proto); + fib_protocol_t proto, + fib_source_t source); /** * @brief @@ -718,9 +736,13 @@ extern void fib_table_unlock(u32 fib_index, * * @paran proto * The protocol of the FIB (and thus the entries therein) + * + * @param source + * The ID of the client/source. */ extern void fib_table_lock(u32 fib_index, - fib_protocol_t proto); + fib_protocol_t proto, + fib_source_t source); /** * @brief diff --git a/src/vnet/fib/fib_test.c b/src/vnet/fib/fib_test.c index 6867cca8..572d7f0d 100644 --- a/src/vnet/fib/fib_test.c +++ b/src/vnet/fib/fib_test.c @@ -739,7 +739,8 @@ fib_test_v4 (void) lb_count = pool_elts(load_balance_pool); /* Find or create FIB table 11 */ - fib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4, 11); + fib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4, 11, + FIB_SOURCE_API); for (ii = 0; ii < 4; ii++) { @@ -4150,7 +4151,7 @@ fib_test_v4 (void) FIB_SOURCE_INTERFACE)), "NO INterface Source'd prefixes"); - fib_table_unlock(fib_index, FIB_PROTOCOL_IP4); + fib_table_unlock(fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_API); FIB_TEST((0 == fib_path_list_db_size()), "path list DB population:%d", fib_path_list_db_size()); @@ -4201,7 +4202,8 @@ fib_test_v6 (void) dpo_drop = drop_dpo_get(DPO_PROTO_IP6); /* Find or create FIB table 11 */ - fib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP6, 11); + fib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP6, 11, + FIB_SOURCE_API); for (ii = 0; ii < 4; ii++) { @@ -5025,7 +5027,7 @@ fib_test_v6 (void) /* * now remove the VRF */ - fib_table_unlock(fib_index, FIB_PROTOCOL_IP6); + fib_table_unlock(fib_index, FIB_PROTOCOL_IP6, FIB_SOURCE_API); FIB_TEST((0 == fib_path_list_db_size()), "path list DB population:%d", fib_path_list_db_size()); @@ -5157,7 +5159,9 @@ fib_test_ae (void) */ u32 import_fib_index1; - import_fib_index1 = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4, 11); + import_fib_index1 = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4, + 11, + FIB_SOURCE_CLI); /* * Add an attached route in the import FIB @@ -5233,7 +5237,8 @@ fib_test_ae (void) */ u32 import_fib_index2; - import_fib_index2 = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4, 12); + import_fib_index2 = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4, 12, + FIB_SOURCE_CLI); /* * Add an attached route in the import FIB @@ -5595,8 +5600,8 @@ fib_test_ae (void) &local_pfx, FIB_SOURCE_API); - fib_table_unlock(import_fib_index1, FIB_PROTOCOL_IP4); - fib_table_unlock(import_fib_index2, FIB_PROTOCOL_IP4); + fib_table_unlock(import_fib_index1, FIB_PROTOCOL_IP4, FIB_SOURCE_CLI); + fib_table_unlock(import_fib_index2, FIB_PROTOCOL_IP4, FIB_SOURCE_CLI); FIB_TEST((0 == adj_nbr_db_size()), "ADJ DB size is %d", adj_nbr_db_size()); @@ -8168,9 +8173,10 @@ lfib_test (void) /* * MPLS enable an interface so we get the MPLS table created */ + mpls_table_create(MPLS_FIB_DEFAULT_TABLE_ID, FIB_SOURCE_API); mpls_sw_interface_enable_disable(&mpls_main, tm->hw[0]->sw_if_index, - 1); + 1, 1); ip46_address_t nh_10_10_10_1 = { .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a01), @@ -8662,7 +8668,8 @@ lfib_test (void) */ mpls_sw_interface_enable_disable(&mpls_main, tm->hw[0]->sw_if_index, - 0); + 0, 1); + mpls_table_delete(MPLS_FIB_DEFAULT_TABLE_ID, FIB_SOURCE_API); FIB_TEST(lb_count == pool_elts(load_balance_pool), "Load-balance resources freed %d of %d", diff --git a/src/vnet/fib/ip4_fib.c b/src/vnet/fib/ip4_fib.c index d563bafd..865e2dd5 100644 --- a/src/vnet/fib/ip4_fib.c +++ b/src/vnet/fib/ip4_fib.c @@ -101,7 +101,8 @@ static const ip4_fib_table_special_prefix_t ip4_specials[] = { static u32 -ip4_create_fib_with_table_id (u32 table_id) +ip4_create_fib_with_table_id (u32 table_id, + fib_source_t src) { fib_table_t *fib_table; ip4_fib_t *v4_fib; @@ -128,7 +129,7 @@ ip4_create_fib_with_table_id (u32 table_id) v4_fib->fwd_classify_table_index = ~0; v4_fib->rev_classify_table_index = ~0; - fib_table_lock(fib_table->ft_index, FIB_PROTOCOL_IP4); + fib_table_lock(fib_table->ft_index, FIB_PROTOCOL_IP4, src); ip4_mtrie_init(&v4_fib->mtrie); @@ -198,23 +199,24 @@ ip4_fib_table_destroy (u32 fib_index) u32 -ip4_fib_table_find_or_create_and_lock (u32 table_id) +ip4_fib_table_find_or_create_and_lock (u32 table_id, + fib_source_t src) { u32 index; index = ip4_fib_index_from_table_id(table_id); if (~0 == index) - return ip4_create_fib_with_table_id(table_id); + return ip4_create_fib_with_table_id(table_id, src); - fib_table_lock(index, FIB_PROTOCOL_IP4); + fib_table_lock(index, FIB_PROTOCOL_IP4, src); return (index); } u32 -ip4_fib_table_create_and_lock (void) +ip4_fib_table_create_and_lock (fib_source_t src) { - return (ip4_create_fib_with_table_id(~0)); + return (ip4_create_fib_with_table_id(~0, src)); } u32 @@ -525,17 +527,32 @@ ip4_show_fib (vlib_main_t * vm, pool_foreach (fib_table, im4->fibs, ({ ip4_fib_t *fib = pool_elt_at_index(im4->v4_fibs, fib_table->ft_index); + fib_source_t source; + u8 *s = NULL; if (table_id >= 0 && table_id != (int)fib->table_id) continue; if (fib_index != ~0 && fib_index != (int)fib->index) continue; - vlib_cli_output (vm, "%U, fib_index:%d, flow hash:[%U] locks:%d", - format_fib_table_name, fib->index, FIB_PROTOCOL_IP4, - fib->index, - format_ip_flow_hash_config, fib_table->ft_flow_hash_config, - fib_table->ft_locks); + s = format(s, "%U, fib_index:%d, flow hash:[%U] locks:[", + format_fib_table_name, fib->index, + FIB_PROTOCOL_IP4, + fib->index, + format_ip_flow_hash_config, + fib_table->ft_flow_hash_config); + FOR_EACH_FIB_SOURCE(source) + { + if (0 != fib_table->ft_locks[source]) + { + s = format(s, "%U:%d, ", + format_fib_source, source, + fib_table->ft_locks[source]); + } + } + s = format (s, "]"); + vlib_cli_output (vm, "%V", s); + vec_free(s); /* Show summary? */ if (! verbose) diff --git a/src/vnet/fib/ip4_fib.h b/src/vnet/fib/ip4_fib.h index 006163b4..495b45cc 100644 --- a/src/vnet/fib/ip4_fib.h +++ b/src/vnet/fib/ip4_fib.h @@ -127,8 +127,9 @@ ip4_fib_lookup (ip4_main_t * im, u32 sw_if_index, ip4_address_t * dst) * @returns A pointer to the retrieved or created fib. * */ -extern u32 ip4_fib_table_find_or_create_and_lock(u32 table_id); -extern u32 ip4_fib_table_create_and_lock(void); +extern u32 ip4_fib_table_find_or_create_and_lock(u32 table_id, + fib_source_t src); +extern u32 ip4_fib_table_create_and_lock(fib_source_t src); static inline diff --git a/src/vnet/fib/ip6_fib.c b/src/vnet/fib/ip6_fib.c index 8fde6f9f..3ddb8453 100644 --- a/src/vnet/fib/ip6_fib.c +++ b/src/vnet/fib/ip6_fib.c @@ -50,7 +50,8 @@ vnet_ip6_fib_init (u32 fib_index) } static u32 -create_fib_with_table_id (u32 table_id) +create_fib_with_table_id (u32 table_id, + fib_source_t src) { fib_table_t *fib_table; ip6_fib_t *v6_fib; @@ -77,29 +78,30 @@ create_fib_with_table_id (u32 table_id) fib_table->ft_flow_hash_config = IP_FLOW_HASH_DEFAULT; vnet_ip6_fib_init(fib_table->ft_index); - fib_table_lock(fib_table->ft_index, FIB_PROTOCOL_IP6); + fib_table_lock(fib_table->ft_index, FIB_PROTOCOL_IP6, src); return (fib_table->ft_index); } u32 -ip6_fib_table_find_or_create_and_lock (u32 table_id) +ip6_fib_table_find_or_create_and_lock (u32 table_id, + fib_source_t src) { uword * p; p = hash_get (ip6_main.fib_index_by_table_id, table_id); if (NULL == p) - return create_fib_with_table_id(table_id); + return create_fib_with_table_id(table_id, src); - fib_table_lock(p[0], FIB_PROTOCOL_IP6); + fib_table_lock(p[0], FIB_PROTOCOL_IP6, src); return (p[0]); } u32 -ip6_fib_table_create_and_lock (void) +ip6_fib_table_create_and_lock (fib_source_t src) { - return (create_fib_with_table_id(~0)); + return (create_fib_with_table_id(~0, src)); } void @@ -588,16 +590,33 @@ ip6_show_fib (vlib_main_t * vm, pool_foreach (fib_table, im6->fibs, ({ + fib_source_t source; + u8 *s = NULL; + fib = pool_elt_at_index(im6->v6_fibs, fib_table->ft_index); if (table_id >= 0 && table_id != (int)fib->table_id) continue; if (fib_index != ~0 && fib_index != (int)fib->index) continue; - vlib_cli_output (vm, "%s, fib_index:%d, flow hash:[%U] locks:%d", - fib_table->ft_desc, fib->index, - format_ip_flow_hash_config, fib_table->ft_flow_hash_config, - fib_table->ft_locks); + s = format(s, "%U, fib_index:%d, flow hash:[%U] locks:[", + format_fib_table_name, fib->index, + FIB_PROTOCOL_IP6, + fib->index, + format_ip_flow_hash_config, + fib_table->ft_flow_hash_config); + FOR_EACH_FIB_SOURCE(source) + { + if (0 != fib_table->ft_locks[source]) + { + s = format(s, "%U:%d, ", + format_fib_source, source, + fib_table->ft_locks[source]); + } + } + s = format (s, "]"); + vlib_cli_output (vm, "%V", s); + vec_free(s); /* Show summary? */ if (! verbose) diff --git a/src/vnet/fib/ip6_fib.h b/src/vnet/fib/ip6_fib.h index aad8305c..9728eecc 100644 --- a/src/vnet/fib/ip6_fib.h +++ b/src/vnet/fib/ip6_fib.h @@ -144,8 +144,9 @@ ip6_src_lookup_for_packet (ip6_main_t * im, * \returns A pointer to the retrieved or created fib. * */ -extern u32 ip6_fib_table_find_or_create_and_lock(u32 table_id); -extern u32 ip6_fib_table_create_and_lock(void); +extern u32 ip6_fib_table_find_or_create_and_lock(u32 table_id, + fib_source_t src); +extern u32 ip6_fib_table_create_and_lock(fib_source_t src); static inline ip6_fib_t * ip6_fib_get (fib_node_index_t index) diff --git a/src/vnet/fib/mpls_fib.c b/src/vnet/fib/mpls_fib.c index ca6271fe..4eeef7ab 100644 --- a/src/vnet/fib/mpls_fib.c +++ b/src/vnet/fib/mpls_fib.c @@ -83,7 +83,8 @@ mpls_fib_index_from_table_id (u32 table_id) } static u32 -mpls_fib_create_with_table_id (u32 table_id) +mpls_fib_create_with_table_id (u32 table_id, + fib_source_t src) { dpo_id_t dpo = DPO_INVALID; fib_table_t *fib_table; @@ -107,7 +108,7 @@ mpls_fib_create_with_table_id (u32 table_id) fib_table->ft_table_id = table_id; fib_table->ft_flow_hash_config = MPLS_FLOW_HASH_DEFAULT; - fib_table_lock(fib_table->ft_index, FIB_PROTOCOL_MPLS); + fib_table_lock(fib_table->ft_index, FIB_PROTOCOL_MPLS, src); if (INDEX_INVALID == mpls_fib_drop_dpo_index) { @@ -220,22 +221,23 @@ mpls_fib_create_with_table_id (u32 table_id) } u32 -mpls_fib_table_find_or_create_and_lock (u32 table_id) +mpls_fib_table_find_or_create_and_lock (u32 table_id, + fib_source_t src) { u32 index; index = mpls_fib_index_from_table_id(table_id); if (~0 == index) - return mpls_fib_create_with_table_id(table_id); + return mpls_fib_create_with_table_id(table_id, src); - fib_table_lock(index, FIB_PROTOCOL_MPLS); + fib_table_lock(index, FIB_PROTOCOL_MPLS, src); return (index); } u32 -mpls_fib_table_create_and_lock (void) +mpls_fib_table_create_and_lock (fib_source_t src) { - return (mpls_fib_create_with_table_id(~0)); + return (mpls_fib_create_with_table_id(~0, src)); } void diff --git a/src/vnet/fib/mpls_fib.h b/src/vnet/fib/mpls_fib.h index dfb8b7fc..29cd1d20 100644 --- a/src/vnet/fib/mpls_fib.h +++ b/src/vnet/fib/mpls_fib.h @@ -59,8 +59,9 @@ mpls_fib_get (fib_node_index_t index) return (pool_elt_at_index(mpls_main.mpls_fibs, index)); } -extern u32 mpls_fib_table_find_or_create_and_lock(u32 table_id); -extern u32 mpls_fib_table_create_and_lock(void); +extern u32 mpls_fib_table_find_or_create_and_lock(u32 table_id, + fib_source_t src); +extern u32 mpls_fib_table_create_and_lock(fib_source_t src); // extern mpls_fib_t * mpls_fib_find(u32 table_id); extern u32 mpls_fib_index_from_table_id(u32 table_id); diff --git a/src/vnet/interface_api.c b/src/vnet/interface_api.c index 113728cd..419fef94 100644 --- a/src/vnet/interface_api.c +++ b/src/vnet/interface_api.c @@ -320,68 +320,189 @@ stats_dsunlock (void) static void vl_api_sw_interface_set_table_t_handler (vl_api_sw_interface_set_table_t * mp) { - int rv = 0; - u32 table_id = ntohl (mp->vrf_id); - u32 sw_if_index = ntohl (mp->sw_if_index); vl_api_sw_interface_set_table_reply_t *rmp; - CLIB_UNUSED (ip_interface_address_t * ia); - u32 fib_index; + u32 sw_if_index = ntohl (mp->sw_if_index); + u32 table_id = ntohl (mp->vrf_id); + int rv = 0; VALIDATE_SW_IF_INDEX (mp); stats_dslock_with_hint (1 /* release hint */ , 4 /* tag */ ); if (mp->is_ipv6) + rv = ip_table_bind (FIB_PROTOCOL_IP6, sw_if_index, table_id, 1); + else + rv = ip_table_bind (FIB_PROTOCOL_IP4, sw_if_index, table_id, 1); + + stats_dsunlock (); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_SW_INTERFACE_SET_TABLE_REPLY); +} + +int +ip_table_bind (fib_protocol_t fproto, + uint32_t sw_if_index, uint32_t table_id, u8 is_api) +{ + CLIB_UNUSED (ip_interface_address_t * ia); + u32 fib_index, mfib_index; + fib_source_t src; + mfib_source_t msrc; + + if (is_api) + { + src = FIB_SOURCE_API; + msrc = MFIB_SOURCE_API; + } + else + { + src = FIB_SOURCE_CLI; + msrc = MFIB_SOURCE_CLI; + } + + /* + * This is temporary whilst I do the song and dance with the CSIT version + */ + if (0 != table_id) { + fib_index = fib_table_find_or_create_and_lock (fproto, table_id, src); + mfib_index = + mfib_table_find_or_create_and_lock (fproto, table_id, msrc); + } + else + { + fib_index = 0; + mfib_index = 0; + } + + /* + * This if table does not exist = error is what we want in the end. + */ + /* fib_index = fib_table_find (fproto, table_id); */ + /* mfib_index = mfib_table_find (fproto, table_id); */ + + /* if (~0 == fib_index || ~0 == mfib_index) */ + /* { */ + /* return (VNET_API_ERROR_NO_SUCH_FIB); */ + /* } */ + + if (FIB_PROTOCOL_IP6 == fproto) + { + /* + * If the interface already has in IP address, then a change int + * VRF is not allowed. The IP address applied must first be removed. + * We do not do that automatically here, since VPP has no knowledge + * of whether thoses subnets are valid in the destination VRF. + */ /* *INDENT-OFF* */ foreach_ip_interface_address (&ip6_main.lookup_main, ia, sw_if_index, 1 /* honor unnumbered */ , ({ - rv = VNET_API_ERROR_ADDRESS_FOUND_FOR_INTERFACE; - goto done; + return (VNET_API_ERROR_ADDRESS_FOUND_FOR_INTERFACE); })); /* *INDENT-ON* */ - fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, - table_id); vec_validate (ip6_main.fib_index_by_sw_if_index, sw_if_index); - ip6_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; - - fib_index = mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, - table_id); vec_validate (ip6_main.mfib_index_by_sw_if_index, sw_if_index); - ip6_main.mfib_index_by_sw_if_index[sw_if_index] = fib_index; + + /* + * tell those that are interested that the binding is changing. + */ + ip6_table_bind_callback_t *cb; + vec_foreach (cb, ip6_main.table_bind_callbacks) + cb->function (&ip6_main, cb->function_opaque, + sw_if_index, + fib_index, + ip6_main.fib_index_by_sw_if_index[sw_if_index]); + + if (0 == table_id) + { + /* reset back to default */ + if (0 != ip6_main.fib_index_by_sw_if_index[sw_if_index]) + fib_table_unlock (ip6_main.fib_index_by_sw_if_index[sw_if_index], + FIB_PROTOCOL_IP6, src); + if (0 != ip6_main.mfib_index_by_sw_if_index[sw_if_index]) + mfib_table_unlock (ip6_main.mfib_index_by_sw_if_index + [sw_if_index], FIB_PROTOCOL_IP6, msrc); + + } + else + { + /* we need to lock the table now it's inuse */ + fib_table_lock (fib_index, FIB_PROTOCOL_IP6, src); + mfib_table_lock (mfib_index, FIB_PROTOCOL_IP6, msrc); + } + + ip6_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; + ip6_main.mfib_index_by_sw_if_index[sw_if_index] = mfib_index; } else { + /* + * If the interface already has in IP address, then a change int + * VRF is not allowed. The IP address applied must first be removed. + * We do not do that automatically here, since VPP has no knowledge + * of whether thoses subnets are valid in the destination VRF. + */ /* *INDENT-OFF* */ foreach_ip_interface_address (&ip4_main.lookup_main, ia, sw_if_index, 1 /* honor unnumbered */ , ({ - rv = VNET_API_ERROR_ADDRESS_FOUND_FOR_INTERFACE; - goto done; + return (VNET_API_ERROR_ADDRESS_FOUND_FOR_INTERFACE); })); /* *INDENT-ON* */ - fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, - table_id); vec_validate (ip4_main.fib_index_by_sw_if_index, sw_if_index); - ip4_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; - - fib_index = mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, - table_id); vec_validate (ip4_main.mfib_index_by_sw_if_index, sw_if_index); - ip4_main.mfib_index_by_sw_if_index[sw_if_index] = fib_index; - } -done: - stats_dsunlock (); + /* + * tell those that are interested that the binding is changing. + */ + ip4_table_bind_callback_t *cb; + vec_foreach (cb, ip4_main.table_bind_callbacks) + cb->function (&ip4_main, cb->function_opaque, + sw_if_index, + fib_index, + ip4_main.fib_index_by_sw_if_index[sw_if_index]); + + if (0 == table_id) + { + /* reset back to default */ + if (0 != ip4_main.fib_index_by_sw_if_index[sw_if_index]) + fib_table_unlock (ip4_main.fib_index_by_sw_if_index[sw_if_index], + FIB_PROTOCOL_IP4, src); + if (0 != ip4_main.mfib_index_by_sw_if_index[sw_if_index]) + mfib_table_unlock (ip4_main.mfib_index_by_sw_if_index + [sw_if_index], FIB_PROTOCOL_IP4, msrc); - BAD_SW_IF_INDEX_LABEL; + } + else + { + /* we need to lock the table now it's inuse */ + fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, + table_id, src); - REPLY_MACRO (VL_API_SW_INTERFACE_SET_TABLE_REPLY); + mfib_index = mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, + table_id, msrc); + } + + ip4_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; + ip4_main.mfib_index_by_sw_if_index[sw_if_index] = mfib_index; + } + + /* + * Temporary. undo the locks from the find and create at the staart + */ + if (0 != table_id) + { + fib_table_unlock (fib_index, fproto, src); + mfib_table_unlock (mfib_index, fproto, msrc); + } + + return (0); } static void diff --git a/src/vnet/ip/ip.h b/src/vnet/ip/ip.h index 70b4ccd8..7aae73ff 100644 --- a/src/vnet/ip/ip.h +++ b/src/vnet/ip/ip.h @@ -184,6 +184,13 @@ void ip_del_all_interface_addresses (vlib_main_t * vm, u32 sw_if_index); extern vlib_node_registration_t ip4_inacl_node; extern vlib_node_registration_t ip6_inacl_node; +void ip_table_create (fib_protocol_t fproto, uint32_t table_id, u8 is_api); + +void ip_table_delete (fib_protocol_t fproto, uint32_t table_id, u8 is_api); + +int ip_table_bind (fib_protocol_t fproto, + uint32_t sw_if_index, uint32_t table_id, u8 is_api); + #endif /* included_ip_main_h */ /* diff --git a/src/vnet/ip/ip4.h b/src/vnet/ip/ip4.h index 8f9a8e27..decb840b 100644 --- a/src/vnet/ip/ip4.h +++ b/src/vnet/ip/ip4.h @@ -72,6 +72,16 @@ typedef struct uword function_opaque; } ip4_add_del_interface_address_callback_t; +typedef void (ip4_table_bind_function_t) + (struct ip4_main_t * im, + uword opaque, u32 sw_if_index, u32 new_fib_index, u32 old_fib_index); + +typedef struct +{ + ip4_table_bind_function_t *function; + uword function_opaque; +} ip4_table_bind_callback_t; + /** * @brief IPv4 main type. * @@ -117,6 +127,9 @@ typedef struct ip4_main_t ip4_add_del_interface_address_callback_t * add_del_interface_address_callbacks; + /** Functions to call when interface to table biding changes. */ + ip4_table_bind_callback_t *table_bind_callbacks; + /** Template used to generate IP4 ARP packets. */ vlib_packet_template_t ip4_arp_request_packet_template; diff --git a/src/vnet/ip/ip4_forward.c b/src/vnet/ip/ip4_forward.c index 2d48e8a9..ec4287bb 100755 --- a/src/vnet/ip/ip4_forward.c +++ b/src/vnet/ip/ip4_forward.c @@ -1198,8 +1198,10 @@ ip4_lookup_init (vlib_main_t * vm) ip_lookup_init (&im->lookup_main, /* is_ip6 */ 0); /* Create FIB with index 0 and table id of 0. */ - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, 0); - mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, 0); + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, 0, + FIB_SOURCE_DEFAULT_ROUTE); + mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, 0, + MFIB_SOURCE_DEFAULT_ROUTE); { pg_node_t *pn; @@ -2794,101 +2796,6 @@ VLIB_REGISTER_NODE (ip4_midchain_node) = { VLIB_NODE_FUNCTION_MULTIARCH (ip4_midchain_node, ip4_midchain); /* *INDENT-ON */ -static clib_error_t * -add_del_interface_table (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - vnet_main_t *vnm = vnet_get_main (); - ip_interface_address_t *ia; - clib_error_t *error = 0; - u32 sw_if_index, table_id; - - sw_if_index = ~0; - - if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index)) - { - error = clib_error_return (0, "unknown interface `%U'", - format_unformat_error, input); - goto done; - } - - if (unformat (input, "%d", &table_id)) - ; - else - { - error = clib_error_return (0, "expected table id `%U'", - format_unformat_error, input); - goto done; - } - - /* - * If the interface already has in IP address, then a change int - * VRF is not allowed. The IP address applied must first be removed. - * We do not do that automatically here, since VPP has no knowledge - * of whether thoses subnets are valid in the destination VRF. - */ - /* *INDENT-OFF* */ - foreach_ip_interface_address (&ip4_main.lookup_main, - ia, sw_if_index, - 1 /* honor unnumbered */, - ({ - ip4_address_t * a; - - a = ip_interface_address_get_address (&ip4_main.lookup_main, ia); - error = clib_error_return (0, "interface %U has address %U", - format_vnet_sw_if_index_name, vnm, - sw_if_index, - format_ip4_address, a); - goto done; - })); - /* *INDENT-ON* */ - -{ - ip4_main_t *im = &ip4_main; - u32 fib_index; - - fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, table_id); - - vec_validate (im->fib_index_by_sw_if_index, sw_if_index); - im->fib_index_by_sw_if_index[sw_if_index] = fib_index; - - fib_index = mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, table_id); - vec_validate (im->mfib_index_by_sw_if_index, sw_if_index); - im->mfib_index_by_sw_if_index[sw_if_index] = fib_index; -} - -done: -return error; -} - -/*? - * Place the indicated interface into the supplied IPv4 FIB table (also known - * as a VRF). If the FIB table does not exist, this command creates it. To - * display the current IPv4 FIB table, use the command 'show ip fib'. - * FIB table will only be displayed if a route has been added to the table, or - * an IP Address is assigned to an interface in the table (which adds a route - * automatically). - * - * @note IP addresses added after setting the interface IP table are added to - * the indicated FIB table. If an IP address is added prior to changing the - * table then this is an error. The control plane must remove these addresses - * first and then change the table. VPP will not automatically move the - * addresses from the old to the new table as it does not know the validity - * of such a change. - * - * @cliexpar - * Example of how to add an interface to an IPv4 FIB table (where 2 is the table-id): - * @cliexcmd{set interface ip table GigabitEthernet2/0/0 2} - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (set_interface_ip_table_command, static) = -{ - .path = "set interface ip table", - .function = add_del_interface_table, - .short_help = "set interface ip table ", -}; -/* *INDENT-ON* */ - int ip4_lookup_validate (ip4_address_t * a, u32 fib_index0) { diff --git a/src/vnet/ip/ip4_source_and_port_range_check.c b/src/vnet/ip/ip4_source_and_port_range_check.c index ae836a11..9aa880ae 100644 --- a/src/vnet/ip/ip4_source_and_port_range_check.c +++ b/src/vnet/ip/ip4_source_and_port_range_check.c @@ -1126,6 +1126,14 @@ ip6_source_and_port_range_check_add_del (ip6_address_t * address, u16 * low_ports, u16 * high_ports, int is_add) { + uint32_t fib_index; + + fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id); + + ASSERT (~0 != fib_index); + + fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_CLASSIFY); + return 0; } @@ -1138,7 +1146,8 @@ ip4_source_and_port_range_check_add_del (ip4_address_t * address, { u32 fib_index; - fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id); + fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, + FIB_SOURCE_CLASSIFY); if (is_add == 0) { diff --git a/src/vnet/ip/ip6.h b/src/vnet/ip/ip6.h index fa922725..8aef53a9 100644 --- a/src/vnet/ip/ip6.h +++ b/src/vnet/ip/ip6.h @@ -103,6 +103,16 @@ typedef struct uword function_opaque; } ip6_add_del_interface_address_callback_t; +typedef void (ip6_table_bind_function_t) + (struct ip6_main_t * im, + uword opaque, u32 sw_if_index, u32 new_fib_index, u32 old_fib_index); + +typedef struct +{ + ip6_table_bind_function_t *function; + uword function_opaque; +} ip6_table_bind_callback_t; + /** * Enumeration of the FIB table instance types */ @@ -183,6 +193,9 @@ typedef struct ip6_main_t ip6_add_del_interface_address_callback_t * add_del_interface_address_callbacks; + /** Functions to call when interface to table biding changes. */ + ip6_table_bind_callback_t *table_bind_callbacks; + /* Template used to generate IP6 neighbor solicitation packets. */ vlib_packet_template_t discover_neighbor_packet_template; diff --git a/src/vnet/ip/ip6_forward.c b/src/vnet/ip/ip6_forward.c index 5832bd0b..1002f6b6 100644 --- a/src/vnet/ip/ip6_forward.c +++ b/src/vnet/ip/ip6_forward.c @@ -2999,8 +2999,10 @@ ip6_lookup_init (vlib_main_t * vm) im->lookup_table_nbuckets, im->lookup_table_size); /* Create FIB with index 0 and table id of 0. */ - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, 0); - mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, 0); + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, 0, + FIB_SOURCE_DEFAULT_ROUTE); + mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, 0, + MFIB_SOURCE_DEFAULT_ROUTE); { pg_node_t *pn; @@ -3045,103 +3047,6 @@ ip6_lookup_init (vlib_main_t * vm) VLIB_INIT_FUNCTION (ip6_lookup_init); -static clib_error_t * -add_del_ip6_interface_table (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - vnet_main_t *vnm = vnet_get_main (); - ip_interface_address_t *ia; - clib_error_t *error = 0; - u32 sw_if_index, table_id; - - sw_if_index = ~0; - - if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index)) - { - error = clib_error_return (0, "unknown interface `%U'", - format_unformat_error, input); - goto done; - } - - if (unformat (input, "%d", &table_id)) - ; - else - { - error = clib_error_return (0, "expected table id `%U'", - format_unformat_error, input); - goto done; - } - - /* - * If the interface already has in IP address, then a change int - * VRF is not allowed. The IP address applied must first be removed. - * We do not do that automatically here, since VPP has no knowledge - * of whether thoses subnets are valid in the destination VRF. - */ - /* *INDENT-OFF* */ - foreach_ip_interface_address (&ip6_main.lookup_main, - ia, sw_if_index, - 1 /* honor unnumbered */, - ({ - ip4_address_t * a; - - a = ip_interface_address_get_address (&ip6_main.lookup_main, ia); - error = clib_error_return (0, "interface %U has address %U", - format_vnet_sw_if_index_name, vnm, - sw_if_index, - format_ip6_address, a); - goto done; - })); - /* *INDENT-ON* */ - - { - u32 fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, - table_id); - - vec_validate (ip6_main.fib_index_by_sw_if_index, sw_if_index); - ip6_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; - - fib_index = mfib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, - table_id); - - vec_validate (ip6_main.mfib_index_by_sw_if_index, sw_if_index); - ip6_main.mfib_index_by_sw_if_index[sw_if_index] = fib_index; - } - - -done: - return error; -} - -/*? - * Place the indicated interface into the supplied IPv6 FIB table (also known - * as a VRF). If the FIB table does not exist, this command creates it. To - * display the current IPv6 FIB table, use the command 'show ip6 fib'. - * FIB table will only be displayed if a route has been added to the table, or - * an IP Address is assigned to an interface in the table (which adds a route - * automatically). - * - * @note IP addresses added after setting the interface IP table are added to - * the indicated FIB table. If an IP address is added prior to changing the - * table then this is an error. The control plane must remove these addresses - * first and then change the table. VPP will not automatically move the - * addresses from the old to the new table as it does not know the validity - * of such a change. - * - * @cliexpar - * Example of how to add an interface to an IPv6 FIB table (where 2 is the table-id): - * @cliexcmd{set interface ip6 table GigabitEthernet2/0/0 2} - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (set_interface_ip6_table_command, static) = -{ - .path = "set interface ip6 table", - .function = add_del_ip6_interface_table, - .short_help = "set interface ip6 table " -}; -/* *INDENT-ON* */ - void ip6_link_local_address_from_ethernet_mac_address (ip6_address_t * ip, u8 * mac) diff --git a/src/vnet/ip/ip6_neighbor.c b/src/vnet/ip/ip6_neighbor.c index 62cf23ac..56f33ac8 100644 --- a/src/vnet/ip/ip6_neighbor.c +++ b/src/vnet/ip/ip6_neighbor.c @@ -250,6 +250,26 @@ format_ip6_neighbor_ip6_entry (u8 * s, va_list * va) return s; } +static void +ip6_neighbor_adj_fib_remove (ip6_neighbor_t * n, uint32_t fib_index) +{ + if (FIB_NODE_INDEX_INVALID != n->fib_entry_index) + { + fib_prefix_t pfx = { + .fp_len = 128, + .fp_proto = FIB_PROTOCOL_IP6, + .fp_addr.ip6 = n->key.ip6_address, + }; + fib_table_entry_path_remove (fib_index, + &pfx, + FIB_SOURCE_ADJ, + DPO_PROTO_IP6, + &pfx.fp_addr, + n->key.sw_if_index, ~0, + 1, FIB_ROUTE_PATH_FLAG_NONE); + } +} + static clib_error_t * ip6_neighbor_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags) @@ -273,22 +293,10 @@ ip6_neighbor_sw_interface_up_down (vnet_main_t * vnm, { n = pool_elt_at_index (nm->neighbor_pool, to_delete[i]); mhash_unset (&nm->neighbor_index_by_key, &n->key, 0); - if (FIB_NODE_INDEX_INVALID != n->fib_entry_index) - { - fib_prefix_t pfx = { - .fp_len = 128, - .fp_proto = FIB_PROTOCOL_IP6, - .fp_addr.ip6 = n->key.ip6_address, - }; - fib_table_entry_path_remove - (ip6_fib_table_get_index_for_sw_if_index (n->key.sw_if_index), - &pfx, - FIB_SOURCE_ADJ, - DPO_PROTO_IP6, - &pfx.fp_addr, - n->key.sw_if_index, ~0, 1, FIB_ROUTE_PATH_FLAG_NONE); - pool_put (nm->neighbor_pool, n); - } + ip6_neighbor_adj_fib_remove (n, + ip6_fib_table_get_index_for_sw_if_index + (n->key.sw_if_index)); + pool_put (nm->neighbor_pool, n); } vec_free (to_delete); } @@ -579,6 +587,24 @@ ip6_ethernet_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai) } } + +static void +ip6_neighbor_adj_fib_add (ip6_neighbor_t * n, uint32_t fib_index) +{ + fib_prefix_t pfx = { + .fp_len = 128, + .fp_proto = FIB_PROTOCOL_IP6, + .fp_addr.ip6 = n->key.ip6_address, + }; + + n->fib_entry_index = + fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ, + FIB_ENTRY_FLAG_ATTACHED, + DPO_PROTO_IP6, &pfx.fp_addr, + n->key.sw_if_index, ~0, 1, NULL, + FIB_ROUTE_PATH_FLAG_NONE); +} + int vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm, u32 sw_if_index, @@ -633,21 +659,9 @@ vnet_set_ip6_ethernet_neighbor (vlib_main_t * vm, */ if (!is_no_fib_entry) { - fib_prefix_t pfx = { - .fp_len = 128, - .fp_proto = FIB_PROTOCOL_IP6, - .fp_addr.ip6 = k.ip6_address, - }; - u32 fib_index; - - fib_index = - ip6_fib_table_get_index_for_sw_if_index (n->key.sw_if_index); - n->fib_entry_index = - fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ, - FIB_ENTRY_FLAG_ATTACHED, - DPO_PROTO_IP6, &pfx.fp_addr, - n->key.sw_if_index, ~0, 1, NULL, - FIB_ROUTE_PATH_FLAG_NONE); + ip6_neighbor_adj_fib_add (n, + ip6_fib_table_get_index_for_sw_if_index + (n->key.sw_if_index)); } else { @@ -3843,6 +3857,33 @@ ip6_set_neighbor_limit (u32 neighbor_limit) return 0; } +static void +ip6_neighbor_table_bind (ip6_main_t * im, + uword opaque, + u32 sw_if_index, + u32 new_fib_index, u32 old_fib_index) +{ + ip6_neighbor_main_t *nm = &ip6_neighbor_main; + ip6_neighbor_t *n = NULL; + u32 i, *to_re_add = 0; + + /* *INDENT-OFF* */ + pool_foreach (n, nm->neighbor_pool, + ({ + if (n->key.sw_if_index == sw_if_index) + vec_add1 (to_re_add, n - nm->neighbor_pool); + })); + /* *INDENT-ON* */ + + for (i = 0; i < vec_len (to_re_add); i++) + { + n = pool_elt_at_index (nm->neighbor_pool, to_re_add[i]); + ip6_neighbor_adj_fib_remove (n, old_fib_index); + ip6_neighbor_adj_fib_add (n, new_fib_index); + } + vec_free (to_re_add); +} + static clib_error_t * ip6_neighbor_init (vlib_main_t * vm) { @@ -3874,6 +3915,11 @@ ip6_neighbor_init (vlib_main_t * vm) cb.function_opaque = 0; vec_add1 (im->add_del_interface_address_callbacks, cb); + ip6_table_bind_callback_t cbt; + cbt.function = ip6_neighbor_table_bind; + cbt.function_opaque = 0; + vec_add1 (im->table_bind_callbacks, cbt); + mhash_init (&nm->pending_resolutions_by_address, /* value size */ sizeof (uword), /* key size */ sizeof (ip6_address_t)); diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c index bba65ab4..384ec3e0 100644 --- a/src/vnet/ip/ip_api.c +++ b/src/vnet/ip/ip_api.c @@ -699,12 +699,58 @@ vl_api_ip_neighbor_add_del_t_handler (vl_api_ip_neighbor_add_del_t * mp, REPLY_MACRO (VL_API_IP_NEIGHBOR_ADD_DEL_REPLY); } +void +ip_table_delete (fib_protocol_t fproto, u32 table_id, u8 is_api) +{ + u32 fib_index, mfib_index; + + /* + * ignore action on the default table - this is always present + * and cannot be added nor deleted from the API + */ + if (0 != table_id) + { + /* + * The API holds only one lock on the table. + * i.e. it can be added many times via the API but needs to be + * deleted only once. + * The FIB index for unicast and multicast is not necessarily the + * same, since internal VPP systesm (like LISP and SR) create + * their own unicast tables. + */ + fib_index = fib_table_find (fproto, table_id); + mfib_index = mfib_table_find (fproto, table_id); + + if (~0 != fib_index) + { + fib_table_unlock (fib_index, fproto, + (is_api ? FIB_SOURCE_API : FIB_SOURCE_CLI)); + } + if (~0 != mfib_index) + { + mfib_table_unlock (mfib_index, fproto, + (is_api ? MFIB_SOURCE_API : MFIB_SOURCE_CLI)); + } + } +} + void vl_api_ip_table_add_del_t_handler (vl_api_ip_table_add_del_t * mp) { vl_api_ip_table_add_del_reply_t *rmp; + fib_protocol_t fproto = (mp->is_ipv6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4); + u32 table_id = ntohl (mp->table_id); int rv = 0; + if (mp->is_add) + { + ip_table_create (fproto, table_id, 1); + } + else + { + ip_table_delete (fproto, table_id, 1); + } + REPLY_MACRO (VL_API_IP_TABLE_ADD_DEL_REPLY); } @@ -866,18 +912,21 @@ add_del_route_check (fib_protocol_t table_proto, u32 next_hop_sw_if_index, dpo_proto_t next_hop_table_proto, u32 next_hop_table_id, - u8 create_missing_tables, u8 is_rpf_id, u32 * fib_index, u32 * next_hop_fib_index) { vnet_main_t *vnm = vnet_get_main (); + /* Temporaray whilst I do the CSIT dance */ + u8 create_missing_tables = 1; + *fib_index = fib_table_find (table_proto, ntohl (table_id)); if (~0 == *fib_index) { if (create_missing_tables) { *fib_index = fib_table_find_or_create_and_lock (table_proto, - ntohl (table_id)); + ntohl (table_id), + FIB_SOURCE_API); } else { @@ -918,12 +967,14 @@ add_del_route_check (fib_protocol_t table_proto, *next_hop_fib_index = mfib_table_find_or_create_and_lock (fib_nh_proto, ntohl - (next_hop_table_id)); + (next_hop_table_id), + MFIB_SOURCE_API); else *next_hop_fib_index = fib_table_find_or_create_and_lock (fib_nh_proto, ntohl - (next_hop_table_id)); + (next_hop_table_id), + FIB_SOURCE_API); } else { @@ -948,8 +999,7 @@ ip4_add_del_route_t_handler (vl_api_ip_add_del_route_t * mp) mp->next_hop_sw_if_index, DPO_PROTO_IP4, mp->next_hop_table_id, - mp->create_vrf_if_needed, 0, - &fib_index, &next_hop_fib_index); + 0, &fib_index, &next_hop_fib_index); if (0 != rv) return (rv); @@ -1008,8 +1058,7 @@ ip6_add_del_route_t_handler (vl_api_ip_add_del_route_t * mp) mp->next_hop_sw_if_index, DPO_PROTO_IP6, mp->next_hop_table_id, - mp->create_vrf_if_needed, 0, - &fib_index, &next_hop_fib_index); + 0, &fib_index, &next_hop_fib_index); if (0 != rv) return (rv); @@ -1074,27 +1123,57 @@ vl_api_ip_add_del_route_t_handler (vl_api_ip_add_del_route_t * mp) REPLY_MACRO (VL_API_IP_ADD_DEL_ROUTE_REPLY); } +void +ip_table_create (fib_protocol_t fproto, u32 table_id, u8 is_api) +{ + u32 fib_index, mfib_index; + + /* + * ignore action on the default table - this is always present + * and cannot be added nor deleted from the API + */ + if (0 != table_id) + { + /* + * The API holds only one lock on the table. + * i.e. it can be added many times via the API but needs to be + * deleted only once. + * The FIB index for unicast and multicast is not necessarily the + * same, since internal VPP systesm (like LISP and SR) create + * their own unicast tables. + */ + fib_index = fib_table_find (fproto, table_id); + mfib_index = mfib_table_find (fproto, table_id); + + if (~0 == fib_index) + { + fib_table_find_or_create_and_lock (fproto, table_id, + (is_api ? + FIB_SOURCE_API : + FIB_SOURCE_CLI)); + } + if (~0 == mfib_index) + { + mfib_table_find_or_create_and_lock (fproto, table_id, + (is_api ? + MFIB_SOURCE_API : + MFIB_SOURCE_CLI)); + } + } +} + static int add_del_mroute_check (fib_protocol_t table_proto, u32 table_id, - u32 next_hop_sw_if_index, - u8 is_local, u8 create_missing_tables, u32 * fib_index) + u32 next_hop_sw_if_index, u8 is_local, u32 * fib_index) { vnet_main_t *vnm = vnet_get_main (); *fib_index = mfib_table_find (table_proto, ntohl (table_id)); if (~0 == *fib_index) { - if (create_missing_tables) - { - *fib_index = mfib_table_find_or_create_and_lock (table_proto, - ntohl (table_id)); - } - else - { - /* No such VRF, and we weren't asked to create one */ - return VNET_API_ERROR_NO_SUCH_FIB; - } + /* No such table */ + return VNET_API_ERROR_NO_SUCH_FIB; } if (~0 != ntohl (next_hop_sw_if_index)) @@ -1163,8 +1242,7 @@ api_mroute_add_del_t_handler (vl_api_ip_mroute_add_del_t * mp) rv = add_del_mroute_check (fproto, mp->table_id, mp->next_hop_sw_if_index, - mp->is_local, - mp->create_vrf_if_needed, &fib_index); + mp->is_local, &fib_index); if (0 != rv) return (rv); diff --git a/src/vnet/ip/lookup.c b/src/vnet/ip/lookup.c index 5537bb04..667c6791 100755 --- a/src/vnet/ip/lookup.c +++ b/src/vnet/ip/lookup.c @@ -687,6 +687,78 @@ done: return error; } +clib_error_t * +vnet_ip_table_cmd (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd, fib_protocol_t fproto) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = NULL; + u32 table_id, is_add; + + is_add = 1; + table_id = ~0; + + /* Get a line of input. */ + if (!unformat_user (main_input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%d", &table_id)) + ; + else if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "add")) + is_add = 1; + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + if (~0 == table_id) + { + error = clib_error_return (0, "No table id"); + goto done; + } + else if (0 == table_id) + { + error = clib_error_return (0, "Can't change the default table"); + goto done; + } + else + { + if (is_add) + { + ip_table_create (fproto, table_id, 0); + } + else + { + ip_table_delete (fproto, table_id, 0); + } + } + +done: + unformat_free (line_input); + return error; +} + +clib_error_t * +vnet_ip4_table_cmd (vlib_main_t * vm, + unformat_input_t * main_input, vlib_cli_command_t * cmd) +{ + return (vnet_ip_table_cmd (vm, main_input, cmd, FIB_PROTOCOL_IP4)); +} + +clib_error_t * +vnet_ip6_table_cmd (vlib_main_t * vm, + unformat_input_t * main_input, vlib_cli_command_t * cmd) +{ + return (vnet_ip_table_cmd (vm, main_input, cmd, FIB_PROTOCOL_IP6)); +} + /* *INDENT-OFF* */ VLIB_CLI_COMMAND (vlib_cli_ip_command, static) = { .path = "ip", @@ -749,6 +821,159 @@ VLIB_CLI_COMMAND (ip_route_command, static) = { .function = vnet_ip_route_cmd, .is_mp_safe = 1, }; + +/* *INDENT-ON* */ +/*? + * This command is used to add or delete IPv4 Tables. All + * Tables must be explicitly added before that can be used. Creating a + * table will add both unicast and multicast FIBs + * + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ip4_table_command, static) = { + .path = "ip table", + .short_help = "ip table [add|del] ", + .function = vnet_ip4_table_cmd, + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +/* *INDENT-ON* */ +/*? + * This command is used to add or delete IPv4 Tables. All + * Tables must be explicitly added before that can be used. Creating a + * table will add both unicast and multicast FIBs + * + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ip6_table_command, static) = { + .path = "ip6 table", + .short_help = "ip6 table [add|del] ", + .function = vnet_ip6_table_cmd, + .is_mp_safe = 1, +}; + +static clib_error_t * +ip_table_bind_cmd (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd, + fib_protocol_t fproto) +{ + vnet_main_t *vnm = vnet_get_main (); + clib_error_t *error = 0; + u32 sw_if_index, table_id; + int rv; + + sw_if_index = ~0; + + if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index)) + { + error = clib_error_return (0, "unknown interface `%U'", + format_unformat_error, input); + goto done; + } + + if (unformat (input, "%d", &table_id)) + ; + else + { + error = clib_error_return (0, "expected table id `%U'", + format_unformat_error, input); + goto done; + } + + rv = ip_table_bind (fproto, sw_if_index, table_id, 0); + + if (VNET_API_ERROR_ADDRESS_FOUND_FOR_INTERFACE == rv) + { + error = clib_error_return (0, "IP addresses are still present on %U", + format_vnet_sw_if_index_name, + vnet_get_main(), + sw_if_index); + } + else if (VNET_API_ERROR_NO_SUCH_FIB == rv) + { + error = clib_error_return (0, "no such table %d", table_id); + } + else if (0 != rv) + { + error = clib_error_return (0, "unknown error"); + } + + done: + return error; +} + +static clib_error_t * +ip4_table_bind_cmd (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + return (ip_table_bind_cmd (vm , input, cmd, FIB_PROTOCOL_IP4)); +} + +static clib_error_t * +ip6_table_bind_cmd (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + return (ip_table_bind_cmd (vm , input, cmd, FIB_PROTOCOL_IP6)); +} + +/*? + * Place the indicated interface into the supplied IPv4 FIB table (also known + * as a VRF). If the FIB table does not exist, this command creates it. To + * display the current IPv4 FIB table, use the command 'show ip fib'. + * FIB table will only be displayed if a route has been added to the table, or + * an IP Address is assigned to an interface in the table (which adds a route + * automatically). + * + * @note IP addresses added after setting the interface IP table are added to + * the indicated FIB table. If an IP address is added prior to changing the + * table then this is an error. The control plane must remove these addresses + * first and then change the table. VPP will not automatically move the + * addresses from the old to the new table as it does not know the validity + * of such a change. + * + * @cliexpar + * Example of how to add an interface to an IPv4 FIB table (where 2 is the table-id): + * @cliexcmd{set interface ip table GigabitEthernet2/0/0 2} + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (set_interface_ip_table_command, static) = +{ + .path = "set interface ip table", + .function = ip4_table_bind_cmd, + .short_help = "set interface ip table ", +}; +/* *INDENT-ON* */ + +/*? + * Place the indicated interface into the supplied IPv6 FIB table (also known + * as a VRF). If the FIB table does not exist, this command creates it. To + * display the current IPv6 FIB table, use the command 'show ip6 fib'. + * FIB table will only be displayed if a route has been added to the table, or + * an IP Address is assigned to an interface in the table (which adds a route + * automatically). + * + * @note IP addresses added after setting the interface IP table are added to + * the indicated FIB table. If an IP address is added prior to changing the + * table then this is an error. The control plane must remove these addresses + * first and then change the table. VPP will not automatically move the + * addresses from the old to the new table as it does not know the validity + * of such a change. + * + * @cliexpar + * Example of how to add an interface to an IPv6 FIB table (where 2 is the table-id): + * @cliexcmd{set interface ip6 table GigabitEthernet2/0/0 2} + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (set_interface_ip6_table_command, static) = +{ + .path = "set interface ip6 table", + .function = ip6_table_bind_cmd, + .short_help = "set interface ip6 table " +}; /* *INDENT-ON* */ clib_error_t * diff --git a/src/vnet/lisp-gpe/interface.c b/src/vnet/lisp-gpe/interface.c index e832c23f..a0c05e85 100644 --- a/src/vnet/lisp-gpe/interface.c +++ b/src/vnet/lisp-gpe/interface.c @@ -505,12 +505,14 @@ lisp_gpe_iface_set_table (u32 sw_if_index, u32 table_id) { fib_node_index_t fib_index; - fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, table_id); + fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, table_id, + FIB_SOURCE_LISP); vec_validate (ip4_main.fib_index_by_sw_if_index, sw_if_index); ip4_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; ip4_sw_interface_enable_disable (sw_if_index, 1); - fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, table_id); + fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, table_id, + FIB_SOURCE_LISP); vec_validate (ip6_main.fib_index_by_sw_if_index, sw_if_index); ip6_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; ip6_sw_interface_enable_disable (sw_if_index, 1); @@ -530,7 +532,7 @@ lisp_gpe_tenant_del_default_routes (u32 table_id) fib_index = fib_table_find (prefix.fp_proto, table_id); fib_table_entry_special_remove (fib_index, &prefix, FIB_SOURCE_LISP); - fib_table_unlock (fib_index, prefix.fp_proto); + fib_table_unlock (fib_index, prefix.fp_proto, FIB_SOURCE_LISP); } } @@ -549,7 +551,8 @@ lisp_gpe_tenant_add_default_routes (u32 table_id) /* * Add a deafult route that results in a control plane punt DPO */ - fib_index = fib_table_find_or_create_and_lock (prefix.fp_proto, table_id); + fib_index = fib_table_find_or_create_and_lock (prefix.fp_proto, table_id, + FIB_SOURCE_LISP); fib_table_entry_special_dpo_add (fib_index, &prefix, FIB_SOURCE_LISP, FIB_ENTRY_FLAG_EXCLUSIVE, lisp_cp_dpo_get (fib_proto_to_dpo diff --git a/src/vnet/lisp-gpe/lisp_gpe_fwd_entry.c b/src/vnet/lisp-gpe/lisp_gpe_fwd_entry.c index d7d3cb86..0a8dc039 100644 --- a/src/vnet/lisp-gpe/lisp_gpe_fwd_entry.c +++ b/src/vnet/lisp-gpe/lisp_gpe_fwd_entry.c @@ -66,6 +66,7 @@ ip_dst_fib_add_route (u32 dst_fib_index, const ip_prefix_t * dst_prefix) /* create a new src FIB. */ src_fib_index = fib_table_create_and_lock (dst_fib_prefix.fp_proto, + FIB_SOURCE_LISP, "LISP-src for [%d,%U]", dst_fib_index, format_fib_prefix, &dst_fib_prefix); @@ -180,7 +181,8 @@ ip_src_dst_fib_del_route (u32 src_fib_index, */ fib_table_entry_special_remove (dst_fib_index, &dst_fib_prefix, FIB_SOURCE_LISP); - fib_table_unlock (src_fib_index, src_fib_prefix.fp_proto); + fib_table_unlock (src_fib_index, src_fib_prefix.fp_proto, + FIB_SOURCE_LISP); } } @@ -544,7 +546,8 @@ add_ip_fwd_entry (lisp_gpe_main_t * lgm, lfe->tenant = lisp_gpe_tenant_find_or_create (lfe->key->vni); lfe->eid_table_id = a->table_id; lfe->eid_fib_index = fib_table_find_or_create_and_lock (fproto, - lfe->eid_table_id); + lfe->eid_table_id, + FIB_SOURCE_LISP); lfe->is_src_dst = a->is_src_dst; if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) @@ -578,7 +581,7 @@ del_ip_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe) fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6); - fib_table_unlock (lfe->eid_fib_index, fproto); + fib_table_unlock (lfe->eid_fib_index, fproto, FIB_SOURCE_LISP); hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key); clib_mem_free (lfe->key); diff --git a/src/vnet/lisp-gpe/lisp_gpe_sub_interface.c b/src/vnet/lisp-gpe/lisp_gpe_sub_interface.c index b234d9dc..26664f53 100644 --- a/src/vnet/lisp-gpe/lisp_gpe_sub_interface.c +++ b/src/vnet/lisp-gpe/lisp_gpe_sub_interface.c @@ -89,13 +89,15 @@ lisp_gpe_sub_interface_set_table (u32 sw_if_index, u32 table_id) { fib_node_index_t fib_index; - fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, table_id); + fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, table_id, + FIB_SOURCE_LISP); ASSERT (FIB_NODE_INDEX_INVALID != fib_index); vec_validate (ip4_main.fib_index_by_sw_if_index, sw_if_index); ip4_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; - fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, table_id); + fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, table_id, + FIB_SOURCE_LISP); ASSERT (FIB_NODE_INDEX_INVALID != fib_index); vec_validate (ip6_main.fib_index_by_sw_if_index, sw_if_index); @@ -105,9 +107,13 @@ lisp_gpe_sub_interface_set_table (u32 sw_if_index, u32 table_id) static void lisp_gpe_sub_interface_unset_table (u32 sw_if_index, u32 table_id) { + fib_table_unlock (ip4_main.fib_index_by_sw_if_index[sw_if_index], + FIB_PROTOCOL_IP4, FIB_SOURCE_LISP); ip4_main.fib_index_by_sw_if_index[sw_if_index] = 0; ip4_sw_interface_enable_disable (sw_if_index, 0); + fib_table_unlock (ip6_main.fib_index_by_sw_if_index[sw_if_index], + FIB_PROTOCOL_IP6, FIB_SOURCE_LISP); ip6_main.fib_index_by_sw_if_index[sw_if_index] = 0; ip6_sw_interface_enable_disable (sw_if_index, 0); } @@ -185,6 +191,7 @@ lisp_gpe_sub_interface_unlock (index_t l3si) l3s = lisp_gpe_sub_interface_get_i (l3si); + ASSERT (0 != l3s->locks); l3s->locks--; if (0 == l3s->locks) diff --git a/src/vnet/mfib/ip4_mfib.c b/src/vnet/mfib/ip4_mfib.c index 1849a3a4..b2482580 100644 --- a/src/vnet/mfib/ip4_mfib.c +++ b/src/vnet/mfib/ip4_mfib.c @@ -33,7 +33,8 @@ static const mfib_prefix_t ip4_specials[] = { }; static u32 -ip4_create_mfib_with_table_id (u32 table_id) +ip4_create_mfib_with_table_id (u32 table_id, + mfib_source_t src) { mfib_table_t *mfib_table; @@ -53,7 +54,7 @@ ip4_create_mfib_with_table_id (u32 table_id) mfib_table->v4.table_id = table_id; - mfib_table_lock(mfib_table->mft_index, FIB_PROTOCOL_IP4); + mfib_table_lock(mfib_table->mft_index, FIB_PROTOCOL_IP4, src); /* * add the special entries into the new FIB @@ -113,14 +114,15 @@ ip4_mfib_table_destroy (ip4_mfib_t *mfib) } u32 -ip4_mfib_table_find_or_create_and_lock (u32 table_id) +ip4_mfib_table_find_or_create_and_lock (u32 table_id, + mfib_source_t src) { u32 index; index = ip4_mfib_index_from_table_id(table_id); if (~0 == index) - return ip4_create_mfib_with_table_id(table_id); - mfib_table_lock(index, FIB_PROTOCOL_IP4); + return ip4_create_mfib_with_table_id(table_id, src); + mfib_table_lock(index, FIB_PROTOCOL_IP4, src); return (index); } diff --git a/src/vnet/mfib/ip4_mfib.h b/src/vnet/mfib/ip4_mfib.h index ea682651..e31fb744 100644 --- a/src/vnet/mfib/ip4_mfib.h +++ b/src/vnet/mfib/ip4_mfib.h @@ -72,8 +72,9 @@ ip4_mfib_get (u32 index) * @returns A pointer to the retrieved or created fib. * */ -extern u32 ip4_mfib_table_find_or_create_and_lock(u32 table_id); -extern u32 ip4_mfib_table_create_and_lock(void); +extern u32 ip4_mfib_table_find_or_create_and_lock(u32 table_id, + mfib_source_t src); +extern u32 ip4_mfib_table_create_and_lock(mfib_source_t src); static inline u32 ip4_mfib_index_from_table_id (u32 table_id) diff --git a/src/vnet/mfib/ip6_mfib.c b/src/vnet/mfib/ip6_mfib.c index 5e48e919..e4861330 100644 --- a/src/vnet/mfib/ip6_mfib.c +++ b/src/vnet/mfib/ip6_mfib.c @@ -151,7 +151,8 @@ static const ip6_mfib_special_t ip6_mfib_specials[] = static u32 -ip6_create_mfib_with_table_id (u32 table_id) +ip6_create_mfib_with_table_id (u32 table_id, + mfib_source_t src) { mfib_table_t *mfib_table; mfib_prefix_t pfx = { @@ -182,7 +183,7 @@ ip6_create_mfib_with_table_id (u32 table_id) mfib_table->v6.table_id = table_id; - mfib_table_lock(mfib_table->mft_index, FIB_PROTOCOL_IP6); + mfib_table_lock(mfib_table->mft_index, FIB_PROTOCOL_IP6, src); mfib_table->v6.rhead = clib_mem_alloc_aligned (sizeof(*mfib_table->v6.rhead), @@ -297,14 +298,15 @@ ip6_mfib_interface_enable_disable (u32 sw_if_index, int is_enable) } u32 -ip6_mfib_table_find_or_create_and_lock (u32 table_id) +ip6_mfib_table_find_or_create_and_lock (u32 table_id, + mfib_source_t src) { u32 index; index = ip6_mfib_index_from_table_id(table_id); if (~0 == index) - return ip6_create_mfib_with_table_id(table_id); - mfib_table_lock(index, FIB_PROTOCOL_IP6); + return ip6_create_mfib_with_table_id(table_id, src); + mfib_table_lock(index, FIB_PROTOCOL_IP6, src); return (index); } diff --git a/src/vnet/mfib/ip6_mfib.h b/src/vnet/mfib/ip6_mfib.h index adaa7ec2..ea81b553 100644 --- a/src/vnet/mfib/ip6_mfib.h +++ b/src/vnet/mfib/ip6_mfib.h @@ -79,8 +79,9 @@ ip6_mfib_get (u32 index) * @returns A pointer to the retrieved or created fib. * */ -extern u32 ip6_mfib_table_find_or_create_and_lock(u32 table_id); -extern u32 ip6_mfib_table_create_and_lock(void); +extern u32 ip6_mfib_table_find_or_create_and_lock(u32 table_id, + mfib_source_t src); +extern u32 ip6_mfib_table_create_and_lock(mfib_source_t src); static inline diff --git a/src/vnet/mfib/mfib_entry.c b/src/vnet/mfib/mfib_entry.c index 804e10ab..2302b9a1 100644 --- a/src/vnet/mfib/mfib_entry.c +++ b/src/vnet/mfib/mfib_entry.c @@ -334,6 +334,17 @@ mfib_entry_get_best_src (const mfib_entry_t *mfib_entry) return (bsrc); } +int +mfib_entry_is_sourced (fib_node_index_t mfib_entry_index, + mfib_source_t source) +{ + mfib_entry_t *mfib_entry; + + mfib_entry = mfib_entry_get(mfib_entry_index); + + return (NULL != mfib_entry_src_find(mfib_entry, source, NULL)); +} + static void mfib_entry_src_flush (mfib_entry_src_t *msrc) { diff --git a/src/vnet/mfib/mfib_entry.h b/src/vnet/mfib/mfib_entry.h index d4377878..96ee49f7 100644 --- a/src/vnet/mfib/mfib_entry.h +++ b/src/vnet/mfib/mfib_entry.h @@ -130,6 +130,8 @@ extern void mfib_entry_unlock(fib_node_index_t fib_entry_index); extern void mfib_entry_get_prefix(fib_node_index_t fib_entry_index, mfib_prefix_t *pfx); extern u32 mfib_entry_get_fib_index(fib_node_index_t fib_entry_index); +extern int mfib_entry_is_sourced(fib_node_index_t fib_entry_index, + mfib_source_t source); extern void mfib_entry_contribute_forwarding( fib_node_index_t mfib_entry_index, diff --git a/src/vnet/mfib/mfib_table.c b/src/vnet/mfib/mfib_table.c index 7ffe8941..e5550adc 100644 --- a/src/vnet/mfib/mfib_table.c +++ b/src/vnet/mfib/mfib_table.c @@ -424,7 +424,8 @@ mfib_table_find (fib_protocol_t proto, u32 mfib_table_find_or_create_and_lock (fib_protocol_t proto, - u32 table_id) + u32 table_id, + mfib_source_t src) { mfib_table_t *mfib_table; fib_node_index_t fi; @@ -432,10 +433,10 @@ mfib_table_find_or_create_and_lock (fib_protocol_t proto, switch (proto) { case FIB_PROTOCOL_IP4: - fi = ip4_mfib_table_find_or_create_and_lock(table_id); + fi = ip4_mfib_table_find_or_create_and_lock(table_id, src); break; case FIB_PROTOCOL_IP6: - fi = ip6_mfib_table_find_or_create_and_lock(table_id); + fi = ip6_mfib_table_find_or_create_and_lock(table_id, src); break; case FIB_PROTOCOL_MPLS: default: @@ -451,6 +452,59 @@ mfib_table_find_or_create_and_lock (fib_protocol_t proto, return (fi); } +/** + * @brief Table flush context. Store the indicies of matching FIB entries + * that need to be removed. + */ +typedef struct mfib_table_flush_ctx_t_ +{ + /** + * The list of entries to flush + */ + fib_node_index_t *mftf_entries; + + /** + * The source we are flushing + */ + mfib_source_t mftf_source; +} mfib_table_flush_ctx_t; + +static int +mfib_table_flush_cb (fib_node_index_t mfib_entry_index, + void *arg) +{ + mfib_table_flush_ctx_t *ctx = arg; + + if (mfib_entry_is_sourced(mfib_entry_index, ctx->mftf_source)) + { + vec_add1(ctx->mftf_entries, mfib_entry_index); + } + return (1); +} + +void +mfib_table_flush (u32 mfib_index, + fib_protocol_t proto, + mfib_source_t source) +{ + fib_node_index_t *mfib_entry_index; + mfib_table_flush_ctx_t ctx = { + .mftf_entries = NULL, + .mftf_source = source, + }; + + mfib_table_walk(mfib_index, proto, + mfib_table_flush_cb, + &ctx); + + vec_foreach(mfib_entry_index, ctx.mftf_entries) + { + mfib_table_entry_delete_index(*mfib_entry_index, source); + } + + vec_free(ctx.mftf_entries); +} + static void mfib_table_destroy (mfib_table_t *mfib_table) { @@ -472,27 +526,43 @@ mfib_table_destroy (mfib_table_t *mfib_table) void mfib_table_unlock (u32 fib_index, - fib_protocol_t proto) + fib_protocol_t proto, + mfib_source_t source) { mfib_table_t *mfib_table; mfib_table = mfib_table_get(fib_index, proto); - mfib_table->mft_locks--; + mfib_table->mft_locks[source]--; + mfib_table->mft_locks[MFIB_TABLE_TOTAL_LOCKS]--; + + if (0 == mfib_table->mft_locks[source]) + { + /* + * The source no longer needs the table. flush any routes + * from it just in case + */ + mfib_table_flush(fib_index, proto, source); + } - if (0 == mfib_table->mft_locks) + if (0 == mfib_table->mft_locks[MFIB_TABLE_TOTAL_LOCKS]) { - mfib_table_destroy(mfib_table); + /* + * no more locak from any source - kill it + */ + mfib_table_destroy(mfib_table); } } void mfib_table_lock (u32 fib_index, - fib_protocol_t proto) + fib_protocol_t proto, + mfib_source_t source) { mfib_table_t *mfib_table; mfib_table = mfib_table_get(fib_index, proto); - mfib_table->mft_locks++; + mfib_table->mft_locks[source]++; + mfib_table->mft_locks[MFIB_TABLE_TOTAL_LOCKS]++; } void diff --git a/src/vnet/mfib/mfib_table.h b/src/vnet/mfib/mfib_table.h index 83aa04ef..c6b0b097 100644 --- a/src/vnet/mfib/mfib_table.h +++ b/src/vnet/mfib/mfib_table.h @@ -22,6 +22,12 @@ #include +/** + * Keep a lock per-source and a total + */ +#define MFIB_TABLE_N_LOCKS (MFIB_N_SOURCES+1) +#define MFIB_TABLE_TOTAL_LOCKS MFIB_N_SOURCES + /** * @brief * A protocol Independent IP multicast FIB table @@ -47,7 +53,7 @@ typedef struct mfib_table_t_ /** * number of locks on the table */ - u16 mft_locks; + u16 mft_locks[MFIB_TABLE_N_LOCKS]; /** * Table ID (hash key) for this FIB. @@ -259,7 +265,8 @@ extern fib_node_index_t mfib_table_entry_special_add(u32 fib_index, * the source to flush */ extern void mfib_table_flush(u32 fib_index, - fib_protocol_t proto); + fib_protocol_t proto, + mfib_source_t source); /** * @brief @@ -307,9 +314,13 @@ extern u32 mfib_table_find(fib_protocol_t proto, u32 table_id); * * @return fib_index * The index of the FIB + * + * @param source + * The ID of the client/source. */ extern u32 mfib_table_find_or_create_and_lock(fib_protocol_t proto, - u32 table_id); + u32 table_id, + mfib_source_t source); /** @@ -321,9 +332,13 @@ extern u32 mfib_table_find_or_create_and_lock(fib_protocol_t proto, * * @paran proto * The protocol of the FIB (and thus the entries therein) + * + * @param source + * The ID of the client/source. */ extern void mfib_table_unlock(u32 fib_index, - fib_protocol_t proto); + fib_protocol_t proto, + mfib_source_t source); /** * @brief @@ -335,9 +350,13 @@ extern void mfib_table_unlock(u32 fib_index, * * @paran proto * The protocol of the FIB (and thus the entries therein) + * + * @param source + * The ID of the client/source. */ extern void mfib_table_lock(u32 fib_index, - fib_protocol_t proto); + fib_protocol_t proto, + mfib_source_t source); /** * @brief diff --git a/src/vnet/mfib/mfib_test.c b/src/vnet/mfib/mfib_test.c index 57787eca..3055844d 100644 --- a/src/vnet/mfib/mfib_test.c +++ b/src/vnet/mfib/mfib_test.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -366,7 +367,7 @@ mfib_test_i (fib_protocol_t PROTO, MFIB_TEST(3 == adj_mcast_db_size(), "3 MCAST adjs"); /* Find or create FIB table 11 */ - fib_index = mfib_table_find_or_create_and_lock(PROTO, 11); + fib_index = mfib_table_find_or_create_and_lock(PROTO, 11, MFIB_SOURCE_API); mfib_prefix_t pfx_dft = { .fp_len = 0, @@ -1113,9 +1114,10 @@ mfib_test_i (fib_protocol_t PROTO, /* * MPLS enable an interface so we get the MPLS table created */ + mpls_table_create(MPLS_FIB_DEFAULT_TABLE_ID, FIB_SOURCE_API); mpls_sw_interface_enable_disable(&mpls_main, tm->hw[0]->sw_if_index, - 1); + 1, 0); lfei = fib_table_entry_update_one_path(0, // default MPLS Table &pfx_3500, @@ -1192,7 +1194,7 @@ mfib_test_i (fib_protocol_t PROTO, /* * Unlock the table - it's the last lock so should be gone thereafter */ - mfib_table_unlock(fib_index, PROTO); + mfib_table_unlock(fib_index, PROTO, MFIB_SOURCE_API); MFIB_TEST((FIB_NODE_INDEX_INVALID == mfib_table_find(PROTO, fib_index)), @@ -1207,7 +1209,8 @@ mfib_test_i (fib_protocol_t PROTO, */ mpls_sw_interface_enable_disable(&mpls_main, tm->hw[0]->sw_if_index, - 0); + 0, 0); + mpls_table_delete(MPLS_FIB_DEFAULT_TABLE_ID, FIB_SOURCE_API); /* * test we've leaked no resources diff --git a/src/vnet/mfib/mfib_types.h b/src/vnet/mfib/mfib_types.h index 863fad16..50aede04 100644 --- a/src/vnet/mfib/mfib_types.h +++ b/src/vnet/mfib/mfib_types.h @@ -166,9 +166,10 @@ typedef enum mfib_source_t_ MFIB_SOURCE_VXLAN, MFIB_SOURCE_DHCP, MFIB_SOURCE_SRv6, - MFIB_SOURCE_DEFAULT_ROUTE, MFIB_SOURCE_GTPU, MFIB_SOURCE_VXLAN_GPE, + MFIB_SOURCE_RR, + MFIB_SOURCE_DEFAULT_ROUTE, } mfib_source_t; #define MFIB_SOURCE_NAMES { \ @@ -178,11 +179,14 @@ typedef enum mfib_source_t_ [MFIB_SOURCE_DHCP] = "DHCP", \ [MFIB_SOURCE_VXLAN] = "VXLAN", \ [MFIB_SOURCE_SRv6] = "SRv6", \ - [MFIB_SOURCE_DEFAULT_ROUTE] = "Default Route", \ [MFIB_SOURCE_GTPU] = "GTPU", \ [MFIB_SOURCE_VXLAN_GPE] = "VXLAN-GPE", \ + [MFIB_SOURCE_RR] = "Recursive-resolution", \ + [MFIB_SOURCE_DEFAULT_ROUTE] = "Default Route", \ } +#define MFIB_N_SOURCES (MFIB_SOURCE_DEFAULT_ROUTE) + /** * \brief Compare two prefixes for equality */ diff --git a/src/vnet/mpls/interface.c b/src/vnet/mpls/interface.c index a085aaa2..d7c8e7d3 100644 --- a/src/vnet/mpls/interface.c +++ b/src/vnet/mpls/interface.c @@ -35,25 +35,33 @@ mpls_sw_interface_is_enabled (u32 sw_if_index) return (mm->mpls_enabled_by_sw_if_index[sw_if_index]); } -void +int mpls_sw_interface_enable_disable (mpls_main_t * mm, u32 sw_if_index, - u8 is_enable) + u8 is_enable, + u8 is_api) { fib_node_index_t lfib_index; vec_validate_init_empty (mm->mpls_enabled_by_sw_if_index, sw_if_index, 0); + lfib_index = fib_table_find(FIB_PROTOCOL_MPLS, + MPLS_FIB_DEFAULT_TABLE_ID); + + if (~0 == lfib_index) + return VNET_API_ERROR_NO_SUCH_FIB; + /* * enable/disable only on the 1<->0 transition */ if (is_enable) { if (1 != ++mm->mpls_enabled_by_sw_if_index[sw_if_index]) - return; + return (0); + + fib_table_lock(lfib_index, FIB_PROTOCOL_MPLS, + (is_api? FIB_SOURCE_API: FIB_SOURCE_CLI)); - lfib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_MPLS, - MPLS_FIB_DEFAULT_TABLE_ID); vec_validate(mm->fib_index_by_sw_if_index, 0); mm->fib_index_by_sw_if_index[sw_if_index] = lfib_index; } @@ -61,15 +69,17 @@ mpls_sw_interface_enable_disable (mpls_main_t * mm, { ASSERT(mm->mpls_enabled_by_sw_if_index[sw_if_index] > 0); if (0 != --mm->mpls_enabled_by_sw_if_index[sw_if_index]) - return; + return (0); fib_table_unlock(mm->fib_index_by_sw_if_index[sw_if_index], - FIB_PROTOCOL_MPLS); + FIB_PROTOCOL_MPLS, + (is_api? FIB_SOURCE_API: FIB_SOURCE_CLI)); } vnet_feature_enable_disable ("mpls-input", "mpls-not-enabled", sw_if_index, !is_enable, 0, 0); + return (0); } static clib_error_t * @@ -101,7 +111,7 @@ mpls_interface_enable_disable (vlib_main_t * vm, goto done; } - mpls_sw_interface_enable_disable(&mpls_main, sw_if_index, enable); + mpls_sw_interface_enable_disable(&mpls_main, sw_if_index, enable, 0); done: return error; diff --git a/src/vnet/mpls/mpls.c b/src/vnet/mpls/mpls.c index 5021ac23..7bdfd8c7 100644 --- a/src/vnet/mpls/mpls.c +++ b/src/vnet/mpls/mpls.c @@ -536,6 +536,78 @@ VLIB_CLI_COMMAND (mpls_local_label_command, static) = { .short_help = "Create/Delete MPL local labels", }; +clib_error_t * +vnet_mpls_table_cmd (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmdo) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = NULL; + u32 table_id, is_add; + + is_add = 1; + table_id = ~0; + + /* Get a line of input. */ + if (!unformat_user (main_input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%d", &table_id)) + ; + else if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "add")) + is_add = 1; + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + if (~0 == table_id) + { + error = clib_error_return (0, "No table id"); + goto done; + } + else if (0 == table_id) + { + error = clib_error_return (0, "Can't change the default table"); + goto done; + } + else + { + if (is_add) + { + mpls_table_create (table_id, 0); + } + else + { + mpls_table_delete (table_id, 0); + } + } + + done: + unformat_free (line_input); + return error; +} + +/* *INDENT-ON* */ +/*? + * This command is used to add or delete MPLS Tables. All + * Tables must be explicitly added before that can be used, + * Including the default table. + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ip6_table_command, static) = { + .path = "mpla table", + .short_help = "mpls table [add|del] ", + .function = vnet_mpls_table_cmd, + .is_mp_safe = 1, +}; + int mpls_fib_reset_labels (u32 fib_id) { @@ -546,12 +618,8 @@ mpls_fib_reset_labels (u32 fib_id) static clib_error_t * mpls_init (vlib_main_t * vm) { - mpls_main_t * mm = &mpls_main; clib_error_t * error; - mm->vlib_main = vm; - mm->vnet_main = vnet_get_main(); - if ((error = vlib_call_init_function (vm, ip_main_init))) return error; diff --git a/src/vnet/mpls/mpls.h b/src/vnet/mpls/mpls.h index b0125e60..31cb1746 100644 --- a/src/vnet/mpls/mpls.h +++ b/src/vnet/mpls/mpls.h @@ -56,10 +56,6 @@ typedef struct { /* IP4 enabled count by software interface */ u8 * mpls_enabled_by_sw_if_index; - - /* convenience */ - vlib_main_t * vlib_main; - vnet_main_t * vnet_main; } mpls_main_t; extern mpls_main_t mpls_main; @@ -77,8 +73,6 @@ extern vlib_node_registration_t mpls_midchain_node; /* Parse mpls protocol as 0xXXXX or protocol name. In either host or network byte order. */ -unformat_function_t unformat_mpls_protocol_host_byte_order; -unformat_function_t unformat_mpls_protocol_net_byte_order; unformat_function_t unformat_mpls_label_net_byte_order; unformat_function_t unformat_mpls_unicast_label; @@ -86,9 +80,10 @@ unformat_function_t unformat_mpls_unicast_label; unformat_function_t unformat_mpls_header; unformat_function_t unformat_pg_mpls_header; -void mpls_sw_interface_enable_disable (mpls_main_t * mm, - u32 sw_if_index, - u8 is_enable); +int mpls_sw_interface_enable_disable (mpls_main_t * mm, + u32 sw_if_index, + u8 is_enable, + u8 is_api); u8 mpls_sw_interface_is_enabled (u32 sw_if_index); @@ -103,4 +98,7 @@ mpls_fib_index_cmp(void * a1, void * a2); int mpls_label_cmp(void * a1, void * a2); +void mpls_table_create(uint32_t table_id, u8 is_api); +void mpls_table_delete(uint32_t table_id, u8 is_api); + #endif /* included_vnet_mpls_h */ diff --git a/src/vnet/mpls/mpls_api.c b/src/vnet/mpls/mpls_api.c index a44b1a25..38f5b014 100644 --- a/src/vnet/mpls/mpls_api.c +++ b/src/vnet/mpls/mpls_api.c @@ -58,6 +58,29 @@ _(MPLS_FIB_DUMP, mpls_fib_dump) extern void stats_dslock_with_hint (int hint, int tag); extern void stats_dsunlock (void); +void +mpls_table_delete (u32 table_id, u8 is_api) +{ + u32 fib_index; + + /* + * The MPLS defult table must also be explicitly created via the API. + * So in contrast to IP, it gets no special treatment here. + * + * The API holds only one lock on the table. + * i.e. it can be added many times via the API but needs to be + * deleted only once. + */ + fib_index = fib_table_find (FIB_PROTOCOL_MPLS, table_id); + + if (~0 != fib_index) + { + fib_table_unlock (fib_index, + FIB_PROTOCOL_MPLS, + (is_api ? FIB_SOURCE_API : FIB_SOURCE_CLI)); + } +} + void vl_api_mpls_table_add_del_t_handler (vl_api_mpls_table_add_del_t * mp) { @@ -68,6 +91,13 @@ vl_api_mpls_table_add_del_t_handler (vl_api_mpls_table_add_del_t * mp) vnm = vnet_get_main (); vnm->api_errno = 0; + if (mp->mt_is_add) + mpls_table_create (ntohl (mp->mt_table_id), 1); + else + mpls_table_delete (ntohl (mp->mt_table_id), 1); + + rv = (rv == 0) ? vnm->api_errno : rv; + REPLY_MACRO (VL_API_MPLS_TABLE_ADD_DEL_REPLY); } @@ -82,14 +112,7 @@ mpls_ip_bind_unbind_handler (vnet_main_t * vnm, if (~0 == mpls_fib_index) { - if (mp->mb_create_table_if_needed) - { - mpls_fib_index = - fib_table_find_or_create_and_lock (FIB_PROTOCOL_MPLS, - ntohl (mp->mb_mpls_table_id)); - } - else - return VNET_API_ERROR_NO_SUCH_FIB; + return VNET_API_ERROR_NO_SUCH_FIB; } ip_fib_index = fib_table_find ((mp->mb_is_ip4 ? @@ -170,7 +193,6 @@ mpls_route_add_del_t_handler (vnet_main_t * vnm, mp->mr_next_hop_sw_if_index, pfx.fp_payload_proto, mp->mr_next_hop_table_id, - mp->mr_create_table_if_needed, mp->mr_is_rpf_id, &fib_index, &next_hop_fib_index); @@ -235,6 +257,32 @@ vl_api_mpls_route_add_del_t_handler (vl_api_mpls_route_add_del_t * mp) REPLY_MACRO (VL_API_MPLS_ROUTE_ADD_DEL_REPLY); } +void +mpls_table_create (u32 table_id, u8 is_api) +{ + u32 fib_index; + + /* + * The MPLS defult table must also be explicitly created via the API. + * So in contrast to IP, it gets no special treatment here. + */ + + /* + * The API holds only one lock on the table. + * i.e. it can be added many times via the API but needs to be + * deleted only once. + */ + fib_index = fib_table_find (FIB_PROTOCOL_MPLS, table_id); + + if (~0 == fib_index) + { + fib_table_find_or_create_and_lock (FIB_PROTOCOL_MPLS, + table_id, + (is_api ? + FIB_SOURCE_API : FIB_SOURCE_CLI)); + } +} + static void vl_api_mpls_tunnel_add_del_t_handler (vl_api_mpls_tunnel_add_del_t * mp) { diff --git a/src/vnet/srv6/sr_policy_rewrite.c b/src/vnet/srv6/sr_policy_rewrite.c index f427bbf3..2f90993a 100755 --- a/src/vnet/srv6/sr_policy_rewrite.c +++ b/src/vnet/srv6/sr_policy_rewrite.c @@ -595,8 +595,10 @@ sr_policy_add (ip6_address_t * bsid, ip6_address_t * segments, if (sm->fib_table_ip6 == (u32) ~ 0) { sm->fib_table_ip6 = fib_table_create_and_lock (FIB_PROTOCOL_IP6, + FIB_SOURCE_SR, "SRv6 steering of IP6 prefixes through BSIDs"); sm->fib_table_ip4 = fib_table_create_and_lock (FIB_PROTOCOL_IP6, + FIB_SOURCE_SR, "SRv6 steering of IP4 prefixes through BSIDs"); } @@ -684,8 +686,8 @@ sr_policy_del (ip6_address_t * bsid, u32 index) /* If FIB empty unlock it */ if (!pool_elts (sm->sr_policies) && !pool_elts (sm->steer_policies)) { - fib_table_unlock (sm->fib_table_ip6, FIB_PROTOCOL_IP6); - fib_table_unlock (sm->fib_table_ip4, FIB_PROTOCOL_IP6); + fib_table_unlock (sm->fib_table_ip6, FIB_PROTOCOL_IP6, FIB_SOURCE_SR); + fib_table_unlock (sm->fib_table_ip4, FIB_PROTOCOL_IP6, FIB_SOURCE_SR); sm->fib_table_ip6 = (u32) ~ 0; sm->fib_table_ip4 = (u32) ~ 0; } diff --git a/src/vnet/srv6/sr_steering.c b/src/vnet/srv6/sr_steering.c index 57fe21f6..cf4e81ab 100755 --- a/src/vnet/srv6/sr_steering.c +++ b/src/vnet/srv6/sr_steering.c @@ -159,8 +159,10 @@ sr_steering_policy (int is_del, ip6_address_t * bsid, u32 sr_policy_index, /* If no more SR policies or steering policies */ if (!pool_elts (sm->sr_policies) && !pool_elts (sm->steer_policies)) { - fib_table_unlock (sm->fib_table_ip6, FIB_PROTOCOL_IP6); - fib_table_unlock (sm->fib_table_ip4, FIB_PROTOCOL_IP6); + fib_table_unlock (sm->fib_table_ip6, + FIB_PROTOCOL_IP6, FIB_SOURCE_SR); + fib_table_unlock (sm->fib_table_ip4, + FIB_PROTOCOL_IP6, FIB_SOURCE_SR); sm->fib_table_ip6 = (u32) ~ 0; sm->fib_table_ip4 = (u32) ~ 0; } diff --git a/src/vpp/api/api.c b/src/vpp/api/api.c index f9c3129c..044ddb5b 100644 --- a/src/vpp/api/api.c +++ b/src/vpp/api/api.c @@ -699,8 +699,9 @@ static void VALIDATE_SW_IF_INDEX (mp); - mpls_sw_interface_enable_disable (&mpls_main, - ntohl (mp->sw_if_index), mp->enable); + rv = mpls_sw_interface_enable_disable (&mpls_main, + ntohl (mp->sw_if_index), + mp->enable, 1); BAD_SW_IF_INDEX_LABEL; REPLY_MACRO (VL_API_SW_INTERFACE_SET_MPLS_ENABLE_REPLY); diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index 1353fe28..be74b83a 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -560,9 +560,6 @@ static void *vl_api_ip_add_del_route_t_print if (mp->table_id != 0) s = format (s, "vrf %d ", ntohl (mp->table_id)); - if (mp->create_vrf_if_needed) - s = format (s, "create-vrf "); - if (mp->next_hop_weight != 1) s = format (s, "weight %d ", mp->next_hop_weight); diff --git a/test/test_dhcp.py b/test/test_dhcp.py index 6fc29182..fe97f6c9 100644 --- a/test/test_dhcp.py +++ b/test/test_dhcp.py @@ -6,7 +6,7 @@ import struct from framework import VppTestCase, VppTestRunner from vpp_neighbor import VppNeighbor -from vpp_ip_route import find_route +from vpp_ip_route import find_route, VppIpTable from util import mk_ll_addr from scapy.layers.l2 import Ether, getmacbyip, ARP @@ -34,9 +34,19 @@ class TestDHCP(VppTestCase): # create 3 pg interfaces self.create_pg_interfaces(range(4)) + self.tables = [] # pg0 and 1 are IP configured in VRF 0 and 1. # pg2 and 3 are non IP-configured in VRF 0 and 1 + table_id = 0 + for table_id in range(1, 4): + tbl4 = VppIpTable(self, table_id) + tbl4.add_vpp_config() + self.tables.append(tbl4) + tbl6 = VppIpTable(self, table_id, is_ip6=1) + tbl6.add_vpp_config() + self.tables.append(tbl6) + table_id = 0 for i in self.pg_interfaces[:2]: i.admin_up() @@ -56,11 +66,15 @@ class TestDHCP(VppTestCase): table_id += 1 def tearDown(self): - super(TestDHCP, self).tearDown() - for i in self.pg_interfaces: + for i in self.pg_interfaces[:2]: i.unconfig_ip4() i.unconfig_ip6() + + for i in self.pg_interfaces: + i.set_table_ip4(0) + i.set_table_ip6(0) i.admin_down() + super(TestDHCP, self).tearDown() def send_and_assert_no_replies(self, intf, pkts, remark): intf.add_stream(pkts) @@ -667,6 +681,8 @@ class TestDHCP(VppTestCase): "DHCP cleanup VRF 0") self.send_and_assert_no_replies(self.pg3, pkts_disc_vrf1, "DHCP cleanup VRF 1") + self.pg2.unconfig_ip4() + self.pg3.unconfig_ip4() def test_dhcp6_proxy(self): """ DHCPv6 Proxy""" @@ -1045,6 +1061,8 @@ class TestDHCP(VppTestCase): server_table_id=0, is_ipv6=1, is_add=0) + self.pg2.unconfig_ip6() + self.pg3.unconfig_ip6() def test_dhcp_client(self): """ DHCP Client""" diff --git a/test/test_gre.py b/test/test_gre.py index 1afc44fb..9046b05f 100644 --- a/test/test_gre.py +++ b/test/test_gre.py @@ -6,7 +6,7 @@ from logging import * from framework import VppTestCase, VppTestRunner from vpp_sub_interface import VppDot1QSubint from vpp_gre_interface import VppGreInterface, VppGre6Interface -from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto +from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto, VppIpTable from vpp_papi_provider import L2_VTR_OP from scapy.packet import Raw @@ -30,6 +30,9 @@ class TestGRE(VppTestCase): # create 3 pg interfaces - set one in a non-default table. self.create_pg_interfaces(range(3)) + + self.tbl = VppIpTable(self, 1) + self.tbl.add_vpp_config() self.pg1.set_table_ip4(1) for i in self.pg_interfaces: @@ -43,11 +46,12 @@ class TestGRE(VppTestCase): self.pg2.resolve_ndp() def tearDown(self): - super(TestGRE, self).tearDown() for i in self.pg_interfaces: i.unconfig_ip4() i.unconfig_ip6() i.admin_down() + self.pg1.set_table_ip4(0) + super(TestGRE, self).tearDown() def create_stream_ip4(self, src_if, src_ip, dst_ip): pkts = [] diff --git a/test/test_ip4.py b/test/test_ip4.py index 7a7098c3..55d16735 100644 --- a/test/test_ip4.py +++ b/test/test_ip4.py @@ -6,7 +6,8 @@ import unittest from framework import VppTestCase, VppTestRunner from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpMRoute, \ - VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind + VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \ + VppMplsTable from scapy.packet import Raw from scapy.layers.l2 import Ether, Dot1Q, ARP @@ -774,6 +775,8 @@ class TestIPLoadBalance(VppTestCase): super(TestIPLoadBalance, self).setUp() self.create_pg_interfaces(range(5)) + mpls_tbl = VppMplsTable(self, 0) + mpls_tbl.add_vpp_config() for i in self.pg_interfaces: i.admin_up() @@ -782,11 +785,11 @@ class TestIPLoadBalance(VppTestCase): i.enable_mpls() def tearDown(self): - super(TestIPLoadBalance, self).tearDown() for i in self.pg_interfaces: i.disable_mpls() i.unconfig_ip4() i.admin_down() + super(TestIPLoadBalance, self).tearDown() def send_and_expect_load_balancing(self, input, pkts, outputs): input.add_stream(pkts) @@ -966,6 +969,8 @@ class TestIPVlan0(VppTestCase): super(TestIPVlan0, self).setUp() self.create_pg_interfaces(range(2)) + mpls_tbl = VppMplsTable(self, 0) + mpls_tbl.add_vpp_config() for i in self.pg_interfaces: i.admin_up() @@ -974,11 +979,11 @@ class TestIPVlan0(VppTestCase): i.enable_mpls() def tearDown(self): - super(TestIPVlan0, self).tearDown() for i in self.pg_interfaces: i.disable_mpls() i.unconfig_ip4() i.admin_down() + super(TestIPVlan0, self).tearDown() def send_and_expect(self, input, pkts, output): input.add_stream(pkts) diff --git a/test/test_ip4_vrf_multi_instance.py b/test/test_ip4_vrf_multi_instance.py index b73ac948..5a8d6760 100644 --- a/test/test_ip4_vrf_multi_instance.py +++ b/test/test_ip4_vrf_multi_instance.py @@ -172,9 +172,10 @@ class TestIp4VrfMultiInst(VppTestCase): pg_if = self.pg_if_by_vrf_id[vrf_id][0] dest_addr = pg_if.remote_hosts[0].ip4n dest_addr_len = 24 + self.vapi.ip_table_add_del(vrf_id, is_add=1) self.vapi.ip_add_del_route( dest_addr, dest_addr_len, pg_if.local_ip4n, - table_id=vrf_id, create_vrf_if_needed=1, is_multipath=1) + table_id=vrf_id, is_multipath=1) self.logger.info("IPv4 VRF ID %d created" % vrf_id) if vrf_id not in self.vrf_list: self.vrf_list.append(vrf_id) @@ -216,6 +217,7 @@ class TestIp4VrfMultiInst(VppTestCase): self.logger.info("IPv4 VRF ID %d reset" % vrf_id) self.logger.debug(self.vapi.ppcli("show ip fib")) self.logger.debug(self.vapi.ppcli("show ip arp")) + self.vapi.ip_table_add_del(vrf_id, is_add=0) def create_stream(self, src_if, packet_sizes): """ diff --git a/test/test_ip6.py b/test/test_ip6.py index 285ce181..aad3713c 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -8,7 +8,7 @@ from vpp_sub_interface import VppSubInterface, VppDot1QSubint from vpp_pg_interface import is_ipv6_misc from vpp_ip_route import VppIpRoute, VppRoutePath, find_route, VppIpMRoute, \ VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \ - VppMplsRoute, DpoProto + VppMplsRoute, DpoProto, VppMplsTable from vpp_neighbor import find_nbr, VppNeighbor from scapy.packet import Raw @@ -1260,6 +1260,9 @@ class TestIP6LoadBalance(VppTestCase): self.create_pg_interfaces(range(5)) + mpls_tbl = VppMplsTable(self, 0) + mpls_tbl.add_vpp_config() + for i in self.pg_interfaces: i.admin_up() i.config_ip6() @@ -1267,11 +1270,11 @@ class TestIP6LoadBalance(VppTestCase): i.enable_mpls() def tearDown(self): - super(TestIP6LoadBalance, self).tearDown() for i in self.pg_interfaces: i.unconfig_ip6() i.admin_down() i.disable_mpls() + super(TestIP6LoadBalance, self).tearDown() def send_and_expect_load_balancing(self, input, pkts, outputs): input.add_stream(pkts) diff --git a/test/test_ip6_vrf_multi_instance.py b/test/test_ip6_vrf_multi_instance.py index af80b5ba..769cb2e5 100644 --- a/test/test_ip6_vrf_multi_instance.py +++ b/test/test_ip6_vrf_multi_instance.py @@ -187,9 +187,10 @@ class TestIP6VrfMultiInst(VppTestCase): pg_if = self.pg_if_by_vrf_id[vrf_id][0] dest_addr = pg_if.remote_hosts[0].ip6n dest_addr_len = 64 + self.vapi.ip_table_add_del(vrf_id, is_add=1, is_ipv6=1) self.vapi.ip_add_del_route( dest_addr, dest_addr_len, pg_if.local_ip6n, is_ipv6=1, - table_id=vrf_id, create_vrf_if_needed=1, is_multipath=1) + table_id=vrf_id, is_multipath=1) self.logger.info("IPv6 VRF ID %d created" % vrf_id) if vrf_id not in self.vrf_list: self.vrf_list.append(vrf_id) @@ -232,6 +233,7 @@ class TestIP6VrfMultiInst(VppTestCase): self.logger.info("IPv6 VRF ID %d reset" % vrf_id) self.logger.debug(self.vapi.ppcli("show ip6 fib")) self.logger.debug(self.vapi.ppcli("show ip6 neighbors")) + self.vapi.ip_table_add_del(vrf_id, is_add=0, is_ipv6=1) def create_stream(self, src_if, packet_sizes): """ diff --git a/test/test_ip_mcast.py b/test/test_ip_mcast.py index 276555d6..7cad683c 100644 --- a/test/test_ip_mcast.py +++ b/test/test_ip_mcast.py @@ -5,7 +5,7 @@ import unittest from framework import VppTestCase, VppTestRunner from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint from vpp_ip_route import VppIpMRoute, VppMRoutePath, VppMFibSignal, \ - MRouteItfFlags, MRouteEntryFlags + MRouteItfFlags, MRouteEntryFlags, VppIpTable from scapy.packet import Raw from scapy.layers.l2 import Ether @@ -44,16 +44,37 @@ class TestIPMcast(VppTestCase): super(TestIPMcast, self).setUp() # create 8 pg interfaces - self.create_pg_interfaces(range(8)) + self.create_pg_interfaces(range(9)) # setup interfaces - for i in self.pg_interfaces: + for i in self.pg_interfaces[:8]: i.admin_up() i.config_ip4() i.config_ip6() i.resolve_arp() i.resolve_ndp() + # one more in a vrf + tbl4 = VppIpTable(self, 10) + tbl4.add_vpp_config() + self.pg8.set_table_ip4(10) + self.pg8.config_ip4() + + tbl6 = VppIpTable(self, 10, is_ip6=1) + tbl6.add_vpp_config() + self.pg8.set_table_ip6(10) + self.pg8.config_ip6() + + def tearDown(self): + for i in self.pg_interfaces: + i.unconfig_ip4() + i.unconfig_ip6() + i.admin_down() + + self.pg8.set_table_ip4(0) + self.pg8.set_table_ip6(0) + super(TestIPMcast, self).tearDown() + def create_stream_ip4(self, src_if, src_ip, dst_ip, payload_size=0): pkts = [] # default to small packet sizes @@ -663,6 +684,77 @@ class TestIPMcast(VppTestCase): # route_232_1_1_1.remove_vpp_config() + def test_ip_mcast_vrf(self): + """ IP Multicast Replication in non-default table""" + + # + # An (S,G). + # one accepting interface, pg0, 2 forwarding interfaces + # + route_1_1_1_1_232_1_1_1 = VppIpMRoute( + self, + "1.1.1.1", + "232.1.1.1", 64, + MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, + [VppMRoutePath(self.pg8.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT), + VppMRoutePath(self.pg1.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD), + VppMRoutePath(self.pg2.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)], + table_id=10) + route_1_1_1_1_232_1_1_1.add_vpp_config() + + # + # a stream that matches the route for (1.1.1.1,232.1.1.1) + # small packets + # + self.vapi.cli("clear trace") + tx = self.create_stream_ip4(self.pg8, "1.1.1.1", "232.1.1.1") + self.pg8.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # We expect replications on Pg1 & 2 + self.verify_capture_ip4(self.pg1, tx) + self.verify_capture_ip4(self.pg2, tx) + + def test_ip6_mcast_vrf(self): + """ IPv6 Multicast Replication in non-default table""" + + # + # An (S,G). + # one accepting interface, pg0, 2 forwarding interfaces + # + route_2001_ff01_1 = VppIpMRoute( + self, + "2001::1", + "ff01::1", 256, + MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, + [VppMRoutePath(self.pg8.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT), + VppMRoutePath(self.pg1.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD), + VppMRoutePath(self.pg2.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)], + table_id=10, + is_ip6=1) + route_2001_ff01_1.add_vpp_config() + + # + # a stream that matches the route for (2001::1, ff00::1) + # + self.vapi.cli("clear trace") + tx = self.create_stream_ip6(self.pg8, "2001::1", "ff01::1") + self.pg8.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # We expect replications on Pg1, 2, + self.verify_capture_ip6(self.pg1, tx) + self.verify_capture_ip6(self.pg2, tx) if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/test_mpls.py b/test/test_mpls.py index b2226a74..460a32d1 100644 --- a/test/test_mpls.py +++ b/test/test_mpls.py @@ -6,7 +6,7 @@ import socket from framework import VppTestCase, VppTestRunner from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \ VppMplsIpBind, VppIpMRoute, VppMRoutePath, \ - MRouteItfFlags, MRouteEntryFlags, DpoProto + MRouteItfFlags, MRouteEntryFlags, DpoProto, VppIpTable, VppMplsTable from vpp_mpls_tunnel_interface import VppMPLSTunnelInterface from scapy.packet import Raw @@ -60,9 +60,23 @@ class TestMPLS(VppTestCase): # setup both interfaces # assign them different tables. table_id = 0 + self.tables = [] + + tbl = VppMplsTable(self, 0) + tbl.add_vpp_config() + self.tables.append(tbl) for i in self.pg_interfaces: i.admin_up() + + if table_id != 0: + tbl = VppIpTable(self, table_id) + tbl.add_vpp_config() + self.tables.append(tbl) + tbl = VppIpTable(self, table_id, is_ip6=1) + tbl.add_vpp_config() + self.tables.append(tbl) + i.set_table_ip4(table_id) i.set_table_ip6(table_id) i.config_ip4() @@ -73,12 +87,15 @@ class TestMPLS(VppTestCase): table_id += 1 def tearDown(self): - super(TestMPLS, self).tearDown() for i in self.pg_interfaces: i.unconfig_ip4() i.unconfig_ip6() i.ip6_disable() + i.set_table_ip4(0) + i.set_table_ip6(0) + i.disable_mpls() i.admin_down() + super(TestMPLS, self).tearDown() # the default of 64 matches the IP packet TTL default def create_stream_labelled_ip4( @@ -1092,6 +1109,9 @@ class TestMPLSDisabled(VppTestCase): # create 2 pg interfaces self.create_pg_interfaces(range(2)) + self.tbl = VppMplsTable(self, 0) + self.tbl.add_vpp_config() + # PG0 is MPLS enalbed self.pg0.admin_up() self.pg0.config_ip4() @@ -1102,11 +1122,13 @@ class TestMPLSDisabled(VppTestCase): self.pg1.admin_up() def tearDown(self): - super(TestMPLSDisabled, self).tearDown() for i in self.pg_interfaces: i.unconfig_ip4() i.admin_down() + self.pg0.disable_mpls() + super(TestMPLSDisabled, self).tearDown() + def send_and_assert_no_replies(self, intf, pkts, remark): intf.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) @@ -1174,6 +1196,13 @@ class TestMPLSPIC(VppTestCase): # create 2 pg interfaces self.create_pg_interfaces(range(4)) + mpls_tbl = VppMplsTable(self, 0) + mpls_tbl.add_vpp_config() + tbl4 = VppIpTable(self, 1) + tbl4.add_vpp_config() + tbl6 = VppIpTable(self, 1, is_ip6=1) + tbl6.add_vpp_config() + # core links self.pg0.admin_up() self.pg0.config_ip4() @@ -1201,14 +1230,15 @@ class TestMPLSPIC(VppTestCase): self.pg3.resolve_ndp() def tearDown(self): - super(TestMPLSPIC, self).tearDown() self.pg0.disable_mpls() + self.pg1.disable_mpls() for i in self.pg_interfaces: i.unconfig_ip4() i.unconfig_ip6() i.set_table_ip4(0) i.set_table_ip6(0) i.admin_down() + super(TestMPLSPIC, self).tearDown() def test_mpls_ibgp_pic(self): """ MPLS iBGP PIC edge convergence @@ -1534,24 +1564,30 @@ class TestMPLSL2(VppTestCase): # create 2 pg interfaces self.create_pg_interfaces(range(2)) + # create the default MPLS table + self.tables = [] + tbl = VppMplsTable(self, 0) + tbl.add_vpp_config() + self.tables.append(tbl) + # use pg0 as the core facing interface self.pg0.admin_up() self.pg0.config_ip4() self.pg0.resolve_arp() self.pg0.enable_mpls() - # use the other 2 for customer facg L2 links + # use the other 2 for customer facing L2 links for i in self.pg_interfaces[1:]: i.admin_up() def tearDown(self): - super(TestMPLSL2, self).tearDown() for i in self.pg_interfaces[1:]: i.admin_down() self.pg0.disable_mpls() self.pg0.unconfig_ip4() self.pg0.admin_down() + super(TestMPLSL2, self).tearDown() def verify_capture_tunneled_ethernet(self, capture, sent, mpls_labels, ttl=255, top=None): diff --git a/test/test_nat.py b/test/test_nat.py index 1f2d17ab..73e9e217 100644 --- a/test/test_nat.py +++ b/test/test_nat.py @@ -549,6 +549,8 @@ class TestNAT44(MethodHolder): cls.pg0.configure_ipv4_neighbors() cls.overlapping_interfaces = list(list(cls.pg_interfaces[4:7])) + cls.vapi.ip_table_add_del(10, is_add=1) + cls.vapi.ip_table_add_del(20, is_add=1) cls.pg4._local_ip4 = "172.16.255.1" cls.pg4._local_ip4n = socket.inet_pton(socket.AF_INET, i.local_ip4) @@ -1797,6 +1799,8 @@ class TestNAT44(MethodHolder): self.pg0.unconfig_ip4() self.pg1.unconfig_ip4() + self.vapi.ip_table_add_del(vrf_id1, is_add=1) + self.vapi.ip_table_add_del(vrf_id2, is_add=1) self.pg0.set_table_ip4(vrf_id1) self.pg1.set_table_ip4(vrf_id2) self.pg0.config_ip4() @@ -1825,6 +1829,13 @@ class TestNAT44(MethodHolder): capture = self.pg2.get_capture(len(pkts)) self.verify_capture_out(capture, nat_ip2) + self.pg0.unconfig_ip4() + self.pg1.unconfig_ip4() + self.pg0.set_table_ip4(0) + self.pg1.set_table_ip4(0) + self.vapi.ip_table_add_del(vrf_id1, is_add=0) + self.vapi.ip_table_add_del(vrf_id2, is_add=0) + def test_vrf_feature_independent(self): """ NAT44 tenant VRF independent address pool mode """ @@ -3042,6 +3053,8 @@ class TestNAT64(MethodHolder): cls.ip6_interfaces.append(cls.pg_interfaces[2]) cls.ip4_interfaces = list(cls.pg_interfaces[1:2]) + cls.vapi.ip_table_add_del(cls.vrf1_id, is_add=1, is_ipv6=1) + cls.pg_interfaces[2].set_table_ip6(cls.vrf1_id) cls.pg0.generate_remote_hosts(2) diff --git a/test/test_neighbor.py b/test/test_neighbor.py index 1c7cc267..68dde2fb 100644 --- a/test/test_neighbor.py +++ b/test/test_neighbor.py @@ -5,7 +5,8 @@ from socket import AF_INET, AF_INET6, inet_pton from framework import VppTestCase, VppTestRunner from vpp_neighbor import VppNeighbor, find_nbr -from vpp_ip_route import VppIpRoute, VppRoutePath, find_route +from vpp_ip_route import VppIpRoute, VppRoutePath, find_route, \ + VppIpTable from scapy.packet import Raw from scapy.layers.l2 import Ether, ARP, Dot1Q @@ -39,11 +40,13 @@ class ARPTestCase(VppTestCase): self.pg1.config_ip6() # pg3 in a different VRF + self.tbl = VppIpTable(self, 1) + self.tbl.add_vpp_config() + self.pg3.set_table_ip4(1) self.pg3.config_ip4() def tearDown(self): - super(ARPTestCase, self).tearDown() self.pg0.unconfig_ip4() self.pg0.unconfig_ip6() @@ -51,10 +54,13 @@ class ARPTestCase(VppTestCase): self.pg1.unconfig_ip6() self.pg3.unconfig_ip4() + self.pg3.set_table_ip4(0) for i in self.pg_interfaces: i.admin_down() + super(ARPTestCase, self).tearDown() + def verify_arp_req(self, rx, smac, sip, dip): ether = rx[Ether] self.assertEqual(ether.dst, "ff:ff:ff:ff:ff:ff") @@ -1080,6 +1086,62 @@ class ARPTestCase(VppTestCase): self.pg0.remote_ip4, self.pg1.remote_hosts[1].ip4) + def test_arp_static(self): + """ ARP Static""" + self.pg2.generate_remote_hosts(3) + + # + # Add a static ARP entry + # + static_arp = VppNeighbor(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[1].mac, + self.pg2.remote_hosts[1].ip4, + is_static=1) + static_arp.add_vpp_config() + + # + # Add the connected prefix to the interface + # + self.pg2.config_ip4() + + # + # We should now find the adj-fib + # + self.assertTrue(find_nbr(self, + self.pg2.sw_if_index, + self.pg2.remote_hosts[1].ip4, + is_static=1)) + self.assertTrue(find_route(self, + self.pg2.remote_hosts[1].ip4, + 32)) + + # + # remove the connected + # + self.pg2.unconfig_ip4() + + # + # put the interface into table 1 + # + self.pg2.set_table_ip4(1) + + # + # configure the same connected and expect to find the + # adj fib in the new table + # + self.pg2.config_ip4() + self.assertTrue(find_route(self, + self.pg2.remote_hosts[1].ip4, + 32, + table_id=1)) + + # + # clean-up + # + self.pg2.unconfig_ip4() + self.pg2.set_table_ip4(0) + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_ip_route.py b/test/vpp_ip_route.py index 2c489e3c..b7993793 100644 --- a/test/vpp_ip_route.py +++ b/test/vpp_ip_route.py @@ -54,6 +54,46 @@ def find_route(test, ip_addr, len, table_id=0, inet=AF_INET): return False +class VppIpTable(VppObject): + + def __init__(self, + test, + table_id, + is_ip6=0): + self._test = test + self.table_id = table_id + self.is_ip6 = is_ip6 + + def add_vpp_config(self): + self._test.vapi.ip_table_add_del( + self.table_id, + is_ipv6=self.is_ip6, + is_add=1) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.ip_table_add_del( + self.table_id, + is_ipv6=self.is_ip6, + is_add=0) + + def query_vpp_config(self): + # find the default route + return find_route(self._test, + "::" if self.is_ip6 else "0.0.0.0", + 0, + self.table_id, + inet=AF_INET6 if self.is_ip6 == 1 else AF_INET) + + def __str__(self): + return self.object_id() + + def object_id(self): + return ("table-%s-%d" % + ("v6" if self.is_ip6 == 1 else "v4", + self.table_id)) + + class VppRoutePath(object): def __init__( @@ -391,6 +431,39 @@ class VppMplsIpBind(VppObject): self.dest_addr_len)) +class VppMplsTable(VppObject): + + def __init__(self, + test, + table_id): + self._test = test + self.table_id = table_id + + def add_vpp_config(self): + self._test.vapi.mpls_table_add_del( + self.table_id, + is_add=1) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.mpls_table_add_del( + self.table_id, + is_add=0) + + def query_vpp_config(self): + # find the default route + dump = self._test.vapi.mpls_fib_dump() + if len(dump): + return True + return False + + def __str__(self): + return self.object_id() + + def object_id(self): + return ("table-mpls-%d" % (self.table_id)) + + class VppMplsRoute(VppObject): """ MPLS Route/LSP diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index b70da026..519aff80 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -652,6 +652,24 @@ class VppPapiProvider(object): return self.api(self.papi.delete_loopback, {'sw_if_index': sw_if_index, }) + def ip_table_add_del(self, + table_id, + is_add=1, + is_ipv6=0): + """ + + :param table_id + :param is_add: (Default value = 1) + :param is_ipv6: (Default value = 0) + + """ + + return self.api( + self.papi.ip_table_add_del, + {'table_id': table_id, + 'is_add': is_add, + 'is_ipv6': is_ipv6}) + def ip_add_del_route( self, dst_address, @@ -664,7 +682,6 @@ class VppPapiProvider(object): next_hop_n_out_labels=0, next_hop_out_label_stack=[], next_hop_via_label=MPLS_LABEL_INVALID, - create_vrf_if_needed=0, is_resolve_host=0, is_resolve_attached=0, classify_table_index=0xFFFFFFFF, @@ -687,7 +704,6 @@ class VppPapiProvider(object): :param vrf_id: (Default value = 0) :param lookup_in_vrf: (Default value = 0) :param classify_table_index: (Default value = 0xFFFFFFFF) - :param create_vrf_if_needed: (Default value = 0) :param is_add: (Default value = 1) :param is_drop: (Default value = 0) :param is_ipv6: (Default value = 0) @@ -707,7 +723,6 @@ class VppPapiProvider(object): 'table_id': table_id, 'classify_table_index': classify_table_index, 'next_hop_table_id': next_hop_table_id, - 'create_vrf_if_needed': create_vrf_if_needed, 'is_add': is_add, 'is_drop': is_drop, 'is_unreach': is_unreach, @@ -912,6 +927,22 @@ class VppPapiProvider(object): def mpls_fib_dump(self): return self.api(self.papi.mpls_fib_dump, {}) + def mpls_table_add_del( + self, + table_id, + is_add=1): + """ + + :param table_id + :param is_add: (Default value = 1) + + """ + + return self.api( + self.papi.mpls_table_add_del, + {'mt_table_id': table_id, + 'mt_is_add': is_add}) + def mpls_route_add_del( self, label, @@ -925,7 +956,6 @@ class VppPapiProvider(object): next_hop_n_out_labels=0, next_hop_out_label_stack=[], next_hop_via_label=MPLS_LABEL_INVALID, - create_vrf_if_needed=0, is_resolve_host=0, is_resolve_attached=0, is_interface_rx=0, @@ -947,7 +977,6 @@ class VppPapiProvider(object): :param vrf_id: (Default value = 0) :param lookup_in_vrf: (Default value = 0) :param classify_table_index: (Default value = 0xFFFFFFFF) - :param create_vrf_if_needed: (Default value = 0) :param is_add: (Default value = 1) :param is_drop: (Default value = 0) :param is_ipv6: (Default value = 0) @@ -968,7 +997,6 @@ class VppPapiProvider(object): 'mr_eos': eos, 'mr_table_id': table_id, 'mr_classify_table_index': classify_table_index, - 'mr_create_table_if_needed': create_vrf_if_needed, 'mr_is_add': is_add, 'mr_is_classify': is_classify, 'mr_is_multipath': is_multipath, @@ -994,7 +1022,6 @@ class VppPapiProvider(object): table_id=0, ip_table_id=0, is_ip4=1, - create_vrf_if_needed=0, is_bind=1): """ """ @@ -1003,7 +1030,6 @@ class VppPapiProvider(object): {'mb_mpls_table_id': table_id, 'mb_label': label, 'mb_ip_table_id': ip_table_id, - 'mb_create_table_if_needed': create_vrf_if_needed, 'mb_is_bind': is_bind, 'mb_is_ip4': is_ip4, 'mb_address_length': dst_address_length, @@ -1020,7 +1046,6 @@ class VppPapiProvider(object): next_hop_n_out_labels=0, next_hop_out_label_stack=[], next_hop_via_label=MPLS_LABEL_INVALID, - create_vrf_if_needed=0, is_add=1, l2_only=0, is_multicast=0): @@ -1034,7 +1059,6 @@ class VppPapiProvider(object): :param vrf_id: (Default value = 0) :param lookup_in_vrf: (Default value = 0) :param classify_table_index: (Default value = 0xFFFFFFFF) - :param create_vrf_if_needed: (Default value = 0) :param is_add: (Default value = 1) :param is_drop: (Default value = 0) :param is_ipv6: (Default value = 0) @@ -1844,7 +1868,6 @@ class VppPapiProvider(object): i_flags, rpf_id=0, table_id=0, - create_vrf_if_needed=0, is_add=1, is_ipv6=0, is_local=0): @@ -1857,7 +1880,6 @@ class VppPapiProvider(object): 'itf_flags': i_flags, 'table_id': table_id, 'rpf_id': rpf_id, - 'create_vrf_if_needed': create_vrf_if_needed, 'is_add': is_add, 'is_ipv6': is_ipv6, 'is_local': is_local, -- cgit 1.2.3-korg