You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3018 lines
94 KiB
3018 lines
94 KiB
////
|
|
vim.syntax: asciidoc
|
|
|
|
Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
|
|
////
|
|
|
|
Netlink Library (libnl)
|
|
=======================
|
|
Thomas Graf <tgraf@suug.ch>
|
|
3.2, May 9 2011:
|
|
:numbered:
|
|
|
|
== Introduction
|
|
|
|
The core library contains the fundamentals required to communicate
|
|
over netlink sockets. It deals with connecting and disconnectng of
|
|
sockets, sending and receiving of data, construction and parsing of
|
|
messages, provides a customizeable receiving state machine, and
|
|
provides a abstract data type framework which eases the implementation
|
|
of object based netlink protocols where objects are added, removed, or
|
|
modified using a netlink based protocol.
|
|
|
|
.Library Hierarchy
|
|
|
|
The suite is split into multiple libraries:
|
|
|
|
image:library_overview.png["Library Hierarchy"]
|
|
|
|
link:core.html[Netlink Library] (libnl)::
|
|
Socket handling, sending and receiving, message construction and parsing, ...
|
|
|
|
link:route.html[Routing Family Library] (libnl-route)::
|
|
Adresses, links, neighbours, routing, traffic control, neighbour tables, ...
|
|
|
|
Netfilter Library (libnl-nf)::
|
|
Connection tracking, logging, queueing
|
|
|
|
Generic Netlink Library (libnl-genl)::
|
|
Controller API, family and command registration
|
|
|
|
|
|
=== How To Read This Documentation
|
|
|
|
The libraries provide a broad set of APIs of which most applications only
|
|
require a small subset of it. Depending on the type of application, some
|
|
users may only be interested in the low level netlink messaging API while
|
|
others wish to make heavy use of the high level API.
|
|
|
|
In any case it is recommended to get familiar with the netlink protocol
|
|
first.
|
|
|
|
- <<core_netlink_fundamentals>>
|
|
|
|
The low level APIs are described in:
|
|
|
|
- <<core_sockets>>
|
|
- <<core_send_recv>>
|
|
|
|
|
|
=== Linking to this Library
|
|
|
|
.Checking the presence of the library using autoconf
|
|
|
|
Projects using autoconf may use +PKG_CHECK_MODULES()+ to check if
|
|
a specific version of libnl is available on the system. The example
|
|
below also shows how to retrieve the +CFLAGS+ and linking dependencies
|
|
required to link against the library.
|
|
|
|
The following example shows how to check for a specific version of libnl. If
|
|
found, it extends the `CFLAGS` and `LIBS` variable appropriately:
|
|
|
|
[source]
|
|
----
|
|
PKG_CHECK_MODULES(LIBNL3, libnl-3.0 >= 3.1, [have_libnl3=yes], [have_libnl3=no])
|
|
if (test "${have_libnl3}" = "yes"); then
|
|
CFLAGS+="$LIBNL3_CFLAGS"
|
|
LIBS+="$LIBNL3_LIBS"
|
|
fi
|
|
----
|
|
|
|
NOTE: The pkgconfig file is named +libnl-3.0.pc+ for historic reasons, it also
|
|
covers library versions >= 3.1.
|
|
|
|
.Header Files
|
|
|
|
The main header file is `<netlink/netlink.h>`. Additional headers may need to
|
|
be included in your sources depending on the subsystems and components your
|
|
program makes use of.
|
|
|
|
[source,c]
|
|
-----
|
|
#include <netlink/netlink.h>
|
|
#include <netlink/cache.h>
|
|
#include <netlink/route/link.h>
|
|
-----
|
|
|
|
.Version Dependent Code
|
|
|
|
If your code wishes to be capable to link against multiple versions of libnl
|
|
you may have direct the compiler to only include portions on the code depending
|
|
on the version of libnl that it is compiled against.
|
|
|
|
[source,c]
|
|
-----
|
|
#include <netlink/version.h>
|
|
|
|
#if LIBNL_VER_NUM >= LIBNL_VER(3,1)
|
|
/* include code if compiled with libnl version >= 3.1 */
|
|
#endif
|
|
-----
|
|
|
|
.Linking
|
|
-----
|
|
$ gcc myprogram.c -o myprogram $(pkgconfig --cflags --libs libnl-3.0)
|
|
-----
|
|
|
|
=== Debugging
|
|
|
|
The library has been compiled with debugging statements enabled it will
|
|
print debug information to +stderr+ if the environment variable +NLDBG+
|
|
is set to > 0.
|
|
|
|
-----
|
|
$ NLDBG=2 ./myprogram
|
|
-----
|
|
|
|
.Debugging Levels
|
|
[options="header", width="80%", cols="1,5", align="center"]
|
|
|===============================================================
|
|
| Level | Description
|
|
| 0 | Debugging disabled (default)
|
|
| 1 | Warnings, important events and notifications
|
|
| 2 | More or less important debugging messages
|
|
| 3 | Repetitive events causing a flood of debugging messages
|
|
| 4 | Even less important messages
|
|
|===============================================================
|
|
|
|
.Debugging the Netlink Protocol
|
|
|
|
It is often useful to peek into the stream of netlink messages exchanged
|
|
with other sockets. Setting the environment variable +NLCB=debug+ will
|
|
cause the debugging message handlers to be used which in turn print the
|
|
netlink messages exchanged in a human readable format to to +stderr+:
|
|
|
|
-----
|
|
$ NLCB=debug ./myprogram
|
|
-- Debug: Sent Message:
|
|
-------------------------- BEGIN NETLINK MESSAGE ---------------------------
|
|
[HEADER] 16 octets
|
|
.nlmsg_len = 20
|
|
.nlmsg_type = 18 <route/link::get>
|
|
.nlmsg_flags = 773 <REQUEST,ACK,ROOT,MATCH>
|
|
.nlmsg_seq = 1301410712
|
|
.nlmsg_pid = 20014
|
|
[PAYLOAD] 16 octets
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
|
--------------------------- END NETLINK MESSAGE ---------------------------
|
|
-- Debug: Received Message:
|
|
-------------------------- BEGIN NETLINK MESSAGE ---------------------------
|
|
[HEADER] 16 octets
|
|
.nlmsg_len = 996
|
|
.nlmsg_type = 16 <route/link::new>
|
|
.nlmsg_flags = 2 <MULTI>
|
|
.nlmsg_seq = 1301410712
|
|
.nlmsg_pid = 20014
|
|
[PAYLOAD] 16 octets
|
|
00 00 04 03 01 00 00 00 49 00 01 00 00 00 00 00 ........I.......
|
|
[ATTR 03] 3 octets
|
|
6c 6f 00 lo.
|
|
[PADDING] 1 octets
|
|
00 .
|
|
[ATTR 13] 4 octets
|
|
00 00 00 00 ....
|
|
[ATTR 16] 1 octets
|
|
00 .
|
|
[PADDING] 3 octets
|
|
00 00 00 ...
|
|
[ATTR 17] 1 octets
|
|
00 .
|
|
[...]
|
|
--------------------------- END NETLINK MESSAGE ---------------------------
|
|
|
|
-----
|
|
|
|
[[core_netlink_fundamentals]]
|
|
== Netlink Protocol Fundamentals
|
|
|
|
The netlink protocol is a socket based IPC mechanism used for
|
|
communication between userspace processes and the kernel or between
|
|
userspace processes themselves. The netlink protocol is based on BSD
|
|
sockets and uses the +AF_NETLINK+ address family. Every netlink
|
|
protocol uses its own protocol number (e.g. +NETLINK_ROUTE+,
|
|
+NETLINK_NETFILTER+, etc). Its addressing schema is based on a 32 bit
|
|
port number, formerly referred to as PID, which uniquely identifies
|
|
each peer.
|
|
|
|
[[core_addressing]]
|
|
=== Addressing
|
|
|
|
The netlink address (port) consists of a 32bit integer. Port 0 (zero)
|
|
is reserved for the kernel and refers to the kernel side socket of each
|
|
netlink protocol family. Other port numbers usually refer to user space
|
|
owned sockets, although this is not enforced.
|
|
|
|
NOTE: In the beginning, it was common practice to use the process
|
|
identifier (PID) as the local port number. This became unpractical
|
|
with the introduction of threaded netlink applications and
|
|
applications requiring multiple sockets. Therefore libnl generates
|
|
unique port numbers based on the process identifier and adds an
|
|
offset to it allowing for multiple sockets to be used. The initial
|
|
socket will still equal to the process identifier for backwards
|
|
compatibility reasons.
|
|
|
|
image:addressing.png["Addressing Example"]
|
|
|
|
The above figure illustrates three applications and the kernel side
|
|
exposing two kernel side sockets. It shows the common netlink use
|
|
cases:
|
|
|
|
* User space to kernel
|
|
* User space to user space
|
|
* Listening to kernel multicast notifications
|
|
|
|
.User Space to Kernel
|
|
|
|
The most common form of netlink usage is for a user space application
|
|
to send requests to the kernel and process the reply which is either
|
|
an error message or a success notification.
|
|
|
|
["mscgen"]
|
|
--------
|
|
msc {
|
|
App1,App2,Kernel;
|
|
App1=>Kernel [label="request (src=11, dst=0)"];
|
|
App1<=Kernel [label="reply (src=0, dst=11)"];
|
|
...;
|
|
App2=>Kernel [label="request (src=21, dst=0)"];
|
|
App2<=Kernel [label="reply (src=0, dst=21)"];
|
|
}
|
|
--------
|
|
|
|
.User Space to User Space
|
|
|
|
Netlink may also be used as an IPC mechanism to communicate between user
|
|
space applications directly. Communication is not limited to two peers,
|
|
any number of peers may communicate with each other and multicasting
|
|
capabilities allow to reach multiple peers with a single message.
|
|
|
|
In order for the sockets to be visible to each other, both sockets must
|
|
be created for the same netlink protocol family.
|
|
|
|
["mscgen"]
|
|
--------
|
|
msc {
|
|
App2,App3;
|
|
App2=>App3 [label="request (src=22, dst=31)"];
|
|
App2<=App3 [label="reply (src=31, dst=22)"];
|
|
...;
|
|
}
|
|
--------
|
|
|
|
.User space listening to kernel notifications
|
|
|
|
This form of netlink communication is typically found in user space
|
|
daemons that need to act on certain kernel events. Such daemons will
|
|
typically maintain a netlink socket subscribed to a multicast group that
|
|
is used by the kernel to notify interested user space parties about
|
|
specific events.
|
|
|
|
["mscgen"]
|
|
--------
|
|
msc {
|
|
Kernel,App3;
|
|
Kernel=>App3 [label="notification (src=0, group=foo)"];
|
|
...;
|
|
}
|
|
--------
|
|
|
|
Use of multicasting is preferred over direct addressing due to the
|
|
flexibility in exchanging the user space component at any time without
|
|
the kernel noticing.
|
|
|
|
[[core_msg_format]]
|
|
=== Message Format
|
|
|
|
A netlink protocol is typically based on messages and consists of the
|
|
netlink message header (+struct nlmsghdr+) plus the payload attached
|
|
to it. The payload can consist of arbitrary data but usually contains
|
|
a fixed size protocol specific header followed by a stream of
|
|
attributes.
|
|
|
|
.Netlink message header (struct nlmsghdr)
|
|
|
|
image:nlmsghdr.png[align="center", alt="Netlink Message Header"]
|
|
|
|
Total Length (32bit)::
|
|
Total length of the message in bytes including the netlink message header.
|
|
|
|
Message Type (16bit)::
|
|
The message type specifies the type of payload the message is carrying.
|
|
Several standard message types are defined by the netlink protocol.
|
|
Additional message types may be defined by each protocol family. See
|
|
<<core_msg_types>> for additional information.
|
|
|
|
Message Flags (16bit)::
|
|
The message flags may be used to modify the behaviour of a message type.
|
|
See section <<core_msg_flags>> for a list of standard message flags.
|
|
|
|
Sequence Number (32bit)::
|
|
The sequence number is optional and may be used to allow referring to
|
|
a previous message, e.g. an error message can refer to the original
|
|
request causing the error.
|
|
|
|
Port Number (32bit)::
|
|
The port number specifies the peer to which the message should be delivered
|
|
to. If not specified, the message will be delivered to the first matching
|
|
kernel side socket of the same protocol family.
|
|
|
|
[[core_msg_types]]
|
|
=== Message Types
|
|
|
|
Netlink differs between requests, notifications, and replies. Requests
|
|
are messages which have the +NLM_F_REQUEST+ flag set and are meant to
|
|
request an action from the receiver. A request is typically sent from
|
|
a userspace process to the kernel. While not strictly enforced, requests
|
|
should carry a sequence number incremented for each request sent.
|
|
|
|
Depending on the nature of the request, the receiver may reply to the
|
|
request with another netlink message. The sequence number of a reply
|
|
must match the sequence number of the request it relates to.
|
|
|
|
Notifications are of informal nature and no reply is expected, therefore
|
|
the sequence number is typically set to 0.
|
|
|
|
["mscgen"]
|
|
--------
|
|
msc {
|
|
A,B;
|
|
A=>B [label="GET (seq=1, NLM_F_REQUEST)"];
|
|
A<=B [label="PUT (seq=1)"];
|
|
...;
|
|
A<=B [label="NOTIFY (seq=0)"];
|
|
}
|
|
--------
|
|
|
|
|
|
The type of message is primarly identified by its 16 bit message type set
|
|
in the message header. The following standard message types are defined:
|
|
|
|
- +NLMSG_NOOP+ - No operation, message must be discarded
|
|
- +NLMSG_ERROR+ - Error message or ACK, see <<core_errmsg>>
|
|
respectively <<core_msg_ack>>
|
|
- +NLMSG_DONE+ - End of multipart sequence, see <<core_multipart>>
|
|
- +NLMSG_OVERRUN+ - Overrun notification (Error)
|
|
|
|
Every netlink protocol is free to define own message types. Note that
|
|
message type values +< NLMSG_MIN_TYPE (0x10)+ are reserved and may
|
|
not be used.
|
|
|
|
It is common practice to use own message types to implement RPC schemas.
|
|
Suppose the goal of the netlink protocol you are implementing is allow
|
|
configuration of a particular network device, therefore you want to
|
|
provide read/write access to various configuration options. The typical
|
|
"netlink way" of doing this would be to define two message types
|
|
+MSG_SETCFG+, +MSG_GETCFG+:
|
|
|
|
[source,c]
|
|
--------
|
|
#define MSG_SETCFG 0x11
|
|
#define MSG_GETCFG 0x12
|
|
--------
|
|
|
|
Sending a +MSG_GETCFG+ request message will typically trigger a reply
|
|
with the message type +MSG_SETCFG+ containing the current configuration.
|
|
In object oriented terms one would describe this as "the kernel sets
|
|
the local copy of the configuration in userspace".
|
|
|
|
["mscgen"]
|
|
--------
|
|
msc {
|
|
A,B;
|
|
A=>B [label="MSG_GETCFG (seq=1, NLM_F_REQUEST)"];
|
|
A<=B [label="MSG_SETCFG (seq=1)"];
|
|
}
|
|
--------
|
|
|
|
The configuration may be changed by sending a +MSG_SETCFG+ which will
|
|
be responded to with either a ACK (see <<core_msg_ack>>)
|
|
or a error message (see <<core_errmsg>>).
|
|
|
|
["mscgen"]
|
|
--------
|
|
msc {
|
|
A,B;
|
|
A=>B [label="MSG_SETCFG (seq=1, NLM_F_REQUEST, NLM_F_ACK)"];
|
|
A<=B [label="ACK (seq=1)"];
|
|
}
|
|
--------
|
|
|
|
Optionally, the kernel may send out notifications for configuration
|
|
changes allowing userspace to listen for changes instead of polling
|
|
frequently. Notifications typically reuse an existing message type
|
|
and rely on the application using a separate socket to differ between
|
|
requests and notifications but you may also specify a separate message
|
|
type.
|
|
|
|
["mscgen"]
|
|
--------
|
|
msc {
|
|
A,B;
|
|
A<=B [label="MSG_SETCFG (seq=0)"];
|
|
}
|
|
--------
|
|
|
|
[[core_multipart]]
|
|
==== Multipart Messages
|
|
|
|
Although in theory a netlink message can be up to 4GiB in size. The socket
|
|
buffers are very likely not large enough to hold message of such sizes.
|
|
Therefore it is common to limit messages to one page size (PAGE_SIZE) and
|
|
use the multipart mechanism to split large pieces of data into several
|
|
messages. A multipart message has the flag +NLM_F_MULTI+ set and the
|
|
receiver is expected to continue receiving and parsing until the special
|
|
message type +NLMSG_DONE+ is received.
|
|
|
|
Multipart messages unlike fragmented ip packets must not be reassmbled
|
|
even though it is perfectly legal to do so if the protocols wishes to
|
|
work this way. Often multipart message are used to send lists or trees
|
|
of objects were each multipart message simply carries multiple objects
|
|
allow for each message to be parsed independently.
|
|
|
|
["mscgen"]
|
|
--------
|
|
msc {
|
|
A,B;
|
|
A=>B [label="GET (seq=1, NLM_F_REQUEST)"];
|
|
A<=B [label="PUT (seq=1, NLM_F_MULTI)"];
|
|
...;
|
|
A<=B [label="PUT (seq=1, NLM_F_MULTI)"];
|
|
A<=B [label="NLMSG_DONE (seq=1)"];
|
|
}
|
|
--------
|
|
|
|
[[core_errmsg]]
|
|
==== Error Message
|
|
|
|
Error messages can be sent in response to a request. Error messages must
|
|
use the standard message type +NLMSG_ERROR+. The payload consists of a
|
|
error code and the original netlink mesage header of the request.
|
|
|
|
image:nlmsgerr.png["Netlink Errror Message header"]
|
|
|
|
Error messages should set the sequence number to the sequence number
|
|
of the request which caused the error.
|
|
|
|
["mscgen"]
|
|
--------
|
|
msc {
|
|
A,B;
|
|
A=>B [label="GET (seq=1, NLM_F_REQUEST)"];
|
|
A<=B [label="NLMSG_ERROR code=EINVAL (seq=1)"];
|
|
}
|
|
--------
|
|
|
|
[[core_msg_ack]]
|
|
==== ACKs
|
|
|
|
A sender can request an ACK message to be sent back for each request
|
|
processed by setting the +NLM_F_ACK+ flag in the request. This is typically
|
|
used to allow the sender to synchronize further processing until the
|
|
request has been processed by the receiver.
|
|
|
|
["mscgen"]
|
|
--------
|
|
msc {
|
|
A,B;
|
|
A=>B [label="GET (seq=1, NLM_F_REQUEST | NLM_F_ACK)"];
|
|
A<=B [label="ACK (seq=1)"];
|
|
}
|
|
--------
|
|
|
|
ACK messages also use the message type +NLMSG_ERROR+ and payload
|
|
format but the error code is set to 0.
|
|
|
|
[[core_msg_flags]]
|
|
==== Message Flags
|
|
|
|
The following standard flags are defined
|
|
|
|
[source,c]
|
|
--------
|
|
#define NLM_F_REQUEST 1
|
|
#define NLM_F_MULTI 2
|
|
#define NLM_F_ACK 4
|
|
#define NLM_F_ECHO 8
|
|
--------
|
|
|
|
- `NLM_F_REQUEST` - Message is a request, see <<core_msg_types>>.
|
|
- `NLM_F_MULTI` - Multipart message, see <<core_multipart>>
|
|
- `NLM_F_ACK` - ACK message requested, see <<core_msg_ack>>.
|
|
- `NLM_F_ECHO` - Request to echo the request.
|
|
|
|
The flag +NLM_F_ECHO+ is similar to the `NLM_F_ACK` flag. It can be
|
|
used in combination with `NLM_F_REQUEST` and causes a notification
|
|
which is sent as a result of a request to also be sent to the sender
|
|
regardless of whether the sender has subscribed to the corresponding
|
|
multicast group or not. See <<core_multicast>>
|
|
|
|
Additional universal message flags are defined which only apply for
|
|
+GET+ requests:
|
|
|
|
[source,c]
|
|
--------
|
|
#define NLM_F_ROOT 0x100
|
|
#define NLM_F_MATCH 0x200
|
|
#define NLM_F_ATOMIC 0x400
|
|
#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
|
|
--------
|
|
|
|
- `NLM_F_ROOT` - Return based on root of tree.
|
|
- `NLM_F_MATCH` - Return all matching entries.
|
|
- `NLM_F_ATOMIC` - Obsoleted, once used to request an atomic operation.
|
|
- `NLM_F_DUMP` - Return a list of all objects
|
|
(`NLM_F_ROOT`|`NLM_F_MATCH`).
|
|
|
|
Use of these flags is completely optional and many netlink protocols only
|
|
make use of the `NLM_F_DUMP` flag which typically requests the receiver
|
|
to send a list of all objects in the context of the message type as a
|
|
sequence of multipart messages (see <<core_multipart>>).
|
|
|
|
Another set of flags exist related to `NEW` or `SET` requests. These
|
|
flags are mutually exclusive to the `GET` flags:
|
|
|
|
[source,c]
|
|
--------
|
|
#define NLM_F_REPLACE 0x100
|
|
#define NLM_F_EXCL 0x200
|
|
#define NLM_F_CREATE 0x400
|
|
#define NLM_F_APPEND 0x800
|
|
--------
|
|
|
|
- `NLM_F_REPLACE` - Replace an existing object if it exists.
|
|
- `NLM_F_EXCL` - Do not update object if it exists already.
|
|
- `NLM_F_CREATE` - Create object if it does not exist yet.
|
|
- `NLM_F_APPEND` - Add object at end of list.
|
|
|
|
Behaviour of these flags may differ slightly between different netlink
|
|
protocols.
|
|
|
|
[[core_seq_num]]
|
|
=== Sequence Numbers
|
|
|
|
Netlink allows the use of sequence numbers to help relate replies to
|
|
requests. It should be noted that unlike in protocols such as TCP
|
|
there is no strict enforcment of the sequence number. The sole purpose
|
|
of sequence numbers is to assist a sender in relating replies to the
|
|
corresponding requests. See <<core_msg_types>> for more information.
|
|
|
|
Sequence numbers are managed on a per socket basis, see
|
|
<<core_sk_seq_num>> for more information on how to use sequence numbers.
|
|
|
|
[[core_multicast]]
|
|
=== Multicast Groups
|
|
|
|
TODO
|
|
|
|
See <<core_sk_multicast>>
|
|
|
|
[[core_sockets]]
|
|
== Netlink Sockets
|
|
|
|
In order to use the netlink protocol, a netlink socket is required.
|
|
Each socket defines an independent context for sending and receiving of
|
|
messages. An application may make use multiple sockets, e.g. a socket to
|
|
send requests and receive the replies and another socket subscribed to a
|
|
multicast group to receive notifications.
|
|
|
|
=== Socket structure (struct nl_sock)
|
|
|
|
The netlink socket and all related attributes including the actual file
|
|
descriptor are represented by +struct nl_sock+.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
struct nl_sock *nl_socket_alloc(void)
|
|
void nl_socket_free(struct nl_sock *sk)
|
|
--------
|
|
|
|
The application must allocate an instance of +struct nl_sock+ for each
|
|
netlink socket it wishes to use.
|
|
|
|
[[core_sk_seq_num]]
|
|
=== Sequence Numbers
|
|
|
|
The library will automatically take care of sequence number handling
|
|
for the application. A sequence number counter is stored in the
|
|
socket structure which is used and incremented automatically when a
|
|
message needs to be sent which is expected to generate a reply such as
|
|
an error or any other message type that needs to be related to the
|
|
original message.
|
|
|
|
Alternatively, the counter can be used directly via the function
|
|
nl_socket_use_seq(). It will return the current value of the counter
|
|
and increment it by one afterwards.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
unsigned int nl_socket_use_seq(struct nl_sock *sk);
|
|
--------
|
|
|
|
Most applications will not want to deal with sequence number handling
|
|
themselves though. When using nl_send_auto() the sequence number is
|
|
filled in automatically and matched again when a reply is received. See
|
|
section <<core_send_recv>> for more information.
|
|
|
|
This behaviour can and must be disabled if the netlink protocol
|
|
implemented does not use a request/reply model, e.g. when a socket is
|
|
used to receive notification messages.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
void nl_socket_disable_seq_check(struct nl_sock *sk);
|
|
--------
|
|
|
|
For more information on the theory behind netlink sequence numbers,
|
|
see section <<core_seq_num>>.
|
|
|
|
[[core_sk_multicast]]
|
|
=== Multicast Group Subscriptions
|
|
|
|
Each socket can subscribe to any number of multicast groups of the
|
|
netlink protocol it is connected to. The socket will then receive a
|
|
copy of each message sent to any of the groups. Multicast groups are
|
|
commonly used to implement event notifications.
|
|
|
|
Prior to kernel 2.6.14 the group subscription was performed using a
|
|
bitmask which limited the number of groups per protocol family to 32.
|
|
This outdated interface can still be accessed via the function
|
|
nl_join_groups() even though it is not recommended for new code.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
void nl_join_groups(struct nl_sock *sk, int bitmask);
|
|
--------
|
|
|
|
Starting with 2.6.14 a new method was introduced which supports subscribing
|
|
to an almost infinite number of multicast groups.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
int nl_socket_add_memberships(struct nl_sock *sk, int group, ...);
|
|
int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...);
|
|
--------
|
|
|
|
==== Multicast Example
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/netlink.h>
|
|
#include <netlink/socket.h>
|
|
#include <netlink/msg.h>
|
|
|
|
/*
|
|
* This function will be called for each valid netlink message received
|
|
* in nl_recvmsgs_default()
|
|
*/
|
|
static int my_func(struct nl_msg *msg, void *arg)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
struct nl_sock *sk;
|
|
|
|
/* Allocate a new socket */
|
|
sk = nl_socket_alloc();
|
|
|
|
/*
|
|
* Notifications do not use sequence numbers, disable sequence number
|
|
* checking.
|
|
*/
|
|
nl_socket_disable_seq_check(sk);
|
|
|
|
/*
|
|
* Define a callback function, which will be called for each notification
|
|
* received
|
|
*/
|
|
nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL);
|
|
|
|
/* Connect to routing netlink protocol */
|
|
nl_connect(sk, NETLINK_ROUTE);
|
|
|
|
/* Subscribe to link notifications group */
|
|
nl_socket_add_memberships(sk, RTNLGRP_LINK, 0);
|
|
|
|
/*
|
|
* Start receiving messages. The function nl_recvmsgs_default() will block
|
|
* until one or more netlink messages (notification) are received which
|
|
* will be passed on to my_func().
|
|
*/
|
|
while (1)
|
|
nl_recvmsgs_default(sock);
|
|
--------
|
|
|
|
[[core_sk_cb]]
|
|
=== Modifiying Socket Callback Configuration
|
|
|
|
See <<core_cb>> for more information on
|
|
callback hooks and overwriting capabilities.
|
|
|
|
Each socket is assigned a callback configuration which controls the
|
|
behaviour of the socket. This is f.e. required to have a separate
|
|
message receive function per socket. It is perfectly legal to share
|
|
callback configurations between sockets though.
|
|
|
|
The following functions can be used to access and set the callback
|
|
configuration of a socket:
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk);
|
|
void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb);
|
|
--------
|
|
|
|
Additionaly a shortcut exists to modify the callback configuration
|
|
assigned to a socket directly:
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, enum nl_cb_kind kind,
|
|
nl_recvmsg_msg_cb_t func, void *arg);
|
|
--------
|
|
|
|
.Example:
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
// Call my_input() for all valid messages received in socket sk
|
|
nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_input, NULL);
|
|
--------
|
|
|
|
=== Socket Attributes
|
|
|
|
.Local Port
|
|
|
|
The local port number uniquely identifies the socket and is used to
|
|
address it. A unique local port is generated automatically when the
|
|
socket is allocated. It will consist of the Process ID (22 bits) and a
|
|
random number (10 bits) thus allowing up to 1024 sockets per process.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
uint32_t nl_socket_get_local_port(const struct nl_sock *sk);
|
|
void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port);
|
|
--------
|
|
|
|
See section <<core_addressing>> for more information on port numbers.
|
|
|
|
CAUTION: Overwriting the local port is possible but you have to ensure
|
|
that the provided value is unique and no other socket in any other
|
|
application is using the same value.
|
|
|
|
.Peer Port
|
|
|
|
A peer port can be assigned to the socket which will result in all
|
|
unicast messages sent over the socket to be addresses to the peer. If
|
|
no peer is specified, the message is sent to the kernel which will try
|
|
to automatically bind the socket to a kernel side socket of the same
|
|
netlink protocol family. It is common practice not to bind the socket
|
|
to a peer port as typically only one kernel side socket exists per
|
|
netlink protocol family.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
uint32_t nl_socket_get_peer_port(const struct nl_sock *sk);
|
|
void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port);
|
|
--------
|
|
|
|
See section <<core_addressing>> for more information on port numbers.
|
|
|
|
.File Descriptor
|
|
|
|
Netlink uses the BSD socket interface, therefore a file descriptor is
|
|
behind each socket and you may use it directly.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
int nl_socket_get_fd(const struct nl_sock *sk);
|
|
--------
|
|
|
|
If a socket is used to only receive notifications it usually is best
|
|
to put the socket in non-blocking mode and periodically poll for new
|
|
notifications.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
int nl_socket_set_nonblocking(const struct nl_sock *sk);
|
|
--------
|
|
|
|
.Send/Receive Buffer Size
|
|
|
|
The socket buffer is used to queue netlink messages between sender and
|
|
receiver. The size of these buffers specifies the maximum size you
|
|
will be able to write() to a netlink socket, i.e. it will indirectly
|
|
define the maximum message size. The default is 32KiB.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
int nl_socket_set_buffer_size(struct nl_sock *sk, int rx, int tx);
|
|
--------
|
|
|
|
[[core_sk_cred]]
|
|
.Enable/Disable Credentials
|
|
|
|
TODO
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
int nl_socket_set_passcred(struct nl_sock *sk, int state);
|
|
--------
|
|
|
|
.Enable/Disable Auto-ACK Mode
|
|
|
|
The following functions allow to enable/disable Auto-ACK mode on a socket.
|
|
See <<core_auto_ack>> for more information on what implications that has.
|
|
Auto-ACK mode is enabled by default.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
void nl_socket_enable_auto_ack(struct nl_sock *sk);
|
|
void nl_socket_disable_auto_ack(struct nl_sock *sk);
|
|
--------
|
|
|
|
.Enable/Disable Message Peeking
|
|
|
|
If enabled, message peeking causes nl_recv() to try and use MSG_PEEK
|
|
to retrieve the size of the next message received and allocate a
|
|
buffer of that size. Message peeking is enabled by default but can be
|
|
disabled using the following function:
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
void nl_socket_enable_msg_peek(struct nl_sock *sk);
|
|
void nl_socket_disable_msg_peek(struct nl_sock *sk);
|
|
--------
|
|
|
|
.Enable/Disable Receival of Packet Information
|
|
|
|
If enabled, each received netlink message from the kernel will include
|
|
an additional struct nl_pktinfo in the control message. The following
|
|
function can be used to enable/disable receival of packet information.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/socket.h>
|
|
|
|
int nl_socket_recv_pktinfo(struct nl_sock *sk, int state);
|
|
--------
|
|
|
|
CAUTION: Processing of NETLINK_PKTINFO has not been implemented yet.
|
|
|
|
[[core_send_recv]]
|
|
== Sending and Receiving of Messages / Data
|
|
|
|
[[core_send]]
|
|
=== Sending Messages
|
|
|
|
The standard method of sending a netlink message over a netlink socket
|
|
is to use the function nl_send_auto(). It will automatically complete
|
|
the netlink message by filling the missing bits and pieces in the
|
|
netlink message header and will deal with addressing based on the
|
|
options and address set in the netlink socket. The message is then
|
|
passed on to nl_send().
|
|
|
|
If the default sending semantics implemented by nl_send() do not suit
|
|
the application, it may overwrite the sending function nl_send() by
|
|
specifying an own implementation using the function
|
|
nl_cb_overwrite_send().
|
|
|
|
[source,c]
|
|
--------
|
|
nl_send_auto(sk, msg)
|
|
|
|
|
|-----> nl_complete_msg(sk, msg)
|
|
|
|
|
|
|
|
| Own send function specified via nl_cb_overwrite_send()
|
|
|- - - - - - - - - - - - - - - - - - - -
|
|
v v
|
|
nl_send(sk, msg) send_func()
|
|
--------
|
|
|
|
.Using nl_send()
|
|
|
|
If you do not require any of the automatic message completion
|
|
functionality you may use nl_send() directly but beware that any
|
|
internal calls to nl_send_auto() by the library to send netlink
|
|
messages will still use nl_send(). Therefore if you wish to use any
|
|
higher level interfaces and the behaviour of nl_send() is to your
|
|
dislike then you must overwrite the nl_send() function via
|
|
nl_cb_overwrite_send()
|
|
|
|
The purpose of nl_send() is to embed the netlink message into a iovec
|
|
structure and pass it on to nl_send_iovec().
|
|
|
|
[source,c]
|
|
--------
|
|
nl_send(sk, msg)
|
|
|
|
|
v
|
|
nl_send_iovec(sk, msg, iov, iovlen)
|
|
--------
|
|
|
|
.Using nl_send_iovec()
|
|
|
|
nl_send_iovec() expects a finalized netlink message and fills out the
|
|
struct msghdr used for addressing. It will first check if the struct
|
|
nl_msg is addressed to a specific peer (see nlmsg_set_dst()). If not,
|
|
it will try to fall back to the peer address specified in the socket
|
|
(see nl_socket_set_peer_port(). Otherwise the message will be sent
|
|
unaddressed and it is left to the kernel to find the correct peer.
|
|
|
|
nl_send_iovec() also adds credentials if present and enabled
|
|
(see <<core_sk_cred>>).
|
|
|
|
The message is then passed on to nl_sendmsg().
|
|
|
|
[source,c]
|
|
--------
|
|
nl_send_iovec(sk, msg, iov, iovlen)
|
|
|
|
|
v
|
|
nl_sendmsg(sk, msg, msghdr)
|
|
--------
|
|
|
|
.Using nl_sendmsg()
|
|
|
|
nl_sendmsg() expects a finalized netlink message and an optional
|
|
struct msghdr containing the peer address. It will copy the local
|
|
address as defined in the socket (see nl_socket_set_local_port()) into
|
|
the netlink message header.
|
|
|
|
At this point, construction of the message finished and it is ready to
|
|
be sent.
|
|
|
|
[source,c]
|
|
--------
|
|
nl_sendmsg(sk, msg, msghdr)
|
|
|- - - - - - - - - - - - - - - - - - - - v
|
|
| NL_CB_MSG_OUT()
|
|
|<- - - - - - - - - - - - - - - - - - - -+
|
|
v
|
|
sendmsg()
|
|
--------
|
|
|
|
Before sending the application has one last chance to modify the
|
|
message. It is passed to the NL_CB_MSG_OUT callback function which
|
|
may inspect or modify the message and return an error code. If this
|
|
error code is NL_OK the message is sent using sendmsg() resulting in
|
|
the number of bytes written being returned. Otherwise the message
|
|
sending process is aborted and the error code specified by the
|
|
callback function is returned. See <<core_sk_cb>> for more information
|
|
on how to set callbacks.
|
|
|
|
.Sending Raw Data with nl_sendto()
|
|
|
|
If you wish to send raw data over a netlink socket, the following
|
|
function will pass on any buffer provided to it directly to sendto():
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/netlink.h>
|
|
|
|
int nl_sendto(struct nl_sock *sk, void *buf, size_t size);
|
|
--------
|
|
|
|
.Sending of Simple Messages
|
|
|
|
A special interface exists for sending of trivial messages. The function
|
|
expects the netlink message type, optional netlink message flags, and an
|
|
optional data buffer and data length.
|
|
[source,c]
|
|
--------
|
|
#include <netlink/netlink.h>
|
|
|
|
int nl_send_simple(struct nl_sock *sk, int type, int flags,
|
|
void *buf, size_t size);
|
|
--------
|
|
|
|
The function will construct a netlink message header based on the message
|
|
type and flags provided and append the data buffer as message payload. The
|
|
newly constructed message is sent with nl_send_auto().
|
|
|
|
The following example will send a netlink request message causing the
|
|
kernel to dump a list of all network links to userspace:
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/netlink.h>
|
|
|
|
struct nl_sock *sk;
|
|
struct rtgenmsg rt_hdr = {
|
|
.rtgen_family = AF_UNSPEC,
|
|
};
|
|
|
|
sk = nl_socket_alloc();
|
|
nl_connect(sk, NETLINK_ROUTE);
|
|
|
|
nl_send_simple(sock, RTM_GETLINK, NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr));
|
|
--------
|
|
|
|
[[core_recv]]
|
|
=== Receiving Messages
|
|
|
|
The easiest method to receive netlink messages is to call nl_recvmsgs_default().
|
|
It will receive messages based on the semantics defined in the socket. The
|
|
application may customize these in detail although the default behaviour will
|
|
probably suit most applications.
|
|
|
|
nl_recvmsgs_default() will also be called internally by the library whenever
|
|
it needs to receive and parse a netlink message.
|
|
|
|
The function will fetch the callback configuration stored in the socket and
|
|
call nl_recvmsgs():
|
|
|
|
[source,c]
|
|
--------
|
|
nl_recvmsgs_default(sk)
|
|
|
|
|
| cb = nl_socket_get_cb(sk)
|
|
v
|
|
nl_recvmsgs(sk, cb)
|
|
--------
|
|
|
|
.Using nl_recvmsgs()
|
|
|
|
nl_recvmsgs() implements the actual receiving loop, it blocks until a
|
|
netlink message has been received unless the socket has been put into
|
|
non-blocking mode.
|
|
|
|
For the unlikely scenario that certain required receive characteristics
|
|
can not be achieved by fine tuning the internal recvmsgs function using
|
|
the callback configuration (see <<core_sk_cb>>) the application may provide
|
|
a complete own implementation of it and overwrite all calls to nl_recvmsgs()
|
|
with the function nl_cb_overwrite_recvmsgs().
|
|
|
|
[source,c]
|
|
--------
|
|
nl_recvmsgs(sk, cb)
|
|
|
|
|
| Own recvmsgs function specified via nl_cb_overwrite_recvmsgs()
|
|
|- - - - - - - - - - - - - - - - - - - -
|
|
v v
|
|
internal_recvmsgs() my_recvmsgs()
|
|
--------
|
|
|
|
[[core_recv_character]]
|
|
.Receive Characteristics
|
|
|
|
If the application does not provide its own recvmsgs() implementation
|
|
with the function nl_cb_overwrite_recvmsgs() the following characteristics
|
|
apply while receiving data from a netlink socket:
|
|
|
|
[source,c]
|
|
--------
|
|
internal_recvmsgs()
|
|
|
|
|
+-------------->| Own recv function specified with nl_cb_overwrite_recv()
|
|
| |- - - - - - - - - - - - - - - -
|
|
| v v
|
|
| nl_recv() my_recv()
|
|
| |<- - - - - - - - - - - - - - -+
|
|
| |<-------------+
|
|
| v | More data to parse? (nlmsg_next())
|
|
| Parse Message |
|
|
| |--------------+
|
|
| v
|
|
+------- NLM_F_MULTI set?
|
|
|
|
|
v
|
|
(SUCCESS)
|
|
--------
|
|
|
|
The function nl_recv() is invoked first to receive data from the
|
|
netlink socket. This function may be overwritten by the application
|
|
by an own implementation using the function nl_cb_overwrite_recv().
|
|
This may be useful if the netlink byte stream is in fact not received
|
|
from a socket directly but is read from a file or another source.
|
|
|
|
If data has been read, it will be attemped to parse the data. This
|
|
will be done repeately until the parser returns NL_STOP, an error was
|
|
returned or all data has been parsed.
|
|
|
|
In case the last message parsed successfully was a multipart message
|
|
(see <<core_multipart>>) and the parser did not
|
|
quit due to either an error or NL_STOP nl_recv() respectively the
|
|
applications own implementation will be called again and the parser
|
|
starts all over.
|
|
|
|
See <<core_parse_character>> for information on how to extract valid
|
|
netlink messages from the parser and on how to control the behaviour
|
|
of it.
|
|
|
|
[[core_parse_character]]
|
|
.Parsing Characteristics
|
|
|
|
The internal parser is invoked for each netlink message received from
|
|
a netlink socket. It is typically fed by nl_recv() (see
|
|
<<core_recv_character>>).
|
|
|
|
The parser will first ensure that the length of the data stream
|
|
provided is sufficient to contain a netlink message header and that
|
|
the message length as specified in the message header does not exceed
|
|
it.
|
|
|
|
If this criteria is met, a new struct nl_msg is allocated and the
|
|
message is passed on to the the callback function NL_CB_MSG_IN if one
|
|
is set. Like any other callback function, it may return NL_SKIP to
|
|
skip the current message but continue parsing the next message or
|
|
NL_STOP to stop parsing completely.
|
|
|
|
The next step is to check the sequence number of the message against
|
|
the currently expected sequence number. The application may provide
|
|
its own sequence number checking algorithm by setting the callback
|
|
function NL_CB_SEQ_CHECK to its own implementation. In fact, calling
|
|
nl_socket_disable_seq_check() to disable sequence number checking will
|
|
do nothing more than set the NL_CB_SEQ_CHECK hook to a function which
|
|
always returns NL_OK.
|
|
|
|
Another callback hook NL_CB_SEND_ACK exists which is called if the
|
|
message has the NLM_F_ACK flag set. Although I am not aware of any
|
|
userspace netlink socket doing this, the application may want to send
|
|
an ACK message back to the sender (see <<core_msg_ack>>).
|
|
|
|
[source,c]
|
|
--------
|
|
parse()
|
|
|
|
|
v
|
|
nlmsg_ok() --> Ignore
|
|
|
|
|
|- - - - - - - - - - - - - - - v
|
|
| NL_CB_MSG_IN()
|
|
|<- - - - - - - - - - - - - - -+
|
|
|
|
|
|- - - - - - - - - - - - - - - v
|
|
Sequence Check NL_CB_SEQ_CHECK()
|
|
|<- - - - - - - - - - - - - - -+
|
|
|
|
|
| Message has NLM_F_ACK set
|
|
|- - - - - - - - - - - - - - - v
|
|
| NL_CB_SEND_ACK()
|
|
|<- - - - - - - - - - - - - - -+
|
|
|
|
|
Handle Message Type
|
|
--------
|
|
|
|
[[core_auto_ack]]
|
|
=== Auto-ACK Mode
|
|
|
|
TODO
|
|
|
|
== Message Parsing & Construction
|
|
|
|
=== Message Format
|
|
|
|
See <<core_netlink_fundamentals>> for an introduction to the netlink
|
|
protocol and its message format.
|
|
|
|
.Alignment
|
|
|
|
Most netlink protocols enforce a strict alignment policy for all
|
|
boundries. The alignment value is defined by NLMSG_ALIGNTO and is
|
|
fixed to 4 bytes. Therefore all netlink message headers, begin of
|
|
payload sections, protocol specific headers, and attribute sections
|
|
must start at an offset which is a multiple of NLMSG_ALIGNTO.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
int nlmsg_size(int payloadlen);
|
|
int nlmsg_total_size(int payloadlen);
|
|
--------
|
|
|
|
The library provides a set of function to handle alignment
|
|
requirements automatically. The function nlmsg_total_size() returns
|
|
the total size of a netlink message including the padding to ensure
|
|
the next message header is aligned correctly.
|
|
|
|
[source,c]
|
|
--------
|
|
<----------- nlmsg_total_size(len) ------------>
|
|
<----------- nlmsg_size(len) ------------>
|
|
+-------------------+- - -+- - - - - - - - +- - -+-------------------+- - -
|
|
| struct nlmsghdr | Pad | Payload | Pad | struct nlsmghdr |
|
|
+-------------------+- - -+- - - - - - - - +- - -+-------------------+- - -
|
|
<---- NLMSG_HDRLEN -----> <- NLMSG_ALIGN(len) -> <---- NLMSG_HDRLEN ---
|
|
--------
|
|
|
|
If you need to know if padding needs to be added at the end of a
|
|
message, nlmsg_padlen() returns the number of padding bytes that need
|
|
to be added for a specific payload length.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
int nlmsg_padlen(int payloadlen);
|
|
--------
|
|
|
|
=== Parsing a Message
|
|
|
|
The library offers two different methods of parsing netlink messages.
|
|
It offers a low level interface for applications which want to do all
|
|
the parsing manually. This method is described below. Alternatively
|
|
the library also offers an interface to implement a parser as part of
|
|
a cache operations set which is especially useful when your protocol
|
|
deals with objects of any sort such as network links, routes, etc.
|
|
This high level interface is described in <<core_cache>>.
|
|
|
|
.Splitting a byte stream into separate messages
|
|
|
|
What you receive from a netlink socket is typically a stream of
|
|
messages. You will be given a buffer and its length, the buffer may
|
|
contain any number of netlink messages.
|
|
|
|
The first message header starts at the beginning of message stream.
|
|
Any subsequent message headers are access by calling nlmsg_next() on
|
|
the previous header.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
struct nlmsghdr *nlmsg_next(struct nlmsghdr *hdr, int *remaining);
|
|
--------
|
|
|
|
The function nlmsg_next() will automatically substract the size of the
|
|
previous message from the remaining number of bytes.
|
|
|
|
Please note, there is no indication in the previous message whether
|
|
another message follows or not. You must assume that more messages
|
|
follow until all bytes of the message stream have been processed.
|
|
|
|
To simplify this, the function nlmsg_ok() exists which returns true if
|
|
another message fits into the remaining number of bytes in the message
|
|
stream. nlmsg_valid_hdr() is similar, it checks whether a specific
|
|
netlink message contains at least a minimum of payload.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
int nlmsg_valid_hdr(const struct nlmsghdr *hdr, int payloadlen);
|
|
int nlmsg_ok(const struct nlmsghdr *hdr, int remaining);
|
|
--------
|
|
|
|
A typical use of these functions looks like this:
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
void my_parse(void *stream, int length)
|
|
{
|
|
struct nlmsghdr *hdr = stream;
|
|
|
|
while (nlmsg_ok(hdr, length)) {
|
|
// Parse message here
|
|
hdr = nlmsg_next(hdr, &length);
|
|
}
|
|
}
|
|
--------
|
|
|
|
CAUTION: nlmsg_ok() only returns true if the *complete* message including
|
|
the message payload fits into the remaining buffer length. It will
|
|
return false if only a part of it fits.
|
|
|
|
The above can also be written using the iterator nlmsg_for_each():
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
struct nlmsghdr *hdr;
|
|
|
|
nlmsg_for_each(hdr, stream, length) {
|
|
/* do something with message */
|
|
}
|
|
--------
|
|
|
|
.Message Payload
|
|
|
|
The message payload is appended to the message header and is guranteed
|
|
to start at a multiple of +NLMSG_ALIGNTO+. Padding at the end of the
|
|
message header is added if necessary to ensure this. The function
|
|
nlmsg_data() will calculate the necessary offset based on the message
|
|
and returns a pointer to the start of the message payload.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
void *nlmsg_data(const struct nlmsghdr *nlh);
|
|
void *nlmsg_tail(const struct nlmsghdr *nlh);
|
|
int nlmsg_datalen(const struct nlmsghdr *nlh);
|
|
--------
|
|
|
|
The length of the message payload is returned by nlmsg_datalen().
|
|
|
|
[source,c]
|
|
--------
|
|
<--- nlmsg_datalen(nlh) --->
|
|
+-------------------+- - -+----------------------------+- - -+
|
|
| struct nlmsghdr | Pad | Payload | Pad |
|
|
+-------------------+- - -+----------------------------+- - -+
|
|
nlmsg_data(nlh) ---------------^ ^
|
|
nlmsg_tail(nlh) --------------------------------------------------^
|
|
--------
|
|
|
|
The payload may consist of arbitary data but may have strict alignment
|
|
and formatting rules depening on the actual netlink protocol.
|
|
|
|
[[core_msg_attr]]
|
|
.Message Attributes
|
|
|
|
Most netlink protocols use netlink attributes. It not only makes the
|
|
protocol self documenting but also gives flexibility in expanding the
|
|
protocol at a later point. New attributes can be added at any time and
|
|
older attributes can be obsoleted by newer ones without breaking
|
|
binary compatibility of the protocol.
|
|
|
|
[source,c]
|
|
--------
|
|
<---------------------- payload ------------------------->
|
|
<----- hdrlen ----> <- nlmsg_attrlen(nlh, hdrlen) ->
|
|
+-------------------+- - -+----- ------------+- - -+--------------------------------+- - -+
|
|
| struct nlmsghdr | Pad | Protocol Header | Pad | Attributes | Pad |
|
|
+-------------------+- - -+-------------------+- - -+--------------------------------+- - -+
|
|
nlmsg_attrdata(nlh, hdrlen) -----------------------------^
|
|
--------
|
|
|
|
The function nlmsg_attrdata() returns a pointer to the begin of the
|
|
attributes section. The length of the attributes section is returned
|
|
by the function nlmsg_attrlen().
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
struct nlattr *nlmsg_attrdata(const struct nlmsghdr *hdr, int hdrlen);
|
|
int nlmsg_attrlen(const struct nlmsghdr *hdr, int hdrlen);
|
|
--------
|
|
|
|
See <<core_attr>> for more information on how to use netlink attributes.
|
|
|
|
.Parsing a Message the Easy Way
|
|
|
|
The function nlmsg_parse() validate a complete netlink message in one
|
|
step. If +hdrlen > 0+ it will first call nlmsg_valid_hdr() to check
|
|
if the protocol header fits into the message. If there is more payload
|
|
to parse, it will assume it to be attributes and parse the payload
|
|
accordingly. The function behaves exactly like nla_parse() when
|
|
parsing attributes, see <<core_attr_parse_easy>>.
|
|
|
|
[source,c]
|
|
--------
|
|
int nlmsg_parse(struct nlmsghdr *hdr, int hdrlen, struct nlattr **attrs,
|
|
int maxtype, struct nla_policy *policy);
|
|
--------
|
|
|
|
The function nlmsg_validate() is based on nla_validate() and behaves
|
|
exactly the same as nlmsg_parse() except that it only validates and
|
|
will not fill a array with pointers to each attribute.
|
|
|
|
[source,c]
|
|
--------
|
|
int nlmsg_validate(struct nlmsghdr *hdr, int hdrlen, intmaxtype,
|
|
struct nla_policy *policy);
|
|
--------
|
|
|
|
See <<core_attr_parse_easy>> for an example and more information on
|
|
attribute parsing.
|
|
|
|
=== Construction of a Message
|
|
|
|
See <<core_msg_format>> for information on the netlink message format
|
|
and alignment requirements.
|
|
|
|
Message construction is based on struct nl_msg which uses an internal
|
|
buffer to store the actual netlink message. struct nl_msg +does not+
|
|
point to the netlink message header. Use nlmsg_hdr() to retrieve a
|
|
pointer to the netlink message header.
|
|
|
|
At allocation time, a maximum message size is specified. It defaults
|
|
to a page (PAGE_SIZE). The application constructing the message will
|
|
reserve space out of this maximum message size repeatedly for each
|
|
header or attribute added. This allows construction of messages across
|
|
various layers of code where lower layers do not need to know about
|
|
the space requirements of upper layers.
|
|
|
|
+Why is setting the maximum message size necessary?+ This
|
|
question is often raised in combination with the proposed solution of
|
|
reallocating the message payload buffer on the fly using realloc().
|
|
While it is possible to reallocate the buffer during construction
|
|
using nlmsg_expand() it will make all pointers into the message buffer
|
|
become stale. This breaks usage of nlmsg_hdr(), nla_nest_start(), and
|
|
nla_nest_end() and is therefore not acceptable as default behaviour.
|
|
|
|
.Allocating struct nl_msg
|
|
|
|
The first step in constructing a new netlink message it to allocate a
|
|
`struct nl_msg` to hold the message header and payload. Several
|
|
functions exist to simplify various tasks.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
struct nl_msg *nlmsg_alloc(void);
|
|
void nlmsg_free(struct nl_msg *msg);
|
|
--------
|
|
|
|
The function nlmsg_alloc() is the default message allocation function.
|
|
It allocates a new message using the default maximum message size which
|
|
equals to one page (PAGE_SIZE). The application can change the default
|
|
size for messages by calling nlmsg_set_default_size():
|
|
|
|
[source,c]
|
|
--------
|
|
void nlmsg_set_default_size(size_t);
|
|
--------
|
|
|
|
CAUTION: Calling nlmsg_set_default_size() does not change the maximum
|
|
message size of already allocated messages.
|
|
|
|
[source,c]
|
|
--------
|
|
struct nl_msg *nlmsg_alloc_size(size_t max);
|
|
--------
|
|
|
|
Instead of changing the default message size, the function
|
|
nlmsg_alloc_size() can be used to allocate a message with a individual
|
|
maximum message size.
|
|
|
|
|
|
If the netlink message header is already known at allocation time, the
|
|
application may sue nlmsg_inherit(). It will allocate a message using
|
|
the default maximum message size and copy the header into the message.
|
|
Calling nlmsg_inherit with +set+ to NULL is equivalent to calling
|
|
nlmsg_alloc().
|
|
|
|
[source,c]
|
|
--------
|
|
struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr);
|
|
--------
|
|
|
|
Alternatively nlmsg_alloc_simple() takes a netlink message type and
|
|
netlink message flags. It is equivalent to nlmsg_inherit() except that it
|
|
takes the two common header fields as arguments instead of a complete
|
|
header.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
struct nl_msg *nlmsg_alloc_simple(int nlmsg_type, int flags);
|
|
--------
|
|
|
|
.Appending the netlink message header
|
|
|
|
After allocating struct nl_msg, the netlink message header needs to be
|
|
added unless one of the function nlmsg_alloc_simple() or nlmsg_inherit()
|
|
have been used for allocation in which case this step will replace the
|
|
netlink message header already in place.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
struct nlmsghdr *nlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seqnr,
|
|
int nlmsg_type, int payload, int nlmsg_flags);
|
|
--------
|
|
|
|
The function nlmsg_put() will build a netlink message header out of
|
|
+nlmsg_type+, +nlmsg_flags+, +seqnr+, and +port+ and copy it into the
|
|
netlink message. +seqnr+ can be set to +NL_AUTO_SEQ+ to indiciate
|
|
that the next possible sequence number should be used automatically.
|
|
To use this feature, the message must be sent using the function
|
|
nl_send_auto(). Like +port+, the argument +seqnr+ can be set to
|
|
+NL_AUTO_PORT+ indicating that the local port assigned to the socket
|
|
should be used as source port. This is generally a good idea unless
|
|
you are replying to a request. See <<core_netlink_fundamentals>>
|
|
for more information on how to fill the header.
|
|
|
|
NOTE: The argument +payload+ can be used by the application to reserve
|
|
room for additional data after the header. A value of > 0 is
|
|
equivalent to calling +nlmsg_reserve(msg, payload, NLMSG_ALIGNTO)+.
|
|
See <<core_msg_reserve>> for more information on reserving room for
|
|
data.
|
|
|
|
.Example
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
struct nlmsghdr *hdr;
|
|
struct nl_msg *msg;
|
|
struct myhdr {
|
|
uint32_t foo1, foo2;
|
|
} hdr = { 10, 20 };
|
|
|
|
/* Allocate a message with the default maximum message size */
|
|
msg = nlmsg_alloc();
|
|
|
|
/*
|
|
* Add header with message type MY_MSGTYPE, the flag NLM_F_CREATE,
|
|
* let library fill port and sequence number, and reserve room for
|
|
* struct myhdr
|
|
*/
|
|
hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, MY_MSGTYPE, sizeof(hdr), NLM_F_CREATE);
|
|
|
|
/* Copy own header into newly reserved payload section */
|
|
memcpy(nlmsg_data(hdr), &hdr, sizeof(hdr));
|
|
|
|
/*
|
|
* The message will now look like this:
|
|
* +-------------------+- - -+----------------+- - -+
|
|
* | struct nlmsghdr | Pad | struct myhdr | Pad |
|
|
* +-------------------+-----+----------------+- - -+
|
|
* nlh -^ / \
|
|
* +--------+---------+
|
|
* | foo1 | foo2 |
|
|
* +--------+---------+
|
|
*/
|
|
--------
|
|
|
|
[[core_msg_reserve]]
|
|
.Reserving room at the end of the message
|
|
|
|
Most functions described later on will automatically take care of
|
|
reserving room for the data that is added to the end of the netlink
|
|
message. In some situations it may be requried for the application
|
|
to reserve room directly though.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
void *nlmsg_reserve(struct nl_msg *msg, size_t len, int pad);
|
|
--------
|
|
|
|
The function nlmsg_reserve() reserves +len+ bytes at the end of the
|
|
netlink message and returns a pointer to the start of the reserved area.
|
|
The +pad+ argument can be used to request +len+ to be aligned to any
|
|
number of bytes prior to reservation.
|
|
|
|
The following example requests to reserve a 17 bytes area at the end of
|
|
message aligned to 4 bytes. Therefore a total of 20 bytes will be
|
|
reserved.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
void *buf = nlmsg_reserve(msg, 17, 4);
|
|
--------
|
|
|
|
NOTE: `nlmsg_reserve()` will *not* align the start of the buffer. Any
|
|
alignment requirements must be provided by the owner of the
|
|
previous message section.
|
|
|
|
.Appending data at the end of the message
|
|
|
|
The function `nlmsg_append()` appends `len` bytes at the end of the
|
|
message, padding it if requested and necessary.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
int nlmsg_append(struct nl_msg *msg, void *data, size_t len, int pad);
|
|
--------
|
|
|
|
It is equivalent to calling `nlmsg_reserve()` and `memcpy()`ing the
|
|
data into the freshly reserved data section.
|
|
|
|
NOTE: `nlmsg_append()` will *not* align the start of the data. Any
|
|
alignment requirements must be provided by the owner of the
|
|
previous message section.
|
|
|
|
.Adding attribtues to a message
|
|
|
|
Construction of attributes and addition of attribtues to the message is
|
|
covereted in section <<core_attr>>.
|
|
|
|
[[core_attr]]
|
|
== Attributes
|
|
|
|
Any form of payload should be encoded as netlink attributes whenever
|
|
possible. Use of attributes allows to extend any netlink protocol in
|
|
the future without breaking binary compatibility. F.e. Suppose your
|
|
device may currently be using 32 bit counters for statistics but years
|
|
later the device switches to maintaining 64 bit counters to account
|
|
for faster network hardware. If your protocol is using attributes the
|
|
move to 64 bit counters is trivial and only involves in sending an
|
|
additional attribute containing the 64 bit variants while still
|
|
providing the old legacy 32 bit counters. If your protocol is not using
|
|
attributes you will not be able to switch data types without breaking
|
|
all existing users of the protocol.
|
|
|
|
The concept of nested attributes also allows for subsystems of your
|
|
protocol to implement and maintain their own attribute schemas. Suppose
|
|
a new generation of network device is introduced which requires a
|
|
completely new set of configuration settings which was unthinkable when
|
|
the netlink protocol was initially designed. Using attributes the new
|
|
generation of devices may define a new attribute and fill it with its
|
|
own new structure of attributes which extend or even obsolete the old
|
|
attributes.
|
|
|
|
Therefore, _always_ use attributes even if you are almost certain that
|
|
the message format will never ever change in the future.
|
|
|
|
[[core_attr_format]]
|
|
=== Attribute Format
|
|
|
|
Netlink attributes allow for any number of data chunks of arbitary
|
|
length to be attached to a netlink message. See <<core_msg_attr>>
|
|
for more information on where attributes are stored in the message.
|
|
|
|
The format of the attributes data returned by nlmsg_attrdata() is as
|
|
follows:
|
|
|
|
[source,c]
|
|
--------
|
|
<----------- nla_total_size(payload) ----------->
|
|
<---------- nla_size(payload) ----------->
|
|
+-----------------+- - -+- - - - - - - - - +- - -+-----------------+- - -
|
|
| struct nlattr | Pad | Payload | Pad | struct nlattr |
|
|
+-----------------+- - -+- - - - - - - - - +- - -+-----------------+- - -
|
|
<---- NLA_HDRLEN -----> <--- NLA_ALIGN(len) ---> <---- NLA_HDRLEN ---
|
|
--------
|
|
|
|
Every attribute must start at an offset which is a multiple of
|
|
+NLA_ALIGNTO+ (4 bytes). If you need to know whether an attribute needs
|
|
to be padded at the end, the function nla_padlen() returns the number
|
|
of padding bytes that will or need to be added.
|
|
|
|
image:attribute_hdr.png["Netlink Attribute Header"]
|
|
|
|
Every attribute is encoded with a type and length field, both 16 bits,
|
|
stored in the attribute header (struct nlattr) preceding the attribute
|
|
payload. The length of an attribute is used to calculate the offset to
|
|
the next attribute.
|
|
|
|
[[core_attr_parse]]
|
|
=== Parsing Attributes
|
|
|
|
[[core_attr_parse_split]]
|
|
.Splitting an Attributes Stream into Attributes
|
|
|
|
Although most applications will use one of the functions from the
|
|
nlmsg_parse() family (See <<core_attr_parse_easy>>) an interface exists
|
|
to split the attributes stream manually.
|
|
|
|
As described in <<core_attr_format>> the attributes section contains a
|
|
infinite sequence or stream of attributes. The pointer returned by
|
|
nlmsg_attrdata() (See <<core_msg_attr>>) points to the first attribute
|
|
header. Any subsequent attribute is accessed with the function nla_next()
|
|
based on the previous header.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/attr.h>
|
|
|
|
struct nlattr *nla_next(const struct nlattr *attr, int *remaining);
|
|
--------
|
|
|
|
The semantics are equivalent to nlmsg_next() and thus nla_next() will also
|
|
subtract the size of the previous attribute from the remaining number of
|
|
bytes in the attributes stream.
|
|
|
|
Like messages, attributes do not contain an indicator whether another
|
|
attribute follows or not. The only indication is the number of bytes left
|
|
in the attribute stream. The function nla_ok() exists to determine whether
|
|
another attribute fits into the remaining number of bytes or not.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/attr.h>
|
|
|
|
int nla_ok(const struct nlattr *attr, int remaining);
|
|
--------
|
|
|
|
A typical use of nla_ok() and nla_next() looks like this:
|
|
|
|
.nla_ok()/nla_next() usage
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
#include <netlink/attr.h>
|
|
|
|
struct nlattr *hdr = nlmsg_attrdata(msg, 0);
|
|
int remaining = nlmsg_attrlen(msg, 0);
|
|
|
|
while (nla_ok(hdr, remaining)) {
|
|
/* parse attribute here */
|
|
hdr = nla_next(hdr, &remaining);
|
|
};
|
|
--------
|
|
|
|
NOTE: `nla_ok()` only returns true if the *complete* attributes
|
|
including the attribute payload fits into the remaining number
|
|
of bytes.
|
|
|
|
.Accessing Attribute Header and Payload
|
|
|
|
Once the individual attributes have been sorted out by either splitting
|
|
the attributes stream or using another interface the attribute header
|
|
and payload can be accessed.
|
|
|
|
[source,c]
|
|
--------
|
|
<- nla_len(hdr) ->
|
|
+-----------------+- - -+- - - - - - - - - +- - -+
|
|
| struct nlattr | Pad | Payload | Pad |
|
|
+-----------------+- - -+- - - - - - - - - +- - -+
|
|
nla_data(hdr) ---------------^
|
|
--------
|
|
|
|
The functions nla_len() and nla_type() can be used to access the attribute
|
|
header. nla_len() will return the length of the payload not including
|
|
eventual padding bytes. nla_type returns the attribute type.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/attr.h>
|
|
|
|
int nla_len(const struct nlattr *hdr);
|
|
int nla_type(const struct nlattr *hdr);
|
|
--------
|
|
|
|
The function nla_data() will return a pointer to the attribute
|
|
payload. Please note that due to +NLA_ALIGNTO+ being 4 bytes it may
|
|
not be safe to cast and dereference the pointer for any datatype
|
|
larger than 32 bit depending on the architecture the application is
|
|
run on.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/attr.h>
|
|
|
|
void *nla_data(const struct nlattr *hdr);
|
|
--------
|
|
|
|
[NOTE]
|
|
Never rely on the size of a payload being what you expect it to be.
|
|
_Always_ verify the payload size and make sure that it matches your
|
|
expectations. See <<core_attr_validation>>
|
|
|
|
[[core_attr_validation]]
|
|
.Attribute Validation
|
|
|
|
When receiving netlink attributes, the receiver has certain expections
|
|
on how the attributes should look like. These expectations must be
|
|
defined to make sure the sending side meets our expecations. For this
|
|
purpose, a attribute validation interface exists which must be used
|
|
prior to accessing any payload.
|
|
|
|
All functions providing attribute validation functionality are based
|
|
on struct nla_policy:
|
|
|
|
[source,c]
|
|
--------
|
|
struct nla_policy {
|
|
uint16_t type;
|
|
uint16_t minlen;
|
|
uint16_t maxlen;
|
|
};
|
|
--------
|
|
|
|
The +type+ member specifies the datatype of the attribute, e.g.
|
|
+NLA_U32+, +NLA_STRING+, +NLA_FLAG+. The default is +NLA_UNSPEC+. The
|
|
+minlen+ member defines the minmum payload length of an attribute to
|
|
be considered a valid attribute. The value for +minlen+ is implicit
|
|
for most basic datatypes such as integers or flags. The +maxlen+
|
|
member can be used to define a maximum payload length for an
|
|
attribute to still be considered valid.
|
|
|
|
NOTE: Specyfing a maximum payload length is not recommended when
|
|
encoding structures in an attribute as it will prevent any
|
|
extension of the structure in the future. Something that is
|
|
frequently done in netlink protocols and does not break
|
|
backwards compatibility.
|
|
|
|
One of the functions which use struct nla_policy is nla_validate().
|
|
The function expects an array of struct nla_policy and will access the
|
|
array using the attribute type as index. If an attribute type is out
|
|
of bounds the attribute is assumed to be valid. This is intentional
|
|
behaviour to allow older applications not yet aware of recently
|
|
introduced attributes to continue functioning.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/attr.h>
|
|
|
|
int nla_validate(struct nlattr *head, int len, int maxtype, struct nla_policy *policy);
|
|
--------
|
|
|
|
The function nla_validate() returns 0 if all attributes are valid,
|
|
otherwise a validation failure specific error code is returned.
|
|
|
|
Most applications will rarely use nla_validate() directly but use
|
|
nla_parse() instead which takes care of validation in the same way but
|
|
also parses the the attributes in the same step. See
|
|
<<core_attr_parse_easy>> for an example and more information.
|
|
|
|
The validation process in detail:
|
|
|
|
. If attribute type is 0 or exceeds +maxtype+ attribute is
|
|
considered valid, 0 is returned.
|
|
. If payload length is < +minlen+, +-NLE_ERANGE+ is returned.
|
|
. If +maxlen+ is defined and payload exceeds it, +-NLE_ERANGE+
|
|
is returned.
|
|
. Datatype specific requirements rules, see <<core_attr_types>>
|
|
. If all is ok, 0 is returned.
|
|
|
|
[[core_attr_parse_easy]]
|
|
.Parsing Attributes the Easy Way
|
|
|
|
Most applications will not want to deal with splitting attribute
|
|
streams themselves as described in <<core_attr_parse_split>>
|
|
A much easier method is to use nla_parse().
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/attr.h>
|
|
|
|
int nla_parse(struct nlattr **attrs, int maxtype, struct nlattr *head,
|
|
int len, struct nla_policy *policy);
|
|
--------
|
|
|
|
The function nla_parse() will iterate over a stream of attributes,
|
|
validate each attribute as described in <<core_attr_validation>>
|
|
If the validation of all attributes succeeds, a pointer to each attribute
|
|
is stored in the +attrs+ array at `attrs[nla_type(attr)]`.
|
|
|
|
As an alernative to nla_parse() the function nlmsg_parse() can be used
|
|
to parse the message and its attributes in one step. See
|
|
<<core_attr_parse_easy>> for information on how to use these functions.
|
|
|
|
.Example:
|
|
|
|
The following example demonstrates how to parse a netlink message sent
|
|
over a netlink protocol which does not use protocol headers. The example
|
|
does enforce a attribute policy however, the attribute MY_ATTR_FOO must
|
|
be a 32 bit integer, and the attribute MY_ATTR_BAR must be a string with
|
|
a maximum length of 16 characters.
|
|
|
|
[source,c]
|
|
---------
|
|
#include <netlink/msg.h>
|
|
#include <netlink/attr.h>
|
|
|
|
enum {
|
|
MY_ATTR_FOO = 1,
|
|
MY_ATTR_BAR,
|
|
__MY_ATTR_MAX,
|
|
};
|
|
|
|
#define MY_ATTR_MAX (__MY_ATTR_MAX - 1)
|
|
|
|
static struct nla_policy my_policy[MY_ATTR_MAX+1] = {
|
|
[MY_ATTR_FOO] = { .type = NLA_U32 },
|
|
[MY_ATTR_BAR] = { .type = NLA_STRING,
|
|
.maxlen = 16 },
|
|
};
|
|
|
|
void parse_msg(struct nlmsghdr *nlh)
|
|
{
|
|
struct nlattr *attrs[MY_ATTR_MAX+1];
|
|
|
|
if (nlmsg_parse(nlh, 0, attrs, MY_ATTR_MAX, my_policy) < 0)
|
|
/* error */
|
|
|
|
if (attrs[MY_ATTR_FOO]) {
|
|
/* MY_ATTR_FOO is present in message */
|
|
printf("value: %u\n", nla_get_u32(attrs[MY_ATTR_FOO]));
|
|
}
|
|
}
|
|
---------
|
|
|
|
.Locating a Single Attribute
|
|
|
|
An application only interested in a single attribute can use one of the
|
|
functions nla_find() or nlmsg_find_attr(). These function will iterate
|
|
over all attributes, search for a matching attribute and return a pointer
|
|
to the corresponding attribute header.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/attr.h>
|
|
|
|
struct nlattr *nla_find(struct nlattr *head, int len, int attrtype);
|
|
--------
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
|
|
struct nlattr *nlmsg_find_attr(struct nlmsghdr *hdr, int hdrlen, int attrtype);
|
|
--------
|
|
|
|
NOTE: `nla_find()` and `nlmsg_find_attr()` will *not* search in nested
|
|
attributes recursively, see <<core_attr_nested>>.
|
|
|
|
==== Iterating over a Stream of Attributes
|
|
|
|
In some situations it does not make sense to assign a unique attribute
|
|
type to each attribute in the attribute stream. For example a list may
|
|
be transferd using a stream of attributes and even if the attribute type
|
|
is incremented for each attribute it may not make sense to use the
|
|
nlmsg_parse() or nla_parse() function to fill an array.
|
|
|
|
Therefore methods exist to iterate over a stream of attributes:
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/attr.h>
|
|
|
|
nla_for_each_attr(attr, head, len, remaining)
|
|
--------
|
|
|
|
nla_for_each_attr() is a macro which can be used in front of a code
|
|
block:
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/attr.h>
|
|
|
|
struct nalttr *nla;
|
|
int rem;
|
|
|
|
nla_for_each_attr(nla, attrstream, streamlen, rem) {
|
|
/* validate & parse attribute */
|
|
}
|
|
|
|
if (rem > 0)
|
|
/* unparsed attribute data */
|
|
--------
|
|
|
|
[[core_attr_constr]]
|
|
=== Attribute Construction
|
|
|
|
The interface to add attributes to a netlink message is based on the
|
|
regular message construction interface. It assumes that the message
|
|
header and an eventual protocol header has been added to the message
|
|
already.
|
|
|
|
[source,c]
|
|
--------
|
|
struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int len);
|
|
--------
|
|
|
|
The function nla_reserve() adds an attribute header at the end of the
|
|
message and reserves room for +len+ bytes of payload. The function
|
|
returns a pointer to the attribute payload section inside the message.
|
|
Padding is added at the end of the attribute to ensure the next
|
|
attribute is properly aligned.
|
|
|
|
[source,c]
|
|
--------
|
|
int nla_put(struct nl_msg *msg, int attrtype, int attrlen, const void *data);
|
|
--------
|
|
|
|
The function nla_put() is base don nla_reserve() but takes an additional
|
|
pointer +data+ pointing to a buffer containing the attribute payload.
|
|
It will copy the buffer into the message automatically.
|
|
|
|
.Example:
|
|
|
|
[source,c]
|
|
--------
|
|
struct my_attr_struct {
|
|
uint32_t a;
|
|
uint32_t b;
|
|
};
|
|
|
|
int my_put(struct nl_msg *msg)
|
|
{
|
|
struct my_attr_struct obj = {
|
|
.a = 10,
|
|
.b = 20,
|
|
};
|
|
|
|
return nla_put(msg, ATTR_MY_STRUCT, sizeof(obj), &obj);
|
|
}
|
|
--------
|
|
|
|
See <<core_attr_types>> for datatype specific attribute construction
|
|
functions.
|
|
|
|
.Exception Based Attribute Construction
|
|
|
|
Like in the kernel API an exception based construction interface is
|
|
provided. The behaviour of the macros is identical to their regular
|
|
function counterparts except that in case of an error, the target
|
|
`nla_put_failure` is jumped.
|
|
|
|
.Example:
|
|
[source,c]
|
|
--------
|
|
#include <netlink/msg.h>
|
|
#include <netlink/attr.h>
|
|
|
|
void construct_attrs(struct nl_msg *msg)
|
|
{
|
|
NLA_PUT_STRING(msg, MY_ATTR_FOO1, "some text");
|
|
NLA_PUT_U32(msg, MY_ATTR_FOO1, 0x1010);
|
|
NLA_PUT_FLAG(msg, MY_ATTR_FOO3, 1);
|
|
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
/* NLA_PUT* macros jump here in case of an error */
|
|
return -EMSGSIZE;
|
|
}
|
|
--------
|
|
|
|
See <<core_attr_types>> for more information on the datatype specific
|
|
exception based variants.
|
|
|
|
[[core_attr_types]]
|
|
=== Attribute Data Types
|
|
|
|
A number of basic data types have been defined to simplify access and
|
|
validation of attributes. The datatype is not encoded in the
|
|
attribute, therefore bthe sender and receiver are required to use the
|
|
same definition on what attribute is of what type.
|
|
|
|
[options="header", cols="1m,5"]
|
|
|================================================
|
|
| Type | Description
|
|
| NLA_UNSPEC | Unspecified attribute
|
|
| NLA_U{8\|16\|32} | Integers
|
|
| NLA_STRING | String
|
|
| NLA_FLAG | Flag
|
|
| NLA_NESTED | Nested attribute
|
|
|================================================
|
|
|
|
Besides simplified access to the payload of such datatypes, the major
|
|
advantage is the automatic validation of each attribute based on a
|
|
policy. The validation ensures safe access to the payload by checking
|
|
for minimal payload size and can also be used to enforce maximum
|
|
payload size for some datatypes.
|
|
|
|
==== Integer Attributes
|
|
|
|
The most frequently used datatypes are integers. Integers come in four
|
|
different sizes:
|
|
[horizontal]
|
|
NLA_U8:: 8bit integer
|
|
NLA_U16:: 16bit integer
|
|
NLA_U32:: 32bit integer
|
|
NLA_U64:: 64bit integer
|
|
|
|
Note that due to the alignment requirements of attributes the integer
|
|
attribtue +NLA_u8+ and +NLA_U16+ will not result in space savings in
|
|
the netlink message. Their use is intended to limit the range of
|
|
values.
|
|
|
|
.Parsing Integer Attributes
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/attr.h>
|
|
|
|
uint8_t nla_get_u8(struct nlattr *hdr);
|
|
uint16_t nla_get_u16(struct nlattr *hdr);
|
|
uint32_t nla_get_u32(struct nlattr *hdr);
|
|
uint64_t nla_get_u64(struct nlattr *hdr);
|
|
--------
|
|
|
|
Example:
|
|
|
|
[source,c]
|
|
--------
|
|
if (attrs[MY_ATTR_FOO])
|
|
uint32_t val = nla_get_u32(attrs[MY_ATTR_FOO]);
|
|
--------
|
|
|
|
.Constructing Integer Attributes
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/attr.h>
|
|
|
|
int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value);
|
|
int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value);
|
|
int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value);
|
|
int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value);
|
|
--------
|
|
|
|
Exception based:
|
|
|
|
[source,c]
|
|
--------
|
|
NLA_PUT_U8(msg, attrtype, value)
|
|
NLA_PUT_U16(msg, attrtype, value)
|
|
NLA_PUT_U32(msg, attrtype, value)
|
|
NLA_PUT_U64(msg, attrtype, value)
|
|
--------
|
|
|
|
.Validation
|
|
|
|
Use +NLA_U8+, +NLA_U16+, +NLA_U32+, or +NLA_U64+ to define the type of
|
|
integer when filling out a struct nla_policy array. It will
|
|
automatically enforce the correct minimum payload length policy.
|
|
|
|
Validation does not differ between signed and unsigned integers, only
|
|
the size matters. If the appliaction wishes to enforce particular value
|
|
ranges it must do so itself.
|
|
|
|
[source,c]
|
|
--------
|
|
static struct nla_policy my_policy[ATTR_MAX+1] = {
|
|
[ATTR_FOO] = { .type = NLA_U32 },
|
|
[ATTR_BAR] = { .type = NLA_U8 },
|
|
};
|
|
--------
|
|
|
|
The above is equivalent to:
|
|
[source,c]
|
|
--------
|
|
static struct nla_policy my_policy[ATTR_MAX+1] = {
|
|
[ATTR_FOO] = { .minlen = sizeof(uint32_t) },
|
|
[ATTR_BAR] = { .minlen = sizeof(uint8_t) },
|
|
};
|
|
--------
|
|
|
|
==== String Attributes
|
|
|
|
The string datatype represents a NUL termianted character string of
|
|
variable length. It is not intended for binary data streams.
|
|
|
|
The payload of string attributes can be accessed with the function
|
|
nla_get_string(). nla_strdup() calls strdup() on the payload and returns
|
|
the newly allocated string.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/attr.h>
|
|
|
|
char *nla_get_string(struct nlattr *hdr);
|
|
char *nla_strdup(struct nlattr *hdr);
|
|
--------
|
|
|
|
String attributes are constructed with the function +nla_put_string()+
|
|
respectively +NLA_PUT_STRING()+. The length of the payload will be
|
|
strlen()+1, the trailing NUL byte is included.
|
|
|
|
[source,c]
|
|
--------
|
|
int nla_put_string(struct nl_msg *msg, int attrtype, const char *data);
|
|
|
|
NLA_PUT_STRING(msg, attrtype, data)
|
|
--------
|
|
|
|
For validation purposes the type +NLA_STRING+ can be used in
|
|
+struct nla_policy+ definitions. It implies a minimum payload length
|
|
of 1 byte and checks for a trailing NUL byte. Optionally the +maxlen+
|
|
member defines the maximum length of a character string (including the
|
|
trailing NUL byte).
|
|
|
|
[source,c]
|
|
--------
|
|
static struct nla_policy my_policy[] = {
|
|
[ATTR_FOO] = { .type = NLA_STRING,
|
|
.maxlen = IFNAMSIZ },
|
|
};
|
|
--------
|
|
|
|
==== Flag Attributes
|
|
|
|
The flag attribute represents a boolean datatype. The presence of the
|
|
attribute implies a value of +true+, the absence of the attribute
|
|
implies the value +false+. Therefore the payload length of flag
|
|
attributes is always 0.
|
|
|
|
[source,c]
|
|
--------
|
|
int nla_get_flag(struct nlattr *hdr);
|
|
int nla_put_flag(struct nl_msg *msg, int attrtype);
|
|
--------
|
|
|
|
The type +NLA_FLAG+ is used for validation purposes. It implies a
|
|
+maxlen+ value of 0 and thus enforces a maximum payload length of 0.
|
|
|
|
.Example:
|
|
[source,c]
|
|
--------
|
|
/* nla_put_flag() appends a zero sized attribute to the message. */
|
|
nla_put_flag(msg, ATTR_FLAG);
|
|
|
|
/* There is no need for a receival function, the presence is the value. */
|
|
if (attrs[ATTR_FLAG])
|
|
/* flag is present */
|
|
--------
|
|
|
|
[[core_attr_nested]]
|
|
==== Nested Attributes
|
|
|
|
As described in <<core_attr>>, attributes can be nested allowing for
|
|
complex tree structures of attributes. It is commonly used to delegate
|
|
the responsibility of a subsection of the message to a subsystem.
|
|
Nested attributes are also commonly used for transmitting list of objects.
|
|
|
|
When nesting attributes, the nested attributes are included as payload
|
|
of a container attribute.
|
|
|
|
NOTE: When validating the attributes using nlmsg_validate(),
|
|
nlmsg_parse(), nla_validate(), or nla_parse() only the
|
|
attributes on the first level are being validated. None of these
|
|
functions will validate attributes recursively. Therefore you
|
|
must explicitely call nla_validate() or use nla_parse_nested()
|
|
for each level of nested attributes.
|
|
|
|
The type +NLA_NESTED+ should be used when defining nested attributes
|
|
in a struct nla_policy definition. It will not enforce any minimum
|
|
payload length unless +minlen+ is specified explicitely. This is
|
|
because some netlink protocols implicitely allow empty container
|
|
attributes.
|
|
|
|
[source,c]
|
|
--------
|
|
static struct nla_policy my_policy[] = {
|
|
[ATTR_OPTS] = { .type = NLA_NESTED },
|
|
};
|
|
--------
|
|
|
|
.Parsing of Nested Attributes
|
|
|
|
The function nla_parse_nested() is used to parse nested attributes.
|
|
Its behaviour is identical to nla_parse() except that it takes a
|
|
struct nlattr as argument and will use the payload as stream of
|
|
attributes.
|
|
|
|
[source,c]
|
|
--------
|
|
if (attrs[ATTR_OPTS]) {
|
|
struct nlattr *nested[NESTED_MAX+1];
|
|
struct nla_policy nested_policy[] = {
|
|
[NESTED_FOO] = { .type = NLA_U32 },
|
|
};
|
|
|
|
if (nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_OPTS], nested_policy) < 0)
|
|
/* error */
|
|
|
|
if (nested[NESTED_FOO])
|
|
uint32_t val = nla_get_u32(nested[NESTED_FOO]);
|
|
}
|
|
--------
|
|
|
|
.Construction of Nested Attributes
|
|
|
|
Attributes are nested by surrounding them with calls to nla_nest_start()
|
|
and nla_nest_end(). nla_nest_start() will add a attribute header to
|
|
the message but no actual payload. All data added to the message from
|
|
this point on will be part of the container attribute until nla_nest_end()
|
|
is called which "closes" the attribute, correcting its payload length to
|
|
include all data length.
|
|
|
|
[source,c]
|
|
--------
|
|
int put_opts(struct nl_msg *msg)
|
|
{
|
|
struct nlattr *opts;
|
|
|
|
if (!(opts = nla_nest_start(msg, ATTR_OPTS)))
|
|
goto nla_put_failure;
|
|
|
|
NLA_PUT_U32(msg, NESTED_FOO, 123);
|
|
NLA_PUT_STRING(msg, NESTED_BAR, "some text");
|
|
|
|
nla_nest_end(msg, opts);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
nla_nest_cancel(msg, opts);
|
|
return -EMSGSIZE;
|
|
}
|
|
--------
|
|
|
|
==== Unspecified Attribute
|
|
|
|
This is the default attribute type and used when none of the basic
|
|
datatypes is suitable. It represents data of arbitary type and length.
|
|
|
|
See <<core_addr_alloc, Address Allocation>> for a more information on
|
|
a special interface allowing the allocation of abstract address object
|
|
based on netlink attributes which carry some form of network address.
|
|
|
|
See <<core_data_alloc, Abstract Data Allocation>> for more information
|
|
on how to allocate abstract data objects based on netlink attributes.
|
|
|
|
Use the function nla_get() and nla_put() to access the payload and
|
|
construct attributes. See <<core_attr_constr, Attribute Construction>>
|
|
for an example.
|
|
|
|
=== Examples
|
|
|
|
==== Constructing a Netlink Message with Attributes
|
|
|
|
[source,c]
|
|
--------
|
|
struct nl_msg *build_msg(int ifindex, struct nl_addr *lladdr, int mtu)
|
|
{
|
|
struct nl_msg *msg;
|
|
struct nlattr *info, *vlan;
|
|
struct ifinfomsg ifi = {
|
|
.ifi_family = AF_INET,
|
|
.ifi_index = ifindex,
|
|
};
|
|
|
|
/* Allocate a default sized netlink message */
|
|
if (!(msg = nlmsg_alloc_simple(RTM_SETLINK, 0)))
|
|
return NULL;
|
|
|
|
/* Append the protocol specific header (struct ifinfomsg)*/
|
|
if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
|
|
goto nla_put_failure
|
|
|
|
/* Append a 32 bit integer attribute to carry the MTU */
|
|
NLA_PUT_U32(msg, IFLA_MTU, mtu);
|
|
|
|
/* Append a unspecific attribute to carry the link layer address */
|
|
NLA_PUT_ADDR(msg, IFLA_ADDRESS, lladdr);
|
|
|
|
/* Append a container for nested attributes to carry link information */
|
|
if (!(info = nla_nest_start(msg, IFLA_LINKINFO)))
|
|
goto nla_put_failure;
|
|
|
|
/* Put a string attribute into the container */
|
|
NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan");
|
|
|
|
/*
|
|
* Append another container inside the open container to carry
|
|
* vlan specific attributes
|
|
*/
|
|
if (!(vlan = nla_nest_start(msg, IFLA_INFO_DATA)))
|
|
goto nla_put_failure;
|
|
|
|
/* add vlan specific info attributes here... */
|
|
|
|
/* Finish nesting the vlan attributes and close the second container. */
|
|
nla_nest_end(msg, vlan);
|
|
|
|
/* Finish nesting the link info attribute and close the first container. */
|
|
nla_nest_end(msg, info);
|
|
|
|
return msg;
|
|
|
|
nla_put_failure:
|
|
nlmsg_free(msg);
|
|
return NULL;
|
|
}
|
|
------
|
|
|
|
==== Parsing a Netlink Message with Attributes
|
|
|
|
[source,c]
|
|
--------
|
|
int parse_message(struct nlmsghdr *hdr)
|
|
{
|
|
/*
|
|
* The policy defines two attributes: a 32 bit integer and a container
|
|
* for nested attributes.
|
|
*/
|
|
struct nla_policy attr_policy[] = {
|
|
[ATTR_FOO] = { .type = NLA_U32 },
|
|
[ATTR_BAR] = { .type = NLA_NESTED },
|
|
};
|
|
struct nlattr *attrs[ATTR_MAX+1];
|
|
int err;
|
|
|
|
/*
|
|
* The nlmsg_parse() function will make sure that the message contains
|
|
* enough payload to hold the header (struct my_hdr), validates any
|
|
* attributes attached to the messages and stores a pointer to each
|
|
* attribute in the attrs[] array accessable by attribute type.
|
|
*/
|
|
if ((err = nlmsg_parse(hdr, sizeof(struct my_hdr), attrs, ATTR_MAX,
|
|
attr_policy)) < 0)
|
|
goto errout;
|
|
|
|
if (attrs[ATTR_FOO]) {
|
|
/*
|
|
* It is safe to directly access the attribute payload without
|
|
* any further checks since nlmsg_parse() enforced the policy.
|
|
*/
|
|
uint32_t foo = nla_get_u32(attrs[ATTR_FOO]);
|
|
}
|
|
|
|
if (attrs[ATTR_BAR]) {
|
|
struct *nested[NESTED_MAX+1];
|
|
|
|
/*
|
|
* Attributes nested in a container can be parsed the same way
|
|
* as top level attributes.
|
|
*/
|
|
err = nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_BAR],
|
|
nested_policy);
|
|
if (err < 0)
|
|
goto errout;
|
|
|
|
// Process nested attributes here.
|
|
}
|
|
|
|
err = 0;
|
|
errout:
|
|
return err;
|
|
}
|
|
--------
|
|
|
|
[[core_cb]]
|
|
== Callback Configurations
|
|
|
|
Callback hooks and overwriting capabilities are provided in various places
|
|
inside library to control the behaviour of several functions. All the
|
|
callback and overwrite functions are packed together in struct nl_cb which
|
|
is attached to a netlink socket or passed on to functions directly.
|
|
|
|
=== Callback Hooks
|
|
|
|
Callback hooks are spread across the library to provide entry points for
|
|
message processing and to take action upon certain events.
|
|
|
|
Callback functions may return the following return codes:
|
|
[options="header", cols="1m,4"]
|
|
|========================================================================
|
|
| Return Code | Description
|
|
| NL_OK | Proceed.
|
|
| NL_SKIP | Skip message currently being processed and continue
|
|
parsing the receive buffer.
|
|
| NL_STOP | Stop parsing and discard all remaining data in the
|
|
receive buffer.
|
|
|========================================================================
|
|
|
|
.Default Callback Implementations
|
|
|
|
The library provides three sets of default callback implementations:
|
|
* +NL_CB_DEFAULT+ This is the default set. It implets the default behaviour.
|
|
See the table below for more information on the return codes of each
|
|
function.
|
|
* +NL_CB_VERBOSE+ This set is based on the default set but will cause an
|
|
error message to be printed to stderr for error messages, invalid
|
|
messages, message overruns and unhandled valid messages. The
|
|
+arg+ pointer in nl_cb_set() and nl_cb_err() can be used to
|
|
provide a FILE * which overwrites stderr.
|
|
* +NL_CB_DEBUG+ This set is intended for debugging purposes. It is
|
|
based on the verbose set but will decode and dump each message sent
|
|
or received to the console.
|
|
|
|
.Message Processing Callbacks
|
|
|
|
.nl_sendmsg() callback hooks:
|
|
[cols="2m,4e,1m", options="header"]
|
|
|============================================================================
|
|
| Callback ID | Description | Default Return Value
|
|
| NL_CB_MSG_OUT | Each message sent | NL_OK
|
|
|============================================================================
|
|
|
|
Any function called by NL_CB_MSG_OUT may return a negative error code to
|
|
prevent the message from being sent and the error code being returned.
|
|
|
|
nl_recvmsgs() callback hooks (ordered by priority):
|
|
[cols="2m,4e,1m", options="header"]
|
|
|============================================================================
|
|
| Callback ID | Description | Default Return Value
|
|
| NL_CB_MSG_IN | Each message received | NL_OK
|
|
| NL_CB_SEQ_CHECK | May overwrite sequence check algo | NL_OK
|
|
| NL_CB_INVALID | Invalid messages | NL_STOP
|
|
| NL_CB_SEND_ACK | Messages with NLM_F_ACK flag set | NL_OK
|
|
| NL_CB_FINISH | Messages of type NLMSG_DONE | NL_STOP
|
|
| NL_CB_SKIPPED | Messages of type NLMSG_NOOP | NL_SKIP
|
|
| NL_CB_OVERRUN | Messages of type NLMSG_OVERRUN | NL_STOP
|
|
| NL_CB_ACK | ACK Messages | NL_STOP
|
|
| NL_CB_VALID | Each valid message | NL_OK
|
|
|============================================================================
|
|
|
|
Any of these functions may return NL_OK, NL_SKIP, or NL_STOP.
|
|
|
|
Message processing callback functions are set with nl_cb_set():
|
|
[source,c]
|
|
--------
|
|
#include <netlink/handlers.h>
|
|
|
|
int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind,
|
|
nl_recvmsg_msg_cb_t func, void *arg);
|
|
|
|
typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg);
|
|
--------
|
|
|
|
.Callback for Error Messages
|
|
|
|
A special function prototype is used for the error message callback hook:
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/handlers.h>
|
|
|
|
int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, nl_recvmsg_err_cb_t func, void *arg);
|
|
|
|
typedef int(* nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg);
|
|
--------
|
|
|
|
.Example: Setting up a callback set
|
|
[source,c]
|
|
--------
|
|
#include <netlink/handlers.h>
|
|
|
|
/* Allocate a callback set and initialize it to the verbose default set */
|
|
struct nl_cb *cb = nl_cb_alloc(NL_CB_VERBOSE);
|
|
|
|
/* Modify the set to call my_func() for all valid messages */
|
|
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL);
|
|
|
|
/*
|
|
* Set the error message handler to the verbose default implementation
|
|
* and direct it to print all errors to the given file descriptor.
|
|
*/
|
|
FILE *file = fopen(...);
|
|
nl_cb_err(cb, NL_CB_VERBOSE, NULL, file);
|
|
--------
|
|
|
|
=== Overwriting of Internal Functions
|
|
|
|
When the library needs to send or receive netlink messages in high level
|
|
interfaces it does so by calling its own low level API. In the case the
|
|
default characteristics are not sufficient for the application, it may
|
|
overwrite several internal function calls with own implementations.
|
|
|
|
.Overwriting recvmsgs()
|
|
|
|
See <<core_recv, Receiving Netlink Messages>> for more information on
|
|
how and when recvmsgs() is called internally.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/handlers.h>
|
|
|
|
void nl_cb_overwrite_recvmsgs(struct nl_cb *cb,
|
|
int (*func)(struct nl_sock *sk, struct nl_cb *cb));
|
|
--------
|
|
|
|
The following criteras must be met if a recvmsgs() implementation is
|
|
supposed to work with high level interfaces:
|
|
|
|
- MUST respect the callback configuration +cb+, therefore:
|
|
- MUST call +NL_CB_VALID+ for all valid messages, passing on
|
|
- MUST call +NL_CB_ACK+ for all ACK messages
|
|
- MUST correctly handle multipart messages, calling NL_CB_VALID for
|
|
each message until a NLMSG_DONE message is received.
|
|
- MUST report error code if a NLMSG_ERROR or NLMSG_OVERRUN mesasge is
|
|
received.
|
|
|
|
.Overwriting nl_recv()
|
|
|
|
Often it is sufficient to overwrite `nl_recv()` which is responsible
|
|
from receiving the actual data from the socket instead of replacing
|
|
the complete `recvmsgs()` logic.
|
|
|
|
See <<core_recv_character, Receive Characteristics>> for more
|
|
information on how and when `nl_recv()` is called internally.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/handlers.h>
|
|
|
|
void nl_cb_overwrite_recv(struct nl_cb *cb,
|
|
int (*func)(struct nl_sock * sk,
|
|
struct sockaddr_nl *addr,
|
|
unsigned char **buf,
|
|
struct ucred **cred));
|
|
--------
|
|
|
|
The following criteras must be met for an own `nl_recv()`
|
|
implementation:
|
|
|
|
- *MUST* return the number of bytes read or a negative error code if
|
|
an error occured. The function may also return 0 to indicate that
|
|
no data has been read.
|
|
- *MUST* set `*buf` to a buffer containing the data read. It must be
|
|
safe for the caller to access the number of bytes read returned as
|
|
return code.
|
|
- *MAY* fill out `*addr` with the netlink address of the peer the
|
|
data has been received from.
|
|
- *MAY* set `*cred` to a newly allocated struct ucred containg
|
|
credentials.
|
|
|
|
.Overwriting nl_send()
|
|
|
|
See <<core_send, Sending Netlink Messages>> for more information on
|
|
how and when nl_send() is called internally.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/handlers.h>
|
|
|
|
void nl_cb_overwrite_send(struct nl_cb *cb, int (*func)(struct nl_sock *sk,
|
|
struct nl_msg *msg));
|
|
--------
|
|
|
|
Own implementations must send the netlink message and return 0 on success
|
|
or a negative error code.
|
|
|
|
[[core_cache]]
|
|
== Cache System
|
|
|
|
=== Allocation of Caches
|
|
|
|
Almost all subsystem provide a function to allocate a new cache
|
|
of some form. The function usually looks like this:
|
|
[source,c]
|
|
--------
|
|
struct nl_cache *<object name>_alloc_cache(struct nl_sock *sk);
|
|
--------
|
|
|
|
These functions allocate a new cache for the own object type,
|
|
initializes it properly and updates it to represent the current
|
|
state of their master, e.g. a link cache would include all
|
|
links currently configured in the kernel.
|
|
|
|
Some of the allocation functions may take additional arguments
|
|
to further specify what will be part of the cache.
|
|
|
|
All such functions return a newly allocated cache or NULL
|
|
in case of an error.
|
|
|
|
=== Cache Manager
|
|
|
|
The purpose of a cache manager is to keep track of caches and
|
|
automatically receive event notifications to keep the caches
|
|
up to date with the kernel state. Each manager has exactly one
|
|
netlink socket assigned which limits the scope of each manager
|
|
to exactly one netlink family. Therefore all caches committed
|
|
to a manager must be part of the same netlink family. Due to the
|
|
nature of a manager, it is not possible to have a cache maintain
|
|
two instances of the same cache type. The socket is subscribed
|
|
to the event notification group of each cache and also put into
|
|
non-blocking mode. Functions exist to poll() on the socket to
|
|
wait for new events to be received.
|
|
|
|
|
|
----
|
|
App libnl Kernel
|
|
| |
|
|
+-----------------+ [ notification, link change ]
|
|
| | Cache Manager | | [ (IFF_UP | IFF_RUNNING) ]
|
|
| | |
|
|
| | +------------+| | | [ notification, new addr ]
|
|
<-------|---| route/link |<-------(async)--+ [ 10.0.1.1/32 dev eth1 ]
|
|
| | +------------+| | |
|
|
| +------------+| |
|
|
<---|---|---| route/addr |<------|-(async)--------------+
|
|
| +------------+|
|
|
| | +------------+| |
|
|
<-------|---| ... ||
|
|
| | +------------+| |
|
|
+-----------------+
|
|
| |
|
|
----
|
|
|
|
.Creating a new cache manager
|
|
|
|
[source,c]
|
|
----
|
|
struct nl_cache_mngr *mngr;
|
|
|
|
// Allocate a new cache manager for RTNETLINK and automatically
|
|
// provide the caches added to the manager.
|
|
err = nl_cache_mngr_alloc(NULL, NETLINK_ROUTE, NL_AUTO_PROVIDE, &mngr);
|
|
----
|
|
|
|
.Keep track of a cache
|
|
|
|
[source,c]
|
|
----
|
|
struct nl_cache *cache;
|
|
|
|
// Create a new cache for links/interfaces and ask the manager to
|
|
// keep it up to date for us. This will trigger a full dump request
|
|
// to initially fill the cache.
|
|
cache = nl_cache_mngr_add(mngr, "route/link");
|
|
-----
|
|
|
|
.Make the manager receive updates
|
|
|
|
[source,c]
|
|
----
|
|
// Give the manager the ability to receive updates, will call poll()
|
|
// with a timeout of 5 seconds.
|
|
if (nl_cache_mngr_poll(mngr, 5000) > 0) {
|
|
// Manager received at least one update, dump cache?
|
|
nl_cache_dump(cache, ...);
|
|
}
|
|
----
|
|
|
|
.Release cache manager
|
|
|
|
[source,c]
|
|
----
|
|
nl_cache_mngr_free(mngr);
|
|
----
|
|
|
|
== Abstract Data Types
|
|
|
|
A few high level abstract data types which are used by a majority netlink
|
|
protocols are implemented in the core library. More may be added in the
|
|
future if the need arises.
|
|
|
|
=== Abstract Address
|
|
|
|
Most netlink protocols deal with networking related topics and thus
|
|
dealing with network addresses is a common task.
|
|
|
|
Currently the following address families are supported:
|
|
|
|
[options="compact"]
|
|
* `AF_INET`
|
|
* `AF_INET6`
|
|
* `AF_LLC`
|
|
* `AF_DECnet`
|
|
* `AF_UNSPEC`
|
|
|
|
[[core_addr_alloc]]
|
|
.Address Allocation
|
|
|
|
The function nl_addr_alloc() allocates a new empty address. The
|
|
+maxsize+ argument defines the maximum length of an address in bytes.
|
|
The size of an address is address family specific. If the address
|
|
family and address data are known at allocation time the function
|
|
nl_addr_build() can be used alternatively. You may also clone
|
|
an address by calling nl_addr_clone()
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/addr.h>
|
|
|
|
struct nl_addr *nl_addr_alloc(size_t maxsize);
|
|
struct nl_addr *nl_addr_clone(struct nl_addr *addr);
|
|
struct nl_addr *nl_addr_build(int family, void *addr, size_t size);
|
|
--------
|
|
|
|
If the address is transported in a netlink attribute, the function
|
|
nl_addr_alloc_attr() allocates a new address based on the payload
|
|
of the attribute provided. The +family+ argument is used to specify
|
|
the address family of the address, set to +AF_UNSPEC+ if unknown.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/addr.h>
|
|
|
|
struct nl_addr *nl_addr_alloc_attr(struct nlattr *attr, int family);
|
|
--------
|
|
|
|
If the address is provided by a user, it is usually stored in a human
|
|
readable format. The function nl_addr_parse() parses a character
|
|
string representing an address and allocates a new address based on
|
|
it.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/addr.h>
|
|
|
|
int nl_addr_parse(const char *addr, int hint, struct nl_addr **result);
|
|
--------
|
|
|
|
If parsing succeeds the function returns 0 and the allocated address
|
|
is stored in +*result+.
|
|
|
|
NOTE: Make sure to return the reference to an address using
|
|
`nl_addr_put()` after usage to allow memory being freed.
|
|
|
|
.Example: Transform character string to abstract address
|
|
[source,c]
|
|
-----
|
|
struct nl_addr *a = nl_addr_parse("::1", AF_UNSPEC);
|
|
printf("Address family: %s\n", nl_af2str(nl_addr_get_family(a)));
|
|
nl_addr_put(a);
|
|
a = nl_addr_parse("11:22:33:44:55:66", AF_UNSPEC);
|
|
printf("Address family: %s\n", nl_af2str(nl_addr_get_family(a)));
|
|
nl_addr_put(a);
|
|
-----
|
|
|
|
.Address References
|
|
|
|
Abstract addresses use reference counting to account for all users of
|
|
a particular address. After the last user has returned the reference
|
|
the address is freed.
|
|
|
|
If you pass on a address object to another function and you are not
|
|
sure how long it will be used, make sure to call nl_addr_get() to
|
|
acquire an additional reference and have that function or code path
|
|
call nl_addr_put() as soon as it has finished using the address.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/addr.h>
|
|
|
|
struct nl_addr *nl_addr_get(struct nl_addr *addr);
|
|
void nl_addr_put(struct nl_addr *addr);
|
|
int nl_addr_shared(struct nl_addr *addr);
|
|
--------
|
|
|
|
You may call nl_addr_shared() at any time to check if you are the only
|
|
user of an address.
|
|
|
|
.Address Attributes
|
|
|
|
The address is usually set at allocation time. If it was unknown at that
|
|
time it can be specified later by calling nl_addr_set_family() and is
|
|
accessed with the function nl_addr_get_family().
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/addr.h>
|
|
|
|
void nl_addr_set_family(struct nl_addr *addr, int family);
|
|
int nl_addr_get_family(struct nl_addr *addr);
|
|
--------
|
|
|
|
The same is true for the actual address data. It is typically present
|
|
at allocation time. For exceptions it can be specified later or
|
|
overwritten with the function `nl_addr_set_binary_addr()`. Beware that
|
|
the length of the address may not exceed `maxlen` specified at
|
|
allocation time. The address data is returned by the function
|
|
`nl_addr_get_binary_addr()` and its length by the function
|
|
`nl_addr_get_len()`.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/addr.h>
|
|
|
|
int nl_addr_set_binary_addr(struct nl_addr *addr, void *data, size_t size);
|
|
void *nl_addr_get_binary_addr(struct nl_addr *addr);
|
|
unsigned int nl_addr_get_len(struct nl_addr *addr);
|
|
--------
|
|
|
|
If you only want to check if the address data consists of all zeros
|
|
the function `nl_addr_iszero()` is a shortcut to that.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/addr.h>
|
|
|
|
int nl_addr_iszero(struct nl_addr *addr);
|
|
--------
|
|
|
|
==== Address Prefix Length
|
|
|
|
Although this functionality is somewhat specific to routing it has
|
|
been implemented here. Addresses can have a prefix length assigned
|
|
which implies that only the first n bits are of importance. This is
|
|
f.e. used to implement subnets.
|
|
|
|
Use set functions `nl_addr_set_prefixlen()` and
|
|
`nl_addr_get_prefixlen()` to work with prefix lengths.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/addr.h>
|
|
|
|
void nl_addr_set_prefixlen(struct nl_addr *addr, int n);
|
|
unsigned int nl_addr_get_prefixlen(struct nl_addr *addr);
|
|
--------
|
|
|
|
NOTE: The default prefix length is set to (address length * 8)
|
|
|
|
.Address Helpers
|
|
|
|
Several functions exist to help when dealing with addresses. The
|
|
function `nl_addr_cmp()` compares two addresses and returns an integer
|
|
less than, equal to or greater than zero without considering the
|
|
prefix length at all. If you want to consider the prefix length, use
|
|
the function `nl_addr_cmp_prefix()`.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/addr.h>
|
|
|
|
int nl_addr_cmp(struct nl_addr *addr, struct nl_addr *addr);
|
|
int nl_addr_cmp_prefix(struct nl_addr *addr, struct nl_addr *addr);
|
|
--------
|
|
|
|
If an abstract address needs to presented to the user it should be
|
|
done in a human readable format which differs depending on the address
|
|
family. The function `nl_addr2str()` takes care of this by calling the
|
|
appropriate conversion functions internaly. It expects a `buf` of
|
|
length `size` to write the character string into and returns a pointer
|
|
to `buf` for easy `printf()` usage.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/addr.h>
|
|
|
|
char *nl_addr2str(struct nl_addr *addr, char *buf, size_t size);
|
|
--------
|
|
|
|
If the address family is unknown, the address data will be printed in
|
|
hexadecimal format `AA:BB:CC:DD:...`
|
|
|
|
Often the only way to figure out the address family is by looking at
|
|
the length of the address. The function `nl_addr_guess_family()` does
|
|
just this and returns the address family guessed based on the address
|
|
size.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/addr.h>
|
|
|
|
int nl_addr_guess_family(struct nl_addr *addr);
|
|
--------
|
|
|
|
Before allocating an address you may want to check if the character
|
|
string actually represents a valid address of the address family you
|
|
are expecting. The function `nl_addr_valid()` can be used for that, it
|
|
returns 1 if the supplised `addr` is a valid address in the context of
|
|
`family`. See `inet_pton(3)`, `dnet_pton(3)` for more information on
|
|
valid adddress formats.
|
|
|
|
[source,c]
|
|
--------
|
|
#include <netlink/addr.h>
|
|
|
|
int nl_addr_valid(char *addr, int family);
|
|
--------
|
|
|
|
=== Abstract Data
|
|
|
|
The abstract data type is a trivial datatype with the primary purpose
|
|
to simplify usage of netlink attributes of arbitary length.
|
|
|
|
[[core_data_alloc]]
|
|
.Allocation of a Data Object
|
|
The function `nl_data_alloc()` alloctes a new abstract data object and
|
|
fill it with the provided data. `nl_data_alloc_attr()` does the same
|
|
but bases the data on the payload of a netlink attribute. New data
|
|
objects can also be allocated by cloning existing ones by using
|
|
`nl_data_clone()`.
|
|
|
|
[source,c]
|
|
--------
|
|
struct nl_data *nl_data_alloc(void *buf, size_t size);
|
|
struct nl_data *nl_data_alloc_attr(struct nlattr *attr);
|
|
struct nl_data *nl_data_clone(struct nl_data *data);
|
|
void nl_data_free(struct nl_data *data);
|
|
--------
|
|
|
|
.Access to Data
|
|
|
|
The function `nl_data_get()` returns a pointer to the data, the size
|
|
of data is returned by `nl_data_get_size()`.
|
|
|
|
[source,c]
|
|
--------
|
|
void *nl_data_get(struct nl_data *data);
|
|
size_t nl_data_get_size(struct nl_data *data);
|
|
--------
|
|
|
|
.Data Helpers
|
|
|
|
The function nl_data_append() reallocates the internal data buffers
|
|
and appends the specified `buf` to the existing data.
|
|
|
|
[source,c]
|
|
--------
|
|
int nl_data_append(struct nl_data *data, void *buf, size_t size);
|
|
--------
|
|
|
|
CAUTION: Any call to `nl_data_append()` invalidates all pointers
|
|
returned by `nl_data_get()` of the same data object.
|
|
|
|
[source,c]
|
|
--------
|
|
int nl_data_cmp(struct nl_data *data, struct nl_data *data);
|
|
--------
|