The net/ip/contiki/tools directory was not in proper place as it contains also non Contiki code. The net/ip/tools is a more suitable location. Change-Id: I1eb621680d4247d56d67ea977120f3c67c013599 Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
417 lines
9.8 KiB
C
417 lines
9.8 KiB
C
/*
|
|
* Copyright (c) 2015 Intel Corporation
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1) Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2) Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3) Neither the name of Intel Corporation nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/* Listen UDP (unicast or multicast) messages from tun device and send
|
|
* them back to client that is running in qemu.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <poll.h>
|
|
#include <errno.h>
|
|
#include <arpa/inet.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <sys/ioctl.h>
|
|
#include <net/if.h>
|
|
#include <linux/ipv6.h>
|
|
#include <ifaddrs.h>
|
|
|
|
#define SERVER_PORT 4242
|
|
#define CLIENT_PORT 8484
|
|
#define MAX_BUF_SIZE 1280 /* min IPv6 MTU, the actual data is smaller */
|
|
#define MAX_TIMEOUT 3 /* in seconds */
|
|
|
|
static inline void reverse(unsigned char *buf, int len)
|
|
{
|
|
int i, last = len - 1;
|
|
|
|
for(i = 0; i < len/2; i++) {
|
|
unsigned char tmp = buf[i];
|
|
buf[i] = buf[last - i];
|
|
buf[last - i] = tmp;
|
|
}
|
|
}
|
|
|
|
static int get_ifindex(const char *name)
|
|
{
|
|
struct ifreq ifr;
|
|
int sk, err;
|
|
|
|
if (!name)
|
|
return -1;
|
|
|
|
sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
|
|
if (sk < 0)
|
|
return -1;
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) - 1);
|
|
|
|
err = ioctl(sk, SIOCGIFINDEX, &ifr);
|
|
|
|
close(sk);
|
|
|
|
if (err < 0)
|
|
return -1;
|
|
|
|
return ifr.ifr_ifindex;
|
|
}
|
|
|
|
static int get_socket(int family)
|
|
{
|
|
int fd;
|
|
|
|
fd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (fd < 0) {
|
|
perror("socket");
|
|
exit(-errno);
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
static int bind_device(int fd, const char *interface, void *addr, int len)
|
|
{
|
|
struct ifreq ifr;
|
|
int ret, val = 1;
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), interface);
|
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
|
|
(void *)&ifr, sizeof(ifr)) < 0) {
|
|
perror("SO_BINDTODEVICE");
|
|
exit(-errno);
|
|
}
|
|
|
|
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
|
|
|
|
ret = bind(fd, (struct sockaddr *)addr, len);
|
|
if (ret < 0) {
|
|
perror("bind");
|
|
exit(-errno);
|
|
}
|
|
}
|
|
|
|
static int receive(int fd, unsigned char *buf, int buflen,
|
|
struct sockaddr *addr, socklen_t *addrlen)
|
|
{
|
|
int ret;
|
|
|
|
ret = recvfrom(fd, buf, buflen, 0, addr, addrlen);
|
|
if (ret <= 0) {
|
|
perror("recv");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int reply(int fd, unsigned char *buf, int buflen,
|
|
struct sockaddr *addr, socklen_t addrlen)
|
|
{
|
|
int ret;
|
|
|
|
ret = sendto(fd, buf, buflen, 0, addr, addrlen);
|
|
if (ret < 0)
|
|
perror("send");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int receive_and_reply(fd_set *rfds, int fd_recv, int fd_send,
|
|
unsigned char *buf, int buflen)
|
|
{
|
|
if (FD_ISSET(fd_recv, rfds)) {
|
|
struct sockaddr from;
|
|
socklen_t fromlen;
|
|
int ret;
|
|
|
|
ret = receive(fd_recv, buf, buflen, &from, &fromlen);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
reverse(buf, ret);
|
|
|
|
ret = reply(fd_send, buf, ret, &from, fromlen);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
fprintf(stderr, ".");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define MY_MCAST_ADDR6 \
|
|
{ { { 0xff,0x84,0,0,0,0,0,0,0,0,0,0,0,0,0,0x2 } } } /* ff84::2 */
|
|
|
|
#define MY_MCAST_ADDR4 "239.192.0.2"
|
|
|
|
int family_to_level(int family)
|
|
{
|
|
switch (family) {
|
|
case AF_INET:
|
|
return IPPROTO_IP;
|
|
case AF_INET6:
|
|
return IPPROTO_IPV6;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int join_mc_group(int sock, int ifindex, int family, void *addr,
|
|
int addr_len)
|
|
{
|
|
struct group_req req;
|
|
int ret, off = 0;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
req.gr_interface = ifindex;
|
|
memcpy(&req.gr_group, addr, addr_len);
|
|
|
|
ret = setsockopt(sock, family_to_level(family), MCAST_JOIN_GROUP,
|
|
&req, sizeof(req));
|
|
if (ret < 0)
|
|
perror("setsockopt(MCAST_JOIN_GROUP)");
|
|
|
|
switch (family) {
|
|
case AF_INET:
|
|
ret = setsockopt(sock, family_to_level(family),
|
|
IP_MULTICAST_LOOP, &off, sizeof(off));
|
|
break;
|
|
case AF_INET6:
|
|
ret = setsockopt(sock, family_to_level(family),
|
|
IPV6_MULTICAST_LOOP, &off, sizeof(off));
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int get_address(int ifindex, int family, void *address)
|
|
{
|
|
struct ifaddrs *ifaddr, *ifa;
|
|
int err = -ENOENT;
|
|
char name[IF_NAMESIZE];
|
|
|
|
if (!if_indextoname(ifindex, name))
|
|
return -EINVAL;
|
|
|
|
if (getifaddrs(&ifaddr) < 0) {
|
|
err = -errno;
|
|
fprintf(stderr, "Cannot get addresses err %d/%s",
|
|
err, strerror(-err));
|
|
return err;
|
|
}
|
|
|
|
for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
|
|
if (!ifa->ifa_addr)
|
|
continue;
|
|
|
|
if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 &&
|
|
ifa->ifa_addr->sa_family == family) {
|
|
if (family == AF_INET) {
|
|
struct sockaddr_in *in4 = (struct sockaddr_in *)
|
|
ifa->ifa_addr;
|
|
if (in4->sin_addr.s_addr == INADDR_ANY)
|
|
continue;
|
|
if ((in4->sin_addr.s_addr & IN_CLASSB_NET) ==
|
|
((in_addr_t) 0xa9fe0000))
|
|
continue;
|
|
memcpy(address, &in4->sin_addr,
|
|
sizeof(struct in_addr));
|
|
} else if (family == AF_INET6) {
|
|
struct sockaddr_in6 *in6 =
|
|
(struct sockaddr_in6 *)ifa->ifa_addr;
|
|
if (memcmp(&in6->sin6_addr, &in6addr_any,
|
|
sizeof(struct in6_addr)) == 0)
|
|
continue;
|
|
if (IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr))
|
|
continue;
|
|
|
|
memcpy(address, &in6->sin6_addr,
|
|
sizeof(struct in6_addr));
|
|
} else {
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
err = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
out:
|
|
freeifaddrs(ifaddr);
|
|
return err;
|
|
}
|
|
|
|
extern int optind, opterr, optopt;
|
|
extern char *optarg;
|
|
|
|
/* The application returns:
|
|
* < 0 : connection or similar error
|
|
* 0 : no errors, all tests passed
|
|
* > 0 : could not send all the data to server
|
|
*/
|
|
int main(int argc, char**argv)
|
|
{
|
|
int c, ret, fd4, fd6, fd4m, fd6m, i = 0, timeout = 0;
|
|
struct sockaddr_in6 addr6_recv = { 0 }, maddr6 = { 0 };
|
|
struct in6_addr mcast6_addr = MY_MCAST_ADDR6;
|
|
struct in_addr mcast4_addr = { 0 };
|
|
struct sockaddr_in addr4_recv = { 0 }, maddr4 = { 0 };
|
|
int family;
|
|
unsigned char buf[MAX_BUF_SIZE];
|
|
char addr_buf[INET6_ADDRSTRLEN];
|
|
const struct in6_addr any = IN6ADDR_ANY_INIT;
|
|
const char *interface = NULL;
|
|
fd_set rfds;
|
|
struct timeval tv = {};
|
|
int ifindex = -1;
|
|
|
|
opterr = 0;
|
|
|
|
while ((c = getopt(argc, argv, "i:")) != -1) {
|
|
switch (c) {
|
|
case 'i':
|
|
interface = optarg;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!interface) {
|
|
printf("usage: %s -i <iface>\n", argv[0]);
|
|
printf("\t-i Use this network interface.\n");
|
|
exit(-EINVAL);
|
|
}
|
|
|
|
ifindex = get_ifindex(interface);
|
|
if (ifindex < 0) {
|
|
printf("Invalid interface %s\n", interface);
|
|
exit(-EINVAL);
|
|
}
|
|
|
|
addr4_recv.sin_family = AF_INET;
|
|
addr4_recv.sin_port = htons(SERVER_PORT);
|
|
|
|
/* We want to bind to global unicast address here so that
|
|
* we can listen correct addresses. We do not want to listen
|
|
* link local addresses in this test.
|
|
*/
|
|
get_address(ifindex, AF_INET, &addr4_recv.sin_addr);
|
|
printf("IPv4: binding to %s\n",
|
|
inet_ntop(AF_INET, &addr4_recv.sin_addr,
|
|
addr_buf, sizeof(addr_buf)));
|
|
|
|
addr6_recv.sin6_family = AF_INET6;
|
|
addr6_recv.sin6_port = htons(SERVER_PORT);
|
|
|
|
/* Bind to global unicast address instead of ll address */
|
|
get_address(ifindex, AF_INET6, &addr6_recv.sin6_addr);
|
|
printf("IPv6: binding to %s\n",
|
|
inet_ntop(AF_INET6, &addr6_recv.sin6_addr,
|
|
addr_buf, sizeof(addr_buf)));
|
|
|
|
memcpy(&maddr6.sin6_addr, &mcast6_addr, sizeof(struct in6_addr));
|
|
maddr6.sin6_family = AF_INET6;
|
|
maddr6.sin6_port = htons(SERVER_PORT);
|
|
|
|
mcast4_addr.s_addr = inet_addr(MY_MCAST_ADDR4);
|
|
memcpy(&maddr4.sin_addr, &mcast4_addr, sizeof(struct in_addr));
|
|
maddr4.sin_family = AF_INET;
|
|
maddr4.sin_port = htons(SERVER_PORT);
|
|
|
|
fd4 = get_socket(AF_INET);
|
|
fd6 = get_socket(AF_INET6);
|
|
fd4m = get_socket(AF_INET);
|
|
fd6m = get_socket(AF_INET6);
|
|
|
|
bind_device(fd4, interface, &addr4_recv, sizeof(addr4_recv));
|
|
bind_device(fd6, interface, &addr6_recv, sizeof(addr6_recv));
|
|
|
|
bind_device(fd4m, interface, &maddr4, sizeof(maddr4));
|
|
bind_device(fd6m, interface, &maddr6, sizeof(maddr6));
|
|
|
|
join_mc_group(fd4m, ifindex, AF_INET, &maddr4, sizeof(maddr4));
|
|
join_mc_group(fd6m, ifindex, AF_INET6, &maddr6, sizeof(maddr6));
|
|
|
|
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
|
|
|
|
while (1) {
|
|
int fd = MAX(fd4m, fd6m);
|
|
|
|
FD_ZERO(&rfds);
|
|
FD_SET(fd4, &rfds);
|
|
FD_SET(fd6, &rfds);
|
|
FD_SET(fd4m, &rfds);
|
|
FD_SET(fd6m, &rfds);
|
|
|
|
tv.tv_sec = MAX_TIMEOUT;
|
|
tv.tv_usec = 0;
|
|
|
|
ret = select(fd + 1, &rfds, NULL, NULL, &tv);
|
|
if (ret < 0) {
|
|
perror("select");
|
|
break;
|
|
} else if (ret == 0) {
|
|
continue;
|
|
}
|
|
|
|
/* Unicast IPv4 */
|
|
if (receive_and_reply(&rfds, fd4, fd4, buf, sizeof(buf)) < 0)
|
|
break;
|
|
|
|
/* Unicast IPv6 */
|
|
if (receive_and_reply(&rfds, fd6, fd6, buf, sizeof(buf)) < 0)
|
|
break;
|
|
|
|
/* Multicast IPv4 */
|
|
if (receive_and_reply(&rfds, fd4m, fd4, buf, sizeof(buf)) < 0)
|
|
break;
|
|
|
|
/* Multicast IPv6 */
|
|
if (receive_and_reply(&rfds, fd6m, fd6, buf, sizeof(buf)) < 0)
|
|
break;
|
|
}
|
|
|
|
close(fd4);
|
|
close(fd6);
|
|
close(fd4m);
|
|
close(fd6m);
|
|
|
|
printf("\n");
|
|
|
|
exit(0);
|
|
}
|