1 /*
2 * Copyright (c) 2013-2015 Cisco Systems, Inc. and others. All rights reserved.
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
7 *
8 */
9
10 #include <sys/socket.h>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <cstring>
16 #include <unistd.h>
17
18 #include <iostream>
19 #include <cerrno>
20 #include <string>
21
22 #include <poll.h>
23 #include <MsgBusInterface.hpp>
24
25 #include "BMPListener.h"
26 #include "md5.h"
27
28 using namespace std;
29
30 /**
31 * Class constructor
32 *
33 * \param [in] logPtr Pointer to existing Logger for app logging
34 * \param [in] config Pointer to the loaded configuration
35 *
36 */
BMPListener(Logger * logPtr,Config * config)37 BMPListener::BMPListener(Logger *logPtr, Config *config) {
38 sock = 0;
39 sockv6 = 0;
40 debug = false;
41
42 // Update pointer to the config
43 cfg = config;
44
45 logger = logPtr;
46
47 if (cfg->debug_bmp)
48 enableDebug();
49
50 svr_addr.sin_family = PF_INET;
51 svr_addr.sin_port = htons(cfg->bmp_port);
52
53 if(cfg->bind_ipv4.length()) {
54 inet_pton(AF_INET, cfg->bind_ipv4.c_str(), &(svr_addr.sin_addr.s_addr));
55 } else {
56 svr_addr.sin_addr.s_addr = INADDR_ANY;
57 }
58
59 svr_addrv6.sin6_family = AF_INET6;
60 svr_addrv6.sin6_port = htons(cfg->bmp_port);
61 svr_addrv6.sin6_scope_id = 0;
62
63 if(cfg->bind_ipv6.length()) {
64 inet_pton(AF_INET6, cfg->bind_ipv6.c_str(), &(svr_addrv6.sin6_addr));
65 } else {
66 svr_addrv6.sin6_addr = in6addr_any;
67 }
68
69 // Open listening sockets
70 open_socket(cfg->svr_ipv4, cfg->svr_ipv6);
71 }
72
73 /**
74 * Destructor
75 */
~BMPListener()76 BMPListener::~BMPListener() {
77 if (sock > 0)
78 close(sock);
79 if (sockv6 > 0)
80 close(sockv6);
81
82 delete cfg;
83 }
84
85 /**
86 * Opens server (v4 or 6) listening socket(s)
87 *
88 * \param [in] ipv4 True to open v4 socket
89 * \param [in] ipv6 True to open v6 socket
90 */
open_socket(bool ipv4,bool ipv6)91 void BMPListener::open_socket(bool ipv4, bool ipv6) {
92 int on = 1;
93
94 if (ipv4) {
95 if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
96 throw "ERROR: Cannot open IPv4 socket.";
97 }
98
99 // Set socket options
100 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
101 close(sock);
102 throw "ERROR: Failed to set IPv4 socket option SO_REUSEADDR";
103 }
104
105 // Bind to the address/port
106 if (::bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) < 0) {
107 close(sock);
108 throw "ERROR: Cannot bind to IPv4 address and port";
109 }
110
111 // listen for incoming connections
112 listen(sock, 10);
113 }
114
115 if (ipv6) {
116 if ((sockv6 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
117 throw "ERROR: Cannot open IPv6 socket.";
118 }
119
120 // Set socket options
121 if (setsockopt(sockv6, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
122 close(sockv6);
123 throw "ERROR: Failed to set IPv6 socket option SO_REUSEADDR";
124 }
125
126 if (setsockopt(sockv6, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
127 close(sockv6);
128 throw "ERROR: Failed to set IPv6 socket option IPV6_V6ONLY";
129 }
130
131 // Bind to the address/port
132 if (::bind(sockv6, (struct sockaddr *) &svr_addrv6, sizeof(svr_addrv6)) < 0) {
133 close(sockv6);
134 throw "ERROR: Cannot bind to IPv6 address and port";
135 }
136
137 // listen for incoming connections
138 listen(sockv6, 10);
139 }
140 }
141
142 /**
143 * Wait and Accept new/pending connections
144 *
145 * Will accept both IPv4 and IPv6 (if configured), but only one will be accepted
146 * per poll/wait period. Must run this method in a loop fashion to accept all
147 * pending connections.
148 *
149 * \param [out] c Ref to client info - this will be updated based on accepted connection
150 * \param [in] timeout Timeout in ms to wait for
151 *
152 * \return True if accepted a connection, false if not (timed out waiting)
153 */
wait_and_accept_connection(ClientInfo & c,int timeout)154 bool BMPListener::wait_and_accept_connection(ClientInfo &c, int timeout) {
155 pollfd pfd[4];
156 int fds_cnt = 0;
157 int cur_sock = 0;
158 bool close_sock = false;
159
160 if (sock > 0) {
161 pfd[fds_cnt].fd = sock;
162 pfd[fds_cnt].events = POLLIN | POLLHUP | POLLERR;
163 pfd[fds_cnt].revents = 0;
164 fds_cnt++;
165 }
166
167 if (sockv6 > 0) {
168 pfd[fds_cnt].fd = sockv6;
169 pfd[fds_cnt].events = POLLIN | POLLHUP | POLLERR;
170 pfd[fds_cnt].revents = 0;
171 fds_cnt++;
172 }
173
174 // Check if the listening socket has a new connection
175 if (poll(pfd, fds_cnt + 1, timeout)) {
176
177 for (int i = 0; i < fds_cnt; i++) {
178 if (pfd[i].revents & POLLHUP or pfd[i].revents & POLLERR) {
179 LOG_WARN("sock=%d: received POLLHUP/POLLHERR while accepting", pfd[i].fd);
180 cur_sock = pfd[i].fd;
181 close_sock = true;
182 break;
183
184 } else if (pfd[i].revents & POLLIN) {
185 cur_sock = pfd[i].fd;
186 break;
187 }
188 }
189 }
190
191 if (cur_sock > 0) {
192 if (close_sock) {
193 if (cur_sock == sock)
194 close(sock);
195 else if (cur_sock == sockv6)
196 close(sockv6);
197 }
198
199 else {
200 if (cur_sock == sock) // IPv4
201 accept_connection(c, true);
202
203 else // IPv6
204 accept_connection(c, false);
205
206 gettimeofday(&c.startTime, NULL); // Stores the start time for client
207
208 return true;
209 }
210 }
211
212 return false;
213 }
214
215
216
217 /**
218 * Accept new/pending connections
219 *
220 * Will accept new connections and wait if one is not currently ready.
221 * Supports IPv4 and IPv6 sockets
222 *
223 * \param [out] c Client information reference to where the client info will be stored
224 * \param [in] isIPv4 True to indicate if IPv4, false if IPv6
225 */
accept_connection(ClientInfo & c,bool isIPv4)226 void BMPListener::accept_connection(ClientInfo &c, bool isIPv4) {
227 socklen_t c_addr_len = sizeof(c.c_addr); // the client info length
228 socklen_t s_addr_len = sizeof(c.s_addr); // the client info length
229 c.initRec=false; // To indicate INIT message not received
230 int sock = isIPv4 ? this->sock : this->sockv6;
231
232 sockaddr_in *v4_addr = (sockaddr_in *) &c.c_addr;
233 sockaddr_in6 *v6_addr = (sockaddr_in6 *) &c.c_addr;
234
235 bzero(c.c_ip, sizeof(c.s_ip));
236 bzero(c.c_ip, sizeof(c.c_ip));
237
238 // Accept the pending client request, or block till one exists
239 if ((c.c_sock = accept(sock, (struct sockaddr *) &c.c_addr, &c_addr_len)) < 0) {
240 string error = "Server accept connection: ";
241 if (errno != EINTR)
242 error += strerror(errno);
243 else
244 error += "Exiting normally per user request to stop server";
245
246 throw error.c_str();
247 }
248
249 // Update returned class to have address and port of client in text form.
250 if (isIPv4) {
251 inet_ntop(AF_INET, &v4_addr->sin_addr, c.c_ip, sizeof(c.c_ip));
252 snprintf(c.c_port, sizeof(c.c_port), "%hu", ntohs(v4_addr->sin_port));
253 } else {
254 inet_ntop(AF_INET6, &v6_addr->sin6_addr, c.c_ip, sizeof(c.c_ip));
255 snprintf(c.c_port, sizeof(c.c_port), "%hu", ntohs(v6_addr->sin6_port));
256 }
257
258 // Get the server source address and port
259 v4_addr = (sockaddr_in *) &c.s_addr;
260 v6_addr = (sockaddr_in6 *) &c.s_addr;
261
262 if (!getsockname(c.c_sock, (struct sockaddr *) &c.s_addr, &s_addr_len)) {
263 if (isIPv4) {
264 inet_ntop(AF_INET, &v4_addr->sin_addr, c.s_ip, sizeof(c.s_ip));
265 snprintf(c.s_port, sizeof(c.s_port), "%hu", ntohs(v4_addr->sin_port));
266 } else {
267 inet_ntop(AF_INET, &v6_addr->sin6_addr, c.s_ip, sizeof(c.s_ip));
268 snprintf(c.s_port, sizeof(c.s_port), "%hu", ntohs(v6_addr->sin6_port));
269 }
270
271 } else {
272 LOG_ERR("sock=%d: Unable to get the socket name/local address information", c.c_sock);
273
274
275 }
276
277 // Enable TCP Keepalives
278 int on = 1;
279 if (setsockopt(c.c_sock, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) {
280 LOG_NOTICE("%s: sock=%d: Unable to enable tcp keepalives", c.c_ip, c.c_sock);
281 }
282
283 hashRouter(c);
284 }
285
286 /**
287 * Generate BMP router HASH
288 *
289 * \param [in,out] client Reference to client info used to generate the hash.
290 *
291 * \return client.hash_id will be updated with the generated hash
292 */
hashRouter(ClientInfo & client)293 void BMPListener::hashRouter(ClientInfo &client) {
294 string c_hash_str;
295 MsgBusInterface::hash_toStr(cfg->c_hash_id, c_hash_str);
296
297 MD5 hash;
298 hash.update((unsigned char *)client.c_ip, strlen(client.c_ip));
299 hash.update((unsigned char *)c_hash_str.c_str(), c_hash_str.length());
300 hash.finalize();
301
302 // Save the hash
303 unsigned char *hash_bin = hash.raw_digest();
304 memcpy(client.hash_id, hash_bin, 16);
305 delete[] hash_bin;
306 }
307
308 /*
309 * Enable/Disable debug
310 */
enableDebug()311 void BMPListener::enableDebug() {
312 debug = true;
313 }
314
disableDebug()315 void BMPListener::disableDebug() {
316 debug = false;
317 }
318