Skip to content

Commit 0847b9e

Browse files
author
shadowy-pycoder
committed
Made OpenLive accept custom packet writers
1 parent 061b2f4 commit 0847b9e

File tree

4 files changed

+112
-117
lines changed

4 files changed

+112
-117
lines changed

.gitignore

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

2929
*.prof
3030

31-
*.pcap*
31+
*.pcap*
32+
*.txt

cmd/mshark/cli.go

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ import (
88
"path/filepath"
99
"strings"
1010
"text/tabwriter"
11+
"time"
1112

1213
ms "github.com/shadowy-pycoder/mshark"
14+
"github.com/shadowy-pycoder/mshark/mpcap"
15+
"github.com/shadowy-pycoder/mshark/mpcapng"
1316
)
1417

1518
const usagePrefix = `
@@ -47,28 +50,29 @@ func displayInterfaces() error {
4750
return w.Flush()
4851
}
4952

53+
func createFile(ext string) (*os.File, error) {
54+
path := fmt.Sprintf("./mshark_%s.%s", time.Now().UTC().Format("20060102_150405"), ext)
55+
f, err := os.OpenFile(filepath.FromSlash(path), os.O_CREATE|os.O_WRONLY, 0644)
56+
if err != nil {
57+
return nil, fmt.Errorf("failed to open file: %v", err)
58+
}
59+
return f, nil
60+
}
61+
5062
func root(args []string) error {
5163
conf := ms.Config{}
5264

5365
flags := flag.NewFlagSet("mshark", flag.ExitOnError)
54-
flags.StringVar(&conf.Iface, "i", "any", "The name of the network interface. Example: eth0")
55-
flags.IntVar(&conf.Snaplen, "s", 0, "The maximum length of each packet snapshot. Defaults to 65535.")
66+
iface := flags.String("i", "any", "The name of the network interface. Example: eth0")
67+
snaplen := flags.Int("s", 0, "The maximum length of each packet snapshot. Defaults to 65535.")
5668
flags.BoolFunc("p", `Promiscuous mode. This setting is ignored for "any" interface. Defaults to false.`, func(flagValue string) error {
5769
conf.Promisc = true
5870
return nil
5971
})
6072
flags.DurationVar(&conf.Timeout, "t", 0, "The maximum duration of the packet capture process. Example: 5s")
6173
flags.IntVar(&conf.PacketCount, "c", 0, "The maximum number of packets to capture.")
6274
flags.StringVar(&conf.Expr, "e", "", `BPF filter expression. Example: "ip proto tcp"`)
63-
path := flags.String("f", "", "File path to write captured packet data to. Defaults to stdout.")
64-
flags.BoolFunc("pcap", "Create a PCAP file in the current working directory.", func(flagValue string) error {
65-
conf.Pcap = true
66-
return nil
67-
})
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-
})
75+
ext := flags.String("f", "", "File extension to write captured packet data to. Supported values: txt, pcap, pcapng")
7276
flags.BoolFunc("D", "Display list of interfaces and exit.", func(flagValue string) error {
7377
if err := displayInterfaces(); err != nil {
7478
fmt.Fprintf(os.Stderr, "mshark: %v\n", err)
@@ -87,16 +91,67 @@ func root(args []string) error {
8791
return err
8892
}
8993

90-
if *path != "" {
91-
f, err := os.OpenFile(filepath.FromSlash(*path), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
92-
if err != nil {
93-
return fmt.Errorf("failed to open file: %v", err)
94+
// getting network interface from the provided name
95+
in, err := ms.InterfaceByName(*iface)
96+
if err != nil {
97+
return err
98+
}
99+
conf.Device = in
100+
101+
// checking snaplen
102+
if *snaplen <= 0 || *snaplen > 65535 {
103+
*snaplen = 65535
104+
}
105+
conf.Snaplen = *snaplen
106+
107+
// creating writers depending on a file extension
108+
var pw ms.PacketWriter
109+
if *ext != "" {
110+
switch *ext {
111+
case "txt":
112+
f, err := createFile(*ext)
113+
if err != nil {
114+
return err
115+
}
116+
defer f.Close()
117+
pw = ms.NewWriter(f)
118+
case "pcap":
119+
f, err := createFile(*ext)
120+
if err != nil {
121+
return err
122+
}
123+
defer f.Close()
124+
pw = mpcap.NewWriter(f)
125+
case "pcapng":
126+
f, err := createFile(*ext)
127+
if err != nil {
128+
return err
129+
}
130+
defer f.Close()
131+
pw = mpcapng.NewWriter(f)
132+
default:
133+
return fmt.Errorf("unsupported file format: %s", *ext)
94134
}
95-
defer f.Close()
96-
conf.File = f
135+
} else {
136+
pw = ms.NewWriter(os.Stdout)
97137
}
98138

99-
if err := ms.OpenLive(&conf); err != nil {
139+
// writing headers
140+
switch t := pw.(type) {
141+
case *ms.Writer:
142+
if err := t.WriteHeader(&conf); err != nil {
143+
return err
144+
}
145+
case *mpcap.Writer:
146+
if err := t.WriteHeader(conf.Snaplen); err != nil {
147+
return err
148+
}
149+
case *mpcapng.Writer:
150+
if err := t.WriteHeader("mshark", conf.Device, conf.Expr, conf.Snaplen); err != nil {
151+
return err
152+
}
153+
}
154+
if err := ms.OpenLive(&conf, pw); err != nil {
100155
return err
101156
}
102157
return nil

mshark.go

Lines changed: 30 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,18 @@ import (
66
"io"
77
"net"
88
"os"
9-
"path/filepath"
109
"time"
1110

1211
"github.com/mdlayher/packet"
1312
"github.com/packetcap/go-pcap/filter"
14-
"github.com/shadowy-pycoder/mshark/mpcap"
15-
"github.com/shadowy-pycoder/mshark/mpcapng"
1613
"golang.org/x/net/bpf"
1714
"golang.org/x/sys/unix"
1815
)
1916

17+
type PacketWriter interface {
18+
WritePacket(timestamp time.Time, data []byte) error
19+
}
20+
2021
type layers struct {
2122
ether EthernetFrame
2223
ip IPv4Packet
@@ -29,39 +30,31 @@ type layers struct {
2930
}
3031

3132
type Config struct {
32-
Iface string // The name of the network interface ("any" means listen on all interfaces).
33-
Snaplen int // The maximum length of each packet snapshot.
34-
Promisc bool // Promiscuous mode. This setting is ignored for "any" interface.
35-
Timeout time.Duration // The maximum duration of the packet capture process.
36-
PacketCount int // The maximum number of packets to capture.
37-
Expr string // BPF filter expression.
38-
File io.Writer // File to write captured packet data to.
39-
Pcap bool // Create a PCAP file in the current working directory.
40-
PcapNG bool // Create a PCAPNG file in the current working directory.
33+
Device *net.Interface // The name of the network interface ("any" means listen on all interfaces).
34+
Snaplen int // The maximum length of each packet snapshot.
35+
Promisc bool // Promiscuous mode. This setting is ignored for "any" interface.
36+
Timeout time.Duration // The maximum duration of the packet capture process.
37+
PacketCount int // The maximum number of packets to capture.
38+
Expr string // BPF filter expression.
4139
}
4240

4341
type Writer struct {
44-
w io.Writer
45-
layers layers
46-
packetNum uint64
42+
w io.Writer
43+
layers layers
44+
packets uint64
4745
}
4846

4947
// NewWriter creates a new mshark Writer.
50-
//
51-
// If the io.Writer is nil, the Writer will write to stdout.
5248
func NewWriter(w io.Writer) *Writer {
53-
if w == nil {
54-
w = os.Stdout
55-
}
5649
return &Writer{w: w}
5750
}
5851

5952
// WritePacket writes a packet to the writer, along with its timestamp.
6053
//
6154
// Timestamps are to be generated by the calling code.
6255
func (mw *Writer) WritePacket(timestamp time.Time, data []byte) error {
63-
mw.packetNum++
64-
fmt.Fprintf(mw.w, "- Packet: %d Timestamp: %s\n", mw.packetNum, timestamp.Format("2006-01-02T15:04:05-0700"))
56+
mw.packets++
57+
fmt.Fprintf(mw.w, "- Packet: %d Timestamp: %s\n", mw.packets, timestamp.Format("2006-01-02T15:04:05-0700"))
6558
fmt.Fprintln(mw.w, "==================================================================")
6659
if err := mw.layers.ether.Parse(data); err != nil {
6760
return err
@@ -134,7 +127,7 @@ func (mw *Writer) WritePacket(timestamp time.Time, data []byte) error {
134127
// - Timeout: 5s
135128
// - Number of Packets: 0
136129
// - BPF Filter: "ip proto tcp"
137-
func (mw *Writer) WriteHeader(c *Config, in *net.Interface, snaplen int) error {
130+
func (mw *Writer) WriteHeader(c *Config) error {
138131
_, err := fmt.Fprintf(mw.w, `- Interface: %s
139132
- Snapshot Length: %d
140133
- Promiscuous Mode: %v
@@ -143,9 +136,9 @@ func (mw *Writer) WriteHeader(c *Config, in *net.Interface, snaplen int) error {
143136
- BPF Filter: %q
144137
145138
`,
146-
in.Name,
147-
snaplen,
148-
in.Name != "any" && c.Promisc,
139+
c.Device.Name,
140+
c.Snaplen,
141+
c.Device.Name != "any" && c.Promisc,
149142
c.Timeout,
150143
c.PacketCount,
151144
c.Expr,
@@ -178,7 +171,7 @@ func InterfaceByName(name string) (*net.Interface, error) {
178171
return in, nil
179172
}
180173

181-
func OpenLive(conf *Config) error {
174+
func OpenLive(conf *Config, pw PacketWriter) error {
182175

183176
packetcfg := packet.Config{}
184177

@@ -197,14 +190,8 @@ func OpenLive(conf *Config) error {
197190
packetcfg.Filter = raw
198191
}
199192

200-
// getting interface
201-
in, err := InterfaceByName(conf.Iface)
202-
if err != nil {
203-
return err
204-
}
205-
206193
// opening connection
207-
c, err := packet.Listen(in, packet.Raw, unix.ETH_P_ALL, &packetcfg)
194+
c, err := packet.Listen(conf.Device, packet.Raw, unix.ETH_P_ALL, &packetcfg)
208195
if err != nil {
209196
if errors.Is(err, os.ErrPermission) {
210197
return fmt.Errorf("permission denied (try setting CAP_NET_RAW capability): %v", err)
@@ -213,7 +200,7 @@ func OpenLive(conf *Config) error {
213200
}
214201

215202
// setting promisc mode
216-
if in.Name != "any" {
203+
if conf.Device.Name != "any" {
217204
if err := c.SetPromiscuous(conf.Promisc); err != nil {
218205
return fmt.Errorf("unable to set promiscuous mode: %v", err)
219206
}
@@ -225,56 +212,17 @@ func OpenLive(conf *Config) error {
225212
return fmt.Errorf("unable to set timeout: %v", err)
226213
}
227214
}
228-
// snaplen
229-
snaplen := conf.Snaplen
230-
if snaplen <= 0 || snaplen > 65535 {
231-
snaplen = 65535
232-
}
233-
b := make([]byte, snaplen)
234-
235-
// file to write packets
236-
f := NewWriter(conf.File)
237-
if err := f.WriteHeader(conf, in, snaplen); err != nil {
238-
return err
239-
}
240-
241-
// pcap file to write packets
242-
var pcap *mpcap.Writer
243-
if conf.Pcap {
244-
path := fmt.Sprintf("./mshark_%s.pcap", time.Now().UTC().Format("20060102_150405"))
245-
fpcap, err := os.OpenFile(filepath.FromSlash(path), os.O_CREATE|os.O_WRONLY, 0644)
246-
if err != nil {
247-
return fmt.Errorf("failed to open file: %v", err)
248-
}
249-
defer fpcap.Close()
250-
pcap = mpcap.NewWriter(fpcap)
251-
if err := pcap.WriteHeader(snaplen); err != nil {
252-
return err
253-
}
254-
}
255-
256-
// pcapng file to write packets
257-
var pcapng *mpcapng.Writer
258-
if conf.PcapNG {
259-
path := fmt.Sprintf("./mshark_%s.pcapng", time.Now().UTC().Format("20060102_150405"))
260-
fpcapng, err := os.OpenFile(filepath.FromSlash(path), os.O_CREATE|os.O_WRONLY, 0644)
261-
if err != nil {
262-
return fmt.Errorf("failed to open file: %v", err)
263-
}
264-
defer fpcapng.Close()
265-
pcapng = mpcapng.NewWriter(fpcapng)
266-
if err := pcapng.WriteHeader("mshark", in, conf.Expr, snaplen); err != nil {
267-
return err
268-
}
269-
}
270215

271216
defer func() {
272217
stats, err := c.Stats()
273218
if err != nil {
274219
fmt.Fprintf(os.Stderr, "failed to fetch stats: %v", err)
275220
} else {
276-
fmt.Fprintf(f.w, "- Packets: %d, Drops: %d, Freeze Queue Count: %d\n- Packets Captured: %d\n",
277-
stats.Packets, stats.Drops, stats.FreezeQueueCount, f.packetNum)
221+
fmt.Printf("- Packets: %d, Drops: %d, Freeze Queue Count: %d\n",
222+
stats.Packets, stats.Drops, stats.FreezeQueueCount)
223+
if w, ok := pw.(*Writer); ok {
224+
fmt.Printf("- Packets Captured: %d\n", w.packets)
225+
}
278226
}
279227
// close Conn
280228
c.Close()
@@ -287,6 +235,8 @@ func OpenLive(conf *Config) error {
287235
}
288236
infinity := count == 0
289237

238+
b := make([]byte, conf.Snaplen)
239+
290240
for i := 0; infinity || i < count; i++ {
291241
n, _, err := c.ReadFrom(b)
292242
if err != nil {
@@ -296,22 +246,7 @@ func OpenLive(conf *Config) error {
296246
return fmt.Errorf("failed to read Ethernet frame: %v", err)
297247
}
298248

299-
timestamp := time.Now().UTC()
300-
data := b[:n]
301-
302-
if conf.Pcap {
303-
if err := pcap.WritePacket(timestamp, data); err != nil {
304-
return err
305-
}
306-
}
307-
308-
if conf.PcapNG {
309-
if err := pcapng.WritePacket(timestamp, data); err != nil {
310-
return err
311-
}
312-
}
313-
314-
if err := f.WritePacket(timestamp, data); err != nil {
249+
if err := pw.WritePacket(time.Now().UTC(), b[:n]); err != nil {
315250
return err
316251
}
317252
}

mshark_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ import (
77

88
func BenchmarkOpenLive(b *testing.B) {
99
b.ResetTimer()
10+
in, err := InterfaceByName("any")
11+
if err != nil {
12+
b.Fatal(err)
13+
}
1014
conf := Config{
11-
Iface: "any",
15+
Device: in,
1216
Snaplen: 1600,
1317
Promisc: true,
1418
PacketCount: b.N,
15-
File: io.Discard,
1619
}
17-
if err := OpenLive(&conf); err != nil {
20+
pw := NewWriter(io.Discard)
21+
if err := OpenLive(&conf, pw); err != nil {
1822
b.Fatal(err)
1923
}
2024
}

0 commit comments

Comments
 (0)