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