diff options
Diffstat (limited to 'src/nm-system.c')
| -rw-r--r-- | src/nm-system.c | 192 |
1 files changed, 142 insertions, 50 deletions
diff --git a/src/nm-system.c b/src/nm-system.c index b980dc53..09e1da58 100644 --- a/src/nm-system.c +++ b/src/nm-system.c @@ -15,7 +15,7 @@ * 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 - 2010 Red Hat, Inc. + * 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> @@ -44,6 +44,7 @@ #include <linux/if.h> #include <linux/sockios.h> #include <linux/if_bonding.h> +#include <linux/if_vlan.h> #include "nm-system.h" #include "nm-device.h" @@ -1734,6 +1735,63 @@ nm_system_iface_release (gint master_ifindex, } /** + * nm_system_get_iface_type_compat: + * @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; + struct vlan_ioctl_args ifv; + struct ifreq ifr; + struct ifbond ifb; + 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; + } + +out: + close (fd); + g_free (ifname); + return res; +} + +/** * nm_system_get_iface_type: * @ifindex: interface index * @name: name of interface @@ -1750,6 +1808,7 @@ nm_system_get_iface_type (int ifindex, const char *name) 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); @@ -1758,8 +1817,12 @@ nm_system_get_iface_type (int ifindex, const char *name) goto out; /* Prefer interface indexes to names */ - if (rtnl_link_get_kernel (nlh, ifindex, ifindex < 0 ? name : NULL, &result) < 0) + 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); @@ -1901,7 +1964,7 @@ nm_system_iface_compat_add_vlan_device (const char *master, int vid) 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.", master, vid); + nm_log_err (LOGD_DEVICE, "couldn't add vlan device %s vid %d: %d.", master, vid, errno); close (fd); return FALSE; } @@ -1937,8 +2000,8 @@ nm_system_iface_compat_rem_vlan_device (const char *iface) static gboolean nm_system_iface_compat_add_vlan (NMConnection *connection, - const char *iface, - int master_ifindex) + const char *iface, + int master_ifindex) { NMSettingVlan *s_vlan; int vlan_id; @@ -1947,7 +2010,8 @@ nm_system_iface_compat_add_vlan (NMConnection *connection, int ifindex; struct rtnl_link *new_link = NULL; char *master = nm_netlink_index_to_iface (master_ifindex); - char *name = NULL; + int itype; + gboolean created = FALSE; s_vlan = nm_connection_get_setting_vlan (connection); g_return_val_if_fail (s_vlan, FALSE); @@ -1959,78 +2023,106 @@ nm_system_iface_compat_add_vlan (NMConnection *connection, g_return_val_if_fail (iface != NULL, FALSE); } - /* - * Use VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD as default, - * we will overwrite it with rtnl_link_set_name() later. - */ - name = nm_utils_new_vlan_name(master, vlan_id); + 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; - /* - * vconfig add - */ + /* 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_iface_compat_add_vlan_device (master, vlan_id)) - goto err_out; + 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; + } - /* - * get corresponding rtnl_link - */ + 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 (!nm_system_iface_compat_set_name (name, iface)) - goto err_out_delete_vlan_with_default_name; + 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 err_out; + goto error; new_link = nm_netlink_index_to_rtnl_link (ifindex); if (!new_link) - goto err_out_delete_vlan_with_default_name; + goto error; - /* - * vconfig set_flag - */ + /* vconfig set_flag */ vlan_flags = nm_setting_vlan_get_flags (s_vlan); - if (vlan_flags) + if (vlan_flags) { if (rtnl_link_vlan_set_flags (new_link, vlan_flags)) - goto err_out_delete_vlan_with_new_name; + goto error_new_link; + } - /* - * vconfig set_ingress_map - */ + /* 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 (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 err_out_delete_vlan_with_new_name; + goto error_new_link; + } } - /* - * vconfig set_egress_map - */ + /* 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 (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 err_out_delete_vlan_with_new_name; + goto error_new_link; + } } rtnl_link_put (new_link); return TRUE; -err_out: - g_free (name); - return FALSE; - -err_out_delete_vlan_with_default_name: - nm_system_iface_compat_rem_vlan_device (name); - g_free (name); - return FALSE; - -err_out_delete_vlan_with_new_name: +error_new_link: rtnl_link_put (new_link); - nm_system_iface_compat_rem_vlan_device (iface); - g_free (name); + /* fall through */ + +error: + if (created) + nm_system_iface_compat_rem_vlan_device (iface); return FALSE; } |