Netlink is a Linux kernel IPC mechanism used for communication between:
- User space processes
- The Linux kernel networking stack
It is used to deliver:
- Interface up/down events
- IP address additions/removals
- Neighbor (ARP/ND) updates
- FDB events (for Linux bridges)
- Route updates
- VLAN notifications
SONiC daemons such as:
- portsyncd
- neighsyncd
- intfmgrd
use netlink to detect real-time changes.
A netlink socket is created with:
socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);- RTM_NEWLINK / RTM_DELLINK – interface state
- RTM_NEWADDR / RTM_DELADDR – IP address
- RTM_NEWNEIGH / RTM_DELNEIGH – ARP/ND
- RTM_NEWROUTE / RTM_DELROUTE – kernel routes
Processes subscribe to specific event groups:
RTNLGRP_LINKRTNLGRP_NEIGHRTNLGRP_IPV4_IFADDRRTNLGRP_IPV6_IFADDRRTNLGRP_IPV4_ROUTERTNLGRP_IPV6_ROUTERTNLGRP_BRVLANRTNLGRP_BRIDGE
Yes.
Example:
nl_groups = RTNLGRP_LINK | RTNLGRP_NEIGH | RTNLGRP_IPV4_IFADDR;struct sockaddr_nl addr = {0};
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid(); // unique for user process
addr.nl_groups = RTNLGRP_LINK | RTNLGRP_NEIGH;
bind(nl_sock, (struct sockaddr*)&addr, sizeof(addr));Netlink messages arrive via recvmsg():
char buf[8192];
struct iovec iov = { buf, sizeof(buf) };
struct sockaddr_nl kernel = {0};
struct msghdr msg = {
.msg_name = &kernel,
.msg_namelen = sizeof(kernel),
.msg_iov = &iov,
.msg_iovlen = 1,
};
int len = recvmsg(nl_sock, &msg, 0);- Blocks until a message is ready
- Retrieves one or more
nlmsghdrstructures - Returns size of received data
- Writes sender address (kernel) into
msg_name
for (struct nlmsghdr *nh = (struct nlmsghdr*)buf;
NLMSG_OK(nh, len);
nh = NLMSG_NEXT(nh, len))
{
if (nh->nlmsg_type == RTM_NEWLINK) {
struct ifinfomsg *ifi = NLMSG_DATA(nh);
// ifi->ifi_index, flags, oper state, etc.
}
if (nh->nlmsg_type == RTM_NEWNEIGH) {
struct ndmsg *nd = NLMSG_DATA(nh);
// nd->ndm_state, nd->ndm_ifindex, etc.
}
}| Daemon | Events Subscribed To |
|---|---|
| portsyncd | RTNLGRP_LINK |
| neighsyncd | RTNLGRP_NEIGH |
| intfmgrd | LINK + ADDR |
| teamsyncd | LINK + TEAM events |
int nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
// Subscribe to multiple groups
int groups = RTNLGRP_LINK | RTNLGRP_NEIGH | RTNLGRP_IPV4_IFADDR;
struct sockaddr_nl addr = {0};
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = groups;
bind(nl_sock, (struct sockaddr*)&addr, sizeof(addr));
while (1) {
char buf[8192];
struct iovec iov = { buf, sizeof(buf) };
struct sockaddr_nl kernel = {0};
struct msghdr msg = {
.msg_name = &kernel,
.msg_namelen = sizeof(kernel),
.msg_iov = &iov,
.msg_iovlen = 1,
};
int len = recvmsg(nl_sock, &msg, 0);
if (len <= 0) continue; // error or no data
// Walk each netlink message in the buffer
for (struct nlmsghdr *nh = (struct nlmsghdr*)buf;
NLMSG_OK(nh, len);
nh = NLMSG_NEXT(nh, len))
{
switch (nh->nlmsg_type)
{
case RTM_NEWLINK:
case RTM_DELLINK:
// Interface state change
break;
case RTM_NEWADDR:
case RTM_DELADDR:
// IP address assigned/removed
break;
case RTM_NEWNEIGH:
case RTM_DELNEIGH:
// ARP or ND update
break;
}
}
}Netlink is the backbone of SONiC kernel interaction. It allows daemons to:
- Detect interface up/down
- Track IP address changes
- Learn ARP/ND updates
- Receive Linux FDB/bridge events
- Synchronize kernel state with Redis and SAI