1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  *
16  *  Author : Guillaume TEISSIER from FTR&D 02/02/2006
17  */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21 
22 #include <pcap.h>
23 #include <stdlib.h>
24 #include <netinet/in.h>
25 #include <netinet/udp.h>
26 
27 #if defined(__HPUX) || defined(__CYGWIN) || defined(__FreeBSD__)
28 #include <netinet/in_systm.h>
29 #endif
30 #include <netinet/ip.h>
31 #ifndef __CYGWIN
32 #include <netinet/ip6.h>
33 #endif
34 #include <string.h>
35 
36 #include "defines.h"
37 #include "endianshim.h"
38 #include "prepare_pcap.h"
39 
40 /* We define our own structures for Ethernet Header and IPv6 Header as they are not available on CYGWIN.
41  * We only need the fields, which are necessary to determine the type of the next header.
42  * we could also define our own structures for UDP and IPv4. We currently use the structures
43  * made available by the platform, as we had no problems to get them on all supported platforms.
44  */
45 
46 typedef struct _ether_type_hdr {
47     uint16_t ether_type; /* we only need the type, so we can determine, if the next header is IPv4 or IPv6 */
48 } ether_type_hdr;
49 
50 typedef struct _ipv6_hdr {
51     char dontcare[6];
52     uint8_t nxt_header; /* we only need the next header, so we can determine, if the next header is UDP or not */
53     char dontcare2[33];
54 } ipv6_hdr;
55 
56 
57 #ifdef __HPUX
check(uint16_t * buffer,int len)58 int check(uint16_t *buffer, int len)
59 {
60 #else
61 inline int check(uint16_t *buffer, int len)
62 {
63 #endif
64     int sum;
65     int i;
66     sum = 0;
67 
68     for (i=0; i<(len&~1); i+= 2)
69         sum += *buffer++;
70 
71     if (len & 1) {
72         sum += htons((*(const uint8_t*)buffer) << 8);
73     }
74     return sum;
75 }
76 
77 #ifdef __HPUX
78 uint16_t checksum_carry(int s)
79 {
80 #else
81 inline uint16_t checksum_carry(int s)
82 {
83 #endif
84     int s_c = (s >> 16) + (s & 0xffff);
85     return (~(s_c + (s_c >> 16)) & 0xffff);
86 }
87 
88 char errbuf[PCAP_ERRBUF_SIZE];
89 
90 /* get octet offset to EtherType block in 802.11 frame
91  */
92 size_t get_802_11_ethertype_offset(int link, const uint8_t* pktdata)
93 {
94     size_t offset = 0;
95     uint8_t frame_type = 0;     /* 2 bits */
96     uint8_t frame_sub_type = 0; /* 4 bits */
97     uint16_t frame_ctl_fld;     /* Frame Control Field */
98 
99     /* get RadioTap header length */
100     if (link == DLT_IEEE802_11_RADIO) {
101         uint16_t rdtap_hdr_len = 0;
102         /* http://www.radiotap.org */
103         /* rdtap_version[1], pad[1], rdtap_hdr_len[2], rdtap_flds[4] */
104         memcpy(&rdtap_hdr_len, pktdata + 2, sizeof(rdtap_hdr_len));
105         /* http://radiotap.org */
106         /* all data fields in the radiotap header are to be specified
107          * in little-endian order */
108         rdtap_hdr_len = le16toh(rdtap_hdr_len);
109         offset += rdtap_hdr_len;
110     }
111 
112     memcpy(&frame_ctl_fld, pktdata + offset, sizeof(frame_ctl_fld));
113     /* extract frame type and subtype from Frame Control Field */
114     frame_type = frame_sub_type = frame_ctl_fld>>8;
115     frame_type = frame_type>>2 & 0x03;
116     frame_sub_type >>= 4;
117     if (frame_type < 0x02) {
118         /* Control or Management frame, so ignore it and try to get
119          * EtherType from next one */
120         offset = 0;
121     } else if (frame_type == 0x02) {
122         /* only Data frames carry the relevant payload and EtherType */
123         if (frame_sub_type < 0x04
124             || (frame_sub_type > 0x07 && frame_sub_type < 0x0c)) {
125             /* MAC header of a Data frame is at least 24 and at most 36
126              * octets long */
127             size_t mac_hdr_len = 24;
128             uint8_t llc_hdr[8] = { 0x00 };
129             while (mac_hdr_len <= 36) {
130                 /* attempt to get Logical-Link Control header */
131                 /* dsap[1],ssap[1],ctrl_fld[1],org_code[3],ethertype[2] */
132                 memcpy(llc_hdr, pktdata + offset + mac_hdr_len, sizeof(llc_hdr));
133                 /* check if Logical-Link Control header */
134                 if (llc_hdr[0] == 0xaa && llc_hdr[1] == 0xaa && llc_hdr[2] == 0x03) {
135                     /* get EtherType and convert to host byte-order.
136                      * (reduce by sizeof(eth_type)) */
137                     offset += mac_hdr_len + (sizeof(llc_hdr) - sizeof(uint16_t));
138                     break;
139                 }
140                 mac_hdr_len++;
141             }
142         } else {
143             /* could be Null Data frame, so ignore it and try to get
144              * EtherType from next one */
145             offset = 0;
146         }
147     } else {
148         ERROR("Unsupported frame type %d", frame_type);
149     }
150     return offset;
151 }
152 
153 /* get octet offset to EtherType block
154  */
155 size_t get_ethertype_offset(int link, const uint8_t* pktdata)
156 {
157     int is_le_encoded = 0; /* little endian */
158     uint16_t eth_type = 0;
159     size_t offset = 0;
160 
161     /* http://www.tcpdump.org/linktypes.html */
162     if (link == DLT_EN10MB) {
163         /* srcmac[6], dstmac[6], ethertype[2] */
164         offset = 12;
165     } else if (link == DLT_LINUX_SLL) {
166         /* http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html */
167         /* pkttype[2], arphrd_type[2], lladdrlen[2], lladdr[8], ethertype[2] */
168         offset = 14;
169     } else if (link == DLT_IEEE802_11
170                || link == DLT_IEEE802_11_RADIO) {
171         offset = get_802_11_ethertype_offset(link, pktdata);
172         /* multi-octet fields in 802.11 frame are to be specified in
173          * little-endian order */
174         is_le_encoded = 1;
175     } else {
176         ERROR("Unsupported link-type %d", link);
177     }
178 
179     if (offset) {
180         /* get EtherType and convert to host byte order */
181         memcpy(&eth_type, pktdata + offset, sizeof(eth_type));
182         eth_type = (is_le_encoded) ? le16toh(eth_type) : ntohs(eth_type);
183         if (eth_type != 0x0800 && eth_type != 0x86dd) {
184             /* check if Ethernet 802.1Q VLAN */
185             if (eth_type == 0x8100) {
186                 /* vlan_tag[4] */
187                 offset += 4;
188             } else {
189                 ERROR("Unsupported ethernet type %d", eth_type);
190             }
191         }
192     }
193     return offset;
194 }
195 
196 /* prepare a pcap file
197  */
198 int prepare_pkts(char *file, pcap_pkts *pkts)
199 {
200     pcap_t* pcap;
201 #ifdef HAVE_PCAP_NEXT_EX
202     struct pcap_pkthdr* pkthdr = NULL;
203 #else
204     struct pcap_pkthdr pkthdr_storage;
205     struct pcap_pkthdr* pkthdr = &pkthdr_storage;
206 #endif
207     const uint8_t* pktdata = NULL;
208     int n_pkts = 0;
209     uint64_t max_length = 0;
210     size_t ether_type_offset = 0;
211     uint16_t base = 0xffff;
212     uint64_t pktlen;
213     pcap_pkt* pkt_index;
214     ether_type_hdr* ethhdr;
215 
216     struct iphdr* iphdr;
217     ipv6_hdr* ip6hdr;
218     struct udphdr* udphdr;
219 
220     pkts->pkts = NULL;
221 
222     pcap = pcap_open_offline(file, errbuf);
223     if (!pcap)
224         ERROR_NO("Can't open PCAP file '%s'", file);
225 #ifdef HAVE_PCAP_NEXT_EX
226     while (pcap_next_ex(pcap, &pkthdr, &pktdata) == 1) {
227 #else
228     while ((pktdata = pcap_next(pcap, pkthdr)) != NULL) {
229 #endif
230         if (pkthdr->len != pkthdr->caplen) {
231             ERROR("You got truncated packets. Please create a new dump with -s0");
232         }
233 
234         /* Determine offset from packet to ether type only once. */
235         if (!ether_type_offset) {
236             int datalink = pcap_datalink(pcap);
237             ether_type_offset = get_ethertype_offset(datalink, pktdata);
238         }
239 
240         ethhdr = (ether_type_hdr *)(pktdata + ether_type_offset);
241         if (ntohs(ethhdr->ether_type) != 0x0800 /* IPv4 */
242                 && ntohs(ethhdr->ether_type) != 0x86dd) { /* IPv6 */
243             fprintf(stderr, "Ignoring non IP{4,6} packet, got ether_type %hu!\n",
244                     ntohs(ethhdr->ether_type));
245             continue;
246         }
247         iphdr = (struct iphdr*)((char*)ethhdr + sizeof(*ethhdr));
248         if (iphdr && iphdr->version == 6) {
249             /* ipv6 */
250             pktlen = (uint64_t)pkthdr->len - sizeof(*ethhdr) - sizeof(*ip6hdr);
251             ip6hdr = (ipv6_hdr*)(void*)iphdr;
252             if (ip6hdr->nxt_header != IPPROTO_UDP) {
253                 fprintf(stderr, "prepare_pcap.c: Ignoring non UDP packet!\n");
254                 continue;
255             }
256             udphdr = (struct udphdr*)((char*)ip6hdr + sizeof(*ip6hdr));
257         } else {
258             /* ipv4 */
259             if (iphdr->protocol != IPPROTO_UDP) {
260                 fprintf(stderr, "prepare_pcap.c: Ignoring non UDP packet!\n");
261                 continue;
262             }
263 #if defined(__DARWIN) || defined(__CYGWIN) || defined(__FreeBSD__)
264             udphdr = (struct udphdr*)((char*)iphdr + (iphdr->ihl << 2) + 4);
265             pktlen = (uint64_t)(ntohs(udphdr->uh_ulen));
266 #elif defined ( __HPUX)
267             udphdr = (struct udphdr*)((char*)iphdr + (iphdr->ihl << 2));
268             pktlen = (uint64_t) pkthdr->len - sizeof(*ethhdr) - sizeof(*iphdr);
269 #else
270             udphdr = (struct udphdr*)((char*)iphdr + (iphdr->ihl << 2));
271             pktlen = (uint64_t)(ntohs(udphdr->len));
272 #endif
273         }
274         if (pktlen > PCAP_MAXPACKET) {
275             ERROR("Packet size is too big! Recompile with bigger PCAP_MAXPACKET in prepare_pcap.h");
276         }
277         pkts->pkts = (pcap_pkt *)realloc(pkts->pkts, sizeof(*(pkts->pkts)) * (n_pkts + 1));
278         if (!pkts->pkts)
279             ERROR("Can't re-allocate memory for pcap pkt");
280         pkt_index = pkts->pkts + n_pkts;
281         pkt_index->pktlen = pktlen;
282         pkt_index->ts = pkthdr->ts;
283         pkt_index->data = (unsigned char *) malloc(pktlen);
284         if (!pkt_index->data)
285             ERROR("Can't allocate memory for pcap pkt data");
286         memcpy(pkt_index->data, udphdr, pktlen);
287 
288 #if defined(__HPUX) || defined(__DARWIN) || (defined __CYGWIN) || defined(__FreeBSD__)
289         udphdr->uh_sum = 0 ;
290 #else
291         udphdr->check = 0;
292 #endif
293 
294         /* compute a partial udp checksum */
295         /* not including port that will be changed */
296         /* when sending RTP */
297 #if defined(__HPUX) || defined(__DARWIN) || (defined __CYGWIN) || defined(__FreeBSD__)
298         pkt_index->partial_check = check((uint16_t*)&udphdr->uh_ulen, pktlen - 4) + ntohs(IPPROTO_UDP + pktlen);
299 #else
300         pkt_index->partial_check = check((uint16_t*)&udphdr->len, pktlen - 4) + ntohs(IPPROTO_UDP + pktlen);
301 #endif
302         if (max_length < pktlen)
303             max_length = pktlen;
304 #if defined(__HPUX) || defined(__DARWIN) || (defined __CYGWIN) || defined(__FreeBSD__)
305         if (base > ntohs(udphdr->uh_dport))
306             base = ntohs(udphdr->uh_dport);
307 #else
308         if (base > ntohs(udphdr->dest))
309             base = ntohs(udphdr->dest);
310 #endif
311         n_pkts++;
312     }
313     pkts->max = pkts->pkts + n_pkts;
314     pkts->max_length = max_length;
315     pkts->base = base;
316     fprintf(stderr, "In pcap %s, npkts %d\nmax pkt length %ld\nbase port %d\n", file, n_pkts, max_length, base);
317     pcap_close(pcap);
318 
319     return 0;
320 }
321