summaryrefslogtreecommitdiffhomepage
path: root/packet/bgp.go
diff options
context:
space:
mode:
Diffstat (limited to 'packet/bgp.go')
-rw-r--r--packet/bgp.go1036
1 files changed, 998 insertions, 38 deletions
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) {