From 75f1f9e04a532697c24bb3d1bb1dc1524a656d3d Mon Sep 17 00:00:00 2001 From: Hugo Santos Date: Mon, 30 Apr 2007 16:56:58 +0000 Subject: [PATCH] added two more test multicast applications: multicat/multisend. sockaddr_in6/in6_addr must be visible through , fixed that as well. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20926 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/posix/netinet/in.h | 7 + src/kits/network/dns/port_after.h | 17 - src/tests/kits/net/multicast/Jamfile | 4 + src/tests/kits/net/multicast/multicat.c | 381 +++++++++++++++++++++++ src/tests/kits/net/multicast/multisend.c | 268 ++++++++++++++++ 5 files changed, 660 insertions(+), 17 deletions(-) create mode 100644 src/tests/kits/net/multicast/multicat.c create mode 100644 src/tests/kits/net/multicast/multisend.c diff --git a/headers/posix/netinet/in.h b/headers/posix/netinet/in.h index 18c77aef07..a20b5300b8 100644 --- a/headers/posix/netinet/in.h +++ b/headers/posix/netinet/in.h @@ -8,6 +8,9 @@ #include #include +/* RFC 2553 states that these must be available through */ +#include + #ifdef __cplusplus extern "C" { #endif @@ -132,6 +135,10 @@ struct group_source_req { #define MCAST_JOIN_SOURCE_GROUP 22 /* group_source_req */ #define MCAST_LEAVE_SOURCE_GROUP 23 /* group_source_req */ +#define IPV6_MULTICAST_IF 24 /* int */ +#define IPV6_MULTICAST_HOPS 25 /* int */ +#define IPV6_MULTICAST_LOOP 26 /* int */ + #define __IPADDR(x) ((uint32_t)htonl((uint32_t)(x))) #define INADDR_ANY __IPADDR(0x00000000) diff --git a/src/kits/network/dns/port_after.h b/src/kits/network/dns/port_after.h index ce8943212d..d5090264c6 100644 --- a/src/kits/network/dns/port_after.h +++ b/src/kits/network/dns/port_after.h @@ -71,10 +71,6 @@ #endif #ifndef HAS_INET6_STRUCTS -/* Replace with structure from later rev of O/S if known. */ -struct in6_addr { - u_int8_t s6_addr[16]; -}; #define IN6ADDR_ANY_INIT \ {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -84,19 +80,6 @@ struct in6_addr { {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }} -/* Replace with structure from later rev of O/S if known. */ -struct sockaddr_in6 { -#ifdef HAVE_SA_LEN - u_int8_t sin6_len; /* length of this struct */ - u_int8_t sin6_family; /* AF_INET6 */ -#else - u_int16_t sin6_family; /* AF_INET6 */ -#endif - u_int16_t sin6_port; /* transport layer port # */ - u_int32_t sin6_flowinfo; /* IPv6 flow information */ - struct in6_addr sin6_addr; /* IPv6 address */ - u_int32_t sin6_scope_id; /* set of interfaces for a scope */ -}; #endif /* HAS_INET6_STRUCTS */ #ifdef BROKEN_IN6ADDR_INIT_MACROS diff --git a/src/tests/kits/net/multicast/Jamfile b/src/tests/kits/net/multicast/Jamfile index 5f3ad63279..63be5b3347 100644 --- a/src/tests/kits/net/multicast/Jamfile +++ b/src/tests/kits/net/multicast/Jamfile @@ -4,3 +4,7 @@ BinCommand multicast : multicast.cpp : libnetwork.so ; + + +BinCommand multicat : multicat.c : libnetwork.so ; +BinCommand multisend : multisend.c : libnetwork.so ; diff --git a/src/tests/kits/net/multicast/multicat.c b/src/tests/kits/net/multicast/multicat.c new file mode 100644 index 0000000000..61a1d1e543 --- /dev/null +++ b/src/tests/kits/net/multicast/multicat.c @@ -0,0 +1,381 @@ +/* + * Copyright 2003-2004 by Marco d'Itri + * This software is distributed under the terms of the GNU GPL. If we meet some + * day, and you think this stuff is worth it, you can buy me a beer in return. + * + */ + +#include +#include +#include +#include +#include /* strerror */ +#include +#include +#include +#include /* getopt_long */ +#include +#include +#include +#include +#include +#include +#include + +#define RECV_BUF_SIZE 64 * 1024 + +/* SSM and protocol-independent API from RFC3678 */ +#ifndef IP_ADD_SOURCE_MEMBERSHIP +# define IP_ADD_SOURCE_MEMBERSHIP 39 +struct ip_mreq_source { + struct in_addr imr_multiaddr; /* multicast group */ + struct in_addr imr_sourceaddr; /* source */ + struct in_addr imr_interface; /* local IP address of the interface */ +}; +#endif /* IP_ADD_SOURCE_MEMBERSHIP */ + +#ifndef MCAST_JOIN_GROUP +# define MCAST_JOIN_GROUP 42 +# define MCAST_JOIN_SOURCE_GROUP 46 +struct group_req { + uint32_t gr_interface; /* interface index */ + struct sockaddr_storage gr_group; /* group address */ +}; +struct group_source_req { + uint32_t gsr_interface; /* interface index */ + struct sockaddr_storage gsr_group; /* group address */ + struct sockaddr_storage gsr_source; /* source address */ +}; +#endif + + +#define v_printf(level, x...) \ + if (verbose >= level) \ + fprintf(stderr, x) + +/* prototypes*/ +//void chat(const int sock, const struct sockaddr_in6 saddr); +void chat(const int sock); +void dump(const int sock, int out_fd, const int dump_packets); +static int open_multicast_socket(const char *group, const char *port, + const char *source); +static int open_packet_file(void *buf, int n, const char *addr); +static void alarm_exit(int signaln); +static void usage(void); +static void err_quit(const char *, ...); +static void err_sys(const char *, ...); + +/* global variables */ +static struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "verbose", no_argument, NULL, 'v' }, + { "timeout", required_argument, NULL, 't' }, + { "output", required_argument, NULL, 'o' }, + { "source", required_argument, NULL, 's' }, + { NULL, 0, NULL, 0 } +}; + +static int verbose = 0; + +int main(int argc, char *argv[]) +{ + int timeout = 0; + int out_fd = STDOUT_FILENO; + int dump_packets = 0; + int ch, sock; + char *source = NULL; + + while ((ch = getopt_long(argc, argv, "hvo:pt:s:", longopts, 0)) > 0) { + switch (ch) { + case 'o': + out_fd = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666); + if (out_fd < 0) + err_sys("open"); + break; + case 'p': + dump_packets = 1; + break; + case 't': + timeout = atoi(optarg); + break; + case 's': + source = optarg; + break; + case 'v': + verbose++; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + sock = open_multicast_socket(argv[0], argv[1], source); + + if (timeout) { + signal(SIGALRM, alarm_exit); + alarm(timeout); + } + + chat(sock); + dump(sock, out_fd, dump_packets); + exit(0); +} + +//void chat(const int sock, const struct sockaddr_in6 saddr) +void chat(const int sock) +{ + fd_set in; + + while (1) { + int res; + + FD_ZERO(&in); + + FD_SET(0, &in); + FD_SET(sock, &in); + + res = select(sock + 1, &in, 0, 0, 0); + if (res < 0) + break; + + if (FD_ISSET(0, &in)) { + char buffer[8192]; + int red = read(0, buffer, sizeof(buffer) - 1); + buffer[red] = 0; +#if 0 + if (sendto(sock, buffer, red + 1, 0, (struct sockaddr *) &saddr, + sizeof(saddr)) < 0) +#else + if (send(sock, buffer, red + 1, 0) < 0) +#endif + err_sys("send"); + } + if (FD_ISSET(sock, &in)) { + struct sockaddr_in6 from; + socklen_t fromlen = sizeof(from); + char taddr[64]; + char buffer[8192]; + + int red = recvfrom(sock, buffer, sizeof(buffer), 0, + (struct sockaddr *) &from, &fromlen); + buffer[red] = 0; + + inet_ntop(AF_INET6, &from.sin6_addr, taddr, sizeof(taddr)); + + printf("<%s> %s\n", taddr, buffer); + } + } +} + +void dump(const int sock, int out_fd, const int dump_packets) { + int n; + struct sockaddr_storage sraddr; + struct sockaddr *raddr = (struct sockaddr *)&sraddr; + socklen_t rlen = sizeof(struct sockaddr_storage); + char buf[RECV_BUF_SIZE]; + + while ((n = recvfrom(sock, buf, sizeof(buf), 0, raddr, &rlen)) > 0) { + char address[NI_MAXHOST], port[NI_MAXSERV]; + + if (verbose >= 2) { + if (getnameinfo(raddr, rlen, address, sizeof(address), port, + sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) < 0) + err_sys("getnameinfo"); + printf("Received %d bytes from [%s]:%s.\n", n, address, port); + } + if (dump_packets) + out_fd = open_packet_file(buf, n, address); + if (write(out_fd, buf, n) != n) + err_sys("write"); + if (dump_packets) + if (close(out_fd) < 0) + err_sys("close"); + } + if (n < 0) + err_sys("recvfrom"); + + if (!dump_packets && out_fd != STDOUT_FILENO) + if (close(out_fd) < 0) + err_sys("close"); + + v_printf(1, "End of stream.\n"); +} + +static int open_multicast_socket(const char *group, const char *port, + const char *source) +{ + int fd; + int yes = 1; +#ifdef AF_INET6 + int err; + struct addrinfo hints, *res, *ai; + int level; + struct group_req gr; + struct group_source_req gsr; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + if ((err = getaddrinfo(group, port, &hints, &res)) != 0) + err_quit("getaddrinfo(%s, %s): %s", source, port, gai_strerror(err)); + for (ai = res; ai; ai = ai->ai_next) { + if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) + continue; /* ignore */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) + err_sys("setsockopt(SO_REUSEADDR)"); + if (bind(fd, ai->ai_addr, ai->ai_addrlen) == 0) + break; /* success */ + close(fd); + } + + if (connect(fd, ai->ai_addr, ai->ai_addrlen) < 0) + err_sys("connect"); + + if (!ai) + err_sys("bind"); + + switch (ai->ai_family) { + case AF_INET: + level = IPPROTO_IP; + break; + case AF_INET6: + level = IPPROTO_IPV6; + break; + default: + err_quit("FATAL: family %d is not known", ai->ai_family); + } + + if (source) { + struct addrinfo shints, *sai; + + memset(&shints, 0, sizeof(shints)); + shints.ai_family = ai->ai_family; + shints.ai_protocol = ai->ai_protocol; + + if ((err = getaddrinfo(source, port, &shints, &sai)) != 0) + err_quit("getaddrinfo(%s, %s): %s", source, port, + gai_strerror(err)); + + memcpy(&gsr.gsr_group, ai->ai_addr, ai->ai_addrlen); + memcpy(&gsr.gsr_source, sai->ai_addr, sai->ai_addrlen); + gsr.gsr_interface = 0; + if (setsockopt(fd, level, MCAST_JOIN_SOURCE_GROUP, &gsr, + sizeof(gsr)) < 0) + err_sys("setsockopt(MCAST_JOIN_SOURCE_GROUP)"); + freeaddrinfo(sai); + } else { + memcpy(&gr.gr_group, ai->ai_addr, ai->ai_addrlen); + gr.gr_interface = 0; + if (setsockopt(fd, level, MCAST_JOIN_GROUP, &gr, sizeof(gr)) < 0) + err_sys("setsockopt(MCAST_JOIN_GROUP)"); + } + + freeaddrinfo(res); +#else + struct hostent *hostinfo; + struct sockaddr_in saddr; + struct ip_mreq mr; + struct ip_mreq_source mrs; + + if ((hostinfo = gethostbyname(group)) == NULL) + err_quit("Host %s not found.", group); + + if (!IN_MULTICAST(ntohl(*hostinfo->h_addr))) + err_quit("%s is not a multicast address", group); + + if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) + err_sys("socket"); + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) + err_sys("setsockopt(SO_REUSEADDR)"); + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_addr = *(struct in_addr *) hostinfo->h_addr; + saddr.sin_port = htons(atoi(port)); + if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) + err_sys("bind"); + + if (source) { + mrs.imr_multiaddr = saddr.sin_addr; + mrs.imr_sourceaddr.s_addr = inet_addr(source); + mrs.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, &mrs, + sizeof(mrs)) < 0) + err_sys("setsockopt(IP_ADD_SOURCE_MEMBERSHIP)"); + } else { + mr.imr_multiaddr = saddr.sin_addr; + mr.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr, sizeof(mr))<0) + err_sys("setsockopt(IP_ADD_MEMBERSHIP)"); + } +#endif + + return fd; +} + +static int open_packet_file(void *buf, int n, const char *address) +{ + static unsigned long int counter = 0; + char filename[1024]; + int fd; + + snprintf(filename, sizeof(filename), "pkt-%lu-%s", counter++, address); + fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0666); + if (fd < 0) + err_sys("open"); + return fd; +} + +static void alarm_exit(int signaln) +{ + v_printf(1, "Timeout.\n"); + exit(0); +} + +static void usage(void) +{ + fprintf(stderr, +"Usage: multicat [OPTIONS...] GROUP PORT\n" +"\n" +" -o, --output=FILE write the stream to FILE\n" +" -p write every packet to a new file\n" +" -t, --timeout=NUM the program will exit after NUM seconds\n" +" -s, --source=ADDR join the SSM source group ADDR\n" +" -v, --verbose tell me more. Use multiple times for more details\n" +" -h, --help display this help and exit\n" +"\n" +"If --output is not specified, standard output is used.\n" +); + exit(0); +} + +/* Error routines */ +static void err_sys(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, ": %s\n", strerror(errno)); + va_end(ap); + exit(1); +} + +static void err_quit(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fputs("\n", stderr); + va_end(ap); + exit(1); +} + diff --git a/src/tests/kits/net/multicast/multisend.c b/src/tests/kits/net/multicast/multisend.c new file mode 100644 index 0000000000..14ace125de --- /dev/null +++ b/src/tests/kits/net/multicast/multisend.c @@ -0,0 +1,268 @@ +/* + * Copyright 2003-2004 by Marco d'Itri + * This software is distributed under the terms of the GNU GPL. If we meet some + * day, and you think this stuff is worth it, you can buy me a beer in return. + * + */ + +#include +#include +#include +#include +#include /* strerror */ +#include +#include +#include /* getopt_long */ +#include +#include +#include +#include +#include +#include +#include + +int open_multicast_socket(const char *group, const char *port, + const char *source, const int ttl); +static void err_sys(const char *fmt, ...); +static void err_quit(const char *fmt, ...); +static void usage(void); + +/* global variables */ +static struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "input", no_argument, NULL, 'i' }, + { "size", required_argument, NULL, 's' }, + { "bind", required_argument, NULL, 'b' }, + { "ttl", required_argument, NULL, 't' }, + { NULL, 0, NULL, 0 } +}; + +int main(int argc, char *argv[]) +{ + int fd, len, ch; + int opt_input = 0; + int ttl = 128; + int sndbuf_size = 1280; + char *source = NULL; + + while ((ch = getopt_long(argc, argv, "his:b:t:", longopts, 0)) > 0) { + switch (ch) { + case 's': + sndbuf_size = atoi(optarg); + break; + case 'i': + opt_input = 1; + break; + case 'b': + source = optarg; + break; + case 't': + ttl = atoi(optarg); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + fd = open_multicast_socket(argv[0], argv[1], source, ttl); + + if (opt_input) { + char buf[sndbuf_size]; + + while ((len = read(STDIN_FILENO, buf, sizeof(buf))) > 0) { + if (send(fd, buf, len, 0) < 0) + err_sys("send"); + } + } else { + struct utsname utsname; + char buf[1024]; + + uname(&utsname); + snprintf(buf, sizeof(buf), "Hello from %s!\n", utsname.nodename); + len = strlen(buf); + + while (1) { + if (send(fd, buf, len, 0) < 0) + err_sys("send"); + + sleep(1); + } + } + + exit(0); +} + +int open_multicast_socket(const char *group, const char *port, + const char *source, const int ttl) +{ + int fd; + int loop = 1; + +#ifdef AF_INET6 + int err; + struct addrinfo hints, *res, *ai; + int level, sockopt_h, sockopt_l; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + if ((err = getaddrinfo(group, port, &hints, &res)) != 0) + err_quit("getaddrinfo(%s, %s): %s", source, port, gai_strerror(err)); + for (ai = res; ai; ai = ai->ai_next) { + if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) + continue; /* ignore */ + if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0) + break; /* success */ + close(fd); + } + + if (!ai) + err_sys("connect"); + + switch (ai->ai_family) { + case AF_INET: + level = IPPROTO_IP; + sockopt_h = IP_MULTICAST_TTL; + sockopt_l = IP_MULTICAST_LOOP; + break; + case AF_INET6: + level = IPPROTO_IPV6; + sockopt_h = IPV6_MULTICAST_HOPS; + sockopt_l = IPV6_MULTICAST_LOOP; + break; + default: + err_quit("FATAL: family %d is not known", ai->ai_family); + } + + if (source) { + struct addrinfo shints, *sai; + int yes = 1; + + memset(&shints, 0, sizeof(shints)); + shints.ai_family = ai->ai_family; + shints.ai_protocol = ai->ai_protocol; + + if ((err = getaddrinfo(source, port, &shints, &sai)) != 0) + err_quit("getaddrinfo(%s, %s): %s", source, port, + gai_strerror(err)); + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) + err_sys("setsockopt(SO_REUSEADDR)"); + + if (bind(fd, sai->ai_addr, sai->ai_addrlen) < 0) + err_sys("bind"); + + freeaddrinfo(sai); + /* + struct ip_addr ifaddr = inet_addr(); + setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(struct in_addr)); + setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifidx, sizeof(unsigned int)); + man 7 netdevice + */ + } + + freeaddrinfo(res); + +#else + struct sockaddr_in addr; + struct hostent *hostinfo; + const int level = IPPROTO_IP; + const int sockopt_h = IP_MULTICAST_TTL; + const int sockopt_l = IP_MULTICAST_LOOP; + + if ((hostinfo = gethostbyname(group)) == NULL) + err_quit("Host %s not found.", group); + + /* create what looks like an ordinary UDP socket */ + if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) + err_sys("socket"); + + /* set up destination address */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr = *(struct in_addr *) hostinfo->h_addr; + addr.sin_port = htons(atoi(port)); + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + err_sys("connect"); + + if (source) { /* XXX */ + int yes = 1; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) + err_sys("setsockopt(SO_REUSEADDR)"); + } +#endif + + if (setsockopt(fd, level, sockopt_h, &ttl, sizeof(ttl)) < 0) + err_sys("setsockopt(IP_MULTICAST_TTL)"); + + if (loop == 0) + if (setsockopt(fd, level, sockopt_l, &loop, sizeof(loop)) < 0) + err_sys("setsockopt(IP_MULTICAST_LOOP)"); + + return fd; +} + + +static void usage(void) +{ + fprintf(stderr, +"Usage: multisend [OPTIONS...] GROUP PORT\n" +"\n" +" -b, --bind=ADDR bind the socket to address ADDR\n" +" -t, --ttl=NUM set the TTL to NUM\n" +" -h, --help display this help and exit\n" +"\n" +"If --ttl is not specified, 128 is used.\n" +); + exit(0); +} + +/* Error routines */ +static void err_sys(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, ": %s\n", strerror(errno)); + va_end(ap); + exit(1); +} + +static void err_quit(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fputs("\n", stderr); + va_end(ap); + exit(1); +} + +#if 0 +/* local network */ +#define HELLO_GROUP "224.0.0.3" +/* ? */ +#define HELLO_GROUP "225.0.0.37" +/* GLOP */ +#define HELLO_GROUP "233.49.235.42" +/* fw */ +#define HELLO_GROUP "239.113.42.66" +/* Organization-Local */ +#define HELLO_GROUP "239.192.42.66" +/* Site-Local */ +#define HELLO_GROUP "239.252.42.66" +/* Source Specific Multicast */ +#define HELLO_GROUP "232.1.1.1" +/* node local */ +#define HELLO_ADDR "FF01::1111" +#endif