Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include "bgp/bgp_session_manager.h"
6 :
7 : #include "base/bgp_as_service_utils.h"
8 : #include "base/task_annotations.h"
9 : #include "base/address_util.h"
10 : #include "bgp/bgp_log.h"
11 : #include "bgp/bgp_peer.h"
12 : #include "bgp/bgp_server.h"
13 : #include "bgp/bgp_session.h"
14 : #include "bgp/routing-instance/peer_manager.h"
15 : #include "bgp/routing-instance/routing_instance.h"
16 :
17 : using namespace boost::asio::ip;
18 :
19 : static const std::string kDefaultBgpSessionIp = "0.0.0.0";
20 :
21 9742 : BgpSessionManager::BgpSessionManager(EventManager *evm, BgpServer *server)
22 : : TcpServer(evm),
23 9742 : server_(server),
24 9742 : session_queue_(
25 : TaskScheduler::GetInstance()->GetTaskId("bgp::Config"), 0,
26 : boost::bind(&BgpSessionManager::ProcessSession, this, _1)),
27 9742 : write_ready_queue_(
28 : TaskScheduler::GetInstance()->GetTaskId("bgp::Config"), 0,
29 19484 : boost::bind(&BgpSessionManager::ProcessWriteReady, this, _1)) {
30 :
31 9742 : boost::system::error_code ec;
32 9742 : session_ip_ = AddressFromString(kDefaultBgpSessionIp, &ec);
33 9742 : }
34 :
35 18967 : BgpSessionManager::~BgpSessionManager() {
36 18967 : }
37 :
38 : //
39 : // Start listening at the given port.
40 : //
41 11421 : bool BgpSessionManager::Initialize(unsigned short port) {
42 : // Changes to already bound bgp listen port number is not supported.
43 11421 : Endpoint local_endpoint = LocalEndpoint();
44 11421 : if (local_endpoint != Endpoint()) {
45 5915 : Endpoint new_endpoint(session_ip_, port);
46 5915 : if (local_endpoint != new_endpoint) {
47 292 : BGP_LOG_WARNING_STR(BgpSocket, BGP_LOG_FLAG_ALL,
48 : "Cannot change already bound " << local_endpoint <<
49 : " to " << new_endpoint);
50 292 : return false;
51 : }
52 5623 : return true;
53 : }
54 :
55 5506 : LOG(DEBUG, "Starting Bgp Server at " << session_ip_ << ":" << port);
56 5506 : bool r = TcpServer::Initialize(port, session_ip_);
57 5506 : if (!r) {
58 219 : BGP_LOG_WARNING_STR(BgpSocket, BGP_LOG_FLAG_ALL,
59 : "Cannot bind to bgp/tcp server ip:port " <<
60 : session_ip_ << ":" << port);
61 : }
62 5506 : return r;
63 : }
64 :
65 : //
66 : // Start listening at the given ip:port.
67 : //
68 191 : bool BgpSessionManager::Initialize(unsigned short port, const IpAddress& ip) {
69 191 : session_ip_ = ip;
70 191 : return true;
71 : }
72 :
73 : //
74 : // Called from BgpServer::DeleteActor's Shutdown method.
75 : // Shutdown the TcpServer.
76 : // Register an exit callback to the WorkQueues so that we can ask BgpServer
77 : // to retry deletion when a WorkQueue becomes empty.
78 : //
79 9638 : void BgpSessionManager::Shutdown() {
80 9638 : CHECK_CONCURRENCY("bgp::Config");
81 9638 : TcpServer::Shutdown();
82 9638 : session_queue_.SetExitCallback(
83 : boost::bind(&BgpSessionManager::WorkQueueExitCallback, this, _1));
84 9638 : write_ready_queue_.SetExitCallback(
85 : boost::bind(&BgpSessionManager::WorkQueueExitCallback, this, _1));
86 9638 : }
87 :
88 : //
89 : // Called when the BgpServer is being destroyed.
90 : //
91 : // The WorkQueues need to be shutdown as the last step to ensure that all
92 : // entries get deleted. Note that there's no need to call DeleteSession on
93 : // the sessions in the WorkQueues since ClearSessions does the same thing.
94 : //
95 9638 : void BgpSessionManager::Terminate() {
96 9638 : CHECK_CONCURRENCY("bgp::Config");
97 9638 : server_ = NULL;
98 9638 : ClearSessions();
99 9638 : session_queue_.Shutdown();
100 9638 : write_ready_queue_.Shutdown();
101 9638 : }
102 :
103 : //
104 : // Return true if all WorkQueues are empty.
105 : //
106 9638 : bool BgpSessionManager::MayDelete() const {
107 9638 : if (!session_queue_.IsQueueEmpty())
108 0 : return false;
109 9638 : if (!write_ready_queue_.IsQueueEmpty())
110 0 : return false;
111 9638 : return true;
112 : }
113 :
114 : //
115 : // Add a BgpSession to the write ready WorkQueue.
116 : // Take a reference to make sure that BgpSession doesn't get deleted before
117 : // it's processed.
118 : //
119 0 : void BgpSessionManager::EnqueueWriteReady(BgpSession *session) {
120 0 : if (!server_ || server_->IsDeleted())
121 0 : return;
122 0 : write_ready_queue_.Enqueue(TcpSessionPtr(session));
123 : }
124 :
125 : //
126 : // Handler for BgpSessions that are dequeued from the write ready WorkQueue.
127 : //
128 : // The BgpServer does not get destroyed if the WorkQueue is non-empty.
129 : //
130 0 : bool BgpSessionManager::ProcessWriteReady(TcpSessionPtr tcp_session) {
131 0 : BgpSession *session = static_cast<BgpSession *>(tcp_session.get());
132 0 : session->ProcessWriteReady();
133 0 : return true;
134 : }
135 :
136 : //
137 : // Search for a matching BgpPeer.
138 : // First look for a matching address in the master instance.
139 : // Then look for a matching port in the EndpointPeerList in BgpServer.
140 : //
141 6334 : BgpPeer *BgpSessionManager::FindPeer(Endpoint remote) {
142 6334 : BgpPeer *peer = NULL;
143 : const RoutingInstance *instance =
144 6334 : server_->routing_instance_mgr()->GetDefaultRoutingInstance();
145 6334 : if (instance && !instance->deleted()) {
146 6334 : peer = instance->peer_manager()->PeerLookup(remote);
147 : }
148 6334 : if (!peer) {
149 854 : uint16_t port = BGPaaSUtils::DecodeBgpaasServicePort(remote.port(),
150 854 : server_->global_config()->bgpaas_port_start(),
151 854 : server_->global_config()->bgpaas_port_end()).first;
152 854 : peer = server_->FindPeer(TcpSession::Endpoint(Ip4Address(), port));
153 : }
154 6334 : return peer;
155 : }
156 :
157 : //
158 : // Create an active BgpSession.
159 : //
160 6575 : TcpSession *BgpSessionManager::CreateSession() {
161 6575 : TcpSession *session = TcpServer::CreateSession();
162 6576 : Socket *socket = session->socket();
163 :
164 6576 : boost::system::error_code ec;
165 6576 : socket->open(boost::asio::ip::tcp::v4(), ec);
166 6576 : if (ec || (ec = session->SetSocketOptions()) || socket_open_failure()) {
167 36 : BGP_LOG_WARNING_STR(BgpSocket, BGP_LOG_FLAG_ALL,
168 : "Failed to open bgp socket, error: " << ec.message());
169 36 : DeleteSession(session);
170 36 : return NULL;
171 : }
172 :
173 6537 : if (session_ip_ != address::from_string(kDefaultBgpSessionIp, ec)) {
174 409 : tcp::endpoint localaddr(session_ip_, 0);
175 408 : socket->bind(localaddr, ec);
176 410 : if (ec) {
177 1 : BGP_LOG_WARNING_STR(BgpSocket, BGP_LOG_FLAG_ALL,
178 : "Failed to bind bgp socket to:" << session_ip_ <<
179 : ", error: " << ec.message());
180 0 : DeleteSession(session);
181 0 : return NULL;
182 : }
183 : }
184 6535 : return session;
185 : }
186 :
187 : //
188 : // Allocate a new BgpSession.
189 : // Called via CreateSession or when the TcpServer accepts a passive session.
190 : //
191 12907 : TcpSession *BgpSessionManager::AllocSession(Socket *socket) {
192 12907 : TcpSession *session = new BgpSession(this, socket);
193 12886 : return session;
194 : }
195 :
196 : //
197 : // Accept incoming BgpSession and add to session WorkQueue for processing.
198 : // This ensures that we don't try to access the BgpServer data structures
199 : // from the IO thread while they are being modified from bgp::Config task.
200 : //
201 : // Stop accepting sessions after delete of the BgpServer gets triggered.
202 : // Note that the BgpServer, and hence the BgpSessionManager will not get
203 : // destroyed if the WorkQueue is non-empty.
204 : //
205 6334 : bool BgpSessionManager::AcceptSession(TcpSession *tcp_session) {
206 6334 : if (!server_ || server_->IsDeleted())
207 0 : return false;
208 6334 : BgpSession *session = dynamic_cast<BgpSession *>(tcp_session);
209 6334 : session->set_read_on_connect(false);
210 6334 : session_queue_.Enqueue(session);
211 6334 : return true;
212 : }
213 :
214 : //
215 : // Handler for BgpSessions that are dequeued from the session WorkQueue.
216 : //
217 : // The BgpServer does not get destroyed if the WorkQueue is non-empty.
218 : //
219 6334 : bool BgpSessionManager::ProcessSession(BgpSession *session) {
220 6334 : CHECK_CONCURRENCY("bgp::Config");
221 :
222 6334 : BgpPeer *peer = FindPeer(session->remote_endpoint());
223 :
224 : // Ignore if server is being deleted.
225 6334 : if (!server_ || server_->IsDeleted()) {
226 0 : session->SendNotification(BgpProto::Notification::Cease,
227 : BgpProto::Notification::PeerDeconfigured);
228 0 : DeleteSession(session);
229 0 : return true;
230 : }
231 :
232 : // Ignore if this server is being held administratively down.
233 6334 : if (server_->admin_down()) {
234 0 : session->SendNotification(BgpProto::Notification::Cease,
235 : BgpProto::Notification::AdminShutdown);
236 0 : DeleteSession(session);
237 0 : return true;
238 : }
239 :
240 : // Ignore if this peer is not configured or is being deleted.
241 6334 : if (peer == NULL || peer->deleter()->IsDeleted()) {
242 115 : session->SendNotification(BgpProto::Notification::Cease,
243 : BgpProto::Notification::PeerDeconfigured);
244 115 : BGP_LOG_WARNING_STR(BgpConfig, BGP_LOG_FLAG_TRACE,
245 : "Remote end-point not found");
246 115 : DeleteSession(session);
247 115 : return true;
248 : }
249 :
250 : // Ignore if this peer is being held administratively down.
251 6219 : if (peer->IsAdminDown()) {
252 1832 : session->SendNotification(BgpProto::Notification::Cease,
253 : BgpProto::Notification::AdminShutdown);
254 1832 : DeleteSession(session);
255 1832 : return true;
256 : }
257 :
258 : // Ignore if the peer's prefix limit idle timer is running.
259 4387 : if (peer->PrefixLimitIdleTimerRunning()) {
260 196 : session->SendNotification(BgpProto::Notification::Cease,
261 : BgpProto::Notification::MaxPrefixes);
262 196 : DeleteSession(session);
263 196 : return true;
264 : }
265 :
266 : // Ignore if this peer is being closed.
267 4191 : if (peer->IsCloseInProgress()) {
268 96 : session->SendNotification(BgpProto::Notification::Cease,
269 : BgpProto::Notification::ConnectionRejected);
270 96 : DeleteSession(session);
271 96 : return true;
272 : }
273 :
274 4095 : if (!peer->ProcessSession()) {
275 65 : session->SendNotification(BgpProto::Notification::Cease,
276 : BgpProto::Notification::ConnectionRejected);
277 65 : DeleteSession(session);
278 65 : return true;
279 : }
280 :
281 4030 : peer->AcceptSession(session);
282 4030 : return true;
283 : }
284 :
285 : //
286 : // Exit callback for the session and write ready WorkQueues.
287 : //
288 0 : void BgpSessionManager::WorkQueueExitCallback(bool done) {
289 0 : server_->RetryDelete();
290 0 : }
291 :
292 0 : size_t BgpSessionManager::GetSessionQueueSize() const {
293 0 : return session_queue_.Length();
294 : }
295 :
296 8 : void BgpSessionManager::SetSessionQueueDisable(bool disabled) {
297 8 : session_queue_.set_disable(disabled);
298 8 : }
|