pim_igmpv3.c 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717
  1. /*
  2. PIM for Quagga
  3. Copyright (C) 2008 Everton da Silva Marques
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; see the file COPYING; if not, write to the
  14. Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
  15. MA 02110-1301 USA
  16. $QuaggaId: $Format:%an, %ai, %h$ $
  17. */
  18. #include <zebra.h>
  19. #include "log.h"
  20. #include "memory.h"
  21. #include "pimd.h"
  22. #include "pim_iface.h"
  23. #include "pim_igmp.h"
  24. #include "pim_igmpv3.h"
  25. #include "pim_str.h"
  26. #include "pim_util.h"
  27. #include "pim_time.h"
  28. #include "pim_zebra.h"
  29. #include "pim_oil.h"
  30. static void group_retransmit_timer_on(struct igmp_group *group);
  31. static long igmp_group_timer_remain_msec(struct igmp_group *group);
  32. static long igmp_source_timer_remain_msec(struct igmp_source *source);
  33. static void group_query_send(struct igmp_group *group);
  34. static void source_query_send_by_flag(struct igmp_group *group,
  35. int num_sources_tosend);
  36. static void on_trace(const char *label,
  37. struct interface *ifp, struct in_addr from,
  38. struct in_addr group_addr,
  39. int num_sources, struct in_addr *sources)
  40. {
  41. if (PIM_DEBUG_IGMP_TRACE) {
  42. char from_str[100];
  43. char group_str[100];
  44. pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
  45. pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
  46. zlog_debug("%s: from %s on %s: group=%s sources=%d",
  47. label, from_str, ifp->name, group_str, num_sources);
  48. }
  49. }
  50. int igmp_group_compat_mode(const struct igmp_sock *igmp,
  51. const struct igmp_group *group)
  52. {
  53. struct pim_interface *pim_ifp;
  54. int64_t now_dsec;
  55. long older_host_present_interval_dsec;
  56. zassert(igmp);
  57. zassert(igmp->interface);
  58. zassert(igmp->interface->info);
  59. pim_ifp = igmp->interface->info;
  60. /*
  61. RFC 3376: 8.13. Older Host Present Interval
  62. This value MUST be ((the Robustness Variable) times (the Query
  63. Interval)) plus (one Query Response Interval).
  64. older_host_present_interval_dsec = \
  65. igmp->querier_robustness_variable * \
  66. 10 * igmp->querier_query_interval + \
  67. pim_ifp->query_max_response_time_dsec;
  68. */
  69. older_host_present_interval_dsec =
  70. PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
  71. igmp->querier_query_interval,
  72. pim_ifp->igmp_query_max_response_time_dsec);
  73. now_dsec = pim_time_monotonic_dsec();
  74. if (now_dsec < 1) {
  75. /* broken timer logged by pim_time_monotonic_dsec() */
  76. return 3;
  77. }
  78. if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec)
  79. return 1; /* IGMPv1 */
  80. if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec)
  81. return 2; /* IGMPv2 */
  82. return 3; /* IGMPv3 */
  83. }
  84. void igmp_group_reset_gmi(struct igmp_group *group)
  85. {
  86. long group_membership_interval_msec;
  87. struct pim_interface *pim_ifp;
  88. struct igmp_sock *igmp;
  89. struct interface *ifp;
  90. igmp = group->group_igmp_sock;
  91. ifp = igmp->interface;
  92. pim_ifp = ifp->info;
  93. /*
  94. RFC 3376: 8.4. Group Membership Interval
  95. The Group Membership Interval is the amount of time that must pass
  96. before a multicast router decides there are no more members of a
  97. group or a particular source on a network.
  98. This value MUST be ((the Robustness Variable) times (the Query
  99. Interval)) plus (one Query Response Interval).
  100. group_membership_interval_msec = querier_robustness_variable *
  101. (1000 * querier_query_interval) +
  102. 100 * query_response_interval_dsec;
  103. */
  104. group_membership_interval_msec =
  105. PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
  106. igmp->querier_query_interval,
  107. pim_ifp->igmp_query_max_response_time_dsec);
  108. if (PIM_DEBUG_IGMP_TRACE) {
  109. char group_str[100];
  110. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  111. zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
  112. group_str,
  113. group_membership_interval_msec / 1000,
  114. group_membership_interval_msec % 1000,
  115. ifp->name);
  116. }
  117. /*
  118. RFC 3376: 6.2.2. Definition of Group Timers
  119. The group timer is only used when a group is in EXCLUDE mode and
  120. it represents the time for the *filter-mode* of the group to
  121. expire and switch to INCLUDE mode.
  122. */
  123. zassert(group->group_filtermode_isexcl);
  124. igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
  125. }
  126. static int igmp_source_timer(struct thread *t)
  127. {
  128. struct igmp_source *source;
  129. struct igmp_group *group;
  130. zassert(t);
  131. source = THREAD_ARG(t);
  132. zassert(source);
  133. group = source->source_group;
  134. if (PIM_DEBUG_IGMP_TRACE) {
  135. char group_str[100];
  136. char source_str[100];
  137. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  138. pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  139. zlog_debug("%s: Source timer expired for group %s source %s on %s",
  140. __PRETTY_FUNCTION__,
  141. group_str, source_str,
  142. group->group_igmp_sock->interface->name);
  143. }
  144. zassert(source->t_source_timer);
  145. source->t_source_timer = 0;
  146. /*
  147. RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
  148. Group
  149. Filter-Mode Source Timer Value Action
  150. ----------- ------------------ ------
  151. INCLUDE TIMER == 0 Suggest to stop forwarding
  152. traffic from source and
  153. remove source record. If
  154. there are no more source
  155. records for the group, delete
  156. group record.
  157. EXCLUDE TIMER == 0 Suggest to not forward
  158. traffic from source
  159. (DO NOT remove record)
  160. Source timer switched from (T > 0) to (T == 0): disable forwarding.
  161. */
  162. zassert(!source->t_source_timer);
  163. if (group->group_filtermode_isexcl) {
  164. /* EXCLUDE mode */
  165. igmp_source_forward_stop(source);
  166. }
  167. else {
  168. /* INCLUDE mode */
  169. /* igmp_source_delete() will stop forwarding source */
  170. igmp_source_delete(source);
  171. /*
  172. If there are no more source records for the group, delete group
  173. record.
  174. */
  175. if (!listcount(group->group_source_list)) {
  176. igmp_group_delete_empty_include(group);
  177. }
  178. }
  179. return 0;
  180. }
  181. static void source_timer_off(struct igmp_group *group,
  182. struct igmp_source *source)
  183. {
  184. if (!source->t_source_timer)
  185. return;
  186. if (PIM_DEBUG_IGMP_TRACE) {
  187. char group_str[100];
  188. char source_str[100];
  189. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  190. pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  191. zlog_debug("Cancelling TIMER event for group %s source %s on %s",
  192. group_str, source_str,
  193. group->group_igmp_sock->interface->name);
  194. }
  195. THREAD_OFF(source->t_source_timer);
  196. zassert(!source->t_source_timer);
  197. }
  198. static void igmp_source_timer_on(struct igmp_group *group,
  199. struct igmp_source *source,
  200. long interval_msec)
  201. {
  202. source_timer_off(group, source);
  203. if (PIM_DEBUG_IGMP_EVENTS) {
  204. char group_str[100];
  205. char source_str[100];
  206. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  207. pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  208. zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
  209. interval_msec / 1000,
  210. interval_msec % 1000,
  211. group_str, source_str,
  212. group->group_igmp_sock->interface->name);
  213. }
  214. THREAD_TIMER_MSEC_ON(master, source->t_source_timer,
  215. igmp_source_timer,
  216. source, interval_msec);
  217. zassert(source->t_source_timer);
  218. /*
  219. RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
  220. Source timer switched from (T == 0) to (T > 0): enable forwarding.
  221. */
  222. igmp_source_forward_start(source);
  223. }
  224. void igmp_source_reset_gmi(struct igmp_sock *igmp,
  225. struct igmp_group *group,
  226. struct igmp_source *source)
  227. {
  228. long group_membership_interval_msec;
  229. struct pim_interface *pim_ifp;
  230. struct interface *ifp;
  231. ifp = igmp->interface;
  232. pim_ifp = ifp->info;
  233. group_membership_interval_msec =
  234. PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
  235. igmp->querier_query_interval,
  236. pim_ifp->igmp_query_max_response_time_dsec);
  237. if (PIM_DEBUG_IGMP_TRACE) {
  238. char group_str[100];
  239. char source_str[100];
  240. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  241. pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  242. zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
  243. source_str,
  244. group_membership_interval_msec / 1000,
  245. group_membership_interval_msec % 1000,
  246. group_str,
  247. ifp->name);
  248. }
  249. igmp_source_timer_on(group, source,
  250. group_membership_interval_msec);
  251. }
  252. static void source_mark_delete_flag(struct list *source_list)
  253. {
  254. struct listnode *src_node;
  255. struct igmp_source *src;
  256. for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
  257. IGMP_SOURCE_DO_DELETE(src->source_flags);
  258. }
  259. }
  260. static void source_mark_send_flag(struct list *source_list)
  261. {
  262. struct listnode *src_node;
  263. struct igmp_source *src;
  264. for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
  265. IGMP_SOURCE_DO_SEND(src->source_flags);
  266. }
  267. }
  268. static int source_mark_send_flag_by_timer(struct list *source_list)
  269. {
  270. struct listnode *src_node;
  271. struct igmp_source *src;
  272. int num_marked_sources = 0;
  273. for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
  274. /* Is source timer running? */
  275. if (src->t_source_timer) {
  276. IGMP_SOURCE_DO_SEND(src->source_flags);
  277. ++num_marked_sources;
  278. }
  279. else {
  280. IGMP_SOURCE_DONT_SEND(src->source_flags);
  281. }
  282. }
  283. return num_marked_sources;
  284. }
  285. static void source_clear_send_flag(struct list *source_list)
  286. {
  287. struct listnode *src_node;
  288. struct igmp_source *src;
  289. for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
  290. IGMP_SOURCE_DONT_SEND(src->source_flags);
  291. }
  292. }
  293. /*
  294. Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
  295. */
  296. static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
  297. {
  298. zassert(group->group_filtermode_isexcl);
  299. if (listcount(group->group_source_list) < 1) {
  300. igmp_anysource_forward_start(group);
  301. }
  302. }
  303. void igmp_source_free(struct igmp_source *source)
  304. {
  305. /* make sure there is no source timer running */
  306. zassert(!source->t_source_timer);
  307. XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
  308. }
  309. static void source_channel_oil_detach(struct igmp_source *source)
  310. {
  311. if (source->source_channel_oil) {
  312. pim_channel_oil_del(source->source_channel_oil);
  313. source->source_channel_oil = 0;
  314. }
  315. }
  316. /*
  317. igmp_source_delete: stop fowarding, and delete the source
  318. igmp_source_forward_stop: stop fowarding, but keep the source
  319. */
  320. void igmp_source_delete(struct igmp_source *source)
  321. {
  322. struct igmp_group *group;
  323. group = source->source_group;
  324. if (PIM_DEBUG_IGMP_TRACE) {
  325. char group_str[100];
  326. char source_str[100];
  327. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  328. pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  329. zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
  330. source_str, group_str,
  331. group->group_igmp_sock->fd,
  332. group->group_igmp_sock->interface->name);
  333. }
  334. source_timer_off(group, source);
  335. igmp_source_forward_stop(source);
  336. /* sanity check that forwarding has been disabled */
  337. if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
  338. char group_str[100];
  339. char source_str[100];
  340. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  341. pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  342. zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
  343. __PRETTY_FUNCTION__,
  344. source_str, group_str,
  345. group->group_igmp_sock->fd,
  346. group->group_igmp_sock->interface->name);
  347. /* warning only */
  348. }
  349. source_channel_oil_detach(source);
  350. /*
  351. notice that listnode_delete() can't be moved
  352. into igmp_source_free() because the later is
  353. called by list_delete_all_node()
  354. */
  355. listnode_delete(group->group_source_list, source);
  356. igmp_source_free(source);
  357. if (group->group_filtermode_isexcl) {
  358. group_exclude_fwd_anysrc_ifempty(group);
  359. }
  360. }
  361. static void source_delete_by_flag(struct list *source_list)
  362. {
  363. struct listnode *src_node;
  364. struct listnode *src_nextnode;
  365. struct igmp_source *src;
  366. for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
  367. if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
  368. igmp_source_delete(src);
  369. }
  370. void igmp_source_delete_expired(struct list *source_list)
  371. {
  372. struct listnode *src_node;
  373. struct listnode *src_nextnode;
  374. struct igmp_source *src;
  375. for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
  376. if (!src->t_source_timer)
  377. igmp_source_delete(src);
  378. }
  379. struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
  380. struct in_addr src_addr)
  381. {
  382. struct listnode *src_node;
  383. struct igmp_source *src;
  384. for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
  385. if (src_addr.s_addr == src->source_addr.s_addr)
  386. return src;
  387. return 0;
  388. }
  389. struct igmp_source *
  390. source_new (struct igmp_group *group,
  391. struct in_addr src_addr)
  392. {
  393. struct igmp_source *src;
  394. if (PIM_DEBUG_IGMP_TRACE) {
  395. char group_str[100];
  396. char source_str[100];
  397. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  398. pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
  399. zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
  400. source_str, group_str,
  401. group->group_igmp_sock->fd,
  402. group->group_igmp_sock->interface->name);
  403. }
  404. src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
  405. if (!src) {
  406. zlog_warn("%s %s: XMALLOC() failure",
  407. __FILE__, __PRETTY_FUNCTION__);
  408. return 0; /* error, not found, could not create */
  409. }
  410. src->t_source_timer = NULL;
  411. src->source_group = group; /* back pointer */
  412. src->source_addr = src_addr;
  413. src->source_creation = pim_time_monotonic_sec();
  414. src->source_flags = 0;
  415. src->source_query_retransmit_count = 0;
  416. src->source_channel_oil = NULL;
  417. listnode_add(group->group_source_list, src);
  418. zassert(!src->t_source_timer); /* source timer == 0 */
  419. /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
  420. igmp_anysource_forward_stop(group);
  421. return src;
  422. }
  423. static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
  424. struct igmp_group *group,
  425. struct in_addr src_addr)
  426. {
  427. struct igmp_source *src;
  428. src = igmp_find_source_by_addr(group, src_addr);
  429. if (src) {
  430. return src;
  431. }
  432. src = source_new(group, src_addr);
  433. if (!src) {
  434. return 0;
  435. }
  436. return src;
  437. }
  438. static void allow(struct igmp_sock *igmp, struct in_addr from,
  439. struct in_addr group_addr,
  440. int num_sources, struct in_addr *sources)
  441. {
  442. struct igmp_group *group;
  443. int i;
  444. /* non-existant group is created as INCLUDE {empty} */
  445. group = igmp_add_group_by_addr(igmp, group_addr);
  446. if (!group) {
  447. return;
  448. }
  449. /* scan received sources */
  450. for (i = 0; i < num_sources; ++i) {
  451. struct igmp_source *source;
  452. struct in_addr *src_addr;
  453. src_addr = sources + i;
  454. source = add_source_by_addr(igmp, group, *src_addr);
  455. if (!source) {
  456. continue;
  457. }
  458. /*
  459. RFC 3376: 6.4.1. Reception of Current-State Records
  460. When receiving IS_IN reports for groups in EXCLUDE mode is
  461. sources should be moved from set with (timers = 0) to set with
  462. (timers > 0).
  463. igmp_source_reset_gmi() below, resetting the source timers to
  464. GMI, accomplishes this.
  465. */
  466. igmp_source_reset_gmi(igmp, group, source);
  467. } /* scan received sources */
  468. }
  469. void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
  470. struct in_addr group_addr,
  471. int num_sources, struct in_addr *sources)
  472. {
  473. on_trace(__PRETTY_FUNCTION__,
  474. igmp->interface, from, group_addr, num_sources, sources);
  475. allow(igmp, from, group_addr, num_sources, sources);
  476. }
  477. static void isex_excl(struct igmp_group *group,
  478. int num_sources, struct in_addr *sources)
  479. {
  480. int i;
  481. /* EXCLUDE mode */
  482. zassert(group->group_filtermode_isexcl);
  483. /* E.1: set deletion flag for known sources (X,Y) */
  484. source_mark_delete_flag(group->group_source_list);
  485. /* scan received sources (A) */
  486. for (i = 0; i < num_sources; ++i) {
  487. struct igmp_source *source;
  488. struct in_addr *src_addr;
  489. src_addr = sources + i;
  490. /* E.2: lookup reported source from (A) in (X,Y) */
  491. source = igmp_find_source_by_addr(group, *src_addr);
  492. if (source) {
  493. /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
  494. IGMP_SOURCE_DONT_DELETE(source->source_flags);
  495. }
  496. else {
  497. /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
  498. source = source_new(group, *src_addr);
  499. if (!source) {
  500. /* ugh, internal malloc failure, skip source */
  501. continue;
  502. }
  503. zassert(!source->t_source_timer); /* timer == 0 */
  504. igmp_source_reset_gmi(group->group_igmp_sock, group, source);
  505. zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
  506. }
  507. } /* scan received sources */
  508. /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
  509. source_delete_by_flag(group->group_source_list);
  510. }
  511. static void isex_incl(struct igmp_group *group,
  512. int num_sources, struct in_addr *sources)
  513. {
  514. int i;
  515. /* INCLUDE mode */
  516. zassert(!group->group_filtermode_isexcl);
  517. /* I.1: set deletion flag for known sources (A) */
  518. source_mark_delete_flag(group->group_source_list);
  519. /* scan received sources (B) */
  520. for (i = 0; i < num_sources; ++i) {
  521. struct igmp_source *source;
  522. struct in_addr *src_addr;
  523. src_addr = sources + i;
  524. /* I.2: lookup reported source (B) */
  525. source = igmp_find_source_by_addr(group, *src_addr);
  526. if (source) {
  527. /* I.3: if found, clear deletion flag (A*B) */
  528. IGMP_SOURCE_DONT_DELETE(source->source_flags);
  529. }
  530. else {
  531. /* I.4: if not found, create source with timer=0 (B-A) */
  532. source = source_new(group, *src_addr);
  533. if (!source) {
  534. /* ugh, internal malloc failure, skip source */
  535. continue;
  536. }
  537. zassert(!source->t_source_timer); /* (B-A) timer=0 */
  538. }
  539. } /* scan received sources */
  540. /* I.5: delete all sources marked with deletion flag (A-B) */
  541. source_delete_by_flag(group->group_source_list);
  542. group->group_filtermode_isexcl = 1; /* boolean=true */
  543. zassert(group->group_filtermode_isexcl);
  544. group_exclude_fwd_anysrc_ifempty(group);
  545. }
  546. void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
  547. struct in_addr group_addr,
  548. int num_sources, struct in_addr *sources)
  549. {
  550. struct interface *ifp = igmp->interface;
  551. struct igmp_group *group;
  552. on_trace(__PRETTY_FUNCTION__,
  553. ifp, from, group_addr, num_sources, sources);
  554. /* non-existant group is created as INCLUDE {empty} */
  555. group = igmp_add_group_by_addr(igmp, group_addr);
  556. if (!group) {
  557. return;
  558. }
  559. if (group->group_filtermode_isexcl) {
  560. /* EXCLUDE mode */
  561. isex_excl(group, num_sources, sources);
  562. }
  563. else {
  564. /* INCLUDE mode */
  565. isex_incl(group, num_sources, sources);
  566. zassert(group->group_filtermode_isexcl);
  567. }
  568. zassert(group->group_filtermode_isexcl);
  569. igmp_group_reset_gmi(group);
  570. }
  571. static void toin_incl(struct igmp_group *group,
  572. int num_sources, struct in_addr *sources)
  573. {
  574. struct igmp_sock *igmp = group->group_igmp_sock;
  575. int num_sources_tosend = listcount(group->group_source_list);
  576. int i;
  577. /* Set SEND flag for all known sources (A) */
  578. source_mark_send_flag(group->group_source_list);
  579. /* Scan received sources (B) */
  580. for (i = 0; i < num_sources; ++i) {
  581. struct igmp_source *source;
  582. struct in_addr *src_addr;
  583. src_addr = sources + i;
  584. /* Lookup reported source (B) */
  585. source = igmp_find_source_by_addr(group, *src_addr);
  586. if (source) {
  587. /* If found, clear SEND flag (A*B) */
  588. IGMP_SOURCE_DONT_SEND(source->source_flags);
  589. --num_sources_tosend;
  590. }
  591. else {
  592. /* If not found, create new source */
  593. source = source_new(group, *src_addr);
  594. if (!source) {
  595. /* ugh, internal malloc failure, skip source */
  596. continue;
  597. }
  598. }
  599. /* (B)=GMI */
  600. igmp_source_reset_gmi(igmp, group, source);
  601. }
  602. /* Send sources marked with SEND flag: Q(G,A-B) */
  603. if (num_sources_tosend > 0) {
  604. source_query_send_by_flag(group, num_sources_tosend);
  605. }
  606. }
  607. static void toin_excl(struct igmp_group *group,
  608. int num_sources, struct in_addr *sources)
  609. {
  610. struct igmp_sock *igmp = group->group_igmp_sock;
  611. int num_sources_tosend;
  612. int i;
  613. /* Set SEND flag for X (sources with timer > 0) */
  614. num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
  615. /* Scan received sources (A) */
  616. for (i = 0; i < num_sources; ++i) {
  617. struct igmp_source *source;
  618. struct in_addr *src_addr;
  619. src_addr = sources + i;
  620. /* Lookup reported source (A) */
  621. source = igmp_find_source_by_addr(group, *src_addr);
  622. if (source) {
  623. if (source->t_source_timer) {
  624. /* If found and timer running, clear SEND flag (X*A) */
  625. IGMP_SOURCE_DONT_SEND(source->source_flags);
  626. --num_sources_tosend;
  627. }
  628. }
  629. else {
  630. /* If not found, create new source */
  631. source = source_new(group, *src_addr);
  632. if (!source) {
  633. /* ugh, internal malloc failure, skip source */
  634. continue;
  635. }
  636. }
  637. /* (A)=GMI */
  638. igmp_source_reset_gmi(igmp, group, source);
  639. }
  640. /* Send sources marked with SEND flag: Q(G,X-A) */
  641. if (num_sources_tosend > 0) {
  642. source_query_send_by_flag(group, num_sources_tosend);
  643. }
  644. /* Send Q(G) */
  645. group_query_send(group);
  646. }
  647. void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
  648. struct in_addr group_addr,
  649. int num_sources, struct in_addr *sources)
  650. {
  651. struct interface *ifp = igmp->interface;
  652. struct igmp_group *group;
  653. on_trace(__PRETTY_FUNCTION__,
  654. ifp, from, group_addr, num_sources, sources);
  655. /* non-existant group is created as INCLUDE {empty} */
  656. group = igmp_add_group_by_addr(igmp, group_addr);
  657. if (!group) {
  658. return;
  659. }
  660. if (group->group_filtermode_isexcl) {
  661. /* EXCLUDE mode */
  662. toin_excl(group, num_sources, sources);
  663. }
  664. else {
  665. /* INCLUDE mode */
  666. toin_incl(group, num_sources, sources);
  667. }
  668. }
  669. static void toex_incl(struct igmp_group *group,
  670. int num_sources, struct in_addr *sources)
  671. {
  672. int num_sources_tosend = 0;
  673. int i;
  674. zassert(!group->group_filtermode_isexcl);
  675. /* Set DELETE flag for all known sources (A) */
  676. source_mark_delete_flag(group->group_source_list);
  677. /* Clear off SEND flag from all known sources (A) */
  678. source_clear_send_flag(group->group_source_list);
  679. /* Scan received sources (B) */
  680. for (i = 0; i < num_sources; ++i) {
  681. struct igmp_source *source;
  682. struct in_addr *src_addr;
  683. src_addr = sources + i;
  684. /* Lookup reported source (B) */
  685. source = igmp_find_source_by_addr(group, *src_addr);
  686. if (source) {
  687. /* If found, clear deletion flag: (A*B) */
  688. IGMP_SOURCE_DONT_DELETE(source->source_flags);
  689. /* and set SEND flag (A*B) */
  690. IGMP_SOURCE_DO_SEND(source->source_flags);
  691. ++num_sources_tosend;
  692. }
  693. else {
  694. /* If source not found, create source with timer=0: (B-A)=0 */
  695. source = source_new(group, *src_addr);
  696. if (!source) {
  697. /* ugh, internal malloc failure, skip source */
  698. continue;
  699. }
  700. zassert(!source->t_source_timer); /* (B-A) timer=0 */
  701. }
  702. } /* Scan received sources (B) */
  703. group->group_filtermode_isexcl = 1; /* boolean=true */
  704. /* Delete all sources marked with DELETE flag (A-B) */
  705. source_delete_by_flag(group->group_source_list);
  706. /* Send sources marked with SEND flag: Q(G,A*B) */
  707. if (num_sources_tosend > 0) {
  708. source_query_send_by_flag(group, num_sources_tosend);
  709. }
  710. zassert(group->group_filtermode_isexcl);
  711. group_exclude_fwd_anysrc_ifempty(group);
  712. }
  713. static void toex_excl(struct igmp_group *group,
  714. int num_sources, struct in_addr *sources)
  715. {
  716. int num_sources_tosend = 0;
  717. int i;
  718. /* set DELETE flag for all known sources (X,Y) */
  719. source_mark_delete_flag(group->group_source_list);
  720. /* clear off SEND flag from all known sources (X,Y) */
  721. source_clear_send_flag(group->group_source_list);
  722. /* scan received sources (A) */
  723. for (i = 0; i < num_sources; ++i) {
  724. struct igmp_source *source;
  725. struct in_addr *src_addr;
  726. src_addr = sources + i;
  727. /* lookup reported source (A) in known sources (X,Y) */
  728. source = igmp_find_source_by_addr(group, *src_addr);
  729. if (source) {
  730. /* if found, clear off DELETE flag from reported source (A) */
  731. IGMP_SOURCE_DONT_DELETE(source->source_flags);
  732. }
  733. else {
  734. /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
  735. long group_timer_msec;
  736. source = source_new(group, *src_addr);
  737. if (!source) {
  738. /* ugh, internal malloc failure, skip source */
  739. continue;
  740. }
  741. zassert(!source->t_source_timer); /* timer == 0 */
  742. group_timer_msec = igmp_group_timer_remain_msec(group);
  743. igmp_source_timer_on(group, source, group_timer_msec);
  744. zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
  745. /* make sure source is created with DELETE flag unset */
  746. zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
  747. }
  748. /* make sure reported source has DELETE flag unset */
  749. zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
  750. if (source->t_source_timer) {
  751. /* if source timer>0 mark SEND flag: Q(G,A-Y) */
  752. IGMP_SOURCE_DO_SEND(source->source_flags);
  753. ++num_sources_tosend;
  754. }
  755. } /* scan received sources (A) */
  756. /*
  757. delete all sources marked with DELETE flag:
  758. Delete (X-A)
  759. Delete (Y-A)
  760. */
  761. source_delete_by_flag(group->group_source_list);
  762. /* send sources marked with SEND flag: Q(G,A-Y) */
  763. if (num_sources_tosend > 0) {
  764. source_query_send_by_flag(group, num_sources_tosend);
  765. }
  766. }
  767. void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
  768. struct in_addr group_addr,
  769. int num_sources, struct in_addr *sources)
  770. {
  771. struct interface *ifp = igmp->interface;
  772. struct igmp_group *group;
  773. on_trace(__PRETTY_FUNCTION__,
  774. ifp, from, group_addr, num_sources, sources);
  775. /* non-existant group is created as INCLUDE {empty} */
  776. group = igmp_add_group_by_addr(igmp, group_addr);
  777. if (!group) {
  778. return;
  779. }
  780. if (group->group_filtermode_isexcl) {
  781. /* EXCLUDE mode */
  782. toex_excl(group, num_sources, sources);
  783. }
  784. else {
  785. /* INCLUDE mode */
  786. toex_incl(group, num_sources, sources);
  787. zassert(group->group_filtermode_isexcl);
  788. }
  789. zassert(group->group_filtermode_isexcl);
  790. /* Group Timer=GMI */
  791. igmp_group_reset_gmi(group);
  792. }
  793. void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
  794. struct in_addr group_addr,
  795. int num_sources, struct in_addr *sources)
  796. {
  797. on_trace(__PRETTY_FUNCTION__,
  798. igmp->interface, from, group_addr, num_sources, sources);
  799. allow(igmp, from, group_addr, num_sources, sources);
  800. }
  801. /*
  802. RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
  803. When transmitting a group specific query, if the group timer is
  804. larger than LMQT, the "Suppress Router-Side Processing" bit is set
  805. in the query message.
  806. */
  807. static void group_retransmit_group(struct igmp_group *group)
  808. {
  809. char query_buf[PIM_IGMP_BUFSIZE_WRITE];
  810. struct igmp_sock *igmp;
  811. struct pim_interface *pim_ifp;
  812. long lmqc; /* Last Member Query Count */
  813. long lmqi_msec; /* Last Member Query Interval */
  814. long lmqt_msec; /* Last Member Query Time */
  815. int s_flag;
  816. igmp = group->group_igmp_sock;
  817. pim_ifp = igmp->interface->info;
  818. lmqc = igmp->querier_robustness_variable;
  819. lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
  820. lmqt_msec = lmqc * lmqi_msec;
  821. /*
  822. RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
  823. When transmitting a group specific query, if the group timer is
  824. larger than LMQT, the "Suppress Router-Side Processing" bit is set
  825. in the query message.
  826. */
  827. s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
  828. if (PIM_DEBUG_IGMP_TRACE) {
  829. char group_str[100];
  830. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  831. zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
  832. group_str, igmp->interface->name, s_flag,
  833. group->group_specific_query_retransmit_count);
  834. }
  835. /*
  836. RFC3376: 4.1.12. IP Destination Addresses for Queries
  837. Group-Specific and Group-and-Source-Specific Queries are sent with
  838. an IP destination address equal to the multicast address of
  839. interest.
  840. */
  841. pim_igmp_send_membership_query(group,
  842. igmp->fd,
  843. igmp->interface->name,
  844. query_buf,
  845. sizeof(query_buf),
  846. 0 /* num_sources_tosend */,
  847. group->group_addr /* dst_addr */,
  848. group->group_addr /* group_addr */,
  849. pim_ifp->igmp_specific_query_max_response_time_dsec,
  850. s_flag,
  851. igmp->querier_robustness_variable,
  852. igmp->querier_query_interval);
  853. }
  854. /*
  855. RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
  856. When building a group and source specific query for a group G, two
  857. separate query messages are sent for the group. The first one has
  858. the "Suppress Router-Side Processing" bit set and contains all the
  859. sources with retransmission state and timers greater than LMQT. The
  860. second has the "Suppress Router-Side Processing" bit clear and
  861. contains all the sources with retransmission state and timers lower
  862. or equal to LMQT. If either of the two calculated messages does not
  863. contain any sources, then its transmission is suppressed.
  864. */
  865. static int group_retransmit_sources(struct igmp_group *group,
  866. int send_with_sflag_set)
  867. {
  868. struct igmp_sock *igmp;
  869. struct pim_interface *pim_ifp;
  870. long lmqc; /* Last Member Query Count */
  871. long lmqi_msec; /* Last Member Query Interval */
  872. long lmqt_msec; /* Last Member Query Time */
  873. char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
  874. char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
  875. int query_buf1_max_sources;
  876. int query_buf2_max_sources;
  877. struct in_addr *source_addr1;
  878. struct in_addr *source_addr2;
  879. int num_sources_tosend1;
  880. int num_sources_tosend2;
  881. struct listnode *src_node;
  882. struct igmp_source *src;
  883. int num_retransmit_sources_left = 0;
  884. query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
  885. query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
  886. source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
  887. source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
  888. igmp = group->group_igmp_sock;
  889. pim_ifp = igmp->interface->info;
  890. lmqc = igmp->querier_robustness_variable;
  891. lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
  892. lmqt_msec = lmqc * lmqi_msec;
  893. /* Scan all group sources */
  894. for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
  895. /* Source has retransmission state? */
  896. if (src->source_query_retransmit_count < 1)
  897. continue;
  898. if (--src->source_query_retransmit_count > 0) {
  899. ++num_retransmit_sources_left;
  900. }
  901. /* Copy source address into appropriate query buffer */
  902. if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
  903. *source_addr1 = src->source_addr;
  904. ++source_addr1;
  905. }
  906. else {
  907. *source_addr2 = src->source_addr;
  908. ++source_addr2;
  909. }
  910. }
  911. num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
  912. num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
  913. if (PIM_DEBUG_IGMP_TRACE) {
  914. char group_str[100];
  915. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  916. zlog_debug("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",
  917. group_str, igmp->interface->name,
  918. num_sources_tosend1,
  919. num_sources_tosend2,
  920. send_with_sflag_set,
  921. num_retransmit_sources_left);
  922. }
  923. if (num_sources_tosend1 > 0) {
  924. /*
  925. Send group-and-source-specific query with s_flag set and all
  926. sources with timers greater than LMQT.
  927. */
  928. if (send_with_sflag_set) {
  929. query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
  930. if (num_sources_tosend1 > query_buf1_max_sources) {
  931. char group_str[100];
  932. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  933. zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
  934. __PRETTY_FUNCTION__, group_str, igmp->interface->name,
  935. num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
  936. }
  937. else {
  938. /*
  939. RFC3376: 4.1.12. IP Destination Addresses for Queries
  940. Group-Specific and Group-and-Source-Specific Queries are sent with
  941. an IP destination address equal to the multicast address of
  942. interest.
  943. */
  944. pim_igmp_send_membership_query(group,
  945. igmp->fd,
  946. igmp->interface->name,
  947. query_buf1,
  948. sizeof(query_buf1),
  949. num_sources_tosend1,
  950. group->group_addr,
  951. group->group_addr,
  952. pim_ifp->igmp_specific_query_max_response_time_dsec,
  953. 1 /* s_flag */,
  954. igmp->querier_robustness_variable,
  955. igmp->querier_query_interval);
  956. }
  957. } /* send_with_sflag_set */
  958. }
  959. if (num_sources_tosend2 > 0) {
  960. /*
  961. Send group-and-source-specific query with s_flag clear and all
  962. sources with timers lower or equal to LMQT.
  963. */
  964. query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
  965. if (num_sources_tosend2 > query_buf2_max_sources) {
  966. char group_str[100];
  967. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  968. zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
  969. __PRETTY_FUNCTION__, group_str, igmp->interface->name,
  970. num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
  971. }
  972. else {
  973. /*
  974. RFC3376: 4.1.12. IP Destination Addresses for Queries
  975. Group-Specific and Group-and-Source-Specific Queries are sent with
  976. an IP destination address equal to the multicast address of
  977. interest.
  978. */
  979. pim_igmp_send_membership_query(group,
  980. igmp->fd,
  981. igmp->interface->name,
  982. query_buf2,
  983. sizeof(query_buf2),
  984. num_sources_tosend2,
  985. group->group_addr,
  986. group->group_addr,
  987. pim_ifp->igmp_specific_query_max_response_time_dsec,
  988. 0 /* s_flag */,
  989. igmp->querier_robustness_variable,
  990. igmp->querier_query_interval);
  991. }
  992. }
  993. return num_retransmit_sources_left;
  994. }
  995. static int igmp_group_retransmit(struct thread *t)
  996. {
  997. struct igmp_group *group;
  998. int num_retransmit_sources_left;
  999. int send_with_sflag_set; /* boolean */
  1000. zassert(t);
  1001. group = THREAD_ARG(t);
  1002. zassert(group);
  1003. if (PIM_DEBUG_IGMP_TRACE) {
  1004. char group_str[100];
  1005. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  1006. zlog_debug("group_retransmit_timer: group %s on %s",
  1007. group_str, group->group_igmp_sock->interface->name);
  1008. }
  1009. /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
  1010. if (group->group_specific_query_retransmit_count > 0) {
  1011. /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
  1012. group_retransmit_group(group);
  1013. --group->group_specific_query_retransmit_count;
  1014. /*
  1015. RFC3376: 6.6.3.2
  1016. If a group specific query is scheduled to be transmitted at the
  1017. same time as a group and source specific query for the same group,
  1018. then transmission of the group and source specific message with the
  1019. "Suppress Router-Side Processing" bit set may be suppressed.
  1020. */
  1021. send_with_sflag_set = 0; /* boolean=false */
  1022. }
  1023. else {
  1024. send_with_sflag_set = 1; /* boolean=true */
  1025. }
  1026. /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
  1027. num_retransmit_sources_left = group_retransmit_sources(group,
  1028. send_with_sflag_set);
  1029. group->t_group_query_retransmit_timer = 0;
  1030. /*
  1031. Keep group retransmit timer running if there is any retransmit
  1032. counter pending
  1033. */
  1034. if ((num_retransmit_sources_left > 0) ||
  1035. (group->group_specific_query_retransmit_count > 0)) {
  1036. group_retransmit_timer_on(group);
  1037. }
  1038. return 0;
  1039. }
  1040. /*
  1041. group_retransmit_timer_on:
  1042. if group retransmit timer isn't running, starts it;
  1043. otherwise, do nothing
  1044. */
  1045. static void group_retransmit_timer_on(struct igmp_group *group)
  1046. {
  1047. struct igmp_sock *igmp;
  1048. struct pim_interface *pim_ifp;
  1049. long lmqi_msec; /* Last Member Query Interval */
  1050. /* if group retransmit timer is running, do nothing */
  1051. if (group->t_group_query_retransmit_timer) {
  1052. return;
  1053. }
  1054. igmp = group->group_igmp_sock;
  1055. pim_ifp = igmp->interface->info;
  1056. lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
  1057. if (PIM_DEBUG_IGMP_TRACE) {
  1058. char group_str[100];
  1059. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  1060. zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
  1061. lmqi_msec / 1000,
  1062. lmqi_msec % 1000,
  1063. group_str,
  1064. igmp->interface->name);
  1065. }
  1066. THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
  1067. igmp_group_retransmit,
  1068. group, lmqi_msec);
  1069. }
  1070. static long igmp_group_timer_remain_msec(struct igmp_group *group)
  1071. {
  1072. return pim_time_timer_remain_msec(group->t_group_timer);
  1073. }
  1074. static long igmp_source_timer_remain_msec(struct igmp_source *source)
  1075. {
  1076. return pim_time_timer_remain_msec(source->t_source_timer);
  1077. }
  1078. /*
  1079. RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
  1080. */
  1081. static void group_query_send(struct igmp_group *group)
  1082. {
  1083. long lmqc; /* Last Member Query Count */
  1084. lmqc = group->group_igmp_sock->querier_robustness_variable;
  1085. /* lower group timer to lmqt */
  1086. igmp_group_timer_lower_to_lmqt(group);
  1087. /* reset retransmission counter */
  1088. group->group_specific_query_retransmit_count = lmqc;
  1089. /* immediately send group specific query (decrease retransmit counter by 1)*/
  1090. group_retransmit_group(group);
  1091. /* make sure group retransmit timer is running */
  1092. group_retransmit_timer_on(group);
  1093. }
  1094. /*
  1095. RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
  1096. */
  1097. static void source_query_send_by_flag(struct igmp_group *group,
  1098. int num_sources_tosend)
  1099. {
  1100. struct igmp_sock *igmp;
  1101. struct pim_interface *pim_ifp;
  1102. struct listnode *src_node;
  1103. struct igmp_source *src;
  1104. long lmqc; /* Last Member Query Count */
  1105. long lmqi_msec; /* Last Member Query Interval */
  1106. long lmqt_msec; /* Last Member Query Time */
  1107. zassert(num_sources_tosend > 0);
  1108. igmp = group->group_igmp_sock;
  1109. pim_ifp = igmp->interface->info;
  1110. lmqc = igmp->querier_robustness_variable;
  1111. lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
  1112. lmqt_msec = lmqc * lmqi_msec;
  1113. /*
  1114. RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
  1115. (...) for each of the sources in X of group G, with source timer larger
  1116. than LMQT:
  1117. o Set number of retransmissions for each source to [Last Member
  1118. Query Count].
  1119. o Lower source timer to LMQT.
  1120. */
  1121. for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
  1122. if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
  1123. /* source "src" in X of group G */
  1124. if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
  1125. src->source_query_retransmit_count = lmqc;
  1126. igmp_source_timer_lower_to_lmqt(src);
  1127. }
  1128. }
  1129. }
  1130. /* send group-and-source specific queries */
  1131. group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
  1132. /* make sure group retransmit timer is running */
  1133. group_retransmit_timer_on(group);
  1134. }
  1135. static void block_excl(struct igmp_group *group,
  1136. int num_sources, struct in_addr *sources)
  1137. {
  1138. int num_sources_tosend = 0;
  1139. int i;
  1140. /* 1. clear off SEND flag from all known sources (X,Y) */
  1141. source_clear_send_flag(group->group_source_list);
  1142. /* 2. scan received sources (A) */
  1143. for (i = 0; i < num_sources; ++i) {
  1144. struct igmp_source *source;
  1145. struct in_addr *src_addr;
  1146. src_addr = sources + i;
  1147. /* lookup reported source (A) in known sources (X,Y) */
  1148. source = igmp_find_source_by_addr(group, *src_addr);
  1149. if (!source) {
  1150. /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
  1151. long group_timer_msec;
  1152. source = source_new(group, *src_addr);
  1153. if (!source) {
  1154. /* ugh, internal malloc failure, skip source */
  1155. continue;
  1156. }
  1157. zassert(!source->t_source_timer); /* timer == 0 */
  1158. group_timer_msec = igmp_group_timer_remain_msec(group);
  1159. igmp_source_timer_on(group, source, group_timer_msec);
  1160. zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
  1161. }
  1162. if (source->t_source_timer) {
  1163. /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
  1164. IGMP_SOURCE_DO_SEND(source->source_flags);
  1165. ++num_sources_tosend;
  1166. }
  1167. }
  1168. /* 5. send sources marked with SEND flag: Q(G,A-Y) */
  1169. if (num_sources_tosend > 0) {
  1170. source_query_send_by_flag(group, num_sources_tosend);
  1171. }
  1172. }
  1173. static void block_incl(struct igmp_group *group,
  1174. int num_sources, struct in_addr *sources)
  1175. {
  1176. int num_sources_tosend = 0;
  1177. int i;
  1178. /* 1. clear off SEND flag from all known sources (B) */
  1179. source_clear_send_flag(group->group_source_list);
  1180. /* 2. scan received sources (A) */
  1181. for (i = 0; i < num_sources; ++i) {
  1182. struct igmp_source *source;
  1183. struct in_addr *src_addr;
  1184. src_addr = sources + i;
  1185. /* lookup reported source (A) in known sources (B) */
  1186. source = igmp_find_source_by_addr(group, *src_addr);
  1187. if (source) {
  1188. /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
  1189. IGMP_SOURCE_DO_SEND(source->source_flags);
  1190. ++num_sources_tosend;
  1191. }
  1192. }
  1193. /* 4. send sources marked with SEND flag: Q(G,A*B) */
  1194. if (num_sources_tosend > 0) {
  1195. source_query_send_by_flag(group, num_sources_tosend);
  1196. }
  1197. }
  1198. void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
  1199. struct in_addr group_addr,
  1200. int num_sources, struct in_addr *sources)
  1201. {
  1202. struct interface *ifp = igmp->interface;
  1203. struct igmp_group *group;
  1204. on_trace(__PRETTY_FUNCTION__,
  1205. ifp, from, group_addr, num_sources, sources);
  1206. /* non-existant group is created as INCLUDE {empty} */
  1207. group = igmp_add_group_by_addr(igmp, group_addr);
  1208. if (!group) {
  1209. return;
  1210. }
  1211. if (group->group_filtermode_isexcl) {
  1212. /* EXCLUDE mode */
  1213. block_excl(group, num_sources, sources);
  1214. }
  1215. else {
  1216. /* INCLUDE mode */
  1217. block_incl(group, num_sources, sources);
  1218. }
  1219. }
  1220. void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
  1221. {
  1222. struct igmp_sock *igmp;
  1223. struct interface *ifp;
  1224. struct pim_interface *pim_ifp;
  1225. char *ifname;
  1226. int lmqi_dsec; /* Last Member Query Interval */
  1227. int lmqc; /* Last Member Query Count */
  1228. int lmqt_msec; /* Last Member Query Time */
  1229. /*
  1230. RFC 3376: 6.2.2. Definition of Group Timers
  1231. The group timer is only used when a group is in EXCLUDE mode and
  1232. it represents the time for the *filter-mode* of the group to
  1233. expire and switch to INCLUDE mode.
  1234. */
  1235. if (!group->group_filtermode_isexcl) {
  1236. return;
  1237. }
  1238. igmp = group->group_igmp_sock;
  1239. ifp = igmp->interface;
  1240. pim_ifp = ifp->info;
  1241. ifname = ifp->name;
  1242. lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
  1243. lmqc = igmp->querier_robustness_variable;
  1244. lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
  1245. if (PIM_DEBUG_IGMP_TRACE) {
  1246. char group_str[100];
  1247. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  1248. zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
  1249. __PRETTY_FUNCTION__,
  1250. group_str, ifname,
  1251. lmqc, lmqi_dsec, lmqt_msec);
  1252. }
  1253. zassert(group->group_filtermode_isexcl);
  1254. igmp_group_timer_on(group, lmqt_msec, ifname);
  1255. }
  1256. void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
  1257. {
  1258. struct igmp_group *group;
  1259. struct igmp_sock *igmp;
  1260. struct interface *ifp;
  1261. struct pim_interface *pim_ifp;
  1262. char *ifname;
  1263. int lmqi_dsec; /* Last Member Query Interval */
  1264. int lmqc; /* Last Member Query Count */
  1265. int lmqt_msec; /* Last Member Query Time */
  1266. group = source->source_group;
  1267. igmp = group->group_igmp_sock;
  1268. ifp = igmp->interface;
  1269. pim_ifp = ifp->info;
  1270. ifname = ifp->name;
  1271. lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
  1272. lmqc = igmp->querier_robustness_variable;
  1273. lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
  1274. if (PIM_DEBUG_IGMP_TRACE) {
  1275. char group_str[100];
  1276. char source_str[100];
  1277. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  1278. pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  1279. zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
  1280. __PRETTY_FUNCTION__,
  1281. group_str, source_str, ifname,
  1282. lmqc, lmqi_dsec, lmqt_msec);
  1283. }
  1284. igmp_source_timer_on(group, source, lmqt_msec);
  1285. }
  1286. /*
  1287. Copy sources to message:
  1288. struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
  1289. if (num_sources > 0) {
  1290. struct listnode *node;
  1291. struct igmp_source *src;
  1292. int i = 0;
  1293. for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
  1294. sources[i++] = src->source_addr;
  1295. }
  1296. }
  1297. */
  1298. void pim_igmp_send_membership_query(struct igmp_group *group,
  1299. int fd,
  1300. const char *ifname,
  1301. char *query_buf,
  1302. int query_buf_size,
  1303. int num_sources,
  1304. struct in_addr dst_addr,
  1305. struct in_addr group_addr,
  1306. int query_max_response_time_dsec,
  1307. uint8_t s_flag,
  1308. uint8_t querier_robustness_variable,
  1309. uint16_t querier_query_interval)
  1310. {
  1311. ssize_t msg_size;
  1312. uint8_t max_resp_code;
  1313. uint8_t qqic;
  1314. ssize_t sent;
  1315. struct sockaddr_in to;
  1316. socklen_t tolen;
  1317. uint16_t checksum;
  1318. zassert(num_sources >= 0);
  1319. msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
  1320. if (msg_size > query_buf_size) {
  1321. zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
  1322. __FILE__, __PRETTY_FUNCTION__,
  1323. msg_size, query_buf_size);
  1324. return;
  1325. }
  1326. s_flag = PIM_FORCE_BOOLEAN(s_flag);
  1327. zassert((s_flag == 0) || (s_flag == 1));
  1328. max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
  1329. qqic = igmp_msg_encode16to8(querier_query_interval);
  1330. /*
  1331. RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
  1332. If non-zero, the QRV field contains the [Robustness Variable]
  1333. value used by the querier, i.e., the sender of the Query. If the
  1334. querier's [Robustness Variable] exceeds 7, the maximum value of
  1335. the QRV field, the QRV is set to zero.
  1336. */
  1337. if (querier_robustness_variable > 7) {
  1338. querier_robustness_variable = 0;
  1339. }
  1340. query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
  1341. query_buf[1] = max_resp_code;
  1342. *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
  1343. memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
  1344. query_buf[8] = (s_flag << 3) | querier_robustness_variable;
  1345. query_buf[9] = qqic;
  1346. *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
  1347. checksum = in_cksum(query_buf, msg_size);
  1348. *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
  1349. if (PIM_DEBUG_IGMP_PACKETS) {
  1350. char dst_str[100];
  1351. char group_str[100];
  1352. pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
  1353. pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
  1354. zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
  1355. __PRETTY_FUNCTION__,
  1356. dst_str, ifname, group_str, num_sources,
  1357. msg_size, s_flag, querier_robustness_variable,
  1358. querier_query_interval, qqic, checksum);
  1359. }
  1360. memset(&to, 0, sizeof(to));
  1361. to.sin_family = AF_INET;
  1362. to.sin_addr = dst_addr;
  1363. tolen = sizeof(to);
  1364. sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
  1365. (struct sockaddr *)&to, tolen);
  1366. if (sent != (ssize_t) msg_size) {
  1367. int e = errno;
  1368. char dst_str[100];
  1369. char group_str[100];
  1370. pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
  1371. pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
  1372. if (sent < 0) {
  1373. zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
  1374. __PRETTY_FUNCTION__,
  1375. dst_str, ifname, group_str, msg_size,
  1376. e, safe_strerror(e));
  1377. }
  1378. else {
  1379. zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
  1380. __PRETTY_FUNCTION__,
  1381. dst_str, ifname, group_str,
  1382. msg_size, sent);
  1383. }
  1384. return;
  1385. }
  1386. /*
  1387. s_flag sanity test: s_flag must be set for general queries
  1388. RFC 3376: 6.6.1. Timer Updates
  1389. When a router sends or receives a query with a clear Suppress
  1390. Router-Side Processing flag, it must update its timers to reflect
  1391. the correct timeout values for the group or sources being queried.
  1392. General queries don't trigger timer update.
  1393. */
  1394. if (!s_flag) {
  1395. /* general query? */
  1396. if (PIM_INADDR_IS_ANY(group_addr)) {
  1397. char dst_str[100];
  1398. char group_str[100];
  1399. pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
  1400. pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
  1401. zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
  1402. __PRETTY_FUNCTION__,
  1403. dst_str, ifname, group_str, num_sources);
  1404. }
  1405. }
  1406. }