From 53af6d6ee6c58131a75e8fc28eaa2bb3d554af14 Mon Sep 17 00:00:00 2001 From: Jayden Nyamiaka Date: Fri, 13 Sep 2024 12:55:05 -0700 Subject: [PATCH] Improve conciseness of parsing keywords and String func for enum constants. Replaced overly wordy switch statements with concise maps. Also makes the style consistent with how meta enum constants are processed. PiperOrigin-RevId: 674402548 --- pkg/tcpip/nftables/nftables.go | 299 +++++++++++++++----------------- pkg/tcpip/nftables/nftinterp.go | 107 ++++++------ 2 files changed, 196 insertions(+), 210 deletions(-) diff --git a/pkg/tcpip/nftables/nftables.go b/pkg/tcpip/nftables/nftables.go index d20a7b4ba..154d2271f 100644 --- a/pkg/tcpip/nftables/nftables.go +++ b/pkg/tcpip/nftables/nftables.go @@ -104,44 +104,40 @@ const ( NumAFs ) +// addressFamilyStrings maps address families to their string representation. +var addressFamilyStrings = map[AddressFamily]string{ + IP: "IPv4", + IP6: "IPv6", + Inet: "Internet (Both IPv4/IPv6)", + Arp: "ARP", + Bridge: "Bridge", + Netdev: "Netdev", +} + // String for AddressFamily returns the name of the address family. func (f AddressFamily) String() string { - switch f { - case IP: - return "IPv4" - case IP6: - return "IPv6" - case Inet: - return "Internet (Both IPv4/IPv6)" - case Arp: - return "ARP" - case Bridge: - return "Bridge" - case Netdev: - return "Netdev" - default: - panic(fmt.Sprintf("invalid address family: %d", int(f))) + if af, ok := addressFamilyStrings[f]; ok { + return af } + panic(fmt.Sprintf("invalid address family: %d", int(f))) +} + +// addressFamilyProtocols maps address families to their protocol number. +var addressFamilyProtocols = map[AddressFamily]uint8{ + IP: linux.NFPROTO_INET, + IP6: linux.NFPROTO_IPV6, + Inet: linux.NFPROTO_IPV6, + Arp: linux.NFPROTO_ARP, + Bridge: linux.NFPROTO_BRIDGE, + Netdev: linux.NFPROTO_NETDEV, } // Protocol returns the protocol number for the address family. func (f AddressFamily) Protocol() uint8 { - switch f { - case IP: - return linux.NFPROTO_INET - case IP6: - return linux.NFPROTO_IPV6 - case Inet: - return linux.NFPROTO_IPV6 - case Arp: - return linux.NFPROTO_ARP - case Bridge: - return linux.NFPROTO_BRIDGE - case Netdev: - return linux.NFPROTO_NETDEV - default: - panic(fmt.Sprintf("invalid address family: %d", int(f))) + if protocol, ok := addressFamilyProtocols[f]; ok { + return protocol } + panic(fmt.Sprintf("invalid address family: %d", int(f))) } // validateAddressFamily ensures the family address is valid (within bounds). @@ -185,26 +181,23 @@ const ( NumHooks ) +// hookStrings maps hooks to their string representation. +var hookStrings = map[Hook]string{ + Prerouting: "Prerouting", + Input: "Input", + Forward: "Forward", + Output: "Output", + Postrouting: "Postrouting", + Ingress: "Ingress", + Egress: "Egress", +} + // String for Hook returns the name of the hook. func (h Hook) String() string { - switch h { - case Prerouting: - return "Prerouting" - case Input: - return "Input" - case Forward: - return "Forward" - case Output: - return "Output" - case Postrouting: - return "Postrouting" - case Ingress: - return "Ingress" - case Egress: - return "Egress" - default: - panic(fmt.Sprintf("invalid hook: %d", int(h))) + if hook, ok := hookStrings[h]; ok { + return hook } + panic(fmt.Sprintf("invalid hook: %d", int(h))) } // supportedHooks maps each address family to its supported hooks. @@ -379,18 +372,19 @@ const ( NumBaseChainTypes ) +// baseChainTypeStrings maps base chain types to their string representation. +var baseChainTypeStrings = map[BaseChainType]string{ + BaseChainTypeFilter: "filter", + BaseChainTypeNat: "nat", + BaseChainTypeRoute: "route", +} + // String for BaseChainType returns the name of the base chain type. func (bcType BaseChainType) String() string { - switch bcType { - case BaseChainTypeFilter: - return "filter" - case BaseChainTypeNat: - return "nat" - case BaseChainTypeRoute: - return "route" - default: - panic(fmt.Sprintf("invalid base chain type: %d", int(bcType))) + if bcTypeString, ok := baseChainTypeStrings[bcType]; ok { + return bcTypeString } + panic(fmt.Sprintf("invalid base chain type: %d", int(bcType))) } // supportedAFsForBaseChainTypes maps each base chain type to its supported @@ -655,24 +649,22 @@ type comparison struct { // include/uapi/linux/netfilter/nf_tables.h and uses the same constants. type cmpOp int +// cmpOpStrings is a map of cmpOp to its string representation. +var cmpOpStrings = map[cmpOp]string{ + linux.NFT_CMP_EQ: "==", + linux.NFT_CMP_NEQ: "!=", + linux.NFT_CMP_LT: "<", + linux.NFT_CMP_LTE: "<=", + linux.NFT_CMP_GT: ">", + linux.NFT_CMP_GTE: ">=", +} + // String for cmpOp returns string representation of the comparison operator. func (cop cmpOp) String() string { - switch cop { - case linux.NFT_CMP_EQ: - return "==" - case linux.NFT_CMP_NEQ: - return "!=" - case linux.NFT_CMP_LT: - return "<" - case linux.NFT_CMP_LTE: - return "<=" - case linux.NFT_CMP_GT: - return ">" - case linux.NFT_CMP_GTE: - return ">=" - default: - panic(fmt.Sprintf("invalid comparison operator: %d", int(cop))) + if copStr, ok := cmpOpStrings[cop]; ok { + return copStr } + panic(fmt.Sprintf("invalid comparison operator: %d", int(cop))) } // validateComparisonOp ensures the comparison operator is valid. @@ -757,16 +749,18 @@ type ranged struct { // include/uapi/linux/netfilter/nf_tables.h and uses the same constants. type rngOp int +// rngOpStrings is a map of rngOp to its string representation. +var rngOpStrings = map[rngOp]string{ + linux.NFT_RANGE_EQ: "range ==", + linux.NFT_RANGE_NEQ: "range !=", +} + // String for rngOp returns string representation of the range operator. func (rop rngOp) String() string { - switch rop { - case linux.NFT_RANGE_EQ: - return "range ==" - case linux.NFT_CMP_NEQ: - return "range !=" - default: - panic(fmt.Sprintf("invalid range operator: %d", int(rop))) + if ropStr, ok := rngOpStrings[rop]; ok { + return ropStr } + panic(fmt.Sprintf("invalid range operator: %d", int(rop))) } // validateRangeOp ensures the range operator is valid. @@ -837,23 +831,21 @@ type payloadLoad struct { // include/uapi/linux/netfilter/nf_tables.h and uses the same constants. type payloadBase int +// payloadBaseStrings is a map of payloadBase to its string representation. +var payloadBaseStrings = map[payloadBase]string{ + linux.NFT_PAYLOAD_LL_HEADER: "Link Layer Header", + linux.NFT_PAYLOAD_NETWORK_HEADER: "Network Header", + linux.NFT_PAYLOAD_TRANSPORT_HEADER: "Transport Header", + linux.NFT_PAYLOAD_INNER_HEADER: "Inner Header", + linux.NFT_PAYLOAD_TUN_HEADER: "Tunneling Header", +} + // String for payloadBase returns the string representation of the payload base. func (base payloadBase) String() string { - // Uses errors from validation to handle unsupported payload bases. - if err := validatePayloadBase(base); err != nil { - panic(err) - } - // Cases for supported payload bases. - switch base { - case linux.NFT_PAYLOAD_LL_HEADER: - return "Link Layer Header" - case linux.NFT_PAYLOAD_NETWORK_HEADER: - return "Network Header" - case linux.NFT_PAYLOAD_TRANSPORT_HEADER: - return "Transport Header" - default: - return fmt.Sprintf("Unknown Supported Payload Base: %d", int(base)) + if baseStr, ok := payloadBaseStrings[base]; ok { + return baseStr } + panic(fmt.Sprintf("Invalid Payload Base: %d", int(base))) } // validatePayloadBase ensures the payload base is valid. @@ -863,10 +855,6 @@ func validatePayloadBase(base payloadBase) error { case linux.NFT_PAYLOAD_LL_HEADER, linux.NFT_PAYLOAD_NETWORK_HEADER, linux.NFT_PAYLOAD_TRANSPORT_HEADER: return nil // Unsupported payload bases. - case linux.NFT_PAYLOAD_INNER_HEADER: - return fmt.Errorf("inner header not supported") - case linux.NFT_PAYLOAD_TUN_HEADER: - return fmt.Errorf("tunneling header not supported") default: return fmt.Errorf("invalid payload base: %d", int(base)) } @@ -1091,19 +1079,20 @@ func (op payloadSet) evaluate(regs *registerSet, pkt *stack.PacketBuffer, rule * // include/uapi/linux/netfilter/nf_tables.h and uses the same constants. type bitwiseOp int +// bitwiseOpStrings is a map of bitwiseOp to its string representation. +var bitwiseOpStrings = map[bitwiseOp]string{ + linux.NFT_BITWISE_BOOL: "bitwise boolean", + linux.NFT_BITWISE_LSHIFT: "bitwise <<", + linux.NFT_BITWISE_RSHIFT: "bitwise >>", +} + // String for bitwiseOp returns the string representation of the bitwise // operator. func (bop bitwiseOp) String() string { - switch bop { - case linux.NFT_BITWISE_BOOL: - return "bitwise boolean" - case linux.NFT_BITWISE_LSHIFT: - return "bitwise <<" - case linux.NFT_BITWISE_RSHIFT: - return "bitwise >>" - default: - panic(fmt.Sprintf("invalid bitwise operator: %d", int(bop))) + if str, ok := bitwiseOpStrings[bop]; ok { + return str } + panic(fmt.Sprintf("invalid bitwise operator: %d", int(bop))) } // bitwise is an operation that performs bitwise math operations over data in @@ -1322,23 +1311,21 @@ type route struct { // include/uapi/linux/netfilter/nf_tables.h and uses the same constants. type routeKey int +// routeKeyStrings is a map of route key to its string representation. +var routeKeyStrings = map[routeKey]string{ + linux.NFT_RT_CLASSID: "Traffic Class ID", + linux.NFT_RT_NEXTHOP4: "Next Hop IPv4", + linux.NFT_RT_NEXTHOP6: "Next Hop IPv6", + linux.NFT_RT_TCPMSS: "TCP Maximum Segment Size (TCPMSS)", + linux.NFT_RT_XFRM: "IPsec Transformation", +} + // String for routeKey returns the string representation of the route key. func (key routeKey) String() string { - // Uses errors from validation to handle unsupported route keys. - if err := validateRouteKey(key); err != nil { - panic(err) - } - // Cases for supported route keys. - switch key { - case linux.NFT_RT_NEXTHOP4: - return "Next Hop IPv4" - case linux.NFT_RT_NEXTHOP6: - return "Next Hop IPv6" - case linux.NFT_RT_TCPMSS: - return "TCP Maximum Segment Size (TCPMSS)" - default: - return fmt.Sprintf("Unknown Supported Route Key: %d", int(key)) + if keyStr, ok := routeKeyStrings[key]; ok { + return keyStr } + panic(fmt.Sprintf("invalid route key: %d", int(key))) } // validateRouteKey ensures the route key is valid. @@ -1430,17 +1417,20 @@ type byteorder struct { // include/uapi/linux/netfilter/nf_tables.h and uses the same constants. type byteorderOp int +// byteorderOpStrings is a map of byteorder operator to its string +// representation. +var byteorderOpStrings = map[byteorderOp]string{ + linux.NFT_BYTEORDER_NTOH: "network to host", + linux.NFT_BYTEORDER_HTON: "host to network", +} + // String for byteorderOp returns the string representation of the byteorder // operator. func (bop byteorderOp) String() string { - switch bop { - case linux.NFT_BYTEORDER_NTOH: - return "network to host" - case linux.NFT_BYTEORDER_HTON: - return "host to network" - default: - panic(fmt.Sprintf("unknown supported byteorder operator: %d", int(bop))) + if bopStr, ok := byteorderOpStrings[bop]; ok { + return bopStr } + panic(fmt.Sprintf("invalid byteorder operator: %d", int(bop))) } // validateByteorderOp ensures the byteorder operator is valid. @@ -1602,11 +1592,10 @@ var metaKeyStrings = map[metaKey]string{ // String for metaKey returns the string representation of the meta key. This // supports strings for supported and unsupported meta keys. func (key metaKey) String() string { - keyStr, ok := metaKeyStrings[key] - if !ok { - return fmt.Sprintf("Unsupported Meta Key: %d", int(key)) + if keyStr, ok := metaKeyStrings[key]; ok { + return keyStr } - return keyStr + panic(fmt.Sprintf("invalid meta key: %d", int(key))) } // metaDataLengths holds the length in bytes for each supported meta key. @@ -1855,7 +1844,7 @@ func newBytesData(bytes []byte) bytesData { // String returns a string representation of the big endian bytes data. func (rd bytesData) String() string { - return fmt.Sprintf("be %x", rd.data) + return fmt.Sprintf("%x", rd.data) } // equal compares the bytes data to another RegisterData object. @@ -1967,36 +1956,30 @@ func VC(v int32) uint32 { return uint32(v) } +// verdictCodeStrings is a map of verdict code to its string representation. +var verdictCodeStrings = map[uint32]string{ + // Netfilter (External) Verdicts: + VC(linux.NF_DROP): "Drop", + VC(linux.NF_ACCEPT): "Accept", + VC(linux.NF_STOLEN): "Stolen", + VC(linux.NF_QUEUE): "Queue", + VC(linux.NF_REPEAT): "Repeat", + VC(linux.NF_STOP): "Stop", + // Nftable (Internal) Verdicts: + VC(linux.NFT_CONTINUE): "Continue", + VC(linux.NFT_BREAK): "Break", + VC(linux.NFT_JUMP): "Jump", + VC(linux.NFT_GOTO): "Goto", + VC(linux.NFT_RETURN): "Return", +} + // VerdictCodeToString prints names for the supported verdicts. func VerdictCodeToString(v uint32) string { - switch v { - // Netfilter (External) Verdicts: - case VC(linux.NF_DROP): - return "Drop" - case VC(linux.NF_ACCEPT): - return "Accept" - case VC(linux.NF_STOLEN): - return "Stolen" - case VC(linux.NF_QUEUE): - return "Queue" - case VC(linux.NF_REPEAT): - return "Repeat" - case VC(linux.NF_STOP): - return "Stop" - // Nftable (Internal) Verdicts: - case VC(linux.NFT_CONTINUE): - return "Continue" - case VC(linux.NFT_BREAK): - return "Break" - case VC(linux.NFT_JUMP): - return "Jump" - case VC(linux.NFT_GOTO): - return "Goto" - case VC(linux.NFT_RETURN): - return "Return" - default: - panic(fmt.Sprintf("invalid verdict: %d", int(v))) + + if vcStr, ok := verdictCodeStrings[v]; ok { + return vcStr } + return fmt.Sprintf("invalid verdict: %d", v) } // ----------------------------------------------------------------------------- diff --git a/pkg/tcpip/nftables/nftinterp.go b/pkg/tcpip/nftables/nftinterp.go index 002bff087..80fdcc35a 100644 --- a/pkg/tcpip/nftables/nftinterp.go +++ b/pkg/tcpip/nftables/nftinterp.go @@ -955,27 +955,27 @@ func parseRegisterData(reg uint8, tokens []string, lnIdx int, tkIdx int) (int, r return 0, nil, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("invalid register data: '%s'", tokens[tkIdx])} } +// verdictCodeFromKeyword is a map of verdict keyword to its corresponding enum value. +var verdictCodeFromKeyword = map[string]int32{ + "accept": linux.NF_ACCEPT, + "drop": linux.NF_DROP, + "continue": linux.NFT_CONTINUE, + "return": linux.NFT_RETURN, + "jump": linux.NFT_JUMP, + "goto": linux.NFT_GOTO, +} + // parseVerdict parses the verdict from the given token and returns // the index of the next token to process (can consume multiple tokens). func parseVerdict(tokens []string, lnIdx int, tkIdx int) (int, Verdict, error) { v := Verdict{} - switch tokens[tkIdx] { - case "accept": - v.Code = VC(linux.NF_ACCEPT) - case "drop": - v.Code = VC(linux.NF_DROP) - case "continue": - v.Code = VC(linux.NFT_CONTINUE) - case "return": - v.Code = VC(linux.NFT_RETURN) - case "jump": - v.Code = VC(linux.NFT_JUMP) - case "goto": - v.Code = VC(linux.NFT_GOTO) - default: + vcString := tokens[tkIdx] + vc, ok := verdictCodeFromKeyword[vcString] + if !ok { return 0, v, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("invalid verdict: '%s'", tokens[tkIdx])} } + v.Code = VC(vc) tkIdx++ // jump and chain verdicts require 2 more tokens to specify the target chain. @@ -1027,24 +1027,24 @@ func parseHexData(tokens []string, lnIdx int, tkIdx int) (int, []byte, error) { return tkIdx, bytes, nil } +// cmpOpFromKeyword is a map of comparison operator keywords to their +// corresponding enum value. +var cmpOpFromKeyword = map[string]int{ + "eq": linux.NFT_CMP_EQ, + "neq": linux.NFT_CMP_NEQ, + "lt": linux.NFT_CMP_LT, + "lte": linux.NFT_CMP_LTE, + "gt": linux.NFT_CMP_GT, + "gte": linux.NFT_CMP_GTE, +} + // parseCmpOp parses the int representing the cmpOp from the given string. func parseCmpOp(copString string, lnIdx int, tkIdx int) (int, error) { - switch copString { - case "eq": - return linux.NFT_CMP_EQ, nil - case "neq": - return linux.NFT_CMP_NEQ, nil - case "lt": - return linux.NFT_CMP_LT, nil - case "lte": - return linux.NFT_CMP_LTE, nil - case "gt": - return linux.NFT_CMP_GT, nil - case "gte": - return linux.NFT_CMP_GTE, nil - default: + cop, ok := cmpOpFromKeyword[copString] + if !ok { return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("invalid comparison operator keyword: '%s'", copString)} } + return cop, nil } // parseUint8PlusChar parses the a uint8 followed by the given character from @@ -1062,40 +1062,43 @@ func parseUint8PlusChar(numString string, char byte, lnIdx int, tkIdx int) (uint return uint8(num), nil } +// payloadBaseFromKeyword is a map of payload base keywords to their +// corresponding enum value. +var payloadBaseFromKeyword = map[string]payloadBase{ + "link": linux.NFT_PAYLOAD_LL_HEADER, + "network": linux.NFT_PAYLOAD_NETWORK_HEADER, + "transport": linux.NFT_PAYLOAD_TRANSPORT_HEADER, +} + // parsePayloadBase parses the payload base header from the given string. func parsePayloadBase(baseString string, lnIdx int, tkIdx int) (payloadBase, error) { - switch baseString { - case "link": - return linux.NFT_PAYLOAD_LL_HEADER, nil - case "network": - return linux.NFT_PAYLOAD_NETWORK_HEADER, nil - case "transport": - return linux.NFT_PAYLOAD_TRANSPORT_HEADER, nil - // Inner and Tunnel Headers cannot be specified in payload load operation. - default: + base, ok := payloadBaseFromKeyword[baseString] + if !ok { + // Inner and Tunnel Headers cannot be specified in payload load operation. return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("invalid payload base keyword: '%s'", baseString)} } + return base, nil +} + +// routeKeys is a map of route key keywords to their corresponding enum value. +var routeKeyFromKeyword = map[string]routeKey{ + // Fully supported route keys. + "nexthop4": linux.NFT_RT_NEXTHOP4, + "nexthop6": linux.NFT_RT_NEXTHOP6, + "tcpmss": linux.NFT_RT_TCPMSS, + // Keys supported for interpretation but not yet for logic/evaluation. + // Note: Will result in logic error during operation construction. + "classid": linux.NFT_RT_CLASSID, + "ipsec": linux.NFT_RT_XFRM, } // parseRouteKey parses the route key from the given string. func parseRouteKey(keyString string, lnIdx int, tkIdx int) (routeKey, error) { - switch keyString { - // Fully supported route keys. - case "nexthop4": - return linux.NFT_RT_NEXTHOP4, nil - case "nexthop6": - return linux.NFT_RT_NEXTHOP6, nil - case "tcpmss": - return linux.NFT_RT_TCPMSS, nil - // Keys supported for interpretation but not yet for logic/evaluation. - // Note: Will result in logic error during operation construction. - case "classid": - return linux.NFT_RT_CLASSID, nil - case "ipsec": - return linux.NFT_RT_XFRM, nil - default: + key, ok := routeKeyFromKeyword[keyString] + if !ok { return 0, &SyntaxError{lnIdx, tkIdx, fmt.Sprintf("invalid route key keyword: '%s'", keyString)} } + return key, nil } // metaKeyFromKeyword is a map of meta key keywords to their corresponding enum value.