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