~ubuntu-branches/ubuntu/precise/xprobe/precise

« back to all changes in this revision

Viewing changes to libs-external/USI++/src/datalink.cc

  • Committer: Bazaar Package Importer
  • Author(s): Richard Atterer
  • Date: 2005-02-22 22:54:24 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050222225424-6cqy8rr45pkna819
Tags: 0.2.2-1
New upstream version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*** This Programs/Libraries are (C)opyright by Sebastian Krahmer.
 
2
 *** You may use it under the terms of the GPL. You should have
 
3
 *** already received the file COPYING that shows you your rights.
 
4
 *** Please look at COPYING for further license-details.
 
5
 ***  
 
6
 *** THERE IS ABSOLUTELY NO WARRANTY. SO YOU USE IT AT YOUR OWN RISK.
 
7
 *** IT WAS WRITTEN IN THE HOPE THAT IT WILL BE USEFULL. I AM NOT RESPONSIBLE
 
8
 *** FOR ANY DAMAGE YOU MAYBE GET DUE TO USING MY PROGRAMS.
 
9
 ***/
 
10
 
 
11
#include "config.h"
 
12
#include "usi++/usi-structs.h"
 
13
#include "usi++/datalink.h"
 
14
 
 
15
#include <string.h>
 
16
#include <unistd.h>
 
17
#include <errno.h>
 
18
#include <sys/ioctl.h>
 
19
 
 
20
#ifdef IMMEDIATE
 
21
#include <net/bpf.h>
 
22
#endif
 
23
 
 
24
namespace usipp {
 
25
 
 
26
Pcap::Pcap()
 
27
    : RX()
 
28
{       
 
29
        // Initialize 
 
30
        memset(d_filter_string, 0, sizeof(d_filter_string));
 
31
        d_pd = NULL;
 
32
        memset(&d_tv, 0, sizeof(d_tv));
 
33
        
 
34
        memset(d_dev, 0, sizeof(d_dev));
 
35
        d_timeout = false;
 
36
        start = 0;
 
37
        
 
38
}
 
39
 
 
40
/* This constructor should be used to
 
41
 *  initialize raw-d_datalink-objects, means not IP/TCP/ICMP etc.
 
42
 *  We need this b/c unlike in derived classes, d_datalink::init_device()
 
43
 *  cannot set a filter!
 
44
 */
 
45
Pcap::Pcap(char *filterStr)
 
46
{       
 
47
        // Initialize 
 
48
        memset(d_filter_string, 0, sizeof(d_filter_string));
 
49
        strncpy(d_filter_string, filterStr, sizeof(d_filter_string));
 
50
        d_pd = NULL;
 
51
        
 
52
        memset(d_dev, 0, sizeof(d_dev));
 
53
        start = 0;
 
54
        
 
55
}
 
56
        
 
57
Pcap::~Pcap()
 
58
{
 
59
        if (d_pd != NULL)
 
60
                pcap_close(d_pd);
 
61
}
 
62
 
 
63
Pcap::Pcap(const Pcap &rhs)
 
64
{
 
65
        if (this == &rhs)
 
66
                return;
 
67
        d_datalink = rhs.d_datalink;
 
68
        d_framelen = rhs.d_framelen;
 
69
        d_filter = rhs.d_filter;
 
70
        d_phdr = rhs.d_phdr;
 
71
 
 
72
        d_ether = rhs.d_ether;
 
73
        strncpy(d_filter_string, rhs.d_filter_string, sizeof(d_filter_string));
 
74
        strncpy(d_dev, rhs.d_dev, sizeof(d_dev));
 
75
        d_has_promisc = rhs.d_has_promisc;
 
76
        d_snaplen = rhs.d_snaplen;
 
77
        
 
78
        start = rhs.start;
 
79
        if (rhs.d_pd)
 
80
                init_device(d_dev, d_has_promisc, d_snaplen);
 
81
 
 
82
        return;
 
83
}
 
84
 
 
85
Pcap &Pcap::operator=(const Pcap &rhs)
 
86
{
 
87
        if (this == &rhs)
 
88
                return *this;
 
89
        d_datalink = rhs.d_datalink;
 
90
        d_framelen = rhs.d_framelen;
 
91
        d_filter = rhs.d_filter;
 
92
        d_phdr = rhs.d_phdr;
 
93
 
 
94
        d_ether = rhs.d_ether;
 
95
        strncpy(d_filter_string, rhs.d_filter_string, sizeof(d_filter_string));
 
96
        strncpy(d_dev, rhs.d_dev, sizeof(d_dev));
 
97
        d_has_promisc = rhs.d_has_promisc;
 
98
        d_snaplen = rhs.d_snaplen;
 
99
        
 
100
        start = rhs.start;
 
101
 
 
102
        if (rhs.d_pd) {
 
103
                if (d_pd)
 
104
                        pcap_close(d_pd);
 
105
                init_device(d_dev, d_has_promisc, d_snaplen);
 
106
        }
 
107
 
 
108
        return *this;
 
109
}
 
110
 
 
111
/*  Return the actual d_datalink of the object.
 
112
 */
 
113
int Pcap::get_datalink()
 
114
{
 
115
        return d_datalink;
 
116
}
 
117
 
 
118
/*  Return the actual framlen of the object.
 
119
 *  (d_framelen depends on d_datalink)
 
120
 */
 
121
int Pcap::get_framelen()
 
122
{
 
123
        return d_framelen;
 
124
}
 
125
 
 
126
 
 
127
/*  Fill buffer with src-hardware-adress of actuall packet,
 
128
 *  use 'd_datalink' to determine what HW the device is.
 
129
 *  Now only ethernet s supportet, but it's extensinable.
 
130
 */
 
131
char *Pcap::get_hwsrc(char *hwaddr, size_t len)
 
132
{
 
133
        unsigned char *s;
 
134
        memset(hwaddr, 0, len);
 
135
 
 
136
        switch (d_datalink) {
 
137
        case DLT_EN10MB:
 
138
                if (len < 2*ETH_ALEN)
 
139
                        return NULL;
 
140
                s = d_ether.ether_shost;
 
141
                sprintf(hwaddr, "%02x:%02x:%02x:%02x:%02x:%02x", (u_char)*s, 
 
142
                       (u_char)*(s+1), (u_char)*(s+2), (u_char)*(s+3), 
 
143
                       (u_char)*(s+4), (u_char)*(s+5));        
 
144
                break;
 
145
        default:
 
146
                return NULL;
 
147
        }
 
148
        return hwaddr;
 
149
}
 
150
 
 
151
/*  Fill buffer with dst-hardware-adress of actuall packet,
 
152
 *  use 'd_datalink' to determine what HW the device is.
 
153
 *  Now only ethernet s supportet.
 
154
 */
 
155
char *Pcap::get_hwdst(char *hwaddr, size_t len)
 
156
{
 
157
        unsigned char *s;
 
158
        
 
159
        memset(hwaddr, 0, len);
 
160
        switch (d_datalink) {
 
161
        case DLT_EN10MB:
 
162
                if (len < 2*ETH_ALEN)
 
163
                       return NULL;
 
164
                s = d_ether.ether_dhost;
 
165
                sprintf(hwaddr, "%02x:%02x:%02x:%02x:%02x:%02x", (u_char)*s, 
 
166
                       (u_char)*(s+1), (u_char)*(s+2), (u_char)*(s+3), 
 
167
                       (u_char)*(s+4), (u_char)*(s+5));        
 
168
                break;
 
169
        default:
 
170
                return NULL;
 
171
        }       
 
172
        return hwaddr;
 
173
}
 
174
 
 
175
/*  Get protocol-type of ethernet-frame
 
176
 *  Maybe moves to ethernet-class in future?
 
177
 */
 
178
u_int16_t Pcap::get_etype()
 
179
{
 
180
        return ntohs(d_ether.ether_type);
 
181
}
 
182
 
 
183
/*  Initialize a device ("eth0" for example) for packet-
 
184
 *  capturing. It MUST be called before sniffpack() is launched.
 
185
 *  Set 'promisc' to 1 if you want the device running in promiscous mode.
 
186
 *  Fetch at most 'd_snaplen' bytes per call.
 
187
 */
 
188
int Pcap::init_device(char *dev, int promisc, size_t d_snaplen)
 
189
{
 
190
        char ebuf[PCAP_ERRBUF_SIZE];
 
191
        memset(ebuf, 0, PCAP_ERRBUF_SIZE);
 
192
        
 
193
        if ((d_pd = pcap_open_live(dev, d_snaplen, promisc, 500, ebuf)) == NULL) {
 
194
                die(ebuf, STDERR, 1);
 
195
        }
 
196
#ifdef HAVE_PCAP_SETNONBLOCK
 
197
                // make sure pcap_next() does not block
 
198
                if (pcap_setnonblock(d_pd, 1, ebuf) < 0) {
 
199
                        die(ebuf, STDERR, 1);
 
200
                }
 
201
#endif
 
202
 
 
203
// Ehem, BSD workarounnd. BSD won't timeout on select()
 
204
// unless we force immediate return for read() (in pcap)
 
205
// for uncomplete packets (queue not full?)
 
206
#ifdef IMMEDIATE
 
207
        int v = 1;
 
208
        if (ioctl(pcap_fileno(d_pd), BIOCIMMEDIATE, &v) < 0) {
 
209
                snprintf(ebuf, sizeof(ebuf),
 
210
                        "Pcap::init_device::ioctl(..., BIOCIMMEDIATE, 1) %s",
 
211
                        strerror(errno));
 
212
                die(ebuf, STDERR, 1);
 
213
        }
 
214
#endif        
 
215
        if (pcap_lookupnet(dev, &d_localnet, &d_netmask, ebuf) < 0) {
 
216
                snprintf(ebuf, sizeof(ebuf), "Pcap::init_device::pcap_lookupnet: %s\n",
 
217
                        pcap_geterr(d_pd));
 
218
                die(ebuf, STDERR, 1);
 
219
        }
 
220
        
 
221
 
 
222
        /* The d_filter_string must be filled by derived classes, such
 
223
         * as IP, where the virtual init_device() simply sets d_filter_string
 
224
         * to "ip" and then calls Pcap::init_device().
 
225
         */
 
226
        if (pcap_compile(d_pd, &d_filter, d_filter_string, 1, d_netmask) < 0) {
 
227
                snprintf(ebuf, sizeof(ebuf), "Pcap::init_device::pcap_compile: %s\n",
 
228
                        pcap_geterr(d_pd));
 
229
                die(ebuf, STDERR, 1);
 
230
        }
 
231
        if (pcap_setfilter(d_pd, &d_filter) < 0) {
 
232
                snprintf(ebuf, sizeof(ebuf), "Pcap::init_device::pcap_setfilter: %s\n",
 
233
                        pcap_geterr(d_pd));
 
234
                die(ebuf, STDERR, 1);
 
235
        }
 
236
        if ((d_datalink = pcap_datalink(d_pd)) < 0) {
 
237
                snprintf(ebuf, sizeof(ebuf), "Pcap::init_device::pcap_d_datalink: %s\n",
 
238
                        pcap_geterr(d_pd));
 
239
                die(ebuf, STDERR, 1);
 
240
        }
 
241
 
 
242
        // turn d_datalink into d_framelen
 
243
        switch (d_datalink) {
 
244
        case DLT_EN10MB:
 
245
                d_framelen = sizeof(d_ether);
 
246
                break;
 
247
        case DLT_PPP:
 
248
                d_framelen = 4; /* shouldn't be 4 */
 
249
                break;
 
250
        case DLT_PPP_BSDOS:
 
251
                d_framelen = 24;
 
252
                break;
 
253
        case DLT_SLIP:
 
254
                d_framelen = 24;
 
255
                break;
 
256
        case DLT_RAW:
 
257
                d_framelen = 0;
 
258
                break;
 
259
 
 
260
        // loopback
 
261
#ifdef DLT_LOOP
 
262
        case DLT_LOOP:
 
263
#endif
 
264
        case DLT_NULL:
 
265
                d_framelen = 4;
 
266
                break;
 
267
#ifdef DLT_LINUX_SLL
 
268
    case DLT_LINUX_SLL:
 
269
        d_framelen = 16;
 
270
        break;          
 
271
#endif
 
272
 
 
273
        default:
 
274
                printf("%d %d\n", d_datalink, DLT_RAW);
 
275
            fprintf(stderr, "Datalink type: %i not supported.  Report!\n", d_datalink);
 
276
                die("Pcap::init_device: Unknown d_datalink.\n", STDERR, 1);
 
277
        }
 
278
       
 
279
        strncpy(d_dev, dev, sizeof(d_dev));
 
280
        d_has_promisc = promisc;
 
281
        d_snaplen = d_snaplen;
 
282
        return 0;
 
283
}
 
284
 
 
285
 
 
286
/*  set a new filter for capturing
 
287
 */
 
288
int Pcap::setfilter(char *s)
 
289
{
 
290
        char ebuf[PCAP_ERRBUF_SIZE];
 
291
        memset(ebuf, 0, PCAP_ERRBUF_SIZE);
 
292
        
 
293
        if (!d_pd) 
 
294
                die("Pcap::setfilter: Device not initialized.\n", STDERR, 1);
 
295
 
 
296
        memset(d_filter_string, 0, sizeof(d_filter_string));
 
297
        snprintf(d_filter_string, sizeof(d_filter_string), "%s", s);
 
298
        
 
299
        if (pcap_compile(d_pd, &d_filter, d_filter_string, 1, d_netmask) < 0) {
 
300
                snprintf(ebuf, sizeof(ebuf), "Pcap::setfilter::pcap_compile: %s\n", pcap_geterr(d_pd));
 
301
                die(ebuf, STDERR, 1);
 
302
        }
 
303
 
 
304
        if (pcap_setfilter(d_pd, &d_filter) < 0) {
 
305
                snprintf(ebuf, sizeof(ebuf), "Pcap::setfilter::pcap_setfilter: %s\n", pcap_geterr(d_pd));
 
306
                die(ebuf, STDERR, 1);
 
307
        }
 
308
        return 0;
 
309
}
 
310
 
 
311
int Pcap::sniffpack(void *s, size_t len)
 
312
{
 
313
        char *tmp;        
 
314
        memset(s, 0, len);
 
315
 
 
316
        d_timeout = false;      
 
317
        if (!d_pd)
 
318
                die("Pcap::sniffpack: Device not initialized.\n", STDERR, 1);
 
319
 
 
320
        /* XXX: with select() packets on loopback interface are lost
 
321
         * so we leave it out for now
 
322
         * 
 
323
        if (d_tv.tv_sec != 0 || d_tv.tv_usec != 0) {    // TO was set
 
324
                while (1) {
 
325
                        fd_set rset;
 
326
                        FD_ZERO(&rset);
 
327
                        int fd = pcap_fileno(d_pd);
 
328
                        FD_SET(fd, &rset);
 
329
                        timeval tmp = d_tv;
 
330
 
 
331
                        // wait for packet
 
332
                        int sr;
 
333
                        if ((sr=select(fd+1, &rset, NULL, NULL, &tmp)) < 0) {
 
334
                                if (errno == EINTR)
 
335
                                        continue;
 
336
                                else
 
337
                                        return -1;
 
338
                        } else if (sr == 0) { // timed out
 
339
                                d_timeout = true;
 
340
                                return 0;
 
341
                        } else          // got packet
 
342
                                break;
 
343
                }
 
344
        }
 
345
        */
 
346
        /* XXX: there is a bug on linux when select() returns 1, but pcap_next()
 
347
         * returns NULL */
 
348
        if (start == 0) // first time sniffpack() is called
 
349
                start = time(NULL);
 
350
 
 
351
        if (d_tv.tv_sec != 0 || d_tv.tv_usec != 0) { // timeout set
 
352
                while ((tmp = (char*)pcap_next(d_pd, &d_phdr)) == NULL)
 
353
                        if ((time(NULL) - start) > d_tv.tv_sec) {
 
354
                                d_timeout = true;
 
355
                                start = 0;
 
356
                                return 0;
 
357
                        }
 
358
        } else { // no timeout set, loop until we get some kind of packet
 
359
                while ((tmp = (char*)pcap_next(d_pd, &d_phdr)) == NULL)
 
360
                        ;
 
361
        }
 
362
 
 
363
        switch (d_datalink) {
 
364
                case DLT_EN10MB:
 
365
                        memcpy(&d_ether, tmp, d_framelen);
 
366
                        break;
 
367
                case DLT_PPP:
 
368
                        break;
 
369
                case DLT_PPP_BSDOS:
 
370
                        break;
 
371
                case DLT_SLIP:
 
372
                        break;
 
373
                case DLT_RAW:
 
374
                        break;
 
375
#ifdef DLT_LOOP
 
376
                case DLT_LOOP:
 
377
#endif
 
378
                case DLT_NULL:
 
379
                        break;
 
380
#ifdef DLT_LINUX_SLL
 
381
                case DLT_LINUX_SLL:
 
382
                        break;          
 
383
#endif  
 
384
                default:
 
385
                        die("Pcap::sniffpack: Unknown d_datalink.\n", STDERR, 1);
 
386
        }
 
387
#ifdef USI_DEBUG
 
388
        cerr<<"Pcap::d_phdr.len="<<d_phdr.len<<endl;
 
389
        cerr<<"Pcap::d_framelen="<<d_framelen<<endl;
 
390
#endif
 
391
        // d_framelen was already calculated by init_device 
 
392
        memcpy(s, (tmp + d_framelen), 
 
393
               d_phdr.len - d_framelen < len ? d_phdr.len - d_framelen : len);
 
394
        return (d_phdr.len - d_framelen);
 
395
}     
 
396
 
 
397
 
 
398
// give back layer2 frame
 
399
void *Pcap::get_frame(void *hwframe, size_t len)
 
400
{
 
401
       
 
402
        // switch over the hardware-layer of the packet 
 
403
        switch (d_datalink) {
 
404
        case DLT_EN10MB:
 
405
                memcpy(hwframe, &d_ether, (len<sizeof(d_ether)?len:sizeof(d_ether)));
 
406
                break;
 
407
        default:
 
408
                return NULL;
 
409
        }
 
410
        return hwframe;
 
411
}
 
412
 
 
413
int Pcap::timeout(struct timeval tv)
 
414
{
 
415
        d_tv = tv;
 
416
        d_timeout = false;
 
417
        return 0;
 
418
}
 
419
 
 
420
bool Pcap::timeout()
 
421
{
 
422
        return d_timeout;
 
423
}
 
424
 
 
425
} // namespace usipp