2 * Copyright (c) 2018 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 #include <vlib/vlib.h>
17 #include <dhcp/dhcp6_packet.h>
18 #include <dhcp/dhcp_proxy.h>
19 #include <vnet/mfib/mfib_table.h>
20 #include <vnet/mfib/ip6_mfib.h>
21 #include <vnet/fib/fib.h>
22 #include <vnet/adj/adj_mcast.h>
23 #include <vnet/ip/ip6_neighbor.h>
24 #include <dhcp/dhcp6_ia_na_client_dp.h>
25 #include <dhcp/dhcp6_client_common_dp.h>
26 #include <vnet/ip/ip_types_api.h>
28 dhcp6_ia_na_client_main_t dhcp6_ia_na_client_main;
29 dhcp6_ia_na_client_public_main_t dhcp6_ia_na_client_public_main;
32 signal_report (address_report_t * r)
34 vlib_main_t *vm = vlib_get_main ();
35 dhcp6_ia_na_client_main_t *cm = &dhcp6_ia_na_client_main;
36 uword ni = cm->publisher_node;
37 uword et = cm->publisher_et;
39 if (ni == (uword) ~ 0)
42 vlib_process_signal_event_data (vm, ni, et, 1, sizeof *q);
48 dhcp6_publish_report (address_report_t * r)
50 void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
51 vl_api_rpc_call_main_thread (signal_report, (u8 *) r, sizeof *r);
56 dhcp6_set_publisher_node (uword node_index, uword event_type)
58 dhcp6_ia_na_client_main_t *cm = &dhcp6_ia_na_client_main;
59 cm->publisher_node = node_index;
60 cm->publisher_et = event_type;
64 stop_sending_client_message (vlib_main_t * vm,
65 dhcp6_ia_na_client_state_t * client_state)
69 client_state->keep_sending_client_message = 0;
70 vec_free (client_state->params.addresses);
71 if (client_state->buffer)
73 bi0 = vlib_get_buffer_index (vm, client_state->buffer);
74 vlib_buffer_free (vm, &bi0, 1);
75 client_state->buffer = 0;
76 adj_unlock (client_state->adj_index);
77 client_state->adj_index = ~0;
81 static vlib_buffer_t *
82 create_buffer_for_client_message (vlib_main_t * vm, u32 sw_if_index,
83 dhcp6_ia_na_client_state_t * client_state,
86 dhcp6_client_common_main_t *ccm = &dhcp6_client_common_main;
87 vnet_main_t *vnm = vnet_get_main ();
93 dhcpv6_header_t *dhcp;
94 ip6_address_t src_addr;
96 client_state->transaction_start = vlib_time_now (vm);
100 vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
101 vnet_sw_interface_t *sup_sw = vnet_get_sup_sw_interface (vnm, sw_if_index);
102 vnet_sw_interface_t *sw = vnet_get_sw_interface (vnm, sw_if_index);
104 /* Interface(s) down? */
105 if ((hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
107 if ((sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
109 if ((sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0)
112 /* Get a link-local address */
113 src_addr = ip6_neighbor_get_link_local_address (sw_if_index);
115 if (src_addr.as_u8[0] != 0xfe)
117 clib_warning ("Could not find source address to send DHCPv6 packet");
121 if (vlib_buffer_alloc (vm, &bi, 1) != 1)
123 clib_warning ("Buffer allocation failed");
127 b = vlib_get_buffer (vm, bi);
128 vnet_buffer (b)->sw_if_index[VLIB_RX] = sw_if_index;
129 vnet_buffer (b)->sw_if_index[VLIB_TX] = sw_if_index;
130 client_state->adj_index = adj_mcast_add_or_lock (FIB_PROTOCOL_IP6,
133 vnet_buffer (b)->ip.adj_index[VLIB_TX] = client_state->adj_index;
134 b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
136 ip = (ip6_header_t *) vlib_buffer_get_current (b);
137 udp = (udp_header_t *) (ip + 1);
138 dhcp = (dhcpv6_header_t *) (udp + 1);
140 ip->src_address = src_addr;
142 ip->ip_version_traffic_class_and_flow_label =
143 clib_host_to_net_u32 (0x6 << 28);
144 ip->payload_length = 0;
145 ip->protocol = IP_PROTOCOL_UDP;
147 udp->src_port = clib_host_to_net_u16 (DHCPV6_CLIENT_PORT);
148 udp->dst_port = clib_host_to_net_u16 (DHCPV6_SERVER_PORT);
152 dhcp->msg_type = type;
153 dhcp->xid[0] = (client_state->transaction_id & 0x00ff0000) >> 16;
154 dhcp->xid[1] = (client_state->transaction_id & 0x0000ff00) >> 8;
155 dhcp->xid[2] = (client_state->transaction_id & 0x000000ff) >> 0;
157 void *d = (void *) dhcp->data;
158 dhcpv6_option_t *duid;
159 dhcpv6_elapsed_t *elapsed;
160 dhcpv6_ia_header_t *ia_hdr;
161 dhcpv6_ia_opt_addr_t *opt_addr;
162 if (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST ||
163 type == DHCPV6_MSG_RENEW || type == DHCPV6_MSG_REBIND ||
164 type == DHCPV6_MSG_RELEASE)
166 duid = (dhcpv6_option_t *) d;
167 duid->option = clib_host_to_net_u16 (DHCPV6_OPTION_CLIENTID);
168 duid->length = clib_host_to_net_u16 (CLIENT_DUID_LENGTH);
169 clib_memcpy (duid + 1, client_duid.bin_string, CLIENT_DUID_LENGTH);
170 d += sizeof (*duid) + CLIENT_DUID_LENGTH;
172 if (client_state->params.server_index != ~0)
175 &ccm->server_ids[client_state->params.server_index];
177 duid = (dhcpv6_option_t *) d;
178 duid->option = clib_host_to_net_u16 (DHCPV6_OPTION_SERVERID);
179 duid->length = clib_host_to_net_u16 (se->len);
180 clib_memcpy (duid + 1, se->data, se->len);
181 d += sizeof (*duid) + se->len;
184 elapsed = (dhcpv6_elapsed_t *) d;
185 elapsed->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_ELAPSED_TIME);
186 elapsed->opt.length =
187 clib_host_to_net_u16 (sizeof (*elapsed) - sizeof (elapsed->opt));
188 elapsed->elapsed_10ms = 0;
189 client_state->elapsed_pos =
190 (char *) &elapsed->elapsed_10ms -
191 (char *) vlib_buffer_get_current (b);
192 d += sizeof (*elapsed);
194 ia_hdr = (dhcpv6_ia_header_t *) d;
195 ia_hdr->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_IA_NA);
196 ia_hdr->iaid = clib_host_to_net_u32 (DHCPV6_CLIENT_IAID);
197 ia_hdr->t1 = clib_host_to_net_u32 (client_state->params.T1);
198 ia_hdr->t2 = clib_host_to_net_u32 (client_state->params.T2);
199 d += sizeof (*ia_hdr);
201 n_addresses = vec_len (client_state->params.addresses);
204 clib_host_to_net_u16 (sizeof (*ia_hdr) +
205 n_addresses * sizeof (*opt_addr) -
206 sizeof (ia_hdr->opt));
208 for (i = 0; i < n_addresses; i++)
210 dhcp6_send_client_message_params_address_t *addr =
211 &client_state->params.addresses[i];
212 opt_addr = (dhcpv6_ia_opt_addr_t *) d;
213 opt_addr->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_IAADDR);
214 opt_addr->opt.length =
215 clib_host_to_net_u16 (sizeof (*opt_addr) -
216 sizeof (opt_addr->opt));
217 opt_addr->addr = addr->address;
218 opt_addr->valid = clib_host_to_net_u32 (addr->valid_lt);
219 opt_addr->preferred = clib_host_to_net_u32 (addr->preferred_lt);
220 d += sizeof (*opt_addr);
225 clib_warning ("State not implemented");
228 dhcp_opt_len = ((u8 *) d) - dhcp->data;
230 clib_host_to_net_u16 (sizeof (*udp) + sizeof (*dhcp) + dhcp_opt_len);
231 ip->payload_length = udp->length;
233 sizeof (*ip) + sizeof (*udp) + sizeof (*dhcp) + dhcp_opt_len;
235 ip->dst_address = all_dhcp6_relay_agents_and_servers;
241 check_send_client_message (vlib_main_t * vm,
242 dhcp6_ia_na_client_state_t * client_state,
243 f64 current_time, f64 * due_time)
253 int bogus_length = 0;
255 dhcp6_send_client_message_params_t *params;
257 f64 now = vlib_time_now (vm);
259 if (!client_state->keep_sending_client_message)
262 params = &client_state->params;
264 if (client_state->due_time > current_time)
266 *due_time = client_state->due_time;
270 p0 = client_state->buffer;
272 next_index = ip6_rewrite_mcast_node.index;
274 c0 = vlib_buffer_copy (vm, p0);
275 ci0 = vlib_get_buffer_index (vm, c0);
277 ip = (ip6_header_t *) vlib_buffer_get_current (c0);
278 udp = (udp_header_t *) (ip + 1);
280 u16 *elapsed_field = (u16 *) ((void *) ip + client_state->elapsed_pos);
282 clib_host_to_net_u16 ((u16)
283 ((now - client_state->transaction_start) * 100));
287 ip6_tcp_udp_icmp_compute_checksum (vm, 0, ip, &bogus_length);
289 f = vlib_get_frame_to_node (vm, next_index);
290 to_next = vlib_frame_vector_args (f);
293 vlib_put_frame_to_node (vm, next_index, f);
295 if (params->mrc != 0 && --client_state->n_left == 0)
296 stop_sending_client_message (vm, client_state);
299 client_state->sleep_interval =
300 (2 + random_f64_from_to (-0.1, 0.1)) * client_state->sleep_interval;
301 if (client_state->sleep_interval > params->mrt)
302 client_state->sleep_interval =
303 (1 + random_f64_from_to (-0.1, 0.1)) * params->mrt;
305 client_state->due_time = current_time + client_state->sleep_interval;
308 && current_time > client_state->start_time + params->mrd)
309 stop_sending_client_message (vm, client_state);
311 *due_time = client_state->due_time;
314 return client_state->keep_sending_client_message;
318 send_dhcp6_client_message_process (vlib_main_t * vm,
319 vlib_node_runtime_t * rt,
322 dhcp6_ia_na_client_main_t *cm = &dhcp6_ia_na_client_main;
323 dhcp6_ia_na_client_state_t *client_state;
324 uword *event_data = 0;
325 f64 sleep_time = 1e9;
333 vlib_process_wait_for_event_or_clock (vm, sleep_time);
334 vlib_process_get_events (vm, &event_data);
335 vec_reset_length (event_data);
337 current_time = vlib_time_now (vm);
340 due_time = current_time + 1e9;
341 for (i = 0; i < vec_len (cm->client_state_by_sw_if_index); i++)
343 client_state = &cm->client_state_by_sw_if_index[i];
344 if (!client_state->entry_valid)
346 if (check_send_client_message
347 (vm, client_state, current_time, &dt) && (dt < due_time))
350 current_time = vlib_time_now (vm);
352 while (due_time < current_time);
354 sleep_time = due_time - current_time;
361 VLIB_REGISTER_NODE (send_dhcp6_client_message_process_node, static) = {
362 .function = send_dhcp6_client_message_process,
363 .type = VLIB_NODE_TYPE_PROCESS,
364 .name = "send-dhcp6-client-message-process",
369 dhcp6_send_client_message (vlib_main_t * vm, u32 sw_if_index, u8 stop,
370 dhcp6_send_client_message_params_t * params)
372 dhcp6_ia_na_client_main_t *cm = &dhcp6_ia_na_client_main;
373 dhcp6_ia_na_client_state_t *client_state = 0;
374 dhcp6_ia_na_client_state_t empty_state = { 0, };
376 ASSERT (~0 != sw_if_index);
378 vec_validate_init_empty (cm->client_state_by_sw_if_index, sw_if_index,
380 client_state = &cm->client_state_by_sw_if_index[sw_if_index];
381 if (!client_state->entry_valid)
383 client_state->entry_valid = 1;
384 client_state->adj_index = ~0;
387 stop_sending_client_message (vm, client_state);
391 client_state->keep_sending_client_message = 1;
392 vec_free (client_state->params.addresses);
393 client_state->params = *params;
394 client_state->params.addresses = vec_dup (params->addresses);
395 client_state->n_left = params->mrc;
396 client_state->start_time = vlib_time_now (vm);
397 client_state->sleep_interval =
398 (1 + random_f64_from_to (-0.1, 0.1)) * params->irt;
399 client_state->due_time = 0; /* send first packet ASAP */
400 client_state->transaction_id = random_u32 (&cm->seed) & 0x00ffffff;
401 client_state->buffer =
402 create_buffer_for_client_message (vm, sw_if_index, client_state,
404 if (!client_state->buffer)
405 client_state->keep_sending_client_message = 0;
407 vlib_process_signal_event (vm,
408 send_dhcp6_client_message_process_node.index,
413 static clib_error_t *
414 dhcp6_client_init (vlib_main_t * vm)
416 dhcp6_ia_na_client_main_t *cm = &dhcp6_ia_na_client_main;
419 cm->vnet_main = vnet_get_main ();
421 cm->publisher_node = ~0;
423 cm->seed = 0xdeaccabe;
428 VLIB_INIT_FUNCTION (dhcp6_client_init);
431 * fd.io coding-style-patch-verification: ON
434 * eval: (c-set-style "gnu")