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