Line data Source code
1 : /*
2 : * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include <cstdarg>
6 : #include "task_map.h"
7 : #include "oper/multicast.h"
8 :
9 : #ifdef __cplusplus
10 : extern "C" {
11 : #endif
12 : #include "mcast_common.h"
13 :
14 : #include "task_map.h"
15 : #include "task_thread_api.h"
16 : #include "patricia_api.h"
17 :
18 : #include "gmp.h"
19 : #include "gmpx_environment.h"
20 : #include "gmp_router.h"
21 : #include "igmp_private.h"
22 : #include "igmp_protocol.h"
23 : #include "gmp_externs.h"
24 :
25 : #include "gmp_intf.h"
26 : #include "gmp_map.h"
27 : #include "gmp_private.h"
28 : #include "gmp_trace.h"
29 : #include "gmpr_trace.h"
30 :
31 : #define MAXSTRINGSIZE_1 1000
32 :
33 : extern void gmpx_trace(void *context, const char *fmt, ...);
34 :
35 : extern void gmp_set_def_igmp_version(uint32_t version);
36 : extern void gmp_set_def_ipv4_ivl_params(uint32_t robust_count, uint32_t qivl,
37 : uint32_t qrivl, uint32_t lmqi);
38 : extern void gmp_set_def_intf_params(mc_af mcast_af);
39 : #ifdef __cplusplus
40 : }
41 : #endif
42 :
43 : mgm_global_data mgm_global[MCAST_AF_MAX];
44 : gmpr_intf_params def_gmpr_intf_params[MCAST_AF_MAX];
45 :
46 : static gmpr_instance_context gmp_inst_ctx = {
47 : gmp_oif_map_cb, // rctx_oif_map_cb
48 : gmp_policy_cb, // rctx_policy_cb
49 : gmp_ssm_check_cb, // rctx_ssm_check_cb
50 : };
51 :
52 : static gmpr_client_context igmp_client_context = {
53 : igmp_notification_ready, // rctx_notif_cb
54 : igmp_host_notification_ready, // rctx_host_notif_cb
55 : mgm_querier_change, // rctx_querier_cb
56 : TRUE, // rctx_delta_notifications
57 : FALSE, // rctx_full_notifications
58 : };
59 :
60 : // Value to be used for IGMP Version during init of GMP.
61 0 : void gmp_set_def_igmp_version(uint32_t version)
62 : {
63 0 : gmpr_intf_params *params = NULL;
64 :
65 0 : params = &def_gmpr_intf_params[MCAST_AF_IPV4];
66 0 : params->gmpr_ifparm_version = version;
67 0 : }
68 :
69 : // Default values to use for IGMP for querier related timer intervals in
70 : // GMP code.
71 1 : void gmp_set_def_ipv4_ivl_params(uint32_t robust_count, uint32_t qivl,
72 : uint32_t qrivl, uint32_t lmqi)
73 : {
74 1 : gmpr_intf_params *params = NULL;
75 :
76 1 : params = &def_gmpr_intf_params[MCAST_AF_IPV4];
77 1 : params->gmpr_ifparm_robustness = robust_count;
78 1 : params->gmpr_ifparm_qivl = qivl;
79 1 : params->gmpr_ifparm_qrivl = qrivl;
80 1 : params->gmpr_ifparm_lmqi = lmqi;
81 :
82 1 : return;
83 : }
84 :
85 : // Set of defaults for use by GMP as part of GMP initialization.
86 1 : void gmp_set_def_intf_params(mc_af mcast_af)
87 : {
88 1 : gmpr_intf_params *params = NULL;
89 :
90 1 : if (mcast_af == MCAST_AF_IPV4) {
91 :
92 1 : params = &def_gmpr_intf_params[mcast_af];
93 1 : params->gmpr_ifparm_version = IGMP_VERSION_3;
94 1 : gmp_set_def_ipv4_ivl_params(IGMP_ROBUST_COUNT, IGMP_QUERY_INTERVAL,
95 : IGMP_QUERY_RESPONSE_INTERVAL,
96 : IGMP_QUERY_LASTMEMBER_INTERVAL);
97 : } else {
98 0 : return;
99 : }
100 :
101 1 : params->gmpr_ifparm_fast_leave = FALSE;
102 1 : params->gmpr_ifparm_passive_receive = FALSE;
103 1 : params->gmpr_ifparm_suppress_gen_query = FALSE;
104 1 : params->gmpr_ifparm_suppress_gs_query = FALSE;
105 1 : params->gmpr_ifparm_chan_limit = 0;
106 1 : params->gmpr_ifparm_chan_threshold = 100;
107 1 : params->gmpr_ifparm_log_interval = 0;
108 :
109 1 : return;
110 : }
111 :
112 : // Create, initialization and return global data structure instance
113 : // that is used as context and mapping data structure with
114 : // GMP code.
115 1 : mgm_global_data *gmp_init(mc_af mcast_af, task *tp, void *gmp_sm)
116 : {
117 : gmp_proto proto;
118 : mgm_global_data *gd;
119 :
120 1 : if (mcast_af == MCAST_AF_IPV4) {
121 1 : proto = GMP_PROTO_IGMP;
122 : } else {
123 0 : return NULL;
124 : }
125 :
126 1 : gd = &mgm_global[mcast_af];
127 1 : gd->gmp_sm = gmp_sm;
128 1 : gd->tp = tp;
129 1 : gd->mgm_gd_af = mcast_af;
130 :
131 1 : gmp_register_io(GMP_ROLE_ROUTER, proto, gmp_xmit_ready);
132 1 : gmp_register_peek_function(GMP_ROLE_ROUTER, gmp_static_peek,
133 : gmp_static_peek);
134 :
135 1 : gd->mgm_gmpr_instance = gmpr_create_instance(proto, gd, &gmp_inst_ctx);
136 1 : gd->mgm_gmpr_client = gmpr_register(gd->mgm_gmpr_instance, gd,
137 : &igmp_client_context);
138 1 : gmpr_update_trace_flags(gd->mgm_gmpr_instance, 0);
139 1 : gmp_set_def_intf_params(mcast_af);
140 :
141 1 : return gd;
142 : }
143 :
144 : // Deinit and destroy data structure once the GMP module is
145 : // de-inited by the caller.
146 1 : void gmp_deinit(mc_af mcast_af)
147 : {
148 : mgm_global_data *gd;
149 :
150 1 : gd = &mgm_global[mcast_af];
151 1 : gmpr_detach(gd->mgm_gmpr_client);
152 1 : gmpr_destroy_instance(gd->mgm_gmpr_instance);
153 :
154 1 : gd->mgm_gmpr_client = NULL;
155 1 : gd->mgm_gmpr_instance = NULL;
156 :
157 1 : return;
158 : }
159 :
160 : // Set the interface params in the GMP.
161 3 : void gmp_set_intf_params(mgm_global_data *gd, gmp_intf *gif)
162 : {
163 : gmp_intf_handle *handle;
164 :
165 3 : if (gd->mgm_gd_af != MCAST_AF_IPV4) {
166 0 : return;
167 : }
168 :
169 3 : handle = gmp_gif_to_handle(gif);
170 3 : gmpr_set_intf_params(gd->mgm_gmpr_instance, handle, &gif->params);
171 3 : return;
172 : }
173 :
174 : // Attach an application interface context with GMP interface context.
175 : // application interface context is generally the either per-IPAM
176 : // gw or dns server in the context of the Agent.
177 3 : gmp_intf *gmp_attach_intf(mgm_global_data *gd, void *mif_state)
178 : {
179 3 : gmp_intf *gif = NULL;
180 :
181 3 : if (!gd) {
182 0 : return NULL;
183 : }
184 :
185 3 : if (gd->mgm_gd_af != MCAST_AF_IPV4) {
186 0 : return NULL;
187 : }
188 :
189 3 : gif = (gmp_intf *)malloc(sizeof(gmp_intf));
190 3 : if (!gif) {
191 0 : return NULL;
192 : }
193 :
194 3 : gif->vm_interface = mif_state;
195 3 : gif->gmpif_proto = GMP_PROTO_IGMP;
196 3 : gif->gmpif_handle.gmpifh_host = FALSE;
197 3 : std::memset(&gif->gmpif_handle.gmpifh_xmit_thread, 0, sizeof(task_thread));
198 3 : gmpr_attach_intf(gd->mgm_gmpr_instance, &gif->gmpif_handle);
199 3 : memcpy(&gif->params, &def_gmpr_intf_params[gd->mgm_gd_af],
200 : sizeof(gif->params));
201 3 : gmp_set_intf_params(gd, gif);
202 :
203 3 : return gif;
204 : }
205 :
206 : // Detach the interface created using above from the GMP.
207 3 : void gmp_detach_intf(mgm_global_data *gd, gmp_intf *gif)
208 : {
209 3 : if (!gd) {
210 0 : return;
211 : }
212 :
213 3 : gmpr_detach_intf(gd->mgm_gmpr_instance, &gif->gmpif_handle);
214 :
215 3 : free(gif);
216 :
217 3 : return;
218 : }
219 :
220 : // Update interface status - currently only IP Address.
221 6 : boolean gmp_update_intf_state(mgm_global_data *gd, gmp_intf *gif,
222 : const gmp_addr_string *intf_addr)
223 : {
224 6 : gmpr_update_intf_state(gd->mgm_gmpr_instance, gmp_gif_to_handle(gif),
225 : (const u_int8_t *)intf_addr);
226 6 : return TRUE;
227 : }
228 :
229 : // Update querying related params - currently query enable/disable.
230 0 : boolean gmp_update_intf_querying(mgm_global_data *gd, gmp_intf *gif,
231 : boolean query)
232 : {
233 0 : gif->params.gmpr_ifparm_suppress_gen_query = query;
234 0 : gif->params.gmpr_ifparm_suppress_gs_query = query;
235 0 : gmp_set_intf_params(gd, gif);
236 0 : return TRUE;
237 : }
238 :
239 : // Process the IGMP payload.
240 : // Preprocessed IP header by Agent packet handler.
241 0 : boolean gmp_process_pkt(mgm_global_data *gd, gmp_intf *gif,
242 : void *rcv_pkt, u_int32_t packet_len,
243 : const gmp_addr_string *src_addr,
244 : const gmp_addr_string *dst_addr)
245 : {
246 0 : boolean parse_ok = FALSE;
247 :
248 0 : if (gd->mgm_gd_af == MCAST_AF_IPV4) {
249 0 : parse_ok = igmp_process_pkt(rcv_pkt, src_addr->gmp_v4_addr,
250 0 : dst_addr->gmp_v4_addr, packet_len,
251 : gmp_gif_to_handle(gif), false, NULL, 0);
252 : }
253 :
254 0 : return parse_ok;
255 : }
256 :
257 0 : boolean gmp_oif_map_cb(void *inst_context UNUSED, gmp_intf_handle *handle,
258 : u_int8_t *group_addr, u_int8_t *source_addr,
259 : gmp_intf_handle **output_handle)
260 : {
261 0 : *output_handle = handle;
262 0 : return TRUE;
263 : }
264 :
265 : // Handle Multicast Policy configured by user.
266 : // Returns allow or deny the <S,G> at VN level.
267 0 : boolean gmp_policy_cb(void *inst_context, gmp_intf_handle *handle,
268 : u_int8_t *group_addr, u_int8_t *source_addr,
269 : boolean static_group)
270 : {
271 0 : mgm_global_data *gd = (mgm_global_data *)inst_context;
272 : gmp_intf *gif;
273 : gmp_addr_string g;
274 : gmp_addr_string s;
275 :
276 0 : if (!inst_context) {
277 0 : return FALSE;
278 : }
279 :
280 0 : gif = gmp_handle_to_gif(handle);
281 0 : if (!gif) {
282 0 : return FALSE;
283 : }
284 :
285 0 : if (static_group) {
286 0 : return FALSE;
287 : }
288 :
289 0 : if (!group_addr) {
290 0 : return FALSE;
291 : }
292 :
293 0 : memcpy(&g, group_addr, IPV4_ADDR_LEN);
294 0 : source_addr ? memcpy(&s, source_addr, IPV4_ADDR_LEN)
295 0 : : memset(&s, 0x00, IPV4_ADDR_LEN);
296 :
297 0 : return gmp_policy_check(gd, gif, s, g);
298 : }
299 :
300 0 : boolean gmp_ssm_check_cb(void *inst_context UNUSED, gmp_intf_handle *handle,
301 : u_int8_t *group_addr)
302 : {
303 0 : return TRUE;
304 : }
305 :
306 : // Notification handler for handling <S,G>
307 : // Values per-<S,G> can be adding, deleting or modifying <S,G>.
308 : // Modifying can be change <S> from allow to block and vice-versa.
309 0 : boolean gmp_client_notification(mgm_global_data *gd)
310 : {
311 0 : boolean pending = FALSE;
312 : gmpr_client_notification *notification;
313 : gmp_intf *gif;
314 : gmp_intf_handle *handle;
315 : gmp_addr_string g;
316 : gmp_addr_string s;
317 0 : int notif_count = IGMP_MAX_NOTIF_PER_PASS;
318 :
319 : /*
320 : * Pull notifications until we run out, hit our max count, or the
321 : * protocol is busy.
322 : *
323 : * NOTE: We might go over the max notifications per pass,
324 : * ie. notif_count might be -ve, because we need to make sure that
325 : * the notifications for the (s,g) of the same group are being
326 : * processed in the same batch.
327 : */
328 0 : notification = NULL;
329 0 : while (notif_count-- > 0 || !gmpr_notification_last_sg(notification)) {
330 : notification =
331 0 : gmpr_get_notification(gd->mgm_gmpr_client, notification);
332 0 : if (!notification)
333 0 : break;
334 :
335 : /*
336 : * Process received notification...
337 : */
338 0 : handle = notification->notif_intf_id;
339 0 : gif = gmp_handle_to_gif(handle);
340 :
341 0 : memcpy(&g, ¬ification->notif_group_addr, IPV4_ADDR_LEN);
342 0 : memcpy(&s, ¬ification->notif_source_addr, IPV4_ADDR_LEN);
343 :
344 0 : switch (notification->notif_type) {
345 0 : case GMPR_NOTIF_ALLOW_SOURCE:
346 0 : if (notification->notif_filter_mode ==
347 : GMP_FILTER_MODE_INCLUDE) {
348 0 : gmp_group_notify(gd, gif, MGM_GROUP_ADDED, s, g);
349 : } else {
350 0 : gmp_cache_resync_notify(gd, gif, s, g);
351 : }
352 0 : break;
353 :
354 0 : case GMPR_NOTIF_BLOCK_SOURCE:
355 0 : if (notification->notif_filter_mode ==
356 : GMP_FILTER_MODE_INCLUDE) {
357 0 : gmp_group_notify(gd, gif, MGM_GROUP_REMOVED, s, g);
358 : } else {
359 0 : gmp_cache_resync_notify(gd, gif, s, g);
360 : }
361 0 : break;
362 :
363 0 : case GMPR_NOTIF_GROUP_DELETE:
364 0 : gmp_group_notify(gd, gif, MGM_GROUP_SRC_REMOVED, s, g);
365 0 : gmp_group_notify(gd, gif, MGM_GROUP_REMOVED, s, g);
366 0 : break;
367 :
368 0 : case GMPR_NOTIF_GROUP_ADD_EXCL:
369 : /*
370 : * Check if PIM requested a refresh.
371 : * If so we don't send a group and source REMOVE
372 : * otherwise PIM will clear its state
373 : */
374 0 : if (gd->refresh_required == FALSE) {
375 0 : gmp_group_notify(gd, gif, MGM_GROUP_SRC_REMOVED, s, g);
376 : }
377 0 : gmp_group_notify(gd, gif, MGM_GROUP_ADDED, s, g);
378 0 : break;
379 :
380 0 : case GMPR_NOTIF_GROUP_ADD_INCL:
381 : /*
382 : * Check if PIM requested a refresh.
383 : * If so we don't send a group and source REMOVE
384 : * otherwise PIM will clear its state
385 : */
386 0 : if (gd->refresh_required == FALSE) {
387 0 : gmp_group_notify(gd, gif, MGM_GROUP_SRC_REMOVED, s, g);
388 0 : gmp_group_notify(gd, gif, MGM_GROUP_REMOVED, s, g);
389 : }
390 0 : break;
391 :
392 0 : case GMPR_NOTIF_REFRESH_END:
393 0 : gd->refresh_required = FALSE;
394 0 : break;
395 :
396 0 : default:
397 : /* We should never get here */
398 0 : assert(0);
399 : }
400 : }
401 :
402 : /*
403 : * If there's a notification pointer, it means that we bailed out due
404 : * to hitting the notification count limit or someone is too busy to
405 : * process the updates.
406 : *
407 : * Either way, we explicitly toss the notification block and then fall
408 : * out, which causes the job to be requeued.
409 : *
410 : * On the other hand, if the notification pointer is NULL, it means that
411 : * we drained the queue, in which case we should kill the job.
412 : */
413 0 : if (notification) {
414 0 : gmpr_return_notification(notification);
415 0 : pending = TRUE;
416 : }
417 :
418 0 : return pending;
419 : }
420 :
421 : // Per host, per-<S,G> notification to indicate join or leave
422 : // among others of an host.
423 0 : boolean gmp_client_host_notification(mgm_global_data *gd)
424 : {
425 0 : boolean pending = FALSE;
426 0 : gmpr_client_host_notification *gmpr_notif = NULL;
427 : gmp_intf *gif;
428 : gmp_intf_handle *handle;
429 : gmp_addr_string g;
430 : gmp_addr_string s;
431 : gmp_addr_string h;
432 0 : int notif_count = IGMP_MAX_HOST_NOTIF_PER_PASS;
433 :
434 : /*
435 : * Do this a limited number of times.
436 : */
437 0 : while (notif_count--) {
438 : /*
439 : * Stop if there are no more notifications.
440 : */
441 : gmpr_notif =
442 0 : gmpr_get_host_notification(gd->mgm_gmpr_client, gmpr_notif);
443 0 : if (!gmpr_notif) {
444 0 : break;
445 : }
446 :
447 : /*
448 : * Skip NULL MIFs (local groups).
449 : */
450 0 : handle = gmpr_notif->host_notif_intf_id;
451 0 : gif = gmp_handle_to_gif(handle);
452 0 : if (!gif) {
453 0 : continue;
454 : }
455 :
456 : /*
457 : * Get needed addresses.
458 : */
459 0 : memcpy(&g, &gmpr_notif->host_notif_group_addr, IPV4_ADDR_LEN);
460 0 : memcpy(&s, &gmpr_notif->host_notif_source_addr, IPV4_ADDR_LEN);
461 0 : memcpy(&h, &gmpr_notif->host_notif_host_addr, IPV4_ADDR_LEN);
462 :
463 : /*
464 : * Update mapped OIF module.
465 : */
466 0 : switch (gmpr_notif->host_notif_type) {
467 0 : case GMPR_NOTIF_HOST_UNKNOWN:
468 0 : break;
469 :
470 0 : case GMPR_NOTIF_HOST_JOIN:
471 : /*
472 : * This is a join.
473 : */
474 0 : gmp_host_update(gd, gif, TRUE, h, s, g);
475 0 : break;
476 :
477 0 : case GMPR_NOTIF_HOST_LEAVE:
478 : case GMPR_NOTIF_HOST_TIMEOUT:
479 : case GMPR_NOTIF_HOST_IFDOWN:
480 : /*
481 : * This is a leave.
482 : */
483 0 : gmp_host_update(gd, gif, FALSE, h, s, g);
484 0 : break;
485 :
486 0 : default:
487 0 : assert(0);
488 : break;
489 : }
490 : }
491 :
492 : /*
493 : * If there's a notification pointer, it means that we bailed out
494 : * due to hitting the notification count limit. In that case we
495 : * explicitly toss the notification block and then fall out, which
496 : * causes the job to be requeued.
497 : *
498 : * On the other hand, if the notification pointer is NULL, it means that
499 : * we drained the queue, in which case we should kill the job.
500 : */
501 0 : if (gmpr_notif) {
502 0 : gmpr_return_host_notification(gmpr_notif);
503 0 : pending = TRUE;
504 : }
505 :
506 0 : return pending;
507 : }
508 :
509 : // Top level handler to handle both <S,G> and also
510 : // per-host, per-<S,G> notifications.
511 0 : boolean gmp_notification_handler(mgm_global_data *gd)
512 : {
513 0 : boolean pending = FALSE;
514 :
515 0 : pending |= gmp_client_notification(gd);
516 :
517 0 : pending |= gmp_client_host_notification(gd);
518 :
519 0 : return pending;
520 : }
521 :
522 : // per-<S,G> notification handler registered with GMP.
523 : // Triggers the agent IGMP TaskTrigger instance
524 0 : void igmp_notification_ready(void *context)
525 : {
526 0 : mgm_global_data *gd = (mgm_global_data *)context;
527 :
528 0 : gmp_notification_ready(gd);
529 0 : }
530 :
531 : // per-host, per-<S,G> notification handler registered with GMP.
532 : // Triggers the agent IGMP TaskTrigger instance
533 0 : void igmp_host_notification_ready(void *context)
534 : {
535 0 : mgm_global_data *gd = (mgm_global_data *)context;
536 :
537 0 : gmp_notification_ready(gd);
538 0 : }
539 :
540 : // No-op for now. Single querier support per-compute
541 3 : void mgm_querier_change(void *cli_context UNUSED, gmp_intf_handle *handle,
542 : boolean querier, u_int8_t *querier_addr)
543 : {
544 3 : return;
545 : }
546 :
547 : // Tracing utility mapped to Multicast trace.
548 1 : void gmpx_trace(void *context, const char *fmt, ...)
549 : {
550 : va_list arglist;
551 : char dest[MAXSTRINGSIZE_1];
552 1 : va_start( arglist, fmt );
553 1 : vsprintf(dest, fmt, arglist);
554 1 : va_end( arglist );
555 : char buff[MAXSTRINGSIZE_1];
556 1 : snprintf(buff, sizeof(buff), dest, arglist);
557 :
558 1 : MCTRACE(Info, "igmp_trace: ", buff);
559 :
560 2 : return;
561 : }
562 :
563 : // Not used.
564 0 : void gmpx_post_event(void *context, gmpx_event_type ev,
565 : const void *parms, ...)
566 : {
567 0 : return;
568 : }
569 :
570 : // Send one IGMP packet at a time.
571 3 : bool igmp_send_one_packet(gmp_intf_handle *intf)
572 : {
573 3 : uint8_t *pkt = NULL;
574 : gmp_addr_string dest_addr;
575 : mgm_global_data *gd;
576 : gmp_intf *gif;
577 :
578 3 : gd = &mgm_global[MCAST_AF_IPV4];
579 3 : gif = gmp_handle_to_gif(intf);
580 3 : if (!gif) {
581 0 : return false;
582 : }
583 :
584 3 : pkt = gmp_get_send_buffer(gd, gif);
585 3 : if (!pkt) {
586 0 : return false;
587 : }
588 :
589 3 : uint32_t formatted_len = igmp_next_xmit_packet(GMP_ROLE_ROUTER, intf, pkt,
590 : dest_addr.gmp_v4_addr, 1500, gd, 0);
591 3 : if (formatted_len == 0) {
592 3 : gmp_free_send_buffer(gd, gif, pkt);
593 3 : return false;
594 : }
595 :
596 0 : gmp_send_one_packet(gd, gif, pkt, formatted_len, dest_addr);
597 :
598 0 : gmp_free_send_buffer(gd, gif, pkt);
599 :
600 0 : return true;
601 : }
602 :
603 : // Called by GMP to send out IGMP packet.
604 3 : void gmp_xmit_ready(gmp_role role, gmp_proto proto, gmpx_intf_id intf_id)
605 : {
606 3 : if (role != GMP_ROLE_ROUTER) {
607 0 : return;
608 : }
609 :
610 3 : if (proto != GMP_PROTO_IGMP) {
611 0 : return;
612 : }
613 :
614 3 : gmp_intf_handle *intf = (gmp_intf_handle*)intf_id;
615 3 : if (!intf) {
616 0 : return;
617 : }
618 :
619 3 : while (igmp_send_one_packet(intf));
620 :
621 3 : return;
622 : }
623 :
624 0 : void gmp_static_peek(gmp_intf_handle *handle, gmp_proto proto,
625 : gmp_packet *rcv_packet)
626 : {
627 0 : return;
628 : }
629 :
|