1 /*
2  * Copyright (c) 2015 Intel Corporation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define EXTRA_DEBUG 0
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdbool.h>
22 #include <stdarg.h>
23 #include <syslog.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <signal.h>
29 #include <sys/uio.h>
30 #include <sys/signalfd.h>
31 #include <sys/time.h>
32 #include <linux/if_ether.h>
33 #include <glib.h>
34 #include <pcap/pcap.h>
35 
36 #define PIPE_IN		".in"
37 #define PIPE_OUT	".out"
38 #define DUMMY_RADIO_15_4_FRAME_TYPE     0xF0
39 
40 static uint8_t input1[128];
41 static uint8_t input1_len, input1_offset, input1_type;
42 
43 static uint8_t input2[128];
44 static uint8_t input2_len, input2_offset, input2_type;
45 
46 static GMainLoop *main_loop = NULL;
47 static char *path = NULL;
48 static char *pipe_1_in = NULL, *pipe_1_out = NULL;
49 static char *pipe_2_in = NULL, *pipe_2_out = NULL;
50 static struct pcap_fd *pcap;
51 static int fd1_in = -1, fd2_in = -1;
52 
53 struct pcap_fd {
54 	int fd;
55 };
56 
57 #define PCAP_FILE_HDR_SIZE (sizeof(struct pcap_file_header))
58 
59 struct pcap_frame {
60 	uint32_t ts_sec;
61 	uint32_t ts_usec;
62 	uint32_t caplen;
63 	uint32_t len;
64 } __attribute__ ((packed));
65 #define PCAP_FRAME_SIZE (sizeof(struct pcap_frame))
66 
67 struct debug_desc {
68 	const char *name;
69 	const char *file;
70 #define DEBUG_FLAG_DEFAULT (0)
71 #define DEBUG_FLAG_PRINT   (1 << 0)
72 	unsigned int flags;
73 } __attribute__((aligned(8)));
74 
debug(const char * format,...)75 void debug(const char *format, ...)
76 {
77 	va_list ap;
78 
79 	va_start(ap, format);
80 
81 	vsyslog(LOG_DEBUG, format, ap);
82 
83 	va_end(ap);
84 }
85 
86 #define DBG(fmt, arg...) do { \
87 	debug("%s:%s() " fmt, __FILE__, __FUNCTION__ , ## arg); \
88 } while (0)
89 
log_init(const char * debug,gboolean detach,gboolean perror)90 int log_init(const char *debug, gboolean detach, gboolean perror)
91 {
92 	int option = LOG_NDELAY | LOG_PID;
93 	const char *name = NULL, *file = NULL;
94 
95 	if (perror)
96 		 option |= LOG_PERROR;
97 
98 	openlog("monitor", option, LOG_DAEMON);
99 
100 	syslog(LOG_INFO, "monitor 15.4");
101 
102 	return 0;
103 }
104 
log_cleanup(void)105 void log_cleanup(void)
106 {
107 	syslog(LOG_INFO, "Exit");
108 
109 	closelog();
110 }
111 
monitor_pcap_free(void)112 void monitor_pcap_free(void)
113 {
114 	if (!pcap)
115 		return;
116 
117 	if (pcap->fd >= 0)
118 		close(pcap->fd);
119 
120 	g_free(pcap);
121 }
122 
monitor_pcap_create(const char * pathname)123 bool monitor_pcap_create(const char *pathname)
124 {
125 	struct pcap_file_header hdr;
126 	ssize_t len;
127 
128 	pcap = g_new0(struct pcap_fd, 1);
129 	pcap->fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
130 					S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
131 	if (pcap->fd < 0) {
132 		DBG("Failed to create PCAP file");
133 		g_free(pcap);
134 		return false;
135 	}
136 
137 	memset(&hdr, 0, PCAP_FILE_HDR_SIZE);
138 	hdr.magic = 0xa1b2c3d4;
139 	hdr.version_major = 0x0002;
140 	hdr.version_minor = 0x0004;
141 	hdr.thiszone = 0;
142 	hdr.sigfigs = 0;
143 	hdr.snaplen = 0x0000ffff;
144 	/*
145 	 * http://www.tcpdump.org/linktypes.html
146 	 * LINKTYPE_IEEE802_15_4_NOFCS : 230
147 	 */
148 	hdr.linktype = 0x000000E6;
149 
150 	len = write(pcap->fd, &hdr, PCAP_FILE_HDR_SIZE);
151 	if (len < 0) {
152 		DBG("Failed to write PCAP header");
153 		goto failed;
154 	}
155 
156 	if (len != PCAP_FILE_HDR_SIZE) {
157 		DBG("Written PCAP header size mimatch\n");
158 		goto failed;
159 	}
160 
161 	return true;
162 
163 failed:
164 	monitor_pcap_free();
165 
166 	return false;
167 }
168 
monitor_pcap_write(const void * data,uint32_t size)169 bool monitor_pcap_write(const void *data, uint32_t size)
170 {
171 	struct pcap_frame frame;
172 	struct timeval tv;
173 	ssize_t len;
174 
175 	if (!pcap)
176 		return false;
177 
178 	memset(&frame, 0, PCAP_FRAME_SIZE);
179 
180 	gettimeofday(&tv, NULL);
181 	frame.ts_sec = tv.tv_sec;
182 	frame.ts_usec = tv.tv_usec;
183 	frame.caplen = size;
184 	frame.len = size;
185 
186 	len = write(pcap->fd, &frame, PCAP_FRAME_SIZE);
187 	if (len < 0 || len != PCAP_FRAME_SIZE) {
188 		DBG("Failed to write PCAP frame or size mismatch %d\n", len);
189 		return false;
190 	}
191 
192 	fsync(pcap->fd);
193 
194 	len = write(pcap->fd, data, size);
195 	if (len < 0 || len != size) {
196 		DBG("Failed to write PCAP data or size mismatch %d\n", len);
197 		return false;
198 	}
199 
200 	fsync(pcap->fd);
201 	return true;
202 }
203 
fifo_handler1(GIOChannel * channel,GIOCondition cond,gpointer user_data)204 static gboolean fifo_handler1(GIOChannel *channel, GIOCondition cond,
205 							gpointer user_data)
206 {
207 	static bool starting = true;
208 	unsigned char buf[1];
209 	ssize_t result;
210 	int fd;
211 
212 	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
213 		DBG("First pipe closed");
214 		return FALSE;
215 	}
216 
217 	memset(buf, 0, sizeof(buf));
218 	fd = g_io_channel_unix_get_fd(channel);
219 
220 	result = read(fd, buf, 1);
221 	if (result != 1) {
222 		DBG("Failed to read %lu", result);
223 		return FALSE;
224 	}
225 
226 	if (write(fd2_in, buf, 1) < 0) {
227 		DBG("Error, write failed to %s", pipe_2_in);
228 		return FALSE;
229 	}
230 #if EXTRA_DEBUG
231 	DBG("[%d/%d] starting %d fifo 1 read from %d and wrote %d bytes to %d",
232 	    input1_offset, input1_len, starting, fd, 1, fd2_in);
233 #endif
234 
235 	if (starting && input1_len == 0 && input1_offset == 0 &&
236 	    buf[0] == 0 && input1_type == 0) {
237 		/* sync byte */
238 		goto done;
239 	} else if (starting && input1_len == 0 && input1_offset == 0
240 		   && buf[0] != 0) {
241 		starting = false;
242 	}
243 
244 #if EXTRA_DEBUG
245 	DBG("Pipe 1 byte 0x%x", buf[0]);
246 #endif
247 	if (input1_len == 0 && input1_offset == 0 &&
248 		buf[0] == DUMMY_RADIO_15_4_FRAME_TYPE) {
249 		input1_type = buf[0];
250 #if EXTRA_DEBUG
251 		DBG("Frame starting in pipe 1");
252 #endif
253 		goto done;
254 	}
255 
256 	if (input1_len == 0 && input1_offset == 0 &&
257 		input1_type == DUMMY_RADIO_15_4_FRAME_TYPE) {
258 		input1_len = buf[0];
259 #if EXTRA_DEBUG
260 		DBG("Expecting pipe 1 buf len %d\n", input1_len);
261 #endif
262 		goto done;
263 	}
264 
265 	if (input1_len) {
266 		input1[input1_offset++] = buf[0];
267 	}
268 
269 	if (input1_len && input1_len == input1_offset) {
270 		DBG("Received %d bytes in pipe 1", input1_len);
271 
272 		monitor_pcap_write(input1, input1_len);
273 		input1_len = input1_offset = 0;
274 		memset(input1, 0, sizeof(input1));
275 
276 		fsync(fd2_in);
277 	}
278 
279 done:
280 	return TRUE;
281 }
282 
fifo_handler2(GIOChannel * channel,GIOCondition cond,gpointer user_data)283 static gboolean fifo_handler2(GIOChannel *channel, GIOCondition cond,
284 							gpointer user_data)
285 {
286 	static bool starting = true;
287 	unsigned char buf[1];
288 	ssize_t result;
289 	int fd;
290 
291 	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
292 		DBG("Second pipe closed");
293 		return FALSE;
294 	}
295 
296 	memset(buf, 0, sizeof(buf));
297 	fd = g_io_channel_unix_get_fd(channel);
298 
299 	result = read(fd, buf, 1);
300 	if (result != 1) {
301 		DBG("Failed to read %lu", result);
302 		return FALSE;
303 	}
304 
305 	if (write(fd1_in, buf, 1) < 0) {
306 		DBG("Error, write failed to %s", pipe_1_in);
307 		return FALSE;
308 	}
309 #if EXTRA_DEBUG
310 	DBG("[%d/%d] starting %d fifo 2 read from %d and wrote %d bytes to %d",
311 	    input2_offset, input2_len, starting, fd, 1, fd1_in);
312 #endif
313 	if (starting && input2_len == 0 && input2_offset == 0 &&
314 	    buf[0] == 0 && input2_type == 0) {
315 		/* sync byte */
316 		goto done;
317 	} else if (starting && input2_len == 0 && input2_offset == 0
318 		   && buf[0] != 0) {
319 		starting = false;
320 	}
321 
322 #if EXTRA_DEBUG
323 	DBG("Pipe 2 byte 0x%x", buf[0]);
324 #endif
325 	if (input2_len == 0 && input2_offset == 0 &&
326 		buf[0] == DUMMY_RADIO_15_4_FRAME_TYPE) {
327 #if EXTRA_DEBUG
328 		DBG("Frame starting in pipe 2");
329 #endif
330 		input2_type = buf[0];
331 		goto done;
332 	}
333 
334 	if (input2_len == 0 && input2_offset == 0 &&
335 		input2_type == DUMMY_RADIO_15_4_FRAME_TYPE) {
336 		input2_len = buf[0];
337 #if EXTRA_DEBUG
338 		DBG("Expecting pipe 2 buf len %d\n", input2_len);
339 #endif
340 		goto done;
341 	}
342 
343 	if (input2_len) {
344 		input2[input2_offset++] = buf[0];
345 	}
346 
347 	if (input2_len && input2_len == input2_offset) {
348 		DBG("Received %d bytes in pipe 2", input2_len);
349 
350 		monitor_pcap_write(input2, input2_len);
351 		input2_len = input2_offset = 0;
352 		memset(input2, 0, sizeof(input2));
353 
354 		fsync(fd1_in);
355 	}
356 
357 done:
358 	return TRUE;
359 }
360 
setup_fifofd1(void)361 static int setup_fifofd1(void)
362 {
363 	GIOChannel *channel;
364 	guint source;
365 	int fd;
366 
367 	fd = open(pipe_1_out, O_RDONLY);
368 	if (fd < 0) {
369 		DBG("Failed to open fifo %s", pipe_1_out);
370 		return fd;
371 	}
372 
373 	DBG("Pipe 1 OUT fd %d", fd);
374 
375 	channel = g_io_channel_unix_new(fd);
376 
377 	g_io_channel_set_close_on_unref(channel, TRUE);
378 
379 	source = g_io_add_watch(channel,
380 				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
381 				fifo_handler1, NULL);
382 
383 	g_io_channel_unref(channel);
384 
385 	return source;
386 }
387 
setup_fifofd2(void)388 static int setup_fifofd2(void)
389 {
390 	GIOChannel *channel;
391 	guint source;
392 	int fd;
393 
394 	fd = open(pipe_2_out, O_RDONLY);
395 	if (fd < 0) {
396 		DBG("Failed to open fifo %s", pipe_2_out);
397 		return fd;
398 	}
399 
400 	DBG("Pipe 2 OUT fd %d", fd);
401 
402 	channel = g_io_channel_unix_new(fd);
403 
404 	g_io_channel_set_close_on_unref(channel, TRUE);
405 
406 	source = g_io_add_watch(channel,
407 				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
408 				fifo_handler2, NULL);
409 
410 	g_io_channel_unref(channel);
411 
412 	return source;
413 }
414 
main(int argc,char * argv[])415 int main(int argc, char *argv[])
416 {
417 	int fifo1 = -1, fifo2 = -1, ret;
418 	char *pipe1 = "/tmp/ip-15-4-1";
419 	char *pipe2 = "/tmp/ip-15-4-2";
420 
421 	if (argc < 2) {
422 		printf("Usage: %s <pcapfile> [<pipe_1> <pipe_2>]\n", argv[0]);
423 		printf("   e.g.: monitor_15_4 sample.pcap [/tmp/ip-15-4-1 /tmp/ip-15-4-2]\n");
424 		exit(-EINVAL);
425 	}
426 
427 	if (argc == 3)
428 		pipe1 = argv[2];
429 
430 	if (argc == 4) {
431 		pipe1 = argv[2];
432 		pipe2 = argv[3];
433 	}
434 
435 	main_loop = g_main_loop_new(NULL, FALSE);
436 
437 	pipe_1_in = g_strconcat(pipe1, PIPE_IN, NULL);
438 	pipe_1_out = g_strconcat(pipe1, PIPE_OUT, NULL);
439 	pipe_2_in = g_strconcat(pipe2, PIPE_IN, NULL);
440 	pipe_2_out = g_strconcat(pipe2, PIPE_OUT, NULL);
441 	path = g_strdup(argv[1]);
442 
443 	log_init("log", FALSE, argc > 4 ? TRUE : FALSE);
444 
445 	DBG("Pipe 1 IN %s OUT %s", pipe_1_in, pipe_1_out);
446 	DBG("Pipe 2 IN %s OUT %s", pipe_2_in, pipe_2_out);
447 
448 	if (!monitor_pcap_create(path)) {
449 		g_free(path);
450 		exit(-EINVAL);
451 	}
452 
453 	fifo1 = setup_fifofd1();
454 	if (fifo1 < 0) {
455 		ret = -EINVAL;
456 		goto exit;
457 	}
458 
459 	fifo2 = setup_fifofd2();
460 	if (fifo2 < 0) {
461 		ret = -EINVAL;
462 		goto exit;
463 	}
464 
465 	fd1_in = open(pipe_1_in, O_WRONLY);
466 	if (fd1_in < 0) {
467 		DBG("Failed to open fifo %s", pipe_1_in);
468 		ret = -EINVAL;
469 		goto exit;
470 	}
471 
472 	fd2_in = open(pipe_2_in, O_WRONLY);
473 	if (fd2_in < 0) {
474 		DBG("Failed to open fifo %s", pipe_2_in);
475 		ret = -EINVAL;
476 		goto exit;
477 	}
478 
479 	DBG("Pipe 1 IN %d, pipe 2 IN %d", fd1_in, fd2_in);
480 
481 	g_main_loop_run(main_loop);
482 	ret = 0;
483 exit:
484 	if (fifo1 >= 0)
485 		g_source_remove(fifo1);
486 
487 	if (fifo2 >= 0)
488 		g_source_remove(fifo2);
489 
490 	if (fd1_in >= 0)
491 		close(fd1_in);
492 
493 	if (fd2_in >= 0)
494 		close(fd2_in);
495 
496 	g_free(path);
497 	g_free(pipe_1_in);
498 	g_free(pipe_1_out);
499 	g_free(pipe_2_in);
500 	g_free(pipe_2_out);
501 	monitor_pcap_free();
502 	log_cleanup();
503 	g_main_loop_unref(main_loop);
504 
505 	exit(ret);
506 }
507