2016-10-18 21:51:54 +00:00
|
|
|
#include <stdio.h>
|
2016-10-18 22:06:22 +00:00
|
|
|
#include <time.h>
|
2016-10-18 22:23:10 +00:00
|
|
|
#include <unistd.h>
|
2016-10-23 17:36:14 +00:00
|
|
|
#include <string.h>
|
2016-10-18 22:06:22 +00:00
|
|
|
|
2016-10-18 21:51:54 +00:00
|
|
|
#include <sys/time.h>
|
2016-11-14 22:22:49 +00:00
|
|
|
#include <sys/types.h>
|
2016-10-18 21:51:54 +00:00
|
|
|
|
2016-11-14 20:29:01 +00:00
|
|
|
#include <osmocom/core/bits.h>
|
2016-10-18 21:51:54 +00:00
|
|
|
#include <osmocom/core/signal.h>
|
|
|
|
#include <osmocom/core/logging.h>
|
2016-11-14 22:22:49 +00:00
|
|
|
#include <osmocom/core/msgb.h>
|
2016-10-18 21:51:54 +00:00
|
|
|
#include <osmocom/core/application.h>
|
2016-10-18 22:38:46 +00:00
|
|
|
#include <osmocom/abis/subchan_demux.h>
|
2016-11-14 22:22:49 +00:00
|
|
|
#include <osmocom/abis/lapd_pcap.h>
|
2016-10-18 21:51:54 +00:00
|
|
|
|
|
|
|
#include "storage.h"
|
|
|
|
#include "recorder.h"
|
2016-11-14 20:29:01 +00:00
|
|
|
#include "flip_bits.h"
|
|
|
|
#include "hdlc.h"
|
2016-10-18 21:51:54 +00:00
|
|
|
|
2019-12-03 21:12:45 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2016-10-18 21:51:54 +00:00
|
|
|
struct e1_recorder g_recorder;
|
|
|
|
|
2016-10-18 22:23:10 +00:00
|
|
|
enum mode {
|
|
|
|
MODE_PRINT,
|
|
|
|
MODE_BIN,
|
2016-10-23 17:36:14 +00:00
|
|
|
MODE_SC,
|
2016-10-18 22:23:10 +00:00
|
|
|
};
|
|
|
|
|
2016-10-23 17:36:14 +00:00
|
|
|
#define MAX_TS 32
|
|
|
|
#define CHUNK_BYTES 160
|
|
|
|
|
|
|
|
/* Ericsson super-channel */
|
|
|
|
struct sc_state {
|
|
|
|
uint8_t ts_data[MAX_TS][CHUNK_BYTES];
|
|
|
|
uint8_t num_ts;
|
2016-11-14 20:29:01 +00:00
|
|
|
uint32_t ts_mask;
|
|
|
|
struct hdlc_proc hdlc;
|
2016-10-23 17:36:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct sc_state g_sc_state[2];
|
|
|
|
|
2016-10-18 22:23:10 +00:00
|
|
|
static enum mode g_mode = MODE_PRINT;
|
|
|
|
static int g_filter_line = -1;
|
|
|
|
static int g_filter_slot = -1;
|
2016-10-18 22:38:46 +00:00
|
|
|
static int g_filter_subslot = -1;
|
|
|
|
static struct osmo_e1cap_pkthdr *g_last_pkthdr;
|
2016-11-14 22:22:49 +00:00
|
|
|
static int g_pcap_fd = -1;
|
|
|
|
struct msgb *g_pcap_msg = NULL;
|
2016-10-18 22:23:10 +00:00
|
|
|
|
2016-10-18 22:06:22 +00:00
|
|
|
static char *timeval2str(struct timeval *tv)
|
|
|
|
{
|
|
|
|
time_t nowtime;
|
|
|
|
struct tm *nowtm;
|
|
|
|
char tmbuf[64];
|
2019-11-06 15:21:55 +00:00
|
|
|
static char buf[64+20];
|
2016-10-18 22:06:22 +00:00
|
|
|
|
|
|
|
nowtime = tv->tv_sec;
|
|
|
|
nowtm = localtime(&nowtime);
|
|
|
|
strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", nowtm);
|
|
|
|
snprintf(buf, sizeof buf, "%s.%06ld", tmbuf, tv->tv_usec);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2019-11-06 15:21:55 +00:00
|
|
|
#if 0
|
2016-10-23 17:36:14 +00:00
|
|
|
static int all_bytes_are(unsigned char ch, const uint8_t *data, int len)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
if (data[i] != ch)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
2019-11-06 15:21:55 +00:00
|
|
|
#endif
|
2016-10-23 17:36:14 +00:00
|
|
|
|
2016-11-14 22:22:49 +00:00
|
|
|
static void handle_hdlc_frame_content(const uint8_t *data, unsigned int len,
|
|
|
|
void *priv)
|
|
|
|
{
|
2019-11-24 17:54:02 +00:00
|
|
|
if (g_pcap_fd >= 0 && len >= 4) {
|
2016-11-14 22:22:49 +00:00
|
|
|
uint8_t *cur = msgb_put(g_pcap_msg, len-2);
|
|
|
|
memcpy(cur, data, len-2);
|
|
|
|
printf("==> %s\n", msgb_hexdump(g_pcap_msg));
|
|
|
|
OSMO_ASSERT(g_pcap_msg->len >= 2);
|
|
|
|
/* FIXME: timestamp! */
|
|
|
|
osmo_pcap_lapd_write(g_pcap_fd, OSMO_LAPD_PCAP_OUTPUT, g_pcap_msg);
|
|
|
|
msgb_reset(g_pcap_msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-23 17:36:14 +00:00
|
|
|
static void handle_sc_out(struct sc_state *scs)
|
|
|
|
{
|
2016-11-14 20:29:01 +00:00
|
|
|
unsigned int num_bytes = scs->num_ts * CHUNK_BYTES;
|
|
|
|
unsigned int num_bits = num_bytes * 8;
|
|
|
|
uint8_t out[32*CHUNK_BYTES];
|
|
|
|
ubit_t out_bits[32*CHUNK_BYTES*8];
|
2016-10-23 17:36:14 +00:00
|
|
|
int i, j, k = 0;
|
|
|
|
|
|
|
|
/* re-shuffle the data from columns to lines */
|
|
|
|
for (i = 0; i < CHUNK_BYTES; i++) {
|
2016-11-14 20:29:01 +00:00
|
|
|
for (j = 0; j < scs->num_ts; j++)
|
2016-10-23 17:36:14 +00:00
|
|
|
out[k++] = scs->ts_data[j][i];
|
|
|
|
}
|
2016-11-14 20:29:01 +00:00
|
|
|
//printf("num_bytes=%u %s\n", num_bytes, osmo_hexdump_nospc(out, num_bytes));
|
|
|
|
osmo_pbit2ubit_ext(out_bits, 0, out, 0, num_bits, 1);
|
|
|
|
//for (i = 0; i < num_bits; i++) fputc(out_bits[i] ? '1' : '.', stdout); fputc('\n', stdout);
|
|
|
|
process_raw_hdlc(&scs->hdlc, out_bits, num_bits);
|
2016-10-23 17:36:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_sc_in(struct osmo_e1cap_pkthdr *pkt, const uint8_t *data, unsigned int len)
|
|
|
|
{
|
|
|
|
struct sc_state *scs;
|
|
|
|
|
|
|
|
if (pkt->line_nr >= ARRAY_SIZE(g_sc_state)) {
|
|
|
|
fprintf(stderr, "Line number out of range\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
scs = &g_sc_state[pkt->line_nr];
|
|
|
|
if (pkt->ts_nr >= ARRAY_SIZE(scs->ts_data)) {
|
|
|
|
fprintf(stderr, "Timeslot number out of range\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len != sizeof(scs->ts_data[pkt->ts_nr])) {
|
|
|
|
fprintf(stderr, "Insufficient data\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2016-11-14 20:29:01 +00:00
|
|
|
if (pkt->ts_nr == 1) {
|
|
|
|
scs->ts_mask = 0;
|
|
|
|
memset(scs->ts_data, 0, sizeof(scs->ts_data));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy over the data */
|
|
|
|
memcpy(scs->ts_data[pkt->ts_nr-1], data, len);
|
|
|
|
/* note that we have valid data for the given timeslot */
|
|
|
|
scs->ts_mask |= (1 << (pkt->ts_nr-1));
|
|
|
|
|
|
|
|
/* make sure we know what's the maximum timeslot number */
|
|
|
|
if (pkt->ts_nr > scs->num_ts)
|
|
|
|
scs->num_ts = pkt->ts_nr;
|
|
|
|
|
|
|
|
/* check if we have data for all needed timeslots */
|
|
|
|
uint32_t ts_mask = (1 << scs->num_ts) -1;
|
|
|
|
//printf("num_ts=%u, ts_mask=0x%x, scs_ts_mask=0x%x\n", scs->num_ts, ts_mask, scs->ts_mask);
|
|
|
|
if (scs->ts_mask == ts_mask) {
|
2016-10-23 17:36:14 +00:00
|
|
|
handle_sc_out(scs);
|
2016-11-14 20:29:01 +00:00
|
|
|
}
|
2016-10-23 17:36:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-06-09 07:02:11 +00:00
|
|
|
static void handle_data(struct osmo_e1cap_pkthdr *pkt, const uint8_t *idata, int len)
|
2016-10-18 22:38:46 +00:00
|
|
|
{
|
2020-06-09 07:02:11 +00:00
|
|
|
uint8_t data[len];
|
2019-11-06 15:21:55 +00:00
|
|
|
struct timeval tv;
|
|
|
|
|
2020-06-09 07:02:11 +00:00
|
|
|
flip_buf_bits(data, idata, len);
|
2016-11-14 20:29:01 +00:00
|
|
|
#if 0
|
|
|
|
/* filter out all-ff/all-fe/all-7f */
|
|
|
|
if (all_bytes_are(0xff, data, len) ||
|
|
|
|
all_bytes_are(0x7f, data, len) ||
|
|
|
|
all_bytes_are(0x7e, data, len) ||
|
|
|
|
all_bytes_are(0xe7, data, len) ||
|
|
|
|
all_bytes_are(0x3f, data, len) ||
|
|
|
|
all_bytes_are(0xf3, data, len) ||
|
|
|
|
all_bytes_are(0x9f, data, len) ||
|
|
|
|
all_bytes_are(0xf9, data, len) ||
|
|
|
|
all_bytes_are(0xcf, data, len) ||
|
|
|
|
all_bytes_are(0xfc, data, len) ||
|
|
|
|
all_bytes_are(0xfe, data, len))
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
2016-10-18 22:38:46 +00:00
|
|
|
switch (g_mode) {
|
|
|
|
case MODE_PRINT:
|
2019-11-06 15:21:55 +00:00
|
|
|
tv.tv_sec = pkt->ts.tv_sec;
|
|
|
|
tv.tv_usec = pkt->ts.tv_usec;
|
2016-10-18 22:38:46 +00:00
|
|
|
printf("%s %02u/%02u %u (%u): %s\n",
|
2019-11-06 15:21:55 +00:00
|
|
|
timeval2str(&tv),
|
2016-10-18 22:38:46 +00:00
|
|
|
pkt->line_nr, pkt->ts_nr, pkt->capture_mode,
|
|
|
|
pkt->len,
|
|
|
|
osmo_hexdump_nospc(data, len));
|
|
|
|
break;
|
|
|
|
case MODE_BIN:
|
|
|
|
write(1, data, len);
|
|
|
|
break;
|
2016-10-23 17:36:14 +00:00
|
|
|
case MODE_SC:
|
|
|
|
handle_sc_in(pkt, data, len);
|
|
|
|
break;
|
2016-10-18 22:38:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 07:02:11 +00:00
|
|
|
static int subch_demux_out_cb(struct subch_demux *dmx, int ch, const ubit_t *data,
|
2016-10-18 22:38:46 +00:00
|
|
|
int len, void *c)
|
|
|
|
{
|
|
|
|
OSMO_ASSERT(ch == g_filter_subslot);
|
2016-11-14 20:29:01 +00:00
|
|
|
|
2016-10-18 22:38:46 +00:00
|
|
|
handle_data(g_last_pkthdr, data, len);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-03 21:12:45 +00:00
|
|
|
static void print_help(void)
|
|
|
|
{
|
|
|
|
printf( " -h Print this message\n"
|
|
|
|
" -V Print version of the program\n"
|
|
|
|
" -l LINE_NR Filter on line number\n"
|
|
|
|
" -s SLOT_NR Filter on timeslot number\n"
|
|
|
|
" -b Raw binary output mode (for piping stdout)\n"
|
|
|
|
" -S Super-Channel mode\n"
|
|
|
|
" -u SUBSLOT_NR Filter on 16k sub-slot number\n"
|
|
|
|
" -p PCAP_FILE Write LAPD PCAP file\n"
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2016-10-18 22:23:10 +00:00
|
|
|
static int handle_options(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int opt;
|
|
|
|
|
2019-12-03 21:12:45 +00:00
|
|
|
while ((opt = getopt(argc, argv, "hVl:s:bSu:p:")) != -1) {
|
2016-10-18 22:23:10 +00:00
|
|
|
switch (opt) {
|
2019-12-03 21:12:45 +00:00
|
|
|
case 'h':
|
|
|
|
print_help();
|
|
|
|
exit(0);
|
|
|
|
case 'V':
|
|
|
|
printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
|
|
|
|
exit(0);
|
|
|
|
break;
|
2016-10-18 22:38:46 +00:00
|
|
|
case 'l': /* Filter on E1 Line Number */
|
2016-10-18 22:23:10 +00:00
|
|
|
g_filter_line = atoi(optarg);
|
|
|
|
break;
|
2016-10-18 22:38:46 +00:00
|
|
|
case 's': /* Filter on E1 Slot Number */
|
2016-10-18 22:23:10 +00:00
|
|
|
g_filter_slot = atoi(optarg);
|
|
|
|
break;
|
2016-10-18 22:38:46 +00:00
|
|
|
case 'b': /* Raw binary output mode (for piping) */
|
2016-10-18 22:23:10 +00:00
|
|
|
g_mode = MODE_BIN;
|
|
|
|
break;
|
2016-10-23 17:36:14 +00:00
|
|
|
case 'S': /* Super Channel Mode */
|
|
|
|
g_mode = MODE_SC;
|
|
|
|
break;
|
2016-10-18 22:38:46 +00:00
|
|
|
case 'u': /* 16k Sub-channel demux + filter */
|
|
|
|
g_filter_subslot = atoi(optarg);
|
|
|
|
if (g_filter_subslot < 0 || g_filter_subslot > 3)
|
|
|
|
exit(2);
|
|
|
|
break;
|
2016-11-14 22:22:49 +00:00
|
|
|
case 'p': /* PCAP writing */
|
|
|
|
g_pcap_fd = osmo_pcap_lapd_open(optarg, 0640);
|
|
|
|
if (g_pcap_fd < 0) {
|
|
|
|
perror("opening pcap file");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
g_pcap_msg = msgb_alloc(1024, "pcap");
|
|
|
|
break;
|
2016-10-18 22:23:10 +00:00
|
|
|
default:
|
|
|
|
fprintf(stderr, "Unknown option '%c'\n", opt);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-14 22:22:49 +00:00
|
|
|
static struct log_info_cat log_categories[] = {
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct log_info log_info = {
|
|
|
|
.cat = log_categories,
|
|
|
|
.num_cat = ARRAY_SIZE(log_categories),
|
|
|
|
};
|
|
|
|
|
2016-10-18 21:51:54 +00:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct osmo_e1cap_file *f;
|
|
|
|
struct osmo_e1cap_pkthdr *pkt;
|
2016-10-18 22:23:10 +00:00
|
|
|
unsigned long num_pkt = 0;
|
2016-10-18 22:38:46 +00:00
|
|
|
struct subch_demux smux;
|
2016-11-14 22:22:49 +00:00
|
|
|
int i;
|
2016-10-18 21:51:54 +00:00
|
|
|
|
|
|
|
printf("sizeof(timeval) = %zu\n", sizeof(struct timeval));
|
|
|
|
printf("sizeof(osmo_e1cap_pkthdr) = %zu\n", sizeof(*pkt));
|
|
|
|
|
2016-11-14 20:29:01 +00:00
|
|
|
memset(g_sc_state, 0, sizeof(g_sc_state));
|
2016-11-14 22:22:49 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(g_sc_state); i++)
|
|
|
|
g_sc_state[i].hdlc.out_cb = handle_hdlc_frame_content;
|
2016-11-14 20:29:01 +00:00
|
|
|
init_flip_bits();
|
|
|
|
|
2019-12-04 13:15:16 +00:00
|
|
|
osmo_init_logging2(NULL, &log_info);
|
2016-11-14 22:22:49 +00:00
|
|
|
|
2016-10-18 22:23:10 +00:00
|
|
|
handle_options(argc, argv);
|
|
|
|
|
|
|
|
if (optind >= argc) {
|
|
|
|
fprintf(stderr, "Missing input file name\n");
|
2016-10-18 21:51:54 +00:00
|
|
|
exit(2);
|
2016-10-18 22:23:10 +00:00
|
|
|
}
|
2016-10-18 21:51:54 +00:00
|
|
|
|
2019-12-03 21:14:20 +00:00
|
|
|
if (argc > optind+1) {
|
|
|
|
fprintf(stderr, "Unsupported positional arguments on command line\n");
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
2016-10-18 22:23:10 +00:00
|
|
|
f = osmo_e1cap_open(NULL, argv[optind++]);
|
|
|
|
if (!f) {
|
|
|
|
fprintf(stderr, "Unable to open input file\n");
|
2016-10-18 21:51:54 +00:00
|
|
|
exit(1);
|
2016-10-18 22:23:10 +00:00
|
|
|
}
|
2016-10-18 21:51:54 +00:00
|
|
|
|
2016-10-18 22:38:46 +00:00
|
|
|
if (g_filter_subslot >= 0) {
|
|
|
|
smux.out_cb = subch_demux_out_cb;
|
|
|
|
subch_demux_init(&smux);
|
|
|
|
subch_demux_activate(&smux, g_filter_subslot);
|
|
|
|
}
|
|
|
|
|
2016-11-14 20:29:01 +00:00
|
|
|
// printf("hdlc=%s\n", osmo_hexdump(&g_sc_state[0].hdlc, sizeof(g_sc_state[0].hdlc)));
|
|
|
|
|
2016-10-18 21:51:54 +00:00
|
|
|
while ((pkt = osmo_e1cap_read_next(f))) {
|
2016-10-18 22:23:10 +00:00
|
|
|
num_pkt++;
|
2016-10-18 22:38:46 +00:00
|
|
|
g_last_pkthdr = pkt;
|
2016-10-18 22:23:10 +00:00
|
|
|
|
|
|
|
if (g_filter_line >= 0 && pkt->line_nr != g_filter_line)
|
|
|
|
continue;
|
|
|
|
if (g_filter_slot >= 0 && pkt->ts_nr != g_filter_slot)
|
|
|
|
continue;
|
|
|
|
|
2016-10-18 22:38:46 +00:00
|
|
|
if (g_filter_subslot >= 0) {
|
|
|
|
subch_demux_in(&smux, pkt->data, pkt->len);
|
|
|
|
continue;
|
2016-10-18 22:23:10 +00:00
|
|
|
}
|
2016-10-18 22:38:46 +00:00
|
|
|
|
|
|
|
handle_data(pkt, pkt->data, pkt->len);
|
|
|
|
|
2016-10-18 21:51:54 +00:00
|
|
|
talloc_free(pkt);
|
|
|
|
}
|
2016-10-18 22:23:10 +00:00
|
|
|
|
|
|
|
fprintf(stderr, "Processed a total of %lu packets\n", num_pkt);
|
2016-10-18 21:51:54 +00:00
|
|
|
}
|