1 /*
2  * SpanDSP - a series of DSP components for telephony
3  *
4  * pcap_parse.c
5  *
6  * Written by Steve Underwood <steveu@coppice.org>
7  *
8  * Copyright (C) 2009 Steve Underwood
9  *
10  * All rights reserved.
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License version 2, as
14  * published by the Free Software Foundation.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  *
25  * Some code from SIPP (http://sf.net/projects/sipp) was used as a model
26  * for how to work with PCAP files. That code was authored by Guillaume
27  * TEISSIER from FTR&D 02/02/2006, and released under the GPL2 licence.
28  */
29 
30 #if defined(HAVE_CONFIG_H)
31 #include "config.h"
32 #endif
33 
34 #include <inttypes.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 
38 #if defined(HAVE_PCAP_H)
39 #include <pcap.h>
40 #include <pcap/sll.h>
41 #endif
42 #include <netinet/in.h>
43 #include <netinet/udp.h>
44 #if defined(__HPUX)  ||  defined(__CYGWIN__)  ||  defined(__FreeBSD__)
45 #include <netinet/in_systm.h>
46 #endif
47 #include <netinet/ip.h>
48 #if !defined(__CYGWIN__)
49 #include <netinet/ip6.h>
50 #endif
51 #include <string.h>
52 
53 #include <netinet/in.h>
54 #include <netinet/udp.h>
55 #include <time.h>
56 
57 #include "udptl.h"
58 #include "spandsp.h"
59 #include "pcap_parse.h"
60 
61 #if defined(__HPUX)  ||  defined(__DARWIN)  ||  defined(__CYGWIN__)  ||  defined(__FreeBSD__)
62 
63 struct iphdr
64 {
65 #if defined(_HPUX_LI)
66     unsigned int ihl:4;
67     unsigned int version:4;
68 #else
69     unsigned int version:4;
70     unsigned int ihl:4;
71 #endif
72     uint8_t tos;
73     uint16_t tot_len;
74     uint16_t id;
75     uint16_t frag_off;
76     uint8_t ttl;
77     uint8_t protocol;
78     uint16_t check;
79     uint32_t saddr;
80     uint32_t daddr;
81     /*The options start here. */
82 };
83 
84 #endif
85 
86 /* We define our own structures for Ethernet Header and IPv6 Header as they are not available on CYGWIN.
87  * We only need the fields, which are necessary to determine the type of the next header.
88  * we could also define our own structures for UDP and IPv4. We currently use the structures
89  * made available by the platform, as we had no problems to get them on all supported platforms.
90  */
91 
92 typedef struct _ether_hdr
93 {
94     char ether_dst[6];
95     char ether_src[6];
96     uint16_t ether_type;
97 } ether_hdr;
98 
99 typedef struct _null_hdr
100 {
101     uint32_t pf_type;
102 } null_hdr;
103 
104 #if !defined(__CYGWIN__)
105 typedef struct _ipv6_hdr
106 {
107     char dontcare[6];
108     u_int8_t nxt_header; /* we only need the next header, so we can determine, if the next header is UDP or not */
109     char dontcare2[33];
110 } ipv6_hdr;
111 #endif
112 
113 char errbuf[PCAP_ERRBUF_SIZE];
114 
pcap_scan_pkts(const char * file,uint32_t src_addr,uint16_t src_port,uint32_t dest_addr,uint16_t dest_port,pcap_timing_update_handler_t * timing_update_handler,pcap_packet_handler_t * packet_handler,void * user_data)115 int pcap_scan_pkts(const char *file,
116                    uint32_t src_addr,
117                    uint16_t src_port,
118                    uint32_t dest_addr,
119                    uint16_t dest_port,
120                    pcap_timing_update_handler_t *timing_update_handler,
121                    pcap_packet_handler_t *packet_handler,
122                    void *user_data)
123 {
124     pcap_t *pcap;
125     struct pcap_pkthdr *pkthdr;
126     uint8_t *pktdata;
127     uint8_t *pktptr;
128     const uint8_t *body;
129     int body_len;
130     int total_pkts;
131     uint32_t pktlen;
132     ether_hdr *ethhdr;
133     struct sll_header *sllhdr;
134     null_hdr *nullhdr;
135     struct iphdr *iphdr;
136 #if !defined(__CYGWIN__)
137     ipv6_hdr *ip6hdr;
138 #endif
139     struct udphdr *udphdr;
140     int datalink;
141     int packet_type;
142 
143     total_pkts = 0;
144     if ((pcap = pcap_open_offline(file, errbuf)) == NULL)
145     {
146         fprintf(stderr, "Can't open PCAP file: %s\n", errbuf);
147         return -1;
148     }
149     //printf("PCAP file version %d.%d\n", pcap_major_version(pcap), pcap_minor_version(pcap));
150     datalink = pcap_datalink(pcap);
151     /* DLT_EN10MB seems to apply to all forms of ethernet, not just the 10MB kind. */
152     switch (datalink)
153     {
154     case DLT_EN10MB:
155         printf("Datalink type ethernet\n");
156         break;
157     case DLT_LINUX_SLL:
158         printf("Datalink type cooked Linux socket\n");
159         break;
160     case DLT_NULL:
161         printf("Datalink type NULL\n");
162         break;
163     default:
164         fprintf(stderr, "Unsupported data link type %d\n", datalink);
165         return -1;
166     }
167 
168     pkthdr = NULL;
169     pktdata = NULL;
170 #if defined(HAVE_PCAP_NEXT_EX)
171     while (pcap_next_ex(pcap, &pkthdr, (const uint8_t **) &pktdata) == 1)
172     {
173 #else
174     if ((pkthdr = (struct pcap_pkthdr *) malloc(sizeof(*pkthdr))) == NULL)
175     {
176         fprintf(stderr, "Can't allocate memory for pcap pkthdr\n");
177         return -1;
178     }
179     while ((pktdata = (uint8_t *) pcap_next(pcap, pkthdr)) != NULL)
180     {
181 #endif
182         pktptr = pktdata;
183         switch (datalink)
184         {
185         case DLT_EN10MB:
186             ethhdr = (ether_hdr *) pktptr;
187             packet_type = ntohs(ethhdr->ether_type);
188             pktptr += sizeof(*ethhdr);
189             /* Check for a 802.1Q Virtual LAN entry we might need to step over */
190             if (packet_type == 0x8100)
191             {
192                 /* Step over the 802.1Q stuff, to get to the next packet type */
193                 pktptr += sizeof(uint16_t);
194                 packet_type = ntohs(*((uint16_t *) pktptr));
195                 pktptr += sizeof(uint16_t);
196             }
197 #if !defined(__CYGWIN__)
198             if (packet_type != 0x0800     /* IPv4 */
199                 &&
200                 packet_type != 0x86DD)    /* IPv6 */
201 #else
202             if (packet_type != 0x0800)    /* IPv4 */
203 #endif
204             {
205                 continue;
206             }
207             iphdr = (struct iphdr *) pktptr;
208             break;
209         case DLT_LINUX_SLL:
210             sllhdr = (struct sll_header *) pktptr;
211             packet_type = ntohs(sllhdr->sll_protocol);
212             pktptr += sizeof(*sllhdr);
213 #if !defined(__CYGWIN__)
214             if (packet_type != 0x0800     /* IPv4 */
215                 &&
216                 packet_type != 0x86DD)    /* IPv6 */
217 #else
218             if (packet_type != 0x0800)    /* IPv4 */
219 #endif
220             {
221                 continue;
222             }
223             iphdr = (struct iphdr *) pktptr;
224             break;
225         case DLT_NULL:
226             nullhdr = (null_hdr *) pktptr;
227             pktptr += sizeof(*nullhdr);
228             if (nullhdr->pf_type != PF_INET  &&  nullhdr->pf_type != PF_INET6)
229                 continue;
230             iphdr = (struct iphdr *) pktptr;
231             break;
232         default:
233             continue;
234         }
235 #if 0
236         {
237             int i;
238             printf("--- %d -", pkthdr->caplen);
239             for (i = 0;  i < pkthdr->caplen;  i++)
240                 printf(" %02x", pktdata[i]);
241             printf("\n");
242         }
243 #endif
244 #if !defined(__CYGWIN__)
245         if (iphdr  &&  iphdr->version == 6)
246         {
247             /* ipv6 */
248             ip6hdr = (ipv6_hdr *) (void *) iphdr;
249             if (ip6hdr->nxt_header != IPPROTO_UDP)
250                 continue;
251             pktlen = (uint32_t) pkthdr->len - (pktptr - pktdata) - sizeof(*ip6hdr);
252             udphdr = (struct udphdr *) ((uint8_t *) ip6hdr + sizeof(*ip6hdr));
253         }
254         else
255 #endif
256         {
257             /* ipv4 */
258             if (iphdr->protocol != IPPROTO_UDP)
259                 continue;
260 #if defined(__DARWIN)  ||  defined(__CYGWIN__)  ||  defined(__FreeBSD__)
261             udphdr = (struct udphdr *) ((uint8_t *) iphdr + (iphdr->ihl << 2) + 4);
262             pktlen = (uint32_t) ntohs(udphdr->uh_ulen);
263 #elif defined ( __HPUX)
264             udphdr = (struct udphdr *) ((uint8_t *) iphdr + (iphdr->ihl << 2));
265             pktlen = (uint32_t) pkthdr->len - (pktptr - pktdata) - sizeof(*iphdr);
266 #else
267             udphdr = (struct udphdr *) ((uint8_t *) iphdr + (iphdr->ihl << 2));
268             pktlen = (uint32_t) ntohs(udphdr->len);
269 #endif
270         }
271 
272         timing_update_handler(user_data, &pkthdr->ts);
273 
274         if (src_addr  &&  ntohl(iphdr->saddr) != src_addr)
275             continue;
276 #if defined(__DARWIN)  ||  defined(__CYGWIN__)  ||  defined(__FreeBSD__)
277         if (src_port  &&  ntohs(udphdr->uh_sport) != src_port)
278 #else
279         if (src_port  &&  ntohs(udphdr->source) != src_port)
280 #endif
281             continue;
282         if (dest_addr  &&  ntohl(iphdr->daddr) != dest_addr)
283             continue;
284 #if defined(__DARWIN)  ||  defined(__CYGWIN__)  ||  defined(__FreeBSD__)
285         if (dest_port  &&  ntohs(udphdr->uh_dport) != dest_port)
286 #else
287         if (dest_port  &&  ntohs(udphdr->dest) != dest_port)
288 #endif
289             continue;
290 
291         if (pkthdr->len != pkthdr->caplen)
292         {
293             fprintf(stderr, "Truncated packet - total len = %d, captured len = %d\n", pkthdr->len, pkthdr->caplen);
294             exit(2);
295         }
296 #if 0
297         printf("%d:%d -> %d:%d\n", ntohl(iphdr->saddr), ntohs(udphdr->source), ntohl(iphdr->daddr), ntohs(udphdr->dest));
298 #endif
299         body = (const uint8_t *) udphdr;
300         body += sizeof(struct udphdr);
301         body_len = pktlen - sizeof(struct udphdr);
302         packet_handler(user_data, body, body_len);
303 
304         total_pkts++;
305     }
306     fprintf(stderr, "In pcap %s there were %d accepted packets\n", file, total_pkts);
307     pcap_close(pcap);
308 
309     return 0;
310 }
311 /*- End of function --------------------------------------------------------*/
312 /*- End of file ------------------------------------------------------------*/
313