source: trunk/src/csamp.c @ 11

Last change on this file since 11 was 4, 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      fprintf(stderr, "INFO: Attempting to set the timestamp source to: %s\n",
191              pcap_tstamp_type_val_to_name(besttst));
192      if(pcap_set_tstamp_type(ret_val, besttst) != 0)
193        fprintf(stderr, "WARN: Failed to set preferred timestamp source.\n");
194    }
195  }
196 
197  /* Attempt to set nanosecond timestamp precision */   
198  if(pcap_set_tstamp_precision(ret_val, PCAP_TSTAMP_PRECISION_NANO) != 0)
199    fprintf(stderr, "INFO: Failed to set packet capture nanosecond precision.\n");
200  else
201    output_template = "{\"local_port\":%u,\"sent\":%u,\"payload_len\":%u,\"tcpseq\":%u,\"tcpack\":%u,\"observed\":%lu%09lu,\"tsval\":%u}\n";
202
203
204  status = pcap_activate(ret_val);
205  if (status < 0)
206    goto fail;
207 
208  return ret_val;
209
210 fail:
211  pcap_close(ret_val);
212  return NULL;
213}
214
215
216static int extract_packet_fields(const struct pcap_pkthdr *header,
217                                 const u_char* packet,
218                                 struct useful_info* fields)
219{
220  const struct sniff_ip* ip;               /* The IP header */
221  const struct sniff_tcp* tcp;             /* The TCP header */
222  u_int iphdr_size;
223  u_int tcphdr_size;
224  uint16_t ip_len;
225  uint16_t tsval;
226  uint8_t* opt;
227  tcp_option_t* _opt;
228 
229  /*printf("snaplen: %u\n", header->caplen);*/
230 
231  ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
232  iphdr_size = IP_HL(ip)*4;
233  if (iphdr_size < 20)
234    return 0;
235  ip_len = ntohs(ip->ip_len);
236 
237  tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + iphdr_size);
238  tcphdr_size = TH_OFF(tcp)*4;
239  if (tcphdr_size < 20)
240    return 0;
241
242  /*fprintf(stderr, "%d\n", (ip_len - iphdr_size - tcphdr_size));*/
243  if ((ip_len - iphdr_size - tcphdr_size < 0)
244      || (payloads_only && (ip_len - iphdr_size - tcphdr_size == 0)))
245  { return 0; }
246
247  fields->tcpseq = ntohl(tcp->th_seq);
248  fields->tcpack = ntohl(tcp->th_ack);
249  fields->payload_len = ip_len - iphdr_size - tcphdr_size;
250  if(ip->ip_src.s_addr == target_ip_raw.s_addr && ntohs(tcp->th_sport) == target_port)
251  {
252    fields->sent = 0;
253    fields->local_port = ntohs(tcp->th_dport);
254  }
255  else
256  {
257    fields->sent = 1;
258    fields->local_port = ntohs(tcp->th_sport);
259  }
260  /*printf("src: %lX / dst: %lX\n", ip->ip_src, ip->ip_dst);*/
261
262  fields->tsval = 0;
263  if(tcphdr_size > 20)
264  {
265    opt = (uint8_t*)(packet + SIZE_ETHERNET + iphdr_size + 20);
266    while((*opt != 0) && (opt - packet) < header->caplen)
267    {
268      _opt = (tcp_option_t*)opt;
269      if(_opt->kind == 1) /* NOP */
270      {
271        ++opt;  // NOP is one byte;
272        continue;
273      }
274     
275      if(_opt->kind == 8) /* timestamp */
276      {
277        fields->tsval = ntohl(*(uint32_t*)(opt+2));
278        break;
279      }
280     
281      opt += _opt->size;
282    }
283  }
284
285  return 1;
286}
287
288
289void process_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
290{
291  struct useful_info fields;
292
293  if(extract_packet_fields(header, packet, &fields))
294  {
295    fprintf(output, output_template, fields.local_port, fields.sent,
296            fields.payload_len, fields.tcpseq, fields.tcpack,
297            header->ts.tv_sec, header->ts.tv_usec, fields.tsval);
298    fflush(output);
299  }
300}
301
302
303
304int main(int argc, char** argv)
305{
306  char* dev;                      /* The device to sniff on */
307  char* my_ip;
308  char* target_ip;
309  pcap_t* handle;                 /* Session handle */
310  char bpf[255];                  /* The filter expression (length of 168 should be enough)*/
311  char errbuf[PCAP_ERRBUF_SIZE];  /* Error string */
312  struct bpf_program fp;          /* The compiled filter */
313  bpf_u_int32 mask;               /* Our netmask */
314  bpf_u_int32 net;                /* Our IP */
315  struct pcap_pkthdr header;      /* The header that pcap gives us */
316  const u_char *packet;           /* The actual packet */
317   
318  if(argc < 6)
319  {
320    fprintf(stderr, "USAGE:\n  %s {interface} {my_ip} {target_ip} {target_port} {output_file} [{payloads_only?}]\n", argv[0]);
321    return 1;
322  }
323
324  dev = argv[1];
325  my_ip = argv[2];
326  target_ip = argv[3];
327  target_port = atoi(argv[4]);
328  if(argc == 7 && argv[6][0] == '0')
329    payloads_only = 0;
330 
331  if(!(output = fopen(argv[5], "w+")))
332  {
333    fprintf(stderr, "ERROR: could not open output file due to: %s\n", strerror(errno));
334    return 2;
335  }
336 
337  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)",
338           target_ip, my_ip, target_port, target_ip, my_ip, target_port);
339
340
341  if(!inet_aton(my_ip, &my_ip_raw))
342  {
343    fprintf(stderr, "Couldn't parse my_ip.\n");
344    return 1;
345  }
346
347  if(!inet_aton(target_ip, &target_ip_raw))
348  {
349    fprintf(stderr, "Couldn't parse target_ip.\n");
350    return 1;
351  }
352
353  memset(errbuf, 0, PCAP_ERRBUF_SIZE);
354
355  /* Find the properties for the device */
356  if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1)
357  {
358    fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
359    net = 0;
360    mask = 0;
361  }
362 
363  /* Open the session in promiscuous mode */
364  /* XXX: does to_ms timeout (param 4) matter? */
365  handle = create_listener(dev, BUFSIZ, 0, 1000, errbuf);
366  if (handle == NULL)
367  {
368    fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
369    return 2;
370  }
371
372  /* Compile and apply the filter */
373  if (pcap_compile(handle, &fp, bpf, 0, net) == -1)
374  {
375    fprintf(stderr, "Couldn't parse filter %s: %s\n", bpf, pcap_geterr(handle));
376    return 2;
377  }
378 
379  if (pcap_setfilter(handle, &fp) == -1)
380  {
381    fprintf(stderr, "Couldn't install filter %s: %s\n", bpf, pcap_geterr(handle));
382    return 2;
383  }
384
385  /* XXX: report errors */
386  pcap_loop(handle, 0, process_packet, NULL);
387 
388  /* And close the session */
389  pcap_close(handle);
390
391  return 0;
392}
Note: See TracBrowser for help on using the repository browser.