Skip to content

Commit

Permalink
Fix #118: Support for joining IPv4 groups with prefix length
Browse files Browse the repository at this point in the history
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
  • Loading branch information
troglobit committed Mar 18, 2019
1 parent 54dbb03 commit 972724d
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 86 deletions.
22 changes: 12 additions & 10 deletions smcroute.8
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
.Nm smcroutectl
.Ao add \ | \ \ rem Ac Ao IFNAME Ac Oo SOURCE Oc Ar GROUP[/LEN] IFNAME Op IFNAME ...
.Nm smcroutectl
.Ao join | leave Ac Ao IFNAME Ac Oo SOURCE Oc Ar GROUP
.Ao join | leave Ac Ao IFNAME Ac Oo SOURCE Oc Ar GROUP[/LEN]
.Sh DESCRIPTION
.Nm
is both a daemon and command line tool to manipulate the multicast
Expand Down Expand Up @@ -251,12 +251,14 @@ works in the daemon, this client command initiates an immediate flush of
all dynamically set (*,G) routes. Useful when a topology change has
been detected and need to be propagated to
.Nm smcrouted.
.It Nm join Ar IFNAME [SOURCE] GROUP
Join a multicast group on a given interface. The source address is
optional, but if given a source specific (SSM) join is performed.
.It Nm leave Ar IFNAME [SOURCE] GROUP
Leave a multicast group on a given interface. As with the join command,
above, the source address is optional.
.It Nm join Ar IFNAME [SOURCE] GROUP[/LEN]
Join a multicast group, with optional prefix length (IPv4 only), on a
given interface. The source address is optional, but if given a source
specific (SSM) join is performed.
.It Nm leave Ar IFNAME [SOURCE] GROUP[/LEN]
Leave a multicast group, with optional prefix length (IPv4 only), on a
given interface. As with the join command, above, the source address is
optional.
.It Nm help [cmd]
Print a usage information message.
.It Nm kill
Expand Down Expand Up @@ -381,8 +383,8 @@ etc. Wildcards are available for inbound interfaces.
# such capabilities. Usually MAC multicast filters exist.
#
# The UNIX kernel usually limits the number of multicast groups
# a socket/client can join. In Linux, 20 mgroup lines can be
# configured by default, but this can be changed with sysctl:
# a socket/client can join. On Linux, 20 groups can be joined
# by default, but this can be changed with sysctl:
#
# sysctl -w net.ipv4.igmp_max_memberships=30
#
Expand All @@ -392,7 +394,7 @@ etc. Wildcards are available for inbound interfaces.
#
# Syntax:
# phyint IFNAME <enable|disable> [mrdisc] [ttl-threshold <1-255>]
# mgroup from IFNAME [source ADDRESS] group MCGROUP
# mgroup from IFNAME [source ADDRESS] group MCGROUP[/LEN]
# mroute from IFNAME [source ADDRESS] group MCGROUP[/LEN] to IFNAME [IFNAME ...]

# This example disables the creation of a multicast VIF for WiFi
Expand Down
15 changes: 14 additions & 1 deletion src/conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ static int join_mgroup(int lineno, char *ifname, char *source, char *group)
} else {
struct in_addr src;
struct in_addr grp;
char *ptr;
int len = 0;

memset(&src, 0, sizeof(src));

Expand All @@ -116,12 +118,23 @@ static int join_mgroup(int lineno, char *ifname, char *source, char *group)
return 1;
}

ptr = strchr(group, '/');
if (ptr) {
*ptr++ = 0;
len = atoi(ptr);

if (len < 0 || len > 32) {
WARN("Invalid IPv4 group prefix length (0-32): %d", len);
return 1;
}
}

if ((inet_pton(AF_INET, group, &grp) <= 0) || !IN_MULTICAST(ntohl(grp.s_addr))) {
WARN("Invalid IPv4 multicast group: %s", group);
return 1;
}

result = mcgroup4_join(ifname, src, grp);
result = mcgroup4_join(ifname, src, grp, len);
}

return result;
Expand Down
121 changes: 76 additions & 45 deletions src/mcgroup.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ struct mgroup {
short inbound;
struct in_addr source;
struct in_addr group;
uint8_t len;
};

/*
Expand Down Expand Up @@ -83,7 +84,7 @@ static struct iface *match_valid_iface(const char *ifname, struct ifmatch *state
return iface;
}

static void list_add(struct iface *iface, struct in_addr source, struct in_addr group, int fail)
static void list_add(struct iface *iface, struct in_addr source, struct in_addr group, int len, int fail)
{
struct mgroup *entry;

Expand All @@ -97,21 +98,23 @@ static void list_add(struct iface *iface, struct in_addr source, struct in_addr
entry->inbound = iface->vif;
entry->source = source;
entry->group = group;
entry->len = len;

if (fail)
LIST_INSERT_HEAD(&mgroup_retry_list, entry, link);
else
LIST_INSERT_HEAD(&mgroup_static_list, entry, link);
}

static void list_rem(struct iface *iface, struct in_addr source, struct in_addr group)
static void list_rem(struct iface *iface, struct in_addr source, struct in_addr group, int len)
{
struct mgroup *entry, *tmp;

LIST_FOREACH_SAFE(entry, &mgroup_static_list, link, tmp) {
if (entry->inbound != iface->vif ||
entry->source.s_addr != source.s_addr ||
entry->group.s_addr != group.s_addr)
entry->group.s_addr != group.s_addr ||
entry->len != len)
continue;

LIST_REMOVE(entry, link);
Expand Down Expand Up @@ -184,27 +187,39 @@ static int join_leave(int sd, int cmd, struct iface *iface, void *src, void *grp
}
#endif

static int join_leave_ipv4(int sd, int cmd, struct iface *iface, struct in_addr group)
static int join_leave_ipv4(int sd, int cmd, struct iface *iface, struct in_addr group, int len)
{
#ifdef HAVE_STRUCT_GROUP_REQ /* Prefer RFC 3678 */
struct sockaddr_in sin;
uint32_t addr, addr_max, mask;
char grp[16];

sin.sin_family = AF_INET;
sin.sin_addr = group;
#ifdef HAVE_SOCKADDR_IN_SIN_LEN
sin.sin_len = sizeof(struct sockaddr_in);
#endif
if (len > 0)
mask = 0xFFFFFFFFu << (32 - len);
else
mask = 0xFFFFFFFFu;

if (join_leave(sd, cmd, iface, NULL, &sin)) {
char grp[16];
addr = ntohl(group.s_addr) & mask;
addr_max = addr | ~mask;

inet_ntop(AF_INET, &group, grp, sizeof(grp));
smclog(LOG_ERR, "Failed joining group %s on %s: %s", grp, iface->name, strerror(errno));
while (addr <= addr_max) {
group.s_addr = htonl(addr++);

return 1;
sin.sin_family = AF_INET;
sin.sin_addr = group;
#ifdef HAVE_SOCKADDR_IN_SIN_LEN
sin.sin_len = sizeof(struct sockaddr_in);
#endif
if (join_leave(sd, cmd, iface, NULL, &sin))
goto error;
}

return 0;
error:
inet_ntop(AF_INET, &group, grp, sizeof(grp));
smclog(LOG_ERR, "Failed join/leave group %s on %s: %s", grp, iface->name, strerror(errno));

return 1;
#else
struct ip_mreq mreq;
int opt, retry = 0;
Expand Down Expand Up @@ -245,34 +260,47 @@ static int join_leave_ipv4(int sd, int cmd, struct iface *iface, struct in_addr
#endif
}

static int join_leave_ssm_ipv4(int sd, int cmd, struct iface *iface, struct in_addr source, struct in_addr group)
static int join_leave_ssm_ipv4(int sd, int cmd, struct iface *iface, struct in_addr source, struct in_addr group, int len)
{
#ifdef HAVE_STRUCT_GROUP_REQ /* Prefer RFC 3678 */
struct sockaddr_in sin_source, sin_group;
uint32_t addr, addr_max, mask;
char src[16], grp[16];

sin_source.sin_family = AF_INET;
sin_source.sin_addr = source;
#ifdef HAVE_SOCKADDR_IN_SIN_LEN
sin_source.sin_len = sizeof(struct sockaddr_in);
#endif
sin_group.sin_family = AF_INET;
sin_group.sin_addr = group;
#ifdef HAVE_SOCKADDR_IN_SIN_LEN
sin_group.sin_len = sizeof(struct sockaddr_in);
#endif

if (join_leave(sd, cmd, iface, &sin_source, &sin_group)) {
char src[16], grp[16];
if (len > 0)
mask = 0xFFFFFFFFu << (32 - len);
else
mask = 0xFFFFFFFFu;

inet_ntop(AF_INET, &source, src, sizeof(src));
inet_ntop(AF_INET, &group, grp, sizeof(grp));
smclog(LOG_ERR, "Failed joining (S,G) %s,%s on %s: %s",
src, grp, iface->name, strerror(errno));
addr = ntohl(group.s_addr) & mask;
addr_max = addr | ~mask;

return 1;
while (addr <= addr_max) {
group.s_addr = htonl(addr++);

sin_group.sin_family = AF_INET;
sin_group.sin_addr = group;
#ifdef HAVE_SOCKADDR_IN_SIN_LEN
sin_group.sin_len = sizeof(struct sockaddr_in);
#endif
if (join_leave(sd, cmd, iface, &sin_source, &sin_group))
goto error;
}

return 0;
error:
inet_ntop(AF_INET, &source, src, sizeof(src));
inet_ntop(AF_INET, &group, grp, sizeof(grp));
smclog(LOG_ERR, "Failed joining (S,G) %s,%s on %s: %s",
src, grp, iface->name, strerror(errno));

return 1;
#else
struct ip_mreq_source mreqsrc;
int opt, retry = 0;
Expand Down Expand Up @@ -316,7 +344,7 @@ static int join_leave_ssm_ipv4(int sd, int cmd, struct iface *iface, struct in_a
#endif
}

static int mcgroup_join_leave_ipv4(int sd, int cmd, const char *ifname, struct in_addr group)
static int mcgroup_join_leave_ipv4(int sd, int cmd, const char *ifname, struct in_addr group, int len)
{
struct ifmatch state;
struct in_addr any_src;
Expand All @@ -326,12 +354,12 @@ static int mcgroup_join_leave_ipv4(int sd, int cmd, const char *ifname, struct i
any_src.s_addr = htonl(INADDR_ANY);
iface_match_init(&state);
while ((iface = match_valid_iface(ifname, &state))) {
ret += join_leave_ipv4(sd, cmd, iface, group);
ret += join_leave_ipv4(sd, cmd, iface, group, len);

if (cmd == 'j')
list_add(iface, any_src, group, ret);
list_add(iface, any_src, group, len, ret);
else
list_rem(iface, any_src, group);
list_rem(iface, any_src, group, len);
}

if (!state.match_count)
Expand All @@ -340,24 +368,24 @@ static int mcgroup_join_leave_ipv4(int sd, int cmd, const char *ifname, struct i
return ret;
}

static int mcgroup_join_leave_ssm_ipv4(int sd, int cmd, const char *ifname, struct in_addr source, struct in_addr group)
static int mcgroup_join_leave_ssm_ipv4(int sd, int cmd, const char *ifname, struct in_addr source, struct in_addr group, int len)
{
#ifndef IP_ADD_SOURCE_MEMBERSHIP
smclog(LOG_WARNING, "Source specific join/leave not supported, ignoring source %s", inet_ntoa(source));
return mcgroup_join_leave_ipv4(sd, cmd, ifname, group);
return mcgroup_join_leave_ipv4(sd, cmd, ifname, group, len);
#else
struct iface *iface;
struct ifmatch state;
int ret = 0;

iface_match_init(&state);
while ((iface = match_valid_iface(ifname, &state))) {
ret += join_leave_ssm_ipv4(sd, cmd, iface, source, group);
ret += join_leave_ssm_ipv4(sd, cmd, iface, source, group, len);

if (cmd == 'j')
list_add(iface, source, group, ret);
list_add(iface, source, group, len, ret);
else
list_rem(iface, source, group);
list_rem(iface, source, group, len);
}

if (!state.match_count)
Expand All @@ -375,14 +403,14 @@ static int mcgroup_join_leave_ssm_ipv4(int sd, int cmd, const char *ifname, stru
* returns: - 0 if the function succeeds
* - 1 if parameters are wrong or the join fails
*/
int mcgroup4_join(const char *ifname, struct in_addr source, struct in_addr group)
int mcgroup4_join(const char *ifname, struct in_addr source, struct in_addr group, int len)
{
mcgroup4_init();

if (!source.s_addr)
return mcgroup_join_leave_ipv4(mcgroup4_socket, 'j', ifname, group);
return mcgroup_join_leave_ipv4(mcgroup4_socket, 'j', ifname, group, len);

return mcgroup_join_leave_ssm_ipv4(mcgroup4_socket, 'j', ifname, source, group);
return mcgroup_join_leave_ssm_ipv4(mcgroup4_socket, 'j', ifname, source, group, len);
}

/*
Expand All @@ -391,14 +419,14 @@ int mcgroup4_join(const char *ifname, struct in_addr source, struct in_addr grou
* returns: - 0 if the function succeeds
* - 1 if parameters are wrong or the join fails
*/
int mcgroup4_leave(const char *ifname, struct in_addr source, struct in_addr group)
int mcgroup4_leave(const char *ifname, struct in_addr source, struct in_addr group, int len)
{
mcgroup4_init();

if (!source.s_addr)
return mcgroup_join_leave_ipv4(mcgroup4_socket, 'l', ifname, group);
return mcgroup_join_leave_ipv4(mcgroup4_socket, 'l', ifname, group, len);

return mcgroup_join_leave_ssm_ipv4(mcgroup4_socket, 'l', ifname, source, group);
return mcgroup_join_leave_ssm_ipv4(mcgroup4_socket, 'l', ifname, source, group, len);
}

/*
Expand Down Expand Up @@ -441,9 +469,9 @@ static void mcgroup4_retry(void)

if (!memcmp(&entry->source, &any_src, sizeof(any_src))) {
is_any = 1;
rc = join_leave_ipv4(mcgroup4_socket, 0, iface, entry->group);
rc = join_leave_ipv4(mcgroup4_socket, 0, iface, entry->group, entry->len);
} else
rc = join_leave_ssm_ipv4(mcgroup4_socket, 0, iface, entry->source, entry->group);
rc = join_leave_ssm_ipv4(mcgroup4_socket, 0, iface, entry->source, entry->group, entry->len);
if (rc)
continue;

Expand Down Expand Up @@ -580,7 +608,10 @@ int mcgroup_show(int sd, int detail)
inet_ntop(AF_INET, &g->source, src, sizeof(src));
inet_ntop(AF_INET, &g->group, grp, sizeof(grp));

snprintf(sg, sizeof(sg), "(%s, %s)", src, grp);
if (g->len > 0)
snprintf(sg, sizeof(sg), "(%s, %s/%d)", src, grp, g->len);
else
snprintf(sg, sizeof(sg), "(%s, %s)", src, grp);
snprintf(buf, sizeof(buf), "%-34s %s\n", sg, i->name);

if (ipc_send(sd, buf, strlen(buf)) < 0) {
Expand Down
4 changes: 2 additions & 2 deletions src/mcgroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

int mcgroup_refresh (void);

int mcgroup4_join (const char *ifname, struct in_addr source, struct in_addr group);
int mcgroup4_leave (const char *ifname, struct in_addr source, struct in_addr group);
int mcgroup4_join (const char *ifname, struct in_addr source, struct in_addr group, int len);
int mcgroup4_leave (const char *ifname, struct in_addr source, struct in_addr group, int len);
void mcgroup4_disable (void);

int mcgroup6_join (const char *ifname, struct in6_addr group);
Expand Down
Loading

0 comments on commit 972724d

Please sign in to comment.