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