diff options
Diffstat (limited to 'src/platform/nm-linux-platform.c')
| -rw-r--r-- | src/platform/nm-linux-platform.c | 15981 |
1 files changed, 8143 insertions, 7838 deletions
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 2f33dc04..05125fa8 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2012 - 2018 Red Hat, Inc. */ @@ -56,17 +56,10 @@ #include <linux/pkt_cls.h> struct tc_defact { - tc_gen; + tc_gen; }; -enum { - TCA_DEF_UNSPEC, - TCA_DEF_TM, - TCA_DEF_PARMS, - TCA_DEF_DATA, - TCA_DEF_PAD, - __TCA_DEF_MAX -}; +enum { TCA_DEF_UNSPEC, TCA_DEF_TM, TCA_DEF_PARMS, TCA_DEF_DATA, TCA_DEF_PAD, __TCA_DEF_MAX }; #define TCA_DEF_MAX (__TCA_DEF_MAX - 1) /*****************************************************************************/ @@ -82,134 +75,135 @@ enum { /*****************************************************************************/ -#define IFQDISCSIZ 32 +#define IFQDISCSIZ 32 /*****************************************************************************/ #ifndef IFLA_PROMISCUITY -#define IFLA_PROMISCUITY 30 + #define IFLA_PROMISCUITY 30 #endif -#define IFLA_NUM_TX_QUEUES 31 -#define IFLA_NUM_RX_QUEUES 32 -#define IFLA_CARRIER 33 -#define IFLA_PHYS_PORT_ID 34 -#define IFLA_LINK_NETNSID 37 -#define __IFLA_MAX 39 - -#define IFLA_INET6_TOKEN 7 -#define IFLA_INET6_ADDR_GEN_MODE 8 -#define __IFLA_INET6_MAX 9 - -#define IFLA_VLAN_PROTOCOL 5 -#define __IFLA_VLAN_MAX 6 - -#define IFA_FLAGS 8 -#define __IFA_MAX 9 - -#define IFLA_MACVLAN_FLAGS 2 -#define __IFLA_MACVLAN_MAX 3 - -#define IFLA_IPTUN_LINK 1 -#define IFLA_IPTUN_LOCAL 2 -#define IFLA_IPTUN_REMOTE 3 -#define IFLA_IPTUN_TTL 4 -#define IFLA_IPTUN_TOS 5 -#define IFLA_IPTUN_ENCAP_LIMIT 6 -#define IFLA_IPTUN_FLOWINFO 7 -#define IFLA_IPTUN_FLAGS 8 -#define IFLA_IPTUN_PROTO 9 -#define IFLA_IPTUN_PMTUDISC 10 -#define __IFLA_IPTUN_MAX 19 +#define IFLA_NUM_TX_QUEUES 31 +#define IFLA_NUM_RX_QUEUES 32 +#define IFLA_CARRIER 33 +#define IFLA_PHYS_PORT_ID 34 +#define IFLA_LINK_NETNSID 37 +#define __IFLA_MAX 39 + +#define IFLA_INET6_TOKEN 7 +#define IFLA_INET6_ADDR_GEN_MODE 8 +#define __IFLA_INET6_MAX 9 + +#define IFLA_VLAN_PROTOCOL 5 +#define __IFLA_VLAN_MAX 6 + +#define IFA_FLAGS 8 +#define __IFA_MAX 9 + +#define IFLA_MACVLAN_FLAGS 2 +#define __IFLA_MACVLAN_MAX 3 + +#define IFLA_IPTUN_LINK 1 +#define IFLA_IPTUN_LOCAL 2 +#define IFLA_IPTUN_REMOTE 3 +#define IFLA_IPTUN_TTL 4 +#define IFLA_IPTUN_TOS 5 +#define IFLA_IPTUN_ENCAP_LIMIT 6 +#define IFLA_IPTUN_FLOWINFO 7 +#define IFLA_IPTUN_FLAGS 8 +#define IFLA_IPTUN_PROTO 9 +#define IFLA_IPTUN_PMTUDISC 10 +#define __IFLA_IPTUN_MAX 19 #ifndef IFLA_IPTUN_MAX -#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) + #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) #endif -#define IFLA_TUN_UNSPEC 0 -#define IFLA_TUN_OWNER 1 -#define IFLA_TUN_GROUP 2 -#define IFLA_TUN_TYPE 3 -#define IFLA_TUN_PI 4 -#define IFLA_TUN_VNET_HDR 5 -#define IFLA_TUN_PERSIST 6 -#define IFLA_TUN_MULTI_QUEUE 7 -#define IFLA_TUN_NUM_QUEUES 8 -#define IFLA_TUN_NUM_DISABLED_QUEUES 9 -#define __IFLA_TUN_MAX 10 -#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) - -G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); -#define RTA_PREF 20 -#undef RTA_MAX -#define RTA_MAX (MAX ((__RTA_MAX - 1), RTA_PREF)) +#define IFLA_TUN_UNSPEC 0 +#define IFLA_TUN_OWNER 1 +#define IFLA_TUN_GROUP 2 +#define IFLA_TUN_TYPE 3 +#define IFLA_TUN_PI 4 +#define IFLA_TUN_VNET_HDR 5 +#define IFLA_TUN_PERSIST 6 +#define IFLA_TUN_MULTI_QUEUE 7 +#define IFLA_TUN_NUM_QUEUES 8 +#define IFLA_TUN_NUM_DISABLED_QUEUES 9 +#define __IFLA_TUN_MAX 10 +#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) + +G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1)); +#define RTA_PREF 20 +#undef RTA_MAX +#define RTA_MAX (MAX((__RTA_MAX - 1), RTA_PREF)) #ifndef MACVLAN_FLAG_NOPROMISC -#define MACVLAN_FLAG_NOPROMISC 1 + #define MACVLAN_FLAG_NOPROMISC 1 #endif -#define IP6_FLOWINFO_TCLASS_MASK 0x0FF00000 -#define IP6_FLOWINFO_TCLASS_SHIFT 20 -#define IP6_FLOWINFO_FLOWLABEL_MASK 0x000FFFFF +#define IP6_FLOWINFO_TCLASS_MASK 0x0FF00000 +#define IP6_FLOWINFO_TCLASS_SHIFT 20 +#define IP6_FLOWINFO_FLOWLABEL_MASK 0x000FFFFF + +#define IFLA_BR_VLAN_STATS_ENABLED 41 /*****************************************************************************/ -/* Appeared in in kernel prior to 3.13 dated 19 January, 2014 */ +/* Appeared in the kernel prior to 3.13 dated 19 January, 2014 */ #ifndef ARPHRD_6LOWPAN -#define ARPHRD_6LOWPAN 825 + #define ARPHRD_6LOWPAN 825 #endif /*****************************************************************************/ -#define FRA_TUN_ID 12 -#define FRA_SUPPRESS_IFGROUP 13 -#define FRA_SUPPRESS_PREFIXLEN 14 -#define FRA_PAD 18 -#define FRA_L3MDEV 19 -#define FRA_UID_RANGE 20 -#define FRA_PROTOCOL 21 -#define FRA_IP_PROTO 22 -#define FRA_SPORT_RANGE 23 -#define FRA_DPORT_RANGE 24 +#define FRA_TUN_ID 12 +#define FRA_SUPPRESS_IFGROUP 13 +#define FRA_SUPPRESS_PREFIXLEN 14 +#define FRA_PAD 18 +#define FRA_L3MDEV 19 +#define FRA_UID_RANGE 20 +#define FRA_PROTOCOL 21 +#define FRA_IP_PROTO 22 +#define FRA_SPORT_RANGE 23 +#define FRA_DPORT_RANGE 24 /*****************************************************************************/ -#define IFLA_MACSEC_UNSPEC 0 -#define IFLA_MACSEC_SCI 1 -#define IFLA_MACSEC_PORT 2 -#define IFLA_MACSEC_ICV_LEN 3 -#define IFLA_MACSEC_CIPHER_SUITE 4 -#define IFLA_MACSEC_WINDOW 5 -#define IFLA_MACSEC_ENCODING_SA 6 -#define IFLA_MACSEC_ENCRYPT 7 -#define IFLA_MACSEC_PROTECT 8 -#define IFLA_MACSEC_INC_SCI 9 -#define IFLA_MACSEC_ES 10 -#define IFLA_MACSEC_SCB 11 -#define IFLA_MACSEC_REPLAY_PROTECT 12 -#define IFLA_MACSEC_VALIDATION 13 -#define IFLA_MACSEC_PAD 14 -#define __IFLA_MACSEC_MAX 15 +#define IFLA_MACSEC_UNSPEC 0 +#define IFLA_MACSEC_SCI 1 +#define IFLA_MACSEC_PORT 2 +#define IFLA_MACSEC_ICV_LEN 3 +#define IFLA_MACSEC_CIPHER_SUITE 4 +#define IFLA_MACSEC_WINDOW 5 +#define IFLA_MACSEC_ENCODING_SA 6 +#define IFLA_MACSEC_ENCRYPT 7 +#define IFLA_MACSEC_PROTECT 8 +#define IFLA_MACSEC_INC_SCI 9 +#define IFLA_MACSEC_ES 10 +#define IFLA_MACSEC_SCB 11 +#define IFLA_MACSEC_REPLAY_PROTECT 12 +#define IFLA_MACSEC_VALIDATION 13 +#define IFLA_MACSEC_PAD 14 +#define __IFLA_MACSEC_MAX 15 /*****************************************************************************/ #define WG_CMD_GET_DEVICE 0 #define WG_CMD_SET_DEVICE 1 -#define WGDEVICE_F_REPLACE_PEERS ((guint32) (1U << 0)) - -#define WGPEER_F_REMOVE_ME ((guint32) (1U << 0)) -#define WGPEER_F_REPLACE_ALLOWEDIPS ((guint32) (1U << 1)) +#define WGDEVICE_F_REPLACE_PEERS ((guint32)(1U << 0)) +#define WGPEER_F_REMOVE_ME ((guint32)(1U << 0)) +#define WGPEER_F_REPLACE_ALLOWEDIPS ((guint32)(1U << 1)) -#define WGDEVICE_A_UNSPEC 0 -#define WGDEVICE_A_IFINDEX 1 -#define WGDEVICE_A_IFNAME 2 -#define WGDEVICE_A_PRIVATE_KEY 3 -#define WGDEVICE_A_PUBLIC_KEY 4 -#define WGDEVICE_A_FLAGS 5 -#define WGDEVICE_A_LISTEN_PORT 6 -#define WGDEVICE_A_FWMARK 7 -#define WGDEVICE_A_PEERS 8 -#define WGDEVICE_A_MAX 8 +#define WGDEVICE_A_UNSPEC 0 +#define WGDEVICE_A_IFINDEX 1 +#define WGDEVICE_A_IFNAME 2 +#define WGDEVICE_A_PRIVATE_KEY 3 +#define WGDEVICE_A_PUBLIC_KEY 4 +#define WGDEVICE_A_FLAGS 5 +#define WGDEVICE_A_LISTEN_PORT 6 +#define WGDEVICE_A_FWMARK 7 +#define WGDEVICE_A_PEERS 8 +#define WGDEVICE_A_MAX 8 #define WGPEER_A_UNSPEC 0 #define WGPEER_A_PUBLIC_KEY 1 @@ -223,58 +217,58 @@ G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); #define WGPEER_A_ALLOWEDIPS 9 #define WGPEER_A_MAX 9 -#define WGALLOWEDIP_A_UNSPEC 0 -#define WGALLOWEDIP_A_FAMILY 1 -#define WGALLOWEDIP_A_IPADDR 2 -#define WGALLOWEDIP_A_CIDR_MASK 3 -#define WGALLOWEDIP_A_MAX 3 +#define WGALLOWEDIP_A_UNSPEC 0 +#define WGALLOWEDIP_A_FAMILY 1 +#define WGALLOWEDIP_A_IPADDR 2 +#define WGALLOWEDIP_A_CIDR_MASK 3 +#define WGALLOWEDIP_A_MAX 3 /*****************************************************************************/ /* Redefine VF enums and structures that are not available on older kernels. */ -#define IFLA_VF_UNSPEC 0 -#define IFLA_VF_MAC 1 -#define IFLA_VF_VLAN 2 -#define IFLA_VF_TX_RATE 3 -#define IFLA_VF_SPOOFCHK 4 -#define IFLA_VF_LINK_STATE 5 -#define IFLA_VF_RATE 6 -#define IFLA_VF_RSS_QUERY_EN 7 -#define IFLA_VF_STATS 8 -#define IFLA_VF_TRUST 9 -#define IFLA_VF_IB_NODE_GUID 10 -#define IFLA_VF_IB_PORT_GUID 11 -#define IFLA_VF_VLAN_LIST 12 - -#define IFLA_VF_VLAN_INFO_UNSPEC 0 -#define IFLA_VF_VLAN_INFO 1 +#define IFLA_VF_UNSPEC 0 +#define IFLA_VF_MAC 1 +#define IFLA_VF_VLAN 2 +#define IFLA_VF_TX_RATE 3 +#define IFLA_VF_SPOOFCHK 4 +#define IFLA_VF_LINK_STATE 5 +#define IFLA_VF_RATE 6 +#define IFLA_VF_RSS_QUERY_EN 7 +#define IFLA_VF_STATS 8 +#define IFLA_VF_TRUST 9 +#define IFLA_VF_IB_NODE_GUID 10 +#define IFLA_VF_IB_PORT_GUID 11 +#define IFLA_VF_VLAN_LIST 12 + +#define IFLA_VF_VLAN_INFO_UNSPEC 0 +#define IFLA_VF_VLAN_INFO 1 /* valid for TRUST, SPOOFCHK, LINK_STATE, RSS_QUERY_EN */ struct _ifla_vf_setting { - guint32 vf; - guint32 setting; + guint32 vf; + guint32 setting; }; struct _ifla_vf_rate { - guint32 vf; - guint32 min_tx_rate; - guint32 max_tx_rate; + guint32 vf; + guint32 min_tx_rate; + guint32 max_tx_rate; }; struct _ifla_vf_vlan_info { - guint32 vf; - guint32 vlan; /* 0 - 4095, 0 disables VLAN filter */ - guint32 qos; - guint16 vlan_proto; /* VLAN protocol, either 802.1Q or 802.1ad */ + guint32 vf; + guint32 vlan; /* 0 - 4095, 0 disables VLAN filter */ + guint32 qos; + guint16 vlan_proto; /* VLAN protocol, either 802.1Q or 802.1ad */ }; /*****************************************************************************/ -/* Appeared in in kernel 4.0 dated April 12, 2015 */ +/* Appeared in the kernel 4.0 dated April 12, 2015 */ #ifndef BRIDGE_VLAN_INFO_RANGE_BEGIN -#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1 << 3) /* VLAN is start of vlan range */ -#define BRIDGE_VLAN_INFO_RANGE_END (1 << 4) /* VLAN is end of vlan range */ + #define BRIDGE_VLAN_INFO_RANGE_BEGIN (1 << 3) /* VLAN is start of vlan range */ + #define BRIDGE_VLAN_INFO_RANGE_END (1 << 4) /* VLAN is end of vlan range */ #endif /*****************************************************************************/ @@ -284,300 +278,318 @@ struct _ifla_vf_vlan_info { /*****************************************************************************/ typedef enum { - INFINIBAND_ACTION_CREATE_CHILD, - INFINIBAND_ACTION_DELETE_CHILD, + INFINIBAND_ACTION_CREATE_CHILD, + INFINIBAND_ACTION_DELETE_CHILD, } InfinibandAction; typedef enum { - CHANGE_LINK_TYPE_UNSPEC, - CHANGE_LINK_TYPE_SET_MTU, - CHANGE_LINK_TYPE_SET_ADDRESS, + CHANGE_LINK_TYPE_UNSPEC, + CHANGE_LINK_TYPE_SET_MTU, + CHANGE_LINK_TYPE_SET_ADDRESS, } ChangeLinkType; typedef struct { - union { - struct { - gconstpointer address; - gsize length; - } set_address; - }; + union { + struct { + gconstpointer address; + gsize length; + } set_address; + }; } ChangeLinkData; typedef enum { - _REFRESH_ALL_TYPE_FIRST = 0, - - REFRESH_ALL_TYPE_LINKS = 0, - REFRESH_ALL_TYPE_IP4_ADDRESSES = 1, - REFRESH_ALL_TYPE_IP6_ADDRESSES = 2, - REFRESH_ALL_TYPE_IP4_ROUTES = 3, - REFRESH_ALL_TYPE_IP6_ROUTES = 4, - REFRESH_ALL_TYPE_ROUTING_RULES_IP4 = 5, - REFRESH_ALL_TYPE_ROUTING_RULES_IP6 = 6, - REFRESH_ALL_TYPE_QDISCS = 7, - REFRESH_ALL_TYPE_TFILTERS = 8, - - _REFRESH_ALL_TYPE_NUM, + _REFRESH_ALL_TYPE_FIRST = 0, + + REFRESH_ALL_TYPE_LINKS = 0, + REFRESH_ALL_TYPE_IP4_ADDRESSES = 1, + REFRESH_ALL_TYPE_IP6_ADDRESSES = 2, + REFRESH_ALL_TYPE_IP4_ROUTES = 3, + REFRESH_ALL_TYPE_IP6_ROUTES = 4, + REFRESH_ALL_TYPE_ROUTING_RULES_IP4 = 5, + REFRESH_ALL_TYPE_ROUTING_RULES_IP6 = 6, + REFRESH_ALL_TYPE_QDISCS = 7, + REFRESH_ALL_TYPE_TFILTERS = 8, + + _REFRESH_ALL_TYPE_NUM, } RefreshAllType; typedef struct { - NMPObjectType obj_type; + NMPObjectType obj_type; - /* for NLM_F_DUMP, which address family to request. */ - int addr_family; + /* for NLM_F_DUMP, which address family to request. */ + int addr_family; } RefreshAllInfo; typedef enum { - DELAYED_ACTION_TYPE_NONE = 0, - -#define F(val, name) ((sizeof (char[(((val)) == (name)) ? 1 : -1]) * 0) + (val)) - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS = 1 << F (0, REFRESH_ALL_TYPE_LINKS), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES = 1 << F (1, REFRESH_ALL_TYPE_IP4_ADDRESSES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = 1 << F (2, REFRESH_ALL_TYPE_IP6_ADDRESSES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = 1 << F (3, REFRESH_ALL_TYPE_IP4_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = 1 << F (4, REFRESH_ALL_TYPE_IP6_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 = 1 << F (5, REFRESH_ALL_TYPE_ROUTING_RULES_IP4), - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6 = 1 << F (6, REFRESH_ALL_TYPE_ROUTING_RULES_IP6), - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = 1 << F (7, REFRESH_ALL_TYPE_QDISCS), - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = 1 << F (8, REFRESH_ALL_TYPE_TFILTERS), + DELAYED_ACTION_TYPE_NONE = 0, + +#define F(val, name) ((sizeof(char[(((val)) == (name)) ? 1 : -1]) * 0) + (val)) + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS = 1 << F(0, REFRESH_ALL_TYPE_LINKS), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES = 1 << F(1, REFRESH_ALL_TYPE_IP4_ADDRESSES), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = 1 << F(2, REFRESH_ALL_TYPE_IP6_ADDRESSES), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = 1 << F(3, REFRESH_ALL_TYPE_IP4_ROUTES), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = 1 << F(4, REFRESH_ALL_TYPE_IP6_ROUTES), + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 = 1 + << F(5, REFRESH_ALL_TYPE_ROUTING_RULES_IP4), + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6 = 1 + << F(6, REFRESH_ALL_TYPE_ROUTING_RULES_IP6), + DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = 1 << F(7, REFRESH_ALL_TYPE_QDISCS), + DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = 1 << F(8, REFRESH_ALL_TYPE_TFILTERS), #undef F - DELAYED_ACTION_TYPE_REFRESH_LINK = 1 << 9, - DELAYED_ACTION_TYPE_MASTER_CONNECTED = 1 << 10, - DELAYED_ACTION_TYPE_READ_NETLINK = 1 << 11, - DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = 1 << 12, + DELAYED_ACTION_TYPE_REFRESH_LINK = 1 << 9, + DELAYED_ACTION_TYPE_MASTER_CONNECTED = 1 << 10, + DELAYED_ACTION_TYPE_READ_NETLINK = 1 << 11, + DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = 1 << 12, - __DELAYED_ACTION_TYPE_MAX, + __DELAYED_ACTION_TYPE_MAX, - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 | - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL = + DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 + | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, - DELAYED_ACTION_TYPE_REFRESH_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, + DELAYED_ACTION_TYPE_REFRESH_ALL = + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS + | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, - DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX -1, + DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX - 1, } DelayedActionType; -#define FOR_EACH_DELAYED_ACTION(iflags, flags_all) \ - for ((iflags) = (DelayedActionType) 0x1LL; \ - ({ \ - gboolean _good = FALSE; \ - \ - nm_assert (nm_utils_is_power_of_two (iflags)); \ - \ - while ((iflags) <= DELAYED_ACTION_TYPE_MAX) { \ - if (NM_FLAGS_ANY ((flags_all), (iflags))) { \ - _good = TRUE; \ - break; \ - } \ - (iflags) <<= 1; \ - } \ - _good; \ - }); \ - (iflags) <<= 1) +#define FOR_EACH_DELAYED_ACTION(iflags, flags_all) \ + for ((iflags) = (DelayedActionType) 0x1LL; ({ \ + gboolean _good = FALSE; \ + \ + nm_assert(nm_utils_is_power_of_two(iflags)); \ + \ + while ((iflags) <= DELAYED_ACTION_TYPE_MAX) { \ + if (NM_FLAGS_ANY((flags_all), (iflags))) { \ + _good = TRUE; \ + break; \ + } \ + (iflags) <<= 1; \ + } \ + _good; \ + }); \ + (iflags) <<= 1) typedef enum { - /* Negative values are errors from kernel. Add dummy member to - * make enum signed. */ - _WAIT_FOR_NL_RESPONSE_RESULT_SYSTEM_ERROR = G_MININT, - - WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN = 0, - WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK, - WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS, + /* Negative values are errors from kernel. Add dummy member to + * make enum signed. */ + _WAIT_FOR_NL_RESPONSE_RESULT_SYSTEM_ERROR = G_MININT, + + WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN = 0, + WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK, + WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS, } WaitForNlResponseResult; typedef enum { - DELAYED_ACTION_RESPONSE_TYPE_VOID = 0, - DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS = 1, - DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET = 2, + DELAYED_ACTION_RESPONSE_TYPE_VOID = 0, + DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS = 1, + DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET = 2, } DelayedActionWaitForNlResponseType; typedef struct { - guint32 seq_number; - WaitForNlResponseResult seq_result; - DelayedActionWaitForNlResponseType response_type; - gint64 timeout_abs_ns; - WaitForNlResponseResult *out_seq_result; - char **out_errmsg; - union { - int *out_refresh_all_in_progress; - NMPObject **out_route_get; - gpointer out_data; - } response; + guint32 seq_number; + WaitForNlResponseResult seq_result; + DelayedActionWaitForNlResponseType response_type; + gint64 timeout_abs_ns; + WaitForNlResponseResult * out_seq_result; + char ** out_errmsg; + union { + int * out_refresh_all_in_progress; + NMPObject **out_route_get; + gpointer out_data; + } response; } DelayedActionWaitForNlResponseData; /*****************************************************************************/ typedef struct { - struct nl_sock *genl; + struct nl_sock *genl; - struct nl_sock *nlh; + struct nl_sock *nlh; - GSource *event_source; + GSource *event_source; - guint32 nlh_seq_next; + guint32 nlh_seq_next; #if NM_MORE_LOGGING - guint32 nlh_seq_last_handled; + guint32 nlh_seq_last_handled; #endif - guint32 nlh_seq_last_seen; + guint32 nlh_seq_last_seen; - guint32 pruning[_REFRESH_ALL_TYPE_NUM]; + guint32 pruning[_REFRESH_ALL_TYPE_NUM]; - GHashTable *sysctl_get_prev_values; - CList sysctl_list; + GHashTable *sysctl_get_prev_values; + CList sysctl_list; - NMUdevClient *udev_client; + NMUdevClient *udev_client; - struct { - /* which delayed actions are scheduled, as marked in @flags. - * Some types have additional arguments in the fields below. */ - DelayedActionType flags; + struct { + /* which delayed actions are scheduled, as marked in @flags. + * Some types have additional arguments in the fields below. */ + DelayedActionType flags; - /* counter that a refresh all action is in progress, separated - * by type. */ - int refresh_all_in_progress[_REFRESH_ALL_TYPE_NUM]; + /* counter that a refresh all action is in progress, separated + * by type. */ + int refresh_all_in_progress[_REFRESH_ALL_TYPE_NUM]; - GPtrArray *list_master_connected; - GPtrArray *list_refresh_link; - GArray *list_wait_for_nl_response; + GPtrArray *list_master_connected; + GPtrArray *list_refresh_link; + GArray * list_wait_for_nl_response; - int is_handling; - } delayed_action; + int is_handling; + } delayed_action; } NMLinuxPlatformPrivate; struct _NMLinuxPlatform { - NMPlatform parent; - NMLinuxPlatformPrivate _priv; + NMPlatform parent; + NMLinuxPlatformPrivate _priv; }; struct _NMLinuxPlatformClass { - NMPlatformClass parent; + NMPlatformClass parent; }; -G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) +G_DEFINE_TYPE(NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) -#define NM_LINUX_PLATFORM_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMLinuxPlatform, NM_IS_LINUX_PLATFORM, NMPlatform) +#define NM_LINUX_PLATFORM_GET_PRIVATE(self) \ + _NM_GET_PRIVATE(self, NMLinuxPlatform, NM_IS_LINUX_PLATFORM, NMPlatform) /*****************************************************************************/ -#define _NMLOG_PREFIX_NAME "platform-linux" -#define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG2_DOMAIN LOGD_PLATFORM -#define _NMLOG(level, ...) _LOG ( level, _NMLOG_DOMAIN, platform, __VA_ARGS__) -#define _NMLOG_err(errsv, level, ...) _LOG_err (errsv, level, _NMLOG_DOMAIN, platform, __VA_ARGS__) -#define _NMLOG2(level, ...) _LOG ( level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) -#define _NMLOG2_err(errsv, level, ...) _LOG_err (errsv, level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) - -#define _LOG_print(__level, __domain, __errsv, self, ...) \ - G_STMT_START { \ - char __prefix[32]; \ - const char *__p_prefix = _NMLOG_PREFIX_NAME; \ - NMPlatform *const __self = (self); \ - \ - if (__self && nm_platform_get_log_with_ptr (__self)) { \ - g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ - __p_prefix = __prefix; \ - } \ - _nm_log (__level, __domain, __errsv, NULL, NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ - } G_STMT_END - -#define _LOG(level, domain, self, ...) \ - G_STMT_START { \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = (domain); \ - \ - if (nm_logging_enabled (__level, __domain)) { \ - _LOG_print (__level, __domain, 0, self, __VA_ARGS__); \ - } \ - } G_STMT_END - -#define _LOG_err(errsv, level, domain, self, ...) \ - G_STMT_START { \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = (domain); \ - \ - if (nm_logging_enabled (__level, __domain)) { \ - int __errsv = (errsv); \ - \ +#define _NMLOG_PREFIX_NAME "platform-linux" +#define _NMLOG_DOMAIN LOGD_PLATFORM +#define _NMLOG2_DOMAIN LOGD_PLATFORM +#define _NMLOG(level, ...) _LOG(level, _NMLOG_DOMAIN, platform, __VA_ARGS__) +#define _NMLOG_err(errsv, level, ...) _LOG_err(errsv, level, _NMLOG_DOMAIN, platform, __VA_ARGS__) +#define _NMLOG2(level, ...) _LOG(level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) +#define _NMLOG2_err(errsv, level, ...) _LOG_err(errsv, level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) + +#define _LOG_print(__level, __domain, __errsv, self, ...) \ + G_STMT_START \ + { \ + char __prefix[32]; \ + const char * __p_prefix = _NMLOG_PREFIX_NAME; \ + NMPlatform *const __self = (self); \ + \ + if (__self && nm_platform_get_log_with_ptr(__self)) { \ + g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ + __p_prefix = __prefix; \ + } \ + _nm_log(__level, \ + __domain, \ + __errsv, \ + NULL, \ + NULL, \ + "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + __p_prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + G_STMT_END + +#define _LOG(level, domain, self, ...) \ + G_STMT_START \ + { \ + const NMLogLevel __level = (level); \ + const NMLogDomain __domain = (domain); \ + \ + if (nm_logging_enabled(__level, __domain)) { \ + _LOG_print(__level, __domain, 0, self, __VA_ARGS__); \ + } \ + } \ + G_STMT_END + +#define _LOG_err(errsv, level, domain, self, ...) \ + G_STMT_START \ + { \ + const NMLogLevel __level = (level); \ + const NMLogDomain __domain = (domain); \ + \ + if (nm_logging_enabled(__level, __domain)) { \ + int __errsv = (errsv); \ + \ /* The %m format specifier (GNU extension) would already allow you to specify the error * message conveniently (and nm_log would get that right too). But we don't want to depend * on that, so instead append the message at the end. - * Currently users are expected not to use %m in the format string. */ \ - _LOG_print (__level, __domain, __errsv, self, \ - _NM_UTILS_MACRO_FIRST (__VA_ARGS__) ": %s (%d)" \ - _NM_UTILS_MACRO_REST (__VA_ARGS__), \ - nm_strerror_native (__errsv), __errsv); \ - } \ - } G_STMT_END + * Currently, users are expected not to use %m in the format string. */ \ + _LOG_print( \ + __level, \ + __domain, \ + __errsv, \ + self, \ + _NM_UTILS_MACRO_FIRST(__VA_ARGS__) ": %s (%d)" _NM_UTILS_MACRO_REST(__VA_ARGS__), \ + nm_strerror_native(__errsv), \ + __errsv); \ + } \ + } \ + G_STMT_END /*****************************************************************************/ -static void delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data); -static gboolean delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink); -static void do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const char *name); -static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type); -static void cache_on_change (NMPlatform *platform, - NMPCacheOpsType cache_op, - const NMPObject *obj_old, - const NMPObject *obj_new); -static void cache_prune_all (NMPlatform *platform); -static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks); -static struct nl_sock *_genl_sock (NMLinuxPlatform *platform); +static void +delayed_action_schedule(NMPlatform *platform, DelayedActionType action_type, gpointer user_data); +static gboolean delayed_action_handle_all(NMPlatform *platform, gboolean read_netlink); +static void do_request_link_no_delayed_actions(NMPlatform *platform, int ifindex, const char *name); +static void do_request_all_no_delayed_actions(NMPlatform *platform, DelayedActionType action_type); +static void cache_on_change(NMPlatform * platform, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new); +static void cache_prune_all(NMPlatform *platform); +static gboolean event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks); +static struct nl_sock *_genl_sock(NMLinuxPlatform *platform); /*****************************************************************************/ static int -wait_for_nl_response_to_nmerr (WaitForNlResponseResult seq_result) +wait_for_nl_response_to_nmerr(WaitForNlResponseResult seq_result) { - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - return 0; - if (seq_result < 0) - return (int) seq_result; - return -NME_PL_NETLINK; + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) + return 0; + if (seq_result < 0) + return (int) seq_result; + return -NME_PL_NETLINK; } static const char * -wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, - const char *errmsg, - char *buf, gsize buf_size) -{ - char *buf0 = buf; - - switch (seq_result) { - case WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN: - nm_utils_strbuf_append_str (&buf, &buf_size, "unknown"); - break; - case WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK: - nm_utils_strbuf_append_str (&buf, &buf_size, "success"); - break; - case WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN: - nm_utils_strbuf_append_str (&buf, &buf_size, "failure"); - break; - default: - if (seq_result < 0) { - nm_utils_strbuf_append (&buf, &buf_size, "failure %d (%s%s%s)", - -((int) seq_result), - nm_strerror_native (-((int) seq_result)), - errmsg ? " - " : "", - errmsg ?: ""); - } - else - nm_utils_strbuf_append (&buf, &buf_size, "internal failure %d", (int) seq_result); - break; - } - return buf0; +wait_for_nl_response_to_string(WaitForNlResponseResult seq_result, + const char * errmsg, + char * buf, + gsize buf_size) +{ + char *buf0 = buf; + + switch (seq_result) { + case WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN: + nm_utils_strbuf_append_str(&buf, &buf_size, "unknown"); + break; + case WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK: + nm_utils_strbuf_append_str(&buf, &buf_size, "success"); + break; + case WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN: + nm_utils_strbuf_append_str(&buf, &buf_size, "failure"); + break; + default: + if (seq_result < 0) { + nm_utils_strbuf_append(&buf, + &buf_size, + "failure %d (%s%s%s)", + -((int) seq_result), + nm_strerror_native(-((int) seq_result)), + errmsg ? " - " : "", + errmsg ?: ""); + } else + nm_utils_strbuf_append(&buf, &buf_size, "internal failure %d", (int) seq_result); + break; + } + return buf0; } /****************************************************************** @@ -585,22 +597,22 @@ wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, ******************************************************************/ static int -_vlan_qos_mapping_cmp_from (gconstpointer a, gconstpointer b, gpointer user_data) +_vlan_qos_mapping_cmp_from(gconstpointer a, gconstpointer b, gpointer user_data) { - const NMVlanQosMapping *map_a = a; - const NMVlanQosMapping *map_b = b; + const NMVlanQosMapping *map_a = a; + const NMVlanQosMapping *map_b = b; - if (map_a->from != map_b->from) - return map_a->from < map_b->from ? -1 : 1; - return 0; + if (map_a->from != map_b->from) + return map_a->from < map_b->from ? -1 : 1; + return 0; } static int -_vlan_qos_mapping_cmp_from_ptr (gconstpointer a, gconstpointer b, gpointer user_data) +_vlan_qos_mapping_cmp_from_ptr(gconstpointer a, gconstpointer b, gpointer user_data) { - return _vlan_qos_mapping_cmp_from (*((const NMVlanQosMapping **) a), - *((const NMVlanQosMapping **) b), - NULL); + return _vlan_qos_mapping_cmp_from(*((const NMVlanQosMapping **) a), + *((const NMVlanQosMapping **) b), + NULL); } /****************************************************************** @@ -608,222 +620,222 @@ _vlan_qos_mapping_cmp_from_ptr (gconstpointer a, gconstpointer b, gpointer user_ ******************************************************************/ typedef struct { - - const char *type_string; - - /* IFLA_INFO_KIND / rtnl_link_get_type() where applicable; the rtnl type - * should only be specified if the device type can be created without - * additional parameters, and if the device type can be determined from - * the rtnl_type. eg, tun/tap should not be specified since both - * tun and tap devices use "tun", and InfiniBand should not be - * specified because a PKey is required at creation. Drivers set this - * value from their 'struct rtnl_link_ops' structure. - */ - const char *rtnl_type; - - /* uevent DEVTYPE where applicable, from /sys/class/net/<ifname>/uevent; - * drivers set this value from their SET_NETDEV_DEV() call and the - * 'struct device_type' name member. - */ - const char *devtype; + const char *type_string; + + /* IFLA_INFO_KIND / rtnl_link_get_type() where applicable; the rtnl type + * should only be specified if the device type can be created without + * additional parameters, and if the device type can be determined from + * the rtnl_type. eg, tun/tap should not be specified since both + * tun and tap devices use "tun", and InfiniBand should not be + * specified because a PKey is required at creation. Drivers set this + * value from their 'struct rtnl_link_ops' structure. + */ + const char *rtnl_type; + + /* uevent DEVTYPE where applicable, from /sys/class/net/<ifname>/uevent; + * drivers set this value from their SET_NETDEV_DEV() call and the + * 'struct device_type' name member. + */ + const char *devtype; } LinkDesc; static const LinkDesc link_descs[] = { - [NM_LINK_TYPE_NONE] = { "none", NULL, NULL }, - [NM_LINK_TYPE_UNKNOWN] = { "unknown", NULL, NULL }, - [NM_LINK_TYPE_ANY] = { "any", NULL, NULL }, - - [NM_LINK_TYPE_ETHERNET] = { "ethernet", NULL, NULL }, - [NM_LINK_TYPE_INFINIBAND] = { "infiniband", NULL, NULL }, - [NM_LINK_TYPE_OLPC_MESH] = { "olpc-mesh", NULL, NULL }, - [NM_LINK_TYPE_WIFI] = { "wifi", NULL, "wlan" }, - [NM_LINK_TYPE_WWAN_NET] = { "wwan", NULL, "wwan" }, - [NM_LINK_TYPE_WIMAX] = { "wimax", "wimax", "wimax" }, - [NM_LINK_TYPE_WPAN] = { "wpan", NULL, NULL }, - [NM_LINK_TYPE_6LOWPAN] = { "6lowpan", NULL, NULL }, - - [NM_LINK_TYPE_BNEP] = { "bluetooth", NULL, "bluetooth" }, - [NM_LINK_TYPE_DUMMY] = { "dummy", "dummy", NULL }, - [NM_LINK_TYPE_GRE] = { "gre", "gre", NULL }, - [NM_LINK_TYPE_GRETAP] = { "gretap", "gretap", NULL }, - [NM_LINK_TYPE_IFB] = { "ifb", "ifb", NULL }, - [NM_LINK_TYPE_IP6TNL] = { "ip6tnl", "ip6tnl", NULL }, - [NM_LINK_TYPE_IP6GRE] = { "ip6gre", "ip6gre", NULL }, - [NM_LINK_TYPE_IP6GRETAP] = { "ip6gretap", "ip6gretap", NULL }, - [NM_LINK_TYPE_IPIP] = { "ipip", "ipip", NULL }, - [NM_LINK_TYPE_LOOPBACK] = { "loopback", NULL, NULL }, - [NM_LINK_TYPE_MACSEC] = { "macsec", "macsec", NULL }, - [NM_LINK_TYPE_MACVLAN] = { "macvlan", "macvlan", NULL }, - [NM_LINK_TYPE_MACVTAP] = { "macvtap", "macvtap", NULL }, - [NM_LINK_TYPE_OPENVSWITCH] = { "openvswitch", "openvswitch", NULL }, - [NM_LINK_TYPE_PPP] = { "ppp", NULL, "ppp" }, - [NM_LINK_TYPE_SIT] = { "sit", "sit", NULL }, - [NM_LINK_TYPE_TUN] = { "tun", "tun", NULL }, - [NM_LINK_TYPE_VETH] = { "veth", "veth", NULL }, - [NM_LINK_TYPE_VLAN] = { "vlan", "vlan", "vlan" }, - [NM_LINK_TYPE_VRF] = { "vrf", "vrf", "vrf" }, - [NM_LINK_TYPE_VXLAN] = { "vxlan", "vxlan", "vxlan" }, - [NM_LINK_TYPE_WIREGUARD] = { "wireguard", "wireguard", "wireguard" }, - - [NM_LINK_TYPE_BRIDGE] = { "bridge", "bridge", "bridge" }, - [NM_LINK_TYPE_BOND] = { "bond", "bond", "bond" }, - [NM_LINK_TYPE_TEAM] = { "team", "team", NULL }, + [NM_LINK_TYPE_NONE] = {"none", NULL, NULL}, + [NM_LINK_TYPE_UNKNOWN] = {"unknown", NULL, NULL}, + [NM_LINK_TYPE_ANY] = {"any", NULL, NULL}, + + [NM_LINK_TYPE_ETHERNET] = {"ethernet", NULL, NULL}, + [NM_LINK_TYPE_INFINIBAND] = {"infiniband", NULL, NULL}, + [NM_LINK_TYPE_OLPC_MESH] = {"olpc-mesh", NULL, NULL}, + [NM_LINK_TYPE_WIFI] = {"wifi", NULL, "wlan"}, + [NM_LINK_TYPE_WWAN_NET] = {"wwan", NULL, "wwan"}, + [NM_LINK_TYPE_WIMAX] = {"wimax", "wimax", "wimax"}, + [NM_LINK_TYPE_WPAN] = {"wpan", NULL, NULL}, + [NM_LINK_TYPE_6LOWPAN] = {"6lowpan", NULL, NULL}, + + [NM_LINK_TYPE_BNEP] = {"bluetooth", NULL, "bluetooth"}, + [NM_LINK_TYPE_DUMMY] = {"dummy", "dummy", NULL}, + [NM_LINK_TYPE_GRE] = {"gre", "gre", NULL}, + [NM_LINK_TYPE_GRETAP] = {"gretap", "gretap", NULL}, + [NM_LINK_TYPE_IFB] = {"ifb", "ifb", NULL}, + [NM_LINK_TYPE_IP6TNL] = {"ip6tnl", "ip6tnl", NULL}, + [NM_LINK_TYPE_IP6GRE] = {"ip6gre", "ip6gre", NULL}, + [NM_LINK_TYPE_IP6GRETAP] = {"ip6gretap", "ip6gretap", NULL}, + [NM_LINK_TYPE_IPIP] = {"ipip", "ipip", NULL}, + [NM_LINK_TYPE_LOOPBACK] = {"loopback", NULL, NULL}, + [NM_LINK_TYPE_MACSEC] = {"macsec", "macsec", NULL}, + [NM_LINK_TYPE_MACVLAN] = {"macvlan", "macvlan", NULL}, + [NM_LINK_TYPE_MACVTAP] = {"macvtap", "macvtap", NULL}, + [NM_LINK_TYPE_OPENVSWITCH] = {"openvswitch", "openvswitch", NULL}, + [NM_LINK_TYPE_PPP] = {"ppp", NULL, "ppp"}, + [NM_LINK_TYPE_SIT] = {"sit", "sit", NULL}, + [NM_LINK_TYPE_TUN] = {"tun", "tun", NULL}, + [NM_LINK_TYPE_VETH] = {"veth", "veth", NULL}, + [NM_LINK_TYPE_VLAN] = {"vlan", "vlan", "vlan"}, + [NM_LINK_TYPE_VRF] = {"vrf", "vrf", "vrf"}, + [NM_LINK_TYPE_VXLAN] = {"vxlan", "vxlan", "vxlan"}, + [NM_LINK_TYPE_WIREGUARD] = {"wireguard", "wireguard", "wireguard"}, + + [NM_LINK_TYPE_BRIDGE] = {"bridge", "bridge", "bridge"}, + [NM_LINK_TYPE_BOND] = {"bond", "bond", "bond"}, + [NM_LINK_TYPE_TEAM] = {"team", "team", NULL}, }; static const LinkDesc * -_link_desc_from_link_type (NMLinkType link_type) +_link_desc_from_link_type(NMLinkType link_type) { - nm_assert (_NM_INT_NOT_NEGATIVE (link_type)); - nm_assert (link_type < G_N_ELEMENTS (link_descs)); - nm_assert (link_descs[link_type].type_string); + nm_assert(_NM_INT_NOT_NEGATIVE(link_type)); + nm_assert(link_type < G_N_ELEMENTS(link_descs)); + nm_assert(link_descs[link_type].type_string); - return &link_descs[link_type]; + return &link_descs[link_type]; } static NMLinkType -_link_type_from_rtnl_type (const char *name) \ -{ - static const NMLinkType LIST[] = { - NM_LINK_TYPE_BOND, /* "bond" */ - NM_LINK_TYPE_BRIDGE, /* "bridge" */ - NM_LINK_TYPE_DUMMY, /* "dummy" */ - NM_LINK_TYPE_GRE, /* "gre" */ - NM_LINK_TYPE_GRETAP, /* "gretap" */ - NM_LINK_TYPE_IFB, /* "ifb" */ - NM_LINK_TYPE_IP6GRE, /* "ip6gre" */ - NM_LINK_TYPE_IP6GRETAP, /* "ip6gretap" */ - NM_LINK_TYPE_IP6TNL, /* "ip6tnl" */ - NM_LINK_TYPE_IPIP, /* "ipip" */ - NM_LINK_TYPE_MACSEC, /* "macsec" */ - NM_LINK_TYPE_MACVLAN, /* "macvlan" */ - NM_LINK_TYPE_MACVTAP, /* "macvtap" */ - NM_LINK_TYPE_OPENVSWITCH, /* "openvswitch" */ - NM_LINK_TYPE_SIT, /* "sit" */ - NM_LINK_TYPE_TEAM, /* "team" */ - NM_LINK_TYPE_TUN, /* "tun" */ - NM_LINK_TYPE_VETH, /* "veth" */ - NM_LINK_TYPE_VLAN, /* "vlan" */ - NM_LINK_TYPE_VRF, /* "vrf" */ - NM_LINK_TYPE_VXLAN, /* "vxlan" */ - NM_LINK_TYPE_WIMAX, /* "wimax" */ - NM_LINK_TYPE_WIREGUARD, /* "wireguard" */ - }; - - nm_assert (name); - - if (NM_MORE_ASSERT_ONCE (5)) { - int i, j, k; - - for (i = 0; i < G_N_ELEMENTS (LIST); i++) { - nm_assert (_link_desc_from_link_type (LIST[i]) == &link_descs[LIST[i]]); - nm_assert (link_descs[LIST[i]].rtnl_type); - if (i > 0) - nm_assert (strcmp (link_descs[LIST[i - 1]].rtnl_type, link_descs[LIST[i]].rtnl_type) < 0); - } - for (i = 0; i < G_N_ELEMENTS (link_descs); i++) { - if (!link_descs[i].rtnl_type) - continue; - for (j = 0, k = 0; j < G_N_ELEMENTS (LIST); j++) - k += (LIST[j] == i); - nm_assert (k == 1); - } - } - - { - int imin = 0; - int imax = (G_N_ELEMENTS (LIST) - 1); - int imid = (G_N_ELEMENTS (LIST) - 1) / 2; - - for (;;) { - const int cmp = strcmp (link_descs[LIST[imid]].rtnl_type, name); - - if (G_UNLIKELY (cmp == 0)) - return LIST[imid]; - - if (cmp < 0) - imin = imid + 1; - else - imax = imid - 1; - - if (G_UNLIKELY (imin > imax)) - return NM_LINK_TYPE_NONE; - - imid = (imin + imax) / 2; - } - } +_link_type_from_rtnl_type(const char *name) +{ + static const NMLinkType LIST[] = { + NM_LINK_TYPE_BOND, /* "bond" */ + NM_LINK_TYPE_BRIDGE, /* "bridge" */ + NM_LINK_TYPE_DUMMY, /* "dummy" */ + NM_LINK_TYPE_GRE, /* "gre" */ + NM_LINK_TYPE_GRETAP, /* "gretap" */ + NM_LINK_TYPE_IFB, /* "ifb" */ + NM_LINK_TYPE_IP6GRE, /* "ip6gre" */ + NM_LINK_TYPE_IP6GRETAP, /* "ip6gretap" */ + NM_LINK_TYPE_IP6TNL, /* "ip6tnl" */ + NM_LINK_TYPE_IPIP, /* "ipip" */ + NM_LINK_TYPE_MACSEC, /* "macsec" */ + NM_LINK_TYPE_MACVLAN, /* "macvlan" */ + NM_LINK_TYPE_MACVTAP, /* "macvtap" */ + NM_LINK_TYPE_OPENVSWITCH, /* "openvswitch" */ + NM_LINK_TYPE_SIT, /* "sit" */ + NM_LINK_TYPE_TEAM, /* "team" */ + NM_LINK_TYPE_TUN, /* "tun" */ + NM_LINK_TYPE_VETH, /* "veth" */ + NM_LINK_TYPE_VLAN, /* "vlan" */ + NM_LINK_TYPE_VRF, /* "vrf" */ + NM_LINK_TYPE_VXLAN, /* "vxlan" */ + NM_LINK_TYPE_WIMAX, /* "wimax" */ + NM_LINK_TYPE_WIREGUARD, /* "wireguard" */ + }; + + nm_assert(name); + + if (NM_MORE_ASSERT_ONCE(5)) { + int i, j, k; + + for (i = 0; i < G_N_ELEMENTS(LIST); i++) { + nm_assert(_link_desc_from_link_type(LIST[i]) == &link_descs[LIST[i]]); + nm_assert(link_descs[LIST[i]].rtnl_type); + if (i > 0) + nm_assert(strcmp(link_descs[LIST[i - 1]].rtnl_type, link_descs[LIST[i]].rtnl_type) + < 0); + } + for (i = 0; i < G_N_ELEMENTS(link_descs); i++) { + if (!link_descs[i].rtnl_type) + continue; + for (j = 0, k = 0; j < G_N_ELEMENTS(LIST); j++) + k += (LIST[j] == i); + nm_assert(k == 1); + } + } + + { + int imin = 0; + int imax = (G_N_ELEMENTS(LIST) - 1); + int imid = (G_N_ELEMENTS(LIST) - 1) / 2; + + for (;;) { + const int cmp = strcmp(link_descs[LIST[imid]].rtnl_type, name); + + if (G_UNLIKELY(cmp == 0)) + return LIST[imid]; + + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + + if (G_UNLIKELY(imin > imax)) + return NM_LINK_TYPE_NONE; + + imid = (imin + imax) / 2; + } + } } static NMLinkType -_link_type_from_devtype (const char *name) \ -{ - static const NMLinkType LIST[] = { - NM_LINK_TYPE_BNEP, /* "bluetooth" */ - NM_LINK_TYPE_BOND, /* "bond" */ - NM_LINK_TYPE_BRIDGE, /* "bridge" */ - NM_LINK_TYPE_PPP, /* "ppp" */ - NM_LINK_TYPE_VLAN, /* "vlan" */ - NM_LINK_TYPE_VRF, /* "vrf" */ - NM_LINK_TYPE_VXLAN, /* "vxlan" */ - NM_LINK_TYPE_WIMAX, /* "wimax" */ - NM_LINK_TYPE_WIREGUARD, /* "wireguard" */ - NM_LINK_TYPE_WIFI, /* "wlan" */ - NM_LINK_TYPE_WWAN_NET, /* "wwan" */ - }; - - nm_assert (name); - - if (NM_MORE_ASSERT_ONCE (5)) { - int i, j, k; - - for (i = 0; i < G_N_ELEMENTS (LIST); i++) { - nm_assert (_link_desc_from_link_type (LIST[i]) == &link_descs[LIST[i]]); - nm_assert (link_descs[LIST[i]].devtype); - if (i > 0) - nm_assert (strcmp (link_descs[LIST[i - 1]].devtype, link_descs[LIST[i]].devtype) < 0); - } - for (i = 0; i < G_N_ELEMENTS (link_descs); i++) { - if (!link_descs[i].devtype) - continue; - for (j = 0, k = 0; j < G_N_ELEMENTS (LIST); j++) - k += (LIST[j] == i); - nm_assert (k == 1); - } - } - - { - int imin = 0; - int imax = (G_N_ELEMENTS (LIST) - 1); - int imid = (G_N_ELEMENTS (LIST) - 1) / 2; - - for (;;) { - const int cmp = strcmp (link_descs[LIST[imid]].devtype, name); - - if (G_UNLIKELY (cmp == 0)) - return LIST[imid]; - - if (cmp < 0) - imin = imid + 1; - else - imax = imid - 1; - - if (G_UNLIKELY (imin > imax)) - return NM_LINK_TYPE_NONE; - - imid = (imin + imax) / 2; - } - } +_link_type_from_devtype(const char *name) +{ + static const NMLinkType LIST[] = { + NM_LINK_TYPE_BNEP, /* "bluetooth" */ + NM_LINK_TYPE_BOND, /* "bond" */ + NM_LINK_TYPE_BRIDGE, /* "bridge" */ + NM_LINK_TYPE_PPP, /* "ppp" */ + NM_LINK_TYPE_VLAN, /* "vlan" */ + NM_LINK_TYPE_VRF, /* "vrf" */ + NM_LINK_TYPE_VXLAN, /* "vxlan" */ + NM_LINK_TYPE_WIMAX, /* "wimax" */ + NM_LINK_TYPE_WIREGUARD, /* "wireguard" */ + NM_LINK_TYPE_WIFI, /* "wlan" */ + NM_LINK_TYPE_WWAN_NET, /* "wwan" */ + }; + + nm_assert(name); + + if (NM_MORE_ASSERT_ONCE(5)) { + int i, j, k; + + for (i = 0; i < G_N_ELEMENTS(LIST); i++) { + nm_assert(_link_desc_from_link_type(LIST[i]) == &link_descs[LIST[i]]); + nm_assert(link_descs[LIST[i]].devtype); + if (i > 0) + nm_assert(strcmp(link_descs[LIST[i - 1]].devtype, link_descs[LIST[i]].devtype) < 0); + } + for (i = 0; i < G_N_ELEMENTS(link_descs); i++) { + if (!link_descs[i].devtype) + continue; + for (j = 0, k = 0; j < G_N_ELEMENTS(LIST); j++) + k += (LIST[j] == i); + nm_assert(k == 1); + } + } + + { + int imin = 0; + int imax = (G_N_ELEMENTS(LIST) - 1); + int imid = (G_N_ELEMENTS(LIST) - 1) / 2; + + for (;;) { + const int cmp = strcmp(link_descs[LIST[imid]].devtype, name); + + if (G_UNLIKELY(cmp == 0)) + return LIST[imid]; + + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + + if (G_UNLIKELY(imin > imax)) + return NM_LINK_TYPE_NONE; + + imid = (imin + imax) / 2; + } + } } static const char * -nm_link_type_to_rtnl_type_string (NMLinkType link_type) +nm_link_type_to_rtnl_type_string(NMLinkType link_type) { - return _link_desc_from_link_type (link_type)->rtnl_type; + return _link_desc_from_link_type(link_type)->rtnl_type; } const char * -nm_link_type_to_string (NMLinkType link_type) +nm_link_type_to_string(NMLinkType link_type) { - return _link_desc_from_link_type (link_type)->type_string; + return _link_desc_from_link_type(link_type)->type_string; } /****************************************************************** @@ -841,80 +853,79 @@ nm_link_type_to_string (NMLinkType link_type) * the uint32 counter wraps every 497 days of uptime, so we have to compensate * for that. */ static gint64 -_timestamp_nl_to_ms (guint32 timestamp_nl, gint64 monotonic_ms) +_timestamp_nl_to_ms(guint32 timestamp_nl, gint64 monotonic_ms) { - const gint64 WRAP_INTERVAL = (((gint64) G_MAXUINT32) + 1) * (1000 / 100); - gint64 timestamp_nl_ms; + const gint64 WRAP_INTERVAL = (((gint64) G_MAXUINT32) + 1) * (1000 / 100); + gint64 timestamp_nl_ms; - /* convert timestamp from 1/100th of a second to msec. */ - timestamp_nl_ms = ((gint64) timestamp_nl) * (1000 / 100); + /* convert timestamp from 1/100th of a second to msec. */ + timestamp_nl_ms = ((gint64) timestamp_nl) * (1000 / 100); - /* timestamp wraps every 497 days. Try to compensate for that.*/ - if (timestamp_nl_ms > monotonic_ms) { - /* timestamp_nl_ms is in the future. Truncate it to *now* */ - timestamp_nl_ms = monotonic_ms; - } else if (monotonic_ms >= WRAP_INTERVAL) { - timestamp_nl_ms += (monotonic_ms / WRAP_INTERVAL) * WRAP_INTERVAL; - if (timestamp_nl_ms > monotonic_ms) - timestamp_nl_ms -= WRAP_INTERVAL; - } + /* timestamp wraps every 497 days. Try to compensate for that.*/ + if (timestamp_nl_ms > monotonic_ms) { + /* timestamp_nl_ms is in the future. Truncate it to *now* */ + timestamp_nl_ms = monotonic_ms; + } else if (monotonic_ms >= WRAP_INTERVAL) { + timestamp_nl_ms += (monotonic_ms / WRAP_INTERVAL) * WRAP_INTERVAL; + if (timestamp_nl_ms > monotonic_ms) + timestamp_nl_ms -= WRAP_INTERVAL; + } - return timestamp_nl_ms; + return timestamp_nl_ms; } static guint32 -_addrtime_timestamp_to_nm (guint32 timestamp, gint32 *out_now_nm) +_addrtime_timestamp_to_nm(guint32 timestamp, gint32 *out_now_nm) { - gint64 now_nl; - gint64 now_nm; - gint64 result; + gint64 now_nl; + gint64 now_nm; + gint64 result; - /* timestamp is unset. Default to 1. */ - if (!timestamp) { - NM_SET_OUT (out_now_nm, 0); - return 1; - } + /* timestamp is unset. Default to 1. */ + if (!timestamp) { + NM_SET_OUT(out_now_nm, 0); + return 1; + } - /* do all the calculations in milliseconds scale */ + /* do all the calculations in milliseconds scale */ - now_nm = nm_utils_get_monotonic_timestamp_msec (); - now_nl = nm_utils_clock_gettime_msec (CLOCK_MONOTONIC); + now_nm = nm_utils_get_monotonic_timestamp_msec(); + now_nl = nm_utils_clock_gettime_msec(CLOCK_MONOTONIC); - nm_assert (now_nm >= 1000); - nm_assert (now_nl >= 0); + nm_assert(now_nm >= 1000); + nm_assert(now_nl >= 0); - result = now_nm - (now_nl - _timestamp_nl_to_ms (timestamp, now_nl)); + result = now_nm - (now_nl - _timestamp_nl_to_ms(timestamp, now_nl)); - NM_SET_OUT (out_now_nm, now_nm / 1000); + NM_SET_OUT(out_now_nm, now_nm / 1000); - /* converting the timestamp into nm_utils_get_monotonic_timestamp_msec() scale is - * a good guess but fails in the following situations: - * - * - If the address existed before start of the process, the timestamp in nm scale would - * be negative or zero. In this case we default to 1. - * - during hibernation, the CLOCK_MONOTONIC/timestamp drifts from - * nm_utils_get_monotonic_timestamp_msec() scale. - */ - if (result <= 1000) - return 1; + /* converting the timestamp into nm_utils_get_monotonic_timestamp_msec() scale is + * a good guess but fails in the following situations: + * + * - If the address existed before start of the process, the timestamp in nm scale would + * be negative or zero. In this case we default to 1. + * - during hibernation, the CLOCK_MONOTONIC/timestamp drifts from + * nm_utils_get_monotonic_timestamp_msec() scale. + */ + if (result <= 1000) + return 1; - if (result > now_nm) - return now_nm / 1000; + if (result > now_nm) + return now_nm / 1000; - return result / 1000; + return result / 1000; } static guint32 -_addrtime_extend_lifetime (guint32 lifetime, guint32 seconds) +_addrtime_extend_lifetime(guint32 lifetime, guint32 seconds) { - guint64 v; + guint64 v; - if ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT - || seconds == 0) - return lifetime; + if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT || seconds == 0) + return lifetime; - v = (guint64) lifetime + (guint64) seconds; - return MIN (v, NM_PLATFORM_LIFETIME_PERMANENT - 1); + v = (guint64) lifetime + (guint64) seconds; + return MIN(v, NM_PLATFORM_LIFETIME_PERMANENT - 1); } /* The rtnl_addr object contains relative lifetimes @valid and @preferred @@ -930,61 +941,58 @@ _addrtime_extend_lifetime (guint32 lifetime, guint32 seconds) * via netlink, the lifetimes keep counting down. **/ static void -_addrtime_get_lifetimes (guint32 timestamp, - guint32 lifetime, - guint32 preferred, - guint32 *out_timestamp, - guint32 *out_lifetime, - guint32 *out_preferred) -{ - gint32 now; - - if ( lifetime != NM_PLATFORM_LIFETIME_PERMANENT - || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { - if (preferred > lifetime) - preferred = lifetime; - timestamp = _addrtime_timestamp_to_nm (timestamp, &now); - - if (now == 0) { - /* strange. failed to detect the last-update time and assumed that timestamp is 1. */ - nm_assert (timestamp == 1); - now = nm_utils_get_monotonic_timestamp_sec (); - } - if (timestamp < now) { - guint32 diff = now - timestamp; - - lifetime = _addrtime_extend_lifetime (lifetime, diff); - preferred = _addrtime_extend_lifetime (preferred, diff); - } else - nm_assert (timestamp == now); - } else - timestamp = 0; - *out_timestamp = timestamp; - *out_lifetime = lifetime; - *out_preferred = preferred; +_addrtime_get_lifetimes(guint32 timestamp, + guint32 lifetime, + guint32 preferred, + guint32 *out_timestamp, + guint32 *out_lifetime, + guint32 *out_preferred) +{ + gint32 now; + + if (lifetime != NM_PLATFORM_LIFETIME_PERMANENT || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { + if (preferred > lifetime) + preferred = lifetime; + timestamp = _addrtime_timestamp_to_nm(timestamp, &now); + + if (now == 0) { + /* strange. failed to detect the last-update time and assumed that timestamp is 1. */ + nm_assert(timestamp == 1); + now = nm_utils_get_monotonic_timestamp_sec(); + } + if (timestamp < now) { + guint32 diff = now - timestamp; + + lifetime = _addrtime_extend_lifetime(lifetime, diff); + preferred = _addrtime_extend_lifetime(preferred, diff); + } else + nm_assert(timestamp == now); + } else + timestamp = 0; + *out_timestamp = timestamp; + *out_lifetime = lifetime; + *out_preferred = preferred; } /*****************************************************************************/ static const NMPObject * -_lookup_cached_link (const NMPCache *cache, - int ifindex, - gboolean *completed_from_cache, - const NMPObject **link_cached) +_lookup_cached_link(const NMPCache * cache, + int ifindex, + gboolean * completed_from_cache, + const NMPObject **link_cached) { - const NMPObject *obj; + const NMPObject *obj; - nm_assert (completed_from_cache && link_cached); + nm_assert(completed_from_cache && link_cached); - if (!*completed_from_cache) { - obj = ifindex > 0 && cache - ? nmp_cache_lookup_link (cache, ifindex) - : NULL; + if (!*completed_from_cache) { + obj = ifindex > 0 && cache ? nmp_cache_lookup_link(cache, ifindex) : NULL; - *link_cached = obj; - *completed_from_cache = TRUE; - } - return *link_cached; + *link_cached = obj; + *completed_from_cache = TRUE; + } + return *link_cached; } /*****************************************************************************/ @@ -992,217 +1000,207 @@ _lookup_cached_link (const NMPCache *cache, #define DEVTYPE_PREFIX "DEVTYPE=" static char * -_linktype_read_devtype (int dirfd) -{ - gs_free char *contents = NULL; - char *cont, *end; - - nm_assert (dirfd >= 0); - - if (!nm_utils_file_get_contents (dirfd, - "uevent", - 1*1024*1024, - NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, - &contents, - NULL, - NULL, - NULL)) - return NULL; - for (cont = contents; cont; cont = end) { - end = strpbrk (cont, "\r\n"); - if (end) - *end++ = '\0'; - if (strncmp (cont, DEVTYPE_PREFIX, NM_STRLEN (DEVTYPE_PREFIX)) == 0) { - cont += NM_STRLEN (DEVTYPE_PREFIX); - memmove (contents, cont, strlen (cont) + 1); - return g_steal_pointer (&contents); - } - } - return NULL; +_linktype_read_devtype(int dirfd) +{ + gs_free char *contents = NULL; + char * cont, *end; + + nm_assert(dirfd >= 0); + + if (!nm_utils_file_get_contents(dirfd, + "uevent", + 1 * 1024 * 1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &contents, + NULL, + NULL, + NULL)) + return NULL; + for (cont = contents; cont; cont = end) { + end = strpbrk(cont, "\r\n"); + if (end) + *end++ = '\0'; + if (strncmp(cont, DEVTYPE_PREFIX, NM_STRLEN(DEVTYPE_PREFIX)) == 0) { + cont += NM_STRLEN(DEVTYPE_PREFIX); + memmove(contents, cont, strlen(cont) + 1); + return g_steal_pointer(&contents); + } + } + return NULL; } static NMLinkType -_linktype_get_type (NMPlatform *platform, - const NMPCache *cache, - const char *kind, - int ifindex, - const char *ifname, - unsigned flags, - unsigned arptype, - gboolean *completed_from_cache, - const NMPObject **link_cached, - const char **out_kind) -{ - NMLinkType link_type; - - NMTST_ASSERT_PLATFORM_NETNS_CURRENT (platform); - nm_assert (ifname); - nm_assert (_link_type_from_devtype ("wlan") == NM_LINK_TYPE_WIFI); - nm_assert (_link_type_from_rtnl_type ("bond") == NM_LINK_TYPE_BOND); - - if (completed_from_cache) { - const NMPObject *obj; - - obj = _lookup_cached_link (cache, ifindex, completed_from_cache, link_cached); - - /* If we detected the link type before, we stick to that - * decision unless the "kind" or "name" changed. If "name" changed, - * it means that their type may not have been determined correctly - * due to race conditions while accessing sysfs. - * - * This way, we save edditional ethtool/sysctl lookups, but moreover, - * we keep the linktype stable and don't change it as long as the link - * exists. - * - * Note that kernel *can* reuse the ifindex (on integer overflow, and - * when moving interfce to other netns). Thus here there is a tiny potential - * of messing stuff up. */ - if ( obj - && obj->_link.netlink.is_in_netlink - && !NM_IN_SET (obj->link.type, NM_LINK_TYPE_UNKNOWN, NM_LINK_TYPE_NONE) - && nm_streq (ifname, obj->link.name) - && ( !kind - || nm_streq0 (kind, obj->link.kind))) { - nm_assert (obj->link.kind == g_intern_string (obj->link.kind)); - *out_kind = obj->link.kind; - return obj->link.type; - } - } - - /* we intern kind to not require us to keep the pointer alive. Essentially - * leaking it in a global cache. That should be safe enough, because the - * kind comes only from kernel messages, which depend on the number of - * available drivers. So, there is not the danger that we leak uncontrolled - * many kinds. */ - *out_kind = g_intern_string (kind); - - if (kind) { - link_type = _link_type_from_rtnl_type (kind); - if (link_type != NM_LINK_TYPE_NONE) - return link_type; - } - - if (arptype == ARPHRD_LOOPBACK) - return NM_LINK_TYPE_LOOPBACK; - else if (arptype == ARPHRD_INFINIBAND) - return NM_LINK_TYPE_INFINIBAND; - else if (arptype == ARPHRD_SIT) - return NM_LINK_TYPE_SIT; - else if (arptype == ARPHRD_TUNNEL6) - return NM_LINK_TYPE_IP6TNL; - else if (arptype == ARPHRD_PPP) - return NM_LINK_TYPE_PPP; - else if (arptype == ARPHRD_IEEE802154) - return NM_LINK_TYPE_WPAN; - else if (arptype == ARPHRD_6LOWPAN) - return NM_LINK_TYPE_6LOWPAN; - - { - NMPUtilsEthtoolDriverInfo driver_info; - - /* Fallback OVS detection for kernel <= 3.16 */ - if (nmp_utils_ethtool_get_driver_info (ifindex, &driver_info)) { - if (nm_streq (driver_info.driver, "openvswitch")) - return NM_LINK_TYPE_OPENVSWITCH; - - if (arptype == 256) { - /* Some s390 CTC-type devices report 256 for the encapsulation type - * for some reason, but we need to call them Ethernet. - */ - if (nm_streq (driver_info.driver, "ctcm")) - return NM_LINK_TYPE_ETHERNET; - } - } - } - - { - nm_auto_close int dirfd = -1; - gs_free char *devtype = NULL; - char ifname_verified[IFNAMSIZ]; - - dirfd = nmp_utils_sysctl_open_netdir (ifindex, ifname, ifname_verified); - if (dirfd >= 0) { - if (faccessat (dirfd, "anycast_mask", F_OK, 0) == 0) - return NM_LINK_TYPE_OLPC_MESH; - - devtype = _linktype_read_devtype (dirfd); - if (devtype) { - link_type = _link_type_from_devtype (devtype); - if (link_type != NM_LINK_TYPE_NONE) { - if ( link_type == NM_LINK_TYPE_BNEP - && arptype != ARPHRD_ETHER) { - /* Both BNEP and 6lowpan use DEVTYPE=bluetooth, so we must - * use arptype to distinguish between them. - */ - } else - return link_type; - } - } - - /* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */ - if (nm_wifi_utils_is_wifi (dirfd, ifname_verified)) - return NM_LINK_TYPE_WIFI; - } - - if (arptype == ARPHRD_ETHER) { - /* Misc non-upstream WWAN drivers. rmnet is Qualcomm's proprietary - * modem interface, ccmni is MediaTek's. FIXME: these drivers should - * really set devtype=WWAN. - */ - if (g_str_has_prefix (ifname, "rmnet") || - g_str_has_prefix (ifname, "rev_rmnet") || - g_str_has_prefix (ifname, "ccmni")) - return NM_LINK_TYPE_WWAN_NET; - - /* Standard wired ethernet interfaces don't report an rtnl_link_type, so - * only allow fallback to Ethernet if no type is given. This should - * prevent future virtual network drivers from being treated as Ethernet - * when they should be Generic instead. - */ - if (!kind && !devtype) - return NM_LINK_TYPE_ETHERNET; - - /* The USB gadget interfaces behave and look like ordinary ethernet devices - * aside from the DEVTYPE. */ - if (nm_streq0 (devtype, "gadget")) - return NM_LINK_TYPE_ETHERNET; - - /* Distributed Switch Architecture switch chips */ - if (nm_streq0 (devtype, "dsa")) - return NM_LINK_TYPE_ETHERNET; - } - } - - return NM_LINK_TYPE_UNKNOWN; +_linktype_get_type(NMPlatform * platform, + const NMPCache * cache, + const char * kind, + int ifindex, + const char * ifname, + unsigned flags, + unsigned arptype, + gboolean * completed_from_cache, + const NMPObject **link_cached, + const char ** out_kind) +{ + NMLinkType link_type; + + NMTST_ASSERT_PLATFORM_NETNS_CURRENT(platform); + nm_assert(ifname); + nm_assert(_link_type_from_devtype("wlan") == NM_LINK_TYPE_WIFI); + nm_assert(_link_type_from_rtnl_type("bond") == NM_LINK_TYPE_BOND); + + if (completed_from_cache) { + const NMPObject *obj; + + obj = _lookup_cached_link(cache, ifindex, completed_from_cache, link_cached); + + /* If we detected the link type before, we stick to that + * decision unless the "kind" or "name" changed. If "name" changed, + * it means that their type may not have been determined correctly + * due to race conditions while accessing sysfs. + * + * This way, we save additional ethtool/sysctl lookups, but moreover, + * we keep the linktype stable and don't change it as long as the link + * exists. + * + * Note that kernel *can* reuse the ifindex (on integer overflow, and + * when moving interface to other netns). Thus here there is a tiny potential + * of messing stuff up. */ + if (obj && obj->_link.netlink.is_in_netlink + && !NM_IN_SET(obj->link.type, NM_LINK_TYPE_UNKNOWN, NM_LINK_TYPE_NONE) + && nm_streq(ifname, obj->link.name) && (!kind || nm_streq0(kind, obj->link.kind))) { + nm_assert(obj->link.kind == g_intern_string(obj->link.kind)); + *out_kind = obj->link.kind; + return obj->link.type; + } + } + + /* we intern kind to not require us to keep the pointer alive. Essentially + * leaking it in a global cache. That should be safe enough, because the + * kind comes only from kernel messages, which depend on the number of + * available drivers. So, there is not the danger that we leak uncontrolled + * many kinds. */ + *out_kind = g_intern_string(kind); + + if (kind) { + link_type = _link_type_from_rtnl_type(kind); + if (link_type != NM_LINK_TYPE_NONE) + return link_type; + } + + if (arptype == ARPHRD_LOOPBACK) + return NM_LINK_TYPE_LOOPBACK; + else if (arptype == ARPHRD_INFINIBAND) + return NM_LINK_TYPE_INFINIBAND; + else if (arptype == ARPHRD_SIT) + return NM_LINK_TYPE_SIT; + else if (arptype == ARPHRD_TUNNEL6) + return NM_LINK_TYPE_IP6TNL; + else if (arptype == ARPHRD_PPP) + return NM_LINK_TYPE_PPP; + else if (arptype == ARPHRD_IEEE802154) + return NM_LINK_TYPE_WPAN; + else if (arptype == ARPHRD_6LOWPAN) + return NM_LINK_TYPE_6LOWPAN; + + { + NMPUtilsEthtoolDriverInfo driver_info; + + /* Fallback OVS detection for kernel <= 3.16 */ + if (nmp_utils_ethtool_get_driver_info(ifindex, &driver_info)) { + if (nm_streq(driver_info.driver, "openvswitch")) + return NM_LINK_TYPE_OPENVSWITCH; + + if (arptype == 256) { + /* Some s390 CTC-type devices report 256 for the encapsulation type + * for some reason, but we need to call them Ethernet. + */ + if (nm_streq(driver_info.driver, "ctcm")) + return NM_LINK_TYPE_ETHERNET; + } + } + } + + { + nm_auto_close int dirfd = -1; + gs_free char * devtype = NULL; + char ifname_verified[IFNAMSIZ]; + + dirfd = nmp_utils_sysctl_open_netdir(ifindex, ifname, ifname_verified); + if (dirfd >= 0) { + if (faccessat(dirfd, "anycast_mask", F_OK, 0) == 0) + return NM_LINK_TYPE_OLPC_MESH; + + devtype = _linktype_read_devtype(dirfd); + if (devtype) { + link_type = _link_type_from_devtype(devtype); + if (link_type != NM_LINK_TYPE_NONE) { + if (link_type == NM_LINK_TYPE_BNEP && arptype != ARPHRD_ETHER) { + /* Both BNEP and 6lowpan use DEVTYPE=bluetooth, so we must + * use arptype to distinguish between them. + */ + } else + return link_type; + } + } + + /* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */ + if (nm_wifi_utils_is_wifi(dirfd, ifname_verified)) + return NM_LINK_TYPE_WIFI; + } + + if (arptype == ARPHRD_ETHER) { + /* Misc non-upstream WWAN drivers. rmnet is Qualcomm's proprietary + * modem interface, ccmni is MediaTek's. FIXME: these drivers should + * really set devtype=WWAN. + */ + if (g_str_has_prefix(ifname, "rmnet") || g_str_has_prefix(ifname, "rev_rmnet") + || g_str_has_prefix(ifname, "ccmni")) + return NM_LINK_TYPE_WWAN_NET; + + /* Standard wired ethernet interfaces don't report an rtnl_link_type, so + * only allow fallback to Ethernet if no type is given. This should + * prevent future virtual network drivers from being treated as Ethernet + * when they should be Generic instead. + */ + if (!kind && !devtype) + return NM_LINK_TYPE_ETHERNET; + + /* The USB gadget interfaces behave and look like ordinary ethernet devices + * aside from the DEVTYPE. */ + if (nm_streq0(devtype, "gadget")) + return NM_LINK_TYPE_ETHERNET; + + /* Distributed Switch Architecture switch chips */ + if (nm_streq0(devtype, "dsa")) + return NM_LINK_TYPE_ETHERNET; + } + } + + return NM_LINK_TYPE_UNKNOWN; } /****************************************************************** * libnl unility functions and wrappers ******************************************************************/ -#define NLMSG_TAIL(nmsg) \ - ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN ((nmsg)->nlmsg_len))) +#define NLMSG_TAIL(nmsg) ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) /* copied from iproute2's addattr_l(). */ static gboolean -_nl_addattr_l (struct nlmsghdr *n, - int maxlen, - int type, - const void *data, - int alen) +_nl_addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen) { - int len = RTA_LENGTH (alen); - struct rtattr *rta; + int len = RTA_LENGTH(alen); + struct rtattr *rta; - if (NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len) > maxlen) - return FALSE; + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) + return FALSE; - rta = NLMSG_TAIL (n); - rta->rta_type = type; - rta->rta_len = len; - memcpy (RTA_DATA (rta), data, alen); - n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len); - return TRUE; + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); + return TRUE; } /****************************************************************** @@ -1210,143 +1208,245 @@ _nl_addattr_l (struct nlmsghdr *n, ******************************************************************/ #define _check_addr_or_return_val(tb, attr, addr_len, ret_val) \ - ({ \ - const struct nlattr *__t = (tb)[(attr)]; \ - \ - if (__t) { \ - if (nla_len (__t) != (addr_len)) { \ - return ret_val; \ - } \ - } \ - !!__t; \ - }) + ({ \ + const struct nlattr *__t = (tb)[(attr)]; \ + \ + if (__t) { \ + if (nla_len(__t) != (addr_len)) { \ + return ret_val; \ + } \ + } \ + !!__t; \ + }) #define _check_addr_or_return_null(tb, attr, addr_len) \ - _check_addr_or_return_val (tb, attr, addr_len, NULL) + _check_addr_or_return_val(tb, attr, addr_len, NULL) /*****************************************************************************/ /* Copied and heavily modified from libnl3's inet6_parse_protinfo(). */ static gboolean -_parse_af_inet6 (NMPlatform *platform, - struct nlattr *attr, - NMUtilsIPv6IfaceId *out_token, - gboolean *out_token_valid, - guint8 *out_addr_gen_mode_inv, - gboolean *out_addr_gen_mode_valid) -{ - static const struct nla_policy policy[] = { - [IFLA_INET6_FLAGS] = { .type = NLA_U32 }, - [IFLA_INET6_CACHEINFO] = { .minlen = nm_offsetofend (struct ifla_cacheinfo, retrans_time) }, - [IFLA_INET6_CONF] = { .minlen = 4 }, - [IFLA_INET6_STATS] = { .minlen = 8 }, - [IFLA_INET6_ICMP6STATS] = { .minlen = 8 }, - [IFLA_INET6_TOKEN] = { .minlen = sizeof (struct in6_addr) }, - [IFLA_INET6_ADDR_GEN_MODE] = { .type = NLA_U8 }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - struct in6_addr i6_token; - gboolean token_valid = FALSE; - gboolean addr_gen_mode_valid = FALSE; - guint8 i6_addr_gen_mode_inv = 0; - - if (nla_parse_nested_arr (tb, attr, policy) < 0) - return FALSE; - - if (tb[IFLA_INET6_CONF] && nla_len (tb[IFLA_INET6_CONF]) % 4) - return FALSE; - if (tb[IFLA_INET6_STATS] && nla_len (tb[IFLA_INET6_STATS]) % 8) - return FALSE; - if (tb[IFLA_INET6_ICMP6STATS] && nla_len (tb[IFLA_INET6_ICMP6STATS]) % 8) - return FALSE; - - if (_check_addr_or_return_val (tb, IFLA_INET6_TOKEN, sizeof (struct in6_addr), FALSE)) { - nla_memcpy (&i6_token, tb[IFLA_INET6_TOKEN], sizeof (struct in6_addr)); - token_valid = TRUE; - } - - /* Hack to detect support addrgenmode of the kernel. We only parse - * netlink messages that we receive from kernel, hence this check - * is valid. */ - if (!_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) { - /* IFLA_INET6_ADDR_GEN_MODE was added in kernel 3.17, dated 5 October, 2014. */ - _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL, - tb[IFLA_INET6_ADDR_GEN_MODE] ? 1 : -1); - } - - if (tb[IFLA_INET6_ADDR_GEN_MODE]) { - i6_addr_gen_mode_inv = _nm_platform_uint8_inv (nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE])); - if (i6_addr_gen_mode_inv == 0) { - /* an inverse addrgenmode of zero is unexpected. We need to reserve zero - * to signal "unset". */ - return FALSE; - } - addr_gen_mode_valid = TRUE; - } - - if (token_valid) { - *out_token_valid = token_valid; - nm_utils_ipv6_interface_identifier_get_from_addr (out_token, &i6_token); - } - if (addr_gen_mode_valid) { - *out_addr_gen_mode_valid = addr_gen_mode_valid; - *out_addr_gen_mode_inv = i6_addr_gen_mode_inv; - } - return TRUE; +_parse_af_inet6(NMPlatform * platform, + struct nlattr * attr, + NMUtilsIPv6IfaceId *out_token, + gboolean * out_token_valid, + guint8 * out_addr_gen_mode_inv, + gboolean * out_addr_gen_mode_valid) +{ + static const struct nla_policy policy[] = { + [IFLA_INET6_FLAGS] = {.type = NLA_U32}, + [IFLA_INET6_CACHEINFO] = {.minlen = nm_offsetofend(struct ifla_cacheinfo, retrans_time)}, + [IFLA_INET6_CONF] = {.minlen = 4}, + [IFLA_INET6_STATS] = {.minlen = 8}, + [IFLA_INET6_ICMP6STATS] = {.minlen = 8}, + [IFLA_INET6_TOKEN] = {.minlen = sizeof(struct in6_addr)}, + [IFLA_INET6_ADDR_GEN_MODE] = {.type = NLA_U8}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + struct in6_addr i6_token; + gboolean token_valid = FALSE; + gboolean addr_gen_mode_valid = FALSE; + guint8 i6_addr_gen_mode_inv = 0; + + if (nla_parse_nested_arr(tb, attr, policy) < 0) + return FALSE; + + if (tb[IFLA_INET6_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4) + return FALSE; + if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8) + return FALSE; + if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8) + return FALSE; + + if (_check_addr_or_return_val(tb, IFLA_INET6_TOKEN, sizeof(struct in6_addr), FALSE)) { + nla_memcpy(&i6_token, tb[IFLA_INET6_TOKEN], sizeof(struct in6_addr)); + token_valid = TRUE; + } + + /* Hack to detect support addrgenmode of the kernel. We only parse + * netlink messages that we receive from kernel, hence this check + * is valid. */ + if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) { + /* IFLA_INET6_ADDR_GEN_MODE was added in kernel 3.17, dated 5 October, 2014. */ + _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL, + tb[IFLA_INET6_ADDR_GEN_MODE] ? 1 : -1); + } + + if (tb[IFLA_INET6_ADDR_GEN_MODE]) { + i6_addr_gen_mode_inv = _nm_platform_uint8_inv(nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE])); + if (i6_addr_gen_mode_inv == 0) { + /* an inverse addrgenmode of zero is unexpected. We need to reserve zero + * to signal "unset". */ + return FALSE; + } + addr_gen_mode_valid = TRUE; + } + + if (token_valid) { + *out_token_valid = token_valid; + nm_utils_ipv6_interface_identifier_get_from_addr(out_token, &i6_token); + } + if (addr_gen_mode_valid) { + *out_addr_gen_mode_valid = addr_gen_mode_valid; + *out_addr_gen_mode_inv = i6_addr_gen_mode_inv; + } + return TRUE; } /*****************************************************************************/ static NMPObject * -_parse_lnk_gre (const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_GRE_LINK] = { .type = NLA_U32 }, - [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, - [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, - [IFLA_GRE_IKEY] = { .type = NLA_U32 }, - [IFLA_GRE_OKEY] = { .type = NLA_U32 }, - [IFLA_GRE_LOCAL] = { .type = NLA_U32 }, - [IFLA_GRE_REMOTE] = { .type = NLA_U32 }, - [IFLA_GRE_TTL] = { .type = NLA_U8 }, - [IFLA_GRE_TOS] = { .type = NLA_U8 }, - [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - NMPObject *obj; - NMPlatformLnkGre *props; - gboolean is_tap; - - if ( !info_data - || !kind) - return NULL; - - if (nm_streq (kind, "gretap")) - is_tap = TRUE; - else if (nm_streq (kind, "gre")) - is_tap = FALSE; - else - return NULL; - - if (nla_parse_nested_arr (tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new (is_tap ? NMP_OBJECT_TYPE_LNK_GRETAP : NMP_OBJECT_TYPE_LNK_GRE, NULL); - props = &obj->lnk_gre; - - props->parent_ifindex = tb[IFLA_GRE_LINK] ? nla_get_u32 (tb[IFLA_GRE_LINK]) : 0; - props->input_flags = tb[IFLA_GRE_IFLAGS] ? ntohs (nla_get_u16 (tb[IFLA_GRE_IFLAGS])) : 0; - props->output_flags = tb[IFLA_GRE_OFLAGS] ? ntohs (nla_get_u16 (tb[IFLA_GRE_OFLAGS])) : 0; - props->input_key = tb[IFLA_GRE_IKEY] ? ntohl (nla_get_u32 (tb[IFLA_GRE_IKEY])) : 0; - props->output_key = tb[IFLA_GRE_OKEY] ? ntohl (nla_get_u32 (tb[IFLA_GRE_OKEY])) : 0; - props->local = tb[IFLA_GRE_LOCAL] ? nla_get_u32 (tb[IFLA_GRE_LOCAL]) : 0; - props->remote = tb[IFLA_GRE_REMOTE] ? nla_get_u32 (tb[IFLA_GRE_REMOTE]) : 0; - props->tos = tb[IFLA_GRE_TOS] ? nla_get_u8 (tb[IFLA_GRE_TOS]) : 0; - props->ttl = tb[IFLA_GRE_TTL] ? nla_get_u8 (tb[IFLA_GRE_TTL]) : 0; - props->path_mtu_discovery = !tb[IFLA_GRE_PMTUDISC] || !!nla_get_u8 (tb[IFLA_GRE_PMTUDISC]); - props->is_tap = is_tap; - - return obj; +_parse_lnk_bridge(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_BR_FORWARD_DELAY] = {.type = NLA_U32}, + [IFLA_BR_HELLO_TIME] = {.type = NLA_U32}, + [IFLA_BR_MAX_AGE] = {.type = NLA_U32}, + [IFLA_BR_AGEING_TIME] = {.type = NLA_U32}, + [IFLA_BR_STP_STATE] = {.type = NLA_U32}, + [IFLA_BR_PRIORITY] = {.type = NLA_U16}, + [IFLA_BR_VLAN_PROTOCOL] = {.type = NLA_U16}, + [IFLA_BR_VLAN_STATS_ENABLED] = {.type = NLA_U8}, + [IFLA_BR_GROUP_FWD_MASK] = {.type = NLA_U16}, + [IFLA_BR_GROUP_ADDR] = {.minlen = sizeof(NMEtherAddr)}, + [IFLA_BR_MCAST_SNOOPING] = {.type = NLA_U8}, + [IFLA_BR_MCAST_ROUTER] = {.type = NLA_U8}, + [IFLA_BR_MCAST_QUERY_USE_IFADDR] = {.type = NLA_U8}, + [IFLA_BR_MCAST_QUERIER] = {.type = NLA_U8}, + [IFLA_BR_MCAST_HASH_MAX] = {.type = NLA_U32}, + [IFLA_BR_MCAST_LAST_MEMBER_CNT] = {.type = NLA_U32}, + [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = {.type = NLA_U32}, + [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = {.type = NLA_U64}, + [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = {.type = NLA_U64}, + [IFLA_BR_MCAST_QUERIER_INTVL] = {.type = NLA_U64}, + [IFLA_BR_MCAST_QUERY_INTVL] = {.type = NLA_U64}, + [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = {.type = NLA_U64}, + [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = {.type = NLA_U64}, + }; + NMPlatformLnkBridge *props; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPObject * obj; + + if (!info_data || !nm_streq0(kind, "bridge")) + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_BRIDGE, NULL); + + props = &obj->lnk_bridge; + *props = nm_platform_lnk_bridge_default; + + if (!_nm_platform_kernel_support_detected( + NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED)) { + /* IFLA_BR_VLAN_STATS_ENABLED was added in kernel 4.10 on April 30, 2016. + * See commit 6dada9b10a0818ba72c249526a742c8c41274a73. */ + _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED, + tb[IFLA_BR_VLAN_STATS_ENABLED] ? 1 : -1); + } + + if (tb[IFLA_BR_FORWARD_DELAY]) + props->forward_delay = nla_get_u32(tb[IFLA_BR_FORWARD_DELAY]); + if (tb[IFLA_BR_HELLO_TIME]) + props->hello_time = nla_get_u32(tb[IFLA_BR_HELLO_TIME]); + if (tb[IFLA_BR_MAX_AGE]) + props->max_age = nla_get_u32(tb[IFLA_BR_MAX_AGE]); + if (tb[IFLA_BR_AGEING_TIME]) + props->ageing_time = nla_get_u32(tb[IFLA_BR_AGEING_TIME]); + if (tb[IFLA_BR_STP_STATE]) + props->stp_state = !!nla_get_u32(tb[IFLA_BR_STP_STATE]); + if (tb[IFLA_BR_PRIORITY]) + props->priority = nla_get_u16(tb[IFLA_BR_PRIORITY]); + if (tb[IFLA_BR_VLAN_PROTOCOL]) + props->vlan_protocol = ntohs(nla_get_u16(tb[IFLA_BR_VLAN_PROTOCOL])); + if (tb[IFLA_BR_VLAN_STATS_ENABLED]) + props->vlan_stats_enabled = nla_get_u8(tb[IFLA_BR_VLAN_STATS_ENABLED]); + if (tb[IFLA_BR_GROUP_FWD_MASK]) + props->group_fwd_mask = nla_get_u16(tb[IFLA_BR_GROUP_FWD_MASK]); + if (tb[IFLA_BR_GROUP_ADDR]) + props->group_addr = *nla_data_as(NMEtherAddr, tb[IFLA_BR_GROUP_ADDR]); + if (tb[IFLA_BR_MCAST_SNOOPING]) + props->mcast_snooping = !!nla_get_u8(tb[IFLA_BR_MCAST_SNOOPING]); + if (tb[IFLA_BR_MCAST_ROUTER]) + props->mcast_router = nla_get_u8(tb[IFLA_BR_MCAST_ROUTER]); + if (tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]) + props->mcast_query_use_ifaddr = !!nla_get_u8(tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]); + if (tb[IFLA_BR_MCAST_QUERIER]) + props->mcast_querier = nla_get_u8(tb[IFLA_BR_MCAST_QUERIER]); + if (tb[IFLA_BR_MCAST_HASH_MAX]) + props->mcast_hash_max = nla_get_u32(tb[IFLA_BR_MCAST_HASH_MAX]); + if (tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]) + props->mcast_last_member_count = nla_get_u32(tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]); + if (tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]) + props->mcast_startup_query_count = nla_get_u32(tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]); + if (tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]) + props->mcast_last_member_interval = nla_get_u64(tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]); + if (tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]) + props->mcast_membership_interval = nla_get_u64(tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]); + if (tb[IFLA_BR_MCAST_QUERIER_INTVL]) + props->mcast_querier_interval = nla_get_u64(tb[IFLA_BR_MCAST_QUERIER_INTVL]); + if (tb[IFLA_BR_MCAST_QUERY_INTVL]) + props->mcast_query_interval = nla_get_u64(tb[IFLA_BR_MCAST_QUERY_INTVL]); + if (tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]) + props->mcast_query_response_interval = nla_get_u64(tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]); + if (tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]) + props->mcast_startup_query_interval = nla_get_u64(tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]); + + return obj; +} + +/***********************************************************************************/ + +static NMPObject * +_parse_lnk_gre(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_GRE_LINK] = {.type = NLA_U32}, + [IFLA_GRE_IFLAGS] = {.type = NLA_U16}, + [IFLA_GRE_OFLAGS] = {.type = NLA_U16}, + [IFLA_GRE_IKEY] = {.type = NLA_U32}, + [IFLA_GRE_OKEY] = {.type = NLA_U32}, + [IFLA_GRE_LOCAL] = {.type = NLA_U32}, + [IFLA_GRE_REMOTE] = {.type = NLA_U32}, + [IFLA_GRE_TTL] = {.type = NLA_U8}, + [IFLA_GRE_TOS] = {.type = NLA_U8}, + [IFLA_GRE_PMTUDISC] = {.type = NLA_U8}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPObject * obj; + NMPlatformLnkGre *props; + gboolean is_tap; + + if (!info_data || !kind) + return NULL; + + if (nm_streq(kind, "gretap")) + is_tap = TRUE; + else if (nm_streq(kind, "gre")) + is_tap = FALSE; + else + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + obj = nmp_object_new(is_tap ? NMP_OBJECT_TYPE_LNK_GRETAP : NMP_OBJECT_TYPE_LNK_GRE, NULL); + props = &obj->lnk_gre; + + props->parent_ifindex = tb[IFLA_GRE_LINK] ? nla_get_u32(tb[IFLA_GRE_LINK]) : 0; + props->input_flags = tb[IFLA_GRE_IFLAGS] ? ntohs(nla_get_u16(tb[IFLA_GRE_IFLAGS])) : 0; + props->output_flags = tb[IFLA_GRE_OFLAGS] ? ntohs(nla_get_u16(tb[IFLA_GRE_OFLAGS])) : 0; + props->input_key = tb[IFLA_GRE_IKEY] ? ntohl(nla_get_u32(tb[IFLA_GRE_IKEY])) : 0; + props->output_key = tb[IFLA_GRE_OKEY] ? ntohl(nla_get_u32(tb[IFLA_GRE_OKEY])) : 0; + props->local = tb[IFLA_GRE_LOCAL] ? nla_get_u32(tb[IFLA_GRE_LOCAL]) : 0; + props->remote = tb[IFLA_GRE_REMOTE] ? nla_get_u32(tb[IFLA_GRE_REMOTE]) : 0; + props->tos = tb[IFLA_GRE_TOS] ? nla_get_u8(tb[IFLA_GRE_TOS]) : 0; + props->ttl = tb[IFLA_GRE_TTL] ? nla_get_u8(tb[IFLA_GRE_TTL]) : 0; + props->path_mtu_discovery = !tb[IFLA_GRE_PMTUDISC] || !!nla_get_u8(tb[IFLA_GRE_PMTUDISC]); + props->is_tap = is_tap; + + return obj; } /*****************************************************************************/ @@ -1366,503 +1466,520 @@ _parse_lnk_gre (const char *kind, struct nlattr *info_data) #define IPOIB_MODE_CONNECTED 1 /* using connected QPs */ static NMPObject * -_parse_lnk_infiniband (const char *kind, struct nlattr *info_data) +_parse_lnk_infiniband(const char *kind, struct nlattr *info_data) { - static const struct nla_policy policy[] = { - [IFLA_IPOIB_PKEY] = { .type = NLA_U16 }, - [IFLA_IPOIB_MODE] = { .type = NLA_U16 }, - [IFLA_IPOIB_UMCAST] = { .type = NLA_U16 }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - NMPlatformLnkInfiniband *info; - NMPObject *obj; - const char *mode; + static const struct nla_policy policy[] = { + [IFLA_IPOIB_PKEY] = {.type = NLA_U16}, + [IFLA_IPOIB_MODE] = {.type = NLA_U16}, + [IFLA_IPOIB_UMCAST] = {.type = NLA_U16}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPlatformLnkInfiniband *info; + NMPObject * obj; + const char * mode; - if ( !info_data - || !nm_streq0 (kind, "ipoib")) - return NULL; + if (!info_data || !nm_streq0(kind, "ipoib")) + return NULL; - if (nla_parse_nested_arr (tb, info_data, policy) < 0) - return NULL; + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; - if ( !tb[IFLA_IPOIB_PKEY] - || !tb[IFLA_IPOIB_MODE]) - return NULL; + if (!tb[IFLA_IPOIB_PKEY] || !tb[IFLA_IPOIB_MODE]) + return NULL; - switch (nla_get_u16 (tb[IFLA_IPOIB_MODE])) { - case IPOIB_MODE_DATAGRAM: - mode = "datagram"; - break; - case IPOIB_MODE_CONNECTED: - mode = "connected"; - break; - default: - return NULL; - } + switch (nla_get_u16(tb[IFLA_IPOIB_MODE])) { + case IPOIB_MODE_DATAGRAM: + mode = "datagram"; + break; + case IPOIB_MODE_CONNECTED: + mode = "connected"; + break; + default: + return NULL; + } - obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_INFINIBAND, NULL); - info = &obj->lnk_infiniband; + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_INFINIBAND, NULL); + info = &obj->lnk_infiniband; - info->p_key = nla_get_u16 (tb[IFLA_IPOIB_PKEY]); - info->mode = mode; + info->p_key = nla_get_u16(tb[IFLA_IPOIB_PKEY]); + info->mode = mode; - return obj; + return obj; } /*****************************************************************************/ static NMPObject * -_parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, - [IFLA_IPTUN_LOCAL] = { .minlen = sizeof (struct in6_addr)}, - [IFLA_IPTUN_REMOTE] = { .minlen = sizeof (struct in6_addr)}, - [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, - [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 }, - [IFLA_IPTUN_FLOWINFO] = { .type = NLA_U32 }, - [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, - [IFLA_IPTUN_FLAGS] = { .type = NLA_U32 }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - NMPObject *obj; - NMPlatformLnkIp6Tnl *props; - guint32 flowinfo; - - if ( !info_data - || !nm_streq0 (kind, "ip6tnl")) - return NULL; - - if (nla_parse_nested_arr (tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_IP6TNL, NULL); - props = &obj->lnk_ip6tnl; - - if (tb[IFLA_IPTUN_LINK]) - props->parent_ifindex = nla_get_u32 (tb[IFLA_IPTUN_LINK]); - if (tb[IFLA_IPTUN_LOCAL]) - props->local = *nla_data_as (struct in6_addr, tb[IFLA_IPTUN_LOCAL]); - if (tb[IFLA_IPTUN_REMOTE]) - props->remote = *nla_data_as (struct in6_addr, tb[IFLA_IPTUN_REMOTE]); - if (tb[IFLA_IPTUN_TTL]) - props->ttl = nla_get_u8 (tb[IFLA_IPTUN_TTL]); - if (tb[IFLA_IPTUN_ENCAP_LIMIT]) - props->encap_limit = nla_get_u8 (tb[IFLA_IPTUN_ENCAP_LIMIT]); - if (tb[IFLA_IPTUN_FLOWINFO]) { - flowinfo = ntohl (nla_get_u32 (tb[IFLA_IPTUN_FLOWINFO])); - props->flow_label = flowinfo & IP6_FLOWINFO_FLOWLABEL_MASK; - props->tclass = (flowinfo & IP6_FLOWINFO_TCLASS_MASK) >> IP6_FLOWINFO_TCLASS_SHIFT; - } - if (tb[IFLA_IPTUN_PROTO]) - props->proto = nla_get_u8 (tb[IFLA_IPTUN_PROTO]); - if (tb[IFLA_IPTUN_FLAGS]) - props->flags = nla_get_u32 (tb[IFLA_IPTUN_FLAGS]); - - return obj; +_parse_lnk_ip6tnl(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_IPTUN_LINK] = {.type = NLA_U32}, + [IFLA_IPTUN_LOCAL] = {.minlen = sizeof(struct in6_addr)}, + [IFLA_IPTUN_REMOTE] = {.minlen = sizeof(struct in6_addr)}, + [IFLA_IPTUN_TTL] = {.type = NLA_U8}, + [IFLA_IPTUN_ENCAP_LIMIT] = {.type = NLA_U8}, + [IFLA_IPTUN_FLOWINFO] = {.type = NLA_U32}, + [IFLA_IPTUN_PROTO] = {.type = NLA_U8}, + [IFLA_IPTUN_FLAGS] = {.type = NLA_U32}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPObject * obj; + NMPlatformLnkIp6Tnl *props; + guint32 flowinfo; + + if (!info_data || !nm_streq0(kind, "ip6tnl")) + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_IP6TNL, NULL); + props = &obj->lnk_ip6tnl; + + if (tb[IFLA_IPTUN_LINK]) + props->parent_ifindex = nla_get_u32(tb[IFLA_IPTUN_LINK]); + if (tb[IFLA_IPTUN_LOCAL]) + props->local = *nla_data_as(struct in6_addr, tb[IFLA_IPTUN_LOCAL]); + if (tb[IFLA_IPTUN_REMOTE]) + props->remote = *nla_data_as(struct in6_addr, tb[IFLA_IPTUN_REMOTE]); + if (tb[IFLA_IPTUN_TTL]) + props->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]); + if (tb[IFLA_IPTUN_ENCAP_LIMIT]) + props->encap_limit = nla_get_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]); + if (tb[IFLA_IPTUN_FLOWINFO]) { + flowinfo = ntohl(nla_get_u32(tb[IFLA_IPTUN_FLOWINFO])); + props->flow_label = flowinfo & IP6_FLOWINFO_FLOWLABEL_MASK; + props->tclass = (flowinfo & IP6_FLOWINFO_TCLASS_MASK) >> IP6_FLOWINFO_TCLASS_SHIFT; + } + if (tb[IFLA_IPTUN_PROTO]) + props->proto = nla_get_u8(tb[IFLA_IPTUN_PROTO]); + if (tb[IFLA_IPTUN_FLAGS]) + props->flags = nla_get_u32(tb[IFLA_IPTUN_FLAGS]); + + return obj; } static NMPObject * -_parse_lnk_ip6gre (const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_GRE_LINK] = { .type = NLA_U32 }, - [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, - [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, - [IFLA_GRE_IKEY] = { .type = NLA_U32 }, - [IFLA_GRE_OKEY] = { .type = NLA_U32 }, - [IFLA_GRE_LOCAL] = { .type = NLA_UNSPEC, - .minlen = sizeof (struct in6_addr)}, - [IFLA_GRE_REMOTE] = { .type = NLA_UNSPEC, - .minlen = sizeof (struct in6_addr)}, - [IFLA_GRE_TTL] = { .type = NLA_U8 }, - [IFLA_GRE_ENCAP_LIMIT] = { .type = NLA_U8 }, - [IFLA_GRE_FLOWINFO] = { .type = NLA_U32 }, - [IFLA_GRE_FLAGS] = { .type = NLA_U32 }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - NMPObject *obj; - NMPlatformLnkIp6Tnl *props; - guint32 flowinfo; - gboolean is_tap; - - if (!info_data || !kind) - return NULL; - - if (nm_streq (kind, "ip6gre")) - is_tap = FALSE; - else if (nm_streq (kind, "ip6gretap")) - is_tap = TRUE; - else - return NULL; - - if (nla_parse_nested_arr (tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new (is_tap ? NMP_OBJECT_TYPE_LNK_IP6GRETAP : NMP_OBJECT_TYPE_LNK_IP6GRE, NULL); - props = &obj->lnk_ip6tnl; - props->is_gre = TRUE; - props->is_tap = is_tap; - - if (tb[IFLA_GRE_LINK]) - props->parent_ifindex = nla_get_u32 (tb[IFLA_GRE_LINK]); - if (tb[IFLA_GRE_IFLAGS]) - props->input_flags = ntohs (nla_get_u16 (tb[IFLA_GRE_IFLAGS])); - if (tb[IFLA_GRE_OFLAGS]) - props->output_flags = ntohs (nla_get_u16 (tb[IFLA_GRE_OFLAGS])); - if (tb[IFLA_GRE_IKEY]) - props->input_key = ntohl (nla_get_u32 (tb[IFLA_GRE_IKEY])); - if (tb[IFLA_GRE_OKEY]) - props->output_key = ntohl (nla_get_u32 (tb[IFLA_GRE_OKEY])); - if (tb[IFLA_GRE_LOCAL]) - props->local = *nla_data_as (struct in6_addr, tb[IFLA_GRE_LOCAL]); - if (tb[IFLA_GRE_REMOTE]) - props->remote = *nla_data_as (struct in6_addr, tb[IFLA_GRE_REMOTE]); - if (tb[IFLA_GRE_TTL]) - props->ttl = nla_get_u8 (tb[IFLA_GRE_TTL]); - if (tb[IFLA_GRE_ENCAP_LIMIT]) - props->encap_limit = nla_get_u8 (tb[IFLA_GRE_ENCAP_LIMIT]); - if (tb[IFLA_GRE_FLOWINFO]) { - flowinfo = ntohl (nla_get_u32 (tb[IFLA_GRE_FLOWINFO])); - props->flow_label = flowinfo & IP6_FLOWINFO_FLOWLABEL_MASK; - props->tclass = (flowinfo & IP6_FLOWINFO_TCLASS_MASK) >> IP6_FLOWINFO_TCLASS_SHIFT; - } - if (tb[IFLA_GRE_FLAGS]) - props->flags = nla_get_u32 (tb[IFLA_GRE_FLAGS]); - - return obj; +_parse_lnk_ip6gre(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_GRE_LINK] = {.type = NLA_U32}, + [IFLA_GRE_IFLAGS] = {.type = NLA_U16}, + [IFLA_GRE_OFLAGS] = {.type = NLA_U16}, + [IFLA_GRE_IKEY] = {.type = NLA_U32}, + [IFLA_GRE_OKEY] = {.type = NLA_U32}, + [IFLA_GRE_LOCAL] = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)}, + [IFLA_GRE_REMOTE] = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)}, + [IFLA_GRE_TTL] = {.type = NLA_U8}, + [IFLA_GRE_ENCAP_LIMIT] = {.type = NLA_U8}, + [IFLA_GRE_FLOWINFO] = {.type = NLA_U32}, + [IFLA_GRE_FLAGS] = {.type = NLA_U32}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPObject * obj; + NMPlatformLnkIp6Tnl *props; + guint32 flowinfo; + gboolean is_tap; + + if (!info_data || !kind) + return NULL; + + if (nm_streq(kind, "ip6gre")) + is_tap = FALSE; + else if (nm_streq(kind, "ip6gretap")) + is_tap = TRUE; + else + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + obj = nmp_object_new(is_tap ? NMP_OBJECT_TYPE_LNK_IP6GRETAP : NMP_OBJECT_TYPE_LNK_IP6GRE, NULL); + props = &obj->lnk_ip6tnl; + props->is_gre = TRUE; + props->is_tap = is_tap; + + if (tb[IFLA_GRE_LINK]) + props->parent_ifindex = nla_get_u32(tb[IFLA_GRE_LINK]); + if (tb[IFLA_GRE_IFLAGS]) + props->input_flags = ntohs(nla_get_u16(tb[IFLA_GRE_IFLAGS])); + if (tb[IFLA_GRE_OFLAGS]) + props->output_flags = ntohs(nla_get_u16(tb[IFLA_GRE_OFLAGS])); + if (tb[IFLA_GRE_IKEY]) + props->input_key = ntohl(nla_get_u32(tb[IFLA_GRE_IKEY])); + if (tb[IFLA_GRE_OKEY]) + props->output_key = ntohl(nla_get_u32(tb[IFLA_GRE_OKEY])); + if (tb[IFLA_GRE_LOCAL]) + props->local = *nla_data_as(struct in6_addr, tb[IFLA_GRE_LOCAL]); + if (tb[IFLA_GRE_REMOTE]) + props->remote = *nla_data_as(struct in6_addr, tb[IFLA_GRE_REMOTE]); + if (tb[IFLA_GRE_TTL]) + props->ttl = nla_get_u8(tb[IFLA_GRE_TTL]); + if (tb[IFLA_GRE_ENCAP_LIMIT]) + props->encap_limit = nla_get_u8(tb[IFLA_GRE_ENCAP_LIMIT]); + if (tb[IFLA_GRE_FLOWINFO]) { + flowinfo = ntohl(nla_get_u32(tb[IFLA_GRE_FLOWINFO])); + props->flow_label = flowinfo & IP6_FLOWINFO_FLOWLABEL_MASK; + props->tclass = (flowinfo & IP6_FLOWINFO_TCLASS_MASK) >> IP6_FLOWINFO_TCLASS_SHIFT; + } + if (tb[IFLA_GRE_FLAGS]) + props->flags = nla_get_u32(tb[IFLA_GRE_FLAGS]); + + return obj; } /*****************************************************************************/ static NMPObject * -_parse_lnk_ipip (const char *kind, struct nlattr *info_data) +_parse_lnk_ipip(const char *kind, struct nlattr *info_data) { - static const struct nla_policy policy[] = { - [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, - [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, - [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, - [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, - [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, - [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - NMPObject *obj; - NMPlatformLnkIpIp *props; + static const struct nla_policy policy[] = { + [IFLA_IPTUN_LINK] = {.type = NLA_U32}, + [IFLA_IPTUN_LOCAL] = {.type = NLA_U32}, + [IFLA_IPTUN_REMOTE] = {.type = NLA_U32}, + [IFLA_IPTUN_TTL] = {.type = NLA_U8}, + [IFLA_IPTUN_TOS] = {.type = NLA_U8}, + [IFLA_IPTUN_PMTUDISC] = {.type = NLA_U8}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPObject * obj; + NMPlatformLnkIpIp *props; - if ( !info_data - || !nm_streq0 (kind, "ipip")) - return NULL; + if (!info_data || !nm_streq0(kind, "ipip")) + return NULL; - if (nla_parse_nested_arr (tb, info_data, policy) < 0) - return NULL; + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; - obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_IPIP, NULL); - props = &obj->lnk_ipip; + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_IPIP, NULL); + props = &obj->lnk_ipip; - props->parent_ifindex = tb[IFLA_IPTUN_LINK] ? nla_get_u32 (tb[IFLA_IPTUN_LINK]) : 0; - props->local = tb[IFLA_IPTUN_LOCAL] ? nla_get_u32 (tb[IFLA_IPTUN_LOCAL]) : 0; - props->remote = tb[IFLA_IPTUN_REMOTE] ? nla_get_u32 (tb[IFLA_IPTUN_REMOTE]) : 0; - props->tos = tb[IFLA_IPTUN_TOS] ? nla_get_u8 (tb[IFLA_IPTUN_TOS]) : 0; - props->ttl = tb[IFLA_IPTUN_TTL] ? nla_get_u8 (tb[IFLA_IPTUN_TTL]) : 0; - props->path_mtu_discovery = !tb[IFLA_IPTUN_PMTUDISC] || !!nla_get_u8 (tb[IFLA_IPTUN_PMTUDISC]); + props->parent_ifindex = tb[IFLA_IPTUN_LINK] ? nla_get_u32(tb[IFLA_IPTUN_LINK]) : 0; + props->local = tb[IFLA_IPTUN_LOCAL] ? nla_get_u32(tb[IFLA_IPTUN_LOCAL]) : 0; + props->remote = tb[IFLA_IPTUN_REMOTE] ? nla_get_u32(tb[IFLA_IPTUN_REMOTE]) : 0; + props->tos = tb[IFLA_IPTUN_TOS] ? nla_get_u8(tb[IFLA_IPTUN_TOS]) : 0; + props->ttl = tb[IFLA_IPTUN_TTL] ? nla_get_u8(tb[IFLA_IPTUN_TTL]) : 0; + props->path_mtu_discovery = !tb[IFLA_IPTUN_PMTUDISC] || !!nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]); - return obj; + return obj; } /*****************************************************************************/ static NMPObject * -_parse_lnk_macvlan (const char *kind, struct nlattr *info_data) +_parse_lnk_macvlan(const char *kind, struct nlattr *info_data) { - static const struct nla_policy policy[] = { - [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, - [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, - }; - NMPlatformLnkMacvlan *props; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - NMPObject *obj; - gboolean tap; + static const struct nla_policy policy[] = { + [IFLA_MACVLAN_MODE] = {.type = NLA_U32}, + [IFLA_MACVLAN_FLAGS] = {.type = NLA_U16}, + }; + NMPlatformLnkMacvlan *props; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPObject * obj; + gboolean tap; - if ( !info_data - || !kind) - return NULL; + if (!info_data || !kind) + return NULL; - if (nm_streq (kind, "macvlan")) - tap = FALSE; - else if (nm_streq (kind, "macvtap")) - tap = TRUE; - else - return NULL; + if (nm_streq(kind, "macvlan")) + tap = FALSE; + else if (nm_streq(kind, "macvtap")) + tap = TRUE; + else + return NULL; - if (nla_parse_nested_arr (tb, info_data, policy) < 0) - return NULL; + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; - if (!tb[IFLA_MACVLAN_MODE]) - return NULL; + if (!tb[IFLA_MACVLAN_MODE]) + return NULL; - obj = nmp_object_new (tap ? NMP_OBJECT_TYPE_LNK_MACVTAP : NMP_OBJECT_TYPE_LNK_MACVLAN, NULL); - props = &obj->lnk_macvlan; - props->mode = nla_get_u32 (tb[IFLA_MACVLAN_MODE]); - props->tap = tap; + obj = nmp_object_new(tap ? NMP_OBJECT_TYPE_LNK_MACVTAP : NMP_OBJECT_TYPE_LNK_MACVLAN, NULL); + props = &obj->lnk_macvlan; + props->mode = nla_get_u32(tb[IFLA_MACVLAN_MODE]); + props->tap = tap; - if (tb[IFLA_MACVLAN_FLAGS]) - props->no_promisc = NM_FLAGS_HAS (nla_get_u16 (tb[IFLA_MACVLAN_FLAGS]), MACVLAN_FLAG_NOPROMISC); + if (tb[IFLA_MACVLAN_FLAGS]) + props->no_promisc = + NM_FLAGS_HAS(nla_get_u16(tb[IFLA_MACVLAN_FLAGS]), MACVLAN_FLAG_NOPROMISC); - return obj; + return obj; } /*****************************************************************************/ static NMPObject * -_parse_lnk_macsec (const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_MACSEC_SCI] = { .type = NLA_U64 }, - [IFLA_MACSEC_ICV_LEN] = { .type = NLA_U8 }, - [IFLA_MACSEC_CIPHER_SUITE] = { .type = NLA_U64 }, - [IFLA_MACSEC_WINDOW] = { .type = NLA_U32 }, - [IFLA_MACSEC_ENCODING_SA] = { .type = NLA_U8 }, - [IFLA_MACSEC_ENCRYPT] = { .type = NLA_U8 }, - [IFLA_MACSEC_PROTECT] = { .type = NLA_U8 }, - [IFLA_MACSEC_INC_SCI] = { .type = NLA_U8 }, - [IFLA_MACSEC_ES] = { .type = NLA_U8 }, - [IFLA_MACSEC_SCB] = { .type = NLA_U8 }, - [IFLA_MACSEC_REPLAY_PROTECT] = { .type = NLA_U8 }, - [IFLA_MACSEC_VALIDATION] = { .type = NLA_U8 }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - NMPObject *obj; - NMPlatformLnkMacsec *props; - - if ( !info_data - || !nm_streq0 (kind, "macsec")) - return NULL; - - if (nla_parse_nested_arr (tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_MACSEC, NULL); - props = &obj->lnk_macsec; - - if (tb[IFLA_MACSEC_SCI]) { props->sci = nla_get_be64 (tb[IFLA_MACSEC_SCI]); } - if (tb[IFLA_MACSEC_ICV_LEN]) { props->icv_length = nla_get_u8 (tb[IFLA_MACSEC_ICV_LEN]); } - if (tb[IFLA_MACSEC_CIPHER_SUITE]) { props->cipher_suite = nla_get_u64 (tb[IFLA_MACSEC_CIPHER_SUITE]); } - if (tb[IFLA_MACSEC_WINDOW]) { props->window = nla_get_u32 (tb[IFLA_MACSEC_WINDOW]); } - if (tb[IFLA_MACSEC_ENCODING_SA]) { props->encoding_sa = !!nla_get_u8 (tb[IFLA_MACSEC_ENCODING_SA]); } - if (tb[IFLA_MACSEC_ENCRYPT]) { props->encrypt = !!nla_get_u8 (tb[IFLA_MACSEC_ENCRYPT]); } - if (tb[IFLA_MACSEC_PROTECT]) { props->protect = !!nla_get_u8 (tb[IFLA_MACSEC_PROTECT]); } - if (tb[IFLA_MACSEC_INC_SCI]) { props->include_sci = !!nla_get_u8 (tb[IFLA_MACSEC_INC_SCI]); } - if (tb[IFLA_MACSEC_ES]) { props->es = !!nla_get_u8 (tb[IFLA_MACSEC_ES]); } - if (tb[IFLA_MACSEC_SCB]) { props->scb = !!nla_get_u8 (tb[IFLA_MACSEC_SCB]); } - if (tb[IFLA_MACSEC_REPLAY_PROTECT]) { props->replay_protect = !!nla_get_u8 (tb[IFLA_MACSEC_REPLAY_PROTECT]); } - if (tb[IFLA_MACSEC_VALIDATION]) { props->validation = nla_get_u8 (tb[IFLA_MACSEC_VALIDATION]); } - - return obj; +_parse_lnk_macsec(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_MACSEC_SCI] = {.type = NLA_U64}, + [IFLA_MACSEC_ICV_LEN] = {.type = NLA_U8}, + [IFLA_MACSEC_CIPHER_SUITE] = {.type = NLA_U64}, + [IFLA_MACSEC_WINDOW] = {.type = NLA_U32}, + [IFLA_MACSEC_ENCODING_SA] = {.type = NLA_U8}, + [IFLA_MACSEC_ENCRYPT] = {.type = NLA_U8}, + [IFLA_MACSEC_PROTECT] = {.type = NLA_U8}, + [IFLA_MACSEC_INC_SCI] = {.type = NLA_U8}, + [IFLA_MACSEC_ES] = {.type = NLA_U8}, + [IFLA_MACSEC_SCB] = {.type = NLA_U8}, + [IFLA_MACSEC_REPLAY_PROTECT] = {.type = NLA_U8}, + [IFLA_MACSEC_VALIDATION] = {.type = NLA_U8}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPObject * obj; + NMPlatformLnkMacsec *props; + + if (!info_data || !nm_streq0(kind, "macsec")) + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_MACSEC, NULL); + props = &obj->lnk_macsec; + + if (tb[IFLA_MACSEC_SCI]) { + props->sci = nla_get_be64(tb[IFLA_MACSEC_SCI]); + } + if (tb[IFLA_MACSEC_ICV_LEN]) { + props->icv_length = nla_get_u8(tb[IFLA_MACSEC_ICV_LEN]); + } + if (tb[IFLA_MACSEC_CIPHER_SUITE]) { + props->cipher_suite = nla_get_u64(tb[IFLA_MACSEC_CIPHER_SUITE]); + } + if (tb[IFLA_MACSEC_WINDOW]) { + props->window = nla_get_u32(tb[IFLA_MACSEC_WINDOW]); + } + if (tb[IFLA_MACSEC_ENCODING_SA]) { + props->encoding_sa = !!nla_get_u8(tb[IFLA_MACSEC_ENCODING_SA]); + } + if (tb[IFLA_MACSEC_ENCRYPT]) { + props->encrypt = !!nla_get_u8(tb[IFLA_MACSEC_ENCRYPT]); + } + if (tb[IFLA_MACSEC_PROTECT]) { + props->protect = !!nla_get_u8(tb[IFLA_MACSEC_PROTECT]); + } + if (tb[IFLA_MACSEC_INC_SCI]) { + props->include_sci = !!nla_get_u8(tb[IFLA_MACSEC_INC_SCI]); + } + if (tb[IFLA_MACSEC_ES]) { + props->es = !!nla_get_u8(tb[IFLA_MACSEC_ES]); + } + if (tb[IFLA_MACSEC_SCB]) { + props->scb = !!nla_get_u8(tb[IFLA_MACSEC_SCB]); + } + if (tb[IFLA_MACSEC_REPLAY_PROTECT]) { + props->replay_protect = !!nla_get_u8(tb[IFLA_MACSEC_REPLAY_PROTECT]); + } + if (tb[IFLA_MACSEC_VALIDATION]) { + props->validation = nla_get_u8(tb[IFLA_MACSEC_VALIDATION]); + } + + return obj; } /*****************************************************************************/ static NMPObject * -_parse_lnk_sit (const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, - [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, - [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, - [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, - [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, - [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 }, - [IFLA_IPTUN_FLAGS] = { .type = NLA_U16 }, - [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - NMPObject *obj; - NMPlatformLnkSit *props; - - if ( !info_data - || !nm_streq0 (kind, "sit")) - return NULL; - - if (nla_parse_nested_arr (tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_SIT, NULL); - props = &obj->lnk_sit; - - props->parent_ifindex = tb[IFLA_IPTUN_LINK] ? nla_get_u32 (tb[IFLA_IPTUN_LINK]) : 0; - props->local = tb[IFLA_IPTUN_LOCAL] ? nla_get_u32 (tb[IFLA_IPTUN_LOCAL]) : 0; - props->remote = tb[IFLA_IPTUN_REMOTE] ? nla_get_u32 (tb[IFLA_IPTUN_REMOTE]) : 0; - props->tos = tb[IFLA_IPTUN_TOS] ? nla_get_u8 (tb[IFLA_IPTUN_TOS]) : 0; - props->ttl = tb[IFLA_IPTUN_TTL] ? nla_get_u8 (tb[IFLA_IPTUN_TTL]) : 0; - props->path_mtu_discovery = !tb[IFLA_IPTUN_PMTUDISC] || !!nla_get_u8 (tb[IFLA_IPTUN_PMTUDISC]); - props->flags = tb[IFLA_IPTUN_FLAGS] ? nla_get_u16 (tb[IFLA_IPTUN_FLAGS]) : 0; - props->proto = tb[IFLA_IPTUN_PROTO] ? nla_get_u8 (tb[IFLA_IPTUN_PROTO]) : 0; - - return obj; +_parse_lnk_sit(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_IPTUN_LINK] = {.type = NLA_U32}, + [IFLA_IPTUN_LOCAL] = {.type = NLA_U32}, + [IFLA_IPTUN_REMOTE] = {.type = NLA_U32}, + [IFLA_IPTUN_TTL] = {.type = NLA_U8}, + [IFLA_IPTUN_TOS] = {.type = NLA_U8}, + [IFLA_IPTUN_PMTUDISC] = {.type = NLA_U8}, + [IFLA_IPTUN_FLAGS] = {.type = NLA_U16}, + [IFLA_IPTUN_PROTO] = {.type = NLA_U8}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPObject * obj; + NMPlatformLnkSit *props; + + if (!info_data || !nm_streq0(kind, "sit")) + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_SIT, NULL); + props = &obj->lnk_sit; + + props->parent_ifindex = tb[IFLA_IPTUN_LINK] ? nla_get_u32(tb[IFLA_IPTUN_LINK]) : 0; + props->local = tb[IFLA_IPTUN_LOCAL] ? nla_get_u32(tb[IFLA_IPTUN_LOCAL]) : 0; + props->remote = tb[IFLA_IPTUN_REMOTE] ? nla_get_u32(tb[IFLA_IPTUN_REMOTE]) : 0; + props->tos = tb[IFLA_IPTUN_TOS] ? nla_get_u8(tb[IFLA_IPTUN_TOS]) : 0; + props->ttl = tb[IFLA_IPTUN_TTL] ? nla_get_u8(tb[IFLA_IPTUN_TTL]) : 0; + props->path_mtu_discovery = !tb[IFLA_IPTUN_PMTUDISC] || !!nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]); + props->flags = tb[IFLA_IPTUN_FLAGS] ? nla_get_u16(tb[IFLA_IPTUN_FLAGS]) : 0; + props->proto = tb[IFLA_IPTUN_PROTO] ? nla_get_u8(tb[IFLA_IPTUN_PROTO]) : 0; + + return obj; } /*****************************************************************************/ static NMPObject * -_parse_lnk_tun (const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_TUN_OWNER] = { .type = NLA_U32 }, - [IFLA_TUN_GROUP] = { .type = NLA_U32 }, - [IFLA_TUN_TYPE] = { .type = NLA_U8 }, - [IFLA_TUN_PI] = { .type = NLA_U8 }, - [IFLA_TUN_VNET_HDR] = { .type = NLA_U8 }, - [IFLA_TUN_PERSIST] = { .type = NLA_U8 }, - [IFLA_TUN_MULTI_QUEUE] = { .type = NLA_U8 }, - [IFLA_TUN_NUM_QUEUES] = { .type = NLA_U32 }, - [IFLA_TUN_NUM_DISABLED_QUEUES] = { .type = NLA_U32 }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - NMPObject *obj; - NMPlatformLnkTun *props; - - if ( !info_data - || !nm_streq0 (kind, "tun")) - return NULL; - - if (nla_parse_nested_arr (tb, info_data, policy) < 0) - return NULL; - - if (!tb[IFLA_TUN_TYPE]) - return NULL; - - obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_TUN, NULL); - props = &obj->lnk_tun; - - props->type = nla_get_u8 (tb[IFLA_TUN_TYPE]); - - props->pi = !!nla_get_u8_cond (tb, IFLA_TUN_PI, FALSE); - props->vnet_hdr = !!nla_get_u8_cond (tb, IFLA_TUN_VNET_HDR, FALSE); - props->multi_queue = !!nla_get_u8_cond (tb, IFLA_TUN_MULTI_QUEUE, FALSE); - props->persist = !!nla_get_u8_cond (tb, IFLA_TUN_PERSIST, FALSE); - - if (tb[IFLA_TUN_OWNER]) { - props->owner_valid = TRUE; - props->owner = nla_get_u32 (tb[IFLA_TUN_OWNER]); - } - if (tb[IFLA_TUN_GROUP]) { - props->group_valid = TRUE; - props->group = nla_get_u32 (tb[IFLA_TUN_GROUP]); - } - return obj; +_parse_lnk_tun(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_TUN_OWNER] = {.type = NLA_U32}, + [IFLA_TUN_GROUP] = {.type = NLA_U32}, + [IFLA_TUN_TYPE] = {.type = NLA_U8}, + [IFLA_TUN_PI] = {.type = NLA_U8}, + [IFLA_TUN_VNET_HDR] = {.type = NLA_U8}, + [IFLA_TUN_PERSIST] = {.type = NLA_U8}, + [IFLA_TUN_MULTI_QUEUE] = {.type = NLA_U8}, + [IFLA_TUN_NUM_QUEUES] = {.type = NLA_U32}, + [IFLA_TUN_NUM_DISABLED_QUEUES] = {.type = NLA_U32}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPObject * obj; + NMPlatformLnkTun *props; + + if (!info_data || !nm_streq0(kind, "tun")) + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + if (!tb[IFLA_TUN_TYPE]) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_TUN, NULL); + props = &obj->lnk_tun; + + props->type = nla_get_u8(tb[IFLA_TUN_TYPE]); + + props->pi = !!nla_get_u8_cond(tb, IFLA_TUN_PI, FALSE); + props->vnet_hdr = !!nla_get_u8_cond(tb, IFLA_TUN_VNET_HDR, FALSE); + props->multi_queue = !!nla_get_u8_cond(tb, IFLA_TUN_MULTI_QUEUE, FALSE); + props->persist = !!nla_get_u8_cond(tb, IFLA_TUN_PERSIST, FALSE); + + if (tb[IFLA_TUN_OWNER]) { + props->owner_valid = TRUE; + props->owner = nla_get_u32(tb[IFLA_TUN_OWNER]); + } + if (tb[IFLA_TUN_GROUP]) { + props->group_valid = TRUE; + props->group = nla_get_u32(tb[IFLA_TUN_GROUP]); + } + return obj; } /*****************************************************************************/ static gboolean -_vlan_qos_mapping_from_nla (struct nlattr *nlattr, - const NMVlanQosMapping **out_map, - guint *out_n_map) +_vlan_qos_mapping_from_nla(struct nlattr * nlattr, + const NMVlanQosMapping **out_map, + guint * out_n_map) { - struct nlattr *nla; - int remaining; - gs_unref_ptrarray GPtrArray *array = NULL; + struct nlattr * nla; + int remaining; + gs_unref_ptrarray GPtrArray *array = NULL; - G_STATIC_ASSERT (sizeof (NMVlanQosMapping) == sizeof (struct ifla_vlan_qos_mapping)); - G_STATIC_ASSERT (sizeof (((NMVlanQosMapping *) 0)->to) == sizeof (((struct ifla_vlan_qos_mapping *) 0)->to)); - G_STATIC_ASSERT (sizeof (((NMVlanQosMapping *) 0)->from) == sizeof (((struct ifla_vlan_qos_mapping *) 0)->from)); - G_STATIC_ASSERT (sizeof (NMVlanQosMapping) == sizeof (((NMVlanQosMapping *) 0)->from) + sizeof (((NMVlanQosMapping *) 0)->to)); + G_STATIC_ASSERT(sizeof(NMVlanQosMapping) == sizeof(struct ifla_vlan_qos_mapping)); + G_STATIC_ASSERT(sizeof(((NMVlanQosMapping *) 0)->to) + == sizeof(((struct ifla_vlan_qos_mapping *) 0)->to)); + G_STATIC_ASSERT(sizeof(((NMVlanQosMapping *) 0)->from) + == sizeof(((struct ifla_vlan_qos_mapping *) 0)->from)); + G_STATIC_ASSERT(sizeof(NMVlanQosMapping) + == sizeof(((NMVlanQosMapping *) 0)->from) + + sizeof(((NMVlanQosMapping *) 0)->to)); - nm_assert (out_map && !*out_map); - nm_assert (out_n_map && !*out_n_map); + nm_assert(out_map && !*out_map); + nm_assert(out_n_map && !*out_n_map); - if (!nlattr) - return TRUE; + if (!nlattr) + return TRUE; - array = g_ptr_array_new (); - nla_for_each_nested (nla, nlattr, remaining) { - if (nla_len (nla) < sizeof (NMVlanQosMapping)) - return FALSE; - g_ptr_array_add (array, nla_data (nla)); - } + array = g_ptr_array_new(); + nla_for_each_nested (nla, nlattr, remaining) { + if (nla_len(nla) < sizeof(NMVlanQosMapping)) + return FALSE; + g_ptr_array_add(array, nla_data(nla)); + } - if (array->len > 0) { - NMVlanQosMapping *list; - guint i, j; + if (array->len > 0) { + NMVlanQosMapping *list; + guint i, j; - /* The sorting is necessary, because for egress mapping, kernel - * doesn't sent the items strictly sorted by the from field. */ - g_ptr_array_sort_with_data (array, _vlan_qos_mapping_cmp_from_ptr, NULL); + /* The sorting is necessary, because for egress mapping, kernel + * doesn't sent the items strictly sorted by the from field. */ + g_ptr_array_sort_with_data(array, _vlan_qos_mapping_cmp_from_ptr, NULL); - list = g_new (NMVlanQosMapping, array->len); + list = g_new(NMVlanQosMapping, array->len); - for (i = 0, j = 0; i < array->len; i++) { - NMVlanQosMapping *map; + for (i = 0, j = 0; i < array->len; i++) { + NMVlanQosMapping *map; - map = array->pdata[i]; + map = array->pdata[i]; - /* kernel doesn't really send us duplicates. Just be extra cautious - * because we want strong guarantees about the sort order and uniqueness - * of our mapping list (for simpler equality comparison). */ - if ( j > 0 - && list[j - 1].from == map->from) - list[j - 1] = *map; - else - list[j++] = *map; - } + /* kernel doesn't really send us duplicates. Just be extra cautious + * because we want strong guarantees about the sort order and uniqueness + * of our mapping list (for simpler equality comparison). */ + if (j > 0 && list[j - 1].from == map->from) + list[j - 1] = *map; + else + list[j++] = *map; + } - *out_n_map = j; - *out_map = list; - } + *out_n_map = j; + *out_map = list; + } - return TRUE; + return TRUE; } /* Copied and heavily modified from libnl3's vlan_parse() */ static NMPObject * -_parse_lnk_vlan (const char *kind, struct nlattr *info_data) +_parse_lnk_vlan(const char *kind, struct nlattr *info_data) { - static const struct nla_policy policy[] = { - [IFLA_VLAN_ID] = { .type = NLA_U16 }, - [IFLA_VLAN_FLAGS] = { .minlen = nm_offsetofend (struct ifla_vlan_flags, flags) }, - [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, - [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED }, - [IFLA_VLAN_PROTOCOL] = { .type = NLA_U16 }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - nm_auto_nmpobj NMPObject *obj = NULL; - NMPObject *obj_result; + static const struct nla_policy policy[] = { + [IFLA_VLAN_ID] = {.type = NLA_U16}, + [IFLA_VLAN_FLAGS] = {.minlen = nm_offsetofend(struct ifla_vlan_flags, flags)}, + [IFLA_VLAN_INGRESS_QOS] = {.type = NLA_NESTED}, + [IFLA_VLAN_EGRESS_QOS] = {.type = NLA_NESTED}, + [IFLA_VLAN_PROTOCOL] = {.type = NLA_U16}, + }; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPObject * obj_result; - if ( !info_data - || !nm_streq0 (kind, "vlan")) - return NULL; + if (!info_data || !nm_streq0(kind, "vlan")) + return NULL; - if (nla_parse_nested_arr (tb, info_data, policy) < 0) - return NULL; + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; - if (!tb[IFLA_VLAN_ID]) - return NULL; + if (!tb[IFLA_VLAN_ID]) + return NULL; - obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); - obj->lnk_vlan.id = nla_get_u16 (tb[IFLA_VLAN_ID]); + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VLAN, NULL); + obj->lnk_vlan.id = nla_get_u16(tb[IFLA_VLAN_ID]); - if (tb[IFLA_VLAN_FLAGS]) { - struct ifla_vlan_flags flags; + if (tb[IFLA_VLAN_FLAGS]) { + struct ifla_vlan_flags flags; - nla_memcpy (&flags, tb[IFLA_VLAN_FLAGS], sizeof (flags)); + nla_memcpy(&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags)); - obj->lnk_vlan.flags = flags.flags; - } + obj->lnk_vlan.flags = flags.flags; + } - if (!_vlan_qos_mapping_from_nla (tb[IFLA_VLAN_INGRESS_QOS], - &obj->_lnk_vlan.ingress_qos_map, - &obj->_lnk_vlan.n_ingress_qos_map)) - return NULL; + if (!_vlan_qos_mapping_from_nla(tb[IFLA_VLAN_INGRESS_QOS], + &obj->_lnk_vlan.ingress_qos_map, + &obj->_lnk_vlan.n_ingress_qos_map)) + return NULL; - if (!_vlan_qos_mapping_from_nla (tb[IFLA_VLAN_EGRESS_QOS], - &obj->_lnk_vlan.egress_qos_map, - &obj->_lnk_vlan.n_egress_qos_map)) - return NULL; + if (!_vlan_qos_mapping_from_nla(tb[IFLA_VLAN_EGRESS_QOS], + &obj->_lnk_vlan.egress_qos_map, + &obj->_lnk_vlan.n_egress_qos_map)) + return NULL; - obj_result = obj; - obj = NULL; - return obj_result; + obj_result = obj; + obj = NULL; + return obj_result; } /*****************************************************************************/ @@ -1872,16 +1989,16 @@ _parse_lnk_vlan (const char *kind, struct nlattr *info_data) * So until we depend on kernel >= 3.11, we just ignore the actual enum * in if_link.h and define the values ourselves. */ -#define IFLA_VXLAN_UNSPEC 0 -#define IFLA_VXLAN_ID 1 -#define IFLA_VXLAN_GROUP 2 -#define IFLA_VXLAN_LINK 3 -#define IFLA_VXLAN_LOCAL 4 -#define IFLA_VXLAN_TTL 5 -#define IFLA_VXLAN_TOS 6 -#define IFLA_VXLAN_LEARNING 7 -#define IFLA_VXLAN_AGEING 8 -#define IFLA_VXLAN_LIMIT 9 +#define IFLA_VXLAN_UNSPEC 0 +#define IFLA_VXLAN_ID 1 +#define IFLA_VXLAN_GROUP 2 +#define IFLA_VXLAN_LINK 3 +#define IFLA_VXLAN_LOCAL 4 +#define IFLA_VXLAN_TTL 5 +#define IFLA_VXLAN_TOS 6 +#define IFLA_VXLAN_LEARNING 7 +#define IFLA_VXLAN_AGEING 8 +#define IFLA_VXLAN_LIMIT 9 #define IFLA_VXLAN_PORT_RANGE 10 #define IFLA_VXLAN_PROXY 11 #define IFLA_VXLAN_RSC 12 @@ -1893,1952 +2010,1984 @@ _parse_lnk_vlan (const char *kind, struct nlattr *info_data) #undef IFLA_VXLAN_MAX #define IFLA_VXLAN_MAX IFLA_VXLAN_LOCAL6 -#define IFLA_VRF_TABLE 1 +#define IFLA_VRF_TABLE 1 /* older kernel header might not contain 'struct ifla_vxlan_port_range'. * Redefine it. */ struct nm_ifla_vxlan_port_range { - guint16 low; - guint16 high; + guint16 low; + guint16 high; }; static NMPObject * -_parse_lnk_vxlan (const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_VXLAN_ID] = { .type = NLA_U32 }, - [IFLA_VXLAN_GROUP] = { .type = NLA_U32 }, - [IFLA_VXLAN_GROUP6] = { .type = NLA_UNSPEC, - .minlen = sizeof (struct in6_addr) }, - [IFLA_VXLAN_LINK] = { .type = NLA_U32 }, - [IFLA_VXLAN_LOCAL] = { .type = NLA_U32 }, - [IFLA_VXLAN_LOCAL6] = { .type = NLA_UNSPEC, - .minlen = sizeof (struct in6_addr) }, - [IFLA_VXLAN_TOS] = { .type = NLA_U8 }, - [IFLA_VXLAN_TTL] = { .type = NLA_U8 }, - [IFLA_VXLAN_LEARNING] = { .type = NLA_U8 }, - [IFLA_VXLAN_AGEING] = { .type = NLA_U32 }, - [IFLA_VXLAN_LIMIT] = { .type = NLA_U32 }, - [IFLA_VXLAN_PORT_RANGE] = { .type = NLA_UNSPEC, - .minlen = sizeof (struct nm_ifla_vxlan_port_range) }, - [IFLA_VXLAN_PROXY] = { .type = NLA_U8 }, - [IFLA_VXLAN_RSC] = { .type = NLA_U8 }, - [IFLA_VXLAN_L2MISS] = { .type = NLA_U8 }, - [IFLA_VXLAN_L3MISS] = { .type = NLA_U8 }, - [IFLA_VXLAN_PORT] = { .type = NLA_U16 }, - }; - NMPlatformLnkVxlan *props; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - NMPObject *obj; - - if ( !info_data - || !nm_streq0 (kind, "vxlan")) - return NULL; - - if (nla_parse_nested_arr (tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_VXLAN, NULL); - - props = &obj->lnk_vxlan; - - if (tb[IFLA_VXLAN_LINK]) - props->parent_ifindex = nla_get_u32 (tb[IFLA_VXLAN_LINK]); - if (tb[IFLA_VXLAN_ID]) - props->id = nla_get_u32 (tb[IFLA_VXLAN_ID]); - if (tb[IFLA_VXLAN_GROUP]) - props->group = nla_get_u32 (tb[IFLA_VXLAN_GROUP]); - if (tb[IFLA_VXLAN_LOCAL]) - props->local = nla_get_u32 (tb[IFLA_VXLAN_LOCAL]); - if (tb[IFLA_VXLAN_LOCAL6]) - props->local6 = *nla_data_as (struct in6_addr, tb[IFLA_VXLAN_LOCAL6]); - if (tb[IFLA_VXLAN_GROUP6]) - props->group6 = *nla_data_as (struct in6_addr, tb[IFLA_VXLAN_GROUP6]); - - if (tb[IFLA_VXLAN_AGEING]) - props->ageing = nla_get_u32 (tb[IFLA_VXLAN_AGEING]); - if (tb[IFLA_VXLAN_LIMIT]) - props->limit = nla_get_u32 (tb[IFLA_VXLAN_LIMIT]); - if (tb[IFLA_VXLAN_TOS]) - props->tos = nla_get_u8 (tb[IFLA_VXLAN_TOS]); - if (tb[IFLA_VXLAN_TTL]) - props->ttl = nla_get_u8 (tb[IFLA_VXLAN_TTL]); - - if (tb[IFLA_VXLAN_PORT]) - props->dst_port = ntohs (nla_get_u16 (tb[IFLA_VXLAN_PORT])); - - if (tb[IFLA_VXLAN_PORT_RANGE]) { - struct nm_ifla_vxlan_port_range *range; - - range = nla_data_as (struct nm_ifla_vxlan_port_range, tb[IFLA_VXLAN_PORT_RANGE]); - props->src_port_min = ntohs (range->low); - props->src_port_max = ntohs (range->high); - } - - if (tb[IFLA_VXLAN_LEARNING]) - props->learning = !!nla_get_u8 (tb[IFLA_VXLAN_LEARNING]); - if (tb[IFLA_VXLAN_PROXY]) - props->proxy = !!nla_get_u8 (tb[IFLA_VXLAN_PROXY]); - if (tb[IFLA_VXLAN_RSC]) - props->rsc = !!nla_get_u8 (tb[IFLA_VXLAN_RSC]); - if (tb[IFLA_VXLAN_L2MISS]) - props->l2miss = !!nla_get_u8 (tb[IFLA_VXLAN_L2MISS]); - if (tb[IFLA_VXLAN_L3MISS]) - props->l3miss = !!nla_get_u8 (tb[IFLA_VXLAN_L3MISS]); - - return obj; +_parse_lnk_vxlan(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_VXLAN_ID] = {.type = NLA_U32}, + [IFLA_VXLAN_GROUP] = {.type = NLA_U32}, + [IFLA_VXLAN_GROUP6] = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)}, + [IFLA_VXLAN_LINK] = {.type = NLA_U32}, + [IFLA_VXLAN_LOCAL] = {.type = NLA_U32}, + [IFLA_VXLAN_LOCAL6] = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)}, + [IFLA_VXLAN_TOS] = {.type = NLA_U8}, + [IFLA_VXLAN_TTL] = {.type = NLA_U8}, + [IFLA_VXLAN_LEARNING] = {.type = NLA_U8}, + [IFLA_VXLAN_AGEING] = {.type = NLA_U32}, + [IFLA_VXLAN_LIMIT] = {.type = NLA_U32}, + [IFLA_VXLAN_PORT_RANGE] = {.type = NLA_UNSPEC, + .minlen = sizeof(struct nm_ifla_vxlan_port_range)}, + [IFLA_VXLAN_PROXY] = {.type = NLA_U8}, + [IFLA_VXLAN_RSC] = {.type = NLA_U8}, + [IFLA_VXLAN_L2MISS] = {.type = NLA_U8}, + [IFLA_VXLAN_L3MISS] = {.type = NLA_U8}, + [IFLA_VXLAN_PORT] = {.type = NLA_U16}, + }; + NMPlatformLnkVxlan *props; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPObject * obj; + + if (!info_data || !nm_streq0(kind, "vxlan")) + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VXLAN, NULL); + + props = &obj->lnk_vxlan; + + if (tb[IFLA_VXLAN_LINK]) + props->parent_ifindex = nla_get_u32(tb[IFLA_VXLAN_LINK]); + if (tb[IFLA_VXLAN_ID]) + props->id = nla_get_u32(tb[IFLA_VXLAN_ID]); + if (tb[IFLA_VXLAN_GROUP]) + props->group = nla_get_u32(tb[IFLA_VXLAN_GROUP]); + if (tb[IFLA_VXLAN_LOCAL]) + props->local = nla_get_u32(tb[IFLA_VXLAN_LOCAL]); + if (tb[IFLA_VXLAN_LOCAL6]) + props->local6 = *nla_data_as(struct in6_addr, tb[IFLA_VXLAN_LOCAL6]); + if (tb[IFLA_VXLAN_GROUP6]) + props->group6 = *nla_data_as(struct in6_addr, tb[IFLA_VXLAN_GROUP6]); + + if (tb[IFLA_VXLAN_AGEING]) + props->ageing = nla_get_u32(tb[IFLA_VXLAN_AGEING]); + if (tb[IFLA_VXLAN_LIMIT]) + props->limit = nla_get_u32(tb[IFLA_VXLAN_LIMIT]); + if (tb[IFLA_VXLAN_TOS]) + props->tos = nla_get_u8(tb[IFLA_VXLAN_TOS]); + if (tb[IFLA_VXLAN_TTL]) + props->ttl = nla_get_u8(tb[IFLA_VXLAN_TTL]); + + if (tb[IFLA_VXLAN_PORT]) + props->dst_port = ntohs(nla_get_u16(tb[IFLA_VXLAN_PORT])); + + if (tb[IFLA_VXLAN_PORT_RANGE]) { + struct nm_ifla_vxlan_port_range *range; + + range = nla_data_as(struct nm_ifla_vxlan_port_range, tb[IFLA_VXLAN_PORT_RANGE]); + props->src_port_min = ntohs(range->low); + props->src_port_max = ntohs(range->high); + } + + if (tb[IFLA_VXLAN_LEARNING]) + props->learning = !!nla_get_u8(tb[IFLA_VXLAN_LEARNING]); + if (tb[IFLA_VXLAN_PROXY]) + props->proxy = !!nla_get_u8(tb[IFLA_VXLAN_PROXY]); + if (tb[IFLA_VXLAN_RSC]) + props->rsc = !!nla_get_u8(tb[IFLA_VXLAN_RSC]); + if (tb[IFLA_VXLAN_L2MISS]) + props->l2miss = !!nla_get_u8(tb[IFLA_VXLAN_L2MISS]); + if (tb[IFLA_VXLAN_L3MISS]) + props->l3miss = !!nla_get_u8(tb[IFLA_VXLAN_L3MISS]); + + return obj; } static NMPObject * -_parse_lnk_vrf (const char *kind, struct nlattr *info_data) +_parse_lnk_vrf(const char *kind, struct nlattr *info_data) { - static const struct nla_policy policy[] = { - [IFLA_VRF_TABLE] = { .type = NLA_U32 }, - }; - NMPlatformLnkVrf *props; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - NMPObject *obj; + static const struct nla_policy policy[] = { + [IFLA_VRF_TABLE] = {.type = NLA_U32}, + }; + NMPlatformLnkVrf *props; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPObject * obj; - if ( !info_data - || !nm_streq0 (kind, "vrf")) - return NULL; + if (!info_data || !nm_streq0(kind, "vrf")) + return NULL; - if (nla_parse_nested_arr (tb, info_data, policy) < 0) - return NULL; + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; - obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_VRF, NULL); + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VRF, NULL); - props = &obj->lnk_vrf; + props = &obj->lnk_vrf; - if (tb[IFLA_VRF_TABLE]) - props->table = nla_get_u32 (tb[IFLA_VRF_TABLE]); + if (tb[IFLA_VRF_TABLE]) + props->table = nla_get_u32(tb[IFLA_VRF_TABLE]); - return obj; + return obj; } /*****************************************************************************/ static gboolean -_wireguard_update_from_allowed_ips_nla (NMPWireGuardAllowedIP *allowed_ip, - struct nlattr *nlattr) +_wireguard_update_from_allowed_ips_nla(NMPWireGuardAllowedIP *allowed_ip, struct nlattr *nlattr) { - static const struct nla_policy policy[] = { - [WGALLOWEDIP_A_FAMILY] = { .type = NLA_U16 }, - [WGALLOWEDIP_A_IPADDR] = { .minlen = sizeof (struct in_addr) }, - [WGALLOWEDIP_A_CIDR_MASK] = { .type = NLA_U8 }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - int family; - int addr_len; + static const struct nla_policy policy[] = { + [WGALLOWEDIP_A_FAMILY] = {.type = NLA_U16}, + [WGALLOWEDIP_A_IPADDR] = {.minlen = sizeof(struct in_addr)}, + [WGALLOWEDIP_A_CIDR_MASK] = {.type = NLA_U8}, + }; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + int family; + int addr_len; - if (nla_parse_nested_arr (tb, nlattr, policy) < 0) - return FALSE; + if (nla_parse_nested_arr(tb, nlattr, policy) < 0) + return FALSE; - if (!tb[WGALLOWEDIP_A_FAMILY]) - return FALSE; + if (!tb[WGALLOWEDIP_A_FAMILY]) + return FALSE; - family = nla_get_u16 (tb[WGALLOWEDIP_A_FAMILY]); - if (family == AF_INET) - addr_len = sizeof (in_addr_t); - else if (family == AF_INET6) - addr_len = sizeof (struct in6_addr); - else - return FALSE; + family = nla_get_u16(tb[WGALLOWEDIP_A_FAMILY]); + if (family == AF_INET) + addr_len = sizeof(in_addr_t); + else if (family == AF_INET6) + addr_len = sizeof(struct in6_addr); + else + return FALSE; - _check_addr_or_return_val (tb, WGALLOWEDIP_A_IPADDR, addr_len, FALSE); + _check_addr_or_return_val(tb, WGALLOWEDIP_A_IPADDR, addr_len, FALSE); - *allowed_ip = (NMPWireGuardAllowedIP) { - .family = family, - }; + *allowed_ip = (NMPWireGuardAllowedIP){ + .family = family, + }; - nm_assert ((int) allowed_ip->family == family); + nm_assert((int) allowed_ip->family == family); - if (tb[WGALLOWEDIP_A_IPADDR]) - nla_memcpy (&allowed_ip->addr, tb[WGALLOWEDIP_A_IPADDR], addr_len); - if (tb[WGALLOWEDIP_A_CIDR_MASK]) - allowed_ip->mask = nla_get_u8 (tb[WGALLOWEDIP_A_CIDR_MASK]); + if (tb[WGALLOWEDIP_A_IPADDR]) + nla_memcpy(&allowed_ip->addr, tb[WGALLOWEDIP_A_IPADDR], addr_len); + if (tb[WGALLOWEDIP_A_CIDR_MASK]) + allowed_ip->mask = nla_get_u8(tb[WGALLOWEDIP_A_CIDR_MASK]); - return TRUE; + return TRUE; } typedef struct { - CList lst; - NMPWireGuardPeer data; + CList lst; + NMPWireGuardPeer data; } WireGuardPeerConstruct; static gboolean -_wireguard_update_from_peers_nla (CList *peers, - GArray **p_allowed_ips, - struct nlattr *peer_attr) -{ - static const struct nla_policy policy[] = { - [WGPEER_A_PUBLIC_KEY] = { .minlen = NMP_WIREGUARD_PUBLIC_KEY_LEN }, - [WGPEER_A_PRESHARED_KEY] = { }, - [WGPEER_A_FLAGS] = { .type = NLA_U32 }, - [WGPEER_A_ENDPOINT] = { }, - [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NLA_U16 }, - [WGPEER_A_LAST_HANDSHAKE_TIME] = { }, - [WGPEER_A_RX_BYTES] = { .type = NLA_U64 }, - [WGPEER_A_TX_BYTES] = { .type = NLA_U64 }, - [WGPEER_A_ALLOWEDIPS] = { .type = NLA_NESTED }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - WireGuardPeerConstruct *peer_c; - - if (nla_parse_nested_arr (tb, peer_attr, policy) < 0) - return FALSE; - - if (!tb[WGPEER_A_PUBLIC_KEY]) - return FALSE; - - /* a peer with the same public key as last peer is just a continuation for extra AllowedIPs */ - peer_c = c_list_last_entry (peers, WireGuardPeerConstruct, lst); - if ( peer_c - && !memcmp (nla_data (tb[WGPEER_A_PUBLIC_KEY]), peer_c->data.public_key, NMP_WIREGUARD_PUBLIC_KEY_LEN)) { - G_STATIC_ASSERT_EXPR (NMP_WIREGUARD_PUBLIC_KEY_LEN == sizeof (peer_c->data.public_key)); - /* this message is a continuation of the previous peer. - * Only parse WGPEER_A_ALLOWEDIPS below. */ - } - else { - /* otherwise, start a new peer */ - peer_c = g_slice_new0 (WireGuardPeerConstruct); - c_list_link_tail (peers, &peer_c->lst); - - nla_memcpy (&peer_c->data.public_key, tb[WGPEER_A_PUBLIC_KEY], sizeof (peer_c->data.public_key)); - - if (tb[WGPEER_A_PRESHARED_KEY]) { - nla_memcpy (&peer_c->data.preshared_key, tb[WGPEER_A_PRESHARED_KEY], sizeof (peer_c->data.preshared_key)); - /* FIXME(netlink-bzero-secret) */ - nm_explicit_bzero (nla_data (tb[WGPEER_A_PRESHARED_KEY]), - nla_len (tb[WGPEER_A_PRESHARED_KEY])); - } - - nm_sock_addr_union_cpy_untrusted (&peer_c->data.endpoint, - tb[WGPEER_A_ENDPOINT] ? nla_data (tb[WGPEER_A_ENDPOINT]) : NULL, - tb[WGPEER_A_ENDPOINT] ? nla_len (tb[WGPEER_A_ENDPOINT]) : 0); - - if (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]) - peer_c->data.persistent_keepalive_interval = nla_get_u16 (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]); - if (tb[WGPEER_A_LAST_HANDSHAKE_TIME]) { - if (nla_len (tb[WGPEER_A_LAST_HANDSHAKE_TIME]) >= sizeof (peer_c->data.last_handshake_time)) - nla_memcpy (&peer_c->data.last_handshake_time, tb[WGPEER_A_LAST_HANDSHAKE_TIME], sizeof (peer_c->data.last_handshake_time)); - } - if (tb[WGPEER_A_RX_BYTES]) - peer_c->data.rx_bytes = nla_get_u64 (tb[WGPEER_A_RX_BYTES]); - if (tb[WGPEER_A_TX_BYTES]) - peer_c->data.tx_bytes = nla_get_u64 (tb[WGPEER_A_TX_BYTES]); - } - - if (tb[WGPEER_A_ALLOWEDIPS]) { - struct nlattr *attr; - int rem; - GArray *allowed_ips = *p_allowed_ips; - - nla_for_each_nested (attr, tb[WGPEER_A_ALLOWEDIPS], rem) { - if (!allowed_ips) { - allowed_ips = g_array_new (FALSE, FALSE, sizeof (NMPWireGuardAllowedIP)); - *p_allowed_ips = allowed_ips; - g_array_set_size (allowed_ips, 1); - } else - g_array_set_size (allowed_ips, allowed_ips->len + 1); - - if (!_wireguard_update_from_allowed_ips_nla (&g_array_index (allowed_ips, - NMPWireGuardAllowedIP, - allowed_ips->len - 1), - attr)) { - /* we ignore the error of parsing one allowed-ip. */ - g_array_set_size (allowed_ips, allowed_ips->len - 1); - continue; - } - - if (!peer_c->data._construct_idx_end) - peer_c->data._construct_idx_start = allowed_ips->len - 1; - peer_c->data._construct_idx_end = allowed_ips->len; - } - } - - return TRUE; +_wireguard_update_from_peers_nla(CList *peers, GArray **p_allowed_ips, struct nlattr *peer_attr) +{ + static const struct nla_policy policy[] = { + [WGPEER_A_PUBLIC_KEY] = {.minlen = NMP_WIREGUARD_PUBLIC_KEY_LEN}, + [WGPEER_A_PRESHARED_KEY] = {}, + [WGPEER_A_FLAGS] = {.type = NLA_U32}, + [WGPEER_A_ENDPOINT] = {}, + [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = {.type = NLA_U16}, + [WGPEER_A_LAST_HANDSHAKE_TIME] = {}, + [WGPEER_A_RX_BYTES] = {.type = NLA_U64}, + [WGPEER_A_TX_BYTES] = {.type = NLA_U64}, + [WGPEER_A_ALLOWEDIPS] = {.type = NLA_NESTED}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + WireGuardPeerConstruct *peer_c; + + if (nla_parse_nested_arr(tb, peer_attr, policy) < 0) + return FALSE; + + if (!tb[WGPEER_A_PUBLIC_KEY]) + return FALSE; + + /* a peer with the same public key as last peer is just a continuation for extra AllowedIPs */ + peer_c = c_list_last_entry(peers, WireGuardPeerConstruct, lst); + if (peer_c + && !memcmp(nla_data(tb[WGPEER_A_PUBLIC_KEY]), + peer_c->data.public_key, + NMP_WIREGUARD_PUBLIC_KEY_LEN)) { + G_STATIC_ASSERT_EXPR(NMP_WIREGUARD_PUBLIC_KEY_LEN == sizeof(peer_c->data.public_key)); + /* this message is a continuation of the previous peer. + * Only parse WGPEER_A_ALLOWEDIPS below. */ + } else { + /* otherwise, start a new peer */ + peer_c = g_slice_new0(WireGuardPeerConstruct); + c_list_link_tail(peers, &peer_c->lst); + + nla_memcpy(&peer_c->data.public_key, + tb[WGPEER_A_PUBLIC_KEY], + sizeof(peer_c->data.public_key)); + + if (tb[WGPEER_A_PRESHARED_KEY]) { + nla_memcpy(&peer_c->data.preshared_key, + tb[WGPEER_A_PRESHARED_KEY], + sizeof(peer_c->data.preshared_key)); + /* FIXME(netlink-bzero-secret) */ + nm_explicit_bzero(nla_data(tb[WGPEER_A_PRESHARED_KEY]), + nla_len(tb[WGPEER_A_PRESHARED_KEY])); + } + + nm_sock_addr_union_cpy_untrusted( + &peer_c->data.endpoint, + tb[WGPEER_A_ENDPOINT] ? nla_data(tb[WGPEER_A_ENDPOINT]) : NULL, + tb[WGPEER_A_ENDPOINT] ? nla_len(tb[WGPEER_A_ENDPOINT]) : 0); + + if (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]) + peer_c->data.persistent_keepalive_interval = + nla_get_u16(tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]); + if (tb[WGPEER_A_LAST_HANDSHAKE_TIME]) { + if (nla_len(tb[WGPEER_A_LAST_HANDSHAKE_TIME]) + >= sizeof(peer_c->data.last_handshake_time)) + nla_memcpy(&peer_c->data.last_handshake_time, + tb[WGPEER_A_LAST_HANDSHAKE_TIME], + sizeof(peer_c->data.last_handshake_time)); + } + if (tb[WGPEER_A_RX_BYTES]) + peer_c->data.rx_bytes = nla_get_u64(tb[WGPEER_A_RX_BYTES]); + if (tb[WGPEER_A_TX_BYTES]) + peer_c->data.tx_bytes = nla_get_u64(tb[WGPEER_A_TX_BYTES]); + } + + if (tb[WGPEER_A_ALLOWEDIPS]) { + struct nlattr *attr; + int rem; + GArray * allowed_ips = *p_allowed_ips; + + nla_for_each_nested (attr, tb[WGPEER_A_ALLOWEDIPS], rem) { + if (!allowed_ips) { + allowed_ips = g_array_new(FALSE, FALSE, sizeof(NMPWireGuardAllowedIP)); + *p_allowed_ips = allowed_ips; + g_array_set_size(allowed_ips, 1); + } else + g_array_set_size(allowed_ips, allowed_ips->len + 1); + + if (!_wireguard_update_from_allowed_ips_nla( + &g_array_index(allowed_ips, NMPWireGuardAllowedIP, allowed_ips->len - 1), + attr)) { + /* we ignore the error of parsing one allowed-ip. */ + g_array_set_size(allowed_ips, allowed_ips->len - 1); + continue; + } + + if (!peer_c->data._construct_idx_end) + peer_c->data._construct_idx_start = allowed_ips->len - 1; + peer_c->data._construct_idx_end = allowed_ips->len; + } + } + + return TRUE; } typedef struct { - const int ifindex; - NMPObject *obj; - CList peers; - GArray *allowed_ips; + const int ifindex; + NMPObject *obj; + CList peers; + GArray * allowed_ips; } WireGuardParseData; static int -_wireguard_get_device_cb (struct nl_msg *msg, void *arg) -{ - static const struct nla_policy policy[] = { - [WGDEVICE_A_IFINDEX] = { .type = NLA_U32 }, - [WGDEVICE_A_IFNAME] = { .type = NLA_NUL_STRING, .maxlen = IFNAMSIZ }, - [WGDEVICE_A_PRIVATE_KEY] = { }, - [WGDEVICE_A_PUBLIC_KEY] = { }, - [WGDEVICE_A_FLAGS] = { .type = NLA_U32 }, - [WGDEVICE_A_LISTEN_PORT] = { .type = NLA_U16 }, - [WGDEVICE_A_FWMARK] = { .type = NLA_U32 }, - [WGDEVICE_A_PEERS] = { .type = NLA_NESTED }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - WireGuardParseData *parse_data = arg; - - if (genlmsg_parse_arr (nlmsg_hdr (msg), 0, tb, policy) < 0) - return NL_SKIP; - - if (tb[WGDEVICE_A_IFINDEX]) { - int ifindex; - - ifindex = (int) nla_get_u32 (tb[WGDEVICE_A_IFINDEX]); - if ( ifindex <= 0 - || parse_data->ifindex != ifindex) - return NL_SKIP; - } else { - if (!parse_data->obj) - return NL_SKIP; - } - - if (parse_data->obj) { - /* we already have an object instance. This means the netlink message - * is a continuation, only providing more WGDEVICE_A_PEERS data below. */ - } else { - NMPObject *obj; - NMPlatformLnkWireGuard *props; - - obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_WIREGUARD, NULL); - props = &obj->lnk_wireguard; - - if (tb[WGDEVICE_A_PRIVATE_KEY]) { - nla_memcpy (props->private_key, tb[WGDEVICE_A_PRIVATE_KEY], sizeof (props->private_key)); - /* FIXME(netlink-bzero-secret): extend netlink library to wipe memory. For now, - * just hack it here (yes, this does not cover all places where the - * private key was copied). */ - nm_explicit_bzero (nla_data (tb[WGDEVICE_A_PRIVATE_KEY]), - nla_len (tb[WGDEVICE_A_PRIVATE_KEY])); - } - if (tb[WGDEVICE_A_PUBLIC_KEY]) - nla_memcpy (props->public_key, tb[WGDEVICE_A_PUBLIC_KEY], sizeof (props->public_key)); - if (tb[WGDEVICE_A_LISTEN_PORT]) - props->listen_port = nla_get_u16 (tb[WGDEVICE_A_LISTEN_PORT]); - if (tb[WGDEVICE_A_FWMARK]) - props->fwmark = nla_get_u32 (tb[WGDEVICE_A_FWMARK]); - - parse_data->obj = obj; - } - - if (tb[WGDEVICE_A_PEERS]) { - struct nlattr *attr; - int rem; - - nla_for_each_nested (attr, tb[WGDEVICE_A_PEERS], rem) { - if (!_wireguard_update_from_peers_nla (&parse_data->peers, &parse_data->allowed_ips, attr)) { - /* we ignore the error of parsing one peer. - * _wireguard_update_from_peers_nla() leaves the @peers array in the - * desired state. */ - } - } - } - - return NL_OK; +_wireguard_get_device_cb(struct nl_msg *msg, void *arg) +{ + static const struct nla_policy policy[] = { + [WGDEVICE_A_IFINDEX] = {.type = NLA_U32}, + [WGDEVICE_A_IFNAME] = {.type = NLA_NUL_STRING, .maxlen = IFNAMSIZ}, + [WGDEVICE_A_PRIVATE_KEY] = {}, + [WGDEVICE_A_PUBLIC_KEY] = {}, + [WGDEVICE_A_FLAGS] = {.type = NLA_U32}, + [WGDEVICE_A_LISTEN_PORT] = {.type = NLA_U16}, + [WGDEVICE_A_FWMARK] = {.type = NLA_U32}, + [WGDEVICE_A_PEERS] = {.type = NLA_NESTED}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + WireGuardParseData *parse_data = arg; + + if (genlmsg_parse_arr(nlmsg_hdr(msg), 0, tb, policy) < 0) + return NL_SKIP; + + if (tb[WGDEVICE_A_IFINDEX]) { + int ifindex; + + ifindex = (int) nla_get_u32(tb[WGDEVICE_A_IFINDEX]); + if (ifindex <= 0 || parse_data->ifindex != ifindex) + return NL_SKIP; + } else { + if (!parse_data->obj) + return NL_SKIP; + } + + if (parse_data->obj) { + /* we already have an object instance. This means the netlink message + * is a continuation, only providing more WGDEVICE_A_PEERS data below. */ + } else { + NMPObject * obj; + NMPlatformLnkWireGuard *props; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_WIREGUARD, NULL); + props = &obj->lnk_wireguard; + + if (tb[WGDEVICE_A_PRIVATE_KEY]) { + nla_memcpy(props->private_key, tb[WGDEVICE_A_PRIVATE_KEY], sizeof(props->private_key)); + /* FIXME(netlink-bzero-secret): extend netlink library to wipe memory. For now, + * just hack it here (yes, this does not cover all places where the + * private key was copied). */ + nm_explicit_bzero(nla_data(tb[WGDEVICE_A_PRIVATE_KEY]), + nla_len(tb[WGDEVICE_A_PRIVATE_KEY])); + } + if (tb[WGDEVICE_A_PUBLIC_KEY]) + nla_memcpy(props->public_key, tb[WGDEVICE_A_PUBLIC_KEY], sizeof(props->public_key)); + if (tb[WGDEVICE_A_LISTEN_PORT]) + props->listen_port = nla_get_u16(tb[WGDEVICE_A_LISTEN_PORT]); + if (tb[WGDEVICE_A_FWMARK]) + props->fwmark = nla_get_u32(tb[WGDEVICE_A_FWMARK]); + + parse_data->obj = obj; + } + + if (tb[WGDEVICE_A_PEERS]) { + struct nlattr *attr; + int rem; + + nla_for_each_nested (attr, tb[WGDEVICE_A_PEERS], rem) { + if (!_wireguard_update_from_peers_nla(&parse_data->peers, + &parse_data->allowed_ips, + attr)) { + /* we ignore the error of parsing one peer. + * _wireguard_update_from_peers_nla() leaves the @peers array in the + * desired state. */ + } + } + } + + return NL_OK; } static const NMPObject * -_wireguard_read_info (NMPlatform *platform /* used only as logging context */, - struct nl_sock *genl, - int wireguard_family_id, - int ifindex) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - NMPObject *obj = NULL; - WireGuardPeerConstruct *peer_c; - WireGuardPeerConstruct *peer_c_safe; - gs_unref_array GArray *allowed_ips = NULL; - WireGuardParseData parse_data = { - .ifindex = ifindex, - }; - guint i; - - nm_assert (genl); - nm_assert (wireguard_family_id >= 0); - nm_assert (ifindex > 0); - - _LOGT ("wireguard: fetching information for ifindex %d (genl-id %d)...", ifindex, wireguard_family_id); - - msg = nlmsg_alloc (); - - if (!genlmsg_put (msg, - NL_AUTO_PORT, - NL_AUTO_SEQ, - wireguard_family_id, - 0, - NLM_F_DUMP, - WG_CMD_GET_DEVICE, - 1)) - return NULL; - - NLA_PUT_U32 (msg, WGDEVICE_A_IFINDEX, (guint32) ifindex); - - if (nl_send_auto (genl, msg) < 0) - return NULL; - - c_list_init (&parse_data.peers); - - /* we ignore errors, and return whatever we could successfully - * parse. */ - nl_recvmsgs (genl, - &((const struct nl_cb) { - .valid_cb = _wireguard_get_device_cb, - .valid_arg = (gpointer) &parse_data, - })); - - /* unpack: transfer ownership */ - obj = parse_data.obj; - allowed_ips = parse_data.allowed_ips; - - if (!obj) { - while ((peer_c = c_list_first_entry (&parse_data.peers, WireGuardPeerConstruct, lst))) { - c_list_unlink_stale (&peer_c->lst); - nm_explicit_bzero (&peer_c->data.preshared_key, sizeof (peer_c->data.preshared_key)); - g_slice_free (WireGuardPeerConstruct, peer_c); - } - return NULL; - } - - /* we receive peers/allowed-ips possibly in separate netlink messages. Hence, while - * parsing the dump, we don't know upfront how many peers/allowed-ips we will receive. - * - * We solve that, by collecting all peers with a CList. It's done this way, - * because a GArray would require growing the array, but we want to bzero() - * the preshared-key of each peer while reallocating. The CList apprach avoids - * that. - * - * For allowed-ips, we instead track one GArray, which are all appended - * there. The realloc/resize of the GArray is fine there. However, - * while we build the GArray, we don't yet have the final pointers. - * Hence, while constructing, we track the indexes with peer->_construct_idx_* - * fields. These indexes must be converted to actual pointers blow. - * - * This is all done during parsing. In the final NMPObjectLnkWireGuard we - * don't want the CList anymore and repackage the NMPObject tightly. The - * reason is, that NMPObject instances are immutable and long-living. Spend - * a bit effort below during construction to obtain a most suitable representation - * in this regard. */ - obj->_lnk_wireguard.peers_len = c_list_length (&parse_data.peers); - obj->_lnk_wireguard.peers = obj->_lnk_wireguard.peers_len > 0 - ? g_new (NMPWireGuardPeer, obj->_lnk_wireguard.peers_len) - : NULL; - - /* duplicate allowed_ips instead of using the pointer. The GArray possibly has more - * space allocated then we need, and we want to get rid of this excess buffer. - * Note that NMPObject instance is possibly put into the cache and long-living. */ - obj->_lnk_wireguard._allowed_ips_buf_len = allowed_ips ? allowed_ips->len : 0u; - obj->_lnk_wireguard._allowed_ips_buf = obj->_lnk_wireguard._allowed_ips_buf_len > 0 - ? (NMPWireGuardAllowedIP *) nm_memdup (allowed_ips->data, - sizeof (NMPWireGuardAllowedIP) * allowed_ips->len) - : NULL; - - i = 0; - c_list_for_each_entry_safe (peer_c, peer_c_safe, &parse_data.peers, lst) { - NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &obj->_lnk_wireguard.peers[i++]; - - *peer = peer_c->data; - - c_list_unlink_stale (&peer_c->lst); - nm_explicit_bzero (&peer_c->data.preshared_key, sizeof (peer_c->data.preshared_key)); - g_slice_free (WireGuardPeerConstruct, peer_c); - - if (peer->_construct_idx_end != 0) { - guint len; - - nm_assert (obj->_lnk_wireguard._allowed_ips_buf); - nm_assert (peer->_construct_idx_end > peer->_construct_idx_start); - nm_assert (peer->_construct_idx_start < obj->_lnk_wireguard._allowed_ips_buf_len); - nm_assert (peer->_construct_idx_end <= obj->_lnk_wireguard._allowed_ips_buf_len); - - len = peer->_construct_idx_end - peer->_construct_idx_start; - peer->allowed_ips = &obj->_lnk_wireguard._allowed_ips_buf[peer->_construct_idx_start]; - peer->allowed_ips_len = len; - } else { - nm_assert (!peer->_construct_idx_start); - nm_assert (!peer->_construct_idx_end); - peer->allowed_ips = NULL; - peer->allowed_ips_len = 0; - } - } - - return obj; +_wireguard_read_info(NMPlatform * platform /* used only as logging context */, + struct nl_sock *genl, + int wireguard_family_id, + int ifindex) +{ + nm_auto_nlmsg struct nl_msg *msg = NULL; + NMPObject * obj = NULL; + WireGuardPeerConstruct * peer_c; + WireGuardPeerConstruct * peer_c_safe; + gs_unref_array GArray *allowed_ips = NULL; + WireGuardParseData parse_data = { + .ifindex = ifindex, + }; + guint i; + + nm_assert(genl); + nm_assert(wireguard_family_id >= 0); + nm_assert(ifindex > 0); + + _LOGT("wireguard: fetching information for ifindex %d (genl-id %d)...", + ifindex, + wireguard_family_id); + + msg = nlmsg_alloc(); + + if (!genlmsg_put(msg, + NL_AUTO_PORT, + NL_AUTO_SEQ, + wireguard_family_id, + 0, + NLM_F_DUMP, + WG_CMD_GET_DEVICE, + 1)) + return NULL; + + NLA_PUT_U32(msg, WGDEVICE_A_IFINDEX, (guint32) ifindex); + + if (nl_send_auto(genl, msg) < 0) + return NULL; + + c_list_init(&parse_data.peers); + + /* we ignore errors, and return whatever we could successfully + * parse. */ + nl_recvmsgs(genl, + &((const struct nl_cb){ + .valid_cb = _wireguard_get_device_cb, + .valid_arg = (gpointer) &parse_data, + })); + + /* unpack: transfer ownership */ + obj = parse_data.obj; + allowed_ips = parse_data.allowed_ips; + + if (!obj) { + while ((peer_c = c_list_first_entry(&parse_data.peers, WireGuardPeerConstruct, lst))) { + c_list_unlink_stale(&peer_c->lst); + nm_explicit_bzero(&peer_c->data.preshared_key, sizeof(peer_c->data.preshared_key)); + g_slice_free(WireGuardPeerConstruct, peer_c); + } + return NULL; + } + + /* we receive peers/allowed-ips possibly in separate netlink messages. Hence, while + * parsing the dump, we don't know upfront how many peers/allowed-ips we will receive. + * + * We solve that, by collecting all peers with a CList. It's done this way, + * because a GArray would require growing the array, but we want to bzero() + * the preshared-key of each peer while reallocating. The CList apprach avoids + * that. + * + * For allowed-ips, we instead track one GArray, which are all appended + * there. The realloc/resize of the GArray is fine there. However, + * while we build the GArray, we don't yet have the final pointers. + * Hence, while constructing, we track the indexes with peer->_construct_idx_* + * fields. These indexes must be converted to actual pointers blow. + * + * This is all done during parsing. In the final NMPObjectLnkWireGuard we + * don't want the CList anymore and repackage the NMPObject tightly. The + * reason is, that NMPObject instances are immutable and long-living. Spend + * a bit effort below during construction to obtain a most suitable representation + * in this regard. */ + obj->_lnk_wireguard.peers_len = c_list_length(&parse_data.peers); + obj->_lnk_wireguard.peers = obj->_lnk_wireguard.peers_len > 0 + ? g_new(NMPWireGuardPeer, obj->_lnk_wireguard.peers_len) + : NULL; + + /* duplicate allowed_ips instead of using the pointer. The GArray possibly has more + * space allocated then we need, and we want to get rid of this excess buffer. + * Note that NMPObject instance is possibly put into the cache and long-living. */ + obj->_lnk_wireguard._allowed_ips_buf_len = allowed_ips ? allowed_ips->len : 0u; + obj->_lnk_wireguard._allowed_ips_buf = + obj->_lnk_wireguard._allowed_ips_buf_len > 0 + ? (NMPWireGuardAllowedIP *) nm_memdup(allowed_ips->data, + sizeof(NMPWireGuardAllowedIP) * allowed_ips->len) + : NULL; + + i = 0; + c_list_for_each_entry_safe (peer_c, peer_c_safe, &parse_data.peers, lst) { + NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &obj->_lnk_wireguard.peers[i++]; + + *peer = peer_c->data; + + c_list_unlink_stale(&peer_c->lst); + nm_explicit_bzero(&peer_c->data.preshared_key, sizeof(peer_c->data.preshared_key)); + g_slice_free(WireGuardPeerConstruct, peer_c); + + if (peer->_construct_idx_end != 0) { + guint len; + + nm_assert(obj->_lnk_wireguard._allowed_ips_buf); + nm_assert(peer->_construct_idx_end > peer->_construct_idx_start); + nm_assert(peer->_construct_idx_start < obj->_lnk_wireguard._allowed_ips_buf_len); + nm_assert(peer->_construct_idx_end <= obj->_lnk_wireguard._allowed_ips_buf_len); + + len = peer->_construct_idx_end - peer->_construct_idx_start; + peer->allowed_ips = &obj->_lnk_wireguard._allowed_ips_buf[peer->_construct_idx_start]; + peer->allowed_ips_len = len; + } else { + nm_assert(!peer->_construct_idx_start); + nm_assert(!peer->_construct_idx_end); + peer->allowed_ips = NULL; + peer->allowed_ips_len = 0; + } + } + + return obj; nla_put_failure: - g_return_val_if_reached (NULL); + g_return_val_if_reached(NULL); } static int -_wireguard_get_family_id (NMPlatform *platform, int ifindex_try) +_wireguard_get_family_id(NMPlatform *platform, int ifindex_try) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int wireguard_family_id = -1; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + int wireguard_family_id = -1; - if (ifindex_try > 0) { - const NMPlatformLink *plink; + if (ifindex_try > 0) { + const NMPlatformLink *plink; - if (nm_platform_link_get_lnk_wireguard (platform, ifindex_try, &plink)) - wireguard_family_id = NMP_OBJECT_UP_CAST (plink)->_link.wireguard_family_id; - } - if (wireguard_family_id < 0) - wireguard_family_id = genl_ctrl_resolve (priv->genl, "wireguard"); - return wireguard_family_id; + if (nm_platform_link_get_lnk_wireguard(platform, ifindex_try, &plink)) + wireguard_family_id = NMP_OBJECT_UP_CAST(plink)->_link.wireguard_family_id; + } + if (wireguard_family_id < 0) + wireguard_family_id = genl_ctrl_resolve(priv->genl, "wireguard"); + return wireguard_family_id; } static const NMPObject * -_wireguard_refresh_link (NMPlatform *platform, - int wireguard_family_id, - int ifindex) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_new = NULL; - nm_auto_nmpobj const NMPObject *lnk_new = NULL; - NMPCacheOpsType cache_op; - const NMPObject *plink = NULL; - nm_auto_nmpobj NMPObject *obj = NULL; - - nm_assert (wireguard_family_id >= 0); - nm_assert (ifindex > 0); - - nm_platform_process_events (platform); - - plink = nm_platform_link_get_obj (platform, ifindex, TRUE); - - if ( !plink - || plink->link.type != NM_LINK_TYPE_WIREGUARD) { - nm_platform_link_refresh (platform, ifindex); - plink = nm_platform_link_get_obj (platform, ifindex, TRUE); - if ( !plink - || plink->link.type != NM_LINK_TYPE_WIREGUARD) - return NULL; - if (NMP_OBJECT_GET_TYPE (plink->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD) - lnk_new = nmp_object_ref (plink->_link.netlink.lnk); - } else { - lnk_new = _wireguard_read_info (platform, - priv->genl, - wireguard_family_id, - ifindex); - if (!lnk_new) { - if (NMP_OBJECT_GET_TYPE (plink->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD) - lnk_new = nmp_object_ref (plink->_link.netlink.lnk); - } else if (nmp_object_equal (plink->_link.netlink.lnk, lnk_new)) { - nmp_object_unref (lnk_new); - lnk_new = nmp_object_ref (plink->_link.netlink.lnk); - } - } - - if ( plink->_link.wireguard_family_id == wireguard_family_id - && plink->_link.netlink.lnk == lnk_new) - return plink; - - /* we use nmp_cache_update_netlink() to re-inject the new object into the cache. - * For that, we need to clone it, and tweak it so that it's suitable. It's a bit - * of a hack, in particular that we need to clear driver and udev-device. */ - obj = nmp_object_clone (plink, FALSE); - obj->_link.wireguard_family_id = wireguard_family_id; - nmp_object_unref (obj->_link.netlink.lnk); - obj->_link.netlink.lnk = g_steal_pointer (&lnk_new); - obj->link.driver = NULL; - nm_clear_pointer (&obj->_link.udev.device, udev_device_unref); - - cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), - obj, - FALSE, - &obj_old, - &obj_new); - nm_assert (NM_IN_SET (cache_op, NMP_CACHE_OPS_UPDATED)); - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - cache_on_change (platform, cache_op, obj_old, obj_new); - nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); - } - - nm_assert ( !obj_new - || ( NMP_OBJECT_GET_TYPE (obj_new) == NMP_OBJECT_TYPE_LINK - && obj_new->link.type == NM_LINK_TYPE_WIREGUARD - && ( !obj_new->_link.netlink.lnk - || NMP_OBJECT_GET_TYPE (obj_new->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD))); - return obj_new; +_wireguard_refresh_link(NMPlatform *platform, int wireguard_family_id, int ifindex) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + nm_auto_nmpobj const NMPObject *lnk_new = NULL; + NMPCacheOpsType cache_op; + const NMPObject * plink = NULL; + nm_auto_nmpobj NMPObject *obj = NULL; + + nm_assert(wireguard_family_id >= 0); + nm_assert(ifindex > 0); + + nm_platform_process_events(platform); + + plink = nm_platform_link_get_obj(platform, ifindex, TRUE); + + if (!plink || plink->link.type != NM_LINK_TYPE_WIREGUARD) { + nm_platform_link_refresh(platform, ifindex); + plink = nm_platform_link_get_obj(platform, ifindex, TRUE); + if (!plink || plink->link.type != NM_LINK_TYPE_WIREGUARD) + return NULL; + if (NMP_OBJECT_GET_TYPE(plink->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD) + lnk_new = nmp_object_ref(plink->_link.netlink.lnk); + } else { + lnk_new = _wireguard_read_info(platform, priv->genl, wireguard_family_id, ifindex); + if (!lnk_new) { + if (NMP_OBJECT_GET_TYPE(plink->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD) + lnk_new = nmp_object_ref(plink->_link.netlink.lnk); + } else if (nmp_object_equal(plink->_link.netlink.lnk, lnk_new)) { + nmp_object_unref(lnk_new); + lnk_new = nmp_object_ref(plink->_link.netlink.lnk); + } + } + + if (plink->_link.wireguard_family_id == wireguard_family_id + && plink->_link.netlink.lnk == lnk_new) + return plink; + + /* we use nmp_cache_update_netlink() to re-inject the new object into the cache. + * For that, we need to clone it, and tweak it so that it's suitable. It's a bit + * of a hack, in particular that we need to clear driver and udev-device. */ + obj = nmp_object_clone(plink, FALSE); + obj->_link.wireguard_family_id = wireguard_family_id; + nmp_object_unref(obj->_link.netlink.lnk); + obj->_link.netlink.lnk = g_steal_pointer(&lnk_new); + obj->link.driver = NULL; + nm_clear_pointer(&obj->_link.udev.device, udev_device_unref); + + cache_op = + nmp_cache_update_netlink(nm_platform_get_cache(platform), obj, FALSE, &obj_old, &obj_new); + nm_assert(NM_IN_SET(cache_op, NMP_CACHE_OPS_UPDATED)); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + cache_on_change(platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); + } + + nm_assert(!obj_new + || (NMP_OBJECT_GET_TYPE(obj_new) == NMP_OBJECT_TYPE_LINK + && obj_new->link.type == NM_LINK_TYPE_WIREGUARD + && (!obj_new->_link.netlink.lnk + || NMP_OBJECT_GET_TYPE(obj_new->_link.netlink.lnk) + == NMP_OBJECT_TYPE_LNK_WIREGUARD))); + return obj_new; } static int -_wireguard_create_change_nlmsgs (NMPlatform *platform, - int ifindex, - int wireguard_family_id, - const NMPlatformLnkWireGuard *lnk_wireguard, - const NMPWireGuardPeer *peers, - const NMPlatformWireGuardChangePeerFlags *peer_flags, - guint peers_len, - NMPlatformWireGuardChangeFlags change_flags, - GPtrArray **out_msgs) -{ - gs_unref_ptrarray GPtrArray *msgs = NULL; - nm_auto_nlmsg struct nl_msg *msg = NULL; - const guint IDX_NIL = G_MAXUINT; - guint idx_peer_curr; - guint idx_allowed_ips_curr; - struct nlattr *nest_peers; - struct nlattr *nest_curr_peer; - struct nlattr *nest_allowed_ips; - struct nlattr *nest_curr_allowed_ip; - NMPlatformWireGuardChangePeerFlags p_flags = NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_DEFAULT; - -#define _nla_nest_end(msg, nest_start) \ - G_STMT_START { \ - if (nla_nest_end ((msg), (nest_start)) < 0) \ - g_return_val_if_reached (-NME_BUG); \ - } G_STMT_END - - /* Adapted from LGPL-2.1+ code [1]. - * - * [1] https://git.zx2c4.com/WireGuard/tree/contrib/examples/embeddable-wg-library/wireguard.c?id=5e99a6d43fe2351adf36c786f5ea2086a8fe7ab8#n1073 */ - - idx_peer_curr = IDX_NIL; - idx_allowed_ips_curr = IDX_NIL; - - /* TODO: for the moment, we always reset all peers and allowed-ips (WGDEVICE_F_REPLACE_PEERS, WGPEER_F_REPLACE_ALLOWEDIPS). - * The platform API should be extended to also support partial updates. In particular, configuring the same configuration - * multiple times, should not clear and re-add all settings, but rather sync the existing settings with the desired configuration. */ +_wireguard_create_change_nlmsgs(NMPlatform * platform, + int ifindex, + int wireguard_family_id, + const NMPlatformLnkWireGuard * lnk_wireguard, + const NMPWireGuardPeer * peers, + const NMPlatformWireGuardChangePeerFlags *peer_flags, + guint peers_len, + NMPlatformWireGuardChangeFlags change_flags, + GPtrArray ** out_msgs) +{ + gs_unref_ptrarray GPtrArray * msgs = NULL; + nm_auto_nlmsg struct nl_msg * msg = NULL; + const guint IDX_NIL = G_MAXUINT; + guint idx_peer_curr; + guint idx_allowed_ips_curr; + struct nlattr * nest_peers; + struct nlattr * nest_curr_peer; + struct nlattr * nest_allowed_ips; + struct nlattr * nest_curr_allowed_ip; + NMPlatformWireGuardChangePeerFlags p_flags = NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_DEFAULT; + +#define _nla_nest_end(msg, nest_start) \ + G_STMT_START \ + { \ + if (nla_nest_end((msg), (nest_start)) < 0) \ + g_return_val_if_reached(-NME_BUG); \ + } \ + G_STMT_END + + /* Adapted from LGPL-2.1+ code [1]. + * + * [1] https://git.zx2c4.com/WireGuard/tree/contrib/examples/embeddable-wg-library/wireguard.c?id=5e99a6d43fe2351adf36c786f5ea2086a8fe7ab8#n1073 */ + + idx_peer_curr = IDX_NIL; + idx_allowed_ips_curr = IDX_NIL; + + /* TODO: for the moment, we always reset all peers and allowed-ips (WGDEVICE_F_REPLACE_PEERS, WGPEER_F_REPLACE_ALLOWEDIPS). + * The platform API should be extended to also support partial updates. In particular, configuring the same configuration + * multiple times, should not clear and re-add all settings, but rather sync the existing settings with the desired configuration. */ again: - msg = nlmsg_alloc (); - if (!genlmsg_put (msg, - NL_AUTO_PORT, - NL_AUTO_SEQ, - wireguard_family_id, - 0, - NLM_F_REQUEST, - WG_CMD_SET_DEVICE, - 1)) - g_return_val_if_reached (-NME_BUG); - - NLA_PUT_U32 (msg, WGDEVICE_A_IFINDEX, (guint32) ifindex); - - if (idx_peer_curr == IDX_NIL) { - guint32 flags; - - if (NM_FLAGS_HAS (change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY)) - NLA_PUT (msg, WGDEVICE_A_PRIVATE_KEY, sizeof (lnk_wireguard->private_key), lnk_wireguard->private_key); - if (NM_FLAGS_HAS (change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT)) - NLA_PUT_U16 (msg, WGDEVICE_A_LISTEN_PORT, lnk_wireguard->listen_port); - if (NM_FLAGS_HAS (change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK)) - NLA_PUT_U32 (msg, WGDEVICE_A_FWMARK, lnk_wireguard->fwmark); - - flags = 0; - if (NM_FLAGS_HAS (change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS)) - flags |= WGDEVICE_F_REPLACE_PEERS; - NLA_PUT_U32 (msg, WGDEVICE_A_FLAGS, flags); - } - - if (peers_len == 0) - goto send; - - nest_curr_peer = NULL; - nest_allowed_ips = NULL; - nest_curr_allowed_ip = NULL; - - nest_peers = nla_nest_start (msg, WGDEVICE_A_PEERS); - if (!nest_peers) - g_return_val_if_reached (-NME_BUG); - - if (idx_peer_curr == IDX_NIL) - idx_peer_curr = 0; - for (; idx_peer_curr < peers_len; idx_peer_curr++) { - const NMPWireGuardPeer *p = &peers[idx_peer_curr]; - - if (peer_flags) { - p_flags = peer_flags[idx_peer_curr]; - if (!NM_FLAGS_ANY (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS)) { - /* no flags set. We take that as indication to skip configuring the peer - * entirely. */ - nm_assert (p_flags == NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE); - continue; - } - } - - nest_curr_peer = nla_nest_start (msg, 0); - if (!nest_curr_peer) - goto toobig_peers; - - if (nla_put (msg, WGPEER_A_PUBLIC_KEY, NMP_WIREGUARD_PUBLIC_KEY_LEN, p->public_key) < 0) - goto toobig_peers; - - if (NM_FLAGS_HAS (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME)) { - /* all other p_flags are silently ignored. */ - if (nla_put_uint32 (msg, WGPEER_A_FLAGS, WGPEER_F_REMOVE_ME) < 0) - goto toobig_peers; - } else { - - if (idx_allowed_ips_curr == IDX_NIL) { - if ( NM_FLAGS_HAS (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY) - && nla_put (msg, WGPEER_A_PRESHARED_KEY, sizeof (p->preshared_key), p->preshared_key) < 0) - goto toobig_peers; - - if ( NM_FLAGS_HAS (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL) - && nla_put_uint16 (msg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, p->persistent_keepalive_interval) < 0) - goto toobig_peers; - - if ( NM_FLAGS_HAS (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS) - && nla_put_uint32 (msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS) < 0) - goto toobig_peers; - - if (NM_FLAGS_HAS (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT)) { - if (NM_IN_SET (p->endpoint.sa.sa_family, AF_INET, AF_INET6)) { - if (nla_put (msg, - WGPEER_A_ENDPOINT, - p->endpoint.sa.sa_family == AF_INET - ? sizeof (p->endpoint.in) - : sizeof (p->endpoint.in6), - &p->endpoint) < 0) - goto toobig_peers; - } else { - /* I think there is no way to clear an endpoint, though there shold be. */ - nm_assert (p->endpoint.sa.sa_family == AF_UNSPEC); - } - } - } - - if ( NM_FLAGS_HAS (p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS) - && p->allowed_ips_len > 0) { - if (idx_allowed_ips_curr == IDX_NIL) - idx_allowed_ips_curr = 0; - - nest_allowed_ips = nla_nest_start (msg, WGPEER_A_ALLOWEDIPS); - if (!nest_allowed_ips) - goto toobig_allowedips; - - for (; idx_allowed_ips_curr < p->allowed_ips_len; idx_allowed_ips_curr++) { - const NMPWireGuardAllowedIP *aip = &p->allowed_ips[idx_allowed_ips_curr]; - - nest_curr_allowed_ip = nla_nest_start (msg, 0); - if (!nest_curr_allowed_ip) - goto toobig_allowedips; - - g_return_val_if_fail (NM_IN_SET (aip->family, AF_INET, AF_INET6), -NME_BUG); - - if (nla_put_uint16 (msg, WGALLOWEDIP_A_FAMILY, aip->family) < 0) - goto toobig_allowedips; - if (nla_put (msg, - WGALLOWEDIP_A_IPADDR, - nm_utils_addr_family_to_size (aip->family), - &aip->addr) < 0) - goto toobig_allowedips; - if (nla_put_uint8 (msg, WGALLOWEDIP_A_CIDR_MASK, aip->mask) < 0) - goto toobig_allowedips; - - _nla_nest_end (msg, nest_curr_allowed_ip); - nest_curr_allowed_ip = NULL; - } - idx_allowed_ips_curr = IDX_NIL; - - _nla_nest_end (msg, nest_allowed_ips); - nest_allowed_ips = NULL; - } - } - - _nla_nest_end (msg, nest_curr_peer); - nest_curr_peer = NULL; - } - - _nla_nest_end (msg, nest_peers); - goto send; + msg = nlmsg_alloc(); + if (!genlmsg_put(msg, + NL_AUTO_PORT, + NL_AUTO_SEQ, + wireguard_family_id, + 0, + NLM_F_REQUEST, + WG_CMD_SET_DEVICE, + 1)) + g_return_val_if_reached(-NME_BUG); + + NLA_PUT_U32(msg, WGDEVICE_A_IFINDEX, (guint32) ifindex); + + if (idx_peer_curr == IDX_NIL) { + guint32 flags; + + if (NM_FLAGS_HAS(change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY)) + NLA_PUT(msg, + WGDEVICE_A_PRIVATE_KEY, + sizeof(lnk_wireguard->private_key), + lnk_wireguard->private_key); + if (NM_FLAGS_HAS(change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT)) + NLA_PUT_U16(msg, WGDEVICE_A_LISTEN_PORT, lnk_wireguard->listen_port); + if (NM_FLAGS_HAS(change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK)) + NLA_PUT_U32(msg, WGDEVICE_A_FWMARK, lnk_wireguard->fwmark); + + flags = 0; + if (NM_FLAGS_HAS(change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS)) + flags |= WGDEVICE_F_REPLACE_PEERS; + NLA_PUT_U32(msg, WGDEVICE_A_FLAGS, flags); + } + + if (peers_len == 0) + goto send; + + nest_curr_peer = NULL; + nest_allowed_ips = NULL; + nest_curr_allowed_ip = NULL; + + nest_peers = nla_nest_start(msg, WGDEVICE_A_PEERS); + if (!nest_peers) + g_return_val_if_reached(-NME_BUG); + + if (idx_peer_curr == IDX_NIL) + idx_peer_curr = 0; + for (; idx_peer_curr < peers_len; idx_peer_curr++) { + const NMPWireGuardPeer *p = &peers[idx_peer_curr]; + + if (peer_flags) { + p_flags = peer_flags[idx_peer_curr]; + if (!NM_FLAGS_ANY(p_flags, + NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME + | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY + | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL + | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT + | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS + | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS)) { + /* no flags set. We take that as indication to skip configuring the peer + * entirely. */ + nm_assert(p_flags == NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE); + continue; + } + } + + nest_curr_peer = nla_nest_start(msg, 0); + if (!nest_curr_peer) + goto toobig_peers; + + if (nla_put(msg, WGPEER_A_PUBLIC_KEY, NMP_WIREGUARD_PUBLIC_KEY_LEN, p->public_key) < 0) + goto toobig_peers; + + if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME)) { + /* all other p_flags are silently ignored. */ + if (nla_put_uint32(msg, WGPEER_A_FLAGS, WGPEER_F_REMOVE_ME) < 0) + goto toobig_peers; + } else { + if (idx_allowed_ips_curr == IDX_NIL) { + if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY) + && nla_put(msg, + WGPEER_A_PRESHARED_KEY, + sizeof(p->preshared_key), + p->preshared_key) + < 0) + goto toobig_peers; + + if (NM_FLAGS_HAS(p_flags, + NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL) + && nla_put_uint16(msg, + WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, + p->persistent_keepalive_interval) + < 0) + goto toobig_peers; + + if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS) + && nla_put_uint32(msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS) < 0) + goto toobig_peers; + + if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT)) { + if (NM_IN_SET(p->endpoint.sa.sa_family, AF_INET, AF_INET6)) { + if (nla_put(msg, + WGPEER_A_ENDPOINT, + p->endpoint.sa.sa_family == AF_INET ? sizeof(p->endpoint.in) + : sizeof(p->endpoint.in6), + &p->endpoint) + < 0) + goto toobig_peers; + } else { + /* I think there is no way to clear an endpoint, though there should be. */ + nm_assert(p->endpoint.sa.sa_family == AF_UNSPEC); + } + } + } + + if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS) + && p->allowed_ips_len > 0) { + if (idx_allowed_ips_curr == IDX_NIL) + idx_allowed_ips_curr = 0; + + nest_allowed_ips = nla_nest_start(msg, WGPEER_A_ALLOWEDIPS); + if (!nest_allowed_ips) + goto toobig_allowedips; + + for (; idx_allowed_ips_curr < p->allowed_ips_len; idx_allowed_ips_curr++) { + const NMPWireGuardAllowedIP *aip = &p->allowed_ips[idx_allowed_ips_curr]; + + nest_curr_allowed_ip = nla_nest_start(msg, 0); + if (!nest_curr_allowed_ip) + goto toobig_allowedips; + + g_return_val_if_fail(NM_IN_SET(aip->family, AF_INET, AF_INET6), -NME_BUG); + + if (nla_put_uint16(msg, WGALLOWEDIP_A_FAMILY, aip->family) < 0) + goto toobig_allowedips; + if (nla_put(msg, + WGALLOWEDIP_A_IPADDR, + nm_utils_addr_family_to_size(aip->family), + &aip->addr) + < 0) + goto toobig_allowedips; + if (nla_put_uint8(msg, WGALLOWEDIP_A_CIDR_MASK, aip->mask) < 0) + goto toobig_allowedips; + + _nla_nest_end(msg, nest_curr_allowed_ip); + nest_curr_allowed_ip = NULL; + } + idx_allowed_ips_curr = IDX_NIL; + + _nla_nest_end(msg, nest_allowed_ips); + nest_allowed_ips = NULL; + } + } + + _nla_nest_end(msg, nest_curr_peer); + nest_curr_peer = NULL; + } + + _nla_nest_end(msg, nest_peers); + goto send; toobig_allowedips: - if (nest_curr_allowed_ip) - nla_nest_cancel (msg, nest_curr_allowed_ip); - if (nest_allowed_ips) - nla_nest_cancel (msg, nest_allowed_ips); - _nla_nest_end (msg, nest_curr_peer); - _nla_nest_end (msg, nest_peers); - goto send; + if (nest_curr_allowed_ip) + nla_nest_cancel(msg, nest_curr_allowed_ip); + if (nest_allowed_ips) + nla_nest_cancel(msg, nest_allowed_ips); + _nla_nest_end(msg, nest_curr_peer); + _nla_nest_end(msg, nest_peers); + goto send; toobig_peers: - if (nest_curr_peer) - nla_nest_cancel (msg, nest_curr_peer); - _nla_nest_end (msg, nest_peers); - goto send; + if (nest_curr_peer) + nla_nest_cancel(msg, nest_curr_peer); + _nla_nest_end(msg, nest_peers); + goto send; send: - if (!msgs) - msgs = g_ptr_array_new_with_free_func ((GDestroyNotify) nlmsg_free); - g_ptr_array_add (msgs, g_steal_pointer (&msg)); + if (!msgs) + msgs = g_ptr_array_new_with_free_func((GDestroyNotify) nlmsg_free); + g_ptr_array_add(msgs, g_steal_pointer(&msg)); - if ( idx_peer_curr != IDX_NIL - && idx_peer_curr < peers_len) - goto again; + if (idx_peer_curr != IDX_NIL && idx_peer_curr < peers_len) + goto again; - NM_SET_OUT (out_msgs, g_steal_pointer (&msgs)); - return 0; + NM_SET_OUT(out_msgs, g_steal_pointer(&msgs)); + return 0; nla_put_failure: - g_return_val_if_reached (-NME_BUG); + g_return_val_if_reached(-NME_BUG); #undef _nla_nest_end } static int -link_wireguard_change (NMPlatform *platform, - int ifindex, - const NMPlatformLnkWireGuard *lnk_wireguard, - const NMPWireGuardPeer *peers, - const NMPlatformWireGuardChangePeerFlags *peer_flags, - guint peers_len, - NMPlatformWireGuardChangeFlags change_flags) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - gs_unref_ptrarray GPtrArray *msgs = NULL; - int wireguard_family_id; - guint i; - int r; - - wireguard_family_id = _wireguard_get_family_id (platform, ifindex); - if (wireguard_family_id < 0) - return -NME_PL_NO_FIRMWARE; - - r = _wireguard_create_change_nlmsgs (platform, - ifindex, - wireguard_family_id, - lnk_wireguard, - peers, - peer_flags, - peers_len, - change_flags, - &msgs); - if (r < 0) { - _LOGW ("wireguard: set-device, cannot construct netlink message: %s", nm_strerror (r)); - return r; - } - - for (i = 0; i < msgs->len; i++) { - r = nl_send_auto (priv->genl, msgs->pdata[i]); - if (r < 0) { - _LOGW ("wireguard: set-device, send netlink message #%u failed: %s", i, nm_strerror (r)); - return r; - } - - do { - r = nl_recvmsgs (priv->genl, NULL); - } while (r == -EAGAIN); - if (r < 0) { - _LOGW ("wireguard: set-device, message #%u was rejected: %s", i, nm_strerror (r)); - return r; - } - - _LOGT ("wireguard: set-device, message #%u sent and confirmed", i); - } - - _wireguard_refresh_link (platform, wireguard_family_id, ifindex); - - return 0; +link_wireguard_change(NMPlatform * platform, + int ifindex, + const NMPlatformLnkWireGuard * lnk_wireguard, + const NMPWireGuardPeer * peers, + const NMPlatformWireGuardChangePeerFlags *peer_flags, + guint peers_len, + NMPlatformWireGuardChangeFlags change_flags) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + gs_unref_ptrarray GPtrArray *msgs = NULL; + int wireguard_family_id; + guint i; + int r; + + wireguard_family_id = _wireguard_get_family_id(platform, ifindex); + if (wireguard_family_id < 0) + return -NME_PL_NO_FIRMWARE; + + r = _wireguard_create_change_nlmsgs(platform, + ifindex, + wireguard_family_id, + lnk_wireguard, + peers, + peer_flags, + peers_len, + change_flags, + &msgs); + if (r < 0) { + _LOGW("wireguard: set-device, cannot construct netlink message: %s", nm_strerror(r)); + return r; + } + + for (i = 0; i < msgs->len; i++) { + r = nl_send_auto(priv->genl, msgs->pdata[i]); + if (r < 0) { + _LOGW("wireguard: set-device, send netlink message #%u failed: %s", i, nm_strerror(r)); + return r; + } + + do { + r = nl_recvmsgs(priv->genl, NULL); + } while (r == -EAGAIN); + if (r < 0) { + _LOGW("wireguard: set-device, message #%u was rejected: %s", i, nm_strerror(r)); + return r; + } + + _LOGT("wireguard: set-device, message #%u sent and confirmed", i); + } + + _wireguard_refresh_link(platform, wireguard_family_id, ifindex); + + return 0; } /*****************************************************************************/ static void -_nmp_link_address_set (NMPLinkAddress *dst, - const struct nlattr *nla) +_nmp_link_address_set(NMPLinkAddress *dst, const struct nlattr *nla) { - *dst = (NMPLinkAddress) { - .len = 0, - }; - if (nla) { - int l = nla_len (nla); + *dst = (NMPLinkAddress){ + .len = 0, + }; + if (nla) { + int l = nla_len(nla); - if ( l > 0 - && l <= NM_UTILS_HWADDR_LEN_MAX) { - G_STATIC_ASSERT_EXPR (sizeof (dst->data) == NM_UTILS_HWADDR_LEN_MAX); - memcpy (dst->data, nla_data (nla), l); - dst->len = l; - } - } + if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) { + G_STATIC_ASSERT_EXPR(sizeof(dst->data) == NM_UTILS_HWADDR_LEN_MAX); + memcpy(dst->data, nla_data(nla), l); + dst->len = l; + } + } } /* Copied and heavily modified from libnl3's link_msg_parser(). */ static NMPObject * -_new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr *nlh, gboolean id_only) -{ - static const struct nla_policy policy[] = { - [IFLA_IFNAME] = { .type = NLA_STRING, - .maxlen = IFNAMSIZ }, - [IFLA_MTU] = { .type = NLA_U32 }, - [IFLA_TXQLEN] = { .type = NLA_U32 }, - [IFLA_LINK] = { .type = NLA_U32 }, - [IFLA_WEIGHT] = { .type = NLA_U32 }, - [IFLA_MASTER] = { .type = NLA_U32 }, - [IFLA_OPERSTATE] = { .type = NLA_U8 }, - [IFLA_LINKMODE] = { .type = NLA_U8 }, - [IFLA_LINKINFO] = { .type = NLA_NESTED }, - [IFLA_QDISC] = { .type = NLA_STRING, - .maxlen = IFQDISCSIZ }, - [IFLA_STATS] = { .minlen = nm_offsetofend (struct rtnl_link_stats, tx_compressed) }, - [IFLA_STATS64] = { .minlen = nm_offsetofend (struct rtnl_link_stats64, tx_compressed)}, - [IFLA_MAP] = { .minlen = nm_offsetofend (struct rtnl_link_ifmap, port) }, - [IFLA_IFALIAS] = { .type = NLA_STRING, .maxlen = IFALIASZ }, - [IFLA_NUM_VF] = { .type = NLA_U32 }, - [IFLA_AF_SPEC] = { .type = NLA_NESTED }, - [IFLA_PROMISCUITY] = { .type = NLA_U32 }, - [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 }, - [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, - [IFLA_GROUP] = { .type = NLA_U32 }, - [IFLA_CARRIER] = { .type = NLA_U8 }, - [IFLA_PHYS_PORT_ID] = { .type = NLA_UNSPEC }, - [IFLA_NET_NS_PID] = { .type = NLA_U32 }, - [IFLA_NET_NS_FD] = { .type = NLA_U32 }, - [IFLA_LINK_NETNSID] = { }, - }; - const struct ifinfomsg *ifi; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - struct nlattr *nl_info_data = NULL; - const char *nl_info_kind = NULL; - nm_auto_nmpobj NMPObject *obj = NULL; - gboolean completed_from_cache_val = FALSE; - gboolean *completed_from_cache = cache ? &completed_from_cache_val : NULL; - const NMPObject *link_cached = NULL; - const NMPObject *lnk_data = NULL; - gboolean address_complete_from_cache = TRUE; - gboolean broadcast_complete_from_cache = TRUE; - gboolean lnk_data_complete_from_cache = TRUE; - gboolean need_ext_data = FALSE; - gboolean af_inet6_token_valid = FALSE; - gboolean af_inet6_addr_gen_mode_valid = FALSE; - - if (!nlmsg_valid_hdr (nlh, sizeof (*ifi))) - return NULL; - - ifi = nlmsg_data (nlh); - - if (ifi->ifi_family != AF_UNSPEC) - return NULL; - if (ifi->ifi_index <= 0) - return NULL; - - obj = nmp_object_new_link (ifi->ifi_index); - - if (id_only) - return g_steal_pointer (&obj); - - if (nlmsg_parse_arr (nlh, sizeof (*ifi), tb, policy) < 0) - return NULL; - - if (!tb[IFLA_IFNAME]) - return NULL; - nla_strlcpy (obj->link.name, tb[IFLA_IFNAME], IFNAMSIZ); - if (!obj->link.name[0]) - return NULL; - - if (!tb[IFLA_MTU]) { - /* Kernel has two places that send RTM_GETLINK messages: - * net/core/rtnetlink.c and net/wireless/ext-core.c. - * Unfotunatelly ext-core.c sets only IFLA_WIRELESS and - * IFLA_IFNAME. This confuses code in this function, because - * it cannot get complete set of data for the interface and - * later incomplete object this function creates is used to - * overwrite existing data in NM's cache. - * Since ext-core.c doesn't set IFLA_MTU we can use it as a - * signal to ignore incoming message. - * To some extent this is a hack and correct approach is to - * merge objects per-field. - */ - return NULL; - } - obj->link.mtu = nla_get_u32 (tb[IFLA_MTU]); - - if (tb[IFLA_LINKINFO]) { - static const struct nla_policy policy_link_info[] = { - [IFLA_INFO_KIND] = { .type = NLA_STRING }, - [IFLA_INFO_DATA] = { .type = NLA_NESTED }, - [IFLA_INFO_XSTATS] = { .type = NLA_NESTED }, - }; - struct nlattr *li[G_N_ELEMENTS (policy_link_info)]; - - if (nla_parse_nested_arr (li, tb[IFLA_LINKINFO], policy_link_info) < 0) - return NULL; - - if (li[IFLA_INFO_KIND]) - nl_info_kind = nla_get_string (li[IFLA_INFO_KIND]); - - nl_info_data = li[IFLA_INFO_DATA]; - } - - if (tb[IFLA_STATS64]) { - const char *stats = nla_data (tb[IFLA_STATS64]); - - obj->link.rx_packets = unaligned_read_ne64 (&stats[G_STRUCT_OFFSET (struct rtnl_link_stats64, rx_packets)]); - obj->link.rx_bytes = unaligned_read_ne64 (&stats[G_STRUCT_OFFSET (struct rtnl_link_stats64, rx_bytes)]); - obj->link.tx_packets = unaligned_read_ne64 (&stats[G_STRUCT_OFFSET (struct rtnl_link_stats64, tx_packets)]); - obj->link.tx_bytes = unaligned_read_ne64 (&stats[G_STRUCT_OFFSET (struct rtnl_link_stats64, tx_bytes)]); - } - - obj->link.n_ifi_flags = ifi->ifi_flags; - obj->link.connected = NM_FLAGS_HAS (obj->link.n_ifi_flags, IFF_LOWER_UP); - obj->link.arptype = ifi->ifi_type; - - obj->link.type = _linktype_get_type (platform, - cache, - nl_info_kind, - obj->link.ifindex, - obj->link.name, - obj->link.n_ifi_flags, - obj->link.arptype, - completed_from_cache, - &link_cached, - &obj->link.kind); - - if (tb[IFLA_MASTER]) - obj->link.master = nla_get_u32 (tb[IFLA_MASTER]); - - if (tb[IFLA_LINK]) { - if (!tb[IFLA_LINK_NETNSID]) - obj->link.parent = nla_get_u32 (tb[IFLA_LINK]); - else - obj->link.parent = NM_PLATFORM_LINK_OTHER_NETNS; - } - - if (tb[IFLA_ADDRESS]) { - _nmp_link_address_set (&obj->link.l_address, tb[IFLA_ADDRESS]); - address_complete_from_cache = FALSE; - } - - if (tb[IFLA_BROADCAST]) { - _nmp_link_address_set (&obj->link.l_broadcast, tb[IFLA_BROADCAST]); - broadcast_complete_from_cache = FALSE; - } - - if (tb[IFLA_AF_SPEC]) { - struct nlattr *af_attr; - int remaining; - - nla_for_each_nested (af_attr, tb[IFLA_AF_SPEC], remaining) { - switch (nla_type (af_attr)) { - case AF_INET6: - _parse_af_inet6 (platform, - af_attr, - &obj->link.inet6_token, - &af_inet6_token_valid, - &obj->link.inet6_addr_gen_mode_inv, - &af_inet6_addr_gen_mode_valid); - break; - } - } - } - - switch (obj->link.type) { - case NM_LINK_TYPE_GRE: - case NM_LINK_TYPE_GRETAP: - lnk_data = _parse_lnk_gre (nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_INFINIBAND: - lnk_data = _parse_lnk_infiniband (nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_IP6TNL: - lnk_data = _parse_lnk_ip6tnl (nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_IP6GRE: - case NM_LINK_TYPE_IP6GRETAP: - lnk_data = _parse_lnk_ip6gre (nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_IPIP: - lnk_data = _parse_lnk_ipip (nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_MACSEC: - lnk_data = _parse_lnk_macsec (nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_MACVLAN: - case NM_LINK_TYPE_MACVTAP: - lnk_data = _parse_lnk_macvlan (nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_SIT: - lnk_data = _parse_lnk_sit (nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_TUN: - lnk_data = _parse_lnk_tun (nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_VLAN: - lnk_data = _parse_lnk_vlan (nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_VRF: - lnk_data = _parse_lnk_vrf (nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_VXLAN: - lnk_data = _parse_lnk_vxlan (nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_WIFI: - case NM_LINK_TYPE_OLPC_MESH: - case NM_LINK_TYPE_WPAN: - need_ext_data = TRUE; - lnk_data_complete_from_cache = FALSE; - break; - case NM_LINK_TYPE_WIREGUARD: - lnk_data_complete_from_cache = TRUE; - break; - default: - lnk_data_complete_from_cache = FALSE; - break; - } - - if ( completed_from_cache - && ( lnk_data_complete_from_cache - || need_ext_data - || address_complete_from_cache - || broadcast_complete_from_cache - || !af_inet6_token_valid - || !af_inet6_addr_gen_mode_valid - || !tb[IFLA_STATS64])) { - _lookup_cached_link (cache, obj->link.ifindex, completed_from_cache, &link_cached); - if ( link_cached - && link_cached->_link.netlink.is_in_netlink) { - if ( lnk_data_complete_from_cache - && link_cached->link.type == obj->link.type - && link_cached->_link.netlink.lnk - && ( !lnk_data - || nmp_object_equal (lnk_data, link_cached->_link.netlink.lnk))) { - /* We always try to look into the cache and reuse the object there. - * We do that, because we consider the lnk object as immutable and don't - * modify it after creating. Hence we can share it and reuse. - * - * Also, sometimes the info-data is missing for updates. In this case - * we want to keep the previously received lnk_data. */ - nmp_object_unref (lnk_data); - lnk_data = nmp_object_ref (link_cached->_link.netlink.lnk); - } - - if ( need_ext_data - && link_cached->link.type == obj->link.type - && link_cached->_link.ext_data) { - /* Prefer reuse of existing ext_data object */ - obj->_link.ext_data = g_object_ref (link_cached->_link.ext_data); - } - - if (address_complete_from_cache) - obj->link.l_address = link_cached->link.l_address; - if (broadcast_complete_from_cache) - obj->link.l_broadcast = link_cached->link.l_broadcast; - if (!af_inet6_token_valid) - obj->link.inet6_token = link_cached->link.inet6_token; - if (!af_inet6_addr_gen_mode_valid) - obj->link.inet6_addr_gen_mode_inv = link_cached->link.inet6_addr_gen_mode_inv; - if (!tb[IFLA_STATS64]) { - obj->link.rx_packets = link_cached->link.rx_packets; - obj->link.rx_bytes = link_cached->link.rx_bytes; - obj->link.tx_packets = link_cached->link.tx_packets; - obj->link.tx_bytes = link_cached->link.tx_bytes; - } - } - } - - obj->_link.netlink.lnk = lnk_data; - - if ( need_ext_data - && obj->_link.ext_data == NULL) { - switch (obj->link.type) { - case NM_LINK_TYPE_WIFI: - case NM_LINK_TYPE_OLPC_MESH: - obj->_link.ext_data = (GObject *) nm_wifi_utils_new (ifi->ifi_index, - _genl_sock (NM_LINUX_PLATFORM (platform)), - TRUE); - break; - case NM_LINK_TYPE_WPAN: - obj->_link.ext_data = (GObject *) nm_wpan_utils_new (ifi->ifi_index, - _genl_sock (NM_LINUX_PLATFORM (platform)), - TRUE); - break; - default: - g_assert_not_reached (); - } - } - - if (obj->link.type == NM_LINK_TYPE_WIREGUARD) { - const NMPObject *lnk_data_new = NULL; - struct nl_sock *genl = NM_LINUX_PLATFORM_GET_PRIVATE (platform)->genl; - - /* The WireGuard kernel module does not yet send link update - * notifications, so we don't actually update the cache. For - * now, always refetch link data here. */ - - _lookup_cached_link (cache, obj->link.ifindex, completed_from_cache, &link_cached); - if ( link_cached - && link_cached->_link.netlink.is_in_netlink - && link_cached->link.type == NM_LINK_TYPE_WIREGUARD) - obj->_link.wireguard_family_id = link_cached->_link.wireguard_family_id; - else - obj->_link.wireguard_family_id = -1; - - if (obj->_link.wireguard_family_id < 0) - obj->_link.wireguard_family_id = genl_ctrl_resolve (genl, "wireguard"); - - if (obj->_link.wireguard_family_id >= 0) { - lnk_data_new = _wireguard_read_info (platform, - genl, - obj->_link.wireguard_family_id, - obj->link.ifindex); - } - - if ( lnk_data_new - && obj->_link.netlink.lnk - && nmp_object_equal (obj->_link.netlink.lnk, lnk_data_new)) - nmp_object_unref (lnk_data_new); - else { - nmp_object_unref (obj->_link.netlink.lnk); - obj->_link.netlink.lnk = lnk_data_new; - } - } - - obj->_link.netlink.is_in_netlink = TRUE; - return g_steal_pointer (&obj); +_new_from_nl_link(NMPlatform * platform, + const NMPCache * cache, + struct nlmsghdr *nlh, + gboolean id_only) +{ + static const struct nla_policy policy[] = { + [IFLA_IFNAME] = {.type = NLA_STRING, .maxlen = IFNAMSIZ}, + [IFLA_MTU] = {.type = NLA_U32}, + [IFLA_TXQLEN] = {.type = NLA_U32}, + [IFLA_LINK] = {.type = NLA_U32}, + [IFLA_WEIGHT] = {.type = NLA_U32}, + [IFLA_MASTER] = {.type = NLA_U32}, + [IFLA_OPERSTATE] = {.type = NLA_U8}, + [IFLA_LINKMODE] = {.type = NLA_U8}, + [IFLA_LINKINFO] = {.type = NLA_NESTED}, + [IFLA_QDISC] = {.type = NLA_STRING, .maxlen = IFQDISCSIZ}, + [IFLA_STATS] = {.minlen = nm_offsetofend(struct rtnl_link_stats, tx_compressed)}, + [IFLA_STATS64] = {.minlen = nm_offsetofend(struct rtnl_link_stats64, tx_compressed)}, + [IFLA_MAP] = {.minlen = nm_offsetofend(struct rtnl_link_ifmap, port)}, + [IFLA_IFALIAS] = {.type = NLA_STRING, .maxlen = IFALIASZ}, + [IFLA_NUM_VF] = {.type = NLA_U32}, + [IFLA_AF_SPEC] = {.type = NLA_NESTED}, + [IFLA_PROMISCUITY] = {.type = NLA_U32}, + [IFLA_NUM_TX_QUEUES] = {.type = NLA_U32}, + [IFLA_NUM_RX_QUEUES] = {.type = NLA_U32}, + [IFLA_GROUP] = {.type = NLA_U32}, + [IFLA_CARRIER] = {.type = NLA_U8}, + [IFLA_PHYS_PORT_ID] = {.type = NLA_UNSPEC}, + [IFLA_NET_NS_PID] = {.type = NLA_U32}, + [IFLA_NET_NS_FD] = {.type = NLA_U32}, + [IFLA_LINK_NETNSID] = {}, + }; + const struct ifinfomsg *ifi; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + struct nlattr * nl_info_data = NULL; + const char * nl_info_kind = NULL; + nm_auto_nmpobj NMPObject *obj = NULL; + gboolean completed_from_cache_val = FALSE; + gboolean * completed_from_cache = cache ? &completed_from_cache_val : NULL; + const NMPObject * link_cached = NULL; + const NMPObject * lnk_data = NULL; + gboolean address_complete_from_cache = TRUE; + gboolean broadcast_complete_from_cache = TRUE; + gboolean lnk_data_complete_from_cache = TRUE; + gboolean need_ext_data = FALSE; + gboolean af_inet6_token_valid = FALSE; + gboolean af_inet6_addr_gen_mode_valid = FALSE; + + if (!nlmsg_valid_hdr(nlh, sizeof(*ifi))) + return NULL; + + ifi = nlmsg_data(nlh); + + if (ifi->ifi_family != AF_UNSPEC) + return NULL; + if (ifi->ifi_index <= 0) + return NULL; + + obj = nmp_object_new_link(ifi->ifi_index); + + if (id_only) + return g_steal_pointer(&obj); + + if (nlmsg_parse_arr(nlh, sizeof(*ifi), tb, policy) < 0) + return NULL; + + if (!tb[IFLA_IFNAME]) + return NULL; + nla_strlcpy(obj->link.name, tb[IFLA_IFNAME], IFNAMSIZ); + if (!obj->link.name[0]) + return NULL; + + if (!tb[IFLA_MTU]) { + /* Kernel has two places that send RTM_GETLINK messages: + * net/core/rtnetlink.c and net/wireless/ext-core.c. + * Unfortunately ext-core.c sets only IFLA_WIRELESS and + * IFLA_IFNAME. This confuses code in this function, because + * it cannot get complete set of data for the interface and + * later incomplete object this function creates is used to + * overwrite existing data in NM's cache. + * Since ext-core.c doesn't set IFLA_MTU we can use it as a + * signal to ignore incoming message. + * To some extent this is a hack and correct approach is to + * merge objects per-field. + */ + return NULL; + } + obj->link.mtu = nla_get_u32(tb[IFLA_MTU]); + + if (tb[IFLA_LINKINFO]) { + static const struct nla_policy policy_link_info[] = { + [IFLA_INFO_KIND] = {.type = NLA_STRING}, + [IFLA_INFO_DATA] = {.type = NLA_NESTED}, + [IFLA_INFO_XSTATS] = {.type = NLA_NESTED}, + }; + struct nlattr *li[G_N_ELEMENTS(policy_link_info)]; + + if (nla_parse_nested_arr(li, tb[IFLA_LINKINFO], policy_link_info) < 0) + return NULL; + + if (li[IFLA_INFO_KIND]) + nl_info_kind = nla_get_string(li[IFLA_INFO_KIND]); + + nl_info_data = li[IFLA_INFO_DATA]; + } + + if (tb[IFLA_STATS64]) { + const char *stats = nla_data(tb[IFLA_STATS64]); + + obj->link.rx_packets = + unaligned_read_ne64(&stats[G_STRUCT_OFFSET(struct rtnl_link_stats64, rx_packets)]); + obj->link.rx_bytes = + unaligned_read_ne64(&stats[G_STRUCT_OFFSET(struct rtnl_link_stats64, rx_bytes)]); + obj->link.tx_packets = + unaligned_read_ne64(&stats[G_STRUCT_OFFSET(struct rtnl_link_stats64, tx_packets)]); + obj->link.tx_bytes = + unaligned_read_ne64(&stats[G_STRUCT_OFFSET(struct rtnl_link_stats64, tx_bytes)]); + } + + obj->link.n_ifi_flags = ifi->ifi_flags; + obj->link.connected = NM_FLAGS_HAS(obj->link.n_ifi_flags, IFF_LOWER_UP); + obj->link.arptype = ifi->ifi_type; + + obj->link.type = _linktype_get_type(platform, + cache, + nl_info_kind, + obj->link.ifindex, + obj->link.name, + obj->link.n_ifi_flags, + obj->link.arptype, + completed_from_cache, + &link_cached, + &obj->link.kind); + + if (tb[IFLA_MASTER]) + obj->link.master = nla_get_u32(tb[IFLA_MASTER]); + + if (tb[IFLA_LINK]) { + if (!tb[IFLA_LINK_NETNSID]) + obj->link.parent = nla_get_u32(tb[IFLA_LINK]); + else + obj->link.parent = NM_PLATFORM_LINK_OTHER_NETNS; + } + + if (tb[IFLA_ADDRESS]) { + _nmp_link_address_set(&obj->link.l_address, tb[IFLA_ADDRESS]); + address_complete_from_cache = FALSE; + } + + if (tb[IFLA_BROADCAST]) { + _nmp_link_address_set(&obj->link.l_broadcast, tb[IFLA_BROADCAST]); + broadcast_complete_from_cache = FALSE; + } + + if (tb[IFLA_AF_SPEC]) { + struct nlattr *af_attr; + int remaining; + + nla_for_each_nested (af_attr, tb[IFLA_AF_SPEC], remaining) { + switch (nla_type(af_attr)) { + case AF_INET6: + _parse_af_inet6(platform, + af_attr, + &obj->link.inet6_token, + &af_inet6_token_valid, + &obj->link.inet6_addr_gen_mode_inv, + &af_inet6_addr_gen_mode_valid); + break; + } + } + } + + switch (obj->link.type) { + case NM_LINK_TYPE_BRIDGE: + lnk_data = _parse_lnk_bridge(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_GRE: + case NM_LINK_TYPE_GRETAP: + lnk_data = _parse_lnk_gre(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_INFINIBAND: + lnk_data = _parse_lnk_infiniband(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_IP6TNL: + lnk_data = _parse_lnk_ip6tnl(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_IP6GRE: + case NM_LINK_TYPE_IP6GRETAP: + lnk_data = _parse_lnk_ip6gre(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_IPIP: + lnk_data = _parse_lnk_ipip(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_MACSEC: + lnk_data = _parse_lnk_macsec(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_MACVLAN: + case NM_LINK_TYPE_MACVTAP: + lnk_data = _parse_lnk_macvlan(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_SIT: + lnk_data = _parse_lnk_sit(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_TUN: + lnk_data = _parse_lnk_tun(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_VLAN: + lnk_data = _parse_lnk_vlan(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_VRF: + lnk_data = _parse_lnk_vrf(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_VXLAN: + lnk_data = _parse_lnk_vxlan(nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_WIFI: + case NM_LINK_TYPE_OLPC_MESH: + case NM_LINK_TYPE_WPAN: + need_ext_data = TRUE; + lnk_data_complete_from_cache = FALSE; + break; + case NM_LINK_TYPE_WIREGUARD: + lnk_data_complete_from_cache = TRUE; + break; + default: + lnk_data_complete_from_cache = FALSE; + break; + } + + if (completed_from_cache + && (lnk_data_complete_from_cache || need_ext_data || address_complete_from_cache + || broadcast_complete_from_cache || !af_inet6_token_valid + || !af_inet6_addr_gen_mode_valid || !tb[IFLA_STATS64])) { + _lookup_cached_link(cache, obj->link.ifindex, completed_from_cache, &link_cached); + if (link_cached && link_cached->_link.netlink.is_in_netlink) { + if (lnk_data_complete_from_cache && link_cached->link.type == obj->link.type + && link_cached->_link.netlink.lnk + && (!lnk_data || nmp_object_equal(lnk_data, link_cached->_link.netlink.lnk))) { + /* We always try to look into the cache and reuse the object there. + * We do that, because we consider the lnk object as immutable and don't + * modify it after creating. Hence we can share it and reuse. + * + * Also, sometimes the info-data is missing for updates. In this case + * we want to keep the previously received lnk_data. */ + nmp_object_unref(lnk_data); + lnk_data = nmp_object_ref(link_cached->_link.netlink.lnk); + } + + if (need_ext_data && link_cached->link.type == obj->link.type + && link_cached->_link.ext_data) { + /* Prefer reuse of existing ext_data object */ + obj->_link.ext_data = g_object_ref(link_cached->_link.ext_data); + } + + if (address_complete_from_cache) + obj->link.l_address = link_cached->link.l_address; + if (broadcast_complete_from_cache) + obj->link.l_broadcast = link_cached->link.l_broadcast; + if (!af_inet6_token_valid) + obj->link.inet6_token = link_cached->link.inet6_token; + if (!af_inet6_addr_gen_mode_valid) + obj->link.inet6_addr_gen_mode_inv = link_cached->link.inet6_addr_gen_mode_inv; + if (!tb[IFLA_STATS64]) { + obj->link.rx_packets = link_cached->link.rx_packets; + obj->link.rx_bytes = link_cached->link.rx_bytes; + obj->link.tx_packets = link_cached->link.tx_packets; + obj->link.tx_bytes = link_cached->link.tx_bytes; + } + } + } + + obj->_link.netlink.lnk = lnk_data; + + if (need_ext_data && obj->_link.ext_data == NULL) { + switch (obj->link.type) { + case NM_LINK_TYPE_WIFI: + case NM_LINK_TYPE_OLPC_MESH: + obj->_link.ext_data = + (GObject *) nm_wifi_utils_new(ifi->ifi_index, + _genl_sock(NM_LINUX_PLATFORM(platform)), + TRUE); + break; + case NM_LINK_TYPE_WPAN: + obj->_link.ext_data = + (GObject *) nm_wpan_utils_new(ifi->ifi_index, + _genl_sock(NM_LINUX_PLATFORM(platform)), + TRUE); + break; + default: + g_assert_not_reached(); + } + } + + if (obj->link.type == NM_LINK_TYPE_WIREGUARD) { + const NMPObject *lnk_data_new = NULL; + struct nl_sock * genl = NM_LINUX_PLATFORM_GET_PRIVATE(platform)->genl; + + /* The WireGuard kernel module does not yet send link update + * notifications, so we don't actually update the cache. For + * now, always refetch link data here. */ + + _lookup_cached_link(cache, obj->link.ifindex, completed_from_cache, &link_cached); + if (link_cached && link_cached->_link.netlink.is_in_netlink + && link_cached->link.type == NM_LINK_TYPE_WIREGUARD) + obj->_link.wireguard_family_id = link_cached->_link.wireguard_family_id; + else + obj->_link.wireguard_family_id = -1; + + if (obj->_link.wireguard_family_id < 0) + obj->_link.wireguard_family_id = genl_ctrl_resolve(genl, "wireguard"); + + if (obj->_link.wireguard_family_id >= 0) { + lnk_data_new = _wireguard_read_info(platform, + genl, + obj->_link.wireguard_family_id, + obj->link.ifindex); + } + + if (lnk_data_new && obj->_link.netlink.lnk + && nmp_object_equal(obj->_link.netlink.lnk, lnk_data_new)) + nmp_object_unref(lnk_data_new); + else { + nmp_object_unref(obj->_link.netlink.lnk); + obj->_link.netlink.lnk = lnk_data_new; + } + } + + obj->_link.netlink.is_in_netlink = TRUE; + return g_steal_pointer(&obj); } /* Copied and heavily modified from libnl3's addr_msg_parser(). */ static NMPObject * -_new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) -{ - static const struct nla_policy policy[] = { - [IFA_LABEL] = { .type = NLA_STRING, - .maxlen = IFNAMSIZ }, - [IFA_CACHEINFO] = { .minlen = nm_offsetofend (struct ifa_cacheinfo, tstamp) }, - [IFA_FLAGS] = { }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - const struct ifaddrmsg *ifa; - gboolean is_v4; - nm_auto_nmpobj NMPObject *obj = NULL; - int addr_len; - guint32 lifetime, preferred, timestamp; - - if (!nlmsg_valid_hdr (nlh, sizeof (*ifa))) - return NULL; - - ifa = nlmsg_data (nlh); - - if (!NM_IN_SET (ifa->ifa_family, AF_INET, AF_INET6)) - return NULL; - - is_v4 = ifa->ifa_family == AF_INET; - - if (nlmsg_parse_arr (nlh, sizeof (*ifa), tb, policy) < 0) - return NULL; - - addr_len = is_v4 - ? sizeof (in_addr_t) - : sizeof (struct in6_addr); - - if (ifa->ifa_prefixlen > (is_v4 ? 32 : 128)) - return NULL; - - /*****************************************************************/ - - obj = nmp_object_new (is_v4 ? NMP_OBJECT_TYPE_IP4_ADDRESS : NMP_OBJECT_TYPE_IP6_ADDRESS, NULL); - - obj->ip_address.ifindex = ifa->ifa_index; - obj->ip_address.plen = ifa->ifa_prefixlen; - - _check_addr_or_return_null (tb, IFA_ADDRESS, addr_len); - _check_addr_or_return_null (tb, IFA_LOCAL, addr_len); - if (is_v4) { - /* For IPv4, kernel omits IFA_LOCAL/IFA_ADDRESS if (and only if) they - * are effectively 0.0.0.0 (all-zero). */ - if (tb[IFA_LOCAL]) - memcpy (&obj->ip4_address.address, nla_data (tb[IFA_LOCAL]), addr_len); - if (tb[IFA_ADDRESS]) - memcpy (&obj->ip4_address.peer_address, nla_data (tb[IFA_ADDRESS]), addr_len); - - _check_addr_or_return_null (tb, IFA_BROADCAST, addr_len); - obj->ip4_address.broadcast_address = tb[IFA_BROADCAST] - ? nla_get_u32 (tb[IFA_BROADCAST]) - : 0u; - obj->ip4_address.use_ip4_broadcast_address = TRUE; - } else { - /* For IPv6, IFA_ADDRESS is always present. - * - * If IFA_LOCAL is missing, IFA_ADDRESS is @address and @peer_address - * is :: (all-zero). - * - * If unexpectely IFA_ADDRESS is missing, make the best of it -- but it _should_ - * actually be there. */ - if (tb[IFA_ADDRESS] || tb[IFA_LOCAL]) { - if (tb[IFA_LOCAL]) { - memcpy (&obj->ip6_address.address, nla_data (tb[IFA_LOCAL]), addr_len); - if (tb[IFA_ADDRESS]) - memcpy (&obj->ip6_address.peer_address, nla_data (tb[IFA_ADDRESS]), addr_len); - else - obj->ip6_address.peer_address = obj->ip6_address.address; - } else - memcpy (&obj->ip6_address.address, nla_data (tb[IFA_ADDRESS]), addr_len); - } - } - - obj->ip_address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; - - obj->ip_address.n_ifa_flags = tb[IFA_FLAGS] - ? nla_get_u32 (tb[IFA_FLAGS]) - : ifa->ifa_flags; - - if (is_v4) { - if (tb[IFA_LABEL]) { - char label[IFNAMSIZ]; - - nla_strlcpy (label, tb[IFA_LABEL], IFNAMSIZ); - - /* Check for ':'; we're only interested in labels used as interface aliases */ - if (strchr (label, ':')) - g_strlcpy (obj->ip4_address.label, label, sizeof (obj->ip4_address.label)); - } - } - - lifetime = NM_PLATFORM_LIFETIME_PERMANENT; - preferred = NM_PLATFORM_LIFETIME_PERMANENT; - timestamp = 0; - /* IPv6 only */ - if (tb[IFA_CACHEINFO]) { - const struct ifa_cacheinfo *ca; - - ca = nla_data_as (struct ifa_cacheinfo, tb[IFA_CACHEINFO]); - lifetime = ca->ifa_valid; - preferred = ca->ifa_prefered; - timestamp = ca->tstamp; - } - _addrtime_get_lifetimes (timestamp, - lifetime, - preferred, - &obj->ip_address.timestamp, - &obj->ip_address.lifetime, - &obj->ip_address.preferred); - - return g_steal_pointer (&obj); +_new_from_nl_addr(struct nlmsghdr *nlh, gboolean id_only) +{ + static const struct nla_policy policy[] = { + [IFA_LABEL] = {.type = NLA_STRING, .maxlen = IFNAMSIZ}, + [IFA_CACHEINFO] = {.minlen = nm_offsetofend(struct ifa_cacheinfo, tstamp)}, + [IFA_FLAGS] = {}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + const struct ifaddrmsg *ifa; + gboolean is_v4; + nm_auto_nmpobj NMPObject *obj = NULL; + int addr_len; + guint32 lifetime, preferred, timestamp; + + if (!nlmsg_valid_hdr(nlh, sizeof(*ifa))) + return NULL; + + ifa = nlmsg_data(nlh); + + if (!NM_IN_SET(ifa->ifa_family, AF_INET, AF_INET6)) + return NULL; + + is_v4 = ifa->ifa_family == AF_INET; + + if (nlmsg_parse_arr(nlh, sizeof(*ifa), tb, policy) < 0) + return NULL; + + addr_len = is_v4 ? sizeof(in_addr_t) : sizeof(struct in6_addr); + + if (ifa->ifa_prefixlen > (is_v4 ? 32 : 128)) + return NULL; + + /*****************************************************************/ + + obj = nmp_object_new(is_v4 ? NMP_OBJECT_TYPE_IP4_ADDRESS : NMP_OBJECT_TYPE_IP6_ADDRESS, NULL); + + obj->ip_address.ifindex = ifa->ifa_index; + obj->ip_address.plen = ifa->ifa_prefixlen; + + _check_addr_or_return_null(tb, IFA_ADDRESS, addr_len); + _check_addr_or_return_null(tb, IFA_LOCAL, addr_len); + if (is_v4) { + /* For IPv4, kernel omits IFA_LOCAL/IFA_ADDRESS if (and only if) they + * are effectively 0.0.0.0 (all-zero). */ + if (tb[IFA_LOCAL]) + memcpy(&obj->ip4_address.address, nla_data(tb[IFA_LOCAL]), addr_len); + if (tb[IFA_ADDRESS]) + memcpy(&obj->ip4_address.peer_address, nla_data(tb[IFA_ADDRESS]), addr_len); + + _check_addr_or_return_null(tb, IFA_BROADCAST, addr_len); + obj->ip4_address.broadcast_address = + tb[IFA_BROADCAST] ? nla_get_u32(tb[IFA_BROADCAST]) : 0u; + obj->ip4_address.use_ip4_broadcast_address = TRUE; + } else { + /* For IPv6, IFA_ADDRESS is always present. + * + * If IFA_LOCAL is missing, IFA_ADDRESS is @address and @peer_address + * is :: (all-zero). + * + * If unexpectedly IFA_ADDRESS is missing, make the best of it -- but it _should_ + * actually be there. */ + if (tb[IFA_ADDRESS] || tb[IFA_LOCAL]) { + if (tb[IFA_LOCAL]) { + memcpy(&obj->ip6_address.address, nla_data(tb[IFA_LOCAL]), addr_len); + if (tb[IFA_ADDRESS]) + memcpy(&obj->ip6_address.peer_address, nla_data(tb[IFA_ADDRESS]), addr_len); + else + obj->ip6_address.peer_address = obj->ip6_address.address; + } else + memcpy(&obj->ip6_address.address, nla_data(tb[IFA_ADDRESS]), addr_len); + } + } + + obj->ip_address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; + + obj->ip_address.n_ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifa->ifa_flags; + + if (is_v4) { + if (tb[IFA_LABEL]) { + char label[IFNAMSIZ]; + + nla_strlcpy(label, tb[IFA_LABEL], IFNAMSIZ); + + /* Check for ':'; we're only interested in labels used as interface aliases */ + if (strchr(label, ':')) + g_strlcpy(obj->ip4_address.label, label, sizeof(obj->ip4_address.label)); + } + } + + lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + preferred = NM_PLATFORM_LIFETIME_PERMANENT; + timestamp = 0; + /* IPv6 only */ + if (tb[IFA_CACHEINFO]) { + const struct ifa_cacheinfo *ca; + + ca = nla_data_as(struct ifa_cacheinfo, tb[IFA_CACHEINFO]); + lifetime = ca->ifa_valid; + preferred = ca->ifa_prefered; + timestamp = ca->tstamp; + } + _addrtime_get_lifetimes(timestamp, + lifetime, + preferred, + &obj->ip_address.timestamp, + &obj->ip_address.lifetime, + &obj->ip_address.preferred); + + return g_steal_pointer(&obj); } /* Copied and heavily modified from libnl3's rtnl_route_parse() and parse_multipath(). */ static NMPObject * -_new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) -{ - static const struct nla_policy policy[] = { - [RTA_TABLE] = { .type = NLA_U32 }, - [RTA_IIF] = { .type = NLA_U32 }, - [RTA_OIF] = { .type = NLA_U32 }, - [RTA_PRIORITY] = { .type = NLA_U32 }, - [RTA_PREF] = { .type = NLA_U8 }, - [RTA_FLOW] = { .type = NLA_U32 }, - [RTA_CACHEINFO] = { .minlen = nm_offsetofend (struct rta_cacheinfo, rta_tsage) }, - [RTA_METRICS] = { .type = NLA_NESTED }, - [RTA_MULTIPATH] = { .type = NLA_NESTED }, - }; - const struct rtmsg *rtm; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - gboolean is_v4; - nm_auto_nmpobj NMPObject *obj = NULL; - int addr_len; - struct { - gboolean is_present; - int ifindex; - NMIPAddr gateway; - } nh = { - .is_present = FALSE, - }; - guint32 mss; - guint32 window = 0; - guint32 cwnd = 0; - guint32 initcwnd = 0; - guint32 initrwnd = 0; - guint32 mtu = 0; - guint32 lock = 0; - - if (!nlmsg_valid_hdr (nlh, sizeof (*rtm))) - return NULL; - - rtm = nlmsg_data (nlh); - - /***************************************************************** - * only handle ~supported~ routes. - *****************************************************************/ - - if (!NM_IN_SET (rtm->rtm_family, AF_INET, AF_INET6)) - return NULL; - - if (!NM_IN_SET (rtm->rtm_type, - RTN_UNICAST, - RTN_LOCAL)) - return NULL; - - if (nlmsg_parse_arr (nlh, - sizeof (struct rtmsg), - tb, - policy) < 0) - return NULL; - - /*****************************************************************/ - - is_v4 = rtm->rtm_family == AF_INET; - addr_len = is_v4 - ? sizeof (in_addr_t) - : sizeof (struct in6_addr); - - if (rtm->rtm_dst_len > (is_v4 ? 32 : 128)) - return NULL; - - /***************************************************************** - * parse nexthops. Only handle routes with one nh. - *****************************************************************/ - - if (tb[RTA_MULTIPATH]) { - size_t tlen = nla_len (tb[RTA_MULTIPATH]); - struct rtnexthop *rtnh; - - if (tlen < sizeof (*rtnh)) - goto rta_multipath_done; - - rtnh = nla_data_as (struct rtnexthop, tb[RTA_MULTIPATH]); - - if (tlen < rtnh->rtnh_len) - goto rta_multipath_done; - - while (TRUE) { - - if (nh.is_present) { - /* we don't support multipath routes. */ - return NULL; - } - - nh.is_present = TRUE; - nh.ifindex = rtnh->rtnh_ifindex; - - if (rtnh->rtnh_len > sizeof (*rtnh)) { - struct nlattr *ntb[G_N_ELEMENTS (policy)]; - - if (nla_parse_arr (ntb, - (struct nlattr *) RTNH_DATA (rtnh), - rtnh->rtnh_len - sizeof (*rtnh), - policy) < 0) - return NULL; - - if (_check_addr_or_return_null (ntb, RTA_GATEWAY, addr_len)) - memcpy (&nh.gateway, nla_data (ntb[RTA_GATEWAY]), addr_len); - } - - if (tlen < RTNH_ALIGN (rtnh->rtnh_len) + sizeof (*rtnh)) - goto rta_multipath_done; - - tlen -= RTNH_ALIGN (rtnh->rtnh_len); - rtnh = RTNH_NEXT (rtnh); - } -rta_multipath_done: - ; - } - - if ( tb[RTA_OIF] - || tb[RTA_GATEWAY] - || tb[RTA_FLOW]) { - int ifindex = 0; - NMIPAddr gateway = { }; - - if (tb[RTA_OIF]) - ifindex = nla_get_u32 (tb[RTA_OIF]); - if (_check_addr_or_return_null (tb, RTA_GATEWAY, addr_len)) - memcpy (&gateway, nla_data (tb[RTA_GATEWAY]), addr_len); - - if (!nh.is_present) { - /* If no nexthops have been provided via RTA_MULTIPATH - * we add it as regular nexthop to maintain backwards - * compatibility */ - nh.ifindex = ifindex; - nh.gateway = gateway; - } else { - /* Kernel supports new style nexthop configuration, - * verify that it is a duplicate and ignore old-style nexthop. */ - if ( nh.ifindex != ifindex - || memcmp (&nh.gateway, &gateway, addr_len) != 0) - return NULL; - } - } else if (!nh.is_present) - return NULL; - - /*****************************************************************/ - - mss = 0; - if (tb[RTA_METRICS]) { - static const struct nla_policy rtax_policy[] = { - [RTAX_LOCK] = { .type = NLA_U32 }, - [RTAX_ADVMSS] = { .type = NLA_U32 }, - [RTAX_WINDOW] = { .type = NLA_U32 }, - [RTAX_CWND] = { .type = NLA_U32 }, - [RTAX_INITCWND] = { .type = NLA_U32 }, - [RTAX_INITRWND] = { .type = NLA_U32 }, - [RTAX_MTU] = { .type = NLA_U32 }, - }; - struct nlattr *mtb[G_N_ELEMENTS (rtax_policy)]; - - if (nla_parse_nested_arr (mtb, tb[RTA_METRICS], rtax_policy) < 0) - return NULL; - - if (mtb[RTAX_LOCK]) - lock = nla_get_u32 (mtb[RTAX_LOCK]); - if (mtb[RTAX_ADVMSS]) - mss = nla_get_u32 (mtb[RTAX_ADVMSS]); - if (mtb[RTAX_WINDOW]) - window = nla_get_u32 (mtb[RTAX_WINDOW]); - if (mtb[RTAX_CWND]) - cwnd = nla_get_u32 (mtb[RTAX_CWND]); - if (mtb[RTAX_INITCWND]) - initcwnd = nla_get_u32 (mtb[RTAX_INITCWND]); - if (mtb[RTAX_INITRWND]) - initrwnd = nla_get_u32 (mtb[RTAX_INITRWND]); - if (mtb[RTAX_MTU]) - mtu = nla_get_u32 (mtb[RTAX_MTU]); - } - - /*****************************************************************/ - - obj = nmp_object_new (is_v4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, NULL); - - obj->ip_route.type_coerced = nm_platform_route_type_coerce (rtm->rtm_type); - obj->ip_route.table_coerced = nm_platform_route_table_coerce ( tb[RTA_TABLE] - ? nla_get_u32 (tb[RTA_TABLE]) - : (guint32) rtm->rtm_table); - - obj->ip_route.ifindex = nh.ifindex; - - if (_check_addr_or_return_null (tb, RTA_DST, addr_len)) - memcpy (obj->ip_route.network_ptr, nla_data (tb[RTA_DST]), addr_len); - - obj->ip_route.plen = rtm->rtm_dst_len; - - if (tb[RTA_PRIORITY]) - obj->ip_route.metric = nla_get_u32 (tb[RTA_PRIORITY]); - - if (is_v4) - obj->ip4_route.gateway = nh.gateway.addr4; - else - obj->ip6_route.gateway = nh.gateway.addr6; - - if (is_v4) - obj->ip4_route.scope_inv = nm_platform_route_scope_inv (rtm->rtm_scope); - - if (_check_addr_or_return_null (tb, RTA_PREFSRC, addr_len)) { - if (is_v4) - memcpy (&obj->ip4_route.pref_src, nla_data (tb[RTA_PREFSRC]), addr_len); - else - memcpy (&obj->ip6_route.pref_src, nla_data (tb[RTA_PREFSRC]), addr_len); - } - - if (is_v4) - obj->ip4_route.tos = rtm->rtm_tos; - else { - if (tb[RTA_SRC]) { - _check_addr_or_return_null (tb, RTA_SRC, addr_len); - memcpy (&obj->ip6_route.src, nla_data (tb[RTA_SRC]), addr_len); - } - obj->ip6_route.src_plen = rtm->rtm_src_len; - } - - obj->ip_route.mss = mss; - obj->ip_route.window = window; - obj->ip_route.cwnd = cwnd; - obj->ip_route.initcwnd = initcwnd; - obj->ip_route.initrwnd = initrwnd; - obj->ip_route.mtu = mtu; - obj->ip_route.lock_window = NM_FLAGS_HAS (lock, 1 << RTAX_WINDOW); - obj->ip_route.lock_cwnd = NM_FLAGS_HAS (lock, 1 << RTAX_CWND); - obj->ip_route.lock_initcwnd = NM_FLAGS_HAS (lock, 1 << RTAX_INITCWND); - obj->ip_route.lock_initrwnd = NM_FLAGS_HAS (lock, 1 << RTAX_INITRWND); - obj->ip_route.lock_mtu = NM_FLAGS_HAS (lock, 1 << RTAX_MTU); - - if (!is_v4) { - - if (!_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF)) { - /* Detect support for RTA_PREF by inspecting the netlink message. - * RTA_PREF was added in kernel 4.1, dated 21 June, 2015. */ - _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF, - tb[RTA_PREF] ? 1 : -1); - } - - if (tb[RTA_PREF]) - obj->ip6_route.rt_pref = nla_get_u8 (tb[RTA_PREF]); - } - - obj->ip_route.r_rtm_flags = rtm->rtm_flags; - obj->ip_route.rt_source = nmp_utils_ip_config_source_from_rtprot (rtm->rtm_protocol); - - return g_steal_pointer (&obj); +_new_from_nl_route(struct nlmsghdr *nlh, gboolean id_only) +{ + static const struct nla_policy policy[] = { + [RTA_TABLE] = {.type = NLA_U32}, + [RTA_IIF] = {.type = NLA_U32}, + [RTA_OIF] = {.type = NLA_U32}, + [RTA_PRIORITY] = {.type = NLA_U32}, + [RTA_PREF] = {.type = NLA_U8}, + [RTA_FLOW] = {.type = NLA_U32}, + [RTA_CACHEINFO] = {.minlen = nm_offsetofend(struct rta_cacheinfo, rta_tsage)}, + [RTA_METRICS] = {.type = NLA_NESTED}, + [RTA_MULTIPATH] = {.type = NLA_NESTED}, + }; + const struct rtmsg *rtm; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + gboolean is_v4; + nm_auto_nmpobj NMPObject *obj = NULL; + int addr_len; + struct { + gboolean is_present; + int ifindex; + NMIPAddr gateway; + } nh = { + .is_present = FALSE, + }; + guint32 mss; + guint32 window = 0; + guint32 cwnd = 0; + guint32 initcwnd = 0; + guint32 initrwnd = 0; + guint32 mtu = 0; + guint32 lock = 0; + + if (!nlmsg_valid_hdr(nlh, sizeof(*rtm))) + return NULL; + + rtm = nlmsg_data(nlh); + + /***************************************************************** + * only handle ~supported~ routes. + *****************************************************************/ + + if (!NM_IN_SET(rtm->rtm_family, AF_INET, AF_INET6)) + return NULL; + + if (!NM_IN_SET(rtm->rtm_type, RTN_UNICAST, RTN_LOCAL)) + return NULL; + + if (nlmsg_parse_arr(nlh, sizeof(struct rtmsg), tb, policy) < 0) + return NULL; + + /*****************************************************************/ + + is_v4 = rtm->rtm_family == AF_INET; + addr_len = is_v4 ? sizeof(in_addr_t) : sizeof(struct in6_addr); + + if (rtm->rtm_dst_len > (is_v4 ? 32 : 128)) + return NULL; + + /***************************************************************** + * parse nexthops. Only handle routes with one nh. + *****************************************************************/ + + if (tb[RTA_MULTIPATH]) { + size_t tlen = nla_len(tb[RTA_MULTIPATH]); + struct rtnexthop *rtnh; + + if (tlen < sizeof(*rtnh)) + goto rta_multipath_done; + + rtnh = nla_data_as(struct rtnexthop, tb[RTA_MULTIPATH]); + + if (tlen < rtnh->rtnh_len) + goto rta_multipath_done; + + while (TRUE) { + if (nh.is_present) { + /* we don't support multipath routes. */ + return NULL; + } + + nh.is_present = TRUE; + nh.ifindex = rtnh->rtnh_ifindex; + + if (rtnh->rtnh_len > sizeof(*rtnh)) { + struct nlattr *ntb[G_N_ELEMENTS(policy)]; + + if (nla_parse_arr(ntb, + (struct nlattr *) RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof(*rtnh), + policy) + < 0) + return NULL; + + if (_check_addr_or_return_null(ntb, RTA_GATEWAY, addr_len)) + memcpy(&nh.gateway, nla_data(ntb[RTA_GATEWAY]), addr_len); + } + + if (tlen < RTNH_ALIGN(rtnh->rtnh_len) + sizeof(*rtnh)) + goto rta_multipath_done; + + tlen -= RTNH_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } +rta_multipath_done:; + } + + if (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW]) { + int ifindex = 0; + NMIPAddr gateway = {}; + + if (tb[RTA_OIF]) + ifindex = nla_get_u32(tb[RTA_OIF]); + if (_check_addr_or_return_null(tb, RTA_GATEWAY, addr_len)) + memcpy(&gateway, nla_data(tb[RTA_GATEWAY]), addr_len); + + if (!nh.is_present) { + /* If no nexthops have been provided via RTA_MULTIPATH + * we add it as regular nexthop to maintain backwards + * compatibility */ + nh.ifindex = ifindex; + nh.gateway = gateway; + } else { + /* Kernel supports new style nexthop configuration, + * verify that it is a duplicate and ignore old-style nexthop. */ + if (nh.ifindex != ifindex || memcmp(&nh.gateway, &gateway, addr_len) != 0) + return NULL; + } + } else if (!nh.is_present) + return NULL; + + /*****************************************************************/ + + mss = 0; + if (tb[RTA_METRICS]) { + static const struct nla_policy rtax_policy[] = { + [RTAX_LOCK] = {.type = NLA_U32}, + [RTAX_ADVMSS] = {.type = NLA_U32}, + [RTAX_WINDOW] = {.type = NLA_U32}, + [RTAX_CWND] = {.type = NLA_U32}, + [RTAX_INITCWND] = {.type = NLA_U32}, + [RTAX_INITRWND] = {.type = NLA_U32}, + [RTAX_MTU] = {.type = NLA_U32}, + }; + struct nlattr *mtb[G_N_ELEMENTS(rtax_policy)]; + + if (nla_parse_nested_arr(mtb, tb[RTA_METRICS], rtax_policy) < 0) + return NULL; + + if (mtb[RTAX_LOCK]) + lock = nla_get_u32(mtb[RTAX_LOCK]); + if (mtb[RTAX_ADVMSS]) + mss = nla_get_u32(mtb[RTAX_ADVMSS]); + if (mtb[RTAX_WINDOW]) + window = nla_get_u32(mtb[RTAX_WINDOW]); + if (mtb[RTAX_CWND]) + cwnd = nla_get_u32(mtb[RTAX_CWND]); + if (mtb[RTAX_INITCWND]) + initcwnd = nla_get_u32(mtb[RTAX_INITCWND]); + if (mtb[RTAX_INITRWND]) + initrwnd = nla_get_u32(mtb[RTAX_INITRWND]); + if (mtb[RTAX_MTU]) + mtu = nla_get_u32(mtb[RTAX_MTU]); + } + + /*****************************************************************/ + + obj = nmp_object_new(is_v4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + + obj->ip_route.type_coerced = nm_platform_route_type_coerce(rtm->rtm_type); + obj->ip_route.table_coerced = nm_platform_route_table_coerce( + tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : (guint32) rtm->rtm_table); + + obj->ip_route.ifindex = nh.ifindex; + + if (_check_addr_or_return_null(tb, RTA_DST, addr_len)) + memcpy(obj->ip_route.network_ptr, nla_data(tb[RTA_DST]), addr_len); + + obj->ip_route.plen = rtm->rtm_dst_len; + + if (tb[RTA_PRIORITY]) + obj->ip_route.metric = nla_get_u32(tb[RTA_PRIORITY]); + + if (is_v4) + obj->ip4_route.gateway = nh.gateway.addr4; + else + obj->ip6_route.gateway = nh.gateway.addr6; + + if (is_v4) + obj->ip4_route.scope_inv = nm_platform_route_scope_inv(rtm->rtm_scope); + + if (_check_addr_or_return_null(tb, RTA_PREFSRC, addr_len)) { + if (is_v4) + memcpy(&obj->ip4_route.pref_src, nla_data(tb[RTA_PREFSRC]), addr_len); + else + memcpy(&obj->ip6_route.pref_src, nla_data(tb[RTA_PREFSRC]), addr_len); + } + + if (is_v4) + obj->ip4_route.tos = rtm->rtm_tos; + else { + if (tb[RTA_SRC]) { + _check_addr_or_return_null(tb, RTA_SRC, addr_len); + memcpy(&obj->ip6_route.src, nla_data(tb[RTA_SRC]), addr_len); + } + obj->ip6_route.src_plen = rtm->rtm_src_len; + } + + obj->ip_route.mss = mss; + obj->ip_route.window = window; + obj->ip_route.cwnd = cwnd; + obj->ip_route.initcwnd = initcwnd; + obj->ip_route.initrwnd = initrwnd; + obj->ip_route.mtu = mtu; + obj->ip_route.lock_window = NM_FLAGS_HAS(lock, 1 << RTAX_WINDOW); + obj->ip_route.lock_cwnd = NM_FLAGS_HAS(lock, 1 << RTAX_CWND); + obj->ip_route.lock_initcwnd = NM_FLAGS_HAS(lock, 1 << RTAX_INITCWND); + obj->ip_route.lock_initrwnd = NM_FLAGS_HAS(lock, 1 << RTAX_INITRWND); + obj->ip_route.lock_mtu = NM_FLAGS_HAS(lock, 1 << RTAX_MTU); + + if (!is_v4) { + if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF)) { + /* Detect support for RTA_PREF by inspecting the netlink message. + * RTA_PREF was added in kernel 4.1, dated 21 June, 2015. */ + _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF, + tb[RTA_PREF] ? 1 : -1); + } + + if (tb[RTA_PREF]) + obj->ip6_route.rt_pref = nla_get_u8(tb[RTA_PREF]); + } + + obj->ip_route.r_rtm_flags = rtm->rtm_flags; + obj->ip_route.rt_source = nmp_utils_ip_config_source_from_rtprot(rtm->rtm_protocol); + + return g_steal_pointer(&obj); } static NMPObject * -_new_from_nl_routing_rule (struct nlmsghdr *nlh, gboolean id_only) -{ - static const struct nla_policy policy[] = { - [FRA_UNSPEC] = { }, - [FRA_DST] = { /* struct in_addr, struct in6_addr */ }, - [FRA_SRC] = { /* struct in_addr, struct in6_addr */ }, - [FRA_IIFNAME] = { .type = NLA_STRING, - .maxlen = IFNAMSIZ, }, - [FRA_GOTO] = { .type = NLA_U32, }, - [FRA_UNUSED2] = { }, - [FRA_PRIORITY] = { .type = NLA_U32, }, - [FRA_UNUSED3] = { }, - [FRA_UNUSED4] = { }, - [FRA_UNUSED5] = { }, - [FRA_FWMARK] = { .type = NLA_U32, }, - [FRA_FLOW] = { .type = NLA_U32, }, - [FRA_TUN_ID] = { .type = NLA_U64, }, - [FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32, }, - [FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32, }, - [FRA_TABLE] = { .type = NLA_U32, }, - [FRA_FWMASK] = { .type = NLA_U32, }, - [FRA_OIFNAME] = { .type = NLA_STRING, - .maxlen = IFNAMSIZ, }, - [FRA_PAD] = { .type = NLA_U32, }, - [FRA_L3MDEV] = { .type = NLA_U8, }, - [FRA_UID_RANGE] = { .minlen = sizeof(NMFibRuleUidRange), - .maxlen = sizeof(NMFibRuleUidRange), }, - [FRA_PROTOCOL] = { .type = NLA_U8, }, - [FRA_IP_PROTO] = { .type = NLA_U8, }, - [FRA_SPORT_RANGE] = { .minlen = sizeof(NMFibRulePortRange), - .maxlen = sizeof(NMFibRulePortRange), }, - [FRA_DPORT_RANGE] = { .minlen = sizeof(NMFibRulePortRange), - .maxlen = sizeof(NMFibRulePortRange), }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - const struct fib_rule_hdr *frh; - NMPlatformRoutingRule *props; - nm_auto_nmpobj NMPObject *obj = NULL; - int addr_family; - guint8 addr_size; - - if (nlmsg_parse_arr (nlh, sizeof (*frh), tb, policy) < 0) - return NULL; - - frh = nlmsg_data (nlh); - - addr_family = frh->family; - - if (!NM_IN_SET (addr_family, AF_INET, AF_INET6)) { - /* we don't care about other address families. */ - return NULL; - } - - addr_size = nm_utils_addr_family_to_size (addr_family); - - obj = nmp_object_new (NMP_OBJECT_TYPE_ROUTING_RULE, NULL); - props = &obj->routing_rule; - - props->addr_family = addr_family; - props->action = frh->action; - props->flags = frh->flags; - props->tos = frh->tos; - - props->table = tb[FRA_TABLE] - ? nla_get_u32 (tb[FRA_TABLE]) - : frh->table; - - if (tb[FRA_SUPPRESS_PREFIXLEN]) - props->suppress_prefixlen_inverse = ~nla_get_u32 (tb[FRA_SUPPRESS_PREFIXLEN]); - - if (tb[FRA_SUPPRESS_IFGROUP]) - props->suppress_ifgroup_inverse = ~nla_get_u32 (tb[FRA_SUPPRESS_IFGROUP]); - - if (tb[FRA_IIFNAME]) - nla_strlcpy (props->iifname, tb[FRA_IIFNAME], sizeof (props->iifname)); - - if (tb[FRA_OIFNAME]) - nla_strlcpy (props->oifname, tb[FRA_OIFNAME], sizeof (props->oifname)); - - if (tb[FRA_PRIORITY]) - props->priority = nla_get_u32 (tb[FRA_PRIORITY]); - - if (tb[FRA_FWMARK]) - props->fwmark = nla_get_u32 (tb[FRA_FWMARK]); - - if (tb[FRA_FWMASK]) - props->fwmask = nla_get_u32 (tb[FRA_FWMASK]); - - if (tb[FRA_GOTO]) - props->goto_target = nla_get_u32 (tb[FRA_GOTO]); - - props->src_len = frh->src_len; - if (props->src_len > addr_size * 8) - return NULL; - if (!tb[FRA_SRC]) { - if (props->src_len > 0) - return NULL; - } else if (!nm_ip_addr_set_from_untrusted (addr_family, - &props->src, - nla_data (tb[FRA_SRC]), - nla_len (tb[FRA_SRC]), - NULL)) - return NULL; - - props->dst_len = frh->dst_len; - if (props->dst_len > addr_size * 8) - return NULL; - if (!tb[FRA_DST]) { - if (props->dst_len > 0) - return NULL; - } else if (!nm_ip_addr_set_from_untrusted (addr_family, - &props->dst, - nla_data (tb[FRA_DST]), - nla_len (tb[FRA_DST]), - NULL)) - return NULL; - - if (tb[FRA_FLOW]) - props->flow = nla_get_u32 (tb[FRA_FLOW]); - - if (tb[FRA_TUN_ID]) - props->tun_id = nla_get_be64 (tb[FRA_TUN_ID]); - - if (tb[FRA_L3MDEV]) { - - if (!_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV)) { - /* support for FRA_L3MDEV was added in 96c63fa7393d0a346acfe5a91e0c7d4c7782641b, - * kernel 4.8, 3 October 2017. - * - * We can only detect support if the attribute is present. A missing attribute - * is not conclusive. */ - _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV, 1); - } - - /* actually, kernel only allows this attribute to be missing or - * "1". Still, encode it as full uint8. - * - * Note that FRA_L3MDEV and FRA_TABLE are mutally exclusive. */ - props->l3mdev = nla_get_u8 (tb[FRA_L3MDEV]); - } - - if (tb[FRA_PROTOCOL]) - props->protocol = nla_get_u8 (tb[FRA_PROTOCOL]); - else - nm_assert (props->protocol == RTPROT_UNSPEC); - - if (!_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL)) { - /* FRA_PROTOCOL was added in kernel 4.17, dated 3 June, 2018. - * See commit 1b71af6053af1bd2f849e9fda4f71c1e3f145dcf. */ - _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL, - tb[FRA_PROTOCOL] ? 1 : -1); - } - - if (tb[FRA_IP_PROTO]) - props->ip_proto = nla_get_u8 (tb[FRA_IP_PROTO]); - - G_STATIC_ASSERT_EXPR (sizeof (NMFibRulePortRange) == 4); - G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRulePortRange, start) == 0); - G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRulePortRange, end) == 2); - - nla_memcpy_checked_size (&props->sport_range, tb[FRA_SPORT_RANGE], sizeof (props->sport_range)); - nla_memcpy_checked_size (&props->dport_range, tb[FRA_DPORT_RANGE], sizeof (props->dport_range)); - - if (!_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO)) { - /* support for FRA_IP_PROTO, FRA_SPORT_RANGE, and FRA_DPORT_RANGE was added together - * by bfff4862653bb96001ab57c1edd6d03f48e5f035, kernel 4.17, 4 June 2018. - * - * Unfortunately, a missing attribute does not tell us anything about support. - * We can only tell for sure when we have support, but not when we don't have. */ - if ( tb[FRA_IP_PROTO] - || tb[FRA_SPORT_RANGE] - || tb[FRA_DPORT_RANGE]) - _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO, 1); - } - - G_STATIC_ASSERT_EXPR (sizeof (NMFibRuleUidRange) == 8); - G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRuleUidRange, start) == 0); - G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (NMFibRuleUidRange, end) == 4); - - if (tb[FRA_UID_RANGE]) { - - if (!_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE)) { - /* support for FRA_UID_RANGE was added in 622ec2c9d52405973c9f1ca5116eb1c393adfc7d, - * kernel 4.10, 19 February 2017. - * - * We can only detect support if the attribute is present. A missing attribute - * is not conclusive. */ - _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE, 1); - } - - nla_memcpy_checked_size (&props->uid_range, tb[FRA_UID_RANGE], sizeof (props->uid_range)); - props->uid_range_has = TRUE; - } - - return g_steal_pointer (&obj); +_new_from_nl_routing_rule(struct nlmsghdr *nlh, gboolean id_only) +{ + static const struct nla_policy policy[] = { + [FRA_UNSPEC] = {}, + [FRA_DST] = {/* struct in_addr, struct in6_addr */}, + [FRA_SRC] = {/* struct in_addr, struct in6_addr */}, + [FRA_IIFNAME] = + { + .type = NLA_STRING, + .maxlen = IFNAMSIZ, + }, + [FRA_GOTO] = + { + .type = NLA_U32, + }, + [FRA_UNUSED2] = {}, + [FRA_PRIORITY] = + { + .type = NLA_U32, + }, + [FRA_UNUSED3] = {}, + [FRA_UNUSED4] = {}, + [FRA_UNUSED5] = {}, + [FRA_FWMARK] = + { + .type = NLA_U32, + }, + [FRA_FLOW] = + { + .type = NLA_U32, + }, + [FRA_TUN_ID] = + { + .type = NLA_U64, + }, + [FRA_SUPPRESS_IFGROUP] = + { + .type = NLA_U32, + }, + [FRA_SUPPRESS_PREFIXLEN] = + { + .type = NLA_U32, + }, + [FRA_TABLE] = + { + .type = NLA_U32, + }, + [FRA_FWMASK] = + { + .type = NLA_U32, + }, + [FRA_OIFNAME] = + { + .type = NLA_STRING, + .maxlen = IFNAMSIZ, + }, + [FRA_PAD] = + { + .type = NLA_U32, + }, + [FRA_L3MDEV] = + { + .type = NLA_U8, + }, + [FRA_UID_RANGE] = + { + .minlen = sizeof(NMFibRuleUidRange), + .maxlen = sizeof(NMFibRuleUidRange), + }, + [FRA_PROTOCOL] = + { + .type = NLA_U8, + }, + [FRA_IP_PROTO] = + { + .type = NLA_U8, + }, + [FRA_SPORT_RANGE] = + { + .minlen = sizeof(NMFibRulePortRange), + .maxlen = sizeof(NMFibRulePortRange), + }, + [FRA_DPORT_RANGE] = + { + .minlen = sizeof(NMFibRulePortRange), + .maxlen = sizeof(NMFibRulePortRange), + }, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + const struct fib_rule_hdr *frh; + NMPlatformRoutingRule * props; + nm_auto_nmpobj NMPObject *obj = NULL; + int addr_family; + guint8 addr_size; + + if (nlmsg_parse_arr(nlh, sizeof(*frh), tb, policy) < 0) + return NULL; + + frh = nlmsg_data(nlh); + + addr_family = frh->family; + + if (!NM_IN_SET(addr_family, AF_INET, AF_INET6)) { + /* we don't care about other address families. */ + return NULL; + } + + addr_size = nm_utils_addr_family_to_size(addr_family); + + obj = nmp_object_new(NMP_OBJECT_TYPE_ROUTING_RULE, NULL); + props = &obj->routing_rule; + + props->addr_family = addr_family; + props->action = frh->action; + props->flags = frh->flags; + props->tos = frh->tos; + + props->table = tb[FRA_TABLE] ? nla_get_u32(tb[FRA_TABLE]) : frh->table; + + if (tb[FRA_SUPPRESS_PREFIXLEN]) + props->suppress_prefixlen_inverse = ~nla_get_u32(tb[FRA_SUPPRESS_PREFIXLEN]); + + if (tb[FRA_SUPPRESS_IFGROUP]) + props->suppress_ifgroup_inverse = ~nla_get_u32(tb[FRA_SUPPRESS_IFGROUP]); + + if (tb[FRA_IIFNAME]) + nla_strlcpy(props->iifname, tb[FRA_IIFNAME], sizeof(props->iifname)); + + if (tb[FRA_OIFNAME]) + nla_strlcpy(props->oifname, tb[FRA_OIFNAME], sizeof(props->oifname)); + + if (tb[FRA_PRIORITY]) + props->priority = nla_get_u32(tb[FRA_PRIORITY]); + + if (tb[FRA_FWMARK]) + props->fwmark = nla_get_u32(tb[FRA_FWMARK]); + + if (tb[FRA_FWMASK]) + props->fwmask = nla_get_u32(tb[FRA_FWMASK]); + + if (tb[FRA_GOTO]) + props->goto_target = nla_get_u32(tb[FRA_GOTO]); + + props->src_len = frh->src_len; + if (props->src_len > addr_size * 8) + return NULL; + if (!tb[FRA_SRC]) { + if (props->src_len > 0) + return NULL; + } else if (!nm_ip_addr_set_from_untrusted(addr_family, + &props->src, + nla_data(tb[FRA_SRC]), + nla_len(tb[FRA_SRC]), + NULL)) + return NULL; + + props->dst_len = frh->dst_len; + if (props->dst_len > addr_size * 8) + return NULL; + if (!tb[FRA_DST]) { + if (props->dst_len > 0) + return NULL; + } else if (!nm_ip_addr_set_from_untrusted(addr_family, + &props->dst, + nla_data(tb[FRA_DST]), + nla_len(tb[FRA_DST]), + NULL)) + return NULL; + + if (tb[FRA_FLOW]) + props->flow = nla_get_u32(tb[FRA_FLOW]); + + if (tb[FRA_TUN_ID]) + props->tun_id = nla_get_be64(tb[FRA_TUN_ID]); + + if (tb[FRA_L3MDEV]) { + if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV)) { + /* support for FRA_L3MDEV was added in 96c63fa7393d0a346acfe5a91e0c7d4c7782641b, + * kernel 4.8, 3 October 2017. + * + * We can only detect support if the attribute is present. A missing attribute + * is not conclusive. */ + _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV, 1); + } + + /* actually, kernel only allows this attribute to be missing or + * "1". Still, encode it as full uint8. + * + * Note that FRA_L3MDEV and FRA_TABLE are mutally exclusive. */ + props->l3mdev = nla_get_u8(tb[FRA_L3MDEV]); + } + + if (tb[FRA_PROTOCOL]) + props->protocol = nla_get_u8(tb[FRA_PROTOCOL]); + else + nm_assert(props->protocol == RTPROT_UNSPEC); + + if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL)) { + /* FRA_PROTOCOL was added in kernel 4.17, dated 3 June, 2018. + * See commit 1b71af6053af1bd2f849e9fda4f71c1e3f145dcf. */ + _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL, + tb[FRA_PROTOCOL] ? 1 : -1); + } + + if (tb[FRA_IP_PROTO]) + props->ip_proto = nla_get_u8(tb[FRA_IP_PROTO]); + + G_STATIC_ASSERT_EXPR(sizeof(NMFibRulePortRange) == 4); + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMFibRulePortRange, start) == 0); + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMFibRulePortRange, end) == 2); + + nla_memcpy_checked_size(&props->sport_range, tb[FRA_SPORT_RANGE], sizeof(props->sport_range)); + nla_memcpy_checked_size(&props->dport_range, tb[FRA_DPORT_RANGE], sizeof(props->dport_range)); + + if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO)) { + /* support for FRA_IP_PROTO, FRA_SPORT_RANGE, and FRA_DPORT_RANGE was added together + * by bfff4862653bb96001ab57c1edd6d03f48e5f035, kernel 4.17, 4 June 2018. + * + * Unfortunately, a missing attribute does not tell us anything about support. + * We can only tell for sure when we have support, but not when we don't have. */ + if (tb[FRA_IP_PROTO] || tb[FRA_SPORT_RANGE] || tb[FRA_DPORT_RANGE]) + _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO, 1); + } + + G_STATIC_ASSERT_EXPR(sizeof(NMFibRuleUidRange) == 8); + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMFibRuleUidRange, start) == 0); + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMFibRuleUidRange, end) == 4); + + if (tb[FRA_UID_RANGE]) { + if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE)) { + /* support for FRA_UID_RANGE was added in 622ec2c9d52405973c9f1ca5116eb1c393adfc7d, + * kernel 4.10, 19 February 2017. + * + * We can only detect support if the attribute is present. A missing attribute + * is not conclusive. */ + _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE, 1); + } + + nla_memcpy_checked_size(&props->uid_range, tb[FRA_UID_RANGE], sizeof(props->uid_range)); + props->uid_range_has = TRUE; + } + + return g_steal_pointer(&obj); } static guint32 -psched_tick_to_time (NMPlatform *platform, guint32 tick) +psched_tick_to_time(NMPlatform *platform, guint32 tick) { - static gboolean initialized; - static double tick_in_usec = 1; + static gboolean initialized; + static double tick_in_usec = 1; - if (!initialized) { - gs_free char *params = NULL; - double clock_factor = 1; - guint32 clock_res; - guint32 t2us; - guint32 us2t; + if (!initialized) { + gs_free char *params = NULL; + double clock_factor = 1; + guint32 clock_res; + guint32 t2us; + guint32 us2t; - initialized = TRUE; - params = nm_platform_sysctl_get (platform, NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/net/psched")); - if ( !params - || sscanf (params, "%08x%08x%08x", &t2us, &us2t, &clock_res) != 3) { - _LOGW ("packet scheduler parameters not available"); - } else { - /* See tc_core_init() in iproute2 */ - if (clock_res == 1000000000) - t2us = us2t; + initialized = TRUE; + params = nm_platform_sysctl_get(platform, NMP_SYSCTL_PATHID_ABSOLUTE("/proc/net/psched")); + if (!params || sscanf(params, "%08x%08x%08x", &t2us, &us2t, &clock_res) != 3) { + _LOGW("packet scheduler parameters not available"); + } else { + /* See tc_core_init() in iproute2 */ + if (clock_res == 1000000000) + t2us = us2t; - clock_factor = (double) clock_res / PSCHED_TIME_UNITS_PER_SEC; - tick_in_usec = (double) t2us / us2t * clock_factor; - } - } + clock_factor = (double) clock_res / PSCHED_TIME_UNITS_PER_SEC; + tick_in_usec = (double) t2us / us2t * clock_factor; + } + } - return tick / tick_in_usec; + return tick / tick_in_usec; } static NMPObject * -_new_from_nl_qdisc (NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_only) -{ - static const struct nla_policy policy[] = { - [TCA_KIND] = { .type = NLA_STRING }, - [TCA_OPTIONS] = { .type = NLA_NESTED }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - const struct tcmsg *tcm; - nm_auto_nmpobj NMPObject *obj = NULL; - - if (nlmsg_parse_arr (nlh, - sizeof (*tcm), - tb, - policy) < 0) - return NULL; - - if (!tb[TCA_KIND]) - return NULL; - - tcm = nlmsg_data (nlh); - - obj = nmp_object_new (NMP_OBJECT_TYPE_QDISC, NULL); - - obj->qdisc.kind = g_intern_string (nla_get_string (tb[TCA_KIND])); - obj->qdisc.ifindex = tcm->tcm_ifindex; - obj->qdisc.addr_family = tcm->tcm_family; - obj->qdisc.handle = tcm->tcm_handle; - obj->qdisc.parent = tcm->tcm_parent; - obj->qdisc.info = tcm->tcm_info; - - if (nm_streq0 (obj->qdisc.kind, "fq_codel")) { - obj->qdisc.fq_codel.memory_limit = NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET; - obj->qdisc.fq_codel.ce_threshold = NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED; - } - - if (tb[TCA_OPTIONS]) { - struct nlattr *options_attr; - int remaining; - - if (nm_streq0 (obj->qdisc.kind, "sfq")) { - struct tc_sfq_qopt_v1 opt; - - if (tb[TCA_OPTIONS]->nla_len >= nla_attr_size (sizeof (opt))) { - memcpy (&opt, nla_data (tb[TCA_OPTIONS]), sizeof (opt)); - obj->qdisc.sfq.quantum = opt.v0.quantum; - obj->qdisc.sfq.perturb_period = opt.v0.perturb_period; - obj->qdisc.sfq.limit = opt.v0.limit; - obj->qdisc.sfq.divisor = opt.v0.divisor; - obj->qdisc.sfq.flows = opt.v0.flows; - obj->qdisc.sfq.depth = opt.depth; - } - } else if (nm_streq0 (obj->qdisc.kind, "tbf")) { - static const struct nla_policy tbf_policy[] = { - [TCA_TBF_PARMS] = { .minlen = sizeof (struct tc_tbf_qopt) }, - [TCA_TBF_RATE64] = { .type = NLA_U64 }, - }; - struct nlattr *tbf_tb[G_N_ELEMENTS (tbf_policy)]; - struct tc_tbf_qopt opt; - - if (nla_parse_nested_arr (tbf_tb, tb[TCA_OPTIONS], tbf_policy) < 0) - return NULL; - if (!tbf_tb[TCA_TBF_PARMS]) - return NULL; - - nla_memcpy_checked_size (&opt, tbf_tb[TCA_TBF_PARMS], sizeof (opt)); - obj->qdisc.tbf.rate = opt.rate.rate; - if (tbf_tb[TCA_TBF_RATE64]) - obj->qdisc.tbf.rate = nla_get_u64 (tbf_tb[TCA_TBF_RATE64]); - obj->qdisc.tbf.burst = ((double) obj->qdisc.tbf.rate * - psched_tick_to_time (platform, opt.buffer)) / - PSCHED_TIME_UNITS_PER_SEC; - obj->qdisc.tbf.limit = opt.limit; - } else { - nla_for_each_nested (options_attr, tb[TCA_OPTIONS], remaining) { - if (nla_len (options_attr) < sizeof (uint32_t)) - continue; - - if (nm_streq0 (obj->qdisc.kind, "fq_codel")) { - switch (nla_type (options_attr)) { - case TCA_FQ_CODEL_LIMIT: - obj->qdisc.fq_codel.limit = nla_get_u32 (options_attr); - break; - case TCA_FQ_CODEL_FLOWS: - obj->qdisc.fq_codel.flows = nla_get_u32 (options_attr); - break; - case TCA_FQ_CODEL_TARGET: - obj->qdisc.fq_codel.target = nla_get_u32 (options_attr); - break; - case TCA_FQ_CODEL_INTERVAL: - obj->qdisc.fq_codel.interval = nla_get_u32 (options_attr); - break; - case TCA_FQ_CODEL_QUANTUM: - obj->qdisc.fq_codel.quantum = nla_get_u32 (options_attr); - break; - case TCA_FQ_CODEL_CE_THRESHOLD: - obj->qdisc.fq_codel.ce_threshold = nla_get_u32 (options_attr); - break; - case TCA_FQ_CODEL_MEMORY_LIMIT: - obj->qdisc.fq_codel.memory_limit = nla_get_u32 (options_attr); - break; - case TCA_FQ_CODEL_ECN: - obj->qdisc.fq_codel.ecn = !!nla_get_u32 (options_attr); - break; - } - } - } - } - } - - return g_steal_pointer (&obj); +_new_from_nl_qdisc(NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_only) +{ + static const struct nla_policy policy[] = { + [TCA_KIND] = {.type = NLA_STRING}, + [TCA_OPTIONS] = {.type = NLA_NESTED}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + const struct tcmsg *tcm; + nm_auto_nmpobj NMPObject *obj = NULL; + + if (nlmsg_parse_arr(nlh, sizeof(*tcm), tb, policy) < 0) + return NULL; + + if (!tb[TCA_KIND]) + return NULL; + + tcm = nlmsg_data(nlh); + + obj = nmp_object_new(NMP_OBJECT_TYPE_QDISC, NULL); + + obj->qdisc.kind = g_intern_string(nla_get_string(tb[TCA_KIND])); + obj->qdisc.ifindex = tcm->tcm_ifindex; + obj->qdisc.addr_family = tcm->tcm_family; + obj->qdisc.handle = tcm->tcm_handle; + obj->qdisc.parent = tcm->tcm_parent; + obj->qdisc.info = tcm->tcm_info; + + if (nm_streq0(obj->qdisc.kind, "fq_codel")) { + obj->qdisc.fq_codel.memory_limit = NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET; + obj->qdisc.fq_codel.ce_threshold = NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED; + } + + if (tb[TCA_OPTIONS]) { + struct nlattr *options_attr; + int remaining; + + if (nm_streq0(obj->qdisc.kind, "sfq")) { + struct tc_sfq_qopt_v1 opt; + + if (tb[TCA_OPTIONS]->nla_len >= nla_attr_size(sizeof(opt))) { + memcpy(&opt, nla_data(tb[TCA_OPTIONS]), sizeof(opt)); + obj->qdisc.sfq.quantum = opt.v0.quantum; + obj->qdisc.sfq.perturb_period = opt.v0.perturb_period; + obj->qdisc.sfq.limit = opt.v0.limit; + obj->qdisc.sfq.divisor = opt.v0.divisor; + obj->qdisc.sfq.flows = opt.v0.flows; + obj->qdisc.sfq.depth = opt.depth; + } + } else if (nm_streq0(obj->qdisc.kind, "tbf")) { + static const struct nla_policy tbf_policy[] = { + [TCA_TBF_PARMS] = {.minlen = sizeof(struct tc_tbf_qopt)}, + [TCA_TBF_RATE64] = {.type = NLA_U64}, + }; + struct nlattr * tbf_tb[G_N_ELEMENTS(tbf_policy)]; + struct tc_tbf_qopt opt; + + if (nla_parse_nested_arr(tbf_tb, tb[TCA_OPTIONS], tbf_policy) < 0) + return NULL; + if (!tbf_tb[TCA_TBF_PARMS]) + return NULL; + + nla_memcpy_checked_size(&opt, tbf_tb[TCA_TBF_PARMS], sizeof(opt)); + obj->qdisc.tbf.rate = opt.rate.rate; + if (tbf_tb[TCA_TBF_RATE64]) + obj->qdisc.tbf.rate = nla_get_u64(tbf_tb[TCA_TBF_RATE64]); + obj->qdisc.tbf.burst = + ((double) obj->qdisc.tbf.rate * psched_tick_to_time(platform, opt.buffer)) + / PSCHED_TIME_UNITS_PER_SEC; + obj->qdisc.tbf.limit = opt.limit; + } else { + nla_for_each_nested (options_attr, tb[TCA_OPTIONS], remaining) { + if (nla_len(options_attr) < sizeof(uint32_t)) + continue; + + if (nm_streq0(obj->qdisc.kind, "fq_codel")) { + switch (nla_type(options_attr)) { + case TCA_FQ_CODEL_LIMIT: + obj->qdisc.fq_codel.limit = nla_get_u32(options_attr); + break; + case TCA_FQ_CODEL_FLOWS: + obj->qdisc.fq_codel.flows = nla_get_u32(options_attr); + break; + case TCA_FQ_CODEL_TARGET: + obj->qdisc.fq_codel.target = nla_get_u32(options_attr); + break; + case TCA_FQ_CODEL_INTERVAL: + obj->qdisc.fq_codel.interval = nla_get_u32(options_attr); + break; + case TCA_FQ_CODEL_QUANTUM: + obj->qdisc.fq_codel.quantum = nla_get_u32(options_attr); + break; + case TCA_FQ_CODEL_CE_THRESHOLD: + obj->qdisc.fq_codel.ce_threshold = nla_get_u32(options_attr); + break; + case TCA_FQ_CODEL_MEMORY_LIMIT: + obj->qdisc.fq_codel.memory_limit = nla_get_u32(options_attr); + break; + case TCA_FQ_CODEL_ECN: + obj->qdisc.fq_codel.ecn = !!nla_get_u32(options_attr); + break; + } + } + } + } + } + + return g_steal_pointer(&obj); } static NMPObject * -_new_from_nl_tfilter (struct nlmsghdr *nlh, gboolean id_only) +_new_from_nl_tfilter(struct nlmsghdr *nlh, gboolean id_only) { - static const struct nla_policy policy[] = { - [TCA_KIND] = { .type = NLA_STRING }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - NMPObject *obj = NULL; - const struct tcmsg *tcm; + static const struct nla_policy policy[] = { + [TCA_KIND] = {.type = NLA_STRING}, + }; + struct nlattr * tb[G_N_ELEMENTS(policy)]; + NMPObject * obj = NULL; + const struct tcmsg *tcm; - if (nlmsg_parse_arr (nlh, sizeof (*tcm), tb, policy) < 0) - return NULL; + if (nlmsg_parse_arr(nlh, sizeof(*tcm), tb, policy) < 0) + return NULL; - if (!tb[TCA_KIND]) - return NULL; + if (!tb[TCA_KIND]) + return NULL; - tcm = nlmsg_data (nlh); + tcm = nlmsg_data(nlh); - obj = nmp_object_new (NMP_OBJECT_TYPE_TFILTER, NULL); + obj = nmp_object_new(NMP_OBJECT_TYPE_TFILTER, NULL); - obj->tfilter.kind = g_intern_string (nla_get_string (tb[TCA_KIND])); - obj->tfilter.ifindex = tcm->tcm_ifindex; - obj->tfilter.addr_family = tcm->tcm_family; - obj->tfilter.handle = tcm->tcm_handle; - obj->tfilter.parent = tcm->tcm_parent; - obj->tfilter.info = tcm->tcm_info; + obj->tfilter.kind = g_intern_string(nla_get_string(tb[TCA_KIND])); + obj->tfilter.ifindex = tcm->tcm_ifindex; + obj->tfilter.addr_family = tcm->tcm_family; + obj->tfilter.handle = tcm->tcm_handle; + obj->tfilter.parent = tcm->tcm_parent; + obj->tfilter.info = tcm->tcm_info; - return obj; + return obj; } /** @@ -3854,1057 +4003,1085 @@ _new_from_nl_tfilter (struct nlmsghdr *nlh, gboolean id_only) * Returns: %NULL or a newly created NMPObject instance. **/ static NMPObject * -nmp_object_new_from_nl (NMPlatform *platform, const NMPCache *cache, struct nl_msg *msg, gboolean id_only) -{ - struct nlmsghdr *msghdr; - - if (nlmsg_get_proto (msg) != NETLINK_ROUTE) - return NULL; - - msghdr = nlmsg_hdr (msg); - - switch (msghdr->nlmsg_type) { - case RTM_NEWLINK: - case RTM_DELLINK: - case RTM_GETLINK: - case RTM_SETLINK: - return _new_from_nl_link (platform, cache, msghdr, id_only); - case RTM_NEWADDR: - case RTM_DELADDR: - case RTM_GETADDR: - return _new_from_nl_addr (msghdr, id_only); - case RTM_NEWROUTE: - case RTM_DELROUTE: - case RTM_GETROUTE: - return _new_from_nl_route (msghdr, id_only); - case RTM_NEWRULE: - case RTM_DELRULE: - case RTM_GETRULE: - return _new_from_nl_routing_rule (msghdr, id_only); - case RTM_NEWQDISC: - case RTM_DELQDISC: - case RTM_GETQDISC: - return _new_from_nl_qdisc (platform, msghdr, id_only); - case RTM_NEWTFILTER: - case RTM_DELTFILTER: - case RTM_GETTFILTER: - return _new_from_nl_tfilter (msghdr, id_only); - default: - return NULL; - } +nmp_object_new_from_nl(NMPlatform * platform, + const NMPCache *cache, + struct nl_msg * msg, + gboolean id_only) +{ + struct nlmsghdr *msghdr; + + if (nlmsg_get_proto(msg) != NETLINK_ROUTE) + return NULL; + + msghdr = nlmsg_hdr(msg); + + switch (msghdr->nlmsg_type) { + case RTM_NEWLINK: + case RTM_DELLINK: + case RTM_GETLINK: + case RTM_SETLINK: + return _new_from_nl_link(platform, cache, msghdr, id_only); + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_GETADDR: + return _new_from_nl_addr(msghdr, id_only); + case RTM_NEWROUTE: + case RTM_DELROUTE: + case RTM_GETROUTE: + return _new_from_nl_route(msghdr, id_only); + case RTM_NEWRULE: + case RTM_DELRULE: + case RTM_GETRULE: + return _new_from_nl_routing_rule(msghdr, id_only); + case RTM_NEWQDISC: + case RTM_DELQDISC: + case RTM_GETQDISC: + return _new_from_nl_qdisc(platform, msghdr, id_only); + case RTM_NEWTFILTER: + case RTM_DELTFILTER: + case RTM_GETTFILTER: + return _new_from_nl_tfilter(msghdr, id_only); + default: + return NULL; + } } /*****************************************************************************/ static gboolean -_nl_msg_new_link_set_afspec (struct nl_msg *msg, - int addr_gen_mode, - NMUtilsIPv6IfaceId *iid) +_nl_msg_new_link_set_afspec(struct nl_msg *msg, int addr_gen_mode, NMUtilsIPv6IfaceId *iid) { - struct nlattr *af_spec; - struct nlattr *af_attr; + struct nlattr *af_spec; + struct nlattr *af_attr; - nm_assert (msg); + nm_assert(msg); - if (!(af_spec = nla_nest_start (msg, IFLA_AF_SPEC))) - goto nla_put_failure; + if (!(af_spec = nla_nest_start(msg, IFLA_AF_SPEC))) + goto nla_put_failure; - if (addr_gen_mode >= 0 || iid) { - if (!(af_attr = nla_nest_start (msg, AF_INET6))) - goto nla_put_failure; + if (addr_gen_mode >= 0 || iid) { + if (!(af_attr = nla_nest_start(msg, AF_INET6))) + goto nla_put_failure; - if (addr_gen_mode >= 0) - NLA_PUT_U8 (msg, IFLA_INET6_ADDR_GEN_MODE, addr_gen_mode); + if (addr_gen_mode >= 0) + NLA_PUT_U8(msg, IFLA_INET6_ADDR_GEN_MODE, addr_gen_mode); - if (iid) { - struct in6_addr i6_token = { .s6_addr = { 0, } }; + if (iid) { + struct in6_addr i6_token = {.s6_addr = { + 0, + }}; - nm_utils_ipv6_addr_set_interface_identifier (&i6_token, *iid); - NLA_PUT (msg, IFLA_INET6_TOKEN, sizeof (struct in6_addr), &i6_token); - } + nm_utils_ipv6_addr_set_interface_identifier(&i6_token, *iid); + NLA_PUT(msg, IFLA_INET6_TOKEN, sizeof(struct in6_addr), &i6_token); + } - nla_nest_end (msg, af_attr); - } + nla_nest_end(msg, af_attr); + } - nla_nest_end (msg, af_spec); + nla_nest_end(msg, af_spec); - return TRUE; + return TRUE; nla_put_failure: - g_return_val_if_reached (FALSE); + g_return_val_if_reached(FALSE); } static gboolean -_nl_msg_new_link_set_linkinfo (struct nl_msg *msg, - NMLinkType link_type, - gconstpointer extra_data) -{ - struct nlattr *info; - struct nlattr *data = NULL; - const char *kind; - - nm_assert (msg); - - kind = nm_link_type_to_rtnl_type_string (link_type); - if (!kind) - goto nla_put_failure; - - if (!(info = nla_nest_start (msg, IFLA_LINKINFO))) - goto nla_put_failure; - - NLA_PUT_STRING (msg, IFLA_INFO_KIND, kind); - - switch (link_type) { - case NM_LINK_TYPE_VLAN: { - const NMPlatformLnkVlan *props = extra_data; - - nm_assert (extra_data); - - if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - NLA_PUT_U16 (msg, IFLA_VLAN_ID, props->id); - - { - struct ifla_vlan_flags flags = { - .flags = props->flags & NM_VLAN_FLAGS_ALL, - .mask = NM_VLAN_FLAGS_ALL, - }; - - NLA_PUT (msg, IFLA_VLAN_FLAGS, sizeof (flags), &flags); - } - break; - } - case NM_LINK_TYPE_VRF: { - const NMPlatformLnkVrf *props = extra_data; - - nm_assert (extra_data); - - if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - NLA_PUT_U32 (msg, IFLA_VRF_TABLE, props->table); - break; - } - case NM_LINK_TYPE_VXLAN: { - const NMPlatformLnkVxlan *props = extra_data; - - nm_assert (extra_data); - - if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - NLA_PUT_U32 (msg, IFLA_VXLAN_ID, props->id); - - if (props->group) - NLA_PUT (msg, IFLA_VXLAN_GROUP, sizeof (props->group), &props->group); - else if (!IN6_IS_ADDR_UNSPECIFIED (&props->group6)) - NLA_PUT (msg, IFLA_VXLAN_GROUP6, sizeof (props->group6), &props->group6); - - if (props->local) - NLA_PUT (msg, IFLA_VXLAN_LOCAL, sizeof (props->local), &props->local); - else if (!IN6_IS_ADDR_UNSPECIFIED (&props->local6)) - NLA_PUT (msg, IFLA_VXLAN_LOCAL6, sizeof (props->local6), &props->local6); - - if (props->parent_ifindex >= 0) - NLA_PUT_U32 (msg, IFLA_VXLAN_LINK, props->parent_ifindex); - - if ( props->src_port_min - || props->src_port_max) { - struct nm_ifla_vxlan_port_range port_range = { - .low = htons (props->src_port_min), - .high = htons (props->src_port_max), - }; - - NLA_PUT (msg, IFLA_VXLAN_PORT_RANGE, sizeof (port_range), &port_range); - } - - NLA_PUT_U16 (msg, IFLA_VXLAN_PORT, htons (props->dst_port)); - NLA_PUT_U8 (msg, IFLA_VXLAN_TOS, props->tos); - NLA_PUT_U8 (msg, IFLA_VXLAN_TTL, props->ttl); - NLA_PUT_U32 (msg, IFLA_VXLAN_AGEING, props->ageing); - NLA_PUT_U32 (msg, IFLA_VXLAN_LIMIT, props->limit); - NLA_PUT_U8 (msg, IFLA_VXLAN_LEARNING, !!props->learning); - NLA_PUT_U8 (msg, IFLA_VXLAN_PROXY, !!props->proxy); - NLA_PUT_U8 (msg, IFLA_VXLAN_RSC, !!props->rsc); - NLA_PUT_U8 (msg, IFLA_VXLAN_L2MISS, !!props->l2miss); - NLA_PUT_U8 (msg, IFLA_VXLAN_L3MISS, !!props->l3miss); - break; - } - case NM_LINK_TYPE_VETH: { - const char *veth_peer = extra_data; - const struct ifinfomsg ifi = { }; - struct nlattr *info_peer; - - nm_assert (veth_peer); - - if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) - goto nla_put_failure; - if (!(info_peer = nla_nest_start (msg, 1 /*VETH_INFO_PEER*/))) - goto nla_put_failure; - if (nlmsg_append_struct (msg, &ifi) < 0) - goto nla_put_failure; - NLA_PUT_STRING (msg, IFLA_IFNAME, veth_peer); - nla_nest_end (msg, info_peer); - break; - } - case NM_LINK_TYPE_GRE: - case NM_LINK_TYPE_GRETAP: { - const NMPlatformLnkGre *props = extra_data; - - nm_assert (props); - nm_assert (props->is_tap == (link_type == NM_LINK_TYPE_GRETAP)); - - if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (props->parent_ifindex) - NLA_PUT_U32 (msg, IFLA_GRE_LINK, props->parent_ifindex); - NLA_PUT_U32 (msg, IFLA_GRE_LOCAL, props->local); - NLA_PUT_U32 (msg, IFLA_GRE_REMOTE, props->remote); - NLA_PUT_U8 (msg, IFLA_GRE_TTL, props->ttl); - NLA_PUT_U8 (msg, IFLA_GRE_TOS, props->tos); - NLA_PUT_U8 (msg, IFLA_GRE_PMTUDISC, !!props->path_mtu_discovery); - NLA_PUT_U32 (msg, IFLA_GRE_IKEY, htonl (props->input_key)); - NLA_PUT_U32 (msg, IFLA_GRE_OKEY, htonl (props->output_key)); - NLA_PUT_U16 (msg, IFLA_GRE_IFLAGS, htons (props->input_flags)); - NLA_PUT_U16 (msg, IFLA_GRE_OFLAGS, htons (props->output_flags)); - break; - } - case NM_LINK_TYPE_SIT: { - const NMPlatformLnkSit *props = extra_data; - - nm_assert (props); - - if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (props->parent_ifindex) - NLA_PUT_U32 (msg, IFLA_IPTUN_LINK, props->parent_ifindex); - NLA_PUT_U32 (msg, IFLA_IPTUN_LOCAL, props->local); - NLA_PUT_U32 (msg, IFLA_IPTUN_REMOTE, props->remote); - NLA_PUT_U8 (msg, IFLA_IPTUN_TTL, props->ttl); - NLA_PUT_U8 (msg, IFLA_IPTUN_TOS, props->tos); - NLA_PUT_U8 (msg, IFLA_IPTUN_PMTUDISC, !!props->path_mtu_discovery); - break; - } - case NM_LINK_TYPE_IP6TNL: { - const NMPlatformLnkIp6Tnl *props = extra_data; - guint32 flowinfo; - - nm_assert (props); - nm_assert (!props->is_gre); - - if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (props->parent_ifindex) - NLA_PUT_U32 (msg, IFLA_IPTUN_LINK, props->parent_ifindex); - - if (!IN6_IS_ADDR_UNSPECIFIED (&props->local)) - NLA_PUT (msg, IFLA_IPTUN_LOCAL, sizeof (props->local), &props->local); - if (!IN6_IS_ADDR_UNSPECIFIED (&props->remote)) - NLA_PUT (msg, IFLA_IPTUN_REMOTE, sizeof (props->remote), &props->remote); - - NLA_PUT_U8 (msg, IFLA_IPTUN_TTL, props->ttl); - NLA_PUT_U8 (msg, IFLA_IPTUN_ENCAP_LIMIT, props->encap_limit); - - flowinfo = props->flow_label & IP6_FLOWINFO_FLOWLABEL_MASK; - flowinfo |= (props->tclass << IP6_FLOWINFO_TCLASS_SHIFT) - & IP6_FLOWINFO_TCLASS_MASK; - NLA_PUT_U32 (msg, IFLA_IPTUN_FLOWINFO, htonl (flowinfo)); - NLA_PUT_U8 (msg, IFLA_IPTUN_PROTO, props->proto); - NLA_PUT_U32 (msg, IFLA_IPTUN_FLAGS, props->flags); - break; - } - case NM_LINK_TYPE_IP6GRE: - case NM_LINK_TYPE_IP6GRETAP: { - const NMPlatformLnkIp6Tnl *props = extra_data; - guint32 flowinfo; - - nm_assert (props); - nm_assert (props->is_gre); - - if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (props->parent_ifindex) - NLA_PUT_U32 (msg, IFLA_GRE_LINK, props->parent_ifindex); - - NLA_PUT_U32 (msg, IFLA_GRE_IKEY, htonl (props->input_key)); - NLA_PUT_U32 (msg, IFLA_GRE_OKEY, htonl (props->output_key)); - NLA_PUT_U16 (msg, IFLA_GRE_IFLAGS, htons (props->input_flags)); - NLA_PUT_U16 (msg, IFLA_GRE_OFLAGS, htons (props->output_flags)); - - if (!IN6_IS_ADDR_UNSPECIFIED (&props->local)) - NLA_PUT (msg, IFLA_GRE_LOCAL, sizeof (props->local), &props->local); - if (!IN6_IS_ADDR_UNSPECIFIED (&props->local)) - NLA_PUT (msg, IFLA_GRE_REMOTE, sizeof (props->remote), &props->remote); - - NLA_PUT_U8 (msg, IFLA_GRE_TTL, props->ttl); - NLA_PUT_U8 (msg, IFLA_GRE_ENCAP_LIMIT, props->encap_limit); - - flowinfo = props->flow_label & IP6_FLOWINFO_FLOWLABEL_MASK; - flowinfo |= (props->tclass << IP6_FLOWINFO_TCLASS_SHIFT) - & IP6_FLOWINFO_TCLASS_MASK; - NLA_PUT_U32 (msg, IFLA_GRE_FLOWINFO, htonl (flowinfo)); - NLA_PUT_U32 (msg, IFLA_GRE_FLAGS, props->flags); - break; - } - case NM_LINK_TYPE_IPIP: { - const NMPlatformLnkIpIp *props = extra_data; - - nm_assert (props); - - if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (props->parent_ifindex) - NLA_PUT_U32 (msg, IFLA_IPTUN_LINK, props->parent_ifindex); - NLA_PUT_U32 (msg, IFLA_IPTUN_LOCAL, props->local); - NLA_PUT_U32 (msg, IFLA_IPTUN_REMOTE, props->remote); - NLA_PUT_U8 (msg, IFLA_IPTUN_TTL, props->ttl); - NLA_PUT_U8 (msg, IFLA_IPTUN_TOS, props->tos); - NLA_PUT_U8 (msg, IFLA_IPTUN_PMTUDISC, !!props->path_mtu_discovery); - break; - } - case NM_LINK_TYPE_MACSEC: { - const NMPlatformLnkMacsec *props = extra_data; - - nm_assert (props); - - if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (props->icv_length) - NLA_PUT_U8 (msg, IFLA_MACSEC_ICV_LEN, 16); - if (props->cipher_suite) - NLA_PUT_U64 (msg, IFLA_MACSEC_CIPHER_SUITE, props->cipher_suite); - if (props->replay_protect) - NLA_PUT_U32 (msg, IFLA_MACSEC_WINDOW, props->window); - - NLA_PUT_U64 (msg, IFLA_MACSEC_SCI, htobe64 (props->sci)); - NLA_PUT_U8 (msg, IFLA_MACSEC_ENCODING_SA, props->encoding_sa); - NLA_PUT_U8 (msg, IFLA_MACSEC_ENCRYPT, props->encrypt); - NLA_PUT_U8 (msg, IFLA_MACSEC_PROTECT, props->protect); - NLA_PUT_U8 (msg, IFLA_MACSEC_INC_SCI, props->include_sci); - NLA_PUT_U8 (msg, IFLA_MACSEC_ES, props->es); - NLA_PUT_U8 (msg, IFLA_MACSEC_SCB, props->scb); - NLA_PUT_U8 (msg, IFLA_MACSEC_REPLAY_PROTECT, props->replay_protect); - NLA_PUT_U8 (msg, IFLA_MACSEC_VALIDATION, props->validation); - break; - }; - case NM_LINK_TYPE_MACVTAP: - case NM_LINK_TYPE_MACVLAN: { - const NMPlatformLnkMacvlan *props = extra_data; - - nm_assert (props); - - if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - NLA_PUT_U32 (msg, IFLA_MACVLAN_MODE, props->mode); - NLA_PUT_U16 (msg, IFLA_MACVLAN_FLAGS, props->no_promisc ? MACVLAN_FLAG_NOPROMISC : 0); - break; - } - default: - nm_assert (!extra_data); - break; - } - - if (data) - nla_nest_end (msg, data); - - nla_nest_end (msg, info); - - return TRUE; +_nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpointer extra_data) +{ + struct nlattr *info; + struct nlattr *data = NULL; + const char * kind; + + nm_assert(msg); + + kind = nm_link_type_to_rtnl_type_string(link_type); + if (!kind) + goto nla_put_failure; + + if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) + goto nla_put_failure; + + NLA_PUT_STRING(msg, IFLA_INFO_KIND, kind); + + switch (link_type) { + case NM_LINK_TYPE_BRIDGE: + { + const NMPlatformLnkBridge *props = extra_data; + + nm_assert(extra_data); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + NLA_PUT_U32(msg, IFLA_BR_FORWARD_DELAY, props->forward_delay); + NLA_PUT_U32(msg, IFLA_BR_HELLO_TIME, props->hello_time); + NLA_PUT_U32(msg, IFLA_BR_MAX_AGE, props->max_age); + NLA_PUT_U32(msg, IFLA_BR_AGEING_TIME, props->ageing_time); + NLA_PUT_U32(msg, IFLA_BR_STP_STATE, !!props->stp_state); + NLA_PUT_U16(msg, IFLA_BR_PRIORITY, props->priority); + NLA_PUT_U16(msg, IFLA_BR_VLAN_PROTOCOL, htons(props->vlan_protocol)); + if (props->vlan_stats_enabled) + NLA_PUT_U8(msg, IFLA_BR_VLAN_STATS_ENABLED, !!props->vlan_stats_enabled); + NLA_PUT_U16(msg, IFLA_BR_GROUP_FWD_MASK, props->group_fwd_mask); + NLA_PUT(msg, IFLA_BR_GROUP_ADDR, sizeof(props->group_addr), &props->group_addr); + NLA_PUT_U8(msg, IFLA_BR_MCAST_SNOOPING, !!props->mcast_snooping); + NLA_PUT_U8(msg, IFLA_BR_MCAST_ROUTER, props->mcast_router); + NLA_PUT_U8(msg, IFLA_BR_MCAST_QUERY_USE_IFADDR, !!props->mcast_query_use_ifaddr); + NLA_PUT_U8(msg, IFLA_BR_MCAST_QUERIER, !!props->mcast_querier); + NLA_PUT_U32(msg, IFLA_BR_MCAST_HASH_MAX, props->mcast_hash_max); + NLA_PUT_U32(msg, IFLA_BR_MCAST_LAST_MEMBER_CNT, props->mcast_last_member_count); + NLA_PUT_U32(msg, IFLA_BR_MCAST_STARTUP_QUERY_CNT, props->mcast_startup_query_count); + NLA_PUT_U64(msg, IFLA_BR_MCAST_LAST_MEMBER_INTVL, props->mcast_last_member_interval); + NLA_PUT_U64(msg, IFLA_BR_MCAST_MEMBERSHIP_INTVL, props->mcast_membership_interval); + NLA_PUT_U64(msg, IFLA_BR_MCAST_QUERIER_INTVL, props->mcast_querier_interval); + NLA_PUT_U64(msg, IFLA_BR_MCAST_QUERY_INTVL, props->mcast_query_interval); + NLA_PUT_U64(msg, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, props->mcast_query_response_interval); + NLA_PUT_U64(msg, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, props->mcast_startup_query_interval); + break; + } + case NM_LINK_TYPE_VLAN: + { + const NMPlatformLnkVlan *props = extra_data; + + nm_assert(extra_data); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + NLA_PUT_U16(msg, IFLA_VLAN_ID, props->id); + + { + struct ifla_vlan_flags flags = { + .flags = props->flags & NM_VLAN_FLAGS_ALL, + .mask = NM_VLAN_FLAGS_ALL, + }; + + NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags); + } + break; + } + case NM_LINK_TYPE_VRF: + { + const NMPlatformLnkVrf *props = extra_data; + + nm_assert(extra_data); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + NLA_PUT_U32(msg, IFLA_VRF_TABLE, props->table); + break; + } + case NM_LINK_TYPE_VXLAN: + { + const NMPlatformLnkVxlan *props = extra_data; + + nm_assert(extra_data); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + NLA_PUT_U32(msg, IFLA_VXLAN_ID, props->id); + + if (props->group) + NLA_PUT(msg, IFLA_VXLAN_GROUP, sizeof(props->group), &props->group); + else if (!IN6_IS_ADDR_UNSPECIFIED(&props->group6)) + NLA_PUT(msg, IFLA_VXLAN_GROUP6, sizeof(props->group6), &props->group6); + + if (props->local) + NLA_PUT(msg, IFLA_VXLAN_LOCAL, sizeof(props->local), &props->local); + else if (!IN6_IS_ADDR_UNSPECIFIED(&props->local6)) + NLA_PUT(msg, IFLA_VXLAN_LOCAL6, sizeof(props->local6), &props->local6); + + if (props->parent_ifindex >= 0) + NLA_PUT_U32(msg, IFLA_VXLAN_LINK, props->parent_ifindex); + + if (props->src_port_min || props->src_port_max) { + struct nm_ifla_vxlan_port_range port_range = { + .low = htons(props->src_port_min), + .high = htons(props->src_port_max), + }; + + NLA_PUT(msg, IFLA_VXLAN_PORT_RANGE, sizeof(port_range), &port_range); + } + + NLA_PUT_U16(msg, IFLA_VXLAN_PORT, htons(props->dst_port)); + NLA_PUT_U8(msg, IFLA_VXLAN_TOS, props->tos); + NLA_PUT_U8(msg, IFLA_VXLAN_TTL, props->ttl); + NLA_PUT_U32(msg, IFLA_VXLAN_AGEING, props->ageing); + NLA_PUT_U32(msg, IFLA_VXLAN_LIMIT, props->limit); + NLA_PUT_U8(msg, IFLA_VXLAN_LEARNING, !!props->learning); + NLA_PUT_U8(msg, IFLA_VXLAN_PROXY, !!props->proxy); + NLA_PUT_U8(msg, IFLA_VXLAN_RSC, !!props->rsc); + NLA_PUT_U8(msg, IFLA_VXLAN_L2MISS, !!props->l2miss); + NLA_PUT_U8(msg, IFLA_VXLAN_L3MISS, !!props->l3miss); + break; + } + case NM_LINK_TYPE_VETH: + { + const char * veth_peer = extra_data; + const struct ifinfomsg ifi = {}; + struct nlattr * info_peer; + + nm_assert(veth_peer); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + if (!(info_peer = nla_nest_start(msg, 1 /*VETH_INFO_PEER*/))) + goto nla_put_failure; + if (nlmsg_append_struct(msg, &ifi) < 0) + goto nla_put_failure; + NLA_PUT_STRING(msg, IFLA_IFNAME, veth_peer); + nla_nest_end(msg, info_peer); + break; + } + case NM_LINK_TYPE_GRE: + case NM_LINK_TYPE_GRETAP: + { + const NMPlatformLnkGre *props = extra_data; + + nm_assert(props); + nm_assert(props->is_tap == (link_type == NM_LINK_TYPE_GRETAP)); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->parent_ifindex) + NLA_PUT_U32(msg, IFLA_GRE_LINK, props->parent_ifindex); + NLA_PUT_U32(msg, IFLA_GRE_LOCAL, props->local); + NLA_PUT_U32(msg, IFLA_GRE_REMOTE, props->remote); + NLA_PUT_U8(msg, IFLA_GRE_TTL, props->ttl); + NLA_PUT_U8(msg, IFLA_GRE_TOS, props->tos); + NLA_PUT_U8(msg, IFLA_GRE_PMTUDISC, !!props->path_mtu_discovery); + NLA_PUT_U32(msg, IFLA_GRE_IKEY, htonl(props->input_key)); + NLA_PUT_U32(msg, IFLA_GRE_OKEY, htonl(props->output_key)); + NLA_PUT_U16(msg, IFLA_GRE_IFLAGS, htons(props->input_flags)); + NLA_PUT_U16(msg, IFLA_GRE_OFLAGS, htons(props->output_flags)); + break; + } + case NM_LINK_TYPE_SIT: + { + const NMPlatformLnkSit *props = extra_data; + + nm_assert(props); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->parent_ifindex) + NLA_PUT_U32(msg, IFLA_IPTUN_LINK, props->parent_ifindex); + NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, props->local); + NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, props->remote); + NLA_PUT_U8(msg, IFLA_IPTUN_TTL, props->ttl); + NLA_PUT_U8(msg, IFLA_IPTUN_TOS, props->tos); + NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, !!props->path_mtu_discovery); + break; + } + case NM_LINK_TYPE_IP6TNL: + { + const NMPlatformLnkIp6Tnl *props = extra_data; + guint32 flowinfo; + + nm_assert(props); + nm_assert(!props->is_gre); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->parent_ifindex) + NLA_PUT_U32(msg, IFLA_IPTUN_LINK, props->parent_ifindex); + + if (!IN6_IS_ADDR_UNSPECIFIED(&props->local)) + NLA_PUT(msg, IFLA_IPTUN_LOCAL, sizeof(props->local), &props->local); + if (!IN6_IS_ADDR_UNSPECIFIED(&props->remote)) + NLA_PUT(msg, IFLA_IPTUN_REMOTE, sizeof(props->remote), &props->remote); + + NLA_PUT_U8(msg, IFLA_IPTUN_TTL, props->ttl); + NLA_PUT_U8(msg, IFLA_IPTUN_ENCAP_LIMIT, props->encap_limit); + + flowinfo = props->flow_label & IP6_FLOWINFO_FLOWLABEL_MASK; + flowinfo |= (props->tclass << IP6_FLOWINFO_TCLASS_SHIFT) & IP6_FLOWINFO_TCLASS_MASK; + NLA_PUT_U32(msg, IFLA_IPTUN_FLOWINFO, htonl(flowinfo)); + NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, props->proto); + NLA_PUT_U32(msg, IFLA_IPTUN_FLAGS, props->flags); + break; + } + case NM_LINK_TYPE_IP6GRE: + case NM_LINK_TYPE_IP6GRETAP: + { + const NMPlatformLnkIp6Tnl *props = extra_data; + guint32 flowinfo; + + nm_assert(props); + nm_assert(props->is_gre); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->parent_ifindex) + NLA_PUT_U32(msg, IFLA_GRE_LINK, props->parent_ifindex); + + NLA_PUT_U32(msg, IFLA_GRE_IKEY, htonl(props->input_key)); + NLA_PUT_U32(msg, IFLA_GRE_OKEY, htonl(props->output_key)); + NLA_PUT_U16(msg, IFLA_GRE_IFLAGS, htons(props->input_flags)); + NLA_PUT_U16(msg, IFLA_GRE_OFLAGS, htons(props->output_flags)); + + if (!IN6_IS_ADDR_UNSPECIFIED(&props->local)) + NLA_PUT(msg, IFLA_GRE_LOCAL, sizeof(props->local), &props->local); + if (!IN6_IS_ADDR_UNSPECIFIED(&props->local)) + NLA_PUT(msg, IFLA_GRE_REMOTE, sizeof(props->remote), &props->remote); + + NLA_PUT_U8(msg, IFLA_GRE_TTL, props->ttl); + NLA_PUT_U8(msg, IFLA_GRE_ENCAP_LIMIT, props->encap_limit); + + flowinfo = props->flow_label & IP6_FLOWINFO_FLOWLABEL_MASK; + flowinfo |= (props->tclass << IP6_FLOWINFO_TCLASS_SHIFT) & IP6_FLOWINFO_TCLASS_MASK; + NLA_PUT_U32(msg, IFLA_GRE_FLOWINFO, htonl(flowinfo)); + NLA_PUT_U32(msg, IFLA_GRE_FLAGS, props->flags); + break; + } + case NM_LINK_TYPE_IPIP: + { + const NMPlatformLnkIpIp *props = extra_data; + + nm_assert(props); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->parent_ifindex) + NLA_PUT_U32(msg, IFLA_IPTUN_LINK, props->parent_ifindex); + NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, props->local); + NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, props->remote); + NLA_PUT_U8(msg, IFLA_IPTUN_TTL, props->ttl); + NLA_PUT_U8(msg, IFLA_IPTUN_TOS, props->tos); + NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, !!props->path_mtu_discovery); + break; + } + case NM_LINK_TYPE_MACSEC: + { + const NMPlatformLnkMacsec *props = extra_data; + + nm_assert(props); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->icv_length) + NLA_PUT_U8(msg, IFLA_MACSEC_ICV_LEN, 16); + if (props->cipher_suite) + NLA_PUT_U64(msg, IFLA_MACSEC_CIPHER_SUITE, props->cipher_suite); + if (props->replay_protect) + NLA_PUT_U32(msg, IFLA_MACSEC_WINDOW, props->window); + + NLA_PUT_U64(msg, IFLA_MACSEC_SCI, htobe64(props->sci)); + NLA_PUT_U8(msg, IFLA_MACSEC_ENCODING_SA, props->encoding_sa); + NLA_PUT_U8(msg, IFLA_MACSEC_ENCRYPT, props->encrypt); + NLA_PUT_U8(msg, IFLA_MACSEC_PROTECT, props->protect); + NLA_PUT_U8(msg, IFLA_MACSEC_INC_SCI, props->include_sci); + NLA_PUT_U8(msg, IFLA_MACSEC_ES, props->es); + NLA_PUT_U8(msg, IFLA_MACSEC_SCB, props->scb); + NLA_PUT_U8(msg, IFLA_MACSEC_REPLAY_PROTECT, props->replay_protect); + NLA_PUT_U8(msg, IFLA_MACSEC_VALIDATION, props->validation); + break; + }; + case NM_LINK_TYPE_MACVTAP: + case NM_LINK_TYPE_MACVLAN: + { + const NMPlatformLnkMacvlan *props = extra_data; + + nm_assert(props); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + NLA_PUT_U32(msg, IFLA_MACVLAN_MODE, props->mode); + NLA_PUT_U16(msg, IFLA_MACVLAN_FLAGS, props->no_promisc ? MACVLAN_FLAG_NOPROMISC : 0); + break; + } + default: + nm_assert(!extra_data); + break; + } + + if (data) + nla_nest_end(msg, data); + + nla_nest_end(msg, info); + + return TRUE; nla_put_failure: - g_return_val_if_reached (FALSE); + g_return_val_if_reached(FALSE); } static gboolean -_nl_msg_new_link_set_linkinfo_vlan (struct nl_msg *msg, - int vlan_id, - guint32 flags_mask, - guint32 flags_set, - const NMVlanQosMapping *ingress_qos, - int ingress_qos_len, - const NMVlanQosMapping *egress_qos, - int egress_qos_len) -{ - struct nlattr *info; - struct nlattr *data; - guint i; - gboolean has_any_vlan_properties = FALSE; - - G_STATIC_ASSERT (NM_VLAN_FLAG_REORDER_HEADERS == (guint32) VLAN_FLAG_REORDER_HDR); - G_STATIC_ASSERT (NM_VLAN_FLAG_GVRP == (guint32) VLAN_FLAG_GVRP); - G_STATIC_ASSERT (NM_VLAN_FLAG_LOOSE_BINDING == (guint32) VLAN_FLAG_LOOSE_BINDING); - G_STATIC_ASSERT (NM_VLAN_FLAG_MVRP == (guint32) VLAN_FLAG_MVRP); +_nl_msg_new_link_set_linkinfo_vlan(struct nl_msg * msg, + int vlan_id, + guint32 flags_mask, + guint32 flags_set, + const NMVlanQosMapping *ingress_qos, + int ingress_qos_len, + const NMVlanQosMapping *egress_qos, + int egress_qos_len) +{ + struct nlattr *info; + struct nlattr *data; + guint i; + gboolean has_any_vlan_properties = FALSE; + + G_STATIC_ASSERT(NM_VLAN_FLAG_REORDER_HEADERS == (guint32) VLAN_FLAG_REORDER_HDR); + G_STATIC_ASSERT(NM_VLAN_FLAG_GVRP == (guint32) VLAN_FLAG_GVRP); + G_STATIC_ASSERT(NM_VLAN_FLAG_LOOSE_BINDING == (guint32) VLAN_FLAG_LOOSE_BINDING); + G_STATIC_ASSERT(NM_VLAN_FLAG_MVRP == (guint32) VLAN_FLAG_MVRP); #define VLAN_XGRESS_PRIO_VALID(from) (((from) & ~(guint32) 0x07) == 0) - nm_assert (msg); - - /* We must not create an empty IFLA_LINKINFO section. Otherwise, kernel - * rejects the request as invalid. */ - if ( flags_mask != 0 - || vlan_id >= 0) - has_any_vlan_properties = TRUE; - if ( !has_any_vlan_properties - && ingress_qos && ingress_qos_len > 0) { - for (i = 0; i < ingress_qos_len; i++) { - if (VLAN_XGRESS_PRIO_VALID (ingress_qos[i].from)) { - has_any_vlan_properties = TRUE; - break; - } - } - } - if ( !has_any_vlan_properties - && egress_qos && egress_qos_len > 0) { - for (i = 0; i < egress_qos_len; i++) { - if (VLAN_XGRESS_PRIO_VALID (egress_qos[i].to)) { - has_any_vlan_properties = TRUE; - break; - } - } - } - if (!has_any_vlan_properties) - return TRUE; - - if (!(info = nla_nest_start (msg, IFLA_LINKINFO))) - goto nla_put_failure; - - NLA_PUT_STRING (msg, IFLA_INFO_KIND, "vlan"); - - if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (vlan_id >= 0) - NLA_PUT_U16 (msg, IFLA_VLAN_ID, vlan_id); - - if (flags_mask != 0) { - struct ifla_vlan_flags flags = { - .flags = flags_mask & flags_set, - .mask = flags_mask, - }; - - NLA_PUT (msg, IFLA_VLAN_FLAGS, sizeof (flags), &flags); - } - - if (ingress_qos && ingress_qos_len > 0) { - struct nlattr *qos = NULL; - - for (i = 0; i < ingress_qos_len; i++) { - /* Silently ignore invalid mappings. Kernel would truncate - * them and modify the wrong mapping. */ - if (VLAN_XGRESS_PRIO_VALID (ingress_qos[i].from)) { - if (!qos) { - if (!(qos = nla_nest_start (msg, IFLA_VLAN_INGRESS_QOS))) - goto nla_put_failure; - } - NLA_PUT (msg, i, sizeof (ingress_qos[i]), &ingress_qos[i]); - } - } - - if (qos) - nla_nest_end (msg, qos); - } - - if (egress_qos && egress_qos_len > 0) { - struct nlattr *qos = NULL; - - for (i = 0; i < egress_qos_len; i++) { - if (VLAN_XGRESS_PRIO_VALID (egress_qos[i].to)) { - if (!qos) { - if (!(qos = nla_nest_start (msg, IFLA_VLAN_EGRESS_QOS))) - goto nla_put_failure; - } - NLA_PUT (msg, i, sizeof (egress_qos[i]), &egress_qos[i]); - } - } - - if (qos) - nla_nest_end (msg, qos); - } - - nla_nest_end (msg, data); - nla_nest_end (msg, info); - - return TRUE; + nm_assert(msg); + + /* We must not create an empty IFLA_LINKINFO section. Otherwise, kernel + * rejects the request as invalid. */ + if (flags_mask != 0 || vlan_id >= 0) + has_any_vlan_properties = TRUE; + if (!has_any_vlan_properties && ingress_qos && ingress_qos_len > 0) { + for (i = 0; i < ingress_qos_len; i++) { + if (VLAN_XGRESS_PRIO_VALID(ingress_qos[i].from)) { + has_any_vlan_properties = TRUE; + break; + } + } + } + if (!has_any_vlan_properties && egress_qos && egress_qos_len > 0) { + for (i = 0; i < egress_qos_len; i++) { + if (VLAN_XGRESS_PRIO_VALID(egress_qos[i].to)) { + has_any_vlan_properties = TRUE; + break; + } + } + } + if (!has_any_vlan_properties) + return TRUE; + + if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) + goto nla_put_failure; + + NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan"); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (vlan_id >= 0) + NLA_PUT_U16(msg, IFLA_VLAN_ID, vlan_id); + + if (flags_mask != 0) { + struct ifla_vlan_flags flags = { + .flags = flags_mask & flags_set, + .mask = flags_mask, + }; + + NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags); + } + + if (ingress_qos && ingress_qos_len > 0) { + struct nlattr *qos = NULL; + + for (i = 0; i < ingress_qos_len; i++) { + /* Silently ignore invalid mappings. Kernel would truncate + * them and modify the wrong mapping. */ + if (VLAN_XGRESS_PRIO_VALID(ingress_qos[i].from)) { + if (!qos) { + if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS))) + goto nla_put_failure; + } + NLA_PUT(msg, i, sizeof(ingress_qos[i]), &ingress_qos[i]); + } + } + + if (qos) + nla_nest_end(msg, qos); + } + + if (egress_qos && egress_qos_len > 0) { + struct nlattr *qos = NULL; + + for (i = 0; i < egress_qos_len; i++) { + if (VLAN_XGRESS_PRIO_VALID(egress_qos[i].to)) { + if (!qos) { + if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS))) + goto nla_put_failure; + } + NLA_PUT(msg, i, sizeof(egress_qos[i]), &egress_qos[i]); + } + } + + if (qos) + nla_nest_end(msg, qos); + } + + nla_nest_end(msg, data); + nla_nest_end(msg, info); + + return TRUE; nla_put_failure: - g_return_val_if_reached (FALSE); + g_return_val_if_reached(FALSE); } static struct nl_msg * -_nl_msg_new_link_full (int nlmsg_type, - int nlmsg_flags, - int ifindex, - const char *ifname, - guint8 family, - unsigned flags_mask, - unsigned flags_set) +_nl_msg_new_link_full(int nlmsg_type, + int nlmsg_flags, + int ifindex, + const char *ifname, + guint8 family, + unsigned flags_mask, + unsigned flags_set) { - nm_auto_nlmsg struct nl_msg *msg = NULL; - const struct ifinfomsg ifi = { - .ifi_family = family, - .ifi_change = flags_mask, - .ifi_flags = flags_set, - .ifi_index = ifindex, - }; + nm_auto_nlmsg struct nl_msg *msg = NULL; + const struct ifinfomsg ifi = { + .ifi_family = family, + .ifi_change = flags_mask, + .ifi_flags = flags_set, + .ifi_index = ifindex, + }; - nm_assert (NM_IN_SET (nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK, RTM_SETLINK)); + nm_assert(NM_IN_SET(nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK, RTM_SETLINK)); - msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags); - if (nlmsg_append_struct (msg, &ifi) < 0) - goto nla_put_failure; + if (nlmsg_append_struct(msg, &ifi) < 0) + goto nla_put_failure; - if (ifname) - NLA_PUT_STRING (msg, IFLA_IFNAME, ifname); + if (ifname) + NLA_PUT_STRING(msg, IFLA_IFNAME, ifname); - return g_steal_pointer (&msg); + return g_steal_pointer(&msg); nla_put_failure: - g_return_val_if_reached (NULL); + g_return_val_if_reached(NULL); } static struct nl_msg * -_nl_msg_new_link (int nlmsg_type, - int nlmsg_flags, - int ifindex, - const char *ifname) +_nl_msg_new_link(int nlmsg_type, int nlmsg_flags, int ifindex, const char *ifname) { - return _nl_msg_new_link_full (nlmsg_type, nlmsg_flags, ifindex, ifname, AF_UNSPEC, 0, 0); + return _nl_msg_new_link_full(nlmsg_type, nlmsg_flags, ifindex, ifname, AF_UNSPEC, 0, 0); } /* Copied and modified from libnl3's build_addr_msg(). */ static struct nl_msg * -_nl_msg_new_address (int nlmsg_type, - int nlmsg_flags, - int family, - int ifindex, - gconstpointer address, - guint8 plen, - gconstpointer peer_address, - guint32 flags, - int scope, - guint32 lifetime, - guint32 preferred, - in_addr_t ip4_broadcast_address, - const char *label) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - struct ifaddrmsg am = { - .ifa_family = family, - .ifa_index = ifindex, - .ifa_prefixlen = plen, - .ifa_flags = flags, - }; - gsize addr_len; - - nm_assert (NM_IN_SET (family, AF_INET, AF_INET6)); - nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWADDR, RTM_DELADDR)); - - msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); - - if (scope == -1) { - /* Allow having scope unset, and detect the scope (including IPv4 compatibility hack). */ - if ( family == AF_INET - && address - && *((char *) address) == 127) - scope = RT_SCOPE_HOST; - else - scope = RT_SCOPE_UNIVERSE; - } - am.ifa_scope = scope, - - addr_len = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); - - if (nlmsg_append_struct (msg, &am) < 0) - goto nla_put_failure; - - if (address) - NLA_PUT (msg, IFA_LOCAL, addr_len, address); - - if (peer_address) - NLA_PUT (msg, IFA_ADDRESS, addr_len, peer_address); - else if (address) - NLA_PUT (msg, IFA_ADDRESS, addr_len, address); - - if (label && label[0]) - NLA_PUT_STRING (msg, IFA_LABEL, label); - - if (ip4_broadcast_address != 0) - NLA_PUT (msg, IFA_BROADCAST, sizeof (in_addr_t), &ip4_broadcast_address); - - if ( lifetime != NM_PLATFORM_LIFETIME_PERMANENT - || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { - struct ifa_cacheinfo ca = { - .ifa_valid = lifetime, - .ifa_prefered = preferred, - }; - - NLA_PUT (msg, IFA_CACHEINFO, sizeof (ca), &ca); - } - - if (flags & ~((guint32) 0xFF)) { - /* only set the IFA_FLAGS attribute, if they actually contain additional - * flags that are not already set to am.ifa_flags. - * - * Older kernels refuse RTM_NEWADDR and RTM_NEWROUTE messages with EINVAL - * if they contain unknown netlink attributes. See net/core/rtnetlink.c, which - * was fixed by kernel commit 661d2967b3f1b34eeaa7e212e7b9bbe8ee072b59. */ - NLA_PUT_U32 (msg, IFA_FLAGS, flags); - } - - return g_steal_pointer (&msg); +_nl_msg_new_address(int nlmsg_type, + int nlmsg_flags, + int family, + int ifindex, + gconstpointer address, + guint8 plen, + gconstpointer peer_address, + guint32 flags, + int scope, + guint32 lifetime, + guint32 preferred, + in_addr_t ip4_broadcast_address, + const char * label) +{ + nm_auto_nlmsg struct nl_msg *msg = NULL; + struct ifaddrmsg am = { + .ifa_family = family, + .ifa_index = ifindex, + .ifa_prefixlen = plen, + .ifa_flags = flags, + }; + gsize addr_len; + + nm_assert(NM_IN_SET(family, AF_INET, AF_INET6)); + nm_assert(NM_IN_SET(nlmsg_type, RTM_NEWADDR, RTM_DELADDR)); + + msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags); + + if (scope == -1) { + /* Allow having scope unset, and detect the scope (including IPv4 compatibility hack). */ + if (family == AF_INET && address && *((char *) address) == 127) + scope = RT_SCOPE_HOST; + else + scope = RT_SCOPE_UNIVERSE; + } + am.ifa_scope = scope, + + addr_len = family == AF_INET ? sizeof(in_addr_t) : sizeof(struct in6_addr); + + if (nlmsg_append_struct(msg, &am) < 0) + goto nla_put_failure; + + if (address) + NLA_PUT(msg, IFA_LOCAL, addr_len, address); + + if (peer_address) + NLA_PUT(msg, IFA_ADDRESS, addr_len, peer_address); + else if (address) + NLA_PUT(msg, IFA_ADDRESS, addr_len, address); + + if (label && label[0]) + NLA_PUT_STRING(msg, IFA_LABEL, label); + + if (ip4_broadcast_address != 0) + NLA_PUT(msg, IFA_BROADCAST, sizeof(in_addr_t), &ip4_broadcast_address); + + if (lifetime != NM_PLATFORM_LIFETIME_PERMANENT || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { + struct ifa_cacheinfo ca = { + .ifa_valid = lifetime, + .ifa_prefered = preferred, + }; + + NLA_PUT(msg, IFA_CACHEINFO, sizeof(ca), &ca); + } + + if (flags & ~((guint32) 0xFF)) { + /* only set the IFA_FLAGS attribute, if they actually contain additional + * flags that are not already set to am.ifa_flags. + * + * Older kernels refuse RTM_NEWADDR and RTM_NEWROUTE messages with EINVAL + * if they contain unknown netlink attributes. See net/core/rtnetlink.c, which + * was fixed by kernel commit 661d2967b3f1b34eeaa7e212e7b9bbe8ee072b59. */ + NLA_PUT_U32(msg, IFA_FLAGS, flags); + } + + return g_steal_pointer(&msg); nla_put_failure: - g_return_val_if_reached (NULL); + g_return_val_if_reached(NULL); } static guint32 -ip_route_get_lock_flag (const NMPlatformIPRoute *route) +ip_route_get_lock_flag(const NMPlatformIPRoute *route) { - return (((guint32) route->lock_window) << RTAX_WINDOW) - | (((guint32) route->lock_cwnd) << RTAX_CWND) - | (((guint32) route->lock_initcwnd) << RTAX_INITCWND) - | (((guint32) route->lock_initrwnd) << RTAX_INITRWND) - | (((guint32) route->lock_mtu) << RTAX_MTU); + return (((guint32) route->lock_window) << RTAX_WINDOW) + | (((guint32) route->lock_cwnd) << RTAX_CWND) + | (((guint32) route->lock_initcwnd) << RTAX_INITCWND) + | (((guint32) route->lock_initrwnd) << RTAX_INITRWND) + | (((guint32) route->lock_mtu) << RTAX_MTU); } /* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */ static struct nl_msg * -_nl_msg_new_route (int nlmsg_type, - guint16 nlmsgflags, - const NMPObject *obj) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); - gboolean is_v4 = klass->addr_family == AF_INET; - const guint32 lock = ip_route_get_lock_flag (NMP_OBJECT_CAST_IP_ROUTE (obj)); - const guint32 table = nm_platform_route_table_uncoerce (NMP_OBJECT_CAST_IP_ROUTE (obj)->table_coerced, TRUE); - const struct rtmsg rtmsg = { - .rtm_family = klass->addr_family, - .rtm_tos = is_v4 - ? obj->ip4_route.tos - : 0, - .rtm_table = table <= 0xFF ? table : RT_TABLE_UNSPEC, - .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot (obj->ip_route.rt_source), - .rtm_scope = is_v4 - ? nm_platform_route_scope_inv (obj->ip4_route.scope_inv) - : RT_SCOPE_NOWHERE, - .rtm_type = nm_platform_route_type_uncoerce (NMP_OBJECT_CAST_IP_ROUTE (obj)->type_coerced), - .rtm_flags = obj->ip_route.r_rtm_flags & ((unsigned) (RTNH_F_ONLINK)), - .rtm_dst_len = obj->ip_route.plen, - .rtm_src_len = is_v4 - ? 0 - : NMP_OBJECT_CAST_IP6_ROUTE (obj)->src_plen, - }; - - gsize addr_len; - - nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); - - msg = nlmsg_alloc_simple (nlmsg_type, (int) nlmsgflags); - - if (nlmsg_append_struct (msg, &rtmsg) < 0) - goto nla_put_failure; - - addr_len = is_v4 - ? sizeof (in_addr_t) - : sizeof (struct in6_addr); - - NLA_PUT (msg, RTA_DST, addr_len, - is_v4 - ? (gconstpointer) &obj->ip4_route.network - : (gconstpointer) &obj->ip6_route.network); - - if (!is_v4) { - if (!IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (obj)->src)) - NLA_PUT (msg, RTA_SRC, addr_len, &obj->ip6_route.src); - } - - NLA_PUT_U32 (msg, RTA_PRIORITY, obj->ip_route.metric); - - if (table > 0xFF) - NLA_PUT_U32 (msg, RTA_TABLE, table); - - if (is_v4) { - if (NMP_OBJECT_CAST_IP4_ROUTE (obj)->pref_src) - NLA_PUT (msg, RTA_PREFSRC, addr_len, &obj->ip4_route.pref_src); - } else { - if (!IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (obj)->pref_src)) - NLA_PUT (msg, RTA_PREFSRC, addr_len, &obj->ip6_route.pref_src); - } - - if ( obj->ip_route.mss - || obj->ip_route.window - || obj->ip_route.cwnd - || obj->ip_route.initcwnd - || obj->ip_route.initrwnd - || obj->ip_route.mtu - || lock) { - struct nlattr *metrics; - - metrics = nla_nest_start (msg, RTA_METRICS); - if (!metrics) - goto nla_put_failure; - - if (obj->ip_route.mss) - NLA_PUT_U32 (msg, RTAX_ADVMSS, obj->ip_route.mss); - if (obj->ip_route.window) - NLA_PUT_U32 (msg, RTAX_WINDOW, obj->ip_route.window); - if (obj->ip_route.cwnd) - NLA_PUT_U32 (msg, RTAX_CWND, obj->ip_route.cwnd); - if (obj->ip_route.initcwnd) - NLA_PUT_U32 (msg, RTAX_INITCWND, obj->ip_route.initcwnd); - if (obj->ip_route.initrwnd) - NLA_PUT_U32 (msg, RTAX_INITRWND, obj->ip_route.initrwnd); - if (obj->ip_route.mtu) - NLA_PUT_U32 (msg, RTAX_MTU, obj->ip_route.mtu); - if (lock) - NLA_PUT_U32 (msg, RTAX_LOCK, lock); - - nla_nest_end (msg, metrics); - } - - /* We currently don't have need for multi-hop routes... */ - if (is_v4) { - NLA_PUT (msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway); - } else { - if (!IN6_IS_ADDR_UNSPECIFIED (&obj->ip6_route.gateway)) - NLA_PUT (msg, RTA_GATEWAY, addr_len, &obj->ip6_route.gateway); - } - NLA_PUT_U32 (msg, RTA_OIF, obj->ip_route.ifindex); - - if ( !is_v4 - && obj->ip6_route.rt_pref != NM_ICMPV6_ROUTER_PREF_MEDIUM) - NLA_PUT_U8 (msg, RTA_PREF, obj->ip6_route.rt_pref); - - return g_steal_pointer (&msg); +_nl_msg_new_route(int nlmsg_type, guint16 nlmsgflags, const NMPObject *obj) +{ + nm_auto_nlmsg struct nl_msg *msg = NULL; + const NMPClass * klass = NMP_OBJECT_GET_CLASS(obj); + gboolean is_v4 = klass->addr_family == AF_INET; + const guint32 lock = ip_route_get_lock_flag(NMP_OBJECT_CAST_IP_ROUTE(obj)); + const guint32 table = + nm_platform_route_table_uncoerce(NMP_OBJECT_CAST_IP_ROUTE(obj)->table_coerced, TRUE); + const struct rtmsg rtmsg = { + .rtm_family = klass->addr_family, + .rtm_tos = is_v4 ? obj->ip4_route.tos : 0, + .rtm_table = table <= 0xFF ? table : RT_TABLE_UNSPEC, + .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot(obj->ip_route.rt_source), + .rtm_scope = + is_v4 ? nm_platform_route_scope_inv(obj->ip4_route.scope_inv) : RT_SCOPE_NOWHERE, + .rtm_type = nm_platform_route_type_uncoerce(NMP_OBJECT_CAST_IP_ROUTE(obj)->type_coerced), + .rtm_flags = obj->ip_route.r_rtm_flags & ((unsigned) (RTNH_F_ONLINK)), + .rtm_dst_len = obj->ip_route.plen, + .rtm_src_len = is_v4 ? 0 : NMP_OBJECT_CAST_IP6_ROUTE(obj)->src_plen, + }; + + gsize addr_len; + + nm_assert( + NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert(NM_IN_SET(nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); + + msg = nlmsg_alloc_simple(nlmsg_type, (int) nlmsgflags); + + if (nlmsg_append_struct(msg, &rtmsg) < 0) + goto nla_put_failure; + + addr_len = is_v4 ? sizeof(in_addr_t) : sizeof(struct in6_addr); + + NLA_PUT(msg, + RTA_DST, + addr_len, + is_v4 ? (gconstpointer) &obj->ip4_route.network + : (gconstpointer) &obj->ip6_route.network); + + if (!is_v4) { + if (!IN6_IS_ADDR_UNSPECIFIED(&NMP_OBJECT_CAST_IP6_ROUTE(obj)->src)) + NLA_PUT(msg, RTA_SRC, addr_len, &obj->ip6_route.src); + } + + NLA_PUT_U32(msg, + RTA_PRIORITY, + is_v4 ? nm_platform_ip4_route_get_effective_metric(&obj->ip4_route) + : nm_platform_ip6_route_get_effective_metric(&obj->ip6_route)); + + if (table > 0xFF) + NLA_PUT_U32(msg, RTA_TABLE, table); + + if (is_v4) { + if (NMP_OBJECT_CAST_IP4_ROUTE(obj)->pref_src) + NLA_PUT(msg, RTA_PREFSRC, addr_len, &obj->ip4_route.pref_src); + } else { + if (!IN6_IS_ADDR_UNSPECIFIED(&NMP_OBJECT_CAST_IP6_ROUTE(obj)->pref_src)) + NLA_PUT(msg, RTA_PREFSRC, addr_len, &obj->ip6_route.pref_src); + } + + if (obj->ip_route.mss || obj->ip_route.window || obj->ip_route.cwnd || obj->ip_route.initcwnd + || obj->ip_route.initrwnd || obj->ip_route.mtu || lock) { + struct nlattr *metrics; + + metrics = nla_nest_start(msg, RTA_METRICS); + if (!metrics) + goto nla_put_failure; + + if (obj->ip_route.mss) + NLA_PUT_U32(msg, RTAX_ADVMSS, obj->ip_route.mss); + if (obj->ip_route.window) + NLA_PUT_U32(msg, RTAX_WINDOW, obj->ip_route.window); + if (obj->ip_route.cwnd) + NLA_PUT_U32(msg, RTAX_CWND, obj->ip_route.cwnd); + if (obj->ip_route.initcwnd) + NLA_PUT_U32(msg, RTAX_INITCWND, obj->ip_route.initcwnd); + if (obj->ip_route.initrwnd) + NLA_PUT_U32(msg, RTAX_INITRWND, obj->ip_route.initrwnd); + if (obj->ip_route.mtu) + NLA_PUT_U32(msg, RTAX_MTU, obj->ip_route.mtu); + if (lock) + NLA_PUT_U32(msg, RTAX_LOCK, lock); + + nla_nest_end(msg, metrics); + } + + /* We currently don't have need for multi-hop routes... */ + if (is_v4) { + NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway); + } else { + if (!IN6_IS_ADDR_UNSPECIFIED(&obj->ip6_route.gateway)) + NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip6_route.gateway); + } + NLA_PUT_U32(msg, RTA_OIF, obj->ip_route.ifindex); + + if (!is_v4 && obj->ip6_route.rt_pref != NM_ICMPV6_ROUTER_PREF_MEDIUM) + NLA_PUT_U8(msg, RTA_PREF, obj->ip6_route.rt_pref); + + return g_steal_pointer(&msg); nla_put_failure: - g_return_val_if_reached (NULL); + g_return_val_if_reached(NULL); } static struct nl_msg * -_nl_msg_new_routing_rule (int nlmsg_type, - int nlmsg_flags, - const NMPlatformRoutingRule *routing_rule) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - const guint8 addr_size = nm_utils_addr_family_to_size (routing_rule->addr_family); - guint32 table; +_nl_msg_new_routing_rule(int nlmsg_type, int nlmsg_flags, const NMPlatformRoutingRule *routing_rule) +{ + nm_auto_nlmsg struct nl_msg *msg = NULL; + const guint8 addr_size = nm_utils_addr_family_to_size(routing_rule->addr_family); + guint32 table; - msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags); - table = routing_rule->table; + table = routing_rule->table; - if ( NM_IN_SET (routing_rule->addr_family, AF_INET, AF_INET6) - && routing_rule->action == FR_ACT_TO_TBL - && routing_rule->l3mdev == 0 - && table == RT_TABLE_UNSPEC) { - /* for IPv6, this setting is invalid and rejected by kernel. That's fine. - * - * for IPv4, kernel will automatically assign an unused table. That's not - * fine, because we don't know what we will get. - * - * The caller must not allow that to happen. */ - nm_assert_not_reached (); - } + if (NM_IN_SET(routing_rule->addr_family, AF_INET, AF_INET6) + && routing_rule->action == FR_ACT_TO_TBL && routing_rule->l3mdev == 0 + && table == RT_TABLE_UNSPEC) { + /* for IPv6, this setting is invalid and rejected by kernel. That's fine. + * + * for IPv4, kernel will automatically assign an unused table. That's not + * fine, because we don't know what we will get. + * + * The caller must not allow that to happen. */ + nm_assert_not_reached(); + } - { - const struct fib_rule_hdr frh = { - .family = routing_rule->addr_family, - .src_len = routing_rule->src_len, - .dst_len = routing_rule->dst_len, - .tos = routing_rule->tos, - .table = table < 0x100u ? (guint8) table : (guint8) RT_TABLE_UNSPEC, - .action = routing_rule->action, + { + const struct fib_rule_hdr frh = { + .family = routing_rule->addr_family, + .src_len = routing_rule->src_len, + .dst_len = routing_rule->dst_len, + .tos = routing_rule->tos, + .table = table < 0x100u ? (guint8) table : (guint8) RT_TABLE_UNSPEC, + .action = routing_rule->action, - /* we only allow setting the "not" flag. */ - .flags = routing_rule->flags & ((guint32) FIB_RULE_INVERT), - }; + /* we only allow setting the "not" flag. */ + .flags = routing_rule->flags & ((guint32) FIB_RULE_INVERT), + }; - if (nlmsg_append_struct (msg, &frh) < 0) - goto nla_put_failure; - } + if (nlmsg_append_struct(msg, &frh) < 0) + goto nla_put_failure; + } - if (table > G_MAXINT8) - NLA_PUT_U32 (msg, FRA_TABLE, table); + if (table > G_MAXINT8) + NLA_PUT_U32(msg, FRA_TABLE, table); - if (routing_rule->suppress_prefixlen_inverse != 0) - NLA_PUT_U32 (msg, FRA_SUPPRESS_PREFIXLEN, ~routing_rule->suppress_prefixlen_inverse); + if (routing_rule->suppress_prefixlen_inverse != 0) + NLA_PUT_U32(msg, FRA_SUPPRESS_PREFIXLEN, ~routing_rule->suppress_prefixlen_inverse); - if (routing_rule->suppress_ifgroup_inverse != 0) - NLA_PUT_U32 (msg, FRA_SUPPRESS_IFGROUP, ~routing_rule->suppress_ifgroup_inverse); + if (routing_rule->suppress_ifgroup_inverse != 0) + NLA_PUT_U32(msg, FRA_SUPPRESS_IFGROUP, ~routing_rule->suppress_ifgroup_inverse); - if (routing_rule->iifname[0] != '\0') - NLA_PUT_STRING (msg, FRA_IIFNAME, routing_rule->iifname); + if (routing_rule->iifname[0] != '\0') + NLA_PUT_STRING(msg, FRA_IIFNAME, routing_rule->iifname); - if (routing_rule->oifname[0] != '\0') - NLA_PUT_STRING (msg, FRA_OIFNAME, routing_rule->oifname); + if (routing_rule->oifname[0] != '\0') + NLA_PUT_STRING(msg, FRA_OIFNAME, routing_rule->oifname); - /* we always set the priority and don't support letting kernel pick one. */ - NLA_PUT_U32 (msg, FRA_PRIORITY, routing_rule->priority); + /* we always set the priority and don't support letting kernel pick one. */ + NLA_PUT_U32(msg, FRA_PRIORITY, routing_rule->priority); - if ( routing_rule->fwmark != 0 - || routing_rule->fwmask != 0) { - NLA_PUT_U32 (msg, FRA_FWMARK, routing_rule->fwmark); - NLA_PUT_U32 (msg, FRA_FWMASK, routing_rule->fwmask); - } + if (routing_rule->fwmark != 0 || routing_rule->fwmask != 0) { + NLA_PUT_U32(msg, FRA_FWMARK, routing_rule->fwmark); + NLA_PUT_U32(msg, FRA_FWMASK, routing_rule->fwmask); + } - if (routing_rule->src_len > 0) - NLA_PUT (msg, FRA_SRC, addr_size, &routing_rule->src); + if (routing_rule->src_len > 0) + NLA_PUT(msg, FRA_SRC, addr_size, &routing_rule->src); - if (routing_rule->dst_len > 0) - NLA_PUT (msg, FRA_DST, addr_size, &routing_rule->dst); + if (routing_rule->dst_len > 0) + NLA_PUT(msg, FRA_DST, addr_size, &routing_rule->dst); - if (routing_rule->flow != 0) { - /* only relevant for IPv4. */ - NLA_PUT_U32 (msg, FRA_FLOW, routing_rule->flow); - } + if (routing_rule->flow != 0) { + /* only relevant for IPv4. */ + NLA_PUT_U32(msg, FRA_FLOW, routing_rule->flow); + } - if (routing_rule->tun_id != 0) - NLA_PUT_U64 (msg, FRA_TUN_ID, htobe64 (routing_rule->tun_id)); + if (routing_rule->tun_id != 0) + NLA_PUT_U64(msg, FRA_TUN_ID, htobe64(routing_rule->tun_id)); - if (routing_rule->l3mdev) - NLA_PUT_U8 (msg, FRA_L3MDEV, routing_rule->l3mdev); + if (routing_rule->l3mdev) + NLA_PUT_U8(msg, FRA_L3MDEV, routing_rule->l3mdev); - if (routing_rule->protocol != RTPROT_UNSPEC) - NLA_PUT_U8 (msg, FRA_PROTOCOL, routing_rule->protocol); + if (routing_rule->protocol != RTPROT_UNSPEC) + NLA_PUT_U8(msg, FRA_PROTOCOL, routing_rule->protocol); - if (routing_rule->ip_proto != 0) - NLA_PUT_U8 (msg, FRA_IP_PROTO, routing_rule->ip_proto); + if (routing_rule->ip_proto != 0) + NLA_PUT_U8(msg, FRA_IP_PROTO, routing_rule->ip_proto); - if ( routing_rule->sport_range.start - || routing_rule->sport_range.end) - NLA_PUT (msg, FRA_SPORT_RANGE, sizeof (routing_rule->sport_range), &routing_rule->sport_range); + if (routing_rule->sport_range.start || routing_rule->sport_range.end) + NLA_PUT(msg, + FRA_SPORT_RANGE, + sizeof(routing_rule->sport_range), + &routing_rule->sport_range); - if ( routing_rule->dport_range.start - || routing_rule->dport_range.end) - NLA_PUT (msg, FRA_DPORT_RANGE, sizeof (routing_rule->dport_range), &routing_rule->dport_range); + if (routing_rule->dport_range.start || routing_rule->dport_range.end) + NLA_PUT(msg, + FRA_DPORT_RANGE, + sizeof(routing_rule->dport_range), + &routing_rule->dport_range); - if (routing_rule->uid_range_has) - NLA_PUT (msg, FRA_UID_RANGE, sizeof (routing_rule->uid_range), &routing_rule->uid_range); + if (routing_rule->uid_range_has) + NLA_PUT(msg, FRA_UID_RANGE, sizeof(routing_rule->uid_range), &routing_rule->uid_range); - switch (routing_rule->action) { - case FR_ACT_GOTO: - NLA_PUT_U32 (msg, FRA_GOTO, routing_rule->goto_target); - break; - } + switch (routing_rule->action) { + case FR_ACT_GOTO: + NLA_PUT_U32(msg, FRA_GOTO, routing_rule->goto_target); + break; + } - return g_steal_pointer (&msg); + return g_steal_pointer(&msg); nla_put_failure: - g_return_val_if_reached (NULL); + g_return_val_if_reached(NULL); } static struct nl_msg * -_nl_msg_new_qdisc (int nlmsg_type, - int nlmsg_flags, - const NMPlatformQdisc *qdisc) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - struct nlattr *tc_options; - const struct tcmsg tcm = { - .tcm_family = qdisc->addr_family, - .tcm_ifindex = qdisc->ifindex, - .tcm_handle = qdisc->handle, - .tcm_parent = qdisc->parent, - .tcm_info = qdisc->info, - }; - - msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags | NMP_NLM_FLAG_F_ECHO); - - if (nlmsg_append_struct (msg, &tcm) < 0) - goto nla_put_failure; - - NLA_PUT_STRING (msg, TCA_KIND, qdisc->kind); - - if (nm_streq (qdisc->kind, "sfq")) { - struct tc_sfq_qopt_v1 opt = { }; - - opt.v0.quantum = qdisc->sfq.quantum; - opt.v0.limit = qdisc->sfq.limit; - opt.v0.perturb_period = qdisc->sfq.perturb_period; - opt.v0.flows = qdisc->sfq.flows; - opt.v0.divisor = qdisc->sfq.divisor; - opt.depth = qdisc->sfq.depth; - - NLA_PUT (msg, TCA_OPTIONS, sizeof (opt), &opt); - } else if (nm_streq (qdisc->kind, "tbf")) { - struct tc_tbf_qopt opt = { }; - - if (!(tc_options = nla_nest_start (msg, TCA_OPTIONS))) - goto nla_put_failure; - - opt.rate.rate = (qdisc->tbf.rate >= (1ULL << 32)) - ? ~0U - : (guint32) qdisc->tbf.rate; - if (qdisc->tbf.limit) - opt.limit = qdisc->tbf.limit; - else if (qdisc->tbf.latency) { - opt.limit = qdisc->tbf.rate * (double) qdisc->tbf.latency - / PSCHED_TIME_UNITS_PER_SEC - + qdisc->tbf.burst; - } - - NLA_PUT (msg, TCA_TBF_PARMS, sizeof (opt), &opt); - if (qdisc->tbf.rate >= (1ULL << 32)) - NLA_PUT_U64 (msg, TCA_TBF_RATE64, qdisc->tbf.rate); - NLA_PUT_U32 (msg, TCA_TBF_BURST, qdisc->tbf.burst); - - nla_nest_end (msg, tc_options); - } else { - if (!(tc_options = nla_nest_start (msg, TCA_OPTIONS))) - goto nla_put_failure; - - if (nm_streq (qdisc->kind, "fq_codel")) { - if (qdisc->fq_codel.limit) - NLA_PUT_U32 (msg, TCA_FQ_CODEL_LIMIT, qdisc->fq_codel.limit); - if (qdisc->fq_codel.flows) - NLA_PUT_U32 (msg, TCA_FQ_CODEL_FLOWS, qdisc->fq_codel.flows); - if (qdisc->fq_codel.target) - NLA_PUT_U32 (msg, TCA_FQ_CODEL_TARGET, qdisc->fq_codel.target); - if (qdisc->fq_codel.interval) - NLA_PUT_U32 (msg, TCA_FQ_CODEL_INTERVAL, qdisc->fq_codel.interval); - if (qdisc->fq_codel.quantum) - NLA_PUT_U32 (msg, TCA_FQ_CODEL_QUANTUM, qdisc->fq_codel.quantum); - if (qdisc->fq_codel.ce_threshold != NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED) - NLA_PUT_U32 (msg, TCA_FQ_CODEL_CE_THRESHOLD, qdisc->fq_codel.ce_threshold); - if (qdisc->fq_codel.memory_limit != NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET) - NLA_PUT_U32 (msg, TCA_FQ_CODEL_MEMORY_LIMIT, qdisc->fq_codel.memory_limit); - if (qdisc->fq_codel.ecn) - NLA_PUT_U32 (msg, TCA_FQ_CODEL_ECN, qdisc->fq_codel.ecn); - } - - nla_nest_end (msg, tc_options); - } - - return g_steal_pointer (&msg); +_nl_msg_new_qdisc(int nlmsg_type, int nlmsg_flags, const NMPlatformQdisc *qdisc) +{ + nm_auto_nlmsg struct nl_msg *msg = NULL; + struct nlattr * tc_options; + const struct tcmsg tcm = { + .tcm_family = qdisc->addr_family, + .tcm_ifindex = qdisc->ifindex, + .tcm_handle = qdisc->handle, + .tcm_parent = qdisc->parent, + .tcm_info = qdisc->info, + }; + + msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags | NMP_NLM_FLAG_F_ECHO); + + if (nlmsg_append_struct(msg, &tcm) < 0) + goto nla_put_failure; + + NLA_PUT_STRING(msg, TCA_KIND, qdisc->kind); + + if (nm_streq(qdisc->kind, "sfq")) { + struct tc_sfq_qopt_v1 opt = {}; + + opt.v0.quantum = qdisc->sfq.quantum; + opt.v0.limit = qdisc->sfq.limit; + opt.v0.perturb_period = qdisc->sfq.perturb_period; + opt.v0.flows = qdisc->sfq.flows; + opt.v0.divisor = qdisc->sfq.divisor; + opt.depth = qdisc->sfq.depth; + + NLA_PUT(msg, TCA_OPTIONS, sizeof(opt), &opt); + } else if (nm_streq(qdisc->kind, "tbf")) { + struct tc_tbf_qopt opt = {}; + + if (!(tc_options = nla_nest_start(msg, TCA_OPTIONS))) + goto nla_put_failure; + + opt.rate.rate = (qdisc->tbf.rate >= (1ULL << 32)) ? ~0U : (guint32) qdisc->tbf.rate; + if (qdisc->tbf.limit) + opt.limit = qdisc->tbf.limit; + else if (qdisc->tbf.latency) { + opt.limit = qdisc->tbf.rate * (double) qdisc->tbf.latency / PSCHED_TIME_UNITS_PER_SEC + + qdisc->tbf.burst; + } + + NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opt), &opt); + if (qdisc->tbf.rate >= (1ULL << 32)) + NLA_PUT_U64(msg, TCA_TBF_RATE64, qdisc->tbf.rate); + NLA_PUT_U32(msg, TCA_TBF_BURST, qdisc->tbf.burst); + + nla_nest_end(msg, tc_options); + } else if (nm_streq(qdisc->kind, "prio")) { + struct tc_prio_qopt opt = {3, {1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}}; + + NLA_PUT(msg, TCA_OPTIONS, sizeof(opt), &opt); + } else { + if (!(tc_options = nla_nest_start(msg, TCA_OPTIONS))) + goto nla_put_failure; + + if (nm_streq(qdisc->kind, "fq_codel")) { + if (qdisc->fq_codel.limit) + NLA_PUT_U32(msg, TCA_FQ_CODEL_LIMIT, qdisc->fq_codel.limit); + if (qdisc->fq_codel.flows) + NLA_PUT_U32(msg, TCA_FQ_CODEL_FLOWS, qdisc->fq_codel.flows); + if (qdisc->fq_codel.target) + NLA_PUT_U32(msg, TCA_FQ_CODEL_TARGET, qdisc->fq_codel.target); + if (qdisc->fq_codel.interval) + NLA_PUT_U32(msg, TCA_FQ_CODEL_INTERVAL, qdisc->fq_codel.interval); + if (qdisc->fq_codel.quantum) + NLA_PUT_U32(msg, TCA_FQ_CODEL_QUANTUM, qdisc->fq_codel.quantum); + if (qdisc->fq_codel.ce_threshold != NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED) + NLA_PUT_U32(msg, TCA_FQ_CODEL_CE_THRESHOLD, qdisc->fq_codel.ce_threshold); + if (qdisc->fq_codel.memory_limit != NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET) + NLA_PUT_U32(msg, TCA_FQ_CODEL_MEMORY_LIMIT, qdisc->fq_codel.memory_limit); + if (qdisc->fq_codel.ecn) + NLA_PUT_U32(msg, TCA_FQ_CODEL_ECN, qdisc->fq_codel.ecn); + } + + nla_nest_end(msg, tc_options); + } + + return g_steal_pointer(&msg); nla_put_failure: - g_return_val_if_reached (NULL); + g_return_val_if_reached(NULL); } static struct nl_msg * -_nl_msg_new_tfilter (int nlmsg_type, - int nlmsg_flags, - const NMPlatformTfilter *tfilter) +_nl_msg_new_tfilter(int nlmsg_type, int nlmsg_flags, const NMPlatformTfilter *tfilter) { - nm_auto_nlmsg struct nl_msg *msg = NULL; - struct nlattr *tc_options; - struct nlattr *act_tab; - const struct tcmsg tcm = { - .tcm_family = tfilter->addr_family, - .tcm_ifindex = tfilter->ifindex, - .tcm_handle = tfilter->handle, - .tcm_parent = tfilter->parent, - .tcm_info = tfilter->info, - }; + nm_auto_nlmsg struct nl_msg *msg = NULL; + struct nlattr * tc_options; + struct nlattr * act_tab; + const struct tcmsg tcm = { + .tcm_family = tfilter->addr_family, + .tcm_ifindex = tfilter->ifindex, + .tcm_handle = tfilter->handle, + .tcm_parent = tfilter->parent, + .tcm_info = tfilter->info, + }; - msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags | NMP_NLM_FLAG_F_ECHO); + msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags | NMP_NLM_FLAG_F_ECHO); - if (nlmsg_append_struct (msg, &tcm) < 0) - goto nla_put_failure; + if (nlmsg_append_struct(msg, &tcm) < 0) + goto nla_put_failure; - NLA_PUT_STRING (msg, TCA_KIND, tfilter->kind); + NLA_PUT_STRING(msg, TCA_KIND, tfilter->kind); - if (!(tc_options = nla_nest_start (msg, TCA_OPTIONS))) - goto nla_put_failure; + if (!(tc_options = nla_nest_start(msg, TCA_OPTIONS))) + goto nla_put_failure; - if (!(act_tab = nla_nest_start (msg, TCA_OPTIONS))) // 3 TCA_ACT_KIND TCA_ACT_KIND - goto nla_put_failure; + if (!(act_tab = nla_nest_start(msg, TCA_OPTIONS))) // 3 TCA_ACT_KIND TCA_ACT_KIND + goto nla_put_failure; - if (tfilter->action.kind) { - const NMPlatformAction *action = &tfilter->action; - struct nlattr *prio; - struct nlattr *act_options; + if (tfilter->action.kind) { + const NMPlatformAction *action = &tfilter->action; + struct nlattr * prio; + struct nlattr * act_options; - if (!(prio = nla_nest_start (msg, 1 /* priority */))) - goto nla_put_failure; + if (!(prio = nla_nest_start(msg, 1 /* priority */))) + goto nla_put_failure; - NLA_PUT_STRING (msg, TCA_ACT_KIND, action->kind); + NLA_PUT_STRING(msg, TCA_ACT_KIND, action->kind); - if (nm_streq (action->kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { - const NMPlatformActionSimple *simple = &action->simple; - struct tc_defact sel = { 0, }; + if (nm_streq(action->kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { + const NMPlatformActionSimple *simple = &action->simple; + struct tc_defact sel = { + 0, + }; - if (!(act_options = nla_nest_start (msg, TCA_ACT_OPTIONS))) - goto nla_put_failure; + if (!(act_options = nla_nest_start(msg, TCA_ACT_OPTIONS))) + goto nla_put_failure; - NLA_PUT (msg, TCA_DEF_PARMS, sizeof (sel), &sel); - NLA_PUT (msg, TCA_DEF_DATA, sizeof (simple->sdata), simple->sdata); + NLA_PUT(msg, TCA_DEF_PARMS, sizeof(sel), &sel); + NLA_PUT(msg, TCA_DEF_DATA, sizeof(simple->sdata), simple->sdata); - nla_nest_end (msg, act_options); + nla_nest_end(msg, act_options); - } else if (nm_streq (action->kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { - const NMPlatformActionMirred *mirred = &action->mirred; - struct tc_mirred sel = { 0, }; + } else if (nm_streq(action->kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { + const NMPlatformActionMirred *mirred = &action->mirred; + struct tc_mirred sel = { + 0, + }; - if (!(act_options = nla_nest_start (msg, TCA_ACT_OPTIONS))) - goto nla_put_failure; + if (!(act_options = nla_nest_start(msg, TCA_ACT_OPTIONS))) + goto nla_put_failure; - if (mirred->egress && mirred->redirect) - sel.eaction = TCA_EGRESS_REDIR; - else if (mirred->egress && mirred->mirror) - sel.eaction = TCA_EGRESS_MIRROR; - else if (mirred->ingress && mirred->redirect) - sel.eaction = TCA_INGRESS_REDIR; - else if (mirred->ingress && mirred->mirror) - sel.eaction = TCA_INGRESS_MIRROR; - sel.ifindex = mirred->ifindex; + if (mirred->egress && mirred->redirect) + sel.eaction = TCA_EGRESS_REDIR; + else if (mirred->egress && mirred->mirror) + sel.eaction = TCA_EGRESS_MIRROR; + else if (mirred->ingress && mirred->redirect) + sel.eaction = TCA_INGRESS_REDIR; + else if (mirred->ingress && mirred->mirror) + sel.eaction = TCA_INGRESS_MIRROR; + sel.ifindex = mirred->ifindex; - NLA_PUT (msg, TCA_MIRRED_PARMS, sizeof (sel), &sel); + NLA_PUT(msg, TCA_MIRRED_PARMS, sizeof(sel), &sel); - nla_nest_end (msg, act_options); - } + nla_nest_end(msg, act_options); + } - nla_nest_end (msg, prio); - } + nla_nest_end(msg, prio); + } - nla_nest_end (msg, tc_options); + nla_nest_end(msg, tc_options); - nla_nest_end (msg, act_tab); + nla_nest_end(msg, act_tab); - return g_steal_pointer (&msg); + return g_steal_pointer(&msg); nla_put_failure: - g_return_val_if_reached (NULL); + g_return_val_if_reached(NULL); } /*****************************************************************************/ static struct nl_sock * -_genl_sock (NMLinuxPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - - return priv->genl; -} - -#define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ - G_STMT_START { \ - const char *const _pathid = (pathid); \ - const int _dirfd = (dirfd); \ - const char *const _path = (path); \ - \ - nm_assert (_path && _path[0]); \ - g_assert (!strstr (_path, "/../")); \ - if (_dirfd < 0) { \ - nm_assert (!_pathid); \ - nm_assert (_path[0] == '/'); \ - nm_assert ( g_str_has_prefix (_path, "/proc/sys/") \ - || g_str_has_prefix (_path, "/sys/") \ - || g_str_has_prefix (_path, "/proc/net")); \ - } else { \ - nm_assert (_pathid && _pathid[0] && _pathid[0] != '/'); \ - nm_assert (_path[0] != '/'); \ - } \ - } G_STMT_END +_genl_sock(NMLinuxPlatform *platform) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + + return priv->genl; +} + +#define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ + G_STMT_START \ + { \ + const char *const _pathid = (pathid); \ + const int _dirfd = (dirfd); \ + const char *const _path = (path); \ + \ + nm_assert(_path &&_path[0]); \ + g_assert(!strstr(_path, "/../")); \ + if (_dirfd < 0) { \ + nm_assert(!_pathid); \ + nm_assert(_path[0] == '/'); \ + nm_assert(g_str_has_prefix(_path, "/proc/sys/") || g_str_has_prefix(_path, "/sys/") \ + || g_str_has_prefix(_path, "/proc/net")); \ + } else { \ + nm_assert(_pathid &&_pathid[0] && _pathid[0] != '/'); \ + nm_assert(_path[0] != '/'); \ + } \ + } \ + G_STMT_END /*****************************************************************************/ @@ -4915,154 +5092,179 @@ _genl_sock (NMLinuxPlatform *platform) #define NM_THREAD_SAFE_ON_MAIN_THREAD 0 static void -_log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value) -{ - GError *error = NULL; - gs_free char *contents = NULL; - gs_free char *value_escaped = g_strescape (value, NULL); - - if (!nm_utils_file_get_contents (dirfd, - path, - 1*1024*1024, - NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, - &contents, - NULL, - NULL, - &error)) { - _LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", pathid ?: path, value_escaped, error->message); - g_clear_error (&error); - return; - } - - g_strstrip (contents); - if (nm_streq (contents, value)) - _LOGD ("sysctl: setting '%s' to '%s' (current value is identical)", pathid ?: path, value_escaped); - else { - gs_free char *contents_escaped = g_strescape (contents, NULL); - - _LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", pathid ?: path, value_escaped, contents_escaped); - } -} - -#define _log_dbg_sysctl_set(platform, pathid, dirfd, path, value) \ - G_STMT_START { \ - if (_LOGD_ENABLED ()) { \ - _log_dbg_sysctl_set_impl (platform, pathid, dirfd, path, value); \ - } \ - } G_STMT_END +_log_dbg_sysctl_set_impl(NMPlatform *platform, + const char *pathid, + int dirfd, + const char *path, + const char *value) +{ + GError * error = NULL; + gs_free char *contents = NULL; + gs_free char *value_escaped = g_strescape(value, NULL); + + if (!nm_utils_file_get_contents(dirfd, + path, + 1 * 1024 * 1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &contents, + NULL, + NULL, + &error)) { + _LOGD("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", + pathid ?: path, + value_escaped, + error->message); + g_clear_error(&error); + return; + } + + g_strstrip(contents); + if (nm_streq(contents, value)) + _LOGD("sysctl: setting '%s' to '%s' (current value is identical)", + pathid ?: path, + value_escaped); + else { + gs_free char *contents_escaped = g_strescape(contents, NULL); + + _LOGD("sysctl: setting '%s' to '%s' (current value is '%s')", + pathid ?: path, + value_escaped, + contents_escaped); + } +} + +#define _log_dbg_sysctl_set(platform, pathid, dirfd, path, value) \ + G_STMT_START \ + { \ + if (_LOGD_ENABLED()) { \ + _log_dbg_sysctl_set_impl(platform, pathid, dirfd, path, value); \ + } \ + } \ + G_STMT_END static gboolean -sysctl_set_internal (NMPlatform *platform, - const char *pathid, - int dirfd, - const char *path, - const char *value) -{ - int fd, tries; - gssize nwrote; - gssize len; - char *actual; - gs_free char *actual_free = NULL; - int errsv; - - if (dirfd < 0) { - pathid = path; - - fd = open (path, O_WRONLY | O_TRUNC | O_CLOEXEC); - if (fd == -1) { - errsv = errno; - if (errsv == ENOENT) { - _LOGD ("sysctl: failed to open '%s': (%d) %s", - pathid, errsv, nm_strerror_native (errsv)); - } else { - _LOGE ("sysctl: failed to open '%s': (%d) %s", - pathid, errsv, nm_strerror_native (errsv)); - } - errno = errsv; - return FALSE; - } - } else { - fd = openat (dirfd, path, O_WRONLY | O_TRUNC | O_CLOEXEC); - if (fd == -1) { - errsv = errno; - if (errsv == ENOENT) { - _LOGD ("sysctl: failed to openat '%s': (%d) %s", - pathid, errsv, nm_strerror_native (errsv)); - } else { - _LOGE ("sysctl: failed to openat '%s': (%d) %s", - pathid, errsv, nm_strerror_native (errsv)); - } - errno = errsv; - return FALSE; - } - } - - _log_dbg_sysctl_set (platform, pathid, dirfd, path, value); - - /* Most sysfs and sysctl options don't care about a trailing LF, while some - * (like infiniband) do. So always add the LF. Also, neither sysfs nor - * sysctl support partial writes so the LF must be added to the string we're - * about to write. - */ - len = strlen (value) + 1; - nm_assert (len > 0); - if (len > 512) - actual = actual_free = g_malloc (len + 1); - else - actual = g_alloca (len + 1); - memcpy (actual, value, len - 1); - actual[len - 1] = '\n'; - actual[len] = '\0'; - - /* Try to write the entire value three times if a partial write occurs */ - errsv = 0; - for (tries = 0, nwrote = 0; tries < 3 && nwrote < len - 1; tries++) { - nwrote = write (fd, actual, len); - if (nwrote == -1) { - errsv = errno; - if (errsv == EINTR) { - _LOGD ("sysctl: interrupted, will try again"); - continue; - } - break; - } - } - if (nwrote == -1) { - NMLogLevel level = LOGL_ERR; - - if (errsv == EEXIST) { - level = LOGL_DEBUG; - } else if ( errsv == EINVAL - && nm_utils_sysctl_ip_conf_is_path (AF_INET6, path, NULL, "mtu")) { - /* setting the MTU can fail under regular conditions. Suppress - * logging a warning. */ - level = LOGL_DEBUG; - } - - _NMLOG (level, "sysctl: failed to set '%s' to '%s': (%d) %s", - path, value, errsv, nm_strerror_native (errsv)); - } else if (nwrote < len - 1) { - _LOGE ("sysctl: failed to set '%s' to '%s' after three attempts", - path, value); - } - - if (nwrote < len - 1) { - if (nm_close (fd) != 0) { - if (errsv != 0) - errno = errsv; - } else if (errsv != 0) - errno = errsv; - else - errno = EIO; - return FALSE; - } - if (nm_close (fd) != 0) { - /* errno is already properly set. */ - return FALSE; - } - - /* success. errno is undefined (no need to set). */ - return TRUE; +sysctl_set_internal(NMPlatform *platform, + const char *pathid, + int dirfd, + const char *path, + const char *value) +{ + int fd, tries; + gssize nwrote; + gssize len; + char * actual; + gs_free char *actual_free = NULL; + int errsv; + + if (dirfd < 0) { + pathid = path; + + fd = open(path, O_WRONLY | O_TRUNC | O_CLOEXEC); + if (fd == -1) { + errsv = errno; + if (errsv == ENOENT) { + _LOGD("sysctl: failed to open '%s': (%d) %s", + pathid, + errsv, + nm_strerror_native(errsv)); + } else { + _LOGE("sysctl: failed to open '%s': (%d) %s", + pathid, + errsv, + nm_strerror_native(errsv)); + } + errno = errsv; + return FALSE; + } + } else { + fd = openat(dirfd, path, O_WRONLY | O_TRUNC | O_CLOEXEC); + if (fd == -1) { + errsv = errno; + if (errsv == ENOENT) { + _LOGD("sysctl: failed to openat '%s': (%d) %s", + pathid, + errsv, + nm_strerror_native(errsv)); + } else { + _LOGE("sysctl: failed to openat '%s': (%d) %s", + pathid, + errsv, + nm_strerror_native(errsv)); + } + errno = errsv; + return FALSE; + } + } + + _log_dbg_sysctl_set(platform, pathid, dirfd, path, value); + + /* Most sysfs and sysctl options don't care about a trailing LF, while some + * (like infiniband) do. So always add the LF. Also, neither sysfs nor + * sysctl support partial writes so the LF must be added to the string we're + * about to write. + */ + len = strlen(value) + 1; + nm_assert(len > 0); + if (len > 512) + actual = actual_free = g_malloc(len + 1); + else + actual = g_alloca(len + 1); + memcpy(actual, value, len - 1); + actual[len - 1] = '\n'; + actual[len] = '\0'; + + /* Try to write the entire value three times if a partial write occurs */ + errsv = 0; + for (tries = 0, nwrote = 0; tries < 3 && nwrote < len - 1; tries++) { + nwrote = write(fd, actual, len); + if (nwrote == -1) { + errsv = errno; + if (errsv == EINTR) { + _LOGD("sysctl: interrupted, will try again"); + continue; + } + break; + } + } + if (nwrote == -1) { + NMLogLevel level = LOGL_ERR; + + if (errsv == EEXIST) { + level = LOGL_DEBUG; + } else if (errsv == EINVAL + && nm_utils_sysctl_ip_conf_is_path(AF_INET6, path, NULL, "mtu")) { + /* setting the MTU can fail under regular conditions. Suppress + * logging a warning. */ + level = LOGL_DEBUG; + } + + _NMLOG(level, + "sysctl: failed to set '%s' to '%s': (%d) %s", + path, + value, + errsv, + nm_strerror_native(errsv)); + } else if (nwrote < len - 1) { + _LOGE("sysctl: failed to set '%s' to '%s' after three attempts", path, value); + } + + if (nwrote < len - 1) { + if (nm_close(fd) != 0) { + if (errsv != 0) + errno = errsv; + } else if (errsv != 0) + errno = errsv; + else + errno = EIO; + return FALSE; + } + if (nm_close(fd) != 0) { + /* errno is already properly set. */ + return FALSE; + } + + /* success. errno is undefined (no need to set). */ + return TRUE; } #undef NM_THREAD_SAFE_ON_MAIN_THREAD @@ -5071,1131 +5273,1196 @@ sysctl_set_internal (NMPlatform *platform, /*****************************************************************************/ static gboolean -sysctl_set (NMPlatform *platform, - const char *pathid, - int dirfd, - const char *path, - const char *value) +sysctl_set(NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value) { - nm_auto_pop_netns NMPNetns *netns = NULL; + nm_auto_pop_netns NMPNetns *netns = NULL; - g_return_val_if_fail (path, FALSE); - g_return_val_if_fail (value, FALSE); + g_return_val_if_fail(path, FALSE); + g_return_val_if_fail(value, FALSE); - ASSERT_SYSCTL_ARGS (pathid, dirfd, path); + ASSERT_SYSCTL_ARGS(pathid, dirfd, path); - if ( dirfd < 0 - && !nm_platform_netns_push (platform, &netns)) { - errno = ENETDOWN; - return FALSE; - } + if (dirfd < 0 && !nm_platform_netns_push(platform, &netns)) { + errno = ENETDOWN; + return FALSE; + } - return sysctl_set_internal (platform, pathid, dirfd, path, value); + return sysctl_set_internal(platform, pathid, dirfd, path, value); } typedef struct { - NMPlatform *platform; - char *pathid; - int dirfd; - char *path; - char **values; - GCancellable *cancellable; - NMPlatformAsyncCallback callback; - gpointer callback_data; + NMPlatform * platform; + char * pathid; + int dirfd; + char * path; + char ** values; + GCancellable * cancellable; + NMPlatformAsyncCallback callback; + gpointer callback_data; } SysctlAsyncInfo; static void -sysctl_async_info_free (SysctlAsyncInfo *info) +sysctl_async_info_free(SysctlAsyncInfo *info) { - g_object_unref (info->platform); - g_free (info->pathid); - if (info->dirfd >= 0) - nm_close (info->dirfd); - g_free (info->path); - g_strfreev (info->values); - g_object_unref (info->cancellable); - g_slice_free (SysctlAsyncInfo, info); + g_object_unref(info->platform); + g_free(info->pathid); + if (info->dirfd >= 0) + nm_close(info->dirfd); + g_free(info->path); + g_strfreev(info->values); + g_object_unref(info->cancellable); + g_slice_free(SysctlAsyncInfo, info); } static void -sysctl_async_cb (GObject *object, - GAsyncResult *res, - gpointer user_data) +sysctl_async_cb(GObject *object, GAsyncResult *res, gpointer user_data) { - NMPlatform *platform; - GTask *task = G_TASK (res); - SysctlAsyncInfo *info; - gs_free_error GError *error = NULL; - gs_free char *values_str = NULL; + NMPlatform * platform; + GTask * task = G_TASK(res); + SysctlAsyncInfo *info; + gs_free_error GError *error = NULL; + gs_free char * values_str = NULL; - info = g_task_get_task_data (task); + info = g_task_get_task_data(task); - if (g_task_propagate_boolean (task, &error)) { - platform = info->platform; - _LOGD ("sysctl: successfully set-async '%s' to values '%s'", - info->pathid ?: info->path, - (values_str = g_strjoinv (", ", info->values))); - } + if (g_task_propagate_boolean(task, &error)) { + platform = info->platform; + _LOGD("sysctl: successfully set-async '%s' to values '%s'", + info->pathid ?: info->path, + (values_str = g_strjoinv(", ", info->values))); + } - if (info->callback) - info->callback (error, info->callback_data); + if (info->callback) + info->callback(error, info->callback_data); } static void -sysctl_async_thread_fn (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - SysctlAsyncInfo *info = task_data; - GError *error = NULL; - char **value; - - if (g_task_return_error_if_cancelled (task)) - return; - - if ( info->dirfd < 0 - && !nm_platform_netns_push (info->platform, &netns)) { - g_set_error_literal (&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "sysctl: failed changing namespace"); - g_task_return_error (task, error); - return; - } - - for (value = info->values; *value; value++) { - if (!sysctl_set_internal (info->platform, - info->pathid, - info->dirfd, - info->path, - *value)) { - g_set_error (&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "sysctl: failed setting '%s' to value '%s': %s", - info->pathid ?: info->path, - *value, - nm_strerror_native (errno)); - g_task_return_error (task, error); - return; - } - if (g_task_return_error_if_cancelled (task)) - return; - } - g_task_return_boolean (task, TRUE); +sysctl_async_thread_fn(GTask * task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + nm_auto_pop_netns NMPNetns *netns = NULL; + SysctlAsyncInfo * info = task_data; + GError * error = NULL; + char ** value; + + if (g_task_return_error_if_cancelled(task)) + return; + + if (info->dirfd < 0 && !nm_platform_netns_push(info->platform, &netns)) { + g_set_error_literal(&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "sysctl: failed changing namespace"); + g_task_return_error(task, error); + return; + } + + for (value = info->values; *value; value++) { + if (!sysctl_set_internal(info->platform, info->pathid, info->dirfd, info->path, *value)) { + g_set_error(&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "sysctl: failed setting '%s' to value '%s': %s", + info->pathid ?: info->path, + *value, + nm_strerror_native(errno)); + g_task_return_error(task, error); + return; + } + if (g_task_return_error_if_cancelled(task)) + return; + } + g_task_return_boolean(task, TRUE); } static void -sysctl_set_async_return_idle (gpointer user_data, - GCancellable *cancellable) +sysctl_set_async_return_idle(gpointer user_data, GCancellable *cancellable) { - gs_unref_object NMPlatform *platform = NULL; - gs_free_error GError *cancelled_error = NULL; - gs_free_error GError *error = NULL; - NMPlatformAsyncCallback callback; - gpointer callback_data; + gs_unref_object NMPlatform *platform = NULL; + gs_free_error GError *cancelled_error = NULL; + gs_free_error GError * error = NULL; + NMPlatformAsyncCallback callback; + gpointer callback_data; - nm_utils_user_data_unpack (user_data, &platform, &callback, &callback_data, &error); - g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error); - callback (cancelled_error ?: error, callback_data); + nm_utils_user_data_unpack(user_data, &platform, &callback, &callback_data, &error); + g_cancellable_set_error_if_cancelled(cancellable, &cancelled_error); + callback(cancelled_error ?: error, callback_data); } static void -sysctl_set_async (NMPlatform *platform, - const char *pathid, - int dirfd, - const char *path, - const char *const *values, - NMPlatformAsyncCallback callback, - gpointer data, - GCancellable *cancellable) -{ - SysctlAsyncInfo *info; - GTask *task; - int dirfd_dup, errsv; - gpointer packed; - GError *error = NULL; - - g_return_if_fail (platform); - g_return_if_fail (path); - g_return_if_fail (values && values[0]); - g_return_if_fail (cancellable); - g_return_if_fail (!data || callback); - - ASSERT_SYSCTL_ARGS (pathid, dirfd, path); - - if (dirfd >= 0) { - dirfd_dup = fcntl (dirfd, F_DUPFD_CLOEXEC, 0); - if (dirfd_dup < 0) { - if (!callback) - return; - errsv = errno; - g_set_error (&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "sysctl: failure duplicating directory fd: %s", - nm_strerror_native (errsv)); - packed = nm_utils_user_data_pack (g_object_ref (platform), - callback, - data, - error); - nm_utils_invoke_on_idle (cancellable, - sysctl_set_async_return_idle, - packed); - return; - } - } else - dirfd_dup = -1; - - info = g_slice_new0 (SysctlAsyncInfo); - info->platform = g_object_ref (platform); - info->pathid = g_strdup (pathid); - info->dirfd = dirfd_dup; - info->path = g_strdup (path); - info->values = g_strdupv ((char **) values); - info->callback = callback; - info->callback_data = data; - info->cancellable = g_object_ref (cancellable); - - task = g_task_new (platform, cancellable, sysctl_async_cb, NULL); - g_task_set_task_data (task, info, (GDestroyNotify) sysctl_async_info_free); - g_task_set_return_on_cancel (task, FALSE); - g_task_run_in_thread (task, sysctl_async_thread_fn); - g_object_unref (task); +sysctl_set_async(NMPlatform * platform, + const char * pathid, + int dirfd, + const char * path, + const char *const * values, + NMPlatformAsyncCallback callback, + gpointer data, + GCancellable * cancellable) +{ + SysctlAsyncInfo *info; + GTask * task; + int dirfd_dup, errsv; + gpointer packed; + GError * error = NULL; + + g_return_if_fail(platform); + g_return_if_fail(path); + g_return_if_fail(values && values[0]); + g_return_if_fail(cancellable); + g_return_if_fail(!data || callback); + + ASSERT_SYSCTL_ARGS(pathid, dirfd, path); + + if (dirfd >= 0) { + dirfd_dup = fcntl(dirfd, F_DUPFD_CLOEXEC, 0); + if (dirfd_dup < 0) { + if (!callback) + return; + errsv = errno; + g_set_error(&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "sysctl: failure duplicating directory fd: %s", + nm_strerror_native(errsv)); + packed = nm_utils_user_data_pack(g_object_ref(platform), callback, data, error); + nm_utils_invoke_on_idle(cancellable, sysctl_set_async_return_idle, packed); + return; + } + } else + dirfd_dup = -1; + + info = g_slice_new0(SysctlAsyncInfo); + info->platform = g_object_ref(platform); + info->pathid = g_strdup(pathid); + info->dirfd = dirfd_dup; + info->path = g_strdup(path); + info->values = g_strdupv((char **) values); + info->callback = callback; + info->callback_data = data; + info->cancellable = g_object_ref(cancellable); + + task = g_task_new(platform, cancellable, sysctl_async_cb, NULL); + g_task_set_task_data(task, info, (GDestroyNotify) sysctl_async_info_free); + g_task_set_return_on_cancel(task, FALSE); + g_task_run_in_thread(task, sysctl_async_thread_fn); + g_object_unref(task); } static GSList *sysctl_clear_cache_list; void -_nm_logging_clear_platform_logging_cache (void) +_nm_logging_clear_platform_logging_cache(void) { - while (sysctl_clear_cache_list) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (sysctl_clear_cache_list->data); + while (sysctl_clear_cache_list) { + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(sysctl_clear_cache_list->data); - sysctl_clear_cache_list = g_slist_delete_link (sysctl_clear_cache_list, sysctl_clear_cache_list); + sysctl_clear_cache_list = + g_slist_delete_link(sysctl_clear_cache_list, sysctl_clear_cache_list); - g_hash_table_destroy (priv->sysctl_get_prev_values); - priv->sysctl_get_prev_values = NULL; - } + g_hash_table_destroy(priv->sysctl_get_prev_values); + priv->sysctl_get_prev_values = NULL; + } } typedef struct { - const char *path; - CList lst; - char *value; - char path_data[]; + const char *path; + CList lst; + char * value; + char path_data[]; } SysctlCacheEntry; static void -sysctl_cache_entry_free (SysctlCacheEntry *entry) +sysctl_cache_entry_free(SysctlCacheEntry *entry) { - c_list_unlink_stale (&entry->lst); - g_free (entry->value); - g_free (entry); + c_list_unlink_stale(&entry->lst); + g_free(entry->value); + g_free(entry); } static void -_log_dbg_sysctl_get_impl (NMPlatform *platform, const char *pathid, const char *contents) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - SysctlCacheEntry *entry = NULL; - - if (!priv->sysctl_get_prev_values) { - sysctl_clear_cache_list = g_slist_prepend (sysctl_clear_cache_list, platform); - c_list_init (&priv->sysctl_list); - priv->sysctl_get_prev_values = g_hash_table_new_full (nm_pstr_hash, - nm_pstr_equal, - (GDestroyNotify) sysctl_cache_entry_free, - NULL); - } else - entry = g_hash_table_lookup (priv->sysctl_get_prev_values, &pathid); - - if (entry) { - if (!nm_streq (entry->value, contents)) { - gs_free char *contents_escaped = g_strescape (contents, NULL); - gs_free char *prev_value_escaped = g_strescape (entry->value, NULL); - - _LOGD ("sysctl: reading '%s': '%s' (changed from '%s' on last read)", pathid, contents_escaped, prev_value_escaped); - g_free (entry->value); - entry->value = g_strdup (contents); - } - nm_c_list_move_front (&priv->sysctl_list, &entry->lst); - } else { - gs_free char *contents_escaped = g_strescape (contents, NULL); - SysctlCacheEntry *old; - size_t len; - - len = strlen (pathid); - entry = g_malloc (sizeof (SysctlCacheEntry) + len + 1); - entry->value = g_strdup (contents); - entry->path = entry->path_data; - memcpy (entry->path_data, pathid, len + 1); - - /* Remove oldest entry when the cache becomes too big */ - if (g_hash_table_size (priv->sysctl_get_prev_values) > 1000) { - old = c_list_last_entry (&priv->sysctl_list, SysctlCacheEntry, lst); - g_hash_table_remove (priv->sysctl_get_prev_values, old); - } - - _LOGD ("sysctl: reading '%s': '%s'", pathid, contents_escaped); - - g_hash_table_add (priv->sysctl_get_prev_values, entry); - c_list_link_front (&priv->sysctl_list, &entry->lst); - } -} - -#define _log_dbg_sysctl_get(platform, pathid, contents) \ - G_STMT_START { \ - if (_LOGD_ENABLED ()) \ - _log_dbg_sysctl_get_impl (platform, pathid, contents); \ - } G_STMT_END +_log_dbg_sysctl_get_impl(NMPlatform *platform, const char *pathid, const char *contents) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + SysctlCacheEntry * entry = NULL; + + if (!priv->sysctl_get_prev_values) { + sysctl_clear_cache_list = g_slist_prepend(sysctl_clear_cache_list, platform); + c_list_init(&priv->sysctl_list); + priv->sysctl_get_prev_values = + g_hash_table_new_full(nm_pstr_hash, + nm_pstr_equal, + (GDestroyNotify) sysctl_cache_entry_free, + NULL); + } else + entry = g_hash_table_lookup(priv->sysctl_get_prev_values, &pathid); + + if (entry) { + if (!nm_streq(entry->value, contents)) { + gs_free char *contents_escaped = g_strescape(contents, NULL); + gs_free char *prev_value_escaped = g_strescape(entry->value, NULL); + + _LOGD("sysctl: reading '%s': '%s' (changed from '%s' on last read)", + pathid, + contents_escaped, + prev_value_escaped); + g_free(entry->value); + entry->value = g_strdup(contents); + } + nm_c_list_move_front(&priv->sysctl_list, &entry->lst); + } else { + gs_free char * contents_escaped = g_strescape(contents, NULL); + SysctlCacheEntry *old; + size_t len; + + len = strlen(pathid); + entry = g_malloc(sizeof(SysctlCacheEntry) + len + 1); + entry->value = g_strdup(contents); + entry->path = entry->path_data; + memcpy(entry->path_data, pathid, len + 1); + + /* Remove oldest entry when the cache becomes too big */ + if (g_hash_table_size(priv->sysctl_get_prev_values) > 1000) { + old = c_list_last_entry(&priv->sysctl_list, SysctlCacheEntry, lst); + g_hash_table_remove(priv->sysctl_get_prev_values, old); + } + + _LOGD("sysctl: reading '%s': '%s'", pathid, contents_escaped); + + g_hash_table_add(priv->sysctl_get_prev_values, entry); + c_list_link_front(&priv->sysctl_list, &entry->lst); + } +} + +#define _log_dbg_sysctl_get(platform, pathid, contents) \ + G_STMT_START \ + { \ + if (_LOGD_ENABLED()) \ + _log_dbg_sysctl_get_impl(platform, pathid, contents); \ + } \ + G_STMT_END static char * -sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *path) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - GError *error = NULL; - gs_free char *contents = NULL; - - ASSERT_SYSCTL_ARGS (pathid, dirfd, path); - - if (dirfd < 0) { - if (!nm_platform_netns_push (platform, &netns)) { - errno = EBUSY; - return NULL; - } - pathid = path; - } - - if (!nm_utils_file_get_contents (dirfd, - path, - 1*1024*1024, - NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, - &contents, - NULL, - NULL, - &error)) { - NMLogLevel log_level = LOGL_ERR; - int errsv = EBUSY; - - if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { - errsv = ENOENT; - log_level = LOGL_DEBUG; - } else if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NODEV) - || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_FAILED)) { - /* We assume FAILED means EOPNOTSUP and don't log a error message. */ - log_level = LOGL_DEBUG; - } - - _NMLOG (log_level, "error reading %s: %s", pathid, error->message); - g_clear_error (&error); - errno = errsv; - return NULL; - } - - g_strstrip (contents); - - _log_dbg_sysctl_get (platform, pathid, contents); - - /* errno is left undefined (as we don't return NULL). */ - return g_steal_pointer (&contents); +sysctl_get(NMPlatform *platform, const char *pathid, int dirfd, const char *path) +{ + nm_auto_pop_netns NMPNetns *netns = NULL; + GError * error = NULL; + gs_free char * contents = NULL; + + ASSERT_SYSCTL_ARGS(pathid, dirfd, path); + + if (dirfd < 0) { + if (!nm_platform_netns_push(platform, &netns)) { + errno = EBUSY; + return NULL; + } + pathid = path; + } + + if (!nm_utils_file_get_contents(dirfd, + path, + 1 * 1024 * 1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &contents, + NULL, + NULL, + &error)) { + NMLogLevel log_level = LOGL_ERR; + int errsv = EBUSY; + + if (g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + errsv = ENOENT; + log_level = LOGL_DEBUG; + } else if (g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NODEV) + || g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_FAILED)) { + /* We assume FAILED means EOPNOTSUP and don't log a error message. */ + log_level = LOGL_DEBUG; + } + + _NMLOG(log_level, "error reading %s: %s", pathid, error->message); + g_clear_error(&error); + errno = errsv; + return NULL; + } + + g_strstrip(contents); + + _log_dbg_sysctl_get(platform, pathid, contents); + + /* errno is left undefined (as we don't return NULL). */ + return g_steal_pointer(&contents); } /*****************************************************************************/ static void -process_events (NMPlatform *platform) +process_events(NMPlatform *platform) { - delayed_action_handle_all (platform, TRUE); + delayed_action_handle_all(platform, TRUE); } /*****************************************************************************/ static const RefreshAllInfo * -refresh_all_type_get_info (RefreshAllType refresh_all_type) -{ - static const RefreshAllInfo infos[] = { -#define R(_refresh_all_type, _obj_type, _addr_family) [_refresh_all_type] = { .obj_type = _obj_type, .addr_family = _addr_family, } - R (REFRESH_ALL_TYPE_LINKS, NMP_OBJECT_TYPE_LINK, AF_UNSPEC), - R (REFRESH_ALL_TYPE_IP4_ADDRESSES, NMP_OBJECT_TYPE_IP4_ADDRESS, AF_UNSPEC), - R (REFRESH_ALL_TYPE_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS, AF_UNSPEC), - R (REFRESH_ALL_TYPE_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE, AF_UNSPEC), - R (REFRESH_ALL_TYPE_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE, AF_UNSPEC), - R (REFRESH_ALL_TYPE_ROUTING_RULES_IP4, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET), - R (REFRESH_ALL_TYPE_ROUTING_RULES_IP6, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET6), - R (REFRESH_ALL_TYPE_QDISCS, NMP_OBJECT_TYPE_QDISC, AF_UNSPEC), - R (REFRESH_ALL_TYPE_TFILTERS, NMP_OBJECT_TYPE_TFILTER, AF_UNSPEC), +refresh_all_type_get_info(RefreshAllType refresh_all_type) +{ + static const RefreshAllInfo infos[] = { +#define R(_refresh_all_type, _obj_type, _addr_family) \ + [_refresh_all_type] = { \ + .obj_type = _obj_type, \ + .addr_family = _addr_family, \ + } + R(REFRESH_ALL_TYPE_LINKS, NMP_OBJECT_TYPE_LINK, AF_UNSPEC), + R(REFRESH_ALL_TYPE_IP4_ADDRESSES, NMP_OBJECT_TYPE_IP4_ADDRESS, AF_UNSPEC), + R(REFRESH_ALL_TYPE_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS, AF_UNSPEC), + R(REFRESH_ALL_TYPE_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE, AF_UNSPEC), + R(REFRESH_ALL_TYPE_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE, AF_UNSPEC), + R(REFRESH_ALL_TYPE_ROUTING_RULES_IP4, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET), + R(REFRESH_ALL_TYPE_ROUTING_RULES_IP6, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET6), + R(REFRESH_ALL_TYPE_QDISCS, NMP_OBJECT_TYPE_QDISC, AF_UNSPEC), + R(REFRESH_ALL_TYPE_TFILTERS, NMP_OBJECT_TYPE_TFILTER, AF_UNSPEC), #undef R - }; - - nm_assert (_NM_INT_NOT_NEGATIVE (refresh_all_type)); - nm_assert (refresh_all_type < G_N_ELEMENTS (infos)); - nm_assert (nmp_class_from_type (infos[refresh_all_type].obj_type)); - - return &infos[refresh_all_type]; -} - -static -NM_UTILS_LOOKUP_DEFINE (delayed_action_type_to_refresh_all_type, DelayedActionType, RefreshAllType, - NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (0), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, REFRESH_ALL_TYPE_LINKS), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, REFRESH_ALL_TYPE_IP4_ADDRESSES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, REFRESH_ALL_TYPE_IP6_ADDRESSES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, REFRESH_ALL_TYPE_IP4_ROUTES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, REFRESH_ALL_TYPE_IP6_ROUTES), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, REFRESH_ALL_TYPE_ROUTING_RULES_IP4), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, REFRESH_ALL_TYPE_ROUTING_RULES_IP6), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, REFRESH_ALL_TYPE_QDISCS), - NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, REFRESH_ALL_TYPE_TFILTERS), - NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (), -); + }; + + nm_assert(_NM_INT_NOT_NEGATIVE(refresh_all_type)); + nm_assert(refresh_all_type < G_N_ELEMENTS(infos)); + nm_assert(nmp_class_from_type(infos[refresh_all_type].obj_type)); + + return &infos[refresh_all_type]; +} + +static NM_UTILS_LOOKUP_DEFINE( + delayed_action_type_to_refresh_all_type, + DelayedActionType, + RefreshAllType, + NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(0), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, REFRESH_ALL_TYPE_LINKS), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, + REFRESH_ALL_TYPE_IP4_ADDRESSES), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, + REFRESH_ALL_TYPE_IP6_ADDRESSES), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, REFRESH_ALL_TYPE_IP4_ROUTES), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, REFRESH_ALL_TYPE_IP6_ROUTES), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, + REFRESH_ALL_TYPE_ROUTING_RULES_IP4), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, + REFRESH_ALL_TYPE_ROUTING_RULES_IP6), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, REFRESH_ALL_TYPE_QDISCS), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, REFRESH_ALL_TYPE_TFILTERS), + NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER(), ); static DelayedActionType -delayed_action_type_from_refresh_all_type (RefreshAllType refresh_all_type) +delayed_action_type_from_refresh_all_type(RefreshAllType refresh_all_type) { - DelayedActionType t; + DelayedActionType t; - nm_assert (refresh_all_type_get_info (refresh_all_type)); + nm_assert(refresh_all_type_get_info(refresh_all_type)); - t = (((DelayedActionType) 1) << refresh_all_type); + t = (((DelayedActionType) 1) << refresh_all_type); - nm_assert (refresh_all_type == delayed_action_type_to_refresh_all_type (t)); + nm_assert(refresh_all_type == delayed_action_type_to_refresh_all_type(t)); - return t; + return t; } static RefreshAllType -refresh_all_type_from_needle_object (const NMPObject *obj_needle) -{ - switch (NMP_OBJECT_GET_TYPE (obj_needle)) { - case NMP_OBJECT_TYPE_LINK: return REFRESH_ALL_TYPE_LINKS; - case NMP_OBJECT_TYPE_IP4_ADDRESS: return REFRESH_ALL_TYPE_IP4_ADDRESSES; - case NMP_OBJECT_TYPE_IP6_ADDRESS: return REFRESH_ALL_TYPE_IP6_ADDRESSES; - case NMP_OBJECT_TYPE_IP4_ROUTE: return REFRESH_ALL_TYPE_IP4_ROUTES; - case NMP_OBJECT_TYPE_IP6_ROUTE: return REFRESH_ALL_TYPE_IP6_ROUTES; - case NMP_OBJECT_TYPE_QDISC: return REFRESH_ALL_TYPE_QDISCS; - case NMP_OBJECT_TYPE_TFILTER: return REFRESH_ALL_TYPE_TFILTERS; - case NMP_OBJECT_TYPE_ROUTING_RULE: - switch (NMP_OBJECT_CAST_ROUTING_RULE (obj_needle)->addr_family) { - case AF_INET: return REFRESH_ALL_TYPE_ROUTING_RULES_IP4; - case AF_INET6: return REFRESH_ALL_TYPE_ROUTING_RULES_IP6; - } - nm_assert_not_reached (); - return 0; - default: - nm_assert_not_reached (); - return 0; - } +refresh_all_type_from_needle_object(const NMPObject *obj_needle) +{ + switch (NMP_OBJECT_GET_TYPE(obj_needle)) { + case NMP_OBJECT_TYPE_LINK: + return REFRESH_ALL_TYPE_LINKS; + case NMP_OBJECT_TYPE_IP4_ADDRESS: + return REFRESH_ALL_TYPE_IP4_ADDRESSES; + case NMP_OBJECT_TYPE_IP6_ADDRESS: + return REFRESH_ALL_TYPE_IP6_ADDRESSES; + case NMP_OBJECT_TYPE_IP4_ROUTE: + return REFRESH_ALL_TYPE_IP4_ROUTES; + case NMP_OBJECT_TYPE_IP6_ROUTE: + return REFRESH_ALL_TYPE_IP6_ROUTES; + case NMP_OBJECT_TYPE_QDISC: + return REFRESH_ALL_TYPE_QDISCS; + case NMP_OBJECT_TYPE_TFILTER: + return REFRESH_ALL_TYPE_TFILTERS; + case NMP_OBJECT_TYPE_ROUTING_RULE: + switch (NMP_OBJECT_CAST_ROUTING_RULE(obj_needle)->addr_family) { + case AF_INET: + return REFRESH_ALL_TYPE_ROUTING_RULES_IP4; + case AF_INET6: + return REFRESH_ALL_TYPE_ROUTING_RULES_IP6; + } + nm_assert_not_reached(); + return 0; + default: + nm_assert_not_reached(); + return 0; + } } static const NMPLookup * -refresh_all_type_init_lookup (RefreshAllType refresh_all_type, - NMPLookup *lookup) +refresh_all_type_init_lookup(RefreshAllType refresh_all_type, NMPLookup *lookup) { - const RefreshAllInfo *refresh_all_info; + const RefreshAllInfo *refresh_all_info; - nm_assert (lookup); + nm_assert(lookup); - refresh_all_info = refresh_all_type_get_info (refresh_all_type); + refresh_all_info = refresh_all_type_get_info(refresh_all_type); - nm_assert (refresh_all_info); + nm_assert(refresh_all_info); - if (NM_IN_SET (refresh_all_info->obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)) { - return nmp_lookup_init_object_by_addr_family (lookup, - refresh_all_info->obj_type, - refresh_all_info->addr_family); - } + if (NM_IN_SET(refresh_all_info->obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)) { + return nmp_lookup_init_object_by_addr_family(lookup, + refresh_all_info->obj_type, + refresh_all_info->addr_family); + } - /* not yet implemented. */ - nm_assert (refresh_all_info->addr_family == AF_UNSPEC); + /* not yet implemented. */ + nm_assert(refresh_all_info->addr_family == AF_UNSPEC); - return nmp_lookup_init_obj_type (lookup, - refresh_all_info->obj_type); + return nmp_lookup_init_obj_type(lookup, refresh_all_info->obj_type); } static DelayedActionType -delayed_action_refresh_from_needle_object (const NMPObject *obj_needle) -{ - return delayed_action_type_from_refresh_all_type (refresh_all_type_from_needle_object (obj_needle)); -} - -static -NM_UTILS_LOOKUP_STR_DEFINE (delayed_action_to_string, DelayedActionType, - NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, "refresh-all-links"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, "refresh-all-ip4-addresses"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, "refresh-all-ip6-addresses"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, "refresh-all-ip4-routes"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, "refresh-all-ip6-routes"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, "refresh-all-routing-rules-ip4"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, "refresh-all-routing-rules-ip6"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, "refresh-all-qdiscs"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, "refresh-all-tfilters"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_LINK, "refresh-link"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_MASTER_CONNECTED, "master-connected"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_READ_NETLINK, "read-netlink"), - NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, "wait-for-nl-response"), - NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_NONE), - NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_REFRESH_ALL), - NM_UTILS_LOOKUP_ITEM_IGNORE (DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL), - NM_UTILS_LOOKUP_ITEM_IGNORE (__DELAYED_ACTION_TYPE_MAX), -); +delayed_action_refresh_from_needle_object(const NMPObject *obj_needle) +{ + return delayed_action_type_from_refresh_all_type( + refresh_all_type_from_needle_object(obj_needle)); +} + +static NM_UTILS_LOOKUP_STR_DEFINE( + delayed_action_to_string, + DelayedActionType, + NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT("unknown"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, "refresh-all-links"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, + "refresh-all-ip4-addresses"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, + "refresh-all-ip6-addresses"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, "refresh-all-ip4-routes"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, "refresh-all-ip6-routes"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, + "refresh-all-routing-rules-ip4"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, + "refresh-all-routing-rules-ip6"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, "refresh-all-qdiscs"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, "refresh-all-tfilters"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_LINK, "refresh-link"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_MASTER_CONNECTED, "master-connected"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_READ_NETLINK, "read-netlink"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, "wait-for-nl-response"), + NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_NONE), + NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_REFRESH_ALL), + NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL), + NM_UTILS_LOOKUP_ITEM_IGNORE(__DELAYED_ACTION_TYPE_MAX), ); static const char * -delayed_action_to_string_full (DelayedActionType action_type, gpointer user_data, char *buf, gsize buf_size) -{ - char *buf0 = buf; - const DelayedActionWaitForNlResponseData *data; - - nm_utils_strbuf_append_str (&buf, &buf_size, delayed_action_to_string (action_type)); - switch (action_type) { - case DELAYED_ACTION_TYPE_MASTER_CONNECTED: - nm_utils_strbuf_append (&buf, &buf_size, " (master-ifindex %d)", GPOINTER_TO_INT (user_data)); - break; - case DELAYED_ACTION_TYPE_REFRESH_LINK: - nm_utils_strbuf_append (&buf, &buf_size, " (ifindex %d)", GPOINTER_TO_INT (user_data)); - break; - case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE: - data = user_data; - - if (data) { - gint64 timeout = data->timeout_abs_ns - nm_utils_get_monotonic_timestamp_nsec (); - char b[255]; - - nm_utils_strbuf_append (&buf, &buf_size, " (seq %u, timeout in %s%"G_GINT64_FORMAT".%09"G_GINT64_FORMAT", response-type %d%s%s)", - data->seq_number, - timeout < 0 ? "-" : "", - (timeout < 0 ? -timeout : timeout) / NM_UTILS_NSEC_PER_SEC, - (timeout < 0 ? -timeout : timeout) % NM_UTILS_NSEC_PER_SEC, - (int) data->response_type, - data->seq_result ? ", " : "", - data->seq_result ? wait_for_nl_response_to_string (data->seq_result, NULL, b, sizeof (b)) : ""); - } else - nm_utils_strbuf_append_str (&buf, &buf_size, " (any)"); - break; - default: - nm_assert (!user_data); - break; - } - return buf0; -} - -#define _LOGt_delayed_action(action_type, user_data, operation) \ - G_STMT_START { \ - char _buf[255]; \ - \ - _LOGt ("delayed-action: %s %s", \ - ""operation, \ - delayed_action_to_string_full (action_type, user_data, _buf, sizeof (_buf))); \ - } G_STMT_END +delayed_action_to_string_full(DelayedActionType action_type, + gpointer user_data, + char * buf, + gsize buf_size) +{ + char * buf0 = buf; + const DelayedActionWaitForNlResponseData *data; + + nm_utils_strbuf_append_str(&buf, &buf_size, delayed_action_to_string(action_type)); + switch (action_type) { + case DELAYED_ACTION_TYPE_MASTER_CONNECTED: + nm_utils_strbuf_append(&buf, &buf_size, " (master-ifindex %d)", GPOINTER_TO_INT(user_data)); + break; + case DELAYED_ACTION_TYPE_REFRESH_LINK: + nm_utils_strbuf_append(&buf, &buf_size, " (ifindex %d)", GPOINTER_TO_INT(user_data)); + break; + case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE: + data = user_data; + + if (data) { + gint64 timeout = data->timeout_abs_ns - nm_utils_get_monotonic_timestamp_nsec(); + char b[255]; + + nm_utils_strbuf_append( + &buf, + &buf_size, + " (seq %u, timeout in %s%" G_GINT64_FORMAT ".%09" G_GINT64_FORMAT + ", response-type %d%s%s)", + data->seq_number, + timeout < 0 ? "-" : "", + (timeout < 0 ? -timeout : timeout) / NM_UTILS_NSEC_PER_SEC, + (timeout < 0 ? -timeout : timeout) % NM_UTILS_NSEC_PER_SEC, + (int) data->response_type, + data->seq_result ? ", " : "", + data->seq_result + ? wait_for_nl_response_to_string(data->seq_result, NULL, b, sizeof(b)) + : ""); + } else + nm_utils_strbuf_append_str(&buf, &buf_size, " (any)"); + break; + default: + nm_assert(!user_data); + break; + } + return buf0; +} + +#define _LOGt_delayed_action(action_type, user_data, operation) \ + G_STMT_START \ + { \ + char _buf[255]; \ + \ + _LOGt("delayed-action: %s %s", \ + "" operation, \ + delayed_action_to_string_full(action_type, user_data, _buf, sizeof(_buf))); \ + } \ + G_STMT_END /*****************************************************************************/ static gboolean -delayed_action_refresh_all_in_progress (NMPlatform *platform, DelayedActionType action_type) +delayed_action_refresh_all_in_progress(NMPlatform *platform, DelayedActionType action_type) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - RefreshAllType refresh_all_type; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + RefreshAllType refresh_all_type; - nm_assert (nm_utils_is_power_of_two (action_type)); - nm_assert (NM_FLAGS_ANY (action_type, DELAYED_ACTION_TYPE_REFRESH_ALL)); - nm_assert (!NM_FLAGS_ANY (action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); + nm_assert(nm_utils_is_power_of_two(action_type)); + nm_assert(NM_FLAGS_ANY(action_type, DELAYED_ACTION_TYPE_REFRESH_ALL)); + nm_assert(!NM_FLAGS_ANY(action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); - if (NM_FLAGS_ANY (priv->delayed_action.flags, action_type)) - return TRUE; + if (NM_FLAGS_ANY(priv->delayed_action.flags, action_type)) + return TRUE; - refresh_all_type = delayed_action_type_to_refresh_all_type (action_type); - return (priv->delayed_action.refresh_all_in_progress[refresh_all_type] > 0); + refresh_all_type = delayed_action_type_to_refresh_all_type(action_type); + return (priv->delayed_action.refresh_all_in_progress[refresh_all_type] > 0); } static void -delayed_action_wait_for_nl_response_complete (NMPlatform *platform, - guint idx, - WaitForNlResponseResult seq_result) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - DelayedActionWaitForNlResponseData *data; - - nm_assert (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); - nm_assert (idx < priv->delayed_action.list_wait_for_nl_response->len); - nm_assert (seq_result); - - data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, idx); - - _LOGt_delayed_action (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, data, "complete"); - - if (priv->delayed_action.list_wait_for_nl_response->len <= 1) - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE; - if (data->out_seq_result) - *data->out_seq_result = seq_result; - switch (data->response_type) { - case DELAYED_ACTION_RESPONSE_TYPE_VOID: - break; - case DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS: - if (data->response.out_refresh_all_in_progress) { - nm_assert (*data->response.out_refresh_all_in_progress > 0); - *data->response.out_refresh_all_in_progress -= 1; - data->response.out_refresh_all_in_progress = NULL; - } - break; - case DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET: - if (data->response.out_route_get) { - nm_assert (!*data->response.out_route_get); - data->response.out_route_get = NULL; - } - break; - } - - g_array_remove_index_fast (priv->delayed_action.list_wait_for_nl_response, idx); +delayed_action_wait_for_nl_response_complete(NMPlatform * platform, + guint idx, + WaitForNlResponseResult seq_result) +{ + NMLinuxPlatformPrivate * priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + DelayedActionWaitForNlResponseData *data; + + nm_assert(NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); + nm_assert(idx < priv->delayed_action.list_wait_for_nl_response->len); + nm_assert(seq_result); + + data = &g_array_index(priv->delayed_action.list_wait_for_nl_response, + DelayedActionWaitForNlResponseData, + idx); + + _LOGt_delayed_action(DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, data, "complete"); + + if (priv->delayed_action.list_wait_for_nl_response->len <= 1) + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE; + if (data->out_seq_result) + *data->out_seq_result = seq_result; + switch (data->response_type) { + case DELAYED_ACTION_RESPONSE_TYPE_VOID: + break; + case DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS: + if (data->response.out_refresh_all_in_progress) { + nm_assert(*data->response.out_refresh_all_in_progress > 0); + *data->response.out_refresh_all_in_progress -= 1; + data->response.out_refresh_all_in_progress = NULL; + } + break; + case DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET: + if (data->response.out_route_get) { + nm_assert(!*data->response.out_route_get); + data->response.out_route_get = NULL; + } + break; + } + + g_array_remove_index_fast(priv->delayed_action.list_wait_for_nl_response, idx); } static void -delayed_action_wait_for_nl_response_complete_check (NMPlatform *platform, - WaitForNlResponseResult force_result, - guint32 *out_next_seq_number, - gint64 *out_next_timeout_abs_ns, - gint64 *p_now_ns) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - guint i; - guint32 next_seq_number = 0; - gint64 next_timeout_abs_ns = 0; - int now_ns = 0; - - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; ) { - const DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); - - if (data->seq_result) - delayed_action_wait_for_nl_response_complete (platform, i, data->seq_result); - else if ( p_now_ns - && ((now_ns ?: (now_ns = nm_utils_get_monotonic_timestamp_nsec ())) >= data->timeout_abs_ns)) { - /* the caller can optionally check for timeout by providing a p_now_ns argument. */ - delayed_action_wait_for_nl_response_complete (platform, i, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT); - } else if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) - delayed_action_wait_for_nl_response_complete (platform, i, force_result); - else { - if ( next_seq_number == 0 - || next_timeout_abs_ns > data->timeout_abs_ns) { - next_seq_number = data->seq_number; - next_timeout_abs_ns = data->timeout_abs_ns; - } - i++; - } - } - - if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) { - nm_assert (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); - nm_assert (priv->delayed_action.list_wait_for_nl_response->len == 0); - } - - NM_SET_OUT (out_next_seq_number, next_seq_number); - NM_SET_OUT (out_next_timeout_abs_ns, next_timeout_abs_ns); - NM_SET_OUT (p_now_ns, now_ns); +delayed_action_wait_for_nl_response_complete_check(NMPlatform * platform, + WaitForNlResponseResult force_result, + guint32 * out_next_seq_number, + gint64 * out_next_timeout_abs_ns, + gint64 * p_now_ns) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + guint i; + guint32 next_seq_number = 0; + gint64 next_timeout_abs_ns = 0; + int now_ns = 0; + + for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len;) { + const DelayedActionWaitForNlResponseData *data = + &g_array_index(priv->delayed_action.list_wait_for_nl_response, + DelayedActionWaitForNlResponseData, + i); + + if (data->seq_result) + delayed_action_wait_for_nl_response_complete(platform, i, data->seq_result); + else if (p_now_ns + && ((now_ns ?: (now_ns = nm_utils_get_monotonic_timestamp_nsec())) + >= data->timeout_abs_ns)) { + /* the caller can optionally check for timeout by providing a p_now_ns argument. */ + delayed_action_wait_for_nl_response_complete( + platform, + i, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT); + } else if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) + delayed_action_wait_for_nl_response_complete(platform, i, force_result); + else { + if (next_seq_number == 0 || next_timeout_abs_ns > data->timeout_abs_ns) { + next_seq_number = data->seq_number; + next_timeout_abs_ns = data->timeout_abs_ns; + } + i++; + } + } + + if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) { + nm_assert( + !NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); + nm_assert(priv->delayed_action.list_wait_for_nl_response->len == 0); + } + + NM_SET_OUT(out_next_seq_number, next_seq_number); + NM_SET_OUT(out_next_timeout_abs_ns, next_timeout_abs_ns); + NM_SET_OUT(p_now_ns, now_ns); } static void -delayed_action_wait_for_nl_response_complete_all (NMPlatform *platform, - WaitForNlResponseResult fallback_result) +delayed_action_wait_for_nl_response_complete_all(NMPlatform * platform, + WaitForNlResponseResult fallback_result) { - delayed_action_wait_for_nl_response_complete_check (platform, - fallback_result, - NULL, - NULL, - NULL); + delayed_action_wait_for_nl_response_complete_check(platform, fallback_result, NULL, NULL, NULL); } /*****************************************************************************/ static void -delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex) +delayed_action_handle_MASTER_CONNECTED(NMPlatform *platform, int master_ifindex) { - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_new = NULL; - NMPCacheOpsType cache_op; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + NMPCacheOpsType cache_op; - cache_op = nmp_cache_update_link_master_connected (nm_platform_get_cache (platform), master_ifindex, &obj_old, &obj_new); - if (cache_op == NMP_CACHE_OPS_UNCHANGED) - return; - cache_on_change (platform, cache_op, obj_old, obj_new); - nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + cache_op = nmp_cache_update_link_master_connected(nm_platform_get_cache(platform), + master_ifindex, + &obj_old, + &obj_new); + if (cache_op == NMP_CACHE_OPS_UNCHANGED) + return; + cache_on_change(platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); } static void -delayed_action_handle_REFRESH_LINK (NMPlatform *platform, int ifindex) +delayed_action_handle_REFRESH_LINK(NMPlatform *platform, int ifindex) { - do_request_link_no_delayed_actions (platform, ifindex, NULL); + do_request_link_no_delayed_actions(platform, ifindex, NULL); } static void -delayed_action_handle_REFRESH_ALL (NMPlatform *platform, DelayedActionType flags) +delayed_action_handle_REFRESH_ALL(NMPlatform *platform, DelayedActionType flags) { - do_request_all_no_delayed_actions (platform, flags); + do_request_all_no_delayed_actions(platform, flags); } static void -delayed_action_handle_READ_NETLINK (NMPlatform *platform) +delayed_action_handle_READ_NETLINK(NMPlatform *platform) { - event_handler_read_netlink (platform, FALSE); + event_handler_read_netlink(platform, FALSE); } static void -delayed_action_handle_WAIT_FOR_NL_RESPONSE (NMPlatform *platform) +delayed_action_handle_WAIT_FOR_NL_RESPONSE(NMPlatform *platform) { - event_handler_read_netlink (platform, TRUE); + event_handler_read_netlink(platform, TRUE); } static gboolean -delayed_action_handle_one (NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - gpointer user_data; - - if (priv->delayed_action.flags == DELAYED_ACTION_TYPE_NONE) - return FALSE; - - /* First process DELAYED_ACTION_TYPE_MASTER_CONNECTED actions. - * This type of action is entirely cache-internal and is here to resolve a - * cache inconsistency. It should be fixed right away. */ - if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_MASTER_CONNECTED)) { - nm_assert (priv->delayed_action.list_master_connected->len > 0); - - user_data = priv->delayed_action.list_master_connected->pdata[0]; - g_ptr_array_remove_index_fast (priv->delayed_action.list_master_connected, 0); - if (priv->delayed_action.list_master_connected->len == 0) - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_MASTER_CONNECTED; - nm_assert (_nm_utils_ptrarray_find_first ((gconstpointer *) priv->delayed_action.list_master_connected->pdata, priv->delayed_action.list_master_connected->len, user_data) < 0); - - _LOGt_delayed_action (DELAYED_ACTION_TYPE_MASTER_CONNECTED, user_data, "handle"); - delayed_action_handle_MASTER_CONNECTED (platform, GPOINTER_TO_INT (user_data)); - return TRUE; - } - nm_assert (priv->delayed_action.list_master_connected->len == 0); - - /* Next we prefer read-netlink, because the buffer size is limited and we want to process events - * from netlink early. */ - if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_READ_NETLINK)) { - _LOGt_delayed_action (DELAYED_ACTION_TYPE_READ_NETLINK, NULL, "handle"); - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_READ_NETLINK; - delayed_action_handle_READ_NETLINK (platform); - return TRUE; - } - - if (NM_FLAGS_ANY (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_ALL)) { - DelayedActionType flags, iflags; - - flags = priv->delayed_action.flags & DELAYED_ACTION_TYPE_REFRESH_ALL; - - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_ALL; - - if (_LOGt_ENABLED ()) { - FOR_EACH_DELAYED_ACTION (iflags, flags) - _LOGt_delayed_action (iflags, NULL, "handle"); - } - - delayed_action_handle_REFRESH_ALL (platform, flags); - return TRUE; - } - - if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)) { - nm_assert (priv->delayed_action.list_refresh_link->len > 0); - - user_data = priv->delayed_action.list_refresh_link->pdata[0]; - g_ptr_array_remove_index_fast (priv->delayed_action.list_refresh_link, 0); - if (priv->delayed_action.list_refresh_link->len == 0) - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; - nm_assert (_nm_utils_ptrarray_find_first ((gconstpointer *) priv->delayed_action.list_refresh_link->pdata, priv->delayed_action.list_refresh_link->len, user_data) < 0); - - _LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, user_data, "handle"); - - delayed_action_handle_REFRESH_LINK (platform, GPOINTER_TO_INT (user_data)); - - return TRUE; - } - - if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - nm_assert (priv->delayed_action.list_wait_for_nl_response->len > 0); - _LOGt_delayed_action (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, NULL, "handle"); - delayed_action_handle_WAIT_FOR_NL_RESPONSE (platform); - return TRUE; - } - - return FALSE; +delayed_action_handle_one(NMPlatform *platform) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + gpointer user_data; + + if (priv->delayed_action.flags == DELAYED_ACTION_TYPE_NONE) + return FALSE; + + /* First process DELAYED_ACTION_TYPE_MASTER_CONNECTED actions. + * This type of action is entirely cache-internal and is here to resolve a + * cache inconsistency. It should be fixed right away. */ + if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_MASTER_CONNECTED)) { + nm_assert(priv->delayed_action.list_master_connected->len > 0); + + user_data = priv->delayed_action.list_master_connected->pdata[0]; + g_ptr_array_remove_index_fast(priv->delayed_action.list_master_connected, 0); + if (priv->delayed_action.list_master_connected->len == 0) + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_MASTER_CONNECTED; + nm_assert(_nm_utils_ptrarray_find_first( + (gconstpointer *) priv->delayed_action.list_master_connected->pdata, + priv->delayed_action.list_master_connected->len, + user_data) + < 0); + + _LOGt_delayed_action(DELAYED_ACTION_TYPE_MASTER_CONNECTED, user_data, "handle"); + delayed_action_handle_MASTER_CONNECTED(platform, GPOINTER_TO_INT(user_data)); + return TRUE; + } + nm_assert(priv->delayed_action.list_master_connected->len == 0); + + /* Next we prefer read-netlink, because the buffer size is limited and we want to process events + * from netlink early. */ + if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_READ_NETLINK)) { + _LOGt_delayed_action(DELAYED_ACTION_TYPE_READ_NETLINK, NULL, "handle"); + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_READ_NETLINK; + delayed_action_handle_READ_NETLINK(platform); + return TRUE; + } + + if (NM_FLAGS_ANY(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_ALL)) { + DelayedActionType flags, iflags; + + flags = priv->delayed_action.flags & DELAYED_ACTION_TYPE_REFRESH_ALL; + + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_ALL; + + if (_LOGt_ENABLED()) { + FOR_EACH_DELAYED_ACTION(iflags, flags) + _LOGt_delayed_action(iflags, NULL, "handle"); + } + + delayed_action_handle_REFRESH_ALL(platform, flags); + return TRUE; + } + + if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)) { + nm_assert(priv->delayed_action.list_refresh_link->len > 0); + + user_data = priv->delayed_action.list_refresh_link->pdata[0]; + g_ptr_array_remove_index_fast(priv->delayed_action.list_refresh_link, 0); + if (priv->delayed_action.list_refresh_link->len == 0) + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; + nm_assert(_nm_utils_ptrarray_find_first( + (gconstpointer *) priv->delayed_action.list_refresh_link->pdata, + priv->delayed_action.list_refresh_link->len, + user_data) + < 0); + + _LOGt_delayed_action(DELAYED_ACTION_TYPE_REFRESH_LINK, user_data, "handle"); + + delayed_action_handle_REFRESH_LINK(platform, GPOINTER_TO_INT(user_data)); + + return TRUE; + } + + if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { + nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0); + _LOGt_delayed_action(DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, NULL, "handle"); + delayed_action_handle_WAIT_FOR_NL_RESPONSE(platform); + return TRUE; + } + + return FALSE; } static gboolean -delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink) +delayed_action_handle_all(NMPlatform *platform, gboolean read_netlink) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - gboolean any = FALSE; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + gboolean any = FALSE; - g_return_val_if_fail (priv->delayed_action.is_handling == 0, FALSE); + g_return_val_if_fail(priv->delayed_action.is_handling == 0, FALSE); - priv->delayed_action.is_handling++; - if (read_netlink) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_READ_NETLINK, NULL); - while (delayed_action_handle_one (platform)) - any = TRUE; - priv->delayed_action.is_handling--; + priv->delayed_action.is_handling++; + if (read_netlink) + delayed_action_schedule(platform, DELAYED_ACTION_TYPE_READ_NETLINK, NULL); + while (delayed_action_handle_one(platform)) + any = TRUE; + priv->delayed_action.is_handling--; - cache_prune_all (platform); + cache_prune_all(platform); - return any; + return any; } static void -delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - DelayedActionType iflags; - - nm_assert (action_type != DELAYED_ACTION_TYPE_NONE); - - switch (action_type) { - case DELAYED_ACTION_TYPE_REFRESH_LINK: - if (_nm_utils_ptrarray_find_first ((gconstpointer *) priv->delayed_action.list_refresh_link->pdata, priv->delayed_action.list_refresh_link->len, user_data) < 0) - g_ptr_array_add (priv->delayed_action.list_refresh_link, user_data); - break; - case DELAYED_ACTION_TYPE_MASTER_CONNECTED: - if (_nm_utils_ptrarray_find_first ((gconstpointer *) priv->delayed_action.list_master_connected->pdata, priv->delayed_action.list_master_connected->len, user_data) < 0) - g_ptr_array_add (priv->delayed_action.list_master_connected, user_data); - break; - case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE: - g_array_append_vals (priv->delayed_action.list_wait_for_nl_response, user_data, 1); - break; - default: - nm_assert (!user_data); - nm_assert (!NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_REFRESH_LINK)); - nm_assert (!NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_MASTER_CONNECTED)); - nm_assert (!NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); - break; - } - - priv->delayed_action.flags |= action_type; - - if (_LOGt_ENABLED ()) { - FOR_EACH_DELAYED_ACTION (iflags, action_type) - _LOGt_delayed_action (iflags, user_data, "schedule"); - } +delayed_action_schedule(NMPlatform *platform, DelayedActionType action_type, gpointer user_data) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + DelayedActionType iflags; + + nm_assert(action_type != DELAYED_ACTION_TYPE_NONE); + + switch (action_type) { + case DELAYED_ACTION_TYPE_REFRESH_LINK: + if (_nm_utils_ptrarray_find_first( + (gconstpointer *) priv->delayed_action.list_refresh_link->pdata, + priv->delayed_action.list_refresh_link->len, + user_data) + < 0) + g_ptr_array_add(priv->delayed_action.list_refresh_link, user_data); + break; + case DELAYED_ACTION_TYPE_MASTER_CONNECTED: + if (_nm_utils_ptrarray_find_first( + (gconstpointer *) priv->delayed_action.list_master_connected->pdata, + priv->delayed_action.list_master_connected->len, + user_data) + < 0) + g_ptr_array_add(priv->delayed_action.list_master_connected, user_data); + break; + case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE: + g_array_append_vals(priv->delayed_action.list_wait_for_nl_response, user_data, 1); + break; + default: + nm_assert(!user_data); + nm_assert(!NM_FLAGS_HAS(action_type, DELAYED_ACTION_TYPE_REFRESH_LINK)); + nm_assert(!NM_FLAGS_HAS(action_type, DELAYED_ACTION_TYPE_MASTER_CONNECTED)); + nm_assert(!NM_FLAGS_HAS(action_type, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); + break; + } + + priv->delayed_action.flags |= action_type; + + if (_LOGt_ENABLED()) { + FOR_EACH_DELAYED_ACTION(iflags, action_type) + _LOGt_delayed_action(iflags, user_data, "schedule"); + } } static void -delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, - guint32 seq_number, - WaitForNlResponseResult *out_seq_result, - char **out_errmsg, - DelayedActionWaitForNlResponseType response_type, - gpointer response_out_data) -{ - DelayedActionWaitForNlResponseData data = { - .seq_number = seq_number, - .timeout_abs_ns = nm_utils_get_monotonic_timestamp_nsec () + (200 * (NM_UTILS_NSEC_PER_SEC / 1000)), - .out_seq_result = out_seq_result, - .out_errmsg = out_errmsg, - .response_type = response_type, - .response.out_data = response_out_data, - }; - - delayed_action_schedule (platform, - DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, - &data); +delayed_action_schedule_WAIT_FOR_NL_RESPONSE(NMPlatform * platform, + guint32 seq_number, + WaitForNlResponseResult * out_seq_result, + char ** out_errmsg, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) +{ + DelayedActionWaitForNlResponseData data = { + .seq_number = seq_number, + .timeout_abs_ns = + nm_utils_get_monotonic_timestamp_nsec() + (200 * (NM_UTILS_NSEC_PER_SEC / 1000)), + .out_seq_result = out_seq_result, + .out_errmsg = out_errmsg, + .response_type = response_type, + .response.out_data = response_out_data, + }; + + delayed_action_schedule(platform, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, &data); } /*****************************************************************************/ static void -cache_prune_one_type (NMPlatform *platform, - const NMPLookup *lookup) +cache_prune_one_type(NMPlatform *platform, const NMPLookup *lookup) { - NMDedupMultiIter iter; - const NMPObject *obj; - NMPCacheOpsType cache_op; - NMPCache *cache = nm_platform_get_cache (platform); + NMDedupMultiIter iter; + const NMPObject *obj; + NMPCacheOpsType cache_op; + NMPCache * cache = nm_platform_get_cache(platform); - nm_dedup_multi_iter_init (&iter, - nmp_cache_lookup (cache, - lookup)); - while (nm_dedup_multi_iter_next (&iter)) { - const NMDedupMultiEntry *main_entry; + nm_dedup_multi_iter_init(&iter, nmp_cache_lookup(cache, lookup)); + while (nm_dedup_multi_iter_next(&iter)) { + const NMDedupMultiEntry *main_entry; - /* we only track the dirty flag for the OBJECT-TYPE index. That means, - * for other lookup types we need to check the dirty flag of the main-entry. */ - main_entry = nmp_cache_reresolve_main_entry (cache, iter.current, lookup); - if (!main_entry->dirty) - continue; + /* we only track the dirty flag for the OBJECT-TYPE index. That means, + * for other lookup types we need to check the dirty flag of the main-entry. */ + main_entry = nmp_cache_reresolve_main_entry(cache, iter.current, lookup); + if (!main_entry->dirty) + continue; - obj = main_entry->obj; + obj = main_entry->obj; - _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + _LOGt("cache-prune: prune %s", + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - { - nm_auto_nmpobj const NMPObject *obj_old = NULL; + { + nm_auto_nmpobj const NMPObject *obj_old = NULL; - cache_op = nmp_cache_remove (cache, obj, TRUE, TRUE, &obj_old); - nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); - cache_on_change (platform, cache_op, obj_old, NULL); - nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, NULL); - } - } + cache_op = nmp_cache_remove(cache, obj, TRUE, TRUE, &obj_old); + nm_assert(cache_op == NMP_CACHE_OPS_REMOVED); + cache_on_change(platform, cache_op, obj_old, NULL); + nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, NULL); + } + } } static void -cache_prune_all (NMPlatform *platform) +cache_prune_all(NMPlatform *platform) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - RefreshAllType refresh_all_type; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + RefreshAllType refresh_all_type; - for (refresh_all_type = _REFRESH_ALL_TYPE_FIRST; refresh_all_type < _REFRESH_ALL_TYPE_NUM; refresh_all_type++) { - NMPLookup lookup; + for (refresh_all_type = _REFRESH_ALL_TYPE_FIRST; refresh_all_type < _REFRESH_ALL_TYPE_NUM; + refresh_all_type++) { + NMPLookup lookup; - if (priv->pruning[refresh_all_type] == 0) - continue; - priv->pruning[refresh_all_type] -= 1; - if (priv->pruning[refresh_all_type] > 0) - continue; - refresh_all_type_init_lookup (refresh_all_type, - &lookup); - cache_prune_one_type (platform, &lookup); - } + if (priv->pruning[refresh_all_type] == 0) + continue; + priv->pruning[refresh_all_type] -= 1; + if (priv->pruning[refresh_all_type] > 0) + continue; + refresh_all_type_init_lookup(refresh_all_type, &lookup); + cache_prune_one_type(platform, &lookup); + } } static void -cache_on_change (NMPlatform *platform, - NMPCacheOpsType cache_op, - const NMPObject *obj_old, - const NMPObject *obj_new) -{ - const NMPClass *klass; - char str_buf[sizeof (_nm_utils_to_string_buffer)]; - char str_buf2[sizeof (_nm_utils_to_string_buffer)]; - NMPCache *cache = nm_platform_get_cache (platform); - - ASSERT_nmp_cache_ops (cache, cache_op, obj_old, obj_new); - nm_assert (cache_op != NMP_CACHE_OPS_UNCHANGED); - - klass = obj_old ? NMP_OBJECT_GET_CLASS (obj_old) : NMP_OBJECT_GET_CLASS (obj_new); - - _LOGt ("update-cache-%s: %s: %s%s%s", - klass->obj_type_name, - (cache_op == NMP_CACHE_OPS_UPDATED - ? "UPDATE" - : (cache_op == NMP_CACHE_OPS_REMOVED - ? "REMOVE" - : (cache_op == NMP_CACHE_OPS_ADDED) ? "ADD" : "???")), - (cache_op != NMP_CACHE_OPS_ADDED - ? nmp_object_to_string (obj_old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2)) - : nmp_object_to_string (obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2))), - (cache_op == NMP_CACHE_OPS_UPDATED) ? " -> " : "", - (cache_op == NMP_CACHE_OPS_UPDATED - ? nmp_object_to_string (obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof (str_buf)) - : "")); - - switch (klass->obj_type) { - case NMP_OBJECT_TYPE_LINK: - { - /* check whether changing a slave link can cause a master link (bridge or bond) to go up/down */ - if ( obj_old - && nmp_cache_link_connected_needs_toggle_by_ifindex (cache, obj_old->link.master, obj_new, obj_old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_old->link.master)); - if ( obj_new - && (!obj_old || obj_old->link.master != obj_new->link.master) - && nmp_cache_link_connected_needs_toggle_by_ifindex (cache, obj_new->link.master, obj_new, obj_old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_new->link.master)); - } - { - /* check whether we are about to change a master link that needs toggling connected state. */ - if ( obj_new /* <-- nonsensical, make coverity happy */ - && nmp_cache_link_connected_needs_toggle (cache, obj_new, obj_new, obj_old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_new->link.ifindex)); - } - { - int ifindex = 0; - - /* if we remove a link (from netlink), we must refresh the addresses, routes, qdiscs and tfilters */ - if ( cache_op == NMP_CACHE_OPS_REMOVED - && obj_old /* <-- nonsensical, make coverity happy */) - ifindex = obj_old->link.ifindex; - else if ( cache_op == NMP_CACHE_OPS_UPDATED - && obj_old && obj_new /* <-- nonsensical, make coverity happy */ - && !obj_new->_link.netlink.is_in_netlink - && obj_new->_link.netlink.is_in_netlink != obj_old->_link.netlink.is_in_netlink) - ifindex = obj_new->link.ifindex; - - if (ifindex > 0) { - delayed_action_schedule (platform, - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, - NULL); - } - } - { - int ifindex = -1; - - /* removal of a link could be caused by moving the link to another netns. - * In this case, we potentially have to update other links that have this link as parent. - * Currently, kernel misses to sent us a notification in this case - * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */ - - if ( cache_op == NMP_CACHE_OPS_REMOVED - && obj_old /* <-- nonsensical, make coverity happy */ - && obj_old->_link.netlink.is_in_netlink) - ifindex = obj_old->link.ifindex; - else if ( cache_op == NMP_CACHE_OPS_UPDATED - && obj_old && obj_new /* <-- nonsensical, make coverity happy */ - && obj_old->_link.netlink.is_in_netlink - && !obj_new->_link.netlink.is_in_netlink) - ifindex = obj_new->link.ifindex; - - if (ifindex > 0) { - NMPLookup lookup; - NMDedupMultiIter iter; - const NMPlatformLink *l; - - nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); - nmp_cache_iter_for_each_link (&iter, - nmp_cache_lookup (cache, &lookup), - &l) { - if (l->parent == ifindex) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (l->ifindex)); - } - } - } - { - /* if a link goes down, we must refresh routes */ - if ( cache_op == NMP_CACHE_OPS_UPDATED - && obj_old && obj_new /* <-- nonsensical, make coverity happy */ - && obj_old->_link.netlink.is_in_netlink - && obj_new->_link.netlink.is_in_netlink - && ( ( NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_UP) - && !NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_UP)) - || ( NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_LOWER_UP) - && !NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_LOWER_UP)))) { - /* FIXME: I suspect that IFF_LOWER_UP must not be considered, and I - * think kernel does send RTM_DELROUTE events for IPv6 routes, so - * we might not need to refresh IPv6 routes. */ - delayed_action_schedule (platform, - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, - NULL); - } - } - if ( NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED) - && (obj_new && obj_new->_link.netlink.is_in_netlink) - && (!obj_old || !obj_old->_link.netlink.is_in_netlink)) - { - gboolean re_request_link = FALSE; - const NMPlatformLnkTun *lnk_tun; - - if ( !obj_new->_link.netlink.lnk - && NM_IN_SET (obj_new->link.type, NM_LINK_TYPE_GRE, - NM_LINK_TYPE_GRETAP, - NM_LINK_TYPE_IP6TNL, - NM_LINK_TYPE_IP6GRE, - NM_LINK_TYPE_IP6GRETAP, - NM_LINK_TYPE_INFINIBAND, - NM_LINK_TYPE_MACVLAN, - NM_LINK_TYPE_MACVLAN, - NM_LINK_TYPE_SIT, - NM_LINK_TYPE_TUN, - NM_LINK_TYPE_VLAN, - NM_LINK_TYPE_VXLAN)) { - /* certain link-types also come with a IFLA_INFO_DATA/lnk_data. It may happen that - * kernel didn't send this notification, thus when we first learn about a link - * that lacks an lnk_data we re-request it again. - * - * For example https://bugzilla.redhat.com/show_bug.cgi?id=1284001 */ - re_request_link = TRUE; - } else if ( obj_new->link.type == NM_LINK_TYPE_TUN - && obj_new->_link.netlink.lnk - && (lnk_tun = &(obj_new->_link.netlink.lnk)->lnk_tun) - && !lnk_tun->persist - && lnk_tun->pi - && !lnk_tun->vnet_hdr - && !lnk_tun->multi_queue - && !lnk_tun->owner_valid - && !lnk_tun->group_valid) { - /* kernel has/had a know issue that the first notification for TUN device would - * be sent with invalid parameters. The message looks like that kind, so refetch - * it. */ - re_request_link = TRUE; - } else if ( obj_new->link.type == NM_LINK_TYPE_VETH - && obj_new->link.parent == 0) { - /* the initial notification when adding a veth pair can lack the parent/IFLA_LINK - * (https://bugzilla.redhat.com/show_bug.cgi?id=1285827). - * Request it again. */ - re_request_link = TRUE; - } else if ( obj_new->link.type == NM_LINK_TYPE_ETHERNET - && obj_new->link.l_address.len == 0) { - /* Due to a kernel bug, we sometimes receive spurious NEWLINK - * messages after a wifi interface has disappeared. Since the - * link is not present anymore we can't determine its type and - * thus it will show up as a Ethernet one, with no address - * specified. Request the link again to check if it really - * exists. https://bugzilla.redhat.com/show_bug.cgi?id=1302037 - */ - re_request_link = TRUE; - } - if (re_request_link) { - delayed_action_schedule (platform, - DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (obj_new->link.ifindex)); - } - } - { - /* on enslave/release, we also refresh the master. */ - int ifindex1 = 0, ifindex2 = 0; - gboolean changed_master, changed_connected; - - changed_master = (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0 ? obj_new->link.master : 0) - != (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0 ? obj_old->link.master : 0); - changed_connected = (obj_new && obj_new->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_LOWER_UP) : 2) - != (obj_old && obj_old->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_LOWER_UP) : 2); - - if (changed_master || changed_connected) { - ifindex1 = (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0) ? obj_old->link.master : 0; - ifindex2 = (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0) ? obj_new->link.master : 0; - - if (ifindex1 > 0) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex1)); - if (ifindex2 > 0 && ifindex1 != ifindex2) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex2)); - } - } - break; - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ADDRESS: - { - /* Address deletion is sometimes accompanied by route deletion. We need to - * check all routes belonging to the same interface. */ - if (cache_op == NMP_CACHE_OPS_REMOVED) { - delayed_action_schedule (platform, - (klass->obj_type == NMP_OBJECT_TYPE_IP4_ADDRESS) - ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, - NULL); - } - } - break; - default: - break; - } +cache_on_change(NMPlatform * platform, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new) +{ + const NMPClass *klass; + char str_buf[sizeof(_nm_utils_to_string_buffer)]; + char str_buf2[sizeof(_nm_utils_to_string_buffer)]; + NMPCache * cache = nm_platform_get_cache(platform); + + ASSERT_nmp_cache_ops(cache, cache_op, obj_old, obj_new); + nm_assert(cache_op != NMP_CACHE_OPS_UNCHANGED); + + klass = obj_old ? NMP_OBJECT_GET_CLASS(obj_old) : NMP_OBJECT_GET_CLASS(obj_new); + + _LOGt( + "update-cache-%s: %s: %s%s%s", + klass->obj_type_name, + (cache_op == NMP_CACHE_OPS_UPDATED ? "UPDATE" + : (cache_op == NMP_CACHE_OPS_REMOVED ? "REMOVE" + : (cache_op == NMP_CACHE_OPS_ADDED) ? "ADD" + : "???")), + (cache_op != NMP_CACHE_OPS_ADDED + ? nmp_object_to_string(obj_old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof(str_buf2)) + : nmp_object_to_string(obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof(str_buf2))), + (cache_op == NMP_CACHE_OPS_UPDATED) ? " -> " : "", + (cache_op == NMP_CACHE_OPS_UPDATED + ? nmp_object_to_string(obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof(str_buf)) + : "")); + + switch (klass->obj_type) { + case NMP_OBJECT_TYPE_LINK: + { + /* check whether changing a slave link can cause a master link (bridge or bond) to go up/down */ + if (obj_old + && nmp_cache_link_connected_needs_toggle_by_ifindex(cache, + obj_old->link.master, + obj_new, + obj_old)) + delayed_action_schedule(platform, + DELAYED_ACTION_TYPE_MASTER_CONNECTED, + GINT_TO_POINTER(obj_old->link.master)); + if (obj_new && (!obj_old || obj_old->link.master != obj_new->link.master) + && nmp_cache_link_connected_needs_toggle_by_ifindex(cache, + obj_new->link.master, + obj_new, + obj_old)) + delayed_action_schedule(platform, + DELAYED_ACTION_TYPE_MASTER_CONNECTED, + GINT_TO_POINTER(obj_new->link.master)); + } + { + /* check whether we are about to change a master link that needs toggling connected state. */ + if (obj_new /* <-- nonsensical, make coverity happy */ + && nmp_cache_link_connected_needs_toggle(cache, obj_new, obj_new, obj_old)) + delayed_action_schedule(platform, + DELAYED_ACTION_TYPE_MASTER_CONNECTED, + GINT_TO_POINTER(obj_new->link.ifindex)); + } + { + int ifindex = 0; + + /* if we remove a link (from netlink), we must refresh the addresses, routes, qdiscs and tfilters */ + if (cache_op == NMP_CACHE_OPS_REMOVED + && obj_old /* <-- nonsensical, make coverity happy */) + ifindex = obj_old->link.ifindex; + else if (cache_op == NMP_CACHE_OPS_UPDATED && obj_old + && obj_new /* <-- nonsensical, make coverity happy */ + && !obj_new->_link.netlink.is_in_netlink + && obj_new->_link.netlink.is_in_netlink + != obj_old->_link.netlink.is_in_netlink) + ifindex = obj_new->link.ifindex; + + if (ifindex > 0) { + delayed_action_schedule(platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL + | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS + | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, + NULL); + } + } + { + int ifindex = -1; + + /* removal of a link could be caused by moving the link to another netns. + * In this case, we potentially have to update other links that have this link as parent. + * Currently, kernel misses to sent us a notification in this case + * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */ + + if (cache_op == NMP_CACHE_OPS_REMOVED + && obj_old /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink) + ifindex = obj_old->link.ifindex; + else if (cache_op == NMP_CACHE_OPS_UPDATED && obj_old + && obj_new /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink + && !obj_new->_link.netlink.is_in_netlink) + ifindex = obj_new->link.ifindex; + + if (ifindex > 0) { + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPlatformLink *l; + + nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK); + nmp_cache_iter_for_each_link (&iter, nmp_cache_lookup(cache, &lookup), &l) { + if (l->parent == ifindex) + delayed_action_schedule(platform, + DELAYED_ACTION_TYPE_REFRESH_LINK, + GINT_TO_POINTER(l->ifindex)); + } + } + } + { + /* if a link goes down, we must refresh routes */ + if (cache_op == NMP_CACHE_OPS_UPDATED && obj_old + && obj_new /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink && obj_new->_link.netlink.is_in_netlink + && ((NM_FLAGS_HAS(obj_old->link.n_ifi_flags, IFF_UP) + && !NM_FLAGS_HAS(obj_new->link.n_ifi_flags, IFF_UP)) + || (NM_FLAGS_HAS(obj_old->link.n_ifi_flags, IFF_LOWER_UP) + && !NM_FLAGS_HAS(obj_new->link.n_ifi_flags, IFF_LOWER_UP)))) { + /* FIXME: I suspect that IFF_LOWER_UP must not be considered, and I + * think kernel does send RTM_DELROUTE events for IPv6 routes, so + * we might not need to refresh IPv6 routes. */ + delayed_action_schedule(platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); + } + } + if (NM_IN_SET(cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED) + && (obj_new && obj_new->_link.netlink.is_in_netlink) + && (!obj_old || !obj_old->_link.netlink.is_in_netlink)) { + gboolean re_request_link = FALSE; + const NMPlatformLnkTun *lnk_tun; + + if (!obj_new->_link.netlink.lnk + && NM_IN_SET(obj_new->link.type, + NM_LINK_TYPE_GRE, + NM_LINK_TYPE_GRETAP, + NM_LINK_TYPE_IP6TNL, + NM_LINK_TYPE_IP6GRE, + NM_LINK_TYPE_IP6GRETAP, + NM_LINK_TYPE_INFINIBAND, + NM_LINK_TYPE_MACVLAN, + NM_LINK_TYPE_MACVLAN, + NM_LINK_TYPE_SIT, + NM_LINK_TYPE_TUN, + NM_LINK_TYPE_VLAN, + NM_LINK_TYPE_VXLAN)) { + /* certain link-types also come with a IFLA_INFO_DATA/lnk_data. It may happen that + * kernel didn't send this notification, thus when we first learn about a link + * that lacks an lnk_data we re-request it again. + * + * For example https://bugzilla.redhat.com/show_bug.cgi?id=1284001 */ + re_request_link = TRUE; + } else if (obj_new->link.type == NM_LINK_TYPE_TUN && obj_new->_link.netlink.lnk + && (lnk_tun = &(obj_new->_link.netlink.lnk)->lnk_tun) && !lnk_tun->persist + && lnk_tun->pi && !lnk_tun->vnet_hdr && !lnk_tun->multi_queue + && !lnk_tun->owner_valid && !lnk_tun->group_valid) { + /* kernel has/had a know issue that the first notification for TUN device would + * be sent with invalid parameters. The message looks like that kind, so refetch + * it. */ + re_request_link = TRUE; + } else if (obj_new->link.type == NM_LINK_TYPE_VETH && obj_new->link.parent == 0) { + /* the initial notification when adding a veth pair can lack the parent/IFLA_LINK + * (https://bugzilla.redhat.com/show_bug.cgi?id=1285827). + * Request it again. */ + re_request_link = TRUE; + } else if (obj_new->link.type == NM_LINK_TYPE_ETHERNET + && obj_new->link.l_address.len == 0) { + /* Due to a kernel bug, we sometimes receive spurious NEWLINK + * messages after a wifi interface has disappeared. Since the + * link is not present anymore we can't determine its type and + * thus it will show up as a Ethernet one, with no address + * specified. Request the link again to check if it really + * exists. https://bugzilla.redhat.com/show_bug.cgi?id=1302037 + */ + re_request_link = TRUE; + } + if (re_request_link) { + delayed_action_schedule(platform, + DELAYED_ACTION_TYPE_REFRESH_LINK, + GINT_TO_POINTER(obj_new->link.ifindex)); + } + } + { + /* on enslave/release, we also refresh the master. */ + int ifindex1 = 0, ifindex2 = 0; + gboolean changed_master, changed_connected; + + changed_master = + (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0 + ? obj_new->link.master + : 0) + != (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0 + ? obj_old->link.master + : 0); + changed_connected = (obj_new && obj_new->_link.netlink.is_in_netlink + ? NM_FLAGS_HAS(obj_new->link.n_ifi_flags, IFF_LOWER_UP) + : 2) + != (obj_old && obj_old->_link.netlink.is_in_netlink + ? NM_FLAGS_HAS(obj_old->link.n_ifi_flags, IFF_LOWER_UP) + : 2); + + if (changed_master || changed_connected) { + ifindex1 = + (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0) + ? obj_old->link.master + : 0; + ifindex2 = + (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0) + ? obj_new->link.master + : 0; + + if (ifindex1 > 0) + delayed_action_schedule(platform, + DELAYED_ACTION_TYPE_REFRESH_LINK, + GINT_TO_POINTER(ifindex1)); + if (ifindex2 > 0 && ifindex1 != ifindex2) + delayed_action_schedule(platform, + DELAYED_ACTION_TYPE_REFRESH_LINK, + GINT_TO_POINTER(ifindex2)); + } + } + break; + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + { + /* Address deletion is sometimes accompanied by route deletion. We need to + * check all routes belonging to the same interface. */ + if (cache_op == NMP_CACHE_OPS_REMOVED) { + delayed_action_schedule(platform, + (klass->obj_type == NMP_OBJECT_TYPE_IP4_ADDRESS) + ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES + : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); + } + } break; + default: + break; + } } /*****************************************************************************/ static guint32 -_nlh_seq_next_get (NMLinuxPlatformPrivate *priv) +_nlh_seq_next_get(NMLinuxPlatformPrivate *priv) { - /* generate a new sequence number, but never return zero. - * Wrapping numbers are not a problem, because we don't rely - * on strictly increasing sequence numbers. */ - return (++priv->nlh_seq_next) ?: (++priv->nlh_seq_next); + /* generate a new sequence number, but never return zero. + * Wrapping numbers are not a problem, because we don't rely + * on strictly increasing sequence numbers. */ + return (++priv->nlh_seq_next) ?: (++priv->nlh_seq_next); } /** @@ -6209,57 +6476,60 @@ _nlh_seq_next_get (NMLinuxPlatformPrivate *priv) * Returns: 0 on success or a negative errno. */ static int -_nl_send_nlmsghdr (NMPlatform *platform, - struct nlmsghdr *nlhdr, - WaitForNlResponseResult *out_seq_result, - char **out_errmsg, - DelayedActionWaitForNlResponseType response_type, - gpointer response_out_data) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - guint32 seq; - int errsv; - - nm_assert (nlhdr); - - seq = _nlh_seq_next_get (priv); - nlhdr->nlmsg_seq = seq; - - { - struct sockaddr_nl nladdr = { - .nl_family = AF_NETLINK, - }; - struct iovec iov = { - .iov_base = nlhdr, - .iov_len = nlhdr->nlmsg_len - }; - struct msghdr msg = { - .msg_name = &nladdr, - .msg_namelen = sizeof (nladdr), - .msg_iov = &iov, - .msg_iovlen = 1, - }; - int try_count; - - if (!nlhdr->nlmsg_pid) - nlhdr->nlmsg_pid = nl_socket_get_local_port (priv->nlh); - nlhdr->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK); - - try_count = 0; +_nl_send_nlmsghdr(NMPlatform * platform, + struct nlmsghdr * nlhdr, + WaitForNlResponseResult * out_seq_result, + char ** out_errmsg, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + guint32 seq; + int errsv; + + nm_assert(nlhdr); + + seq = _nlh_seq_next_get(priv); + nlhdr->nlmsg_seq = seq; + + { + struct sockaddr_nl nladdr = { + .nl_family = AF_NETLINK, + }; + struct iovec iov = {.iov_base = nlhdr, .iov_len = nlhdr->nlmsg_len}; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + int try_count; + + if (!nlhdr->nlmsg_pid) + nlhdr->nlmsg_pid = nl_socket_get_local_port(priv->nlh); + nlhdr->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK); + + try_count = 0; again: - errsv = sendmsg (nl_socket_get_fd (priv->nlh), &msg, 0); - if (errsv < 0) { - errsv = errno; - if (errsv == EINTR && try_count++ < 100) - goto again; - _LOGD ("netlink: nl-send-nlmsghdr: failed sending message: %s (%d)", nm_strerror_native (errsv), errsv); - return -nm_errno_from_native (errsv); - } - } - - delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, out_errmsg, - response_type, response_out_data); - return 0; + errsv = sendmsg(nl_socket_get_fd(priv->nlh), &msg, 0); + if (errsv < 0) { + errsv = errno; + if (errsv == EINTR && try_count++ < 100) + goto again; + _LOGD("netlink: nl-send-nlmsghdr: failed sending message: %s (%d)", + nm_strerror_native(errsv), + errsv); + return -nm_errno_from_native(errsv); + } + } + + delayed_action_schedule_WAIT_FOR_NL_RESPONSE(platform, + seq, + out_seq_result, + out_errmsg, + response_type, + response_out_data); + return 0; } /** @@ -6273,3107 +6543,3142 @@ again: * Returns: 0 on success, or a negative libnl3 error code (beware, it's not an errno). */ static int -_nl_send_nlmsg (NMPlatform *platform, - struct nl_msg *nlmsg, - WaitForNlResponseResult *out_seq_result, - char **out_errmsg, - DelayedActionWaitForNlResponseType response_type, - gpointer response_out_data) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nlmsghdr *nlhdr; - guint32 seq; - int nle; - - nlhdr = nlmsg_hdr (nlmsg); - seq = _nlh_seq_next_get (priv); - nlhdr->nlmsg_seq = seq; - - nle = nl_send_auto (priv->nlh, nlmsg); - if (nle < 0) { - _LOGD ("netlink: nl-send-nlmsg: failed sending message: %s (%d)", nm_strerror (nle), nle); - return nle; - } - - delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, out_errmsg, - response_type, response_out_data); - return 0; +_nl_send_nlmsg(NMPlatform * platform, + struct nl_msg * nlmsg, + WaitForNlResponseResult * out_seq_result, + char ** out_errmsg, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + struct nlmsghdr * nlhdr; + guint32 seq; + int nle; + + nlhdr = nlmsg_hdr(nlmsg); + seq = _nlh_seq_next_get(priv); + nlhdr->nlmsg_seq = seq; + + nle = nl_send_auto(priv->nlh, nlmsg); + if (nle < 0) { + _LOGD("netlink: nl-send-nlmsg: failed sending message: %s (%d)", nm_strerror(nle), nle); + return nle; + } + + delayed_action_schedule_WAIT_FOR_NL_RESPONSE(platform, + seq, + out_seq_result, + out_errmsg, + response_type, + response_out_data); + return 0; } static void -do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const char *name) +do_request_link_no_delayed_actions(NMPlatform *platform, int ifindex, const char *name) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - int nle; + NMLinuxPlatformPrivate * priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + int nle; - if (name && !name[0]) - name = NULL; + if (name && !name[0]) + name = NULL; - g_return_if_fail (ifindex > 0 || name); + g_return_if_fail(ifindex > 0 || name); - _LOGD ("do-request-link: %d %s", ifindex, name ?: ""); + _LOGD("do-request-link: %d %s", ifindex, name ?: ""); - if (ifindex > 0) { - const NMDedupMultiEntry *entry; + if (ifindex > 0) { + const NMDedupMultiEntry *entry; - entry = nmp_cache_lookup_entry_link (nm_platform_get_cache (platform), ifindex); - if (entry) { - priv->pruning[REFRESH_ALL_TYPE_LINKS] += 1; - nm_dedup_multi_entry_set_dirty (entry, TRUE); - } - } + entry = nmp_cache_lookup_entry_link(nm_platform_get_cache(platform), ifindex); + if (entry) { + priv->pruning[REFRESH_ALL_TYPE_LINKS] += 1; + nm_dedup_multi_entry_set_dirty(entry, TRUE); + } + } - event_handler_read_netlink (platform, FALSE); + event_handler_read_netlink(platform, FALSE); - nlmsg = _nl_msg_new_link (RTM_GETLINK, - 0, - ifindex, - name); - if (nlmsg) { - nle = _nl_send_nlmsg (platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); - if (nle < 0) { - _LOGE ("do-request-link: %d %s: failed sending netlink request \"%s\" (%d)", - ifindex, name ?: "", - nm_strerror (nle), -nle); - return; - } - } + nlmsg = _nl_msg_new_link(RTM_GETLINK, 0, ifindex, name); + if (nlmsg) { + nle = _nl_send_nlmsg(platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + if (nle < 0) { + _LOGE("do-request-link: %d %s: failed sending netlink request \"%s\" (%d)", + ifindex, + name ?: "", + nm_strerror(nle), + -nle); + return; + } + } } static void -do_request_link (NMPlatform *platform, int ifindex, const char *name) +do_request_link(NMPlatform *platform, int ifindex, const char *name) { - do_request_link_no_delayed_actions (platform, ifindex, name); - delayed_action_handle_all (platform, FALSE); + do_request_link_no_delayed_actions(platform, ifindex, name); + delayed_action_handle_all(platform, FALSE); } static struct nl_msg * -_nl_msg_new_dump (NMPObjectType obj_type, - int preferred_addr_family) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - const NMPClass *klass; - - klass = nmp_class_from_type (obj_type); - - nm_assert (klass); - nm_assert (klass->rtm_gettype > 0); - - nlmsg = nlmsg_alloc_simple (klass->rtm_gettype, NLM_F_DUMP); - - if (klass->addr_family != AF_UNSPEC) { - /* if the class specifies a particular address family, then it is preferred. */ - nm_assert (NM_IN_SET (preferred_addr_family, AF_UNSPEC, klass->addr_family)); - preferred_addr_family = klass->addr_family; - } - - switch (klass->obj_type) { - case NMP_OBJECT_TYPE_QDISC: - case NMP_OBJECT_TYPE_TFILTER: - { - const struct tcmsg tcmsg = { - .tcm_family = preferred_addr_family, - }; - - if (nlmsg_append_struct (nlmsg, &tcmsg) < 0) - g_return_val_if_reached (NULL); - } - break; - case NMP_OBJECT_TYPE_LINK: - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ADDRESS: - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - case NMP_OBJECT_TYPE_ROUTING_RULE: - { - const struct rtgenmsg gmsg = { - .rtgen_family = preferred_addr_family, - }; - - if (nlmsg_append_struct (nlmsg, &gmsg) < 0) - g_return_val_if_reached (NULL); - } - break; - default: - g_return_val_if_reached (NULL); - } - - return g_steal_pointer (&nlmsg); +_nl_msg_new_dump(NMPObjectType obj_type, int preferred_addr_family) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + const NMPClass * klass; + + klass = nmp_class_from_type(obj_type); + + nm_assert(klass); + nm_assert(klass->rtm_gettype > 0); + + nlmsg = nlmsg_alloc_simple(klass->rtm_gettype, NLM_F_DUMP); + + if (klass->addr_family != AF_UNSPEC) { + /* if the class specifies a particular address family, then it is preferred. */ + nm_assert(NM_IN_SET(preferred_addr_family, AF_UNSPEC, klass->addr_family)); + preferred_addr_family = klass->addr_family; + } + + switch (klass->obj_type) { + case NMP_OBJECT_TYPE_QDISC: + case NMP_OBJECT_TYPE_TFILTER: + { + const struct tcmsg tcmsg = { + .tcm_family = preferred_addr_family, + }; + + if (nlmsg_append_struct(nlmsg, &tcmsg) < 0) + g_return_val_if_reached(NULL); + } break; + case NMP_OBJECT_TYPE_LINK: + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + case NMP_OBJECT_TYPE_ROUTING_RULE: + { + const struct rtgenmsg gmsg = { + .rtgen_family = preferred_addr_family, + }; + + if (nlmsg_append_struct(nlmsg, &gmsg) < 0) + g_return_val_if_reached(NULL); + } break; + default: + g_return_val_if_reached(NULL); + } + + return g_steal_pointer(&nlmsg); } static void -do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - DelayedActionType action_type_prune; - DelayedActionType iflags; - - nm_assert (!NM_FLAGS_ANY (action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); - action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; - - action_type_prune = action_type; - - /* calling nmp_cache_dirty_set_all_main() with a non-main lookup-index requires an extra - * cache lookup for every entry. - * - * Avoid that, by special casing routing-rules here. */ - if (NM_FLAGS_ALL (action_type_prune, DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL)) { - NMPLookup lookup; - - priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP4] += 1; - priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP6] += 1; - nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); - nmp_cache_dirty_set_all_main (nm_platform_get_cache (platform), - &lookup); - action_type_prune &= ~DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL; - } - - FOR_EACH_DELAYED_ACTION (iflags, action_type_prune) { - RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type (iflags); - NMPLookup lookup; - - priv->pruning[refresh_all_type] += 1; - refresh_all_type_init_lookup (refresh_all_type, - &lookup); - nmp_cache_dirty_set_all_main (nm_platform_get_cache (platform), - &lookup); - } - - FOR_EACH_DELAYED_ACTION (iflags, action_type) { - RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type (iflags); - const RefreshAllInfo *refresh_all_info = refresh_all_type_get_info (refresh_all_type); - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - int *out_refresh_all_in_progress; - - out_refresh_all_in_progress = &priv->delayed_action.refresh_all_in_progress[refresh_all_type]; - nm_assert (*out_refresh_all_in_progress >= 0); - *out_refresh_all_in_progress += 1; - - /* clear any delayed action that request a refresh of this object type. */ - priv->delayed_action.flags &= ~iflags; - _LOGt_delayed_action (iflags, NULL, "handle (do-request-all)"); - - if (refresh_all_type == REFRESH_ALL_TYPE_LINKS) { - nm_assert ( (priv->delayed_action.list_refresh_link->len > 0) - == NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)); - if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)) { - _LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, NULL, "clear (do-request-all)"); - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; - g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); - } - } - - event_handler_read_netlink (platform, FALSE); - - nlmsg = _nl_msg_new_dump (refresh_all_info->obj_type, - refresh_all_info->addr_family); - if (!nlmsg) - goto next_after_fail; - - if (_nl_send_nlmsg (platform, - nlmsg, - NULL, - NULL, - DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, - out_refresh_all_in_progress) < 0) - goto next_after_fail; - - continue; +do_request_all_no_delayed_actions(NMPlatform *platform, DelayedActionType action_type) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + DelayedActionType action_type_prune; + DelayedActionType iflags; + + nm_assert(!NM_FLAGS_ANY(action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); + action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; + + action_type_prune = action_type; + + /* calling nmp_cache_dirty_set_all_main() with a non-main lookup-index requires an extra + * cache lookup for every entry. + * + * Avoid that, by special casing routing-rules here. */ + if (NM_FLAGS_ALL(action_type_prune, DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL)) { + NMPLookup lookup; + + priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP4] += 1; + priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP6] += 1; + nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + nmp_cache_dirty_set_all_main(nm_platform_get_cache(platform), &lookup); + action_type_prune &= ~DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL; + } + + FOR_EACH_DELAYED_ACTION(iflags, action_type_prune) + { + RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type(iflags); + NMPLookup lookup; + + priv->pruning[refresh_all_type] += 1; + refresh_all_type_init_lookup(refresh_all_type, &lookup); + nmp_cache_dirty_set_all_main(nm_platform_get_cache(platform), &lookup); + } + + FOR_EACH_DELAYED_ACTION(iflags, action_type) + { + RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type(iflags); + const RefreshAllInfo *refresh_all_info = refresh_all_type_get_info(refresh_all_type); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + int * out_refresh_all_in_progress; + + out_refresh_all_in_progress = + &priv->delayed_action.refresh_all_in_progress[refresh_all_type]; + nm_assert(*out_refresh_all_in_progress >= 0); + *out_refresh_all_in_progress += 1; + + /* clear any delayed action that request a refresh of this object type. */ + priv->delayed_action.flags &= ~iflags; + _LOGt_delayed_action(iflags, NULL, "handle (do-request-all)"); + + if (refresh_all_type == REFRESH_ALL_TYPE_LINKS) { + nm_assert( + (priv->delayed_action.list_refresh_link->len > 0) + == NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)); + if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)) { + _LOGt_delayed_action(DELAYED_ACTION_TYPE_REFRESH_LINK, + NULL, + "clear (do-request-all)"); + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; + g_ptr_array_set_size(priv->delayed_action.list_refresh_link, 0); + } + } + + event_handler_read_netlink(platform, FALSE); + + nlmsg = _nl_msg_new_dump(refresh_all_info->obj_type, refresh_all_info->addr_family); + if (!nlmsg) + goto next_after_fail; + + if (_nl_send_nlmsg(platform, + nlmsg, + NULL, + NULL, + DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, + out_refresh_all_in_progress) + < 0) + goto next_after_fail; + + continue; next_after_fail: - nm_assert (*out_refresh_all_in_progress > 0); - *out_refresh_all_in_progress -= 1; - } + nm_assert(*out_refresh_all_in_progress > 0); + *out_refresh_all_in_progress -= 1; + } } static void -do_request_one_type_by_needle_object (NMPlatform *platform, const NMPObject *obj_needle) +do_request_one_type_by_needle_object(NMPlatform *platform, const NMPObject *obj_needle) { - do_request_all_no_delayed_actions (platform, delayed_action_refresh_from_needle_object (obj_needle)); - delayed_action_handle_all (platform, FALSE); + do_request_all_no_delayed_actions(platform, + delayed_action_refresh_from_needle_object(obj_needle)); + delayed_action_handle_all(platform, FALSE); } static void -event_seq_check_refresh_all (NMPlatform *platform, guint32 seq_number) +event_seq_check_refresh_all(NMPlatform *platform, guint32 seq_number) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - DelayedActionWaitForNlResponseData *data; - guint i; + NMLinuxPlatformPrivate * priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + DelayedActionWaitForNlResponseData *data; + guint i; - if (NM_IN_SET (seq_number, 0, priv->nlh_seq_last_seen)) - return; + if (NM_IN_SET(seq_number, 0, priv->nlh_seq_last_seen)) + return; - if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - nm_assert (priv->delayed_action.list_wait_for_nl_response->len > 0); + if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { + nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0); - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { - data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); + for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { + data = &g_array_index(priv->delayed_action.list_wait_for_nl_response, + DelayedActionWaitForNlResponseData, + i); - if ( data->response_type == DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS - && data->response.out_refresh_all_in_progress - && data->seq_number == priv->nlh_seq_last_seen) { - *data->response.out_refresh_all_in_progress -= 1; - data->response.out_refresh_all_in_progress = NULL; - break; - } - } - } + if (data->response_type == DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS + && data->response.out_refresh_all_in_progress + && data->seq_number == priv->nlh_seq_last_seen) { + *data->response.out_refresh_all_in_progress -= 1; + data->response.out_refresh_all_in_progress = NULL; + break; + } + } + } - priv->nlh_seq_last_seen = seq_number; + priv->nlh_seq_last_seen = seq_number; } static void -event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResult seq_result, const char *msg) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - DelayedActionWaitForNlResponseData *data; - guint i; - - if (seq_number == 0) - return; - - if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - nm_assert (priv->delayed_action.list_wait_for_nl_response->len > 0); - - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { - data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); - - if (data->seq_number == seq_number) { - /* We potentially receive many parts partial responses for the same sequence number. - * Thus, we only remember the result, and collect it later. */ - if (data->seq_result < 0) { - /* we already saw an error for this sequence number. - * Preserve it. */ - } else if ( seq_result != WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN - || data->seq_result == WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) - data->seq_result = seq_result; - if (data->out_errmsg && !*data->out_errmsg) - *data->out_errmsg = g_strdup (msg); - return; - } - } - } +event_seq_check(NMPlatform * platform, + guint32 seq_number, + WaitForNlResponseResult seq_result, + const char * msg) +{ + NMLinuxPlatformPrivate * priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + DelayedActionWaitForNlResponseData *data; + guint i; + + if (seq_number == 0) + return; + + if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { + nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0); + + for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { + data = &g_array_index(priv->delayed_action.list_wait_for_nl_response, + DelayedActionWaitForNlResponseData, + i); + + if (data->seq_number == seq_number) { + /* We potentially receive many parts partial responses for the same sequence number. + * Thus, we only remember the result, and collect it later. */ + if (data->seq_result < 0) { + /* we already saw an error for this sequence number. + * Preserve it. */ + } else if (seq_result != WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN + || data->seq_result == WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) + data->seq_result = seq_result; + if (data->out_errmsg && !*data->out_errmsg) + *data->out_errmsg = g_strdup(msg); + return; + } + } + } #if NM_MORE_LOGGING - if (seq_number != priv->nlh_seq_last_handled) - _LOGt ("netlink: recvmsg: unwaited sequence number %u", seq_number); - priv->nlh_seq_last_handled = seq_number; + if (seq_number != priv->nlh_seq_last_handled) + _LOGt("netlink: recvmsg: unwaited sequence number %u", seq_number); + priv->nlh_seq_last_handled = seq_number; #endif } static void -event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_events) -{ - NMLinuxPlatformPrivate *priv; - nm_auto_nmpobj NMPObject *obj = NULL; - NMPCacheOpsType cache_op; - struct nlmsghdr *msghdr; - char buf_nlmsghdr[400]; - gboolean is_del = FALSE; - gboolean is_dump = FALSE; - NMPCache *cache = nm_platform_get_cache (platform); - - msghdr = nlmsg_hdr (msg); - - if ( !_nm_platform_kernel_support_detected (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS) - && msghdr->nlmsg_type == RTM_NEWADDR) { - /* IFA_FLAGS is set for IPv4 and IPv6 addresses. It was added first to IPv6, - * but if we encounter an IPv4 address with IFA_FLAGS, we surely have support. */ - if ( nlmsg_valid_hdr (msghdr, sizeof (struct ifaddrmsg)) - && NM_IN_SET (((struct ifaddrmsg *) nlmsg_data (msghdr))->ifa_family, - AF_INET, - AF_INET6)) { - /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, - * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR - * and IFA_F_NOPREFIXROUTE for IPv6. They were added together in kernel 3.14, - * dated 30 March, 2014. - * - * For IPv4, IFA_F_NOPREFIXROUTE was added later, but there is no easy - * way to detect kernel support. */ - _nm_platform_kernel_support_init (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS, - !!nlmsg_find_attr (msghdr, sizeof (struct ifaddrmsg), IFA_FLAGS) - ? 1 - : -1); - } - } - - if (!handle_events) - return; - - if (NM_IN_SET (msghdr->nlmsg_type, RTM_DELLINK, - RTM_DELADDR, - RTM_DELROUTE, - RTM_DELRULE, - RTM_DELQDISC, - RTM_DELTFILTER)) { - /* The event notifies about a deleted object. We don't need to initialize all - * fields of the object. */ - is_del = TRUE; - } - - obj = nmp_object_new_from_nl (platform, cache, msg, is_del); - if (!obj) { - _LOGT ("event-notification: %s: ignore", - nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); - return; - } - - if ( !is_del - && NM_IN_SET (msghdr->nlmsg_type, RTM_NEWADDR, - RTM_NEWLINK, - RTM_NEWROUTE, - RTM_NEWRULE, - RTM_NEWQDISC, - RTM_NEWTFILTER)) { - is_dump = delayed_action_refresh_all_in_progress (platform, - delayed_action_refresh_from_needle_object (obj)); - } - - _LOGT ("event-notification: %s%s: %s", - nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)), - is_dump ? ", in-dump" : "", - nmp_object_to_string (obj, - is_del ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, - NULL, 0)); - - { - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_new = NULL; - - switch (msghdr->nlmsg_type) { - - case RTM_GETLINK: - case RTM_NEWADDR: - case RTM_NEWLINK: - case RTM_NEWQDISC: - case RTM_NEWRULE: - case RTM_NEWTFILTER: - cache_op = nmp_cache_update_netlink (cache, obj, is_dump, &obj_old, &obj_new); - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - cache_on_change (platform, cache_op, obj_old, obj_new); - nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); - } - break; - - case RTM_NEWROUTE: { - nm_auto_nmpobj const NMPObject *obj_replace = NULL; - gboolean resync_required = FALSE; - gboolean only_dirty = FALSE; - gboolean is_ipv6; - - /* IPv4 routes that are a response to RTM_GETROUTE must have - * the cloned flag while IPv6 routes don't have to. */ - is_ipv6 = NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP6_ROUTE; - if (is_ipv6 || NM_FLAGS_HAS (obj->ip_route.r_rtm_flags, RTM_F_CLONED)) { - nm_assert (is_ipv6 || !nmp_object_is_alive (obj)); - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - guint i; - - nm_assert (priv->delayed_action.list_wait_for_nl_response->len > 0); - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { - DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); - - if ( data->response_type == DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET - && data->response.out_route_get) { - nm_assert (!*data->response.out_route_get); - if (data->seq_number == nlmsg_hdr (msg)->nlmsg_seq) { - *data->response.out_route_get = nmp_object_clone (obj, FALSE); - data->response.out_route_get = NULL; - break; - } - } - } - } - } - - cache_op = nmp_cache_update_netlink_route (cache, - obj, - is_dump, - msghdr->nlmsg_flags, - &obj_old, - &obj_new, - &obj_replace, - &resync_required); - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - if (obj_replace) { - const NMDedupMultiEntry *entry_replace; - - /* we found an object that is to be replaced by the RTM_NEWROUTE message. - * While we invoke the signal, the platform cache might change and invalidate - * the findings. Mitigate that (for the most part), by marking the entry as - * dirty and only delete @obj_replace if it is still dirty afterwards. - * - * Yes, there is a tiny tiny chance for still getting it wrong. But in practice, - * the signal handlers do not cause to call the platform again, so the cache - * is not really changing. -- if they would, it would anyway be dangerous to overflow - * the stack and it's not ensured that the processing of netlink messages is - * reentrant (maybe it is). - */ - entry_replace = nmp_cache_lookup_entry (cache, obj_replace); - nm_assert (entry_replace && entry_replace->obj == obj_replace); - nm_dedup_multi_entry_set_dirty (entry_replace, TRUE); - only_dirty = TRUE; - } - cache_on_change (platform, cache_op, obj_old, obj_new); - nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); - } - - if (obj_replace) { - /* the RTM_NEWROUTE message indicates that another route was replaced. - * Remove it now. */ - cache_op = nmp_cache_remove (cache, obj_replace, TRUE, only_dirty, NULL); - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); - cache_on_change (platform, cache_op, obj_replace, NULL); - nm_platform_cache_update_emit_signal (platform, cache_op, obj_replace, NULL); - } - } - - if (resync_required) { - /* we'd like to avoid such resyncs as they are expensive and we should only rely on the - * netlink events. This needs investigation. */ - _LOGT ("schedule resync of routes after RTM_NEWROUTE"); - delayed_action_schedule (platform, - delayed_action_refresh_from_needle_object (obj), - NULL); - } - break; - } - - case RTM_DELADDR: - case RTM_DELLINK: - case RTM_DELQDISC: - case RTM_DELROUTE: - case RTM_DELRULE: - case RTM_DELTFILTER: - cache_op = nmp_cache_remove_netlink (cache, obj, &obj_old, &obj_new); - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - cache_on_change (platform, cache_op, obj_old, obj_new); - nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); - } - break; - default: - break; - } - } +event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events) +{ + NMLinuxPlatformPrivate *priv; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPCacheOpsType cache_op; + struct nlmsghdr * msghdr; + char buf_nlmsghdr[400]; + gboolean is_del = FALSE; + gboolean is_dump = FALSE; + NMPCache * cache = nm_platform_get_cache(platform); + + msghdr = nlmsg_hdr(msg); + + if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS) + && msghdr->nlmsg_type == RTM_NEWADDR) { + /* IFA_FLAGS is set for IPv4 and IPv6 addresses. It was added first to IPv6, + * but if we encounter an IPv4 address with IFA_FLAGS, we surely have support. */ + if (nlmsg_valid_hdr(msghdr, sizeof(struct ifaddrmsg)) + && NM_IN_SET(((struct ifaddrmsg *) nlmsg_data(msghdr))->ifa_family, + AF_INET, + AF_INET6)) { + /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, + * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR + * and IFA_F_NOPREFIXROUTE for IPv6. They were added together in kernel 3.14, + * dated 30 March, 2014. + * + * For IPv4, IFA_F_NOPREFIXROUTE was added later, but there is no easy + * way to detect kernel support. */ + _nm_platform_kernel_support_init( + NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS, + !!nlmsg_find_attr(msghdr, sizeof(struct ifaddrmsg), IFA_FLAGS) ? 1 : -1); + } + } + + if (!handle_events) + return; + + if (NM_IN_SET(msghdr->nlmsg_type, + RTM_DELLINK, + RTM_DELADDR, + RTM_DELROUTE, + RTM_DELRULE, + RTM_DELQDISC, + RTM_DELTFILTER)) { + /* The event notifies about a deleted object. We don't need to initialize all + * fields of the object. */ + is_del = TRUE; + } + + obj = nmp_object_new_from_nl(platform, cache, msg, is_del); + if (!obj) { + _LOGT("event-notification: %s: ignore", + nl_nlmsghdr_to_str(msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr))); + return; + } + + if (!is_del + && NM_IN_SET(msghdr->nlmsg_type, + RTM_NEWADDR, + RTM_NEWLINK, + RTM_NEWROUTE, + RTM_NEWRULE, + RTM_NEWQDISC, + RTM_NEWTFILTER)) { + is_dump = + delayed_action_refresh_all_in_progress(platform, + delayed_action_refresh_from_needle_object(obj)); + } + + _LOGT("event-notification: %s%s: %s", + nl_nlmsghdr_to_str(msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)), + is_dump ? ", in-dump" : "", + nmp_object_to_string(obj, + is_del ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, + NULL, + 0)); + + { + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + + switch (msghdr->nlmsg_type) { + case RTM_GETLINK: + case RTM_NEWADDR: + case RTM_NEWLINK: + case RTM_NEWQDISC: + case RTM_NEWRULE: + case RTM_NEWTFILTER: + cache_op = nmp_cache_update_netlink(cache, obj, is_dump, &obj_old, &obj_new); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + cache_on_change(platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); + } + break; + + case RTM_NEWROUTE: + { + nm_auto_nmpobj const NMPObject *obj_replace = NULL; + gboolean resync_required = FALSE; + gboolean only_dirty = FALSE; + gboolean is_ipv6; + + /* IPv4 routes that are a response to RTM_GETROUTE must have + * the cloned flag while IPv6 routes don't have to. */ + is_ipv6 = NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP6_ROUTE; + if (is_ipv6 || NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, RTM_F_CLONED)) { + nm_assert(is_ipv6 || !nmp_object_is_alive(obj)); + priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + if (NM_FLAGS_HAS(priv->delayed_action.flags, + DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { + guint i; + + nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0); + for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { + DelayedActionWaitForNlResponseData *data = + &g_array_index(priv->delayed_action.list_wait_for_nl_response, + DelayedActionWaitForNlResponseData, + i); + + if (data->response_type == DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET + && data->response.out_route_get) { + nm_assert(!*data->response.out_route_get); + if (data->seq_number == nlmsg_hdr(msg)->nlmsg_seq) { + *data->response.out_route_get = nmp_object_clone(obj, FALSE); + data->response.out_route_get = NULL; + break; + } + } + } + } + } + + cache_op = nmp_cache_update_netlink_route(cache, + obj, + is_dump, + msghdr->nlmsg_flags, + &obj_old, + &obj_new, + &obj_replace, + &resync_required); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + if (obj_replace) { + const NMDedupMultiEntry *entry_replace; + + /* we found an object that is to be replaced by the RTM_NEWROUTE message. + * While we invoke the signal, the platform cache might change and invalidate + * the findings. Mitigate that (for the most part), by marking the entry as + * dirty and only delete @obj_replace if it is still dirty afterwards. + * + * Yes, there is a tiny tiny chance for still getting it wrong. But in practice, + * the signal handlers do not cause to call the platform again, so the cache + * is not really changing. -- if they would, it would anyway be dangerous to overflow + * the stack and it's not ensured that the processing of netlink messages is + * reentrant (maybe it is). + */ + entry_replace = nmp_cache_lookup_entry(cache, obj_replace); + nm_assert(entry_replace && entry_replace->obj == obj_replace); + nm_dedup_multi_entry_set_dirty(entry_replace, TRUE); + only_dirty = TRUE; + } + cache_on_change(platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); + } + + if (obj_replace) { + /* the RTM_NEWROUTE message indicates that another route was replaced. + * Remove it now. */ + cache_op = nmp_cache_remove(cache, obj_replace, TRUE, only_dirty, NULL); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + nm_assert(cache_op == NMP_CACHE_OPS_REMOVED); + cache_on_change(platform, cache_op, obj_replace, NULL); + nm_platform_cache_update_emit_signal(platform, cache_op, obj_replace, NULL); + } + } + + if (resync_required) { + /* we'd like to avoid such resyncs as they are expensive and we should only rely on the + * netlink events. This needs investigation. */ + _LOGT("schedule resync of routes after RTM_NEWROUTE"); + delayed_action_schedule(platform, + delayed_action_refresh_from_needle_object(obj), + NULL); + } + break; + } + + case RTM_DELADDR: + case RTM_DELLINK: + case RTM_DELQDISC: + case RTM_DELROUTE: + case RTM_DELRULE: + case RTM_DELTFILTER: + cache_op = nmp_cache_remove_netlink(cache, obj, &obj_old, &obj_new); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + cache_on_change(platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); + } + break; + default: + break; + } + } } /*****************************************************************************/ static int -do_add_link_with_lookup (NMPlatform *platform, - NMLinkType link_type, - const char *name, - struct nl_msg *nlmsg, - const NMPlatformLink **out_link) -{ - const NMPObject *obj = NULL; - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - gs_free char *errmsg = NULL; - int nle; - char s_buf[256]; - NMPCache *cache = nm_platform_get_cache (platform); - - event_handler_read_netlink (platform, FALSE); - - nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); - if (nle < 0) { - _LOGE ("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)", - name, - nm_link_type_to_string (link_type), - nm_strerror (nle), -nle); - NM_SET_OUT (out_link, NULL); - return nle; - } - - delayed_action_handle_all (platform, FALSE); - - nm_assert (seq_result); - - _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK - ? LOGL_DEBUG - : LOGL_WARN, - "do-add-link[%s/%s]: %s", - name, - nm_link_type_to_string (link_type), - wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); - - if (out_link) { - obj = nmp_cache_lookup_link_full (cache, 0, name, FALSE, link_type, NULL, NULL); - *out_link = NMP_OBJECT_CAST_LINK (obj); - } - - return wait_for_nl_response_to_nmerr (seq_result); +do_add_link_with_lookup(NMPlatform * platform, + NMLinkType link_type, + const char * name, + struct nl_msg * nlmsg, + const NMPlatformLink **out_link) +{ + const NMPObject * obj = NULL; + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + gs_free char * errmsg = NULL; + int nle; + char s_buf[256]; + NMPCache * cache = nm_platform_get_cache(platform); + + event_handler_read_netlink(platform, FALSE); + + nle = _nl_send_nlmsg(platform, + nlmsg, + &seq_result, + &errmsg, + DELAYED_ACTION_RESPONSE_TYPE_VOID, + NULL); + if (nle < 0) { + _LOGE("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)", + name, + nm_link_type_to_string(link_type), + nm_strerror(nle), + -nle); + NM_SET_OUT(out_link, NULL); + return nle; + } + + delayed_action_handle_all(platform, FALSE); + + nm_assert(seq_result); + + _NMLOG(seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG : LOGL_WARN, + "do-add-link[%s/%s]: %s", + name, + nm_link_type_to_string(link_type), + wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf))); + + if (out_link) { + obj = nmp_cache_lookup_link_full(cache, 0, name, FALSE, link_type, NULL, NULL); + *out_link = NMP_OBJECT_CAST_LINK(obj); + } + + return wait_for_nl_response_to_nmerr(seq_result); } static int -do_add_addrroute (NMPlatform *platform, - const NMPObject *obj_id, - struct nl_msg *nlmsg, - gboolean suppress_netlink_failure) -{ - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - gs_free char *errmsg = NULL; - int nle; - char s_buf[256]; - - nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), - NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS, - NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - - event_handler_read_netlink (platform, FALSE); - - nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); - if (nle < 0) { - _LOGE ("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", - NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, - nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), - nm_strerror (nle), -nle); - return -NME_PL_NETLINK; - } - - delayed_action_handle_all (platform, FALSE); - - nm_assert (seq_result); - - _NMLOG (( seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK - || ( suppress_netlink_failure - && seq_result < 0)) - ? LOGL_DEBUG - : LOGL_WARN, - "do-add-%s[%s]: %s", - NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, - nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), - wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); - - if (NMP_OBJECT_GET_TYPE (obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { - /* In rare cases, the object is not yet ready as we received the ACK from - * kernel. Need to refetch. - * - * We want to safe the expensive refetch, thus we look first into the cache - * whether the object exists. - * - * rh#1484434 */ - if (!nmp_cache_lookup_obj (nm_platform_get_cache (platform), obj_id)) - do_request_one_type_by_needle_object (platform, obj_id); - } - - return wait_for_nl_response_to_nmerr (seq_result); +do_add_addrroute(NMPlatform * platform, + const NMPObject *obj_id, + struct nl_msg * nlmsg, + gboolean suppress_netlink_failure) +{ + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + gs_free char * errmsg = NULL; + int nle; + char s_buf[256]; + + nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), + NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + + event_handler_read_netlink(platform, FALSE); + + nle = _nl_send_nlmsg(platform, + nlmsg, + &seq_result, + &errmsg, + DELAYED_ACTION_RESPONSE_TYPE_VOID, + NULL); + if (nle < 0) { + _LOGE("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", + NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, + nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nm_strerror(nle), + -nle); + return -NME_PL_NETLINK; + } + + delayed_action_handle_all(platform, FALSE); + + nm_assert(seq_result); + + _NMLOG((seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + || (suppress_netlink_failure && seq_result < 0)) + ? LOGL_DEBUG + : LOGL_WARN, + "do-add-%s[%s]: %s", + NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, + nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf))); + + if (NMP_OBJECT_GET_TYPE(obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { + /* In rare cases, the object is not yet ready as we received the ACK from + * kernel. Need to refetch. + * + * We want to safe the expensive refetch, thus we look first into the cache + * whether the object exists. + * + * rh#1484434 */ + if (!nmp_cache_lookup_obj(nm_platform_get_cache(platform), obj_id)) + do_request_one_type_by_needle_object(platform, obj_id); + } + + return wait_for_nl_response_to_nmerr(seq_result); } static gboolean -do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) -{ - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - gs_free char *errmsg = NULL; - int nle; - char s_buf[256]; - gboolean success; - const char *log_detail = ""; - - event_handler_read_netlink (platform, FALSE); - - nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); - if (nle < 0) { - _LOGE ("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", - NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, - nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), - nm_strerror (nle), -nle); - return FALSE; - } - - delayed_action_handle_all (platform, FALSE); - - nm_assert (seq_result); - - success = TRUE; - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { - /* ok */ - } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) - log_detail = ", meaning the object was already removed"; - else if ( NM_IN_SET (-((int) seq_result), ENXIO) - && NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), NMP_OBJECT_TYPE_IP6_ADDRESS)) { - /* On RHEL7 kernel, deleting a non existing address fails with ENXIO */ - log_detail = ", meaning the address was already removed"; - } else if ( NM_IN_SET (-((int) seq_result), EADDRNOTAVAIL) - && NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)) - log_detail = ", meaning the address was already removed"; - else - success = FALSE; - - _NMLOG (success ? LOGL_DEBUG : LOGL_WARN, - "do-delete-%s[%s]: %s%s", - NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, - nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), - wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf)), - log_detail); - - if (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), - NMP_OBJECT_TYPE_IP6_ADDRESS, - NMP_OBJECT_TYPE_QDISC, - NMP_OBJECT_TYPE_TFILTER)) { - /* In rare cases, the object is still there after we receive the ACK from - * kernel. Need to refetch. - * - * We want to safe the expensive refetch, thus we look first into the cache - * whether the object exists. - * - * rh#1484434 */ - if (nmp_cache_lookup_obj (nm_platform_get_cache (platform), obj_id)) - do_request_one_type_by_needle_object (platform, obj_id); - } - - return success; +do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) +{ + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + gs_free char * errmsg = NULL; + int nle; + char s_buf[256]; + gboolean success; + const char * log_detail = ""; + + event_handler_read_netlink(platform, FALSE); + + nle = _nl_send_nlmsg(platform, + nlmsg, + &seq_result, + &errmsg, + DELAYED_ACTION_RESPONSE_TYPE_VOID, + NULL); + if (nle < 0) { + _LOGE("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", + NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, + nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nm_strerror(nle), + -nle); + return FALSE; + } + + delayed_action_handle_all(platform, FALSE); + + nm_assert(seq_result); + + success = TRUE; + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { + /* ok */ + } else if (NM_IN_SET(-((int) seq_result), ESRCH, ENOENT)) + log_detail = ", meaning the object was already removed"; + else if (NM_IN_SET(-((int) seq_result), ENXIO) + && NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), NMP_OBJECT_TYPE_IP6_ADDRESS)) { + /* On RHEL7 kernel, deleting a non existing address fails with ENXIO */ + log_detail = ", meaning the address was already removed"; + } else if (NM_IN_SET(-((int) seq_result), EADDRNOTAVAIL) + && NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), + NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS)) + log_detail = ", meaning the address was already removed"; + else + success = FALSE; + + _NMLOG(success ? LOGL_DEBUG : LOGL_WARN, + "do-delete-%s[%s]: %s%s", + NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, + nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)), + log_detail); + + if (NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_QDISC, + NMP_OBJECT_TYPE_TFILTER)) { + /* In rare cases, the object is still there after we receive the ACK from + * kernel. Need to refetch. + * + * We want to safe the expensive refetch, thus we look first into the cache + * whether the object exists. + * + * rh#1484434 */ + if (nmp_cache_lookup_obj(nm_platform_get_cache(platform), obj_id)) + do_request_one_type_by_needle_object(platform, obj_id); + } + + return success; } static int -do_change_link (NMPlatform *platform, - ChangeLinkType change_link_type, - int ifindex, - struct nl_msg *nlmsg, - const ChangeLinkData *data) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - int nle; - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - gs_free char *errmsg = NULL; - char s_buf[256]; - int result = 0; - NMLogLevel log_level = LOGL_DEBUG; - const char *log_result = "failure"; - const char *log_detail = ""; - gs_free char *log_detail_free = NULL; - const NMPObject *obj_cache; - - if (!nm_platform_netns_push (platform, &netns)) { - log_level = LOGL_ERR; - log_detail = ", failure to change network namespace"; - goto out; - } +do_change_link(NMPlatform * platform, + ChangeLinkType change_link_type, + int ifindex, + struct nl_msg * nlmsg, + const ChangeLinkData *data) +{ + nm_auto_pop_netns NMPNetns *netns = NULL; + int nle; + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + gs_free char * errmsg = NULL; + char s_buf[256]; + int result = 0; + NMLogLevel log_level = LOGL_DEBUG; + const char * log_result = "failure"; + const char * log_detail = ""; + gs_free char * log_detail_free = NULL; + const NMPObject * obj_cache; + + if (!nm_platform_netns_push(platform, &netns)) { + log_level = LOGL_ERR; + log_detail = ", failure to change network namespace"; + goto out; + } retry: - nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); - if (nle < 0) { - log_level = LOGL_ERR; - log_detail_free = g_strdup_printf (", failure sending netlink request: %s (%d)", - nm_strerror (nle), -nle); - log_detail = log_detail_free; - goto out; - } - - /* always refetch the link after changing it. There seems to be issues - * and we sometimes lack events. Nuke it from the orbit... */ - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex)); - - delayed_action_handle_all (platform, FALSE); - - nm_assert (seq_result); - - if ( NM_IN_SET (-((int) seq_result), EOPNOTSUPP) - && nlmsg_hdr (nlmsg)->nlmsg_type == RTM_NEWLINK) { - nlmsg_hdr (nlmsg)->nlmsg_type = RTM_SETLINK; - goto retry; - } - - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { - log_result = "success"; - } else if (NM_IN_SET (-((int) seq_result), EEXIST, EADDRINUSE)) { - /* */ - } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) { - log_detail = ", firmware not found"; - result = -NME_PL_NO_FIRMWARE; - } else if ( NM_IN_SET (-((int) seq_result), ERANGE) - && change_link_type == CHANGE_LINK_TYPE_SET_MTU) { - log_detail = ", setting MTU to requested size is not possible"; - result = -NME_PL_CANT_SET_MTU; - } else if ( NM_IN_SET (-((int) seq_result), ENFILE) - && change_link_type == CHANGE_LINK_TYPE_SET_ADDRESS - && (obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex)) - && obj_cache->link.l_address.len == data->set_address.length - && memcmp (obj_cache->link.l_address.data, data->set_address.address, data->set_address.length) == 0) { - /* workaround ENFILE which may be wrongly returned (bgo #770456). - * If the MAC address is as expected, assume success? */ - log_result = "success"; - log_detail = " (assume success changing address)"; - result = 0; - } else if (NM_IN_SET (-((int) seq_result), ENODEV)) { - log_level = LOGL_DEBUG; - result = -NME_PL_NOT_FOUND; - } else if (-((int) seq_result) == EAFNOSUPPORT) { - log_level = LOGL_DEBUG; - result = -NME_PL_OPNOTSUPP; - } else { - log_level = LOGL_WARN; - result = -NME_UNSPEC; - } + nle = _nl_send_nlmsg(platform, + nlmsg, + &seq_result, + &errmsg, + DELAYED_ACTION_RESPONSE_TYPE_VOID, + NULL); + if (nle < 0) { + log_level = LOGL_ERR; + log_detail_free = + g_strdup_printf(", failure sending netlink request: %s (%d)", nm_strerror(nle), -nle); + log_detail = log_detail_free; + goto out; + } + + /* always refetch the link after changing it. There seems to be issues + * and we sometimes lack events. Nuke it from the orbit... */ + delayed_action_schedule(platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER(ifindex)); + + delayed_action_handle_all(platform, FALSE); + + nm_assert(seq_result); + + if (NM_IN_SET(-((int) seq_result), EOPNOTSUPP) && nlmsg_hdr(nlmsg)->nlmsg_type == RTM_NEWLINK) { + nlmsg_hdr(nlmsg)->nlmsg_type = RTM_SETLINK; + goto retry; + } + + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { + log_result = "success"; + } else if (NM_IN_SET(-((int) seq_result), EEXIST, EADDRINUSE)) { + /* */ + } else if (NM_IN_SET(-((int) seq_result), ESRCH, ENOENT)) { + log_detail = ", firmware not found"; + result = -NME_PL_NO_FIRMWARE; + } else if (NM_IN_SET(-((int) seq_result), ERANGE) + && change_link_type == CHANGE_LINK_TYPE_SET_MTU) { + log_detail = ", setting MTU to requested size is not possible"; + result = -NME_PL_CANT_SET_MTU; + } else if (NM_IN_SET(-((int) seq_result), ENFILE) + && change_link_type == CHANGE_LINK_TYPE_SET_ADDRESS + && (obj_cache = nmp_cache_lookup_link(nm_platform_get_cache(platform), ifindex)) + && obj_cache->link.l_address.len == data->set_address.length + && memcmp(obj_cache->link.l_address.data, + data->set_address.address, + data->set_address.length) + == 0) { + /* workaround ENFILE which may be wrongly returned (bgo #770456). + * If the MAC address is as expected, assume success? */ + log_result = "success"; + log_detail = " (assume success changing address)"; + result = 0; + } else if (NM_IN_SET(-((int) seq_result), ENODEV)) { + log_level = LOGL_DEBUG; + result = -NME_PL_NOT_FOUND; + } else if (-((int) seq_result) == EAFNOSUPPORT) { + log_level = LOGL_DEBUG; + result = -NME_PL_OPNOTSUPP; + } else { + log_level = LOGL_WARN; + result = -NME_UNSPEC; + } out: - _NMLOG (log_level, - "do-change-link[%d]: %s changing link: %s%s", - ifindex, - log_result, - wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf)), - log_detail); - return result; + _NMLOG(log_level, + "do-change-link[%d]: %s changing link: %s%s", + ifindex, + log_result, + wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)), + log_detail); + return result; } static int -link_add (NMPlatform *platform, - NMLinkType type, - const char *name, - int parent, - const void *address, - size_t address_len, - gconstpointer extra_data, - const NMPlatformLink **out_link) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - - if (type == NM_LINK_TYPE_BOND) { - /* 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)) - (void) nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL); - } - - nlmsg = _nl_msg_new_link (RTM_NEWLINK, - NLM_F_CREATE | NLM_F_EXCL, - 0, - name); - if (!nlmsg) - return -NME_UNSPEC; - - if (parent > 0) - NLA_PUT_U32 (nlmsg, IFLA_LINK, parent); - - if (address && address_len) - NLA_PUT (nlmsg, IFLA_ADDRESS, address_len, address); - - if (!_nl_msg_new_link_set_linkinfo (nlmsg, type, extra_data)) - return -NME_UNSPEC; - - return do_add_link_with_lookup (platform, type, name, nlmsg, out_link); +link_add(NMPlatform * platform, + NMLinkType type, + const char * name, + int parent, + const void * address, + size_t address_len, + gconstpointer extra_data, + const NMPlatformLink **out_link) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + + if (type == NM_LINK_TYPE_BOND) { + /* 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)) + (void) nm_utils_modprobe(NULL, TRUE, "bonding", "max_bonds=0", NULL); + } + + nlmsg = _nl_msg_new_link(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, name); + if (!nlmsg) + return -NME_UNSPEC; + + if (parent > 0) + NLA_PUT_U32(nlmsg, IFLA_LINK, parent); + + if (address && address_len) + NLA_PUT(nlmsg, IFLA_ADDRESS, address_len, address); + + if (!_nl_msg_new_link_set_linkinfo(nlmsg, type, extra_data)) + return -NME_UNSPEC; + + return do_add_link_with_lookup(platform, type, name, nlmsg, out_link); nla_put_failure: - g_return_val_if_reached (-NME_BUG); + g_return_val_if_reached(-NME_BUG); } static gboolean -link_delete (NMPlatform *platform, int ifindex) +link_delete(NMPlatform *platform, int ifindex) { - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj_id; - const NMPObject *obj; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj_id; + const NMPObject * obj; - obj = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); - if (!obj || !obj->_link.netlink.is_in_netlink) - return FALSE; + obj = nmp_cache_lookup_link(nm_platform_get_cache(platform), ifindex); + if (!obj || !obj->_link.netlink.is_in_netlink) + return FALSE; - nlmsg = _nl_msg_new_link (RTM_DELLINK, - 0, - ifindex, - NULL); + nlmsg = _nl_msg_new_link(RTM_DELLINK, 0, ifindex, NULL); - nmp_object_stackinit_id_link (&obj_id, ifindex); - return do_delete_object (platform, &obj_id, nlmsg); + nmp_object_stackinit_id_link(&obj_id, ifindex); + return do_delete_object(platform, &obj_id, nlmsg); } static gboolean -link_refresh (NMPlatform *platform, int ifindex) +link_refresh(NMPlatform *platform, int ifindex) { - do_request_link (platform, ifindex, NULL); - return !!nm_platform_link_get_obj (platform, ifindex, TRUE); + do_request_link(platform, ifindex, NULL); + return !!nm_platform_link_get_obj(platform, ifindex, TRUE); } static gboolean -link_set_netns (NMPlatform *platform, - int ifindex, - int netns_fd) +link_set_netns(NMPlatform *platform, int ifindex, int netns_fd) { - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - nlmsg = _nl_msg_new_link (RTM_NEWLINK, - 0, - ifindex, - NULL); - if (!nlmsg) - return FALSE; + nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); + if (!nlmsg) + return FALSE; - NLA_PUT (nlmsg, IFLA_NET_NS_FD, 4, &netns_fd); - return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); + NLA_PUT(nlmsg, IFLA_NET_NS_FD, 4, &netns_fd); + return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); nla_put_failure: - g_return_val_if_reached (FALSE); + g_return_val_if_reached(FALSE); } static int -link_change_flags (NMPlatform *platform, - int ifindex, - unsigned flags_mask, - unsigned flags_set) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - char s_flags[100]; - - _LOGD ("link: change %d: flags: set 0x%x/0x%x ([%s] / [%s])", - ifindex, - flags_set, - flags_mask, - nm_platform_link_flags2str (flags_set, s_flags, sizeof (s_flags)), - nm_platform_link_flags2str (flags_mask, NULL, 0)); - - nlmsg = _nl_msg_new_link_full (RTM_NEWLINK, - 0, - ifindex, - NULL, - AF_UNSPEC, - flags_mask, - flags_set); - if (!nlmsg) - return -NME_UNSPEC; - return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); +link_change_flags(NMPlatform *platform, int ifindex, unsigned flags_mask, unsigned flags_set) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + char s_flags[100]; + + _LOGD("link: change %d: flags: set 0x%x/0x%x ([%s] / [%s])", + ifindex, + flags_set, + flags_mask, + nm_platform_link_flags2str(flags_set, s_flags, sizeof(s_flags)), + nm_platform_link_flags2str(flags_mask, NULL, 0)); + + nlmsg = _nl_msg_new_link_full(RTM_NEWLINK, 0, ifindex, NULL, AF_UNSPEC, flags_mask, flags_set); + if (!nlmsg) + return -NME_UNSPEC; + return do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); } static gboolean -link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) +link_set_up(NMPlatform *platform, int ifindex, gboolean *out_no_firmware) { - int r; + int r; - r = link_change_flags (platform, ifindex, IFF_UP, IFF_UP); - NM_SET_OUT (out_no_firmware, (r == -NME_PL_NO_FIRMWARE)); - return r >= 0; + r = link_change_flags(platform, ifindex, IFF_UP, IFF_UP); + NM_SET_OUT(out_no_firmware, (r == -NME_PL_NO_FIRMWARE)); + return r >= 0; } static gboolean -link_set_down (NMPlatform *platform, int ifindex) +link_set_down(NMPlatform *platform, int ifindex) { - return (link_change_flags (platform, ifindex, IFF_UP, 0) >= 0); + return (link_change_flags(platform, ifindex, IFF_UP, 0) >= 0); } static gboolean -link_set_arp (NMPlatform *platform, int ifindex) +link_set_arp(NMPlatform *platform, int ifindex) { - return (link_change_flags (platform, ifindex, IFF_NOARP, 0) >= 0); + return (link_change_flags(platform, ifindex, IFF_NOARP, 0) >= 0); } static gboolean -link_set_noarp (NMPlatform *platform, int ifindex) +link_set_noarp(NMPlatform *platform, int ifindex) { - return (link_change_flags (platform, ifindex, IFF_NOARP, IFF_NOARP) >= 0); + return (link_change_flags(platform, ifindex, IFF_NOARP, IFF_NOARP) >= 0); } static int -link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled) +link_set_user_ipv6ll_enabled(NMPlatform *platform, int ifindex, gboolean enabled) { - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - guint8 mode = enabled ? NM_IN6_ADDR_GEN_MODE_NONE : NM_IN6_ADDR_GEN_MODE_EUI64; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + guint8 mode = enabled ? NM_IN6_ADDR_GEN_MODE_NONE : NM_IN6_ADDR_GEN_MODE_EUI64; - _LOGD ("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", - ifindex, - nm_platform_link_inet6_addrgenmode2str (mode, NULL, 0)); + _LOGD("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", + ifindex, + nm_platform_link_inet6_addrgenmode2str(mode, NULL, 0)); - if (!nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) { - _LOGD ("link: change %d: user-ipv6ll: not supported", ifindex); - return -NME_PL_OPNOTSUPP; - } + if (!nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) { + _LOGD("link: change %d: user-ipv6ll: not supported", ifindex); + return -NME_PL_OPNOTSUPP; + } - nlmsg = _nl_msg_new_link (RTM_NEWLINK, - 0, - ifindex, - NULL); - if ( !nlmsg - || !_nl_msg_new_link_set_afspec (nlmsg, mode, NULL)) - g_return_val_if_reached (-NME_BUG); + nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); + if (!nlmsg || !_nl_msg_new_link_set_afspec(nlmsg, mode, NULL)) + g_return_val_if_reached(-NME_BUG); - return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); + return do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); } static gboolean -link_set_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid) +link_set_token(NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid) { - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - _LOGD ("link: change %d: token: set IPv6 address generation token to %s", - ifindex, nm_utils_inet6_interface_identifier_to_token (iid, sbuf)); + _LOGD("link: change %d: token: set IPv6 address generation token to %s", + ifindex, + nm_utils_inet6_interface_identifier_to_token(iid, sbuf)); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, NULL); - if (!nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, -1, &iid)) - g_return_val_if_reached (FALSE); + nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); + if (!nlmsg || !_nl_msg_new_link_set_afspec(nlmsg, -1, &iid)) + g_return_val_if_reached(FALSE); - return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); + return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); } static gboolean -link_supports_carrier_detect (NMPlatform *platform, int ifindex) +link_supports_carrier_detect(NMPlatform *platform, int ifindex) { - nm_auto_pop_netns NMPNetns *netns = NULL; + nm_auto_pop_netns NMPNetns *netns = NULL; - if (!nm_platform_netns_push (platform, &netns)) - return FALSE; + if (!nm_platform_netns_push(platform, &netns)) + return FALSE; - /* We use netlink for the actual carrier detection, but netlink can't tell - * us whether the device actually supports carrier detection in the first - * place. We assume any device that does implements one of these two APIs. - */ - return nmp_utils_ethtool_supports_carrier_detect (ifindex) - || nmp_utils_mii_supports_carrier_detect (ifindex); + /* We use netlink for the actual carrier detection, but netlink can't tell + * us whether the device actually supports carrier detection in the first + * place. We assume any device that does implements one of these two APIs. + */ + return nmp_utils_ethtool_supports_carrier_detect(ifindex) + || nmp_utils_mii_supports_carrier_detect(ifindex); } static gboolean -link_supports_vlans (NMPlatform *platform, int ifindex) +link_supports_vlans(NMPlatform *platform, int ifindex) { - nm_auto_pop_netns NMPNetns *netns = NULL; - const NMPObject *obj; + nm_auto_pop_netns NMPNetns *netns = NULL; + const NMPObject * obj; - obj = nm_platform_link_get_obj (platform, ifindex, TRUE); + obj = nm_platform_link_get_obj(platform, ifindex, TRUE); - /* Only ARPHRD_ETHER links can possibly support VLANs. */ - if (!obj || obj->link.arptype != ARPHRD_ETHER) - return FALSE; + /* Only ARPHRD_ETHER links can possibly support VLANs. */ + if (!obj || obj->link.arptype != ARPHRD_ETHER) + return FALSE; - if (!nm_platform_netns_push (platform, &netns)) - return FALSE; + if (!nm_platform_netns_push(platform, &netns)) + return FALSE; - return nmp_utils_ethtool_supports_vlans (ifindex); + return nmp_utils_ethtool_supports_vlans(ifindex); } static gboolean -link_supports_sriov (NMPlatform *platform, int ifindex) +link_supports_sriov(NMPlatform *platform, int ifindex) { - nm_auto_pop_netns NMPNetns *netns = NULL; - nm_auto_close int dirfd = -1; - char ifname[IFNAMSIZ]; - int num = -1; + nm_auto_pop_netns NMPNetns *netns = NULL; + nm_auto_close int dirfd = -1; + char ifname[IFNAMSIZ]; + int num = -1; - if (!nm_platform_netns_push (platform, &netns)) - return FALSE; + if (!nm_platform_netns_push(platform, &netns)) + return FALSE; - dirfd = nm_platform_sysctl_open_netdir (platform, ifindex, ifname); - if (dirfd < 0) - return FALSE; + dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname); + if (dirfd < 0) + return FALSE; - num = nm_platform_sysctl_get_int32 (platform, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_numvfs"), - -1); + num = + nm_platform_sysctl_get_int32(platform, + NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_numvfs"), + -1); - return num != -1; + return num != -1; } static int -link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size_t length) +link_set_address(NMPlatform *platform, int ifindex, gconstpointer address, size_t length) { - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - const ChangeLinkData d = { - .set_address = { - .address = address, - .length = length, - }, - }; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + const ChangeLinkData d = { + .set_address = + { + .address = address, + .length = length, + }, + }; - if (!address || !length) - g_return_val_if_reached (-NME_BUG); + if (!address || !length) + g_return_val_if_reached(-NME_BUG); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, - 0, - ifindex, - NULL); - if (!nlmsg) - g_return_val_if_reached (-NME_BUG); + nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); + if (!nlmsg) + g_return_val_if_reached(-NME_BUG); - NLA_PUT (nlmsg, IFLA_ADDRESS, length, address); + NLA_PUT(nlmsg, IFLA_ADDRESS, length, address); - return do_change_link (platform, CHANGE_LINK_TYPE_SET_ADDRESS, ifindex, nlmsg, &d); + return do_change_link(platform, CHANGE_LINK_TYPE_SET_ADDRESS, ifindex, nlmsg, &d); nla_put_failure: - g_return_val_if_reached (-NME_BUG); + g_return_val_if_reached(-NME_BUG); } static int -link_set_name (NMPlatform *platform, int ifindex, const char *name) +link_set_name(NMPlatform *platform, int ifindex, const char *name) { - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - nlmsg = _nl_msg_new_link (RTM_NEWLINK, - 0, - ifindex, - NULL); - if (!nlmsg) - g_return_val_if_reached (-NME_BUG); + nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); + if (!nlmsg) + g_return_val_if_reached(-NME_BUG); - NLA_PUT (nlmsg, IFLA_IFNAME, strlen (name) + 1, name); + NLA_PUT(nlmsg, IFLA_IFNAME, strlen(name) + 1, name); - return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); + return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); nla_put_failure: - g_return_val_if_reached (FALSE); + g_return_val_if_reached(FALSE); } static gboolean -link_get_permanent_address (NMPlatform *platform, - int ifindex, - guint8 *buf, - size_t *length) +link_get_permanent_address(NMPlatform *platform, int ifindex, guint8 *buf, size_t *length) { - nm_auto_pop_netns NMPNetns *netns = NULL; + nm_auto_pop_netns NMPNetns *netns = NULL; - if (!nm_platform_netns_push (platform, &netns)) - return FALSE; + if (!nm_platform_netns_push(platform, &netns)) + return FALSE; - return nmp_utils_ethtool_get_permanent_address (ifindex, buf, length); + return nmp_utils_ethtool_get_permanent_address(ifindex, buf, length); } static int -link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) +link_set_mtu(NMPlatform *platform, int ifindex, guint32 mtu) { - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - nlmsg = _nl_msg_new_link (RTM_NEWLINK, - 0, - ifindex, - NULL); - if (!nlmsg) - return FALSE; + nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); + if (!nlmsg) + return FALSE; - NLA_PUT_U32 (nlmsg, IFLA_MTU, mtu); + NLA_PUT_U32(nlmsg, IFLA_MTU, mtu); - return do_change_link (platform, CHANGE_LINK_TYPE_SET_MTU, ifindex, nlmsg, NULL); + return do_change_link(platform, CHANGE_LINK_TYPE_SET_MTU, ifindex, nlmsg, NULL); nla_put_failure: - g_return_val_if_reached (FALSE); + g_return_val_if_reached(FALSE); } static void -sriov_idle_cb (gpointer user_data, - GCancellable *cancellable) +sriov_idle_cb(gpointer user_data, GCancellable *cancellable) { - gs_unref_object NMPlatform *platform = NULL; - gs_free_error GError *cancelled_error = NULL; - gs_free_error GError *error = NULL; - NMPlatformAsyncCallback callback; - gpointer callback_data; + gs_unref_object NMPlatform *platform = NULL; + gs_free_error GError *cancelled_error = NULL; + gs_free_error GError * error = NULL; + NMPlatformAsyncCallback callback; + gpointer callback_data; - g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error); - nm_utils_user_data_unpack (user_data, &platform, &error, &callback, &callback_data); - callback (cancelled_error ?: error, callback_data); + g_cancellable_set_error_if_cancelled(cancellable, &cancelled_error); + nm_utils_user_data_unpack(user_data, &platform, &error, &callback, &callback_data); + callback(cancelled_error ?: error, callback_data); } static void -link_set_sriov_params_async (NMPlatform *platform, - int ifindex, - guint num_vfs, - NMTernary autoprobe, - NMPlatformAsyncCallback callback, - gpointer data, - GCancellable *cancellable) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - gs_free_error GError *error = NULL; - nm_auto_close int dirfd = -1; - int current_autoprobe; - guint i, total; - gint64 current_num; - char ifname[IFNAMSIZ]; - gpointer packed; - const char *values[3]; - char buf[64]; - - g_return_if_fail (callback || !data); - g_return_if_fail (cancellable); - - if (!nm_platform_netns_push (platform, &netns)) { - g_set_error_literal (&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "couldn't change namespace"); - goto out_idle; - } - - dirfd = nm_platform_sysctl_open_netdir (platform, ifindex, ifname); - if (!dirfd) { - g_set_error_literal (&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "couldn't open netdir"); - goto out_idle; - } - - total = nm_platform_sysctl_get_int_checked (platform, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_totalvfs"), - 10, 0, G_MAXUINT, 0); - if (!errno && num_vfs > total) { - _LOGW ("link: %d only supports %u VFs (requested %u)", ifindex, total, num_vfs); - num_vfs = total; - } - - /* - * Take special care when setting new values: - * - don't touch anything if the right values are already set - * - to change the number of VFs or autoprobe we need to destroy existing VFs - * - the autoprobe setting is irrelevant when numvfs is zero - */ - current_num = nm_platform_sysctl_get_int_checked (platform, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_numvfs"), - 10, 0, G_MAXUINT, -1); - current_autoprobe = nm_platform_sysctl_get_int_checked (platform, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_drivers_autoprobe"), - 10, 0, 1, -1); - - if ( current_autoprobe == -1 - && errno == ENOENT) { - /* older kernel versions don't have this sysctl. Assume the value is - * "1". */ - current_autoprobe = 1; - } - - if ( current_num == num_vfs - && (autoprobe == NM_TERNARY_DEFAULT || current_autoprobe == autoprobe)) - goto out_idle; - - if ( NM_IN_SET (autoprobe, NM_TERNARY_TRUE, NM_TERNARY_FALSE) - && current_autoprobe != autoprobe - && !nm_platform_sysctl_set (NM_PLATFORM_GET, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_drivers_autoprobe"), - nm_sprintf_buf (buf, "%d", (int) autoprobe))) { - g_set_error (&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "couldn't set SR-IOV drivers-autoprobe to %d: %s", - (int) autoprobe, nm_strerror_native (errno)); - goto out_idle; - } - - if (current_num == 0 && num_vfs == 0) - goto out_idle; - - i = 0; - if (current_num != 0) - values[i++] = "0"; - if (num_vfs != 0) - values[i++] = nm_sprintf_bufa (32, "%u", num_vfs); - values[i++] = NULL; - - sysctl_set_async (platform, - NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_numvfs"), - values, - callback, - data, - cancellable); - return; +link_set_sriov_params_async(NMPlatform * platform, + int ifindex, + guint num_vfs, + NMTernary autoprobe, + NMPlatformAsyncCallback callback, + gpointer data, + GCancellable * cancellable) +{ + nm_auto_pop_netns NMPNetns *netns = NULL; + gs_free_error GError *error = NULL; + nm_auto_close int dirfd = -1; + int current_autoprobe; + guint i, total; + gint64 current_num; + char ifname[IFNAMSIZ]; + gpointer packed; + const char * values[3]; + char buf[64]; + + g_return_if_fail(callback || !data); + g_return_if_fail(cancellable); + + if (!nm_platform_netns_push(platform, &netns)) { + g_set_error_literal(&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "couldn't change namespace"); + goto out_idle; + } + + dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname); + if (!dirfd) { + g_set_error_literal(&error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "couldn't open netdir"); + goto out_idle; + } + + total = nm_platform_sysctl_get_int_checked( + platform, + NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_totalvfs"), + 10, + 0, + G_MAXUINT, + 0); + if (!errno && num_vfs > total) { + _LOGW("link: %d only supports %u VFs (requested %u)", ifindex, total, num_vfs); + num_vfs = total; + } + + /* + * Take special care when setting new values: + * - don't touch anything if the right values are already set + * - to change the number of VFs or autoprobe we need to destroy existing VFs + * - the autoprobe setting is irrelevant when numvfs is zero + */ + current_num = nm_platform_sysctl_get_int_checked( + platform, + NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_numvfs"), + 10, + 0, + G_MAXUINT, + -1); + current_autoprobe = nm_platform_sysctl_get_int_checked( + platform, + NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_drivers_autoprobe"), + 10, + 0, + 1, + -1); + + if (current_autoprobe == -1 && errno == ENOENT) { + /* older kernel versions don't have this sysctl. Assume the value is + * "1". */ + current_autoprobe = 1; + } + + if (current_num == num_vfs + && (autoprobe == NM_TERNARY_DEFAULT || current_autoprobe == autoprobe)) + goto out_idle; + + if (NM_IN_SET(autoprobe, NM_TERNARY_TRUE, NM_TERNARY_FALSE) && current_autoprobe != autoprobe + && !nm_platform_sysctl_set( + NM_PLATFORM_GET, + NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_drivers_autoprobe"), + nm_sprintf_buf(buf, "%d", (int) autoprobe))) { + g_set_error(&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "couldn't set SR-IOV drivers-autoprobe to %d: %s", + (int) autoprobe, + nm_strerror_native(errno)); + goto out_idle; + } + + if (current_num == 0 && num_vfs == 0) + goto out_idle; + + i = 0; + if (current_num != 0) + values[i++] = "0"; + if (num_vfs != 0) + values[i++] = nm_sprintf_bufa(32, "%u", num_vfs); + values[i++] = NULL; + + sysctl_set_async(platform, + NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_numvfs"), + values, + callback, + data, + cancellable); + return; out_idle: - if (callback) { - packed = nm_utils_user_data_pack (g_object_ref (platform), - g_steal_pointer (&error), - callback, - data); - nm_utils_invoke_on_idle (cancellable, sriov_idle_cb, packed); - } + if (callback) { + packed = nm_utils_user_data_pack(g_object_ref(platform), + g_steal_pointer(&error), + callback, + data); + nm_utils_invoke_on_idle(cancellable, sriov_idle_cb, packed); + } } static gboolean -link_set_sriov_vfs (NMPlatform *platform, int ifindex, const NMPlatformVF *const *vfs) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - struct nlattr *list, *info, *vlan_list; - guint i; - - nlmsg = _nl_msg_new_link (RTM_NEWLINK, - 0, - ifindex, - NULL); - if (!nlmsg) - g_return_val_if_reached (-NME_BUG); - - if (!(list = nla_nest_start (nlmsg, IFLA_VFINFO_LIST))) - goto nla_put_failure; - - for (i = 0; vfs[i]; i++) { - const NMPlatformVF *vf = vfs[i]; - - if (!(info = nla_nest_start (nlmsg, IFLA_VF_INFO))) - goto nla_put_failure; - - if (vf->spoofchk >= 0) { - struct _ifla_vf_setting ivs = { 0 }; - - ivs.vf = vf->index; - ivs.setting = vf->spoofchk; - NLA_PUT (nlmsg, IFLA_VF_SPOOFCHK, sizeof (ivs), &ivs); - } - - if (vf->trust >= 0) { - struct _ifla_vf_setting ivs = { 0 }; - - ivs.vf = vf->index; - ivs.setting = vf->trust; - NLA_PUT (nlmsg, IFLA_VF_TRUST, sizeof (ivs), &ivs); - } - - if (vf->mac.len) { - struct ifla_vf_mac ivm = { 0 }; - - ivm.vf = vf->index; - memcpy (ivm.mac, vf->mac.data, vf->mac.len); - NLA_PUT (nlmsg, IFLA_VF_MAC, sizeof (ivm), &ivm); - } - - if (vf->min_tx_rate || vf->max_tx_rate) { - struct _ifla_vf_rate ivr = { 0 }; - - ivr.vf = vf->index; - ivr.min_tx_rate = vf->min_tx_rate; - ivr.max_tx_rate = vf->max_tx_rate; - NLA_PUT (nlmsg, IFLA_VF_RATE, sizeof (ivr), &ivr); - } - - /* Kernel only supports one VLAN per VF now. If this - * changes in the future, we need to figure out how to - * clear existing VLANs and set new ones in one message - * with the new API.*/ - if (vf->num_vlans > 1) { - _LOGW ("multiple VLANs per VF are not supported at the moment"); - return FALSE; - } else { - struct _ifla_vf_vlan_info ivvi = { 0 }; - - if (!(vlan_list = nla_nest_start (nlmsg, IFLA_VF_VLAN_LIST))) - goto nla_put_failure; - - ivvi.vf = vf->index; - if (vf->num_vlans == 1) { - ivvi.vlan = vf->vlans[0].id; - ivvi.qos = vf->vlans[0].qos; - ivvi.vlan_proto = htons (vf->vlans[0].proto_ad ? ETH_P_8021AD : ETH_P_8021Q); - } else { - /* Clear existing VLAN */ - ivvi.vlan = 0; - ivvi.qos = 0; - ivvi.vlan_proto = htons (ETH_P_8021Q); - } - - NLA_PUT (nlmsg, IFLA_VF_VLAN_INFO, sizeof (ivvi), &ivvi); - nla_nest_end (nlmsg, vlan_list); - } - nla_nest_end (nlmsg, info); - } - nla_nest_end (nlmsg, list); - - return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); +link_set_sriov_vfs(NMPlatform *platform, int ifindex, const NMPlatformVF *const *vfs) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr * list, *info, *vlan_list; + guint i; + + nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); + if (!nlmsg) + g_return_val_if_reached(-NME_BUG); + + if (!(list = nla_nest_start(nlmsg, IFLA_VFINFO_LIST))) + goto nla_put_failure; + + for (i = 0; vfs[i]; i++) { + const NMPlatformVF *vf = vfs[i]; + + if (!(info = nla_nest_start(nlmsg, IFLA_VF_INFO))) + goto nla_put_failure; + + if (vf->spoofchk >= 0) { + struct _ifla_vf_setting ivs = {0}; + + ivs.vf = vf->index; + ivs.setting = vf->spoofchk; + NLA_PUT(nlmsg, IFLA_VF_SPOOFCHK, sizeof(ivs), &ivs); + } + + if (vf->trust >= 0) { + struct _ifla_vf_setting ivs = {0}; + + ivs.vf = vf->index; + ivs.setting = vf->trust; + NLA_PUT(nlmsg, IFLA_VF_TRUST, sizeof(ivs), &ivs); + } + + if (vf->mac.len) { + struct ifla_vf_mac ivm = {0}; + + ivm.vf = vf->index; + memcpy(ivm.mac, vf->mac.data, vf->mac.len); + NLA_PUT(nlmsg, IFLA_VF_MAC, sizeof(ivm), &ivm); + } + + if (vf->min_tx_rate || vf->max_tx_rate) { + struct _ifla_vf_rate ivr = {0}; + + ivr.vf = vf->index; + ivr.min_tx_rate = vf->min_tx_rate; + ivr.max_tx_rate = vf->max_tx_rate; + NLA_PUT(nlmsg, IFLA_VF_RATE, sizeof(ivr), &ivr); + } + + /* Kernel only supports one VLAN per VF now. If this + * changes in the future, we need to figure out how to + * clear existing VLANs and set new ones in one message + * with the new API.*/ + if (vf->num_vlans > 1) { + _LOGW("multiple VLANs per VF are not supported at the moment"); + return FALSE; + } else { + struct _ifla_vf_vlan_info ivvi = {0}; + + if (!(vlan_list = nla_nest_start(nlmsg, IFLA_VF_VLAN_LIST))) + goto nla_put_failure; + + ivvi.vf = vf->index; + if (vf->num_vlans == 1) { + ivvi.vlan = vf->vlans[0].id; + ivvi.qos = vf->vlans[0].qos; + ivvi.vlan_proto = htons(vf->vlans[0].proto_ad ? ETH_P_8021AD : ETH_P_8021Q); + } else { + /* Clear existing VLAN */ + ivvi.vlan = 0; + ivvi.qos = 0; + ivvi.vlan_proto = htons(ETH_P_8021Q); + } + + NLA_PUT(nlmsg, IFLA_VF_VLAN_INFO, sizeof(ivvi), &ivvi); + nla_nest_end(nlmsg, vlan_list); + } + nla_nest_end(nlmsg, info); + } + nla_nest_end(nlmsg, list); + + return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); nla_put_failure: - g_return_val_if_reached (FALSE); + g_return_val_if_reached(FALSE); } static gboolean -link_set_bridge_vlans (NMPlatform *platform, - int ifindex, - gboolean on_master, - const NMPlatformBridgeVlan *const *vlans) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - struct nlattr *list; - struct bridge_vlan_info vinfo = { }; - guint i; - - nlmsg = _nl_msg_new_link_full (vlans ? RTM_SETLINK : RTM_DELLINK, - 0, - ifindex, - NULL, - AF_BRIDGE, - 0, - 0); - if (!nlmsg) - g_return_val_if_reached (-NME_BUG); - - if (!(list = nla_nest_start (nlmsg, IFLA_AF_SPEC))) - goto nla_put_failure; - - NLA_PUT_U16 (nlmsg, - IFLA_BRIDGE_FLAGS, - on_master ? BRIDGE_FLAGS_MASTER : BRIDGE_FLAGS_SELF); - - if (vlans) { - /* Add VLANs */ - for (i = 0; vlans[i]; i++) { - const NMPlatformBridgeVlan *vlan = vlans[i]; - gboolean is_range = vlan->vid_start != vlan->vid_end; - - vinfo.vid = vlan->vid_start; - vinfo.flags = is_range ? BRIDGE_VLAN_INFO_RANGE_BEGIN : 0; - - if (vlan->untagged) - vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; - if (vlan->pvid) - vinfo.flags |= BRIDGE_VLAN_INFO_PVID; - - NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); - - if (is_range) { - vinfo.vid = vlan->vid_end; - vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END; - NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); - } - } - } else { - /* Flush existing VLANs */ - vinfo.vid = 1; - vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; - NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); - - vinfo.vid = 4094; - vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END; - NLA_PUT (nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof (vinfo), &vinfo); - } - - nla_nest_end (nlmsg, list); - - return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); +link_set_bridge_vlans(NMPlatform * platform, + int ifindex, + gboolean on_master, + const NMPlatformBridgeVlan *const *vlans) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr * list; + struct bridge_vlan_info vinfo = {}; + guint i; + + nlmsg = + _nl_msg_new_link_full(vlans ? RTM_SETLINK : RTM_DELLINK, 0, ifindex, NULL, AF_BRIDGE, 0, 0); + if (!nlmsg) + g_return_val_if_reached(-NME_BUG); + + if (!(list = nla_nest_start(nlmsg, IFLA_AF_SPEC))) + goto nla_put_failure; + + NLA_PUT_U16(nlmsg, IFLA_BRIDGE_FLAGS, on_master ? BRIDGE_FLAGS_MASTER : BRIDGE_FLAGS_SELF); + + if (vlans) { + /* Add VLANs */ + for (i = 0; vlans[i]; i++) { + const NMPlatformBridgeVlan *vlan = vlans[i]; + gboolean is_range = vlan->vid_start != vlan->vid_end; + + vinfo.vid = vlan->vid_start; + vinfo.flags = is_range ? BRIDGE_VLAN_INFO_RANGE_BEGIN : 0; + + if (vlan->untagged) + vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; + if (vlan->pvid) + vinfo.flags |= BRIDGE_VLAN_INFO_PVID; + + NLA_PUT(nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); + + if (is_range) { + vinfo.vid = vlan->vid_end; + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END; + NLA_PUT(nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); + } + } + } else { + /* Flush existing VLANs */ + vinfo.vid = 1; + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; + NLA_PUT(nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); + + vinfo.vid = 4094; + vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END; + NLA_PUT(nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); + } + + nla_nest_end(nlmsg, list); + + return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); nla_put_failure: - g_return_val_if_reached (FALSE); + g_return_val_if_reached(FALSE); } static char * -link_get_physical_port_id (NMPlatform *platform, int ifindex) +link_get_physical_port_id(NMPlatform *platform, int ifindex) { - nm_auto_close int dirfd = -1; - char ifname_verified[IFNAMSIZ]; + nm_auto_close int dirfd = -1; + char ifname_verified[IFNAMSIZ]; - dirfd = nm_platform_sysctl_open_netdir (platform, ifindex, ifname_verified); - if (dirfd < 0) - return NULL; - return sysctl_get (platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "phys_port_id")); + dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname_verified); + if (dirfd < 0) + return NULL; + return sysctl_get(platform, NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_verified, "phys_port_id")); } static guint -link_get_dev_id (NMPlatform *platform, int ifindex) +link_get_dev_id(NMPlatform *platform, int ifindex) { - nm_auto_close int dirfd = -1; - char ifname_verified[IFNAMSIZ]; + nm_auto_close int dirfd = -1; + char ifname_verified[IFNAMSIZ]; - dirfd = nm_platform_sysctl_open_netdir (platform, ifindex, ifname_verified); - if (dirfd < 0) - return 0; - return nm_platform_sysctl_get_int_checked (platform, - NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "dev_id"), - 16, 0, G_MAXUINT16, 0); + dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname_verified); + if (dirfd < 0) + return 0; + return nm_platform_sysctl_get_int_checked( + platform, + NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_verified, "dev_id"), + 16, + 0, + G_MAXUINT16, + 0); } static gboolean -link_tun_add (NMPlatform *platform, - const char *name, - const NMPlatformLnkTun *props, - const NMPlatformLink **out_link, - int *out_fd) -{ - const NMPObject *obj; - struct ifreq ifr = { }; - nm_auto_close int fd = -1; - - nm_assert (NM_IN_SET (props->type, IFF_TAP, IFF_TUN)); - nm_assert (props->persist || out_fd); - - fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC); - if (fd < 0) - return FALSE; - - nm_utils_ifname_cpy (ifr.ifr_name, name); - ifr.ifr_flags = ((short) props->type) - | ((short) IFF_TUN_EXCL) - | (!props->pi ? (short) IFF_NO_PI : (short) 0) - | ( props->vnet_hdr ? (short) IFF_VNET_HDR : (short) 0) - | ( props->multi_queue ? (short) NM_IFF_MULTI_QUEUE : (short) 0); - if (ioctl (fd, TUNSETIFF, &ifr)) - return FALSE; - - if (props->owner_valid) { - if (ioctl (fd, TUNSETOWNER, (uid_t) props->owner)) - return FALSE; - } - - if (props->group_valid) { - if (ioctl (fd, TUNSETGROUP, (gid_t) props->group)) - return FALSE; - } - - if (props->persist) { - if (ioctl (fd, TUNSETPERSIST, 1)) - return FALSE; - } - - do_request_link (platform, 0, name); - obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), - 0, name, FALSE, - NM_LINK_TYPE_TUN, - NULL, NULL); - - if (!obj) - return FALSE; - - NM_SET_OUT (out_link, &obj->link); - NM_SET_OUT (out_fd, nm_steal_fd (&fd)); - return TRUE; +link_tun_add(NMPlatform * platform, + const char * name, + const NMPlatformLnkTun *props, + const NMPlatformLink ** out_link, + int * out_fd) +{ + const NMPObject * obj; + struct ifreq ifr = {}; + nm_auto_close int fd = -1; + + nm_assert(NM_IN_SET(props->type, IFF_TAP, IFF_TUN)); + nm_assert(props->persist || out_fd); + + fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC); + if (fd < 0) + return FALSE; + + nm_utils_ifname_cpy(ifr.ifr_name, name); + ifr.ifr_flags = ((short) props->type) | ((short) IFF_TUN_EXCL) + | (!props->pi ? (short) IFF_NO_PI : (short) 0) + | (props->vnet_hdr ? (short) IFF_VNET_HDR : (short) 0) + | (props->multi_queue ? (short) NM_IFF_MULTI_QUEUE : (short) 0); + if (ioctl(fd, TUNSETIFF, &ifr)) + return FALSE; + + if (props->owner_valid) { + if (ioctl(fd, TUNSETOWNER, (uid_t) props->owner)) + return FALSE; + } + + if (props->group_valid) { + if (ioctl(fd, TUNSETGROUP, (gid_t) props->group)) + return FALSE; + } + + if (props->persist) { + if (ioctl(fd, TUNSETPERSIST, 1)) + return FALSE; + } + + do_request_link(platform, 0, name); + obj = nmp_cache_lookup_link_full(nm_platform_get_cache(platform), + 0, + name, + FALSE, + NM_LINK_TYPE_TUN, + NULL, + NULL); + + if (!obj) + return FALSE; + + NM_SET_OUT(out_link, &obj->link); + NM_SET_OUT(out_fd, nm_steal_fd(&fd)); + return TRUE; } static void -_vlan_change_vlan_qos_mapping_create (gboolean is_ingress_map, - gboolean reset_all, - const NMVlanQosMapping *current_map, - guint current_n_map, - const NMVlanQosMapping *set_map, - guint set_n_map, - NMVlanQosMapping **out_map, - guint *out_n_map) -{ - NMVlanQosMapping *map; - guint i, j, len; - const guint INGRESS_RANGE_LEN = 8; - - nm_assert (out_map && !*out_map); - nm_assert (out_n_map && !*out_n_map); - - if (!reset_all) - current_n_map = 0; - else if (is_ingress_map) - current_n_map = INGRESS_RANGE_LEN; - - len = current_n_map + set_n_map; - - if (len == 0) - return; - - map = g_new (NMVlanQosMapping, len); - - if (current_n_map) { - if (is_ingress_map) { - /* For the ingress-map, there are only 8 entries (0 to 7). - * When the user requests to reset all entries, we don't actually - * need the cached entries, we can just explicitly clear all possible - * ones. - * - * That makes only a real difference in case our cache is out-of-date. - * - * For the egress map we cannot do that, because there are far too - * many. There we can only clear the entries that we know about. */ - for (i = 0; i < INGRESS_RANGE_LEN; i++) { - map[i].from = i; - map[i].to = 0; - } - } else { - for (i = 0; i < current_n_map; i++) { - map[i].from = current_map[i].from; - map[i].to = 0; - } - } - } - if (set_n_map) - memcpy (&map[current_n_map], set_map, sizeof (*set_map) * set_n_map); - - g_qsort_with_data (map, - len, - sizeof (*map), - _vlan_qos_mapping_cmp_from, - NULL); - - for (i = 0, j = 0; i < len; i++) { - if ( ( is_ingress_map && !VLAN_XGRESS_PRIO_VALID (map[i].from)) - || (!is_ingress_map && !VLAN_XGRESS_PRIO_VALID (map[i].to))) - continue; - if ( j > 0 - && map[j - 1].from == map[i].from) - map[j - 1] = map[i]; - else - map[j++] = map[i]; - } - - *out_map = map; - *out_n_map = j; +_vlan_change_vlan_qos_mapping_create(gboolean is_ingress_map, + gboolean reset_all, + const NMVlanQosMapping *current_map, + guint current_n_map, + const NMVlanQosMapping *set_map, + guint set_n_map, + NMVlanQosMapping ** out_map, + guint * out_n_map) +{ + NMVlanQosMapping *map; + guint i, j, len; + const guint INGRESS_RANGE_LEN = 8; + + nm_assert(out_map && !*out_map); + nm_assert(out_n_map && !*out_n_map); + + if (!reset_all) + current_n_map = 0; + else if (is_ingress_map) + current_n_map = INGRESS_RANGE_LEN; + + len = current_n_map + set_n_map; + + if (len == 0) + return; + + map = g_new(NMVlanQosMapping, len); + + if (current_n_map) { + if (is_ingress_map) { + /* For the ingress-map, there are only 8 entries (0 to 7). + * When the user requests to reset all entries, we don't actually + * need the cached entries, we can just explicitly clear all possible + * ones. + * + * That makes only a real difference in case our cache is out-of-date. + * + * For the egress map we cannot do that, because there are far too + * many. There we can only clear the entries that we know about. */ + for (i = 0; i < INGRESS_RANGE_LEN; i++) { + map[i].from = i; + map[i].to = 0; + } + } else { + for (i = 0; i < current_n_map; i++) { + map[i].from = current_map[i].from; + map[i].to = 0; + } + } + } + if (set_n_map) + memcpy(&map[current_n_map], set_map, sizeof(*set_map) * set_n_map); + + g_qsort_with_data(map, len, sizeof(*map), _vlan_qos_mapping_cmp_from, NULL); + + for (i = 0, j = 0; i < len; i++) { + if ((is_ingress_map && !VLAN_XGRESS_PRIO_VALID(map[i].from)) + || (!is_ingress_map && !VLAN_XGRESS_PRIO_VALID(map[i].to))) + continue; + if (j > 0 && map[j - 1].from == map[i].from) + map[j - 1] = map[i]; + else + map[j++] = map[i]; + } + + *out_map = map; + *out_n_map = j; } static gboolean -link_vlan_change (NMPlatform *platform, - int ifindex, - NMVlanFlags flags_mask, - NMVlanFlags flags_set, - gboolean ingress_reset_all, - const NMVlanQosMapping *ingress_map, - gsize n_ingress_map, - gboolean egress_reset_all, - const NMVlanQosMapping *egress_map, - gsize n_egress_map) -{ - const NMPObject *obj_cache; - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - const NMPObjectLnkVlan *lnk; - guint new_n_ingress_map = 0; - guint new_n_egress_map = 0; - gs_free NMVlanQosMapping *new_ingress_map = NULL; - gs_free NMVlanQosMapping *new_egress_map = NULL; - - obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); - if ( !obj_cache - || !obj_cache->_link.netlink.is_in_netlink) { - _LOGD ("link: change %d: %s: link does not exist", ifindex, "vlan"); - return FALSE; - } - - lnk = obj_cache->_link.netlink.lnk ? &obj_cache->_link.netlink.lnk->_lnk_vlan : NULL; - - flags_set &= flags_mask; - - _vlan_change_vlan_qos_mapping_create (TRUE, - ingress_reset_all, - lnk ? lnk->ingress_qos_map : NULL, - lnk ? lnk->n_ingress_qos_map : 0, - ingress_map, - n_ingress_map, - &new_ingress_map, - &new_n_ingress_map); - - _vlan_change_vlan_qos_mapping_create (FALSE, - egress_reset_all, - lnk ? lnk->egress_qos_map : NULL, - lnk ? lnk->n_egress_qos_map : 0, - egress_map, - n_egress_map, - &new_egress_map, - &new_n_egress_map); - - nlmsg = _nl_msg_new_link (RTM_NEWLINK, - 0, - ifindex, - NULL); - if ( !nlmsg - || !_nl_msg_new_link_set_linkinfo_vlan (nlmsg, - -1, - flags_mask, - flags_set, - new_ingress_map, - new_n_ingress_map, - new_egress_map, - new_n_egress_map)) - g_return_val_if_reached (FALSE); - - return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); +link_vlan_change(NMPlatform * platform, + int ifindex, + NMVlanFlags flags_mask, + NMVlanFlags flags_set, + gboolean ingress_reset_all, + const NMVlanQosMapping *ingress_map, + gsize n_ingress_map, + gboolean egress_reset_all, + const NMVlanQosMapping *egress_map, + gsize n_egress_map) +{ + const NMPObject * obj_cache; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + const NMPObjectLnkVlan * lnk; + guint new_n_ingress_map = 0; + guint new_n_egress_map = 0; + gs_free NMVlanQosMapping *new_ingress_map = NULL; + gs_free NMVlanQosMapping *new_egress_map = NULL; + + obj_cache = nmp_cache_lookup_link(nm_platform_get_cache(platform), ifindex); + if (!obj_cache || !obj_cache->_link.netlink.is_in_netlink) { + _LOGD("link: change %d: %s: link does not exist", ifindex, "vlan"); + return FALSE; + } + + lnk = obj_cache->_link.netlink.lnk ? &obj_cache->_link.netlink.lnk->_lnk_vlan : NULL; + + flags_set &= flags_mask; + + _vlan_change_vlan_qos_mapping_create(TRUE, + ingress_reset_all, + lnk ? lnk->ingress_qos_map : NULL, + lnk ? lnk->n_ingress_qos_map : 0, + ingress_map, + n_ingress_map, + &new_ingress_map, + &new_n_ingress_map); + + _vlan_change_vlan_qos_mapping_create(FALSE, + egress_reset_all, + lnk ? lnk->egress_qos_map : NULL, + lnk ? lnk->n_egress_qos_map : 0, + egress_map, + n_egress_map, + &new_egress_map, + &new_n_egress_map); + + nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); + if (!nlmsg + || !_nl_msg_new_link_set_linkinfo_vlan(nlmsg, + -1, + flags_mask, + flags_set, + new_ingress_map, + new_n_ingress_map, + new_egress_map, + new_n_egress_map)) + g_return_val_if_reached(FALSE); + + return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); } static gboolean -link_enslave (NMPlatform *platform, int master, int slave) +link_enslave(NMPlatform *platform, int master, int slave) { - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - int ifindex = slave; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + int ifindex = slave; - nlmsg = _nl_msg_new_link (RTM_NEWLINK, - 0, - ifindex, - NULL); - if (!nlmsg) - return FALSE; + nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); + if (!nlmsg) + return FALSE; - NLA_PUT_U32 (nlmsg, IFLA_MASTER, master); + NLA_PUT_U32(nlmsg, IFLA_MASTER, master); - return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); + return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); nla_put_failure: - g_return_val_if_reached (FALSE); + g_return_val_if_reached(FALSE); } static gboolean -link_release (NMPlatform *platform, int master, int slave) +link_release(NMPlatform *platform, int master, int slave) { - return link_enslave (platform, 0, slave); + return link_enslave(platform, 0, slave); } /*****************************************************************************/ static gboolean -_infiniband_partition_action (NMPlatform *platform, - InfinibandAction action, - int parent, - int p_key, - const NMPlatformLink **out_link) -{ - nm_auto_close int dirfd = -1; - char ifname_parent[IFNAMSIZ]; - const NMPObject *obj; - char id[20]; - char name[IFNAMSIZ]; - gboolean success; - - nm_assert (NM_IN_SET (action, INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD)); - nm_assert (p_key > 0 && p_key <= 0xffff && p_key != 0x8000); - - dirfd = nm_platform_sysctl_open_netdir (platform, parent, ifname_parent); - if (dirfd < 0) { - errno = ENOENT; - return FALSE; - } - - nm_sprintf_buf (id, "0x%04x", p_key); - if (action == INFINIBAND_ACTION_CREATE_CHILD) - success = nm_platform_sysctl_set (platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_parent, "create_child"), id); - else - success = nm_platform_sysctl_set (platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_parent, "delete_child"), id); - - if (!success) { - if ( action == INFINIBAND_ACTION_DELETE_CHILD - && errno == ENODEV) - return TRUE; - return FALSE; - } - - nm_utils_new_infiniband_name (name, ifname_parent, p_key); - do_request_link (platform, 0, name); - - if (action == INFINIBAND_ACTION_DELETE_CHILD) - return TRUE; - - obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, name, FALSE, - NM_LINK_TYPE_INFINIBAND, NULL, NULL); - if (out_link) - *out_link = obj ? &obj->link : NULL; - return !!obj; +_infiniband_partition_action(NMPlatform * platform, + InfinibandAction action, + int parent, + int p_key, + const NMPlatformLink **out_link) +{ + nm_auto_close int dirfd = -1; + char ifname_parent[IFNAMSIZ]; + const NMPObject * obj; + char id[20]; + char name[IFNAMSIZ]; + gboolean success; + + nm_assert(NM_IN_SET(action, INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD)); + nm_assert(p_key > 0 && p_key <= 0xffff && p_key != 0x8000); + + dirfd = nm_platform_sysctl_open_netdir(platform, parent, ifname_parent); + if (dirfd < 0) { + errno = ENOENT; + return FALSE; + } + + nm_sprintf_buf(id, "0x%04x", p_key); + if (action == INFINIBAND_ACTION_CREATE_CHILD) + success = + nm_platform_sysctl_set(platform, + NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_parent, "create_child"), + id); + else + success = + nm_platform_sysctl_set(platform, + NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_parent, "delete_child"), + id); + + if (!success) { + if (action == INFINIBAND_ACTION_DELETE_CHILD && errno == ENODEV) + return TRUE; + return FALSE; + } + + nm_utils_new_infiniband_name(name, ifname_parent, p_key); + do_request_link(platform, 0, name); + + if (action == INFINIBAND_ACTION_DELETE_CHILD) + return TRUE; + + obj = nmp_cache_lookup_link_full(nm_platform_get_cache(platform), + 0, + name, + FALSE, + NM_LINK_TYPE_INFINIBAND, + NULL, + NULL); + if (out_link) + *out_link = obj ? &obj->link : NULL; + return !!obj; } static gboolean -infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMPlatformLink **out_link) +infiniband_partition_add(NMPlatform * platform, + int parent, + int p_key, + const NMPlatformLink **out_link) { - return _infiniband_partition_action (platform, INFINIBAND_ACTION_CREATE_CHILD, parent, p_key, out_link); + return _infiniband_partition_action(platform, + INFINIBAND_ACTION_CREATE_CHILD, + parent, + p_key, + out_link); } static gboolean -infiniband_partition_delete (NMPlatform *platform, int parent, int p_key) +infiniband_partition_delete(NMPlatform *platform, int parent, int p_key) { - return _infiniband_partition_action (platform, INFINIBAND_ACTION_DELETE_CHILD, parent, p_key, NULL); + return _infiniband_partition_action(platform, + INFINIBAND_ACTION_DELETE_CHILD, + parent, + p_key, + NULL); } /*****************************************************************************/ static GObject * -get_ext_data (NMPlatform *platform, int ifindex) +get_ext_data(NMPlatform *platform, int ifindex) { - const NMPObject *obj; + const NMPObject *obj; - obj = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); - if (!obj) - return NULL; + obj = nmp_cache_lookup_link(nm_platform_get_cache(platform), ifindex); + if (!obj) + return NULL; - return obj->_link.ext_data; + return obj->_link.ext_data; } /*****************************************************************************/ #define WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, retval) \ - nm_auto_pop_netns NMPNetns *netns = NULL; \ - NMWifiUtils *wifi_data; \ - if (!nm_platform_netns_push (platform, &netns)) \ - return retval; \ - wifi_data = NM_WIFI_UTILS (get_ext_data (platform, ifindex)); \ - if (!wifi_data) \ - return retval; + nm_auto_pop_netns NMPNetns *netns = NULL; \ + NMWifiUtils * wifi_data; \ + if (!nm_platform_netns_push(platform, &netns)) \ + return retval; \ + wifi_data = NM_WIFI_UTILS(get_ext_data(platform, ifindex)); \ + if (!wifi_data) \ + return retval; static gboolean -wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps) +wifi_get_capabilities(NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); - if (caps) - *caps = nm_wifi_utils_get_caps (wifi_data); - return TRUE; + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + if (caps) + *caps = nm_wifi_utils_get_caps(wifi_data); + return TRUE; } static gboolean -wifi_get_bssid (NMPlatform *platform, int ifindex, guint8 *bssid) +wifi_get_bssid(NMPlatform *platform, int ifindex, guint8 *bssid) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_get_bssid (wifi_data, bssid); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_get_bssid(wifi_data, bssid); } static guint32 -wifi_get_frequency (NMPlatform *platform, int ifindex) +wifi_get_frequency(NMPlatform *platform, int ifindex) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, 0); - return nm_wifi_utils_get_freq (wifi_data); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0); + return nm_wifi_utils_get_freq(wifi_data); } static gboolean -wifi_get_quality (NMPlatform *platform, int ifindex) +wifi_get_quality(NMPlatform *platform, int ifindex) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_get_qual (wifi_data); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_get_qual(wifi_data); } static guint32 -wifi_get_rate (NMPlatform *platform, int ifindex) +wifi_get_rate(NMPlatform *platform, int ifindex) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_get_rate (wifi_data); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_get_rate(wifi_data); } static NM80211Mode -wifi_get_mode (NMPlatform *platform, int ifindex) +wifi_get_mode(NMPlatform *platform, int ifindex) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, NM_802_11_MODE_UNKNOWN); - return nm_wifi_utils_get_mode (wifi_data); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, NM_802_11_MODE_UNKNOWN); + return nm_wifi_utils_get_mode(wifi_data); } static void -wifi_set_mode (NMPlatform *platform, int ifindex, NM80211Mode mode) +wifi_set_mode(NMPlatform *platform, int ifindex, NM80211Mode mode) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, ); - nm_wifi_utils_set_mode (wifi_data, mode); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, ); + nm_wifi_utils_set_mode(wifi_data, mode); } static void -wifi_set_powersave (NMPlatform *platform, int ifindex, guint32 powersave) +wifi_set_powersave(NMPlatform *platform, int ifindex, guint32 powersave) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, ); - nm_wifi_utils_set_powersave (wifi_data, powersave); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, ); + nm_wifi_utils_set_powersave(wifi_data, powersave); } static guint32 -wifi_find_frequency (NMPlatform *platform, int ifindex, const guint32 *freqs) +wifi_find_frequency(NMPlatform *platform, int ifindex, const guint32 *freqs) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, 0); - return nm_wifi_utils_find_freq (wifi_data, freqs); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0); + return nm_wifi_utils_find_freq(wifi_data, freqs); } static void -wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean running) +wifi_indicate_addressing_running(NMPlatform *platform, int ifindex, gboolean running) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, ); - nm_wifi_utils_indicate_addressing_running (wifi_data, running); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, ); + nm_wifi_utils_indicate_addressing_running(wifi_data, running); } static NMSettingWirelessWakeOnWLan -wifi_get_wake_on_wlan (NMPlatform *platform, int ifindex) +wifi_get_wake_on_wlan(NMPlatform *platform, int ifindex) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_get_wake_on_wlan (wifi_data); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_get_wake_on_wlan(wifi_data); } static gboolean -wifi_set_wake_on_wlan (NMPlatform *platform, int ifindex, - NMSettingWirelessWakeOnWLan wowl) +wifi_set_wake_on_wlan(NMPlatform *platform, int ifindex, NMSettingWirelessWakeOnWLan wowl) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_set_wake_on_wlan (wifi_data, wowl); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_set_wake_on_wlan(wifi_data, wowl); } /*****************************************************************************/ static gboolean -link_can_assume (NMPlatform *platform, int ifindex) +link_can_assume(NMPlatform *platform, int ifindex) { - NMPLookup lookup; - const NMPObject *link, *o; - NMDedupMultiIter iter; - NMPCache *cache = nm_platform_get_cache (platform); + NMPLookup lookup; + const NMPObject *link, *o; + NMDedupMultiIter iter; + NMPCache * cache = nm_platform_get_cache(platform); - if (ifindex <= 0) - return FALSE; + if (ifindex <= 0) + return FALSE; - link = nm_platform_link_get_obj (platform, ifindex, TRUE); - if (!link) - return FALSE; + link = nm_platform_link_get_obj(platform, ifindex, TRUE); + if (!link) + return FALSE; - if (!NM_FLAGS_HAS (link->link.n_ifi_flags, IFF_UP)) - return FALSE; + if (!NM_FLAGS_HAS(link->link.n_ifi_flags, IFF_UP)) + return FALSE; - if (link->link.master > 0) - return TRUE; + if (link->link.master > 0) + return TRUE; - nmp_lookup_init_object (&lookup, - NMP_OBJECT_TYPE_IP4_ADDRESS, - ifindex); - if (nmp_cache_lookup (cache, &lookup)) - return TRUE; + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex); + if (nmp_cache_lookup(cache, &lookup)) + return TRUE; - nmp_lookup_init_object (&lookup, - NMP_OBJECT_TYPE_IP6_ADDRESS, - ifindex); - nmp_cache_iter_for_each (&iter, - nmp_cache_lookup (cache, &lookup), - &o) { - nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_TYPE_IP6_ADDRESS); - if (!IN6_IS_ADDR_LINKLOCAL (&o->ip6_address.address)) - return TRUE; - } - return FALSE; + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex); + nmp_cache_iter_for_each (&iter, nmp_cache_lookup(cache, &lookup), &o) { + nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_TYPE_IP6_ADDRESS); + if (!IN6_IS_ADDR_LINKLOCAL(&o->ip6_address.address)) + return TRUE; + } + return FALSE; } /*****************************************************************************/ static guint32 -mesh_get_channel (NMPlatform *platform, int ifindex) +mesh_get_channel(NMPlatform *platform, int ifindex) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, 0); - return nm_wifi_utils_get_mesh_channel (wifi_data); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0); + return nm_wifi_utils_get_mesh_channel(wifi_data); } static gboolean -mesh_set_channel (NMPlatform *platform, int ifindex, guint32 channel) +mesh_set_channel(NMPlatform *platform, int ifindex, guint32 channel) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_set_mesh_channel (wifi_data, channel); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_set_mesh_channel(wifi_data, channel); } static gboolean -mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) +mesh_set_ssid(NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) { - WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_set_mesh_ssid (wifi_data, ssid, len); + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_set_mesh_ssid(wifi_data, ssid, len); } /*****************************************************************************/ -#define WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, retval) \ - NMWpanUtils *wpan_data = NM_WPAN_UTILS (get_ext_data (platform, ifindex)); \ - if (!wpan_data) \ - return retval; +#define WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, retval) \ + NMWpanUtils *wpan_data = NM_WPAN_UTILS(get_ext_data(platform, ifindex)); \ + if (!wpan_data) \ + return retval; static guint16 -wpan_get_pan_id (NMPlatform *platform, int ifindex) +wpan_get_pan_id(NMPlatform *platform, int ifindex) { - WPAN_GET_WPAN_DATA (wpan_data, platform, ifindex, G_MAXINT16); - return nm_wpan_utils_get_pan_id (wpan_data); + WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, G_MAXINT16); + return nm_wpan_utils_get_pan_id(wpan_data); } static gboolean -wpan_set_pan_id (NMPlatform *platform, int ifindex, guint16 pan_id) +wpan_set_pan_id(NMPlatform *platform, int ifindex, guint16 pan_id) { - WPAN_GET_WPAN_DATA (wpan_data, platform, ifindex, FALSE); - return nm_wpan_utils_set_pan_id (wpan_data, pan_id); + WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, FALSE); + return nm_wpan_utils_set_pan_id(wpan_data, pan_id); } static guint16 -wpan_get_short_addr (NMPlatform *platform, int ifindex) +wpan_get_short_addr(NMPlatform *platform, int ifindex) { - WPAN_GET_WPAN_DATA (wpan_data, platform, ifindex, G_MAXINT16); - return nm_wpan_utils_get_short_addr (wpan_data); + WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, G_MAXINT16); + return nm_wpan_utils_get_short_addr(wpan_data); } static gboolean -wpan_set_short_addr (NMPlatform *platform, int ifindex, guint16 short_addr) +wpan_set_short_addr(NMPlatform *platform, int ifindex, guint16 short_addr) { - WPAN_GET_WPAN_DATA (wpan_data, platform, ifindex, FALSE); - return nm_wpan_utils_set_short_addr (wpan_data, short_addr); + WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, FALSE); + return nm_wpan_utils_set_short_addr(wpan_data, short_addr); } static gboolean -wpan_set_channel (NMPlatform *platform, int ifindex, guint8 page, guint8 channel) +wpan_set_channel(NMPlatform *platform, int ifindex, guint8 page, guint8 channel) { - WPAN_GET_WPAN_DATA (wpan_data, platform, ifindex, FALSE); - return nm_wpan_utils_set_channel (wpan_data, page, channel); + WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, FALSE); + return nm_wpan_utils_set_channel(wpan_data, page, channel); } /*****************************************************************************/ static gboolean -link_get_wake_on_lan (NMPlatform *platform, int ifindex) +link_get_wake_on_lan(NMPlatform *platform, int ifindex) { - nm_auto_pop_netns NMPNetns *netns = NULL; - NMLinkType type = nm_platform_link_get_type (platform, ifindex); + nm_auto_pop_netns NMPNetns *netns = NULL; + NMLinkType type = nm_platform_link_get_type(platform, ifindex); - if (!nm_platform_netns_push (platform, &netns)) - return FALSE; + if (!nm_platform_netns_push(platform, &netns)) + return FALSE; - if (type == NM_LINK_TYPE_ETHERNET) - return nmp_utils_ethtool_get_wake_on_lan (ifindex); - else if (type == NM_LINK_TYPE_WIFI) { - NMWifiUtils *wifi_data = NM_WIFI_UTILS (get_ext_data (platform, ifindex)); + if (type == NM_LINK_TYPE_ETHERNET) + return nmp_utils_ethtool_get_wake_on_lan(ifindex); + else if (type == NM_LINK_TYPE_WIFI) { + NMWifiUtils *wifi_data = NM_WIFI_UTILS(get_ext_data(platform, ifindex)); - if (!wifi_data) - return FALSE; + if (!wifi_data) + return FALSE; - return !NM_IN_SET (nm_wifi_utils_get_wake_on_wlan (wifi_data), - NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE, - NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE); + return !NM_IN_SET(nm_wifi_utils_get_wake_on_wlan(wifi_data), + NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE, + NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE); - } else - return FALSE; + } else + return FALSE; } static gboolean -link_get_driver_info (NMPlatform *platform, - int ifindex, - char **out_driver_name, - char **out_driver_version, - char **out_fw_version) +link_get_driver_info(NMPlatform *platform, + int ifindex, + char ** out_driver_name, + char ** out_driver_version, + char ** out_fw_version) { - nm_auto_pop_netns NMPNetns *netns = NULL; - NMPUtilsEthtoolDriverInfo driver_info; + nm_auto_pop_netns NMPNetns *netns = NULL; + NMPUtilsEthtoolDriverInfo driver_info; - if (!nm_platform_netns_push (platform, &netns)) - return FALSE; + if (!nm_platform_netns_push(platform, &netns)) + return FALSE; - if (!nmp_utils_ethtool_get_driver_info (ifindex, &driver_info)) - return FALSE; - NM_SET_OUT (out_driver_name, g_strdup (driver_info.driver)); - NM_SET_OUT (out_driver_version, g_strdup (driver_info.version)); - NM_SET_OUT (out_fw_version, g_strdup (driver_info.fw_version)); - return TRUE; + if (!nmp_utils_ethtool_get_driver_info(ifindex, &driver_info)) + return FALSE; + NM_SET_OUT(out_driver_name, g_strdup(driver_info.driver)); + NM_SET_OUT(out_driver_version, g_strdup(driver_info.version)); + NM_SET_OUT(out_fw_version, g_strdup(driver_info.fw_version)); + return TRUE; } /*****************************************************************************/ static gboolean -ip4_address_add (NMPlatform *platform, - int ifindex, - in_addr_t addr, - guint8 plen, - in_addr_t peer_addr, - in_addr_t broadcast_address, - guint32 lifetime, - guint32 preferred, - guint32 flags, - const char *label) -{ - NMPObject obj_id; - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - - nlmsg = _nl_msg_new_address (RTM_NEWADDR, - NLM_F_CREATE | NLM_F_REPLACE, - AF_INET, - ifindex, - &addr, - plen, - &peer_addr, - flags, - nm_utils_ip4_address_is_link_local (addr) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE, - lifetime, - preferred, - broadcast_address, - label); - - nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_addr); - return (do_add_addrroute (platform, &obj_id, nlmsg, FALSE) >= 0); +ip4_address_add(NMPlatform *platform, + int ifindex, + in_addr_t addr, + guint8 plen, + in_addr_t peer_addr, + in_addr_t broadcast_address, + guint32 lifetime, + guint32 preferred, + guint32 flags, + const char *label) +{ + NMPObject obj_id; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + + nlmsg = _nl_msg_new_address(RTM_NEWADDR, + NLM_F_CREATE | NLM_F_REPLACE, + AF_INET, + ifindex, + &addr, + plen, + &peer_addr, + flags, + nm_utils_ip4_address_is_link_local(addr) ? RT_SCOPE_LINK + : RT_SCOPE_UNIVERSE, + lifetime, + preferred, + broadcast_address, + label); + + nmp_object_stackinit_id_ip4_address(&obj_id, ifindex, addr, plen, peer_addr); + return (do_add_addrroute(platform, &obj_id, nlmsg, FALSE) >= 0); } static gboolean -ip6_address_add (NMPlatform *platform, - int ifindex, - struct in6_addr addr, - guint8 plen, - struct in6_addr peer_addr, - guint32 lifetime, - guint32 preferred, - guint32 flags) -{ - NMPObject obj_id; - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - - nlmsg = _nl_msg_new_address (RTM_NEWADDR, - NLM_F_CREATE | NLM_F_REPLACE, - AF_INET6, - ifindex, - &addr, - plen, - IN6_IS_ADDR_UNSPECIFIED (&peer_addr) ? NULL : &peer_addr, - flags, - RT_SCOPE_UNIVERSE, - lifetime, - preferred, - 0, - NULL); - - nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr); - return (do_add_addrroute (platform, &obj_id, nlmsg, FALSE) >= 0); +ip6_address_add(NMPlatform * platform, + int ifindex, + struct in6_addr addr, + guint8 plen, + struct in6_addr peer_addr, + guint32 lifetime, + guint32 preferred, + guint32 flags) +{ + NMPObject obj_id; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + + nlmsg = _nl_msg_new_address(RTM_NEWADDR, + NLM_F_CREATE | NLM_F_REPLACE, + AF_INET6, + ifindex, + &addr, + plen, + IN6_IS_ADDR_UNSPECIFIED(&peer_addr) ? NULL : &peer_addr, + flags, + RT_SCOPE_UNIVERSE, + lifetime, + preferred, + 0, + NULL); + + nmp_object_stackinit_id_ip6_address(&obj_id, ifindex, &addr); + return (do_add_addrroute(platform, &obj_id, nlmsg, FALSE) >= 0); } static gboolean -ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj_id; - - nlmsg = _nl_msg_new_address (RTM_DELADDR, - 0, - AF_INET, - ifindex, - &addr, - plen, - &peer_address, - 0, - RT_SCOPE_NOWHERE, - NM_PLATFORM_LIFETIME_PERMANENT, - NM_PLATFORM_LIFETIME_PERMANENT, - 0, - NULL); - if (!nlmsg) - g_return_val_if_reached (FALSE); - - nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_address); - return do_delete_object (platform, &obj_id, nlmsg); +ip4_address_delete(NMPlatform *platform, + int ifindex, + in_addr_t addr, + guint8 plen, + in_addr_t peer_address) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj_id; + + nlmsg = _nl_msg_new_address(RTM_DELADDR, + 0, + AF_INET, + ifindex, + &addr, + plen, + &peer_address, + 0, + RT_SCOPE_NOWHERE, + NM_PLATFORM_LIFETIME_PERMANENT, + NM_PLATFORM_LIFETIME_PERMANENT, + 0, + NULL); + if (!nlmsg) + g_return_val_if_reached(FALSE); + + nmp_object_stackinit_id_ip4_address(&obj_id, ifindex, addr, plen, peer_address); + return do_delete_object(platform, &obj_id, nlmsg); } static gboolean -ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj_id; - - nlmsg = _nl_msg_new_address (RTM_DELADDR, - 0, - AF_INET6, - ifindex, - &addr, - plen, - NULL, - 0, - RT_SCOPE_NOWHERE, - NM_PLATFORM_LIFETIME_PERMANENT, - NM_PLATFORM_LIFETIME_PERMANENT, - 0, - NULL); - if (!nlmsg) - g_return_val_if_reached (FALSE); - - nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr); - return do_delete_object (platform, &obj_id, nlmsg); +ip6_address_delete(NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj_id; + + nlmsg = _nl_msg_new_address(RTM_DELADDR, + 0, + AF_INET6, + ifindex, + &addr, + plen, + NULL, + 0, + RT_SCOPE_NOWHERE, + NM_PLATFORM_LIFETIME_PERMANENT, + NM_PLATFORM_LIFETIME_PERMANENT, + 0, + NULL); + if (!nlmsg) + g_return_val_if_reached(FALSE); + + nmp_object_stackinit_id_ip6_address(&obj_id, ifindex, &addr); + return do_delete_object(platform, &obj_id, nlmsg); } /*****************************************************************************/ static int -ip_route_add (NMPlatform *platform, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj; - - switch (addr_family) { - case AF_INET: - nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) route); - break; - case AF_INET6: - nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); - break; - default: - nm_assert_not_reached (); - } - - nm_platform_ip_route_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (&obj)); - - nlmsg = _nl_msg_new_route (RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj); - if (!nlmsg) - g_return_val_if_reached (-NME_BUG); - return do_add_addrroute (platform, - &obj, - nlmsg, - NM_FLAGS_HAS (flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE)); +ip_route_add(NMPlatform * platform, + NMPNlmFlags flags, + int addr_family, + const NMPlatformIPRoute *route) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj; + + switch (addr_family) { + case AF_INET: + nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) route); + break; + case AF_INET6: + nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); + break; + default: + nm_assert_not_reached(); + } + + nm_platform_ip_route_normalize(addr_family, NMP_OBJECT_CAST_IP_ROUTE(&obj)); + + nlmsg = _nl_msg_new_route(RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj); + if (!nlmsg) + g_return_val_if_reached(-NME_BUG); + return do_add_addrroute(platform, + &obj, + nlmsg, + NM_FLAGS_HAS(flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE)); } static gboolean -object_delete (NMPlatform *platform, - const NMPObject *obj) -{ - nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL; - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - - if (!NMP_OBJECT_IS_STACKINIT (obj)) - obj_keep_alive = nmp_object_ref (obj); - - switch (NMP_OBJECT_GET_TYPE (obj)) { - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - nlmsg = _nl_msg_new_route (RTM_DELROUTE, 0, obj); - break; - case NMP_OBJECT_TYPE_ROUTING_RULE: - nlmsg = _nl_msg_new_routing_rule (RTM_DELRULE, 0, NMP_OBJECT_CAST_ROUTING_RULE (obj)); - break; - case NMP_OBJECT_TYPE_QDISC: - nlmsg = _nl_msg_new_qdisc (RTM_DELQDISC, 0, NMP_OBJECT_CAST_QDISC (obj)); - break; - case NMP_OBJECT_TYPE_TFILTER: - nlmsg = _nl_msg_new_tfilter (RTM_DELTFILTER, 0, NMP_OBJECT_CAST_TFILTER (obj)); - break; - default: - break; - } - - if (!nlmsg) - g_return_val_if_reached (FALSE); - return do_delete_object (platform, obj, nlmsg); +object_delete(NMPlatform *platform, const NMPObject *obj) +{ + nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL; + nm_auto_nlmsg struct nl_msg * nlmsg = NULL; + + if (!NMP_OBJECT_IS_STACKINIT(obj)) + obj_keep_alive = nmp_object_ref(obj); + + switch (NMP_OBJECT_GET_TYPE(obj)) { + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + nlmsg = _nl_msg_new_route(RTM_DELROUTE, 0, obj); + break; + case NMP_OBJECT_TYPE_ROUTING_RULE: + nlmsg = _nl_msg_new_routing_rule(RTM_DELRULE, 0, NMP_OBJECT_CAST_ROUTING_RULE(obj)); + break; + case NMP_OBJECT_TYPE_QDISC: + nlmsg = _nl_msg_new_qdisc(RTM_DELQDISC, 0, NMP_OBJECT_CAST_QDISC(obj)); + break; + case NMP_OBJECT_TYPE_TFILTER: + nlmsg = _nl_msg_new_tfilter(RTM_DELTFILTER, 0, NMP_OBJECT_CAST_TFILTER(obj)); + break; + default: + break; + } + + if (!nlmsg) + g_return_val_if_reached(FALSE); + return do_delete_object(platform, obj, nlmsg); } /*****************************************************************************/ static int -ip_route_get (NMPlatform *platform, - int addr_family, - gconstpointer address, - int oif_ifindex, - NMPObject **out_route) -{ - const gboolean is_v4 = (addr_family == AF_INET); - const int addr_len = is_v4 ? 4 : 16; - int try_count = 0; - WaitForNlResponseResult seq_result; - int nle; - nm_auto_nmpobj NMPObject *route = NULL; - - nm_assert (NM_IS_LINUX_PLATFORM (platform)); - nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - nm_assert (address); - - do { - struct { - struct nlmsghdr n; - struct rtmsg r; - char buf[64]; - } req = { - .n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)), - .n.nlmsg_flags = NLM_F_REQUEST, - .n.nlmsg_type = RTM_GETROUTE, - .r.rtm_family = addr_family, - .r.rtm_tos = 0, - .r.rtm_dst_len = is_v4 ? 32 : 128, - .r.rtm_flags = 0x1000 /* RTM_F_LOOKUP_TABLE */, - }; - - nm_clear_pointer (&route, nmp_object_unref); - - if (!_nl_addattr_l (&req.n, sizeof (req), RTA_DST, address, addr_len)) - nm_assert_not_reached (); - - if (oif_ifindex > 0) { - gint32 ii = oif_ifindex; - - if (!_nl_addattr_l (&req.n, sizeof (req), RTA_OIF, &ii, sizeof (ii))) - nm_assert_not_reached (); - } - - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - nle = _nl_send_nlmsghdr (platform, &req.n, &seq_result, NULL, DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET, &route); - if (nle < 0) { - _LOGE ("get-route: failure sending netlink request \"%s\" (%d)", - nm_strerror_native (-nle), -nle); - return -NME_UNSPEC; - } - - delayed_action_handle_all (platform, FALSE); - - /* Retry, if we failed due to a cache resync. That can happen when the netlink - * socket fills up and we lost the response. */ - } while ( seq_result == WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC - && ++try_count < 10); - - if (seq_result < 0) { - /* negative seq_result is an errno from kernel. Map it to negative - * int (which are also errno). */ - return (int) seq_result; - } - - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { - if (route) { - NM_SET_OUT (out_route, g_steal_pointer (&route)); - return 0; - } - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; - } - - return -NME_UNSPEC; +ip_route_get(NMPlatform * platform, + int addr_family, + gconstpointer address, + int oif_ifindex, + NMPObject ** out_route) +{ + const gboolean is_v4 = (addr_family == AF_INET); + const int addr_len = is_v4 ? 4 : 16; + int try_count = 0; + WaitForNlResponseResult seq_result; + int nle; + nm_auto_nmpobj NMPObject *route = NULL; + + nm_assert(NM_IS_LINUX_PLATFORM(platform)); + nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); + nm_assert(address); + + do { + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[64]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETROUTE, + .r.rtm_family = addr_family, + .r.rtm_tos = 0, + .r.rtm_dst_len = is_v4 ? 32 : 128, + .r.rtm_flags = 0x1000 /* RTM_F_LOOKUP_TABLE */, + }; + + nm_clear_pointer(&route, nmp_object_unref); + + if (!_nl_addattr_l(&req.n, sizeof(req), RTA_DST, address, addr_len)) + nm_assert_not_reached(); + + if (oif_ifindex > 0) { + gint32 ii = oif_ifindex; + + if (!_nl_addattr_l(&req.n, sizeof(req), RTA_OIF, &ii, sizeof(ii))) + nm_assert_not_reached(); + } + + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + nle = _nl_send_nlmsghdr(platform, + &req.n, + &seq_result, + NULL, + DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET, + &route); + if (nle < 0) { + _LOGE("get-route: failure sending netlink request \"%s\" (%d)", + nm_strerror_native(-nle), + -nle); + return -NME_UNSPEC; + } + + delayed_action_handle_all(platform, FALSE); + + /* Retry, if we failed due to a cache resync. That can happen when the netlink + * socket fills up and we lost the response. */ + } while (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC && ++try_count < 10); + + if (seq_result < 0) { + /* negative seq_result is an errno from kernel. Map it to negative + * int (which are also errno). */ + return (int) seq_result; + } + + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { + if (route) { + NM_SET_OUT(out_route, g_steal_pointer(&route)); + return 0; + } + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; + } + + return -NME_UNSPEC; } /*****************************************************************************/ static int -routing_rule_add (NMPlatform *platform, - NMPNlmFlags flags, - const NMPlatformRoutingRule *routing_rule) +routing_rule_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformRoutingRule *routing_rule) { - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - nm_auto_nlmsg struct nl_msg *msg = NULL; - gs_free char *errmsg = NULL; - char s_buf[256]; - int nle; + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + nm_auto_nlmsg struct nl_msg *msg = NULL; + gs_free char * errmsg = NULL; + char s_buf[256]; + int nle; - msg = _nl_msg_new_routing_rule (RTM_NEWRULE, flags, routing_rule); + msg = _nl_msg_new_routing_rule(RTM_NEWRULE, flags, routing_rule); - event_handler_read_netlink (platform, FALSE); + event_handler_read_netlink(platform, FALSE); - nle = _nl_send_nlmsg (platform, msg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); - if (nle < 0) { - _LOGE ("do-add-rule: failed sending netlink request \"%s\" (%d)", - nm_strerror (nle), -nle); - return -NME_PL_NETLINK; - } + nle = _nl_send_nlmsg(platform, + msg, + &seq_result, + &errmsg, + DELAYED_ACTION_RESPONSE_TYPE_VOID, + NULL); + if (nle < 0) { + _LOGE("do-add-rule: failed sending netlink request \"%s\" (%d)", nm_strerror(nle), -nle); + return -NME_PL_NETLINK; + } - delayed_action_handle_all (platform, FALSE); + delayed_action_handle_all(platform, FALSE); - nm_assert (seq_result); + nm_assert(seq_result); - _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK - ? LOGL_DEBUG - : LOGL_WARN, - "do-add-rule: %s", - wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); + _NMLOG(seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG : LOGL_WARN, + "do-add-rule: %s", + wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf))); - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - return 0; - if (seq_result < 0) - return seq_result; - return -NME_UNSPEC; + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) + return 0; + if (seq_result < 0) + return seq_result; + return -NME_UNSPEC; } /*****************************************************************************/ static int -qdisc_add (NMPlatform *platform, - NMPNlmFlags flags, - const NMPlatformQdisc *qdisc) +qdisc_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformQdisc *qdisc) { - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - gs_free char *errmsg = NULL; - int nle; - char s_buf[256]; - nm_auto_nlmsg struct nl_msg *msg = NULL; + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + gs_free char * errmsg = NULL; + int nle; + char s_buf[256]; + nm_auto_nlmsg struct nl_msg *msg = NULL; - /* Note: @qdisc must not be copied or kept alive because the lifetime of qdisc.kind - * is undefined. */ + /* Note: @qdisc must not be copied or kept alive because the lifetime of qdisc.kind + * is undefined. */ - msg = _nl_msg_new_qdisc (RTM_NEWQDISC, flags, qdisc); + msg = _nl_msg_new_qdisc(RTM_NEWQDISC, flags, qdisc); - event_handler_read_netlink (platform, FALSE); + event_handler_read_netlink(platform, FALSE); - nle = _nl_send_nlmsg (platform, msg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); - if (nle < 0) { - _LOGE ("do-add-qdisc: failed sending netlink request \"%s\" (%d)", - nm_strerror (nle), -nle); - return -NME_PL_NETLINK; - } + nle = _nl_send_nlmsg(platform, + msg, + &seq_result, + &errmsg, + DELAYED_ACTION_RESPONSE_TYPE_VOID, + NULL); + if (nle < 0) { + _LOGE("do-add-qdisc: failed sending netlink request \"%s\" (%d)", nm_strerror(nle), -nle); + return -NME_PL_NETLINK; + } - delayed_action_handle_all (platform, FALSE); + delayed_action_handle_all(platform, FALSE); - nm_assert (seq_result); + nm_assert(seq_result); - _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK - ? LOGL_DEBUG - : LOGL_WARN, - "do-add-qdisc: %s", - wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); + _NMLOG(seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG : LOGL_WARN, + "do-add-qdisc: %s", + wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf))); - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - return 0; - if (seq_result < 0) - return seq_result; - return -NME_UNSPEC; + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) + return 0; + if (seq_result < 0) + return seq_result; + return -NME_UNSPEC; } /*****************************************************************************/ static int -tfilter_add (NMPlatform *platform, - NMPNlmFlags flags, - const NMPlatformTfilter *tfilter) +tfilter_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformTfilter *tfilter) { - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - gs_free char *errmsg = NULL; - int nle; - char s_buf[256]; - nm_auto_nlmsg struct nl_msg *msg = NULL; + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + gs_free char * errmsg = NULL; + int nle; + char s_buf[256]; + nm_auto_nlmsg struct nl_msg *msg = NULL; - /* Note: @tfilter must not be copied or kept alive because the lifetime of tfilter.kind - * and tfilter.action.kind is undefined. */ + /* Note: @tfilter must not be copied or kept alive because the lifetime of tfilter.kind + * and tfilter.action.kind is undefined. */ - msg = _nl_msg_new_tfilter (RTM_NEWTFILTER, flags, tfilter); + msg = _nl_msg_new_tfilter(RTM_NEWTFILTER, flags, tfilter); - event_handler_read_netlink (platform, FALSE); + event_handler_read_netlink(platform, FALSE); - nle = _nl_send_nlmsg (platform, msg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); - if (nle < 0) { - _LOGE ("do-add-tfilter: failed sending netlink request \"%s\" (%d)", - nm_strerror (nle), -nle); - return -NME_PL_NETLINK; - } + nle = _nl_send_nlmsg(platform, + msg, + &seq_result, + &errmsg, + DELAYED_ACTION_RESPONSE_TYPE_VOID, + NULL); + if (nle < 0) { + _LOGE("do-add-tfilter: failed sending netlink request \"%s\" (%d)", nm_strerror(nle), -nle); + return -NME_PL_NETLINK; + } - delayed_action_handle_all (platform, FALSE); + delayed_action_handle_all(platform, FALSE); - nm_assert (seq_result); + nm_assert(seq_result); - _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK - ? LOGL_DEBUG - : LOGL_WARN, - "do-add-tfilter: %s", - wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); + _NMLOG(seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG : LOGL_WARN, + "do-add-tfilter: %s", + wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf))); - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - return 0; + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) + return 0; - return -NME_UNSPEC; + return -NME_UNSPEC; } /*****************************************************************************/ static gboolean -event_handler (int fd, - GIOCondition io_condition, - gpointer user_data) +event_handler(int fd, GIOCondition io_condition, gpointer user_data) { - delayed_action_handle_all (NM_PLATFORM (user_data), TRUE); - return TRUE; + delayed_action_handle_all(NM_PLATFORM(user_data), TRUE); + return TRUE; } /*****************************************************************************/ /* copied from libnl3's recvmsgs() */ static int -event_handler_recvmsgs (NMPlatform *platform, gboolean handle_events) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nl_sock *sk = priv->nlh; - int n; - int err = 0; - gboolean multipart = 0; - gboolean interrupted = FALSE; - struct nlmsghdr *hdr; - WaitForNlResponseResult seq_result; - struct sockaddr_nl nla = {0}; - struct ucred creds; - gboolean creds_has; - nm_auto_free unsigned char *buf = NULL; +event_handler_recvmsgs(NMPlatform *platform, gboolean handle_events) +{ + NMLinuxPlatformPrivate * priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + struct nl_sock * sk = priv->nlh; + int n; + int err = 0; + gboolean multipart = 0; + gboolean interrupted = FALSE; + struct nlmsghdr * hdr; + WaitForNlResponseResult seq_result; + struct sockaddr_nl nla = {0}; + struct ucred creds; + gboolean creds_has; + nm_auto_free unsigned char *buf = NULL; continue_reading: - nm_clear_pointer (&buf, free); - n = nl_recv (sk, &nla, &buf, &creds, &creds_has); - - if (n <= 0) { - - if (n == -NME_NL_MSG_TRUNC) { - int buf_size; - - /* the message receive buffer was too small. We lost one message, which - * is unfortunate. Try to double the buffer size for the next time. */ - buf_size = nl_socket_get_msg_buf_size (sk); - if (buf_size < 512*1024) { - buf_size *= 2; - _LOGT ("netlink: recvmsg: increase message buffer size for recvmsg() to %d bytes", buf_size); - if (nl_socket_set_msg_buf_size (sk, buf_size) < 0) - nm_assert_not_reached (); - if (!handle_events) - goto continue_reading; - } - } - - return n; - } - - hdr = (struct nlmsghdr *) buf; - while (nlmsg_ok (hdr, n)) { - nm_auto_nlmsg struct nl_msg *msg = NULL; - gboolean abort_parsing = FALSE; - gboolean process_valid_msg = FALSE; - guint32 seq_number; - char buf_nlmsghdr[400]; - const char *extack_msg = NULL; - - msg = nlmsg_alloc_convert (hdr); - - nlmsg_set_proto (msg, NETLINK_ROUTE); - nlmsg_set_src (msg, &nla); - - if (!creds_has || creds.pid) { - if (!creds_has) - _LOGT ("netlink: recvmsg: received message without credentials"); - else - _LOGT ("netlink: recvmsg: received non-kernel message (pid %d)", creds.pid); - err = 0; - goto stop; - } - - _LOGt ("netlink: recvmsg: new message %s", - nl_nlmsghdr_to_str (hdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); - - nlmsg_set_creds (msg, &creds); - - if (hdr->nlmsg_flags & NLM_F_MULTI) - multipart = TRUE; - - if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { - /* - * We have to continue reading to clear - * all messages until a NLMSG_DONE is - * received and report the inconsistency. - */ - interrupted = TRUE; - } - - /* Other side wishes to see an ack for this message */ - if (hdr->nlmsg_flags & NLM_F_ACK) { - /* FIXME: implement */ - } - - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; - - if (hdr->nlmsg_type == NLMSG_DONE) { - /* messages terminates a multipart message, this is - * usually the end of a message and therefore we slip - * out of the loop by default. the user may overrule - * this action by skipping this packet. */ - multipart = FALSE; - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; - } else if (hdr->nlmsg_type == NLMSG_NOOP) { - /* Message to be ignored, the default action is to - * skip this message if no callback is specified. The - * user may overrule this action by returning - * NL_PROCEED. */ - } else if (hdr->nlmsg_type == NLMSG_OVERRUN) { - /* Data got lost, report back to user. The default action is to - * quit parsing. The user may overrule this action by retuning - * NL_SKIP or NL_PROCEED (dangerous) */ - err = -NME_NL_MSG_OVERFLOW; - abort_parsing = TRUE; - } else if (hdr->nlmsg_type == NLMSG_ERROR) { - /* Message carries a nlmsgerr */ - struct nlmsgerr *e = nlmsg_data (hdr); - - if (hdr->nlmsg_len < nlmsg_size (sizeof (*e))) { - /* Truncated error message, the default action - * is to stop parsing. The user may overrule - * this action by returning NL_SKIP or - * NL_PROCEED (dangerous) */ - err = -NME_NL_MSG_TRUNC; - abort_parsing = TRUE; - } else if (e->error) { - int errsv = nm_errno_native (e->error); - - if ( NM_FLAGS_HAS (hdr->nlmsg_flags, NLM_F_ACK_TLVS) - && hdr->nlmsg_len >= sizeof (*e) + e->msg.nlmsg_len) { - static const struct nla_policy policy[] = { - [NLMSGERR_ATTR_MSG] = { .type = NLA_STRING }, - [NLMSGERR_ATTR_OFFS] = { .type = NLA_U32 }, - }; - struct nlattr *tb[G_N_ELEMENTS (policy)]; - struct nlattr *tlvs; - - tlvs = (struct nlattr *) ((char *) e + sizeof (*e) + e->msg.nlmsg_len - NLMSG_HDRLEN); - if (nla_parse_arr (tb, - tlvs, - hdr->nlmsg_len - sizeof (*e) - e->msg.nlmsg_len, - policy) >= 0) { - if (tb[NLMSGERR_ATTR_MSG]) - extack_msg = nla_get_string (tb[NLMSGERR_ATTR_MSG]); - } - } - - /* Error message reported back from kernel. */ - _LOGD ("netlink: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d", - nm_strerror_native (errsv), - errsv, - NM_PRINT_FMT_QUOTED (extack_msg, " \"", extack_msg, "\"", ""), - nlmsg_hdr (msg)->nlmsg_seq); - seq_result = -NM_ERRNO_NATIVE (errsv); - } else - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; - } else - process_valid_msg = TRUE; - - seq_number = nlmsg_hdr (msg)->nlmsg_seq; - - /* check whether the seq number is different from before, and - * whether the previous number (@nlh_seq_last_seen) is a pending - * refresh-all request. In that case, the pending request is thereby - * completed. - * - * We must do that before processing the message with event_valid_msg(), - * because we must track the completion of the pending request before that. */ - event_seq_check_refresh_all (platform, seq_number); - - if (process_valid_msg) { - /* Valid message (not checking for MULTIPART bit to - * get along with broken kernels. NL_SKIP has no - * effect on this. */ - - event_valid_msg (platform, msg, handle_events); - - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; - } - - event_seq_check (platform, seq_number, seq_result, extack_msg); - - if (abort_parsing) - goto stop; - - err = 0; - hdr = nlmsg_next (hdr, &n); - } - - if (multipart) { - /* Multipart message not yet complete, continue reading */ - goto continue_reading; - } + nm_clear_pointer(&buf, free); + n = nl_recv(sk, &nla, &buf, &creds, &creds_has); + + if (n <= 0) { + if (n == -NME_NL_MSG_TRUNC) { + int buf_size; + + /* the message receive buffer was too small. We lost one message, which + * is unfortunate. Try to double the buffer size for the next time. */ + buf_size = nl_socket_get_msg_buf_size(sk); + if (buf_size < 512 * 1024) { + buf_size *= 2; + _LOGT("netlink: recvmsg: increase message buffer size for recvmsg() to %d bytes", + buf_size); + if (nl_socket_set_msg_buf_size(sk, buf_size) < 0) + nm_assert_not_reached(); + if (!handle_events) + goto continue_reading; + } + } + + return n; + } + + hdr = (struct nlmsghdr *) buf; + while (nlmsg_ok(hdr, n)) { + nm_auto_nlmsg struct nl_msg *msg = NULL; + gboolean abort_parsing = FALSE; + gboolean process_valid_msg = FALSE; + guint32 seq_number; + char buf_nlmsghdr[400]; + const char * extack_msg = NULL; + + msg = nlmsg_alloc_convert(hdr); + + nlmsg_set_proto(msg, NETLINK_ROUTE); + nlmsg_set_src(msg, &nla); + + if (!creds_has || creds.pid) { + if (!creds_has) + _LOGT("netlink: recvmsg: received message without credentials"); + else + _LOGT("netlink: recvmsg: received non-kernel message (pid %d)", creds.pid); + err = 0; + goto stop; + } + + _LOGt("netlink: recvmsg: new message %s", + nl_nlmsghdr_to_str(hdr, buf_nlmsghdr, sizeof(buf_nlmsghdr))); + + nlmsg_set_creds(msg, &creds); + + if (hdr->nlmsg_flags & NLM_F_MULTI) + multipart = TRUE; + + if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { + /* + * We have to continue reading to clear + * all messages until a NLMSG_DONE is + * received and report the inconsistency. + */ + interrupted = TRUE; + } + + /* Other side wishes to see an ack for this message */ + if (hdr->nlmsg_flags & NLM_F_ACK) { + /* FIXME: implement */ + } + + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; + + if (hdr->nlmsg_type == NLMSG_DONE) { + /* messages terminates a multipart message, this is + * usually the end of a message and therefore we slip + * out of the loop by default. the user may overrule + * this action by skipping this packet. */ + multipart = FALSE; + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + } else if (hdr->nlmsg_type == NLMSG_NOOP) { + /* Message to be ignored, the default action is to + * skip this message if no callback is specified. The + * user may overrule this action by returning + * NL_PROCEED. */ + } else if (hdr->nlmsg_type == NLMSG_OVERRUN) { + /* Data got lost, report back to user. The default action is to + * quit parsing. The user may overrule this action by returning + * NL_SKIP or NL_PROCEED (dangerous) */ + err = -NME_NL_MSG_OVERFLOW; + abort_parsing = TRUE; + } else if (hdr->nlmsg_type == NLMSG_ERROR) { + /* Message carries a nlmsgerr */ + struct nlmsgerr *e = nlmsg_data(hdr); + + if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) { + /* Truncated error message, the default action + * is to stop parsing. The user may overrule + * this action by returning NL_SKIP or + * NL_PROCEED (dangerous) */ + err = -NME_NL_MSG_TRUNC; + abort_parsing = TRUE; + } else if (e->error) { + int errsv = nm_errno_native(e->error); + + if (NM_FLAGS_HAS(hdr->nlmsg_flags, NLM_F_ACK_TLVS) + && hdr->nlmsg_len >= sizeof(*e) + e->msg.nlmsg_len) { + static const struct nla_policy policy[] = { + [NLMSGERR_ATTR_MSG] = {.type = NLA_STRING}, + [NLMSGERR_ATTR_OFFS] = {.type = NLA_U32}, + }; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + struct nlattr *tlvs; + + tlvs = (struct nlattr *) ((char *) e + sizeof(*e) + e->msg.nlmsg_len + - NLMSG_HDRLEN); + if (nla_parse_arr(tb, + tlvs, + hdr->nlmsg_len - sizeof(*e) - e->msg.nlmsg_len, + policy) + >= 0) { + if (tb[NLMSGERR_ATTR_MSG]) + extack_msg = nla_get_string(tb[NLMSGERR_ATTR_MSG]); + } + } + + /* Error message reported back from kernel. */ + _LOGD("netlink: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d", + nm_strerror_native(errsv), + errsv, + NM_PRINT_FMT_QUOTED(extack_msg, " \"", extack_msg, "\"", ""), + nlmsg_hdr(msg)->nlmsg_seq); + seq_result = -NM_ERRNO_NATIVE(errsv); + } else + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + } else + process_valid_msg = TRUE; + + seq_number = nlmsg_hdr(msg)->nlmsg_seq; + + /* check whether the seq number is different from before, and + * whether the previous number (@nlh_seq_last_seen) is a pending + * refresh-all request. In that case, the pending request is thereby + * completed. + * + * We must do that before processing the message with event_valid_msg(), + * because we must track the completion of the pending request before that. */ + event_seq_check_refresh_all(platform, seq_number); + + if (process_valid_msg) { + /* Valid message (not checking for MULTIPART bit to + * get along with broken kernels. NL_SKIP has no + * effect on this. */ + + event_valid_msg(platform, msg, handle_events); + + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + } + + event_seq_check(platform, seq_number, seq_result, extack_msg); + + if (abort_parsing) + goto stop; + + err = 0; + hdr = nlmsg_next(hdr, &n); + } + + if (multipart) { + /* Multipart message not yet complete, continue reading */ + goto continue_reading; + } stop: - if (!handle_events) { - /* when we don't handle events, we want to drain all messages from the socket - * without handling the messages (but still check for sequence numbers). - * Repeat reading. */ - goto continue_reading; - } + if (!handle_events) { + /* when we don't handle events, we want to drain all messages from the socket + * without handling the messages (but still check for sequence numbers). + * Repeat reading. */ + goto continue_reading; + } - if (interrupted) - return -NME_NL_DUMP_INTR; - return err; + if (interrupted) + return -NME_NL_DUMP_INTR; + return err; } /*****************************************************************************/ static gboolean -event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int r; - struct pollfd pfd; - gboolean any = FALSE; - int timeout_msec; - struct { - guint32 seq_number; - gint64 timeout_abs_ns; - gint64 now_ns; - } next; - - if (!nm_platform_netns_push (platform, &netns)) { - delayed_action_wait_for_nl_response_complete_all (platform, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS); - return FALSE; - } - - for (;;) { - for (;;) { - int nle; - - nle = event_handler_recvmsgs (platform, TRUE); - - if (nle < 0) { - switch (nle) { - case -EAGAIN: - goto after_read; - case -NME_NL_DUMP_INTR: - _LOGD ("netlink: read: uncritical failure to retrieve incoming events: %s (%d)", nm_strerror (nle), nle); - break; - case -NME_NL_MSG_TRUNC: - case -ENOBUFS: - _LOGI ("netlink: read: %s. Need to resynchronize platform cache", - ({ - const char *_reason = "unknown"; - switch (nle) { - case -NME_NL_MSG_TRUNC: _reason = "message truncated"; break; - case -ENOBUFS: _reason = "too many netlink events"; break; - } - _reason; - })); - event_handler_recvmsgs (platform, FALSE); - delayed_action_wait_for_nl_response_complete_all (platform, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC); - - delayed_action_schedule (platform, - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, - NULL); - break; - default: - _LOGE ("netlink: read: failed to retrieve incoming events: %s (%d)", nm_strerror (nle), nle); - break; - } - } - any = TRUE; - } +event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks) +{ + nm_auto_pop_netns NMPNetns *netns = NULL; + NMLinuxPlatformPrivate * priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + int r; + struct pollfd pfd; + gboolean any = FALSE; + int timeout_msec; + struct { + guint32 seq_number; + gint64 timeout_abs_ns; + gint64 now_ns; + } next; + + if (!nm_platform_netns_push(platform, &netns)) { + delayed_action_wait_for_nl_response_complete_all(platform, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS); + return FALSE; + } + + for (;;) { + for (;;) { + int nle; + + nle = event_handler_recvmsgs(platform, TRUE); + + if (nle < 0) { + switch (nle) { + case -EAGAIN: + goto after_read; + case -NME_NL_DUMP_INTR: + _LOGD("netlink: read: uncritical failure to retrieve incoming events: %s (%d)", + nm_strerror(nle), + nle); + break; + case -NME_NL_MSG_TRUNC: + case -ENOBUFS: + _LOGI("netlink: read: %s. Need to resynchronize platform cache", ({ + const char *_reason = "unknown"; + switch (nle) { + case -NME_NL_MSG_TRUNC: + _reason = "message truncated"; + break; + case -ENOBUFS: + _reason = "too many netlink events"; + break; + } + _reason; + })); + event_handler_recvmsgs(platform, FALSE); + delayed_action_wait_for_nl_response_complete_all( + platform, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC); + + delayed_action_schedule(platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL + | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS + | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, + NULL); + break; + default: + _LOGE("netlink: read: failed to retrieve incoming events: %s (%d)", + nm_strerror(nle), + nle); + break; + } + } + any = TRUE; + } after_read: - if (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) - return any; + if (!NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) + return any; - delayed_action_wait_for_nl_response_complete_check (platform, - WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN, - &next.seq_number, - &next.timeout_abs_ns, - &next.now_ns); + delayed_action_wait_for_nl_response_complete_check(platform, + WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN, + &next.seq_number, + &next.timeout_abs_ns, + &next.now_ns); - if ( !wait_for_acks - || !NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) - return any; + if (!wait_for_acks + || !NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) + return any; - nm_assert (next.seq_number); - nm_assert (next.now_ns > 0); - nm_assert (next.timeout_abs_ns > next.now_ns); + nm_assert(next.seq_number); + nm_assert(next.now_ns > 0); + nm_assert(next.timeout_abs_ns > next.now_ns); - _LOGT ("netlink: read: wait for ACK for sequence number %u...", next.seq_number); + _LOGT("netlink: read: wait for ACK for sequence number %u...", next.seq_number); - timeout_msec = (next.timeout_abs_ns - next.now_ns) / (NM_UTILS_NSEC_PER_SEC / 1000); + timeout_msec = (next.timeout_abs_ns - next.now_ns) / (NM_UTILS_NSEC_PER_SEC / 1000); - memset (&pfd, 0, sizeof (pfd)); - pfd.fd = nl_socket_get_fd (priv->nlh); - pfd.events = POLLIN; - r = poll (&pfd, 1, MAX (1, timeout_msec)); + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = nl_socket_get_fd(priv->nlh); + pfd.events = POLLIN; + r = poll(&pfd, 1, MAX(1, timeout_msec)); - if (r == 0) { - /* timeout and there is nothing to read. */ - goto after_read; - } + if (r == 0) { + /* timeout and there is nothing to read. */ + goto after_read; + } - if (r < 0) { - int errsv = errno; + if (r < 0) { + int errsv = errno; - if (errsv != EINTR) { - _LOGE ("netlink: read: poll failed with %s", nm_strerror_native (errsv)); - delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL); - return any; - } - /* Continue to read again, even if there might be nothing to read after EINTR. */ - } - } + if (errsv != EINTR) { + _LOGE("netlink: read: poll failed with %s", nm_strerror_native(errsv)); + delayed_action_wait_for_nl_response_complete_all( + platform, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL); + return any; + } + /* Continue to read again, even if there might be nothing to read after EINTR. */ + } + } } /*****************************************************************************/ static void -cache_update_link_udev (NMPlatform *platform, - int ifindex, - struct udev_device *udevice) +cache_update_link_udev(NMPlatform *platform, int ifindex, struct udev_device *udevice) { - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_new = NULL; - NMPCacheOpsType cache_op; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + NMPCacheOpsType cache_op; - cache_op = nmp_cache_update_link_udev (nm_platform_get_cache (platform), ifindex, udevice, &obj_old, &obj_new); + cache_op = nmp_cache_update_link_udev(nm_platform_get_cache(platform), + ifindex, + udevice, + &obj_old, + &obj_new); - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - nm_auto_pop_netns NMPNetns *netns = NULL; + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + nm_auto_pop_netns NMPNetns *netns = NULL; - cache_on_change (platform, cache_op, obj_old, obj_new); - if (!nm_platform_netns_push (platform, &netns)) - return; - nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); - } + cache_on_change(platform, cache_op, obj_old, obj_new); + if (!nm_platform_netns_push(platform, &netns)) + return; + nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); + } } static void -udev_device_added (NMPlatform *platform, - struct udev_device *udevice) -{ - const char *ifname; - const char *ifindex_s; - int ifindex; - - ifname = udev_device_get_sysname (udevice); - if (!ifname) { - _LOGD ("udev-add: failed to get device's interface"); - return; - } - - ifindex_s = udev_device_get_property_value (udevice, "IFINDEX"); - if (!ifindex_s) { - _LOGW ("udev-add[%s]failed to get device's ifindex", ifname); - return; - } - ifindex = _nm_utils_ascii_str_to_int64 (ifindex_s, 10, 1, G_MAXINT, 0); - if (ifindex <= 0) { - _LOGW ("udev-add[%s]: retrieved invalid IFINDEX=%d", ifname, ifindex); - return; - } - - if (!udev_device_get_syspath (udevice)) { - _LOGD ("udev-add[%s,%d]: couldn't determine device path; ignoring...", ifname, ifindex); - return; - } - - _LOGT ("udev-add[%s,%d]: device added", ifname, ifindex); - cache_update_link_udev (platform, ifindex, udevice); +udev_device_added(NMPlatform *platform, struct udev_device *udevice) +{ + const char *ifname; + const char *ifindex_s; + int ifindex; + + ifname = udev_device_get_sysname(udevice); + if (!ifname) { + _LOGD("udev-add: failed to get device's interface"); + return; + } + + ifindex_s = udev_device_get_property_value(udevice, "IFINDEX"); + if (!ifindex_s) { + _LOGW("udev-add[%s]failed to get device's ifindex", ifname); + return; + } + ifindex = _nm_utils_ascii_str_to_int64(ifindex_s, 10, 1, G_MAXINT, 0); + if (ifindex <= 0) { + _LOGW("udev-add[%s]: retrieved invalid IFINDEX=%d", ifname, ifindex); + return; + } + + if (!udev_device_get_syspath(udevice)) { + _LOGD("udev-add[%s,%d]: couldn't determine device path; ignoring...", ifname, ifindex); + return; + } + + _LOGT("udev-add[%s,%d]: device added", ifname, ifindex); + cache_update_link_udev(platform, ifindex, udevice); } static gboolean -_udev_device_removed_match_link (const NMPObject *obj, gpointer udevice) +_udev_device_removed_match_link(const NMPObject *obj, gpointer udevice) { - return obj->_link.udev.device == udevice; + return obj->_link.udev.device == udevice; } static void -udev_device_removed (NMPlatform *platform, - struct udev_device *udevice) +udev_device_removed(NMPlatform *platform, struct udev_device *udevice) { - const char *ifindex_s; - int ifindex = 0; + const char *ifindex_s; + int ifindex = 0; - ifindex_s = udev_device_get_property_value (udevice, "IFINDEX"); - ifindex = _nm_utils_ascii_str_to_int64 (ifindex_s, 10, 1, G_MAXINT, 0); - if (ifindex <= 0) { - const NMPObject *obj; + ifindex_s = udev_device_get_property_value(udevice, "IFINDEX"); + ifindex = _nm_utils_ascii_str_to_int64(ifindex_s, 10, 1, G_MAXINT, 0); + if (ifindex <= 0) { + const NMPObject *obj; - obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), - 0, NULL, FALSE, NM_LINK_TYPE_NONE, _udev_device_removed_match_link, udevice); - if (obj) - ifindex = obj->link.ifindex; - } + obj = nmp_cache_lookup_link_full(nm_platform_get_cache(platform), + 0, + NULL, + FALSE, + NM_LINK_TYPE_NONE, + _udev_device_removed_match_link, + udevice); + if (obj) + ifindex = obj->link.ifindex; + } - _LOGD ("udev-remove: IFINDEX=%d", ifindex); - if (ifindex <= 0) - return; + _LOGD("udev-remove: IFINDEX=%d", ifindex); + if (ifindex <= 0) + return; - cache_update_link_udev (platform, ifindex, NULL); + cache_update_link_udev(platform, ifindex, NULL); } static void -handle_udev_event (NMUdevClient *udev_client, - struct udev_device *udevice, - gpointer user_data) +handle_udev_event(NMUdevClient *udev_client, struct udev_device *udevice, gpointer user_data) { - nm_auto_pop_netns NMPNetns *netns = NULL; - NMPlatform *platform = NM_PLATFORM (user_data); - const char *subsys; - const char *ifindex; - guint64 seqnum; - const char *action; + nm_auto_pop_netns NMPNetns *netns = NULL; + NMPlatform * platform = NM_PLATFORM(user_data); + const char * subsys; + const char * ifindex; + guint64 seqnum; + const char * action; - action = udev_device_get_action (udevice); - g_return_if_fail (action); + action = udev_device_get_action(udevice); + g_return_if_fail(action); - subsys = udev_device_get_subsystem (udevice); - g_return_if_fail (nm_streq0 (subsys, "net")); + subsys = udev_device_get_subsystem(udevice); + g_return_if_fail(nm_streq0(subsys, "net")); - if (!nm_platform_netns_push (platform, &netns)) - return; + if (!nm_platform_netns_push(platform, &netns)) + return; - ifindex = udev_device_get_property_value (udevice, "IFINDEX"); - seqnum = udev_device_get_seqnum (udevice); - _LOGD ("UDEV event: action '%s' subsys '%s' device '%s' (%s); seqnum=%" G_GUINT64_FORMAT, - action, subsys, udev_device_get_sysname (udevice), - ifindex ?: "unknown", seqnum); + ifindex = udev_device_get_property_value(udevice, "IFINDEX"); + seqnum = udev_device_get_seqnum(udevice); + _LOGD("UDEV event: action '%s' subsys '%s' device '%s' (%s); seqnum=%" G_GUINT64_FORMAT, + action, + subsys, + udev_device_get_sysname(udevice), + ifindex ?: "unknown", + seqnum); - if (NM_IN_STRSET (action, "add", "move")) - udev_device_added (platform, udevice); - else if (NM_IN_STRSET (action, "remove")) - udev_device_removed (platform, udevice); + if (NM_IN_STRSET(action, "add", "move")) + udev_device_added(platform, udevice); + else if (NM_IN_STRSET(action, "remove")) + udev_device_removed(platform, udevice); } /*****************************************************************************/ void -nm_linux_platform_setup (void) +nm_linux_platform_setup(void) { - nm_platform_setup (nm_linux_platform_new (FALSE, FALSE)); + nm_platform_setup(nm_linux_platform_new(FALSE, FALSE)); } /*****************************************************************************/ static void -nm_linux_platform_init (NMLinuxPlatform *self) +nm_linux_platform_init(NMLinuxPlatform *self) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (self); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(self); - priv->delayed_action.list_master_connected = g_ptr_array_new (); - priv->delayed_action.list_refresh_link = g_ptr_array_new (); - priv->delayed_action.list_wait_for_nl_response = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData)); + priv->delayed_action.list_master_connected = g_ptr_array_new(); + priv->delayed_action.list_refresh_link = g_ptr_array_new(); + priv->delayed_action.list_wait_for_nl_response = + g_array_new(FALSE, TRUE, sizeof(DelayedActionWaitForNlResponseData)); } static void -constructed (GObject *_object) -{ - NMPlatform *platform = NM_PLATFORM (_object); - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int nle; - int fd; - - nm_assert (!platform->_netns || platform->_netns == nmp_netns_get_current ()); - - if (nm_platform_get_use_udev (platform)) { - priv->udev_client = nm_udev_client_new (NM_MAKE_STRV ("net"), - handle_udev_event, platform); - } - - _LOGD ("create (%s netns, %s, %s udev)", - !platform->_netns ? "ignore" : "use", - !platform->_netns && nmp_netns_is_initial () - ? "initial netns" - : (!nmp_netns_get_current () - ? "no netns support" - : nm_sprintf_bufa (100, "in netns[%p]%s", - nmp_netns_get_current (), - nmp_netns_get_current () == nmp_netns_get_initial () ? "/main" : "")), - nm_platform_get_use_udev (platform) ? "use" : "no"); - - - priv->genl = nl_socket_alloc (); - g_assert (priv->genl); - - nle = nl_connect (priv->genl, NETLINK_GENERIC); - if (nle) { - _LOGE ("unable to connect the generic netlink socket \"%s\" (%d)", - nm_strerror (nle), -nle); - nl_socket_free (priv->genl); - priv->genl = NULL; - } - - priv->nlh = nl_socket_alloc (); - g_assert (priv->nlh); - - nle = nl_connect (priv->nlh, NETLINK_ROUTE); - g_assert (!nle); - nle = nl_socket_set_passcred (priv->nlh, 1); - g_assert (!nle); - - /* No blocking for event socket, so that we can drain it safely. */ - nle = nl_socket_set_nonblocking (priv->nlh); - g_assert (!nle); - - /* use 8 MB for receive socket kernel queue. */ - nle = nl_socket_set_buffer_size (priv->nlh, 8*1024*1024, 0); - g_assert (!nle); - - nle = nl_socket_set_ext_ack (priv->nlh, TRUE); - if (nle) - _LOGD ("could not enable extended acks on netlink socket"); - - /* explicitly set the msg buffer size and disable MSG_PEEK. - * If we later encounter NME_NL_MSG_TRUNC, we will adjust the buffer size. */ - nl_socket_disable_msg_peek (priv->nlh); - nle = nl_socket_set_msg_buf_size (priv->nlh, 32 * 1024); - g_assert (!nle); - - nle = nl_socket_add_memberships (priv->nlh, - RTNLGRP_IPV4_IFADDR, - RTNLGRP_IPV4_ROUTE, - RTNLGRP_IPV4_RULE, - RTNLGRP_IPV6_RULE, - RTNLGRP_IPV6_IFADDR, - RTNLGRP_IPV6_ROUTE, - RTNLGRP_LINK, - RTNLGRP_TC, - 0); - g_assert (!nle); - - fd = nl_socket_get_fd (priv->nlh); - - _LOGD ("Netlink socket for events established: port=%u, fd=%d", nl_socket_get_local_port (priv->nlh), fd); - - priv->event_source = nm_g_unix_fd_source_new (fd, - G_IO_IN | G_IO_NVAL | G_IO_PRI | G_IO_ERR | G_IO_HUP, - G_PRIORITY_DEFAULT, - event_handler, - platform, - NULL); - g_source_attach (priv->event_source, NULL); - - /* complete construction of the GObject instance before populating the cache. */ - G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object); - - _LOGD ("populate platform cache"); - delayed_action_schedule (platform, - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES | - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, - NULL); - - delayed_action_handle_all (platform, FALSE); - - /* Set up udev monitoring */ - if (priv->udev_client) { - struct udev_enumerate *enumerator; - struct udev_list_entry *devices, *l; - - /* And read initial device list */ - enumerator = nm_udev_client_enumerate_new (priv->udev_client); - - udev_enumerate_add_match_is_initialized (enumerator); - - udev_enumerate_scan_devices (enumerator); - - devices = udev_enumerate_get_list_entry (enumerator); - for (l = devices; l; l = udev_list_entry_get_next (l)) { - struct udev_device *udevice; - - udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerator), - udev_list_entry_get_name (l)); - if (!udevice) - continue; - - udev_device_added (platform, udevice); - udev_device_unref (udevice); - } - - udev_enumerate_unref (enumerator); - } +constructed(GObject *_object) +{ + NMPlatform * platform = NM_PLATFORM(_object); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + int nle; + int fd; + + nm_assert(!platform->_netns || platform->_netns == nmp_netns_get_current()); + + if (nm_platform_get_use_udev(platform)) { + priv->udev_client = nm_udev_client_new(NM_MAKE_STRV("net"), handle_udev_event, platform); + } + + _LOGD("create (%s netns, %s, %s udev)", + !platform->_netns ? "ignore" : "use", + !platform->_netns && nmp_netns_is_initial() + ? "initial netns" + : (!nmp_netns_get_current() + ? "no netns support" + : nm_sprintf_bufa(100, + "in netns[%p]%s", + nmp_netns_get_current(), + nmp_netns_get_current() == nmp_netns_get_initial() ? "/main" + : "")), + nm_platform_get_use_udev(platform) ? "use" : "no"); + + priv->genl = nl_socket_alloc(); + g_assert(priv->genl); + + nle = nl_connect(priv->genl, NETLINK_GENERIC); + if (nle) { + _LOGE("unable to connect the generic netlink socket \"%s\" (%d)", nm_strerror(nle), -nle); + nl_socket_free(priv->genl); + priv->genl = NULL; + } + + priv->nlh = nl_socket_alloc(); + g_assert(priv->nlh); + + nle = nl_connect(priv->nlh, NETLINK_ROUTE); + g_assert(!nle); + nle = nl_socket_set_passcred(priv->nlh, 1); + g_assert(!nle); + + /* No blocking for event socket, so that we can drain it safely. */ + nle = nl_socket_set_nonblocking(priv->nlh); + g_assert(!nle); + + /* use 8 MB for receive socket kernel queue. */ + nle = nl_socket_set_buffer_size(priv->nlh, 8 * 1024 * 1024, 0); + g_assert(!nle); + + nle = nl_socket_set_ext_ack(priv->nlh, TRUE); + if (nle) + _LOGD("could not enable extended acks on netlink socket"); + + /* explicitly set the msg buffer size and disable MSG_PEEK. + * If we later encounter NME_NL_MSG_TRUNC, we will adjust the buffer size. */ + nl_socket_disable_msg_peek(priv->nlh); + nle = nl_socket_set_msg_buf_size(priv->nlh, 32 * 1024); + g_assert(!nle); + + nle = nl_socket_add_memberships(priv->nlh, + RTNLGRP_IPV4_IFADDR, + RTNLGRP_IPV4_ROUTE, + RTNLGRP_IPV4_RULE, + RTNLGRP_IPV6_RULE, + RTNLGRP_IPV6_IFADDR, + RTNLGRP_IPV6_ROUTE, + RTNLGRP_LINK, + RTNLGRP_TC, + 0); + g_assert(!nle); + + fd = nl_socket_get_fd(priv->nlh); + + _LOGD("Netlink socket for events established: port=%u, fd=%d", + nl_socket_get_local_port(priv->nlh), + fd); + + priv->event_source = + nm_g_unix_fd_source_new(fd, + G_IO_IN | G_IO_NVAL | G_IO_PRI | G_IO_ERR | G_IO_HUP, + G_PRIORITY_DEFAULT, + event_handler, + platform, + NULL); + g_source_attach(priv->event_source, NULL); + + /* complete construction of the GObject instance before populating the cache. */ + G_OBJECT_CLASS(nm_linux_platform_parent_class)->constructed(_object); + + _LOGD("populate platform cache"); + delayed_action_schedule( + platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL + | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, + NULL); + + delayed_action_handle_all(platform, FALSE); + + /* Set up udev monitoring */ + if (priv->udev_client) { + struct udev_enumerate * enumerator; + struct udev_list_entry *devices, *l; + + /* And read initial device list */ + enumerator = nm_udev_client_enumerate_new(priv->udev_client); + + udev_enumerate_add_match_is_initialized(enumerator); + + udev_enumerate_scan_devices(enumerator); + + devices = udev_enumerate_get_list_entry(enumerator); + for (l = devices; l; l = udev_list_entry_get_next(l)) { + struct udev_device *udevice; + + udevice = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerator), + udev_list_entry_get_name(l)); + if (!udevice) + continue; + + udev_device_added(platform, udevice); + udev_device_unref(udevice); + } + + udev_enumerate_unref(enumerator); + } } NMPlatform * -nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support) +nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support) { - gboolean use_udev = FALSE; + gboolean use_udev = FALSE; - if ( nmp_netns_is_initial () - && access ("/sys", W_OK) == 0) - use_udev = TRUE; + if (nmp_netns_is_initial() && access("/sys", W_OK) == 0) + use_udev = TRUE; - return g_object_new (NM_TYPE_LINUX_PLATFORM, - NM_PLATFORM_LOG_WITH_PTR, log_with_ptr, - NM_PLATFORM_USE_UDEV, use_udev, - NM_PLATFORM_NETNS_SUPPORT, netns_support, - NULL); + return g_object_new(NM_TYPE_LINUX_PLATFORM, + NM_PLATFORM_LOG_WITH_PTR, + log_with_ptr, + NM_PLATFORM_USE_UDEV, + use_udev, + NM_PLATFORM_NETNS_SUPPORT, + netns_support, + NULL); } static void -dispose (GObject *object) +dispose(GObject *object) { - NMPlatform *platform = NM_PLATFORM (object); - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + NMPlatform * platform = NM_PLATFORM(object); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - _LOGD ("dispose"); + _LOGD("dispose"); - delayed_action_wait_for_nl_response_complete_all (platform, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING); + delayed_action_wait_for_nl_response_complete_all(platform, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING); - priv->delayed_action.flags = DELAYED_ACTION_TYPE_NONE; - g_ptr_array_set_size (priv->delayed_action.list_master_connected, 0); - g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); + priv->delayed_action.flags = DELAYED_ACTION_TYPE_NONE; + g_ptr_array_set_size(priv->delayed_action.list_master_connected, 0); + g_ptr_array_set_size(priv->delayed_action.list_refresh_link, 0); - G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object); + G_OBJECT_CLASS(nm_linux_platform_parent_class)->dispose(object); } static void -finalize (GObject *object) +finalize(GObject *object) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (object); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(object); - g_ptr_array_unref (priv->delayed_action.list_master_connected); - g_ptr_array_unref (priv->delayed_action.list_refresh_link); - g_array_unref (priv->delayed_action.list_wait_for_nl_response); + g_ptr_array_unref(priv->delayed_action.list_master_connected); + g_ptr_array_unref(priv->delayed_action.list_refresh_link); + g_array_unref(priv->delayed_action.list_wait_for_nl_response); - nl_socket_free (priv->genl); + nl_socket_free(priv->genl); - nm_clear_g_source_inst (&priv->event_source); + nm_clear_g_source_inst(&priv->event_source); - nl_socket_free (priv->nlh); + nl_socket_free(priv->nlh); - if (priv->sysctl_get_prev_values) { - sysctl_clear_cache_list = g_slist_remove (sysctl_clear_cache_list, object); - g_hash_table_destroy (priv->sysctl_get_prev_values); - } + if (priv->sysctl_get_prev_values) { + sysctl_clear_cache_list = g_slist_remove(sysctl_clear_cache_list, object); + g_hash_table_destroy(priv->sysctl_get_prev_values); + } - priv->udev_client = nm_udev_client_unref (priv->udev_client); + priv->udev_client = nm_udev_client_unref(priv->udev_client); - G_OBJECT_CLASS (nm_linux_platform_parent_class)->finalize (object); + G_OBJECT_CLASS(nm_linux_platform_parent_class)->finalize(object); } static void -nm_linux_platform_class_init (NMLinuxPlatformClass *klass) +nm_linux_platform_class_init(NMLinuxPlatformClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - NMPlatformClass *platform_class = NM_PLATFORM_CLASS (klass); + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMPlatformClass *platform_class = NM_PLATFORM_CLASS(klass); - object_class->constructed = constructed; - object_class->dispose = dispose; - object_class->finalize = finalize; + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; - platform_class->sysctl_set = sysctl_set; - platform_class->sysctl_set_async = sysctl_set_async; - platform_class->sysctl_get = sysctl_get; + platform_class->sysctl_set = sysctl_set; + platform_class->sysctl_set_async = sysctl_set_async; + platform_class->sysctl_get = sysctl_get; - platform_class->link_add = link_add; - platform_class->link_delete = link_delete; + platform_class->link_add = link_add; + platform_class->link_delete = link_delete; - platform_class->link_refresh = link_refresh; + platform_class->link_refresh = link_refresh; - platform_class->link_set_netns = link_set_netns; + platform_class->link_set_netns = link_set_netns; - platform_class->link_set_up = link_set_up; - platform_class->link_set_down = link_set_down; - platform_class->link_set_arp = link_set_arp; - platform_class->link_set_noarp = link_set_noarp; + platform_class->link_set_up = link_set_up; + platform_class->link_set_down = link_set_down; + platform_class->link_set_arp = link_set_arp; + platform_class->link_set_noarp = link_set_noarp; - platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; - platform_class->link_set_token = link_set_token; + platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; + platform_class->link_set_token = link_set_token; - platform_class->link_set_address = link_set_address; - platform_class->link_get_permanent_address = link_get_permanent_address; - platform_class->link_set_mtu = link_set_mtu; - platform_class->link_set_name = link_set_name; - platform_class->link_set_sriov_params_async = link_set_sriov_params_async; - platform_class->link_set_sriov_vfs = link_set_sriov_vfs; - platform_class->link_set_bridge_vlans = link_set_bridge_vlans; + platform_class->link_set_address = link_set_address; + platform_class->link_get_permanent_address = link_get_permanent_address; + platform_class->link_set_mtu = link_set_mtu; + platform_class->link_set_name = link_set_name; + platform_class->link_set_sriov_params_async = link_set_sriov_params_async; + platform_class->link_set_sriov_vfs = link_set_sriov_vfs; + platform_class->link_set_bridge_vlans = link_set_bridge_vlans; - platform_class->link_get_physical_port_id = link_get_physical_port_id; - platform_class->link_get_dev_id = link_get_dev_id; - platform_class->link_get_wake_on_lan = link_get_wake_on_lan; - platform_class->link_get_driver_info = link_get_driver_info; + platform_class->link_get_physical_port_id = link_get_physical_port_id; + platform_class->link_get_dev_id = link_get_dev_id; + platform_class->link_get_wake_on_lan = link_get_wake_on_lan; + platform_class->link_get_driver_info = link_get_driver_info; - platform_class->link_supports_carrier_detect = link_supports_carrier_detect; - platform_class->link_supports_vlans = link_supports_vlans; - platform_class->link_supports_sriov = link_supports_sriov; + platform_class->link_supports_carrier_detect = link_supports_carrier_detect; + platform_class->link_supports_vlans = link_supports_vlans; + platform_class->link_supports_sriov = link_supports_sriov; - platform_class->link_enslave = link_enslave; - platform_class->link_release = link_release; + platform_class->link_enslave = link_enslave; + platform_class->link_release = link_release; - platform_class->link_can_assume = link_can_assume; + platform_class->link_can_assume = link_can_assume; - platform_class->link_vlan_change = link_vlan_change; - platform_class->link_wireguard_change = link_wireguard_change; + platform_class->link_vlan_change = link_vlan_change; + platform_class->link_wireguard_change = link_wireguard_change; - platform_class->infiniband_partition_add = infiniband_partition_add; - platform_class->infiniband_partition_delete = infiniband_partition_delete; + platform_class->infiniband_partition_add = infiniband_partition_add; + platform_class->infiniband_partition_delete = infiniband_partition_delete; - platform_class->wifi_get_capabilities = wifi_get_capabilities; - platform_class->wifi_get_bssid = wifi_get_bssid; - platform_class->wifi_get_frequency = wifi_get_frequency; - platform_class->wifi_get_quality = wifi_get_quality; - platform_class->wifi_get_rate = wifi_get_rate; - platform_class->wifi_get_mode = wifi_get_mode; - platform_class->wifi_set_mode = wifi_set_mode; - platform_class->wifi_set_powersave = wifi_set_powersave; - platform_class->wifi_find_frequency = wifi_find_frequency; - platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running; - platform_class->wifi_get_wake_on_wlan = wifi_get_wake_on_wlan; - platform_class->wifi_set_wake_on_wlan = wifi_set_wake_on_wlan; + platform_class->wifi_get_capabilities = wifi_get_capabilities; + platform_class->wifi_get_bssid = wifi_get_bssid; + platform_class->wifi_get_frequency = wifi_get_frequency; + platform_class->wifi_get_quality = wifi_get_quality; + platform_class->wifi_get_rate = wifi_get_rate; + platform_class->wifi_get_mode = wifi_get_mode; + platform_class->wifi_set_mode = wifi_set_mode; + platform_class->wifi_set_powersave = wifi_set_powersave; + platform_class->wifi_find_frequency = wifi_find_frequency; + platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running; + platform_class->wifi_get_wake_on_wlan = wifi_get_wake_on_wlan; + platform_class->wifi_set_wake_on_wlan = wifi_set_wake_on_wlan; - platform_class->mesh_get_channel = mesh_get_channel; - platform_class->mesh_set_channel = mesh_set_channel; - platform_class->mesh_set_ssid = mesh_set_ssid; + platform_class->mesh_get_channel = mesh_get_channel; + platform_class->mesh_set_channel = mesh_set_channel; + platform_class->mesh_set_ssid = mesh_set_ssid; - platform_class->wpan_get_pan_id = wpan_get_pan_id; - platform_class->wpan_set_pan_id = wpan_set_pan_id; - platform_class->wpan_get_short_addr = wpan_get_short_addr; - platform_class->wpan_set_short_addr = wpan_set_short_addr; - platform_class->wpan_set_channel = wpan_set_channel; + platform_class->wpan_get_pan_id = wpan_get_pan_id; + platform_class->wpan_set_pan_id = wpan_set_pan_id; + platform_class->wpan_get_short_addr = wpan_get_short_addr; + platform_class->wpan_set_short_addr = wpan_set_short_addr; + platform_class->wpan_set_channel = wpan_set_channel; - platform_class->link_tun_add = link_tun_add; + platform_class->link_tun_add = link_tun_add; - platform_class->object_delete = object_delete; - platform_class->ip4_address_add = ip4_address_add; - platform_class->ip6_address_add = ip6_address_add; - platform_class->ip4_address_delete = ip4_address_delete; - platform_class->ip6_address_delete = ip6_address_delete; + platform_class->object_delete = object_delete; + platform_class->ip4_address_add = ip4_address_add; + platform_class->ip6_address_add = ip6_address_add; + platform_class->ip4_address_delete = ip4_address_delete; + platform_class->ip6_address_delete = ip6_address_delete; - platform_class->ip_route_add = ip_route_add; - platform_class->ip_route_get = ip_route_get; + platform_class->ip_route_add = ip_route_add; + platform_class->ip_route_get = ip_route_get; - platform_class->routing_rule_add = routing_rule_add; + platform_class->routing_rule_add = routing_rule_add; - platform_class->qdisc_add = qdisc_add; - platform_class->tfilter_add = tfilter_add; + platform_class->qdisc_add = qdisc_add; + platform_class->tfilter_add = tfilter_add; - platform_class->process_events = process_events; + platform_class->process_events = process_events; } - |