summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>2016-04-11 05:09:13 +0000
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2016-04-20 13:14:09 +0900
commit802879087ddf14da1c2dab90d05dd09debb747a7 (patch)
tree564416d955485fdaed137c0836a6a66907ed10b9
parentd62e65f4fe06deca5c4ce7dee957a9b517b80f12 (diff)
server: support route-target constraint
Signed-off-by: ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>
-rw-r--r--config/bgp_configs.go21
-rw-r--r--config/util.go9
-rw-r--r--gobgp/cmd/common.go4
-rw-r--r--server/server.go230
-rw-r--r--test/lib/base.py4
-rw-r--r--test/lib/gobgp.py8
-rw-r--r--test/scenario_test/evpn_test.py6
-rw-r--r--test/scenario_test/rtc_test.py94
-rw-r--r--tools/pyang_plugins/gobgp.yang8
9 files changed, 320 insertions, 64 deletions
diff --git a/config/bgp_configs.go b/config/bgp_configs.go
index 47d070b4..1b3d2a73 100644
--- a/config/bgp_configs.go
+++ b/config/bgp_configs.go
@@ -2597,6 +2597,22 @@ func (lhs *Collector) Equal(rhs *Collector) bool {
return true
}
+//struct for container gobgp:route-target-membership
+type RouteTargetMembership struct {
+ // original -> gobgp:deferral-time
+ DeferralTime uint16 `mapstructure:"deferral-time"`
+}
+
+func (lhs *RouteTargetMembership) Equal(rhs *RouteTargetMembership) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.DeferralTime != rhs.DeferralTime {
+ return false
+ }
+ return true
+}
+
//struct for container bgp-mp:l2vpn-evpn
type L2vpnEvpn struct {
// original -> bgp-mp:prefix-limit
@@ -3224,6 +3240,8 @@ type AfiSafi struct {
UseMultiplePaths UseMultiplePaths `mapstructure:"use-multiple-paths"`
// original -> bgp-mp:prefix-limit
PrefixLimit PrefixLimit `mapstructure:"prefix-limit"`
+ // original -> gobgp:route-target-membership
+ RouteTargetMembership RouteTargetMembership `mapstructure:"route-target-membership"`
}
func (lhs *AfiSafi) Equal(rhs *AfiSafi) bool {
@@ -3284,6 +3302,9 @@ func (lhs *AfiSafi) Equal(rhs *AfiSafi) bool {
if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) {
return false
}
+ if !lhs.RouteTargetMembership.Equal(&(rhs.RouteTargetMembership)) {
+ return false
+ }
return true
}
diff --git a/config/util.go b/config/util.go
index 47edc98f..8d7d2546 100644
--- a/config/util.go
+++ b/config/util.go
@@ -57,3 +57,12 @@ func CreateRfMap(p *Neighbor) map[bgp.RouteFamily]bool {
}
return rfMap
}
+
+func GetAfiSafi(p *Neighbor, family bgp.RouteFamily) *AfiSafi {
+ for _, a := range p.AfiSafis {
+ if string(a.AfiSafiName) == family.String() {
+ return &a
+ }
+ }
+ return nil
+}
diff --git a/gobgp/cmd/common.go b/gobgp/cmd/common.go
index 1a04a3aa..98e493af 100644
--- a/gobgp/cmd/common.go
+++ b/gobgp/cmd/common.go
@@ -440,9 +440,9 @@ func checkAddressFamily(def bgp.RouteFamily) (bgp.RouteFamily, error) {
rf = bgp.RF_IPv4_UC
case "ipv6", "v6", "6":
rf = bgp.RF_IPv6_UC
- case "vpnv4", "vpn-ipv4":
+ case "ipv4-l3vpn", "vpnv4", "vpn-ipv4":
rf = bgp.RF_IPv4_VPN
- case "vpnv6", "vpn-ipv6":
+ case "ipv6-l3vpn", "vpnv6", "vpn-ipv6":
rf = bgp.RF_IPv6_VPN
case "ipv4-labeled", "ipv4-labelled", "ipv4-mpls":
rf = bgp.RF_IPv4_MPLS
diff --git a/server/server.go b/server/server.go
index 1c73f4d9..a182c26b 100644
--- a/server/server.go
+++ b/server/server.go
@@ -399,64 +399,81 @@ func filterpath(peer *Peer, path *table.Path) *table.Path {
return nil
}
- remoteAddr := peer.fsm.pConf.Config.NeighborAddress
-
//iBGP handling
- if !path.IsLocal() && peer.isIBGPPeer() {
- ignore := true
- info := path.GetSource()
-
- //if the path comes from eBGP peer
- if info.AS != peer.fsm.pConf.Config.PeerAs {
- ignore = false
- }
- // RFC4456 8. Avoiding Routing Information Loops
- // A router that recognizes the ORIGINATOR_ID attribute SHOULD
- // ignore a route received with its BGP Identifier as the ORIGINATOR_ID.
- if id := path.GetOriginatorID(); peer.fsm.gConf.Config.RouterId == id.String() {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": remoteAddr,
- "OriginatorID": id,
- "Data": path,
- }).Debug("Originator ID is mine, ignore")
- return nil
- }
- if info.RouteReflectorClient {
- ignore = false
+ if peer.isIBGPPeer() {
+ ignore := false
+ //RFC4684 Constrained Route Distribution
+ if peer.fsm.rfMap[bgp.RF_RTC_UC] && path.GetRouteFamily() != bgp.RF_RTC_UC {
+ ignore = true
+ for _, ext := range path.GetExtCommunities() {
+ for _, path := range peer.adjRibIn.PathList([]bgp.RouteFamily{bgp.RF_RTC_UC}, true) {
+ rt := path.GetNlri().(*bgp.RouteTargetMembershipNLRI).RouteTarget
+ if ext.String() == rt.String() {
+ ignore = false
+ break
+ }
+ }
+ if !ignore {
+ break
+ }
+ }
}
- if peer.isRouteReflectorClient() {
+
+ if !path.IsLocal() {
+ ignore = true
+ info := path.GetSource()
+ //if the path comes from eBGP peer
+ if info.AS != peer.fsm.pConf.Config.PeerAs {
+ ignore = false
+ }
// RFC4456 8. Avoiding Routing Information Loops
- // If the local CLUSTER_ID is found in the CLUSTER_LIST,
- // the advertisement received SHOULD be ignored.
- for _, clusterId := range path.GetClusterList() {
- if clusterId.Equal(peer.fsm.peerInfo.RouteReflectorClusterID) {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": remoteAddr,
- "ClusterID": clusterId,
- "Data": path,
- }).Debug("cluster list path attribute has local cluster id, ignore")
- return nil
+ // A router that recognizes the ORIGINATOR_ID attribute SHOULD
+ // ignore a route received with its BGP Identifier as the ORIGINATOR_ID.
+ if id := path.GetOriginatorID(); peer.fsm.gConf.Config.RouterId == id.String() {
+ log.WithFields(log.Fields{
+ "Topic": "Peer",
+ "Key": peer.ID(),
+ "OriginatorID": id,
+ "Data": path,
+ }).Debug("Originator ID is mine, ignore")
+ return nil
+ }
+ if info.RouteReflectorClient {
+ ignore = false
+ }
+ if peer.isRouteReflectorClient() {
+ // RFC4456 8. Avoiding Routing Information Loops
+ // If the local CLUSTER_ID is found in the CLUSTER_LIST,
+ // the advertisement received SHOULD be ignored.
+ for _, clusterId := range path.GetClusterList() {
+ if clusterId.Equal(peer.fsm.peerInfo.RouteReflectorClusterID) {
+ log.WithFields(log.Fields{
+ "Topic": "Peer",
+ "Key": peer.ID(),
+ "ClusterID": clusterId,
+ "Data": path,
+ }).Debug("cluster list path attribute has local cluster id, ignore")
+ return nil
+ }
}
+ ignore = false
}
- ignore = false
}
if ignore {
log.WithFields(log.Fields{
"Topic": "Peer",
- "Key": remoteAddr,
+ "Key": peer.ID(),
"Data": path,
}).Debug("From same AS, ignore.")
return nil
}
}
- if remoteAddr == path.GetSource().Address.String() {
+ if peer.ID() == path.GetSource().Address.String() {
log.WithFields(log.Fields{
"Topic": "Peer",
- "Key": remoteAddr,
+ "Key": peer.ID(),
"Data": path,
}).Debug("From me, ignore.")
return nil
@@ -717,6 +734,7 @@ func (server *BgpServer) propagateUpdate(peer *Peer, pathList []*table.Path) ([]
rib := server.globalRib
var alteredPathList, newly, withdrawn []*table.Path
var best map[string][]*table.Path
+ msgs := make([]*SenderMsg, 0, len(server.neighborMap))
if peer != nil && peer.isRouteServerClient() {
for _, path := range pathList {
@@ -744,7 +762,48 @@ func (server *BgpServer) propagateUpdate(peer *Peer, pathList []*table.Path) ([]
server.validatePaths(newly, withdrawn, false)
} else {
for idx, path := range pathList {
- pathList[idx] = server.policy.ApplyPolicy(table.GLOBAL_RIB_NAME, table.POLICY_DIRECTION_IMPORT, path, nil)
+ path = server.policy.ApplyPolicy(table.GLOBAL_RIB_NAME, table.POLICY_DIRECTION_IMPORT, path, nil)
+ pathList[idx] = path
+ // RFC4684 Constrained Route Distribution 6. Operation
+ //
+ // When a BGP speaker receives a BGP UPDATE that advertises or withdraws
+ // a given Route Target membership NLRI, it should examine the RIB-OUTs
+ // of VPN NLRIs and re-evaluate the advertisement status of routes that
+ // match the Route Target in question.
+ //
+ // A BGP speaker should generate the minimum set of BGP VPN route
+ // updates (advertisements and/or withdrawls) necessary to transition
+ // between the previous and current state of the route distribution
+ // graph that is derived from Route Target membership information.
+ if peer != nil && path != nil && path.GetRouteFamily() == bgp.RF_RTC_UC {
+ rt := path.GetNlri().(*bgp.RouteTargetMembershipNLRI).RouteTarget
+ fs := make([]bgp.RouteFamily, 0, len(peer.configuredRFlist()))
+ for _, f := range peer.configuredRFlist() {
+ if f != bgp.RF_RTC_UC {
+ fs = append(fs, f)
+ }
+ }
+ var candidates []*table.Path
+ if path.IsWithdraw {
+ candidates = peer.adjRibOut.PathList(fs, false)
+ } else {
+ candidates = rib.GetBestPathList(peer.TableID(), fs)
+ }
+ paths := make([]*table.Path, 0, len(pathList))
+ for _, p := range candidates {
+ t := false
+ for _, ext := range p.GetExtCommunities() {
+ if ext.String() == rt.String() {
+ t = true
+ break
+ }
+ }
+ if t {
+ paths = append(paths, p.Clone(path.IsWithdraw))
+ }
+ }
+ msgs = append(msgs, newSenderMsg(peer, paths, nil, false))
+ }
}
alteredPathList = pathList
best, newly, withdrawn = rib.ProcessPaths([]string{table.GLOBAL_RIB_NAME}, pathList)
@@ -757,7 +816,6 @@ func (server *BgpServer) propagateUpdate(peer *Peer, pathList []*table.Path) ([]
}
}
- msgs := make([]*SenderMsg, 0, len(server.neighborMap))
for _, targetPeer := range server.neighborMap {
if (peer == nil && targetPeer.isRouteServerClient()) || (peer != nil && peer.isRouteServerClient() != targetPeer.isRouteServerClient()) {
continue
@@ -811,8 +869,36 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) []*SenderMsg {
// update for export policy
laddr, _ := peer.fsm.LocalHostPort()
peer.fsm.pConf.Transport.Config.LocalAddress = laddr
+ deferralExpiredFunc := func(family bgp.RouteFamily) func() {
+ return func() {
+ req := NewGrpcRequest(REQ_DEFERRAL_TIMER_EXPIRED, peer.ID(), family, nil)
+ server.GrpcReqCh <- req
+ <-req.ResponseCh
+ }
+ }
if !peer.fsm.pConf.GracefulRestart.State.LocalRestarting {
- pathList, _ := peer.getBestFromLocal(peer.configuredRFlist())
+ // When graceful-restart cap (which means intention
+ // of sending EOR) and route-target address family are negotiated,
+ // send route-target NLRIs first, and wait to send others
+ // till receiving EOR of route-target address family.
+ // This prevents sending uninterested routes to peers.
+ //
+ // However, when the peer is graceful restarting, give up
+ // waiting sending non-route-target NLRIs since the peer won't send
+ // any routes (and EORs) before we send ours (or deferral-timer expires).
+ var pathList []*table.Path
+ if c := config.GetAfiSafi(peer.fsm.pConf, bgp.RF_RTC_UC); !peer.fsm.pConf.GracefulRestart.State.PeerRestarting && peer.fsm.rfMap[bgp.RF_RTC_UC] && c.RouteTargetMembership.DeferralTime > 0 {
+ pathList, _ = peer.getBestFromLocal([]bgp.RouteFamily{bgp.RF_RTC_UC})
+ t := c.RouteTargetMembership.DeferralTime
+ for _, f := range peer.configuredRFlist() {
+ if f != bgp.RF_RTC_UC {
+ time.AfterFunc(time.Second*time.Duration(t), deferralExpiredFunc(f))
+ }
+ }
+ } else {
+ pathList, _ = peer.getBestFromLocal(peer.configuredRFlist())
+ }
+
if len(pathList) > 0 {
peer.adjRibOut.Update(pathList)
msgs = []*SenderMsg{newSenderMsg(peer, pathList, nil, false)}
@@ -827,13 +913,9 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) []*SenderMsg {
deferral := peer.fsm.pConf.GracefulRestart.Config.DeferralTime
log.WithFields(log.Fields{
"Topic": "Peer",
- "Key": peer.fsm.pConf.Config.NeighborAddress,
+ "Key": peer.ID(),
}).Debugf("now syncing, suppress sending updates. start deferral timer(%d)", deferral)
- time.AfterFunc(time.Second*time.Duration(deferral), func() {
- req := NewGrpcRequest(REQ_DEFERRAL_TIMER_EXPIRED, peer.fsm.pConf.Config.NeighborAddress, bgp.RouteFamily(0), nil)
- server.GrpcReqCh <- req
- <-req.ResponseCh
- })
+ time.AfterFunc(time.Second*time.Duration(deferral), deferralExpiredFunc(bgp.RouteFamily(0)))
}
} else {
if server.shutdown && nextState == bgp.BGP_FSM_IDLE {
@@ -915,7 +997,11 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) []*SenderMsg {
}
if len(eor) > 0 {
+ rtc := false
for _, f := range eor {
+ if f == bgp.RF_RTC_UC {
+ rtc = true
+ }
for i, a := range peer.fsm.pConf.AfiSafis {
if g, _ := bgp.GetRouteFamily(string(a.AfiSafiName)); f == g {
peer.fsm.pConf.AfiSafis[i].MpGracefulRestart.State.EndOfRibReceived = true
@@ -954,7 +1040,11 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) []*SenderMsg {
log.WithFields(log.Fields{
"Topic": "Server",
}).Info("sync finished")
+
}
+
+ // we don't delay non-route-target NLRIs when local-restarting
+ rtc = false
}
if peer.fsm.pConf.GracefulRestart.State.PeerRestarting {
if peer.recvedAllEOR() {
@@ -967,6 +1057,28 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) []*SenderMsg {
m, _ := server.propagateUpdate(peer, pathList)
msgs = append(msgs, m...)
}
+
+ // we don't delay non-route-target NLRIs when peer is restarting
+ rtc = false
+ }
+
+ // received EOR of route-target address family
+ // outbound filter is now ready, let's flash non-route-target NLRIs
+ if c := config.GetAfiSafi(peer.fsm.pConf, bgp.RF_RTC_UC); rtc && c != nil && c.RouteTargetMembership.DeferralTime > 0 {
+ log.WithFields(log.Fields{
+ "Topic": "Peer",
+ "Key": peer.ID(),
+ }).Debug("received route-target eor. flash non-route-target NLRIs")
+ families := make([]bgp.RouteFamily, 0, len(peer.configuredRFlist()))
+ for _, f := range peer.configuredRFlist() {
+ if f != bgp.RF_RTC_UC {
+ families = append(families, f)
+ }
+ }
+ if paths, _ := peer.getBestFromLocal(families); len(paths) > 0 {
+ peer.adjRibOut.Update(paths)
+ msgs = append(msgs, newSenderMsg(peer, paths, nil, false))
+ }
}
}
default:
@@ -2057,22 +2169,30 @@ func (server *BgpServer) handleGrpc(grpcReq *GrpcRequest) []*SenderMsg {
continue
}
+ families := []bgp.RouteFamily{grpcReq.RouteFamily}
+ if families[0] == bgp.RouteFamily(0) {
+ families = peer.configuredRFlist()
+ }
+
if grpcReq.RequestType == REQ_DEFERRAL_TIMER_EXPIRED {
if peer.fsm.pConf.GracefulRestart.State.LocalRestarting {
peer.fsm.pConf.GracefulRestart.State.LocalRestarting = false
log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.fsm.pConf.Config.NeighborAddress,
+ "Topic": "Peer",
+ "Key": peer.ID(),
+ "Families": families,
}).Debug("deferral timer expired")
+ } else if c := config.GetAfiSafi(peer.fsm.pConf, bgp.RF_RTC_UC); peer.fsm.rfMap[bgp.RF_RTC_UC] && !c.MpGracefulRestart.State.EndOfRibReceived {
+ log.WithFields(log.Fields{
+ "Topic": "Peer",
+ "Key": peer.ID(),
+ "Families": families,
+ }).Debug("route-target deferral timer expired")
} else {
continue
}
}
- families := []bgp.RouteFamily{grpcReq.RouteFamily}
- if families[0] == bgp.RouteFamily(0) {
- families = peer.configuredRFlist()
- }
sentPathList := peer.adjRibOut.PathList(families, false)
peer.adjRibOut.Drop(families)
pathList, filtered := peer.getBestFromLocal(families)
diff --git a/test/lib/base.py b/test/lib/base.py
index d5dfc221..41807cbf 100644
--- a/test/lib/base.py
+++ b/test/lib/base.py
@@ -250,7 +250,7 @@ class BGPContainer(Container):
super(BGPContainer, self).run()
return self.WAIT_FOR_BOOT
- def add_peer(self, peer, passwd=None, evpn=False, is_rs_client=False,
+ def add_peer(self, peer, passwd=None, vpn=False, is_rs_client=False,
policies=None, passive=False,
is_rr_client=False, cluster_id=None,
flowspec=False, bridge='', reload_config=True, as2=False,
@@ -273,7 +273,7 @@ class BGPContainer(Container):
self.peers[peer] = {'neigh_addr': neigh_addr,
'passwd': passwd,
- 'evpn': evpn,
+ 'vpn': vpn,
'flowspec': flowspec,
'is_rs_client': is_rs_client,
'is_rr_client': is_rr_client,
diff --git a/test/lib/gobgp.py b/test/lib/gobgp.py
index 36b4b86e..0b352fa5 100644
--- a/test/lib/gobgp.py
+++ b/test/lib/gobgp.py
@@ -212,10 +212,12 @@ class GoBGPContainer(BGPContainer):
else:
Exception('invalid ip address version. {0}'.format(version))
- if info['evpn']:
+ if info['vpn']:
+ afi_safi_list.append({'afi-safi-name': 'l3vpn-ipv4-unicast'})
+ afi_safi_list.append({'afi-safi-name': 'l3vpn-ipv6-unicast'})
afi_safi_list.append({'afi-safi-name': 'l2vpn-evpn'})
- afi_safi_list.append({'afi-safi-name': 'encap'})
- afi_safi_list.append({'afi-safi-name': 'rtc'})
+ afi_safi_list.append({'afi-safi-name': 'rtc',
+ 'route-target-membership': {'deferral-time': 10}})
if info['flowspec']:
afi_safi_list.append({'afi-safi-name': 'ipv4-flowspec'})
diff --git a/test/scenario_test/evpn_test.py b/test/scenario_test/evpn_test.py
index 7920bf2b..01827805 100644
--- a/test/scenario_test/evpn_test.py
+++ b/test/scenario_test/evpn_test.py
@@ -56,10 +56,12 @@ class GoBGPTestBase(unittest.TestCase):
initial_wait_time = max(ctn.run() for ctn in ctns)
time.sleep(initial_wait_time)
+ g1.local("gobgp vrf add vrf1 rd 10:10 rt both 10:10")
+ g2.local("gobgp vrf add vrf1 rd 10:10 rt both 10:10")
for a, b in combinations(ctns, 2):
- a.add_peer(b, evpn=True, passwd='evpn')
- b.add_peer(a, evpn=True, passwd='evpn')
+ a.add_peer(b, vpn=True, passwd='evpn')
+ b.add_peer(a, vpn=True, passwd='evpn')
cls.g1 = g1
cls.g2 = g2
diff --git a/test/scenario_test/rtc_test.py b/test/scenario_test/rtc_test.py
new file mode 100644
index 00000000..f1f417c7
--- /dev/null
+++ b/test/scenario_test/rtc_test.py
@@ -0,0 +1,94 @@
+# Copyright (C) 2016 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.
+
+import unittest
+from fabric.api import local
+from lib import base
+from lib.gobgp import *
+from lib.quagga import *
+import sys
+import os
+import time
+import nose
+from noseplugin import OptionParser, parser_option
+from itertools import combinations
+
+
+class GoBGPTestBase(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ gobgp_ctn_image_name = parser_option.gobgp_image
+ base.TEST_PREFIX = parser_option.test_prefix
+
+ g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1',
+ ctn_image_name=gobgp_ctn_image_name,
+ log_level=parser_option.gobgp_log_level)
+ g2 = GoBGPContainer(name='g2', asn=65000, router_id='192.168.0.2',
+ ctn_image_name=gobgp_ctn_image_name,
+ log_level=parser_option.gobgp_log_level)
+ ctns = [g1, g2]
+
+ initial_wait_time = max(ctn.run() for ctn in ctns)
+
+ time.sleep(initial_wait_time)
+
+ g1.local("gobgp vrf add vrf1 rd 100:100 rt both 100:100")
+ g1.local("gobgp vrf add vrf2 rd 200:200 rt both 200:200")
+ g2.local("gobgp vrf add vrf1 rd 100:100 rt both 100:100")
+ g2.local("gobgp vrf add vrf3 rd 300:300 rt both 300:300")
+
+ g1.local("gobgp vrf vrf1 rib add 10.0.0.0/24")
+ g1.local("gobgp vrf vrf2 rib add 10.0.0.0/24")
+ g2.local("gobgp vrf vrf1 rib add 20.0.0.0/24")
+ g2.local("gobgp vrf vrf3 rib add 20.0.0.0/24")
+
+ for a, b in combinations(ctns, 2):
+ a.add_peer(b, vpn=True, passwd='rtc', graceful_restart=True)
+ b.add_peer(a, vpn=True, passwd='rtc', graceful_restart=True)
+
+ cls.g1 = g1
+ cls.g2 = g2
+
+ # test each neighbor state is turned establish
+ def test_01_neighbor_established(self):
+ self.g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=self.g2)
+
+ def test_02_check_gobgp_adj_rib_out(self):
+ time.sleep(10)
+ self.assertTrue(len(self.g1.get_adj_rib_out(self.g2, rf='ipv4-l3vpn')) == 1)
+ self.assertTrue(len(self.g2.get_adj_rib_out(self.g1, rf='ipv4-l3vpn')) == 1)
+
+ def test_03_add_vrf(self):
+ self.g1.local("gobgp vrf add vrf3 rd 300:300 rt both 300:300")
+ time.sleep(10)
+ self.assertTrue(len(self.g1.get_adj_rib_in(self.g2, rf='ipv4-l3vpn')) == 2)
+
+ def test_04_del_vrf(self):
+ self.g1.local("gobgp vrf del vrf1")
+ time.sleep(10)
+ self.assertTrue(len(self.g1.get_adj_rib_in(self.g2, rf='ipv4-l3vpn')) == 1)
+
+if __name__ == '__main__':
+ if os.geteuid() is not 0:
+ print "you are not root."
+ sys.exit(1)
+ output = local("which docker 2>&1 > /dev/null ; echo $?", capture=True)
+ if int(output) is not 0:
+ print "docker not found"
+ sys.exit(1)
+
+ nose.main(argv=sys.argv, addplugins=[OptionParser()],
+ defaultTest=sys.argv[0])
diff --git a/tools/pyang_plugins/gobgp.yang b/tools/pyang_plugins/gobgp.yang
index 911de3bc..30cc9d30 100644
--- a/tools/pyang_plugins/gobgp.yang
+++ b/tools/pyang_plugins/gobgp.yang
@@ -826,4 +826,12 @@ module gobgp {
augment "/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi" {
uses bgp-mp:all-afi-safi-common;
}
+
+ augment "/bgp:bgp/bgp:global/bgp:afi-safis/bgp:afi-safi" {
+ container route-target-membership {
+ leaf deferral-time {
+ type uint16;
+ }
+ }
+ }
}