From cfad33001fc5031399bdd073048ca86889378678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ketelaars?= Date: Fri, 9 Jul 2021 22:19:12 +0200 Subject: [PATCH] Complement phyint whitelist with blacklist Fixes: #54 Implement new phyint configuration option (blacklist), which enables blocking of specific traffic. --- doc/igmpproxy.conf.5.in | 20 ++++++++++++++++++++ src/config.c | 22 +++++++++++++++++++--- src/igmp.c | 7 ------- src/igmpproxy.h | 1 + src/request.c | 27 +++++++++++++++++++-------- src/rttable.c | 21 ++++++++++++++------- 6 files changed, 73 insertions(+), 25 deletions(-) diff --git a/doc/igmpproxy.conf.5.in b/doc/igmpproxy.conf.5.in index cd7eade2..74378417 100644 --- a/doc/igmpproxy.conf.5.in +++ b/doc/igmpproxy.conf.5.in @@ -161,6 +161,26 @@ You may also specify whitelist entries for the upstream interface. Only igmp mem for explicitly whitelisted multicast groups will be sent out on the upstream interface. This is useful if you want to use multicast groups only between your downstream interfaces, like SSDP from a UPnP server. + +This option can be combined with +.B blacklist +for fine-grained control. +.RE + +.B blacklist +.I networkaddr +.RS +Defines a blacklist for multicast groups. Similar to +.B whitelist +except that if a blacklist entry is defined, all igmp membership reports for +that multicast group will be ignored and therefore not be served by igmpproxy. + +Each time a multicast group is forwarded or requested, whitelist and blacklist +entries are evaluated in sequential order, from first to last. The last matching +entry decides what action is taken; if no entry matches the multicast group, the +default action is to serve. Note that, if at least one whitelist entry is +defined before any blacklist entry, all igmp membership reports for not +explicitly whitelisted multicast groups will be ignored. .RE .SH EXAMPLE diff --git a/src/config.c b/src/config.c index aab26f97..2e1e3aba 100644 --- a/src/config.c +++ b/src/config.c @@ -351,10 +351,26 @@ struct vifconfig *parsePhyintToken(void) { *agrpPtr = parseSubnetAddress(token); if(*agrpPtr == NULL) { - parseError = 1; - my_log(LOG_WARNING, 0, "Unable to parse subnet address."); - break; + free(tmpPtr->name); + free(tmpPtr); + my_log(LOG_ERR, 0, "Unable to parse subnet address."); + } else { + (*agrpPtr)->allow = true; + agrpPtr = &(*agrpPtr)->next; + } + } + else if(strcmp("blacklist", token)==0) { + // Blacklist + token = nextConfigToken(); + my_log(LOG_DEBUG, 0, "Config: IF: Got blacklist token %s.", token); + + *agrpPtr = parseSubnetAddress(token); + if(*agrpPtr == NULL) { + free(tmpPtr->name); + free(tmpPtr); + my_log(LOG_ERR, 0, "Unable to parse subnet address."); } else { + (*agrpPtr)->allow = false; agrpPtr = &(*agrpPtr)->next; } } diff --git a/src/igmp.c b/src/igmp.c index 38914377..7f18ddce 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -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 diff --git a/src/igmpproxy.h b/src/igmpproxy.h index 94685046..dbc696fe 100644 --- a/src/igmpproxy.h +++ b/src/igmpproxy.h @@ -145,6 +145,7 @@ struct SubnetList { uint32_t subnet_addr; uint32_t subnet_mask; struct SubnetList *next; + bool allow; }; struct IfDesc { diff --git a/src/request.c b/src/request.c index 06d14c44..d032d7c9 100644 --- a/src/request.c +++ b/src/request.c @@ -83,22 +83,33 @@ void acceptGroupReport(uint32_t src, uint32_t group) { my_log(LOG_DEBUG, 0, "Should insert group %s (from: %s) to route table. Vif Ix : %d", inetFmt(group,s1), inetFmt(src,s2), sourceVif->index); - // If we don't have a whitelist we insertRoute and done + // If we don't have a black- and whitelist we insertRoute and done if(sourceVif->allowedgroups == NULL) { insertRoute(group, sourceVif->index, src); return; } + // Check if this Request is legit on this interface - struct SubnetList *sn; - for(sn = sourceVif->allowedgroups; sn != NULL; sn = sn->next) + bool allow_list = false; + struct SubnetList *match = NULL; + struct SubnetList *sn; + + for(sn = sourceVif->allowedgroups; sn != NULL; sn = sn->next) { + // Check if there is a whitelist + if (sn->allow) + allow_list = true; if((group & sn->subnet_mask) == sn->subnet_addr) - { - // The membership report was OK... Insert it into the route table.. - insertRoute(group, sourceVif->index, src); - return; + match = sn; + } + + if((!allow_list && match == NULL) || + (allow_list && match != NULL && match->allow)) { + // The membership report was OK... Insert it into the route table.. + insertRoute(group, sourceVif->index, src); + 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 2593ecaf..ceddcc73 100644 --- a/src/rttable.c +++ b/src/rttable.c @@ -157,18 +157,25 @@ static void sendJoinLeaveUpstream(struct RouteTable* route, int join) { my_log(LOG_ERR, 0 ,"FATAL: Unable to get Upstream IF."); } - // Check if there is a white list for the upstram VIF + // Check if there is a black- or whitelist for the upstram VIF if (upstrIf->allowedgroups != NULL) { - uint32_t group = route->group; - struct SubnetList* sn; + bool allow_list = false; + struct SubnetList *match = NULL; + struct SubnetList *sn; + uint32_t group = route->group; // Check if this Request is legit to be forwarded to upstream - for(sn = upstrIf->allowedgroups; sn != NULL; sn = sn->next) + for(sn = upstrIf->allowedgroups; sn != NULL; sn = sn->next) { + // Check if there is a whitelist + if (sn->allow) + allow_list = true; if((group & sn->subnet_mask) == sn->subnet_addr) - // Forward is OK... - break; + match = sn; + } - if (sn == NULL) { + // Keep in sync with request.c, note the negation + if(!((!allow_list && match == NULL) || + (allow_list && match != NULL && match->allow))) { my_log(LOG_INFO, 0, "The group address %s may not be forwarded upstream. Ignoring.", inetFmt(group, s1)); return; }