diff options
-rw-r--r-- | config/bgp_configs.go | 7 | ||||
-rw-r--r-- | docs/sources/cli-command-syntax.md | 6 | ||||
-rw-r--r-- | docs/sources/flowspec.md | 20 | ||||
-rw-r--r-- | gobgp/cmd/common.go | 2 | ||||
-rw-r--r-- | gobgp/cmd/global.go | 60 | ||||
-rw-r--r-- | packet/bgp.go | 327 | ||||
-rw-r--r-- | packet/bgp_test.go | 29 | ||||
-rw-r--r-- | packet/constant.go | 61 | ||||
-rw-r--r-- | tools/pyang_plugins/gobgp.yang | 9 |
9 files changed, 438 insertions, 83 deletions
diff --git a/config/bgp_configs.go b/config/bgp_configs.go index b5e18c5b..4fa29bd4 100644 --- a/config/bgp_configs.go +++ b/config/bgp_configs.go @@ -234,6 +234,7 @@ const ( AFI_SAFI_TYPE_L3VPN_IPV4_FLOWSPEC AfiSafiType = "l3vpn-ipv4-flowspec" AFI_SAFI_TYPE_IPV6_FLOWSPEC AfiSafiType = "ipv6-flowspec" AFI_SAFI_TYPE_L3VPN_IPV6_FLOWSPEC AfiSafiType = "l3vpn-ipv6-flowspec" + AFI_SAFI_TYPE_L2VPN_FLOWSPEC AfiSafiType = "l2vpn-flowspec" AFI_SAFI_TYPE_OPAQUE AfiSafiType = "opaque" ) @@ -256,7 +257,8 @@ var AfiSafiTypeToIntMap = map[AfiSafiType]int{ AFI_SAFI_TYPE_L3VPN_IPV4_FLOWSPEC: 15, AFI_SAFI_TYPE_IPV6_FLOWSPEC: 16, AFI_SAFI_TYPE_L3VPN_IPV6_FLOWSPEC: 17, - AFI_SAFI_TYPE_OPAQUE: 18, + AFI_SAFI_TYPE_L2VPN_FLOWSPEC: 18, + AFI_SAFI_TYPE_OPAQUE: 19, } func (v AfiSafiType) ToInt() int { @@ -286,7 +288,8 @@ var IntToAfiSafiTypeMap = map[int]AfiSafiType{ 15: AFI_SAFI_TYPE_L3VPN_IPV4_FLOWSPEC, 16: AFI_SAFI_TYPE_IPV6_FLOWSPEC, 17: AFI_SAFI_TYPE_L3VPN_IPV6_FLOWSPEC, - 18: AFI_SAFI_TYPE_OPAQUE, + 18: AFI_SAFI_TYPE_L2VPN_FLOWSPEC, + 19: AFI_SAFI_TYPE_OPAQUE, } func (v AfiSafiType) Validate() error { diff --git a/docs/sources/cli-command-syntax.md b/docs/sources/cli-command-syntax.md index 7034bfc8..011209e3 100644 --- a/docs/sources/cli-command-syntax.md +++ b/docs/sources/cli-command-syntax.md @@ -80,7 +80,7 @@ The following options can be specified in the global subcommand: | short |long | description | default | |--------|---------------|--------------------------------------------|---------| -|a |address-family |specify any one from among `ipv4`, `ipv6`, `vpnv4`, `vpnv6`, `ipv4-labeled`, `ipv6-labeled`, `evpn`, `encap`, `rtc`, `ipv4-flowspec`, `ipv6-flowspec`, `opaque` | `ipv4` | +|a |address-family |specify any one from among `ipv4`, `ipv6`, `vpnv4`, `vpnv6`, `ipv4-labeled`, `ipv6-labeled`, `evpn`, `encap`, `rtc`, `ipv4-flowspec`, `ipv6-flowspec`, `l2vpn-flowspec`, `opaque` | `ipv4` | <br> @@ -110,7 +110,7 @@ The following options can be specified in the global subcommand: | short |long | description | default | |--------|---------------|--------------------------------------------|---------| -|a |address-family |specify any one from among `ipv4`, `ipv6`, `vpnv4`, `vpnv6`, `ipv4-labeled`, `ipv6-labeld`, `evpn`, `encap`, `rtc`, `ipv4-flowspec`, `ipv6-flowspec`, `opaque` | `ipv4` | +|a |address-family |specify any one from among `ipv4`, `ipv6`, `vpnv4`, `vpnv6`, `ipv4-labeled`, `ipv6-labeld`, `evpn`, `encap`, `rtc`, `ipv4-flowspec`, `ipv6-flowspec`, `l2vpn-flowspec`, `opaque` | `ipv4` | ### 2.3. Show Rib - local-rib/adj-rib-in/adj-rib-out - #### - syntax @@ -132,7 +132,7 @@ The following options can be specified in the neighbor subcommand: | short |long | description | default | |--------|---------------|--------------------------------------------|---------| -|a |address-family |specify any one from among `ipv4`, `ipv6`, `vpnv4`, `vpnv6`, `ipv4-labeled`, `ipv6-labeld`, `evpn`, `encap`, `rtc`, `ipv4-flowspec`, `ipv6-flowspec`, `opaque` | `ipv4` | +|a |address-family |specify any one from among `ipv4`, `ipv6`, `vpnv4`, `vpnv6`, `ipv4-labeled`, `ipv6-labeld`, `evpn`, `encap`, `rtc`, `ipv4-flowspec`, `ipv6-flowspec`, `l2vpn-flowspec`, `opaque` | `ipv4` | ### 2.4. Operations for Policy - add/del/show - diff --git a/docs/sources/flowspec.md b/docs/sources/flowspec.md index 97ab2a85..34c20608 100644 --- a/docs/sources/flowspec.md +++ b/docs/sources/flowspec.md @@ -2,7 +2,8 @@ GoBGP supports [RFC5575](https://tools.ietf.org/html/rfc5575), [draft-ietf-idr-flowspec-redirect-rt-bis-05](http://tools.ietf.org/html/draft-ietf-idr-flowspec-redirect-rt-bis-05) -and [draft-ietf-idr-flow-spec-v6-06](https://tools.ietf.org/html/draft-ietf-idr-flow-spec-v6-06). +, [draft-ietf-idr-flow-spec-v6-06](https://tools.ietf.org/html/draft-ietf-idr-flow-spec-v6-06) +and [draft-ietf-idr-flowspec-l2vpn-03](https://tools.ietf.org/html/draft-ietf-idr-flowspec-l2vpn-03). ## Prerequisites @@ -28,11 +29,15 @@ afi-safis like below. peer-as = 64512 [[neighbors.afi-safis]] afi-safi-name = "ipv4-flowspec" +[[neighbors.afi-safis]] + afi-safi-name = "ipv6-flowspec" +[[neighbors.afi-safis]] + afi-safi-name = "l2vpn-flowspec" ``` ## <a name="section1"> Add Flowspec routes through CLI -CLI syntax to add flowspec is +CLI syntax to add ipv4/ipv6 flowspec rule is ```shell % global rib add match <MATCH_EXPR> then <THEN_EXPR> -a [ipv4-flowspec|ipv6-flowspec] @@ -47,6 +52,17 @@ CLI syntax to add flowspec is <RT> : xxx:yyy, xx.xx.xx.xx:yyy, xxx.xxx:yyy ``` +that for l2vpn flowspec rule is + +``` shell +% global rib add match <MATCH_EXPR> then <THEN_EXPR> -a [l2vpn-flowspec] + <MATCH_EXPR> : { { destination-mac | source-mac } <MAC> | ether-type <ETHER_TYPE> | { llc-dsap | llc-ssap | llc-control | snap | vid | cos | inner-vid | inner-cos } <ITEM>... }... + <ETHER_TYPE> : arp, vmtp, ipx, snmp, net-bios, xtp, pppoe-discovery, ipv4, rarp, ipv6, pppoe-session, loopback, apple-talk, aarp + <ITEM> : &?{<|>|=}<value> + <THEN_EXPR> : { accept | discard | rate-limit <value> | redirect <RT> | mark <value> | action { sample | terminal | sample-terminal } | rt <RT>... }... + <RT> : xxx:yyy, xx.xx.xx.xx:yyy, xxx.xxx:yyy +``` + ### Examples ```shell diff --git a/gobgp/cmd/common.go b/gobgp/cmd/common.go index 8a53df48..5694a572 100644 --- a/gobgp/cmd/common.go +++ b/gobgp/cmd/common.go @@ -456,6 +456,8 @@ func checkAddressFamily(def bgp.RouteFamily) (bgp.RouteFamily, error) { rf = bgp.RF_FS_IPv4_UC case "ipv6-flowspec", "ipv6-flow", "flow6": rf = bgp.RF_FS_IPv6_UC + case "l2vpn-flowspec": + rf = bgp.RF_FS_L2_VPN case "opaque": rf = bgp.RF_OPAQUE case "": diff --git a/gobgp/cmd/global.go b/gobgp/cmd/global.go index 6b328ab1..abddc526 100644 --- a/gobgp/cmd/global.go +++ b/gobgp/cmd/global.go @@ -301,6 +301,9 @@ func ParseFlowSpecArgs(rf bgp.RouteFamily, args []string) (bgp.AddrPrefixInterfa case bgp.RF_FS_IPv6_UC: nlri = bgp.NewFlowSpecIPv6Unicast(cmp) fnlri = &nlri.(*bgp.FlowSpecIPv6Unicast).FlowSpecNLRI + case bgp.RF_FS_L2_VPN: + nlri = bgp.NewFlowSpecL2VPN(cmp) + fnlri = &nlri.(*bgp.FlowSpecL2VPN).FlowSpecNLRI default: return nil, nil, fmt.Errorf("invalid route family") } @@ -646,7 +649,7 @@ 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, bgp.RF_FS_IPv6_UC: + case bgp.RF_FS_IPv4_UC, bgp.RF_FS_IPv6_UC, bgp.RF_FS_L2_VPN: nlri, extcomms, err = ParseFlowSpecArgs(rf, args) case bgp.RF_OPAQUE: m := extractReserved(args, []string{"key", "value"}) @@ -719,20 +722,29 @@ func modPath(resource api.Resource, name, modtype string, args []string) error { ss = append(ss, v) } flags := strings.Join(ss, ", ") + ss = make([]string, 0, len(bgp.EthernetTypeNameMap)) + for _, v := range bgp.EthernetTypeNameMap { + ss = append(ss, v) + } + etherTypes := strings.Join(ss, ", ") helpErrMap := map[bgp.RouteFamily]error{} helpErrMap[bgp.RF_IPv4_UC] = fmt.Errorf("usage: %s rib %s <PREFIX> [origin { igp | egp | incomplete }] [nexthop <ADDRESS>] [med <VALUE>] [local-pref <VALUE>] [community <VALUE>] [aigp metric <METRIC>] -a ipv4", cmdstr, modtype) helpErrMap[bgp.RF_IPv6_UC] = fmt.Errorf("usage: %s rib %s <PREFIX> [origin { igp | egp | incomplete }] [nexthop <ADDRESS>] [med <VALUE>] [local-pref <VALUE>] [community <VALUE>] [aigp metric <METRIC>] -a ipv6", cmdstr, modtype) 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 [not] [match] <TCPFLAG>... | - { %s | %s | %s | %s | %s | %s | %s | %s } <ITEM>... }... - <PROTO> : %s - <FRAGMENT_TYPE> : dont-fragment, is-fragment, first-fragment, last-fragment, not-a-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`, err, cmdstr, modtype, +%%s + <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`, err, cmdstr, modtype, + ExtCommNameMap[ACCEPT], ExtCommNameMap[DISCARD], + ExtCommNameMap[RATE], ExtCommNameMap[REDIRECT], + ExtCommNameMap[MARK], ExtCommNameMap[ACTION], ExtCommNameMap[RT]) + ipFsMatchExpr := fmt.Sprintf(` <MATCH_EXPR> : { %s <PREFIX> [<OFFSET>] | %s <PREFIX> [<OFFSET>] | + %s <PROTO>... | %s <FRAGMENT_TYPE> | %s [not] [match] <TCPFLAG>... | + { %s | %s | %s | %s | %s | %s | %s | %s } <ITEM>... }... + <PROTO> : %s + <FRAGMENT_TYPE> : dont-fragment, is-fragment, first-fragment, last-fragment, not-a-fragment + <TCPFLAG> : %s + <ITEM> : &?{<|>|=}<value>`, bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_DST_PREFIX], bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_SRC_PREFIX], bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_IP_PROTO], @@ -746,12 +758,28 @@ usage: %s rib %s match <MATCH_EXPR> then <THEN_EXPR> -a %%s 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_FS_IPv4_UC] = fmt.Errorf(fsHelpMsgFmt, "ipv4-flowspec") - helpErrMap[bgp.RF_FS_IPv6_UC] = fmt.Errorf(fsHelpMsgFmt, "ipv6-flowspec") + protos, + flags, + ) + helpErrMap[bgp.RF_FS_IPv4_UC] = fmt.Errorf(fsHelpMsgFmt, "ipv4-flowspec", ipFsMatchExpr) + helpErrMap[bgp.RF_FS_IPv6_UC] = fmt.Errorf(fsHelpMsgFmt, "ipv6-flowspec", ipFsMatchExpr) + macFsMatchExpr := fmt.Sprintf(` <MATCH_EXPR> : { { %s | %s } <MAC> | %s <ETHER_TYPE> | { %s | %s | %s | %s | %s | %s | %s | %s } <ITEM>... }... + <ETHER_TYPE> : %s + <ITEM> : &?{<|>|=}<value>`, + bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_DST_MAC], + bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_SRC_MAC], + bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_ETHERNET_TYPE], + bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LLC_DSAP], + bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LLC_SSAP], + bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_LLC_CONTROL], + bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_SNAP], + bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_VID], + bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_COS], + bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_INNER_VID], + bgp.FlowSpecNameMap[bgp.FLOW_SPEC_TYPE_INNER_COS], + etherTypes, + ) + helpErrMap[bgp.RF_FS_L2_VPN] = fmt.Errorf(fsHelpMsgFmt, "l2vpn-flowspec", macFsMatchExpr) 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) diff --git a/packet/bgp.go b/packet/bgp.go index b899e8f2..34b812a0 100644 --- a/packet/bgp.go +++ b/packet/bgp.go @@ -2070,39 +2070,72 @@ const ( FLOW_SPEC_TYPE_DSCP FLOW_SPEC_TYPE_FRAGMENT FLOW_SPEC_TYPE_LABEL + FLOW_SPEC_TYPE_ETHERNET_TYPE // 14 + FLOW_SPEC_TYPE_SRC_MAC + FLOW_SPEC_TYPE_DST_MAC + FLOW_SPEC_TYPE_LLC_DSAP + FLOW_SPEC_TYPE_LLC_SSAP + FLOW_SPEC_TYPE_LLC_CONTROL + FLOW_SPEC_TYPE_SNAP + FLOW_SPEC_TYPE_VID + FLOW_SPEC_TYPE_COS + FLOW_SPEC_TYPE_INNER_VID + FLOW_SPEC_TYPE_INNER_COS ) 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", - FLOW_SPEC_TYPE_LABEL: "label", + 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", + FLOW_SPEC_TYPE_LABEL: "label", + FLOW_SPEC_TYPE_ETHERNET_TYPE: "ether-type", + FLOW_SPEC_TYPE_SRC_MAC: "source-mac", + FLOW_SPEC_TYPE_DST_MAC: "destination-mac", + FLOW_SPEC_TYPE_LLC_DSAP: "llc-dsap", + FLOW_SPEC_TYPE_LLC_SSAP: "llc-ssap", + FLOW_SPEC_TYPE_LLC_CONTROL: "llc-control", + FLOW_SPEC_TYPE_SNAP: "snap", + FLOW_SPEC_TYPE_VID: "vid", + FLOW_SPEC_TYPE_COS: "cos", + FLOW_SPEC_TYPE_INNER_VID: "inner-vid", + FLOW_SPEC_TYPE_INNER_COS: "inner-cos", } 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, - FlowSpecNameMap[FLOW_SPEC_TYPE_LABEL]: FLOW_SPEC_TYPE_LABEL, + 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, + FlowSpecNameMap[FLOW_SPEC_TYPE_LABEL]: FLOW_SPEC_TYPE_LABEL, + FlowSpecNameMap[FLOW_SPEC_TYPE_ETHERNET_TYPE]: FLOW_SPEC_TYPE_ETHERNET_TYPE, + FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_MAC]: FLOW_SPEC_TYPE_SRC_MAC, + FlowSpecNameMap[FLOW_SPEC_TYPE_DST_MAC]: FLOW_SPEC_TYPE_DST_MAC, + FlowSpecNameMap[FLOW_SPEC_TYPE_LLC_DSAP]: FLOW_SPEC_TYPE_LLC_DSAP, + FlowSpecNameMap[FLOW_SPEC_TYPE_LLC_SSAP]: FLOW_SPEC_TYPE_LLC_SSAP, + FlowSpecNameMap[FLOW_SPEC_TYPE_LLC_CONTROL]: FLOW_SPEC_TYPE_LLC_CONTROL, + FlowSpecNameMap[FLOW_SPEC_TYPE_SNAP]: FLOW_SPEC_TYPE_SNAP, + FlowSpecNameMap[FLOW_SPEC_TYPE_VID]: FLOW_SPEC_TYPE_VID, + FlowSpecNameMap[FLOW_SPEC_TYPE_COS]: FLOW_SPEC_TYPE_COS, + FlowSpecNameMap[FLOW_SPEC_TYPE_INNER_VID]: FLOW_SPEC_TYPE_INNER_VID, + FlowSpecNameMap[FLOW_SPEC_TYPE_INNER_COS]: FLOW_SPEC_TYPE_INNER_COS, } func flowSpecPrefixParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { @@ -2205,6 +2238,29 @@ func flowSpecTcpFlagParser(rf RouteFamily, args []string) (FlowSpecComponentInte return NewFlowSpecComponent(FLOW_SPEC_TYPE_TCP_FLAG, items), nil } +func flowSpecEtherTypeParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { + ss := make([]string, 0, len(EthernetTypeNameMap)) + for _, v := range EthernetTypeNameMap { + ss = append(ss, v) + } + protos := strings.Join(ss, "|") + exp := regexp.MustCompile(fmt.Sprintf("^%s (((%s) )*)(%s)$", FlowSpecNameMap[FLOW_SPEC_TYPE_ETHERNET_TYPE], 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 := EthernetTypeValueMap[v] + if !ok { + continue + } + items = append(items, NewFlowSpecComponentItem(eq, int(p))) + } + } + items = append(items, NewFlowSpecComponentItem(eq, int(EthernetTypeValueMap[elems[4]]))) + return NewFlowSpecComponent(FLOW_SPEC_TYPE_ETHERNET_TYPE, items), nil +} + func doFlowSpecNumericParser(rf RouteFamily, args []string, validationFunc func(int) error) (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") @@ -2321,20 +2377,52 @@ func flowSpecFragmentParser(rf RouteFamily, args []string) (FlowSpecComponentInt return NewFlowSpecComponent(FlowSpecValueMap[args[0]], items), nil } +func flowSpecMacParser(rf RouteFamily, args []string) (FlowSpecComponentInterface, error) { + if len(args) < 2 { + return nil, fmt.Errorf("invalid flowspec dst/src mac") + } + if rf != RF_FS_L2_VPN { + return nil, fmt.Errorf("invalid family") + } + typ := args[0] + mac, err := net.ParseMAC(args[1]) + if err != nil { + return nil, fmt.Errorf("invalid mac") + } + switch typ { + case FlowSpecNameMap[FLOW_SPEC_TYPE_DST_MAC]: + return NewFlowSpecDestinationMac(mac), nil + case FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_MAC]: + return NewFlowSpecSourceMac(mac), nil + } + return nil, fmt.Errorf("invalid type. only %s or %s allowed", FlowSpecNameMap[FLOW_SPEC_TYPE_DST_MAC], FlowSpecNameMap[FLOW_SPEC_TYPE_SRC_MAC]) +} + 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, - FLOW_SPEC_TYPE_PORT: flowSpecPortParser, - FLOW_SPEC_TYPE_DST_PORT: flowSpecPortParser, - FLOW_SPEC_TYPE_SRC_PORT: flowSpecPortParser, - 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: flowSpecDscpParser, - FLOW_SPEC_TYPE_FRAGMENT: flowSpecFragmentParser, - FLOW_SPEC_TYPE_LABEL: flowSpecNumericParser, + FLOW_SPEC_TYPE_DST_PREFIX: flowSpecPrefixParser, + FLOW_SPEC_TYPE_SRC_PREFIX: flowSpecPrefixParser, + FLOW_SPEC_TYPE_IP_PROTO: flowSpecIpProtoParser, + FLOW_SPEC_TYPE_PORT: flowSpecPortParser, + FLOW_SPEC_TYPE_DST_PORT: flowSpecPortParser, + FLOW_SPEC_TYPE_SRC_PORT: flowSpecPortParser, + 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: flowSpecDscpParser, + FLOW_SPEC_TYPE_FRAGMENT: flowSpecFragmentParser, + FLOW_SPEC_TYPE_LABEL: flowSpecNumericParser, + FLOW_SPEC_TYPE_ETHERNET_TYPE: flowSpecEtherTypeParser, + FLOW_SPEC_TYPE_DST_MAC: flowSpecMacParser, + FLOW_SPEC_TYPE_SRC_MAC: flowSpecMacParser, + FLOW_SPEC_TYPE_LLC_DSAP: flowSpecNumericParser, + FLOW_SPEC_TYPE_LLC_SSAP: flowSpecNumericParser, + FLOW_SPEC_TYPE_LLC_CONTROL: flowSpecNumericParser, + FLOW_SPEC_TYPE_SNAP: flowSpecNumericParser, + FLOW_SPEC_TYPE_VID: flowSpecNumericParser, + FLOW_SPEC_TYPE_COS: flowSpecNumericParser, + FLOW_SPEC_TYPE_INNER_VID: flowSpecNumericParser, + FLOW_SPEC_TYPE_INNER_COS: flowSpecNumericParser, } func ParseFlowSpecComponents(rf RouteFamily, input string) ([]FlowSpecComponentInterface, error) { @@ -2513,6 +2601,66 @@ func NewFlowSpecSourcePrefix6(prefix AddrPrefixInterface, offset uint8) *FlowSpe return &FlowSpecSourcePrefix6{flowSpecPrefix6{prefix, offset, FLOW_SPEC_TYPE_SRC_PREFIX}} } +type flowSpecMac struct { + Mac net.HardwareAddr + type_ BGPFlowSpecType +} + +func (p *flowSpecMac) DecodeFromBytes(data []byte) error { + if len(data) < 2 || len(data) < 2+int(data[1]) { + return fmt.Errorf("not all mac bits available") + } + p.type_ = BGPFlowSpecType(data[0]) + p.Mac = net.HardwareAddr(data[2 : 2+int(data[1])]) + return nil +} + +func (p *flowSpecMac) Serialize() ([]byte, error) { + if len(p.Mac) == 0 { + return nil, fmt.Errorf("mac unset") + } + buf := []byte{byte(p.Type()), byte(len(p.Mac))} + return append(buf, []byte(p.Mac)...), nil +} + +func (p *flowSpecMac) Len() int { + return 2 + len(p.Mac) +} + +func (p *flowSpecMac) Type() BGPFlowSpecType { + return p.type_ +} + +func (p *flowSpecMac) String() string { + return fmt.Sprintf("[%s:%s]", p.Type(), p.Mac.String()) +} + +func (p *flowSpecMac) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type BGPFlowSpecType `json:"type"` + Value string `json:"value"` + }{ + Type: p.Type(), + Value: p.Mac.String(), + }) +} + +type FlowSpecSourceMac struct { + flowSpecMac +} + +func NewFlowSpecSourceMac(mac net.HardwareAddr) *FlowSpecSourceMac { + return &FlowSpecSourceMac{flowSpecMac{Mac: mac, type_: FLOW_SPEC_TYPE_SRC_MAC}} +} + +type FlowSpecDestinationMac struct { + flowSpecMac +} + +func NewFlowSpecDestinationMac(mac net.HardwareAddr) *FlowSpecDestinationMac { + return &FlowSpecDestinationMac{flowSpecMac{Mac: mac, type_: FLOW_SPEC_TYPE_DST_MAC}} +} + type FlowSpecComponentItem struct { Op int `json:"op"` Value int `json:"value"` @@ -2702,27 +2850,39 @@ func formatFragment(op int, value int) string { return fmt.Sprintf("%s%s", formatNumericOp(op), ss[0]) } +func formatEtherType(op int, value int) string { + return fmt.Sprintf(" %s", EthernetType(value).String()) +} + 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, - FLOW_SPEC_TYPE_LABEL: formatNumeric, + 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, + FLOW_SPEC_TYPE_LABEL: formatNumeric, + FLOW_SPEC_TYPE_ETHERNET_TYPE: formatEtherType, + FLOW_SPEC_TYPE_LLC_DSAP: formatNumeric, + FLOW_SPEC_TYPE_LLC_SSAP: formatNumeric, + FLOW_SPEC_TYPE_LLC_CONTROL: formatNumeric, + FLOW_SPEC_TYPE_SNAP: formatNumeric, + FLOW_SPEC_TYPE_VID: formatNumeric, + FLOW_SPEC_TYPE_COS: formatNumeric, + FLOW_SPEC_TYPE_INNER_VID: formatNumeric, + FLOW_SPEC_TYPE_INNER_COS: formatNumeric, } 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[FLOW_SPEC_TYPE_UNKNOWN] + if _, ok := flowSpecFormatMap[p.Type()]; ok { + f = flowSpecFormatMap[p.Type()] } - f = flowSpecFormatMap[p.Type()] buf := bytes.NewBuffer(make([]byte, 0, 32)) for _, i := range p.Items { buf.WriteString(f(i.Op, i.Value)) @@ -2782,17 +2942,22 @@ type FlowSpecNLRI struct { func (n *FlowSpecNLRI) decodeFromBytes(rf RouteFamily, data []byte) error { var length int - if (data[0] >> 4) == 0xf { + if (data[0]>>4) == 0xf && len(data) > 2 { length = int(binary.BigEndian.Uint16(data[0:2])) data = data[2:] - } else { + } else if len(data) > 1 { length = int(data[0]) data = data[1:] + } else { + return fmt.Errorf("not all flowspec component bytes available") } n.rf = rf for l := length; l > 0; { + if len(data) == 0 { + return fmt.Errorf("not all flowspec component bytes available") + } t := BGPFlowSpecType(data[0]) var i FlowSpecComponentInterface switch t { @@ -2822,9 +2987,25 @@ func (n *FlowSpecNLRI) decodeFromBytes(rf RouteFamily, data []byte) error { default: return fmt.Errorf("Invalid RF: %v", rf) } + case FLOW_SPEC_TYPE_SRC_MAC: + switch rf { + case RF_FS_L2_VPN: + i = NewFlowSpecSourceMac(nil) + default: + return fmt.Errorf("invalid family: %v", rf) + } + case FLOW_SPEC_TYPE_DST_MAC: + switch rf { + case RF_FS_L2_VPN: + i = NewFlowSpecDestinationMac(nil) + default: + return fmt.Errorf("invalid family: %v", rf) + } 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_LABEL: + FLOW_SPEC_TYPE_DSCP, FLOW_SPEC_TYPE_FRAGMENT, FLOW_SPEC_TYPE_LABEL, FLOW_SPEC_TYPE_ETHERNET_TYPE, + FLOW_SPEC_TYPE_LLC_DSAP, FLOW_SPEC_TYPE_LLC_SSAP, FLOW_SPEC_TYPE_LLC_CONTROL, FLOW_SPEC_TYPE_SNAP, + FLOW_SPEC_TYPE_VID, FLOW_SPEC_TYPE_COS, FLOW_SPEC_TYPE_INNER_VID, FLOW_SPEC_TYPE_INNER_COS: i = NewFlowSpecComponent(t, nil) default: i = &FlowSpecUnknown{} @@ -2981,6 +3162,29 @@ func NewFlowSpecIPv6VPN(value []FlowSpecComponentInterface) *FlowSpecIPv6VPN { }} } +type FlowSpecL2VPN struct { + FlowSpecNLRI +} + +func (n *FlowSpecL2VPN) DecodeFromBytes(data []byte) error { + return n.decodeFromBytes(AfiSafiToRouteFamily(n.AFI(), n.SAFI()), data) +} + +func (n *FlowSpecL2VPN) AFI() uint16 { + return AFI_L2VPN +} + +func (n *FlowSpecL2VPN) SAFI() uint8 { + return SAFI_FLOW_SPEC_VPN +} + +func NewFlowSpecL2VPN(value []FlowSpecComponentInterface) *FlowSpecL2VPN { + return &FlowSpecL2VPN{FlowSpecNLRI{ + Value: value, + rf: RF_FS_L2_VPN, + }} +} + type OpaqueNLRI struct { Length uint8 Key []byte @@ -3068,6 +3272,7 @@ const ( 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 + RF_FS_L2_VPN RouteFamily = AFI_L2VPN<<16 | SAFI_FLOW_SPEC_VPN RF_OPAQUE RouteFamily = AFI_OPAQUE<<16 | SAFI_KEY_VALUE ) @@ -3090,6 +3295,7 @@ var AddressFamilyNameMap = map[RouteFamily]string{ RF_FS_IPv4_VPN: "l3vpn-ipv4-flowspec", RF_FS_IPv6_UC: "ipv6-flowspec", RF_FS_IPv6_VPN: "l3vpn-ipv6-flowspec", + RF_FS_L2_VPN: "l2vpn-flowspec", RF_OPAQUE: "opaque", } @@ -3112,6 +3318,7 @@ var AddressFamilyValueMap = map[string]RouteFamily{ AddressFamilyNameMap[RF_FS_IPv4_VPN]: RF_FS_IPv4_VPN, AddressFamilyNameMap[RF_FS_IPv6_UC]: RF_FS_IPv6_UC, AddressFamilyNameMap[RF_FS_IPv6_VPN]: RF_FS_IPv6_VPN, + AddressFamilyNameMap[RF_FS_L2_VPN]: RF_FS_L2_VPN, AddressFamilyNameMap[RF_OPAQUE]: RF_OPAQUE, } @@ -3150,6 +3357,8 @@ func NewPrefixFromRouteFamily(afi uint16, safi uint8) (prefix AddrPrefixInterfac prefix = &FlowSpecIPv6Unicast{} case RF_FS_IPv6_VPN: prefix = &FlowSpecIPv6VPN{} + case RF_FS_L2_VPN: + prefix = &FlowSpecL2VPN{} case RF_OPAQUE: prefix = &OpaqueNLRI{} default: diff --git a/packet/bgp_test.go b/packet/bgp_test.go index f243a785..29aa4e69 100644 --- a/packet/bgp_test.go +++ b/packet/bgp_test.go @@ -3,6 +3,7 @@ package bgp import ( "bytes" "encoding/binary" + "fmt" "github.com/stretchr/testify/assert" "net" "reflect" @@ -534,3 +535,31 @@ func Test_Aigp(t *testing.T) { t.Log(bytes.Equal(buf1, buf2)) } } + +func Test_FlowSpecNlriL2(t *testing.T) { + assert := assert.New(t) + mac, _ := net.ParseMAC("01:23:45:67:89:ab") + cmp := make([]FlowSpecComponentInterface, 0) + cmp = append(cmp, NewFlowSpecDestinationMac(mac)) + cmp = append(cmp, NewFlowSpecSourceMac(mac)) + eq := 0x1 + item1 := NewFlowSpecComponentItem(eq, int(IPv4)) + cmp = append(cmp, NewFlowSpecComponent(FLOW_SPEC_TYPE_ETHERNET_TYPE, []*FlowSpecComponentItem{item1})) + n1 := NewFlowSpecL2VPN(cmp) + buf1, err := n1.Serialize() + assert.Nil(err) + n2, err := NewPrefixFromRouteFamily(RouteFamilyToAfiSafi(RF_FS_L2_VPN)) + 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)) + } + fmt.Println(n1, n2) +} diff --git a/packet/constant.go b/packet/constant.go index ac270cad..aa9b17b9 100644 --- a/packet/constant.go +++ b/packet/constant.go @@ -131,3 +131,64 @@ func (f TCPFlag) String() string { } return strings.Join(ss, "|") } + +type EthernetType int + +const ( + IPv4 EthernetType = 0x0800 + ARP EthernetType = 0x0806 + RARP EthernetType = 0x8035 + VMTP EthernetType = 0x805B + APPLE_TALK EthernetType = 0x809B + AARP EthernetType = 0x80F3 + IPX EthernetType = 0x8137 + SNMP EthernetType = 0x814C + NET_BIOS EthernetType = 0x8191 + XTP EthernetType = 0x817D + IPv6 EthernetType = 0x86DD + PPPoE_DISCOVERY EthernetType = 0x8863 + PPPoE_SESSION EthernetType = 0x8864 + LOOPBACK EthernetType = 0x9000 +) + +var EthernetTypeNameMap = map[EthernetType]string{ + IPv4: "ipv4", + ARP: "arp", + RARP: "rarp", + VMTP: "vmtp", + APPLE_TALK: "apple-talk", + AARP: "aarp", + IPX: "ipx", + SNMP: "snmp", + NET_BIOS: "net-bios", + XTP: "xtp", + IPv6: "ipv6", + PPPoE_DISCOVERY: "pppoe-discovery", + PPPoE_SESSION: "pppoe-session", + LOOPBACK: "loopback", +} + +var EthernetTypeValueMap = map[string]EthernetType{ + EthernetTypeNameMap[IPv4]: IPv4, + EthernetTypeNameMap[ARP]: ARP, + EthernetTypeNameMap[RARP]: RARP, + EthernetTypeNameMap[VMTP]: VMTP, + EthernetTypeNameMap[APPLE_TALK]: APPLE_TALK, + EthernetTypeNameMap[AARP]: AARP, + EthernetTypeNameMap[IPX]: IPX, + EthernetTypeNameMap[SNMP]: SNMP, + EthernetTypeNameMap[NET_BIOS]: NET_BIOS, + EthernetTypeNameMap[XTP]: XTP, + EthernetTypeNameMap[IPv6]: IPv6, + EthernetTypeNameMap[PPPoE_DISCOVERY]: PPPoE_DISCOVERY, + EthernetTypeNameMap[PPPoE_SESSION]: PPPoE_SESSION, + EthernetTypeNameMap[LOOPBACK]: LOOPBACK, +} + +func (t EthernetType) String() string { + n, ok := EthernetTypeNameMap[t] + if !ok { + return fmt.Sprintf("%d", t) + } + return n +} diff --git a/tools/pyang_plugins/gobgp.yang b/tools/pyang_plugins/gobgp.yang index a15fe80d..775d851d 100644 --- a/tools/pyang_plugins/gobgp.yang +++ b/tools/pyang_plugins/gobgp.yang @@ -119,6 +119,13 @@ module gobgp { reference "RFC5575"; } + identity L2VPN-FLOWSPEC { + base bgp-types:afi-safi-type; + description + "L2VPN flowspec (AFI,SAFI = 25,134)"; + reference "draft-ietf-idr-flowspec-l2vpn-03"; + } + identity OPAQUE { base bgp-types:afi-safi-type; description @@ -743,7 +750,7 @@ module gobgp { "Configures a file name to be written."; } leaf interval { - type int64; + type uint64; } } } |