Skip to content

Commit 9c44213

Browse files
committed
Add nanosecond timestamp resolution to pcap.
In case libpcap (version < 1.5), the file, hardware, or os does not support nanosecond resolution, microsecond is used. Actual resolution can be queried with Resolution(). This fixes google#298
1 parent 97e2a55 commit 9c44213

File tree

5 files changed

+95
-17
lines changed

5 files changed

+95
-17
lines changed

pcap/doc.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ This package is meant to be used with its parent,
1212
http://github.com/google/gopacket, although it can also be used independently
1313
if you just want to get packet data from the wire.
1414
15+
Depending on libpcap version, os support, or file timestamp resolution,
16+
nanosecond resolution is used for the internal timestamps. Returned timestamps
17+
are always scaled to nanosecond resolution due to the usage of time.Time.
18+
libpcap must be at least version 1.5 to support nanosecond timestamps. OpenLive
19+
suports only microsecond resolution.
20+
1521
Reading PCAP Files
1622
1723
The following code can be used to read in data from a pcap file.
@@ -28,7 +34,7 @@ The following code can be used to read in data from a pcap file.
2834
Reading Live Packets
2935
3036
The following code can be used to read in data from a live device, in this case
31-
"eth0".
37+
"eth0". Be aware, that OpenLive only supports microsecond resolution.
3238
3339
if handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever); err != nil {
3440
panic(err)

pcap/pcap.go

+71-11
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,42 @@ package pcap
3333
// See http://upstream-tracker.org/versions/libpcap.html
3434
3535
#ifndef PCAP_ERROR_TSTAMP_PRECISION_NOTSUP // < v1.5
36+
#define PCAP_ERROR_TSTAMP_PRECISION_NOTSUP -12
3637
3738
int pcap_set_immediate_mode(pcap_t *p, int mode) {
3839
return PCAP_ERROR;
3940
}
4041
42+
#define PCAP_TSTAMP_PRECISION_MICRO 0
43+
#define PCAP_TSTAMP_PRECISION_NANO 1
44+
45+
#ifdef WIN32
46+
pcap_t *pcap_hopen_offline_with_tstamp_precision(intptr_t osfd, u_int precision,
47+
char *errbuf) {
48+
return pcap_hopen_offline(osdf, errbuf);
49+
}
50+
#else
51+
pcap_t *pcap_fopen_offline_with_tstamp_precision(FILE *fp, u_int precision,
52+
char *errbuf) {
53+
return pcap_fopen_offline(fp, errbuf);
54+
}
55+
#endif
56+
57+
pcap_t *pcap_open_offline_with_tstamp_precision(const char *fname, u_int precision,
58+
char *errbuf) {
59+
return pcap_open_offline(fname, errbuf);
60+
}
61+
62+
int pcap_set_tstamp_precision(pcap_t *p, int tstamp_precision) {
63+
if (tstamp_precision == PCAP_TSTAMP_PRECISION_MICRO)
64+
return 0;
65+
return PCAP_ERROR_TSTAMP_PRECISION_NOTSUP;
66+
}
67+
68+
int pcap_get_tstamp_precision(pcap_t *p) {
69+
return PCAP_TSTAMP_PRECISION_MICRO
70+
}
71+
4172
#ifndef PCAP_TSTAMP_HOST // < v1.2
4273
4374
int pcap_set_tstamp_type(pcap_t* p, int t) { return -1; }
@@ -159,12 +190,13 @@ type Handle struct {
159190
// This must be the first entry to ensure alignment for sync.atomic
160191
stop uint64
161192
// cptr is the handle for the actual pcap C object.
162-
cptr *C.pcap_t
163-
timeout time.Duration
164-
device string
165-
deviceIndex int
166-
mu sync.Mutex
167-
closeMu sync.Mutex
193+
cptr *C.pcap_t
194+
timeout time.Duration
195+
device string
196+
deviceIndex int
197+
mu sync.Mutex
198+
closeMu sync.Mutex
199+
nanoSecsFactor int64
168200

169201
// Since pointers to these objects are passed into a C function, if
170202
// they're declared locally then the Go compiler thinks they may have
@@ -239,7 +271,8 @@ func timeoutMillis(timeout time.Duration) C.int {
239271
// OpenLive opens a device and returns a *Handle.
240272
// It takes as arguments the name of the device ("eth0"), the maximum size to
241273
// read for each packet (snaplen), whether to put the interface in promiscuous
242-
// mode, and a timeout.
274+
// mode, and a timeout. Warning: this function supports only microsecond timestamps.
275+
// For nanosecond resolution use an InactiveHandle.
243276
//
244277
// See the package documentation for important details regarding 'timeout'.
245278
func OpenLive(device string, snaplen int32, promisc bool, timeout time.Duration) (handle *Handle, _ error) {
@@ -268,6 +301,7 @@ func OpenLive(device string, snaplen int32, promisc bool, timeout time.Duration)
268301
if p.cptr == nil {
269302
return nil, errors.New(C.GoString(buf))
270303
}
304+
p.nanoSecsFactor = 1000
271305

272306
// Only set the PCAP handle into non-blocking mode if we have a timeout
273307
// greater than zero. If the user wants to block forever, we'll let libpcap
@@ -282,22 +316,33 @@ func OpenLive(device string, snaplen int32, promisc bool, timeout time.Duration)
282316
return p, nil
283317
}
284318

285-
// OpenOffline opens a file and returns its contents as a *Handle.
319+
// OpenOffline opens a file and returns its contents as a *Handle. Depending on libpcap support and
320+
// on the timestamp resolution used in the file, nanosecond or microsecond resolution is used
321+
// internally. All returned timestamps are scaled to nanosecond resolution. Resolution() can be used
322+
// to query the actual resolution used.
286323
func OpenOffline(file string) (handle *Handle, err error) {
287324
buf := (*C.char)(C.calloc(errorBufferSize, 1))
288325
defer C.free(unsafe.Pointer(buf))
289326
cf := C.CString(file)
290327
defer C.free(unsafe.Pointer(cf))
291328

292-
cptr := C.pcap_open_offline(cf, buf)
329+
cptr := C.pcap_open_offline_with_tstamp_precision(cf, C.PCAP_TSTAMP_PRECISION_NANO, buf)
293330
if cptr == nil {
294331
return nil, errors.New(C.GoString(buf))
295332
}
296333
h := &Handle{cptr: cptr}
334+
if C.pcap_get_tstamp_precision(cptr) == C.PCAP_TSTAMP_PRECISION_NANO {
335+
h.nanoSecsFactor = 1
336+
} else {
337+
h.nanoSecsFactor = 1000
338+
}
297339
return h, nil
298340
}
299341

300-
// OpenOfflineFile returns contents of input file as a *Handle.
342+
// OpenOfflineFile returns contents of input file as a *Handle. Depending on libpcap support and
343+
// on the timestamp resolution used in the file, nanosecond or microsecond resolution is used
344+
// internally. All returned timestamps are scaled to nanosecond resolution. Resolution() can be used
345+
// to query the actual resolution used.
301346
func OpenOfflineFile(file *os.File) (handle *Handle, err error) {
302347
return openOfflineFile(file)
303348
}
@@ -408,7 +453,7 @@ func (p *Handle) getNextBufPtrLocked(ci *gopacket.CaptureInfo) error {
408453
// got a packet, set capture info and return
409454
sec := int64(p.pkthdr.ts.tv_sec)
410455
// convert micros to nanos
411-
nanos := int64(p.pkthdr.ts.tv_usec) * 1000
456+
nanos := int64(p.pkthdr.ts.tv_usec) * p.nanoSecsFactor
412457

413458
ci.Timestamp = time.Unix(sec, nanos)
414459
ci.CaptureLength = int(p.pkthdr.caplen)
@@ -923,6 +968,14 @@ func (p *Handle) SnapLen() int {
923968
return int(len)
924969
}
925970

971+
// Resolution returns the timestamp resolution of acquired timestamps before scaling to NanosecondTimestampResolution.
972+
func (p *Handle) Resolution() gopacket.TimestampResolution {
973+
if p.nanoSecsFactor == 1 {
974+
return gopacket.TimestampResolutionMillisecond
975+
}
976+
return gopacket.TimestampResolutionNanosecond
977+
}
978+
926979
// TimestampSource tells PCAP which type of timestamp to use for packets.
927980
type TimestampSource C.int
928981

@@ -968,6 +1021,8 @@ func (p *InactiveHandle) Error() error {
9681021
// Activate activates the handle. The current InactiveHandle becomes invalid
9691022
// and all future function calls on it will fail.
9701023
func (p *InactiveHandle) Activate() (*Handle, error) {
1024+
// ignore error with set_tstamp_precision, since the actual precision is queried later anyway
1025+
C.pcap_set_tstamp_precision(p.cptr, C.PCAP_TSTAMP_PRECISION_NANO)
9711026
err := activateError(C.pcap_activate(p.cptr))
9721027
if err != aeNoError {
9731028
if err == aeWarning {
@@ -981,6 +1036,11 @@ func (p *InactiveHandle) Activate() (*Handle, error) {
9811036
device: p.device,
9821037
deviceIndex: p.deviceIndex,
9831038
}
1039+
if C.pcap_get_tstamp_precision(h.cptr) == C.PCAP_TSTAMP_PRECISION_NANO {
1040+
h.nanoSecsFactor = 1
1041+
} else {
1042+
h.nanoSecsFactor = 1000
1043+
}
9841044
p.cptr = nil
9851045
return h, nil
9861046
}

pcap/pcap_unix.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,15 @@ func openOfflineFile(file *os.File) (handle *Handle, err error) {
7979
defer C.free(unsafe.Pointer(cmode))
8080
cf := C.fdopen(C.int(file.Fd()), cmode)
8181

82-
cptr := C.pcap_fopen_offline(cf, buf)
82+
cptr := C.pcap_fopen_offline_with_tstamp_precision(cf, C.PCAP_TSTAMP_PRECISION_NANO, buf)
8383
if cptr == nil {
8484
return nil, errors.New(C.GoString(buf))
8585
}
86-
return &Handle{cptr: cptr}, nil
86+
h := &Handle{cptr: cptr}
87+
if C.pcap_get_tstamp_precision(cptr) == C.PCAP_TSTAMP_PRECISION_NANO {
88+
h.nanoSecsFactor = 1
89+
} else {
90+
h.nanoSecsFactor = 1000
91+
}
92+
return h, nil
8793
}

pcap/pcap_windows.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,15 @@ func openOfflineFile(file *os.File) (handle *Handle, err error) {
3636
defer C.free(unsafe.Pointer(buf))
3737
cf := C.intptr_t(file.Fd())
3838

39-
cptr := C.pcap_hopen_offline(cf, buf)
39+
cptr := C.pcap_hopen_offline_with_tstamp_precision(cf, C.PCAP_TSTAMP_PRECISION_NANO, buf)
4040
if cptr == nil {
4141
return nil, errors.New(C.GoString(buf))
4242
}
43-
return &Handle{cptr: cptr}, nil
43+
h := &Handle{cptr: cptr}
44+
if C.pcap_get_tstamp_precision(cptr) == C.PCAP_TSTAMP_PRECISION_NANO {
45+
h.nanoSecsFactor = 1
46+
} else {
47+
h.nanoSecsFactor = 1000
48+
}
49+
return h, nil
4450
}

pcap/pcapnggo_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func TestPCAPGoNgWrite(t *testing.T) {
2525
}
2626
data := []byte{0xab, 0xcd, 0xef, 0x01, 0x02, 0x03, 0x04}
2727
ci := gopacket.CaptureInfo{
28-
Timestamp: time.Unix(12345667, 1234567000),
28+
Timestamp: time.Unix(12345667, 1234567123),
2929
Length: 700,
3030
CaptureLength: len(data),
3131
}

0 commit comments

Comments
 (0)