xref: /freebsd/tools/regression/ethernet/ethermulti/ethermulti.c (revision 5e3190f700637fcfc1a52daeaa4a031fdd2557c7)
1 /*-
2  * Copyright (c) 2007 Bruce M. Simpson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/time.h>
31 #include <sys/ioctl.h>
32 
33 #include <net/if.h>
34 #include <net/if_dl.h>
35 #include <net/if_types.h>
36 #include <net/ethernet.h>
37 
38 #include <err.h>
39 #include <errno.h>
40 #include <getopt.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 
46 #include <ifaddrs.h>
47 
48 static int dorandom = 0;
49 static int verbose = 0;
50 static char *ifname = NULL;
51 
52 /*
53  * The test tool exercises IP-level socket options by interrogating the
54  * getsockopt()/setsockopt() APIs.  It does not currently test that the
55  * intended semantics of each option are implemented (i.e., that setting IP
56  * options on the socket results in packets with the desired IP options in
57  * it).
58  */
59 
60 /*
61  * get_socket() is a wrapper function that returns a socket of the specified
62  * type, and created with or without restored root privilege (if running
63  * with a real uid of root and an effective uid of some other user).  This
64  * us to test whether the same rights are granted using a socket with a
65  * privileged cached credential vs. a socket with a regular credential.
66  */
67 #define	PRIV_ASIS	0
68 #define	PRIV_GETROOT	1
69 static int
70 get_socket_unpriv(int type)
71 {
72 
73 	return (socket(PF_INET, type, 0));
74 }
75 
76 static int
77 get_socket_priv(int type)
78 {
79 	uid_t olduid;
80 	int sock;
81 
82 	if (getuid() != 0)
83 		errx(-1, "get_sock_priv: running without real uid 0");
84 
85 	olduid = geteuid();
86 	if (seteuid(0) < 0)
87 		err(-1, "get_sock_priv: seteuid(0)");
88 
89 	sock = socket(PF_INET, type, 0);
90 
91 	if (seteuid(olduid) < 0)
92 		err(-1, "get_sock_priv: seteuid(%d)", olduid);
93 
94 	return (sock);
95 }
96 
97 static int
98 get_socket(int type, int priv)
99 {
100 
101 	if (priv)
102 		return (get_socket_priv(type));
103 	else
104 		return (get_socket_unpriv(type));
105 }
106 
107 union sockunion {
108 	struct sockaddr_storage	ss;
109 	struct sockaddr		sa;
110 	struct sockaddr_dl	sdl;
111 };
112 typedef union sockunion sockunion_t;
113 
114 static void
115 test_ether_multi(int sock)
116 {
117 	struct ifreq ifr;
118 	struct sockaddr_dl *dlp;
119 	struct ether_addr ea;
120 	struct ifmaddrs *ifma, *ifmap;
121 	int found;
122 
123 	/* Choose an 802 multicast address. */
124 	if (dorandom) {
125 		uint32_t mac4;
126 
127 		srandomdev();
128 		mac4 = random();
129 		ea.octet[0] = 0x01;
130 		ea.octet[1] = 0x80;
131 		ea.octet[2] = ((mac4 >> 24 & 0xFF));
132 		ea.octet[3] = ((mac4 >> 16 & 0xFF));
133 		ea.octet[4] = ((mac4 >> 8 & 0xFF));
134 		ea.octet[5] = (mac4 & 0xFF);
135 	} else {
136 		struct ether_addr *nep = ether_aton("01:80:DE:FA:CA:7E");
137 		ea = *nep;
138 	}
139 
140 	/* Fill out ifreq, and fill out 802 group address. */
141 	memset(&ifr, 0, sizeof(struct ifreq));
142 	strlcpy(&ifr.ifr_name[0], ifname, IFNAMSIZ);
143 	dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
144 	memset(dlp, 0, sizeof(struct sockaddr_dl));
145 	dlp->sdl_len = sizeof(struct sockaddr_dl);
146 	dlp->sdl_family = AF_LINK;
147 	dlp->sdl_alen = sizeof(struct ether_addr);
148 	memcpy(LLADDR(dlp), &ea, sizeof(struct ether_addr));
149 
150 	/* Join an 802 group. */
151 	if (ioctl(sock, SIOCADDMULTI, &ifr) < 0) {
152 		warn("can't add ethernet multicast membership");
153 		return;
154 	}
155 
156 	/* Check that we joined the group by calling getifmaddrs(). */
157 	found = 0;
158 	if (getifmaddrs(&ifmap) != 0) {
159 		warn("getifmaddrs()");
160 	} else {
161 		for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
162 			sockunion_t *psa = (sockunion_t *)ifma->ifma_addr;
163 			if (ifma->ifma_name == NULL || psa == NULL)
164 				continue;
165 
166 			if (psa->sa.sa_family != AF_LINK ||
167 			    psa->sdl.sdl_alen != ETHER_ADDR_LEN)
168 				continue;
169 
170 			if (bcmp(LLADDR(&psa->sdl), LLADDR(dlp),
171 			    ETHER_ADDR_LEN) == 0) {
172 				found = 1;
173 				break;
174 			}
175 		}
176 		freeifmaddrs(ifmap);
177 	}
178 	if (!found) {
179 		warnx("group membership for %s not returned by getifmaddrs()",
180 		   ether_ntoa(&ea));
181 	}
182 
183 	/* Fill out ifreq, and fill out 802 group address. */
184 	memset(&ifr, 0, sizeof(struct ifreq));
185 	strlcpy(&ifr.ifr_name[0], ifname, IFNAMSIZ);
186 	dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
187 	memset(dlp, 0, sizeof(struct sockaddr_dl));
188 	dlp->sdl_len = sizeof(struct sockaddr_dl);
189 	dlp->sdl_family = AF_LINK;
190 	dlp->sdl_alen = sizeof(struct ether_addr);
191 	memcpy(LLADDR(dlp), &ea, sizeof(struct ether_addr));
192 
193 	/* Leave an 802 group. */
194 	if (ioctl(sock, SIOCDELMULTI, &ifr) < 0)
195 		warn("can't delete ethernet multicast membership");
196 
197 }
198 
199 static void
200 testsuite(int priv)
201 {
202 	int sock;
203 
204 	sock = get_socket(SOCK_DGRAM, 0);
205 	if (sock == -1)
206 		err(-1, "get_socket(SOCK_DGRAM) for test_ether_multi()", priv);
207 	test_ether_multi(sock);
208 	close(sock);
209 }
210 
211 static void
212 usage()
213 {
214 
215 	fprintf(stderr, "usage: ethermulti -i ifname [-r] [-v]\n");
216 	exit(EXIT_FAILURE);
217 }
218 
219 int
220 main(int argc, char *argv[])
221 {
222 	int ch;
223 
224 	while ((ch = getopt(argc, argv, "i:rv")) != -1) {
225 		switch (ch) {
226 		case 'i':
227 			ifname = optarg;
228 			break;
229 		case 'r':
230 			dorandom = 1;	/* introduce non-determinism */
231 			break;
232 		case 'v':
233 			verbose = 1;
234 			break;
235 		default:
236 			usage();
237 		}
238 	}
239 	if (ifname == NULL)
240 		usage();
241 
242 	printf("1..1\n");
243 	if (geteuid() != 0) {
244 		errx(1, "Not running as root, can't run tests as non-root");
245 		/*NOTREACHED*/
246 	} else {
247 		fprintf(stderr,
248 		    "Running tests with ruid %d euid %d sock uid 0\n",
249 		    getuid(), geteuid());
250 		testsuite(PRIV_ASIS);
251 	}
252 	printf("ok 1 - ethermulti\n");
253 	exit(0);
254 }
255