core: misc style fixes to libnl compat code
[NetworkManager.git] / src / nm-system.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) 2004 - 2010 Red Hat, Inc.
19  * Copyright (C) 2005 - 2008 Novell, Inc.
20  * Copyright (C) 1996 - 1997 Yoichi Hariguchi <yoichi@fore.com>
21  * Copyright (C) January, 1998 Sergei Viznyuk <sv@phystech.com>
22  */
23
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <sys/ioctl.h>
27 #include <sys/stat.h>
28 #include <netinet/in.h>
29 #include <net/route.h>
30 #include <arpa/nameser.h>
31 #include <arpa/inet.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <syslog.h>
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <resolv.h>
40 #include <netdb.h>
41 #include <glib.h>
42 #include <ctype.h>
43 #include <net/if.h>
44
45 #include "nm-system.h"
46 #include "nm-device.h"
47 #include "NetworkManagerUtils.h"
48 #include "nm-utils.h"
49 #include "nm-logging.h"
50 #include "nm-netlink-monitor.h"
51 #include "nm-netlink-utils.h"
52 #include "nm-netlink-compat.h"
53
54 #include <netlink/route/addr.h>
55 #include <netlink/route/route.h>
56 #include <netlink/netlink.h>
57 #include <netlink/utils.h>
58 #include <netlink/route/link.h>
59
60 static void nm_system_device_set_priority (int ifindex,
61                                            NMIP4Config *config,
62                                            int priority);
63
64 static gboolean
65 ip4_dest_in_same_subnet (NMIP4Config *config, guint32 dest, guint32 dest_prefix)
66 {
67         int num;
68         int i;
69
70         num = nm_ip4_config_get_num_addresses (config);
71         for (i = 0; i < num; i++) {
72                 NMIP4Address *addr = nm_ip4_config_get_address (config, i);
73                 guint32 prefix = nm_ip4_address_get_prefix (addr);
74                 guint32 address = nm_ip4_address_get_address (addr);
75
76                 if (prefix <= dest_prefix) {
77                         guint32 masked_addr = ntohl(address) >> (32 - prefix);
78                         guint32 masked_dest = ntohl(dest) >> (32 - prefix);
79
80                         if (masked_addr == masked_dest)
81                                 return TRUE;
82                 }
83         }
84
85         return FALSE;
86 }
87
88 static struct rtnl_route *
89 nm_system_device_set_ip4_route (int ifindex, 
90                                 guint32 ip4_dest,
91                                 guint32 ip4_prefix,
92                                 guint32 ip4_gateway,
93                                 guint32 metric,
94                                 int mss)
95 {
96         struct nl_sock *nlh;
97         struct rtnl_route *route;
98         struct nl_addr *dest_addr;
99         struct nl_addr *gw_addr = NULL;
100         int err;
101         const char *iface;
102
103         g_return_val_if_fail (ifindex > 0, NULL);
104
105         nlh = nm_netlink_get_default_handle ();
106         g_return_val_if_fail (nlh != NULL, NULL);
107
108         iface = nm_netlink_index_to_iface (ifindex);
109         g_return_val_if_fail (iface != NULL, NULL);
110
111         route = nm_netlink_route_new (ifindex, AF_INET, mss,
112                                       NMNL_PROP_PRIO, metric,
113                                       NULL);
114         g_return_val_if_fail (route != NULL, NULL);
115
116         /* Destination */
117         dest_addr = nl_addr_build (AF_INET, &ip4_dest, sizeof (ip4_dest));
118         g_return_val_if_fail (dest_addr != NULL, NULL);
119         nl_addr_set_prefixlen (dest_addr, (int) ip4_prefix);
120
121         rtnl_route_set_dst (route, dest_addr);
122         nl_addr_put (dest_addr);
123
124         /* Gateway */
125         if (ip4_gateway) {
126                 gw_addr = nl_addr_build (AF_INET, &ip4_gateway, sizeof (ip4_gateway));
127                 if (gw_addr) {
128                         rtnl_route_set_gateway (route, gw_addr);
129                         rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE);
130                 } else {
131                         nm_log_err (LOGD_DEVICE | LOGD_IP4, "Invalid gateway 0x%X", ip4_gateway);
132                         rtnl_route_put (route);
133                         return NULL;
134                 }
135         }
136
137         /* Add the route */
138         err = rtnl_route_add (nlh, route, 0);
139         if (err == -ESRCH && ip4_gateway) {
140                 /* Gateway might be over a bridge; try adding a route to gateway first */
141                 struct rtnl_route *route2;
142
143                 route2 = nm_netlink_route_new (ifindex, AF_INET, mss, NULL);
144                 if (route2) {
145                         /* Add route to gateway over bridge */
146                         rtnl_route_set_dst (route2, gw_addr);
147                         err = rtnl_route_add (nlh, route2, 0);
148                         if (!err) {
149                                 /* Try adding the route again */
150                                 err = rtnl_route_add (nlh, route, 0);
151                                 if (err)
152                                         nm_netlink_route_delete (route2);
153                         }
154                         rtnl_route_put (route2);
155                 }
156         }
157
158         if (gw_addr)
159                 nl_addr_put (gw_addr);
160
161         if (err) {
162                 nm_log_err (LOGD_DEVICE | LOGD_IP4,
163                             "(%s): failed to set IPv4 route: %s",
164                             iface, nl_geterror (err));
165                 rtnl_route_put (route);
166                 route = NULL;
167         }
168
169         return route;
170 }
171
172 static gboolean
173 sync_addresses (int ifindex,
174                 int family,
175                                 struct rtnl_addr **addrs,
176                                 int num_addrs)
177 {
178         struct nl_sock *nlh;
179         struct nl_cache *addr_cache;
180         struct rtnl_addr *filter_addr, *match_addr;
181         struct nl_object *match;
182         struct nl_addr *nladdr;
183         int i, err;
184         guint32 log_domain = (family == AF_INET) ? LOGD_IP4 : LOGD_IP6;
185         char buf[INET6_ADDRSTRLEN + 1];
186         const char *iface;
187
188         iface = nm_netlink_index_to_iface (ifindex);
189         g_return_val_if_fail (iface != NULL, FALSE);
190
191         log_domain |= LOGD_DEVICE;
192
193         nlh = nm_netlink_get_default_handle ();
194         if (!nlh)
195                 return FALSE;
196
197         err = rtnl_addr_alloc_cache (nlh, &addr_cache);
198         if (err < 0)
199                 return FALSE;
200
201         filter_addr = rtnl_addr_alloc ();
202         if (!filter_addr) {
203                 nl_cache_free (addr_cache);
204                 return FALSE;
205         }
206         rtnl_addr_set_ifindex (filter_addr, ifindex);
207         if (family)
208                 rtnl_addr_set_family (filter_addr, family);
209
210         nm_log_dbg (log_domain, "(%s): syncing addresses (family %d)", iface, family);
211
212         /* Walk through the cache, comparing the addresses already on
213          * the interface to the addresses in addrs.
214          */
215         for (match = nl_cache_get_first (addr_cache); match; match = nl_cache_get_next (match)) {
216                 gboolean buf_valid = FALSE;
217                 match_addr = (struct rtnl_addr *) match;
218
219                 /* Skip addresses not on our interface */
220                 if (!nl_object_match_filter (match, (struct nl_object *) filter_addr))
221                         continue;
222
223                 if (addrs) {
224                         for (i = 0; i < num_addrs; i++) {
225                                 if (addrs[i] && nl_object_identical (match, (struct nl_object *) addrs[i]))
226                                         break;
227                         }
228
229                         if (addrs[i]) {
230                                 /* match == addrs[i], so remove it from addrs so we don't
231                                  * try to add it to the interface again below.
232                                  */
233                                 rtnl_addr_put (addrs[i]);
234                                 addrs[i] = NULL;
235                                 continue;
236                         }
237                 }
238
239                 nladdr = rtnl_addr_get_local (match_addr);
240
241                 /* Don't delete IPv6 link-local addresses; they don't belong to NM */
242                 if (rtnl_addr_get_family (match_addr) == AF_INET6) {
243                         struct in6_addr *tmp;
244
245                         if (rtnl_addr_get_scope (match_addr) == RT_SCOPE_LINK) {
246                                 nm_log_dbg (log_domain, "(%s): ignoring IPv6 link-local address", iface);
247                                 continue;
248                         }
249
250                         tmp = nl_addr_get_binary_addr (nladdr);
251                         if (inet_ntop (AF_INET6, tmp, buf, sizeof (buf)))
252                                 buf_valid = TRUE;
253                 } else if (rtnl_addr_get_family (match_addr) == AF_INET) {
254                         struct in_addr *tmp;
255
256                         tmp = nl_addr_get_binary_addr (nladdr);
257                         if (inet_ntop (AF_INET, tmp, buf, sizeof (buf)))
258                                 buf_valid = TRUE;
259                 }
260
261                 if (buf_valid) {
262                         nm_log_dbg (log_domain, "(%s): removing address '%s/%d'",
263                                     iface, buf, nl_addr_get_prefixlen (nladdr));
264                 }
265
266                 /* Otherwise, match_addr should be removed from the interface. */
267                 err = rtnl_addr_delete (nlh, match_addr, 0);
268                 if (err < 0) {
269                         nm_log_err (log_domain, "(%s): error %d returned from rtnl_addr_delete(): %s",
270                                                 iface, err, nl_geterror (err));
271                 }
272         }
273
274         rtnl_addr_put (filter_addr);
275         nl_cache_free (addr_cache);
276
277         /* Now add the remaining new addresses */
278         for (i = 0; i < num_addrs; i++) {
279                 struct in6_addr *in6tmp;
280                 struct in_addr *in4tmp;
281                 gboolean buf_valid = FALSE;
282
283                 if (!addrs[i])
284                         continue;
285
286                 nladdr = rtnl_addr_get_local (addrs[i]);
287                 if (rtnl_addr_get_family (addrs[i]) == AF_INET6) {
288                         in6tmp = nl_addr_get_binary_addr (nladdr);
289                         if (inet_ntop (AF_INET6, in6tmp, buf, sizeof (buf)))
290                                 buf_valid = TRUE;
291                 } else if (rtnl_addr_get_family (addrs[i]) == AF_INET) {
292                         in4tmp = nl_addr_get_binary_addr (nladdr);
293                         if (inet_ntop (AF_INET, in4tmp, buf, sizeof (buf)))
294                                 buf_valid = TRUE;
295                 }
296
297                 if (buf_valid) {
298                         nm_log_dbg (log_domain, "(%s): adding address '%s/%d'",
299                                     iface, buf, nl_addr_get_prefixlen (nladdr));
300                 }
301
302                 err = rtnl_addr_add (nlh, addrs[i], 0);
303                 if (err < 0 && (err != -NLE_EXIST)) {
304                         nm_log_err (log_domain,
305                                     "(%s): error %d returned from rtnl_addr_add():\n%s",
306                                     iface, err, nl_geterror (err));
307                 }
308
309                 rtnl_addr_put (addrs[i]);
310         }
311         g_free (addrs);
312
313         return TRUE;
314 }
315
316 static gboolean
317 add_ip4_addresses (NMIP4Config *config, const char *iface)
318 {
319         int num_addrs, i, iface_idx;
320         guint32 flags = 0;
321         gboolean did_gw = FALSE;
322         struct rtnl_addr **addrs;
323
324         iface_idx = nm_netlink_iface_to_index (iface);
325
326         num_addrs = nm_ip4_config_get_num_addresses (config);
327         addrs = g_new0 (struct rtnl_addr *, num_addrs + 1);
328
329         for (i = 0; i < num_addrs; i++) {
330                 NMIP4Address *addr;
331
332                 addr = nm_ip4_config_get_address (config, i);
333                 g_assert (addr);
334
335                 flags = NM_RTNL_ADDR_DEFAULT;
336                 if (nm_ip4_address_get_gateway (addr) && !did_gw) {
337                         if (nm_ip4_config_get_ptp_address (config))
338                                 flags |= NM_RTNL_ADDR_PTP_ADDR;
339                         did_gw = TRUE;
340                 }
341
342                 addrs[i] = nm_ip4_config_to_rtnl_addr (config, i, flags);
343                 if (!addrs[i]) {
344                         nm_log_warn (LOGD_DEVICE | LOGD_IP4,
345                                      "(%s): couldn't create rtnl address!",
346                                      iface);
347                         continue;
348                 }
349                 rtnl_addr_set_ifindex (addrs[i], iface_idx);
350         }
351
352         return sync_addresses (iface_idx, AF_INET, addrs, num_addrs);
353 }
354
355 struct rtnl_route *
356 nm_system_add_ip4_vpn_gateway_route (NMDevice *parent_device, NMIP4Config *vpn_config)
357 {
358         NMIP4Config *parent_config;
359         guint32 parent_gw = 0, parent_prefix = 0, vpn_gw = 0, i;
360         NMIP4Address *tmp;
361         struct rtnl_route *route = NULL;
362
363         g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL);
364
365         /* Set up a route to the VPN gateway's public IP address through the default
366          * network device if the VPN gateway is on a different subnet.
367          */
368
369         parent_config = nm_device_get_ip4_config (parent_device);
370         g_return_val_if_fail (parent_config != NULL, NULL);
371
372         for (i = 0; i < nm_ip4_config_get_num_addresses (parent_config); i++) {
373                 tmp = nm_ip4_config_get_address (parent_config, i);
374                 if (nm_ip4_address_get_gateway (tmp)) {
375                         parent_gw = nm_ip4_address_get_gateway (tmp);
376                         parent_prefix = nm_ip4_address_get_prefix (tmp);
377                         break;
378                 }
379         }
380
381         for (i = 0; i < nm_ip4_config_get_num_addresses (vpn_config); i++) {
382                 tmp = nm_ip4_config_get_address (vpn_config, i);
383                 if (nm_ip4_address_get_gateway (tmp)) {
384                         vpn_gw = nm_ip4_address_get_gateway (tmp);
385                         break;
386                 }
387         }
388
389         if (!parent_gw || !vpn_gw)
390                 return NULL;
391
392         /* If the VPN gateway is in the same subnet as one of the parent device's
393          * IP addresses, don't add the host route to it, but a route through the
394          * parent device.
395          */
396         if (ip4_dest_in_same_subnet (parent_config, vpn_gw, parent_prefix)) {
397                 route = nm_system_device_set_ip4_route (nm_device_get_ip_ifindex (parent_device),
398                                                         vpn_gw, 32, 0, 0, nm_ip4_config_get_mss (parent_config));
399         } else {
400                 route = nm_system_device_set_ip4_route (nm_device_get_ip_ifindex (parent_device),
401                                                         vpn_gw, 32, parent_gw, 0, nm_ip4_config_get_mss (parent_config));
402         }
403
404         return route;
405 }
406
407 /*
408  * nm_system_apply_ip4_config
409  *
410  * Set IPv4 configuration of the device from an NMIP4Config object.
411  *
412  */
413 gboolean
414 nm_system_apply_ip4_config (int ifindex,
415                             NMIP4Config *config,
416                             int priority,
417                             NMIP4ConfigCompareFlags flags)
418 {
419         const char *iface;
420         int i;
421
422         g_return_val_if_fail (ifindex > 0, FALSE);
423         g_return_val_if_fail (config != NULL, FALSE);
424
425         iface = nm_netlink_index_to_iface (ifindex);
426         g_return_val_if_fail (iface != NULL, FALSE);
427
428         if (flags & NM_IP4_COMPARE_FLAG_ADDRESSES) {
429                 if (!add_ip4_addresses (config, iface))
430                         return FALSE;
431                 sleep (1);
432         }
433
434         if (flags & NM_IP4_COMPARE_FLAG_ROUTES) {
435                 for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) {
436                         NMIP4Route *route = nm_ip4_config_get_route (config, i);
437                         struct rtnl_route *tmp;
438
439                         /* Don't add the route if it's more specific than one of the subnets
440                          * the device already has an IP address on.
441                          */
442                         if (ip4_dest_in_same_subnet (config,
443                                                      nm_ip4_route_get_dest (route),
444                                                      nm_ip4_route_get_prefix (route)))
445                                 continue;
446
447                         /* Don't add the route if it doesn't have a gateway and the connection
448                          * is never supposed to be the default connection.
449                          */
450                         if (   nm_ip4_config_get_never_default (config)
451                             && nm_ip4_route_get_dest (route) == 0)
452                                 continue;
453
454                         tmp = nm_system_device_set_ip4_route (ifindex,
455                                                               nm_ip4_route_get_dest (route),
456                                                               nm_ip4_route_get_prefix (route),
457                                                               nm_ip4_route_get_next_hop (route),
458                                                               nm_ip4_route_get_metric (route),
459                                                               nm_ip4_config_get_mss (config));
460                         rtnl_route_put (tmp);
461                 }
462         }
463
464         if (flags & NM_IP4_COMPARE_FLAG_MTU) {
465                 if (nm_ip4_config_get_mtu (config))
466                         nm_system_iface_set_mtu (ifindex, nm_ip4_config_get_mtu (config));
467         }
468
469         if (priority > 0)
470                 nm_system_device_set_priority (ifindex, config, priority);
471
472         return TRUE;
473 }
474
475 int
476 nm_system_set_ip6_route (int ifindex,
477                          const struct in6_addr *ip6_dest,
478                          guint32 ip6_prefix,
479                          const struct in6_addr *ip6_gateway,
480                          guint32 metric,
481                          int mss,
482                          int protocol,
483                          int table,
484                          struct rtnl_route **out_route)
485 {
486         struct nl_sock *nlh;
487         struct rtnl_route *route;
488         struct nl_addr *dest_addr;
489         struct nl_addr *gw_addr = NULL;
490         int err = 0;
491
492         g_return_val_if_fail (ifindex >= 0, -1);
493
494         nlh = nm_netlink_get_default_handle ();
495         g_return_val_if_fail (nlh != NULL, -1);
496
497         route = nm_netlink_route_new (ifindex, AF_INET6, mss,
498                                       NMNL_PROP_PROT, protocol,
499                                       NMNL_PROP_PRIO, metric,
500                                       NMNL_PROP_TABLE, table,
501                                       NULL);
502         g_return_val_if_fail (route != NULL, -1);
503
504         /* Destination */
505         dest_addr = nl_addr_build (AF_INET6, (struct in6_addr *) ip6_dest, sizeof (*ip6_dest));
506         g_return_val_if_fail (dest_addr != NULL, -1);
507         nl_addr_set_prefixlen (dest_addr, (int) ip6_prefix);
508
509         rtnl_route_set_dst (route, dest_addr);
510         nl_addr_put (dest_addr);
511
512         /* Gateway */
513         if (ip6_gateway && !IN6_IS_ADDR_UNSPECIFIED (ip6_gateway)) {
514                 gw_addr = nl_addr_build (AF_INET6, (struct in6_addr *) ip6_gateway, sizeof (*ip6_gateway));
515                 if (gw_addr) {
516                         rtnl_route_set_gateway (route, gw_addr);
517                         rtnl_route_set_scope (route, RT_SCOPE_UNIVERSE);
518                 } else {
519                         nm_log_warn (LOGD_DEVICE | LOGD_IP6, "Invalid gateway");
520                         rtnl_route_put (route);
521                         return -1;
522                 }
523         }
524
525         /* Add the route */
526         err = rtnl_route_add (nlh, route, 0);
527         if (err == -ESRCH && ip6_gateway) {
528                 /* Gateway might be over a bridge; try adding a route to gateway first */
529                 struct rtnl_route *route2;
530
531                 route2 = nm_netlink_route_new (ifindex, AF_INET6, mss, NULL);
532                 if (route2) {
533                         /* Add route to gateway over bridge */
534                         rtnl_route_set_dst (route2, gw_addr);
535                         err = rtnl_route_add (nlh, route2, 0);
536                         if (!err) {
537                                 /* Try adding the route again */
538                                 err = rtnl_route_add (nlh, route, 0);
539                                 if (err)
540                                         nm_netlink_route_delete (route2);
541                         }
542                         rtnl_route_put (route2);
543                 }
544         }
545
546         if (gw_addr)
547                 nl_addr_put (gw_addr);
548
549         if (out_route)
550                 *out_route = route;
551         else
552                 rtnl_route_put (route);
553
554         return err;
555 }
556
557 static gboolean
558 add_ip6_addresses (NMIP6Config *config, const char *iface)
559 {
560         int num_addrs, i, iface_idx;
561         struct rtnl_addr **addrs;
562
563         iface_idx = nm_netlink_iface_to_index (iface);
564
565         num_addrs = nm_ip6_config_get_num_addresses (config);
566         addrs = g_new0 (struct rtnl_addr *, num_addrs + 1);
567
568         for (i = 0; i < num_addrs; i++) {
569                 NMIP6Address *addr;
570
571                 addr = nm_ip6_config_get_address (config, i);
572                 g_assert (addr);
573
574                 addrs[i] = nm_ip6_config_to_rtnl_addr (config, i, NM_RTNL_ADDR_DEFAULT);
575                 if (!addrs[i]) {
576                         nm_log_warn (LOGD_DEVICE | LOGD_IP6,
577                                      "(%s): couldn't create rtnl address!",
578                                      iface);
579                         continue;
580                 }
581                 rtnl_addr_set_ifindex (addrs[i], iface_idx);
582         }
583
584         return sync_addresses (iface_idx, AF_INET6, addrs, num_addrs);
585 }
586
587 /*
588  * nm_system_apply_ip6_config
589  *
590  * Set IPv6 configuration of the device from an NMIP6Config object.
591  *
592  */
593 gboolean
594 nm_system_apply_ip6_config (int ifindex,
595                             NMIP6Config *config,
596                             int priority,
597                             NMIP6ConfigCompareFlags flags)
598 {
599         const char *iface;
600         int i;
601
602         g_return_val_if_fail (ifindex > 0, FALSE);
603         g_return_val_if_fail (config != NULL, FALSE);
604
605         iface = nm_netlink_index_to_iface (ifindex);
606         g_return_val_if_fail (iface != NULL, FALSE);
607
608         if (flags & NM_IP6_COMPARE_FLAG_ADDRESSES) {
609                 if (!add_ip6_addresses (config, iface))
610                         return FALSE;
611                 sleep (1); // FIXME?
612         }
613
614         if (flags & NM_IP6_COMPARE_FLAG_ROUTES) {
615                 for (i = 0; i < nm_ip6_config_get_num_routes (config); i++) {
616                         NMIP6Route *route = nm_ip6_config_get_route (config, i);
617                         int err;
618
619                         /* Don't add the route if it doesn't have a gateway and the connection
620                          * is never supposed to be the default connection.
621                          */
622                         if (   nm_ip6_config_get_never_default (config)
623                             && IN6_IS_ADDR_UNSPECIFIED (nm_ip6_route_get_dest (route)))
624                                 continue;
625
626                         err = nm_system_set_ip6_route (ifindex,
627                                                        nm_ip6_route_get_dest (route),
628                                                        nm_ip6_route_get_prefix (route),
629                                                        nm_ip6_route_get_next_hop (route),
630                                                        nm_ip6_route_get_metric (route),
631                                                        nm_ip6_config_get_mss (config),
632                                                        RTPROT_UNSPEC,
633                                                        RT_TABLE_UNSPEC,
634                                                        NULL);
635                         if (err) {
636                                 nm_log_err (LOGD_DEVICE | LOGD_IP6,
637                                             "(%s): failed to set IPv6 route: %s",
638                                             iface, nl_geterror (err));
639                         }
640                 }
641         }
642
643 // FIXME
644 //      if (priority > 0)
645 //              nm_system_device_set_priority (iface, config, priority);
646
647         return TRUE;
648 }
649
650 /**
651  * nm_system_iface_set_up:
652  * @ifindex: interface index
653  * @up: %TRUE to bring interface up, or %FALSE to take it down
654  * @no_firmware: on return, %TRUE if the operation may have failed due to
655  * missing firmware
656  *
657  * Bring the interface up or take it down.
658  *
659  * Returns: %TRUE on success, %FALSE on failure
660  **/
661 gboolean
662 nm_system_iface_set_up (int ifindex,
663                         gboolean up,
664                         gboolean *no_firmware)
665 {
666         struct rtnl_link *request = NULL, *old = NULL;
667         struct nl_sock *nlh;
668         gboolean success = FALSE;
669         int err;
670
671         g_return_val_if_fail (ifindex > 0, FALSE);
672         if (no_firmware)
673                 g_return_val_if_fail (*no_firmware == FALSE, FALSE);
674
675         if (!(request = rtnl_link_alloc ()))
676                 return FALSE;
677
678         if (up)
679                 rtnl_link_set_flags (request, IFF_UP);
680         else
681                 rtnl_link_unset_flags (request, IFF_UP);
682
683         old = nm_netlink_index_to_rtnl_link (ifindex);
684         if (old) {
685                 nlh = nm_netlink_get_default_handle ();
686                 if (nlh) {
687                         err = rtnl_link_change (nlh, old, request, 0);
688                         if (err == 0) {
689                                 success = TRUE;
690                         } else {
691                                 if ((err == -NLE_OBJ_NOTFOUND) && no_firmware && up)
692                                         *no_firmware = TRUE;
693                         }
694                 }
695         }
696
697         rtnl_link_put (old);
698         rtnl_link_put (request);
699         return success;
700 }
701
702 /**
703  * nm_system_iface_is_up:
704  * @ifindex: interface index
705  *
706  * Returns: %TRUE if the interface is up, %FALSE if it was down or the check
707  * failed.
708  **/
709 gboolean
710 nm_system_iface_is_up (int ifindex)
711 {
712         const char *iface;
713         struct rtnl_link *l;
714         guint32 flags;
715
716         g_return_val_if_fail (ifindex > 0, FALSE);
717
718         iface = nm_netlink_index_to_iface (ifindex);
719         g_return_val_if_fail (iface != NULL, FALSE);
720
721         l = nm_netlink_index_to_rtnl_link (ifindex);
722         if (l == NULL) {
723                 nm_log_err (LOGD_HW, "(%s): failed to get interface link object", iface);
724                 return FALSE;
725         }
726
727         flags = rtnl_link_get_flags (l);
728         rtnl_link_put (l);
729
730         return flags & IFF_UP;
731 }
732
733 /**
734  * nm_system_iface_set_mtu:
735  * @ifindex: interface index
736  * @mtu: the new MTU
737  *
738  * Returns: %TRUE if the request was successful, %FALSE if it failed
739  **/
740 gboolean
741 nm_system_iface_set_mtu (int ifindex, guint32 mtu)
742 {
743         struct rtnl_link *old;
744         struct rtnl_link *new;
745         gboolean success = FALSE;
746         struct nl_sock *nlh;
747         const char *iface;
748         int err;
749
750         g_return_val_if_fail (ifindex > 0, FALSE);
751         g_return_val_if_fail (mtu > 0, FALSE);
752
753         iface = nm_netlink_index_to_iface (ifindex);
754         g_return_val_if_fail (iface != NULL, FALSE);
755
756         new = rtnl_link_alloc ();
757         if (!new)
758                 return FALSE;
759
760         old = nm_netlink_index_to_rtnl_link (ifindex);
761         if (old) {
762                 rtnl_link_set_mtu (new, mtu);
763                 nlh = nm_netlink_get_default_handle ();
764                 if (nlh) {
765                         err = rtnl_link_change (nlh, old, new, 0);
766                         if (err == 0)
767                                 success = TRUE;
768                         else
769                                 nm_log_warn (LOGD_HW, "(%s): failed to change interface MTU", iface);
770                 }
771                 rtnl_link_put (old);
772         }
773         rtnl_link_put (new);
774
775         return success;
776 }
777
778 /**
779  * nm_system_iface_set_mac:
780  * @ifindex: interface index
781  * @mac: new MAC address
782  *
783  * Attempts to change the interface's MAC address to the requested value,
784  * ie MAC spoofing or cloning.
785  *
786  * Returns: %TRUE if the request succeeded, %FALSE if it failed.
787  **/
788 gboolean
789 nm_system_iface_set_mac (int ifindex, const struct ether_addr *mac)
790 {
791         struct rtnl_link *old, *new;
792         gboolean success = FALSE;
793         struct nl_sock *nlh;
794         const char *iface;
795         struct nl_addr *addr = NULL;
796         int err;
797
798         g_return_val_if_fail (ifindex > 0, FALSE);
799         g_return_val_if_fail (mac != NULL, FALSE);
800
801         iface = nm_netlink_index_to_iface (ifindex);
802         g_return_val_if_fail (iface != NULL, FALSE);
803
804         new = rtnl_link_alloc ();
805         if (!new)
806                 return FALSE;
807
808         old = nm_netlink_index_to_rtnl_link (ifindex);
809         if (old) {
810                 addr = nl_addr_build (AF_LLC, (void *) mac, ETH_ALEN);
811                 if (!addr) {
812                         nm_log_err (LOGD_HW, "(%s): failed to allocate memory for MAC address change", iface);
813                         return FALSE;
814                 }
815                 rtnl_link_set_addr (new, addr);
816                 nlh = nm_netlink_get_default_handle ();
817                 if (nlh) {
818                         err = rtnl_link_change (nlh, old, new, 0);
819                         if (err == 0)
820                                 success = TRUE;
821                         else
822                                 nm_log_warn (LOGD_HW, "(%s): failed to change interface MAC address", iface);
823                 }
824                 rtnl_link_put (old);
825         }
826
827         rtnl_link_put (new);
828         return success;
829 }
830
831 static struct rtnl_route *
832 add_ip4_route_to_gateway (int ifindex, guint32 gw, guint32 mss)
833 {
834         struct nl_sock *nlh;
835         struct rtnl_route *route = NULL;
836         struct nl_addr *gw_addr = NULL;
837         const char *iface;
838         int err;
839
840         iface = nm_netlink_index_to_iface (ifindex);
841         g_return_val_if_fail (iface != NULL, FALSE);
842
843         nlh = nm_netlink_get_default_handle ();
844         g_return_val_if_fail (nlh != NULL, NULL);
845
846         /* Gateway might be over a bridge; try adding a route to gateway first */
847         route = nm_netlink_route_new (ifindex, AF_INET, mss,
848                                       NMNL_PROP_SCOPE, RT_SCOPE_LINK,
849                                       NMNL_PROP_TABLE, RT_TABLE_MAIN,
850                                       NULL);
851         g_return_val_if_fail (route != NULL, NULL);
852
853         gw_addr = nl_addr_build (AF_INET, &gw, sizeof (gw));
854         if (!gw_addr)
855                 goto error;
856         nl_addr_set_prefixlen (gw_addr, 32);
857         rtnl_route_set_dst (route, gw_addr);
858         nl_addr_put (gw_addr);
859
860         /* Add direct route to the gateway */
861         err = rtnl_route_add (nlh, route, 0);
862         if (err) {
863                 nm_log_err (LOGD_DEVICE | LOGD_IP4,
864                             "(%s): failed to add IPv4 route to gateway (%d)",
865                             iface, err);
866                 goto error;
867         }
868
869         return route;
870
871 error:
872         rtnl_route_put (route);
873         return NULL;
874 }
875
876 static int
877 replace_default_ip4_route (int ifindex, guint32 gw, guint32 mss)
878 {
879         struct rtnl_route *route = NULL;
880         struct nl_sock *nlh;
881         struct nl_addr *dst_addr = NULL;
882         guint32 dst = 0;
883         struct nl_addr *gw_addr = NULL;
884         int err = -1;
885
886         g_return_val_if_fail (ifindex > 0, -ENODEV);
887
888         nlh = nm_netlink_get_default_handle ();
889         g_return_val_if_fail (nlh != NULL, -ENOMEM);
890
891         route = nm_netlink_route_new (ifindex, AF_INET, mss,
892                                       NMNL_PROP_SCOPE, RT_SCOPE_UNIVERSE,
893                                       NMNL_PROP_TABLE, RT_TABLE_MAIN,
894                                       NULL);
895         g_return_val_if_fail (route != NULL, -ENOMEM);
896
897         /* Build up the destination address */
898         dst_addr = nl_addr_build (AF_INET, &dst, sizeof (dst));
899         if (!dst_addr) {
900                 err = -ENOMEM;
901                 goto out;
902         }
903         nl_addr_set_prefixlen (dst_addr, 0);
904         rtnl_route_set_dst (route, dst_addr);
905
906         /* Build up the gateway address */
907         gw_addr = nl_addr_build (AF_INET, &gw, sizeof (gw));
908         if (!gw_addr) {
909                 err = -ENOMEM;
910                 goto out;
911         }
912         nl_addr_set_prefixlen (gw_addr, 0);
913         rtnl_route_set_gateway (route, gw_addr);
914
915         /* Add the new default route */
916         err = rtnl_route_add (nlh, route, NLM_F_REPLACE);
917
918 out:
919         if (dst_addr)
920                 nl_addr_put (dst_addr);
921         if (gw_addr)
922                 nl_addr_put (gw_addr);
923         rtnl_route_put (route);
924         return err;
925 }
926
927 /*
928  * nm_system_replace_default_ip4_route_vpn
929  *
930  * Replace default IPv4 route with one via the current device
931  *
932  */
933 gboolean
934 nm_system_replace_default_ip4_route_vpn (int ifindex,
935                                          guint32 ext_gw,
936                                          guint32 int_gw,
937                                          guint32 mss,
938                                          int parent_ifindex,
939                                          guint32 parent_mss)
940 {
941         struct rtnl_route *gw_route = NULL;
942         struct nl_sock *nlh;
943         gboolean success = FALSE;
944         int err;
945         const char *iface;
946
947         iface = nm_netlink_index_to_iface (ifindex);
948         g_return_val_if_fail (iface != NULL, FALSE);
949
950         nlh = nm_netlink_get_default_handle ();
951         g_return_val_if_fail (nlh != NULL, FALSE);
952
953         err = replace_default_ip4_route (ifindex, int_gw, mss);
954         if (err == 0) {
955                 return TRUE;
956         } else if (err != -ESRCH) {
957                 nm_log_err (LOGD_DEVICE | LOGD_IP4,
958                             "(%s): failed to set IPv4 default route: %d",
959                             iface, err);
960                 return FALSE;
961         }
962
963         /* Try adding a direct route to the gateway first */
964         gw_route = add_ip4_route_to_gateway (parent_ifindex, ext_gw, parent_mss);
965         if (!gw_route)
966                 return FALSE;
967
968         /* Try adding the original route again */
969         err = replace_default_ip4_route (ifindex, int_gw, mss);
970         if (err != 0) {
971                 nm_netlink_route_delete (gw_route);
972                 nm_log_err (LOGD_DEVICE | LOGD_IP4,
973                             "(%s): failed to set IPv4 default route (pass #2): %d",
974                             iface, err);
975         } else
976                 success = TRUE;
977
978         rtnl_route_put (gw_route);
979         return success;
980 }
981
982 /*
983  * nm_system_replace_default_ip4_route
984  *
985  * Replace default IPv4 route with one via the current device
986  *
987  */
988 gboolean
989 nm_system_replace_default_ip4_route (int ifindex, guint32 gw, guint32 mss)
990 {
991         struct rtnl_route *gw_route = NULL;
992         gboolean success = FALSE;
993         const char *iface;
994         int err;
995
996         iface = nm_netlink_index_to_iface (ifindex);
997         g_return_val_if_fail (iface != NULL, FALSE);
998
999         err = replace_default_ip4_route (ifindex, gw, mss);
1000         if (err == 0) {
1001                 return TRUE;
1002         } else if (err != -ESRCH) {
1003                 nm_log_err (LOGD_DEVICE | LOGD_IP4,
1004                             "(%s): failed to set IPv4 default route: %d",
1005                             iface, err);
1006                 return FALSE;
1007         }
1008
1009         /* Try adding a direct route to the gateway first */
1010         gw_route = add_ip4_route_to_gateway (ifindex, gw, mss);
1011         if (!gw_route)
1012                 return FALSE;
1013
1014         /* Try adding the original route again */
1015         err = replace_default_ip4_route (ifindex, gw, mss);
1016         if (err != 0) {
1017                 nm_netlink_route_delete (gw_route);
1018                 nm_log_err (LOGD_DEVICE | LOGD_IP4,
1019                             "(%s): failed to set IPv4 default route (pass #2): %d",
1020                             iface, err);
1021         } else
1022                 success = TRUE;
1023
1024         rtnl_route_put (gw_route);
1025         return success;
1026 }
1027
1028 static struct rtnl_route *
1029 add_ip6_route_to_gateway (int ifindex, const struct in6_addr *gw)
1030 {
1031         struct nl_sock *nlh;
1032         struct rtnl_route *route = NULL;
1033         struct nl_addr *gw_addr = NULL;
1034         const char *iface;
1035         int err;
1036
1037         iface = nm_netlink_index_to_iface (ifindex);
1038         g_return_val_if_fail (iface != NULL, NULL);
1039
1040         nlh = nm_netlink_get_default_handle ();
1041         g_return_val_if_fail (nlh != NULL, NULL);
1042
1043         /* Gateway might be over a bridge; try adding a route to gateway first */
1044         route = nm_netlink_route_new (ifindex, AF_INET6, 0,
1045                                       NMNL_PROP_SCOPE, RT_SCOPE_LINK,
1046                                       NMNL_PROP_TABLE, RT_TABLE_MAIN,
1047                                       NULL);
1048         g_return_val_if_fail (route != NULL, NULL);
1049
1050         gw_addr = nl_addr_build (AF_INET, (void *) gw, sizeof (*gw));
1051         if (!gw_addr)
1052                 goto error;
1053         nl_addr_set_prefixlen (gw_addr, 128);
1054         rtnl_route_set_dst (route, gw_addr);
1055         nl_addr_put (gw_addr);
1056
1057         /* Add direct route to the gateway */
1058         err = rtnl_route_add (nlh, route, 0);
1059         if (err) {
1060                 nm_log_err (LOGD_DEVICE | LOGD_IP6,
1061                             "(%s): failed to add IPv4 route to gateway (%d)",
1062                             iface, err);
1063                 goto error;
1064         }
1065
1066         return route;
1067
1068 error:
1069         rtnl_route_put (route);
1070         return NULL;
1071 }
1072
1073 static int
1074 replace_default_ip6_route (int ifindex, const struct in6_addr *gw)
1075 {
1076         struct rtnl_route *route = NULL;
1077         struct nl_sock *nlh;
1078         struct nl_addr *gw_addr = NULL;
1079         const char *iface;
1080         int err = -1;
1081
1082         g_return_val_if_fail (ifindex > 0, FALSE);
1083
1084         iface = nm_netlink_index_to_iface (ifindex);
1085         g_return_val_if_fail (iface != NULL, FALSE);
1086
1087         nlh = nm_netlink_get_default_handle ();
1088         g_return_val_if_fail (nlh != NULL, -ENOMEM);
1089
1090         route = nm_netlink_route_new (ifindex, AF_INET6, 0,
1091                                       NMNL_PROP_SCOPE, RT_SCOPE_UNIVERSE,
1092                                       NMNL_PROP_TABLE, RT_TABLE_MAIN,
1093                                       NULL);
1094         g_return_val_if_fail (route != NULL, -ENOMEM);
1095
1096         if (gw && !IN6_IS_ADDR_UNSPECIFIED (gw)) {
1097                 /* Build up the gateway address */
1098                 gw_addr = nl_addr_build (AF_INET6, (void *) gw, sizeof (*gw));
1099                 if (!gw_addr) {
1100                         err = -ENOMEM;
1101                         goto out;
1102                 }
1103                 nl_addr_set_prefixlen (gw_addr, -1);
1104                 rtnl_route_set_gateway (route, gw_addr);
1105         }
1106
1107         /* Add the new default route */
1108         err = rtnl_route_add (nlh, route, NLM_F_REPLACE);
1109         if (err == -EEXIST) {
1110                 /* FIXME: even though we use NLM_F_REPLACE the kernel won't replace
1111                  * the route if it's the same.  Should try to remove it first, then
1112                  * add the new one again here.
1113                  */
1114                 err = 0;
1115         }
1116
1117 out:
1118         if (gw_addr)
1119                 nl_addr_put (gw_addr);
1120         rtnl_route_put (route);
1121         return err;
1122 }
1123
1124 /*
1125  * nm_system_replace_default_ip6_route
1126  *
1127  * Replace default IPv6 route with one via the given gateway
1128  *
1129  */
1130 gboolean
1131 nm_system_replace_default_ip6_route (int ifindex, const struct in6_addr *gw)
1132 {
1133         struct rtnl_route *gw_route = NULL;
1134         gboolean success = FALSE;
1135         const char *iface;
1136         int err;
1137
1138         iface = nm_netlink_index_to_iface (ifindex);
1139         g_return_val_if_fail (iface != NULL, FALSE);
1140
1141         err = replace_default_ip6_route (ifindex, gw);
1142         if (err == 0)
1143                 return TRUE;
1144         if (err != -ESRCH) {
1145                 nm_log_err (LOGD_DEVICE | LOGD_IP6,
1146                             "(%s): failed to set IPv6 default route: %d",
1147                             iface, err);
1148                 return FALSE;
1149         }
1150
1151         /* Try adding a direct route to the gateway first */
1152         gw_route = add_ip6_route_to_gateway (ifindex, gw);
1153         if (!gw_route)
1154                 return FALSE;
1155
1156         /* Try adding the original route again */
1157         err = replace_default_ip6_route (ifindex, gw);
1158         if (err != 0) {
1159                 nm_netlink_route_delete (gw_route);
1160                 nm_log_err (LOGD_DEVICE | LOGD_IP6,
1161                             "(%s): failed to set IPv6 default route (pass #2): %d",
1162                             iface, err);
1163         } else
1164                 success = TRUE;
1165
1166         rtnl_route_put (gw_route);
1167         return success;
1168 }
1169
1170 /*
1171  * nm_system_iface_flush_addresses
1172  *
1173  * Flush all network addresses associated with a network device
1174  *
1175  */
1176 gboolean
1177 nm_system_iface_flush_addresses (int ifindex, int family)
1178 {
1179         g_return_val_if_fail (ifindex > 0, FALSE);
1180         return sync_addresses (ifindex, family, NULL, 0);
1181 }
1182
1183
1184 static struct rtnl_route *
1185 delete_one_route (struct rtnl_route *route,
1186                   struct nl_addr *dst,
1187                   const char *iface,
1188                   gpointer user_data)
1189 {
1190         guint32 log_level = GPOINTER_TO_UINT (user_data);
1191
1192         nm_log_dbg (log_level, "   deleting route");
1193         if (!nm_netlink_route_delete (route))
1194                 nm_log_err (LOGD_DEVICE, "(%s): failed to delete route", iface);
1195
1196         return NULL;
1197 }
1198
1199 /**
1200  * nm_system_iface_flush_routes:
1201  * @ifindex: interface index
1202  * @family: address family, i.e. AF_INET, AF_INET6, or AF_UNSPEC
1203  *
1204  * Flush all network addresses associated with a network device.
1205  *
1206  * Returns: %TRUE on success, %FALSE on failure
1207  **/
1208 gboolean
1209 nm_system_iface_flush_routes (int ifindex, int family)
1210 {
1211         guint32 log_level = LOGD_IP4 | LOGD_IP6;
1212         const char *sf = "UNSPEC";
1213         const char *iface;
1214
1215         g_return_val_if_fail (ifindex > 0, FALSE);
1216
1217         iface = nm_netlink_index_to_iface (ifindex);
1218         g_return_val_if_fail (iface != NULL, FALSE);
1219
1220         if (family == AF_INET) {
1221                 log_level = LOGD_IP4;
1222                 sf = "INET";
1223         } else if (family == AF_INET6) {
1224                 log_level = LOGD_IP6;
1225                 sf = "INET6";
1226         }
1227         nm_log_dbg (log_level, "(%s): flushing routes ifindex %d family %s (%d)",
1228                     iface, ifindex, sf, family);
1229
1230         /* We don't want to flush IPv6 link-local routes that may exist on the
1231          * the interface since the LL address and routes should normally stay
1232          * assigned all the time.
1233          */
1234         nm_netlink_foreach_route (ifindex, family, RT_SCOPE_UNIVERSE, TRUE, delete_one_route, GUINT_TO_POINTER (log_level));
1235         return TRUE;
1236 }
1237
1238 static struct rtnl_route *
1239 find_route (struct rtnl_route *route,
1240             struct nl_addr *dst,
1241             const char *iface,
1242             gpointer user_data)
1243 {
1244         NMIP4Config *config = user_data;
1245         struct in_addr *dst_addr;
1246         int num;
1247         int i;
1248
1249         if (dst && (nl_addr_get_family (dst) != AF_INET))
1250                 return NULL;
1251
1252         /* Find the first route that handles a subnet of at least one of the
1253          * device's IPv4 addresses.
1254          */
1255         dst_addr = nl_addr_get_binary_addr (dst);
1256         num = nm_ip4_config_get_num_addresses (config);
1257         for (i = 0; i < num; i++) {
1258                 NMIP4Address *addr = nm_ip4_config_get_address (config, i);
1259                 guint32 prefix = nm_ip4_address_get_prefix (addr);
1260                 guint32 address = nm_ip4_address_get_address (addr);
1261
1262                 if (   prefix == nl_addr_get_prefixlen (dst)
1263                     && (address & nm_utils_ip4_prefix_to_netmask (prefix)) == dst_addr->s_addr)
1264                         return route;
1265         }
1266         return NULL;
1267 }
1268
1269 static void
1270 nm_system_device_set_priority (int ifindex,
1271                                NMIP4Config *config,
1272                                int priority)
1273 {
1274         struct nl_sock *nlh;
1275         struct rtnl_route *found;
1276
1277         found = nm_netlink_foreach_route (ifindex, AF_INET, RT_SCOPE_LINK, FALSE,  find_route, config);
1278         if (found) {
1279                 nlh = nm_netlink_get_default_handle ();
1280                 nm_netlink_route_delete (found);
1281                 rtnl_route_set_priority (found, priority);
1282                 rtnl_route_add (nlh, found, 0);
1283                 rtnl_route_put (found);
1284         }
1285 }