Skip to content

Instantly share code, notes, and snippets.

@haxibami
Last active March 30, 2026 14:54
Show Gist options
  • Select an option

  • Save haxibami/256afc38c85fb64be682f3511602e15e to your computer and use it in GitHub Desktop.

Select an option

Save haxibami/256afc38c85fb64be682f3511602e15e to your computer and use it in GitHub Desktop.
VPP IPv6 HGW patch

VPP IPv6 HGW patch

About

My VPP custom patch for home router use. Tested on VPP 26.02.

Contents

  • RA: Allow setting RDNSS
  • DHCPv6 / RA: Renew prefix's (e.g. /64) lifetime when its parent (e.g. /56) is renewed
  • DHCPv6 Proxy
    • Stop listening on UDP 546 (which conflicts with DHCPv6 client, and seemingly useless)
    • Stop broadcasting reply message: resolve client correctly with ip6-lookup

TODO

  • Add tests?
diff --git a/src/plugins/dhcp/dhcp6_proxy_node.c b/src/plugins/dhcp/dhcp6_proxy_node.c
index 55348ff86..56da84cf6 100644
--- a/src/plugins/dhcp/dhcp6_proxy_node.c
+++ b/src/plugins/dhcp/dhcp6_proxy_node.c
@@ -31,6 +31,18 @@ typedef enum
DHCPV6_PROXY_TO_SERVER_INPUT_N_NEXT,
} dhcpv6_proxy_to_server_input_next_t;
+#define foreach_dhcpv6_proxy_to_client_input_next \
+ _ (DROP, "error-drop") \
+ _ (LOOKUP, "ip6-lookup")
+
+typedef enum
+{
+#define _(s, n) DHCPV6_PROXY_TO_CLIENT_INPUT_NEXT_##s,
+ foreach_dhcpv6_proxy_to_client_input_next
+#undef _
+ DHCPV6_PROXY_TO_CLIENT_INPUT_N_NEXT,
+} dhcpv6_proxy_to_client_input_next_t;
+
typedef struct
{
/* 0 => to server, 1 => to client */
@@ -549,7 +561,7 @@ dhcpv6_proxy_to_client_input (vlib_main_t * vm,
{
u32 n_left_from, *from;
- ethernet_main_t *em = vnet_get_ethernet_main ();
+ u32 next_index, *to_next;
dhcp_proxy_main_t *dm = &dhcp_proxy_main;
dhcp_proxy_t *proxy;
dhcp_server_t *server;
@@ -558,243 +570,190 @@ dhcpv6_proxy_to_client_input (vlib_main_t * vm,
from = vlib_frame_vector_args (from_frame);
n_left_from = from_frame->n_vectors;
+ next_index = node->cached_next_index;
while (n_left_from > 0)
{
- u32 bi0;
- vlib_buffer_t *b0;
- udp_header_t *u0, *u1 = 0;
- dhcpv6_relay_hdr_t *h0;
- ip6_header_t *ip1 = 0, *ip0;
- ip6_address_t *ia0 = 0;
- ip6_address_t client_address;
- ethernet_interface_t *ei0;
- ethernet_header_t *mac0;
- vnet_hw_interface_t *hi0;
- vlib_frame_t *f0;
- u32 *to_next0;
- u32 sw_if_index = ~0;
- u32 original_sw_if_index = ~0;
- vnet_sw_interface_t *si0;
- u32 inner_vlan = (u32) ~ 0;
- u32 outer_vlan = (u32) ~ 0;
- u32 error0 = (u32) ~ 0;
- vnet_sw_interface_t *swif;
- dhcpv6_option_t *r0 = 0, *o;
- u16 len = 0;
- u8 interface_opt_flag = 0;
- u8 relay_msg_opt_flag = 0;
- ip6_main_t *im = &ip6_main;
- u32 server_fib_idx, client_fib_idx;
-
- bi0 = from[0];
- from += 1;
- n_left_from -= 1;
-
- b0 = vlib_get_buffer (vm, bi0);
- h0 = vlib_buffer_get_current (b0);
-
- if (DHCPV6_MSG_RELAY_REPL != h0->msg_type)
+ u32 n_left_to_next;
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
{
- error0 = DHCPV6_PROXY_ERROR_WRONG_MESSAGE_TYPE;
+ u32 bi0;
+ vlib_buffer_t *b0;
+ udp_header_t *u0, *u1 = 0;
+ dhcpv6_relay_hdr_t *h0;
+ ip6_header_t *ip1 = 0, *ip0;
+ ip6_address_t *ia0 = 0;
+ ip6_address_t client_address;
+ u32 sw_if_index = ~0;
+ u32 original_sw_if_index = ~0;
+ u32 error0 = (u32) ~0;
+ u32 next0 = DHCPV6_PROXY_TO_CLIENT_INPUT_NEXT_LOOKUP;
+ vnet_sw_interface_t *swif;
+ dhcpv6_option_t *r0 = 0, *o;
+ u16 len = 0;
+ u8 interface_opt_flag = 0;
+ u8 relay_msg_opt_flag = 0;
+ ip6_main_t *im = &ip6_main;
+ u32 server_fib_idx, client_fib_idx;
- drop_packet:
- vlib_node_increment_counter (vm, dhcpv6_proxy_to_client_node.index,
- error0, 1);
+ bi0 = from[0];
+ from += 1;
+ n_left_from -= 1;
- f0 = vlib_get_frame_to_node (vm, dm->error_drop_node_index);
- to_next0 = vlib_frame_vector_args (f0);
- to_next0[0] = bi0;
- f0->n_vectors = 1;
- vlib_put_frame_to_node (vm, dm->error_drop_node_index, f0);
- goto do_trace;
- }
- /* hop count seems not need to be checked */
- if (HOP_COUNT_LIMIT < h0->hop_count)
- {
- error0 = DHCPV6_RELAY_PKT_DROP_MAX_HOPS;
- goto drop_packet;
- }
- u0 = (void *) h0 - (sizeof (*u0));
- ip0 = (void *) u0 - (sizeof (*ip0));
+ b0 = vlib_get_buffer (vm, bi0);
+ h0 = vlib_buffer_get_current (b0);
+
+ if (DHCPV6_MSG_RELAY_REPL != h0->msg_type)
+ {
+ error0 = DHCPV6_PROXY_ERROR_WRONG_MESSAGE_TYPE;
+ next0 = DHCPV6_PROXY_TO_CLIENT_INPUT_NEXT_DROP;
+ goto do_trace;
+ }
+ /* hop count seems not need to be checked */
+ if (HOP_COUNT_LIMIT < h0->hop_count)
+ {
+ error0 = DHCPV6_RELAY_PKT_DROP_MAX_HOPS;
+ next0 = DHCPV6_PROXY_TO_CLIENT_INPUT_NEXT_DROP;
+ goto do_trace;
+ }
+ u0 = (void *) h0 - (sizeof (*u0));
+ ip0 = (void *) u0 - (sizeof (*ip0));
- vlib_buffer_advance (b0, sizeof (*h0));
- o = vlib_buffer_get_current (b0);
+ vlib_buffer_advance (b0, sizeof (*h0));
+ o = vlib_buffer_get_current (b0);
- /* Parse through TLVs looking for option 18 (DHCPV6_OPTION_INTERFACE_ID)
- _and_ option 9 (DHCPV6_OPTION_RELAY_MSG) option which must be there.
- Currently assuming no other options need to be processed
- The interface-ID is the FIB number we need
- to track down the client-facing interface */
+ /* Parse through TLVs looking for option 18 (DHCPV6_OPTION_INTERFACE_ID)
+ _and_ option 9 (DHCPV6_OPTION_RELAY_MSG) option which must be there.
+ Currently assuming no other options need to be processed
+ The interface-ID is the FIB number we need
+ to track down the client-facing interface */
- while ((u8 *) o < (b0->data + b0->current_data + b0->current_length))
- {
- if (DHCPV6_OPTION_INTERFACE_ID == clib_net_to_host_u16 (o->option))
+ while ((u8 *) o < (b0->data + b0->current_data + b0->current_length))
{
- interface_opt_flag = 1;
- if (clib_net_to_host_u16 (o->length) == sizeof (sw_if_index))
- sw_if_index =
- clib_net_to_host_u32 (((dhcpv6_int_id_t *) o)->int_idx);
- if (sw_if_index >= vec_len (im->fib_index_by_sw_if_index))
+ if (DHCPV6_OPTION_INTERFACE_ID == clib_net_to_host_u16 (o->option))
{
- error0 = DHCPV6_PROXY_ERROR_WRONG_INTERFACE_ID_OPTION;
- goto drop_packet;
+ interface_opt_flag = 1;
+ if (clib_net_to_host_u16 (o->length) == sizeof (sw_if_index))
+ sw_if_index = clib_net_to_host_u32 (((dhcpv6_int_id_t *) o)->int_idx);
+ if (sw_if_index >= vec_len (im->fib_index_by_sw_if_index))
+ {
+ error0 = DHCPV6_PROXY_ERROR_WRONG_INTERFACE_ID_OPTION;
+ next0 = DHCPV6_PROXY_TO_CLIENT_INPUT_NEXT_DROP;
+ goto do_trace;
+ }
+ }
+ if (DHCPV6_OPTION_RELAY_MSG == clib_net_to_host_u16 (o->option))
+ {
+ relay_msg_opt_flag = 1;
+ r0 = vlib_buffer_get_current (b0);
}
+ if ((relay_msg_opt_flag == 1) && (interface_opt_flag == 1))
+ break;
+ vlib_buffer_advance (b0, sizeof (*o) + clib_net_to_host_u16 (o->length));
+ o =
+ (dhcpv6_option_t *) (((uword) o) + clib_net_to_host_u16 (o->length) + sizeof (*o));
}
- if (DHCPV6_OPTION_RELAY_MSG == clib_net_to_host_u16 (o->option))
+
+ if ((relay_msg_opt_flag == 0) || (r0 == 0))
{
- relay_msg_opt_flag = 1;
- r0 = vlib_buffer_get_current (b0);
+ error0 = DHCPV6_PROXY_ERROR_NO_RELAY_MESSAGE_OPTION;
+ next0 = DHCPV6_PROXY_TO_CLIENT_INPUT_NEXT_DROP;
+ goto do_trace;
}
- if ((relay_msg_opt_flag == 1) && (interface_opt_flag == 1))
- break;
- vlib_buffer_advance (b0,
- sizeof (*o) +
- clib_net_to_host_u16 (o->length));
- o =
- (dhcpv6_option_t *) (((uword) o) +
- clib_net_to_host_u16 (o->length) +
- sizeof (*o));
- }
- if ((relay_msg_opt_flag == 0) || (r0 == 0))
- {
- error0 = DHCPV6_PROXY_ERROR_NO_RELAY_MESSAGE_OPTION;
- goto drop_packet;
- }
-
- if ((u32) ~ 0 == sw_if_index)
- {
- error0 = DHCPV6_PROXY_ERROR_NO_CIRCUIT_ID_OPTION;
- goto drop_packet;
- }
-
- //Advance buffer to start of encapsulated DHCPv6 message
- vlib_buffer_advance (b0, sizeof (*r0));
+ if ((u32) ~0 == sw_if_index)
+ {
+ error0 = DHCPV6_PROXY_ERROR_NO_CIRCUIT_ID_OPTION;
+ next0 = DHCPV6_PROXY_TO_CLIENT_INPUT_NEXT_DROP;
+ goto do_trace;
+ }
- client_fib_idx = im->mfib_index_by_sw_if_index[sw_if_index];
- proxy = dhcp_get_proxy (dm, client_fib_idx, FIB_PROTOCOL_IP6);
+ // Advance buffer to start of encapsulated DHCPv6 message
+ vlib_buffer_advance (b0, sizeof (*r0));
- if (NULL == proxy)
- {
- error0 = DHCPV6_PROXY_ERROR_NO_SERVER;
- goto drop_packet;
- }
+ client_fib_idx = im->mfib_index_by_sw_if_index[sw_if_index];
+ proxy = dhcp_get_proxy (dm, client_fib_idx, FIB_PROTOCOL_IP6);
- server_fib_idx = im->fib_index_by_sw_if_index
- [vnet_buffer (b0)->sw_if_index[VLIB_RX]];
+ if (NULL == proxy)
+ {
+ error0 = DHCPV6_PROXY_ERROR_NO_SERVER;
+ next0 = DHCPV6_PROXY_TO_CLIENT_INPUT_NEXT_DROP;
+ goto do_trace;
+ }
- 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;
- }
- }
+ server_fib_idx = im->fib_index_by_sw_if_index[vnet_buffer (b0)->sw_if_index[VLIB_RX]];
- //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;
+ }
+ }
- server_found:
- vnet_buffer (b0)->sw_if_index[VLIB_TX] = original_sw_if_index
- = sw_if_index;
+ // drop packet if not from server with configured address or FIB
+ error0 = DHCPV6_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS;
+ next0 = DHCPV6_PROXY_TO_CLIENT_INPUT_NEXT_DROP;
+ goto do_trace;
- swif = vnet_get_sw_interface (vnm, original_sw_if_index);
- if (swif->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED)
- sw_if_index = swif->unnumbered_sw_if_index;
+ server_found:
+ original_sw_if_index = sw_if_index;
+ vnet_buffer (b0)->sw_if_index[VLIB_RX] = original_sw_if_index;
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0;
- /*
- * udp_local hands us the DHCPV6 header, need udp hdr,
- * ip hdr to relay to client
- */
- vlib_buffer_advance (b0, -(sizeof (*u1)));
- u1 = vlib_buffer_get_current (b0);
+ swif = vnet_get_sw_interface (vnm, original_sw_if_index);
+ if (swif->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED)
+ sw_if_index = swif->unnumbered_sw_if_index;
- vlib_buffer_advance (b0, -(sizeof (*ip1)));
- ip1 = vlib_buffer_get_current (b0);
+ /*
+ * udp_local hands us the DHCPV6 header, need udp hdr,
+ * ip hdr to relay to client
+ */
+ vlib_buffer_advance (b0, -(sizeof (*u1)));
+ u1 = vlib_buffer_get_current (b0);
- copy_ip6_address (&client_address, &h0->peer_addr);
+ vlib_buffer_advance (b0, -(sizeof (*ip1)));
+ ip1 = vlib_buffer_get_current (b0);
- ia0 = ip6_interface_first_address (&ip6_main, sw_if_index);
- if (ia0 == 0)
- {
- error0 = DHCPV6_PROXY_ERROR_NO_INTERFACE_ADDRESS;
- goto drop_packet;
- }
+ copy_ip6_address (&client_address, &h0->peer_addr);
- len = clib_net_to_host_u16 (r0->length);
- clib_memset (ip1, 0, sizeof (*ip1));
- copy_ip6_address (&ip1->dst_address, &client_address);
- u1->checksum = 0;
- u1->src_port = clib_net_to_host_u16 (UDP_DST_PORT_dhcpv6_to_server);
- u1->dst_port = clib_net_to_host_u16 (UDP_DST_PORT_dhcpv6_to_client);
- u1->length = clib_host_to_net_u16 (len + sizeof (udp_header_t));
-
- ip1->ip_version_traffic_class_and_flow_label =
- ip0->ip_version_traffic_class_and_flow_label & 0x00000fff;
- ip1->payload_length = u1->length;
- ip1->protocol = PROTO_UDP;
- ip1->hop_limit = HOP_COUNT_LIMIT;
- copy_ip6_address (&ip1->src_address, ia0);
-
- u1->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip1,
- &bogus_length);
- ASSERT (bogus_length == 0);
-
- vlib_buffer_advance (b0, -(sizeof (ethernet_header_t)));
- si0 = vnet_get_sw_interface (vnm, original_sw_if_index);
- if (si0->type == VNET_SW_INTERFACE_TYPE_SUB)
- {
- if (si0->sub.eth.flags.one_tag == 1)
- {
- vlib_buffer_advance (b0, -4 /* space for 1 VLAN tag */ );
- outer_vlan = (si0->sub.eth.outer_vlan_id << 16) | 0x86dd;
- }
- else if (si0->sub.eth.flags.two_tags == 1)
+ ia0 = ip6_interface_first_address (&ip6_main, sw_if_index);
+ if (ia0 == 0)
{
- vlib_buffer_advance (b0, -8 /* space for 2 VLAN tag */ );
- outer_vlan = (si0->sub.eth.outer_vlan_id << 16) | 0x8100;
- inner_vlan = (si0->sub.eth.inner_vlan_id << 16) | 0x86dd;
+ error0 = DHCPV6_PROXY_ERROR_NO_INTERFACE_ADDRESS;
+ next0 = DHCPV6_PROXY_TO_CLIENT_INPUT_NEXT_DROP;
+ goto do_trace;
}
- }
- mac0 = vlib_buffer_get_current (b0);
+ len = clib_net_to_host_u16 (r0->length);
+ clib_memset (ip1, 0, sizeof (*ip1));
+ copy_ip6_address (&ip1->dst_address, &client_address);
+ u1->checksum = 0;
+ u1->src_port = clib_net_to_host_u16 (UDP_DST_PORT_dhcpv6_to_server);
+ u1->dst_port = clib_net_to_host_u16 (UDP_DST_PORT_dhcpv6_to_client);
+ u1->length = clib_host_to_net_u16 (len + sizeof (udp_header_t));
- hi0 = vnet_get_sup_hw_interface (vnm, original_sw_if_index);
- ei0 = pool_elt_at_index (em->interfaces, hi0->hw_instance);
- clib_memcpy (mac0->src_address, &ei0->address,
- sizeof (mac0->src_address));
- clib_memset (&mac0->dst_address, 0xff, sizeof (mac0->dst_address));
+ ip1->ip_version_traffic_class_and_flow_label =
+ ip0->ip_version_traffic_class_and_flow_label & 0x00000fff;
+ ip1->payload_length = u1->length;
+ ip1->protocol = PROTO_UDP;
+ ip1->hop_limit = HOP_COUNT_LIMIT;
+ copy_ip6_address (&ip1->src_address, ia0);
- if (si0->type == VNET_SW_INTERFACE_TYPE_SUB && outer_vlan != (u32) ~ 0)
- {
- mac0->type = (si0->sub.eth.flags.dot1ad == 1) ?
- clib_net_to_host_u16 (0x88a8) : clib_net_to_host_u16 (0x8100);
- u32 *vlan_tag = (u32 *) (mac0 + 1);
- *vlan_tag = clib_host_to_net_u32 (outer_vlan);
- if (inner_vlan != (u32) ~ 0)
+ u1->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip1, &bogus_length);
+ ASSERT (bogus_length == 0);
+
+ do_trace:
+ if (PREDICT_FALSE (error0 != (u32) ~0))
{
- u32 *inner_vlan_tag = (u32 *) (vlan_tag + 1);
- *inner_vlan_tag = clib_host_to_net_u32 (inner_vlan);
+ vlib_node_increment_counter (vm, dhcpv6_proxy_to_client_node.index, error0, 1);
}
- }
- else
- {
- mac0->type = clib_net_to_host_u16 (0x86dd);
- }
-
- /* $$$ consider adding a dynamic next to the graph node, for performance */
- f0 = vlib_get_frame_to_node (vm, hi0->output_node_index);
- to_next0 = vlib_frame_vector_args (f0);
- to_next0[0] = bi0;
- f0->n_vectors = 1;
- vlib_put_frame_to_node (vm, hi0->output_node_index, f0);
- do_trace:
if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
{
dhcpv6_proxy_trace_t *tr = vlib_add_trace (vm, node,
@@ -806,21 +765,33 @@ dhcpv6_proxy_to_client_input (vlib_main_t * vm,
tr->original_sw_if_index = original_sw_if_index;
tr->sw_if_index = sw_if_index;
}
+ 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);
+ }
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
}
return from_frame->n_vectors;
-
}
-VLIB_REGISTER_NODE (dhcpv6_proxy_to_client_node, static) = {
- .function = dhcpv6_proxy_to_client_input,
- .name = "dhcpv6-proxy-to-client",
- /* Takes a vector of packets. */
- .vector_size = sizeof (u32),
-
- .n_errors = DHCPV6_PROXY_N_ERROR,
- .error_strings = dhcpv6_proxy_error_strings,
- .format_buffer = format_dhcpv6_proxy_header_with_length,
- .format_trace = format_dhcpv6_proxy_trace,
+VLIB_REGISTER_NODE (dhcpv6_proxy_to_client_node,
+ static) = { .function = dhcpv6_proxy_to_client_input,
+ .name = "dhcpv6-proxy-to-client",
+ /* Takes a vector of packets. */
+ .vector_size = sizeof (u32),
+
+ .n_errors = DHCPV6_PROXY_N_ERROR,
+ .error_strings = dhcpv6_proxy_error_strings,
+ .n_next_nodes = DHCPV6_PROXY_TO_CLIENT_INPUT_N_NEXT,
+ .next_nodes = {
+#define _(s, n) [DHCPV6_PROXY_TO_CLIENT_INPUT_NEXT_##s] = n,
+ foreach_dhcpv6_proxy_to_client_input_next
+#undef _
+ },
+ .format_buffer = format_dhcpv6_proxy_header_with_length,
+ .format_trace = format_dhcpv6_proxy_trace,
#if 0
.unformat_buffer = unformat_dhcpv6_proxy_header,
#endif
@@ -888,8 +859,6 @@ dhcp6_proxy_set_server (ip46_address_t * addr,
mfib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP6,
MFIB_SOURCE_DHCP);
- udp_unregister_dst_port (vm, UDP_DST_PORT_dhcpv6_to_client,
- 0 /* is_ip6 */ );
udp_unregister_dst_port (vm, UDP_DST_PORT_dhcpv6_to_server,
0 /* is_ip6 */ );
}
@@ -926,9 +895,6 @@ dhcp6_proxy_set_server (ip46_address_t * addr,
MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
mfib_table_lock (rx_fib_index, FIB_PROTOCOL_IP6, MFIB_SOURCE_DHCP);
- udp_register_dst_port (vm, UDP_DST_PORT_dhcpv6_to_client,
- dhcpv6_proxy_to_client_node.index,
- 0 /* is_ip6 */ );
udp_register_dst_port (vm, UDP_DST_PORT_dhcpv6_to_server,
dhcpv6_proxy_to_server_node.index,
0 /* is_ip6 */ );
diff --git a/src/vnet/ip/icmp46_packet.h b/src/vnet/ip/icmp46_packet.h
index 9cca6d134..c5c0b2d8b 100644
--- a/src/vnet/ip/icmp46_packet.h
+++ b/src/vnet/ip/icmp46_packet.h
@@ -248,6 +248,13 @@ typedef CLIB_PACKED (struct
u16 unused; u32 mtu;
}) icmp6_neighbor_discovery_mtu_option_t;
+typedef CLIB_PACKED (struct {
+ icmp6_neighbor_discovery_option_header_t header;
+ u16 reserved;
+ u32 lifetime;
+ ip6_address_t dns_servers[0];
+}) icmp6_neighbor_discovery_recursive_dns_server_option_t;
+
typedef CLIB_PACKED (struct
{
icmp6_neighbor_discovery_option_header_t header;
diff --git a/src/vnet/ip6-nd/ip6_nd.api b/src/vnet/ip6-nd/ip6_nd.api
index 3ddf25103..646c0d53a 100644
--- a/src/vnet/ip6-nd/ip6_nd.api
+++ b/src/vnet/ip6-nd/ip6_nd.api
@@ -41,6 +41,8 @@ import "vnet/interface_types.api";
@param lifetime -
@param initial_count -
@param initial_interval -
+ @param reachable_time -
+ @param retransmit_interval -
*/
autoreply define sw_interface_ip6nd_ra_config
{
@@ -60,6 +62,8 @@ autoreply define sw_interface_ip6nd_ra_config
u32 lifetime;
u32 initial_count;
u32 initial_interval;
+ u32 reachable_time;
+ u32 retransmit_interval;
};
/** \brief IPv6 router advertisement prefix config request
@@ -140,6 +144,24 @@ typedef ip6nd_ra_prefix
bool no_advertise;
};
+/** \brief IPv6 router advertisement RDNSS (DNS server) config request
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param sw_if_index - The interface the RA DNS server config applies to
+ @param is_no - 0 for add, 1 for remove
+ @param server - IPv6 address of the DNS server
+ @param lifetime - DNS server lifetime in seconds
+*/
+autoreply define sw_interface_ip6nd_ra_dns_server_config
+{
+ u32 client_index;
+ u32 context;
+ vl_api_interface_index_t sw_if_index;
+ bool is_no;
+ vl_api_ip6_address_t server;
+ u32 lifetime;
+};
+
/** \brief Dump IPv6 Router Advertisements details on a per-interface basis
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
diff --git a/src/vnet/ip6-nd/ip6_nd_api.c b/src/vnet/ip6-nd/ip6_nd_api.c
index 218ea710e..7f6f7668e 100644
--- a/src/vnet/ip6-nd/ip6_nd_api.c
+++ b/src/vnet/ip6-nd/ip6_nd_api.c
@@ -163,14 +163,11 @@ static void
VALIDATE_SW_IF_INDEX (mp);
- rv = ip6_ra_config (vm, ntohl (mp->sw_if_index),
- suppress, managed, other,
- ll_option, send_unicast, cease,
- default_router, ntohl (mp->lifetime),
- ntohl (mp->initial_count),
- ntohl (mp->initial_interval),
- ntohl (mp->max_interval),
- ntohl (mp->min_interval), is_no);
+ rv =
+ ip6_ra_config (vm, ntohl (mp->sw_if_index), suppress, managed, other, ll_option, send_unicast,
+ cease, default_router, ntohl (mp->lifetime), ntohl (mp->initial_count),
+ ntohl (mp->initial_interval), ntohl (mp->max_interval), ntohl (mp->min_interval),
+ ntohl (mp->reachable_time), ntohl (mp->retransmit_interval), is_no);
BAD_SW_IF_INDEX_LABEL;
@@ -208,6 +205,26 @@ static void
REPLY_MACRO (VL_API_SW_INTERFACE_IP6ND_RA_PREFIX_REPLY);
}
+static void
+vl_api_sw_interface_ip6nd_ra_dns_server_config_t_handler (
+ vl_api_sw_interface_ip6nd_ra_dns_server_config_t *mp)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ vl_api_sw_interface_ip6nd_ra_dns_server_config_reply_t *rmp;
+ ip6_address_t server_addr;
+ int rv = 0;
+
+ VALIDATE_SW_IF_INDEX (mp);
+
+ ip6_address_decode (mp->server, &server_addr);
+
+ rv =
+ ip6_ra_dns_server (vm, ntohl (mp->sw_if_index), &server_addr, ntohl (mp->lifetime), mp->is_no);
+
+ BAD_SW_IF_INDEX_LABEL;
+ REPLY_MACRO (VL_API_SW_INTERFACE_IP6ND_RA_DNS_SERVER_CONFIG_REPLY);
+}
+
static void
ip6_radv_prefix_encode (f64 now, const ip6_radv_prefix_t *in,
vl_api_ip6nd_ra_prefix_t *out)
diff --git a/src/vnet/ip6-nd/ip6_nd_test.c b/src/vnet/ip6-nd/ip6_nd_test.c
index cf5c0a145..8b9074af6 100644
--- a/src/vnet/ip6-nd/ip6_nd_test.c
+++ b/src/vnet/ip6-nd/ip6_nd_test.c
@@ -234,6 +234,8 @@ api_sw_interface_ip6nd_ra_config (vat_main_t * vam)
u32 lifetime = 0;
u32 initial_count = 0;
u32 initial_interval = 0;
+ u32 reachable_time = 0;
+ u32 retransmit_interval = 0;
int ret;
@@ -254,6 +256,10 @@ api_sw_interface_ip6nd_ra_config (vat_main_t * vam)
;
else if (unformat (i, "interval %d", &initial_interval))
;
+ else if (unformat (i, "reachable %d", &reachable_time))
+ ;
+ else if (unformat (i, "retrans %d", &retransmit_interval))
+ ;
else if (unformat (i, "suppress") || unformat (i, "surpress"))
suppress = 1;
else if (unformat (i, "managed"))
@@ -300,6 +306,8 @@ api_sw_interface_ip6nd_ra_config (vat_main_t * vam)
mp->cease = cease;
mp->is_no = is_no;
mp->default_router = default_router;
+ mp->reachable_time = reachable_time;
+ mp->retransmit_interval = retransmit_interval;
/* send it... */
S (mp);
@@ -308,6 +316,65 @@ api_sw_interface_ip6nd_ra_config (vat_main_t * vam)
W (ret);
return ret;
}
+
+static int
+api_sw_interface_ip6nd_ra_dns_server_config (vat_main_t *vam)
+{
+ unformat_input_t *i = vam->input;
+ vl_api_sw_interface_ip6nd_ra_dns_server_config_t *mp;
+ u32 sw_if_index = ~0;
+ u8 sw_if_index_set = 0;
+ u8 is_no = 0;
+ vl_api_ip6_address_t server_addr;
+ u32 lifetime = ~0;
+ u8 server_addr_set = 0;
+ int ret;
+
+ /* Parse args required to build the message */
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
+ sw_if_index_set = 1;
+ else if (unformat (i, "sw_if_index %d", &sw_if_index))
+ sw_if_index_set = 1;
+ else if (unformat (i, "del") || unformat (i, "no"))
+ is_no = 1;
+ else if (unformat (i, "server %U", unformat_ip6_address, &server_addr))
+ server_addr_set = 1;
+ else if (unformat (i, "lifetime %d", &lifetime))
+ ;
+ else
+ break;
+ }
+
+ if (sw_if_index_set == 0)
+ {
+ errmsg ("missing interface name or sw_if_index");
+ return -99;
+ }
+ if (server_addr_set == 0)
+ {
+ errmsg ("missing DNS server address");
+ return -99;
+ }
+
+ /* Construct the API message */
+ M (SW_INTERFACE_IP6ND_RA_DNS_SERVER_CONFIG, mp);
+
+ mp->sw_if_index = ntohl (sw_if_index);
+ mp->is_no = is_no;
+ mp->lifetime = ntohl (lifetime);
+
+ clib_memcpy (mp->server, server_addr, sizeof (server_addr));
+
+ /* send it... */
+ S (mp);
+
+ /* Wait for a reply... */
+ W (ret);
+ return ret;
+}
+
static int
api_ip6nd_proxy_enable_disable (vat_main_t *vam)
{
diff --git a/src/vnet/ip6-nd/ip6_ra.c b/src/vnet/ip6-nd/ip6_ra.c
index 7c2a3d342..433027231 100644
--- a/src/vnet/ip6-nd/ip6_ra.c
+++ b/src/vnet/ip6-nd/ip6_ra.c
@@ -511,6 +511,42 @@ icmp6_router_solicitation (vlib_main_t * vm,
}
}
+ /* add RDNSS option */
+ if (vec_len (radv_info->adv_dns_servers) > 0)
+ {
+ icmp6_neighbor_discovery_recursive_dns_server_option_t h;
+
+ u32 n_servers = vec_len (radv_info->adv_dns_servers);
+
+ h.header.type = ICMP6_NEIGHBOR_DISCOVERY_OPTION_recursive_dns_server;
+ h.header.n_data_u64s = 1 + 2 * n_servers;
+ h.reserved = 0;
+ h.lifetime = clib_host_to_net_u32 (radv_info->adv_dns_server_lifetime);
+
+ /* fixed length */
+ if (vlib_buffer_add_data (vm, &bi0, (void *) &h, sizeof (h)))
+ {
+ error0 = ICMP6_ERROR_ALLOC_FAILURE;
+ goto drop0;
+ }
+
+ payload_length += sizeof (h);
+
+ /* dynamic length */
+ int i;
+ for (i = 0; i < n_servers; i++)
+ {
+ if (vlib_buffer_add_data (vm, &bi0,
+ (void *) &radv_info->adv_dns_servers[i],
+ sizeof (ip6_address_t)))
+ {
+ error0 = ICMP6_ERROR_ALLOC_FAILURE;
+ goto drop0;
+ }
+ payload_length += sizeof (ip6_address_t);
+ }
+ }
+
/* add additional options before here */
/* finish building the router advertisement... */
@@ -1299,6 +1335,8 @@ ip6_ra_link_enable (u32 sw_if_index)
radv_info->initial_adverts_sent = radv_info->initial_adverts_count - 1;
radv_info->initial_adverts_interval = MAX_INITIAL_RTR_ADVERT_INTERVAL;
+ radv_info->adv_dns_server_lifetime = DEF_DEF_RTR_LIFETIME;
+
/* fill in delegate for this interface that will be needed later */
radv_info->adv_link_mtu =
vnet_sw_interface_get_mtu (vnet_get_main (), sw_if_index, VNET_MTU_IP6);
@@ -1326,6 +1364,8 @@ ip6_ra_delegate_disable (index_t rai)
pool_free (radv_info->adv_prefixes_pool);
+ vec_free (radv_info->adv_dns_servers);
+
mhash_free (&radv_info->address_to_prefix_index);
pool_put (ip6_ra_pool, radv_info);
@@ -1364,14 +1404,15 @@ ip6_ra_update_secondary_radv_info (ip6_address_t * address, u8 prefix_len,
pool_foreach (this_prefix, radv_info->adv_prefixes_pool)
{
- if (this_prefix->prefix_len == prefix_len
+ /* clang-format off */
+ if (this_prefix->prefix_len >= prefix_len
&& ip6_address_is_equal_masked (&this_prefix->prefix, address,
&mask))
{
int rv = ip6_ra_prefix (vm,
radv_info->sw_if_index,
- address,
- prefix_len,
+ &this_prefix->prefix,
+ this_prefix->prefix_len,
0 /* use_default */,
valid_time,
preferred_time,
@@ -1383,6 +1424,7 @@ ip6_ra_update_secondary_radv_info (ip6_address_t * address, u8 prefix_len,
if (rv != 0)
clib_warning ("ip6_neighbor_ra_prefix returned %d", rv);
}
+ /* clang-format on */
}
}
}
@@ -1581,12 +1623,10 @@ ip6_ra_publish (ip6_ra_report_t * r)
/* API support functions */
int
-ip6_ra_config (vlib_main_t * vm, u32 sw_if_index,
- u8 suppress, u8 managed, u8 other,
- u8 ll_option, u8 send_unicast, u8 cease,
- u8 use_lifetime, u32 lifetime,
- u32 initial_count, u32 initial_interval,
- u32 max_interval, u32 min_interval, u8 is_no)
+ip6_ra_config (vlib_main_t *vm, u32 sw_if_index, u8 suppress, u8 managed, u8 other, u8 ll_option,
+ u8 send_unicast, u8 cease, u8 use_lifetime, u32 lifetime, u32 initial_count,
+ u32 initial_interval, u32 max_interval, u32 min_interval, u32 reachable_time,
+ u32 retransmit_interval, u8 is_no)
{
ip6_ra_t *radv_info;
@@ -1664,6 +1704,17 @@ ip6_ra_config (vlib_main_t * vm, u32 sw_if_index,
0) ? ((is_no) ? MAX_INITIAL_RTR_ADVERT_INTERVAL : initial_interval) :
radv_info->initial_adverts_interval;
+ if (reachable_time != ~0)
+ {
+ radv_info->adv_neighbor_reachable_time_in_msec = (is_no) ? 0 : reachable_time;
+ }
+
+ if (retransmit_interval != ~0)
+ {
+ radv_info->adv_time_in_msec_between_retransmitted_neighbor_solicitations =
+ (is_no) ? 0 : retransmit_interval;
+ }
+
/* restart */
if ((cease != 0) && (is_no))
radv_info->send_radv = 1;
@@ -1803,6 +1854,60 @@ restart:
return (0);
}
+int
+ip6_ra_dns_server (vlib_main_t *vm, u32 sw_if_index, ip6_address_t *dns_server_addr, u32 lifetime,
+ u8 is_no)
+{
+ ip6_ra_t *radv_info;
+
+ radv_info = ip6_ra_get_itf (sw_if_index);
+
+ if (!radv_info)
+ return (VNET_API_ERROR_IP6_NOT_ENABLED);
+
+ if (is_no)
+ {
+ /* delete */
+ int i;
+ vec_foreach_index (i, radv_info->adv_dns_servers)
+ {
+ if (ip6_address_is_equal (&radv_info->adv_dns_servers[i], dns_server_addr))
+ {
+ vec_del1 (radv_info->adv_dns_servers, i);
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* add (check duplication) */
+ int i, found = 0;
+ vec_foreach_index (i, radv_info->adv_dns_servers)
+ {
+ if (ip6_address_is_equal (&radv_info->adv_dns_servers[i], dns_server_addr))
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ vec_add1 (radv_info->adv_dns_servers, *dns_server_addr);
+
+ /* set lifetime (if specified) */
+ if (lifetime != ~0)
+ radv_info->adv_dns_server_lifetime = lifetime;
+ }
+
+ /* restart */
+ radv_info->initial_adverts_sent = radv_info->initial_adverts_count - 1;
+ radv_info->next_multicast_time = vlib_time_now (vm);
+ radv_info->last_multicast_time = vlib_time_now (vm);
+ radv_info->last_radv_time = 0;
+
+ return (0);
+}
+
clib_error_t *
ip6_ra_cmd (vlib_main_t * vm,
unformat_input_t * main_input, vlib_cli_command_t * cmd)
@@ -1816,10 +1921,14 @@ ip6_ra_cmd (vlib_main_t * vm,
u32 sw_if_index, ra_lifetime = 0, ra_initial_count =
0, ra_initial_interval = 0;
u32 ra_max_interval = 0, ra_min_interval = 0;
+ u32 ra_reachable_time = ~0;
+ u32 ra_retransmit_interval = ~0;
unformat_input_t _line_input, *line_input = &_line_input;
int add_radv_info = 1;
+ int add_dns_server = 0;
+ u32 dns_server_lifetime = ~0;
ip6_address_t ip6_addr;
u32 addr_len;
@@ -1867,6 +1976,11 @@ ip6_ra_cmd (vlib_main_t * vm,
{
add_radv_info = 0;
}
+ else if (unformat (line_input, "dns server %U", unformat_ip6_address, &ip6_addr))
+ {
+ add_radv_info = 0;
+ add_dns_server = 1;
+ }
else if (unformat (line_input, "ra-managed-config-flag"))
{
managed = 1;
@@ -1918,6 +2032,22 @@ ip6_ra_cmd (vlib_main_t * vm,
if (!unformat (line_input, "%d", &ra_min_interval))
ra_min_interval = 0;
}
+ else if (unformat (line_input, "ra-reachable-time"))
+ {
+ if (!unformat (line_input, "%d", &ra_reachable_time))
+ {
+ error = unformat_parse_error (line_input);
+ goto done;
+ }
+ }
+ else if (unformat (line_input, "ra-retransmit-interval"))
+ {
+ if (!unformat (line_input, "%d", &ra_retransmit_interval))
+ {
+ error = unformat_parse_error (line_input);
+ goto done;
+ }
+ }
else if (unformat (line_input, "ra-cease"))
{
cease = 1;
@@ -1930,12 +2060,25 @@ ip6_ra_cmd (vlib_main_t * vm,
if (add_radv_info)
{
- ip6_ra_config (vm, sw_if_index,
- suppress, managed, other,
- suppress_ll_option, send_unicast, cease,
- use_lifetime, ra_lifetime,
- ra_initial_count, ra_initial_interval,
- ra_max_interval, ra_min_interval, is_no);
+ ip6_ra_config (vm, sw_if_index, suppress, managed, other, suppress_ll_option, send_unicast,
+ cease, use_lifetime, ra_lifetime, ra_initial_count, ra_initial_interval,
+ ra_max_interval, ra_min_interval, ra_reachable_time, ra_retransmit_interval,
+ is_no);
+ }
+ else if (add_dns_server)
+ {
+ /* get the rest of the command */
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "lifetime %d", &dns_server_lifetime))
+ ;
+ else
+ {
+ error = unformat_parse_error (line_input);
+ goto done;
+ }
+ }
+ ip6_ra_dns_server (vm, sw_if_index, &ip6_addr, dns_server_lifetime, is_no);
}
else
{
@@ -2023,6 +2166,19 @@ format_ip6_ra (u8 * s, va_list * args)
format_ip6_address, &p->prefix, p->prefix_len);
}
+ if (vec_len (radv_info->adv_dns_servers) > 0)
+ {
+ s = format (s, "%UAdvertised DNS Servers:\n", format_white_space, indent);
+ int i;
+ vec_foreach_index (i, radv_info->adv_dns_servers)
+ {
+ s = format (s, "%U%U\n", format_white_space, indent + 2, format_ip6_address,
+ &radv_info->adv_dns_servers[i]);
+ }
+ s = format (s, "%UDNS Server Lifetime is %d\n", format_white_space, indent + 2,
+ radv_info->adv_dns_server_lifetime);
+ }
+
s = format (s, "%UMTU is %d\n",
format_white_space, indent, radv_info->adv_link_mtu);
s = format (s, "%UICMP error messages are unlimited\n",
@@ -2082,7 +2238,9 @@ format_ip6_ra (u8 * s, va_list * args)
* [ra-other-config-flag] | [ra-suppress] | [ra-suppress-link-layer] |
* [ra-send-unicast] | [ra-lifetime <lifetime>] |
* [ra-initial <cnt> <interval>] |
- * [ra-interval <max-interval> [<min-interval>]] | [ra-cease]
+ * [ra-interval <max-interval> [<min-interval>]] |
+ * [ra-reachable-time <reachable-time>] |
+ * [ra-retransmit-interval <interval>] | [ra-cease]
* @cliend
*
* Where:
@@ -2133,6 +2291,10 @@ format_ip6_ra (u8 * s, va_list * args)
* 150 seconds. The '<em>no</em>' option returns both to their default
* value.
*
+ * <em>[no] ra-reachable-time <reachable-time></em> - Configures AdvReachableTime
+ *
+ * <em>[no] ra-retransmit-interval <interval></em> - Configures AdvRetransTimer
+ *
* <em>[no] ra-cease</em> - Cease sending ICMPv6 router-advertisement messages.
* The '<em>no</em>' options implies to start (or restart) sending
* ICMPv6 router-advertisement messages.
@@ -2196,6 +2358,20 @@ format_ip6_ra (u8 * s, va_list * args)
* infinite no-advertise}
* Example of how to delete a prefix:
* @cliexcmd{ip6 nd GigabitEthernet2/0/0 no prefix fe80::fe:28ff:fe9c:75b3/64}
+ *
+ *
+ * <b>Format 4 - DNS Option:</b>
+ *
+ * @clistart
+ * ip6 nd <interface> [no] dns [server <ip6-address> [lifetime <seconds>]]
+ * [search <domain>]
+ * @cliend
+ *
+ * Where:
+ *
+ * <em>no</em> - Removes the specified DNS server address from the advertisement.
+ *
+ * <em>lifetime</em> - The maximum time in seconds that the DNS server address may be used.
?*/
VLIB_CLI_COMMAND (ip6_nd_command, static) =
{
diff --git a/src/vnet/ip6-nd/ip6_ra.h b/src/vnet/ip6-nd/ip6_ra.h
index 61247bde4..8d188c85b 100644
--- a/src/vnet/ip6-nd/ip6_ra.h
+++ b/src/vnet/ip6-nd/ip6_ra.h
@@ -57,6 +57,10 @@ typedef struct ip6_ra_t_
/* mtu option */
u32 adv_link_mtu;
+ /* rdnss option */
+ ip6_address_t *adv_dns_servers;
+ u32 adv_dns_server_lifetime;
+
/* local information */
u32 sw_if_index;
int send_radv; /* radv on/off on this interface - set by config */
@@ -108,12 +112,10 @@ typedef struct ip6_ra_t_
extern ip6_ra_t *ip6_ra_get_itf (u32 sw_if_index);
-extern int ip6_ra_config (vlib_main_t * vm, u32 sw_if_index,
- u8 suppress, u8 managed, u8 other,
- u8 ll_option, u8 send_unicast, u8 cease,
- u8 use_lifetime, u32 lifetime,
- u32 initial_count, u32 initial_interval,
- u32 max_interval, u32 min_interval, u8 is_no);
+extern int ip6_ra_config (vlib_main_t *vm, u32 sw_if_index, u8 suppress, u8 managed, u8 other,
+ u8 ll_option, u8 send_unicast, u8 cease, u8 use_lifetime, u32 lifetime,
+ u32 initial_count, u32 initial_interval, u32 max_interval,
+ u32 min_interval, u32 reachable_time, u32 retransmit_interval, u8 is_no);
extern int ip6_ra_prefix (vlib_main_t * vm, u32 sw_if_index,
ip6_address_t * prefix_addr, u8 prefix_len,
@@ -122,6 +124,9 @@ extern int ip6_ra_prefix (vlib_main_t * vm, u32 sw_if_index,
u8 off_link, u8 no_autoconfig,
u8 no_onlink, u8 is_no);
+extern int ip6_ra_dns_server (vlib_main_t *vm, u32 sw_if_index, ip6_address_t *dns_server_addr,
+ u32 lifetime, u8 is_no);
+
typedef walk_rc_t (*ip6_ra_itf_walk_fn_t) (u32 sw_if_index, void *ctx);
extern void ip6_ra_itf_walk (ip6_ra_itf_walk_fn_t fn, void *ctx);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment