diff options
author | Jeff Bean <jeffreyrobertbean@gmail.com> | 2018-06-13 19:32:25 -0700 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-06-14 22:20:08 +0900 |
commit | 0d64c69d136d6a49a11c820604ba1dde0cd60799 (patch) | |
tree | 3f23ff6a3aa3b36cddb4913e43e9093aee59a401 | |
parent | 8df79bebb4be83199bd051691f73eb455b0f101e (diff) |
Pull regex into variables to prevent the regex to compile in the hotpath
-rw-r--r-- | packet/bgp/bgp.go | 100 | ||||
-rw-r--r-- | packet/bgp/bgp_test.go | 36 |
2 files changed, 91 insertions, 45 deletions
diff --git a/packet/bgp/bgp.go b/packet/bgp/bgp.go index d12c3e2f..13320e54 100644 --- a/packet/bgp/bgp.go +++ b/packet/bgp/bgp.go @@ -287,6 +287,51 @@ func (c BGPCapabilityCode) String() string { return fmt.Sprintf("UnknownCapability(%d)", c) } +var ( + // Used parsing RouteDistinguisher + _regexpRouteDistinguisher = regexp.MustCompile("^((\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)|((\\d+)\\.)?(\\d+)|([\\w]+:[\\w:]*:[\\w]+)):(\\d+)$") + + // Used for operator and value for the FlowSpec numeric type + // Example: + // re.FindStringSubmatch("&==80") + // >>> ["&==80" "&" "==" "80"] + _regexpFlowSpecNumericType = regexp.MustCompile("(&?)(==|=|>|>=|<|<=|!|!=|=!)?(\\d+|-\\d|true|false)") + + // - "=!" is used in the old style format of "tcp-flags" and "fragment". + // - The value field should be one of the followings: + // * Decimal value (e.g., 80) + // * Combination of the small letters, decimals, "-" and "+" + // (e.g., tcp, ipv4, is-fragment+first-fragment) + // * Capital letters (e.g., SA) + _regexpFlowSpecOperator = regexp.MustCompile("&|=|>|<|!|[\\w\\-+]+") + _regexpFlowSpecOperatorValue = regexp.MustCompile("[\\w\\-+]+") + + // Note: "(-*)" and "(.*)" catch the invalid flags + // Example: In this case, "Z" is unsupported flag type. + // re.FindStringSubmatch("&==-SZU") + // >>> ["&==-SZU" "&" "==" "-" "S" "ZU"] + _regexpFlowSpecTCPFlag = regexp.MustCompile("(&?)(==|=|!|!=|=!)?(-*)([FSRPAUCE]+)(.*)") + + // Note: "(.*)" catches the invalid flags + // re.FindStringSubmatch("&!=+first-fragment+last-fragment+invalid-fragment") + // >>> ["&!=+first-fragment+last-fragment+invalid-fragment" "&" "!=" "+first-fragment+last-fragment" "+last-fragment" "+" "last" "+invalid-fragment"] + _regexpFlowSpecFragment = regexp.MustCompile("(&?)(==|=|!|!=|=!)?(((\\+)?(dont|is|first|last|not-a)-fragment)+)(.*)") + + // re.FindStringSubmatch("192.168.0.0/24") + // >>> ["192.168.0.0/24" "192.168.0.0" "/24" "24"] + // re.FindStringSubmatch("192.168.0.1") + // >>> ["192.168.0.1" "192.168.0.1" "" ""] + _regexpFindIPv4Prefix = regexp.MustCompile("^([\\d.]+)(/(\\d{1,2}))?") + + // re.FindStringSubmatch("2001:dB8::/64") + // >>> ["2001:dB8::/64" "2001:dB8::" "/64" "64" "" ""] + // re.FindStringSubmatch("2001:dB8::/64/8") + // >>> ["2001:dB8::/64/8" "2001:dB8::" "/64" "64" "/8" "8"] + // re.FindStringSubmatch("2001:dB8::1") + // >>> ["2001:dB8::1" "2001:dB8::1" "" "" "" ""] + _regexpFindIPv6Prefix = regexp.MustCompile("^([a-fA-F\\d:.]+)(/(\\d{1,3}))?(/(\\d{1,3}))?") +) + type ParameterCapabilityInterface interface { DecodeFromBytes([]byte) error Serialize() ([]byte, error) @@ -1430,8 +1475,7 @@ func GetRouteDistinguisher(data []byte) RouteDistinguisherInterface { } func parseRdAndRt(input string) ([]string, error) { - exp := regexp.MustCompile("^((\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)|((\\d+)\\.)?(\\d+)|([\\w]+:[\\w:]*:[\\w]+)):(\\d+)$") - elems := exp.FindStringSubmatch(input) + elems := _regexpRouteDistinguisher.FindStringSubmatch(input) if len(elems) != 11 { return nil, fmt.Errorf("failed to parse") } @@ -3142,22 +3186,12 @@ var FlowSpecValueMap = map[string]BGPFlowSpecType{ // fmt.Printf("%q", normalizeFlowSpecOpValues(args)) // >>> ["<=80" "tcp" "!=udp" "=!SA" "&=U" "!F" "=is-fragment+last-fragment"] func normalizeFlowSpecOpValues(args []string) []string { - // Note: - // - "=!" is used in the old style format of "tcp-flags" and "fragment". - // - The value field should be one of the followings: - // * Decimal value (e.g., 80) - // * Combination of the small letters, decimals, "-" and "+" - // (e.g., tcp, ipv4, is-fragment+first-fragment) - // * Capital letters (e.g., SA) - re := regexp.MustCompile("&|=|>|<|!|[\\w\\-+]+") - reValue := regexp.MustCompile("[\\w\\-+]+") - // Extracts keywords from the given args. sub := "" subs := make([]string, 0) - for _, s := range re.FindAllString(strings.Join(args, " "), -1) { + for _, s := range _regexpFlowSpecOperator.FindAllString(strings.Join(args, " "), -1) { sub += s - if reValue.MatchString(s) { + if _regexpFlowSpecOperatorValue.MatchString(s) { subs = append(subs, sub) sub = "" } @@ -3195,12 +3229,8 @@ func parseFlowSpecNumericOperator(submatch []string) (operator uint8, err error) func parseFlowSpecNumericOpValues(typ BGPFlowSpecType, args []string, validationFunc func(uint64) error) (FlowSpecComponentInterface, error) { argsLen := len(args) items := make([]*FlowSpecComponentItem, 0, argsLen) - re := regexp.MustCompile("(&?)(==|=|>|>=|<|<=|!|!=|=!)?(\\d+|-\\d|true|false)") for idx, arg := range args { - // Example: - // re.FindStringSubmatch("&==80") - // >>> ["&==80" "&" "==" "80"] - m := re.FindStringSubmatch(arg) + m := _regexpFlowSpecNumericType.FindStringSubmatch(arg) if len(m) < 4 { return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args) } @@ -3298,12 +3328,7 @@ func flowSpecPrefixParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (F return nil, fmt.Errorf("cannot specify offset for ipv4 prefix") } invalidIPv4PrefixError := fmt.Errorf("invalid ipv4 prefix: %s", args[0]) - re := regexp.MustCompile("^([\\d.]+)(/(\\d{1,2}))?") - // re.FindStringSubmatch("192.168.0.0/24") - // >>> ["192.168.0.0/24" "192.168.0.0" "/24" "24"] - // re.FindStringSubmatch("192.168.0.1") - // >>> ["192.168.0.1" "192.168.0.1" "" ""] - m := re.FindStringSubmatch(args[0]) + m := _regexpFindIPv4Prefix.FindStringSubmatch(args[0]) if len(m) < 4 { return nil, invalidIPv4PrefixError } @@ -3331,14 +3356,7 @@ func flowSpecPrefixParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (F return nil, fmt.Errorf("invalid arguments for ipv6 prefix: %q", args) } invalidIPv6PrefixError := fmt.Errorf("invalid ipv6 prefix: %s", args[0]) - re := regexp.MustCompile("^([a-fA-F\\d:.]+)(/(\\d{1,3}))?(/(\\d{1,3}))?") - // re.FindStringSubmatch("2001:dB8::/64") - // >>> ["2001:dB8::/64" "2001:dB8::" "/64" "64" "" ""] - // re.FindStringSubmatch("2001:dB8::/64/8") - // >>> ["2001:dB8::/64/8" "2001:dB8::" "/64" "64" "/8" "8"] - // re.FindStringSubmatch("2001:dB8::1") - // >>> ["2001:dB8::1" "2001:dB8::1" "" "" "" ""] - m := re.FindStringSubmatch(args[0]) + m := _regexpFindIPv6Prefix.FindStringSubmatch(args[0]) if len(m) < 4 { return nil, invalidIPv6PrefixError } @@ -3419,13 +3437,9 @@ func flowSpecTcpFlagParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (F argsLen := len(args) items := make([]*FlowSpecComponentItem, 0, argsLen) - // Note: "(-*)" and "(.*)" catch the invalid flags - re := regexp.MustCompile("(&?)(==|=|!|!=|=!)?(-*)([FSRPAUCE]+)(.*)") + for _, arg := range args { - // Example: In this case, "Z" is unsupported flag type. - // re.FindStringSubmatch("&==-SZU") - // >>> ["&==-SZU" "&" "==" "-" "S" "ZU"] - m := re.FindStringSubmatch(arg) + m := _regexpFlowSpecTCPFlag.FindStringSubmatch(arg) if len(m) < 6 { return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args) } else if mLast := m[len(m)-1]; mLast != "" || m[3] != "" { @@ -3475,13 +3489,9 @@ func flowSpecFragmentParser(_ RouteFamily, typ BGPFlowSpecType, args []string) ( argsLen := len(args) items := make([]*FlowSpecComponentItem, 0, argsLen) - // Note: "(.*)" catches the invalid flags - re := regexp.MustCompile("(&?)(==|=|!|!=|=!)?(((\\+)?(dont|is|first|last|not-a)-fragment)+)(.*)") + for _, arg := range args { - // Example: - // re.FindStringSubmatch("&!=+first-fragment+last-fragment+invalid-fragment") - // >>> ["&!=+first-fragment+last-fragment+invalid-fragment" "&" "!=" "+first-fragment+last-fragment" "+last-fragment" "+" "last" "+invalid-fragment"] - m := re.FindStringSubmatch(arg) + m := _regexpFlowSpecFragment.FindStringSubmatch(arg) if len(m) < 4 { return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args) } else if mLast := m[len(m)-1]; mLast != "" { diff --git a/packet/bgp/bgp_test.go b/packet/bgp/bgp_test.go index ef1cca8f..697c204b 100644 --- a/packet/bgp/bgp_test.go +++ b/packet/bgp/bgp_test.go @@ -38,6 +38,16 @@ func refresh() *BGPMessage { return NewBGPRouteRefreshMessage(1, 2, 10) } +var result []string + +func BenchmarkNormalizeFlowSpecOpValues(b *testing.B) { + var r []string + for n := 0; n < b.N; n++ { + r = normalizeFlowSpecOpValues([]string{"&<=80"}) + } + result = r +} + func Test_Message(t *testing.T) { l := []*BGPMessage{keepalive(), notification(), refresh(), NewTestBGPOpenMessage(), NewTestBGPUpdateMessage()} for _, m1 := range l { @@ -1215,3 +1225,29 @@ func TestFuzzCrashers(t *testing.T) { ParseBGPMessage([]byte(f)) } } + +func TestNormalizeFlowSpecOpValues(t *testing.T) { + tests := []struct { + msg string + args []string + want []string + }{ + { + msg: "valid match", + args: []string{" & <=80", " tcp != udp ", " =! SA & =U! F", " = is-fragment+last-fragment"}, + want: []string{"<=80", "tcp", "!=udp", "=!SA", "&=U", "!F", "=is-fragment+last-fragment"}, + }, + { + msg: "RFC5575 trims & prefix", + args: []string{"&<=80"}, + want: []string{"<=80"}, + }, + } + + for _, tt := range tests { + t.Run(tt.msg, func(t *testing.T) { + got := normalizeFlowSpecOpValues(tt.args) + assert.Equal(t, tt.want, got) + }) + } +} |