diff options
Diffstat (limited to 'packet')
-rw-r--r-- | packet/bgp.go | 1036 | ||||
-rw-r--r-- | packet/bgp_test.go | 77 | ||||
-rw-r--r-- | packet/constant.go | 102 | ||||
-rw-r--r-- | packet/routefamily_string.go | 11 |
4 files changed, 1180 insertions, 46 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) { 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]] |