Skip to content

Commit 375edd2

Browse files
author
shadowy-pycoder
committed
Added mpcapng writer for creating PCAPNG files
1 parent acd2af2 commit 375edd2

File tree

6 files changed

+443
-116
lines changed

6 files changed

+443
-116
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ tls/
2828

2929
*.prof
3030

31-
*.pcap
31+
*.pcap*

cmd/mshark/cli.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ func root(args []string) error {
6565
conf.Pcap = true
6666
return nil
6767
})
68+
flags.BoolFunc("pcapng", "Create a PCAPNG file in the current working directory.", func(flagValue string) error {
69+
conf.PcapNG = true
70+
return nil
71+
})
6872
flags.BoolFunc("D", "Display list of interfaces and exit.", func(flagValue string) error {
6973
if err := displayInterfaces(); err != nil {
7074
fmt.Fprintf(os.Stderr, "mshark: %v\n", err)

mpcap/write.go

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package mpcap
22

33
import (
4-
"encoding/binary"
54
"fmt"
65
"io"
76
"time"
8-
"unsafe"
7+
8+
"github.com/shadowy-pycoder/mshark/native"
99
)
1010

1111
// https://wiki.wireshark.org/Development/LibpcapFileFormat/
@@ -18,33 +18,26 @@ const (
1818
network uint32 = 1
1919
)
2020

21-
var nativeEndian binary.ByteOrder
22-
23-
func init() {
24-
// https://stackoverflow.com/questions/51332658/any-better-way-to-check-endianness-in-go
25-
buf := [2]byte{}
26-
*(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD)
27-
28-
switch buf {
29-
case [2]byte{0xCD, 0xAB}:
30-
nativeEndian = binary.LittleEndian
31-
case [2]byte{0xAB, 0xCD}:
32-
nativeEndian = binary.BigEndian
33-
default:
34-
panic("Could not determine native endianness.")
35-
}
36-
}
21+
var nativeEndian = native.Endian
3722

38-
type PcapWriter struct {
23+
type Writer struct {
3924
w io.Writer
4025
buf [16]byte
4126
}
4227

43-
func NewPcapWriter(w io.Writer) *PcapWriter {
44-
return &PcapWriter{w: w}
28+
// NewWriter creates a new PCAP Writer that writes to the given io.Writer.
29+
func NewWriter(w io.Writer) *Writer {
30+
return &Writer{w: w}
4531
}
4632

47-
func (pw *PcapWriter) WriteGlobalHeader(snaplen int) error {
33+
// WriteHeader writes a global header block to the pcap file.
34+
//
35+
// The global header block contains metadata about the capture, such as the
36+
// timestamp format and the maximum packet length.
37+
//
38+
// See https://wiki.wireshark.org/Development/LibpcapFileFormat for more
39+
// information about the pcap file format.
40+
func (pw *Writer) WriteHeader(snaplen int) error {
4841
var buf [24]byte
4942
nativeEndian.PutUint32(buf[0:4], magicNumber)
5043
nativeEndian.PutUint16(buf[4:6], versionMajor)
@@ -57,7 +50,7 @@ func (pw *PcapWriter) WriteGlobalHeader(snaplen int) error {
5750
return err
5851
}
5952

60-
func (pw *PcapWriter) writePacketHeader(timestamp time.Time, packetLen int) error {
53+
func (pw *Writer) writePacketHeader(timestamp time.Time, packetLen int) error {
6154
secs := timestamp.Unix()
6255
msecs := timestamp.Nanosecond() / 1e6
6356
nativeEndian.PutUint32(pw.buf[0:4], uint32(secs))
@@ -68,7 +61,16 @@ func (pw *PcapWriter) writePacketHeader(timestamp time.Time, packetLen int) erro
6861
return err
6962
}
7063

71-
func (pw *PcapWriter) WritePacket(timestamp time.Time, data []byte) error {
64+
// WritePacket writes a packet to the pcap file.
65+
//
66+
// The packet is written as a packet header plus the packet data.
67+
// The timestamp is written as the number of seconds since the epoch,
68+
// and the packet data is written as a sequence of bytes of the length
69+
// specified in the packet header.
70+
//
71+
// See https://wiki.wireshark.org/Development/LibpcapFileFormat for more
72+
// information about the pcap file format.
73+
func (pw *Writer) WritePacket(timestamp time.Time, data []byte) error {
7274
if err := pw.writePacketHeader(timestamp, len(data)); err != nil {
7375
return fmt.Errorf("error writing packet header: %v", err)
7476
}

mpcapng/write.go

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
package mpcapng
2+
3+
import (
4+
"bytes"
5+
"encoding/binary"
6+
"io"
7+
"net"
8+
"os/exec"
9+
"time"
10+
11+
"github.com/shadowy-pycoder/mshark/native"
12+
)
13+
14+
// https://.com/
15+
const (
16+
shbBlockType uint32 = 0x0a0d0d0a
17+
byteOrderMagic uint32 = 0x1a2b3c4d
18+
versionMajor uint16 = 0x0001
19+
versionMinor uint16 = 0x0000
20+
sectionLen uint64 = 0xffffffffffffffff
21+
shbHardwareCode uint16 = 0x0002
22+
shbOSCode uint16 = 0x0003
23+
shbUserAppCode uint16 = 0x0004
24+
idbBlockType uint32 = 0x00000001
25+
linkType uint16 = 1
26+
reserved uint16 = 0
27+
timeRes uint8 = 0x03
28+
ifNameCode uint16 = 0x0002
29+
ifDescCode uint16 = 0x0003
30+
ifMACCode uint16 = 0x0006
31+
ifTsResCode uint16 = 0x0009
32+
ifFilterCode uint16 = 0x000b
33+
ifOSCode uint16 = 0x000c
34+
epbBlockType uint32 = 0x00000006
35+
interfaceId uint32 = 0x00000000 // only support one IDB
36+
)
37+
38+
var (
39+
nativeEndian binary.ByteOrder = native.Endian
40+
zero []byte = []byte{0}
41+
)
42+
43+
type Writer struct {
44+
w io.Writer
45+
}
46+
47+
// NewWriter creates a new PCAPNG Writer that writes to the given io.Writer.
48+
func NewWriter(w io.Writer) *Writer {
49+
return &Writer{w: w}
50+
}
51+
52+
// WriteHeader writes a Section Header Block (SHB) and an Interface Description Block (IDB)
53+
// to the pcapng file.
54+
//
55+
// The SHB contains metadata about the capture, and the IDB describes the interface
56+
// that the packets were captured on.
57+
func (pw *Writer) WriteHeader(app string, in *net.Interface, expr string, snaplen int) error {
58+
if err := pw.writeSHB(app); err != nil {
59+
return err
60+
}
61+
if err := pw.writeIDB(in, expr, snaplen); err != nil {
62+
return err
63+
}
64+
return nil
65+
}
66+
67+
// writeSHB writes a Section Header Block (SHB) to the file.
68+
//
69+
// https://www.ietf.org/archive/id/draft-tuexen-opsawg--05.html#section_shb
70+
func (pw *Writer) writeSHB(app string) error {
71+
options, err := pw.writeShbOptions(app)
72+
if err != nil {
73+
return err
74+
}
75+
blockLen := 4 + 4 + 4 + 2 + 2 + 8 + len(options) + 4
76+
buf := bytes.NewBuffer(make([]byte, 0, blockLen))
77+
binary.Write(buf, nativeEndian, shbBlockType)
78+
binary.Write(buf, nativeEndian, uint32(blockLen))
79+
binary.Write(buf, nativeEndian, byteOrderMagic)
80+
binary.Write(buf, nativeEndian, versionMajor)
81+
binary.Write(buf, nativeEndian, versionMinor)
82+
binary.Write(buf, nativeEndian, sectionLen)
83+
binary.Write(buf, nativeEndian, options)
84+
binary.Write(buf, nativeEndian, uint32(blockLen))
85+
_, err = pw.w.Write(buf.Bytes())
86+
return err
87+
}
88+
89+
func pad(size int) int {
90+
return (4 - (size & 3)) & 3
91+
}
92+
93+
func gethwinfo() ([]byte, []byte, error) {
94+
hwinfo, err := exec.Command("sh", "-c", "lscpu | grep 'Model name' | cut -f 2 -d ':' | awk '{$1=$1}1'").Output()
95+
if err != nil {
96+
return nil, nil, err
97+
}
98+
hwinfo = bytes.TrimRight(hwinfo, "\n")
99+
osinfo, err := exec.Command("sh", "-c", "uname -orm").Output()
100+
if err != nil {
101+
return nil, nil, err
102+
}
103+
osinfo = bytes.TrimRight(osinfo, "\n")
104+
return hwinfo, osinfo, nil
105+
}
106+
107+
func (pw *Writer) writeShbOptions(app string) ([]byte, error) {
108+
hwinfo, osinfo, err := gethwinfo()
109+
if err != nil {
110+
return nil, err
111+
}
112+
hwLen := len(hwinfo)
113+
hPad := pad(len(hwinfo))
114+
osLen := len(osinfo)
115+
osPad := pad(len(osinfo))
116+
appLen := len(app)
117+
userPad := pad(len(app))
118+
buflen := (2 + // shb_hardware code
119+
2 + // shb_hardware length
120+
hwLen + // shb_hardware
121+
hPad + // padding
122+
2 + // shb_os code
123+
2 + // shb_os length
124+
osLen + // shb_os
125+
osPad + // padding
126+
2 + // shb_userappl code
127+
2 + // shb_userappl length
128+
appLen + // shb_userappl
129+
userPad + // padding
130+
2 + // opt_endofopt
131+
2) // opt_endofopt length (must be 0)
132+
buf := bytes.NewBuffer(make([]byte, 0, buflen))
133+
binary.Write(buf, nativeEndian, shbHardwareCode)
134+
binary.Write(buf, nativeEndian, uint16(hwLen))
135+
binary.Write(buf, nativeEndian, hwinfo)
136+
buf.Write(bytes.Repeat(zero, hPad))
137+
binary.Write(buf, nativeEndian, shbOSCode)
138+
binary.Write(buf, nativeEndian, uint16(osLen))
139+
binary.Write(buf, nativeEndian, osinfo)
140+
buf.Write(bytes.Repeat(zero, osPad))
141+
binary.Write(buf, nativeEndian, shbUserAppCode)
142+
binary.Write(buf, nativeEndian, uint16(appLen))
143+
binary.Write(buf, nativeEndian, []byte(app))
144+
buf.Write(bytes.Repeat(zero, userPad))
145+
buf.Write(bytes.Repeat(zero, 4))
146+
return buf.Bytes(), nil
147+
}
148+
149+
// writeIDB writes an Interface Description Block (IDB) to the file.
150+
//
151+
// https://www.ietf.org/archive/id/draft-tuexen-opsawg--05.html#name-interface-description-block
152+
func (pw *Writer) writeIDB(in *net.Interface, expr string, snaplen int) error {
153+
options, err := pw.writeIdbOptions(in, expr)
154+
if err != nil {
155+
return err
156+
}
157+
blockLen := 4 + 4 + 2 + 2 + 4 + len(options) + 4
158+
buf := bytes.NewBuffer(make([]byte, 0, blockLen))
159+
binary.Write(buf, nativeEndian, idbBlockType)
160+
binary.Write(buf, nativeEndian, uint32(blockLen))
161+
binary.Write(buf, nativeEndian, linkType)
162+
binary.Write(buf, nativeEndian, reserved)
163+
binary.Write(buf, nativeEndian, uint32(snaplen))
164+
binary.Write(buf, nativeEndian, options)
165+
binary.Write(buf, nativeEndian, uint32(blockLen))
166+
_, err = pw.w.Write(buf.Bytes())
167+
return err
168+
}
169+
170+
func (pw *Writer) writeIdbOptions(in *net.Interface, expr string) ([]byte, error) {
171+
_, osinfo, err := gethwinfo()
172+
if err != nil {
173+
return nil, err
174+
}
175+
osLen := len(osinfo)
176+
osPad := pad(len(osinfo))
177+
ifName := in.Name
178+
ifNameLen := len(ifName)
179+
ifNamePad := pad(ifNameLen)
180+
exprLen := len(expr) + 1
181+
exprPad := pad(exprLen)
182+
buflen := (2 + // if_name code
183+
2 + // if_name length
184+
ifNameLen + // if_name
185+
ifNamePad + // padding
186+
2 + // if_description code
187+
2 + // if_description length
188+
ifNameLen + // if_description
189+
ifNamePad + // padding
190+
2 + // if_MACaddr code
191+
2 + // if_MACaddr length
192+
6 + // MAC address
193+
2 + // padding
194+
2 + // if_tsresol code
195+
2 + // if_tsresol length
196+
1 + // if_tsresol
197+
3 + // padding
198+
2 + // if_filter code
199+
2 + // if_filter length
200+
1 + // BPF string
201+
exprLen + // if_filter
202+
exprPad + // padding
203+
2 + // if_os code
204+
2 + // if_os length
205+
osLen + // if_os
206+
osPad + // padding
207+
2 + // opt_endofopt
208+
2) // opt_endofopt length (must be 0)
209+
buf := bytes.NewBuffer(make([]byte, 0, buflen))
210+
binary.Write(buf, nativeEndian, ifNameCode)
211+
binary.Write(buf, nativeEndian, uint16(ifNameLen))
212+
binary.Write(buf, nativeEndian, []byte(ifName))
213+
buf.Write(bytes.Repeat(zero, ifNamePad))
214+
binary.Write(buf, nativeEndian, ifDescCode)
215+
binary.Write(buf, nativeEndian, uint16(ifNameLen))
216+
binary.Write(buf, nativeEndian, []byte(ifName))
217+
buf.Write(bytes.Repeat(zero, ifNamePad))
218+
binary.Write(buf, nativeEndian, ifMACCode)
219+
binary.Write(buf, nativeEndian, uint16(6))
220+
if in.Name == "any" {
221+
buf.Write(bytes.Repeat(zero, 6))
222+
} else {
223+
binary.Write(buf, nativeEndian, in.HardwareAddr)
224+
}
225+
buf.Write(bytes.Repeat(zero, 2))
226+
binary.Write(buf, nativeEndian, ifTsResCode)
227+
binary.Write(buf, nativeEndian, uint16(1))
228+
binary.Write(buf, nativeEndian, timeRes)
229+
buf.Write(bytes.Repeat(zero, 3))
230+
binary.Write(buf, nativeEndian, ifFilterCode)
231+
binary.Write(buf, nativeEndian, uint16(exprLen))
232+
buf.Write(bytes.Repeat(zero, 1))
233+
binary.Write(buf, nativeEndian, []byte(expr))
234+
buf.Write(bytes.Repeat(zero, exprPad))
235+
binary.Write(buf, nativeEndian, ifOSCode)
236+
binary.Write(buf, nativeEndian, uint16(osLen))
237+
binary.Write(buf, nativeEndian, osinfo)
238+
buf.Write(bytes.Repeat(zero, osPad))
239+
buf.Write(bytes.Repeat(zero, 4))
240+
return buf.Bytes(), nil
241+
}
242+
243+
// WritePacket writes an Enhanced Packet Block (EPB) to the file.
244+
//
245+
// https://www.ietf.org/archive/id/draft-tuexen-opsawg--05.html#name-enhanced-packet-block
246+
func (pw *Writer) WritePacket(timestamp time.Time, data []byte) error {
247+
packetLen := len(data)
248+
blockLen := 4 + 4 + 4 + 4 + 4 + 4 + 4 + packetLen + 4
249+
binary.Write(pw.w, nativeEndian, epbBlockType)
250+
binary.Write(pw.w, nativeEndian, uint32(blockLen))
251+
binary.Write(pw.w, nativeEndian, interfaceId)
252+
msecs := uint64(timestamp.UnixMilli())
253+
binary.Write(pw.w, nativeEndian, uint32(msecs>>32))
254+
binary.Write(pw.w, nativeEndian, uint32(msecs&(1<<32-1)))
255+
binary.Write(pw.w, nativeEndian, uint32(packetLen))
256+
binary.Write(pw.w, nativeEndian, uint32(packetLen))
257+
if _, err := pw.w.Write(data); err != nil {
258+
return err
259+
}
260+
pw.w.Write(bytes.Repeat(zero, pad(packetLen)))
261+
binary.Write(pw.w, nativeEndian, uint32(blockLen))
262+
return nil
263+
}

0 commit comments

Comments
 (0)