]> git.sommitrealweird.co.uk Git - quagga-debian.git/blob - pimd/pim_igmp.c
New upstream version 1.2.4
[quagga-debian.git] / pimd / pim_igmp.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 */
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
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
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
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
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
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
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
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
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
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
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
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  */
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
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
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
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
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 }