Module Name:    src
Committed By:   ryo
Date:           Thu Aug 10 04:31:58 UTC 2017

Modified Files:
        src/distrib/sets/lists/debug: mi
        src/distrib/sets/lists/tests: mi
        src/share/man/man4: ip.4
        src/sys/netinet: in.c in.h in_pcb.c in_pcb.h ip_output.c ip_var.h
            raw_ip.c udp_usrreq.c udp_var.h
        src/tests/net/net: Makefile
Added Files:
        src/tests/net/net: t_pktinfo_send.c

Log Message:
Add support IP_PKTINFO for sendmsg(2).

The source address or output interface can be specified by adding IP_PKTINFO
to the control part of the message on a SOCK_DGRAM or SOCK_RAW socket.

Reviewed by ozaki-r@ and christos@. thanks.


To generate a diff of this commit:
cvs rdiff -u -r1.221 -r1.222 src/distrib/sets/lists/debug/mi
cvs rdiff -u -r1.759 -r1.760 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.37 -r1.38 src/share/man/man4/ip.4
cvs rdiff -u -r1.206 -r1.207 src/sys/netinet/in.c
cvs rdiff -u -r1.100 -r1.101 src/sys/netinet/in.h
cvs rdiff -u -r1.178 -r1.179 src/sys/netinet/in_pcb.c
cvs rdiff -u -r1.63 -r1.64 src/sys/netinet/in_pcb.h
cvs rdiff -u -r1.283 -r1.284 src/sys/netinet/ip_output.c
cvs rdiff -u -r1.119 -r1.120 src/sys/netinet/ip_var.h
cvs rdiff -u -r1.165 -r1.166 src/sys/netinet/raw_ip.c
cvs rdiff -u -r1.234 -r1.235 src/sys/netinet/udp_usrreq.c
cvs rdiff -u -r1.41 -r1.42 src/sys/netinet/udp_var.h
cvs rdiff -u -r1.20 -r1.21 src/tests/net/net/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/net/net/t_pktinfo_send.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/debug/mi
diff -u src/distrib/sets/lists/debug/mi:1.221 src/distrib/sets/lists/debug/mi:1.222
--- src/distrib/sets/lists/debug/mi:1.221	Sun Jul 23 19:26:05 2017
+++ src/distrib/sets/lists/debug/mi	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.221 2017/07/23 19:26:05 perseant Exp $
+# $NetBSD: mi,v 1.222 2017/08/10 04:31:58 ryo Exp $
 ./etc/mtree/set.debug                           comp-sys-root
 ./usr/lib					comp-sys-usr		compatdir
 ./usr/lib/i18n/libBIG5_g.a			comp-c-debuglib		debuglib,compatfile
@@ -2280,6 +2280,7 @@
 ./usr/libdata/debug/usr/tests/net/mcast/mcast.debug		tests-net-debug		debug,atf,rump
 ./usr/libdata/debug/usr/tests/net/mcast/t_mcast.debug		tests-obsolete		debug,atf,rump,obsolete
 ./usr/libdata/debug/usr/tests/net/net/t_pktinfo.debug		tests-net-debug		debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/net/net/t_pktinfo_test.debug	tests-net-debug		debug,atf,rump
 ./usr/libdata/debug/usr/tests/net/net/t_raw.debug		tests-net-debug		debug,atf,rump
 ./usr/libdata/debug/usr/tests/net/net/t_tcp.debug		tests-net-debug		debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/net/net/t_udp.debug		tests-net-debug		debug,atf,compattestfile

Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.759 src/distrib/sets/lists/tests/mi:1.760
--- src/distrib/sets/lists/tests/mi:1.759	Wed Aug  2 02:19:56 2017
+++ src/distrib/sets/lists/tests/mi	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.759 2017/08/02 02:19:56 ozaki-r Exp $
+# $NetBSD: mi,v 1.760 2017/08/10 04:31:58 ryo Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -3361,6 +3361,7 @@
 ./usr/tests/net/net/t_ping6_opts		tests-net-tests		atf,rump
 ./usr/tests/net/net/t_ping_opts			tests-net-tests		atf,rump
 ./usr/tests/net/net/t_pktinfo			tests-net-tests		compattestfile,atf
+./usr/tests/net/net/t_pktinfo_send		tests-net-tests		atf,rump
 ./usr/tests/net/net/t_raw			tests-net-tests		atf,rump
 ./usr/tests/net/net/t_tcp			tests-net-tests		compattestfile,atf
 ./usr/tests/net/net/t_udp			tests-net-tests		compattestfile,atf

Index: src/share/man/man4/ip.4
diff -u src/share/man/man4/ip.4:1.37 src/share/man/man4/ip.4:1.38
--- src/share/man/man4/ip.4:1.37	Mon Jul  3 21:30:58 2017
+++ src/share/man/man4/ip.4	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-.\"	$NetBSD: ip.4,v 1.37 2017/07/03 21:30:58 wiz Exp $
+.\"	$NetBSD: ip.4,v 1.38 2017/08/10 04:31:58 ryo Exp $
 .\"
 .\" Copyright (c) 1983, 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
@@ -29,7 +29,7 @@
 .\"
 .\"     @(#)ip.4	8.2 (Berkeley) 11/30/93
 .\"
-.Dd June 27, 2013
+.Dd August 10, 2017
 .Dt IP 4
 .Os
 .Sh NAME
@@ -121,6 +121,7 @@ structure, which contains
 	struct in_addr ipi_addr;	/* the source or destination address */
 	unsigned int ipi_ifindex;	/* the interface index */
 .Ed
+.Pp
 and added to the control portion of the message:
 The cmsghdr fields have the following values:
 .Bd -literal
@@ -129,6 +130,16 @@ cmsg_level = IPPROTO_IP
 cmsg_type = IP_PKTINFO
 .Ed
 .Pp
+For
+.Xr sendmsg 2 ,
+the source address or output interface can be specified by adding
+.Dv IP_PKTINFO
+to the control part of the message on a
+.Dv SOCK_DGRAM
+or
+.Dv SOCK_RAW
+socket.
+.Pp
 The
 .Dv IP_PORTALGO
 can be used to randomize the port selection.

Index: src/sys/netinet/in.c
diff -u src/sys/netinet/in.c:1.206 src/sys/netinet/in.c:1.207
--- src/sys/netinet/in.c:1.206	Fri Aug  4 20:17:45 2017
+++ src/sys/netinet/in.c	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in.c,v 1.206 2017/08/04 20:17:45 uwe Exp $	*/
+/*	$NetBSD: in.c,v 1.207 2017/08/10 04:31:58 ryo Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -91,7 +91,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.206 2017/08/04 20:17:45 uwe Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.207 2017/08/10 04:31:58 ryo Exp $");
 
 #include "arp.h"
 
@@ -258,6 +258,43 @@ in_localaddr(struct in_addr in)
 }
 
 /*
+ * like in_localaddr() but can specify ifp.
+ */
+int
+in_direct(struct in_addr in, struct ifnet *ifp)
+{
+	struct ifaddr *ifa;
+	int localaddr = 0;
+	int s;
+
+	KASSERT(ifp != NULL);
+
+#define ia (ifatoia(ifa))
+	s = pserialize_read_enter();
+	if (subnetsarelocal) {
+		IFADDR_READER_FOREACH(ifa, ifp) {
+			if (ifa->ifa_addr->sa_family == AF_INET &&
+			    ((in.s_addr & ia->ia_netmask) == ia->ia_net)) {
+				localaddr = 1;
+				break;
+			}
+		}
+	} else {
+		IFADDR_READER_FOREACH(ifa, ifp) {
+			if (ifa->ifa_addr->sa_family == AF_INET &&
+			    (in.s_addr & ia->ia_subnetmask) == ia->ia_subnet) {
+				localaddr = 1;
+				break;
+			}
+		}
+	}
+	pserialize_read_exit(s);
+
+	return localaddr;
+#undef ia
+}
+
+/*
  * Determine whether an IP address is in a reserved set of addresses
  * that may not be forwarded, or whether datagrams to that destination
  * may be forwarded.

Index: src/sys/netinet/in.h
diff -u src/sys/netinet/in.h:1.100 src/sys/netinet/in.h:1.101
--- src/sys/netinet/in.h:1.100	Thu Feb 16 08:12:44 2017
+++ src/sys/netinet/in.h	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in.h,v 1.100 2017/02/16 08:12:44 knakahara Exp $	*/
+/*	$NetBSD: in.h,v 1.101 2017/08/10 04:31:58 ryo Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1990, 1993
@@ -562,6 +562,7 @@ extern	u_char	ip_protox[];
 extern const struct sockaddr_in in_any;
 
 int	in_broadcast(struct in_addr, struct ifnet *);
+int	in_direct(struct in_addr, struct ifnet *);
 int	in_canforward(struct in_addr);
 int	cpu_in_cksum(struct mbuf *, int, int, uint32_t);
 int	in_cksum(struct mbuf *, int);

Index: src/sys/netinet/in_pcb.c
diff -u src/sys/netinet/in_pcb.c:1.178 src/sys/netinet/in_pcb.c:1.179
--- src/sys/netinet/in_pcb.c:1.178	Tue Apr 25 05:44:11 2017
+++ src/sys/netinet/in_pcb.c	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in_pcb.c,v 1.178 2017/04/25 05:44:11 ozaki-r Exp $	*/
+/*	$NetBSD: in_pcb.c,v 1.179 2017/08/10 04:31:58 ryo Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -93,7 +93,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in_pcb.c,v 1.178 2017/04/25 05:44:11 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in_pcb.c,v 1.179 2017/08/10 04:31:58 ryo Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -270,8 +270,8 @@ in_pcbsetport(struct sockaddr_in *sin, s
 	return (0);
 }
 
-static int
-in_pcbbind_addr(struct inpcb *inp, struct sockaddr_in *sin, kauth_cred_t cred)
+int
+in_pcbbindableaddr(struct sockaddr_in *sin, kauth_cred_t cred)
 {
 	int error = EADDRNOTAVAIL;
 	struct ifaddr *ifa = NULL;
@@ -298,13 +298,20 @@ in_pcbbind_addr(struct inpcb *inp, struc
 		if (ia->ia4_flags & IN_IFF_DUPLICATED)
 			goto error;
 	}
+	error = 0;
+ error:
 	pserialize_read_exit(s);
+	return error;
+}
 
-	inp->inp_laddr = sin->sin_addr;
+static int
+in_pcbbind_addr(struct inpcb *inp, struct sockaddr_in *sin, kauth_cred_t cred)
+{
+	int error;
 
-	return (0);
-error:
-	pserialize_read_exit(s);
+	error = in_pcbbindableaddr(sin, cred);
+	if (error == 0)
+		inp->inp_laddr = sin->sin_addr;
 	return error;
 }
 

Index: src/sys/netinet/in_pcb.h
diff -u src/sys/netinet/in_pcb.h:1.63 src/sys/netinet/in_pcb.h:1.64
--- src/sys/netinet/in_pcb.h:1.63	Thu Mar  2 05:29:31 2017
+++ src/sys/netinet/in_pcb.h	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in_pcb.h,v 1.63 2017/03/02 05:29:31 ozaki-r Exp $	*/
+/*	$NetBSD: in_pcb.h,v 1.64 2017/08/10 04:31:58 ryo Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -135,6 +135,7 @@ struct inpcb {
 #ifdef _KERNEL
 void	in_losing(struct inpcb *);
 int	in_pcballoc(struct socket *, void *);
+int	in_pcbbindableaddr(struct sockaddr_in *, kauth_cred_t);
 int	in_pcbbind(void *, struct sockaddr_in *, struct lwp *);
 int	in_pcbconnect(void *, struct sockaddr_in *, struct lwp *);
 void	in_pcbdetach(void *);

Index: src/sys/netinet/ip_output.c
diff -u src/sys/netinet/ip_output.c:1.283 src/sys/netinet/ip_output.c:1.284
--- src/sys/netinet/ip_output.c:1.283	Sun Jul 23 10:55:00 2017
+++ src/sys/netinet/ip_output.c	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip_output.c,v 1.283 2017/07/23 10:55:00 para Exp $	*/
+/*	$NetBSD: ip_output.c,v 1.284 2017/08/10 04:31:58 ryo Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -91,7 +91,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.283 2017/07/23 10:55:00 para Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.284 2017/08/10 04:31:58 ryo Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -127,6 +127,7 @@ __KERNEL_RCSID(0, "$NetBSD: ip_output.c,
 #include <netinet/in_offload.h>
 #include <netinet/portalgo.h>
 #include <netinet/udp.h>
+#include <netinet/udp_var.h>
 
 #ifdef INET6
 #include <netinet6/ip6_var.h>
@@ -329,8 +330,9 @@ ip_output(struct mbuf *m0, struct mbuf *
 		mtu = ifp->if_mtu;
 		ip->ip_ttl = 1;
 		isbroadcast = in_broadcast(dst->sin_addr, ifp);
-	} else if ((IN_MULTICAST(ip->ip_dst.s_addr) ||
-	    ip->ip_dst.s_addr == INADDR_BROADCAST) &&
+	} else if (((IN_MULTICAST(ip->ip_dst.s_addr) ||
+	    ip->ip_dst.s_addr == INADDR_BROADCAST) ||
+	    (flags & IP_ROUTETOIFINDEX)) &&
 	    imo != NULL && imo->imo_multicast_if_index != 0) {
 		ifp = mifp = if_get_byindex(imo->imo_multicast_if_index, &psref);
 		if (ifp == NULL) {
@@ -344,7 +346,31 @@ ip_output(struct mbuf *m0, struct mbuf *
 			error = EADDRNOTAVAIL;
 			goto bad;
 		}
-		isbroadcast = 0;
+		if (IN_MULTICAST(ip->ip_dst.s_addr) ||
+		    ip->ip_dst.s_addr == INADDR_BROADCAST) {
+			isbroadcast = 0;
+		} else {
+			/* IP_ROUTETOIFINDEX */
+			isbroadcast = in_broadcast(dst->sin_addr, ifp);
+			if ((isbroadcast == 0) && ((ifp->if_flags &
+			    (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0) &&
+			    (in_direct(dst->sin_addr, ifp) == 0)) {
+				/* gateway address required */
+				if (rt == NULL)
+					rt = rtcache_init(ro);
+				if (rt == NULL || rt->rt_ifp != ifp) {
+					IP_STATINC(IP_STAT_NOROUTE);
+					error = EHOSTUNREACH;
+					goto bad;
+				}
+				rt->rt_use++;
+				if (rt->rt_flags & RTF_GATEWAY)
+					dst = satosin(rt->rt_gateway);
+				if (rt->rt_flags & RTF_HOST)
+					isbroadcast =
+					    rt->rt_flags & RTF_BROADCAST;
+			}
+		}
 	} else {
 		if (rt == NULL)
 			rt = rtcache_init(ro);
@@ -1320,6 +1346,131 @@ ip_ctloutput(int op, struct socket *so, 
 	return error;
 }
 
+static int
+ip_pktinfo_prepare(const struct in_pktinfo *pktinfo, struct ip_pktopts *pktopts,
+    int *flags, kauth_cred_t cred)
+{
+	struct ip_moptions *imo;
+	int error = 0;
+	bool addrset = false;
+
+	if (!in_nullhost(pktinfo->ipi_addr)) {
+		pktopts->ippo_laddr.sin_addr = pktinfo->ipi_addr;
+		/* EADDRNOTAVAIL? */
+		error = in_pcbbindableaddr(&pktopts->ippo_laddr, cred);
+		if (error != 0)
+			return error;
+		addrset = true;
+	}
+
+	if (pktinfo->ipi_ifindex != 0) {
+		if (!addrset) {
+			struct ifnet *ifp;
+			struct in_ifaddr *ia;
+			int s;
+
+			/* pick up primary address */
+			s = pserialize_read_enter();
+			ifp = if_byindex(pktinfo->ipi_ifindex);
+			if (ifp == NULL) {
+				pserialize_read_exit(s);
+				return EADDRNOTAVAIL;
+			}
+			ia = in_get_ia_from_ifp(ifp);
+			if (ia == NULL) {
+				pserialize_read_exit(s);
+				return EADDRNOTAVAIL;
+			}
+			pktopts->ippo_laddr.sin_addr = IA_SIN(ia)->sin_addr;
+			pserialize_read_exit(s);
+		}
+
+		/*
+		 * If specified ipi_ifindex,
+		 * use copied or locally initialized ip_moptions.
+		 * Original ip_moptions must not be modified.
+		 */
+		imo = &pktopts->ippo_imobuf;	/* local buf in pktopts */
+		if (pktopts->ippo_imo != NULL) {
+			memcpy(imo, pktopts->ippo_imo, sizeof(*imo));
+		} else {
+			memset(imo, 0, sizeof(*imo));
+			imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
+			imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
+		}
+		imo->imo_multicast_if_index = pktinfo->ipi_ifindex;
+		pktopts->ippo_imo = imo;
+		*flags |= IP_ROUTETOIFINDEX;
+	}
+	return error;
+}
+
+/*
+ * Set up IP outgoing packet options. Even if control is NULL,
+ * pktopts->ippo_laddr and pktopts->ippo_imo are set and used.
+ */
+int
+ip_setpktopts(struct mbuf *control, struct ip_pktopts *pktopts, int *flags,
+    struct inpcb *inp, kauth_cred_t cred, int uproto)
+{
+	struct inpcb *xinp;
+	struct cmsghdr *cm;
+	struct in_pktinfo *pktinfo;
+	int error;
+
+	pktopts->ippo_imo = inp->inp_moptions;
+	sockaddr_in_init(&pktopts->ippo_laddr, &inp->inp_laddr, 0);
+
+	if (control == NULL)
+		return 0;
+
+	/*
+	 * XXX: Currently, we assume all the optional information is
+	 * stored in a single mbuf.
+	 */
+	if (control->m_next)
+		return EINVAL;
+
+	for (; control->m_len > 0;
+	    control->m_data += CMSG_ALIGN(cm->cmsg_len),
+	    control->m_len -= CMSG_ALIGN(cm->cmsg_len)) {
+		cm = mtod(control, struct cmsghdr *);
+		if ((control->m_len < sizeof(*cm)) ||
+		    (cm->cmsg_len == 0) ||
+		    (cm->cmsg_len > control->m_len)) {
+			return EINVAL;
+		}
+		if (cm->cmsg_level != IPPROTO_IP)
+			continue;
+
+		switch (cm->cmsg_type) {
+		case IP_PKTINFO:
+			if (cm->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
+				return EINVAL;
+
+			pktinfo = (struct in_pktinfo *)CMSG_DATA(cm);
+			error = ip_pktinfo_prepare(pktinfo, pktopts, flags,
+			    cred);
+			if (error != 0)
+				return error;
+
+			if ((uproto == IPPROTO_UDP) &&
+			    !in_nullhost(pktopts->ippo_laddr.sin_addr)) {
+				/* Checking laddr:port already in use? */
+				xinp = in_pcblookup_bind(&udbtable,
+				    pktopts->ippo_laddr.sin_addr,
+				    inp->inp_lport);
+				if ((xinp != NULL) && (xinp != inp))
+					return EADDRINUSE;
+			}
+			break;
+		default:
+			return ENOPROTOOPT;
+		}
+	}
+	return 0;
+}
+
 /*
  * Set up IP options in pcb for insertion in output packets.
  * Store in mbuf with pointer in pcbopt, adding pseudo-option

Index: src/sys/netinet/ip_var.h
diff -u src/sys/netinet/ip_var.h:1.119 src/sys/netinet/ip_var.h:1.120
--- src/sys/netinet/ip_var.h:1.119	Fri Mar 31 06:49:44 2017
+++ src/sys/netinet/ip_var.h	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip_var.h,v 1.119 2017/03/31 06:49:44 ozaki-r Exp $	*/
+/*	$NetBSD: ip_var.h,v 1.120 2017/08/10 04:31:58 ryo Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1993
@@ -123,6 +123,12 @@ struct ip_moptions {
 	struct	  in_multi *imo_membership[IP_MAX_MEMBERSHIPS];
 };
 
+struct ip_pktopts {
+	struct sockaddr_in ippo_laddr;	/* source address */
+	struct ip_moptions *ippo_imo;	/* inp->inp_moptions or &ippo_imobuf */
+	struct ip_moptions ippo_imobuf;	/* use when IP_PKTINFO */
+};
+
 /*
  * IP statistics.
  * Each counter is an unsigned 64-bit value.
@@ -182,6 +188,7 @@ __CTASSERT(SO_BROADCAST ==	0x0020);
 
 #define	IP_IGMP_MCAST		0x0040		/* IGMP for mcast join/leave */
 #define	IP_MTUDISC		0x0400		/* Path MTU Discovery; set DF */
+#define	IP_ROUTETOIFINDEX	0x0800	/* force route imo_multicast_if_index */
 
 extern struct domain inetdomain;
 extern const struct pr_usrreqs rip_usrreqs;
@@ -207,6 +214,8 @@ void	ip_init(void);
 void	in_init(void);
 
 int	 ip_ctloutput(int, struct socket *, struct sockopt *);
+int	 ip_setpktopts(struct mbuf *, struct ip_pktopts *, int *,
+	    struct inpcb *, kauth_cred_t, int);
 void	 ip_drain(void);
 void	 ip_drainstub(void);
 void	 ip_freemoptions(struct ip_moptions *);
@@ -233,7 +242,7 @@ void *	 rip_ctlinput(int, const struct s
 int	 rip_ctloutput(int, struct socket *, struct sockopt *);
 void	 rip_init(void);
 void	 rip_input(struct mbuf *, ...);
-int	 rip_output(struct mbuf *, struct inpcb *);
+int	 rip_output(struct mbuf *, struct inpcb *, struct mbuf *, struct lwp *);
 int	 rip_usrreq(struct socket *,
 	    int, struct mbuf *, struct mbuf *, struct mbuf *, struct lwp *);
 

Index: src/sys/netinet/raw_ip.c
diff -u src/sys/netinet/raw_ip.c:1.165 src/sys/netinet/raw_ip.c:1.166
--- src/sys/netinet/raw_ip.c:1.165	Thu Jul  6 17:08:57 2017
+++ src/sys/netinet/raw_ip.c	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: raw_ip.c,v 1.165 2017/07/06 17:08:57 christos Exp $	*/
+/*	$NetBSD: raw_ip.c,v 1.166 2017/08/10 04:31:58 ryo Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -65,7 +65,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: raw_ip.c,v 1.165 2017/07/06 17:08:57 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: raw_ip.c,v 1.166 2017/08/10 04:31:58 ryo Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -303,15 +303,31 @@ rip_ctlinput(int cmd, const struct socka
  * Tack on options user may have setup with control call.
  */
 int
-rip_output(struct mbuf *m, struct inpcb *inp)
+rip_output(struct mbuf *m, struct inpcb *inp, struct mbuf *control,
+    struct lwp *l)
 {
 	struct ip *ip;
 	struct mbuf *opts;
-	int flags;
-
-	flags =
-	    (inp->inp_socket->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST
-	    | IP_RETURNMTU;
+	struct ip_pktopts pktopts;
+	kauth_cred_t cred;
+	int error, flags;
+
+	flags = (inp->inp_socket->so_options & SO_DONTROUTE) |
+	    IP_ALLOWBROADCAST | IP_RETURNMTU;
+
+	if (l == NULL)
+		cred = NULL;
+	else
+		cred = l->l_cred;
+
+	/* Setup IP outgoing packet options */
+	memset(&pktopts, 0, sizeof(pktopts));
+	error = ip_setpktopts(control, &pktopts, &flags, inp, cred,
+	    IPPROTO_RAW);
+	if (control != NULL)
+		m_freem(control);
+	if (error != 0)
+		goto release;
 
 	/*
 	 * If the user handed us a complete IP packet, use it.
@@ -319,25 +335,27 @@ rip_output(struct mbuf *m, struct inpcb 
 	 */
 	if ((inp->inp_flags & INP_HDRINCL) == 0) {
 		if ((m->m_pkthdr.len + sizeof(struct ip)) > IP_MAXPACKET) {
-			m_freem(m);
-			return (EMSGSIZE);
+			error = EMSGSIZE;
+			goto release;
 		}
 		M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
-		if (!m)
-			return (ENOBUFS);
+		if (!m) {
+			error = ENOBUFS;
+			goto release;
+		}
 		ip = mtod(m, struct ip *);
 		ip->ip_tos = 0;
 		ip->ip_off = htons(0);
 		ip->ip_p = inp->inp_ip.ip_p;
 		ip->ip_len = htons(m->m_pkthdr.len);
-		ip->ip_src = inp->inp_laddr;
+		ip->ip_src = pktopts.ippo_laddr.sin_addr;
 		ip->ip_dst = inp->inp_faddr;
 		ip->ip_ttl = MAXTTL;
 		opts = inp->inp_options;
 	} else {
 		if (m->m_pkthdr.len > IP_MAXPACKET) {
-			m_freem(m);
-			return (EMSGSIZE);
+			error = EMSGSIZE;
+			goto release;
 		}
 		ip = mtod(m, struct ip *);
 
@@ -350,15 +368,17 @@ rip_output(struct mbuf *m, struct inpcb 
 			int hlen = ip->ip_hl << 2;
 
 			m = m_copyup(m, hlen, (max_linkhdr + 3) & ~3);
-			if (m == NULL)
-				return (ENOMEM);	/* XXX */
+			if (m == NULL) {
+				error = ENOMEM;	/* XXX */
+				goto release;
+			}
 			ip = mtod(m, struct ip *);
 		}
 
 		/* XXX userland passes ip_len and ip_off in host order */
 		if (m->m_pkthdr.len != ip->ip_len) {
-			m_freem(m);
-			return (EINVAL);
+			error = EINVAL;
+			goto release;
 		}
 		HTONS(ip->ip_len);
 		HTONS(ip->ip_off);
@@ -374,8 +394,13 @@ rip_output(struct mbuf *m, struct inpcb 
 	 * IP output.  Note: if IP_RETURNMTU flag is set, the MTU size
 	 * will be stored in inp_errormtu.
 	 */
-	return ip_output(m, opts, &inp->inp_route, flags, inp->inp_moptions,
-	     inp);
+	return ip_output(m, opts, &inp->inp_route, flags, pktopts.ippo_imo,
+	    inp);
+
+ release:
+	if (m != NULL)
+		m_freem(m);
+	return error;
 }
 
 /*
@@ -747,12 +772,6 @@ rip_send(struct socket *so, struct mbuf 
 	 * Ship a packet out.  The appropriate raw output
 	 * routine handles any massaging necessary.
 	 */
-	if (control && control->m_len) {
-		m_freem(control);
-		m_freem(m);
-		return EINVAL;
-	}
-
 	s = splsoftnet();
 	if (nam) {
 		if ((so->so_state & SS_ISCONNECTED) != 0) {
@@ -760,21 +779,24 @@ rip_send(struct socket *so, struct mbuf 
 			goto die;
 		}
 		error = rip_connect_pcb(inp, (struct sockaddr_in *)nam);
-		if (error) {
-		die:
-			m_freem(m);
-			splx(s);
-			return error;
-		}
+		if (error)
+			goto die;
 	} else {
 		if ((so->so_state & SS_ISCONNECTED) == 0) {
 			error = ENOTCONN;
 			goto die;
 		}
 	}
-	error = rip_output(m, inp);
+	error = rip_output(m, inp, control, l);
+	m = NULL;
+	control = NULL;
 	if (nam)
 		rip_disconnect1(inp);
+ die:
+	if (m != NULL)
+		m_freem(m);
+	if (control != NULL)
+		m_freem(control);
 
 	splx(s);
 	return error;

Index: src/sys/netinet/udp_usrreq.c
diff -u src/sys/netinet/udp_usrreq.c:1.234 src/sys/netinet/udp_usrreq.c:1.235
--- src/sys/netinet/udp_usrreq.c:1.234	Thu Jul  6 17:08:57 2017
+++ src/sys/netinet/udp_usrreq.c	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: udp_usrreq.c,v 1.234 2017/07/06 17:08:57 christos Exp $	*/
+/*	$NetBSD: udp_usrreq.c,v 1.235 2017/08/10 04:31:58 ryo Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -66,7 +66,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: udp_usrreq.c,v 1.234 2017/07/06 17:08:57 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: udp_usrreq.c,v 1.235 2017/08/10 04:31:58 ryo Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -765,14 +765,16 @@ end:
 	return error;
 }
 
-
 int
-udp_output(struct mbuf *m, struct inpcb *inp)
+udp_output(struct mbuf *m, struct inpcb *inp, struct mbuf *control,
+    struct lwp *l)
 {
 	struct udpiphdr *ui;
 	struct route *ro;
+	struct ip_pktopts pktopts;
+	kauth_cred_t cred;
 	int len = m->m_pkthdr.len;
-	int error = 0;
+	int error, flags = 0;
 
 	MCLAIM(m, &udp_tx_mowner);
 
@@ -795,13 +797,30 @@ udp_output(struct mbuf *m, struct inpcb 
 		goto release;
 	}
 
+	if (l == NULL)
+		cred = NULL;
+	else
+		cred = l->l_cred;
+
+	/* Setup IP outgoing packet options */
+	memset(&pktopts, 0, sizeof(pktopts));
+	error = ip_setpktopts(control, &pktopts, &flags, inp, cred,
+	    IPPROTO_UDP);
+	if (error != 0)
+		goto release;
+
+	if (control != NULL) {
+		m_freem(control);
+		control = NULL;
+	}
+
 	/*
 	 * Fill in mbuf with extended UDP header
 	 * and addresses and length put into network format.
 	 */
 	ui = mtod(m, struct udpiphdr *);
 	ui->ui_pr = IPPROTO_UDP;
-	ui->ui_src = inp->inp_laddr;
+	ui->ui_src = pktopts.ippo_laddr.sin_addr;
 	ui->ui_dst = inp->inp_faddr;
 	ui->ui_sport = inp->inp_lport;
 	ui->ui_dport = inp->inp_fport;
@@ -829,13 +848,14 @@ udp_output(struct mbuf *m, struct inpcb 
 	((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos;	/* XXX */
 	UDP_STATINC(UDP_STAT_OPACKETS);
 
-	return (ip_output(m, inp->inp_options, ro,
-	    inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST),
-	    inp->inp_moptions, inp));
+	flags |= inp->inp_socket->so_options & (SO_DONTROUTE|SO_BROADCAST);
+	return ip_output(m, inp->inp_options, ro, flags, pktopts.ippo_imo, inp);
 
-release:
+ release:
+	if (control != NULL)
+		m_freem(control);
 	m_freem(m);
-	return (error);
+	return error;
 }
 
 static int
@@ -1067,12 +1087,6 @@ udp_send(struct socket *so, struct mbuf 
 	KASSERT(inp != NULL);
 	KASSERT(m != NULL);
 
-	if (control && control->m_len) {
-		m_freem(control);
-		m_freem(m);
-		return EINVAL;
-	}
-
 	memset(&laddr, 0, sizeof laddr);
 
 	s = splsoftnet();
@@ -1091,16 +1105,19 @@ udp_send(struct socket *so, struct mbuf 
 			goto die;
 		}
 	}
-	error = udp_output(m, inp);
+	error = udp_output(m, inp, control, l);
 	m = NULL;
+	control = NULL;
 	if (nam) {
 		in_pcbdisconnect(inp);
 		inp->inp_laddr = laddr;		/* XXX */
 		in_pcbstate(inp, INP_BOUND);	/* XXX */
 	}
   die:
-	if (m)
+	if (m != NULL)
 		m_freem(m);
+	if (control != NULL)
+		m_freem(control);
 
 	splx(s);
 	return error;

Index: src/sys/netinet/udp_var.h
diff -u src/sys/netinet/udp_var.h:1.41 src/sys/netinet/udp_var.h:1.42
--- src/sys/netinet/udp_var.h:1.41	Wed Jan 20 22:01:18 2016
+++ src/sys/netinet/udp_var.h	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: udp_var.h,v 1.41 2016/01/20 22:01:18 riastradh Exp $	*/
+/*	$NetBSD: udp_var.h,v 1.42 2017/08/10 04:31:58 ryo Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1989, 1993
@@ -96,7 +96,7 @@ int	 udp_ctloutput(int, struct socket *,
 void	 udp_init(void);
 void	 udp_init_common(void);
 void	 udp_input(struct mbuf *, ...);
-int	 udp_output(struct mbuf *, struct inpcb *);
+int	 udp_output(struct mbuf *, struct inpcb *, struct mbuf *, struct lwp *);
 int	 udp_sysctl(int *, u_int, void *, size_t *, void *, size_t);
 
 int	udp_input_checksum(int af, struct mbuf *, const struct udphdr *, int,

Index: src/tests/net/net/Makefile
diff -u src/tests/net/net/Makefile:1.20 src/tests/net/net/Makefile:1.21
--- src/tests/net/net/Makefile:1.20	Fri Mar 31 06:41:40 2017
+++ src/tests/net/net/Makefile	Thu Aug 10 04:31:58 2017
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.20 2017/03/31 06:41:40 ozaki-r Exp $
+# $NetBSD: Makefile,v 1.21 2017/08/10 04:31:58 ryo Exp $
 #
 
 .include <bsd.own.mk>
@@ -10,6 +10,7 @@ TESTS_C+=	t_tcp
 TESTS_C+=	t_udp
 TESTS_C+=	t_pktinfo
 .if (${MKRUMP} != "no") && !defined(BSD_MK_COMPAT_FILE)
+TESTS_C+=	t_pktinfo_send
 TESTS_C+=	t_raw
 
 .for name in forwarding ipaddress ipv6address ipv6_lifetime mtudisc mtudisc6 \
@@ -19,6 +20,9 @@ TESTS_SH_SRC_t_${name}=	../net_common.sh
 .endfor
 .endif
 
+LDADD.t_pktinfo_send+=	-lrumpnet_local -lrumpnet_netinet -lrumpnet_net
+LDADD.t_pktinfo_send+=	-lrumpdev -lrumpnet_shmif -lrumpnet
+LDADD.t_pktinfo_send+=	-lrumpvfs -lrump -lrumpuser -lrump -lpthread
 LDADD.t_raw+=	-lrumpnet_local -lrumpnet_netinet -lrumpnet_net -lrumpdev
 LDADD.t_raw+=	-lrumpnet -lrumpvfs -lrump -lrumpuser -lrump -lpthread
 

Added files:

Index: src/tests/net/net/t_pktinfo_send.c
diff -u /dev/null src/tests/net/net/t_pktinfo_send.c:1.1
--- /dev/null	Thu Aug 10 04:31:58 2017
+++ src/tests/net/net/t_pktinfo_send.c	Thu Aug 10 04:31:58 2017
@@ -0,0 +1,822 @@
+/*	$NetBSD: t_pktinfo_send.c,v 1.1 2017/08/10 04:31:58 ryo 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.1 2017/08/10 04:31:58 ryo 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_set_md_var(tc, "timeout", "5");
+}
+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_set_md_var(tc, "timeout", "5");
+}
+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_set_md_var(tc, "timeout", "5");
+}
+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_set_md_var(tc, "timeout", "5");
+}
+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_set_md_var(tc, "timeout", "5");
+}
+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_set_md_var(tc, "timeout", "5");
+}
+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));
+
+	/*
+	 * other socket is bound "127.0.0.2:12345".
+	 * client socket is bound "127.0.0.3:12345".
+	 * client socket sendto w/IP_PKTINFO "127.0.0.2",
+	 * this should be error as EADDRINUSE.
+	 */
+	rv = sendto_pktinfo(client, message, strlen(message), 0,
+	    "127.0.0.2", "127.0.0.1", SERVERPORT);
+
+	ATF_REQUIRE_MSG(rv == -1,
+	    "sendmsg with in-use address:port should be error,"
+	    " but success");
+	ATF_REQUIRE_MSG(errno == EADDRINUSE,
+	    "sendmsg with in-use address:port should be EADDRINUSE,"
+	    " but got %s", strerror(errno));
+
+
+	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_set_md_var(tc, "timeout", "5");
+}
+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_set_md_var(tc, "timeout", "5");
+}
+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_set_md_var(tc, "timeout", "5");
+}
+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_set_md_var(tc, "timeout", "5");
+}
+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_set_md_var(tc, "timeout", "5");
+}
+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_set_md_var(tc, "timeout", "5");
+}
+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_set_md_var(tc, "timeout", "5");
+}
+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_set_md_var(tc, "timeout", "5");
+}
+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();
+}

Reply via email to