blob: 7127401ab2c8b0107198507fb6b50681ab6cf9d9 [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 Freyther7309a902011-06-01 14:04:22 +020065 filename = talloc_asprintf(conn, "%s/trace-%s-%d%.2d%.2d_%.2d%.2d%.2d.pcap",
66 conn->server->base_path, conn->name,
67 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
68 tm->tm_hour, tm->tm_min, tm->tm_sec);
Daniel Willmannde773862011-07-17 17:48:17 +020069
70 if (!filename) {
71 LOGP(DSERVER, LOGL_ERROR, "Failed to assemble filename for %s.\n", conn->name);
72 return;
73 }
74
Holger Hans Peter Freytherf60990e2011-06-10 15:29:46 +020075 conn->local_fd = creat(filename, 0440);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +020076 if (conn->local_fd < 0) {
77 LOGP(DSERVER, LOGL_ERROR, "Failed to file: '%s'\n", filename);
Daniel Willmannde773862011-07-17 17:48:17 +020078 talloc_free(filename);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +020079 return;
80 }
81
82 rc = write(conn->local_fd, &conn->file_hdr, sizeof(conn->file_hdr));
83 if (rc != sizeof(conn->file_hdr)) {
84 LOGP(DSERVER, LOGL_ERROR, "Failed to write the header: %d\n", errno);
85 close(conn->local_fd);
86 conn->local_fd = -1;
Daniel Willmannde773862011-07-17 17:48:17 +020087 talloc_free(filename);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +020088 return;
89 }
90
91 conn->last_write = *tm;
92 talloc_free(filename);
93}
94
95static void link_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
96{
97 struct pcap_file_header *hdr;
98
99 if (data->len != sizeof(*hdr)) {
100 LOGP(DSERVER, LOGL_ERROR, "The pcap_file_header does not fit.\n");
101 close_connection(conn);
102 return;
103 }
104
105 hdr = (struct pcap_file_header *) &data->data[0];
106 if (conn->local_fd < 0) {
107 conn->file_hdr = *hdr;
108 restart_pcap(conn);
109 } else if (memcmp(&conn->file_hdr, hdr, sizeof(*hdr)) != 0) {
110 conn->file_hdr = *hdr;
111 restart_pcap(conn);
112 }
113}
114
115/*
116 * Check if we are past the limit or on a day change
117 */
118static void write_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
119{
120 time_t now = time(NULL);
121 struct tm *tm = localtime(&now);
122 int rc;
123
124 if (conn->local_fd < -1) {
125 LOGP(DSERVER, LOGL_ERROR, "No file is open. close connection.\n");
126 close_connection(conn);
127 return;
128 }
129
130 off_t cur = lseek(conn->local_fd, 0, SEEK_CUR);
131 if (cur > conn->server->max_size) {
132 LOGP(DSERVER, LOGL_NOTICE, "Rolling over file for %s\n", conn->name);
133 restart_pcap(conn);
134 } else if (conn->last_write.tm_mday != tm->tm_mday ||
135 conn->last_write.tm_mon != tm->tm_mon ||
136 conn->last_write.tm_year != tm->tm_year) {
137 LOGP(DSERVER, LOGL_NOTICE, "Rolling over file for %s\n", conn->name);
138 restart_pcap(conn);
139 }
140
141 conn->last_write = *tm;
142 rc = write(conn->local_fd, &data->data[0], data->len);
143 if (rc != data->len) {
144 LOGP(DSERVER, LOGL_ERROR, "Failed to write for %s\n", conn->name);
145 close_connection(conn);
146 }
147}
148
149
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200150void osmo_pcap_server_delete(struct osmo_pcap_conn *conn)
151{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200152 close_connection(conn);
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200153 llist_del(&conn->entry);
154 talloc_free(conn);
155}
156
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200157struct osmo_pcap_conn *osmo_pcap_server_find(struct osmo_pcap_server *server,
158 const char *name)
159{
160 struct osmo_pcap_conn *conn;
161 llist_for_each_entry(conn, &server->conn, entry) {
162 if (strcmp(conn->name, name) == 0)
163 return conn;
164 }
165
166 conn = talloc_zero(server, struct osmo_pcap_conn);
167 if (!conn) {
168 LOGP(DSERVER, LOGL_ERROR,
169 "Failed to find the connection.\n");
170 return NULL;
171 }
172
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200173 conn->name = talloc_strdup(conn, name);
174 conn->rem_fd.fd = -1;
Holger Hans Peter Freytherdc3d1dc2011-06-01 16:32:29 +0200175 conn->local_fd = -1;
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200176 conn->server = server;
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200177 conn->data = (struct osmo_pcap_data *) &conn->buf[0];
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200178 llist_add_tail(&conn->entry, &server->conn);
179 return conn;
180}
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200181
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200182static int read_cb_initial(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200183{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200184 int rc;
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200185 rc = read(fd->fd, &conn->buf[sizeof(*conn->data) - conn->pend], conn->pend);
186 if (rc <= 0) {
187 LOGP(DSERVER, LOGL_ERROR,
188 "Too short packet. Got %d, wanted %d\n", rc, conn->data->len);
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200189 close_connection(conn);
190 return -1;
191 }
192
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200193 conn->pend -= rc;
194 if (conn->pend < 0) {
195 LOGP(DSERVER, LOGL_ERROR,
196 "Someone got the pending read wrong: %d\n", conn->pend);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200197 close_connection(conn);
198 return -1;
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200199 } else if (conn->pend == 0) {
200 conn->data->len = ntohs(conn->data->len);
201
Holger Hans Peter Freytherff1a5dc2015-12-03 19:32:04 +0100202 if (conn->data->len > SERVER_MAX_DATA_SIZE) {
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200203 LOGP(DSERVER, LOGL_ERROR,
Holger Hans Peter Freyther26327bd2015-12-03 19:29:38 +0100204 "Implausible data length: %u\n", conn->data->len);
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200205 close_connection(conn);
206 return -1;
207 }
208
209 conn->state = STATE_DATA;
210 conn->pend = conn->data->len;
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200211 }
212
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200213 return 0;
214}
215
216static int read_cb_data(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
217{
218 int rc;
219 rc = read(fd->fd, &conn->data->data[conn->data->len - conn->pend], conn->pend);
220 if (rc <= 0) {
Holger Hans Peter Freyther59bfb582011-06-01 17:00:12 +0200221 LOGP(DSERVER, LOGL_ERROR,
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200222 "Too short packet. Got %d, wanted %d\n", rc, conn->data->len);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200223 close_connection(conn);
224 return -1;
225 }
226
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200227 conn->pend -= rc;
228 if (conn->pend < 0) {
229 LOGP(DSERVER, LOGL_ERROR,
230 "Someone got the pending read wrong: %d\n", conn->pend);
231 close_connection(conn);
232 return -1;
233 } else if (conn->pend == 0) {
234 conn->state = STATE_INITIAL;
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200235 conn->pend = sizeof(*conn->data);
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200236 switch (conn->data->type) {
237 case PKT_LINK_HDR:
238 link_data(conn, conn->data);
239 break;
240 case PKT_LINK_DATA:
241 write_data(conn, conn->data);
242 break;
243 }
244 }
245
246 return 0;
247}
248
249static int read_cb(struct osmo_fd *fd, unsigned int what)
250{
251 struct osmo_pcap_conn *conn;
252
253 conn = fd->data;
254
255 if (conn->state == STATE_INITIAL) {
Daniel Willmannc7401c62011-07-17 17:48:18 +0200256 if (conn->reopen) {
257 LOGP(DSERVER, LOGL_INFO, "Reopening log for %s now.\n", conn->name);
258 restart_pcap(conn);
259 conn->reopen = 0;
260 }
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200261 return read_cb_initial(fd, conn);
262 } else if (conn->state == STATE_DATA) {
263 return read_cb_data(fd, conn);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200264 }
265
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200266 return 0;
267}
268
269static void new_connection(struct osmo_pcap_server *server,
270 struct osmo_pcap_conn *client, int new_fd)
271{
272 close_connection(client);
273
274 memset(&client->file_hdr, 0, sizeof(client->file_hdr));
275 client->rem_fd.fd = new_fd;
276 if (osmo_fd_register(&client->rem_fd) != 0) {
277 LOGP(DSERVER, LOGL_ERROR, "Failed to register fd.\n");
278 client->rem_fd.fd = -1;
279 close(new_fd);
280 return;
281 }
282
283 client->rem_fd.data = client;
284 client->rem_fd.when = BSC_FD_READ;
285 client->rem_fd.cb = read_cb;
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200286 client->state = STATE_INITIAL;
Holger Hans Peter Freyther91eaae32011-06-02 17:58:46 +0200287 client->pend = sizeof(*client->data);
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200288}
289
290static int accept_cb(struct osmo_fd *fd, unsigned int when)
291{
292 struct osmo_pcap_conn *conn;
293 struct osmo_pcap_server *server;
294 struct sockaddr_in addr;
295 socklen_t size = sizeof(addr);
296 int new_fd;
297
298 new_fd = accept(fd->fd, (struct sockaddr *) &addr, &size);
299 if (new_fd < 0) {
300 LOGP(DSERVER, LOGL_ERROR, "Failed to accept socket: %d\n", errno);
301 return -1;
302 }
303
304 server = fd->data;
305 llist_for_each_entry(conn, &server->conn, entry) {
306 if (conn->remote_addr.s_addr == addr.sin_addr.s_addr) {
307 LOGP(DSERVER, LOGL_NOTICE,
308 "New connection from %s\n", conn->name);
309 new_connection(server, conn, new_fd);
310 return 0;
311 }
312 }
313
314 LOGP(DSERVER, LOGL_ERROR,
315 "Failed to find client for %s\n", inet_ntoa(addr.sin_addr));
316 close(new_fd);
317 return -1;
318}
319
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200320int osmo_pcap_server_listen(struct osmo_pcap_server *server)
321{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200322 int fd;
323
324 fd = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
Daniel Willmannb0003682011-07-17 17:48:19 +0200325 server->addr, server->port, OSMO_SOCK_F_BIND);
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200326 if (fd < 0) {
327 LOGP(DSERVER, LOGL_ERROR, "Failed to create the server socket.\n");
328 return -1;
329 }
330
331 server->listen_fd.fd = fd;
332 server->listen_fd.when = BSC_FD_READ;
333 server->listen_fd.cb = accept_cb;
334 server->listen_fd.data = server;
335
336 if (osmo_fd_register(&server->listen_fd) != 0) {
337 LOGP(DSERVER, LOGL_ERROR, "Failed to register the socket.\n");
338 close(fd);
339 return -1;
340 }
341
342 return 0;
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200343}
Daniel Willmannc7401c62011-07-17 17:48:18 +0200344
345void osmo_pcap_server_reopen(struct osmo_pcap_server *server)
346{
347 struct osmo_pcap_conn *conn;
348 LOGP(DSERVER, LOGL_INFO, "Reopening all logfiles.\n");
349 llist_for_each_entry(conn, &server->conn, entry) {
350 /* Write the complete packet out first */
351 if (conn->state == STATE_INITIAL) {
352 restart_pcap(conn);
353 } else {
354 LOGP(DSERVER, LOGL_INFO, "Delaying %s until current packet is complete.\n", conn->name);
355 conn->reopen = 1;
356 }
357 }
358}