IP Address use case validation

-validates IPAddress/gateway using inet_pton api.
-validates prefix length for ipv4/ipv6.
-if dhcp is enabled, it won't allow to set ip/gateway.

Resolves openbmc/openbmc#1671

Change-Id: I76c1d9d11dfb59002eb1310d87e94ee622714a4a
Signed-off-by: Nagaraju Goruganti <ngorugan@in.ibm.com>
diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp
index ffaac74..5a7c61c 100644
--- a/ethernet_interface.cpp
+++ b/ethernet_interface.cpp
@@ -33,6 +33,7 @@
 
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using Argument = xyz::openbmc_project::Common::InvalidArgument;
 
 EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus,
                                      const std::string& objPath,
@@ -78,7 +79,7 @@
         {
             origin = IP::AddressOrigin::DHCP;
         }
-        else if (isLinkLocal(addr.ipaddress))
+        else if (isLinkLocalIP(addr.ipaddress))
         {
             origin = IP::AddressOrigin::LinkLocal;
         }
@@ -113,11 +114,41 @@
     if (dHCPEnabled())
     {
         log<level::INFO>("DHCP enabled on the interface"),
-                        entry("INTERFACE=%s",interfaceName().c_str());
+            entry("INTERFACE=%s", interfaceName().c_str());
+        dHCPEnabled(false);
+    }
+
+
+    IP::AddressOrigin origin = IP::AddressOrigin::Static;
+
+    int addressFamily = (protType == IP::Protocol::IPv4) ? AF_INET : AF_INET6;
+
+    if (!isValidIP(addressFamily, ipaddress))
+    {
+        log<level::ERR>("Not a valid IP address"),
+            entry("ADDRESS=%s", ipaddress.c_str());
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipaddress"),
+                              Argument::ARGUMENT_VALUE(ipaddress.c_str()));
+    }
+
+    if (!gateway.empty() && (!isValidIP(addressFamily, gateway)))
+    {
+        log<level::ERR>("Not a valid Gateway"),
+            entry("GATEWAY=%s", gateway.c_str());
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("gateway"),
+                              Argument::ARGUMENT_VALUE(gateway.c_str()));
+    }
+
+    if (!isValidPrefix(addressFamily, prefixLength))
+    {
+        log<level::ERR>("PrefixLength is not correct "),
+            entry("PREFIXLENGTH=%d", gateway.c_str());
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("prefixLength"),
+                              Argument::ARGUMENT_VALUE(std::to_string(
+                                          prefixLength).c_str()));
         return;
     }
 
-    IP::AddressOrigin origin = IP::AddressOrigin::Static;
 
     std::string objectPath = generateObjectPath(protType,
                                                 ipaddress,
diff --git a/ipaddress.cpp b/ipaddress.cpp
index b2407ef..4daccd5 100644
--- a/ipaddress.cpp
+++ b/ipaddress.cpp
@@ -1,7 +1,10 @@
 #include "ipaddress.hpp"
 #include "ethernet_interface.hpp"
+#include "util.hpp"
 
+#include "xyz/openbmc_project/Common/error.hpp"
 #include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog-errors.hpp>
 
 namespace phosphor
 {
@@ -9,6 +12,7 @@
 {
 
 using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 
 IPAddress::IPAddress(sdbusplus::bus::bus& bus,
           const char* objPath,
@@ -34,6 +38,21 @@
 
 void IPAddress::delete_()
 {
+    if (parent.dHCPEnabled())
+    {
+        log<level::ERR>("DHCP enabled on the interface"),
+            entry("INTERFACE=%s", parent.interfaceName().c_str());
+        elog<InternalFailure>();
+    }
+
+    if (isLinkLocalIP(address()))
+    {
+        log<level::ERR>("Can not delete the LinkLocal address"),
+            entry("INTERFACE=%s ADDRESS=%s",
+                  parent.interfaceName().c_str(), address().c_str());
+        elog<InternalFailure>();
+    }
+
     parent.deleteObject(address());
 }
 
diff --git a/test/test_util.cpp b/test/test_util.cpp
index 9b1d34a..1c78b64 100644
--- a/test/test_util.cpp
+++ b/test/test_util.cpp
@@ -64,13 +64,22 @@
 TEST_F(TestUtil, isLinkLocaladdress)
 {
     std::string ipaddress = "fe80:fec0::";
-    EXPECT_TRUE(isLinkLocal(ipaddress));
+    EXPECT_TRUE(isLinkLocalIP(ipaddress));
 
-    ipaddress = "2000:4567:789::";
-    EXPECT_FALSE(isLinkLocal(ipaddress));
+    ipaddress = "2000:fe80:789::";
+    EXPECT_FALSE(isLinkLocalIP(ipaddress));
 
     ipaddress = "2000:fe80::";
-    EXPECT_FALSE(isLinkLocal(ipaddress));
+    EXPECT_FALSE(isLinkLocalIP(ipaddress));
+
+    ipaddress = "169.254.3.3";
+    EXPECT_TRUE(isLinkLocalIP(ipaddress));
+
+    ipaddress = "3.169.254.3";
+    EXPECT_FALSE(isLinkLocalIP(ipaddress));
+
+    ipaddress = "3.3.169.254";
+    EXPECT_FALSE(isLinkLocalIP(ipaddress));
 }
 
 TEST_F(TestUtil, convertPrefixToMask)
diff --git a/util.cpp b/util.cpp
index 48b5fcb..31bdd65 100644
--- a/util.cpp
+++ b/util.cpp
@@ -202,12 +202,39 @@
     return networkString;
 }
 
-bool isLinkLocal(const std::string& address)
+bool isLinkLocalIP(const std::string& address)
 {
-    std::string linklocal = "fe80";
-    return std::mismatch(linklocal.begin(), linklocal.end(),
-                         address.begin()).first == linklocal.end() ?
-           true : false;
+    return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0;
+}
+
+bool isValidIP(int addressFamily, const std::string& address)
+{
+    unsigned char buf[sizeof(struct in6_addr)];
+
+    return inet_pton(addressFamily, address.c_str(), buf) > 0;
+}
+
+bool isValidPrefix(int addressFamily, uint8_t prefixLength)
+{
+    if (addressFamily == AF_INET)
+    {
+        if (prefixLength < IPV4_MIN_PREFIX_LENGTH ||
+            prefixLength > IPV4_MAX_PREFIX_LENGTH)
+        {
+            return false;
+        }
+    }
+
+    if (addressFamily == AF_INET6)
+    {
+        if (prefixLength < IPV4_MIN_PREFIX_LENGTH ||
+            prefixLength > IPV6_MAX_PREFIX_LENGTH)
+        {
+            return false;
+        }
+    }
+
+    return true;
 }
 
 IntfAddrMap getInterfaceAddrs()
diff --git a/util.hpp b/util.hpp
index 669c68f..ff91d17 100644
--- a/util.hpp
+++ b/util.hpp
@@ -11,6 +11,13 @@
 {
 namespace network
 {
+
+constexpr auto IPV4_MIN_PREFIX_LENGTH = 1;
+constexpr auto IPV4_MAX_PREFIX_LENGTH = 32;
+constexpr auto IPV6_MAX_PREFIX_LENGTH = 64;
+constexpr auto IPV4_PREFIX = "169.254";
+constexpr auto IPV6_PREFIX = "fe80";
+
 namespace mac_address
 {
 
@@ -81,7 +88,21 @@
  * @param[in] address - IP address.
  * @returns true if it is linklocal otherwise false.
  */
-bool isLinkLocal(const std::string& address);
+bool isLinkLocalIP(const std::string& address);
+
+/* @brief checks that the given ip address valid or not.
+ * @param[in] addressFamily - IP address family(AF_INET/AF_INET6).
+ * @param[in] address - IP address.
+ * @returns true if it is valid otherwise false.
+ */
+bool isValidIP(int addressFamily, const std::string& address);
+
+/* @brief checks that the given prefix is valid or not.
+ * @param[in] addressFamily - IP address family(AF_INET/AF_INET6).
+ * @param[in] prefix - prefix length.
+ * @returns true if it is valid otherwise false.
+ */
+bool isValidPrefix(int addressFamily, uint8_t prefixLength);
 
 /* @brief gets the network section of the ip adress.
  * @param[in] addressFamily - IP address family(AF_INET/AF_INET6).