diff options
Diffstat (limited to 'md/notes')
-rw-r--r-- | md/notes/kernel/netlink_show_ip.md | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/md/notes/kernel/netlink_show_ip.md b/md/notes/kernel/netlink_show_ip.md new file mode 100644 index 0000000..87f0b83 --- /dev/null +++ b/md/notes/kernel/netlink_show_ip.md @@ -0,0 +1,423 @@ +title: Netlink socket show ip +keywords: kernel,linux,netlink,socket,ip + +# Netlink show network interface ip's + +This example based on reading netlink socket and using kernel builtin netlink protocols. + +As base is used netlink example. + + +__Makefile__ +```Makefile +make: + gcc netlink_show_ip.c -o netlink_show_ip +``` + + +__netlink_show_ip.c__ +```c +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/socket.h> +#include <linux/netlink.h> + +#define PAYLOAD_SIZE 1024 /* maximum payload size*/ +#define NETLINK_PROTO 23 + +int main(int argc, char **argv) { + + struct sockaddr_nl src_addr; + struct nlmsghdr *nlh; + struct msghdr msg; + struct iovec iov; + int sock_fd; + int ret; + + + sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock_fd < 0) + { + printf("socket creation for NETLINK failed...\n"); + return -1; + } + + close(sock_fd); + + return 0; +} +``` + +## Add socket configuration + +Here we configure netlink socket and send message to request ip addresses from NETLINK_ROUTE + +```c +... +int main(int argc, char **argv) { +... + memset(&src_addr, 0, sizeof(src_addr)); + src_addr.nl_family = AF_NETLINK; + + //bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));//??? + + + // assemble the message according to the netlink protocol + nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(PAYLOAD_SIZE)); + memset(nlh, 0, NLMSG_SPACE(PAYLOAD_SIZE)); + nlh->nlmsg_len = NLMSG_SPACE(PAYLOAD_SIZE); + nlh->nlmsg_type = RTM_GETADDR; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; + + struct ifaddrmsg *ifa; + ifa = (struct ifaddrmsg*)NLMSG_DATA(nlh); + ifa->ifa_family = AF_INET; // we only get ipv4 address here + + // prepare struct msghdr for sending. + memset(&iov, 0, sizeof(iov)); + iov.iov_base = (void *)nlh; + iov.iov_len = nlh->nlmsg_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (void *)&src_addr; + msg.msg_namelen = sizeof(src_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + + // send netlink message to kernel. + ret = sendmsg(sock_fd, &msg, 0); + if (ret < 0) { + printf("Can't send message to netlink socket\n"); + } +... +} + +``` + +## Receive and process netlink replies + +```c +#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)) +``` + + +Message types + +```c +#define NLMSG_NOOP 0x1 /* Nothing. */ +#define NLMSG_ERROR 0x2 /* Error */ +#define NLMSG_DONE 0x3 /* End of a dump */ +#define NLMSG_OVERRUN 0x4 /* Data lost */ +``` + +Definition of message rtnetlink types + +```c + RTM_NEWADDR = 20, +#define RTM_NEWADDR RTM_NEWADDR +``` + +List of type of RTA types + +```c +enum { + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, + IFA_MULTICAST, + IFA_FLAGS, + IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */ + IFA_TARGET_NETNSID, + IFA_PROTO, /* u8, address protocol */ + __IFA_MAX, +}; +``` + +Receiving netlink message from a + +```c +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +#include <net/if.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +... +... + +int main(){ +... +do { + ret = recvmsg(sock_fd, &msg_recv, 0); + if (ret < 0) { + printf("Can't receive reply message\n"); + break; + } + + if (ret==0) { + printf("received 0 byte reply\n"); + break; + } + + ret_len = ret; + nlh_recv = (struct nlmsghdr*)recv_buf; + while (NLMSG_OK(nlh_recv, (uint32_t)ret_len) && (nlh_recv->nlmsg_type != NLMSG_DONE)) + { + printf("received message type %d\n", nlh_recv->nlmsg_type); + if (nlh_recv->nlmsg_type == NLMSG_ERROR) { + printf("Message error\n"); + break; + } + + if (nlh_recv->nlmsg_type == RTM_NEWADDR) { + ... + } + nlh_recv = NLMSG_NEXT(nlh_recv, ret); + } + } while ((nl_msg_type != NLMSG_DONE) && (nl_msg_type != NLMSG_ERROR)); +... +} +``` + +After message type is detected parse the RTNETLINK message + +```c +int main() { +... + ifa_recv = (struct ifaddrmsg*)NLMSG_DATA(nlh_recv); + int ifa_len = IFA_PAYLOAD(nlh_recv); + rta = IFA_RTA(ifa_recv); + printf("interface %s\n", if_indextoname(ifa_recv->ifa_index, ifname)); + printf("prefix length %d\n", ifa_recv->ifa_prefixlen); + while (RTA_OK(rta, ret_len)) { + rta = RTA_NEXT(rta, ifa_len); + + char ip[INET6_ADDRSTRLEN]; + + //parse the message + if (rta->rta_type == IFA_ADDRESS) { + inet_ntop(ifa_recv->ifa_family, RTA_DATA(rta), ip, INET6_ADDRSTRLEN); + printf("IFA_ADDRESS %s\n", ip); + } + + if (rta->rta_type == IFA_LOCAL) { + inet_ntop(ifa_recv->ifa_family, RTA_DATA(rta), ip, INET6_ADDRSTRLEN); + printf("IFA_LOCAL %s\n", ip); + } + + if (rta->rta_type == IFA_BROADCAST) { + inet_ntop(ifa_recv->ifa_family, RTA_DATA(rta), ip, INET6_ADDRSTRLEN); + printf("IFA_BROADCAST %s\n", ip); + } + } +... +} +``` + + + +## Build + +```sh +make +``` + +## Run + +```sh +./netlink_show_ip +``` + +Output looks like + +``` +received message type 20 +interface lo +prefix length 8 +IFA_LOCAL 127.0.0.1 +received message type 20 +interface enp0 +prefix length 22 +IFA_LOCAL 192.168.1.149 +IFA_BROADCAST 192.168.1.255 +``` + +## Final result + + +__netlink_show_ip.c__ +```c +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +#include <net/if.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +#define PAYLOAD_SIZE 4096 /* maximum payload size*/ + +char recv_buf[4096]; + +int main(int argc, char **argv) { + + struct sockaddr_nl src_addr; + struct nlmsghdr *nlh; + struct nlmsghdr *nlh_recv; + struct msghdr msg; + struct msghdr msg_recv; + struct iovec iov; + struct iovec iov_recv; + struct ifaddrmsg *ifa; + struct ifaddrmsg *ifa_recv; + struct rtattr *rta = NULL; + int sock_fd; + int ret; + int ret_len; + uint32_t nl_msg_type; + char ifname[IF_NAMESIZE]; + + sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock_fd < 0) + { + printf("socket creation for NETLINK failed...\n"); + return -1; + } + + memset(&src_addr, 0, sizeof(src_addr)); + src_addr.nl_family = AF_NETLINK; + + bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));//??? + + + // assemble the message according to the netlink protocol + nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(PAYLOAD_SIZE)); + memset(nlh, 0, NLMSG_SPACE(PAYLOAD_SIZE)); + nlh->nlmsg_len = NLMSG_SPACE(PAYLOAD_SIZE); + nlh->nlmsg_type = RTM_GETADDR; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; + + + ifa = (struct ifaddrmsg*)NLMSG_DATA(nlh); + ifa->ifa_family = AF_INET; // we only get ipv4 address here + + // prepare struct msghdr for sending. + memset(&iov, 0, sizeof(iov)); + iov.iov_base = (void *)nlh; + iov.iov_len = nlh->nlmsg_len; + + //struct msghdr msg = { src_addr, sizeof(*src_addr), &iov, 1, NULL, 0, 0 }; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (void *)&src_addr; + msg.msg_namelen = sizeof(src_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + // send netlink message to kernel. + ret = sendmsg(sock_fd, &msg, 0); + if (ret < 0) { + printf("Can't send message to netlink socket\n"); + } + + //prepare to receive reply + memset(&iov_recv, 0, sizeof(iov_recv)); + iov_recv.iov_base = recv_buf; + iov_recv.iov_len = PAYLOAD_SIZE; + + memset(&msg_recv, 0, sizeof(msg_recv)); + msg_recv.msg_name = (void *)&src_addr; + msg_recv.msg_namelen = sizeof(src_addr); + msg_recv.msg_iov = &iov_recv; + msg_recv.msg_iovlen = 1; + + + do { + ret = recvmsg(sock_fd, &msg_recv, 0); + if (ret < 0) { + printf("Can't receive reply message\n"); + break; + } + + if (ret==0) { + printf("received 0 byte reply\n"); + break; + } + + ret_len = ret; + nlh_recv = (struct nlmsghdr*)recv_buf; + while (NLMSG_OK(nlh_recv, (uint32_t)ret_len) && (nlh_recv->nlmsg_type != NLMSG_DONE)) + { + printf("received message type %d\n", nlh_recv->nlmsg_type); + if (nlh_recv->nlmsg_type == NLMSG_ERROR) { + printf("Message error\n"); + break; + } + + if (nlh_recv->nlmsg_type == RTM_NEWADDR) { + ifa_recv = (struct ifaddrmsg*)NLMSG_DATA(nlh_recv); + int ifa_len = IFA_PAYLOAD(nlh_recv); + rta = IFA_RTA(ifa_recv); + printf("interface %s\n", if_indextoname(ifa_recv->ifa_index, ifname)); + printf("prefix length %d\n", ifa_recv->ifa_prefixlen); + while (RTA_OK(rta, ret_len)) { + rta = RTA_NEXT(rta, ifa_len); + + char ip[INET6_ADDRSTRLEN]; + + //parse the message + if (rta->rta_type == IFA_ADDRESS) { + inet_ntop(ifa_recv->ifa_family, RTA_DATA(rta), ip, INET6_ADDRSTRLEN); + printf("IFA_ADDRESS %s\n", ip); + } + + if (rta->rta_type == IFA_LOCAL) { + inet_ntop(ifa_recv->ifa_family, RTA_DATA(rta), ip, INET6_ADDRSTRLEN); + printf("IFA_LOCAL %s\n", ip); + } + + if (rta->rta_type == IFA_BROADCAST) { + inet_ntop(ifa_recv->ifa_family, RTA_DATA(rta), ip, INET6_ADDRSTRLEN); + printf("IFA_BROADCAST %s\n", ip); + } + } + } + nlh_recv = NLMSG_NEXT(nlh_recv, ret); + } + } while ((nl_msg_type != NLMSG_DONE) && (nl_msg_type != NLMSG_ERROR)); + + close(sock_fd); + + return 0; +} +``` + +## Links + +1. [/notes/kernel/netlink_socket.md](/notes/kernel/netlink_socket.md) +1. https://github.com/d0u9/examples/blob/master/C/netlink/ip_show.c +2. https://gist.github.com/luohao-brian/7535fa346f37450137e5db45cd793ace +3. https://github.com/mwarning/netlink-examples/tree/master +4. https://elixir.bootlin.com/linux/v6.6.20/source/include/uapi/linux/rtnetlink.h#L42 +5. https://elixir.bootlin.com/linux/v6.6.20/source/include/uapi/linux/netlink.h#L100 + |