blob: da3e777ceb8ba42326264b490417a17d3acaf086 [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{
41 if (conn->rem_fd.fd != -1) {
42 close(conn->rem_fd.fd);
43 conn->rem_fd.fd = -1;
44 osmo_fd_unregister(&conn->rem_fd);
45 }
46
47 if (conn->local_fd != -1) {
48 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;
168 conn->server = server;
Holger Hans Peter Freyther9f6127f2011-05-31 22:52:41 +0200169 llist_add_tail(&conn->entry, &server->conn);
170 return conn;
171}
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200172
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200173static int read_cb(struct osmo_fd *fd, unsigned int what)
174{
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200175 struct osmo_pcap_data *data;
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200176 struct osmo_pcap_conn *conn;
177 char buf[4096];
178 int rc;
179
180 conn = fd->data;
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200181 data = (struct osmo_pcap_data *) &buf[0];
182
183 rc = read(fd->fd, buf, sizeof(*data));
184 if (rc != sizeof(*data)) {
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200185 LOGP(DSERVER, LOGL_ERROR, "Failed to read from %s\n", conn->name);
186 close_connection(conn);
187 return -1;
188 }
189
Holger Hans Peter Freyther7309a902011-06-01 14:04:22 +0200190 if (data->len > 2000) {
191 LOGP(DSERVER, LOGL_ERROR, "Unplausible result %u\n", data->len);
192 close_connection(conn);
193 return -1;
194 }
195
196 rc = read(fd->fd, &data->data[0], data->len);
197 if (rc != data->len) {
198 LOGP(DSERVER, LOGL_ERROR, "Two short packet %d\n", rc);
199 close_connection(conn);
200 return -1;
201 }
202
203 switch (data->type) {
204 case PKT_LINK_HDR:
205 link_data(conn, data);
206 break;
207 case PKT_LINK_DATA:
208 write_data(conn, data);
209 break;
210 }
211
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200212 return 0;
213}
214
215static void new_connection(struct osmo_pcap_server *server,
216 struct osmo_pcap_conn *client, int new_fd)
217{
218 close_connection(client);
219
220 memset(&client->file_hdr, 0, sizeof(client->file_hdr));
221 client->rem_fd.fd = new_fd;
222 if (osmo_fd_register(&client->rem_fd) != 0) {
223 LOGP(DSERVER, LOGL_ERROR, "Failed to register fd.\n");
224 client->rem_fd.fd = -1;
225 close(new_fd);
226 return;
227 }
228
229 client->rem_fd.data = client;
230 client->rem_fd.when = BSC_FD_READ;
231 client->rem_fd.cb = read_cb;
232}
233
234static int accept_cb(struct osmo_fd *fd, unsigned int when)
235{
236 struct osmo_pcap_conn *conn;
237 struct osmo_pcap_server *server;
238 struct sockaddr_in addr;
239 socklen_t size = sizeof(addr);
240 int new_fd;
241
242 new_fd = accept(fd->fd, (struct sockaddr *) &addr, &size);
243 if (new_fd < 0) {
244 LOGP(DSERVER, LOGL_ERROR, "Failed to accept socket: %d\n", errno);
245 return -1;
246 }
247
248 server = fd->data;
249 llist_for_each_entry(conn, &server->conn, entry) {
250 if (conn->remote_addr.s_addr == addr.sin_addr.s_addr) {
251 LOGP(DSERVER, LOGL_NOTICE,
252 "New connection from %s\n", conn->name);
253 new_connection(server, conn, new_fd);
254 return 0;
255 }
256 }
257
258 LOGP(DSERVER, LOGL_ERROR,
259 "Failed to find client for %s\n", inet_ntoa(addr.sin_addr));
260 close(new_fd);
261 return -1;
262}
263
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200264int osmo_pcap_server_listen(struct osmo_pcap_server *server)
265{
Holger Hans Peter Freyther80b8b602011-05-31 23:42:20 +0200266 int fd;
267
268 fd = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
269 server->addr, server->port, 1);
270 if (fd < 0) {
271 LOGP(DSERVER, LOGL_ERROR, "Failed to create the server socket.\n");
272 return -1;
273 }
274
275 server->listen_fd.fd = fd;
276 server->listen_fd.when = BSC_FD_READ;
277 server->listen_fd.cb = accept_cb;
278 server->listen_fd.data = server;
279
280 if (osmo_fd_register(&server->listen_fd) != 0) {
281 LOGP(DSERVER, LOGL_ERROR, "Failed to register the socket.\n");
282 close(fd);
283 return -1;
284 }
285
286 return 0;
Holger Hans Peter Freyther13619dd2011-05-31 22:09:08 +0200287}