api: multiple connections per process
[vpp.git] / src / plugins / dhcp / dhcp6_pd_client_cp.c
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 <vnet/vnet.h>
17 #include <vlibmemory/api.h>
18 #include <dhcp/dhcp6_packet.h>
19 #include <dhcp/dhcp6_pd_client_dp.h>
20 #include <vnet/ip/ip.h>
21 #include <vnet/ip/ip6.h>
22 #include <vnet/ip/ip6_neighbor.h>
23 #include <float.h>
24 #include <math.h>
25 #include <string.h>
26 #include <vnet/ip/ip_types_api.h>
27
28 typedef struct
29 {
30   u32 prefix_group_index;
31   uword opaque_data;            // used by prefix publisher
32   ip6_address_t prefix;
33   u8 prefix_length;
34   u32 preferred_lt;
35   u32 valid_lt;
36   f64 due_time;
37 } prefix_info_t;
38
39 typedef struct
40 {
41   u8 enabled;
42   u32 prefix_group_index;
43   u32 server_index;
44   u32 T1;
45   u32 T2;
46   f64 T1_due_time;
47   f64 T2_due_time;
48   u32 prefix_count;
49   u8 rebinding;
50 } client_state_t;
51
52 typedef struct
53 {
54   client_state_t *client_state_by_sw_if_index;
55   clib_bitmap_t *prefix_ownership_bitmap;
56   u32 n_clients;
57   f64 max_valid_due_time;
58
59   /* convenience */
60   vlib_main_t *vlib_main;
61   vnet_main_t *vnet_main;
62   api_main_t *api_main;
63   u32 node_index;
64 } dhcp6_pd_client_cp_main_t;
65
66 static dhcp6_pd_client_cp_main_t dhcp6_pd_client_cp_main;
67
68 typedef struct
69 {
70   prefix_info_t *prefix_pool;
71   const u8 **prefix_group_name_by_index;
72 } ip6_prefix_main_t;
73
74 static ip6_prefix_main_t ip6_prefix_main;
75
76 typedef struct
77 {
78   /* config */
79   u32 sw_if_index;
80   u32 prefix_group_index;
81   ip6_address_t address;
82   u8 prefix_length;
83
84   /* state */
85   u8 configured_in_data_plane;
86 } ip6_address_info_t;
87
88 typedef struct
89 {
90   ip6_address_info_t *addresses;
91   u32 *active_prefix_index_by_prefix_group_index;
92 } ip6_address_with_prefix_main_t;
93
94 static ip6_address_with_prefix_main_t ip6_address_with_prefix_main;
95
96 enum
97 {
98   DHCPV6_PD_EVENT_INTERRUPT,
99   DHCPV6_PD_EVENT_DISABLE,
100 };
101
102 static_always_inline u32
103 active_prefix_index_by_prefix_group_index_get (u32 prefix_group_index)
104 {
105   ip6_address_with_prefix_main_t *apm = &ip6_address_with_prefix_main;
106
107   if (prefix_group_index >=
108       vec_len (apm->active_prefix_index_by_prefix_group_index))
109     return ~0;
110
111   return apm->active_prefix_index_by_prefix_group_index[prefix_group_index];
112 }
113
114 static_always_inline void
115 active_prefix_index_by_prefix_group_index_set (u32 prefix_group_index,
116                                                u32 prefix_index)
117 {
118   ip6_address_with_prefix_main_t *apm = &ip6_address_with_prefix_main;
119   static const u32 empty = ~0;
120
121   ASSERT (prefix_group_index != ~0);
122
123   if (prefix_index == ~0
124       && prefix_group_index >=
125       vec_len (apm->active_prefix_index_by_prefix_group_index))
126     return;
127
128   vec_validate_init_empty (apm->active_prefix_index_by_prefix_group_index,
129                            prefix_group_index, empty);
130   apm->active_prefix_index_by_prefix_group_index[prefix_group_index] =
131     prefix_index;
132 }
133
134 static_always_inline u8
135 is_dhcpv6_pd_prefix (prefix_info_t * prefix_info)
136 {
137   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
138   ip6_prefix_main_t *pm = &ip6_prefix_main;
139   u32 prefix_index;
140
141   prefix_index = prefix_info - pm->prefix_pool;
142   return clib_bitmap_get (rm->prefix_ownership_bitmap, prefix_index);
143 }
144
145 static_always_inline void
146 set_is_dhcpv6_pd_prefix (prefix_info_t * prefix_info, u8 value)
147 {
148   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
149   ip6_prefix_main_t *pm = &ip6_prefix_main;
150   u32 prefix_index;
151
152   prefix_index = prefix_info - pm->prefix_pool;
153   rm->prefix_ownership_bitmap =
154     clib_bitmap_set (rm->prefix_ownership_bitmap, prefix_index, value);
155 }
156
157 static void
158 cp_ip6_address_prefix_add_del_handler (u32 prefix_index, u8 is_add);
159
160 static void
161 notify_prefix_add_del (u32 prefix_index, u8 is_add)
162 {
163   // TODO: use registries
164   cp_ip6_address_prefix_add_del_handler (prefix_index, is_add);
165 }
166
167 static void
168 send_client_message_start_stop (u32 sw_if_index, u32 server_index,
169                                 u8 msg_type, prefix_info_t * prefix_list,
170                                 u8 start)
171 {
172   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
173   dhcp6_pd_send_client_message_params_t params = { 0, };
174   dhcp6_pd_send_client_message_params_prefix_t *prefixes = 0, *pref;
175   u32 i;
176
177   ASSERT (sw_if_index < vec_len (rm->client_state_by_sw_if_index) &&
178           rm->client_state_by_sw_if_index[sw_if_index].enabled);
179   client_state_t *client_state =
180     &rm->client_state_by_sw_if_index[sw_if_index];
181
182   params.sw_if_index = sw_if_index;
183   params.server_index = server_index;
184   params.msg_type = msg_type;
185   if (start)
186     {
187       if (msg_type == DHCPV6_MSG_SOLICIT)
188         {
189           params.irt = 1;
190           params.mrt = 120;
191         }
192       else if (msg_type == DHCPV6_MSG_REQUEST)
193         {
194           params.irt = 1;
195           params.mrt = 30;
196           params.mrc = 10;
197         }
198       else if (msg_type == DHCPV6_MSG_RENEW)
199         {
200           params.irt = 10;
201           params.mrt = 600;
202           f64 current_time = vlib_time_now (rm->vlib_main);
203           i32 diff_time = client_state->T2 - current_time;
204           if (diff_time < 0)
205             diff_time = 0;
206           params.mrd = diff_time;
207         }
208       else if (msg_type == DHCPV6_MSG_REBIND)
209         {
210           params.irt = 10;
211           params.mrt = 600;
212           f64 current_time = vlib_time_now (rm->vlib_main);
213           i32 diff_time = rm->max_valid_due_time - current_time;
214           if (diff_time < 0)
215             diff_time = 0;
216           params.mrd = diff_time;
217         }
218       else if (msg_type == DHCPV6_MSG_RELEASE)
219         {
220           params.mrc = 1;
221         }
222     }
223
224   params.T1 = 0;
225   params.T2 = 0;
226   if (vec_len (prefix_list) != 0)
227     vec_validate (prefixes, vec_len (prefix_list) - 1);
228   for (i = 0; i < vec_len (prefix_list); i++)
229     {
230       prefix_info_t *prefix = &prefix_list[i];
231       pref = &prefixes[i];
232       pref->valid_lt = prefix->valid_lt;
233       pref->preferred_lt = prefix->preferred_lt;
234       pref->prefix = prefix->prefix;
235       pref->prefix_length = prefix->prefix_length;
236     }
237   params.prefixes = prefixes;
238
239   dhcp6_pd_send_client_message (rm->vlib_main, sw_if_index, !start, &params);
240
241   vec_free (params.prefixes);
242 }
243
244 static void interrupt_process (void);
245
246 static u32
247 ip6_enable (u32 sw_if_index)
248 {
249   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
250   clib_error_t *rv;
251
252   rv = enable_ip6_interface (rm->vlib_main, sw_if_index);
253
254   return rv != 0;
255 }
256
257 static u8
258 ip6_prefixes_equal (ip6_address_t * prefix1, ip6_address_t * prefix2, u8 len)
259 {
260   if (len >= 64)
261     {
262       if (prefix1->as_u64[0] != prefix2->as_u64[0])
263         return 0;
264       if (len == 64)
265         return 1;
266       return clib_net_to_host_u64 (prefix1->as_u64[1]) >> (128 - len) ==
267         clib_net_to_host_u64 (prefix2->as_u64[1]) >> (128 - len);
268     }
269   return clib_net_to_host_u64 (prefix1->as_u64[0]) >> (64 - len) ==
270     clib_net_to_host_u64 (prefix2->as_u64[0]) >> (64 - len);
271 }
272
273 static clib_error_t *
274 dhcp6_pd_reply_event_handler (vl_api_dhcp6_pd_reply_event_t * mp)
275 {
276   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
277   ip6_prefix_main_t *pm = &ip6_prefix_main;
278   vlib_main_t *vm = rm->vlib_main;
279   client_state_t *client_state;
280   ip6_address_t *prefix;
281   u32 sw_if_index;
282   u32 n_prefixes;
283   vl_api_dhcp6_pd_prefix_info_t *api_prefix;
284   u32 inner_status_code;
285   u32 status_code;
286   u32 server_index;
287   f64 current_time;
288   clib_error_t *error = 0;
289   u32 i;
290
291   current_time = vlib_time_now (vm);
292
293   sw_if_index = ntohl (mp->sw_if_index);
294
295   if (sw_if_index >= vec_len (rm->client_state_by_sw_if_index))
296     return 0;
297
298   client_state = &rm->client_state_by_sw_if_index[sw_if_index];
299
300   if (!client_state->enabled)
301     return 0;
302
303   server_index = ntohl (mp->server_index);
304
305   n_prefixes = ntohl (mp->n_prefixes);
306
307   inner_status_code = ntohs (mp->inner_status_code);
308   status_code = ntohs (mp->status_code);
309
310   if (mp->msg_type == DHCPV6_MSG_API_ADVERTISE
311       && client_state->server_index == ~0)
312     {
313       prefix_info_t *prefix_list = 0, *prefix_info;
314       u8 prefix_length;
315
316       if (inner_status_code == DHCPV6_STATUS_NOPREFIX_AVAIL)
317         {
318           clib_warning
319             ("Advertise message arrived with NoPrefixAvail status code");
320           return 0;
321         }
322
323       if (n_prefixes > 0)
324         vec_validate (prefix_list, n_prefixes - 1);
325       for (i = 0; i < n_prefixes; i++)
326         {
327           api_prefix = &mp->prefixes[i];
328           prefix = (ip6_address_t *) api_prefix->prefix.address;
329           prefix_length = api_prefix->prefix.len;
330
331           prefix_info = &prefix_list[i];
332           prefix_info->prefix = *prefix;
333           prefix_info->prefix_length = prefix_length;
334           prefix_info->preferred_lt = 0;
335           prefix_info->valid_lt = 0;
336         }
337
338       client_state->server_index = server_index;
339
340       send_client_message_start_stop (sw_if_index, server_index,
341                                       DHCPV6_MSG_REQUEST, prefix_list, 1);
342       vec_free (prefix_list);
343     }
344
345   if (mp->msg_type != DHCPV6_MSG_API_REPLY)
346     return 0;
347
348   if (!client_state->rebinding && client_state->server_index != server_index)
349     {
350       clib_warning ("Reply message arrived with Server ID different "
351                     "from that in Request or Renew message");
352       return 0;
353     }
354
355   if (inner_status_code == DHCPV6_STATUS_NOPREFIX_AVAIL)
356     {
357       clib_warning ("Reply message arrived with NoPrefixAvail status code");
358       if (n_prefixes > 0)
359         {
360           clib_warning
361             ("Invalid Reply message arrived: It contains NoPrefixAvail "
362              "status code but also contains prefixes");
363           return 0;
364         }
365     }
366
367   if (status_code == DHCPV6_STATUS_UNSPEC_FAIL)
368     {
369       clib_warning ("Reply message arrived with UnspecFail status code");
370       return 0;
371     }
372
373   send_client_message_start_stop (sw_if_index, server_index,
374                                   mp->msg_type, 0, 0);
375
376   for (i = 0; i < n_prefixes; i++)
377     {
378       prefix_info_t *prefix_info = 0;
379       u8 prefix_length;
380       u32 valid_time;
381       u32 preferred_time;
382
383       api_prefix = &mp->prefixes[i];
384
385       prefix = (ip6_address_t *) api_prefix->prefix.address;
386       prefix_length = api_prefix->prefix.len;
387
388       if (ip6_address_is_link_local_unicast (prefix))
389         continue;
390
391       valid_time = ntohl (api_prefix->valid_time);
392       preferred_time = ntohl (api_prefix->preferred_time);
393       prefix_length = api_prefix->prefix.len;
394
395       if (preferred_time > valid_time)
396         continue;
397
398       u8 address_prefix_present = 0;
399       /* *INDENT-OFF* */
400       pool_foreach (prefix_info, pm->prefix_pool,
401       ({
402         if (is_dhcpv6_pd_prefix (prefix_info) &&
403             prefix_info->opaque_data == sw_if_index &&
404             prefix_info->prefix_length == prefix_length &&
405             ip6_prefixes_equal (&prefix_info->prefix, prefix, prefix_length))
406           {
407             address_prefix_present = 1;
408             goto prefix_pool_foreach_out;
409           }
410       }));
411       /* *INDENT-ON* */
412     prefix_pool_foreach_out:
413
414       if (address_prefix_present)
415         {
416           /*
417            * We found the prefix. Move along.
418            * Don't touch the prefix timers!
419            * If we happen to receive a renew reply just before we
420            * would have sent a solicit to renew the prefix delegation,
421            * we forget to renew the delegation. Worse luck, we start
422            * sending router advertisements with a valid time of zero,
423            * and the wheels fall off...
424            */
425           continue;
426         }
427
428       if (valid_time == 0)
429         continue;
430
431       pool_get (pm->prefix_pool, prefix_info);
432       prefix_info->prefix_group_index = client_state->prefix_group_index;
433       set_is_dhcpv6_pd_prefix (prefix_info, 1);
434       prefix_info->opaque_data = sw_if_index;
435       prefix_info->prefix_length = prefix_length;
436       prefix_info->prefix = *prefix;
437       prefix_info->preferred_lt = preferred_time;
438       prefix_info->valid_lt = valid_time;
439       prefix_info->due_time = current_time + valid_time;
440       if (prefix_info->due_time > rm->max_valid_due_time)
441         rm->max_valid_due_time = prefix_info->due_time;
442       rm->client_state_by_sw_if_index[sw_if_index].prefix_count++;
443
444       u32 prefix_index = prefix_info - pm->prefix_pool;
445       notify_prefix_add_del (prefix_index, 1);
446     }
447
448   client_state->server_index = server_index;
449   client_state->T1 = ntohl (mp->T1);
450   client_state->T2 = ntohl (mp->T2);
451   if (client_state->T1 != 0)
452     client_state->T1_due_time = current_time + client_state->T1;
453   if (client_state->T2 != 0)
454     client_state->T2_due_time = current_time + client_state->T2;
455   client_state->rebinding = 0;
456
457   interrupt_process ();
458
459   return error;
460 }
461
462 static prefix_info_t *
463 create_prefix_list (u32 sw_if_index)
464 {
465   ip6_prefix_main_t *pm = &ip6_prefix_main;
466   prefix_info_t *prefix_info, *prefix_list = 0;;
467
468   /* *INDENT-OFF* */
469   pool_foreach (prefix_info, pm->prefix_pool,
470   ({
471     if (is_dhcpv6_pd_prefix (prefix_info) &&
472         prefix_info->opaque_data == sw_if_index)
473       {
474         u32 pos = vec_len (prefix_list);
475         vec_validate (prefix_list, pos);
476         clib_memcpy (&prefix_list[pos], prefix_info, sizeof (*prefix_info));
477       }
478   }));
479   /* *INDENT-ON* */
480
481   return prefix_list;
482 }
483
484 VNET_DHCP6_PD_REPLY_EVENT_FUNCTION (dhcp6_pd_reply_event_handler);
485
486 static uword
487 dhcp6_pd_client_cp_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
488                             vlib_frame_t * f)
489 {
490   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
491   ip6_prefix_main_t *pm = &ip6_prefix_main;
492   prefix_info_t *prefix_info;
493   client_state_t *client_state;
494   f64 sleep_time = 1e9;
495   f64 current_time;
496   f64 due_time;
497   uword event_type;
498   uword *event_data = 0;
499   int i;
500
501   while (1)
502     {
503       vlib_process_wait_for_event_or_clock (vm, sleep_time);
504       event_type = vlib_process_get_events (vm, &event_data);
505       vec_reset_length (event_data);
506
507       if (event_type == DHCPV6_PD_EVENT_DISABLE)
508         {
509           vlib_node_set_state (vm, rm->node_index, VLIB_NODE_STATE_DISABLED);
510           sleep_time = 1e9;
511           continue;
512         }
513
514       current_time = vlib_time_now (vm);
515       do
516         {
517           due_time = current_time + 1e9;
518           /* *INDENT-OFF* */
519           pool_foreach (prefix_info, pm->prefix_pool,
520           ({
521             if (is_dhcpv6_pd_prefix (prefix_info))
522               {
523                 if (prefix_info->due_time > current_time)
524                   {
525                     if (prefix_info->due_time < due_time)
526                       due_time = prefix_info->due_time;
527                   }
528                 else
529                   {
530                     u32 prefix_index = prefix_info - pm->prefix_pool;
531                     notify_prefix_add_del (prefix_index, 0);
532                     u32 sw_if_index = prefix_info->opaque_data;
533                     set_is_dhcpv6_pd_prefix (prefix_info, 0);
534                     pool_put (pm->prefix_pool, prefix_info);
535                     client_state = &rm->client_state_by_sw_if_index[sw_if_index];
536                     if (--client_state->prefix_count == 0)
537                       {
538                         client_state->rebinding = 0;
539                         client_state->server_index = ~0;
540                         send_client_message_start_stop (sw_if_index, ~0,
541                                                         DHCPV6_MSG_SOLICIT,
542                                                         0, 1);
543                       }
544                   }
545               }
546           }));
547           /* *INDENT-ON* */
548           for (i = 0; i < vec_len (rm->client_state_by_sw_if_index); i++)
549             {
550               client_state_t *cs = &rm->client_state_by_sw_if_index[i];
551               if (cs->enabled && cs->server_index != ~0)
552                 {
553                   if (cs->T2_due_time > current_time)
554                     {
555                       if (cs->T2_due_time < due_time)
556                         due_time = cs->T2_due_time;
557                       if (cs->T1_due_time > current_time)
558                         {
559                           if (cs->T1_due_time < due_time)
560                             due_time = cs->T1_due_time;
561                         }
562                       else
563                         {
564                           cs->T1_due_time = DBL_MAX;
565                           prefix_info_t *prefix_list;
566                           prefix_list = create_prefix_list (i);
567                           send_client_message_start_stop (i, cs->server_index,
568                                                           DHCPV6_MSG_RENEW,
569                                                           prefix_list, 1);
570                           vec_free (prefix_list);
571                         }
572                     }
573                   else
574                     {
575                       cs->T2_due_time = DBL_MAX;
576                       prefix_info_t *prefix_list;
577                       prefix_list = create_prefix_list (i);
578                       cs->rebinding = 1;
579                       send_client_message_start_stop (i, ~0,
580                                                       DHCPV6_MSG_REBIND,
581                                                       prefix_list, 1);
582                       vec_free (prefix_list);
583                     }
584                 }
585             }
586           current_time = vlib_time_now (vm);
587         }
588       while (due_time < current_time);
589
590       sleep_time = due_time - current_time;
591     }
592
593   return 0;
594 }
595
596 /* *INDENT-OFF* */
597 VLIB_REGISTER_NODE (dhcp6_pd_client_cp_process_node) = {
598     .function = dhcp6_pd_client_cp_process,
599     .type = VLIB_NODE_TYPE_PROCESS,
600     .name = "dhcp6-pd-client-cp-process",
601 };
602 /* *INDENT-ON* */
603
604 static void
605 interrupt_process (void)
606 {
607   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
608   vlib_main_t *vm = rm->vlib_main;
609
610   vlib_process_signal_event (vm, dhcp6_pd_client_cp_process_node.index,
611                              DHCPV6_PD_EVENT_INTERRUPT, 0);
612 }
613
614 static void
615 disable_process (void)
616 {
617   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
618   vlib_main_t *vm = rm->vlib_main;
619
620   vlib_process_signal_event (vm, dhcp6_pd_client_cp_process_node.index,
621                              DHCPV6_PD_EVENT_DISABLE, 0);
622 }
623
624 static void
625 enable_process (void)
626 {
627   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
628   vlib_main_t *vm = rm->vlib_main;
629   vlib_node_t *node;
630
631   node = vec_elt (vm->node_main.nodes, rm->node_index);
632
633   vlib_node_set_state (vm, rm->node_index, VLIB_NODE_STATE_POLLING);
634   vlib_start_process (vm, node->runtime_index);
635 }
636
637 static u32
638 cp_ip6_construct_address (ip6_address_info_t * address_info, u32 prefix_index,
639                           ip6_address_t * r_addr)
640 {
641   ip6_prefix_main_t *pm = &ip6_prefix_main;
642   prefix_info_t *prefix;
643   u64 mask, addr0, pref;
644
645   addr0 = clib_net_to_host_u64 (address_info->address.as_u64[0]);
646   prefix = &pm->prefix_pool[prefix_index];
647   if (prefix->prefix_length > 64)
648     {
649       clib_warning ("Prefix length is bigger that 64 bits");
650       return 1;
651     }
652   mask = ((u64) 1 << (64 - prefix->prefix_length)) - 1;
653   addr0 &= mask;
654   pref = clib_host_to_net_u64 (prefix->prefix.as_u64[0]);
655   pref &= ~mask;
656   addr0 |= pref;
657   r_addr->as_u64[0] = clib_host_to_net_u64 (addr0);
658   r_addr->as_u64[1] = address_info->address.as_u64[1];
659
660   return 0;
661 }
662
663 static void
664 cp_ip6_address_add_del_now (ip6_address_info_t * address_info, u8 is_add)
665 {
666   vlib_main_t *vm = vlib_get_main ();
667   u32 prefix_index;
668   ip6_address_t addr;
669   clib_error_t *error;
670
671   if (address_info->prefix_group_index != ~0)
672     prefix_index =
673       active_prefix_index_by_prefix_group_index_get
674       (address_info->prefix_group_index);
675   else
676     prefix_index = ~0;
677
678   if (is_add && !address_info->configured_in_data_plane)
679     {
680       if (prefix_index != ~0)
681         {
682           if (cp_ip6_construct_address (address_info, prefix_index, &addr) !=
683               0)
684             return;
685           error =
686             ip6_add_del_interface_address (vm, address_info->sw_if_index,
687                                            &addr, address_info->prefix_length,
688                                            0 /* add */ );
689           if (error)
690             clib_warning ("Failed adding IPv6 address: %U",
691                           format_clib_error, error);
692           else
693             {
694               if (CLIB_DEBUG > 0)
695                 clib_warning ("Add address %U on %U",
696                               format_ip6_address_and_length,
697                               &addr, address_info->prefix_length,
698                               format_vnet_sw_if_index_name,
699                               vnet_get_main (), address_info->sw_if_index);
700
701               address_info->configured_in_data_plane = 1;
702             }
703         }
704       else
705         {
706           if (address_info->prefix_group_index == ~0)
707             {
708               error =
709                 ip6_add_del_interface_address (vm, address_info->sw_if_index,
710                                                &address_info->address,
711                                                address_info->prefix_length,
712                                                0 /* add */ );
713               if (error)
714                 clib_warning ("Failed adding IPv6 address: %U",
715                               format_clib_error, error);
716               else
717                 {
718                   if (CLIB_DEBUG > 0)
719                     clib_warning ("Add address %U on %U",
720                                   format_ip6_address_and_length,
721                                   &addr, address_info->prefix_length,
722                                   format_vnet_sw_if_index_name,
723                                   vnet_get_main (),
724                                   address_info->sw_if_index);
725
726                   address_info->configured_in_data_plane = 1;
727                 }
728             }
729         }
730     }
731   else if (!is_add && address_info->configured_in_data_plane)
732     {
733       if (prefix_index == ~0)
734         {
735           if (address_info->prefix_group_index == ~0)
736             {
737               error =
738                 ip6_add_del_interface_address (vm, address_info->sw_if_index,
739                                                &address_info->address,
740                                                address_info->prefix_length,
741                                                1 /* del */ );
742               if (error)
743                 clib_warning ("Failed deleting IPv6 address: %U",
744                               format_clib_error, error);
745               address_info->configured_in_data_plane = 0;
746             }
747           else
748             clib_warning ("Deleting address with prefix "
749                           "but active prefix index is not set");
750         }
751       else
752         {
753           if (cp_ip6_construct_address (address_info, prefix_index, &addr) !=
754               0)
755             return;
756           error =
757             ip6_add_del_interface_address (vm, address_info->sw_if_index,
758                                            &addr, address_info->prefix_length,
759                                            1 /* del */ );
760           if (error)
761             clib_warning ("Failed deleting IPv6 address: %U",
762                           format_clib_error, error);
763           address_info->configured_in_data_plane = 0;
764         }
765     }
766 }
767
768 static u32
769 cp_ip6_address_find_new_active_prefix (u32 prefix_group_index,
770                                        u32 ignore_prefix_index)
771 {
772   ip6_prefix_main_t *pm = &ip6_prefix_main;
773   prefix_info_t *prefix_info;
774
775   /* *INDENT-OFF* */
776   pool_foreach (prefix_info, pm->prefix_pool,
777   ({
778     if (prefix_info->prefix_group_index == prefix_group_index &&
779         prefix_info - pm->prefix_pool != ignore_prefix_index)
780         return prefix_info - pm->prefix_pool;
781   }));
782   /* *INDENT-ON* */
783   return ~0;
784 }
785
786 static void
787 cp_ip6_advertise_prefix (prefix_info_t * prefix_info,
788                          ip6_address_info_t * address_info, int enable)
789 {
790   vlib_main_t *vm = vlib_get_main ();
791   ip6_main_t *im = &ip6_main;
792   u32 prefix_index;
793   ip6_address_t addr;
794   int rv;
795
796   prefix_index =
797     active_prefix_index_by_prefix_group_index_get
798     (address_info->prefix_group_index);
799
800   if (cp_ip6_construct_address (address_info, prefix_index, &addr) != 0)
801     {
802       clib_warning ("address construction FAIL");
803       return;
804     }
805
806   /* The RA code assumes that host bits are zero, so clear them */
807   addr.as_u64[0] &= im->fib_masks[address_info->prefix_length].as_u64[0];
808   addr.as_u64[1] &= im->fib_masks[address_info->prefix_length].as_u64[1];
809
810   rv = ip6_neighbor_ra_prefix (vm, address_info->sw_if_index,
811                                &addr, address_info->prefix_length,
812                                0 /* use_default */ ,
813                                prefix_info->valid_lt,
814                                prefix_info->preferred_lt,
815                                0 /* no_advertise */ ,
816                                0 /* off_link */ ,
817                                0 /* no_autoconfig */ ,
818                                0 /* no_onlink */ ,
819                                enable == 0 /* is_no */ );
820   if (rv != 0)
821     {
822       clib_warning ("ip6_neighbor_ra_prefix returned %d", rv);
823       return;
824     }
825
826   if (CLIB_DEBUG > 0)
827     clib_warning ("Advertise prefix %U valid lt %u preferred lt %u",
828                   format_ip6_address_and_length, &addr,
829                   address_info->prefix_length, prefix_info->valid_lt,
830                   prefix_info->preferred_lt);
831 }
832
833
834 static void
835 cp_ip6_address_prefix_add_del_handler (u32 prefix_index, u8 is_add)
836 {
837   ip6_address_with_prefix_main_t *apm = &ip6_address_with_prefix_main;
838   ip6_prefix_main_t *pm = &ip6_prefix_main;
839   ip6_address_info_t *address_info;
840   prefix_info_t *prefix;
841   u32 new_prefix_index;
842   u32 prefix_group_index;
843   u32 i;
844
845   prefix = &pm->prefix_pool[prefix_index];
846   prefix_group_index = prefix->prefix_group_index;
847
848   if (is_add)
849     {
850       if (active_prefix_index_by_prefix_group_index_get
851           (prefix_group_index) == ~0)
852         {
853           active_prefix_index_by_prefix_group_index_set
854             (prefix_group_index, prefix_index);
855           for (i = 0; i < vec_len (apm->addresses); i++)
856             {
857               address_info = &apm->addresses[i];
858               if (address_info->prefix_group_index == prefix_group_index)
859                 {
860                   /* Add the prefix to the interface */
861                   cp_ip6_address_add_del_now (address_info, 1 /* add */ );
862                   /* And advertise the prefix on the interface */
863                   cp_ip6_advertise_prefix (prefix, address_info,
864                                            1 /* enable */ );
865                 }
866             }
867         }
868     }
869   else
870     {
871       if (active_prefix_index_by_prefix_group_index_get
872           (prefix_group_index) == prefix_index)
873         {
874           for (i = 0; i < vec_len (apm->addresses); i++)
875             {
876               address_info = &apm->addresses[i];
877               if (address_info->prefix_group_index == prefix_group_index)
878                 {
879                   cp_ip6_advertise_prefix (prefix, address_info,
880                                            0 /* enable */ );
881                   cp_ip6_address_add_del_now (address_info, 0 /* del */ );
882                 }
883             }
884           active_prefix_index_by_prefix_group_index_set
885             (prefix_group_index, ~0);
886           new_prefix_index =
887             cp_ip6_address_find_new_active_prefix (prefix_group_index,
888                                                    prefix_index);
889           if (new_prefix_index != ~0)
890             {
891               active_prefix_index_by_prefix_group_index_set
892                 (prefix_group_index, new_prefix_index);
893               for (i = 0; i < vec_len (apm->addresses); i++)
894                 {
895                   address_info = &apm->addresses[i];
896                   if (address_info->prefix_group_index == prefix_group_index)
897                     {
898                       cp_ip6_address_add_del_now (address_info, 1 /* add */ );
899                       cp_ip6_advertise_prefix (prefix, address_info,
900                                                1 /* enable */ );
901                     }
902                 }
903             }
904         }
905     }
906 }
907
908 static u32
909 prefix_group_find_or_create (const u8 * name, u8 create)
910 {
911   ip6_prefix_main_t *pm = &ip6_prefix_main;
912   u32 free_index = ~0;
913   u8 *name_dup;
914   u32 i;
915
916   for (i = 0; i < vec_len (pm->prefix_group_name_by_index); i++)
917     {
918       if (pm->prefix_group_name_by_index[i] == 0)
919         free_index = i;
920       else if (0 ==
921                strcmp ((const char *) pm->prefix_group_name_by_index[i],
922                        (const char *) name))
923         return i;
924     }
925   if (!create)
926     return ~0;
927   name_dup = (u8 *) strdup ((const char *) name);
928   if (free_index != ~0)
929     {
930       pm->prefix_group_name_by_index[free_index] = name_dup;
931       return free_index;
932     }
933   else
934     {
935       vec_add1 (pm->prefix_group_name_by_index, name_dup);
936       return vec_len (pm->prefix_group_name_by_index) - 1;
937     }
938 }
939
940 int
941 dhcp6_cp_ip6_address_add_del (u32 sw_if_index, const u8 * prefix_group,
942                               ip6_address_t address, u8 prefix_length,
943                               u8 is_add)
944 {
945
946   ip6_address_with_prefix_main_t *apm = &ip6_address_with_prefix_main;
947   vnet_main_t *vnm = vnet_get_main ();
948   ip6_address_info_t *address_info;
949   u32 prefix_group_index;
950   u32 n;
951
952   if (!vnet_sw_interface_is_api_valid (vnm, sw_if_index))
953     {
954       clib_warning ("Invalid sw_if_index");
955       return VNET_API_ERROR_INVALID_VALUE;
956     }
957
958   if (prefix_group != 0 && prefix_group[0] != '\0')
959     {
960       if (strnlen ((const char *) prefix_group, 64) == 64)
961         return VNET_API_ERROR_INVALID_VALUE;
962
963       prefix_group_index = prefix_group_find_or_create (prefix_group, 1);
964     }
965   else
966     prefix_group_index = ~0;
967
968   n = vec_len (apm->addresses);
969
970   vec_foreach (address_info, apm->addresses)
971   {
972     if (address_info->sw_if_index == sw_if_index &&
973         address_info->prefix_group_index == prefix_group_index &&
974         address_info->prefix_length == prefix_length &&
975         0 == memcmp (&address_info->address, &address, 16))
976       {
977         if (is_add)
978           return VNET_API_ERROR_DUPLICATE_IF_ADDRESS;
979         cp_ip6_address_add_del_now (address_info, 0 /* del */ );
980         *address_info = apm->addresses[n - 1];
981         _vec_len (apm->addresses) = n - 1;
982         return 0;
983       }
984   }
985
986   if (!is_add)
987     return VNET_API_ERROR_ADDRESS_NOT_FOUND_FOR_INTERFACE;
988
989   vec_validate (apm->addresses, n);
990   address_info = &apm->addresses[n];
991   address_info->sw_if_index = sw_if_index;
992   address_info->prefix_group_index = prefix_group_index;
993   address_info->address = address;
994   address_info->prefix_length = prefix_length;
995   cp_ip6_address_add_del_now (address_info, 1 /* add */ );
996
997   return 0;
998 }
999
1000 static clib_error_t *
1001 cp_ip6_address_add_del_command_function (vlib_main_t * vm,
1002                                          unformat_input_t * input,
1003                                          vlib_cli_command_t * cmd)
1004 {
1005   vnet_main_t *vnm = vnet_get_main ();
1006   clib_error_t *error = 0;
1007   u32 sw_if_index = ~0;
1008   u8 *prefix_group = 0;
1009   ip6_address_t address;
1010   u32 prefix_length;
1011   u8 address_set = 0;
1012   u8 add = 1;
1013   unformat_input_t _line_input, *line_input = &_line_input;
1014
1015   if (!unformat_user (input, unformat_line_input, line_input))
1016     return 0;
1017
1018   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1019     {
1020       if (unformat
1021           (line_input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index));
1022       else if (unformat (line_input, "prefix group %s", &prefix_group));
1023       else
1024         if (unformat (line_input, "%U/%d", unformat_ip6_address,
1025                       &address, &prefix_length))
1026         address_set = 1;
1027       else if (unformat (line_input, "del"))
1028         add = 0;
1029       else
1030         {
1031           error = clib_error_return (0, "unexpected input `%U'",
1032                                      format_unformat_error, line_input);
1033           unformat_free (line_input);
1034           goto done;
1035         }
1036     }
1037
1038   unformat_free (line_input);
1039
1040   if (sw_if_index == ~0)
1041     error = clib_error_return (0, "Missing sw_if_index");
1042   else if (address_set == 0)
1043     error = clib_error_return (0, "Missing address");
1044   else
1045     {
1046       if (dhcp6_cp_ip6_address_add_del
1047           (sw_if_index, prefix_group, address, prefix_length, add) != 0)
1048         error = clib_error_return (0, "Error adding or removing address");
1049     }
1050
1051 done:
1052   return error;
1053 }
1054
1055 /*?
1056  * This command is used to add/delete IPv6 address
1057  * potentially using available prefix from specified prefix group
1058  *
1059  * @cliexpar
1060  * @parblock
1061  * Example of how to add IPv6 address:
1062  * @cliexcmd{set ip6 address GigabitEthernet2/0/0
1063  *           prefix group my-prefix-group ::7/64}
1064  * Example of how to delete IPv6 address:
1065  * @cliexcmd{set ip6 address GigabitEthernet2/0/0
1066  *           prefix group my-prefix-group ::7/64 del}
1067  * @endparblock
1068 ?*/
1069 /* *INDENT-OFF* */
1070 VLIB_CLI_COMMAND (ip6_address_add_del_command, static) = {
1071   .path = "set ip6 address",
1072   .short_help = "set ip6 address <interface> [prefix group <string>] "
1073                 "<address> [del]",
1074   .function = cp_ip6_address_add_del_command_function,
1075 };
1076 /* *INDENT-ON* */
1077
1078 static clib_error_t *
1079 cp_ip6_addresses_show_command_function (vlib_main_t * vm,
1080                                         unformat_input_t * input,
1081                                         vlib_cli_command_t * cmd)
1082 {
1083   ip6_address_with_prefix_main_t *apm = &ip6_address_with_prefix_main;
1084   ip6_prefix_main_t *pm = &ip6_prefix_main;
1085   ip6_address_info_t *address_info;
1086   const u8 *prefix_group;
1087   clib_error_t *error = 0;
1088   int i;
1089
1090   for (i = 0; i < vec_len (apm->addresses); i++)
1091     {
1092       address_info = &apm->addresses[i];
1093       if (address_info->prefix_group_index == ~0)
1094         prefix_group = (const u8 *) "NONE";
1095       else
1096         prefix_group =
1097           pm->prefix_group_name_by_index[address_info->prefix_group_index];
1098       vlib_cli_output (vm,
1099                        "sw_if_index: %u, prefix_group: %s, address: %U/%d",
1100                        address_info->sw_if_index, prefix_group,
1101                        format_ip6_address, &address_info->address,
1102                        address_info->prefix_length);
1103     }
1104
1105   return error;
1106 }
1107
1108 /* *INDENT-OFF* */
1109 VLIB_CLI_COMMAND (ip6_addresses_show_command, static) = {
1110   .path = "show ip6 addresses",
1111   .short_help = "show ip6 addresses",
1112   .function = cp_ip6_addresses_show_command_function,
1113 };
1114 /* *INDENT-ON* */
1115
1116 static clib_error_t *
1117 cp_ip6_prefixes_show_command_function (vlib_main_t * vm,
1118                                        unformat_input_t * input,
1119                                        vlib_cli_command_t * cmd)
1120 {
1121   ip6_prefix_main_t *pm = &ip6_prefix_main;
1122   clib_error_t *error = 0;
1123   prefix_info_t *prefix_info;
1124   const u8 *prefix_group;
1125   f64 current_time = vlib_time_now (vm);
1126
1127   /* *INDENT-OFF* */
1128   pool_foreach (prefix_info, pm->prefix_pool,
1129   ({
1130     prefix_group =
1131       pm->prefix_group_name_by_index[prefix_info->prefix_group_index];
1132     vlib_cli_output (vm, "opaque_data: %lu, prefix: %U/%d, prefix group: %s, "
1133                      "preferred lifetime: %u, valid lifetime: %u "
1134                      "(%f remaining)",
1135                      prefix_info->opaque_data, format_ip6_address,
1136                      &prefix_info->prefix, prefix_info->prefix_length,
1137                      prefix_group,
1138                      prefix_info->preferred_lt, prefix_info->valid_lt,
1139                      prefix_info->due_time - current_time);
1140   }));
1141   /* *INDENT-ON* */
1142
1143   return error;
1144 }
1145
1146 /* *INDENT-OFF* */
1147 VLIB_CLI_COMMAND (ip6_prefixes_show_command, static) = {
1148   .path = "show ip6 prefixes",
1149   .short_help = "show ip6 prefixes",
1150   .function = cp_ip6_prefixes_show_command_function,
1151 };
1152 /* *INDENT-ON* */
1153
1154 static clib_error_t *
1155 ip6_pd_clients_show_command_function (vlib_main_t * vm,
1156                                       unformat_input_t * input,
1157                                       vlib_cli_command_t * cmd)
1158 {
1159   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
1160   ip6_prefix_main_t *pm = &ip6_prefix_main;
1161   clib_error_t *error = 0;
1162   client_state_t *cs;
1163   f64 current_time = vlib_time_now (vm);
1164   const u8 *prefix_group;
1165   char buf1[256];
1166   char buf2[256];
1167   const char *rebinding;
1168   u32 i;
1169
1170   for (i = 0; i < vec_len (rm->client_state_by_sw_if_index); i++)
1171     {
1172       cs = &rm->client_state_by_sw_if_index[i];
1173       if (cs->enabled)
1174         {
1175           if (cs->T1_due_time != DBL_MAX && cs->T1_due_time > current_time)
1176             {
1177               sprintf (buf1, "%u remaining",
1178                        (u32) round (cs->T1_due_time - current_time));
1179             }
1180           else
1181             sprintf (buf1, "timeout");
1182           if (cs->T2_due_time != DBL_MAX && cs->T2_due_time > current_time)
1183             sprintf (buf2, "%u remaining",
1184                      (u32) round (cs->T2_due_time - current_time));
1185           else
1186             sprintf (buf2, "timeout");
1187           if (cs->rebinding)
1188             rebinding = ", REBINDING";
1189           else
1190             rebinding = "";
1191           prefix_group =
1192             pm->prefix_group_name_by_index[cs->prefix_group_index];
1193           if (cs->T1)
1194             vlib_cli_output (vm,
1195                              "sw_if_index: %u, prefix group: %s, T1: %u (%s), "
1196                              "T2: %u (%s), server index: %d%s", i,
1197                              prefix_group, cs->T1, buf1, cs->T2, buf2,
1198                              cs->server_index, rebinding);
1199           else
1200             vlib_cli_output (vm, "sw_if_index: %u, prefix group: %s%s", i,
1201                              prefix_group, rebinding);
1202         }
1203     }
1204
1205   return error;
1206 }
1207
1208 /* *INDENT-OFF* */
1209 VLIB_CLI_COMMAND (ip6_pd_clients_show_command, static) = {
1210   .path = "show ip6 pd clients",
1211   .short_help = "show ip6 pd clients",
1212   .function = ip6_pd_clients_show_command_function,
1213 };
1214 /* *INDENT-ON* */
1215
1216
1217
1218 int
1219 dhcp6_pd_client_enable_disable (u32 sw_if_index,
1220                                 const u8 * prefix_group, u8 enable)
1221 {
1222   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
1223   ip6_prefix_main_t *pm = &ip6_prefix_main;
1224   vnet_main_t *vnm = rm->vnet_main;
1225   client_state_t *client_state;
1226   static client_state_t empty_config = {
1227     0
1228   };
1229   prefix_info_t *prefix_info;
1230   prefix_info_t *prefix_list = 0;
1231   u32 prefix_group_index;
1232
1233   if (!vnet_sw_interface_is_api_valid (vnm, sw_if_index))
1234     {
1235       clib_warning ("Invalid sw_if_index");
1236       return VNET_API_ERROR_INVALID_VALUE;
1237     }
1238
1239   vec_validate_init_empty (rm->client_state_by_sw_if_index, sw_if_index,
1240                            empty_config);
1241   client_state = &rm->client_state_by_sw_if_index[sw_if_index];
1242
1243   u8 old_enabled = client_state->enabled;
1244
1245   if (enable)
1246     {
1247       if (strnlen ((const char *) prefix_group, 64) == 64
1248           || prefix_group[0] == '\0')
1249         return VNET_API_ERROR_INVALID_VALUE;
1250       prefix_group_index =
1251         prefix_group_find_or_create (prefix_group, !old_enabled);
1252       if (old_enabled
1253           && prefix_group_index != client_state->prefix_group_index)
1254         return VNET_API_ERROR_INVALID_VALUE;
1255     }
1256
1257   if (!old_enabled && enable)
1258     {
1259       client_state->enabled = 1;
1260       client_state->prefix_group_index = prefix_group_index;
1261       ASSERT (client_state->prefix_group_index != ~0);
1262       client_state->server_index = ~0;
1263
1264       rm->n_clients++;
1265       if (rm->n_clients == 1)
1266         {
1267           enable_process ();
1268           dhcp6_clients_enable_disable (1);
1269         }
1270
1271       ip6_enable (sw_if_index);
1272       send_client_message_start_stop (sw_if_index, ~0, DHCPV6_MSG_SOLICIT,
1273                                       0, 1);
1274     }
1275   else if (old_enabled && !enable)
1276     {
1277       send_client_message_start_stop (sw_if_index, ~0, ~0, 0, 0);
1278
1279       rm->n_clients--;
1280       if (rm->n_clients == 0)
1281         {
1282           dhcp6_clients_enable_disable (0);
1283           disable_process ();
1284         }
1285
1286       vec_validate (prefix_list, 0);
1287
1288       /* *INDENT-OFF* */
1289       pool_foreach (prefix_info, pm->prefix_pool,
1290       ({
1291         if (is_dhcpv6_pd_prefix (prefix_info) &&
1292             prefix_info->opaque_data == sw_if_index)
1293           {
1294             ASSERT (sw_if_index < vec_len (rm->client_state_by_sw_if_index) &&
1295                     rm->client_state_by_sw_if_index[sw_if_index].enabled);
1296             client_state_t *client_state =
1297               &rm->client_state_by_sw_if_index[sw_if_index];
1298             prefix_list[0] = *prefix_info;
1299             send_client_message_start_stop (sw_if_index,
1300                                             client_state->server_index,
1301                                             DHCPV6_MSG_RELEASE, prefix_list,
1302                                             1);
1303             u32 prefix_index = prefix_info - pm->prefix_pool;
1304             notify_prefix_add_del (prefix_index, 0);
1305             set_is_dhcpv6_pd_prefix (prefix_info, 0);
1306             pool_put (pm->prefix_pool, prefix_info);
1307           }
1308       }));
1309       /* *INDENT-ON* */
1310
1311       vec_free (prefix_list);
1312
1313       clib_memset (client_state, 0, sizeof (*client_state));
1314     }
1315
1316   return 0;
1317 }
1318
1319 static clib_error_t *
1320 dhcp6_pd_client_enable_disable_command_fn (vlib_main_t *
1321                                            vm,
1322                                            unformat_input_t
1323                                            * input, vlib_cli_command_t * cmd)
1324 {
1325   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
1326   vnet_main_t *vnm = rm->vnet_main;
1327   clib_error_t *error = 0;
1328   u8 *prefix_group = 0;
1329   u32 sw_if_index = ~0;
1330   u8 enable = 1;
1331   unformat_input_t _line_input, *line_input = &_line_input;
1332
1333   if (!unformat_user (input, unformat_line_input, line_input))
1334     return 0;
1335
1336   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1337     {
1338       if (unformat
1339           (line_input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
1340         ;
1341       else if (unformat (line_input, "prefix group %s", &prefix_group));
1342       else if (unformat (line_input, "disable"))
1343         enable = 0;
1344       else
1345         {
1346           error = clib_error_return (0, "unexpected input `%U'",
1347                                      format_unformat_error, line_input);
1348           goto done;
1349         }
1350     }
1351
1352   if (prefix_group == 0 && enable)
1353     error = clib_error_return (0, "Prefix group must be set when enabling");
1354   else if (sw_if_index != ~0)
1355     {
1356       if (dhcp6_pd_client_enable_disable (sw_if_index, prefix_group, enable)
1357           != 0)
1358         error = clib_error_return (0, "Invalid sw_if_index or prefix group");
1359     }
1360   else
1361     error = clib_error_return (0, "Missing sw_if_index");
1362
1363 done:
1364   vec_free (prefix_group);
1365   unformat_free (line_input);
1366
1367   return error;
1368 }
1369
1370 /*?
1371  * This command is used to enable/disable DHCPv6 PD client
1372  * on particular interface.
1373  *
1374  * @cliexpar
1375  * @parblock
1376  * Example of how to enable DHCPv6 PD client:
1377  * @cliexcmd{dhcp6 pd client GigabitEthernet2/0/0 prefix group my-pd-group}
1378  * Example of how to disable DHCPv6 PD client:
1379  * @cliexcmd{dhcp6 pd client GigabitEthernet2/0/0 disable}
1380  * @endparblock
1381 ?*/
1382 /* *INDENT-OFF* */
1383 VLIB_CLI_COMMAND (dhcp6_pd_client_enable_disable_command, static) = {
1384   .path = "dhcp6 pd client",
1385   .short_help = "dhcp6 pd client <interface> (prefix group <string> | disable)",
1386   .function = dhcp6_pd_client_enable_disable_command_fn,
1387 };
1388 /* *INDENT-ON* */
1389
1390 static clib_error_t *
1391 dhcp_pd_client_cp_init (vlib_main_t * vm)
1392 {
1393   dhcp6_pd_client_cp_main_t *rm = &dhcp6_pd_client_cp_main;
1394
1395   rm->vlib_main = vm;
1396   rm->vnet_main = vnet_get_main ();
1397   rm->api_main = vlibapi_get_main ();
1398   rm->node_index = dhcp6_pd_client_cp_process_node.index;
1399
1400   return (NULL);
1401 }
1402
1403 VLIB_INIT_FUNCTION (dhcp_pd_client_cp_init);
1404
1405 /*
1406  * fd.io coding-style-patch-verification: ON
1407  *
1408  * Local Variables:
1409  * eval: (c-set-style "gnu")
1410  * End:
1411  */