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