core: misc style fixes to libnl compat code
[NetworkManager.git] / src / nm-netlink-utils.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
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 as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * Copyright (C) 2011 Red Hat, Inc.
19  */
20
21 #include "logging/nm-logging.h"
22 #include "nm-netlink-utils.h"
23 #include "nm-netlink-monitor.h"
24 #include "nm-netlink-compat.h"
25
26 #include <arpa/inet.h>
27 #include <netinet/in.h>
28 #include <netlink/netlink.h>
29 #include <netlink/addr.h>
30 #include <netlink/route/addr.h>
31
32 #include <errno.h>
33
34 typedef struct {
35         int ifindex;
36         int family;
37         void *addr;
38         int addrlen;
39         int prefix;
40         gboolean found;
41 } FindAddrInfo;
42
43 static void
44 find_one_address (struct nl_object *object, void *user_data)
45 {
46         FindAddrInfo *info = user_data;
47         struct rtnl_addr *addr = (struct rtnl_addr *) object;
48         struct nl_addr *local;
49         void *binaddr;
50
51         if (info->found)
52                 return;
53
54         if (rtnl_addr_get_ifindex (addr) != info->ifindex)
55                 return;
56         if (rtnl_addr_get_family (addr) != info->family)
57                 return;
58
59         if (rtnl_addr_get_prefixlen (addr) != info->prefix)
60                 return;
61
62         local = rtnl_addr_get_local (addr);
63         if (nl_addr_get_family (local) != info->family)
64                 return;
65         if (nl_addr_get_len (local) != info->addrlen)
66                 return;
67         binaddr = nl_addr_get_binary_addr (local);
68         if (binaddr) {
69                 if (memcmp (binaddr, info->addr, info->addrlen) == 0)
70                         info->found = TRUE; /* Yay, found it */
71         }
72 }
73
74 /**
75  * nm_netlink_find_address:
76  * @ifindex: interface index
77  * @family: address family, either AF_INET or AF_INET6
78  * @addr: binary address, either struct in_addr* or struct in6_addr*
79  * @prefix: prefix length
80  *
81  * Searches for a matching address on the given interface.
82  *
83  * Returns: %TRUE if the given address was found on the interface, %FALSE if it
84  * was not found or an error occurred.
85  **/
86 gboolean
87 nm_netlink_find_address (int ifindex,
88                          int family,
89                          void *addr,  /* struct in_addr or struct in6_addr */
90                          int prefix)
91 {
92         struct nl_sock *nlh = NULL;
93         struct nl_cache *cache = NULL;
94         FindAddrInfo info;
95
96         g_return_val_if_fail (ifindex > 0, FALSE);
97         g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE);
98         g_return_val_if_fail (addr != NULL, FALSE);
99         g_return_val_if_fail (prefix >= 0, FALSE);
100
101         memset (&info, 0, sizeof (info));
102         info.ifindex = ifindex;
103         info.family = family;
104         info.prefix = prefix;
105         info.addr = addr;
106         if (family == AF_INET)
107                 info.addrlen = sizeof (struct in_addr);
108         else if (family == AF_INET6)
109                 info.addrlen = sizeof (struct in6_addr);
110         else
111                 g_assert_not_reached ();
112
113         nlh = nm_netlink_get_default_handle ();
114         if (nlh) {
115                 rtnl_addr_alloc_cache(nlh, &cache);
116                 if (cache) {
117                         nl_cache_mngt_provide (cache);
118                         nl_cache_foreach (cache, find_one_address, &info);
119                         nl_cache_free (cache);
120                 }
121         }
122         return info.found;
123 }
124
125 struct rtnl_route *
126 nm_netlink_route_new (int ifindex,
127                       int family,
128                       int mss,
129                       ...)
130 {
131         va_list var_args;
132         struct rtnl_route *route;
133         NmNlProp prop = NMNL_PROP_INVALID;
134         int value;
135
136         route = rtnl_route_alloc ();
137         g_return_val_if_fail (route != NULL, NULL);
138
139         if (ifindex >= 0)
140                 rtnl_route_set_oif (route, ifindex);
141         if (family != AF_UNSPEC)
142                 rtnl_route_set_family (route, family);
143         if (mss > 0)
144                 rtnl_route_set_metric (route, RTAX_ADVMSS, mss);
145
146         va_start (var_args, mss);
147         prop = va_arg (var_args, NmNlProp);
148         while (prop != NMNL_PROP_INVALID) {
149                 value = va_arg (var_args, int);
150
151                 if (prop == NMNL_PROP_PROT && value != RTPROT_UNSPEC)
152                         rtnl_route_set_protocol (route, value);
153                 else if (prop == NMNL_PROP_TABLE && value != RT_TABLE_UNSPEC)
154                         rtnl_route_set_table (route, value);
155                 else if (prop == NMNL_PROP_SCOPE && value != RT_SCOPE_NOWHERE)
156                         rtnl_route_set_scope (route, value);
157                 else if (prop == NMNL_PROP_PRIO && value > 0)
158                         rtnl_route_set_priority (route, value);
159
160                 prop = va_arg (var_args, NmNlProp);
161         }
162         va_end (var_args);
163
164         return route;
165 }
166
167 /**
168  * nm_netlink_route_delete:
169  * @route: the route to delete
170  *
171  * Returns: %TRUE if the request was successful, %FALSE if it failed
172  **/
173 gboolean
174 nm_netlink_route_delete (struct rtnl_route *route)
175 {
176         struct nl_sock *nlh;
177         int err = 0;
178
179         g_return_val_if_fail (route != NULL, FALSE);
180
181         nlh = nm_netlink_get_default_handle ();
182         err = rtnl_route_delete (nlh, route, 0);
183
184         return (err && (err != -NLE_RANGE)) ? FALSE : TRUE;
185 }
186
187
188 static void
189 dump_route (struct rtnl_route *route)
190 {
191         char buf6[INET6_ADDRSTRLEN];
192         char buf4[INET_ADDRSTRLEN];
193         struct nl_addr *nl;
194         struct in6_addr *addr6 = NULL;
195         struct in_addr *addr4 = NULL;
196         int prefixlen = 0;
197         const char *sf = "UNSPEC";
198         int family = rtnl_route_get_family (route);
199         guint32 log_level = LOGD_IP4 | LOGD_IP6;
200
201         memset (buf6, 0, sizeof (buf6));
202         memset (buf4, 0, sizeof (buf4));
203         nl = rtnl_route_get_dst (route);
204         if (nl) {
205                 if (nl_addr_get_family (nl) == AF_INET) {
206                         addr4 = nl_addr_get_binary_addr (nl);
207                         if (addr4)
208                                 inet_ntop (AF_INET, addr4, &buf4[0], sizeof (buf4));
209                 } else if (nl_addr_get_family (nl) == AF_INET6) {
210                         addr6 = nl_addr_get_binary_addr (nl);
211                         if (addr6)
212                                 inet_ntop (AF_INET6, addr6, &buf6[0], sizeof (buf6));
213                 }
214                 prefixlen = nl_addr_get_prefixlen (nl);
215         }
216
217         if (family == AF_INET) {
218                 sf = "INET";
219                 log_level = LOGD_IP4;
220         } else if (family == AF_INET6) {
221                 sf = "INET6";
222                 log_level = LOGD_IP6;
223         }
224
225         nm_log_dbg (log_level, "  route idx %d family %s (%d) addr %s/%d",
226                     rtnl_route_get_oif (route),
227                     sf, family,
228                     strlen (buf4) ? buf4 : (strlen (buf6) ? buf6 : "<unknown>"),
229                     prefixlen);
230 }
231
232
233 typedef struct {
234         int ifindex;
235         int family;
236         int scope;
237         gboolean ignore_inet6_ll_mc;
238         const char *iface;
239         NlRouteForeachFunc callback;
240         gpointer user_data;
241         struct rtnl_route *out_route;
242 } ForeachRouteInfo;
243
244 static void
245 foreach_route_cb (struct nl_object *object, void *user_data)
246 {
247         ForeachRouteInfo *info = user_data;
248         struct rtnl_route *route = (struct rtnl_route *) object;
249         struct nl_addr *dst;
250
251         if (info->out_route)
252                 return;
253
254         if (nm_logging_level_enabled (LOGL_DEBUG))
255                 dump_route (route);
256
257         if (   info->ifindex >= 0
258             && rtnl_route_get_oif (route) != info->ifindex)
259                 return;
260
261         if (   info->scope != RT_SCOPE_UNIVERSE
262             && rtnl_route_get_scope (route) != info->scope)
263                 return;
264
265         if (   info->family != AF_UNSPEC
266             && rtnl_route_get_family (route) != info->family)
267                 return;
268
269         dst = rtnl_route_get_dst (route);
270
271         /* Check for IPv6 LL and MC routes that might need to be ignored */
272         if (   (info->family == AF_INET6 || info->family == AF_UNSPEC)
273             && (rtnl_route_get_family (route) == AF_INET6)) {
274                 struct in6_addr *addr = NULL;
275
276                 if (dst)
277                         addr = nl_addr_get_binary_addr (dst);
278                 if (addr) {
279                         if (   IN6_IS_ADDR_LINKLOCAL (addr)
280                             || IN6_IS_ADDR_MC_LINKLOCAL (addr)
281                             || (IN6_IS_ADDR_MULTICAST (addr) && (nl_addr_get_prefixlen (dst) == 8)))
282                                 return;
283                 }
284         }
285
286         info->out_route = info->callback (route, dst, info->iface, info->user_data);
287         if (info->out_route) {
288                 /* Ref the route so it sticks around after the cache is cleared */
289                 rtnl_route_get (info->out_route);
290         }
291 }
292
293 /**
294  * nm_netlink_foreach_route:
295  * @ifindex: the interface index to filter routes for
296  * @family: the address family to filter routes for
297  * @scope: route scope, eg RT_SCOPE_LINK
298  * @ignore_inet6_ll_mc: if %TRUE ignore IPv6 link-local and multi-cast routes
299  * @callback: function called when a route matches the filter
300  * @user_data: data passed to @callback
301  *
302  * Filters each route in the routing table against the given @ifindex and
303  * @family (if given) and calls @callback for each matching route.
304  *
305  * Returns: a route if @callback returned one; the caller must dispose of the
306  * route using rtnl_route_put() when it is no longer required.
307  **/
308 struct rtnl_route *
309 nm_netlink_foreach_route (int ifindex,
310                           int family,
311                           int scope,
312                           gboolean ignore_inet6_ll_mc,
313                           NlRouteForeachFunc callback,
314                           gpointer user_data)
315 {
316         struct nl_cache *cache;
317         ForeachRouteInfo info;
318
319         memset (&info, 0, sizeof (info));
320         info.ifindex = ifindex;
321         info.family = family;
322         info.scope = scope;
323         info.ignore_inet6_ll_mc = ignore_inet6_ll_mc;
324         info.callback = callback;
325         info.user_data = user_data;
326         info.iface = nm_netlink_index_to_iface (ifindex);
327
328         rtnl_route_alloc_cache (nm_netlink_get_default_handle (), family, NL_AUTO_PROVIDE, &cache);
329         g_return_val_if_fail (cache != NULL, NULL);
330         nl_cache_foreach (cache, foreach_route_cb, &info);
331         nl_cache_free (cache);
332         return info.out_route;
333 }
334
335