-
Notifications
You must be signed in to change notification settings - Fork 0
/
mshark.go
246 lines (222 loc) · 6 KB
/
mshark.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
package mshark
import (
"errors"
"fmt"
"io"
"net"
"os"
"time"
"github.com/mdlayher/packet"
"github.com/packetcap/go-pcap/filter"
"github.com/shadowy-pycoder/mshark/layers"
"golang.org/x/net/bpf"
)
const unixEthPAll int = 0x03
var colorMap = map[int]string{
0: "\033[37m",
1: "\033[36m",
2: "\033[32m",
3: "\033[33m",
4: "\033[35m",
}
var _ PacketWriter = &Writer{}
type PacketWriter interface {
WritePacket(timestamp time.Time, data []byte) error
}
type Config struct {
Device *net.Interface // The name of the network interface ("any" means listen on all interfaces).
Snaplen int // The maximum length of each packet snapshot.
Promisc bool // Promiscuous mode. This setting is ignored for "any" interface.
Timeout time.Duration // The maximum duration of the packet capture process.
PacketCount int // The maximum number of packets to capture.
Expr string // BPF filter expression.
}
type Writer struct {
w io.Writer
packets uint64
stdout bool
verbose bool
}
// NewWriter creates a new mshark Writer.
func NewWriter(w io.Writer, verbose bool) *Writer {
return &Writer{
w: w,
stdout: w == os.Stdout,
verbose: verbose}
}
// printPacket prints a layer packet to the writer. If the writer is an instance of os.Stdout,
// the packet will be printed with color, based on the layerNum.
func (mw *Writer) printPacket(layer layers.Layer, layerNum int) {
var packet string
if mw.verbose {
packet = layer.String()
} else {
packet = layer.Summary()
}
if mw.stdout {
if color, ok := colorMap[layerNum]; ok {
packet = color + packet + "\033[0m"
}
}
fmt.Fprintln(mw.w, packet)
}
// WritePacket writes a packet to the writer, along with its timestamp.
//
// Timestamps are to be generated by the calling code.
func (mw *Writer) WritePacket(timestamp time.Time, data []byte) error {
mw.packets++
fmt.Fprintf(mw.w, "- Packet: %d Timestamp: %s\n", mw.packets, timestamp.Format("2006-01-02T15:04:05-0700"))
fmt.Fprintln(mw.w, "==================================================================")
next := layers.LayerMap["ETH"]
if err := next.Parse(data); err != nil {
return err
}
var layerNum int
mw.printPacket(next, layerNum)
for {
name, data := next.NextLayer()
if name == "" || data == nil || len(data) == 0 {
return nil
}
next = layers.LayerMap[name]
if err := next.Parse(data); err != nil {
return err
}
layerNum++
mw.printPacket(next, layerNum)
}
}
// WriteHeader writes a header to the writer.
//
// The header contains metadata about the capture, such as the interface name,
// snapshot length, promiscuous mode, timeout, number of packets, and BPF filter.
//
// The header is written in the following format:
//
// - Interface: eth0
// - Snapshot Length: 65535
// - Promiscuous Mode: true
// - Timeout: 5s
// - Number of Packets: 0
// - BPF Filter: "ip proto tcp"
// - Verbose: true
func (mw *Writer) WriteHeader(c *Config) error {
_, err := fmt.Fprintf(mw.w, `- Interface: %s
- Snapshot Length: %d
- Promiscuous Mode: %v
- Timeout: %s
- Number of Packets: %d
- BPF Filter: %q
- Verbose: %v
`,
c.Device.Name,
c.Snaplen,
c.Device.Name != "any" && c.Promisc,
c.Timeout,
c.PacketCount,
c.Expr,
mw.verbose,
)
return err
}
// InterfaceByName returns the interface specified by name.
func InterfaceByName(name string) (*net.Interface, error) {
var (
in *net.Interface
err error
)
if name == "any" {
in = &net.Interface{Index: 0, Name: "any"}
} else {
in, err = net.InterfaceByName(name)
if err != nil {
return nil, fmt.Errorf("unknown interface %s: %v", name, err)
}
ok := true &&
// Look for an Ethernet interface.
len(in.HardwareAddr) == 6 &&
// Look for up, multicast, broadcast.
in.Flags&(net.FlagUp|net.FlagMulticast|net.FlagBroadcast) != 0
if !ok {
return nil, fmt.Errorf("interface %s is not up", name)
}
}
return in, nil
}
// OpenLive opens a live capture based on the given configuration and writes
// all captured packets to the given PacketWriters.
func OpenLive(conf *Config, pw ...PacketWriter) error {
packetcfg := packet.Config{}
// setting up filter
if conf.Expr != "" {
e := filter.NewExpression(conf.Expr)
f := e.Compile()
instructions, err := f.Compile()
if err != nil {
return fmt.Errorf("failed to compile filter into instructions: %v", err)
}
raw, err := bpf.Assemble(instructions)
if err != nil {
return fmt.Errorf("bpf assembly failed: %v", err)
}
packetcfg.Filter = raw
}
// opening connection
c, err := packet.Listen(conf.Device, packet.Raw, unixEthPAll, &packetcfg)
if err != nil {
if errors.Is(err, os.ErrPermission) {
return fmt.Errorf("permission denied (try setting CAP_NET_RAW capability): %v", err)
}
return fmt.Errorf("failed to listen: %v", err)
}
// setting promisc mode
if conf.Device.Name != "any" {
if err := c.SetPromiscuous(conf.Promisc); err != nil {
return fmt.Errorf("unable to set promiscuous mode: %v", err)
}
}
// timeout
if conf.Timeout > 0 {
if err := c.SetDeadline(time.Now().Add(conf.Timeout)); err != nil {
return fmt.Errorf("unable to set timeout: %v", err)
}
}
defer func() {
stats, err := c.Stats()
if err != nil {
fmt.Fprintf(os.Stderr, "failed to fetch stats: %v", err)
} else {
fmt.Printf("- Packets: %d, Drops: %d, Freeze Queue Count: %d\n",
stats.Packets, stats.Drops, stats.FreezeQueueCount)
for _, w := range pw {
if w, ok := w.(*Writer); ok {
fmt.Fprintf(w.w, "- Packets Captured: %d\n", w.packets)
}
}
}
// close Conn
c.Close()
}()
// number of packets
count := conf.PacketCount
if count < 0 {
count = 0
}
infinity := count == 0
b := make([]byte, conf.Snaplen)
for i := 0; infinity || i < count; i++ {
n, _, err := c.ReadFrom(b)
if err != nil {
if errors.Is(err, os.ErrDeadlineExceeded) {
return nil
}
return fmt.Errorf("failed to read Ethernet frame: %v", err)
}
for _, w := range pw {
if err := w.WritePacket(time.Now().UTC(), b[:n]); err != nil {
return err
}
}
}
return nil
}