blob: 3a4ef278306c8b6b77a1507fca1571efb08df6ce [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
65
66 filename = talloc_asprintf(conn, "%s/trace-%s-%d%.2d%.2d_%.2d%.2d%.2d.pcap",
67 conn->server->base_path, conn->name,
68 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
69 tm->tm_hour, tm->tm_min, tm->tm_sec);
Holger Hans Peter Freythere6acfea2011-06-01 15:31:50 +020070 conn->local_fd = creat(filename, 0400);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +020071 if (conn->local_fd < 0) {
72 LOGP(DSERVER, LOGL_ERROR, "Failed to file: '%s'\n", filename);
73 return;
74 }
75
76 rc = write(conn->local_fd, &conn->file_hdr, sizeof(conn->file_hdr));
77 if (rc != sizeof(conn->file_hdr)) {
78 LOGP(DSERVER, LOGL_ERROR, "Failed to write the header: %d\n", errno);
79 close(conn->local_fd);
80 conn->local_fd = -1;
81 return;
82 }
83
84 conn->last_write = *tm;
85 talloc_free(filename);
86}
87
88static void link_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
89{
90 struct pcap_file_header *hdr;
91
92 if (data->len != sizeof(*hdr)) {
93 LOGP(DSERVER, LOGL_ERROR, "The pcap_file_header does not fit.\n");
94 close_connection(conn);
95 return;
96 }
97
98 hdr = (struct pcap_file_header *) &data->data[0];
99 if (conn->local_fd < 0) {
100 conn->file_hdr = *hdr;
101 restart_pcap(conn);
102 } else if (memcmp(&conn->file_hdr, hdr, sizeof(*hdr)) != 0) {
103 conn->file_hdr = *hdr;
104 restart_pcap(conn);
105 }
106}
107
108/*
109 * Check if we are past the limit or on a day change
110 */
111static void write_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
112{
113 time_t now = time(NULL);
114 struct tm *tm = localtime(&now);
115 int rc;
116
117 if (conn->local_fd < -1) {
118 LOGP(DSERVER, LOGL_ERROR, "No file is open. close connection.\n");
119 close_connection(conn);
120 return;
121 }
122
123 off_t cur = lseek(conn->local_fd, 0, SEEK_CUR);
124 if (cur > conn->server->max_size) {
125 LOGP(DSERVER, LOGL_NOTICE, "Rolling over file for %s\n", conn->name);
126 restart_pcap(conn);
127 } else if (conn->last_write.tm_mday != tm->tm_mday ||
128 conn->last_write.tm_mon != tm->tm_mon ||
129 conn->last_write.tm_year != tm->tm_year) {
130 LOGP(DSERVER, LOGL_NOTICE, "Rolling over file for %s\n", conn->name);
131 restart_pcap(conn);
132 }
133
134 conn->last_write = *tm;
135 rc = write(conn->local_fd, &data->data[0], data->len);
136 if (rc != data->len) {
137 LOGP(DSERVER, LOGL_ERROR, "Failed to write for %s\n", conn->name);
138 close_connection(conn);
139 }
140}
141
142
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200143void osmo_pcap_server_delete(struct osmo_pcap_conn *conn)
144{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200145 close_connection(conn);
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200146 llist_del(&conn->entry);
147 talloc_free(conn);
148}
149
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200150struct osmo_pcap_conn *osmo_pcap_server_find(struct osmo_pcap_server *server,
151 const char *name)
152{
153 struct osmo_pcap_conn *conn;
154 llist_for_each_entry(conn, &server->conn, entry) {
155 if (strcmp(conn->name, name) == 0)
156 return conn;
157 }
158
159 conn = talloc_zero(server, struct osmo_pcap_conn);
160 if (!conn) {
161 LOGP(DSERVER, LOGL_ERROR,
162 "Failed to find the connection.\n");
163 return NULL;
164 }
165
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200166 conn->name = talloc_strdup(conn, name);
167 conn->rem_fd.fd = -1;
Holger Hans Peter Freytherdc3d1dc2011-06-01 16:32:29 +0200168 conn->local_fd = -1;
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200169 conn->server = server;
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200170 conn->data = (struct osmo_pcap_data *) &conn->buf[0];
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200171 llist_add_tail(&conn->entry, &server->conn);
172 return conn;
173}
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200174
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200175static int read_cb_initial(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200176{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200177 int rc;
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200178 rc = read(fd->fd, conn->buf, sizeof(*conn->data));
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200179
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200180 if (rc != sizeof(*conn->data)) {
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200181 LOGP(DSERVER, LOGL_ERROR, "Failed to read from %s\n", conn->name);
182 close_connection(conn);
183 return -1;
184 }
185
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200186 conn->data->len = ntohs(conn->data->len);
187 if (conn->data->len > 2000) {
188 LOGP(DSERVER, LOGL_ERROR, "Unplausible result %u\n", conn->data->len);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200189 close_connection(conn);
190 return -1;
191 }
192
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200193 conn->state = STATE_DATA;
194 conn->pend = conn->data->len;
195 return 0;
196}
197
198static int read_cb_data(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
199{
200 int rc;
201 rc = read(fd->fd, &conn->data->data[conn->data->len - conn->pend], conn->pend);
202 if (rc <= 0) {
Holger Hans Peter Freyther59bfb582011-06-01 17:00:12 +0200203 LOGP(DSERVER, LOGL_ERROR,
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200204 "Too short packet. Got %d, wanted %d\n", rc, conn->data->len);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200205 close_connection(conn);
206 return -1;
207 }
208
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200209 conn->pend -= rc;
210 if (conn->pend < 0) {
211 LOGP(DSERVER, LOGL_ERROR,
212 "Someone got the pending read wrong: %d\n", conn->pend);
213 close_connection(conn);
214 return -1;
215 } else if (conn->pend == 0) {
216 conn->state = STATE_INITIAL;
217 switch (conn->data->type) {
218 case PKT_LINK_HDR:
219 link_data(conn, conn->data);
220 break;
221 case PKT_LINK_DATA:
222 write_data(conn, conn->data);
223 break;
224 }
225 }
226
227 return 0;
228}
229
230static int read_cb(struct osmo_fd *fd, unsigned int what)
231{
232 struct osmo_pcap_conn *conn;
233
234 conn = fd->data;
235
236 if (conn->state == STATE_INITIAL) {
237 return read_cb_initial(fd, conn);
238 } else if (conn->state == STATE_DATA) {
239 return read_cb_data(fd, conn);
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200240 }
241
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200242 return 0;
243}
244
245static void new_connection(struct osmo_pcap_server *server,
246 struct osmo_pcap_conn *client, int new_fd)
247{
248 close_connection(client);
249
250 memset(&client->file_hdr, 0, sizeof(client->file_hdr));
251 client->rem_fd.fd = new_fd;
252 if (osmo_fd_register(&client->rem_fd) != 0) {
253 LOGP(DSERVER, LOGL_ERROR, "Failed to register fd.\n");
254 client->rem_fd.fd = -1;
255 close(new_fd);
256 return;
257 }
258
259 client->rem_fd.data = client;
260 client->rem_fd.when = BSC_FD_READ;
261 client->rem_fd.cb = read_cb;
Holger Hans Peter Freyther39d904f2011-06-01 18:49:07 +0200262 client->state = STATE_INITIAL;
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200263}
264
265static int accept_cb(struct osmo_fd *fd, unsigned int when)
266{
267 struct osmo_pcap_conn *conn;
268 struct osmo_pcap_server *server;
269 struct sockaddr_in addr;
270 socklen_t size = sizeof(addr);
271 int new_fd;
272
273 new_fd = accept(fd->fd, (struct sockaddr *) &addr, &size);
274 if (new_fd < 0) {
275 LOGP(DSERVER, LOGL_ERROR, "Failed to accept socket: %d\n", errno);
276 return -1;
277 }
278
279 server = fd->data;
280 llist_for_each_entry(conn, &server->conn, entry) {
281 if (conn->remote_addr.s_addr == addr.sin_addr.s_addr) {
282 LOGP(DSERVER, LOGL_NOTICE,
283 "New connection from %s\n", conn->name);
284 new_connection(server, conn, new_fd);
285 return 0;
286 }
287 }
288
289 LOGP(DSERVER, LOGL_ERROR,
290 "Failed to find client for %s\n", inet_ntoa(addr.sin_addr));
291 close(new_fd);
292 return -1;
293}
294
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200295int osmo_pcap_server_listen(struct osmo_pcap_server *server)
296{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200297 int fd;
298
299 fd = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
300 server->addr, server->port, 1);
301 if (fd < 0) {
302 LOGP(DSERVER, LOGL_ERROR, "Failed to create the server socket.\n");
303 return -1;
304 }
305
306 server->listen_fd.fd = fd;
307 server->listen_fd.when = BSC_FD_READ;
308 server->listen_fd.cb = accept_cb;
309 server->listen_fd.data = server;
310
311 if (osmo_fd_register(&server->listen_fd) != 0) {
312 LOGP(DSERVER, LOGL_ERROR, "Failed to register the socket.\n");
313 close(fd);
314 return -1;
315 }
316
317 return 0;
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200318}