diff options
Diffstat (limited to 'md')
| -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   +  | 
