From 5721ca94790cbd2af3c4eb592d5290ee59369ba8 Mon Sep 17 00:00:00 2001 From: Nico Berlee Date: Sat, 2 Jul 2022 18:02:27 +0200 Subject: [PATCH] fw: support for icmpv6 nftables in system rules - Add support for all available nftables ICMPv6 types (ip6tables -m icmpv6 --help) - Build nftables ICMPv6 rules - Create a default outbound ICMPv6 echo-request/reply rule (currently outbound echo-request ICMPv6 is by default denied) Signed-off-by: Nico Berlee --- daemon/firewall/nftables/exprs/enums.go | 4 +++ daemon/firewall/nftables/exprs/protocol.go | 10 ++++++++ daemon/firewall/nftables/exprs/utils.go | 29 ++++++++++++++++++++++ daemon/firewall/nftables/parser.go | 2 +- daemon/firewall/nftables/rule_helpers.go | 25 +++++++++++++++---- daemon/system-fw.json | 25 +++++++++++++++++++ 6 files changed, 89 insertions(+), 6 deletions(-) diff --git a/daemon/firewall/nftables/exprs/enums.go b/daemon/firewall/nftables/exprs/enums.go index fd95eb2f0f..450c559cc1 100644 --- a/daemon/firewall/nftables/exprs/enums.go +++ b/daemon/firewall/nftables/exprs/enums.go @@ -170,4 +170,8 @@ const ( ICMP_ROUTER_SOLICITATION = "router-solicitation" ICMP_ADDRESS_MASK_REQUEST = "address-mask-request" ICMP_ADDRESS_MASK_REPLY = "address-mask-reply" + + ICMP_PACKET_TOO_BIG = "packet-too-big" + ICMP_NEIGHBOUR_SOLICITATION = "neighbour-solicitation" + ICMP_NEIGHBOUR_ADVERTISEMENT = "neighbour-advertisement" ) diff --git a/daemon/firewall/nftables/exprs/protocol.go b/daemon/firewall/nftables/exprs/protocol.go index 7ba3f90e78..639290676f 100644 --- a/daemon/firewall/nftables/exprs/protocol.go +++ b/daemon/firewall/nftables/exprs/protocol.go @@ -71,6 +71,16 @@ func NewExprProtocol(proto string) (*[]expr.Any, error) { }, }, nil + case NFT_PROTO_ICMPv6: + return &[]expr.Any{ + &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, + &expr.Cmp{ + Op: expr.CmpOpEq, + Register: 1, + Data: []byte{unix.IPPROTO_ICMPV6}, + }, + }, nil + default: return nil, fmt.Errorf("Not valid protocol rule, invalid or not supported protocol: %s", proto) } diff --git a/daemon/firewall/nftables/exprs/utils.go b/daemon/firewall/nftables/exprs/utils.go index abe16cb8ac..7838680713 100644 --- a/daemon/firewall/nftables/exprs/utils.go +++ b/daemon/firewall/nftables/exprs/utils.go @@ -78,6 +78,35 @@ func GetICMPType(icmpType string) uint8 { return 0 } +// GetICMPv6Type returns an ICMPv6 type code +func GetICMPv6Type(icmpType string) uint8 { + switch icmpType { + case ICMP_DEST_UNREACHABLE: + return layers.ICMPv6TypeDestinationUnreachable + case ICMP_PACKET_TOO_BIG: + return layers.ICMPv6TypePacketTooBig + case ICMP_TIME_EXCEEDED: + return layers.ICMPv6TypeTimeExceeded + case ICMP_PARAMETER_PROBLEM: + return layers.ICMPv6TypeParameterProblem + case ICMP_ECHO_REQUEST: + return layers.ICMPv6TypeEchoRequest + case ICMP_ECHO_REPLY: + return layers.ICMPv6TypeEchoReply + case ICMP_ROUTER_SOLICITATION: + return layers.ICMPv6TypeRouterSolicitation + case ICMP_ROUTER_ADVERTISEMENT: + return layers.ICMPv6TypeRouterAdvertisement + case ICMP_NEIGHBOUR_SOLICITATION: + return layers.ICMPv6TypeNeighborSolicitation + case ICMP_NEIGHBOUR_ADVERTISEMENT: + return layers.ICMPv6TypeNeighborAdvertisement + case ICMP_REDIRECT: + return layers.ICMPv6TypeRedirect + } + return 0 +} + func getICMPv6RejectCode(reason string) uint8 { switch reason { case ICMP_HOST_UNREACHABLE, ICMP_NET_UNREACHABLE, ICMP_NO_ROUTE: diff --git a/daemon/firewall/nftables/parser.go b/daemon/firewall/nftables/parser.go index b2bbc85e3f..0ef2419734 100644 --- a/daemon/firewall/nftables/parser.go +++ b/daemon/firewall/nftables/parser.go @@ -71,7 +71,7 @@ func (n *Nft) parseExpression(table, chain, family string, expression *config.Ex exprList = append(exprList, *exprIP...) case exprs.NFT_PROTO_ICMP, exprs.NFT_PROTO_ICMPv6: - exprICMP := n.buildICMPRule(table, family, expression.Statement.Values) + exprICMP := n.buildICMPRule(table, family, expression.Statement.Name, expression.Statement.Values) if exprICMP == nil { log.Warning("%s icmp statement error", logTag) return nil diff --git a/daemon/firewall/nftables/rule_helpers.go b/daemon/firewall/nftables/rule_helpers.go index 8dc85dca76..6dfa25d19d 100644 --- a/daemon/firewall/nftables/rule_helpers.go +++ b/daemon/firewall/nftables/rule_helpers.go @@ -12,15 +12,25 @@ import ( // rules examples: https://github.com/google/nftables/blob/master/nftables_test.go -func (n *Nft) buildICMPRule(table, family string, icmpOptions []*config.ExprValues) *[]expr.Any { +func (n *Nft) buildICMPRule(table, family string, icmpProtoVersion string, icmpOptions []*config.ExprValues) *[]expr.Any { tbl := getTable(table, family) if tbl == nil { return nil } offset := uint32(0) - setType := nftables.TypeICMPType + icmpType := uint8(0) + setType := nftables.SetDatatype{} + + switch icmpProtoVersion { + case exprs.NFT_PROTO_ICMP: + setType = nftables.TypeICMPType + case exprs.NFT_PROTO_ICMPv6: + setType = nftables.TypeICMP6Type + default: + return nil + } - exprICMP, _ := exprs.NewExprProtocol(exprs.NFT_PROTO_ICMP) + exprICMP, _ := exprs.NewExprProtocol(icmpProtoVersion) ICMPrule := []expr.Any{} ICMPrule = append(ICMPrule, *exprICMP...) @@ -29,10 +39,15 @@ func (n *Nft) buildICMPRule(table, family string, icmpOptions []*config.ExprValu for _, icmp := range icmpOptions { switch icmp.Key { case exprs.NFT_ICMP_TYPE: + if exprs.NFT_PROTO_ICMPv6 == icmpProtoVersion { + icmpType = exprs.GetICMPv6Type(icmp.Value) + } else { + icmpType = exprs.GetICMPType(icmp.Value) + } exprCmp := &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, - Data: []byte{exprs.GetICMPType(icmp.Value)}, + Data: []byte{icmpType}, } ICMPtemp = append(ICMPtemp, []expr.Any{exprCmp}...) @@ -40,7 +55,7 @@ func (n *Nft) buildICMPRule(table, family string, icmpOptions []*config.ExprValu setElements = append(setElements, []nftables.SetElement{ { - Key: []byte{exprs.GetICMPType(icmp.Value)}, + Key: []byte{icmpType}, }, }...) case exprs.NFT_ICMP_CODE: diff --git a/daemon/system-fw.json b/daemon/system-fw.json index b1f14aa6f2..187351ba49 100644 --- a/daemon/system-fw.json +++ b/daemon/system-fw.json @@ -175,6 +175,31 @@ "Target": "accept", "TargetParameters": "" }, + { + "Enabled": true, + "Position": 0, + "Description": "Allow ICMPv6", + "Expressions": [ + { + "Statement": { + "Op": "", + "Name": "icmpv6", + "Values": [ + { + "Key": "type", + "Value": "echo-request" + }, + { + "Key": "type", + "Value": "echo-reply" + } + ] + } + } + ], + "Target": "accept", + "TargetParameters": "" + }, { "Enabled": false, "Position": "0",