Module Name:    src
Committed By:   martin
Date:           Sun Mar 18 10:57:02 UTC 2018

Modified Files:
        src/share/man/man4 [netbsd-8]: ip.4
        src/share/man/man9 [netbsd-8]: sockopt.9
        src/sys/kern [netbsd-8]: uipc_socket.c uipc_syscalls.c
        src/sys/netinet [netbsd-8]: in.h in_pcb.c in_pcb.h ip_input.c
            ip_output.c
        src/sys/sys [netbsd-8]: socketvar.h

Log Message:
Pull up following revision(s) (requested by tih in ticket #639):
        sys/kern/uipc_socket.c: revision 1.258
        sys/kern/uipc_socket.c: revision 1.259
        sys/netinet/ip_input.c: revision 1.364 (via patch)
        sys/netinet/ip_output.c: revision 1.289
        sys/netinet/in.h: revision 1.102
        sys/netinet/in_pcb.c: revision 1.181
        share/man/man9/sockopt.9: revision 1.11
        sys/netinet/in_pcb.h: revision 1.65
        sys/sys/socketvar.h: revision 1.146
        sys/kern/uipc_syscalls.c: revision 1.189
        sys/netinet/ip_output.c: revision 1.290
        share/man/man4/ip.4: revision 1.41
        share/man/man4/ip.4: revision 1.42
        sys/kern/uipc_syscalls.c: revision 1.190

pass valsize for getsockopt like we do for setsockopt
make sure that we have enough space, don't require the exact size
(Tom Ivar Helbekkmo)

1) "#define ipi_spec_dst ipi_addr" in <netinet/in.h>
2) Change the IP_RECVPKTINFO option to control the generation of
   IP_PKTINFO control messages, the way it's done in Solaris.
3) Remove the superfluous IP_RECVPKTINFO control message.
4) Change the IP_PKTINFO option to do different things depending on
   the parameter it's supplied with:
   - If it's sizeof(int), assume it's being used as in Linux:
     - If it's non-zero, turn on the IP_RECVPKTINFO option.
     - If it's zero, turn off the IP_RECVPKTINFO option.
   - If it's sizeof(struct in_pktinfo), assume it's being used as in
     Solaris, to set a default for the source interface and/or
     source address for outgoing packets on the socket.
5) Return what Linux or Solaris compatible code expects, depending
   on data size, and just added a fallback to a Linux (and current NetBSD)
   compatible value if the size is unknown (as it is now), or,
   in the future, if the calling application specifies a receiving
   buffer that doesn't match either data item.

From: Tom Ivar Helbekkmo

new sentence-new line

Remove comment now that the getsockopt code passes the size.

Add a new sockopt member to keep track of the actual size of the option
that should be returned to the caller in getsockopt(2).
(Tom Ivar Helbekkmo)


To generate a diff of this commit:
cvs rdiff -u -r1.36.20.1 -r1.36.20.2 src/share/man/man4/ip.4
cvs rdiff -u -r1.10 -r1.10.4.1 src/share/man/man9/sockopt.9
cvs rdiff -u -r1.255 -r1.255.2.1 src/sys/kern/uipc_socket.c
cvs rdiff -u -r1.186 -r1.186.6.1 src/sys/kern/uipc_syscalls.c
cvs rdiff -u -r1.100.6.2 -r1.100.6.3 src/sys/netinet/in.h
cvs rdiff -u -r1.178.4.2 -r1.178.4.3 src/sys/netinet/in_pcb.c
cvs rdiff -u -r1.63.6.1 -r1.63.6.2 src/sys/netinet/in_pcb.h
cvs rdiff -u -r1.355.2.5 -r1.355.2.6 src/sys/netinet/ip_input.c
cvs rdiff -u -r1.279.2.6 -r1.279.2.7 src/sys/netinet/ip_output.c
cvs rdiff -u -r1.144 -r1.144.6.1 src/sys/sys/socketvar.h

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

Modified files:

Index: src/share/man/man4/ip.4
diff -u src/share/man/man4/ip.4:1.36.20.1 src/share/man/man4/ip.4:1.36.20.2
--- src/share/man/man4/ip.4:1.36.20.1	Thu Dec 21 21:08:13 2017
+++ src/share/man/man4/ip.4	Sun Mar 18 10:57:02 2018
@@ -1,4 +1,4 @@
-.\"	$NetBSD: ip.4,v 1.36.20.1 2017/12/21 21:08:13 snj Exp $
+.\"	$NetBSD: ip.4,v 1.36.20.2 2018/03/18 10:57:02 martin 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 August 10, 2017
+.Dd December 31, 2017
 .Dt IP 4
 .Os
 .Sh NAME
@@ -111,8 +111,8 @@ setsockopt(s, IPPROTO_IP, IP_IPSEC_POLIC
 .Ed
 .Pp
 The
-.Dv IP_PKTINFO
-option can be used to turn on receiving of information about the source
+.Dv IP_RECVPKTINFO
+option can be used to turn on receiving of information about the destination
 address of the packet, and the interface index.
 The information is passed in a
 .Vt struct in_pktinfo
@@ -132,13 +132,25 @@ cmsg_type = IP_PKTINFO
 .Pp
 For
 .Xr sendmsg 2 ,
-the source address or output interface can be specified by adding
+the source address or output interface can be specified by adding an
 .Dv IP_PKTINFO
-to the control part of the message on a
+message to the control part of the message on a
 .Dv SOCK_DGRAM
 or
 .Dv SOCK_RAW
 socket.
+Setting ipi_ifindex will cause the primary address of that interface
+to be used; setting ipi_addr will directly choose that address.
+The IP_PKTINFO cmsghdr structure from a received message may be
+used unchanged, in which case the outgoing message will be sent
+from the address the incoming message was received on.
+.Pp
+Setting the
+.Dv IP_PKTINFO
+option on a socket, with the same
+.Vt struct in_pktinfo
+structure, will set the default source address to be used until set
+again, unless explicitly overridden on a per-packet basis, as above.
 .Pp
 The
 .Dv IP_PORTALGO
@@ -199,6 +211,19 @@ cmsg_level = IPPROTO_IP
 cmsg_type = IP_RECVDSTADDR
 .Ed
 .Pp
+For
+.Xr sendmsg 2 ,
+the source address can be specified by adding
+.Dv IP_SENDSRCADDR
+to the control part of the message on a
+.Dv SOCK_DGRAM
+or
+.Dv SOCK_RAW
+socket.
+The IP_RECVDSTADDR cmsghdr structure from a received message may
+be used unchanged, in which case the outgoing message will be sent
+from the address the incoming message was received on.
+.Pp
 If the
 .Dv IP_RECVIF
 option is enabled on a
@@ -219,12 +244,6 @@ cmsg_level = IPPROTO_IP
 cmsg_type = IP_RECVIF
 .Ed
 .Pp
-The
-.Dv IP_RECVPKTINFO
-option is similar to the
-.Dv IP_PKTINFO
-one, only in this case the inbound information is returned.
-.Pp
 If the
 .Dv IP_RECVTTL
 option is enabled on a
@@ -491,6 +510,25 @@ An unknown socket option name was given.
 The IP option field was improperly formed; an option field was
 shorter than the minimum value or longer than the option buffer provided.
 .El
+.Sh COMPATIBILITY
+The
+.Dv IP_RECVPKTINFO
+option is used because it is directly compatible with Solaris, AIX, etc.,
+and the
+.Dv IP_PKTINFO
+option is intended to be used in their manner, to set the default source
+address for outgoing packets on a
+.Dv SOCK_DGRAM
+or
+.Dv SOCK_RAW
+socket.
+For compatibility with Linux, however, if you attempt to set the
+.Dv IP_PKTINFO
+option, using an integer parameter as a boolean value, this will
+transparently manipulate the
+.Dv IP_RECVPKTINFO
+option instead.
+Source code compatbility with both environments is thus maintained.
 .Sh SEE ALSO
 .Xr getsockopt 2 ,
 .Xr recv 2 ,

Index: src/share/man/man9/sockopt.9
diff -u src/share/man/man9/sockopt.9:1.10 src/share/man/man9/sockopt.9:1.10.4.1
--- src/share/man/man9/sockopt.9:1.10	Mon Jan 16 12:54:25 2017
+++ src/share/man/man9/sockopt.9	Sun Mar 18 10:57:01 2018
@@ -1,4 +1,4 @@
-.\"	$NetBSD: sockopt.9,v 1.10 2017/01/16 12:54:25 njoly Exp $
+.\"	$NetBSD: sockopt.9,v 1.10.4.1 2018/03/18 10:57:01 martin Exp $
 .\"
 .\" Copyright (c) 2008 Iain Hibbert
 .\" All rights reserved.
@@ -23,7 +23,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd September 4, 2009
+.Dd January 3, 2018
 .Dt SOCKOPT 9
 .Os
 .Sh NAME
@@ -57,6 +57,7 @@ struct sockopt {
 	int		sopt_level;		/* option level */
 	int		sopt_name;		/* option name */
 	size_t		sopt_size;		/* data length */
+	size_t		sopt_retsize;		/* returned data length */
 	void *		sopt_data;		/* data pointer */
 	uint8_t		sopt_buf[sizeof(int)];	/* internal storage */
 };
@@ -133,7 +134,7 @@ context using
 which will not fail.
 .It Fn sockopt_setint "sopt" "value"
 Common case of set sockopt integer value.
-The sockpt structure must contain an int sized data field or be previously
+The sockopt structure must contain an int sized data field or be previously
 unset, in which case the data pointer will be set to the internal storage.
 .El
 .Sh CODE REFERENCES

Index: src/sys/kern/uipc_socket.c
diff -u src/sys/kern/uipc_socket.c:1.255 src/sys/kern/uipc_socket.c:1.255.2.1
--- src/sys/kern/uipc_socket.c:1.255	Sat May 27 21:02:56 2017
+++ src/sys/kern/uipc_socket.c	Sun Mar 18 10:57:01 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: uipc_socket.c,v 1.255 2017/05/27 21:02:56 bouyer Exp $	*/
+/*	$NetBSD: uipc_socket.c,v 1.255.2.1 2018/03/18 10:57:01 martin Exp $	*/
 
 /*-
  * Copyright (c) 2002, 2007, 2008, 2009 The NetBSD Foundation, Inc.
@@ -71,7 +71,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uipc_socket.c,v 1.255 2017/05/27 21:02:56 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uipc_socket.c,v 1.255.2.1 2018/03/18 10:57:01 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_compat_netbsd.h"
@@ -2109,8 +2109,12 @@ sockopt_set(struct sockopt *sopt, const 
 			return error;
 	}
 
-	KASSERT(sopt->sopt_size == len);
+	if (sopt->sopt_size < len)
+		return EINVAL;
+	
 	memcpy(sopt->sopt_data, buf, len);
+	sopt->sopt_retsize = len;
+
 	return 0;
 }
 
@@ -2169,9 +2173,12 @@ sockopt_setmbuf(struct sockopt *sopt, st
 			return error;
 	}
 
-	KASSERT(sopt->sopt_size == len);
+	if (sopt->sopt_size < len)
+		return EINVAL;
+	
 	m_copydata(m, 0, len, sopt->sopt_data);
 	m_freem(m);
+	sopt->sopt_retsize = len;
 
 	return 0;
 }

Index: src/sys/kern/uipc_syscalls.c
diff -u src/sys/kern/uipc_syscalls.c:1.186 src/sys/kern/uipc_syscalls.c:1.186.6.1
--- src/sys/kern/uipc_syscalls.c:1.186	Fri Feb  3 16:06:45 2017
+++ src/sys/kern/uipc_syscalls.c	Sun Mar 18 10:57:01 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: uipc_syscalls.c,v 1.186 2017/02/03 16:06:45 christos Exp $	*/
+/*	$NetBSD: uipc_syscalls.c,v 1.186.6.1 2018/03/18 10:57:01 martin Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uipc_syscalls.c,v 1.186 2017/02/03 16:06:45 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uipc_syscalls.c,v 1.186.6.1 2018/03/18 10:57:01 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_pipe.h"
@@ -1235,7 +1235,10 @@ sys_getsockopt(struct lwp *l, const stru
 	if ((error = fd_getsock1(SCARG(uap, s), &so, &fp)) != 0)
 		return (error);
 
-	sockopt_init(&sopt, SCARG(uap, level), SCARG(uap, name), 0);
+	if (valsize > MCLBYTES)
+		return EINVAL;
+
+	sockopt_init(&sopt, SCARG(uap, level), SCARG(uap, name), valsize);
 
 	if (fp->f_flag & FNOSIGPIPE)
 		so->so_options |= SO_NOSIGPIPE;
@@ -1246,7 +1249,7 @@ sys_getsockopt(struct lwp *l, const stru
 		goto out;
 
 	if (valsize > 0) {
-		len = min(valsize, sopt.sopt_size);
+		len = min(valsize, sopt.sopt_retsize);
 		error = copyout(sopt.sopt_data, SCARG(uap, val), len);
 		if (error)
 			goto out;

Index: src/sys/netinet/in.h
diff -u src/sys/netinet/in.h:1.100.6.2 src/sys/netinet/in.h:1.100.6.3
--- src/sys/netinet/in.h:1.100.6.2	Sun Feb 11 21:17:34 2018
+++ src/sys/netinet/in.h	Sun Mar 18 10:57:01 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: in.h,v 1.100.6.2 2018/02/11 21:17:34 snj Exp $	*/
+/*	$NetBSD: in.h,v 1.100.6.3 2018/03/18 10:57:01 martin Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1990, 1993
@@ -289,8 +289,10 @@ struct ip_opts {
 #define	IP_IPSEC_POLICY		22   /* struct; get/set security policy */
 #define	IP_RECVTTL		23   /* bool; receive IP TTL w/dgram */
 #define	IP_MINTTL		24   /* minimum TTL for packet or drop */
-#define	IP_PKTINFO		25   /* int; send interface and src addr */
-#define	IP_RECVPKTINFO		26   /* int; send interface and dst addr */
+#define	IP_PKTINFO		25   /* struct; set default src if/addr */
+#define	IP_RECVPKTINFO		26   /* int; receive dst if/addr w/dgram */
+
+#define IP_SENDSRCADDR IP_RECVDSTADDR /* FreeBSD compatibility */
 
 /*
  * Information sent in the control message of a datagram socket for
@@ -301,6 +303,8 @@ struct in_pktinfo {
 	unsigned int ipi_ifindex;	/* interface index */
 };
 
+#define ipi_spec_dst ipi_addr	/* Solaris/Linux compatibility */
+
 /*
  * Defaults and limits for options
  */

Index: src/sys/netinet/in_pcb.c
diff -u src/sys/netinet/in_pcb.c:1.178.4.2 src/sys/netinet/in_pcb.c:1.178.4.3
--- src/sys/netinet/in_pcb.c:1.178.4.2	Tue Jan  2 10:20:34 2018
+++ src/sys/netinet/in_pcb.c	Sun Mar 18 10:57:01 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: in_pcb.c,v 1.178.4.2 2018/01/02 10:20:34 snj Exp $	*/
+/*	$NetBSD: in_pcb.c,v 1.178.4.3 2018/03/18 10:57:01 martin 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.4.2 2018/01/02 10:20:34 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in_pcb.c,v 1.178.4.3 2018/03/18 10:57:01 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -204,6 +204,7 @@ in_pcballoc(struct socket *so, void *v)
 	inp->inp_errormtu = -1;
 	inp->inp_portalgo = PORTALGO_DEFAULT;
 	inp->inp_bindportonsend = false;
+	inp->inp_prefsrcip.s_addr = INADDR_ANY;
 #if defined(IPSEC)
 	if (ipsec_enabled) {
 		int error = ipsec_init_pcbpolicy(so, &inp->inp_sp);

Index: src/sys/netinet/in_pcb.h
diff -u src/sys/netinet/in_pcb.h:1.63.6.1 src/sys/netinet/in_pcb.h:1.63.6.2
--- src/sys/netinet/in_pcb.h:1.63.6.1	Thu Dec 21 21:08:13 2017
+++ src/sys/netinet/in_pcb.h	Sun Mar 18 10:57:01 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: in_pcb.h,v 1.63.6.1 2017/12/21 21:08:13 snj Exp $	*/
+/*	$NetBSD: in_pcb.h,v 1.63.6.2 2018/03/18 10:57:01 martin Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -95,6 +95,7 @@ struct inpcb {
 	int	  inp_errormtu;		/* MTU of last xmit status = EMSGSIZE */
 	uint8_t	  inp_ip_minttl;
 	bool      inp_bindportonsend;
+	struct    in_addr inp_prefsrcip; /* preferred src IP when wild  */
 };
 
 #define	inp_faddr	inp_ip.ip_dst
@@ -121,11 +122,9 @@ struct inpcb {
 					 * Cancels INP_HDRINCL.
 					 */
 #define	INP_RECVTTL		0x0800	/* receive incoming IP TTL */
-#define	INP_PKTINFO		0x1000	/* receive dst packet info */
-#define	INP_RECVPKTINFO		0x2000	/* receive dst packet info */
+#define	INP_RECVPKTINFO		0x1000	/* receive IP dst if/addr */
 #define	INP_CONTROLOPTS		(INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR|\
-				INP_RECVIF|INP_RECVTTL|INP_RECVPKTINFO|\
-				INP_PKTINFO)
+				INP_RECVIF|INP_RECVTTL|INP_RECVPKTINFO)
 
 #define	sotoinpcb(so)		((struct inpcb *)(so)->so_pcb)
 #define	inp_lock(inp)		solock((inp)->inp_socket)

Index: src/sys/netinet/ip_input.c
diff -u src/sys/netinet/ip_input.c:1.355.2.5 src/sys/netinet/ip_input.c:1.355.2.6
--- src/sys/netinet/ip_input.c:1.355.2.5	Mon Feb 26 13:32:01 2018
+++ src/sys/netinet/ip_input.c	Sun Mar 18 10:57:01 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip_input.c,v 1.355.2.5 2018/02/26 13:32:01 martin Exp $	*/
+/*	$NetBSD: ip_input.c,v 1.355.2.6 2018/03/18 10:57:01 martin Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -91,7 +91,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.355.2.5 2018/02/26 13:32:01 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.355.2.6 2018/03/18 10:57:01 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -1546,15 +1546,6 @@ ip_savecontrol(struct inpcb *inp, struct
 	}
 	if (inpflags & INP_RECVPKTINFO) {
 		struct in_pktinfo ipi;
-		ipi.ipi_addr = ip->ip_src;
-		ipi.ipi_ifindex = ifp->if_index;
-		*mp = sbcreatecontrol((void *) &ipi,
-		    sizeof(ipi), IP_RECVPKTINFO, IPPROTO_IP);
-		if (*mp)
-			mp = &(*mp)->m_next;
-	}
-	if (inpflags & INP_PKTINFO) {
-		struct in_pktinfo ipi;
 		ipi.ipi_addr = ip->ip_dst;
 		ipi.ipi_ifindex = ifp->if_index;
 		*mp = sbcreatecontrol((void *) &ipi,

Index: src/sys/netinet/ip_output.c
diff -u src/sys/netinet/ip_output.c:1.279.2.6 src/sys/netinet/ip_output.c:1.279.2.7
--- src/sys/netinet/ip_output.c:1.279.2.6	Mon Feb 19 18:39:43 2018
+++ src/sys/netinet/ip_output.c	Sun Mar 18 10:57:01 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: ip_output.c,v 1.279.2.6 2018/02/19 18:39:43 snj Exp $	*/
+/*	$NetBSD: ip_output.c,v 1.279.2.7 2018/03/18 10:57:01 martin 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.6 2018/02/19 18:39:43 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.279.2.7 2018/03/18 10:57:01 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -1081,6 +1081,7 @@ ip_ctloutput(int op, struct socket *so, 
 	struct ip *ip = &inp->inp_ip;
 	int inpflags = inp->inp_flags;
 	int optval = 0, error = 0;
+	struct in_pktinfo pktinfo;
 
 	KASSERT(solocked(so));
 
@@ -1103,7 +1104,6 @@ ip_ctloutput(int op, struct socket *so, 
 		case IP_TOS:
 		case IP_TTL:
 		case IP_MINTTL:
-		case IP_PKTINFO:
 		case IP_RECVOPTS:
 		case IP_RECVRETOPTS:
 		case IP_RECVDSTADDR:
@@ -1135,10 +1135,6 @@ ip_ctloutput(int op, struct socket *so, 
 	else \
 		inpflags &= ~bit;
 
-			case IP_PKTINFO:
-				OPTSET(INP_PKTINFO);
-				break;
-
 			case IP_RECVOPTS:
 				OPTSET(INP_RECVOPTS);
 				break;
@@ -1163,6 +1159,45 @@ ip_ctloutput(int op, struct socket *so, 
 				OPTSET(INP_RECVTTL);
 				break;
 			}
+			break;
+		case IP_PKTINFO:
+			error = sockopt_getint(sopt, &optval);
+			if (!error) {
+				/* Linux compatibility */
+				OPTSET(INP_RECVPKTINFO);
+				break;
+			}
+			error = sockopt_get(sopt, &pktinfo, sizeof(pktinfo));
+			if (error)
+				break;
+
+			if (pktinfo.ipi_ifindex == 0) {
+				inp->inp_prefsrcip = pktinfo.ipi_addr;
+				break;
+			}
+
+			/* Solaris compatibility */
+			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);
+				error = EADDRNOTAVAIL;
+				break;
+			}
+			ia = in_get_ia_from_ifp(ifp);
+			if (ia == NULL) {
+				pserialize_read_exit(s);
+				error = EADDRNOTAVAIL;
+				break;
+			}
+			inp->inp_prefsrcip = IA_SIN(ia)->sin_addr;
+			pserialize_read_exit(s);
+			break;
 		break;
 #undef OPTSET
 
@@ -1239,7 +1274,6 @@ ip_ctloutput(int op, struct socket *so, 
 			}
 			break;
 		}
-		case IP_PKTINFO:
 		case IP_TOS:
 		case IP_TTL:
 		case IP_MINTTL:
@@ -1269,10 +1303,6 @@ ip_ctloutput(int op, struct socket *so, 
 
 #define	OPTBIT(bit)	(inpflags & bit ? 1 : 0)
 
-			case IP_PKTINFO:
-				optval = OPTBIT(INP_PKTINFO);
-				break;
-
 			case IP_RECVOPTS:
 				optval = OPTBIT(INP_RECVOPTS);
 				break;
@@ -1300,6 +1330,33 @@ ip_ctloutput(int op, struct socket *so, 
 			error = sockopt_setint(sopt, optval);
 			break;
 
+		case IP_PKTINFO:
+			switch (sopt->sopt_size) {
+			case sizeof(int):
+				/* Linux compatibility */
+				optval = OPTBIT(INP_RECVPKTINFO);
+				error = sockopt_setint(sopt, optval);
+				break;
+			case sizeof(struct in_pktinfo):
+				/* Solaris compatibility */
+				pktinfo.ipi_ifindex = 0;
+				pktinfo.ipi_addr = inp->inp_prefsrcip;
+				error = sockopt_set(sopt, &pktinfo,
+				    sizeof(pktinfo));
+				break;
+			default:
+				/*
+				 * While size is stuck at 0, and, later, if
+				 * the caller doesn't use an exactly sized
+				 * recipient for the data, default to Linux
+				 * compatibility
+				 */
+				optval = OPTBIT(INP_RECVPKTINFO);
+				error = sockopt_setint(sopt, optval);
+				break;
+			}
+			break;
+
 #if 0	/* defined(IPSEC) */
 		case IP_IPSEC_POLICY:
 		{
@@ -1416,11 +1473,14 @@ ip_setpktopts(struct mbuf *control, stru
     struct inpcb *inp, kauth_cred_t cred)
 {
 	struct cmsghdr *cm;
-	struct in_pktinfo *pktinfo;
+	struct in_pktinfo pktinfo;
 	int error;
 
 	pktopts->ippo_imo = inp->inp_moptions;
-	sockaddr_in_init(&pktopts->ippo_laddr, &inp->inp_laddr, 0);
+
+	struct in_addr *ia = in_nullhost(inp->inp_prefsrcip) ? &inp->inp_laddr :
+	    &inp->inp_prefsrcip;
+	sockaddr_in_init(&pktopts->ippo_laddr, ia, 0);
 
 	if (control == NULL)
 		return 0;
@@ -1446,13 +1506,23 @@ ip_setpktopts(struct mbuf *control, stru
 
 		switch (cm->cmsg_type) {
 		case IP_PKTINFO:
-			if (cm->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
+			if (cm->cmsg_len != CMSG_LEN(sizeof(pktinfo)))
 				return EINVAL;
-
-			pktinfo = (struct in_pktinfo *)CMSG_DATA(cm);
-			error = ip_pktinfo_prepare(pktinfo, pktopts, flags,
+			memcpy(&pktinfo, CMSG_DATA(cm), sizeof(pktinfo));
+			error = ip_pktinfo_prepare(&pktinfo, pktopts, flags,
 			    cred);
-			if (error != 0)
+			if (error)
+				return error;
+			break;
+		case IP_SENDSRCADDR: /* FreeBSD compatibility */
+			if (cm->cmsg_len != CMSG_LEN(sizeof(struct in_addr)))
+				return EINVAL;
+			pktinfo.ipi_ifindex = 0;
+			pktinfo.ipi_addr =
+			    ((struct in_pktinfo *)CMSG_DATA(cm))->ipi_addr;
+			error = ip_pktinfo_prepare(&pktinfo, pktopts, flags,
+			    cred);
+			if (error)
 				return error;
 			break;
 		default:

Index: src/sys/sys/socketvar.h
diff -u src/sys/sys/socketvar.h:1.144 src/sys/sys/socketvar.h:1.144.6.1
--- src/sys/sys/socketvar.h:1.144	Fri Feb  3 16:06:45 2017
+++ src/sys/sys/socketvar.h	Sun Mar 18 10:57:01 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: socketvar.h,v 1.144 2017/02/03 16:06:45 christos Exp $	*/
+/*	$NetBSD: socketvar.h,v 1.144.6.1 2018/03/18 10:57:01 martin Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -224,6 +224,7 @@ struct sockopt {
 	int		sopt_level;		/* option level */
 	int		sopt_name;		/* option name */
 	size_t		sopt_size;		/* data length */
+	size_t		sopt_retsize;		/* returned data length */
 	void *		sopt_data;		/* data pointer */
 	uint8_t		sopt_buf[sizeof(int)];	/* internal storage */
 };

Reply via email to