From nobody Tue Jan 30 13:55:13 2024 X-Original-To: bugs@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4TPRWQ28JBz58t6L for ; Tue, 30 Jan 2024 13:55:14 +0000 (UTC) (envelope-from bugzilla-noreply@freebsd.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4TPRWP6LT7z4fR0 for ; Tue, 30 Jan 2024 13:55:13 +0000 (UTC) (envelope-from bugzilla-noreply@freebsd.org) ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1706622913; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=XkSXesh+kkkSsNwJ1JmMafViqxlflPh3tTaSsO6PMOc=; b=ee66IfY68O4b13WqTmILcIE3Mydmv12oSLeqW9puJM7vZrTOR6LARQrqRffcoXVQUbQwBi sM70MzWG7v36HNRA8KhUuxlfuHK4goKz+FLOEbqt4H8Lcmv7HXO5MP8MtDIdOmpkOVnNLt G5GlFsavwdixdVvPADJn0jQYDJ/5+yL2L+e9B7XBMRw2Sc/LsTeimRPpjLcSMjbvo74F4m 2khAkSwX+KX+un7Gfb42Tn8gXq5Ejwh0UrFPaBD/iZi9Mwi6BEjS75ZpfIX3C02hzwLXl0 9jtcL995lMeL/u/unJwvthPl6YNlLZPiTHxGU5MzVyw4yNGuB0zUb8VbdrEcaQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1706622913; a=rsa-sha256; cv=none; b=frHdBFyPqb/hJ7xR7c1nhsuh86gqTIVJy2Z6+JhUcM2bKCt0s9749kJwJ2gCsTrQGg9N2x duJNhaai5r2IG3aSNcmocAmTIt5Oc1xszqLrZORCwby1vbzZTYvHK92hK9MK51KGRgIGrM euN5O/7LXTnDmGC5ih0aWtrWl040DVG4awZkWvL10Kla20B4ZC2025c9Jl1a0lwkVMoXzK ipxS9TEB5aZf0/dlUTh1SmPxgxA6k06c43l2kdJpugWecpCWJ/BVZn6roNEUKHjeXVnI5W b0wL1m35nUz7TZJeyTrKLKbiopJWT+TVkWHt7030Xsd+btlJp+lpzy231svJxA== Received: from kenobi.freebsd.org (kenobi.freebsd.org [IPv6:2610:1c1:1:606c::50:1d]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4TPRWP5PnGzdlf for ; Tue, 30 Jan 2024 13:55:13 +0000 (UTC) (envelope-from bugzilla-noreply@freebsd.org) Received: from kenobi.freebsd.org ([127.0.1.5]) by kenobi.freebsd.org (8.15.2/8.15.2) with ESMTP id 40UDtDNC001600 for ; Tue, 30 Jan 2024 13:55:13 GMT (envelope-from bugzilla-noreply@freebsd.org) Received: (from www@localhost) by kenobi.freebsd.org (8.15.2/8.15.2/Submit) id 40UDtDHM001593 for bugs@FreeBSD.org; Tue, 30 Jan 2024 13:55:13 GMT (envelope-from bugzilla-noreply@freebsd.org) X-Authentication-Warning: kenobi.freebsd.org: www set sender to bugzilla-noreply@freebsd.org using -f From: bugzilla-noreply@freebsd.org To: bugs@FreeBSD.org Subject: [Bug 276732] IPFW keep-state rules with untag do not go through parent rule cmd Date: Tue, 30 Jan 2024 13:55:13 +0000 X-Bugzilla-Reason: AssignedTo X-Bugzilla-Type: new X-Bugzilla-Watch-Reason: None X-Bugzilla-Product: Base System X-Bugzilla-Component: kern X-Bugzilla-Version: CURRENT X-Bugzilla-Keywords: X-Bugzilla-Severity: Affects Some People X-Bugzilla-Who: fodillemlinkarim@gmail.com X-Bugzilla-Status: New X-Bugzilla-Resolution: X-Bugzilla-Priority: --- X-Bugzilla-Assigned-To: bugs@FreeBSD.org X-Bugzilla-Flags: X-Bugzilla-Changed-Fields: bug_id short_desc product version rep_platform op_sys bug_status bug_severity priority component assigned_to reporter Message-ID: Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Bugzilla-URL: https://bugs.freebsd.org/bugzilla/ Auto-Submitted: auto-generated List-Id: Bug reports List-Archive: https://lists.freebsd.org/archives/freebsd-bugs List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-freebsd-bugs@freebsd.org MIME-Version: 1.0 https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D276732 Bug ID: 276732 Summary: IPFW keep-state rules with untag do not go through parent rule cmd Product: Base System Version: CURRENT Hardware: Any OS: Any Status: New Severity: Affects Some People Priority: --- Component: kern Assignee: bugs@FreeBSD.org Reporter: fodillemlinkarim@gmail.com A FreeBSD user reported this one in the mailing list and I have a fix for t= he issue. I am convinced this bug is present in the oldest versions of FBSD. TLDR, here is the fix for it (diff is against CURRENT): diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index d2b01fd..57c02dc 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -2887,7 +2887,8 @@ do {=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20 \ l =3D f->cmd_len - f->act_ofs; cmdlen =3D 0; match =3D 1; - break; + continue; + break; /* not reached */ } /* * Dynamic entry not found. If CHECK_STATE, On 2023-11-08 2:26 a.m., Mikhail Holt wrote: > Hello List, > > On a recent Stable 13 test host I, by accident, found that: > > /sbin/ipfw -q add 0031 allow tcp from 192.168.64.0/24 to me = dst-port ssh in via igb3 setup keep-state WORKS > > /sbin/ipfw -q add 0031 allow log tcp from 192.168.64.0/24 to me = dst-port ssh in via igb3 setup keep-state WORKS > > /sbin/ipfw -q add 0031 allow log tag 10 tcp from 192.168.64.0/24 to me = dst-port ssh in via igb3 setup keep-state WORKS > > /sbin/ipfw -q add 0031 allow log untag 10 tcp from 192.168.64.0/24 to me = dst-port ssh in via igb3 setup keep-state WORKS > > /sbin/ipfw -q add 0031 allow untag 10 tcp from 192.168.64.0/24 to me = dst-port ssh in via igb3 setup keep-state DOES NOT WORK? > - A dynamic rule is created as per the rules that work. > - Packets are logged by a deny all rule which of course is never reached = by the rules that work. This is a very old bug in FreeBSD/ipfw O_PROBE_STATE and the good news is I have a fix for it. The fix is very easy and I will try to explain here what= the bug is and what the fix is. The issue is only related to rules that have the F_NOT bit set and that are the beginning of the actions list of a keep-state rule. I'm also CCing some other folks that have authority in moving this forward so it hopefully gets into main eventually. If you are like me, this is a perfect read for a Friday afternoon ;) So here is how it goes: When you create a keep-state rule, the userland part of ipfw will insert a O_PROBE_STATE action at the very beginning of the rule so when a packet hits the rule the kernel will PROBE the list of installed dynamic rules for a ma= tch before installing such a rule (we don't want to create more than one dynamic rule per parent rule). The code responsible for this looks like this in ipfw2.c: /* * generate O_PROBE_STATE if necessary */ if (have_state && have_state->opcode !=3D O_CHECK_STATE && !have_rs= tate) { fill_cmd(dst, O_PROBE_STATE, 0, have_state->arg1); dst =3D next_cmd(dst, &rblen); }=20=20 Now ipfw userland also does something else when building the list of actions for a rule, it records the actions offset in the rule and put those actions together in a predetermined way, here is the section of ipfw2.c that does something like this: /* * start action section */ rule->act_ofs =3D dst - rule->cmd; /* put back O_LOG, O_ALTQ, O_TAG if necessary */ log --> if (have_log) { i =3D F_LEN(have_log); CHECK_RBUFLEN(i); bcopy(have_log, dst, i * sizeof(uint32_t)); dst +=3D i; } if (have_altq) { i =3D F_LEN(have_altq); CHECK_RBUFLEN(i); bcopy(have_altq, dst, i * sizeof(uint32_t)); dst +=3D i; } tag --> if (have_tag) { i =3D F_LEN(have_tag); CHECK_RBUFLEN(i); bcopy(have_tag, dst, i * sizeof(uint32_t)); dst +=3D i;=20=20=20 } Nothing wrong with all this and if you are still with me (kudos to you) ple= ase take a moment to notice the 'log' action is _before_ the 'tag' action. This will be important later to understand why some rules in your example works = and why some don't, although you may already have a clue... Now let's transport ourselves into the kernel code, precisely in sys/netpfil/ipfw/ip_fw2.c around line 2865. Here we are inside the O_PROBE_STATE case and notice how after we find a dynamic entry how the ker= nel will 'reset' the cmd pointer to the action part of the parent rule by doing something like this: /* * Found dynamic entry, jump to the * 'action' part of the parent rule * by setting f, cmd, l and clearing * cmdlen. */ f =3D q; f_pos =3D dyn_info.f_pos; cmd pointer is reset --> cmd =3D ACTION_PTR(f); l =3D f->cmd_len - f->act_ofs; cmdlen =3D 0; match =3D 1; break; } At this precise moment (pointed by the -->), if we refer to your example be= low, we would be hitting the dynamic rule entry and have cmd pointer reset to the ACTION part of the 'untag 10' action you have entered. We then set a few th= ings and break from the switch statement. Now this gets us at the end of the swi= tch statement where FreeBSD does something like this (around line 3337): /* * if we get here with l=3D0, then match is irrelev= ant. */ cmd is 'untag' --> if (cmd->len & F_NOT) match =3D !match; if (match) { if (cmd->len & F_OR) skip_or =3D 1; } else { if (!(cmd->len & F_OR)) /* not an OR block,= */ exits too early --> break; /* try next rule = */ } } /* end of inner loop, scan opcodes */ If you look at the check for F_NOT bit (which is cleverly folded in the len part of the cmd) you realize that check is actually made against the 'untag' action and since untag is actually NOT tag it will match and change match i= nto !match. The code will not try to go through the actual action and it will e= xit the loop too early. Essentially, by resetting the cmd action pointer we should give a chance for O_TAG (F_NOT) to be called and not simply turn match into a no match. The f= ix for this is very simple and goes like this: diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index d2b01fd..57c02dc 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -2887,7 +2887,8 @@ do {=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20 \ l =3D f->cmd_len - f->act_ofs; cmdlen =3D 0; match =3D 1; - break; + continue; + break; /* not reached */ } /* * Dynamic entry not found. If CHECK_STATE, Now why did it work for you when you used log? Well I'm sure you remember t= hat, because O_LOG is inserted _before_ O_TAG and that O_LOG doesn't have the F_= NOT bit set then match is still 1 at the end of the loop and the actual cmd (LO= G in this case) will have a chance to execute. Finally, if your still reading, I think this bug has been in ipfw for a very long time, great catch buddy! Best, Karim. --=20 You are receiving this mail because: You are the assignee for the bug.=