reassembled packets and pfil
Matthew Luckie
mjl at luckie.org.nz
Tue Apr 13 22:32:49 UTC 2010
> >I think that a patch like the one you propose is very useful (for
> >ipv4 as well) but it requires a sysctl or other mechanism to make
> >sure that when it is enabled we don't pass fragments through the
> >firewall.
>
> i've looked further into this and I now wonder if is a byproduct of my
> use of ipfw. the problem seems to be that offset will always be
> non-zero with any packet with a v6 fragment header, so a rule requiring
> offset to be zero is never run. i'll spend a bit more time on this
> tomorrow, and come back with a patch for ipfw.
Here's a patch to ipfw. We keep a copy of the MF bit for IPv6
fragments so it can be passed to ipfw_log. Otherwise, the offset
field no longer has the MF bit embedded in it as before.
Note that apart from the various transport-layer checks that require
offset to be zero, the O_FRAG opcode now has a different behaviour.
Only subsequent fragments will match this rule. If you want the exact
same behaviour as before, then
case O_FRAG:
match = (offset != 0);
break;
should become
case O_FRAG:
match = (offset != 0 || ext_hd & EXT_FRAGMENT);
break;
If you are generally happy with this patch, let me know and I'll file
a PR so it doesn't get lost.
--- ip_fw2.c.orig 2008-11-25 15:59:29.000000000 +1300
+++ ip_fw2.c 2010-04-14 10:05:46.000000000 +1200
@@ -758,6 +758,7 @@ ipfw_log(struct ip_fw *f, u_int hlen, st
char *action;
int limit_reached = 0;
char action2[40], proto[128], fragment[32];
+ u_short mf = 0;
fragment[0] = '\0';
proto[0] = '\0';
@@ -903,6 +904,8 @@ ipfw_log(struct ip_fw *f, u_int hlen, st
snprintf(dst, sizeof(dst), "[%s]",
ip6_sprintf(ip6buf, &args->f_id.dst_ip6));
+ mf = offset & IP6F_MORE_FRAG;
+ offset &= IP6F_OFF_MASK;
ip6 = (struct ip6_hdr *)ip;
tcp = (struct tcphdr *)(((char *)ip) + hlen);
udp = (struct udphdr *)(((char *)ip) + hlen);
@@ -972,13 +975,13 @@ ipfw_log(struct ip_fw *f, u_int hlen, st
#ifdef INET6
if (IS_IP6_FLOW_ID(&(args->f_id))) {
- if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG))
+ if (offset || mf)
snprintf(SNPARGS(fragment, 0),
" (frag %08x:%d@%d%s)",
args->f_id.frag_id6,
ntohs(ip6->ip6_plen) - hlen,
- ntohs(offset & IP6F_OFF_MASK) << 3,
- (offset & IP6F_MORE_FRAG) ? "+" : "");
+ ntohs(offset) << 3,
+ mf ? "+" : "");
} else
#endif
{
@@ -2151,16 +2154,13 @@ ipfw_chk(struct ip_fw_args *args)
/*
* offset The offset of a fragment. offset != 0 means that
- * we have a fragment at this offset of an IPv4 packet.
- * offset == 0 means that (if this is an IPv4 packet)
- * this is the first or only fragment.
- * For IPv6 offset == 0 means there is no Fragment Header.
- * If offset != 0 for IPv6 always use correct mask to
- * get the correct offset because we add IP6F_MORE_FRAG
- * to be able to dectect the first fragment which would
- * otherwise have offset = 0.
+ * we have a fragment at this offset.
+ * offset == 0 means that this is the first or only fragment.
+ *
+ * mf The MF bit masked out of IPv6 packets.
*/
u_short offset = 0;
+ u_short mf = 0;
/*
* Local copies of addresses. They are only valid if we have
@@ -2311,17 +2311,8 @@ do { \
proto = ((struct ip6_frag *)ulp)->ip6f_nxt;
offset = ((struct ip6_frag *)ulp)->ip6f_offlg &
IP6F_OFF_MASK;
- /* Add IP6F_MORE_FRAG for offset of first
- * fragment to be != 0. */
- offset |= ((struct ip6_frag *)ulp)->ip6f_offlg &
+ mf = ((struct ip6_frag *)ulp)->ip6f_offlg &
IP6F_MORE_FRAG;
- if (offset == 0) {
- printf("IPFW2: IPV6 - Invalid Fragment "
- "Header\n");
- if (fw_deny_unknown_exthdrs)
- return (IP_FW_DENY);
- break;
- }
args->f_id.frag_id6 =
ntohl(((struct ip6_frag *)ulp)->ip6f_ident);
ulp = NULL;
@@ -2904,7 +2895,7 @@ check_body:
case O_LOG:
if (fw_verbose)
ipfw_log(f, hlen, args, m,
- oif, offset, tablearg, ip);
+ oif, offset|mf, tablearg, ip);
match = 1;
break;
More information about the freebsd-net
mailing list