pim_igmp.c 41 KB

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