From: Mateusz Malicki Date: Tue, 17 Mar 2015 14:55:16 +0000 (+0100) Subject: Possibility to remove ipv4/ipv6 addresses from interface X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=70a84ae9b6a4d2f7288cfce840b5b536fa917e2c;p=platform%2Fcore%2Fsecurity%2Fvasum.git Possibility to remove ipv4/ipv6 addresses from interface [Feature] Possibility to remove ipv4/ipv6 addresses from interface [Cause] N/A [Solution] Implemented: vsm_netdev_del_ipv4_addr, vsm_netdev_del_ipv6_addr [Verification] Build, run test Change-Id: If1e43c73f443e5480e5f1794a1d1f29fa78c3dd3 --- diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 1de4de0..0c5482a 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -744,6 +744,44 @@ VsmStatus Client::vsm_netdev_set_ipv6_addr(const char* zone, } } +VsmStatus Client::vsm_netdev_del_ipv4_addr(const char* zone, + const char* netdevId, + struct in_addr* addr, + int prefix) noexcept +{ + std::string ip; + try { + //CIDR notation + ip = toString(addr) + "/" + to_string(prefix); + } catch(const std::exception& ex) { + mStatus = Status(VSMCLIENT_INVALID_ARGUMENT, ex.what()); + return vsm_get_status(); + } + + GVariant* args_in = g_variant_new("(sss)", zone, netdevId, ip.c_str()); + return callMethod(HOST_INTERFACE, api::host::METHOD_DELETE_NETDEV_IP_ADDRESS, args_in); +} + +VsmStatus Client::vsm_netdev_del_ipv6_addr(const char* zone, + const char* netdevId, + struct in6_addr* addr, + int prefix) noexcept +{ + + std::string ip; + try { + //CIDR notation + ip = toString(addr) + "/" + to_string(prefix); + } catch(const std::exception& ex) { + mStatus = Status(VSMCLIENT_INVALID_ARGUMENT, ex.what()); + return vsm_get_status(); + } + + GVariant* args_in = g_variant_new("(sss)", zone, netdevId, ip.c_str()); + return callMethod(HOST_INTERFACE, api::host::METHOD_DELETE_NETDEV_IP_ADDRESS, args_in); +} + + VsmStatus Client::vsm_netdev_up(const char* zone, const char* netdevId) noexcept { try { diff --git a/client/vasum-client-impl.hpp b/client/vasum-client-impl.hpp index 1493a00..4e01966 100644 --- a/client/vasum-client-impl.hpp +++ b/client/vasum-client-impl.hpp @@ -235,6 +235,22 @@ public: int prefix) noexcept; /** + * @see ::vsm_netdev_del_ipv4_addr + */ + VsmStatus vsm_netdev_del_ipv4_addr(const char* zone, + const char* netdevId, + struct in_addr* addr, + int prefix) noexcept; + + /** + * @see ::vsm_netdev_del_ipv6_addr + */ + VsmStatus vsm_netdev_del_ipv6_addr(const char* zone, + const char* netdevId, + struct in6_addr* addr, + int prefix) noexcept; + + /** * @see ::vsm_netdev_up */ VsmStatus vsm_netdev_up(const char* zone, const char* netdevId) noexcept; diff --git a/client/vasum-client.cpp b/client/vasum-client.cpp index 9c0ca60..c12e8c6 100644 --- a/client/vasum-client.cpp +++ b/client/vasum-client.cpp @@ -250,6 +250,24 @@ API VsmStatus vsm_netdev_set_ipv6_addr(VsmClient client, return getClient(client).vsm_netdev_set_ipv6_addr(zone, netdevId, addr, prefix); } +API VsmStatus vsm_netdev_del_ipv4_addr(VsmClient client, + const char* zone, + const char* netdevId, + struct in_addr* addr, + int prefix) +{ + return getClient(client).vsm_netdev_del_ipv4_addr(zone, netdevId, addr, prefix); +} + +API VsmStatus vsm_netdev_del_ipv6_addr(VsmClient client, + const char* zone, + const char* netdevId, + struct in6_addr* addr, + int prefix) +{ + return getClient(client).vsm_netdev_del_ipv6_addr(zone, netdevId, addr, prefix); +} + API VsmStatus vsm_netdev_up(VsmClient client, const char* zone, const char* netdevId) diff --git a/client/vasum-client.h b/client/vasum-client.h index 18af990..c252b32 100644 --- a/client/vasum-client.h +++ b/client/vasum-client.h @@ -552,6 +552,38 @@ VsmStatus vsm_netdev_set_ipv6_addr(VsmClient client, int prefix); /** + * Remove ipv4 address from netdev + * + * @param[in] client vasum-server's client + * @param[in] zone zone name + * @param[in] netdevId network device id + * @param[in] addr ipv4 address + * @param[in] prefix bit-length of the network prefix + * @return status of this function call + */ +VsmStatus vsm_netdev_del_ipv4_addr(VsmClient client, + const char* zone, + const char* netdevId, + struct in_addr* addr, + int prefix); + +/** + * Remove ipv6 address from netdev + * + * @param[in] client vasum-server's client + * @param[in] zone zone name + * @param[in] netdevId network device id + * @param[in] addr ipv6 address + * @param[in] prefix bit-length of the network prefix + * @return status of this function call + */ +VsmStatus vsm_netdev_del_ipv6_addr(VsmClient client, + const char* zone, + const char* netdevId, + struct in6_addr* addr, + int prefix); + +/** * Turn up a network device in the zone * * @param[in] client vasum-server's client @@ -750,9 +782,9 @@ VsmStatus vsm_remove_declaration(VsmClient client, * @param data custom user's data pointer passed to vsm_add_notification_callback() */ typedef void (*VsmNotificationCallback)(const char* zone, - const char* application, - const char* message, - void* data); + const char* application, + const char* message, + void* data); /** * Send message to active zone. * diff --git a/common/api/messages.hpp b/common/api/messages.hpp index a567975..de193bd 100644 --- a/common/api/messages.hpp +++ b/common/api/messages.hpp @@ -146,6 +146,19 @@ struct CreateNetDevMacvlanIn { ) }; +struct DeleteNetdevIpAddressIn { + std::string zone; + std::string netdev; + std::string ip; + + CONFIG_REGISTER + ( + zone, + netdev, + ip + ) +}; + struct DeclareFileIn { std::string zone; int32_t type; diff --git a/server/host-connection.cpp b/server/host-connection.cpp index 993b63d..f3cbfa0 100644 --- a/server/host-connection.cpp +++ b/server/host-connection.cpp @@ -167,6 +167,11 @@ void HostConnection::setDestroyNetdevCallback(const DestroyNetdevCallback& callb mDestroyNetdevCallback = callback; } +void HostConnection::setDeleleNetdevIpAddressCallback(const DeleteNetdevIpAddressCallback& callback) +{ + mDeleteNetdevIpAddressCallback = callback; +} + void HostConnection::setDeclareFileCallback(const DeclareFileCallback& callback) { mDeclareFileCallback = callback; @@ -397,6 +402,15 @@ void HostConnection::onMessageCall(const std::string& objectPath, } } + if (methodName == api::host::METHOD_DELETE_NETDEV_IP_ADDRESS) { + api::DeleteNetdevIpAddressIn data; + config::loadFromGVariant(parameters, data); + if (mDeleteNetdevIpAddressCallback) { + auto rb = std::make_shared>(result); + mDeleteNetdevIpAddressCallback(data, rb); + } + } + if (methodName == api::host::METHOD_DECLARE_FILE) { api::DeclareFileIn data; config::loadFromGVariant(parameters, data); diff --git a/server/host-connection.hpp b/server/host-connection.hpp index 4cb1c8e..4ad07c8 100644 --- a/server/host-connection.hpp +++ b/server/host-connection.hpp @@ -82,6 +82,9 @@ public: typedef std::function CreateNetdevPhysCallback; + typedef std::function DeleteNetdevIpAddressCallback; typedef std::function DestroyNetdevCallback; @@ -194,6 +197,11 @@ public: void setDestroyNetdevCallback(const DestroyNetdevCallback& callback); /** + * Register a callback called to remove ip address from netdev + */ + void setDeleleNetdevIpAddressCallback(const DeleteNetdevIpAddressCallback& callback); + + /** * Register a callback called to declare file */ void setDeclareFileCallback(const DeclareFileCallback& callback); @@ -291,6 +299,7 @@ private: CreateNetdevMacvlanCallback mCreateNetdevMacvlanCallback; CreateNetdevPhysCallback mCreateNetdevPhysCallback; DestroyNetdevCallback mDestroyNetdevCallback; + DeleteNetdevIpAddressCallback mDeleteNetdevIpAddressCallback; DeclareFileCallback mDeclareFileCallback; DeclareMountCallback mDeclareMountCallback; DeclareLinkCallback mDeclareLinkCallback; diff --git a/server/host-dbus-definitions.hpp b/server/host-dbus-definitions.hpp index 2ac5745..1e6fecc 100644 --- a/server/host-dbus-definitions.hpp +++ b/server/host-dbus-definitions.hpp @@ -32,39 +32,40 @@ namespace vasum { namespace api { namespace host { -const std::string BUS_NAME = "org.tizen.vasum.host"; -const std::string OBJECT_PATH = "/org/tizen/vasum/host"; -const std::string INTERFACE = "org.tizen.vasum.host.manager"; +const std::string BUS_NAME = "org.tizen.vasum.host"; +const std::string OBJECT_PATH = "/org/tizen/vasum/host"; +const std::string INTERFACE = "org.tizen.vasum.host.manager"; -const std::string ERROR_ZONE_NOT_RUNNING = "org.tizen.vasum.host.Error.ZonesNotRunning"; +const std::string ERROR_ZONE_NOT_RUNNING = "org.tizen.vasum.host.Error.ZonesNotRunning"; -const std::string METHOD_GET_ZONE_DBUSES = "GetZoneDbuses"; -const std::string METHOD_GET_ZONE_ID_LIST = "GetZoneIds"; -const std::string METHOD_GET_ACTIVE_ZONE_ID = "GetActiveZoneId"; -const std::string METHOD_GET_ZONE_INFO = "GetZoneInfo"; -const std::string METHOD_SET_NETDEV_ATTRS = "SetNetdevAttrs"; -const std::string METHOD_GET_NETDEV_ATTRS = "GetNetdevAttrs"; -const std::string METHOD_GET_NETDEV_LIST = "GetNetdevList"; -const std::string METHOD_CREATE_NETDEV_VETH = "CreateNetdevVeth"; -const std::string METHOD_CREATE_NETDEV_MACVLAN = "CreateNetdevMacvlan"; -const std::string METHOD_CREATE_NETDEV_PHYS = "CreateNetdevPhys"; -const std::string METHOD_DESTROY_NETDEV = "DestroyNetdev"; -const std::string METHOD_DECLARE_FILE = "DeclareFile"; -const std::string METHOD_DECLARE_MOUNT = "DeclareMount"; -const std::string METHOD_DECLARE_LINK = "DeclareLink"; -const std::string METHOD_GET_DECLARATIONS = "GetDeclarations"; -const std::string METHOD_REMOVE_DECLARATION = "RemoveDeclaration"; -const std::string METHOD_SET_ACTIVE_ZONE = "SetActiveZone"; -const std::string METHOD_CREATE_ZONE = "CreateZone"; -const std::string METHOD_DESTROY_ZONE = "DestroyZone"; -const std::string METHOD_SHUTDOWN_ZONE = "ShutdownZone"; -const std::string METHOD_START_ZONE = "StartZone"; -const std::string METHOD_LOCK_ZONE = "LockZone"; -const std::string METHOD_UNLOCK_ZONE = "UnlockZone"; -const std::string METHOD_GRANT_DEVICE = "GrantDevice"; -const std::string METHOD_REVOKE_DEVICE = "RevokeDevice"; +const std::string METHOD_GET_ZONE_DBUSES = "GetZoneDbuses"; +const std::string METHOD_GET_ZONE_ID_LIST = "GetZoneIds"; +const std::string METHOD_GET_ACTIVE_ZONE_ID = "GetActiveZoneId"; +const std::string METHOD_GET_ZONE_INFO = "GetZoneInfo"; +const std::string METHOD_SET_NETDEV_ATTRS = "SetNetdevAttrs"; +const std::string METHOD_GET_NETDEV_ATTRS = "GetNetdevAttrs"; +const std::string METHOD_GET_NETDEV_LIST = "GetNetdevList"; +const std::string METHOD_CREATE_NETDEV_VETH = "CreateNetdevVeth"; +const std::string METHOD_CREATE_NETDEV_MACVLAN = "CreateNetdevMacvlan"; +const std::string METHOD_CREATE_NETDEV_PHYS = "CreateNetdevPhys"; +const std::string METHOD_DESTROY_NETDEV = "DestroyNetdev"; +const std::string METHOD_DELETE_NETDEV_IP_ADDRESS = "DeleteNetdevIpAddress"; +const std::string METHOD_DECLARE_FILE = "DeclareFile"; +const std::string METHOD_DECLARE_MOUNT = "DeclareMount"; +const std::string METHOD_DECLARE_LINK = "DeclareLink"; +const std::string METHOD_GET_DECLARATIONS = "GetDeclarations"; +const std::string METHOD_REMOVE_DECLARATION = "RemoveDeclaration"; +const std::string METHOD_SET_ACTIVE_ZONE = "SetActiveZone"; +const std::string METHOD_CREATE_ZONE = "CreateZone"; +const std::string METHOD_DESTROY_ZONE = "DestroyZone"; +const std::string METHOD_SHUTDOWN_ZONE = "ShutdownZone"; +const std::string METHOD_START_ZONE = "StartZone"; +const std::string METHOD_LOCK_ZONE = "LockZone"; +const std::string METHOD_UNLOCK_ZONE = "UnlockZone"; +const std::string METHOD_GRANT_DEVICE = "GrantDevice"; +const std::string METHOD_REVOKE_DEVICE = "RevokeDevice"; -const std::string SIGNAL_ZONE_DBUS_STATE = "ZoneDbusState"; +const std::string SIGNAL_ZONE_DBUS_STATE = "ZoneDbusState"; const std::string DEFINITION = @@ -125,6 +126,11 @@ const std::string DEFINITION = " " " " " " + " " + " " + " " + " " + " " " " " " " " diff --git a/server/netdev.cpp b/server/netdev.cpp index da58641..38ccb88 100644 --- a/server/netdev.cpp +++ b/server/netdev.cpp @@ -120,6 +120,11 @@ uint32_t getInterfaceIndex(const string& name, pid_t nsPid) { return infoPeer.ifi_index; } +int getIpFamily(const std::string& ip) +{ + return ip.find(':') == std::string::npos ? AF_INET : AF_INET6; +} + void validateNetdevName(const string& name) { if (name.size() <= 1 || name.size() >= IFNAMSIZ) { @@ -331,6 +336,36 @@ void setIpAddresses(const pid_t nsPid, send(nlm, nsPid); } +void deleteIpAddress(const pid_t nsPid, + const uint32_t index, + const std::string& ip, + int prefixlen, + int family) +{ + NetlinkMessage nlm(RTM_DELADDR, NLM_F_REQUEST | NLM_F_ACK); + ifaddrmsg infoAddr = utils::make_clean(); + infoAddr.ifa_family = family; + infoAddr.ifa_index = index; + infoAddr.ifa_prefixlen = prefixlen; + nlm.put(infoAddr); + if (family == AF_INET6) { + in6_addr addr6; + if (inet_pton(AF_INET6, ip.c_str(), &addr6) != 1) { + throw VasumException("Can't delete ipv6 address"); + }; + nlm.put(IFA_ADDRESS, addr6); + nlm.put(IFA_LOCAL, addr6); + } else { + assert(family == AF_INET); + in_addr addr4; + if (inet_pton(AF_INET, ip.c_str(), &addr4) != 1) { + throw VasumException("Can't delete ipv4 address"); + }; + nlm.put(IFA_LOCAL, addr4); + } + send(nlm, nsPid); +} + } // namespace void createVeth(const pid_t& nsPid, const string& nsDev, const string& hostDev) @@ -565,6 +600,27 @@ void setAttrs(const pid_t nsPid, const std::string& netdev, const Attrs& attrs) setIp(ipv6, infoPeer.ifi_index, AF_INET6); } +void deleteIpAddress(const pid_t nsPid, + const std::string& netdev, + const std::string& ip) +{ + uint32_t index = getInterfaceIndex(netdev, nsPid); + size_t slash = ip.find('/'); + if (slash == string::npos) { + LOGE("Wrong address format: it is not CIDR notation: can't find '/'"); + throw VasumException("Wrong address format"); + } + int prefixlen = 0; + try { + prefixlen = stoi(ip.substr(slash + 1)); + } catch (const std::exception& ex) { + LOGE("Wrong address format: invalid prefixlen"); + throw VasumException("Wrong address format: invalid prefixlen"); + } + deleteIpAddress(nsPid, index, ip.substr(0, slash), prefixlen, getIpFamily(ip)); +} + + } //namespace netdev } //namespace vasum diff --git a/server/netdev.hpp b/server/netdev.hpp index 5e5821d..b3c574d 100644 --- a/server/netdev.hpp +++ b/server/netdev.hpp @@ -57,6 +57,11 @@ void createBridge(const std::string& netdev); Attrs getAttrs(const pid_t nsPid, const std::string& netdev); void setAttrs(const pid_t nsPid, const std::string& netdev, const Attrs& attrs); +/** + * Remove ipv4/ipv6 address from interface + */ +void deleteIpAddress(const pid_t nsPid, const std::string& netdev, const std::string& ip); + } //namespace netdev } //namespace vasum diff --git a/server/zone-admin.cpp b/server/zone-admin.cpp index b584279..3098f29 100644 --- a/server/zone-admin.cpp +++ b/server/zone-admin.cpp @@ -322,4 +322,9 @@ std::vector ZoneAdmin::getNetdevList() return netdev::listNetdev(mZone.getInitPid()); } +void ZoneAdmin::deleteNetdevIpAddress(const std::string& netdev, const std::string& ip) +{ + netdev::deleteIpAddress(mZone.getInitPid(), netdev, ip); +} + } // namespace vasum diff --git a/server/zone-admin.hpp b/server/zone-admin.hpp index db2b0f2..d1b179d 100644 --- a/server/zone-admin.hpp +++ b/server/zone-admin.hpp @@ -169,6 +169,11 @@ public: */ std::vector getNetdevList(); + /** + * Remove ipv4/ipv6 address from network device + */ + void deleteNetdevIpAddress(const std::string& netdev, const std::string& ip); + private: const ZoneConfig& mConfig; const ZoneDynamicConfig& mDynamicConfig; diff --git a/server/zone.cpp b/server/zone.cpp index b0c3c28..e26da52 100644 --- a/server/zone.cpp +++ b/server/zone.cpp @@ -513,4 +513,10 @@ std::vector Zone::getNetdevList() return mAdmin->getNetdevList(); } +void Zone::deleteNetdevIpAddress(const std::string& netdev, const std::string& ip) +{ + Lock lock(mReconnectMutex); + mAdmin->deleteNetdevIpAddress(netdev, ip); +} + } // namespace vasum diff --git a/server/zone.hpp b/server/zone.hpp index b981e9a..687077f 100644 --- a/server/zone.hpp +++ b/server/zone.hpp @@ -311,6 +311,11 @@ public: */ std::vector getNetdevList(); + /** + * Remove ipv4/ipv6 address from network device + */ + void deleteNetdevIpAddress(const std::string& netdev, const std::string& ip); + private: utils::Worker::Pointer mWorker; ZoneConfig mConfig; diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 67e2b88..4f34a76 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -163,6 +163,9 @@ ZonesManager::ZonesManager(const std::string& configPath) mHostConnection.setDestroyNetdevCallback(bind(&ZonesManager::handleDestroyNetdevCall, this, _1, _2)); + mHostConnection.setDeleleNetdevIpAddressCallback(bind(&ZonesManager::handleDeleteNetdevIpAddressCall, + this, _1, _2)); + mHostConnection.setDeclareFileCallback(bind(&ZonesManager::handleDeclareFileCall, this, _1, _2)); @@ -945,6 +948,23 @@ void ZonesManager::handleDestroyNetdevCall(const api::DestroyNetDevIn& data, } } +void ZonesManager::handleDeleteNetdevIpAddressCall(const api::DeleteNetdevIpAddressIn& data, + api::MethodResultBuilder::Pointer result) +{ + LOGI("DelNetdevIpAddress call"); + try { + Lock lock(mMutex); + getZone(data.zone).deleteNetdevIpAddress(data.netdev, data.ip); + result->setVoid(); + } catch (const InvalidZoneIdException&) { + LOGE("No zone with id=" << data.zone); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + } catch (const VasumException& ex) { + LOGE("Can't delete address: " << ex.what()); + result->setError(api::ERROR_INTERNAL, ex.what()); + } +} + void ZonesManager::handleDeclareFileCall(const api::DeclareFileIn& data, api::MethodResultBuilder::Pointer result) { diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index ae89566..f789595 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -187,6 +187,8 @@ private: api::MethodResultBuilder::Pointer result); void handleDestroyNetdevCall(const api::DestroyNetDevIn& data, api::MethodResultBuilder::Pointer result); + void handleDeleteNetdevIpAddressCall(const api::DeleteNetdevIpAddressIn& data, + api::MethodResultBuilder::Pointer result); void handleDeclareFileCall(const api::DeclareFileIn& data, api::MethodResultBuilder::Pointer result); void handleDeclareMountCall(const api::DeclareMountIn& data, diff --git a/tests/unit_tests/server/ut-zone.cpp b/tests/unit_tests/server/ut-zone.cpp index 722f32c..dc66753 100644 --- a/tests/unit_tests/server/ut-zone.cpp +++ b/tests/unit_tests/server/ut-zone.cpp @@ -155,7 +155,7 @@ BOOST_AUTO_TEST_CASE(DbusConnection) c->stop(true); } -// TODO: DbusReconnectionTest +// TODO: DbusReconnection BOOST_AUTO_TEST_CASE(ListNetdev) { @@ -208,7 +208,7 @@ BOOST_AUTO_TEST_CASE(CreateNetdevMacvlan) BOOST_CHECK(find(netdevs.begin(), netdevs.end(), ZONE_NETDEV) != netdevs.end()); } -BOOST_AUTO_TEST_CASE(GetNetdevAttrsTest) +BOOST_AUTO_TEST_CASE(GetNetdevAttrs) { setupBridge(BRIDGE_NAME); auto c = create(TEST_CONFIG_PATH); @@ -240,7 +240,7 @@ BOOST_AUTO_TEST_CASE(GetNetdevAttrsTest) BOOST_CHECK(gotType); } -BOOST_AUTO_TEST_CASE(SetNetdevAttrsTest) +BOOST_AUTO_TEST_CASE(SetNetdevAttrs) { setupBridge(BRIDGE_NAME); auto c = create(TEST_CONFIG_PATH); @@ -269,7 +269,7 @@ BOOST_AUTO_TEST_CASE(SetNetdevAttrsTest) WhatEquals("Unsupported attribute: does_not_exists")); } -BOOST_AUTO_TEST_CASE(SetNetdevIpv4Test) +BOOST_AUTO_TEST_CASE(SetNetdevIpv4) { setupBridge(BRIDGE_NAME); auto c = create(TEST_CONFIG_PATH); @@ -309,7 +309,7 @@ BOOST_AUTO_TEST_CASE(SetNetdevIpv4Test) BOOST_CHECK_EQUAL(gotIp, 3); } -BOOST_AUTO_TEST_CASE(SetNetdevIpv6Test) +BOOST_AUTO_TEST_CASE(SetNetdevIpv6) { setupBridge(BRIDGE_NAME); auto c = create(TEST_CONFIG_PATH); @@ -349,4 +349,41 @@ BOOST_AUTO_TEST_CASE(SetNetdevIpv6Test) BOOST_CHECK_EQUAL(gotIp, 3); } +BOOST_AUTO_TEST_CASE(DelNetdevIpAddress) +{ + auto contain = [](const ZoneAdmin::NetdevAttrs& container, const std::string& key) { + return container.end() != find_if(container.begin(), + container.end(), + [&](const ZoneAdmin::NetdevAttrs::value_type& value) { + return std::get<0>(value) == key; + }); + }; + + setupBridge(BRIDGE_NAME); + auto c = create(TEST_CONFIG_PATH); + c->start(); + ensureStarted(); + c->createNetdevVeth(ZONE_NETDEV, BRIDGE_NAME); + ZoneAdmin::NetdevAttrs attrs; + attrs.push_back(std::make_tuple("ipv6", "ip:2001:db8::1,prefixlen:64")); + attrs.push_back(std::make_tuple("ipv4", "ip:192.168.4.1,prefixlen:24")); + c->setNetdevAttrs(ZONE_NETDEV, attrs); + attrs = c->getNetdevAttrs(ZONE_NETDEV); + BOOST_REQUIRE(contain(attrs, "ipv4")); + BOOST_REQUIRE(contain(attrs, "ipv6")); + + c->deleteNetdevIpAddress(ZONE_NETDEV, "192.168.4.1/24"); + attrs = c->getNetdevAttrs(ZONE_NETDEV); + BOOST_CHECK(!contain(attrs, "ipv4")); + BOOST_CHECK(contain(attrs, "ipv6")); + + c->deleteNetdevIpAddress(ZONE_NETDEV, "2001:db8::1/64"); + attrs = c->getNetdevAttrs(ZONE_NETDEV); + BOOST_REQUIRE(!contain(attrs, "ipv4")); + BOOST_REQUIRE(!contain(attrs, "ipv6")); + + BOOST_CHECK_THROW(c->deleteNetdevIpAddress(ZONE_NETDEV, "192.168.4.1/24"), VasumException); + BOOST_CHECK_THROW(c->deleteNetdevIpAddress(ZONE_NETDEV, "2001:db8::1/64"), VasumException); +} + BOOST_AUTO_TEST_SUITE_END()