blob: 6812f35af4e7eec0e0e8128c23ef6a6fc9467f80 [file] [log] [blame]
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +02001/*
2 * osmo-pcap-server code
3 *
4 * (C) 2011 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * (C) 2011 by On-Waves
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#include <osmo-pcap/osmo_pcap_server.h>
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +020024#include <osmo-pcap/common.h>
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +020025#include <osmo-pcap/wireformat.h>
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +020026
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +020027#include <osmocom/core/socket.h>
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +020028#include <osmocom/core/talloc.h>
29
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +020030#include <arpa/inet.h>
31#include <sys/socket.h>
32#include <sys/types.h>
33
Holger Hans Peter Freythere0248692016-08-05 15:47:08 +020034#include <zmq.h>
35
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +020036#include <fcntl.h>
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +020037#include <errno.h>
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +020038#include <string.h>
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +020039#include <unistd.h>
40
Holger Hans Peter Freythere0248692016-08-05 15:47:08 +020041static void pcap_zmq_send(void *publ, const void *data, size_t len, int flags)
42{
43 int rc;
44 zmq_msg_t msg;
45
46 rc = zmq_msg_init_size(&msg, len);
47 if (rc != 0) {
48 /* sigh.. we said SNDMORE but can't... */
49 LOGP(DSERVER, LOGL_ERROR, "Failed to init rc=%d errno=%d/%s\n",
50 rc, errno, strerror(errno));
51 return;
52 }
53 memcpy(zmq_msg_data(&msg), data, len);
54 rc = zmq_msg_send(&msg, publ, flags);
55 if (rc == -1) {
56 /* is the zmq_msg now owned? leak??? */
57 LOGP(DSERVER, LOGL_ERROR, "Failed to send data rc=%d errno=%d/%s\n",
58 rc, errno, strerror(errno));
59 return;
60 }
61}
62
63static void client_event(struct osmo_pcap_conn *conn,
64 const char *event, const char *data)
65{
66 char *event_name;
67
68 if (!conn->server->zmq_publ)
69 return;
70
71 /*
72 * This multi-part support is insane... so if we lose the first
73 * or the last part of the multipart message stuff is going out
74 * of sync. *great* As we can't do anything about it right now
75 * just close the eyese and send it.
76 */
77 event_name = talloc_asprintf(conn, "event.v1.%s.%s",
78 event, conn->name);
79 pcap_zmq_send(conn->server->zmq_publ,
80 event_name, strlen(event_name),
81 data ? ZMQ_SNDMORE : 0);
82 talloc_free(event_name);
83 if (data)
84 pcap_zmq_send(conn->server->zmq_publ, data, strlen(data), 0);
85}
86
87static void client_data(struct osmo_pcap_conn *conn,
88 struct osmo_pcap_data *data)
89{
90 char *event_name;
91
92 if (!conn->server->zmq_publ)
93 return;
94
95 /*
96 * This multi-part support is insane... so if we lose the first
97 * or the last part of the multipart message stuff is going out
98 * of sync. *great* As we can't do anything about it right now
99 * just close the eyese and send it.
100 */
101 event_name = talloc_asprintf(conn, "data.v1.%s", conn->name);
102 pcap_zmq_send(conn->server->zmq_publ, event_name, strlen(event_name), ZMQ_SNDMORE);
103 talloc_free(event_name);
104
105 pcap_zmq_send(conn->server->zmq_publ,
106 &conn->file_hdr, sizeof(conn->file_hdr),
107 ZMQ_SNDMORE);
108 pcap_zmq_send(conn->server->zmq_publ,
109 &data->data[0], data->len,
110 0);
111}
112
113void osmo_pcap_server_close_trace(struct osmo_pcap_conn *conn)
114{
115 if (conn->local_fd >= 0) {
116 close(conn->local_fd);
117 conn->local_fd = -1;
118 }
119
120 if (conn->curr_filename) {
121 client_event(conn, "closingtracefile", conn->curr_filename);
122 talloc_free(conn->curr_filename);
123 conn->curr_filename = NULL;
124 }
125}
126
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200127static void close_connection(struct osmo_pcap_conn *conn)
128{
Holger Hans Peter Freyther54ff0b12011-06-01 16:33:11 +0200129 if (conn->rem_fd.fd >= 0) {
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200130 close(conn->rem_fd.fd);
131 conn->rem_fd.fd = -1;
132 osmo_fd_unregister(&conn->rem_fd);
133 }
134
Holger Hans Peter Freythere0248692016-08-05 15:47:08 +0200135 osmo_pcap_server_close_trace(conn);
136 client_event(conn, "disconnect", NULL);
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200137}
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200138
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200139static void restart_pcap(struct osmo_pcap_conn *conn)
140{
141 time_t now = time(NULL);
142 struct tm *tm = localtime(&now);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200143 int rc;
144
Holger Hans Peter Freythere0248692016-08-05 15:47:08 +0200145 osmo_pcap_server_close_trace(conn);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200146
Holger Hans Peter Freyther28994282016-08-04 16:14:38 +0200147 /* omit any storing/creation of the file */
148 if (conn->no_store) {
149 conn->last_write = *tm;
Holger Hans Peter Freythere0248692016-08-05 15:47:08 +0200150 talloc_free(conn->curr_filename);
151 conn->curr_filename = NULL;
Holger Hans Peter Freyther28994282016-08-04 16:14:38 +0200152 return;
153 }
154
Holger Hans Peter Freythere0248692016-08-05 15:47:08 +0200155 conn->curr_filename = talloc_asprintf(conn, "%s/trace-%s-%d%.2d%.2d_%.2d%.2d%.2d.pcap",
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200156 conn->server->base_path, conn->name,
157 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
158 tm->tm_hour, tm->tm_min, tm->tm_sec);
Daniel Willmannde773862011-07-17 17:48:17 +0200159
Holger Hans Peter Freythere0248692016-08-05 15:47:08 +0200160 if (!conn->curr_filename) {
Daniel Willmannde773862011-07-17 17:48:17 +0200161 LOGP(DSERVER, LOGL_ERROR, "Failed to assemble filename for %s.\n", conn->name);
162 return;
163 }
164
Holger Hans Peter Freythere0248692016-08-05 15:47:08 +0200165 conn->local_fd = creat(conn->curr_filename, 0440);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200166 if (conn->local_fd < 0) {
Holger Hans Peter Freythere0248692016-08-05 15:47:08 +0200167 LOGP(DSERVER, LOGL_ERROR, "Failed to file: '%s'\n", conn->curr_filename);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200168 return;
169 }
170
171 rc = write(conn->local_fd, &conn->file_hdr, sizeof(conn->file_hdr));
172 if (rc != sizeof(conn->file_hdr)) {
173 LOGP(DSERVER, LOGL_ERROR, "Failed to write the header: %d\n", errno);
174 close(conn->local_fd);
175 conn->local_fd = -1;
176 return;
177 }
178
179 conn->last_write = *tm;
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200180}
181
182static void link_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
183{
184 struct pcap_file_header *hdr;
185
186 if (data->len != sizeof(*hdr)) {
187 LOGP(DSERVER, LOGL_ERROR, "The pcap_file_header does not fit.\n");
188 close_connection(conn);
189 return;
190 }
191
192 hdr = (struct pcap_file_header *) &data->data[0];
Holger Hans Peter Freyther28994282016-08-04 16:14:38 +0200193 if (!conn->no_store && conn->local_fd < 0) {
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200194 conn->file_hdr = *hdr;
195 restart_pcap(conn);
196 } else if (memcmp(&conn->file_hdr, hdr, sizeof(*hdr)) != 0) {
197 conn->file_hdr = *hdr;
198 restart_pcap(conn);
199 }
200}
201
202/*
203 * Check if we are past the limit or on a day change
204 */
205static void write_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
206{
207 time_t now = time(NULL);
208 struct tm *tm = localtime(&now);
209 int rc;
210
Holger Hans Peter Freythere0248692016-08-05 15:47:08 +0200211 client_data(conn, data);
212
Holger Hans Peter Freyther28994282016-08-04 16:14:38 +0200213 if (conn->no_store) {
214 conn->last_write = *tm;
215 return;
216 }
217
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200218 if (conn->local_fd < -1) {
219 LOGP(DSERVER, LOGL_ERROR, "No file is open. close connection.\n");
220 close_connection(conn);
221 return;
222 }
223
224 off_t cur = lseek(conn->local_fd, 0, SEEK_CUR);
225 if (cur > conn->server->max_size) {
226 LOGP(DSERVER, LOGL_NOTICE, "Rolling over file for %s\n", conn->name);
227 restart_pcap(conn);
228 } else if (conn->last_write.tm_mday != tm->tm_mday ||
229 conn->last_write.tm_mon != tm->tm_mon ||
230 conn->last_write.tm_year != tm->tm_year) {
231 LOGP(DSERVER, LOGL_NOTICE, "Rolling over file for %s\n", conn->name);
232 restart_pcap(conn);
233 }
234
235 conn->last_write = *tm;
236 rc = write(conn->local_fd, &data->data[0], data->len);
237 if (rc != data->len) {
238 LOGP(DSERVER, LOGL_ERROR, "Failed to write for %s\n", conn->name);
239 close_connection(conn);
240 }
241}
242
243
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200244void osmo_pcap_server_delete(struct osmo_pcap_conn *conn)
245{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200246 close_connection(conn);
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200247 llist_del(&conn->entry);
248 talloc_free(conn);
249}
250
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200251struct osmo_pcap_conn *osmo_pcap_server_find(struct osmo_pcap_server *server,
252 const char *name)
253{
254 struct osmo_pcap_conn *conn;
255 llist_for_each_entry(conn, &server->conn, entry) {
256 if (strcmp(conn->name, name) == 0)
257 return conn;
258 }
259
260 conn = talloc_zero(server, struct osmo_pcap_conn);
261 if (!conn) {
262 LOGP(DSERVER, LOGL_ERROR,
263 "Failed to find the connection.\n");
264 return NULL;
265 }
266
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200267 conn->name = talloc_strdup(conn, name);
268 conn->rem_fd.fd = -1;
Holger Hans Peter Freytherdc3d1dc2011-06-01 16:32:29 +0200269 conn->local_fd = -1;
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200270 conn->server = server;
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200271 conn->data = (struct osmo_pcap_data *) &conn->buf[0];
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200272 llist_add_tail(&conn->entry, &server->conn);
273 return conn;
274}
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200275
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200276static int read_cb_initial(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200277{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200278 int rc;
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200279 rc = read(fd->fd, &conn->buf[sizeof(*conn->data) - conn->pend], conn->pend);
280 if (rc <= 0) {
281 LOGP(DSERVER, LOGL_ERROR,
282 "Too short packet. Got %d, wanted %d\n", rc, conn->data->len);
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200283 close_connection(conn);
284 return -1;
285 }
286
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200287 conn->pend -= rc;
288 if (conn->pend < 0) {
289 LOGP(DSERVER, LOGL_ERROR,
290 "Someone got the pending read wrong: %d\n", conn->pend);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200291 close_connection(conn);
292 return -1;
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200293 } else if (conn->pend == 0) {
294 conn->data->len = ntohs(conn->data->len);
295
Holger Hans Peter Freytherff1a5dc2015-12-03 19:32:04 +0100296 if (conn->data->len > SERVER_MAX_DATA_SIZE) {
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200297 LOGP(DSERVER, LOGL_ERROR,
Holger Hans Peter Freyther26327bd2015-12-03 19:29:38 +0100298 "Implausible data length: %u\n", conn->data->len);
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200299 close_connection(conn);
300 return -1;
301 }
302
303 conn->state = STATE_DATA;
304 conn->pend = conn->data->len;
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200305 }
306
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200307 return 0;
308}
309
310static int read_cb_data(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
311{
312 int rc;
313 rc = read(fd->fd, &conn->data->data[conn->data->len - conn->pend], conn->pend);
314 if (rc <= 0) {
Holger Hans Peter Freyther59bfb582011-06-01 17:00:12 +0200315 LOGP(DSERVER, LOGL_ERROR,
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200316 "Too short packet. Got %d, wanted %d\n", rc, conn->data->len);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200317 close_connection(conn);
318 return -1;
319 }
320
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200321 conn->pend -= rc;
322 if (conn->pend < 0) {
323 LOGP(DSERVER, LOGL_ERROR,
324 "Someone got the pending read wrong: %d\n", conn->pend);
325 close_connection(conn);
326 return -1;
327 } else if (conn->pend == 0) {
328 conn->state = STATE_INITIAL;
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200329 conn->pend = sizeof(*conn->data);
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200330 switch (conn->data->type) {
331 case PKT_LINK_HDR:
332 link_data(conn, conn->data);
333 break;
334 case PKT_LINK_DATA:
335 write_data(conn, conn->data);
336 break;
337 }
338 }
339
340 return 0;
341}
342
343static int read_cb(struct osmo_fd *fd, unsigned int what)
344{
345 struct osmo_pcap_conn *conn;
346
347 conn = fd->data;
348
349 if (conn->state == STATE_INITIAL) {
Daniel Willmannc7401c62011-07-17 17:48:18 +0200350 if (conn->reopen) {
351 LOGP(DSERVER, LOGL_INFO, "Reopening log for %s now.\n", conn->name);
352 restart_pcap(conn);
353 conn->reopen = 0;
354 }
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200355 return read_cb_initial(fd, conn);
356 } else if (conn->state == STATE_DATA) {
357 return read_cb_data(fd, conn);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200358 }
359
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200360 return 0;
361}
362
363static void new_connection(struct osmo_pcap_server *server,
364 struct osmo_pcap_conn *client, int new_fd)
365{
366 close_connection(client);
367
368 memset(&client->file_hdr, 0, sizeof(client->file_hdr));
369 client->rem_fd.fd = new_fd;
370 if (osmo_fd_register(&client->rem_fd) != 0) {
371 LOGP(DSERVER, LOGL_ERROR, "Failed to register fd.\n");
372 client->rem_fd.fd = -1;
373 close(new_fd);
374 return;
375 }
376
377 client->rem_fd.data = client;
378 client->rem_fd.when = BSC_FD_READ;
379 client->rem_fd.cb = read_cb;
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200380 client->state = STATE_INITIAL;
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200381 client->pend = sizeof(*client->data);
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200382}
383
384static int accept_cb(struct osmo_fd *fd, unsigned int when)
385{
386 struct osmo_pcap_conn *conn;
387 struct osmo_pcap_server *server;
388 struct sockaddr_in addr;
389 socklen_t size = sizeof(addr);
390 int new_fd;
391
392 new_fd = accept(fd->fd, (struct sockaddr *) &addr, &size);
393 if (new_fd < 0) {
394 LOGP(DSERVER, LOGL_ERROR, "Failed to accept socket: %d\n", errno);
395 return -1;
396 }
397
398 server = fd->data;
399 llist_for_each_entry(conn, &server->conn, entry) {
400 if (conn->remote_addr.s_addr == addr.sin_addr.s_addr) {
401 LOGP(DSERVER, LOGL_NOTICE,
402 "New connection from %s\n", conn->name);
Holger Hans Peter Freythere0248692016-08-05 15:47:08 +0200403 client_event(conn, "connect", NULL);
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200404 new_connection(server, conn, new_fd);
405 return 0;
406 }
407 }
408
409 LOGP(DSERVER, LOGL_ERROR,
410 "Failed to find client for %s\n", inet_ntoa(addr.sin_addr));
411 close(new_fd);
412 return -1;
413}
414
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200415int osmo_pcap_server_listen(struct osmo_pcap_server *server)
416{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200417 int fd;
418
419 fd = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
Daniel Willmannb0003682011-07-17 17:48:19 +0200420 server->addr, server->port, OSMO_SOCK_F_BIND);
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200421 if (fd < 0) {
422 LOGP(DSERVER, LOGL_ERROR, "Failed to create the server socket.\n");
423 return -1;
424 }
425
426 server->listen_fd.fd = fd;
427 server->listen_fd.when = BSC_FD_READ;
428 server->listen_fd.cb = accept_cb;
429 server->listen_fd.data = server;
430
431 if (osmo_fd_register(&server->listen_fd) != 0) {
432 LOGP(DSERVER, LOGL_ERROR, "Failed to register the socket.\n");
433 close(fd);
434 return -1;
435 }
436
437 return 0;
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200438}
Daniel Willmannc7401c62011-07-17 17:48:18 +0200439
440void osmo_pcap_server_reopen(struct osmo_pcap_server *server)
441{
442 struct osmo_pcap_conn *conn;
443 LOGP(DSERVER, LOGL_INFO, "Reopening all logfiles.\n");
444 llist_for_each_entry(conn, &server->conn, entry) {
445 /* Write the complete packet out first */
446 if (conn->state == STATE_INITIAL) {
447 restart_pcap(conn);
448 } else {
449 LOGP(DSERVER, LOGL_INFO, "Delaying %s until current packet is complete.\n", conn->name);
450 conn->reopen = 1;
451 }
452 }
453}