diff options
author | ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp> | 2015-08-10 01:25:18 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2015-08-12 08:04:31 +0900 |
commit | 5c066cc6a64b03126a737fb41954a62bae762806 (patch) | |
tree | 9c7c36561a75f3f4948bfd5ab1ae06a289a42c06 | |
parent | 163a9338fb13b10872889406079be95322cd0f46 (diff) |
bgp/cli: support flowspec (RFC5575)
draft-ietf-idr-flowspec-redirect-rt-bis-05 is also implmented.
TODO: draft-ietf-idr-flow-spec-v6-06
Signed-off-by: ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>
-rw-r--r-- | config/default.go | 2 | ||||
-rw-r--r-- | gobgp/common.go | 2 | ||||
-rw-r--r-- | gobgp/global.go | 428 | ||||
-rw-r--r-- | gobgp/main.go | 1 | ||||
-rw-r--r-- | packet/bgp.go | 1036 | ||||
-rw-r--r-- | packet/bgp_test.go | 77 | ||||
-rw-r--r-- | packet/constant.go | 102 | ||||
-rw-r--r-- | packet/routefamily_string.go | 11 | ||||
-rw-r--r-- | server/fsm_test.go | 2 |
9 files changed, 1509 insertions, 152 deletions
diff --git a/config/default.go b/config/default.go index cdb37c32..be6a8cf7 100644 --- a/config/default.go +++ b/config/default.go @@ -38,6 +38,8 @@ func SetDefaultConfigValues(md toml.MetaData, bt *Bgp) error { AfiSafi{AfiSafiName: "l2vpn-evpn"}, AfiSafi{AfiSafiName: "encap"}, AfiSafi{AfiSafiName: "rtc"}, + AfiSafi{AfiSafiName: "ipv4-flowspec"}, + AfiSafi{AfiSafiName: "l3vpn-ipnv4-flowspec"}, } } diff --git a/gobgp/common.go b/gobgp/common.go index 9ea7b25a..31e0802a 100644 --- a/gobgp/common.go +++ b/gobgp/common.go @@ -410,6 +410,8 @@ func checkAddressFamily(ip net.IP) (bgp.RouteFamily, error) { rf = bgp.RF_ENCAP case "rtc": rf = bgp.RF_RTC_UC + case "ipv4-flowspec", "ipv4-flow", "flow4": + rf = bgp.RF_FS_IPv4_UC case "": if len(ip) == 0 || ip.To4() != nil { rf = bgp.RF_IPv4_UC diff --git a/gobgp/global.go b/gobgp/global.go index 60e32355..6da0251b 100644 --- a/gobgp/global.go +++ b/gobgp/global.go @@ -23,23 +23,323 @@ import ( "golang.org/x/net/context" "net" "os" + "regexp" "strconv" + "strings" ) func showGlobalRib(args []string) error { return showNeighborRib(CMD_GLOBAL, "", args) } -func getSerizliedRouteTarget(args []string) ([]byte, error) { - rts := make([]bgp.ExtendedCommunityInterface, 0, len(args)) - for _, elem := range args { - rt, err := bgp.ParseRouteTarget(elem) +type ExtCommType int + +const ( + ACCEPT ExtCommType = iota + DISCARD + RATE + REDIRECT + MARK + ACTION + RT +) + +var ExtCommNameMap = map[ExtCommType]string{ + ACCEPT: "accept", + DISCARD: "discard", + RATE: "rate-limit", + REDIRECT: "redirect", + MARK: "mark", + ACTION: "action", + RT: "rt", +} + +var ExtCommValueMap = map[string]ExtCommType{ + ExtCommNameMap[ACCEPT]: ACCEPT, + ExtCommNameMap[DISCARD]: DISCARD, + ExtCommNameMap[RATE]: RATE, + ExtCommNameMap[REDIRECT]: REDIRECT, + ExtCommNameMap[MARK]: MARK, + ExtCommNameMap[ACTION]: ACTION, + ExtCommNameMap[RT]: RT, +} + +func rateLimitParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { + exp := regexp.MustCompile(fmt.Sprintf("^(%s|(%s) (\\d+)(\\.(\\d+))?)( as (\\d+))?$", ExtCommNameMap[DISCARD], ExtCommNameMap[RATE])) + elems := exp.FindStringSubmatch(strings.Join(args, " ")) + if len(elems) != 8 { + return nil, fmt.Errorf("invalid rate-limit") + } + var rate float32 + var as int + if elems[2] == ExtCommNameMap[RATE] { + f, err := strconv.ParseFloat(elems[3]+elems[4], 32) if err != nil { return nil, err } - rts = append(rts, rt) + rate = float32(f) + } + if elems[7] != "" { + var err error + as, err = strconv.Atoi(elems[7]) + if err != nil { + return nil, err + } + } + return []bgp.ExtendedCommunityInterface{bgp.NewTrafficRateExtended(uint16(as), rate)}, nil +} + +func redirectParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { + if len(args) < 2 || args[0] != ExtCommNameMap[REDIRECT] { + return nil, fmt.Errorf("invalid redirect") + } + rt, err := bgp.ParseRouteTarget(strings.Join(args[1:], " ")) + if err != nil { + return nil, err + } + t, _ := rt.GetTypes() + switch t { + case bgp.EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC: + r := rt.(*bgp.TwoOctetAsSpecificExtended) + return []bgp.ExtendedCommunityInterface{bgp.NewRedirectTwoOctetAsSpecificExtended(r.AS, r.LocalAdmin)}, nil + case bgp.EC_TYPE_TRANSITIVE_IP4_SPECIFIC: + r := rt.(*bgp.IPv4AddressSpecificExtended) + return []bgp.ExtendedCommunityInterface{bgp.NewRedirectIPv4AddressSpecificExtended(r.IPv4.String(), r.LocalAdmin)}, nil + case bgp.EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC: + r := rt.(*bgp.FourOctetAsSpecificExtended) + return []bgp.ExtendedCommunityInterface{bgp.NewRedirectFourOctetAsSpecificExtended(r.AS, r.LocalAdmin)}, nil + } + return nil, fmt.Errorf("invalid redirect") +} + +func markParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { + if len(args) < 2 || args[0] != ExtCommNameMap[MARK] { + return nil, fmt.Errorf("invalid mark") + } + dscp, err := strconv.Atoi(args[1]) + if err != nil { + return nil, fmt.Errorf("invalid mark") + } + return []bgp.ExtendedCommunityInterface{bgp.NewTrafficRemarkExtended(uint8(dscp))}, nil +} + +func actionParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { + if len(args) < 2 || args[0] != ExtCommNameMap[ACTION] { + return nil, fmt.Errorf("invalid action") + } + sample := false + terminal := false + switch args[1] { + case "sample": + sample = true + case "terminal": + terminal = true + case "terminal-sample", "sample-terminal": + sample = true + terminal = true + default: + return nil, fmt.Errorf("invalid action") + } + return []bgp.ExtendedCommunityInterface{bgp.NewTrafficActionExtended(terminal, sample)}, nil +} + +func rtParser(args []string) ([]bgp.ExtendedCommunityInterface, error) { + if len(args) < 2 || args[0] != ExtCommNameMap[RT] { + return nil, fmt.Errorf("invalid rt") + } + exts := make([]bgp.ExtendedCommunityInterface, 0, len(args[1:])) + for _, arg := range args[1:] { + rt, err := bgp.ParseRouteTarget(arg) + if err != nil { + return nil, err + } + exts = append(exts, rt) + } + return exts, nil +} + +var ExtCommParserMap = map[ExtCommType]func([]string) ([]bgp.ExtendedCommunityInterface, error){ + ACCEPT: nil, + DISCARD: rateLimitParser, + RATE: rateLimitParser, + REDIRECT: redirectParser, + MARK: markParser, + ACTION: actionParser, + RT: rtParser, +} + +func ParseExtendedCommunities(input string) ([]bgp.ExtendedCommunityInterface, error) { + idxs := make([]struct { + t ExtCommType + i int + }, 0, len(ExtCommNameMap)) + args := strings.Split(input, " ") + for idx, v := range args { + if t, ok := ExtCommValueMap[v]; ok { + idxs = append(idxs, struct { + t ExtCommType + i int + }{t, idx}) + } + } + exts := make([]bgp.ExtendedCommunityInterface, 0, len(idxs)) + for i, idx := range idxs { + var a []string + f := ExtCommParserMap[idx.t] + if f == nil { + continue + } + if i < len(idxs)-1 { + a = args[idx.i:idxs[i+1].i] + } else { + a = args[idx.i:] + } + ext, err := f(a) + if err != nil { + return nil, err + } + exts = append(exts, ext...) + } + return exts, nil +} + +func parseFlowSpecArgs(modtype string, args []string) (bgp.AddrPrefixInterface, string, []string, error) { + thenPos := len(args) + for idx, v := range args { + if v == "then" { + thenPos = idx + } + } + ss := make([]string, 0, len(bgp.ProtocolNameMap)) + for _, v := range bgp.ProtocolNameMap { + ss = append(ss, v) + } + protos := strings.Join(ss, ", ") + ss = make([]string, 0, len(bgp.TCPFlagNameMap)) + for _, v := range bgp.TCPFlagNameMap { + ss = append(ss, v) + } + flags := strings.Join(ss, ", ") + helpErr := fmt.Errorf(`usage: global rib %s match <MATCH_EXPR> then <THEN_EXPR> -a ipv4-flowspec + <MATCH_EXPR> : { dst <PREFIX> | src <PREFIX> | proto <PROTO>... | fragment <FRAGMENT_TYPE> | tcp-flags <TCPFLAG>... | { port | dst-port | src-port | icmp-type | icmp-code | pkt-len | dscp } <ITEM>... }... + <PROTO> : %s + <FRAGMENT_TYPE> : not-a-fragment, is-a-fragment, first-fragment, last-fragment + <TCPFLAG> : %s + <ITEM> : &?{<|>|=}<value> + <THEN_EXPR> : { accept | discard | rate-limit <value> | redirect <RT> | mark <value> | action { sample | terminal | sample-terminal }}... + <RT> : xxx:yyy, xx.xx.xx.xx:yyy, xxx.xxx:yyy`, modtype, protos, flags) + + if len(args) < 4 || args[0] != "match" || thenPos > len(args)-2 { + return nil, "", nil, helpErr + } + matchArgs := args[1:thenPos] + cmp, err := bgp.ParseFlowSpecComponents(strings.Join(matchArgs, " ")) + if err != nil { + return nil, "", nil, fmt.Errorf("%s\n%s", err, helpErr) + } + nlri := bgp.NewFlowSpecIPv4Unicast(cmp) + return nlri, "0.0.0.0", args[thenPos:], nil +} +func parseEvpnArgs(modtype string, args []string) (bgp.AddrPrefixInterface, string, []string, error) { + if len(args) < 1 { + return nil, "", nil, fmt.Errorf("usage: global rib %s { macadv | multicast } ... -a evpn", modtype) + } + subtype := args[0] + args = args[1:] + + var nlri bgp.AddrPrefixInterface + var rts []string + + switch subtype { + case "macadv": + if len(args) < 6 || args[4] != "rd" || args[6] != "rt" { + return nil, "", nil, fmt.Errorf("usage: global rib %s macadv <mac address> <ip address> <etag> <label> rd <rd> rt <rt>... -a evpn", modtype) + } + mac, err := net.ParseMAC(args[0]) + if err != nil { + return nil, "", nil, fmt.Errorf("invalid mac: %s", args[0]) + } + var ip net.IP + iplen := 0 + if args[1] != "0.0.0.0" || args[1] != "::" { + ip = net.ParseIP(args[1]) + if ip == nil { + return nil, "", nil, fmt.Errorf("invalid ip prefix: %s", args[1]) + } + iplen = net.IPv4len * 8 + if ip.To4() == nil { + iplen = net.IPv6len * 8 + } + } + eTag, err := strconv.Atoi(args[2]) + if err != nil { + return nil, "", nil, fmt.Errorf("invalid eTag: %s. err: %s", args[2], err) + } + label, err := strconv.Atoi(args[3]) + if err != nil { + return nil, "", nil, fmt.Errorf("invalid label: %s. err: %s", args[3], err) + } + rd, err := bgp.ParseRouteDistinguisher(args[5]) + if err != nil { + return nil, "", nil, err + } + + rts = args[6:] + + macIpAdv := &bgp.EVPNMacIPAdvertisementRoute{ + RD: rd, + ESI: bgp.EthernetSegmentIdentifier{ + Type: bgp.ESI_ARBITRARY, + }, + MacAddressLength: 48, + MacAddress: mac, + IPAddressLength: uint8(iplen), + IPAddress: ip, + Labels: []uint32{uint32(label)}, + ETag: uint32(eTag), + } + nlri = bgp.NewEVPNNLRI(bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT, 0, macIpAdv) + case "multicast": + if len(args) < 5 || args[2] != "rd" || args[4] != "rt" { + return nil, "", nil, fmt.Errorf("usage : global rib %s multicast <ip address> <etag> rd <rd> rt <rt> -a evpn", modtype) + } + + var ip net.IP + iplen := 0 + if args[0] != "0.0.0.0" || args[0] != "::" { + ip = net.ParseIP(args[0]) + if ip == nil { + return nil, "", nil, fmt.Errorf("invalid ip prefix: %s", args[0]) + } + iplen = net.IPv4len * 8 + if ip.To4() == nil { + iplen = net.IPv6len * 8 + } + } + + eTag, err := strconv.Atoi(args[1]) + if err != nil { + return nil, "", nil, fmt.Errorf("invalid eTag: %s. err: %s", args[1], err) + } + + rd, err := bgp.ParseRouteDistinguisher(args[3]) + if err != nil { + return nil, "", nil, err + } + + rts = args[4:] + + multicastEtag := &bgp.EVPNMulticastEthernetTagRoute{ + RD: rd, + IPAddressLength: uint8(iplen), + IPAddress: ip, + ETag: uint32(eTag), + } + nlri = bgp.NewEVPNNLRI(bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG, 0, multicastEtag) + default: + return nil, "", nil, fmt.Errorf("usage: global rib add { macadv | multicast | ... -a evpn") } - return bgp.NewPathAttributeExtendedCommunities(rts).Serialize() + return nlri, "0.0.0.0", rts, nil } func modPath(modtype string, args []string) error { @@ -50,7 +350,7 @@ func modPath(modtype string, args []string) error { var nlri bgp.AddrPrefixInterface var nexthop string - var rts []string + var extcomms []string switch rf { case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC: @@ -85,7 +385,7 @@ func modPath(modtype string, args []string) error { return err } - rts = args[4:] + extcomms = args[3:] mpls := bgp.NewMPLSLabelStack() @@ -104,102 +404,15 @@ func modPath(modtype string, args []string) error { } case bgp.RF_EVPN: - if len(args) < 1 { - return fmt.Errorf("usage: global rib %s { macadv | multicast } ... -a evpn", modtype) + nlri, nexthop, extcomms, err = parseEvpnArgs(modtype, args) + if err != nil { + return err } - subtype := args[0] - args = args[1:] - - switch subtype { - case "macadv": - if len(args) < 6 || args[4] != "rd" || args[6] != "rt" { - return fmt.Errorf("usage: global rib %s macadv <mac address> <ip address> <etag> <label> rd <rd> rt <rt>... -a evpn", modtype) - } - mac, err := net.ParseMAC(args[0]) - if err != nil { - return fmt.Errorf("invalid mac: %s", args[0]) - } - var ip net.IP - iplen := 0 - if args[1] != "0.0.0.0" || args[1] != "::" { - ip = net.ParseIP(args[1]) - if ip == nil { - return fmt.Errorf("invalid ip prefix: %s", args[1]) - } - iplen = net.IPv4len * 8 - if ip.To4() == nil { - iplen = net.IPv6len * 8 - } - } - eTag, err := strconv.Atoi(args[2]) - if err != nil { - return fmt.Errorf("invalid eTag: %s. err: %s", args[2], err) - } - label, err := strconv.Atoi(args[3]) - if err != nil { - return fmt.Errorf("invalid label: %s. err: %s", args[3], err) - } - rd, err := bgp.ParseRouteDistinguisher(args[5]) - if err != nil { - return err - } - - rts = args[7:] - - macIpAdv := &bgp.EVPNMacIPAdvertisementRoute{ - RD: rd, - ESI: bgp.EthernetSegmentIdentifier{ - Type: bgp.ESI_ARBITRARY, - }, - MacAddressLength: 48, - MacAddress: mac, - IPAddressLength: uint8(iplen), - IPAddress: ip, - Labels: []uint32{uint32(label)}, - ETag: uint32(eTag), - } - nlri = bgp.NewEVPNNLRI(bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT, 0, macIpAdv) - case "multicast": - if len(args) < 5 || args[2] != "rd" || args[4] != "rt" { - return fmt.Errorf("usage : global rib %s multicast <ip address> <etag> rd <rd> rt <rt> -a evpn", modtype) - } - - var ip net.IP - iplen := 0 - if args[0] != "0.0.0.0" || args[0] != "::" { - ip = net.ParseIP(args[0]) - if ip == nil { - return fmt.Errorf("invalid ip prefix: %s", args[0]) - } - iplen = net.IPv4len * 8 - if ip.To4() == nil { - iplen = net.IPv6len * 8 - } - } - - eTag, err := strconv.Atoi(args[1]) - if err != nil { - return fmt.Errorf("invalid eTag: %s. err: %s", args[1], err) - } - - rd, err := bgp.ParseRouteDistinguisher(args[3]) - if err != nil { - return err - } - - rts = args[5:] - - multicastEtag := &bgp.EVPNMulticastEthernetTagRoute{ - RD: rd, - IPAddressLength: uint8(iplen), - IPAddress: ip, - ETag: uint32(eTag), - } - nlri = bgp.NewEVPNNLRI(bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG, 0, multicastEtag) - default: - return fmt.Errorf("usage: global rib add { macadv | multicast | ... -a evpn") + case bgp.RF_FS_IPv4_UC: + nlri, nexthop, extcomms, err = parseFlowSpecArgs(modtype, args) + if err != nil { + return err } - nexthop = "0.0.0.0" default: return fmt.Errorf("Unsupported route family: %s", rf) } @@ -228,12 +441,17 @@ func modPath(modtype string, args []string) error { origin, _ := bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP).Serialize() arg.RawPattrs = append(arg.RawPattrs, origin) - if rts != nil && len(rts) > 0 { - extcomm, err := getSerizliedRouteTarget(rts) + if extcomms != nil && len(extcomms) > 0 { + extcomms, err := ParseExtendedCommunities(strings.Join(extcomms, " ")) + if err != nil { + return err + } + p := bgp.NewPathAttributeExtendedCommunities(extcomms) + buf, err := p.Serialize() if err != nil { return err } - arg.RawPattrs = append(arg.RawPattrs, extcomm) + arg.RawPattrs = append(arg.RawPattrs, buf) } stream, err := client.ModPath(context.Background()) diff --git a/gobgp/main.go b/gobgp/main.go index 1d4463b1..feaf3aba 100644 --- a/gobgp/main.go +++ b/gobgp/main.go @@ -34,6 +34,7 @@ var cmds []string var client api.GrpcClient func main() { + cobra.EnablePrefixMatching = true rootCmd := &cobra.Command{ Use: "gobgp", PersistentPreRun: func(cmd *cobra.Command, args []string) { diff --git a/packet/bgp.go b/packet/bgp.go index 2f246951..5f1723ba 100644 --- a/packet/bgp.go +++ b/packet/bgp.go @@ -45,6 +45,8 @@ const ( SAFI_MPLS_VPN = 128 SAFI_MPLS_VPN_MULTICAST = 129 SAFI_ROUTE_TARGET_CONSTRTAINS = 132 + SAFI_FLOW_SPEC_UNICAST = 133 + SAFI_FLOW_SPEC_VPN = 134 ) const ( @@ -85,6 +87,9 @@ const ( EC_TYPE_NON_TRANSITIVE_OPAQUE ExtendedCommunityAttrType = 0x43 EC_TYPE_NON_TRANSITIVE_QOS_MARKING ExtendedCommunityAttrType = 0x44 EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL ExtendedCommunityAttrType = 0x80 + //draft-ietf-idr-flowspec-redirect-rt-bis-05 + EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2 ExtendedCommunityAttrType = 0x81 + EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3 ExtendedCommunityAttrType = 0x82 ) // RFC7153 5.2. Registraction for the "Sub-Type" Field @@ -1739,6 +1744,678 @@ func NewEncapNLRI(endpoint string) *EncapNLRI { } } +type BGPFlowSpecType uint8 + +const ( + FLOW_SPEC_TYPE_UNKNOWN BGPFlowSpecType = iota + FLOW_SPEC_TYPE_DST_PREFIX + FLOW_SPEC_TYPE_SRC_PREFIX + FLOW_SPEC_TYPE_IP_PROTO + FLOW_SPEC_TYPE_PORT + FLOW_SPEC_TYPE_DST_PORT + FLOW_SPEC_TYPE_SRC_PORT + FLOW_SPEC_TYPE_ICMP_TYPE + FLOW_SPEC_TYPE_ICMP_CODE + FLOW_SPEC_TYPE_TCP_FLAG + FLOW_SPEC_TYPE_PKT_LEN + FLOW_SPEC_TYPE_DSCP + FLOW_SPEC_TYPE_FRAGMENT +) + +var FlowSpecNameMap = map[BGPFlowSpecType]string{ + FLOW_SPEC_TYPE_UNKNOWN: "unknown", + FLOW_SPEC_TYPE_DST_PREFIX: "destination", + FLOW_SPEC_TYPE_SRC_PREFIX: "source", + FLOW_SPEC_TYPE_IP_PROTO: "protocol", + FLOW_SPEC_TYPE_PORT: "port", + FLOW_SPEC_TYPE_DST_PORT: "destination-port", + FLOW_SPEC_TYPE_SRC_PORT: "source-port", + FLOW_SPEC_TYPE_ICMP_TYPE: "icmp-type", + FLOW_SPEC_TYPE_ICMP_CODE: "icmp-code", + FLOW_SPEC_TYPE_TCP_FLAG: "tcp-flags", + FLOW_SPEC_TYPE_PKT_LEN: "packet-length", + FLOW_SPEC_TYPE_DSCP: "dscp", + FLOW_SPEC_TYPE_FRAGMENT: "fragment", +} + +var FlowSpecValueMap = map[string]BGPFlowSpecType{ + FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PREFIX]: FLOW_SPEC_TYPE_DST_PREFIX, + FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PREFIX]: FLOW_SPEC_TYPE_SRC_PREFIX, + FlowSpecNameMap[FLOW_SPEC_TYPE_IP_PROTO]: FLOW_SPEC_TYPE_IP_PROTO, + FlowSpecNameMap[FLOW_SPEC_TYPE_PORT]: FLOW_SPEC_TYPE_PORT, + FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PORT]: FLOW_SPEC_TYPE_DST_PORT, + FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PORT]: FLOW_SPEC_TYPE_SRC_PORT, + FlowSpecNameMap[FLOW_SPEC_TYPE_ICMP_TYPE]: FLOW_SPEC_TYPE_ICMP_TYPE, + FlowSpecNameMap[FLOW_SPEC_TYPE_ICMP_CODE]: FLOW_SPEC_TYPE_ICMP_CODE, + FlowSpecNameMap[FLOW_SPEC_TYPE_TCP_FLAG]: FLOW_SPEC_TYPE_TCP_FLAG, + FlowSpecNameMap[FLOW_SPEC_TYPE_PKT_LEN]: FLOW_SPEC_TYPE_PKT_LEN, + FlowSpecNameMap[FLOW_SPEC_TYPE_DSCP]: FLOW_SPEC_TYPE_DSCP, + FlowSpecNameMap[FLOW_SPEC_TYPE_FRAGMENT]: FLOW_SPEC_TYPE_FRAGMENT, +} + +func flowSpecPrefixParser(args []string) (FlowSpecComponentInterface, error) { + if len(args) < 2 { + return nil, fmt.Errorf("invalid flowspec dst/src prefix") + } + typ := args[0] + ip, net, _ := net.ParseCIDR(args[1]) + if ip.To4() == nil { + return nil, fmt.Errorf("invalid ipv4 prefix") + } + ones, _ := net.Mask.Size() + + switch typ { + case FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PREFIX]: + return NewFlowSpecDestinationPrefix(NewIPAddrPrefix(uint8(ones), ip.String())), nil + case FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PREFIX]: + return NewFlowSpecSourcePrefix(NewIPAddrPrefix(uint8(ones), ip.String())), nil + } + return nil, fmt.Errorf("invalid type. only destination or source is allowed") +} + +func flowSpecIpProtoParser(args []string) (FlowSpecComponentInterface, error) { + ss := make([]string, 0, len(ProtocolNameMap)) + for _, v := range ProtocolNameMap { + ss = append(ss, v) + } + protos := strings.Join(ss, "|") + exp := regexp.MustCompile(fmt.Sprintf("^%s (((%s) )*)(%s)$", FlowSpecNameMap[FLOW_SPEC_TYPE_IP_PROTO], protos, protos)) + elems := exp.FindStringSubmatch(strings.Join(args, " ")) + items := make([]*FlowSpecComponentItem, 0) + eq := 0x1 + if elems[1] != "" { + for _, v := range strings.Split(elems[1], " ") { + p, ok := ProtocolValueMap[v] + if !ok { + continue + } + items = append(items, NewFlowSpecComponentItem(eq, int(p))) + } + } + items = append(items, NewFlowSpecComponentItem(eq, int(ProtocolValueMap[elems[4]]))) + return NewFlowSpecComponent(FLOW_SPEC_TYPE_IP_PROTO, items), nil +} + +func flowSpecTcpFlagParser(args []string) (FlowSpecComponentInterface, error) { + ss := make([]string, 0, len(TCPFlagNameMap)) + for _, v := range TCPFlagNameMap { + ss = append(ss, v) + } + protos := strings.Join(ss, "|") + exp := regexp.MustCompile(fmt.Sprintf("^%s (((%s) )*)(%s)$", FlowSpecNameMap[FLOW_SPEC_TYPE_TCP_FLAG], protos, protos)) + elems := exp.FindStringSubmatch(strings.Join(args, " ")) + items := make([]*FlowSpecComponentItem, 0) + if elems[1] != "" { + for _, v := range strings.Split(elems[1], " ") { + p, ok := TCPFlagValueMap[v] + if !ok { + continue + } + items = append(items, NewFlowSpecComponentItem(0, int(p))) + } + } + items = append(items, NewFlowSpecComponentItem(0, int(TCPFlagValueMap[elems[4]]))) + return NewFlowSpecComponent(FLOW_SPEC_TYPE_TCP_FLAG, items), nil +} + +func flowSpecNumericParser(args []string) (FlowSpecComponentInterface, error) { + exp := regexp.MustCompile("^(([<>=])(\\d+)&)?([<>=])(\\d+)$") + items := make([]*FlowSpecComponentItem, 0) + + f := func(and bool, o, v string) *FlowSpecComponentItem { + op := 0 + if and { + op |= 0x40 + } + switch o { + case ">": + op |= 0x2 + case "<": + op |= 0x4 + case "=": + op |= 0x1 + } + value, err := strconv.Atoi(v) + if err != nil { + return nil + } + return NewFlowSpecComponentItem(op, value) + } + + for _, arg := range args[1:] { + var and bool + elems := exp.FindStringSubmatch(arg) + if elems[1] != "" { + and = true + items = append(items, f(false, elems[2], elems[3])) + } + items = append(items, f(and, elems[4], elems[5])) + + } + return NewFlowSpecComponent(FlowSpecValueMap[args[0]], items), nil +} + +func flowSpecFragmentParser(args []string) (FlowSpecComponentInterface, error) { + if len(args) < 2 { + return nil, fmt.Errorf("invalid flowspec fragment specifier") + } + value := 0 + switch args[1] { + case "not-a-fragment": + value = 0x1 + case "is-a-fragment": + value = 0x2 + case "first-fragment": + value = 0x4 + case "last-fragment": + value = 0x8 + default: + return nil, fmt.Errorf("invalid flowspec fragment specifier") + } + items := []*FlowSpecComponentItem{NewFlowSpecComponentItem(0, value)} + return NewFlowSpecComponent(FlowSpecValueMap[args[0]], items), nil +} + +var flowSpecParserMap = map[BGPFlowSpecType]func([]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: flowSpecNumericParser, + FLOW_SPEC_TYPE_DST_PORT: flowSpecNumericParser, + FLOW_SPEC_TYPE_SRC_PORT: flowSpecNumericParser, + FLOW_SPEC_TYPE_ICMP_TYPE: flowSpecNumericParser, + FLOW_SPEC_TYPE_ICMP_CODE: flowSpecNumericParser, + FLOW_SPEC_TYPE_TCP_FLAG: flowSpecTcpFlagParser, + FLOW_SPEC_TYPE_PKT_LEN: flowSpecNumericParser, + FLOW_SPEC_TYPE_DSCP: flowSpecNumericParser, + FLOW_SPEC_TYPE_FRAGMENT: flowSpecFragmentParser, +} + +func ParseFlowSpecComponents(input string) ([]FlowSpecComponentInterface, error) { + idxs := make([]struct { + t BGPFlowSpecType + i int + }, 0, 8) + args := strings.Split(input, " ") + for idx, v := range args { + if t, ok := FlowSpecValueMap[v]; ok { + idxs = append(idxs, struct { + t BGPFlowSpecType + i int + }{t, idx}) + } + } + cmps := make([]FlowSpecComponentInterface, 0, len(idxs)) + for i, idx := range idxs { + var a []string + f := flowSpecParserMap[idx.t] + if i < len(idxs)-1 { + a = args[idx.i:idxs[i+1].i] + } else { + a = args[idx.i:] + } + cmp, err := f(a) + if err != nil { + return nil, err + } + cmps = append(cmps, cmp) + } + return cmps, nil +} + +func (t BGPFlowSpecType) String() string { + name, ok := FlowSpecNameMap[t] + if !ok { + return fmt.Sprintf("%s(%d)", FlowSpecNameMap[FLOW_SPEC_TYPE_UNKNOWN], t) + } + return name +} + +type FlowSpecComponentInterface interface { + DecodeFromBytes([]byte) error + Serialize() ([]byte, error) + Len() int + Type() BGPFlowSpecType + String() string +} + +type flowSpecPrefix struct { + Prefix AddrPrefixInterface + type_ BGPFlowSpecType +} + +func (p *flowSpecPrefix) DecodeFromBytes(data []byte) error { + p.type_ = BGPFlowSpecType(data[0]) + return p.Prefix.DecodeFromBytes(data[1:]) +} + +func (p *flowSpecPrefix) Serialize() ([]byte, error) { + buf := []byte{byte(p.Type())} + bbuf, err := p.Prefix.Serialize() + if err != nil { + return nil, err + } + return append(buf, bbuf...), nil +} + +func (p *flowSpecPrefix) Len() int { + l := p.Prefix.Len() + if l < 0xf0 { + return l + 1 + } else { + return l + 2 + } +} + +func (p *flowSpecPrefix) Type() BGPFlowSpecType { + return p.type_ +} + +func (p *flowSpecPrefix) String() string { + return fmt.Sprintf("[%s:%s]", p.Type(), p.Prefix.String()) +} + +type FlowSpecDestinationPrefix struct { + flowSpecPrefix +} + +func NewFlowSpecDestinationPrefix(prefix AddrPrefixInterface) *FlowSpecDestinationPrefix { + return &FlowSpecDestinationPrefix{flowSpecPrefix{prefix, FLOW_SPEC_TYPE_DST_PREFIX}} +} + +type FlowSpecSourcePrefix struct { + flowSpecPrefix +} + +func NewFlowSpecSourcePrefix(prefix AddrPrefixInterface) *FlowSpecSourcePrefix { + return &FlowSpecSourcePrefix{flowSpecPrefix{prefix, FLOW_SPEC_TYPE_SRC_PREFIX}} +} + +type FlowSpecComponentItem struct { + Op int + Value int +} + +func (v *FlowSpecComponentItem) Serialize() ([]byte, error) { + if v.Value < 0 { + return nil, fmt.Errorf("invalid value size(too small): %d", v.Value) + } + if v.Op < 0 || v.Op > math.MaxUint8 { + return nil, fmt.Errorf("invalid op size: %d", v.Op) + } + + for i := 0; i < 3; i++ { + if v.Value < (1 << ((1 << uint(i)) * 8)) { + buf := make([]byte, 1+(1<<uint(i))) + switch i { + case 0: + buf[1] = byte(v.Value) + v.Op &^= 0x30 + case 1: + binary.BigEndian.PutUint16(buf[1:], uint16(v.Value)) + v.Op |= 0x10 + v.Op &^= 0x20 + case 2: + binary.BigEndian.PutUint32(buf[1:], uint32(v.Value)) + v.Op &^= 0x10 + v.Op |= 0x20 + case 3: + binary.BigEndian.PutUint64(buf[1:], uint64(v.Value)) + v.Op |= 0x30 + } + buf[0] = byte(v.Op) + return buf, nil + } + } + return nil, fmt.Errorf("invalid value size(too big): %d", v.Value) +} + +func NewFlowSpecComponentItem(op int, value int) *FlowSpecComponentItem { + return &FlowSpecComponentItem{op, value} +} + +type FlowSpecComponent struct { + Items []*FlowSpecComponentItem + type_ BGPFlowSpecType +} + +func (p *FlowSpecComponent) DecodeFromBytes(data []byte) error { + p.type_ = BGPFlowSpecType(data[0]) + data = data[1:] + p.Items = make([]*FlowSpecComponentItem, 0) + for { + if len(data) < 2 { + return fmt.Errorf("not all flowspec component bytes available") + } + op := data[0] + end := op & 0x80 + l := 1 << ((op >> 4) & 0x3) // (min, max) = (1, 8) + v := make([]byte, 8) + copy(v[8-l:], data[1:1+l]) + i := int(binary.BigEndian.Uint64(v)) + item := &FlowSpecComponentItem{int(op), i} + p.Items = append(p.Items, item) + if end > 0 { + break + } + data = data[1+l:] + } + return nil +} + +func (p *FlowSpecComponent) Serialize() ([]byte, error) { + buf := []byte{byte(p.Type())} + for i, v := range p.Items { + //set end-of-list bit + if i == (len(p.Items) - 1) { + v.Op |= 0x80 + } else { + v.Op &^= 0x80 + } + bbuf, err := v.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, bbuf...) + } + return buf, nil +} + +func (p *FlowSpecComponent) Len() int { + buf, _ := p.Serialize() + return len(buf) +} + +func (p *FlowSpecComponent) Type() BGPFlowSpecType { + return p.type_ +} + +func formatRaw(op int, value int) string { + return fmt.Sprintf("op: %b, value: %d", op, value) +} + +func formatNumericOp(op int) string { + var opstr string + if op&0x40 > 0 { + opstr = "&" + } else { + opstr = " " + } + if op&0x2 > 0 { + opstr += ">" + } + if op&0x4 > 0 { + opstr += "<" + } + if op&0x1 > 0 { + opstr += "=" + } + return opstr +} + +func formatNumeric(op int, value int) string { + return fmt.Sprintf("%s%d", formatNumericOp(op), value) +} + +func formatProto(op int, value int) string { + return fmt.Sprintf(" %s", Protocol(value).String()) +} + +func formatFlag(op int, value int) string { + and := " " + ss := make([]string, 0, 2) + if op&0x40 > 0 { + and = "&" + } + if op&0x1 > 0 { + ss = append(ss, "match") + } + if op&0x2 > 0 { + ss = append(ss, "not") + } + if len(ss) > 0 { + return fmt.Sprintf("%s(%s)%s", and, strings.Join(ss, "|"), TCPFlag(value).String()) + } + return fmt.Sprintf("%s%s", and, TCPFlag(value).String()) +} + +func formatFragment(op int, value int) string { + ss := make([]string, 0, 4) + if value&0x1 > 0 { + ss = append(ss, "not-a-fragment") + } + if value&0x2 > 0 { + ss = append(ss, "is-a-fragment") + } + if value&0x4 > 0 { + ss = append(ss, "first-fragment") + } + if value&0x8 > 0 { + ss = append(ss, "last-fragment") + } + if len(ss) > 1 { + return fmt.Sprintf("%s(%s)", formatNumericOp(op), strings.Join(ss, "|")) + } + return fmt.Sprintf("%s%s", formatNumericOp(op), ss[0]) +} + +var flowSpecFormatMap = map[BGPFlowSpecType]func(op int, value int) string{ + FLOW_SPEC_TYPE_UNKNOWN: formatRaw, + FLOW_SPEC_TYPE_IP_PROTO: formatProto, + FLOW_SPEC_TYPE_PORT: formatNumeric, + FLOW_SPEC_TYPE_DST_PORT: formatNumeric, + FLOW_SPEC_TYPE_SRC_PORT: formatNumeric, + FLOW_SPEC_TYPE_ICMP_TYPE: formatNumeric, + FLOW_SPEC_TYPE_ICMP_CODE: formatNumeric, + FLOW_SPEC_TYPE_TCP_FLAG: formatFlag, + FLOW_SPEC_TYPE_PKT_LEN: formatNumeric, + FLOW_SPEC_TYPE_DSCP: formatNumeric, + FLOW_SPEC_TYPE_FRAGMENT: formatFragment, +} + +func (p *FlowSpecComponent) String() string { + var f func(op int, value int) string + if _, ok := flowSpecFormatMap[p.Type()]; !ok { + f = flowSpecFormatMap[FLOW_SPEC_TYPE_UNKNOWN] + } + f = flowSpecFormatMap[p.Type()] + buf := bytes.NewBuffer(make([]byte, 0, 32)) + for _, i := range p.Items { + buf.WriteString(f(i.Op, i.Value)) + } + return fmt.Sprintf("[%s:%s]", p.type_, buf.String()) +} + +func NewFlowSpecComponent(type_ BGPFlowSpecType, items []*FlowSpecComponentItem) *FlowSpecComponent { + return &FlowSpecComponent{ + Items: items, + type_: type_, + } +} + +type FlowSpecUnknown struct { + Value []byte +} + +func (p *FlowSpecUnknown) DecodeFromBytes(data []byte) error { + p.Value = data + return nil +} + +func (p *FlowSpecUnknown) Serialize() ([]byte, error) { + return p.Value, nil +} + +func (p *FlowSpecUnknown) Len() int { + return len(p.Value) +} + +func (p *FlowSpecUnknown) Type() BGPFlowSpecType { + if len(p.Value) > 0 { + return BGPFlowSpecType(p.Value[0]) + } + return FLOW_SPEC_TYPE_UNKNOWN +} + +func (p *FlowSpecUnknown) String() string { + return fmt.Sprintf("[unknown:%v]", p.Value) +} + +type FlowSpecNLRI struct { + Value []FlowSpecComponentInterface + rf RouteFamily +} + +func (n *FlowSpecNLRI) decodeFromBytes(rf RouteFamily, data []byte) error { + var length int + if (data[0] >> 4) == 0xf { + length = int(binary.BigEndian.Uint16(data[0:2])) + data = data[2:] + } else { + length = int(data[0]) + data = data[1:] + } + + n.rf = rf + + for l := length; l > 0; { + t := BGPFlowSpecType(data[0]) + var i FlowSpecComponentInterface + switch t { + case FLOW_SPEC_TYPE_DST_PREFIX: + p := &FlowSpecDestinationPrefix{} + switch rf { + case RF_FS_IPv4_UC: + p.Prefix = &IPAddrPrefix{} + case RF_FS_IPv4_VPN: + p.Prefix = &LabeledVPNIPAddrPrefix{} + default: + return fmt.Errorf("Invalid RF: %v", rf) + } + i = p + case FLOW_SPEC_TYPE_SRC_PREFIX: + p := &FlowSpecSourcePrefix{} + switch rf { + case RF_FS_IPv4_UC: + p.Prefix = &IPAddrPrefix{} + case RF_FS_IPv4_VPN: + p.Prefix = &LabeledVPNIPAddrPrefix{} + default: + return fmt.Errorf("Invalid RF: %v", rf) + } + i = p + case FLOW_SPEC_TYPE_IP_PROTO, FLOW_SPEC_TYPE_PORT, FLOW_SPEC_TYPE_DST_PORT, FLOW_SPEC_TYPE_SRC_PORT, + FLOW_SPEC_TYPE_ICMP_TYPE, FLOW_SPEC_TYPE_ICMP_CODE, FLOW_SPEC_TYPE_TCP_FLAG, FLOW_SPEC_TYPE_PKT_LEN, + FLOW_SPEC_TYPE_DSCP, FLOW_SPEC_TYPE_FRAGMENT: + i = NewFlowSpecComponent(t, nil) + default: + i = &FlowSpecUnknown{} + } + + err := i.DecodeFromBytes(data) + if err != nil { + i = &FlowSpecUnknown{data} + } + l -= i.Len() + data = data[i.Len():] + n.Value = append(n.Value, i) + } + + return nil +} + +func (n *FlowSpecNLRI) Serialize() ([]byte, error) { + buf := make([]byte, 0, 32) + for _, v := range n.Value { + b, err := v.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, b...) + } + length := n.Len() + if length > 0xfff { + return nil, fmt.Errorf("Too large: %d", length) + } else if length < 0xf0 { + length -= 1 + buf = append([]byte{byte(length)}, buf...) + } else { + length -= 2 + b := make([]byte, 2) + binary.BigEndian.PutUint16(buf, uint16(length)) + buf = append(b, buf...) + } + + return buf, nil +} + +func (n *FlowSpecNLRI) Len() int { + l := 0 + for _, v := range n.Value { + l += v.Len() + } + if l < 0xf0 { + return l + 1 + } else { + return l + 2 + } +} + +func (n *FlowSpecNLRI) String() string { + buf := bytes.NewBuffer(make([]byte, 0, 32)) + for _, v := range n.Value { + buf.WriteString(v.String()) + } + return buf.String() +} + +func (n *FlowSpecNLRI) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Value []FlowSpecComponentInterface `json:"value"` + }{ + Value: n.Value, + }) +} + +type FlowSpecIPv4Unicast struct { + FlowSpecNLRI +} + +func (n *FlowSpecIPv4Unicast) DecodeFromBytes(data []byte) error { + return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data) +} + +func (n *FlowSpecIPv4Unicast) AFI() uint16 { + return AFI_IP +} + +func (n *FlowSpecIPv4Unicast) SAFI() uint8 { + return SAFI_FLOW_SPEC_UNICAST +} + +func NewFlowSpecIPv4Unicast(value []FlowSpecComponentInterface) *FlowSpecIPv4Unicast { + return &FlowSpecIPv4Unicast{FlowSpecNLRI{value, RF_FS_IPv4_UC}} +} + +type FlowSpecIPv4VPN struct { + FlowSpecNLRI +} + +func (n *FlowSpecIPv4VPN) DecodeFromBytes(data []byte) error { + return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data) +} + +func (n *FlowSpecIPv4VPN) AFI() uint16 { + return AFI_IP +} + +func (n *FlowSpecIPv4VPN) SAFI() uint8 { + return SAFI_FLOW_SPEC_VPN +} + +func NewFlowSpecIPv4VPN(value []FlowSpecComponentInterface) *FlowSpecIPv4VPN { + return &FlowSpecIPv4VPN{FlowSpecNLRI{value, RF_FS_IPv4_VPN}} +} func AfiSafiToRouteFamily(afi uint16, safi uint8) RouteFamily { return RouteFamily(int(afi)<<16 | int(safi)) } @@ -1764,6 +2441,8 @@ const ( RF_EVPN RouteFamily = AFI_L2VPN<<16 | SAFI_EVPN RF_RTC_UC RouteFamily = AFI_IP<<16 | SAFI_ROUTE_TARGET_CONSTRTAINS RF_ENCAP RouteFamily = AFI_IP<<16 | SAFI_ENCAPSULATION + RF_FS_IPv4_UC RouteFamily = AFI_IP<<16 | SAFI_FLOW_SPEC_UNICAST + RF_FS_IPv4_VPN RouteFamily = AFI_IP<<16 | SAFI_FLOW_SPEC_VPN ) func GetRouteFamily(name string) (RouteFamily, error) { @@ -1796,6 +2475,10 @@ func GetRouteFamily(name string) (RouteFamily, error) { return RF_RTC_UC, nil case "encap": return RF_ENCAP, nil + case "ipv4-flowspec": + return RF_FS_IPv4_UC, nil + case "l3vpn-ipv4-flowspec": + return RF_FS_IPv4_VPN, nil } return RouteFamily(0), fmt.Errorf("%s isn't a valid route family name", name) } @@ -1820,6 +2503,10 @@ func NewPrefixFromRouteFamily(afi uint16, safi uint8) (prefix AddrPrefixInterfac prefix = &RouteTargetMembershipNLRI{} case RF_ENCAP: prefix = NewEncapNLRI("") + case RF_FS_IPv4_UC: + prefix = &FlowSpecIPv4Unicast{} + case RF_FS_IPv4_VPN: + prefix = &FlowSpecIPv4VPN{} default: return nil, fmt.Errorf("unknown route family. AFI: %d, SAFI: %d", afi, safi) } @@ -1836,7 +2523,7 @@ const ( ) func (f BGPAttrFlag) String() string { - var strs []string = make([]string, 0, 4) + strs := make([]string, 0, 4) if f&BGP_ATTR_FLAG_EXTENDED_LENGTH > 0 { strs = append(strs, "EXTENDED_LENGTH") } @@ -3134,9 +3821,9 @@ func (e *TwoOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, Exte return t, e.SubType } -func NewTwoOctetAsSpecificExtended(as uint16, localAdmin uint32, isTransitive bool) *TwoOctetAsSpecificExtended { +func NewTwoOctetAsSpecificExtended(subtype ExtendedCommunityAttrSubType, as uint16, localAdmin uint32, isTransitive bool) *TwoOctetAsSpecificExtended { return &TwoOctetAsSpecificExtended{ - SubType: ExtendedCommunityAttrSubType(EC_SUBTYPE_ROUTE_TARGET), + SubType: subtype, AS: as, LocalAdmin: localAdmin, IsTransitive: isTransitive, @@ -3188,13 +3875,13 @@ func (e *IPv4AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, Ext return t, e.SubType } -func NewIPv4AddressSpecificExtended(ip string, localAdmin uint16, isTransitive bool) *IPv4AddressSpecificExtended { +func NewIPv4AddressSpecificExtended(subtype ExtendedCommunityAttrSubType, ip string, localAdmin uint16, isTransitive bool) *IPv4AddressSpecificExtended { ipv4 := net.ParseIP(ip) if ipv4.To4() == nil { return nil } return &IPv4AddressSpecificExtended{ - SubType: ExtendedCommunityAttrSubType(EC_SUBTYPE_ROUTE_TARGET), + SubType: subtype, IPv4: ipv4.To4(), LocalAdmin: localAdmin, IsTransitive: isTransitive, @@ -3250,9 +3937,9 @@ func (e *FourOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, Ext return t, e.SubType } -func NewFourOctetAsSpecificExtended(as uint32, localAdmin uint16, isTransitive bool) *FourOctetAsSpecificExtended { +func NewFourOctetAsSpecificExtended(subtype ExtendedCommunityAttrSubType, as uint32, localAdmin uint16, isTransitive bool) *FourOctetAsSpecificExtended { return &FourOctetAsSpecificExtended{ - SubType: ExtendedCommunityAttrSubType(EC_SUBTYPE_ROUTE_TARGET), + SubType: subtype, AS: as, LocalAdmin: localAdmin, IsTransitive: isTransitive, @@ -3269,15 +3956,15 @@ func ParseRouteTarget(rt string) (ExtendedCommunityInterface, error) { isTransitive := true switch { case ip.To4() != nil: - return NewIPv4AddressSpecificExtended(elems[1], uint16(localAdmin), isTransitive), nil + return NewIPv4AddressSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, elems[1], uint16(localAdmin), isTransitive), nil case elems[6] == "" && elems[7] == "": asn, _ := strconv.Atoi(elems[8]) - return NewTwoOctetAsSpecificExtended(uint16(asn), uint32(localAdmin), isTransitive), nil + return NewTwoOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, uint16(asn), uint32(localAdmin), isTransitive), nil default: fst, _ := strconv.Atoi(elems[7]) snd, _ := strconv.Atoi(elems[8]) asn := fst<<16 | snd - return NewFourOctetAsSpecificExtended(uint32(asn), uint16(localAdmin), isTransitive), nil + return NewFourOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, uint32(asn), uint16(localAdmin), isTransitive), nil } } @@ -3347,7 +4034,7 @@ func (e *EncapExtended) String() string { case TUNNEL_TYPE_VXLAN_GRE: return "VXLAN GRE" default: - return fmt.Sprintf("TUNNEL TYPE: %d", e.TunnelType) + return fmt.Sprintf("tunnel: %d", e.TunnelType) } } @@ -3448,7 +4135,12 @@ func (e *ESILabelExtended) Serialize() ([]byte, error) { } func (e *ESILabelExtended) String() string { - return fmt.Sprintf("ESI LABEL: Label [%d] IsSingleActive [%t]", e.Label, e.IsSingleActive) + buf := bytes.NewBuffer(make([]byte, 0, 32)) + buf.WriteString(fmt.Sprintf("esi-label: %d", e.Label)) + if e.IsSingleActive { + buf.WriteString(", single-active") + } + return buf.String() } func (e *ESILabelExtended) MarshalJSON() ([]byte, error) { @@ -3490,7 +4182,7 @@ func (e *ESImportRouteTarget) Serialize() ([]byte, error) { } func (e *ESImportRouteTarget) String() string { - return fmt.Sprintf("ES-IMPORT ROUTE TARGET: ES-Import [%s]", e.ESImport.String()) + return fmt.Sprintf("es-import rt: %s", e.ESImport.String()) } func (e *ESImportRouteTarget) MarshalJSON() ([]byte, error) { @@ -3537,7 +4229,12 @@ func (e *MacMobilityExtended) Serialize() ([]byte, error) { } func (e *MacMobilityExtended) String() string { - return fmt.Sprintf("MAC MOBILITY: Seq [%d] IsSticky [%t]", e.Sequence, e.IsSticky) + buf := bytes.NewBuffer(make([]byte, 0, 32)) + buf.WriteString(fmt.Sprintf("mac-mobility: %d", e.Sequence)) + if e.IsSticky { + buf.WriteString(", sticky") + } + return buf.String() } func (e *MacMobilityExtended) MarshalJSON() ([]byte, error) { @@ -3597,7 +4294,267 @@ func parseEvpnExtended(data []byte) (ExtendedCommunityInterface, error) { IsSticky: isSticky, }, nil } - return nil, fmt.Errorf("unknown subtype: %d", subType) + return nil, fmt.Errorf("unknown evpn subtype: %d", subType) +} + +type TrafficRateExtended struct { + AS uint16 + Rate float32 +} + +func (e *TrafficRateExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE) + binary.BigEndian.PutUint16(buf[2:4], e.AS) + binary.BigEndian.PutUint32(buf[4:8], math.Float32bits(e.Rate)) + return buf, nil +} + +func (e *TrafficRateExtended) String() string { + buf := bytes.NewBuffer(make([]byte, 0, 32)) + if e.Rate == 0 { + buf.WriteString("discard") + } else { + buf.WriteString(fmt.Sprintf("rate: %f", e.Rate)) + } + if e.AS != 0 { + buf.WriteString(fmt.Sprintf("(as: %d)", e.AS)) + } + return buf.String() +} + +func (e *TrafficRateExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + As uint16 `json:"as"` + Rate float32 `json:"rate"` + }{t, s, e.AS, e.Rate}) +} + +func (e *TrafficRateExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE +} + +func NewTrafficRateExtended(as uint16, rate float32) *TrafficRateExtended { + return &TrafficRateExtended{as, rate} +} + +type TrafficActionExtended struct { + Terminal bool + Sample bool +} + +func (e *TrafficActionExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION) + if e.Terminal { + buf[7] = 0x01 + } + if e.Sample { + buf[7] = buf[7] | 0x2 + } + return buf, nil +} + +func (e *TrafficActionExtended) String() string { + ss := make([]string, 0, 2) + if e.Terminal { + ss = append(ss, "terminal") + } + if e.Sample { + ss = append(ss, "sample") + } + return fmt.Sprintf("action: %s", strings.Join(ss, "-")) +} + +func (e *TrafficActionExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Terminal bool `json:"terminal"` + Sample bool `json:"sample"` + }{t, s, e.Terminal, e.Sample}) +} + +func (e *TrafficActionExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION +} + +func NewTrafficActionExtended(terminal bool, sample bool) *TrafficActionExtended { + return &TrafficActionExtended{terminal, sample} +} + +type RedirectTwoOctetAsSpecificExtended struct { + TwoOctetAsSpecificExtended +} + +func (e *RedirectTwoOctetAsSpecificExtended) Serialize() ([]byte, error) { + buf, err := e.TwoOctetAsSpecificExtended.Serialize() + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT) + return buf, err +} + +func (e *RedirectTwoOctetAsSpecificExtended) String() string { + return fmt.Sprintf("redirect: %s", e.TwoOctetAsSpecificExtended.String()) +} + +func (e *RedirectTwoOctetAsSpecificExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value string `json:"value"` + }{t, s, e.TwoOctetAsSpecificExtended.String()}) +} + +func (e *RedirectTwoOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_REDIRECT +} + +func NewRedirectTwoOctetAsSpecificExtended(as uint16, localAdmin uint32) *RedirectTwoOctetAsSpecificExtended { + return &RedirectTwoOctetAsSpecificExtended{*NewTwoOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, as, localAdmin, false)} +} + +type RedirectIPv4AddressSpecificExtended struct { + IPv4AddressSpecificExtended +} + +func (e *RedirectIPv4AddressSpecificExtended) Serialize() ([]byte, error) { + buf, err := e.IPv4AddressSpecificExtended.Serialize() + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT) + return buf, err +} + +func (e *RedirectIPv4AddressSpecificExtended) String() string { + return fmt.Sprintf("redirect: %s", e.IPv4AddressSpecificExtended.String()) +} + +func (e *RedirectIPv4AddressSpecificExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value string `json:"value"` + }{t, s, e.IPv4AddressSpecificExtended.String()}) +} + +func (e *RedirectIPv4AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2, EC_SUBTYPE_FLOWSPEC_REDIRECT +} + +func NewRedirectIPv4AddressSpecificExtended(ipv4 string, localAdmin uint16) *RedirectIPv4AddressSpecificExtended { + return &RedirectIPv4AddressSpecificExtended{*NewIPv4AddressSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, ipv4, localAdmin, false)} +} + +type RedirectFourOctetAsSpecificExtended struct { + FourOctetAsSpecificExtended +} + +func (e *RedirectFourOctetAsSpecificExtended) Serialize() ([]byte, error) { + buf, err := e.FourOctetAsSpecificExtended.Serialize() + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_REDIRECT) + return buf, err +} + +func (e *RedirectFourOctetAsSpecificExtended) String() string { + return fmt.Sprintf("redirect: %s", e.FourOctetAsSpecificExtended.String()) +} + +func (e *RedirectFourOctetAsSpecificExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value string `json:"value"` + }{t, s, e.FourOctetAsSpecificExtended.String()}) +} + +func (e *RedirectFourOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3, EC_SUBTYPE_FLOWSPEC_REDIRECT +} + +func NewRedirectFourOctetAsSpecificExtended(as uint32, localAdmin uint16) *RedirectFourOctetAsSpecificExtended { + return &RedirectFourOctetAsSpecificExtended{*NewFourOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, as, localAdmin, false)} +} + +type TrafficRemarkExtended struct { + DSCP uint8 +} + +func (e *TrafficRemarkExtended) Serialize() ([]byte, error) { + buf := make([]byte, 8) + buf[0] = byte(EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL) + buf[1] = byte(EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK) + buf[7] = byte(e.DSCP) + return buf, nil +} + +func (e *TrafficRemarkExtended) String() string { + return fmt.Sprintf("remark: %d", e.DSCP) +} + +func (e *TrafficRemarkExtended) MarshalJSON() ([]byte, error) { + t, s := e.GetTypes() + return json.Marshal(struct { + Type ExtendedCommunityAttrType `json:"type"` + Subtype ExtendedCommunityAttrSubType `json:"subtype"` + Value uint8 `json:"value"` + }{t, s, e.DSCP}) +} + +func (e *TrafficRemarkExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK +} + +func NewTrafficRemarkExtended(dscp uint8) *TrafficRemarkExtended { + return &TrafficRemarkExtended{dscp} +} + +func parseFlowSpecExtended(data []byte) (ExtendedCommunityInterface, error) { + typ := ExtendedCommunityAttrType(data[0]) + if typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL && typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2 && typ != EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3 { + return nil, fmt.Errorf("ext comm type is not EC_TYPE_FLOWSPEC: %d", data[0]) + } + subType := ExtendedCommunityAttrSubType(data[1]) + switch subType { + case EC_SUBTYPE_FLOWSPEC_TRAFFIC_RATE: + asn := binary.BigEndian.Uint16(data[2:4]) + bits := binary.BigEndian.Uint32(data[4:8]) + rate := math.Float32frombits(bits) + return NewTrafficRateExtended(asn, rate), nil + case EC_SUBTYPE_FLOWSPEC_TRAFFIC_ACTION: + terminal := data[7]&0x1 == 1 + sample := (data[7]>>1)&0x1 == 1 + return NewTrafficActionExtended(terminal, sample), nil + case EC_SUBTYPE_FLOWSPEC_REDIRECT: + //draft-haas-idr-flowspec-redirect-rt-bis-05 + switch typ { + case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL: + as := binary.BigEndian.Uint16(data[2:4]) + localAdmin := binary.BigEndian.Uint32(data[4:8]) + return NewRedirectTwoOctetAsSpecificExtended(as, localAdmin), nil + case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2: + ipv4 := net.IP(data[2:6]).String() + localAdmin := binary.BigEndian.Uint16(data[6:8]) + return NewRedirectIPv4AddressSpecificExtended(ipv4, localAdmin), nil + case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3: + as := binary.BigEndian.Uint32(data[2:6]) + localAdmin := binary.BigEndian.Uint16(data[6:8]) + return NewRedirectFourOctetAsSpecificExtended(as, localAdmin), nil + } + case EC_SUBTYPE_FLOWSPEC_TRAFFIC_REMARK: + dscp := data[7] + return NewTrafficRemarkExtended(dscp), nil + } + return nil, fmt.Errorf("unknown flowspec subtype: %d", subType) } type UnknownExtended struct { @@ -3625,7 +4582,7 @@ func (e *UnknownExtended) MarshalJSON() ([]byte, error) { return json.Marshal(struct { Type ExtendedCommunityAttrType `json:"type"` Subtype ExtendedCommunityAttrSubType `json:"subtype"` - Value []byte `json:"string"` + Value []byte `json:"value"` }{ Type: t, Subtype: s, @@ -3643,39 +4600,34 @@ type PathAttributeExtendedCommunities struct { } func ParseExtended(data []byte) (ExtendedCommunityInterface, error) { + if len(data) < 8 { + return nil, fmt.Errorf("not all extended community bytes are available") + } attrType := ExtendedCommunityAttrType(data[0]) + subtype := ExtendedCommunityAttrSubType(data[1]) transitive := false switch attrType { case EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC: transitive = true fallthrough case EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC: - e := &TwoOctetAsSpecificExtended{} - e.IsTransitive = transitive - e.SubType = ExtendedCommunityAttrSubType(data[1]) - e.AS = binary.BigEndian.Uint16(data[2:4]) - e.LocalAdmin = binary.BigEndian.Uint32(data[4:8]) - return e, nil + as := binary.BigEndian.Uint16(data[2:4]) + localAdmin := binary.BigEndian.Uint32(data[4:8]) + return NewTwoOctetAsSpecificExtended(subtype, as, localAdmin, transitive), nil case EC_TYPE_TRANSITIVE_IP4_SPECIFIC: transitive = true fallthrough case EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC: - e := &IPv4AddressSpecificExtended{} - e.IsTransitive = transitive - e.SubType = ExtendedCommunityAttrSubType(data[1]) - e.IPv4 = data[2:6] - e.LocalAdmin = binary.BigEndian.Uint16(data[6:8]) - return e, nil + ipv4 := net.IP(data[2:6]).String() + localAdmin := binary.BigEndian.Uint16(data[6:8]) + return NewIPv4AddressSpecificExtended(subtype, ipv4, localAdmin, transitive), nil case EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC: transitive = true fallthrough case EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC: - e := &FourOctetAsSpecificExtended{} - e.IsTransitive = transitive - e.SubType = ExtendedCommunityAttrSubType(data[1]) - e.AS = binary.BigEndian.Uint32(data[2:6]) - e.LocalAdmin = binary.BigEndian.Uint16(data[6:8]) - return e, nil + as := binary.BigEndian.Uint32(data[2:6]) + localAdmin := binary.BigEndian.Uint16(data[6:8]) + return NewFourOctetAsSpecificExtended(subtype, as, localAdmin, transitive), nil case EC_TYPE_TRANSITIVE_OPAQUE: transitive = true fallthrough @@ -3685,6 +4637,9 @@ func ParseExtended(data []byte) (ExtendedCommunityInterface, error) { return e, err case EC_TYPE_EVPN: return parseEvpnExtended(data) + case EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL, EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL2, EC_TYPE_GENERIC_TRANSITIVE_EXPERIMENTAL3: + p, err := parseFlowSpecExtended(data) + return p, err default: e := &UnknownExtended{} e.Type = BGPAttrType(data[0]) @@ -3729,11 +4684,16 @@ func (p *PathAttributeExtendedCommunities) Serialize() ([]byte, error) { } func (p *PathAttributeExtendedCommunities) String() string { - l := []string{} - for _, v := range p.Value { - l = append(l, v.String()) + buf := bytes.NewBuffer(make([]byte, 0, 32)) + for idx, v := range p.Value { + buf.WriteString("[") + buf.WriteString(v.String()) + buf.WriteString("]") + if idx < len(p.Value)-1 { + buf.WriteString(", ") + } } - return fmt.Sprintf("{Extcomms: %s}", strings.Join(l, ", ")) + return fmt.Sprintf("{Extcomms: %s}", buf.String()) } func (p *PathAttributeExtendedCommunities) MarshalJSON() ([]byte, error) { diff --git a/packet/bgp_test.go b/packet/bgp_test.go index e3502d26..74fcb510 100644 --- a/packet/bgp_test.go +++ b/packet/bgp_test.go @@ -62,9 +62,9 @@ func update() *BGPMessage { isTransitive := true ecommunities := []ExtendedCommunityInterface{ - NewTwoOctetAsSpecificExtended(10003, 3<<20, isTransitive), - NewFourOctetAsSpecificExtended(1<<20, 300, isTransitive), - NewIPv4AddressSpecificExtended("192.2.1.2", 3000, isTransitive), + NewTwoOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, 10003, 3<<20, isTransitive), + NewFourOctetAsSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, 1<<20, 300, isTransitive), + NewIPv4AddressSpecificExtended(EC_SUBTYPE_ROUTE_TARGET, "192.2.1.2", 3000, isTransitive), &OpaqueExtended{ Value: &DefaultOpaqueExtendedValue{[]byte{0, 1, 2, 3, 4, 5, 6, 7}}, }, @@ -391,3 +391,74 @@ func Test_MPLSLabelStack(t *testing.T) { assert.Equal(1, len(mpls.Labels)) assert.Equal(WITHDRAW_LABEL, mpls.Labels[0]) } + +func Test_FlowSpecNlri(t *testing.T) { + assert := assert.New(t) + cmp := make([]FlowSpecComponentInterface, 0) + cmp = append(cmp, NewFlowSpecDestinationPrefix(NewIPAddrPrefix(24, "10.0.0.0"))) + cmp = append(cmp, NewFlowSpecSourcePrefix(NewIPAddrPrefix(24, "10.0.0.0"))) + eq := 0x1 + gt := 0x2 + lt := 0x4 + and := 0x40 + not := 0x2 + item1 := NewFlowSpecComponentItem(eq, TCP) + cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_IP_PROTO, []*FlowSpecComponentItem{item1})) + item2 := NewFlowSpecComponentItem(gt|eq, 20) + item3 := NewFlowSpecComponentItem(and|lt|eq, 30) + item4 := NewFlowSpecComponentItem(eq, 10) + cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_PORT, []*FlowSpecComponentItem{item2, item3, item4})) + cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_DST_PORT, []*FlowSpecComponentItem{item2, item3, item4})) + cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_SRC_PORT, []*FlowSpecComponentItem{item2, item3, item4})) + cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_ICMP_TYPE, []*FlowSpecComponentItem{item2, item3, item4})) + cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_ICMP_CODE, []*FlowSpecComponentItem{item2, item3, item4})) + cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_PKT_LEN, []*FlowSpecComponentItem{item2, item3, item4})) + cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_DSCP, []*FlowSpecComponentItem{item2, item3, item4})) + isFlagment := 0x02 + item5 := NewFlowSpecComponentItem(isFlagment, 0) + cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_FRAGMENT, []*FlowSpecComponentItem{item5})) + item6 := NewFlowSpecComponentItem(0, TCP_FLAG_ACK) + item7 := NewFlowSpecComponentItem(and|not, TCP_FLAG_URGENT) + cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_TCP_FLAG, []*FlowSpecComponentItem{item6, item7})) + n1 := NewFlowSpecIPv4Unicast(cmp) + buf1, err := n1.Serialize() + assert.Nil(err) + n2, err := NewPrefixFromRouteFamily(RouteFamilyToAfiSafi(RF_FS_IPv4_UC)) + assert.Nil(err) + err = n2.DecodeFromBytes(buf1) + assert.Nil(err) + buf2, _ := n2.Serialize() + if reflect.DeepEqual(n1, n2) == true { + t.Log("OK") + } else { + t.Error("Something wrong") + t.Error(len(buf1), n1, buf1) + t.Error(len(buf2), n2, buf2) + t.Log(bytes.Equal(buf1, buf2)) + } +} + +func Test_FlowSpecExtended(t *testing.T) { + assert := assert.New(t) + exts := make([]ExtendedCommunityInterface, 0) + exts = append(exts, NewTrafficRateExtended(100, 9600.0)) + exts = append(exts, NewTrafficActionExtended(true, false)) + exts = append(exts, NewRedirectTwoOctetAsSpecificExtended(1000, 1000)) + exts = append(exts, NewRedirectIPv4AddressSpecificExtended("10.0.0.1", 1000)) + exts = append(exts, NewRedirectFourOctetAsSpecificExtended(10000000, 1000)) + exts = append(exts, NewTrafficRemarkExtended(10)) + m1 := NewPathAttributeExtendedCommunities(exts) + buf1, err := m1.Serialize() + assert.Nil(err) + m2 := NewPathAttributeExtendedCommunities(nil) + err = m2.DecodeFromBytes(buf1) + assert.Nil(err) + buf2, _ := m2.Serialize() + if reflect.DeepEqual(m1, m2) == true { + t.Log("OK") + } else { + t.Error("Something wrong") + t.Error(len(buf1), m1, buf1) + t.Error(len(buf2), m2, buf2) + } +} diff --git a/packet/constant.go b/packet/constant.go index 31002c1b..c8de8007 100644 --- a/packet/constant.go +++ b/packet/constant.go @@ -15,6 +15,11 @@ package bgp +import ( + "fmt" + "strings" +) + const AS_TRANS = 23456 const BGP_PORT = 179 @@ -30,3 +35,100 @@ const ( BGP_FSM_OPENCONFIRM BGP_FSM_ESTABLISHED ) + +// partially taken from http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml +type Protocol int + +const ( + Unknown Protocol = iota + ICMP = 0x01 + IGMP = 0x02 + TCP = 0x06 + EGP = 0x08 + IGP = 0x09 + UDP = 0x11 + RSVP = 0x2e + GRE = 0x2f + OSPF = 0x59 + IPIP = 0x5e + PIM = 0x67 + SCTP = 0x84 +) + +var ProtocolNameMap = map[Protocol]string{ + Unknown: "unknown", + ICMP: "icmp", + IGMP: "igmp", + TCP: "tcp", + EGP: "egp", + IGP: "igp", + UDP: "udp", + RSVP: "rsvp", + GRE: "gre", + OSPF: "ospf", + IPIP: "ipip", + PIM: "pim", + SCTP: "sctp", +} + +var ProtocolValueMap = map[string]Protocol{ + ProtocolNameMap[ICMP]: ICMP, + ProtocolNameMap[IGMP]: IGMP, + ProtocolNameMap[TCP]: TCP, + ProtocolNameMap[EGP]: EGP, + ProtocolNameMap[IGP]: IGP, + ProtocolNameMap[UDP]: UDP, + ProtocolNameMap[RSVP]: RSVP, + ProtocolNameMap[GRE]: GRE, + ProtocolNameMap[OSPF]: OSPF, + ProtocolNameMap[IPIP]: IPIP, + ProtocolNameMap[PIM]: PIM, + ProtocolNameMap[SCTP]: SCTP, +} + +func (p Protocol) String() string { + name, ok := ProtocolNameMap[p] + if !ok { + return fmt.Sprintf("%d", p) + } + return name +} + +type TCPFlag int + +const ( + TCP_FLAG_FIN = 0x01 + TCP_FLAG_SYN = 0x02 + TCP_FLAG_RST = 0x04 + TCP_FLAG_PUSH = 0x08 + TCP_FLAG_ACK = 0x10 + TCP_FLAG_URGENT = 0x20 +) + +var TCPFlagNameMap = map[TCPFlag]string{ + TCP_FLAG_FIN: "fin", + TCP_FLAG_SYN: "syn", + TCP_FLAG_RST: "rst", + TCP_FLAG_PUSH: "push", + TCP_FLAG_ACK: "ack", + TCP_FLAG_URGENT: "urgent", +} + +var TCPFlagValueMap = map[string]TCPFlag{ + TCPFlagNameMap[TCP_FLAG_FIN]: TCP_FLAG_FIN, + TCPFlagNameMap[TCP_FLAG_SYN]: TCP_FLAG_SYN, + TCPFlagNameMap[TCP_FLAG_RST]: TCP_FLAG_RST, + TCPFlagNameMap[TCP_FLAG_PUSH]: TCP_FLAG_PUSH, + TCPFlagNameMap[TCP_FLAG_ACK]: TCP_FLAG_ACK, + TCPFlagNameMap[TCP_FLAG_URGENT]: TCP_FLAG_URGENT, +} + +func (f TCPFlag) String() string { + ss := make([]string, 0, 6) + for k, v := range TCPFlagNameMap { + if f&k > 0 { + ss = append(ss, v) + } + } + return strings.Join(ss, "|") +} diff --git a/packet/routefamily_string.go b/packet/routefamily_string.go index 9f61c5e5..8d718e1d 100644 --- a/packet/routefamily_string.go +++ b/packet/routefamily_string.go @@ -1,4 +1,4 @@ -// generated by stringer -type=RouteFamily bgp.go validate.go; DO NOT EDIT +// generated by stringer -type=RouteFamily bgp.go validate.go constant.go; DO NOT EDIT package bgp @@ -9,7 +9,7 @@ const ( _RouteFamily_name_1 = "RF_IPv4_MPLS" _RouteFamily_name_2 = "RF_ENCAP" _RouteFamily_name_3 = "RF_IPv4_VPNRF_IPv4_VPN_MC" - _RouteFamily_name_4 = "RF_RTC_UC" + _RouteFamily_name_4 = "RF_RTC_UCRF_FS_IPv4_UCRF_FS_IPv4_VPN" _RouteFamily_name_5 = "RF_IPv6_UCRF_IPv6_MC" _RouteFamily_name_6 = "RF_IPv6_MPLS" _RouteFamily_name_7 = "RF_IPv6_VPNRF_IPv6_VPN_MC" @@ -22,7 +22,7 @@ var ( _RouteFamily_index_1 = [...]uint8{0, 12} _RouteFamily_index_2 = [...]uint8{0, 8} _RouteFamily_index_3 = [...]uint8{0, 11, 25} - _RouteFamily_index_4 = [...]uint8{0, 9} + _RouteFamily_index_4 = [...]uint8{0, 9, 22, 36} _RouteFamily_index_5 = [...]uint8{0, 10, 20} _RouteFamily_index_6 = [...]uint8{0, 12} _RouteFamily_index_7 = [...]uint8{0, 11, 25} @@ -42,8 +42,9 @@ func (i RouteFamily) String() string { case 65664 <= i && i <= 65665: i -= 65664 return _RouteFamily_name_3[_RouteFamily_index_3[i]:_RouteFamily_index_3[i+1]] - case i == 65668: - return _RouteFamily_name_4 + case 65668 <= i && i <= 65670: + i -= 65668 + return _RouteFamily_name_4[_RouteFamily_index_4[i]:_RouteFamily_index_4[i+1]] case 131073 <= i && i <= 131074: i -= 131073 return _RouteFamily_name_5[_RouteFamily_index_5[i]:_RouteFamily_index_5[i+1]] diff --git a/server/fsm_test.go b/server/fsm_test.go index 498c35b7..db9be4b3 100644 --- a/server/fsm_test.go +++ b/server/fsm_test.go @@ -290,7 +290,7 @@ func makePeerAndHandler() (*Peer, *FSMHandler) { p := &Peer{ gConf: gConf, conf: pConf, - capMap: make(map[bgp.BGPCapabilityCode]bgp.ParameterCapabilityInterface), + capMap: make(map[bgp.BGPCapabilityCode][]bgp.ParameterCapabilityInterface), } p.fsm = NewFSM(&gConf, &pConf) |