# HG changeset patch # User Mychaela Falconia # Date 1681633519 0 # Node ID 34f8549ff0b10f7a057e396f72c8c5d0d0608454 # Parent 13d27ff5b5df6700cd5d122988cdf50913df8b2b drop pcap component - moved to rtp-debug-utils repository diff -r 13d27ff5b5df -r 34f8549ff0b1 .hgignore --- a/.hgignore Sat Feb 18 23:24:54 2023 +0000 +++ b/.hgignore Sun Apr 16 08:25:19 2023 +0000 @@ -40,8 +40,3 @@ ^miscutil/gsmrec-dump$ ^miscutil/pcm16-raw2wav$ ^miscutil/pcm16-wav2raw$ - -^pcap/rtp-cont-check$ -^pcap/rtp-g711-extr$ -^pcap/rtp-gsmfr-extr$ -^pcap/rtp-jitter-view$ diff -r 13d27ff5b5df -r 34f8549ff0b1 INSTALL --- a/INSTALL Sat Feb 18 23:24:54 2023 +0000 +++ b/INSTALL Sun Apr 16 08:25:19 2023 +0000 @@ -82,15 +82,3 @@ efrtest/Makefile frtest/Makefile miscutil/Makefile - pcap/Makefile - -pcap subdirectory and libpcap dependency -======================================== - -The present package includes (as part of Division 2) a set of command line -utilities for analyzing RTP streams that have been captured with tcpdump or -equivalent tools in pcap format. These utilities, described in the -doc/RTP-analysis article, are built in the pcap subdirectory and naturally -depend on libpcap. If your system lacks libpcap and you don't need these RTP -analysis utilities, you can edit the top level Makefile and remove pcap from -the list in SUBDIR_UTILS. diff -r 13d27ff5b5df -r 34f8549ff0b1 Makefile --- a/Makefile Sat Feb 18 23:24:54 2023 +0000 +++ b/Makefile Sun Apr 16 08:25:19 2023 +0000 @@ -2,7 +2,7 @@ CFLAGS= -O2 SUBDIR_LIBPROD= libgsmefr libgsmfrp -SUBDIR_UTILS= amrconv efrtest frtest miscutil pcap +SUBDIR_UTILS= amrconv efrtest frtest miscutil SUBDIR_INT= dev libtest SUBDIR= ${SUBDIR_LIBPROD} ${SUBDIR_UTILS} ${SUBDIR_INT} diff -r 13d27ff5b5df -r 34f8549ff0b1 PACKAGING --- a/PACKAGING Sat Feb 18 23:24:54 2023 +0000 +++ b/PACKAGING Sun Apr 16 08:25:19 2023 +0000 @@ -25,10 +25,6 @@ classic libgsm - the latter is pre-existing software, not provided by Themyscira. -themwi-pcap-utils: the set of command line utilities built in the pcap -subdirectory, with a dependency on libpcap but no dependencies on any GSM -libraries. - With the division recommended above, the set of end-user packages will exhibit a sensible functional division from the user's perspective, and a clean and sensible dependency graph. diff -r 13d27ff5b5df -r 34f8549ff0b1 doc/RTP-BFI-extension --- a/doc/RTP-BFI-extension Sat Feb 18 23:24:54 2023 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -We (Themyscira Wireless) have invented our own non-standard extension to the -generally accepted standard for RTP-based transport of GSM FR and EFR traffic -within a GSM RAN, on stretches running from a BTS to a TRAU-like component. - -The fundamental question is: when the radio subsystem of the BTS does not have -any good traffic frame to send in a given 20 ms window, what should it do? The -generally accepted standard behavior is that no packet is sent, an intentional -gap is created in the RTP stream (the next time an RTP packet does go out, the -timestamp increments over the gap while the sequence number increments only by -1, indicating an intentional gap rather than packet loss), and apparently the -intent was/is that this gap in the RTP stream serves as the BFI (bad frame -indication). - -The problem with this generally accepted gap-as-BFI approach is that it deprives -the downstream transcoding MGW (a "soft TRAU" of sorts) of its timing source. -If the TRAU-like entity on the receiving end of the RTP stream originating from -the BTS were an RTP to TDM gateway, there would be no problem - such a gateway -would have to buffer received RTP packets in order to synchronize to fixed TDM -timing, and the absence of an RTP packet arriving in time would serve just fine -as the BFI marker, signaling BFI condition to the Rx DTX handler. But what if -the G.711 interface on the 64 kbps side of the TRAU is also an RTP stream, this -time going to a PSTN-via-SIP connectivity provider? Now the TRAU-like component -becomes a transcoding RTP forwarding MGW without any inherently fixed timing. - -If the desire is to implement a traditional TRAU in every way except for an -RTP-based implementation instead of TDM-based, i.e., if the desire is to emit a -fully continuous G.711 RTP stream from the MGW toward PSTN with comfort noise -generation and in-band DTMF insertion happening inside the MGW, rather than -emit gaps in the outgoing stream or punt CN generation (and DTMF) to VoIP -network elements, this task becomes dramatically easier if the BTS can be -forced to send an RTP packet in every 20 ms window, be it rain or shine, -conveying either a good traffic frame or a BFI marker. - -Representing BFI markers in an RTP stream -========================================= - -In the case of AMR codec, the existing standard RTP payload format already -provides an obvious way to send a BFI marker: it is the NO_DATA frame type, -i.e., FT=15 - see RFC 4867 section 4.3.2. That same section also categorizes -what we seek to do here as a "SHOULD NOT": - - Note that packets containing only NO_DATA frames SHOULD NOT be - transmitted in any payload format configuration, [...] - -However, the just-quoted directive is a SHOULD NOT rather than a MUST NOT, -and RFC 2119 states: - - SHOULD NOT This phrase, or the phrase "NOT RECOMMENDED" mean that - there may exist valid reasons in particular circumstances when the - particular behavior is acceptable or even useful, but the full - implications should be understood and the case carefully weighed - before implementing any behavior described with this label. - -Our situation is just that: in our particular circumstance (desire to implement -a traditional GSM TRAU in an RTP-to-RTP environment with no TDM network to act -as a rigid timing governor) a valid reason exists why this "SHOULD NOT" behavior -is not only acceptable, but becomes necessary. Thus in the case of AMR, we are -good - there is no need to invent our own totally non-standard extensions to -RTP payload format, it just needs to be a configurable option in the IP-based -BTS or in OsmoMGW converting from an E1-based BTS to RTP. - -The same situation holds for the rarely-used HR1 codec: RFC 5993 extends GSM-HR -RTP representation with a ToC byte modeled after the one defined for AMR in RFC -4867. Just like in AMR, GSM-HR ToC byte allows the possibility of a No_Data -frame (FT=7 for GSM-HR), with exactly the same semantics - and exactly the same -argument as above applies for sending such No_Data frames against the general -SHOULD NOT. - -But what about the older FR and EFR codecs? In the case of existing standard -RTP payload formats for FR and EFR, there is no defined way to represent a BFI -condition as distinct from any possible good traffic frame, and there lies our -challenge. - -Inventing an RTP BFI marker for FR and EFR -========================================== - -The existing code in osmo-bts-trx (but not in the osmo-bts-sysmo version of -interest to us) already contains a partial implementation of what we seek to do -here: it runs its own ECU instance in the case of a BFI from the channel -decoding layer, and if there is still no luck, there is code present to send a -BFI packet. The implemented behavior is not useful for us because RTP output -is still fully suppressed when the uplink is expected to be in DTX, and there -is a higher-level check in common/l1sap.c (l1sap_tch_ind() function) that also -suppresses RTP output, but still, the point is that someone did already write -code for sending an RTP packet intended to serve as a BFI. In the case of AMR, -that code sends out the expected NO_DATA (aka AMR_BAD) frame type - but what -about FR and EFR? - -The existing code in osmo-bts-trx sends its FR codec BFI as a valid-looking FR -frame with all 260 content bits set to 0, and it sends its EFR codec BFI as a -valid-looking EFR frame with all 244 content bits set to 0. I (Mother Mychaela) -have given consideration to using this all-zeros in-band BFI representation as -our RTP BFI marker for ThemWi, but then rejected this idea and decided to -implement our own non-standard extension to RTP payload format instead, -described further below. - -The fundamental philosophical problem which I (Mother Mychaela) have with this -in-band BFI representation is that in the world of ETSI and 3GPP standards, BFI -has always been meant to be out-of-band, not in-band. In the TRAU frame format -defined in GSM 08.60 there is an explicit control bit that carries BFI - the -condition is NOT to be derived from the 260 or 244 traffic frame bits carried -in data bit positions. Abusing one particular bit pattern within the regular -260-bit or 244-bit frame, even if it happens to be all zeros, goes against the -spirit of classic GSM and 3GPP. Per the specs, an FR codec frame of all zeros -would be a SID frame with all LAR coefficients set to 0, and standards-compliant -FR decoders would accept it as a valid SID frame, not as BFI. The situation is -likely to be even worse with EFR, where a frame of all zeros would not be -treated as SID (EFR SID code word is 95 ones instead of 95 zeros) and would -probably produce garbage at the decoder output. - -Themyscira Wireless implemented solution -======================================== - -We have invented our own non-standard extension to RTP payload format for GSM -FR and EFR codecs. Our extension is as follows: wherever a BTS needs to send a -BFI marker in the place of a traffic frame, instead of sending a 33-byte payload -beginning with 0xD nibble or a 31-byte payload beginning with 0xC nibble, it -needs to send a 2-byte payload formatted as follows: - -byte 0: 0xBF signature; -byte 1: least-significant bit encoding TAF per GSM 06.31 or GSM 06.81, - section 6.1.1 in both documents; other bits are reserved. - -In the uplink direction, with an RTP stream going from a BTS to our "soft TRAU" -MGW, our themwi-mgw recognizes these BFI packets and acts accordingly, feeding -BFI and TAF to the spec-prescribed Rx DTX handler for FR or EFR. However, if a -BTS receives these BFI marker packets in the downlink direction as a result of -TrFO (the RTP stream comes from the uplink of another GSM call), it simply -discards them without any processing - because a BTS always runs on its own TDMA -timing, there is no difference between receiving a BFI packet vs receiving no -RTP packet at all for that 20 ms frame. diff -r 13d27ff5b5df -r 34f8549ff0b1 doc/RTP-analysis --- a/doc/RTP-analysis Sat Feb 18 23:24:54 2023 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -The present package includes a number of utilities for analyzing RTP streams -that have been captured with tcpdump or equivalent tools in pcap format. In -order to use any of these utilities, you need to have a pcap file (obviously), -and you need to identify the RTP stream to be analyzed or extracted by either -source or destination IP:port. All tools begin by applying a filter, -considering only those packets that are UDP in IPv4 (no IPv6 support currently), -and only those that match the specified source or destination IP:port. Every -matched packet is checked for a valid RTP header, and then the actual RTP stream -analysis or extraction takes place, depending on the specific tool: - -rtp-cont-check This program checks the selected RTP stream for continuity. It - verifies that every matched packet has the same SSRC, that the - sequence number always increments by 1 from each individual - packet to the next, and that the RTP header timestamp always - increments by 160 units. (The assumption is that the - application at hand is in the traditional telephony domain, - with a sampling rate of 8000 samples/s and 20 ms packetization - for RTP.) This tool also looks at the capture time deltas - between successive packets and reports the observed minimum and - maximum; by seeing min and max delta-T, a developer can easily - notice timing aberrations that aren't caught by RTP header - sequence number and timestamp checks. - -rtp-g711-extr This program focuses on a single selected RTP stream like the - others, enforces its continuity just like rtp-cont-check, and - then further enforces that every RTP packet be a 160-byte - payload, presumed to be either PCMU or PCMA. (The payload type - number is NOT considered, only the payload length.) The - selected G.711 RTP stream is then extracted and written into a - raw binary file. - -rtp-gsmfr-extr This program focuses on a single selected RTP stream like the - others, enforces its continuity just like rtp-cont-check, but - then further enforces that every RTP packet be one of these 3 - possibilities: a GSM FR1 codec frame, a GSM EFR codec frame or - a Themyscira BFI marker as defined in the RTP-BFI-extension - document. (The payload type number is NOT considered, only the - payload length and the characteristic signature of each of the - 3 allowed possibilities.) The selected RTP stream is then - extracted and written into a gsmx file (see Binary-file-format), - which can then be analyzed with gsmrec-dump or decoded to - playable WAV with gsmfr-decode or gsmefr-decode. - -rtp-jitter-view This program analyzes a single selected RTP stream just like - rtp-cont-check, but instead of reporting minimum and maximum - time deltas for the entire stream, it prints the individual - capture time delta between every successive pair of packets. diff -r 13d27ff5b5df -r 34f8549ff0b1 doc/Utils-overview --- a/doc/Utils-overview Sat Feb 18 23:24:54 2023 +0000 +++ b/doc/Utils-overview Sun Apr 16 08:25:19 2023 +0000 @@ -51,8 +51,3 @@ pcm16-raw2wav See PCM-file-formats article. pcm16-wav2raw - -rtp-cont-check See RTP-analysis article. -rtp-g711-extr -rtp-gsmfr-extr -rtp-jitter-view diff -r 13d27ff5b5df -r 34f8549ff0b1 pcap/Makefile --- a/pcap/Makefile Sat Feb 18 23:24:54 2023 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -CC= gcc -CFLAGS= -O2 -PROGS= rtp-cont-check rtp-g711-extr rtp-gsmfr-extr rtp-jitter-view -INSTBIN=/opt/freecalypso/bin - -all: ${PROGS} - -rtp-cont-check: rtp-cont-check.c - ${CC} ${CFLAGS} -o $@ $@.c -lpcap - -rtp-g711-extr: rtp-g711-extr.c - ${CC} ${CFLAGS} -o $@ $@.c -lpcap - -rtp-gsmfr-extr: rtp-gsmfr-extr.c - ${CC} ${CFLAGS} -o $@ $@.c -lpcap - -rtp-jitter-view: rtp-jitter-view.c - ${CC} ${CFLAGS} -o $@ $@.c -lpcap - -install: - mkdir -p ${INSTBIN} - install -c ${PROGS} ${INSTBIN} - -clean: - rm -f *.o *.out ${PROGS} diff -r 13d27ff5b5df -r 34f8549ff0b1 pcap/rtp-cont-check.c --- a/pcap/rtp-cont-check.c Sat Feb 18 23:24:54 2023 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,234 +0,0 @@ -/* - * This program reads a pcap file, extracts packets belonging to a - * particular RTP stream as identified by a source or destination - * IP:port, and checks its continuity: verifies that the sequence - * number always increments by 1 and that the timestamp always - * increments by 160 units. Finally, this program prints out - * the minimum and maximum observed capture time deltas between - * successive packets of the stream. - * - * The codec can be anything, and this program can also be used to - * check continuity of RTP streams coming from PSTN/SIP, but the - * core assumption is that packets must arrive every 20 ms, with - * the timestamp field incrementing by 160 units each time. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static pcap_t *pcap; -static in_addr_t match_ip_addr; -static u_short match_udp_port; -static unsigned iphdr_addr_offset, udphdr_port_offset; -static unsigned link_hdr_len, ethertype_offset; -static int stream_init_flag, deltat_init_flag; -static unsigned last_seq, last_tstamp, stream_ssrc; -static struct timeval last_pkt_time; -static unsigned deltat_min, deltat_max; - -static void -check_dl_type() -{ - int dltype; - - dltype = pcap_datalink(pcap); - switch (dltype) { - case DLT_EN10MB: - link_hdr_len = 14; - ethertype_offset = 12; - break; - case DLT_RAW: - link_hdr_len = 0; - break; - case DLT_LINUX_SLL: - link_hdr_len = 16; - ethertype_offset = 14; - break; - default: - fprintf(stderr, "error: unsupported data link type %d\n", - dltype); - exit(1); - } -} - -static void -rtp_stream_logic(rtp_hdr, pkt_idx, pkt_time) - u_char *rtp_hdr; - unsigned pkt_idx; - struct timeval *pkt_time; -{ - unsigned cur_seq, cur_tstamp, cur_ssrc; - struct timeval deltat; - - cur_seq = (rtp_hdr[2] << 8) | rtp_hdr[3]; - cur_tstamp = (rtp_hdr[4] << 24) | (rtp_hdr[5] << 16) | - (rtp_hdr[6] << 8) | rtp_hdr[7]; - cur_ssrc = (rtp_hdr[8] << 24) | (rtp_hdr[9] << 16) | - (rtp_hdr[10] << 8) | rtp_hdr[11]; - if (stream_init_flag) { - if (cur_ssrc != stream_ssrc) { - fprintf(stderr, - "error in packet #%u: SSRC change from 0x%08X to 0x%08X\n", - pkt_idx, stream_ssrc, cur_ssrc); - exit(1); - } - if (cur_seq != last_seq + 1 && - (cur_seq != 0 || last_seq != 0xFFFF)) { - fprintf(stderr, - "error in packet #%u: seq break from 0x%04X to 0x%04X\n", - pkt_idx, last_seq, cur_seq); - exit(1); - } - if (cur_tstamp != last_tstamp + 160) { - fprintf(stderr, - "error in packet #%u: timestamp break from 0x%08X to 0x%08X\n", - pkt_idx, last_tstamp, cur_tstamp); - exit(1); - } - if (timercmp(pkt_time, &last_pkt_time, <)) { - fprintf(stderr, - "packet #%u timing error: Rx time goes backward\n", - pkt_idx); - exit(1); - } - timersub(pkt_time, &last_pkt_time, &deltat); - if (deltat.tv_sec) { - fprintf(stderr, - "packet #%u timing error: Rx time gap >= 1 s\n", - pkt_idx); - exit(1); - } - if (deltat_init_flag) { - if (deltat.tv_usec < deltat_min) - deltat_min = deltat.tv_usec; - if (deltat.tv_usec > deltat_max) - deltat_max = deltat.tv_usec; - } else { - deltat_min = deltat.tv_usec; - deltat_max = deltat.tv_usec; - deltat_init_flag = 1; - } - } else { - stream_init_flag = 1; - stream_ssrc = cur_ssrc; - } - last_seq = cur_seq; - last_tstamp = cur_tstamp; - bcopy(pkt_time, &last_pkt_time, sizeof(struct timeval)); -} - -static void -process_packet(pkt, caplen, pkt_idx, pkt_time) - u_char *pkt; - unsigned caplen, pkt_idx; - struct timeval *pkt_time; -{ - unsigned udplen; - - if (caplen < link_hdr_len + 28) - return; - if (link_hdr_len) { - if (pkt[ethertype_offset] != 0x08) - return; - if (pkt[ethertype_offset+1] != 0x00) - return; - pkt += link_hdr_len; - caplen -= link_hdr_len; - } - /* check IP header */ - if (pkt[0] != 0x45) - return; - if (pkt[9] != 17) /* UDP */ - return; - if (bcmp(pkt + iphdr_addr_offset, &match_ip_addr, 4)) - return; - /* check UDP header */ - if (bcmp(pkt + 20 + udphdr_port_offset, &match_udp_port, 2)) - return; - /* it is our target - now scrutinize it */ - udplen = (pkt[24] << 8) | pkt[25]; - if (caplen < udplen + 20) { - fprintf(stderr, - "error: packet #%u is truncated in the capture\n", - pkt_idx); - exit(1); - } - if (udplen < 20) { - fprintf(stderr, - "error in packet #%u: UDP length is too short for RTP header\n", - pkt_idx); - exit(1); - } - if (pkt[28] != 0x80) { - fprintf(stderr, - "error in packet #%u: unsupported RTP header structure\n", - pkt_idx); - exit(1); - } - rtp_stream_logic(pkt + 28, pkt_idx, pkt_time); -} - -main(argc, argv) - char **argv; -{ - char errbuf[PCAP_ERRBUF_SIZE]; - u_char *pkt; - struct pcap_pkthdr pkthdr; - unsigned pkt_idx; - - if (argc != 5) { - fprintf(stderr, - "usage: %s pcap-file src|dest ip-addr udp-port\n", - argv[0]); - exit(1); - } - pcap = pcap_open_offline(argv[1], errbuf); - if (!pcap) { - fprintf(stderr, "%s: %s\n", argv[1], errbuf); - exit(1); - } - check_dl_type(); - if (!strcmp(argv[2], "src")) { - iphdr_addr_offset = 12; - udphdr_port_offset = 0; - } else if (!strcmp(argv[2], "dest")) { - iphdr_addr_offset = 16; - udphdr_port_offset = 2; - } else { - fprintf(stderr, - "error: direction argument must be \"src\" or \"dest\"\n"); - exit(1); - } - match_ip_addr = inet_addr(argv[3]); - if (match_ip_addr == INADDR_NONE) { - fprintf(stderr, "error: IP address argument is invalid\n"); - exit(1); - } - match_udp_port = htons(strtoul(argv[4], 0, 0)); - for (pkt_idx = 0; ; pkt_idx++) { - pkt = pcap_next(pcap, &pkthdr); - if (!pkt) - break; - process_packet(pkt, (unsigned) pkthdr.caplen, pkt_idx, - &pkthdr.ts); - } - if (!stream_init_flag) { - fprintf(stderr, "error: specified RTP stream not found\n"); - exit(1); - } - if (!deltat_init_flag) { - fprintf(stderr, "error: found only one matching packet\n"); - exit(1); - } - printf("Minimum time delta: %u us\n", deltat_min); - printf("Maximum time delta: %u us\n", deltat_max); - exit(0); -} diff -r 13d27ff5b5df -r 34f8549ff0b1 pcap/rtp-g711-extr.c --- a/pcap/rtp-g711-extr.c Sat Feb 18 23:24:54 2023 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +0,0 @@ -/* - * This program reads a pcap file, extracts packets belonging to a - * particular RTP stream as identified by a source or destination - * IP:port, and verifies that an unbroken RTP stream is present, - * with 160-byte payloads corresponding to timestamp increments - * of 160 units per packet, as expected for a G.711 (PCMU or PCMA) - * RTP stream with 20 ms packetization. The extracted G.711 stream - * is written to a raw binary file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static pcap_t *pcap; -static in_addr_t match_ip_addr; -static u_short match_udp_port; -static unsigned iphdr_addr_offset, udphdr_port_offset; -static unsigned link_hdr_len, ethertype_offset; -static FILE *outfile; -static int stream_init_flag; -static unsigned last_seq, last_tstamp, stream_ssrc; - -static void -check_dl_type() -{ - int dltype; - - dltype = pcap_datalink(pcap); - switch (dltype) { - case DLT_EN10MB: - link_hdr_len = 14; - ethertype_offset = 12; - break; - case DLT_RAW: - link_hdr_len = 0; - break; - case DLT_LINUX_SLL: - link_hdr_len = 16; - ethertype_offset = 14; - break; - default: - fprintf(stderr, "error: unsupported data link type %d\n", - dltype); - exit(1); - } -} - -static void -rtp_stream_logic(rtp_hdr, pkt_idx) - u_char *rtp_hdr; - unsigned pkt_idx; -{ - unsigned cur_seq, cur_tstamp, cur_ssrc; - - cur_seq = (rtp_hdr[2] << 8) | rtp_hdr[3]; - cur_tstamp = (rtp_hdr[4] << 24) | (rtp_hdr[5] << 16) | - (rtp_hdr[6] << 8) | rtp_hdr[7]; - cur_ssrc = (rtp_hdr[8] << 24) | (rtp_hdr[9] << 16) | - (rtp_hdr[10] << 8) | rtp_hdr[11]; - if (stream_init_flag) { - if (cur_ssrc != stream_ssrc) { - fprintf(stderr, - "error in packet #%u: SSRC change from 0x%08X to 0x%08X\n", - pkt_idx, stream_ssrc, cur_ssrc); - exit(1); - } - if (cur_seq != last_seq + 1 && - (cur_seq != 0 || last_seq != 0xFFFF)) { - fprintf(stderr, - "error in packet #%u: seq break from 0x%04X to 0x%04X\n", - pkt_idx, last_seq, cur_seq); - exit(1); - } - if (cur_tstamp != last_tstamp + 160) { - fprintf(stderr, - "error in packet #%u: timestamp break from 0x%08X to 0x%08X\n", - pkt_idx, last_tstamp, cur_tstamp); - exit(1); - } - } else { - stream_init_flag = 1; - stream_ssrc = cur_ssrc; - } - last_seq = cur_seq; - last_tstamp = cur_tstamp; -} - -static void -process_packet(pkt, caplen, pkt_idx) - u_char *pkt; - unsigned caplen, pkt_idx; -{ - unsigned udplen, payload_len; - - if (caplen < link_hdr_len + 28) - return; - if (link_hdr_len) { - if (pkt[ethertype_offset] != 0x08) - return; - if (pkt[ethertype_offset+1] != 0x00) - return; - pkt += link_hdr_len; - caplen -= link_hdr_len; - } - /* check IP header */ - if (pkt[0] != 0x45) - return; - if (pkt[9] != 17) /* UDP */ - return; - if (bcmp(pkt + iphdr_addr_offset, &match_ip_addr, 4)) - return; - /* check UDP header */ - if (bcmp(pkt + 20 + udphdr_port_offset, &match_udp_port, 2)) - return; - /* it is our target - now scrutinize it */ - udplen = (pkt[24] << 8) | pkt[25]; - if (caplen < udplen + 20) { - fprintf(stderr, - "error: packet #%u is truncated in the capture\n", - pkt_idx); - exit(1); - } - if (udplen < 20) { - fprintf(stderr, - "error in packet #%u: UDP length is too short for RTP header\n", - pkt_idx); - exit(1); - } - if (pkt[28] != 0x80) { - fprintf(stderr, - "error in packet #%u: unsupported RTP header structure\n", - pkt_idx); - exit(1); - } - rtp_stream_logic(pkt + 28, pkt_idx); - payload_len = udplen - 20; - if (payload_len != 160) { - fprintf(stderr, "error in packet #%u: unsupported payload\n", - pkt_idx); - exit(1); - } - fwrite(pkt + 40, 1, payload_len, outfile); -} - -main(argc, argv) - char **argv; -{ - char errbuf[PCAP_ERRBUF_SIZE]; - u_char *pkt; - struct pcap_pkthdr pkthdr; - unsigned pkt_idx; - - if (argc != 6) { - fprintf(stderr, - "usage: %s pcap-file src|dest ip-addr udp-port outfile\n", - argv[0]); - exit(1); - } - pcap = pcap_open_offline(argv[1], errbuf); - if (!pcap) { - fprintf(stderr, "%s: %s\n", argv[1], errbuf); - exit(1); - } - check_dl_type(); - if (!strcmp(argv[2], "src")) { - iphdr_addr_offset = 12; - udphdr_port_offset = 0; - } else if (!strcmp(argv[2], "dest")) { - iphdr_addr_offset = 16; - udphdr_port_offset = 2; - } else { - fprintf(stderr, - "error: direction argument must be \"src\" or \"dest\"\n"); - exit(1); - } - match_ip_addr = inet_addr(argv[3]); - if (match_ip_addr == INADDR_NONE) { - fprintf(stderr, "error: IP address argument is invalid\n"); - exit(1); - } - match_udp_port = htons(strtoul(argv[4], 0, 0)); - outfile = fopen(argv[5], "w"); - if (!outfile) { - perror(argv[5]); - exit(1); - } - for (pkt_idx = 0; ; pkt_idx++) { - pkt = pcap_next(pcap, &pkthdr); - if (!pkt) - break; - process_packet(pkt, (unsigned) pkthdr.caplen, pkt_idx); - } - if (!stream_init_flag) { - fprintf(stderr, "error: specified RTP stream not found\n"); - exit(1); - } - fclose(outfile); - exit(0); -} diff -r 13d27ff5b5df -r 34f8549ff0b1 pcap/rtp-gsmfr-extr.c --- a/pcap/rtp-gsmfr-extr.c Sat Feb 18 23:24:54 2023 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,219 +0,0 @@ -/* - * This program reads a pcap file, extracts packets belonging to a - * particular RTP stream as identified by a source or destination - * IP:port, verifies that an unbroken RTP stream is present, in - * GSM FR or EFR codec, and writes the FR/EFR frame stream to our - * extended-libgsm format binary file, to be fed to decoding test - * programs. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static pcap_t *pcap; -static in_addr_t match_ip_addr; -static u_short match_udp_port; -static unsigned iphdr_addr_offset, udphdr_port_offset; -static unsigned link_hdr_len, ethertype_offset; -static FILE *outfile; -static int stream_init_flag; -static unsigned last_seq, last_tstamp, stream_ssrc; - -static void -check_dl_type() -{ - int dltype; - - dltype = pcap_datalink(pcap); - switch (dltype) { - case DLT_EN10MB: - link_hdr_len = 14; - ethertype_offset = 12; - break; - case DLT_RAW: - link_hdr_len = 0; - break; - case DLT_LINUX_SLL: - link_hdr_len = 16; - ethertype_offset = 14; - break; - default: - fprintf(stderr, "error: unsupported data link type %d\n", - dltype); - exit(1); - } -} - -static void -rtp_stream_logic(rtp_hdr, pkt_idx) - u_char *rtp_hdr; - unsigned pkt_idx; -{ - unsigned cur_seq, cur_tstamp, cur_ssrc; - - cur_seq = (rtp_hdr[2] << 8) | rtp_hdr[3]; - cur_tstamp = (rtp_hdr[4] << 24) | (rtp_hdr[5] << 16) | - (rtp_hdr[6] << 8) | rtp_hdr[7]; - cur_ssrc = (rtp_hdr[8] << 24) | (rtp_hdr[9] << 16) | - (rtp_hdr[10] << 8) | rtp_hdr[11]; - if (stream_init_flag) { - if (cur_ssrc != stream_ssrc) { - fprintf(stderr, - "error in packet #%u: SSRC change from 0x%08X to 0x%08X\n", - pkt_idx, stream_ssrc, cur_ssrc); - exit(1); - } - if (cur_seq != last_seq + 1 && - (cur_seq != 0 || last_seq != 0xFFFF)) { - fprintf(stderr, - "error in packet #%u: seq break from 0x%04X to 0x%04X\n", - pkt_idx, last_seq, cur_seq); - exit(1); - } - if (cur_tstamp != last_tstamp + 160) { - fprintf(stderr, - "error in packet #%u: timestamp break from 0x%08X to 0x%08X\n", - pkt_idx, last_tstamp, cur_tstamp); - exit(1); - } - } else { - stream_init_flag = 1; - stream_ssrc = cur_ssrc; - } - last_seq = cur_seq; - last_tstamp = cur_tstamp; -} - -static void -process_packet(pkt, caplen, pkt_idx) - u_char *pkt; - unsigned caplen, pkt_idx; -{ - unsigned udplen, payload_len; - - if (caplen < link_hdr_len + 28) - return; - if (link_hdr_len) { - if (pkt[ethertype_offset] != 0x08) - return; - if (pkt[ethertype_offset+1] != 0x00) - return; - pkt += link_hdr_len; - caplen -= link_hdr_len; - } - /* check IP header */ - if (pkt[0] != 0x45) - return; - if (pkt[9] != 17) /* UDP */ - return; - if (bcmp(pkt + iphdr_addr_offset, &match_ip_addr, 4)) - return; - /* check UDP header */ - if (bcmp(pkt + 20 + udphdr_port_offset, &match_udp_port, 2)) - return; - /* it is our target - now scrutinize it */ - udplen = (pkt[24] << 8) | pkt[25]; - if (caplen < udplen + 20) { - fprintf(stderr, - "error: packet #%u is truncated in the capture\n", - pkt_idx); - exit(1); - } - if (udplen < 20) { - fprintf(stderr, - "error in packet #%u: UDP length is too short for RTP header\n", - pkt_idx); - exit(1); - } - if (pkt[28] != 0x80) { - fprintf(stderr, - "error in packet #%u: unsupported RTP header structure\n", - pkt_idx); - exit(1); - } - rtp_stream_logic(pkt + 28, pkt_idx); - payload_len = udplen - 20; - switch (payload_len) { - case 2: - if (pkt[40] == 0xBF) - break; - goto bad_payload; - case 31: - if ((pkt[40] & 0xF0) == 0xC0) - break; - goto bad_payload; - case 33: - if ((pkt[40] & 0xF0) == 0xD0) - break; - /* FALL THRU */ - default: - bad_payload: - fprintf(stderr, "error in packet #%u: unsupported payload\n", - pkt_idx); - exit(1); - } - fwrite(pkt + 40, 1, payload_len, outfile); -} - -main(argc, argv) - char **argv; -{ - char errbuf[PCAP_ERRBUF_SIZE]; - u_char *pkt; - struct pcap_pkthdr pkthdr; - unsigned pkt_idx; - - if (argc != 6) { - fprintf(stderr, - "usage: %s pcap-file src|dest ip-addr udp-port outfile\n", - argv[0]); - exit(1); - } - pcap = pcap_open_offline(argv[1], errbuf); - if (!pcap) { - fprintf(stderr, "%s: %s\n", argv[1], errbuf); - exit(1); - } - check_dl_type(); - if (!strcmp(argv[2], "src")) { - iphdr_addr_offset = 12; - udphdr_port_offset = 0; - } else if (!strcmp(argv[2], "dest")) { - iphdr_addr_offset = 16; - udphdr_port_offset = 2; - } else { - fprintf(stderr, - "error: direction argument must be \"src\" or \"dest\"\n"); - exit(1); - } - match_ip_addr = inet_addr(argv[3]); - if (match_ip_addr == INADDR_NONE) { - fprintf(stderr, "error: IP address argument is invalid\n"); - exit(1); - } - match_udp_port = htons(strtoul(argv[4], 0, 0)); - outfile = fopen(argv[5], "w"); - if (!outfile) { - perror(argv[5]); - exit(1); - } - for (pkt_idx = 0; ; pkt_idx++) { - pkt = pcap_next(pcap, &pkthdr); - if (!pkt) - break; - process_packet(pkt, (unsigned) pkthdr.caplen, pkt_idx); - } - if (!stream_init_flag) { - fprintf(stderr, "error: specified RTP stream not found\n"); - exit(1); - } - fclose(outfile); - exit(0); -} diff -r 13d27ff5b5df -r 34f8549ff0b1 pcap/rtp-jitter-view.c --- a/pcap/rtp-jitter-view.c Sat Feb 18 23:24:54 2023 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,220 +0,0 @@ -/* - * This program reads a pcap file, extracts packets belonging to a - * particular RTP stream as identified by a source or destination - * IP:port, checks its continuity at the packet header level - * (verifies that the sequence number always increments by 1 and - * that the timestamp always increments by 160 units) and prints out - * the Rx time delta of each stream packet (the capture time of - * the current packet minus the capture time of the previous packet) - * on stdout, allowing visual analysis of timing jitter. - * - * The codec can be anything, and this program can also be used to - * examine the jitter of RTP streams coming from PSTN/SIP, but the - * core assumption is that packets must arrive every 20 ms, with - * the timestamp field incrementing by 160 units each time. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static pcap_t *pcap; -static in_addr_t match_ip_addr; -static u_short match_udp_port; -static unsigned iphdr_addr_offset, udphdr_port_offset; -static unsigned link_hdr_len, ethertype_offset; -static int stream_init_flag; -static unsigned last_seq, last_tstamp, stream_ssrc; -static struct timeval last_pkt_time; - -static void -check_dl_type() -{ - int dltype; - - dltype = pcap_datalink(pcap); - switch (dltype) { - case DLT_EN10MB: - link_hdr_len = 14; - ethertype_offset = 12; - break; - case DLT_RAW: - link_hdr_len = 0; - break; - case DLT_LINUX_SLL: - link_hdr_len = 16; - ethertype_offset = 14; - break; - default: - fprintf(stderr, "error: unsupported data link type %d\n", - dltype); - exit(1); - } -} - -static void -rtp_stream_logic(rtp_hdr, pkt_idx, pkt_time) - u_char *rtp_hdr; - unsigned pkt_idx; - struct timeval *pkt_time; -{ - unsigned cur_seq, cur_tstamp, cur_ssrc; - struct timeval deltat; - - cur_seq = (rtp_hdr[2] << 8) | rtp_hdr[3]; - cur_tstamp = (rtp_hdr[4] << 24) | (rtp_hdr[5] << 16) | - (rtp_hdr[6] << 8) | rtp_hdr[7]; - cur_ssrc = (rtp_hdr[8] << 24) | (rtp_hdr[9] << 16) | - (rtp_hdr[10] << 8) | rtp_hdr[11]; - if (stream_init_flag) { - if (cur_ssrc != stream_ssrc) { - fprintf(stderr, - "error in packet #%u: SSRC change from 0x%08X to 0x%08X\n", - pkt_idx, stream_ssrc, cur_ssrc); - exit(1); - } - if (cur_seq != last_seq + 1 && - (cur_seq != 0 || last_seq != 0xFFFF)) { - fprintf(stderr, - "error in packet #%u: seq break from 0x%04X to 0x%04X\n", - pkt_idx, last_seq, cur_seq); - exit(1); - } - if (cur_tstamp != last_tstamp + 160) { - fprintf(stderr, - "error in packet #%u: timestamp break from 0x%08X to 0x%08X\n", - pkt_idx, last_tstamp, cur_tstamp); - exit(1); - } - if (timercmp(pkt_time, &last_pkt_time, <)) { - fprintf(stderr, - "packet #%u timing error: Rx time goes backward\n", - pkt_idx); - exit(1); - } - timersub(pkt_time, &last_pkt_time, &deltat); - if (deltat.tv_sec) { - fprintf(stderr, - "packet #%u timing error: Rx time gap >= 1 s\n", - pkt_idx); - exit(1); - } - printf("Packet #%u: time delta %u us\n", pkt_idx, - (unsigned) deltat.tv_usec); - } else { - stream_init_flag = 1; - stream_ssrc = cur_ssrc; - } - last_seq = cur_seq; - last_tstamp = cur_tstamp; - bcopy(pkt_time, &last_pkt_time, sizeof(struct timeval)); -} - -static void -process_packet(pkt, caplen, pkt_idx, pkt_time) - u_char *pkt; - unsigned caplen, pkt_idx; - struct timeval *pkt_time; -{ - unsigned udplen; - - if (caplen < link_hdr_len + 28) - return; - if (link_hdr_len) { - if (pkt[ethertype_offset] != 0x08) - return; - if (pkt[ethertype_offset+1] != 0x00) - return; - pkt += link_hdr_len; - caplen -= link_hdr_len; - } - /* check IP header */ - if (pkt[0] != 0x45) - return; - if (pkt[9] != 17) /* UDP */ - return; - if (bcmp(pkt + iphdr_addr_offset, &match_ip_addr, 4)) - return; - /* check UDP header */ - if (bcmp(pkt + 20 + udphdr_port_offset, &match_udp_port, 2)) - return; - /* it is our target - now scrutinize it */ - udplen = (pkt[24] << 8) | pkt[25]; - if (caplen < udplen + 20) { - fprintf(stderr, - "error: packet #%u is truncated in the capture\n", - pkt_idx); - exit(1); - } - if (udplen < 20) { - fprintf(stderr, - "error in packet #%u: UDP length is too short for RTP header\n", - pkt_idx); - exit(1); - } - if (pkt[28] != 0x80) { - fprintf(stderr, - "error in packet #%u: unsupported RTP header structure\n", - pkt_idx); - exit(1); - } - rtp_stream_logic(pkt + 28, pkt_idx, pkt_time); -} - -main(argc, argv) - char **argv; -{ - char errbuf[PCAP_ERRBUF_SIZE]; - u_char *pkt; - struct pcap_pkthdr pkthdr; - unsigned pkt_idx; - - if (argc != 5) { - fprintf(stderr, - "usage: %s pcap-file src|dest ip-addr udp-port\n", - argv[0]); - exit(1); - } - pcap = pcap_open_offline(argv[1], errbuf); - if (!pcap) { - fprintf(stderr, "%s: %s\n", argv[1], errbuf); - exit(1); - } - check_dl_type(); - if (!strcmp(argv[2], "src")) { - iphdr_addr_offset = 12; - udphdr_port_offset = 0; - } else if (!strcmp(argv[2], "dest")) { - iphdr_addr_offset = 16; - udphdr_port_offset = 2; - } else { - fprintf(stderr, - "error: direction argument must be \"src\" or \"dest\"\n"); - exit(1); - } - match_ip_addr = inet_addr(argv[3]); - if (match_ip_addr == INADDR_NONE) { - fprintf(stderr, "error: IP address argument is invalid\n"); - exit(1); - } - match_udp_port = htons(strtoul(argv[4], 0, 0)); - for (pkt_idx = 0; ; pkt_idx++) { - pkt = pcap_next(pcap, &pkthdr); - if (!pkt) - break; - process_packet(pkt, (unsigned) pkthdr.caplen, pkt_idx, - &pkthdr.ts); - } - if (!stream_init_flag) { - fprintf(stderr, "error: specified RTP stream not found\n"); - exit(1); - } - exit(0); -}