mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00

Moves the vendor/ directory to cmd/vendor. Vendored binaries are built from cmd/, which is backed by symlinks pointing back to repo root.
528 lines
12 KiB
Go
528 lines
12 KiB
Go
package pcap
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"net"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
TYPE_IP = 0x0800
|
|
TYPE_ARP = 0x0806
|
|
TYPE_IP6 = 0x86DD
|
|
TYPE_VLAN = 0x8100
|
|
|
|
IP_ICMP = 1
|
|
IP_INIP = 4
|
|
IP_TCP = 6
|
|
IP_UDP = 17
|
|
)
|
|
|
|
const (
|
|
ERRBUF_SIZE = 256
|
|
|
|
// According to pcap-linktype(7).
|
|
LINKTYPE_NULL = 0
|
|
LINKTYPE_ETHERNET = 1
|
|
LINKTYPE_TOKEN_RING = 6
|
|
LINKTYPE_ARCNET = 7
|
|
LINKTYPE_SLIP = 8
|
|
LINKTYPE_PPP = 9
|
|
LINKTYPE_FDDI = 10
|
|
LINKTYPE_ATM_RFC1483 = 100
|
|
LINKTYPE_RAW = 101
|
|
LINKTYPE_PPP_HDLC = 50
|
|
LINKTYPE_PPP_ETHER = 51
|
|
LINKTYPE_C_HDLC = 104
|
|
LINKTYPE_IEEE802_11 = 105
|
|
LINKTYPE_FRELAY = 107
|
|
LINKTYPE_LOOP = 108
|
|
LINKTYPE_LINUX_SLL = 113
|
|
LINKTYPE_LTALK = 104
|
|
LINKTYPE_PFLOG = 117
|
|
LINKTYPE_PRISM_HEADER = 119
|
|
LINKTYPE_IP_OVER_FC = 122
|
|
LINKTYPE_SUNATM = 123
|
|
LINKTYPE_IEEE802_11_RADIO = 127
|
|
LINKTYPE_ARCNET_LINUX = 129
|
|
LINKTYPE_LINUX_IRDA = 144
|
|
LINKTYPE_LINUX_LAPD = 177
|
|
)
|
|
|
|
type addrHdr interface {
|
|
SrcAddr() string
|
|
DestAddr() string
|
|
Len() int
|
|
}
|
|
|
|
type addrStringer interface {
|
|
String(addr addrHdr) string
|
|
}
|
|
|
|
func decodemac(pkt []byte) uint64 {
|
|
mac := uint64(0)
|
|
for i := uint(0); i < 6; i++ {
|
|
mac = (mac << 8) + uint64(pkt[i])
|
|
}
|
|
return mac
|
|
}
|
|
|
|
// Decode decodes the headers of a Packet.
|
|
func (p *Packet) Decode() {
|
|
if len(p.Data) <= 14 {
|
|
return
|
|
}
|
|
|
|
p.Type = int(binary.BigEndian.Uint16(p.Data[12:14]))
|
|
p.DestMac = decodemac(p.Data[0:6])
|
|
p.SrcMac = decodemac(p.Data[6:12])
|
|
|
|
if len(p.Data) >= 15 {
|
|
p.Payload = p.Data[14:]
|
|
}
|
|
|
|
switch p.Type {
|
|
case TYPE_IP:
|
|
p.decodeIp()
|
|
case TYPE_IP6:
|
|
p.decodeIp6()
|
|
case TYPE_ARP:
|
|
p.decodeArp()
|
|
case TYPE_VLAN:
|
|
p.decodeVlan()
|
|
}
|
|
}
|
|
|
|
func (p *Packet) headerString(headers []interface{}) string {
|
|
// If there's just one header, return that.
|
|
if len(headers) == 1 {
|
|
if hdr, ok := headers[0].(fmt.Stringer); ok {
|
|
return hdr.String()
|
|
}
|
|
}
|
|
// If there are two headers (IPv4/IPv6 -> TCP/UDP/IP..)
|
|
if len(headers) == 2 {
|
|
// Commonly the first header is an address.
|
|
if addr, ok := p.Headers[0].(addrHdr); ok {
|
|
if hdr, ok := p.Headers[1].(addrStringer); ok {
|
|
return fmt.Sprintf("%s %s", p.Time, hdr.String(addr))
|
|
}
|
|
}
|
|
}
|
|
// For IP in IP, we do a recursive call.
|
|
if len(headers) >= 2 {
|
|
if addr, ok := headers[0].(addrHdr); ok {
|
|
if _, ok := headers[1].(addrHdr); ok {
|
|
return fmt.Sprintf("%s > %s IP in IP: ",
|
|
addr.SrcAddr(), addr.DestAddr(), p.headerString(headers[1:]))
|
|
}
|
|
}
|
|
}
|
|
|
|
var typeNames []string
|
|
for _, hdr := range headers {
|
|
typeNames = append(typeNames, reflect.TypeOf(hdr).String())
|
|
}
|
|
|
|
return fmt.Sprintf("unknown [%s]", strings.Join(typeNames, ","))
|
|
}
|
|
|
|
// String prints a one-line representation of the packet header.
|
|
// The output is suitable for use in a tcpdump program.
|
|
func (p *Packet) String() string {
|
|
// If there are no headers, print "unsupported protocol".
|
|
if len(p.Headers) == 0 {
|
|
return fmt.Sprintf("%s unsupported protocol %d", p.Time, int(p.Type))
|
|
}
|
|
return fmt.Sprintf("%s %s", p.Time, p.headerString(p.Headers))
|
|
}
|
|
|
|
// Arphdr is a ARP packet header.
|
|
type Arphdr struct {
|
|
Addrtype uint16
|
|
Protocol uint16
|
|
HwAddressSize uint8
|
|
ProtAddressSize uint8
|
|
Operation uint16
|
|
SourceHwAddress []byte
|
|
SourceProtAddress []byte
|
|
DestHwAddress []byte
|
|
DestProtAddress []byte
|
|
}
|
|
|
|
func (arp *Arphdr) String() (s string) {
|
|
switch arp.Operation {
|
|
case 1:
|
|
s = "ARP request"
|
|
case 2:
|
|
s = "ARP Reply"
|
|
}
|
|
if arp.Addrtype == LINKTYPE_ETHERNET && arp.Protocol == TYPE_IP {
|
|
s = fmt.Sprintf("%012x (%s) > %012x (%s)",
|
|
decodemac(arp.SourceHwAddress), arp.SourceProtAddress,
|
|
decodemac(arp.DestHwAddress), arp.DestProtAddress)
|
|
} else {
|
|
s = fmt.Sprintf("addrtype = %d protocol = %d", arp.Addrtype, arp.Protocol)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (p *Packet) decodeArp() {
|
|
if len(p.Payload) < 8 {
|
|
return
|
|
}
|
|
|
|
pkt := p.Payload
|
|
arp := new(Arphdr)
|
|
arp.Addrtype = binary.BigEndian.Uint16(pkt[0:2])
|
|
arp.Protocol = binary.BigEndian.Uint16(pkt[2:4])
|
|
arp.HwAddressSize = pkt[4]
|
|
arp.ProtAddressSize = pkt[5]
|
|
arp.Operation = binary.BigEndian.Uint16(pkt[6:8])
|
|
|
|
if len(pkt) < int(8+2*arp.HwAddressSize+2*arp.ProtAddressSize) {
|
|
return
|
|
}
|
|
arp.SourceHwAddress = pkt[8 : 8+arp.HwAddressSize]
|
|
arp.SourceProtAddress = pkt[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
|
|
arp.DestHwAddress = pkt[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
|
|
arp.DestProtAddress = pkt[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]
|
|
|
|
p.Headers = append(p.Headers, arp)
|
|
|
|
if len(pkt) >= int(8+2*arp.HwAddressSize+2*arp.ProtAddressSize) {
|
|
p.Payload = p.Payload[8+2*arp.HwAddressSize+2*arp.ProtAddressSize:]
|
|
}
|
|
}
|
|
|
|
// IPadr is the header of an IP packet.
|
|
type Iphdr struct {
|
|
Version uint8
|
|
Ihl uint8
|
|
Tos uint8
|
|
Length uint16
|
|
Id uint16
|
|
Flags uint8
|
|
FragOffset uint16
|
|
Ttl uint8
|
|
Protocol uint8
|
|
Checksum uint16
|
|
SrcIp []byte
|
|
DestIp []byte
|
|
}
|
|
|
|
func (p *Packet) decodeIp() {
|
|
if len(p.Payload) < 20 {
|
|
return
|
|
}
|
|
|
|
pkt := p.Payload
|
|
ip := new(Iphdr)
|
|
|
|
ip.Version = uint8(pkt[0]) >> 4
|
|
ip.Ihl = uint8(pkt[0]) & 0x0F
|
|
ip.Tos = pkt[1]
|
|
ip.Length = binary.BigEndian.Uint16(pkt[2:4])
|
|
ip.Id = binary.BigEndian.Uint16(pkt[4:6])
|
|
flagsfrags := binary.BigEndian.Uint16(pkt[6:8])
|
|
ip.Flags = uint8(flagsfrags >> 13)
|
|
ip.FragOffset = flagsfrags & 0x1FFF
|
|
ip.Ttl = pkt[8]
|
|
ip.Protocol = pkt[9]
|
|
ip.Checksum = binary.BigEndian.Uint16(pkt[10:12])
|
|
ip.SrcIp = pkt[12:16]
|
|
ip.DestIp = pkt[16:20]
|
|
|
|
pEnd := int(ip.Length)
|
|
if pEnd > len(pkt) {
|
|
pEnd = len(pkt)
|
|
}
|
|
|
|
if len(pkt) >= pEnd && int(ip.Ihl*4) < pEnd {
|
|
p.Payload = pkt[ip.Ihl*4 : pEnd]
|
|
} else {
|
|
p.Payload = []byte{}
|
|
}
|
|
|
|
p.Headers = append(p.Headers, ip)
|
|
p.IP = ip
|
|
|
|
switch ip.Protocol {
|
|
case IP_TCP:
|
|
p.decodeTcp()
|
|
case IP_UDP:
|
|
p.decodeUdp()
|
|
case IP_ICMP:
|
|
p.decodeIcmp()
|
|
case IP_INIP:
|
|
p.decodeIp()
|
|
}
|
|
}
|
|
|
|
func (ip *Iphdr) SrcAddr() string { return net.IP(ip.SrcIp).String() }
|
|
func (ip *Iphdr) DestAddr() string { return net.IP(ip.DestIp).String() }
|
|
func (ip *Iphdr) Len() int { return int(ip.Length) }
|
|
|
|
type Vlanhdr struct {
|
|
Priority byte
|
|
DropEligible bool
|
|
VlanIdentifier int
|
|
Type int // Not actually part of the vlan header, but the type of the actual packet
|
|
}
|
|
|
|
func (v *Vlanhdr) String() {
|
|
fmt.Sprintf("VLAN Priority:%d Drop:%v Tag:%d", v.Priority, v.DropEligible, v.VlanIdentifier)
|
|
}
|
|
|
|
func (p *Packet) decodeVlan() {
|
|
pkt := p.Payload
|
|
vlan := new(Vlanhdr)
|
|
if len(pkt) < 4 {
|
|
return
|
|
}
|
|
|
|
vlan.Priority = (pkt[2] & 0xE0) >> 13
|
|
vlan.DropEligible = pkt[2]&0x10 != 0
|
|
vlan.VlanIdentifier = int(binary.BigEndian.Uint16(pkt[:2])) & 0x0FFF
|
|
vlan.Type = int(binary.BigEndian.Uint16(p.Payload[2:4]))
|
|
p.Headers = append(p.Headers, vlan)
|
|
|
|
if len(pkt) >= 5 {
|
|
p.Payload = p.Payload[4:]
|
|
}
|
|
|
|
switch vlan.Type {
|
|
case TYPE_IP:
|
|
p.decodeIp()
|
|
case TYPE_IP6:
|
|
p.decodeIp6()
|
|
case TYPE_ARP:
|
|
p.decodeArp()
|
|
}
|
|
}
|
|
|
|
type Tcphdr struct {
|
|
SrcPort uint16
|
|
DestPort uint16
|
|
Seq uint32
|
|
Ack uint32
|
|
DataOffset uint8
|
|
Flags uint16
|
|
Window uint16
|
|
Checksum uint16
|
|
Urgent uint16
|
|
Data []byte
|
|
}
|
|
|
|
const (
|
|
TCP_FIN = 1 << iota
|
|
TCP_SYN
|
|
TCP_RST
|
|
TCP_PSH
|
|
TCP_ACK
|
|
TCP_URG
|
|
TCP_ECE
|
|
TCP_CWR
|
|
TCP_NS
|
|
)
|
|
|
|
func (p *Packet) decodeTcp() {
|
|
if len(p.Payload) < 20 {
|
|
return
|
|
}
|
|
|
|
pkt := p.Payload
|
|
tcp := new(Tcphdr)
|
|
tcp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
|
|
tcp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
|
|
tcp.Seq = binary.BigEndian.Uint32(pkt[4:8])
|
|
tcp.Ack = binary.BigEndian.Uint32(pkt[8:12])
|
|
tcp.DataOffset = (pkt[12] & 0xF0) >> 4
|
|
tcp.Flags = binary.BigEndian.Uint16(pkt[12:14]) & 0x1FF
|
|
tcp.Window = binary.BigEndian.Uint16(pkt[14:16])
|
|
tcp.Checksum = binary.BigEndian.Uint16(pkt[16:18])
|
|
tcp.Urgent = binary.BigEndian.Uint16(pkt[18:20])
|
|
if len(pkt) >= int(tcp.DataOffset*4) {
|
|
p.Payload = pkt[tcp.DataOffset*4:]
|
|
}
|
|
p.Headers = append(p.Headers, tcp)
|
|
p.TCP = tcp
|
|
}
|
|
|
|
func (tcp *Tcphdr) String(hdr addrHdr) string {
|
|
return fmt.Sprintf("TCP %s:%d > %s:%d %s SEQ=%d ACK=%d LEN=%d",
|
|
hdr.SrcAddr(), int(tcp.SrcPort), hdr.DestAddr(), int(tcp.DestPort),
|
|
tcp.FlagsString(), int64(tcp.Seq), int64(tcp.Ack), hdr.Len())
|
|
}
|
|
|
|
func (tcp *Tcphdr) FlagsString() string {
|
|
var sflags []string
|
|
if 0 != (tcp.Flags & TCP_SYN) {
|
|
sflags = append(sflags, "syn")
|
|
}
|
|
if 0 != (tcp.Flags & TCP_FIN) {
|
|
sflags = append(sflags, "fin")
|
|
}
|
|
if 0 != (tcp.Flags & TCP_ACK) {
|
|
sflags = append(sflags, "ack")
|
|
}
|
|
if 0 != (tcp.Flags & TCP_PSH) {
|
|
sflags = append(sflags, "psh")
|
|
}
|
|
if 0 != (tcp.Flags & TCP_RST) {
|
|
sflags = append(sflags, "rst")
|
|
}
|
|
if 0 != (tcp.Flags & TCP_URG) {
|
|
sflags = append(sflags, "urg")
|
|
}
|
|
if 0 != (tcp.Flags & TCP_NS) {
|
|
sflags = append(sflags, "ns")
|
|
}
|
|
if 0 != (tcp.Flags & TCP_CWR) {
|
|
sflags = append(sflags, "cwr")
|
|
}
|
|
if 0 != (tcp.Flags & TCP_ECE) {
|
|
sflags = append(sflags, "ece")
|
|
}
|
|
return fmt.Sprintf("[%s]", strings.Join(sflags, " "))
|
|
}
|
|
|
|
type Udphdr struct {
|
|
SrcPort uint16
|
|
DestPort uint16
|
|
Length uint16
|
|
Checksum uint16
|
|
}
|
|
|
|
func (p *Packet) decodeUdp() {
|
|
if len(p.Payload) < 8 {
|
|
return
|
|
}
|
|
|
|
pkt := p.Payload
|
|
udp := new(Udphdr)
|
|
udp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
|
|
udp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
|
|
udp.Length = binary.BigEndian.Uint16(pkt[4:6])
|
|
udp.Checksum = binary.BigEndian.Uint16(pkt[6:8])
|
|
p.Headers = append(p.Headers, udp)
|
|
p.UDP = udp
|
|
if len(p.Payload) >= 8 {
|
|
p.Payload = pkt[8:]
|
|
}
|
|
}
|
|
|
|
func (udp *Udphdr) String(hdr addrHdr) string {
|
|
return fmt.Sprintf("UDP %s:%d > %s:%d LEN=%d CHKSUM=%d",
|
|
hdr.SrcAddr(), int(udp.SrcPort), hdr.DestAddr(), int(udp.DestPort),
|
|
int(udp.Length), int(udp.Checksum))
|
|
}
|
|
|
|
type Icmphdr struct {
|
|
Type uint8
|
|
Code uint8
|
|
Checksum uint16
|
|
Id uint16
|
|
Seq uint16
|
|
Data []byte
|
|
}
|
|
|
|
func (p *Packet) decodeIcmp() *Icmphdr {
|
|
if len(p.Payload) < 8 {
|
|
return nil
|
|
}
|
|
|
|
pkt := p.Payload
|
|
icmp := new(Icmphdr)
|
|
icmp.Type = pkt[0]
|
|
icmp.Code = pkt[1]
|
|
icmp.Checksum = binary.BigEndian.Uint16(pkt[2:4])
|
|
icmp.Id = binary.BigEndian.Uint16(pkt[4:6])
|
|
icmp.Seq = binary.BigEndian.Uint16(pkt[6:8])
|
|
p.Payload = pkt[8:]
|
|
p.Headers = append(p.Headers, icmp)
|
|
return icmp
|
|
}
|
|
|
|
func (icmp *Icmphdr) String(hdr addrHdr) string {
|
|
return fmt.Sprintf("ICMP %s > %s Type = %d Code = %d ",
|
|
hdr.SrcAddr(), hdr.DestAddr(), icmp.Type, icmp.Code)
|
|
}
|
|
|
|
func (icmp *Icmphdr) TypeString() (result string) {
|
|
switch icmp.Type {
|
|
case 0:
|
|
result = fmt.Sprintf("Echo reply seq=%d", icmp.Seq)
|
|
case 3:
|
|
switch icmp.Code {
|
|
case 0:
|
|
result = "Network unreachable"
|
|
case 1:
|
|
result = "Host unreachable"
|
|
case 2:
|
|
result = "Protocol unreachable"
|
|
case 3:
|
|
result = "Port unreachable"
|
|
default:
|
|
result = "Destination unreachable"
|
|
}
|
|
case 8:
|
|
result = fmt.Sprintf("Echo request seq=%d", icmp.Seq)
|
|
case 30:
|
|
result = "Traceroute"
|
|
}
|
|
return
|
|
}
|
|
|
|
type Ip6hdr struct {
|
|
// http://www.networksorcery.com/enp/protocol/ipv6.htm
|
|
Version uint8 // 4 bits
|
|
TrafficClass uint8 // 8 bits
|
|
FlowLabel uint32 // 20 bits
|
|
Length uint16 // 16 bits
|
|
NextHeader uint8 // 8 bits, same as Protocol in Iphdr
|
|
HopLimit uint8 // 8 bits
|
|
SrcIp []byte // 16 bytes
|
|
DestIp []byte // 16 bytes
|
|
}
|
|
|
|
func (p *Packet) decodeIp6() {
|
|
if len(p.Payload) < 40 {
|
|
return
|
|
}
|
|
|
|
pkt := p.Payload
|
|
ip6 := new(Ip6hdr)
|
|
ip6.Version = uint8(pkt[0]) >> 4
|
|
ip6.TrafficClass = uint8((binary.BigEndian.Uint16(pkt[0:2]) >> 4) & 0x00FF)
|
|
ip6.FlowLabel = binary.BigEndian.Uint32(pkt[0:4]) & 0x000FFFFF
|
|
ip6.Length = binary.BigEndian.Uint16(pkt[4:6])
|
|
ip6.NextHeader = pkt[6]
|
|
ip6.HopLimit = pkt[7]
|
|
ip6.SrcIp = pkt[8:24]
|
|
ip6.DestIp = pkt[24:40]
|
|
|
|
if len(p.Payload) >= 40 {
|
|
p.Payload = pkt[40:]
|
|
}
|
|
|
|
p.Headers = append(p.Headers, ip6)
|
|
|
|
switch ip6.NextHeader {
|
|
case IP_TCP:
|
|
p.decodeTcp()
|
|
case IP_UDP:
|
|
p.decodeUdp()
|
|
case IP_ICMP:
|
|
p.decodeIcmp()
|
|
case IP_INIP:
|
|
p.decodeIp()
|
|
}
|
|
}
|
|
|
|
func (ip6 *Ip6hdr) SrcAddr() string { return net.IP(ip6.SrcIp).String() }
|
|
func (ip6 *Ip6hdr) DestAddr() string { return net.IP(ip6.DestIp).String() }
|
|
func (ip6 *Ip6hdr) Len() int { return int(ip6.Length) }
|