1. Create a socket

struct sockaddr_nl { sa_family_t nl_family; /* AF_NETLINK */ unsigned short nl_pad; /* Zero. */ pid_t nl_pid; /* Port ID. */ __u32 nl_groups; /* Multicast groups mask. */ };

nl_pid = 0 // if the destination is in the kernel nl_pid = getpid() // For a user-space process

socket(AF_NETLINK, socket_type, netlink_family)

socket type: SOCK_RAW / SOCK_DGRAM

netlink family: NETLINK_ROUTE Routing tables, IP addresses, link parameters, neighbor setups, traffic classes, etc. NETLINK_FIREWALL NETLINK_XFRM NETLINK_SELINUX NETLINK_GENERIC …

e.g. code

1
2
3
4
5
6
7
8
9
struct sockaddr_nl sa;

memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
//sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;

fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
bind(fd, (struct sockaddr *) &sa, sizeof(sa));

  1. Send message to kernel Ref: man 3 netlink, man 7 netlink, man 7 rtnetlink

Netlink messages consist of a byte stream with one or multiple nlmsghdr headers and associated payload. The byte stream should be accessed only with the standard NLMSG_* macros.

If multiple nlmsghdr headersthe first and all following headers should have the NLM_F_MULTI flag set, except for the last header which has the type NLMSG_DONE.

After each nlmsghdr the payload follows.

struct nlmsghdr { __u32 nlmsg_len; /* Length of message including header. */ __u16 nlmsg_type; /* Type of message content. */ __u16 nlmsg_flags; /* Additional flags. */ __u32 nlmsg_seq; /* Sequence number. */ __u32 nlmsg_pid; /* Sender port ID. */ };

nlmsg_type:

  • Stand:

    • NLMSG_ERROR
    • NLMSG_DONE
    • NLM_F_MULTI
  • NETLINK_ROUTE

    • RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK
    • RTM_NEWADDR, RTM_DELADDR, RTM_GETADDR
    • RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE
  • RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK struct ifinfomsg { unsigned char ifi_family; /* AF_UNSPEC */ unsigned short ifi_type; /* Device type */ int ifi_index; /* Interface index */ unsigned int ifi_flags; /* Device flags */ unsigned int ifi_change; /* change mask */ };

ifi_flags: man 7 netdevice ifi_change: 0xFFFFFFFF

  • RTM_NEWADDR, RTM_DELADDR, RTM_GETADDR struct ifaddrmsg { unsigned char ifa_family; /* Address type */ unsigned char ifa_prefixlen; /* Prefixlength of address */ unsigned char ifa_flags; /* Address flags */ unsigned char ifa_scope; /* Address scope */ int ifa_index; /* Interface index */ };

  • optional attributes: man 3 rtnetlink struct rtattr { unsigned short rta_len; /* Length of option */ unsigned short rta_type; /* Type of option */ /* Data follows */ }

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <linux/rtnetlink.h>

...

struct {
    struct nlmsghdr  nh;
    struct ifinfomsg if;
    char             attrbuf[512];
} req;

struct rtattr *rta;
unsigned int mtu = 1000;

int rtnetlink_sk = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);

memset(&req, 0, sizeof(req));
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req.nh.nlmsg_flags = NLM_F_REQUEST;
req.nh.nlmsg_type = RTM_NEWLINK;
req.if.ifi_family = AF_UNSPEC;
req.if.ifi_index = INTERFACE_INDEX;
req.if.ifi_change = 0xffffffff; /* ??? */
rta = (struct rtattr *)(((char *) &req) +
                         NLMSG_ALIGN(req.nh.nlmsg_len));
rta->rta_type = IFLA_MTU;
rta->rta_len = RTA_LENGTH(sizeof(unsigned int));
req.n.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) +
                              RTA_LENGTH(sizeof(mtu));
memcpy(RTA_DATA(rta), &mtu, sizeof(mtu));
send(rtnetlink_sk, &req, req.nh.nlmsg_len, 0);
  1. reading netlink message

IFLA_RTA: get a pointer to the first attribute in the RTM_NEWLINK message rtalen = nh->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)); or rtalen = IFLA_PAYLOAD(nh);

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int len;
char buf[4096];
struct iovec iov = { buf, sizeof(buf) };
struct sockaddr_nl sa;
struct msghdr msg;
struct nlmsghdr *nh;

msg = { &sa, sizeof(sa), &iov, 1, NULL, 0, 0 };
len = recvmsg(fd, &msg, 0);

nh = (struct nlmsghdr *) buf;
for (nh; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
	/* The end of multipart message. */
	if (nh->nlmsg_type == NLMSG_DONE)
		return;

	if (nh->nlmsg_type == NLMSG_ERROR)
		/* Do some error handling. */
		...
	/* Continue with parsing payload. */
		...
}

ref: http://maz-programmersdiary.blogspot.com/2011/09/netlink-sockets.html

socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)

struct sockaddr_nl nl_addr = {AF_NETLINK, 0, 0, 0};

struct { struct nlmsghdr; struct ifinfomsg; char attrbuf[BUFFER]; } msg

sendto()

man 7 netlink: netlink_socket = socket(AF_NETLINK, socket_type, netlink_family);

struct nlmsghdr { __u32 nlmsg_len; /* Length of message including header. */ __u16 nlmsg_type; /* Type of message content. */ __u16 nlmsg_flags; /* Additional flags. */ __u32 nlmsg_seq; /* Sequence number. */ __u32 nlmsg_pid; /* Sender port ID. */ };

Use NLMSG_* macros access nlmsghdr headers. see man 3 netlink.

struct sockaddr_nl { sa_family_t nl_family; /* AF_NETLINK */ unsigned short nl_pad; /* Zero. */ pid_t nl_pid; /* Port ID. */ __u32 nl_groups; /* Multicast groups mask. */ };

man 7 rtnetlink: rtnetlink_socket = socket(AF_NETLINK, int socket_type, NETLINK_ROUTE);

struct rtattr { unsigned short rta_len; /* Length of option */ unsigned short rta_type; /* Type of option */ /* Data follows */ };

Use RTA_* macros to modify attributes, see man 3 rtnetlink.

Messages:

RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK:

struct ifinfomsg { unsigned char ifi_family; /* AF_UNSPEC */ unsigned short ifi_type; /* Device type */ int ifi_index; /* Interface index */ unsigned int ifi_flags; /* Device flags */ unsigned int ifi_change; /* change mask */ };

RTM_NEWADDR, RTM_DELADDR, RTM_GETADDR

struct ifaddrmsg { unsigned char ifa_family; /* Address type */ unsigned char ifa_prefixlen; /* Prefixlength of address */ unsigned char ifa_flags; /* Address flags */ unsigned char ifa_scope; /* Address scope */ int ifa_index; /* Interface index */ };

Reference:

[]