#include <sys/cdefs.h>
__RCSID("$NetBSD: t_pktinfo_send.c,v 1.3 2017/12/30 22:02:34 gson Exp $");
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <rump/rump.h>
#include <rump/rump_syscalls.h>
#include "h_macros.h"
#include "../config/netconfig.c"
#include <atf-c.h>
#define SERVERPORT 54321
#define CLIENTPORT 12345
char message[128] = "Hello IP_PKTINFO";
static void
setup_test_environment(void)
{
RZ(rump_init());
netcfg_rump_if("lo0", "127.0.0.2", "255.0.0.0");
netcfg_rump_if("lo0", "127.0.0.3", "255.0.0.0");
netcfg_rump_if("lo0", "127.0.0.4", "255.0.0.0");
netcfg_rump_if("lo0", "127.0.0.5", "255.0.0.0");
}
static void
sock_in_init(struct sockaddr_in *sin, const char *addr, in_port_t port)
{
memset(sin, 0, sizeof(struct sockaddr_in));
sin->sin_family = AF_INET;
sin->sin_port = htons(port);
inet_pton(AF_INET, addr, &sin->sin_addr);
}
static int
sock_bind(int sock, const char *addr, in_port_t port)
{
struct sockaddr_in bindaddr;
sock_in_init(&bindaddr, addr, port);
return rump_sys_bind(sock,
(struct sockaddr *)&bindaddr, sizeof(bindaddr));
}
static int
udp_server(const char *addr, in_port_t port)
{
int s, rv;
RL(s = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
RL(rv = sock_bind(s, addr, port));
return s;
}
static int
addrcmp(struct sockaddr_in *sin, const char *addr)
{
struct in_addr inaddr;
inet_pton(AF_INET, addr, &inaddr);
return memcmp(&inaddr, &sin->sin_addr, sizeof(inaddr));
}
static ssize_t
sendto_pktinfo(int s, const void *buf, size_t len, int flags,
const char *src, const char *dst, in_port_t dstport)
{
struct sockaddr_in to;
struct msghdr msg;
char cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo)) * 2];
struct iovec vec;
struct cmsghdr *cmsg;
struct in_pktinfo *pi;
struct in_addr addr;
sock_in_init(&to, dst, dstport);
vec.iov_base = __UNCONST(buf);
vec.iov_len = len;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
msg.msg_name = (caddr_t)&to;
msg.msg_namelen = sizeof(to);
msg.msg_control = cmsgbuf;
msg.msg_controllen = 0;
cmsg = (struct cmsghdr *)cmsgbuf;
msg.msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo));
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
pi = (struct in_pktinfo *)CMSG_DATA(cmsg);
memset(pi, 0, sizeof(*pi));
inet_pton(AF_INET, src, &addr);
if (ntohl(addr.s_addr) >> 24 == 0)
pi->ipi_ifindex = ntohl(addr.s_addr) & 0xffffff;
else {
pi->ipi_addr = addr;
}
if (msg.msg_controllen == 0)
msg.msg_control = NULL;
return rump_sys_sendmsg(s, &msg, flags);
}
static void
try_sendmsg_pktinfo(int client, int server, const char *data, size_t datalen,
const char *src, const char *dst, in_port_t dstport)
{
struct sockaddr_in from;
socklen_t fromlen;
int rv;
char buf[sizeof(message) * 2];
RL(rv = sendto_pktinfo(client, data, datalen, 0,
src, dst, dstport));
fromlen = sizeof(from);
RL(rv = rump_sys_recvfrom(server, buf, sizeof(buf), 0,
(struct sockaddr *)&from, &fromlen));
ATF_REQUIRE_MSG(addrcmp(&from, src) == 0,
"source address of received packet is %s, must be %s",
inet_ntoa(from.sin_addr), src);
}
static void
do_send_pktinfo_tests(int client, int server, const char *data, size_t datalen)
{
struct sockaddr_in sa_before, sa_after;
socklen_t sa_beforelen, sa_afterlen;
int rv;
char ipbuf1[sizeof("255.255.255.255")];
char ipbuf2[sizeof("255.255.255.255")];
sa_beforelen = sizeof(sa_before);
RL(rv = rump_sys_getsockname(client,
(struct sockaddr *)&sa_before, &sa_beforelen));
try_sendmsg_pktinfo(client, server, data, datalen,
"127.0.0.2", "127.0.0.1", SERVERPORT);
try_sendmsg_pktinfo(client, server, data, datalen,
"127.0.0.3", "127.0.0.1", SERVERPORT);
try_sendmsg_pktinfo(client, server, data, datalen,
"127.0.0.4", "127.0.0.1", SERVERPORT);
try_sendmsg_pktinfo(client, server, data, datalen,
"127.0.0.5", "127.0.0.1", SERVERPORT);
sa_afterlen = sizeof(sa_after);
RL(rv = rump_sys_getsockname(client,
(struct sockaddr *)&sa_after, &sa_afterlen));
inet_ntop(AF_INET, &sa_before.sin_addr, ipbuf1, sizeof(ipbuf1));
inet_ntop(AF_INET, &sa_after.sin_addr, ipbuf2, sizeof(ipbuf2));
ATF_REQUIRE_MSG(sa_before.sin_addr.s_addr == sa_after.sin_addr.s_addr,
"sockaddr is different from before send. before=%s, after=%s",
ipbuf1, ipbuf2);
}
ATF_TC(pktinfo_send_unbound);
ATF_TC_HEAD(pktinfo_send_unbound, tc)
{
atf_tc_set_md_var(tc, "descr", "sendmsg with IP_PKTINFO");
}
ATF_TC_BODY(pktinfo_send_unbound, tc)
{
int client, server;
setup_test_environment();
server = udp_server("127.0.0.1", SERVERPORT);
RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
do_send_pktinfo_tests(client, server, message, strlen(message));
rump_sys_close(client);
rump_sys_close(server);
}
ATF_TC(pktinfo_send_bindany);
ATF_TC_HEAD(pktinfo_send_bindany, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg with IP_PKTINFO on bind(INADDR_ANY) socket");
}
ATF_TC_BODY(pktinfo_send_bindany, tc)
{
int client, server, rv;
setup_test_environment();
server = udp_server("127.0.0.1", SERVERPORT);
RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
RL(rv = sock_bind(client, "0.0.0.0", 0));
do_send_pktinfo_tests(client, server, message, strlen(message));
rump_sys_close(client);
rump_sys_close(server);
}
ATF_TC(pktinfo_send_bindaddr);
ATF_TC_HEAD(pktinfo_send_bindaddr, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg with IP_PKTINFO on bind(addr:0) socket");
}
ATF_TC_BODY(pktinfo_send_bindaddr, tc)
{
int client, server, rv;
setup_test_environment();
server = udp_server("127.0.0.1", SERVERPORT);
RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
RL(rv = sock_bind(client, "127.0.0.1", 0));
do_send_pktinfo_tests(client, server, message, strlen(message));
rump_sys_close(client);
rump_sys_close(server);
}
ATF_TC(pktinfo_send_bindport);
ATF_TC_HEAD(pktinfo_send_bindport, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg with IP_PKTINFO on bind(INADDR_ANY:12345) socket");
}
ATF_TC_BODY(pktinfo_send_bindport, tc)
{
int client, server, rv;
setup_test_environment();
server = udp_server("127.0.0.1", SERVERPORT);
RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
RL(rv = sock_bind(client, "0.0.0.0", CLIENTPORT));
do_send_pktinfo_tests(client, server, message, strlen(message));
rump_sys_close(client);
rump_sys_close(server);
}
ATF_TC(pktinfo_send_bindaddrport);
ATF_TC_HEAD(pktinfo_send_bindaddrport, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg with IP_PKTINFO on bind(addr:12345) socket");
}
ATF_TC_BODY(pktinfo_send_bindaddrport, tc)
{
int client, server, rv;
setup_test_environment();
server = udp_server("127.0.0.1", SERVERPORT);
RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
RL(rv = sock_bind(client, "127.0.0.2", CLIENTPORT));
do_send_pktinfo_tests(client, server, message, strlen(message));
rump_sys_close(client);
rump_sys_close(server);
}
ATF_TC(pktinfo_send_bindother);
ATF_TC_HEAD(pktinfo_send_bindother, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg with IP_PKTINFO from address bound by other socket");
}
ATF_TC_BODY(pktinfo_send_bindother, tc)
{
int client, server, other, rv;
setup_test_environment();
server = udp_server("127.0.0.1", SERVERPORT);
other = udp_server("127.0.0.2", CLIENTPORT);
RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
RL(rv = sock_bind(client, "127.0.0.3", CLIENTPORT));
do_send_pktinfo_tests(client, server, message, strlen(message));
rump_sys_close(client);
rump_sys_close(server);
rump_sys_close(other);
}
ATF_TC(pktinfo_send_connected);
ATF_TC_HEAD(pktinfo_send_connected, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg with IP_PKTINFO on connected socket");
}
ATF_TC_BODY(pktinfo_send_connected, tc)
{
struct sockaddr_in connectaddr;
int client, server, rv;
setup_test_environment();
server = udp_server("127.0.0.1", SERVERPORT);
RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
sock_in_init(&connectaddr, "127.0.0.1", SERVERPORT);
RL(rv = rump_sys_connect(client,
(struct sockaddr *)&connectaddr, sizeof(connectaddr)));
rv = sendto_pktinfo(client, message, strlen(message), 0,
"127.0.0.2", "127.0.0.1", SERVERPORT);
ATF_REQUIRE_MSG(rv == -1,
"sendmsg w/IP_PKTINFO on connected socket should be error,"
" but success");
ATF_REQUIRE_MSG(errno == EISCONN,
"sendmsg with in-use address:port should be EISCONN,"
" but got %s", strerror(errno));
rump_sys_close(client);
rump_sys_close(server);
}
ATF_TC(pktinfo_send_notown);
ATF_TC_HEAD(pktinfo_send_notown, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg with IP_PKTINFO from no-own address");
}
ATF_TC_BODY(pktinfo_send_notown, tc)
{
int client, server, rv;
setup_test_environment();
server = udp_server("127.0.0.1", SERVERPORT);
RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
rv = sendto_pktinfo(client, message, strlen(message), 0,
"127.0.0.100", "127.0.0.1", SERVERPORT);
ATF_REQUIRE_MSG(rv == -1,
"sendmsg w/IP_PKTINFO from unavailable address"
" should be error, but success");
ATF_REQUIRE_MSG(errno == EADDRNOTAVAIL,
"sendmsg with in-use address:port"
" should be EADDRNOTAVAIL, but got %s", strerror(errno));
rump_sys_close(client);
rump_sys_close(server);
}
ATF_TC(pktinfo_send_notown_bind);
ATF_TC_HEAD(pktinfo_send_notown_bind, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg with IP_PKTINFO from no-own address on bind socket");
}
ATF_TC_BODY(pktinfo_send_notown_bind, tc)
{
int client, server, rv;
setup_test_environment();
server = udp_server("127.0.0.1", SERVERPORT);
RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
RL(rv = sock_bind(client, "127.0.0.2", CLIENTPORT));
rv = sendto_pktinfo(client, message, strlen(message), 0,
"127.0.0.100", "127.0.0.1", SERVERPORT);
ATF_REQUIRE_MSG(rv == -1,
"sendmsg w/IP_PKTINFO from unavailable address should be error,"
" but success");
ATF_REQUIRE_MSG(errno == EADDRNOTAVAIL,
"sendmsg with in-use address:port should be EADDRNOTAVAIL,"
" but got %s", strerror(errno));
rump_sys_close(client);
rump_sys_close(server);
}
static u_int16_t
in_cksum(uint16_t *p, int len)
{
u_int32_t sum;
for (sum = 0; len >= 2; len -= 2)
sum += *p++;
if (len == 1)
sum += ntohs(*(uint8_t *)p * 256);
sum = (sum >> 16) + (sum & 0xffff);
sum = (sum >> 16) + (sum & 0xffff);
return ~sum;
}
ATF_TC(pktinfo_send_rawip);
ATF_TC_HEAD(pktinfo_send_rawip, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg raw-ip with IP_PKTINFO");
}
ATF_TC_BODY(pktinfo_send_rawip, tc)
{
struct icmp icmp;
size_t icmplen;
int client, server;
setup_test_environment();
RL(server = rump_sys_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP));
RL(client = rump_sys_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP));
memset(&icmp, 0, sizeof(icmp));
icmp.icmp_type = ICMP_ECHOREPLY;
icmp.icmp_id = htons(getpid());
icmp.icmp_cksum = in_cksum((uint16_t *)&icmp, sizeof(icmp));
icmplen = sizeof(icmp);
try_sendmsg_pktinfo(client, server, (const char *)&icmp, icmplen,
"127.0.0.2", "127.0.0.1", 0);
rump_sys_close(client);
rump_sys_close(server);
}
ATF_TC(pktinfo_send_rawip_notown);
ATF_TC_HEAD(pktinfo_send_rawip_notown, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg raw-ip with IP_PKTINFO from no-own address");
}
ATF_TC_BODY(pktinfo_send_rawip_notown, tc)
{
struct icmp icmp;
size_t icmplen;
int client, rv;
setup_test_environment();
RL(client = rump_sys_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP));
memset(&icmp, 0, sizeof(icmp));
icmp.icmp_type = ICMP_ECHOREPLY;
icmp.icmp_id = htons(getpid());
icmp.icmp_cksum = in_cksum((uint16_t *)&icmp, sizeof(icmp));
icmplen = sizeof(icmp);
rv = sendto_pktinfo(client, (const char *)&icmp, icmplen, 0,
"127.0.0.100", "127.0.0.1", 0);
ATF_REQUIRE_MSG(rv == -1,
"sendmsg w/IP_PKTINFO from unavailable address"
" should be error, but success");
ATF_REQUIRE_MSG(errno == EADDRNOTAVAIL,
"sendmsg with in-use address:port"
" should be EADDRNOTAVAIL, but got %s", strerror(errno));
rump_sys_close(client);
}
ATF_TC(pktinfo_send_invalidarg);
ATF_TC_HEAD(pktinfo_send_invalidarg, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg IP_PKTINFO with invalid argument");
}
ATF_TC_BODY(pktinfo_send_invalidarg, tc)
{
int client, rv;
setup_test_environment();
RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
rv = sendto_pktinfo(client, message, strlen(message), 0,
"0.0.0.0", "127.0.0.1", SERVERPORT);
ATF_REQUIRE_MSG(rv != -1,
"sendmsg w/IP_PKTINFO ipi_ifindex=0, ipi_addr=0"
" does nothing (no error), but error %s", strerror(errno));
rv = sendto_pktinfo(client, message, strlen(message), 0,
"0.0.0.99", "127.0.0.1", SERVERPORT);
ATF_REQUIRE_MSG(rv == -1,
"sendmsg w/IP_PKTINFO from unavailable ifindex"
" should be error, but success");
ATF_REQUIRE_MSG(errno == EADDRNOTAVAIL,
"sendmsg with in-use address:port"
" should be EADDRNOTAVAIL, but got %s", strerror(errno));
rump_sys_close(client);
}
ATF_TC(pktinfo_send_ifindex);
ATF_TC_HEAD(pktinfo_send_ifindex, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg with IP_PKTINFO to specified interface");
}
ATF_TC_BODY(pktinfo_send_ifindex, tc)
{
pid_t child;
int channel[2], i;
char ifname2[IFNAMSIZ];
char ifname3[IFNAMSIZ];
char ethername[MAXPATHLEN];
char token;
snprintf(ethername, sizeof(ethername),
"t_pktinfo_send.link.%u", getpid());
RL(pipe(channel));
child = fork();
RZ(rump_init());
netcfg_rump_makeshmif(ethername, ifname2);
netcfg_rump_makeshmif(ethername, ifname3);
switch (child) {
case -1:
atf_tc_fail_errno("fork failed");
case 0:
{
int client, rv;
netcfg_rump_if(ifname2, "192.168.2.1", "255.255.255.0");
netcfg_rump_if(ifname3, "192.168.0.1", "255.255.0.0");
RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
close(channel[1]);
ATF_CHECK(read(channel[0], &token, 1) == 1 &&
token == 'U');
close(channel[0]);
for (i = 0; i < 3; i++) {
snprintf(message, sizeof(message), "Hello PKTINFO %d", i);
RL(rv = sendto_pktinfo(client, message, strlen(message),
0, "0.0.0.3", "192.168.2.2", SERVERPORT));
}
rump_sys_close(client);
pause();
}
break;
default:
{
struct sockaddr_in from;
socklen_t fromlen;
int server, rv;
char buf[sizeof(message)];
netcfg_rump_if(ifname2, "192.168.2.2", "255.255.255.0");
netcfg_rump_if(ifname3, "192.168.0.2", "255.255.255.0");
server = udp_server("0.0.0.0", SERVERPORT);
close(channel[0]);
ATF_CHECK(write(channel[1], "U", 1) == 1);
close(channel[1]);
memset(buf, 0, sizeof(buf));
fromlen = sizeof(from);
RL(rv = rump_sys_recvfrom(server, buf, sizeof(buf), 0,
(struct sockaddr *)&from, &fromlen));
printf("%s: received \"%s\" from %s:%u\n", __func__,
buf,
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
ATF_REQUIRE_MSG(addrcmp(&from, "192.168.0.1") == 0,
"source address of received packet is %s,"
" must be %s",
inet_ntoa(from.sin_addr), "192.168.0.1");
rump_sys_close(server);
kill(child, SIGKILL);
}
break;
}
}
ATF_TC(pktinfo_send_multicast);
ATF_TC_HEAD(pktinfo_send_multicast, tc)
{
atf_tc_set_md_var(tc, "descr",
"sendmsg with IP_PKTINFO to multicast address"
" and specified interface");
}
ATF_TC_BODY(pktinfo_send_multicast, tc)
{
pid_t child;
int channel[2];
char ifname2[IFNAMSIZ];
char ifname3[IFNAMSIZ];
char ethername[MAXPATHLEN];
char token;
snprintf(ethername, sizeof(ethername),
"t_pktinfo_send.link.%u", getpid());
RL(pipe(channel));
child = fork();
RZ(rump_init());
netcfg_rump_makeshmif(ethername, ifname2);
netcfg_rump_makeshmif(ethername, ifname3);
switch (child) {
case -1:
atf_tc_fail_errno("fork failed");
case 0:
{
int client, rv;
netcfg_rump_if(ifname2, "192.168.2.1", "255.255.255.0");
netcfg_rump_if(ifname3, "192.168.0.1", "255.255.0.0");
RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0));
close(channel[1]);
ATF_CHECK(read(channel[0], &token, 1) == 1 &&
token == 'U');
close(channel[0]);
RL(rv = sendto_pktinfo(client, message, strlen(message),
0, "0.0.0.2", "224.0.0.1", SERVERPORT));
RL(rv = sendto_pktinfo(client, message, strlen(message),
0, "0.0.0.3", "224.0.0.1", SERVERPORT));
rump_sys_close(client);
pause();
}
break;
default:
{
struct sockaddr_in from;
socklen_t fromlen;
int server, i, rv;
char buf[sizeof(message) * 2];
netcfg_rump_if(ifname2, "192.168.2.2", "255.255.255.0");
netcfg_rump_if(ifname3, "192.168.0.2", "255.255.255.0");
server = udp_server("0.0.0.0", SERVERPORT);
close(channel[0]);
ATF_CHECK(write(channel[1], "U", 1) == 1);
close(channel[1]);
for (i = 0; i < 2; i++) {
fromlen = sizeof(from);
RL(rv = rump_sys_recvfrom(server, buf,
sizeof(buf), 0,
(struct sockaddr *)&from, &fromlen));
printf("%s: received from %s:%u\n", __func__,
inet_ntoa(from.sin_addr),
ntohs(from.sin_port));
}
rump_sys_close(server);
kill(child, SIGKILL);
}
break;
}
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, pktinfo_send_unbound);
ATF_TP_ADD_TC(tp, pktinfo_send_bindany);
ATF_TP_ADD_TC(tp, pktinfo_send_bindaddr);
ATF_TP_ADD_TC(tp, pktinfo_send_bindport);
ATF_TP_ADD_TC(tp, pktinfo_send_bindaddrport);
ATF_TP_ADD_TC(tp, pktinfo_send_bindother);
ATF_TP_ADD_TC(tp, pktinfo_send_connected);
ATF_TP_ADD_TC(tp, pktinfo_send_notown);
ATF_TP_ADD_TC(tp, pktinfo_send_notown_bind);
ATF_TP_ADD_TC(tp, pktinfo_send_rawip);
ATF_TP_ADD_TC(tp, pktinfo_send_rawip_notown);
ATF_TP_ADD_TC(tp, pktinfo_send_invalidarg);
ATF_TP_ADD_TC(tp, pktinfo_send_ifindex);
ATF_TP_ADD_TC(tp, pktinfo_send_multicast);
return atf_no_error();
}