dhcp6_pd_client_dp.c revision 20b962d3
1/*
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:
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
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.
14 */
15
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_pd_client_dp.h>
25#include <dhcp/dhcp6_client_common_dp.h>
26#include <vnet/ip/ip_types_api.h>
27
28dhcp6_pd_client_main_t dhcp6_pd_client_main;
29dhcp6_pd_client_public_main_t dhcp6_pd_client_public_main;
30
31static void
32signal_report (prefix_report_t * r)
33{
34  vlib_main_t *vm = vlib_get_main ();
35  dhcp6_pd_client_main_t *cm = &dhcp6_pd_client_main;
36  uword ni = cm->publisher_node;
37  uword et = cm->publisher_et;
38
39  if (ni == (uword) ~ 0)
40    return;
41  prefix_report_t *q =
42    vlib_process_signal_event_data (vm, ni, et, 1, sizeof *q);
43
44  *q = *r;
45}
46
47int
48dhcp6_pd_publish_report (prefix_report_t * r)
49{
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);
52  return 0;
53}
54
55void
56dhcp6_pd_set_publisher_node (uword node_index, uword event_type)
57{
58  dhcp6_pd_client_main_t *cm = &dhcp6_pd_client_main;
59  cm->publisher_node = node_index;
60  cm->publisher_et = event_type;
61}
62
63static void
64stop_sending_client_message (vlib_main_t * vm,
65			     dhcp6_pd_client_state_t * client_state)
66{
67  u32 bi0;
68
69  client_state->keep_sending_client_message = 0;
70  vec_free (client_state->params.prefixes);
71  if (client_state->buffer)
72    {
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;
78    }
79}
80
81static vlib_buffer_t *
82create_buffer_for_client_message (vlib_main_t * vm,
83				  u32 sw_if_index,
84				  dhcp6_pd_client_state_t
85				  * client_state, u32 type)
86{
87  dhcp6_client_common_main_t *ccm = &dhcp6_client_common_main;
88  vlib_buffer_t *b;
89  u32 bi;
90  ip6_header_t *ip;
91  udp_header_t *udp;
92  dhcpv6_header_t *dhcp;
93  ip6_address_t src_addr;
94  u32 dhcp_opt_len = 0;
95  client_state->transaction_start = vlib_time_now (vm);
96  u32 n_prefixes;
97  u32 i;
98
99  /*
100   * Note: do NOT psychoanalyze link-state here.
101   * If the interface is down, let the driver turf the packet.
102   */
103
104  /* Get a link-local address */
105  src_addr = ip6_neighbor_get_link_local_address (sw_if_index);
106
107  if (src_addr.as_u8[0] != 0xfe)
108    {
109      clib_warning ("Could not find source address to send DHCPv6 packet");
110      return NULL;
111    }
112
113  if (vlib_buffer_alloc (vm, &bi, 1) != 1)
114    {
115      clib_warning ("Buffer allocation failed");
116      return NULL;
117    }
118
119  b = vlib_get_buffer (vm, bi);
120  vnet_buffer (b)->sw_if_index[VLIB_RX] = sw_if_index;
121  vnet_buffer (b)->sw_if_index[VLIB_TX] = sw_if_index;
122  client_state->adj_index = adj_mcast_add_or_lock (FIB_PROTOCOL_IP6,
123						   VNET_LINK_IP6,
124						   sw_if_index);
125  vnet_buffer (b)->ip.adj_index[VLIB_TX] = client_state->adj_index;
126  b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
127
128  ip = (ip6_header_t *) vlib_buffer_get_current (b);
129  udp = (udp_header_t *) (ip + 1);
130  dhcp = (dhcpv6_header_t *) (udp + 1);
131
132  ip->src_address = src_addr;
133  ip->hop_limit = 255;
134  ip->ip_version_traffic_class_and_flow_label =
135    clib_host_to_net_u32 (0x6 << 28);
136  ip->payload_length = 0;
137  ip->protocol = IP_PROTOCOL_UDP;
138
139  udp->src_port = clib_host_to_net_u16 (DHCPV6_CLIENT_PORT);
140  udp->dst_port = clib_host_to_net_u16 (DHCPV6_SERVER_PORT);
141  udp->checksum = 0;
142  udp->length = 0;
143
144  dhcp->msg_type = type;
145  dhcp->xid[0] = (client_state->transaction_id & 0x00ff0000) >> 16;
146  dhcp->xid[1] = (client_state->transaction_id & 0x0000ff00) >> 8;
147  dhcp->xid[2] = (client_state->transaction_id & 0x000000ff) >> 0;
148
149  void *d = (void *) dhcp->data;
150  dhcpv6_option_t *duid;
151  dhcpv6_elapsed_t *elapsed;
152  dhcpv6_ia_header_t *ia_hdr;
153  dhcpv6_ia_opt_pd_t *opt_pd;
154  if (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST ||
155      type == DHCPV6_MSG_RENEW || type == DHCPV6_MSG_REBIND ||
156      type == DHCPV6_MSG_RELEASE)
157    {
158      duid = (dhcpv6_option_t *) d;
159      duid->option = clib_host_to_net_u16 (DHCPV6_OPTION_CLIENTID);
160      duid->length = clib_host_to_net_u16 (CLIENT_DUID_LENGTH);
161      clib_memcpy (duid + 1, client_duid.bin_string, CLIENT_DUID_LENGTH);
162      d += sizeof (*duid) + CLIENT_DUID_LENGTH;
163
164      if (client_state->params.server_index != ~0)
165	{
166	  server_id_t *se =
167	    &ccm->server_ids[client_state->params.server_index];
168
169	  duid = (dhcpv6_option_t *) d;
170	  duid->option = clib_host_to_net_u16 (DHCPV6_OPTION_SERVERID);
171	  duid->length = clib_host_to_net_u16 (se->len);
172	  clib_memcpy (duid + 1, se->data, se->len);
173	  d += sizeof (*duid) + se->len;
174	}
175
176      elapsed = (dhcpv6_elapsed_t *) d;
177      elapsed->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_ELAPSED_TIME);
178      elapsed->opt.length =
179	clib_host_to_net_u16 (sizeof (*elapsed) - sizeof (elapsed->opt));
180      elapsed->elapsed_10ms = 0;
181      client_state->elapsed_pos =
182	(char *) &elapsed->elapsed_10ms -
183	(char *) vlib_buffer_get_current (b);
184      d += sizeof (*elapsed);
185
186      ia_hdr = (dhcpv6_ia_header_t *) d;
187      ia_hdr->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_IA_PD);
188      ia_hdr->iaid = clib_host_to_net_u32 (DHCPV6_CLIENT_IAID);
189      ia_hdr->t1 = clib_host_to_net_u32 (client_state->params.T1);
190      ia_hdr->t2 = clib_host_to_net_u32 (client_state->params.T2);
191      d += sizeof (*ia_hdr);
192
193      n_prefixes = vec_len (client_state->params.prefixes);
194
195      ia_hdr->opt.length =
196	clib_host_to_net_u16 (sizeof (*ia_hdr) +
197			      n_prefixes * sizeof (*opt_pd) -
198			      sizeof (ia_hdr->opt));
199
200      for (i = 0; i < n_prefixes; i++)
201	{
202	  dhcp6_pd_send_client_message_params_prefix_t *pref =
203	    &client_state->params.prefixes[i];
204	  opt_pd = (dhcpv6_ia_opt_pd_t *) d;
205	  opt_pd->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_IAPREFIX);
206	  opt_pd->opt.length =
207	    clib_host_to_net_u16 (sizeof (*opt_pd) - sizeof (opt_pd->opt));
208	  opt_pd->addr = pref->prefix;
209	  opt_pd->prefix = pref->prefix_length;
210	  opt_pd->valid = clib_host_to_net_u32 (pref->valid_lt);
211	  opt_pd->preferred = clib_host_to_net_u32 (pref->preferred_lt);
212	  d += sizeof (*opt_pd);
213	}
214    }
215  else
216    {
217      clib_warning ("State not implemented");
218    }
219
220  dhcp_opt_len = ((u8 *) d) - dhcp->data;
221  udp->length =
222    clib_host_to_net_u16 (sizeof (*udp) + sizeof (*dhcp) + dhcp_opt_len);
223  ip->payload_length = udp->length;
224  b->current_length =
225    sizeof (*ip) + sizeof (*udp) + sizeof (*dhcp) + dhcp_opt_len;
226
227  ip->dst_address = all_dhcp6_relay_agents_and_servers;
228
229  return b;
230}
231
232static inline u8
233check_pd_send_client_message (vlib_main_t * vm,
234			      dhcp6_pd_client_state_t * client_state,
235			      f64 current_time, f64 * due_time)
236{
237  vlib_buffer_t *p0;
238  vlib_frame_t *f;
239  u32 *to_next;
240  u32 next_index;
241  vlib_buffer_t *c0;
242  ip6_header_t *ip;
243  udp_header_t *udp;
244  u32 ci0;
245  int bogus_length = 0;
246
247  dhcp6_pd_send_client_message_params_t *params;
248
249  f64 now = vlib_time_now (vm);
250
251  if (!client_state->keep_sending_client_message)
252    return false;
253
254  params = &client_state->params;
255
256  if (client_state->due_time > current_time)
257    {
258      *due_time = client_state->due_time;
259      return true;
260    }
261
262  p0 = client_state->buffer;
263
264  next_index = ip6_rewrite_mcast_node.index;
265
266  c0 = vlib_buffer_copy (vm, p0);
267  ci0 = vlib_get_buffer_index (vm, c0);
268
269  ip = (ip6_header_t *) vlib_buffer_get_current (c0);
270  udp = (udp_header_t *) (ip + 1);
271
272  u16 *elapsed_field = (u16 *) ((void *) ip + client_state->elapsed_pos);
273  *elapsed_field =
274    clib_host_to_net_u16 ((u16)
275			  ((now - client_state->transaction_start) * 100));
276
277  udp->checksum = 0;
278  udp->checksum =
279    ip6_tcp_udp_icmp_compute_checksum (vm, 0, ip, &bogus_length);
280
281  f = vlib_get_frame_to_node (vm, next_index);
282  to_next = vlib_frame_vector_args (f);
283  to_next[0] = ci0;
284  f->n_vectors = 1;
285  vlib_put_frame_to_node (vm, next_index, f);
286
287  if (params->mrc != 0 && --client_state->n_left == 0)
288    stop_sending_client_message (vm, client_state);
289  else
290    {
291      client_state->sleep_interval =
292	(2.0 + random_f64_from_to (-0.1, 0.1)) * client_state->sleep_interval;
293      if (client_state->sleep_interval > params->mrt)
294	client_state->sleep_interval =
295	  (1.0 + random_f64_from_to (-0.1, 0.1)) * params->mrt;
296
297      client_state->due_time = current_time + client_state->sleep_interval;
298
299      if (params->mrd != 0
300	  && current_time > client_state->start_time + params->mrd)
301	stop_sending_client_message (vm, client_state);
302      else
303	*due_time = client_state->due_time;
304    }
305
306  return client_state->keep_sending_client_message;
307}
308
309static uword
310send_dhcp6_pd_client_message_process (vlib_main_t * vm,
311				      vlib_node_runtime_t * rt,
312				      vlib_frame_t * f0)
313{
314  dhcp6_pd_client_main_t *cm = &dhcp6_pd_client_main;
315  dhcp6_pd_client_state_t *client_state;
316  uword *event_data = 0;
317  f64 sleep_time = 1e9;
318  f64 current_time;
319  f64 due_time;
320  f64 dt = 0;
321  int i;
322
323  while (true)
324    {
325      vlib_process_wait_for_event_or_clock (vm, sleep_time);
326      vlib_process_get_events (vm, &event_data);
327      vec_reset_length (event_data);
328
329      current_time = vlib_time_now (vm);
330      do
331	{
332	  due_time = current_time + 1e9;
333	  for (i = 0; i < vec_len (cm->client_state_by_sw_if_index); i++)
334	    {
335	      client_state = &cm->client_state_by_sw_if_index[i];
336	      if (!client_state->entry_valid)
337		continue;
338	      if (check_pd_send_client_message
339		  (vm, client_state, current_time, &dt) && (dt < due_time))
340		due_time = dt;
341	    }
342	  current_time = vlib_time_now (vm);
343	}
344      while (due_time < current_time);
345
346      sleep_time = due_time - current_time;
347    }
348
349  return 0;
350}
351
352/* *INDENT-OFF* */
353VLIB_REGISTER_NODE (send_dhcp6_pd_client_message_process_node, static) = {
354    .function = send_dhcp6_pd_client_message_process,
355    .type = VLIB_NODE_TYPE_PROCESS,
356    .name = "send-dhcp6-pd-client-message-process",
357};
358/* *INDENT-ON* */
359
360void
361dhcp6_pd_send_client_message (vlib_main_t * vm, u32 sw_if_index, u8 stop,
362			      dhcp6_pd_send_client_message_params_t * params)
363{
364  dhcp6_pd_client_main_t *cm = &dhcp6_pd_client_main;
365  dhcp6_pd_client_state_t *client_state = 0;
366  dhcp6_pd_client_state_t empty_state = {
367    0,
368  };
369
370  ASSERT (~0 != sw_if_index);
371
372  vec_validate_init_empty (cm->client_state_by_sw_if_index, sw_if_index,
373			   empty_state);
374  client_state = &cm->client_state_by_sw_if_index[sw_if_index];
375  if (!client_state->entry_valid)
376    {
377      client_state->entry_valid = 1;
378      client_state->adj_index = ~0;
379    }
380
381  stop_sending_client_message (vm, client_state);
382
383  if (!stop)
384    {
385      client_state->keep_sending_client_message = 1;
386      vec_free (client_state->params.prefixes);
387      client_state->params = *params;
388      client_state->params.prefixes = vec_dup (params->prefixes);
389      client_state->n_left = params->mrc;
390      client_state->start_time = vlib_time_now (vm);
391      client_state->sleep_interval =
392	(1 + random_f64_from_to (-0.1, 0.1)) * params->irt;
393      client_state->due_time = 0;	/* send first packet ASAP */
394      client_state->transaction_id = random_u32 (&cm->seed) & 0x00ffffff;
395      client_state->buffer =
396	create_buffer_for_client_message (vm, sw_if_index, client_state,
397					  params->msg_type);
398      if (client_state->buffer)
399	vlib_process_signal_event
400	  (vm, send_dhcp6_pd_client_message_process_node.index, 1, 0);
401    }
402}
403
404static clib_error_t *
405dhcp6_pd_client_init (vlib_main_t * vm)
406{
407  dhcp6_pd_client_main_t *cm = &dhcp6_pd_client_main;
408
409  cm->vlib_main = vm;
410  cm->vnet_main = vnet_get_main ();
411  cm->publisher_node = ~0;
412  cm->seed = (u32) clib_cpu_time_now ();
413
414  return 0;
415}
416
417VLIB_INIT_FUNCTION (dhcp6_pd_client_init);
418
419/*
420 * fd.io coding-style-patch-verification: ON
421 *
422 * Local Variables:
423 * eval: (c-set-style "gnu")
424 * End:
425 */
426