diff options
author | ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp> | 2015-07-31 18:51:05 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2015-08-08 20:56:46 +0900 |
commit | bf9e135ba85cad641c3812abace9221cbf5a2615 (patch) | |
tree | 435728bd72e93ddfea7a3c02d78cfb1a9c65f16f | |
parent | ecd079e318e1c5d5aa2d2f1348a4ad1a37daec37 (diff) |
server: support vrf
to add/delete vrf
$ gobgp vrf [add|del] <vrf-name> rd <rd> rt [import|export|both] <rt>...
show vrf
$ gobgp vrf
to add/delete a path to a specific vrf
$ gobgp vrf <vrf-name> rib [add|del] <prefix> -a <address-family>
show paths contained in a specific vrf
$ gobgp vrf <vrf-name> rib -a <address-family>
Signed-off-by: ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>
-rw-r--r-- | gobgp/common.go | 15 | ||||
-rw-r--r-- | gobgp/global.go | 3 | ||||
-rw-r--r-- | gobgp/main.go | 3 | ||||
-rw-r--r-- | gobgp/neighbor.go | 42 | ||||
-rw-r--r-- | gobgp/vrf.go | 402 | ||||
-rw-r--r-- | packet/bgp.go | 67 | ||||
-rw-r--r-- | packet/bgp_test.go | 4 | ||||
-rw-r--r-- | policy/policy.go | 21 | ||||
-rw-r--r-- | server/grpc_server.go | 63 | ||||
-rw-r--r-- | server/server.go | 209 | ||||
-rw-r--r-- | table/table_manager.go | 9 | ||||
-rw-r--r-- | table/vrf.go | 46 |
12 files changed, 804 insertions, 80 deletions
diff --git a/gobgp/common.go b/gobgp/common.go index f94b36df..7e4f2b20 100644 --- a/gobgp/common.go +++ b/gobgp/common.go @@ -59,6 +59,7 @@ const ( CMD_DUMP = "dump" CMD_INJECT = "inject" CMD_RPKI = "rpki" + CMD_VRF = "vrf" ) var subOpts struct { @@ -288,6 +289,20 @@ func (r roas) Less(i, j int) bool { return strings.Less(0, 1) } +type vrfs []*api.Vrf + +func (v vrfs) Len() int { + return len(v) +} + +func (v vrfs) Swap(i, j int) { + v[i], v[j] = v[j], v[i] +} + +func (v vrfs) Less(i, j int) bool { + return v[i].Name < v[j].Name +} + func connGrpc() *grpc.ClientConn { timeout := grpc.WithTimeout(time.Second) diff --git a/gobgp/global.go b/gobgp/global.go index 1ea62ddb..61e1d2f9 100644 --- a/gobgp/global.go +++ b/gobgp/global.go @@ -27,8 +27,7 @@ import ( ) func showGlobalRib(args []string) error { - bogusIp := net.IP{} - return showNeighborRib(CMD_GLOBAL, bogusIp, args) + return showNeighborRib(CMD_GLOBAL, "", args) } func getSerizliedRouteTarget(args []string) ([]byte, error) { diff --git a/gobgp/main.go b/gobgp/main.go index 08134c4b..1d4463b1 100644 --- a/gobgp/main.go +++ b/gobgp/main.go @@ -57,10 +57,11 @@ func main() { globalCmd := NewGlobalCmd() neighborCmd := NewNeighborCmd() + vrfCmd := NewVrfCmd() policyCmd := NewPolicyCmd() monitorCmd := NewMonitorCmd() mrtCmd := NewMrtCmd() rpkiCmd := NewRPKICmd() - rootCmd.AddCommand(globalCmd, neighborCmd, policyCmd, monitorCmd, mrtCmd, rpkiCmd) + rootCmd.AddCommand(globalCmd, neighborCmd, vrfCmd, policyCmd, monitorCmd, mrtCmd, rpkiCmd) rootCmd.Execute() } diff --git a/gobgp/neighbor.go b/gobgp/neighbor.go index bb0755f6..51d82dd3 100644 --- a/gobgp/neighbor.go +++ b/gobgp/neighbor.go @@ -395,7 +395,7 @@ func showRoute(pathList []*api.Path, showAge bool, showBest bool, isMonitor bool } } -func showNeighborRib(r string, remoteIP net.IP, args []string) error { +func showNeighborRib(r string, name string, args []string) error { var resource api.Resource switch r { case CMD_GLOBAL: @@ -406,8 +406,10 @@ func showNeighborRib(r string, remoteIP net.IP, args []string) error { resource = api.Resource_ADJ_IN case CMD_ADJ_OUT: resource = api.Resource_ADJ_OUT + case CMD_VRF: + resource = api.Resource_VRF } - rt, err := checkAddressFamily(remoteIP) + rf, err := checkAddressFamily(net.ParseIP(name)) if err != nil { return err } @@ -415,7 +417,7 @@ func showNeighborRib(r string, remoteIP net.IP, args []string) error { var prefix string var host net.IP if len(args) > 0 { - if rt != api.AF_IPV4_UC && rt != api.AF_IPV6_UC { + if rf != api.AF_IPV4_UC && rf != api.AF_IPV6_UC { return fmt.Errorf("route filtering is only supported for IPv4/IPv6 unicast routes") } _, p, err := net.ParseCIDR(args[0]) @@ -431,13 +433,18 @@ func showNeighborRib(r string, remoteIP net.IP, args []string) error { arg := &api.Arguments{ Resource: resource, - Af: rt, - Name: remoteIP.String(), + Af: rf, + Name: name, } ps := paths{} showBest := false showAge := true + + var stream interface { + Recv() (*api.Path, error) + } + switch resource { case api.Resource_LOCAL, api.Resource_GLOBAL: showBest = true @@ -489,9 +496,14 @@ func showNeighborRib(r string, remoteIP net.IP, args []string) error { showAge = false fallthrough case api.Resource_ADJ_IN: - stream, e := client.GetAdjRib(context.Background(), arg) - if e != nil { - return e + stream, err = client.GetAdjRib(context.Background(), arg) + fallthrough + case api.Resource_VRF: + if stream == nil { + stream, err = client.GetVrf(context.Background(), arg) + } + if err != nil { + return err } maxOnes := 0 for { @@ -538,13 +550,13 @@ func showNeighborRib(r string, remoteIP net.IP, args []string) error { return nil } -func resetNeighbor(cmd string, remoteIP net.IP, args []string) error { - rt, err := checkAddressFamily(remoteIP) +func resetNeighbor(cmd string, remoteIP string, args []string) error { + rt, err := checkAddressFamily(net.ParseIP(remoteIP)) if err != nil { return err } arg := &api.Arguments{ - Name: remoteIP.String(), + Name: remoteIP, Af: rt, } switch cmd { @@ -560,10 +572,10 @@ func resetNeighbor(cmd string, remoteIP net.IP, args []string) error { return nil } -func stateChangeNeighbor(cmd string, remoteIP net.IP, args []string) error { +func stateChangeNeighbor(cmd string, remoteIP string, args []string) error { arg := &api.Arguments{ Af: api.AF_IPV4_UC, - Name: remoteIP.String(), + Name: remoteIP, } var err error switch cmd { @@ -695,7 +707,7 @@ func NewNeighborCmd() *cobra.Command { type cmds struct { names []string - f func(string, net.IP, []string) error + f func(string, string, []string) error } c := make([]cmds, 0, 3) @@ -714,7 +726,7 @@ func NewNeighborCmd() *cobra.Command { fmt.Println("invalid ip address:", args[len(args)-1]) os.Exit(1) } - err := f(cmd.Use, remoteIP, args[:len(args)-1]) + err := f(cmd.Use, remoteIP.String(), args[:len(args)-1]) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/gobgp/vrf.go b/gobgp/vrf.go new file mode 100644 index 00000000..26086dde --- /dev/null +++ b/gobgp/vrf.go @@ -0,0 +1,402 @@ +// Copyright (C) 2015 Nippon Telegraph and Telephone Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "encoding/json" + "fmt" + "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/packet" + "github.com/spf13/cobra" + "golang.org/x/net/context" + "io" + "net" + "os" + "sort" + "strconv" + "strings" +) + +func getVrfs() (vrfs, error) { + arg := &api.Arguments{} + stream, err := client.GetVrfs(context.Background(), arg) + if err != nil { + return nil, err + } + vs := make(vrfs, 0) + for { + v, err := stream.Recv() + if err == io.EOF { + break + } else if err != nil { + return nil, err + } + vs = append(vs, v) + } + + sort.Sort(vs) + + return vs, nil +} + +func showVrfs() error { + maxLens := []int{20, 20, 20, 20} + vrfs, err := getVrfs() + if err != nil { + return err + } + if globalOpts.Json { + j, _ := json.Marshal(vrfs) + fmt.Println(string(j)) + return nil + } + lines := make([][]string, 0, len(vrfs)) + for _, v := range vrfs { + name := v.Name + rd := bgp.GetRouteDistinguisher(v.Rd).String() + + f := func(bufs [][]byte) (string, error) { + ret := make([]string, 0, len(bufs)) + for _, rt := range bufs { + r, err := bgp.ParseExtended(rt) + if err != nil { + return "", err + } + ret = append(ret, r.String()) + } + return strings.Join(ret, ", "), nil + } + + importRts, _ := f(v.ImportRt) + exportRts, _ := f(v.ExportRt) + lines = append(lines, []string{name, rd, importRts, exportRts}) + + for i, v := range []int{len(name), len(rd), len(importRts), len(exportRts)} { + if v > maxLens[i] { + maxLens[i] = v + 4 + } + } + + } + format := fmt.Sprintf(" %%-%ds %%-%ds %%-%ds %%-%ds\n", maxLens[0], maxLens[1], maxLens[2], maxLens[3]) + fmt.Printf(format, "Name", "RD", "Import RT", "Export RT") + for _, l := range lines { + fmt.Printf(format, l[0], l[1], l[2], l[3]) + } + return nil +} + +func showVrf(name string) error { + return showNeighborRib(CMD_VRF, name, nil) +} + +func modVrf(typ string, args []string) error { + var arg *api.ModVrfArguments + switch typ { + case CMD_ADD: + if len(args) < 6 || args[1] != "rd" || args[3] != "rt" { + return fmt.Errorf("Usage: gobgp vrf add <vrf name> rd <rd> rt { import | export | both } <rt>...") + } + name := args[0] + rd, err := bgp.ParseRouteDistinguisher(args[2]) + if err != nil { + return err + } + cur := "" + importRt := make([][]byte, 0) + exportRt := make([][]byte, 0) + for _, elem := range args[4:] { + if elem == "import" || elem == "export" || elem == "both" { + cur = elem + continue + } + rt, err := bgp.ParseRouteTarget(elem) + if err != nil { + return err + } + buf, err := rt.Serialize() + if err != nil { + return err + } + switch cur { + case "import": + importRt = append(importRt, buf) + case "export": + exportRt = append(importRt, buf) + case "both": + importRt = append(importRt, buf) + exportRt = append(exportRt, buf) + default: + return fmt.Errorf("Usage: gobgp vrf add <vrf name> rd <rd> rt { import | export | both } <rt>...") + } + } + buf, _ := rd.Serialize() + arg = &api.ModVrfArguments{ + Operation: api.Operation_ADD, + Vrf: &api.Vrf{ + Name: name, + Rd: buf, + ImportRt: importRt, + ExportRt: exportRt, + }, + } + case CMD_DEL: + if len(args) != 1 { + return fmt.Errorf("Usage: gobgp vrf del <vrf name>") + } + arg = &api.ModVrfArguments{ + Operation: api.Operation_DEL, + Vrf: &api.Vrf{ + Name: args[0], + }, + } + } + + _, err := client.ModVrf(context.Background(), arg) + return err +} + +func modVrfPath(modtype string, vrf string, args []string) error { + rf, err := checkAddressFamily(net.IP{}) + if err != nil { + return err + } + + var nlri bgp.AddrPrefixInterface + var nexthop string + + switch rf { + case api.AF_IPV4_UC, api.AF_IPV6_UC: + if len(args) != 1 { + return fmt.Errorf("usage: vrf %s rib %s <prefix> -a { ipv4 | ipv6 }", vrf, modtype) + } + ip, net, _ := net.ParseCIDR(args[0]) + if rf == api.AF_IPV4_UC { + if ip.To4() == nil { + return fmt.Errorf("invalid ipv4 prefix") + } + nexthop = "0.0.0.0" + ones, _ := net.Mask.Size() + nlri = bgp.NewNLRInfo(uint8(ones), ip.String()) + } else { + if ip.To16() == nil { + return fmt.Errorf("invalid ipv6 prefix") + } + nexthop = "::" + ones, _ := net.Mask.Size() + nlri = bgp.NewIPv6AddrPrefix(uint8(ones), ip.String()) + } + case api.AF_EVPN: + if len(args) < 1 { + return fmt.Errorf("usage: vrf %s rib %s { macadv | multicast } ... -a evpn", vrf, modtype) + } + subtype := args[0] + args = args[1:] + + switch subtype { + case "macadv": + if len(args) < 4 { + return fmt.Errorf("usage: vrf %s rib %s macadv <mac address> <ip address> <etag> <label> -a evpn", vrf, modtype) + } + mac, err := net.ParseMAC(args[0]) + if err != nil { + return fmt.Errorf("invalid mac: %s", args[0]) + } + var ip net.IP + iplen := 0 + if args[1] != "0.0.0.0" || args[1] != "::" { + ip = net.ParseIP(args[1]) + if ip == nil { + return fmt.Errorf("invalid ip prefix: %s", args[1]) + } + iplen = net.IPv4len * 8 + if ip.To4() == nil { + iplen = net.IPv6len * 8 + } + } + eTag, err := strconv.Atoi(args[2]) + if err != nil { + return fmt.Errorf("invalid eTag: %s. err: %s", args[2], err) + } + label, err := strconv.Atoi(args[3]) + if err != nil { + return fmt.Errorf("invalid label: %s. err: %s", args[3], err) + } + macIpAdv := &bgp.EVPNMacIPAdvertisementRoute{ + ESI: bgp.EthernetSegmentIdentifier{ + Type: bgp.ESI_ARBITRARY, + }, + MacAddressLength: 48, + MacAddress: mac, + IPAddressLength: uint8(iplen), + IPAddress: ip, + Labels: []uint32{uint32(label)}, + ETag: uint32(eTag), + } + nlri = bgp.NewEVPNNLRI(bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT, 0, macIpAdv) + case "multicast": + if len(args) < 2 { + return fmt.Errorf("usage : vrf %s rib %s multicast <ip address> <etag> -a evpn", vrf, modtype) + } + + var ip net.IP + iplen := 0 + if args[0] != "0.0.0.0" || args[0] != "::" { + ip = net.ParseIP(args[0]) + if ip == nil { + return fmt.Errorf("invalid ip prefix: %s", args[0]) + } + iplen = net.IPv4len * 8 + if ip.To4() == nil { + iplen = net.IPv6len * 8 + } + } + + eTag, err := strconv.Atoi(args[1]) + if err != nil { + return fmt.Errorf("invalid eTag: %s. err: %s", args[1], err) + } + + multicastEtag := &bgp.EVPNMulticastEthernetTagRoute{ + IPAddressLength: uint8(iplen), + IPAddress: ip, + ETag: uint32(eTag), + } + nlri = bgp.NewEVPNNLRI(bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG, 0, multicastEtag) + default: + return fmt.Errorf("usage: vrf %s rib %s { macadv | multicast | ... -a evpn", vrf, modtype) + } + nexthop = "0.0.0.0" + default: + return fmt.Errorf("Unsupported route family: %s", rf) + } + + arg := &api.ModPathArguments{ + Resource: api.Resource_VRF, + Name: vrf, + RawPattrs: make([][]byte, 0), + } + + switch modtype { + case CMD_ADD: + arg.IsWithdraw = false + case CMD_DEL: + arg.IsWithdraw = true + } + + if rf == api.AF_IPV4_UC { + arg.RawNlri, _ = nlri.Serialize() + n, _ := bgp.NewPathAttributeNextHop(nexthop).Serialize() + arg.RawPattrs = append(arg.RawPattrs, n) + } else { + mpreach, _ := bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri}).Serialize() + arg.RawPattrs = append(arg.RawPattrs, mpreach) + } + + origin, _ := bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP).Serialize() + arg.RawPattrs = append(arg.RawPattrs, origin) + + stream, err := client.ModPath(context.Background()) + if err != nil { + return err + } + err = stream.Send(arg) + if err != nil { + return err + } + stream.CloseSend() + + res, e := stream.CloseAndRecv() + if e != nil { + return e + } + if res.Code != api.Error_SUCCESS { + return fmt.Errorf("error: code: %d, msg: %s", res.Code, res.Msg) + } + return nil +} + +func NewVrfCmd() *cobra.Command { + + ribCmd := &cobra.Command{ + Use: CMD_RIB, + Run: func(cmd *cobra.Command, args []string) { + var err error + if len(args) == 1 { + err = showVrf(args[0]) + } else { + err = fmt.Errorf("usage: gobgp vrf <vrf-name> rib") + } + if err != nil { + fmt.Println(err) + os.Exit(1) + } + }, + } + + for _, v := range []string{CMD_ADD, CMD_DEL} { + cmd := &cobra.Command{ + Use: v, + Run: func(cmd *cobra.Command, args []string) { + err := modVrfPath(cmd.Use, args[len(args)-1], args[:len(args)-1]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + }, + } + ribCmd.AddCommand(cmd) + } + + vrfCmdImpl := &cobra.Command{} + vrfCmdImpl.AddCommand(ribCmd) + + vrfCmd := &cobra.Command{ + Use: CMD_VRF, + Run: func(cmd *cobra.Command, args []string) { + var err error + if len(args) == 0 { + err = showVrfs() + } else if len(args) == 1 { + } else { + args = append(args[1:], args[0]) + vrfCmdImpl.SetArgs(args) + err = vrfCmdImpl.Execute() + } + if err != nil { + fmt.Println(err) + os.Exit(1) + } + }, + } + + for _, v := range []string{CMD_ADD, CMD_DEL} { + cmd := &cobra.Command{ + Use: v, + Run: func(cmd *cobra.Command, args []string) { + err := modVrf(cmd.Use, args) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + }, + } + vrfCmd.AddCommand(cmd) + } + vrfCmd.PersistentFlags().StringVarP(&subOpts.AddressFamily, "address-family", "a", "", "address family") + + return vrfCmd +} diff --git a/packet/bgp.go b/packet/bgp.go index 9fc3366e..62be473a 100644 --- a/packet/bgp.go +++ b/packet/bgp.go @@ -845,7 +845,7 @@ type RouteDistinguisherUnknown struct { DefaultRouteDistinguisher } -func getRouteDistinguisher(data []byte) RouteDistinguisherInterface { +func GetRouteDistinguisher(data []byte) RouteDistinguisherInterface { rdtype := binary.BigEndian.Uint16(data[0:2]) switch rdtype { case BGP_RD_TWO_OCTET_AS: @@ -1016,7 +1016,7 @@ func (l *LabeledVPNIPAddrPrefix) DecodeFromBytes(data []byte) error { l.Labels.Labels = []uint32{} } data = data[l.Labels.Len():] - l.RD = getRouteDistinguisher(data) + l.RD = GetRouteDistinguisher(data) data = data[l.RD.Len():] restbits := int(l.Length) - 8*(l.Labels.Len()+l.RD.Len()) l.decodePrefix(data, uint8(restbits), l.addrlen) @@ -1208,7 +1208,7 @@ func (n *RouteTargetMembershipNLRI) DecodeFromBytes(data []byte) error { return fmt.Errorf("Not all RouteTargetMembershipNLRI bytes available") } n.AS = binary.BigEndian.Uint32(data[0:4]) - rt, err := parseExtended(data[4:]) + rt, err := ParseExtended(data[4:]) n.RouteTarget = rt if err != nil { return err @@ -1389,7 +1389,7 @@ type EVPNEthernetAutoDiscoveryRoute struct { } func (er *EVPNEthernetAutoDiscoveryRoute) DecodeFromBytes(data []byte) error { - er.RD = getRouteDistinguisher(data) + er.RD = GetRouteDistinguisher(data) data = data[er.RD.Len():] err := er.ESI.DecodeFromBytes(data) if err != nil { @@ -1403,11 +1403,16 @@ func (er *EVPNEthernetAutoDiscoveryRoute) DecodeFromBytes(data []byte) error { } func (er *EVPNEthernetAutoDiscoveryRoute) Serialize() ([]byte, error) { - buf, err := er.RD.Serialize() - if err != nil { - return nil, err + var buf []byte + var err error + if er.RD != nil { + buf, err = er.RD.Serialize() + if err != nil { + return nil, err + } + } else { + buf = make([]byte, 8) } - tbuf, err := er.ESI.Serialize() if err != nil { return nil, err @@ -1441,7 +1446,7 @@ type EVPNMacIPAdvertisementRoute struct { } func (er *EVPNMacIPAdvertisementRoute) DecodeFromBytes(data []byte) error { - er.RD = getRouteDistinguisher(data) + er.RD = GetRouteDistinguisher(data) data = data[er.RD.Len():] err := er.ESI.DecodeFromBytes(data) if err != nil { @@ -1472,9 +1477,15 @@ func (er *EVPNMacIPAdvertisementRoute) DecodeFromBytes(data []byte) error { } func (er *EVPNMacIPAdvertisementRoute) Serialize() ([]byte, error) { - buf, err := er.RD.Serialize() - if err != nil { - return nil, err + var buf []byte + var err error + if er.RD != nil { + buf, err = er.RD.Serialize() + if err != nil { + return nil, err + } + } else { + buf = make([]byte, 8) } tbuf, err := er.ESI.Serialize() @@ -1536,7 +1547,7 @@ type EVPNMulticastEthernetTagRoute struct { } func (er *EVPNMulticastEthernetTagRoute) DecodeFromBytes(data []byte) error { - er.RD = getRouteDistinguisher(data) + er.RD = GetRouteDistinguisher(data) data = data[er.RD.Len():] er.ETag = binary.BigEndian.Uint32(data[0:4]) er.IPAddressLength = data[4] @@ -1550,9 +1561,15 @@ func (er *EVPNMulticastEthernetTagRoute) DecodeFromBytes(data []byte) error { } func (er *EVPNMulticastEthernetTagRoute) Serialize() ([]byte, error) { - buf, err := er.RD.Serialize() - if err != nil { - return nil, err + var buf []byte + var err error + if er.RD != nil { + buf, err = er.RD.Serialize() + if err != nil { + return nil, err + } + } else { + buf = make([]byte, 8) } tbuf := make([]byte, 4) binary.BigEndian.PutUint32(tbuf, er.ETag) @@ -1593,7 +1610,7 @@ type EVPNEthernetSegmentRoute struct { } func (er *EVPNEthernetSegmentRoute) DecodeFromBytes(data []byte) error { - er.RD = getRouteDistinguisher(data) + er.RD = GetRouteDistinguisher(data) data = data[er.RD.Len():] er.ESI.DecodeFromBytes(data) data = data[10:] @@ -1608,9 +1625,15 @@ func (er *EVPNEthernetSegmentRoute) DecodeFromBytes(data []byte) error { } func (er *EVPNEthernetSegmentRoute) Serialize() ([]byte, error) { - buf, err := er.RD.Serialize() - if err != nil { - return nil, err + var buf []byte + var err error + if er.RD != nil { + buf, err = er.RD.Serialize() + if err != nil { + return nil, err + } + } else { + buf = make([]byte, 8) } tbuf, err := er.ESI.Serialize() if err != nil { @@ -3585,7 +3608,7 @@ type PathAttributeExtendedCommunities struct { Value []ExtendedCommunityInterface } -func parseExtended(data []byte) (ExtendedCommunityInterface, error) { +func ParseExtended(data []byte) (ExtendedCommunityInterface, error) { attrType := ExtendedCommunityAttrType(data[0]) transitive := false switch attrType { @@ -3648,7 +3671,7 @@ func (p *PathAttributeExtendedCommunities) DecodeFromBytes(data []byte) error { } value := p.PathAttribute.Value for len(value) >= 8 { - e, err := parseExtended(value) + e, err := ParseExtended(value) if err != nil { return err } diff --git a/packet/bgp_test.go b/packet/bgp_test.go index 731b9870..e3502d26 100644 --- a/packet/bgp_test.go +++ b/packet/bgp_test.go @@ -270,7 +270,7 @@ func Test_RFC5512(t *testing.T) { buf[0] = byte(EC_TYPE_TRANSITIVE_OPAQUE) buf[1] = byte(EC_SUBTYPE_COLOR) binary.BigEndian.PutUint32(buf[4:], 1000000) - ec, err := parseExtended(buf) + ec, err := ParseExtended(buf) assert.Equal(nil, err) assert.Equal("1000000", ec.String()) buf, err = ec.Serialize() @@ -281,7 +281,7 @@ func Test_RFC5512(t *testing.T) { buf[0] = byte(EC_TYPE_TRANSITIVE_OPAQUE) buf[1] = byte(EC_SUBTYPE_ENCAPSULATION) binary.BigEndian.PutUint16(buf[6:], uint16(TUNNEL_TYPE_VXLAN)) - ec, err = parseExtended(buf) + ec, err = ParseExtended(buf) assert.Equal(nil, err) assert.Equal("VXLAN", ec.String()) buf, err = ec.Serialize() diff --git a/policy/policy.go b/policy/policy.go index 70d1bbc3..5d0dc631 100644 --- a/policy/policy.go +++ b/policy/policy.go @@ -2062,3 +2062,24 @@ func PoliciesToString(reqPolicies []*api.PolicyDefinition) []string { } return policies } + +func CanImportToVrf(v *table.Vrf, path *table.Path) bool { + f := func(arg []bgp.ExtendedCommunityInterface) []config.ExtCommunity { + ret := make([]config.ExtCommunity, 0, len(arg)) + for _, a := range arg { + ret = append(ret, config.ExtCommunity{ + ExtCommunity: fmt.Sprintf("RT:%s", a.String()), + }) + } + return ret + } + set := config.ExtCommunitySet{ + ExtCommunitySetName: v.Name, + ExtCommunityList: f(v.ImportRt), + } + matchSet := config.MatchExtCommunitySet{ + ExtCommunitySet: v.Name, + MatchSetOptions: config.MATCH_SET_OPTIONS_TYPE_ANY, + } + return NewExtCommunityCondition(matchSet, []config.ExtCommunitySet{set}).evaluate(path) +} diff --git a/server/grpc_server.go b/server/grpc_server.go index 17fb6c87..041f0659 100644 --- a/server/grpc_server.go +++ b/server/grpc_server.go @@ -48,8 +48,6 @@ const ( REQ_NEIGHBOR_POLICY_DEL_EXPORT REQ_NEIGHBOR_POLICY_DEL_DISTRIBUTE REQ_GLOBAL_RIB - REQ_GLOBAL_ADD - REQ_GLOBAL_DELETE REQ_POLICY_PREFIX REQ_POLICY_PREFIXES REQ_POLICY_PREFIX_ADD @@ -85,6 +83,10 @@ const ( REQ_MRT_GLOBAL_RIB REQ_MRT_LOCAL_RIB REQ_RPKI + REQ_VRF + REQ_VRFS + REQ_VRF_MOD + REQ_MOD_PATH ) const GRPC_PORT = 8080 @@ -329,16 +331,11 @@ func (s *Server) ModPath(stream api.Grpc_ModPathServer) error { return err } - if arg.Resource != api.Resource_GLOBAL { + if arg.Resource != api.Resource_GLOBAL && arg.Resource != api.Resource_VRF { return fmt.Errorf("unsupported resource: %s", arg.Resource) } - reqType := REQ_GLOBAL_ADD - if arg.IsWithdraw { - reqType = REQ_GLOBAL_DELETE - } - - req := NewGrpcRequest(reqType, "", bgp.RouteFamily(0), arg) + req := NewGrpcRequest(REQ_MOD_PATH, arg.Name, bgp.RouteFamily(0), arg) s.bgpServerCh <- req res := <-req.ResponseCh @@ -641,6 +638,54 @@ func (s *Server) GetRPKI(arg *api.Arguments, stream api.Grpc_GetRPKIServer) erro return nil } +func (s *Server) GetVrf(arg *api.Arguments, stream api.Grpc_GetVrfServer) error { + rf, err := convertAf2Rf(arg.Af) + if err != nil { + return err + } + req := NewGrpcRequest(REQ_VRF, "", rf, arg) + s.bgpServerCh <- req + + for res := range req.ResponseCh { + if err := res.Err(); err != nil { + log.Debug(err.Error()) + return err + } + if err := stream.Send(res.Data.(*api.Path)); err != nil { + return err + } + } + return nil +} + +func (s *Server) GetVrfs(arg *api.Arguments, stream api.Grpc_GetVrfsServer) error { + req := NewGrpcRequest(REQ_VRFS, "", bgp.RouteFamily(0), nil) + s.bgpServerCh <- req + + for res := range req.ResponseCh { + if err := res.Err(); err != nil { + log.Debug(err.Error()) + return err + } + if err := stream.Send(res.Data.(*api.Vrf)); err != nil { + return err + } + } + return nil +} + +func (s *Server) ModVrf(ctx context.Context, arg *api.ModVrfArguments) (*api.Error, error) { + none := &api.Error{} + req := NewGrpcRequest(REQ_VRF_MOD, "", bgp.RouteFamily(0), arg) + s.bgpServerCh <- req + + res := <-req.ResponseCh + if err := res.Err(); err != nil { + return none, err + } + return none, nil +} + type GrpcRequest struct { RequestType int RemoteAddr string diff --git a/server/server.go b/server/server.go index 4cabc7b9..56ef8c68 100644 --- a/server/server.go +++ b/server/server.go @@ -831,33 +831,31 @@ func getMacMobilityExtendedCommunity(etag uint32, mac net.HardwareAddr, evpnPath return nil } -func (server *BgpServer) handleGlobalRibRequest(grpcReq *GrpcRequest, peerInfo *table.PeerInfo) []*table.Path { - var isWithdraw bool +func (server *BgpServer) handleModPathRequest(grpcReq *GrpcRequest, peerInfo *table.PeerInfo) []*table.Path { var nlri bgp.AddrPrefixInterface result := &GrpcResponse{} pattr := make([]bgp.PathAttributeInterface, 0) extcomms := make([]bgp.ExtendedCommunityInterface, 0) + var nexthop string + var rf bgp.RouteFamily - args, ok := grpcReq.Data.(*api.ModPathArguments) + arg, ok := grpcReq.Data.(*api.ModPathArguments) if !ok { result.ResponseErr = fmt.Errorf("type assertion failed") goto ERR } - if grpcReq.RequestType == REQ_GLOBAL_DELETE { - isWithdraw = true - } - if len(args.RawNlri) > 0 { + if len(arg.RawNlri) > 0 { nlri = &bgp.NLRInfo{} - err := nlri.DecodeFromBytes(args.RawNlri) + err := nlri.DecodeFromBytes(arg.RawNlri) if err != nil { result.ResponseErr = err goto ERR } } - for _, attr := range args.RawPattrs { + for _, attr := range arg.RawPattrs { p, err := bgp.GetPathAttribute(attr) if err != nil { result.ResponseErr = err @@ -871,30 +869,70 @@ func (server *BgpServer) handleGlobalRibRequest(grpcReq *GrpcRequest, peerInfo * } switch p.GetType() { + case bgp.BGP_ATTR_TYPE_NEXT_HOP: + nexthop = p.(*bgp.PathAttributeNextHop).Value.String() case bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES: value := p.(*bgp.PathAttributeExtendedCommunities).Value if len(value) > 0 { extcomms = append(extcomms, value...) } case bgp.BGP_ATTR_TYPE_MP_REACH_NLRI: - value := p.(*bgp.PathAttributeMpReachNLRI).Value - if len(value) != 1 { + mpreach := p.(*bgp.PathAttributeMpReachNLRI) + if len(mpreach.Value) != 1 { result.ResponseErr = fmt.Errorf("include only one route in mp_reach_nlri") goto ERR } - nlri = p.(*bgp.PathAttributeMpReachNLRI).Value[0] - fallthrough + nlri = mpreach.Value[0] + nexthop = mpreach.Nexthop.String() default: pattr = append(pattr, p) } } - if nlri == nil { - result.ResponseErr = fmt.Errorf("no nlri included") + if nlri == nil || nexthop == "" { + result.ResponseErr = fmt.Errorf("not found nlri or nexthop") goto ERR } - if bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI()) == bgp.RF_EVPN { + rf = bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI()) + + if arg.Resource == api.Resource_VRF { + vrfs := server.localRibMap[GLOBAL_RIB_NAME].rib.Vrfs + if _, ok := vrfs[arg.Name]; !ok { + result.ResponseErr = fmt.Errorf("vrf %s not found", arg.Name) + goto ERR + } + vrf := vrfs[arg.Name] + + switch rf { + case bgp.RF_IPv4_UC: + n := nlri.(*bgp.NLRInfo) + nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(), vrf.Rd) + case bgp.RF_IPv6_UC: + n := nlri.(*bgp.IPv6AddrPrefix) + nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(), vrf.Rd) + case bgp.RF_EVPN: + n := nlri.(*bgp.EVPNNLRI) + switch n.RouteType { + case bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT: + n.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute).RD = vrf.Rd + case bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG: + n.RouteTypeData.(*bgp.EVPNMulticastEthernetTagRoute).RD = vrf.Rd + } + default: + result.ResponseErr = fmt.Errorf("unsupported route family for vrf: %s", rf) + goto ERR + } + extcomms = append(extcomms, vrf.ExportRt...) + } + + if arg.Resource != api.Resource_VRF && rf == bgp.RF_IPv4_UC { + pattr = append(pattr, bgp.NewPathAttributeNextHop(nexthop)) + } else { + pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri})) + } + + if rf == bgp.RF_EVPN { evpnNlri := nlri.(*bgp.EVPNNLRI) if evpnNlri.RouteType == bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT { macIpAdv := evpnNlri.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute) @@ -911,7 +949,7 @@ func (server *BgpServer) handleGlobalRibRequest(grpcReq *GrpcRequest, peerInfo * pattr = append(pattr, bgp.NewPathAttributeExtendedCommunities(extcomms)) } - return []*table.Path{table.NewPath(peerInfo, nlri, isWithdraw, pattr, false, time.Now(), args.NoImplicitWithdraw)} + return []*table.Path{table.NewPath(peerInfo, nlri, arg.IsWithdraw, pattr, false, time.Now(), arg.NoImplicitWithdraw)} ERR: grpcReq.ResponseCh <- result close(grpcReq.ResponseCh) @@ -919,8 +957,123 @@ ERR: } +func (server *BgpServer) handleVrfMod(arg *api.ModVrfArguments) error { + vrfs := server.localRibMap[GLOBAL_RIB_NAME].rib.Vrfs + switch arg.Operation { + case api.Operation_ADD: + if _, ok := vrfs[arg.Vrf.Name]; ok { + return fmt.Errorf("vrf %s already exists", arg.Vrf.Name) + } + rd := bgp.GetRouteDistinguisher(arg.Vrf.Rd) + f := func(bufs [][]byte) ([]bgp.ExtendedCommunityInterface, error) { + ret := make([]bgp.ExtendedCommunityInterface, 0, len(bufs)) + for _, rt := range bufs { + r, err := bgp.ParseExtended(rt) + if err != nil { + return nil, err + } + ret = append(ret, r) + } + return ret, nil + } + importRt, err := f(arg.Vrf.ImportRt) + if err != nil { + return err + } + exportRt, err := f(arg.Vrf.ImportRt) + if err != nil { + return err + } + log.WithFields(log.Fields{ + "Topic": "Vrf", + "Key": arg.Vrf.Name, + "Rd": rd, + "ImportRt": importRt, + "ExportRt": exportRt, + }).Debugf("add vrf") + vrfs[arg.Vrf.Name] = &table.Vrf{ + Name: arg.Vrf.Name, + Rd: rd, + ImportRt: importRt, + ExportRt: exportRt, + } + case api.Operation_DEL: + if _, ok := vrfs[arg.Vrf.Name]; !ok { + return fmt.Errorf("vrf %s not found", arg.Vrf.Name) + } + vrf := vrfs[arg.Vrf.Name] + log.WithFields(log.Fields{ + "Topic": "Vrf", + "Key": vrf.Name, + "Rd": vrf.Rd, + "ImportRt": vrf.ImportRt, + "ExportRt": vrf.ExportRt, + }).Debugf("delete vrf") + delete(vrfs, arg.Vrf.Name) + default: + return fmt.Errorf("unknown operation:", arg.Operation) + } + return nil +} + +func (server *BgpServer) handleVrfRequest(req *GrpcRequest) []*table.Path { + var msgs []*table.Path + result := &GrpcResponse{} + + switch req.RequestType { + case REQ_VRF: + arg := req.Data.(*api.Arguments) + rib := server.localRibMap[GLOBAL_RIB_NAME].rib + vrfs := rib.Vrfs + if _, ok := vrfs[arg.Name]; !ok { + result.ResponseErr = fmt.Errorf("vrf %s not found", arg.Name) + break + } + var rf bgp.RouteFamily + switch req.RouteFamily { + case bgp.RF_IPv4_UC: + rf = bgp.RF_IPv4_VPN + case bgp.RF_IPv6_UC: + rf = bgp.RF_IPv6_VPN + case bgp.RF_EVPN: + rf = bgp.RF_EVPN + default: + result.ResponseErr = fmt.Errorf("unsupported route family: %s", req.RouteFamily) + break + } + for _, path := range rib.GetPathList(rf) { + ok := policy.CanImportToVrf(vrfs[arg.Name], path) + if !ok { + continue + } + req.ResponseCh <- &GrpcResponse{ + Data: path.ToApiStruct(), + } + } + goto END + case REQ_VRFS: + vrfs := server.localRibMap[GLOBAL_RIB_NAME].rib.Vrfs + for _, vrf := range vrfs { + req.ResponseCh <- &GrpcResponse{ + Data: vrf.ToApiStruct(), + } + } + goto END + case REQ_VRF_MOD: + arg := req.Data.(*api.ModVrfArguments) + result.ResponseErr = server.handleVrfMod(arg) + default: + result.ResponseErr = fmt.Errorf("unknown request type:", req.RequestType) + } + + req.ResponseCh <- result +END: + close(req.ResponseCh) + return msgs +} + func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg { - msgs := make([]*SenderMsg, 0) + var msgs []*SenderMsg switch grpcReq.RequestType { case REQ_GLOBAL_RIB: @@ -933,14 +1086,14 @@ func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg { } close(grpcReq.ResponseCh) - case REQ_GLOBAL_ADD, REQ_GLOBAL_DELETE: + case REQ_MOD_PATH: pi := &table.PeerInfo{ AS: server.bgpConfig.Global.GlobalConfig.As, LocalID: server.bgpConfig.Global.GlobalConfig.RouterId, } - pathList := server.handleGlobalRibRequest(grpcReq, pi) + pathList := server.handleModPathRequest(grpcReq, pi) if len(pathList) > 0 { - msgs = append(msgs, server.propagateUpdate("", false, pathList)...) + msgs = server.propagateUpdate("", false, pathList) grpcReq.ResponseCh <- &GrpcResponse{} close(grpcReq.ResponseCh) } @@ -1011,7 +1164,7 @@ func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg { break } m := bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN, nil) - msgs = append(msgs, newSenderMsg(peer, []*bgp.BGPMessage{m})) + msgs = []*SenderMsg{newSenderMsg(peer, []*bgp.BGPMessage{m})} grpcReq.ResponseCh <- &GrpcResponse{} close(grpcReq.ResponseCh) @@ -1022,7 +1175,7 @@ func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg { } peer.fsm.idleHoldTime = peer.conf.Timers.TimersConfig.IdleHoldTimeAfterReset m := bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_RESET, nil) - msgs = append(msgs, newSenderMsg(peer, []*bgp.BGPMessage{m})) + msgs = []*SenderMsg{newSenderMsg(peer, []*bgp.BGPMessage{m})} grpcReq.ResponseCh <- &GrpcResponse{} close(grpcReq.ResponseCh) @@ -1032,8 +1185,7 @@ func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg { break } pathList := peer.adjRib.GetInPathList(grpcReq.RouteFamily) - msgs = append(msgs, server.propagateUpdate(peer.conf.NeighborConfig.NeighborAddress.String(), - peer.isRouteServerClient(), pathList)...) + msgs = server.propagateUpdate(peer.conf.NeighborConfig.NeighborAddress.String(), peer.isRouteServerClient(), pathList) if grpcReq.RequestType == REQ_NEIGHBOR_SOFT_RESET_IN { grpcReq.ResponseCh <- &GrpcResponse{} @@ -1048,7 +1200,7 @@ func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg { } pathList := peer.adjRib.GetOutPathList(grpcReq.RouteFamily) msgList := table.CreateUpdateMsgFromPaths(pathList) - msgs = append(msgs, newSenderMsg(peer, msgList)) + msgs = []*SenderMsg{newSenderMsg(peer, msgList)} grpcReq.ResponseCh <- &GrpcResponse{} close(grpcReq.ResponseCh) @@ -1239,6 +1391,11 @@ func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg { server.handleMrt(grpcReq) case REQ_RPKI: server.roaClient.handleGRPC(grpcReq) + case REQ_VRF, REQ_VRFS, REQ_VRF_MOD: + pathList := server.handleVrfRequest(grpcReq) + if len(pathList) > 0 { + msgs = server.propagateUpdate("", false, pathList) + } default: errmsg := fmt.Errorf("Unknown request type: %v", grpcReq.RequestType) result := &GrpcResponse{ diff --git a/table/table_manager.go b/table/table_manager.go index 2b34f2c6..41630246 100644 --- a/table/table_manager.go +++ b/table/table_manager.go @@ -113,17 +113,20 @@ func ProcessMessage(m *bgp.BGPMessage, peerInfo *PeerInfo) []*Path { type TableManager struct { Tables map[bgp.RouteFamily]*Table + Vrfs map[string]*Vrf localAsn uint32 owner string } func NewTableManager(owner string, rfList []bgp.RouteFamily) *TableManager { - t := &TableManager{} - t.Tables = make(map[bgp.RouteFamily]*Table) + t := &TableManager{ + Tables: make(map[bgp.RouteFamily]*Table), + Vrfs: make(map[string]*Vrf), + owner: owner, + } for _, rf := range rfList { t.Tables[rf] = NewTable(rf) } - t.owner = owner return t } diff --git a/table/vrf.go b/table/vrf.go new file mode 100644 index 00000000..3833ecac --- /dev/null +++ b/table/vrf.go @@ -0,0 +1,46 @@ +// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package table + +import ( + "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/packet" +) + +type Vrf struct { + Name string + Rd bgp.RouteDistinguisherInterface + ImportRt []bgp.ExtendedCommunityInterface + ExportRt []bgp.ExtendedCommunityInterface +} + +func (v *Vrf) ToApiStruct() *api.Vrf { + f := func(rts []bgp.ExtendedCommunityInterface) [][]byte { + ret := make([][]byte, 0, len(rts)) + for _, rt := range rts { + b, _ := rt.Serialize() + ret = append(ret, b) + } + return ret + } + rd, _ := v.Rd.Serialize() + return &api.Vrf{ + Name: v.Name, + Rd: rd, + ImportRt: f(v.ImportRt), + ExportRt: f(v.ExportRt), + } +} |