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>