Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 4 Aug 1998 21:47:05 -0400 (EDT)
From:      Garrett Wollman <wollman@khavrinen.lcs.mit.edu>
To:        net@FreeBSD.ORG
Subject:   Socket option processing changes
Message-ID:  <199808050147.VAA05270@khavrinen.lcs.mit.edu>

next in thread | raw e-mail | index | archive | help
Here's a patch I am presently developing.  It works on my machine
(or, rather, hasn't crashed yet).  It does not include support for
anything that isn't included in my laptop's config file.  This is
the last of the ``easy'' mbuf types to remove...  The standard
behavior of returning EINVAL when the provided option buffer is too
short ought to be documented.  If you have a patch for your favorite
protocol or option set, please send it to me.  Obviously, I'd like
some other reports of success or failure.

-GAWollman

Index: kern/uipc_proto.c
===================================================================
RCS file: /home/cvs/src/sys/kern/uipc_proto.c,v
retrieving revision 1.16
diff -u -r1.16 uipc_proto.c
--- uipc_proto.c	1998/06/21 14:53:18	1.16
+++ uipc_proto.c	1998/08/05 01:25:02
@@ -41,6 +41,7 @@
 #include <sys/socket.h>
 #include <sys/sysctl.h>
 #include <sys/un.h>
+#include <sys/unpcb.h>
 
 #include <net/raw_cb.h>
 
Index: kern/uipc_socket.c
===================================================================
RCS file: /home/cvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.42
diff -u -r1.42 uipc_socket.c
--- uipc_socket.c	1998/07/18 18:48:45	1.42
+++ uipc_socket.c	1998/08/05 01:25:02
@@ -898,31 +898,69 @@
 	sbrelease(&asb);
 }
 
+/*
+ * Perhaps this routine, and sooptcopyout(), below, ought to come in
+ * an additional variant to handle the case where the option value needs
+ * to be some kind of integer, but not a specific size.
+ * In addition to their use here, these functions are also called by the
+ * protocol-level pr_ctloutput() routines.
+ */
 int
-sosetopt(so, level, optname, m0, p)
-	register struct socket *so;
-	int level, optname;
-	struct mbuf *m0;
-	struct proc *p;
+sooptcopyin(sopt, buf, len, minlen)
+	struct	sockopt *sopt;
+	void	*buf;
+	size_t	len;
+	size_t	minlen;
 {
-	int error = 0;
-	register struct mbuf *m = m0;
+	size_t	valsize;
 
-	if (level != SOL_SOCKET) {
+	/*
+	 * If the user gives us more than we wanted, we ignore it,
+	 * but if we don't get the minimum length the caller
+	 * wants, we return EINVAL.  On success, sopt->sopt_valsize
+	 * is set to however much we actually retrieved.
+	 */
+	if ((valsize = sopt->sopt_valsize) < minlen)
+		return EINVAL;
+	if (valsize > len)
+		sopt->sopt_valsize = valsize = len;
+
+	if (sopt->sopt_p != 0)
+		return (copyin(sopt->sopt_val, buf, valsize));
+
+	bcopy(sopt->sopt_val, buf, valsize);
+	return 0;
+}
+
+int
+sosetopt(so, sopt)
+	struct socket *so;
+	struct sockopt *sopt;
+{
+	int	error, optval;
+	struct	linger l;
+	struct	timeval tv;
+	short	val;
+
+	error = 0;
+	if (sopt->sopt_level != SOL_SOCKET) {
 		if (so->so_proto && so->so_proto->pr_ctloutput)
 			return ((*so->so_proto->pr_ctloutput)
-				  (PRCO_SETOPT, so, level, optname, &m0, p));
+				  (so, sopt));
 		error = ENOPROTOOPT;
 	} else {
-		switch (optname) {
-
+		switch (sopt->sopt_name) {
 		case SO_LINGER:
-			if (m == NULL || m->m_len != sizeof (struct linger)) {
-				error = EINVAL;
+			error = sooptcopyin(sopt, &l, sizeof l, sizeof l);
+			if (error)
 				goto bad;
-			}
-			so->so_linger = mtod(m, struct linger *)->l_linger;
-			/* fall thru... */
+
+			so->so_linger = l.l_linger;
+			if (l.l_onoff)
+				so->so_options |= SO_LINGER;
+			else
+				so->so_options &= ~SO_LINGER;
+			break;
 
 		case SO_DEBUG:
 		case SO_KEEPALIVE:
@@ -933,45 +971,40 @@
 		case SO_REUSEPORT:
 		case SO_OOBINLINE:
 		case SO_TIMESTAMP:
-			if (m == NULL || m->m_len < sizeof (int)) {
-				error = EINVAL;
+			error = sooptcopyin(sopt, &optval, sizeof optval,
+					    sizeof optval);
+			if (error)
 				goto bad;
-			}
-			if (*mtod(m, int *))
-				so->so_options |= optname;
+			if (optval)
+				so->so_options |= sopt->sopt_name;
 			else
-				so->so_options &= ~optname;
+				so->so_options &= ~sopt->sopt_name;
 			break;
 
 		case SO_SNDBUF:
 		case SO_RCVBUF:
 		case SO_SNDLOWAT:
 		case SO_RCVLOWAT:
-		    {
-			int optval;
-
-			if (m == NULL || m->m_len < sizeof (int)) {
-				error = EINVAL;
+			error = sooptcopyin(sopt, &optval, sizeof optval,
+					    sizeof optval);
+			if (error)
 				goto bad;
-			}
 
 			/*
 			 * Values < 1 make no sense for any of these
 			 * options, so disallow them.
 			 */
-			optval = *mtod(m, int *);
 			if (optval < 1) {
 				error = EINVAL;
 				goto bad;
 			}
 
-			switch (optname) {
-
+			switch (sopt->sopt_name) {
 			case SO_SNDBUF:
 			case SO_RCVBUF:
-				if (sbreserve(optname == SO_SNDBUF ?
-				    &so->so_snd : &so->so_rcv,
-				    (u_long) optval) == 0) {
+				if (sbreserve(sopt->sopt_name == SO_SNDBUF ?
+					      &so->so_snd : &so->so_rcv,
+					      (u_long) optval) == 0) {
 					error = ENOBUFS;
 					goto bad;
 				}
@@ -993,27 +1026,21 @@
 				break;
 			}
 			break;
-		    }
 
 		case SO_SNDTIMEO:
 		case SO_RCVTIMEO:
-		    {
-			struct timeval *tv;
-			short val;
-
-			if (m == NULL || m->m_len < sizeof (*tv)) {
-				error = EINVAL;
+			error = sooptcopyin(sopt, &tv, sizeof tv,
+					    sizeof tv);
+			if (error)
 				goto bad;
-			}
-			tv = mtod(m, struct timeval *);
-			if (tv->tv_sec > SHRT_MAX / hz - hz) {
+
+			if (tv.tv_sec > SHRT_MAX / hz - hz) {
 				error = EDOM;
 				goto bad;
 			}
-			val = tv->tv_sec * hz + tv->tv_usec / tick;
-
-			switch (optname) {
+			val = tv.tv_sec * hz + tv.tv_usec / tick;
 
+			switch (sopt->sopt_name) {
 			case SO_SNDTIMEO:
 				so->so_snd.sb_timeo = val;
 				break;
@@ -1022,7 +1049,6 @@
 				break;
 			}
 			break;
-		    }
 
 		default:
 			error = ENOPROTOOPT;
@@ -1030,42 +1056,69 @@
 		}
 		if (error == 0 && so->so_proto && so->so_proto->pr_ctloutput) {
 			(void) ((*so->so_proto->pr_ctloutput)
-				  (PRCO_SETOPT, so, level, optname, &m0, p));
-			m = NULL;	/* freed by protocol */
+				  (so, sopt));
 		}
 	}
 bad:
-	if (m)
-		(void) m_free(m);
 	return (error);
 }
 
+/* Helper routine for getsockopt */
 int
-sogetopt(so, level, optname, mp, p)
-	register struct socket *so;
-	int level, optname;
-	struct mbuf **mp;
-	struct proc *p;
+sooptcopyout(sopt, buf, len)
+	struct	sockopt *sopt;
+	void	*buf;
+	size_t	len;
 {
-	register struct mbuf *m;
+	int	error;
+	size_t	valsize;
+
+	error = 0;
+
+	/*
+	 * Documented get behavior is that we always return a value,
+	 * possibly truncated to fit in the user's buffer.
+	 * We leave the correct length in sopt->sopt_valsize,
+	 * to be copied out in getsockopt().  Note that this
+	 * interface is not idempotent; the entire answer must
+	 * generated ahead of time.
+	 */
+	valsize = len;
+	if (sopt->sopt_valsize < valsize) {
+		valsize = sopt->sopt_valsize;
+		sopt->sopt_valsize = len;
+	}
+	if (sopt->sopt_val != 0) {
+		if (sopt->sopt_p != 0)
+			error = copyout(buf, sopt->sopt_val, valsize);
+		else
+			bcopy(buf, sopt->sopt_val, valsize);
+	}
+	return error;
+}
 
-	if (level != SOL_SOCKET) {
+int
+sogetopt(so, sopt)
+	struct socket *so;
+	struct sockopt *sopt;
+{
+	int	error, optval;
+	struct	linger l;
+	struct	timeval tv;
+
+	error = 0;
+	if (sopt->sopt_level != SOL_SOCKET) {
 		if (so->so_proto && so->so_proto->pr_ctloutput) {
 			return ((*so->so_proto->pr_ctloutput)
-				  (PRCO_GETOPT, so, level, optname, mp, p));
+				  (so, sopt));
 		} else
 			return (ENOPROTOOPT);
 	} else {
-		m = m_get(M_WAIT, MT_SOOPTS);
-		m->m_len = sizeof (int);
-
-		switch (optname) {
-
+		switch (sopt->sopt_name) {
 		case SO_LINGER:
-			m->m_len = sizeof (struct linger);
-			mtod(m, struct linger *)->l_onoff =
-				so->so_options & SO_LINGER;
-			mtod(m, struct linger *)->l_linger = so->so_linger;
+			l.l_onoff = so->so_options & SO_LINGER;
+			l.l_linger = so->so_linger;
+			error = sooptcopyout(sopt, &l, sizeof l);
 			break;
 
 		case SO_USELOOPBACK:
@@ -1077,53 +1130,51 @@
 		case SO_BROADCAST:
 		case SO_OOBINLINE:
 		case SO_TIMESTAMP:
-			*mtod(m, int *) = so->so_options & optname;
+			optval = so->so_options & sopt->sopt_name;
+integer:
+			error = sooptcopyout(sopt, &optval, sizeof optval);
 			break;
 
 		case SO_TYPE:
-			*mtod(m, int *) = so->so_type;
-			break;
+			optval = so->so_type;
+			goto integer;
 
 		case SO_ERROR:
-			*mtod(m, int *) = so->so_error;
+			optval = so->so_error;
 			so->so_error = 0;
-			break;
+			goto integer;
 
 		case SO_SNDBUF:
-			*mtod(m, int *) = so->so_snd.sb_hiwat;
-			break;
+			optval = so->so_snd.sb_hiwat;
+			goto integer;
 
 		case SO_RCVBUF:
-			*mtod(m, int *) = so->so_rcv.sb_hiwat;
-			break;
+			optval = so->so_rcv.sb_hiwat;
+			goto integer;
 
 		case SO_SNDLOWAT:
-			*mtod(m, int *) = so->so_snd.sb_lowat;
-			break;
+			optval = so->so_snd.sb_lowat;
+			goto integer;
 
 		case SO_RCVLOWAT:
-			*mtod(m, int *) = so->so_rcv.sb_lowat;
-			break;
+			optval = so->so_rcv.sb_lowat;
+			goto integer;
 
 		case SO_SNDTIMEO:
 		case SO_RCVTIMEO:
-		    {
-			int val = (optname == SO_SNDTIMEO ?
-			     so->so_snd.sb_timeo : so->so_rcv.sb_timeo);
-
-			m->m_len = sizeof(struct timeval);
-			mtod(m, struct timeval *)->tv_sec = val / hz;
-			mtod(m, struct timeval *)->tv_usec =
-			    (val % hz) * tick;
-			break;
-		    }
+			optval = (sopt->sopt_name == SO_SNDTIMEO ?
+				  so->so_snd.sb_timeo : so->so_rcv.sb_timeo);
+
+			tv.tv_sec = optval / hz;
+			tv.tv_usec = (optval % hz) * tick;
+			error = sooptcopyout(sopt, &tv, sizeof tv);
+			break;			
 
 		default:
-			(void)m_free(m);
-			return (ENOPROTOOPT);
+			error = ENOPROTOOPT;
+			break;
 		}
-		*mp = m;
-		return (0);
+		return (error);
 	}
 }
 
Index: kern/uipc_syscalls.c
===================================================================
RCS file: /home/cvs/src/sys/kern/uipc_syscalls.c,v
retrieving revision 1.40
diff -u -r1.40 uipc_syscalls.c
--- uipc_syscalls.c	1998/06/10 10:30:23	1.40
+++ uipc_syscalls.c	1998/08/05 01:25:02
@@ -981,34 +981,26 @@
 	} */ *uap;
 {
 	struct file *fp;
-	struct mbuf *m = NULL;
+	struct sockopt sopt;
 	int error;
 
+	if (uap->val == 0 && uap->valsize != 0)
+		return (EFAULT);
+	if (uap->valsize < 0)
+		return (EINVAL);
+
 	error = getsock(p->p_fd, uap->s, &fp);
 	if (error)
 		return (error);
-	if (uap->valsize > MCLBYTES)
-		return (EINVAL);
-	if (uap->val) {
-		m = m_get(M_WAIT, MT_SOOPTS);
-		if (m == NULL)
-			return (ENOBUFS);
-		if (uap->valsize > MLEN) {
-			MCLGET(m, M_WAIT);
-			if(!(m->m_flags & M_EXT)) {
-				m_free(m);
-				return (ENOBUFS);
-			}
-		}
-		error = copyin(uap->val, mtod(m, caddr_t), (u_int)uap->valsize);
-		if (error) {
-			(void) m_free(m);
-			return (error);
-		}
-		m->m_len = uap->valsize;
-	}
-	return (sosetopt((struct socket *)fp->f_data, uap->level,
-	    uap->name, m, p));
+
+	sopt.sopt_dir = SOPT_SET;
+	sopt.sopt_level = uap->level;
+	sopt.sopt_name = uap->name;
+	sopt.sopt_val = uap->val;
+	sopt.sopt_valsize = uap->valsize;
+	sopt.sopt_p = p;
+
+	return (sosetopt((struct socket *)fp->f_data, &sopt));
 }
 
 /* ARGSUSED */
@@ -1023,9 +1015,9 @@
 		int	*avalsize;
 	} */ *uap;
 {
-	struct file *fp;
-	struct mbuf *m = NULL, *m0;
-	int op, i, valsize, error;
+	int	valsize, error;
+	struct	file *fp;
+	struct	sockopt sopt;
 
 	error = getsock(p->p_fd, uap->s, &fp);
 	if (error)
@@ -1035,26 +1027,24 @@
 		    sizeof (valsize));
 		if (error)
 			return (error);
+		if (valsize < 0)
+			return (EINVAL);
 	} else
 		valsize = 0;
-	if ((error = sogetopt((struct socket *)fp->f_data, uap->level,
-	    uap->name, &m, p)) == 0 && uap->val && valsize && m != NULL) {
-		op = 0;
-		while (m && !error && op < valsize) {
-			i = min(m->m_len, (valsize - op));
-			error = copyout(mtod(m, caddr_t), uap->val, (u_int)i);
-			op += i;
-			uap->val += i;
-			m0 = m;
-			MFREE(m0,m);
-		}
-		valsize = op;
-		if (error == 0)
-			error = copyout((caddr_t)&valsize,
-			    (caddr_t)uap->avalsize, sizeof (valsize));
+
+	sopt.sopt_dir = SOPT_GET;
+	sopt.sopt_level = uap->level;
+	sopt.sopt_name = uap->name;
+	sopt.sopt_val = uap->val;
+	sopt.sopt_valsize = (size_t)valsize; /* checked non-negative above */
+	sopt.sopt_p = p;
+
+	error = sogetopt((struct socket *)fp->f_data, &sopt);
+	if (error == 0) {
+		valsize = sopt.sopt_valsize;
+		error = copyout((caddr_t)&valsize,
+				(caddr_t)uap->avalsize, sizeof (valsize));
 	}
-	if (m != NULL)
-		(void) m_free(m);
 	return (error);
 }
 
Index: net/if_vlan.c
===================================================================
RCS file: /home/cvs/src/sys/net/if_vlan.c,v
retrieving revision 1.2
diff -u -r1.2 if_vlan.c
--- if_vlan.c	1998/05/15 20:02:47	1.2
+++ if_vlan.c	1998/08/05 01:25:06
@@ -80,7 +80,7 @@
 
 static	void vlan_start(struct ifnet *ifp);
 static	void vlan_ifinit(void *foo);
-static	int vlan_ioctl(struct ifnet *ifp, int cmd, caddr_t addr);
+static	int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
 
 static void
 vlaninit(void *dummy)
@@ -262,7 +262,7 @@
 }
 
 static int
-vlan_ioctl(struct ifnet *ifp, int cmd, caddr_t data)
+vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 {
 	struct ifaddr *ifa;
 	struct ifnet *p;
Index: netinet/in.h
===================================================================
RCS file: /home/cvs/src/sys/netinet/in.h,v
retrieving revision 1.36
diff -u -r1.36 in.h
--- in.h	1998/07/06 03:20:12	1.36
+++ in.h	1998/08/05 01:25:07
@@ -429,21 +429,6 @@
 int	 in_localaddr __P((struct in_addr));
 char 	*inet_ntoa __P((struct in_addr)); /* in libkern */
 
-/* Firewall hooks */
-struct ip;
-typedef	int ip_fw_chk_t __P((struct ip**, int, struct ifnet*, u_int16_t*, struct mbuf**, struct sockaddr_in**));
-typedef	int ip_fw_ctl_t __P((int, struct mbuf**));
-extern	ip_fw_chk_t *ip_fw_chk_ptr;
-extern	ip_fw_ctl_t *ip_fw_ctl_ptr;
-
-/* IP NAT hooks */
-typedef	int ip_nat_t __P((struct ip**, struct mbuf**, struct ifnet*, int));
-typedef	int ip_nat_ctl_t __P((int, struct mbuf**));
-extern	ip_nat_t *ip_nat_ptr;
-extern	ip_nat_ctl_t *ip_nat_ctl_ptr;
-#define	IP_NAT_IN	0x00000001
-#define	IP_NAT_OUT	0x00000002
-
 #endif /* KERNEL */
 
 #endif
Index: netinet/in_proto.c
===================================================================
RCS file: /home/cvs/src/sys/netinet/in_proto.c,v
retrieving revision 1.46
diff -u -r1.46 in_proto.c
--- in_proto.c	1998/03/21 11:33:57	1.46
+++ in_proto.c	1998/08/05 01:25:07
@@ -71,15 +71,6 @@
 #include <netns/ns_if.h>
 #endif
 
-#ifdef TPIP
-void	tpip_input(), tpip_ctlinput(), tp_init(), tp_slowtimo(), tp_drain();
-int	tp_ctloutput(), tp_usrreq();
-#endif
-
-#ifdef EON
-void	eoninput(), eonctlinput(), eonprotoinit();
-#endif /* EON */
-
 extern	struct domain inetdomain;
 static	struct pr_usrreqs nousrreqs;
 
Index: netinet/ip_fw.h
===================================================================
RCS file: /home/cvs/src/sys/netinet/ip_fw.h,v
retrieving revision 1.33
diff -u -r1.33 ip_fw.h
--- ip_fw.h	1998/07/06 03:20:15	1.33
+++ ip_fw.h	1998/08/05 01:25:07
@@ -184,6 +184,23 @@
  */
 void ip_fw_init __P((void));
 
+/* Firewall hooks */
+struct ip;
+struct sockopt;
+typedef	int ip_fw_chk_t __P((struct ip **, int, struct ifnet *, u_int16_t *,
+			     struct mbuf **, struct sockaddr_in **));
+typedef	int ip_fw_ctl_t __P((struct sockopt *));
+extern	ip_fw_chk_t *ip_fw_chk_ptr;
+extern	ip_fw_ctl_t *ip_fw_ctl_ptr;
+
+/* IP NAT hooks */
+typedef	int ip_nat_t __P((struct ip **, struct mbuf **, struct ifnet *, int));
+typedef	int ip_nat_ctl_t __P((struct sockopt *));
+extern	ip_nat_t *ip_nat_ptr;
+extern	ip_nat_ctl_t *ip_nat_ctl_ptr;
+#define	IP_NAT_IN	0x00000001
+#define	IP_NAT_OUT	0x00000002
+
 #endif /* KERNEL */
 
 #endif /* _IP_FW_H */
Index: netinet/ip_input.c
===================================================================
RCS file: /home/cvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.97
diff -u -r1.97 ip_input.c
--- ip_input.c	1998/07/13 12:12:24	1.97
+++ ip_input.c	1998/08/05 01:25:09
@@ -142,6 +142,9 @@
 #endif
 
 #ifdef COMPAT_IPFW
+
+#include <netinet/ip_fw.h>
+
 /* Firewall hooks */
 ip_fw_chk_t *ip_fw_chk_ptr;
 ip_fw_ctl_t *ip_fw_ctl_ptr;
@@ -1234,7 +1237,7 @@
 
 	if (ip_nhops == 0)
 		return ((struct mbuf *)0);
-	m = m_get(M_DONTWAIT, MT_SOOPTS);
+	m = m_get(M_DONTWAIT, MT_HEADER);
 	if (m == 0)
 		return ((struct mbuf *)0);
 
Index: netinet/ip_mroute.c
===================================================================
RCS file: /home/cvs/src/sys/netinet/ip_mroute.c,v
retrieving revision 1.47
diff -u -r1.47 ip_mroute.c
--- ip_mroute.c	1998/06/30 10:56:31	1.47
+++ ip_mroute.c	1998/08/05 01:25:14
@@ -54,10 +54,8 @@
 extern int	_ip_mforward __P((struct ip *ip, struct ifnet *ifp,
 				  struct mbuf *m, struct ip_moptions *imo));
 extern int	_ip_mrouter_done __P((void));
-extern int	_ip_mrouter_get __P((int cmd, struct socket *so,
-				     struct mbuf **m));
-extern int	_ip_mrouter_set __P((int cmd, struct socket *so,
-				     struct mbuf *m));
+extern int	_ip_mrouter_get __P((struct socket *so, struct sockopt *sopt));
+extern int	_ip_mrouter_set __P((struct socket *so, struct sockopt *sopt));
 extern int	_mrt_ioctl __P((int req, caddr_t data, struct proc *p));
 
 /*
@@ -70,27 +68,25 @@
 u_int		rsvpdebug = 0;
 
 int
-_ip_mrouter_set(cmd, so, m)
-	int cmd;
+_ip_mrouter_set(so, sopt)
 	struct socket *so;
-	struct mbuf *m;
+	struct sockopt *sopt;
 {
 	return(EOPNOTSUPP);
 }
 
-int (*ip_mrouter_set)(int, struct socket *, struct mbuf *) = _ip_mrouter_set;
+int (*ip_mrouter_set)(struct socket *, struct sockopt *) = _ip_mrouter_set;
 
 
 int
-_ip_mrouter_get(cmd, so, m)
-	int cmd;
+_ip_mrouter_get(so, sopt)
 	struct socket *so;
-	struct mbuf **m;
+	struct sockopt *sopt;
 {
 	return(EOPNOTSUPP);
 }
 
-int (*ip_mrouter_get)(int, struct socket *, struct mbuf **) = _ip_mrouter_get;
+int (*ip_mrouter_get)(struct socket *, struct sockopt *) = _ip_mrouter_get;
 
 int
 _ip_mrouter_done()
@@ -161,17 +157,17 @@
 u_long (*ip_mcast_src)(int) = _ip_mcast_src;
 
 int
-ip_rsvp_vif_init(so, m)
+ip_rsvp_vif_init(so, sopt)
     struct socket *so;
-    struct mbuf *m;
+    struct sockopt *sopt;
 {
     return(EINVAL);
 }
 
 int
-ip_rsvp_vif_done(so, m)
+ip_rsvp_vif_done(so, sopt)
     struct socket *so;
-    struct mbuf *m;
+    struct sockopt *sopt;
 {
     return(EINVAL);
 }
Index: netinet/ip_mroute.h
===================================================================
RCS file: /home/cvs/src/sys/netinet/ip_mroute.h,v
retrieving revision 1.13
diff -u -r1.13 ip_mroute.h
--- ip_mroute.h	1997/02/22 09:41:35	1.13
+++ ip_mroute.h	1998/08/05 01:25:15
@@ -248,8 +248,10 @@
 
 #ifdef KERNEL
 
-extern int	(*ip_mrouter_set) __P((int, struct socket *, struct mbuf *));
-extern int	(*ip_mrouter_get) __P((int, struct socket *, struct mbuf **));
+struct sockopt;
+
+extern int	(*ip_mrouter_set) __P((struct socket *, struct sockopt *));
+extern int	(*ip_mrouter_get) __P((struct socket *, struct sockopt *));
 extern int	(*ip_mrouter_done) __P((void));
 #ifdef MROUTING
 extern int	(*mrt_ioctl) __P((int, caddr_t));
Index: netinet/ip_output.c
===================================================================
RCS file: /home/cvs/src/sys/netinet/ip_output.c,v
retrieving revision 1.79
diff -u -r1.79 ip_output.c
--- ip_output.c	1998/07/13 12:12:25	1.79
+++ ip_output.c	1998/08/05 01:25:19
@@ -72,6 +72,10 @@
 #undef COMPAT_IPFW
 #endif
 
+#ifdef COMPAT_IPFW
+#include <netinet/ip_fw.h>
+#endif
+
 #ifdef IPFIREWALL_FORWARD_DEBUG
 #define print_ip(a)	 printf("%ld.%ld.%ld.%ld",(ntohl(a.s_addr)>>24)&0xFF,\
 				 		  (ntohl(a.s_addr)>>16)&0xFF,\
@@ -85,10 +89,10 @@
 static void	ip_mloopback
 	__P((struct ifnet *, struct mbuf *, struct sockaddr_in *, int));
 static int	ip_getmoptions
-	__P((int, struct ip_moptions *, struct mbuf **));
-static int	ip_pcbopts __P((struct mbuf **, struct mbuf *));
+	__P((struct sockopt *, struct ip_moptions *));
+static int	ip_pcbopts __P((int, struct mbuf **, struct mbuf *));
 static int	ip_setmoptions
-	__P((int, struct ip_moptions **, struct mbuf *));
+	__P((struct sockopt *, struct ip_moptions **));
 
 #if defined(IPFILTER_LKM) || defined(IPFILTER)
 int	ip_optcopy __P((struct ip *, struct ip *));
@@ -743,33 +747,43 @@
  * IP socket option processing.
  */
 int
-ip_ctloutput(op, so, level, optname, mp, p)
-	int op;
+ip_ctloutput(so, sopt)
 	struct socket *so;
-	int level, optname;
-	struct mbuf **mp;
-	struct proc *p;
+	struct sockopt *sopt;
 {
-	register struct inpcb *inp = sotoinpcb(so);
-	register struct mbuf *m = *mp;
-	register int optval = 0;
-	int error = 0;
+	struct	inpcb *inp = sotoinpcb(so);
+	int	error, optval;
 
-	if (level != IPPROTO_IP) {
-		error = EINVAL;
-		if (op == PRCO_SETOPT && *mp)
-			(void) m_free(*mp);
-	} else switch (op) {
+	error = optval = 0;
+	if (sopt->sopt_level != IPPROTO_IP) {
+		return (EINVAL);
+	}
 
-	case PRCO_SETOPT:
-		switch (optname) {
+	switch (sopt->sopt_dir) {
+	case SOPT_SET:
+		switch (sopt->sopt_name) {
 		case IP_OPTIONS:
 #ifdef notyet
 		case IP_RETOPTS:
-			return (ip_pcbopts(optname, &inp->inp_options, m));
-#else
-			return (ip_pcbopts(&inp->inp_options, m));
 #endif
+		{
+			struct mbuf *m;
+			if (sopt->sopt_valsize > MLEN) {
+				error = EMSGSIZE;
+				break;
+			}
+			MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_HEADER);
+			if (m == 0) {
+				error = ENOBUFS;
+				break;
+			}
+			m->m_len = sopt->sopt_valsize;
+			error = sooptcopyin(sopt, mtod(m, char *), m->m_len,
+					    m->m_len);
+			
+			return (ip_pcbopts(sopt->sopt_name, &inp->inp_options,
+					   m));
+		}
 
 		case IP_TOS:
 		case IP_TTL:
@@ -777,41 +791,40 @@
 		case IP_RECVRETOPTS:
 		case IP_RECVDSTADDR:
 		case IP_RECVIF:
-			if (m == 0 || m->m_len != sizeof(int))
-				error = EINVAL;
-			else {
-				optval = *mtod(m, int *);
-				switch (optname) {
+			error = sooptcopyin(sopt, &optval, sizeof optval,
+					    sizeof optval);
+			if (error)
+				break;
 
-				case IP_TOS:
-					inp->inp_ip_tos = optval;
-					break;
+			switch (sopt->sopt_name) {
+			case IP_TOS:
+				inp->inp_ip_tos = optval;
+				break;
 
-				case IP_TTL:
-					inp->inp_ip_ttl = optval;
-					break;
+			case IP_TTL:
+				inp->inp_ip_ttl = optval;
+				break;
 #define	OPTSET(bit) \
 	if (optval) \
 		inp->inp_flags |= bit; \
 	else \
 		inp->inp_flags &= ~bit;
 
-				case IP_RECVOPTS:
-					OPTSET(INP_RECVOPTS);
-					break;
+			case IP_RECVOPTS:
+				OPTSET(INP_RECVOPTS);
+				break;
 
-				case IP_RECVRETOPTS:
-					OPTSET(INP_RECVRETOPTS);
-					break;
+			case IP_RECVRETOPTS:
+				OPTSET(INP_RECVRETOPTS);
+				break;
 
-				case IP_RECVDSTADDR:
-					OPTSET(INP_RECVDSTADDR);
-					break;
+			case IP_RECVDSTADDR:
+				OPTSET(INP_RECVDSTADDR);
+				break;
 
-				case IP_RECVIF:
-					OPTSET(INP_RECVIF);
-					break;
-				}
+			case IP_RECVIF:
+				OPTSET(INP_RECVIF);
+				break;
 			}
 			break;
 #undef OPTSET
@@ -822,36 +835,34 @@
 		case IP_MULTICAST_LOOP:
 		case IP_ADD_MEMBERSHIP:
 		case IP_DROP_MEMBERSHIP:
-			error = ip_setmoptions(optname, &inp->inp_moptions, m);
+			error = ip_setmoptions(sopt, &inp->inp_moptions);
 			break;
 
 		case IP_PORTRANGE:
-			if (m == 0 || m->m_len != sizeof(int))
-				error = EINVAL;
-			else {
-				optval = *mtod(m, int *);
-
-				switch (optval) {
+			error = sooptcopyin(sopt, &optval, sizeof optval,
+					    sizeof optval);
+			if (error)
+				break;
 
-				case IP_PORTRANGE_DEFAULT:
-					inp->inp_flags &= ~(INP_LOWPORT);
-					inp->inp_flags &= ~(INP_HIGHPORT);
-					break;
+			switch (optval) {
+			case IP_PORTRANGE_DEFAULT:
+				inp->inp_flags &= ~(INP_LOWPORT);
+				inp->inp_flags &= ~(INP_HIGHPORT);
+				break;
 
-				case IP_PORTRANGE_HIGH:
-					inp->inp_flags &= ~(INP_LOWPORT);
-					inp->inp_flags |= INP_HIGHPORT;
-					break;
+			case IP_PORTRANGE_HIGH:
+				inp->inp_flags &= ~(INP_LOWPORT);
+				inp->inp_flags |= INP_HIGHPORT;
+				break;
 
-				case IP_PORTRANGE_LOW:
-					inp->inp_flags &= ~(INP_HIGHPORT);
-					inp->inp_flags |= INP_LOWPORT;
-					break;
+			case IP_PORTRANGE_LOW:
+				inp->inp_flags &= ~(INP_HIGHPORT);
+				inp->inp_flags |= INP_LOWPORT;
+				break;
 
-				default:
-					error = EINVAL;
-					break;
-				}
+			default:
+				error = EINVAL;
+				break;
 			}
 			break;
 
@@ -859,21 +870,19 @@
 			error = ENOPROTOOPT;
 			break;
 		}
-		if (m)
-			(void)m_free(m);
 		break;
 
-	case PRCO_GETOPT:
-		switch (optname) {
+	case SOPT_GET:
+		switch (sopt->sopt_name) {
 		case IP_OPTIONS:
 		case IP_RETOPTS:
-			*mp = m = m_get(M_WAIT, MT_SOOPTS);
-			if (inp->inp_options) {
-				m->m_len = inp->inp_options->m_len;
-				bcopy(mtod(inp->inp_options, void *),
-				    mtod(m, void *), m->m_len);
-			} else
-				m->m_len = 0;
+			if (inp->inp_options)
+				error = sooptcopyout(sopt, 
+						     mtod(inp->inp_options,
+							  char *),
+						     inp->inp_options->m_len);
+			else
+				sopt->sopt_valsize = 0;
 			break;
 
 		case IP_TOS:
@@ -882,9 +891,8 @@
 		case IP_RECVRETOPTS:
 		case IP_RECVDSTADDR:
 		case IP_RECVIF:
-			*mp = m = m_get(M_WAIT, MT_SOOPTS);
-			m->m_len = sizeof(int);
-			switch (optname) {
+		case IP_PORTRANGE:
+			switch (sopt->sopt_name) {
 
 			case IP_TOS:
 				optval = inp->inp_ip_tos;
@@ -911,8 +919,17 @@
 			case IP_RECVIF:
 				optval = OPTBIT(INP_RECVIF);
 				break;
+
+			case IP_PORTRANGE:
+				if (inp->inp_flags & INP_HIGHPORT)
+					optval = IP_PORTRANGE_HIGH;
+				else if (inp->inp_flags & INP_LOWPORT)
+					optval = IP_PORTRANGE_LOW;
+				else
+					optval = 0;
+				break;
 			}
-			*mtod(m, int *) = optval;
+			error = sooptcopyout(sopt, &optval, sizeof optval);
 			break;
 
 		case IP_MULTICAST_IF:
@@ -921,21 +938,7 @@
 		case IP_MULTICAST_LOOP:
 		case IP_ADD_MEMBERSHIP:
 		case IP_DROP_MEMBERSHIP:
-			error = ip_getmoptions(optname, inp->inp_moptions, mp);
-			break;
-
-		case IP_PORTRANGE:
-			*mp = m = m_get(M_WAIT, MT_SOOPTS);
-			m->m_len = sizeof(int);
-
-			if (inp->inp_flags & INP_HIGHPORT)
-				optval = IP_PORTRANGE_HIGH;
-			else if (inp->inp_flags & INP_LOWPORT)
-				optval = IP_PORTRANGE_LOW;
-			else
-				optval = 0;
-
-			*mtod(m, int *) = optval;
+			error = ip_getmoptions(sopt, inp->inp_moptions);
 			break;
 
 		default:
@@ -953,12 +956,8 @@
  * with destination address if source routed.
  */
 static int
-#ifdef notyet
 ip_pcbopts(optname, pcbopt, m)
 	int optname;
-#else
-ip_pcbopts(pcbopt, m)
-#endif
 	struct mbuf **pcbopt;
 	register struct mbuf *m;
 {
@@ -1055,23 +1054,28 @@
 }
 
 /*
+ * XXX
+ * The whole multicast option thing needs to be re-thought.
+ * Several of these options are equally applicable to non-multicast
+ * transmission, and one (IP_MULTICAST_TTL) totally duplicates a
+ * standard option (IP_TTL).
+ */
+/*
  * Set the IP multicast options in response to user setsockopt().
  */
 static int
-ip_setmoptions(optname, imop, m)
-	int optname;
+ip_setmoptions(sopt, imop)
+	struct sockopt *sopt;
 	struct ip_moptions **imop;
-	struct mbuf *m;
 {
-	register int error = 0;
-	u_char loop;
-	register int i;
+	int error = 0;
+	int i;
 	struct in_addr addr;
-	register struct ip_mreq *mreq;
-	register struct ifnet *ifp;
-	register struct ip_moptions *imo = *imop;
+	struct ip_mreq mreq;
+	struct ifnet *ifp;
+	struct ip_moptions *imo = *imop;
 	struct route ro;
-	register struct sockaddr_in *dst;
+	struct sockaddr_in *dst;
 	int s;
 
 	if (imo == NULL) {
@@ -1092,18 +1096,16 @@
 		imo->imo_num_memberships = 0;
 	}
 
-	switch (optname) {
+	switch (sopt->sopt_name) {
 	/* store an index number for the vif you wanna use in the send */
 	case IP_MULTICAST_VIF:
-		if (!legal_vif_num) {
+		if (legal_vif_num == 0) {
 			error = EOPNOTSUPP;
 			break;
 		}
-		if (m == NULL || m->m_len != sizeof(int)) {
-			error = EINVAL;
+		error = sooptcopyin(sopt, &i, sizeof i, sizeof i);
+		if (error)
 			break;
-		}
-		i = *(mtod(m, int *));
 		if (!legal_vif_num(i) && (i != -1)) {
 			error = EINVAL;
 			break;
@@ -1115,11 +1117,9 @@
 		/*
 		 * Select the interface for outgoing multicast packets.
 		 */
-		if (m == NULL || m->m_len != sizeof(struct in_addr)) {
-			error = EINVAL;
+		error = sooptcopyin(sopt, &addr, sizeof addr, sizeof addr);
+		if (error)
 			break;
-		}
-		addr = *(mtod(m, struct in_addr *));
 		/*
 		 * INADDR_ANY is used to remove a previous selection.
 		 * When no interface is selected, a default one is
@@ -1148,25 +1148,50 @@
 	case IP_MULTICAST_TTL:
 		/*
 		 * Set the IP time-to-live for outgoing multicast packets.
-		 */
-		if (m == NULL || m->m_len != 1) {
-			error = EINVAL;
-			break;
+		 * The original multicast API required a char argument,
+		 * which is inconsistent with the rest of the socket API.
+		 * We allow either a char or an int.
+		 */
+		if (sopt->sopt_valsize == 1) {
+			u_char ttl;
+			error = sooptcopyin(sopt, &ttl, 1, 1);
+			if (error)
+				break;
+			imo->imo_multicast_ttl = ttl;
+		} else {
+			u_int ttl;
+			error = sooptcopyin(sopt, &ttl, sizeof ttl, 
+					    sizeof ttl);
+			if (error)
+				break;
+			if (ttl > 255)
+				error = EINVAL;
+			else
+				imo->imo_multicast_ttl = ttl;
 		}
-		imo->imo_multicast_ttl = *(mtod(m, u_char *));
 		break;
 
 	case IP_MULTICAST_LOOP:
 		/*
 		 * Set the loopback flag for outgoing multicast packets.
-		 * Must be zero or one.
-		 */
-		if (m == NULL || m->m_len != 1 ||
-		   (loop = *(mtod(m, u_char *))) > 1) {
-			error = EINVAL;
-			break;
+		 * Must be zero or one.  The original multicast API required a
+		 * char argument, which is inconsistent with the rest
+		 * of the socket API.  We allow either a char or an int.
+		 */
+		if (sopt->sopt_valsize == 1) {
+			u_char loop;
+			error = sooptcopyin(sopt, &loop, 1, 1);
+			if (error)
+				break;
+			imo->imo_multicast_loop = !!loop;
+		} else {
+			u_int loop;
+			error = sooptcopyin(sopt, &loop, sizeof loop,
+					    sizeof loop);
+			if (error)
+				break;
+			imo->imo_multicast_loop = !!loop;
 		}
-		imo->imo_multicast_loop = loop;
 		break;
 
 	case IP_ADD_MEMBERSHIP:
@@ -1174,12 +1199,11 @@
 		 * Add a multicast group membership.
 		 * Group must be a valid IP multicast address.
 		 */
-		if (m == NULL || m->m_len != sizeof(struct ip_mreq)) {
-			error = EINVAL;
+		error = sooptcopyin(sopt, &mreq, sizeof mreq, sizeof mreq);
+		if (error)
 			break;
-		}
-		mreq = mtod(m, struct ip_mreq *);
-		if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) {
+
+		if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) {
 			error = EINVAL;
 			break;
 		}
@@ -1188,12 +1212,12 @@
 		 * If no interface address was provided, use the interface of
 		 * the route to the given multicast address.
 		 */
-		if (mreq->imr_interface.s_addr == INADDR_ANY) {
+		if (mreq.imr_interface.s_addr == INADDR_ANY) {
 			bzero((caddr_t)&ro, sizeof(ro));
 			dst = (struct sockaddr_in *)&ro.ro_dst;
 			dst->sin_len = sizeof(*dst);
 			dst->sin_family = AF_INET;
-			dst->sin_addr = mreq->imr_multiaddr;
+			dst->sin_addr = mreq.imr_multiaddr;
 			rtalloc(&ro);
 			if (ro.ro_rt == NULL) {
 				error = EADDRNOTAVAIL;
@@ -1204,7 +1228,7 @@
 			rtfree(ro.ro_rt);
 		}
 		else {
-			INADDR_TO_IFP(mreq->imr_interface, ifp);
+			INADDR_TO_IFP(mreq.imr_interface, ifp);
 		}
 
 		/*
@@ -1223,7 +1247,7 @@
 		for (i = 0; i < imo->imo_num_memberships; ++i) {
 			if (imo->imo_membership[i]->inm_ifp == ifp &&
 			    imo->imo_membership[i]->inm_addr.s_addr
-						== mreq->imr_multiaddr.s_addr)
+						== mreq.imr_multiaddr.s_addr)
 				break;
 		}
 		if (i < imo->imo_num_memberships) {
@@ -1241,7 +1265,7 @@
 		 * address list for the given interface.
 		 */
 		if ((imo->imo_membership[i] =
-		    in_addmulti(&mreq->imr_multiaddr, ifp)) == NULL) {
+		    in_addmulti(&mreq.imr_multiaddr, ifp)) == NULL) {
 			error = ENOBUFS;
 			splx(s);
 			break;
@@ -1255,12 +1279,11 @@
 		 * Drop a multicast group membership.
 		 * Group must be a valid IP multicast address.
 		 */
-		if (m == NULL || m->m_len != sizeof(struct ip_mreq)) {
-			error = EINVAL;
+		error = sooptcopyin(sopt, &mreq, sizeof mreq, sizeof mreq);
+		if (error)
 			break;
-		}
-		mreq = mtod(m, struct ip_mreq *);
-		if (!IN_MULTICAST(ntohl(mreq->imr_multiaddr.s_addr))) {
+
+		if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) {
 			error = EINVAL;
 			break;
 		}
@@ -1270,10 +1293,10 @@
 		 * If an interface address was specified, get a pointer
 		 * to its ifnet structure.
 		 */
-		if (mreq->imr_interface.s_addr == INADDR_ANY)
+		if (mreq.imr_interface.s_addr == INADDR_ANY)
 			ifp = NULL;
 		else {
-			INADDR_TO_IFP(mreq->imr_interface, ifp);
+			INADDR_TO_IFP(mreq.imr_interface, ifp);
 			if (ifp == NULL) {
 				error = EADDRNOTAVAIL;
 				splx(s);
@@ -1287,7 +1310,7 @@
 			if ((ifp == NULL ||
 			     imo->imo_membership[i]->inm_ifp == ifp) &&
 			     imo->imo_membership[i]->inm_addr.s_addr ==
-			     mreq->imr_multiaddr.s_addr)
+			     mreq.imr_multiaddr.s_addr)
 				break;
 		}
 		if (i == imo->imo_num_memberships) {
@@ -1333,57 +1356,63 @@
  * Return the IP multicast options in response to user getsockopt().
  */
 static int
-ip_getmoptions(optname, imo, mp)
-	int optname;
+ip_getmoptions(sopt, imo)
+	struct sockopt *sopt;
 	register struct ip_moptions *imo;
-	register struct mbuf **mp;
 {
-	u_char *ttl;
-	u_char *loop;
-	struct in_addr *addr;
+	struct in_addr addr;
 	struct in_ifaddr *ia;
+	int error, optval;
+	u_char coptval;
 
-	*mp = m_get(M_WAIT, MT_SOOPTS);
-
-	switch (optname) {
-
+	error = 0;
+	switch (sopt->sopt_name) {
 	case IP_MULTICAST_VIF: 
 		if (imo != NULL)
-			*(mtod(*mp, int *)) = imo->imo_multicast_vif;
+			optval = imo->imo_multicast_vif;
 		else
-			*(mtod(*mp, int *)) = -1;
-		(*mp)->m_len = sizeof(int);
-		return(0);
+			optval = -1;
+		error = sooptcopyout(sopt, &optval, sizeof optval);
+		break;
 
 	case IP_MULTICAST_IF:
-		addr = mtod(*mp, struct in_addr *);
-		(*mp)->m_len = sizeof(struct in_addr);
 		if (imo == NULL || imo->imo_multicast_ifp == NULL)
-			addr->s_addr = INADDR_ANY;
+			addr.s_addr = INADDR_ANY;
 		else {
 			IFP_TO_IA(imo->imo_multicast_ifp, ia);
-			addr->s_addr = (ia == NULL) ? INADDR_ANY
-					: IA_SIN(ia)->sin_addr.s_addr;
+			addr.s_addr = (ia == NULL) ? INADDR_ANY
+				: IA_SIN(ia)->sin_addr.s_addr;
 		}
-		return (0);
+		error = sooptcopyout(sopt, &addr, sizeof addr);
+		break;
 
 	case IP_MULTICAST_TTL:
-		ttl = mtod(*mp, u_char *);
-		(*mp)->m_len = 1;
-		*ttl = (imo == NULL) ? IP_DEFAULT_MULTICAST_TTL
-				     : imo->imo_multicast_ttl;
-		return (0);
+		if (imo == 0)
+			optval = coptval = IP_DEFAULT_MULTICAST_TTL;
+		else
+			optval = coptval = imo->imo_multicast_ttl;
+		if (sopt->sopt_valsize == 1)
+			error = sooptcopyout(sopt, &coptval, 1);
+		else
+			error = sooptcopyout(sopt, &optval, sizeof optval);
+		break;
 
 	case IP_MULTICAST_LOOP:
-		loop = mtod(*mp, u_char *);
-		(*mp)->m_len = 1;
-		*loop = (imo == NULL) ? IP_DEFAULT_MULTICAST_LOOP
-				      : imo->imo_multicast_loop;
-		return (0);
+		if (imo == 0)
+			optval = coptval = IP_DEFAULT_MULTICAST_LOOP;
+		else
+			optval = coptval = imo->imo_multicast_loop;
+		if (sopt->sopt_valsize == 1)
+			error = sooptcopyout(sopt, &coptval, 1);
+		else
+			error = sooptcopyout(sopt, &optval, sizeof optval);
+		break;
 
 	default:
-		return (EOPNOTSUPP);
+		error = ENOPROTOOPT;
+		break;
 	}
+	return (error);
 }
 
 /*
Index: netinet/ip_var.h
===================================================================
RCS file: /home/cvs/src/sys/netinet/ip_var.h,v
retrieving revision 1.43
diff -u -r1.43 ip_var.h
--- ip_var.h	1998/07/13 12:20:07	1.43
+++ ip_var.h	1998/08/05 01:25:19
@@ -160,6 +160,7 @@
 #define	IP_ROUTETOIF		SO_DONTROUTE	/* bypass routing tables */
 #define	IP_ALLOWBROADCAST	SO_BROADCAST	/* can send broadcast packets */
 
+struct ip;
 struct inpcb;
 struct route;
 
@@ -175,8 +176,7 @@
 extern int rsvp_on;
 extern struct	pr_usrreqs rip_usrreqs;
 
-int	 ip_ctloutput __P((int, struct socket *, int, int, struct mbuf **,
-			   struct proc *));
+int	 ip_ctloutput __P((struct socket *, struct sockopt *sopt));
 void	 ip_drain __P((void));
 void	 ip_freemoptions __P((struct ip_moptions *));
 void	 ip_init __P((void));
@@ -190,8 +190,7 @@
 struct mbuf *
 	 ip_srcroute __P((void));
 void	 ip_stripoptions __P((struct mbuf *, struct mbuf *));
-int	 rip_ctloutput __P((int, struct socket *, int, int, struct mbuf **,
-			    struct proc *p));
+int	 rip_ctloutput __P((struct socket *, struct sockopt *));
 void	 rip_ctlinput __P((int, struct sockaddr *, void *));
 void	 rip_init __P((void));
 void	 rip_input __P((struct mbuf *, int));
@@ -200,8 +199,8 @@
 void	rsvp_input __P((struct mbuf *, int));
 int	ip_rsvp_init __P((struct socket *));
 int	ip_rsvp_done __P((void));
-int	ip_rsvp_vif_init __P((struct socket *, struct mbuf *));
-int	ip_rsvp_vif_done __P((struct socket *, struct mbuf *));
+int	ip_rsvp_vif_init __P((struct socket *, struct sockopt *));
+int	ip_rsvp_vif_done __P((struct socket *, struct sockopt *));
 void	ip_rsvp_force_done __P((struct socket *));
 
 #ifdef IPDIVERT
Index: netinet/raw_ip.c
===================================================================
RCS file: /home/cvs/src/sys/netinet/raw_ip.c,v
retrieving revision 1.54
diff -u -r1.54 raw_ip.c
--- raw_ip.c	1998/05/15 20:11:34	1.54
+++ raw_ip.c	1998/08/05 01:25:21
@@ -225,101 +225,127 @@
  * Raw IP socket option processing.
  */
 int
-rip_ctloutput(op, so, level, optname, m, p)
-	int op;
+rip_ctloutput(so, sopt)
 	struct socket *so;
-	int level, optname;
-	struct mbuf **m;
-	struct proc *p;
+	struct sockopt *sopt;
 {
-	register struct inpcb *inp = sotoinpcb(so);
-	register int error;
+	struct	inpcb *inp = sotoinpcb(so);
+	int	error, optval;
 
-	if (level != IPPROTO_IP) {
-		if (op == PRCO_SETOPT && *m)
-			(void)m_free(*m);
+	if (sopt->sopt_level != IPPROTO_IP)
 		return (EINVAL);
-	}
 
-	switch (optname) {
+	error = 0;
 
-	case IP_HDRINCL:
-		error = 0;
-		if (op == PRCO_SETOPT) {
-			if (m == 0 || *m == 0 || (*m)->m_len < sizeof (int))
-				error = EINVAL;
-			else if (*mtod(*m, int *))
-				inp->inp_flags |= INP_HDRINCL;
-			else
-				inp->inp_flags &= ~INP_HDRINCL;
-			if (*m)
-				(void)m_free(*m);
-		} else {
-			*m = m_get(M_WAIT, MT_SOOPTS);
-			(*m)->m_len = sizeof (int);
-			*mtod(*m, int *) = inp->inp_flags & INP_HDRINCL;
-		}
-		return (error);
+	switch (sopt->sopt_dir) {
+	case SOPT_GET:
+		switch (sopt->sopt_name) {
+		case IP_HDRINCL:
+			optval = inp->inp_flags & INP_HDRINCL;
+			error = sooptcopyout(sopt, &optval, sizeof optval);
+			break;
 
 #ifdef COMPAT_IPFW
-	case IP_FW_GET:
-		if (ip_fw_ctl_ptr == NULL || op == PRCO_SETOPT) {
-			if (*m) (void)m_free(*m);
-			return(EINVAL);
-		}
-		return (*ip_fw_ctl_ptr)(optname, m); 
-
-	case IP_FW_ADD:
-	case IP_FW_DEL:
-	case IP_FW_FLUSH:
-	case IP_FW_ZERO:
-		if (ip_fw_ctl_ptr == NULL || op != PRCO_SETOPT) {
-			if (*m) (void)m_free(*m);
-			return(EINVAL);
-		}
-		return (*ip_fw_ctl_ptr)(optname, m); 
+		case IP_FW_GET:
+			if (ip_fw_ctl_ptr == 0)
+				error = ENOPROTOOPT;
+			else
+				error = ip_fw_ctl_ptr(sopt);
+			break;
 
-	case IP_NAT:
-		if (ip_nat_ctl_ptr == NULL) {
-			if (*m) (void)m_free(*m);
-			return(EINVAL);
+		case IP_NAT:
+			if (ip_nat_ctl_ptr == 0)
+				error = ENOPROTOOPT;
+			else
+				error = ip_nat_ctl_ptr(sopt);
+			break;
+#endif /* COMPAT_IPFW */
+
+		case MRT_INIT:
+		case MRT_DONE:
+		case MRT_ADD_VIF:
+		case MRT_DEL_VIF:
+		case MRT_ADD_MFC:
+		case MRT_DEL_MFC:
+		case MRT_VERSION:
+		case MRT_ASSERT:
+			error = ip_mrouter_get(so, sopt);
+			break;
+
+		default:
+			error = ip_ctloutput(so, sopt);
+			break;
 		}
-		return (*ip_nat_ctl_ptr)(op, m); 
-
-#endif
-	case IP_RSVP_ON:
-		return ip_rsvp_init(so);
 		break;
 
-	case IP_RSVP_OFF:
-		return ip_rsvp_done();
-		break;
-
-	case IP_RSVP_VIF_ON:
-		return ip_rsvp_vif_init(so, *m);
+	case SOPT_SET:
+		switch (sopt->sopt_name) {
+		case IP_HDRINCL:
+			error = sooptcopyin(sopt, &optval, sizeof optval,
+					    sizeof optval);
+			if (error)
+				break;
+			if (optval)
+				inp->inp_flags |= INP_HDRINCL;
+			else
+				inp->inp_flags &= ~INP_HDRINCL;
+			break;
 
-	case IP_RSVP_VIF_OFF:
-		return ip_rsvp_vif_done(so, *m);
+#ifdef COMPAT_IPFW
+		case IP_FW_ADD:
+		case IP_FW_DEL:
+		case IP_FW_FLUSH:
+		case IP_FW_ZERO:
+			if (ip_fw_ctl_ptr == 0)
+				error = ENOPROTOOPT;
+			else
+				error = ip_fw_ctl_ptr(sopt);
+			break;
 
-	case MRT_INIT:
-	case MRT_DONE:
-	case MRT_ADD_VIF:
-	case MRT_DEL_VIF:
-	case MRT_ADD_MFC:
-	case MRT_DEL_MFC:
-	case MRT_VERSION:
-	case MRT_ASSERT:
-		if (op == PRCO_SETOPT) {
-			error = ip_mrouter_set(optname, so, *m);
-			if (*m)
-				(void)m_free(*m);
-		} else if (op == PRCO_GETOPT) {
-			error = ip_mrouter_get(optname, so, m);
-		} else
-			error = EINVAL;
-		return (error);
+		case IP_NAT:
+			if (ip_nat_ctl_ptr == 0)
+				error = ENOPROTOOPT;
+			else
+				error = ip_nat_ctl_ptr(sopt);
+			break;
+#endif /* COMPAT_IPFW */
+
+		case IP_RSVP_ON:
+			error = ip_rsvp_init(so);
+			break;
+
+		case IP_RSVP_OFF:
+			error = ip_rsvp_done();
+			break;
+
+			/* XXX - should be combined */
+		case IP_RSVP_VIF_ON:
+			error = ip_rsvp_vif_init(so, sopt);
+			break;
+			
+		case IP_RSVP_VIF_OFF:
+			error = ip_rsvp_vif_done(so, sopt);
+			break;
+
+		case MRT_INIT:
+		case MRT_DONE:
+		case MRT_ADD_VIF:
+		case MRT_DEL_VIF:
+		case MRT_ADD_MFC:
+		case MRT_DEL_MFC:
+		case MRT_VERSION:
+		case MRT_ASSERT:
+			error = ip_mrouter_set(so, sopt);
+			break;
+
+		default:
+			error = ip_ctloutput(so, sopt);
+			break;
+		}
+		break;
 	}
-	return (ip_ctloutput(op, so, level, optname, m, p));
+
+	return (error);
 }
 
 /*
@@ -340,7 +366,7 @@
 	int err;
 	int flags;
 
-	switch(cmd) {
+	switch (cmd) {
 	case PRC_IFDOWN:
 		for (ia = in_ifaddrhead.tqh_first; ia;
 		     ia = ia->ia_link.tqe_next) {
Index: netinet/tcp_usrreq.c
===================================================================
RCS file: /home/cvs/src/sys/netinet/tcp_usrreq.c,v
retrieving revision 1.37
diff -u -r1.37 tcp_usrreq.c
--- tcp_usrreq.c	1998/01/27 09:15:11	1.37
+++ tcp_usrreq.c	1998/08/05 01:25:23
@@ -560,104 +560,107 @@
 	return 0;
 }
 
+/*
+ * The new sockopt interface makes it possible for us to block in the
+ * copyin/out step (if we take a page fault).  Taking a page fault at
+ * splnet() is probably a Bad Thing.  (Since sockets and pcbs both now
+ * use TSM, there probably isn't any need for this function to run at
+ * splnet() any more.  This needs more examination.)
+ */
 int
-tcp_ctloutput(op, so, level, optname, mp, p)
-	int op;
+tcp_ctloutput(so, sopt)
 	struct socket *so;
-	int level, optname;
-	struct mbuf **mp;
-	struct proc *p;
+	struct sockopt *sopt;
 {
-	int error = 0, s;
-	struct inpcb *inp;
-	register struct tcpcb *tp;
-	register struct mbuf *m;
-	register int i;
+	int	error, opt, optval, s;
+	struct	inpcb *inp;
+	struct	tcpcb *tp;
+	struct	mbuf *m;
 
-	s = splnet();
+	error = 0;
+	s = splnet();		/* XXX */
 	inp = sotoinpcb(so);
 	if (inp == NULL) {
 		splx(s);
-		if (op == PRCO_SETOPT && *mp)
-			(void) m_free(*mp);
 		return (ECONNRESET);
 	}
-	if (level != IPPROTO_TCP) {
-		error = ip_ctloutput(op, so, level, optname, mp, p);
+	if (sopt->sopt_level != IPPROTO_TCP) {
+		error = ip_ctloutput(so, sopt);
 		splx(s);
 		return (error);
 	}
 	tp = intotcpcb(inp);
 
-	switch (op) {
-
-	case PRCO_SETOPT:
-		m = *mp;
-		switch (optname) {
-
+	switch (sopt->sopt_dir) {
+	case SOPT_SET:
+		switch (sopt->sopt_name) {
 		case TCP_NODELAY:
-			if (m == NULL || m->m_len < sizeof (int))
-				error = EINVAL;
-			else if (*mtod(m, int *))
-				tp->t_flags |= TF_NODELAY;
+		case TCP_NOOPT:
+		case TCP_NOPUSH:
+			error = sooptcopyin(sopt, &optval, sizeof optval,
+					    sizeof optval);
+			if (error)
+				break;
+
+			switch (sopt->sopt_name) {
+			case TCP_NODELAY:
+				opt = TF_NODELAY;
+				break;
+			case TCP_NOOPT:
+				opt = TF_NOOPT;
+				break;
+			case TCP_NOPUSH:
+				opt = TF_NOPUSH;
+				break;
+			default:
+				opt = 0; /* dead code to fool gcc */
+				break;
+			}
+
+			if (optval)
+				tp->t_flags |= opt;
 			else
-				tp->t_flags &= ~TF_NODELAY;
+				tp->t_flags &= ~opt;
 			break;
 
 		case TCP_MAXSEG:
-			if (m && (i = *mtod(m, int *)) > 0 && i <= tp->t_maxseg)
-				tp->t_maxseg = i;
-			else
-				error = EINVAL;
-			break;
+			error = sooptcopyin(sopt, &optval, sizeof optval,
+					    sizeof optval);
+			if (error)
+				break;
 
-		case TCP_NOOPT:
-			if (m == NULL || m->m_len < sizeof (int))
-				error = EINVAL;
-			else if (*mtod(m, int *))
-				tp->t_flags |= TF_NOOPT;
+			if (optval > 0 && optval <= tp->t_maxseg)
+				tp->t_maxseg = optval;
 			else
-				tp->t_flags &= ~TF_NOOPT;
-			break;
-
-		case TCP_NOPUSH:
-			if (m == NULL || m->m_len < sizeof (int))
 				error = EINVAL;
-			else if (*mtod(m, int *))
-				tp->t_flags |= TF_NOPUSH;
-			else
-				tp->t_flags &= ~TF_NOPUSH;
 			break;
 
 		default:
 			error = ENOPROTOOPT;
 			break;
 		}
-		if (m)
-			(void) m_free(m);
 		break;
 
-	case PRCO_GETOPT:
-		*mp = m = m_get(M_WAIT, MT_SOOPTS);
-		m->m_len = sizeof(int);
-
-		switch (optname) {
+	case SOPT_GET:
+		switch (sopt->sopt_name) {
 		case TCP_NODELAY:
-			*mtod(m, int *) = tp->t_flags & TF_NODELAY;
+			optval = tp->t_flags & TF_NODELAY;
 			break;
 		case TCP_MAXSEG:
-			*mtod(m, int *) = tp->t_maxseg;
+			optval = tp->t_maxseg;
 			break;
 		case TCP_NOOPT:
-			*mtod(m, int *) = tp->t_flags & TF_NOOPT;
+			optval = tp->t_flags & TF_NOOPT;
 			break;
 		case TCP_NOPUSH:
-			*mtod(m, int *) = tp->t_flags & TF_NOPUSH;
+			optval = tp->t_flags & TF_NOPUSH;
 			break;
 		default:
 			error = ENOPROTOOPT;
 			break;
 		}
+		if (error == 0)
+			error = sooptcopyout(sopt, &optval, sizeof optval);
 		break;
 	}
 	splx(s);
Index: netinet/tcp_var.h
===================================================================
RCS file: /home/cvs/src/sys/netinet/tcp_var.h,v
retrieving revision 1.46
diff -u -r1.46 tcp_var.h
--- tcp_var.h	1998/07/13 11:09:52	1.46
+++ tcp_var.h	1998/08/05 01:25:24
@@ -344,8 +344,7 @@
 struct tcpcb *
 	 tcp_close __P((struct tcpcb *));
 void	 tcp_ctlinput __P((int, struct sockaddr *, void *));
-int	 tcp_ctloutput __P((int, struct socket *, int, int, struct mbuf **,
-			    struct proc *));
+int	 tcp_ctloutput __P((struct socket *, struct sockopt *));
 struct tcpcb *
 	 tcp_drop __P((struct tcpcb *, int));
 void	 tcp_drain __P((void));
Index: nfs/nfs_socket.c
===================================================================
RCS file: /home/cvs/src/sys/nfs/nfs_socket.c,v
retrieving revision 1.42
diff -u -r1.42 nfs_socket.c
--- nfs_socket.c	1998/07/15 02:32:24	1.42
+++ nfs_socket.c	1998/08/05 01:25:30
@@ -282,16 +282,28 @@
 		if (nmp->nm_sotype != SOCK_STREAM)
 			panic("nfscon sotype");
 		if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
-			MGET(m, M_WAIT, MT_SOOPTS);
-			*mtod(m, int32_t *) = 1;
-			m->m_len = sizeof(int32_t);
-			sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m, p);
+			struct sockopt sopt;
+			int val;
+
+			bzero(&sopt, sizeof sopt);
+			sopt.sopt_level = SOL_SOCKET;
+			sopt.sopt_name = SO_KEEPALIVE;
+			sopt.sopt_val = &val;
+			sopt.sopt_valsize = sizeof val;
+			val = 1;
+			sosetopt(so, &sopt);
 		}
 		if (so->so_proto->pr_protocol == IPPROTO_TCP) {
-			MGET(m, M_WAIT, MT_SOOPTS);
-			*mtod(m, int32_t *) = 1;
-			m->m_len = sizeof(int32_t);
-			sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m, p);
+			struct sockopt sopt;
+			int val;
+
+			bzero(&sopt, sizeof sopt);
+			sopt.sopt_level = IPPROTO_TCP;
+			sopt.sopt_name = TCP_NODELAY;
+			sopt.sopt_val = &val;
+			sopt.sopt_valsize = sizeof val;
+			val = 1;
+			sosetopt(so, &sopt);
 		}
 		sndreserve = (nmp->nm_wsize + NFS_MAXPKTHDR +
 		    sizeof (u_int32_t)) * 2;
Index: nfs/nfs_syscalls.c
===================================================================
RCS file: /home/cvs/src/sys/nfs/nfs_syscalls.c,v
retrieving revision 1.41
diff -u -r1.41 nfs_syscalls.c
--- nfs_syscalls.c	1998/05/31 20:08:55	1.41
+++ nfs_syscalls.c	1998/08/05 01:25:33
@@ -400,17 +400,29 @@
 	 * repeatedly for the same socket, but that isn't harmful.
 	 */
 	if (so->so_type == SOCK_STREAM) {
-		MGET(m, M_WAIT, MT_SOOPTS);
-		*mtod(m, int32_t *) = 1;
-		m->m_len = sizeof(int32_t);
-		sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m, p);
+		struct sockopt sopt;
+		int val;
+
+		bzero(&sopt, sizeof sopt);
+		sopt.sopt_level = SOL_SOCKET;
+		sopt.sopt_name = SO_KEEPALIVE;
+		sopt.sopt_val = &val;
+		sopt.sopt_valsize = sizeof val;
+		val = 1;
+		sosetopt(so, &sopt);
 	}
 	if (so->so_proto->pr_domain->dom_family == AF_INET &&
 	    so->so_proto->pr_protocol == IPPROTO_TCP) {
-		MGET(m, M_WAIT, MT_SOOPTS);
-		*mtod(m, int32_t *) = 1;
-		m->m_len = sizeof(int32_t);
-		sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m, p);
+		struct sockopt sopt;
+		int val;
+
+		bzero(&sopt, sizeof sopt);
+		sopt.sopt_level = IPPROTO_TCP;
+		sopt.sopt_name = TCP_NODELAY;
+		sopt.sopt_val = &val;
+		sopt.sopt_valsize = sizeof val;
+		val = 1;
+		sosetopt(so, &sopt);
 	}
 	so->so_rcv.sb_flags &= ~SB_NOINTR;
 	so->so_rcv.sb_timeo = 0;
Index: sys/mbuf.h
===================================================================
RCS file: /home/cvs/src/sys/sys/mbuf.h,v
retrieving revision 1.27
diff -u -r1.27 mbuf.h
--- mbuf.h	1998/07/15 04:17:53	1.27
+++ mbuf.h	1998/08/05 01:25:37
@@ -138,7 +138,7 @@
 /*efine	MT_HTABLE	6*/	/* IMP host tables */
 /*efine	MT_ATABLE	7*/	/* address resolution tables */
 #define	MT_SONAME	8	/* socket name */
-#define	MT_SOOPTS	10	/* socket options */
+/*efine	MT_SOOPTS	10*/	/* socket options */
 #define	MT_FTABLE	11	/* fragment reassembly header */
 /*efine	MT_RIGHTS	12*/	/* access rights */
 /*efine	MT_IFADDR	13*/	/* interface address */
@@ -168,10 +168,10 @@
  * drivers.
  */
 #define	MBUFLOCK(code) \
-	{ int ms = splimp(); \
+	do { int ms = splimp(); \
 	  { code } \
 	  splx(ms); \
-	}
+	} while(0)
 
 /*
  * mbuf allocation/deallocation macros:
Index: sys/protosw.h
===================================================================
RCS file: /home/cvs/src/sys/sys/protosw.h,v
retrieving revision 1.24
diff -u -r1.24 protosw.h
--- protosw.h	1998/06/07 17:13:03	1.24
+++ protosw.h	1998/08/05 01:25:37
@@ -42,6 +42,7 @@
 struct proc;
 struct sockaddr;
 struct socket;
+struct sockopt;
 
 /*#ifdef KERNEL*/
 /*
@@ -58,13 +59,13 @@
  *
  * Protocols pass data between themselves as chains of mbufs using
  * the pr_input and pr_output hooks.  Pr_input passes data up (towards
- * UNIX) and pr_output passes it down (towards the imps); control
+ * the users) and pr_output passes it down (towards the interfaces); control
  * information passes up and down on pr_ctlinput and pr_ctloutput.
  * The protocol is responsible for the space occupied by any the
  * arguments to these entries and must dispose it.
  *
- * The userreq routine interfaces protocols to the system and is
- * described below.
+ * In retrospect, it would be a lot nicer to use an interface
+ * similar to the vnode VOP interface.
  */
 struct protosw {
 	short	pr_type;		/* socket type used for */
@@ -78,8 +79,7 @@
 					/* output to protocol (from above) */
 	void	(*pr_ctlinput)__P((int, struct sockaddr *, void *));
 					/* control input (from below) */
-	int	(*pr_ctloutput)__P((int, struct socket *, int, int,
-				    struct mbuf **, struct proc *));
+	int	(*pr_ctloutput)__P((struct socket *, struct sockopt *));
 					/* control output (from above) */
 /* user-protocol hook */
 	void	*pr_ousrreq;
Index: sys/socketvar.h
===================================================================
RCS file: /home/cvs/src/sys/sys/socketvar.h,v
retrieving revision 1.28
diff -u -r1.28 socketvar.h
--- socketvar.h	1998/06/07 17:13:03	1.28
+++ socketvar.h	1998/08/05 01:25:38
@@ -102,9 +102,10 @@
 #define	SB_UPCALL	0x20		/* someone wants an upcall */
 #define	SB_NOINTR	0x40		/* operations not interruptible */
 
-	void	(*so_upcall) __P((struct socket *so, caddr_t arg, int waitf));
-	caddr_t	so_upcallarg;		/* Arg for above */
+	void	(*so_upcall) __P((struct socket *, void *, int));
+	void	*so_upcallarg;
 	uid_t	so_uid;			/* who opened the socket */
+	/* NB: generation count must not be first; easiest to make it last. */
 	so_gen_t so_gencnt;		/* generation count */
 };
 
@@ -119,7 +120,6 @@
 #define	SS_CANTRCVMORE		0x0020	/* can't receive more data from peer */
 #define	SS_RCVATMARK		0x0040	/* at mark on input */
 
-/*efine	SS_PRIV			0x0080	   privileged for broadcast, raw... */
 #define	SS_NBIO			0x0100	/* non-blocking ops */
 #define	SS_ASYNC		0x0200	/* async i/o notify */
 #define	SS_ISCONFIRMING		0x0400	/* deciding to accept connection req */
@@ -242,6 +242,20 @@
 
 #ifdef KERNEL
 
+/*
+ * Argument structure for sosetopt et seq.  This is in the KERNEL
+ * section because it will never be visible to user code.
+ */
+enum sopt_dir { SOPT_GET, SOPT_SET };
+struct sockopt {
+	enum	sopt_dir sopt_dir; /* is this a get or a set? */
+	int	sopt_level;	/* second arg of [gs]etsockopt */
+	int	sopt_name;	/* third arg of [gs]etsockopt */
+	void   *sopt_val;	/* fourth arg of [gs]etsockopt */
+	size_t	sopt_valsize;	/* (almost) fifth arg of [gs]etsockopt */
+	struct	proc *sopt_p;	/* calling process or null if kernel */
+};
+
 #ifdef MALLOC_DECLARE
 MALLOC_DECLARE(M_PCB);
 MALLOC_DECLARE(M_SONAME);
@@ -309,8 +323,7 @@
 void	sodealloc __P((struct socket *so));
 int	sodisconnect __P((struct socket *so));
 void	sofree __P((struct socket *so));
-int	sogetopt __P((struct socket *so, int level, int optname,
-	    struct mbuf **mp, struct proc *p));
+int	sogetopt __P((struct socket *so, struct sockopt *sopt));
 void	sohasoutofband __P((struct socket *so));
 void	soisconnected __P((struct socket *so));
 void	soisconnecting __P((struct socket *so));
@@ -321,6 +334,9 @@
 	sodropablereq __P((struct socket *head));
 struct socket *
 	sonewconn __P((struct socket *head, int connstatus));
+int	sooptcopyin __P((struct sockopt *sopt, void *buf, size_t len,
+			 size_t minlen));
+int	sooptcopyout __P((struct sockopt *sopt, void *buf, size_t len));
 int	sopoll __P((struct socket *so, int events, struct ucred *cred,
 		    struct proc *p));
 int	soreceive __P((struct socket *so, struct sockaddr **paddr,
@@ -331,8 +347,7 @@
 int	sosend __P((struct socket *so, struct sockaddr *addr, struct uio *uio,
 		    struct mbuf *top, struct mbuf *control, int flags,
 		    struct proc *p));
-int	sosetopt __P((struct socket *so, int level, int optname,
-	    struct mbuf *m0, struct proc *p));
+int	sosetopt __P((struct socket *so, struct sockopt *sopt));
 int	soshutdown __P((struct socket *so, int how));
 void	sotoxsocket __P((struct socket *so, struct xsocket *xso));
 void	sowakeup __P((struct socket *so, struct sockbuf *sb));

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-net" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199808050147.VAA05270>