| /* rtnl - rtnetlink utility functions |
| * |
| * (C) 2004 by Astaro AG, written by Harald Welte <hwelte@astaro.com> |
| * |
| * Adapted to nfnetlink by Eric Leblond <eric@inl.fr> |
| * |
| * This software is free software and licensed under GNU GPLv2. |
| * |
| */ |
| |
| /* rtnetlink - routing table netlink interface */ |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <time.h> |
| #include <sys/types.h> |
| #include <sys/uio.h> |
| |
| #include <netinet/in.h> |
| |
| #include <linux/types.h> |
| #include <sys/socket.h> |
| #include <linux/netlink.h> |
| #include <linux/rtnetlink.h> |
| |
| #include "rtnl.h" |
| |
| #define rtnl_log(x, ...) |
| |
| static inline struct rtnl_handler * |
| find_handler(struct rtnl_handle *rtnl_handle, u_int16_t type) |
| { |
| struct rtnl_handler *h; |
| for (h = rtnl_handle->handlers; h; h = h->next) { |
| if (h->nlmsg_type == type) |
| return h; |
| } |
| return NULL; |
| } |
| |
| static int call_handler(struct rtnl_handle *rtnl_handle, |
| u_int16_t type, |
| struct nlmsghdr *hdr) |
| { |
| struct rtnl_handler *h = find_handler(rtnl_handle, type); |
| |
| if (!h) { |
| rtnl_log(LOG_DEBUG, "no registered handler for type %u", type); |
| return 0; |
| } |
| |
| return (h->handlefn)(hdr, h->arg); |
| } |
| |
| /* rtnl_handler_register - register handler for given nlmsg type |
| * @hdlr: handler structure |
| */ |
| int rtnl_handler_register(struct rtnl_handle *rtnl_handle, |
| struct rtnl_handler *hdlr) |
| { |
| rtnl_log(LOG_DEBUG, "registering handler for type %u", |
| hdlr->nlmsg_type); |
| hdlr->next = rtnl_handle->handlers; |
| rtnl_handle->handlers = hdlr; |
| return 1; |
| } |
| |
| /* rtnl_handler_unregister - unregister handler for given nlmst type |
| * @hdlr: handler structure |
| */ |
| int rtnl_handler_unregister(struct rtnl_handle *rtnl_handle, |
| struct rtnl_handler *hdlr) |
| { |
| struct rtnl_handler *h, *prev = NULL; |
| |
| rtnl_log(LOG_DEBUG, "unregistering handler for type %u", |
| hdlr->nlmsg_type); |
| |
| for (h = rtnl_handle->handlers; h; h = h->next) { |
| if (h == hdlr) { |
| if (prev) |
| prev->next = h->next; |
| else |
| rtnl_handle->handlers = h->next; |
| return 1; |
| } |
| prev = h; |
| } |
| return 0; |
| } |
| |
| int rtnl_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) |
| { |
| memset(tb, 0, sizeof(struct rtattr *) * max); |
| |
| while (RTA_OK(rta, len)) { |
| if (rta->rta_type <= max) |
| tb[rta->rta_type] = rta; |
| rta = RTA_NEXT(rta,len); |
| } |
| if (len) |
| return -1; |
| return 0; |
| } |
| |
| /* rtnl_dump_type - ask rtnetlink to dump a specific table |
| * @type: type of table to be dumped |
| */ |
| int rtnl_dump_type(struct rtnl_handle *rtnl_handle, unsigned int type) |
| { |
| struct { |
| struct nlmsghdr nlh; |
| struct rtgenmsg g; |
| } req; |
| struct sockaddr_nl nladdr; |
| |
| memset(&nladdr, 0, sizeof(nladdr)); |
| memset(&req, 0, sizeof(req)); |
| nladdr.nl_family = AF_NETLINK; |
| |
| req.nlh.nlmsg_len = sizeof(req); |
| req.nlh.nlmsg_type = type; |
| req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; |
| req.nlh.nlmsg_pid = 0; |
| req.nlh.nlmsg_seq = rtnl_handle->rtnl_dump = ++(rtnl_handle->rtnl_seq); |
| req.g.rtgen_family = AF_INET; |
| |
| return sendto(rtnl_handle->rtnl_fd, &req, sizeof(req), 0, |
| (struct sockaddr*)&nladdr, sizeof(nladdr)); |
| } |
| |
| /* rtnl_receive - receive netlink packets from rtnetlink socket */ |
| int rtnl_receive(struct rtnl_handle *rtnl_handle) |
| { |
| int status; |
| char buf[8192]; |
| struct sockaddr_nl nladdr; |
| struct iovec iov = { buf, sizeof(buf) }; |
| struct nlmsghdr *h; |
| |
| struct msghdr msg = { |
| .msg_name = &nladdr, |
| .msg_namelen = sizeof(nladdr), |
| .msg_iov = &iov, |
| .msg_iovlen = 1, |
| }; |
| |
| status = recvmsg(rtnl_handle->rtnl_fd, &msg, 0); |
| if (status < 0) { |
| if (errno == EINTR) |
| return 0; |
| rtnl_log(LOG_NOTICE, "OVERRUN on rtnl socket"); |
| return -1; |
| } |
| if (status == 0) { |
| rtnl_log(LOG_ERROR, "EOF on rtnl socket"); |
| return -1; |
| } |
| if (msg.msg_namelen != sizeof(nladdr)) { |
| rtnl_log(LOG_ERROR, "invalid address size"); |
| return -1; |
| } |
| |
| h = (struct nlmsghdr *) buf; |
| while (NLMSG_OK(h, status)) { |
| #if 0 |
| if (h->nlmsg_pid != rtnl_local.nl_pid || |
| h->nlmsg_seq != rtnl_dump) { |
| goto skip; |
| } |
| #endif |
| |
| if (h->nlmsg_type == NLMSG_DONE) { |
| rtnl_log(LOG_NOTICE, "NLMSG_DONE"); |
| return 0; |
| } |
| if (h->nlmsg_type == NLMSG_ERROR) { |
| struct nlmsgerr *err = NLMSG_DATA(h); |
| if (h->nlmsg_len>=NLMSG_LENGTH(sizeof(struct nlmsgerr))) |
| errno = -err->error; |
| rtnl_log(LOG_ERROR, "NLMSG_ERROR, errnp=%d", |
| errno); |
| return -1; |
| } |
| |
| if (call_handler(rtnl_handle, h->nlmsg_type, h) == 0) |
| rtnl_log(LOG_NOTICE, "unhandled nlmsg_type %u", |
| h->nlmsg_type); |
| h = NLMSG_NEXT(h, status); |
| } |
| return 1; |
| } |
| |
| int rtnl_receive_multi(struct rtnl_handle *rtnl_handle) |
| { |
| while (1) { |
| if (rtnl_receive(rtnl_handle) <= 0) |
| break; |
| } |
| return 1; |
| } |
| |
| /* rtnl_open - constructor of rtnetlink module */ |
| struct rtnl_handle *rtnl_open(void) |
| { |
| socklen_t addrlen; |
| struct rtnl_handle *h; |
| |
| h = calloc(1, sizeof(struct rtnl_handle)); |
| if (!h) |
| return NULL; |
| |
| addrlen = sizeof(h->rtnl_local); |
| |
| h->rtnl_local.nl_pid = getpid(); |
| h->rtnl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
| if (h->rtnl_fd < 0) { |
| rtnl_log(LOG_ERROR, "unable to create rtnetlink socket"); |
| goto err; |
| } |
| |
| memset(&h->rtnl_local, 0, sizeof(h->rtnl_local)); |
| h->rtnl_local.nl_family = AF_NETLINK; |
| h->rtnl_local.nl_groups = RTMGRP_LINK; |
| if (bind(h->rtnl_fd, (struct sockaddr *) &h->rtnl_local, addrlen) < 0) { |
| rtnl_log(LOG_ERROR, "unable to bind rtnetlink socket"); |
| goto err_close; |
| } |
| |
| if (getsockname(h->rtnl_fd, |
| (struct sockaddr *) &h->rtnl_local, |
| &addrlen) < 0) { |
| rtnl_log(LOG_ERROR, "cannot gescockname(rtnl_socket)"); |
| goto err_close; |
| } |
| |
| if (addrlen != sizeof(h->rtnl_local)) { |
| rtnl_log(LOG_ERROR, "invalid address size %u", addr_len); |
| goto err_close; |
| } |
| |
| if (h->rtnl_local.nl_family != AF_NETLINK) { |
| rtnl_log(LOG_ERROR, "invalid AF %u", h->rtnl_local.nl_family); |
| goto err_close; |
| } |
| |
| h->rtnl_seq = time(NULL); |
| |
| return h; |
| |
| err_close: |
| close(h->rtnl_fd); |
| err: |
| free(h); |
| return NULL; |
| } |
| |
| /* rtnl_close - destructor of rtnetlink module */ |
| void rtnl_close(struct rtnl_handle *rtnl_handle) |
| { |
| close(rtnl_handle->rtnl_fd); |
| free(rtnl_handle); |
| return; |
| } |