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