├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.mkd ├── decode.go ├── decode_test.go ├── go.mod ├── io.go ├── packet.go ├── pcap.go ├── pcap_test.go ├── test └── pcap_files │ └── Network_Join_Nokia_Mobile.pcap ├── tools ├── pass │ └── pass.go ├── pcaptest │ └── pcaptest.go └── tcpdump │ └── tcpdump.go └── version.go /.gitignore: -------------------------------------------------------------------------------- 1 | #* 2 | *~ 3 | /tools/pass/pass 4 | /tools/pcaptest/pcaptest 5 | /tools/tcpdump/tcpdump 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | install: 4 | - sudo apt-get install libpcap-dev 5 | 6 | # running tcpdump inside travis does not really work as a good test. 7 | script: 8 | - go build 9 | #- sudo $GOROOT/bin/go test 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2011 Andreas Krennmair. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Andreas Krennmair nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | define GO 2 | //+build ignore 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/miekg/pcap" 10 | ) 11 | 12 | func main() { 13 | fmt.Println(pcap.GoVersion) 14 | } 15 | endef 16 | $(file > version_release.go,$(GO)) 17 | VERSION:=$(shell go run version_release.go) 18 | TAG="v$(VERSION)" 19 | 20 | all: 21 | @echo "use \'make release\' to release $(VERSION)" 22 | rm -f version_release.go 23 | 24 | .PHONY: test 25 | test: 26 | @echo "TAG" $(TAG) 27 | @echo "VERSION" $(VERSION) 28 | rm -f version_release.go 29 | 30 | .PHONY: release 31 | release: commit push 32 | rm -f version_release.go 33 | @echo "released $(VERSION)" 34 | 35 | .PHONY: commit 36 | commit: 37 | @echo committing release $(VERSION) 38 | git commit -am"Release $(VERSION)" 39 | git tag $(TAG) 40 | 41 | .PHONY: push 42 | push: 43 | @echo pushing $(VERSION) to GitHub 44 | git push --tags 45 | git push 46 | -------------------------------------------------------------------------------- /README.mkd: -------------------------------------------------------------------------------- 1 | # PCAP [![Build Status](https://travis-ci.org/miekg/pcap.png)](https://travis-ci.org/miekg/pcap) 2 | 3 | This is a simple wrapper around libpcap for Go. Originally written by Andreas 4 | Krennmair and only minorly touched up by Mark Smith . 5 | 6 | Please see the included pcaptest.go and tcpdump.go programs for instructions on 7 | how to use this library. 8 | 9 | Miek Gieben has created a more Go-like package and replaced functionality 10 | with standard functions from the standard library. The package has also been renamed to 11 | pcap. 12 | 13 | ## TODO 14 | 15 | * Could use some more documentation. 16 | -------------------------------------------------------------------------------- /decode.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strings" 7 | ) 8 | 9 | const ( 10 | TYPE_IP = 0x0800 11 | TYPE_ARP = 0x0806 12 | TYPE_IP6 = 0x86DD 13 | 14 | IP_ICMP = 1 15 | IP_INIP = 4 16 | IP_TCP = 6 17 | IP_UDP = 17 18 | ) 19 | 20 | // Port from sf-pcap.c file. 21 | const ( 22 | TCPDUMP_MAGIC = 0xa1b2c3d4 23 | KUZNETZOV_TCPDUMP_MAGIC = 0xa1b2cd34 24 | FMESQUITA_TCPDUMP_MAGIC = 0xa1b234cd 25 | NAVTEL_TCPDUMP_MAGIC = 0xa12b3c4d 26 | NSEC_TCPDUMP_MAGIC = 0xa1b23c4d 27 | ) 28 | 29 | // DLT, 30 | // these are the types that are the same on all platforms, and that 31 | // have been defined by for ages. 32 | const ( 33 | DLT_NULL = 0 // BSD loopback encapsulation 34 | DLT_EN10MB = 1 // Ethernet (10Mb) 35 | DLT_EN3MB = 2 // Experimental Ethernet (3Mb) 36 | DLT_AX25 = 3 // Amateur Radio AX.25 37 | DLT_PRONET = 4 // Proteon ProNET Token Ring 38 | DLT_CHAOS = 5 // Chaos 39 | DLT_IEEE802 = 6 // 802.5 Token Ring 40 | DLT_ARCNET = 7 // ARCNET, with BSD-style header 41 | DLT_SLIP = 8 // Serial Line IP 42 | DLT_PPP = 9 // Point-to-point Protocol 43 | DLT_FDDI = 10 // FDDI 44 | ) 45 | 46 | const ( 47 | ERRBUF_SIZE = 256 48 | 49 | // According to pcap-linktype(7). 50 | LINKTYPE_NULL = DLT_NULL 51 | LINKTYPE_ETHERNET = DLT_EN10MB 52 | LINKTYPE_TOKEN_RING = DLT_IEEE802 53 | 54 | LINKTYPE_EXP_ETHERNET = DLT_EN3MB /* 3Mb experimental Ethernet */ 55 | LINKTYPE_AX25 = DLT_AX25 56 | LINKTYPE_PRONET = DLT_PRONET 57 | LINKTYPE_CHAOS = DLT_CHAOS 58 | LINKTYPE_ARCNET_BSD = DLT_ARCNET /* BSD-style headers */ 59 | LINKTYPE_SLIP = DLT_SLIP 60 | LINKTYPE_PPP = DLT_PPP 61 | LINKTYPE_FDDI = DLT_FDDI 62 | 63 | LINKTYPE_ARCNET = 7 64 | LINKTYPE_ATM_RFC1483 = 100 65 | LINKTYPE_RAW = 101 66 | LINKTYPE_PPP_HDLC = 50 67 | LINKTYPE_PPP_ETHER = 51 68 | LINKTYPE_C_HDLC = 104 69 | LINKTYPE_IEEE802_11 = 105 70 | LINKTYPE_FRELAY = 107 71 | LINKTYPE_LOOP = 108 72 | LINKTYPE_LINUX_SLL = 113 73 | LINKTYPE_LTALK = 104 74 | LINKTYPE_PFLOG = 117 75 | LINKTYPE_PRISM_HEADER = 119 76 | LINKTYPE_IP_OVER_FC = 122 77 | LINKTYPE_SUNATM = 123 78 | LINKTYPE_IEEE802_11_RADIO = 127 79 | LINKTYPE_ARCNET_LINUX = 129 80 | LINKTYPE_LINUX_IRDA = 144 81 | LINKTYPE_LINUX_LAPD = 177 82 | ) 83 | 84 | type addrHdr interface { 85 | SrcAddr() string 86 | DestAddr() string 87 | Len() int 88 | } 89 | 90 | type addrStringer interface { 91 | String(addr addrHdr) string 92 | } 93 | 94 | func decodemac(pkt []byte) uint64 { 95 | mac := uint64(0) 96 | for i := uint(0); i < 6; i++ { 97 | mac = (mac << 8) + uint64(pkt[i]) 98 | } 99 | return mac 100 | } 101 | 102 | // Arphdr is a ARP packet header. 103 | type Arphdr struct { 104 | Addrtype uint16 105 | Protocol uint16 106 | HwAddressSize uint8 107 | ProtAddressSize uint8 108 | Operation uint16 109 | SourceHwAddress []byte 110 | SourceProtAddress []byte 111 | DestHwAddress []byte 112 | DestProtAddress []byte 113 | } 114 | 115 | func (arp *Arphdr) String() (s string) { 116 | switch arp.Operation { 117 | case 1: 118 | s = "ARP request" 119 | case 2: 120 | s = "ARP Reply" 121 | } 122 | if arp.Addrtype == LINKTYPE_ETHERNET && arp.Protocol == TYPE_IP { 123 | s = fmt.Sprintf("%012x (%s) > %012x (%s)", 124 | decodemac(arp.SourceHwAddress), arp.SourceProtAddress, 125 | decodemac(arp.DestHwAddress), arp.DestProtAddress) 126 | } else { 127 | s = fmt.Sprintf("addrtype = %d protocol = %d", arp.Addrtype, arp.Protocol) 128 | } 129 | return 130 | } 131 | 132 | // IPhdr is the header of an IP packet. 133 | type Iphdr struct { 134 | Version uint8 135 | Ihl uint8 136 | Tos uint8 137 | Length uint16 138 | Id uint16 139 | Flags uint8 140 | FragOffset uint16 141 | Ttl uint8 142 | Protocol uint8 143 | Checksum uint16 144 | SrcIp []byte 145 | DestIp []byte 146 | } 147 | 148 | func (ip *Iphdr) SrcAddr() string { return net.IP(ip.SrcIp).String() } 149 | func (ip *Iphdr) DestAddr() string { return net.IP(ip.DestIp).String() } 150 | func (ip *Iphdr) Len() int { return int(ip.Length) } 151 | 152 | type Tcphdr struct { 153 | SrcPort uint16 154 | DestPort uint16 155 | Seq uint32 156 | Ack uint32 157 | DataOffset uint8 158 | Flags uint16 159 | Window uint16 160 | Checksum uint16 161 | Urgent uint16 162 | Data []byte 163 | } 164 | 165 | const ( 166 | TCP_FIN = 1 << iota 167 | TCP_SYN 168 | TCP_RST 169 | TCP_PSH 170 | TCP_ACK 171 | TCP_URG 172 | TCP_ECE 173 | TCP_CWR 174 | TCP_NS 175 | ) 176 | 177 | func (tcp *Tcphdr) String(hdr addrHdr) string { 178 | return fmt.Sprintf("TCP %s:%d > %s:%d %s SEQ=%d ACK=%d LEN=%d", 179 | hdr.SrcAddr(), int(tcp.SrcPort), hdr.DestAddr(), int(tcp.DestPort), 180 | tcp.FlagsString(), int64(tcp.Seq), int64(tcp.Ack), hdr.Len()) 181 | } 182 | 183 | func (tcp *Tcphdr) FlagsString() string { 184 | var sflags []string 185 | if 0 != (tcp.Flags & TCP_SYN) { 186 | sflags = append(sflags, "syn") 187 | } 188 | if 0 != (tcp.Flags & TCP_FIN) { 189 | sflags = append(sflags, "fin") 190 | } 191 | if 0 != (tcp.Flags & TCP_ACK) { 192 | sflags = append(sflags, "ack") 193 | } 194 | if 0 != (tcp.Flags & TCP_PSH) { 195 | sflags = append(sflags, "psh") 196 | } 197 | if 0 != (tcp.Flags & TCP_RST) { 198 | sflags = append(sflags, "rst") 199 | } 200 | if 0 != (tcp.Flags & TCP_URG) { 201 | sflags = append(sflags, "urg") 202 | } 203 | if 0 != (tcp.Flags & TCP_NS) { 204 | sflags = append(sflags, "ns") 205 | } 206 | if 0 != (tcp.Flags & TCP_CWR) { 207 | sflags = append(sflags, "cwr") 208 | } 209 | if 0 != (tcp.Flags & TCP_ECE) { 210 | sflags = append(sflags, "ece") 211 | } 212 | return fmt.Sprintf("[%s]", strings.Join(sflags, " ")) 213 | } 214 | 215 | type Udphdr struct { 216 | SrcPort uint16 217 | DestPort uint16 218 | Length uint16 219 | Checksum uint16 220 | } 221 | 222 | func (udp *Udphdr) String(hdr addrHdr) string { 223 | return fmt.Sprintf("UDP %s:%d > %s:%d LEN=%d CHKSUM=%d", 224 | hdr.SrcAddr(), int(udp.SrcPort), hdr.DestAddr(), int(udp.DestPort), 225 | int(udp.Length), int(udp.Checksum)) 226 | } 227 | 228 | type Icmphdr struct { 229 | Type uint8 230 | Code uint8 231 | Checksum uint16 232 | Id uint16 233 | Seq uint16 234 | Data []byte 235 | } 236 | 237 | func (icmp *Icmphdr) String(hdr addrHdr) string { 238 | return fmt.Sprintf("ICMP %s > %s Type = %d Code = %d ", 239 | hdr.SrcAddr(), hdr.DestAddr(), icmp.Type, icmp.Code) 240 | } 241 | 242 | func (icmp *Icmphdr) TypeString() (result string) { 243 | switch icmp.Type { 244 | case 0: 245 | result = fmt.Sprintf("Echo reply seq=%d", icmp.Seq) 246 | case 3: 247 | switch icmp.Code { 248 | case 0: 249 | result = "Network unreachable" 250 | case 1: 251 | result = "Host unreachable" 252 | case 2: 253 | result = "Protocol unreachable" 254 | case 3: 255 | result = "Port unreachable" 256 | default: 257 | result = "Destination unreachable" 258 | } 259 | case 8: 260 | result = fmt.Sprintf("Echo request seq=%d", icmp.Seq) 261 | case 30: 262 | result = "Traceroute" 263 | } 264 | return 265 | } 266 | 267 | type Ip6hdr struct { 268 | // http://www.networksorcery.com/enp/protocol/ipv6.htm 269 | Version uint8 // 4 bits 270 | TrafficClass uint8 // 8 bits 271 | FlowLabel uint32 // 20 bits 272 | Length uint16 // 16 bits 273 | NextHeader uint8 // 8 bits, same as Protocol in Iphdr 274 | HopLimit uint8 // 8 bits 275 | SrcIp []byte // 16 bytes 276 | DestIp []byte // 16 bytes 277 | } 278 | 279 | func (ip6 *Ip6hdr) SrcAddr() string { return net.IP(ip6.SrcIp).String() } 280 | func (ip6 *Ip6hdr) DestAddr() string { return net.IP(ip6.DestIp).String() } 281 | func (ip6 *Ip6hdr) Len() int { return int(ip6.Length) } 282 | -------------------------------------------------------------------------------- /decode_test.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestDecodeSimpleTcpPacket(t *testing.T) { 9 | p := &Packet{ 10 | Data: []byte{ 11 | 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x20, 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, 12 | 0x08, 0x00, 0x45, 0x00, 0x01, 0xa4, 0x39, 0xdf, 0x40, 0x00, 0x40, 0x06, 13 | 0x55, 0x5a, 0xac, 0x11, 0x51, 0x49, 0xad, 0xde, 0xfe, 0xe1, 0xc5, 0xf7, 14 | 0x00, 0x50, 0xc5, 0x7e, 0x0e, 0x48, 0x49, 0x07, 0x42, 0x32, 0x80, 0x18, 15 | 0x00, 0x73, 0xab, 0xb1, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x03, 0x77, 16 | 0x37, 0x9c, 0x42, 0x77, 0x5e, 0x3a, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x20, 17 | 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 18 | 0x73, 0x74, 0x3a, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x66, 0x69, 0x73, 0x68, 19 | 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 20 | 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x6b, 0x65, 0x65, 0x70, 0x2d, 0x61, 21 | 0x6c, 0x69, 0x76, 0x65, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 22 | 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, 23 | 0x61, 0x2f, 0x35, 0x2e, 0x30, 0x20, 0x28, 0x58, 0x31, 0x31, 0x3b, 0x20, 24 | 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34, 25 | 0x29, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x57, 0x65, 0x62, 0x4b, 0x69, 26 | 0x74, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32, 0x20, 0x28, 0x4b, 0x48, 0x54, 27 | 0x4d, 0x4c, 0x2c, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x47, 0x65, 0x63, 28 | 0x6b, 0x6f, 0x29, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x2f, 0x31, 29 | 0x35, 0x2e, 0x30, 0x2e, 0x38, 0x37, 0x34, 0x2e, 0x31, 0x32, 0x31, 0x20, 30 | 0x53, 0x61, 0x66, 0x61, 0x72, 0x69, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32, 31 | 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x74, 0x65, 32 | 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 33 | 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d, 34 | 0x6c, 0x2b, 0x78, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 35 | 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, 36 | 0x30, 0x2e, 0x39, 0x2c, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 37 | 0x38, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x45, 0x6e, 38 | 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a, 0x69, 0x70, 39 | 0x2c, 0x64, 0x65, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, 0x63, 40 | 0x68, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x4c, 0x61, 41 | 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x65, 0x6e, 0x2d, 0x55, 42 | 0x53, 0x2c, 0x65, 0x6e, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x38, 0x0d, 0x0a, 43 | 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x43, 0x68, 0x61, 0x72, 0x73, 44 | 0x65, 0x74, 0x3a, 0x20, 0x49, 0x53, 0x4f, 0x2d, 0x38, 0x38, 0x35, 0x39, 45 | 0x2d, 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x3b, 0x71, 0x3d, 0x30, 46 | 0x2e, 0x37, 0x2c, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x33, 0x0d, 0x0a, 47 | 0x0d, 0x0a, 48 | }} 49 | p.Decode() 50 | if p.DestMac != 0x00000c9ff020 { 51 | t.Error("Dest mac", p.DestMac) 52 | } 53 | if p.SrcMac != 0xbc305be8d349 { 54 | t.Error("Src mac", p.SrcMac) 55 | } 56 | if len(p.Headers) != 2 { 57 | t.Error("Incorrect number of headers", len(p.Headers)) 58 | return 59 | } 60 | if ip, ipOk := p.Headers[0].(*Iphdr); ipOk { 61 | if ip.Version != 4 { 62 | t.Error("ip Version", ip.Version) 63 | } 64 | if ip.Ihl != 5 { 65 | t.Error("ip header length", ip.Ihl) 66 | } 67 | if ip.Tos != 0 { 68 | t.Error("ip TOS", ip.Tos) 69 | } 70 | if ip.Length != 420 { 71 | t.Error("ip Length", ip.Length) 72 | } 73 | if ip.Id != 14815 { 74 | t.Error("ip ID", ip.Id) 75 | } 76 | if ip.Flags != 0x02 { 77 | t.Error("ip Flags", ip.Flags) 78 | } 79 | if ip.FragOffset != 0 { 80 | t.Error("ip Fragoffset", ip.FragOffset) 81 | } 82 | if ip.Ttl != 64 { 83 | t.Error("ip TTL", ip.Ttl) 84 | } 85 | if ip.Protocol != 6 { 86 | t.Error("ip Protocol", ip.Protocol) 87 | } 88 | if ip.Checksum != 0x555A { 89 | t.Error("ip Checksum", ip.Checksum) 90 | } 91 | if !bytes.Equal(ip.SrcIp, []byte{172, 17, 81, 73}) { 92 | t.Error("ip Src", ip.SrcIp) 93 | } 94 | if !bytes.Equal(ip.DestIp, []byte{173, 222, 254, 225}) { 95 | t.Error("ip Dest", ip.DestIp) 96 | } 97 | if tcp, tcpOk := p.Headers[1].(*Tcphdr); tcpOk { 98 | if tcp.SrcPort != 50679 { 99 | t.Error("tcp srcport", tcp.SrcPort) 100 | } 101 | if tcp.DestPort != 80 { 102 | t.Error("tcp destport", tcp.DestPort) 103 | } 104 | if tcp.Seq != 0xc57e0e48 { 105 | t.Error("tcp seq", tcp.Seq) 106 | } 107 | if tcp.Ack != 0x49074232 { 108 | t.Error("tcp ack", tcp.Ack) 109 | } 110 | if tcp.DataOffset != 8 { 111 | t.Error("tcp dataoffset", tcp.DataOffset) 112 | } 113 | if tcp.Flags != 0x18 { 114 | t.Error("tcp flags", tcp.Flags) 115 | } 116 | if tcp.Window != 0x73 { 117 | t.Error("tcp window", tcp.Window) 118 | } 119 | if tcp.Checksum != 0xabb1 { 120 | t.Error("tcp checksum", tcp.Checksum) 121 | } 122 | if tcp.Urgent != 0 { 123 | t.Error("tcp urgent", tcp.Urgent) 124 | } 125 | } else { 126 | t.Error("Second header is not TCP header") 127 | } 128 | } else { 129 | t.Error("First header is not IP header") 130 | } 131 | if string(p.Payload) != "GET / HTTP/1.1\r\nHost: www.fish.com\r\nConnection: keep-alive\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n\r\n" { 132 | t.Error("--- PAYLOAD STRING ---\n", string(p.Payload), "\n--- PAYLOAD BYTES ---\n", p.Payload) 133 | } 134 | } 135 | 136 | // Makes sure packet payload doesn't display the 6 trailing null of this packet 137 | // as part of the payload. They're actually the ethernet trailer. 138 | func TestDecodeSmallTcpPacketHasEmptyPayload(t *testing.T) { 139 | p := &Packet{ 140 | // This packet is only 54 bits (an empty TCP RST), thus 6 trailing null 141 | // bytes are added by the ethernet layer to make it the minimum packet size. 142 | Data: []byte{ 143 | 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, 0xb8, 0xac, 0x6f, 0x92, 0xd5, 0xbf, 144 | 0x08, 0x00, 0x45, 0x00, 0x00, 0x28, 0x00, 0x00, 0x40, 0x00, 0x40, 0x06, 145 | 0x3f, 0x9f, 0xac, 0x11, 0x51, 0xc5, 0xac, 0x11, 0x51, 0x49, 0x00, 0x63, 146 | 0x9a, 0xef, 0x00, 0x00, 0x00, 0x00, 0x2e, 0xc1, 0x27, 0x83, 0x50, 0x14, 147 | 0x00, 0x00, 0xc3, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 148 | }} 149 | p.Decode() 150 | if p.Payload == nil { 151 | t.Error("Nil payload") 152 | } 153 | if len(p.Payload) != 0 { 154 | t.Error("Non-empty payload:", p.Payload) 155 | } 156 | } 157 | 158 | func TestDecodeMaliciousIPHeaderLength(t *testing.T) { 159 | p := &Packet{ 160 | // This packet is only 54 bits (an empty TCP RST), thus 6 trailing null 161 | // bytes are added by the ethernet layer to make it the minimum packet size. 162 | Data: []byte{ 163 | 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, 0xb8, 0xac, 0x6f, 0x92, 0xd5, 0xbf, 164 | 0x08, 0x00, 0x4f, 0x00, 0x00, 0x28, 0x00, 0x00, 0x40, 0x00, 0x40, 0x06, 165 | 0x3f, 0x9f, 0xac, 0x11, 0x51, 0xc5, 0xac, 0x11, 0x51, 0x49, 0x00, 0x63, 166 | 0x9a, 0xef, 0x00, 0x00, 0x00, 0x00, 0x2e, 0xc1, 0x27, 0x83, 0x50, 0x14, 167 | 0x00, 0x00, 0xc3, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 168 | }} 169 | p.Decode() 170 | } 171 | 172 | func TestDecodeTruncatedUpperLayer(t *testing.T) { 173 | // TCP 174 | p := &Packet{ 175 | Data: []byte{ 176 | 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x20, 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, 177 | 0x08, 0x00, 0x45, 0x00, 0x01, 0xa4, 0x39, 0xdf, 0x40, 0x00, 0x40, IP_TCP, 178 | 0x55, 0x5a, 0xac, 0x11, 0x51, 0x49, 0xad, 0xde, 0xfe, 0xe1, 0xc5, 179 | }} 180 | p.Decode() 181 | 182 | // ICMP 183 | p = &Packet{ 184 | Data: []byte{ 185 | 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x20, 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, 186 | 0x08, 0x00, 0x45, 0x00, 0x01, 0xa4, 0x39, 0xdf, 0x40, 0x00, 0x40, IP_ICMP, 187 | 0x55, 0x5a, 0xac, 0x11, 0x51, 0x49, 0xad, 0xde, 0xfe, 0xe1, 0xc5, 188 | }} 189 | p.Decode() 190 | 191 | // UDP 192 | p = &Packet{ 193 | Data: []byte{ 194 | 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x20, 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, 195 | 0x08, 0x00, 0x45, 0x00, 0x01, 0xa4, 0x39, 0xdf, 0x40, 0x00, 0x40, IP_UDP, 196 | 0x55, 0x5a, 0xac, 0x11, 0x51, 0x49, 0xad, 0xde, 0xfe, 0xe1, 0xc5, 197 | }} 198 | p.Decode() 199 | } 200 | 201 | func TestDecodeMaliciousTCPDataOffset(t *testing.T) { 202 | p := &Packet{ 203 | Data: []byte{ 204 | 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x20, 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, 205 | 0x08, 0x00, 0x45, 0x00, 0x01, 0xa4, 0x39, 0xdf, 0x40, 0x00, 0x40, 0x06, 206 | 0x55, 0x5a, 0xac, 0x11, 0x51, 0x49, 0xad, 0xde, 0xfe, 0xe1, 0xc5, 0xf7, 207 | 0x00, 0x50, 0xc5, 0x7e, 0x0e, 0x48, 0x49, 0x07, 0x42, 0x32, 0xf0, 0x18, 208 | 0x00, 0x73, 0xab, 0xb1, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x03, 0x77, 209 | 0x37, 0x9c, 0x42, 0x77, 0x5e, 0x3a, 210 | }} 211 | p.Decode() 212 | } 213 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/miekg/pcap 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /io.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io" 7 | "time" 8 | ) 9 | 10 | // FileHeader is the parsed header of a pcap file. 11 | // http://wiki.wireshark.org/Development/LibpcapFileFormat 12 | type FileHeader struct { 13 | MagicNumber uint32 14 | VersionMajor uint16 15 | VersionMinor uint16 16 | TimeZone int32 17 | SigFigs uint32 18 | SnapLen uint32 19 | 20 | // NOTE: 'Network' property has been changed to `linktype` 21 | // Please see pcap/pcap.h header file. 22 | // Network uint32 23 | LinkType uint32 24 | } 25 | 26 | // Reader parses pcap files. 27 | type Reader struct { 28 | flip bool 29 | buf io.Reader 30 | err error 31 | fourBytes []byte 32 | twoBytes []byte 33 | sixteenBytes []byte 34 | Header FileHeader 35 | } 36 | 37 | // NewReader reads pcap data from an io.Reader. 38 | func NewReader(reader io.Reader) (*Reader, error) { 39 | r := &Reader{ 40 | buf: reader, 41 | fourBytes: make([]byte, 4), 42 | twoBytes: make([]byte, 2), 43 | sixteenBytes: make([]byte, 16), 44 | } 45 | switch magic := r.readUint32(); magic { 46 | case 0xa1b2c3d4: 47 | r.flip = false 48 | case 0xd4c3b2a1: 49 | r.flip = true 50 | default: 51 | return nil, fmt.Errorf("pcap: bad magic number: %0x", magic) 52 | } 53 | r.Header = FileHeader{ 54 | MagicNumber: 0xa1b2c3d4, 55 | VersionMajor: r.readUint16(), 56 | VersionMinor: r.readUint16(), 57 | TimeZone: r.readInt32(), 58 | SigFigs: r.readUint32(), 59 | SnapLen: r.readUint32(), 60 | LinkType: r.readUint32(), 61 | } 62 | return r, nil 63 | } 64 | 65 | // Next returns the next packet or nil if no more packets can be read. 66 | func (r *Reader) Next() *Packet { 67 | d := r.sixteenBytes 68 | r.err = r.read(d) 69 | if r.err != nil { 70 | return nil 71 | } 72 | timeSec := asUint32(d[0:4], r.flip) 73 | timeUsec := asUint32(d[4:8], r.flip) 74 | capLen := asUint32(d[8:12], r.flip) 75 | origLen := asUint32(d[12:16], r.flip) 76 | 77 | data := make([]byte, capLen) 78 | if r.err = r.read(data); r.err != nil { 79 | return nil 80 | } 81 | return &Packet{ 82 | Time: time.Unix(int64(timeSec), int64(timeUsec)), 83 | Caplen: capLen, 84 | Len: origLen, 85 | Data: data, 86 | } 87 | } 88 | 89 | func (r *Reader) read(data []byte) error { 90 | var err error 91 | n, err := r.buf.Read(data) 92 | for err == nil && n != len(data) { 93 | var chunk int 94 | chunk, err = r.buf.Read(data[n:]) 95 | n += chunk 96 | } 97 | if len(data) == n { 98 | return nil 99 | } 100 | return err 101 | } 102 | 103 | func (r *Reader) readUint32() uint32 { 104 | data := r.fourBytes 105 | if r.err = r.read(data); r.err != nil { 106 | return 0 107 | } 108 | return asUint32(data, r.flip) 109 | } 110 | 111 | func (r *Reader) readInt32() int32 { 112 | data := r.fourBytes 113 | if r.err = r.read(data); r.err != nil { 114 | return 0 115 | } 116 | return int32(asUint32(data, r.flip)) 117 | } 118 | 119 | func (r *Reader) readUint16() uint16 { 120 | data := r.twoBytes 121 | if r.err = r.read(data); r.err != nil { 122 | return 0 123 | } 124 | return asUint16(data, r.flip) 125 | } 126 | 127 | // Writer writes a pcap file. 128 | type Writer struct { 129 | writer io.Writer 130 | buf []byte 131 | } 132 | 133 | // NewWriter creates a Writer that stores output in an io.Writer. 134 | // The FileHeader is written immediately. 135 | func NewWriter(writer io.Writer, header *FileHeader) (*Writer, error) { 136 | w := &Writer{ 137 | writer: writer, 138 | buf: make([]byte, 24), 139 | } 140 | binary.LittleEndian.PutUint32(w.buf, header.MagicNumber) 141 | binary.LittleEndian.PutUint16(w.buf[4:], header.VersionMajor) 142 | binary.LittleEndian.PutUint16(w.buf[6:], header.VersionMinor) 143 | binary.LittleEndian.PutUint32(w.buf[8:], uint32(header.TimeZone)) 144 | binary.LittleEndian.PutUint32(w.buf[12:], header.SigFigs) 145 | binary.LittleEndian.PutUint32(w.buf[16:], header.SnapLen) 146 | binary.LittleEndian.PutUint32(w.buf[20:], header.LinkType) 147 | if _, err := writer.Write(w.buf); err != nil { 148 | return nil, err 149 | } 150 | return w, nil 151 | } 152 | 153 | // Writer writes a packet to the underlying writer. 154 | func (w *Writer) Write(pkt *Packet) error { 155 | binary.LittleEndian.PutUint32(w.buf, uint32(pkt.Time.Unix())) 156 | binary.LittleEndian.PutUint32(w.buf[4:], uint32(pkt.Time.Nanosecond())) 157 | binary.LittleEndian.PutUint32(w.buf[8:], pkt.Caplen) 158 | binary.LittleEndian.PutUint32(w.buf[12:], pkt.Len) 159 | if _, err := w.writer.Write(w.buf[:16]); err != nil { 160 | return err 161 | } 162 | _, err := w.writer.Write(pkt.Data) 163 | return err 164 | } 165 | 166 | func asUint32(data []byte, flip bool) uint32 { 167 | if flip { 168 | return binary.BigEndian.Uint32(data) 169 | } 170 | return binary.LittleEndian.Uint32(data) 171 | } 172 | 173 | func asUint16(data []byte, flip bool) uint16 { 174 | if flip { 175 | return binary.BigEndian.Uint16(data) 176 | } 177 | return binary.LittleEndian.Uint16(data) 178 | } 179 | -------------------------------------------------------------------------------- /packet.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "reflect" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | type PacketTime struct { 13 | Sec int32 14 | Usec int32 15 | } 16 | 17 | // Packet is a single packet parsed from a pcap file. 18 | type Packet struct { 19 | // porting from 'pcap_pkthdr' struct 20 | Time time.Time // packet send/receive time 21 | Caplen uint32 // bytes stored in the file (caplen <= len) 22 | Len uint32 // bytes sent/received 23 | 24 | Data []byte // packet data 25 | 26 | Type int // protocol type, see LINKTYPE_* 27 | DestMac uint64 28 | SrcMac uint64 29 | 30 | Headers []interface{} // decoded headers, in order 31 | Payload []byte // remaining non-header bytes 32 | } 33 | 34 | // Decode decodes the headers of a Packet. 35 | func (p *Packet) Decode() error { 36 | 37 | if len(p.Data) <= 14 { 38 | return errors.New("invalid header") 39 | } 40 | p.Type = int(binary.BigEndian.Uint16(p.Data[12:14])) 41 | p.DestMac = decodemac(p.Data[0:6]) 42 | p.SrcMac = decodemac(p.Data[6:12]) 43 | p.Payload = p.Data[14:] 44 | 45 | switch p.Type { 46 | case TYPE_IP: 47 | p.decodeIp() 48 | case TYPE_IP6: 49 | p.decodeIp6() 50 | case TYPE_ARP: 51 | p.decodeArp() 52 | } 53 | 54 | return nil 55 | } 56 | 57 | func (p *Packet) headerString(headers []interface{}) string { 58 | // If there's just one header, return that. 59 | if len(headers) == 1 { 60 | if hdr, ok := headers[0].(fmt.Stringer); ok { 61 | return hdr.String() 62 | } 63 | } 64 | // If there are two headers (IPv4/IPv6 -> TCP/UDP/IP..) 65 | if len(headers) == 2 { 66 | // Commonly the first header is an address. 67 | if addr, ok := p.Headers[0].(addrHdr); ok { 68 | if hdr, ok := p.Headers[1].(addrStringer); ok { 69 | return fmt.Sprintf("%s %s", p.Time, hdr.String(addr)) 70 | } 71 | } 72 | } 73 | // For IP in IP, we do a recursive call. 74 | if len(headers) >= 2 { 75 | if addr, ok := headers[0].(addrHdr); ok { 76 | if _, ok := headers[1].(addrHdr); ok { 77 | return fmt.Sprintf("%s > %s IP in IP: %s", 78 | addr.SrcAddr(), addr.DestAddr(), p.headerString(headers[1:])) 79 | } 80 | } 81 | } 82 | 83 | var typeNames []string 84 | for _, hdr := range headers { 85 | typeNames = append(typeNames, reflect.TypeOf(hdr).String()) 86 | } 87 | 88 | return fmt.Sprintf("unknown [%s]", strings.Join(typeNames, ",")) 89 | } 90 | 91 | // String prints a one-line representation of the packet header. 92 | // The output is suitable for use in a tcpdump program. 93 | func (p *Packet) String() string { 94 | // If there are no headers, print "unsupported protocol". 95 | if len(p.Headers) == 0 { 96 | return fmt.Sprintf("%s unsupported protocol %d", p.Time, int(p.Type)) 97 | } 98 | return fmt.Sprintf("%s %s", p.Time, p.headerString(p.Headers)) 99 | } 100 | 101 | func (p *Packet) decodeArp() { 102 | pkt := p.Payload 103 | arp := new(Arphdr) 104 | arp.Addrtype = binary.BigEndian.Uint16(pkt[0:2]) 105 | arp.Protocol = binary.BigEndian.Uint16(pkt[2:4]) 106 | arp.HwAddressSize = pkt[4] 107 | arp.ProtAddressSize = pkt[5] 108 | arp.Operation = binary.BigEndian.Uint16(pkt[6:8]) 109 | arp.SourceHwAddress = pkt[8 : 8+arp.HwAddressSize] 110 | arp.SourceProtAddress = pkt[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize] 111 | arp.DestHwAddress = pkt[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize] 112 | arp.DestProtAddress = pkt[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize] 113 | 114 | p.Headers = append(p.Headers, arp) 115 | p.Payload = p.Payload[8+2*arp.HwAddressSize+2*arp.ProtAddressSize:] 116 | } 117 | 118 | func (p *Packet) decodeIp() { 119 | if len(p.Payload) < 20 { 120 | return 121 | } 122 | pkt := p.Payload 123 | ip := new(Iphdr) 124 | 125 | ip.Version = uint8(pkt[0]) >> 4 126 | ip.Ihl = uint8(pkt[0]) & 0x0F 127 | ip.Tos = pkt[1] 128 | ip.Length = binary.BigEndian.Uint16(pkt[2:4]) 129 | ip.Id = binary.BigEndian.Uint16(pkt[4:6]) 130 | flagsfrags := binary.BigEndian.Uint16(pkt[6:8]) 131 | ip.Flags = uint8(flagsfrags >> 13) 132 | ip.FragOffset = flagsfrags & 0x1FFF 133 | ip.Ttl = pkt[8] 134 | ip.Protocol = pkt[9] 135 | ip.Checksum = binary.BigEndian.Uint16(pkt[10:12]) 136 | ip.SrcIp = pkt[12:16] 137 | ip.DestIp = pkt[16:20] 138 | pEnd := int(ip.Length) 139 | if pEnd > len(pkt) { 140 | pEnd = len(pkt) 141 | } 142 | pIhl := int(ip.Ihl) * 4 143 | if pIhl > pEnd { 144 | pIhl = pEnd 145 | } 146 | p.Payload = pkt[pIhl:pEnd] 147 | p.Headers = append(p.Headers, ip) 148 | 149 | switch ip.Protocol { 150 | case IP_TCP: 151 | p.decodeTcp() 152 | case IP_UDP: 153 | p.decodeUdp() 154 | case IP_ICMP: 155 | p.decodeIcmp() 156 | case IP_INIP: 157 | p.decodeIp() 158 | } 159 | } 160 | 161 | func (p *Packet) decodeTcp() { 162 | pLenPayload := len(p.Payload) 163 | if pLenPayload < 20 { 164 | return 165 | } 166 | pkt := p.Payload 167 | tcp := new(Tcphdr) 168 | tcp.Data = pkt 169 | tcp.SrcPort = binary.BigEndian.Uint16(pkt[0:2]) 170 | tcp.DestPort = binary.BigEndian.Uint16(pkt[2:4]) 171 | tcp.Seq = binary.BigEndian.Uint32(pkt[4:8]) 172 | tcp.Ack = binary.BigEndian.Uint32(pkt[8:12]) 173 | tcp.DataOffset = (pkt[12] & 0xF0) >> 4 174 | tcp.Flags = binary.BigEndian.Uint16(pkt[12:14]) & 0x1FF 175 | tcp.Window = binary.BigEndian.Uint16(pkt[14:16]) 176 | tcp.Checksum = binary.BigEndian.Uint16(pkt[16:18]) 177 | tcp.Urgent = binary.BigEndian.Uint16(pkt[18:20]) 178 | pDataOffset := int(tcp.DataOffset * 4) 179 | if pDataOffset > pLenPayload { 180 | pDataOffset = pLenPayload 181 | } 182 | p.Payload = pkt[pDataOffset:] 183 | p.Headers = append(p.Headers, tcp) 184 | } 185 | 186 | func (p *Packet) decodeUdp() { 187 | if len(p.Payload) < 8 { 188 | return 189 | } 190 | pkt := p.Payload 191 | udp := new(Udphdr) 192 | udp.SrcPort = binary.BigEndian.Uint16(pkt[0:2]) 193 | udp.DestPort = binary.BigEndian.Uint16(pkt[2:4]) 194 | udp.Length = binary.BigEndian.Uint16(pkt[4:6]) 195 | udp.Checksum = binary.BigEndian.Uint16(pkt[6:8]) 196 | p.Headers = append(p.Headers, udp) 197 | p.Payload = pkt[8:] 198 | } 199 | 200 | func (p *Packet) decodeIcmp() *Icmphdr { 201 | if len(p.Payload) < 8 { 202 | return nil 203 | } 204 | pkt := p.Payload 205 | icmp := new(Icmphdr) 206 | icmp.Type = pkt[0] 207 | icmp.Code = pkt[1] 208 | icmp.Checksum = binary.BigEndian.Uint16(pkt[2:4]) 209 | icmp.Id = binary.BigEndian.Uint16(pkt[4:6]) 210 | icmp.Seq = binary.BigEndian.Uint16(pkt[6:8]) 211 | p.Payload = pkt[8:] 212 | p.Headers = append(p.Headers, icmp) 213 | return icmp 214 | } 215 | 216 | func (p *Packet) decodeIp6() { 217 | if len(p.Payload) < 40 { 218 | return 219 | } 220 | pkt := p.Payload 221 | ip6 := new(Ip6hdr) 222 | ip6.Version = uint8(pkt[0]) >> 4 223 | ip6.TrafficClass = uint8((binary.BigEndian.Uint16(pkt[0:2]) >> 4) & 0x00FF) 224 | ip6.FlowLabel = binary.BigEndian.Uint32(pkt[0:4]) & 0x000FFFFF 225 | ip6.Length = binary.BigEndian.Uint16(pkt[4:6]) 226 | ip6.NextHeader = pkt[6] 227 | ip6.HopLimit = pkt[7] 228 | ip6.SrcIp = pkt[8:24] 229 | ip6.DestIp = pkt[24:40] 230 | p.Payload = pkt[40:] 231 | p.Headers = append(p.Headers, ip6) 232 | 233 | switch ip6.NextHeader { 234 | case IP_TCP: 235 | p.decodeTcp() 236 | case IP_UDP: 237 | p.decodeUdp() 238 | case IP_ICMP: 239 | p.decodeIcmp() 240 | case IP_INIP: 241 | p.decodeIp() 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /pcap.go: -------------------------------------------------------------------------------- 1 | // Package pcap is a wrapper around the pcap library. 2 | package pcap 3 | 4 | /* 5 | #cgo LDFLAGS: -lpcap 6 | #include 7 | #include 8 | 9 | // Workaround for not knowing how to cast to const u_char** 10 | int hack_pcap_next_ex(pcap_t * p, struct pcap_pkthdr **pkt_header, 11 | u_char ** pkt_data) 12 | { 13 | return pcap_next_ex(p, pkt_header, (const u_char **)pkt_data); 14 | } 15 | 16 | void hack_pcap_dump(pcap_dumper_t * dumper, struct pcap_pkthdr *pkt_header, 17 | u_char * pkt_data) 18 | { 19 | pcap_dump((u_char *)dumper, pkt_header, pkt_data); 20 | } 21 | */ 22 | import "C" 23 | 24 | import ( 25 | "errors" 26 | "net" 27 | "syscall" 28 | "time" 29 | "unsafe" 30 | ) 31 | 32 | type Pcap struct { 33 | cptr *C.pcap_t 34 | } 35 | 36 | type PcapDumper struct { 37 | cptr *C.pcap_dumper_t 38 | } 39 | 40 | type pcapError struct{ string } 41 | 42 | type Stat struct { 43 | PacketsReceived uint32 44 | PacketsDropped uint32 45 | PacketsIfDropped uint32 46 | } 47 | 48 | type Interface struct { 49 | Name string 50 | Description string 51 | Addresses []IFAddress 52 | // TODO: add more elements 53 | } 54 | 55 | type IFAddress struct { 56 | IP net.IP 57 | Netmask net.IPMask 58 | // TODO: add broadcast + PtP dst ? 59 | } 60 | 61 | func Version() string { return C.GoString(C.pcap_lib_version()) } 62 | func (p *Pcap) Datalink() int { return int(C.pcap_datalink(p.cptr)) } 63 | func (e *pcapError) Error() string { return e.string } 64 | func (p *Pcap) Geterror() error { return &pcapError{C.GoString(C.pcap_geterr(p.cptr))} } 65 | func (p *Pcap) Next() (pkt *Packet) { rv, _ := p.NextEx(); return rv } 66 | 67 | func Create(device string) (handle *Pcap, err error) { 68 | var buf *C.char 69 | buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1)) 70 | h := new(Pcap) 71 | 72 | dev := C.CString(device) 73 | defer C.free(unsafe.Pointer(dev)) 74 | 75 | h.cptr = C.pcap_create(dev, buf) 76 | if nil == h.cptr { 77 | handle = nil 78 | err = &pcapError{C.GoString(buf)} 79 | } else { 80 | handle = h 81 | } 82 | 83 | C.free(unsafe.Pointer(buf)) 84 | return 85 | } 86 | 87 | // Set buffer size (units in bytes) on activated handle. 88 | func (p *Pcap) SetBufferSize(sz int32) error { 89 | if C.pcap_set_buffer_size(p.cptr, C.int(sz)) != 0 { 90 | return p.Geterror() 91 | } 92 | return nil 93 | } 94 | 95 | // If arg p is non-zero promiscuous mode will be set on capture handle when it is activated. 96 | func (p *Pcap) SetPromisc(promisc bool) error { 97 | var pro int32 98 | if promisc { 99 | pro = 1 100 | } 101 | 102 | if C.pcap_set_promisc(p.cptr, C.int(pro)) != 0 { 103 | return p.Geterror() 104 | } 105 | return nil 106 | } 107 | 108 | func (p *Pcap) SetSnapLen(s int32) error { 109 | if C.pcap_set_snaplen(p.cptr, C.int(s)) != 0 { 110 | return p.Geterror() 111 | } 112 | return nil 113 | } 114 | 115 | // Set read timeout (milliseconds) that will be used on a capture handle when it is activated. 116 | func (p *Pcap) SetReadTimeout(toMs int32) error { 117 | if C.pcap_set_timeout(p.cptr, C.int(toMs)) != 0 { 118 | return p.Geterror() 119 | } 120 | return nil 121 | } 122 | 123 | // Activate a packet capture handle to look at packets on the network, with the options that 124 | // were set on the handle being in effect. 125 | func (p *Pcap) Activate() error { 126 | if C.pcap_activate(p.cptr) != 0 { 127 | return p.Geterror() 128 | } 129 | return nil 130 | } 131 | 132 | // OpenLive opens a device and returns a handler. 133 | func OpenLive(device string, snaplen int32, promisc bool, timeout_ms int32) (handle *Pcap, err error) { 134 | var buf *C.char 135 | buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1)) 136 | h := new(Pcap) 137 | var pro int32 138 | if promisc { 139 | pro = 1 140 | } 141 | 142 | dev := C.CString(device) 143 | defer C.free(unsafe.Pointer(dev)) 144 | 145 | h.cptr = C.pcap_open_live(dev, C.int(snaplen), C.int(pro), C.int(timeout_ms), buf) 146 | if nil == h.cptr { 147 | handle = nil 148 | err = &pcapError{C.GoString(buf)} 149 | } else { 150 | handle = h 151 | } 152 | C.free(unsafe.Pointer(buf)) 153 | return 154 | } 155 | 156 | // Openoffline 157 | func OpenOffline(file string) (handle *Pcap, err error) { 158 | var buf *C.char 159 | buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1)) 160 | h := new(Pcap) 161 | 162 | cf := C.CString(file) 163 | defer C.free(unsafe.Pointer(cf)) 164 | 165 | h.cptr = C.pcap_open_offline(cf, buf) 166 | if nil == h.cptr { 167 | handle = nil 168 | err = &pcapError{C.GoString(buf)} 169 | } else { 170 | handle = h 171 | } 172 | C.free(unsafe.Pointer(buf)) 173 | return 174 | } 175 | 176 | // Pcap closes a handler. 177 | func (p *Pcap) Close() { 178 | C.pcap_close(p.cptr) 179 | } 180 | 181 | func (p *Pcap) NextEx() (pkt *Packet, result int32) { 182 | var pkthdr_ptr *C.struct_pcap_pkthdr 183 | var pkthdr C.struct_pcap_pkthdr 184 | 185 | var buf_ptr *C.u_char 186 | var buf unsafe.Pointer 187 | result = int32(C.hack_pcap_next_ex(p.cptr, &pkthdr_ptr, &buf_ptr)) 188 | 189 | buf = unsafe.Pointer(buf_ptr) 190 | pkthdr = *pkthdr_ptr 191 | 192 | if nil == buf { 193 | return 194 | } 195 | pkt = new(Packet) 196 | pkt.Time = time.Unix(int64(pkthdr.ts.tv_sec), int64(pkthdr.ts.tv_usec)*1000) // pcap provides usec but time.Unix requires nsec 197 | pkt.Caplen = uint32(pkthdr.caplen) 198 | pkt.Len = uint32(pkthdr.len) 199 | pkt.Data = make([]byte, pkthdr.caplen) 200 | 201 | for i := uint32(0); i < pkt.Caplen; i++ { 202 | pkt.Data[i] = *(*byte)(unsafe.Pointer(uintptr(buf) + uintptr(i))) 203 | } 204 | return 205 | } 206 | 207 | func (p *Pcap) Getstats() (stat *Stat, err error) { 208 | var cstats C.struct_pcap_stat 209 | if -1 == C.pcap_stats(p.cptr, &cstats) { 210 | return nil, p.Geterror() 211 | } 212 | stats := new(Stat) 213 | stats.PacketsReceived = uint32(cstats.ps_recv) 214 | stats.PacketsDropped = uint32(cstats.ps_drop) 215 | stats.PacketsIfDropped = uint32(cstats.ps_ifdrop) 216 | 217 | return stats, nil 218 | } 219 | 220 | func (p *Pcap) SetFilter(expr string) (err error) { 221 | var bpf C.struct_bpf_program 222 | cexpr := C.CString(expr) 223 | defer C.free(unsafe.Pointer(cexpr)) 224 | 225 | if -1 == C.pcap_compile(p.cptr, &bpf, cexpr, 1, 0) { 226 | return p.Geterror() 227 | } 228 | 229 | if -1 == C.pcap_setfilter(p.cptr, &bpf) { 230 | C.pcap_freecode(&bpf) 231 | return p.Geterror() 232 | } 233 | C.pcap_freecode(&bpf) 234 | return nil 235 | } 236 | 237 | func (p *Pcap) SetDirection(direction string) (err error) { 238 | var pcap_direction C.pcap_direction_t 239 | if direction == "in" { 240 | pcap_direction = C.PCAP_D_IN 241 | } else if direction == "out" { 242 | pcap_direction = C.PCAP_D_OUT 243 | } else { 244 | pcap_direction = C.PCAP_D_INOUT 245 | } 246 | if -1 == C.pcap_setdirection(p.cptr, pcap_direction) { 247 | return p.Geterror() 248 | } 249 | return nil 250 | } 251 | 252 | func (p *Pcap) SetDataLink(dlt int) error { 253 | if -1 == C.pcap_set_datalink(p.cptr, C.int(dlt)) { 254 | return p.Geterror() 255 | } 256 | return nil 257 | } 258 | 259 | func DatalinkValueToName(dlt int) string { 260 | if name := C.pcap_datalink_val_to_name(C.int(dlt)); name != nil { 261 | return C.GoString(name) 262 | } 263 | return "" 264 | } 265 | 266 | func DatalinkValueToDescription(dlt int) string { 267 | if desc := C.pcap_datalink_val_to_description(C.int(dlt)); desc != nil { 268 | return C.GoString(desc) 269 | } 270 | return "" 271 | } 272 | 273 | func FindAllDevs() (ifs []Interface, err error) { 274 | var buf *C.char 275 | buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1)) 276 | defer C.free(unsafe.Pointer(buf)) 277 | var alldevsp *C.pcap_if_t 278 | 279 | if -1 == C.pcap_findalldevs((**C.pcap_if_t)(&alldevsp), buf) { 280 | return nil, errors.New(C.GoString(buf)) 281 | } 282 | defer C.pcap_freealldevs((*C.pcap_if_t)(alldevsp)) 283 | dev := alldevsp 284 | var i uint32 285 | for i = 0; dev != nil; dev = (*C.pcap_if_t)(dev.next) { 286 | i++ 287 | } 288 | ifs = make([]Interface, i) 289 | dev = alldevsp 290 | for j := uint32(0); dev != nil; dev = (*C.pcap_if_t)(dev.next) { 291 | var iface Interface 292 | iface.Name = C.GoString(dev.name) 293 | iface.Description = C.GoString(dev.description) 294 | iface.Addresses = findAllAddresses(dev.addresses) 295 | // TODO: add more elements 296 | ifs[j] = iface 297 | j++ 298 | } 299 | return 300 | } 301 | 302 | func findAllAddresses(addresses *C.struct_pcap_addr) (retval []IFAddress) { 303 | // TODO - make it support more than IPv4 and IPv6? 304 | retval = make([]IFAddress, 0, 1) 305 | for curaddr := addresses; curaddr != nil; curaddr = (*C.struct_pcap_addr)(curaddr.next) { 306 | if curaddr.addr == nil { 307 | continue 308 | } 309 | var a IFAddress 310 | var err error 311 | if a.IP, err = sockaddrToIP((*syscall.RawSockaddr)(unsafe.Pointer(curaddr.addr))); err != nil { 312 | continue 313 | } 314 | if a.Netmask, err = sockaddrToIP((*syscall.RawSockaddr)(unsafe.Pointer(curaddr.netmask))); err != nil { 315 | continue 316 | } 317 | retval = append(retval, a) 318 | } 319 | return 320 | } 321 | 322 | func sockaddrToIP(rsa *syscall.RawSockaddr) (IP []byte, err error) { 323 | switch rsa.Family { 324 | case syscall.AF_INET: 325 | pp := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa)) 326 | IP = make([]byte, 4) 327 | for i := 0; i < len(IP); i++ { 328 | IP[i] = pp.Addr[i] 329 | } 330 | return 331 | case syscall.AF_INET6: 332 | pp := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa)) 333 | IP = make([]byte, 16) 334 | for i := 0; i < len(IP); i++ { 335 | IP[i] = pp.Addr[i] 336 | } 337 | return 338 | } 339 | err = errors.New("Unsupported address type") 340 | return 341 | } 342 | 343 | // Inject ... 344 | func (p *Pcap) Inject(data []byte) (err error) { 345 | buf := (*C.char)(C.malloc((C.size_t)(len(data)))) 346 | 347 | for i := 0; i < len(data); i++ { 348 | *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(buf)) + uintptr(i))) = data[i] 349 | } 350 | 351 | if -1 == C.pcap_inject(p.cptr, unsafe.Pointer(buf), (C.size_t)(len(data))) { 352 | err = p.Geterror() 353 | } 354 | C.free(unsafe.Pointer(buf)) 355 | return 356 | } 357 | 358 | func (p *Pcap) DumpOpen(ofile *string) (dumper *PcapDumper, err error) { 359 | d := new(PcapDumper) 360 | d.cptr = C.pcap_dump_open(p.cptr, C.CString(*ofile)) 361 | if nil == d.cptr { 362 | return d, errors.New("Cannot open dumpfile") 363 | } 364 | dumper = d 365 | return 366 | } 367 | 368 | func (p *Pcap) PcapLoop(pktnum int, dumper *PcapDumper) (result int32, err error) { 369 | var pkthdr_ptr *C.struct_pcap_pkthdr 370 | var buf_ptr *C.u_char 371 | 372 | for i := 0; true; { 373 | result = int32(C.hack_pcap_next_ex(p.cptr, &pkthdr_ptr, &buf_ptr)) 374 | switch result { 375 | case 0: 376 | continue // timeout 377 | case 1: 378 | // success : capturing packet 379 | case -1: 380 | return result, errors.New("Error in pcap next ex") 381 | case -2: 382 | return // reach EOF in offline mode 383 | } 384 | if nil == buf_ptr { 385 | continue 386 | } 387 | if nil != dumper { 388 | p.PcapDump(dumper, pkthdr_ptr, buf_ptr) 389 | p.PcapDumpFlush(dumper) 390 | } 391 | if pktnum > 0 { 392 | i++ 393 | if i >= pktnum { 394 | break 395 | } 396 | } 397 | } 398 | return 399 | } 400 | 401 | func (p *Pcap) PcapDump(dumper *PcapDumper, pkthdr_ptr *C.struct_pcap_pkthdr, buf_ptr *C.u_char) { 402 | C.hack_pcap_dump(dumper.cptr, pkthdr_ptr, buf_ptr) 403 | } 404 | 405 | func (p *Pcap) PcapDumpFlush(dumper *PcapDumper) error { 406 | if -1 == C.pcap_dump_flush(dumper.cptr) { 407 | return p.Geterror() 408 | } 409 | return nil 410 | } 411 | 412 | func (p *Pcap) PcapDumpClose(dumper *PcapDumper) { 413 | C.pcap_dump_close(dumper.cptr) 414 | } 415 | -------------------------------------------------------------------------------- /pcap_test.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestPcap(t *testing.T) { 12 | h, err := OpenOffline("test/pcap_files/Network_Join_Nokia_Mobile.pcap") 13 | if h == nil { 14 | t.Fail() 15 | return 16 | } 17 | _ = err 18 | } 19 | 20 | type pcapNewHandleFunc func(intf string, filter string, readTo int32) (h *Pcap, err error) 21 | 22 | func testPcapHandle(t *testing.T, newHandle pcapNewHandleFunc) { 23 | port := 54321 24 | h, err := newHandle("lo", fmt.Sprintf("udp dst port %d", port), 2000) 25 | if h == nil || err != nil { 26 | if h != nil { 27 | h.Close() 28 | } 29 | t.Fatalf("Failed to create/init pcap handle err:%s", err) 30 | } 31 | 32 | numPkts := 5 33 | go udpSvr(port, numPkts, t) 34 | go udpClient(port, numPkts, 1*time.Second, t) 35 | 36 | pktsRecvd := 0 37 | for pkt := h.Next(); pkt != nil; pkt = h.Next() { 38 | pkt.Decode() 39 | t.Logf("Packet:%s dataLen:%d", pkt, len(pkt.Payload)) 40 | pktsRecvd += 1 41 | if pktsRecvd >= numPkts { 42 | // some platforms ignore read timeout: 43 | // https://github.com/the-tcpdump-group/libpcap/issues/550 44 | break 45 | } 46 | } 47 | 48 | if pktsRecvd != numPkts { 49 | t.Fatalf("Capture failed pkts-send:%d, pkts-recvd:%d", numPkts, pktsRecvd) 50 | } 51 | 52 | t.Logf("Successfully captured %d packets", numPkts) 53 | } 54 | 55 | func TestPcapCreate(t *testing.T) { 56 | testPcapHandle(t, pcapCreate) 57 | } 58 | 59 | func TestPcapOpenLive(t *testing.T) { 60 | testPcapHandle(t, pcapOpenLive) 61 | } 62 | 63 | func TestPcapDump(t *testing.T) { 64 | port := 54321 65 | h, err := pcapOpenLive("lo", fmt.Sprintf("udp dst port %d", port), 2000) 66 | if h == nil || err != nil { 67 | if h != nil { 68 | h.Close() 69 | } 70 | t.Fatalf("Failed to create/init pcap handle err:%s", err) 71 | } 72 | 73 | ofile := "test/pcap_files/dump_test.pcap" 74 | d, err := h.DumpOpen(&ofile) 75 | if d == nil || err != nil { 76 | return 77 | } 78 | 79 | numPkts := 5 80 | go udpSvr(port, numPkts, t) 81 | go udpClient(port, numPkts, 1*time.Second, t) 82 | 83 | r, err := h.PcapLoop(5, d) 84 | if r < 0 || err != nil { 85 | return 86 | } 87 | 88 | h.PcapDumpClose(d) 89 | h.Close() 90 | 91 | newh, err := OpenOffline(ofile) 92 | if newh == nil { 93 | t.Fatalf("Failed to open pcap:%s", err) 94 | return 95 | } 96 | t.Log("Successfully open pcap") 97 | 98 | pktsRecvd := 0 99 | for pkt := newh.Next(); pkt != nil; pkt = newh.Next() { 100 | pkt.Decode() 101 | t.Logf("Packet:%s dataLen:%d", pkt, len(pkt.Payload)) 102 | pktsRecvd += 1 103 | if pktsRecvd >= numPkts { 104 | // some platforms ignore read timeout: 105 | // https://github.com/the-tcpdump-group/libpcap/issues/550 106 | break 107 | } 108 | } 109 | newh.Close() 110 | 111 | if pktsRecvd != numPkts { 112 | t.Fatalf("Capture failed pkts-send:%d, pkts-recvd:%d", numPkts, pktsRecvd) 113 | } 114 | 115 | t.Logf("Successfully captured %d packets", numPkts) 116 | 117 | err = os.Remove(ofile) 118 | if err != nil { 119 | t.Fatalf("Failed to remote pcap file: %s", err) 120 | } 121 | 122 | return 123 | } 124 | 125 | func pcapCreate(intf string, filter string, readTo int32) (h *Pcap, err error) { 126 | h, err = Create("lo") 127 | if h == nil || err != nil { 128 | return 129 | } 130 | 131 | err = h.SetSnapLen(65535) 132 | if err != nil { 133 | return 134 | } 135 | 136 | err = h.SetReadTimeout(readTo) 137 | if err != nil { 138 | return 139 | } 140 | 141 | err = h.SetBufferSize(3 * 1024 * 1024) 142 | if err != nil { 143 | return 144 | } 145 | 146 | err = h.Activate() 147 | if err != nil { 148 | return 149 | } 150 | 151 | err = h.SetFilter(filter) 152 | if err != nil { 153 | return 154 | } 155 | 156 | return 157 | } 158 | 159 | func pcapOpenLive(intf string, filter string, readTo int32) (h *Pcap, err error) { 160 | h, err = OpenLive(intf, 65535, true, readTo) 161 | if h == nil || err != nil { 162 | return 163 | } 164 | 165 | err = h.SetFilter(filter) 166 | if err != nil { 167 | return 168 | } 169 | 170 | return 171 | } 172 | 173 | // Udp client which sends a fixed num of packets to given port after a fixed delay. 174 | // Delay ensures that capture code is ready to recv packets. 175 | func udpClient(port int, numPkts int, wait time.Duration, t *testing.T) { 176 | time.Sleep(wait) 177 | 178 | addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", port)) 179 | if err != nil { 180 | t.Logf("ERROR Failed to resolve udp addr err:%s", err) 181 | return 182 | } 183 | 184 | conn, err := net.DialUDP("udp", nil, addr) 185 | if err != nil { 186 | t.Logf("ERROR Failed to dial udp port:%d err:%s", port, err) 187 | return 188 | } 189 | 190 | t.Logf("Start packets to port:%d", port) 191 | 192 | pkt := []byte("hello") 193 | for i := 0; i < numPkts; i++ { 194 | if l, err := conn.Write(pkt); err != nil || l != len(pkt) { 195 | t.Logf("ERROR Failed to send packet size:%d wlen:%d err:%s", len(pkt), l, err) 196 | } 197 | } 198 | 199 | t.Logf("Completed sending packets to port:%d", port) 200 | } 201 | 202 | // Udp server which listens on a port and recvs a fixed number of packets. 203 | func udpSvr(port int, numPkts int, t *testing.T) { 204 | addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", port)) 205 | if err != nil { 206 | t.Logf("ERROR UDP Server: failed to resolve udp addr err:%s", err) 207 | return 208 | } 209 | 210 | sock, err := net.ListenUDP("udp", addr) 211 | if err != nil { 212 | t.Logf("ERROR UDP Server: failed to listen on port:%d err:%s", port, err) 213 | return 214 | } 215 | 216 | buf := make([]byte, 10, 1024) 217 | for i := 0; i < numPkts; i++ { 218 | _, _, err := sock.ReadFromUDP(buf) 219 | if err != nil { 220 | t.Logf("Failed to recv packets on port:%d err:%s", port, err) 221 | } 222 | } 223 | 224 | sock.Close() 225 | } 226 | -------------------------------------------------------------------------------- /test/pcap_files/Network_Join_Nokia_Mobile.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/miekg/pcap/7ba8bad341a2612676799159fc097162d7c49434/test/pcap_files/Network_Join_Nokia_Mobile.pcap -------------------------------------------------------------------------------- /tools/pass/pass.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Parses a pcap file, writes it back to disk, then verifies the files 4 | // are the same. 5 | import ( 6 | "bufio" 7 | "flag" 8 | "fmt" 9 | "io" 10 | "os" 11 | 12 | "github.com/miekg/pcap" 13 | ) 14 | 15 | var input *string = flag.String("input", "", "input file") 16 | var output *string = flag.String("output", "", "output file") 17 | var decode *bool = flag.Bool("decode", false, "print decoded packets") 18 | 19 | func copyPcap(dest, src string) { 20 | f, err := os.Open(src) 21 | if err != nil { 22 | fmt.Printf("couldn't open %q: %v\n", src, err) 23 | return 24 | } 25 | defer f.Close() 26 | reader, err := pcap.NewReader(bufio.NewReader(f)) 27 | if err != nil { 28 | fmt.Printf("couldn't create reader: %v\n", err) 29 | return 30 | } 31 | w, err := os.Create(dest) 32 | if err != nil { 33 | fmt.Printf("couldn't open %q: %v\n", dest, err) 34 | return 35 | } 36 | defer w.Close() 37 | buf := bufio.NewWriter(w) 38 | writer, err := pcap.NewWriter(buf, &reader.Header) 39 | if err != nil { 40 | fmt.Printf("couldn't create writer: %v\n", err) 41 | return 42 | } 43 | for { 44 | pkt := reader.Next() 45 | if pkt == nil { 46 | break 47 | } 48 | if *decode { 49 | pkt.Decode() 50 | fmt.Println(pkt.String()) 51 | } 52 | writer.Write(pkt) 53 | } 54 | buf.Flush() 55 | } 56 | 57 | func check(dest, src string) { 58 | f, err := os.Open(src) 59 | if err != nil { 60 | fmt.Printf("couldn't open %q: %v\n", src, err) 61 | return 62 | } 63 | defer f.Close() 64 | freader := bufio.NewReader(f) 65 | 66 | g, err := os.Open(dest) 67 | if err != nil { 68 | fmt.Printf("couldn't open %q: %v\n", src, err) 69 | return 70 | } 71 | defer g.Close() 72 | greader := bufio.NewReader(g) 73 | 74 | for { 75 | fb, ferr := freader.ReadByte() 76 | gb, gerr := greader.ReadByte() 77 | 78 | if ferr == io.EOF && gerr == io.EOF { 79 | break 80 | } 81 | if fb == gb { 82 | continue 83 | } 84 | fmt.Println("FAIL") 85 | return 86 | } 87 | 88 | fmt.Println("PASS") 89 | } 90 | 91 | func main() { 92 | flag.Parse() 93 | 94 | copyPcap(*output, *input) 95 | check(*output, *input) 96 | } 97 | -------------------------------------------------------------------------------- /tools/pcaptest/pcaptest.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/miekg/pcap" 9 | ) 10 | 11 | func min(x uint32, y uint32) uint32 { 12 | if x < y { 13 | return x 14 | } 15 | return y 16 | } 17 | 18 | func main() { 19 | var device *string = flag.String("d", "", "device") 20 | var file *string = flag.String("r", "", "file") 21 | var expr *string = flag.String("e", "", "filter expression") 22 | 23 | flag.Parse() 24 | 25 | var h *pcap.Pcap 26 | 27 | ifs, err := pcap.FindAllDevs() 28 | if len(ifs) == 0 { 29 | fmt.Printf("Warning: no devices found : %s\n", err) 30 | } else { 31 | for i := 0; i < len(ifs); i++ { 32 | fmt.Printf("dev %d: %s (%s)\n", i+1, ifs[i].Name, ifs[i].Description) 33 | } 34 | } 35 | 36 | if *device != "" { 37 | h, err = pcap.OpenLive(*device, 65535, true, 0) 38 | if h == nil { 39 | fmt.Printf("OpenLive(%s) failed: %s\n", *device, err) 40 | return 41 | } 42 | } else if *file != "" { 43 | h, err = pcap.OpenOffline(*file) 44 | if h == nil { 45 | fmt.Printf("Openoffline(%s) failed: %s\n", *file, err) 46 | return 47 | } 48 | } else { 49 | fmt.Printf("usage: pcaptest [-d | -r ]\n") 50 | return 51 | } 52 | 53 | fmt.Printf("pcap version: %s\n", pcap.Version()) 54 | 55 | if *expr != "" { 56 | fmt.Printf("Setting filter: %s\n", *expr) 57 | err := h.SetFilter(*expr) 58 | if err != nil { 59 | fmt.Printf("Warning: setting filter failed: %s\n", err) 60 | } 61 | } 62 | 63 | for pkt := h.Next(); pkt != nil; pkt = h.Next() { 64 | fmt.Printf("time: %d.%06d (%s) caplen: %d len: %d\nData:", 65 | int64(pkt.Time.Second()), int64(pkt.Time.Nanosecond()), 66 | time.Unix(int64(pkt.Time.Second()), 0).String(), int64(pkt.Caplen), int64(pkt.Len)) 67 | for i := uint32(0); i < pkt.Caplen; i++ { 68 | if i%32 == 0 { 69 | fmt.Printf("\n") 70 | } 71 | if 32 <= pkt.Data[i] && pkt.Data[i] <= 126 { 72 | fmt.Printf("%c", pkt.Data[i]) 73 | } else { 74 | fmt.Printf(".") 75 | } 76 | } 77 | fmt.Printf("\n\n") 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /tools/tcpdump/tcpdump.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "os/signal" 8 | 9 | "github.com/miekg/pcap" 10 | ) 11 | 12 | const ( 13 | TYPE_IP = 0x0800 14 | TYPE_ARP = 0x0806 15 | TYPE_IP6 = 0x86DD 16 | 17 | IP_ICMP = 1 18 | IP_INIP = 4 19 | IP_TCP = 6 20 | IP_UDP = 17 21 | ) 22 | 23 | var ( 24 | device = flag.String("i", "", "interface") 25 | ofile = flag.String("w", "", "file") 26 | snaplen = flag.Int("s", 65535, "snaplen") 27 | hexdump = flag.Bool("X", false, "hexdump") 28 | help = flag.Bool("h", false, "help") 29 | ) 30 | 31 | func main() { 32 | expr := "" 33 | 34 | flag.Usage = func() { 35 | fmt.Fprintf(os.Stderr, "usage: %s [ -i interface ] [ -s snaplen ] [ -X hexdump ] [ -w file ] [ -h show usage] [ expression ] \n", os.Args[0]) 36 | os.Exit(1) 37 | } 38 | 39 | flag.Parse() 40 | 41 | if len(flag.Args()) > 0 { 42 | expr = flag.Arg(0) 43 | } 44 | 45 | if *help { 46 | flag.Usage() 47 | } 48 | 49 | if *device == "" { 50 | devs, err := pcap.FindAllDevs() 51 | if err != nil { 52 | fmt.Fprintln(os.Stderr, "tcpdump: couldn't find any devices:", err) 53 | } 54 | if 0 == len(devs) { 55 | flag.Usage() 56 | } 57 | *device = devs[0].Name 58 | } 59 | 60 | h, err := pcap.OpenLive(*device, int32(*snaplen), true, 500) 61 | if h == nil { 62 | fmt.Fprintf(os.Stderr, "tcpdump: %s", err) 63 | return 64 | } 65 | defer h.Close() 66 | 67 | if expr != "" { 68 | fmt.Println("tcpdump: setting filter to", expr) 69 | ferr := h.SetFilter(expr) 70 | if ferr != nil { 71 | fmt.Println("tcpdump:", ferr) 72 | } 73 | } 74 | 75 | if *ofile != "" { 76 | dumper, oerr := h.DumpOpen(ofile) 77 | addHandler(h, dumper) 78 | if oerr != nil { 79 | fmt.Fprintln(os.Stderr, "tcpdump: couldn't write to file:", oerr) 80 | } 81 | _, lerr := h.PcapLoop(0, dumper) 82 | if lerr != nil { 83 | fmt.Fprintln(os.Stderr, "tcpdump: loop error:", lerr, h.Geterror()) 84 | } 85 | defer h.PcapDumpClose(dumper) 86 | return 87 | } 88 | 89 | for pkt, r := h.NextEx(); r >= 0; pkt, r = h.NextEx() { 90 | if r == 0 { 91 | // timeout, continue 92 | continue 93 | } 94 | pkt.Decode() 95 | fmt.Println(pkt) 96 | if *hexdump { 97 | Hexdump(pkt) 98 | } 99 | 100 | } 101 | fmt.Fprintln(os.Stderr, "tcpdump:", h.Geterror()) 102 | 103 | } 104 | 105 | func addHandler(h *pcap.Pcap, dumper *pcap.PcapDumper) { 106 | c := make(chan os.Signal, 1) 107 | signal.Notify(c, os.Interrupt) 108 | go func() { 109 | for sig := range c { 110 | fmt.Fprintln(os.Stderr, "tcpdump: received signal:", sig) 111 | if os.Interrupt == sig { 112 | h.PcapDumpClose(dumper) 113 | h.Close() 114 | os.Exit(1) 115 | } 116 | } 117 | }() 118 | } 119 | 120 | func min(a, b int) int { 121 | if a < b { 122 | return a 123 | } 124 | return b 125 | } 126 | 127 | func Hexdump(pkt *pcap.Packet) { 128 | for i := 0; i < len(pkt.Data); i += 16 { 129 | Dumpline(uint32(i), pkt.Data[i:min(i+16, len(pkt.Data))]) 130 | } 131 | } 132 | 133 | func Dumpline(addr uint32, line []byte) { 134 | fmt.Printf("\t0x%04x: ", int32(addr)) 135 | var i uint16 136 | for i = 0; i < 16 && i < uint16(len(line)); i++ { 137 | if i%2 == 0 { 138 | fmt.Print(" ") 139 | } 140 | fmt.Printf("%02x", line[i]) 141 | } 142 | for j := i; j <= 16; j++ { 143 | if j%2 == 0 { 144 | fmt.Print(" ") 145 | } 146 | fmt.Print(" ") 147 | } 148 | fmt.Print(" ") 149 | for i = 0; i < 16 && i < uint16(len(line)); i++ { 150 | if line[i] >= 32 && line[i] <= 126 { 151 | fmt.Printf("%c\n", line[i]) 152 | } else { 153 | fmt.Print(".") 154 | } 155 | } 156 | fmt.Println() 157 | } 158 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package pcap 2 | 3 | // GoVersion is the version of the pcap Go package. 4 | var GoVersion = "1.0.1" 5 | --------------------------------------------------------------------------------