diff options
-rw-r--r-- | config/default.go | 4 | ||||
-rw-r--r-- | gobgp/cmd/common.go | 2 | ||||
-rw-r--r-- | gobgp/cmd/global.go | 35 | ||||
-rw-r--r-- | packet/bgp.go | 218 | ||||
-rw-r--r-- | packet/bgp_test.go | 47 | ||||
-rw-r--r-- | test/scenario_test/flow_spec_test.py | 9 | ||||
-rw-r--r-- | test/scenario_test/lib/exabgp.py | 5 | ||||
-rw-r--r-- | test/scenario_test/lib/gobgp.py | 5 |
8 files changed, 281 insertions, 44 deletions
diff --git a/config/default.go b/config/default.go index 94f5dc9d..d3160989 100644 --- a/config/default.go +++ b/config/default.go @@ -39,7 +39,9 @@ func SetDefaultConfigValues(md toml.MetaData, bt *Bgp) error { AfiSafi{AfiSafiName: "encap"}, AfiSafi{AfiSafiName: "rtc"}, AfiSafi{AfiSafiName: "ipv4-flowspec"}, - AfiSafi{AfiSafiName: "l3vpn-ipnv4-flowspec"}, + AfiSafi{AfiSafiName: "l3vpn-ipv4-flowspec"}, + AfiSafi{AfiSafiName: "ipv6-flowspec"}, + AfiSafi{AfiSafiName: "l3vpn-ipv6-flowspec"}, } } diff --git a/gobgp/cmd/common.go b/gobgp/cmd/common.go index 4d828366..bd971fea 100644 --- a/gobgp/cmd/common.go +++ b/gobgp/cmd/common.go @@ -458,6 +458,8 @@ func checkAddressFamily(ip net.IP) (bgp.RouteFamily, error) { rf = bgp.RF_RTC_UC case "ipv4-flowspec", "ipv4-flow", "flow4": rf = bgp.RF_FS_IPv4_UC + case "ipv6-flowspec", "ipv6-flow", "flow6": + rf = bgp.RF_FS_IPv6_UC case "": if len(ip) == 0 || ip.To4() != nil { rf = bgp.RF_IPv4_UC diff --git a/gobgp/cmd/global.go b/gobgp/cmd/global.go index 40c2c9da..5951fe25 100644 --- a/gobgp/cmd/global.go +++ b/gobgp/cmd/global.go @@ -235,7 +235,7 @@ func ParseExtendedCommunities(input string) ([]bgp.ExtendedCommunityInterface, e return exts, nil } -func ParseFlowSpecArgs(args []string) (bgp.AddrPrefixInterface, []string, error) { +func ParseFlowSpecArgs(rf bgp.RouteFamily, args []string) (bgp.AddrPrefixInterface, []string, error) { thenPos := len(args) for idx, v := range args { if v == "then" { @@ -246,13 +246,20 @@ func ParseFlowSpecArgs(args []string) (bgp.AddrPrefixInterface, []string, error) return nil, nil, fmt.Errorf("invalid format") } matchArgs := args[1:thenPos] - cmp, err := bgp.ParseFlowSpecComponents(strings.Join(matchArgs, " ")) + cmp, err := bgp.ParseFlowSpecComponents(rf, strings.Join(matchArgs, " ")) if err != nil { return nil, nil, err } - nlri := bgp.NewFlowSpecIPv4Unicast(cmp) - extcomms := args[thenPos:] - return nlri, extcomms, nil + var nlri bgp.AddrPrefixInterface + switch rf { + case bgp.RF_FS_IPv4_UC: + nlri = bgp.NewFlowSpecIPv4Unicast(cmp) + case bgp.RF_FS_IPv6_UC: + nlri = bgp.NewFlowSpecIPv6Unicast(cmp) + default: + return nil, nil, fmt.Errorf("invalid route family") + } + return nlri, args[thenPos:], nil } func ParseEvpnMacAdvArgs(args []string) (bgp.AddrPrefixInterface, []string, error) { @@ -449,8 +456,8 @@ func ParsePath(rf bgp.RouteFamily, args []string) (*api.Path, error) { } case bgp.RF_EVPN: nlri, extcomms, err = ParseEvpnArgs(args) - case bgp.RF_FS_IPv4_UC: - nlri, extcomms, err = ParseFlowSpecArgs(args) + case bgp.RF_FS_IPv4_UC, bgp.RF_FS_IPv6_UC: + nlri, extcomms, err = ParseFlowSpecArgs(rf, args) default: return nil, fmt.Errorf("Unsupported route family: %s", rf) } @@ -520,16 +527,17 @@ func modPath(resource api.Resource, name, modtype string, args []string) error { helpErrMap := map[bgp.RouteFamily]error{} helpErrMap[bgp.RF_IPv4_UC] = fmt.Errorf("usage: %s rib %s <PREFIX> [nexthop <ADDRESS>] -a ipv4", cmdstr, modtype) helpErrMap[bgp.RF_IPv6_UC] = fmt.Errorf("usage: %s rib %s <PREFIX> [nexthop <ADDRESS>] -a ipv6", cmdstr, modtype) - helpErrMap[bgp.RF_FS_IPv4_UC] = fmt.Errorf(`usage: %s rib %s match <MATCH_EXPR> then <THEN_EXPR> [nexthop <ADDRESS>] -a ipv4-flowspec - <MATCH_EXPR> : { %s <PREFIX> | %s <PREFIX> | + fsHelpMsgFmt := fmt.Sprintf(`err: %s +usage: %s rib %s match <MATCH_EXPR> then <THEN_EXPR> -a %%s + <MATCH_EXPR> : { %s <PREFIX> [<OFFSET>] | %s <PREFIX> [<OFFSET>] | %s <PROTO>... | %s <FRAGMENT_TYPE> | %s <TCPFLAG>... | - { %s | %s | %s | %s | %s | %s | %s } <ITEM>... }... + { %s | %s | %s | %s | %s | %s | %s | %s } <ITEM>... }... <PROTO> : %s <FRAGMENT_TYPE> : not-a-fragment, is-a-fragment, first-fragment, last-fragment <TCPFLAG> : %s <ITEM> : &?{<|>|=}<value> <THEN_EXPR> : { %s | %s | %s <value> | %s <RT> | %s <value> | %s { sample | terminal | sample-terminal } | %s <RT>... }... - <RT> : xxx:yyy, xx.xx.xx.xx:yyy, xxx.xxx:yyy`, cmdstr, modtype, + <RT> : xxx:yyy, xx.xx.xx.xx:yyy, xxx.xxx:yyy`, err, cmdstr, modtype, bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_DST_PREFIX], bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_SRC_PREFIX], bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_IP_PROTO], @@ -542,11 +550,14 @@ func modPath(resource api.Resource, name, modtype string, args []string) error { bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_ICMP_CODE], bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_PKT_LEN], bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_DSCP], + bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LABEL], protos, flags, ExtCommNameMap[ACCEPT], ExtCommNameMap[DISCARD], ExtCommNameMap[RATE], ExtCommNameMap[REDIRECT], ExtCommNameMap[MARK], ExtCommNameMap[ACTION], ExtCommNameMap[RT]) - helpErrMap[bgp.RF_EVPN] = fmt.Errorf(`usage: %s rib %s { macadv <MACADV> | multicast <MULTICAST> } [nexthop <ADDRESS>] -a evpn + helpErrMap[bgp.RF_FS_IPv4_UC] = fmt.Errorf(fsHelpMsgFmt, "ipv4-flowspec") + helpErrMap[bgp.RF_FS_IPv6_UC] = fmt.Errorf(fsHelpMsgFmt, "ipv6-flowspec") + helpErrMap[bgp.RF_EVPN] = fmt.Errorf(`usage: %s rib %s { macadv <MACADV> | multicast <MULTICAST> } -a evpn <MACADV> : <mac address> <ip address> <etag> <label> rd <rd> rt <rt>... [encap <encap type>] <MULTICAST> : <ip address> <etag> rd <rd> rt <rt>... [encap <encap type>]`, cmdstr, modtype) if err, ok := helpErrMap[rf]; ok { diff --git a/packet/bgp.go b/packet/bgp.go index ed827327..5cc6d33e 100644 --- a/packet/bgp.go +++ b/packet/bgp.go @@ -1860,6 +1860,7 @@ const ( FLOW_SPEC_TYPE_PKT_LEN FLOW_SPEC_TYPE_DSCP FLOW_SPEC_TYPE_FRAGMENT + FLOW_SPEC_TYPE_LABEL ) var FlowSpecNameMap = map[BGPFlowSpecType]string{ @@ -1876,6 +1877,7 @@ var FlowSpecNameMap = map[BGPFlowSpecType]string{ FLOW_SPEC_TYPE_PKT_LEN: "packet-length", FLOW_SPEC_TYPE_DSCP: "dscp", FLOW_SPEC_TYPE_FRAGMENT: "fragment", + FLOW_SPEC_TYPE_LABEL: "label", } var FlowSpecValueMap = map[string]BGPFlowSpecType{ @@ -1891,29 +1893,58 @@ var FlowSpecValueMap = map[string]BGPFlowSpecType{ 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, + FlowSpecNameMap[FLOW_SPEC_TYPE_LABEL]: FLOW_SPEC_TYPE_LABEL, } -func flowSpecPrefixParser(args []string) (FlowSpecComponentInterface, error) { +func flowSpecPrefixParser(rf RouteFamily, 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 { + ip, net, err := net.ParseCIDR(args[1]) + if err != nil { + return nil, fmt.Errorf("invalid ip prefix") + } + afi, _ := RouteFamilyToAfiSafi(rf) + if afi == AFI_IP && ip.To4() == nil { return nil, fmt.Errorf("invalid ipv4 prefix") + } else if afi == AFI_IP6 && !strings.Contains(ip.String(), ":") { + return nil, fmt.Errorf("invalid ipv6 prefix") } ones, _ := net.Mask.Size() + var offset uint8 + if len(args) > 2 { + o, err := strconv.Atoi(args[2]) + offset = uint8(o) + if err != nil { + return nil, err + } + } switch typ { case FlowSpecNameMap[FLOW_SPEC_TYPE_DST_PREFIX]: - return NewFlowSpecDestinationPrefix(NewIPAddrPrefix(uint8(ones), ip.String())), nil + switch rf { + case RF_FS_IPv4_UC: + return NewFlowSpecDestinationPrefix(NewIPAddrPrefix(uint8(ones), ip.String())), nil + case RF_FS_IPv6_UC: + return NewFlowSpecDestinationPrefix6(NewIPv6AddrPrefix(uint8(ones), ip.String()), offset), nil + default: + return nil, fmt.Errorf("invalid type. only RF_FS_IPv4_UC or RF_FS_IPv6_UC is allowed") + } case FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_PREFIX]: - return NewFlowSpecSourcePrefix(NewIPAddrPrefix(uint8(ones), ip.String())), nil + switch rf { + case RF_FS_IPv4_UC: + return NewFlowSpecSourcePrefix(NewIPAddrPrefix(uint8(ones), ip.String())), nil + case RF_FS_IPv6_UC: + return NewFlowSpecSourcePrefix6(NewIPv6AddrPrefix(uint8(ones), ip.String()), offset), nil + default: + return nil, fmt.Errorf("invalid type. only RF_FS_IPv4_UC or RF_FS_IPv6_UC is allowed") + } } return nil, fmt.Errorf("invalid type. only destination or source is allowed") } -func flowSpecIpProtoParser(args []string) (FlowSpecComponentInterface, error) { +func flowSpecIpProtoParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { ss := make([]string, 0, len(ProtocolNameMap)) for _, v := range ProtocolNameMap { ss = append(ss, v) @@ -1936,7 +1967,7 @@ func flowSpecIpProtoParser(args []string) (FlowSpecComponentInterface, error) { return NewFlowSpecComponent(FLOW_SPEC_TYPE_IP_PROTO, items), nil } -func flowSpecTcpFlagParser(args []string) (FlowSpecComponentInterface, error) { +func flowSpecTcpFlagParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { ss := make([]string, 0, len(TCPFlagNameMap)) for _, v := range TCPFlagNameMap { ss = append(ss, v) @@ -1958,7 +1989,10 @@ func flowSpecTcpFlagParser(args []string) (FlowSpecComponentInterface, error) { return NewFlowSpecComponent(FLOW_SPEC_TYPE_TCP_FLAG, items), nil } -func flowSpecNumericParser(args []string) (FlowSpecComponentInterface, error) { +func flowSpecNumericParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { + if afi, _ := RouteFamilyToAfiSafi(rf); afi == AFI_IP && FlowSpecValueMap[args[0]] == FLOW_SPEC_TYPE_LABEL { + return nil, fmt.Errorf("flow label spec is only allowed for ipv6") + } exp := regexp.MustCompile("^((<=|>=|[<>=])(\\d+)&)?(<=|>=|[<>=])?(\\d+)$") items := make([]*FlowSpecComponentItem, 0) @@ -2000,16 +2034,20 @@ func flowSpecNumericParser(args []string) (FlowSpecComponentInterface, error) { items = append(items, f(and, elems[4], elems[5])) } + return NewFlowSpecComponent(FlowSpecValueMap[args[0]], items), nil } -func flowSpecFragmentParser(args []string) (FlowSpecComponentInterface, error) { +func flowSpecFragmentParser(rf RouteFamily, 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": + if afi, _ := RouteFamilyToAfiSafi(rf); afi == AFI_IP6 { + return nil, fmt.Errorf("can't specify not-a-fragment for ipv6") + } value = 0x1 case "is-a-fragment": value = 0x2 @@ -2024,7 +2062,7 @@ func flowSpecFragmentParser(args []string) (FlowSpecComponentInterface, error) { return NewFlowSpecComponent(FlowSpecValueMap[args[0]], items), nil } -var flowSpecParserMap = map[BGPFlowSpecType]func([]string) (FlowSpecComponentInterface, error){ +var flowSpecParserMap = map[BGPFlowSpecType]func(RouteFamily, []string) (FlowSpecComponentInterface, error){ FLOW_SPEC_TYPE_DST_PREFIX: flowSpecPrefixParser, FLOW_SPEC_TYPE_SRC_PREFIX: flowSpecPrefixParser, FLOW_SPEC_TYPE_IP_PROTO: flowSpecIpProtoParser, @@ -2037,9 +2075,10 @@ var flowSpecParserMap = map[BGPFlowSpecType]func([]string) (FlowSpecComponentInt FLOW_SPEC_TYPE_PKT_LEN: flowSpecNumericParser, FLOW_SPEC_TYPE_DSCP: flowSpecNumericParser, FLOW_SPEC_TYPE_FRAGMENT: flowSpecFragmentParser, + FLOW_SPEC_TYPE_LABEL: flowSpecNumericParser, } -func ParseFlowSpecComponents(input string) ([]FlowSpecComponentInterface, error) { +func ParseFlowSpecComponents(rf RouteFamily, input string) ([]FlowSpecComponentInterface, error) { idxs := make([]struct { t BGPFlowSpecType i int @@ -2065,7 +2104,7 @@ func ParseFlowSpecComponents(input string) ([]FlowSpecComponentInterface, error) } else { a = args[idx.i:] } - cmp, err := f(a) + cmp, err := f(rf, a) if err != nil { return nil, err } @@ -2110,12 +2149,8 @@ func (p *flowSpecPrefix) Serialize() ([]byte, error) { } func (p *flowSpecPrefix) Len() int { - l := p.Prefix.Len() - if l < 0xf0 { - return l + 1 - } else { - return l + 2 - } + buf, _ := p.Serialize() + return len(buf) } func (p *flowSpecPrefix) Type() BGPFlowSpecType { @@ -2136,6 +2171,57 @@ func (p *flowSpecPrefix) MarshalJSON() ([]byte, error) { }) } +type flowSpecPrefix6 struct { + Prefix AddrPrefixInterface + Offset uint8 + type_ BGPFlowSpecType +} + +// draft-ietf-idr-flow-spec-v6-06 +// <type (1 octet), prefix length (1 octet), prefix offset(1 octet), prefix> +func (p *flowSpecPrefix6) DecodeFromBytes(data []byte) error { + p.type_ = BGPFlowSpecType(data[0]) + p.Offset = data[2] + prefix := append([]byte{data[1]}, data[3:]...) + return p.Prefix.DecodeFromBytes(prefix) +} + +func (p *flowSpecPrefix6) Serialize() ([]byte, error) { + buf := []byte{byte(p.Type())} + bbuf, err := p.Prefix.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, bbuf[0]) + buf = append(buf, p.Offset) + return append(buf, bbuf[1:]...), nil +} + +func (p *flowSpecPrefix6) Len() int { + buf, _ := p.Serialize() + return len(buf) +} + +func (p *flowSpecPrefix6) Type() BGPFlowSpecType { + return p.type_ +} + +func (p *flowSpecPrefix6) String() string { + return fmt.Sprintf("[%s:%s/%d]", p.Type(), p.Prefix.String(), p.Offset) +} + +func (p *flowSpecPrefix6) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPFlowSpecType `json:"type"` + Value AddrPrefixInterface `json:"value"` + Offset uint8 `json:"offset"` + }{ + Type: p.Type(), + Value: p.Prefix, + Offset: p.Offset, + }) +} + type FlowSpecDestinationPrefix struct { flowSpecPrefix } @@ -2152,6 +2238,22 @@ func NewFlowSpecSourcePrefix(prefix AddrPrefixInterface) *FlowSpecSourcePrefix { return &FlowSpecSourcePrefix{flowSpecPrefix{prefix, FLOW_SPEC_TYPE_SRC_PREFIX}} } +type FlowSpecDestinationPrefix6 struct { + flowSpecPrefix6 +} + +func NewFlowSpecDestinationPrefix6(prefix AddrPrefixInterface, offset uint8) *FlowSpecDestinationPrefix6 { + return &FlowSpecDestinationPrefix6{flowSpecPrefix6{prefix, offset, FLOW_SPEC_TYPE_DST_PREFIX}} +} + +type FlowSpecSourcePrefix6 struct { + flowSpecPrefix6 +} + +func NewFlowSpecSourcePrefix6(prefix AddrPrefixInterface, offset uint8) *FlowSpecSourcePrefix6 { + return &FlowSpecSourcePrefix6{flowSpecPrefix6{prefix, offset, FLOW_SPEC_TYPE_SRC_PREFIX}} +} + type FlowSpecComponentItem struct { Op int `json:"op"` Value int `json:"value"` @@ -2332,6 +2434,7 @@ var flowSpecFormatMap = map[BGPFlowSpecType]func(op int, value int) string{ FLOW_SPEC_TYPE_PKT_LEN: formatNumeric, FLOW_SPEC_TYPE_DSCP: formatNumeric, FLOW_SPEC_TYPE_FRAGMENT: formatFragment, + FLOW_SPEC_TYPE_LABEL: formatNumeric, } func (p *FlowSpecComponent) String() string { @@ -2414,30 +2517,34 @@ func (n *FlowSpecNLRI) decodeFromBytes(rf RouteFamily, data []byte) error { var i FlowSpecComponentInterface switch t { case FLOW_SPEC_TYPE_DST_PREFIX: - p := &FlowSpecDestinationPrefix{} switch rf { case RF_FS_IPv4_UC: - p.Prefix = &IPAddrPrefix{} + i = NewFlowSpecDestinationPrefix(NewIPAddrPrefix(0, "")) case RF_FS_IPv4_VPN: - p.Prefix = &LabeledVPNIPAddrPrefix{} + i = NewFlowSpecDestinationPrefix(NewLabeledVPNIPAddrPrefix(0, "", *NewMPLSLabelStack(), nil)) + case RF_FS_IPv6_UC: + i = NewFlowSpecDestinationPrefix6(NewIPv6AddrPrefix(0, ""), 0) + case RF_FS_IPv6_VPN: + i = NewFlowSpecDestinationPrefix6(NewLabeledVPNIPv6AddrPrefix(0, "", *NewMPLSLabelStack(), nil), 0) 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{} + i = NewFlowSpecSourcePrefix(NewIPAddrPrefix(0, "")) case RF_FS_IPv4_VPN: - p.Prefix = &LabeledVPNIPAddrPrefix{} + i = NewFlowSpecSourcePrefix(NewLabeledVPNIPAddrPrefix(0, "", *NewMPLSLabelStack(), nil)) + case RF_FS_IPv6_UC: + i = NewFlowSpecSourcePrefix6(NewIPv6AddrPrefix(0, ""), 0) + case RF_FS_IPv6_VPN: + i = NewFlowSpecSourcePrefix6(NewLabeledVPNIPv6AddrPrefix(0, "", *NewMPLSLabelStack(), nil), 0) 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: + FLOW_SPEC_TYPE_DSCP, FLOW_SPEC_TYPE_FRAGMENT, FLOW_SPEC_TYPE_LABEL: i = NewFlowSpecComponent(t, nil) default: i = &FlowSpecUnknown{} @@ -2547,6 +2654,53 @@ func (n *FlowSpecIPv4VPN) SAFI() uint8 { func NewFlowSpecIPv4VPN(value []FlowSpecComponentInterface) *FlowSpecIPv4VPN { return &FlowSpecIPv4VPN{FlowSpecNLRI{value, RF_FS_IPv4_VPN}} } + +type FlowSpecIPv6Unicast struct { + FlowSpecNLRI +} + +func (n *FlowSpecIPv6Unicast) DecodeFromBytes(data []byte) error { + return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data) +} + +func (n *FlowSpecIPv6Unicast) AFI() uint16 { + return AFI_IP6 +} + +func (n *FlowSpecIPv6Unicast) SAFI() uint8 { + return SAFI_FLOW_SPEC_UNICAST +} + +func NewFlowSpecIPv6Unicast(value []FlowSpecComponentInterface) *FlowSpecIPv6Unicast { + return &FlowSpecIPv6Unicast{FlowSpecNLRI{ + Value: value, + rf: RF_FS_IPv6_UC, + }} +} + +type FlowSpecIPv6VPN struct { + FlowSpecNLRI +} + +func (n *FlowSpecIPv6VPN) DecodeFromBytes(data []byte) error { + return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data) +} + +func (n *FlowSpecIPv6VPN) AFI() uint16 { + return AFI_IP6 +} + +func (n *FlowSpecIPv6VPN) SAFI() uint8 { + return SAFI_FLOW_SPEC_VPN +} + +func NewFlowSpecIPv6VPN(value []FlowSpecComponentInterface) *FlowSpecIPv6VPN { + return &FlowSpecIPv6VPN{FlowSpecNLRI{ + Value: value, + rf: RF_FS_IPv6_VPN, + }} +} + func AfiSafiToRouteFamily(afi uint16, safi uint8) RouteFamily { return RouteFamily(int(afi)<<16 | int(safi)) } @@ -2574,6 +2728,8 @@ const ( 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 + RF_FS_IPv6_UC RouteFamily = AFI_IP6<<16 | SAFI_FLOW_SPEC_UNICAST + RF_FS_IPv6_VPN RouteFamily = AFI_IP6<<16 | SAFI_FLOW_SPEC_VPN ) func GetRouteFamily(name string) (RouteFamily, error) { @@ -2610,6 +2766,10 @@ func GetRouteFamily(name string) (RouteFamily, error) { return RF_FS_IPv4_UC, nil case "l3vpn-ipv4-flowspec": return RF_FS_IPv4_VPN, nil + case "ipv6-flowspec": + return RF_FS_IPv6_UC, nil + case "l3vpn-ipv6-flowspec": + return RF_FS_IPv6_VPN, nil } return RouteFamily(0), fmt.Errorf("%s isn't a valid route family name", name) } @@ -2638,6 +2798,10 @@ func NewPrefixFromRouteFamily(afi uint16, safi uint8) (prefix AddrPrefixInterfac prefix = &FlowSpecIPv4Unicast{} case RF_FS_IPv4_VPN: prefix = &FlowSpecIPv4VPN{} + case RF_FS_IPv6_UC: + prefix = &FlowSpecIPv6Unicast{} + case RF_FS_IPv6_VPN: + prefix = &FlowSpecIPv6VPN{} default: return nil, fmt.Errorf("unknown route family. AFI: %d, SAFI: %d", afi, safi) } diff --git a/packet/bgp_test.go b/packet/bgp_test.go index 22db55b2..55931ef7 100644 --- a/packet/bgp_test.go +++ b/packet/bgp_test.go @@ -464,3 +464,50 @@ func Test_FlowSpecExtended(t *testing.T) { t.Error(len(buf2), m2, buf2) } } + +func Test_FlowSpecNlriv6(t *testing.T) { + assert := assert.New(t) + cmp := make([]FlowSpecComponentInterface, 0) + cmp = append(cmp, NewFlowSpecDestinationPrefix6(NewIPv6AddrPrefix(64, "2001::"), 12)) + cmp = append(cmp, NewFlowSpecSourcePrefix6(NewIPv6AddrPrefix(64, "2001::"), 12)) + 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})) + cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_LABEL, []*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 := NewFlowSpecIPv6Unicast(cmp) + buf1, err := n1.Serialize() + assert.Nil(err) + n2, err := NewPrefixFromRouteFamily(RouteFamilyToAfiSafi(RF_FS_IPv6_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)) + } +} diff --git a/test/scenario_test/flow_spec_test.py b/test/scenario_test/flow_spec_test.py index e29f9c91..bba92e1e 100644 --- a/test/scenario_test/flow_spec_test.py +++ b/test/scenario_test/flow_spec_test.py @@ -47,6 +47,12 @@ class GoBGPTestBase(unittest.TestCase): matchs2 = ['tcp-flags syn', 'protocol tcp udp', "packet-length '>1000&<2000'"] thens2 = ['rate-limit 9600', 'redirect 0.10:100', 'mark 20', 'action sample'] g1.add_route(route='flow1', rf='ipv4-flowspec', matchs=matchs2, thens=thens2) + matchs3 = ['destination 2001::/24/10', 'source 2002::/24/15'] + thens3 = ['discard'] + e1.add_route(route='flow2', rf='ipv6-flowspec', matchs=matchs3, thens=thens3) + matchs4 = ['destination 2001::/24 10', "label '=100'"] + thens4 = ['discard'] + g1.add_route(route='flow2', rf='ipv6-flowspec', matchs=matchs4, thens=thens4) initial_wait_time = max(ctn.run() for ctn in ctns) @@ -71,6 +77,9 @@ class GoBGPTestBase(unittest.TestCase): def test_02_check_gobgp_global_rib(self): self.assertTrue(len(self.gobgp.get_global_rib(rf='ipv4-flowspec')) == 2) + def test_03_check_gobgp_global_rib(self): + self.assertTrue(len(self.gobgp.get_global_rib(rf='ipv6-flowspec')) == 2) + if __name__ == '__main__': if os.geteuid() is not 0: diff --git a/test/scenario_test/lib/exabgp.py b/test/scenario_test/lib/exabgp.py index a3be012b..af310530 100644 --- a/test/scenario_test/lib/exabgp.py +++ b/test/scenario_test/lib/exabgp.py @@ -14,7 +14,7 @@ # limitations under the License. from base import * - +from itertools import chain class ExaBGPContainer(BGPContainer): @@ -101,8 +101,7 @@ class ExaBGPContainer(BGPContainer): cmd << '{0};'.format(str(r)) cmd << ' }' - routes = [r for r in self.routes.values() if r['rf'] == 'ipv4-flowspec'] - + routes = [r for r in self.routes.itervalues() if 'flowspec' in r['rf']] if len(routes) > 0: cmd << ' flow {' for route in routes: diff --git a/test/scenario_test/lib/gobgp.py b/test/scenario_test/lib/gobgp.py index f969ca14..2c48c179 100644 --- a/test/scenario_test/lib/gobgp.py +++ b/test/scenario_test/lib/gobgp.py @@ -189,6 +189,9 @@ class GoBGPContainer(BGPContainer): if info['flowspec']: afi_safi_list.append({'AfiSafiName': 'ipv4-flowspec'}) + afi_safi_list.append({'AfiSafiName': 'l3vpn-ipv4-flowspec'}) + afi_safi_list.append({'AfiSafiName': 'ipv6-flowspec'}) + afi_safi_list.append({'AfiSafiName': 'l3vpn-ipv6-flowspec'}) n = {'NeighborConfig': {'NeighborAddress': info['neigh_addr'].split('/')[0], @@ -301,7 +304,7 @@ class GoBGPContainer(BGPContainer): if v['rf'] == 'ipv4' or v['rf'] == 'ipv6': cmd = 'gobgp global '\ 'rib add {0} -a {1}'.format(v['prefix'], v['rf']) - elif v['rf']== 'ipv4-flowspec': + elif v['rf'] == 'ipv4-flowspec' or v['rf'] == 'ipv6-flowspec': cmd = 'gobgp global '\ 'rib add match {0} then {1} -a {2}'.format(' '.join(v['matchs']), ' '.join(v['thens']), v['rf']) else: |