summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>2015-08-10 01:25:18 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2015-08-12 08:04:31 +0900
commit5c066cc6a64b03126a737fb41954a62bae762806 (patch)
tree9c7c36561a75f3f4948bfd5ab1ae06a289a42c06
parent163a9338fb13b10872889406079be95322cd0f46 (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.go2
-rw-r--r--gobgp/common.go2
-rw-r--r--gobgp/global.go428
-rw-r--r--gobgp/main.go1
-rw-r--r--packet/bgp.go1036
-rw-r--r--packet/bgp_test.go77
-rw-r--r--packet/constant.go102
-rw-r--r--packet/routefamily_string.go11
-rw-r--r--server/fsm_test.go2
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)