diff options
author | Andrew Topp <andrewt@telekinetica.net> | 2024-08-04 17:52:57 +1000 |
---|---|---|
committer | Andrew Topp <andrewt@telekinetica.net> | 2024-08-04 17:52:57 +1000 |
commit | 60b0614296874c144665417130d4881461114db0 (patch) | |
tree | 404eb8bf72582b60cad69d9c23535b41a49094f6 /src | |
parent | 15c77978f30bebe7c6d4f4e9a87c56e12e1382cd (diff) | |
download | vyos-1x-60b0614296874c144665417130d4881461114db0.tar.gz vyos-1x-60b0614296874c144665417130d4881461114db0.zip |
firewall: T4694: Adding GRE flags & fields matches to firewall rules
* Only matching flags and fields used by modern RFC2890 "extended GRE" -
this is backwards-compatible, but does not match all possible flags.
* There are no nftables helpers for the GRE key field, which is critical
to match individual tunnel sessions (more detail in the forum post)
* nft expression syntax is not flexible enough for multiple field
matches in a single rule and the key offset changes depending on flags.
* Thus, clumsy compromise in requiring an explicit match on the "checksum"
flag if a key is present, so we know where key will be. In most cases,
nobody uses the checksum, but assuming it to be off or automatically
adding a "not checksum" match unless told otherwise would be confusing
* The automatic "flags key" check when specifying a key doesn't have similar
validation, I added it first and it makes sense. I would still like
to find a workaround to the "checksum" offset problem.
* If we could add 2 rules from 1 config definition, we could match
both cases with appropriate offsets, but this would break existing
FW generation logic, logging, etc.
* Added a "test_gre_match" smoketest
Diffstat (limited to 'src')
-rwxr-xr-x | src/conf_mode/firewall.py | 30 |
1 files changed, 30 insertions, 0 deletions
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index 02bf00bcc..b71ce7124 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -231,6 +231,36 @@ def verify_rule(firewall, family, hook, priority, rule_id, rule_conf): if not {'count', 'time'} <= set(rule_conf['recent']): raise ConfigError('Recent "count" and "time" values must be defined') + if 'gre' in rule_conf: + if dict_search_args(rule_conf, 'protocol') != 'gre': + raise ConfigError('Protocol must be gre when matching GRE flags and fields') + + if dict_search_args(rule_conf, 'gre', 'key'): + if dict_search_args(rule_conf, 'gre', 'version') == 'pptp': + raise ConfigError('GRE tunnel keys are not present in PPTP') + + if dict_search_args(rule_conf, 'gre', 'flags', 'checksum') is None: + # There is no builtin match in nftables for the GRE key, so we need to do a raw lookup. + # The offset of the key within the packet shifts depending on the C-flag. + # 99% of the time, nobody will have checksums enabled - it's usually a manual config option. + # We can either assume it is unset unless otherwise directed + # (confusing, requires doco to explain why it doesn't work sometimes) + # or, demand an explicit selection to be made for this specific match rule. + # This check enforces the latter. The user is free to create rules for both cases. + raise ConfigError('Matching GRE tunnel key requires an explicit checksum flag match. For most cases, use "gre flags checksum unset"') + + if dict_search_args(rule_conf, 'gre', 'flags', 'key', 'unset') is not None: + raise ConfigError('Matching GRE tunnel key implies "flags key", cannot specify "flags key unset"') + + gre_inner_proto = dict_search_args(rule_conf, 'gre', 'inner_proto') + if gre_inner_proto is not None: + try: + gre_inner_value = int(gre_inner_proto, 0) + if gre_inner_value < 0 or gre_inner_value > 65535: + raise ConfigError('inner-proto outside valid ethertype range 0-65535') + except ValueError: + pass # Symbolic constant, pre-validated before reaching here. + tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags') if tcp_flags: if dict_search_args(rule_conf, 'protocol') != 'tcp': |