1 # Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 from ryu.services.protocols.bgp.base import Activity
17 from ryu.lib import hub
18 from ryu.lib.packet import bmp
19 from ryu.lib.packet import bgp
22 from calendar import timegm
23 from ryu.services.protocols.bgp.signals.emit import BgpSignalBus
24 from ryu.services.protocols.bgp.info_base.ipv4 import Ipv4Path
25 from ryu.lib.packet.bgp import BGPUpdate
26 from ryu.lib.packet.bgp import BGPPathAttributeMpUnreachNLRI
28 LOG = logging.getLogger('bgpspeaker.bmp')
31 class BMPClient(Activity):
34 Try to establish BMP session between a configured BMP server.
35 If BMP session is established, transfer information about peers
36 (e.g. received and sent open msgs, contents of adj-rib-in, other stats)
40 def __init__(self, core_service, host, port):
41 super(BMPClient, self).__init__(name='BMPClient(%s:%s)' % (host, port))
42 self._core_service = core_service
43 self._core_service.signal_bus.register_listener(
44 BgpSignalBus.BGP_ADJ_RIB_IN_CHANGED,
45 lambda _, data: self.on_adj_rib_in_changed(data)
47 self._core_service.signal_bus.register_listener(
48 BgpSignalBus.BGP_ADJ_UP,
49 lambda _, data: self.on_adj_up(data)
51 self._core_service.signal_bus.register_listener(
52 BgpSignalBus.BGP_ADJ_DOWN,
53 lambda _, data: self.on_adj_down(data)
56 self.server_address = (host, port)
57 self._connect_retry_event = hub.Event()
58 self._connect_retry_time = 5
61 self._connect_retry_event.set()
64 self._connect_retry_event.wait()
67 self._connect_retry_event.clear()
68 self._connect_tcp(self.server_address,
69 self._handle_bmp_session)
71 self._connect_retry_event.set()
72 LOG.info('Will try to reconnect to %s after %s secs: %s',
73 self.server_address, self._connect_retry_time,
74 self._connect_retry_event.is_set())
76 self.pause(self._connect_retry_time)
81 assert isinstance(msg, bmp.BMPMessage)
82 self._socket.send(msg.serialize())
84 def on_adj_rib_in_changed(self, data):
86 path = data['received_route']
87 msg = self._construct_route_monitoring(peer, path)
90 def on_adj_up(self, data):
92 msg = self._construct_peer_up_notification(peer)
95 def on_adj_down(self, data):
97 msg = self._construct_peer_down_notification(peer)
100 def _construct_peer_up_notification(self, peer):
101 if peer.is_mpbgp_cap_valid(bgp.RF_IPv4_VPN) or \
102 peer.is_mpbgp_cap_valid(bgp.RF_IPv6_VPN):
103 peer_type = bmp.BMP_PEER_TYPE_L3VPN
105 peer_type = bmp.BMP_PEER_TYPE_GLOBAL
107 peer_distinguisher = 0
108 peer_as = peer._neigh_conf.remote_as
109 peer_bgp_id = peer.protocol.recv_open_msg.bgp_identifier
110 timestamp = peer.state._established_time
112 local_address = peer.host_bind_ip
113 local_port = int(peer.host_bind_port)
114 peer_address, remote_port = peer.protocol._remotename
115 remote_port = int(remote_port)
117 sent_open_msg = peer.protocol.sent_open_msg
118 recv_open_msg = peer.protocol.recv_open_msg
120 msg = bmp.BMPPeerUpNotification(local_address=local_address,
121 local_port=local_port,
122 remote_port=remote_port,
123 sent_open_message=sent_open_msg,
124 received_open_message=recv_open_msg,
126 is_post_policy=False,
127 peer_distinguisher=peer_distinguisher,
128 peer_address=peer_address,
130 peer_bgp_id=peer_bgp_id,
135 def _construct_peer_down_notification(self, peer):
136 if peer.is_mpbgp_cap_valid(bgp.RF_IPv4_VPN) or \
137 peer.is_mpbgp_cap_valid(bgp.RF_IPv6_VPN):
138 peer_type = bmp.BMP_PEER_TYPE_L3VPN
140 peer_type = bmp.BMP_PEER_TYPE_GLOBAL
142 peer_as = peer._neigh_conf.remote_as
143 peer_bgp_id = peer.protocol.recv_open_msg.bgp_identifier
144 peer_address, _ = peer.protocol._remotename
146 return bmp.BMPPeerDownNotification(bmp.BMP_PEER_DOWN_REASON_UNKNOWN,
149 is_post_policy=False,
150 peer_distinguisher=0,
151 peer_address=peer_address,
153 peer_bgp_id=peer_bgp_id,
156 def _construct_update(self, path):
157 # Get copy of path's path attributes.
158 new_pathattr = [attr for attr in path.pathattr_map.values()]
161 if isinstance(path, Ipv4Path):
162 return BGPUpdate(withdrawn_routes=[path.nlri],
163 path_attributes=new_pathattr)
165 mpunreach_attr = BGPPathAttributeMpUnreachNLRI(
166 path.route_family.afi, path.route_family.safi, [path.nlri]
168 new_pathattr.append(mpunreach_attr)
170 if isinstance(path, Ipv4Path):
171 return BGPUpdate(nlri=[path.nlri],
172 path_attributes=new_pathattr)
174 return BGPUpdate(path_attributes=new_pathattr)
176 def _construct_route_monitoring(self, peer, route):
177 if peer.is_mpbgp_cap_valid(bgp.RF_IPv4_VPN) or \
178 peer.is_mpbgp_cap_valid(bgp.RF_IPv6_VPN):
179 peer_type = bmp.BMP_PEER_TYPE_L3VPN
181 peer_type = bmp.BMP_PEER_TYPE_GLOBAL
183 peer_distinguisher = 0
184 peer_as = peer._neigh_conf.remote_as
185 peer_bgp_id = peer.protocol.recv_open_msg.bgp_identifier
186 peer_address, _ = peer.protocol._remotename
188 bgp_update = self._construct_update(route.path)
189 is_post_policy = not route.filtered
190 timestamp = timegm(route.timestamp)
192 msg = bmp.BMPRouteMonitoring(bgp_update=bgp_update,
194 is_post_policy=is_post_policy,
195 peer_distinguisher=peer_distinguisher,
196 peer_address=peer_address,
197 peer_as=peer_as, peer_bgp_id=peer_bgp_id,
202 def _handle_bmp_session(self, socket):
204 self._socket = socket
206 init_info = {'type': bmp.BMP_INIT_TYPE_STRING,
207 'value': u'This is Ryu BGP BMP message'}
208 init_msg = bmp.BMPInitiation([init_info])
211 # send peer-up message for each peers
212 peer_manager = self._core_service.peer_manager
214 for peer in (p for p in peer_manager.iterpeers if p.in_established()):
215 msg = self._construct_peer_up_notification(peer)
218 for path in peer._adj_rib_in.values():
219 msg = self._construct_route_monitoring(peer, path)
222 # TODO periodically send stats to bmpstation
225 # bmpstation shouldn't send any packet to bmpclient.
226 # this recv() is only meant to detect socket closed
227 ret = self._socket.recv(1)
229 LOG.debug('BMP socket is closed. retry connecting..')
231 self._connect_retry_event.set()
234 # silently ignore packets from the bmpstation