freeBSD /ipfw/ divert socket

Kelly Yancey kbyanc at posi.net
Tue Apr 25 03:12:35 UTC 2006


On Mon, 24 Apr 2006, Kelly Yancey wrote:

> On Fri, 21 Apr 2006, Amit Mondal wrote:
>
> > Hi All,
> >
> > I need a little help with FreeBSD Kernel stuff. I wanna use Divert Socket to
> > sniff IP packet in FreeBSD.
> > For that I have compiled the kernel with options IPDIVERT and everything is
> > ok.
> >
> > Now, when I am not really sniffing and re-injecting the packet back to the
> > network stack, it is basically dropping all the packets. But I want it
> > pass-through it, when no application is reading at divert socket. My
> > question is, HOW CAN I MAKE IT PASS-THROUGH? IF NO APPLICATION IS READING
> > FROM DIVERT SOCKET, IT SHOULD WORK AS IF THERE IS NO DIVERT SOCKET.
> >
> > Thanks in adavnce
> >
> > Rgds
> > Amit
> >
>
>   Attached is a really old patch I made against FreeBSD 4.7.  It might
> apply to 4.9.  Even if it doesn't, it should give you a pretty good idea
> how to implement the functionality you desire.
>
>   Kelly
>

  Sorry, wrong patch.  The correct patch is attached.

  Kelly

--
Kelly Yancey  -  kbyanc@{posi.net,FreeBSD.org}  -  kelly at nttmcl.com
FreeBSD, The Power To Serve: http://www.freebsd.org/
-------------- next part --------------
Index: sys/netinet/ip_divert.c
===================================================================
RCS file: /home/cvs/acs/base/src/sys/netinet/ip_divert.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -p -r1.3 -r1.4
--- ip_divert.c	10 Oct 2002 20:42:00 -0000	1.3
+++ ip_divert.c	23 Nov 2002 05:34:10 -0000	1.4
@@ -109,6 +109,23 @@ static u_long	div_recvspace = DIVRCVQ;	/
 /* Optimization: have this preinitialized */
 static struct sockaddr_in divsrc = { sizeof(divsrc), AF_INET };
 
+
+static int	div_output(struct socket *so, struct mbuf *m,
+			   struct sockaddr_in *sin, struct mbuf *control);
+static int	div_attach(struct socket *so, int proto, struct proc *p);
+static int	div_detach(struct socket *so);
+static int	div_abort(struct socket *so);
+static int	div_disconnect(struct socket *so);
+static int	div_bind(struct socket *so, struct sockaddr *nam,
+			 struct proc *p);
+static int	div_shutdown(struct socket *so);
+static int	div_send(struct socket *so, int flags, struct mbuf *m,
+			 struct sockaddr *nam, struct mbuf *control,
+			 struct proc *p);
+static int	div_pcblist(SYSCTL_HANDLER_ARGS);
+
+
+
 /*
  * Initialize divert connection block queue.
  */
@@ -146,8 +163,9 @@ div_input(struct mbuf *m, int off, int p
  * then pass them along with mbuf chain.
  */
 void
-divert_packet(struct mbuf *m, int incoming, int port, int rule)
+divert_packet(struct mbuf *m, int flags, int port, int rule)
 {
+	static struct socket *divnullso;
 	struct ip *ip;
 	struct inpcb *inp;
 	struct socket *sa;
@@ -169,7 +187,7 @@ divert_packet(struct mbuf *m, int incomi
 	 * But only for incoming packets.
 	 */
 	divsrc.sin_addr.s_addr = 0;
-	if (incoming) {
+	if (flags & IP_DIVERT_INCOMING) {
 		struct ifaddr *ifa;
 
 		/* Sanity check */
@@ -227,6 +245,22 @@ divert_packet(struct mbuf *m, int incomi
 			m_freem(m);
 		else
 			sorwakeup(sa);
+	} else if (flags & IP_DIVERT_DONTDROP) {
+		/* Pretend the packet was passed back unchanged. */
+		ipstat.ips_delivered--;
+		if (divnullso == NULL) {
+			/*
+			 * Allocate a dummy socket for ip_output() when
+			 * looping back diverted packets.
+			 */
+			if (socreate(PF_INET, &divnullso, SOCK_RAW,
+			    IPPROTO_DIVERT, &proc0) != 0) {
+				m_freem(m);
+				ipstat.ips_odropped++;
+				return;
+			}
+		}
+		div_output(divnullso, m, &divsrc, NULL);
 	} else {
 		m_freem(m);
 		ipstat.ips_noproto++;
@@ -245,8 +279,8 @@ static int
 div_output(struct socket *so, struct mbuf *m,
 	struct sockaddr_in *sin, struct mbuf *control)
 {
-	int error = 0;
 	struct m_hdr divert_tag;
+	int error = 0;
 
 	/*
 	 * Prepare the tag for divert info. Note that a packet
Index: sys/netinet/ip_fw.h
===================================================================
RCS file: /home/cvs/acs/base/src/sys/netinet/ip_fw.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -p -r1.4 -r1.5
--- ip_fw.h	15 Nov 2002 00:11:42 -0000	1.4
+++ ip_fw.h	23 Nov 2002 05:34:10 -0000	1.5
@@ -330,6 +330,7 @@ struct ipfw_dyn_rule {
  */
 #ifdef _KERNEL
 
+#define	IP_FW_PORT_MASK		0x0ffff
 #define	IP_FW_PORT_DYNT_FLAG	0x10000
 #define	IP_FW_PORT_TEE_FLAG	0x20000
 #define	IP_FW_PORT_DENY_FLAG	0x40000
Index: sys/netinet/ip_fw2.c
===================================================================
RCS file: /home/cvs/acs/base/src/sys/netinet/ip_fw2.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -p -r1.4 -r1.5
--- ip_fw2.c	19 Nov 2002 20:29:00 -0000	1.4
+++ ip_fw2.c	23 Nov 2002 05:34:10 -0000	1.5
@@ -462,6 +462,10 @@ ipfw_log(struct ip_fw *f, u_int hlen, st
 		case O_COUNT:
 			action = "Count";
 			break;
+		case O_DIVERT_NOP:
+			snprintf(SNPARGS(action2, 0), "Divert/Nop %d",
+				cmd->arg1);
+			break;
 		case O_DIVERT:
 			snprintf(SNPARGS(action2, 0), "Divert %d",
 				cmd->arg1);
@@ -1215,6 +1219,10 @@ lookup_next_rule(struct ip_fw *me)
  *
  *		- If IP_FW_PORT_DYNT_FLAG is set, interpret the lower
  *		  16 bits as a dummynet pipe number instead of diverting
+ *
+ *		- If IP_FW_PORT_NODROP_FLAG is set, don't drop the packet
+ *		  if there is no listener on the divert port; instead reinject
+ *		  the packet immediately.
  */
 
 static int
@@ -1862,14 +1870,17 @@ check_body:
 				retval = cmd->arg1 | IP_FW_PORT_DYNT_FLAG;
 				goto done;
 
+			case O_DIVERT_NOP:
 			case O_DIVERT:
 			case O_TEE:
 				if (args->eh) /* not on layer 2 */
 					break;
 				args->divert_rule = f->rulenum;
-				retval = (cmd->opcode == O_DIVERT) ?
-				    cmd->arg1 :
-				    cmd->arg1 | IP_FW_PORT_TEE_FLAG;
+				retval = cmd->arg1;
+				if (cmd->opcode == O_DIVERT_NOP)
+					retval |= IP_FW_PORT_NODROP_FLAG;
+				else if (cmd->opcode == O_TEE)
+					retval |= IP_FW_PORT_TEE_FLAG;
 				goto done;
 
 			case O_COUNT:
@@ -2431,6 +2442,7 @@ check_ipfw_struct(struct ip_fw *rule, in
 		case O_DENY:
 		case O_REJECT:
 		case O_SKIPTO:
+		case O_DIVERT_NOP:
 		case O_DIVERT:
 		case O_TEE:
 			if (cmdlen != F_INSN_SIZE(ipfw_insn))
Index: sys/netinet/ip_fw2.h
===================================================================
RCS file: /home/cvs/acs/base/src/sys/netinet/ip_fw2.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -p -r1.2 -r1.3
--- ip_fw2.h	15 Nov 2002 00:11:43 -0000	1.2
+++ ip_fw2.h	23 Nov 2002 05:34:10 -0000	1.3
@@ -110,6 +110,7 @@ enum ipfw_opcodes {		/* arguments (4 byt
 	O_SKIPTO,		/* arg1=next rule number	*/
 	O_PIPE,			/* arg1=pipe number		*/
 	O_QUEUE,		/* arg1=queue number		*/
+	O_DIVERT_NOP,		/* arg1=port number		*/
 	O_DIVERT,		/* arg1=port number		*/
 	O_TEE,			/* arg1=port number		*/
 	O_FORWARD_IP,		/* fwd sockaddr			*/
@@ -359,9 +360,11 @@ struct _ipfw_dyn_rule {
  */
 #ifdef _KERNEL
 
+#define IP_FW_PORT_MASK		0x0ffff
 #define	IP_FW_PORT_DYNT_FLAG	0x10000
 #define	IP_FW_PORT_TEE_FLAG	0x20000
 #define	IP_FW_PORT_DENY_FLAG	0x40000
+#define	IP_FW_PORT_NODROP_FLAG	0x80000
 
 /*
  * arguments for calling ipfw_chk() and dummynet_io(). We put them
Index: sys/netinet/ip_input.c
===================================================================
RCS file: /home/cvs/acs/base/src/sys/netinet/ip_input.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -p -r1.10 -r1.11
--- ip_input.c	19 Nov 2002 20:23:34 -0000	1.10
+++ ip_input.c	23 Nov 2002 05:42:03 -0000	1.11
@@ -463,7 +496,7 @@ iphack:
 			goto pass;
                 if (DUMMYNET_LOADED && (i & IP_FW_PORT_DYNT_FLAG) != 0) {
 			/* Send packet to the appropriate pipe */
-			ip_dn_io_ptr(m, i&0xffff, DN_TO_IP_IN, &args);
+			ip_dn_io_ptr(m, i & IP_FW_PORT_MASK, DN_TO_IP_IN,&args);
 			return;
 		}
 #ifdef IPDIVERT
@@ -772,6 +798,7 @@ found:
 	 */
 	if (divert_info != 0) {
 		struct mbuf *clone = NULL;
+		int flags = IP_DIVERT_INCOMING;
 
 		/* Clone packet if we're doing a 'tee' */
 		if ((divert_info & IP_FW_PORT_TEE_FLAG) != 0)
@@ -783,7 +810,10 @@ found:
 		ip->ip_off = htons(ip->ip_off);
 
 		/* Deliver packet to divert input routine */
-		divert_packet(m, 1, divert_info & 0xffff, args.divert_rule);
+		if ((divert_info & IP_FW_PORT_NODROP_FLAG) != 0)
+			flags |= IP_DIVERT_DONTDROP;
+		divert_packet(m, flags, divert_info & IP_FW_PORT_MASK,
+		    args.divert_rule);
 		ipstat.ips_delivered++;
 
 		/* If 'tee', continue with original packet */
Index: sys/netinet/ip_output.c
===================================================================
RCS file: /home/cvs/acs/base/src/sys/netinet/ip_output.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -p -r1.7 -r1.8
--- ip_output.c	19 Nov 2002 20:37:39 -0000	1.7
+++ ip_output.c	23 Nov 2002 05:34:11 -0000	1.8
@@ -625,13 +625,14 @@ skip_ipsec:
 			args.dst = dst;
 			args.flags = flags;
 
-			error = ip_dn_io_ptr(m, off & 0xffff, DN_TO_IP_OUT,
-				&args);
+			error = ip_dn_io_ptr(m, off & IP_FW_PORT_MASK,
+			    DN_TO_IP_OUT, &args);
 			goto done;
 		}
 #ifdef IPDIVERT
 		if (off != 0 && (off & IP_FW_PORT_DYNT_FLAG) == 0) {
 			struct mbuf *clone = NULL;
+			int flags = 0;
 
 			/* Clone packet if we're doing a 'tee' */
 			if ((off & IP_FW_PORT_TEE_FLAG) != 0)
@@ -651,8 +652,12 @@ skip_ipsec:
 			ip->ip_len = htons(ip->ip_len);
 			ip->ip_off = htons(ip->ip_off);
 
+			if ((off & IP_FW_PORT_NODROP_FLAG) != 0)
+				flags |= IP_DIVERT_DONTDROP;
+
 			/* Deliver packet to divert input routine */
-			divert_packet(m, 0, off & 0xffff, args.divert_rule);
+			divert_packet(m, flags, off & IP_FW_PORT_MASK,
+			    args.divert_rule);
 
 			/* If 'tee', continue with original packet */
 			if (clone != NULL) {
Index: sys/netinet/ip_var.h
===================================================================
RCS file: /home/cvs/acs/base/src/sys/netinet/ip_var.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -p -r1.3 -r1.4
--- ip_var.h	10 Oct 2002 20:42:01 -0000	1.3
+++ ip_var.h	23 Nov 2002 05:34:11 -0000	1.4
@@ -190,6 +190,10 @@ int	ip_rsvp_vif_done(struct socket *, st
 void	ip_rsvp_force_done(struct socket *);
 
 #ifdef IPDIVERT
+
+#define	IP_DIVERT_INCOMING	0x01
+#define	IP_DIVERT_DONTDROP	0x02
+
 void	div_init(void);
 void	div_input(struct mbuf *, int, int);
 void	divert_packet(struct mbuf *m, int incoming, int port, int rule);
Index: sbin/ipfw/ipfw2.c
===================================================================
RCS file: /home/cvs/acs/base/src/sbin/ipfw/ipfw2.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -p -r1.4 -r1.5
--- ipfw2.c	22 Nov 2002 00:27:10 -0000	1.4
+++ ipfw2.c	23 Nov 2002 05:38:42 -0000	1.5
@@ -188,6 +188,7 @@ enum tokens {
 	TOK_COUNT,
 	TOK_PIPE,
 	TOK_QUEUE,
+	TOK_DIVERT_NOP,
 	TOK_DIVERT,
 	TOK_TEE,
 	TOK_FORWARD,
@@ -277,6 +278,8 @@ struct _s_x rule_actions[] = {
 	{ "count",		TOK_COUNT },
 	{ "pipe",		TOK_PIPE },
 	{ "queue",		TOK_QUEUE },
+	{ "divert/nop",		TOK_DIVERT_NOP },
+	{ "divert/drop",	TOK_DIVERT },
 	{ "divert",		TOK_DIVERT },
 	{ "tee",		TOK_TEE },
 	{ "fwd",		TOK_FORWARD },
@@ -903,6 +906,10 @@ show_ipfw(struct ip_fw *rule)
 			printf("queue %u", cmd->arg1);
 			break;
 
+		case O_DIVERT_NOP:
+			printf("divert/nop %u", cmd->arg1);
+			break;
+
 		case O_DIVERT:
 			printf("divert %u", cmd->arg1);
 			break;
@@ -1680,7 +1687,7 @@ help(void)
 "BODY:		check-state [LOG] (no body) |\n"
 "		ACTION [LOG] MATCH_ADDR [OPTION_LIST]\n"
 "ACTION:	check-state | allow | count | deny | reject | skipto N |\n"
-"		{divert|tee} PORT | forward ADDR | pipe N | queue N\n"
+"		{divert|divert/nop|tee} PORT | forward ADDR | pipe N | queue N\n"
 "ADDR:		[ MAC dst src ether_type ] \n"
 "		[ from IPLIST [ PORT ] to IPLIST [ PORTLIST ] ]\n"
 "IPLIST:	IPADDR | ( IPADDR or ... or IPADDR )\n"
@@ -2578,9 +2585,12 @@ add(int ac, char *av[])
 		av++; ac--;
 		break;
 
+	case TOK_DIVERT_NOP:
 	case TOK_DIVERT:
 	case TOK_TEE:
-		action->opcode = (i == TOK_DIVERT) ? O_DIVERT : O_TEE;
+		action->opcode = (i == TOK_DIVERT) ? O_DIVERT :
+				 (i == TOK_DIVERT_NOP) ? O_DIVERT_NOP :
+				 O_TEE;
 		NEED1("missing divert/tee port");
 		action->arg1 = strtoul(*av, NULL, 0);
 		if (action->arg1 == 0) {
Index: sbin/ipfw/ipfw.8
===================================================================
RCS file: /home/cvs/acs/base/src/sbin/ipfw/ipfw.8,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -p -r1.2 -r1.3
--- ipfw.8	22 Nov 2002 00:27:10 -0000	1.2
+++ ipfw.8	23 Nov 2002 05:38:42 -0000	1.3
@@ -551,6 +551,11 @@ Divert packets that match this rule to t
 socket bound to port
 .Ar port .
 The search terminates.
+.It Cm divert/nop Ar port
+Similar to
+.Cm divert
+except that if no application is listening on the given port then the search
+continues, making the rule effectively a no-op.
 .It Cm fwd | forward Ar ipaddr Ns Op , Ns Ar port
 Change the next-hop on matching packets to
 .Ar ipaddr ,


More information about the freebsd-net mailing list