#include #include #include #include #include #include #include #include typedef struct { /** Handle returned from tw_start_timer */ u32 stop_timer_handle; /** Test item should expire at this clock tick */ u64 expected_to_expire; } tw_timer_test_elt_t; typedef struct { /** Pool of test objects */ tw_timer_test_elt_t *test_elts; /** The single-wheel */ tw_timer_wheel_2t_1w_2048sl_t single_wheel; /** The double-wheel */ tw_timer_wheel_16t_2w_512sl_t double_wheel; /* The triple wheel */ tw_timer_wheel_4t_3w_256sl_t triple_wheel; /* The triple wheel with overflow vector */ tw_timer_wheel_1t_3w_1024sl_ov_t triple_ov_wheel; /* Another two timer wheel geometry */ tw_timer_wheel_2t_2w_512sl_t two_timer_double_wheel; /** random number seed */ u64 seed; /** number of timers */ u32 ntimers; /** number of "churn" iterations */ u32 niter; /** number of clock ticks per churn iteration */ u32 ticks_per_iter; /** cpu timer */ clib_time_t clib_time; } tw_timer_test_main_t; tw_timer_test_main_t tw_timer_test_main; static void run_single_wheel (tw_timer_wheel_2t_1w_2048sl_t * tw, u32 n_ticks) { u32 i; f64 now = tw->last_run_time + 1.01; for (i = 0; i < n_ticks; i++) { tw_timer_expire_timers_2t_1w_2048sl (tw, now); now += 1.01; } } static void run_double_wheel (tw_timer_wheel_16t_2w_512sl_t * tw, u32 n_ticks) { u32 i; f64 now = tw->last_run_time + 1.01; for (i = 0; i < n_ticks; i++) { tw_timer_expire_timers_16t_2w_512sl (tw, now); now += 1.01; } } static void run_two_timer_double_wheel (tw_timer_wheel_2t_2w_512sl_t * tw, u32 n_ticks) { u32 i; f64 now = tw->last_run_time + 1.01; for (i = 0; i < n_ticks; i++) { tw_timer_expire_timers_2t_2w_512sl (tw, now); now += 1.01; } } static void run_triple_wheel (tw_timer_wheel_4t_3w_256sl_t * tw, u32 n_ticks) { u32 i; f64 now = tw->last_run_time + 1.01; for (i = 0; i < n_ticks; i++) { tw_timer_expire_timers_4t_3w_256sl (tw, now); now += 1.01; } } static void run_triple_ov_wheel (tw_timer_wheel_1t_3w_1024sl_ov_t * tw, u32 n_ticks) { u32 i; f64 now = tw->last_run_time + 1.01; for (i = 0; i < n_ticks; i++) { tw_timer_expire_timers_1t_3w_1024sl_ov (tw, now); now += 1.01; } } static void expired_timer_single_callback (u32 * expired_timers) { int i; u32 pool_index, timer_id; tw_timer_test_elt_t *e; tw_timer_test_main_t *tm = &tw_timer_test_main; for (i = 0; i < vec_len (expired_timers); i++) { pool_index = expired_timers[i] & 0x7FFFFFFF; timer_id = expired_timers[i] >> 31; ASSERT (timer_id == 1); e = pool_elt_at_index (tm->test_elts, pool_index); if (e->expected_to_expire != tm->single_wheel.current_tick) { fformat (stdout, "[%d] expired at %lld not %lld\n", e - tm->test_elts, tm->single_wheel.current_tick, e->expected_to_expire); } pool_put (tm->test_elts, e); } } static void expired_timer_double_callback (u32 * expired_timers) { int i; u32 pool_index, timer_id; tw_timer_test_elt_t *e; tw_timer_test_main_t *tm = &tw_timer_test_main; for (i = 0; i < vec_len (expired_timers); i++) { pool_index = expired_timers[i] & 0x0FFFFFFF; timer_id = expired_timers[i] >> 28; ASSERT (timer_id == 14); e = pool_elt_at_index (tm->test_elts, pool_index); if (e->expected_to_expire != tm->double_wheel.current_tick) { fformat (stdout, "[%d] expired at %lld not %lld\n", e - tm->test_elts, tm->double_wheel.current_tick, e->expected_to_expire); } pool_put (tm->test_elts, e); } } static void expired_timer_two_timer_double_callback (u32 * expired_timers) { int i; u32 pool_index, timer_id; tw_timer_test_elt_t *e; tw_timer_test_main_t *tm = &tw_timer_test_main; for (i = 0; i < vec_len (expired_timers); i++) { pool_index = expired_timers[i] & 0x7FFFFFFF; timer_id = expired_timers[i] >> 31; ASSERT (timer_id == 1); e = pool_elt_at_index (tm->test_elts, pool_index); if (e->expected_to_expire != tm->two_timer_double_wheel.current_tick) { fformat (stdout, "[%d] expired at %lld not %lld\n", e - tm->test_elts, tm->two_timer_double_wheel.current_tick, e->expected_to_expire); } pool_put (tm->test_elts, e); } } static void expired_timer_triple_callback (u32 * expired_timers) { int i; u32 pool_index, timer_id; tw_timer_test_elt_t *e; tw_timer_test_main_t *tm = &tw_timer_test_main; for (i = 0; i < vec_len (expired_timers); i++) { pool_index = expired_timers[i] & 0x3FFFFFFF; timer_id = expired_timers[i] >> 30; ASSERT (timer_id == 3); e = pool_elt_at_index (tm->test_elts, pool_index); if (e->expected_to_expire != tm->triple_wheel.current_tick) { fformat (stdout, "[%d] expired at %lld not %lld\n", e - tm->test_elts, tm->triple_wheel.current_tick, e->expected_to_expire); } pool_put (tm->test_elts, e); } } static void expired_timer_triple_ov_callback (u32 * expired_timers) { int i; u32 pool_index; tw_timer_test_elt_t *e; tw_timer_test_main_t *tm = &tw_timer_test_main; for (i = 0; i < vec_len (expired_timers); i++) { pool_index = expired_timers[i]; e = pool_elt_at_index (tm->test_elts, pool_index); if (e->expected_to_expire != tm->triple_ov_wheel.current_tick) { fformat (stdout, "[%d] expired at %lld not %lld\n", e - tm->test_elts, tm->triple_ov_wheel.current_tick, e->expected_to_expire); } pool_put (tm->test_elts, e); } } static clib_error_t * test2_single (tw_timer_test_main_t * tm) { u32 i, j; tw_timer_test_elt_t *e; u32 initial_wheel_offset; u64 expiration_time; u32 max_expiration_time = 0; u32 *deleted_indices = 0; u32 adds = 0, deletes = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_2t_1w_2048sl (&tm->single_wheel, expired_timer_single_callback, 1.0 /* timer interval */ , ~0); /* Prime offset */ initial_wheel_offset = 757; run_single_wheel (&tm->single_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fast index %d\n", tm->single_wheel.current_tick, tm->single_wheel.current_index[TW_TIMER_RING_FAST]); initial_wheel_offset = tm->single_wheel.current_tick; fformat (stdout, "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n", tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed); before = clib_time_now (&tm->clib_time); /* Prime the pump */ for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & (2047); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + initial_wheel_offset; e->stop_timer_handle = tw_timer_start_2t_1w_2048sl (&tm->single_wheel, e - tm->test_elts, 1 /* timer id */ , expiration_time); } adds += i; for (i = 0; i < tm->niter; i++) { run_single_wheel (&tm->single_wheel, tm->ticks_per_iter); j = 0; vec_reset_length (deleted_indices); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ tw_timer_stop_2t_1w_2048sl (&tm->single_wheel, e->stop_timer_handle); vec_add1 (deleted_indices, e - tm->test_elts); if (++j >= tm->ntimers / 4) goto del_and_re_add; })); /* *INDENT-ON* */ del_and_re_add: for (j = 0; j < vec_len (deleted_indices); j++) { pool_put_index (tm->test_elts, deleted_indices[j]); } deletes += j; for (j = 0; j < tm->ntimers / 4; j++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & (2047); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + tm->single_wheel.current_tick; e->stop_timer_handle = tw_timer_start_2t_1w_2048sl (&tm->single_wheel, e - tm->test_elts, 1 /* timer id */ , expiration_time); } adds += j; } vec_free (deleted_indices); run_single_wheel (&tm->single_wheel, max_expiration_time + 1); after = clib_time_now (&tm->clib_time); fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes, tm->single_wheel.current_tick); fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n", (after - before), ((f64) adds + (f64) deletes + (f64) tm->single_wheel.current_tick) / (after - before)); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat (stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ pool_free (tm->test_elts); tw_timer_wheel_free_2t_1w_2048sl (&tm->single_wheel); return 0; } static clib_error_t * test2_double (tw_timer_test_main_t * tm) { u32 i, j; tw_timer_test_elt_t *e; u32 initial_wheel_offset; u32 expiration_time; u32 max_expiration_time = 0; u32 *deleted_indices = 0; u32 adds = 0, deletes = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel, expired_timer_double_callback, 1.0 /* timer interval */ , ~0); /* Prime offset */ initial_wheel_offset = 7577; run_double_wheel (&tm->double_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n", tm->double_wheel.current_tick, tm->double_wheel.current_index[TW_TIMER_RING_FAST], tm->double_wheel.current_index[TW_TIMER_RING_SLOW]); initial_wheel_offset = tm->double_wheel.current_tick; fformat (stdout, "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n", tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed); before = clib_time_now (&tm->clib_time); /* Prime the pump */ for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + initial_wheel_offset; e->stop_timer_handle = tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ , expiration_time); } adds += i; for (i = 0; i < tm->niter; i++) { run_double_wheel (&tm->double_wheel, tm->ticks_per_iter); j = 0; vec_reset_length (deleted_indices); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ tw_timer_stop_16t_2w_512sl (&tm->double_wheel, e->stop_timer_handle); vec_add1 (deleted_indices, e - tm->test_elts); if (++j >= tm->ntimers / 4) goto del_and_re_add; })); /* *INDENT-ON* */ del_and_re_add: for (j = 0; j < vec_len (deleted_indices); j++) pool_put_index (tm->test_elts, deleted_indices[j]); deletes += j; for (j = 0; j < tm->ntimers / 4; j++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + tm->double_wheel.current_tick; e->stop_timer_handle = tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ , expiration_time); } adds += j; } vec_free (deleted_indices); run_double_wheel (&tm->double_wheel, max_expiration_time + 1); after = clib_time_now (&tm->clib_time); fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes, tm->double_wheel.current_tick); fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n", (after - before), ((f64) adds + (f64) deletes + (f64) tm->double_wheel.current_tick) / (after - before)); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat (stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ pool_free (tm->test_elts); tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel); return 0; } static u32 get_expiration_time (tw_timer_test_main_t * tm) { u32 expiration_time; do { expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1); } while (expiration_time == 0); return expiration_time; } static clib_error_t * test2_double_updates (tw_timer_test_main_t * tm) { u32 i, j; tw_timer_test_elt_t *e; u32 initial_wheel_offset; u32 expiration_time; u32 max_expiration_time = 0, updates = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel, expired_timer_double_callback, 1.0 /* timer interval */ , ~0); /* Prime offset */ initial_wheel_offset = 7577; run_double_wheel (&tm->double_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n", tm->double_wheel.current_tick, tm->double_wheel.current_index[TW_TIMER_RING_FAST], tm->double_wheel.current_index[TW_TIMER_RING_SLOW]); initial_wheel_offset = tm->double_wheel.current_tick; fformat (stdout, "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n", tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed); before = clib_time_now (&tm->clib_time); /* Prime the pump */ for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); expiration_time = get_expiration_time (tm); max_expiration_time = clib_max (expiration_time, max_expiration_time); e->expected_to_expire = expiration_time + initial_wheel_offset; e->stop_timer_handle = tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ , expiration_time); } for (i = 0; i < tm->niter; i++) { run_double_wheel (&tm->double_wheel, tm->ticks_per_iter); j = 0; /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ expiration_time = get_expiration_time (tm); max_expiration_time = clib_max (expiration_time, max_expiration_time); e->expected_to_expire = expiration_time + tm->double_wheel.current_tick; tw_timer_update_16t_2w_512sl (&tm->double_wheel, e->stop_timer_handle, expiration_time); if (++j >= tm->ntimers / 4) goto done; })); /* *INDENT-ON* */ done: updates += j; } run_double_wheel (&tm->double_wheel, max_expiration_time + 1); after = clib_time_now (&tm->clib_time); fformat (stdout, "%d updates, %d ticks\n", updates, tm->double_wheel.current_tick); fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n", (after - before), ((f64) updates + (f64) tm->double_wheel.current_tick) / (after - before)); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat (stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ pool_free (tm->test_elts); tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel); return 0; } static clib_error_t * test2_triple (tw_timer_test_main_t * tm) { u32 i, j; tw_timer_test_elt_t *e; u32 initial_wheel_offset = 0; u32 expiration_time; u32 max_expiration_time = 0; u32 *deleted_indices = 0; u32 adds = 0, deletes = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_4t_3w_256sl (&tm->triple_wheel, expired_timer_triple_callback, 1.0 /* timer interval */ , ~0); /* Prime offset */ initial_wheel_offset = 75700; run_triple_wheel (&tm->triple_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fi %d si %d gi %d\n", tm->triple_wheel.current_tick, tm->triple_wheel.current_index[TW_TIMER_RING_FAST], tm->triple_wheel.current_index[TW_TIMER_RING_SLOW], tm->triple_wheel.current_index[TW_TIMER_RING_GLACIER]); initial_wheel_offset = tm->triple_wheel.current_tick; fformat (stdout, "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n", tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed); before = clib_time_now (&tm->clib_time); /* Prime the pump */ for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + initial_wheel_offset; e->stop_timer_handle = tw_timer_start_4t_3w_256sl (&tm->triple_wheel, e - tm->test_elts, 3 /* timer id */ , expiration_time); } adds += i; for (i = 0; i < tm->niter; i++) { run_triple_wheel (&tm->triple_wheel, tm->ticks_per_iter); j = 0; vec_reset_length (deleted_indices); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ tw_timer_stop_4t_3w_256sl (&tm->triple_wheel, e->stop_timer_handle); vec_add1 (deleted_indices, e - tm->test_elts); if (++j >= tm->ntimers / 4) goto del_and_re_add; })); /* *INDENT-ON* */ del_and_re_add: for (j = 0; j < vec_len (deleted_indices); j++) pool_put_index (tm->test_elts, deleted_indices[j]); deletes += j; for (j = 0; j < tm->ntimers / 4; j++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + tm->triple_wheel.current_tick; e->stop_timer_handle = tw_timer_start_4t_3w_256sl (&tm->triple_wheel, e - tm->test_elts, 3 /* timer id */ , expiration_time); } adds += j; } vec_free (deleted_indices); run_triple_wheel (&tm->triple_wheel, max_expiration_time + 1); after = clib_time_now (&tm->clib_time); fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes, tm->triple_wheel.current_tick); fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n", (after - before), ((f64) adds + (f64) deletes + (f64) tm->triple_wheel.current_tick) / (after - before)); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat (stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ pool_free (tm->test_elts); tw_timer_wheel_free_4t_3w_256sl (&tm->triple_wheel); return 0; } static clib_error_t * test2_triple_ov (tw_timer_test_main_t * tm) { u32 i, j; tw_timer_test_elt_t *e; u32 initial_wheel_offset = 0; u32 expiration_time; u32 max_expiration_time = 0; u32 *deleted_indices = 0; u32 adds = 0, deletes = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_1t_3w_1024sl_ov (&tm->triple_ov_wheel, expired_timer_triple_ov_callback, 1.0 /* timer interval */ , ~0); /* Prime offset */ initial_wheel_offset = 75700; run_triple_ov_wheel (&tm->triple_ov_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fi %d si %d gi %d\n", tm->triple_ov_wheel.current_tick, tm->triple_ov_wheel.current_index[TW_TIMER_RING_FAST], tm->triple_ov_wheel.current_index[TW_TIMER_RING_SLOW], tm->triple_ov_wheel.current_index[TW_TIMER_RING_GLACIER]); initial_wheel_offset = tm->triple_ov_wheel.current_tick; fformat (stdout, "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n", tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed); before = clib_time_now (&tm->clib_time); /* Prime the pump */ for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e));
/*
 * Copyright (c) 2018 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vnet/dhcp/dhcp6_client_common_dp.h>
#include <vnet/dhcp/dhcp6_ia_na_client_dp.h>
#include <vnet/dhcp/dhcp6_pd_client_dp.h>
#include <vnet/dhcp/dhcp6_packet.h>
#include <vnet/udp/udp.h>

dhcp6_client_common_main_t dhcp6_client_common_main;
dhcpv6_duid_ll_string_t client_duid;

u32
server_index_get_or_create (u8 * data, u16 len)
{
  dhcp6_client_common_main_t *ccm = &dhcp6_client_common_main;
  u32 i;
  server_id_t *se;
  server_id_t new_se;

  for (i = 0; i < vec_len (ccm->server_ids); i++)
    {
      se = &ccm->server_ids[i];
      if (se->len == len && 0 == memcmp (se->data, data, len))
	return i;
    }

  new_se.len = len;
  new_se.data = 0;
  vec_validate (new_se.data, len - 1);
  memcpy (new_se.data, data, len);

  vec_add1 (ccm->server_ids, new_se);

  return vec_len (ccm->server_ids) - 1;
}

void
vl_api_dhcp6_duid_ll_set_t_handler (vl_api_dhcp6_duid_ll_set_t * mp)
{
  vl_api_dhcp6_duid_ll_set_reply_t *rmp;
  dhcpv6_duid_ll_string_t *duid;
  int rv = 0;

  duid = (dhcpv6_duid_ll_string_t *) mp->duid_ll;
  if (duid->duid_type != htonl (DHCPV6_DUID_LL))
    {
      rv = VNET_API_ERROR_INVALID_VALUE;
      goto reply;
    }
  clib_memcpy (&client_duid, &duid, sizeof (client_duid));

reply:
  REPLY_MACRO (VL_API_DHCP6_DUID_LL_SET_REPLY);
}

static void
generate_client_duid (void)
{
  client_duid.duid_type = htons (DHCPV6_DUID_LL);
  client_duid.hardware_type = htons (1);

  vnet_main_t *vnm = vnet_get_main ();
  vnet_interface_main_t *im = &vnm->interface_main;
  vnet_hw_interface_t *hi;
  ethernet_interface_t