Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 15 Nov 2004 13:43:31 +0300
From:      Gleb Smirnoff <glebius@freebsd.org>
To:        julian@freebsd.org, archie@freebsd.org
Cc:        net@freebsd.org
Subject:   divert(4) socket isn't connection oriented
Message-ID:  <20041115104331.GA93477@cell.sick.ru>

next in thread | raw e-mail | index | archive | help

--MGYHOYXEY6WxJCY8
Content-Type: text/plain; charset=koi8-r
Content-Disposition: inline

  Hi!

  I've spent several days digging in interaction between divert
protocol and ng_ksocket. I've find some oddities in there.

  Look at div_output(), it tells incoming packet from outgoing
by presence of sockaddr_in structure. Depending on it packet
is passed either to ip_input() or ip_output(). This sockaddr_in was
previuosly supplied by divert interface to application at the end
of divert_packet() with help of sbappendaddr_locked().

  Look at ng_ksocket_incoming2(), near 'pru_soreceive'. When
ng_ksocket takes data from socket it saves supplied sockaddr in
m_tag attached to packet. After packet travel thru netgraph(4) it
may return to ng_ksocket in ng_ksocket_rcvdata() and this sockaddr
will be used in call to sosend().

  It is important that ng_ksocket does not save sockaddr if socket is
connected (see ng_ksocket_incoming2(), near 'pru_soreceive'). And this
is correct!  If a generic socket is connected, all data must be sent to
connection destination. The problem is that divert(4) socket is
always marked as connected (see end of div_attach()), and thus
ng_ksocket_rcvdata() does not supply sockaddr in call to sosend(). So
div_output() _always_ sends data to ip_output()! This is strange and odd
but working - packets flow in both directions. This setup is working:

/usr/sbin/ngctl -f- <<-SEQ
        mkpeer echo dummy dummy
        name .:dummy echo_div
        mkpeer echo_div: ksocket echo inet/raw/divert
        name echo_div:echo div_sock
        rmhook dummy
        msg div_sock: bind inet/0.0.0.0:8888
SEQ
ipfw add 1 divert 8888 all from any to any via fxp0 in
ping -c 1 www.com

Since it is working, it was not noticed quickly. Real problems occur when
a multicast packet comes on interface: it is diverted to ng_ksocket, returned
and div_output() sends it to ip_output(). In ip_output() it is ip_mloopback()ed
and if_simloop()ed. A copy of packet enters divert socket, duplicated... a
forever loop and total freeze.

Removing 'always connected status' from divert sockets fixes the problem
and incoming packets go into ip_input(), not ip_output(). Please review attached
patch. It:

- removes SS_ISCONNECTED from so->so_state
- removes pru_disconnect method, since it won't be called
- removes pru_abort method, since it must not be called on divert
  socket. It was used indirectly via disconnect method.

With this patch codepath of packets is correct and incoming packets do
not enter ip_output() anymore.

An alternative may be adding a kludge in ng_ksocket_incoming2(), so that sockaddr
is saved always if socket is a divert socket. I don't like it.

Awaiting for your feedback!

-- 
Totus tuus, Glebius.
GLEBIUS-RIPN GLEB-RIPE

--MGYHOYXEY6WxJCY8
Content-Type: text/plain; charset=koi8-r
Content-Disposition: attachment; filename="ip_divert.c.diff"

Index: ip_divert.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_divert.c,v
retrieving revision 1.109
diff -u -r1.109 ip_divert.c
--- ip_divert.c	12 Nov 2004 22:17:42 -0000	1.109
+++ ip_divert.c	15 Nov 2004 10:14:37 -0000
@@ -415,12 +415,7 @@
 	inp->inp_ip_p = proto;
 	inp->inp_vflag |= INP_IPV4;
 	inp->inp_flags |= INP_HDRINCL;
-	/* The socket is always "connected" because
-	   we always know "where" to send the packet */
 	INP_UNLOCK(inp);
-	SOCK_LOCK(so);
-	so->so_state |= SS_ISCONNECTED;
-	SOCK_UNLOCK(so);
 	return 0;
 }
 
@@ -442,32 +437,6 @@
 }
 
 static int
-div_abort(struct socket *so)
-{
-	struct inpcb *inp;
-
-	INP_INFO_WLOCK(&divcbinfo);
-	inp = sotoinpcb(so);
-	if (inp == 0) {
-		INP_INFO_WUNLOCK(&divcbinfo);
-		return EINVAL;	/* ??? possible? panic instead? */
-	}
-	INP_LOCK(inp);
-	soisdisconnected(so);
-	in_pcbdetach(inp);
-	INP_INFO_WUNLOCK(&divcbinfo);
-	return 0;
-}
-
-static int
-div_disconnect(struct socket *so)
-{
-	if ((so->so_state & SS_ISCONNECTED) == 0)
-		return ENOTCONN;
-	return div_abort(so);
-}
-
-static int
 div_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
 {
 	struct inpcb *inp;
@@ -662,12 +631,10 @@
 #endif
 
 struct pr_usrreqs div_usrreqs = {
-	.pru_abort =		div_abort,
 	.pru_attach =		div_attach,
 	.pru_bind =		div_bind,
 	.pru_control =		in_control,
 	.pru_detach =		div_detach,
-	.pru_disconnect =	div_disconnect,
 	.pru_peeraddr =		div_peeraddr,
 	.pru_send =		div_send,
 	.pru_shutdown =		div_shutdown,

--MGYHOYXEY6WxJCY8--



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