]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
12e41d03 | 2 | /* |
896014f4 DL |
3 | * PIM for Quagga |
4 | * Copyright (C) 2008 Everton da Silva Marques | |
896014f4 | 5 | */ |
12e41d03 DL |
6 | |
7 | #include <zebra.h> | |
8 | #include "log.h" | |
9 | #include "memory.h" | |
744d91b3 | 10 | #include "if.h" |
3613d898 | 11 | #include "lib_errors.h" |
12e41d03 DL |
12 | |
13 | #include "pimd.h" | |
993e3d8e | 14 | #include "pim_instance.h" |
12e41d03 DL |
15 | #include "pim_iface.h" |
16 | #include "pim_igmp.h" | |
17 | #include "pim_igmpv3.h" | |
18 | #include "pim_str.h" | |
19 | #include "pim_util.h" | |
20 | #include "pim_time.h" | |
21 | #include "pim_zebra.h" | |
22 | #include "pim_oil.h" | |
f6053ff2 | 23 | #include "pim_ssm.h" |
12e41d03 | 24 | |
a16db099 SG |
25 | static void group_retransmit_timer_on(struct gm_group *group); |
26 | static long igmp_group_timer_remain_msec(struct gm_group *group); | |
51700107 | 27 | static long igmp_source_timer_remain_msec(struct gm_source *source); |
a16db099 SG |
28 | static void group_query_send(struct gm_group *group); |
29 | static void source_query_send_by_flag(struct gm_group *group, | |
12e41d03 DL |
30 | int num_sources_tosend); |
31 | ||
d62a17ae | 32 | static void on_trace(const char *label, struct interface *ifp, |
33 | struct in_addr from, struct in_addr group_addr, | |
12e41d03 DL |
34 | int num_sources, struct in_addr *sources) |
35 | { | |
a96d64b0 | 36 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 37 | char from_str[INET_ADDRSTRLEN]; |
38 | char group_str[INET_ADDRSTRLEN]; | |
12e41d03 | 39 | |
d62a17ae | 40 | pim_inet4_dump("<from?>", from, from_str, sizeof(from_str)); |
41 | pim_inet4_dump("<group?>", group_addr, group_str, | |
42 | sizeof(group_str)); | |
12e41d03 | 43 | |
d62a17ae | 44 | zlog_debug("%s: from %s on %s: group=%s sources=%d", label, |
45 | from_str, ifp->name, group_str, num_sources); | |
46 | } | |
12e41d03 DL |
47 | } |
48 | ||
a16db099 | 49 | static inline long igmp_gmi_msec(struct gm_group *group) |
dda4d23c DL |
50 | { |
51 | struct pim_interface *pim_ifp = group->interface->info; | |
c5f76fad | 52 | struct gm_sock *igmp; |
dda4d23c DL |
53 | struct listnode *sock_node; |
54 | ||
55 | long qrv = 0, qqi = 0; | |
56 | ||
18adcff1 | 57 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) { |
dda4d23c DL |
58 | qrv = MAX(qrv, igmp->querier_robustness_variable); |
59 | qqi = MAX(qqi, igmp->querier_query_interval); | |
60 | } | |
61 | return PIM_IGMP_GMI_MSEC(qrv, qqi, | |
18adcff1 | 62 | pim_ifp->gm_query_max_response_time_dsec); |
dda4d23c DL |
63 | } |
64 | ||
a16db099 | 65 | void igmp_group_reset_gmi(struct gm_group *group) |
12e41d03 | 66 | { |
d62a17ae | 67 | long group_membership_interval_msec; |
d62a17ae | 68 | struct interface *ifp; |
69 | ||
dda4d23c | 70 | ifp = group->interface; |
d62a17ae | 71 | |
72 | /* | |
73 | RFC 3376: 8.4. Group Membership Interval | |
74 | ||
75 | The Group Membership Interval is the amount of time that must pass | |
76 | before a multicast router decides there are no more members of a | |
77 | group or a particular source on a network. | |
78 | ||
79 | This value MUST be ((the Robustness Variable) times (the Query | |
80 | Interval)) plus (one Query Response Interval). | |
81 | ||
82 | group_membership_interval_msec = querier_robustness_variable * | |
83 | (1000 * querier_query_interval) + | |
84 | 100 * query_response_interval_dsec; | |
85 | */ | |
dda4d23c | 86 | group_membership_interval_msec = igmp_gmi_msec(group); |
d62a17ae | 87 | |
a96d64b0 | 88 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 89 | char group_str[INET_ADDRSTRLEN]; |
90 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
91 | sizeof(group_str)); | |
92 | zlog_debug( | |
93 | "Resetting group %s timer to GMI=%ld.%03ld sec on %s", | |
94 | group_str, group_membership_interval_msec / 1000, | |
95 | group_membership_interval_msec % 1000, ifp->name); | |
96 | } | |
97 | ||
98 | /* | |
99 | RFC 3376: 6.2.2. Definition of Group Timers | |
100 | ||
101 | The group timer is only used when a group is in EXCLUDE mode and | |
102 | it represents the time for the *filter-mode* of the group to | |
103 | expire and switch to INCLUDE mode. | |
104 | */ | |
df5dfb77 | 105 | assert(group->group_filtermode_isexcl); |
d62a17ae | 106 | |
107 | igmp_group_timer_on(group, group_membership_interval_msec, ifp->name); | |
12e41d03 DL |
108 | } |
109 | ||
cc9f21da | 110 | static void igmp_source_timer(struct thread *t) |
12e41d03 | 111 | { |
51700107 | 112 | struct gm_source *source; |
a16db099 | 113 | struct gm_group *group; |
d62a17ae | 114 | |
115 | source = THREAD_ARG(t); | |
116 | ||
117 | group = source->source_group; | |
118 | ||
a96d64b0 | 119 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 120 | char group_str[INET_ADDRSTRLEN]; |
121 | char source_str[INET_ADDRSTRLEN]; | |
122 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
123 | sizeof(group_str)); | |
124 | pim_inet4_dump("<source?>", source->source_addr, source_str, | |
125 | sizeof(source_str)); | |
126 | zlog_debug( | |
127 | "%s: Source timer expired for group %s source %s on %s", | |
15569c58 | 128 | __func__, group_str, source_str, |
dda4d23c | 129 | group->interface->name); |
d62a17ae | 130 | } |
131 | ||
132 | /* | |
133 | RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules | |
134 | ||
135 | Group | |
136 | Filter-Mode Source Timer Value Action | |
137 | ----------- ------------------ ------ | |
138 | INCLUDE TIMER == 0 Suggest to stop forwarding | |
139 | traffic from source and | |
140 | remove source record. If | |
141 | there are no more source | |
142 | records for the group, delete | |
143 | group record. | |
144 | ||
145 | EXCLUDE TIMER == 0 Suggest to not forward | |
146 | traffic from source | |
147 | (DO NOT remove record) | |
148 | ||
149 | Source timer switched from (T > 0) to (T == 0): disable forwarding. | |
150 | */ | |
151 | ||
152 | if (group->group_filtermode_isexcl) { | |
153 | /* EXCLUDE mode */ | |
154 | ||
155 | igmp_source_forward_stop(source); | |
156 | } else { | |
157 | /* INCLUDE mode */ | |
158 | ||
159 | /* igmp_source_delete() will stop forwarding source */ | |
160 | igmp_source_delete(source); | |
161 | ||
162 | /* | |
163 | If there are no more source records for the group, delete | |
164 | group | |
165 | record. | |
166 | */ | |
167 | if (!listcount(group->group_source_list)) { | |
168 | igmp_group_delete_empty_include(group); | |
169 | } | |
170 | } | |
12e41d03 DL |
171 | } |
172 | ||
a16db099 | 173 | static void source_timer_off(struct gm_group *group, struct gm_source *source) |
12e41d03 | 174 | { |
d62a17ae | 175 | if (!source->t_source_timer) |
176 | return; | |
177 | ||
a96d64b0 | 178 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 179 | char group_str[INET_ADDRSTRLEN]; |
180 | char source_str[INET_ADDRSTRLEN]; | |
181 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
182 | sizeof(group_str)); | |
183 | pim_inet4_dump("<source?>", source->source_addr, source_str, | |
184 | sizeof(source_str)); | |
185 | zlog_debug( | |
186 | "Cancelling TIMER event for group %s source %s on %s", | |
a1a4295a | 187 | group_str, source_str, group->interface->name); |
d62a17ae | 188 | } |
189 | ||
190 | THREAD_OFF(source->t_source_timer); | |
12e41d03 DL |
191 | } |
192 | ||
a16db099 | 193 | static void igmp_source_timer_on(struct gm_group *group, |
51700107 | 194 | struct gm_source *source, long interval_msec) |
12e41d03 | 195 | { |
d62a17ae | 196 | source_timer_off(group, source); |
dda4d23c | 197 | struct pim_interface *pim_ifp = group->interface->info; |
d62a17ae | 198 | |
95b13dc5 | 199 | if (PIM_DEBUG_GM_EVENTS) { |
d62a17ae | 200 | char group_str[INET_ADDRSTRLEN]; |
201 | char source_str[INET_ADDRSTRLEN]; | |
202 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
203 | sizeof(group_str)); | |
204 | pim_inet4_dump("<source?>", source->source_addr, source_str, | |
205 | sizeof(source_str)); | |
206 | zlog_debug( | |
207 | "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s", | |
208 | interval_msec / 1000, interval_msec % 1000, group_str, | |
dda4d23c | 209 | source_str, group->interface->name); |
d62a17ae | 210 | } |
211 | ||
36417fcc DS |
212 | thread_add_timer_msec(router->master, igmp_source_timer, source, |
213 | interval_msec, &source->t_source_timer); | |
d62a17ae | 214 | |
215 | /* | |
216 | RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules | |
217 | ||
218 | Source timer switched from (T == 0) to (T > 0): enable forwarding. | |
219 | */ | |
e52e3223 | 220 | igmp_source_forward_start(pim_ifp->pim, source); |
12e41d03 DL |
221 | } |
222 | ||
a16db099 | 223 | void igmp_source_reset_gmi(struct gm_group *group, struct gm_source *source) |
12e41d03 | 224 | { |
d62a17ae | 225 | long group_membership_interval_msec; |
d62a17ae | 226 | struct interface *ifp; |
227 | ||
dda4d23c | 228 | ifp = group->interface; |
d62a17ae | 229 | |
dda4d23c | 230 | group_membership_interval_msec = igmp_gmi_msec(group); |
d62a17ae | 231 | |
a96d64b0 | 232 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 233 | char group_str[INET_ADDRSTRLEN]; |
234 | char source_str[INET_ADDRSTRLEN]; | |
235 | ||
236 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
237 | sizeof(group_str)); | |
238 | pim_inet4_dump("<source?>", source->source_addr, source_str, | |
239 | sizeof(source_str)); | |
240 | ||
241 | zlog_debug( | |
242 | "Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s", | |
243 | source_str, group_membership_interval_msec / 1000, | |
244 | group_membership_interval_msec % 1000, group_str, | |
245 | ifp->name); | |
246 | } | |
247 | ||
248 | igmp_source_timer_on(group, source, group_membership_interval_msec); | |
12e41d03 DL |
249 | } |
250 | ||
a16db099 | 251 | static void source_mark_delete_flag(struct gm_group *group) |
12e41d03 | 252 | { |
d62a17ae | 253 | struct listnode *src_node; |
51700107 | 254 | struct gm_source *src; |
12e41d03 | 255 | |
d62a17ae | 256 | for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { |
257 | IGMP_SOURCE_DO_DELETE(src->source_flags); | |
258 | } | |
12e41d03 DL |
259 | } |
260 | ||
a16db099 | 261 | static void source_mark_send_flag(struct gm_group *group) |
12e41d03 | 262 | { |
d62a17ae | 263 | struct listnode *src_node; |
51700107 | 264 | struct gm_source *src; |
12e41d03 | 265 | |
d62a17ae | 266 | for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { |
267 | IGMP_SOURCE_DO_SEND(src->source_flags); | |
268 | } | |
12e41d03 DL |
269 | } |
270 | ||
a16db099 | 271 | static int source_mark_send_flag_by_timer(struct gm_group *group) |
12e41d03 | 272 | { |
d62a17ae | 273 | struct listnode *src_node; |
51700107 | 274 | struct gm_source *src; |
d62a17ae | 275 | int num_marked_sources = 0; |
276 | ||
277 | for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { | |
278 | /* Is source timer running? */ | |
279 | if (src->t_source_timer) { | |
280 | IGMP_SOURCE_DO_SEND(src->source_flags); | |
281 | ++num_marked_sources; | |
282 | } else { | |
283 | IGMP_SOURCE_DONT_SEND(src->source_flags); | |
284 | } | |
285 | } | |
286 | ||
287 | return num_marked_sources; | |
12e41d03 DL |
288 | } |
289 | ||
290 | static void source_clear_send_flag(struct list *source_list) | |
291 | { | |
d62a17ae | 292 | struct listnode *src_node; |
51700107 | 293 | struct gm_source *src; |
12e41d03 | 294 | |
d62a17ae | 295 | for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) { |
296 | IGMP_SOURCE_DONT_SEND(src->source_flags); | |
297 | } | |
12e41d03 DL |
298 | } |
299 | ||
300 | /* | |
301 | Any source (*,G) is forwarded only if mode is EXCLUDE {empty} | |
302 | */ | |
a16db099 | 303 | static void group_exclude_fwd_anysrc_ifempty(struct gm_group *group) |
12e41d03 | 304 | { |
dda4d23c | 305 | struct pim_interface *pim_ifp = group->interface->info; |
e52e3223 | 306 | |
df5dfb77 | 307 | assert(group->group_filtermode_isexcl); |
12e41d03 | 308 | |
d62a17ae | 309 | if (listcount(group->group_source_list) < 1) { |
e52e3223 | 310 | igmp_anysource_forward_start(pim_ifp->pim, group); |
d62a17ae | 311 | } |
12e41d03 DL |
312 | } |
313 | ||
51700107 | 314 | void igmp_source_free(struct gm_source *source) |
12e41d03 | 315 | { |
d62a17ae | 316 | /* make sure there is no source timer running */ |
df5dfb77 | 317 | assert(!source->t_source_timer); |
12e41d03 | 318 | |
d62a17ae | 319 | XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source); |
12e41d03 DL |
320 | } |
321 | ||
51700107 | 322 | static void source_channel_oil_detach(struct gm_source *source) |
12e41d03 | 323 | { |
d62a17ae | 324 | if (source->source_channel_oil) { |
15569c58 | 325 | pim_channel_oil_del(source->source_channel_oil, __func__); |
d62a17ae | 326 | source->source_channel_oil = NULL; |
327 | } | |
12e41d03 DL |
328 | } |
329 | ||
330 | /* | |
07b12758 DS |
331 | igmp_source_delete: stop forwarding, and delete the source |
332 | igmp_source_forward_stop: stop forwarding, but keep the source | |
12e41d03 | 333 | */ |
51700107 | 334 | void igmp_source_delete(struct gm_source *source) |
12e41d03 | 335 | { |
a16db099 | 336 | struct gm_group *group; |
d62a17ae | 337 | struct in_addr src; |
338 | ||
339 | group = source->source_group; | |
340 | ||
a96d64b0 | 341 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 342 | char group_str[INET_ADDRSTRLEN]; |
343 | char source_str[INET_ADDRSTRLEN]; | |
344 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
345 | sizeof(group_str)); | |
346 | pim_inet4_dump("<source?>", source->source_addr, source_str, | |
347 | sizeof(source_str)); | |
348 | zlog_debug( | |
dda4d23c | 349 | "Deleting IGMP source %s for group %s from interface %s c_oil ref_count %d", |
a1a4295a | 350 | source_str, group_str, group->interface->name, |
d62a17ae | 351 | source->source_channel_oil |
352 | ? source->source_channel_oil->oil_ref_count | |
353 | : 0); | |
354 | } | |
355 | ||
356 | source_timer_off(group, source); | |
357 | igmp_source_forward_stop(source); | |
358 | ||
359 | /* sanity check that forwarding has been disabled */ | |
360 | if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { | |
361 | char group_str[INET_ADDRSTRLEN]; | |
362 | char source_str[INET_ADDRSTRLEN]; | |
363 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
364 | sizeof(group_str)); | |
365 | pim_inet4_dump("<source?>", source->source_addr, source_str, | |
366 | sizeof(source_str)); | |
367 | zlog_warn( | |
dda4d23c | 368 | "%s: forwarding=ON(!) IGMP source %s for group %s from interface %s", |
15569c58 | 369 | __func__, source_str, group_str, |
dda4d23c | 370 | group->interface->name); |
d62a17ae | 371 | /* warning only */ |
372 | } | |
373 | ||
374 | source_channel_oil_detach(source); | |
375 | ||
376 | /* | |
377 | notice that listnode_delete() can't be moved | |
378 | into igmp_source_free() because the later is | |
379 | called by list_delete_all_node() | |
380 | */ | |
381 | listnode_delete(group->group_source_list, source); | |
382 | ||
383 | src.s_addr = source->source_addr.s_addr; | |
384 | igmp_source_free(source); | |
385 | ||
386 | /* Group source list is empty and current source is * then | |
387 | *,G group going away so do not trigger start */ | |
388 | if (group->group_filtermode_isexcl | |
389 | && (listcount(group->group_source_list) != 0) | |
390 | && src.s_addr != INADDR_ANY) { | |
391 | group_exclude_fwd_anysrc_ifempty(group); | |
392 | } | |
12e41d03 DL |
393 | } |
394 | ||
395 | static void source_delete_by_flag(struct list *source_list) | |
396 | { | |
d62a17ae | 397 | struct listnode *src_node; |
398 | struct listnode *src_nextnode; | |
51700107 | 399 | struct gm_source *src; |
d62a17ae | 400 | |
401 | for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src)) | |
402 | if (IGMP_SOURCE_TEST_DELETE(src->source_flags)) | |
403 | igmp_source_delete(src); | |
12e41d03 DL |
404 | } |
405 | ||
406 | void igmp_source_delete_expired(struct list *source_list) | |
407 | { | |
d62a17ae | 408 | struct listnode *src_node; |
409 | struct listnode *src_nextnode; | |
51700107 | 410 | struct gm_source *src; |
d62a17ae | 411 | |
412 | for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src)) | |
413 | if (!src->t_source_timer) | |
414 | igmp_source_delete(src); | |
12e41d03 DL |
415 | } |
416 | ||
a16db099 SG |
417 | struct gm_source *igmp_find_source_by_addr(struct gm_group *group, |
418 | struct in_addr src_addr) | |
12e41d03 | 419 | { |
d62a17ae | 420 | struct listnode *src_node; |
51700107 | 421 | struct gm_source *src; |
12e41d03 | 422 | |
d62a17ae | 423 | for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) |
424 | if (src_addr.s_addr == src->source_addr.s_addr) | |
425 | return src; | |
12e41d03 | 426 | |
d62a17ae | 427 | return 0; |
12e41d03 DL |
428 | } |
429 | ||
a16db099 SG |
430 | struct gm_source *igmp_get_source_by_addr(struct gm_group *group, |
431 | struct in_addr src_addr, bool *new) | |
12e41d03 | 432 | { |
51700107 | 433 | struct gm_source *src; |
d62a17ae | 434 | |
5421bf8f DL |
435 | if (new) |
436 | *new = false; | |
437 | ||
438 | src = igmp_find_source_by_addr(group, src_addr); | |
439 | if (src) | |
440 | return src; | |
441 | ||
a96d64b0 | 442 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 443 | char group_str[INET_ADDRSTRLEN]; |
444 | char source_str[INET_ADDRSTRLEN]; | |
445 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
446 | sizeof(group_str)); | |
447 | pim_inet4_dump("<source?>", src_addr, source_str, | |
448 | sizeof(source_str)); | |
449 | zlog_debug( | |
dda4d23c DL |
450 | "Creating new IGMP source %s for group %s on interface %s", |
451 | source_str, group_str, group->interface->name); | |
d62a17ae | 452 | } |
453 | ||
454 | src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src)); | |
d62a17ae | 455 | |
14c7ac50 MR |
456 | if (new) |
457 | *new = true; | |
458 | ||
d62a17ae | 459 | src->t_source_timer = NULL; |
460 | src->source_group = group; /* back pointer */ | |
461 | src->source_addr = src_addr; | |
462 | src->source_creation = pim_time_monotonic_sec(); | |
463 | src->source_flags = 0; | |
464 | src->source_query_retransmit_count = 0; | |
465 | src->source_channel_oil = NULL; | |
466 | ||
467 | listnode_add(group->group_source_list, src); | |
468 | ||
469 | /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ | |
470 | igmp_anysource_forward_stop(group); | |
d62a17ae | 471 | return src; |
12e41d03 DL |
472 | } |
473 | ||
c5f76fad | 474 | static void allow(struct gm_sock *igmp, struct in_addr from, |
d62a17ae | 475 | struct in_addr group_addr, int num_sources, |
476 | struct in_addr *sources) | |
12e41d03 | 477 | { |
51700107 | 478 | struct gm_source *source; |
a16db099 | 479 | struct gm_group *group; |
d62a17ae | 480 | int i; |
481 | ||
fcf5d438 SG |
482 | if (num_sources == 0) { |
483 | /* | |
484 | RFC 3376: 3.1. Socket-State | |
485 | If the requested filter mode is INCLUDE *and* the requested | |
486 | source list is empty, then the entry corresponding to the | |
487 | requested interface and multicast address is deleted if | |
488 | present. If no such entry is present, the request is ignored. | |
489 | So, deleting the group present. | |
490 | */ | |
491 | group = find_group_by_addr(igmp, group_addr); | |
26d0cbbb JAG |
492 | if (!group) { |
493 | return; | |
494 | } | |
495 | if (group->group_filtermode_isexcl) { | |
496 | if (listcount(group->group_source_list) == 1) { | |
497 | struct in_addr star = {.s_addr = INADDR_ANY}; | |
498 | ||
499 | source = igmp_find_source_by_addr(group, star); | |
500 | if (source) | |
dda4d23c | 501 | igmp_source_reset_gmi(group, source); |
26d0cbbb JAG |
502 | } |
503 | } else { | |
fcf5d438 | 504 | igmp_group_delete(group); |
26d0cbbb JAG |
505 | } |
506 | ||
fcf5d438 SG |
507 | return; |
508 | } | |
509 | ||
b97047ed | 510 | /* non-existent group is created as INCLUDE {empty} */ |
d62a17ae | 511 | group = igmp_add_group_by_addr(igmp, group_addr); |
512 | if (!group) { | |
513 | return; | |
514 | } | |
515 | ||
516 | /* scan received sources */ | |
517 | for (i = 0; i < num_sources; ++i) { | |
518 | struct in_addr *src_addr; | |
519 | ||
520 | src_addr = sources + i; | |
521 | ||
5421bf8f DL |
522 | source = igmp_get_source_by_addr(group, *src_addr, NULL); |
523 | if (!source) | |
d62a17ae | 524 | continue; |
d62a17ae | 525 | |
526 | /* | |
527 | RFC 3376: 6.4.1. Reception of Current-State Records | |
528 | ||
529 | When receiving IS_IN reports for groups in EXCLUDE mode is | |
530 | sources should be moved from set with (timers = 0) to set with | |
531 | (timers > 0). | |
532 | ||
533 | igmp_source_reset_gmi() below, resetting the source timers to | |
534 | GMI, accomplishes this. | |
535 | */ | |
dda4d23c | 536 | igmp_source_reset_gmi(group, source); |
d62a17ae | 537 | |
538 | } /* scan received sources */ | |
12e41d03 DL |
539 | } |
540 | ||
c5f76fad | 541 | void igmpv3_report_isin(struct gm_sock *igmp, struct in_addr from, |
d62a17ae | 542 | struct in_addr group_addr, int num_sources, |
543 | struct in_addr *sources) | |
12e41d03 | 544 | { |
15569c58 DA |
545 | on_trace(__func__, igmp->interface, from, group_addr, num_sources, |
546 | sources); | |
12e41d03 | 547 | |
d62a17ae | 548 | allow(igmp, from, group_addr, num_sources, sources); |
12e41d03 DL |
549 | } |
550 | ||
a16db099 | 551 | static void isex_excl(struct gm_group *group, int num_sources, |
d62a17ae | 552 | struct in_addr *sources) |
12e41d03 | 553 | { |
51700107 | 554 | struct gm_source *source; |
d62a17ae | 555 | int i; |
556 | ||
557 | /* EXCLUDE mode */ | |
df5dfb77 | 558 | assert(group->group_filtermode_isexcl); |
d62a17ae | 559 | |
560 | /* E.1: set deletion flag for known sources (X,Y) */ | |
561 | source_mark_delete_flag(group); | |
562 | ||
563 | /* scan received sources (A) */ | |
564 | for (i = 0; i < num_sources; ++i) { | |
565 | struct in_addr *src_addr; | |
5421bf8f | 566 | bool new; |
d62a17ae | 567 | |
568 | src_addr = sources + i; | |
569 | ||
570 | /* E.2: lookup reported source from (A) in (X,Y) */ | |
5421bf8f DL |
571 | source = igmp_get_source_by_addr(group, *src_addr, &new); |
572 | if (!source) | |
573 | continue; | |
574 | ||
575 | if (!new) { | |
d62a17ae | 576 | /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */ |
577 | IGMP_SOURCE_DONT_DELETE(source->source_flags); | |
578 | } else { | |
579 | /* E.4: if not found, create source with timer=GMI: | |
580 | * (A-X-Y) */ | |
df5dfb77 | 581 | assert(!source->t_source_timer); /* timer == 0 */ |
dda4d23c | 582 | igmp_source_reset_gmi(group, source); |
df5dfb77 | 583 | assert(source->t_source_timer); /* (A-X-Y) timer > 0 */ |
d62a17ae | 584 | } |
585 | ||
586 | } /* scan received sources */ | |
587 | ||
588 | /* | |
589 | * If we are in isexcl mode and num_sources == 0 | |
590 | * than that means we have a *,g entry that | |
591 | * needs to be handled | |
592 | */ | |
593 | if (group->group_filtermode_isexcl && num_sources == 0) { | |
594 | struct in_addr star = {.s_addr = INADDR_ANY}; | |
595 | source = igmp_find_source_by_addr(group, star); | |
596 | if (source) { | |
597 | IGMP_SOURCE_DONT_DELETE(source->source_flags); | |
dda4d23c | 598 | igmp_source_reset_gmi(group, source); |
d62a17ae | 599 | } |
600 | } | |
601 | ||
602 | /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */ | |
603 | source_delete_by_flag(group->group_source_list); | |
12e41d03 DL |
604 | } |
605 | ||
a16db099 | 606 | static void isex_incl(struct gm_group *group, int num_sources, |
d62a17ae | 607 | struct in_addr *sources) |
12e41d03 | 608 | { |
d62a17ae | 609 | int i; |
610 | ||
611 | /* INCLUDE mode */ | |
df5dfb77 | 612 | assert(!group->group_filtermode_isexcl); |
d62a17ae | 613 | |
614 | /* I.1: set deletion flag for known sources (A) */ | |
615 | source_mark_delete_flag(group); | |
616 | ||
617 | /* scan received sources (B) */ | |
618 | for (i = 0; i < num_sources; ++i) { | |
51700107 | 619 | struct gm_source *source; |
d62a17ae | 620 | struct in_addr *src_addr; |
5421bf8f | 621 | bool new; |
d62a17ae | 622 | |
623 | src_addr = sources + i; | |
624 | ||
625 | /* I.2: lookup reported source (B) */ | |
5421bf8f DL |
626 | source = igmp_get_source_by_addr(group, *src_addr, &new); |
627 | if (!source) | |
628 | continue; | |
629 | ||
630 | if (!new) { | |
d62a17ae | 631 | /* I.3: if found, clear deletion flag (A*B) */ |
632 | IGMP_SOURCE_DONT_DELETE(source->source_flags); | |
633 | } else { | |
634 | /* I.4: if not found, create source with timer=0 (B-A) | |
635 | */ | |
df5dfb77 | 636 | assert(!source->t_source_timer); /* (B-A) timer=0 */ |
d62a17ae | 637 | } |
638 | ||
639 | } /* scan received sources */ | |
640 | ||
641 | /* I.5: delete all sources marked with deletion flag (A-B) */ | |
642 | source_delete_by_flag(group->group_source_list); | |
643 | ||
644 | group->group_filtermode_isexcl = 1; /* boolean=true */ | |
645 | ||
df5dfb77 | 646 | assert(group->group_filtermode_isexcl); |
d62a17ae | 647 | |
648 | group_exclude_fwd_anysrc_ifempty(group); | |
12e41d03 DL |
649 | } |
650 | ||
c5f76fad | 651 | void igmpv3_report_isex(struct gm_sock *igmp, struct in_addr from, |
d62a17ae | 652 | struct in_addr group_addr, int num_sources, |
653 | struct in_addr *sources, int from_igmp_v2_report) | |
12e41d03 | 654 | { |
d62a17ae | 655 | struct interface *ifp = igmp->interface; |
a16db099 | 656 | struct gm_group *group; |
d62a17ae | 657 | |
15569c58 | 658 | on_trace(__func__, ifp, from, group_addr, num_sources, sources); |
d62a17ae | 659 | |
b0f525a8 QY |
660 | if (pim_is_group_filtered(ifp->info, &group_addr)) |
661 | return; | |
662 | ||
b97047ed | 663 | /* non-existent group is created as INCLUDE {empty} */ |
d62a17ae | 664 | group = igmp_add_group_by_addr(igmp, group_addr); |
665 | if (!group) { | |
666 | return; | |
667 | } | |
668 | ||
669 | /* So we can display how we learned the group in our show command output | |
670 | */ | |
671 | if (from_igmp_v2_report) | |
672 | group->igmp_version = 2; | |
673 | ||
674 | if (group->group_filtermode_isexcl) { | |
675 | /* EXCLUDE mode */ | |
676 | isex_excl(group, num_sources, sources); | |
677 | } else { | |
678 | /* INCLUDE mode */ | |
679 | isex_incl(group, num_sources, sources); | |
df5dfb77 | 680 | assert(group->group_filtermode_isexcl); |
d62a17ae | 681 | } |
682 | ||
df5dfb77 | 683 | assert(group->group_filtermode_isexcl); |
d62a17ae | 684 | |
685 | igmp_group_reset_gmi(group); | |
12e41d03 DL |
686 | } |
687 | ||
a16db099 | 688 | static void toin_incl(struct gm_group *group, int num_sources, |
d62a17ae | 689 | struct in_addr *sources) |
12e41d03 | 690 | { |
d62a17ae | 691 | int num_sources_tosend = listcount(group->group_source_list); |
692 | int i; | |
693 | ||
694 | /* Set SEND flag for all known sources (A) */ | |
695 | source_mark_send_flag(group); | |
696 | ||
697 | /* Scan received sources (B) */ | |
698 | for (i = 0; i < num_sources; ++i) { | |
51700107 | 699 | struct gm_source *source; |
d62a17ae | 700 | struct in_addr *src_addr; |
5421bf8f | 701 | bool new; |
d62a17ae | 702 | |
703 | src_addr = sources + i; | |
704 | ||
705 | /* Lookup reported source (B) */ | |
5421bf8f DL |
706 | source = igmp_get_source_by_addr(group, *src_addr, &new); |
707 | if (!source) | |
708 | continue; | |
709 | ||
710 | if (!new) { | |
d62a17ae | 711 | /* If found, clear SEND flag (A*B) */ |
712 | IGMP_SOURCE_DONT_SEND(source->source_flags); | |
713 | --num_sources_tosend; | |
d62a17ae | 714 | } |
715 | ||
716 | /* (B)=GMI */ | |
dda4d23c | 717 | igmp_source_reset_gmi(group, source); |
d62a17ae | 718 | } |
719 | ||
720 | /* Send sources marked with SEND flag: Q(G,A-B) */ | |
721 | if (num_sources_tosend > 0) { | |
722 | source_query_send_by_flag(group, num_sources_tosend); | |
723 | } | |
12e41d03 DL |
724 | } |
725 | ||
a16db099 | 726 | static void toin_excl(struct gm_group *group, int num_sources, |
d62a17ae | 727 | struct in_addr *sources) |
12e41d03 | 728 | { |
d62a17ae | 729 | int num_sources_tosend; |
730 | int i; | |
731 | ||
732 | /* Set SEND flag for X (sources with timer > 0) */ | |
733 | num_sources_tosend = source_mark_send_flag_by_timer(group); | |
734 | ||
735 | /* Scan received sources (A) */ | |
736 | for (i = 0; i < num_sources; ++i) { | |
51700107 | 737 | struct gm_source *source; |
d62a17ae | 738 | struct in_addr *src_addr; |
5421bf8f | 739 | bool new; |
d62a17ae | 740 | |
741 | src_addr = sources + i; | |
742 | ||
743 | /* Lookup reported source (A) */ | |
5421bf8f DL |
744 | source = igmp_get_source_by_addr(group, *src_addr, &new); |
745 | if (!source) | |
746 | continue; | |
747 | ||
748 | if (source->t_source_timer) { | |
749 | /* If found and timer running, clear SEND flag | |
750 | * (X*A) */ | |
751 | IGMP_SOURCE_DONT_SEND(source->source_flags); | |
752 | --num_sources_tosend; | |
d62a17ae | 753 | } |
754 | ||
755 | /* (A)=GMI */ | |
dda4d23c | 756 | igmp_source_reset_gmi(group, source); |
d62a17ae | 757 | } |
758 | ||
759 | /* Send sources marked with SEND flag: Q(G,X-A) */ | |
760 | if (num_sources_tosend > 0) { | |
761 | source_query_send_by_flag(group, num_sources_tosend); | |
762 | } | |
763 | ||
764 | /* Send Q(G) */ | |
765 | group_query_send(group); | |
12e41d03 DL |
766 | } |
767 | ||
c5f76fad | 768 | void igmpv3_report_toin(struct gm_sock *igmp, struct in_addr from, |
d62a17ae | 769 | struct in_addr group_addr, int num_sources, |
770 | struct in_addr *sources) | |
12e41d03 | 771 | { |
d62a17ae | 772 | struct interface *ifp = igmp->interface; |
a16db099 | 773 | struct gm_group *group; |
d62a17ae | 774 | |
15569c58 | 775 | on_trace(__func__, ifp, from, group_addr, num_sources, sources); |
d62a17ae | 776 | |
777 | /* | |
778 | * If the requested filter mode is INCLUDE *and* the requested source | |
779 | * list is empty, then the entry corresponding to the requested | |
780 | * interface and multicast address is deleted if present. If no such | |
781 | * entry is present, the request is ignored. | |
782 | */ | |
783 | if (num_sources) { | |
b97047ed | 784 | /* non-existent group is created as INCLUDE {empty} */ |
d62a17ae | 785 | group = igmp_add_group_by_addr(igmp, group_addr); |
786 | if (!group) { | |
787 | return; | |
788 | } | |
789 | } else { | |
790 | group = find_group_by_addr(igmp, group_addr); | |
791 | if (!group) | |
792 | return; | |
793 | } | |
794 | ||
795 | if (group->group_filtermode_isexcl) { | |
796 | /* EXCLUDE mode */ | |
797 | toin_excl(group, num_sources, sources); | |
798 | } else { | |
799 | /* INCLUDE mode */ | |
800 | toin_incl(group, num_sources, sources); | |
801 | } | |
12e41d03 DL |
802 | } |
803 | ||
a16db099 | 804 | static void toex_incl(struct gm_group *group, int num_sources, |
d62a17ae | 805 | struct in_addr *sources) |
12e41d03 | 806 | { |
d62a17ae | 807 | int num_sources_tosend = 0; |
808 | int i; | |
809 | ||
df5dfb77 | 810 | assert(!group->group_filtermode_isexcl); |
d62a17ae | 811 | |
812 | /* Set DELETE flag for all known sources (A) */ | |
813 | source_mark_delete_flag(group); | |
814 | ||
815 | /* Clear off SEND flag from all known sources (A) */ | |
816 | source_clear_send_flag(group->group_source_list); | |
817 | ||
818 | /* Scan received sources (B) */ | |
819 | for (i = 0; i < num_sources; ++i) { | |
51700107 | 820 | struct gm_source *source; |
d62a17ae | 821 | struct in_addr *src_addr; |
5421bf8f | 822 | bool new; |
d62a17ae | 823 | |
824 | src_addr = sources + i; | |
825 | ||
826 | /* Lookup reported source (B) */ | |
5421bf8f DL |
827 | source = igmp_get_source_by_addr(group, *src_addr, &new); |
828 | if (!new) { | |
d62a17ae | 829 | /* If found, clear deletion flag: (A*B) */ |
830 | IGMP_SOURCE_DONT_DELETE(source->source_flags); | |
831 | /* and set SEND flag (A*B) */ | |
832 | IGMP_SOURCE_DO_SEND(source->source_flags); | |
833 | ++num_sources_tosend; | |
d62a17ae | 834 | } |
835 | ||
836 | } /* Scan received sources (B) */ | |
837 | ||
838 | group->group_filtermode_isexcl = 1; /* boolean=true */ | |
839 | ||
840 | /* Delete all sources marked with DELETE flag (A-B) */ | |
841 | source_delete_by_flag(group->group_source_list); | |
842 | ||
843 | /* Send sources marked with SEND flag: Q(G,A*B) */ | |
844 | if (num_sources_tosend > 0) { | |
845 | source_query_send_by_flag(group, num_sources_tosend); | |
846 | } | |
847 | ||
df5dfb77 | 848 | assert(group->group_filtermode_isexcl); |
d62a17ae | 849 | |
850 | group_exclude_fwd_anysrc_ifempty(group); | |
12e41d03 DL |
851 | } |
852 | ||
a16db099 | 853 | static void toex_excl(struct gm_group *group, int num_sources, |
d62a17ae | 854 | struct in_addr *sources) |
12e41d03 | 855 | { |
d62a17ae | 856 | int num_sources_tosend = 0; |
857 | int i; | |
858 | ||
859 | /* set DELETE flag for all known sources (X,Y) */ | |
860 | source_mark_delete_flag(group); | |
861 | ||
862 | /* clear off SEND flag from all known sources (X,Y) */ | |
863 | source_clear_send_flag(group->group_source_list); | |
864 | ||
865 | if (num_sources == 0) { | |
51700107 | 866 | struct gm_source *source; |
d62a17ae | 867 | struct in_addr any = {.s_addr = INADDR_ANY}; |
868 | ||
869 | source = igmp_find_source_by_addr(group, any); | |
870 | if (source) | |
871 | IGMP_SOURCE_DONT_DELETE(source->source_flags); | |
872 | } | |
873 | ||
874 | /* scan received sources (A) */ | |
875 | for (i = 0; i < num_sources; ++i) { | |
51700107 | 876 | struct gm_source *source; |
d62a17ae | 877 | struct in_addr *src_addr; |
5421bf8f | 878 | bool new; |
d62a17ae | 879 | |
880 | src_addr = sources + i; | |
881 | ||
882 | /* lookup reported source (A) in known sources (X,Y) */ | |
5421bf8f DL |
883 | source = igmp_get_source_by_addr(group, *src_addr, &new); |
884 | if (!source) | |
885 | continue; | |
886 | ||
887 | if (!new) { | |
d62a17ae | 888 | /* if found, clear off DELETE flag from reported source |
889 | * (A) */ | |
890 | IGMP_SOURCE_DONT_DELETE(source->source_flags); | |
891 | } else { | |
892 | /* if not found, create source with Group Timer: | |
893 | * (A-X-Y)=Group Timer */ | |
894 | long group_timer_msec; | |
d62a17ae | 895 | |
df5dfb77 | 896 | assert(!source->t_source_timer); /* timer == 0 */ |
d62a17ae | 897 | group_timer_msec = igmp_group_timer_remain_msec(group); |
898 | igmp_source_timer_on(group, source, group_timer_msec); | |
df5dfb77 | 899 | assert(source->t_source_timer); /* (A-X-Y) timer > 0 */ |
d62a17ae | 900 | |
901 | /* make sure source is created with DELETE flag unset */ | |
df5dfb77 | 902 | assert(!IGMP_SOURCE_TEST_DELETE(source->source_flags)); |
d62a17ae | 903 | } |
904 | ||
905 | /* make sure reported source has DELETE flag unset */ | |
df5dfb77 | 906 | assert(!IGMP_SOURCE_TEST_DELETE(source->source_flags)); |
d62a17ae | 907 | |
908 | if (source->t_source_timer) { | |
909 | /* if source timer>0 mark SEND flag: Q(G,A-Y) */ | |
910 | IGMP_SOURCE_DO_SEND(source->source_flags); | |
911 | ++num_sources_tosend; | |
912 | } | |
913 | ||
914 | } /* scan received sources (A) */ | |
915 | ||
916 | /* | |
917 | delete all sources marked with DELETE flag: | |
918 | Delete (X-A) | |
919 | Delete (Y-A) | |
920 | */ | |
921 | source_delete_by_flag(group->group_source_list); | |
922 | ||
923 | /* send sources marked with SEND flag: Q(G,A-Y) */ | |
924 | if (num_sources_tosend > 0) { | |
925 | source_query_send_by_flag(group, num_sources_tosend); | |
926 | } | |
12e41d03 DL |
927 | } |
928 | ||
c5f76fad | 929 | void igmpv3_report_toex(struct gm_sock *igmp, struct in_addr from, |
d62a17ae | 930 | struct in_addr group_addr, int num_sources, |
931 | struct in_addr *sources) | |
12e41d03 | 932 | { |
d62a17ae | 933 | struct interface *ifp = igmp->interface; |
a16db099 | 934 | struct gm_group *group; |
d62a17ae | 935 | |
15569c58 | 936 | on_trace(__func__, ifp, from, group_addr, num_sources, sources); |
d62a17ae | 937 | |
b97047ed | 938 | /* non-existent group is created as INCLUDE {empty} */ |
d62a17ae | 939 | group = igmp_add_group_by_addr(igmp, group_addr); |
940 | if (!group) { | |
941 | return; | |
942 | } | |
943 | ||
944 | if (group->group_filtermode_isexcl) { | |
945 | /* EXCLUDE mode */ | |
946 | toex_excl(group, num_sources, sources); | |
947 | } else { | |
948 | /* INCLUDE mode */ | |
949 | toex_incl(group, num_sources, sources); | |
df5dfb77 | 950 | assert(group->group_filtermode_isexcl); |
d62a17ae | 951 | } |
df5dfb77 | 952 | assert(group->group_filtermode_isexcl); |
d62a17ae | 953 | |
954 | /* Group Timer=GMI */ | |
955 | igmp_group_reset_gmi(group); | |
12e41d03 DL |
956 | } |
957 | ||
c5f76fad | 958 | void igmpv3_report_allow(struct gm_sock *igmp, struct in_addr from, |
d62a17ae | 959 | struct in_addr group_addr, int num_sources, |
960 | struct in_addr *sources) | |
12e41d03 | 961 | { |
15569c58 DA |
962 | on_trace(__func__, igmp->interface, from, group_addr, num_sources, |
963 | sources); | |
12e41d03 | 964 | |
d62a17ae | 965 | allow(igmp, from, group_addr, num_sources, sources); |
12e41d03 DL |
966 | } |
967 | ||
a16db099 | 968 | static void igmp_send_query_group(struct gm_group *group, char *query_buf, |
dda4d23c DL |
969 | size_t query_buf_size, int num_sources, |
970 | int s_flag) | |
971 | { | |
972 | struct interface *ifp = group->interface; | |
973 | struct pim_interface *pim_ifp = ifp->info; | |
c5f76fad | 974 | struct gm_sock *igmp; |
dda4d23c DL |
975 | struct listnode *sock_node; |
976 | ||
18adcff1 | 977 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) { |
29e58225 | 978 | igmp_send_query( |
278912ea DA |
979 | pim_ifp->igmp_version, group, query_buf, query_buf_size, |
980 | num_sources, group->group_addr, group->group_addr, | |
18adcff1 | 981 | pim_ifp->gm_specific_query_max_response_time_dsec, |
278912ea | 982 | s_flag, igmp); |
dda4d23c DL |
983 | } |
984 | } | |
985 | ||
12e41d03 DL |
986 | /* |
987 | RFC3376: 6.6.3.1. Building and Sending Group Specific Queries | |
988 | ||
989 | When transmitting a group specific query, if the group timer is | |
990 | larger than LMQT, the "Suppress Router-Side Processing" bit is set | |
991 | in the query message. | |
992 | */ | |
a16db099 | 993 | static void group_retransmit_group(struct gm_group *group) |
12e41d03 | 994 | { |
d62a17ae | 995 | struct pim_interface *pim_ifp; |
996 | long lmqc; /* Last Member Query Count */ | |
997 | long lmqi_msec; /* Last Member Query Interval */ | |
998 | long lmqt_msec; /* Last Member Query Time */ | |
999 | int s_flag; | |
1000 | int query_buf_size; | |
1001 | ||
dda4d23c | 1002 | pim_ifp = group->interface->info; |
d62a17ae | 1003 | |
29e58225 | 1004 | if (pim_ifp->igmp_version == 3) { |
d62a17ae | 1005 | query_buf_size = PIM_IGMP_BUFSIZE_WRITE; |
1006 | } else { | |
1007 | query_buf_size = IGMP_V12_MSG_SIZE; | |
1008 | } | |
1009 | ||
1010 | char query_buf[query_buf_size]; | |
1011 | ||
18adcff1 SG |
1012 | lmqc = pim_ifp->gm_last_member_query_count; |
1013 | lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec; | |
d62a17ae | 1014 | lmqt_msec = lmqc * lmqi_msec; |
1015 | ||
1016 | /* | |
1017 | RFC3376: 6.6.3.1. Building and Sending Group Specific Queries | |
1018 | ||
1019 | When transmitting a group specific query, if the group timer is | |
1020 | larger than LMQT, the "Suppress Router-Side Processing" bit is set | |
1021 | in the query message. | |
1022 | */ | |
1023 | s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec; | |
1024 | ||
a96d64b0 | 1025 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 1026 | char group_str[INET_ADDRSTRLEN]; |
1027 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
1028 | sizeof(group_str)); | |
1029 | zlog_debug( | |
1030 | "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d", | |
dda4d23c | 1031 | group_str, group->interface->name, s_flag, |
d62a17ae | 1032 | group->group_specific_query_retransmit_count); |
1033 | } | |
1034 | ||
1035 | /* | |
1036 | RFC3376: 4.1.12. IP Destination Addresses for Queries | |
1037 | ||
1038 | Group-Specific and Group-and-Source-Specific Queries are sent with | |
1039 | an IP destination address equal to the multicast address of | |
1040 | interest. | |
1041 | */ | |
1042 | ||
dda4d23c | 1043 | igmp_send_query_group(group, query_buf, sizeof(query_buf), 0, s_flag); |
12e41d03 DL |
1044 | } |
1045 | ||
1046 | /* | |
1047 | RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries | |
1048 | ||
1049 | When building a group and source specific query for a group G, two | |
1050 | separate query messages are sent for the group. The first one has | |
1051 | the "Suppress Router-Side Processing" bit set and contains all the | |
1052 | sources with retransmission state and timers greater than LMQT. The | |
1053 | second has the "Suppress Router-Side Processing" bit clear and | |
1054 | contains all the sources with retransmission state and timers lower | |
1055 | or equal to LMQT. If either of the two calculated messages does not | |
1056 | contain any sources, then its transmission is suppressed. | |
1057 | */ | |
a16db099 | 1058 | static int group_retransmit_sources(struct gm_group *group, |
12e41d03 DL |
1059 | int send_with_sflag_set) |
1060 | { | |
d62a17ae | 1061 | struct pim_interface *pim_ifp; |
1062 | long lmqc; /* Last Member Query Count */ | |
1063 | long lmqi_msec; /* Last Member Query Interval */ | |
1064 | long lmqt_msec; /* Last Member Query Time */ | |
1065 | char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */ | |
1066 | char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */ | |
1067 | int query_buf1_max_sources; | |
1068 | int query_buf2_max_sources; | |
1069 | struct in_addr *source_addr1; | |
1070 | struct in_addr *source_addr2; | |
1071 | int num_sources_tosend1; | |
1072 | int num_sources_tosend2; | |
1073 | struct listnode *src_node; | |
51700107 | 1074 | struct gm_source *src; |
d62a17ae | 1075 | int num_retransmit_sources_left = 0; |
1076 | ||
1077 | source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); | |
1078 | source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); | |
1079 | ||
dda4d23c | 1080 | pim_ifp = group->interface->info; |
d62a17ae | 1081 | |
18adcff1 SG |
1082 | lmqc = pim_ifp->gm_last_member_query_count; |
1083 | lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec; | |
d62a17ae | 1084 | lmqt_msec = lmqc * lmqi_msec; |
1085 | ||
1086 | /* Scan all group sources */ | |
1087 | for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { | |
1088 | ||
1089 | /* Source has retransmission state? */ | |
1090 | if (src->source_query_retransmit_count < 1) | |
1091 | continue; | |
1092 | ||
1093 | if (--src->source_query_retransmit_count > 0) { | |
1094 | ++num_retransmit_sources_left; | |
1095 | } | |
1096 | ||
1097 | /* Copy source address into appropriate query buffer */ | |
1098 | if (igmp_source_timer_remain_msec(src) > lmqt_msec) { | |
1099 | *source_addr1 = src->source_addr; | |
1100 | ++source_addr1; | |
1101 | } else { | |
1102 | *source_addr2 = src->source_addr; | |
1103 | ++source_addr2; | |
1104 | } | |
1105 | } | |
1106 | ||
1107 | num_sources_tosend1 = | |
1108 | source_addr1 | |
1109 | - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); | |
1110 | num_sources_tosend2 = | |
1111 | source_addr2 | |
1112 | - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); | |
1113 | ||
a96d64b0 | 1114 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 1115 | char group_str[INET_ADDRSTRLEN]; |
1116 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
1117 | sizeof(group_str)); | |
1118 | zlog_debug( | |
1119 | "retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d", | |
dda4d23c | 1120 | group_str, group->interface->name, num_sources_tosend1, |
d62a17ae | 1121 | num_sources_tosend2, send_with_sflag_set, |
1122 | num_retransmit_sources_left); | |
1123 | } | |
1124 | ||
1125 | if (num_sources_tosend1 > 0) { | |
1126 | /* | |
1127 | Send group-and-source-specific query with s_flag set and all | |
1128 | sources with timers greater than LMQT. | |
1129 | */ | |
1130 | ||
1131 | if (send_with_sflag_set) { | |
1132 | ||
1133 | query_buf1_max_sources = | |
1134 | (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) | |
1135 | >> 2; | |
1136 | if (num_sources_tosend1 > query_buf1_max_sources) { | |
1137 | char group_str[INET_ADDRSTRLEN]; | |
1138 | pim_inet4_dump("<group?>", group->group_addr, | |
1139 | group_str, sizeof(group_str)); | |
1140 | zlog_warn( | |
1141 | "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)", | |
5e81f5dd | 1142 | __func__, group_str, |
dda4d23c | 1143 | group->interface->name, |
d62a17ae | 1144 | num_sources_tosend1, sizeof(query_buf1), |
1145 | query_buf1_max_sources); | |
1146 | } else { | |
1147 | /* | |
1148 | RFC3376: 4.1.12. IP Destination Addresses for | |
1149 | Queries | |
1150 | ||
1151 | Group-Specific and Group-and-Source-Specific | |
1152 | Queries are sent with | |
1153 | an IP destination address equal to the | |
1154 | multicast address of | |
1155 | interest. | |
1156 | */ | |
1157 | ||
a1a4295a DL |
1158 | igmp_send_query_group( |
1159 | group, query_buf1, sizeof(query_buf1), | |
1160 | num_sources_tosend1, 1 /* s_flag */); | |
d62a17ae | 1161 | } |
1162 | ||
1163 | } /* send_with_sflag_set */ | |
1164 | } | |
1165 | ||
1166 | if (num_sources_tosend2 > 0) { | |
1167 | /* | |
1168 | Send group-and-source-specific query with s_flag clear and all | |
1169 | sources with timers lower or equal to LMQT. | |
1170 | */ | |
1171 | ||
1172 | query_buf2_max_sources = | |
1173 | (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2; | |
1174 | if (num_sources_tosend2 > query_buf2_max_sources) { | |
1175 | char group_str[INET_ADDRSTRLEN]; | |
1176 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
1177 | sizeof(group_str)); | |
1178 | zlog_warn( | |
1179 | "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)", | |
dda4d23c | 1180 | __func__, group_str, group->interface->name, |
5e81f5dd DS |
1181 | num_sources_tosend2, sizeof(query_buf2), |
1182 | query_buf2_max_sources); | |
d62a17ae | 1183 | } else { |
1184 | /* | |
1185 | RFC3376: 4.1.12. IP Destination Addresses for Queries | |
1186 | ||
1187 | Group-Specific and Group-and-Source-Specific Queries | |
1188 | are sent with | |
1189 | an IP destination address equal to the multicast | |
1190 | address of | |
1191 | interest. | |
1192 | */ | |
1193 | ||
a1a4295a DL |
1194 | igmp_send_query_group( |
1195 | group, query_buf2, sizeof(query_buf2), | |
1196 | num_sources_tosend2, 0 /* s_flag */); | |
d62a17ae | 1197 | } |
1198 | } | |
1199 | ||
1200 | return num_retransmit_sources_left; | |
12e41d03 DL |
1201 | } |
1202 | ||
cc9f21da | 1203 | static void igmp_group_retransmit(struct thread *t) |
12e41d03 | 1204 | { |
a16db099 | 1205 | struct gm_group *group; |
d62a17ae | 1206 | int num_retransmit_sources_left; |
1207 | int send_with_sflag_set; /* boolean */ | |
1208 | ||
1209 | group = THREAD_ARG(t); | |
1210 | ||
a96d64b0 | 1211 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 1212 | char group_str[INET_ADDRSTRLEN]; |
1213 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
1214 | sizeof(group_str)); | |
1215 | zlog_debug("group_retransmit_timer: group %s on %s", group_str, | |
dda4d23c | 1216 | group->interface->name); |
d62a17ae | 1217 | } |
1218 | ||
1219 | /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */ | |
1220 | if (group->group_specific_query_retransmit_count > 0) { | |
1221 | ||
1222 | /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */ | |
1223 | group_retransmit_group(group); | |
1224 | --group->group_specific_query_retransmit_count; | |
1225 | ||
1226 | /* | |
1227 | RFC3376: 6.6.3.2 | |
1228 | If a group specific query is scheduled to be transmitted at | |
1229 | the | |
1230 | same time as a group and source specific query for the same | |
1231 | group, | |
1232 | then transmission of the group and source specific message | |
1233 | with the | |
1234 | "Suppress Router-Side Processing" bit set may be suppressed. | |
1235 | */ | |
1236 | send_with_sflag_set = 0; /* boolean=false */ | |
1237 | } else { | |
1238 | send_with_sflag_set = 1; /* boolean=true */ | |
1239 | } | |
1240 | ||
1241 | /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */ | |
1242 | num_retransmit_sources_left = | |
1243 | group_retransmit_sources(group, send_with_sflag_set); | |
1244 | ||
1245 | /* | |
1246 | Keep group retransmit timer running if there is any retransmit | |
1247 | counter pending | |
1248 | */ | |
1249 | if ((num_retransmit_sources_left > 0) | |
1250 | || (group->group_specific_query_retransmit_count > 0)) { | |
1251 | group_retransmit_timer_on(group); | |
1252 | } | |
12e41d03 DL |
1253 | } |
1254 | ||
1255 | /* | |
1256 | group_retransmit_timer_on: | |
1257 | if group retransmit timer isn't running, starts it; | |
1258 | otherwise, do nothing | |
1259 | */ | |
a16db099 | 1260 | static void group_retransmit_timer_on(struct gm_group *group) |
12e41d03 | 1261 | { |
d62a17ae | 1262 | struct pim_interface *pim_ifp; |
1263 | long lmqi_msec; /* Last Member Query Interval */ | |
1264 | ||
1265 | /* if group retransmit timer is running, do nothing */ | |
1266 | if (group->t_group_query_retransmit_timer) { | |
1267 | return; | |
1268 | } | |
1269 | ||
dda4d23c | 1270 | pim_ifp = group->interface->info; |
d62a17ae | 1271 | |
18adcff1 | 1272 | lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec; |
d62a17ae | 1273 | |
a96d64b0 | 1274 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 1275 | char group_str[INET_ADDRSTRLEN]; |
1276 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
1277 | sizeof(group_str)); | |
1278 | zlog_debug( | |
1279 | "Scheduling %ld.%03ld sec retransmit timer for group %s on %s", | |
1280 | lmqi_msec / 1000, lmqi_msec % 1000, group_str, | |
dda4d23c | 1281 | group->interface->name); |
d62a17ae | 1282 | } |
1283 | ||
36417fcc DS |
1284 | thread_add_timer_msec(router->master, igmp_group_retransmit, group, |
1285 | lmqi_msec, | |
d62a17ae | 1286 | &group->t_group_query_retransmit_timer); |
12e41d03 DL |
1287 | } |
1288 | ||
a16db099 | 1289 | static long igmp_group_timer_remain_msec(struct gm_group *group) |
12e41d03 | 1290 | { |
d62a17ae | 1291 | return pim_time_timer_remain_msec(group->t_group_timer); |
12e41d03 DL |
1292 | } |
1293 | ||
51700107 | 1294 | static long igmp_source_timer_remain_msec(struct gm_source *source) |
12e41d03 | 1295 | { |
d62a17ae | 1296 | return pim_time_timer_remain_msec(source->t_source_timer); |
12e41d03 DL |
1297 | } |
1298 | ||
1299 | /* | |
1300 | RFC3376: 6.6.3.1. Building and Sending Group Specific Queries | |
1301 | */ | |
a16db099 | 1302 | static void group_query_send(struct gm_group *group) |
12e41d03 | 1303 | { |
59115451 | 1304 | struct pim_interface *pim_ifp; |
d62a17ae | 1305 | long lmqc; /* Last Member Query Count */ |
12e41d03 | 1306 | |
dda4d23c | 1307 | pim_ifp = group->interface->info; |
18adcff1 | 1308 | lmqc = pim_ifp->gm_last_member_query_count; |
12e41d03 | 1309 | |
d62a17ae | 1310 | /* lower group timer to lmqt */ |
1311 | igmp_group_timer_lower_to_lmqt(group); | |
12e41d03 | 1312 | |
d62a17ae | 1313 | /* reset retransmission counter */ |
1314 | group->group_specific_query_retransmit_count = lmqc; | |
12e41d03 | 1315 | |
d62a17ae | 1316 | /* immediately send group specific query (decrease retransmit counter by |
1317 | * 1)*/ | |
1318 | group_retransmit_group(group); | |
12e41d03 | 1319 | |
d62a17ae | 1320 | /* make sure group retransmit timer is running */ |
1321 | group_retransmit_timer_on(group); | |
12e41d03 DL |
1322 | } |
1323 | ||
1324 | /* | |
1325 | RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries | |
1326 | */ | |
a16db099 | 1327 | static void source_query_send_by_flag(struct gm_group *group, |
12e41d03 DL |
1328 | int num_sources_tosend) |
1329 | { | |
d62a17ae | 1330 | struct pim_interface *pim_ifp; |
1331 | struct listnode *src_node; | |
51700107 | 1332 | struct gm_source *src; |
d62a17ae | 1333 | long lmqc; /* Last Member Query Count */ |
1334 | long lmqi_msec; /* Last Member Query Interval */ | |
1335 | long lmqt_msec; /* Last Member Query Time */ | |
1336 | ||
df5dfb77 | 1337 | assert(num_sources_tosend > 0); |
d62a17ae | 1338 | |
dda4d23c | 1339 | pim_ifp = group->interface->info; |
d62a17ae | 1340 | |
18adcff1 SG |
1341 | lmqc = pim_ifp->gm_last_member_query_count; |
1342 | lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec; | |
d62a17ae | 1343 | lmqt_msec = lmqc * lmqi_msec; |
1344 | ||
1345 | /* | |
1346 | RFC3376: 6.6.3.2. Building and Sending Group and Source Specific | |
1347 | Queries | |
1348 | ||
1349 | (...) for each of the sources in X of group G, with source timer | |
1350 | larger | |
1351 | than LMQT: | |
1352 | o Set number of retransmissions for each source to [Last Member | |
1353 | Query Count]. | |
1354 | o Lower source timer to LMQT. | |
1355 | */ | |
1356 | for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { | |
1357 | if (IGMP_SOURCE_TEST_SEND(src->source_flags)) { | |
1358 | /* source "src" in X of group G */ | |
1359 | if (igmp_source_timer_remain_msec(src) > lmqt_msec) { | |
1360 | src->source_query_retransmit_count = lmqc; | |
1361 | igmp_source_timer_lower_to_lmqt(src); | |
1362 | } | |
1363 | } | |
1364 | } | |
1365 | ||
1366 | /* send group-and-source specific queries */ | |
1367 | group_retransmit_sources(group, 1 /* send_with_sflag_set=true */); | |
1368 | ||
1369 | /* make sure group retransmit timer is running */ | |
1370 | group_retransmit_timer_on(group); | |
12e41d03 DL |
1371 | } |
1372 | ||
a16db099 | 1373 | static void block_excl(struct gm_group *group, int num_sources, |
d62a17ae | 1374 | struct in_addr *sources) |
12e41d03 | 1375 | { |
d62a17ae | 1376 | int num_sources_tosend = 0; |
1377 | int i; | |
1378 | ||
1379 | /* 1. clear off SEND flag from all known sources (X,Y) */ | |
1380 | source_clear_send_flag(group->group_source_list); | |
1381 | ||
1382 | /* 2. scan received sources (A) */ | |
1383 | for (i = 0; i < num_sources; ++i) { | |
51700107 | 1384 | struct gm_source *source; |
d62a17ae | 1385 | struct in_addr *src_addr; |
5421bf8f | 1386 | bool new; |
d62a17ae | 1387 | |
1388 | src_addr = sources + i; | |
1389 | ||
1390 | /* lookup reported source (A) in known sources (X,Y) */ | |
5421bf8f DL |
1391 | source = igmp_get_source_by_addr(group, *src_addr, &new); |
1392 | if (!source) | |
1393 | continue; | |
1394 | ||
1395 | if (new) { | |
d62a17ae | 1396 | /* 3: if not found, create source with Group Timer: |
1397 | * (A-X-Y)=Group Timer */ | |
1398 | long group_timer_msec; | |
d62a17ae | 1399 | |
df5dfb77 | 1400 | assert(!source->t_source_timer); /* timer == 0 */ |
d62a17ae | 1401 | group_timer_msec = igmp_group_timer_remain_msec(group); |
1402 | igmp_source_timer_on(group, source, group_timer_msec); | |
df5dfb77 | 1403 | assert(source->t_source_timer); /* (A-X-Y) timer > 0 */ |
d62a17ae | 1404 | } |
1405 | ||
1406 | if (source->t_source_timer) { | |
1407 | /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */ | |
1408 | IGMP_SOURCE_DO_SEND(source->source_flags); | |
1409 | ++num_sources_tosend; | |
1410 | } | |
1411 | } | |
1412 | ||
1413 | /* 5. send sources marked with SEND flag: Q(G,A-Y) */ | |
1414 | if (num_sources_tosend > 0) { | |
1415 | source_query_send_by_flag(group, num_sources_tosend); | |
1416 | } | |
12e41d03 DL |
1417 | } |
1418 | ||
a16db099 | 1419 | static void block_incl(struct gm_group *group, int num_sources, |
d62a17ae | 1420 | struct in_addr *sources) |
12e41d03 | 1421 | { |
d62a17ae | 1422 | int num_sources_tosend = 0; |
1423 | int i; | |
1424 | ||
1425 | /* 1. clear off SEND flag from all known sources (B) */ | |
1426 | source_clear_send_flag(group->group_source_list); | |
1427 | ||
1428 | /* 2. scan received sources (A) */ | |
1429 | for (i = 0; i < num_sources; ++i) { | |
51700107 | 1430 | struct gm_source *source; |
d62a17ae | 1431 | struct in_addr *src_addr; |
1432 | ||
1433 | src_addr = sources + i; | |
1434 | ||
1435 | /* lookup reported source (A) in known sources (B) */ | |
1436 | source = igmp_find_source_by_addr(group, *src_addr); | |
1437 | if (source) { | |
1438 | /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */ | |
1439 | IGMP_SOURCE_DO_SEND(source->source_flags); | |
1440 | ++num_sources_tosend; | |
1441 | } | |
1442 | } | |
1443 | ||
1444 | /* 4. send sources marked with SEND flag: Q(G,A*B) */ | |
1445 | if (num_sources_tosend > 0) { | |
1446 | source_query_send_by_flag(group, num_sources_tosend); | |
1447 | } | |
12e41d03 DL |
1448 | } |
1449 | ||
c5f76fad | 1450 | void igmpv3_report_block(struct gm_sock *igmp, struct in_addr from, |
d62a17ae | 1451 | struct in_addr group_addr, int num_sources, |
1452 | struct in_addr *sources) | |
12e41d03 | 1453 | { |
d62a17ae | 1454 | struct interface *ifp = igmp->interface; |
a16db099 | 1455 | struct gm_group *group; |
d62a17ae | 1456 | |
15569c58 | 1457 | on_trace(__func__, ifp, from, group_addr, num_sources, sources); |
d62a17ae | 1458 | |
b97047ed | 1459 | /* non-existent group is created as INCLUDE {empty} */ |
d62a17ae | 1460 | group = igmp_add_group_by_addr(igmp, group_addr); |
1461 | if (!group) { | |
1462 | return; | |
1463 | } | |
1464 | ||
1465 | if (group->group_filtermode_isexcl) { | |
1466 | /* EXCLUDE mode */ | |
1467 | block_excl(group, num_sources, sources); | |
1468 | } else { | |
1469 | /* INCLUDE mode */ | |
1470 | block_incl(group, num_sources, sources); | |
1471 | } | |
12e41d03 DL |
1472 | } |
1473 | ||
a16db099 | 1474 | void igmp_group_timer_lower_to_lmqt(struct gm_group *group) |
12e41d03 | 1475 | { |
d62a17ae | 1476 | struct interface *ifp; |
1477 | struct pim_interface *pim_ifp; | |
1478 | char *ifname; | |
1479 | int lmqi_dsec; /* Last Member Query Interval */ | |
1480 | int lmqc; /* Last Member Query Count */ | |
1481 | int lmqt_msec; /* Last Member Query Time */ | |
1482 | ||
1483 | /* | |
1484 | RFC 3376: 6.2.2. Definition of Group Timers | |
1485 | ||
1486 | The group timer is only used when a group is in EXCLUDE mode and | |
1487 | it represents the time for the *filter-mode* of the group to | |
1488 | expire and switch to INCLUDE mode. | |
1489 | */ | |
1490 | if (!group->group_filtermode_isexcl) { | |
1491 | return; | |
1492 | } | |
1493 | ||
dda4d23c | 1494 | ifp = group->interface; |
d62a17ae | 1495 | pim_ifp = ifp->info; |
1496 | ifname = ifp->name; | |
1497 | ||
18adcff1 SG |
1498 | lmqi_dsec = pim_ifp->gm_specific_query_max_response_time_dsec; |
1499 | lmqc = pim_ifp->gm_last_member_query_count; | |
d62a17ae | 1500 | lmqt_msec = PIM_IGMP_LMQT_MSEC( |
1501 | lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ | |
1502 | ||
a96d64b0 | 1503 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 1504 | char group_str[INET_ADDRSTRLEN]; |
1505 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
1506 | sizeof(group_str)); | |
1507 | zlog_debug( | |
1508 | "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec", | |
15569c58 | 1509 | __func__, group_str, ifname, lmqc, lmqi_dsec, |
d62a17ae | 1510 | lmqt_msec); |
1511 | } | |
1512 | ||
df5dfb77 | 1513 | assert(group->group_filtermode_isexcl); |
d62a17ae | 1514 | |
1515 | igmp_group_timer_on(group, lmqt_msec, ifname); | |
12e41d03 DL |
1516 | } |
1517 | ||
51700107 | 1518 | void igmp_source_timer_lower_to_lmqt(struct gm_source *source) |
12e41d03 | 1519 | { |
a16db099 | 1520 | struct gm_group *group; |
d62a17ae | 1521 | struct interface *ifp; |
1522 | struct pim_interface *pim_ifp; | |
1523 | char *ifname; | |
1524 | int lmqi_dsec; /* Last Member Query Interval */ | |
1525 | int lmqc; /* Last Member Query Count */ | |
1526 | int lmqt_msec; /* Last Member Query Time */ | |
1527 | ||
1528 | group = source->source_group; | |
dda4d23c | 1529 | ifp = group->interface; |
d62a17ae | 1530 | pim_ifp = ifp->info; |
1531 | ifname = ifp->name; | |
1532 | ||
18adcff1 SG |
1533 | lmqi_dsec = pim_ifp->gm_specific_query_max_response_time_dsec; |
1534 | lmqc = pim_ifp->gm_last_member_query_count; | |
d62a17ae | 1535 | lmqt_msec = PIM_IGMP_LMQT_MSEC( |
1536 | lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ | |
1537 | ||
a96d64b0 | 1538 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 1539 | char group_str[INET_ADDRSTRLEN]; |
1540 | char source_str[INET_ADDRSTRLEN]; | |
1541 | pim_inet4_dump("<group?>", group->group_addr, group_str, | |
1542 | sizeof(group_str)); | |
1543 | pim_inet4_dump("<source?>", source->source_addr, source_str, | |
1544 | sizeof(source_str)); | |
1545 | zlog_debug( | |
1546 | "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec", | |
15569c58 DA |
1547 | __func__, group_str, source_str, ifname, lmqc, |
1548 | lmqi_dsec, lmqt_msec); | |
d62a17ae | 1549 | } |
1550 | ||
1551 | igmp_source_timer_on(group, source, lmqt_msec); | |
12e41d03 DL |
1552 | } |
1553 | ||
a16db099 | 1554 | void igmp_v3_send_query(struct gm_group *group, int fd, const char *ifname, |
d62a17ae | 1555 | char *query_buf, int query_buf_size, int num_sources, |
1556 | struct in_addr dst_addr, struct in_addr group_addr, | |
1557 | int query_max_response_time_dsec, uint8_t s_flag, | |
1558 | uint8_t querier_robustness_variable, | |
1559 | uint16_t querier_query_interval) | |
12e41d03 | 1560 | { |
d62a17ae | 1561 | ssize_t msg_size; |
1562 | uint8_t max_resp_code; | |
1563 | uint8_t qqic; | |
1564 | ssize_t sent; | |
1565 | struct sockaddr_in to; | |
1566 | socklen_t tolen; | |
1567 | uint16_t checksum; | |
1568 | ||
df5dfb77 | 1569 | assert(num_sources >= 0); |
d62a17ae | 1570 | |
1571 | msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2); | |
1572 | if (msg_size > query_buf_size) { | |
af4c2728 | 1573 | flog_err( |
450971aa | 1574 | EC_LIB_DEVELOPMENT, |
d62a17ae | 1575 | "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d", |
15569c58 | 1576 | __FILE__, __func__, msg_size, query_buf_size); |
d62a17ae | 1577 | return; |
1578 | } | |
1579 | ||
1580 | s_flag = PIM_FORCE_BOOLEAN(s_flag); | |
df5dfb77 | 1581 | assert((s_flag == 0) || (s_flag == 1)); |
d62a17ae | 1582 | |
1583 | max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec); | |
1584 | qqic = igmp_msg_encode16to8(querier_query_interval); | |
1585 | ||
1586 | /* | |
1587 | RFC 3376: 4.1.6. QRV (Querier's Robustness Variable) | |
1588 | ||
1589 | If non-zero, the QRV field contains the [Robustness Variable] | |
1590 | value used by the querier, i.e., the sender of the Query. If the | |
1591 | querier's [Robustness Variable] exceeds 7, the maximum value of | |
1592 | the QRV field, the QRV is set to zero. | |
1593 | */ | |
1594 | if (querier_robustness_variable > 7) { | |
1595 | querier_robustness_variable = 0; | |
1596 | } | |
1597 | ||
1598 | query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY; | |
1599 | query_buf[1] = max_resp_code; | |
1600 | *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = | |
1601 | 0; /* for computing checksum */ | |
1602 | memcpy(query_buf + 4, &group_addr, sizeof(struct in_addr)); | |
1603 | ||
1604 | query_buf[8] = (s_flag << 3) | querier_robustness_variable; | |
1605 | query_buf[9] = qqic; | |
1606 | *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = | |
1607 | htons(num_sources); | |
1608 | ||
1609 | checksum = in_cksum(query_buf, msg_size); | |
1610 | *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum; | |
1611 | ||
55eb347d | 1612 | if (PIM_DEBUG_GM_PACKETS) { |
d62a17ae | 1613 | char dst_str[INET_ADDRSTRLEN]; |
1614 | char group_str[INET_ADDRSTRLEN]; | |
1615 | pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str)); | |
1616 | pim_inet4_dump("<group?>", group_addr, group_str, | |
1617 | sizeof(group_str)); | |
1618 | zlog_debug( | |
1619 | "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x", | |
1620 | dst_str, ifname, group_str, num_sources, msg_size, | |
1621 | s_flag, querier_robustness_variable, | |
1622 | querier_query_interval, qqic); | |
1623 | } | |
1624 | ||
1625 | memset(&to, 0, sizeof(to)); | |
1626 | to.sin_family = AF_INET; | |
1627 | to.sin_addr = dst_addr; | |
1628 | tolen = sizeof(to); | |
1629 | ||
1630 | sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT, | |
1631 | (struct sockaddr *)&to, tolen); | |
1632 | if (sent != (ssize_t)msg_size) { | |
1633 | char dst_str[INET_ADDRSTRLEN]; | |
1634 | char group_str[INET_ADDRSTRLEN]; | |
1635 | pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str)); | |
1636 | pim_inet4_dump("<group?>", group_addr, group_str, | |
1637 | sizeof(group_str)); | |
1638 | if (sent < 0) { | |
1639 | zlog_warn( | |
1640 | "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s", | |
1641 | dst_str, ifname, group_str, msg_size, errno, | |
1642 | safe_strerror(errno)); | |
1643 | } else { | |
1644 | zlog_warn( | |
1645 | "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd", | |
1646 | dst_str, ifname, group_str, msg_size, sent); | |
1647 | } | |
1648 | return; | |
1649 | } | |
1650 | ||
1651 | /* | |
1652 | s_flag sanity test: s_flag must be set for general queries | |
1653 | ||
1654 | RFC 3376: 6.6.1. Timer Updates | |
1655 | ||
1656 | When a router sends or receives a query with a clear Suppress | |
1657 | Router-Side Processing flag, it must update its timers to reflect | |
1658 | the correct timeout values for the group or sources being queried. | |
1659 | ||
1660 | General queries don't trigger timer update. | |
1661 | */ | |
1662 | if (!s_flag) { | |
1663 | /* general query? */ | |
3ca68c9c | 1664 | if (group_addr.s_addr == INADDR_ANY) { |
d62a17ae | 1665 | char dst_str[INET_ADDRSTRLEN]; |
1666 | char group_str[INET_ADDRSTRLEN]; | |
1667 | pim_inet4_dump("<dst?>", dst_addr, dst_str, | |
1668 | sizeof(dst_str)); | |
1669 | pim_inet4_dump("<group?>", group_addr, group_str, | |
1670 | sizeof(group_str)); | |
1671 | zlog_warn( | |
1672 | "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!", | |
15569c58 | 1673 | __func__, dst_str, ifname, group_str, |
d62a17ae | 1674 | num_sources); |
1675 | } | |
1676 | } | |
b05b72e8 DW |
1677 | } |
1678 | ||
c5f76fad | 1679 | void igmp_v3_recv_query(struct gm_sock *igmp, const char *from_str, |
d62a17ae | 1680 | char *igmp_msg) |
b05b72e8 | 1681 | { |
d62a17ae | 1682 | struct interface *ifp; |
1683 | struct pim_interface *pim_ifp; | |
1684 | struct in_addr group_addr; | |
1685 | uint8_t resv_s_qrv = 0; | |
1686 | uint8_t s_flag = 0; | |
1687 | uint8_t qrv = 0; | |
1688 | int i; | |
1689 | ||
1690 | memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); | |
1691 | ifp = igmp->interface; | |
1692 | pim_ifp = ifp->info; | |
1693 | ||
1694 | /* | |
1695 | * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable) | |
1696 | * | |
1697 | * Routers adopt the QRV value from the most recently received Query | |
1698 | * as their own [Robustness Variable] value, unless that most | |
1699 | * recently received QRV was zero, in which case the receivers use | |
1700 | * the default [Robustness Variable] value specified in section 8.1 | |
1701 | * or a statically configured value. | |
1702 | */ | |
1703 | resv_s_qrv = igmp_msg[8]; | |
1704 | qrv = 7 & resv_s_qrv; | |
1705 | igmp->querier_robustness_variable = | |
18adcff1 | 1706 | qrv ? qrv : pim_ifp->gm_default_robustness_variable; |
d62a17ae | 1707 | |
1708 | /* | |
1709 | * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code) | |
1710 | * | |
1711 | * Multicast routers that are not the current querier adopt the QQI | |
1712 | * value from the most recently received Query as their own [Query | |
1713 | * Interval] value, unless that most recently received QQI was zero, | |
1714 | * in which case the receiving routers use the default. | |
1715 | */ | |
1716 | if (igmp->t_other_querier_timer) { | |
1717 | /* other querier present */ | |
1718 | uint8_t qqic; | |
1719 | uint16_t qqi; | |
1720 | qqic = igmp_msg[9]; | |
1721 | qqi = igmp_msg_decode8to16(qqic); | |
1722 | igmp->querier_query_interval = | |
18adcff1 | 1723 | qqi ? qqi : pim_ifp->gm_default_query_interval; |
d62a17ae | 1724 | |
a96d64b0 | 1725 | if (PIM_DEBUG_GM_TRACE) { |
d62a17ae | 1726 | char ifaddr_str[INET_ADDRSTRLEN]; |
1727 | pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, | |
1728 | sizeof(ifaddr_str)); | |
1729 | zlog_debug( | |
1730 | "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)", | |
1731 | ifaddr_str, | |
1732 | qqi ? "recv-non-default" : "default", | |
1733 | igmp->querier_query_interval, qqic, from_str); | |
1734 | } | |
1735 | } | |
1736 | ||
1737 | /* | |
1738 | * RFC 3376: 6.6.1. Timer Updates | |
1739 | * | |
1740 | * When a router sends or receives a query with a clear Suppress | |
1741 | * Router-Side Processing flag, it must update its timers to reflect | |
1742 | * the correct timeout values for the group or sources being queried. | |
1743 | * | |
1744 | * General queries don't trigger timer update. | |
1745 | */ | |
1746 | s_flag = (1 << 3) & resv_s_qrv; | |
1747 | ||
1748 | if (!s_flag) { | |
1749 | /* s_flag is clear */ | |
1750 | ||
3ca68c9c | 1751 | if (group_addr.s_addr == INADDR_ANY) { |
d62a17ae | 1752 | /* this is a general query */ |
1753 | /* log that general query should have the s_flag set */ | |
1754 | zlog_warn( | |
1755 | "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear", | |
1756 | from_str, ifp->name); | |
1757 | } else { | |
a16db099 | 1758 | struct gm_group *group; |
d62a17ae | 1759 | |
1760 | /* this is a non-general query: perform timer updates */ | |
1761 | ||
1762 | group = find_group_by_addr(igmp, group_addr); | |
1763 | if (group) { | |
1764 | int recv_num_sources = ntohs(*( | |
1765 | uint16_t | |
1766 | *)(igmp_msg | |
1767 | + IGMP_V3_NUMSOURCES_OFFSET)); | |
1768 | ||
1769 | /* | |
1770 | * RFC 3376: 6.6.1. Timer Updates | |
1771 | * Query Q(G,A): Source Timer for sources in A | |
1772 | * are lowered to LMQT | |
1773 | * Query Q(G): Group Timer is lowered to LMQT | |
1774 | */ | |
1775 | if (recv_num_sources < 1) { | |
1776 | /* Query Q(G): Group Timer is lowered to | |
1777 | * LMQT */ | |
1778 | ||
1779 | igmp_group_timer_lower_to_lmqt(group); | |
1780 | } else { | |
1781 | /* Query Q(G,A): Source Timer for | |
1782 | * sources in A are lowered to LMQT */ | |
1783 | ||
1784 | /* Scan sources in query and lower their | |
1785 | * timers to LMQT */ | |
1786 | struct in_addr *sources = | |
1787 | (struct in_addr | |
1788 | *)(igmp_msg | |
1789 | + IGMP_V3_SOURCES_OFFSET); | |
1790 | for (i = 0; i < recv_num_sources; ++i) { | |
1791 | struct in_addr src_addr; | |
51700107 | 1792 | struct gm_source *src; |
d62a17ae | 1793 | memcpy(&src_addr, sources + i, |
1794 | sizeof(struct in_addr)); | |
1795 | src = igmp_find_source_by_addr( | |
1796 | group, src_addr); | |
1797 | if (src) { | |
1798 | igmp_source_timer_lower_to_lmqt( | |
1799 | src); | |
1800 | } | |
1801 | } | |
1802 | } | |
1803 | } else { | |
1804 | char group_str[INET_ADDRSTRLEN]; | |
1805 | pim_inet4_dump("<group?>", group_addr, | |
1806 | group_str, sizeof(group_str)); | |
1807 | zlog_warn( | |
1808 | "IGMP query v3 from %s on %s: could not find group %s for timer update", | |
1809 | from_str, ifp->name, group_str); | |
1810 | } | |
1811 | } | |
1812 | } /* s_flag is clear: timer updates */ | |
b05b72e8 DW |
1813 | } |
1814 | ||
f6053ff2 A |
1815 | static bool igmp_pkt_grp_addr_ok(struct interface *ifp, const char *from_str, |
1816 | struct in_addr grp, int rec_type) | |
1817 | { | |
1818 | struct pim_interface *pim_ifp; | |
1819 | struct in_addr grp_addr; | |
1820 | ||
1821 | pim_ifp = ifp->info; | |
1822 | ||
1823 | /* determine filtering status for group */ | |
1824 | if (pim_is_group_filtered(pim_ifp, &grp)) { | |
55eb347d | 1825 | if (PIM_DEBUG_GM_PACKETS) { |
f6053ff2 A |
1826 | zlog_debug( |
1827 | "Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s", | |
1828 | &grp.s_addr, from_str, ifp->name, | |
1829 | pim_ifp->boundary_oil_plist); | |
1830 | } | |
1831 | return false; | |
1832 | } | |
1833 | ||
1834 | /* | |
1835 | * If we receive a igmp report with the group in 224.0.0.0/24 | |
1836 | * then we should ignore it | |
1837 | */ | |
1838 | ||
1839 | grp_addr.s_addr = ntohl(grp.s_addr); | |
1840 | ||
1841 | if (pim_is_group_224_0_0_0_24(grp_addr)) { | |
55eb347d | 1842 | if (PIM_DEBUG_GM_PACKETS) { |
f6053ff2 A |
1843 | zlog_debug( |
1844 | "Ignoring IGMPv3 group record %pI4 from %s on %s group range falls in 224.0.0.0/24", | |
1845 | &grp.s_addr, from_str, ifp->name); | |
1846 | } | |
1847 | return false; | |
1848 | } | |
1849 | ||
1850 | /* | |
1851 | * RFC 4604 | |
1852 | * section 2.2.1 | |
1853 | * EXCLUDE mode does not apply to SSM addresses, and an SSM-aware router | |
1854 | * will ignore MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE requests in | |
1855 | * the SSM range. | |
1856 | */ | |
1857 | if (pim_is_grp_ssm(pim_ifp->pim, grp)) { | |
1858 | switch (rec_type) { | |
1859 | case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE: | |
1860 | case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE: | |
55eb347d | 1861 | if (PIM_DEBUG_GM_PACKETS) { |
f6053ff2 A |
1862 | zlog_debug( |
1863 | "Ignoring IGMPv3 group record %pI4 from %s on %s exclude mode in SSM range", | |
1864 | &grp.s_addr, from_str, ifp->name); | |
1865 | } | |
1866 | return false; | |
1867 | } | |
1868 | } | |
1869 | ||
1870 | return true; | |
1871 | } | |
1872 | ||
c5f76fad | 1873 | int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from, |
d62a17ae | 1874 | const char *from_str, char *igmp_msg, int igmp_msg_len) |
b05b72e8 | 1875 | { |
d62a17ae | 1876 | int num_groups; |
1877 | uint8_t *group_record; | |
1878 | uint8_t *report_pastend = (uint8_t *)igmp_msg + igmp_msg_len; | |
1879 | struct interface *ifp = igmp->interface; | |
c7c5d685 | 1880 | struct pim_interface *pim_ifp = ifp->info; |
d62a17ae | 1881 | int i; |
b0f525a8 | 1882 | |
f83f3966 MS |
1883 | if (igmp->mtrace_only) |
1884 | return 0; | |
1885 | ||
d62a17ae | 1886 | if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) { |
1887 | zlog_warn( | |
1888 | "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d", | |
1889 | from_str, ifp->name, igmp_msg_len, | |
1890 | IGMP_V3_MSG_MIN_SIZE); | |
1891 | return -1; | |
1892 | } | |
1893 | ||
9041c30a | 1894 | if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) { |
d62a17ae | 1895 | zlog_warn( |
9041c30a MR |
1896 | "Recv IGMPv3 report from %s on %s with invalid checksum", |
1897 | from_str, ifp->name); | |
d62a17ae | 1898 | return -1; |
1899 | } | |
1900 | ||
21313cbf | 1901 | /* Collecting IGMP Rx stats */ |
f2058cb4 | 1902 | igmp->igmp_stats.report_v3++; |
21313cbf | 1903 | |
c7c5d685 DS |
1904 | if (pim_ifp->igmp_version == 2) { |
1905 | zlog_warn( | |
1906 | "Received Version 3 packet but interface: %s is configured for version 2", | |
1907 | ifp->name); | |
1908 | return -1; | |
1909 | } | |
1910 | ||
d62a17ae | 1911 | num_groups = ntohs( |
1912 | *(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET)); | |
1913 | if (num_groups < 1) { | |
1914 | zlog_warn( | |
1915 | "Recv IGMP report v3 from %s on %s: missing group records", | |
1916 | from_str, ifp->name); | |
1917 | return -1; | |
1918 | } | |
1919 | ||
55eb347d | 1920 | if (PIM_DEBUG_GM_PACKETS) { |
d62a17ae | 1921 | zlog_debug( |
9041c30a MR |
1922 | "Recv IGMP report v3 from %s on %s: size=%d groups=%d", |
1923 | from_str, ifp->name, igmp_msg_len, num_groups); | |
d62a17ae | 1924 | } |
1925 | ||
1926 | group_record = (uint8_t *)igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET; | |
1927 | ||
1928 | /* Scan groups */ | |
1929 | for (i = 0; i < num_groups; ++i) { | |
1930 | struct in_addr rec_group; | |
1931 | uint8_t *sources; | |
1932 | uint8_t *src; | |
1933 | int rec_type; | |
1934 | int rec_auxdatalen; | |
1935 | int rec_num_sources; | |
1936 | int j; | |
d62a17ae | 1937 | |
1938 | if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) | |
1939 | > report_pastend) { | |
1940 | zlog_warn( | |
1941 | "Recv IGMP report v3 from %s on %s: group record beyond report end", | |
1942 | from_str, ifp->name); | |
1943 | return -1; | |
1944 | } | |
1945 | ||
1946 | rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET]; | |
1947 | rec_auxdatalen = | |
1948 | group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET]; | |
1949 | rec_num_sources = ntohs(*( | |
1950 | uint16_t *)(group_record | |
1951 | + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET)); | |
1952 | ||
1953 | memcpy(&rec_group, | |
1954 | group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, | |
1955 | sizeof(struct in_addr)); | |
1956 | ||
55eb347d | 1957 | if (PIM_DEBUG_GM_PACKETS) { |
d62a17ae | 1958 | zlog_debug( |
ee2bbf7c | 1959 | " Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%pI4", |
d62a17ae | 1960 | from_str, ifp->name, i, rec_type, |
1961 | rec_auxdatalen, rec_num_sources, | |
ee2bbf7c | 1962 | &rec_group); |
d62a17ae | 1963 | } |
1964 | ||
1965 | /* Scan sources */ | |
1966 | ||
1967 | sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET; | |
1968 | ||
1969 | for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) { | |
1970 | ||
1971 | if ((src + 4) > report_pastend) { | |
1972 | zlog_warn( | |
1973 | "Recv IGMP report v3 from %s on %s: group source beyond report end", | |
1974 | from_str, ifp->name); | |
1975 | return -1; | |
1976 | } | |
1977 | ||
55eb347d | 1978 | if (PIM_DEBUG_GM_PACKETS) { |
d62a17ae | 1979 | char src_str[200]; |
1980 | ||
1981 | if (!inet_ntop(AF_INET, src, src_str, | |
1982 | sizeof(src_str))) | |
772270f3 QY |
1983 | snprintf(src_str, sizeof(src_str), |
1984 | "<source?>"); | |
d62a17ae | 1985 | |
1986 | zlog_debug( | |
ee2bbf7c | 1987 | " Recv IGMP report v3 from %s on %s: record=%d group=%pI4 source=%s", |
d62a17ae | 1988 | from_str, ifp->name, i, |
ee2bbf7c | 1989 | &rec_group, src_str); |
d62a17ae | 1990 | } |
1991 | } /* for (sources) */ | |
1992 | ||
1993 | ||
f6053ff2 | 1994 | if (igmp_pkt_grp_addr_ok(ifp, from_str, rec_group, rec_type)) |
d62a17ae | 1995 | switch (rec_type) { |
1996 | case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE: | |
1997 | igmpv3_report_isin(igmp, from, rec_group, | |
1998 | rec_num_sources, | |
1999 | (struct in_addr *)sources); | |
2000 | break; | |
2001 | case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE: | |
2002 | igmpv3_report_isex( | |
2003 | igmp, from, rec_group, rec_num_sources, | |
2004 | (struct in_addr *)sources, 0); | |
2005 | break; | |
2006 | case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE: | |
2007 | igmpv3_report_toin(igmp, from, rec_group, | |
2008 | rec_num_sources, | |
2009 | (struct in_addr *)sources); | |
2010 | break; | |
2011 | case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE: | |
2012 | igmpv3_report_toex(igmp, from, rec_group, | |
2013 | rec_num_sources, | |
2014 | (struct in_addr *)sources); | |
2015 | break; | |
2016 | case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES: | |
2017 | igmpv3_report_allow(igmp, from, rec_group, | |
2018 | rec_num_sources, | |
2019 | (struct in_addr *)sources); | |
2020 | break; | |
2021 | case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES: | |
2022 | igmpv3_report_block(igmp, from, rec_group, | |
2023 | rec_num_sources, | |
2024 | (struct in_addr *)sources); | |
2025 | break; | |
2026 | default: | |
2027 | zlog_warn( | |
2028 | "Recv IGMP report v3 from %s on %s: unknown record type: type=%d", | |
2029 | from_str, ifp->name, rec_type); | |
2030 | } | |
2031 | ||
2032 | group_record += | |
2033 | 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2); | |
d62a17ae | 2034 | |
2035 | } /* for (group records) */ | |
2036 | ||
2037 | return 0; | |
12e41d03 | 2038 | } |