diff options
Diffstat (limited to 'src/nm-system.c')
| -rw-r--r-- | src/nm-system.c | 2603 |
1 files changed, 0 insertions, 2603 deletions
diff --git a/src/nm-system.c b/src/nm-system.c deleted file mode 100644 index 39983424..00000000 --- a/src/nm-system.c +++ /dev/null @@ -1,2603 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2004 - 2012 Red Hat, Inc. - * Copyright (C) 2005 - 2008 Novell, Inc. - * Copyright (C) 1996 - 1997 Yoichi Hariguchi <yoichi@fore.com> - * Copyright (C) January, 1998 Sergei Viznyuk <sv@phystech.com> - */ - -#include <config.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <netinet/in.h> -#include <net/route.h> -#include <arpa/nameser.h> -#include <arpa/inet.h> -#include <fcntl.h> -#include <stdio.h> -#include <string.h> -#include <syslog.h> -#include <errno.h> -#include <stdlib.h> -#include <unistd.h> -#include <resolv.h> -#include <netdb.h> -#include <glib.h> -#include <linux/if.h> -#include <linux/sockios.h> -#include <linux/if_bonding.h> -#include <linux/if_vlan.h> -#include <linux/if_bridge.h> - -#include "nm-system.h" -#include "nm-device.h" -#include "NetworkManagerUtils.h" -#include "nm-utils.h" -#include "nm-logging.h" -#include "nm-netlink-monitor.h" -#include "nm-netlink-utils.h" -#include "nm-netlink-compat.h" - -#include <netlink/route/addr.h> -#include <netlink/route/route.h> -#include <netlink/netlink.h> -#include <netlink/utils.h> -#include <netlink/route/link.h> -#include <netlink/route/link/bonding.h> -#include <netlink/route/link/vlan.h> - -#if !HAVE_VLAN_FLAG_LOOSE_BINDING -/* Older kernels don't have this flag */ -#define VLAN_FLAG_LOOSE_BINDING 0x04 -#endif - -static void nm_system_device_set_priority (int ifindex, - NMIP4Config *config, - int priority); - -static gboolean -ip4_dest_in_same_subnet (NMIP4Config *config, guint32 dest, guint32 dest_prefix) -{ - int num; - int i; - - num = nm_ip4_config_get_num_addresses (config); - for (i = 0; i < num; i++) { - NMIP4Address *addr = nm_ip4_config_get_address (config, i); - guint32 prefix = nm_ip4_address_get_prefix (addr); - guint32 address = nm_ip4_address_get_address (addr); - - if (prefix <= dest_prefix) { - guint32 masked_addr = ntohl(address) >> (32 - prefix); - guint32 masked_dest = ntohl(dest) >> (32 - prefix); - - if (masked_addr == masked_dest) - return TRUE; - } - } - - return FALSE; -} - -static struct rtnl_route * -nm_system_device_set_ip4_route (int ifindex, - guint32 ip4_dest, - guint32 ip4_prefix, - guint32 ip4_gateway, - guint32 metric, - int mss) -{ - struct nl_sock *nlh; - struct rtnl_route *route; - int err; - - g_return_val_if_fail (ifindex > 0, NULL); - - nlh = nm_netlink_get_default_handle (); - g_return_val_if_fail (nlh != NULL, NULL); - - route = nm_netlink_route_new (ifindex, AF_INET, mss, - NMNL_PROP_PRIO, metric, - NULL); - g_return_val_if_fail (route != NULL, NULL); - - /* Add the route */ - err = nm_netlink_route4_add (route, &ip4_dest, ip4_prefix, &ip4_gateway, 0); - if (err == -NLE_OBJ_NOTFOUND && ip4_gateway) { - /* Gateway might be over a bridge; try adding a route to gateway first */ - struct rtnl_route *route2; - - route2 = nm_netlink_route_new (ifindex, AF_INET, mss, NULL); - if (route2) { - /* Add route to gateway over bridge */ - err = nm_netlink_route4_add (route2, &ip4_gateway, 32, NULL, 0); - if (!err) { - err = nm_netlink_route4_add (route, &ip4_dest, ip4_prefix, &ip4_gateway, 0); - if (err) - nm_netlink_route_delete (route2); - } - rtnl_route_put (route2); - } - } - - if (err) { - char *iface = nm_netlink_index_to_iface (ifindex); - - nm_log_err (LOGD_DEVICE | LOGD_IP4, - "(%s): failed to set IPv4 route: %s", - iface ? iface : "unknown", nl_geterror (err)); - g_free (iface); - - rtnl_route_put (route); - route = NULL; - } - - return route; -} - -static gboolean -sync_addresses (int ifindex, - int family, - struct rtnl_addr **addrs, - int num_addrs) -{ - struct nl_sock *nlh; - struct nl_cache *addr_cache = NULL; - struct rtnl_addr *filter_addr = NULL, *match_addr; - struct nl_object *match; - struct nl_addr *nladdr; - int i, err; - guint32 log_domain = (family == AF_INET) ? LOGD_IP4 : LOGD_IP6; - char buf[INET6_ADDRSTRLEN + 1]; - char *iface = NULL; - gboolean success = FALSE; - - log_domain |= LOGD_DEVICE; - - nlh = nm_netlink_get_default_handle (); - if (!nlh) - return FALSE; - - err = rtnl_addr_alloc_cache (nlh, &addr_cache); - if (err < 0) - return FALSE; - - filter_addr = rtnl_addr_alloc (); - if (!filter_addr) - goto out; - - rtnl_addr_set_ifindex (filter_addr, ifindex); - if (family) - rtnl_addr_set_family (filter_addr, family); - - iface = nm_netlink_index_to_iface (ifindex); - if (!iface) - goto out; - - nm_log_dbg (log_domain, "(%s): syncing addresses (family %d)", iface, family); - - /* Walk through the cache, comparing the addresses already on - * the interface to the addresses in addrs. - */ - for (match = nl_cache_get_first (addr_cache); match; match = nl_cache_get_next (match)) { - gboolean buf_valid = FALSE; - match_addr = (struct rtnl_addr *) match; - - /* Skip addresses not on our interface */ - if (!nl_object_match_filter (match, (struct nl_object *) filter_addr)) - continue; - - if (addrs) { - for (i = 0; i < num_addrs; i++) { - if (addrs[i] && nl_object_identical (match, (struct nl_object *) addrs[i])) - break; - } - - if (addrs[i]) { - /* match == addrs[i], so remove it from addrs so we don't - * try to add it to the interface again below. - */ - rtnl_addr_put (addrs[i]); - addrs[i] = NULL; - continue; - } - } - - nladdr = rtnl_addr_get_local (match_addr); - - /* Don't delete IPv6 link-local addresses; they don't belong to NM */ - if (rtnl_addr_get_family (match_addr) == AF_INET6) { - struct in6_addr *tmp; - - if (rtnl_addr_get_scope (match_addr) == RT_SCOPE_LINK) { - nm_log_dbg (log_domain, "(%s): ignoring IPv6 link-local address", iface); - continue; - } - - tmp = nl_addr_get_binary_addr (nladdr); - if (inet_ntop (AF_INET6, tmp, buf, sizeof (buf))) - buf_valid = TRUE; - } else if (rtnl_addr_get_family (match_addr) == AF_INET) { - struct in_addr *tmp; - - tmp = nl_addr_get_binary_addr (nladdr); - if (inet_ntop (AF_INET, tmp, buf, sizeof (buf))) - buf_valid = TRUE; - } - - if (buf_valid) { - nm_log_dbg (log_domain, "(%s): removing address '%s/%d'", - iface, buf, rtnl_addr_get_prefixlen (match_addr)); - } - - /* Otherwise, match_addr should be removed from the interface. */ - err = rtnl_addr_delete (nlh, match_addr, 0); - if (err < 0) { - nm_log_err (log_domain, "(%s): error %d returned from rtnl_addr_delete(): %s", - iface, err, nl_geterror (err)); - } - } - - /* Now add the remaining new addresses */ - for (i = 0; i < num_addrs; i++) { - struct in6_addr *in6tmp; - struct in_addr *in4tmp; - gboolean buf_valid = FALSE; - - if (!addrs[i]) - continue; - - nladdr = rtnl_addr_get_local (addrs[i]); - if (rtnl_addr_get_family (addrs[i]) == AF_INET6) { - in6tmp = nl_addr_get_binary_addr (nladdr); - if (inet_ntop (AF_INET6, in6tmp, buf, sizeof (buf))) - buf_valid = TRUE; - } else if (rtnl_addr_get_family (addrs[i]) == AF_INET) { - in4tmp = nl_addr_get_binary_addr (nladdr); - if (inet_ntop (AF_INET, in4tmp, buf, sizeof (buf))) - buf_valid = TRUE; - } - - if (buf_valid) { - nm_log_dbg (log_domain, "(%s): adding address '%s/%d'", - iface, buf, nl_addr_get_prefixlen (nladdr)); - } - - err = rtnl_addr_add (nlh, addrs[i], 0); - if (err < 0 && (err != -NLE_EXIST)) { - nm_log_err (log_domain, - "(%s): error %d returned from rtnl_addr_add():\n%s", - iface, err, nl_geterror (err)); - } - - rtnl_addr_put (addrs[i]); - } - g_free (addrs); - success = TRUE; - -out: - if (filter_addr) - rtnl_addr_put (filter_addr); - if (addr_cache) - nl_cache_free (addr_cache); - g_free (iface); - return success; -} - -static gboolean -add_ip4_addresses (NMIP4Config *config, int ifindex) -{ - char *iface; - int num_addrs, i; - guint32 flags = 0; - gboolean did_gw = FALSE; - struct rtnl_addr **addrs; - - g_return_val_if_fail (ifindex > 0, FALSE); - - iface = nm_netlink_index_to_iface (ifindex); - if (!iface) - return FALSE; - - num_addrs = nm_ip4_config_get_num_addresses (config); - addrs = g_new0 (struct rtnl_addr *, num_addrs + 1); - - for (i = 0; i < num_addrs; i++) { - NMIP4Address *addr; - - addr = nm_ip4_config_get_address (config, i); - g_assert (addr); - - flags = NM_RTNL_ADDR_DEFAULT; - if (nm_ip4_address_get_gateway (addr) && !did_gw) { - if (nm_ip4_config_get_ptp_address (config)) - flags |= NM_RTNL_ADDR_PTP_ADDR; - did_gw = TRUE; - } - - addrs[i] = nm_ip4_config_to_rtnl_addr (config, i, flags); - if (!addrs[i]) { - nm_log_warn (LOGD_DEVICE | LOGD_IP4, - "(%s): couldn't create rtnl address!", - iface ? iface : "unknown"); - continue; - } - rtnl_addr_set_ifindex (addrs[i], ifindex); - } - g_free (iface); - - return sync_addresses (ifindex, AF_INET, addrs, num_addrs); -} - -struct rtnl_route * -nm_system_add_ip4_vpn_gateway_route (NMDevice *parent_device, guint32 vpn_gw) -{ - NMIP4Config *parent_config; - guint32 parent_gw = 0, parent_prefix = 0, i; - NMIP4Address *tmp; - struct rtnl_route *route = NULL; - - g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL); - g_return_val_if_fail (vpn_gw != 0, NULL); - - /* Set up a route to the VPN gateway's public IP address through the default - * network device if the VPN gateway is on a different subnet. - */ - - parent_config = nm_device_get_ip4_config (parent_device); - g_return_val_if_fail (parent_config != NULL, NULL); - - for (i = 0; i < nm_ip4_config_get_num_addresses (parent_config); i++) { - tmp = nm_ip4_config_get_address (parent_config, i); - if (nm_ip4_address_get_gateway (tmp)) { - parent_gw = nm_ip4_address_get_gateway (tmp); - parent_prefix = nm_ip4_address_get_prefix (tmp); - break; - } - } - - if (!parent_gw) - return NULL; - - /* If the VPN gateway is in the same subnet as one of the parent device's - * IP addresses, don't add the host route to it, but a route through the - * parent device. - */ - if (ip4_dest_in_same_subnet (parent_config, vpn_gw, parent_prefix)) { - route = nm_system_device_set_ip4_route (nm_device_get_ip_ifindex (parent_device), - vpn_gw, 32, 0, 0, nm_ip4_config_get_mss (parent_config)); - } else { - route = nm_system_device_set_ip4_route (nm_device_get_ip_ifindex (parent_device), - vpn_gw, 32, parent_gw, 0, nm_ip4_config_get_mss (parent_config)); - } - - return route; -} - -/* - * nm_system_apply_ip4_config - * - * Set IPv4 configuration of the device from an NMIP4Config object. - * - */ -gboolean -nm_system_apply_ip4_config (int ifindex, - NMIP4Config *config, - int priority, - NMIP4ConfigCompareFlags flags) -{ - int i; - - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (config != NULL, FALSE); - - if (flags & NM_IP4_COMPARE_FLAG_ADDRESSES) { - if (!add_ip4_addresses (config, ifindex)) - return FALSE; - sleep (1); - } - - if (flags & NM_IP4_COMPARE_FLAG_ROUTES) { - for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) { - NMIP4Route *route = nm_ip4_config_get_route (config, i); - struct rtnl_route *tmp; - - /* Don't add the route if it's more specific than one of the subnets - * the device already has an IP address on. - */ - if (ip4_dest_in_same_subnet (config, - nm_ip4_route_get_dest (route), - nm_ip4_route_get_prefix (route))) - continue; - - /* Don't add the route if it doesn't have a gateway and the connection - * is never supposed to be the default connection. - */ - if ( nm_ip4_config_get_never_default (config) - && nm_ip4_route_get_dest (route) == 0) - continue; - - tmp = nm_system_device_set_ip4_route (ifindex, - nm_ip4_route_get_dest (route), - nm_ip4_route_get_prefix (route), - nm_ip4_route_get_next_hop (route), - nm_ip4_route_get_metric (route), - nm_ip4_config_get_mss (config)); - rtnl_route_put (tmp); - } - } - - if (flags & NM_IP4_COMPARE_FLAG_MTU) { - if (nm_ip4_config_get_mtu (config)) - nm_system_iface_set_mtu (ifindex, nm_ip4_config_get_mtu (config)); - } - - if (priority > 0) - nm_system_device_set_priority (ifindex, config, priority); - - return TRUE; -} - -int -nm_system_set_ip6_route (int ifindex, - const struct in6_addr *ip6_dest, - guint32 ip6_prefix, - const struct in6_addr *ip6_gateway, - guint32 metric, - int mss, - int protocol, - int table, - struct rtnl_route **out_route) -{ - struct nl_sock *nlh; - struct rtnl_route *route; - int err = 0; - - g_return_val_if_fail (ifindex > 0, -1); - - nlh = nm_netlink_get_default_handle (); - g_return_val_if_fail (nlh != NULL, -1); - - route = nm_netlink_route_new (ifindex, AF_INET6, mss, - NMNL_PROP_PROT, protocol, - NMNL_PROP_PRIO, metric, - NMNL_PROP_TABLE, table, - NULL); - g_return_val_if_fail (route != NULL, -1); - - /* Add the route */ - err = nm_netlink_route6_add (route, ip6_dest, ip6_prefix, ip6_gateway, 0); - if (err == -NLE_OBJ_NOTFOUND && ip6_gateway) { - /* Gateway might be over a bridge; try adding a route to gateway first */ - struct rtnl_route *route2; - - route2 = nm_netlink_route_new (ifindex, AF_INET6, mss, NULL); - if (route2) { - err = nm_netlink_route6_add (route, ip6_gateway, 128, NULL, 0); - /* Add route to gateway over bridge */ - if (!err) { - /* Try adding the route again */ - err = nm_netlink_route6_add (route, ip6_dest, ip6_prefix, ip6_gateway, 0); - if (err) - nm_netlink_route_delete (route2); - } - rtnl_route_put (route2); - } - } - - if (out_route) - *out_route = route; - else - rtnl_route_put (route); - - return err; -} - -static gboolean -ip6_dest_in_same_subnet (NMIP6Config *config, const struct in6_addr *dest, guint32 dest_prefix) -{ - int num; - int i; - - num = nm_ip6_config_get_num_addresses (config); - for (i = 0; i < num; i++) { - NMIP6Address *addr = nm_ip6_config_get_address (config, i); - guint32 prefix = nm_ip6_address_get_prefix (addr); - const struct in6_addr *address = nm_ip6_address_get_address (addr); - - if (prefix <= dest_prefix) { - const guint8 *maskbytes = (const guint8 *)address; - const guint8 *addrbytes = (const guint8 *)dest; - int nbytes, nbits; - - /* Copied from g_inet_address_mask_matches() */ - nbytes = prefix / 8; - if (nbytes != 0 && memcmp (maskbytes, addrbytes, nbytes) != 0) - continue; - - nbits = prefix % 8; - if (nbits == 0) - return TRUE; - - if (maskbytes[nbytes] == (addrbytes[nbytes] & (0xFF << (8 - nbits)))) - return TRUE; - } - } - - return FALSE; -} - -struct rtnl_route * -nm_system_add_ip6_vpn_gateway_route (NMDevice *parent_device, - const struct in6_addr *vpn_gw) -{ - NMIP6Config *parent_config; - const struct in6_addr *parent_gw = NULL; - guint32 parent_prefix = 0; - int i, err; - NMIP6Address *tmp; - struct rtnl_route *route = NULL; - - g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL); - g_return_val_if_fail (vpn_gw != NULL, NULL); - - /* This is all just the same as - * nm_system_add_ip4_vpn_gateway_route(), except with an IPv6 - * address for the VPN gateway. - */ - - parent_config = nm_device_get_ip6_config (parent_device); - g_return_val_if_fail (parent_config != NULL, NULL); - - for (i = 0; i < nm_ip6_config_get_num_addresses (parent_config); i++) { - tmp = nm_ip6_config_get_address (parent_config, i); - if (nm_ip6_address_get_gateway (tmp)) { - parent_gw = nm_ip6_address_get_gateway (tmp); - parent_prefix = nm_ip6_address_get_prefix (tmp); - break; - } - } - - if (!parent_gw) - return NULL; - - if (ip6_dest_in_same_subnet (parent_config, vpn_gw, parent_prefix)) { - err = nm_system_set_ip6_route (nm_device_get_ip_ifindex (parent_device), - vpn_gw, 128, NULL, 0, - nm_ip6_config_get_mss (parent_config), - RTPROT_UNSPEC, RT_TABLE_UNSPEC, - &route); - } else { - err = nm_system_set_ip6_route (nm_device_get_ip_ifindex (parent_device), - vpn_gw, 128, parent_gw, 0, - nm_ip6_config_get_mss (parent_config), - RTPROT_UNSPEC, RT_TABLE_UNSPEC, - &route); - } - - if (err) { - nm_log_err (LOGD_DEVICE | LOGD_IP6, - "(%s): failed to add IPv6 route to VPN gateway (%d)", - nm_device_get_iface (parent_device), err); - } - return route; -} - -static gboolean -add_ip6_addresses (NMIP6Config *config, int ifindex) -{ - char *iface; - int num_addrs, i; - struct rtnl_addr **addrs; - - g_return_val_if_fail (ifindex > 0, FALSE); - - iface = nm_netlink_index_to_iface (ifindex); - if (!iface) - return FALSE; - - num_addrs = nm_ip6_config_get_num_addresses (config); - addrs = g_new0 (struct rtnl_addr *, num_addrs + 1); - - for (i = 0; i < num_addrs; i++) { - NMIP6Address *addr; - - addr = nm_ip6_config_get_address (config, i); - g_assert (addr); - - addrs[i] = nm_ip6_config_to_rtnl_addr (config, i, NM_RTNL_ADDR_DEFAULT); - if (!addrs[i]) { - nm_log_warn (LOGD_DEVICE | LOGD_IP6, - "(%s): couldn't create rtnl address!", - iface ? iface : "unknown"); - continue; - } - rtnl_addr_set_ifindex (addrs[i], ifindex); - } - g_free (iface); - - return sync_addresses (ifindex, AF_INET6, addrs, num_addrs); -} - -/* - * nm_system_apply_ip6_config - * - * Set IPv6 configuration of the device from an NMIP6Config object. - * - */ -gboolean -nm_system_apply_ip6_config (int ifindex, - NMIP6Config *config, - int priority, - NMIP6ConfigCompareFlags flags) -{ - int i; - - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (config != NULL, FALSE); - - if (flags & NM_IP6_COMPARE_FLAG_ADDRESSES) { - if (!add_ip6_addresses (config, ifindex)) - return FALSE; - sleep (1); // FIXME? - } - - if (flags & NM_IP6_COMPARE_FLAG_ROUTES) { - char *iface = nm_netlink_index_to_iface (ifindex); - - for (i = 0; i < nm_ip6_config_get_num_routes (config); i++) { - NMIP6Route *route = nm_ip6_config_get_route (config, i); - int err; - - /* Don't add the route if it doesn't have a gateway and the connection - * is never supposed to be the default connection. - */ - if ( nm_ip6_config_get_never_default (config) - && IN6_IS_ADDR_UNSPECIFIED (nm_ip6_route_get_dest (route))) - continue; - - err = nm_system_set_ip6_route (ifindex, - nm_ip6_route_get_dest (route), - nm_ip6_route_get_prefix (route), - nm_ip6_route_get_next_hop (route), - nm_ip6_route_get_metric (route), - nm_ip6_config_get_mss (config), - RTPROT_UNSPEC, - RT_TABLE_UNSPEC, - NULL); - if (err && (err != -NLE_EXIST)) { - nm_log_err (LOGD_DEVICE | LOGD_IP6, - "(%s): failed to set IPv6 route: %s", - iface ? iface : "unknown", - nl_geterror (err)); - } - } - g_free (iface); - } - -// FIXME -// if (priority > 0) -// nm_system_device_set_priority (iface, config, priority); - - return TRUE; -} - -/** - * nm_system_iface_set_up: - * @ifindex: interface index - * @up: %TRUE to bring interface up, or %FALSE to take it down - * @no_firmware: on return, %TRUE if the operation may have failed due to - * missing firmware - * - * Bring the interface up or take it down. - * - * Returns: %TRUE on success, %FALSE on failure - **/ -gboolean -nm_system_iface_set_up (int ifindex, - gboolean up, - gboolean *no_firmware) -{ - struct rtnl_link *request = NULL, *old = NULL; - struct nl_sock *nlh; - gboolean success = FALSE; - int err; - - g_return_val_if_fail (ifindex > 0, FALSE); - if (no_firmware) - g_return_val_if_fail (*no_firmware == FALSE, FALSE); - - if (!(request = rtnl_link_alloc ())) - return FALSE; - - if (up) - rtnl_link_set_flags (request, IFF_UP); - else - rtnl_link_unset_flags (request, IFF_UP); - - old = nm_netlink_index_to_rtnl_link (ifindex); - if (old) { - nlh = nm_netlink_get_default_handle (); - if (nlh) { - err = rtnl_link_change (nlh, old, request, 0); - if (err == 0) { - success = TRUE; - } else { - if ((err == -NLE_OBJ_NOTFOUND) && no_firmware && up) - *no_firmware = TRUE; - } - } - } - - rtnl_link_put (old); - rtnl_link_put (request); - return success; -} - -guint32 -nm_system_iface_get_flags (int ifindex) -{ - struct rtnl_link *l; - guint32 flags; - - g_return_val_if_fail (ifindex > 0, FALSE); - - l = nm_netlink_index_to_rtnl_link (ifindex); - if (l == NULL) { - char *iface = nm_netlink_index_to_iface (ifindex); - - g_warn_if_fail (iface != NULL); - nm_log_err (LOGD_HW, "(%s): failed to get interface link object", - iface ? iface : "unknown"); - g_free (iface); - return FALSE; - } - - flags = rtnl_link_get_flags (l); - rtnl_link_put (l); - - return flags; -} - -/** - * nm_system_iface_is_up: - * @ifindex: interface index - * - * Returns: %TRUE if the interface is up, %FALSE if it was down or the check - * failed. - **/ -gboolean -nm_system_iface_is_up (int ifindex) -{ - return nm_system_iface_get_flags (ifindex) & IFF_UP; -} - -/** - * nm_system_iface_set_mtu: - * @ifindex: interface index - * @mtu: the new MTU - * - * Returns: %TRUE if the request was successful, %FALSE if it failed - **/ -gboolean -nm_system_iface_set_mtu (int ifindex, guint32 mtu) -{ - struct rtnl_link *old; - struct rtnl_link *new; - gboolean success = FALSE; - struct nl_sock *nlh; - int err; - - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (mtu > 0, FALSE); - - new = rtnl_link_alloc (); - if (!new) - return FALSE; - - old = nm_netlink_index_to_rtnl_link (ifindex); - if (old) { - rtnl_link_set_mtu (new, mtu); - nlh = nm_netlink_get_default_handle (); - if (nlh) { - err = rtnl_link_change (nlh, old, new, 0); - if (err == 0) - success = TRUE; - else { - char *iface = nm_netlink_index_to_iface (ifindex); - - nm_log_warn (LOGD_HW, "(%s): failed to change interface MTU", - iface ? iface : "unknown"); - g_free (iface); - } - } - rtnl_link_put (old); - } - rtnl_link_put (new); - - return success; -} - -/** - * nm_system_iface_set_mac: - * @ifindex: interface index - * @mac: new MAC address - * - * Attempts to change the interface's MAC address to the requested value, - * ie MAC spoofing or cloning. - * - * Returns: %TRUE if the request succeeded, %FALSE if it failed. - **/ -gboolean -nm_system_iface_set_mac (int ifindex, const struct ether_addr *mac) -{ - struct rtnl_link *old, *new; - gboolean success = FALSE; - struct nl_sock *nlh; - char *iface; - struct nl_addr *addr = NULL; - int err; - - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (mac != NULL, FALSE); - - new = rtnl_link_alloc (); - if (!new) - return FALSE; - - iface = nm_netlink_index_to_iface (ifindex); - if (!iface) - goto out; - - old = nm_netlink_index_to_rtnl_link (ifindex); - if (old) { - addr = nl_addr_build (AF_LLC, (void *) mac, ETH_ALEN); - if (!addr) { - nm_log_err (LOGD_HW, "(%s): failed to allocate memory for MAC address change", iface); - rtnl_link_put (old); - rtnl_link_put (new); - return FALSE; - } - rtnl_link_set_addr (new, addr); - nl_addr_put (addr); - nlh = nm_netlink_get_default_handle (); - if (nlh) { - err = rtnl_link_change (nlh, old, new, 0); - if (err == 0) - success = TRUE; - else - nm_log_warn (LOGD_HW, "(%s): failed to change interface MAC address", iface); - } - rtnl_link_put (old); - } - -out: - rtnl_link_put (new); - g_free (iface); - return success; -} - -/** - * nm_system_iface_set_arp: - * @ifindex: interface index - * @enable: %TRUE to enable ARP, or %FALSE to disable - * - * Sets a flag to indicate that ARP should or should not be used on the - * interface. Point-to-point or IPv4 /32 interfaces often require that ARP - * be disabled. - * - * Returns: %TRUE on success, %FALSE on failure - **/ -gboolean -nm_system_iface_set_arp (int ifindex, gboolean enable) -{ - struct rtnl_link *request = NULL, *old = NULL; - struct nl_sock *nlh; - gboolean success = FALSE; - int err; - - g_return_val_if_fail (ifindex > 0, FALSE); - - if (!(request = rtnl_link_alloc ())) - return FALSE; - - if (enable) - rtnl_link_unset_flags (request, IFF_NOARP); - else - rtnl_link_set_flags (request, IFF_NOARP); - - old = nm_netlink_index_to_rtnl_link (ifindex); - if (old) { - nlh = nm_netlink_get_default_handle (); - if (nlh) { - err = rtnl_link_change (nlh, old, request, 0); - if (err == 0) - success = TRUE; - } - } - - rtnl_link_put (old); - rtnl_link_put (request); - return success; -} - -static struct rtnl_route * -add_ip4_route_to_gateway (int ifindex, guint32 gw, guint32 mss) -{ - struct nl_sock *nlh; - struct rtnl_route *route = NULL; - int err; - - nlh = nm_netlink_get_default_handle (); - g_return_val_if_fail (nlh != NULL, NULL); - - /* Gateway might be over a bridge; try adding a route to gateway first */ - route = nm_netlink_route_new (ifindex, AF_INET, mss, - NMNL_PROP_SCOPE, RT_SCOPE_LINK, - NMNL_PROP_TABLE, RT_TABLE_MAIN, - NULL); - g_return_val_if_fail (route != NULL, NULL); - - /* Add direct route to the gateway */ - err = nm_netlink_route4_add (route, &gw, 32, NULL, 0); - if (err) { - char *iface = nm_netlink_index_to_iface (ifindex); - - nm_log_err (LOGD_DEVICE | LOGD_IP4, - "(%s): failed to add IPv4 route to gateway (%d)", - iface ? iface : "unknown", err); - g_free (iface); - goto error; - } - - return route; - -error: - rtnl_route_put (route); - return NULL; -} - -static int -replace_default_ip4_route (int ifindex, guint32 gw, guint32 mss) -{ - struct rtnl_route *route = NULL; - struct nl_sock *nlh; - int err = -1; - guint32 dst = 0; - - g_return_val_if_fail (ifindex > 0, -ENODEV); - - nlh = nm_netlink_get_default_handle (); - g_return_val_if_fail (nlh != NULL, -ENOMEM); - - route = nm_netlink_route_new (ifindex, AF_INET, mss, - NMNL_PROP_SCOPE, RT_SCOPE_UNIVERSE, - NMNL_PROP_TABLE, RT_TABLE_MAIN, - NULL); - g_return_val_if_fail (route != NULL, -ENOMEM); - - /* Add the new default route */ - err = nm_netlink_route4_add (route, &dst, 0, &gw, NLM_F_REPLACE); - if (err == -NLE_EXIST) - err = 0; - - rtnl_route_put (route); - return err; -} - -/* - * nm_system_replace_default_ip4_route_vpn - * - * Replace default IPv4 route with one via the current device - * - */ -gboolean -nm_system_replace_default_ip4_route_vpn (int ifindex, - guint32 ext_gw, - guint32 int_gw, - guint32 mss, - int parent_ifindex, - guint32 parent_mss) -{ - struct rtnl_route *gw_route = NULL; - struct nl_sock *nlh; - gboolean success = FALSE; - int err; - char *iface; - - nlh = nm_netlink_get_default_handle (); - g_return_val_if_fail (nlh != NULL, FALSE); - - err = replace_default_ip4_route (ifindex, int_gw, mss); - if (err == 0) - return TRUE; - - iface = nm_netlink_index_to_iface (ifindex); - if (!iface) - goto out; - - if ((err != -NLE_OBJ_NOTFOUND) && (err != -NLE_FAILURE)) { - nm_log_err (LOGD_DEVICE | LOGD_VPN | LOGD_IP4, - "(%s): failed to set IPv4 default route: %d", - iface, err); - goto out; - } - - /* Try adding a direct route to the gateway first */ - gw_route = add_ip4_route_to_gateway (parent_ifindex, ext_gw, parent_mss); - if (!gw_route) - goto out; - - /* Try adding the original route again */ - err = replace_default_ip4_route (ifindex, int_gw, mss); - if (err != 0) { - nm_netlink_route_delete (gw_route); - nm_log_err (LOGD_DEVICE | LOGD_VPN | LOGD_IP4, - "(%s): failed to set IPv4 default route (pass #2): %d", - iface, err); - } else - success = TRUE; - -out: - if (gw_route) - rtnl_route_put (gw_route); - g_free (iface); - return success; -} - -/* - * nm_system_replace_default_ip4_route - * - * Replace default IPv4 route with one via the current device - * - */ -gboolean -nm_system_replace_default_ip4_route (int ifindex, guint32 gw, guint32 mss) -{ - struct rtnl_route *gw_route = NULL; - gboolean success = FALSE; - char *iface; - int err; - - err = replace_default_ip4_route (ifindex, gw, mss); - if (err == 0) - return TRUE; - - iface = nm_netlink_index_to_iface (ifindex); - if (!iface) - goto out; - - if (err != -NLE_OBJ_NOTFOUND) { - nm_log_err (LOGD_DEVICE | LOGD_IP4, - "(%s): failed to set IPv4 default route: %d", - iface, err); - goto out; - } - - /* Try adding a direct route to the gateway first */ - gw_route = add_ip4_route_to_gateway (ifindex, gw, mss); - if (!gw_route) - goto out; - - /* Try adding the original route again */ - err = replace_default_ip4_route (ifindex, gw, mss); - if (err != 0) { - nm_netlink_route_delete (gw_route); - nm_log_err (LOGD_DEVICE | LOGD_IP4, - "(%s): failed to set IPv4 default route (pass #2): %d", - iface, err); - } else - success = TRUE; - -out: - if (gw_route) - rtnl_route_put (gw_route); - g_free (iface); - return success; -} - -static struct rtnl_route * -add_ip6_route_to_gateway (int ifindex, const struct in6_addr *gw, int mss) -{ - struct nl_sock *nlh; - struct rtnl_route *route = NULL; - int err; - - nlh = nm_netlink_get_default_handle (); - g_return_val_if_fail (nlh != NULL, NULL); - - /* Gateway might be over a bridge; try adding a route to gateway first */ - route = nm_netlink_route_new (ifindex, AF_INET6, mss, - NMNL_PROP_SCOPE, RT_SCOPE_LINK, - NMNL_PROP_TABLE, RT_TABLE_MAIN, - NULL); - g_return_val_if_fail (route != NULL, NULL); - - /* Add direct route to the gateway */ - err = nm_netlink_route6_add (route, gw, 128, NULL, 0); - if (err) { - char *iface = nm_netlink_index_to_iface (ifindex); - - nm_log_err (LOGD_DEVICE | LOGD_IP6, - "(%s): failed to add IPv6 route to gateway (%d)", - iface ? iface : "unknown", err); - g_free (iface); - - rtnl_route_put (route); - route = NULL; - } - - return route; -} - -static int -add_default_ip6_route (int ifindex, const struct in6_addr *gw, int mss) -{ - struct rtnl_route *route = NULL; - struct nl_sock *nlh; - int err = -1; - - g_return_val_if_fail (ifindex > 0, FALSE); - - nlh = nm_netlink_get_default_handle (); - g_return_val_if_fail (nlh != NULL, -ENOMEM); - - route = nm_netlink_route_new (ifindex, AF_INET6, mss, - NMNL_PROP_SCOPE, RT_SCOPE_UNIVERSE, - NMNL_PROP_TABLE, RT_TABLE_MAIN, - NMNL_PROP_PRIO, 1, - NULL); - g_return_val_if_fail (route != NULL, -ENOMEM); - - /* Add the new default route */ - err = nm_netlink_route6_add (route, &in6addr_any, 0, gw, NLM_F_CREATE | NLM_F_REPLACE); - if (err == -NLE_EXIST) - err = 0; - - rtnl_route_put (route); - return err; -} - -static struct rtnl_route * -find_static_default_routes (struct rtnl_route *route, - struct nl_addr *dst, - const char *iface, - gpointer user_data) -{ - GList **def_routes = user_data; - - if ( nl_addr_get_prefixlen (dst) == 0 - && rtnl_route_get_protocol (route) == RTPROT_STATIC) { - rtnl_route_get (route); - *def_routes = g_list_prepend (*def_routes, route); - } - - return NULL; -} - -static int -replace_default_ip6_route (int ifindex, const struct in6_addr *gw, int mss) -{ - GList *def_routes, *iter; - struct rtnl_route *route; - char *iface; - char gw_str[INET6_ADDRSTRLEN + 1]; - - g_return_val_if_fail (ifindex > 0, FALSE); - - if (nm_logging_level_enabled (LOGL_DEBUG)) { - if (gw) { - memset (gw_str, 0, sizeof (gw_str)); - if (inet_ntop (AF_INET6, gw, gw_str, sizeof (gw_str) - 1)) - nm_log_dbg (LOGD_IP6, "Setting IPv6 default route via %s", gw_str); - } else { - nm_log_dbg (LOGD_IP6, "Setting IPv6 default route via %s", - nm_netlink_index_to_iface (ifindex)); - } - } - - /* We can't just use NLM_F_REPLACE here like in the IPv4 case, because - * the kernel doesn't like it if we replace the default routes it - * creates. (See rh#785772.) So we delete any non-kernel default routes, - * and then add a new default route of our own with a lower metric than - * the kernel ones. - */ - def_routes = NULL; - nm_netlink_foreach_route (ifindex, AF_INET6, RT_SCOPE_UNIVERSE, TRUE, - find_static_default_routes, &def_routes); - for (iter = def_routes; iter; iter = iter->next) { - route = iter->data; - if (!nm_netlink_route_delete (route)) { - iface = nm_netlink_index_to_iface (ifindex); - nm_log_err (LOGD_DEVICE | LOGD_IP6, - "(%s): failed to delete existing IPv6 default route", - iface); - g_free (iface); - } - rtnl_route_put (route); - } - g_list_free (def_routes); - - return add_default_ip6_route (ifindex, gw, mss); -} - -/* - * nm_system_replace_default_ip6_route - * - * Replace default IPv6 route with one via the given gateway - * - */ -gboolean -nm_system_replace_default_ip6_route (int ifindex, const struct in6_addr *gw) -{ - struct rtnl_route *gw_route = NULL; - gboolean success = FALSE; - char *iface; - int err; - - err = replace_default_ip6_route (ifindex, gw, 0); - if (err == 0 || err == -NLE_EXIST) - return TRUE; - - iface = nm_netlink_index_to_iface (ifindex); - if (!iface) - goto out; - - if (err != -NLE_OBJ_NOTFOUND) { - nm_log_err (LOGD_DEVICE | LOGD_IP6, - "(%s): failed to set IPv6 default route: %d", - iface, err); - goto out; - } - - /* Try adding a direct route to the gateway first */ - gw_route = add_ip6_route_to_gateway (ifindex, gw, 0); - if (!gw_route) - goto out; - - /* Try adding the original route again */ - err = replace_default_ip6_route (ifindex, gw, 0); - if (err != 0) { - nm_netlink_route_delete (gw_route); - nm_log_err (LOGD_DEVICE | LOGD_IP6, - "(%s): failed to set IPv6 default route (pass #2): %d", - iface, err); - } else - success = TRUE; - -out: - if (gw_route) - rtnl_route_put (gw_route); - g_free (iface); - return success; -} - -gboolean -nm_system_replace_default_ip6_route_vpn (int ifindex, - const struct in6_addr *ext_gw, - const struct in6_addr *int_gw, - guint32 mss, - int parent_ifindex, - guint32 parent_mss) -{ - struct rtnl_route *gw_route = NULL; - struct nl_sock *nlh; - gboolean success = FALSE; - int err; - char *iface; - - nlh = nm_netlink_get_default_handle (); - g_return_val_if_fail (nlh != NULL, FALSE); - - err = replace_default_ip6_route (ifindex, int_gw, mss); - if (err == 0) - return TRUE; - - iface = nm_netlink_index_to_iface (ifindex); - if (!iface) - goto out; - - if ((err != -NLE_OBJ_NOTFOUND) && (err != -NLE_FAILURE)) { - nm_log_err (LOGD_DEVICE | LOGD_VPN | LOGD_IP6, - "(%s): failed to set IPv6 default route: %d", - iface, err); - goto out; - } - - /* Try adding a direct route to the gateway first */ - gw_route = add_ip6_route_to_gateway (parent_ifindex, ext_gw, parent_mss); - if (!gw_route) - goto out; - - /* Try adding the original route again */ - err = replace_default_ip6_route (ifindex, int_gw, mss); - if (err != 0) { - nm_netlink_route_delete (gw_route); - nm_log_err (LOGD_DEVICE | LOGD_VPN | LOGD_IP6, - "(%s): failed to set IPv6 default route (pass #2): %d", - iface, err); - } else - success = TRUE; - -out: - if (gw_route) - rtnl_route_put (gw_route); - g_free (iface); - return success; -} - -/* - * nm_system_iface_flush_addresses - * - * Flush all network addresses associated with a network device - * - */ -gboolean -nm_system_iface_flush_addresses (int ifindex, int family) -{ - g_return_val_if_fail (ifindex > 0, FALSE); - return sync_addresses (ifindex, family, NULL, 0); -} - - -static struct rtnl_route * -delete_one_route (struct rtnl_route *route, - struct nl_addr *dst, - const char *iface, - gpointer user_data) -{ - guint32 log_level = GPOINTER_TO_UINT (user_data); - - nm_log_dbg (log_level, " deleting route"); - if (!nm_netlink_route_delete (route)) - nm_log_err (LOGD_DEVICE, "(%s): failed to delete route", iface); - - return NULL; -} - -/** - * nm_system_iface_flush_routes: - * @ifindex: interface index - * @family: address family, i.e. AF_INET, AF_INET6, or AF_UNSPEC - * - * Flush all network addresses associated with a network device. - * - * Returns: %TRUE on success, %FALSE on failure - **/ -gboolean -nm_system_iface_flush_routes (int ifindex, int family) -{ - guint32 log_level = LOGD_IP4 | LOGD_IP6; - const char *sf = "UNSPEC"; - char *iface; - - g_return_val_if_fail (ifindex > 0, FALSE); - - iface = nm_netlink_index_to_iface (ifindex); - g_return_val_if_fail (iface != NULL, FALSE); - - if (family == AF_INET) { - log_level = LOGD_IP4; - sf = "INET"; - } else if (family == AF_INET6) { - log_level = LOGD_IP6; - sf = "INET6"; - } - nm_log_dbg (log_level, "(%s): flushing routes ifindex %d family %s (%d)", - iface, ifindex, sf, family); - - /* We don't want to flush IPv6 link-local routes that may exist on the - * the interface since the LL address and routes should normally stay - * assigned all the time. - */ - nm_netlink_foreach_route (ifindex, family, RT_SCOPE_UNIVERSE, TRUE, delete_one_route, GUINT_TO_POINTER (log_level)); - - g_free (iface); - return TRUE; -} - -static struct rtnl_route * -find_route (struct rtnl_route *route, - struct nl_addr *dst, - const char *iface, - gpointer user_data) -{ - NMIP4Config *config = user_data; - struct in_addr *dst_addr; - int num; - int i; - - if (dst && (nl_addr_get_family (dst) != AF_INET)) - return NULL; - - /* Find the first route that handles a subnet of at least one of the - * device's IPv4 addresses. - */ - dst_addr = nl_addr_get_binary_addr (dst); - num = nm_ip4_config_get_num_addresses (config); - for (i = 0; i < num; i++) { - NMIP4Address *addr = nm_ip4_config_get_address (config, i); - guint32 prefix = nm_ip4_address_get_prefix (addr); - guint32 address = nm_ip4_address_get_address (addr); - - if ( prefix == nl_addr_get_prefixlen (dst) - && (address & nm_utils_ip4_prefix_to_netmask (prefix)) == dst_addr->s_addr) - return route; - } - return NULL; -} - -static void -nm_system_device_set_priority (int ifindex, - NMIP4Config *config, - int priority) -{ - struct nl_sock *nlh; - struct rtnl_route *found; - - found = nm_netlink_foreach_route (ifindex, AF_INET, RT_SCOPE_LINK, FALSE, find_route, config); - if (found) { - nlh = nm_netlink_get_default_handle (); - nm_netlink_route_delete (found); - rtnl_route_set_priority (found, priority); - rtnl_route_add (nlh, found, 0); - rtnl_route_put (found); - } -} - -static const struct { - const char *option; - const char *default_value; -} bonding_defaults[] = { - { "mode", "balance-rr" }, - { "arp_interval", "0" }, - { "miimon", "0" }, - - { "ad_select", "stable" }, - { "arp_validate", "none" }, - { "downdelay", "0" }, - { "fail_over_mac", "none" }, - { "lacp_rate", "slow" }, - { "min_links", "0" }, - { "num_grat_arp", "1" }, - { "num_unsol_na", "1" }, - { "primary", "" }, - { "primary_reselect", "always" }, - { "resend_igmp", "1" }, - { "updelay", "0" }, - { "use_carrier", "1" }, - { "xmit_hash_policy", "layer2" }, - { NULL, NULL } -}; - -static void -remove_bonding_entries (const char *iface, const char *path) -{ - char cmd[20]; - char *value, **entries; - gboolean ret; - int i; - - if (!g_file_get_contents (path, &value, NULL, NULL)) - return; - - entries = g_strsplit (value, " ", -1); - for (i = 0; entries[i]; i++) { - snprintf (cmd, sizeof (cmd), "-%s", g_strstrip (entries[i])); - ret = nm_utils_do_sysctl (path, cmd); - if (!ret) { - nm_log_warn (LOGD_HW, "(%s): failed to remove entry '%s' from '%s'", - iface, entries[i], path); - } - } - g_strfreev (entries); -} - -static gboolean -option_valid_for_nm_setting (const char *option, const char **valid_opts) -{ - while (*valid_opts) { - if (strcmp (option, *valid_opts) == 0) - return TRUE; - valid_opts++; - } - return FALSE; -} - -gboolean -nm_system_apply_bonding_config (const char *iface, NMSettingBond *s_bond) -{ - const char **valid_opts; - const char *option, *value; - char path[FILENAME_MAX]; - char *current, *space; - gboolean ret; - int i; - - g_return_val_if_fail (iface != NULL, FALSE); - - /* Remove old slaves and arp_ip_targets */ - snprintf (path, sizeof (path), "/sys/class/net/%s/bonding/arp_ip_target", iface); - remove_bonding_entries (iface, path); - snprintf (path, sizeof (path), "/sys/class/net/%s/bonding/slaves", iface); - remove_bonding_entries (iface, path); - - /* Apply config/defaults */ - valid_opts = nm_setting_bond_get_valid_options (s_bond); - for (i = 0; bonding_defaults[i].option; i++) { - option = bonding_defaults[i].option; - if (option_valid_for_nm_setting (option, valid_opts)) - value = nm_setting_bond_get_option_by_name (s_bond, option); - else - value = NULL; - if (!value) - value = bonding_defaults[i].default_value; - - snprintf (path, sizeof (path), "/sys/class/net/%s/bonding/%s", iface, option); - if (g_file_get_contents (path, ¤t, NULL, NULL)) { - g_strstrip (current); - space = strchr (current, ' '); - if (space) - *space = '\0'; - if (strcmp (current, value) != 0) { - ret = nm_utils_do_sysctl (path, value); - if (!ret) { - nm_log_warn (LOGD_HW, "(%s): failed to set bonding attribute " - "'%s' to '%s'", iface, option, value); - } - } - } - } - - /* Handle arp_ip_target */ - value = nm_setting_bond_get_option_by_name (s_bond, "arp_ip_target"); - if (value) { - char **addresses, cmd[20]; - - snprintf (path, sizeof (path), "/sys/class/net/%s/bonding/arp_ip_target", iface); - addresses = g_strsplit (value, ",", -1); - for (i = 0; addresses[i]; i++) { - snprintf (cmd, sizeof (cmd), "+%s", g_strstrip (addresses[i])); - ret = nm_utils_do_sysctl (path, cmd); - if (!ret) { - nm_log_warn (LOGD_HW, "(%s): failed to add arp_ip_target '%s'", - iface, addresses[i]); - } - } - g_strfreev (addresses); - } - - return TRUE; -} - -/** - * nm_system_add_bonding_master: - * @iface: the interface name for the new bond master - * - * Adds a virtual bonding device if it does not exist yet. - * - * Returns: %TRUE on success, %FALSE on failure - */ -gboolean -nm_system_add_bonding_master (const char *iface) -{ - struct nl_sock *sock; - int err; - - g_return_val_if_fail (iface != NULL, FALSE); - - /* When the kernel loads the bond module, either via explicit modprobe - * or automatically in response to creating a bond master, it will also - * create a 'bond0' interface. Since the bond we're about to create may - * or may not be named 'bond0' prevent potential confusion about a bond - * that the user didn't want by telling the bonding module not to create - * bond0 automatically. - */ - if (!g_file_test ("/sys/class/net/bonding_masters", G_FILE_TEST_EXISTS)) - nm_spawn_process ("modprobe bonding max_bonds=0"); - - sock = nm_netlink_get_default_handle (); - - /* Existing bonding devices with matching name will be reused */ - err = rtnl_link_bond_add (sock, iface, NULL); - if (err < 0) { - nm_log_err (LOGD_DEVICE, "(%s): error %d returned from " - "rtnl_link_bond_add(): %s", - iface, err, nl_geterror (err)); - return FALSE; - } - return TRUE; -} - -static gboolean -nm_system_bond_compat_enslave (const char *master_iface, const char *slave_iface) -{ - struct ifreq ifr; - int fd; - gboolean ret = FALSE; - - memset (&ifr, 0, sizeof (ifr)); - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_err (LOGD_DEVICE, "couldn't open control socket."); - return FALSE; - } - - strncpy (ifr.ifr_name, master_iface, IFNAMSIZ); - strncpy (ifr.ifr_slave, slave_iface, IFNAMSIZ); - - if (ioctl (fd, SIOCBONDENSLAVE, &ifr) < 0 && - ioctl (fd, BOND_ENSLAVE_OLD, &ifr) < 0) { - nm_log_err (LOGD_DEVICE, "(%s): error enslaving %s: %d (%s)", - master_iface, slave_iface, errno, strerror (errno)); - } else - ret = TRUE; - - close (fd); - - return ret; -} - -/** - * nm_system_bond_enslave: - * @master_ifindex: master device interface index - * @master_iface: master device interface name - * @slave_ifindex: slave device interface index - * @slave_iface: slave device interface name - * - * Enslaves the 'slave' to 'master. This function targets implementing a - * generic interface to attaching all kinds of slaves to masters. Currently - * only bonding is properly supported due to the backwards compatibility - * function being bonding specific. - * - * The slave device needs to be down as a prerequisite. - * - * Returns: %TRUE on success, or %FALSE - */ -gboolean -nm_system_bond_enslave (gint master_ifindex, - const char *master_iface, - gint slave_ifindex, - const char *slave_iface) -{ - struct nl_sock *sock; - int err; - - g_return_val_if_fail (master_ifindex > 0, FALSE); - g_return_val_if_fail (master_iface != NULL, FALSE); - g_return_val_if_fail (slave_ifindex > 0, FALSE); - g_return_val_if_fail (slave_iface != NULL, FALSE); - - sock = nm_netlink_get_default_handle (); - - if (!(nm_system_iface_get_flags (master_ifindex) & IFF_MASTER)) { - nm_log_err (LOGD_DEVICE, "(%s): interface is not a master", master_iface); - return FALSE; - } - - g_assert (!nm_system_iface_is_up (slave_ifindex)); - - if (nm_system_iface_get_flags (slave_ifindex) & IFF_SLAVE) { - struct rtnl_link *lk; - int existing_master = -1; - - /* Get the ifindex of the existing master device */ - lk = nm_netlink_index_to_rtnl_link (slave_ifindex); - g_warn_if_fail (lk != NULL); - if (lk) { - existing_master = rtnl_link_get_master (lk); - rtnl_link_put (lk); - } - - if (existing_master > 0) { - /* Fail if the device is already a slave of a different master */ - if (existing_master != master_ifindex) { - nm_log_err (LOGD_DEVICE, "(%s): already a slave of a different master", - slave_iface); - return FALSE; - } - - nm_log_dbg (LOGD_DEVICE, "(%s): %s is already enslaved", - master_iface, slave_iface); - return TRUE; - } - } - - err = rtnl_link_bond_enslave_ifindex (sock, master_ifindex, slave_ifindex); - if (err == -NLE_OPNOTSUPP) - return nm_system_bond_compat_enslave (master_iface, slave_iface); - - if (err < 0) { - nm_log_err (LOGD_DEVICE, "(%s): error enslaving %s: %d (%s)", - master_iface, slave_iface, err, nl_geterror (err)); - return FALSE; - } - - return TRUE; -} - -static gboolean -nm_system_bond_compat_release (const char *master_iface, const char *slave_iface) -{ - struct ifreq ifr; - int fd; - gboolean ret = FALSE; - - memset (&ifr, 0, sizeof (ifr)); - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_err (LOGD_DEVICE, "couldn't open control socket."); - return FALSE; - } - - strncpy (ifr.ifr_name, master_iface, IFNAMSIZ); - strncpy (ifr.ifr_slave, slave_iface, IFNAMSIZ); - - if (ioctl (fd, SIOCBONDRELEASE, &ifr) < 0 && - ioctl (fd, BOND_RELEASE_OLD, &ifr) < 0) { - nm_log_err (LOGD_DEVICE, "(%s): error releasing slave %s: %d (%s)", - master_iface, slave_iface, errno, strerror (errno)); - } else - ret = TRUE; - - close (fd); - return ret; -} - -/** - * nm_system_bond_release: - * @master_ifindex: master device interface index - * @master_iface: master device interface name - * @slave_ifindex: slave device interface index - * @slave_iface: slave device interface name - * - * Releases the 'slave' which is attached to 'master. This function targets - * implementing a generic interface to releasing all kinds of slaves. Currently - * only bonding is properly supported due to the backwards compatibility - * function being bonding specific. - * - * Returns: %TRUE on success, or %FALSE - */ -gboolean -nm_system_bond_release (gint master_ifindex, - const char *master_iface, - gint slave_ifindex, - const char *slave_iface) -{ - struct nl_sock *sock; - int err; - - g_return_val_if_fail (master_ifindex > 0, FALSE); - g_return_val_if_fail (master_iface != NULL, FALSE); - g_return_val_if_fail (slave_ifindex > 0, FALSE); - g_return_val_if_fail (slave_iface != NULL, FALSE); - - sock = nm_netlink_get_default_handle (); - - /* Only release if this is actually a slave */ - if (!(nm_system_iface_get_flags (slave_ifindex) & IFF_SLAVE)) - return TRUE; - - err = rtnl_link_bond_release_ifindex (sock, slave_ifindex); - if (err == -NLE_OPNOTSUPP) - return nm_system_bond_compat_release (master_iface, slave_iface); - else if (err < 0) { - nm_log_err (LOGD_DEVICE, "(%s): error releasing slave %s: %d (%s)", - master_iface, slave_iface, err, nl_geterror (err)); - return FALSE; - } - - return TRUE; -} - -/** - * nm_system_compat_get_iface_type: - * @ifindex: interface index - * @name: name of interface - * - * Lookup the type of an interface. At least one of @ifindex or @name must - * be provided. - * - * Returns: Interface type (NM_IFACE_TYPE_*) or NM_IFACE_TYPE_UNSPEC. - **/ -static int -nm_system_compat_get_iface_type (int ifindex, const char *name) -{ - int res = NM_IFACE_TYPE_UNSPEC; - char *ifname = NULL, *path = NULL; - struct vlan_ioctl_args ifv; - struct ifreq ifr; - struct ifbond ifb; - struct stat st; - int fd; - - g_return_val_if_fail (ifindex > 0 || name, NM_IFACE_TYPE_UNSPEC); - - if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { - nm_log_err (LOGD_DEVICE, "couldn't open control socket."); - goto out; - } - - if (!name) { - g_assert (ifindex > 0); - ifname = nm_netlink_index_to_iface (ifindex); - } - - /* Check VLAN */ - memset (&ifv, 0, sizeof (ifv)); - ifv.cmd = GET_VLAN_VID_CMD; - strncpy (ifv.device1, ifname ? ifname : name, sizeof (ifv.device1) - 1); - if (ioctl (fd, SIOCGIFVLAN, &ifv) == 0) { - res = NM_IFACE_TYPE_VLAN; - goto out; - } - - /* and bond */ - memset (&ifr, 0, sizeof (ifr)); - strncpy (ifr.ifr_name, ifname ? ifname : name, sizeof (ifr.ifr_name) - 1); - memset (&ifb, 0, sizeof (ifb)); - ifr.ifr_data = (caddr_t) &ifb; - if (ioctl (fd, SIOCBONDINFOQUERY, &ifr) == 0) { - res = NM_IFACE_TYPE_BOND; - goto out; - } - - /* and bridge */ - path = g_strdup_printf ("/sys/class/net/%s/bridge", ifname ? ifname : name); - if ((stat (path, &st) == 0) && S_ISDIR (st.st_mode)) { - res = NM_IFACE_TYPE_BRIDGE; - goto out; - } - -out: - g_free (path); - close (fd); - g_free (ifname); - return res; -} - -/** - * nm_system_get_iface_type: - * @ifindex: interface index - * @name: name of interface - * - * Lookup the type of an interface. At least one of @ifindex or @name must - * be provided. - * - * Returns: Interface type (NM_IFACE_TYPE_*) or NM_IFACE_TYPE_UNSPEC. - **/ -int -nm_system_get_iface_type (int ifindex, const char *name) -{ - struct rtnl_link *result; - struct nl_sock *nlh; - char *type; - int res = NM_IFACE_TYPE_UNSPEC; - int err; - - g_return_val_if_fail (ifindex > 0 || name, NM_IFACE_TYPE_UNSPEC); - - nlh = nm_netlink_get_default_handle (); - if (!nlh) - goto out; - - /* Prefer interface indexes to names */ - err = rtnl_link_get_kernel (nlh, ifindex, ifindex <= 0 ? name : NULL, &result); - if (err < 0) { - if (err == -NLE_OPNOTSUPP) - res = nm_system_compat_get_iface_type (ifindex, name); - goto out; - } - - type = rtnl_link_get_type (result); - - if (!g_strcmp0 (type, "bond")) - res = NM_IFACE_TYPE_BOND; - else if (!g_strcmp0 (type, "vlan")) - res = NM_IFACE_TYPE_VLAN; - else if (!g_strcmp0 (type, "bridge")) - res = NM_IFACE_TYPE_BRIDGE; - else if (!g_strcmp0 (type, "dummy")) - res = NM_IFACE_TYPE_DUMMY; - - rtnl_link_put (result); -out: - return res; -} - -/** - * nm_system_get_iface_vlan_info: - * @ifindex: the VLAN interface index - * @out_parent_ifindex: on success, the interface index of the parent interface of - * @iface - * @out_vlan_id: on success, the VLAN ID of @iface - * - * Gets the VLAN parent interface name and VLAN ID. - * - * Returns: %TRUE if the interface is a VLAN device and no error occurred; - * %FALSE if the interface was not a VLAN interface or an error occurred - **/ -gboolean -nm_system_get_iface_vlan_info (int ifindex, - int *out_parent_ifindex, - int *out_vlan_id) -{ - struct nl_sock *nlh; - struct rtnl_link *lk; - struct nl_cache *cache = NULL; - gboolean success = FALSE; - int ret; - - if (nm_system_get_iface_type (ifindex, NULL) != NM_IFACE_TYPE_VLAN) - return FALSE; - - nlh = nm_netlink_get_default_handle (); - if (!nlh) - return FALSE; - - ret = rtnl_link_alloc_cache (nlh, AF_UNSPEC, &cache); - g_return_val_if_fail (ret == 0, FALSE); - g_return_val_if_fail (cache != NULL, FALSE); - - lk = rtnl_link_get (cache, ifindex); - if (lk) { - if (out_parent_ifindex) - *out_parent_ifindex = rtnl_link_get_link (lk); - if (out_vlan_id) - *out_vlan_id = rtnl_link_vlan_get_id (lk); - - rtnl_link_put (lk); - success = TRUE; - } - - nl_cache_free (cache); - return success; -} - -static gboolean -nm_system_iface_compat_set_name (const char *old_name, const char *new_name) -{ - int fd; - struct ifreq ifr; - - if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { - nm_log_err (LOGD_DEVICE, "couldn't open control socket."); - return -1; - } - - memset (&ifr, 0, sizeof (struct ifreq)); - strncpy (ifr.ifr_name, old_name, sizeof (ifr.ifr_name)); - strncpy (ifr.ifr_newname, new_name, sizeof (ifr.ifr_newname)); - - if (ioctl (fd, SIOCSIFNAME, &ifr) < 0) { - nm_log_err (LOGD_DEVICE, "cann't change %s with %s.", old_name, new_name); - close (fd); - return FALSE; - } - - close (fd); - return TRUE; -} - -static gboolean -nm_system_iface_compat_set_vlan_name_type (int name_type) -{ - int fd; - struct vlan_ioctl_args if_request; - - if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { - nm_log_err (LOGD_DEVICE, "couldn't open control socket."); - return -1; - } - - memset (&if_request, 0, sizeof (struct vlan_ioctl_args)); - if_request.cmd = SET_VLAN_NAME_TYPE_CMD; - if_request.u.name_type = name_type; - - if (ioctl (fd, SIOCSIFVLAN, &if_request) < 0) { - nm_log_err (LOGD_DEVICE, "couldn't set name type."); - close (fd); - return FALSE; - } - - close (fd); - return TRUE; -} - -static gboolean -nm_system_iface_compat_add_vlan_device (const char *master, int vid) -{ - int fd; - struct vlan_ioctl_args if_request; - - g_return_val_if_fail (master, FALSE); - g_return_val_if_fail (vid < 4096, FALSE); - - /* - * use VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD as default, - * we will overwrite it with rtnl_link_set_name() later. - */ - if (!nm_system_iface_compat_set_vlan_name_type (VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD)) - return FALSE; - - if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { - nm_log_err (LOGD_DEVICE, "couldn't open control socket."); - return -1; - } - - memset (&if_request, 0, sizeof (struct vlan_ioctl_args)); - g_strlcpy (if_request.device1, master, sizeof (if_request.device1)); - if_request.cmd = ADD_VLAN_CMD; - if_request.u.VID = vid; - - if (ioctl (fd, SIOCSIFVLAN, &if_request) < 0) { - nm_log_err (LOGD_DEVICE, "couldn't add vlan device %s vid %d: %d.", master, vid, errno); - close (fd); - return FALSE; - } - - close (fd); - return TRUE; -} - -static gboolean -nm_system_iface_compat_rem_vlan_device (const char *iface) -{ - int fd; - struct vlan_ioctl_args if_request; - - if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { - nm_log_err (LOGD_DEVICE, "couldn't open control socket."); - return -1; - } - - memset (&if_request, 0, sizeof (struct vlan_ioctl_args)); - g_strlcpy (if_request.device1, iface, sizeof (if_request.device1)); - if_request.cmd = DEL_VLAN_CMD; - - if (ioctl (fd, SIOCSIFVLAN, &if_request) < 0) { - nm_log_err (LOGD_DEVICE, "couldn't rem vlan device %s.", iface); - close (fd); - return FALSE; - } - - close (fd); - return TRUE; -} - -static gboolean -nm_system_iface_compat_add_vlan (NMConnection *connection, - const char *iface, - int master_ifindex) -{ - NMSettingVlan *s_vlan; - int vlan_id; - guint32 vlan_flags = 0; - guint32 num, i, from, to; - int ifindex; - struct rtnl_link *new_link = NULL; - char *master = nm_netlink_index_to_iface (master_ifindex); - int itype; - gboolean created = FALSE; - - s_vlan = nm_connection_get_setting_vlan (connection); - g_return_val_if_fail (s_vlan, FALSE); - - vlan_id = nm_setting_vlan_get_id (s_vlan); - - if (!iface) { - iface = nm_connection_get_virtual_iface_name (connection); - g_return_val_if_fail (iface != NULL, FALSE); - } - - itype = nm_system_get_iface_type (-1, iface); - if (itype == NM_IFACE_TYPE_UNSPEC) { - char *name; - - /* Create the VLAN interface. Use VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD as - * default and change the name later via nm_system_iface_compat_set_name(). - * The old ioctl-based VLAN kernel API has no ability to directly return - * the new interface's name or index, so we have to create it with a - * known name and do the rename dance instead. - */ - if (!nm_system_iface_compat_add_vlan_device (master, vlan_id)) - return FALSE; - - /* And rename it to what the connection wants */ - name = nm_utils_new_vlan_name (master, vlan_id); - if (strcmp (name, iface) != 0) { - if (!nm_system_iface_compat_set_name (name, iface)) { - nm_system_iface_compat_rem_vlan_device (name); - g_free (name); - return FALSE; - } - } - g_free (name); - created = TRUE; - } else if (itype != NM_IFACE_TYPE_VLAN) { - nm_log_err (LOGD_DEVICE, "(%s): already exists but is not a VLAN interface.", iface); - return FALSE; - } else { - int tmp_vlan_id = -1, tmp_master_ifindex = -1; - - /* VLAN interface with this name already exists. Be a bit paranoid and - * double-check the VLAN ID and parent ifindex. - */ - ifindex = nm_netlink_iface_to_index (iface); - if (ifindex <= 0) - return FALSE; - - if (!nm_system_get_iface_vlan_info (ifindex, &tmp_master_ifindex, &tmp_vlan_id)) { - nm_log_err (LOGD_DEVICE, "(%s): failed to get VLAN interface info.", iface); - return FALSE; - } - - if (tmp_master_ifindex != master_ifindex) { - nm_log_err (LOGD_DEVICE, "(%s): master ifindex (%d) does match expected (%d).", - iface, tmp_master_ifindex, master_ifindex); - return FALSE; - } - - if (tmp_vlan_id != vlan_id) { - nm_log_err (LOGD_DEVICE, "(%s): VLAN ID %d does match expected ID %d.", - iface, tmp_vlan_id, vlan_id); - return FALSE; - } - - nm_log_dbg (LOGD_DEVICE, "(%s): found existing VLAN interface.", iface); - } - - ifindex = nm_netlink_iface_to_index (iface); - if (ifindex <= 0) - goto error; - - new_link = nm_netlink_index_to_rtnl_link (ifindex); - if (!new_link) - goto error; - - /* vconfig set_flag */ - vlan_flags = nm_setting_vlan_get_flags (s_vlan); - if (vlan_flags) { - if (rtnl_link_vlan_set_flags (new_link, vlan_flags)) - goto error_new_link; - } - - /* vconfig set_ingress_map */ - num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_INGRESS_MAP); - for (i = 0; i < num; i++) { - if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_INGRESS_MAP, i, &from, &to)) { - if (rtnl_link_vlan_set_ingress_map (new_link, from, to)) - goto error_new_link; - } - } - - /* vconfig set_egress_map */ - num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_EGRESS_MAP); - for (i = 0; i < num; i++) { - if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_EGRESS_MAP, i, &from, &to)) { - if (rtnl_link_vlan_set_egress_map (new_link, from, to)) - goto error_new_link; - } - } - - rtnl_link_put (new_link); - return TRUE; - -error_new_link: - rtnl_link_put (new_link); - /* fall through */ - -error: - if (created) - nm_system_iface_compat_rem_vlan_device (iface); - return FALSE; -} - -/** - * nm_system_add_vlan_iface: - * @connection: the #NMConnection that describes the VLAN interface - * @iface: the interface name of the new VLAN interface - * @parent_ifindex: the interface index of the new VLAN interface's master - * interface - * - * Add a VLAN device named @iface and specified in @connection. - * - * Returns: %TRUE on success, %FALSE on failure - */ -gboolean -nm_system_add_vlan_iface (NMConnection *connection, - const char *iface, - int parent_ifindex) -{ - NMSettingVlan *s_vlan; - int ret = -1; - struct rtnl_link *new_link = NULL; - struct nl_sock *nlh = NULL; - guint32 vlan_id = 0; - guint32 vlan_flags = 0; - guint32 num, i, from, to; - - g_return_val_if_fail (parent_ifindex > 0, FALSE); - - nlh = nm_netlink_get_default_handle (); - g_return_val_if_fail (nlh != NULL, FALSE); - - s_vlan = nm_connection_get_setting_vlan (connection); - g_return_val_if_fail (s_vlan, FALSE); - - vlan_id = nm_setting_vlan_get_id (s_vlan); - - if (!iface) { - iface = nm_connection_get_virtual_iface_name (connection); - g_return_val_if_fail (iface != NULL, FALSE); - } - - new_link = rtnl_link_alloc (); - if (!new_link) { - g_warn_if_fail (new_link != NULL); - goto out; - } - - ret = rtnl_link_set_type (new_link, "vlan"); - if (ret == -NLE_OPNOTSUPP) { - /* - * There is no linbl3, try ioctl. - */ - ret = -1; - if (nm_system_iface_compat_add_vlan (connection, iface, parent_ifindex)) - ret = 0; - goto out; - } - - rtnl_link_set_link (new_link, parent_ifindex); - rtnl_link_set_name (new_link, iface); - rtnl_link_vlan_set_id (new_link, vlan_id); - - vlan_flags = nm_setting_vlan_get_flags (s_vlan); - if (vlan_flags) { - guint kernel_flags = 0; - - if (vlan_flags & NM_VLAN_FLAG_REORDER_HEADERS) - kernel_flags |= VLAN_FLAG_REORDER_HDR; - if (vlan_flags & NM_VLAN_FLAG_GVRP) - kernel_flags |= VLAN_FLAG_GVRP; - if (vlan_flags & NM_VLAN_FLAG_LOOSE_BINDING) - kernel_flags |= VLAN_FLAG_LOOSE_BINDING; - - rtnl_link_vlan_set_flags (new_link, kernel_flags); - } - - num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_INGRESS_MAP); - for (i = 0; i < num; i++) { - if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_INGRESS_MAP, i, &from, &to)) - rtnl_link_vlan_set_ingress_map (new_link, (int) from, (int) to); - } - - num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_EGRESS_MAP); - for (i = 0; i < num; i++) { - if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_EGRESS_MAP, i, &from, &to)) - rtnl_link_vlan_set_egress_map (new_link, (int) from, (int) to); - } - - ret = rtnl_link_add (nlh, new_link, NLM_F_CREATE); - -out: - if (new_link) - rtnl_link_put (new_link); - return (ret == 0); -} - -/** - * nm_system_del_vlan_iface: - * @iface: the interface name - * - * Delete a VLAN interface specified by @iface. - * - * Returns: %TRUE on success, or %FALSE - */ -gboolean -nm_system_del_vlan_iface (const char *iface) -{ - int ret = 0; - struct nl_sock *nlh = NULL; - struct nl_cache *cache = NULL; - struct rtnl_link *new_link = NULL; - int itype; - - g_return_val_if_fail (iface != NULL, FALSE); - - itype = nm_system_get_iface_type (-1, iface); - g_return_val_if_fail (itype == NM_IFACE_TYPE_VLAN, FALSE); - - nlh = nm_netlink_get_default_handle (); - g_return_val_if_fail (nlh != NULL, FALSE); - - ret = rtnl_link_alloc_cache (nlh, AF_UNSPEC, &cache); - g_return_val_if_fail (ret == 0, FALSE); - g_return_val_if_fail (cache != NULL, FALSE); - - new_link = rtnl_link_get_by_name (cache, iface); - if (new_link) { - ret = rtnl_link_delete (nlh, new_link); - if (ret == -NLE_OPNOTSUPP) { - /* - * There is no linbl3, try ioctl. - */ - ret = -1; - if (nm_system_iface_compat_rem_vlan_device (iface)) - ret = 0; - } - } - - rtnl_link_put (new_link); - nl_cache_free (cache); - return (ret == 0) ? TRUE : FALSE; -} - -static int -_bridge_create_compat (const char *iface) -{ - int ret = 0, fd; - - if ((fd = socket (AF_LOCAL, SOCK_STREAM, 0)) < 0) { - nm_log_err (LOGD_DEVICE, "couldn't open control socket."); - return -EBADF; - } - - if (ioctl (fd, SIOCBRADDBR, iface) < 0) - ret = -errno; - - close (fd); - return ret; -} - -/** - * nm_system_create_bridge: - * @iface: Name bridging device to create - * @out_exists: on return, %TRUE if the bridge already exists - * - * Creates a new bridging device in the kernel. If a bridging device with - * the specified name already exists, it is reused and no error is returned, - * but @out_exists is set to %TRUE. - * - * Returns: %TRUE on success, %FALSE on error. - */ -gboolean -nm_system_create_bridge (const char *iface, gboolean *out_exists) -{ - int err; - - /* FIXME: use netlink */ - err = _bridge_create_compat (iface); - if (err < 0 && err != -EEXIST) { - nm_log_err (LOGD_DEVICE, "(%s): error while adding bridge: %s", - iface, strerror (-err)); - return FALSE; - } - if (out_exists && err == -EEXIST) - *out_exists = TRUE; - return TRUE; -} - -static int -_bridge_del_compat (const char *iface) -{ - int ret = 0, fd; - - if ((fd = socket (AF_LOCAL, SOCK_STREAM, 0)) < 0) { - nm_log_err (LOGD_DEVICE, "couldn't open control socket."); - return -EBADF; - } - - if (ioctl (fd, SIOCBRDELBR, iface) < 0) - ret = -errno; - - close (fd); - return ret; -} - -/** - * nm_system_del_bridge: - * @iface: Name of bridging device to delete - * - * Deletes the specified bridging device in the kernel. - * - * Returns: %TRUE on success, %FALSE on error. - */ -gboolean -nm_system_del_bridge (const char *iface) -{ - int err; - - /* FIXME: use netlink */ - err = _bridge_del_compat (iface); - if (err < 0 && err != -ENXIO) { - nm_log_err (LOGD_DEVICE, "(%s): error while deleting bridge: %s ", - iface, strerror (-err)); - return FALSE; - } - return TRUE; -} - -static int -_bridge_attach_compat (int master_ifindex, - const char *master_iface, - int slave_ifindex, - const char *slave_iface) -{ - int ret = 0, fd; - struct ifreq ifr; - - if ((fd = socket (AF_LOCAL, SOCK_STREAM, 0)) < 0) { - nm_log_err (LOGD_DEVICE, "couldn't open control socket."); - return -EBADF; - } - - memset (&ifr, 0, sizeof (ifr)); - strncpy (ifr.ifr_name, master_iface, IFNAMSIZ); - ifr.ifr_ifindex = slave_ifindex; - if (ioctl (fd, SIOCBRADDIF, &ifr) < 0) - ret = -errno; - - close (fd); - return ret; -} - -static int -_bridge_detach_compat (int master_ifindex, - const char *master_iface, - int slave_ifindex, - const char *slave_iface) -{ - int ret = 0, fd; - struct ifreq ifr; - - if ((fd = socket (AF_LOCAL, SOCK_STREAM, 0)) < 0) { - nm_log_err (LOGD_DEVICE, "couldn't open control socket."); - return -EBADF; - } - - memset (&ifr, 0, sizeof(ifr)); - strncpy (ifr.ifr_name, master_iface, IFNAMSIZ); - ifr.ifr_ifindex = slave_ifindex; - if (ioctl (fd, SIOCBRDELIF, &ifr) < 0) - ret = -errno; - - close (fd); - return ret; -} - -/** - * nm_system_bridge_attach: - * @master_ifindex: master device interface index - * @master_iface: master device interface name - * @slave_ifindex: slave device interface index - * @slave_iface: slave device interface name - * - * Attaches interface 'slave' to bridge 'master' - * - * Returns: %TRUE on success, or %FALSE - */ -gboolean -nm_system_bridge_attach (int master_ifindex, - const char *master_iface, - int slave_ifindex, - const char *slave_iface) -{ - char *mif = NULL, *sif = NULL; - int err = -1; - - g_return_val_if_fail (master_ifindex >= 0, FALSE); - g_return_val_if_fail (slave_ifindex >= 0, FALSE); - - if (!master_iface) { - mif = nm_netlink_index_to_iface (master_ifindex); - if (mif == NULL) { - nm_log_err (LOGD_DEVICE, "interface name lookup failed for index %d", master_ifindex); - goto out; - } - } - - if (!slave_ifindex) { - sif = nm_netlink_index_to_iface (slave_ifindex); - if (sif == NULL) { - nm_log_err (LOGD_DEVICE, "interface name lookup failed for index %d", slave_ifindex); - goto out; - } - } - - /* FIXME: long term plan is to use netlink for this */ - err = _bridge_attach_compat (master_ifindex, - mif ? mif : master_iface, - slave_ifindex, - sif ? sif : slave_iface); - if (err < 0) { - if (err == -EBUSY) { - /* Interface already attached to the given bridge */ - err = 0; - } else { - nm_log_err (LOGD_DEVICE, "(%s): failed to attach slave %s: %s", - master_iface, slave_iface, strerror (-err)); - } - } - -out: - g_free (sif); - g_free (mif); - return err == 0 ? TRUE : FALSE; -} - -/** - * nm_system_bridge_detach: - * @master_ifindex: master device interface index - * @master_iface: master device interface name - * @slave_ifindex: slave device interface index - * @slave_iface: slave device interface name - * - * Detaches the interface 'slave' from the bridge 'master'. - * - * Returns: %TRUE on success, or %FALSE - */ -gboolean -nm_system_bridge_detach (int master_ifindex, - const char *master_iface, - int slave_ifindex, - const char *slave_iface) -{ - char *mif = NULL, *sif = NULL; - int err = -1; - - g_return_val_if_fail (master_ifindex >= 0, FALSE); - g_return_val_if_fail (slave_ifindex >= 0, FALSE); - - if (!master_iface) { - mif = nm_netlink_index_to_iface (master_ifindex); - if (mif == NULL) { - nm_log_err (LOGD_DEVICE, "interface name lookup failed for index %d", master_ifindex); - goto out; - } - } - - if (!slave_ifindex) { - sif = nm_netlink_index_to_iface (slave_ifindex); - if (sif == NULL) { - nm_log_err (LOGD_DEVICE, "interface name lookup failed for index %d", slave_ifindex); - goto out; - } - } - - /* FIXME: long term plan is to use netlink for this */ - err = _bridge_detach_compat (master_ifindex, - mif ? mif : master_iface, - slave_ifindex, - sif ? sif : slave_iface); - /* Kernel doesn't return an error detaching an already-detached interface */ - if (err < 0) { - nm_log_err (LOGD_DEVICE, "(%s): failed to detach slave %s: %s", - master_iface, slave_iface, strerror (-err)); - } - -out: - g_free (mif); - g_free (sif); - return err == 0 ? TRUE : FALSE; -} |