diff options
-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 |