From 5c066cc6a64b03126a737fb41954a62bae762806 Mon Sep 17 00:00:00 2001 From: ISHIDA Wataru Date: Mon, 10 Aug 2015 01:25:18 +0900 Subject: 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 --- packet/bgp.go | 1036 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 998 insertions(+), 38 deletions(-) (limited to 'packet/bgp.go') 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<> 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) { -- cgit v1.2.3