1180394Sgnn//
2180394Sgnn//  Copyright (c) 2008, Neville-Neil Consulting
3180394Sgnn//  All rights reserved.
4180394Sgnn//
5180394Sgnn// Redistribution and use in source and binary forms, with or without
6180394Sgnn// modification, are permitted provided that the following conditions
7180394Sgnn// are met:
8180394Sgnn// 1. Redistributions of source code must retain the above copyright
9180394Sgnn//    notice, this list of conditions and the following disclaimer.
10180394Sgnn// 2. Redistributions in binary form must reproduce the above copyright
11180394Sgnn//    notice, this list of conditions and the following disclaimer in the
12180394Sgnn//    documentation and/or other materials provided with the distribution.
13180394Sgnn//
14180394Sgnn// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15180394Sgnn// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16180394Sgnn// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17180394Sgnn// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18180394Sgnn// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19180394Sgnn// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20180394Sgnn// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21180394Sgnn// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22180394Sgnn// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23180394Sgnn// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24180394Sgnn// SUCH DAMAGE.
25180394Sgnn//
26180394Sgnn//  This test simply grabs N multicast addresses starting
27180394Sgnn//  from a base address.  The purpose is to make sure that switching a device
28180394Sgnn//  from using a multicast filtering table or function to promiscuous
29180394Sgnn//  multicast listening mode does not cause deleterious side effects.
30180394Sgnn//
31180394Sgnn
32180394Sgnn#include <sys/cdefs.h>
33180394Sgnn__FBSDID("$FreeBSD$");
34180394Sgnn
35180394Sgnn// C++ STL and other related includes
36180394Sgnn#include <stdlib.h>
37180394Sgnn#include <limits.h>
38180394Sgnn#include <iostream>
39180394Sgnn#include <string.h>
40180394Sgnn#include <string>
41180394Sgnn
42180394Sgnn// Operating System and other C based includes
43180394Sgnn#include <unistd.h>
44180394Sgnn#include <errno.h>
45180394Sgnn#include <sys/types.h>
46180394Sgnn#include <sys/time.h>
47180394Sgnn#include <sys/socket.h>
48180394Sgnn#include <sys/ioctl.h>
49180394Sgnn#include <netinet/in.h>
50180394Sgnn#include <net/if.h>
51180394Sgnn#include <arpa/inet.h>
52180394Sgnn
53180394Sgnn// Private include files
54180394Sgnn#include "mctest.h"
55180394Sgnn
56180394Sgnnusing namespace std;
57180394Sgnn
58180394Sgnn//
59180394Sgnn// usage - just the program's usage line
60180394Sgnn//
61180394Sgnn//
62180394Sgnnvoid usage()
63180394Sgnn{
64180394Sgnn    cout << "mcgrab -i interface -g multicast group -n number of groups\n";
65180394Sgnn    exit(-1);
66180394Sgnn}
67180394Sgnn
68180394Sgnn//
69180394Sgnn// usage - print out the usage with a possible message and exit
70180394Sgnn//
71180394Sgnn// \param message optional string
72180394Sgnn//
73180394Sgnn//
74180394Sgnnvoid usage(string message)
75180394Sgnn{
76180394Sgnn
77180394Sgnn    cerr << message << endl;
78180394Sgnn    usage();
79180394Sgnn}
80180394Sgnn
81180394Sgnn
82180394Sgnn//
83180394Sgnn// grab a set of addresses
84180394Sgnn//
85180394Sgnn// @param interface             ///< text name of the interface (em0 etc.)
86180394Sgnn// @param group			///< multicast group
87180394Sgnn// @param number		///< number of addresses to grab
88180394Sgnn//
89180394Sgnn// @return 0 for 0K, -1 for error, sets errno
90180394Sgnn//
91260791Sgnnvoid grab(char *interface, struct in_addr *group, int number, int block) {
92180394Sgnn
93180394Sgnn
94180394Sgnn    int sock;
95180394Sgnn    struct ip_mreq mreq;
96180394Sgnn    struct ifreq ifreq;
97180394Sgnn    struct in_addr lgroup;
98180394Sgnn
99180394Sgnn    if (group == NULL) {
100180394Sgnn	group = &lgroup;
101180394Sgnn	if (inet_pton(AF_INET, DEFAULT_GROUP, group) < 1)
102180394Sgnn	    return;
103180394Sgnn    }
104180394Sgnn
105180394Sgnn    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
106180394Sgnn	perror("failed to open datagram socket");
107180394Sgnn	return;
108180394Sgnn    }
109180394Sgnn
110180394Sgnn    for (int i = 0; i < number; i++) {
111180394Sgnn	bzero((struct ip_mreq *)&mreq, sizeof(mreq));
112180394Sgnn	bzero((struct ifreq *)&ifreq, sizeof(ifreq));
113180394Sgnn
114180394Sgnn	strncpy(ifreq.ifr_name, interface, IFNAMSIZ);
115180394Sgnn	if (ioctl(sock, SIOCGIFADDR, &ifreq) < 0) {
116180394Sgnn	    perror("no such interface");
117180394Sgnn	    return;
118180394Sgnn	}
119180394Sgnn
120180394Sgnn	memcpy(&mreq.imr_interface,
121180394Sgnn	       &((struct sockaddr_in*) &ifreq.ifr_addr)->sin_addr,
122180394Sgnn	       sizeof(struct in_addr));
123180394Sgnn
124180394Sgnn	mreq.imr_multiaddr.s_addr = group->s_addr;
125180394Sgnn	if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
126180394Sgnn		       sizeof(mreq)) < 0) {
127180394Sgnn
128180394Sgnn	    perror("failed to add membership");
129180394Sgnn	    return;
130180394Sgnn	}
131180394Sgnn
132180394Sgnn	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
133180394Sgnn		       &((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr,
134180394Sgnn		       sizeof(struct in_addr)) < 0) {
135180394Sgnn	    perror("failed to bind interface");
136180394Sgnn	    return;
137180394Sgnn	}
138180394Sgnn
139180394Sgnn	group->s_addr = htonl(ntohl(group->s_addr) + 1);
140180394Sgnn    }
141260791Sgnn    if (block > 0) {
142260791Sgnn	    printf("Press Control-C to exit.\n");
143260791Sgnn	    sleep(INT_MAX);
144260791Sgnn    }
145180394Sgnn
146180394Sgnn}
147180394Sgnn
148180394Sgnn
149180394Sgnn//
150180394Sgnn// main - the main program
151180394Sgnn//
152180394Sgnn// \param -g multicast group address which we will hold
153180394Sgnn// \param -i interface on which we're holding the address
154180394Sgnn//
155180394Sgnn//
156180394Sgnnint main(int argc, char**argv)
157180394Sgnn{
158180394Sgnn
159180394Sgnn	char ch;		///< character from getopt()
160180394Sgnn	extern char* optarg;	///< option argument
161180394Sgnn
162180394Sgnn	char* interface = 0;    ///< Name of the interface
163180394Sgnn	struct in_addr *group = NULL;	///< the multicast group address
164180394Sgnn	int number = 0;		///< Number of addresses to grab
165260791Sgnn	int block = 1;		///< Do we block or just return?
166180394Sgnn
167260791Sgnn	if ((argc < 7) || (argc > 8))
168180394Sgnn		usage();
169180394Sgnn
170260791Sgnn	while ((ch = getopt(argc, argv, "g:i:n:bh")) != -1) {
171180394Sgnn		switch (ch) {
172180394Sgnn		case 'g':
173180394Sgnn			group = new (struct in_addr );
174180394Sgnn			if (inet_pton(AF_INET, optarg, group) < 1)
175180394Sgnn				usage(argv[0] + string(" Error: invalid multicast group") +
176180394Sgnn				      optarg);
177180394Sgnn			break;
178180394Sgnn		case 'i':
179180394Sgnn			interface = optarg;
180180394Sgnn			break;
181180394Sgnn		case 'n':
182180394Sgnn			number = atoi(optarg);
183180394Sgnn			break;
184260791Sgnn		case 'b':
185260791Sgnn			block = 0;
186260791Sgnn			break;
187180394Sgnn		case 'h':
188180394Sgnn			usage(string("Help\n"));
189180394Sgnn			break;
190180394Sgnn		}
191180394Sgnn	}
192180394Sgnn
193260791Sgnn	grab(interface, group, number, block);
194180394Sgnn
195180394Sgnn}
196