123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- package packet
- import (
- "encoding/binary"
- "fmt"
- "net/netip"
- "github.com/google/gopacket"
- "github.com/google/gopacket/layers"
- "golang.org/x/net/icmp"
- "golang.org/x/net/ipv4"
- "golang.org/x/net/ipv6"
- )
- const (
- ipv4MinHeaderLen = 20
- ipv6HeaderLen = 40
- ipv4MinMTU = 576
- ipv6MinMTU = 1280
- icmpHeaderLen = 8
- // https://www.rfc-editor.org/rfc/rfc792 and https://datatracker.ietf.org/doc/html/rfc4443#section-3.3 define 2 codes.
- // 0 = ttl exceed in transit, 1 = fragment reassembly time exceeded
- icmpTTLExceedInTransitCode = 0
- DefaultTTL uint8 = 255
- pseudoHeaderLen = 40
- )
- // Packet represents an IP packet or a packet that is encapsulated by IP
- type Packet interface {
- // IPLayer returns the IP of the packet
- IPLayer() *IP
- // EncodeLayers returns the layers that make up this packet. They can be passed to an Encoder to serialize into RawPacket
- EncodeLayers() ([]gopacket.SerializableLayer, error)
- }
- // IP represents a generic IP packet. It can be embedded in more specific IP protocols
- type IP struct {
- Src netip.Addr
- Dst netip.Addr
- Protocol layers.IPProtocol
- TTL uint8
- }
- func newIPv4(ipLayer *layers.IPv4) (*IP, error) {
- src, ok := netip.AddrFromSlice(ipLayer.SrcIP)
- if !ok {
- return nil, fmt.Errorf("cannot convert source IP %s to netip.Addr", ipLayer.SrcIP)
- }
- dst, ok := netip.AddrFromSlice(ipLayer.DstIP)
- if !ok {
- return nil, fmt.Errorf("cannot convert source IP %s to netip.Addr", ipLayer.DstIP)
- }
- return &IP{
- Src: src,
- Dst: dst,
- Protocol: ipLayer.Protocol,
- TTL: ipLayer.TTL,
- }, nil
- }
- func newIPv6(ipLayer *layers.IPv6) (*IP, error) {
- src, ok := netip.AddrFromSlice(ipLayer.SrcIP)
- if !ok {
- return nil, fmt.Errorf("cannot convert source IP %s to netip.Addr", ipLayer.SrcIP)
- }
- dst, ok := netip.AddrFromSlice(ipLayer.DstIP)
- if !ok {
- return nil, fmt.Errorf("cannot convert source IP %s to netip.Addr", ipLayer.DstIP)
- }
- return &IP{
- Src: src,
- Dst: dst,
- Protocol: ipLayer.NextHeader,
- TTL: ipLayer.HopLimit,
- }, nil
- }
- func (ip *IP) IPLayer() *IP {
- return ip
- }
- func (ip *IP) isIPv4() bool {
- return ip.Src.Is4()
- }
- func (ip *IP) EncodeLayers() ([]gopacket.SerializableLayer, error) {
- if ip.isIPv4() {
- return []gopacket.SerializableLayer{
- &layers.IPv4{
- Version: 4,
- SrcIP: ip.Src.AsSlice(),
- DstIP: ip.Dst.AsSlice(),
- Protocol: layers.IPProtocol(ip.Protocol),
- TTL: ip.TTL,
- },
- }, nil
- } else {
- return []gopacket.SerializableLayer{
- &layers.IPv6{
- Version: 6,
- SrcIP: ip.Src.AsSlice(),
- DstIP: ip.Dst.AsSlice(),
- NextHeader: layers.IPProtocol(ip.Protocol),
- HopLimit: ip.TTL,
- },
- }, nil
- }
- }
- // ICMP represents is an IP packet + ICMP message
- type ICMP struct {
- *IP
- *icmp.Message
- }
- func (i *ICMP) EncodeLayers() ([]gopacket.SerializableLayer, error) {
- ipLayers, err := i.IP.EncodeLayers()
- if err != nil {
- return nil, err
- }
- var serializedPsh []byte = nil
- if i.Protocol == layers.IPProtocolICMPv6 {
- psh := &PseudoHeader{
- SrcIP: i.Src.As16(),
- DstIP: i.Dst.As16(),
- // i.Marshal re-calculates the UpperLayerPacketLength
- UpperLayerPacketLength: 0,
- NextHeader: uint8(i.Protocol),
- }
- serializedPsh = psh.Marshal()
- }
- msg, err := i.Marshal(serializedPsh)
- if err != nil {
- return nil, err
- }
- icmpLayer := gopacket.Payload(msg)
- return append(ipLayers, icmpLayer), nil
- }
- // https://www.rfc-editor.org/rfc/rfc2460#section-8.1
- type PseudoHeader struct {
- SrcIP [16]byte
- DstIP [16]byte
- UpperLayerPacketLength uint32
- zero [3]byte
- NextHeader uint8
- }
- func (ph *PseudoHeader) Marshal() []byte {
- buf := make([]byte, pseudoHeaderLen)
- index := 0
- copy(buf, ph.SrcIP[:])
- index += 16
- copy(buf[index:], ph.DstIP[:])
- index += 16
- binary.BigEndian.PutUint32(buf[index:], ph.UpperLayerPacketLength)
- index += 4
- copy(buf[index:], ph.zero[:])
- buf[pseudoHeaderLen-1] = ph.NextHeader
- return buf
- }
- func NewICMPTTLExceedPacket(originalIP *IP, originalPacket RawPacket, routerIP netip.Addr) *ICMP {
- var (
- protocol layers.IPProtocol
- icmpType icmp.Type
- )
- if originalIP.Dst.Is4() {
- protocol = layers.IPProtocolICMPv4
- icmpType = ipv4.ICMPTypeTimeExceeded
- } else {
- protocol = layers.IPProtocolICMPv6
- icmpType = ipv6.ICMPTypeTimeExceeded
- }
- return &ICMP{
- IP: &IP{
- Src: routerIP,
- Dst: originalIP.Src,
- Protocol: protocol,
- TTL: DefaultTTL,
- },
- Message: &icmp.Message{
- Type: icmpType,
- Code: icmpTTLExceedInTransitCode,
- Body: &icmp.TimeExceeded{
- Data: originalDatagram(originalPacket, originalIP.Dst.Is4()),
- },
- },
- }
- }
- // originalDatagram returns a slice of the original datagram for ICMP error messages
- // https://www.rfc-editor.org/rfc/rfc1812#section-4.3.2.3 suggests to copy as much without exceeding 576 bytes.
- // https://datatracker.ietf.org/doc/html/rfc4443#section-3.3 suggests to copy as much without exceeding 1280 bytes
- func originalDatagram(originalPacket RawPacket, isIPv4 bool) []byte {
- var upperBound int
- if isIPv4 {
- upperBound = ipv4MinMTU - ipv4MinHeaderLen - icmpHeaderLen
- if upperBound > len(originalPacket.Data) {
- upperBound = len(originalPacket.Data)
- }
- } else {
- upperBound = ipv6MinMTU - ipv6HeaderLen - icmpHeaderLen
- if upperBound > len(originalPacket.Data) {
- upperBound = len(originalPacket.Data)
- }
- }
- return originalPacket.Data[:upperBound]
- }
|