| /* |
| * Copyright 2012 Daniel Drown |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * netlink_msg.c - send an ifaddrmsg/ifinfomsg/rtmsg via netlink |
| */ |
| |
| #include <errno.h> |
| #include <linux/netlink.h> |
| #include <linux/rtnetlink.h> |
| #include <netinet/in.h> |
| #include <string.h> |
| |
| #include <netlink-private/object-api.h> |
| #include <netlink-private/types.h> |
| #include <netlink/msg.h> |
| #include <netlink/netlink.h> |
| #include <netlink/socket.h> |
| |
| #include "netlink_callbacks.h" |
| #include "netlink_msg.h" |
| |
| /* function: family_size |
| * returns the size of the address structure for the given family, or 0 on error |
| * family - AF_INET or AF_INET6 |
| */ |
| size_t inet_family_size(int family) { |
| if (family == AF_INET) { |
| return sizeof(struct in_addr); |
| } else if (family == AF_INET6) { |
| return sizeof(struct in6_addr); |
| } else { |
| return 0; |
| } |
| } |
| |
| /* function: nlmsg_alloc_generic |
| * allocates a netlink message with the given struct inside of it. returns NULL on failure |
| * type - netlink message type |
| * flags - netlink message flags |
| * payload_struct - pointer to a struct to add to netlink message |
| * payload_len - bytelength of structure |
| */ |
| struct nl_msg *nlmsg_alloc_generic(uint16_t type, uint16_t flags, void *payload_struct, |
| size_t payload_len) { |
| struct nl_msg *msg; |
| |
| msg = nlmsg_alloc(); |
| if (!msg) { |
| return NULL; |
| } |
| |
| if ((sizeof(struct nl_msg) + payload_len) > msg->nm_size) { |
| nlmsg_free(msg); |
| return NULL; |
| } |
| |
| msg->nm_nlh->nlmsg_len = NLMSG_LENGTH(payload_len); |
| msg->nm_nlh->nlmsg_flags = flags; |
| msg->nm_nlh->nlmsg_type = type; |
| |
| memcpy(nlmsg_data(msg->nm_nlh), payload_struct, payload_len); |
| |
| return msg; |
| } |
| |
| /* function: nlmsg_alloc_ifaddr |
| * allocates a netlink message with a struct ifaddrmsg inside of it. returns NULL on failure |
| * type - netlink message type |
| * flags - netlink message flags |
| * ifa - ifaddrmsg to copy into the new netlink message |
| */ |
| struct nl_msg *nlmsg_alloc_ifaddr(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa) { |
| return nlmsg_alloc_generic(type, flags, ifa, sizeof(*ifa)); |
| } |
| |
| /* function: nlmsg_alloc_ifinfo |
| * allocates a netlink message with a struct ifinfomsg inside of it. returns NULL on failure |
| * type - netlink message type |
| * flags - netlink message flags |
| * ifi - ifinfomsg to copy into the new netlink message |
| */ |
| struct nl_msg *nlmsg_alloc_ifinfo(uint16_t type, uint16_t flags, struct ifinfomsg *ifi) { |
| return nlmsg_alloc_generic(type, flags, ifi, sizeof(*ifi)); |
| } |
| |
| /* function: nlmsg_alloc_rtmsg |
| * allocates a netlink message with a struct rtmsg inside of it. returns NULL on failure |
| * type - netlink message type |
| * flags - netlink message flags |
| * rt - rtmsg to copy into the new netlink message |
| */ |
| struct nl_msg *nlmsg_alloc_rtmsg(uint16_t type, uint16_t flags, struct rtmsg *rt) { |
| return nlmsg_alloc_generic(type, flags, rt, sizeof(*rt)); |
| } |
| |
| /* function: netlink_set_kernel_only |
| * sets a socket to receive messages only from the kernel |
| * sock - socket to connect |
| */ |
| int netlink_set_kernel_only(struct nl_sock *nl_sk) { |
| struct sockaddr_nl addr = { AF_NETLINK, 0, 0, 0 }; |
| |
| if (!nl_sk) { |
| return -EFAULT; |
| } |
| |
| int sockfd = nl_socket_get_fd(nl_sk); |
| return connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); |
| } |
| |
| /* function: send_netlink_msg |
| * sends a netlink message, reads a response, and hands the response(s) to the callbacks |
| * msg - netlink message to send |
| * callbacks - callbacks to use on responses |
| */ |
| void send_netlink_msg(struct nl_msg *msg, struct nl_cb *callbacks) { |
| struct nl_sock *nl_sk; |
| |
| nl_sk = nl_socket_alloc(); |
| if (!nl_sk) goto cleanup; |
| |
| if (nl_connect(nl_sk, NETLINK_ROUTE) != 0) goto cleanup; |
| |
| if (nl_send_auto_complete(nl_sk, msg) < 0) goto cleanup; |
| |
| if (netlink_set_kernel_only(nl_sk) < 0) goto cleanup; |
| |
| nl_recvmsgs(nl_sk, callbacks); |
| |
| cleanup: |
| if (nl_sk) nl_socket_free(nl_sk); |
| } |
| |
| /* function: send_ifaddrmsg |
| * sends a netlink/ifaddrmsg message and hands the responses to the callbacks |
| * type - netlink message type |
| * flags - netlink message flags |
| * ifa - ifaddrmsg to send |
| * callbacks - callbacks to use with the responses |
| */ |
| void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct nl_cb *callbacks) { |
| struct nl_msg *msg = NULL; |
| |
| msg = nlmsg_alloc_ifaddr(type, flags, ifa); |
| if (!msg) return; |
| |
| send_netlink_msg(msg, callbacks); |
| |
| nlmsg_free(msg); |
| } |
| |
| /* function: netlink_sendrecv |
| * send a nl_msg and return an int status - only supports OK/ERROR responses |
| * msg - msg to send |
| */ |
| int netlink_sendrecv(struct nl_msg *msg) { |
| struct nl_cb *callbacks = NULL; |
| int retval = -EIO; |
| |
| callbacks = alloc_ack_callbacks(&retval); |
| if (!callbacks) { |
| return -ENOMEM; |
| } |
| |
| send_netlink_msg(msg, callbacks); |
| |
| nl_cb_put(callbacks); |
| |
| return retval; |
| } |