pim_igmp.c 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435
  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 "memory.h"
  20. #include "pimd.h"
  21. #include "pim_igmp.h"
  22. #include "pim_igmpv3.h"
  23. #include "pim_iface.h"
  24. #include "pim_sock.h"
  25. #include "pim_mroute.h"
  26. #include "pim_str.h"
  27. #include "pim_util.h"
  28. #include "pim_time.h"
  29. #include "pim_zebra.h"
  30. #define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE (1)
  31. #define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE (2)
  32. #define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
  33. #define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
  34. #define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES (5)
  35. #define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES (6)
  36. static void group_timer_off(struct igmp_group *group);
  37. static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
  38. struct in_addr group_addr);
  39. static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex,
  40. uint32_t pim_options)
  41. {
  42. int fd;
  43. int join = 0;
  44. struct in_addr group;
  45. fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, 1 /* loop=true */);
  46. if (fd < 0)
  47. return -1;
  48. if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
  49. if (inet_aton(PIM_ALL_ROUTERS, &group)) {
  50. if (!pim_socket_join(fd, group, ifaddr, ifindex))
  51. ++join;
  52. }
  53. else {
  54. zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
  55. __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
  56. PIM_ALL_ROUTERS, errno, safe_strerror(errno));
  57. }
  58. }
  59. /*
  60. IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1
  61. IGMP routers must receive general queries for querier election.
  62. */
  63. if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
  64. if (!pim_socket_join(fd, group, ifaddr, ifindex))
  65. ++join;
  66. }
  67. else {
  68. zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
  69. __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
  70. PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
  71. }
  72. if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
  73. if (!pim_socket_join(fd, group, ifaddr, ifindex)) {
  74. ++join;
  75. }
  76. }
  77. else {
  78. zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
  79. __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
  80. PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
  81. }
  82. if (!join) {
  83. zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
  84. fd, inet_ntoa(ifaddr));
  85. close(fd);
  86. fd = -1;
  87. }
  88. return fd;
  89. }
  90. #undef IGMP_SOCK_DUMP
  91. #ifdef IGMP_SOCK_DUMP
  92. static void igmp_sock_dump(array_t *igmp_sock_array)
  93. {
  94. int size = array_size(igmp_sock_array);
  95. int i;
  96. for (i = 0; i < size; ++i) {
  97. struct igmp_sock *igmp = array_get(igmp_sock_array, i);
  98. zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d",
  99. __FILE__, __PRETTY_FUNCTION__,
  100. i, size,
  101. inet_ntoa(igmp->ifaddr),
  102. igmp->fd);
  103. }
  104. }
  105. #endif
  106. struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
  107. struct in_addr ifaddr)
  108. {
  109. struct listnode *sock_node;
  110. struct igmp_sock *igmp;
  111. #ifdef IGMP_SOCK_DUMP
  112. igmp_sock_dump(igmp_sock_list);
  113. #endif
  114. for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
  115. if (ifaddr.s_addr == igmp->ifaddr.s_addr)
  116. return igmp;
  117. return 0;
  118. }
  119. struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list,
  120. int fd)
  121. {
  122. struct listnode *sock_node;
  123. struct igmp_sock *igmp;
  124. for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
  125. if (fd == igmp->fd)
  126. return igmp;
  127. return 0;
  128. }
  129. static int pim_igmp_other_querier_expire(struct thread *t)
  130. {
  131. struct igmp_sock *igmp;
  132. zassert(t);
  133. igmp = THREAD_ARG(t);
  134. zassert(igmp);
  135. zassert(igmp->t_other_querier_timer);
  136. zassert(!igmp->t_igmp_query_timer);
  137. if (PIM_DEBUG_IGMP_TRACE) {
  138. char ifaddr_str[100];
  139. pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  140. zlog_debug("%s: Querier %s resuming",
  141. __PRETTY_FUNCTION__,
  142. ifaddr_str);
  143. }
  144. igmp->t_other_querier_timer = 0;
  145. /*
  146. We are the current querier, then
  147. re-start sending general queries.
  148. */
  149. pim_igmp_general_query_on(igmp);
  150. return 0;
  151. }
  152. void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
  153. {
  154. long other_querier_present_interval_msec;
  155. struct pim_interface *pim_ifp;
  156. zassert(igmp);
  157. zassert(igmp->interface);
  158. zassert(igmp->interface->info);
  159. pim_ifp = igmp->interface->info;
  160. if (igmp->t_other_querier_timer) {
  161. /*
  162. There is other querier present already,
  163. then reset the other-querier-present timer.
  164. */
  165. if (PIM_DEBUG_IGMP_TRACE) {
  166. char ifaddr_str[100];
  167. pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  168. zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
  169. ifaddr_str);
  170. }
  171. THREAD_OFF(igmp->t_other_querier_timer);
  172. zassert(!igmp->t_other_querier_timer);
  173. }
  174. else {
  175. /*
  176. We are the current querier, then stop sending general queries:
  177. igmp->t_igmp_query_timer = 0;
  178. */
  179. pim_igmp_general_query_off(igmp);
  180. }
  181. /*
  182. Since this socket is starting the other-querier-present timer,
  183. there should not be periodic query timer for this socket.
  184. */
  185. zassert(!igmp->t_igmp_query_timer);
  186. /*
  187. RFC 3376: 8.5. Other Querier Present Interval
  188. The Other Querier Present Interval is the length of time that must
  189. pass before a multicast router decides that there is no longer
  190. another multicast router which should be the querier. This value
  191. MUST be ((the Robustness Variable) times (the Query Interval)) plus
  192. (one half of one Query Response Interval).
  193. other_querier_present_interval_msec = \
  194. igmp->querier_robustness_variable * \
  195. 1000 * igmp->querier_query_interval + \
  196. 100 * (pim_ifp->query_max_response_time_dsec >> 1);
  197. */
  198. other_querier_present_interval_msec =
  199. PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable,
  200. igmp->querier_query_interval,
  201. pim_ifp->igmp_query_max_response_time_dsec);
  202. if (PIM_DEBUG_IGMP_TRACE) {
  203. char ifaddr_str[100];
  204. pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  205. zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
  206. ifaddr_str,
  207. other_querier_present_interval_msec / 1000,
  208. other_querier_present_interval_msec % 1000);
  209. }
  210. THREAD_TIMER_MSEC_ON(master, igmp->t_other_querier_timer,
  211. pim_igmp_other_querier_expire,
  212. igmp, other_querier_present_interval_msec);
  213. }
  214. void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
  215. {
  216. zassert(igmp);
  217. if (PIM_DEBUG_IGMP_TRACE) {
  218. if (igmp->t_other_querier_timer) {
  219. char ifaddr_str[100];
  220. pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  221. zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
  222. ifaddr_str, igmp->fd, igmp->interface->name);
  223. }
  224. }
  225. THREAD_OFF(igmp->t_other_querier_timer);
  226. zassert(!igmp->t_other_querier_timer);
  227. }
  228. static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
  229. int max_resp_code,
  230. struct in_addr from, const char *from_str,
  231. char *igmp_msg, int igmp_msg_len)
  232. {
  233. struct interface *ifp;
  234. struct pim_interface *pim_ifp;
  235. uint8_t resv_s_qrv = 0;
  236. uint8_t s_flag = 0;
  237. uint8_t qrv = 0;
  238. struct in_addr group_addr;
  239. uint16_t recv_checksum;
  240. uint16_t checksum;
  241. int i;
  242. //group_addr = *(struct in_addr *)(igmp_msg + 4);
  243. memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
  244. ifp = igmp->interface;
  245. pim_ifp = ifp->info;
  246. recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
  247. /* for computing checksum */
  248. *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
  249. checksum = in_cksum(igmp_msg, igmp_msg_len);
  250. if (checksum != recv_checksum) {
  251. zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
  252. query_version, from_str, ifp->name, recv_checksum, checksum);
  253. return -1;
  254. }
  255. if (PIM_DEBUG_IGMP_PACKETS) {
  256. char group_str[100];
  257. pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
  258. zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s",
  259. query_version, from_str, ifp->name,
  260. igmp_msg_len, checksum, group_str);
  261. }
  262. /*
  263. RFC 3376: 6.6.2. Querier Election
  264. When a router receives a query with a lower IP address, it sets
  265. the Other-Querier-Present timer to Other Querier Present Interval
  266. and ceases to send queries on the network if it was the previously
  267. elected querier.
  268. */
  269. if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
  270. if (PIM_DEBUG_IGMP_TRACE) {
  271. char ifaddr_str[100];
  272. pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  273. zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
  274. ifp->name,
  275. ifaddr_str, ntohl(igmp->ifaddr.s_addr),
  276. from_str, ntohl(from.s_addr));
  277. }
  278. pim_igmp_other_querier_timer_on(igmp);
  279. }
  280. if (query_version == 3) {
  281. /*
  282. RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
  283. Routers adopt the QRV value from the most recently received Query
  284. as their own [Robustness Variable] value, unless that most
  285. recently received QRV was zero, in which case the receivers use
  286. the default [Robustness Variable] value specified in section 8.1
  287. or a statically configured value.
  288. */
  289. resv_s_qrv = igmp_msg[8];
  290. qrv = 7 & resv_s_qrv;
  291. igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
  292. }
  293. /*
  294. RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
  295. Multicast routers that are not the current querier adopt the QQI
  296. value from the most recently received Query as their own [Query
  297. Interval] value, unless that most recently received QQI was zero,
  298. in which case the receiving routers use the default.
  299. */
  300. if (igmp->t_other_querier_timer && query_version == 3) {
  301. /* other querier present */
  302. uint8_t qqic;
  303. uint16_t qqi;
  304. qqic = igmp_msg[9];
  305. qqi = igmp_msg_decode8to16(qqic);
  306. igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
  307. if (PIM_DEBUG_IGMP_TRACE) {
  308. char ifaddr_str[100];
  309. pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  310. zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
  311. ifaddr_str,
  312. qqi ? "recv-non-default" : "default",
  313. igmp->querier_query_interval,
  314. qqic,
  315. from_str);
  316. }
  317. }
  318. /*
  319. RFC 3376: 6.6.1. Timer Updates
  320. When a router sends or receives a query with a clear Suppress
  321. Router-Side Processing flag, it must update its timers to reflect
  322. the correct timeout values for the group or sources being queried.
  323. General queries don't trigger timer update.
  324. */
  325. if (query_version == 3) {
  326. s_flag = (1 << 3) & resv_s_qrv;
  327. }
  328. else {
  329. /* Neither V1 nor V2 have this field. Pimd should really go into
  330. * a compatibility mode here and run as V2 (or V1) but it doesn't
  331. * so for now, lets just set the flag to suppress these timer updates.
  332. */
  333. s_flag = 1;
  334. }
  335. if (!s_flag) {
  336. /* s_flag is clear */
  337. if (PIM_INADDR_IS_ANY(group_addr)) {
  338. /* this is a general query */
  339. /* log that general query should have the s_flag set */
  340. zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear",
  341. query_version, from_str, ifp->name);
  342. }
  343. else {
  344. struct igmp_group *group;
  345. /* this is a non-general query: perform timer updates */
  346. group = find_group_by_addr(igmp, group_addr);
  347. if (group) {
  348. int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
  349. /*
  350. RFC 3376: 6.6.1. Timer Updates
  351. Query Q(G,A): Source Timer for sources in A are lowered to LMQT
  352. Query Q(G): Group Timer is lowered to LMQT
  353. */
  354. if (recv_num_sources < 1) {
  355. /* Query Q(G): Group Timer is lowered to LMQT */
  356. igmp_group_timer_lower_to_lmqt(group);
  357. }
  358. else {
  359. /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
  360. /* Scan sources in query and lower their timers to LMQT */
  361. struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
  362. for (i = 0; i < recv_num_sources; ++i) {
  363. //struct in_addr src_addr = sources[i];
  364. //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr);
  365. struct in_addr src_addr;
  366. struct igmp_source *src;
  367. memcpy(&src_addr, sources + i, sizeof(struct in_addr));
  368. src = igmp_find_source_by_addr(group, src_addr);
  369. if (src) {
  370. igmp_source_timer_lower_to_lmqt(src);
  371. }
  372. }
  373. }
  374. }
  375. else {
  376. char group_str[100];
  377. pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
  378. zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update",
  379. query_version, from_str, ifp->name, group_str);
  380. }
  381. }
  382. } /* s_flag is clear: timer updates */
  383. return 0;
  384. }
  385. static int igmp_v3_report(struct igmp_sock *igmp,
  386. struct in_addr from, const char *from_str,
  387. char *igmp_msg, int igmp_msg_len)
  388. {
  389. uint16_t recv_checksum;
  390. uint16_t checksum;
  391. int num_groups;
  392. uint8_t *group_record;
  393. uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
  394. struct interface *ifp = igmp->interface;
  395. int i;
  396. if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
  397. zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
  398. from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
  399. return -1;
  400. }
  401. recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
  402. /* for computing checksum */
  403. *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
  404. checksum = in_cksum(igmp_msg, igmp_msg_len);
  405. if (checksum != recv_checksum) {
  406. zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
  407. from_str, ifp->name, recv_checksum, checksum);
  408. return -1;
  409. }
  410. num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
  411. if (num_groups < 1) {
  412. zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
  413. from_str, ifp->name);
  414. return -1;
  415. }
  416. if (PIM_DEBUG_IGMP_PACKETS) {
  417. zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
  418. from_str, ifp->name, igmp_msg_len, checksum, num_groups);
  419. }
  420. group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
  421. /* Scan groups */
  422. for (i = 0; i < num_groups; ++i) {
  423. struct in_addr rec_group;
  424. uint8_t *sources;
  425. uint8_t *src;
  426. int rec_type;
  427. int rec_auxdatalen;
  428. int rec_num_sources;
  429. int j;
  430. if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
  431. zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
  432. from_str, ifp->name);
  433. return -1;
  434. }
  435. rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
  436. rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
  437. rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
  438. //rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET);
  439. memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
  440. if (PIM_DEBUG_IGMP_PACKETS) {
  441. zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
  442. from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
  443. }
  444. /* Scan sources */
  445. sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
  446. for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
  447. if ((src + 4) > report_pastend) {
  448. zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
  449. from_str, ifp->name);
  450. return -1;
  451. }
  452. if (PIM_DEBUG_IGMP_PACKETS) {
  453. char src_str[200];
  454. if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
  455. sprintf(src_str, "<source?>");
  456. zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
  457. from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
  458. }
  459. } /* for (sources) */
  460. switch (rec_type) {
  461. case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
  462. igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
  463. break;
  464. case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
  465. igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
  466. break;
  467. case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
  468. igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
  469. break;
  470. case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
  471. igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
  472. break;
  473. case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
  474. igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
  475. break;
  476. case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
  477. igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
  478. break;
  479. default:
  480. zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
  481. from_str, ifp->name, rec_type);
  482. }
  483. group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
  484. } /* for (group records) */
  485. return 0;
  486. }
  487. static void on_trace(const char *label,
  488. struct interface *ifp, struct in_addr from)
  489. {
  490. if (PIM_DEBUG_IGMP_TRACE) {
  491. char from_str[100];
  492. pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
  493. zlog_debug("%s: from %s on %s",
  494. label, from_str, ifp->name);
  495. }
  496. }
  497. static int igmp_v2_report(struct igmp_sock *igmp,
  498. struct in_addr from, const char *from_str,
  499. char *igmp_msg, int igmp_msg_len)
  500. {
  501. struct interface *ifp = igmp->interface;
  502. struct igmp_group *group;
  503. struct in_addr group_addr;
  504. on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
  505. if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
  506. zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d",
  507. from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
  508. return -1;
  509. }
  510. if (PIM_DEBUG_IGMP_TRACE) {
  511. zlog_warn("%s %s: FIXME WRITEME",
  512. __FILE__, __PRETTY_FUNCTION__);
  513. }
  514. //group_addr = *(struct in_addr *)(igmp_msg + 4);
  515. memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
  516. /* non-existant group is created as INCLUDE {empty} */
  517. group = igmp_add_group_by_addr(igmp, group_addr);
  518. if (!group) {
  519. return -1;
  520. }
  521. group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec();
  522. return 0;
  523. }
  524. static int igmp_v2_leave(struct igmp_sock *igmp,
  525. struct in_addr from, const char *from_str,
  526. char *igmp_msg, int igmp_msg_len)
  527. {
  528. struct interface *ifp = igmp->interface;
  529. on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
  530. if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
  531. zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d",
  532. from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
  533. return -1;
  534. }
  535. if (PIM_DEBUG_IGMP_TRACE) {
  536. zlog_warn("%s %s: FIXME WRITEME",
  537. __FILE__, __PRETTY_FUNCTION__);
  538. }
  539. return 0;
  540. }
  541. static int igmp_v1_report(struct igmp_sock *igmp,
  542. struct in_addr from, const char *from_str,
  543. char *igmp_msg, int igmp_msg_len)
  544. {
  545. struct interface *ifp = igmp->interface;
  546. struct igmp_group *group;
  547. struct in_addr group_addr;
  548. on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
  549. if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
  550. zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
  551. from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
  552. return -1;
  553. }
  554. if (PIM_DEBUG_IGMP_TRACE) {
  555. zlog_warn("%s %s: FIXME WRITEME",
  556. __FILE__, __PRETTY_FUNCTION__);
  557. }
  558. //group_addr = *(struct in_addr *)(igmp_msg + 4);
  559. memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
  560. /* non-existant group is created as INCLUDE {empty} */
  561. group = igmp_add_group_by_addr(igmp, group_addr);
  562. if (!group) {
  563. return -1;
  564. }
  565. group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
  566. return 0;
  567. }
  568. int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
  569. {
  570. struct ip *ip_hdr;
  571. size_t ip_hlen; /* ip header length in bytes */
  572. char *igmp_msg;
  573. int igmp_msg_len;
  574. int msg_type;
  575. char from_str[100];
  576. char to_str[100];
  577. if (len < sizeof(*ip_hdr)) {
  578. zlog_warn("IGMP packet size=%zu shorter than minimum=%zu",
  579. len, sizeof(*ip_hdr));
  580. return -1;
  581. }
  582. ip_hdr = (struct ip *) buf;
  583. pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str , sizeof(from_str));
  584. pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str , sizeof(to_str));
  585. ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
  586. if (PIM_DEBUG_IGMP_PACKETS) {
  587. zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
  588. from_str, to_str, igmp->interface->name, len, ip_hlen, ip_hdr->ip_p);
  589. }
  590. if (ip_hdr->ip_p != PIM_IP_PROTO_IGMP) {
  591. zlog_warn("IP packet protocol=%d is not IGMP=%d",
  592. ip_hdr->ip_p, PIM_IP_PROTO_IGMP);
  593. return -1;
  594. }
  595. if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
  596. zlog_warn("IP packet header size=%zu shorter than minimum=%d",
  597. ip_hlen, PIM_IP_HEADER_MIN_LEN);
  598. return -1;
  599. }
  600. if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
  601. zlog_warn("IP packet header size=%zu greater than maximum=%d",
  602. ip_hlen, PIM_IP_HEADER_MAX_LEN);
  603. return -1;
  604. }
  605. igmp_msg = buf + ip_hlen;
  606. msg_type = *igmp_msg;
  607. igmp_msg_len = len - ip_hlen;
  608. if (PIM_DEBUG_IGMP_PACKETS) {
  609. zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
  610. from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, msg_type,
  611. igmp_msg_len);
  612. }
  613. if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
  614. zlog_warn("IGMP message size=%d shorter than minimum=%d",
  615. igmp_msg_len, PIM_IGMP_MIN_LEN);
  616. return -1;
  617. }
  618. switch (msg_type) {
  619. case PIM_IGMP_MEMBERSHIP_QUERY:
  620. {
  621. int max_resp_code = igmp_msg[1];
  622. int query_version;
  623. /*
  624. RFC 3376: 7.1. Query Version Distinctions
  625. IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
  626. IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
  627. IGMPv3 Query: length >= 12 octets
  628. */
  629. if (igmp_msg_len == 8) {
  630. query_version = max_resp_code ? 2 : 1;
  631. }
  632. else if (igmp_msg_len >= 12) {
  633. query_version = 3;
  634. }
  635. else {
  636. zlog_warn("Unknown IGMP query version");
  637. return -1;
  638. }
  639. return recv_igmp_query(igmp, query_version, max_resp_code,
  640. ip_hdr->ip_src, from_str,
  641. igmp_msg, igmp_msg_len);
  642. }
  643. case PIM_IGMP_V3_MEMBERSHIP_REPORT:
  644. return igmp_v3_report(igmp, ip_hdr->ip_src, from_str,
  645. igmp_msg, igmp_msg_len);
  646. case PIM_IGMP_V2_MEMBERSHIP_REPORT:
  647. return igmp_v2_report(igmp, ip_hdr->ip_src, from_str,
  648. igmp_msg, igmp_msg_len);
  649. case PIM_IGMP_V1_MEMBERSHIP_REPORT:
  650. return igmp_v1_report(igmp, ip_hdr->ip_src, from_str,
  651. igmp_msg, igmp_msg_len);
  652. case PIM_IGMP_V2_LEAVE_GROUP:
  653. return igmp_v2_leave(igmp, ip_hdr->ip_src, from_str,
  654. igmp_msg, igmp_msg_len);
  655. }
  656. zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
  657. return -1;
  658. }
  659. static int pim_igmp_general_query(struct thread *t);
  660. void pim_igmp_general_query_on(struct igmp_sock *igmp)
  661. {
  662. struct pim_interface *pim_ifp;
  663. int startup_mode;
  664. int query_interval;
  665. zassert(igmp);
  666. zassert(igmp->interface);
  667. /*
  668. Since this socket is starting as querier,
  669. there should not exist a timer for other-querier-present.
  670. */
  671. zassert(!igmp->t_other_querier_timer);
  672. pim_ifp = igmp->interface->info;
  673. zassert(pim_ifp);
  674. /*
  675. RFC 3376: 8.6. Startup Query Interval
  676. The Startup Query Interval is the interval between General Queries
  677. sent by a Querier on startup. Default: 1/4 the Query Interval.
  678. */
  679. startup_mode = igmp->startup_query_count > 0;
  680. if (startup_mode) {
  681. --igmp->startup_query_count;
  682. /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
  683. query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
  684. }
  685. else {
  686. query_interval = igmp->querier_query_interval;
  687. }
  688. if (PIM_DEBUG_IGMP_TRACE) {
  689. char ifaddr_str[100];
  690. pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  691. zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
  692. ifaddr_str,
  693. query_interval,
  694. startup_mode ? "startup" : "non-startup",
  695. igmp->fd);
  696. }
  697. igmp->t_igmp_query_timer = 0;
  698. zassert(!igmp->t_igmp_query_timer);
  699. THREAD_TIMER_ON(master, igmp->t_igmp_query_timer,
  700. pim_igmp_general_query,
  701. igmp, query_interval);
  702. }
  703. void pim_igmp_general_query_off(struct igmp_sock *igmp)
  704. {
  705. zassert(igmp);
  706. if (PIM_DEBUG_IGMP_TRACE) {
  707. if (igmp->t_igmp_query_timer) {
  708. char ifaddr_str[100];
  709. pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  710. zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
  711. ifaddr_str, igmp->fd, igmp->interface->name);
  712. }
  713. }
  714. THREAD_OFF(igmp->t_igmp_query_timer);
  715. zassert(!igmp->t_igmp_query_timer);
  716. }
  717. /* Issue IGMP general query */
  718. static int pim_igmp_general_query(struct thread *t)
  719. {
  720. char query_buf[PIM_IGMP_BUFSIZE_WRITE];
  721. struct igmp_sock *igmp;
  722. struct in_addr dst_addr;
  723. struct in_addr group_addr;
  724. struct pim_interface *pim_ifp;
  725. zassert(t);
  726. igmp = THREAD_ARG(t);
  727. zassert(igmp);
  728. zassert(igmp->interface);
  729. zassert(igmp->interface->info);
  730. pim_ifp = igmp->interface->info;
  731. /*
  732. RFC3376: 4.1.12. IP Destination Addresses for Queries
  733. In IGMPv3, General Queries are sent with an IP destination address
  734. of 224.0.0.1, the all-systems multicast address. Group-Specific
  735. and Group-and-Source-Specific Queries are sent with an IP
  736. destination address equal to the multicast address of interest.
  737. */
  738. dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
  739. group_addr.s_addr = PIM_NET_INADDR_ANY;
  740. if (PIM_DEBUG_IGMP_TRACE) {
  741. char querier_str[100];
  742. char dst_str[100];
  743. pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
  744. sizeof(querier_str));
  745. pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
  746. zlog_debug("Querier %s issuing IGMP general query to %s on %s",
  747. querier_str, dst_str, igmp->interface->name);
  748. }
  749. pim_igmp_send_membership_query(0 /* igmp_group */,
  750. igmp->fd,
  751. igmp->interface->name,
  752. query_buf,
  753. sizeof(query_buf),
  754. 0 /* num_sources */,
  755. dst_addr,
  756. group_addr,
  757. pim_ifp->igmp_query_max_response_time_dsec,
  758. 1 /* s_flag: always set for general queries */,
  759. igmp->querier_robustness_variable,
  760. igmp->querier_query_interval);
  761. pim_igmp_general_query_on(igmp);
  762. return 0;
  763. }
  764. static int pim_igmp_read(struct thread *t);
  765. static void igmp_read_on(struct igmp_sock *igmp)
  766. {
  767. zassert(igmp);
  768. if (PIM_DEBUG_IGMP_TRACE) {
  769. zlog_debug("Scheduling READ event on IGMP socket fd=%d",
  770. igmp->fd);
  771. }
  772. igmp->t_igmp_read = 0;
  773. zassert(!igmp->t_igmp_read);
  774. THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
  775. }
  776. static int pim_igmp_read(struct thread *t)
  777. {
  778. struct igmp_sock *igmp;
  779. int fd;
  780. struct sockaddr_in from;
  781. struct sockaddr_in to;
  782. socklen_t fromlen = sizeof(from);
  783. socklen_t tolen = sizeof(to);
  784. uint8_t buf[PIM_IGMP_BUFSIZE_READ];
  785. int len;
  786. ifindex_t ifindex = -1;
  787. int result = -1; /* defaults to bad */
  788. zassert(t);
  789. igmp = THREAD_ARG(t);
  790. zassert(igmp);
  791. fd = THREAD_FD(t);
  792. zassert(fd == igmp->fd);
  793. len = pim_socket_recvfromto(fd, buf, sizeof(buf),
  794. &from, &fromlen,
  795. &to, &tolen,
  796. &ifindex);
  797. if (len < 0) {
  798. zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
  799. fd, errno, safe_strerror(errno));
  800. goto done;
  801. }
  802. if (PIM_DEBUG_IGMP_PACKETS) {
  803. char from_str[100];
  804. char to_str[100];
  805. if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
  806. sprintf(from_str, "<from?>");
  807. if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
  808. sprintf(to_str, "<to?>");
  809. zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
  810. len, from_str, to_str, fd, ifindex, igmp->interface->ifindex);
  811. }
  812. #ifdef PIM_CHECK_RECV_IFINDEX_SANITY
  813. /* ifindex sanity check */
  814. if (ifindex != (int) igmp->interface->ifindex) {
  815. char from_str[100];
  816. char to_str[100];
  817. struct interface *ifp;
  818. if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
  819. sprintf(from_str, "<from?>");
  820. if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
  821. sprintf(to_str, "<to?>");
  822. ifp = if_lookup_by_index(ifindex);
  823. if (ifp) {
  824. zassert(ifindex == (int) ifp->ifindex);
  825. }
  826. #ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
  827. zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
  828. from_str, to_str, fd,
  829. ifindex, ifp ? ifp->name : "<if-notfound>",
  830. igmp->interface->ifindex, igmp->interface->name);
  831. #endif
  832. goto done;
  833. }
  834. #endif
  835. if (pim_igmp_packet(igmp, (char *)buf, len)) {
  836. goto done;
  837. }
  838. result = 0; /* good */
  839. done:
  840. igmp_read_on(igmp);
  841. return result;
  842. }
  843. static void sock_close(struct igmp_sock *igmp)
  844. {
  845. pim_igmp_other_querier_timer_off(igmp);
  846. pim_igmp_general_query_off(igmp);
  847. if (PIM_DEBUG_IGMP_TRACE) {
  848. if (igmp->t_igmp_read) {
  849. zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
  850. inet_ntoa(igmp->ifaddr), igmp->fd,
  851. igmp->interface->name);
  852. }
  853. }
  854. THREAD_OFF(igmp->t_igmp_read);
  855. zassert(!igmp->t_igmp_read);
  856. if (close(igmp->fd)) {
  857. zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
  858. inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name,
  859. errno, safe_strerror(errno));
  860. }
  861. if (PIM_DEBUG_IGMP_TRACE) {
  862. zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
  863. inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name);
  864. }
  865. }
  866. void igmp_startup_mode_on(struct igmp_sock *igmp)
  867. {
  868. struct pim_interface *pim_ifp;
  869. pim_ifp = igmp->interface->info;
  870. /*
  871. RFC 3376: 8.7. Startup Query Count
  872. The Startup Query Count is the number of Queries sent out on
  873. startup, separated by the Startup Query Interval. Default: the
  874. Robustness Variable.
  875. */
  876. igmp->startup_query_count = igmp->querier_robustness_variable;
  877. /*
  878. Since we're (re)starting, reset QQI to default Query Interval
  879. */
  880. igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
  881. }
  882. static void igmp_group_free(struct igmp_group *group)
  883. {
  884. zassert(!group->t_group_query_retransmit_timer);
  885. zassert(!group->t_group_timer);
  886. zassert(group->group_source_list);
  887. zassert(!listcount(group->group_source_list));
  888. list_free(group->group_source_list);
  889. XFREE(MTYPE_PIM_IGMP_GROUP, group);
  890. }
  891. static void igmp_group_delete(struct igmp_group *group)
  892. {
  893. struct listnode *src_node;
  894. struct listnode *src_nextnode;
  895. struct igmp_source *src;
  896. if (PIM_DEBUG_IGMP_TRACE) {
  897. char group_str[100];
  898. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  899. zlog_debug("Deleting IGMP group %s from socket %d interface %s",
  900. group_str,
  901. group->group_igmp_sock->fd,
  902. group->group_igmp_sock->interface->name);
  903. }
  904. for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, src)) {
  905. igmp_source_delete(src);
  906. }
  907. if (group->t_group_query_retransmit_timer) {
  908. THREAD_OFF(group->t_group_query_retransmit_timer);
  909. zassert(!group->t_group_query_retransmit_timer);
  910. }
  911. group_timer_off(group);
  912. listnode_delete(group->group_igmp_sock->igmp_group_list, group);
  913. igmp_group_free(group);
  914. }
  915. void igmp_group_delete_empty_include(struct igmp_group *group)
  916. {
  917. zassert(!group->group_filtermode_isexcl);
  918. zassert(!listcount(group->group_source_list));
  919. igmp_group_delete(group);
  920. }
  921. void igmp_sock_free(struct igmp_sock *igmp)
  922. {
  923. zassert(!igmp->t_igmp_read);
  924. zassert(!igmp->t_igmp_query_timer);
  925. zassert(!igmp->t_other_querier_timer);
  926. zassert(igmp->igmp_group_list);
  927. zassert(!listcount(igmp->igmp_group_list));
  928. list_free(igmp->igmp_group_list);
  929. XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
  930. }
  931. void igmp_sock_delete(struct igmp_sock *igmp)
  932. {
  933. struct pim_interface *pim_ifp;
  934. struct listnode *grp_node;
  935. struct listnode *grp_nextnode;
  936. struct igmp_group *grp;
  937. for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, grp)) {
  938. igmp_group_delete(grp);
  939. }
  940. sock_close(igmp);
  941. pim_ifp = igmp->interface->info;
  942. listnode_delete(pim_ifp->igmp_socket_list, igmp);
  943. igmp_sock_free(igmp);
  944. }
  945. static struct igmp_sock *igmp_sock_new(int fd,
  946. struct in_addr ifaddr,
  947. struct interface *ifp)
  948. {
  949. struct pim_interface *pim_ifp;
  950. struct igmp_sock *igmp;
  951. pim_ifp = ifp->info;
  952. if (PIM_DEBUG_IGMP_TRACE) {
  953. zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
  954. fd, inet_ntoa(ifaddr), ifp->name);
  955. }
  956. igmp = XMALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
  957. if (!igmp) {
  958. zlog_warn("%s %s: XMALLOC() failure",
  959. __FILE__, __PRETTY_FUNCTION__);
  960. return 0;
  961. }
  962. igmp->igmp_group_list = list_new();
  963. if (!igmp->igmp_group_list) {
  964. zlog_err("%s %s: failure: igmp_group_list = list_new()",
  965. __FILE__, __PRETTY_FUNCTION__);
  966. return 0;
  967. }
  968. igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free;
  969. igmp->fd = fd;
  970. igmp->interface = ifp;
  971. igmp->ifaddr = ifaddr;
  972. igmp->t_igmp_read = 0;
  973. igmp->t_igmp_query_timer = 0;
  974. igmp->t_other_querier_timer = 0; /* no other querier present */
  975. igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable;
  976. igmp->sock_creation = pim_time_monotonic_sec();
  977. /*
  978. igmp_startup_mode_on() will reset QQI:
  979. igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
  980. */
  981. igmp_startup_mode_on(igmp);
  982. igmp_read_on(igmp);
  983. pim_igmp_general_query_on(igmp);
  984. return igmp;
  985. }
  986. struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
  987. struct in_addr ifaddr,
  988. struct interface *ifp)
  989. {
  990. struct pim_interface *pim_ifp;
  991. struct igmp_sock *igmp;
  992. int fd;
  993. pim_ifp = ifp->info;
  994. fd = igmp_sock_open(ifaddr, ifp->ifindex, pim_ifp->options);
  995. if (fd < 0) {
  996. zlog_warn("Could not open IGMP socket for %s on %s",
  997. inet_ntoa(ifaddr), ifp->name);
  998. return 0;
  999. }
  1000. igmp = igmp_sock_new(fd, ifaddr, ifp);
  1001. if (!igmp) {
  1002. zlog_err("%s %s: igmp_sock_new() failure",
  1003. __FILE__, __PRETTY_FUNCTION__);
  1004. close(fd);
  1005. return 0;
  1006. }
  1007. listnode_add(igmp_sock_list, igmp);
  1008. #ifdef IGMP_SOCK_DUMP
  1009. igmp_sock_dump(igmp_sock_array);
  1010. #endif
  1011. return igmp;
  1012. }
  1013. /*
  1014. RFC 3376: 6.5. Switching Router Filter-Modes
  1015. When a router's filter-mode for a group is EXCLUDE and the group
  1016. timer expires, the router filter-mode for the group transitions to
  1017. INCLUDE.
  1018. A router uses source records with running source timers as its state
  1019. for the switch to a filter-mode of INCLUDE. If there are any source
  1020. records with source timers greater than zero (i.e., requested to be
  1021. forwarded), a router switches to filter-mode of INCLUDE using those
  1022. source records. Source records whose timers are zero (from the
  1023. previous EXCLUDE mode) are deleted.
  1024. */
  1025. static int igmp_group_timer(struct thread *t)
  1026. {
  1027. struct igmp_group *group;
  1028. zassert(t);
  1029. group = THREAD_ARG(t);
  1030. zassert(group);
  1031. if (PIM_DEBUG_IGMP_TRACE) {
  1032. char group_str[100];
  1033. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  1034. zlog_debug("%s: Timer for group %s on interface %s",
  1035. __PRETTY_FUNCTION__,
  1036. group_str, group->group_igmp_sock->interface->name);
  1037. }
  1038. zassert(group->group_filtermode_isexcl);
  1039. group->t_group_timer = 0;
  1040. group->group_filtermode_isexcl = 0;
  1041. /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
  1042. igmp_anysource_forward_stop(group);
  1043. igmp_source_delete_expired(group->group_source_list);
  1044. zassert(!group->t_group_timer);
  1045. zassert(!group->group_filtermode_isexcl);
  1046. /*
  1047. RFC 3376: 6.2.2. Definition of Group Timers
  1048. If there are no more source records for the group, delete group
  1049. record.
  1050. */
  1051. if (listcount(group->group_source_list) < 1) {
  1052. igmp_group_delete_empty_include(group);
  1053. }
  1054. return 0;
  1055. }
  1056. static void group_timer_off(struct igmp_group *group)
  1057. {
  1058. if (!group->t_group_timer)
  1059. return;
  1060. if (PIM_DEBUG_IGMP_TRACE) {
  1061. char group_str[100];
  1062. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  1063. zlog_debug("Cancelling TIMER event for group %s on %s",
  1064. group_str, group->group_igmp_sock->interface->name);
  1065. }
  1066. THREAD_OFF(group->t_group_timer);
  1067. zassert(!group->t_group_timer);
  1068. }
  1069. void igmp_group_timer_on(struct igmp_group *group,
  1070. long interval_msec, const char *ifname)
  1071. {
  1072. group_timer_off(group);
  1073. if (PIM_DEBUG_IGMP_EVENTS) {
  1074. char group_str[100];
  1075. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  1076. zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
  1077. interval_msec / 1000,
  1078. interval_msec % 1000,
  1079. group_str, ifname);
  1080. }
  1081. /*
  1082. RFC 3376: 6.2.2. Definition of Group Timers
  1083. The group timer is only used when a group is in EXCLUDE mode and
  1084. it represents the time for the *filter-mode* of the group to
  1085. expire and switch to INCLUDE mode.
  1086. */
  1087. zassert(group->group_filtermode_isexcl);
  1088. THREAD_TIMER_MSEC_ON(master, group->t_group_timer,
  1089. igmp_group_timer,
  1090. group, interval_msec);
  1091. }
  1092. static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
  1093. struct in_addr group_addr)
  1094. {
  1095. struct igmp_group *group;
  1096. struct listnode *node;
  1097. for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, node, group))
  1098. if (group_addr.s_addr == group->group_addr.s_addr)
  1099. return group;
  1100. return 0;
  1101. }
  1102. struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
  1103. struct in_addr group_addr)
  1104. {
  1105. struct igmp_group *group;
  1106. group = find_group_by_addr(igmp, group_addr);
  1107. if (group) {
  1108. return group;
  1109. }
  1110. /*
  1111. Non-existant group is created as INCLUDE {empty}:
  1112. RFC 3376 - 5.1. Action on Change of Interface State
  1113. If no interface state existed for that multicast address before
  1114. the change (i.e., the change consisted of creating a new
  1115. per-interface record), or if no state exists after the change
  1116. (i.e., the change consisted of deleting a per-interface record),
  1117. then the "non-existent" state is considered to have a filter mode
  1118. of INCLUDE and an empty source list.
  1119. */
  1120. group = XMALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
  1121. if (!group) {
  1122. zlog_warn("%s %s: XMALLOC() failure",
  1123. __FILE__, __PRETTY_FUNCTION__);
  1124. return 0; /* error, not found, could not create */
  1125. }
  1126. group->group_source_list = list_new();
  1127. if (!group->group_source_list) {
  1128. zlog_warn("%s %s: list_new() failure",
  1129. __FILE__, __PRETTY_FUNCTION__);
  1130. XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */
  1131. return 0; /* error, not found, could not initialize */
  1132. }
  1133. group->group_source_list->del = (void (*)(void *)) igmp_source_free;
  1134. group->t_group_timer = NULL;
  1135. group->t_group_query_retransmit_timer = NULL;
  1136. group->group_specific_query_retransmit_count = 0;
  1137. group->group_addr = group_addr;
  1138. group->group_igmp_sock = igmp;
  1139. group->last_igmp_v1_report_dsec = -1;
  1140. group->last_igmp_v2_report_dsec = -1;
  1141. group->group_creation = pim_time_monotonic_sec();
  1142. /* initialize new group as INCLUDE {empty} */
  1143. group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
  1144. listnode_add(igmp->igmp_group_list, group);
  1145. if (PIM_DEBUG_IGMP_TRACE) {
  1146. char group_str[100];
  1147. pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  1148. zlog_debug("Creating new IGMP group %s on socket %d interface %s",
  1149. group_str, igmp->fd, igmp->interface->name);
  1150. }
  1151. /*
  1152. RFC 3376: 6.2.2. Definition of Group Timers
  1153. The group timer is only used when a group is in EXCLUDE mode and
  1154. it represents the time for the *filter-mode* of the group to
  1155. expire and switch to INCLUDE mode.
  1156. */
  1157. zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */
  1158. zassert(!group->t_group_timer); /* group timer == 0 */
  1159. /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
  1160. igmp_anysource_forward_stop(group);
  1161. return group;
  1162. }