diff --git a/doc/igmpproxy.conf.5.in b/doc/igmpproxy.conf.5.in index 74378417..6154b412 100644 --- a/doc/igmpproxy.conf.5.in +++ b/doc/igmpproxy.conf.5.in @@ -78,6 +78,17 @@ the risk of bandwidth saturation. .RE +.B proto +.I protover +.RS +Specifies IGMP protocol version. On default or whith 'proto igmpv2', IGMPv1/2 +queries are send out. With 'proto igmpv3', you can enable IGMPv3 frames for +sending queries. This is needed for IGMPv3 capable hosts to send IGMPv3 reports. +When a IGMPv3 capable host receives a IGMPv1/2 query, it answers with a IGMPv1/2 +report. +.RE + + .B phyint .I interface .I role @@ -187,6 +198,9 @@ explicitly whitelisted multicast groups will be ignored. ## Enable quickleave quickleave .br +## Enable IGMPv3 queries instead of IGMPv1/2 +igmpv3 +.br ## Define settings for eth0 (upstream) .br phyint eth0 upstream diff --git a/src/config.c b/src/config.c index eef7f1af..c8a50714 100644 --- a/src/config.c +++ b/src/config.c @@ -84,6 +84,9 @@ static void initCommonConfig(void) { // If 1, a leave message is sent upstream on leave messages from downstream. commonConfig.fastUpstreamLeave = 0; + // If 1, IGMPv3 queries are send instead of IGMPv1/2 + commonConfig.useIgmpv3 = 0; + // Default size of hash table is 32 bytes (= 256 bits) and can store // up to the 256 non-collision hosts, approximately half of /24 subnet commonConfig.downstreamHostsHashTableSize = 32; @@ -221,6 +224,23 @@ int loadConfig(char *configFile) { my_log(LOG_DEBUG, 0, "Config: user set to %s", commonConfig.user); token = nextConfigToken(); continue; + } + else if(strcmp("proto", token)==0) { + // IGMP protocol version to use is in next token + token = nextConfigToken(); + + if (strcmp("igmpv3", token) == 0) { + my_log(LOG_DEBUG, 0, "Config: IGMPv3 mode enabled."); + commonConfig.useIgmpv3 = 1; + } + else if (strcmp("igmpv2", token) == 0) { + my_log(LOG_DEBUG, 0, "Config: IGMPv2 mode enabled."); + commonConfig.useIgmpv3 = 0; + } + + // Read next token... + token = nextConfigToken(); + continue; } else { // Unparsable token... Exit... closeConfigFile(); diff --git a/src/igmp.c b/src/igmp.c index a80c4e58..7400bd56 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -260,15 +260,22 @@ void acceptIgmp(int recvlen) { * Construct an IGMP message in the output packet buffer. The caller may * have already placed data in that buffer, of length 'datalen'. */ -static void buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen) { +static int buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen) { struct ip *ip; - struct igmp *igmp; + struct igmpv3_query *igmp; + struct Config *conf = getCommonConfig(); extern int curttl; + int query_minlen = IGMP_MINLEN; 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); + + if(conf->useIgmpv3) { + query_minlen = IGMP_V3_QUERY_MINLEN; + } + + ip_set_len(ip, IP_HEADER_RAOPT_LEN + query_minlen + datalen); if (IN_MULTICAST(ntohl(dst))) { ip->ip_ttl = curttl; @@ -282,14 +289,24 @@ 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_query *)(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); + if(conf->useIgmpv3) { + 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); + } else { + igmp->igmp_cksum = inetChksum((unsigned short *)igmp, + IP_HEADER_RAOPT_LEN + datalen); + } + + return IP_HEADER_RAOPT_LEN + query_minlen + datalen; } /* @@ -301,9 +318,9 @@ static void buildIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t g */ void sendIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen, int ifidx) { struct sockaddr_in sdst; - int setloop = 0, setigmpsource = 0; + int setloop = 0, setigmpsource = 0, sendlen = 0; - buildIgmp(src, dst, type, code, group, datalen); + sendlen = buildIgmp(src, dst, type, code, group, datalen); if (IN_MULTICAST(ntohl(dst))) { k_set_if(src, ifidx); @@ -320,8 +337,7 @@ void sendIgmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, in sdst.sin_len = sizeof(sdst); #endif sdst.sin_addr.s_addr = dst; - if (sendto(MRouterFD, send_buf, - IP_HEADER_RAOPT_LEN + IGMP_MINLEN + datalen, 0, + if (sendto(MRouterFD, send_buf, sendlen, 0, (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { if (errno == ENETDOWN) my_log(LOG_ERR, errno, "Sender VIF was down."); diff --git a/src/igmpproxy.h b/src/igmpproxy.h index b8f46a35..96c1eb18 100644 --- a/src/igmpproxy.h +++ b/src/igmpproxy.h @@ -175,6 +175,8 @@ struct Config { unsigned int lastMemberQueryCount; // Set if upstream leave messages should be sent instantly.. unsigned short fastUpstreamLeave; + // Set if IGMPv3 should be used for IGMP queries + unsigned short useIgmpv3; // Size in bytes of hash table of downstream hosts used for fast leave unsigned int downstreamHostsHashTableSize; //~ aimwang added diff --git a/src/igmpv3.h b/src/igmpv3.h index f8658237..986b870d 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_query { + 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[0]; /* source addresses */ +}; + struct igmpv3_grec { u_int8_t grec_type; u_int8_t grec_auxwords; diff --git a/src/os-linux.h b/src/os-linux.h index e0a271d4..bdfbe234 100644 --- a/src/os-linux.h +++ b/src/os-linux.h @@ -8,6 +8,7 @@ #include #define IGMP_V3_MEMBERSHIP_REPORT 0x22 +#define IGMP_V3_QUERY_MINLEN IGMPV3_MINLEN #define INADDR_ALLIGMPV3_GROUP ((in_addr_t) 0xe0000016)