root/tests/net/net/t_pktinfo_send.c
/*      $NetBSD: t_pktinfo_send.c,v 1.3 2017/12/30 22:02:34 gson Exp $  */

/*-
 * Copyright (c) 2017 Internet Initiative Japan Inc.
 * All rights reserved.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */
#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)
{
        /* for sendmsg */
        struct sockaddr_in to;
        struct msghdr msg;
        char cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo)) * 2];
        struct iovec vec;

        /* for store to cmsghdr */
        struct cmsghdr *cmsg;

        /* for pktinfo */
        struct in_pktinfo *pi;
        struct in_addr addr;

        /* setup msghdr */
        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;

        /* setup ip_pktinfo */
        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));

        /* treat 0.x.x.x/8 as interface index (like RFC1724 ss.3.3) */
        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")];

        /* get sockaddr before sendmsg w/IP_PKTINFO */
        sa_beforelen = sizeof(sa_before);
        RL(rv = rump_sys_getsockname(client,
            (struct sockaddr *)&sa_before, &sa_beforelen));

        /*
         * sendmsg with IP_PKTINFO: 127.0.0.[2345] -> 127.0.0.1:54321, and
         * check received packet is from 127.0.0.[2345]
         */
        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);

        /* get sockaddr after sendmsg w/IP_PKTINFO */
        sa_afterlen = sizeof(sa_after);
        RL(rv = rump_sys_getsockname(client,
            (struct sockaddr *)&sa_after, &sa_afterlen));

        /* confirm sockaddr is not changed */
        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 sendmsg w/IP_PKTINFO tests */
        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 sendmsg w/IP_PKTINFO tests */
        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 sendmsg w/IP_PKTINFO tests */
        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 sendmsg w/IP_PKTINFO tests */
        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 sendmsg w/IP_PKTINFO tests */
        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 sendmsg w/IP_PKTINFO tests */
        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)));

        /* sendmsg w/IP_PKTINFO on connected socket should be error */
        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));

        /* sendmsg w/IP_PKTINFO from 127.0.0.100 (not own) should be error */
        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));

        /* sendmsg w/IP_PKTINFO from 127.0.0.100 (not own) should be error */
        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; /* against confuse REQ with REPLY */
        icmp.icmp_id = htons(getpid());
        icmp.icmp_cksum = in_cksum((uint16_t *)&icmp, sizeof(icmp));
        icmplen = sizeof(icmp);

        /* sendmsg w/IP_PKTINFO from 127.0.0.2 */
        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; /* against confuse REQ with REPLY */
        icmp.icmp_id = htons(getpid());
        icmp.icmp_cksum = in_cksum((uint16_t *)&icmp, sizeof(icmp));
        icmplen = sizeof(icmp);

        /* sendmsg w/IP_PKTINFO from 127.0.0.100 (not own) should be error */
        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));

        /* sendmsg w/IP_PKTINFO (ipi_ifindex=0, ipi_addr=0) does nothing */
        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));


        /* sendmsg w/IP_PKTINFO from 0.0.0.99 (ifindex=99) should be error */
        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());        /* XXX: lo0 is ifindex 1 */
        netcfg_rump_makeshmif(ethername, ifname2);      /* XXX: ifindex=2 */
        netcfg_rump_makeshmif(ethername, ifname3);      /* XXX: ifindex=3 */

        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));

                        /* wait to ready */
                        close(channel[1]);
                        ATF_CHECK(read(channel[0], &token, 1) == 1 &&
                           token == 'U');
                        close(channel[0]);

                        /* few packets would be discarded while resolving arp */
                        for (i = 0; i < 3; i++) {
                                /* send from ifindex 3 = 192.168.0.1 */
                                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);

                        /* notify to child */
                        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());        /* XXX: lo0 is ifindex 1 */
        netcfg_rump_makeshmif(ethername, ifname2);      /* XXX: ifindex=2 */
        netcfg_rump_makeshmif(ethername, ifname3);      /* XXX: ifindex=3 */

        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));

                        /* wait to ready */
                        close(channel[1]);
                        ATF_CHECK(read(channel[0], &token, 1) == 1 &&
                           token == 'U');
                        close(channel[0]);

                        /* send from ifindex 2 = 192.168.2.1 */
                        RL(rv = sendto_pktinfo(client, message, strlen(message),
                            0, "0.0.0.2", "224.0.0.1", SERVERPORT));

                        /* send from ifindex 3 = 192.168.0.1 */
                        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);

                        /* notify to child */
                        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();
}