Commit 751f4da0 authored by Jan Grulich's avatar Jan Grulich

Update WireGuard to match NetworkManager 1.16 interface

Summary:
In NetworkManager 1.16 handling of WireGuard interfaces was changed
from a VPN add-on to a core interface type with a different API. This
plasma-nm update is intended to match that change including (but not
limited to) moving address specification to the IPv4 and IPv6 tabs and
the ability to add multiple Peers to an interface.

BUG: 405501

Reviewers: jgrulich, #vdg, ngraham

Reviewed By: jgrulich, #vdg, ngraham

Subscribers: ngraham, plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D20930
parent 5986ffbb
......@@ -345,6 +345,9 @@ PlasmaComponents.ListItem {
}
}
/* This generates the formatted text under the connection name
in the popup where the connections can be "Connect"ed and
"Disconnect"ed. */
function itemText() {
if (ConnectionState == PlasmaNM.Enums.Activating) {
if (Type == PlasmaNM.Enums.Vpn)
......
......@@ -25,6 +25,7 @@
#include "mobileconnectionwizard.h"
#include "uiutils.h"
#include "vpnuiplugin.h"
#include "settings/wireguardinterfacewidget.h"
// KDE
#include <KMessageBox>
......@@ -39,12 +40,14 @@
#include <NetworkManagerQt/ConnectionSettings>
#include <NetworkManagerQt/GsmSetting>
#include <NetworkManagerQt/Ipv4Setting>
#include <NetworkManagerQt/Ipv6Setting>
#include <NetworkManagerQt/Settings>
#include <NetworkManagerQt/Utils>
#include <NetworkManagerQt/VpnSetting>
#include <NetworkManagerQt/WiredSetting>
#include <NetworkManagerQt/WirelessSetting>
#include <NetworkManagerQt/WirelessDevice>
#include <NetworkManagerQt/WireguardSetting>
// Qt
#include <QFileDialog>
......@@ -349,6 +352,14 @@ void KCMNetworkmanagement::onRequestCreateConnection(int connectionType, const Q
connectionSettings->setAutoconnect(false);
}
}
if (type == NetworkManager::ConnectionSettings::WireGuard) {
NetworkManager::WireGuardSetting::Ptr wireguardSetting = connectionSettings->setting(NetworkManager::Setting::WireGuard).dynamicCast<NetworkManager::WireGuardSetting>();
NetworkManager::Ipv4Setting::Ptr ipv4Setting = connectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast<NetworkManager::Ipv4Setting>();
NetworkManager::Ipv6Setting::Ptr ipv6Setting = connectionSettings->setting(NetworkManager::Setting::Ipv6).dynamicCast<NetworkManager::Ipv6Setting>();
connectionSettings->setAutoconnect(false);
ipv4Setting->setMethod(NetworkManager::Ipv4Setting::Disabled);
ipv6Setting->setMethod(NetworkManager::Ipv6Setting::Ignored);
}
// Generate new UUID
connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid());
addConnection(connectionSettings);
......@@ -501,6 +512,22 @@ void KCMNetworkmanagement::importVpn()
const QString ext = QStringLiteral("*.") % fi.suffix();
qCDebug(PLASMA_NM) << "Importing VPN connection " << filename << "extension:" << ext;
// Handle WireGuard separately because it is different than all the other VPNs
if (WireGuardInterfaceWidget::supportedFileExtensions().contains(ext)) {
NMVariantMapMap connection = WireGuardInterfaceWidget::importConnectionSettings(filename);
NetworkManager::ConnectionSettings connectionSettings;
connectionSettings.fromMap(connection);
connectionSettings.setUuid(NetworkManager::ConnectionSettings::createNewUuid());
// qCDebug(PLASMA_NM) << "Converted connection:" << connectionSettings;
m_handler->addConnection(connectionSettings.toMap());
// qCDebug(PLASMA_NM) << "Adding imported connection under id:" << conId;
if (!connection.isEmpty()) {
return; // get out if the import produced at least some output
}
}
for (const KService::Ptr &service : services) {
VpnUiPlugin * vpnPlugin = service->createInstance<VpnUiPlugin>(this);
if (vpnPlugin && vpnPlugin->supportedFileExtensions().contains(ext)) {
......
......@@ -154,6 +154,8 @@ ListItem {
}
}
/* This generates the status description under each connection
in the list at the left side of the applet. */
function itemText() {
if (ConnectionState == PlasmaNM.Enums.Activated) {
return i18n("Connected")
......
......@@ -400,6 +400,9 @@ void Notification::onActiveConnectionStateChanged(NetworkManager::ActiveConnecti
case NetworkManager::ConnectionSettings::Wired:
iconName = QStringLiteral("network-wired-activated");
break;
case NetworkManager::ConnectionSettings::WireGuard:
iconName = QStringLiteral("network-vpn");
break;
default: // silence warning
break;
}
......
......@@ -35,6 +35,7 @@
#include <NetworkManagerQt/VpnSetting>
#include <NetworkManagerQt/WirelessSecuritySetting>
#include <NetworkManagerQt/WirelessSetting>
#include <NetworkManagerQt/WireguardSetting>
#include <QStringBuilder>
#include <QDialog>
......@@ -92,7 +93,6 @@ NMVariantMapMap SecretAgent::GetSecrets(const NMVariantMapMap &connection, const
m_calls << request;
processNext();
return NMVariantMapMap();
}
......@@ -360,6 +360,7 @@ bool SecretAgent::processGetSecrets(SecretsRequest &request) const
const bool userRequested = request.flags & UserRequested;
const bool allowInteraction = request.flags & AllowInteraction;
const bool isVpn = (setting->type() == NetworkManager::Setting::Vpn);
const bool isWireGuard = (setting->type() == NetworkManager::Setting::WireGuard);
if (isVpn) {
NetworkManager::VpnSetting::Ptr vpnSetting = connectionSettings->setting(NetworkManager::Setting::Vpn).dynamicCast<NetworkManager::VpnSetting>();
......@@ -399,7 +400,7 @@ bool SecretAgent::processGetSecrets(SecretsRequest &request) const
if (!secretsMap.isEmpty()) {
setting->secretsFromStringMap(secretsMap);
if (!isVpn && setting->needSecrets(requestNew).isEmpty()) {
if (!(isVpn || isWireGuard) && setting->needSecrets(requestNew).isEmpty()) {
// Enough secrets were retrieved from storage
request.connection[request.setting_name] = setting->secretsToMap();
sendSecrets(request.connection, request.message);
......@@ -411,7 +412,25 @@ bool SecretAgent::processGetSecrets(SecretsRequest &request) const
sendError(SecretAgent::NoSecrets, "Cannot authenticate", request.message);
emit secretsError(request.connection_path.path(), i18n("Authentication to %1 failed. Wrong password?", request.connection.value("connection").value("id").toString()));
return true;
} else if (isWireGuard && userRequested) { // Just return what we have
NMVariantMapMap result;
NetworkManager::WireGuardSetting::Ptr wireGuardSetting;
wireGuardSetting = connectionSettings->setting(NetworkManager::Setting::WireGuard).dynamicCast<NetworkManager::WireGuardSetting>();
//FIXME workaround when NM is asking for secrets which should be system-stored, if we send an empty map it
// won't ask for additional secrets with AllowInteraction flag which would display the authentication dialog
if (wireGuardSetting->secretsToMap().isEmpty()) {
// Insert an empty secrets map as it was before I fixed it in NetworkManagerQt to make sure NM will ask again
// with flags we need
QVariantMap secretsMap;
secretsMap.insert(QLatin1String("secrets"), QVariant::fromValue<NMStringMap>(NMStringMap()));
result.insert("wireguard", secretsMap);
} else {
result.insert("wireguard", wireGuardSetting->secretsToMap());
}
sendSecrets(result, request.message);
return true;
} else if (requestNew || (allowInteraction && !setting->needSecrets(requestNew).isEmpty()) || (allowInteraction && userRequested) || (isVpn && allowInteraction)) {
m_dialog = new PasswordDialog(connectionSettings, request.flags, request.setting_name);
connect(m_dialog, &PasswordDialog::accepted, this, &SecretAgent::dialogAccepted);
connect(m_dialog, &PasswordDialog::rejected, this, &SecretAgent::dialogRejected);
......
......@@ -282,6 +282,9 @@ void ConnectionIcon::setStates()
if (activeConnection->state() == NetworkManager::ActiveConnection::Activating && UiUtils::isConnectionTypeSupported(activeConnection->type())) {
connecting = true;
}
if (activeConnection->type() == NetworkManager::ConnectionSettings::ConnectionType::WireGuard) {
vpn = true;
}
} else {
if (vpnConnection->state() == NetworkManager::VpnConnection::Activated) {
vpn = true;
......@@ -341,8 +344,11 @@ void ConnectionIcon::setIcons()
}
} else if (type == NetworkManager::ConnectionSettings::Vpn) {
connection = activeConnection;
} else if (type == NetworkManager::ConnectionSettings::WireGuard) {
connection = activeConnection;
} else if (type == NetworkManager::ConnectionSettings::Wired) {
if (connection && connection->type() != NetworkManager::ConnectionSettings::Vpn) {
if (connection && (connection->type() != NetworkManager::ConnectionSettings::Vpn
|| connection->type() != NetworkManager::ConnectionSettings::WireGuard)) {
connection = activeConnection;
}
} else if (type == NetworkManager::ConnectionSettings::Wireless) {
......@@ -394,6 +400,11 @@ void ConnectionIcon::setIcons()
setConnectionTooltipIcon("preferences-system-bluetooth");
}
}
} else if (type == 29) { // TODO change to WireGuard enum value once it is added
// WireGuard is a VPN but is not implemented
// in NetworkManager as a VPN, so we don't want to
// do anything just because it has a device
// associated with it.
} else {
// Ignore other devices (bond/bridge/team etc.)
setDisconnectedIcon();
......
......@@ -20,7 +20,6 @@
#include "networkstatus.h"
#include "uiutils.h"
#include <QDBusConnection>
#include <NetworkManagerQt/ActiveConnection>
......@@ -61,6 +60,9 @@ NetworkStatus::SortedConnectionType NetworkStatus::connectionTypeToSortedType(Ne
case NetworkManager::ConnectionSettings::Wireless:
return NetworkStatus::Wireless;
break;
case NetworkManager::ConnectionSettings::WireGuard:
return NetworkStatus::Wireguard;
break;
default:
return NetworkStatus::Other;
break;
......@@ -169,7 +171,8 @@ void NetworkStatus::changeActiveConnections()
if (!active->devices().isEmpty() && UiUtils::isConnectionTypeSupported(active->type())) {
NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(active->devices().first());
if (device && device->type() != NetworkManager::Device::Generic && device->type() <= NetworkManager::Device::Team) {
if (device && ((device->type() != NetworkManager::Device::Generic && device->type() <= NetworkManager::Device::Team)
|| device->type() == 29)) { // TODO: Change to WireGuard enum value when it is added
bool connecting = false;
bool connected = false;
QString conType;
......@@ -198,6 +201,11 @@ void NetworkStatus::changeActiveConnections()
}
}
if (active->type() == NetworkManager::ConnectionSettings::ConnectionType::WireGuard) {
conType = i18n("WireGuard");
connected = true;
}
NetworkManager::Connection::Ptr connection = active->connection();
if (connecting) {
status = i18n("Connecting to %1", connection->name());
......
......@@ -47,6 +47,7 @@ public:
Infiniband,
OLPCMesh,
Bluetooth,
Wireguard,
Vpn,
Other };
......
......@@ -19,6 +19,9 @@ set(plasmanm_editor_SRCS
settings/wifisecurity.cpp
settings/wiredconnectionwidget.cpp
settings/wiredsecurity.cpp
settings/wireguardinterfacewidget.cpp
settings/wireguardtabwidget.cpp
settings/wireguardpeerwidget.cpp
widgets/advancedpermissionswidget.cpp
widgets/bssidcombobox.cpp
......@@ -41,6 +44,7 @@ set(plasmanm_editor_SRCS
simpleipv4addressvalidator.cpp
simpleipv6addressvalidator.cpp
simpleiplistvalidator.cpp
wireguardkeyvalidator.cpp
vpnuiplugin.cpp
../configuration.cpp
......@@ -75,6 +79,9 @@ ki18n_wrap_ui(plasmanm_editor_SRCS
settings/ui/wifisecurity.ui
settings/ui/wiredconnectionwidget.ui
settings/ui/wiredsecurity.ui
settings/ui/wireguardinterfacewidget.ui
settings/ui/wireguardtabwidget.ui
settings/ui/wireguardpeerwidget.ui
widgets/ui/advancedpermissionswidget.ui
widgets/ui/ipv4routes.ui
......@@ -89,6 +96,7 @@ PUBLIC
KF5::NetworkManagerQt
KF5::WidgetsAddons
KF5::Completion
KF5::ConfigWidgets
Qt5::Widgets
PRIVATE
Qt5::Network
......
......@@ -39,6 +39,7 @@
#include "settings/wifisecurity.h"
#include "settings/wiredconnectionwidget.h"
#include "settings/wiredsecurity.h"
#include "settings/wireguardinterfacewidget.h"
#include "vpnuiplugin.h"
#include <NetworkManagerQt/ActiveConnection>
......@@ -132,6 +133,8 @@ NMVariantMapMap ConnectionEditorBase::setting() const
connectionSettings->fromMap(settings);
connectionSettings->setId(connectionName());
if (connectionSettings->connectionType() == NetworkManager::ConnectionSettings::WireGuard)
connectionSettings->setInterfaceName(connectionName());
connectionSettings->setUuid(m_connection->uuid());
if (connectionSettings->connectionType() == NetworkManager::ConnectionSettings::Wireless) {
......@@ -144,7 +147,6 @@ NMVariantMapMap ConnectionEditorBase::setting() const
}
}
}
return connectionSettings->toMap();
}
......@@ -240,6 +242,9 @@ void ConnectionEditorBase::initialize()
} else if (type == NetworkManager::ConnectionSettings::Team) { // Team
TeamWidget *teamWidget = new TeamWidget(m_connection->uuid(), m_connection->setting(NetworkManager::Setting::Team), this);
addSettingWidget(teamWidget, i18n("Team"));
} else if (type == NetworkManager::ConnectionSettings::WireGuard) { // WireGuard
WireGuardInterfaceWidget *wireGuardInterfaceWidget = new WireGuardInterfaceWidget(m_connection->setting(NetworkManager::Setting::WireGuard), this);
addSettingWidget(wireGuardInterfaceWidget, i18n("WireGuard Interface"));
} else if (type == NetworkManager::ConnectionSettings::Vpn) { // VPN
QString error;
VpnUiPlugin *vpnPlugin = nullptr;
......@@ -284,8 +289,8 @@ void ConnectionEditorBase::initialize()
|| type == NetworkManager::ConnectionSettings::Bond
|| type == NetworkManager::ConnectionSettings::Bridge
|| type == NetworkManager::ConnectionSettings::Vlan
|| (type == NetworkManager::ConnectionSettings::Vpn && serviceType == QLatin1String("org.freedesktop.NetworkManager.openvpn"))
|| (type == NetworkManager::ConnectionSettings::Vpn && serviceType == QLatin1String("org.freedesktop.NetworkManager.wireguard"))) && !m_connection->isSlave()) {
|| type == NetworkManager::ConnectionSettings::WireGuard
|| (type == NetworkManager::ConnectionSettings::Vpn && serviceType == QLatin1String("org.freedesktop.NetworkManager.openvpn"))) && !m_connection->isSlave()) {
IPv6Widget *ipv6Widget = new IPv6Widget(m_connection->setting(NetworkManager::Setting::Ipv6), this);
addSettingWidget(ipv6Widget, i18n("IPv6"));
}
......@@ -353,6 +358,13 @@ void ConnectionEditorBase::initialize()
setting = securitySetting->toMap();
settingName = QLatin1String("802-1x");
}
} else if (m_connection->connectionType() == NetworkManager::ConnectionSettings::WireGuard) {
NetworkManager::WireGuardSetting::Ptr securitySetting = connection->settings()->setting(NetworkManager::Setting::WireGuard).staticCast<NetworkManager::WireGuardSetting>();
if (securitySetting && !securitySetting->needSecrets().isEmpty()) {
requiredSecrets = securitySetting->needSecrets();
setting = securitySetting->toMap();
settingName = QLatin1String("wireguard");
}
} else if (m_connection->connectionType() == NetworkManager::ConnectionSettings::Wireless) {
NetworkManager::WirelessSecuritySetting::Ptr wifiSecuritySetting = connection->settings()->setting(NetworkManager::Setting::WirelessSecurity).staticCast<NetworkManager::WirelessSecuritySetting>();
if (wifiSecuritySetting &&
......
......@@ -29,7 +29,6 @@
#include <QDialog>
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <KUser>
#include <KAcceleratorManager>
#include <KLocalizedString>
......@@ -179,7 +178,8 @@ NMStringMap ConnectionWidget::vpnConnections() const
for (const NetworkManager::Connection::Ptr &conn : list) {
NetworkManager::ConnectionSettings::Ptr conSet = conn->settings();
if (conSet->connectionType() == NetworkManager::ConnectionSettings::Vpn) {
if (conSet->connectionType() == NetworkManager::ConnectionSettings::Vpn
|| conSet->connectionType() == NetworkManager::ConnectionSettings::WireGuard) {
// qCDebug(PLASMA_NM) << "Found VPN" << conSet->id() << conSet->uuid();
result.insert(conSet->uuid(), conSet->id());
}
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WireGuardProp</class>
<class>WireGuardInterfaceProp</class>
<widget class="QWidget" name="WireGuardProp">
<property name="geometry">
<rect>
......@@ -21,47 +21,13 @@
</property>
<layout class="QFormLayout" name="form1Layout">
<item row="0" column="0">
<widget class="QLabel" name="addressIPv4Label">
<property name="text">
<string>Address (IPv4):</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="addressIPv4LineEdit">
<property name="toolTip">
<string>IPv4 Internet address with
CIDR (example: 10.22.13.123/32)
assigned to the local interface.
IPv4 or IPv6 address (or both) required</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="addressIPv6Label">
<property name="text">
<string>Address (IPv6):</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="addressIPv6LineEdit">
<property name="toolTip">
<string>IPv6 Internet address with
CIDR assigned to the local interface.
(example: fc00:aaaa:aaaa:aa03::1bc9/128)
IPv4 or IPv6 address (or both) required</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="privateKeyLabel">
<property name="text">
<string>Private key:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="0" column="1">
<widget class="PasswordField" name="privateKeyLineEdit">
<property name="toolTip">
<string>Required.
......@@ -69,88 +35,76 @@ A base64 private key generated by wg genkey.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Peer</string>
</property>
<layout class="QFormLayout" name="form2Layout">
<item row="0" column="0">
<widget class="QLabel" name="publicKeyLabel">
<item row="1" column="0">
<widget class="QLabel" name="listenPortLabel">
<property name="text">
<string>Public key:</string>
<string>Listen port:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="publicKeyLineEdit">
<item row="1" column="1">
<widget class="QLineEdit" name="listenPortLineEdit">
<property name="toolTip">
<string>Required.
A base64 public key calculated by wg pubkey
from a private key, and usually transmitted
out of band to the author of the configuration file.</string>
<string>Optional.
Listen port number. Chosen randomly if left as 'Automatic'.</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="allowedIPsLabel">
<item row="2" column="0">
<widget class="QLabel" name="fwmarkLabel">
<property name="text">
<string>Allowed IPs:</string>
<string>fwmark:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="allowedIPsLineEdit">
<item row="2" column="1">
<widget class="QLineEdit" name="fwmarkLineEdit">
<property name="toolTip">
<string>Required.
A comma-separated list of IP (v4 or v6) addresses
with CIDR masks from which incoming traffic for
this peer is allowed and to which outgoing traffic
for this peer is directed. The catch-all 0.0.0.0/0
may be specified for matching all IPv4 addresses,
and ::/0 may be specified for matching all IPv6 addresses.</string>
<string>Optional.
An fwmark for outgoing packets. If set to 0 or 'off', this
option is disabled. May be specified in hexadecimal by
prepending '0x'.</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="endpointAddressLabel">
<item row="3" column="0">
<widget class="QLabel" name="mtuLabel">
<property name="text">
<string>Endpoint Address:</string>
<string>MTU:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="endpointAddressLineEdit">
<item row="3" column="1">
<widget class="QLineEdit" name="mtuLineEdit">
<property name="toolTip">
<string>Optional.
An endpoint IP address or name.</string>
If not specified, the MTU is automatically determined
from the endpoint addresses or the system default route,
which is usually a sane choice. However, to manually
specify an MTU and to override this automatic discovery,
this value may be specified explicitly.</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="endpointPortLabel">
<item row="4" column="0">
<widget class="QLabel" name="peerRouteLabel">
<property name="text">
<string>Endpoint Port:</string>
<string>Autoroute peers:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="endpointPortLineEdit">
<item row="4" column="1">
<widget class="QCheckBox" name="peerRouteCheckBox">
<property name="toolTip">
<string>Optional.
An endpoint port number.</string>
<string>Whether to automatically add routes for the AllowedIPs ranges
of the peers.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
......@@ -167,14 +121,16 @@ An endpoint port number.</string>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnAdvanced">
<widget class="QPushButton" name="btnPeers">
<property name="text">
<string>Advanced...</string>
<string>Peers...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WireGuardPeersProp</class>
<widget class="QWidget" name="WireGuardProp">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>495</width>
<height>454</height>
</rect>
</property>
<layout class="QFormLayout" name="form2Layout">
<item row="1" column="0">
<widget class="QLabel" name="publicKeyLabel">
<property name="text">
<string>Public key:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="publicKeyLineEdit">
<property name="toolTip">
<string>Required.
A base64 public key calculated by wg pubkey
from a private key, and usually transmitted
out of band to the author of the configuration file.</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="allowedIPsLabel">
<property name="text">
<string>Allowed IPs:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="allowedIPsLineEdit">
<property name="toolTip">
<string>Required.
A comma-separated list of IP (v4 or v6) addresses
with CIDR masks from which incoming traffic for
this peer is allowed and to which outgoing traffic
for this peer is directed. The catch-all 0.0.0.0/0
may be specified for matching all IPv4 addresses,
and ::/0 may be specified for matching all IPv6 addresses.</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="endpointAddressLabel">
<property name="text">
<string>Endpoint address:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="endpointAddressLineEdit">
<property name="toolTip">
<string>Optional.
An endpoint for the connection. Can be an
IPv4 address, IPv6 address, or FQDN (fully
qualified domain name such as abc.com). If
present, Endpoint port must also be set.</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="endpointPortLabel">
<property name="text">
<string>Endpoint port:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="endpointPortLineEdit">
<property name="toolTip">
<string>Optional.
The port number of an endpoint. If present Endpoint
Address must also be set.</string>
</property>
</widget>