
netlink_ipsec_monitor-source.tar.gz (8.69 KB)
mzudev-source.tar.gz (4.94 KB)
netlink_rtnetlink_monitor-source-20180628.tar.gz (11.5 KB)
netlink_iotop-source-~20190319.tar.gz (6.12 KB)
+----------------------+----------------------+----------------------+----------------------+ ~ ~ ~ ~ ~ +----------------------+ | Netlink messaeg #1 | Netlink messaeg #2 | Netlink messaeg #3 | Netlink messaeg #4 | ... | Netlink messaeg #n | | (Header+Payload+Pad) | (Header+Payload+Pad) | (Header+Payload+Pad) | (Header+Payload+Pad) | | (Header+Payload+Pad) | +----------------------+----------------------+----------------------+----------------------+ ~ ~ ~ ~ ~ +----------------------+ <--------------------------------------------------- Request OR Response packet ----------------------------------------------->
| 0 | 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 | 31 | Description | |
| 32bit Length (Header를 포함한 message 크기) | ↑↓16 bytes NLMSG_HDRLEN (struct nlmsghdr) | ↑↓Netlink message (nlmsghdr.nlmsg_len) | |||||||||||||||||||||||||||||||
| 16-bit Type (Message content) | 16-bit Flags (Additional flags) | ||||||||||||||||||||||||||||||||
| 32bit Sequence Number | |||||||||||||||||||||||||||||||||
| 32bit Process ID (PID, Sending process port ID) | |||||||||||||||||||||||||||||||||
| Payload (Variable data) data_ptr = NLMSG_DATA(nlmsghdr) | ↑↓(nlmsghdr.nlmsg_len - NLMSG_HDRLEN) | ||||||||||||||||||||||||||||||||
<----- NLMSG_HDRLEN ------> <-------- Payload-Len --------> +---------------------+- - -+- - - - - - - - - - - - - - - -+ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | Header | Pad | Payload | ... (Next netlink message) ... | (struct nlmsghdr) | ing | Specific data + [attribute..] | +---------------------+- - -+- - - - - - - - - - - - - - - -+ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ↑ nlmsghdr ↑ NLMSG_DATA(&nlmsghdr) ↑ NLMSG_NEXT(&nlmsghdr) <------------------ nlmsghdr->nlmsg_len ------------------> <------------------ NLMSG_LENGTH(Payload-Len) ------------> Payload의 선두부분은 nlmsg_type에 따른 고유 구조체(Specific data) 형식이 올 수 있으며 NLMSG_ALIGN(sizeof(Specific data)) 정렬 후 그 다음에 attribute로 구성되는게 일반적입니다.
<------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> +---------------------+- - -+- - - - - - - - - -+- - -+ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | Header | Pad | Payload | Pad | ... (Next attribute) ... | (struct nlattr) | ing | | ing | +---------------------+- - -+- - - - - - - - - -+- - -+ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ↑ nlattr <-------------- nlattr->nla_len --------------> nla_type (16 bits) +---+---+-------------------------------+ | N | O | Attribute Type | +---+---+-------------------------------+ N := Carries nested attributes O := Payload stored in network byte order Note: The N and O flag are mutually exclusive.
struct nlmsghdr {
uint32_t nlmsg_len; /* Header를 포함한 Netlink message 크기 */
uint16_t nlmsg_type; /* Message content */
uint16_t nlmsg_flags; /* Additional flags */
uint32_t nlmsg_seq; /* Sequence number */
uint32_t nlmsg_pid; /* Sending process port ID */
};
struct nlattr {
uint16_t nla_len; /* Header를 포함한 attribute 크기 */
uint16_t nla_type; /* Attribute type */
};
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Netlink message header (nlmsghdr) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Generic Netlink message header (genlmsghdr) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Optional user specific message header | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Optional Generic Netlink message payload | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
s_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_XFRM /* 6 - ipsec */);
__u32 s_nl_groups; struct sockaddr_nl s_sockaddr_nl; s_nl_groups |= XFRMNLGRP_ACQUIRE; s_nl_groups |= XFRMNLGRP_EXPIRE; s_nl_groups |= XFRMNLGRP_SA; s_nl_groups |= XFRMNLGRP_POLICY; s_nl_groups |= XFRMNLGRP_AEVENTS; s_nl_groups |= XFRMNLGRP_REPORT; s_nl_groups |= XFRMNLGRP_MIGRATE; s_nl_groups |= XFRMNLGRP_MAPPING; s_sockaddr_nl.nl_family = AF_NETLINK; s_sockaddr_nl.nl_pad = (unsigned short)0u; s_sockaddr_nl.nl_pid = (pid_t)0; s_sockaddr_nl.nl_groups = s_nl_groups; /* Multicast groups mask */ bind(s_socket, (const struct sockaddr *)(&s_sockaddr_nl), (socklen_t)sizeof(s_sockaddr_nl));
socklen_t s_socklen;
s_socklen = (socklen_t)sizeof(s_sockaddr_nl);
s_recv_bytes = recvfrom(
s_socket,
s_buffer,
s_buffer_size,
MSG_NOSIGNAL,
(struct sockaddr *)(&s_sockaddr_nl),
(socklen_t *)(&s_socklen)
);
size_t s_msg_size;
struct nlmsghdr *s_nlmsghdr;
size_t s_payload_size;
void *s_payload;
s_msg_size = (size_t)s_recv_bytes;
for(s_nlmsghdr = (struct nlmsghdr *)s_buffer;(s_is_break == 0) && NLMSG_OK(s_nlmsghdr, s_msg_size);s_nlmsghdr = NLMSG_NEXT(s_nlmsghdr, s_msg_size)) { /* Netlink 수신패킷 하나에 여러개의 Netlink header가 탑재될 수 있는데 이를 각 Header 단위로 분리하는 Loop */
s_payload_size = (size_t)NLMSG_PAYLOAD(s_nlmsghdr, 0); /* Header 내의 실제 Data 크기 */
s_payload = NLMSG_DATA(s_nlmsghdr); /* Header 내의 실제 Data 위치 포인터 */
switch(s_nlmsghdr->nlmsg_type) { /* 각 메세지의 종류별로 다른 파싱구조를 가지고 있으므로 커널을 참조하여 해당 부분을 파싱해야 합니다. */
.....
}
}
#include <linux/netlink.h> #include <linux/genetlink.h>
size_t hwport_generate_netlink_message(void *s_buffer, size_t s_buffer_size, unsigned int s_nlmsg_type, unsigned int s_nlmsg_flags, unsigned int s_nlmsg_seq, unsigned int s_nlmsg_pid, const void *s_payload, size_t s_payload_size)
{
int s_aligned_payload_size;
struct nlmsghdr *s_nlmsghdr;
void *s_payload_ptr;
s_aligned_payload_size = (int)NLMSG_ALIGN((uint32_t)s_payload_size);
if (s_buffer_size < ((size_t)NLMSG_LENGTH(s_aligned_payload_size))) {
return((size_t)0u);
}
s_nlmsghdr = (struct nlmsghdr *)memset(s_buffer, 0, sizeof(struct nlmsghdr));
s_nlmsghdr->nlmsg_len = (uint32_t)NLMSG_LENGTH(s_aligned_payload_size);
s_nlmsghdr->nlmsg_type = (uint16_t)s_nlmsg_type;
s_nlmsghdr->nlmsg_flags = (uint16_t)s_nlmsg_flags;
s_nlmsghdr->nlmsg_seq = (uint32_t)s_nlmsg_seq;
s_nlmsghdr->nlmsg_pid = (uint32_t)s_nlmsg_pid;
s_payload_ptr = (void *)NLMSG_DATA(s_nlmsghdr);
if (s_payload_size > ((size_t)0u)) {
if (s_payload == ((const void *)(NULL))) {
(void)memset(s_payload_ptr, 0, s_payload_size);
}
else {
(void)memcpy(s_payload_ptr, s_payload, s_payload_size);
}
}
return((size_t)s_nlmsghdr->nlmsg_len);
}
size_t hwport_append_netlink_attr(void *s_buffer, size_t s_buffer_size, unsigned int s_nla_type, const void *s_attr1, size_t s_attr1_size, const void *s_attr2, size_t s_attr2_size)
{
struct nlmsghdr *s_nlmsghdr;
struct nlattr *s_nlattr;
size_t s_attr_size;
void *s_attr_ptr;
s_attr_size = s_attr1_size + s_attr2_size;
s_nlmsghdr = (struct nlmsghdr *)s_buffer;
if (s_buffer_size < ((size_t)(s_nlmsghdr->nlmsg_len + NLA_HDRLEN + NLA_ALIGN(s_attr_size)))) {
return((size_t)0u);
}
s_nlattr = (struct nlattr *)(((uint8_t *)s_buffer) + s_nlmsghdr->nlmsg_len);
s_nlattr->nla_len = (uint16_t)NLA_HDRLEN + s_attr_size;
s_nlattr->nla_type = (uint16_t)s_nla_type;
s_attr_ptr = (void *)(((uint8_t *)s_nlattr) + NLA_HDRLEN);
if(s_attr1_size > ((size_t)0u)) {
if(s_attr1 == ((const void *)0)) {
(void)memset((void *)s_attr_ptr, 0, s_attr1_size);
}
else if(((const void *)s_attr_ptr) != s_attr1) {
(void)memcpy((void *)s_attr_ptr, s_attr1, s_attr1_size);
}
s_attr_ptr = (void *)(((uint8_t *)s_attr_ptr) + s_attr1_size);
}
if(s_attr2_size > ((size_t)0u)) {
if(s_attr2 == ((const void *)0)) {
(void)memset((void *)s_attr_ptr, 0, s_attr2_size);
}
else if(((const void *)s_attr_ptr) != s_attr2) {
(void)memcpy((void *)s_attr_ptr, s_attr2, s_attr2_size);
}
}
s_nlmsghdr->nlmsg_len += (uint32_t)NLA_ALIGN(s_nlattr->nla_len);
return((size_t)s_nlmsghdr->nlmsg_len);
}
ssize_t hwport_request_generic_netlink(int s_socket, const void *s_data, size_t s_size, const struct sockaddr *s_sockaddr, socklen_t s_socklen)
{
size_t s_sent_size;
size_t s_want_size;
ssize_t s_send_bytes;
const uint8_t *s_uint8_ptr;
struct sockaddr_nl s_sockaddr_nl;
if (s_socket == (-1)) {
errno = EINVAL;
return((ssize_t)(-1));
}
if (s_sockaddr == ((struct sockaddr *)(NULL))) {
s_socklen = (socklen_t)sizeof(s_sockaddr_nl);
s_sockaddr = (const struct sockaddr *)memset((void *)(&s_sockaddr_nl), 0, sizeof(s_sockaddr_nl));
s_sockaddr_nl.nl_family = AF_NETLINK;
s_sockaddr_nl.nl_pid = 0; /* port ID */
s_sockaddr_nl.nl_groups = 0; /* multicast groups mask */
}
s_sent_size = (size_t)0u;
s_uint8_ptr = (const uint8_t *)s_data;
while (s_sent_size < s_size) {
s_want_size = s_size - s_sent_size;
s_send_bytes = sendto(
s_socket,
(const void *)(&s_uint8_ptr[s_sent_size]),
s_want_size,
MSG_NOSIGNAL,
s_sockaddr,
s_socklen
);
if (s_send_bytes > ((ssize_t)0)) {
s_sent_size += (size_t)s_send_bytes;
}
else if((s_send_bytes == ((ssize_t)(-1))) && ((errno == EINTR) || (errno == EAGAIN))) {
continue;
}
else { /* error */
return(s_send_bytes);
}
}
return((ssize_t)s_sent_size);
}
ssize_t hwport_response_generic_netlink(int s_socket, void *s_data, size_t s_size, struct sockaddr *s_sockaddr, socklen_t *s_socklen_ptr)
{
struct sockaddr_storage s_sockaddr_storage;
socklen_t s_socklen;
if (s_socket == (-1)) {
errno = EINVAL;
return((ssize_t)(-1));
}
if (s_sockaddr == ((struct sockaddr *)(NULL))) {
s_sockaddr = (struct sockaddr *)memset((void *)(&s_sockaddr_storage), 0, sizeof(s_sockaddr_storage));
}
if (s_socklen_ptr == ((socklen_t *)(NULL))) {
s_socklen = (socklen_t)sizeof(struct sockaddr_nl);
s_socklen_ptr = (socklen_t *)(&s_socklen);
}
return(recvfrom(s_socket, s_data, s_size, MSG_NOSIGNAL, s_sockaddr, s_socklen_ptr));
}
int hwport_get_family_id_by_name(int s_socket, const char *s_family_name)
{
int s_family_id;
size_t s_family_name_size;
uint8_t s_buffer[ 4 << 10 ];
size_t s_message_size;
size_t s_offset;
int s_is_break;
struct nlmsghdr *s_nlmsghdr;
struct genlmsghdr *s_genlmsghdr;
struct nlattr *s_nlattr;
ssize_t s_send_bytes;
ssize_t s_recv_bytes;
if (s_socket == (-1)) {
errno = EINVAL;
return(-1);
}
if (s_family_name == ((const char *)(NULL))) {
errno = EINVAL;
return(-1);
}
s_family_name_size = strlen(s_family_name);
s_nlmsghdr = (struct nlmsghdr *)(&s_buffer[0]);
s_message_size = hwport_generate_netlink_message(
(void *)s_nlmsghdr,
sizeof(s_buffer),
(unsigned int)GENL_ID_CTRL, /* nlmsg_type */
(unsigned int)NLM_F_REQUEST /* | NLM_F_ACK */, /* nlmsg_flags */
0u, /* nlmsg_seq */
(unsigned int)getpid(), /* nlmsg_pid */
(const void *)(NULL), /* payload */
sizeof(struct genlmsghdr)
);
if (s_message_size <= ((size_t)0u)) {
errno = ENOMEM;
return(-1);
}
s_genlmsghdr = (struct genlmsghdr *)memset((void *)NLMSG_DATA(s_nlmsghdr), 0, sizeof(struct genlmsghdr));
s_genlmsghdr->cmd = CTRL_CMD_GETFAMILY; /* SeeAlso : CTRL_CMD_XXXX in "include/uapi/linux/genetlink.h" */
s_genlmsghdr->version = 1 /* 해당 message 종류에 따른 VERSION을 넣어야 함 */;
s_message_size = hwport_append_netlink_attr(
(void *)s_nlmsghdr,
sizeof(s_buffer),
(unsigned int)CTRL_ATTR_FAMILY_NAME, /* nla_type */
(const void *)s_family_name, /* attr1 */
s_family_name_size + ((size_t)1u), /* attr1_size (family name 은 문자열 뒤의 nul terminate까지 포함한 길이여야 함) */
(const void *)(NULL), /* attr2 */
(size_t)0u /* attr2_size */
);
if (s_message_size <= ((size_t)0u)) {
errno = ENOMEM;
return(-1);
}
s_send_bytes = hwport_request_generic_netlink(
s_socket,
(const void *)s_nlmsghdr,
s_message_size,
(const struct sockaddr *)(NULL),
(socklen_t)0
);
if (s_send_bytes <= ((ssize_t)0)) {
return(-1);
}
s_recv_bytes = hwport_response_generic_netlink(
s_socket,
(void *)(&s_buffer[0]),
sizeof(s_buffer),
(struct sockaddr *)(NULL),
(socklen_t *)(NULL)
);
if (s_recv_bytes <= ((ssize_t)0)) {
return(-1);
}
s_family_id = (-1);
s_is_break = 0;
s_message_size = (size_t)s_recv_bytes;
for (s_nlmsghdr = (struct nlmsghdr *)(&s_buffer[0]);(s_is_break == 0) && NLMSG_OK(s_nlmsghdr, s_message_size);s_nlmsghdr = NLMSG_NEXT(s_nlmsghdr, s_message_size)) {
/* payload_size = (size_t)NLMSG_PAYLOAD(s_nlmsghdr, 0); */
/* payload = NLMSG_DATA(s_nlmsghdr); */
switch(s_nlmsghdr->nlmsg_type) {
case NLMSG_NOOP:
break;
case NLMSG_ERROR:
s_is_break = 1;
break;
case NLMSG_DONE:
s_is_break = 1;
break;
case NLMSG_OVERRUN:
s_is_break = 1;
break;
case GENL_ID_CTRL: /* NLMSG_MIN_TYPE */
s_genlmsghdr = (struct genlmsghdr *)NLMSG_DATA(s_nlmsghdr);
for (s_offset = (size_t)(NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(struct genlmsghdr)));s_offset < s_nlmsghdr->nlmsg_len;) {
if ((s_offset + ((size_t)NLA_HDRLEN)) > ((size_t)s_nlmsghdr->nlmsg_len)) {
/* attibute header 만큼이 남지 않았음. */
break;
}
s_nlattr = (struct nlattr *)(((uint8_t *)s_nlmsghdr) + s_offset);
if (s_nlattr->nla_len < ((size_t)NLA_HDRLEN)) {
/* attribute length 가 최소 크기를 만족하지 못함 */
break;
}
if ((s_offset + ((size_t)NLA_ALIGN(s_nlattr->nla_len))) > ((size_t)s_nlmsghdr->nlmsg_len)) {
/* attribute 공간이 nlmsg를 넘어섬 */
break;
}
switch(s_nlattr->nla_type) { /* SeeAlso : CTRL_ATTR_XXXX in "include/uapi/linux/genetlink.h" */
case CTRL_ATTR_UNSPEC: /* 0 */
break;
case CTRL_ATTR_FAMILY_ID: /* 1 */
s_family_id = (int)(*((uint16_t *)(((uint8_t *)s_nlattr) + NLA_HDRLEN)));
#if 1L
if (s_family_id != (-1)) {
/* family id 를 인지했으므로 더이상의 attr은 볼 필요가 없음 */
s_is_break = 1;
break;
}
#endif
break;
case CTRL_ATTR_FAMILY_NAME: /* 2 */
break;
case CTRL_ATTR_VERSION: /* 3 */
break;
case CTRL_ATTR_HDRSIZE: /* 4 */
break;
case CTRL_ATTR_MAXATTR: /* 5 */
break;
case CTRL_ATTR_OPS: /* 6 */
break;
case CTRL_ATTR_MCAST_GROUPS: /* 7 */
break;
default: /* 8 >= */
break;
}
s_offset += (size_t)NLA_ALIGN(s_nlattr->nla_len);
}
break;
default:
break;
}
}
return(s_family_id);
}
int hwport_open_generic_netlink(uint32_t s_port_id, uint32_t s_groups_mask)
{
int s_socket;
struct sockaddr_nl s_sockaddr_nl;
s_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (s_socket == (-1)) {
return(-1);
}
(void)memset((void *)(&s_sockaddr_nl), 0, sizeof(s_sockaddr_nl));
s_sockaddr_nl.nl_family = AF_NETLINK;
#if 0L
s_sockaddr_nl.nl_pad = 0;
#endif
s_sockaddr_nl.nl_pid = s_port_id; /* port ID */
s_sockaddr_nl.nl_groups = s_groups_mask; /* multicast groups mask */
if (bind(s_socket, (struct sockaddr *)(&s_sockaddr_nl), (socklen_t)sizeof(s_sockaddr_nl)) == (-1)) {
int s_check;
int s_save_errno;
s_save_errno = errno;
do {
s_check = close(s_socket);
}while((s_check == (-1)) && (errno == EINTR));
errno = s_save_errno;
return(-1);
}
return(s_socket);
}
int hwport_close_generic_netlink(int s_socket)
{
int s_check;
if (s_socket == (-1)) {
errno = EINVAL;
return(-1);
}
do {
s_check = close(s_socket);
}while((s_check == (-1)) && (errno == EINTR));
return(s_check);
}
https://www.rfc-editor.org/rfc/rfc3549
https://github.com/shemminger/iproute2
<= Netlink 구현은 이 소스가 이해하는데 많은 도움이 됩니다. (추천)
http://www.linuxfoundation.org/collaborate/workgroups/networking/generic_netlink_howto
https://people.redhat.com/nhorman/papers/netlink.pdf
http://inai.de/documents/Netlink_Protocol.pdf
https://nscpolteksby.ac.id/ebook/files/Ebook/Computer%20Engineering/Linux%20Kernel%20Networking%20-%20Implementation%20(2014)/chapter%202%20Netlink%20Sockets.pdf
https://people.netfilter.org/pablo/netlink/netlink-libmnl-manual.pdf
http://haifux.org/lectures/219/netLec6.pdf
https://medium.com/@mdlayher/linux-netlink-and-go-part-1-netlink-4781aaeeaca8
https://stackoverrun.com/ko/q/7837552
https://www.infradead.org/~tgr/libnl/doc/core.html
https://www.ctolib.com/topics-134609.html
http://linux-development-for-fresher.blogspot.com/2012/05/understanding-netlink-socket.html
https://gist.github.com/arunk-s/c897bb9d75a6c98733d6