This is an automated email from the ASF dual-hosted git repository.

weizhou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new af8a582055c api/utils/ui: List protocol numbers and icmp types (#8293)
af8a582055c is described below

commit af8a582055c4194d1e42c8c1abd96969692046ed
Author: Wei Zhou <[email protected]>
AuthorDate: Fri Feb 2 15:49:04 2024 +0100

    api/utils/ui: List protocol numbers and icmp types (#8293)
    
    This PR contains the following changes
    
    * adds a new API to list network procotols and details/types/codes, etc
    * get network protocols on UI and add dropdowns for procotol numbers and 
icmp types/codes
    * validate icmp types/codes when add network ACL
---
 .../org/apache/cloudstack/api/ApiConstants.java    |   1 +
 .../user/network/ListNetworkProtocolsCmd.java      | 109 +++++++
 .../api/response/NetworkProtocolResponse.java      |  89 +++++
 .../user/network/ListNetworkProtocolsCmdTest.java  |  95 ++++++
 .../network/security/SecurityGroupManagerImpl.java |  26 +-
 .../cloud/network/vpc/NetworkACLServiceImpl.java   |   2 +-
 .../com/cloud/server/ManagementServerImpl.java     |   2 +
 .../network/vpc/NetworkACLServiceImplTest.java     |   8 +-
 ui/src/views/network/AclListRulesTab.vue           |  97 +++++-
 ui/src/views/network/EgressRulesTab.vue            |  61 +++-
 ui/src/views/network/FirewallRules.vue             |  63 +++-
 .../views/network/IngressEgressRuleConfigure.vue   |  83 ++++-
 .../main/java/com/cloud/utils/net/NetUtils.java    |  15 +
 .../java/com/cloud/utils/net/NetworkProtocols.java | 362 +++++++++++++++++++++
 .../java/com/cloud/utils/net/NetUtilsTest.java     |  46 +++
 .../com/cloud/utils/net/NetworkProtocolsTest.java  |  47 +++
 16 files changed, 1054 insertions(+), 52 deletions(-)

diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java 
b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index 3ae0f319189..db0c5ce494c 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -321,6 +321,7 @@ public class ApiConstants {
     public static final String IS_DEFAULT_USE = "defaultuse";
     public static final String OLD_FORMAT = "oldformat";
     public static final String OP = "op";
+    public static final String OPTION = "option";
     public static final String OPTIONS = "options";
     public static final String OS_CATEGORY_ID = "oscategoryid";
     public static final String OS_CATEGORY_NAME = "oscategoryname";
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkProtocolsCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkProtocolsCmd.java
new file mode 100644
index 00000000000..3008d1a8191
--- /dev/null
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkProtocolsCmd.java
@@ -0,0 +1,109 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.command.user.network;
+
+import com.cloud.utils.net.NetworkProtocols;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.NetworkProtocolResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@APICommand(name = "listNetworkProtocols", description = "Lists details of 
network protocols", responseObject = NetworkProtocolResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        authorized = { RoleType.Admin, RoleType.DomainAdmin, 
RoleType.ResourceAdmin, RoleType.User}, since = "4.19.0")
+public class ListNetworkProtocolsCmd extends BaseCmd {
+    public static final Logger s_logger = 
Logger.getLogger(ListNetworkProtocolsCmd.class.getName());
+
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.OPTION, type = CommandType.STRING, required 
= true,
+            description = "The option of network protocols. Supported values 
are: protocolnumber, icmptype.")
+    private String option;
+
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+
+    public String getOption() {
+        return option;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() {
+        ListResponse<NetworkProtocolResponse> response = new ListResponse<>();
+        List<NetworkProtocolResponse> networkProtocolResponses = new 
ArrayList<>();
+
+        NetworkProtocols.Option option = 
NetworkProtocols.Option.getOption(getOption());
+        switch (option) {
+            case ProtocolNumber:
+                updateResponseWithProtocolNumbers(networkProtocolResponses);
+                break;
+            case IcmpType:
+                updateResponseWithIcmpTypes(networkProtocolResponses);
+                break;
+            default:
+                break;
+        }
+
+        response.setResponses(networkProtocolResponses);
+        response.setResponseName(getCommandName());
+        setResponseObject(response);
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+
+    private void 
updateResponseWithProtocolNumbers(List<NetworkProtocolResponse> responses) {
+        for (NetworkProtocols.ProtocolNumber protocolNumber : 
NetworkProtocols.ProtocolNumbers) {
+            NetworkProtocolResponse networkProtocolResponse = new 
NetworkProtocolResponse(protocolNumber.getNumber(),
+                    protocolNumber.getKeyword(), protocolNumber.getProtocol());
+            networkProtocolResponse.setObjectName("networkprotocol");
+            responses.add(networkProtocolResponse);
+        }
+    }
+
+    private void updateResponseWithIcmpTypes(List<NetworkProtocolResponse> 
responses) {
+        for (NetworkProtocols.IcmpType icmpType : NetworkProtocols.IcmpTypes) {
+            NetworkProtocolResponse networkProtocolResponse = new 
NetworkProtocolResponse(icmpType.getType(),
+                    null, icmpType.getDescription());
+            for (NetworkProtocols.IcmpCode code : icmpType.getIcmpCodes()) {
+                
networkProtocolResponse.addDetail(String.valueOf(code.getCode()), 
code.getDescription());
+            }
+            networkProtocolResponse.setObjectName("networkprotocol");
+            responses.add(networkProtocolResponse);
+        }
+    }
+}
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/response/NetworkProtocolResponse.java
 
b/api/src/main/java/org/apache/cloudstack/api/response/NetworkProtocolResponse.java
new file mode 100644
index 00000000000..775333f7192
--- /dev/null
+++ 
b/api/src/main/java/org/apache/cloudstack/api/response/NetworkProtocolResponse.java
@@ -0,0 +1,89 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.response;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+
+public class NetworkProtocolResponse extends BaseResponse {
+    @SerializedName(ApiConstants.INDEX)
+    @Param(description = "the index (ID, Value, Code, Type, Option, etc) of 
the protocol parameter")
+    private Integer index;
+
+    @SerializedName(ApiConstants.NAME)
+    @Param(description = "the name of the protocol parameter")
+    private String name;
+
+    @SerializedName(ApiConstants.DESCRIPTION)
+    @Param(description = "the description of the protocol parameter")
+    private String description;
+
+    @SerializedName(ApiConstants.DETAILS)
+    @Param(description = "the details of the protocol parameter")
+    private Map details;
+
+    public NetworkProtocolResponse(Integer index, String name, String 
description) {
+        this.index = index;
+        this.name = name;
+        this.description = description;
+    }
+
+    public Integer getIndex() {
+        return index;
+    }
+
+    public void setIndex(Integer index) {
+        this.index = index;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Map getDetails() {
+        return details;
+    }
+
+    public void setDetails(Map details) {
+        this.details = details;
+    }
+
+    public void addDetail(String key, String value) {
+        if (this.details == null) {
+            this.details = new LinkedHashMap();
+        }
+        this.details.put(key, value);
+    }
+}
diff --git 
a/api/src/test/java/org/apache/cloudstack/api/command/user/network/ListNetworkProtocolsCmdTest.java
 
b/api/src/test/java/org/apache/cloudstack/api/command/user/network/ListNetworkProtocolsCmdTest.java
new file mode 100644
index 00000000000..7c29de69ade
--- /dev/null
+++ 
b/api/src/test/java/org/apache/cloudstack/api/command/user/network/ListNetworkProtocolsCmdTest.java
@@ -0,0 +1,95 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.api.command.user.network;
+
+import com.cloud.utils.net.NetworkProtocols;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.NetworkProtocolResponse;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.mockito.junit.MockitoJUnitRunner;
+import org.springframework.test.util.ReflectionTestUtils;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ListNetworkProtocolsCmdTest {
+
+    @Test
+    public void testListNetworkProtocolNumbers() {
+        ListNetworkProtocolsCmd cmd = new ListNetworkProtocolsCmd();
+        String option = NetworkProtocols.Option.ProtocolNumber.toString();
+        ReflectionTestUtils.setField(cmd, "option", option);
+        Assert.assertEquals(cmd.getOption(), option);
+
+        try {
+            cmd.execute();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        Object response = cmd.getResponseObject();
+        Assert.assertTrue(response instanceof ListResponse);
+        ListResponse listResponse = (ListResponse) response;
+        Assert.assertEquals(BaseCmd.getResponseNameByClass(cmd.getClass()), 
listResponse.getResponseName());
+        Assert.assertNotNull(listResponse.getResponses());
+        Assert.assertNotEquals(0, listResponse.getResponses().size());
+        Object firstResponse = listResponse.getResponses().get(0);
+        Assert.assertTrue(firstResponse instanceof NetworkProtocolResponse);
+        Assert.assertEquals("networkprotocol", ((NetworkProtocolResponse) 
firstResponse).getObjectName());
+        Assert.assertEquals(Integer.valueOf(0), ((NetworkProtocolResponse) 
firstResponse).getIndex());
+        Assert.assertEquals("HOPOPT", ((NetworkProtocolResponse) 
firstResponse).getName());
+    }
+
+    @Test
+    public void testListIcmpTypes() {
+        ListNetworkProtocolsCmd cmd = new ListNetworkProtocolsCmd();
+        String option = NetworkProtocols.Option.IcmpType.toString();
+        ReflectionTestUtils.setField(cmd, "option", option);
+        Assert.assertEquals(cmd.getOption(), option);
+
+        try {
+            cmd.execute();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        Object response = cmd.getResponseObject();
+        Assert.assertTrue(response instanceof ListResponse);
+        ListResponse listResponse = (ListResponse) response;
+        Assert.assertEquals(BaseCmd.getResponseNameByClass(cmd.getClass()), 
listResponse.getResponseName());
+        Assert.assertNotNull(listResponse.getResponses());
+        Assert.assertNotEquals(0, listResponse.getResponses().size());
+        Object firstResponse = listResponse.getResponses().get(0);
+        Assert.assertTrue(firstResponse instanceof NetworkProtocolResponse);
+        Assert.assertEquals("networkprotocol", ((NetworkProtocolResponse) 
firstResponse).getObjectName());
+        Assert.assertEquals(Integer.valueOf(0), ((NetworkProtocolResponse) 
firstResponse).getIndex());
+        Assert.assertNotNull(((NetworkProtocolResponse) 
firstResponse).getDetails());
+        System.out.println(((NetworkProtocolResponse) 
firstResponse).getDetails());
+        Assert.assertEquals("Echo reply", ((NetworkProtocolResponse) 
firstResponse).getDetails().get("0"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testListInvalidOption() {
+        ListNetworkProtocolsCmd cmd = new ListNetworkProtocolsCmd();
+        String option = "invalid-option";
+        ReflectionTestUtils.setField(cmd, "option", option);
+        Assert.assertEquals(cmd.getOption(), option);
+
+        cmd.execute();
+    }
+}
diff --git 
a/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java 
b/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java
index 5d4b4737cbe..e35503f32de 100644
--- 
a/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java
+++ 
b/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java
@@ -658,10 +658,14 @@ public class SecurityGroupManagerImpl extends ManagerBase 
implements SecurityGro
         if(StringUtils.isNumeric(protocol)){
             int protoNumber = Integer.parseInt(protocol);
             // Deal with ICMP(protocol number 1) specially because it need to 
be paired with icmp type and code
-            if (protoNumber == 1) {
-                protocol = "icmp";
-                icmpCode = -1;
-                icmpType = -1;
+            if (protoNumber == NetUtils.ICMP_PROTO_NUMBER) {
+                protocol = NetUtils.ICMP_PROTO;
+                if (icmpCode == null) {
+                    icmpCode = -1;
+                }
+                if (icmpType == null) {
+                    icmpType = -1;
+                }
             } else if(protoNumber < 0 || protoNumber > 255){
                 throw new InvalidParameterValueException("Invalid protocol 
number: " + protoNumber);
             }
@@ -673,18 +677,7 @@ public class SecurityGroupManagerImpl extends ManagerBase 
implements SecurityGro
             }
         }
         if (protocol.equals(NetUtils.ICMP_PROTO)) {
-            if ((icmpType == null) || (icmpCode == null)) {
-                throw new InvalidParameterValueException("Invalid ICMP 
type/code specified, icmpType = " + icmpType + ", icmpCode = " + icmpCode);
-            }
-            if (icmpType == -1 && icmpCode != -1) {
-                throw new InvalidParameterValueException("Invalid icmp code");
-            }
-            if (icmpType != -1 && icmpCode == -1) {
-                throw new InvalidParameterValueException("Invalid icmp code: 
need non-negative icmp code ");
-            }
-            if (icmpCode > 255 || icmpType > 255 || icmpCode < -1 || icmpType 
< -1) {
-                throw new InvalidParameterValueException("Invalid icmp 
type/code ");
-            }
+            NetUtils.validateIcmpTypeAndCode(icmpType, icmpCode);
             startPortOrType = icmpType;
             endPortOrCode = icmpCode;
         } else if (protocol.equals(NetUtils.ALL_PROTO)) {
@@ -785,6 +778,7 @@ public class SecurityGroupManagerImpl extends ManagerBase 
implements SecurityGro
                         SecurityGroupRuleVO securityGroupRule = 
_securityGroupRuleDao.findByProtoPortsAndAllowedGroupId(securityGroup.getId(), 
protocolFinal, startPortOrTypeFinal,
                                 endPortOrCodeFinal, ngVO.getId());
                         if ((securityGroupRule != null) && 
(securityGroupRule.getRuleType() == ruleType)) {
+                            s_logger.warn("The rule already exists. id= " + 
securityGroupRule.getUuid());
                             continue; // rule already exists.
                         }
                         securityGroupRule = new SecurityGroupRuleVO(ruleType, 
securityGroup.getId(), startPortOrTypeFinal, endPortOrCodeFinal, protocolFinal, 
ngVO.getId());
diff --git 
a/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java 
b/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java
index 8139ac1c49e..773d36175c3 100644
--- a/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java
@@ -583,7 +583,7 @@ public class NetworkACLServiceImpl extends ManagerBase 
implements NetworkACLServ
         Integer icmpCode = networkACLItemVO.getIcmpCode();
         Integer icmpType = networkACLItemVO.getIcmpType();
         // icmp code and icmp type can't be passed in for any other protocol 
rather than icmp
-        boolean isIcmpProtocol = 
protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO);
+        boolean isIcmpProtocol = 
protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO) || 
protocol.equalsIgnoreCase(String.valueOf(NetUtils.ICMP_PROTO_NUMBER));
         if (!isIcmpProtocol && (icmpCode != null || icmpType != null)) {
             throw new InvalidParameterValueException("Can specify icmpCode and 
icmpType for ICMP protocol only");
         }
diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java 
b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
index f794736a4d5..a73ba9b092c 100644
--- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
@@ -446,6 +446,7 @@ import 
org.apache.cloudstack.api.command.user.network.ListNetworkACLListsCmd;
 import org.apache.cloudstack.api.command.user.network.ListNetworkACLsCmd;
 import org.apache.cloudstack.api.command.user.network.ListNetworkOfferingsCmd;
 import 
org.apache.cloudstack.api.command.user.network.ListNetworkPermissionsCmd;
+import org.apache.cloudstack.api.command.user.network.ListNetworkProtocolsCmd;
 import org.apache.cloudstack.api.command.user.network.ListNetworksCmd;
 import org.apache.cloudstack.api.command.user.network.MoveNetworkAclItemCmd;
 import 
org.apache.cloudstack.api.command.user.network.RemoveNetworkPermissionsCmd;
@@ -3621,6 +3622,7 @@ public class ManagementServerImpl extends ManagerBase 
implements ManagementServe
         cmdList.add(DeleteNetworkCmd.class);
         cmdList.add(ListNetworkACLsCmd.class);
         cmdList.add(ListNetworkOfferingsCmd.class);
+        cmdList.add(ListNetworkProtocolsCmd.class);
         cmdList.add(ListNetworksCmd.class);
         cmdList.add(RestartNetworkCmd.class);
         cmdList.add(UpdateNetworkCmd.class);
diff --git 
a/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java 
b/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java
index 18a072172ad..3d63b1db507 100644
--- a/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java
+++ b/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java
@@ -622,8 +622,8 @@ public class NetworkACLServiceImplTest {
 
     @Test(expected = InvalidParameterValueException.class)
     public void validateProtocolTestProtocolNotIcmpWithIcmpConfigurations() {
-        Mockito.when(networkAclItemVoMock.getIcmpCode()).thenReturn(1);
-        Mockito.when(networkAclItemVoMock.getIcmpType()).thenReturn(1);
+        Mockito.when(networkAclItemVoMock.getIcmpCode()).thenReturn(2);
+        Mockito.when(networkAclItemVoMock.getIcmpType()).thenReturn(3);
 
         Mockito.when(networkAclItemVoMock.getProtocol()).thenReturn("tcp");
         networkAclServiceImpl.validateProtocol(networkAclItemVoMock);
@@ -647,8 +647,8 @@ public class NetworkACLServiceImplTest {
 
     @Test
     public void validateProtocolTestProtocolIcmpWithIcmpConfigurations() {
-        Mockito.when(networkAclItemVoMock.getIcmpCode()).thenReturn(1);
-        Mockito.when(networkAclItemVoMock.getIcmpType()).thenReturn(1);
+        Mockito.when(networkAclItemVoMock.getIcmpCode()).thenReturn(2);
+        Mockito.when(networkAclItemVoMock.getIcmpType()).thenReturn(3);
 
         
Mockito.when(networkAclItemVoMock.getSourcePortStart()).thenReturn(null);
         Mockito.when(networkAclItemVoMock.getSourcePortEnd()).thenReturn(null);
diff --git a/ui/src/views/network/AclListRulesTab.vue 
b/ui/src/views/network/AclListRulesTab.vue
index caf72caf0a5..a0336cb1923 100644
--- a/ui/src/views/network/AclListRulesTab.vue
+++ b/ui/src/views/network/AclListRulesTab.vue
@@ -64,19 +64,19 @@
                 <div class="list__label">{{ $t('label.protocol') }}</div>
                 <div>{{ element.protocol }}</div>
               </div>
-              <div class="list__col" v-if="element.startport">
+              <div class="list__col" v-if="element.startport !== undefined">
                 <div class="list__label">{{ $t('label.startport') }}</div>
                 <div>{{ element.startport }}</div>
               </div>
-              <div class="list__col" v-if="element.endport">
+              <div class="list__col" v-if="element.endport !== undefined">
                 <div class="list__label">{{ $t('label.endport') }}</div>
                 <div>{{ element.endport }}</div>
               </div>
-              <div class="list__col" v-if="element.icmpcode">
+              <div class="list__col" v-if="element.icmpcode !== undefined">
                 <div class="list__label">{{ $t('label.icmpcode') }}</div>
                 <div>{{ element.icmpcode }}</div>
               </div>
-              <div class="list__col" v-if="element.icmptype">
+              <div class="list__col" v-if="element.icmptype !== undefined">
                 <div class="list__label">{{ $t('label.icmptype') }}</div>
                 <div>{{ element.icmptype }}</div>
               </div>
@@ -208,19 +208,50 @@
           :label="$t('label.protocolnumber')"
           ref="protocolnumber"
           name="protocolnumber">
-          <a-input v-model:value="form.protocolnumber" />
+          <a-select
+            v-model:value="form.protocolnumber"
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }" >
+            <a-select-option v-for="(opt, optIndex) in protocolNumbers" 
:key="optIndex" :label="opt.name">
+              {{ opt.index + ' - ' + opt.name }}
+            </a-select-option>
+          </a-select>
         </a-form-item>
 
-        <div v-if="form.protocol === 'icmp'">
+        <div v-if="form.protocol === 'icmp' || (form.protocol === 
'protocolnumber' && form.protocolnumber === 1)">
           <a-form-item :label="$t('label.icmptype')" ref="icmptype" 
name="icmptype">
-            <a-input v-model:value="form.icmptype" 
:placeholder="$t('icmp.type.desc')" />
+            <a-select
+              v-model:value="form.icmptype"
+              @change="val => { updateIcmpCodes(val) }"
+              showSearch
+              optionFilterProp="label"
+              :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }" >
+              <a-select-option v-for="(opt) in icmpTypes" :key="opt.index" 
:label="opt.description">
+                {{ opt.index + ' - ' + opt.description }}
+              </a-select-option>
+            </a-select>
           </a-form-item>
           <a-form-item :label="$t('label.icmpcode')" ref="icmpcode" 
name="icmpcode">
-            <a-input v-model:value="form.icmpcode" 
:placeholder="$t('icmp.code.desc')" />
+            <a-select
+              v-model:value="form.icmpcode"
+              showSearch
+              optionFilterProp="label"
+              :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }" >
+              <a-select-option v-for="(opt) in icmpCodes" :key="opt.code" 
:label="opt.description">
+                {{ opt.code + ' - ' + opt.description }}
+              </a-select-option>
+            </a-select>
           </a-form-item>
         </div>
 
-        <div v-show="['tcp', 'udp', 'protocolnumber'].includes(form.protocol)">
+        <div v-show="['tcp', 'udp', 'protocolnumber'].includes(form.protocol) 
&& !(form.protocol === 'protocolnumber' && form.protocolnumber === 1)">
           <a-form-item :label="$t('label.startport')" ref="startport" 
name="startport">
             <a-input-number style="width: 100%" v-model:value="form.startport" 
/>
           </a-form-item>
@@ -285,6 +316,9 @@ export default {
     return {
       acls: [],
       fetchLoading: false,
+      protocolNumbers: [],
+      icmpTypes: [],
+      icmpCodes: [],
       tags: [],
       selectedAcl: null,
       tagsModalVisible: false,
@@ -296,6 +330,7 @@ export default {
   },
   created () {
     this.initForm()
+    this.fetchNetworkProtocols()
     this.fetchData()
   },
   watch: {
@@ -310,6 +345,8 @@ export default {
       this.formRef = ref()
       this.form = reactive({})
       this.rules = reactive({})
+      this.form.icmptype = -1
+      this.form.icmpcode = -1
     },
     csv ({ data = null, columnDelimiter = ',', lineDelimiter = '\n' }) {
       let result = null
@@ -351,6 +388,35 @@ export default {
 
       return result
     },
+    fetchNetworkProtocols () {
+      api('listNetworkProtocols', {
+        option: 'protocolnumber'
+      }).then(json => {
+        this.protocolNumbers = 
json.listnetworkprotocolsresponse?.networkprotocol || []
+      })
+      api('listNetworkProtocols', {
+        option: 'icmptype'
+      }).then(json => {
+        this.icmpTypes.push({ index: -1, description: this.$t('label.all') })
+        const results = json.listnetworkprotocolsresponse?.networkprotocol || 
[]
+        for (const result of results) {
+          this.icmpTypes.push(result)
+        }
+      })
+      this.icmpCodes.push({ code: -1, description: this.$t('label.all') })
+    },
+    updateIcmpCodes (val) {
+      this.form.icmpcode = -1
+      this.icmpCodes = []
+      this.icmpCodes.push({ code: -1, description: this.$t('label.all') })
+      const icmpType = this.icmpTypes.find(icmpType => icmpType.index === val)
+      if (icmpType && icmpType.details) {
+        const icmpTypeDetails = icmpType.details
+        for (const k of Object.keys(icmpTypeDetails)) {
+          this.icmpCodes.push({ code: parseInt(k), description: 
icmpTypeDetails[k] })
+        }
+      }
+    },
     fetchData () {
       this.fetchLoading = true
       api('listNetworkACLs', { aclid: this.resource.id }).then(json => {
@@ -476,8 +542,15 @@ export default {
         self.form.cidrlist = acl.cidrlist
         self.form.action = acl.action
         self.form.protocol = acl.protocol
+        if (!['tcp', 'udp', 'icmp', 'all'].includes(acl.protocol)) {
+          self.form.protocol = 'protocolnumber'
+          self.form.protocolnumber = parseInt(acl.protocol)
+        }
         self.form.startport = acl.startport
         self.form.endport = acl.endport
+        self.form.icmptype = parseInt(acl.icmptype)
+        this.updateIcmpCodes(self.form.icmptype)
+        self.form.icmpcode = acl.icmpcode
         self.form.traffictype = acl.traffictype
         self.form.reason = acl.reason
       }, 200)
@@ -497,9 +570,9 @@ export default {
         data.endport = values.endport || ''
       }
 
-      if (values.protocol === 'icmp') {
-        data.icmptype = values.icmptype || -1
-        data.icmpcode = values.icmpcode || -1
+      if (values.protocol === 'icmp' || (values.protocol === 'protocolnumber' 
&& values.protocolnumber === 1)) {
+        data.icmptype = values.icmptype
+        data.icmpcode = values.icmpcode
       }
 
       if (values.protocol === 'protocolnumber') {
diff --git a/ui/src/views/network/EgressRulesTab.vue 
b/ui/src/views/network/EgressRulesTab.vue
index a0fb7085534..9d315416e2a 100644
--- a/ui/src/views/network/EgressRulesTab.vue
+++ b/ui/src/views/network/EgressRulesTab.vue
@@ -63,11 +63,32 @@
         </div>
         <div v-show="newRule.protocol === 'icmp'" class="form__item">
           <div class="form__label">{{ $t('label.icmptype') }}</div>
-          <a-input v-model:value="newRule.icmptype"></a-input>
+          <a-select
+            v-model:value="newRule.icmptype"
+            @change="val => { updateIcmpCodes(val) }"
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }" >
+            <a-select-option v-for="(opt) in icmpTypes" :key="opt.index" 
:label="opt.description">
+              {{ opt.index + ' - ' + opt.description }}
+            </a-select-option>
+          </a-select>
         </div>
         <div v-show="newRule.protocol === 'icmp'" class="form__item">
           <div class="form__label">{{ $t('label.icmpcode') }}</div>
-          <a-input v-model:value="newRule.icmpcode"></a-input>
+          <a-select
+            v-model:value="newRule.icmpcode"
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }" >
+            <a-select-option v-for="(opt) in icmpCodes" :key="opt.code" 
:label="opt.description">
+              {{ opt.code + ' - ' + opt.description }}
+            </a-select-option>
+          </a-select>
         </div>
         <div class="form__item">
           <a-button ref="submit" :disabled="!('createEgressFirewallRule' in 
$store.getters.apis)" type="primary" @click="addRule">
@@ -102,10 +123,10 @@
           {{ getCapitalise(record.protocol) }}
         </template>
         <template v-if="column.key === 'startport'">
-          {{ record.icmptype || record.startport >= 0 ? record.icmptype || 
record.startport : 'All' }}
+          {{ record.icmptype >= 0 ? String(record.icmptype): record.startport 
>= 0 ? String(record.startport): 'All' }}
         </template>
         <template v-if="column.key === 'endport'">
-          {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || 
record.endport : 'All' }}
+          {{ record.icmpcode >= 0 ? String(record.icmpcode): record.endport >= 
0 ? String(record.endport): 'All' }}
         </template>
         <template v-if="column.key === 'actions'">
           <tooltip-button
@@ -196,6 +217,9 @@ export default {
         startport: null,
         endport: null
       },
+      protocolNumbers: [],
+      icmpTypes: [],
+      icmpCodes: [],
       totalCount: 0,
       page: 1,
       pageSize: 10,
@@ -233,6 +257,7 @@ export default {
     }
   },
   created () {
+    this.fetchNetworkProtocols()
     this.fetchData()
   },
   watch: {
@@ -248,6 +273,34 @@ export default {
   },
   inject: ['parentFetchData'],
   methods: {
+    fetchNetworkProtocols () {
+      api('listNetworkProtocols', {
+        option: 'protocolnumber'
+      }).then(json => {
+        this.protocolNumbers = 
json.listnetworkprotocolsresponse?.networkprotocol || []
+      })
+      api('listNetworkProtocols', {
+        option: 'icmptype'
+      }).then(json => {
+        this.icmpTypes.push({ index: -1, description: this.$t('label.all') })
+        const results = json.listnetworkprotocolsresponse?.networkprotocol || 
[]
+        for (const result of results) {
+          this.icmpTypes.push(result)
+        }
+      })
+    },
+    updateIcmpCodes (val) {
+      this.newRule.icmpcode = -1
+      this.icmpCodes = []
+      this.icmpCodes.push({ code: -1, description: this.$t('label.all') })
+      const icmpType = this.icmpTypes.find(icmpType => icmpType.index === val)
+      if (icmpType && icmpType.details) {
+        const icmpTypeDetails = icmpType.details
+        for (const k of Object.keys(icmpTypeDetails)) {
+          this.icmpCodes.push({ code: parseInt(k), description: 
icmpTypeDetails[k] })
+        }
+      }
+    },
     fetchData () {
       this.loading = true
       api('listEgressFirewallRules', {
diff --git a/ui/src/views/network/FirewallRules.vue 
b/ui/src/views/network/FirewallRules.vue
index 787f5c2d8e2..43ee9536be5 100644
--- a/ui/src/views/network/FirewallRules.vue
+++ b/ui/src/views/network/FirewallRules.vue
@@ -49,11 +49,32 @@
         </div>
         <div v-show="newRule.protocol === 'icmp'" class="form__item">
           <div class="form__label">{{ $t('label.icmptype') }}</div>
-          <a-input v-model:value="newRule.icmptype"></a-input>
+          <a-select
+            v-model:value="newRule.icmptype"
+            @change="val => { updateIcmpCodes(val) }"
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }" >
+            <a-select-option v-for="(opt) in icmpTypes" :key="opt.index" 
:label="opt.description">
+              {{ opt.index + ' - ' + opt.description }}
+            </a-select-option>
+          </a-select>
         </div>
         <div v-show="newRule.protocol === 'icmp'" class="form__item">
           <div class="form__label">{{ $t('label.icmpcode') }}</div>
-          <a-input v-model:value="newRule.icmpcode"></a-input>
+          <a-select
+            v-model:value="newRule.icmpcode"
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }" >
+            <a-select-option v-for="(opt) in icmpCodes" :key="opt.code" 
:label="opt.description">
+              {{ opt.code + ' - ' + opt.description }}
+            </a-select-option>
+          </a-select>
         </div>
         <div class="form__item" style="margin-left: auto;">
           <a-button :disabled="!('createFirewallRule' in $store.getters.apis)" 
type="primary" ref="submit" @click="addRule">{{ $t('label.add') }}</a-button>
@@ -85,10 +106,10 @@
           {{ getCapitalise(record.protocol) }}
         </template>
         <template v-if="column.key === 'startport'">
-          {{ record.icmptype || record.startport >= 0 ? record.icmptype || 
record.startport : $t('label.all') }}
+          {{ record.icmptype >= 0 ? String(record.icmptype): record.startport 
>= 0 ? String(record.startport): 'All' }}
         </template>
         <template v-if="column.key === 'endport'">
-          {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || 
record.endport : $t('label.all') }}
+          {{ record.icmpcode >= 0 ? String(record.icmpcode): record.endport >= 
0 ? String(record.endport): 'All' }}
         </template>
         <template v-if="column.key === 'actions'">
           <div class="actions">
@@ -238,6 +259,9 @@ export default {
         startport: null,
         endport: null
       },
+      protocolNumbers: [],
+      icmpTypes: [],
+      icmpCodes: [],
       tagsModalVisible: false,
       selectedRule: null,
       tags: [],
@@ -279,6 +303,7 @@ export default {
   },
   created () {
     this.initForm()
+    this.fetchNetworkProtocols()
     this.fetchData()
   },
   watch: {
@@ -301,6 +326,34 @@ export default {
         value: [{ required: true, message: 
this.$t('message.specify.tag.value') }]
       })
     },
+    fetchNetworkProtocols () {
+      api('listNetworkProtocols', {
+        option: 'protocolnumber'
+      }).then(json => {
+        this.protocolNumbers = 
json.listnetworkprotocolsresponse?.networkprotocol || []
+      })
+      api('listNetworkProtocols', {
+        option: 'icmptype'
+      }).then(json => {
+        this.icmpTypes.push({ index: -1, description: this.$t('label.all') })
+        const results = json.listnetworkprotocolsresponse?.networkprotocol || 
[]
+        for (const result of results) {
+          this.icmpTypes.push(result)
+        }
+      })
+    },
+    updateIcmpCodes (val) {
+      this.newRule.icmpcode = -1
+      this.icmpCodes = []
+      this.icmpCodes.push({ code: -1, description: this.$t('label.all') })
+      const icmpType = this.icmpTypes.find(icmpType => icmpType.index === val)
+      if (icmpType && icmpType.details) {
+        const icmpTypeDetails = icmpType.details
+        for (const k of Object.keys(icmpTypeDetails)) {
+          this.icmpCodes.push({ code: parseInt(k), description: 
icmpTypeDetails[k] })
+        }
+      }
+    },
     fetchData () {
       this.loading = true
       api('listFirewallRules', {
@@ -620,7 +673,7 @@ export default {
     &__item {
       display: flex;
       flex-direction: column;
-      /*flex: 1;*/
+      flex: 1;
       padding-right: 20px;
       margin-bottom: 20px;
 
diff --git a/ui/src/views/network/IngressEgressRuleConfigure.vue 
b/ui/src/views/network/IngressEgressRuleConfigure.vue
index adf013cb9aa..92cd2fe50d0 100644
--- a/ui/src/views/network/IngressEgressRuleConfigure.vue
+++ b/ui/src/views/network/IngressEgressRuleConfigure.vue
@@ -47,25 +47,56 @@
             <a-select-option value="protocolnumber" 
:label="$t('label.protocol.number')">{{ capitalise($t('label.protocol.number')) 
}}</a-select-option>
           </a-select>
         </div>
-        <div v-show="newRule.protocol === 'tcp' || newRule.protocol === 'udp'" 
class="form__item">
+        <div v-show="newRule.protocol === 'protocolnumber'" class="form__item">
+          <div class="form__label">{{ $t('label.protocol.number') }}</div>
+          <a-select
+            v-model:value="newRule.protocolnumber"
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }" >
+            <a-select-option v-for="(opt, optIndex) in protocolNumbers" 
:key="optIndex" :label="opt.name">
+              {{ opt.index + ' - ' + opt.name }}
+            </a-select-option>
+          </a-select>
+        </div>
+        <div v-show="['tcp', 'udp', 
'protocolnumber'].includes(newRule.protocol) && !(newRule.protocol === 
'protocolnumber' && newRule.protocolnumber === 1)" class="form__item">
           <div class="form__label">{{ $t('label.startport') }}</div>
           <a-input v-model:value="newRule.startport"></a-input>
         </div>
-        <div v-show="newRule.protocol === 'tcp' || newRule.protocol === 'udp'" 
class="form__item">
+        <div v-show="['tcp', 'udp', 
'protocolnumber'].includes(newRule.protocol) && !(newRule.protocol === 
'protocolnumber' && newRule.protocolnumber === 1)" class="form__item">
           <div class="form__label">{{ $t('label.endport') }}</div>
           <a-input v-model:value="newRule.endport"></a-input>
         </div>
-        <div v-show="newRule.protocol === 'protocolnumber'" class="form__item">
-          <div class="form__label">{{ $t('label.protocol.number') }}</div>
-          <a-input v-model:value="newRule.protocolnumber"></a-input>
-        </div>
-        <div v-show="newRule.protocol === 'icmp'" class="form__item">
+        <div v-show="newRule.protocol === 'icmp' || (newRule.protocol === 
'protocolnumber' && newRule.protocolnumber === 1)" class="form__item">
           <div class="form__label">{{ $t('label.icmptype') }}</div>
-          <a-input v-model:value="newRule.icmptype"></a-input>
+          <a-select
+            v-model:value="newRule.icmptype"
+            @change="val => { updateIcmpCodes(val) }"
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }" >
+            <a-select-option v-for="(opt) in icmpTypes" :key="opt.index" 
:label="opt.description">
+              {{ opt.index + ' - ' + opt.description }}
+            </a-select-option>
+          </a-select>
         </div>
-        <div v-show="newRule.protocol === 'icmp'" class="form__item">
+        <div v-show="newRule.protocol === 'icmp' || (newRule.protocol === 
'protocolnumber' && newRule.protocolnumber === 1)" class="form__item">
           <div class="form__label">{{ $t('label.icmpcode') }}</div>
-          <a-input v-model:value="newRule.icmpcode"></a-input>
+          <a-select
+            v-model:value="newRule.icmpcode"
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }" >
+            <a-select-option v-for="(opt) in icmpCodes" :key="opt.code" 
:label="opt.description">
+              {{ opt.code + ' - ' + opt.description }}
+            </a-select-option>
+          </a-select>
         </div>
         <div class="form__item" v-if="addType === 'cidr'">
           <div class="form__label">{{ $t('label.cidr') }}</div>
@@ -211,6 +242,9 @@ export default {
           group: null
         }
       },
+      protocolNumbers: [],
+      icmpTypes: [],
+      icmpCodes: [],
       tagsModalVisible: false,
       tags: [],
       newTag: {
@@ -275,6 +309,7 @@ export default {
   },
   created () {
     this.initForm()
+    this.fetchNetworkProtocols()
     this.fetchData()
   },
   methods: {
@@ -286,6 +321,34 @@ export default {
         value: [{ required: true, message: 
this.$t('message.specify.tag.value') }]
       })
     },
+    fetchNetworkProtocols () {
+      api('listNetworkProtocols', {
+        option: 'protocolnumber'
+      }).then(json => {
+        this.protocolNumbers = 
json.listnetworkprotocolsresponse?.networkprotocol || []
+      })
+      api('listNetworkProtocols', {
+        option: 'icmptype'
+      }).then(json => {
+        this.icmpTypes.push({ index: -1, description: this.$t('label.all') })
+        const results = json.listnetworkprotocolsresponse?.networkprotocol || 
[]
+        for (const result of results) {
+          this.icmpTypes.push(result)
+        }
+      })
+    },
+    updateIcmpCodes (val) {
+      this.newRule.icmpcode = -1
+      this.icmpCodes = []
+      this.icmpCodes.push({ code: -1, description: this.$t('label.all') })
+      const icmpType = this.icmpTypes.find(icmpType => icmpType.index === val)
+      if (icmpType && icmpType.details) {
+        const icmpTypeDetails = icmpType.details
+        for (const k of Object.keys(icmpTypeDetails)) {
+          this.icmpCodes.push({ code: parseInt(k), description: 
icmpTypeDetails[k] })
+        }
+      }
+    },
     fetchData () {
       this.tabType = this.$route.query.tab === 'ingress.rule' ? 'ingress' : 
'egress'
       this.rules = this.tabType === 'ingress' ? this.resource.ingressrule : 
this.resource.egressrule
diff --git a/utils/src/main/java/com/cloud/utils/net/NetUtils.java 
b/utils/src/main/java/com/cloud/utils/net/NetUtils.java
index 018912a2140..2a39b12afdf 100644
--- a/utils/src/main/java/com/cloud/utils/net/NetUtils.java
+++ b/utils/src/main/java/com/cloud/utils/net/NetUtils.java
@@ -78,6 +78,7 @@ public class NetUtils {
     public final static String ANY_PROTO = "any";
     public final static String ICMP_PROTO = "icmp";
     public static final String ICMP6_PROTO = "icmp6";
+    public final static int ICMP_PROTO_NUMBER = 1;
     public final static String ALL_PROTO = "all";
     public final static String HTTP_PROTO = "http";
     public final static String HTTPS_PROTO = "https";
@@ -1771,4 +1772,18 @@ public class NetUtils {
         }
     }
 
+    public static void validateIcmpTypeAndCode(Integer icmpType, Integer 
icmpCode) {
+        if ((icmpType == null) || (icmpCode == null)) {
+            throw new CloudRuntimeException("Invalid ICMP type/code specified, 
icmpType = " + icmpType + ", icmpCode = " + icmpCode);
+        }
+        if (icmpType == -1 && icmpCode != -1) {
+            throw new CloudRuntimeException("Invalid icmp code");
+        }
+        if (icmpType != -1 && icmpCode == -1) {
+            throw new CloudRuntimeException("Invalid icmp code: need 
non-negative icmp code ");
+        }
+        if (icmpCode > 255 || icmpType > 255 || icmpCode < -1 || icmpType < 
-1) {
+            throw new CloudRuntimeException("Invalid icmp type/code ");
+        }
+    }
 }
diff --git a/utils/src/main/java/com/cloud/utils/net/NetworkProtocols.java 
b/utils/src/main/java/com/cloud/utils/net/NetworkProtocols.java
new file mode 100644
index 00000000000..e3ebe43757f
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/net/NetworkProtocols.java
@@ -0,0 +1,362 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.utils.net;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Network protocols and parameters.
+ * see <a href="https://www.iana.org/protocols";>Protocol Registries</a>
+ *
+ */
+public class NetworkProtocols {
+
+    public enum Option {
+        ProtocolNumber, IcmpType;
+
+        public static Option getOption(String value) {
+            return Arrays.stream(Option.values())
+                    .filter(option -> option.name().equalsIgnoreCase(value))
+                    .findFirst()
+                    .orElseThrow(() -> new 
IllegalArgumentException(String.format("Option " + value + " is not supported. 
Supported values are %s", Arrays.toString(Option.values()))));
+        }
+    }
+
+    // Refer to 
https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
+    public static List<ProtocolNumber> ProtocolNumbers = new ArrayList<>();
+    static {
+        ProtocolNumbers.add(new ProtocolNumber(0, "HOPOPT", "IPv6 Hop-by-Hop 
Option"));
+        ProtocolNumbers.add(new ProtocolNumber(1, "ICMP", "Internet Control 
Message"));
+        ProtocolNumbers.add(new ProtocolNumber(2, "IGMP", "Internet Group 
Management"));
+        ProtocolNumbers.add(new ProtocolNumber(3, "GGP", 
"Gateway-to-Gateway"));
+        ProtocolNumbers.add(new ProtocolNumber(4, "IPv4", "IPv4 
encapsulation"));
+        ProtocolNumbers.add(new ProtocolNumber(5, "ST", "Stream"));
+        ProtocolNumbers.add(new ProtocolNumber(6, "TCP", "Transmission 
Control"));
+        ProtocolNumbers.add(new ProtocolNumber(7, "CBT", "CBT"));
+        ProtocolNumbers.add(new ProtocolNumber(8, "EGP", "Exterior Gateway 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(9, "IGP", "any private interior 
gateway"));
+        ProtocolNumbers.add(new ProtocolNumber(10, "BBN-RCC-MON", "BBN RCC 
Monitoring"));
+        ProtocolNumbers.add(new ProtocolNumber(11, "NVP-II", "Network Voice 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(12, "PUP", "PUP"));
+        ProtocolNumbers.add(new ProtocolNumber(13, "ARGUS (deprecated)", 
"ARGUS"));
+        ProtocolNumbers.add(new ProtocolNumber(14, "EMCON", "EMCON"));
+        ProtocolNumbers.add(new ProtocolNumber(15, "XNET", "Cross Net 
Debugger"));
+        ProtocolNumbers.add(new ProtocolNumber(16, "CHAOS", "Chaos"));
+        ProtocolNumbers.add(new ProtocolNumber(17, "UDP", "User Datagram"));
+        ProtocolNumbers.add(new ProtocolNumber(18, "MUX", "Multiplexing"));
+        ProtocolNumbers.add(new ProtocolNumber(19, "DCN-MEAS", "DCN 
Measurement Subsystems"));
+        ProtocolNumbers.add(new ProtocolNumber(20, "HMP", "Host Monitoring"));
+        ProtocolNumbers.add(new ProtocolNumber(21, "PRM", "Packet Radio 
Measurement"));
+        ProtocolNumbers.add(new ProtocolNumber(22, "XNS-IDP", "XEROX NS IDP"));
+        ProtocolNumbers.add(new ProtocolNumber(23, "TRUNK-1", "Trunk-1"));
+        ProtocolNumbers.add(new ProtocolNumber(24, "TRUNK-2", "Trunk-2"));
+        ProtocolNumbers.add(new ProtocolNumber(25, "LEAF-1", "Leaf-1"));
+        ProtocolNumbers.add(new ProtocolNumber(26, "LEAF-2", "Leaf-2"));
+        ProtocolNumbers.add(new ProtocolNumber(27, "RDP", "Reliable Data 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(28, "IRTP", "Internet Reliable 
Transaction"));
+        ProtocolNumbers.add(new ProtocolNumber(29, "ISO-TP4", "ISO Transport 
Protocol Class 4"));
+        ProtocolNumbers.add(new ProtocolNumber(30, "NETBLT", "Bulk Data 
Transfer Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(31, "MFE-NSP", "MFE Network 
Services Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(32, "MERIT-INP", "MERIT 
Internodal Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(33, "DCCP", "Datagram 
Congestion Control Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(34, "3PC", "Third Party Connect 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(35, "IDPR", "Inter-Domain 
Policy Routing Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(36, "XTP", "XTP"));
+        ProtocolNumbers.add(new ProtocolNumber(37, "DDP", "Datagram Delivery 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(38, "IDPR-CMTP", "IDPR Control 
Message Transport Proto"));
+        ProtocolNumbers.add(new ProtocolNumber(39, "TP++", "TP++ Transport 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(40, "IL", "IL Transport 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(41, "IPv6", "IPv6 
encapsulation"));
+        ProtocolNumbers.add(new ProtocolNumber(42, "SDRP", "Source Demand 
Routing Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(43, "IPv6-Route", "Routing 
Header for IPv6"));
+        ProtocolNumbers.add(new ProtocolNumber(44, "IPv6-Frag", "Fragment 
Header for IPv6"));
+        ProtocolNumbers.add(new ProtocolNumber(45, "IDRP", "Inter-Domain 
Routing Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(46, "RSVP", "Reservation 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(47, "GRE", "Generic Routing 
Encapsulation"));
+        ProtocolNumbers.add(new ProtocolNumber(48, "DSR", "Dynamic Source 
Routing Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(49, "BNA", "BNA"));
+        ProtocolNumbers.add(new ProtocolNumber(50, "ESP", "Encap Security 
Payload"));
+        ProtocolNumbers.add(new ProtocolNumber(51, "AH", "Authentication 
Header"));
+        ProtocolNumbers.add(new ProtocolNumber(52, "I-NLSP", "Integrated Net 
Layer Security TUBA"));
+        ProtocolNumbers.add(new ProtocolNumber(53, "SWIPE (deprecated)", "IP 
with Encryption"));
+        ProtocolNumbers.add(new ProtocolNumber(54, "NARP", "NBMA Address 
Resolution Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(55, "MOBILE", "Minimal IPv4 
Encapsulation"));
+        ProtocolNumbers.add(new ProtocolNumber(56, "TLSP", "Transport Layer 
Security Protocol using Kryptonet key management"));
+        ProtocolNumbers.add(new ProtocolNumber(57, "SKIP", "SKIP"));
+        ProtocolNumbers.add(new ProtocolNumber(58, "IPv6-ICMP", "ICMP for 
IPv6"));
+        ProtocolNumbers.add(new ProtocolNumber(59, "IPv6-NoNxt", "No Next 
Header for IPv6"));
+        ProtocolNumbers.add(new ProtocolNumber(60, "IPv6-Opts", "Destination 
Options for IPv6"));
+        ProtocolNumbers.add(new ProtocolNumber(61, "Any host internal 
protocol", "Any host internal protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(62, "CFTP", "CFTP"));
+        ProtocolNumbers.add(new ProtocolNumber(63, "Any local network", "Any 
local network"));
+        ProtocolNumbers.add(new ProtocolNumber(64, "SAT-EXPAK", "SATNET and 
Backroom EXPAK"));
+        ProtocolNumbers.add(new ProtocolNumber(65, "KRYPTOLAN", "Kryptolan"));
+        ProtocolNumbers.add(new ProtocolNumber(66, "RVD", "MIT Remote Virtual 
Disk Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(67, "IPPC", "Internet Pluribus 
Packet Core"));
+        ProtocolNumbers.add(new ProtocolNumber(68, "Any distributed file 
system", "Any distributed file system"));
+        ProtocolNumbers.add(new ProtocolNumber(69, "SAT-MON", "SATNET 
Monitoring"));
+        ProtocolNumbers.add(new ProtocolNumber(70, "VISA", "VISA Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(71, "IPCV", "Internet Packet 
Core Utility"));
+        ProtocolNumbers.add(new ProtocolNumber(72, "CPNX", "Computer Protocol 
Network Executive"));
+        ProtocolNumbers.add(new ProtocolNumber(73, "CPHB", "Computer Protocol 
Heart Beat"));
+        ProtocolNumbers.add(new ProtocolNumber(74, "WSN", "Wang Span 
Network"));
+        ProtocolNumbers.add(new ProtocolNumber(75, "PVP", "Packet Video 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(76, "BR-SAT-MON", "Backroom 
SATNET Monitoring"));
+        ProtocolNumbers.add(new ProtocolNumber(77, "SUN-ND", "SUN ND 
PROTOCOL-Temporary"));
+        ProtocolNumbers.add(new ProtocolNumber(78, "WB-MON", "WIDEBAND 
Monitoring"));
+        ProtocolNumbers.add(new ProtocolNumber(79, "WB-EXPAK", "WIDEBAND 
EXPAK"));
+        ProtocolNumbers.add(new ProtocolNumber(80, "ISO-IP", "ISO Internet 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(81, "VMTP", "VMTP"));
+        ProtocolNumbers.add(new ProtocolNumber(82, "SECURE-VMTP", 
"SECURE-VMTP"));
+        ProtocolNumbers.add(new ProtocolNumber(83, "VINES", "VINES"));
+        ProtocolNumbers.add(new ProtocolNumber(84, "TTP or IPTM", "Internet 
Protocol Traffic Manager"));
+        ProtocolNumbers.add(new ProtocolNumber(85, "NSFNET-IGP", 
"NSFNET-IGP"));
+        ProtocolNumbers.add(new ProtocolNumber(86, "DGP", "Dissimilar Gateway 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(87, "TCF", "TCF"));
+        ProtocolNumbers.add(new ProtocolNumber(88, "EIGRP", "EIGRP"));
+        ProtocolNumbers.add(new ProtocolNumber(89, "OSPFIGP", "OSPFIGP"));
+        ProtocolNumbers.add(new ProtocolNumber(90, "Sprite-RPC", "Sprite RPC 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(91, "LARP", "Locus Address 
Resolution Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(92, "MTP", "Multicast Transport 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(93, "AX.25", "AX.25 Frames"));
+        ProtocolNumbers.add(new ProtocolNumber(94, "IPIP", "IP-within-IP 
Encapsulation Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(95, "MICP (deprecated)", 
"Mobile Internetworking Control Pro."));
+        ProtocolNumbers.add(new ProtocolNumber(96, "SCC-SP", "Semaphore 
Communications Sec. Pro."));
+        ProtocolNumbers.add(new ProtocolNumber(97, "ETHERIP", 
"Ethernet-within-IP Encapsulation"));
+        ProtocolNumbers.add(new ProtocolNumber(98, "ENCAP", "Encapsulation 
Header"));
+        ProtocolNumbers.add(new ProtocolNumber(99, "Any private encryption 
scheme", "Any private encryption scheme"));
+        ProtocolNumbers.add(new ProtocolNumber(100, "GMTP", "GMTP"));
+        ProtocolNumbers.add(new ProtocolNumber(101, "IFMP", "Ipsilon Flow 
Management Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(102, "PNNI", "PNNI over IP"));
+        ProtocolNumbers.add(new ProtocolNumber(103, "PIM", "Protocol 
Independent Multicast"));
+        ProtocolNumbers.add(new ProtocolNumber(104, "ARIS", "ARIS"));
+        ProtocolNumbers.add(new ProtocolNumber(105, "SCPS", "SCPS"));
+        ProtocolNumbers.add(new ProtocolNumber(106, "QNX", "QNX"));
+        ProtocolNumbers.add(new ProtocolNumber(107, "A/N", "Active Networks"));
+        ProtocolNumbers.add(new ProtocolNumber(108, "IPComp", "IP Payload 
Compression Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(109, "SNP", "Sitara Networks 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(110, "Compaq-Peer", "Compaq 
Peer Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(111, "IPX-in-IP", "IPX in IP"));
+        ProtocolNumbers.add(new ProtocolNumber(112, "VRRP", "Virtual Router 
Redundancy Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(113, "PGM", "PGM Reliable 
Transport Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(114, "Any 0-hop protocol", "Any 
0-hop protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(115, "L2TP", "Layer Two 
Tunneling Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(116, "DDX", "D-II Data Exchange 
(DDX)"));
+        ProtocolNumbers.add(new ProtocolNumber(117, "IATP", "Interactive Agent 
Transfer Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(118, "STP", "Schedule Transfer 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(119, "SRP", "SpectraLink Radio 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(120, "UTI", "UTI"));
+        ProtocolNumbers.add(new ProtocolNumber(121, "SMP", "Simple Message 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(122, "SM (deprecated)", "Simple 
Multicast Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(123, "PTP", "Performance 
Transparency Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(124, "ISIS over IPv4", ""));
+        ProtocolNumbers.add(new ProtocolNumber(125, "FIRE", ""));
+        ProtocolNumbers.add(new ProtocolNumber(126, "CRTP", "Combat Radio 
Transport Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(127, "CRUDP", "Combat Radio 
User Datagram"));
+        ProtocolNumbers.add(new ProtocolNumber(128, "SSCOPMCE", ""));
+        ProtocolNumbers.add(new ProtocolNumber(129, "IPLT", ""));
+        ProtocolNumbers.add(new ProtocolNumber(130, "SPS", "Secure Packet 
Shield"));
+        ProtocolNumbers.add(new ProtocolNumber(131, "PIPE", "Private IP 
Encapsulation within IP"));
+        ProtocolNumbers.add(new ProtocolNumber(132, "SCTP", "Stream Control 
Transmission Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(133, "FC", "Fibre Channel"));
+        ProtocolNumbers.add(new ProtocolNumber(134, "RSVP-E2E-IGNORE", ""));
+        ProtocolNumbers.add(new ProtocolNumber(135, "Mobility Header", ""));
+        ProtocolNumbers.add(new ProtocolNumber(136, "UDPLite", ""));
+        ProtocolNumbers.add(new ProtocolNumber(137, "MPLS-in-IP", ""));
+        ProtocolNumbers.add(new ProtocolNumber(138, "manet", "MANET 
Protocols"));
+        ProtocolNumbers.add(new ProtocolNumber(139, "HIP", "Host Identity 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(140, "Shim6", "Shim6 
Protocol"));
+        ProtocolNumbers.add(new ProtocolNumber(141, "WESP", "Wrapped 
Encapsulating Security Payload"));
+        ProtocolNumbers.add(new ProtocolNumber(142, "ROHC", "Robust Header 
Compression"));
+        ProtocolNumbers.add(new ProtocolNumber(143, "Ethernet", "Ethernet"));
+        ProtocolNumbers.add(new ProtocolNumber(144, "AGGFRAG", "AGGFRAG 
encapsulation payload for ESP"));
+        ProtocolNumbers.add(new ProtocolNumber(145, "NSH", "Network Service 
Header"));
+    }
+    /**
+     * Different Internet Protocol Numbers.
+     */
+    public static class ProtocolNumber {
+
+        private final Integer number;
+
+        private final String keyword;
+
+        private final String protocol;
+
+        public ProtocolNumber(Integer number, String keyword, String protocol) 
{
+            this.number = number;
+            this.keyword = keyword;
+            this.protocol = protocol;
+        }
+
+        public Integer getNumber() {
+            return number;
+        }
+
+        public String getKeyword() {
+            return keyword;
+        }
+
+        public String getProtocol() {
+            return protocol;
+        }
+    }
+
+    // Refer to 
https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
+    public static List<IcmpType> IcmpTypes = new ArrayList<>();
+    static {
+        IcmpTypes.add(new IcmpType(0, "Echo Reply"));
+        IcmpTypes.add(new IcmpType(3, "Destination Unreachable"));
+        IcmpTypes.add(new IcmpType(5, "Redirect"));
+        IcmpTypes.add(new IcmpType(8, "Echo"));
+        IcmpTypes.add(new IcmpType(9, "Router Advertisement"));
+        IcmpTypes.add(new IcmpType(10, "Router Solicitation"));
+        IcmpTypes.add(new IcmpType(11, "Time Exceeded"));
+        IcmpTypes.add(new IcmpType(12, "Parameter Problem"));
+        IcmpTypes.add(new IcmpType(13, "Timestamp"));
+        IcmpTypes.add(new IcmpType(14, "Timestamp Reply"));
+        IcmpTypes.add(new IcmpType(40, "Photuris"));
+        IcmpTypes.add(new IcmpType(42, "Extended Echo Request"));
+        IcmpTypes.add(new IcmpType(43, "Extended Echo Reply"));
+    }
+
+    /**
+     * Different types of ICMP (Internet Control Message Protocol).
+     */
+    public static class IcmpType {
+
+        private final Integer type;
+
+        private final String description;
+
+        private final List<IcmpCode> icmpCodes = new ArrayList<>();
+
+        public IcmpType(Integer type, String description) {
+            this.type = type;
+            this.description = description;
+        }
+
+        public Integer getType() {
+            return type;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public List<IcmpCode> getIcmpCodes() {
+            return icmpCodes;
+        }
+
+        public void addIcmpCodes(IcmpCode code) {
+            this.icmpCodes.add(code);
+        }
+    }
+
+    static void addIcmpCode(IcmpCode code) {
+        IcmpType type = IcmpTypes.stream().filter(icmpType -> 
icmpType.getType().equals(code.getType())).findFirst().get();
+        type.addIcmpCodes(code);
+    }
+
+    public static boolean validateIcmpTypeAndCode(Integer type, Integer code) {
+        if (type != null && type != -1) {
+            Optional<IcmpType> icmpTypeOptional = IcmpTypes.stream().filter(t 
-> t.getType().equals(type)).findFirst();
+            if (icmpTypeOptional == null || icmpTypeOptional.isEmpty()) {
+                return false;
+            }
+            IcmpType icmpType = icmpTypeOptional.get();
+            if (code != null && code != -1) {
+                Optional<IcmpCode> icmpCodeOptional = 
icmpType.getIcmpCodes().stream().filter(c -> 
c.getCode().equals(code)).findFirst();
+                if (icmpCodeOptional == null || icmpCodeOptional.isEmpty()) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    static {
+        addIcmpCode(new IcmpCode(0, 0, "Echo reply"));
+        addIcmpCode(new IcmpCode(3, 0, "Net unreachable"));
+        addIcmpCode(new IcmpCode(3, 1, "Host unreachable"));
+        addIcmpCode(new IcmpCode(3, 2, "Protocol unreachable"));
+        addIcmpCode(new IcmpCode(3, 3, "Port unreachable"));
+        addIcmpCode(new IcmpCode(3, 4, "Fragmentation needed and DF set"));
+        addIcmpCode(new IcmpCode(3, 5, "Source route failed"));
+        addIcmpCode(new IcmpCode(3, 6, "Destination network unknown"));
+        addIcmpCode(new IcmpCode(3, 7, "Destination host unknown"));
+        addIcmpCode(new IcmpCode(3, 9, "Network administratively prohibited"));
+        addIcmpCode(new IcmpCode(3, 10, "Host administratively prohibited"));
+        addIcmpCode(new IcmpCode(3, 11, "Network unreachable for ToS"));
+        addIcmpCode(new IcmpCode(3, 12, "Host unreachable for ToS"));
+        addIcmpCode(new IcmpCode(3, 13, "Communication administratively 
prohibited"));
+        addIcmpCode(new IcmpCode(3, 14, "Host Precedence Violation"));
+        addIcmpCode(new IcmpCode(3, 15, "Precedence cutoff in effect"));
+        addIcmpCode(new IcmpCode(5, 0, "Redirect Datagram for the Network"));
+        addIcmpCode(new IcmpCode(5, 1, "Redirect Datagram for the Host"));
+        addIcmpCode(new IcmpCode(5, 2, "Redirect Datagram for the ToS & 
network"));
+        addIcmpCode(new IcmpCode(5, 3, "Redirect Datagram for the ToS & 
host"));
+        addIcmpCode(new IcmpCode(8, 0, "Echo request"));
+        addIcmpCode(new IcmpCode(9, 0, "Router advertisement"));
+        addIcmpCode(new IcmpCode(9, 16, "Does not route common traffic"));
+        addIcmpCode(new IcmpCode(10, 0, "Router solicitation"));
+        addIcmpCode(new IcmpCode(11, 0, "TTL expired in transit"));
+        addIcmpCode(new IcmpCode(11, 1, "Fragment reassembly time exceeded"));
+        addIcmpCode(new IcmpCode(12, 0, "Parameter problem: Pointer indicates 
the error"));
+        addIcmpCode(new IcmpCode(12, 1, "Parameter problem: Missing a required 
option"));
+        addIcmpCode(new IcmpCode(12, 2, "Parameter problem: Bad length"));
+        addIcmpCode(new IcmpCode(13, 0, "Timestamp"));
+        addIcmpCode(new IcmpCode(14, 0, "Timestamp reply"));
+        addIcmpCode(new IcmpCode(40, 0, "Photuris: Security failures"));
+        addIcmpCode(new IcmpCode(40, 1, "Photuris: Authentication failed"));
+        addIcmpCode(new IcmpCode(40, 2, "Photuris: Decompression failed"));
+        addIcmpCode(new IcmpCode(40, 3, "Photuris: Decryption failed"));
+        addIcmpCode(new IcmpCode(40, 4, "Photuris: Need authentication"));
+        addIcmpCode(new IcmpCode(40, 5, "Photuris: Need authorization"));
+    }
+
+    /**
+     * Code field of ICMP types.
+     */
+    public static class IcmpCode {
+
+        private final Integer type;
+        private final Integer code;
+        private final String description;
+
+        public IcmpCode(Integer type, Integer code, String description) {
+            this.type = type;
+            this.code = code;
+            this.description = description;
+        }
+
+        public Integer getType() {
+            return type;
+        }
+
+        public Integer getCode() {
+            return code;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+    }
+}
diff --git a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java 
b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
index defb440c2a5..5da4ac96d69 100644
--- a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
+++ b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
@@ -843,4 +843,50 @@ public class NetUtilsTest {
         Assert.assertEquals(expected, result);
         networkInterfaceMocked.close();
     }
+
+    @Test
+    public void validateIcmpTypeAndCodesGood1() {
+        NetUtils.validateIcmpTypeAndCode(-1, -1);
+    }
+    @Test
+    public void validateIcmpTypeAndCodesGood2() {
+        NetUtils.validateIcmpTypeAndCode(3, 2);
+    }
+
+    @Test(expected=CloudRuntimeException.class)
+    public void validateIcmpTypeAndCodesThrowsException1() {
+        NetUtils.validateIcmpTypeAndCode(null, null);
+    }
+    @Test(expected=CloudRuntimeException.class)
+    public void validateIcmpTypeAndCodesThrowsException2() {
+        NetUtils.validateIcmpTypeAndCode(null, -1);
+    }
+    @Test(expected=CloudRuntimeException.class)
+    public void validateIcmpTypeAndCodesThrowsException3() {
+        NetUtils.validateIcmpTypeAndCode(-1, null);
+    }
+    @Test(expected=CloudRuntimeException.class)
+    public void validateIcmpTypeAndCodesThrowsException4() {
+        NetUtils.validateIcmpTypeAndCode(-1, 2);
+    }
+    @Test(expected=CloudRuntimeException.class)
+    public void validateIcmpTypeAndCodesThrowsException5() {
+        NetUtils.validateIcmpTypeAndCode(3, -1);
+    }
+    @Test(expected=CloudRuntimeException.class)
+    public void validateIcmpTypeAndCodesThrowsException6() {
+        NetUtils.validateIcmpTypeAndCode(-2, 2);
+    }
+    @Test(expected=CloudRuntimeException.class)
+    public void validateIcmpTypeAndCodesThrowsException7() {
+        NetUtils.validateIcmpTypeAndCode(257, 2);
+    }
+    @Test(expected=CloudRuntimeException.class)
+    public void validateIcmpTypeAndCodesThrowsException8() {
+        NetUtils.validateIcmpTypeAndCode(3, -2);
+    }
+    @Test(expected=CloudRuntimeException.class)
+    public void validateIcmpTypeAndCodesThrowsException9() {
+        NetUtils.validateIcmpTypeAndCode(3, -257);
+    }
 }
diff --git a/utils/src/test/java/com/cloud/utils/net/NetworkProtocolsTest.java 
b/utils/src/test/java/com/cloud/utils/net/NetworkProtocolsTest.java
new file mode 100644
index 00000000000..9f1780ba2e7
--- /dev/null
+++ b/utils/src/test/java/com/cloud/utils/net/NetworkProtocolsTest.java
@@ -0,0 +1,47 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package com.cloud.utils.net;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.junit.runner.RunWith;
+import org.mockito.runners.MockitoJUnitRunner;
+
+
+@RunWith(MockitoJUnitRunner.class)
+public class NetworkProtocolsTest {
+
+    @Test
+    public void validateIcmpTypeAndCode() {
+        validateIcmpTypeAndCodeInternal(null, null, true);
+        validateIcmpTypeAndCodeInternal(null, -1, true);
+        validateIcmpTypeAndCodeInternal(-1, -1, true);
+        validateIcmpTypeAndCodeInternal(3, -1, true);
+        validateIcmpTypeAndCodeInternal(3, 15, true);
+        validateIcmpTypeAndCodeInternal(4, -1, false);
+        validateIcmpTypeAndCodeInternal(5, 10, false);
+    }
+
+    private void validateIcmpTypeAndCodeInternal(Integer type, Integer code, 
boolean expected) {
+        boolean actual = NetworkProtocols.validateIcmpTypeAndCode(type, code);
+        Assert.assertEquals(expected, actual);
+    }
+}

Reply via email to