From 2035738d43663dc187621dd72b1e08eed3e51a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Merlin=20Kr=C3=BCmmel?= Date: Sat, 25 Apr 2020 15:08:48 +0200 Subject: [PATCH 01/25] implemented igmpv3 query sending on downstream interfaces --- src/igmp.c | 12 ++++++++---- src/igmpv3.h | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/igmp.c b/src/igmp.c index 38914377..28a53be1 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -269,13 +269,14 @@ void acceptIgmp(int recvlen) { */ static void buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen) { struct ip *ip; - struct igmp *igmp; + struct igmpv3 *igmp; + struct Config *conf = getCommonConfig(); extern int curttl; ip = (struct ip *)send_buf; ip->ip_src.s_addr = src; ip->ip_dst.s_addr = dst; - ip_set_len(ip, IP_HEADER_RAOPT_LEN + IGMP_MINLEN + datalen); + ip_set_len(ip, IP_HEADER_RAOPT_LEN + IGMPV3_MINLEN + datalen); if (IN_MULTICAST(ntohl(dst))) { ip->ip_ttl = curttl; @@ -289,13 +290,16 @@ static void buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t g ((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[2] = 0x00; ((unsigned char*)send_buf+MIN_IP_HEADER_LEN)[3] = 0x00; - igmp = (struct igmp *)(send_buf + IP_HEADER_RAOPT_LEN); + igmp = (struct igmpv3 *)(send_buf + IP_HEADER_RAOPT_LEN); igmp->igmp_type = type; igmp->igmp_code = code; igmp->igmp_group.s_addr = group; igmp->igmp_cksum = 0; igmp->igmp_cksum = inetChksum((unsigned short *)igmp, IP_HEADER_RAOPT_LEN + datalen); + igmp->igmp_misc = conf->robustnessValue & 0x7; + igmp->igmp_qqi = conf->queryInterval; + igmp->igmp_numsrc = 0; } @@ -326,7 +330,7 @@ void sendIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, in #endif sdst.sin_addr.s_addr = dst; if (sendto(MRouterFD, send_buf, - IP_HEADER_RAOPT_LEN + IGMP_MINLEN + datalen, 0, + IP_HEADER_RAOPT_LEN + IGMPV3_MINLEN + datalen, 0, (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { if (errno == ENETDOWN) my_log(LOG_ERR, errno, "Sender VIF was down."); diff --git a/src/igmpv3.h b/src/igmpv3.h index f8658237..5d1d96ae 100644 --- a/src/igmpv3.h +++ b/src/igmpv3.h @@ -21,6 +21,21 @@ * igmpv3.h - Header file for common IGMPv3 includes. */ +/* + * IGMP v3 query format. + */ +struct igmpv3 { + u_int8_t igmp_type; /* version & type of IGMP message */ + u_int8_t igmp_code; /* subtype for routing msgs */ + u_int16_t igmp_cksum; /* IP-style checksum */ + struct in_addr igmp_group; /* group address being reported */ + /* (zero for queries) */ + u_int8_t igmp_misc; /* reserved/suppress/robustness */ + u_int8_t igmp_qqi; /* querier's query interval */ + u_int16_t igmp_numsrc; /* number of sources */ + /*struct in_addr igmp_sources[1];*/ /* source addresses */ +}; + struct igmpv3_grec { u_int8_t grec_type; u_int8_t grec_auxwords; From 9b032b8f7df0184614db676d6fb44f2e2acca7d5 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Mon, 27 Apr 2020 01:39:34 +0200 Subject: [PATCH 02/25] added common igmp.h with structs and defines for IGMPv3 --- src/igmp.c | 42 ++++++------- src/igmp.h | 153 +++++++++++++++++++++++++++++++++++++++++++++++ src/os-freebsd.h | 6 +- 3 files changed, 176 insertions(+), 25 deletions(-) create mode 100644 src/igmp.h diff --git a/src/igmp.c b/src/igmp.c index 28a53be1..2f57e9da 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -37,7 +37,7 @@ */ #include "igmpproxy.h" -#include "igmpv3.h" +#include "igmp.h" // Globals uint32_t allhosts_group; /* All hosts addr in net order */ @@ -107,8 +107,8 @@ void acceptIgmp(int recvlen) { register uint32_t src, dst, group; struct ip *ip; struct igmp *igmp; - struct igmpv3_report *igmpv3; - struct igmpv3_grec *grec; + struct igmp_report *igmpv3; + struct igmp_grouprec *grec; int ipdatalen, iphdrlen, ngrec, nsrcs, i; if (recvlen < (int)sizeof(struct ip)) { @@ -192,7 +192,7 @@ void acceptIgmp(int recvlen) { igmp = (struct igmp *)(recv_buf + iphdrlen); if ((ipdatalen < IGMP_MINLEN) || - (igmp->igmp_type == IGMP_V3_MEMBERSHIP_REPORT && ipdatalen <= IGMPV3_MINLEN)) { + (igmp->igmp_type == IGMP_V3_MEMBERSHIP_REPORT && ipdatalen <= IGMP_V3_REPORT_MINLEN)) { my_log(LOG_WARNING, 0, "received IP data field too short (%u bytes) for IGMP, from %s", ipdatalen, inetFmt(src, s1)); @@ -211,37 +211,37 @@ void acceptIgmp(int recvlen) { return; case IGMP_V3_MEMBERSHIP_REPORT: - igmpv3 = (struct igmpv3_report *)(recv_buf + iphdrlen); - grec = &igmpv3->igmp_grec[0]; - ngrec = ntohs(igmpv3->igmp_ngrec); + igmpv3 = (struct igmpv_report *)(recv_buf + iphdrlen); + grec = &igmpv3->ir_groups[0]; + ngrec = ntohs(igmpv3->ir_numgrps); while (ngrec--) { if ((uint8_t *)igmpv3 + ipdatalen < (uint8_t *)grec + sizeof(*grec)) break; - group = grec->grec_mca.s_addr; - nsrcs = ntohs(grec->grec_nsrcs); - switch (grec->grec_type) { - case IGMPV3_MODE_IS_INCLUDE: - case IGMPV3_CHANGE_TO_INCLUDE: + group = grec->ig_group.s_addr; + nsrcs = ntohs(grec->ig_numsrc); + switch (grec->ig_type) { + case IGMP_MODE_IS_INCLUDE: + case IGMP_CHANGE_TO_INCLUDE_MODE: if (nsrcs == 0) { acceptLeaveMessage(src, group); break; } /* else fall through */ - case IGMPV3_MODE_IS_EXCLUDE: - case IGMPV3_CHANGE_TO_EXCLUDE: - case IGMPV3_ALLOW_NEW_SOURCES: + case IGMP_MODE_IS_EXCLUDE: + case IGMP_CHANGE_TO_EXCLUDE_MODE: + case IGMP_ALLOW_NEW_SOURCES: acceptGroupReport(src, group); break; - case IGMPV3_BLOCK_OLD_SOURCES: + case IGMP_BLOCK_OLD_SOURCES: break; default: my_log(LOG_INFO, 0, "ignoring unknown IGMPv3 group record type %x from %s to %s for %s", - grec->grec_type, inetFmt(src, s1), inetFmt(dst, s2), + grec->ig_type, inetFmt(src, s1), inetFmt(dst, s2), inetFmt(group, s3)); break; } - grec = (struct igmpv3_grec *) - (&grec->grec_src[nsrcs] + grec->grec_auxwords * 4); + grec = (struct igmp_grouprec *) + (&grec->ig_sources[nsrcs] + grec->ig_datalen * 4); } return; @@ -276,7 +276,7 @@ static void buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t g ip = (struct ip *)send_buf; ip->ip_src.s_addr = src; ip->ip_dst.s_addr = dst; - ip_set_len(ip, IP_HEADER_RAOPT_LEN + IGMPV3_MINLEN + datalen); + ip_set_len(ip, IP_HEADER_RAOPT_LEN + IGMP_V3_QUERY_MINLEN + datalen); if (IN_MULTICAST(ntohl(dst))) { ip->ip_ttl = curttl; @@ -330,7 +330,7 @@ void sendIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, in #endif sdst.sin_addr.s_addr = dst; if (sendto(MRouterFD, send_buf, - IP_HEADER_RAOPT_LEN + IGMPV3_MINLEN + datalen, 0, + IP_HEADER_RAOPT_LEN + IGMP_V3_QUERY_MINLEN + datalen, 0, (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { if (errno == ENETDOWN) my_log(LOG_ERR, errno, "Sender VIF was down."); diff --git a/src/igmp.h b/src/igmp.h new file mode 100644 index 00000000..35ffdc67 --- /dev/null +++ b/src/igmp.h @@ -0,0 +1,153 @@ +/*- + * Copyright (c) 1988 Stephen Deering. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Stephen Deering of Stanford University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)igmp.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD: releng/11.2/sys/netinet/igmp.h 331722 2018-03-29 02:50:57Z eadler $ + */ + +#ifndef _NETINET_IGMP_H_ +#define _NETINET_IGMP_H_ + +/* + * Internet Group Management Protocol (IGMP) definitions. + * + * Written by Steve Deering, Stanford, May 1988. + * + * MULTICAST Revision: 3.5.1.2 + */ + +/* Minimum length of any IGMP protocol message. */ +#define IGMP_MINLEN 8 + +/* + * IGMPv1/v2 query and host report format. + */ +struct igmp { + u_char igmp_type; /* version & type of IGMP message */ + u_char igmp_code; /* subtype for routing msgs */ + u_short igmp_cksum; /* IP-style checksum */ + struct in_addr igmp_group; /* group address being reported */ +}; /* (zero for queries) */ + +/* + * IGMP v3 query format. + */ +struct igmpv3 { + u_char igmp_type; /* version & type of IGMP message */ + u_char igmp_code; /* subtype for routing msgs */ + u_short igmp_cksum; /* IP-style checksum */ + struct in_addr igmp_group; /* group address being reported */ + /* (zero for queries) */ + u_char igmp_misc; /* reserved/suppress/robustness */ + u_char igmp_qqi; /* querier's query interval */ + u_short igmp_numsrc; /* number of sources */ + /*struct in_addr igmp_sources[1];*/ /* source addresses */ +}; +#define IGMP_V3_QUERY_MINLEN 12 +#define IGMP_EXP(x) (((x) >> 4) & 0x07) +#define IGMP_MANT(x) ((x) & 0x0f) +#define IGMP_QRESV(x) (((x) >> 4) & 0x0f) +#define IGMP_SFLAG(x) (((x) >> 3) & 0x01) +#define IGMP_QRV(x) ((x) & 0x07) + +struct igmp_grouprec { + u_char ig_type; /* record type */ + u_char ig_datalen; /* length of auxiliary data */ + u_short ig_numsrc; /* number of sources */ + struct in_addr ig_group; /* group address being reported */ + struct in_addr ig_sources[1]; /* source addresses */ +}; +#define IGMP_GRPREC_HDRLEN 8 + +/* + * IGMPv3 host membership report header. + */ +struct igmp_report { + u_char ir_type; /* IGMP_v3_HOST_MEMBERSHIP_REPORT */ + u_char ir_rsv1; /* must be zero */ + u_short ir_cksum; /* checksum */ + u_short ir_rsv2; /* must be zero */ + u_short ir_numgrps; /* number of group records */ + struct igmp_grouprec ir_groups[1]; /* group records */ +}; +#define IGMP_V3_REPORT_MINLEN 8 +#define IGMP_V3_REPORT_MAXRECS 65535 + +/* + * Message types, including version number. + */ +#define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* membership query */ +#define IGMP_v1_HOST_MEMBERSHIP_REPORT 0x12 /* Ver. 1 membership report */ +#define IGMP_DVMRP 0x13 /* DVMRP routing message */ +#define IGMP_PIM 0x14 /* PIMv1 message (historic) */ +#define IGMP_v2_HOST_MEMBERSHIP_REPORT 0x16 /* Ver. 2 membership report */ +#define IGMP_HOST_LEAVE_MESSAGE 0x17 /* Leave-group message */ +#define IGMP_MTRACE_REPLY 0x1e /* mtrace(8) reply */ +#define IGMP_MTRACE_QUERY 0x1f /* mtrace(8) probe */ +#define IGMP_v3_HOST_MEMBERSHIP_REPORT 0x22 /* Ver. 3 membership report */ + +/* + * IGMPv3 report modes. + */ +#define IGMP_DO_NOTHING 0 /* don't send a record */ +#define IGMP_MODE_IS_INCLUDE 1 /* MODE_IN */ +#define IGMP_MODE_IS_EXCLUDE 2 /* MODE_EX */ +#define IGMP_CHANGE_TO_INCLUDE_MODE 3 /* TO_IN */ +#define IGMP_CHANGE_TO_EXCLUDE_MODE 4 /* TO_EX */ +#define IGMP_ALLOW_NEW_SOURCES 5 /* ALLOW_NEW */ +#define IGMP_BLOCK_OLD_SOURCES 6 /* BLOCK_OLD */ + +/* + * IGMPv3 query types. + */ +#define IGMP_V3_GENERAL_QUERY 1 +#define IGMP_V3_GROUP_QUERY 2 +#define IGMP_V3_GROUP_SOURCE_QUERY 3 + +/* + * Maximum report interval for IGMP v1/v2 host membership reports [RFC 1112] + */ +#define IGMP_V1V2_MAX_RI 10 +#define IGMP_MAX_HOST_REPORT_DELAY IGMP_V1V2_MAX_RI + +/* + * IGMP_TIMER_SCALE denotes that the igmp code field specifies + * time in tenths of a second. + */ +#define IGMP_TIMER_SCALE 10 + +/* + * Group address or IGMPv3 messages: 224.0.0.22 + */ +#define INADDR_ALLIGMPV3_GROUP ((in_addr_t) 0xe0000016) + +#endif /* _NETINET_IGMP_H_ */ diff --git a/src/os-freebsd.h b/src/os-freebsd.h index 4edef92d..d01310e4 100644 --- a/src/os-freebsd.h +++ b/src/os-freebsd.h @@ -2,7 +2,7 @@ #include #include #include -#include +/*#include */ #if __FreeBSD_version >= 800069 && defined BURN_BRIDGES \ || __FreeBSD_version >= 800098 @@ -10,10 +10,8 @@ #define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT #define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT #define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE +#define IGMP_V3_MEMBERSHIP_REPORT IGMP_v3_HOST_MEMBERSHIP_REPORT #endif -#define IGMP_V3_MEMBERSHIP_REPORT 0x22 - -#define INADDR_ALLIGMPV3_GROUP ((in_addr_t) 0xe0000016) static inline unsigned short ip_data_len(const struct ip *ip) { From a3240b28c553d11deefc53e1b0aac9999be923a5 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Mon, 27 Apr 2020 01:52:38 +0200 Subject: [PATCH 03/25] fixed includes missing --- src/igmp.c | 3 +-- src/os-freebsd.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/igmp.c b/src/igmp.c index 2f57e9da..c23d8b6a 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -37,7 +37,6 @@ */ #include "igmpproxy.h" -#include "igmp.h" // Globals uint32_t allhosts_group; /* All hosts addr in net order */ @@ -211,7 +210,7 @@ void acceptIgmp(int recvlen) { return; case IGMP_V3_MEMBERSHIP_REPORT: - igmpv3 = (struct igmpv_report *)(recv_buf + iphdrlen); + igmpv3 = (struct igmp_report *)(recv_buf + iphdrlen); grec = &igmpv3->ir_groups[0]; ngrec = ntohs(igmpv3->ir_numgrps); while (ngrec--) { diff --git a/src/os-freebsd.h b/src/os-freebsd.h index d01310e4..69e02266 100644 --- a/src/os-freebsd.h +++ b/src/os-freebsd.h @@ -2,7 +2,7 @@ #include #include #include -/*#include */ +#include "igmp.h" #if __FreeBSD_version >= 800069 && defined BURN_BRIDGES \ || __FreeBSD_version >= 800098 From d96a7df42f5313a05960988c871116ab6109e58c Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Tue, 28 Apr 2020 23:22:30 +0200 Subject: [PATCH 04/25] fixed timer delays fixed issue that delays for timers were ignored and functions are called continously --- src/igmpproxy.c | 2 +- src/request.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/igmpproxy.c b/src/igmpproxy.c index c72a567f..8597616f 100644 --- a/src/igmpproxy.c +++ b/src/igmpproxy.c @@ -340,7 +340,7 @@ void igmpProxyRun(void) { * call gettimeofday. */ if (Rt == 0) { - curtime.tv_sec = lasttime.tv_sec + secs; + curtime.tv_sec = lasttime.tv_sec + timeout->tv_sec; curtime.tv_nsec = lasttime.tv_nsec; Rt = -1; /* don't do this next time through the loop */ } else { diff --git a/src/request.c b/src/request.c index 06d14c44..683d3083 100644 --- a/src/request.c +++ b/src/request.c @@ -193,7 +193,7 @@ void sendGroupSpecificMemberQuery(void *argument) { conf->lastMemberQueryInterval * IGMP_TIMER_SCALE, gvDesc->group, 0); - my_log(LOG_DEBUG, 0, "Sent membership query from %s to %s. Delay: %d", + my_log(LOG_DEBUG, 0, "Sent group specific membership query from %s to %s. Delay: %d", inetFmt(Dp->InAdr.s_addr,s1), inetFmt(gvDesc->group,s2), conf->lastMemberQueryInterval); } @@ -224,7 +224,7 @@ void sendGeneralMembershipQuery(void) { conf->queryResponseInterval * IGMP_TIMER_SCALE, 0, 0); my_log(LOG_DEBUG, 0, - "Sent membership query from %s to %s. Delay: %d", + "Sent general membership query from %s to %s. Delay: %d", inetFmt(Dp->InAdr.s_addr,s1), inetFmt(allhosts_group,s2), conf->queryResponseInterval); From 5f514c125d08eae73c78f910483de8b4d962e952 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Thu, 30 Apr 2020 03:18:03 +0200 Subject: [PATCH 05/25] first try to implement SSM with IGMPv3 the behavior is not fully complaint to RFC3376 eg. IS_EX, TO_EX and BLOCK not handled correctly, especially with more than one downstream host for a given group --- src/ifvc.c | 6 ++--- src/igmp.c | 14 +++++----- src/igmpproxy.h | 12 ++++----- src/igmpv3.h | 63 -------------------------------------------- src/mcgroup.c | 32 ++++++++++++++++------- src/request.c | 10 +++---- src/rttable.c | 69 ++++++++++++++++++++++++++++++++----------------- 7 files changed, 90 insertions(+), 116 deletions(-) delete mode 100644 src/igmpv3.h diff --git a/src/ifvc.c b/src/ifvc.c index 509012f0..06bbde57 100644 --- a/src/ifvc.c +++ b/src/ifvc.c @@ -162,7 +162,7 @@ void rebuildIfVc () { my_log(LOG_NOTICE, 0, "%s [Hidden -> Downstream]", Dp->Name); Dp->state = IF_STATE_DOWNSTREAM; addVIF(Dp); - joinMcGroup(getMcGroupSock(), Dp, allrouters_group); + joinMcGroup(getMcGroupSock(), Dp, allrouters_group, 0 ); } // addVIF when found new IF @@ -170,7 +170,7 @@ void rebuildIfVc () { my_log(LOG_NOTICE, 0, "%s [New]", Dp->Name); Dp->state = config->defaultInterfaceState; addVIF(Dp); - joinMcGroup(getMcGroupSock(), Dp, allrouters_group); + joinMcGroup(getMcGroupSock(), Dp, allrouters_group, 0 ); IfDescEp++; } @@ -187,7 +187,7 @@ void rebuildIfVc () { if (IF_STATE_LOST == Dp->state) { my_log(LOG_NOTICE, 0, "%s [Downstream -> Hidden]", Dp->Name); Dp->state = IF_STATE_HIDDEN; - leaveMcGroup( getMcGroupSock(), Dp, allrouters_group ); + leaveMcGroup( getMcGroupSock(), Dp, allrouters_group, 0 ); delVIF(Dp); } } diff --git a/src/igmp.c b/src/igmp.c index c23d8b6a..83aa51f1 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -206,7 +206,7 @@ void acceptIgmp(int recvlen) { case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: group = igmp->igmp_group.s_addr; - acceptGroupReport(src, group); + acceptGroupReport(src, group, NULL, 0, 0); return; case IGMP_V3_MEMBERSHIP_REPORT: @@ -222,16 +222,18 @@ void acceptIgmp(int recvlen) { case IGMP_MODE_IS_INCLUDE: case IGMP_CHANGE_TO_INCLUDE_MODE: if (nsrcs == 0) { - acceptLeaveMessage(src, group); + acceptLeaveMessage(src, group, NULL, 0); break; - } /* else fall through */ + } case IGMP_MODE_IS_EXCLUDE: case IGMP_CHANGE_TO_EXCLUDE_MODE: case IGMP_ALLOW_NEW_SOURCES: - acceptGroupReport(src, group); + acceptGroupReport(src, group, grec->ig_sources, nsrcs, grec->ig_type); break; case IGMP_BLOCK_OLD_SOURCES: + acceptLeaveMessage(src, group, grec->ig_sources, nsrcs); break; + default: my_log(LOG_INFO, 0, "ignoring unknown IGMPv3 group record type %x from %s to %s for %s", @@ -246,7 +248,7 @@ void acceptIgmp(int recvlen) { case IGMP_V2_LEAVE_GROUP: group = igmp->igmp_group.s_addr; - acceptLeaveMessage(src, group); + acceptLeaveMessage(src, group, NULL, 0); return; case IGMP_MEMBERSHIP_QUERY: @@ -269,7 +271,7 @@ void acceptIgmp(int recvlen) { static void buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen) { struct ip *ip; struct igmpv3 *igmp; - struct Config *conf = getCommonConfig(); + struct Config *conf = getCommonConfig(); extern int curttl; ip = (struct ip *)send_buf; diff --git a/src/igmpproxy.h b/src/igmpproxy.h index 5e25b23a..23d1c614 100644 --- a/src/igmpproxy.h +++ b/src/igmpproxy.h @@ -254,26 +254,26 @@ int openUdpSocket( uint32_t PeerInAdr, uint16_t PeerPort ); /* mcgroup.c */ -int joinMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr ); -int leaveMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr ); +int joinMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, uint32_t originAddr ); +int leaveMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, uint32_t originAddr ); /* rttable.c */ void initRouteTable(void); void clearAllRoutes(void); -int insertRoute(uint32_t group, int ifx, uint32_t src); +int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAddr, uint16_t numOriginAddr, u_char type); int activateRoute(uint32_t group, uint32_t originAddr, int upstrVif); void ageActiveRoutes(void); -void setRouteLastMemberMode(uint32_t group, uint32_t src); +void setRouteLastMemberMode(uint32_t group, uint32_t src, struct in_addr *originAddr, uint16_t numOriginAddr ); int lastMemberGroupAge(uint32_t group); int interfaceInRoute(int32_t group, int Ix); int getMcGroupSock(void); /* request.c */ -void acceptGroupReport(uint32_t src, uint32_t group); -void acceptLeaveMessage(uint32_t src, uint32_t group); +void acceptGroupReport(uint32_t src, uint32_t group, struct in_addr *originAddr, uint16_t numOriginAddr, u_char type); +void acceptLeaveMessage(uint32_t src, uint32_t group, struct in_addr *originAddr, uint16_t numOriginAddr ); void sendGeneralMembershipQuery(void); /* callout.c diff --git a/src/igmpv3.h b/src/igmpv3.h deleted file mode 100644 index 5d1d96ae..00000000 --- a/src/igmpv3.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -** igmpproxy - IGMP proxy based multicast router -** Copyright (C) 2005 Johnny Egeland -** -** This program is free software; you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation; either version 2 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -** -*/ -/** -* igmpv3.h - Header file for common IGMPv3 includes. -*/ - -/* - * IGMP v3 query format. - */ -struct igmpv3 { - u_int8_t igmp_type; /* version & type of IGMP message */ - u_int8_t igmp_code; /* subtype for routing msgs */ - u_int16_t igmp_cksum; /* IP-style checksum */ - struct in_addr igmp_group; /* group address being reported */ - /* (zero for queries) */ - u_int8_t igmp_misc; /* reserved/suppress/robustness */ - u_int8_t igmp_qqi; /* querier's query interval */ - u_int16_t igmp_numsrc; /* number of sources */ - /*struct in_addr igmp_sources[1];*/ /* source addresses */ -}; - -struct igmpv3_grec { - u_int8_t grec_type; - u_int8_t grec_auxwords; - u_int16_t grec_nsrcs; - struct in_addr grec_mca; - struct in_addr grec_src[0]; -}; - -struct igmpv3_report { - u_int8_t igmp_type; - u_int8_t igmp_resv1; - u_int16_t igmp_cksum; - u_int16_t igmp_resv2; - u_int16_t igmp_ngrec; - struct igmpv3_grec igmp_grec[0]; -}; - -#define IGMPV3_MODE_IS_INCLUDE 1 -#define IGMPV3_MODE_IS_EXCLUDE 2 -#define IGMPV3_CHANGE_TO_INCLUDE 3 -#define IGMPV3_CHANGE_TO_EXCLUDE 4 -#define IGMPV3_ALLOW_NEW_SOURCES 5 -#define IGMPV3_BLOCK_OLD_SOURCES 6 - -#define IGMPV3_MINLEN 12 diff --git a/src/mcgroup.c b/src/mcgroup.c index a2355627..06c68466 100644 --- a/src/mcgroup.c +++ b/src/mcgroup.c @@ -42,22 +42,34 @@ /** * Common function for joining or leaving a MCast group. */ -static int joinleave( int Cmd, int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr ) { - struct ip_mreq CtlReq; +static int joinleave( int Cmd, int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, uint32_t originAddr ) { + struct ip_mreq_source CtlReq; const char *CmdSt = Cmd == 'j' ? "join" : "leave"; memset(&CtlReq, 0, sizeof(CtlReq)); CtlReq.imr_multiaddr.s_addr = mcastaddr; CtlReq.imr_interface.s_addr = IfDp->InAdr.s_addr; + CtlReq.imr_sourceaddr.s_addr = originAddr; { - my_log( LOG_NOTICE, 0, "%sMcGroup: %s on %s", CmdSt, - inetFmt( mcastaddr, s1 ), IfDp ? IfDp->Name : "" ); + my_log( LOG_NOTICE, 0, "%sMcGroup: %s on %s from %s", CmdSt, + inetFmt( mcastaddr, s1 ), IfDp ? IfDp->Name : "", originAddr != 0 ? inetFmt( originAddr, s2 ) : "" ); } - if( setsockopt( UdpSock, IPPROTO_IP, + int ret; + + if(originAddr == 0) { + ret = setsockopt( UdpSock, IPPROTO_IP, Cmd == 'j' ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, - (void *)&CtlReq, sizeof( CtlReq ) ) ) + (void *)&CtlReq, sizeof( struct ip_mreq ) ); + } + else { + ret = setsockopt( UdpSock, IPPROTO_IP, + Cmd == 'j' ? IP_ADD_SOURCE_MEMBERSHIP : IP_BLOCK_SOURCE, + (void *)&CtlReq, sizeof( struct ip_mreq_source ) ); + } + + if( ret ) { int mcastGroupExceeded = (Cmd == 'j' && errno == ENOBUFS); my_log( LOG_WARNING, errno, "MRT_%s_MEMBERSHIP failed", Cmd == 'j' ? "ADD" : "DROP" ); @@ -80,8 +92,8 @@ static int joinleave( int Cmd, int UdpSock, struct IfDesc *IfDp, uint32_t mcasta * * @return 0 if the function succeeds, 1 if parameters are wrong or the join fails */ -int joinMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr ) { - return joinleave( 'j', UdpSock, IfDp, mcastaddr ); +int joinMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, uint32_t originAddr ) { + return joinleave( 'j', UdpSock, IfDp, mcastaddr, originAddr ); } /** @@ -89,6 +101,6 @@ int joinMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr ) { * * @return 0 if the function succeeds, 1 if parameters are wrong or the join fails */ -int leaveMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr ) { - return joinleave( 'l', UdpSock, IfDp, mcastaddr ); +int leaveMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, uint32_t originAddr ) { + return joinleave( 'l', UdpSock, IfDp, mcastaddr, originAddr ); } diff --git a/src/request.c b/src/request.c index 06d14c44..6bb884e6 100644 --- a/src/request.c +++ b/src/request.c @@ -54,7 +54,7 @@ typedef struct { * Handles incoming membership reports, and * appends them to the routing table. */ -void acceptGroupReport(uint32_t src, uint32_t group) { +void acceptGroupReport(uint32_t src, uint32_t group, struct in_addr *originAddr, uint16_t numOriginAddr, u_char type) { struct IfDesc *sourceVif; // Sanitycheck the group adress... @@ -86,7 +86,7 @@ void acceptGroupReport(uint32_t src, uint32_t group) { // If we don't have a whitelist we insertRoute and done if(sourceVif->allowedgroups == NULL) { - insertRoute(group, sourceVif->index, src); + insertRoute(group, sourceVif->index, src, originAddr, numOriginAddr, type); return; } // Check if this Request is legit on this interface @@ -95,7 +95,7 @@ void acceptGroupReport(uint32_t src, uint32_t group) { if((group & sn->subnet_mask) == sn->subnet_addr) { // The membership report was OK... Insert it into the route table.. - insertRoute(group, sourceVif->index, src); + insertRoute(group, sourceVif->index, src, originAddr, numOriginAddr, type); return; } my_log(LOG_INFO, 0, "The group address %s may not be requested from this interface. Ignoring.", inetFmt(group, s1)); @@ -109,7 +109,7 @@ void acceptGroupReport(uint32_t src, uint32_t group) { /** * Recieves and handles a group leave message. */ -void acceptLeaveMessage(uint32_t src, uint32_t group) { +void acceptLeaveMessage(uint32_t src, uint32_t group, struct in_addr *originAddr, uint16_t numOriginAddr ) { struct IfDesc *sourceVif; my_log(LOG_DEBUG, 0, @@ -138,7 +138,7 @@ void acceptLeaveMessage(uint32_t src, uint32_t group) { gvDesc = (GroupVifDesc*) malloc(sizeof(GroupVifDesc)); // Tell the route table that we are checking for remaining members... - setRouteLastMemberMode(group, src); + setRouteLastMemberMode(group, src, originAddr, numOriginAddr); // Call the group spesific membership querier... gvDesc->group = group; diff --git a/src/rttable.c b/src/rttable.c index 3d33c77e..bf89d205 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -144,11 +144,11 @@ void initRouteTable(void) { inetFmt(allrouters_group,s1),inetFmt(Dp->InAdr.s_addr,s2)); //k_join(allrouters_group, Dp->InAdr.s_addr); - joinMcGroup( getMcGroupSock(), Dp, allrouters_group ); + joinMcGroup( getMcGroupSock(), Dp, allrouters_group, 0 ); my_log(LOG_DEBUG, 0, "Joining all igmpv3 multicast routers group %s on vif %s", inetFmt(alligmp3_group,s1),inetFmt(Dp->InAdr.s_addr,s2)); - joinMcGroup( getMcGroupSock(), Dp, alligmp3_group ); + joinMcGroup( getMcGroupSock(), Dp, alligmp3_group, 0 ); } } } @@ -157,9 +157,9 @@ void initRouteTable(void) { * Internal function to send join or leave requests for * a specified route upstream... */ -static void sendJoinLeaveUpstream(struct RouteTable* route, int join) { +static void sendJoinLeaveUpstream(struct RouteTable* route, int join, struct in_addr *originAddr, uint16_t numOriginAddr ) { struct IfDesc* upstrIf; - int i; + int i, cmd; for(i=0; iallowedgroups; sn != NULL; sn = sn->next) - if((group & sn->subnet_mask) == sn->subnet_addr) + for(sn = upstrIf->allowedgroups; sn != NULL; sn = sn->next) { + if((group & sn->subnet_mask) == sn->subnet_addr) { // Forward is OK... break; + } + } if (sn == NULL) { my_log(LOG_INFO, 0, "The group address %s may not be forwarded upstream. Ignoring.", inetFmt(group, s1)); - return; + continue; } } @@ -196,13 +199,13 @@ static void sendJoinLeaveUpstream(struct RouteTable* route, int join) { inetFmt(route->group, s1), inetFmt(upstrIf->InAdr.s_addr, s2)); - //k_join(route->group, upstrIf->InAdr.s_addr); - joinMcGroup( getMcGroupSock(), upstrIf, route->group ); - + cmd = 'j'; route->upstrState = ROUTESTATE_JOINED; } else { my_log(LOG_DEBUG, 0, "No downstream listeners for group %s. No join sent.", inetFmt(route->group, s1)); + + continue; } } else { // Only leave if group is not left already... @@ -211,10 +214,21 @@ static void sendJoinLeaveUpstream(struct RouteTable* route, int join) { inetFmt(route->group, s1), inetFmt(upstrIf->InAdr.s_addr, s2)); - //k_leave(route->group, upstrIf->InAdr.s_addr); - leaveMcGroup( getMcGroupSock(), upstrIf, route->group ); - + cmd = 'l'; route->upstrState = ROUTESTATE_NOTJOINED; + } else { + continue; + } + } + + if(numOriginAddr == 0) { + //k_leave(route->group, upstrIf->InAdr.s_addr); + joinleave( cmd, getMcGroupSock(), upstrIf, route->group, 0 ); + } else { + int u; + for(u = 0; u < numOriginAddr; u++) { + //k_leave(route->group, upstrIf->InAdr.s_addr); + joinleave( cmd, getMcGroupSock(), upstrIf, route->group, originAddr[u] ); } } } @@ -246,7 +260,7 @@ void clearAllRoutes(void) { } // Send Leave message upstream. - sendJoinLeaveUpstream(croute, 0); + sendJoinLeaveUpstream(croute, 0, NULL, 0); // Clear memory, and set pointer to next route... free(croute); @@ -278,7 +292,7 @@ static struct RouteTable *findRoute(uint32_t group) { * If the route already exists, the existing route * is updated... */ -int insertRoute(uint32_t group, int ifx, uint32_t src) { +int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAddr, uint16_t numOriginAddr, u_char type) { struct Config *conf = getCommonConfig(); struct RouteTable* croute; @@ -414,10 +428,19 @@ int insertRoute(uint32_t group, int ifx, uint32_t src) { } } - // Send join message upstream, if the route has no joined flag... - if(croute->upstrState != ROUTESTATE_JOINED) { - // Send Join request upstream - sendJoinLeaveUpstream(croute, 1); + switch(type) { + case IGMP_MODE_IS_INCLUDE: + case IGMP_MODE_IS_EXCLUDE: + // Send join message upstream, if the route has no joined flag... + if(croute->upstrState == ROUTESTATE_JOINED) { + break; + } + case IGMP_CHANGE_TO_EXCLUDE_MODE: + case IGMP_CHANGE_TO_INCLUDE_MODE: + case IGMP_ALLOW_NEW_SOURCES: + // Send Join request upstream + sendJoinLeaveUpstream(croute, 1, originAddr, numOriginAddr); + break; } logRouteTable("Insert Route"); @@ -442,7 +465,7 @@ int activateRoute(uint32_t group, uint32_t originAddr, int upstrVif) { inetFmt(group, s1),inetFmt(originAddr, s2)); // Insert route, but no interfaces have yet requested it downstream. - insertRoute(group, -1, 0); + insertRoute(group, -1, 0, NULL, 0, 0); // Retrieve the route from table... croute = findRoute(group); @@ -539,7 +562,7 @@ int numberOfInterfaces(struct RouteTable *croute) { * Should be called when a leave message is received, to * mark a route for the last member probe state. */ -void setRouteLastMemberMode(uint32_t group, uint32_t src) { +void setRouteLastMemberMode(uint32_t group, uint32_t src, struct in_addr *originAddr, uint16_t numOriginAddr ) { struct Config *conf = getCommonConfig(); struct RouteTable *croute; int routeStateCheck = 1; @@ -564,7 +587,7 @@ void setRouteLastMemberMode(uint32_t group, uint32_t src) { // It is possible that there are still some interfaces active but no downstream host in hash table due to hash collision if (routeStateCheck && numberOfInterfaces(croute) <= 1) { my_log(LOG_DEBUG, 0, "quickleave is enabled and this was the last downstream host, leaving group %s now", inetFmt(croute->group, s1)); - sendJoinLeaveUpstream(croute, 0); + sendJoinLeaveUpstream(croute, 0, originAddr, numOriginAddr); } else { my_log(LOG_DEBUG, 0, "quickleave is enabled but there are still some downstream hosts left, not leaving group %s", inetFmt(croute->group, s1)); } @@ -628,7 +651,7 @@ static int removeRoute(struct RouteTable* croute) { if(croute->upstrState == ROUTESTATE_JOINED || (croute->upstrState == ROUTESTATE_CHECK_LAST_MEMBER && !conf->fastUpstreamLeave)) { - sendJoinLeaveUpstream(croute, 0); + sendJoinLeaveUpstream(croute, 0, NULL, 0); } // Update pointers... From 7ebc53f881c407ca6c66cc65cd9f14db3e23b6e6 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Fri, 1 May 2020 00:13:38 +0200 Subject: [PATCH 06/25] fixed linker error --- src/igmpproxy.h | 1 + src/mcgroup.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/igmpproxy.h b/src/igmpproxy.h index 23d1c614..b26832b4 100644 --- a/src/igmpproxy.h +++ b/src/igmpproxy.h @@ -254,6 +254,7 @@ int openUdpSocket( uint32_t PeerInAdr, uint16_t PeerPort ); /* mcgroup.c */ +int joinleave( int Cmd, int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, uint32_t originAddr ); int joinMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, uint32_t originAddr ); int leaveMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, uint32_t originAddr ); diff --git a/src/mcgroup.c b/src/mcgroup.c index 06c68466..85c040b4 100644 --- a/src/mcgroup.c +++ b/src/mcgroup.c @@ -42,7 +42,7 @@ /** * Common function for joining or leaving a MCast group. */ -static int joinleave( int Cmd, int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, uint32_t originAddr ) { +int joinleave( int Cmd, int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, uint32_t originAddr ) { struct ip_mreq_source CtlReq; const char *CmdSt = Cmd == 'j' ? "join" : "leave"; From 55939aebf6c8dc439d4591cbdc574a2725217b79 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Fri, 1 May 2020 00:17:57 +0200 Subject: [PATCH 07/25] fixed compiler error --- src/rttable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rttable.c b/src/rttable.c index bf89d205..716c0077 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -228,7 +228,7 @@ static void sendJoinLeaveUpstream(struct RouteTable* route, int join, struct in_ int u; for(u = 0; u < numOriginAddr; u++) { //k_leave(route->group, upstrIf->InAdr.s_addr); - joinleave( cmd, getMcGroupSock(), upstrIf, route->group, originAddr[u] ); + joinleave( cmd, getMcGroupSock(), upstrIf, route->group, originAddr[u].s_addr ); } } } From 02df1888a6ccfd13c8624b7f67d8ca7eba79e489 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Fri, 1 May 2020 03:16:30 +0200 Subject: [PATCH 08/25] fixed logging error in fkt interfaceInRoute --- src/rttable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rttable.c b/src/rttable.c index 716c0077..b6800dbf 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -850,7 +850,7 @@ int interfaceInRoute(int32_t group, int Ix) { struct RouteTable* croute; croute = findRoute(group); if (croute != NULL) { - my_log(LOG_DEBUG, 0, "Interface id %d is in group $d", Ix, group); + my_log(LOG_DEBUG, 0, "Interface id %d is in group %d", Ix, group); return BIT_TST(croute->vifBits, Ix); } else { return 0; From 02427209b8e98d22f2ca53a403dd807a85d8d183 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Fri, 1 May 2020 04:58:45 +0200 Subject: [PATCH 09/25] fix exclude with sources handled like include, now exclude sources are ignored, --- src/igmp.c | 6 ++++-- src/mcgroup.c | 23 ++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/igmp.c b/src/igmp.c index 83aa51f1..f88d4bbd 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -225,11 +225,13 @@ void acceptIgmp(int recvlen) { acceptLeaveMessage(src, group, NULL, 0); break; } - case IGMP_MODE_IS_EXCLUDE: - case IGMP_CHANGE_TO_EXCLUDE_MODE: case IGMP_ALLOW_NEW_SOURCES: acceptGroupReport(src, group, grec->ig_sources, nsrcs, grec->ig_type); break; + case IGMP_MODE_IS_EXCLUDE: + case IGMP_CHANGE_TO_EXCLUDE_MODE: + acceptGroupReport(src, group, NULL, 0, grec->ig_type); + break; case IGMP_BLOCK_OLD_SOURCES: acceptLeaveMessage(src, group, grec->ig_sources, nsrcs); break; diff --git a/src/mcgroup.c b/src/mcgroup.c index 85c040b4..eafc6008 100644 --- a/src/mcgroup.c +++ b/src/mcgroup.c @@ -43,14 +43,8 @@ * Common function for joining or leaving a MCast group. */ int joinleave( int Cmd, int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, uint32_t originAddr ) { - struct ip_mreq_source CtlReq; const char *CmdSt = Cmd == 'j' ? "join" : "leave"; - memset(&CtlReq, 0, sizeof(CtlReq)); - CtlReq.imr_multiaddr.s_addr = mcastaddr; - CtlReq.imr_interface.s_addr = IfDp->InAdr.s_addr; - CtlReq.imr_sourceaddr.s_addr = originAddr; - { my_log( LOG_NOTICE, 0, "%sMcGroup: %s on %s from %s", CmdSt, inetFmt( mcastaddr, s1 ), IfDp ? IfDp->Name : "", originAddr != 0 ? inetFmt( originAddr, s2 ) : "" ); @@ -59,14 +53,25 @@ int joinleave( int Cmd, int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, ui int ret; if(originAddr == 0) { + struct ip_mreq CtlReq; + memset(&CtlReq, 0, sizeof(CtlReq)); + CtlReq.imr_multiaddr.s_addr = mcastaddr; + CtlReq.imr_interface.s_addr = IfDp->InAdr.s_addr; + ret = setsockopt( UdpSock, IPPROTO_IP, Cmd == 'j' ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, - (void *)&CtlReq, sizeof( struct ip_mreq ) ); + (void *)&CtlReq, sizeof( CtlReq ) ); } else { + struct ip_mreq_source CtlReq; + memset(&CtlReq, 0, sizeof(CtlReq)); + CtlReq.imr_multiaddr.s_addr = mcastaddr; + CtlReq.imr_interface.s_addr = IfDp->InAdr.s_addr; + CtlReq.imr_sourceaddr.s_addr = originAddr; + ret = setsockopt( UdpSock, IPPROTO_IP, - Cmd == 'j' ? IP_ADD_SOURCE_MEMBERSHIP : IP_BLOCK_SOURCE, - (void *)&CtlReq, sizeof( struct ip_mreq_source ) ); + Cmd == 'j' ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP, + (void *)&CtlReq, sizeof( CtlReq ) ); } if( ret ) From e6f03839caf8b5b200bf74967f69e925c6445ae4 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Fri, 1 May 2020 22:46:28 +0200 Subject: [PATCH 10/25] fixed IGMPv2 group join --- src/igmpproxy.h | 8 ++++---- src/request.c | 8 ++++---- src/rttable.c | 29 +++++++++++++++++++---------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/igmpproxy.h b/src/igmpproxy.h index b26832b4..6688bf60 100644 --- a/src/igmpproxy.h +++ b/src/igmpproxy.h @@ -263,18 +263,18 @@ int leaveMcGroup( int UdpSock, struct IfDesc *IfDp, uint32_t mcastaddr, uint32_t */ void initRouteTable(void); void clearAllRoutes(void); -int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAddr, uint16_t numOriginAddr, u_char type); +int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAddr, u_short numOriginAddr, u_char type); int activateRoute(uint32_t group, uint32_t originAddr, int upstrVif); void ageActiveRoutes(void); -void setRouteLastMemberMode(uint32_t group, uint32_t src, struct in_addr *originAddr, uint16_t numOriginAddr ); +void setRouteLastMemberMode(uint32_t group, uint32_t src, struct in_addr *originAddr, u_short numOriginAddr ); int lastMemberGroupAge(uint32_t group); int interfaceInRoute(int32_t group, int Ix); int getMcGroupSock(void); /* request.c */ -void acceptGroupReport(uint32_t src, uint32_t group, struct in_addr *originAddr, uint16_t numOriginAddr, u_char type); -void acceptLeaveMessage(uint32_t src, uint32_t group, struct in_addr *originAddr, uint16_t numOriginAddr ); +void acceptGroupReport(uint32_t src, uint32_t group, struct in_addr *originAddr, u_short numOriginAddr, u_char type); +void acceptLeaveMessage(uint32_t src, uint32_t group, struct in_addr *originAddr, u_short numOriginAddr ); void sendGeneralMembershipQuery(void); /* callout.c diff --git a/src/request.c b/src/request.c index 6bb884e6..17fd15f3 100644 --- a/src/request.c +++ b/src/request.c @@ -54,7 +54,7 @@ typedef struct { * Handles incoming membership reports, and * appends them to the routing table. */ -void acceptGroupReport(uint32_t src, uint32_t group, struct in_addr *originAddr, uint16_t numOriginAddr, u_char type) { +void acceptGroupReport(uint32_t src, uint32_t group, struct in_addr *originAddr, u_short numOriginAddr, u_char mode) { struct IfDesc *sourceVif; // Sanitycheck the group adress... @@ -86,7 +86,7 @@ void acceptGroupReport(uint32_t src, uint32_t group, struct in_addr *originAddr, // If we don't have a whitelist we insertRoute and done if(sourceVif->allowedgroups == NULL) { - insertRoute(group, sourceVif->index, src, originAddr, numOriginAddr, type); + insertRoute(group, sourceVif->index, src, originAddr, numOriginAddr, mode); return; } // Check if this Request is legit on this interface @@ -95,7 +95,7 @@ void acceptGroupReport(uint32_t src, uint32_t group, struct in_addr *originAddr, if((group & sn->subnet_mask) == sn->subnet_addr) { // The membership report was OK... Insert it into the route table.. - insertRoute(group, sourceVif->index, src, originAddr, numOriginAddr, type); + insertRoute(group, sourceVif->index, src, originAddr, numOriginAddr, mode); return; } my_log(LOG_INFO, 0, "The group address %s may not be requested from this interface. Ignoring.", inetFmt(group, s1)); @@ -109,7 +109,7 @@ void acceptGroupReport(uint32_t src, uint32_t group, struct in_addr *originAddr, /** * Recieves and handles a group leave message. */ -void acceptLeaveMessage(uint32_t src, uint32_t group, struct in_addr *originAddr, uint16_t numOriginAddr ) { +void acceptLeaveMessage(uint32_t src, uint32_t group, struct in_addr *originAddr, u_short numOriginAddr ) { struct IfDesc *sourceVif; my_log(LOG_DEBUG, 0, diff --git a/src/rttable.c b/src/rttable.c index b6800dbf..256abc00 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -51,6 +51,7 @@ struct RouteTable { uint32_t group; // The group to route uint32_t originAddrs[MAX_ORIGINS]; // The origin adresses (only set on activated routes) uint32_t vifBits; // Bits representing recieving VIFs. + u_char mode; // IGMPv3 filter mode // Keeps the upstream membership state... short upstrState; // Upstream membership state. @@ -157,7 +158,7 @@ void initRouteTable(void) { * Internal function to send join or leave requests for * a specified route upstream... */ -static void sendJoinLeaveUpstream(struct RouteTable* route, int join, struct in_addr *originAddr, uint16_t numOriginAddr ) { +static void sendJoinLeaveUpstream(struct RouteTable* route, int join, struct in_addr *originAddr, u_short numOriginAddr ) { struct IfDesc* upstrIf; int i, cmd; @@ -292,7 +293,7 @@ static struct RouteTable *findRoute(uint32_t group) { * If the route already exists, the existing route * is updated... */ -int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAddr, uint16_t numOriginAddr, u_char type) { +int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAddr, u_short numOriginAddr, u_char mode) { struct Config *conf = getCommonConfig(); struct RouteTable* croute; @@ -328,6 +329,7 @@ int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAdd newroute->nextroute = NULL; newroute->prevroute = NULL; newroute->upstrVif = -1; + newroute->mode = 0; if(conf->fastUpstreamLeave) { // Init downstream hosts bit hash table @@ -428,21 +430,28 @@ int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAdd } } - switch(type) { - case IGMP_MODE_IS_INCLUDE: - case IGMP_MODE_IS_EXCLUDE: - // Send join message upstream, if the route has no joined flag... - if(croute->upstrState == ROUTESTATE_JOINED) { - break; - } + switch(mode) { case IGMP_CHANGE_TO_EXCLUDE_MODE: case IGMP_CHANGE_TO_INCLUDE_MODE: + // + if(croute->mode == mode) { + break; + } case IGMP_ALLOW_NEW_SOURCES: // Send Join request upstream sendJoinLeaveUpstream(croute, 1, originAddr, numOriginAddr); break; + case IGMP_MODE_IS_INCLUDE: + case IGMP_MODE_IS_EXCLUDE: + default: + // Send join message upstream, if the route has no joined flag... + if(croute->upstrState != ROUTESTATE_JOINED) { + sendJoinLeaveUpstream(croute, 1, originAddr, numOriginAddr); + } } + croute->mode = mode; + logRouteTable("Insert Route"); return 1; @@ -562,7 +571,7 @@ int numberOfInterfaces(struct RouteTable *croute) { * Should be called when a leave message is received, to * mark a route for the last member probe state. */ -void setRouteLastMemberMode(uint32_t group, uint32_t src, struct in_addr *originAddr, uint16_t numOriginAddr ) { +void setRouteLastMemberMode(uint32_t group, uint32_t src, struct in_addr *originAddr, u_short numOriginAddr ) { struct Config *conf = getCommonConfig(); struct RouteTable *croute; int routeStateCheck = 1; From 1d5b19319fe4f2d015fb4b1e05e7d6ae38b53505 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Sun, 3 May 2020 00:49:03 +0200 Subject: [PATCH 11/25] reworked group join for IGMPv3 SSM, blocking addresses in exclude mode not supported --- src/igmp.h | 6 ++++ src/request.c | 2 +- src/rttable.c | 94 ++++++++++++++++++++++++++++++++++----------------- 3 files changed, 70 insertions(+), 32 deletions(-) diff --git a/src/igmp.h b/src/igmp.h index 35ffdc67..39bd9e4e 100644 --- a/src/igmp.h +++ b/src/igmp.h @@ -126,6 +126,12 @@ struct igmp_report { #define IGMP_ALLOW_NEW_SOURCES 5 /* ALLOW_NEW */ #define IGMP_BLOCK_OLD_SOURCES 6 /* BLOCK_OLD */ +/* + * IGMPv3 filter modes + */ +#define IGMP_FILTER_MODE_EXCLUDE 1 +#define IGMP_FILTER_MODE_INCLUDE 2 + /* * IGMPv3 query types. */ diff --git a/src/request.c b/src/request.c index 17fd15f3..87014b8c 100644 --- a/src/request.c +++ b/src/request.c @@ -98,7 +98,7 @@ void acceptGroupReport(uint32_t src, uint32_t group, struct in_addr *originAddr, insertRoute(group, sourceVif->index, src, originAddr, numOriginAddr, mode); return; } - my_log(LOG_INFO, 0, "The group address %s may not be requested from this interface. Ignoring.", inetFmt(group, s1)); + my_log(LOG_INFO, 0, "The group address %s may not be requested from this interface. Ignoring.", inetFmt(group, s1)); } else { // Log the state of the interface the report was received on. my_log(LOG_INFO, 0, "Mebership report was received on %s. Ignoring.", diff --git a/src/rttable.c b/src/rttable.c index 256abc00..dba5353f 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -52,6 +52,7 @@ struct RouteTable { uint32_t originAddrs[MAX_ORIGINS]; // The origin adresses (only set on activated routes) uint32_t vifBits; // Bits representing recieving VIFs. u_char mode; // IGMPv3 filter mode + uint32_t reqOriginAddr[MAX_ORIGINS]; // IGMPv3 requested source // Keeps the upstream membership state... short upstrState; // Upstream membership state. @@ -160,7 +161,7 @@ void initRouteTable(void) { */ static void sendJoinLeaveUpstream(struct RouteTable* route, int join, struct in_addr *originAddr, u_short numOriginAddr ) { struct IfDesc* upstrIf; - int i, cmd; + int i; for(i=0; ivifBits > 0) { my_log(LOG_DEBUG, 0, "Joining group %s upstream on IF address %s", - inetFmt(route->group, s1), - inetFmt(upstrIf->InAdr.s_addr, s2)); + inetFmt(route->group, s1), + inetFmt(upstrIf->InAdr.s_addr, s2)); + + int u, v; + for(u = 0; u < numOriginAddr; u++) { + for(v = 0; v < MAX_ORIGINS; v++) { + if(route->reqOriginAddr[v] == originAddr[u].s_addr) { + break; + } else if(route->reqOriginAddr[v] == 0) { + route->reqOriginAddr[v] = originAddr[u].s_addr; + joinleave( 'j', getMcGroupSock(), upstrIf, route->group, originAddr[u].s_addr ); + break; + } + } + } + + if(numOriginAddr == 0) { + //k_leave(route->group, upstrIf->InAdr.s_addr); + joinleave( 'j', getMcGroupSock(), upstrIf, route->group, 0 ); + } - cmd = 'j'; route->upstrState = ROUTESTATE_JOINED; } else { my_log(LOG_DEBUG, 0, "No downstream listeners for group %s. No join sent.", - inetFmt(route->group, s1)); + inetFmt(route->group, s1)); continue; } @@ -212,26 +230,37 @@ static void sendJoinLeaveUpstream(struct RouteTable* route, int join, struct in_ // Only leave if group is not left already... if(route->upstrState != ROUTESTATE_NOTJOINED) { my_log(LOG_DEBUG, 0, "Leaving group %s upstream on IF address %s", - inetFmt(route->group, s1), - inetFmt(upstrIf->InAdr.s_addr, s2)); + inetFmt(route->group, s1), + inetFmt(upstrIf->InAdr.s_addr, s2)); + + int u, v; + for(u = 0; u < numOriginAddr; u++) { + for(v = 0; v < MAX_ORIGINS; v++) { + if(route->reqOriginAddr[v] == 0) { + break; + } else if(route->reqOriginAddr[v] == originAddr[u].s_addr) { + int w = v; + while(w + 1 < MAX_ORIGINS && route->reqOriginAddr[w] != 0) { + route->reqOriginAddr[w] = route->reqOriginAddr[w + 1]; + w++; + } + + route->reqOriginAddr[w] = 0; + joinleave( 'l', getMcGroupSock(), upstrIf, route->group, originAddr[u].s_addr ); + break; + } + } + } - cmd = 'l'; - route->upstrState = ROUTESTATE_NOTJOINED; + if(numOriginAddr == 0 || route->reqOriginAddr[0] == 0) { + //k_leave(route->group, upstrIf->InAdr.s_addr); + joinleave( 'l', getMcGroupSock(), upstrIf, route->group, 0 ); + route->upstrState = ROUTESTATE_NOTJOINED; + } } else { continue; } } - - if(numOriginAddr == 0) { - //k_leave(route->group, upstrIf->InAdr.s_addr); - joinleave( cmd, getMcGroupSock(), upstrIf, route->group, 0 ); - } else { - int u; - for(u = 0; u < numOriginAddr; u++) { - //k_leave(route->group, upstrIf->InAdr.s_addr); - joinleave( cmd, getMcGroupSock(), upstrIf, route->group, originAddr[u].s_addr ); - } - } } else { @@ -431,26 +460,28 @@ int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAdd } switch(mode) { - case IGMP_CHANGE_TO_EXCLUDE_MODE: case IGMP_CHANGE_TO_INCLUDE_MODE: - // - if(croute->mode == mode) { + case IGMP_MODE_IS_INCLUDE: + // don't allow include mode when other downstream hosts have requested exclude mode + if(croute->mode == IGMP_FILTER_MODE_EXCLUDE) { break; } + + croute->mode = IGMP_FILTER_MODE_INCLUDE; + break; case IGMP_ALLOW_NEW_SOURCES: - // Send Join request upstream - sendJoinLeaveUpstream(croute, 1, originAddr, numOriginAddr); break; - case IGMP_MODE_IS_INCLUDE: + case IGMP_CHANGE_TO_EXCLUDE_MODE: case IGMP_MODE_IS_EXCLUDE: default: - // Send join message upstream, if the route has no joined flag... - if(croute->upstrState != ROUTESTATE_JOINED) { - sendJoinLeaveUpstream(croute, 1, originAddr, numOriginAddr); - } + croute->mode = IGMP_FILTER_MODE_EXCLUDE; } - croute->mode = mode; + // Send join message upstream, if the route has no joined flag... + if((mode != IGMP_MODE_IS_INCLUDE && mode != IGMP_MODE_IS_EXCLUDE) || croute->upstrState != ROUTESTATE_JOINED) { + // Send Join request upstream + sendJoinLeaveUpstream(croute, 1, originAddr, numOriginAddr); + } logRouteTable("Insert Route"); @@ -595,6 +626,7 @@ void setRouteLastMemberMode(uint32_t group, uint32_t src, struct in_addr *origin // Send a leave message right away but only when the route is not active anymore on any downstream host // It is possible that there are still some interfaces active but no downstream host in hash table due to hash collision if (routeStateCheck && numberOfInterfaces(croute) <= 1) { + croute->mode = 0; my_log(LOG_DEBUG, 0, "quickleave is enabled and this was the last downstream host, leaving group %s now", inetFmt(croute->group, s1)); sendJoinLeaveUpstream(croute, 0, originAddr, numOriginAddr); } else { From 29e359f7e600565f8c00ec8bcb3f32ae06bef01f Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Sun, 3 May 2020 19:08:38 +0200 Subject: [PATCH 12/25] fixed filtering of SSDP messages for IGMPv3 --- src/igmp.c | 7 ------- src/request.c | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/igmp.c b/src/igmp.c index f88d4bbd..796c4c9f 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -120,13 +120,6 @@ void acceptIgmp(int recvlen) { src = ip->ip_src.s_addr; dst = ip->ip_dst.s_addr; - /* filter local multicast 239.255.255.250 */ - if (dst == htonl(0xEFFFFFFA)) - { - my_log(LOG_NOTICE, 0, "The IGMP message was local multicast. Ignoring."); - return; - } - /* * this is most likely a message from the kernel indicating that * a new src grp pair message has arrived and so, it would be diff --git a/src/request.c b/src/request.c index 87014b8c..d972c15f 100644 --- a/src/request.c +++ b/src/request.c @@ -77,6 +77,13 @@ void acceptGroupReport(uint32_t src, uint32_t group, struct in_addr *originAddr, return; } + /* filter local multicast 239.255.255.250 */ + if (group == htonl(0xEFFFFFFA)) + { + my_log(LOG_NOTICE, 0, "The IGMP message was local multicast. Ignoring."); + return; + } + // We have a IF so check that it's an downstream IF. if(sourceVif->state == IF_STATE_DOWNSTREAM) { From 5da387682e71c8fedc8996376d679d3b6eeeffe2 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Sun, 3 May 2020 20:29:39 +0200 Subject: [PATCH 13/25] fixed length of structures with flexible array members --- src/igmp.h | 6 +++--- src/request.c | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/igmp.h b/src/igmp.h index 39bd9e4e..277f21f9 100644 --- a/src/igmp.h +++ b/src/igmp.h @@ -70,7 +70,7 @@ struct igmpv3 { u_char igmp_misc; /* reserved/suppress/robustness */ u_char igmp_qqi; /* querier's query interval */ u_short igmp_numsrc; /* number of sources */ - /*struct in_addr igmp_sources[1];*/ /* source addresses */ + /*struct in_addr igmp_sources[];*/ /* source addresses */ }; #define IGMP_V3_QUERY_MINLEN 12 #define IGMP_EXP(x) (((x) >> 4) & 0x07) @@ -84,7 +84,7 @@ struct igmp_grouprec { u_char ig_datalen; /* length of auxiliary data */ u_short ig_numsrc; /* number of sources */ struct in_addr ig_group; /* group address being reported */ - struct in_addr ig_sources[1]; /* source addresses */ + struct in_addr ig_sources[]; /* source addresses */ }; #define IGMP_GRPREC_HDRLEN 8 @@ -97,7 +97,7 @@ struct igmp_report { u_short ir_cksum; /* checksum */ u_short ir_rsv2; /* must be zero */ u_short ir_numgrps; /* number of group records */ - struct igmp_grouprec ir_groups[1]; /* group records */ + struct igmp_grouprec ir_groups[]; /* group records */ }; #define IGMP_V3_REPORT_MINLEN 8 #define IGMP_V3_REPORT_MAXRECS 65535 diff --git a/src/request.c b/src/request.c index d972c15f..82bb265c 100644 --- a/src/request.c +++ b/src/request.c @@ -78,8 +78,7 @@ void acceptGroupReport(uint32_t src, uint32_t group, struct in_addr *originAddr, } /* filter local multicast 239.255.255.250 */ - if (group == htonl(0xEFFFFFFA)) - { + if (group == htonl(0xEFFFFFFA)) { my_log(LOG_NOTICE, 0, "The IGMP message was local multicast. Ignoring."); return; } From 2ee6d750cd11d8f1d91af15c5d993642dec60e8d Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Sun, 3 May 2020 21:23:32 +0200 Subject: [PATCH 14/25] fixed syscall error when to_ex is send twice --- src/rttable.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/rttable.c b/src/rttable.c index dba5353f..03037a05 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -326,6 +326,7 @@ int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAdd struct Config *conf = getCommonConfig(); struct RouteTable* croute; + int sendJoin = 0; // Sanitycheck the group adress... if( ! IN_MULTICAST( ntohl(group) )) { @@ -460,7 +461,11 @@ int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAdd } switch(mode) { + case IGMP_ALLOW_NEW_SOURCES: case IGMP_CHANGE_TO_INCLUDE_MODE: + if(croute->mode == IGMP_FILTER_MODE_INCLUDE) { + sendJoin = 1; + } case IGMP_MODE_IS_INCLUDE: // don't allow include mode when other downstream hosts have requested exclude mode if(croute->mode == IGMP_FILTER_MODE_EXCLUDE) { @@ -469,16 +474,17 @@ int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAdd croute->mode = IGMP_FILTER_MODE_INCLUDE; break; - case IGMP_ALLOW_NEW_SOURCES: - break; case IGMP_CHANGE_TO_EXCLUDE_MODE: + if(croute->mode == IGMP_FILTER_MODE_INCLUDE) { + sendJoin = 1; + } case IGMP_MODE_IS_EXCLUDE: default: croute->mode = IGMP_FILTER_MODE_EXCLUDE; } // Send join message upstream, if the route has no joined flag... - if((mode != IGMP_MODE_IS_INCLUDE && mode != IGMP_MODE_IS_EXCLUDE) || croute->upstrState != ROUTESTATE_JOINED) { + if(sendJoin || croute->upstrState != ROUTESTATE_JOINED) { // Send Join request upstream sendJoinLeaveUpstream(croute, 1, originAddr, numOriginAddr); } From ccc75de0f704d3c1017387767d61ab23ea242e2b Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Sun, 3 May 2020 23:01:14 +0200 Subject: [PATCH 15/25] changed timeout fix fo better portability (timeout can be changed in pselect) --- src/igmpproxy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/igmpproxy.c b/src/igmpproxy.c index 8597616f..556f46c7 100644 --- a/src/igmpproxy.c +++ b/src/igmpproxy.c @@ -299,7 +299,7 @@ void igmpProxyRun(void) { timeout = NULL; } else { timeout->tv_nsec = 0; - timeout->tv_sec = (secs > 3) ? 3 : secs; // aimwang: set max timeout + timeout->tv_sec = secs = (secs > 3) ? 3 : secs; // aimwang: set max timeout } // Prepare for select. @@ -340,7 +340,7 @@ void igmpProxyRun(void) { * call gettimeofday. */ if (Rt == 0) { - curtime.tv_sec = lasttime.tv_sec + timeout->tv_sec; + curtime.tv_sec = lasttime.tv_sec + secs; curtime.tv_nsec = lasttime.tv_nsec; Rt = -1; /* don't do this next time through the loop */ } else { From fca44b979e2f47c0578c7b6e96d55a4c35878329 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Mon, 4 May 2020 00:02:20 +0200 Subject: [PATCH 16/25] fixed log output in interfaceInRoute --- src/rttable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rttable.c b/src/rttable.c index 03037a05..d38f18b6 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -897,7 +897,7 @@ int interfaceInRoute(int32_t group, int Ix) { struct RouteTable* croute; croute = findRoute(group); if (croute != NULL) { - my_log(LOG_DEBUG, 0, "Interface id %d is in group %d", Ix, group); + my_log(LOG_DEBUG, 0, "Interface id %d is in group %s", Ix, inetfmt(group, s1); return BIT_TST(croute->vifBits, Ix); } else { return 0; From 4e3a1e8c459fc3ed97ef9433f9d1bae96fe556d1 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Mon, 4 May 2020 00:03:42 +0200 Subject: [PATCH 17/25] fixed compile errors --- src/rttable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rttable.c b/src/rttable.c index d38f18b6..e0218751 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -897,7 +897,7 @@ int interfaceInRoute(int32_t group, int Ix) { struct RouteTable* croute; croute = findRoute(group); if (croute != NULL) { - my_log(LOG_DEBUG, 0, "Interface id %d is in group %s", Ix, inetfmt(group, s1); + my_log(LOG_DEBUG, 0, "Interface id %d is in group %s", Ix, inetFmt(group, s1)); return BIT_TST(croute->vifBits, Ix); } else { return 0; From b36f66148477ca9c5c7d7682dc5ef78cb60e7c1c Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Mon, 4 May 2020 00:42:15 +0200 Subject: [PATCH 18/25] fixed checksum calculation --- src/igmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/igmp.c b/src/igmp.c index 796c4c9f..9d6ea760 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -291,11 +291,11 @@ static void buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t g igmp->igmp_code = code; igmp->igmp_group.s_addr = group; igmp->igmp_cksum = 0; - igmp->igmp_cksum = inetChksum((unsigned short *)igmp, - IP_HEADER_RAOPT_LEN + datalen); igmp->igmp_misc = conf->robustnessValue & 0x7; igmp->igmp_qqi = conf->queryInterval; igmp->igmp_numsrc = 0; + igmp->igmp_cksum = inetChksum((unsigned short *)igmp, + IGMP_V3_QUERY_MINLEN + datalen); } From 3be9d1a90dbb5c9aba69cfb558977c1742eea972 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Tue, 5 May 2020 22:36:07 +0200 Subject: [PATCH 19/25] set DF flag on IGMP queries --- src/igmp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/igmp.c b/src/igmp.c index 9d6ea760..f37181e1 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -70,8 +70,9 @@ void initIgmp(void) { */ ip->ip_v = IPVERSION; ip->ip_hl = (sizeof(struct ip) + 4) >> 2; /* +4 for Router Alert option */ - ip->ip_tos = 0xc0; /* Internet Control */ - ip->ip_ttl = MAXTTL; /* applies to unicasts only */ + ip->ip_tos = 0xc0; /* Internet Control */ + ip->ip_off = htons(IP_DF); /* set Don't Fragment flag */ + ip->ip_ttl = MAXTTL; /* applies to unicasts only */ ip->ip_p = IPPROTO_IGMP; allhosts_group = htonl(INADDR_ALLHOSTS_GROUP); From dbd313d30f5a9a1af1c6c993b7d4827c50a1fb4f Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Wed, 13 May 2020 03:03:48 +0200 Subject: [PATCH 20/25] change os headers for new igmp.h --- src/os-dragonfly.h | 10 ++++++---- src/os-linux.h | 10 ++++++---- src/os-netbsd.h | 6 ++---- src/os-openbsd.h | 5 ++--- src/os-qnxnto.h | 3 ++- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/os-dragonfly.h b/src/os-dragonfly.h index 5ac8cc7e..2d9358ff 100644 --- a/src/os-dragonfly.h +++ b/src/os-dragonfly.h @@ -1,11 +1,13 @@ #include #include #include -#include +#include "igmp.h" -#define IGMP_V3_MEMBERSHIP_REPORT 0x22 - -#define INADDR_ALLIGMPV3_GROUP ((in_addr_t) 0xe0000016) +#define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY +#define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT +#define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT +#define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE +#define IGMP_V3_MEMBERSHIP_REPORT IGMP_v3_HOST_MEMBERSHIP_REPORT static inline unsigned short ip_data_len(const struct ip *ip) { diff --git a/src/os-linux.h b/src/os-linux.h index e0a271d4..c2b49b69 100644 --- a/src/os-linux.h +++ b/src/os-linux.h @@ -2,14 +2,16 @@ #define _GNU_SOURCE #include #include -#include #include #include #include +#include "igmp.h" -#define IGMP_V3_MEMBERSHIP_REPORT 0x22 - -#define INADDR_ALLIGMPV3_GROUP ((in_addr_t) 0xe0000016) +#define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY +#define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT +#define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT +#define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE +#define IGMP_V3_MEMBERSHIP_REPORT IGMP_v3_HOST_MEMBERSHIP_REPORT static inline unsigned short ip_data_len(const struct ip *ip) { diff --git a/src/os-netbsd.h b/src/os-netbsd.h index 311d0658..583c86a8 100644 --- a/src/os-netbsd.h +++ b/src/os-netbsd.h @@ -1,15 +1,13 @@ #include #include #include -#include +#include "igmp.h" #define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY #define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT #define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT -#define IGMP_V3_MEMBERSHIP_REPORT 0x22 #define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE - -#define INADDR_ALLIGMPV3_GROUP ((in_addr_t) 0xe0000016) +#define IGMP_V3_MEMBERSHIP_REPORT IGMP_v3_HOST_MEMBERSHIP_REPORT static inline unsigned short ip_data_len(const struct ip *ip) { diff --git a/src/os-openbsd.h b/src/os-openbsd.h index aeb36860..a2d2d18e 100644 --- a/src/os-openbsd.h +++ b/src/os-openbsd.h @@ -1,16 +1,15 @@ #include #include #include -#include +#include "igmp.h" #define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY #define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT #define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT -#define IGMP_V3_MEMBERSHIP_REPORT 0x22 #define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE +#define IGMP_V3_MEMBERSHIP_REPORT IGMP_v3_HOST_MEMBERSHIP_REPORT #define INADDR_ALLRTRS_GROUP INADDR_ALLROUTERS_GROUP -#define INADDR_ALLIGMPV3_GROUP ((in_addr_t) 0xe0000016) static inline unsigned short ip_data_len(const struct ip *ip) { diff --git a/src/os-qnxnto.h b/src/os-qnxnto.h index 150067a6..91f5f5c0 100644 --- a/src/os-qnxnto.h +++ b/src/os-qnxnto.h @@ -1,12 +1,13 @@ #include #include #include -#include +#include "igmp.h" #define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY #define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT #define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT #define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE +#define IGMP_V3_MEMBERSHIP_REPORT IGMP_v3_HOST_MEMBERSHIP_REPORT #define IPOPT_RA 148 /* router alert */ From 2bea55c8828d59ea50ea18771f9007d3ea04ae59 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Wed, 13 May 2020 03:06:04 +0200 Subject: [PATCH 21/25] fix source specific leave for last source and more than one upstream interface --- src/rttable.c | 190 ++++++++++++++++++++++++++------------------------ 1 file changed, 99 insertions(+), 91 deletions(-) diff --git a/src/rttable.c b/src/rttable.c index e0218751..fe59ef74 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -155,116 +155,124 @@ void initRouteTable(void) { } } -/** -* Internal function to send join or leave requests for -* a specified route upstream... -*/ -static void sendJoinLeaveUpstream(struct RouteTable* route, int join, struct in_addr *originAddr, u_short numOriginAddr ) { - struct IfDesc* upstrIf; +static void JoinLeaveUpstreams(int cmd, uint32_t mcastaddr, uint32_t originAddr) { int i; - for(i=0; iallowedgroups != NULL) { + struct SubnetList* sn; + + // Check if this Request is legit to be forwarded to upstream + for(sn = upstrIf->allowedgroups; sn != NULL; sn = sn->next) { + if((mcastaddr & sn->subnet_mask) == sn->subnet_addr) { + // Forward is OK... + break; + } + } + + if (sn == NULL) { + my_log(LOG_INFO, 0, "The group address %s may not be forwarded upstream. Ignoring.", inetFmt(mcastaddr, s1)); continue; } + } - // Check if there is a white list for the upstram VIF - if (upstrIf->allowedgroups != NULL) { - uint32_t group = route->group; - struct SubnetList* sn; + joinleave( cmd, getMcGroupSock(), upstrIf, mcastaddr, originAddr ); + } +} - // Check if this Request is legit to be forwarded to upstream - for(sn = upstrIf->allowedgroups; sn != NULL; sn = sn->next) { - if((group & sn->subnet_mask) == sn->subnet_addr) { - // Forward is OK... +/** +* Internal function to send join or leave requests for +* a specified route upstream... +*/ +static void sendJoinLeaveUpstream(struct RouteTable* route, int join, struct in_addr *originAddr, u_short numOriginAddr ) { + // Send join or leave request... + if(join) { + // Only join a group if there are listeners downstream... + if(route->vifBits > 0) { + my_log(LOG_DEBUG, 0, "Joining group %s upstream on IF address %s", + inetFmt(route->group, s1), + inetFmt(upstrIf->InAdr.s_addr, s2)); + + int u, v; + for(u = 0; u < numOriginAddr; u++) { + for(v = 0; v < MAX_ORIGINS; v++) { + if(route->reqOriginAddr[v] == originAddr[u].s_addr) { + // source are already joined + break; + } else if(route->reqOriginAddr[v] == 0) { + // save on first free place, then join for this source + route->reqOriginAddr[v] = originAddr[u].s_addr; + JoinLeaveUpstreams('j', route->group, originAddr[u].s_addr); break; } } - - if (sn == NULL) { - my_log(LOG_INFO, 0, "The group address %s may not be forwarded upstream. Ignoring.", inetFmt(group, s1)); - continue; - } } - // Send join or leave request... - if(join) { - // Only join a group if there are listeners downstream... - if(route->vifBits > 0) { - my_log(LOG_DEBUG, 0, "Joining group %s upstream on IF address %s", - inetFmt(route->group, s1), - inetFmt(upstrIf->InAdr.s_addr, s2)); - - int u, v; - for(u = 0; u < numOriginAddr; u++) { - for(v = 0; v < MAX_ORIGINS; v++) { - if(route->reqOriginAddr[v] == originAddr[u].s_addr) { - break; - } else if(route->reqOriginAddr[v] == 0) { - route->reqOriginAddr[v] = originAddr[u].s_addr; - joinleave( 'j', getMcGroupSock(), upstrIf, route->group, originAddr[u].s_addr ); - break; - } - } - } - - if(numOriginAddr == 0) { - //k_leave(route->group, upstrIf->InAdr.s_addr); - joinleave( 'j', getMcGroupSock(), upstrIf, route->group, 0 ); - } + // join for all sources if no sources are specified + if(numOriginAddr == 0) { + //k_leave(route->group, upstrIf->InAdr.s_addr); + JoinLeaveUpstreams( 'j', route->group, 0 ); + } - route->upstrState = ROUTESTATE_JOINED; - } else { - my_log(LOG_DEBUG, 0, "No downstream listeners for group %s. No join sent.", - inetFmt(route->group, s1)); + route->upstrState = ROUTESTATE_JOINED; + } else { + my_log(LOG_DEBUG, 0, "No downstream listeners for group %s. No join sent.", + inetFmt(route->group, s1)); - continue; - } - } else { - // Only leave if group is not left already... - if(route->upstrState != ROUTESTATE_NOTJOINED) { - my_log(LOG_DEBUG, 0, "Leaving group %s upstream on IF address %s", - inetFmt(route->group, s1), - inetFmt(upstrIf->InAdr.s_addr, s2)); - - int u, v; - for(u = 0; u < numOriginAddr; u++) { - for(v = 0; v < MAX_ORIGINS; v++) { - if(route->reqOriginAddr[v] == 0) { - break; - } else if(route->reqOriginAddr[v] == originAddr[u].s_addr) { - int w = v; - while(w + 1 < MAX_ORIGINS && route->reqOriginAddr[w] != 0) { - route->reqOriginAddr[w] = route->reqOriginAddr[w + 1]; - w++; - } - - route->reqOriginAddr[w] = 0; - joinleave( 'l', getMcGroupSock(), upstrIf, route->group, originAddr[u].s_addr ); - break; - } + return; + } + } else { + // Only leave if group is not left already... + if(route->upstrState != ROUTESTATE_NOTJOINED) { + my_log(LOG_DEBUG, 0, "Leaving group %s upstream on IF address %s", + inetFmt(route->group, s1), + inetFmt(upstrIf->InAdr.s_addr, s2)); + + int u, v; + for(u = 0; u < numOriginAddr; u++) { + for(v = 0; v < MAX_ORIGINS; v++) { + if(route->reqOriginAddr[v] == 0) { + // reached end of source list + break; + } else if(route->reqOriginAddr[v] == originAddr[u].s_addr) { + int w = v; + while(w + 1 < MAX_ORIGINS && route->reqOriginAddr[w] != 0) { + route->reqOriginAddr[w] = route->reqOriginAddr[w + 1]; + w++; } - } - if(numOriginAddr == 0 || route->reqOriginAddr[0] == 0) { - //k_leave(route->group, upstrIf->InAdr.s_addr); - joinleave( 'l', getMcGroupSock(), upstrIf, route->group, 0 ); - route->upstrState = ROUTESTATE_NOTJOINED; + route->reqOriginAddr[w] = 0; + + // check if its the last source for this group + if(route->reqOriginAddr[0] != 0) { + JoinLeaveUpstreams( 'l', route->group, originAddr[u].s_addr ); + } else { + JoinLeaveUpstreams( 'l', route->group, 0 ); + route->upstrState = ROUTESTATE_NOTJOINED; + return; + } + break; } - } else { - continue; } } - } - else - { - i = MAX_UPS_VIFS; + + // leave for all sources if no sources are specified + if(numOriginAddr == 0) { + //k_leave(route->group, upstrIf->InAdr.s_addr); + JoinLeaveUpstreams( 'l', route->group, 0 ); + route->upstrState = ROUTESTATE_NOTJOINED; + } } } } From 203aa8d64af10dd2b31089dbc848d6ffb7c35cc7 Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Wed, 13 May 2020 03:37:24 +0200 Subject: [PATCH 22/25] fix log message on join/leave --- src/rttable.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/rttable.c b/src/rttable.c index fe59ef74..bc4df935 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -187,6 +187,11 @@ static void JoinLeaveUpstreams(int cmd, uint32_t mcastaddr, uint32_t originAddr) } } + my_log(LOG_DEBUG, 0, "%s group %s upstream on IF address %s", + cmd == 'l' ? "Leaving" : "Joining", + inetFmt(mcastaddr, s1), + inetFmt(upstrIf->InAdr.s_addr, s2)); + joinleave( cmd, getMcGroupSock(), upstrIf, mcastaddr, originAddr ); } } @@ -200,15 +205,11 @@ static void sendJoinLeaveUpstream(struct RouteTable* route, int join, struct in_ if(join) { // Only join a group if there are listeners downstream... if(route->vifBits > 0) { - my_log(LOG_DEBUG, 0, "Joining group %s upstream on IF address %s", - inetFmt(route->group, s1), - inetFmt(upstrIf->InAdr.s_addr, s2)); - int u, v; for(u = 0; u < numOriginAddr; u++) { for(v = 0; v < MAX_ORIGINS; v++) { if(route->reqOriginAddr[v] == originAddr[u].s_addr) { - // source are already joined + // source is already joined break; } else if(route->reqOriginAddr[v] == 0) { // save on first free place, then join for this source @@ -235,10 +236,6 @@ static void sendJoinLeaveUpstream(struct RouteTable* route, int join, struct in_ } else { // Only leave if group is not left already... if(route->upstrState != ROUTESTATE_NOTJOINED) { - my_log(LOG_DEBUG, 0, "Leaving group %s upstream on IF address %s", - inetFmt(route->group, s1), - inetFmt(upstrIf->InAdr.s_addr, s2)); - int u, v; for(u = 0; u < numOriginAddr; u++) { for(v = 0; v < MAX_ORIGINS; v++) { From 3ddeb342691f333d7038860ab35dc1e8718f37dd Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Wed, 13 May 2020 03:41:59 +0200 Subject: [PATCH 23/25] add comment --- src/rttable.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rttable.c b/src/rttable.c index bc4df935..6fadcd75 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -155,6 +155,10 @@ void initRouteTable(void) { } } +/** +* Internal function to send join or leave requests +* on all upstream interfaces... +*/ static void JoinLeaveUpstreams(int cmd, uint32_t mcastaddr, uint32_t originAddr) { int i; for(i=0; i Date: Wed, 13 May 2020 23:29:04 +0200 Subject: [PATCH 24/25] add comment on fall through --- src/igmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/igmp.c b/src/igmp.c index f37181e1..1dc8d19f 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -218,7 +218,7 @@ void acceptIgmp(int recvlen) { if (nsrcs == 0) { acceptLeaveMessage(src, group, NULL, 0); break; - } + } /* else fall through */ case IGMP_ALLOW_NEW_SOURCES: acceptGroupReport(src, group, grec->ig_sources, nsrcs, grec->ig_type); break; From e7feb3730e7471ff63be754ef57e440e08c6325c Mon Sep 17 00:00:00 2001 From: Zocker007 Date: Wed, 13 May 2020 23:41:22 +0200 Subject: [PATCH 25/25] add comment on remaining fallthroughs --- src/rttable.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rttable.c b/src/rttable.c index 6fadcd75..6702b335 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -474,7 +474,7 @@ int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAdd case IGMP_CHANGE_TO_INCLUDE_MODE: if(croute->mode == IGMP_FILTER_MODE_INCLUDE) { sendJoin = 1; - } + } /* else fall through */ case IGMP_MODE_IS_INCLUDE: // don't allow include mode when other downstream hosts have requested exclude mode if(croute->mode == IGMP_FILTER_MODE_EXCLUDE) { @@ -486,7 +486,7 @@ int insertRoute(uint32_t group, int ifx, uint32_t src, struct in_addr *originAdd case IGMP_CHANGE_TO_EXCLUDE_MODE: if(croute->mode == IGMP_FILTER_MODE_INCLUDE) { sendJoin = 1; - } + } /* else fall through */ case IGMP_MODE_IS_EXCLUDE: default: croute->mode = IGMP_FILTER_MODE_EXCLUDE;