Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 12 Mar 2003 00:06:22 -0800
From:      "Crist J. Clark" <crist.clark@attbi.com>
To:        ipfw@freebsd.org
Subject:   Anti-Spoofing Option
Message-ID:  <20030312080622.GA42446@blossom.cjclark.org>

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

--yrj/dFKFPuw6o+aM
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

I've created a new option for ipfw(8) (IPFW2-only to be exact) that
basically does automatic anti-spoofing. I've called the knob,
"verrevpath," in honor of the Cisco command,

  ip verify unicast reverse-path

When the option is specified in a rule, a packet tested against the
rule matches iff,

  a) The packet is _not_ entering the system, or

  b) The packet is coming into the interface that traffic sent to the
     packet's source address would go out of.

For example, take a firewall with three interfaces,

    Internet}---if0[Firewall]if1---{192.168.0.0/24
                       --
                       if2---{172.16.0.0/16

Any packets arriving on if0 with a source of 192.168.0.0/24 or
172.16.0.0/16 will not match a rule with 'verrevpath.' Likewise,
anything coming in on if1 that doesn't have a source of 192.168.0.0/24
will not match, nor will anything on if2 without a 172.16.0.0/16
source.

To turn on anti-spoofing on a firewall, put,

  # ipfw add 100 pass ip from any to any verrevpath

Before any other rules. All done (well, only if you're not using
dynamic rules).

The check is done by simply getting the route for the source of the
packet and making sure the interface the route goes out on is the same
as the one the packet arrived on.

Of course, the really interesting appeal of this may not necessarily
for "firewalls," but for routers running dynamic routing protocols
(which is why I was thinking sysctl(8) at first).

Patch for CURRENT is attached. It should apply to STABLE (make sure to
patch ip_fw2.h rather than ip_fw.h), but I've had a little trouble
getting IPFW2 running right on my STABLE crash box so I have not
tested it.

Now, some discussion. I originally was just going to implement a
sysctl(8) knob that did the check in ip_input() for every packet. But
after starting on that, it occured to me that it might be better as a
firewall action. I started doing that when I realized that it doesn't
really work well as a firewall action. I went back to a sysctl(8)
until I decided that it would work fine as a firewall option. I'm not
100% on any of those choices yet.

One of the problems I had with making it an action (and one of the
initial reasons I leaned away from a sysctl(8) knob) is how to handle
logging. And I'm still not happy with the logging issue. It would be
nice to somehow include in the logs that the packet was dropped
because there was a RPF problem and log the incoming interface and
where we expected such a packet to come from. Right now, there is no
logging angle. Making it an action could give you more ways to go
there, but how do you tell it to log only failures? To log failure and
success? To log neither? (Actually, that's pretty easy to do, but the
rules would get ugly lookin'.)

Anyone have any ideas on how to improve on this before I commit it
(after which making major changes is less desirable)? Ideas how to do
the logging better? Keep in mind that I don't want to have to put
terrible, ugly hacks in luigi's purty IPFW2 code to implement any
suggestions. 

Oh, and of course, please test it. Also, some thoughts about
configurations where this option can break things (like when you are
purposely doing asymmetric routing) and any creative uses.
-- 
Crist J. Clark                     |     cjclark@alum.mit.edu
                                   |     cjclark@jhu.edu
http://people.freebsd.org/~cjc/    |     cjc@freebsd.org

--yrj/dFKFPuw6o+aM
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="ipfw_verrevpath_5.patch"

Index: src/sys/netinet/ip_fw2.c
===================================================================
RCS file: /export/freebsd/ncvs/src/sys/netinet/ip_fw2.c,v
retrieving revision 1.27
diff -u -r1.27 ip_fw2.c
--- src/sys/netinet/ip_fw2.c	19 Feb 2003 05:47:34 -0000	1.27
+++ src/sys/netinet/ip_fw2.c	12 Mar 2003 07:16:11 -0000
@@ -402,6 +402,43 @@
 	return(0);	/* no match, fail ... */
 }
 
+/*
+ * The 'verrevpath' option checks that the interface that an IP packet
+ * arrives on is the same interface that traffic destined for the 
+ * packet's source address would be routed out of. This is a measure
+ * to block forged packets. This is also commonly known as "anti-spoofing"
+ * or Unicast Reverse Path Forwarding (Unicast RFP) in Cisco-ese. The
+ * name of the knob is purposely reminisent of the Cisco IOS command,
+ *
+ *   ip verify unicast reverse-path
+ *
+ * which implements the same functionality. But note that syntax is
+ * misleading. The check may be performed on all IP packets whether unicast,
+ * multicast, or broadcast.
+ */
+static int
+verify_rev_path(struct in_addr src, struct ifnet *ifp)
+{
+	struct route ro;
+	struct sockaddr_in *dst;
+
+	dst = (struct sockaddr_in *)&(ro.ro_dst);
+	ro.ro_rt = NULL;
+
+	bzero(dst, sizeof(*dst));
+	dst->sin_family = AF_INET;
+	dst->sin_len = sizeof(*dst);
+	dst->sin_addr = src;
+
+	rtalloc_ign(&ro, RTF_CLONING|RTF_PRCLONING);
+	if ((ro.ro_rt == NULL) || (ifp == NULL) ||
+	    (ro.ro_rt->rt_ifp->if_index != ifp->if_index))
+		return 0;
+	
+    	return 1;
+}
+
+
 static u_int64_t norule_counter;	/* counter for ipfw_log(NULL...) */
 
 #define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0
@@ -1755,6 +1792,13 @@
 				match = (random()<((ipfw_insn_u32 *)cmd)->d[0]);
 				break;
 
+			case O_VERREVPATH:
+				/* Outgoing packets automatically pass/match */
+				match = ((oif != NULL) ||
+				    (m->m_pkthdr.rcvif == NULL) ||	 
+				    verify_rev_path(src_ip, m->m_pkthdr.rcvif));
+				break;
+
 			/*
 			 * The second set of opcodes represents 'actions',
 			 * i.e. the terminal part of a rule once the packet
@@ -2322,6 +2366,7 @@
 		case O_TCPFLAGS:
 		case O_TCPOPTS:
 		case O_ESTAB:
+		case O_VERREVPATH:
 			if (cmdlen != F_INSN_SIZE(ipfw_insn))
 				goto bad_size;
 			break;
Index: src/sys/netinet/ip_fw.h
===================================================================
RCS file: /export/freebsd/ncvs/src/sys/netinet/ip_fw.h,v
retrieving revision 1.75
diff -u -r1.75 ip_fw.h
--- src/sys/netinet/ip_fw.h	24 Oct 2002 22:32:13 -0000	1.75
+++ src/sys/netinet/ip_fw.h	11 Mar 2003 19:10:10 -0000
@@ -89,6 +89,8 @@
 	O_ICMPTYPE,		/* u32 = icmp bitmap		*/
 	O_TCPOPTS,		/* arg1 = 2*u8 bitmap		*/
 
+	O_VERREVPATH,		/* none				*/
+
 	O_PROBE_STATE,		/* none				*/
 	O_KEEP_STATE,		/* none				*/
 	O_LIMIT,		/* ipfw_insn_limit		*/
Index: src/sbin/ipfw/ipfw2.c
===================================================================
RCS file: /export/freebsd/ncvs/src/sbin/ipfw/ipfw2.c,v
retrieving revision 1.21
diff -u -r1.21 ipfw2.c
--- src/sbin/ipfw/ipfw2.c	12 Jan 2003 03:31:10 -0000	1.21
+++ src/sbin/ipfw/ipfw2.c	11 Mar 2003 19:09:38 -0000
@@ -224,6 +224,7 @@
 	TOK_ICMPTYPES,
 	TOK_MAC,
 	TOK_MACTYPE,
+	TOK_VERREVPATH,
 
 	TOK_PLR,
 	TOK_NOERROR,
@@ -333,6 +334,7 @@
 	{ "MAC",		TOK_MAC },
 	{ "mac",		TOK_MAC },
 	{ "mac-type",		TOK_MACTYPE },
+	{ "verrevpath",		TOK_VERREVPATH },
 
 	{ "not",		TOK_NOT },		/* pseudo option */
 	{ "!", /* escape ? */	TOK_NOT },		/* pseudo option */
@@ -1161,6 +1163,10 @@
 			    }
 				break;
 
+			case O_VERREVPATH:
+				printf(" verrevpath");
+				break;
+			  
 			case O_KEEP_STATE:
 				printf(" keep-state");
 				break;
@@ -3132,6 +3138,10 @@
 			ac--; av++;
 			break;
 
+		case TOK_VERREVPATH:
+			fill_cmd(cmd, O_VERREVPATH, 0, 0);
+			break;
+		  
 		default:
 			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
 		}

--yrj/dFKFPuw6o+aM--

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




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