Home

Awesome

packet

Implements layer 2, IPv4, IPv6, UDP, TCP and application level packet processing.

The motivation for this package is to enable fast, memory efficient parsing of network packets and tracking of LAN hosts. The package automatically track host IPs on the LAN and notifies when the IP is online offline.

Features

Fast parsing

The package is designed to operate in low power CPUs (i.e. ARM) and with limited memory. It uses memory mapped slices to provide direct access to protocol fields without copying to a new structure. This technique provides fast access to fields with zero allocation and the compiler inlines most protocol field functions to a memory reference.

Use Session to capture raw network packets

Session provides easy access to read raw packets from the wire. Session automatically places the nic in promiscuous mode so we receive all packets hiting the network card including packets sent by us.

	s, err := packet.NewSession("eth0")
	if err != nil {
		fmt.Printf("conn error: %s", err)
		return
	}
	defer s.Close()

	buffer := make([]byte, packet.EthMaxSize)
	for {
		n, _, err := s.ReadFrom(buffer)
		if err != nil {
			fmt.Println("error reading packet", err)
			return
		}
        // process packet...
    }

Use Parse() to map a raw packet into protocol types

Parse() provides a memory mapping of protocols to slices without copying or allocation. Given a network packet b that contains a udp frame inside an ip4 and ethernet frame, you can access all protocol fields after calling Parse(). Parse returns a Frame structure with accessor for all interesting bits.

  n, _, _ := s.ReadFrom(b)          // read network packet
  frame, err := session.Parse(b[:n]) // fast parse and set references
  ether := frame.Ether()            // memory mapped slice to access ether fields; return nil if not valid ethernet
  ip := frame.IP4()                 // memory mapped slice to access ipv4 fields; return nil if it does not contain IP4
  udp := frame.UDP()                // memory mapped slice to access udp fields; return nil if it does not contain UDP
  payload := frame.Payload()        // memory mapped slice to access payload

  fmt.Println("ether", ether.Src(), ether.Dst())
  fmt.Println("ip4", ip.Src(), ip.Dst())
  fmt.Println("udp", udp.SrcPort(), udp.DstPort())
  fmt.Printf("payloadID=%s payload=[%x]\n", frame.PayloadID, payload)

Payload identification

Frame contains a payload ID that identifies the last payload in the packet. In most cases, this will be the application protocol ID. For example, if the packet is a udp DHCP4 packet, PayloadID will return PayloadDHCP4 and Payload() will return a slice to the DHCP4 packet.

    frame, err := s.Parse(b[:n])
    // if err

    switch frame.PayloadID {
    case packet.PayloadARP: 
        // Process arp packets
    case packet.PayloadDHCP4: 
        // Process DHCPv4 packets
    }

IPv4 and IPv6 parsing

Working with IPv4, IPv6, UDP frames is fairly straight forward. For example:

  frame, err := packet.Parse(b[:n])
  if err != nil { panic(err)}
  
  // if you are only interested in mac and ip (either v4 or v6), 
  // frame.Src() and frame.Dst() are convenient structures to get this data.
  fmt.Println("srcAddr", frame.Src().MAC, frame.Src().IP) // will print source mac and ip
  fmt.Println("dstAddr", frame.Dst().MAC, frame.Dst().IP) // will print source mac and ip

  // if you are interested in the IPv4 fields
  if p := frame.IP4(); p != nil {
    // access ipv4 fields
    fmt.Println("version", p.Version(), "src", p.Src(), "dst", p.Dst(),"proto", p.Protocol(), "ttl", p.TTL())
  }

  // if you are intereste in the IPv6 fields
  if p := frame.IP6(); p != nil {
    fmt.Println("version", p.Version(), "src", p.Src(), "dst", p.Dst(),"nextHeader", p.NextHeader(), "hopLimit", p.HopLimit())
  }

  // if you are interested in UDP fields
  if p := frame.UDP(); p != nil {
    fmt.Println(p)
  }

Session provides notifications for Host online and offline

Session tracks when a host changes to online or offline and sends notifications via a go channel. The first time an IP is detected or when an existing host changes IP, Session sends an online notification via the notification channel. Similarly, if an IP is not seen on the network for 5 minutes or more, Session sends an offline notification via the notification channel.

s, err := packet.NewSession(*nic)
go func() {
    for {
        notification := <-s.C
        switch notification.Online {
        case true:
            fmt.Printf("is online: %s\n", notification)
        default:
            fmt.Printf("is offline: %s\n", notification)
        }
        s.PrintTable()
    }
}()

for {
    n, _, err := s.ReadFrom(buffer)
    if err != nil { panic(err) }

    frame, err := s.Parse(buffer[:n])
    // work on the packet...
}

arp spoofing

The package contains an arp_spoofer handler that can spoof the arp table on a target host. The handler will send poisoned arp packets continously to keep the arp cache pointing to us. See example arp_spoofer application.

If you like to spoof all hosts on a network, use the notification handler to start and stop the arp hunt for each host.

s, err := packet.NewSession(*nic)
defer s.Close()
arpSpoofer, err = arp.New(s)
defer arpSpoofer.Close()

go func() {
		for {
			notification := <-s.C
			switch notification.Online {
			case true:
				fmt.Printf("is online: %s\n", notification)
				arpSpoofer.StartHunt(notification.Addr)
			default:
				fmt.Printf("is offline: %s\n", notification)
				arpSpoofer.StopHunt(notification.Addr)
			}
			s.PrintTable()
		}
	}()
	
// In your main packet processing loop, call the s.Notify() function as the last action in the loop.
// See example here: https://github.com/irai/packet/tree/main/examples/hosts

IPv6 icmp spoofing

The package contains an icmpv6_spoofer handler that can spoof the neighgor discovery protocol on a target host making all IPv6 traffic redirect to us. The handler will send poisoned icmpv6 packets continously to keep the ndp cache pointing to us.

See example application below.

DHCP4 server and spoofing

need notes refer to example dhcp server.

DNS naming

The package includes a dns_naming handler that creates a map of names to a mac address to simplify host identification.

Examples

arp spoofer
dhcpd
dnslistener
hosts
tcpdump

Credits

Thanks to the following individuals for making their outstanding source code available to all to learn and re-use.

Many other packages contributed ideas or "code reuse" and references to these are mentioned in the source code.