Path: blob/main/tools/regression/netinet/msocket/msocket.c
39507 views
/*-1* Copyright (c) 2005 Robert N. M. Watson2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/types.h>27#include <sys/socket.h>2829#include <netinet/in.h>3031#include <arpa/inet.h>3233#include <err.h>34#include <errno.h>35#include <fcntl.h>36#include <stdio.h>37#include <string.h>38#include <unistd.h>3940/*41* Regression test for multicast sockets and options:42*43* - Check the defaults for ttl, if, and loopback. Make sure they can be set44* and then read.45*46* - Check that adding and removing multicast addresses seems to work.47*48* - Send a test message over loop back multicast and make sure it arrives.49*50* NB:51*52* Would be nice to use BPF or if_tap to actually check packet contents and53* layout, make sure that the ttl is set right, etc.54*55* Would be nice if attempts to use multicast options on TCP sockets returned56* an error, as the docs suggest it might.57*/5859#ifdef WARN_TCP60#define WARN_SUCCESS 0x00000001 /* Set for TCP to warn on success. */61#else62#define WARN_SUCCESS 0x0000000063#endif6465/*66* Multicast test address, picked arbitrarily. Will be used with the67* loopback interface.68*/69#define TEST_MADDR "224.100.100.100"7071/*72* Test that a given IP socket option (optname) has a default value of73* 'defaultv', that we can set it to 'modifiedv', and use 'fakev' as a dummy74* value that shouldn't be returned at any point during the tests. Perform75* the tests on the raw socket, tcp socket, and upd socket passed.76* 'optstring' is used in printing warnings and errors as needed.77*/78static void79test_u_char(int optname, const char *optstring, u_char defaultv,80u_char modifiedv, u_char fakev, const char *socktype, int sock,81int flags)82{83socklen_t socklen;84u_char uc;85int ret;8687/*88* Check that we read back the expected default.89*/90uc = fakev;91socklen = sizeof(uc);9293ret = getsockopt(sock, IPPROTO_IP, optname, &uc, &socklen);94if (ret < 0)95err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",96socktype, optstring);97if (ret == 0 && (flags & WARN_SUCCESS))98warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",99socktype, optstring);100if (uc != defaultv)101errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) default is "102"%d not %d", socktype, optstring, uc, defaultv);103104/*105* Set to a modifiedv value, read it back and make sure it got there.106*/107uc = modifiedv;108ret = setsockopt(sock, IPPROTO_IP, optname, &uc, sizeof(uc));109if (ret == -1)110err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, %s)",111socktype, optstring);112if (ret == 0 && (flags & WARN_SUCCESS))113warnx("WARN: setsockopt(%s, IPPROTO_IP, %s) returned 0",114socktype, optstring);115116uc = fakev;117socklen = sizeof(uc);118ret = getsockopt(sock, IPPROTO_IP, optname, &uc, &socklen);119if (ret < 0)120err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",121socktype, optstring);122if (ret == 0 && (flags & WARN_SUCCESS))123warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",124socktype, optstring);125if (uc != modifiedv)126errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) set value is "127"%d not %d", socktype, optstring, uc, modifiedv);128}129130/*131* test_in_addr() is like test_u_char(), only it runs on a struct in_addr132* (surprise).133*/134static void135test_in_addr(int optname, const char *optstring, struct in_addr defaultv,136struct in_addr modifiedv, struct in_addr fakev, const char *socktype,137int sock, int flags)138{139socklen_t socklen;140struct in_addr ia;141int ret;142143/*144* Check that we read back the expected default.145*/146ia = fakev;147socklen = sizeof(ia);148149ret = getsockopt(sock, IPPROTO_IP, optname, &ia, &socklen);150if (ret < 0)151err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",152socktype, optstring);153if (ret == 0 && (flags & WARN_SUCCESS))154warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",155socktype, optstring);156if (memcmp(&ia, &defaultv, sizeof(struct in_addr)))157errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) default is "158"%s not %s", socktype, optstring, inet_ntoa(ia),159inet_ntoa(defaultv));160161/*162* Set to a modifiedv value, read it back and make sure it got there.163*/164ia = modifiedv;165ret = setsockopt(sock, IPPROTO_IP, optname, &ia, sizeof(ia));166if (ret == -1)167err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, %s)",168socktype, optstring);169if (ret == 0 && (flags & WARN_SUCCESS))170warnx("WARN: setsockopt(%s, IPPROTO_IP, %s) returned 0",171socktype, optstring);172173ia = fakev;174socklen = sizeof(ia);175ret = getsockopt(sock, IPPROTO_IP, optname, &ia, &socklen);176if (ret < 0)177err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",178socktype, optstring);179if (ret == 0 && (flags & WARN_SUCCESS))180warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",181socktype, optstring);182if (memcmp(&ia, &modifiedv, sizeof(struct in_addr)))183errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) set value is "184"%s not %s", socktype, optstring, inet_ntoa(ia),185inet_ntoa(modifiedv));186}187188static void189test_ttl(int raw_sock, int tcp_sock, int udp_sock)190{191192test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243,193"raw_sock", raw_sock, 0);194test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243,195"tcp_sock", tcp_sock, WARN_SUCCESS);196test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243,197"udp_sock", udp_sock, 0);198}199200static void201test_loop(int raw_sock, int tcp_sock, int udp_sock)202{203204test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243,205"raw_sock", raw_sock, 0);206test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243,207"tcp_sock", tcp_sock, WARN_SUCCESS);208test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243,209"udp_sock", udp_sock, 0);210}211212static void213test_if(int raw_sock, int tcp_sock, int udp_sock)214{215struct in_addr defaultv, modifiedv, fakev;216217defaultv.s_addr = inet_addr("0.0.0.0");218219/* Should be valid on all hosts. */220modifiedv.s_addr = inet_addr("127.0.0.1");221222/* Should not happen. */223fakev.s_addr = inet_addr("255.255.255.255");224225test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv,226fakev, "raw_sock", raw_sock, 0);227test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv,228fakev, "tcp_sock", tcp_sock, WARN_SUCCESS);229test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv,230fakev, "udp_sock", udp_sock, 0);231}232233/*234* Add a multicast address to an interface. Warn if appropriate. No query235* interface so can't check if it's there directly; instead we have to try236* to add it a second time and make sure we get back EADDRINUSE.237*/238static void239test_add_multi(int sock, const char *socktype, struct ip_mreq imr,240int flags)241{242char buf[128];243int ret;244245ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr,246sizeof(imr));247if (ret < 0) {248strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);249err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "250"%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));251}252if (ret == 0 && (flags & WARN_SUCCESS)) {253strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);254warnx("WARN: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "255"%s, %s) returned 0", socktype, buf,256inet_ntoa(imr.imr_interface));257}258259/* Try to add a second time to make sure it got there. */260ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr,261sizeof(imr));262if (ret == 0) {263strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);264err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "265"%s, %s) dup returned 0", socktype, buf,266inet_ntoa(imr.imr_interface));267}268if (ret < 0 && errno != EADDRINUSE) {269strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);270err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "271"%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));272}273}274275/*276* Drop a multicast address from an interface. Warn if appropriate. No277* query interface so can't check if it's gone directly; instead we have to278* try to drop it a second time and make sure we get back EADDRNOTAVAIL.279*/280static void281test_drop_multi(int sock, const char *socktype, struct ip_mreq imr,282int flags)283{284char buf[128];285int ret;286287ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr,288sizeof(imr));289if (ret < 0) {290strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);291err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "292"%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));293}294if (ret == 0 && (flags & WARN_SUCCESS)) {295strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);296warnx("WARN: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "297"%s, %s) returned 0", socktype, buf,298inet_ntoa(imr.imr_interface));299}300301/* Try a second time to make sure it's gone. */302ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr,303sizeof(imr));304if (ret == 0) {305strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);306err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "307"%s, %s) returned 0", socktype, buf,308inet_ntoa(imr.imr_interface));309}310if (ret < 0 && errno != EADDRNOTAVAIL) {311strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);312err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "313"%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));314}315}316317/*318* Should really also test trying to add an invalid address, delete one319* that's not there, etc.320*/321static void322test_addr(int raw_sock, int tcp_sock, int udp_sock)323{324struct ip_mreq imr;325326/* Arbitrary. */327imr.imr_multiaddr.s_addr = inet_addr(TEST_MADDR);328329/* Localhost should be OK. */330imr.imr_interface.s_addr = inet_addr("127.0.0.1");331332test_add_multi(raw_sock, "raw_sock", imr, 0);333test_drop_multi(raw_sock, "raw_sock", imr, 0);334335test_add_multi(tcp_sock, "raw_sock", imr, WARN_SUCCESS);336test_drop_multi(tcp_sock, "raw_sock", imr, WARN_SUCCESS);337338test_add_multi(udp_sock, "raw_sock", imr, 0);339test_drop_multi(udp_sock, "raw_sock", imr, 0);340}341342/*343* Test an actual simple UDP message - send a single byte to an address we're344* subscribed to, and hope to get it back. We create a new UDP socket for345* this purpose because we will need to bind it.346*/347#define UDP_PORT 5012348static void349test_udp(void)350{351struct sockaddr_in sin;352struct ip_mreq imr;353struct in_addr if_addr;354char message;355ssize_t len;356int sock;357358sock = socket(PF_INET, SOCK_DGRAM, 0);359if (sock < 0)360err(-1, "FAIL: test_udp: socket(PF_INET, SOCK_DGRAM)");361362if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)363err(-1, "FAIL: test_udp: fcntl(F_SETFL, O_NONBLOCK)");364365bzero(&sin, sizeof(sin));366sin.sin_len = sizeof(sin);367sin.sin_family = AF_INET;368sin.sin_port = htons(UDP_PORT);369sin.sin_addr.s_addr = inet_addr(TEST_MADDR);370371if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)372err(-1, "FAIL: test_udp: bind(udp_sock, 127.0.0.1:%d",373UDP_PORT);374375/* Arbitrary. */376imr.imr_multiaddr.s_addr = inet_addr(TEST_MADDR);377378/* Localhost should be OK. */379imr.imr_interface.s_addr = inet_addr("127.0.0.1");380381/*382* Tell socket what interface to send on -- use localhost.383*/384if_addr.s_addr = inet_addr("127.0.0.1");385if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &if_addr,386sizeof(if_addr)) < 0)387err(-1, "test_udp: setsockopt(IPPROTO_IP, IP_MULTICAST_IF)");388389test_add_multi(sock, "udp_sock", imr, 0);390391bzero(&sin, sizeof(sin));392sin.sin_len = sizeof(sin);393sin.sin_family = AF_INET;394sin.sin_port = htons(UDP_PORT);395sin.sin_addr.s_addr = inet_addr(TEST_MADDR);396397message = 'A';398len = sizeof(message);399len = sendto(sock, &message, len, 0, (struct sockaddr *)&sin,400sizeof(sin));401if (len < 0)402err(-1, "test_udp: sendto");403404if (len != sizeof(message))405errx(-1, "test_udp: sendto: expected to send %d, instead %d",406sizeof(message), len);407408message = 'B';409len = sizeof(sin);410len = recvfrom(sock, &message, sizeof(message), 0,411(struct sockaddr *)&sin, &len);412if (len < 0)413err(-1, "test_udp: recvfrom");414415if (len != sizeof(message))416errx(-1, "test_udp: recvfrom: len %d != message len %d",417len, sizeof(message));418419if (message != 'A')420errx(-1, "test_udp: recvfrom: expected 'A', got '%c'",421message);422423test_drop_multi(sock, "udp_sock", imr, 0);424425close(sock);426}427#undef UDP_PORT428429int430main(int argc, char *argv[])431{432int raw_sock, tcp_sock, udp_sock;433434if (geteuid() != 0)435errx(-1, "FAIL: root privilege required");436437raw_sock = socket(PF_INET, SOCK_RAW, 0);438if (raw_sock == -1)439err(-1, "FAIL: socket(PF_INET, SOCK_RAW)");440441tcp_sock = socket(PF_INET, SOCK_STREAM, 0);442if (raw_sock == -1)443err(-1, "FAIL: socket(PF_INET, SOCK_STREAM)");444445udp_sock = socket(PF_INET, SOCK_DGRAM, 0);446if (raw_sock == -1)447err(-1, "FAIL: socket(PF_INET, SOCK_DGRAM)");448449test_ttl(raw_sock, tcp_sock, udp_sock);450test_loop(raw_sock, tcp_sock, udp_sock);451test_if(raw_sock, tcp_sock, udp_sock);452test_addr(raw_sock, tcp_sock, udp_sock);453454close(udp_sock);455close(tcp_sock);456close(raw_sock);457458test_udp();459460return (0);461}462463464