diff options
author | IWASE Yusuke <iwase.yusuke0@gmail.com> | 2017-03-22 14:03:41 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2017-03-27 14:43:54 +0900 |
commit | f6648758080cbe139a4345a384243ede52bec6bf (patch) | |
tree | 9c3f046f48cf4f98a7841dc3c3f59d4186d6392d | |
parent | 61240f45b0e2d09e251dbd4176a151964e98f696 (diff) |
server/zclient: Retry Zebra message version negotiation
Currently, the Zebra message version used by ZClient is configurable
(default 2), but if the given version is miss-matched with that of
Zebra daemon, ZCient will fail to connect.
This patch fixes ZClient to retry the version negotiation.
For example, if failed with the version 2, retry with the version 3.
Note: In order to receive the first message from Zebra daemon when
instantiating ZClient, this patch fixes ZClient to send HELLO and
ROUTER_ID_ADD messages automatically.
Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
-rw-r--r-- | server/zclient.go | 19 | ||||
-rw-r--r-- | zebra/zapi.go | 93 |
2 files changed, 75 insertions, 37 deletions
diff --git a/server/zclient.go b/server/zclient.go index 4bba83ec..d432b96d 100644 --- a/server/zclient.go +++ b/server/zclient.go @@ -519,10 +519,23 @@ func newZebraClient(s *BgpServer, url string, protos []string, version uint8, nh } cli, err := zebra.NewClient(l[0], l[1], zebra.ROUTE_BGP, version) if err != nil { - return nil, err + // Retry with another Zebra message version + var retry_version uint8 = 2 + if version == 2 { + retry_version = 3 + } + 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 + } } - cli.SendHello() - cli.SendRouterIDAdd() + // Note: HELLO/ROUTER_ID_ADD messages are automatically sent to negotiate + // the Zebra message version in zebra.NewClient(). + // cli.SendHello() + // cli.SendRouterIDAdd() cli.SendInterfaceAdd() for _, typ := range protos { t, err := zebra.RouteTypeFromString(typ) diff --git a/zebra/zapi.go b/zebra/zapi.go index 74d17f28..f4b8615e 100644 --- a/zebra/zapi.go +++ b/zebra/zapi.go @@ -332,46 +332,71 @@ func NewClient(network, address string, typ ROUTE_TYPE, version uint8) (*Client, } }() - go func() { - for { - headerBuf, err := readAll(conn, int(HeaderSize(version))) - if err != nil { - log.WithFields(log.Fields{ - "Topic": "Zebra", - }).Errorf("failed to read header:%s", err) - return - } + // Send HELLO/ROUTER_ID_ADD messages to negotiate the Zebra message version. + c.SendHello() + c.SendRouterIDAdd() + + receiveSingleMsg := func() (*Message, error) { + headerBuf, err := readAll(conn, int(HeaderSize(version))) + if err != nil { + err = fmt.Errorf("failed to read header: %s", err) log.WithFields(log.Fields{ "Topic": "Zebra", - }).Debugf("read header from zebra: %v", headerBuf) - hd := &Header{} - err = hd.DecodeFromBytes(headerBuf) - if err != nil { - log.WithFields(log.Fields{ - "Topic": "Zebra", - }).Errorf("failed to decode header:%s", err) - return - } + }).Error(err) + return nil, err + } + log.WithFields(log.Fields{ + "Topic": "Zebra", + }).Debugf("read header from zebra: %v", headerBuf) + hd := &Header{} + err = hd.DecodeFromBytes(headerBuf) + if err != nil { + err = fmt.Errorf("failed to decode header: %s", err) + log.WithFields(log.Fields{ + "Topic": "Zebra", + }).Error(err) + return nil, err + } - bodyBuf, err := readAll(conn, int(hd.Len-HeaderSize(version))) - if err != nil { - log.WithFields(log.Fields{ - "Topic": "Zebra", - }).Errorf("failed to read body:%s", err) - return - } + bodyBuf, err := readAll(conn, int(hd.Len-HeaderSize(version))) + if err != nil { + err = fmt.Errorf("failed to read body: %s", err) log.WithFields(log.Fields{ "Topic": "Zebra", - }).Debugf("read body from zebra: %v", bodyBuf) - m, err := ParseMessage(hd, bodyBuf) - if err != nil { - log.WithFields(log.Fields{ - "Topic": "Zebra", - }).Warnf("failed to parse message:%s", err) - continue - } + }).Error(err) + return nil, err + } + log.WithFields(log.Fields{ + "Topic": "Zebra", + }).Debugf("read body from zebra: %v", bodyBuf) + m, err := ParseMessage(hd, bodyBuf) + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Zebra", + }).Warnf("failed to parse message: %s", err) + return nil, nil + } - incoming <- m + return m, nil + } + + // Try to receive the first message from Zebra. + if m, err := receiveSingleMsg(); err != nil { + c.Close() + // Return error explicitly in order to retry connection. + return nil, err + } else if m != nil { + incoming <- m + } + + // Start receive loop only when the first message successfully received. + go func() { + for { + if m, err := receiveSingleMsg(); err != nil { + return + } else if m != nil { + incoming <- m + } } }() |