diff options
author | IWASE Yusuke <iwase.yusuke0@gmail.com> | 2017-11-18 23:37:01 +0900 |
---|---|---|
committer | IWASE Yusuke <iwase.yusuke0@gmail.com> | 2017-11-24 12:55:34 +0900 |
commit | 68d2478e2ea1d8607f85e6ab3d4a792e054ce6c2 (patch) | |
tree | 3c04819270fdccc355b1094463fb9db23fd47e53 | |
parent | 35c79d833feafb7fa12afa90e984e6f1f474675e (diff) |
packet/bgp: Use regexp to parse FlowSpec rules
Currently, the parser for the string representation of FlowSpec rules
splits the given string into characters and validates them character
by character.
So we need to handle the unexpected white spaces carefully.
This patch introduces the regular expressions to parse the FlowSpec
rules and simplify the parsers.
Also improves robustness for the unexpectedly inserted white spaces
like "& == tcp".
Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
-rw-r--r-- | packet/bgp/bgp.go | 753 | ||||
-rw-r--r-- | packet/bgp/constant.go | 33 |
2 files changed, 451 insertions, 335 deletions
diff --git a/packet/bgp/bgp.go b/packet/bgp/bgp.go index 57afae9a..86b4cfd7 100644 --- a/packet/bgp/bgp.go +++ b/packet/bgp/bgp.go @@ -2950,418 +2950,501 @@ var FlowSpecValueMap = map[string]BGPFlowSpecType{ FlowSpecNameMap[FLOW_SPEC_TYPE_INNER_COS]: FLOW_SPEC_TYPE_INNER_COS, } -func flowSpecPrefixParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { - if len(args) < 2 { - return nil, fmt.Errorf("invalid flowspec dst/src prefix") - } - typ := args[0] - ip, nw, err := net.ParseCIDR(args[1]) - if err != nil { - return nil, fmt.Errorf("invalid ip prefix") - } - afi, _ := RouteFamilyToAfiSafi(rf) - if afi == AFI_IP && ip.To4() == nil { - return nil, fmt.Errorf("invalid ipv4 prefix") - } else if afi == AFI_IP6 && !strings.Contains(ip.String(), ":") { - return nil, fmt.Errorf("invalid ipv6 prefix") - } - ones, _ := nw.Mask.Size() - var offset uint8 - if len(args) > 2 { - o, err := strconv.Atoi(args[2]) - offset = uint8(o) +// Joins the given and args into a single string and normalize it. +// Example: +// args := []string{" & <=80", " tcp != udp ", " =! SA & =U! F", " = is-fragment+last-fragment"} +// 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) { + sub += s + if reValue.MatchString(s) { + subs = append(subs, sub) + sub = "" + } + } + + // RFC5575 says "It should be unset in the first operator byte of a + // sequence". + if len(subs) > 0 { + subs[0] = strings.TrimPrefix(subs[0], "&") + } + + return subs +} + +// Parses the FlowSpec numeric operator using the given submatch which should be +// the return value of func (*Regexp) FindStringSubmatch. +func parseFlowSpecNumericOperator(submatch []string) (operator int, err error) { + if submatch[1] == "&" { + operator = DEC_NUM_OP_AND + } + value, ok := DECNumOpValueMap[submatch[2]] + if !ok { + return 0, fmt.Errorf("invalid numeric operator: %s%s", submatch[1], submatch[2]) + } + operator |= int(value) + return operator, nil +} + +// Parses the pairs of operator and value for the FlowSpec numeric type. The +// given validationFunc is applied to evaluate whether the parsed value is +// valid or not (e.g., if exceeds range or not). +// Note: Each of the args should be formatted in single pair of operator and +// value before calling this function. +// e.g.) "&==100", ">=200" or "&<300" +func parseFlowSpecNumericOpValues(typ BGPFlowSpecType, args []string, validationFunc func(int) 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) + if len(m) < 4 { + return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args) + } + operator, err := parseFlowSpecNumericOperator(m) if err != nil { return nil, err } + // "true" and "false" is operator, but here handles them as value. + value := 0 + switch m[3] { + case "true", "false": + if idx != argsLen-1 { + return nil, fmt.Errorf("%s should be the last of each rule", m[3]) + } + operator = int(DECNumOpValueMap[m[3]]) + default: + if value, err = strconv.Atoi(m[3]); err != nil { + return nil, fmt.Errorf("invalid numeric value: %s", m[3]) + } + if err = validationFunc(value); err != nil { + return nil, err + } + } + items = append(items, NewFlowSpecComponentItem(operator, value)) } - switch typ { - case FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PREFIX]: - switch rf { - case RF_FS_IPv4_UC, RF_FS_IPv4_VPN: - return NewFlowSpecDestinationPrefix(NewIPAddrPrefix(uint8(ones), ip.String())), nil - case RF_FS_IPv6_UC, RF_FS_IPv6_VPN: - return NewFlowSpecDestinationPrefix6(NewIPv6AddrPrefix(uint8(ones), ip.String()), offset), nil - default: - return nil, fmt.Errorf("invalid type") - } - case FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PREFIX]: - switch rf { - case RF_FS_IPv4_UC, RF_FS_IPv4_VPN: - return NewFlowSpecSourcePrefix(NewIPAddrPrefix(uint8(ones), ip.String())), nil - case RF_FS_IPv6_UC, RF_FS_IPv6_VPN: - return NewFlowSpecSourcePrefix6(NewIPv6AddrPrefix(uint8(ones), ip.String()), offset), nil - default: - return nil, fmt.Errorf("invalid type") + // Marks end-of-list bit + items[argsLen-1].Op |= int(DEC_NUM_OP_END) + + return NewFlowSpecComponent(typ, items), nil +} + +func flowSpecNumeric1ByteParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + args = normalizeFlowSpecOpValues(args) + + f := func(i int) error { + if 0 <= i && i <= 0xff { // 1 byte + return nil } + return fmt.Errorf("%s range exceeded", typ.String()) } - return nil, fmt.Errorf("invalid type. only destination or source is allowed") + + return parseFlowSpecNumericOpValues(typ, args, f) } -func flowSpecIpProtoParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { - if len(args) < 2 || args[0] != FlowSpecNameMap[FLOW_SPEC_TYPE_IP_PROTO] { - return nil, fmt.Errorf("invalid ip-proto format") +func flowSpecNumeric2BytesParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + args = normalizeFlowSpecOpValues(args) + + f := func(i int) error { + if 0 <= i && i <= 0xffff { // 2 bytes + return nil + } + return fmt.Errorf("%s range exceeded", typ.String()) } + + return parseFlowSpecNumericOpValues(typ, args, f) +} + +// Parses the FlowSpec bitmask operand using the given submatch which should be +// the return value of func (*Regexp) FindStringSubmatch. +func parseFlowSpecBitmaskOperand(submatch []string) (operand int, err error) { + if submatch[1] == "&" { + operand = BITMASK_FLAG_OP_AND + } + value, ok := BitmaskFlagOpValueMap[submatch[2]] + if !ok { + return 0, fmt.Errorf("invalid bitmask operand: %s%s", submatch[1], submatch[2]) + } + operand |= int(value) + return operand, nil +} + +func flowSpecPrefixParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + // args[0]: IP Prefix or IP Address (suppose prefix length is 32) + // args[1]: Offset in bit (IPv6 only) + // + // Example: + // - IPv4 Prefix + // args := []string{"192.168.0.0/24"} + // - IPv4 Address + // args := []string{"192.168.0.1"} + // - IPv6 Prefix + // args := []string{"2001:db8:1::/64"} + // - IPv6 Prefix with offset + // args := []string{"2001:db8:1::/64", "16"} + afi, _ := RouteFamilyToAfiSafi(rf) + var prefix net.IP + var prefixLen int + _, nw, err := net.ParseCIDR(args[0]) + if err != nil { + prefix = net.ParseIP(args[0]) + if prefix == nil { + return nil, fmt.Errorf("invalid ip prefix: %s", args[0]) + } + switch afi { + case AFI_IP: + prefixLen = net.IPv4len * 8 + case AFI_IP6: + prefixLen = net.IPv6len * 8 + } + } else { + prefix = nw.IP + prefixLen, _ = nw.Mask.Size() + } + + switch afi { + case AFI_IP: + if prefix.To4() == nil { + return nil, fmt.Errorf("invalid ipv4 prefix: %s", args[0]) + } + if len(args) > 1 { + return nil, fmt.Errorf("cannot specify offset for ipv4 prefix") + } + switch typ { + case FLOW_SPEC_TYPE_DST_PREFIX: + return NewFlowSpecDestinationPrefix(NewIPAddrPrefix(uint8(prefixLen), prefix.String())), nil + case FLOW_SPEC_TYPE_SRC_PREFIX: + return NewFlowSpecSourcePrefix(NewIPAddrPrefix(uint8(prefixLen), prefix.String())), nil + } + return nil, fmt.Errorf("invalid traffic filtering rule type: %s", typ.String()) + case AFI_IP6: + if prefix.To16() == nil { + return nil, fmt.Errorf("invalid ipv6 prefix: %s", args[0]) + } + var offset uint8 + if len(args) > 1 { + o, err := strconv.Atoi(args[1]) + if err != nil { + return nil, fmt.Errorf("invalid ipv6 prefix offset: %s", args[0]) + } + offset = uint8(o) + } + switch typ { + case FLOW_SPEC_TYPE_DST_PREFIX: + return NewFlowSpecDestinationPrefix6(NewIPv6AddrPrefix(uint8(prefixLen), prefix.String()), offset), nil + case FLOW_SPEC_TYPE_SRC_PREFIX: + return NewFlowSpecSourcePrefix6(NewIPv6AddrPrefix(uint8(prefixLen), prefix.String()), offset), nil + } + return nil, fmt.Errorf("invalid traffic filtering rule type: %s", typ.String()) + } + return nil, fmt.Errorf("invalid address family: %s", rf.String()) +} + +func flowSpecIpProtoParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + // args: List of pairs of Operator and IP protocol type + // + // Example: + // - TCP or UDP + // args := []string{"tcp", "==udp"} + // - Not TCP and not UDP + // args := []string{"!=tcp", "&!=udp"} + args = normalizeFlowSpecOpValues(args) s := strings.Join(args, " ") for i, name := range ProtocolNameMap { s = strings.Replace(s, name, fmt.Sprintf("%d", i), -1) } args = strings.Split(s, " ") - validationFunc := func(i int) error { - if 0 < i && i < 255 { + + f := func(i int) error { + if 0 <= i && i <= 0xff { // 1 byte return nil } - return fmt.Errorf("ip protocol range exceeded") + return fmt.Errorf("%s range exceeded", typ.String()) } - return doFlowSpecNumericParser(0, args, validationFunc) + + return parseFlowSpecNumericOpValues(typ, args, f) } -func flowSpecTcpFlagParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { - args = append(args[:0], args[1:]...) // removing tcp-flags string - fullCmd := strings.Join(args, " ") // rebuiling tcp filters - opsFlags, err := parseTcpFlagCmd(fullCmd) - if err != nil { - return nil, err +func flowSpecTcpFlagParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + // args: List of pairs of Operand and TCP Flags + // + // Example: + // - SYN or SYN/ACK + // args := []string{"==S", "==SA"} + // - Not FIN and not URG + // args := []string{"!=F", "&!=U"} + args = normalizeFlowSpecOpValues(args) + + 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) + 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] != "" { + return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args) + } + operand, err := parseFlowSpecBitmaskOperand(m) + if err != nil { + return nil, err + } + value := 0 + for flag, name := range TCPFlagNameMap { + if strings.Contains(m[4], name) { + value |= int(flag) + } + } + items = append(items, NewFlowSpecComponentItem(operand, value)) } - items := make([]*FlowSpecComponentItem, 0) - for _, opFlag := range opsFlags { - items = append(items, NewFlowSpecComponentItem(opFlag[0], opFlag[1])) + + // Marks end-of-list bit + items[argsLen-1].Op |= BITMASK_FLAG_OP_END + + return NewFlowSpecComponent(typ, items), nil +} + +func flowSpecDscpParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + args = normalizeFlowSpecOpValues(args) + + f := func(i int) error { + if 0 <= i && i < 64 { // 6 bits + return nil + } + return fmt.Errorf("%s range exceeded", typ.String()) } - return NewFlowSpecComponent(FLOW_SPEC_TYPE_TCP_FLAG, items), nil + + return parseFlowSpecNumericOpValues(typ, args, f) } -func parseTcpFlagCmd(myCmd string) ([][2]int, error) { - var index int = 0 - var tcpOperatorsFlagsValues [][2]int - var operatorValue [2]int - for index < len(myCmd) { - myCmdChar := myCmd[index : index+1] - switch myCmdChar { - case BitmaskFlagOpNameMap[BITMASK_FLAG_OP_MATCH]: - if bit := BitmaskFlagOpValueMap[myCmdChar]; bit&BitmaskFlagOp(operatorValue[0]) == 0 { - operatorValue[0] |= int(bit) - index++ - } else { - err := fmt.Errorf("Match flag appears multiple time") - return nil, err - } - case BitmaskFlagOpNameMap[BITMASK_FLAG_OP_NOT]: - if bit := BitmaskFlagOpValueMap[myCmdChar]; bit&BitmaskFlagOp(operatorValue[0]) == 0 { - operatorValue[0] |= int(bit) - index++ - } else { - err := fmt.Errorf("Not flag appears multiple time") - return nil, err - } - case BitmaskFlagOpNameMap[BITMASK_FLAG_OP_AND], BitmaskFlagOpNameMap[BITMASK_FLAG_OP_OR]: - if bit := BitmaskFlagOpValueMap[myCmdChar]; bit&BitmaskFlagOp(operatorValue[0]) == 0 { - tcpOperatorsFlagsValues = append(tcpOperatorsFlagsValues, operatorValue) - operatorValue[0] = int(bit) - operatorValue[1] = 0 - index++ - } else { - err := fmt.Errorf("AND or OR (space) operator appears multiple time") - return nil, err - } - case TCPFlagNameMap[TCP_FLAG_ACK], TCPFlagNameMap[TCP_FLAG_SYN], TCPFlagNameMap[TCP_FLAG_FIN], - TCPFlagNameMap[TCP_FLAG_URGENT], TCPFlagNameMap[TCP_FLAG_ECE], TCPFlagNameMap[TCP_FLAG_RST], - TCPFlagNameMap[TCP_FLAG_CWR], TCPFlagNameMap[TCP_FLAG_PUSH]: - myLoopChar := myCmdChar - loopIndex := index - // we loop till we reach the end of TCP flags description - // exit conditions : we reach the end of tcp flags (we find & or ' ') or we reach the end of the line - for loopIndex < len(myCmd) && - (myLoopChar != BitmaskFlagOpNameMap[BITMASK_FLAG_OP_AND] && myLoopChar != BitmaskFlagOpNameMap[BITMASK_FLAG_OP_OR]) { - // we check if inspected charater is a well known tcp flag and if it doesn't appear twice - if bit, isPresent := TCPFlagValueMap[myLoopChar]; isPresent && (bit&TCPFlag(operatorValue[1]) == 0) { - operatorValue[1] |= int(bit) // we set this flag - loopIndex++ // we move to next character - if loopIndex < len(myCmd) { - myLoopChar = myCmd[loopIndex : loopIndex+1] // we move to the next character only if we didn't reach the end of cmd - } - } else { - err := fmt.Errorf("flag %s appears multiple time or is not part of TCP flags", myLoopChar) - return nil, err - } - } - // we are done with flags, we give back the next cooming charater to the main loop - index = loopIndex - default: - err := fmt.Errorf("flag %s not part of tcp flags", myCmdChar) +func flowSpecFragmentParser(_ RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + // args: List of pairs of Operator and Fragment flags + // + // Example: + // - is-fragment or last-fragment + // args := []string{"==is-fragment", "==last-fragment"} + // - is-fragment and last-fragment (exact match) + // args := []string{"==is-fragment+last-fragment"} + args = normalizeFlowSpecOpValues(args) + + 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) + 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 != "" { + return nil, fmt.Errorf("invalid argument for %s: %s in %q", typ.String(), arg, args) + } + operand, err := parseFlowSpecBitmaskOperand(m) + if err != nil { return nil, err } + value := 0 + // Example: + // m[3] = "first-fragment+last-fragment" + for flag, name := range FragmentFlagNameMap { + if strings.Contains(m[3], name) { + value |= int(flag) + } + } + items = append(items, NewFlowSpecComponentItem(operand, value)) + } + + // Marks end-of-list bit + items[argsLen-1].Op |= BITMASK_FLAG_OP_END + + return NewFlowSpecComponent(typ, items), nil +} + +func flowSpecLabelParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + afi, _ := RouteFamilyToAfiSafi(rf) + if afi == AFI_IP { + return nil, fmt.Errorf("%s is not supported for ipv4", typ.String()) + } + + args = normalizeFlowSpecOpValues(args) + + f := func(i int) error { + if 0 <= i && i <= 0xfffff { // 20 bits + return nil + } + return fmt.Errorf("flow label range exceeded") } - operatorValue[0] |= int(BitmaskFlagOpValueMap["E"]) - tcpOperatorsFlagsValues = append(tcpOperatorsFlagsValues, operatorValue) - return tcpOperatorsFlagsValues, nil + + return parseFlowSpecNumericOpValues(typ, args, f) } -func flowSpecEtherTypeParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { - if len(args) < 2 || args[0] != FlowSpecNameMap[FLOW_SPEC_TYPE_ETHERNET_TYPE] { - return nil, fmt.Errorf("invalid ethernet-type format") +func flowSpecEtherTypeParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + // args: List of pairs of Operator and Ether Types + // + // Example: + // - ARP or IPv4 + // args := []string{"==arp", "==ipv4"} + // - Not IPv4 and not IPv6 + // args := []string{"!=ipv4", "&!=ipv6"} + if rf != RF_FS_L2_VPN { + return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String()) } + + args = normalizeFlowSpecOpValues(args) s := strings.Join(args, " ") for i, name := range EthernetTypeNameMap { s = strings.Replace(s, name, fmt.Sprintf("%d", i), -1) } args = strings.Split(s, " ") - validationFunc := func(i int) error { - if 0 < i && i < 0xffff { + + f := func(i int) error { + if 0 <= i && i <= 0xffff { // 2 bytes return nil } - return fmt.Errorf("ethernet type range exceeded") + return fmt.Errorf("%s range exceeded", typ.String()) } - return doFlowSpecNumericParser(0, args, validationFunc) + + return parseFlowSpecNumericOpValues(typ, args, f) } -func doFlowSpecNumericParser(rf RouteFamily, args []string, validationFunc func(int) error) (FlowSpecComponentInterface, error) { - if afi, _ := RouteFamilyToAfiSafi(rf); afi == AFI_IP && FlowSpecValueMap[args[0]] == FLOW_SPEC_TYPE_LABEL { - return nil, fmt.Errorf("flow label spec is only allowed for ipv6") +func flowSpecMacParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + // args[0]: MAC address + if rf != RF_FS_L2_VPN { + return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String()) } - cmdType := args[0] - args = append(args[:0], args[1:]...) // removing command string - fullCmd := strings.Join(args, " ") // rebuiling tcp filters - opsFlags, err := parseDecValuesCmd(fullCmd, validationFunc) + + mac, err := net.ParseMAC(args[0]) if err != nil { - return nil, err + return nil, fmt.Errorf("invalid mac address: %s", args[0]) } - items := make([]*FlowSpecComponentItem, 0) - for _, opFlag := range opsFlags { - items = append(items, NewFlowSpecComponentItem(opFlag[0], opFlag[1])) - } - return NewFlowSpecComponent(FlowSpecValueMap[cmdType], items), nil -} - -func parseDecValuesCmd(myCmd string, validationFunc func(int) error) ([][2]int, error) { - var index int = 0 - var decOperatorsAndValues [][2]int - var operatorValue [2]int - var errorNum error - for index < len(myCmd) { - myCmdChar := myCmd[index : index+1] - switch myCmdChar { - case DECNumOpNameMap[DEC_NUM_OP_GT], DECNumOpNameMap[DEC_NUM_OP_LT]: - // We found a < or > let's check if we face >= or <= - if myCmd[index+1:index+2] == "=" { - myCmdChar = myCmd[index : index+2] - index++ - } - if bit := DECNumOpValueMap[myCmdChar]; bit&DECNumOp(operatorValue[0]) == 0 { - operatorValue[0] |= int(bit) - index++ - } else { - err := fmt.Errorf("Operator > < or >= <= appears multiple times") - return nil, err - } - case "!", "=": - // we found the beginning of a not let's check secong character - if myCmd[index+1:index+2] == "=" { - myCmdChar = myCmd[index : index+2] - if bit := DECNumOpValueMap[myCmdChar]; bit&DECNumOp(operatorValue[0]) == 0 { - operatorValue[0] |= int(bit) - index += 2 - } else { - err := fmt.Errorf("Not or Equal operator appears multiple time") - return nil, err - } - } else { - err := fmt.Errorf("Malformed not or equal operator") - return nil, err - } - case "t", "f": // we could be facing true or false, let's check - if myCmd == DECNumOpNameMap[DEC_NUM_OP_FALSE] || myCmd == DECNumOpNameMap[DEC_NUM_OP_TRUE] { - if bit := DECNumOpValueMap[myCmd]; bit&DECNumOp(operatorValue[0]) == 0 { - operatorValue[0] |= int(bit) - index = index + len(myCmd) - } else { - err := fmt.Errorf("Boolean operator appears multiple times") - return nil, err - } - } else { - err := fmt.Errorf("Boolean operator %s badly formatted", myCmd) - return nil, err - } - case DECLogicOpNameMap[DEC_LOGIC_OP_AND], DECLogicOpNameMap[DEC_LOGIC_OP_OR]: - if index == 0 { - err := fmt.Errorf("Logic operator appears a first character") - return nil, err - } - bit := DECLogicOpValueMap[myCmdChar] - decOperatorsAndValues = append(decOperatorsAndValues, operatorValue) - if myCmdChar == DECLogicOpNameMap[DEC_LOGIC_OP_AND] { - operatorValue[0] = int(bit) - } else { - operatorValue[0] = 0 - } - operatorValue[1] = 0 - index++ - case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": - myLoopChar := myCmdChar - loopIndex := index - // we loop till we reach the end of decimal value - // exit conditions : we reach the end of decimal value (we found & or ' ') or we reach the end of the line - for loopIndex < len(myCmd) && - (myLoopChar != DECLogicOpNameMap[DEC_LOGIC_OP_AND] && myLoopChar != DECLogicOpNameMap[DEC_LOGIC_OP_OR]) { - // we check if inspected charater is a number - if _, err := strconv.Atoi(myLoopChar); err == nil { - // we move to next character - loopIndex++ - if loopIndex < len(myCmd) { - myLoopChar = myCmd[loopIndex : loopIndex+1] // we move to the next character only if we didn't reach the end of cmd - } - } else { - err := fmt.Errorf("Decimal value badly formatted: %s", myLoopChar) - return nil, err - } - } - decimalValueString := myCmd[index:loopIndex] - operatorValue[1], errorNum = strconv.Atoi(decimalValueString) - if errorNum != nil { - return nil, errorNum - } - err := validationFunc(operatorValue[1]) - if err != nil { - return nil, err - } - // we check if we found any operator, if not we set default as == - if operatorValue[0] == 0 { - operatorValue[0] = DEC_NUM_OP_EQ - } - // we are done with decimal value, we give back the next cooming charater to the main loop - index = loopIndex - default: - err := fmt.Errorf("%s not part of flowspec decimal value or operators", myCmdChar) - return nil, err - } + + switch typ { + case FLOW_SPEC_TYPE_DST_MAC: + return NewFlowSpecDestinationMac(mac), nil + case FLOW_SPEC_TYPE_SRC_MAC: + return NewFlowSpecSourceMac(mac), nil } - operatorValue[0] |= int(DECLogicOpValueMap["E"]) - decOperatorsAndValues = append(decOperatorsAndValues, operatorValue) - return decOperatorsAndValues, nil + return nil, fmt.Errorf("invalid traffic filtering rule type: %s", typ.String()) } -func flowSpecNumericParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { - f := func(i int) error { - return nil +func flowSpecLlcParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + if rf != RF_FS_L2_VPN { + return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String()) } - return doFlowSpecNumericParser(rf, args, f) + + return flowSpecNumeric1ByteParser(rf, typ, args) } -func flowSpecPortParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { - f := func(i int) error { - if 0 <= i && i < 65536 { - return nil - } - return fmt.Errorf("port range exceeded") +func flowSpecSnapParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + if rf != RF_FS_L2_VPN { + return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String()) } - return doFlowSpecNumericParser(rf, args, f) -} -func flowSpecDscpParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { + args = normalizeFlowSpecOpValues(args) + f := func(i int) error { - if 0 < i && i < 64 { + if 0 <= i && i <= 0xffffffffff { // 5 bytes return nil } - return fmt.Errorf("dscp value range exceeded") + return fmt.Errorf("%s range exceeded", typ.String()) } - return doFlowSpecNumericParser(rf, args, f) + + return parseFlowSpecNumericOpValues(typ, args, f) } -func flowSpecFragmentParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { - if len(args) < 2 { - return nil, fmt.Errorf("invalid flowspec fragment specifier") +func flowSpecVlanIDParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { + if rf != RF_FS_L2_VPN { + return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String()) } - items := make([]*FlowSpecComponentItem, 0) - cmd := strings.Join(args[1:], " ") - var op byte - var flags byte - for cmd != "" { - next := 1 - c := cmd[0:1] - switch c { - case BitmaskFlagOpNameMap[BITMASK_FLAG_OP_MATCH]: - if op&BITMASK_FLAG_OP_MATCH != 0 { - err := fmt.Errorf("invalid flowspec fragment specifier: '=' flag appears multiple time: %s", cmd) - return nil, err - } - op |= BITMASK_FLAG_OP_MATCH - case BitmaskFlagOpNameMap[BITMASK_FLAG_OP_NOT]: - if op&BITMASK_FLAG_OP_NOT != 0 { - err := fmt.Errorf("invalid flowspec fragment specifier: '!' flag appears multiple time: %s", cmd) - return nil, err - } - op = op | BITMASK_FLAG_OP_NOT - case BitmaskFlagOpNameMap[BITMASK_FLAG_OP_AND], BitmaskFlagOpNameMap[BITMASK_FLAG_OP_OR]: - operand := BitmaskFlagOpValueMap[c] - items = append(items, NewFlowSpecComponentItem(int(op), int(flags))) - op = byte(operand) - flags = byte(0) - default: - for k, v := range FragmentFlagNameMap { - length := len(v) - if (len(cmd) >= length) && (cmd[:length] == v) { - flags = flags | byte(k) - next = length - break - } - } - // if not matched with any of FragmentFlags - if next == 1 { - return nil, fmt.Errorf("invalid flowspec fragment specifier: %s", cmd) - } + + args = normalizeFlowSpecOpValues(args) + s := strings.Join(args, " ") + for i, name := range EthernetTypeNameMap { + s = strings.Replace(s, name, fmt.Sprintf("%d", i), -1) + } + args = strings.Split(s, " ") + + f := func(i int) error { + if 0 <= i && i <= 4095 { // 12 bits + return nil } - cmd = cmd[next:] + return fmt.Errorf("%s range exceeded", typ.String()) } - op = op | BITMASK_FLAG_OP_END - items = append(items, NewFlowSpecComponentItem(int(op), int(flags))) - return NewFlowSpecComponent(FlowSpecValueMap[args[0]], items), nil + + return parseFlowSpecNumericOpValues(typ, args, f) } -func flowSpecMacParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { - if len(args) < 2 { - return nil, fmt.Errorf("invalid flowspec dst/src mac") - } +func flowSpecVlanCosParser(rf RouteFamily, typ BGPFlowSpecType, args []string) (FlowSpecComponentInterface, error) { if rf != RF_FS_L2_VPN { - return nil, fmt.Errorf("invalid family") + return nil, fmt.Errorf("%s is supported for only l2vpn", typ.String()) } - typ := args[0] - mac, err := net.ParseMAC(args[1]) - if err != nil { - return nil, fmt.Errorf("invalid mac") + + args = normalizeFlowSpecOpValues(args) + s := strings.Join(args, " ") + for i, name := range EthernetTypeNameMap { + s = strings.Replace(s, name, fmt.Sprintf("%d", i), -1) } - switch typ { - case FlowSpecNameMap[FLOW_SPEC_TYPE_DST_MAC]: - return NewFlowSpecDestinationMac(mac), nil - case FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_MAC]: - return NewFlowSpecSourceMac(mac), nil + args = strings.Split(s, " ") + + f := func(i int) error { + if 0 <= i && i <= 7 { // 3 bits + return nil + } + return fmt.Errorf("%s range exceeded", typ.String()) } - return nil, fmt.Errorf("invalid type. only %s or %s allowed", FlowSpecNameMap[FLOW_SPEC_TYPE_DST_MAC], FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_MAC]) + + return parseFlowSpecNumericOpValues(typ, args, f) } -var flowSpecParserMap = map[BGPFlowSpecType]func(RouteFamily, []string) (FlowSpecComponentInterface, error){ +var flowSpecParserMap = map[BGPFlowSpecType]func(RouteFamily, BGPFlowSpecType, []string) (FlowSpecComponentInterface, error){ FLOW_SPEC_TYPE_DST_PREFIX: flowSpecPrefixParser, FLOW_SPEC_TYPE_SRC_PREFIX: flowSpecPrefixParser, FLOW_SPEC_TYPE_IP_PROTO: flowSpecIpProtoParser, - FLOW_SPEC_TYPE_PORT: flowSpecPortParser, - FLOW_SPEC_TYPE_DST_PORT: flowSpecPortParser, - FLOW_SPEC_TYPE_SRC_PORT: flowSpecPortParser, - FLOW_SPEC_TYPE_ICMP_TYPE: flowSpecNumericParser, - FLOW_SPEC_TYPE_ICMP_CODE: flowSpecNumericParser, + FLOW_SPEC_TYPE_PORT: flowSpecNumeric2BytesParser, + FLOW_SPEC_TYPE_DST_PORT: flowSpecNumeric2BytesParser, + FLOW_SPEC_TYPE_SRC_PORT: flowSpecNumeric2BytesParser, + FLOW_SPEC_TYPE_ICMP_TYPE: flowSpecNumeric1ByteParser, + FLOW_SPEC_TYPE_ICMP_CODE: flowSpecNumeric1ByteParser, FLOW_SPEC_TYPE_TCP_FLAG: flowSpecTcpFlagParser, - FLOW_SPEC_TYPE_PKT_LEN: flowSpecNumericParser, + FLOW_SPEC_TYPE_PKT_LEN: flowSpecNumeric2BytesParser, FLOW_SPEC_TYPE_DSCP: flowSpecDscpParser, FLOW_SPEC_TYPE_FRAGMENT: flowSpecFragmentParser, - FLOW_SPEC_TYPE_LABEL: flowSpecNumericParser, + FLOW_SPEC_TYPE_LABEL: flowSpecLabelParser, FLOW_SPEC_TYPE_ETHERNET_TYPE: flowSpecEtherTypeParser, FLOW_SPEC_TYPE_DST_MAC: flowSpecMacParser, FLOW_SPEC_TYPE_SRC_MAC: flowSpecMacParser, - FLOW_SPEC_TYPE_LLC_DSAP: flowSpecNumericParser, - FLOW_SPEC_TYPE_LLC_SSAP: flowSpecNumericParser, - FLOW_SPEC_TYPE_LLC_CONTROL: flowSpecNumericParser, - FLOW_SPEC_TYPE_SNAP: flowSpecNumericParser, - FLOW_SPEC_TYPE_VID: flowSpecNumericParser, - FLOW_SPEC_TYPE_COS: flowSpecNumericParser, - FLOW_SPEC_TYPE_INNER_VID: flowSpecNumericParser, - FLOW_SPEC_TYPE_INNER_COS: flowSpecNumericParser, + FLOW_SPEC_TYPE_LLC_DSAP: flowSpecLlcParser, + FLOW_SPEC_TYPE_LLC_SSAP: flowSpecLlcParser, + FLOW_SPEC_TYPE_LLC_CONTROL: flowSpecLlcParser, + FLOW_SPEC_TYPE_SNAP: flowSpecSnapParser, + FLOW_SPEC_TYPE_VID: flowSpecVlanIDParser, + FLOW_SPEC_TYPE_COS: flowSpecVlanCosParser, + FLOW_SPEC_TYPE_INNER_VID: flowSpecVlanIDParser, + FLOW_SPEC_TYPE_INNER_COS: flowSpecVlanCosParser, } func extractFlowSpecArgs(args []string) map[BGPFlowSpecType][]string { diff --git a/packet/bgp/constant.go b/packet/bgp/constant.go index f743e331..d4120471 100644 --- a/packet/bgp/constant.go +++ b/packet/bgp/constant.go @@ -145,6 +145,21 @@ var BitmaskFlagOpNameMap = map[BitmaskFlagOp]string{ BITMASK_FLAG_OP_MATCH: "=", } +// Note: Meaning of "" is different from that of the numeric operator because +// RFC5575 says if the Match bit in the bitmask operand is set, it should be +// "strictly" matching against the given value. +var BitmaskFlagOpValueMap = map[string]BitmaskFlagOp{ + " ": BITMASK_FLAG_OP_OR, + "": BITMASK_FLAG_OP_OR, + "==": BITMASK_FLAG_OP_MATCH, + "=": BITMASK_FLAG_OP_MATCH, + "!": BITMASK_FLAG_OP_NOT, + "!=": BITMASK_FLAG_OP_NOT_MATCH, + "=!": BITMASK_FLAG_OP_NOT_MATCH, // For the backward compatibility + "&": BITMASK_FLAG_OP_AND, + "E": BITMASK_FLAG_OP_END, +} + func (f BitmaskFlagOp) String() string { ops := make([]string, 0) if f&BITMASK_FLAG_OP_AND > 0 { @@ -230,6 +245,24 @@ var DECNumOpNameMap = map[DECNumOp]string{ DEC_NUM_OP_END: "E", } +var DECNumOpValueMap = map[string]DECNumOp{ + "true": DEC_NUM_OP_TRUE, + "": DEC_NUM_OP_EQ, + "==": DEC_NUM_OP_EQ, + "=": DEC_NUM_OP_EQ, + ">": DEC_NUM_OP_GT, + ">=": DEC_NUM_OP_GT_EQ, + "<": DEC_NUM_OP_LT, + "<=": DEC_NUM_OP_LT_EQ, + "!=": DEC_NUM_OP_NOT_EQ, + "=!": DEC_NUM_OP_NOT_EQ, + "!": DEC_NUM_OP_NOT_EQ, + "false": DEC_NUM_OP_FALSE, + " ": DEC_NUM_OP_OR, + "&": DEC_NUM_OP_AND, + "E": DEC_NUM_OP_END, +} + func (f DECNumOp) String() string { ops := make([]string, 0) logicFlag := DECNumOp(f & 0xc0) // higher 2 bits |