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 }