Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: matthiasl/Corelatus-GTH-example-code
base: 67481b2678
...
head fork: matthiasl/Corelatus-GTH-example-code
compare: a40b63c513
Checking mergeability… Don't worry, you can still create the pull request.
  • 4 commits
  • 19 files changed
  • 0 commit comments
  • 1 contributor
Commits on Dec 12, 2013
@matthiasl Improve usage examples a7c102f
@matthiasl Teach C sample code to display version information e1d4804
Commits on Dec 15, 2013
@matthiasl Teach save_to_pcap to write PCap-NG files
The PCap-NG file format is used by default by Wireshark as of
Wireshark 1.8.0 (June 2012). The main advantages of PCap-NG for
capturing via Corelatus hardware are:

   - Each packet has information about which interface it came from.

   - The capture file can include information about where the capture
     came from. This allows us to add the address of the capturing
     hardware.
f987124
Commits on Dec 16, 2013
@matthiasl Add the -c switch to allow save_to_pcap to write to classic PCap a40b63c
View
1  c/.gitignore
@@ -15,3 +15,4 @@ record
save_to_pcap
unmap
wide_recorder
+version.c
View
20 c/Makefile
@@ -25,6 +25,7 @@ headers=gth_apilib.h gth_client_xml_parse.h
# For windows, we need a .exe suffix on all executables
win_targets=$(addsuffix .exe, $(target_basenames))
common_c_files=gth_apilib.c gth_client_xml_parse.c
+example_c_files=$(addsuffix .c, $(target_basenames))
# present on newer debian systems (2012)
CC_WIN32_64bit=i686-w64-mingw32-gcc
@@ -36,25 +37,34 @@ LIBS_WIN32=-lws2_32
CC=gcc
+repo_head=$(shell git describe --abbrev=8 --dirty --always)
+
unix: $(target_basenames)
win32: $(win_targets)
clean:
- rm -f $(win_targets) $(target_basenames) *~ *.zip README.dos
+ rm -f $(win_targets) $(target_basenames) *~ *.zip README.dos version.h
-ship:
+ship: version.c
make clean
make
make win32
sed -e 's/$$/\r/g' < README.unix > README.txt
zip gth_c_examples *.c *.h NMakefile Makefile audio/* README.* *.exe
-target_basenames win_targets: $(headers) $(common_c_files)
+version.c: $(headers)
+ @echo "#include \"gth_apilib.h\"" > $@
+ @echo "const char git_head[] = \""$(repo_head)"\";" >> $@
+ @echo "const char build_hostname[] = \""$(shell hostname)"\";" >> $@
+
+%: %.c $(headers)
-%: %.c $(common_c_files)
+%: %.c $(common_c_files) version.c
$(CC) -Wall -g -o $@ $^ $(LIBS)
-%.exe: %.c $(common_c_files)
+%.exe: %.c $(headers)
+
+%.exe: %.c $(common_c_files) version.c
$(CC_WIN32) -Wall -o $@ $^ $(LIBS_WIN32)
View
8 c/connect_timeslots.c
@@ -51,7 +51,10 @@
static void usage()
{
- fprintf(stderr, "connect_timeslots [-v] <GTH-IP> <src span> <src ts> <dst span> <dest ts> ...\n"
+ fprintf(stderr,
+ "connect_timeslots git_head: %s build_hostname: %s\n\n"
+
+ "connect_timeslots [-v] <GTH-IP> <src span> <src ts> <dst span> <dest ts> ...\n"
"Connect the given timeslot a GTH.\n\n"
"Multiple sources and sinks can be given. You may want\n"
"to use 'query_set' to set up L1 before starting.\n\n"
@@ -66,7 +69,8 @@ static void usage()
"Examples:\n"
"./connect_timeslots 172.16.1.10 1A 16 2A 16\n"
"./connect_timeslots 172.16.1.10 1A 1 2A 13 1A 2 3A 1 2A 13 1A 1\n"
- "./connect_timeslots 172.16.1.10 1A 16 2A 16 2A 16 1A 16\n");
+ "./connect_timeslots 172.16.1.10 1A 16 2A 16 2A 16 1A 16\n",
+ git_head, build_hostname);
exit(-1);
}
View
6 c/disable.c
@@ -50,6 +50,8 @@
static void usage()
{
fprintf(stderr,
+ "disable git_head: %s build_hostname: %s\n\n"
+
"disable [-v] <GTH-IP> <resource>\n\n"
"Disable an SDH/SONET or E1/T1 interface.\n\n"
@@ -59,8 +61,8 @@ static void usage()
"Examples:\n"
"./disable 172.16.1.10 sdh1\n"
- "./disable 172.16.1.10 pcm1A\n\n"
- );
+ "./disable 172.16.1.10 pcm1A\n\n",
+ git_head, build_hostname);
exit(-1);
}
View
5 c/duplex_lapd.c
@@ -88,13 +88,16 @@ typedef struct {
void usage() {
fprintf(stderr,
+ "duplex_lapd git_head: %s build_hostname: %s\n\n"
+
"duplex_lapd [-v] <GTH-IP> <span> <timeslot> [<end>]\n\n"
"Enable ISDN LAPD on the specified timeslot.\n\n"
"-v: print the API commands and responses (verbose)\n"
"<GTH-IP> is the GTH's IP address or hostname\n"
"<span> is the E1/T1 interface, e.g. '1A'\n"
"<timeslot> is the timeslot, 1--31\n"
- "<end> is either 'user' (default) or 'network'\n");
+ "<end> is either 'user' (default) or 'network'\n",
+ git_head, build_hostname);
fprintf(stderr, "Typical use:\n");
fprintf(stderr, "./duplex_lapd 172.16.1.10 1A 16\n");
View
5 c/enable.c
@@ -50,6 +50,8 @@
static void usage()
{
fprintf(stderr,
+ "enable git_head: %s build_hostname: %s\n\n"
+
"enable [-v] <GTH-IP> <resource> [<attribute> <value>]\n\n"
"Enable an SDH/SONET or E1/T1 interface.\n\n"
"Multiple <attribute> <value> pairs may be given.\n\n"
@@ -66,7 +68,8 @@ static void usage()
"./enable 172.16.1.10 sdh1 SONET true OC 3 VT 2\n"
"./enable 172.16.1.10 pcm1A\n"
"./enable 172.16.1.10 pcm2A framing multiframe\n"
- "./enable 172.16.1.10 pcm2A framing multiframe monitoring true\n"
+ "./enable 172.16.1.10 pcm2A framing multiframe monitoring true\n",
+ git_head, build_hostname
);
exit(-1);
View
4 c/gth_apilib.h
@@ -90,6 +90,10 @@ typedef struct {
GTH_tone_handler *tone_handler;
} GTH_api;
+// For embedding the version into the object file
+extern const char git_head[];
+extern const char build_hostname[];
+
// Close an API connection to the GTH, cleanly.
//
// Return: 0 on success.
View
5 c/install_release.c
@@ -45,6 +45,8 @@
static void usage()
{
fprintf(stderr,
+ "install_release git_head: %s build_hostname: %s\n\n"
+
"install_release [-f] [-v] <GTH-IP> <filename>\n\n"
"installs a software image on a GTH\n\n"
@@ -55,7 +57,8 @@ static void usage()
"<filename> is the firmware image from www.corelatus.com\n\n"
"Example: \n"
- " ./install_release 172.16.1.10 gth2_system_33c.gth\n\n");
+ " ./install_release 172.16.1.10 gth2_system_33c.gth\n\n",
+ git_head, build_hostname);
exit(-1);
}
View
5 c/install_start_script.c
@@ -42,6 +42,8 @@
static void usage()
{
fprintf(stderr,
+ "install_start_script git_head: %s build_hostname: %s\n\n"
+
"install_start_script <GTH-IP> <filename>\n\n"
"Installs a start script on a GTH\n\n"
@@ -50,7 +52,8 @@ static void usage()
"<filename> is a file containing an XML start script\n\n"
"Typical invocation: \n"
- " ./install_start_script 172.16.1.10 /tmp/start.xml\n\n");
+ " ./install_start_script 172.16.1.10 /tmp/start.xml\n\n",
+ git_head, build_hostname);
exit(-1);
}
View
6 c/map.c
@@ -52,6 +52,8 @@
static void usage()
{
fprintf(stderr,
+ "map git_head: %s build_hostname: %s\n\n"
+
"map [-v] <GTH-IP> <sdh-resource>\n\n"
"Map an E1/T1 link carried on SDH/SONET. Print the name of the\n"
"E1/T1 resource created by the mapping.\n\n"
@@ -61,8 +63,8 @@ static void usage()
"<resource> is an SDH/SONET LOP resource\n"
"Example:\n"
- "./map 172.16.1.10 sdh1:hop1_1:lop1_5_3\n"
- );
+ "./map 172.16.1.10 sdh1:hop1_1:lop1_5_3\n",
+ git_head, build_hostname);
exit(-1);
}
View
5 c/monitor_cas.c
@@ -57,6 +57,8 @@
static void usage()
{
fprintf(stderr,
+ "monitor_cas git_head: %s build_hostname: %s\n\n"
+
"monitor_cas [-v] <GTH-IP>\n\n"
"Monitor an E1, pcm2A, for CAS signalling, print the signalling\n\n"
@@ -64,7 +66,8 @@ static void usage()
"<GTH-IP> is the GTH's IP address or hostname\n\n"
"Typical use:\n"
- "monitor_cas 172.16.1.10\n");
+ "monitor_cas 172.16.1.10\n",
+ git_head, build_hostname);
exit(-1);
}
View
5 c/monitor_dtmf.c
@@ -61,6 +61,8 @@
static void usage()
{
fprintf(stderr,
+ "monitor_dtmf git_head: %s build_hostname: %s\n\n"
+
"monitor_dtmf [-v] <GTH-IP> <span> <timeslot>.\n\n"
"Set up DTMF monitoring on a GTH and print all received tones\n\n"
@@ -70,7 +72,8 @@ static void usage()
"<timeslot> is the timeslot, 1--31\n\n"
"Typical use:\n"
- "monitor_dtmf 172.16.1.10 1A 23\n");
+ "monitor_dtmf 172.16.1.10 1A 23\n",
+ git_head, build_hostname);
exit(-1);
}
View
5 c/playback_file.c
@@ -53,13 +53,16 @@
static void usage()
{
fprintf(stderr,
+ "playback_file git_head: %s build_hostname: %s\n\n"
+
"playback_file [-v] <GTH-IP> <span> <timeslot> <filename>\n\n"
"Play the contents of a file on a timeslot.\n"
"\n-v: print the API commands and responses (verbose)"
"\n<GTH-IP> is the GTH's IP address or hostname"
"\n<span> is the name of a span, e.g. '1A'"
"\n<ts> is a timeslot number, from 1 to 31"
- "\n<filename> is the file to read data from");
+ "\n<filename> is the file to read data from",
+ git_head, build_hostname);
fprintf(stderr, "Typical use:\n");
fprintf(stderr, "./playback_file 172.16.1.10 1A 1 audio/mfc_fwd_4\n");
View
5 c/query_set.c
@@ -50,6 +50,8 @@
static void usage()
{
fprintf(stderr,
+ "query_set git_head: %s build_hostname: %s\n\n"
+
"query_set [-v] <GTH-IP> <resource> [<attribute> [<value>]]\n\n"
"Query or set resource parameters on a GTH.\n\n"
"If no <value> is given, just query the GTH.\n"
@@ -68,7 +70,8 @@ static void usage()
"./query_set 172.16.1.10 pcm1A\n"
"./query_set 172.16.1.10 pcm1A code_violation\n"
"./query_set 172.16.1.10 pcm1A status enabled\n"
- "./query_set 172.16.1.10 pcm1A status enabled framing multiframe\n");
+ "./query_set 172.16.1.10 pcm1A status enabled framing multiframe\n",
+ git_head, build_hostname);
exit(-1);
}
View
5 c/record.c
@@ -57,6 +57,8 @@
static void usage()
{
fprintf(stderr,
+ "record git_head: %s build_hostname: %s\n\n"
+
"record [-v] [-T] <GTH-IP> <span> <timeslot> <filename>\n\n"
"Save bit-exact data from a timeslot to a file\n\n"
"-v: print the API commands and responses (verbose)\n"
@@ -64,7 +66,8 @@ static void usage()
"<GTH-IP> is the GTH's IP address or hostname\n"
"<span> is the E1/T1 interface, e.g. '1A'\n"
"<timeslot> is the timeslot, 1--31\n"
- "<filename> is the filename to save to\n\n");
+ "<filename> is the filename to save to\n\n",
+ git_head, build_hostname);
fprintf(stderr, "Typical use:\n");
fprintf(stderr, "./record 172.16.1.10 1A 1 my_capture.wav\n\n");
View
653 c/save_to_pcap.c
@@ -1,15 +1,13 @@
//----------------------------------------------------------------------
// An example program. Shows how to capture SS7 data off the wire (an E1/T1)
-// with a GTH and save it to a libpcap file (or stdout) for further
-// analysis with e.g. wireshark or tshark.
+// with a GTH and save it to a file (or stdout) in either PCap-NG or
+// classic PCap format for further analysis with e.g. wireshark or tshark.
//
-// The libpcap file format is documented here:
+// References:
//
-// http://wiki.wireshark.org/Development/LibpcapFileFormat
-//
-// The GTH API is documented here:
-//
-// http://www.corelatus.com/gth/api/
+// PCap-NG doc: http://www.winpcap.org/ntar/draft/PCAP-DumpFileFormat.html
+// classic PCap: http://wiki.wireshark.org/Development/LibpcapFileFormat
+// GTH API: http://www.corelatus.com/gth/api/
//
// Limitations:
//
@@ -63,25 +61,69 @@
#include "gth_win32_compat.h"
#include "gth_apilib.h"
-// PCAP file format structures.
+// Types used for fixed-length packets. These typedefs are checked at runtime.
+typedef unsigned int u32;
+typedef unsigned short u16;
+
+//--------------------------------------------------
+// PCap classic file format structures.
+#pragma pack(1)
+typedef struct {
+ u32 magic;
+ u16 major_version;
+ u16 minor_version;
+ u32 GMT_to_localtime;
+ u32 sigfigs;
+ u32 snaplen;
+ u32 network;
+} PACK_SUFFIX PCap_classic_global_header;
+
#pragma pack(1)
typedef struct {
- unsigned int magic;
- unsigned short major_version;
- unsigned short minor_version;
- unsigned int GMT_to_localtime;
- unsigned int sigfigs;
- unsigned int snaplen;
- unsigned int network;
-} PACK_SUFFIX PCAP_global_header;
+ u32 ts_sec;
+ u32 ts_us;
+ u32 incl_len;
+ u32 orig_len;
+} PACK_SUFFIX PCap_classic_packet_header;
+
+//--------------------------------------------------
+// Pcap-NG file format structures
+
+#pragma pack(1)
+typedef struct {
+ u16 code;
+ u16 length;
+} PACK_SUFFIX PCap_NG_option;
+
#pragma pack(1)
typedef struct {
- unsigned int ts_sec;
- unsigned int ts_us;
- unsigned int incl_len;
- unsigned int orig_len;
-} PACK_SUFFIX PCAP_packet_header;
+ u32 type;
+ u32 block_total_length;
+ u32 byte_order_magic;
+ u16 major_version;
+ u16 minor_version;
+ unsigned long long section_length;
+} PACK_SUFFIX PCap_NG_shb;
+
+typedef struct {
+ u32 type;
+ u32 block_total_length;
+ u16 link_type;
+ u16 reserved;
+ u32 snaplen;
+} PACK_SUFFIX PCap_NG_idb;
+
+#pragma pack(1)
+typedef struct {
+ u32 type;
+ u32 block_total_length;
+ u32 interface_id;
+ u32 timestamp_hi;
+ u32 timestamp_lo;
+ u32 captured_len;
+ u32 packet_len;
+} PACK_SUFFIX PCap_NG_epb;
// GTH socket structure. An SS7 MTP-2 signal unit can't be more than
// 279 octets long according to Q.703.
@@ -99,22 +141,37 @@ typedef struct {
#define MAX_MTP2_CHANNELS 72
typedef struct {
- unsigned short tag;
- unsigned short flags;
- unsigned short timestamp_hi;
- unsigned int timestamp_lo;
+ u16 tag;
+ u16 flags;
+ u16 timestamp_hi;
+ u32 timestamp_lo;
char payload[MAX_SIGNAL_UNIT];
} GTH_mtp2;
-void usage() {
+enum PCap_format { PCAP_CLASSIC, PCAP_NG };
+
+// Link types, defined in PCap-NG spec appendix C
+#define LINK_TYPE_MTP2 140
+
+typedef struct {
+ char *pcm;
+ int timeslot;
+} Channel_t;
+
+//----------------------------------------------------------------------
+void
+usage() {
fprintf(stderr,
+ "save_to_pcap git_head: %s build_hostname: %s\n\n"
+
"save_to_pcap <options> <GTH-IP> <channels> [<channels>...] <filename>"
"\n\nSave decoded MTP-2 signal units to a file in libpcap format, "
"\nsuitable for examining with wireshark, tshark or other network"
"\nanalyser software.\n"
- "\n<options>: [-m] [-v] [-n <count>]"
+ "\n<options>: [-c] [-m] [-v] [-n <count>]"
"\n-m: tells the GTH that you are using a -20dB monitor point"
"\n-n <count>: rotate the output file after <count> packets, 0 means never"
+ "\n-c: save in the classic Pcap format (default is the newer Pcap-NG)"
"\n-v: print API commands and responses (verbose)"
"\n<GTH-IP> is the GTH's IP address or hostname"
"\n<channels> is a list of spans and timeslots:"
@@ -123,22 +180,23 @@ void usage() {
"\n e.g. 1A 1 2A 2 3A 3 4 monitors timeslot 1 on 1A, 2 on 2A and 3 and 4 on 3A."
"\n<span> is the name of a span, e.g. '1A'"
"\n<timeslot> is a timeslot number, from 1 to 31"
- "\n<filename> can be -, which means standard output.\n\n");
+ "\n<filename> can be -, which means standard output.\n\n",
+ git_head, build_hostname);
fprintf(stderr,
"Examples (on hardware with electrical E1/T1 ports):\n"
- "./save_to_pcap 172.16.1.10 1A 2A 16 isup_capture.pcap\n"
- "./save_to_pcap 172.16.1.10 1A 2A 3A 4A 1 2 3 4 isup_capture.pcap\n"
- "./save_to_pcap 172.16.1.10 1A 2A 16 1B 5 6 7 8 capture.pcap\n"
- "./save_to_pcap -m 172.16.1.10 1A 2A 16 isup_capture.pcap\n"
- "./save_to_pcap -m -n 1000 172.16.1.10 1A 2A 16 isup_capture.pcap\n"
+ "./save_to_pcap 172.16.1.10 1A 2A 16 isup_capture.pcapng\n"
+ "./save_to_pcap 172.16.1.10 1A 2A 3A 4A 1 2 3 4 isup_capture.pcapng\n"
+ "./save_to_pcap 172.16.1.10 1A 2A 16 1B 5 6 7 8 capture.pcapng\n"
+ "./save_to_pcap -m 172.16.1.10 1A 2A 16 isup_capture.pcapng\n"
+ "./save_to_pcap -m -n 1000 172.16.1.10 1A 2A 16 isup_capture.pcapng\n"
"./save_to_pcap 172.16.1.10 1A 2A 16 - | tshark -V -i - \n"
"./save_to_pcap 172.16.1.10 1A 2A 16 - | wireshark -k -i - \n"
"./save_to_pcap 172.16.1.10 1A 2A 16 \\\\.\\pipe\\isup_capture.1\n");
fprintf(stderr,
"\nExamples (on SDH/SONET hardware, usually optical):\n"
- "./save_to_pcap 172.16.1.10 pcm55 16 isup_capture.pcap\n\n");
+ "./save_to_pcap 172.16.1.10 pcm55 16 isup_capture.pcapng\n\n");
exit(-1);
}
@@ -151,10 +209,11 @@ void usage() {
#ifdef WIN32
// Return 1 on success
#define fwrite write_to_handle_or_file
-static int write_to_handle_or_file(void *buffer,
- int length,
- int items,
- HANDLE_OR_FILEPTR file)
+static int
+write_to_handle_or_file(void *buffer,
+ int length,
+ int items,
+ HANDLE_OR_FILEPTR file)
{
int result;
DWORD written;
@@ -166,7 +225,8 @@ static int write_to_handle_or_file(void *buffer,
return 1;
}
-static HANDLE_OR_FILEPTR stdout_handle_or_file()
+static HANDLE_OR_FILEPTR
+stdout_handle_or_file()
{
return GetStdHandle(STD_OUTPUT_HANDLE);
}
@@ -174,16 +234,18 @@ static HANDLE_OR_FILEPTR stdout_handle_or_file()
#define fclose CloseHandle
#else
-static HANDLE_OR_FILEPTR stdout_handle_or_file()
+static HANDLE_OR_FILEPTR
+stdout_handle_or_file()
{
return stdout;
}
#endif
-static HANDLE_OR_FILEPTR open_windows_pipe(const char *filename)
+static HANDLE_OR_FILEPTR
+open_windows_pipe(const char *filename)
{
- #ifdef WIN32
+#ifdef WIN32
HANDLE pipe;
int result;
@@ -206,16 +268,17 @@ static HANDLE_OR_FILEPTR open_windows_pipe(const char *filename)
}
return pipe;
- #else
+#else
die("Cannot open a windows named pipe on a non-windows OS. Giving up.");
return 0;
- #endif
+#endif
}
-static void open_file_for_writing(HANDLE_OR_FILEPTR *hf, const char *filename)
+static void
+open_file_for_writing(HANDLE_OR_FILEPTR *hf, const char *filename)
{
int result = 0;
- #ifdef WIN32
+#ifdef WIN32
*hf = CreateFile(filename,
GENERIC_WRITE,
0,
@@ -239,9 +302,10 @@ static void open_file_for_writing(HANDLE_OR_FILEPTR *hf, const char *filename)
//----------------------------------------------------------------------
// Optical E1/T1: just enable; no special options required.
-static void enable_optical_l1(GTH_api *api,
- const char* span,
- const int monitoring)
+static void
+enable_optical_l1(GTH_api *api,
+ const char* span,
+ const int monitoring)
{
int result;
@@ -255,9 +319,10 @@ static void enable_optical_l1(GTH_api *api,
}
// Electrical E1: disable TX pins and possibly enable -20dB monitoring
-static void enable_electrical_l1(GTH_api *api,
- const char* span,
- const int monitoring)
+static void
+enable_electrical_l1(GTH_api *api,
+ const char* span,
+ const int monitoring)
{
int result;
char pcm_name[20];
@@ -279,32 +344,45 @@ static void enable_electrical_l1(GTH_api *api,
die("Setting up L1 failed. (-v switch gives more information)");
}
-// Start up L1 on the given span.
-static void enable_l1(GTH_api *api, const char* span, const int monitoring)
+// Start L1 for all channels.
+// This can cause an L1 to be started multiple times. That's OK.
+static void
+enable_l1(GTH_api *api,
+ const Channel_t channels[],
+ const int n_channels,
+ const int monitoring)
{
char architecture[10];
+ int i;
int result;
- result = gth_query_resource_attribute(api, "board", "architecture",
+ result = gth_query_resource_attribute(api, "board", "architecture",
architecture, 10);
if (result != 0)
die("Unable to query hardware architecture. Giving up.");
architecture[3] = 0;
- if (strcmp(architecture, "gth") == 0)
- enable_electrical_l1(api, span, monitoring);
- else
- enable_optical_l1(api, span, monitoring);
+ if (strcmp(architecture, "gth") == 0) {
+ for (i = 0; i < n_channels; i++) {
+ enable_electrical_l1(api, channels[i].pcm, monitoring);
+ }
+ }
+ else {
+ for (i = 0; i < n_channels; i++) {
+ enable_optical_l1(api, channels[i].pcm, monitoring);
+ }
+ }
}
// Start up MTP-2 monitoring on the given span and timeslot
-static void monitor_mtp2(GTH_api *api,
- const char *span,
- int timeslot,
- int tag,
- int listen_port,
- int listen_socket
- )
+static void
+monitor_mtp2(GTH_api *api,
+ const char *span,
+ int timeslot,
+ int tag,
+ int listen_port,
+ int listen_socket
+ )
{
int result;
char job_id[MAX_JOB_ID];
@@ -318,7 +396,9 @@ static void monitor_mtp2(GTH_api *api,
}
// Read exactly the requested number of bytes from the given descriptor
-void read_exact(int fd, char* buf, size_t count) {
+void
+read_exact(int fd, char* buf, size_t count)
+{
size_t this_time;
while (count > 0) {
@@ -331,11 +411,21 @@ void read_exact(int fd, char* buf, size_t count) {
}
}
-// Write a PCAP global header
-static void write_pcap_header(HANDLE_OR_FILEPTR file)
+void inline
+checked_fwrite(void *b, int n, HANDLE_OR_FILEPTR f)
{
- int result;
- PCAP_global_header header;
+ int result = fwrite(b, n, 1, f);
+
+ if (result != 1) {
+ die("fwrite failed. (Is the output file writeable?)");
+ }
+}
+
+
+static void
+write_pcap_classic_header(HANDLE_OR_FILEPTR file)
+{
+ PCap_classic_global_header header;
// The pcap file is native-endian, i.e. wireshark uses the magic value
// to figure out if it was created on a little-endian or big-endian machine.
@@ -345,24 +435,152 @@ static void write_pcap_header(HANDLE_OR_FILEPTR file)
header.GMT_to_localtime = 0;
header.sigfigs = 0;
header.snaplen = 65535;
- header.network = 140; // 140 == MTP-2
+ header.network = LINK_TYPE_MTP2;
- result = fwrite((void*)&header, sizeof header, 1, file);
+ checked_fwrite((void*)&header, sizeof header, file);
- if (result != 1) {
- die("Unable to write PCAP header. (Is the file writeable?)");
- }
+ return;
+}
+
+// Must be a multiple of 4 because of padding rules in PCap-ng
+#define MAX_HW_DESCRIPTION 400
+char hw_description[MAX_HW_DESCRIPTION];
+
+void
+read_hw_description(GTH_api *api, const char *hostname)
+{
+ int result;
+ char architecture[10];
+
+ result = gth_query_resource_attribute(api, "board", "architecture",
+ architecture, 10);
+ if (result != 0)
+ die("Unable to query hardware architecture. Giving up.");
+
+ result = snprintf(hw_description, MAX_HW_DESCRIPTION,
+ "save_to_pcap (Corelatus %s %s)",
+ architecture, hostname);
+ if (result >= MAX_HW_DESCRIPTION)
+ die("Hardware description is too long");
+}
+
+static int inline
+round_up_32_bit(int x)
+{
+ return (x + 3) & (~3);
+}
+
+static void
+write_pcap_ng_shb(HANDLE_OR_FILEPTR file)
+{
+ PCap_NG_shb shb;
+ u32 btl;
+ PCap_NG_option userappl = {4, 0}; // 4 = user application name
+ PCap_NG_option end_of_options = {0, 0};
+ userappl.length = strlen(hw_description);
+
+ btl = sizeof(PCap_NG_shb)
+ + sizeof(userappl) + round_up_32_bit(userappl.length)
+ + sizeof(end_of_options)
+ + sizeof(shb.block_total_length);
+
+ shb.type = 0x0A0D0D0A;
+ shb.block_total_length = btl;
+ shb.byte_order_magic = 0x1A2B3C4D;
+ shb.major_version = 1;
+ shb.minor_version = 0;
+ shb.section_length = 0xffffffffFFFFFFFFULL; // "unknown"
+
+ checked_fwrite((void*)&shb, sizeof shb, file);
+ checked_fwrite((void*)&userappl, sizeof userappl, file);
+ checked_fwrite((void*)hw_description, round_up_32_bit(userappl.length), file);
+ checked_fwrite((void*)&end_of_options, sizeof end_of_options, file);
+ checked_fwrite((void*)&btl, sizeof(btl), file);
return;
}
-// Write a PCAP per-packet header
-static void write_packet_header(HANDLE_OR_FILEPTR file,
- unsigned int timestamp_hi,
- unsigned int timestamp_lo,
- int length)
+// Must be a multiple of 4 because of padding rules in PCap-ng
+#define MAX_IF_NAME 32
+
+// Having both if_description and if_name causes Wireshark 1.10.3 to
+// display only if_description. So we only have if_name.
+static void
+write_pcap_idbs(HANDLE_OR_FILEPTR file, Channel_t *c, int n)
+{
+ int x;
+ PCap_NG_option end_of_options = {0, 0};
+
+ for (x = 0; x < n; x++) {
+ PCap_NG_idb idb;
+ PCap_NG_option if_name;
+ char idb_if_name[MAX_IF_NAME];
+ PCap_NG_option if_tsresol;
+ char tsresol[4] = {3, 0, 0, 0}; // 10^-3 resolution, three padding bytes
+
+ u32 block_total_length;
+
+ if_name.code = 2; // if_name
+ if_name.length = snprintf(idb_if_name, MAX_IF_NAME, "%s:%d",
+ c[x].pcm, c[x].timeslot);
+ if (if_name.length < 0 || if_name.length >= MAX_IF_NAME)
+ die("interface name is too long");
+
+ block_total_length = sizeof(idb)
+ + sizeof(PCap_NG_option) + round_up_32_bit(if_name.length)
+ + sizeof(PCap_NG_option) + sizeof(tsresol)
+ + sizeof(PCap_NG_option)
+ + sizeof(block_total_length);
+
+ idb.type = 1;
+ idb.block_total_length = block_total_length;
+ idb.link_type = LINK_TYPE_MTP2;
+ idb.reserved = 0;
+ idb.snaplen = 279;
+
+ if_tsresol.code = 9; // tsresol
+ if_tsresol.length = 1;
+
+ checked_fwrite((void*)&idb, sizeof(idb), file);
+
+ checked_fwrite((void*)&if_name, sizeof (if_name), file);
+ checked_fwrite((void*)&idb_if_name, round_up_32_bit(if_name.length), file);
+
+ checked_fwrite((void*)&if_tsresol, sizeof(if_tsresol), file);
+ checked_fwrite((void*)&tsresol, sizeof(tsresol), file);
+
+ checked_fwrite((void*)&end_of_options, sizeof(end_of_options), file);
+
+ checked_fwrite((void*)&block_total_length, sizeof block_total_length, file);
+ }
+}
+
+static void
+write_pcap_global_header(HANDLE_OR_FILEPTR file,
+ enum PCap_format format,
+ Channel_t *channels,
+ int n_channels)
{
- PCAP_packet_header pcap_header;
+ switch (format) {
+ case PCAP_CLASSIC:
+ write_pcap_classic_header(file);
+ break;
+
+ case PCAP_NG:
+ write_pcap_ng_shb(file);
+ write_pcap_idbs(file, channels, n_channels);
+ break;
+
+ default: die("internal error writing global pcap header");
+ }
+}
+
+static void write_classic_packet_header(HANDLE_OR_FILEPTR file,
+ u32 timestamp_hi,
+ u32 timestamp_lo,
+ int length)
+{
+ PCap_classic_packet_header pcap_header;
unsigned long long ts_sec;
unsigned long long ts_us;
int result;
@@ -376,8 +594,8 @@ static void write_packet_header(HANDLE_OR_FILEPTR file,
ts_sec = ts_us / 1000;
ts_us = (ts_us % 1000) * 1000;
- pcap_header.ts_sec = (unsigned int)ts_sec;
- pcap_header.ts_us = (unsigned int)ts_us;
+ pcap_header.ts_sec = (u32)ts_sec;
+ pcap_header.ts_us = (u32)ts_us;
pcap_header.incl_len = length;
pcap_header.orig_len = length;
@@ -387,24 +605,71 @@ static void write_packet_header(HANDLE_OR_FILEPTR file,
}
}
-static void write_packet_payload(HANDLE_OR_FILEPTR file, char *payload,
- int length) {
- int result;
+static u32
+write_ng_packet_header(HANDLE_OR_FILEPTR file,
+ u32 timestamp_hi,
+ u32 timestamp_lo,
+ u16 tag,
+ int length)
+{
+ PCap_NG_epb epb;
- result = fwrite(payload, length, 1, file);
- if (result != 1)
- die("Unable to write packet to the given file. (Is it writeable?)");
+ epb.type = 6;
+ epb.block_total_length = sizeof(epb)
+ + round_up_32_bit(length)
+ + sizeof(epb.block_total_length);
+ epb.interface_id = tag;
+ epb.timestamp_hi = timestamp_hi;
+ epb.timestamp_lo = timestamp_lo;
+ epb.captured_len = length;
+ epb.packet_len = length;
+
+ checked_fwrite(&epb, sizeof epb, file);
+
+ return epb.block_total_length;
+}
+
+static inline void
+write_packet(HANDLE_OR_FILEPTR file,
+ u32 timestamp_hi,
+ u32 timestamp_lo,
+ u16 tag,
+ void *payload,
+ int length,
+ int format)
+{
+ u32 total_length;
+
+ switch (format) {
+ case PCAP_CLASSIC:
+ write_classic_packet_header(file, timestamp_hi, timestamp_lo, length);
+ checked_fwrite(payload, length, file);
+ break;
+
+ case PCAP_NG:
+ total_length = write_ng_packet_header(file, timestamp_hi,
+ timestamp_lo, tag, length);
+ checked_fwrite(payload, round_up_32_bit(length), file);
+ checked_fwrite(&total_length, sizeof total_length, file);
+ break;
+
+ default:
+ die("internal error");
+ }
}
#define MAX_FILENAME 100
// Loop forever, converting the incoming GTH data to libpcap format
-static void convert_to_pcap(int data_socket,
- const char *base_name,
- const int n_sus_per_file
- )
+static void
+convert_to_pcap(int data_socket,
+ const char *base_name,
+ const int n_sus_per_file,
+ Channel_t channels[],
+ int n_channels,
+ const enum PCap_format format)
{
- unsigned short length;
+ u16 length;
GTH_mtp2 signal_unit;
int su_count;
int file_number = 1;
@@ -419,53 +684,60 @@ static void convert_to_pcap(int data_socket,
while (1) {
char filename[MAX_FILENAME];
- if (!write_to_stdout && !write_to_pipe) {
+ if (!write_to_stdout && !write_to_pipe)
+ {
snprintf(filename, MAX_FILENAME, "%s.%d",
base_name, file_number);
open_file_for_writing(&file, filename);
fprintf(stderr, "saving to file %s\n", filename);
}
- else if (write_to_stdout) {
+ else if (write_to_stdout)
+ {
file = stdout_handle_or_file();
fprintf(stderr, "saving capture to stdout\n");
}
- else {
+ else
+ {
fprintf(stderr, "saving capture to a windows named pipe\n");
file = open_windows_pipe(base_name);
}
- write_pcap_header(file);
+ write_pcap_global_header(file, format, channels, n_channels);
+
file_number++;
su_count = 0;
while ( write_to_stdout
|| write_to_pipe
|| n_sus_per_file == 0
- || (su_count++ < n_sus_per_file) ) {
- read_exact(data_socket, (void*)&length, sizeof length);
- length = ntohs(length);
- assert(length <= sizeof signal_unit);
- read_exact(data_socket, (void*)&signal_unit, length);
-
- length -= (signal_unit.payload - (char*)&(signal_unit.tag));
-
- write_packet_header(file,
- signal_unit.timestamp_hi, signal_unit.timestamp_lo,
- length);
-
- write_packet_payload(file, signal_unit.payload, length);
-
- if (write_to_stdout)
- {
- fflush(stdout);
- }
- }
-
+ || (su_count++ < n_sus_per_file) )
+ {
+ read_exact(data_socket, (void*)&length, sizeof length);
+ length = ntohs(length);
+ assert(length <= sizeof signal_unit);
+ read_exact(data_socket, (void*)&signal_unit, length);
+
+ length -= (signal_unit.payload - (char*)&(signal_unit.tag));
+ write_packet(file,
+ ntohs(signal_unit.timestamp_hi),
+ ntohl(signal_unit.timestamp_lo),
+ ntohs(signal_unit.tag),
+ signal_unit.payload,
+ length,
+ format);
+
+ if (write_to_stdout)
+ {
+ fflush(stdout);
+ }
+ }
fclose(file);
}
}
-static int is_span_name(char *arg){
+static int
+is_span_name(char *arg)
+{
if (strstr(arg, "pcm")) return 1; // Optical hardware uses e.g. 'pcm55'
if (strstr(arg, "A")) return 1;
if (strstr(arg, "B")) return 1;
@@ -474,12 +746,8 @@ static int is_span_name(char *arg){
return 0;
}
-typedef struct {
- char *pcm;
- int timeslot;
-} Channels_t;
-
-static void print_channels(Channels_t *channels, int n)
+static void
+print_channels(Channel_t *channels, int n)
{
while (n-- > 0) {
fprintf(stderr, "monitoring %s:%d\n", channels->pcm, channels->timeslot);
@@ -488,13 +756,14 @@ static void print_channels(Channels_t *channels, int n)
}
// Return the number of arguments consumed
-static int arguments_to_channels(int argc,
- char **argv,
- Channels_t *channels,
- int *n_channels,
- char **pcms,
- int *n_pcms)
+static int
+arguments_to_channels(int argc,
+ char **argv,
+ Channel_t *channels,
+ int *n_channels)
{
+ char *pcms[MAX_PCMS];
+ int n_pcms = 0;
int current_arg = 0;
int timeslot = 0;
int i;
@@ -504,22 +773,22 @@ static int arguments_to_channels(int argc,
if (is_span_name(argv[current_arg])){
if (timeslot){
// We are starting a new pcm group
- *n_pcms = 0;
+ n_pcms = 0;
timeslot = 0;
}
- pcms[*n_pcms] = argv[current_arg];
- (*n_pcms)++;
+ pcms[n_pcms] = argv[current_arg];
+ n_pcms++;
}
- else{
+ else {
timeslot = atoi(argv[current_arg]);
if ( (timeslot < 1) || (timeslot > 31) ) {
fprintf(stderr, "Valid timeslots are 1--31, not %d. Abort.\n", timeslot);
exit(-1);
}
- if (!*n_pcms){
+ if (!n_pcms){
die("Timeslot given without previous pcm.");
}
- for (i = 0; i < *n_pcms; i++){
+ for (i = 0; i < n_pcms; i++){
if (*n_channels >= MAX_MTP2_CHANNELS)
die("Attempted to start too many signalling channels. Abort.");
@@ -543,47 +812,38 @@ static int arguments_to_channels(int argc,
return current_arg;
}
-// Entry point
-int main(int argc, char **argv)
+void static
+process_arguments(char **argv,
+ int argc,
+ int *monitoring,
+ int *verbose,
+ int *n_sus_per_file,
+ char **hostname,
+ Channel_t channels[],
+ int *n_channels,
+ char **base_filename,
+ enum PCap_format *format)
{
- GTH_api api;
int current_arg;
- int data_socket = -1;
- int result;
- int monitoring = 0;
- int verbose = 0;
- Channels_t channels[MAX_MTP2_CHANNELS];
- char *pcms[MAX_PCMS];
- int n_pcms = 0;
- int i;
- int n_channels = 0;
- int n_sus_per_file = 0;
- int listen_port = 0;
- int listen_socket = -1;
- int tag;
-
- // Check a couple of assumptions about type size.
- assert(sizeof(unsigned int) == 4);
- assert(sizeof(unsigned short) == 2);
-
- win32_specific_startup();
while (argc > 1 && argv[1][0] == '-') {
switch (argv[1][1]) {
- case 'm': monitoring = 1; break;
+ case 'c': *format = PCAP_CLASSIC; break;
- case 'v': verbose = 1; break;
+ case 'm': *monitoring = 1; break;
case 'n':
if (argc < 3) {
usage();
} else {
- n_sus_per_file = atoi(argv[2]);
+ *n_sus_per_file = atoi(argv[2]);
argc--;
argv++;
}
break;
+ case 'v': *verbose = 1; break;
+
default: usage();
}
argc--;
@@ -594,10 +854,7 @@ int main(int argc, char **argv)
usage();
}
- result = gth_connect(&api, argv[1], verbose);
- if (result != 0) {
- die("Unable to connect to the GTH. Giving up.");
- }
+ *hostname = argv[1];
argv += 2;
argc -= 2;
@@ -605,33 +862,65 @@ int main(int argc, char **argv)
current_arg = arguments_to_channels(argc,
argv,
channels,
- &n_channels,
- pcms,
- &n_pcms);
+ n_channels);
+ if (!n_channels){
+ die("No timeslots given (or, perhaps, no output filename given).");
+ }
- print_channels(channels, n_channels);
+ print_channels(channels, *n_channels);
- for (i = 0; i < n_pcms; i++) {
- enable_l1(&api, pcms[i], monitoring);
- }
+ *base_filename = argv[current_arg];
+}
- if (!n_channels){
- die("No timeslots given (or, perhaps, no output filename given).");
+// Entry point
+int
+main(int argc, char **argv)
+{
+ GTH_api api;
+ int data_socket = -1;
+ int result;
+ int monitoring = 0;
+ int verbose = 0;
+ Channel_t channels[MAX_MTP2_CHANNELS];
+ int i;
+ int n_channels = 0;
+ int n_sus_per_file = 0;
+ int listen_port = 0;
+ int listen_socket = -1;
+ enum PCap_format format = PCAP_NG;
+ char *hostname;
+ char *base_filename;
+
+ // Check a couple of assumptions about type size.
+ assert(sizeof(u32) == 4);
+ assert(sizeof(u16) == 2);
+
+ win32_specific_startup();
+
+ process_arguments(argv, argc,
+ &monitoring, &verbose, &n_sus_per_file, &hostname,
+ channels, &n_channels, &base_filename, &format);
+
+ result = gth_connect(&api, hostname, verbose);
+ if (result != 0) {
+ die("Unable to connect to the GTH. Giving up.");
}
- tag = 0;
+ read_hw_description(&api, hostname);
+ enable_l1(&api, channels, n_channels, monitoring);
+
listen_socket = gth_make_listen_socket(&listen_port);
for (i = 0; i < n_channels; i++){
monitor_mtp2(&api, channels[i].pcm, channels[i].timeslot,
- tag, listen_port, listen_socket);
- if (!tag) {
+ i, listen_port, listen_socket);
+ if (i == 0) {
data_socket = gth_wait_for_accept(listen_socket);
}
- tag++;
}
fprintf(stderr, "capturing packets, press ^C to abort\n");
- convert_to_pcap(data_socket, argv[current_arg], n_sus_per_file);
+ convert_to_pcap(data_socket, base_filename, n_sus_per_file,
+ channels, n_channels, format);
return 0; // not reached
}
View
6 c/unmap.c
@@ -50,6 +50,8 @@
static void usage()
{
fprintf(stderr,
+ "unmap git_head: %s build_hostname: %s\n\n"
+
"unmap [-v] <GTH-IP> <E1/T1 resource>\n\n"
"Unmap an E1/T1 link carried on SDH/SONET.\n\n"
@@ -58,8 +60,8 @@ static void usage()
"<resource> is an E1/T1 resource\n"
"Example:\n"
- "./unmap 172.16.1.10 pcm1\n"
- );
+ "./unmap 172.16.1.10 pcm1\n",
+ git_head, build_hostname);
exit(-1);
}
View
5 c/wide_recorder.c
@@ -50,12 +50,15 @@
static void usage()
{
fprintf(stderr,
+ "wide_recorder git_head: %s build_hostname: %s\n\n"
+
"wide_recorder [-v] <GTH-IP> <span> <filename>\n\n"
"Save bit-exact representing an entire E1/T1 to a file\n\n"
"-v: print the API commands and responses (verbose)\n"
"<GTH-IP> is the GTH's IP address or hostname\n"
"<span> is the E1/T1 interface, e.g. '1A'\n"
- "<filename> is the filename to save to\n\n");
+ "<filename> is the filename to save to\n\n",
+ git_head, build_hostname);
fprintf(stderr, "Typical use:\n");
fprintf(stderr, "./wide_recorder 172.16.1.10 13 capture_file.raw\n\n");
View
20 erlang/save_to_pcap.erl
@@ -1,8 +1,9 @@
-module(save_to_pcap).
%%
-%% Start up signalling (MTP-2, frame relay or AAL5) monitoring on the given
-%% E1 interface/timeslots and save the signal units to a file in
-%% libpcap format, suitable for viewing with wireshark or tcpdump.
+%% Start up signalling (MTP-2, frame relay, LAPD or AAL5) monitoring
+%% on the given E1 interface/timeslots and save the signal units to a
+%% file in libpcap format, suitable for viewing with wireshark or
+%% tcpdump.
%%
%% The AAL5 can be either SAAL (SS7 signalling over ATM) or LLC (IP over ATM)
%%
@@ -14,7 +15,18 @@
%%
%% or
%%
-%% save_to_pcap:frame_relay("172.16.2.7", "2B", lists:seq(1,15), "/tmp/gb.pcap").
+%% save_to_pcap:frame_relay("172.16.2.7", "2B", lists:seq(1,15),
+%% "/tmp/gb.pcap").
+%%
+%% or
+%%
+%% save_to_pcap:lapd("172.16.2.7", "2B", 16, "/tmp/lapd.pcap").
+%%
+%% or
+%%
+%% save_to_pcap:aal5("172.16.2.7", "2B", lists:seq(1,31), {0, 5},
+%% "/tmp/aal5.pcap").
+%%
%%
%% The Layer 1 setup assumes an E1 connected through a monitor point.
%% Hack the 'gth:set' line if you're using T1 and/or are connected via

No commit comments for this range

Something went wrong with that request. Please try again.