From a714fbc462588647d52e1190057dd30e76a5a5a3 Mon Sep 17 00:00:00 2001 From: Dylan Mikus Date: Tue, 10 Nov 2015 19:12:28 +0000 Subject: [PATCH] Record RTP in PCAP files in the /tmp directory. RTP Engine creates PCAP files for recorded calls on offer answer instead of initial offer. We make up bogus values for the nonessential parts of the PCAP, UDP, and IP headers. We might be able to pull these from other parts of the RTP Engine, but that information was unnecessary for recording calls so they can be recorded to audio files. If you change the packet headers, be really careful about byte order and datatype size! --- daemon/Makefile | 2 ++ daemon/call.c | 62 +++++++++++++++++++++-------------- daemon/call.h | 4 ++- daemon/main.c | 2 +- daemon/media_socket.c | 76 +++++++++++++++++++++++++++++++++---------- daemon/media_socket.h | 4 ++- debian/control | 1 + 7 files changed, 107 insertions(+), 44 deletions(-) diff --git a/daemon/Makefile b/daemon/Makefile index 941a520e5..ac353440f 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -6,6 +6,7 @@ CFLAGS+= `pkg-config --cflags gthread-2.0` CFLAGS+= `pkg-config --cflags zlib` CFLAGS+= `pkg-config --cflags openssl` CFLAGS+= `pkg-config --cflags libevent_pthreads` +CFLAGS+= "-lpcap" CFLAGS+= `pcre-config --cflags` CFLAGS+= -I../kernel-module/ CFLAGS+= -D_GNU_SOURCE @@ -50,6 +51,7 @@ LDFLAGS+= `pkg-config --libs libpcre` LDFLAGS+= `pkg-config --libs libcrypto` LDFLAGS+= `pkg-config --libs openssl` LDFLAGS+= `pkg-config --libs libevent_pthreads` +LDFLAGS+= "-lpcap" LDFLAGS+= `pcre-config --libs` LDFLAGS+= `xmlrpc-c-config client --libs` LDFLAGS+= -lhiredis diff --git a/daemon/call.c b/daemon/call.c index 6728e5903..f7552e36f 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "poller.h" #include "aux.h" @@ -699,6 +700,8 @@ static struct endpoint_map *__get_endpoint_map(struct call_media *media, unsigne socket_t *sock; struct intf_list *il, *em_il; + ilog(LOG_INFO, "XXDylan: __get_endpoint_map"); + for (l = media->endpoint_maps.tail; l; l = l->prev) { em = l->data; if (em->logical_intf != media->logical_intf) @@ -1503,7 +1506,6 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, struct endpoint_map *em; struct call *call; - call = monologue->call; call->last_signal = poller_now; @@ -1524,6 +1526,8 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, ml_media = other_ml_media = NULL; + setup_recording_files(call, monologue); + for (media_iter = streams->head; media_iter; media_iter = media_iter->next) { sp = media_iter->data; __C_DBG("processing media stream #%u", sp->index); @@ -2406,26 +2410,8 @@ struct call_monologue *__monologue_create(struct call *call) { ret->call = call; ret->created = poller_now; ret->other_tags = g_hash_table_new(str_hash, str_equal); - if (call->record_call) { - char recording_path[15]; - char logbuf[15]; - /* - * - * create a file descriptor per monologue which can be used for writing rtp to disk - * aka call recording. - */ - - sprintf(recording_path, "/tmp/%d", rand()); - GSList *list = NULL; - call->recording_pcaps = g_slist_prepend(call->recording_pcaps, g_strdup(recording_path)); - ilog(LOG_INFO, "xxegreen: path2 %s", call->recording_pcaps->data); - ilog(LOG_INFO, "XXXECT: Creating new file descriptor for recording at path %s", recording_path); - ret->recording_fd = open(recording_path, O_WRONLY | O_CREAT | O_TRUNC); - sprintf(logbuf, "%d", ret->recording_fd); - ilog(LOG_INFO, "XXXECT: FD created: %s", logbuf); - } else { - ret->recording_fd = -1; - } + ret->recording_pd = NULL; + ret->recording_pdumper = NULL; g_queue_init(&ret->medias); gettimeofday(&ret->started, NULL); @@ -2500,9 +2486,14 @@ static void __monologue_destroy(struct call_monologue *monologue) { GList *l; call = monologue->call; - /* XXXECT BEGIN */ - close(monologue->recording_fd); - /* XXXECT END */ + ilog(LOG_INFO, "XXXDylan: closing pcap stuff"); + if (monologue->recording_pdumper != NULL) { + pcap_dump_flush(monologue->recording_pdumper); + pcap_dump_close(monologue->recording_pdumper); + } + if (monologue->recording_pd != NULL) { + pcap_close(monologue->recording_pd); + } g_hash_table_remove(call->tags, &monologue->tag); @@ -2862,3 +2853,26 @@ const struct transport_protocol *transport_protocol(const str *s) { out: return NULL; } + +void setup_recording_files(struct call *call, struct call_monologue *monologue) { + if (call->record_call + && monologue->recording_pd == NULL && monologue->recording_pdumper == NULL) { + char rec_path_prefix[16]; + char recording_path[21]; + /* + * + * create a file descriptor per monologue which can be used for writing rtp to disk + * aka call recording. + */ + + snprintf(rec_path_prefix, 15, "/tmp/%d", rand()); + snprintf(recording_path, 20, "%s.pcap", rec_path_prefix); + call->recording_pcaps = g_slist_prepend(call->recording_pcaps, g_strdup(recording_path)); + ilog(LOG_INFO, "XXXDylan: Creating new pcap dumper for recording at path %s", recording_path); + monologue->recording_pd = pcap_open_dead(DLT_RAW, 65535); + monologue->recording_pdumper = pcap_dump_open(monologue->recording_pd, recording_path); + } else { + monologue->recording_pd = NULL; + monologue->recording_pdumper = NULL; + } +} diff --git a/daemon/call.h b/daemon/call.h index 99f04a89e..25678ec45 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -14,6 +14,7 @@ #include #include #include +#include #include "compat.h" #include "socket.h" @@ -397,8 +398,9 @@ struct call_monologue { enum termination_reason term_reason; GHashTable *other_tags; struct call_monologue *active_dialogue; - int recording_fd; GQueue medias; + pcap_t *recording_pd; + pcap_dumper_t *recording_pdumper; }; struct call { diff --git a/daemon/main.c b/daemon/main.c index b43e1d9cd..374796df7 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -108,7 +108,7 @@ static void sighandler(gpointer x) { continue; abort(); } - + if (ret == SIGINT || ret == SIGTERM) g_shutdown = 1; else if (ret == SIGUSR1) { diff --git a/daemon/media_socket.c b/daemon/media_socket.c index 78a566134..a27965f51 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "str.h" #include "ice.h" #include "socket.h" @@ -47,6 +48,8 @@ struct streamhandler { static void determine_handler(struct packet_stream *in, const struct packet_stream *out); +static void stream_pcap_dump(pcap_dumper_t *pdumper, str *s); + static int __k_null(struct rtpengine_srtp *s, struct packet_stream *); static int __k_srtp_encrypt(struct rtpengine_srtp *s, struct packet_stream *); static int __k_srtp_decrypt(struct rtpengine_srtp *s, struct packet_stream *); @@ -1027,8 +1030,7 @@ static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin, int ret = 0, update = 0, stun_ret = 0, handler_ret = 0, muxed_rtcp = 0, rtcp = 0, unk = 0; int i; - // XXEGREEN... This makes me nervous. - int recording_fd = sfd->stream->media->monologue->recording_fd; + pcap_dumper_t *recording_pdumper; struct call *call; struct callmaster *cm; /*unsigned char cc;*/ @@ -1038,6 +1040,7 @@ static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin, struct rtp_header *rtp_h; struct rtp_stats *rtp_s; + recording_pdumper = sfd->stream->media->monologue->recording_pdumper; call = sfd->call; cm = call->callmaster; @@ -1179,20 +1182,13 @@ loop_ok: * 1 = forward and push update to redis */ if (rwf_in) { handler_ret = rwf_in(s, in_srtp); - ilog(LOG_INFO, "xxegreen peer address as %s", endpoint_print_buf(fsin)); } - // This might be the hook that rfuchs might be referring to - // ilog(LOG_WARNING, "xxegreen0: %s", s->s); - // EGREEN: This is working pretty nicely but we need to remove the first 12 bytes from each packet that it is dumping - if (recording_fd && recording_fd != -1) { - // I am aware that we need to do better and that this is a naive approach - int writelen = (s->len)-12; - char towrite[writelen]; - memcpy(towrite, &s->s[12], writelen); - write(recording_fd, towrite, writelen); - - // EGREEN: This is going to happen for every packet. We need to do better - PS_SET(stream, FORCE_DAEMON_MODE); + + // If recording pcap dumper is set, then we record the call. + if (recording_pdumper != NULL) { + stream_pcap_dump(recording_pdumper, s); + // EGREEN: This is going to happen for every packet. We need to do better + PS_SET(stream, FORCE_DAEMON_MODE); } if (handler_ret >= 0) { @@ -1329,7 +1325,6 @@ forward: goto drop; // s is my packet? - ilog(LOG_INFO, "XXEGREEN NOT"); ret = socket_sendto(&sink->selected_sfd->socket, s->s, s->len, &sink->endpoint); __C_DBG("Forward to sink endpoint: %s:%d", sockaddr_print_buf(&sink->endpoint.address), sink->endpoint.port); @@ -1374,6 +1369,54 @@ unlock_out: } +static void stream_pcap_dump(pcap_dumper_t *pdumper, str *s) { + // Wrap RTP in fake UDP packet header + // Right now, we spoof it all + u_int16_t udp_len = ((u_int16_t)s->len) + 8; + u_int16_t udp_header[4]; + udp_header[0] = htons(5028); // source port + udp_header[1] = htons(50116); // destination port + udp_header[2] = htons(udp_len); // packet length + udp_header[3] = 0; // checksum + + // Wrap RTP in fake IP packet header + u_int8_t ip_header[20]; + u_int16_t *ip_total_length = (u_int16_t*)(ip_header + 2); + u_int32_t *ip_src_addr = (u_int32_t*)(ip_header + 12); + u_int32_t *ip_dst_addr = (u_int32_t*)(ip_header + 16); + memset(ip_header, 0, 20); + ip_header[0] = 4 << 4; // IP version - 4 bits + ip_header[0] = ip_header[0] | 5; // Internet Header Length (IHL) - 4 bits + ip_header[1] = 0; // DSCP - 6 bits + ip_header[1] = 0; // ECN - 2 bits + *ip_total_length = htons(udp_len + 20); // Total Length (entire packet size) - 2 bytes + ip_header[4] = 0; ip_header[5] = 0 ; // Identification - 2 bytes + ip_header[6] = 0; // Flags - 3 bits + ip_header[7] = 0; // Fragment Offset - 13 bits + ip_header[8] = 64; // TTL - 1 byte + ip_header[9] = 17; // Protocol (defines protocol in data portion) - 1 byte + ip_header[10] = 0; ip_header[11] = 0; // Header Checksum - 2 bytes + *ip_src_addr = htonl(2130706433); // Source IP (set to localhost) - 4 bytes + *ip_dst_addr = htonl(2130706433); // Destination IP (set to localhost) - 4 bytes + + // Set up PCAP packet header + struct pcap_pkthdr header; + ZERO(header); + header.ts = g_now; + header.caplen = s->len + 28; + // This must be the same value we use in `pcap_open_dead` + header.len = s->len + 28; + + // Copy all the headers and payload into a new string + unsigned char pkt_s[*ip_total_length]; + memcpy(pkt_s, ip_header, 20); + memcpy(pkt_s + 20, udp_header, 8); + memcpy(pkt_s + 28, s->s, s->len); + + // Write the packet to the PCAP file + // Casting quiets compiler warning. + pcap_dump((unsigned char *)pdumper, &header, (unsigned char *)pkt_s); +} static void stream_fd_readable(int fd, void *p, uintptr_t u) { @@ -1472,7 +1515,6 @@ struct stream_fd *stream_fd_new(socket_t *fd, struct call *call, const struct lo sfd->call = obj_get(call); sfd->local_intf = lif; g_queue_push_tail(&call->stream_fds, sfd); /* hand over ref */ - //sfd->recording_fd = recording_fd; __C_DBG("stream_fd_new localport=%d", sfd->socket.local.port); diff --git a/daemon/media_socket.h b/daemon/media_socket.h index 78e3453a4..5c233a94a 100644 --- a/daemon/media_socket.h +++ b/daemon/media_socket.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "str.h" #include "obj.h" #include "aux.h" @@ -64,7 +65,8 @@ struct stream_fd { struct packet_stream *stream; /* LOCK: call->master_lock */ struct crypto_context crypto; /* IN direction, LOCK: stream->in_lock */ struct dtls_connection dtls; /* LOCK: stream->in_lock */ - int recording_fd; /* XXEGREEN file descriptor to record rtp to */ + pcap_t *recording_pd; + pcap_dumper_t *recording_pdumper; }; diff --git a/debian/control b/debian/control index 500078f00..9a55c3522 100644 --- a/debian/control +++ b/debian/control @@ -8,6 +8,7 @@ Build-Depends: debhelper (>= 5), libevent-dev (>= 2.0), libglib2.0-dev (>= 2.30), libhiredis-dev, + libpcap-dev, libpcre3-dev, libssl-dev (>= 1.0.1), libxmlrpc-c3-dev (>= 1.16.07) | libxmlrpc-core-c3-dev (>= 1.16.07),