summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorIWASE Yusuke <iwase.yusuke0@gmail.com>2017-08-25 15:05:39 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2017-11-02 00:14:09 +0900
commitd2a34c964aa0a43afc488f1d4497e4cd51a8c15a (patch)
tree2c86b9b69e9db679ced84a5813bc6ebbae04805c
parent1dc56d5a724a394f5db5cb75e305e8da286c52dc (diff)
zclient: Enable to connect to FRRouting
Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
-rw-r--r--config/default.go4
-rw-r--r--docs/sources/configuration.md1
-rw-r--r--docs/sources/zebra.md24
-rw-r--r--server/zclient.go136
-rw-r--r--server/zclient_test.go18
-rw-r--r--zebra/zapi.go58
6 files changed, 140 insertions, 101 deletions
diff --git a/config/default.go b/config/default.go
index c1a38ab6..41bcbd40 100644
--- a/config/default.go
+++ b/config/default.go
@@ -381,8 +381,10 @@ func setDefaultConfigValuesWithViper(v *viper.Viper, b *BgpConfigSet) error {
if b.Zebra.Config.Url == "" {
b.Zebra.Config.Url = "unix:/var/run/quagga/zserv.api"
}
- if b.Zebra.Config.Version < 2 || 3 > b.Zebra.Config.Version {
+ if b.Zebra.Config.Version < 2 {
b.Zebra.Config.Version = 2
+ } else if b.Zebra.Config.Version > 4 {
+ b.Zebra.Config.Version = 4
}
if !v.IsSet("zebra.config.nexthop-trigger-enable") && !b.Zebra.Config.NexthopTriggerEnable && b.Zebra.Config.Version > 2 {
b.Zebra.Config.NexthopTriggerEnable = true
diff --git a/docs/sources/configuration.md b/docs/sources/configuration.md
index e334a772..0c795649 100644
--- a/docs/sources/configuration.md
+++ b/docs/sources/configuration.md
@@ -41,6 +41,7 @@
enabled = true
url = "unix:/var/run/quagga/zserv.api"
redistribute-route-type-list = ["connect"]
+ version = 2 # version used in Quagga on Ubuntu 16.04
[[neighbors]]
[neighbors.config]
diff --git a/docs/sources/zebra.md b/docs/sources/zebra.md
index b5b431d1..6d3193a9 100644
--- a/docs/sources/zebra.md
+++ b/docs/sources/zebra.md
@@ -3,11 +3,12 @@
This page explains how to perform FIB manipulation; kernel routing
table updates, interface lookups, and redistribution of routes between
different routing protocols. GoBGP uses zebra included in
-[Quagga](http://www.nongnu.org/quagga/).
+[Quagga](http://www.nongnu.org/quagga/) or [FRRouting](https://frrouting.org/).
## Prerequisites
-Assume you finished [Getting Started](https://github.com/osrg/gobgp/blob/master/docs/sources/getting-started.md) and installing [Quagga](http://www.nongnu.org/quagga/) on the same host with GoBGP.
+Assume you finished [Getting Started](https://github.com/osrg/gobgp/blob/master/docs/sources/getting-started.md)
+and installing Quagga or FRRouting on the same host with GoBGP.
## Contents
- [Configuration](#section0)
@@ -22,13 +23,22 @@ You need to enable the zebra feature in the Global configuration as follows.
enabled = true
url = "unix:/var/run/quagga/zserv.api"
redistribute-route-type-list = ["connect"]
+ version = 2
```
-You can skip Url. If it's skipped, GoBGP uses "unix:/var/run/quagga/zserv.api" as the Url.
-This configuration specifies unix domain socket in its Url and you can change it to the one using TCP.
-If you use TCP, Url can be like "tcp:192.168.24.1:2600".
-Specify which route type you want to redistribute through bgp.
-Here gobgp will redistribute connected routes which zebra has.
+- `url` specifies the path to the unix domain socket or the TCP port for connecting to Zebra API.
+If omitted, GoBGP will use `"unix:/var/run/quagga/zserv.api"` by the default.
+Please note that with FRRouting, the path to the unix domain socket would be like
+`"unix:/var/run/frr/zserv.api"`.
+To specify the TCP port, `url` value would be like `"tcp:192.168.24.1:2600"`.
+
+- `redistribute-route-type-list` specifies which route types you want to receive from Zebra
+daemon. For example, with `["connect"]`, GoBGP will receive the connected routes and redistribute
+them.
+
+- `version` specifies Zebra API version. `2` is the version used by Quagga on Ubuntu 16.04 LTS.
+To enable the Next-Hop Tracking features, please specify `3` or later.
+For connecting to FRRouting, please specify `4`.
## <a name="section1">Check Routes from zebra
diff --git a/server/zclient.go b/server/zclient.go
index 89747fe3..0b541353 100644
--- a/server/zclient.go
+++ b/server/zclient.go
@@ -199,24 +199,18 @@ func filterOutExternalPath(paths pathList) pathList {
return filteredPaths
}
-func newIPRouteMessage(dst pathList, version uint8, vrfId uint16) *zebra.Message {
+func newIPRouteBody(dst pathList) (*zebra.IPRouteBody, bool) {
paths := filterOutExternalPath(dst)
if len(paths) == 0 {
- return nil
+ return nil, false
}
path := paths[0]
l := strings.SplitN(path.GetNlri().String(), "/", 2)
- var command zebra.API_TYPE
var prefix net.IP
nexthops := make([]net.IP, 0, len(paths))
switch path.GetRouteFamily() {
case bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN:
- if path.IsWithdraw == true {
- command = zebra.IPV4_ROUTE_DELETE
- } else {
- command = zebra.IPV4_ROUTE_ADD
- }
if path.GetRouteFamily() == bgp.RF_IPv4_UC {
prefix = path.GetNlri().(*bgp.IPAddrPrefix).IPAddrPrefixDefault.Prefix.To4()
} else {
@@ -226,11 +220,6 @@ func newIPRouteMessage(dst pathList, version uint8, vrfId uint16) *zebra.Message
nexthops = append(nexthops, p.GetNexthop().To4())
}
case bgp.RF_IPv6_UC, bgp.RF_IPv6_VPN:
- if path.IsWithdraw == true {
- command = zebra.IPV6_ROUTE_DELETE
- } else {
- command = zebra.IPV6_ROUTE_ADD
- }
if path.GetRouteFamily() == bgp.RF_IPv6_UC {
prefix = path.GetNlri().(*bgp.IPv6AddrPrefix).IPAddrPrefixDefault.Prefix.To16()
} else {
@@ -240,7 +229,7 @@ func newIPRouteMessage(dst pathList, version uint8, vrfId uint16) *zebra.Message
nexthops = append(nexthops, p.GetNexthop().To16())
}
default:
- return nil
+ return nil, false
}
msgFlags := zebra.MESSAGE_NEXTHOP
plen, _ := strconv.Atoi(l[1])
@@ -255,50 +244,38 @@ func newIPRouteMessage(dst pathList, version uint8, vrfId uint16) *zebra.Message
} else if info.MultihopTtl > 0 {
flags = zebra.FLAG_INTERNAL
}
- return &zebra.Message{
- Header: zebra.Header{
- Len: zebra.HeaderSize(version),
- Marker: zebra.HEADER_MARKER,
- Version: version,
- Command: command,
- VrfId: vrfId,
- },
- Body: &zebra.IPRouteBody{
- Type: zebra.ROUTE_BGP,
- Flags: flags,
- SAFI: zebra.SAFI_UNICAST,
- Message: msgFlags,
- Prefix: prefix,
- PrefixLength: uint8(plen),
- Nexthops: nexthops,
- Metric: med,
- },
- }
+ return &zebra.IPRouteBody{
+ Type: zebra.ROUTE_BGP,
+ Flags: flags,
+ SAFI: zebra.SAFI_UNICAST,
+ Message: msgFlags,
+ Prefix: prefix,
+ PrefixLength: uint8(plen),
+ Nexthops: nexthops,
+ Metric: med,
+ }, path.IsWithdraw
}
-func newNexthopRegisterMessage(dst pathList, version uint8, vrfId uint16, nhtManager *nexthopTrackingManager) *zebra.Message {
- // Note: NEXTHOP_REGISTER and NEXTHOP_UNREGISTER messages are not
- // supported in Zebra protocol version<3.
- if version < 3 || nhtManager == nil {
- return nil
+func newNexthopRegisterBody(dst pathList, nhtManager *nexthopTrackingManager) (*zebra.NexthopRegisterBody, bool) {
+ if nhtManager == nil {
+ return nil, false
}
paths := filterOutNilPath(dst)
if len(paths) == 0 {
- return nil
+ return nil, false
}
+ path := paths[0]
- route_family := paths[0].GetRouteFamily()
- command := zebra.NEXTHOP_REGISTER
- if paths[0].IsWithdraw == true {
+ if path.IsWithdraw == true {
// TODO:
// Send NEXTHOP_UNREGISTER message if the given nexthop is no longer
// referred by any path. Currently, do not send NEXTHOP_UNREGISTER
// message to simplify the implementation.
- //command = zebra.NEXTHOP_UNREGISTER
- return nil
+ return nil, true
}
+ family := path.GetRouteFamily()
nexthops := make([]*zebra.RegisteredNexthop, 0, len(paths))
for _, p := range paths {
nexthop := p.GetNexthop()
@@ -312,7 +289,7 @@ func newNexthopRegisterMessage(dst pathList, version uint8, vrfId uint16, nhtMan
}
var nh *zebra.RegisteredNexthop
- switch route_family {
+ switch family {
case bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN:
nh = &zebra.RegisteredNexthop{
Family: syscall.AF_INET,
@@ -324,7 +301,7 @@ func newNexthopRegisterMessage(dst pathList, version uint8, vrfId uint16, nhtMan
Prefix: nexthop.To16(),
}
default:
- return nil
+ return nil, path.IsWithdraw
}
nexthops = append(nexthops, nh)
nhtManager.registerNexthop(nexthop)
@@ -333,36 +310,22 @@ func newNexthopRegisterMessage(dst pathList, version uint8, vrfId uint16, nhtMan
// If no nexthop needs to be registered or unregistered,
// skips to send message.
if len(nexthops) == 0 {
- return nil
+ return nil, path.IsWithdraw
}
- return &zebra.Message{
- Header: zebra.Header{
- Len: zebra.HeaderSize(version),
- Marker: zebra.HEADER_MARKER,
- Version: version,
- Command: command,
- VrfId: vrfId,
- },
- Body: &zebra.NexthopRegisterBody{
- Nexthops: nexthops,
- },
- }
+ return &zebra.NexthopRegisterBody{
+ Nexthops: nexthops,
+ }, path.IsWithdraw
}
func createPathFromIPRouteMessage(m *zebra.Message) *table.Path {
-
header := m.Header
body := m.Body.(*zebra.IPRouteBody)
- family := bgp.RF_IPv6_UC
- if header.Command == zebra.IPV4_ROUTE_ADD || header.Command == zebra.IPV4_ROUTE_DELETE {
- family = bgp.RF_IPv4_UC
- }
+ family := body.RouteFamily()
+ isWithdraw := body.IsWithdraw()
var nlri bgp.AddrPrefixInterface
pattr := make([]bgp.PathAttributeInterface, 0)
- var isWithdraw bool = header.Command == zebra.IPV4_ROUTE_DELETE || header.Command == zebra.IPV6_ROUTE_DELETE
-
origin := bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP)
pattr = append(pattr, origin)
@@ -468,7 +431,7 @@ func (z *zebraClient) loop() {
case <-z.dead:
return
case msg := <-z.client.Receive():
- switch msg.Body.(type) {
+ switch body := msg.Body.(type) {
case *zebra.IPRouteBody:
if p := createPathFromIPRouteMessage(msg); p != nil {
if _, err := z.server.AddPath("", pathList{p}); err != nil {
@@ -477,7 +440,6 @@ func (z *zebraClient) loop() {
}
case *zebra.NexthopUpdateBody:
if z.nhtManager != nil {
- body := msg.Body.(*zebra.NexthopUpdateBody)
if paths, err := createPathListFromNexthopUpdateMessage(msg, z.server.globalRib); err != nil {
log.Errorf("failed to create updated path list related to nexthop %s", body.Prefix.String())
} else {
@@ -489,11 +451,11 @@ func (z *zebraClient) loop() {
msg := ev.(*WatchEventBestPath)
if table.UseMultiplePaths.Enabled {
for _, dst := range msg.MultiPathList {
- if m := newIPRouteMessage(dst, z.client.Version, 0); m != nil {
- z.client.Send(m)
+ if body, isDelete := newIPRouteBody(dst); body != nil {
+ z.client.SendIPRoute(0, body, isDelete)
}
- if m := newNexthopRegisterMessage(dst, z.client.Version, 0, z.nhtManager); m != nil {
- z.client.Send(m)
+ if body, isUnregister := newNexthopRegisterBody(dst, z.nhtManager); body != nil {
+ z.client.SendNexthopRegister(0, body, isUnregister)
}
}
} else {
@@ -502,11 +464,11 @@ func (z *zebraClient) loop() {
path.VrfIds = []uint16{0}
}
for _, i := range path.VrfIds {
- if m := newIPRouteMessage(pathList{path}, z.client.Version, i); m != nil {
- z.client.Send(m)
+ if body, isWithdraw := newIPRouteBody(pathList{path}); body != nil {
+ z.client.SendIPRoute(i, body, isWithdraw)
}
- if m := newNexthopRegisterMessage(pathList{path}, z.client.Version, i, z.nhtManager); m != nil {
- z.client.Send(m)
+ if body, isWithdraw := newNexthopRegisterBody(pathList{path}, z.nhtManager); body != nil {
+ z.client.SendNexthopRegister(i, body, isWithdraw)
}
}
}
@@ -520,20 +482,20 @@ func newZebraClient(s *BgpServer, url string, protos []string, version uint8, nh
if len(l) != 2 {
return nil, fmt.Errorf("unsupported url: %s", url)
}
- cli, err := zebra.NewClient(l[0], l[1], zebra.ROUTE_BGP, version)
- if err != nil {
- // Retry with another Zebra message version
- var retry_version uint8 = 2
- if version == 2 {
- retry_version = 3
+ var cli *zebra.Client
+ var err error
+ for _, ver := range []uint8{version, 2, 3, 4} {
+ cli, err = zebra.NewClient(l[0], l[1], zebra.ROUTE_BGP, ver)
+ if err == nil {
+ break
}
+ // Retry with another Zebra message version
log.WithFields(log.Fields{
"Topic": "Zebra",
- }).Warnf("cannot connect to Zebra with message version %d. retry with version %d", version, retry_version)
- cli, err = zebra.NewClient(l[0], l[1], zebra.ROUTE_BGP, retry_version)
- if err != nil {
- return nil, err
- }
+ }).Warnf("cannot connect to Zebra with message version %d. going to retry another version...", ver)
+ }
+ if cli == nil {
+ return nil, err
}
// Note: HELLO/ROUTER_ID_ADD messages are automatically sent to negotiate
// the Zebra message version in zebra.NewClient().
diff --git a/server/zclient_test.go b/server/zclient_test.go
index d15155c9..ac20a0f4 100644
--- a/server/zclient_test.go
+++ b/server/zclient_test.go
@@ -24,9 +24,10 @@ import (
"time"
)
-func Test_createRequestFromIPRouteMessage(t *testing.T) {
+func Test_createPathFromIPRouteMessage(t *testing.T) {
assert := assert.New(t)
+ // IPv4 Route Add
m := &zebra.Message{}
h := &zebra.Header{
Len: zebra.HeaderSize(2),
@@ -34,7 +35,6 @@ func Test_createRequestFromIPRouteMessage(t *testing.T) {
Version: 2,
Command: zebra.IPV4_ROUTE_ADD,
}
-
b := &zebra.IPRouteBody{
Type: zebra.ROUTE_TYPE(zebra.ROUTE_STATIC),
Flags: zebra.FLAG(zebra.FLAG_SELECTED),
@@ -49,7 +49,6 @@ func Test_createRequestFromIPRouteMessage(t *testing.T) {
Mtu: uint32(0),
Api: zebra.API_TYPE(zebra.IPV4_ROUTE_ADD),
}
-
m.Header = *h
m.Body = b
@@ -61,9 +60,12 @@ func Test_createRequestFromIPRouteMessage(t *testing.T) {
assert.True(pp.IsFromExternal())
assert.False(pp.IsWithdraw)
- // withdraw
+ // IPv4 Route Delete
h.Command = zebra.IPV4_ROUTE_DELETE
+ b.Api = zebra.IPV4_ROUTE_DELETE
m.Header = *h
+ m.Body = b
+
path = createPathFromIPRouteMessage(m)
pp = table.NewPath(nil, path.GetNlri(), path.IsWithdraw, path.GetPathAttrs(), time.Now(), false)
pp.SetIsFromExternal(path.IsFromExternal())
@@ -74,8 +76,9 @@ func Test_createRequestFromIPRouteMessage(t *testing.T) {
assert.True(pp.IsFromExternal())
assert.True(pp.IsWithdraw)
- // IPv6
+ // IPv6 Route Add
h.Command = zebra.IPV6_ROUTE_ADD
+ b.Api = zebra.IPV6_ROUTE_ADD
b.Prefix = net.ParseIP("2001:db8:0:f101::")
b.PrefixLength = uint8(64)
b.Nexthops = []net.IP{net.ParseIP("::")}
@@ -92,9 +95,12 @@ func Test_createRequestFromIPRouteMessage(t *testing.T) {
assert.True(pp.IsFromExternal())
assert.False(pp.IsWithdraw)
- // withdraw
+ // IPv6 Route Delete
h.Command = zebra.IPV6_ROUTE_DELETE
+ b.Api = zebra.IPV6_ROUTE_DELETE
m.Header = *h
+ m.Body = b
+
path = createPathFromIPRouteMessage(m)
pp = table.NewPath(nil, path.GetNlri(), path.IsWithdraw, path.GetPathAttrs(), time.Now(), false)
pp.SetIsFromExternal(path.IsFromExternal())
diff --git a/zebra/zapi.go b/zebra/zapi.go
index 6998bd4e..e9e91015 100644
--- a/zebra/zapi.go
+++ b/zebra/zapi.go
@@ -23,6 +23,7 @@ import (
"strings"
"syscall"
+ "github.com/osrg/gobgp/packet/bgp"
log "github.com/sirupsen/logrus"
)
@@ -751,6 +752,43 @@ func (c *Client) SendRedistributeDelete(t ROUTE_TYPE) error {
return nil
}
+func (c *Client) SendIPRoute(vrfId uint16, body *IPRouteBody, isWithdraw bool) error {
+ command := IPV4_ROUTE_ADD
+ if c.Version <= 3 {
+ if isWithdraw {
+ command = IPV4_ROUTE_DELETE
+ }
+ } else { // version >= 4
+ if isWithdraw {
+ command = FRR_IPV4_ROUTE_DELETE
+ } else {
+ command = FRR_IPV4_ROUTE_ADD
+ }
+ }
+ return c.SendCommand(command, vrfId, body)
+}
+
+func (c *Client) SendNexthopRegister(vrfId uint16, body *NexthopRegisterBody, isWithdraw bool) error {
+ // Note: NEXTHOP_REGISTER and NEXTHOP_UNREGISTER messages are not
+ // supported in Zebra protocol version<3.
+ if c.Version < 3 {
+ return fmt.Errorf("NEXTHOP_REGISTER/NEXTHOP_UNREGISTER are not supported in version: %d", c.Version)
+ }
+ command := NEXTHOP_REGISTER
+ if c.Version == 3 {
+ if isWithdraw {
+ command = NEXTHOP_UNREGISTER
+ }
+ } else { // version >= 4
+ if isWithdraw {
+ command = FRR_NEXTHOP_UNREGISTER
+ } else {
+ command = FRR_NEXTHOP_REGISTER
+ }
+ }
+ return c.SendCommand(command, vrfId, body)
+}
+
func (c *Client) Close() error {
close(c.outgoing)
return c.conn.Close()
@@ -1046,6 +1084,26 @@ type IPRouteBody struct {
Api API_TYPE
}
+func (b *IPRouteBody) RouteFamily() bgp.RouteFamily {
+ switch b.Api {
+ case IPV4_ROUTE_ADD, IPV4_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV4_ADD, FRR_REDISTRIBUTE_IPV4_DEL:
+ return bgp.RF_IPv4_UC
+ case IPV6_ROUTE_ADD, IPV6_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV6_ADD, FRR_REDISTRIBUTE_IPV6_DEL:
+ return bgp.RF_IPv6_UC
+ default:
+ return bgp.RF_OPAQUE
+ }
+}
+
+func (b *IPRouteBody) IsWithdraw() bool {
+ switch b.Api {
+ case IPV4_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV4_DEL, IPV6_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV6_DEL:
+ return true
+ default:
+ return false
+ }
+}
+
func (b *IPRouteBody) Serialize(version uint8) ([]byte, error) {
var buf []byte