Page MenuHomeFreeBSD

D48453.diff
No OneTemporary

D48453.diff

diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -173,7 +173,7 @@
PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES,
PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK,
PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY,
- PF_STATE_OPT_PFLOW };
+ PF_STATE_OPT_PFLOW, PF_STATE_OPT_ALLOW_RELATED };
enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE };
@@ -526,7 +526,7 @@
%token DNPIPE DNQUEUE RIDENTIFIER
%token LOAD RULESET_OPTIMIZATION PRIO
%token STICKYADDRESS ENDPI MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
-%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW
+%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW ALLOW_RELATED
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS
%token DIVERTTO DIVERTREPLY BRIDGE_TO RECEIVEDON NE LE GE AFTO
%token <v.string> STRING
@@ -2651,6 +2651,14 @@
}
r.rule_flag |= PFRULE_PFLOW;
break;
+ case PF_STATE_OPT_ALLOW_RELATED:
+ if (r.rule_flag & PFRULE_ALLOW_RELATED) {
+ yyerror("state allow-related option: "
+ "multiple definitions");
+ YYERROR;
+ }
+ r.rule_flag |= PFRULE_ALLOW_RELATED;
+ break;
case PF_STATE_OPT_TIMEOUT:
if (o->data.timeout.number ==
PFTM_ADAPTIVE_START ||
@@ -4494,6 +4502,14 @@
$$->next = NULL;
$$->tail = $$;
}
+ | ALLOW_RELATED {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_ALLOW_RELATED;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
| STRING NUMBER {
int i;
@@ -6429,6 +6445,7 @@
{ "af-to", AFTO},
{ "all", ALL},
{ "allow-opts", ALLOWOPTS},
+ { "allow-related", ALLOW_RELATED},
{ "altq", ALTQ},
{ "anchor", ANCHOR},
{ "antispoof", ANTISPOOF},
diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5
--- a/share/man/man5/pf.conf.5
+++ b/share/man/man5/pf.conf.5
@@ -27,7 +27,7 @@
.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd December 6, 2024
+.Dd January 10, 2025
.Dt PF.CONF 5
.Os
.Sh NAME
@@ -2508,6 +2508,10 @@
States created by this rule are exported on the
.Xr pflow 4
interface.
+.It Ar allow-related
+Automatically allow connections related to this one, regardless of rules that
+might otherwise affect them.
+This currently only applies to SCTP multihomed connection.
.El
.Pp
Multiple options can be specified, separated by commas:
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1658,6 +1658,7 @@
#define PFDESC_SCTP_ADD_IP 0x1000
u_int16_t sctp_flags;
u_int32_t sctp_initiate_tag;
+ struct pf_krule *related_rule;
struct pf_sctp_multihome_jobs sctp_multihome_jobs;
};
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -617,6 +617,7 @@
#define PFRULE_IFBOUND 0x00010000 /* if-bound */
#define PFRULE_STATESLOPPY 0x00020000 /* sloppy state tracking */
#define PFRULE_PFLOW 0x00040000
+#define PFRULE_ALLOW_RELATED 0x00080000
#ifdef _KERNEL
#define PFRULE_REFS 0x0080 /* rule has references */
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -462,6 +462,14 @@
if (st->rule->rt == PF_REPLYTO || (pd->af != pd->naf))
return (V_pfi_all);
+ /*
+ * If this state is created based on another state (e.g. SCTP
+ * multihome) always set it floating initially. We can't know for sure
+ * what interface the actual traffic for this state will come in on.
+ */
+ if (pd->related_rule)
+ return (V_pfi_all);
+
/* Don't overrule the interface for states created on incoming packets. */
if (st->direction == PF_IN)
return (k);
@@ -5700,6 +5708,10 @@
}
while (r != NULL) {
+ if (pd->related_rule) {
+ *rm = pd->related_rule;
+ break;
+ }
pf_counter_u64_add(&r->evaluations, 1);
PF_TEST_ATTRIB(pfi_kkif_match(r->kif, pd->kif) == r->ifnot,
r->skip[PF_SKIP_IFP]);
@@ -7180,6 +7192,15 @@
dst->scrub->pfss_v_tag = pd->sctp_initiate_tag;
}
+ /*
+ * Bind to the correct interface if we're if-bound. For multihomed
+ * extra associations we don't know which interface that will be until
+ * here, so we've inserted the state on V_pf_all. Fix that now.
+ */
+ if ((*state)->kif == V_pfi_all &&
+ (*state)->rule->rule_flag & PFRULE_IFBOUND)
+ (*state)->kif = pd->kif;
+
if (pd->sctp_flags & (PFDESC_SCTP_COOKIE | PFDESC_SCTP_HEARTBEAT_ACK)) {
if (src->state < SCTP_ESTABLISHED) {
pf_set_protostate(*state, psrc, SCTP_ESTABLISHED);
@@ -7396,6 +7417,9 @@
j->pd.sctp_flags |= PFDESC_SCTP_ADD_IP;
PF_RULES_RLOCK();
sm = NULL;
+ if (s->rule->rule_flag & PFRULE_ALLOW_RELATED) {
+ j->pd.related_rule = s->rule;
+ }
ret = pf_test_rule(&r, &sm,
&j->pd, &ra, &rs, NULL);
PF_RULES_RUNLOCK();
diff --git a/tests/sys/netpfil/pf/sctp.py b/tests/sys/netpfil/pf/sctp.py
--- a/tests/sys/netpfil/pf/sctp.py
+++ b/tests/sys/netpfil/pf/sctp.py
@@ -426,6 +426,82 @@
assert re.search(r"all sctp 192.0.2.4:.*192.0.2.3:1234", states)
assert re.search(r"all sctp 192.0.2.4:.*192.0.2.2:1234", states)
+ @pytest.mark.require_user("root")
+ def test_disallow_related(self):
+ srv_vnet = self.vnet_map["vnet2"]
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "block proto sctp",
+ "pass inet proto sctp to 192.0.2.3",
+ "pass on lo"])
+
+ # Sanity check, we can communicate with the primary address.
+ client = SCTPClient("192.0.2.3", 1234)
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ # This shouldn't work
+ success=False
+ try:
+ client.newpeer("192.0.2.2")
+ client.send(b"world", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "world"
+ success=True
+ except:
+ success=False
+ assert not success
+
+ # Check that we have a state for 192.0.2.3, but not 192.0.2.2 to 192.0.2.1
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ assert re.search(r"all sctp 192.0.2.1:.*192.0.2.3:1234", states)
+ assert not re.search(r"all sctp 192.0.2.1:.*192.0.2.2:1234", states)
+
+ @pytest.mark.require_user("root")
+ def test_allow_related(self):
+ srv_vnet = self.vnet_map["vnet2"]
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "set state-policy if-bound",
+ "block proto sctp",
+ "pass inet proto sctp to 192.0.2.3 keep state (allow-related)",
+ "pass on lo"])
+
+ # Sanity check, we can communicate with the primary address.
+ client = SCTPClient("192.0.2.3", 1234)
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ success=False
+ try:
+ client.newpeer("192.0.2.2")
+ client.send(b"world", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "world"
+ success=True
+ finally:
+ # Debug output
+ ToolsHelper.print_output("/sbin/pfctl -ss")
+ ToolsHelper.print_output("/sbin/pfctl -sr -vv")
+ assert success
+
+ # Check that we have a state for 192.0.2.3 and 192.0.2.2 to 192.0.2.1
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ assert re.search(r"epair.*sctp 192.0.2.1:.*192.0.2.3:1234", states)
+ assert re.search(r"epair.*sctp 192.0.2.1:.*192.0.2.2:1234", states)
+
class TestSCTPv6(VnetTestTemplate):
REQUIRED_MODULES = ["sctp", "pf"]
TOPOLOGY = {

File Metadata

Mime Type
text/plain
Expires
Wed, Jan 15, 1:22 PM (7 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15811013
Default Alt Text
D48453.diff (7 KB)

Event Timeline