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