Module Name: src
Committed By: snj
Date: Thu Dec 21 21:08:13 UTC 2017
Modified Files:
src/distrib/sets/lists/debug [netbsd-8]: mi
src/distrib/sets/lists/tests [netbsd-8]: mi
src/share/man/man4 [netbsd-8]: ip.4
src/sys/netinet [netbsd-8]: 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 [netbsd-8]: Makefile
Added Files:
src/tests/net/net [netbsd-8]: t_pktinfo_send.c
Log Message:
Pull up following revision(s) (requested by ryo in ticket #445):
distrib/sets/lists/debug/mi: revision 1.222
distrib/sets/lists/tests/mi: revision 1.760
share/man/man4/ip.4: revision 1.38
sys/netinet/in.c: revision 1.207
sys/netinet/in.h: revision 1.101
sys/netinet/in_pcb.c: revision 1.179
sys/netinet/in_pcb.h: revision 1.64
sys/netinet/ip_output.c: revision 1.284, 1.286
sys/netinet/ip_var.h: revision 1.120-1.121
sys/netinet/raw_ip.c: revision 1.166-1.167
sys/netinet/udp_usrreq.c: revision 1.235-1.236
sys/netinet/udp_var.h: revision 1.42
tests/net/net/Makefile: revision 1.21
tests/net/net/t_pktinfo_send.c: revision 1.1-1.2
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.
--
As is the case with IPV6_PKTINFO, IP_PKTINFO can be sent without EADDRINUSE
even if the UDP address:port in use is specified.
To generate a diff of this commit:
cvs rdiff -u -r1.216.2.5 -r1.216.2.6 src/distrib/sets/lists/debug/mi
cvs rdiff -u -r1.752.2.6 -r1.752.2.7 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.36 -r1.36.20.1 src/share/man/man4/ip.4
cvs rdiff -u -r1.203.2.2 -r1.203.2.3 src/sys/netinet/in.c
cvs rdiff -u -r1.100 -r1.100.6.1 src/sys/netinet/in.h
cvs rdiff -u -r1.178 -r1.178.4.1 src/sys/netinet/in_pcb.c
cvs rdiff -u -r1.63 -r1.63.6.1 src/sys/netinet/in_pcb.h
cvs rdiff -u -r1.279.2.1 -r1.279.2.2 src/sys/netinet/ip_output.c
cvs rdiff -u -r1.119 -r1.119.6.1 src/sys/netinet/ip_var.h
cvs rdiff -u -r1.164 -r1.164.4.1 src/sys/netinet/raw_ip.c
cvs rdiff -u -r1.233 -r1.233.4.1 src/sys/netinet/udp_usrreq.c
cvs rdiff -u -r1.41 -r1.41.10.1 src/sys/netinet/udp_var.h
cvs rdiff -u -r1.20 -r1.20.4.1 src/tests/net/net/Makefile
cvs rdiff -u -r0 -r1.2.2.2 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.216.2.5 src/distrib/sets/lists/debug/mi:1.216.2.6
--- src/distrib/sets/lists/debug/mi:1.216.2.5 Fri Nov 17 20:43:10 2017
+++ src/distrib/sets/lists/debug/mi Thu Dec 21 21:08:13 2017
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.216.2.5 2017/11/17 20:43:10 snj Exp $
+# $NetBSD: mi,v 1.216.2.6 2017/12/21 21:08:13 snj 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.752.2.6 src/distrib/sets/lists/tests/mi:1.752.2.7
--- src/distrib/sets/lists/tests/mi:1.752.2.6 Fri Nov 17 20:43:10 2017
+++ src/distrib/sets/lists/tests/mi Thu Dec 21 21:08:13 2017
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.752.2.6 2017/11/17 20:43:10 snj Exp $
+# $NetBSD: mi,v 1.752.2.7 2017/12/21 21:08:13 snj Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@@ -3363,6 +3363,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.36 src/share/man/man4/ip.4:1.36.20.1
--- src/share/man/man4/ip.4:1.36 Sat Jul 13 09:24:25 2013
+++ src/share/man/man4/ip.4 Thu Dec 21 21:08:13 2017
@@ -1,4 +1,4 @@
-.\" $NetBSD: ip.4,v 1.36 2013/07/13 09:24:25 njoly Exp $
+.\" $NetBSD: ip.4,v 1.36.20.1 2017/12/21 21:08:13 snj 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.203.2.2 src/sys/netinet/in.c:1.203.2.3
--- src/sys/netinet/in.c:1.203.2.2 Fri Nov 17 20:24:05 2017
+++ src/sys/netinet/in.c Thu Dec 21 21:08:13 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: in.c,v 1.203.2.2 2017/11/17 20:24:05 snj Exp $ */
+/* $NetBSD: in.c,v 1.203.2.3 2017/12/21 21:08:13 snj 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.203.2.2 2017/11/17 20:24:05 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.203.2.3 2017/12/21 21:08:13 snj 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.100.6.1
--- src/sys/netinet/in.h:1.100 Thu Feb 16 08:12:44 2017
+++ src/sys/netinet/in.h Thu Dec 21 21:08:13 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: in.h,v 1.100 2017/02/16 08:12:44 knakahara Exp $ */
+/* $NetBSD: in.h,v 1.100.6.1 2017/12/21 21:08:13 snj 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.178.4.1
--- src/sys/netinet/in_pcb.c:1.178 Tue Apr 25 05:44:11 2017
+++ src/sys/netinet/in_pcb.c Thu Dec 21 21:08:13 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.178.4.1 2017/12/21 21:08:13 snj 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.178.4.1 2017/12/21 21:08:13 snj 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.63.6.1
--- src/sys/netinet/in_pcb.h:1.63 Thu Mar 2 05:29:31 2017
+++ src/sys/netinet/in_pcb.h Thu Dec 21 21:08:13 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.63.6.1 2017/12/21 21:08:13 snj 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.279.2.1 src/sys/netinet/ip_output.c:1.279.2.2
--- src/sys/netinet/ip_output.c:1.279.2.1 Fri Jul 7 09:23:01 2017
+++ src/sys/netinet/ip_output.c Thu Dec 21 21:08:13 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: ip_output.c,v 1.279.2.1 2017/07/07 09:23:01 martin Exp $ */
+/* $NetBSD: ip_output.c,v 1.279.2.2 2017/12/21 21:08:13 snj 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.279.2.1 2017/07/07 09:23:01 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.279.2.2 2017/12/21 21:08:13 snj 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,120 @@ 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)
+{
+ 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;
+ 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.119.6.1
--- src/sys/netinet/ip_var.h:1.119 Fri Mar 31 06:49:44 2017
+++ src/sys/netinet/ip_var.h Thu Dec 21 21:08:13 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.119.6.1 2017/12/21 21:08:13 snj 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);
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.164 src/sys/netinet/raw_ip.c:1.164.4.1
--- src/sys/netinet/raw_ip.c:1.164 Thu Apr 20 08:46:07 2017
+++ src/sys/netinet/raw_ip.c Thu Dec 21 21:08:13 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: raw_ip.c,v 1.164 2017/04/20 08:46:07 ozaki-r Exp $ */
+/* $NetBSD: raw_ip.c,v 1.164.4.1 2017/12/21 21:08:13 snj 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.164 2017/04/20 08:46:07 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: raw_ip.c,v 1.164.4.1 2017/12/21 21:08:13 snj Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -311,15 +311,30 @@ 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);
+ if (control != NULL)
+ m_freem(control);
+ if (error != 0)
+ goto release;
/*
* If the user handed us a complete IP packet, use it.
@@ -327,25 +342,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 *);
@@ -358,15 +375,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);
@@ -382,8 +401,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;
}
/*
@@ -755,12 +779,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) {
@@ -768,21 +786,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.233 src/sys/netinet/udp_usrreq.c:1.233.4.1
--- src/sys/netinet/udp_usrreq.c:1.233 Thu Apr 20 08:46:07 2017
+++ src/sys/netinet/udp_usrreq.c Thu Dec 21 21:08:13 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: udp_usrreq.c,v 1.233 2017/04/20 08:46:07 ozaki-r Exp $ */
+/* $NetBSD: udp_usrreq.c,v 1.233.4.1 2017/12/21 21:08:13 snj 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.233 2017/04/20 08:46:07 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: udp_usrreq.c,v 1.233.4.1 2017/12/21 21:08:13 snj Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -773,14 +773,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);
@@ -803,13 +805,29 @@ 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);
+ 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;
@@ -837,13 +855,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
@@ -1075,12 +1094,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();
@@ -1099,16 +1112,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.41.10.1
--- src/sys/netinet/udp_var.h:1.41 Wed Jan 20 22:01:18 2016
+++ src/sys/netinet/udp_var.h Thu Dec 21 21:08:13 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.41.10.1 2017/12/21 21:08:13 snj 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.20.4.1
--- src/tests/net/net/Makefile:1.20 Fri Mar 31 06:41:40 2017
+++ src/tests/net/net/Makefile Thu Dec 21 21:08:13 2017
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.20 2017/03/31 06:41:40 ozaki-r Exp $
+# $NetBSD: Makefile,v 1.20.4.1 2017/12/21 21:08:13 snj 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.2.2.2
--- /dev/null Thu Dec 21 21:08:13 2017
+++ src/tests/net/net/t_pktinfo_send.c Thu Dec 21 21:08:13 2017
@@ -0,0 +1,808 @@
+/* $NetBSD: t_pktinfo_send.c,v 1.2.2.2 2017/12/21 21:08:13 snj 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.2.2.2 2017/12/21 21:08:13 snj 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));
+
+ /* 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_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();
+}