Skip to content

Commit

Permalink
Add --blockcidr and --allowcidr options
Browse files Browse the repository at this point in the history
  • Loading branch information
deonvdw committed May 19, 2022
1 parent a7f7e04 commit 928a6ad
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 19 deletions.
51 changes: 35 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
Test version with improved general SSD support and small fixes
--------------------------------------------------------------

- Removed the `-s 1.1.1.3` option and replaced it with a more general `--msearch`
option, which allows finer control (see USAGE section)
- Updated the expiry time for M-SEARCH, Locator and REST proxies.
- Fixed build information not being printed when `-d` is specififed
- Introduced a second level of debug info, activated by specifying `-d` twice


UDP Broadcast Relay for Linux / FreeBSD / pfSense / OPNsense
============================================================
( For Opensense a plugin is already available )
Expand Down Expand Up @@ -37,6 +27,8 @@ USAGE
--dev eth0 --dev eth1
[--dev ethx...] \
[--blockid id...] \
[--blockcidr network-prefix/size] \
[--allowcidr network-prefix/size] \
[--msearch action[,search-term]] \
[--multicast 224.0.0.251] \
[-s <spoof_source_ip>]
Expand All @@ -52,6 +44,16 @@ USAGE
echo and should be discarded.
- `blockid` can be used to drop packets received from other instances of
udpbroadcastrelay using the specified ID value.
- `--blockcidr` can be used to block packets from a range of IP source
addresses, given in CIDR notation. This option can be specified multiple
times to block more than one range. Where multiple overlapping CIDRs are
specified with the `--blockcidr` and `--allowcidr` options the most
specific match (longest prefix) will take effect.
- `--allowcidr` can be used to only allow packets from a range of IP source
addresses, given in CIDR notation. This option can be specified multiple
times to allow more than one range. Once this option is specified the
default behaviour for packets which does not any CIDRs changes from
Allow to Block.
- `udp-port` Destination udp port to listen to. Range 1 - 65535.
Example values for common services are given below.
- `-dev <ethx>` specifies the name of an interface to receive and
Expand Down Expand Up @@ -104,9 +106,20 @@ EXAMPLE

(Chromecast requires broadcasts to originate from an address on its subnet)

#### mDNS example which allows messages from hosts on 192.168.1.0/24 and 192.168.20.0/24 subnets but blocks host 192.168.20.20
`./udpbroadcastrelay --id 1 --port 5353 --dev eth0 --dev eth1 --multicast 224.0.0.251 -s 1.1.1.1 --allowcidr 192.168.1.0/24 --allowcidr 192.168.20.0/24 --blockcidr 192.168.20.20/32`

This will prevent relaying broadcast/multicast packets from host 192.168.20.20. It will not stop any unicast traffic from this host.

#### SSDP (Roku Discovery, DLNA Media, Sonos, UPnP + More)
`./udpbroadcastrelay --id 1 --port 1900 --dev eth0 --dev eth1 --multicast 239.255.255.250`

#### Youtube Application on Smart TV
`./udpbroadcastrelay --id 1 --dev eth0 --dev eth1 --port 1900 --multicast 239.255.255.250 -s 1.1.1.2 --msearch dial`

#### Youtube Application on Smart TV along with DLNA media playback
`./udpbroadcastrelay --id 1 --dev eth0 --dev eth1 --port 1900 --multicast 239.255.255.250 -s 1.1.1.2 --msearch proxy,urn:schemas-upnp-org:device:MediaServer:1 --msearch dial`

#### Lifx Bulb Discovery
`./udpbroadcastrelay --id 1 --port 56700 --dev eth0 --dev eth1`

Expand Down Expand Up @@ -134,12 +147,6 @@ EXAMPLE
#### Raknet Discovery (Minecraft)
`./udpbroadcastrelay --id 1 --port 19132 --dev eth0 --dev eth1`

#### Youtube Application on Smart TV
`./udpbroadcastrelay --id 1 --dev eth0 --dev eth1 --port 1900 --multicast 239.255.255.250 -s 1.1.1.2 --msearch dial`

#### Youtube Application on Smart TV along with DLNA media playback
`./udpbroadcastrelay --id 1 --dev eth0 --dev eth1 --port 1900 --multicast 239.255.255.250 -s 1.1.1.2 --msearch proxy,urn:schemas-upnp-org:device:MediaServer:1 --msearch dial`

Note about firewall rules
---

Expand All @@ -154,3 +161,15 @@ sending a broadcast packet to port 1900 to discover devices on the network.
The devices then respond to the broadcast with a unicast packet back to the
original sender. You will need to make sure that your firewall rules allow
these response packets to make it back to the original sender.


Recent changes
--------------

- Added --blockcidr and --allowcidr options
- Print interface names instead of numbers in packet information messages
- Removed the `-s 1.1.1.3` option and replaced it with a more general `--msearch`
option, which allows finer control (see USAGE section)
- Updated the expiry time for M-SEARCH, Locator and REST proxies.
- Fixed build information not being printed when `-d` is specififed
- Introduced a second level of debug info, activated by specifying `-d` twice
133 changes: 130 additions & 3 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ GNU General Public License for more details.
*/

#define MAXIFS 256
#define MAXCIDRACL 256
#define MAXMULTICAST 256
#define MAXBLOCKIDS 256
#define MAX_MSEARCH_FILTERS 64
Expand Down Expand Up @@ -85,6 +86,20 @@ struct Iface {
static struct Iface ifs[MAXIFS];
static int maxifs = 0;

#define ACTION_BLOCK 0
#define ACTION_ALLOW 1

/* list of CIDR ACL entries */
struct CIDRACL {
struct in_addr network;
struct in_addr mask;
u_short numbits;
u_short action;
};
static struct CIDRACL CIDRs[MAXCIDRACL];
static int numCIDRs = 0;
static int defaultCIDRaction = ACTION_ALLOW;

/* Where we forge our packets */
static u_char gram[4096+HEADER_LEN]=
{
Expand Down Expand Up @@ -240,11 +255,68 @@ char* ifname_from_idx (int ifindex)
return ifname_buf;
}

/* CIDR ACL compare function to qsort with descending mask size */
int CIDRcompare (const void*a, const void* b)
{
return ((const struct CIDRACL*) b)->numbits - ((const struct CIDRACL*) a)->numbits;
}

/* Parse CIDR string into a network number and mask. Return 0 if CIDR string is invalid */
int parse_cidr(char *str, struct CIDRACL* CIDR)
{
char buf[256];
char *s;
int numbits;

strncpy(buf, str, sizeof(buf));
s = strchr(buf, '/');
if (!s) {
return 0;
}
*(s++) = 0;

if (!inet_aton(buf, &(CIDR->network))) {
return 0;
}
numbits = atoi(s);
if ((numbits < 0) || (numbits > 32)) {
return 0;
}
CIDR->numbits = numbits;
CIDR->mask.s_addr = htonl((0xFFFFFFFFUL << (32 - numbits)) & 0xFFFFFFFFUL);
CIDR->network.s_addr &= CIDR->mask.s_addr;
return 1;
}

void inet_ntoa2(struct in_addr in, char* chr, int len) {
char* from = inet_ntoa(in);
strncpy(chr, from, len);
}

/* Check if packet should be blocked based on source IP. Return 0 to block packet */
int check_cidr_acl(struct in_addr fromAddress)
{
int i;
int action = defaultCIDRaction;
for (i = 0; i < numCIDRs; i++) {
if ((fromAddress.s_addr & CIDRs[i].mask.s_addr) == CIDRs[i].network.s_addr) {
action = CIDRs[i].action;
break;
}
}
if (action == ACTION_BLOCK) {
if (i < numCIDRs) {
char network[255];
inet_ntoa2(CIDRs[i].network, network, sizeof(network));
DPRINT("Source IP matches blocked CIDR %s/%i. Packet Ignored.\n\n", network, CIDRs[i].numbits);
} else {
DPRINT("Source IP does not match any allowed CIDR range. Packet Ignored.\n\n");
}
return 0;
}
return 1;
}

// Print formatted current time for log
void printtime (void)
{
Expand Down Expand Up @@ -391,6 +463,11 @@ int recv_with_addrinfo (int s, void *buf, size_t buflen, struct Iface **iface_ou
return -3;
}

if (numCIDRs) {
if (!check_cidr_acl(from_inaddr))
return -4;
}

if (from_inaddr_out) *from_inaddr_out = from_inaddr;
if (from_port_out) *from_port_out = from_port;
if (to_inaddr_out) *to_inaddr_out = to_inaddr;
Expand Down Expand Up @@ -1549,7 +1626,10 @@ void display_usage(FILE *stream, const char *arg0) {
fprintf(stream, "usage: %s [--id ID] [--port udp-port]\n"
" [--dev dev1] [--dev dev2] [--dev devX]\n"
" [-s IP] [--multicast ip1] [--multicast ipX]\n"
" [-t|--ttl-id] [-d] [-f]\n"
" [--msearch action[,search-term]]\n"
" [--blockcidr network-prefix/size]\n"
" [--allowcidr network-prefix/size]\n"
" [--blockid ID] [-t|--ttl-id] [-d] [-f]\n"
" [-h|--help]\n", arg0);
}

Expand Down Expand Up @@ -1618,6 +1698,17 @@ void display_help(const char *arg0) {
" --blockid ID Block traffic relayed by another udpbroadcastrelay\n"
" instance with the specified ID. --blockid can be\n"
" specified multiple times to block more than one ID.\n"
" --blockcidr network-prefix/size\n"
" Block traffic from source IP addresses within the\n"
" specified IP range. Where multiple overlapping CIDRs\n"
" are specified with the --blockcidr and --allowcidr\n"
" options the most specific match will take effect.\n"
" --allowcidr network-prefix/size\n"
" Allow traffic from source IP addresses within the\n"
" specified IP range. When one or more --allowcidr\n"
" options are specified the default behaviour changes\n"
" from allowing unmatched traffic to blocking traffic\n"
" without a matching --allowcidr rule.\n"
" -d Enables debugging. Specify twice for extra debug info.\n"
" -f Forces forking to background. A PID file will be created\n"
" at /var/run/udpbroadcastrelay_ID.pid\n"
Expand Down Expand Up @@ -1671,7 +1762,7 @@ srandom(time(NULL) ^ getpid());
if (strcmp(argv[i],"-d") == 0) {
debug++;
if (debug == 1) {
DPRINT ("udpbroadcastrelay v1.2.20 built on " __DATE__ " " __TIME__ "\n");
DPRINT ("udpbroadcastrelay v1.3.00 built on " __DATE__ " " __TIME__ "\n");
DPRINT ("Debugging Mode enabled\n");
}
}
Expand Down Expand Up @@ -1716,6 +1807,24 @@ srandom(time(NULL) ^ getpid());
blockIDs[numBlockIDs] = atoi(argv[i]);
numBlockIDs++;
}
else if ((strcmp(argv[i],"--blockcidr") == 0) || (strcmp(argv[i],"--allowcidr") == 0)) {
u_short action = ACTION_BLOCK;
if (strcmp(argv[i],"--allowcidr") == 0) {
action = ACTION_ALLOW;
defaultCIDRaction = ACTION_BLOCK;
}
if (numCIDRs >= (MAXCIDRACL)) {
fprintf(stderr, "More than maximum %i CIDR ACLs specified.\n", MAXCIDRACL);
exit(1);
}
i++;
if (!parse_cidr(argv[i], CIDRs + numCIDRs)) {
fprintf(stderr, "Unable to parse CIDR %s\n", argv[i]);
exit(1);
}
CIDRs[numCIDRs].action = action;
numCIDRs++;
}
else if (strcmp(argv[i],"--port") == 0) {
i++;
port = atoi(argv[i]);
Expand Down Expand Up @@ -1814,6 +1923,19 @@ srandom(time(NULL) ^ getpid());
}
DPRINT ("\n");
}

if (numCIDRs) {
qsort(CIDRs, numCIDRs, sizeof(struct CIDRACL), CIDRcompare);
DPRINT ("CIDR access control list:\n");
for (i = 0; i < numCIDRs; i++) {
char network[255];
inet_ntoa2(CIDRs[i].network, network, sizeof(network));
char mask[255];
inet_ntoa2(CIDRs[i].mask, mask, sizeof(mask));
DPRINT (" %s CIDR %s/%i (mask %s)\n", (CIDRs[i].action == ACTION_ALLOW) ? "Allow" : "Block", network, CIDRs[i].numbits, mask);
}
DPRINT ("Packets with unmatched CIDR: %s\n\n", (defaultCIDRaction == ACTION_ALLOW) ? "Allow" : "Block");
}

if (id < 1 || id > MAXID)
{
Expand Down Expand Up @@ -2243,11 +2365,16 @@ srandom(time(NULL) ^ getpid());
break;
}
if (i < numBlockIDs) {
DPRINT ("Packet ID %i matches a blocklist ID. Packet Ignored.\n\n", rxid);
DPRINT("Packet ID %i matches a blocklist ID. Packet Ignored.\n\n", rxid);
continue;
}
}

if (numCIDRs) {
if (!check_cidr_acl(origFromAddress))
continue;
}

if (!fromIface) {
DPRINT("Not from managed iface\n\n");
continue;
Expand Down

0 comments on commit 928a6ad

Please sign in to comment.