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(); +}