FreeCalypso > hg > rtp-debug-utils
comparison pcap-study/rtp-tfo-trace.c @ 10:e686bc92c7d8
revamp for new subdir structure and configure script
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Wed, 15 May 2024 01:44:46 +0000 |
parents | rtp-tfo-trace.c@41eba040785a |
children |
comparison
equal
deleted
inserted
replaced
9:c00510e1ae8b | 10:e686bc92c7d8 |
---|---|
1 /* | |
2 * This program reads a pcap file containing RTP packets of a PSTN call | |
3 * (PCMU or PCMA, 160 samples per RTP packet), flowing in one or both | |
4 * directions, and looks for TFO IS messages. | |
5 */ | |
6 | |
7 #include <sys/types.h> | |
8 #include <sys/socket.h> | |
9 #include <netinet/in.h> | |
10 #include <arpa/inet.h> | |
11 #include <stdio.h> | |
12 #include <stdlib.h> | |
13 #include <string.h> | |
14 #include <strings.h> | |
15 #include <pcap/pcap.h> | |
16 | |
17 static pcap_t *pcap; | |
18 static in_addr_t match_ip_addr; | |
19 static u_short match_udp_port; | |
20 static unsigned link_hdr_len, ethertype_offset; | |
21 | |
22 static struct onedir { | |
23 int init_flag; | |
24 unsigned last_seq; | |
25 unsigned last_tstamp; | |
26 unsigned stream_ssrc; | |
27 u_char is_hunt_buf[320]; | |
28 int is_state; | |
29 unsigned is_hunt_fill; | |
30 unsigned is_offset; | |
31 unsigned is_alignment; | |
32 unsigned is_bit_count; | |
33 unsigned is_rx_word; | |
34 } rx_state, tx_state; | |
35 | |
36 static const u_char hdr_pattern[20] = {0, 1, 0, 1, 0, 1, 1, 0, 1, 0, | |
37 0, 1, 1, 0, 1, 0, 1, 0, 0, 1}; | |
38 | |
39 static void | |
40 check_dl_type() | |
41 { | |
42 int dltype; | |
43 | |
44 dltype = pcap_datalink(pcap); | |
45 switch (dltype) { | |
46 case DLT_EN10MB: | |
47 link_hdr_len = 14; | |
48 ethertype_offset = 12; | |
49 break; | |
50 case DLT_RAW: | |
51 link_hdr_len = 0; | |
52 break; | |
53 case DLT_LINUX_SLL: | |
54 link_hdr_len = 16; | |
55 ethertype_offset = 14; | |
56 break; | |
57 default: | |
58 fprintf(stderr, "error: unsupported data link type %d\n", | |
59 dltype); | |
60 exit(1); | |
61 } | |
62 } | |
63 | |
64 static void | |
65 rtp_stream_logic(rtp_hdr, pkt_idx, st, dir_str) | |
66 u_char *rtp_hdr; | |
67 unsigned pkt_idx; | |
68 struct onedir *st; | |
69 char *dir_str; | |
70 { | |
71 unsigned cur_seq, cur_tstamp, cur_ssrc; | |
72 | |
73 cur_seq = (rtp_hdr[2] << 8) | rtp_hdr[3]; | |
74 cur_tstamp = (rtp_hdr[4] << 24) | (rtp_hdr[5] << 16) | | |
75 (rtp_hdr[6] << 8) | rtp_hdr[7]; | |
76 cur_ssrc = (rtp_hdr[8] << 24) | (rtp_hdr[9] << 16) | | |
77 (rtp_hdr[10] << 8) | rtp_hdr[11]; | |
78 if (st->init_flag) { | |
79 if (cur_ssrc != st->stream_ssrc) { | |
80 printf( | |
81 "error in %s packet #%u: SSRC change from 0x%08X to 0x%08X\n", | |
82 dir_str, pkt_idx, st->stream_ssrc, cur_ssrc); | |
83 } else if (cur_seq != st->last_seq + 1 && | |
84 (cur_seq != 0 || st->last_seq != 0xFFFF)) { | |
85 printf( | |
86 "error in %s packet #%u: seq break from 0x%04X to 0x%04X\n", | |
87 dir_str, pkt_idx, st->last_seq, cur_seq); | |
88 } else if (cur_tstamp != st->last_tstamp + 160) { | |
89 printf( | |
90 "error in %s packet #%u: timestamp break from 0x%08X to 0x%08X\n", | |
91 dir_str, pkt_idx, st->last_tstamp, cur_tstamp); | |
92 } | |
93 } else | |
94 st->init_flag = 1; | |
95 st->last_seq = cur_seq; | |
96 st->last_tstamp = cur_tstamp; | |
97 st->stream_ssrc = cur_ssrc; | |
98 } | |
99 | |
100 static void | |
101 is_rx_hunt(input_pos, pkt_idx, st, dir_str) | |
102 unsigned input_pos; | |
103 unsigned pkt_idx; | |
104 struct onedir *st; | |
105 char *dir_str; | |
106 { | |
107 unsigned offset, n; | |
108 | |
109 for (offset = 0; offset < 16; offset++) { | |
110 for (n = 0; n < 20; n++) | |
111 if ((st->is_hunt_buf[offset + n*16] & 1) != | |
112 hdr_pattern[n]) | |
113 break; | |
114 if (n == 20) | |
115 break; | |
116 } | |
117 if (n != 20) | |
118 return; | |
119 st->is_offset = offset; | |
120 st->is_alignment = input_pos * 16 + offset; | |
121 st->is_state = 1; | |
122 st->is_bit_count = 0; | |
123 st->is_rx_word = 0; | |
124 st->is_hunt_fill = 0; | |
125 } | |
126 | |
127 static void | |
128 is_process_cmd(pkt_idx, st, dir_str) | |
129 unsigned pkt_idx; | |
130 struct onedir *st; | |
131 char *dir_str; | |
132 { | |
133 int cont; | |
134 | |
135 printf("#%u: %s (align %u) ", pkt_idx, dir_str, st->is_alignment); | |
136 switch (st->is_rx_word) { | |
137 case 0x05D: | |
138 printf("IS_REQ\n"); | |
139 cont = 1; | |
140 break; | |
141 case 0x0BA: | |
142 printf("IS_ACK\n"); | |
143 cont = 1; | |
144 break; | |
145 case 0x0E7: | |
146 printf("IS_IPE\n"); | |
147 cont = 1; | |
148 break; | |
149 case 0x129: | |
150 printf("IS_FILL\n"); | |
151 cont = 0; | |
152 break; | |
153 case 0x174: | |
154 printf("IS_DUP\n"); | |
155 cont = 0; | |
156 break; | |
157 case 0x193: | |
158 printf("IS_SYL\n"); | |
159 cont = 0; | |
160 break; | |
161 default: | |
162 printf("Unknown IS_Command 0x%03X\n", st->is_rx_word); | |
163 cont = 0; | |
164 } | |
165 if (cont) { | |
166 st->is_state = 2; | |
167 st->is_bit_count = 0; | |
168 st->is_rx_word = 0; | |
169 } else | |
170 st->is_state = 0; | |
171 } | |
172 | |
173 static void | |
174 is_process_ext(pkt_idx, st, dir_str) | |
175 unsigned pkt_idx; | |
176 struct onedir *st; | |
177 char *dir_str; | |
178 { | |
179 printf("#%u: %s IS_Extension: 0x%05X", pkt_idx, dir_str, | |
180 st->is_rx_word); | |
181 if (st->is_rx_word & 0x80200) { | |
182 printf(" (bad sync)\n"); | |
183 st->is_state = 0; | |
184 return; | |
185 } | |
186 switch (st->is_rx_word & 3) { | |
187 case 0: | |
188 printf(" (final)\n"); | |
189 st->is_state = 0; | |
190 return; | |
191 case 3: | |
192 printf(" (continue)\n"); | |
193 st->is_state = 2; | |
194 st->is_bit_count = 0; | |
195 st->is_rx_word = 0; | |
196 return; | |
197 default: | |
198 printf(" (bad EX)\n"); | |
199 st->is_state = 0; | |
200 } | |
201 } | |
202 | |
203 static void | |
204 is_rx_process(input, input_pos, pkt_idx, st, dir_str) | |
205 uint8_t *input; | |
206 unsigned input_pos; | |
207 unsigned pkt_idx; | |
208 struct onedir *st; | |
209 char *dir_str; | |
210 { | |
211 unsigned new_bit; | |
212 | |
213 memmove(st->is_hunt_buf, st->is_hunt_buf + 16, 304); | |
214 memcpy(st->is_hunt_buf + 304, input, 16); | |
215 if (!st->is_state) { | |
216 if (st->is_hunt_fill < 20) | |
217 st->is_hunt_fill++; | |
218 if (st->is_hunt_fill == 20) | |
219 is_rx_hunt(input_pos, pkt_idx, st, dir_str); | |
220 return; | |
221 } | |
222 new_bit = input[st->is_offset] & 1; | |
223 st->is_rx_word <<= 1; | |
224 st->is_rx_word |= new_bit; | |
225 st->is_bit_count++; | |
226 if (st->is_state == 1 && st->is_bit_count == 10) | |
227 is_process_cmd(pkt_idx, st, dir_str); | |
228 else if (st->is_state == 2 && st->is_bit_count == 20) | |
229 is_process_ext(pkt_idx, st, dir_str); | |
230 } | |
231 | |
232 static void | |
233 process_packet_onedir(pkt, caplen, pkt_idx, st, dir_str) | |
234 u_char *pkt; | |
235 unsigned caplen, pkt_idx; | |
236 struct onedir *st; | |
237 char *dir_str; | |
238 { | |
239 unsigned udplen, payload_len; | |
240 unsigned is_chunk; | |
241 | |
242 udplen = (pkt[24] << 8) | pkt[25]; | |
243 if (caplen < udplen + 20) { | |
244 printf("error: %s packet #%u is truncated in the capture\n", | |
245 dir_str, pkt_idx); | |
246 return; | |
247 } | |
248 if (udplen < 20) { | |
249 printf( | |
250 "error in %s packet #%u: UDP length is too short for RTP header\n", | |
251 dir_str, pkt_idx); | |
252 return; | |
253 } | |
254 if (pkt[28] != 0x80) { | |
255 printf( | |
256 "error in %s packet #%u: unsupported RTP header structure\n", | |
257 dir_str, pkt_idx); | |
258 return; | |
259 } | |
260 rtp_stream_logic(pkt + 28, pkt_idx, st, dir_str); | |
261 payload_len = udplen - 20; | |
262 if (payload_len != 160) { | |
263 printf("error in %s packet #%u: wrong payload length\n", | |
264 dir_str, pkt_idx); | |
265 return; | |
266 } | |
267 for (is_chunk = 0; is_chunk < 10; is_chunk++) | |
268 is_rx_process(pkt + 40 + is_chunk * 16, is_chunk, pkt_idx, st, | |
269 dir_str); | |
270 } | |
271 | |
272 static void | |
273 process_packet(pkt, caplen, pkt_idx) | |
274 u_char *pkt; | |
275 unsigned caplen, pkt_idx; | |
276 { | |
277 if (caplen < link_hdr_len + 28) | |
278 return; | |
279 if (link_hdr_len) { | |
280 if (pkt[ethertype_offset] != 0x08) | |
281 return; | |
282 if (pkt[ethertype_offset+1] != 0x00) | |
283 return; | |
284 pkt += link_hdr_len; | |
285 caplen -= link_hdr_len; | |
286 } | |
287 /* check IP header */ | |
288 if (pkt[0] != 0x45) | |
289 return; | |
290 if (pkt[9] != 17) /* UDP */ | |
291 return; | |
292 if (!bcmp(pkt + 12, &match_ip_addr, 4) && | |
293 !bcmp(pkt + 20, &match_udp_port, 2)) | |
294 process_packet_onedir(pkt, caplen, pkt_idx, &tx_state, "-->"); | |
295 else if (!bcmp(pkt + 16, &match_ip_addr, 4) && | |
296 !bcmp(pkt + 22, &match_udp_port, 2)) | |
297 process_packet_onedir(pkt, caplen, pkt_idx, &rx_state, "<--"); | |
298 } | |
299 | |
300 main(argc, argv) | |
301 char **argv; | |
302 { | |
303 char errbuf[PCAP_ERRBUF_SIZE]; | |
304 u_char *pkt; | |
305 struct pcap_pkthdr pkthdr; | |
306 unsigned pkt_idx; | |
307 | |
308 if (argc != 4) { | |
309 fprintf(stderr, "usage: %s pcap-file ip-addr udp-port\n", | |
310 argv[0]); | |
311 exit(1); | |
312 } | |
313 pcap = pcap_open_offline(argv[1], errbuf); | |
314 if (!pcap) { | |
315 fprintf(stderr, "%s: %s\n", argv[1], errbuf); | |
316 exit(1); | |
317 } | |
318 check_dl_type(); | |
319 match_ip_addr = inet_addr(argv[2]); | |
320 if (match_ip_addr == INADDR_NONE) { | |
321 fprintf(stderr, "error: IP address argument is invalid\n"); | |
322 exit(1); | |
323 } | |
324 match_udp_port = htons(strtoul(argv[3], 0, 0)); | |
325 for (pkt_idx = 0; ; pkt_idx++) { | |
326 pkt = pcap_next(pcap, &pkthdr); | |
327 if (!pkt) | |
328 break; | |
329 process_packet(pkt, (unsigned) pkthdr.caplen, pkt_idx); | |
330 } | |
331 if (!tx_state.init_flag) | |
332 printf( | |
333 "Warning: found no packets with src matching specified IP:port\n"); | |
334 if (!rx_state.init_flag) | |
335 printf( | |
336 "Warning: found no packets with dest matching specified IP:port\n"); | |
337 exit(0); | |
338 } |