blob: d530ef7fa0b59075a9b253baa15cd85c25f6cf7d [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 Freyther7309a902011-06-01 14:04:22 +020034#include <fcntl.h>
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +020035#include <errno.h>
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +020036#include <string.h>
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +020037#include <unistd.h>
38
39static void close_connection(struct osmo_pcap_conn *conn)
40{
Holger Hans Peter Freyther54ff0b12011-06-01 16:33:11 +020041 if (conn->rem_fd.fd >= 0) {
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +020042 close(conn->rem_fd.fd);
43 conn->rem_fd.fd = -1;
44 osmo_fd_unregister(&conn->rem_fd);
45 }
46
Holger Hans Peter Freyther54ff0b12011-06-01 16:33:11 +020047 if (conn->local_fd >= 0) {
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +020048 close(conn->local_fd);
49 conn->local_fd = -1;
50 }
51}
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +020052
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +020053static void restart_pcap(struct osmo_pcap_conn *conn)
54{
55 time_t now = time(NULL);
56 struct tm *tm = localtime(&now);
57 char *filename;
58 int rc;
59
60 if (conn->local_fd >= 0) {
61 close(conn->local_fd);
62 conn->local_fd = -1;
63 }
64
Holger Hans Peter Freyther28994282016-08-04 16:14:38 +020065 /* omit any storing/creation of the file */
66 if (conn->no_store) {
67 conn->last_write = *tm;
68 return;
69 }
70
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +020071 filename = talloc_asprintf(conn, "%s/trace-%s-%d%.2d%.2d_%.2d%.2d%.2d.pcap",
72 conn->server->base_path, conn->name,
73 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
74 tm->tm_hour, tm->tm_min, tm->tm_sec);
Daniel Willmannde773862011-07-17 17:48:17 +020075
76 if (!filename) {
77 LOGP(DSERVER, LOGL_ERROR, "Failed to assemble filename for %s.\n", conn->name);
78 return;
79 }
80
Holger Hans Peter Freytherf60990e2011-06-10 15:29:46 +020081 conn->local_fd = creat(filename, 0440);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +020082 if (conn->local_fd < 0) {
83 LOGP(DSERVER, LOGL_ERROR, "Failed to file: '%s'\n", filename);
Daniel Willmannde773862011-07-17 17:48:17 +020084 talloc_free(filename);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +020085 return;
86 }
87
88 rc = write(conn->local_fd, &conn->file_hdr, sizeof(conn->file_hdr));
89 if (rc != sizeof(conn->file_hdr)) {
90 LOGP(DSERVER, LOGL_ERROR, "Failed to write the header: %d\n", errno);
91 close(conn->local_fd);
92 conn->local_fd = -1;
Daniel Willmannde773862011-07-17 17:48:17 +020093 talloc_free(filename);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +020094 return;
95 }
96
97 conn->last_write = *tm;
98 talloc_free(filename);
99}
100
101static void link_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
102{
103 struct pcap_file_header *hdr;
104
105 if (data->len != sizeof(*hdr)) {
106 LOGP(DSERVER, LOGL_ERROR, "The pcap_file_header does not fit.\n");
107 close_connection(conn);
108 return;
109 }
110
111 hdr = (struct pcap_file_header *) &data->data[0];
Holger Hans Peter Freyther28994282016-08-04 16:14:38 +0200112 if (!conn->no_store && conn->local_fd < 0) {
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200113 conn->file_hdr = *hdr;
114 restart_pcap(conn);
115 } else if (memcmp(&conn->file_hdr, hdr, sizeof(*hdr)) != 0) {
116 conn->file_hdr = *hdr;
117 restart_pcap(conn);
118 }
119}
120
121/*
122 * Check if we are past the limit or on a day change
123 */
124static void write_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
125{
126 time_t now = time(NULL);
127 struct tm *tm = localtime(&now);
128 int rc;
129
Holger Hans Peter Freyther28994282016-08-04 16:14:38 +0200130 if (conn->no_store) {
131 conn->last_write = *tm;
132 return;
133 }
134
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200135 if (conn->local_fd < -1) {
136 LOGP(DSERVER, LOGL_ERROR, "No file is open. close connection.\n");
137 close_connection(conn);
138 return;
139 }
140
141 off_t cur = lseek(conn->local_fd, 0, SEEK_CUR);
142 if (cur > conn->server->max_size) {
143 LOGP(DSERVER, LOGL_NOTICE, "Rolling over file for %s\n", conn->name);
144 restart_pcap(conn);
145 } else if (conn->last_write.tm_mday != tm->tm_mday ||
146 conn->last_write.tm_mon != tm->tm_mon ||
147 conn->last_write.tm_year != tm->tm_year) {
148 LOGP(DSERVER, LOGL_NOTICE, "Rolling over file for %s\n", conn->name);
149 restart_pcap(conn);
150 }
151
152 conn->last_write = *tm;
153 rc = write(conn->local_fd, &data->data[0], data->len);
154 if (rc != data->len) {
155 LOGP(DSERVER, LOGL_ERROR, "Failed to write for %s\n", conn->name);
156 close_connection(conn);
157 }
158}
159
160
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200161void osmo_pcap_server_delete(struct osmo_pcap_conn *conn)
162{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200163 close_connection(conn);
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200164 llist_del(&conn->entry);
165 talloc_free(conn);
166}
167
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200168struct osmo_pcap_conn *osmo_pcap_server_find(struct osmo_pcap_server *server,
169 const char *name)
170{
171 struct osmo_pcap_conn *conn;
172 llist_for_each_entry(conn, &server->conn, entry) {
173 if (strcmp(conn->name, name) == 0)
174 return conn;
175 }
176
177 conn = talloc_zero(server, struct osmo_pcap_conn);
178 if (!conn) {
179 LOGP(DSERVER, LOGL_ERROR,
180 "Failed to find the connection.\n");
181 return NULL;
182 }
183
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200184 conn->name = talloc_strdup(conn, name);
185 conn->rem_fd.fd = -1;
Holger Hans Peter Freytherdc3d1dc2011-06-01 16:32:29 +0200186 conn->local_fd = -1;
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200187 conn->server = server;
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200188 conn->data = (struct osmo_pcap_data *) &conn->buf[0];
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200189 llist_add_tail(&conn->entry, &server->conn);
190 return conn;
191}
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200192
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200193static int read_cb_initial(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200194{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200195 int rc;
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200196 rc = read(fd->fd, &conn->buf[sizeof(*conn->data) - conn->pend], conn->pend);
197 if (rc <= 0) {
198 LOGP(DSERVER, LOGL_ERROR,
199 "Too short packet. Got %d, wanted %d\n", rc, conn->data->len);
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200200 close_connection(conn);
201 return -1;
202 }
203
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200204 conn->pend -= rc;
205 if (conn->pend < 0) {
206 LOGP(DSERVER, LOGL_ERROR,
207 "Someone got the pending read wrong: %d\n", conn->pend);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200208 close_connection(conn);
209 return -1;
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200210 } else if (conn->pend == 0) {
211 conn->data->len = ntohs(conn->data->len);
212
Holger Hans Peter Freytherff1a5dc2015-12-03 19:32:04 +0100213 if (conn->data->len > SERVER_MAX_DATA_SIZE) {
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200214 LOGP(DSERVER, LOGL_ERROR,
Holger Hans Peter Freyther26327bd2015-12-03 19:29:38 +0100215 "Implausible data length: %u\n", conn->data->len);
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200216 close_connection(conn);
217 return -1;
218 }
219
220 conn->state = STATE_DATA;
221 conn->pend = conn->data->len;
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200222 }
223
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200224 return 0;
225}
226
227static int read_cb_data(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
228{
229 int rc;
230 rc = read(fd->fd, &conn->data->data[conn->data->len - conn->pend], conn->pend);
231 if (rc <= 0) {
Holger Hans Peter Freyther59bfb582011-06-01 17:00:12 +0200232 LOGP(DSERVER, LOGL_ERROR,
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200233 "Too short packet. Got %d, wanted %d\n", rc, conn->data->len);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200234 close_connection(conn);
235 return -1;
236 }
237
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200238 conn->pend -= rc;
239 if (conn->pend < 0) {
240 LOGP(DSERVER, LOGL_ERROR,
241 "Someone got the pending read wrong: %d\n", conn->pend);
242 close_connection(conn);
243 return -1;
244 } else if (conn->pend == 0) {
245 conn->state = STATE_INITIAL;
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200246 conn->pend = sizeof(*conn->data);
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200247 switch (conn->data->type) {
248 case PKT_LINK_HDR:
249 link_data(conn, conn->data);
250 break;
251 case PKT_LINK_DATA:
252 write_data(conn, conn->data);
253 break;
254 }
255 }
256
257 return 0;
258}
259
260static int read_cb(struct osmo_fd *fd, unsigned int what)
261{
262 struct osmo_pcap_conn *conn;
263
264 conn = fd->data;
265
266 if (conn->state == STATE_INITIAL) {
Daniel Willmannc7401c62011-07-17 17:48:18 +0200267 if (conn->reopen) {
268 LOGP(DSERVER, LOGL_INFO, "Reopening log for %s now.\n", conn->name);
269 restart_pcap(conn);
270 conn->reopen = 0;
271 }
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200272 return read_cb_initial(fd, conn);
273 } else if (conn->state == STATE_DATA) {
274 return read_cb_data(fd, conn);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200275 }
276
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200277 return 0;
278}
279
280static void new_connection(struct osmo_pcap_server *server,
281 struct osmo_pcap_conn *client, int new_fd)
282{
283 close_connection(client);
284
285 memset(&client->file_hdr, 0, sizeof(client->file_hdr));
286 client->rem_fd.fd = new_fd;
287 if (osmo_fd_register(&client->rem_fd) != 0) {
288 LOGP(DSERVER, LOGL_ERROR, "Failed to register fd.\n");
289 client->rem_fd.fd = -1;
290 close(new_fd);
291 return;
292 }
293
294 client->rem_fd.data = client;
295 client->rem_fd.when = BSC_FD_READ;
296 client->rem_fd.cb = read_cb;
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200297 client->state = STATE_INITIAL;
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200298 client->pend = sizeof(*client->data);
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200299}
300
301static int accept_cb(struct osmo_fd *fd, unsigned int when)
302{
303 struct osmo_pcap_conn *conn;
304 struct osmo_pcap_server *server;
305 struct sockaddr_in addr;
306 socklen_t size = sizeof(addr);
307 int new_fd;
308
309 new_fd = accept(fd->fd, (struct sockaddr *) &addr, &size);
310 if (new_fd < 0) {
311 LOGP(DSERVER, LOGL_ERROR, "Failed to accept socket: %d\n", errno);
312 return -1;
313 }
314
315 server = fd->data;
316 llist_for_each_entry(conn, &server->conn, entry) {
317 if (conn->remote_addr.s_addr == addr.sin_addr.s_addr) {
318 LOGP(DSERVER, LOGL_NOTICE,
319 "New connection from %s\n", conn->name);
320 new_connection(server, conn, new_fd);
321 return 0;
322 }
323 }
324
325 LOGP(DSERVER, LOGL_ERROR,
326 "Failed to find client for %s\n", inet_ntoa(addr.sin_addr));
327 close(new_fd);
328 return -1;
329}
330
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200331int osmo_pcap_server_listen(struct osmo_pcap_server *server)
332{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200333 int fd;
334
335 fd = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
Daniel Willmannb0003682011-07-17 17:48:19 +0200336 server->addr, server->port, OSMO_SOCK_F_BIND);
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200337 if (fd < 0) {
338 LOGP(DSERVER, LOGL_ERROR, "Failed to create the server socket.\n");
339 return -1;
340 }
341
342 server->listen_fd.fd = fd;
343 server->listen_fd.when = BSC_FD_READ;
344 server->listen_fd.cb = accept_cb;
345 server->listen_fd.data = server;
346
347 if (osmo_fd_register(&server->listen_fd) != 0) {
348 LOGP(DSERVER, LOGL_ERROR, "Failed to register the socket.\n");
349 close(fd);
350 return -1;
351 }
352
353 return 0;
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200354}
Daniel Willmannc7401c62011-07-17 17:48:18 +0200355
356void osmo_pcap_server_reopen(struct osmo_pcap_server *server)
357{
358 struct osmo_pcap_conn *conn;
359 LOGP(DSERVER, LOGL_INFO, "Reopening all logfiles.\n");
360 llist_for_each_entry(conn, &server->conn, entry) {
361 /* Write the complete packet out first */
362 if (conn->state == STATE_INITIAL) {
363 restart_pcap(conn);
364 } else {
365 LOGP(DSERVER, LOGL_INFO, "Delaying %s until current packet is complete.\n", conn->name);
366 conn->reopen = 1;
367 }
368 }
369}