Line data Source code
1 : /*
2 : *------------------------------------------------------------------
3 : * ip_api.c - vnet ip api
4 : *
5 : * Copyright (c) 2016 Cisco and/or its affiliates.
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at:
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : *------------------------------------------------------------------
18 : */
19 :
20 : #include <stddef.h>
21 :
22 : #include <vnet/ip6-nd/ip6_nd.h>
23 : #include <vnet/ip6-nd/ip6_ra.h>
24 :
25 : #include <vnet/fib/fib_table.h>
26 : #include <vnet/ip/ip_types_api.h>
27 :
28 : #include <vpp/app/version.h>
29 :
30 : #include <vlibapi/api.h>
31 : #include <vlibmemory/api.h>
32 :
33 : /* define message IDs */
34 : #include <vnet/format_fns.h>
35 : #include <vnet/ip6-nd/ip6_nd.api_enum.h>
36 : #include <vnet/ip6-nd/ip6_nd.api_types.h>
37 :
38 : /**
39 : * Base message ID fot the plugin
40 : */
41 : static u32 ip6_nd_base_msg_id;
42 : #define REPLY_MSG_ID_BASE ip6_nd_base_msg_id
43 :
44 : #include <vlibapi/api_helper_macros.h>
45 :
46 : static void
47 0 : send_ip6nd_proxy_details (vl_api_registration_t * reg,
48 : u32 context,
49 : const ip46_address_t * addr, u32 sw_if_index)
50 : {
51 : vl_api_ip6nd_proxy_details_t *mp;
52 :
53 0 : mp = vl_msg_api_alloc (sizeof (*mp));
54 0 : clib_memset (mp, 0, sizeof (*mp));
55 0 : mp->_vl_msg_id = ntohs (VL_API_IP6ND_PROXY_DETAILS);
56 0 : mp->context = context;
57 0 : mp->sw_if_index = htonl (sw_if_index);
58 :
59 0 : ip6_address_encode (&addr->ip6, mp->ip);
60 :
61 0 : vl_api_send_msg (reg, (u8 *) mp);
62 0 : }
63 :
64 : typedef struct api_ip6nd_proxy_fib_table_walk_ctx_t_
65 : {
66 : u32 *indices;
67 : } api_ip6nd_proxy_fib_table_walk_ctx_t;
68 :
69 : static fib_table_walk_rc_t
70 0 : api_ip6nd_proxy_fib_table_walk (fib_node_index_t fei, void *arg)
71 : {
72 0 : api_ip6nd_proxy_fib_table_walk_ctx_t *ctx = arg;
73 :
74 0 : if (fib_entry_is_sourced (fei, FIB_SOURCE_IP6_ND_PROXY))
75 : {
76 0 : vec_add1 (ctx->indices, fei);
77 : }
78 :
79 0 : return (FIB_TABLE_WALK_CONTINUE);
80 : }
81 :
82 : static void
83 0 : vl_api_ip6nd_proxy_dump_t_handler (vl_api_ip6nd_proxy_dump_t * mp)
84 : {
85 0 : ip6_main_t *im6 = &ip6_main;
86 : u32 fib_index;
87 0 : api_ip6nd_proxy_fib_table_walk_ctx_t ctx = {
88 : .indices = NULL,
89 : };
90 : fib_node_index_t *feip;
91 : const fib_prefix_t *pfx;
92 : vl_api_registration_t *reg;
93 :
94 0 : reg = vl_api_client_index_to_registration (mp->client_index);
95 0 : if (!reg)
96 0 : return;
97 :
98 : /* *INDENT-OFF* */
99 0 : pool_foreach_index (fib_index, im6->fibs)
100 : {
101 0 : fib_table_walk (fib_index, FIB_PROTOCOL_IP6,
102 : api_ip6nd_proxy_fib_table_walk, &ctx);
103 : }
104 : /* *INDENT-ON* */
105 :
106 0 : vec_sort_with_function (ctx.indices, fib_entry_cmp_for_sort);
107 :
108 0 : vec_foreach (feip, ctx.indices)
109 : {
110 0 : pfx = fib_entry_get_prefix (*feip);
111 :
112 0 : send_ip6nd_proxy_details (reg,
113 : mp->context,
114 : &pfx->fp_addr,
115 : fib_entry_get_resolving_interface (*feip));
116 : }
117 :
118 0 : vec_free (ctx.indices);
119 : }
120 :
121 : static void
122 2 : vl_api_ip6nd_proxy_enable_disable_t_handler (
123 : vl_api_ip6nd_proxy_enable_disable_t *mp)
124 : {
125 : vl_api_ip6nd_proxy_enable_disable_reply_t *rmp;
126 2 : int rv = 0;
127 :
128 2 : VALIDATE_SW_IF_INDEX (mp);
129 :
130 2 : if (mp->is_enable)
131 1 : rv = ip6_nd_proxy_enable_disable (ntohl (mp->sw_if_index), 1);
132 : else
133 1 : rv = ip6_nd_proxy_enable_disable (ntohl (mp->sw_if_index), 0);
134 :
135 2 : BAD_SW_IF_INDEX_LABEL;
136 2 : REPLY_MACRO (VL_API_IP6ND_PROXY_ENABLE_DISABLE_REPLY);
137 : }
138 :
139 : static void
140 4 : vl_api_ip6nd_proxy_add_del_t_handler (vl_api_ip6nd_proxy_add_del_t * mp)
141 : {
142 : vl_api_ip6nd_proxy_add_del_reply_t *rmp;
143 : ip6_address_t ip6;
144 4 : int rv = 0;
145 :
146 4 : VALIDATE_SW_IF_INDEX (mp);
147 :
148 4 : ip6_address_decode (mp->ip, &ip6);
149 4 : if (mp->is_add)
150 2 : rv = ip6_nd_proxy_add (ntohl (mp->sw_if_index), &ip6);
151 : else
152 2 : rv = ip6_nd_proxy_del (ntohl (mp->sw_if_index), &ip6);
153 :
154 4 : BAD_SW_IF_INDEX_LABEL;
155 4 : REPLY_MACRO (VL_API_IP6ND_PROXY_ADD_DEL_REPLY);
156 : }
157 :
158 : static void
159 183 : vl_api_sw_interface_ip6nd_ra_config_t_handler
160 : (vl_api_sw_interface_ip6nd_ra_config_t * mp)
161 : {
162 : vl_api_sw_interface_ip6nd_ra_config_reply_t *rmp;
163 183 : vlib_main_t *vm = vlib_get_main ();
164 183 : int rv = 0;
165 : u8 is_no, suppress, managed, other, ll_option, send_unicast, cease,
166 : default_router;
167 :
168 183 : is_no = mp->is_no == 1;
169 183 : suppress = mp->suppress == 1;
170 183 : managed = mp->managed == 1;
171 183 : other = mp->other == 1;
172 183 : ll_option = mp->ll_option == 1;
173 183 : send_unicast = mp->send_unicast == 1;
174 183 : cease = mp->cease == 1;
175 183 : default_router = mp->default_router == 1;
176 :
177 183 : VALIDATE_SW_IF_INDEX (mp);
178 :
179 183 : rv = ip6_ra_config (vm, ntohl (mp->sw_if_index),
180 : suppress, managed, other,
181 : ll_option, send_unicast, cease,
182 : default_router, ntohl (mp->lifetime),
183 : ntohl (mp->initial_count),
184 : ntohl (mp->initial_interval),
185 : ntohl (mp->max_interval),
186 : ntohl (mp->min_interval), is_no);
187 :
188 183 : BAD_SW_IF_INDEX_LABEL;
189 :
190 183 : REPLY_MACRO (VL_API_SW_INTERFACE_IP6ND_RA_CONFIG_REPLY);
191 : }
192 :
193 : static void
194 13 : vl_api_sw_interface_ip6nd_ra_prefix_t_handler
195 : (vl_api_sw_interface_ip6nd_ra_prefix_t * mp)
196 : {
197 13 : vlib_main_t *vm = vlib_get_main ();
198 : vl_api_sw_interface_ip6nd_ra_prefix_reply_t *rmp;
199 : fib_prefix_t pfx;
200 13 : int rv = 0;
201 : u8 is_no, use_default, no_advertise, off_link, no_autoconfig, no_onlink;
202 :
203 13 : VALIDATE_SW_IF_INDEX (mp);
204 :
205 13 : ip_prefix_decode (&mp->prefix, &pfx);
206 13 : is_no = mp->is_no == 1;
207 13 : use_default = mp->use_default == 1;
208 13 : no_advertise = mp->no_advertise == 1;
209 13 : off_link = mp->off_link == 1;
210 13 : no_autoconfig = mp->no_autoconfig == 1;
211 13 : no_onlink = mp->no_onlink == 1;
212 :
213 13 : rv = ip6_ra_prefix (vm, ntohl (mp->sw_if_index),
214 : &pfx.fp_addr.ip6,
215 13 : pfx.fp_len, use_default,
216 : ntohl (mp->val_lifetime),
217 : ntohl (mp->pref_lifetime), no_advertise,
218 : off_link, no_autoconfig, no_onlink, is_no);
219 :
220 13 : BAD_SW_IF_INDEX_LABEL;
221 13 : REPLY_MACRO (VL_API_SW_INTERFACE_IP6ND_RA_PREFIX_REPLY);
222 : }
223 :
224 : static void
225 2 : ip6_radv_prefix_encode (f64 now, const ip6_radv_prefix_t *in,
226 : vl_api_ip6nd_ra_prefix_t *out)
227 : {
228 2 : fib_prefix_t in_ip6_pfx = {
229 : .fp_addr = {
230 : .ip6 = in->prefix,
231 : },
232 2 : .fp_len = in->prefix_len,
233 : .fp_proto = FIB_PROTOCOL_IP6,
234 : };
235 :
236 2 : ip_prefix_encode (&in_ip6_pfx, &out->prefix);
237 :
238 2 : out->onlink_flag = in->adv_on_link_flag;
239 2 : out->autonomous_flag = in->adv_autonomous_flag;
240 2 : out->val_lifetime = htonl (in->adv_valid_lifetime_in_secs);
241 2 : out->pref_lifetime = htonl (in->adv_pref_lifetime_in_secs);
242 :
243 2 : if (in->adv_valid_lifetime_in_secs != ~0)
244 : {
245 0 : out->valid_lifetime_expires =
246 0 : clib_host_to_net_f64 (in->valid_lifetime_expires - now);
247 : }
248 :
249 2 : if (in->adv_pref_lifetime_in_secs != ~0)
250 : {
251 0 : out->pref_lifetime_expires =
252 0 : clib_host_to_net_f64 (in->pref_lifetime_expires - now);
253 : }
254 :
255 2 : out->decrement_lifetime_flag = in->decrement_lifetime_flag;
256 2 : out->no_advertise = (in->enabled == 0);
257 2 : }
258 :
259 : static void
260 7 : send_sw_interface_ip6nd_ra_details (vl_api_registration_t *reg, u32 context,
261 : ip6_ra_t *radv_info)
262 : {
263 7 : vl_api_sw_interface_ip6nd_ra_details_t *rmp = 0;
264 : vl_api_ip6nd_ra_prefix_t *api_radv_pfx;
265 7 : u32 n_prefixes = pool_elts (radv_info->adv_prefixes_pool);
266 : ip6_radv_prefix_t *radv_pfx;
267 7 : u32 msg_size = sizeof (*rmp) + n_prefixes * sizeof (*api_radv_pfx);
268 7 : vlib_main_t *vm = vlib_get_main ();
269 7 : f64 now = vlib_time_now (vm);
270 :
271 7 : rmp = vl_msg_api_alloc (msg_size);
272 7 : if (!rmp)
273 0 : return;
274 7 : clib_memset (rmp, 0, msg_size);
275 7 : rmp->_vl_msg_id =
276 7 : ntohs (VL_API_SW_INTERFACE_IP6ND_RA_DETAILS + REPLY_MSG_ID_BASE);
277 7 : rmp->context = context;
278 :
279 7 : rmp->sw_if_index = htonl (radv_info->sw_if_index);
280 7 : rmp->cur_hop_limit = radv_info->curr_hop_limit;
281 7 : rmp->adv_managed_flag = radv_info->adv_managed_flag;
282 7 : rmp->adv_other_flag = radv_info->adv_other_flag;
283 7 : rmp->adv_router_lifetime = htons (radv_info->adv_router_lifetime_in_sec);
284 7 : rmp->adv_neighbor_reachable_time =
285 7 : htonl (radv_info->adv_neighbor_reachable_time_in_msec);
286 7 : rmp->adv_retransmit_interval = htonl (
287 : radv_info->adv_time_in_msec_between_retransmitted_neighbor_solicitations);
288 7 : rmp->adv_link_mtu = htonl (radv_info->adv_link_mtu);
289 7 : rmp->send_radv = radv_info->send_radv;
290 7 : rmp->cease_radv = radv_info->cease_radv;
291 7 : rmp->send_unicast = radv_info->send_unicast;
292 7 : rmp->adv_link_layer_address = radv_info->adv_link_layer_address;
293 7 : rmp->max_radv_interval = clib_host_to_net_f64 (radv_info->max_radv_interval);
294 7 : rmp->min_radv_interval = clib_host_to_net_f64 (radv_info->min_radv_interval);
295 :
296 7 : if (radv_info->last_radv_time > 0.0)
297 : {
298 0 : rmp->last_radv_time =
299 0 : clib_host_to_net_f64 (now - radv_info->last_radv_time);
300 : }
301 :
302 7 : if ((radv_info->next_multicast_time - radv_info->last_multicast_time) > 0.0)
303 : {
304 1 : rmp->last_multicast_time =
305 1 : clib_host_to_net_f64 (now - radv_info->last_multicast_time);
306 1 : rmp->next_multicast_time =
307 1 : clib_host_to_net_f64 (radv_info->next_multicast_time - now);
308 : }
309 :
310 7 : rmp->initial_adverts_count = htonl (radv_info->initial_adverts_count);
311 7 : rmp->initial_adverts_interval =
312 7 : clib_host_to_net_f64 (radv_info->initial_adverts_interval);
313 7 : rmp->initial_adverts_sent = (radv_info->initial_adverts_sent == 0);
314 7 : rmp->n_advertisements_sent = htonl (radv_info->n_advertisements_sent);
315 7 : rmp->n_solicitations_rcvd = htonl (radv_info->n_solicitations_rcvd);
316 7 : rmp->n_solicitations_dropped = htonl (radv_info->n_solicitations_dropped);
317 7 : rmp->n_prefixes = htonl (n_prefixes);
318 :
319 7 : api_radv_pfx = rmp->prefixes;
320 9 : pool_foreach (radv_pfx, radv_info->adv_prefixes_pool)
321 : {
322 2 : ip6_radv_prefix_encode (now, radv_pfx, api_radv_pfx);
323 :
324 2 : api_radv_pfx++;
325 : }
326 :
327 7 : vl_api_send_msg (reg, (u8 *) rmp);
328 : }
329 :
330 : typedef struct
331 : {
332 : u32 *sw_if_indices;
333 : } api_dump_ip6_ra_itf_walk_ctx_t;
334 :
335 : static walk_rc_t
336 5 : api_dump_ip6_ra_itf_walk_fn (u32 sw_if_index, void *arg)
337 : {
338 5 : api_dump_ip6_ra_itf_walk_ctx_t *ctx = arg;
339 :
340 5 : vec_add1 (ctx->sw_if_indices, sw_if_index);
341 :
342 5 : return (WALK_CONTINUE);
343 : }
344 :
345 : static void
346 3 : vl_api_sw_interface_ip6nd_ra_dump_t_handler (
347 : vl_api_sw_interface_ip6nd_ra_dump_t *mp)
348 : {
349 : vl_api_registration_t *reg;
350 : u32 sw_if_index;
351 : ip6_ra_t *radv_info;
352 :
353 3 : reg = vl_api_client_index_to_registration (mp->client_index);
354 3 : if (!reg)
355 0 : return;
356 :
357 3 : sw_if_index = ntohl (mp->sw_if_index);
358 :
359 3 : if (sw_if_index == INDEX_INVALID)
360 : {
361 : /* dump all interfaces */
362 :
363 1 : api_dump_ip6_ra_itf_walk_ctx_t ctx = {
364 : .sw_if_indices = NULL,
365 : };
366 : u32 *sw_if_i;
367 :
368 1 : ip6_ra_itf_walk (api_dump_ip6_ra_itf_walk_fn, &ctx);
369 :
370 6 : vec_foreach (sw_if_i, ctx.sw_if_indices)
371 : {
372 5 : radv_info = ip6_ra_get_itf (*sw_if_i);
373 5 : if (radv_info != NULL)
374 : {
375 5 : send_sw_interface_ip6nd_ra_details (reg, mp->context, radv_info);
376 : }
377 : }
378 :
379 1 : vec_free (ctx.sw_if_indices);
380 : }
381 : else
382 : {
383 : /* dump a single interface */
384 :
385 2 : radv_info = ip6_ra_get_itf (sw_if_index);
386 2 : if (radv_info != NULL)
387 : {
388 2 : send_sw_interface_ip6nd_ra_details (reg, mp->context, radv_info);
389 : }
390 : }
391 : }
392 :
393 : static void
394 1 : vl_api_ip6nd_send_router_solicitation_t_handler
395 : (vl_api_ip6nd_send_router_solicitation_t * mp)
396 : {
397 : vl_api_ip6nd_send_router_solicitation_reply_t *rmp;
398 : icmp6_send_router_solicitation_params_t params;
399 1 : vlib_main_t *vm = vlib_get_main ();
400 1 : int rv = 0;
401 :
402 1 : VALIDATE_SW_IF_INDEX (mp);
403 :
404 1 : BAD_SW_IF_INDEX_LABEL;
405 1 : REPLY_MACRO (VL_API_IP6ND_SEND_ROUTER_SOLICITATION_REPLY);
406 :
407 1 : if (rv != 0)
408 0 : return;
409 :
410 1 : params.irt = ntohl (mp->irt);
411 1 : params.mrt = ntohl (mp->mrt);
412 1 : params.mrc = ntohl (mp->mrc);
413 1 : params.mrd = ntohl (mp->mrd);
414 :
415 1 : icmp6_send_router_solicitation (vm, ntohl (mp->sw_if_index), mp->stop,
416 : ¶ms);
417 : }
418 :
419 : static void
420 1 : ip6_ra_handle_report (const ip6_ra_report_t * rap)
421 : {
422 : /* *INDENT-OFF* */
423 : vpe_client_registration_t *rp;
424 :
425 2 : pool_foreach (rp, vpe_api_main.ip6_ra_events_registrations)
426 : {
427 : vl_api_registration_t *vl_reg;
428 :
429 1 : vl_reg = vl_api_client_index_to_registration (rp->client_index);
430 :
431 1 : if (vl_reg && vl_api_can_send_msg (vl_reg))
432 : {
433 : vl_api_ip6_ra_prefix_info_t *prefix;
434 : vl_api_ip6_ra_event_t *event;
435 :
436 0 : u32 event_size = (sizeof (vl_api_ip6_ra_event_t) +
437 1 : vec_len (rap->prefixes) *
438 : sizeof (vl_api_ip6_ra_prefix_info_t));
439 1 : event = vl_msg_api_alloc_zero (event_size);
440 :
441 1 : event->_vl_msg_id = htons (VL_API_IP6_RA_EVENT + REPLY_MSG_ID_BASE);
442 1 : event->client_index = rp->client_index;
443 1 : event->pid = rp->client_pid;
444 1 : event->sw_if_index = clib_host_to_net_u32 (rap->sw_if_index);
445 :
446 1 : ip6_address_encode (&rap->router_address,
447 1 : event->router_addr);
448 :
449 1 : event->current_hop_limit = rap->current_hop_limit;
450 1 : event->flags = rap->flags;
451 1 : event->router_lifetime_in_sec =
452 1 : clib_host_to_net_u16 (rap->router_lifetime_in_sec);
453 1 : event->neighbor_reachable_time_in_msec =
454 1 : clib_host_to_net_u32 (rap->neighbor_reachable_time_in_msec);
455 1 : event->time_in_msec_between_retransmitted_neighbor_solicitations =
456 1 : clib_host_to_net_u32 (rap->time_in_msec_between_retransmitted_neighbor_solicitations);
457 1 : event->n_prefixes = clib_host_to_net_u32 (vec_len (rap->prefixes));
458 :
459 1 : prefix = event->prefixes;
460 : // (typeof (prefix)) event->prefixes;
461 : u32 j;
462 3 : for (j = 0; j < vec_len (rap->prefixes); j++)
463 : {
464 2 : ra_report_prefix_info_t *info = &rap->prefixes[j];
465 2 : ip_prefix_encode(&info->prefix, &prefix->prefix);
466 2 : prefix->flags = info->flags;
467 2 : prefix->valid_time = clib_host_to_net_u32 (info->valid_time);
468 2 : prefix->preferred_time =
469 2 : clib_host_to_net_u32 (info->preferred_time);
470 2 : prefix++;
471 : }
472 :
473 1 : vl_api_send_msg (vl_reg, (u8 *) event);
474 : }
475 : }
476 : /* *INDENT-ON* */
477 1 : }
478 :
479 : static void
480 1 : vl_api_want_ip6_ra_events_t_handler (vl_api_want_ip6_ra_events_t * mp)
481 : {
482 1 : vpe_api_main_t *am = &vpe_api_main;
483 : vl_api_want_ip6_ra_events_reply_t *rmp;
484 1 : int rv = 0, had_reg, have_reg;
485 :
486 1 : had_reg = hash_elts (am->ip6_ra_events_registration_hash);
487 1 : uword *p = hash_get (am->ip6_ra_events_registration_hash, mp->client_index);
488 : vpe_client_registration_t *rp;
489 1 : if (p)
490 : {
491 0 : if (mp->enable)
492 : {
493 0 : clib_warning ("pid %d: already enabled...", ntohl (mp->pid));
494 0 : rv = VNET_API_ERROR_INVALID_REGISTRATION;
495 0 : goto reply;
496 : }
497 : else
498 : {
499 0 : rp = pool_elt_at_index (am->ip6_ra_events_registrations, p[0]);
500 0 : pool_put (am->ip6_ra_events_registrations, rp);
501 0 : hash_unset (am->ip6_ra_events_registration_hash, mp->client_index);
502 0 : goto reply;
503 : }
504 : }
505 1 : if (mp->enable == 0)
506 : {
507 0 : clib_warning ("pid %d: already disabled...", ntohl (mp->pid));
508 0 : rv = VNET_API_ERROR_INVALID_REGISTRATION;
509 0 : goto reply;
510 : }
511 1 : pool_get (am->ip6_ra_events_registrations, rp);
512 1 : rp->client_index = mp->client_index;
513 1 : rp->client_pid = ntohl (mp->pid);
514 1 : hash_set (am->ip6_ra_events_registration_hash, rp->client_index,
515 : rp - am->ip6_ra_events_registrations);
516 :
517 1 : reply:
518 1 : have_reg = hash_elts (am->ip6_ra_events_registration_hash);
519 :
520 1 : if (!had_reg && have_reg)
521 1 : ip6_ra_report_register (ip6_ra_handle_report);
522 0 : else if (had_reg && !have_reg)
523 0 : ip6_ra_report_unregister (ip6_ra_handle_report);
524 :
525 1 : REPLY_MACRO (VL_API_WANT_IP6_RA_EVENTS_REPLY);
526 : }
527 :
528 : static clib_error_t *
529 1241 : want_ip6_ra_events_reaper (u32 client_index)
530 : {
531 1241 : vpe_api_main_t *am = &vpe_api_main;
532 : vpe_client_registration_t *rp;
533 : uword *p;
534 :
535 1241 : p = hash_get (am->ip6_ra_events_registration_hash, client_index);
536 :
537 1241 : if (p)
538 : {
539 0 : rp = pool_elt_at_index (am->ip6_ra_events_registrations, p[0]);
540 0 : pool_put (am->ip6_ra_events_registrations, rp);
541 0 : hash_unset (am->ip6_ra_events_registration_hash, client_index);
542 : }
543 1241 : return (NULL);
544 : }
545 :
546 575 : VL_MSG_API_REAPER_FUNCTION (want_ip6_ra_events_reaper);
547 :
548 : #include <vnet/ip6-nd/ip6_nd.api.c>
549 :
550 : static clib_error_t *
551 575 : ip6_nd_api_init (vlib_main_t * vm)
552 : {
553 : /* Ask for a correctly-sized block of API message decode slots */
554 575 : ip6_nd_base_msg_id = setup_message_id_table ();
555 :
556 575 : return 0;
557 : }
558 :
559 97919 : VLIB_INIT_FUNCTION (ip6_nd_api_init);
560 :
561 : /*
562 : * fd.io coding-style-patch-verification: ON
563 : *
564 : * Local Variables:
565 : * eval: (c-set-style "gnu")
566 : * End:
567 : */
|