1166961Sbms/*-
2166961Sbms * Copyright (c) 2007 Bruce M. Simpson
3166961Sbms * All rights reserved.
4166961Sbms *
5166961Sbms * Redistribution and use in source and binary forms, with or without
6166961Sbms * modification, are permitted provided that the following conditions
7166961Sbms * are met:
8166961Sbms * 1. Redistributions of source code must retain the above copyright
9166961Sbms *    notice, this list of conditions and the following disclaimer.
10166961Sbms * 2. Redistributions in binary form must reproduce the above copyright
11166961Sbms *    notice, this list of conditions and the following disclaimer in the
12166961Sbms *    documentation and/or other materials provided with the distribution.
13166961Sbms *
14166961Sbms * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15166961Sbms * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16166961Sbms * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17166961Sbms * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18166961Sbms * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19166961Sbms * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20166961Sbms * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21166961Sbms * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22166961Sbms * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23166961Sbms * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24166961Sbms * SUCH DAMAGE.
25166961Sbms */
26166961Sbms
27166961Sbms#include <sys/cdefs.h>
28166961Sbms__FBSDID("$FreeBSD$");
29166961Sbms
30166961Sbms#include <sys/types.h>
31166961Sbms#include <sys/socket.h>
32166961Sbms#include <sys/time.h>
33166961Sbms#include <sys/ioctl.h>
34166961Sbms
35166961Sbms#include <net/if.h>
36166961Sbms#include <net/if_dl.h>
37166961Sbms#include <net/if_types.h>
38166961Sbms#include <net/ethernet.h>
39166961Sbms
40166961Sbms#include <err.h>
41166961Sbms#include <errno.h>
42166961Sbms#include <getopt.h>
43166961Sbms#include <stdio.h>
44166961Sbms#include <stdlib.h>
45166961Sbms#include <string.h>
46166961Sbms#include <unistd.h>
47166961Sbms
48166961Sbms#include <ifaddrs.h>
49166961Sbms
50166961Sbmsstatic int dorandom = 0;
51166961Sbmsstatic int verbose = 0;
52166961Sbmsstatic char *ifname = NULL;
53166961Sbms
54166961Sbms/*
55166961Sbms * The test tool exercises IP-level socket options by interrogating the
56166961Sbms * getsockopt()/setsockopt() APIs.  It does not currently test that the
57166961Sbms * intended semantics of each option are implemented (i.e., that setting IP
58166961Sbms * options on the socket results in packets with the desired IP options in
59166961Sbms * it).
60166961Sbms */
61166961Sbms
62166961Sbms/*
63166961Sbms * get_socket() is a wrapper function that returns a socket of the specified
64166961Sbms * type, and created with or without restored root privilege (if running
65166961Sbms * with a real uid of root and an effective uid of some other user).  This
66166961Sbms * us to test whether the same rights are granted using a socket with a
67166961Sbms * privileged cached credential vs. a socket with a regular credential.
68166961Sbms */
69166961Sbms#define	PRIV_ASIS	0
70166961Sbms#define	PRIV_GETROOT	1
71166961Sbmsstatic int
72166961Sbmsget_socket_unpriv(int type)
73166961Sbms{
74166961Sbms
75166961Sbms	return (socket(PF_INET, type, 0));
76166961Sbms}
77166961Sbms
78166961Sbmsstatic int
79166961Sbmsget_socket_priv(int type)
80166961Sbms{
81166961Sbms	uid_t olduid;
82166961Sbms	int sock;
83166961Sbms
84166961Sbms	if (getuid() != 0)
85166961Sbms		errx(-1, "get_sock_priv: running without real uid 0");
86166961Sbms
87166961Sbms	olduid = geteuid();
88166961Sbms	if (seteuid(0) < 0)
89166961Sbms		err(-1, "get_sock_priv: seteuid(0)");
90166961Sbms
91166961Sbms	sock = socket(PF_INET, type, 0);
92166961Sbms
93166961Sbms	if (seteuid(olduid) < 0)
94166961Sbms		err(-1, "get_sock_priv: seteuid(%d)", olduid);
95166961Sbms
96166961Sbms	return (sock);
97166961Sbms}
98166961Sbms
99166961Sbmsstatic int
100166961Sbmsget_socket(int type, int priv)
101166961Sbms{
102166961Sbms
103166961Sbms	if (priv)
104166961Sbms		return (get_socket_priv(type));
105166961Sbms	else
106166961Sbms		return (get_socket_unpriv(type));
107166961Sbms}
108166961Sbms
109166961Sbmsunion sockunion {
110166961Sbms	struct sockaddr_storage	ss;
111166961Sbms	struct sockaddr		sa;
112166961Sbms	struct sockaddr_dl	sdl;
113166961Sbms};
114166961Sbmstypedef union sockunion sockunion_t;
115166961Sbms
116166961Sbmsstatic void
117166961Sbmstest_ether_multi(int sock)
118166961Sbms{
119166961Sbms	struct ifreq ifr;
120166961Sbms	struct sockaddr_dl *dlp;
121166961Sbms	struct ether_addr ea;
122166961Sbms	struct ifmaddrs *ifma, *ifmap;
123166961Sbms	int found;
124166961Sbms
125166961Sbms	/* Choose an 802 multicast address. */
126166961Sbms	if (dorandom) {
127166961Sbms		uint32_t mac4;
128166961Sbms
129166961Sbms		srandomdev();
130166961Sbms		mac4 = random();
131166961Sbms		ea.octet[0] = 0x01;
132166961Sbms		ea.octet[1] = 0x80;
133166961Sbms		ea.octet[2] = ((mac4 >> 24 & 0xFF));
134166961Sbms		ea.octet[3] = ((mac4 >> 16 & 0xFF));
135166961Sbms		ea.octet[4] = ((mac4 >> 8 & 0xFF));
136166961Sbms		ea.octet[5] = (mac4 & 0xFF);
137166961Sbms	} else {
138166961Sbms		struct ether_addr *nep = ether_aton("01:80:DE:FA:CA:7E");
139166961Sbms		ea = *nep;
140166961Sbms	}
141166961Sbms
142166961Sbms	/* Fill out ifreq, and fill out 802 group address. */
143166961Sbms	memset(&ifr, 0, sizeof(struct ifreq));
144166961Sbms	strlcpy(&ifr.ifr_name[0], ifname, IFNAMSIZ);
145166961Sbms	dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
146166961Sbms	memset(dlp, 0, sizeof(struct sockaddr_dl));
147166961Sbms	dlp->sdl_len = sizeof(struct sockaddr_dl);
148166961Sbms	dlp->sdl_family = AF_LINK;
149166961Sbms	dlp->sdl_alen = sizeof(struct ether_addr);
150166961Sbms	memcpy(LLADDR(dlp), &ea, sizeof(struct ether_addr));
151166961Sbms
152166961Sbms	/* Join an 802 group. */
153166961Sbms	if (ioctl(sock, SIOCADDMULTI, &ifr) < 0) {
154166961Sbms		warn("can't add ethernet multicast membership");
155166961Sbms		return;
156166961Sbms	}
157166961Sbms
158166961Sbms	/* Check that we joined the group by calling getifmaddrs(). */
159166961Sbms	found = 0;
160166961Sbms	if (getifmaddrs(&ifmap) != 0) {
161166961Sbms		warn("getifmaddrs()");
162166961Sbms	} else {
163166961Sbms		for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
164166961Sbms			sockunion_t *psa = (sockunion_t *)ifma->ifma_addr;
165166961Sbms			if (ifma->ifma_name == NULL || psa == NULL)
166166961Sbms				continue;
167166961Sbms
168166961Sbms			if (psa->sa.sa_family != AF_LINK ||
169166961Sbms			    psa->sdl.sdl_alen != ETHER_ADDR_LEN)
170166961Sbms				continue;
171166961Sbms
172166961Sbms			if (bcmp(LLADDR(&psa->sdl), LLADDR(dlp),
173166961Sbms			    ETHER_ADDR_LEN) == 0) {
174166961Sbms				found = 1;
175166961Sbms				break;
176166961Sbms			}
177166961Sbms		}
178166961Sbms		freeifmaddrs(ifmap);
179166961Sbms	}
180166961Sbms	if (!found) {
181166961Sbms		warnx("group membership for %s not returned by getifmaddrs()",
182166961Sbms		   ether_ntoa(&ea));
183166961Sbms	}
184166961Sbms
185166961Sbms	/* Fill out ifreq, and fill out 802 group address. */
186166961Sbms	memset(&ifr, 0, sizeof(struct ifreq));
187166961Sbms	strlcpy(&ifr.ifr_name[0], ifname, IFNAMSIZ);
188166961Sbms	dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
189166961Sbms	memset(dlp, 0, sizeof(struct sockaddr_dl));
190166961Sbms	dlp->sdl_len = sizeof(struct sockaddr_dl);
191166961Sbms	dlp->sdl_family = AF_LINK;
192166961Sbms	dlp->sdl_alen = sizeof(struct ether_addr);
193166961Sbms	memcpy(LLADDR(dlp), &ea, sizeof(struct ether_addr));
194166961Sbms
195166961Sbms	/* Leave an 802 group. */
196166961Sbms	if (ioctl(sock, SIOCDELMULTI, &ifr) < 0)
197166961Sbms		warn("can't delete ethernet multicast membership");
198166961Sbms
199166961Sbms}
200166961Sbms
201166961Sbmsstatic void
202166961Sbmstestsuite(int priv)
203166961Sbms{
204166961Sbms	int sock;
205166961Sbms
206166961Sbms	sock = get_socket(SOCK_DGRAM, 0);
207166961Sbms	if (sock == -1)
208166961Sbms		err(-1, "get_socket(SOCK_DGRAM) for test_ether_multi()", priv);
209166961Sbms	test_ether_multi(sock);
210166961Sbms	close(sock);
211166961Sbms}
212166961Sbms
213166961Sbmsstatic void
214166961Sbmsusage()
215166961Sbms{
216166961Sbms
217166961Sbms	fprintf(stderr, "usage: ethermulti -i ifname [-r] [-v]\n");
218166961Sbms	exit(EXIT_FAILURE);
219166961Sbms}
220166961Sbms
221166961Sbmsint
222166961Sbmsmain(int argc, char *argv[])
223166961Sbms{
224166961Sbms	int ch;
225166961Sbms
226166961Sbms	while ((ch = getopt(argc, argv, "i:rv")) != -1) {
227166961Sbms		switch (ch) {
228166961Sbms		case 'i':
229166961Sbms			ifname = optarg;
230166961Sbms			break;
231166961Sbms		case 'r':
232166961Sbms			dorandom = 1;	/* introduce non-determinism */
233166961Sbms			break;
234166961Sbms		case 'v':
235166961Sbms			verbose = 1;
236166961Sbms			break;
237166961Sbms		default:
238166961Sbms			usage();
239166961Sbms		}
240166961Sbms	}
241166961Sbms	if (ifname == NULL)
242166961Sbms		usage();
243166961Sbms
244166961Sbms	printf("1..1\n");
245166961Sbms	if (geteuid() != 0) {
246166961Sbms		errx(1, "Not running as root, can't run tests as non-root");
247166961Sbms		/*NOTREACHED*/
248166961Sbms	} else {
249166961Sbms		fprintf(stderr,
250166961Sbms		    "Running tests with ruid %d euid %d sock uid 0\n",
251166961Sbms		    getuid(), geteuid());
252166961Sbms		testsuite(PRIV_ASIS);
253166961Sbms	}
254166961Sbms	printf("ok 1 - ethermulti\n");
255166961Sbms	exit(0);
256166961Sbms}
257