1 /*
2  * Copyright 2012 Daniel Drown
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * netlink_msg.c - send an ifaddrmsg/ifinfomsg/rtmsg via netlink
17  */
18 
19 #include <netinet/in.h>
20 #include <linux/netlink.h>
21 #include <linux/rtnetlink.h>
22 #include <string.h>
23 #include <errno.h>
24 
25 #include <netlink-local.h>
26 #include <netlink-types.h>
27 #include <netlink/socket.h>
28 #include <netlink/netlink.h>
29 #include <netlink/msg.h>
30 
31 #include "netlink_msg.h"
32 #include "netlink_callbacks.h"
33 
34 /* function: family_size
35  * returns the size of the address structure for the given family, or 0 on error
36  * family - AF_INET or AF_INET6
37  */
inet_family_size(int family)38 size_t inet_family_size(int family) {
39   if(family == AF_INET) {
40     return sizeof(struct in_addr);
41   } else if(family == AF_INET6) {
42     return sizeof(struct in6_addr);
43   } else {
44     return 0;
45   }
46 }
47 
48 /* function: nlmsg_alloc_generic
49  * allocates a netlink message with the given struct inside of it. returns NULL on failure
50  * type           - netlink message type
51  * flags          - netlink message flags
52  * payload_struct - pointer to a struct to add to netlink message
53  * payload_len    - bytelength of structure
54  */
nlmsg_alloc_generic(uint16_t type,uint16_t flags,void * payload_struct,size_t payload_len)55 struct nl_msg *nlmsg_alloc_generic(uint16_t type, uint16_t flags, void *payload_struct, size_t payload_len) {
56   struct nl_msg *msg;
57 
58   msg = nlmsg_alloc();
59   if(!msg) {
60     return NULL;
61   }
62 
63   if ((sizeof(struct nl_msg) + payload_len) > msg->nm_size) {
64     nlmsg_free(msg);
65     return NULL;
66   }
67 
68   msg->nm_nlh->nlmsg_len = NLMSG_LENGTH(payload_len);
69   msg->nm_nlh->nlmsg_flags = flags;
70   msg->nm_nlh->nlmsg_type = type;
71 
72   memcpy(nlmsg_data(msg->nm_nlh), payload_struct, payload_len);
73 
74   return msg;
75 }
76 
77 /* function: nlmsg_alloc_ifaddr
78  * allocates a netlink message with a struct ifaddrmsg inside of it. returns NULL on failure
79  * type  - netlink message type
80  * flags - netlink message flags
81  * ifa   - ifaddrmsg to copy into the new netlink message
82  */
nlmsg_alloc_ifaddr(uint16_t type,uint16_t flags,struct ifaddrmsg * ifa)83 struct nl_msg *nlmsg_alloc_ifaddr(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa) {
84   return nlmsg_alloc_generic(type, flags, ifa, sizeof(*ifa));
85 }
86 
87 /* function: nlmsg_alloc_ifinfo
88  * allocates a netlink message with a struct ifinfomsg inside of it. returns NULL on failure
89  * type  - netlink message type
90  * flags - netlink message flags
91  * ifi   - ifinfomsg to copy into the new netlink message
92  */
nlmsg_alloc_ifinfo(uint16_t type,uint16_t flags,struct ifinfomsg * ifi)93 struct nl_msg *nlmsg_alloc_ifinfo(uint16_t type, uint16_t flags, struct ifinfomsg *ifi) {
94   return nlmsg_alloc_generic(type, flags, ifi, sizeof(*ifi));
95 }
96 
97 /* function: nlmsg_alloc_rtmsg
98  * allocates a netlink message with a struct rtmsg inside of it. returns NULL on failure
99  * type  - netlink message type
100  * flags - netlink message flags
101  * rt    - rtmsg to copy into the new netlink message
102  */
nlmsg_alloc_rtmsg(uint16_t type,uint16_t flags,struct rtmsg * rt)103 struct nl_msg *nlmsg_alloc_rtmsg(uint16_t type, uint16_t flags, struct rtmsg *rt) {
104   return nlmsg_alloc_generic(type, flags, rt, sizeof(*rt));
105 }
106 
107 /* function: netlink_set_kernel_only
108  * sets a socket to receive messages only from the kernel
109  * sock - socket to connect
110  */
netlink_set_kernel_only(struct nl_sock * nl_sk)111 int netlink_set_kernel_only(struct nl_sock *nl_sk) {
112   struct sockaddr_nl addr = { AF_NETLINK, 0, 0, 0 };
113 
114   if (!nl_sk) {
115     return -EFAULT;
116   }
117 
118   int sockfd = nl_socket_get_fd(nl_sk);
119   return connect(sockfd, (struct sockaddr *) &addr, sizeof(addr));
120 }
121 
122 /* function: send_netlink_msg
123  * sends a netlink message, reads a response, and hands the response(s) to the callbacks
124  * msg       - netlink message to send
125  * callbacks - callbacks to use on responses
126  */
send_netlink_msg(struct nl_msg * msg,struct nl_cb * callbacks)127 void send_netlink_msg(struct nl_msg *msg, struct nl_cb *callbacks) {
128   struct nl_sock *nl_sk;
129 
130   nl_sk = nl_socket_alloc();
131   if(!nl_sk)
132     goto cleanup;
133 
134   if(nl_connect(nl_sk, NETLINK_ROUTE) != 0)
135     goto cleanup;
136 
137   if(nl_send_auto_complete(nl_sk, msg) < 0)
138     goto cleanup;
139 
140   if(netlink_set_kernel_only(nl_sk) < 0)
141     goto cleanup;
142 
143   nl_recvmsgs(nl_sk, callbacks);
144 
145 cleanup:
146   if(nl_sk)
147     nl_socket_free(nl_sk);
148 }
149 
150 /* function: send_ifaddrmsg
151  * sends a netlink/ifaddrmsg message and hands the responses to the callbacks
152  * type      - netlink message type
153  * flags     - netlink message flags
154  * ifa       - ifaddrmsg to send
155  * callbacks - callbacks to use with the responses
156  */
send_ifaddrmsg(uint16_t type,uint16_t flags,struct ifaddrmsg * ifa,struct nl_cb * callbacks)157 void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct nl_cb *callbacks) {
158   struct nl_msg *msg = NULL;
159 
160   msg = nlmsg_alloc_ifaddr(type, flags, ifa);
161   if(!msg)
162     return;
163 
164   send_netlink_msg(msg, callbacks);
165 
166   nlmsg_free(msg);
167 }
168 
169 /* function: netlink_sendrecv
170  * send a nl_msg and return an int status - only supports OK/ERROR responses
171  * msg - msg to send
172  */
netlink_sendrecv(struct nl_msg * msg)173 int netlink_sendrecv(struct nl_msg *msg) {
174   struct nl_cb *callbacks = NULL;
175   int retval = -EIO;
176 
177   callbacks = alloc_ack_callbacks(&retval);
178   if(!callbacks) {
179     return -ENOMEM;
180   }
181 
182   send_netlink_msg(msg, callbacks);
183 
184   nl_cb_put(callbacks);
185 
186   return retval;
187 }
188