2 * Copyright (C) 2017 Hans Dedecker <dedeckeh@gmail.com>
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.
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.
19 #include <linux/netlink.h>
20 #include <linux/if_addr.h>
21 #include <linux/neighbour.h>
22 #include <linux/rtnetlink.h>
24 #include <netlink/msg.h>
25 #include <netlink/socket.h>
26 #include <netlink/attr.h>
30 static struct nl_sock *rtnl_socket = NULL;
33 int netlink_init(void)
35 if (!(rtnl_socket = netlink_create_socket(NETLINK_ROUTE))) {
36 syslog(LOG_ERR, "Unable to open nl socket: %s", strerror(errno));
44 struct nl_sock *netlink_create_socket(int protocol)
46 struct nl_sock *nl_sock;
48 nl_sock = nl_socket_alloc();
52 if (nl_connect(nl_sock, protocol) < 0)
59 nl_socket_free(nl_sock);
68 struct odhcpd_ipaddr **addrs;
74 static int cb_valid_handler(struct nl_msg *msg, void *arg)
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;
82 if (hdr->nlmsg_type != RTM_NEWADDR)
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))
91 nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL);
93 switch (ifa->ifa_family) {
96 nla_addr = nla[IFA_ADDRESS];
101 nla_addr = nla[IFA_LOCAL];
110 addrs = realloc(addrs, sizeof(*addrs)*(ctxt->ret + 1));
114 memset(&addrs[ctxt->ret], 0, sizeof(addrs[ctxt->ret]));
115 addrs[ctxt->ret].prefix = ifa->ifa_prefixlen;
117 nla_memcpy(&addrs[ctxt->ret].addr, nla_addr,
118 sizeof(addrs[ctxt->ret].addr));
120 if (nla[IFA_BROADCAST])
121 nla_memcpy(&addrs[ctxt->ret].broadcast, nla[IFA_BROADCAST],
122 sizeof(addrs[ctxt->ret].broadcast));
124 if (nla[IFA_CACHEINFO]) {
125 struct ifa_cacheinfo *ifc = nla_data(nla[IFA_CACHEINFO]);
127 addrs[ctxt->ret].preferred = ifc->ifa_prefered;
128 addrs[ctxt->ret].valid = ifc->ifa_valid;
131 if (ifa->ifa_flags & IFA_F_DEPRECATED)
132 addrs[ctxt->ret].preferred = 0;
135 *(ctxt->addrs) = addrs;
141 static int cb_finish_handler(_unused struct nl_msg *msg, void *arg)
143 struct addr_info *ctxt = (struct addr_info *)arg;
151 static int cb_error_handler(_unused struct sockaddr_nl *nla, struct nlmsgerr *err,
154 struct addr_info *ctxt = (struct addr_info *)arg;
157 ctxt->ret = err->error;
163 static int prefix_cmp(const void *va, const void *vb)
165 const struct odhcpd_ipaddr *a = va, *b = vb;
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;
172 ret = a->prefix < b->prefix ? 1 : -1;
178 // compare IPv6 prefixes
179 static int prefix6_cmp(const void *va, const void *vb)
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;
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)
192 struct ifaddrmsg ifa = {
193 .ifa_family = v6? AF_INET6: AF_INET,
197 .ifa_index = ifindex, };
198 struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
199 struct addr_info ctxt = {
201 .af = v6? AF_INET6: AF_INET,
212 msg = nlmsg_alloc_simple(RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP);
219 nlmsg_append(msg, &ifa, sizeof(ifa), 0);
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);
225 nl_send_auto_complete(rtnl_socket, msg);
226 while (ctxt.pending > 0)
227 nl_recvmsgs(rtnl_socket, cb);
234 time_t now = odhcpd_time();
235 struct odhcpd_ipaddr *addr = *addrs;
237 qsort(addr, ctxt.ret, sizeof(*addr), v6 ? prefix6_cmp : prefix_cmp);
239 for (ssize_t i = 0; i < ctxt.ret; ++i) {
240 if (addr[i].preferred < UINT32_MAX - now)
241 addr[i].preferred += now;
243 if (addr[i].valid < UINT32_MAX - now)
244 addr[i].valid += now;
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)
260 .rtm_family = AF_INET6,
261 .rtm_dst_len = prefixlen,
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),
270 msg = nlmsg_alloc_simple(add ? RTM_NEWROUTE : RTM_DELROUTE,
271 add ? NLM_F_CREATE | NLM_F_REPLACE : 0);
275 nlmsg_append(msg, &rtm, sizeof(rtm), 0);
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);
282 nla_put(msg, RTA_GATEWAY, sizeof(*gw), gw);
284 ret = nl_send_auto_complete(rtnl_socket, msg);
290 return nl_wait_for_ack(rtnl_socket);
294 int netlink_setup_proxy_neigh(const struct in6_addr *addr,
295 const struct interface *iface, const bool add)
299 .ndm_family = AF_INET6,
300 .ndm_flags = NTF_PROXY,
301 .ndm_ifindex = iface->ifindex,
303 int ret = 0, flags = NLM_F_REQUEST;
306 flags |= NLM_F_REPLACE | NLM_F_CREATE;
308 msg = nlmsg_alloc_simple(add ? RTM_NEWNEIGH : RTM_DELNEIGH, flags);
312 nlmsg_append(msg, &ndm, sizeof(ndm), 0);
314 nla_put(msg, NDA_DST, sizeof(*addr), addr);
316 ret = nl_send_auto_complete(rtnl_socket, msg);
322 return nl_wait_for_ack(rtnl_socket);
326 int netlink_setup_addr(struct odhcpd_ipaddr *addr,
327 const struct interface *iface, const bool v6,
331 struct ifaddrmsg ifa = {
332 .ifa_family = v6 ? AF_INET6 : AF_INET,
333 .ifa_prefixlen = addr->prefix,
336 .ifa_index = iface->ifindex, };
337 int ret = 0, flags = NLM_F_REQUEST;
340 flags |= NLM_F_REPLACE | NLM_F_CREATE;
342 msg = nlmsg_alloc_simple(add ? RTM_NEWADDR : RTM_DELADDR, 0);
346 nlmsg_append(msg, &ifa, sizeof(ifa), flags);
347 nla_put(msg, IFA_LOCAL, v6 ? 16 : 4, &addr->addr);
349 struct ifa_cacheinfo cinfo = { .ifa_prefered = 0xffffffffU,
350 .ifa_valid = 0xffffffffU,
353 time_t now = odhcpd_time();
355 if (addr->preferred) {
356 int64_t preferred = addr->preferred - now;
359 else if (preferred > UINT32_MAX)
360 preferred = UINT32_MAX;
362 cinfo.ifa_prefered = preferred;
366 int64_t valid = addr->valid - now;
371 else if (valid > UINT32_MAX)
374 cinfo.ifa_valid = valid;
377 nla_put(msg, IFA_CACHEINFO, sizeof(cinfo), &cinfo);
379 nla_put_u32(msg, IFA_FLAGS, IFA_F_NOPREFIXROUTE);
381 if (addr->broadcast.s_addr)
382 nla_put_u32(msg, IFA_BROADCAST, addr->broadcast.s_addr);
385 ret = nl_send_auto_complete(rtnl_socket, msg);
391 return nl_wait_for_ack(rtnl_socket);