Merge a slightly cleaner and SSM capable mtest(8).
This commit is contained in:
bms 2007-04-07 03:04:49 +00:00
parent 4054f36633
commit 1f9332241a
2 changed files with 439 additions and 195 deletions

View File

@ -2,7 +2,7 @@
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.\" The following requests are required for all man pages. .\" The following requests are required for all man pages.
.Dd December 15, 1996 .Dd March 8, 2007
.Os .Os
.Dt MTEST 8 .Dt MTEST 8
.Sh NAME .Sh NAME
@ -46,13 +46,74 @@ Set or reset ALLMULTI mode on interface
.It Ic p Ar ifname Ar 1/0 .It Ic p Ar ifname Ar 1/0
Set or reset promiscuous mode on interface Set or reset promiscuous mode on interface
.Ar ifname . .Ar ifname .
.\"
.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
to include filter mode, and add
.Ar n
sources beginning with
.Ar x.x.x.x
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
to exclude filter mode, and add
.Ar n
sources beginning with
.Ar x.x.x.x
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
to block traffic from source
.Ar s.s.s.s .
.\"
.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
to allow traffic from source
.Ar s.s.s.s .
.\"
.It Ic g Ar g.g.g.g Ar i.i.i.i Ar n
Print
.Ar n
source filter entries for group
.An g.g.g.g
on IPv4 address
.An i.i.i.i .
.\"
.It Ic f Ar filename
Read commands from the file
.Ar filename .
.It Ic s Ar n
Sleep for
.Ar n
seconds.
.It Ic ?\& .It Ic ?\&
List legal commands. List legal commands.
.It Ic q .It Ic q
Quit the program. Quit the program.
.El .El
.\" .Sh SEE ALSO .Sh SEE ALSO
.Rs
.%A D. Thaler
.%A B. Fenner
.%A B. Quinn
.%T "Socket Interface Extensions for Multicast Filters"
.%O RFC 3678
.Re
.Sh AUTHORS .Sh AUTHORS
.An Steve Deering .An -split
.Sh BUGS .An "Steve Deering"
The command parser is not very flexible. .An "Wilbert De Graaf"

View File

@ -1,228 +1,411 @@
/* /*-
* Program to test new [sg]etsockopts and ioctls for manipulating IP and * Copyright (c) 2007 Bruce M. Simpson.
* Ethernet multicast address filters. * Copyright (c) 2000 Wilbert De Graaf.
* All rights reserved.
* *
* Written by Steve Deering, Stanford University, February 1989. * 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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
* 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.
*/
/*
* Diagnostic and test utility for IPv4 multicast sockets.
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__FBSDID("$FreeBSD$"); __FBSDID("$FreeBSD$");
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/errno.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/ioctl.h>
#include <net/if.h> #include <net/if.h>
#include <net/if_dl.h> #include <net/if_dl.h>
#include <sys/ioctl.h> #include <net/ethernet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.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);
#ifdef WITH_IGMPV3
static int inaddr_cmp(const void *a, const void *b);
#endif
#define MAX_ADDRS 20
#define STR_SIZE 20
#define LINE_LENGTH 80
int int
main( argc, argv ) main(int argc, char **argv)
int argc; {
char **argv; char line[LINE_LENGTH];
{ char *p;
int so; int i, s;
char line[80];
char *lineptr;
struct ip_mreq imr;
struct ifreq ifr;
int n, f;
unsigned i1, i2, i3, i4, g1, g2, g3, g4;
unsigned e1, e2, e3, e4, e5, e6;
if( (so = socket( AF_INET, SOCK_DGRAM, 0 )) == -1) s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
err( 1, "can't open socket" ); if (s == -1)
err(1, "can't open socket");
printf( "multicast membership test program; " ); if (argc < 2) {
printf( "enter ? for list of commands\n" ); if (isatty(STDIN_FILENO)) {
printf("multicast membership test program; "
"enter ? for list of commands\n");
}
do {
if (fgets(line, sizeof(line), stdin) != NULL) {
if (line[0] != 'f')
process_cmd(line, s, 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);
}
}
} while (!feof(stdin));
} else {
for (i = 1; i < argc; i++) {
process_file(argv[i], s);
}
}
while( fgets( line, 79, stdin ) != NULL ) exit (0);
{ }
lineptr = line;
while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr; static void
switch( *lineptr ) process_file(char *fname, int s)
{ {
case '?': char line[80];
{ FILE *fp;
printf( "%s%s%s%s%s%s%s", char *lineptr;
" j g.g.g.g i.i.i.i - join IP multicast group \n",
" l g.g.g.g i.i.i.i - leave IP multicast group \n", fp = fopen(fname, "r");
" a ifname e.e.e.e.e.e - add ether multicast address \n", if (fp == NULL) {
" d ifname e.e.e.e.e.e - del ether multicast address \n", warn("fopen");
" m ifname 1/0 - set/clear ether allmulti flag \n", return;
" p ifname 1/0 - set/clear ether promisc flag \n", }
" q - quit \n\n" );
/* Skip comments and empty lines. */
while (fgets(line, sizeof(line), fp) != NULL) {
lineptr = line;
while (isblank(*lineptr))
lineptr++;
if (*lineptr != '#' && *lineptr != '\n')
process_cmd(lineptr, s, fp);
}
fclose(fp);
}
static void
process_cmd(char *cmd, int s, FILE *fp __unused)
{
char str1[STR_SIZE];
char str2[STR_SIZE];
#ifdef WITH_IGMPV3
char str3[STR_SIZE];
char filtbuf[IP_MSFILTER_SIZE(MAX_ADDRS)];
#endif
struct ifreq ifr;
struct ip_mreq imr;
#ifdef WITH_IGMPV3
struct ip_mreq_source imrs;
struct ip_msfilter *imsfp;
#endif
char *line;
int n, opt, f, flags;
line = cmd;
while (isblank(*++line))
; /* Skip whitespace. */
switch (*cmd) {
case '?':
usage();
break; break;
}
case 'j': case 'q':
{ close(s);
++lineptr; exit(0);
while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
if( (n = sscanf( lineptr, "%u.%u.%u.%u %u.%u.%u.%u", case 's':
&g1, &g2, &g3, &g4, &i1, &i2, &i3, &i4 )) != 8 ) if ((sscanf(line, "%d", &n) != 1) || (n < 1)) {
{ printf("-1\n");
printf( "bad args\n" ); break;
break; }
} sleep(n);
imr.imr_multiaddr.s_addr = (g1<<24) | (g2<<16) | (g3<<8) | g4; printf("ok\n");
imr.imr_multiaddr.s_addr = htonl(imr.imr_multiaddr.s_addr);
imr.imr_interface.s_addr = (i1<<24) | (i2<<16) | (i3<<8) | i4;
imr.imr_interface.s_addr = htonl(imr.imr_interface.s_addr);
if( setsockopt( so, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&imr, sizeof(struct ip_mreq) ) == -1 )
warn( "can't join group" );
else printf( "group joined\n" );
break; break;
}
case 'l': case 'j':
{ case 'l':
++lineptr; sscanf(line, "%s %s", str1, str2);
while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr; if (((imr.imr_multiaddr.s_addr = inet_addr(str1)) ==
if( (n = sscanf( lineptr, "%u.%u.%u.%u %u.%u.%u.%u", INADDR_NONE) ||
&g1, &g2, &g3, &g4, &i1, &i2, &i3, &i4 )) != 8 ) ((imr.imr_interface.s_addr = inet_addr(str2)) ==
{ INADDR_NONE)) {
printf( "bad args\n" ); printf("-1\n");
break; break;
} }
imr.imr_multiaddr.s_addr = (g1<<24) | (g2<<16) | (g3<<8) | g4; opt = (*cmd == 'j') ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
imr.imr_multiaddr.s_addr = htonl(imr.imr_multiaddr.s_addr); if (setsockopt( s, IPPROTO_IP, opt, &imr,
imr.imr_interface.s_addr = (i1<<24) | (i2<<16) | (i3<<8) | i4; sizeof(imr)) != 0)
imr.imr_interface.s_addr = htonl(imr.imr_interface.s_addr); warn("setsockopt IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP");
if( setsockopt( so, IPPROTO_IP, IP_DROP_MEMBERSHIP, else
&imr, sizeof(struct ip_mreq) ) == -1 ) printf("ok\n");
warn( "can't leave group" );
else printf( "group left\n" );
break; break;
}
case 'a': case 'a':
{ case 'd': {
struct sockaddr_dl *dlp; struct sockaddr_dl *dlp;
unsigned char *bp; struct ether_addr *ep;
++lineptr;
while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr; memset(&ifr, 0, sizeof(struct ifreq));
if( (n = sscanf( lineptr, "%s %x.%x.%x.%x.%x.%x",
ifr.ifr_name, &e1, &e2, &e3, &e4, &e5, &e6 )) != 7 )
{
printf( "bad args\n" );
break;
}
dlp = (struct sockaddr_dl *)&ifr.ifr_addr; dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
dlp->sdl_len = sizeof(struct sockaddr_dl); dlp->sdl_len = sizeof(struct sockaddr_dl);
dlp->sdl_family = AF_LINK; dlp->sdl_family = AF_LINK;
dlp->sdl_index = 0; dlp->sdl_index = 0;
dlp->sdl_nlen = 0; dlp->sdl_nlen = 0;
dlp->sdl_alen = 6; dlp->sdl_alen = ETHER_ADDR_LEN;
dlp->sdl_slen = 0; dlp->sdl_slen = 0;
bp = LLADDR(dlp); if (sscanf(line, "%s %s", str1, str2) != 2) {
bp[0] = e1; warnc(EINVAL, "sscanf");
bp[1] = e2; break;
bp[2] = e3; }
bp[3] = e4; ep = ether_aton(str2);
bp[4] = e5; if (ep == NULL) {
bp[5] = e6; warnc(EINVAL, "ether_aton");
if( ioctl( so, SIOCADDMULTI, &ifr ) == -1 ) break;
warn( "can't add ether address" ); }
else printf( "ether address added\n" ); strlcpy(ifr.ifr_name, str1, IF_NAMESIZE);
memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN);
if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI,
&ifr) == -1)
warn("ioctl SIOCADDMULTI/SIOCDELMULTI");
else
printf("ok\n");
break; break;
} }
case 'd': case 'm':
{ printf("warning: IFF_ALLMULTI cannot be set from userland "
struct sockaddr_dl *dlp; "in FreeBSD; command ignored.\n");
unsigned char *bp;
++lineptr;
while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
if( (n = sscanf( lineptr, "%s %x.%x.%x.%x.%x.%x",
ifr.ifr_name, &e1, &e2, &e3, &e4, &e5, &e6 )) != 7 )
{
printf( "bad args\n" );
break;
}
dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
dlp->sdl_len = sizeof(struct sockaddr_dl);
dlp->sdl_family = AF_LINK;
dlp->sdl_index = 0;
dlp->sdl_nlen = 0;
dlp->sdl_alen = 6;
dlp->sdl_slen = 0;
bp = LLADDR(dlp);
bp[0] = e1;
bp[1] = e2;
bp[2] = e3;
bp[3] = e4;
bp[4] = e5;
bp[5] = e6;
if( ioctl( so, SIOCDELMULTI, &ifr ) == -1 )
warn( "can't delete ether address" );
else printf( "ether address deleted\n" );
break; break;
} case 'p':
if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) {
case 'm': printf("-1\n");
{ break;
++lineptr; }
while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr; if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
if( (n = sscanf( lineptr, "%s %u", ifr.ifr_name, &f )) != 2 ) warn("ioctl SIOCGIFFLAGS");
{ break;
printf( "bad args\n" ); }
break; flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
} opt = IFF_PPROMISC;
if( ioctl( so, SIOCGIFFLAGS, &ifr ) == -1 ) if (f == 0) {
{ flags &= ~opt;
warn( "can't get interface flags" ); } else {
break; flags |= opt;
} }
printf( "interface flags %x, ", ifr.ifr_flags ); ifr.ifr_flags = flags & 0xffff;
fflush( stdout ); ifr.ifr_flagshigh = flags >> 16;
if( f ) ifr.ifr_flags |= IFF_ALLMULTI; if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1)
else ifr.ifr_flags &= ~IFF_ALLMULTI; warn("ioctl SIOCGIFFLAGS");
if( ioctl( so, SIOCSIFFLAGS, &ifr ) == -1 ) else
warn( "can't set" ); printf( "changed to 0x%08x\n", flags );
else printf( "changed to %x\n", ifr.ifr_flags );
break; break;
}
case 'p': #ifdef WITH_IGMPV3
{ /*
++lineptr; * Set the socket to include or exclude filter mode, and
while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr; * add some sources to the filterlist, using the full-state,
if( (n = sscanf( lineptr, "%s %u", ifr.ifr_name, &f )) != 2 ) * or advanced api.
{ */
printf( "bad args\n" ); case 'i':
break; case 'e':
} if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
if( ioctl( so, SIOCGIFFLAGS, &ifr ) == -1 ) printf("-1\n");
{ break;
warn( "can't get interface flags" ); }
break; imsfp = (struct ip_msfilter *)filtbuf;
} if (((imsfp->imsf_multiaddr.s_addr = inet_addr(str1)) ==
printf( "interface flags %x, ", ifr.ifr_flags ); INADDR_NONE) ||
fflush( stdout ); ((imsfp->imsf_interface.s_addr = inet_addr(str2)) ==
if( f ) ifr.ifr_flags |= IFF_PROMISC; INADDR_NONE) || (n > MAX_ADDRS)) {
else ifr.ifr_flags &= ~IFF_PROMISC; printf("-1\n");
if( ioctl( so, SIOCSIFFLAGS, &ifr ) == -1 ) break;
warn( "can't set" ); }
else printf( "changed to %x\n", ifr.ifr_flags ); imsfp->imsf_fmode = (*cmd == 'i') ? MCAST_INCLUDE :
MCAST_EXCLUDE;
imsfp->imsf_numsrc = n;
for (i = 0; i < n; i++) {
fgets(str1, sizeof(str1), fp);
if ((imsfp->imsf_slist[i].s_addr = inet_addr(str1)) ==
INADDR_NONE) {
printf("-1\n");
return;
}
}
if (ioctl(s, SIOCSIPMSFILTER, imsfp) != 0)
warn("setsockopt SIOCSIPMSFILTER");
else
printf("ok\n");
break; break;
}
case 'q': exit( 0 ); /*
* 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;
}
case 0: /* First determine out current filter mode. */
case '\n': break; imsfp = (struct ip_msfilter *)filtbuf;
imsfp->imsf_multiaddr.s_addr = imrs.imr_multiaddr.s_addr;
default: imsfp->imsf_interface.s_addr = imrs.imr_interface.s_addr;
{ imsfp->imsf_numsrc = 5;
printf( "bad command\n" ); if (ioctl(s, SIOCSIPMSFILTER, imsfp) != 0) {
/* It's only okay for 't' to fail */
if (*cmd != 't') {
warn("ioctl SIOCSIPMSFILTER");
break;
} else {
imsfp->imsf_fmode = MCAST_INCLUDE;
}
}
if (imsfp->imsf_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; break;
}
} case 'g':
} if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
return(0); printf("-1\n");
} break;
}
imsfp = (struct ip_msfilter *)filtbuf;
if (((imsfp->imsf_multiaddr.s_addr = inet_addr(str1)) ==
INADDR_NONE) ||
((imsfp->imsf_interface.s_addr = inet_addr(str2)) ==
INADDR_NONE) || (n < 0 || n > MAX_ADDRS)) {
printf("-1\n");
break;
}
imsfp->imsf_numsrc = n;
if (ioctl(s, SIOCSIPMSFILTER, imsfp) != 0) {
warn("setsockopt SIOCSIPMSFILTER");
break;
}
printf("%s\n", (imsfp->imsf_fmode == MCAST_INCLUDE) ?
"include" : "exclude");
printf("%d\n", imsfp->imsf_numsrc);
if (n >= imsfp->imsf_numsrc) {
n = imsfp->imsf_numsrc;
qsort(imsfp->imsf_slist, n, sizeof(struct in_addr),
&inaddr_cmp);
for (i = 0; i < n; i++)
printf("%s\n", inet_ntoa(imsfp->imsf_slist[i]));
}
break;
#else /* !WITH_IGMPV3 */
case 'i':
case 'e':
case 't':
case 'b':
case 'g':
printf("warning: IGMPv3 is not supported by this version "
"of FreeBSD; command ignored.\n");
break;
#endif /* WITH_IGMPV3 */
case '\n':
break;
default:
printf("invalid command\n");
break;
}
}
static void
usage(void)
{
printf("j g.g.g.g i.i.i.i - join IP multicast group\n");
printf("l g.g.g.g i.i.i.i - 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("m ifname 1/0 - set/clear ether allmulti flag\n");
printf("p ifname 1/0 - set/clear ether promisc flag\n");
#ifdef WITH_IGMPV3
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");
#endif
printf("f filename - read command(s) from file\n");
printf("s seconds - sleep for some time\n");
printf("q - quit\n");
}
#ifdef WITH_IGMPV3
static int
inaddr_cmp(const void *a, const void *b)
{
return((int)((const struct in_addr *)a)->s_addr -
((const struct in_addr *)b)->s_addr);
}
#endif