diff options
author | ae <ae@FreeBSD.org> | 2017-03-30 14:20:27 +0000 |
---|---|---|
committer | ae <ae@FreeBSD.org> | 2017-03-30 14:20:27 +0000 |
commit | ab3e7e42d2f4196cc40513ff5d86b05bb15355e8 (patch) | |
tree | 03eb3fea2c3ef89be3a3a50f7ec866539c76d3e5 /sbin | |
parent | 1c6bb19dc8b20fa7afe678a22492891ed8975853 (diff) | |
download | FreeBSD-src-ab3e7e42d2f4196cc40513ff5d86b05bb15355e8.zip FreeBSD-src-ab3e7e42d2f4196cc40513ff5d86b05bb15355e8.tar.gz |
MFC r303018:
Add named dynamic states support to ipfw(4).
The keep-state, limit and check-state now will have additional argument
flowname. This flowname will be assigned to dynamic rule by keep-state
or limit opcode. And then can be matched by check-state opcode or
O_PROBE_STATE internal opcode. To reduce possible breakage and to maximize
compatibility with old rulesets default flowname introduced.
It will be assigned to the rules when user has omitted state name in
keep-state and check-state opcodes. Also if name is ambiguous (can be
evaluated as rule opcode) it will be replaced to default.
Reviewed by: julian
Obtained from: Yandex LLC
Relnotes: yes
Sponsored by: Yandex LLC
Differential Revision: https://reviews.freebsd.org/D6674
MFC r304087:
Do not warn about ambiguous state name when we inspect a comment token.
MFC r304089:
Add an ability to attach comment to check-state rules.
MFC r310727 (by marius):
Fix a bug in r272840; given that the optlen parameter of setsockopt(2)
is a 32-bit socklen_t, do_get3() passes the kernel to access the wrong
32-bit half on big-endian LP64 machines when simply casting the 64-bit
size_t optlen to a socklen_t pointer.
While at it and given that the intention of do_get3() apparently is to
hide/wrap the fact that socket options are used for communication with
ipfw(4), change the optlen parameter of do_set3() to be of type size_t
and as such more appropriate than uintptr_t, too.
MFC r315305:
Change the syntax of ipfw's named states.
Since the state name is an optional argument, it often can conflict
with other options. To avoid ambiguity now the state name must be
prefixed with a colon.
Sponsored by: Yandex LLC
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/ipfw/ipfw.8 | 46 | ||||
-rw-r--r-- | sbin/ipfw/ipfw2.c | 104 | ||||
-rw-r--r-- | sbin/ipfw/ipfw2.h | 2 |
3 files changed, 127 insertions, 25 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 0d26dca..84bad58 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1,7 +1,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 26, 2016 +.Dd March 15, 2017 .Dt IPFW 8 .Os .Sh NAME @@ -737,7 +737,7 @@ will be executed when the packet matches the body of the rule. .It Cm allow | accept | pass | permit Allow packets that match rule. The search terminates. -.It Cm check-state +.It Cm check-state Op Ar :flowname | Cm :any Checks the packet against the dynamic ruleset. If a match is found, execute the action associated with the rule which generated this dynamic rule, otherwise @@ -752,6 +752,17 @@ rule is found, the dynamic ruleset is checked at the first or .Cm limit rule. +The +.Ar :flowname +is symbolic name assigned to dynamic rule by +.Cm keep-state +opcode. +The special flowname +.Cm :any +can be used to ignore states flowname when matching. +The +.Cm :default +keyword is special name used for compatibility with old rulesets. .It Cm count Update counters for all packets that match rule. The search continues with the next rule. @@ -1574,7 +1585,7 @@ specified in the same way as .It Cm ipversion Ar ver Matches IP packets whose IP version field is .Ar ver . -.It Cm keep-state +.It Cm keep-state Op Ar :flowname Upon a match, the firewall will create a dynamic rule, whose default behaviour is to match bidirectional traffic between source and destination IP/port using the same protocol. @@ -1582,11 +1593,20 @@ The rule has a limited lifetime (controlled by a set of .Xr sysctl 8 variables), and the lifetime is refreshed every time a matching packet is found. +The +.Ar :flowname +is used to assign additional to addresses, ports and protocol parameter +to dynamic rule. It can be used for more accurate matching by +.Cm check-state +rule. +The +.Cm :default +keyword is special name used for compatibility with old rulesets. .It Cm layer2 Matches only layer2 packets, i.e., those passed to .Nm from ether_demux() and ether_output_frame(). -.It Cm limit Bro Cm src-addr | src-port | dst-addr | dst-port Brc Ar N +.It Cm limit Bro Cm src-addr | src-port | dst-addr | dst-port Brc Ar N Op Ar :flowname The firewall will only allow .Ar N connections with the same @@ -1594,8 +1614,6 @@ set of parameters as specified in the rule. One or more of source and destination addresses and ports can be specified. -Currently, -only IPv4 flows are supported. .It Cm lookup Bro Cm dst-ip | dst-port | src-ip | src-port | uid | jail Brc Ar name Search an entry in lookup table .Ar name @@ -2188,6 +2206,12 @@ and .Em dst are used here only to denote the initial match addresses, but they are completely equivalent afterwards). +Rules created by +.Cm keep-state +option also have a +.Ar :flowname +taken from it. +This name is used in matching together with addresses, ports and protocol. Dynamic rules will be checked at the first .Cm check-state, keep-state or @@ -2196,23 +2220,23 @@ occurrence, and the action performed upon a match will be the same as in the parent rule. .Pp Note that no additional attributes other than protocol and IP addresses -and ports are checked on dynamic rules. +and ports and :flowname are checked on dynamic rules. .Pp The typical use of dynamic rules is to keep a closed firewall configuration, but let the first TCP SYN packet from the inside network install a dynamic rule for the flow so that packets belonging to that session will be allowed through the firewall: .Pp -.Dl "ipfw add check-state" -.Dl "ipfw add allow tcp from my-subnet to any setup keep-state" +.Dl "ipfw add check-state :OUTBOUND" +.Dl "ipfw add allow tcp from my-subnet to any setup keep-state :OUTBOUND" .Dl "ipfw add deny tcp from any to any" .Pp A similar approach can be used for UDP, where an UDP packet coming from the inside will install a dynamic rule to let the response through the firewall: .Pp -.Dl "ipfw add check-state" -.Dl "ipfw add allow udp from my-subnet to any keep-state" +.Dl "ipfw add check-state :OUTBOUND" +.Dl "ipfw add allow udp from my-subnet to any keep-state :OUTBOUND" .Dl "ipfw add deny udp from any to any" .Pp Dynamic rules expire after some time, which depends on the status diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index 5979f41..7e75098 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -588,7 +588,7 @@ do_cmd(int optname, void *optval, uintptr_t optlen) * Returns 0 on success or errno otherwise. */ int -do_set3(int optname, ip_fw3_opheader *op3, uintptr_t optlen) +do_set3(int optname, ip_fw3_opheader *op3, size_t optlen) { if (co.test_only) @@ -618,6 +618,7 @@ int do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen) { int error; + socklen_t len; if (co.test_only) return (0); @@ -629,8 +630,9 @@ do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen) op3->opcode = optname; - error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, - (socklen_t *)optlen); + len = *optlen; + error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, &len); + *optlen = len; return (error); } @@ -1403,6 +1405,7 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, int l; ipfw_insn *cmd, *has_eaction = NULL, *tagptr = NULL; const char *comment = NULL; /* ptr to comment if we have one */ + const char *ename; int proto = 0; /* default */ int flags = 0; /* prerequisites */ ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */ @@ -1472,6 +1475,12 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, switch(cmd->opcode) { case O_CHECK_STATE: bprintf(bp, "check-state"); + if (cmd->arg1 != 0) + ename = object_search_ctlv(fo->tstate, + cmd->arg1, IPFW_TLV_STATE_NAME); + else + ename = NULL; + bprintf(bp, " :%s", ename ? ename: "any"); /* avoid printing anything else */ flags = HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP | HAVE_IP; @@ -1589,8 +1598,6 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, break; case O_EXTERNAL_ACTION: { - const char *ename; - /* * The external action can consists of two following * each other opcodes - O_EXTERNAL_ACTION and @@ -1611,8 +1618,6 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, } case O_EXTERNAL_INSTANCE: { - const char *ename; - if (has_eaction == NULL) break; /* @@ -2068,6 +2073,9 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, case O_KEEP_STATE: bprintf(bp, " keep-state"); + bprintf(bp, " :%s", + object_search_ctlv(fo->tstate, cmd->arg1, + IPFW_TLV_STATE_NAME)); break; case O_LIMIT: { @@ -2084,6 +2092,9 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, comma = ","; } bprint_uint_arg(bp, " ", c->conn_limit); + bprintf(bp, " :%s", + object_search_ctlv(fo->tstate, cmd->arg1, + IPFW_TLV_STATE_NAME)); break; } @@ -2182,7 +2193,10 @@ show_dyn_state(struct cmdline_opts *co, struct format_opts *fo, bprintf(bp, " <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf, sizeof(buf)), d->id.dst_port); } else - bprintf(bp, " UNKNOWN <-> UNKNOWN\n"); + bprintf(bp, " UNKNOWN <-> UNKNOWN"); + if (d->kidx != 0) + bprintf(bp, " :%s", object_search_ctlv(fo->tstate, + d->kidx, IPFW_TLV_STATE_NAME)); } static int @@ -2823,6 +2837,18 @@ ipfw_check_object_name(const char *name) return (0); } +static char *default_state_name = "default"; +static int +state_check_name(const char *name) +{ + + if (ipfw_check_object_name(name) != 0) + return (EINVAL); + if (strcmp(name, "any") == 0) + return (EINVAL); + return (0); +} + static int eaction_check_name(const char *name) { @@ -3685,6 +3711,25 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) case TOK_CHECKSTATE: have_state = action; action->opcode = O_CHECK_STATE; + if (*av == NULL || + match_token(rule_options, *av) == TOK_COMMENT) { + action->arg1 = pack_object(tstate, + default_state_name, IPFW_TLV_STATE_NAME); + break; + } + if (*av[0] == ':') { + if (strcmp(*av + 1, "any") == 0) + action->arg1 = 0; + else if (state_check_name(*av + 1) == 0) + action->arg1 = pack_object(tstate, *av + 1, + IPFW_TLV_STATE_NAME); + else + errx(EX_DATAERR, "Invalid state name %s", + *av); + av++; + break; + } + errx(EX_DATAERR, "Invalid state name %s", *av); break; case TOK_ACCEPT: @@ -4073,8 +4118,17 @@ chkarg: cmd = next_cmd(cmd, &cblen); } - if (have_state) /* must be a check-state, we are done */ + if (have_state) { /* must be a check-state, we are done */ + if (*av != NULL && + match_token(rule_options, *av) == TOK_COMMENT) { + /* check-state has a comment */ + av++; + fill_comment(cmd, av, cblen); + cmd = next_cmd(cmd, &cblen); + av[0] = NULL; + } goto done; + } #define OR_START(target) \ if (av[0] && (*av[0] == '(' || *av[0] == '{')) { \ @@ -4509,16 +4563,29 @@ read_options: av++; break; - case TOK_KEEPSTATE: + case TOK_KEEPSTATE: { + uint16_t uidx; + if (open_par) errx(EX_USAGE, "keep-state cannot be part " "of an or block"); if (have_state) errx(EX_USAGE, "only one of keep-state " "and limit is allowed"); + if (*av != NULL && *av[0] == ':') { + if (state_check_name(*av + 1) != 0) + errx(EX_DATAERR, + "Invalid state name %s", *av); + uidx = pack_object(tstate, *av + 1, + IPFW_TLV_STATE_NAME); + av++; + } else + uidx = pack_object(tstate, default_state_name, + IPFW_TLV_STATE_NAME); have_state = cmd; - fill_cmd(cmd, O_KEEP_STATE, 0, 0); + fill_cmd(cmd, O_KEEP_STATE, 0, uidx); break; + } case TOK_LIMIT: { ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; @@ -4549,8 +4616,18 @@ read_options: GET_UINT_ARG(c->conn_limit, IPFW_ARG_MIN, IPFW_ARG_MAX, TOK_LIMIT, rule_options); - av++; + + if (*av != NULL && *av[0] == ':') { + if (state_check_name(*av + 1) != 0) + errx(EX_DATAERR, + "Invalid state name %s", *av); + cmd->arg1 = pack_object(tstate, *av + 1, + IPFW_TLV_STATE_NAME); + av++; + } else + cmd->arg1 = pack_object(tstate, + default_state_name, IPFW_TLV_STATE_NAME); break; } @@ -4756,7 +4833,7 @@ done: * generate O_PROBE_STATE if necessary */ if (have_state && have_state->opcode != O_CHECK_STATE) { - fill_cmd(dst, O_PROBE_STATE, 0, 0); + fill_cmd(dst, O_PROBE_STATE, 0, have_state->arg1); dst = next_cmd(dst, &rblen); } @@ -5141,6 +5218,7 @@ static struct _s_x intcmds[] = { static struct _s_x otypes[] = { { "EACTION", IPFW_TLV_EACTION }, + { "DYNSTATE", IPFW_TLV_STATE_NAME }, { NULL, 0 } }; diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index c89a1c5..3e5e83a 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -299,7 +299,7 @@ void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set); struct _ip_fw3_opheader; int do_cmd(int optname, void *optval, uintptr_t optlen); -int do_set3(int optname, struct _ip_fw3_opheader *op3, uintptr_t optlen); +int do_set3(int optname, struct _ip_fw3_opheader *op3, size_t optlen); int do_get3(int optname, struct _ip_fw3_opheader *op3, size_t *optlen); struct in6_addr; |