Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : * Copyright Contributors to the OpenSDN project
4 : */
5 :
6 : //
7 : // sandesh_server.cc
8 : //
9 : // Sandesh server implementation
10 : //
11 :
12 : #include <boost/bind/bind.hpp>
13 : #include <boost/assign.hpp>
14 :
15 : #include <base/address_util.h>
16 : #include <sandesh/protocol/TXMLProtocol.h>
17 : #include <sandesh/sandesh_types.h>
18 : #include <sandesh/sandesh.h>
19 : #include <sandesh/sandesh_ctrl_types.h>
20 : #include "sandesh_connection.h"
21 : #include "sandesh_session.h"
22 : #include "sandesh_server.h"
23 :
24 : using namespace std;
25 : using namespace boost::asio;
26 : using namespace boost::placeholders;
27 :
28 : const std::string SandeshServer::kStateMachineTask = "sandesh::SandeshStateMachine";
29 : const std::string SandeshServer::kLifetimeMgrTask = "sandesh::LifetimeMgr";
30 : const std::string SandeshServer::kSessionReaderTask = "io::ReaderTask";
31 :
32 : class SandeshServer::DeleteActor : public LifetimeActor {
33 : public:
34 37 : DeleteActor(SandeshServer *server) :
35 37 : LifetimeActor(server->lifetime_manager()), server_(server) { }
36 37 : virtual bool MayDelete() const {
37 37 : return true;
38 : }
39 37 : virtual void Shutdown() {
40 37 : server_->SessionShutdown();
41 37 : }
42 37 : virtual void Destroy() {
43 37 : }
44 : private:
45 : SandeshServer *server_;
46 : };
47 :
48 : bool SandeshServer::task_policy_set_ = false;
49 :
50 37 : SandeshServer::SandeshServer(EventManager *evm, const SandeshConfig &config)
51 : : SslServer(evm, boost::asio::ssl::context::tlsv12_server,
52 37 : config.sandesh_ssl_enable),
53 37 : sm_task_id_(TaskScheduler::GetInstance()->GetTaskId(kStateMachineTask)),
54 37 : session_reader_task_id_(TaskScheduler::GetInstance()->GetTaskId(kSessionReaderTask)),
55 37 : lifetime_mgr_task_id_(TaskScheduler::GetInstance()->GetTaskId(kLifetimeMgrTask)),
56 37 : lifetime_manager_(new LifetimeManager(lifetime_mgr_task_id_)),
57 74 : deleter_(new DeleteActor(this)) {
58 : // Set task policy for exclusion between :
59 : // 1. State machine and lifetime mgr since state machine delete happens
60 : // in lifetime mgr task
61 37 : if (!task_policy_set_) {
62 : TaskPolicy lm_task_policy = boost::assign::list_of
63 3 : (TaskExclusion(sm_task_id_))
64 3 : (TaskExclusion(session_reader_task_id_));
65 3 : TaskScheduler::GetInstance()->SetPolicy(lifetime_mgr_task_id_, lm_task_policy);
66 3 : task_policy_set_ = true;
67 3 : }
68 37 : if (config.sandesh_ssl_enable) {
69 0 : boost::asio::ssl::context *ctx = context();
70 0 : boost::system::error_code ec;
71 0 : ctx->set_options(boost::asio::ssl::context::default_workarounds |
72 : boost::asio::ssl::context::no_tlsv1 |
73 : boost::asio::ssl::context::no_sslv3 |
74 : boost::asio::ssl::context::no_sslv2 |
75 : boost::asio::ssl::context::no_tlsv1_1, ec);
76 0 : if (ec.value() != 0) {
77 0 : SANDESH_LOG(ERROR, "Error setting ssl options: " << ec.message());
78 0 : exit(EINVAL);
79 : }
80 : // CA certificate
81 0 : if (!config.ca_cert.empty()) {
82 : // Verify that the peer certificate is signed by a trusted CA
83 0 : ctx->set_verify_mode(boost::asio::ssl::verify_peer |
84 : boost::asio::ssl::verify_fail_if_no_peer_cert,
85 : ec);
86 0 : if (ec.value() != 0) {
87 0 : SANDESH_LOG(ERROR, "Error setting verification mode: " <<
88 : ec.message());
89 0 : exit(EINVAL);
90 : }
91 0 : ctx->load_verify_file(config.ca_cert, ec);
92 0 : if (ec.value() != 0) {
93 0 : SANDESH_LOG(ERROR, "Error loading CA certificate: " <<
94 : ec.message());
95 0 : exit(EINVAL);
96 : }
97 : }
98 : // Server certificate
99 0 : ctx->use_certificate_chain_file(config.server_certfile, ec);
100 0 : if (ec.value() != 0) {
101 0 : SANDESH_LOG(ERROR, "Error using server certificate: " <<
102 : ec.message());
103 0 : exit(EINVAL);
104 : }
105 : // Server private key
106 0 : ctx->use_private_key_file(config.server_keyfile,
107 : boost::asio::ssl::context::pem, ec);
108 0 : if (ec.value() != 0) {
109 0 : SANDESH_LOG(ERROR, "Error using server private key file: " <<
110 : ec.message());
111 0 : exit(EINVAL);
112 : }
113 : }
114 37 : }
115 :
116 37 : SandeshServer::~SandeshServer() {
117 37 : TcpServer::ClearSessions();
118 37 : }
119 :
120 0 : int SandeshServer::lifetime_mgr_task_id() {
121 0 : return lifetime_mgr_task_id_;
122 : }
123 :
124 37 : void SandeshServer::SessionShutdown() {
125 37 : TcpServer::Shutdown();
126 37 : }
127 :
128 37 : bool SandeshServer::Initialize(short port, const std::string &ip) {
129 37 : int count = 0;
130 :
131 37 : boost::system::error_code ec;
132 37 : boost::asio::ip::address ip_addr = AddressFromString(ip, &ec);
133 37 : if (ec) {
134 0 : SANDESH_LOG(ERROR, __func__ << ": Invalid server address: " <<
135 : ip << " Error: " << ec);
136 0 : return false;
137 : }
138 37 : while (count++ < kMaxInitRetries) {
139 37 : if (TcpServer::Initialize(port, ip_addr))
140 37 : break;
141 0 : sleep(1);
142 : }
143 37 : if (!(count < kMaxInitRetries)) {
144 0 : SANDESH_LOG(ERROR, "Process EXITING: TCP Server initialization failed for port " << port);
145 0 : exit(1);
146 : }
147 37 : return true;
148 : }
149 :
150 37 : int SandeshServer::AllocConnectionIndex() {
151 37 : std::scoped_lock lock(mutex_);
152 37 : size_t bit = conn_bmap_.find_first();
153 37 : if (bit == conn_bmap_.npos) {
154 37 : bit = conn_bmap_.size();
155 37 : conn_bmap_.resize(bit + 1, true);
156 : }
157 37 : conn_bmap_.reset(bit);
158 37 : return bit;
159 37 : }
160 :
161 37 : void SandeshServer::FreeConnectionIndex(int id) {
162 37 : std::scoped_lock lock(mutex_);
163 37 : conn_bmap_.set(id);
164 :
165 74 : for (size_t i = conn_bmap_.size(); i != 0; i--) {
166 37 : if (conn_bmap_[i-1] != true) {
167 0 : if (i != conn_bmap_.size()) {
168 0 : conn_bmap_.resize(i);
169 : }
170 0 : return;
171 : }
172 : }
173 37 : conn_bmap_.clear();
174 37 : }
175 :
176 0 : TcpSession *SandeshServer::CreateSession() {
177 : typedef boost::asio::detail::socket_option::boolean<
178 : SOL_SOCKET, SO_REUSEADDR> reuse_addr_t;
179 0 : TcpSession *session = SslServer::CreateSession();
180 0 : Socket *socket = session->socket();
181 :
182 0 : boost::system::error_code err;
183 0 : socket->open(ip::tcp::v4(), err);
184 0 : if (err) {
185 0 : SANDESH_LOG(ERROR, __func__ << " Server Open Fail " << err.message());
186 : }
187 :
188 0 : socket->set_option(reuse_addr_t(true), err);
189 0 : if (err) {
190 0 : SANDESH_LOG(ERROR, __func__ << " SetSockOpt Fail " << err.message());
191 0 : return session;
192 : }
193 :
194 0 : socket->bind(LocalEndpoint(), err);
195 0 : if (err) {
196 0 : SANDESH_LOG(ERROR, __func__ << " Server Bind Failure " << err.message());
197 : }
198 :
199 0 : return session;
200 : }
201 :
202 37 : void SandeshServer::Shutdown() {
203 37 : assert(deleter_.get());
204 37 : deleter_->Delete();
205 37 : }
206 :
207 0 : bool SandeshServer::Compare(const Endpoint &peer_addr,
208 : const SandeshConnectionPair &p) const {
209 0 : return (peer_addr == p.second->endpoint() ? false : true);
210 : }
211 :
212 0 : SandeshConnection *SandeshServer::FindConnection(const Endpoint &peer_addr) {
213 0 : std::scoped_lock lock(mutex_);
214 0 : SandeshConnectionMap::iterator loc = find_if(connection_.begin(),
215 : connection_.end(), boost::bind(&SandeshServer::Compare, this,
216 : boost::ref(peer_addr), _1));
217 0 : if (loc != connection_.end()) {
218 0 : return loc->second;
219 : }
220 0 : return NULL;
221 0 : }
222 :
223 37 : SslSession *SandeshServer::AllocSession(SslSocket *socket) {
224 : // Use the state machine task to run the session send queue since
225 : // they need to be exclusive as session delete happens from state
226 : // machine
227 : SslSession *session = new SandeshSession(this, socket,
228 37 : AllocConnectionIndex(), session_writer_task_id(),
229 37 : session_reader_task_id());
230 37 : return session;
231 : }
232 :
233 37 : void SandeshServer::RemoveConnection(SandeshConnection *connection) {
234 37 : std::scoped_lock lock(mutex_);
235 37 : boost::asio::ip::tcp::endpoint endpoint = connection->endpoint();
236 37 : connection_.erase(endpoint);
237 37 : }
238 :
239 37 : bool SandeshServer::AcceptSession(TcpSession *session) {
240 37 : std::scoped_lock lock(mutex_);
241 : SandeshConnection *connection;
242 37 : SandeshSession *ssession = dynamic_cast<SandeshSession *>(session);
243 37 : assert(ssession);
244 37 : ip::tcp::endpoint remote = session->remote_endpoint();
245 37 : SandeshConnectionMap::iterator loc = connection_.find(remote);
246 :
247 37 : if (loc == connection_.end()) {
248 37 : SANDESH_LOG(INFO, "Server: " << __func__ << " " << "Create Connection");
249 : //create a connection_
250 37 : connection = new SandeshServerConnection(this, remote,
251 37 : ssession->GetSessionInstance(),
252 37 : sm_task_id_);
253 37 : connection->Initialize();
254 37 : connection_.insert(remote, connection);
255 : } else {
256 0 : connection = loc->second;
257 0 : if (connection->session() != NULL) {
258 0 : return false;
259 : }
260 : }
261 37 : connection->AcceptSession(ssession);
262 37 : return true;
263 37 : }
264 :
265 37 : bool SandeshServer::ReceiveSandeshCtrlMsg(SandeshStateMachine *sm,
266 : SandeshSession *session, const Sandesh *sandesh) {
267 : const SandeshCtrlClientToServer *snh =
268 37 : dynamic_cast<const SandeshCtrlClientToServer *>(sandesh);
269 37 : if (!snh) {
270 0 : SANDESH_LOG(DEBUG, "Received Ctrl Message with wrong type " << sandesh->Name());
271 0 : return false;
272 : }
273 37 : SANDESH_LOG(DEBUG, "Received Ctrl Message from " << snh->get_module_name());
274 37 : std::vector<UVETypeInfo> vu;
275 37 : SandeshCtrlServerToClient::Request(vu, true, "ctrl", session->connection());
276 37 : return true;
277 37 : }
278 :
279 37 : LifetimeActor *SandeshServer::deleter() {
280 37 : return deleter_.get();
281 : }
282 :
283 74 : LifetimeManager *SandeshServer::lifetime_manager() {
284 74 : return lifetime_manager_.get();
285 : }
|