summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Meyerriecks <rmeyerreicks@digium.com>2011-11-01 18:10:14 +0000
committerRuss Meyerriecks <rmeyerreicks@digium.com>2011-11-01 18:10:14 +0000
commitbfa413298ae251fdd60df09a0924763cd8089a72 (patch)
treead43a5fb91bdcb80646315c01fab52c120fbd435
parent61efa562e2f475afff5b25927f30c7cd32b82ed2 (diff)
dahdi_pcap: Imported user space utility for managing pcap streams
This utility will export packet captures for channels in dahdi. It requires CONFIG_DAHDI_MIRROR to be defined in dahdi-linux as it uses the unsupported DAHDI_MIRROR ioctl interface. Internal-Issue-ID: DAHTOOL-49 From: Torrey Searle <tsearle@gmail.com> Signed-off-by: Russ Meyerriecks <rmeyerriecks@digium.com> Acked-by: Shaun Ruffell <sruffell@digium.com> git-svn-id: http://svn.asterisk.org/svn/dahdi/tools/trunk@10293 a0bf4364-ded3-4de4-8d8a-66a801d63aff
-rw-r--r--Makefile4
-rw-r--r--dahdi_pcap.c332
2 files changed, 336 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index be30fee..dd7e2fb 100644
--- a/Makefile
+++ b/Makefile
@@ -176,6 +176,9 @@ $(LTZ_SO): $(LTZ_SO_OBJS)
dahdi_cfg: $(LTZ_A)
dahdi_cfg: LIBS+=-lm
+dahdi_pcap:
+ $(CC) $(CFLAGS) dahdi_pcap.c -lpcap -o $@ $<
+
fxstest: $(LTZ_SO)
fxstest: LIBS+=-lm
@@ -345,6 +348,7 @@ clean:
rm -f core
rm -f dahdi_cfg-shared fxstest
rm -rf $(GENERATED_DOCS) *.asciidoc tonezones.txt
+ rm -f dahdi_pcap
distclean: dist-clean
diff --git a/dahdi_pcap.c b/dahdi_pcap.c
new file mode 100644
index 0000000..f6fa7fe
--- /dev/null
+++ b/dahdi_pcap.c
@@ -0,0 +1,332 @@
+/*
+ * Capturing a pcap from the DAHDI interface
+ *
+ * Copyright (C) 2011 Torrey Searle
+ *
+ * ISDN support added by Horacio Peña
+ * Command line cleanups by Sverker Abrahamsson
+ *
+ * Requirements:
+ * - pcap development library
+ * - DAHDI_MIRROR ioctl which isn't enabled by default in dahdi-linux
+ * To enable this unsupported feature, #define CONFIG_DAHDI_MIRROR
+ * in dahdi-linux
+ * - To build this program call the 'make dahdi_pcap' target
+ */
+
+/*
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2 as published by the
+ * Free Software Foundation. See the LICENSE file included with
+ * this program for more details.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <dahdi/user.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <pcap.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#define BLOCK_SIZE 512
+#define MAX_CHAN 16
+//char ETH_P_LAPD[2] = {0x00, 0x30};
+
+struct mtp2_phdr {
+ u_int8_t sent;
+ u_int8_t annex_a_used;
+ u_int16_t link_number;
+};
+
+
+struct lapd_sll_hdr {
+ u_int16_t sll_pkttype; /* packet type */
+ u_int16_t sll_hatype;
+ u_int16_t sll_halen;
+ u_int8_t sll_addr[8];
+ u_int8_t sll_protocol[2]; /* protocol, should be ETH_P_LAPD */
+};
+
+
+struct chan_fds {
+ int rfd;
+ int tfd;
+ int chan_id;
+ int proto;
+ char tx_buf[BLOCK_SIZE * 4];
+ int tx_len;
+ char rx_buf[BLOCK_SIZE * 4];
+ int rx_len;
+};
+
+int make_mirror(long type, int chan)
+{
+ int res = 0;
+ int fd = 0;
+ struct dahdi_bufferinfo bi;
+ fd = open("/dev/dahdi/pseudo", O_RDONLY);
+
+ memset(&bi, 0, sizeof(bi));
+ bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
+ bi.numbufs = 32;
+ bi.bufsize = BLOCK_SIZE;
+
+ ioctl(fd, DAHDI_SET_BUFINFO, &bi);
+
+ res = ioctl(fd, type, &chan);
+
+ if(res)
+ {
+ printf("error setting channel err=%d!\n", res);
+ return -1;
+ }
+
+
+ return fd;
+}
+
+int log_packet(struct chan_fds * fd, char is_read, pcap_dumper_t * dump)
+{
+ unsigned char buf[BLOCK_SIZE * 4];
+ int res = 0;
+
+ struct pcap_pkthdr hdr;
+ struct mtp2_phdr * mtp2 = (struct mtp2_phdr *)buf;
+ struct lapd_sll_hdr * lapd = (struct lapd_sll_hdr *)buf;
+
+ unsigned char *dataptr = buf;
+ int datasize = sizeof(buf);
+
+ if(fd->proto == DLT_LINUX_LAPD)
+ {
+ dataptr += sizeof(struct lapd_sll_hdr);
+ datasize -= sizeof(struct lapd_sll_hdr);
+ }
+ else
+ {
+ dataptr += sizeof(struct mtp2_phdr);
+ datasize -= sizeof(struct mtp2_phdr);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ if(is_read)
+ {
+ res = read(fd->rfd, dataptr, datasize);
+ if(fd->rx_len > 0 && res == fd->rx_len && !memcmp(fd->rx_buf, dataptr, res) )
+ {
+ //skipping dup
+ return 0;
+ }
+
+ memcpy(fd->rx_buf, dataptr, res);
+ fd->rx_len = res;
+ }
+ else
+ {
+ res = read(fd->tfd, dataptr, datasize);
+ if(fd->tx_len > 0 && res == fd->tx_len && !memcmp(fd->tx_buf, dataptr, res) )
+ {
+ //skipping dup
+ return 0;
+ }
+
+ memcpy(fd->tx_buf, dataptr, res);
+ fd->tx_len = res;
+ }
+
+ gettimeofday(&hdr.ts, NULL);
+
+
+
+
+ if(res > 0)
+ {
+ if(fd->proto == DLT_LINUX_LAPD)
+ {
+ hdr.caplen = res+sizeof(struct lapd_sll_hdr)-2;
+ hdr.len = res+sizeof(struct lapd_sll_hdr)-2;
+
+ lapd->sll_pkttype = 3;
+ lapd->sll_hatype = 0;
+ lapd->sll_halen = res;
+ // lapd->sll_addr = ???
+ lapd->sll_protocol[0] = 0x00;
+ lapd->sll_protocol[1] = 0x30;
+
+ }
+ else
+ {
+ hdr.caplen = res+sizeof(struct mtp2_phdr);
+ hdr.len = res+sizeof(struct mtp2_phdr);
+
+ if(is_read)
+ {
+ mtp2->sent = 0;
+ mtp2->annex_a_used = 0;
+ }
+ else
+ {
+ mtp2->sent = 1;
+ mtp2->annex_a_used = 0;
+ }
+ mtp2->link_number = htons(fd->chan_id);
+ }
+ pcap_dump((u_char*)dump, &hdr, buf);
+ pcap_dump_flush(dump);
+ }
+ return 1;
+}
+
+void usage()
+{
+ printf("Usage: dahdi_pcap [OPTIONS]\n");
+ printf("Capture packets from DAHDI channels to pcap file\n\n");
+ printf("Options:\n");
+ printf(" -p, --proto=[mtp2|lapd] The protocol to capture, default mtp2\n");
+ printf(" -c, --chan=<channels> Comma separated list of channels to capture from, max %d. Mandatory\n", MAX_CHAN);
+ printf(" -f, --file=<filename> The pcap file to capture to. Mandatory\n");
+ printf(" -h, --help Display this text\n");
+}
+
+int main(int argc, char **argv)
+{
+ struct chan_fds chans[MAX_CHAN];
+ char *filename = NULL;
+ int num_chans = 0;
+ int max_fd = 0;
+ int proto = DLT_MTP2_WITH_PHDR;
+
+ int i;
+ int packetcount;
+ int c;
+
+ while (1) {
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"proto", required_argument, 0, 'p'},
+ {"chan", required_argument, 0, 'c'},
+ {"file", required_argument, 0, 'f'},
+ {"help", 0, 0, 'h'},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "p:c:f:?",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'p':
+ // Protocol
+ if(strcasecmp("LAPD", optarg)==0)
+ {
+ proto = DLT_LINUX_LAPD;
+ }
+ else if(argc > 0 && strcasecmp("MTP2", argv[1])==0)
+ {
+ proto = DLT_MTP2_WITH_PHDR;
+ }
+ break;
+ case 'c':
+ // TODO Should it be possible to override protocol per channel?
+ // Channels, comma separated list
+ while(optarg != NULL && num_chans < MAX_CHAN)
+ {
+ int chan = atoi(strsep(&optarg, ","));
+
+
+ chans[num_chans].tfd = make_mirror(DAHDI_TXMIRROR, chan);
+ chans[num_chans].rfd = make_mirror(DAHDI_RXMIRROR, chan);
+ chans[num_chans].chan_id = chan;
+ chans[num_chans].proto = proto;
+
+ if(chans[num_chans].tfd > max_fd)
+ {
+ max_fd = chans[num_chans].tfd;
+ }
+ if(chans[num_chans].rfd > max_fd)
+ {
+ max_fd = chans[num_chans].rfd;
+ }
+
+ num_chans++;
+ }
+ max_fd++;
+ break;
+ case 'f':
+ // File to capture to
+ filename=optarg;
+ break;
+ case 'h':
+ default:
+ // Usage
+ usage();
+ exit(0);
+ }
+ }
+ if((num_chans == 0) || (filename == NULL)) {
+ usage();
+ exit(0);
+ }
+ else
+ {
+ printf("Capturing protocol %s on channels ", (proto == DLT_MTP2_WITH_PHDR ? "mtp2":"lapd"));
+ for(i = 0; i < num_chans; i++)
+ {
+ printf("%d", chans[i].chan_id);
+ if(i<num_chans-1)
+ {
+ printf(", ");
+ }
+ }
+ printf(" to file %s\n", filename);
+ }
+
+ pcap_t * pcap = pcap_open_dead(chans[0].proto, BLOCK_SIZE*4);
+ pcap_dumper_t * dump = pcap_dump_open(pcap, filename);
+
+ packetcount=0;
+ while(1)
+ {
+ fd_set rd_set;
+ FD_ZERO(&rd_set);
+ for(i = 0; i < num_chans; i++)
+ {
+ FD_SET(chans[i].tfd, &rd_set);
+ FD_SET(chans[i].rfd, &rd_set);
+ }
+
+ select(max_fd, &rd_set, NULL, NULL, NULL);
+
+ for(i = 0; i < num_chans; i++)
+ {
+ if(FD_ISSET(chans[i].rfd, &rd_set))
+ {
+ packetcount += log_packet(&chans[i], 1, dump);
+ }
+ if(FD_ISSET(chans[i].tfd, &rd_set))
+ {
+ packetcount += log_packet(&chans[i], 0, dump);
+ }
+ }
+ printf("Packets captured: %d\r", packetcount);
+ fflush(stdout);
+ }
+
+ return 0;
+}