mirror of
https://git.FreeBSD.org/src.git
synced 2024-11-23 07:31:31 +00:00
Merge IPv6-capable mtest(8) from MLDv2 branch.
This commit is contained in:
parent
82fcb0f192
commit
7369700e92
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=191651
@ -1,6 +1,18 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
PROG= mtest
|
||||
MAN= mtest.8
|
||||
|
||||
BINMODE= 555
|
||||
WARNS?= 2
|
||||
|
||||
# XXX This assumes INET support in the base system.
|
||||
CFLAGS+=-DINET
|
||||
|
||||
.if ${MK_INET6_SUPPORT} != "no"
|
||||
CFLAGS+=-DINET6
|
||||
.endif
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
@ -26,111 +26,116 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd March 3, 2009
|
||||
.Dd April 29, 2009
|
||||
.Os
|
||||
.Dt MTEST 8
|
||||
.Sh NAME
|
||||
.Nm mtest
|
||||
.Nd test multicast membership socket operations and ioctls
|
||||
.Nd test multicast socket operations
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility
|
||||
is a small program for testing the multicast membership socket operations
|
||||
and ioctls.
|
||||
It accepts the following commands, interactively:
|
||||
is a small program for testing multicast socket operations.
|
||||
.Pp
|
||||
It accepts the following commands, interactively, or as part of a scripted
|
||||
input file (useful for automated testing):
|
||||
.Bl -tag -width "a ifname e.e.e.e e.e.e.e" -compact -offset indent
|
||||
.It Ic j Ar g.g.g.g Ar i.i.i.i Op Ar s.s.s.s
|
||||
Join the IP group address
|
||||
.Ar g.g.g.g
|
||||
on the interface with address
|
||||
.Ar i.i.i.i .
|
||||
.Pp
|
||||
If an optional source
|
||||
.Ar s.s.s.s
|
||||
is specified, a source-specific join will be performed;
|
||||
if
|
||||
.Nm
|
||||
is already a member of the group, the source
|
||||
will be added to its filter list.
|
||||
.Pp
|
||||
.Ar i.i.i.i
|
||||
may be specified as 0.0.0.0 to use the default interface,
|
||||
although this is legacy behaviour and is not recommended,
|
||||
as group memberships are keyed to each individual link.
|
||||
.It Ic l Ar g.g.g.g Ar i.i.i.i Op Ar s.s.s.s
|
||||
Leave the IP group address
|
||||
.Ar g.g.g.g
|
||||
on the interface with address
|
||||
.Ar i.i.i.i .
|
||||
If a source
|
||||
.Ar s.s.s.s
|
||||
is specified, only that source will be left.
|
||||
.It Ic a Ar ifname Ar e.e.e.e.e.e
|
||||
Join the Ethernet group address
|
||||
.Ar e.e.e.e.e.e
|
||||
.\"
|
||||
.It Ic a Ar ifname Ar mac-addr
|
||||
Join the link-layer group address
|
||||
.Ar mac-addr
|
||||
on interface
|
||||
.Ar ifname .
|
||||
.It Ic d Ar ifname Ar e.e.e.e.e.e
|
||||
Leave the Ethernet group address
|
||||
.Ar e.e.e.e.e.e
|
||||
The group address should be in IEEE 802 MAC format,
|
||||
delimited by colon (':') characters.
|
||||
.It Ic d Ar ifname Ar mac-addr
|
||||
Leave the link-layer group address
|
||||
.Ar mac-addr
|
||||
on interface
|
||||
.Ar ifname .
|
||||
.It Ic m Ar ifname Ar 1/0
|
||||
Set or reset ALLMULTI mode on interface
|
||||
.Ar ifname .
|
||||
This option is deprecated and is now a no-op.
|
||||
.It Ic p Ar ifname Ar 1/0
|
||||
Set or reset promiscuous mode on interface
|
||||
.Ar ifname .
|
||||
.Pp
|
||||
.It Ic j Ar mcast-addr Ar ifname Op Ar source-addr
|
||||
Join the multicast address
|
||||
.Ar mcast-addr
|
||||
on the interface with name
|
||||
.Ar ifname .
|
||||
.Pp
|
||||
If an optional source
|
||||
.Ar source-addr
|
||||
is specified, a source-specific join will be performed;
|
||||
if
|
||||
.Nm
|
||||
is already joined to the multicast address, the source
|
||||
will be added to its filter list.
|
||||
.Pp
|
||||
.It Ic l Ar mcast-addr Ar ifname Op Ar source-addr
|
||||
Leave the multicast address
|
||||
.Ar mcast-addr
|
||||
on the interface with address
|
||||
.Ar ifname .
|
||||
If a source
|
||||
.Ar source-addr
|
||||
is specified, only that source will be left.
|
||||
.\"
|
||||
.It Ic i Ar g.g.g.g Ar i.i.i.i Ar n Ar x.x.x.x ...
|
||||
Set the socket with group membership of
|
||||
.Ar g.g.g.g
|
||||
on IPv4 address
|
||||
.Ar i.i.i.i
|
||||
.It Ic i Ar mcast-addr Ar ifname Ar n Ar source-addr ...
|
||||
Set the socket with membership of
|
||||
.Ar mcast-addr
|
||||
on interface
|
||||
.Ar ifname
|
||||
to include filter mode, and add
|
||||
.Ar n
|
||||
sources beginning with
|
||||
.Ar x.x.x.x
|
||||
.Ar source-addr
|
||||
to the inclusion filter list.
|
||||
.\"
|
||||
.It Ic e Ar g.g.g.g Ar i.i.i.i Ar n Ar x.x.x.x ...
|
||||
Set the socket with group membership of
|
||||
.Ar g.g.g.g
|
||||
on IPv4 address
|
||||
.Ar i.i.i.i
|
||||
.It Ic e Ar mcast-addr Ar ifname Ar n Ar source-addr ...
|
||||
Set the socket with membership of
|
||||
.Ar mcast-addr
|
||||
on interface
|
||||
.Ar ifname
|
||||
to exclude filter mode, and add
|
||||
.Ar n
|
||||
sources beginning with
|
||||
.Ar x.x.x.x
|
||||
.Ar source-addr
|
||||
to the exclusion filter list.
|
||||
.\"
|
||||
.It Ic t Ar g.g.g.g Ar i.i.i.i Ar s.s.s.s
|
||||
Set the socket with group membership of
|
||||
.Ar g.g.g.g
|
||||
on IPv4 address
|
||||
.Ar i.i.i.i
|
||||
.It Ic t Ar mcast-addr Ar ifname Ar source-addr
|
||||
Set the socket with membership of
|
||||
.Ar mcast-addr
|
||||
on interface
|
||||
.Ar ifname
|
||||
to block traffic from source
|
||||
.Ar s.s.s.s .
|
||||
.Ar source-addr .
|
||||
.\"
|
||||
.It Ic b Ar g.g.g.g Ar i.i.i.i Ar s.s.s.s
|
||||
Set the socket with group membership of
|
||||
.Ar g.g.g.g
|
||||
on IPv4 address
|
||||
.Ar i.i.i.i
|
||||
.It Ic b Ar mcast-addr Ar ifname Ar source-addr
|
||||
Set the socket with membership of
|
||||
.Ar mcast-addr
|
||||
on interface
|
||||
.Ar ifname
|
||||
to allow traffic from source
|
||||
.Ar s.s.s.s .
|
||||
.Ar source-addr .
|
||||
.\"
|
||||
.It Ic g Ar g.g.g.g Ar i.i.i.i Ar n
|
||||
.Pp
|
||||
.It Ic g Ar mcast-addr Ar ifname Ar n
|
||||
Print
|
||||
.Ar n
|
||||
source filter entries for group
|
||||
.An g.g.g.g
|
||||
on IPv4 address
|
||||
.An i.i.i.i .
|
||||
source filter entries for
|
||||
.An mcast-addr
|
||||
on interface
|
||||
.An ifname .
|
||||
.\"
|
||||
.Pp
|
||||
.It Ic f Ar filename
|
||||
Read commands from the file
|
||||
.Ar filename .
|
||||
@ -143,6 +148,18 @@ List legal commands.
|
||||
.It Ic q
|
||||
Quit the program.
|
||||
.El
|
||||
.Sh IMPLEMENTATION NOTES
|
||||
For each command implemented by
|
||||
.Nm ,
|
||||
the address family of each argument must be identical; it is not possible
|
||||
to mix IPv4 multicast memberships with IPv6, for example.
|
||||
.Pp
|
||||
To support IPv6, all commands have now changed to accept an interface
|
||||
name rather than an interface address.
|
||||
For IPv4, the program will perform
|
||||
a lookup of the primary IP address based on the interface name.
|
||||
This may fail if no primary IP address is assigned.
|
||||
.Pp
|
||||
.Sh SEE ALSO
|
||||
.Rs
|
||||
.%A D. Thaler
|
||||
|
@ -29,13 +29,18 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Diagnostic and test utility for IPv4 multicast sockets.
|
||||
* Diagnostic and test utility for multicast sockets.
|
||||
* XXX: This file currently assumes INET support in the base system.
|
||||
* TODO: Support embedded KAME Scope ID in IPv6 group addresses.
|
||||
* TODO: Use IPv4 link-local address when source address selection
|
||||
* is implemented; use MCAST_JOIN_SOURCE for IPv4.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
@ -44,42 +49,169 @@ __FBSDID("$FreeBSD$");
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/ethernet.h>
|
||||
#ifdef INET
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip_var.h>
|
||||
#endif
|
||||
#ifdef INET6
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip6.h>
|
||||
#endif
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void process_file(char *, int);
|
||||
static void process_cmd(char*, int, FILE *fp);
|
||||
static void usage(void);
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
union sockunion {
|
||||
struct sockaddr_storage ss;
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_dl sdl;
|
||||
#ifdef INET
|
||||
struct sockaddr_in sin;
|
||||
#endif
|
||||
#ifdef INET6
|
||||
struct sockaddr_in6 sin6;
|
||||
#endif
|
||||
};
|
||||
typedef union sockunion sockunion_t;
|
||||
|
||||
union mrequnion {
|
||||
#ifdef INET
|
||||
struct ip_mreq mr;
|
||||
struct ip_mreq_source mrs;
|
||||
#endif
|
||||
#ifdef INET6
|
||||
struct ipv6_mreq mr6;
|
||||
struct group_source_req gr;
|
||||
#endif
|
||||
};
|
||||
typedef union mrequnion mrequnion_t;
|
||||
|
||||
#define MAX_ADDRS 20
|
||||
#define STR_SIZE 20
|
||||
#define LINE_LENGTH 80
|
||||
|
||||
#ifdef INET
|
||||
static int __ifindex_to_primary_ip(const uint32_t, struct in_addr *);
|
||||
#endif
|
||||
static uint32_t parse_cmd_args(sockunion_t *, sockunion_t *,
|
||||
const char *, const char *, const char *);
|
||||
static void process_file(char *, int, int);
|
||||
static void process_cmd(char*, int, int, FILE *);
|
||||
static int su_cmp(const void *, const void *);
|
||||
static void usage(void);
|
||||
|
||||
/*
|
||||
* Ordering predicate for qsort().
|
||||
*/
|
||||
static int
|
||||
inaddr_cmp(const void *a, const void *b)
|
||||
su_cmp(const void *a, const void *b)
|
||||
{
|
||||
return ((int)((const struct in_addr *)a)->s_addr -
|
||||
((const struct in_addr *)b)->s_addr);
|
||||
const sockunion_t *sua = (const sockunion_t *)a;
|
||||
const sockunion_t *sub = (const sockunion_t *)b;
|
||||
|
||||
assert(sua->sa.sa_family == sub->sa.sa_family);
|
||||
|
||||
switch (sua->sa.sa_family) {
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
return ((int)(sua->sin.sin_addr.s_addr -
|
||||
sub->sin.sin_addr.s_addr));
|
||||
break;
|
||||
#endif
|
||||
#ifdef INET6
|
||||
case AF_INET6:
|
||||
return (memcmp(&sua->sin6.sin6_addr, &sub->sin6.sin6_addr,
|
||||
sizeof(struct in6_addr)));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
assert(sua->sa.sa_len == sub->sa.sa_len);
|
||||
return (memcmp(sua, sub, sua->sa.sa_len));
|
||||
}
|
||||
|
||||
#ifdef INET
|
||||
/*
|
||||
* Internal: Map an interface index to primary IPv4 address.
|
||||
* This is somewhat inefficient. This is a useful enough operation
|
||||
* that it probably belongs in the C library.
|
||||
* Return zero if found, -1 on error, 1 on not found.
|
||||
*/
|
||||
static int
|
||||
__ifindex_to_primary_ip(const uint32_t ifindex, struct in_addr *pina)
|
||||
{
|
||||
char ifname[IFNAMSIZ];
|
||||
struct ifaddrs *ifa;
|
||||
struct ifaddrs *ifaddrs;
|
||||
sockunion_t *psu;
|
||||
int retval;
|
||||
|
||||
assert(ifindex != 0);
|
||||
|
||||
retval = -1;
|
||||
if (if_indextoname(ifindex, ifname) == NULL)
|
||||
return (retval);
|
||||
if (getifaddrs(&ifaddrs) < 0)
|
||||
return (retval);
|
||||
|
||||
/*
|
||||
* Find the ifaddr entry corresponding to the interface name,
|
||||
* and return the first matching IPv4 address.
|
||||
*/
|
||||
retval = 1;
|
||||
for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (strcmp(ifa->ifa_name, ifname) != 0)
|
||||
continue;
|
||||
psu = (sockunion_t *)ifa->ifa_addr;
|
||||
if (psu && psu->sa.sa_family == AF_INET) {
|
||||
retval = 0;
|
||||
memcpy(pina, &psu->sin.sin_addr,
|
||||
sizeof(struct in_addr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (retval != 0)
|
||||
errno = EADDRNOTAVAIL; /* XXX */
|
||||
|
||||
freeifaddrs(ifaddrs);
|
||||
return (retval);
|
||||
}
|
||||
#endif /* INET */
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char line[LINE_LENGTH];
|
||||
char *p;
|
||||
int i, s;
|
||||
int i, s, s6;
|
||||
|
||||
s = -1;
|
||||
s6 = -1;
|
||||
#ifdef INET
|
||||
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (s == -1)
|
||||
err(1, "can't open socket");
|
||||
err(1, "can't open IPv4 socket");
|
||||
#endif
|
||||
#ifdef INET6
|
||||
s6 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (s6 == -1)
|
||||
err(1, "can't open IPv6 socket");
|
||||
#endif
|
||||
|
||||
if (argc < 2) {
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
@ -89,28 +221,33 @@ main(int argc, char **argv)
|
||||
do {
|
||||
if (fgets(line, sizeof(line), stdin) != NULL) {
|
||||
if (line[0] != 'f')
|
||||
process_cmd(line, s, stdin);
|
||||
process_cmd(line, s, s6, stdin);
|
||||
else {
|
||||
/* Get the filename */
|
||||
for (i = 1; isblank(line[i]); i++);
|
||||
if ((p = (char*)strchr(line, '\n'))
|
||||
!= NULL)
|
||||
*p = '\0';
|
||||
process_file(&line[i], s);
|
||||
process_file(&line[i], s, s6);
|
||||
}
|
||||
}
|
||||
} while (!feof(stdin));
|
||||
} else {
|
||||
for (i = 1; i < argc; i++) {
|
||||
process_file(argv[i], s);
|
||||
process_file(argv[i], s, s6);
|
||||
}
|
||||
}
|
||||
|
||||
if (s != -1)
|
||||
close(s);
|
||||
if (s6 != -1)
|
||||
close(s6);
|
||||
|
||||
exit (0);
|
||||
}
|
||||
|
||||
static void
|
||||
process_file(char *fname, int s)
|
||||
process_file(char *fname, int s, int s6)
|
||||
{
|
||||
char line[80];
|
||||
FILE *fp;
|
||||
@ -128,25 +265,130 @@ process_file(char *fname, int s)
|
||||
while (isblank(*lineptr))
|
||||
lineptr++;
|
||||
if (*lineptr != '#' && *lineptr != '\n')
|
||||
process_cmd(lineptr, s, fp);
|
||||
process_cmd(lineptr, s, s6, fp);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse join/leave/allow/block arguments, given:
|
||||
* str1: group (as AF_INET or AF_INET6 printable)
|
||||
* str2: ifname
|
||||
* str3: optional source address (may be NULL).
|
||||
* This argument must have the same parsed address family as str1.
|
||||
* Return the ifindex of ifname, or 0 if any parse element failed.
|
||||
*/
|
||||
static uint32_t
|
||||
parse_cmd_args(sockunion_t *psu, sockunion_t *psu2,
|
||||
const char *str1, const char *str2, const char *str3)
|
||||
{
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res;
|
||||
uint32_t ifindex;
|
||||
int af, error;
|
||||
|
||||
assert(psu != NULL);
|
||||
assert(str1 != NULL);
|
||||
assert(str2 != NULL);
|
||||
|
||||
af = AF_UNSPEC;
|
||||
|
||||
ifindex = if_nametoindex(str2);
|
||||
if (ifindex == 0)
|
||||
return (0);
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
memset(psu, 0, sizeof(sockunion_t));
|
||||
psu->sa.sa_family = AF_UNSPEC;
|
||||
|
||||
error = getaddrinfo(str1, "0", &hints, &res);
|
||||
if (error) {
|
||||
warnx("getaddrinfo: %s", gai_strerror(error));
|
||||
return (0);
|
||||
}
|
||||
assert(res != NULL);
|
||||
af = res->ai_family;
|
||||
memcpy(psu, res->ai_addr, res->ai_addrlen);
|
||||
freeaddrinfo(res);
|
||||
|
||||
/* sscanf() may pass the empty string. */
|
||||
if (psu2 != NULL && str3 != NULL && *str3 != '\0') {
|
||||
memset(psu2, 0, sizeof(sockunion_t));
|
||||
psu2->sa.sa_family = AF_UNSPEC;
|
||||
|
||||
/* look for following address family; str3 is *optional*. */
|
||||
hints.ai_family = af;
|
||||
error = getaddrinfo(str3, "0", &hints, &res);
|
||||
if (error) {
|
||||
warnx("getaddrinfo: %s", gai_strerror(error));
|
||||
ifindex = 0;
|
||||
} else {
|
||||
if (af != res->ai_family) {
|
||||
errno = EINVAL; /* XXX */
|
||||
ifindex = 0;
|
||||
}
|
||||
memcpy(psu2, res->ai_addr, res->ai_addrlen);
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
}
|
||||
|
||||
return (ifindex);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
af2sock(const int af, int s, int s6)
|
||||
{
|
||||
|
||||
#ifdef INET
|
||||
if (af == AF_INET)
|
||||
return (s);
|
||||
#endif
|
||||
#ifdef INET6
|
||||
if (af == AF_INET6)
|
||||
return (s6);
|
||||
#endif
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
af2socklen(const int af)
|
||||
{
|
||||
|
||||
#ifdef INET
|
||||
if (af == AF_INET)
|
||||
return (sizeof(struct sockaddr_in));
|
||||
#endif
|
||||
#ifdef INET6
|
||||
if (af == AF_INET6)
|
||||
return (sizeof(struct sockaddr_in6));
|
||||
#endif
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static void
|
||||
process_cmd(char *cmd, int s, FILE *fp __unused)
|
||||
process_cmd(char *cmd, int s, int s6 __unused, FILE *fp __unused)
|
||||
{
|
||||
char str1[STR_SIZE];
|
||||
char str2[STR_SIZE];
|
||||
char str3[STR_SIZE];
|
||||
struct in_addr sources[MAX_ADDRS];
|
||||
mrequnion_t mr;
|
||||
sockunion_t su, su2;
|
||||
struct ifreq ifr;
|
||||
struct ip_mreq imr;
|
||||
struct ip_mreq_source imrs;
|
||||
char *line;
|
||||
uint32_t fmode;
|
||||
int i, n, opt, f, flags;
|
||||
char *toptname;
|
||||
void *optval;
|
||||
uint32_t fmode, ifindex;
|
||||
socklen_t optlen;
|
||||
int af, error, f, flags, i, level, n, optname;
|
||||
|
||||
af = AF_UNSPEC;
|
||||
su.sa.sa_family = AF_UNSPEC;
|
||||
su2.sa.sa_family = AF_UNSPEC;
|
||||
|
||||
line = cmd;
|
||||
while (isblank(*++line))
|
||||
@ -173,52 +415,327 @@ process_cmd(char *cmd, int s, FILE *fp __unused)
|
||||
case 'j':
|
||||
case 'l':
|
||||
str3[0] = '\0';
|
||||
toptname = "";
|
||||
sscanf(line, "%s %s %s", str1, str2, str3);
|
||||
if ((imrs.imr_sourceaddr.s_addr = inet_addr(str3)) !=
|
||||
INADDR_NONE) {
|
||||
/*
|
||||
* inclusive mode join with source, possibly
|
||||
* on existing membership.
|
||||
*/
|
||||
if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) ==
|
||||
INADDR_NONE) ||
|
||||
((imrs.imr_interface.s_addr = inet_addr(str2)) ==
|
||||
INADDR_NONE)) {
|
||||
ifindex = parse_cmd_args(&su, &su2, str1, str2, str3);
|
||||
if (ifindex == 0) {
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
af = su.sa.sa_family;
|
||||
#ifdef INET
|
||||
if (af == AF_INET) {
|
||||
struct in_addr ina;
|
||||
|
||||
error = __ifindex_to_primary_ip(ifindex, &ina);
|
||||
if (error != 0) {
|
||||
warn("primary_ip_lookup %s", str2);
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
opt = (*cmd == 'j') ? IP_ADD_SOURCE_MEMBERSHIP :
|
||||
IP_DROP_SOURCE_MEMBERSHIP;
|
||||
if (setsockopt( s, IPPROTO_IP, opt, &imrs,
|
||||
sizeof(imrs)) != 0) {
|
||||
warn("setsockopt %s", (*cmd == 'j') ?
|
||||
level = IPPROTO_IP;
|
||||
|
||||
if (su2.sa.sa_family != AF_UNSPEC) {
|
||||
mr.mrs.imr_multiaddr = su.sin.sin_addr;
|
||||
mr.mrs.imr_sourceaddr = su2.sin.sin_addr;
|
||||
mr.mrs.imr_interface = ina;
|
||||
optname = (*cmd == 'j') ?
|
||||
IP_ADD_SOURCE_MEMBERSHIP :
|
||||
IP_DROP_SOURCE_MEMBERSHIP;
|
||||
toptname = (*cmd == 'j') ?
|
||||
"IP_ADD_SOURCE_MEMBERSHIP" :
|
||||
"IP_DROP_SOURCE_MEMBERSHIP");
|
||||
"IP_DROP_SOURCE_MEMBERSHIP";
|
||||
optval = (void *)&mr.mrs;
|
||||
optlen = sizeof(mr.mrs);
|
||||
} else {
|
||||
printf("ok\n");
|
||||
mr.mr.imr_multiaddr = su.sin.sin_addr;
|
||||
mr.mr.imr_interface = ina;
|
||||
optname = (*cmd == 'j') ?
|
||||
IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
|
||||
toptname = (*cmd == 'j') ?
|
||||
"IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP";
|
||||
optval = (void *)&mr.mr;
|
||||
optlen = sizeof(mr.mr);
|
||||
}
|
||||
} else {
|
||||
/* exclusive mode join w/o source. */
|
||||
if (((imr.imr_multiaddr.s_addr = inet_addr(str1)) ==
|
||||
INADDR_NONE) ||
|
||||
((imr.imr_interface.s_addr = inet_addr(str2)) ==
|
||||
INADDR_NONE)) {
|
||||
printf("-1\n");
|
||||
if (setsockopt(s, level, optname, optval,
|
||||
optlen) == 0) {
|
||||
printf("ok\n");
|
||||
break;
|
||||
}
|
||||
opt = (*cmd == 'j') ? IP_ADD_MEMBERSHIP :
|
||||
IP_DROP_MEMBERSHIP;
|
||||
if (setsockopt( s, IPPROTO_IP, opt, &imr,
|
||||
sizeof(imr)) != 0) {
|
||||
warn("setsockopt %s", (*cmd == 'j') ?
|
||||
"IP_ADD_MEMBERSHIP" :
|
||||
"IP_DROP_MEMBERSHIP");
|
||||
} else {
|
||||
printf("ok\n");
|
||||
warn("setsockopt %s", toptname);
|
||||
}
|
||||
}
|
||||
#ifdef INET6
|
||||
else
|
||||
#endif /* INET with INET6 */
|
||||
#endif /* INET */
|
||||
#ifdef INET6
|
||||
if (af == AF_INET6) {
|
||||
level = IPPROTO_IPV6;
|
||||
if (su2.sa.sa_family != AF_UNSPEC) {
|
||||
mr.gr.gsr_interface = ifindex;
|
||||
mr.gr.gsr_group = su.ss;
|
||||
mr.gr.gsr_source = su2.ss;
|
||||
optname = (*cmd == 'j') ?
|
||||
MCAST_JOIN_SOURCE_GROUP:
|
||||
MCAST_LEAVE_SOURCE_GROUP;
|
||||
toptname = (*cmd == 'j') ?
|
||||
"MCAST_JOIN_SOURCE_GROUP":
|
||||
"MCAST_LEAVE_SOURCE_GROUP";
|
||||
optval = (void *)&mr.gr;
|
||||
optlen = sizeof(mr.gr);
|
||||
} else {
|
||||
mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr;
|
||||
mr.mr6.ipv6mr_interface = ifindex;
|
||||
optname = (*cmd == 'j') ?
|
||||
IPV6_JOIN_GROUP :
|
||||
IPV6_LEAVE_GROUP;
|
||||
toptname = (*cmd == 'j') ?
|
||||
"IPV6_JOIN_GROUP" :
|
||||
"IPV6_LEAVE_GROUP";
|
||||
optval = (void *)&mr.mr6;
|
||||
optlen = sizeof(mr.mr6);
|
||||
}
|
||||
if (setsockopt(s6, level, optname, optval,
|
||||
optlen) == 0) {
|
||||
printf("ok\n");
|
||||
break;
|
||||
} else {
|
||||
warn("setsockopt %s", toptname);
|
||||
}
|
||||
}
|
||||
#endif /* INET6 */
|
||||
/* FALLTHROUGH */
|
||||
printf("-1\n");
|
||||
break;
|
||||
|
||||
/*
|
||||
* Set the socket to include or exclude filter mode, and
|
||||
* add some sources to the filterlist, using the full-state API.
|
||||
*/
|
||||
case 'i':
|
||||
case 'e': {
|
||||
sockunion_t sources[MAX_ADDRS];
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res;
|
||||
char *cp;
|
||||
int af1;
|
||||
|
||||
n = 0;
|
||||
fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE;
|
||||
if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
|
||||
ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
|
||||
if (ifindex == 0 || n < 0 || n > MAX_ADDRS) {
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
af = su.sa.sa_family;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
hints.ai_family = af;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
sockunion_t *psu = (sockunion_t *)&sources[i];
|
||||
/*
|
||||
* Trim trailing whitespace, as getaddrinfo()
|
||||
* can't cope with it.
|
||||
*/
|
||||
fgets(str1, sizeof(str1), fp);
|
||||
cp = strchr(str1, '\n');
|
||||
if (cp != NULL)
|
||||
*cp = '\0';
|
||||
|
||||
res = NULL;
|
||||
error = getaddrinfo(str1, "0", &hints, &res);
|
||||
if (error)
|
||||
break;
|
||||
assert(res != NULL);
|
||||
|
||||
memset(psu, 0, sizeof(sockunion_t));
|
||||
af1 = res->ai_family;
|
||||
if (af1 == af)
|
||||
memcpy(psu, res->ai_addr, res->ai_addrlen);
|
||||
freeaddrinfo(res);
|
||||
if (af1 != af)
|
||||
break;
|
||||
}
|
||||
if (i < n) {
|
||||
if (error)
|
||||
warnx("getaddrinfo: %s", gai_strerror(error));
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
if (setsourcefilter(af2sock(af, s, s6), ifindex,
|
||||
&su.sa, su.sa.sa_len, fmode, n, &sources[0].ss) != 0)
|
||||
warn("setsourcefilter");
|
||||
else
|
||||
printf("ok\n");
|
||||
} break;
|
||||
|
||||
/*
|
||||
* Allow or block traffic from a source, using the
|
||||
* delta based api.
|
||||
*/
|
||||
case 't':
|
||||
case 'b': {
|
||||
str3[0] = '\0';
|
||||
toptname = "";
|
||||
sscanf(line, "%s %s %s", str1, str2, str3);
|
||||
ifindex = parse_cmd_args(&su, &su2, str1, str2, str3);
|
||||
if (ifindex == 0 || su2.sa.sa_family == AF_UNSPEC) {
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
af = su.sa.sa_family;
|
||||
|
||||
/* First determine our current filter mode. */
|
||||
n = 0;
|
||||
if (getsourcefilter(af2sock(af, s, s6), ifindex,
|
||||
&su.sa, su.sa.sa_len, &fmode, &n, NULL) != 0) {
|
||||
warn("getsourcefilter");
|
||||
break;
|
||||
}
|
||||
#ifdef INET
|
||||
if (af == AF_INET) {
|
||||
struct in_addr ina;
|
||||
|
||||
error = __ifindex_to_primary_ip(ifindex, &ina);
|
||||
if (error != 0) {
|
||||
warn("primary_ip_lookup %s", str2);
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
level = IPPROTO_IP;
|
||||
optval = (void *)&mr.mrs;
|
||||
optlen = sizeof(mr.mrs);
|
||||
mr.mrs.imr_multiaddr = su.sin.sin_addr;
|
||||
mr.mrs.imr_sourceaddr = su2.sin.sin_addr;
|
||||
mr.mrs.imr_interface = ina;
|
||||
if (fmode == MCAST_EXCLUDE) {
|
||||
/* Any-source mode socket membership. */
|
||||
optname = (*cmd == 't') ?
|
||||
IP_UNBLOCK_SOURCE :
|
||||
IP_BLOCK_SOURCE;
|
||||
toptname = (*cmd == 't') ?
|
||||
"IP_UNBLOCK_SOURCE" :
|
||||
"IP_BLOCK_SOURCE";
|
||||
} else {
|
||||
/* Source-specific mode socket membership. */
|
||||
optname = (*cmd == 't') ?
|
||||
IP_ADD_SOURCE_MEMBERSHIP :
|
||||
IP_DROP_SOURCE_MEMBERSHIP;
|
||||
toptname = (*cmd == 't') ?
|
||||
"IP_ADD_SOURCE_MEMBERSHIP" :
|
||||
"IP_DROP_SOURCE_MEMBERSHIP";
|
||||
}
|
||||
if (setsockopt(s, level, optname, optval,
|
||||
optlen) == 0) {
|
||||
printf("ok\n");
|
||||
break;
|
||||
} else {
|
||||
warn("setsockopt %s", toptname);
|
||||
}
|
||||
}
|
||||
#ifdef INET6
|
||||
else
|
||||
#endif /* INET with INET6 */
|
||||
#endif /* INET */
|
||||
#ifdef INET6
|
||||
if (af == AF_INET6) {
|
||||
level = IPPROTO_IPV6;
|
||||
mr.gr.gsr_interface = ifindex;
|
||||
mr.gr.gsr_group = su.ss;
|
||||
mr.gr.gsr_source = su2.ss;
|
||||
if (fmode == MCAST_EXCLUDE) {
|
||||
/* Any-source mode socket membership. */
|
||||
optname = (*cmd == 't') ?
|
||||
MCAST_UNBLOCK_SOURCE :
|
||||
MCAST_BLOCK_SOURCE;
|
||||
toptname = (*cmd == 't') ?
|
||||
"MCAST_UNBLOCK_SOURCE" :
|
||||
"MCAST_BLOCK_SOURCE";
|
||||
} else {
|
||||
/* Source-specific mode socket membership. */
|
||||
optname = (*cmd == 't') ?
|
||||
MCAST_JOIN_SOURCE_GROUP :
|
||||
MCAST_LEAVE_SOURCE_GROUP;
|
||||
toptname = (*cmd == 't') ?
|
||||
"MCAST_JOIN_SOURCE_GROUP":
|
||||
"MCAST_LEAVE_SOURCE_GROUP";
|
||||
}
|
||||
optval = (void *)&mr.gr;
|
||||
optlen = sizeof(mr.gr);
|
||||
if (setsockopt(s6, level, optname, optval,
|
||||
optlen) == 0) {
|
||||
printf("ok\n");
|
||||
break;
|
||||
} else {
|
||||
warn("setsockopt %s", toptname);
|
||||
}
|
||||
}
|
||||
#endif /* INET6 */
|
||||
/* FALLTHROUGH */
|
||||
printf("-1\n");
|
||||
} break;
|
||||
|
||||
case 'g': {
|
||||
sockunion_t sources[MAX_ADDRS];
|
||||
char addrbuf[NI_MAXHOST];
|
||||
int nreqsrc, nsrc;
|
||||
|
||||
if ((sscanf(line, "%s %s %d", str1, str2, &nreqsrc)) != 3) {
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
|
||||
if (ifindex == 0 || (n < 0 || n > MAX_ADDRS)) {
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
|
||||
af = su.sa.sa_family;
|
||||
nsrc = nreqsrc;
|
||||
if (getsourcefilter(af2sock(af, s, s6), ifindex, &su.sa,
|
||||
su.sa.sa_len, &fmode, &nsrc, &sources[0].ss) != 0) {
|
||||
warn("getsourcefilter");
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" :
|
||||
"exclude");
|
||||
printf("%d\n", nsrc);
|
||||
|
||||
nsrc = MIN(nreqsrc, nsrc);
|
||||
fprintf(stderr, "hexdump of sources:\n");
|
||||
uint8_t *bp = (uint8_t *)&sources[0];
|
||||
for (i = 0; i < (nsrc * sizeof(sources[0])); i++) {
|
||||
fprintf(stderr, "%02x", bp[i]);
|
||||
}
|
||||
fprintf(stderr, "\nend hexdump\n");
|
||||
|
||||
qsort(sources, nsrc, af2socklen(af), su_cmp);
|
||||
for (i = 0; i < nsrc; i++) {
|
||||
sockunion_t *psu = (sockunion_t *)&sources[i];
|
||||
addrbuf[0] = '\0';
|
||||
error = getnameinfo(&psu->sa, psu->sa.sa_len,
|
||||
addrbuf, sizeof(addrbuf), NULL, 0,
|
||||
NI_NUMERICHOST);
|
||||
if (error)
|
||||
warnx("getnameinfo: %s", gai_strerror(error));
|
||||
else
|
||||
printf("%s\n", addrbuf);
|
||||
}
|
||||
printf("ok\n");
|
||||
} break;
|
||||
|
||||
/* link-layer stuff follows. */
|
||||
|
||||
case 'a':
|
||||
case 'd': {
|
||||
struct sockaddr_dl *dlp;
|
||||
@ -244,16 +761,19 @@ process_cmd(char *cmd, int s, FILE *fp __unused)
|
||||
strlcpy(ifr.ifr_name, str1, IF_NAMESIZE);
|
||||
memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN);
|
||||
if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI,
|
||||
&ifr) == -1)
|
||||
&ifr) == -1) {
|
||||
warn("ioctl SIOCADDMULTI/SIOCDELMULTI");
|
||||
else
|
||||
printf("-1\n");
|
||||
} else
|
||||
printf("ok\n");
|
||||
break;
|
||||
}
|
||||
|
||||
case 'm':
|
||||
printf("warning: IFF_ALLMULTI cannot be set from userland "
|
||||
fprintf(stderr,
|
||||
"warning: IFF_ALLMULTI cannot be set from userland "
|
||||
"in FreeBSD; command ignored.\n");
|
||||
printf("-1\n");
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
@ -266,11 +786,10 @@ process_cmd(char *cmd, int s, FILE *fp __unused)
|
||||
break;
|
||||
}
|
||||
flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
|
||||
opt = IFF_PPROMISC;
|
||||
if (f == 0) {
|
||||
flags &= ~opt;
|
||||
flags &= ~IFF_PPROMISC;
|
||||
} else {
|
||||
flags |= opt;
|
||||
flags |= IFF_PPROMISC;
|
||||
}
|
||||
ifr.ifr_flags = flags & 0xffff;
|
||||
ifr.ifr_flagshigh = flags >> 16;
|
||||
@ -280,106 +799,6 @@ process_cmd(char *cmd, int s, FILE *fp __unused)
|
||||
printf( "changed to 0x%08x\n", flags );
|
||||
break;
|
||||
|
||||
/*
|
||||
* Set the socket to include or exclude filter mode, and
|
||||
* add some sources to the filterlist, using the full-state,
|
||||
* or advanced api.
|
||||
*/
|
||||
case 'i':
|
||||
case 'e':
|
||||
n = 0;
|
||||
fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE;
|
||||
if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
/* recycle imrs struct for convenience */
|
||||
if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) ==
|
||||
INADDR_NONE) ||
|
||||
((imrs.imr_interface.s_addr = inet_addr(str2)) ==
|
||||
INADDR_NONE) || (n < 0 || n > MAX_ADDRS)) {
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
fgets(str1, sizeof(str1), fp);
|
||||
if ((sources[i].s_addr = inet_addr(str1)) ==
|
||||
INADDR_NONE) {
|
||||
printf("-1\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (setipv4sourcefilter(s, imrs.imr_interface,
|
||||
imrs.imr_multiaddr, fmode, n, sources) != 0)
|
||||
warn("getipv4sourcefilter");
|
||||
else
|
||||
printf("ok\n");
|
||||
break;
|
||||
|
||||
/*
|
||||
* Allow or block traffic from a source, using the
|
||||
* delta based api.
|
||||
*/
|
||||
case 't':
|
||||
case 'b':
|
||||
sscanf(line, "%s %s %s", str1, str2, str3);
|
||||
if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) ==
|
||||
INADDR_NONE) ||
|
||||
((imrs.imr_interface.s_addr = inet_addr(str2)) ==
|
||||
INADDR_NONE) ||
|
||||
((imrs.imr_sourceaddr.s_addr = inet_addr(str3)) ==
|
||||
INADDR_NONE)) {
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
/* First determine our current filter mode. */
|
||||
n = 0;
|
||||
if (getipv4sourcefilter(s, imrs.imr_interface,
|
||||
imrs.imr_multiaddr, &fmode, &n, NULL) != 0) {
|
||||
warn("getipv4sourcefilter");
|
||||
break;
|
||||
}
|
||||
if (fmode == MCAST_EXCLUDE) {
|
||||
/* Any source */
|
||||
opt = (*cmd == 't') ? IP_UNBLOCK_SOURCE :
|
||||
IP_BLOCK_SOURCE;
|
||||
} else {
|
||||
/* Controlled source */
|
||||
opt = (*cmd == 't') ? IP_ADD_SOURCE_MEMBERSHIP :
|
||||
IP_DROP_SOURCE_MEMBERSHIP;
|
||||
}
|
||||
if (setsockopt(s, IPPROTO_IP, opt, &imrs, sizeof(imrs)) == -1)
|
||||
warn("ioctl IP_ADD_SOURCE_MEMBERSHIP/IP_DROP_SOURCE_MEMBERSHIP/IP_UNBLOCK_SOURCE/IP_BLOCK_SOURCE");
|
||||
else
|
||||
printf("ok\n");
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
/* recycle imrs struct for convenience */
|
||||
if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) ==
|
||||
INADDR_NONE) ||
|
||||
((imrs.imr_interface.s_addr = inet_addr(str2)) ==
|
||||
INADDR_NONE) || (n < 0 || n > MAX_ADDRS)) {
|
||||
printf("-1\n");
|
||||
break;
|
||||
}
|
||||
if (getipv4sourcefilter(s, imrs.imr_interface,
|
||||
imrs.imr_multiaddr, &fmode, &n, sources) != 0) {
|
||||
warn("getipv4sourcefilter");
|
||||
break;
|
||||
}
|
||||
printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" :
|
||||
"exclude");
|
||||
printf("%d\n", n);
|
||||
qsort(sources, n, sizeof(struct in_addr), &inaddr_cmp);
|
||||
for (i = 0; i < n; i++)
|
||||
printf("%s\n", inet_ntoa(sources[i]));
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
break;
|
||||
default:
|
||||
@ -392,18 +811,21 @@ static void
|
||||
usage(void)
|
||||
{
|
||||
|
||||
printf("j g.g.g.g i.i.i.i [s.s.s.s] - join IP multicast group\n");
|
||||
printf("l g.g.g.g i.i.i.i [s.s.s.s] - leave IP multicast group\n");
|
||||
printf("a ifname e.e.e.e.e.e - add ether multicast address\n");
|
||||
printf("d ifname e.e.e.e.e.e - delete ether multicast address\n");
|
||||
printf("j mcast-addr ifname [src-addr] - join IP multicast group\n");
|
||||
printf("l mcast-addr ifname [src-addr] - leave IP multicast group\n");
|
||||
printf(
|
||||
"i mcast-addr ifname n - set n include mode src filter\n");
|
||||
printf(
|
||||
"e mcast-addr ifname n - set n exclude mode src filter\n");
|
||||
printf("t mcast-addr ifname src-addr - allow traffic from src\n");
|
||||
printf("b mcast-addr ifname src-addr - block traffic from src\n");
|
||||
printf("g mcast-addr ifname n - get and show n src filters\n");
|
||||
printf("a ifname mac-addr - add link multicast filter\n");
|
||||
printf("d ifname mac-addr - delete link multicast filter\n");
|
||||
printf("m ifname 1/0 - set/clear ether allmulti flag\n");
|
||||
printf("p ifname 1/0 - set/clear ether promisc flag\n");
|
||||
printf("i g.g.g.g i.i.i.i n - set n include mode src filter\n");
|
||||
printf("e g.g.g.g i.i.i.i n - set n exclude mode src filter\n");
|
||||
printf("t g.g.g.g i.i.i.i s.s.s.s - allow traffic from src\n");
|
||||
printf("b g.g.g.g i.i.i.i s.s.s.s - block traffic from src\n");
|
||||
printf("g g.g.g.g i.i.i.i n - get and show n src filters\n");
|
||||
printf("f filename - read command(s) from file\n");
|
||||
printf("s seconds - sleep for some time\n");
|
||||
printf("q - quit\n");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user