6

I'm trying to send a UDP packet to 127.0.0.1 with gopacket. Here is my code:

package main

import (
    "fmt"
    "net"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
)

func main() {
    handle, err := pcap.OpenLive("lo", 1500, false, pcap.BlockForever)
    if err != nil {
        fmt.Printf("%s\n", err.Error())
        return
    }

    eth := layers.Ethernet{
        EthernetType: layers.EthernetTypeIPv4,
        SrcMAC:       net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
        DstMAC:       net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
    }

    ip := layers.IPv4{
        Version:  4,
        TTL:      64,
        SrcIP:    net.IP{127, 0, 0, 1},
        DstIP:    net.IP{127, 0, 0, 1},
        Protocol: layers.IPProtocolUDP,
    }

    udp := layers.UDP{
        SrcPort: 62003,
        DstPort: 8080,
    }
    udp.SetNetworkLayerForChecksum(&ip)

    payload := []byte{'a', 'b', 'c', '\n'}

    options := gopacket.SerializeOptions{
        ComputeChecksums: true,
        FixLengths:       true,
    }

    buffer := gopacket.NewSerializeBuffer()

    err = gopacket.SerializeLayers(buffer, options,
        &eth,
        &ip,
        &udp,
        gopacket.Payload(payload),
    )
    if err != nil {
        fmt.Printf("[-] Serialize error: %s\n", err.Error())
        return
    }
    outgoingPacket := buffer.Bytes()

    err = handle.WritePacketData(outgoingPacket)
    if err != nil {
        fmt.Printf("[-] Error while sending: %s\n", err.Error())
        return
    }

}

And in a terminal I listen to incoming packets with netcat:

nc -ulp 8080 -s 127.0.0.1

When I run my code, I can see the generated packet in wireshark on loopback interface with correct checksums but the packet never arrives to netcat. What may be the problem?

paddlesteamer
  • 63
  • 1
  • 6

1 Answers1

4

If you look at this diagram, you'll notice tcpdump acts on the Ethernet layer. Then comes, IP then TCP/UDP, then Sockets. nc operates at TCP/UDP layer.

In the IP level, the packets might be getting dropped. Very often the case is Reverse Path Filtering.

So, you are able to see the packets arriving at the ethernet layer which can be seen by tcpdump, but packets are not arriving for nc because they might be routed somewhere else, or dropped.

So, it's better to check if disabling RP filtering and checking iptable rules, helps!

Update:

As you're operating on the loopback interface:

MAC's are used at the lowest level of ethernet traffic, and only within one LAN and help direct traffic around within it. It isn't needed on a local network interface (lo) because packets are being handled internally.

The loopback address connects to the same computer directly in the IP layer without using any physical hardware. So, it lets you bypass Ethernet, PPP, other drivers.

package main

import (
    "fmt"
    "net"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
)

func main() {
    handle, err := pcap.OpenLive("lo0", 1500, false, pcap.BlockForever)
    if err != nil {
        fmt.Printf("%s\n", err.Error())
        return
    }

    eth := layers.Ethernet{
        EthernetType: layers.EthernetTypeIPv4,
        SrcMAC:       net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
        DstMAC:       net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
    }

    _ = eth // Ignore. Use where ethernet interface is used

    // Used for loopback interface
    lo := layers.Loopback{
        Family: layers.ProtocolFamilyIPv4,
    }

    ip := layers.IPv4{
        Version:  4,
        TTL:      64,
        SrcIP:    net.IP{127, 0, 0, 1},
        DstIP:    net.IP{127, 0, 0, 1},
        Protocol: layers.IPProtocolUDP,
    }

    udp := layers.UDP{
        SrcPort: 62003,
        DstPort: 9000,
    }
    udp.SetNetworkLayerForChecksum(&ip)

    payload := []byte{'a', 'b', 'c', '\n'}

    options := gopacket.SerializeOptions{
        ComputeChecksums: true,
        FixLengths:       true,
    }

    buffer := gopacket.NewSerializeBuffer()

    // if err = gopacket.SerializeLayers(buffer, options,
    //  &eth,
    //  &ip,
    //  &udp,
    //  gopacket.Payload(payload),
    // ); err != nil {
    //  fmt.Printf("[-] Serialize error: %s\n", err.Error())
    //  return
    // }
    if err = gopacket.SerializeLayers(buffer, options,
        &lo,
        &ip,
        &udp,
        gopacket.Payload(payload),
    ); err != nil {
        fmt.Printf("[-] Serialize error: %s\n", err.Error())
        return
    }
    outgoingPacket := buffer.Bytes()

    if err = handle.WritePacketData(outgoingPacket); err != nil {
        fmt.Printf("[-] Error while sending: %s\n", err.Error())
        return
    }

}

enter image description here

I'm running the program on macOS Catalina, and if you see the screenshot it's working. nc can receive the custom generated packet. In case the checksum isn't ok, then it would be dropped.

I hope it helps!

Community
  • 1
  • 1
shmsr
  • 3,802
  • 2
  • 18
  • 29
  • Did it fix your issue? – shmsr Jun 04 '20 at 17:31
  • I turned off rp_filter in both /proc/sys/net/ipv4/conf/lo and /all then changed all my iptables rules to ACCEPT to no avail. RP filtering does not seem to be the cause of the packets being dropped particularly since these are calls from 127.0.0.1 -> 127.0.0.1 which are valid. – user1634494 Jun 04 '20 at 19:33
  • For me, rp_filtering was already disabled and with changing 'layers.Ethernet' to 'layers.Loopback' I don't even see the packet in tcpdump. – paddlesteamer Jun 07 '20 at 14:36
  • Please change `lo0` to `lo` if you haven't. And yes, `rp_filtering` won't matter here; you can enable it if you want. – shmsr Jun 07 '20 at 14:37
  • If you want, we can debug this on chat. Join this room`https://chat.stackoverflow.com/rooms/215469/gopacket` and share the commands, OS you're using. – shmsr Jun 07 '20 at 14:43