treewide: add netlink file
[project/odhcpd.git] / src / netlink.c
1 /**
2 * Copyright (C) 2017 Hans Dedecker <dedeckeh@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License v2 as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15 #include <errno.h>
16 #include <string.h>
17 #include <syslog.h>
18
19 #include <linux/netlink.h>
20 #include <linux/if_addr.h>
21 #include <linux/neighbour.h>
22 #include <linux/rtnetlink.h>
23
24 #include <netlink/msg.h>
25 #include <netlink/socket.h>
26 #include <netlink/attr.h>
27
28 #include "odhcpd.h"
29
30 static struct nl_sock *rtnl_socket = NULL;
31
32
33 int netlink_init(void)
34 {
35 if (!(rtnl_socket = netlink_create_socket(NETLINK_ROUTE))) {
36 syslog(LOG_ERR, "Unable to open nl socket: %s", strerror(errno));
37 return -1;
38 }
39
40 return 0;
41 }
42
43
44 struct nl_sock *netlink_create_socket(int protocol)
45 {
46 struct nl_sock *nl_sock;
47
48 nl_sock = nl_socket_alloc();
49 if (!nl_sock)
50 goto err;
51
52 if (nl_connect(nl_sock, protocol) < 0)
53 goto err;
54
55 return nl_sock;
56
57 err:
58 if (nl_sock)
59 nl_socket_free(nl_sock);
60
61 return NULL;
62 }
63
64
65 struct addr_info {
66 int ifindex;
67 int af;
68 struct odhcpd_ipaddr **addrs;
69 int pending;
70 ssize_t ret;
71 };
72
73
74 static int cb_valid_handler(struct nl_msg *msg, void *arg)
75 {
76 struct addr_info *ctxt = (struct addr_info *)arg;
77 struct odhcpd_ipaddr *addrs = *(ctxt->addrs);
78 struct nlmsghdr *hdr = nlmsg_hdr(msg);
79 struct ifaddrmsg *ifa;
80 struct nlattr *nla[__IFA_MAX], *nla_addr = NULL;
81
82 if (hdr->nlmsg_type != RTM_NEWADDR)
83 return NL_SKIP;
84
85 ifa = NLMSG_DATA(hdr);
86 if (ifa->ifa_scope != RT_SCOPE_UNIVERSE ||
87 (ctxt->af != ifa->ifa_family) ||
88 (ctxt->ifindex && ifa->ifa_index != (unsigned)ctxt->ifindex))
89 return NL_SKIP;
90
91 nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL);
92
93 switch (ifa->ifa_family) {
94 case AF_INET6:
95 if (nla[IFA_ADDRESS])
96 nla_addr = nla[IFA_ADDRESS];
97 break;
98
99 case AF_INET:
100 if (nla[IFA_LOCAL])
101 nla_addr = nla[IFA_LOCAL];
102 break;
103
104 default:
105 break;
106 }
107 if (!nla_addr)
108 return NL_SKIP;
109
110 addrs = realloc(addrs, sizeof(*addrs)*(ctxt->ret + 1));
111 if (!addrs)
112 return NL_SKIP;
113
114 memset(&addrs[ctxt->ret], 0, sizeof(addrs[ctxt->ret]));
115 addrs[ctxt->ret].prefix = ifa->ifa_prefixlen;
116
117 nla_memcpy(&addrs[ctxt->ret].addr, nla_addr,
118 sizeof(addrs[ctxt->ret].addr));
119
120 if (nla[IFA_BROADCAST])
121 nla_memcpy(&addrs[ctxt->ret].broadcast, nla[IFA_BROADCAST],
122 sizeof(addrs[ctxt->ret].broadcast));
123
124 if (nla[IFA_CACHEINFO]) {
125 struct ifa_cacheinfo *ifc = nla_data(nla[IFA_CACHEINFO]);
126
127 addrs[ctxt->ret].preferred = ifc->ifa_prefered;
128 addrs[ctxt->ret].valid = ifc->ifa_valid;
129 }
130
131 if (ifa->ifa_flags & IFA_F_DEPRECATED)
132 addrs[ctxt->ret].preferred = 0;
133
134 ctxt->ret++;
135 *(ctxt->addrs) = addrs;
136
137 return NL_OK;
138 }
139
140
141 static int cb_finish_handler(_unused struct nl_msg *msg, void *arg)
142 {
143 struct addr_info *ctxt = (struct addr_info *)arg;
144
145 ctxt->pending = 0;
146
147 return NL_STOP;
148 }
149
150
151 static int cb_error_handler(_unused struct sockaddr_nl *nla, struct nlmsgerr *err,
152 void *arg)
153 {
154 struct addr_info *ctxt = (struct addr_info *)arg;
155
156 ctxt->pending = 0;
157 ctxt->ret = err->error;
158
159 return NL_STOP;
160 }
161
162
163 static int prefix_cmp(const void *va, const void *vb)
164 {
165 const struct odhcpd_ipaddr *a = va, *b = vb;
166 int ret = 0;
167
168 if (a->prefix == b->prefix) {
169 ret = (ntohl(a->addr.in.s_addr) < ntohl(b->addr.in.s_addr)) ? 1 :
170 (ntohl(a->addr.in.s_addr) > ntohl(b->addr.in.s_addr)) ? -1 : 0;
171 } else
172 ret = a->prefix < b->prefix ? 1 : -1;
173
174 return ret;
175 }
176
177
178 // compare IPv6 prefixes
179 static int prefix6_cmp(const void *va, const void *vb)
180 {
181 const struct odhcpd_ipaddr *a = va, *b = vb;
182 uint32_t a_pref = IN6_IS_ADDR_ULA(&a->addr.in6) ? 1 : a->preferred;
183 uint32_t b_pref = IN6_IS_ADDR_ULA(&b->addr.in6) ? 1 : b->preferred;
184 return (a_pref < b_pref) ? 1 : (a_pref > b_pref) ? -1 : 0;
185 }
186
187
188 // Detect an IPV6-address currently assigned to the given interface
189 ssize_t netlink_get_interface_addrs(int ifindex, bool v6, struct odhcpd_ipaddr **addrs)
190 {
191 struct nl_msg *msg;
192 struct ifaddrmsg ifa = {
193 .ifa_family = v6? AF_INET6: AF_INET,
194 .ifa_prefixlen = 0,
195 .ifa_flags = 0,
196 .ifa_scope = 0,
197 .ifa_index = ifindex, };
198 struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
199 struct addr_info ctxt = {
200 .ifindex = ifindex,
201 .af = v6? AF_INET6: AF_INET,
202 .addrs = addrs,
203 .ret = 0,
204 .pending = 1,
205 };
206
207 if (!cb) {
208 ctxt.ret = -1;
209 goto out;
210 }
211
212 msg = nlmsg_alloc_simple(RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP);
213
214 if (!msg) {
215 ctxt.ret = - 1;
216 goto out;
217 }
218
219 nlmsg_append(msg, &ifa, sizeof(ifa), 0);
220
221 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_valid_handler, &ctxt);
222 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_finish_handler, &ctxt);
223 nl_cb_err(cb, NL_CB_CUSTOM, cb_error_handler, &ctxt);
224
225 nl_send_auto_complete(rtnl_socket, msg);
226 while (ctxt.pending > 0)
227 nl_recvmsgs(rtnl_socket, cb);
228
229 nlmsg_free(msg);
230
231 if (ctxt.ret <= 0)
232 goto out;
233
234 time_t now = odhcpd_time();
235 struct odhcpd_ipaddr *addr = *addrs;
236
237 qsort(addr, ctxt.ret, sizeof(*addr), v6 ? prefix6_cmp : prefix_cmp);
238
239 for (ssize_t i = 0; i < ctxt.ret; ++i) {
240 if (addr[i].preferred < UINT32_MAX - now)
241 addr[i].preferred += now;
242
243 if (addr[i].valid < UINT32_MAX - now)
244 addr[i].valid += now;
245 }
246
247 out:
248 nl_cb_put(cb);
249
250 return ctxt.ret;
251 }
252
253
254 int netlink_setup_route(const struct in6_addr *addr, const int prefixlen,
255 const struct interface *iface, const struct in6_addr *gw,
256 const uint32_t metric, const bool add)
257 {
258 struct nl_msg *msg;
259 struct rtmsg rtm = {
260 .rtm_family = AF_INET6,
261 .rtm_dst_len = prefixlen,
262 .rtm_src_len = 0,
263 .rtm_table = RT_TABLE_MAIN,
264 .rtm_protocol = (add ? RTPROT_STATIC : RTPROT_UNSPEC),
265 .rtm_scope = (add ? (gw ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK) : RT_SCOPE_NOWHERE),
266 .rtm_type = (add ? RTN_UNICAST : RTN_UNSPEC),
267 };
268 int ret = 0;
269
270 msg = nlmsg_alloc_simple(add ? RTM_NEWROUTE : RTM_DELROUTE,
271 add ? NLM_F_CREATE | NLM_F_REPLACE : 0);
272 if (!msg)
273 return -1;
274
275 nlmsg_append(msg, &rtm, sizeof(rtm), 0);
276
277 nla_put(msg, RTA_DST, sizeof(*addr), addr);
278 nla_put_u32(msg, RTA_OIF, iface->ifindex);
279 nla_put_u32(msg, RTA_PRIORITY, metric);
280
281 if (gw)
282 nla_put(msg, RTA_GATEWAY, sizeof(*gw), gw);
283
284 ret = nl_send_auto_complete(rtnl_socket, msg);
285 nlmsg_free(msg);
286
287 if (ret < 0)
288 return ret;
289
290 return nl_wait_for_ack(rtnl_socket);
291 }
292
293
294 int netlink_setup_proxy_neigh(const struct in6_addr *addr,
295 const struct interface *iface, const bool add)
296 {
297 struct nl_msg *msg;
298 struct ndmsg ndm = {
299 .ndm_family = AF_INET6,
300 .ndm_flags = NTF_PROXY,
301 .ndm_ifindex = iface->ifindex,
302 };
303 int ret = 0, flags = NLM_F_REQUEST;
304
305 if (add)
306 flags |= NLM_F_REPLACE | NLM_F_CREATE;
307
308 msg = nlmsg_alloc_simple(add ? RTM_NEWNEIGH : RTM_DELNEIGH, flags);
309 if (!msg)
310 return -1;
311
312 nlmsg_append(msg, &ndm, sizeof(ndm), 0);
313
314 nla_put(msg, NDA_DST, sizeof(*addr), addr);
315
316 ret = nl_send_auto_complete(rtnl_socket, msg);
317 nlmsg_free(msg);
318
319 if (ret < 0)
320 return ret;
321
322 return nl_wait_for_ack(rtnl_socket);
323 }
324
325
326 int netlink_setup_addr(struct odhcpd_ipaddr *addr,
327 const struct interface *iface, const bool v6,
328 const bool add)
329 {
330 struct nl_msg *msg;
331 struct ifaddrmsg ifa = {
332 .ifa_family = v6 ? AF_INET6 : AF_INET,
333 .ifa_prefixlen = addr->prefix,
334 .ifa_flags = 0,
335 .ifa_scope = 0,
336 .ifa_index = iface->ifindex, };
337 int ret = 0, flags = NLM_F_REQUEST;
338
339 if (add)
340 flags |= NLM_F_REPLACE | NLM_F_CREATE;
341
342 msg = nlmsg_alloc_simple(add ? RTM_NEWADDR : RTM_DELADDR, 0);
343 if (!msg)
344 return -1;
345
346 nlmsg_append(msg, &ifa, sizeof(ifa), flags);
347 nla_put(msg, IFA_LOCAL, v6 ? 16 : 4, &addr->addr);
348 if (v6) {
349 struct ifa_cacheinfo cinfo = { .ifa_prefered = 0xffffffffU,
350 .ifa_valid = 0xffffffffU,
351 .cstamp = 0,
352 .tstamp = 0 };
353 time_t now = odhcpd_time();
354
355 if (addr->preferred) {
356 int64_t preferred = addr->preferred - now;
357 if (preferred < 0)
358 preferred = 0;
359 else if (preferred > UINT32_MAX)
360 preferred = UINT32_MAX;
361
362 cinfo.ifa_prefered = preferred;
363 }
364
365 if (addr->valid) {
366 int64_t valid = addr->valid - now;
367 if (valid <= 0) {
368 nlmsg_free(msg);
369 return -1;
370 }
371 else if (valid > UINT32_MAX)
372 valid = UINT32_MAX;
373
374 cinfo.ifa_valid = valid;
375 }
376
377 nla_put(msg, IFA_CACHEINFO, sizeof(cinfo), &cinfo);
378
379 nla_put_u32(msg, IFA_FLAGS, IFA_F_NOPREFIXROUTE);
380 } else {
381 if (addr->broadcast.s_addr)
382 nla_put_u32(msg, IFA_BROADCAST, addr->broadcast.s_addr);
383 }
384
385 ret = nl_send_auto_complete(rtnl_socket, msg);
386 nlmsg_free(msg);
387
388 if (ret < 0)
389 return ret;
390
391 return nl_wait_for_ack(rtnl_socket);
392 }