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