diff --git a/src/ifvc.c b/src/ifvc.c index 41fb630b..e902b69b 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); - k_join(Dp, allrouters_group); + k_join(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); - k_join(Dp, allrouters_group); + k_join(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; - k_leave(Dp, allrouters_group); + k_leave(Dp, allrouters_group, 0); delVIF(Dp); } } diff --git a/src/igmp.c b/src/igmp.c index 38914377..1dc8d19f 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -37,7 +37,6 @@ */ #include "igmpproxy.h" -#include "igmpv3.h" // Globals uint32_t allhosts_group; /* All hosts addr in net order */ @@ -71,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); @@ -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)) { @@ -121,13 +121,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 @@ -192,7 +185,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)); @@ -207,47 +200,51 @@ 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: - igmpv3 = (struct igmpv3_report *)(recv_buf + iphdrlen); - grec = &igmpv3->igmp_grec[0]; - ngrec = ntohs(igmpv3->igmp_ngrec); + igmpv3 = (struct igmp_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); + acceptLeaveMessage(src, group, NULL, 0); break; } /* else fall through */ - case IGMPV3_MODE_IS_EXCLUDE: - case IGMPV3_CHANGE_TO_EXCLUDE: - case IGMPV3_ALLOW_NEW_SOURCES: - acceptGroupReport(src, group); + case IGMP_ALLOW_NEW_SOURCES: + acceptGroupReport(src, group, grec->ig_sources, nsrcs, grec->ig_type); break; - case IGMPV3_BLOCK_OLD_SOURCES: + 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; + 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; 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,13 +266,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 + IGMP_V3_QUERY_MINLEN + datalen); if (IN_MULTICAST(ntohl(dst))) { ip->ip_ttl = curttl; @@ -289,13 +287,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_misc = conf->robustnessValue & 0x7; + igmp->igmp_qqi = conf->queryInterval; + igmp->igmp_numsrc = 0; igmp->igmp_cksum = inetChksum((unsigned short *)igmp, - IP_HEADER_RAOPT_LEN + datalen); + IGMP_V3_QUERY_MINLEN + datalen); } @@ -326,7 +327,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 + 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..277f21f9 --- /dev/null +++ b/src/igmp.h @@ -0,0 +1,159 @@ +/*- + * 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[];*/ /* 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[]; /* 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[]; /* 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 filter modes + */ +#define IGMP_FILTER_MODE_EXCLUDE 1 +#define IGMP_FILTER_MODE_INCLUDE 2 + +/* + * 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/igmpproxy.c b/src/igmpproxy.c index c72a567f..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. diff --git a/src/igmpproxy.h b/src/igmpproxy.h index 768516ac..4ca9fc7e 100644 --- a/src/igmpproxy.h +++ b/src/igmpproxy.h @@ -242,24 +242,25 @@ void k_hdr_include(int hdrincl); void k_set_ttl(int t); void k_set_loop(int l); void k_set_if(uint32_t ifa); -void k_join(struct IfDesc *ifd, uint32_t grp); -void k_leave(struct IfDesc *ifd, uint32_t grp); +int k_joinleave(int cmd, struct IfDesc *ifd, uint32_t grp, uint32_t originAddr) +void k_join(struct IfDesc *ifd, uint32_t grp, uint32_t originAddr); +void k_leave(struct IfDesc *ifd, uint32_t grp, 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, 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); +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); /* 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, 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/igmpv3.h b/src/igmpv3.h deleted file mode 100644 index f8658237..00000000 --- a/src/igmpv3.h +++ /dev/null @@ -1,48 +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. -*/ - -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/kern.c b/src/kern.c index e99ea350..9664cd77 100644 --- a/src/kern.c +++ b/src/kern.c @@ -111,16 +111,63 @@ void k_set_if(uint32_t ifa) { inetFmt(ifa, s1)); } -void k_join(struct IfDesc *ifd, uint32_t grp) { - struct ip_mreq mreq; +int k_joinleave(int cmd, struct IfDesc *ifd, uint32_t grp, uint32_t originAddr) +{ + int ret ; + + if(originAddr == 0) { + struct ip_mreq mreq; + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr.s_addr = grp; + mreq.imr_interface.s_addr = ifd->InAdr.s_addr; + + switch(cmd) + { + case 'j': + ret = setsockopt(MRouterFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char *)&mreq, sizeof(mreq)); + break; + case 'l': + ret = setsockopt(MRouterFD, IPPROTO_IP, IP_DROP_MEMBERSHIP, + (char *)&mreq, sizeof(mreq)); + break; + default: + ret = -1; + break; + } + } + else { + struct ip_mreq_source mreq_source; + memset(&mreq_source, 0, sizeof(mreq_source)); + mreq_source.imr_multiaddr.s_addr = grp; + mreq_source.imr_interface.s_addr = ifd->InAdr.s_addr; + mreq_source.imr_sourceaddr.s_addr = originAddr; + + switch(cmd) + { + case 'j': + ret = setsockopt(MRouterFD, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, + (char *)&mreq_source, sizeof(mreq_source)); + break; + case 'l': + ret = setsockopt(MRouterFD, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, + (char *)&mreq_source, sizeof(mreq_source)); + break; + default: + ret = -1; + break; + } + } - mreq.imr_multiaddr.s_addr = grp; - mreq.imr_interface.s_addr = ifd->InAdr.s_addr; + return ret; +} + +void k_join(struct IfDesc *ifd, uint32_t grp, uint32_t originAddr) { - my_log(LOG_NOTICE, 0, "Joining group %s on interface %s", inetFmt(grp, s1), ifd->Name); + my_log(LOG_NOTICE, 0, "Joining group %s on interface %s from %s", + inetFmt(grp, s1), ifd->Name, originAddr != 0 ? inetFmt( originAddr, s2 ) : ""); - if (setsockopt(MRouterFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, - (char *)&mreq, sizeof(mreq)) < 0) { + if (k_joinleave('j', ifd, grp, originAddr) < 0) { int mcastGroupExceeded = (errno == ENOBUFS); my_log(LOG_WARNING, errno, "can't join group %s on interface %s", inetFmt(grp, s1), ifd->Name); @@ -133,16 +180,12 @@ void k_join(struct IfDesc *ifd, uint32_t grp) { } } -void k_leave(struct IfDesc *ifd, uint32_t grp) { - struct ip_mreq mreq; - - mreq.imr_multiaddr.s_addr = grp; - mreq.imr_interface.s_addr = ifd->InAdr.s_addr; +void k_leave(struct IfDesc *ifd, uint32_t grp, uint32_t originAddr) { - my_log(LOG_NOTICE, 0, "Leaving group %s on interface %s", inetFmt(grp, s1), ifd->Name); + my_log(LOG_NOTICE, 0, "Leaving group %s on interface %s from %s", + inetFmt(grp, s1), ifd->Name, originAddr != 0 ? inetFmt( originAddr, s2 ) : ""); - if (setsockopt(MRouterFD, IPPROTO_IP, IP_DROP_MEMBERSHIP, - (char *)&mreq, sizeof(mreq)) < 0) + if (k_joinleave('l', ifd, grp, originAddr) < 0) my_log(LOG_WARNING, errno, "can't leave group %s on interface %s", inetFmt(grp, s1), ifd->Name); } 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-freebsd.h b/src/os-freebsd.h index 9957d524..909dc51e 100644 --- a/src/os-freebsd.h +++ b/src/os-freebsd.h @@ -6,7 +6,7 @@ #include #include #include -#include +#include "igmp.h" #if __FreeBSD_version >= 800069 && defined BURN_BRIDGES \ || __FreeBSD_version >= 800098 @@ -14,10 +14,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) { 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 */ diff --git a/src/request.c b/src/request.c index 06d14c44..c44b6e46 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, u_short numOriginAddr, u_char mode) { struct IfDesc *sourceVif; // Sanitycheck the group adress... @@ -77,6 +77,12 @@ void acceptGroupReport(uint32_t src, uint32_t group) { 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) { @@ -86,7 +92,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, mode); return; } // Check if this Request is legit on this interface @@ -95,10 +101,10 @@ 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, 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.", @@ -109,7 +115,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, u_short numOriginAddr ) { struct IfDesc *sourceVif; my_log(LOG_DEBUG, 0, @@ -138,7 +144,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; @@ -193,7 +199,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 +230,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); diff --git a/src/rttable.c b/src/rttable.c index 2593ecaf..ad5d4dbf 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -51,6 +51,8 @@ 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 + uint32_t reqOriginAddr[MAX_ORIGINS]; // IGMPv3 requested source // Keeps the upstream membership state... short upstrState; // Upstream membership state. @@ -130,81 +132,134 @@ void initRouteTable(void) { my_log(LOG_DEBUG, 0, "Joining all-routers group %s on vif %s", inetFmt(allrouters_group,s1),inetFmt(Dp->InAdr.s_addr,s2)); - k_join(Dp, allrouters_group); + k_join(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)); - k_join(Dp, alligmp3_group); + k_join(Dp, alligmp3_group, 0); } } } /** -* Internal function to send join or leave requests for -* a specified route upstream... +* Internal function to send join or leave requests +* on all upstream interfaces... */ -static void sendJoinLeaveUpstream(struct RouteTable* route, int join) { - struct IfDesc* upstrIf; +static void JoinLeaveUpstreams(int cmd, uint32_t mcastaddr, uint32_t originAddr) { int i; - for(i=0; iallowedgroups != NULL) { - uint32_t group = route->group; - struct SubnetList* sn; + if (-1 == upStreamIfIdx[i]) { + return; + } + // Get the upstream IF... + struct IfDesc *upstrIf = getIfByIx( upStreamIfIdx[i] ); + if(upstrIf == NULL) { + my_log(LOG_ERR, 0 ,"FATAL: Unable to get Upstream IF."); + continue; + } - // 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... - break; + // Check if there is a white list for the upstram VIF + if (upstrIf->allowedgroups != NULL) { + struct SubnetList* sn; - if (sn == NULL) { - my_log(LOG_INFO, 0, "The group address %s may not be forwarded upstream. Ignoring.", inetFmt(group, s1)); - return; + // 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; } } - // 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)); + if (sn == NULL) { + my_log(LOG_INFO, 0, "The group address %s may not be forwarded upstream. Ignoring.", inetFmt(mcastaddr, s1)); + continue; + } + } - k_join(upstrIf, route->group); + 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)); + + k_joinleave(cmd, upstrIf, mcastaddr, originAddr); + } +} - route->upstrState = ROUTESTATE_JOINED; - } else { - my_log(LOG_DEBUG, 0, "No downstream listeners for group %s. No join sent.", - inetFmt(route->group, s1)); +/** +* 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) { + 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 is 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; + } } - } 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)); + } - k_leave(upstrIf, route->group); + // 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_NOTJOINED; + return; + } + } else { + // Only leave if group is not left already... + if(route->upstrState != ROUTESTATE_NOTJOINED) { + 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++; + } + + 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 - { - 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; + } } } } @@ -230,7 +285,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); @@ -262,10 +317,11 @@ 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, u_short numOriginAddr, u_char mode) { struct Config *conf = getCommonConfig(); struct RouteTable* croute; + int sendJoin = 0; // Sanitycheck the group adress... if( ! IN_MULTICAST( ntohl(group) )) { @@ -297,6 +353,7 @@ int insertRoute(uint32_t group, int ifx, uint32_t src) { newroute->nextroute = NULL; newroute->prevroute = NULL; newroute->upstrVif = -1; + newroute->mode = 0; if(conf->fastUpstreamLeave) { // Init downstream hosts bit hash table @@ -397,10 +454,33 @@ int insertRoute(uint32_t group, int ifx, uint32_t src) { } } + switch(mode) { + case IGMP_ALLOW_NEW_SOURCES: + 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) { + break; + } + + croute->mode = IGMP_FILTER_MODE_INCLUDE; + break; + 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; + } + // Send join message upstream, if the route has no joined flag... - if(croute->upstrState != ROUTESTATE_JOINED) { + if(sendJoin || croute->upstrState != ROUTESTATE_JOINED) { // Send Join request upstream - sendJoinLeaveUpstream(croute, 1); + sendJoinLeaveUpstream(croute, 1, originAddr, numOriginAddr); } logRouteTable("Insert Route"); @@ -425,7 +505,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); @@ -522,7 +602,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, u_short numOriginAddr ) { struct Config *conf = getCommonConfig(); struct RouteTable *croute; int routeStateCheck = 1; @@ -546,8 +626,9 @@ void setRouteLastMemberMode(uint32_t group, uint32_t src) { // 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); + 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)); } @@ -611,7 +692,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... @@ -810,7 +891,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;