aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/scripts/vnet/dhcp/proxy3
-rw-r--r--src/vat/api_format.c56
-rw-r--r--src/vnet/dhcp/client.c2
-rw-r--r--src/vnet/dhcp/dhcp.api12
-rw-r--r--src/vnet/dhcp/dhcp4_packet.h5
-rw-r--r--src/vnet/dhcp/dhcp4_proxy_node.c151
-rw-r--r--src/vnet/dhcp/dhcp6_proxy_node.c158
-rw-r--r--src/vnet/dhcp/dhcp_api.c66
-rw-r--r--src/vnet/dhcp/dhcp_proxy.c129
-rw-r--r--src/vnet/dhcp/dhcp_proxy.h85
-rw-r--r--src/vnet/ip/ip6_packet.h2
-rw-r--r--src/vnet/pg/input.c6
-rw-r--r--test/test_dhcp.py356
13 files changed, 796 insertions, 235 deletions
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 <vnet/api_errno.h>
#include <vnet/dhcp/dhcp_proxy.h>
#include <vnet/dhcp/client.h>
+#include <vnet/fib/fib_table.h>
#include <vnet/vnet_msg_enum.h>
@@ -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,9 +58,10 @@ 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
@@ -68,22 +69,47 @@ typedef struct dhcp_server_t_ {
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);
/**
@@ -192,6 +219,18 @@ void dhcp_vss_walk(fib_protocol_t proto,
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
*/
static inline dhcp_vss_t *
@@ -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]
@@ -453,6 +469,128 @@ class TestDHCP(VppTestCase):
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,
+ fib_id=1, oui=4)
+
+ #
# Remove the VSS config
# relayed DHCP has default vlaues in the option.
#
@@ -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,