/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * 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, 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) 2013 Red Hat, Inc. */ #include "config.h" #include #include #include #include #include #include "nm-default.h" #include "nm-platform.h" #include "nm-linux-platform.h" #include "nm-fake-platform.h" #include "nm-utils.h" #define error(...) fprintf (stderr, __VA_ARGS__) typedef gboolean boolean_t; typedef int decimal_t; typedef const char *string_t; #define print_boolean(value) printf ("%s\n", value ? "yes" : "no") #define print_decimal(value) printf ("%d\n", value) #define print_string(value) printf ("%s\n", value) static gboolean do_sysctl_set (char **argv) { return nm_platform_sysctl_set (NM_PLATFORM_GET, argv[0], argv[1]); } static gboolean do_sysctl_get (char **argv) { gs_free char *value = nm_platform_sysctl_get (NM_PLATFORM_GET, argv[0]); printf ("%s\n", value); return !!value; } static int parse_ifindex (const char *str) { char *endptr; int ifindex = 0; ifindex = strtol (str, &endptr, 10); if (*endptr) { ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, str); } return ifindex; } static gboolean do_link_get_all (char **argv) { GArray *links; NMPlatformLink *device; int i; links = nm_platform_link_get_all (NM_PLATFORM_GET); for (i = 0; i < links->len; i++) { device = &g_array_index (links, NMPlatformLink, i); printf ("%d: %s type %d\n", device->ifindex, device->name, device->type); } g_array_unref (links); return TRUE; } static gboolean do_dummy_add (char **argv) { return nm_platform_dummy_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_bridge_add (char **argv) { return nm_platform_bridge_add (NM_PLATFORM_GET, argv[0], NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_bond_add (char **argv) { return nm_platform_bond_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_team_add (char **argv) { return nm_platform_team_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_vlan_add (char **argv) { const char *name = *argv++; int parent = parse_ifindex (*argv++); int vlanid = strtol (*argv++, NULL, 10); guint32 vlan_flags = strtol (*argv++, NULL, 10); return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent, vlanid, vlan_flags, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_link_exists (char **argv) { gboolean value = !!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, argv[0]); print_boolean (value); return TRUE; } #define LINK_CMD(cmdname) \ static gboolean \ do_link_##cmdname (char **argv) \ { \ int ifindex = parse_ifindex (argv[0]); \ return ifindex ? nm_platform_link_##cmdname (NM_PLATFORM_GET, ifindex) : FALSE; \ } #define LINK_CMD_GET_FULL(cmdname, type, cond) \ static gboolean \ do_link_##cmdname (char **argv) \ { \ int ifindex = parse_ifindex (argv[0]); \ if (ifindex) { \ type##_t value = nm_platform_link_##cmdname (NM_PLATFORM_GET, ifindex); \ if (cond) { \ print_##type (value); \ return TRUE; \ } \ } \ return FALSE; \ } #define LINK_CMD_GET(cmdname, type) LINK_CMD_GET_FULL (cmdname, type, TRUE); LINK_CMD (delete) /* do_link_delete_by_ifname: * * We don't need this as we allow ifname instead of ifindex anyway. */ static gboolean do_link_get_ifindex (char **argv) { int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, argv[0]); if (ifindex) printf ("%d\n", ifindex); return !!ifindex; } LINK_CMD_GET_FULL (get_name, string, value) LINK_CMD_GET_FULL (get_type, decimal, value > 0) LINK_CMD_GET (is_software, boolean) LINK_CMD_GET (supports_slaves, boolean) static gboolean do_link_set_up (char **argv) { int ifindex = parse_ifindex (argv[0]); return ifindex ? nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL) : FALSE; } LINK_CMD (set_down) LINK_CMD (set_arp) LINK_CMD (set_noarp) LINK_CMD_GET (is_up, boolean) LINK_CMD_GET (is_connected, boolean) LINK_CMD_GET (uses_arp, boolean) static gboolean do_link_set_address (char **argv) { int ifindex = parse_ifindex (*argv++); char *hex = *argv++; int hexlen = strlen (hex); char address[hexlen/2]; char *endptr; int i; g_assert (!(hexlen % 2)); for (i = 0; i < sizeof (address); i++) { char digit[3]; digit[0] = hex[2*i]; digit[1] = hex[2*i+1]; digit[2] = '\0'; address[i] = strtoul (digit, &endptr, 16); g_assert (!*endptr); } return nm_platform_link_set_address (NM_PLATFORM_GET, ifindex, address, sizeof (address)); } static gboolean do_link_get_address (char **argv) { int ifindex = parse_ifindex (*argv++); const char *address; size_t length; int i; address = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &length); if (!address || length <= 0) return FALSE; for (i = 0; i < length; i++) printf ("%02x", address[i]); printf ("\n"); return TRUE; } static gboolean do_link_set_mtu (char **argv) { int ifindex = parse_ifindex (*argv++); int mtu = strtoul (*argv++, NULL, 10); return nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, mtu); } LINK_CMD_GET (get_mtu, decimal); LINK_CMD_GET (supports_carrier_detect, boolean) LINK_CMD_GET (supports_vlans, boolean) static gboolean do_link_enslave (char **argv) { int master = parse_ifindex (*argv++); int slave = parse_ifindex (*argv++); return nm_platform_link_enslave (NM_PLATFORM_GET, master, slave); } static gboolean do_link_release (char **argv) { int master = parse_ifindex (*argv++); int slave = parse_ifindex (*argv++); return nm_platform_link_release (NM_PLATFORM_GET, master, slave); } LINK_CMD_GET (get_master, decimal) static gboolean do_master_set_option (char **argv) { int ifindex = parse_ifindex (*argv++); const char *option = *argv++; const char *value = *argv++; return nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, option, value); } static gboolean do_master_get_option (char **argv) { int ifindex = parse_ifindex (*argv++); const char *option = *argv++; gs_free char *value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, option); printf ("%s\n", value); return !!value; } static gboolean do_slave_set_option (char **argv) { int ifindex = parse_ifindex (*argv++); const char *option = *argv++; const char *value = *argv++; return nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, option, value); } static gboolean do_slave_get_option (char **argv) { int ifindex = parse_ifindex (*argv++); const char *option = *argv++; gs_free char *value = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex, option); printf ("%s\n", value); return !!value; } static gboolean do_vlan_get_info (char **argv) { int ifindex = parse_ifindex (*argv++); const NMPlatformLink *plink; const NMPlatformLnkVlan *plnk; plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, &plink); if (!plnk) return FALSE; printf ("%d %d\n", plink->parent, plnk->id); return TRUE; } static gboolean do_vlan_set_ingress_map (char **argv) { int ifindex = parse_ifindex (*argv++); int from = strtol (*argv++, NULL, 10); int to = strtol (*argv++, NULL, 10); return nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, from, to); } static gboolean do_vlan_set_egress_map (char **argv) { int ifindex = parse_ifindex (*argv++); int from = strtol (*argv++, NULL, 10); int to = strtol (*argv++, NULL, 10); return nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, from, to); } static gboolean do_tun_get_properties (char **argv) { int ifindex = parse_ifindex (*argv++); NMPlatformTunProperties props; if (!nm_platform_tun_get_properties (NM_PLATFORM_GET, ifindex, &props)) return FALSE; printf ("mode: %s\n", props.mode); if (props.owner == -1) printf ("owner: none\n"); else printf ("owner: %lu\n", (gulong) props.owner); if (props.group == -1) printf ("group: none\n"); else printf ("group: %lu\n", (gulong) props.group); printf ("no-pi: "); print_boolean (props.no_pi); printf ("vnet-hdr: "); print_boolean (props.vnet_hdr); printf ("multi-queue: "); print_boolean (props.multi_queue); return TRUE; } static gboolean do_macvlan_get_properties (char **argv) { int ifindex = parse_ifindex (*argv++); const NMPlatformLink *plink; const NMPlatformLnkMacvlan *props; props = nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, ifindex, &plink); if (!props) return FALSE; printf ("parent: %d\n", plink->parent); printf ("mode: %s\n", props->mode); printf ("no-promisc: "); print_boolean (props->no_promisc); return TRUE; } static gboolean do_vxlan_get_properties (char **argv) { int ifindex = parse_ifindex (*argv++); const NMPlatformLnkVxlan *props; char addrstr[INET6_ADDRSTRLEN]; props = nm_platform_link_get_lnk_vxlan (NM_PLATFORM_GET, ifindex, NULL); if (props) return FALSE; printf ("parent-ifindex: %u\n", props->parent_ifindex); printf ("id: %u\n", props->id); if (props->group) inet_ntop (AF_INET, &props->group, addrstr, sizeof (addrstr)); else if (props->group6.s6_addr[0]) inet_ntop (AF_INET6, &props->group6, addrstr, sizeof (addrstr)); else strcpy (addrstr, "-"); printf ("group: %s\n", addrstr); if (props->local) inet_ntop (AF_INET, &props->local, addrstr, sizeof (addrstr)); else if (props->local6.s6_addr[0]) inet_ntop (AF_INET6, &props->local6, addrstr, sizeof (addrstr)); else strcpy (addrstr, "-"); printf ("local: %s\n", addrstr); printf ("tos: %u\n", props->tos); printf ("ttl: %u\n", props->ttl); printf ("learning: "); print_boolean (props->learning); printf ("ageing: %u\n", props->ageing); printf ("limit: %u\n", props->limit); printf ("dst-port: %u\n", props->dst_port); printf ("src-port-min: %u\n", props->src_port_min); printf ("src-port-max: %u\n", props->src_port_max); printf ("proxy: "); print_boolean (props->proxy); printf ("rsc: "); print_boolean (props->rsc); printf ("l2miss: "); print_boolean (props->l2miss); printf ("l3miss: "); print_boolean (props->l3miss); return TRUE; } static gboolean do_gre_get_properties (char **argv) { int ifindex = parse_ifindex (*argv++); const NMPlatformLnkGre *props; char addrstr[INET_ADDRSTRLEN]; props = nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, ifindex, NULL); if (!props) return FALSE; printf ("parent-ifindex: %u\n", props->parent_ifindex); printf ("input-flags: %u\n", props->input_flags); printf ("output-flags: %u\n", props->input_flags); printf ("input-key: %u\n", props->input_key); printf ("output-key: %u\n", props->output_key); if (props->local) inet_ntop (AF_INET, &props->local, addrstr, sizeof (addrstr)); else strcpy (addrstr, "-"); printf ("local: %s\n", addrstr); if (props->remote) inet_ntop (AF_INET, &props->remote, addrstr, sizeof (addrstr)); else strcpy (addrstr, "-"); printf ("remote: %s\n", addrstr); printf ("ttl: %u\n", props->ttl); printf ("tos: %u\n", props->tos); printf ("path-mtu-discovery: "); print_boolean (props->path_mtu_discovery); return TRUE; } static gboolean do_ip4_address_get_all (char **argv) { int ifindex = parse_ifindex (argv[0]); GArray *addresses; NMPlatformIP4Address *address; int i; if (ifindex) { addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); for (i = 0; i < addresses->len; i++) { address = &g_array_index (addresses, NMPlatformIP4Address, i); printf ("%s", nm_utils_inet4_ntop (address->address, NULL)); if (address->address != address->peer_address) printf (" peer %s", nm_utils_inet4_ntop (address->peer_address, NULL)); printf ("/%d\n", address->plen); } g_array_unref (addresses); } return !!ifindex; } static gboolean do_ip6_address_get_all (char **argv) { int ifindex = parse_ifindex (argv[0]); GArray *addresses; NMPlatformIP6Address *address; char addrstr[INET6_ADDRSTRLEN]; int i; if (ifindex) { addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); for (i = 0; i < addresses->len; i++) { address = &g_array_index (addresses, NMPlatformIP6Address, i); inet_ntop (AF_INET6, &address->address, addrstr, sizeof (addrstr)); printf ("%s/%d\n", addrstr, address->plen); } g_array_unref (addresses); } return !!ifindex; } static gboolean parse_ip_address (int family, char *str, gpointer address, int *plen) { char *endptr; if (plen) *plen = 0; if (plen) { char *ptr = strchr (str, '/'); if (ptr) { *ptr++ = '\0'; *plen = strtol (ptr, &endptr, 10); if (*endptr) ptr = NULL; } if (!ptr) { error ("Bad format of IP address, expected address/plen.\n"); return FALSE; } } if (inet_pton (family, str, address)) return TRUE; error ("Bad format of IP address, expected address%s.\n", plen ? "/plen" : ""); return FALSE; } typedef in_addr_t ip4_t; typedef struct in6_addr ip6_t; #define parse_ip4_address(s, a, p) parse_ip_address (AF_INET, s, a, p) #define parse_ip6_address(s, a, p) parse_ip_address (AF_INET6, s, a, p) static gboolean do_ip4_address_add (char **argv) { int ifindex = parse_ifindex (*argv++); ip4_t address; int plen; if (ifindex && parse_ip4_address (*argv++, &address, &plen)) { guint32 lifetime = strtol (*argv++, NULL, 10); guint32 preferred = strtol (*argv++, NULL, 10); gboolean value = nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, address, plen, address, lifetime, preferred, NULL); return value; } else return FALSE; } static gboolean do_ip6_address_add (char **argv) { int ifindex = parse_ifindex (*argv++); ip6_t address; int plen; if (ifindex && parse_ip6_address (*argv++, &address, &plen)) { guint32 lifetime = strtol (*argv++, NULL, 10); guint32 preferred = strtol (*argv++, NULL, 10); guint flags = (*argv) ? rtnl_addr_str2flags (*argv++) : 0; gboolean value = nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, address, plen, in6addr_any, lifetime, preferred, flags); return value; } else return FALSE; } #define ADDR_CMD_FULL(v, cmdname, print, ...) \ static gboolean \ do_##v##_address_##cmdname (char **argv) \ { \ int ifindex = parse_ifindex (*argv++); \ v##_t address; \ int plen; \ if (ifindex && parse_##v##_address (*argv++, &address, &plen)) { \ gboolean value = !!nm_platform_##v##_address_##cmdname (NM_PLATFORM_GET, ifindex, address, plen, ##__VA_ARGS__); \ if (print) { \ print_boolean (value); \ return TRUE; \ } else \ return value; \ } else \ return FALSE; \ } #define ADDR_CMD(cmdname, ...) ADDR_CMD_FULL (ip4, cmdname, FALSE, address, ##__VA_ARGS__) ADDR_CMD_FULL (ip6, cmdname, FALSE) #define ADDR_CMD_PRINT(cmdname, ...) ADDR_CMD_FULL (ip4, cmdname, TRUE, ##__VA_ARGS__) ADDR_CMD_FULL (ip6, cmdname, TRUE) ADDR_CMD (delete) ADDR_CMD_PRINT (get, address) static gboolean do_ip4_route_get_all (char **argv) { int ifindex = parse_ifindex (argv[0]); GArray *routes; NMPlatformIP4Route *route; char networkstr[INET_ADDRSTRLEN], gatewaystr[INET_ADDRSTRLEN]; int i; if (ifindex) { routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); for (i = 0; i < routes->len; i++) { route = &g_array_index (routes, NMPlatformIP4Route, i); inet_ntop (AF_INET, &route->network, networkstr, sizeof (networkstr)); inet_ntop (AF_INET, &route->gateway, gatewaystr, sizeof (gatewaystr)); printf ("%s/%d via %s metric %d\n", networkstr, route->plen, gatewaystr, route->metric); } g_array_unref (routes); } return !!ifindex; } static gboolean do_ip6_route_get_all (char **argv) { int ifindex = parse_ifindex (argv[0]); GArray *routes; NMPlatformIP6Route *route; char networkstr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN]; int i; if (ifindex) { routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); for (i = 0; i < routes->len; i++) { route = &g_array_index (routes, NMPlatformIP6Route, i); inet_ntop (AF_INET6, &route->network, networkstr, sizeof (networkstr)); inet_ntop (AF_INET6, &route->gateway, gatewaystr, sizeof (gatewaystr)); printf ("%s/%d via %s metric %d\n", networkstr, route->plen, gatewaystr, route->metric); } g_array_unref (routes); } return !!ifindex; } static gboolean do_ip4_route_add (char **argv) { int ifindex = parse_ifindex (*argv++); in_addr_t network, gateway; int plen, metric, mss; parse_ip4_address (*argv++, &network, &plen); parse_ip4_address (*argv++, &gateway, NULL); metric = strtol (*argv++, NULL, 10); mss = strtol (*argv++, NULL, 10); return nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss); } static gboolean do_ip6_route_add (char **argv) { int ifindex = parse_ifindex (*argv++); struct in6_addr network, gateway; int plen, metric, mss; parse_ip6_address (*argv++, &network, &plen); parse_ip6_address (*argv++, &gateway, NULL); metric = strtol (*argv++, NULL, 10); mss = strtol (*argv++, NULL, 10); return nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss); } static gboolean do_ip4_route_delete (char **argv) { int ifindex = parse_ifindex (*argv++); in_addr_t network; int plen, metric; parse_ip4_address (*argv++, &network, &plen); metric = strtol (*argv++, NULL, 10); return nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric); } static gboolean do_ip6_route_delete (char **argv) { int ifindex = parse_ifindex (*argv++); struct in6_addr network; int plen, metric; parse_ip6_address (*argv++, &network, &plen); metric = strtol (*argv++, NULL, 10); return nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric); } static gboolean do_ip4_route_get (char **argv) { int ifindex = parse_ifindex (*argv++); in_addr_t network; int plen, metric; parse_ip4_address (*argv++, &network, &plen); metric = strtol (*argv++, NULL, 10); print_boolean (!!nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); return TRUE; } static gboolean do_ip6_route_get (char **argv) { int ifindex = parse_ifindex (*argv++); struct in6_addr network; int plen, metric; parse_ip6_address (*argv++, &network, &plen); metric = strtol (*argv++, NULL, 10); print_boolean (!!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); return TRUE; } typedef struct { const char *name; const char *help; int (*handler) (char **argv); int argc; const char *arghelp; } command_t; static const command_t commands[] = { { "sysctl-set", "get /proc/sys or /sys value", do_sysctl_set, 2, " " }, { "sysctl-get", "get /proc/sys or /sys value", do_sysctl_get, 1, "" }, { "link-get-all", "print all links", do_link_get_all, 0, "" }, { "dummy-add", "add dummy interface", do_dummy_add, 1, "" }, { "bridge-add", "add bridge interface", do_bridge_add, 1, "" }, { "bond-add", "add bond interface", do_bond_add, 1, "" }, { "team-add", "add team interface", do_team_add, 1, "" }, { "vlan-add", "add vlan interface", do_vlan_add, 4, " " }, { "link-exists", "check ifname for existance", do_link_exists, 1, "" }, { "link-delete", "delete interface", do_link_delete, 1, "" }, { "link-get-ifindex>", "get interface index", do_link_get_ifindex, 1, "" }, { "link-get-name", "get interface name", do_link_get_name, 1, "" }, { "link-get-type", "get interface type", do_link_get_type, 1, "" }, { "link-is-software", "check if interface is a software one", do_link_is_software, 1, "" }, { "link-supports-slaves", "check if interface supports slaves", do_link_supports_slaves, 1, "" }, { "link-set-up", "set interface up", do_link_set_up, 1, "" }, { "link-set-down", "set interface down", do_link_set_down, 1, "" }, { "link-set-arp", "activate interface arp", do_link_set_arp, 1, "" }, { "link-set-noarp", "deactivate interface arp", do_link_set_noarp, 1, "" }, { "link-is-up", "check if interface is up", do_link_is_up, 1, "" }, { "link-is-connected", "check interface carrier", do_link_is_connected, 1, "" }, { "link-uses-arp", "check whether interface uses arp", do_link_uses_arp, 1, "" }, { "link-get-address", "print link address", do_link_get_address, 1, "" }, { "link-set-address", "set link address", do_link_set_address, 2, " " }, { "link-get-mtu", "print link mtu", do_link_get_mtu, 1, "" }, { "link-set-mtu", "set link mtu", do_link_set_mtu, 2, " " }, { "link-supports-carrier-detect", "check whether interface supports carrier detect", do_link_supports_carrier_detect, 1, "" }, { "link-supports-vlans", "check whether interface supports VLANs", do_link_supports_vlans, 1, "" }, { "link-enslave", "enslave slave interface with master", do_link_enslave, 2, " " }, { "link-release", "release save interface from master", do_link_release, 2, " " }, { "link-get-master", "print master interface of a slave", do_link_get_master, 1, "" }, { "link-master-set-option", "set master option", do_master_set_option, 3, "