Netlink被用來當作kernel和user space之間溝通資訊的方式之一,使用標準的socket介面來作為Netlink的API,其address family必須填AF_NETLINK,而socket type為SOCK_RAW或SOCK_DGRAM,protocol則根據不同的netlink group不同而有所不同,如NETLINK_ROUTE或NETLINK_GENERIC等等(詳細資訊可以man 7 netlink)。
socket = socket(AF_NETLINK, SOCK_RAW, netlink_family);
Netlink Socket Address Structure
如同一般的socket,Netlink也需要socket address,其socket address為:struct sockaddr_nl { sa_family_t nl_family; /* AF_NETLINK */ unsigned short nl_pad; /* Zero. */ pid_t nl_pid; /* Process ID. */ __u32 nl_groups; /* Multicast groups mask. */ };如果傳送的對象是kernel或者以multicast傳送,則nl_pid設為0。如果是kernel傳送給user-space上面的application時(multicast),就會填入正確的pid,然而nl_pid實際的意義並不是指PID,而只是用於識別一個netlink socket而已,對於application在建立一個netlink socket時,可以將nl_pid設為0,然後bind(),kernel會自動將PID填入。而nl_groups是用multicast,採用bit mask方式,所以每個netlink family有32個multicast group。
Netlink Format
Netlink message包含一個或多個struct nlmsghdr,主要用於辨識後面的payload內容為何。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; /* PID of the sending process. */ };
所以常見的讀取netlink message形式如下:
int len; char buf[4096]; struct iovec iov = { buf, sizeof(buf) }; struct sockaddr_nl sa; struct msghdr msg; struct nlmsghdr *nh; msg = { (void *)&sa, sizeof(sa), &iov, 1, NULL, 0, 0 }; len = recvmsg(fd, &msg, 0); for (nh = (struct nlmsghdr *) buf; 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. */ ... }
Macro
Netlink也提供一些macro來操作這些netlink datagram,因為kernel和user-space溝通會有align的問題,所以不論是在組packet或者parse packet,都請用這些macro。#define NLMSG_ALIGNTO 4 #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) #define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) #define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN)) #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) #define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ (struct nlmsghdr*)(((char*)(nlh)) + \ NLMSG_ALIGN((nlh)->nlmsg_len))) #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ (nlh)->nlmsg_len <= (len)) #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))NLMSG_ALIGN()很明顯的就是在做align。
NLMSG_HDRLEN()回傳struct nlmsghdr所占的大小。
NLMSG_LENGTH()回傳len + struct nlmsghdr所占的大小,通常用於填struct nlmsghdr的nlmsg_len用。
NLMSG_SPACE()回傳len + struct nlmsghdr再取align的大小,也就是netlink message的大小。
NLMSG_DATA()回傳nlh往後一個struct nlmsghdr所占的大小,就是取struct nlmsghdr後面跟著的資料。
NLMSG_NEXT()取得下一個struct nlmsghdr,並且將len減去原本的nlmsg_len,就等同在算剩餘大小。
NLMSG_OK()先算剩餘長度是否大於等於一個struct nlmsghdr,再算這個nlmsg_len是否大於等於一個struct nlmsghdr,而且nlmsg_len還要小於剩餘長度(len),如果都符合,就算是一個OK的struct nlmsghdr。
各macro與netlink message之間的關係。
Common API
基於netlink family基本上都是大同小異,所以參考Document/accounting/getdelays.c寫了一些API來用。static int create_nl_socket(int proto) { int sock; struct sockaddr_nl addr; if ((sock = socket(AF_NETLINK, SOCK_RAW, proto)) < 0) { fprintf(stderr, "open sock failed.(%s)\n", strerror(errno)); return -1; } memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { fprintf(stderr, "bind failed.(%s)\n", strerror(errno)); goto bind_err; } return sock; bind_err: close(sock); return -1; }
這個章節單純的只對netlink提供一些overview,後面會針對各個netlink family有更詳細的描述。