summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJeff Bean <jeffreyrobertbean@gmail.com>2018-06-13 19:32:25 -0700
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2018-06-14 22:20:08 +0900
commit0d64c69d136d6a49a11c820604ba1dde0cd60799 (patch)
tree3f23ff6a3aa3b36cddb4913e43e9093aee59a401
parent8df79bebb4be83199bd051691f73eb455b0f101e (diff)
Pull regex into variables to prevent the regex to compile in the hotpath
-rw-r--r--packet/bgp/bgp.go100
-rw-r--r--packet/bgp/bgp_test.go36
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)
+ })
+ }
+}