fl0cke
fl0cke

Reputation: 2884

IPv6 packet header manipulation

I need to inspect and modify the IPv6 extension headers. So I set up a raw socket to listen for all IP packets on the local address.

package main

import (
    "log"
    "net"
)

func main() {
    c, err := net.ListenIP("ip6:tcp", &net.IPAddr{
        IP:   net.IPv6loopback,
        Zone: "",
    })
    if err != nil {
        panic(err)
    }

    buf := make([]byte, 1024)
    for {
        numRead, ipaddr, err := c.ReadFromIP(buf)
        log.Print(numRead, ipaddr, err)
        log.Printf("% X\n", buf[:numRead])
    }
}

I tried all the Read*() methods on the connection but it seems like they just return the payload without the header. So my question is: How can I access the IPv6 header of a packet?

Upvotes: 2

Views: 1423

Answers (1)

cnicutar
cnicutar

Reputation: 182639

Raw sockets with IPv6 are different compared to IPv4. Relevant for your case is RFC 3542. Note:

Another difference from IPv4 raw sockets is that complete packets (that is, IPv6 packets with extension headers) cannot be sent or received using the IPv6 raw sockets API. Instead, ancillary data objects are used to transfer the extension headers and hoplimit information, as described in Section 6. Should an application need access to the complete IPv6 packet, some other technique, such as the datalink interfaces BPF or DLPI, must be used.


You can find this out on your own. By running (and strace'ing) your program on my box for every packet I get:

recvfrom(3, "\x45\x00\x00\x3c\x6a\x7d...
               ^^

This means IP version = 4, IHL = 5 (20 bytes).

When I try the same thing with IPv6 (i.e. your original code), I get something different every time:

recvfrom(3, "\xc6\x22\x00\x50\x4d

In this case every time the kernel is returning stuff starting directly with TCP (in this case the port is 50722, i.e 0xC622).

Another interesting part should be noted in the sources:

switch sa := sa.(type) {
case *syscall.SockaddrInet4:
    addr = &IPAddr{IP: sa.Addr[0:]}
    n = stripIPv4Header(n, b)
case *syscall.SockaddrInet6:
    addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
}

The header is stripped manually for IPv4 but not for IPv6: for IPv6 there is nothing to remove.


Note, there are mechanisms that will return extra information (but by no means the whole packet). For example the IPV6_RECVPKTINFO socket option will give you access to:

struct in6_pktinfo {
    struct in6_addr ipi6_addr;    /* src/dst IPv6 address */
    unsigned int    ipi6_ifindex; /* send/recv interface index */
};

Similar options exist for the routing header option, hop limit etc.

Upvotes: 3

Related Questions