diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2017-10-30 21:48:22 +0100 |
---|---|---|
committer | Lubomir Rintel <lkundrak@v3.sk> | 2017-10-30 21:48:22 +0100 |
commit | 79168a54344f8fb72fda88bf02bce908d2ce3f31 (patch) | |
tree | 9b572f96500061687babbac56ad85220c2aa1ef7 | |
parent | 7f84f15e069bbaa02426fe8a59bf29a44611adb8 (diff) | |
parent | 6b532fed5046d0d658164e5ab9813ce8871cfbeb (diff) |
merge: branch 'lr/ovs'
https://bugzilla.redhat.com/show_bug.cgi?id=1470282
64 files changed, 6236 insertions, 415 deletions
diff --git a/Makefile.am b/Makefile.am index 83fe9de83d..46799cb1f1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -206,6 +206,12 @@ introspection_sources = \ introspection/org.freedesktop.NetworkManager.Device.Modem.h \ introspection/org.freedesktop.NetworkManager.Device.OlpcMesh.c \ introspection/org.freedesktop.NetworkManager.Device.OlpcMesh.h \ + introspection/org.freedesktop.NetworkManager.Device.OvsInterface.c \ + introspection/org.freedesktop.NetworkManager.Device.OvsInterface.h \ + introspection/org.freedesktop.NetworkManager.Device.OvsPort.c \ + introspection/org.freedesktop.NetworkManager.Device.OvsPort.h \ + introspection/org.freedesktop.NetworkManager.Device.OvsBridge.c \ + introspection/org.freedesktop.NetworkManager.Device.OvsBridge.h \ introspection/org.freedesktop.NetworkManager.Device.Ppp.c \ introspection/org.freedesktop.NetworkManager.Device.Ppp.h \ introspection/org.freedesktop.NetworkManager.Device.Statistics.c \ @@ -281,6 +287,9 @@ DBUS_INTERFACE_DOCS = \ docs/api/dbus-org.freedesktop.NetworkManager.Device.Tun.xml \ docs/api/dbus-org.freedesktop.NetworkManager.Device.Bridge.xml \ docs/api/dbus-org.freedesktop.NetworkManager.Device.OlpcMesh.xml \ + docs/api/dbus-org.freedesktop.NetworkManager.Device.OvsInterface.xml \ + docs/api/dbus-org.freedesktop.NetworkManager.Device.OvsPort.xml \ + docs/api/dbus-org.freedesktop.NetworkManager.Device.OvsBridge.xml \ docs/api/dbus-org.freedesktop.NetworkManager.Device.Ppp.xml \ docs/api/dbus-org.freedesktop.NetworkManager.DHCP4Config.xml \ docs/api/dbus-org.freedesktop.NetworkManager.Device.Generic.xml \ @@ -329,6 +338,9 @@ dbusinterfaces_DATA = \ introspection/org.freedesktop.NetworkManager.Device.Macvlan.xml \ introspection/org.freedesktop.NetworkManager.Device.Modem.xml \ introspection/org.freedesktop.NetworkManager.Device.OlpcMesh.xml \ + introspection/org.freedesktop.NetworkManager.Device.OvsInterface.xml \ + introspection/org.freedesktop.NetworkManager.Device.OvsPort.xml \ + introspection/org.freedesktop.NetworkManager.Device.OvsBridge.xml \ introspection/org.freedesktop.NetworkManager.Device.Ppp.xml \ introspection/org.freedesktop.NetworkManager.Device.Statistics.xml \ introspection/org.freedesktop.NetworkManager.Device.Team.xml \ @@ -393,6 +405,10 @@ libnm_core_lib_h_pub_real = \ libnm-core/nm-setting-macsec.h \ libnm-core/nm-setting-macvlan.h \ libnm-core/nm-setting-olpc-mesh.h \ + libnm-core/nm-setting-ovs-bridge.h \ + libnm-core/nm-setting-ovs-interface.h \ + libnm-core/nm-setting-ovs-patch.h \ + libnm-core/nm-setting-ovs-port.h \ libnm-core/nm-setting-ppp.h \ libnm-core/nm-setting-pppoe.h \ libnm-core/nm-setting-proxy.h \ @@ -474,6 +490,10 @@ libnm_core_lib_c_real = \ libnm-core/nm-setting-macsec.c \ libnm-core/nm-setting-macvlan.c \ libnm-core/nm-setting-olpc-mesh.c \ + libnm-core/nm-setting-ovs-bridge.c \ + libnm-core/nm-setting-ovs-interface.c \ + libnm-core/nm-setting-ovs-patch.c \ + libnm-core/nm-setting-ovs-port.c \ libnm-core/nm-setting-ppp.c \ libnm-core/nm-setting-pppoe.c \ libnm-core/nm-setting-proxy.c \ @@ -731,6 +751,9 @@ libnm_lib_h_pub_real = \ libnm/nm-device-macvlan.h \ libnm/nm-device-modem.h \ libnm/nm-device-olpc-mesh.h \ + libnm/nm-device-ovs-interface.h \ + libnm/nm-device-ovs-port.h \ + libnm/nm-device-ovs-bridge.h \ libnm/nm-device-ppp.h \ libnm/nm-device-team.h \ libnm/nm-device-tun.h \ @@ -784,6 +807,9 @@ libnm_lib_c_real = \ libnm/nm-device-macvlan.c \ libnm/nm-device-modem.c \ libnm/nm-device-olpc-mesh.c \ + libnm/nm-device-ovs-interface.c \ + libnm/nm-device-ovs-port.c \ + libnm/nm-device-ovs-bridge.c \ libnm/nm-device-ppp.c \ libnm/nm-device-team.c \ libnm/nm-device-tun.c \ @@ -2743,6 +2769,63 @@ check_local += check-local-devices-team endif ############################################################################### +# src/devices/ovs +############################################################################### + +if WITH_OPENVSWITCH + +if HAVE_SYSTEMD + +systemdnmunitdir = $(systemdsystemunitdir)/NetworkManager.service.d +systemdnmunit_DATA = \ + data/NetworkManager-ovs.conf + +endif + +core_plugins += src/devices/ovs/libnm-device-plugin-ovs.la + +src_devices_ovs_libnm_device_plugin_ovs_la_SOURCES = \ + src/devices/ovs/nm-ovsdb.c \ + src/devices/ovs/nm-ovsdb.h \ + src/devices/ovs/nm-ovs-factory.c \ + src/devices/ovs/nm-device-ovs-interface.c \ + src/devices/ovs/nm-device-ovs-interface.h \ + src/devices/ovs/nm-device-ovs-port.c \ + src/devices/ovs/nm-device-ovs-port.h \ + src/devices/ovs/nm-device-ovs-bridge.c \ + src/devices/ovs/nm-device-ovs-bridge.h + +src_devices_ovs_libnm_device_plugin_ovs_la_CPPFLAGS = \ + -I$(srcdir)/src \ + -I$(builddir)/src \ + -I$(srcdir)/shared \ + -I$(builddir)/shared \ + -I$(builddir)/libnm-core \ + -I$(srcdir)/libnm-core \ + \ + -DG_LOG_DOMAIN=\""NetworkManager"\" \ + -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \ + -DRUNSTATEDIR=\"$(runstatedir)\" \ + \ + $(JANSSON_CFLAGS) \ + $(GLIB_CFLAGS) + +src_devices_ovs_libnm_device_plugin_ovs_la_LDFLAGS = \ + -module -avoid-version \ + -Wl,--version-script="$(srcdir)/linker-script-devices.ver" + +src_devices_ovs_libnm_device_plugin_ovs_la_LIBADD = \ + introspection/libnmdbus.la \ + $(JANSSON_LIBS) \ + $(GLIB_LIBS) + +check-local-devices-ovs: src/devices/ovs/libnm-device-plugin-ovs.la + $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/ovs/.libs/libnm-device-plugin-ovs.so "$(srcdir)/linker-script-devices.ver" + $(call check_so_symbols,$(builddir)/src/devices/ovs/.libs/libnm-device-plugin-ovs.so) + +endif + +############################################################################### # src/dnsmasq/tests ############################################################################### @@ -4292,6 +4375,13 @@ man_pages_autogen += \ man/nm-settings-keyfile.5 \ man/nm-settings.5 +if WITH_OPENVSWITCH +man_pages += man/nm-openvswitch.7 +else +EXTRA_DIST += man/nm-openvswitch.7 +dist_dependencies += man/nm-openvswitch.7 +endif + if CONFIG_PLUGIN_IFCFG_RH man_pages_autogen += man/nm-settings-ifcfg-rh.5 else diff --git a/clients/cli/connections.c b/clients/cli/connections.c index a13af882e2..73c1de2644 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -136,6 +136,10 @@ const NmcMetaGenericInfo *const nmc_fields_con_active_details_general[] = { NM_SETTING_BRIDGE_PORT_SETTING_NAME","\ NM_SETTING_TEAM_SETTING_NAME","\ NM_SETTING_TEAM_PORT_SETTING_NAME"," \ + NM_SETTING_OVS_BRIDGE_SETTING_NAME","\ + NM_SETTING_OVS_INTERFACE_SETTING_NAME","\ + NM_SETTING_OVS_PATCH_SETTING_NAME","\ + NM_SETTING_OVS_PORT_SETTING_NAME","\ NM_SETTING_DCB_SETTING_NAME"," \ NM_SETTING_TUN_SETTING_NAME"," \ NM_SETTING_IP_TUNNEL_SETTING_NAME"," \ diff --git a/clients/common/nm-client-utils.c b/clients/common/nm-client-utils.c index 9423202ca7..bf4dcc77f3 100644 --- a/clients/common/nm-client-utils.c +++ b/clients/common/nm-client-utils.c @@ -243,9 +243,11 @@ nmc_device_state_to_string (NMDeviceState state) return _("deactivating"); case NM_DEVICE_STATE_FAILED: return _("connection failed"); - default: + case NM_DEVICE_STATE_UNKNOWN: return _("unknown"); } + + return _("unknown"); } const char * @@ -260,9 +262,11 @@ nmc_device_metered_to_string (NMMetered value) return _("yes (guessed)"); case NM_METERED_GUESS_NO: return _("no (guessed)"); - default: + case NM_METERED_UNKNOWN: return _("unknown"); } + + return _("unknown"); } const char * @@ -395,10 +399,13 @@ nmc_device_reason_to_string (NMDeviceStateReason reason) return _("The device's parent changed"); case NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED: return _("The device parent's management changed"); - default: - /* TRANSLATORS: Unknown reason for a device state change (NMDeviceStateReason) */ - return _("Unknown"); + + case NM_DEVICE_STATE_REASON_OVSDB_FAILED: + return _("OpenVSwitch database connection failed"); } + + /* TRANSLATORS: Unknown reason for a device state change (NMDeviceStateReason) */ + return _("Unknown"); } const char * diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 4e90c42643..0231fe06ce 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -5009,6 +5009,8 @@ static const NMMetaPropertyInfo *const property_infos_CONNECTION[] = { .property_typ_data = DEFINE_PROPERTY_TYP_DATA ( .values_static = VALUES_STATIC (NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BRIDGE_SETTING_NAME, + NM_SETTING_OVS_BRIDGE_SETTING_NAME, + NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_TEAM_SETTING_NAME), ), ), @@ -5804,6 +5806,81 @@ static const NMMetaPropertyInfo *const property_infos_PPPOE[] = { }; #undef _CURRENT_NM_META_SETTING_TYPE +#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_BRIDGE +static const NMMetaPropertyInfo *const property_infos_OVS_BRIDGE[] = { + PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_FAIL_MODE, + .property_type = &_pt_gobject_string, + .property_typ_data = DEFINE_PROPERTY_TYP_DATA ( + .values_static = VALUES_STATIC ("secure", "standalone"), + ), + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE, + .property_type = &_pt_gobject_bool, + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_RSTP_ENABLE, + .property_type = &_pt_gobject_bool, + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_BRIDGE_STP_ENABLE, + .property_type = &_pt_gobject_bool, + ), + NULL +}; + +#undef _CURRENT_NM_META_SETTING_TYPE +#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_INTERFACE +static const NMMetaPropertyInfo *const property_infos_OVS_INTERFACE[] = { + PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_INTERFACE_TYPE, + .property_type = &_pt_gobject_string, + .property_typ_data = DEFINE_PROPERTY_TYP_DATA ( + .values_static = VALUES_STATIC ("internal", "patch"), + ), + ), + NULL +}; + +#undef _CURRENT_NM_META_SETTING_TYPE +#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_PATCH +static const NMMetaPropertyInfo *const property_infos_OVS_PATCH[] = { + PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PATCH_PEER, + .property_type = &_pt_gobject_string, + ), + NULL +}; + +#undef _CURRENT_NM_META_SETTING_TYPE +#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OVS_PORT +static const NMMetaPropertyInfo *const property_infos_OVS_PORT[] = { + PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_VLAN_MODE, + .property_type = &_pt_gobject_string, + .property_typ_data = DEFINE_PROPERTY_TYP_DATA ( + .values_static = VALUES_STATIC ("access", "native-tagged", "native-untagged", "trunk"), + ), + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_TAG, + .property_type = &_pt_gobject_int, + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_LACP, + .property_type = &_pt_gobject_string, + .property_typ_data = DEFINE_PROPERTY_TYP_DATA ( + .values_static = VALUES_STATIC ("active", "off", "passive"), + ), + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_BOND_MODE, + .property_type = &_pt_gobject_string, + .property_typ_data = DEFINE_PROPERTY_TYP_DATA ( + .values_static = VALUES_STATIC ("active-backup", "balance-slb", "balance-tcp"), + ), + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_BOND_UPDELAY, + .property_type = &_pt_gobject_int, + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_OVS_PORT_BOND_DOWNDELAY, + .property_type = &_pt_gobject_int, + ), + NULL +}; + +#undef _CURRENT_NM_META_SETTING_TYPE #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_PPP static const NMMetaPropertyInfo *const property_infos_PPP[] = { PROPERTY_INFO_WITH_DESC (NM_SETTING_PPP_NOAUTH, @@ -6722,6 +6799,10 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN) #define SETTING_PRETTY_NAME_MACSEC N_("MACsec connection") #define SETTING_PRETTY_NAME_MACVLAN N_("macvlan connection") #define SETTING_PRETTY_NAME_OLPC_MESH N_("OLPC Mesh connection") +#define SETTING_PRETTY_NAME_OVS_BRIDGE N_("OpenVSwitch bridge settings") +#define SETTING_PRETTY_NAME_OVS_INTERFACE N_("OpenVSwitch interface settings") +#define SETTING_PRETTY_NAME_OVS_PATCH N_("OpenVSwitch patch interface settings") +#define SETTING_PRETTY_NAME_OVS_PORT N_("OpenVSwitch port settings") #define SETTING_PRETTY_NAME_PPP N_("PPP settings") #define SETTING_PRETTY_NAME_PPPOE N_("PPPoE") #define SETTING_PRETTY_NAME_PROXY N_("Proxy") @@ -6871,6 +6952,29 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { ), .setting_init_fcn = _setting_init_fcn_olpc_mesh, ), + SETTING_INFO (OVS_BRIDGE, + .valid_parts = NM_META_SETTING_VALID_PARTS ( + NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), + NM_META_SETTING_VALID_PART_ITEM (OVS_BRIDGE, TRUE), + ), + ), + SETTING_INFO (OVS_INTERFACE, + .valid_parts = NM_META_SETTING_VALID_PARTS ( + NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), + NM_META_SETTING_VALID_PART_ITEM (OVS_INTERFACE, TRUE), + NM_META_SETTING_VALID_PART_ITEM (OVS_PATCH, FALSE), + NM_META_SETTING_VALID_PART_ITEM (IP4_CONFIG, FALSE), + NM_META_SETTING_VALID_PART_ITEM (IP6_CONFIG, FALSE), + NM_META_SETTING_VALID_PART_ITEM (WIRED, FALSE), + ), + ), + SETTING_INFO (OVS_PATCH), + SETTING_INFO (OVS_PORT, + .valid_parts = NM_META_SETTING_VALID_PARTS ( + NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), + NM_META_SETTING_VALID_PART_ITEM (OVS_PORT, TRUE), + ), + ), SETTING_INFO (PPPOE, /* PPPoE is a base connection type from historical reasons. * See libnm-core/nm-setting.c:_nm_setting_is_base_type() @@ -6977,6 +7081,16 @@ static const NMMetaSettingValidPartItem *const valid_settings_slave_bridge[] = { NULL, }; +static const NMMetaSettingValidPartItem *const valid_settings_slave_ovs_bridge[] = { + NM_META_SETTING_VALID_PART_ITEM (OVS_PORT, FALSE), + NULL, +}; + +static const NMMetaSettingValidPartItem *const valid_settings_slave_ovs_port[] = { + NM_META_SETTING_VALID_PART_ITEM (OVS_INTERFACE, FALSE), + NULL, +}; + static const NMMetaSettingValidPartItem *const valid_settings_slave_team[] = { NM_META_SETTING_VALID_PART_ITEM (TEAM_PORT, TRUE), NULL, @@ -6997,6 +7111,14 @@ nm_meta_setting_info_valid_parts_for_slave_type (const char *slave_type, const c NM_SET_OUT (out_slave_name, "bridge-slave"); return valid_settings_slave_bridge; } + if (nm_streq (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) { + NM_SET_OUT (out_slave_name, "ovs-slave"); + return valid_settings_slave_ovs_bridge; + } + if (nm_streq (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME)) { + NM_SET_OUT (out_slave_name, "ovs-slave"); + return valid_settings_slave_ovs_port; + } if (nm_streq (slave_type, NM_SETTING_TEAM_SETTING_NAME)) { NM_SET_OUT (out_slave_name, "team-slave"); return valid_settings_slave_team; diff --git a/clients/common/settings-docs.c.in b/clients/common/settings-docs.c.in index 4d1e94e817..1378f2dced 100644 --- a/clients/common/settings-docs.c.in +++ b/clients/common/settings-docs.c.in @@ -197,7 +197,7 @@ #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_INPUT_KEY N_("The key used for tunnel input packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used.") #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_LOCAL N_("The local endpoint of the tunnel; the value can be empty, otherwise it must contain an IPv4 or IPv6 address.") #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_MODE N_("The tunneling mode, for example NM_IP_TUNNEL_MODE_IPIP (1) or NM_IP_TUNNEL_MODE_GRE (2).") -#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_MTU N_("None") +#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments.") #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".") #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_OUTPUT_KEY N_("The key used for tunnel output packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used.") #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_PARENT N_("If given, specifies the parent interface name or parent connection UUID the new device will be bound to so that tunneled packets will only be routed via that interface.") @@ -262,6 +262,22 @@ #define DESCRIBE_DOC_NM_SETTING_MACVLAN_PARENT N_("If given, specifies the parent interface name or parent connection UUID from which this MAC-VLAN interface should be created. If this property is not specified, the connection must contain an \"802-3-ethernet\" setting with a \"mac-address\" property.") #define DESCRIBE_DOC_NM_SETTING_MACVLAN_PROMISCUOUS N_("Whether the interface should be put in promiscuous mode.") #define DESCRIBE_DOC_NM_SETTING_MACVLAN_TAP N_("Whether the interface should be a MACVTAP.") +#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_FAIL_MODE N_("The bridge failure mode. One of \"secure\", \"standalone\" or empty.") +#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE N_("Enable or disable multicast snooping.") +#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".") +#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_RSTP_ENABLE N_("Enable or disable RSTP.") +#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_STP_ENABLE N_("Enable or disable STP.") +#define DESCRIBE_DOC_NM_SETTING_OVS_INTERFACE_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".") +#define DESCRIBE_DOC_NM_SETTING_OVS_INTERFACE_TYPE N_("The interface type. Either \"internal\", or empty.") +#define DESCRIBE_DOC_NM_SETTING_OVS_PATCH_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".") +#define DESCRIBE_DOC_NM_SETTING_OVS_PATCH_PEER N_("Specifies the unicast destination IP address of a remote OpenVSwitch bridge port to connect to.") +#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_DOWNDELAY N_("The time port must be inactive in order to be considered down.") +#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_MODE N_("Bonding mode. One of \"active-backup\", \"balance-slb\", or \"balance-tcp\".") +#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_UPDELAY N_("The time port must be active befor it starts forwarding traffic.") +#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_LACP N_("LACP mode. One of \"active\", \"off\", or \"passive\".") +#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".") +#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_TAG N_("The VLAN tag in the range 0-4095.") +#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_VLAN_MODE N_("The VLAN mode. One of \"access\", \"native-tagged\", \"native-untagged\", \"trunk\" or unset.") #define DESCRIBE_DOC_NM_SETTING_PPP_BAUD N_("If non-zero, instruct pppd to set the serial port to the specified baudrate. This value should normally be left as 0 to automatically choose the speed.") #define DESCRIBE_DOC_NM_SETTING_PPP_CRTSCTS N_("If TRUE, specify that pppd should set the serial port to use hardware flow control with RTS and CTS signals. This value should normally be set to FALSE.") #define DESCRIBE_DOC_NM_SETTING_PPP_LCP_ECHO_FAILURE N_("If non-zero, instruct pppd to presume the connection to the peer has failed if the specified number of LCP echo-requests go unanswered by the peer. The \"lcp-echo-interval\" property must also be set to a non-zero value if this property is used.") diff --git a/configure.ac b/configure.ac index a4353760a4..146f7ccd15 100644 --- a/configure.ac +++ b/configure.ac @@ -871,6 +871,20 @@ if test "$with_dhcpcanon" != "no"; then else AC_DEFINE(WITH_DHCPCANON, 0, [Define if you have dhcpcanon]) fi + +# OpenVSwitch integration +AC_ARG_ENABLE(ovs, AS_HELP_STRING([--enable-ovs], [enable OpenVSwitch support])) +if test "${enable_ovs}" != "no"; then + enable_ovs='yes' + if test "$have_jansson" = "no"; then + AC_MSG_ERROR(Jansson is required for ovs support) + fi + AC_DEFINE(WITH_OPENVSWITCH, 1, [Define if you have OpenVSwitch support]) +else + AC_DEFINE(WITH_OPENVSWITCH, 0, [Define if you have OpenVSwitch support]) +fi +AM_CONDITIONAL(WITH_OPENVSWITCH, test "${enable_ovs}" = "yes") + # DHCP client support AC_ARG_WITH([dhclient], AS_HELP_STRING([--with-dhclient=yes|no|path], [Enable dhclient 4.x support])) @@ -1282,6 +1296,8 @@ if test "$build_docs" != "yes" -a \ -f "$srcdir"/man/nmcli.1 -a \ -f "$srcdir"/man/nmtui.1 -a \ \ + -f "$srcdir"/man/nm-openvswitch.7 -a \ + \ -f "$srcdir"/man/nm-settings-ifcfg-rh.5 -a \ -f "$srcdir"/man/nm-settings-keyfile.5 -a \ -f "$srcdir"/man/nm-settings.5 -a \ @@ -1359,6 +1375,7 @@ echo " modemmanager-1: $with_modem_manager_1" echo " ofono: $with_ofono" echo " concheck: $enable_concheck" echo " libteamdctl: $enable_teamdctl" +echo " ovs: $enable_ovs" echo " libnm-glib: $with_libnm_glib" echo " nmcli: $build_nmcli" echo " nmtui: $build_nmtui" diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec index 64393526c4..4778f15042 100644 --- a/contrib/fedora/rpm/NetworkManager.spec +++ b/contrib/fedora/rpm/NetworkManager.spec @@ -50,6 +50,7 @@ %bcond_without wwan %bcond_without team %bcond_without wifi +%bcond_without ovs %bcond_without ppp %bcond_without nmtui %bcond_without regen_docs @@ -240,6 +241,19 @@ This package contains NetworkManager support for mobile broadband (WWAN) devices. %endif + +%if %{with ovs} +%package ovs +Summary: OpenVSwitch device plugin for NetworkManager +Group: System Environment/Base +Requires: %{name}%{?_isa} = %{epoch}:%{version}-%{release} +Requires: openvswitch + +%description ovs +This package contains NetworkManager support for OpenVSwitch bridges. +%endif + + %if %{with ppp} %package ppp Summary: PPP plugin for NetworkManager @@ -563,7 +577,7 @@ fi %dir %{nmlibdir}/VPN %{_mandir}/man1/* %{_mandir}/man5/* -%{_mandir}/man7/* +%{_mandir}/man7/nmcli-examples.7* %{_mandir}/man8/* %dir %{_localstatedir}/lib/NetworkManager %dir %{_sysconfdir}/NetworkManager/system-connections @@ -608,6 +622,13 @@ fi %{_libdir}/%{name}/libnm-wwan.so %endif +%if %{with ovs} +%files ovs +%{_libdir}/%{name}/libnm-device-plugin-ovs.so +%{systemd_dir}/NetworkManager.service.d/NetworkManager-ovs.conf +%{_mandir}/man7/nm-openvswitch.7* +%endif + %if %{with ppp} %files ppp %{_libdir}/pppd/%{ppp_version}/nm-pppd-plugin.so diff --git a/data/NetworkManager-ovs.conf b/data/NetworkManager-ovs.conf new file mode 100644 index 0000000000..dde5ba5897 --- /dev/null +++ b/data/NetworkManager-ovs.conf @@ -0,0 +1,2 @@ +[Unit] +After=openvswitch.service diff --git a/docs/api/Makefile.am b/docs/api/Makefile.am index f5def7b8b8..67f0a22691 100644 --- a/docs/api/Makefile.am +++ b/docs/api/Makefile.am @@ -88,6 +88,7 @@ content_files = \ $(top_builddir)/man/nmcli-examples.xml \ $(top_builddir)/man/nm-settings.xml \ $(top_builddir)/man/nm-settings-keyfile.xml \ + $(top_builddir)/man/nm-openvswitch.xml \ version.xml \ ../../COPYING \ $(NULL) diff --git a/docs/api/network-manager-docs.xml b/docs/api/network-manager-docs.xml index dd5f0e47f5..0002f8ee11 100644 --- a/docs/api/network-manager-docs.xml +++ b/docs/api/network-manager-docs.xml @@ -76,6 +76,7 @@ <xi:include href="../../man/nm-settings-keyfile.xml"><xi:fallback /></xi:include> <xi:include href="../../man/nm-settings-ifcfg-rh.xml"><xi:fallback /></xi:include> <xi:include href="../../man/nm-online.xml"/> + <xi:include href="../../man/nm-openvswitch.xml"/> </part> <part id="ref-settings"> diff --git a/introspection/org.freedesktop.NetworkManager.Device.OvsBridge.xml b/introspection/org.freedesktop.NetworkManager.Device.OvsBridge.xml new file mode 100644 index 0000000000..08ed5dc01b --- /dev/null +++ b/introspection/org.freedesktop.NetworkManager.Device.OvsBridge.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<node name="/"> + <!-- + org.freedesktop.NetworkManager.Device.OvsBridge: + @short_description: OvsBridge Device + + --> + <interface name="org.freedesktop.NetworkManager.Device.OvsBridge"> + + <!-- + PropertiesChanged: + @properties: A dictionary mapping property names to variant boxed values + + DEPRECATED. Use the standard "PropertiesChanged" signal from "org.freedesktop.DBus.Properties" instead which exists since version NetworkManager 1.2.0. + --> + <signal name="PropertiesChanged"> + <arg name="properties" type="a{sv}"/> + </signal> + </interface> +</node> diff --git a/introspection/org.freedesktop.NetworkManager.Device.OvsInterface.xml b/introspection/org.freedesktop.NetworkManager.Device.OvsInterface.xml new file mode 100644 index 0000000000..9605a958b4 --- /dev/null +++ b/introspection/org.freedesktop.NetworkManager.Device.OvsInterface.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<node name="/"> + <!-- + org.freedesktop.NetworkManager.Device.OvsInterface: + @short_description: OvsInterface Device + + --> + <interface name="org.freedesktop.NetworkManager.Device.OvsInterface"> + + <!-- + PropertiesChanged: + @properties: A dictionary mapping property names to variant boxed values + + DEPRECATED. Use the standard "PropertiesChanged" signal from "org.freedesktop.DBus.Properties" instead which exists since version NetworkManager 1.2.0. + --> + <signal name="PropertiesChanged"> + <arg name="properties" type="a{sv}"/> + </signal> + </interface> +</node> diff --git a/introspection/org.freedesktop.NetworkManager.Device.OvsPort.xml b/introspection/org.freedesktop.NetworkManager.Device.OvsPort.xml new file mode 100644 index 0000000000..d4900f510c --- /dev/null +++ b/introspection/org.freedesktop.NetworkManager.Device.OvsPort.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<node name="/"> + <!-- + org.freedesktop.NetworkManager.Device.OvsPort: + @short_description: OvsPort Device + + --> + <interface name="org.freedesktop.NetworkManager.Device.OvsPort"> + + <!-- + PropertiesChanged: + @properties: A dictionary mapping property names to variant boxed values + + DEPRECATED. Use the standard "PropertiesChanged" signal from "org.freedesktop.DBus.Properties" instead which exists since version NetworkManager 1.2.0. + --> + <signal name="PropertiesChanged"> + <arg name="properties" type="a{sv}"/> + </signal> + </interface> +</node> diff --git a/libnm-core/nm-connection-private.h b/libnm-core/nm-connection-private.h index 7dd088ace0..ee2d7264b9 100644 --- a/libnm-core/nm-connection-private.h +++ b/libnm-core/nm-connection-private.h @@ -34,4 +34,10 @@ const char *_nm_connection_detect_bluetooth_type (NMConnection *self); gboolean _nm_connection_verify_required_interface_name (NMConnection *connection, GError **error); +int _nm_setting_ovs_interface_verify_interface_type (NMSettingOvsInterface *self, + NMConnection *connection, + gboolean normalize, + gboolean *out_modified, + GError **error); + #endif /* __NM_CONNECTION_PRIVATE_H__ */ diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c index 0296c6f9b2..d5e28f7b3e 100644 --- a/libnm-core/nm-connection.c +++ b/libnm-core/nm-connection.c @@ -729,6 +729,10 @@ _nm_connection_detect_slave_type (NMConnection *connection, NMSetting **out_s_po i_slave_type = NM_SETTING_BRIDGE_SETTING_NAME; else if (!strcmp (name, NM_SETTING_TEAM_PORT_SETTING_NAME)) i_slave_type = NM_SETTING_TEAM_SETTING_NAME; + else if (!strcmp (name, NM_SETTING_OVS_PORT_SETTING_NAME)) + i_slave_type = NM_SETTING_OVS_BRIDGE_SETTING_NAME; + else if (!strcmp (name, NM_SETTING_OVS_INTERFACE_SETTING_NAME)) + i_slave_type = NM_SETTING_OVS_PORT_SETTING_NAME; else continue; @@ -809,9 +813,20 @@ _normalize_ethernet_link_neg (NMConnection *self) } static gboolean +_without_ip_config (NMConnection *self) +{ + const char *connection_type = nm_connection_get_connection_type (self); + + g_return_val_if_fail (connection_type, FALSE); + if (strcmp (connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME) == 0) + return FALSE; + + return !!nm_setting_connection_get_master (nm_connection_get_setting_connection (self)); +} + +static gboolean _normalize_ip_config (NMConnection *self, GHashTable *parameters) { - NMSettingConnection *s_con = nm_connection_get_setting_connection (self); const char *default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; const char *default_ip6_method = NULL; NMSettingIPConfig *s_ip4, *s_ip6; @@ -829,7 +844,7 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters) s_ip6 = nm_connection_get_setting_ip6_config (self); s_proxy = nm_connection_get_setting_proxy (self); - if (nm_setting_connection_get_master (s_con)) { + if (_without_ip_config (self)) { /* Slave connections don't have IP configuration. */ if (s_ip4) @@ -1096,6 +1111,27 @@ _normalize_bluetooth_type (NMConnection *self, GHashTable *parameters) } static gboolean +_normalize_ovs_interface_type (NMConnection *self, GHashTable *parameters) +{ + NMSettingOvsInterface *s_ovs_interface = nm_connection_get_setting_ovs_interface (self); + gboolean modified; + int v; + + if (!s_ovs_interface) + return FALSE; + + v = _nm_setting_ovs_interface_verify_interface_type (s_ovs_interface, + self, + TRUE, + &modified, + NULL); + if (v != TRUE) + g_return_val_if_reached (modified); + + return modified; +} + +static gboolean _normalize_required_settings (NMConnection *self, GHashTable *parameters) { NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (self); @@ -1252,40 +1288,42 @@ _nm_connection_verify (NMConnection *connection, GError **error) s_ip6 = nm_connection_get_setting_ip6_config (connection); s_proxy = nm_connection_get_setting_proxy (connection); - if (nm_setting_connection_get_master (s_con)) { - if ( NM_IN_SET (normalizable_error_type, NM_SETTING_VERIFY_SUCCESS, - NM_SETTING_VERIFY_NORMALIZABLE) - && (s_ip4 || s_ip6 || s_proxy)) { - g_clear_error (&normalizable_error); - g_set_error_literal (&normalizable_error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_SETTING, - _("setting not allowed in slave connection")); - g_prefix_error (&normalizable_error, "%s: ", - s_ip4 - ? NM_SETTING_IP4_CONFIG_SETTING_NAME - : (s_ip6 - ? NM_SETTING_IP6_CONFIG_SETTING_NAME - : NM_SETTING_PROXY_SETTING_NAME)); - /* having a slave with IP config *was* and is a verify() error. */ - normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR; - } - } else { - if ( NM_IN_SET (normalizable_error_type, NM_SETTING_VERIFY_SUCCESS) - && (!s_ip4 || !s_ip6 || !s_proxy)) { - g_set_error_literal (&normalizable_error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_SETTING, - _("setting is required for non-slave connections")); - g_prefix_error (&normalizable_error, "%s: ", - !s_ip4 - ? NM_SETTING_IP4_CONFIG_SETTING_NAME - : (!s_ip6 - ? NM_SETTING_IP6_CONFIG_SETTING_NAME - : NM_SETTING_PROXY_SETTING_NAME)); - /* having a master without IP config was not a verify() error, accept - * it for backward compatibility. */ - normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE; + nm_assert (normalizable_error_type != NM_SETTING_VERIFY_ERROR); + if (NM_IN_SET (normalizable_error_type, NM_SETTING_VERIFY_SUCCESS, + NM_SETTING_VERIFY_NORMALIZABLE)) { + if (_without_ip_config (connection)) { + if (s_ip4 || s_ip6 || s_proxy) { + g_clear_error (&normalizable_error); + g_set_error_literal (&normalizable_error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_SETTING, + _("setting not allowed in slave connection")); + g_prefix_error (&normalizable_error, "%s: ", + s_ip4 + ? NM_SETTING_IP4_CONFIG_SETTING_NAME + : (s_ip6 + ? NM_SETTING_IP6_CONFIG_SETTING_NAME + : NM_SETTING_PROXY_SETTING_NAME)); + /* having a slave with IP config *was* and is a verify() error. */ + normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR; + } + } else { + if ( normalizable_error_type == NM_SETTING_VERIFY_SUCCESS + && (!s_ip4 || !s_ip6 || !s_proxy)) { + g_set_error_literal (&normalizable_error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_SETTING, + _("setting is required for non-slave connections")); + g_prefix_error (&normalizable_error, "%s: ", + !s_ip4 + ? NM_SETTING_IP4_CONFIG_SETTING_NAME + : (!s_ip6 + ? NM_SETTING_IP6_CONFIG_SETTING_NAME + : NM_SETTING_PROXY_SETTING_NAME)); + /* having a master without IP config was not a verify() error, accept + * it for backward compatibility. */ + normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE; + } } } @@ -1397,6 +1435,7 @@ nm_connection_normalize (NMConnection *connection, was_modified |= _normalize_team_config (connection, parameters); was_modified |= _normalize_team_port_config (connection, parameters); was_modified |= _normalize_bluetooth_type (connection, parameters); + was_modified |= _normalize_ovs_interface_type (connection, parameters); /* Verify anew. */ success = _nm_connection_verify (connection, error); @@ -1414,6 +1453,7 @@ nm_connection_normalize (NMConnection *connection, NM_CONNECTION_ERROR_FAILED, _("Unexpected failure to normalize the connection")); } + g_warning ("connection did not verify after normalization: %s", error ? (*error)->message : "??"); g_return_val_if_reached (FALSE); } @@ -1747,52 +1787,82 @@ _for_each_sort (NMSetting **p_a, NMSetting **p_b, void *unused) } /** - * nm_connection_for_each_setting_value: - * @connection: the #NMConnection - * @func: (scope call): user-supplied function called for each setting's property - * @user_data: user data passed to @func at each invocation + * nm_connection_get_settings: + * @connection: the #NMConnection instance + * @out_length: (allow-none): (out): the length of the returned array * - * Iterates over the properties of each #NMSetting object in the #NMConnection, - * calling the supplied user function for each property. - **/ -void -nm_connection_for_each_setting_value (NMConnection *connection, - NMSettingValueIterFn func, - gpointer user_data) + * Retrieves the settings in @connection. + * + * The returned array is %NULL-terminated. + * + * Returns: (array length=out_length) (transfer container): a + * %NULL-terminated array containing every setting of + * @connection. + * If the connection has no settings, %NULL is returned. + * + * Since: 1.10 + */ +NMSetting ** +nm_connection_get_settings (NMConnection *connection, + guint *out_length) { NMConnectionPrivate *priv; - gs_free NMSetting **arr_free = NULL; - NMSetting *arr_temp[20], **arr; + NMSetting **arr; GHashTableIter iter; - gpointer value; + NMSetting *setting; guint i, size; - g_return_if_fail (NM_IS_CONNECTION (connection)); - g_return_if_fail (func); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); priv = NM_CONNECTION_GET_PRIVATE (connection); size = g_hash_table_size (priv->settings); - if (!size) - return; - if (size > G_N_ELEMENTS (arr_temp)) - arr = arr_free = g_new (NMSetting *, size); - else - arr = arr_temp; + if (!size) { + NM_SET_OUT (out_length, 0); + return NULL; + } + + arr = g_new (NMSetting *, size + 1); g_hash_table_iter_init (&iter, priv->settings); - for (i = 0; g_hash_table_iter_next (&iter, NULL, &value); i++) - arr[i] = NM_SETTING (value); - g_assert (i == size); + for (i = 0; g_hash_table_iter_next (&iter, NULL, (gpointer *) &setting); i++) + arr[i] = setting; + nm_assert (i == size); + arr[size] = NULL; /* sort the settings. This has an effect on the order in which keyfile * prints them. */ if (size > 1) g_qsort_with_data (arr, size, sizeof (NMSetting *), (GCompareDataFunc) _for_each_sort, NULL); - for (i = 0; i < size; i++) - nm_setting_enumerate_values (arr[i], func, user_data); + NM_SET_OUT (out_length, size); + return arr; +} + +/** + * nm_connection_for_each_setting_value: + * @connection: the #NMConnection + * @func: (scope call): user-supplied function called for each setting's property + * @user_data: user data passed to @func at each invocation + * + * Iterates over the properties of each #NMSetting object in the #NMConnection, + * calling the supplied user function for each property. + **/ +void +nm_connection_for_each_setting_value (NMConnection *connection, + NMSettingValueIterFn func, + gpointer user_data) +{ + gs_free NMSetting **settings = NULL; + guint i, length = 0; + + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (func); + + settings = nm_connection_get_settings (connection, &length); + for (i = 0; i < length; i++) + nm_setting_enumerate_values (settings[i], func, user_data); } /** @@ -1983,6 +2053,9 @@ nm_connection_is_virtual (NMConnection *connection) || !strcmp (type, NM_SETTING_IP_TUNNEL_SETTING_NAME) || !strcmp (type, NM_SETTING_MACSEC_SETTING_NAME) || !strcmp (type, NM_SETTING_MACVLAN_SETTING_NAME) + || !strcmp (type, NM_SETTING_OVS_BRIDGE_SETTING_NAME) + || !strcmp (type, NM_SETTING_OVS_INTERFACE_SETTING_NAME) + || !strcmp (type, NM_SETTING_OVS_PORT_SETTING_NAME) || !strcmp (type, NM_SETTING_VXLAN_SETTING_NAME)) return TRUE; @@ -2336,6 +2409,70 @@ nm_connection_get_setting_olpc_mesh (NMConnection *connection) } /** + * nm_connection_get_setting_ovs_bridge: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingOvsBridge the connection might contain. + * + * Returns: (transfer none): an #NMSettingOvsBridge if the connection contains one, otherwise %NULL + * + * Since: 1.10 + **/ +NMSettingOvsBridge * +nm_connection_get_setting_ovs_bridge (NMConnection *connection) +{ + return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_BRIDGE); +} + +/** + * nm_connection_get_setting_ovs_interface: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingOvsInterface the connection might contain. + * + * Returns: (transfer none): an #NMSettingOvsInterface if the connection contains one, otherwise %NULL + * + * Since: 1.10 + **/ +NMSettingOvsInterface * +nm_connection_get_setting_ovs_interface (NMConnection *connection) +{ + return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_INTERFACE); +} + +/** + * nm_connection_get_setting_ovs_patch: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingOvsPatch the connection might contain. + * + * Returns: (transfer none): an #NMSettingOvsPatch if the connection contains one, otherwise %NULL + * + * Since: 1.10 + **/ +NMSettingOvsPatch * +nm_connection_get_setting_ovs_patch (NMConnection *connection) +{ + return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_PATCH); +} + +/** + * nm_connection_get_setting_ovs_port: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingOvsPort the connection might contain. + * + * Returns: (transfer none): an #NMSettingOvsPort if the connection contains one, otherwise %NULL + * + * Since: 1.10 + **/ +NMSettingOvsPort * +nm_connection_get_setting_ovs_port (NMConnection *connection) +{ + return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_PORT); +} + +/** * nm_connection_get_setting_ppp: * @connection: the #NMConnection * diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h index fae9862a40..c6b23a00bc 100644 --- a/libnm-core/nm-connection.h +++ b/libnm-core/nm-connection.h @@ -179,6 +179,10 @@ void nm_connection_for_each_setting_value (NMConnection *connection, NMSettingValueIterFn func, gpointer user_data); +NM_AVAILABLE_IN_1_10 +NMSetting ** nm_connection_get_settings (NMConnection *connection, + guint *out_length); + void nm_connection_dump (NMConnection *connection); /* Helpers */ @@ -213,6 +217,13 @@ NMSettingMacsec * nm_connection_get_setting_macsec (NMConnec NM_AVAILABLE_IN_1_2 NMSettingMacvlan * nm_connection_get_setting_macvlan (NMConnection *connection); NMSettingOlpcMesh * nm_connection_get_setting_olpc_mesh (NMConnection *connection); +NM_AVAILABLE_IN_1_10 +NMSettingOvsBridge * nm_connection_get_setting_ovs_bridge (NMConnection *connection); +NM_AVAILABLE_IN_1_10 +NMSettingOvsInterface * nm_connection_get_setting_ovs_interface (NMConnection *connection); +NMSettingOvsPatch * nm_connection_get_setting_ovs_patch (NMConnection *connection); +NM_AVAILABLE_IN_1_10 +NMSettingOvsPort * nm_connection_get_setting_ovs_port (NMConnection *connection); NMSettingPpp * nm_connection_get_setting_ppp (NMConnection *connection); NMSettingPppoe * nm_connection_get_setting_pppoe (NMConnection *connection); NM_AVAILABLE_IN_1_6 diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index adba8ed116..59ebfcb132 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -56,6 +56,10 @@ #include "nm-setting-macsec.h" #include "nm-setting-macvlan.h" #include "nm-setting-olpc-mesh.h" +#include "nm-setting-ovs-bridge.h" +#include "nm-setting-ovs-interface.h" +#include "nm-setting-ovs-patch.h" +#include "nm-setting-ovs-port.h" #include "nm-setting-ppp.h" #include "nm-setting-pppoe.h" #include "nm-setting-serial.h" diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h index 3b9ec37b8e..ae680b7a5a 100644 --- a/libnm-core/nm-core-types.h +++ b/libnm-core/nm-core-types.h @@ -50,6 +50,10 @@ typedef struct _NMSettingIP6Config NMSettingIP6Config; typedef struct _NMSettingMacsec NMSettingMacsec; typedef struct _NMSettingMacvlan NMSettingMacvlan; typedef struct _NMSettingOlpcMesh NMSettingOlpcMesh; +typedef struct _NMSettingOvsBridge NMSettingOvsBridge; +typedef struct _NMSettingOvsInterface NMSettingOvsInterface; +typedef struct _NMSettingOvsPatch NMSettingOvsPatch; +typedef struct _NMSettingOvsPort NMSettingOvsPort; typedef struct _NMSettingPpp NMSettingPpp; typedef struct _NMSettingPppoe NMSettingPppoe; typedef struct _NMSettingSerial NMSettingSerial; diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index 8b92dbe7e6..7f75c84955 100644 --- a/libnm-core/nm-dbus-interface.h +++ b/libnm-core/nm-dbus-interface.h @@ -36,43 +36,46 @@ /* * dbus services details */ -#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager" - -#define NM_DBUS_PATH "/org/freedesktop/NetworkManager" -#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager" -#define NM_DBUS_INTERFACE_DEVICE NM_DBUS_INTERFACE ".Device" -#define NM_DBUS_INTERFACE_DEVICE_WIRED NM_DBUS_INTERFACE_DEVICE ".Wired" -#define NM_DBUS_INTERFACE_DEVICE_ADSL NM_DBUS_INTERFACE_DEVICE ".Adsl" -#define NM_DBUS_INTERFACE_DEVICE_WIRELESS NM_DBUS_INTERFACE_DEVICE ".Wireless" -#define NM_DBUS_INTERFACE_DEVICE_BLUETOOTH NM_DBUS_INTERFACE_DEVICE ".Bluetooth" -#define NM_DBUS_INTERFACE_DEVICE_OLPC_MESH NM_DBUS_INTERFACE_DEVICE ".OlpcMesh" -#define NM_DBUS_PATH_ACCESS_POINT NM_DBUS_PATH "/AccessPoint" -#define NM_DBUS_INTERFACE_ACCESS_POINT NM_DBUS_INTERFACE ".AccessPoint" -#define NM_DBUS_INTERFACE_DEVICE_MODEM NM_DBUS_INTERFACE_DEVICE ".Modem" -#define NM_DBUS_INTERFACE_DEVICE_WIMAX NM_DBUS_INTERFACE_DEVICE ".WiMax" -#define NM_DBUS_INTERFACE_WIMAX_NSP NM_DBUS_INTERFACE ".WiMax.Nsp" -#define NM_DBUS_PATH_WIMAX_NSP NM_DBUS_PATH "/Nsp" -#define NM_DBUS_INTERFACE_ACTIVE_CONNECTION NM_DBUS_INTERFACE ".Connection.Active" -#define NM_DBUS_INTERFACE_IP4_CONFIG NM_DBUS_INTERFACE ".IP4Config" -#define NM_DBUS_INTERFACE_DHCP4_CONFIG NM_DBUS_INTERFACE ".DHCP4Config" -#define NM_DBUS_INTERFACE_IP6_CONFIG NM_DBUS_INTERFACE ".IP6Config" -#define NM_DBUS_INTERFACE_DHCP6_CONFIG NM_DBUS_INTERFACE ".DHCP6Config" -#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband" -#define NM_DBUS_INTERFACE_DEVICE_BOND NM_DBUS_INTERFACE_DEVICE ".Bond" -#define NM_DBUS_INTERFACE_DEVICE_DUMMY NM_DBUS_INTERFACE_DEVICE ".Dummy" -#define NM_DBUS_INTERFACE_DEVICE_TEAM NM_DBUS_INTERFACE_DEVICE ".Team" -#define NM_DBUS_INTERFACE_DEVICE_VLAN NM_DBUS_INTERFACE_DEVICE ".Vlan" -#define NM_DBUS_INTERFACE_DEVICE_BRIDGE NM_DBUS_INTERFACE_DEVICE ".Bridge" -#define NM_DBUS_INTERFACE_DEVICE_GENERIC NM_DBUS_INTERFACE_DEVICE ".Generic" -#define NM_DBUS_INTERFACE_DEVICE_VETH NM_DBUS_INTERFACE_DEVICE ".Veth" -#define NM_DBUS_INTERFACE_DEVICE_TUN NM_DBUS_INTERFACE_DEVICE ".Tun" -#define NM_DBUS_INTERFACE_DEVICE_MACSEC NM_DBUS_INTERFACE_DEVICE ".Macsec" -#define NM_DBUS_INTERFACE_DEVICE_MACVLAN NM_DBUS_INTERFACE_DEVICE ".Macvlan" -#define NM_DBUS_INTERFACE_DEVICE_PPP NM_DBUS_INTERFACE_DEVICE ".Ppp" -#define NM_DBUS_INTERFACE_DEVICE_VXLAN NM_DBUS_INTERFACE_DEVICE ".Vxlan" -#define NM_DBUS_INTERFACE_DEVICE_GRE NM_DBUS_INTERFACE_DEVICE ".Gre" -#define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL NM_DBUS_INTERFACE_DEVICE ".IPTunnel" -#define NM_DBUS_INTERFACE_DEVICE_STATISTICS NM_DBUS_INTERFACE_DEVICE ".Statistics" +#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager" + +#define NM_DBUS_PATH "/org/freedesktop/NetworkManager" +#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager" +#define NM_DBUS_INTERFACE_DEVICE NM_DBUS_INTERFACE ".Device" +#define NM_DBUS_INTERFACE_DEVICE_WIRED NM_DBUS_INTERFACE_DEVICE ".Wired" +#define NM_DBUS_INTERFACE_DEVICE_ADSL NM_DBUS_INTERFACE_DEVICE ".Adsl" +#define NM_DBUS_INTERFACE_DEVICE_WIRELESS NM_DBUS_INTERFACE_DEVICE ".Wireless" +#define NM_DBUS_INTERFACE_DEVICE_BLUETOOTH NM_DBUS_INTERFACE_DEVICE ".Bluetooth" +#define NM_DBUS_INTERFACE_DEVICE_OLPC_MESH NM_DBUS_INTERFACE_DEVICE ".OlpcMesh" +#define NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE NM_DBUS_INTERFACE_DEVICE ".OvsInterface" +#define NM_DBUS_INTERFACE_DEVICE_OVS_PORT NM_DBUS_INTERFACE_DEVICE ".OvsPort" +#define NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE NM_DBUS_INTERFACE_DEVICE ".OvsBridge" +#define NM_DBUS_PATH_ACCESS_POINT NM_DBUS_PATH "/AccessPoint" +#define NM_DBUS_INTERFACE_ACCESS_POINT NM_DBUS_INTERFACE ".AccessPoint" +#define NM_DBUS_INTERFACE_DEVICE_MODEM NM_DBUS_INTERFACE_DEVICE ".Modem" +#define NM_DBUS_INTERFACE_DEVICE_WIMAX NM_DBUS_INTERFACE_DEVICE ".WiMax" +#define NM_DBUS_INTERFACE_WIMAX_NSP NM_DBUS_INTERFACE ".WiMax.Nsp" +#define NM_DBUS_PATH_WIMAX_NSP NM_DBUS_PATH "/Nsp" +#define NM_DBUS_INTERFACE_ACTIVE_CONNECTION NM_DBUS_INTERFACE ".Connection.Active" +#define NM_DBUS_INTERFACE_IP4_CONFIG NM_DBUS_INTERFACE ".IP4Config" +#define NM_DBUS_INTERFACE_DHCP4_CONFIG NM_DBUS_INTERFACE ".DHCP4Config" +#define NM_DBUS_INTERFACE_IP6_CONFIG NM_DBUS_INTERFACE ".IP6Config" +#define NM_DBUS_INTERFACE_DHCP6_CONFIG NM_DBUS_INTERFACE ".DHCP6Config" +#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband" +#define NM_DBUS_INTERFACE_DEVICE_BOND NM_DBUS_INTERFACE_DEVICE ".Bond" +#define NM_DBUS_INTERFACE_DEVICE_DUMMY NM_DBUS_INTERFACE_DEVICE ".Dummy" +#define NM_DBUS_INTERFACE_DEVICE_TEAM NM_DBUS_INTERFACE_DEVICE ".Team" +#define NM_DBUS_INTERFACE_DEVICE_VLAN NM_DBUS_INTERFACE_DEVICE ".Vlan" +#define NM_DBUS_INTERFACE_DEVICE_BRIDGE NM_DBUS_INTERFACE_DEVICE ".Bridge" +#define NM_DBUS_INTERFACE_DEVICE_GENERIC NM_DBUS_INTERFACE_DEVICE ".Generic" +#define NM_DBUS_INTERFACE_DEVICE_VETH NM_DBUS_INTERFACE_DEVICE ".Veth" +#define NM_DBUS_INTERFACE_DEVICE_TUN NM_DBUS_INTERFACE_DEVICE ".Tun" +#define NM_DBUS_INTERFACE_DEVICE_MACSEC NM_DBUS_INTERFACE_DEVICE ".Macsec" +#define NM_DBUS_INTERFACE_DEVICE_MACVLAN NM_DBUS_INTERFACE_DEVICE ".Macvlan" +#define NM_DBUS_INTERFACE_DEVICE_PPP NM_DBUS_INTERFACE_DEVICE ".Ppp" +#define NM_DBUS_INTERFACE_DEVICE_VXLAN NM_DBUS_INTERFACE_DEVICE ".Vxlan" +#define NM_DBUS_INTERFACE_DEVICE_GRE NM_DBUS_INTERFACE_DEVICE ".Gre" +#define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL NM_DBUS_INTERFACE_DEVICE ".IPTunnel" +#define NM_DBUS_INTERFACE_DEVICE_STATISTICS NM_DBUS_INTERFACE_DEVICE ".Statistics" #define NM_DBUS_INTERFACE_SETTINGS "org.freedesktop.NetworkManager.Settings" #define NM_DBUS_PATH_SETTINGS "/org/freedesktop/NetworkManager/Settings" @@ -205,35 +208,41 @@ typedef enum { * @NM_DEVICE_TYPE_MACSEC: a MACsec interface * @NM_DEVICE_TYPE_DUMMY: a dummy interface * @NM_DEVICE_TYPE_PPP: a PPP interface + * @NM_DEVICE_TYPE_OVS_INTERFACE: a OpenVSwitch interface + * @NM_DEVICE_TYPE_OVS_PORT: a OpenVSwitch port + * @NM_DEVICE_TYPE_OVS_BRIDGE: a OpenVSwitch bridge * * #NMDeviceType values indicate the type of hardware represented by a * device object. **/ typedef enum { - NM_DEVICE_TYPE_UNKNOWN = 0, - NM_DEVICE_TYPE_ETHERNET = 1, - NM_DEVICE_TYPE_WIFI = 2, - NM_DEVICE_TYPE_UNUSED1 = 3, - NM_DEVICE_TYPE_UNUSED2 = 4, - NM_DEVICE_TYPE_BT = 5, /* Bluetooth */ - NM_DEVICE_TYPE_OLPC_MESH = 6, - NM_DEVICE_TYPE_WIMAX = 7, - NM_DEVICE_TYPE_MODEM = 8, - NM_DEVICE_TYPE_INFINIBAND = 9, - NM_DEVICE_TYPE_BOND = 10, - NM_DEVICE_TYPE_VLAN = 11, - NM_DEVICE_TYPE_ADSL = 12, - NM_DEVICE_TYPE_BRIDGE = 13, - NM_DEVICE_TYPE_GENERIC = 14, - NM_DEVICE_TYPE_TEAM = 15, - NM_DEVICE_TYPE_TUN = 16, - NM_DEVICE_TYPE_IP_TUNNEL = 17, - NM_DEVICE_TYPE_MACVLAN = 18, - NM_DEVICE_TYPE_VXLAN = 19, - NM_DEVICE_TYPE_VETH = 20, - NM_DEVICE_TYPE_MACSEC = 21, - NM_DEVICE_TYPE_DUMMY = 22, - NM_DEVICE_TYPE_PPP = 23, + NM_DEVICE_TYPE_UNKNOWN = 0, + NM_DEVICE_TYPE_ETHERNET = 1, + NM_DEVICE_TYPE_WIFI = 2, + NM_DEVICE_TYPE_UNUSED1 = 3, + NM_DEVICE_TYPE_UNUSED2 = 4, + NM_DEVICE_TYPE_BT = 5, /* Bluetooth */ + NM_DEVICE_TYPE_OLPC_MESH = 6, + NM_DEVICE_TYPE_WIMAX = 7, + NM_DEVICE_TYPE_MODEM = 8, + NM_DEVICE_TYPE_INFINIBAND = 9, + NM_DEVICE_TYPE_BOND = 10, + NM_DEVICE_TYPE_VLAN = 11, + NM_DEVICE_TYPE_ADSL = 12, + NM_DEVICE_TYPE_BRIDGE = 13, + NM_DEVICE_TYPE_GENERIC = 14, + NM_DEVICE_TYPE_TEAM = 15, + NM_DEVICE_TYPE_TUN = 16, + NM_DEVICE_TYPE_IP_TUNNEL = 17, + NM_DEVICE_TYPE_MACVLAN = 18, + NM_DEVICE_TYPE_VXLAN = 19, + NM_DEVICE_TYPE_VETH = 20, + NM_DEVICE_TYPE_MACSEC = 21, + NM_DEVICE_TYPE_DUMMY = 22, + NM_DEVICE_TYPE_PPP = 23, + NM_DEVICE_TYPE_OVS_INTERFACE = 24, + NM_DEVICE_TYPE_OVS_PORT = 25, + NM_DEVICE_TYPE_OVS_BRIDGE = 26, } NMDeviceType; /** @@ -541,6 +550,7 @@ typedef enum { * @NM_DEVICE_STATE_REASON_NEW_ACTIVATION: New connection activation was enqueued * @NM_DEVICE_STATE_REASON_PARENT_CHANGED: the device's parent changed * @NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED: the device parent's management changed + * @NM_DEVICE_STATE_REASON_OVSDB_FAILED: problem communicating with OpenVSwitch database * * Device state change reason codes */ @@ -608,6 +618,7 @@ typedef enum { NM_DEVICE_STATE_REASON_NEW_ACTIVATION = 60, NM_DEVICE_STATE_REASON_PARENT_CHANGED = 61, NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED = 62, + NM_DEVICE_STATE_REASON_OVSDB_FAILED = 63, } NMDeviceStateReason; /** diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c index 728367c9b2..28d7a8f6b7 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -859,6 +859,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) gboolean is_slave; const char *slave_setting_type; NMSetting *normerr_base_type = NULL; + const char *type; + const char *slave_type; const char *normerr_slave_setting_type = NULL; const char *normerr_missing_slave_type = NULL; const char *normerr_missing_slave_type_port = NULL; @@ -904,8 +906,10 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } - if (!priv->type) { - if (!connection || !(normerr_base_type = _nm_connection_find_base_type_setting (connection))) { + type = priv->type; + if (!type) { + if ( !connection + || !(normerr_base_type = _nm_connection_find_base_type_setting (connection))) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY, @@ -913,10 +917,11 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE); return FALSE; } + type = nm_setting_get_name (normerr_base_type); } else { GType base_type; - if (!priv->type[0]) { + if (!type[0]) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -925,21 +930,21 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - base_type = nm_setting_lookup_type (priv->type); + base_type = nm_setting_lookup_type (type); if ( base_type == G_TYPE_INVALID || _nm_setting_type_get_base_type_priority (base_type) == NM_SETTING_PRIORITY_INVALID) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, _("connection type '%s' is not valid"), - priv->type); + type); g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE); return FALSE; } /* Make sure the corresponding 'type' item is present */ if ( connection - && !nm_connection_get_setting_by_name (connection, priv->type)) { + && !nm_connection_get_setting_by_name (connection, type)) { NMSetting *s_base; NMConnection *connection2; @@ -952,7 +957,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) g_object_unref (connection2); if (!normerr_base_setting) { - _set_error_missing_base_setting (error, priv->type); + _set_error_missing_base_setting (error, type); return FALSE; } } @@ -960,13 +965,14 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) is_slave = FALSE; slave_setting_type = NULL; - if (priv->slave_type) { - is_slave = _nm_setting_slave_type_is_valid (priv->slave_type, &slave_setting_type); + slave_type = priv->slave_type; + if (slave_type) { + is_slave = _nm_setting_slave_type_is_valid (slave_type, &slave_setting_type); if (!is_slave) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("Unknown slave type '%s'"), priv->slave_type); + _("Unknown slave type '%s'"), slave_type); g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE); return FALSE; } @@ -986,8 +992,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) && !nm_connection_get_setting_by_name (connection, slave_setting_type)) normerr_slave_setting_type = slave_setting_type; } else { + nm_assert (!slave_type); if (priv->master) { - const char *slave_type; NMSetting *s_port; if ( connection @@ -1006,6 +1012,18 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } + if ( nm_streq0 (type, NM_SETTING_OVS_PORT_SETTING_NAME) + && !nm_streq0 (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_PROPERTY, + _("Only '%s' connections can be enslaved to '%s'"), + NM_SETTING_OVS_PORT_SETTING_NAME, + NM_SETTING_OVS_BRIDGE_SETTING_NAME); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE); + return FALSE; + } + if (priv->metered != NM_METERED_UNKNOWN && priv->metered != NM_METERED_YES && priv->metered != NM_METERED_NO) { diff --git a/libnm-core/nm-setting-ip-tunnel.c b/libnm-core/nm-setting-ip-tunnel.c index 7e1737fc3b..fa8d4a7a24 100644 --- a/libnm-core/nm-setting-ip-tunnel.c +++ b/libnm-core/nm-setting-ip-tunnel.c @@ -758,7 +758,7 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *setting_class) G_PARAM_STATIC_STRINGS)); /** - * NMSettingIPTunel:mtu: + * NMSettingIPTunnel:mtu: * * If non-zero, only transmit packets of the specified size or smaller, * breaking larger packets up into multiple fragments. diff --git a/libnm-core/nm-setting-ovs-bridge.c b/libnm-core/nm-setting-ovs-bridge.c new file mode 100644 index 0000000000..fbae95d0c9 --- /dev/null +++ b/libnm-core/nm-setting-ovs-bridge.c @@ -0,0 +1,337 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-setting-ovs-bridge.h" + +#include "nm-connection-private.h" +#include "nm-setting-connection.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-ovs-bridge + * @short_description: Describes connection properties for OpenVSwitch bridges. + * + * The #NMSettingOvsBridge object is a #NMSetting subclass that describes properties + * necessary for OpenVSwitch bridges. + **/ + +enum { + PROP_0, + PROP_FAIL_MODE, + PROP_MCAST_SNOOPING_ENABLE, + PROP_RSTP_ENABLE, + PROP_STP_ENABLE, + LAST_PROP +}; + +/** + * NMSettingOvsBridge: + * + * OvsBridge Link Settings + */ +struct _NMSettingOvsBridge { + NMSetting parent; + + char *fail_mode; + gboolean mcast_snooping_enable; + gboolean rstp_enable; + gboolean stp_enable; +}; + +struct _NMSettingOvsBridgeClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE_WITH_CODE (NMSettingOvsBridge, nm_setting_ovs_bridge, NM_TYPE_SETTING, + _nm_register_setting (OVS_BRIDGE, NM_SETTING_PRIORITY_HW_BASE)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OVS_BRIDGE) + +/*****************************************************************************/ + +/** + * nm_setting_ovs_bridge_get_fail_mode: + * @self: the #NMSettingOvsBridge + * + * Returns: the #NMSettingOvsBridge:fail_mode property of the setting + * + * Since: 1.10 + **/ +const char * +nm_setting_ovs_bridge_get_fail_mode (NMSettingOvsBridge *self) +{ + g_return_val_if_fail (NM_IS_SETTING_OVS_BRIDGE (self), NULL); + + return self->fail_mode; +} + +/** + * nm_setting_ovs_bridge_get_mcast_snooping_enable: + * @self: the #NMSettingOvsBridge + * + * Returns: the #NMSettingOvsBridge:mcast_snooping_enable property of the setting + * + * Since: 1.10 + **/ +gboolean +nm_setting_ovs_bridge_get_mcast_snooping_enable (NMSettingOvsBridge *self) +{ + g_return_val_if_fail (NM_IS_SETTING_OVS_BRIDGE (self), FALSE); + + return self->mcast_snooping_enable; +} + +/** + * nm_setting_ovs_bridge_get_rstp_enable: + * @self: the #NMSettingOvsBridge + * + * Returns: the #NMSettingOvsBridge:rstp_enable property of the setting + * + * Since: 1.10 + **/ +gboolean +nm_setting_ovs_bridge_get_rstp_enable (NMSettingOvsBridge *self) +{ + g_return_val_if_fail (NM_IS_SETTING_OVS_BRIDGE (self), FALSE); + + return self->rstp_enable; +} + +/** + * nm_setting_ovs_bridge_get_stp_enable: + * @self: the #NMSettingOvsBridge + * + * Returns: the #NMSettingOvsBridge:stp_enable property of the setting + * + * Since: 1.10 + **/ +gboolean +nm_setting_ovs_bridge_get_stp_enable (NMSettingOvsBridge *self) +{ + g_return_val_if_fail (NM_IS_SETTING_OVS_BRIDGE (self), FALSE); + + return self->stp_enable; +} + +/*****************************************************************************/ + +static int +verify (NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingOvsBridge *self = NM_SETTING_OVS_BRIDGE (setting); + + if (!_nm_connection_verify_required_interface_name (connection, error)) + return FALSE; + + if (connection) { + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (connection); + if (!s_con) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_SETTING, + _("missing setting")); + g_prefix_error (error, "%s: ", NM_SETTING_CONNECTION_SETTING_NAME); + return FALSE; + } + + if (nm_setting_connection_get_master (s_con)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("A connection with a '%s' setting must not have a master."), + NM_SETTING_OVS_BRIDGE_SETTING_NAME); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER); + return FALSE; + } + } + + if (!NM_IN_STRSET (self->fail_mode, "secure", "standalone", NULL)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not allowed in fail_mode"), + self->fail_mode); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_BRIDGE_SETTING_NAME, NM_SETTING_OVS_BRIDGE_FAIL_MODE); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingOvsBridge *self = NM_SETTING_OVS_BRIDGE (object); + + switch (prop_id) { + case PROP_FAIL_MODE: + g_value_set_string (value, self->fail_mode); + break; + case PROP_MCAST_SNOOPING_ENABLE: + g_value_set_boolean (value, self->mcast_snooping_enable); + break; + case PROP_RSTP_ENABLE: + g_value_set_boolean (value, self->rstp_enable); + break; + case PROP_STP_ENABLE: + g_value_set_boolean (value, self->stp_enable); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingOvsBridge *self = NM_SETTING_OVS_BRIDGE (object); + + switch (prop_id) { + case PROP_FAIL_MODE: + g_free (self->fail_mode); + self->fail_mode = g_value_dup_string (value); + break; + case PROP_MCAST_SNOOPING_ENABLE: + self->mcast_snooping_enable = g_value_get_boolean (value); + break; + case PROP_RSTP_ENABLE: + self->rstp_enable = g_value_get_boolean (value); + break; + case PROP_STP_ENABLE: + self->stp_enable = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_setting_ovs_bridge_init (NMSettingOvsBridge *self) +{ +} + +/** + * nm_setting_ovs_bridge_new: + * + * Creates a new #NMSettingOvsBridge object with default values. + * + * Returns: (transfer full): the new empty #NMSettingOvsBridge object + * + * Since: 1.10 + **/ +NMSetting * +nm_setting_ovs_bridge_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_BRIDGE, NULL); +} + +static void +finalize (GObject *object) +{ + NMSettingOvsBridge *self = NM_SETTING_OVS_BRIDGE (object); + + g_free (self->fail_mode); + + G_OBJECT_CLASS (nm_setting_ovs_bridge_parent_class)->finalize (object); +} + +static void +nm_setting_ovs_bridge_class_init (NMSettingOvsBridgeClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /** + * NMSettingOvsBridge:fail-mode: + * + * The bridge failure mode. One of "secure", "standalone" or empty. + * + * Since: 1.10 + **/ + g_object_class_install_property + (object_class, PROP_FAIL_MODE, + g_param_spec_string (NM_SETTING_OVS_BRIDGE_FAIL_MODE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingOvsBridge:mcast-snooping-enable: + * + * Enable or disable multicast snooping. + * + * Since: 1.10 + **/ + g_object_class_install_property + (object_class, PROP_MCAST_SNOOPING_ENABLE, + g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingOvsBridge:rstp-enable: + * + * Enable or disable RSTP. + * + * Since: 1.10 + **/ + g_object_class_install_property + (object_class, PROP_RSTP_ENABLE, + g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_RSTP_ENABLE, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingOvsBridge:stp-enable: + * + * Enable or disable STP. + * + * Since: 1.10 + **/ + g_object_class_install_property + (object_class, PROP_STP_ENABLE, + g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_STP_ENABLE, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-ovs-bridge.h b/libnm-core/nm-setting-ovs-bridge.h new file mode 100644 index 0000000000..d4837e58a2 --- /dev/null +++ b/libnm-core/nm-setting-ovs-bridge.h @@ -0,0 +1,63 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_SETTING_OVS_BRIDGE_H__ +#define __NM_SETTING_OVS_BRIDGE_H__ + +#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION) +#error "Only <NetworkManager.h> can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_OVS_BRIDGE (nm_setting_ovs_bridge_get_type ()) +#define NM_SETTING_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OVS_BRIDGE, NMSettingOvsBridge)) +#define NM_SETTING_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OVS_BRIDGECONFIG, NMSettingOvsBridgeClass)) +#define NM_IS_SETTING_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OVS_BRIDGE)) +#define NM_IS_SETTING_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OVS_BRIDGE)) +#define NM_SETTING_OVS_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OVS_BRIDGE, NMSettingOvsBridgeClass)) + +#define NM_SETTING_OVS_BRIDGE_SETTING_NAME "ovs-bridge" + +#define NM_SETTING_OVS_BRIDGE_FAIL_MODE "fail-mode" +#define NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE "mcast-snooping-enable" +#define NM_SETTING_OVS_BRIDGE_RSTP_ENABLE "rstp-enable" +#define NM_SETTING_OVS_BRIDGE_STP_ENABLE "stp-enable" + +typedef struct _NMSettingOvsBridgeClass NMSettingOvsBridgeClass; + +NM_AVAILABLE_IN_1_10 +GType nm_setting_ovs_bridge_get_type (void); +NM_AVAILABLE_IN_1_10 +NMSetting *nm_setting_ovs_bridge_new (void); + +NM_AVAILABLE_IN_1_10 +const char *nm_setting_ovs_bridge_get_fail_mode (NMSettingOvsBridge *self); +NM_AVAILABLE_IN_1_10 +gboolean nm_setting_ovs_bridge_get_mcast_snooping_enable (NMSettingOvsBridge *self); +NM_AVAILABLE_IN_1_10 +gboolean nm_setting_ovs_bridge_get_rstp_enable (NMSettingOvsBridge *self); +NM_AVAILABLE_IN_1_10 +gboolean nm_setting_ovs_bridge_get_stp_enable (NMSettingOvsBridge *self); + +G_END_DECLS + +#endif /* __NM_SETTING_OVS_BRIDGE_H__ */ diff --git a/libnm-core/nm-setting-ovs-interface.c b/libnm-core/nm-setting-ovs-interface.c new file mode 100644 index 0000000000..f06db32abb --- /dev/null +++ b/libnm-core/nm-setting-ovs-interface.c @@ -0,0 +1,392 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-setting-ovs-interface.h" + +#include "nm-connection-private.h" +#include "nm-setting-connection.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-ovs-interface + * @short_description: Describes connection properties for OpenVSwitch interfaces. + * + * The #NMSettingOvsInterface object is a #NMSetting subclass that describes properties + * necessary for OpenVSwitch interfaces. + **/ + +enum { + PROP_0, + PROP_TYPE, + LAST_PROP +}; + +/** + * NMSettingOvsInterface: + * + * OpenVSwitch Interface Settings + */ +struct _NMSettingOvsInterface { + NMSetting parent; + + char *type; +}; + +struct _NMSettingOvsInterfaceClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE_WITH_CODE (NMSettingOvsInterface, nm_setting_ovs_interface, NM_TYPE_SETTING, + _nm_register_setting (OVS_INTERFACE, NM_SETTING_PRIORITY_HW_BASE)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OVS_INTERFACE) + +/*****************************************************************************/ + +/** + * nm_setting_ovs_interface_get_interface_type: + * @self: the #NMSettingOvsInterface + * + * Returns: the #NMSettingOvsInterface:type property of the setting + * + * Since: 1.10 + **/ +const char * +nm_setting_ovs_interface_get_interface_type (NMSettingOvsInterface *self) +{ + g_return_val_if_fail (NM_IS_SETTING_OVS_INTERFACE (self), NULL); + + return self->type; +} + +/*****************************************************************************/ + +int +_nm_setting_ovs_interface_verify_interface_type (NMSettingOvsInterface *self, + NMConnection *connection, + gboolean normalize, + gboolean *out_modified, + GError **error) +{ + gboolean has_patch; + const char *type; + const char *connection_type; + gboolean is_ovs_connection_type; + gboolean missing_patch_setting = FALSE; + + g_return_val_if_fail (NM_IS_SETTING_OVS_INTERFACE (self), FALSE); + if (normalize) { + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + nm_assert (self == nm_connection_get_setting_ovs_interface (connection)); + } else + g_return_val_if_fail (!connection || NM_IS_CONNECTION (connection), FALSE); + + NM_SET_OUT (out_modified, FALSE); + + type = self ? self->type : NULL; + + if ( type + && !NM_IN_STRSET (type, + "internal", + "system", + "patch")) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid interface type"), + type); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE); + return FALSE; + } + + if (!connection) + return TRUE; + + connection_type = nm_connection_get_connection_type (connection); + if (!connection_type) { + /* if we have an ovs-interface, then the connection type must be either + * "ovs-interface" (for non "system" type) or anything else (for "system" type). + * + * The connection type usually can be normalized based on the presence of a + * base setting. However, in this case, if the connection type is missing, + * that is too complicate to guess what the user wanted. + * + * Require the use to be explicit and fail. */ + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("A connection with a '%s' setting needs connection.type explicitly set"), + NM_SETTING_OVS_INTERFACE_SETTING_NAME); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE); + return FALSE; + } + + if (nm_streq (connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME)) { + if ( type + && nm_streq (type, "system")) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("A connection of type '%s' cannot have ovs-interface.type \"system\""), + NM_SETTING_OVS_INTERFACE_SETTING_NAME); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE); + return FALSE; + } + is_ovs_connection_type = TRUE; + } else { + if ( type + && !nm_streq (type, "system")) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("A connection of type '%s' cannot have an ovs-interface.type \"%s\""), + connection_type, + type); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE); + return FALSE; + } + is_ovs_connection_type = FALSE; + } + + has_patch = !!nm_connection_get_setting_by_name (connection, NM_SETTING_OVS_PATCH_SETTING_NAME); + + if (has_patch) { + if (!is_ovs_connection_type) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("A connection with '%s' setting must be of connection.type \"ovs-interface\" but is \"%s\""), + NM_SETTING_OVS_PATCH_SETTING_NAME, + connection_type); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE); + return FALSE; + } + if (type) { + if (!nm_streq (type, "patch")) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("A connection with '%s' setting needs to be of 'patch' interface type, not '%s'"), + NM_SETTING_OVS_PATCH_SETTING_NAME, + type); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE); + return FALSE; + } + return TRUE; + } + type = "patch"; + goto normalize; + } else { + if (nm_streq0 (type, "patch")) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_SETTING, + _("A connection with ovs-interface.type '%s' setting a 'ovs-patch' setting"), + type); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE); + return FALSE; + } + } + + if (type) + return TRUE; + + if (is_ovs_connection_type) + type = "internal"; + else + type = "system"; +normalize: + if (!normalize) { + if (!self) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_SETTING, + _("Missing ovs interface setting")); + g_prefix_error (error, "%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME); + } else { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_PROPERTY, + _("Missing ovs interface type")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_INTERFACE_SETTING_NAME, NM_SETTING_OVS_INTERFACE_TYPE); + } + if (missing_patch_setting) { + } + return NM_SETTING_VERIFY_NORMALIZABLE_ERROR; + } + + if (!self) { + self = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ()); + nm_connection_add_setting (connection, NM_SETTING (self)); + } + g_object_set (self, + NM_SETTING_OVS_INTERFACE_TYPE, type, + NULL); + NM_SET_OUT (out_modified, TRUE); + + return TRUE; +} + + +static int +verify (NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (setting); + + if (connection) { + NMSettingConnection *s_con; + const char *slave_type; + + s_con = nm_connection_get_setting_connection (connection); + if (!s_con) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_SETTING, + _("missing setting")); + g_prefix_error (error, "%s: ", NM_SETTING_CONNECTION_SETTING_NAME); + return FALSE; + } + + if (!nm_setting_connection_get_master (s_con)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("A connection with a '%s' setting must have a master."), + NM_SETTING_OVS_INTERFACE_SETTING_NAME); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER); + return FALSE; + } + + slave_type = nm_setting_connection_get_slave_type (s_con); + if ( slave_type + && !nm_streq (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("A connection with a '%s' setting must have the slave-type set to '%s'. Instead it is '%s'"), + NM_SETTING_OVS_INTERFACE_SETTING_NAME, + NM_SETTING_OVS_PORT_SETTING_NAME, + slave_type); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE); + return FALSE; + } + } + + return _nm_setting_ovs_interface_verify_interface_type (self, + connection, + FALSE, + NULL, + error); +} + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (object); + + switch (prop_id) { + case PROP_TYPE: + g_value_set_string (value, self->type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (object); + + switch (prop_id) { + case PROP_TYPE: + g_free (self->type); + self->type = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_setting_ovs_interface_init (NMSettingOvsInterface *self) +{ +} + +/** + * nm_setting_ovs_interface_new: + * + * Creates a new #NMSettingOvsInterface object with default values. + * + * Returns: (transfer full): the new empty #NMSettingOvsInterface object + * + * Since: 1.10 + **/ +NMSetting * +nm_setting_ovs_interface_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_INTERFACE, NULL); +} + +static void +finalize (GObject *object) +{ + NMSettingOvsInterface *self = NM_SETTING_OVS_INTERFACE (object); + + g_free (self->type); + + G_OBJECT_CLASS (nm_setting_ovs_interface_parent_class)->finalize (object); +} + +static void +nm_setting_ovs_interface_class_init (NMSettingOvsInterfaceClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /** + * NMSettingOvsInterface:type: + * + * The interface type. Either "internal", or empty. + * + * Since: 1.10 + **/ + g_object_class_install_property + (object_class, PROP_TYPE, + g_param_spec_string (NM_SETTING_OVS_INTERFACE_TYPE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-ovs-interface.h b/libnm-core/nm-setting-ovs-interface.h new file mode 100644 index 0000000000..7261cfd8c9 --- /dev/null +++ b/libnm-core/nm-setting-ovs-interface.h @@ -0,0 +1,54 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_SETTING_OVS_INTERFACE_H__ +#define __NM_SETTING_OVS_INTERFACE_H__ + +#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION) +#error "Only <NetworkManager.h> can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_get_type ()) +#define NM_SETTING_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OVS_INTERFACE, NMSettingOvsInterface)) +#define NM_SETTING_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OVS_INTERFACECONFIG, NMSettingOvsInterfaceClass)) +#define NM_IS_SETTING_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OVS_INTERFACE)) +#define NM_IS_SETTING_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OVS_INTERFACE)) +#define NM_SETTING_OVS_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OVS_INTERFACE, NMSettingOvsInterfaceClass)) + +#define NM_SETTING_OVS_INTERFACE_SETTING_NAME "ovs-interface" + +#define NM_SETTING_OVS_INTERFACE_TYPE "type" + +typedef struct _NMSettingOvsInterfaceClass NMSettingOvsInterfaceClass; + +NM_AVAILABLE_IN_1_10 +GType nm_setting_ovs_interface_get_type (void); +NM_AVAILABLE_IN_1_10 +NMSetting *nm_setting_ovs_interface_new (void); + +NM_AVAILABLE_IN_1_10 +const char *nm_setting_ovs_interface_get_interface_type (NMSettingOvsInterface *self); + +G_END_DECLS + +#endif /* __NM_SETTING_OVS_INTERFACE_H__ */ diff --git a/libnm-core/nm-setting-ovs-patch.c b/libnm-core/nm-setting-ovs-patch.c new file mode 100644 index 0000000000..9e38091474 --- /dev/null +++ b/libnm-core/nm-setting-ovs-patch.c @@ -0,0 +1,215 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-setting-ovs-patch.h" + +#include "nm-connection-private.h" +#include "nm-setting-connection.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-ovs-patch + * @short_description: Describes connection properties for OpenVSwitch patch interfaces. + * + * The #NMSettingOvsPatch object is a #NMSetting subclass that describes properties + * necessary for OpenVSwitch interfaces of type "patch". + **/ + +enum { + PROP_0, + PROP_PEER, + LAST_PROP +}; + +/** + * NMSettingOvsPatch: + * + * OvsPatch Link Settings + */ +struct _NMSettingOvsPatch { + NMSetting parent; + + char *peer; +}; + +struct _NMSettingOvsPatchClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE_WITH_CODE (NMSettingOvsPatch, nm_setting_ovs_patch, NM_TYPE_SETTING, + _nm_register_setting (OVS_PATCH, NM_SETTING_PRIORITY_HW_BASE)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OVS_PATCH) + +/*****************************************************************************/ + +/** + * nm_setting_ovs_patch_get_peer: + * @self: the #NMSettingOvsPatch + * + * Returns: the #NMSettingOvsPatch:peer property of the setting + * + * Since: 1.10 + **/ +const char * +nm_setting_ovs_patch_get_peer (NMSettingOvsPatch *self) +{ + g_return_val_if_fail (NM_IS_SETTING_OVS_PATCH (self), NULL); + + return self->peer; +} + +/*****************************************************************************/ + +static int +verify (NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (setting); + int family = AF_UNSPEC; + + if (!_nm_connection_verify_required_interface_name (connection, error)) + return FALSE; + + if (!self->peer) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", + NM_SETTING_OVS_PATCH_SETTING_NAME, + NM_SETTING_OVS_PATCH_PEER); + return FALSE; + } + + if (nm_utils_ipaddr_valid (AF_INET, self->peer)) + family = AF_INET; + else if (nm_utils_ipaddr_valid (AF_INET6, self->peer)) + family = AF_INET6; + else { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid IP address"), + self->peer); + g_prefix_error (error, "%s.%s: ", + NM_SETTING_OVS_PATCH_SETTING_NAME, + NM_SETTING_OVS_PATCH_PEER); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (object); + + switch (prop_id) { + case PROP_PEER: + g_value_set_string (value, self->peer); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (object); + + switch (prop_id) { + case PROP_PEER: + g_free (self->peer); + self->peer = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_setting_ovs_patch_init (NMSettingOvsPatch *self) +{ +} + +/** + * nm_setting_ovs_patch_new: + * + * Creates a new #NMSettingOvsPatch object with default values. + * + * Returns: (transfer full): the new empty #NMSettingOvsPatch object + * + * Since: 1.10 + **/ +NMSetting * +nm_setting_ovs_patch_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_PATCH, NULL); +} + +static void +finalize (GObject *object) +{ + NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (object); + + g_free (self->peer); + + G_OBJECT_CLASS (nm_setting_ovs_patch_parent_class)->finalize (object); +} + +static void +nm_setting_ovs_patch_class_init (NMSettingOvsPatchClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /** + * NMSettingOvsPatch:peer: + * + * Specifies the unicast destination IP address of a remote OpenVSwitch + * bridge port to connect to. + * + * Since: 1.10 + **/ + g_object_class_install_property + (object_class, PROP_PEER, + g_param_spec_string (NM_SETTING_OVS_PATCH_PEER, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-ovs-patch.h b/libnm-core/nm-setting-ovs-patch.h new file mode 100644 index 0000000000..091f0d43d5 --- /dev/null +++ b/libnm-core/nm-setting-ovs-patch.h @@ -0,0 +1,54 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_SETTING_OVS_PATCH_H__ +#define __NM_SETTING_OVS_PATCH_H__ + +#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION) +#error "Only <NetworkManager.h> can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_OVS_PATCH (nm_setting_ovs_patch_get_type ()) +#define NM_SETTING_OVS_PATCH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OVS_PATCH, NMSettingOvsPatch)) +#define NM_SETTING_OVS_PATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OVS_PATCHCONFIG, NMSettingOvsPatchClass)) +#define NM_IS_SETTING_OVS_PATCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OVS_PATCH)) +#define NM_IS_SETTING_OVS_PATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OVS_PATCH)) +#define NM_SETTING_OVS_PATCH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OVS_PATCH, NMSettingOvsPatchClass)) + +#define NM_SETTING_OVS_PATCH_SETTING_NAME "ovs-patch" + +#define NM_SETTING_OVS_PATCH_PEER "peer" + +typedef struct _NMSettingOvsPatchClass NMSettingOvsPatchClass; + +NM_AVAILABLE_IN_1_10 +GType nm_setting_ovs_patch_get_type (void); +NM_AVAILABLE_IN_1_10 +NMSetting *nm_setting_ovs_patch_new (void); + +NM_AVAILABLE_IN_1_10 +const char *nm_setting_ovs_patch_get_peer (NMSettingOvsPatch *self); + +G_END_DECLS + +#endif /* __NM_SETTING_OVS_PATCH_H__ */ diff --git a/libnm-core/nm-setting-ovs-port.c b/libnm-core/nm-setting-ovs-port.c new file mode 100644 index 0000000000..bd05f2e7b2 --- /dev/null +++ b/libnm-core/nm-setting-ovs-port.c @@ -0,0 +1,471 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-setting-ovs-port.h" + +#include "nm-connection-private.h" +#include "nm-setting-connection.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-ovs-port + * @short_description: Describes connection properties for OpenVSwitch ports. + * + * The #NMSettingOvsPort object is a #NMSetting subclass that describes properties + * necessary for OpenVSwitch ports. + **/ + +enum { + PROP_0, + PROP_VLAN_MODE, + PROP_TAG, + PROP_LACP, + PROP_BOND_MODE, + PROP_BOND_UPDELAY, + PROP_BOND_DOWNDELAY, + LAST_PROP +}; + +/** + * NMSettingOvsPort: + * + * OvsPort Link Settings + */ +struct _NMSettingOvsPort { + NMSetting parent; + + char *vlan_mode; + guint tag; + char *lacp; + char *bond_mode; + guint bond_updelay; + guint bond_downdelay; +}; + +struct _NMSettingOvsPortClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE_WITH_CODE (NMSettingOvsPort, nm_setting_ovs_port, NM_TYPE_SETTING, + _nm_register_setting (OVS_PORT, NM_SETTING_PRIORITY_HW_BASE)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OVS_PORT) + +/*****************************************************************************/ + +/** + * nm_setting_ovs_port_get_vlan_mode: + * @self: the #NMSettingOvsPort + * + * Returns: the #NMSettingOvsPort:vlan-mode property of the setting + * + * Since: 1.10 + **/ +const char * +nm_setting_ovs_port_get_vlan_mode (NMSettingOvsPort *self) +{ + g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), NULL); + + return self->vlan_mode; +} + +/** + * nm_setting_ovs_port_get_tag: + * @self: the #NMSettingOvsPort + * + * Returns: the #NMSettingOvsPort:tag property of the setting + * + * Since: 1.10 + **/ +guint +nm_setting_ovs_port_get_tag (NMSettingOvsPort *self) +{ + g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), 0); + + return self->tag; +} + +/** + * nm_setting_ovs_port_get_lacp: + * @self: the #NMSettingOvsPort + * + * Returns: the #NMSettingOvsPort:lacp property of the setting + * + * Since: 1.10 + **/ +const char * +nm_setting_ovs_port_get_lacp (NMSettingOvsPort *self) +{ + g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), NULL); + + return self->lacp; +} + +/** + * nm_setting_ovs_port_get_bond_mode: + * @self: the #NMSettingOvsPort + * + * Returns: the #NMSettingOvsPort:bond-mode property of the setting + * + * Since: 1.10 + **/ +const char * +nm_setting_ovs_port_get_bond_mode (NMSettingOvsPort *self) +{ + g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), NULL); + + return self->bond_mode; +} + +/** + * nm_setting_ovs_port_get_bond_updelay: + * @self: the #NMSettingOvsPort + * + * Returns: the #NMSettingOvsPort:bond-updelay property of the setting + * + * Since: 1.10 + **/ +guint +nm_setting_ovs_port_get_bond_updelay (NMSettingOvsPort *self) +{ + g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), 0); + + return self->bond_updelay; +} + +/** + * nm_setting_ovs_port_get_bond_downdelay: + * @self: the #NMSettingOvsPort + * + * Returns: the #NMSettingOvsPort:bond-downdelay property of the setting + * + * Since: 1.10 + **/ +guint +nm_setting_ovs_port_get_bond_downdelay (NMSettingOvsPort *self) +{ + g_return_val_if_fail (NM_IS_SETTING_OVS_PORT (self), 0); + + return self->bond_downdelay; +} + +/*****************************************************************************/ + +static int +verify (NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingOvsPort *self = NM_SETTING_OVS_PORT (setting); + + if (!_nm_connection_verify_required_interface_name (connection, error)) + return FALSE; + + if (connection) { + NMSettingConnection *s_con; + const char *slave_type; + + s_con = nm_connection_get_setting_connection (connection); + if (!s_con) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_SETTING, + _("missing setting")); + g_prefix_error (error, "%s: ", NM_SETTING_CONNECTION_SETTING_NAME); + return FALSE; + } + + if (!nm_setting_connection_get_master (s_con)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("A connection with a '%s' setting must have a master."), + NM_SETTING_OVS_PORT_SETTING_NAME); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER); + return FALSE; + } + + slave_type = nm_setting_connection_get_slave_type (s_con); + if ( slave_type + && strcmp (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("A connection with a '%s' setting must have the slave-type set to '%s'. Instead it is '%s'"), + NM_SETTING_OVS_PORT_SETTING_NAME, + NM_SETTING_OVS_BRIDGE_SETTING_NAME, + slave_type); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE); + return FALSE; + } + } + + if (!NM_IN_STRSET (self->vlan_mode, "access", "native-tagged", "native-untagged", "trunk", NULL)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not allowed in vlan_mode"), + self->vlan_mode); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_OVS_PORT_VLAN_MODE); + return FALSE; + } + + if (self->tag >= 4095) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("the tag id must be in range 0-4094 but is %u"), + self->tag); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_OVS_PORT_TAG); + return FALSE; + } + + if (!NM_IN_STRSET (self->lacp, "active", "off", "passive", NULL)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not allowed in lacp"), + self->lacp); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_OVS_PORT_LACP); + return FALSE; + } + + if (!NM_IN_STRSET (self->bond_mode, "active-backup", "balance-slb", "balance-tcp", NULL)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not allowed in bond_mode"), + self->bond_mode); + g_prefix_error (error, "%s.%s: ", NM_SETTING_OVS_PORT_SETTING_NAME, NM_SETTING_OVS_PORT_BOND_MODE); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingOvsPort *self = NM_SETTING_OVS_PORT (object); + + switch (prop_id) { + case PROP_VLAN_MODE: + g_value_set_string (value, self->vlan_mode); + break; + case PROP_TAG: + g_value_set_uint (value, self->tag); + break; + case PROP_LACP: + g_value_set_string (value, self->lacp); + break; + case PROP_BOND_MODE: + g_value_set_string (value, self->bond_mode); + break; + case PROP_BOND_UPDELAY: + g_value_set_uint (value, self->bond_updelay); + break; + case PROP_BOND_DOWNDELAY: + g_value_set_uint (value, self->bond_downdelay); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingOvsPort *self = NM_SETTING_OVS_PORT (object); + + switch (prop_id) { + case PROP_VLAN_MODE: + g_free (self->vlan_mode); + self->vlan_mode = g_value_dup_string (value); + break; + case PROP_TAG: + self->tag = g_value_get_uint (value); + break; + case PROP_LACP: + g_free (self->lacp); + self->lacp = g_value_dup_string (value); + break; + case PROP_BOND_MODE: + g_free (self->bond_mode); + self->bond_mode = g_value_dup_string (value); + break; + case PROP_BOND_UPDELAY: + self->bond_updelay = g_value_get_uint (value); + break; + case PROP_BOND_DOWNDELAY: + self->bond_downdelay = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_setting_ovs_port_init (NMSettingOvsPort *self) +{ +} + +/** + * nm_setting_ovs_port_new: + * + * Creates a new #NMSettingOvsPort object with default values. + * + * Returns: (transfer full): the new empty #NMSettingOvsPort object + * + * Since: 1.10 + **/ +NMSetting * +nm_setting_ovs_port_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_OVS_PORT, NULL); +} + +static void +finalize (GObject *object) +{ + NMSettingOvsPort *self = NM_SETTING_OVS_PORT (object); + + g_free (self->vlan_mode); + g_free (self->lacp); + g_free (self->bond_mode); + + G_OBJECT_CLASS (nm_setting_ovs_port_parent_class)->finalize (object); +} + +static void +nm_setting_ovs_port_class_init (NMSettingOvsPortClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /** + * NMSettingOvsPort:vlan-mode: + * + * The VLAN mode. One of "access", "native-tagged", "native-untagged", + * "trunk" or unset. + * + * Since: 1.10 + **/ + g_object_class_install_property + (object_class, PROP_VLAN_MODE, + g_param_spec_string (NM_SETTING_OVS_PORT_VLAN_MODE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingOvsPort:tag: + * + * The VLAN tag in the range 0-4095. + * + * Since: 1.10 + **/ + g_object_class_install_property + (object_class, PROP_TAG, + g_param_spec_uint (NM_SETTING_OVS_PORT_TAG, "", "", + 0, 4095, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingOvsPort:lacp: + * + * LACP mode. One of "active", "off", or "passive". + * + * Since: 1.10 + **/ + g_object_class_install_property + (object_class, PROP_LACP, + g_param_spec_string (NM_SETTING_OVS_PORT_LACP, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingOvsPort:bond-mode: + * + * Bonding mode. One of "active-backup", "balance-slb", or "balance-tcp". + * + * Since: 1.10 + **/ + g_object_class_install_property + (object_class, PROP_BOND_MODE, + g_param_spec_string (NM_SETTING_OVS_PORT_BOND_MODE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + + /** + * NMSettingOvsPort:bond-updelay: + * + * The time port must be active befor it starts forwarding traffic. + * + * Since: 1.10 + **/ + g_object_class_install_property + (object_class, PROP_BOND_UPDELAY, + g_param_spec_uint (NM_SETTING_OVS_PORT_BOND_UPDELAY, "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingOvsPort:bond-downdelay: + * + * The time port must be inactive in order to be considered down. + * + * Since: 1.10 + **/ + g_object_class_install_property + (object_class, PROP_BOND_DOWNDELAY, + g_param_spec_uint (NM_SETTING_OVS_PORT_BOND_DOWNDELAY, "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-ovs-port.h b/libnm-core/nm-setting-ovs-port.h new file mode 100644 index 0000000000..4c14f123df --- /dev/null +++ b/libnm-core/nm-setting-ovs-port.h @@ -0,0 +1,69 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_SETTING_OVS_PORT_H__ +#define __NM_SETTING_OVS_PORT_H__ + +#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION) +#error "Only <NetworkManager.h> can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_OVS_PORT (nm_setting_ovs_port_get_type ()) +#define NM_SETTING_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OVS_PORT, NMSettingOvsPort)) +#define NM_SETTING_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OVS_PORTCONFIG, NMSettingOvsPortClass)) +#define NM_IS_SETTING_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OVS_PORT)) +#define NM_IS_SETTING_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OVS_PORT)) +#define NM_SETTING_OVS_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OVS_PORT, NMSettingOvsPortClass)) + +#define NM_SETTING_OVS_PORT_SETTING_NAME "ovs-port" + +#define NM_SETTING_OVS_PORT_VLAN_MODE "vlan-mode" +#define NM_SETTING_OVS_PORT_TAG "tag" +#define NM_SETTING_OVS_PORT_LACP "lacp" +#define NM_SETTING_OVS_PORT_BOND_MODE "bond-mode" +#define NM_SETTING_OVS_PORT_BOND_UPDELAY "bond-updelay" +#define NM_SETTING_OVS_PORT_BOND_DOWNDELAY "bond-downdelay" + +typedef struct _NMSettingOvsPortClass NMSettingOvsPortClass; + +NM_AVAILABLE_IN_1_10 +GType nm_setting_ovs_port_get_type (void); +NM_AVAILABLE_IN_1_10 +NMSetting *nm_setting_ovs_port_new (void); + +NM_AVAILABLE_IN_1_10 +const char *nm_setting_ovs_port_get_vlan_mode (NMSettingOvsPort *self); +NM_AVAILABLE_IN_1_10 +guint nm_setting_ovs_port_get_tag (NMSettingOvsPort *self); +NM_AVAILABLE_IN_1_10 +const char *nm_setting_ovs_port_get_lacp (NMSettingOvsPort *self); +NM_AVAILABLE_IN_1_10 +const char *nm_setting_ovs_port_get_bond_mode (NMSettingOvsPort *self); +NM_AVAILABLE_IN_1_10 +guint nm_setting_ovs_port_get_bond_updelay (NMSettingOvsPort *self); +NM_AVAILABLE_IN_1_10 +guint nm_setting_ovs_port_get_bond_downdelay (NMSettingOvsPort *self); + +G_END_DECLS + +#endif /* __NM_SETTING_OVS_PORT_H__ */ diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c index 2f282db6aa..9c8e53aeac 100644 --- a/libnm-core/nm-setting.c +++ b/libnm-core/nm-setting.c @@ -219,7 +219,7 @@ _nm_setting_get_base_type_priority (NMSetting *setting) GType nm_setting_lookup_type (const char *name) { - SettingInfo *info; + const SettingInfo *info; g_return_val_if_fail (name, G_TYPE_INVALID); @@ -258,6 +258,10 @@ _nm_setting_slave_type_is_valid (const char *slave_type, const char **out_port_t ; else if (!strcmp (slave_type, NM_SETTING_BRIDGE_SETTING_NAME)) port_type = NM_SETTING_BRIDGE_PORT_SETTING_NAME; + else if (!strcmp (slave_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) + port_type = NM_SETTING_OVS_PORT_SETTING_NAME; + else if (!strcmp (slave_type, NM_SETTING_OVS_PORT_SETTING_NAME)) + port_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME; else if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME)) port_type = NM_SETTING_TEAM_PORT_SETTING_NAME; else diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 5f4540c57a..0dc7616d6c 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -4655,6 +4655,344 @@ test_connection_normalize_shared_addresses (void) } static void +test_connection_normalize_ovs_interface_type_system (gconstpointer test_data) +{ + const guint TEST_CASE = GPOINTER_TO_UINT (test_data); + gs_unref_object NMConnection *con = NULL; + NMSettingConnection *s_con; + NMSettingOvsInterface *s_ovs_if; + + con = nmtst_create_minimal_connection ("test_connection_normalize_ovs_interface_type_system", + NULL, + NM_SETTING_WIRED_SETTING_NAME, &s_con); + + switch (TEST_CASE) { + case 1: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NULL); + + nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING); + + nmtst_connection_normalize (con); + nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME); + s_ovs_if = nm_connection_get_setting_ovs_interface (con); + g_assert (s_ovs_if); + g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "system"); + break; + case 2: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NULL); + + s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ()); + nm_connection_add_setting (con, NM_SETTING (s_ovs_if)); + + nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY); + + nmtst_connection_normalize (con); + nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME); + g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con)); + g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "system"); + break; + case 3: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NULL); + + s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ()); + nm_connection_add_setting (con, NM_SETTING (s_ovs_if)); + + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "system", + NULL); + nmtst_assert_connection_verifies_without_normalization (con); + nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME); + break; + case 4: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NULL); + + s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ()); + nm_connection_add_setting (con, NM_SETTING (s_ovs_if)); + + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "internal", + NULL); + /* the setting doesn't verify, because the interface-type must be "system". */ + nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + break; + case 5: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NULL); + + s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ()); + nm_connection_add_setting (con, NM_SETTING (s_ovs_if)); + + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "system", + NULL); + nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY); + nmtst_connection_normalize (con); + nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME); + g_assert (s_con == nm_connection_get_setting_connection (con)); + g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME); + break; + case 6: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, + NULL); + + s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ()); + nm_connection_add_setting (con, NM_SETTING (s_ovs_if)); + + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "system", + NULL); + nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + break; + case 7: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, + NULL); + + nm_connection_add_setting (con, nm_setting_bridge_port_new ()); + + s_ovs_if = NM_SETTING_OVS_INTERFACE (nm_setting_ovs_interface_new ()); + nm_connection_add_setting (con, NM_SETTING (s_ovs_if)); + + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "system", + NULL); + nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +test_connection_normalize_ovs_interface_type_ovs_interface (gconstpointer test_data) +{ + const guint TEST_CASE = GPOINTER_TO_UINT (test_data); + gs_unref_object NMConnection *con = NULL; + NMSettingConnection *s_con; + NMSettingOvsInterface *s_ovs_if; + NMSettingOvsPatch *s_ovs_patch; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + + con = nmtst_create_minimal_connection ("test_connection_normalize_ovs_interface_type_ovs_interface", + NULL, + NM_SETTING_OVS_INTERFACE_SETTING_NAME, &s_con); + s_ovs_if = nm_connection_get_setting_ovs_interface (con); + g_assert (s_ovs_if); + + switch (TEST_CASE) { + case 1: + nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + break; + case 2: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NULL); + nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY); + nmtst_connection_normalize (con); + nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_PROXY_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME); + g_assert (s_con == nm_connection_get_setting_connection (con)); + g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con)); + g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME); + g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "internal"); + break; + case 3: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NULL); + nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY); + nmtst_connection_normalize (con); + nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_PROXY_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME); + g_assert (s_con == nm_connection_get_setting_connection (con)); + g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con)); + g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME); + g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "internal"); + break; + case 4: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NULL); + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "internal", + NULL); + nmtst_assert_connection_verifies_after_normalization (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY); + nmtst_connection_normalize (con); + nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_PROXY_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME); + g_assert (s_con == nm_connection_get_setting_connection (con)); + g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con)); + g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME); + g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "internal"); + break; + case 5: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NULL); + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "internal", + NULL); + nm_connection_add_setting (con, nm_setting_ip4_config_new ()); + nm_connection_add_setting (con, nm_setting_ip6_config_new ()); + nm_connection_add_setting (con, nm_setting_proxy_new ()); + s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting_ip4_config (con)); + s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (con)); + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, "auto", + NULL); + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, "auto", + NULL); + nmtst_assert_connection_verifies_without_normalization (con); + nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_PROXY_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME); + break; + case 6: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NULL); + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "internal", + NULL); + nmtst_assert_connection_verifies_and_normalizable (con); + nmtst_connection_normalize (con); + nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_PROXY_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME); + g_assert (s_con == nm_connection_get_setting_connection (con)); + g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con)); + g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME); + g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "internal"); + break; + case 7: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NULL); + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "system", + NULL); + nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + break; + case 8: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NULL); + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "bogus", + NULL); + nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + break; + case 9: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NULL); + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "patch", + NULL); + nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING); + break; + case 10: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NULL); + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "patch", + NULL); + nm_connection_add_setting (con, nm_setting_ovs_patch_new ()); + nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY); + break; + case 11: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NM_SETTING_CONNECTION_INTERFACE_NAME, "adsf", + NULL); + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "patch", + NULL); + nm_connection_add_setting (con, nm_setting_ovs_patch_new ()); + nmtst_assert_connection_unnormalizable (con, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY); + break; + case 12: + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "master0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_OVS_PORT_SETTING_NAME, + NM_SETTING_CONNECTION_INTERFACE_NAME, "adsf", + NULL); + g_object_set (s_ovs_if, + NM_SETTING_OVS_INTERFACE_TYPE, "patch", + NULL); + s_ovs_patch = NM_SETTING_OVS_PATCH (nm_setting_ovs_patch_new ()); + nm_connection_add_setting (con, NM_SETTING (s_ovs_patch)); + g_object_set (s_ovs_patch, + NM_SETTING_OVS_PATCH_PEER, "1.2.3.4", + NULL); + nmtst_assert_connection_verifies_and_normalizable (con); + nmtst_connection_normalize (con); + nmtst_assert_connection_has_settings (con, NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_PROXY_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME, + NM_SETTING_OVS_PATCH_SETTING_NAME); + g_assert (s_con == nm_connection_get_setting_connection (con)); + g_assert (s_ovs_if == nm_connection_get_setting_ovs_interface (con)); + g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_OVS_PORT_SETTING_NAME); + g_assert_cmpstr (nm_setting_ovs_interface_get_interface_type (s_ovs_if), ==, "patch"); + break; + default: + g_assert_not_reached (); + } +} + +static void test_setting_ip4_gateway (void) { NMConnection *conn; @@ -6540,6 +6878,25 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/test_connection_normalize_gateway_never_default", test_connection_normalize_gateway_never_default); g_test_add_func ("/core/general/test_connection_normalize_may_fail", test_connection_normalize_may_fail); g_test_add_func ("/core/general/test_connection_normalize_shared_addresses", test_connection_normalize_shared_addresses); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/1", GUINT_TO_POINTER (1), test_connection_normalize_ovs_interface_type_system); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/2", GUINT_TO_POINTER (2), test_connection_normalize_ovs_interface_type_system); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/3", GUINT_TO_POINTER (3), test_connection_normalize_ovs_interface_type_system); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/4", GUINT_TO_POINTER (4), test_connection_normalize_ovs_interface_type_system); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/5", GUINT_TO_POINTER (5), test_connection_normalize_ovs_interface_type_system); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/6", GUINT_TO_POINTER (6), test_connection_normalize_ovs_interface_type_system); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_system/7", GUINT_TO_POINTER (7), test_connection_normalize_ovs_interface_type_system); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/1", GUINT_TO_POINTER (1), test_connection_normalize_ovs_interface_type_ovs_interface); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/2", GUINT_TO_POINTER (2), test_connection_normalize_ovs_interface_type_ovs_interface); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/3", GUINT_TO_POINTER (3), test_connection_normalize_ovs_interface_type_ovs_interface); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/4", GUINT_TO_POINTER (4), test_connection_normalize_ovs_interface_type_ovs_interface); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/5", GUINT_TO_POINTER (5), test_connection_normalize_ovs_interface_type_ovs_interface); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/6", GUINT_TO_POINTER (6), test_connection_normalize_ovs_interface_type_ovs_interface); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/7", GUINT_TO_POINTER (7), test_connection_normalize_ovs_interface_type_ovs_interface); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/8", GUINT_TO_POINTER (8), test_connection_normalize_ovs_interface_type_ovs_interface); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/9", GUINT_TO_POINTER (9), test_connection_normalize_ovs_interface_type_ovs_interface); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/10", GUINT_TO_POINTER (10), test_connection_normalize_ovs_interface_type_ovs_interface); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/11", GUINT_TO_POINTER (11), test_connection_normalize_ovs_interface_type_ovs_interface); + g_test_add_data_func ("/core/general/test_connection_normalize_ovs_interface_type_ovs_interface/12", GUINT_TO_POINTER (12), test_connection_normalize_ovs_interface_type_ovs_interface); g_test_add_func ("/core/general/test_setting_connection_permissions_helpers", test_setting_connection_permissions_helpers); g_test_add_func ("/core/general/test_setting_connection_permissions_property", test_setting_connection_permissions_property); diff --git a/libnm/NetworkManager.h b/libnm/NetworkManager.h index 97327f92d6..27737e0e1b 100644 --- a/libnm/NetworkManager.h +++ b/libnm/NetworkManager.h @@ -41,6 +41,9 @@ #include "nm-device-macvlan.h" #include "nm-device-modem.h" #include "nm-device-olpc-mesh.h" +#include "nm-device-ovs-interface.h" +#include "nm-device-ovs-port.h" +#include "nm-device-ovs-bridge.h" #include "nm-device-team.h" #include "nm-device-tun.h" #include "nm-device-vlan.h" @@ -73,6 +76,10 @@ #include "nm-setting-macsec.h" #include "nm-setting-macvlan.h" #include "nm-setting-olpc-mesh.h" +#include "nm-setting-ovs-bridge.h" +#include "nm-setting-ovs-interface.h" +#include "nm-setting-ovs-patch.h" +#include "nm-setting-ovs-port.h" #include "nm-setting-ppp.h" #include "nm-setting-pppoe.h" #include "nm-setting-proxy.h" diff --git a/libnm/generate-setting-docs.py b/libnm/generate-setting-docs.py index 9a8df4bfd1..aa0c96ebb2 100755 --- a/libnm/generate-setting-docs.py +++ b/libnm/generate-setting-docs.py @@ -257,6 +257,9 @@ for settingxml in settings: prop_upper = prop.upper().replace('-', '_') + if value_desc is None: + raise Exception("%s.%s needs a documentation description" % (setting.props.name, prop)) + if default_value is not None: outfile.write(" <property name=\"%s\" name_upper=\"%s\" type=\"%s\" default=\"%s\" description=\"%s\" />\n" % (prop, prop_upper, value_type, escape(default_value), escape(value_desc))) diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 0fd28beecd..1b245b277b 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1185,11 +1185,35 @@ global: nm_client_connectivity_check_get_available; nm_client_connectivity_check_get_enabled; nm_client_connectivity_check_set_enabled; + nm_connection_get_settings; nm_device_dummy_get_hw_address; + nm_device_ovs_bridge_get_type; + nm_device_ovs_interface_get_type; + nm_device_ovs_port_get_type; nm_device_ppp_get_type; nm_ip_route_equal_full; nm_setting_bridge_get_group_forward_mask; nm_setting_ip_config_get_route_table; + nm_setting_ovs_bridge_get_fail_mode; + nm_setting_ovs_bridge_get_mcast_snooping_enable; + nm_setting_ovs_bridge_get_rstp_enable; + nm_setting_ovs_bridge_get_stp_enable; + nm_setting_ovs_bridge_get_type; + nm_setting_ovs_bridge_new; + nm_setting_ovs_interface_get_interface_type; + nm_setting_ovs_interface_get_type; + nm_setting_ovs_interface_new; + nm_setting_ovs_patch_get_peer; + nm_setting_ovs_patch_get_type; + nm_setting_ovs_patch_new; + nm_setting_ovs_port_get_bond_downdelay; + nm_setting_ovs_port_get_bond_mode; + nm_setting_ovs_port_get_bond_updelay; + nm_setting_ovs_port_get_lacp; + nm_setting_ovs_port_get_tag; + nm_setting_ovs_port_get_type; + nm_setting_ovs_port_get_vlan_mode; + nm_setting_ovs_port_new; nm_setting_pppoe_get_parent; nm_setting_wireless_security_get_pmf; nm_setting_wireless_security_get_wps_method; diff --git a/libnm/nm-client.c b/libnm/nm-client.c index 8912a5f419..8c7e293147 100644 --- a/libnm/nm-client.c +++ b/libnm/nm-client.c @@ -63,6 +63,9 @@ #include "nm-device-macvlan.h" #include "nm-device-modem.h" #include "nm-device-olpc-mesh.h" +#include "nm-device-ovs-interface.h" +#include "nm-device-ovs-port.h" +#include "nm-device-ovs-bridge.h" #include "nm-device-ppp.h" #include "nm-device-team.h" #include "nm-device-tun.h" @@ -2147,6 +2150,12 @@ obj_nm_for_gdbus_object (NMClient *self, GDBusObject *object, GDBusObjectManager type = NM_TYPE_DEVICE_MODEM; else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_OLPC_MESH) == 0) type = NM_TYPE_DEVICE_OLPC_MESH; + else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE) == 0) + type = NM_TYPE_DEVICE_OVS_INTERFACE; + else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_OVS_PORT) == 0) + type = NM_TYPE_DEVICE_OVS_PORT; + else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE) == 0) + type = NM_TYPE_DEVICE_OVS_BRIDGE; else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_PPP) == 0) type = NM_TYPE_DEVICE_PPP; else if (strcmp (ifname, NM_DBUS_INTERFACE_DEVICE_TEAM) == 0) diff --git a/libnm/nm-device-ovs-bridge.c b/libnm/nm-device-ovs-bridge.c new file mode 100644 index 0000000000..798f6d20de --- /dev/null +++ b/libnm/nm-device-ovs-bridge.c @@ -0,0 +1,96 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include <string.h> + +#include "nm-device-ovs-bridge.h" +#include "nm-object-private.h" +#include "nm-setting-ovs-bridge.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-connection.h" + +/** + * NMDeviceOvsBridge: + */ +struct _NMDeviceOvsBridge { + NMDevice parent; +}; + +struct _NMDeviceOvsBridgeClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE (NMDeviceOvsBridge, nm_device_ovs_bridge, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + return "ovs-bridge"; +} + +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS (nm_device_ovs_bridge_parent_class)->connection_compatible (device, connection, error)) + return FALSE; + + if (!nm_connection_is_type (connection, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) { + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a ovs_bridge connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name (connection); + if (!iface_name) { + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_OVS_BRIDGE; +} + +/*****************************************************************************/ + +static void +nm_device_ovs_bridge_init (NMDeviceOvsBridge *device) +{ +} + +static void +nm_device_ovs_bridge_class_init (NMDeviceOvsBridgeClass *ovs_bridge_class) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS (ovs_bridge_class); + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; +} diff --git a/libnm/nm-device-ovs-bridge.h b/libnm/nm-device-ovs-bridge.h new file mode 100644 index 0000000000..b4b0b98b00 --- /dev/null +++ b/libnm/nm-device-ovs-bridge.h @@ -0,0 +1,45 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_DEVICE_OVS_BRIDGE_H__ +#define __NM_DEVICE_OVS_BRIDGE_H__ + +#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION) +#error "Only <NetworkManager.h> can be included directly." +#endif + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_OVS_BRIDGE (nm_device_ovs_bridge_get_type ()) +#define NM_DEVICE_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridge)) +#define NM_DEVICE_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridgeClass)) +#define NM_IS_DEVICE_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_BRIDGE)) +#define NM_IS_DEVICE_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_BRIDGE)) +#define NM_DEVICE_OVS_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridgeClass)) + +typedef struct _NMDeviceOvsBridgeClass NMDeviceOvsBridgeClass; + +NM_AVAILABLE_IN_1_10 +GType nm_device_ovs_bridge_get_type (void); + +G_END_DECLS + +#endif /* __NM_DEVICE_OVS_BRIDGE_H__ */ diff --git a/libnm/nm-device-ovs-interface.c b/libnm/nm-device-ovs-interface.c new file mode 100644 index 0000000000..ed3dbd4ca4 --- /dev/null +++ b/libnm/nm-device-ovs-interface.c @@ -0,0 +1,96 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include <string.h> + +#include "nm-device-ovs-interface.h" +#include "nm-object-private.h" +#include "nm-setting-ovs-interface.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-connection.h" + +/** + * NMDeviceOvsInterface: + */ +struct _NMDeviceOvsInterface { + NMDevice parent; +}; + +struct _NMDeviceOvsInterfaceClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE (NMDeviceOvsInterface, nm_device_ovs_interface, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + return "ovs-interface"; +} + +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->connection_compatible (device, connection, error)) + return FALSE; + + if (!nm_connection_is_type (connection, NM_SETTING_OVS_INTERFACE_SETTING_NAME)) { + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a ovs_interface connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name (connection); + if (!iface_name) { + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_OVS_INTERFACE; +} + +/*****************************************************************************/ + +static void +nm_device_ovs_interface_init (NMDeviceOvsInterface *device) +{ +} + +static void +nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *ovs_interface_class) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS (ovs_interface_class); + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; +} diff --git a/libnm/nm-device-ovs-interface.h b/libnm/nm-device-ovs-interface.h new file mode 100644 index 0000000000..a49db559ae --- /dev/null +++ b/libnm/nm-device-ovs-interface.h @@ -0,0 +1,45 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_DEVICE_OVS_INTERFACE_H__ +#define __NM_DEVICE_OVS_INTERFACE_H__ + +#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION) +#error "Only <NetworkManager.h> can be included directly." +#endif + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_OVS_INTERFACE (nm_device_ovs_interface_get_type ()) +#define NM_DEVICE_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterface)) +#define NM_DEVICE_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterfaceClass)) +#define NM_IS_DEVICE_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_INTERFACE)) +#define NM_IS_DEVICE_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_INTERFACE)) +#define NM_DEVICE_OVS_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterfaceClass)) + +typedef struct _NMDeviceOvsInterfaceClass NMDeviceOvsInterfaceClass; + +NM_AVAILABLE_IN_1_10 +GType nm_device_ovs_interface_get_type (void); + +G_END_DECLS + +#endif /* __NM_DEVICE_OVS_INTERFACE_H__ */ diff --git a/libnm/nm-device-ovs-port.c b/libnm/nm-device-ovs-port.c new file mode 100644 index 0000000000..17176dba3c --- /dev/null +++ b/libnm/nm-device-ovs-port.c @@ -0,0 +1,95 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include <string.h> + +#include "nm-device-ovs-port.h" +#include "nm-object-private.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-connection.h" + +/** + * NMDeviceOvsPort: + */ +struct _NMDeviceOvsPort { + NMDevice parent; +}; + +struct _NMDeviceOvsPortClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE (NMDeviceOvsPort, nm_device_ovs_port, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + return "ovs-port"; +} +static gboolean +connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + const char *iface_name; + + if (!NM_DEVICE_CLASS (nm_device_ovs_port_parent_class)->connection_compatible (device, connection, error)) + return FALSE; + + if (!nm_connection_is_type (connection, NM_SETTING_OVS_PORT_SETTING_NAME)) { + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + _("The connection was not a ovs_port connection.")); + return FALSE; + } + + iface_name = nm_connection_get_interface_name (connection); + if (!iface_name) { + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, + _("The connection did not specify an interface name.")); + return FALSE; + } + + return TRUE; +} + +static GType +get_setting_type (NMDevice *device) +{ + return NM_TYPE_SETTING_OVS_PORT; +} + +/*****************************************************************************/ + +static void +nm_device_ovs_port_init (NMDeviceOvsPort *device) +{ +} + +static void +nm_device_ovs_port_class_init (NMDeviceOvsPortClass *ovs_port_class) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS (ovs_port_class); + + device_class->get_type_description = get_type_description; + device_class->connection_compatible = connection_compatible; + device_class->get_setting_type = get_setting_type; +} diff --git a/libnm/nm-device-ovs-port.h b/libnm/nm-device-ovs-port.h new file mode 100644 index 0000000000..af661e7b82 --- /dev/null +++ b/libnm/nm-device-ovs-port.h @@ -0,0 +1,45 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_DEVICE_OVS_PORT_H__ +#define __NM_DEVICE_OVS_PORT_H__ + +#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION) +#error "Only <NetworkManager.h> can be included directly." +#endif + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_OVS_PORT (nm_device_ovs_port_get_type ()) +#define NM_DEVICE_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPort)) +#define NM_DEVICE_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPortClass)) +#define NM_IS_DEVICE_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_PORT)) +#define NM_IS_DEVICE_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_PORT)) +#define NM_DEVICE_OVS_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPortClass)) + +typedef struct _NMDeviceOvsPortClass NMDeviceOvsPortClass; + +NM_AVAILABLE_IN_1_10 +GType nm_device_ovs_port_get_type (void); + +G_END_DECLS + +#endif /* __NM_DEVICE_OVS_PORT_H__ */ diff --git a/libnm/nm-device.c b/libnm/nm-device.c index 7f15436638..65afd078dc 100644 --- a/libnm/nm-device.c +++ b/libnm/nm-device.c @@ -261,6 +261,9 @@ coerce_type (NMDeviceType type) case NM_DEVICE_TYPE_WIFI: case NM_DEVICE_TYPE_BT: case NM_DEVICE_TYPE_OLPC_MESH: + case NM_DEVICE_TYPE_OVS_INTERFACE: + case NM_DEVICE_TYPE_OVS_PORT: + case NM_DEVICE_TYPE_OVS_BRIDGE: case NM_DEVICE_TYPE_WIMAX: case NM_DEVICE_TYPE_MODEM: case NM_DEVICE_TYPE_INFINIBAND: @@ -1522,6 +1525,12 @@ get_type_name (NMDevice *device) return _("Bluetooth"); case NM_DEVICE_TYPE_OLPC_MESH: return _("OLPC Mesh"); + case NM_DEVICE_TYPE_OVS_INTERFACE: + return _("OpenVSwitch Interface"); + case NM_DEVICE_TYPE_OVS_PORT: + return _("OpenVSwitch Port"); + case NM_DEVICE_TYPE_OVS_BRIDGE: + return _("OpenVSwitch Bridge"); case NM_DEVICE_TYPE_WIMAX: return _("WiMAX"); case NM_DEVICE_TYPE_MODEM: diff --git a/libnm/nm-types.h b/libnm/nm-types.h index 1e06d15feb..4310120fec 100644 --- a/libnm/nm-types.h +++ b/libnm/nm-types.h @@ -26,35 +26,38 @@ #include "nm-dbus-interface.h" #include "nm-connection.h" -typedef struct _NMAccessPoint NMAccessPoint; -typedef struct _NMActiveConnection NMActiveConnection; -typedef struct _NMClient NMClient; -typedef struct _NMDevice NMDevice; -typedef struct _NMDeviceAdsl NMDeviceAdsl; -typedef struct _NMDeviceBond NMDeviceBond; -typedef struct _NMDeviceBridge NMDeviceBridge; -typedef struct _NMDeviceBt NMDeviceBt; -typedef struct _NMDeviceDummy NMDeviceDummy; -typedef struct _NMDeviceEthernet NMDeviceEthernet; -typedef struct _NMDeviceGeneric NMDeviceGeneric; -typedef struct _NMDeviceInfiniband NMDeviceInfiniband; -typedef struct _NMDeviceIPTunnel NMDeviceIPTunnel; -typedef struct _NMDeviceMacsec NMDeviceMacsec; -typedef struct _NMDeviceMacvlan NMDeviceMacvlan; -typedef struct _NMDeviceModem NMDeviceModem; -typedef struct _NMDeviceOlpcMesh NMDeviceOlpcMesh; -typedef struct _NMDevicePpp NMDevicePpp; -typedef struct _NMDeviceTeam NMDeviceTeam; -typedef struct _NMDeviceTun NMDeviceTun; -typedef struct _NMDeviceVlan NMDeviceVlan; -typedef struct _NMDeviceVxlan NMDeviceVxlan; -typedef struct _NMDeviceWifi NMDeviceWifi; -typedef struct _NMDeviceWimax NMDeviceWimax; -typedef struct _NMDhcpConfig NMDhcpConfig; -typedef struct _NMIPConfig NMIPConfig; -typedef struct _NMObject NMObject; -typedef struct _NMRemoteConnection NMRemoteConnection; -typedef struct _NMVpnConnection NMVpnConnection; -typedef struct _NMWimaxNsp NMWimaxNsp; +typedef struct _NMAccessPoint NMAccessPoint; +typedef struct _NMActiveConnection NMActiveConnection; +typedef struct _NMClient NMClient; +typedef struct _NMDevice NMDevice; +typedef struct _NMDeviceAdsl NMDeviceAdsl; +typedef struct _NMDeviceBond NMDeviceBond; +typedef struct _NMDeviceBridge NMDeviceBridge; +typedef struct _NMDeviceBt NMDeviceBt; +typedef struct _NMDeviceDummy NMDeviceDummy; +typedef struct _NMDeviceEthernet NMDeviceEthernet; +typedef struct _NMDeviceGeneric NMDeviceGeneric; +typedef struct _NMDeviceInfiniband NMDeviceInfiniband; +typedef struct _NMDeviceIPTunnel NMDeviceIPTunnel; +typedef struct _NMDeviceMacsec NMDeviceMacsec; +typedef struct _NMDeviceMacvlan NMDeviceMacvlan; +typedef struct _NMDeviceModem NMDeviceModem; +typedef struct _NMDeviceOlpcMesh NMDeviceOlpcMesh; +typedef struct _NMDeviceOvsBridge NMDeviceOvsBridge; +typedef struct _NMDeviceOvsInterface NMDeviceOvsInterface; +typedef struct _NMDeviceOvsPort NMDeviceOvsPort; +typedef struct _NMDevicePpp NMDevicePpp; +typedef struct _NMDeviceTeam NMDeviceTeam; +typedef struct _NMDeviceTun NMDeviceTun; +typedef struct _NMDeviceVlan NMDeviceVlan; +typedef struct _NMDeviceVxlan NMDeviceVxlan; +typedef struct _NMDeviceWifi NMDeviceWifi; +typedef struct _NMDeviceWimax NMDeviceWimax; +typedef struct _NMDhcpConfig NMDhcpConfig; +typedef struct _NMIPConfig NMIPConfig; +typedef struct _NMObject NMObject; +typedef struct _NMRemoteConnection NMRemoteConnection; +typedef struct _NMVpnConnection NMVpnConnection; +typedef struct _NMWimaxNsp NMWimaxNsp; #endif /* NM_TYPES_H */ diff --git a/man/nm-openvswitch.xml b/man/nm-openvswitch.xml new file mode 100644 index 0000000000..5573046c38 --- /dev/null +++ b/man/nm-openvswitch.xml @@ -0,0 +1,204 @@ +<?xml version='1.0'?> +<?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" +"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [ +<!ENTITY % entities SYSTEM "common.ent" > +%entities; +]> + +<!-- + nmcli-examples(7) manual page + + Copyright 2017 Red Hat, Inc. + + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.1 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. You may obtain a copy of the GNU Free Documentation License + from the Free Software Foundation by visiting their Web site or by + writing to: + + Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +--> + +<refentry id="nm-openvswitch"> + <refentryinfo> + <title>nm-openvswitch</title> + <author>NetworkManager OpenVSwitch support</author> + </refentryinfo> + + <refmeta> + <refentrytitle>nm-openvswitch</refentrytitle> + <manvolnum>7</manvolnum> + <refmiscinfo class="source">NetworkManager</refmiscinfo> + <refmiscinfo class="manual">OpenVSwitch support overview</refmiscinfo> + <refmiscinfo class="version">&NM_VERSION;</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>nm-openvswitch</refname> + <refpurpose>overview of NetworkManager OpenVSwitch support</refpurpose> + </refnamediv> + + <refsect1> + <title>Overview</title> + + <para>NetworkManager includes basic OpenVSwitch support, good enough + to be capable of setting up simple OpenVSwitch configurations. It is not + extensive and does not expose all functionality of OpenVSwitch provides. + For large or complicated deployments users are advised to use native tools + shipped with OpenVSwitch. This document seeks to provide overview of + functionality currently provided by NetworkManager, its capabilities and + limitations.</para> + + <para>First and foremost: NetworkManager applies the configuration by + modifying the OVSDB directly. Its configuration model follows the OVSDB + database model closely and it does not provide the level of abstraction + <command>ovs-vsctl</command> provides.</para> + + <para>In practical terms it means the following: + <itemizedlist> + <listitem> + <para>NetworkManager only ever talks to a single OVSDB instance via an + UNIX domain socket.</para> + </listitem> + <listitem> + <para>The configuration is made up of Bridges, Ports and + Interfaces. Interfaces are always enslaved to Ports, and Ports are always + enslaved to Bridges.</para> + </listitem> + <listitem> + <para>NetworkManager only creates Bridges, Ports and Interfaces + you ask it to. Unlike <command>ovs-vsctl</command>, it doesn't create the + local interface nor its port automatically.</para> + </listitem> + <listitem> + <para>You can't enslave Interface directly to a Bridge. You + always need a Port, even if it has just one interface.</para> + </listitem> + <listitem> + <para>There are no VLANs. The VLAN tagging is enabled by setting a + <link linkend="nm-settings.property.ovs-port.tag">ovs-port.tag</link> + property on a Port.</para> + </listitem> + <listitem> + <para>There are no bonds either. The bonding is enabled by + enslaving multiple Interfaces to a Port and configured by setting + properties on a port.</para> + </listitem> + </itemizedlist> + </para> + + <refsect2> + <title>Bridges</title> + + <para>Bridges are represented by connections of ovs-bridge + <link linkend="nm-settings.property.connection.type">type</link>. + Due to the limitations of OVSDB, "empty" Bridges (with no Ports) can't exist. + NetworkManager inserts the records for Bridges into OVSDB when a Port is + enslaved. + </para> + </refsect2> + + <refsect2> + <title>Ports</title> + + <para>Ports are represented by connections of ovs-port + <link linkend="nm-settings.property.connection.type">type</link>. + Due to the limitations of OVSDB, "empty" Ports (with no Interfaces) can't + exist. Ports can also be configured to do VLAN tagging or Bonding. + NetworkManager inserts the records for Ports into OVSDB when an Interface is + enslaved. Ports must be enslaved to a Bridge.</para> + </refsect2> + + <refsect2> + <title>Interfaces</title> + + <para>Interfaces are represented by a connections enslaved to a Port. The + system interfaces (that have a corresponding Linux link) have a respective + <link linkend="nm-settings.property.connection.type">connection.type</link> + of the link (e.g. "wired", "bond", "dummy", etc.). Other interfaces ("internal" + or "patch" interfaces) are of ovs-interface type. The OVSDB entries are + inserted upon enslavement to a Port.</para> + </refsect2> + </refsect1> + + + <refsect1> + <title>Examples</title> + + <example><title>Creating a Bridge with a single internal Interface</title> +<screen><prompt>$ </prompt><userinput>nmcli conn add conn.type ovs-bridge conn.interface bridge0</userinput> +Connection 'ovs-bridge-bridge0' (d10fc64d-1d48-4394-a1b8-e1aea72f27d5) successfully added. +<prompt>$ </prompt><userinput>nmcli conn add conn.type ovs-port conn.interface port0 conn.master bridge0</userinput> +Connection 'ovs-port-port0' (5ae22bae-bba4-4815-9ade-7e635633e1f0) successfully added. +<prompt>$ </prompt><userinput>nmcli conn add conn.type ovs-interface conn.interface iface0 conn.master port0 \ + ipv4.method manual ipv4.address 192.0.2.1/24</userinput> +Connection 'ovs-interface-iface0' (3640d2a1-a2fd-4718-92f1-cffadb5b6cdc) successfully added. +</screen> + <para>As said above, you need to create a Port even for a single interface. + Also, before you add the Interface, the Bridge and Port devices appear active, + but are not configured in OVSDB yet. You can inspect the results with + <command>ovs-vsctl show</command>.</para> + </example> + + <example><title>Adding a Linux interface to a Bridge</title> +<screen><prompt>$ </prompt><userinput>nmcli conn add conn.type ovs-port conn.interface port1 conn.master bridge0</userinput> +Connection 'ovs-port-port1' (67d041eb-8e7b-4458-afee-a1d07c9c4552) successfully added. +<prompt>$ </prompt><userinput>nmcli conn add conn.type ethernet conn.interface eth0 conn.master port1</userinput> +Connection 'ovs-slave-eth0' (d459c45c-cf78-4c1c-b4b7-505e71379624) successfully added. +</screen> +<para>Again, you need a port.</para> + </example> + + <example><title>Creating a VLAN</title> +<screen><prompt>$ </prompt><userinput>nmcli conn add conn.type ovs-port conn.interface port2 conn.master bridge0 ovs-port.tag 120</userinput> +Connection 'ovs-port-port2' (3994c093-4ef7-4549-a4fd-627b831c3cb8) successfully added. +<prompt>$ </prompt><userinput>nmcli conn add conn.type ethernet conn.interface eth1 conn.master port2</userinput> +Connection 'ovs-slave-eth1' (099be06e-71ad-484d-8d5a-fcadc5f207f5) successfully added. +</screen> + <para>It's just a port with a tag.</para> + </example> + + <example><title>Creating a Bond</title> +<screen><prompt>$ </prompt><userinput>nmcli conn add conn.type ovs-port conn.interface bond0 conn.master bridge0</userinput> +Connection 'ovs-port-bond0' (d154ebf9-e999-4e1b-a084-a3de53d25d8a) successfully added. +<prompt>$ </prompt><userinput>nmcli conn add conn.type ethernet conn.interface eth2 conn.master bond0</userinput> +Connection 'ovs-slave-eth2' (475ac1bf-30b2-4534-a877-27f33f58b082) successfully added. +<prompt>$ </prompt><userinput>nmcli conn add conn.type ethernet conn.interface eth3 conn.master bond0</userinput> +Connection 'ovs-slave-eth3' (8dedeecb-ed12-482b-b77a-24a4fb835136) successfully added. +</screen> + <para>It's just a Port with multiple interfaces. See nm-settings manual for + Bonding options you can use with "nmcli c add" or "nmcli c modify". You could + even set a VLAN tag on the same Port to do VLAN tagging and bonding at the same + time.</para> + </example> + </refsect1> + + <refsect1> + <title>Bugs</title> + + <itemizedlist> + <listitem> + <para>Not all OpenVSwitch capabilities are supported.</para> + </listitem> + <listitem> + <para>OpenVSwitch devices don't expose many useful properties on D-Bus.</para> + </listitem> + </itemizedlist> + <para>Probably many more.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <ulink url="https://www.rfc-editor.org/rfc/rfc7047.txt">RFC 7047: The Open vSwitch Database Management Protocol</ulink>, + <citerefentry><refentrytitle>ovs-vsctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>, + <citerefentry><refentrytitle>ovs-vswitchd.conf.db</refentrytitle><manvolnum>5</manvolnum></citerefentry>, + <link linkend='nm-settings'><citerefentry><refentrytitle>nm-settings</refentrytitle><manvolnum>5</manvolnum></citerefentry></link>, + <link linkend='nmcli'><citerefentry><refentrytitle>nmcli</refentrytitle><manvolnum>1</manvolnum></citerefentry></link> + </para> + </refsect1> +</refentry> diff --git a/po/POTFILES.in b/po/POTFILES.in index d4dfc496bb..c86a0dbdab 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -75,6 +75,10 @@ libnm-core/nm-setting-ip-tunnel.c libnm-core/nm-setting-macsec.c libnm-core/nm-setting-macvlan.c libnm-core/nm-setting-olpc-mesh.c +libnm-core/nm-setting-ovs-interface.c +libnm-core/nm-setting-ovs-patch.c +libnm-core/nm-setting-ovs-port.c +libnm-core/nm-setting-ovs-bridge.c libnm-core/nm-setting-ppp.c libnm-core/nm-setting-pppoe.c libnm-core/nm-setting-proxy.c @@ -137,6 +141,9 @@ libnm/nm-device-ip-tunnel.c libnm/nm-device-macvlan.c libnm/nm-device-modem.c libnm/nm-device-olpc-mesh.c +libnm/nm-device-ovs-bridge.c +libnm/nm-device-ovs-interface.c +libnm/nm-device-ovs-port.c libnm/nm-device-team.c libnm/nm-device-vlan.c libnm/nm-device-vxlan.c diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c index 3730bb8925..734b824246 100644 --- a/shared/nm-meta-setting.c +++ b/shared/nm-meta-setting.c @@ -43,6 +43,10 @@ #include "nm-setting-macsec.h" #include "nm-setting-macvlan.h" #include "nm-setting-olpc-mesh.h" +#include "nm-setting-ovs-bridge.h" +#include "nm-setting-ovs-interface.h" +#include "nm-setting-ovs-patch.h" +#include "nm-setting-ovs-port.h" #include "nm-setting-ppp.h" #include "nm-setting-pppoe.h" #include "nm-setting-proxy.h" @@ -235,6 +239,26 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_OLPC_MESH_SETTING_NAME, .get_setting_gtype = nm_setting_olpc_mesh_get_type, }, + [NM_META_SETTING_TYPE_OVS_BRIDGE] = { + .meta_type = NM_META_SETTING_TYPE_OVS_BRIDGE, + .setting_name = NM_SETTING_OVS_BRIDGE_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_bridge_get_type, + }, + [NM_META_SETTING_TYPE_OVS_INTERFACE] = { + .meta_type = NM_META_SETTING_TYPE_OVS_INTERFACE, + .setting_name = NM_SETTING_OVS_INTERFACE_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_interface_get_type, + }, + [NM_META_SETTING_TYPE_OVS_PATCH] = { + .meta_type = NM_META_SETTING_TYPE_OVS_PATCH, + .setting_name = NM_SETTING_OVS_PATCH_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_patch_get_type, + }, + [NM_META_SETTING_TYPE_OVS_PORT] = { + .meta_type = NM_META_SETTING_TYPE_OVS_PORT, + .setting_name = NM_SETTING_OVS_PORT_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_port_get_type, + }, [NM_META_SETTING_TYPE_PPPOE] = { .meta_type = NM_META_SETTING_TYPE_PPPOE, .setting_name = NM_SETTING_PPPOE_SETTING_NAME, diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h index 949a6920c6..f36e322e89 100644 --- a/shared/nm-meta-setting.h +++ b/shared/nm-meta-setting.h @@ -75,6 +75,10 @@ typedef enum { NM_META_SETTING_TYPE_MACSEC, NM_META_SETTING_TYPE_MACVLAN, NM_META_SETTING_TYPE_OLPC_MESH, + NM_META_SETTING_TYPE_OVS_BRIDGE, + NM_META_SETTING_TYPE_OVS_INTERFACE, + NM_META_SETTING_TYPE_OVS_PATCH, + NM_META_SETTING_TYPE_OVS_PORT, NM_META_SETTING_TYPE_PPP, NM_META_SETTING_TYPE_PPPOE, NM_META_SETTING_TYPE_PROXY, diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h index f7a87aae82..126546ec93 100644 --- a/shared/nm-utils/nm-test-utils.h +++ b/shared/nm-utils/nm-test-utils.h @@ -1697,6 +1697,63 @@ nmtst_assert_setting_verifies (NMSetting *setting) g_assert (success); } +#if defined(__NM_SIMPLE_CONNECTION_H__) +static inline void +_nmtst_assert_connection_has_settings (NMConnection *connection, gboolean has_at_least, gboolean has_at_most, ...) +{ + gs_unref_hashtable GHashTable *names = NULL; + gs_free NMSetting **settings = NULL; + va_list ap; + const char *name; + guint i, len; + gs_unref_ptrarray GPtrArray *names_arr = NULL; + + g_assert (NM_IS_CONNECTION (connection)); + + names = g_hash_table_new (g_str_hash, g_str_equal); + names_arr = g_ptr_array_new (); + + va_start (ap, has_at_most); + while ((name = va_arg (ap, const char *))) { + if (!nm_g_hash_table_add (names, (gpointer) name)) + g_assert_not_reached (); + g_ptr_array_add (names_arr, (gpointer) name); + } + va_end (ap); + + g_ptr_array_add (names_arr, NULL); + + settings = nm_connection_get_settings (connection, &len); + for (i = 0; i < len; i++) { + if ( !g_hash_table_remove (names, nm_setting_get_name (settings[i])) + && has_at_most) { + g_error ("nmtst_assert_connection_has_settings(): has setting \"%s\" which is not expected", + nm_setting_get_name (settings[i])); + } + } + if ( g_hash_table_size (names) > 0 + && has_at_least) { + gs_free char *expected_str = g_strjoinv (" ", (char **) names_arr->pdata); + gs_free const char **settings_names = NULL; + gs_free char *has_str = NULL; + + settings_names = g_new0 (const char *, len + 1); + for (i = 0; i < len; i++) + settings_names[i] = nm_setting_get_name (settings[i]); + has_str = g_strjoinv (" ", (char **) settings_names); + + g_error ("nmtst_assert_connection_has_settings(): the setting lacks %u expected settings (expected: [%s] vs. has: [%s])", + g_hash_table_size (names), + expected_str, + has_str); + } +} +#define nmtst_assert_connection_has_settings(connection, ...) _nmtst_assert_connection_has_settings ((connection), TRUE, TRUE, __VA_ARGS__, NULL) +#define nmtst_assert_connection_has_settings_at_least(connection, ...) _nmtst_assert_connection_has_settings ((connection), TRUE, FALSE, __VA_ARGS__, NULL) +#define nmtst_assert_connection_has_settings_at_most(connection, ...) _nmtst_assert_connection_has_settings ((connection), FALSE, TRUE, __VA_ARGS__, NULL) + +#endif /* __NM_SIMPLE_CONNECTION_H__ */ + static inline void nmtst_assert_setting_verify_fails (NMSetting *setting, GQuark expect_error_domain, diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index b7a2faaa17..596c5bb362 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -175,32 +175,24 @@ nm_utils_get_ip_config_method (NMConnection *connection, if (ip_setting_type == NM_TYPE_SETTING_IP4_CONFIG) { g_return_val_if_fail (s_con != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO); - if (nm_setting_connection_get_master (s_con)) + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (!s_ip4) return NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - else { - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (!s_ip4) - return NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - method = nm_setting_ip_config_get_method (s_ip4); - g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO); - - return method; - } + method = nm_setting_ip_config_get_method (s_ip4); + g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + return method; } else if (ip_setting_type == NM_TYPE_SETTING_IP6_CONFIG) { g_return_val_if_fail (s_con != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO); - if (nm_setting_connection_get_master (s_con)) + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (!s_ip6) return NM_SETTING_IP6_CONFIG_METHOD_IGNORE; - else { - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (!s_ip6) - return NM_SETTING_IP6_CONFIG_METHOD_IGNORE; - method = nm_setting_ip_config_get_method (s_ip6); - g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO); - - return method; - } + method = nm_setting_ip_config_get_method (s_ip6); + g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO); + + return method; } else g_assert_not_reached (); diff --git a/src/devices/nm-device-logging.h b/src/devices/nm-device-logging.h index f0c7e59134..5f2e7d0da6 100644 --- a/src/devices/nm-device-logging.h +++ b/src/devices/nm-device-logging.h @@ -40,13 +40,16 @@ _nm_device_log_self_to_device (t *self) \ \ if (nm_logging_enabled (_level, _domain)) { \ typeof (*self) *const _self = (self); \ - const char *const _ifname = _nm_device_get_iface (_nm_device_log_self_to_device (_self)); \ + NMDevice *_device = _nm_device_log_self_to_device (_self); \ + const char *const _ifname = _nm_device_get_iface (_device); \ + const char *const _type_description = _ifname ? nm_device_get_type_description (_device) : NULL; \ \ nm_log_obj (_level, _domain, \ _ifname, NULL, \ _self, "device", \ - "%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - NM_PRINT_FMT_QUOTED (_ifname, "(", _ifname, ")", "[null]") \ + "%s%s%s%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + NM_PRINT_FMT_QUOTED (_ifname, "(", _ifname, "", "[null]"), \ + NM_PRINT_FMT_QUOTED (_ifname, ",", _type_description, ")", "") \ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } \ } G_STMT_END diff --git a/src/devices/nm-device-ppp.c b/src/devices/nm-device-ppp.c index 09b25f50e2..8b3968d513 100644 --- a/src/devices/nm-device-ppp.c +++ b/src/devices/nm-device-ppp.c @@ -117,7 +117,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager, return; } if (renamed) - nm_manager_remove_device (nm_manager_get (), iface); + nm_manager_remove_device (nm_manager_get (), iface, NM_DEVICE_TYPE_PPP); nm_device_activate_schedule_ip4_config_result (device, config); return; @@ -195,7 +195,7 @@ act_stage3_ip4_config_start (NMDevice *device, if (!nm_device_take_over_link (device, priv->pending_ifname, &renamed)) return NM_ACT_STAGE_RETURN_FAILURE; if (renamed) - nm_manager_remove_device (nm_manager_get (), priv->pending_ifname); + nm_manager_remove_device (nm_manager_get (), priv->pending_ifname, NM_DEVICE_TYPE_PPP); if (out_config) *out_config = g_steal_pointer (&priv->pending_ip4_config); else diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 52d7e3e3d3..c286d4668a 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -646,6 +646,7 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_reason_to_string, NMDeviceStateReason, NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_NEW_ACTIVATION, "new-activation"), NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PARENT_CHANGED, "parent-changed"), NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED, "parent-managed-changed"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_OVSDB_FAILED, "ovsdb-failed"), ); #define reason_to_string(reason) \ @@ -1661,6 +1662,10 @@ _get_route_metric_default (NMDevice *self) return 700; case NM_DEVICE_TYPE_BT: return 750; + case NM_DEVICE_TYPE_OVS_BRIDGE: + case NM_DEVICE_TYPE_OVS_INTERFACE: + case NM_DEVICE_TYPE_OVS_PORT: + return 800; case NM_DEVICE_TYPE_GENERIC: return 950; case NM_DEVICE_TYPE_UNKNOWN: @@ -2538,12 +2543,27 @@ static void device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevice *master; + nm_auto_nmpobj const NMPObject *plink_master_keep_alive = NULL; + const NMPlatformLink *plink_master; g_return_if_fail (plink); if (plink->master <= 0) return; + master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master); + plink_master = nm_platform_link_get (nm_device_get_platform (self), plink->master); + plink_master_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink_master)); + + if ( master == NULL + && plink_master + && g_strcmp0 (plink_master->name, "ovs-system") == 0 + && plink_master->type == NM_LINK_TYPE_OPENVSWITCH) { + _LOGD (LOGD_DEVICE, "the device claimed by openvswitch"); + return; + } + if (priv->master) { if ( plink->master > 0 && plink->master == nm_device_get_ifindex (priv->master)) { @@ -2555,20 +2575,16 @@ device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink) nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); } - if (plink->master > 0) { - NMDevice *master; - master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master); - if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) - nm_device_master_add_slave (master, self, FALSE); - else if (master) { - _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring", - nm_device_get_iface (master)); - } else { - _LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s", - plink->master, - nm_platform_link_get_name (nm_device_get_platform (self), plink->master)); - } + if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) + nm_device_master_add_slave (master, self, FALSE); + else if (master) { + _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring", + nm_device_get_iface (master)); + } else { + _LOGW (LOGD_DEVICE, "enslaved to unknown device %d (%s%s%s)", + plink->master, + NM_PRINT_FMT_QUOTED (plink_master, "\"", plink_master->name, "\"", "??")); } } @@ -2650,8 +2666,7 @@ device_link_changed (NMDevice *self) NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean ip_ifname_changed = FALSE; - const char *udi; - NMPlatformLink info; + nm_auto_nmpobj const NMPObject *pllink_keep_alive = NULL; const NMPlatformLink *pllink; int ifindex; gboolean was_up; @@ -2665,37 +2680,20 @@ device_link_changed (NMDevice *self) if (!pllink) return G_SOURCE_REMOVE; - info = *pllink; - - udi = nm_platform_link_get_udi (nm_device_get_platform (self), info.ifindex); - if (udi && !nm_streq0 (udi, priv->udi)) { - /* Update UDI to what udev gives us */ - g_free (priv->udi); - priv->udi = g_strdup (udi); - _notify (self, PROP_UDI); - } - - if (!nm_streq0 (info.driver, priv->driver)) { - g_free (priv->driver); - priv->driver = g_strdup (info.driver); - _notify (self, PROP_DRIVER); - } - - _set_mtu (self, info.mtu); + pllink_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (pllink)); - if (ifindex == nm_device_get_ip_ifindex (self)) - _stats_update_counters_from_pllink (self, &info); + nm_device_update_from_platform_link (self, pllink); had_hw_addr = (priv->hw_addr != NULL); nm_device_update_hw_address (self); got_hw_addr = (!had_hw_addr && priv->hw_addr); nm_device_update_permanent_hw_address (self, FALSE); - if (info.name[0] && strcmp (priv->iface, info.name) != 0) { + if (pllink->name[0] && strcmp (priv->iface, pllink->name) != 0) { _LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'", - priv->ifindex, priv->iface, info.name); + priv->ifindex, priv->iface, pllink->name); g_free (priv->iface); - priv->iface = g_strdup (info.name); + priv->iface = g_strdup (pllink->name); /* If the device has no explicit ip_iface, then changing iface changes ip_iface too. */ ip_ifname_changed = !priv->ip_iface; @@ -2718,8 +2716,8 @@ device_link_changed (NMDevice *self) nm_device_emit_recheck_auto_activate (self); } - if (priv->ndisc && info.inet6_token.id) { - if (nm_ndisc_set_iid (priv->ndisc, info.inet6_token)) + if (priv->ndisc && pllink->inet6_token.id) { + if (nm_ndisc_set_iid (priv->ndisc, pllink->inet6_token)) _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); } @@ -2728,16 +2726,16 @@ device_link_changed (NMDevice *self) && !nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) nm_device_set_carrier (self, pllink->connected); - klass->link_changed (self, &info); + klass->link_changed (self, pllink); /* Update DHCP, etc, if needed */ if (ip_ifname_changed) nm_device_update_dynamic_ip_setup (self); was_up = priv->up; - priv->up = NM_FLAGS_HAS (info.n_ifi_flags, IFF_UP); + priv->up = NM_FLAGS_HAS (pllink->n_ifi_flags, IFF_UP); - if ( info.initialized + if ( pllink->initialized && nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { NMDeviceStateReason reason; @@ -2767,7 +2765,7 @@ device_link_changed (NMDevice *self) set_unmanaged_external_down (self, FALSE); - device_recheck_slave_status (self, &info); + device_recheck_slave_status (self, pllink); if (priv->up && !was_up) { /* the link was down and just came up. That happens for example, while changing MTU. @@ -3137,38 +3135,54 @@ nm_device_create_and_realize (NMDevice *self, return TRUE; } -static void -update_device_from_platform_link (NMDevice *self, const NMPlatformLink *plink) +void +nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *udi; + const char *str; + int ifindex; + guint32 mtu; - g_return_if_fail (plink != NULL); + g_return_if_fail (plink == NULL || link_type_compatible (self, plink->type, NULL, NULL)); - udi = nm_platform_link_get_udi (nm_device_get_platform (self), plink->ifindex); - if (udi && !nm_streq0 (udi, priv->udi)) { + str = plink ? nm_platform_link_get_udi (nm_device_get_platform (self), plink->ifindex) : NULL; + if (g_strcmp0 (str, priv->udi)) { g_free (priv->udi); - priv->udi = g_strdup (udi); + priv->udi = g_strdup (str); _notify (self, PROP_UDI); } - if (!g_strcmp0 (plink->name, priv->iface)) { + str = plink ? plink->name : NULL; + if (str && g_strcmp0 (str, priv->iface)) { g_free (priv->iface); - priv->iface = g_strdup (plink->name); + priv->iface = g_strdup (str); _notify (self, PROP_IFACE); } - if (priv->ifindex != plink->ifindex) { - priv->ifindex = plink->ifindex; - _notify (self, PROP_IFINDEX); - } - - priv->up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP); - if (plink->driver && g_strcmp0 (plink->driver, priv->driver) != 0) { + str = plink ? plink->driver : NULL; + if (g_strcmp0 (str, priv->driver) != 0) { g_free (priv->driver); - priv->driver = g_strdup (plink->driver); + priv->driver = g_strdup (str); _notify (self, PROP_DRIVER); } + + if (plink) { + priv->up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP); + if (plink->ifindex == nm_device_get_ip_ifindex (self)) + _stats_update_counters_from_pllink (self, plink); + } else { + priv->up = FALSE; + } + + mtu = plink ? plink->mtu : 0; + _set_mtu (self, mtu); + + ifindex = plink ? plink->ifindex : 0; + if (priv->ifindex != ifindex) { + priv->ifindex = ifindex; + _notify (self, PROP_IFINDEX); + NM_DEVICE_GET_CLASS (self)->link_changed (self, plink); + } } static void @@ -3282,11 +3296,8 @@ realize_start_setup (NMDevice *self, nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL); - if (plink) { - g_return_if_fail (link_type_compatible (self, plink->type, NULL, NULL)); - update_device_from_platform_link (self, plink); - _stats_update_counters_from_pllink (self, plink); - } + if (plink) + nm_device_update_from_platform_link (self, plink); if (priv->ifindex > 0) { priv->physical_port_id = nm_platform_link_get_physical_port_id (nm_device_get_platform (self), priv->ifindex); @@ -3883,6 +3894,113 @@ nm_device_get_master (NMDevice *self) return NULL; } +static gboolean +get_ip_config_may_fail (NMDevice *self, int addr_family) +{ + NMConnection *connection; + NMSettingIPConfig *s_ip = NULL; + + connection = nm_device_get_applied_connection (self); + + /* Fail the connection if the failed IP method is required to complete */ + switch (addr_family) { + case AF_INET: + s_ip = nm_connection_get_setting_ip4_config (connection); + break; + case AF_INET6: + s_ip = nm_connection_get_setting_ip6_config (connection); + break; + default: + nm_assert_not_reached (); + } + + return !s_ip || nm_setting_ip_config_get_may_fail (s_ip); +} + +/* + * check_ip_state + * + * Transition the device from IP_CONFIG to the next state according to the + * outcome of IPv4 and IPv6 configuration. @may_fail indicates that we are + * called just after the initial configuration and thus IPv4/IPv6 are allowed to + * fail if the ipvx.may-fail properties say so, because the IP methods couldn't + * even be started. + */ +static void +check_ip_state (NMDevice *self, gboolean may_fail) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean ip4_disabled = FALSE, ip6_ignore = FALSE; + NMSettingIPConfig *s_ip4, *s_ip6; + NMDeviceState state; + + if (nm_device_get_state (self) != NM_DEVICE_STATE_IP_CONFIG) + return; + + /* Don't progress into IP_CHECK or SECONDARIES if we're waiting for the + * master to enslave us. */ + if ( nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request)) + && !priv->is_enslaved) + return; + + s_ip4 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP4_CONFIG); + if (s_ip4 && nm_streq0 (nm_setting_ip_config_get_method (s_ip4), + NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) + ip4_disabled = TRUE; + + s_ip6 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + if (s_ip6 && nm_streq0 (nm_setting_ip_config_get_method (s_ip6), + NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) + ip6_ignore = TRUE; + + if ( priv->ip4_state == IP_DONE + && priv->ip6_state == IP_DONE) { + /* Both method completed (or disabled), proceed with activation */ + nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); + return; + } + + if ( (priv->ip4_state == IP_FAIL || (ip4_disabled && priv->ip4_state == IP_DONE)) + && (priv->ip6_state == IP_FAIL || (ip6_ignore && priv->ip6_state == IP_DONE))) { + /* Either both methods failed, or only one failed and the other is + * disabled */ + if (nm_device_sys_iface_state_is_external_or_assume (self)) { + /* We have assumed configuration, but couldn't redo it. No problem, + * move to check state. */ + _set_ip_state (self, AF_INET, IP_DONE); + _set_ip_state (self, AF_INET6, IP_DONE); + state = NM_DEVICE_STATE_IP_CHECK; + } else if ( may_fail + && get_ip_config_may_fail (self, AF_INET) + && get_ip_config_may_fail (self, AF_INET6)) { + /* Couldn't start either IPv6 and IPv4 autoconfiguration, + * but both are allowed to fail. */ + state = NM_DEVICE_STATE_SECONDARIES; + } else { + /* Autoconfiguration attempted without success. */ + state = NM_DEVICE_STATE_FAILED; + } + + nm_device_state_changed (self, + state, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + return; + } + + /* If a method is still pending but required, wait */ + if (priv->ip4_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET)) + return; + if (priv->ip6_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET6)) + return; + + /* If at least a method has completed, proceed with activation */ + if ( (priv->ip4_state == IP_DONE && !ip4_disabled) + || (priv->ip6_state == IP_DONE && !ip6_ignore)) { + nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); + return; + } +} + /** * nm_device_slave_notify_enslave: * @self: the slave device @@ -3919,10 +4037,8 @@ nm_device_slave_notify_enslave (NMDevice *self, gboolean success) } if (activating) { - _set_ip_state (self, AF_INET, IP_DONE); - _set_ip_state (self, AF_INET6, IP_DONE); if (success) - nm_device_queue_state (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE); + check_ip_state (self, FALSE); else nm_device_queue_state (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN); } else @@ -4893,29 +5009,6 @@ activation_source_is_scheduled (NMDevice *self, /*****************************************************************************/ -static gboolean -get_ip_config_may_fail (NMDevice *self, int addr_family) -{ - NMConnection *connection; - NMSettingIPConfig *s_ip = NULL; - - connection = nm_device_get_applied_connection (self); - - /* Fail the connection if the failed IP method is required to complete */ - switch (addr_family) { - case AF_INET: - s_ip = nm_connection_get_setting_ip4_config (connection); - break; - case AF_INET6: - s_ip = nm_connection_get_setting_ip6_config (connection); - break; - default: - nm_assert_not_reached (); - } - - return !s_ip || nm_setting_ip_config_get_may_fail (s_ip); -} - static void master_ready (NMDevice *self, NMActiveConnection *active) @@ -5210,84 +5303,6 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self) activation_source_schedule (self, activate_stage2_device_config, AF_INET); } -/* - * check_ip_state - * - * Transition the device from IP_CONFIG to the next state according to the - * outcome of IPv4 and IPv6 configuration. @may_fail indicates that we are - * called just after the initial configuration and thus IPv4/IPv6 are allowed to - * fail if the ipvx.may-fail properties say so, because the IP methods couldn't - * even be started. - */ -static void -check_ip_state (NMDevice *self, gboolean may_fail) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean ip4_disabled = FALSE, ip6_ignore = FALSE; - NMSettingIPConfig *s_ip4, *s_ip6; - NMDeviceState state; - - if (nm_device_get_state (self) != NM_DEVICE_STATE_IP_CONFIG) - return; - - s_ip4 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP4_CONFIG); - if (s_ip4 && nm_streq0 (nm_setting_ip_config_get_method (s_ip4), - NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) - ip4_disabled = TRUE; - - s_ip6 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); - if (s_ip6 && nm_streq0 (nm_setting_ip_config_get_method (s_ip6), - NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) - ip6_ignore = TRUE; - - if ( priv->ip4_state == IP_DONE - && priv->ip6_state == IP_DONE) { - /* Both method completed (or disabled), proceed with activation */ - nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); - return; - } - - if ( (priv->ip4_state == IP_FAIL || (ip4_disabled && priv->ip4_state == IP_DONE)) - && (priv->ip6_state == IP_FAIL || (ip6_ignore && priv->ip6_state == IP_DONE))) { - /* Either both methods failed, or only one failed and the other is - * disabled */ - if (nm_device_sys_iface_state_is_external_or_assume (self)) { - /* We have assumed configuration, but couldn't redo it. No problem, - * move to check state. */ - _set_ip_state (self, AF_INET, IP_DONE); - _set_ip_state (self, AF_INET6, IP_DONE); - state = NM_DEVICE_STATE_IP_CHECK; - } else if ( may_fail - && get_ip_config_may_fail (self, AF_INET) - && get_ip_config_may_fail (self, AF_INET6)) { - /* Couldn't start either IPv6 and IPv4 autoconfiguration, - * but both are allowed to fail. */ - state = NM_DEVICE_STATE_SECONDARIES; - } else { - /* Autoconfiguration attempted without success. */ - state = NM_DEVICE_STATE_FAILED; - } - - nm_device_state_changed (self, - state, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return; - } - - /* If a method is still pending but required, wait */ - if (priv->ip4_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET)) - return; - if (priv->ip6_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET6)) - return; - - /* If at least a method has completed, proceed with activation */ - if ( (priv->ip4_state == IP_DONE && !ip4_disabled) - || (priv->ip6_state == IP_DONE && !ip6_ignore)) { - nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); - return; - } -} - void nm_device_ip_method_failed (NMDevice *self, int addr_family, @@ -6249,9 +6264,16 @@ static gboolean connection_requires_carrier (NMConnection *connection) { NMSettingIPConfig *s_ip4, *s_ip6; + NMSettingConnection *s_con; gboolean ip4_carrier_wanted, ip6_carrier_wanted; gboolean ip4_used = FALSE, ip6_used = FALSE; + /* We can progress to IP_CONFIG now, so that we're enslaved. + * That may actually cause carrier to go up and thus continue acivation. */ + s_con = nm_connection_get_setting_connection (connection); + if (nm_setting_connection_get_master (s_con)) + return FALSE; + ip4_carrier_wanted = connection_ip4_method_requires_carrier (connection, &ip4_used); if (ip4_carrier_wanted) { /* If IPv4 wants a carrier and cannot fail, the whole connection @@ -8009,12 +8031,6 @@ nm_device_activate_stage3_ip4_start (NMDevice *self) g_assert (priv->ip4_state == IP_WAIT); - /* Slaves stay in IP_CONFIG state until master is ready, and then - * they go directly to SECONDARIES without configuring IPv4. - */ - if (nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request))) - return TRUE; - _set_ip_state (self, AF_INET, IP_CONF); ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &ip4_config, &failure_reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { @@ -8056,12 +8072,6 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) g_assert (priv->ip6_state == IP_WAIT); - /* Slaves stay in IP_CONFIG state until master is ready, and then - * they go directly to SECONDARIES without configuring IPv6. - */ - if (nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request))) - return TRUE; - _set_ip_state (self, AF_INET6, IP_CONF); ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &ip6_config, &failure_reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { @@ -8100,10 +8110,6 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) static void activate_stage3_ip_config_start (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActiveConnection *master; - NMDevice *master_device; - _set_ip_state (self, AF_INET, IP_WAIT); _set_ip_state (self, AF_INET6, IP_WAIT); @@ -8116,26 +8122,6 @@ activate_stage3_ip_config_start (NMDevice *self) if (!nm_platform_link_is_up (nm_device_get_platform (self), nm_device_get_ip_ifindex (self))) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); - /* If the device is a slave, then we don't do any IP configuration but we - * use the IP config stage to indicate to the master we're ready for - * enslavement. If the master is already activating, it will have tried to - * enslave us when we changed state to IP_CONFIG, causing us to queue a - * transition to SECONDARIES (or FAILED if the enslavement failed), with - * our IP states set to IP_DONE either way. If the master isn't yet - * activating, then they'll still be in IP_WAIT. Either way, we bail out - * of IP config here. - */ - master = nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request)); - if (master) { - master_device = nm_active_connection_get_device (master); - if (priv->ip4_state == IP_WAIT && priv->ip6_state == IP_WAIT) { - _LOGI (LOGD_DEVICE, "Activation: connection '%s' waiting on master '%s'", - nm_connection_get_id (nm_device_get_applied_connection (self)), - master_device ? nm_device_get_iface (master_device) : "(unknown)"); - } - return; - } - /* IPv4 */ if ( nm_device_activate_ip4_state_in_wait (self) && !nm_device_activate_stage3_ip4_start (self)) @@ -8901,7 +8887,7 @@ delete_on_deactivate_link_delete (gpointer user_data) if (!nm_device_unrealize (data->device, TRUE, &error)) _LOGD (LOGD_DEVICE, "delete_on_deactivate: unrealizing %d failed (%s)", data->ifindex, error->message); - } else + } else if (data->ifindex > 0) nm_platform_link_delete (nm_device_get_platform (self), data->ifindex); g_free (data); @@ -8932,8 +8918,6 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); DeleteOnDeactivateData *data; - if (ifindex <= 0) - return; if (!priv->nm_owned) return; if (priv->queued_act_request) diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index ef83f1b53e..921db8aa5e 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -660,6 +660,9 @@ gboolean nm_device_unrealize (NMDevice *device, gboolean remove_resources, GError **error); +void nm_device_update_from_platform_link (NMDevice *self, + const NMPlatformLink *plink); + gboolean nm_device_get_autoconnect (NMDevice *device); void nm_device_set_autoconnect_intern (NMDevice *device, gboolean autoconnect); void nm_device_emit_recheck_auto_activate (NMDevice *device); diff --git a/src/devices/ovs/nm-device-ovs-bridge.c b/src/devices/ovs/nm-device-ovs-bridge.c new file mode 100644 index 0000000000..53ea2b8287 --- /dev/null +++ b/src/devices/ovs/nm-device-ovs-bridge.c @@ -0,0 +1,156 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-device-ovs-bridge.h" +#include "nm-device-ovs-port.h" +#include "nm-ovsdb.h" + +#include "devices/nm-device-private.h" +#include "nm-active-connection.h" +#include "nm-setting-connection.h" +#include "nm-setting-ovs-bridge.h" + +#include "introspection/org.freedesktop.NetworkManager.Device.OvsBridge.h" + +#include "devices/nm-device-logging.h" +_LOG_DECLARE_SELF(NMDeviceOvsBridge); + +/*****************************************************************************/ + +struct _NMDeviceOvsBridge { + NMDevice parent; +}; + +struct _NMDeviceOvsBridgeClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE (NMDeviceOvsBridge, nm_device_ovs_bridge, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + return "ovs-bridge"; +} + +static gboolean +create_and_realize (NMDevice *device, + NMConnection *connection, + NMDevice *parent, + const NMPlatformLink **out_plink, + GError **error) +{ + /* The actual backing resources will be created on enslavement by the port + * when it can identify the port and the bridge. */ + + return TRUE; +} + +static gboolean +unrealize (NMDevice *device, GError **error) +{ + return TRUE; +} + +static NMDeviceCapabilities +get_generic_capabilities (NMDevice *device) +{ + return NM_DEVICE_CAP_IS_SOFTWARE; +} + +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection) +{ + const char *connection_type; + + if (!NM_DEVICE_CLASS (nm_device_ovs_bridge_parent_class)->check_connection_compatible (device, connection)) + return FALSE; + + connection_type = nm_connection_get_connection_type (connection); + if (!nm_streq0 (connection_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)) + return FALSE; + + return TRUE; +} + +static NMActStageReturn +act_stage3_ip4_config_start (NMDevice *device, + NMIP4Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + return NM_ACT_STAGE_RETURN_IP_FAIL; +} + +static NMActStageReturn +act_stage3_ip6_config_start (NMDevice *device, + NMIP6Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + return NM_ACT_STAGE_RETURN_IP_FAIL; +} + +static gboolean +enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure) +{ + if (!configure) + return TRUE; + + if (!NM_IS_DEVICE_OVS_PORT (slave)) + return FALSE; + + return TRUE; +} + +static void +release_slave (NMDevice *device, NMDevice *slave, gboolean configure) +{ +} + +/*****************************************************************************/ + +static void +nm_device_ovs_bridge_init (NMDeviceOvsBridge *self) +{ +} + +static void +nm_device_ovs_bridge_class_init (NMDeviceOvsBridgeClass *klass) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + + device_class->connection_type = NM_SETTING_OVS_BRIDGE_SETTING_NAME; + device_class->is_master = TRUE; + device_class->get_type_description = get_type_description; + device_class->create_and_realize = create_and_realize; + device_class->unrealize = unrealize; + device_class->get_generic_capabilities = get_generic_capabilities; + device_class->check_connection_compatible = check_connection_compatible; + device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + device_class->enslave_slave = enslave_slave; + device_class->release_slave = release_slave; + + nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), + NMDBUS_TYPE_DEVICE_OVS_BRIDGE_SKELETON, + NULL); +} diff --git a/src/devices/ovs/nm-device-ovs-bridge.h b/src/devices/ovs/nm-device-ovs-bridge.h new file mode 100644 index 0000000000..631b4754ca --- /dev/null +++ b/src/devices/ovs/nm-device-ovs-bridge.h @@ -0,0 +1,35 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_DEVICE_OVS_BRIDGE_H__ +#define __NETWORKMANAGER_DEVICE_OVS_BRIDGE_H__ + +#define NM_TYPE_DEVICE_OVS_BRIDGE (nm_device_ovs_bridge_get_type ()) +#define NM_DEVICE_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridge)) +#define NM_DEVICE_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridgeClass)) +#define NM_IS_DEVICE_OVS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_BRIDGE)) +#define NM_IS_DEVICE_OVS_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_BRIDGE)) +#define NM_DEVICE_OVS_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_BRIDGE, NMDeviceOvsBridgeClass)) + +typedef struct _NMDeviceOvsBridge NMDeviceOvsBridge; +typedef struct _NMDeviceOvsBridgeClass NMDeviceOvsBridgeClass; + +GType nm_device_ovs_bridge_get_type (void); + +#endif /* __NETWORKMANAGER_DEVICE_OVS_BRIDGE_H__ */ diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c new file mode 100644 index 0000000000..426521c52c --- /dev/null +++ b/src/devices/ovs/nm-device-ovs-interface.c @@ -0,0 +1,191 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-device-ovs-interface.h" +#include "nm-ovsdb.h" + +#include "devices/nm-device-private.h" +#include "nm-active-connection.h" +#include "nm-setting-connection.h" +#include "nm-setting-ovs-interface.h" +#include "nm-setting-ovs-port.h" + +#include "introspection/org.freedesktop.NetworkManager.Device.OvsInterface.h" + +#include "devices/nm-device-logging.h" +_LOG_DECLARE_SELF(NMDeviceOvsInterface); + +/*****************************************************************************/ + +struct _NMDeviceOvsInterface { + NMDevice parent; +}; + +struct _NMDeviceOvsInterfaceClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE (NMDeviceOvsInterface, nm_device_ovs_interface, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + return "ovs-interface"; +} + +static gboolean +create_and_realize (NMDevice *device, + NMConnection *connection, + NMDevice *parent, + const NMPlatformLink **out_plink, + GError **error) +{ + /* The actual backing resources will be created once an interface is + * added to a port of ours, since there can be neither an empty port nor + * an empty bridge. */ + + return TRUE; +} + +static NMDeviceCapabilities +get_generic_capabilities (NMDevice *device) +{ + return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE; +} + +static gboolean +is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) +{ + return TRUE; +} + +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection) +{ + NMSettingConnection *s_con; + NMSettingOvsInterface *s_ovs_iface; + + if (!NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->check_connection_compatible (device, connection)) + return FALSE; + + s_ovs_iface = nm_connection_get_setting_ovs_interface (connection); + if (!s_ovs_iface) + return FALSE; + if (!NM_IN_STRSET (nm_setting_ovs_interface_get_interface_type (s_ovs_iface), + "internal", "patch")) { + return FALSE; + } + + s_con = nm_connection_get_setting_connection (connection); + if (g_strcmp0 (nm_setting_connection_get_connection_type (s_con), + NM_SETTING_OVS_INTERFACE_SETTING_NAME) != 0) { + return FALSE; + } + + return TRUE; +} + +static void +link_changed (NMDevice *device, + const NMPlatformLink *pllink) +{ + if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) { + nm_device_bring_up (device, TRUE, NULL); + nm_device_activate_schedule_stage3_ip_config_start (device); + } +} + +static gboolean +_is_internal_interface (NMDevice *device) +{ + NMConnection *connection = nm_device_get_applied_connection (device); + NMSettingOvsInterface *s_ovs_iface = nm_connection_get_setting_ovs_interface (connection); + + g_return_val_if_fail (s_ovs_iface, FALSE); + + return strcmp (nm_setting_ovs_interface_get_interface_type (s_ovs_iface), "internal") == 0; +} + +static NMActStageReturn +act_stage3_ip4_config_start (NMDevice *device, + NMIP4Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + if (!_is_internal_interface (device)) + return NM_ACT_STAGE_RETURN_IP_FAIL; + + if (!nm_device_get_ip_ifindex (device)) + return NM_ACT_STAGE_RETURN_POSTPONE; + + return NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->act_stage3_ip4_config_start (device, out_config, out_failure_reason); +} + +static NMActStageReturn +act_stage3_ip6_config_start (NMDevice *device, + NMIP6Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + if (!_is_internal_interface (device)) + return NM_ACT_STAGE_RETURN_IP_FAIL; + + if (!nm_device_get_ip_ifindex (device)) + return NM_ACT_STAGE_RETURN_POSTPONE; + + return NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->act_stage3_ip6_config_start (device, out_config, out_failure_reason); +} + +static gboolean +can_unmanaged_external_down (NMDevice *self) +{ + return FALSE; +} + +/*****************************************************************************/ + +static void +nm_device_ovs_interface_init (NMDeviceOvsInterface *self) +{ +} + +static void +nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_OPENVSWITCH); + + device_class->connection_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME; + device_class->get_type_description = get_type_description; + device_class->create_and_realize = create_and_realize; + device_class->get_generic_capabilities = get_generic_capabilities; + device_class->is_available = is_available; + device_class->check_connection_compatible = check_connection_compatible; + device_class->link_changed = link_changed; + device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + device_class->can_unmanaged_external_down = can_unmanaged_external_down; + + nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), + NMDBUS_TYPE_DEVICE_OVS_INTERFACE_SKELETON, + NULL); +} diff --git a/src/devices/ovs/nm-device-ovs-interface.h b/src/devices/ovs/nm-device-ovs-interface.h new file mode 100644 index 0000000000..a748e206e3 --- /dev/null +++ b/src/devices/ovs/nm-device-ovs-interface.h @@ -0,0 +1,35 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_DEVICE_OVS_INTERFACE_H__ +#define __NETWORKMANAGER_DEVICE_OVS_INTERFACE_H__ + +#define NM_TYPE_DEVICE_OVS_INTERFACE (nm_device_ovs_interface_get_type ()) +#define NM_DEVICE_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterface)) +#define NM_DEVICE_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterfaceClass)) +#define NM_IS_DEVICE_OVS_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_INTERFACE)) +#define NM_IS_DEVICE_OVS_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_INTERFACE)) +#define NM_DEVICE_OVS_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_INTERFACE, NMDeviceOvsInterfaceClass)) + +typedef struct _NMDeviceOvsInterface NMDeviceOvsInterface; +typedef struct _NMDeviceOvsInterfaceClass NMDeviceOvsInterfaceClass; + +GType nm_device_ovs_interface_get_type (void); + +#endif /* __NETWORKMANAGER_DEVICE_OVS_INTERFACE_H__ */ diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c new file mode 100644 index 0000000000..83199f2d4c --- /dev/null +++ b/src/devices/ovs/nm-device-ovs-port.c @@ -0,0 +1,201 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-device-ovs-port.h" +#include "nm-ovsdb.h" + +#include "devices/nm-device-private.h" +#include "nm-active-connection.h" +#include "nm-setting-connection.h" +#include "nm-setting-ovs-port.h" +#include "nm-setting-ovs-port.h" + +#include "introspection/org.freedesktop.NetworkManager.Device.OvsPort.h" + +#include "devices/nm-device-logging.h" +_LOG_DECLARE_SELF(NMDeviceOvsPort); + +/*****************************************************************************/ + +struct _NMDeviceOvsPort { + NMDevice parent; +}; + +struct _NMDeviceOvsPortClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE (NMDeviceOvsPort, nm_device_ovs_port, NM_TYPE_DEVICE) + +/*****************************************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + return "ovs-port"; +} + +static gboolean +create_and_realize (NMDevice *device, + NMConnection *connection, + NMDevice *parent, + const NMPlatformLink **out_plink, + GError **error) +{ + /* The port will be added to ovsdb when an interface is enslaved, + * because there's no such thing like an empty port. */ + + return TRUE; +} + +static NMDeviceCapabilities +get_generic_capabilities (NMDevice *device) +{ + return NM_DEVICE_CAP_IS_SOFTWARE; +} + + +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection) +{ + NMSettingConnection *s_con; + const char *connection_type; + + if (!NM_DEVICE_CLASS (nm_device_ovs_port_parent_class)->check_connection_compatible (device, connection)) + return FALSE; + + s_con = nm_connection_get_setting_connection (connection); + connection_type = nm_setting_connection_get_connection_type (s_con); + if (!connection_type) + return FALSE; + + if (strcmp (connection_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0) + return TRUE; + + return FALSE; +} + +static NMActStageReturn +act_stage3_ip4_config_start (NMDevice *device, + NMIP4Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + return NM_ACT_STAGE_RETURN_IP_FAIL; +} + +static NMActStageReturn +act_stage3_ip6_config_start (NMDevice *device, + NMIP6Config **out_config, + NMDeviceStateReason *out_failure_reason) +{ + return NM_ACT_STAGE_RETURN_IP_FAIL; +} + +static void +add_iface_cb (GError *error, gpointer user_data) +{ + NMDevice *slave = user_data; + + if (error) { + nm_log_warn (LOGD_DEVICE, "device %s could not be added to a ovs port: %s", + nm_device_get_iface (slave), error->message); + nm_device_state_changed (slave, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_OVSDB_FAILED); + } + + g_object_unref (slave); +} + +static gboolean +enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure) +{ + NMActiveConnection *ac_port = NULL; + NMActiveConnection *ac_bridge = NULL; + + if (!configure) + return TRUE; + + + ac_port = NM_ACTIVE_CONNECTION (nm_device_get_act_request (device)); + ac_bridge = nm_active_connection_get_master (ac_port); + if (!ac_bridge) + ac_bridge = ac_port; + + nm_ovsdb_add_interface (nm_ovsdb_get (), + nm_active_connection_get_applied_connection (ac_bridge), + nm_device_get_applied_connection (device), + nm_device_get_applied_connection (slave), + add_iface_cb, g_object_ref (slave)); + + return TRUE; +} + +static void +del_iface_cb (GError *error, gpointer user_data) +{ + NMDevice *slave = user_data; + + if (error) { + nm_log_warn (LOGD_DEVICE, "device %s could not be removed from a ovs port: %s", + nm_device_get_iface (slave), error->message); + nm_device_state_changed (slave, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_OVSDB_FAILED); + } + + g_object_unref (slave); +} + +static void +release_slave (NMDevice *device, NMDevice *slave, gboolean configure) +{ + nm_ovsdb_del_interface (nm_ovsdb_get (), nm_device_get_iface (slave), + del_iface_cb, g_object_ref (slave)); +} + +/*****************************************************************************/ + +static void +nm_device_ovs_port_init (NMDeviceOvsPort *self) +{ +} + +static void +nm_device_ovs_port_class_init (NMDeviceOvsPortClass *klass) +{ + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + + device_class->connection_type = NM_SETTING_OVS_PORT_SETTING_NAME; + device_class->is_master = TRUE; + device_class->get_type_description = get_type_description; + device_class->create_and_realize = create_and_realize; + device_class->get_generic_capabilities = get_generic_capabilities; + device_class->check_connection_compatible = check_connection_compatible; + device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + device_class->enslave_slave = enslave_slave; + device_class->release_slave = release_slave; + + nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), + NMDBUS_TYPE_DEVICE_OVS_PORT_SKELETON, + NULL); +} diff --git a/src/devices/ovs/nm-device-ovs-port.h b/src/devices/ovs/nm-device-ovs-port.h new file mode 100644 index 0000000000..5ccf1ec195 --- /dev/null +++ b/src/devices/ovs/nm-device-ovs-port.h @@ -0,0 +1,35 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_DEVICE_OVS_PORT_H__ +#define __NETWORKMANAGER_DEVICE_OVS_PORT_H__ + +#define NM_TYPE_DEVICE_OVS_PORT (nm_device_ovs_port_get_type ()) +#define NM_DEVICE_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPort)) +#define NM_DEVICE_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPortClass)) +#define NM_IS_DEVICE_OVS_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OVS_PORT)) +#define NM_IS_DEVICE_OVS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OVS_PORT)) +#define NM_DEVICE_OVS_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OVS_PORT, NMDeviceOvsPortClass)) + +typedef struct _NMDeviceOvsPort NMDeviceOvsPort; +typedef struct _NMDeviceOvsPortClass NMDeviceOvsPortClass; + +GType nm_device_ovs_port_get_type (void); + +#endif /* __NETWORKMANAGER_DEVICE_OVS_PORT_H__ */ diff --git a/src/devices/ovs/nm-ovs-factory.c b/src/devices/ovs/nm-ovs-factory.c new file mode 100644 index 0000000000..830f94fc0c --- /dev/null +++ b/src/devices/ovs/nm-ovs-factory.c @@ -0,0 +1,195 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-manager.h" +#include "nm-ovsdb.h" +#include "nm-device-ovs-interface.h" +#include "nm-device-ovs-port.h" +#include "nm-device-ovs-bridge.h" +#include "platform/nm-platform.h" +#include "nm-core-internal.h" +#include "devices/nm-device-factory.h" + +/*****************************************************************************/ + +typedef struct { + NMDeviceFactory parent; +} NMOvsFactory; + +typedef struct { + NMDeviceFactoryClass parent; +} NMOvsFactoryClass; + +#define NM_TYPE_OVS_FACTORY (nm_ovs_factory_get_type ()) +#define NM_OVS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_OVS_FACTORY, NMOvsFactory)) +#define NM_OVS_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_OVS_FACTORY, NMOvsFactoryClass)) +#define NM_IS_OVS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_OVS_FACTORY)) +#define NM_IS_OVS_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_OVS_FACTORY)) +#define NM_OVS_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_OVS_FACTORY, NMOvsFactoryClass)) + +static GType nm_ovs_factory_get_type (void); +G_DEFINE_TYPE (NMOvsFactory, nm_ovs_factory, NM_TYPE_DEVICE_FACTORY) + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_DEVICE +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "ovs", __VA_ARGS__) + +/*****************************************************************************/ + +NM_DEVICE_FACTORY_DECLARE_TYPES ( + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_OPENVSWITCH) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_OVS_BRIDGE_SETTING_NAME, + NM_SETTING_OVS_INTERFACE_SETTING_NAME, + NM_SETTING_OVS_PORT_SETTING_NAME) +) + +G_MODULE_EXPORT NMDeviceFactory * +nm_device_factory_create (GError **error) +{ + return (NMDeviceFactory *) g_object_new (NM_TYPE_OVS_FACTORY, NULL); +} + +static NMDevice * +new_device_from_type (const char *name, NMDeviceType device_type) +{ + GType type; + const char *type_desc; + NMLinkType link_type = NM_LINK_TYPE_NONE; + + if (nm_manager_get_device (nm_manager_get (), name, device_type)) + return NULL; + + if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE) { + type = NM_TYPE_DEVICE_OVS_INTERFACE; + type_desc = "OpenVSwitch Interface"; + link_type = NM_LINK_TYPE_OPENVSWITCH; + } else if (device_type == NM_DEVICE_TYPE_OVS_PORT) { + type = NM_TYPE_DEVICE_OVS_PORT; + type_desc = "OpenVSwitch Port"; + } else if (device_type == NM_DEVICE_TYPE_OVS_BRIDGE) { + type = NM_TYPE_DEVICE_OVS_BRIDGE; + type_desc = "OpenVSwitch Bridge"; + } else { + return NULL; + } + + return g_object_new (type, + NM_DEVICE_IFACE, name, + NM_DEVICE_DRIVER, "openvswitch", + NM_DEVICE_DEVICE_TYPE, device_type, + NM_DEVICE_TYPE_DESC, type_desc, + NM_DEVICE_LINK_TYPE, link_type, + NULL); +} + +static void +ovsdb_device_added (NMOvsdb *ovsdb, const char *name, NMDeviceType device_type, + NMDeviceFactory *self) +{ + NMDevice *device = NULL; + + device = new_device_from_type (name, device_type); + if (!device) + return; + + g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device); + g_object_unref (device); +} + +static void +ovsdb_device_removed (NMOvsdb *ovsdb, const char *name, NMDeviceType device_type, + NMDeviceFactory *self) +{ + NMDevice *device; + NMDeviceState device_state; + + device = nm_manager_get_device (nm_manager_get (), name, device_type); + if (!device) + return; + + device_state = nm_device_get_state (device); + if ( device_type == NM_DEVICE_TYPE_OVS_INTERFACE + && device_state > NM_DEVICE_STATE_DISCONNECTED + && device_state < NM_DEVICE_STATE_DEACTIVATING) { + nm_device_state_changed (device, + NM_DEVICE_STATE_DEACTIVATING, + NM_DEVICE_STATE_REASON_REMOVED); + } else if (device_state == NM_DEVICE_STATE_UNMANAGED) { + nm_device_unrealize (device, TRUE, NULL); + } +} + +static void +start (NMDeviceFactory *self) +{ + NMOvsdb *ovsdb; + + ovsdb = nm_ovsdb_get (); + + g_signal_connect_object (ovsdb, NM_OVSDB_DEVICE_ADDED, G_CALLBACK (ovsdb_device_added), self, (GConnectFlags) 0); + g_signal_connect_object (ovsdb, NM_OVSDB_DEVICE_REMOVED, G_CALLBACK (ovsdb_device_removed), self, (GConnectFlags) 0); +} + +static NMDevice * +create_device (NMDeviceFactory *self, + const char *iface, + const NMPlatformLink *plink, + NMConnection *connection, + gboolean *out_ignore) +{ + NMDeviceType device_type = NM_DEVICE_TYPE_UNKNOWN; + const char *connection_type = NULL; + + if (g_strcmp0 (iface, "ovs-system") == 0) { + *out_ignore = TRUE; + return NULL; + } + + if (connection) + connection_type = nm_connection_get_connection_type (connection); + + if (plink) + device_type = NM_DEVICE_TYPE_OVS_INTERFACE; + else if (g_strcmp0 (connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME) == 0) + device_type = NM_DEVICE_TYPE_OVS_INTERFACE; + else if (g_strcmp0 (connection_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0) + device_type = NM_DEVICE_TYPE_OVS_PORT; + else if (g_strcmp0 (connection_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME) == 0) + device_type = NM_DEVICE_TYPE_OVS_BRIDGE; + + return new_device_from_type (iface, device_type); +} + +static void +nm_ovs_factory_init (NMOvsFactory *self) +{ +} + +static void +nm_ovs_factory_class_init (NMOvsFactoryClass *klass) +{ + NMDeviceFactoryClass *factory_class = NM_DEVICE_FACTORY_CLASS (klass); + + factory_class->get_supported_types = get_supported_types; + factory_class->start = start; + factory_class->create_device = create_device; +} diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c new file mode 100644 index 0000000000..28f44f287b --- /dev/null +++ b/src/devices/ovs/nm-ovsdb.c @@ -0,0 +1,1591 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-ovsdb.h" + +#include <string.h> +#include <jansson.h> +#include <gmodule.h> +#include <gio/gunixsocketaddress.h> + +#include "devices/nm-device.h" +#include "platform/nm-platform.h" +#include "nm-core-internal.h" + +/* Added in Jansson v2.4 (released Sep 23 2012), but travis.ci has v2.2. */ +#ifndef json_boolean +#define json_boolean(val) ((val) ? json_true() : json_false()) +#endif + +/*****************************************************************************/ + +typedef struct { + char *name; + char *connection_uuid; + GPtrArray *interfaces; /* interface uuids */ +} OpenvswitchPort; + +typedef struct { + char *name; + char *connection_uuid; + GPtrArray *ports; /* port uuids */ +} OpenvswitchBridge; + +typedef struct { + char *name; + char *type; + char *connection_uuid; +} OpenvswitchInterface; + +/*****************************************************************************/ + +enum { + DEVICE_ADDED, + DEVICE_REMOVED, + DEVICE_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct { + GSocketClient *client; + GSocketConnection *conn; + GCancellable *cancellable; + char buf[4096]; /* Input buffer */ + size_t bufp; /* Last decoded byte in the input buffer. */ + GString *input; /* JSON stream waiting for decoding. */ + GString *output; /* JSON stream to be sent. */ + gint64 seq; + GArray *calls; /* Method calls waiting for a response. */ + GHashTable *interfaces; /* interface uuid => OpenvswitchInterface */ + GHashTable *ports; /* port uuid => OpenvswitchPort */ + GHashTable *bridges; /* bridge uuid => OpenvswitchBridge */ + const char *db_uuid; +} NMOvsdbPrivate; + +struct _NMOvsdb { + GObject parent; + NMOvsdbPrivate _priv; +}; + +struct _NMOvsdbClass { + GObjectClass parent; +}; + +G_DEFINE_TYPE (NMOvsdb, nm_ovsdb, G_TYPE_OBJECT) + +#define NM_OVSDB_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMOvsdb, NM_IS_OVSDB) + +#define _NMLOG_DOMAIN LOGD_DEVICE +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "ovsdb", __VA_ARGS__) + +NM_DEFINE_SINGLETON_GETTER (NMOvsdb, nm_ovsdb_get, NM_TYPE_OVSDB); + +/*****************************************************************************/ + +static void ovsdb_try_connect (NMOvsdb *self); +static void ovsdb_disconnect (NMOvsdb *self); +static void ovsdb_read (NMOvsdb *self); +static void ovsdb_write (NMOvsdb *self); +static void ovsdb_next_command (NMOvsdb *self); + +/*****************************************************************************/ + +/* ovsdb command abstraction. */ + +typedef void (*OvsdbMethodCallback) (NMOvsdb *self, json_t *response, + GError *error, gpointer user_data); + +typedef enum { + OVSDB_MONITOR, + OVSDB_ADD_INTERFACE, + OVSDB_DEL_INTERFACE, +} OvsdbCommand; + +typedef struct { + gint64 id; +#define COMMAND_PENDING -1 /* id not yet assigned */ + OvsdbCommand command; + OvsdbMethodCallback callback; + gpointer user_data; + union { + const char *ifname; + struct { + NMConnection *bridge; + NMConnection *port; + NMConnection *interface; + }; + }; +} OvsdbMethodCall; + +static void +_call_trace (const char *comment, OvsdbMethodCall *call, json_t *msg) +{ +#ifdef NM_MORE_LOGGING + char *str = NULL; + + if (msg) + str = json_dumps (msg, 0); + + switch (call->command) { + case OVSDB_MONITOR: + _LOGT ("%s: monitor%s%s", + comment, + msg ? ": " : "", + msg ? str : ""); + break; + case OVSDB_ADD_INTERFACE: + _LOGT ("%s: add-iface bridge=%s port=%s interface=%s%s%s", + comment, + nm_connection_get_interface_name (call->bridge), + nm_connection_get_interface_name (call->port), + nm_connection_get_interface_name (call->interface), + msg ? ": " : "", + msg ? str : ""); + break; + case OVSDB_DEL_INTERFACE: + _LOGT ("%s: del-iface interface=%s%s%s", + comment, call->ifname, + msg ? ": " : "", + msg ? str : ""); + break; + } + + if (msg) + g_free (str); +#endif +} + +/** + * ovsdb_call_method: + * + * Queues the ovsdb command. Eventually fires the command right away if + * there's no command pending completion. + */ +static void +ovsdb_call_method (NMOvsdb *self, OvsdbCommand command, + const char *ifname, + NMConnection *bridge, NMConnection *port, NMConnection *interface, + OvsdbMethodCallback callback, gpointer user_data) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + OvsdbMethodCall *call; + + /* Ensure we're not unsynchronized before we queue the method call. */ + ovsdb_try_connect (self); + + g_array_set_size (priv->calls, priv->calls->len + 1); + call = &g_array_index (priv->calls, OvsdbMethodCall, priv->calls->len - 1); + call->id = COMMAND_PENDING; + call->command = command; + call->callback = callback; + call->user_data = user_data; + + switch (call->command) { + case OVSDB_MONITOR: + break; + case OVSDB_ADD_INTERFACE: + call->bridge = nm_simple_connection_new_clone (bridge); + call->port = nm_simple_connection_new_clone (port); + call->interface = nm_simple_connection_new_clone (interface); + break; + case OVSDB_DEL_INTERFACE: + call->ifname = g_strdup (ifname); + break; + } + + _call_trace ("enqueue", call, NULL); + + ovsdb_next_command (self); +} + +/*****************************************************************************/ + +/* Create and process the JSON-RPC messages from ovsdb. */ + +/** + * _expect_ovs_bridges: + * + * Return a command that will fail the transaction if the actual set of + * bridges doesn't match @bridges. This is a way of detecting race conditions + * with other ovsdb clients that might be adding or removing bridges + * at the same time. + */ +static void +_expect_ovs_bridges (json_t *params, const char *db_uuid, json_t *bridges) +{ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:i, s:[s], s:s, s:[{s:[s, O]}], s:[[s, s, [s, s]]]}", + "op", "wait", "table", "Open_vSwitch", + "timeout", 0, "columns", "bridges", + "until", "==", "rows", "bridges", "set", bridges, + "where", "_uuid", "==", "uuid", db_uuid) + ); +} + +/** + * _set_ovs_bridges: + * + * Return a command that will update the list of bridges in @db_uuid + * database to @new_bridges. + */ +static void +_set_ovs_bridges (json_t *params, const char *db_uuid, json_t *new_bridges) +{ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:{s:[s, O]}, s:[[s, s, [s, s]]]}", + "op", "update", "table", "Open_vSwitch", + "row", "bridges", "set", new_bridges, + "where", "_uuid", "==", "uuid", db_uuid) + ); +} + +/** + * _expect_bridge_ports: + * + * Return a command that will fail the transaction if the actual set of + * ports in bridge @ifname doesn't match @ports. This is a way of detecting + * race conditions with other ovsdb clients that might be adding or removing + * bridge ports at the same time. + */ +static void +_expect_bridge_ports (json_t *params, const char *ifname, json_t *ports) +{ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:i, s:[s], s:s, s:[{s:[s, O]}], s:[[s, s, s]]}", + "op", "wait", "table", "Bridge", + "timeout", 0, "columns", "ports", + "until", "==", "rows", "ports", "set", ports, + "where", "name", "==", ifname) + ); +} + +/** + * _set_bridge_ports: + * + * Return a command that will update the list of ports of bridge + * @ifname to @new_ports. + */ +static void +_set_bridge_ports (json_t *params, const char *ifname, json_t *new_ports) +{ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:{s:[s, O]}, s:[[s, s, s]]}", + "op", "update", "table", "Bridge", + "row", "ports", "set", new_ports, + "where", "name", "==", ifname) + ); +} + +/** + * _expect_port_interfaces: + * + * Return a command that will fail the transaction if the actual set of + * interfaces in port @ifname doesn't match @interfaces. This is a way of + * detecting race conditions with other ovsdb clients that might be adding + * or removing port interfaces at the same time. + */ +static void +_expect_port_interfaces (json_t *params, const char *ifname, json_t *interfaces) +{ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:i, s:[s], s:s, s:[{s:[s, O]}], s:[[s, s, s]]}", + "op", "wait", "table", "Port", + "timeout", 0, "columns", "interfaces", + "until", "==", "rows", "interfaces", "set", interfaces, + "where", "name", "==", ifname) + ); +} + +/** + * _set_port_interfaces: + * + * Return a command that will update the list of interfaces of port @ifname + * to @new_interfaces. + */ +static void +_set_port_interfaces (json_t *params, const char *ifname, json_t *new_interfaces) +{ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:{s:[s, O]}, s:[[s, s, s]]}", + "op", "update", "table", "Port", + "row", "interfaces", "set", new_interfaces, + "where", "name", "==", ifname) + ); +} + +/** + * _insert_interface: + * + * Returns an commands that adds new interface from a given connection. + */ +static void +_insert_interface (json_t *params, NMConnection *interface) +{ + const char *type = NULL; + NMSettingOvsInterface *s_ovs_iface; + NMSettingOvsPatch *s_ovs_patch; + json_t *options = json_array (); + + s_ovs_iface = nm_connection_get_setting_ovs_interface (interface); + if (s_ovs_iface) + type = nm_setting_ovs_interface_get_interface_type (s_ovs_iface); + + json_array_append (options, json_string ("map")); + s_ovs_patch = nm_connection_get_setting_ovs_patch (interface); + if (s_ovs_patch) { + json_array_append (options, json_pack ("[[s, s]]", + "peer", + nm_setting_ovs_patch_get_peer (s_ovs_patch))); + } else { + json_array_append (options, json_array ()); + } + + json_array_append_new (params, + json_pack ("{s:s, s:s, s:{s:s, s:s, s:o, s:[s, [[s, s]]]}, s:s}", + "op", "insert", "table", "Interface", "row", + "name", nm_connection_get_interface_name (interface), + "type", type ? type : "", + "options", options, + "external_ids", "map", "NM.connection.uuid", nm_connection_get_uuid (interface), + "uuid-name", "rowInterface")); +} + +/** + * _insert_port: + * + * Returns an commands that adds new port from a given connection. + */ +static void +_insert_port (json_t *params, NMConnection *port, json_t *new_interfaces) +{ + NMSettingOvsPort *s_ovs_port; + const char *vlan_mode = NULL; + guint tag = 0; + const char *lacp = NULL; + const char *bond_mode = NULL; + guint bond_updelay = 0; + guint bond_downdelay = 0; + json_t *row; + + s_ovs_port = nm_connection_get_setting_ovs_port (port); + + row = json_object (); + + if (s_ovs_port) { + vlan_mode = nm_setting_ovs_port_get_vlan_mode (s_ovs_port); + tag = nm_setting_ovs_port_get_tag (s_ovs_port); + lacp = nm_setting_ovs_port_get_lacp (s_ovs_port); + bond_mode = nm_setting_ovs_port_get_bond_mode (s_ovs_port); + bond_updelay = nm_setting_ovs_port_get_bond_updelay (s_ovs_port); + bond_downdelay = nm_setting_ovs_port_get_bond_downdelay (s_ovs_port); + } + + if (vlan_mode) + json_object_set_new (row, "vlan_mode", json_string (vlan_mode)); + if (tag) + json_object_set_new (row, "tag", json_integer (tag)); + if (lacp) + json_object_set_new (row, "lacp", json_string (lacp)); + if (bond_mode) + json_object_set_new (row, "bond_mode", json_string (bond_mode)); + if (bond_updelay) + json_object_set_new (row, "bond_updelay", json_integer (bond_updelay)); + if (bond_downdelay) + json_object_set_new (row, "bond_downdelay", json_integer (bond_downdelay)); + + json_object_set_new (row, "name", json_string (nm_connection_get_interface_name (port))); + json_object_set_new (row, "interfaces", json_pack ("[s, O]", "set", new_interfaces)); + json_object_set_new (row, "external_ids", + json_pack ("[s, [[s, s]]]", "map", + "NM.connection.uuid", nm_connection_get_uuid (port))); + + /* Create a new one. */ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:o, s:s}", "op", "insert", "table", "Port", + "row", row, "uuid-name", "rowPort")); +} + +/** + * _insert_bridge: + * + * Returns an commands that adds new bridge from a given connection. + */ +static void +_insert_bridge (json_t *params, NMConnection *bridge, json_t *new_ports) +{ + NMSettingOvsBridge *s_ovs_bridge; + const char *fail_mode = NULL; + gboolean mcast_snooping_enable = FALSE; + gboolean rstp_enable = FALSE; + gboolean stp_enable = FALSE; + json_t *row; + + s_ovs_bridge = nm_connection_get_setting_ovs_bridge (bridge); + + row = json_object (); + + if (s_ovs_bridge) { + fail_mode = nm_setting_ovs_bridge_get_fail_mode (s_ovs_bridge); + mcast_snooping_enable = nm_setting_ovs_bridge_get_mcast_snooping_enable (s_ovs_bridge); + rstp_enable = nm_setting_ovs_bridge_get_rstp_enable (s_ovs_bridge); + stp_enable = nm_setting_ovs_bridge_get_stp_enable (s_ovs_bridge); + } + + if (fail_mode) + json_object_set_new (row, "fail_mode", json_string (fail_mode)); + if (mcast_snooping_enable) + json_object_set_new (row, "mcast_snooping_enable", json_boolean (mcast_snooping_enable)); + if (rstp_enable) + json_object_set_new (row, "rstp_enable", json_boolean (rstp_enable)); + if (stp_enable) + json_object_set_new (row, "stp_enable", json_boolean (stp_enable)); + + json_object_set_new (row, "name", json_string (nm_connection_get_interface_name (bridge))); + json_object_set_new (row, "ports", json_pack ("[s, O]", "set", new_ports)); + json_object_set_new (row, "external_ids", + json_pack ("[s, [[s, s]]]", "map", + "NM.connection.uuid", nm_connection_get_uuid (bridge))); + + /* Create a new one. */ + json_array_append_new (params, + json_pack ("{s:s, s:s, s:o, s:s}", "op", "insert", "table", "Bridge", + "row", row, "uuid-name", "rowBridge")); +} + +/** + * _inc_next_cfg: + * + * Returns an mutate command that bumps next_cfg upon successful completion + * of the transaction it is in. + */ +static json_t * +_inc_next_cfg (const char *db_uuid) +{ + return json_pack ("{s:s, s:s, s:[[s, s, i]], s:[[s, s, [s, s]]]}", + "op", "mutate", "table", "Open_vSwitch", + "mutations", "next_cfg", "+=", 1, + "where", "_uuid", "==", "uuid", db_uuid); +} + +/** + * _add_interface: + * + * Adds an interface as specified by @interface connection, optionally creating + * a parent @port and @bridge if needed. + */ +static void +_add_interface (NMOvsdb *self, json_t *params, + NMConnection *bridge, NMConnection *port, NMConnection *interface) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + GHashTableIter iter; + const char *bridge_uuid; + const char *port_uuid; + const char *interface_uuid; + OpenvswitchBridge *ovs_bridge = NULL; + OpenvswitchPort *ovs_port = NULL; + OpenvswitchInterface *ovs_interface = NULL; + int pi; + int ii; + json_t *bridges, *new_bridges; + json_t *ports, *new_ports; + json_t *interfaces, *new_interfaces; + gboolean has_interface = FALSE; + + bridges = json_array (); + ports = json_array (); + interfaces = json_array (); + new_bridges = json_array (); + new_ports = json_array (); + new_interfaces = json_array (); + + g_hash_table_iter_init (&iter, priv->bridges); + while (g_hash_table_iter_next (&iter, (gpointer) &bridge_uuid, (gpointer) &ovs_bridge)) { + json_array_append_new (bridges, json_pack ("[s, s]", "uuid", bridge_uuid)); + + if ( g_strcmp0 (ovs_bridge->name, nm_connection_get_interface_name (bridge)) != 0 + || g_strcmp0 (ovs_bridge->connection_uuid, nm_connection_get_uuid (bridge)) != 0) + continue; + + for (pi = 0; pi < ovs_bridge->ports->len; pi++) { + port_uuid = g_ptr_array_index (ovs_bridge->ports, pi); + ovs_port = g_hash_table_lookup (priv->ports, port_uuid); + + json_array_append_new (ports, json_pack ("[s, s]", "uuid", port_uuid)); + + if ( g_strcmp0 (ovs_port->name, nm_connection_get_interface_name (port)) != 0 + || g_strcmp0 (ovs_port->connection_uuid, nm_connection_get_uuid (port)) != 0) + continue; + + for (ii = 0; ii < ovs_port->interfaces->len; ii++) { + interface_uuid = g_ptr_array_index (ovs_port->interfaces, ii); + ovs_interface = g_hash_table_lookup (priv->interfaces, interface_uuid); + + json_array_append_new (interfaces, json_pack ("[s, s]", "uuid", interface_uuid)); + + if ( g_strcmp0 (ovs_interface->name, nm_connection_get_interface_name (interface)) == 0 + && g_strcmp0 (ovs_interface->connection_uuid, nm_connection_get_uuid (interface)) == 0) + has_interface = TRUE; + } + + break; + } + + break; + } + + json_array_extend (new_bridges, bridges); + json_array_extend (new_ports, ports); + json_array_extend (new_interfaces, interfaces); + + if (json_array_size (interfaces) == 0) { + /* Need to create a port. */ + if (json_array_size (ports) == 0) { + /* Need to create a bridge. */ + _expect_ovs_bridges (params, priv->db_uuid, bridges); + json_array_append_new (new_bridges, json_pack ("[s, s]", "named-uuid", "rowBridge")); + _set_ovs_bridges (params, priv->db_uuid, new_bridges); + _insert_bridge (params, bridge, new_ports); + } else { + /* Bridge already exists. */ + g_return_if_fail (ovs_bridge); + _expect_bridge_ports (params, ovs_bridge->name, ports); + _set_bridge_ports (params, nm_connection_get_interface_name (bridge), new_ports); + } + + json_array_append_new (new_ports, json_pack ("[s, s]", "named-uuid", "rowPort")); + _insert_port (params, port, new_interfaces); + } else { + /* Port already exists */ + g_return_if_fail (ovs_port); + _expect_port_interfaces (params, ovs_port->name, interfaces); + _set_port_interfaces (params, nm_connection_get_interface_name (port), new_interfaces); + } + + if (!has_interface) { + _insert_interface (params, interface); + json_array_append_new (new_interfaces, json_pack ("[s, s]", "named-uuid", "rowInterface")); + } + + json_decref (interfaces); + json_decref (ports); + json_decref (bridges); + + json_decref (new_interfaces); + json_decref (new_ports); + json_decref (new_bridges); +} + +/** + * _delete_interface: + * + * Removes an interface of @ifname name, collecting empty ports and bridge + * if last item is removed from them. + */ +static void +_delete_interface (NMOvsdb *self, json_t *params, const char *ifname) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + GHashTableIter iter; + char *bridge_uuid; + char *port_uuid; + char *interface_uuid; + OpenvswitchBridge *ovs_bridge; + OpenvswitchPort *ovs_port; + OpenvswitchInterface *ovs_interface; + int pi; + int ii; + json_t *bridges, *new_bridges; + json_t *ports, *new_ports; + json_t *interfaces, *new_interfaces; + gboolean bridges_changed; + gboolean ports_changed; + gboolean interfaces_changed; + + bridges = json_array (); + new_bridges = json_array (); + bridges_changed = FALSE; + + g_hash_table_iter_init (&iter, priv->bridges); + while (g_hash_table_iter_next (&iter, (gpointer) &bridge_uuid, (gpointer) &ovs_bridge)) { + json_array_append_new (bridges, json_pack ("[s,s]", "uuid", bridge_uuid)); + + ports = json_array (); + new_ports = json_array (); + ports_changed = FALSE; + + for (pi = 0; pi < ovs_bridge->ports->len; pi++) { + port_uuid = g_ptr_array_index (ovs_bridge->ports, pi); + ovs_port = g_hash_table_lookup (priv->ports, port_uuid); + + json_array_append_new (ports, json_pack ("[s,s]", "uuid", port_uuid)); + + interfaces = json_array (); + new_interfaces = json_array (); + interfaces_changed = FALSE; + + for (ii = 0; ii < ovs_port->interfaces->len; ii++) { + interface_uuid = g_ptr_array_index (ovs_port->interfaces, ii); + ovs_interface = g_hash_table_lookup (priv->interfaces, interface_uuid); + + json_array_append_new (interfaces, json_pack ("[s,s]", "uuid", interface_uuid)); + + if (strcmp (ovs_interface->name, ifname) == 0) { + /* skip the interface */ + interfaces_changed = TRUE; + continue; + } + + json_array_append_new (new_interfaces, json_pack ("[s,s]", "uuid", interface_uuid)); + } + + if (json_array_size (new_interfaces) == 0) { + ports_changed = TRUE; + } else { + if (interfaces_changed) { + _expect_port_interfaces (params, ovs_port->name, interfaces); + _set_port_interfaces (params, ovs_port->name, new_interfaces); + } + json_array_append_new (new_ports, json_pack ("[s,s]", "uuid", port_uuid)); + } + + json_decref (interfaces); + json_decref (new_interfaces); + } + + if (json_array_size (new_ports) == 0) { + bridges_changed = TRUE; + } else { + if (ports_changed) { + _expect_bridge_ports (params, ovs_bridge->name, ports); + _set_bridge_ports (params, ovs_bridge->name, new_ports); + } + json_array_append_new (new_bridges, json_pack ("[s,s]", "uuid", bridge_uuid)); + } + + json_decref (ports); + json_decref (new_ports); + } + + if (bridges_changed) { + _expect_ovs_bridges (params, priv->db_uuid, bridges); + _set_ovs_bridges (params, priv->db_uuid, new_bridges); + } +} + +/** + * ovsdb_next_command: + * + * Translates a higher level operation (add/remove bridge/port) to a RFC 7047 + * command serialized into JSON ands sends it over to the database. + + * Only called when no command is waiting for a response, since the serialized + * command might depend on result of a previous one (add and remove need to + * include an up to date bridge list in their transactions to rule out races). + */ +static void +ovsdb_next_command (NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + OvsdbMethodCall *call = NULL; + char *cmd; + json_t *msg = NULL; + json_t *params; + + if (!priv->conn) + return; + if (!priv->calls->len) + return; + call = &g_array_index (priv->calls, OvsdbMethodCall, 0); + if (call->id != COMMAND_PENDING) + return; + call->id = priv->seq++; + + switch (call->command) { + case OVSDB_MONITOR: + msg = json_pack ("{s:i, s:s, s:[s, n, {" + " s:[{s:[s, s, s]}]," + " s:[{s:[s, s, s]}]," + " s:[{s:[s, s, s]}]," + " s:[{s:[]}]" + "}]}", + "id", call->id, + "method", "monitor", "params", "Open_vSwitch", + "Bridge", "columns", "name", "ports", "external_ids", + "Port", "columns", "name", "interfaces", "external_ids", + "Interface", "columns", "name", "type", "external_ids", + "Open_vSwitch", "columns"); + break; + case OVSDB_ADD_INTERFACE: + params = json_array (); + json_array_append_new (params, json_string ("Open_vSwitch")); + json_array_append_new (params, _inc_next_cfg (priv->db_uuid)); + + _add_interface (self, params, call->bridge, call->port, call->interface); + + msg = json_pack ("{s:i, s:s, s:o}", + "id", call->id, + "method", "transact", "params", params); + break; + case OVSDB_DEL_INTERFACE: + params = json_array (); + json_array_append_new (params, json_string ("Open_vSwitch")); + json_array_append_new (params, _inc_next_cfg (priv->db_uuid)); + + _delete_interface (self, params, call->ifname); + + msg = json_pack ("{s:i, s:s, s:o}", + "id", call->id, + "method", "transact", "params", params); + break; + } + + g_return_if_fail (msg); + _call_trace ("send", call, msg); + cmd = json_dumps (msg, 0); + + g_string_append (priv->output, cmd); + json_decref (msg); + free (cmd); + + ovsdb_write (self); +} + +/** + * _uuids_to_array: + * + * This tidies up the somewhat non-straightforward way ovsdb represents an array + * of UUID elements. The single element is a tuple (called <atom> in RFC7047), + * + * [ "uuid", "aa095ffb-e1f1-0fc4-8038-82c1ea7e4797" ] + * + * while the list of multiple UUIDs are turned into a set of such tuples ("atoms"): + * + * [ "set", [ [ "uuid", "aa095ffb-e1f1-0fc4-8038-82c1ea7e4797" ], + * [ "uuid", "185c93f6-0b39-424e-8587-77d074aa7ce0" ], ... ] ] + */ +static void +_uuids_to_array (GPtrArray *array, const json_t *items) +{ + const char *key; + json_t *value; + size_t index = 0; + json_t *set_value; + size_t set_index; + + while (index < json_array_size (items)) { + key = json_string_value (json_array_get (items, index)); + index++; + value = json_array_get (items, index); + index++; + + if (!value) + return; + + if (g_strcmp0 (key, "uuid") == 0 && json_is_string (value)) { + g_ptr_array_add (array, g_strdup (json_string_value (value))); + } else if (g_strcmp0 (key, "set") == 0 && json_is_array (value)) { + json_array_foreach (value, set_index, set_value) { + _uuids_to_array (array, set_value); + } + } + } +} + +static char * +_connection_uuid_from_external_ids (json_t *external_ids) +{ + json_t *value; + size_t index; + + if (g_strcmp0 ("map", json_string_value (json_array_get (external_ids, 0))) != 0) + return NULL; + + json_array_foreach (json_array_get (external_ids, 1), index, value) { + if (g_strcmp0 ("NM.connection.uuid", json_string_value (json_array_get (value, 0))) == 0) + return g_strdup (json_string_value (json_array_get (value, 1))); + } + + return NULL; +} + +/** + * ovsdb_got_update: + * + * Called when we've got an "update" method call (we asked for it with the monitor + * command). We use it to maintain a consistent view of bridge list regardless of + * whether the changes are done by us or externally. + */ +static void +ovsdb_got_update (NMOvsdb *self, json_t *msg) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + json_t *ovs = NULL; + json_t *bridge = NULL; + json_t *port = NULL; + json_t *interface = NULL; + json_t *items; + json_t *external_ids; + json_error_t json_error = { 0, }; + void *iter; + const char *name; + const char *key; + const char *type; + json_t *value; + OpenvswitchBridge *ovs_bridge; + OpenvswitchPort *ovs_port; + OpenvswitchInterface *ovs_interface; + + if (json_unpack_ex (msg, &json_error, 0, "{s?:o, s?:o, s?:o, s?:o}", + "Open_vSwitch", &ovs, + "Bridge", &bridge, + "Port", &port, + "Interface", &interface) == -1) { + /* This doesn't really have to be an error; the key might + * be missing if there really are no bridges present. */ + _LOGD ("Bad update: %s", json_error.text); + } + + if (ovs) { + iter = json_object_iter (ovs); + priv->db_uuid = g_strdup (iter ? json_object_iter_key (iter) : NULL); + } + + /* Interfaces */ + json_object_foreach (interface, key, value) { + gboolean old = FALSE; + gboolean new = FALSE; + + if (json_unpack (value, "{s:{}}", "old") == 0) + old = TRUE; + + if (json_unpack (value, "{s:{s:s, s:s, s:o}}", "new", + "name", &name, + "type", &type, + "external_ids", &external_ids) == 0) + new = TRUE; + + if (old) { + ovs_interface = g_hash_table_lookup (priv->interfaces, key); + if (!new || g_strcmp0 (ovs_interface->name, name) != 0) { + old = FALSE; + _LOGT ("removed an '%s' interface: %s%s%s", + ovs_interface->type, ovs_interface->name, + ovs_interface->connection_uuid ? ", " : "", + ovs_interface->connection_uuid ? ovs_interface->connection_uuid : ""); + if (g_strcmp0 (ovs_interface->type, "internal") == 0) { + /* Currently the factory only creates NMDevices for + * internal interfaces. Ignore the rest. */ + g_signal_emit (self, signals[DEVICE_REMOVED], 0, + ovs_interface->name, NM_DEVICE_TYPE_OVS_INTERFACE); + } + } + g_hash_table_remove (priv->interfaces, key); + } + + if (new) { + ovs_interface = g_slice_new (OpenvswitchInterface); + ovs_interface->name = g_strdup (name); + ovs_interface->type = g_strdup (type); + ovs_interface->connection_uuid = _connection_uuid_from_external_ids (external_ids); + if (old) { + _LOGT ("changed an '%s' interface: %s%s%s", type, ovs_interface->name, + ovs_interface->connection_uuid ? ", " : "", + ovs_interface->connection_uuid ? ovs_interface->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_CHANGED], 0, + "ovs-interface", ovs_interface->name); + } else { + _LOGT ("added an '%s' interface: %s%s%s", + ovs_interface->type, ovs_interface->name, + ovs_interface->connection_uuid ? ", " : "", + ovs_interface->connection_uuid ? ovs_interface->connection_uuid : ""); + if (g_strcmp0 (ovs_interface->type, "internal") == 0) { + /* Currently the factory only creates NMDevices for + * internal interfaces. Ignore the rest. */ + g_signal_emit (self, signals[DEVICE_ADDED], 0, + ovs_interface->name, NM_DEVICE_TYPE_OVS_INTERFACE); + } + } + g_hash_table_insert (priv->interfaces, g_strdup (key), ovs_interface); + } + } + + /* Ports */ + json_object_foreach (port, key, value) { + gboolean old = FALSE; + gboolean new = FALSE; + + if (json_unpack (value, "{s:{}}", "old") == 0) + old = TRUE; + + if (json_unpack (value, "{s:{s:s, s:o, s:o}}", "new", + "name", &name, + "external_ids", &external_ids, + "interfaces", &items) == 0) + new = TRUE; + + if (old) { + ovs_port = g_hash_table_lookup (priv->ports, key); + if (!new || g_strcmp0 (ovs_port->name, name) != 0) { + old = FALSE; + _LOGT ("removed a port: %s%s%s", ovs_port->name, + ovs_port->connection_uuid ? ", " : "", + ovs_port->connection_uuid ? ovs_port->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_REMOVED], 0, + ovs_port->name, NM_DEVICE_TYPE_OVS_PORT); + } + g_hash_table_remove (priv->ports, key); + } + + if (new) { + ovs_port = g_slice_new (OpenvswitchPort); + ovs_port->name = g_strdup (name); + ovs_port->connection_uuid = _connection_uuid_from_external_ids (external_ids); + ovs_port->interfaces = g_ptr_array_new_with_free_func (g_free); + _uuids_to_array (ovs_port->interfaces, items); + if (old) { + _LOGT ("changed a port: %s%s%s", ovs_port->name, + ovs_port->connection_uuid ? ", " : "", + ovs_port->connection_uuid ? ovs_port->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_CHANGED], 0, + NM_SETTING_OVS_PORT_SETTING_NAME, ovs_port->name); + } else { + _LOGT ("added a port: %s%s%s", ovs_port->name, + ovs_port->connection_uuid ? ", " : "", + ovs_port->connection_uuid ? ovs_port->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_ADDED], 0, + ovs_port->name, NM_DEVICE_TYPE_OVS_PORT); + } + g_hash_table_insert (priv->ports, g_strdup (key), ovs_port); + } + } + + /* Bridges */ + json_object_foreach (bridge, key, value) { + gboolean old = FALSE; + gboolean new = FALSE; + + if (json_unpack (value, "{s:{}}", "old") == 0) + old = TRUE; + + if (json_unpack (value, "{s:{s:s, s:o, s:o}}", "new", + "name", &name, + "external_ids", &external_ids, + "ports", &items) == 0) + new = TRUE; + + if (old) { + ovs_bridge = g_hash_table_lookup (priv->bridges, key); + if (!new || g_strcmp0 (ovs_bridge->name, name) != 0) { + old = FALSE; + _LOGT ("removed a bridge: %s%s%s", ovs_bridge->name, + ovs_bridge->connection_uuid ? ", " : "", + ovs_bridge->connection_uuid ? ovs_bridge->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_REMOVED], 0, + ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE); + } + g_hash_table_remove (priv->bridges, key); + } + + if (new) { + ovs_bridge = g_slice_new (OpenvswitchBridge); + ovs_bridge->name = g_strdup (name); + ovs_bridge->connection_uuid = _connection_uuid_from_external_ids (external_ids); + ovs_bridge->ports = g_ptr_array_new_with_free_func (g_free); + _uuids_to_array (ovs_bridge->ports, items); + if (old) { + _LOGT ("changed a bridge: %s%s%s", ovs_bridge->name, + ovs_bridge->connection_uuid ? ", " : "", + ovs_bridge->connection_uuid ? ovs_bridge->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_CHANGED], 0, + NM_SETTING_OVS_BRIDGE_SETTING_NAME, ovs_bridge->name); + } else { + _LOGT ("added a bridge: %s%s%s", ovs_bridge->name, + ovs_bridge->connection_uuid ? ", " : "", + ovs_bridge->connection_uuid ? ovs_bridge->connection_uuid : ""); + g_signal_emit (self, signals[DEVICE_ADDED], 0, + ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE); + } + g_hash_table_insert (priv->bridges, g_strdup (key), ovs_bridge); + } + } + +} + +/** + * ovsdb_got_echo: + * + * Only implemented because the specification mandates it. Actual ovsdb hasn't been + * seen doing this. + */ +static void +ovsdb_got_echo (NMOvsdb *self, json_int_t id, json_t *data) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + json_t *msg; + char *reply; + gboolean output_was_empty; + + output_was_empty = priv->output->len == 0; + + msg = json_pack ("{s:I, s:O}", "id", id, "result", data); + reply = json_dumps (msg, 0); + g_string_append (priv->output, reply); + json_decref (msg); + free (reply); + + if (output_was_empty) + ovsdb_write (self); +} + +/** + * ovsdb_got_msg:: + * + * Called when when a complete JSON object was seen and unmarshalled. + * Either finishes a method call or processes a method call. + */ +static void +ovsdb_got_msg (NMOvsdb *self, json_t *msg) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + json_error_t json_error = { 0, }; + json_t *json_id = NULL; + gint64 id = -1; + const char *method = NULL; + json_t *params = NULL; + json_t *result = NULL; + json_t *error = NULL; + OvsdbMethodCall *call = NULL; + OvsdbMethodCallback callback; + gpointer user_data; + GError *local = NULL; + + if (json_unpack_ex (msg, &json_error, 0, "{s?:o, s?:s, s?:o, s?:o, s?:o}", + "id", &json_id, + "method", &method, + "params", ¶ms, + "result", &result, + "error", &error) == -1) { + _LOGW ("couldn't grok the message: %s", json_error.text); + ovsdb_disconnect (self); + return; + } + + if (json_is_number (json_id)) + id = json_integer_value (json_id); + + if (method) { + /* It's a method call! */ + if (!params) { + _LOGW ("a method call with no params: '%s'", method); + ovsdb_disconnect (self); + return; + } + + if (g_strcmp0 (method, "update") == 0) { + /* This is a update method call. */ + ovsdb_got_update (self, json_array_get (params, 1)); + } else if (g_strcmp0 (method, "echo") == 0) { + /* This is an echo request. */ + ovsdb_got_echo (self, id, params); + } else { + _LOGW ("got an unknown method call: '%s'", method); + } + return; + } + + if (id > -1) { + /* This is a response to a method call. */ + if (!priv->calls->len) { + _LOGE ("there are no queued calls expecting response %ld", id); + ovsdb_disconnect (self); + return; + } + call = &g_array_index (priv->calls, OvsdbMethodCall, 0); + if (call->id != id) { + _LOGE ("expected a response to call %ld, not %ld", call->id, id); + ovsdb_disconnect (self); + return; + } + /* Cool, we found a corresponsing call. Finish it. */ + + _call_trace ("response", call, msg); + + if (!json_is_null (error)) { + /* The response contains an error. */ + g_set_error (&local, G_IO_ERROR, G_IO_ERROR_FAILED, + "Error call to OVSDB returned an error: %s", + json_string_value (error)); + } + + callback = call->callback; + user_data = call->user_data; + g_array_remove_index (priv->calls, 0); + callback (self, result, local, user_data); + + /* Don't progress further commands in case the callback hit an error + * and disconnected us. */ + if (!priv->conn) + return; + + /* Now we're free to serialize and send the next command, if any. */ + ovsdb_next_command (self); + + return; + } + + + /* This is a message we are not interested in. */ + _LOGW ("got an unknown message, ignoring"); +} + +/*****************************************************************************/ + +/* Lower level marshalling and demarshalling of the JSON-RPC traffic on the + * ovsdb socket. */ + +static size_t +_json_callback (void *buffer, size_t buflen, void *user_data) +{ + NMOvsdb *self = NM_OVSDB (user_data); + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + + if (priv->bufp == priv->input->len) { + /* No more bytes buffered for decoding. */ + return 0; + } + + /* Pass one more byte to the JSON decoder. */ + *(char *)buffer = priv->input->str[priv->bufp]; + priv->bufp++; + + return (size_t)1; +} + +/** + * ovsdb_read_cb: + * + * Read out the data available from the ovsdb socket and try to deserialize + * the JSON. If we see a complete object, pass it upwards to ovsdb_got_msg(). + */ +static void +ovsdb_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + NMOvsdb *self = NM_OVSDB (user_data); + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + GInputStream *stream = G_INPUT_STREAM (source_object); + GError *error = NULL; + gssize size; + json_t *msg; + json_error_t json_error = { 0, }; + + size = g_input_stream_read_finish (stream, res, &error); + if (size == -1) { + _LOGW ("short read from ovsdb: %s", error->message); + g_clear_error (&error); + ovsdb_disconnect (self); + return; + } + + g_string_append_len (priv->input, priv->buf, size); + do { + priv->bufp = 0; + /* The callback always eats up only up to a single byte. This makes + * it possible for us to identify complete JSON objects in spite of + * us not knowing the length in advance. */ + msg = json_load_callback (_json_callback, self, JSON_DISABLE_EOF_CHECK, &json_error); + if (msg) { + ovsdb_got_msg (self, msg); + g_string_erase (priv->input, 0, priv->bufp); + } + json_decref (msg); + } while (msg); + + if (!priv->conn) + return; + + if (size) + ovsdb_read (self); +} + +static void +ovsdb_read (NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + + g_input_stream_read_async (g_io_stream_get_input_stream (G_IO_STREAM (priv->conn)), + priv->buf, sizeof(priv->buf), + G_PRIORITY_DEFAULT, NULL, ovsdb_read_cb, self); +} + +static void +ovsdb_write_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GOutputStream *stream = G_OUTPUT_STREAM (source_object); + NMOvsdb *self = NM_OVSDB (user_data); + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + GError *error = NULL; + gssize size; + + size = g_output_stream_write_finish (stream, res, &error); + if (size == -1) { + _LOGW ("short write to ovsdb: %s", error->message); + g_clear_error (&error); + ovsdb_disconnect (self); + return; + } + + if (!priv->conn) + return; + + g_string_erase (priv->output, 0, size); + + ovsdb_write (self); +} + +static void +ovsdb_write (NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + GOutputStream *stream; + + if (!priv->output->len) + return; + + stream = g_io_stream_get_output_stream (G_IO_STREAM (priv->conn)); + if (g_output_stream_has_pending (stream)) + return; + + g_output_stream_write_async (stream, + priv->output->str, priv->output->len, + G_PRIORITY_DEFAULT, NULL, ovsdb_write_cb, self); +} + +/*****************************************************************************/ + +/* Routines to maintain the ovsdb connection. */ + +/** + * ovsdb_disconnect: + * + * Clean up the internal state to the point equivalent to before connecting. + * Apart from clean shutdown this is a good response to unexpected trouble, + * since the next method call attempt a will trigger reconnect which hopefully + * puts us back in sync. + */ +static void +ovsdb_disconnect (NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + OvsdbMethodCall *call; + OvsdbMethodCallback callback; + gpointer user_data; + GError *error; + + _LOGD ("disconnecting from ovsdb"); + + while (priv->calls->len) { + error = NULL; + call = &g_array_index (priv->calls, OvsdbMethodCall, priv->calls->len - 1); + g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled"); + + callback = call->callback; + user_data = call->user_data; + g_array_remove_index (priv->calls, priv->calls->len - 1); + callback (self, NULL, error, user_data); + } + + priv->bufp = 0; + g_string_truncate (priv->input, 0); + g_string_truncate (priv->output, 0); + g_clear_object (&priv->client); + g_clear_object (&priv->conn); + g_clear_pointer (&priv->db_uuid, g_free); +} + +static void +_monitor_bridges_cb (NMOvsdb *self, json_t *result, GError *error, gpointer user_data) +{ + if (error) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + _LOGI ("%s", error->message); + ovsdb_disconnect (self); + } + + g_clear_error (&error); + return; + } + + /* Treat the first response the same as the subsequent "update" + * messages we eventually get. */ + ovsdb_got_update (self, result); +} + +static void +_client_connect_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GSocketClient *client = G_SOCKET_CLIENT (source_object); + NMOvsdb *self = NM_OVSDB (user_data); + NMOvsdbPrivate *priv; + GError *error = NULL; + GSocketConnection *conn; + + conn = g_socket_client_connect_finish (client, res, &error); + if (conn == NULL) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + _LOGI ("%s", error->message); + + ovsdb_disconnect (self); + g_clear_error (&error); + return; + } + + priv = NM_OVSDB_GET_PRIVATE (self); + priv->conn = conn; + g_clear_object (&priv->cancellable); + + ovsdb_read (self); + ovsdb_next_command (self); +} + +/** + * ovsdb_try_connect: + * + * Establish a connection to ovsdb unless it's already established or being + * established. Queues a monitor command as a very first one so that we're in + * sync when other commands are issued. + */ +static void +ovsdb_try_connect (NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + GSocketAddress *addr; + + if (priv->client) + return; + + /* XXX: This should probably be made configurable via NetworkManager.conf */ + addr = g_unix_socket_address_new (RUNSTATEDIR "/openvswitch/db.sock"); + + priv->client = g_socket_client_new (); + priv->cancellable = g_cancellable_new (); + g_socket_client_connect_async (priv->client, G_SOCKET_CONNECTABLE (addr), + priv->cancellable, _client_connect_cb, self); + g_object_unref (addr); + + /* Queue a monitor call before any other command, ensuring that we have an up + * to date view of existing bridged that we need for add and remove ops. */ + ovsdb_call_method (self, OVSDB_MONITOR, NULL, + NULL, NULL, NULL, _monitor_bridges_cb, NULL); +} + +/*****************************************************************************/ + +/* Public functions useful for NMDeviceOpenvswitch to maintain the life cycle of + * their ovsdb entries without having to deal with ovsdb complexities themselves. */ + +typedef struct { + NMOvsdbCallback callback; + gpointer user_data; +} OvsdbCall; + +static void +_transact_cb (NMOvsdb *self, json_t *result, GError *error, gpointer user_data) +{ + OvsdbCall *call = user_data; + const char *err; + const char *err_details; + size_t index; + json_t *value; + + if (error) + goto out; + + json_array_foreach (result, index, value) { + if (json_unpack (value, "{s:s, s:s}", "error", &err, "details", &err_details) == 0) { + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Error running the transaction: %s: %s", err, err_details); + goto out; + } + } + +out: + call->callback (error, call->user_data); + g_slice_free (OvsdbCall, call); +} + +void +nm_ovsdb_add_interface (NMOvsdb *self, + NMConnection *bridge, NMConnection *port, NMConnection *interface, + NMOvsdbCallback callback, gpointer user_data) +{ + OvsdbCall *call; + + call = g_slice_new (OvsdbCall); + call->callback = callback; + call->user_data = user_data; + + ovsdb_call_method (self, OVSDB_ADD_INTERFACE, NULL, + bridge, port, interface, _transact_cb, call); +} + +void +nm_ovsdb_del_interface (NMOvsdb *self, const char *ifname, + NMOvsdbCallback callback, gpointer user_data) +{ + OvsdbCall *call; + + call = g_slice_new (OvsdbCall); + call->callback = callback; + call->user_data = user_data; + + ovsdb_call_method (self, OVSDB_DEL_INTERFACE, ifname, + NULL, NULL, NULL, _transact_cb, call); +} + +/*****************************************************************************/ + +static void +_clear_call (gpointer data) +{ + OvsdbMethodCall *call = data; + + switch (call->command) { + case OVSDB_MONITOR: + break; + case OVSDB_ADD_INTERFACE: + g_clear_object (&call->bridge); + g_clear_object (&call->port); + g_clear_object (&call->interface); + break; + case OVSDB_DEL_INTERFACE: + g_clear_pointer (&call->ifname, g_free); + break; + } +} + +static void +_free_bridge (gpointer data) +{ + OpenvswitchBridge *ovs_bridge = data; + + g_free (ovs_bridge->name); + g_free (ovs_bridge->connection_uuid); + g_ptr_array_free (ovs_bridge->ports, TRUE); + g_slice_free (OpenvswitchBridge, ovs_bridge); +} + +static void +_free_port (gpointer data) +{ + OpenvswitchPort *ovs_port = data; + + g_free (ovs_port->name); + g_free (ovs_port->connection_uuid); + g_ptr_array_free (ovs_port->interfaces, TRUE); + g_slice_free (OpenvswitchPort, ovs_port); +} + +static void +_free_interface (gpointer data) +{ + OpenvswitchInterface *ovs_interface = data; + + g_free (ovs_interface->name); + g_free (ovs_interface->connection_uuid); + g_free (ovs_interface->type); + g_slice_free (OpenvswitchInterface, ovs_interface); +} + +static void +nm_ovsdb_init (NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + + priv->calls = g_array_new (FALSE, TRUE, sizeof (OvsdbMethodCall)); + g_array_set_clear_func (priv->calls, _clear_call); + priv->input = g_string_new (NULL); + priv->output = g_string_new (NULL); + priv->bridges = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, _free_bridge); + priv->ports = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, _free_port); + priv->interfaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, _free_interface); + + ovsdb_try_connect (self); +} + +static void +dispose (GObject *object) +{ + NMOvsdb *self = NM_OVSDB (object); + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self); + + ovsdb_disconnect (self); + + g_string_free (priv->input, TRUE); + priv->input = NULL; + g_string_free (priv->output, TRUE); + priv->output = NULL; + + if (priv->calls) { + g_array_free (priv->calls, TRUE); + priv->calls = NULL; + } + + g_clear_pointer (&priv->bridges, g_hash_table_destroy); + g_clear_pointer (&priv->ports, g_hash_table_destroy); + g_clear_pointer (&priv->interfaces, g_hash_table_destroy); + + g_cancellable_cancel (priv->cancellable); + g_clear_object (&priv->cancellable); + + G_OBJECT_CLASS (nm_ovsdb_parent_class)->dispose (object); +} + +static void +nm_ovsdb_class_init (NMOvsdbClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = dispose; + + signals[DEVICE_ADDED] = + g_signal_new (NM_OVSDB_DEVICE_ADDED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); + + signals[DEVICE_REMOVED] = + g_signal_new (NM_OVSDB_DEVICE_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); + + signals[DEVICE_CHANGED] = + g_signal_new (NM_OVSDB_DEVICE_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); +} diff --git a/src/devices/ovs/nm-ovsdb.h b/src/devices/ovs/nm-ovsdb.h new file mode 100644 index 0000000000..cf9fe2a21b --- /dev/null +++ b/src/devices/ovs/nm-ovsdb.h @@ -0,0 +1,50 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_OVSDB_H__ +#define __NETWORKMANAGER_OVSDB_H__ + +#define NM_TYPE_OVSDB (nm_ovsdb_get_type ()) +#define NM_OVSDB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_OVSDB, NMOvsdb)) +#define NM_OVSDB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_OVSDB, NMOvsdbClass)) +#define NM_IS_OVSDB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_OVSDB)) +#define NM_IS_OVSDB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_OVSDB)) +#define NM_OVSDB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_OVSDB, NMOvsdbClass)) + +#define NM_OVSDB_DEVICE_ADDED "device-added" +#define NM_OVSDB_DEVICE_REMOVED "device-removed" +#define NM_OVSDB_DEVICE_CHANGED "device-changed" + +typedef struct _NMOvsdb NMOvsdb; +typedef struct _NMOvsdbClass NMOvsdbClass; + +typedef void (*NMOvsdbCallback) (GError *error, gpointer user_data); + +NMOvsdb *nm_ovsdb_get (void); + +GType nm_ovsdb_get_type (void); + +void nm_ovsdb_add_interface (NMOvsdb *self, + NMConnection *bridge, NMConnection *port, NMConnection *interface, + NMOvsdbCallback callback, gpointer user_data); + +void nm_ovsdb_del_interface (NMOvsdb *self, const char *ifname, + NMOvsdbCallback callback, gpointer user_data); + +#endif /* __NETWORKMANAGER_OVSDB_H__ */ diff --git a/src/nm-manager.c b/src/nm-manager.c index 46214130b3..be64a868ba 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1270,22 +1270,38 @@ nm_manager_iface_for_uuid (NMManager *self, const char *uuid) return nm_connection_get_interface_name (NM_CONNECTION (connection)); } -gboolean -nm_manager_remove_device (NMManager *self, const char *ifname) +NMDevice * +nm_manager_get_device (NMManager *self, const char *ifname, NMDeviceType device_type) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GSList *iter; NMDevice *d; + g_return_val_if_fail (ifname, NULL); + g_return_val_if_fail (device_type != NM_DEVICE_TYPE_UNKNOWN, NULL); + for (iter = priv->devices; iter; iter = iter->next) { d = iter->data; - if (nm_streq0 (nm_device_get_iface (d), ifname)) { - remove_device (self, d, FALSE, FALSE); - return TRUE; - } + + if ( nm_device_get_device_type (d) == device_type + && nm_streq0 (nm_device_get_iface (d), ifname)) + return d; } - return FALSE; + return NULL; +} + +gboolean +nm_manager_remove_device (NMManager *self, const char *ifname, NMDeviceType device_type) +{ + NMDevice *d; + + d = nm_manager_get_device (self, ifname, device_type); + if (!d) + return FALSE; + + remove_device (self, d, FALSE, FALSE); + return TRUE; } /** @@ -2031,6 +2047,7 @@ device_ip_iface_changed (NMDevice *device, NMManager *self) { const char *ip_iface = nm_device_get_ip_iface (device); + NMDeviceType device_type = nm_device_get_device_type (device); GSList *iter; /* Remove NMDevice objects that are actually child devices of others, @@ -2043,6 +2060,7 @@ device_ip_iface_changed (NMDevice *device, if ( candidate != device && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0 + && nm_device_get_device_type (candidate) == device_type && nm_device_is_real (candidate)) { remove_device (self, candidate, FALSE, FALSE); break; @@ -2339,6 +2357,9 @@ platform_link_added (NMManager *self, gboolean compatible = TRUE; gs_free_error GError *error = NULL; + if (nm_device_get_link_type (candidate) != plink->type) + continue; + if (strcmp (nm_device_get_iface (candidate), plink->name)) continue; @@ -2346,6 +2367,7 @@ platform_link_added (NMManager *self, /* Ignore the link added event since there's already a realized * device with the link's name. */ + nm_device_update_from_platform_link (candidate, plink); return; } else if (nm_device_realize_start (candidate, plink, @@ -2476,6 +2498,8 @@ _platform_link_cb_idle (PlatformLinkCbData *data) _LOG2W (LOGD_DEVICE, device, "failed to unrealize: %s", error->message); g_clear_error (&error); remove_device (self, device, FALSE, TRUE); + } else { + nm_device_update_from_platform_link (device, NULL); } } else { /* Hardware and external devices always get removed when their kernel link is gone */ diff --git a/src/nm-manager.h b/src/nm-manager.h index fdcae121c5..622edb5b54 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -126,6 +126,11 @@ gboolean nm_manager_deactivate_connection (NMManager *manager, void nm_manager_set_capability (NMManager *self, NMCapability cap); -gboolean nm_manager_remove_device (NMManager *self, const char *ifname); +NMDevice * nm_manager_get_device (NMManager *self, + const char *ifname, + NMDeviceType device_type); +gboolean nm_manager_remove_device (NMManager *self, + const char *ifname, + NMDeviceType device_type); #endif /* __NETWORKMANAGER_MANAGER_H__ */ |