github.com/gopacket/gopacket@v1.1.0/pcapgo/capture.go (about) 1 // Copyright 2012 Google, Inc. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style license 4 // that can be found in the LICENSE file in the root of the source 5 // tree. 6 //go:build linux 7 8 package pcapgo 9 10 import ( 11 "fmt" 12 "io" 13 "net" 14 "runtime" 15 "sync" 16 "sync/atomic" 17 "syscall" 18 "time" 19 "unsafe" 20 21 "golang.org/x/net/bpf" 22 "golang.org/x/sys/unix" 23 24 "github.com/gopacket/gopacket" 25 ) 26 27 var hdrLen = unix.CmsgSpace(0) 28 var auxLen = unix.CmsgSpace(int(unsafe.Sizeof(unix.TpacketAuxdata{}))) 29 var timensLen = unix.CmsgSpace(int(unsafe.Sizeof(unix.Timespec{}))) 30 var timeLen = unix.CmsgSpace(int(unsafe.Sizeof(unix.Timeval{}))) 31 32 func htons(data uint16) uint16 { return data<<8 | data>>8 } 33 34 // EthernetHandle holds shared buffers and file descriptor of af_packet socket 35 type EthernetHandle struct { 36 fd uintptr 37 buffer []byte 38 oob []byte 39 ancil []interface{} 40 mu sync.Mutex 41 intf int 42 addr net.HardwareAddr 43 } 44 45 // readOne reads a packet from the handle and returns a capture info + vlan info 46 func (h *EthernetHandle) readOne() (ci gopacket.CaptureInfo, vlan int, haveVlan bool, err error) { 47 // we could use unix.Recvmsg, but that does a memory allocation (for the returned sockaddr) :( 48 var msg unix.Msghdr 49 var sa unix.RawSockaddrLinklayer 50 var handle = atomic.LoadUintptr(&h.fd) 51 52 /* 53 * check if the handle got closed already 54 * if so return EOF to also stop waiting for packets 55 */ 56 if int(handle) < 0 { 57 err = io.EOF 58 return 59 } 60 61 msg.Name = (*byte)(unsafe.Pointer(&sa)) 62 msg.Namelen = uint32(unsafe.Sizeof(sa)) 63 64 var iov unix.Iovec 65 if len(h.buffer) > 0 { 66 iov.Base = &h.buffer[0] 67 iov.SetLen(len(h.buffer)) 68 } 69 msg.Iov = &iov 70 msg.Iovlen = 1 71 72 if len(h.oob) > 0 { 73 msg.Control = &h.oob[0] 74 msg.SetControllen(len(h.oob)) 75 } 76 77 /* 78 * use msg_trunc, so we know packet size without auxdata, which might be missing 79 */ 80 n, _, e := syscall.Syscall(unix.SYS_RECVMSG, handle, uintptr(unsafe.Pointer(&msg)), uintptr(unix.MSG_TRUNC)) 81 82 switch { 83 case e.Temporary() || e.Timeout(): 84 return ci, 0, false, e 85 case e != 0: 86 return ci, 0, false, fmt.Errorf("couldn't read packet: %w", e) 87 } 88 89 if sa.Family == unix.AF_PACKET { 90 ci.InterfaceIndex = int(sa.Ifindex) 91 } else { 92 ci.InterfaceIndex = h.intf 93 } 94 95 // custom aux parsing so we don't allocate stuff (unix.ParseSocketControlMessage allocates a slice) 96 // we're getting at most 2 cmsgs anyway and know which ones they are (auxdata + timestamp(ns)) 97 oob := h.oob[:msg.Controllen] 98 gotAux := false 99 100 for len(oob) > hdrLen { // > hdrLen, because we also need something after the cmsg header 101 hdr := (*unix.Cmsghdr)(unsafe.Pointer(&oob[0])) 102 switch { 103 case hdr.Level == unix.SOL_PACKET && hdr.Type == unix.PACKET_AUXDATA && len(oob) >= auxLen: 104 aux := (*unix.TpacketAuxdata)(unsafe.Pointer(&oob[hdrLen])) 105 ci.CaptureLength = int(n) 106 ci.Length = int(aux.Len) 107 vlan = int(aux.Vlan_tci) 108 haveVlan = (aux.Status & unix.TP_STATUS_VLAN_VALID) != 0 109 gotAux = true 110 case hdr.Level == unix.SOL_SOCKET && hdr.Type == unix.SO_TIMESTAMPNS && len(oob) >= timensLen: 111 tstamp := (*unix.Timespec)(unsafe.Pointer(&oob[hdrLen])) 112 ci.Timestamp = time.Unix(tstamp.Sec, tstamp.Nsec) 113 case hdr.Level == unix.SOL_SOCKET && hdr.Type == unix.SO_TIMESTAMP && len(oob) >= timeLen: 114 tstamp := (*unix.Timeval)(unsafe.Pointer(&oob[hdrLen])) 115 ci.Timestamp = time.Unix(tstamp.Sec, int64(tstamp.Usec)*1000) 116 } 117 oob = oob[unix.CmsgSpace(int(hdr.Len))-hdrLen:] 118 } 119 120 if !gotAux { 121 // fallback for no aux cmsg 122 ci.CaptureLength = int(n) 123 ci.Length = int(n) 124 haveVlan = false 125 } 126 127 // fix up capture length if we needed to truncate 128 if ci.CaptureLength > len(h.buffer) { 129 ci.CaptureLength = len(h.buffer) 130 } 131 132 if ci.Timestamp.IsZero() { 133 // we got no timestamp info -> emulate it 134 ci.Timestamp = time.Now() 135 } 136 137 return ci, vlan, haveVlan, nil 138 } 139 140 // ReadPacketData implements gopacket.PacketDataSource. If this was captured on a vlan, the vlan id will be in the AncillaryData[0] 141 func (h *EthernetHandle) ReadPacketData() ([]byte, gopacket.CaptureInfo, error) { 142 h.mu.Lock() 143 ci, vlan, haveVlan, err := h.readOne() 144 if err != nil { 145 h.mu.Unlock() 146 return nil, gopacket.CaptureInfo{}, fmt.Errorf("couldn't read packet data: %s", err) 147 } 148 149 b := make([]byte, ci.CaptureLength) 150 copy(b, h.buffer) 151 h.mu.Unlock() 152 153 if haveVlan { 154 ci.AncillaryData = []interface{}{vlan} 155 156 } 157 158 return b, ci, nil 159 } 160 161 // ZeroCopyReadPacketData implements gopacket.ZeroCopyPacketDataSource. If this was captured on a vlan, the vlan id will be in the AncillaryData[0]. 162 // This function does not allocate memory. Beware that the next call to ZeroCopyReadPacketData will overwrite existing slices (returned data AND AncillaryData)! 163 // Due to shared buffers this must not be called concurrently 164 func (h *EthernetHandle) ZeroCopyReadPacketData() ([]byte, gopacket.CaptureInfo, error) { 165 ci, vlan, haveVlan, err := h.readOne() 166 if err != nil { 167 return nil, gopacket.CaptureInfo{}, fmt.Errorf("couldn't read packet data: %s", err) 168 } 169 170 if haveVlan { 171 h.ancil[0] = vlan 172 ci.AncillaryData = h.ancil 173 } 174 175 return h.buffer[:ci.CaptureLength], ci, nil 176 } 177 178 // Close closes the underlying socket 179 func (h *EthernetHandle) Close() (err error) { 180 if handle := atomic.LoadUintptr(&h.fd); handle != 0 { 181 _ = unix.Shutdown(int(handle), unix.SHUT_RDWR) 182 // close no matter if shutdown returned an error or not to make sure the socket is closed 183 err = unix.Close(int(handle)) 184 atomic.SwapUintptr(&h.fd, 0) 185 runtime.SetFinalizer(h, nil) 186 } 187 return err 188 } 189 190 // SetCaptureLength sets the maximum capture length to the given value 191 func (h *EthernetHandle) SetCaptureLength(len int) error { 192 if len < 0 { 193 return fmt.Errorf("illegal capture length %d. Must be at least 0", len) 194 } 195 h.buffer = make([]byte, len) 196 return nil 197 } 198 199 // GetCaptureLength returns the maximum capture length 200 func (h *EthernetHandle) GetCaptureLength() int { 201 return len(h.buffer) 202 } 203 204 // SetBPF attaches the given BPF filter to the socket. After this, only the packets for which the filter returns a value greater than zero are received. 205 // If a filter was already attached, it will be overwritten. To remove the filter, provide an empty slice. 206 func (h *EthernetHandle) SetBPF(filter []bpf.RawInstruction) error { 207 if len(filter) == 0 { 208 return unix.SetsockoptInt(int(h.fd), unix.SOL_SOCKET, unix.SO_DETACH_FILTER, 0) 209 } 210 f := make([]unix.SockFilter, len(filter)) 211 for i := range filter { 212 f[i].Code = filter[i].Op 213 f[i].Jf = filter[i].Jf 214 f[i].Jt = filter[i].Jt 215 f[i].K = filter[i].K 216 } 217 fprog := &unix.SockFprog{ 218 Len: uint16(len(filter)), 219 Filter: &f[0], 220 } 221 return unix.SetsockoptSockFprog(int(h.fd), unix.SOL_SOCKET, unix.SO_ATTACH_FILTER, fprog) 222 } 223 224 // LocalAddr returns the local network address 225 func (h *EthernetHandle) LocalAddr() net.HardwareAddr { 226 // Hardware Address might have changed. Fetch new one and fall back to the stored one if fetching interface fails 227 intf, err := net.InterfaceByIndex(h.intf) 228 if err == nil { 229 h.addr = intf.HardwareAddr 230 } 231 return h.addr 232 } 233 234 // SetPromiscuous sets promiscous mode to the required value. If it is enabled, traffic not destined for the interface will also be captured. 235 func (h *EthernetHandle) SetPromiscuous(b bool) error { 236 mreq := unix.PacketMreq{ 237 Ifindex: int32(h.intf), 238 Type: unix.PACKET_MR_PROMISC, 239 } 240 241 opt := unix.PACKET_ADD_MEMBERSHIP 242 if !b { 243 opt = unix.PACKET_DROP_MEMBERSHIP 244 } 245 246 return unix.SetsockoptPacketMreq(int(h.fd), unix.SOL_PACKET, opt, &mreq) 247 } 248 249 // Stats returns number of packets and dropped packets. This will be the number of packets/dropped packets since the last call to stats (not the cummulative sum!). 250 func (h *EthernetHandle) Stats() (*unix.TpacketStats, error) { 251 return unix.GetsockoptTpacketStats(int(h.fd), unix.SOL_PACKET, unix.PACKET_STATISTICS) 252 } 253 254 // NewEthernetHandle implements pcap.OpenLive for network devices. 255 // If you want better performance have a look at github.com/gopacket/gopacket/afpacket. 256 // SetCaptureLength can be used to limit the maximum capture length. 257 func NewEthernetHandle(ifname string) (*EthernetHandle, error) { 258 intf, err := net.InterfaceByName(ifname) 259 if err != nil { 260 return nil, fmt.Errorf("couldn't query interface %s: %s", ifname, err) 261 } 262 263 fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, int(htons(unix.ETH_P_ALL))) 264 if err != nil { 265 return nil, fmt.Errorf("couldn't open packet socket: %s", err) 266 } 267 268 addr := unix.SockaddrLinklayer{ 269 Protocol: htons(unix.ETH_P_ALL), 270 Ifindex: intf.Index, 271 } 272 273 if err := unix.Bind(fd, &addr); err != nil { 274 return nil, fmt.Errorf("couldn't bind to interface %s: %s", ifname, err) 275 } 276 277 ooblen := 0 278 279 if err := unix.SetsockoptInt(fd, unix.SOL_PACKET, unix.PACKET_AUXDATA, 1); err != nil { 280 // we can't get auxdata -> no vlan info 281 } else { 282 ooblen += auxLen 283 } 284 285 if err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_TIMESTAMPNS, 1); err != nil { 286 // no nanosecond resolution :( -> try ms 287 if err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_TIMESTAMP, 1); err != nil { 288 // if this doesn't work we well use time.Now() -> ignore errors here 289 } else { 290 ooblen += timeLen 291 } 292 } else { 293 ooblen += timensLen 294 } 295 296 handle := &EthernetHandle{ 297 fd: uintptr(fd), 298 buffer: make([]byte, intf.MTU), 299 oob: make([]byte, ooblen), 300 ancil: make([]interface{}, 1), 301 intf: intf.Index, 302 addr: intf.HardwareAddr, 303 } 304 runtime.SetFinalizer(handle, (*EthernetHandle).Close) 305 return handle, nil 306 }