mdlayher / netlink

Package netlink provides low-level access to Linux netlink sockets (AF_NETLINK). MIT Licensed.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

forward messages besides NLM_F_MULTI

florianl opened this issue · comments

Hi,
I've tried, to receive statistics from the conntrack subsystem.
Unfortunately, the first returned message has the flag NLM_F_MULTI. Because of this, there are only blocking recvmsg events and the actuall message is never returned.

Below, I've written a piece of code, which reproduces this issue:

package main

import (
	"fmt"

	"github.com/mdlayher/netlink"
	"github.com/mdlayher/netlink/nlenc"

	"golang.org/x/sys/unix"
)

func putExtraHeader(familiy, version uint8, resid uint16) []byte {
	buf := make([]byte, 2)
	nlenc.PutUint16(buf, resid)
	return append([]byte{familiy, version}, buf...)
}

func main() {
	con, err := netlink.Dial(unix.NETLINK_NETFILTER, nil)
	if err != nil {
		fmt.Println("Could not open socket to NETLINK_NETFILTER")
		return
	}

	data := putExtraHeader(unix.AF_UNSPEC, unix.NFNETLINK_V0, 0)

	req := netlink.Message{
		Header: netlink.Header{
			Type: netlink.HeaderType( 0x1 << 8 | 5),
			Flags: netlink.HeaderFlagsRequest | netlink.HeaderFlagsDump,
		},
		Data: data,
	}
	con.Execute(req)
}

If you run this peace of code with strace, you see the request is sent to the kernel and then follows a blocking recvmsg event.

@ti-mo gave me the hint, to replace netlink.HeaderFlagsDump with netlink.HeaderFlagsAcknowledge. This way, the kernel returns only one value and not a series of messages, starting with a NLM_F_MULTI flaged message. 👍

As a result, maybe it would be an idea, to provide an call to get messages directly?

@florianl What change are you suggesting?

The call you're running in particular (I assume IPCTNL_MSG_CT_GET_STATS by the looks of it) returns, by definition, a multipart response (https://github.com/torvalds/linux/blob/v4.16/net/netfilter/nf_conntrack_netlink.c#L2115). Because it's a query that only has a single response attribute implemented (CTA_STATS_GLOBAL_ENTRIES), with the possibility of being extended, you need to signal to the underlying (netlink) layer to follow up with an ack, or it will expect the next message until it sees an ack. This behaviour should be altered by the dump flag, but the function I linked obeys no such flag, presumably because it only knows a single type.

I don't see this as a bug in the library; this is a kernel oddity (one of many), and reading the source is the only way to write client code for it. This call's sibling, IPCTNL_MSG_CT_GET_STATS_CPU, does not suffer from this behaviour. 🙂

You've spotted IPCTNL_MSG_CT_GET_STATS correctly. Since kernel 4.18, you get two values [1] with this request.
I also don't see it as a bug of netlink. It's more like an idea to forward messages from the kernel directly, instead of handling them all, if the NLM_F_MULTI flag is set.

[1] https://github.com/torvalds/linux/blob/v4.18/net/netfilter/nf_conntrack_netlink.c#L2188