]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mtest/mtest.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / usr.sbin / mtest / mtest.c
1 /*-
2  * Copyright (c) 2007 Bruce M. Simpson.
3  * Copyright (c) 2000 Wilbert De Graaf.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 /*
32  * Diagnostic and test utility for IPv4 multicast sockets.
33  */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/types.h>
39 #include <sys/errno.h>
40 #include <sys/socket.h>
41 #include <sys/time.h>
42 #include <sys/ioctl.h>
43
44 #include <net/if.h>
45 #include <net/if_dl.h>
46 #include <net/ethernet.h>
47 #include <netinet/in.h>
48
49 #include <arpa/inet.h>
50
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <ctype.h>
55 #include <err.h>
56 #include <unistd.h>
57
58 static void     process_file(char *, int);
59 static void     process_cmd(char*, int, FILE *fp);
60 static void     usage(void);
61 #ifdef WITH_IGMPV3
62 static int      inaddr_cmp(const void *a, const void *b);
63 #endif
64
65 #define MAX_ADDRS       20
66 #define STR_SIZE        20
67 #define LINE_LENGTH     80
68
69 int
70 main(int argc, char **argv)
71 {
72         char     line[LINE_LENGTH];
73         char    *p;
74         int      i, s;
75
76         s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
77         if (s == -1)
78                 err(1, "can't open socket");
79
80         if (argc < 2) {
81                 if (isatty(STDIN_FILENO)) {
82                         printf("multicast membership test program; "
83                             "enter ? for list of commands\n");
84                 }
85                 do {
86                         if (fgets(line, sizeof(line), stdin) != NULL) {
87                                 if (line[0] != 'f')
88                                         process_cmd(line, s, stdin);
89                                 else {
90                                         /* Get the filename */
91                                         for (i = 1; isblank(line[i]); i++);
92                                         if ((p = (char*)strchr(line, '\n'))
93                                             != NULL)
94                                                 *p = '\0';
95                                         process_file(&line[i], s);
96                                 }
97                         }
98                 } while (!feof(stdin));
99         } else {
100                 for (i = 1; i < argc; i++) {
101                         process_file(argv[i], s);
102                 }
103         }
104
105         exit (0);
106 }
107
108 static void
109 process_file(char *fname, int s)
110 {
111         char line[80];
112         FILE *fp;
113         char *lineptr;
114
115         fp = fopen(fname, "r");
116         if (fp == NULL) {
117                 warn("fopen");
118                 return;
119         }
120
121         /* Skip comments and empty lines. */
122         while (fgets(line, sizeof(line), fp) != NULL) {
123                 lineptr = line;
124                 while (isblank(*lineptr))
125                         lineptr++;
126                 if (*lineptr != '#' && *lineptr != '\n')
127                         process_cmd(lineptr, s, fp);
128         }
129
130         fclose(fp);
131 }
132
133 static void
134 process_cmd(char *cmd, int s, FILE *fp __unused)
135 {
136         char                     str1[STR_SIZE];
137         char                     str2[STR_SIZE];
138 #ifdef WITH_IGMPV3
139         char                     str3[STR_SIZE];
140         char                     filtbuf[IP_MSFILTER_SIZE(MAX_ADDRS)];
141 #endif
142         struct ifreq             ifr;
143         struct ip_mreq           imr;
144 #ifdef WITH_IGMPV3
145         struct ip_mreq_source    imrs;
146         struct ip_msfilter      *imsfp;
147 #endif
148         char                    *line;
149         int                      n, opt, f, flags;
150
151         line = cmd;
152         while (isblank(*++line))
153                 ;       /* Skip whitespace. */
154
155         switch (*cmd) {
156         case '?':
157                 usage();
158                 break;
159
160         case 'q':
161                 close(s);
162                 exit(0);
163
164         case 's':
165                 if ((sscanf(line, "%d", &n) != 1) || (n < 1)) {
166                         printf("-1\n");
167                         break;
168                 }
169                 sleep(n);
170                 printf("ok\n");
171                 break;
172
173         case 'j':
174         case 'l':
175                 sscanf(line, "%s %s", str1, str2);
176                 if (((imr.imr_multiaddr.s_addr = inet_addr(str1)) ==
177                     INADDR_NONE) ||
178                     ((imr.imr_interface.s_addr = inet_addr(str2)) ==
179                     INADDR_NONE)) {
180                         printf("-1\n");
181                         break;
182                 }
183                 opt = (*cmd == 'j') ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
184                 if (setsockopt( s, IPPROTO_IP, opt, &imr,
185                     sizeof(imr)) != 0)
186                         warn("setsockopt IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP");
187                 else
188                         printf("ok\n");
189                 break;
190
191         case 'a':
192         case 'd': {
193                 struct sockaddr_dl      *dlp;
194                 struct ether_addr       *ep;
195
196                 memset(&ifr, 0, sizeof(struct ifreq));
197                 dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
198                 dlp->sdl_len = sizeof(struct sockaddr_dl);
199                 dlp->sdl_family = AF_LINK;
200                 dlp->sdl_index = 0;
201                 dlp->sdl_nlen = 0;
202                 dlp->sdl_alen = ETHER_ADDR_LEN;
203                 dlp->sdl_slen = 0;
204                 if (sscanf(line, "%s %s", str1, str2) != 2) {
205                         warnc(EINVAL, "sscanf");
206                         break;
207                 }
208                 ep = ether_aton(str2);
209                 if (ep == NULL) {
210                         warnc(EINVAL, "ether_aton");
211                         break;
212                 }
213                 strlcpy(ifr.ifr_name, str1, IF_NAMESIZE);
214                 memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN);
215                 if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI,
216                     &ifr) == -1)
217                         warn("ioctl SIOCADDMULTI/SIOCDELMULTI");
218                 else
219                         printf("ok\n");
220                 break;
221         }
222
223         case 'm':
224                 printf("warning: IFF_ALLMULTI cannot be set from userland "
225                     "in FreeBSD; command ignored.\n");
226                 break;
227         case 'p':
228                 if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) {
229                         printf("-1\n");
230                         break;
231                 }
232                 if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
233                         warn("ioctl SIOCGIFFLAGS");
234                         break;
235                 }
236                 flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
237                 opt = IFF_PPROMISC;
238                 if (f == 0) {
239                         flags &= ~opt;
240                 } else {
241                         flags |= opt;
242                 }
243                 ifr.ifr_flags = flags & 0xffff;
244                 ifr.ifr_flagshigh = flags >> 16;
245                 if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1)
246                         warn("ioctl SIOCGIFFLAGS");
247                 else
248                         printf( "changed to 0x%08x\n", flags );
249                 break;
250
251 #ifdef WITH_IGMPV3
252         /*
253          * Set the socket to include or exclude filter mode, and
254          * add some sources to the filterlist, using the full-state,
255          * or advanced api.
256          */
257         case 'i':
258         case 'e':
259                 if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
260                         printf("-1\n");
261                         break;
262                 }
263                 imsfp = (struct ip_msfilter *)filtbuf;
264                 if (((imsfp->imsf_multiaddr.s_addr = inet_addr(str1)) ==
265                     INADDR_NONE) ||
266                     ((imsfp->imsf_interface.s_addr = inet_addr(str2)) ==
267                     INADDR_NONE) || (n > MAX_ADDRS)) {
268                         printf("-1\n");
269                         break;
270                 }
271                 imsfp->imsf_fmode = (*cmd == 'i') ? MCAST_INCLUDE :
272                     MCAST_EXCLUDE;
273                 imsfp->imsf_numsrc = n;
274                 for (i = 0; i < n; i++) {
275                         fgets(str1, sizeof(str1), fp);
276                         if ((imsfp->imsf_slist[i].s_addr = inet_addr(str1)) ==
277                             INADDR_NONE) {
278                                 printf("-1\n");
279                                 return;
280                         }
281                 }
282                 if (ioctl(s, SIOCSIPMSFILTER, imsfp) != 0)
283                         warn("setsockopt SIOCSIPMSFILTER");
284                 else
285                         printf("ok\n");
286                 break;
287
288         /*
289          * Allow or block traffic from a source, using the
290          * delta based api.
291          */
292         case 't':
293         case 'b':
294                 sscanf(line, "%s %s %s", str1, str2, str3);
295                 if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) ==
296                     INADDR_NONE) ||
297                         ((imrs.imr_interface.s_addr = inet_addr(str2)) ==
298                     INADDR_NONE) ||
299                         ((imrs.imr_sourceaddr.s_addr = inet_addr(str3)) ==
300                     INADDR_NONE)) {
301                         printf("-1\n");
302                         break;
303                 }
304
305                 /* First determine out current filter mode. */
306                 imsfp = (struct ip_msfilter *)filtbuf;
307                 imsfp->imsf_multiaddr.s_addr = imrs.imr_multiaddr.s_addr;
308                 imsfp->imsf_interface.s_addr = imrs.imr_interface.s_addr;
309                 imsfp->imsf_numsrc = 5;
310                 if (ioctl(s, SIOCSIPMSFILTER, imsfp) != 0) {
311                         /* It's only okay for 't' to fail */
312                         if (*cmd != 't') {
313                                 warn("ioctl SIOCSIPMSFILTER");
314                                 break;
315                         } else {
316                                 imsfp->imsf_fmode = MCAST_INCLUDE;
317                         }
318                 }
319                 if (imsfp->imsf_fmode == MCAST_EXCLUDE) {
320                         /* Any source */
321                         opt = (*cmd == 't') ? IP_UNBLOCK_SOURCE :
322                             IP_BLOCK_SOURCE;
323                 } else {
324                         /* Controlled source */
325                         opt = (*cmd == 't') ? IP_ADD_SOURCE_MEMBERSHIP :
326                             IP_DROP_SOURCE_MEMBERSHIP;
327                 }
328                 if (setsockopt(s, IPPROTO_IP, opt, &imrs, sizeof(imrs)) == -1)
329                         warn("ioctl IP_ADD_SOURCE_MEMBERSHIP/IP_DROP_SOURCE_MEMBERSHIP/IP_UNBLOCK_SOURCE/IP_BLOCK_SOURCE");
330                 else
331                         printf("ok\n");
332                 break;
333
334         case 'g':
335                 if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
336                         printf("-1\n");
337                         break;
338                 }
339                 imsfp = (struct ip_msfilter *)filtbuf;
340                 if (((imsfp->imsf_multiaddr.s_addr = inet_addr(str1)) ==
341                     INADDR_NONE) ||
342                     ((imsfp->imsf_interface.s_addr = inet_addr(str2)) ==
343                     INADDR_NONE) || (n < 0 || n > MAX_ADDRS)) {
344                         printf("-1\n");
345                         break;
346                 }
347                 imsfp->imsf_numsrc = n;
348                 if (ioctl(s, SIOCSIPMSFILTER, imsfp) != 0) {
349                         warn("setsockopt SIOCSIPMSFILTER");
350                         break;
351                 }
352                 printf("%s\n", (imsfp->imsf_fmode == MCAST_INCLUDE) ?
353                     "include" : "exclude");
354                 printf("%d\n", imsfp->imsf_numsrc);
355                 if (n >= imsfp->imsf_numsrc) {
356                         n = imsfp->imsf_numsrc;
357                         qsort(imsfp->imsf_slist, n, sizeof(struct in_addr),
358                             &inaddr_cmp);
359                         for (i = 0; i < n; i++)
360                                 printf("%s\n", inet_ntoa(imsfp->imsf_slist[i]));
361                 }
362                 break;
363 #else   /* !WITH_IGMPV3 */
364         case 'i':
365         case 'e':
366         case 't':
367         case 'b':
368         case 'g':
369                 printf("warning: IGMPv3 is not supported by this version "
370                     "of FreeBSD; command ignored.\n");
371                 break;
372 #endif  /* WITH_IGMPV3 */
373
374         case '\n':
375                 break;
376         default:
377                 printf("invalid command\n");
378                 break;
379         }
380 }
381
382 static void
383 usage(void)
384 {
385
386         printf("j g.g.g.g i.i.i.i          - join IP multicast group\n");
387         printf("l g.g.g.g i.i.i.i          - leave IP multicast group\n");
388         printf("a ifname e.e.e.e.e.e       - add ether multicast address\n");
389         printf("d ifname e.e.e.e.e.e       - delete ether multicast address\n");
390         printf("m ifname 1/0               - set/clear ether allmulti flag\n");
391         printf("p ifname 1/0               - set/clear ether promisc flag\n");
392 #ifdef WITH_IGMPV3
393         printf("i g.g.g.g i.i.i.i n        - set n include mode src filter\n");
394         printf("e g.g.g.g i.i.i.i n        - set n exclude mode src filter\n");
395         printf("t g.g.g.g i.i.i.i s.s.s.s  - allow traffic from src\n");
396         printf("b g.g.g.g i.i.i.i s.s.s.s  - block traffic from src\n");
397         printf("g g.g.g.g i.i.i.i n        - get and show n src filters\n");
398 #endif
399         printf("f filename                 - read command(s) from file\n");
400         printf("s seconds                  - sleep for some time\n");
401         printf("q                          - quit\n");
402 }
403
404 #ifdef WITH_IGMPV3
405 static int
406 inaddr_cmp(const void *a, const void *b)
407 {
408         return((int)((const struct in_addr *)a)->s_addr -
409             ((const struct in_addr *)b)->s_addr);
410 }
411 #endif