blob: 4cc8538003544cc8b197ec260dcdba096f4ba8e8 [file] [log] [blame]
Harald Weltedbb0f5a2016-10-18 23:51:54 +02001#include <stdio.h>
Harald Welte74d1e342016-10-19 00:06:22 +02002#include <time.h>
Harald Weltef4032322016-10-19 00:23:10 +02003#include <unistd.h>
Harald Weltec1b9cab2016-10-23 19:36:14 +02004#include <string.h>
Harald Welte74d1e342016-10-19 00:06:22 +02005
Harald Weltedbb0f5a2016-10-18 23:51:54 +02006#include <sys/time.h>
Harald Welte41a53002016-11-14 23:22:49 +01007#include <sys/types.h>
Harald Weltedbb0f5a2016-10-18 23:51:54 +02008
Harald Welte1df5cf42016-11-14 21:29:01 +01009#include <osmocom/core/bits.h>
Harald Weltedbb0f5a2016-10-18 23:51:54 +020010#include <osmocom/core/signal.h>
11#include <osmocom/core/logging.h>
Harald Welte41a53002016-11-14 23:22:49 +010012#include <osmocom/core/msgb.h>
Harald Weltedbb0f5a2016-10-18 23:51:54 +020013#include <osmocom/core/application.h>
Harald Welte525af182016-10-19 00:38:46 +020014#include <osmocom/abis/subchan_demux.h>
Harald Welte41a53002016-11-14 23:22:49 +010015#include <osmocom/abis/lapd_pcap.h>
Harald Weltedbb0f5a2016-10-18 23:51:54 +020016
17#include "storage.h"
18#include "recorder.h"
Harald Welte1df5cf42016-11-14 21:29:01 +010019#include "flip_bits.h"
20#include "hdlc.h"
Harald Weltedbb0f5a2016-10-18 23:51:54 +020021
Harald Welteb36921f2019-12-03 22:12:45 +010022#include "config.h"
23
Harald Weltedbb0f5a2016-10-18 23:51:54 +020024struct e1_recorder g_recorder;
25
Harald Weltef4032322016-10-19 00:23:10 +020026enum mode {
27 MODE_PRINT,
28 MODE_BIN,
Harald Weltec1b9cab2016-10-23 19:36:14 +020029 MODE_SC,
Harald Weltef4032322016-10-19 00:23:10 +020030};
31
Harald Weltec1b9cab2016-10-23 19:36:14 +020032#define MAX_TS 32
33#define CHUNK_BYTES 160
34
35/* Ericsson super-channel */
36struct sc_state {
37 uint8_t ts_data[MAX_TS][CHUNK_BYTES];
38 uint8_t num_ts;
Harald Welte1df5cf42016-11-14 21:29:01 +010039 uint32_t ts_mask;
40 struct hdlc_proc hdlc;
Harald Weltec1b9cab2016-10-23 19:36:14 +020041};
42
43static struct sc_state g_sc_state[2];
44
Harald Weltef4032322016-10-19 00:23:10 +020045static enum mode g_mode = MODE_PRINT;
46static int g_filter_line = -1;
47static int g_filter_slot = -1;
Harald Welte525af182016-10-19 00:38:46 +020048static int g_filter_subslot = -1;
49static struct osmo_e1cap_pkthdr *g_last_pkthdr;
Harald Welte41a53002016-11-14 23:22:49 +010050static int g_pcap_fd = -1;
51struct msgb *g_pcap_msg = NULL;
Harald Weltef4032322016-10-19 00:23:10 +020052
Harald Welte74d1e342016-10-19 00:06:22 +020053static char *timeval2str(struct timeval *tv)
54{
55 time_t nowtime;
56 struct tm *nowtm;
57 char tmbuf[64];
Harald Welte6df07292019-11-06 16:21:55 +010058 static char buf[64+20];
Harald Welte74d1e342016-10-19 00:06:22 +020059
60 nowtime = tv->tv_sec;
61 nowtm = localtime(&nowtime);
62 strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", nowtm);
63 snprintf(buf, sizeof buf, "%s.%06ld", tmbuf, tv->tv_usec);
64 return buf;
65}
66
Harald Welte6df07292019-11-06 16:21:55 +010067#if 0
Harald Weltec1b9cab2016-10-23 19:36:14 +020068static int all_bytes_are(unsigned char ch, const uint8_t *data, int len)
69{
70 int i;
71
72 for (i = 0; i < len; i++) {
73 if (data[i] != ch)
74 return 0;
75 }
76 return 1;
77}
Harald Welte6df07292019-11-06 16:21:55 +010078#endif
Harald Weltec1b9cab2016-10-23 19:36:14 +020079
Harald Welte41a53002016-11-14 23:22:49 +010080static void handle_hdlc_frame_content(const uint8_t *data, unsigned int len,
81 void *priv)
82{
Harald Welte3863bee2019-11-24 18:54:02 +010083 if (g_pcap_fd >= 0 && len >= 4) {
Harald Welte41a53002016-11-14 23:22:49 +010084 uint8_t *cur = msgb_put(g_pcap_msg, len-2);
85 memcpy(cur, data, len-2);
86 printf("==> %s\n", msgb_hexdump(g_pcap_msg));
87 OSMO_ASSERT(g_pcap_msg->len >= 2);
88 /* FIXME: timestamp! */
89 osmo_pcap_lapd_write(g_pcap_fd, OSMO_LAPD_PCAP_OUTPUT, g_pcap_msg);
90 msgb_reset(g_pcap_msg);
91 }
92}
93
Harald Weltec1b9cab2016-10-23 19:36:14 +020094static void handle_sc_out(struct sc_state *scs)
95{
Harald Welte1df5cf42016-11-14 21:29:01 +010096 unsigned int num_bytes = scs->num_ts * CHUNK_BYTES;
97 unsigned int num_bits = num_bytes * 8;
98 uint8_t out[32*CHUNK_BYTES];
99 ubit_t out_bits[32*CHUNK_BYTES*8];
Harald Weltec1b9cab2016-10-23 19:36:14 +0200100 int i, j, k = 0;
101
102 /* re-shuffle the data from columns to lines */
103 for (i = 0; i < CHUNK_BYTES; i++) {
Harald Welte1df5cf42016-11-14 21:29:01 +0100104 for (j = 0; j < scs->num_ts; j++)
Harald Weltec1b9cab2016-10-23 19:36:14 +0200105 out[k++] = scs->ts_data[j][i];
106 }
Harald Welte1df5cf42016-11-14 21:29:01 +0100107 //printf("num_bytes=%u %s\n", num_bytes, osmo_hexdump_nospc(out, num_bytes));
108 osmo_pbit2ubit_ext(out_bits, 0, out, 0, num_bits, 1);
109 //for (i = 0; i < num_bits; i++) fputc(out_bits[i] ? '1' : '.', stdout); fputc('\n', stdout);
110 process_raw_hdlc(&scs->hdlc, out_bits, num_bits);
Harald Weltec1b9cab2016-10-23 19:36:14 +0200111}
112
113static void handle_sc_in(struct osmo_e1cap_pkthdr *pkt, const uint8_t *data, unsigned int len)
114{
115 struct sc_state *scs;
116
117 if (pkt->line_nr >= ARRAY_SIZE(g_sc_state)) {
118 fprintf(stderr, "Line number out of range\n");
119 exit(1);
120 }
121
122 scs = &g_sc_state[pkt->line_nr];
123 if (pkt->ts_nr >= ARRAY_SIZE(scs->ts_data)) {
124 fprintf(stderr, "Timeslot number out of range\n");
125 exit(1);
126 }
127
128 if (len != sizeof(scs->ts_data[pkt->ts_nr])) {
129 fprintf(stderr, "Insufficient data\n");
130 exit(1);
131 }
132
Harald Welte1df5cf42016-11-14 21:29:01 +0100133 if (pkt->ts_nr == 1) {
134 scs->ts_mask = 0;
135 memset(scs->ts_data, 0, sizeof(scs->ts_data));
136 }
137
138 /* copy over the data */
139 memcpy(scs->ts_data[pkt->ts_nr-1], data, len);
140 /* note that we have valid data for the given timeslot */
141 scs->ts_mask |= (1 << (pkt->ts_nr-1));
142
143 /* make sure we know what's the maximum timeslot number */
144 if (pkt->ts_nr > scs->num_ts)
145 scs->num_ts = pkt->ts_nr;
146
147 /* check if we have data for all needed timeslots */
148 uint32_t ts_mask = (1 << scs->num_ts) -1;
149 //printf("num_ts=%u, ts_mask=0x%x, scs_ts_mask=0x%x\n", scs->num_ts, ts_mask, scs->ts_mask);
150 if (scs->ts_mask == ts_mask) {
Harald Weltec1b9cab2016-10-23 19:36:14 +0200151 handle_sc_out(scs);
Harald Welte1df5cf42016-11-14 21:29:01 +0100152 }
Harald Weltec1b9cab2016-10-23 19:36:14 +0200153}
154
155
Harald Welte130eeb32020-06-09 09:02:11 +0200156static void handle_data(struct osmo_e1cap_pkthdr *pkt, const uint8_t *idata, int len)
Harald Welte525af182016-10-19 00:38:46 +0200157{
Harald Welte130eeb32020-06-09 09:02:11 +0200158 uint8_t data[len];
Harald Welte6df07292019-11-06 16:21:55 +0100159 struct timeval tv;
160
Harald Welte130eeb32020-06-09 09:02:11 +0200161 flip_buf_bits(data, idata, len);
Harald Welte1df5cf42016-11-14 21:29:01 +0100162#if 0
163 /* filter out all-ff/all-fe/all-7f */
164 if (all_bytes_are(0xff, data, len) ||
165 all_bytes_are(0x7f, data, len) ||
166 all_bytes_are(0x7e, data, len) ||
167 all_bytes_are(0xe7, data, len) ||
168 all_bytes_are(0x3f, data, len) ||
169 all_bytes_are(0xf3, data, len) ||
170 all_bytes_are(0x9f, data, len) ||
171 all_bytes_are(0xf9, data, len) ||
172 all_bytes_are(0xcf, data, len) ||
173 all_bytes_are(0xfc, data, len) ||
174 all_bytes_are(0xfe, data, len))
175 return;
176#endif
177
Harald Welte525af182016-10-19 00:38:46 +0200178 switch (g_mode) {
179 case MODE_PRINT:
Harald Welte6df07292019-11-06 16:21:55 +0100180 tv.tv_sec = pkt->ts.tv_sec;
181 tv.tv_usec = pkt->ts.tv_usec;
Harald Welte525af182016-10-19 00:38:46 +0200182 printf("%s %02u/%02u %u (%u): %s\n",
Harald Welte6df07292019-11-06 16:21:55 +0100183 timeval2str(&tv),
Harald Welte525af182016-10-19 00:38:46 +0200184 pkt->line_nr, pkt->ts_nr, pkt->capture_mode,
185 pkt->len,
186 osmo_hexdump_nospc(data, len));
187 break;
188 case MODE_BIN:
189 write(1, data, len);
190 break;
Harald Weltec1b9cab2016-10-23 19:36:14 +0200191 case MODE_SC:
192 handle_sc_in(pkt, data, len);
193 break;
Harald Welte525af182016-10-19 00:38:46 +0200194 }
195}
196
Harald Welte130eeb32020-06-09 09:02:11 +0200197static int subch_demux_out_cb(struct subch_demux *dmx, int ch, const ubit_t *data,
Harald Welte525af182016-10-19 00:38:46 +0200198 int len, void *c)
199{
200 OSMO_ASSERT(ch == g_filter_subslot);
Harald Welte1df5cf42016-11-14 21:29:01 +0100201
Harald Welte525af182016-10-19 00:38:46 +0200202 handle_data(g_last_pkthdr, data, len);
203
204 return 0;
205}
206
Harald Welteb36921f2019-12-03 22:12:45 +0100207static void print_help(void)
208{
209 printf( " -h Print this message\n"
210 " -V Print version of the program\n"
211 " -l LINE_NR Filter on line number\n"
212 " -s SLOT_NR Filter on timeslot number\n"
213 " -b Raw binary output mode (for piping stdout)\n"
214 " -S Super-Channel mode\n"
215 " -u SUBSLOT_NR Filter on 16k sub-slot number\n"
216 " -p PCAP_FILE Write LAPD PCAP file\n"
217 );
218};
219
Harald Weltef4032322016-10-19 00:23:10 +0200220static int handle_options(int argc, char **argv)
221{
222 int opt;
223
Harald Welteb36921f2019-12-03 22:12:45 +0100224 while ((opt = getopt(argc, argv, "hVl:s:bSu:p:")) != -1) {
Harald Weltef4032322016-10-19 00:23:10 +0200225 switch (opt) {
Harald Welteb36921f2019-12-03 22:12:45 +0100226 case 'h':
227 print_help();
228 exit(0);
229 case 'V':
230 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
231 exit(0);
232 break;
Harald Welte525af182016-10-19 00:38:46 +0200233 case 'l': /* Filter on E1 Line Number */
Harald Weltef4032322016-10-19 00:23:10 +0200234 g_filter_line = atoi(optarg);
235 break;
Harald Welte525af182016-10-19 00:38:46 +0200236 case 's': /* Filter on E1 Slot Number */
Harald Weltef4032322016-10-19 00:23:10 +0200237 g_filter_slot = atoi(optarg);
238 break;
Harald Welte525af182016-10-19 00:38:46 +0200239 case 'b': /* Raw binary output mode (for piping) */
Harald Weltef4032322016-10-19 00:23:10 +0200240 g_mode = MODE_BIN;
241 break;
Harald Weltec1b9cab2016-10-23 19:36:14 +0200242 case 'S': /* Super Channel Mode */
243 g_mode = MODE_SC;
244 break;
Harald Welte525af182016-10-19 00:38:46 +0200245 case 'u': /* 16k Sub-channel demux + filter */
246 g_filter_subslot = atoi(optarg);
247 if (g_filter_subslot < 0 || g_filter_subslot > 3)
248 exit(2);
249 break;
Harald Welte41a53002016-11-14 23:22:49 +0100250 case 'p': /* PCAP writing */
251 g_pcap_fd = osmo_pcap_lapd_open(optarg, 0640);
252 if (g_pcap_fd < 0) {
253 perror("opening pcap file");
254 exit(1);
255 }
256 g_pcap_msg = msgb_alloc(1024, "pcap");
257 break;
Harald Weltef4032322016-10-19 00:23:10 +0200258 default:
259 fprintf(stderr, "Unknown option '%c'\n", opt);
260 exit(EXIT_FAILURE);
261 break;
262 }
263 }
264
265 return 0;
266}
267
Harald Welte41a53002016-11-14 23:22:49 +0100268static struct log_info_cat log_categories[] = {
269};
270
271static struct log_info log_info = {
272 .cat = log_categories,
273 .num_cat = ARRAY_SIZE(log_categories),
274};
275
Harald Weltedbb0f5a2016-10-18 23:51:54 +0200276int main(int argc, char **argv)
277{
278 struct osmo_e1cap_file *f;
279 struct osmo_e1cap_pkthdr *pkt;
Harald Weltef4032322016-10-19 00:23:10 +0200280 unsigned long num_pkt = 0;
Harald Welte525af182016-10-19 00:38:46 +0200281 struct subch_demux smux;
Harald Welte41a53002016-11-14 23:22:49 +0100282 int i;
Harald Weltedbb0f5a2016-10-18 23:51:54 +0200283
284 printf("sizeof(timeval) = %zu\n", sizeof(struct timeval));
285 printf("sizeof(osmo_e1cap_pkthdr) = %zu\n", sizeof(*pkt));
286
Harald Welte1df5cf42016-11-14 21:29:01 +0100287 memset(g_sc_state, 0, sizeof(g_sc_state));
Harald Welte41a53002016-11-14 23:22:49 +0100288 for (i = 0; i < ARRAY_SIZE(g_sc_state); i++)
289 g_sc_state[i].hdlc.out_cb = handle_hdlc_frame_content;
Harald Welte1df5cf42016-11-14 21:29:01 +0100290 init_flip_bits();
291
Harald Welte1b41abd2019-12-04 14:15:16 +0100292 osmo_init_logging2(NULL, &log_info);
Harald Welte41a53002016-11-14 23:22:49 +0100293
Harald Weltef4032322016-10-19 00:23:10 +0200294 handle_options(argc, argv);
Harald Weltedbb0f5a2016-10-18 23:51:54 +0200295
Harald Weltef4032322016-10-19 00:23:10 +0200296 if (optind >= argc) {
297 fprintf(stderr, "Missing input file name\n");
298 exit(2);
299 }
300
Harald Weltecbf23b62019-12-03 22:14:20 +0100301 if (argc > optind+1) {
302 fprintf(stderr, "Unsupported positional arguments on command line\n");
303 exit(2);
304 }
305
Harald Weltef4032322016-10-19 00:23:10 +0200306 f = osmo_e1cap_open(NULL, argv[optind++]);
307 if (!f) {
308 fprintf(stderr, "Unable to open input file\n");
Harald Weltedbb0f5a2016-10-18 23:51:54 +0200309 exit(1);
Harald Weltef4032322016-10-19 00:23:10 +0200310 }
Harald Weltedbb0f5a2016-10-18 23:51:54 +0200311
Harald Welte525af182016-10-19 00:38:46 +0200312 if (g_filter_subslot >= 0) {
313 smux.out_cb = subch_demux_out_cb;
314 subch_demux_init(&smux);
315 subch_demux_activate(&smux, g_filter_subslot);
316 }
317
Harald Welte1df5cf42016-11-14 21:29:01 +0100318// printf("hdlc=%s\n", osmo_hexdump(&g_sc_state[0].hdlc, sizeof(g_sc_state[0].hdlc)));
319
Harald Weltedbb0f5a2016-10-18 23:51:54 +0200320 while ((pkt = osmo_e1cap_read_next(f))) {
Harald Weltef4032322016-10-19 00:23:10 +0200321 num_pkt++;
Harald Welte525af182016-10-19 00:38:46 +0200322 g_last_pkthdr = pkt;
Harald Weltef4032322016-10-19 00:23:10 +0200323
324 if (g_filter_line >= 0 && pkt->line_nr != g_filter_line)
325 continue;
326 if (g_filter_slot >= 0 && pkt->ts_nr != g_filter_slot)
327 continue;
328
Harald Welte525af182016-10-19 00:38:46 +0200329 if (g_filter_subslot >= 0) {
330 subch_demux_in(&smux, pkt->data, pkt->len);
331 continue;
Harald Weltef4032322016-10-19 00:23:10 +0200332 }
Harald Welte525af182016-10-19 00:38:46 +0200333
334 handle_data(pkt, pkt->data, pkt->len);
335
Harald Weltedbb0f5a2016-10-18 23:51:54 +0200336 talloc_free(pkt);
337 }
Harald Weltef4032322016-10-19 00:23:10 +0200338
339 fprintf(stderr, "Processed a total of %lu packets\n", num_pkt);
Harald Weltedbb0f5a2016-10-18 23:51:54 +0200340}