source: trunk/src/listen.c

Last change on this file was 16, checked in by tim, 9 years ago

.

File size: 10.1 KB
Line 
1/*
2Portions are Copyright 2002 Tim Carstens. All rights reserved.
3Redistribution and use, with or without modification, are permitted provided
4that the following conditions are met:
5
6- Redistribution must retain the above copyright notice and this list of
7  conditions.
8
9- The name of Tim Carstens may not be used to endorse or promote products
10  derived from this document without specific prior written permission.
11*/
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <sys/types.h>
17#include <inttypes.h>
18#include <sys/stat.h>
19#include <fcntl.h>
20#include <errno.h>
21#include <netinet/in.h>
22#include <pcap.h>
23
24#define DEBUG 0
25
26/* ethernet headers are always exactly 14 bytes */
27/* XXX: ^^^ really? */
28#define SIZE_ETHERNET 14
29
30/* Ethernet addresses are 6 bytes */
31#define ETHER_ADDR_LEN  6
32
33/* Ethernet header */
34/*
35struct sniff_ethernet
36{
37  u_char ether_dhost[ETHER_ADDR_LEN]; / * Destination host address * /
38  u_char ether_shost[ETHER_ADDR_LEN]; / * Source host address * /
39  u_short ether_type; / * IP? ARP? RARP? etc * /
40};
41*/
42
43/* IP header */
44struct sniff_ip
45{
46  u_char ip_vhl;                /* version << 4 | header length >> 2 */
47  u_char ip_tos;                /* type of service */
48  u_short ip_len;               /* total length */
49  u_short ip_id;                /* identification */
50  u_short ip_off;               /* fragment offset field */
51#define IP_RF 0x8000            /* reserved fragment flag */
52#define IP_DF 0x4000            /* dont fragment flag */
53#define IP_MF 0x2000            /* more fragments flag */
54#define IP_OFFMASK 0x1fff       /* mask for fragmenting bits */
55  u_char ip_ttl;                /* time to live */
56  u_char ip_p;          /* protocol */
57  u_short ip_sum;               /* checksum */
58  struct in_addr ip_src,ip_dst; /* source and dest address */
59};
60#define IP_HL(ip)               (((ip)->ip_vhl) & 0x0f)
61#define IP_V(ip)                (((ip)->ip_vhl) >> 4)
62
63/* TCP header */
64typedef u_int tcp_seq;
65
66struct sniff_tcp
67{
68  u_short th_sport;     /* source port */
69  u_short th_dport;     /* destination port */
70  tcp_seq th_seq;               /* sequence number */
71  tcp_seq th_ack;               /* acknowledgement number */
72  u_char th_offx2;      /* data offset, rsvd */
73#define TH_OFF(th)      (((th)->th_offx2 & 0xf0) >> 4)
74  u_char th_flags;
75#define TH_FIN 0x01
76#define TH_SYN 0x02
77#define TH_RST 0x04
78#define TH_PUSH 0x08
79#define TH_ACK 0x10
80#define TH_URG 0x20
81#define TH_ECE 0x40
82#define TH_CWR 0x80
83#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
84  u_short th_win;               /* window */
85  u_short th_sum;               /* checksum */
86  u_short th_urp;               /* urgent pointer */
87};
88
89typedef struct {
90  uint8_t kind;
91  uint8_t size;
92} tcp_option_t;
93
94
95
96struct in_addr my_ip_raw;
97struct in_addr target_ip_raw;
98uint16_t target_port;
99
100FILE* output;
101
102struct useful_info
103{
104  uint32_t tcpseq;
105  uint32_t tcpack;
106  uint32_t tsval;
107  uint16_t local_port;
108  uint16_t payload_len;
109  uint8_t sent;
110};
111
112
113char* output_template = "{\"local_port\":%u,\"sent\":%u,\"payload_len\":%u,\"tcpseq\":%u,\"tcpack\":%u,\"observed\":%lu%06lu000,\"tsval\":%u}\n";
114uint8_t payloads_only = 1;
115
116
117pcap_t* create_listener(const char* dev, int snaplen, int promisc, int to_ms, char* errbuf)
118{
119  pcap_t* ret_val;
120  int status;
121  int tstcount, i, besttst;
122  int* tstypes;
123
124  ret_val = pcap_create(dev, errbuf);
125  if (ret_val == NULL)
126    return (NULL);
127 
128  status = pcap_set_snaplen(ret_val, snaplen);
129  if (status < 0)
130    goto fail;
131 
132  status = pcap_set_promisc(ret_val, promisc);
133  if (status < 0)
134    goto fail;
135 
136  status = pcap_set_timeout(ret_val, to_ms);
137  if (status < 0)
138    goto fail;
139
140
141  /* Try to select the best timestamp source for packet capture */
142  tstcount = pcap_list_tstamp_types(ret_val, &tstypes);
143  if(tstcount > 0)
144  {
145#if DEBUG
146    fprintf(stderr, "INFO: Available Packet Timers: ");
147#endif
148    besttst = -1;
149    for(i=0; i < tstcount; i++)
150    {
151#if DEBUG
152        fprintf(stderr, " %s", pcap_tstamp_type_val_to_name(tstypes[i]));
153#endif
154      switch (tstypes[i])
155      {
156      case PCAP_TSTAMP_HOST:
157        break;
158      case PCAP_TSTAMP_HOST_LOWPREC:
159        break;
160      case PCAP_TSTAMP_HOST_HIPREC:
161        {
162          if(besttst != PCAP_TSTAMP_ADAPTER_UNSYNCED
163             && besttst != PCAP_TSTAMP_ADAPTER)
164            besttst = PCAP_TSTAMP_HOST_HIPREC;
165          break;
166        }
167      case PCAP_TSTAMP_ADAPTER:
168        {
169          if(besttst != PCAP_TSTAMP_ADAPTER_UNSYNCED)
170            besttst = PCAP_TSTAMP_ADAPTER;
171          break;
172        }
173      case PCAP_TSTAMP_ADAPTER_UNSYNCED:
174          /*besttst = PCAP_TSTAMP_ADAPTER_UNSYNCED;*/
175          break;
176      default:
177#if DEBUG
178          fprintf(stderr, " unknown_type_%d", tstypes[i]);
179#endif
180        break;
181      }
182    }
183    pcap_free_tstamp_types(tstypes);
184#if DEBUG
185    fprintf(stderr, "\n");
186#endif
187    /*besttst = PCAP_TSTAMP_HOST;*/
188    if(besttst != -1)
189    {
190#if DEBUG
191      fprintf(stderr, "INFO: Attempting to set the timestamp source to: %s\n",
192              pcap_tstamp_type_val_to_name(besttst));
193#endif
194      if(pcap_set_tstamp_type(ret_val, besttst) != 0)
195        fprintf(stderr, "WARN: Failed to set preferred timestamp source.\n");
196    }
197  }
198 
199  /* Attempt to set nanosecond timestamp precision */   
200  if(pcap_set_tstamp_precision(ret_val, PCAP_TSTAMP_PRECISION_NANO) != 0)
201    fprintf(stderr, "INFO: Failed to set packet capture nanosecond precision.\n");
202  else
203    output_template = "{\"local_port\":%u,\"sent\":%u,\"payload_len\":%u,\"tcpseq\":%u,\"tcpack\":%u,\"observed\":%lu%09lu,\"tsval\":%u}\n";
204
205
206  status = pcap_activate(ret_val);
207  if (status < 0)
208    goto fail;
209 
210  return ret_val;
211
212 fail:
213  pcap_close(ret_val);
214  return NULL;
215}
216
217
218static int extract_packet_fields(const struct pcap_pkthdr *header,
219                                 const u_char* packet,
220                                 struct useful_info* fields)
221{
222  const struct sniff_ip* ip;               /* The IP header */
223  const struct sniff_tcp* tcp;             /* The TCP header */
224  u_int iphdr_size;
225  u_int tcphdr_size;
226  uint16_t ip_len;
227  uint16_t tsval;
228  uint8_t* opt;
229  tcp_option_t* _opt;
230 
231  /*printf("snaplen: %u\n", header->caplen);*/
232 
233  ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
234  iphdr_size = IP_HL(ip)*4;
235  if (iphdr_size < 20)
236    return 0;
237  ip_len = ntohs(ip->ip_len);
238 
239  tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + iphdr_size);
240  tcphdr_size = TH_OFF(tcp)*4;
241  if (tcphdr_size < 20)
242    return 0;
243
244  /*fprintf(stderr, "%d\n", (ip_len - iphdr_size - tcphdr_size));*/
245  if ((ip_len - iphdr_size - tcphdr_size < 0)
246      || (payloads_only && (ip_len - iphdr_size - tcphdr_size == 0)))
247  { return 0; }
248
249  fields->tcpseq = ntohl(tcp->th_seq);
250  fields->tcpack = ntohl(tcp->th_ack);
251  fields->payload_len = ip_len - iphdr_size - tcphdr_size;
252  if(ip->ip_src.s_addr == target_ip_raw.s_addr && ntohs(tcp->th_sport) == target_port)
253  {
254    fields->sent = 0;
255    fields->local_port = ntohs(tcp->th_dport);
256  }
257  else
258  {
259    fields->sent = 1;
260    fields->local_port = ntohs(tcp->th_sport);
261  }
262  /*printf("src: %lX / dst: %lX\n", ip->ip_src, ip->ip_dst);*/
263
264  fields->tsval = 0;
265  if(tcphdr_size > 20)
266  {
267    opt = (uint8_t*)(packet + SIZE_ETHERNET + iphdr_size + 20);
268    while((*opt != 0) && (opt - packet) < header->caplen)
269    {
270      _opt = (tcp_option_t*)opt;
271      if(_opt->kind == 1) /* NOP */
272      {
273        ++opt;  // NOP is one byte;
274        continue;
275      }
276     
277      if(_opt->kind == 8) /* timestamp */
278      {
279        fields->tsval = ntohl(*(uint32_t*)(opt+2));
280        break;
281      }
282     
283      opt += _opt->size;
284    }
285  }
286
287  return 1;
288}
289
290
291void process_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
292{
293  struct useful_info fields;
294
295  if(extract_packet_fields(header, packet, &fields))
296  {
297    fprintf(output, output_template, fields.local_port, fields.sent,
298            fields.payload_len, fields.tcpseq, fields.tcpack,
299            header->ts.tv_sec, header->ts.tv_usec, fields.tsval);
300    fflush(output);
301  }
302}
303
304
305
306int main(int argc, char** argv)
307{
308  char* dev;                      /* The device to sniff on */
309  char* my_ip;
310  char* target_ip;
311  pcap_t* handle;                 /* Session handle */
312  char bpf[255];                  /* The filter expression (length of 168 should be enough)*/
313  char errbuf[PCAP_ERRBUF_SIZE];  /* Error string */
314  struct bpf_program fp;          /* The compiled filter */
315  bpf_u_int32 mask;               /* Our netmask */
316  bpf_u_int32 net;                /* Our IP */
317  struct pcap_pkthdr header;      /* The header that pcap gives us */
318  const u_char *packet;           /* The actual packet */
319   
320  if(argc < 6)
321  {
322    fprintf(stderr, "USAGE:\n  %s {interface} {my_ip} {target_ip} {target_port} {output_file} [{payloads_only?}]\n", argv[0]);
323    return 1;
324  }
325
326  dev = argv[1];
327  my_ip = argv[2];
328  target_ip = argv[3];
329  target_port = atoi(argv[4]);
330  if(argc == 7 && argv[6][0] == '0')
331    payloads_only = 0;
332 
333  if(!(output = fopen(argv[5], "w+")))
334  {
335    fprintf(stderr, "ERROR: could not open output file due to: %s\n", strerror(errno));
336    return 2;
337  }
338 
339  snprintf(bpf, 255, "(src host %s and dst host %s and tcp and src port %u) or (dst host %s and src host %s and tcp and dst port %u)",
340           target_ip, my_ip, target_port, target_ip, my_ip, target_port);
341
342
343  if(!inet_aton(my_ip, &my_ip_raw))
344  {
345    fprintf(stderr, "Couldn't parse my_ip.\n");
346    return 1;
347  }
348
349  if(!inet_aton(target_ip, &target_ip_raw))
350  {
351    fprintf(stderr, "Couldn't parse target_ip.\n");
352    return 1;
353  }
354
355  memset(errbuf, 0, PCAP_ERRBUF_SIZE);
356
357  /* Find the properties for the device */
358  if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1)
359  {
360    fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
361    net = 0;
362    mask = 0;
363  }
364 
365  /* Open the session in promiscuous mode */
366  /* XXX: does to_ms timeout (param 4) matter? */
367  handle = create_listener(dev, BUFSIZ, 0, 1000, errbuf);
368  if (handle == NULL)
369  {
370    fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
371    return 2;
372  }
373
374  /* Compile and apply the filter */
375  if (pcap_compile(handle, &fp, bpf, 0, net) == -1)
376  {
377    fprintf(stderr, "Couldn't parse filter %s: %s\n", bpf, pcap_geterr(handle));
378    return 2;
379  }
380 
381  if (pcap_setfilter(handle, &fp) == -1)
382  {
383    fprintf(stderr, "Couldn't install filter %s: %s\n", bpf, pcap_geterr(handle));
384    return 2;
385  }
386
387  /* XXX: report errors */
388  pcap_loop(handle, 0, process_packet, NULL);
389 
390  /* And close the session */
391  pcap_close(handle);
392
393  return 0;
394}
Note: See TracBrowser for help on using the repository browser.