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
This commit is contained in:
Wei Zhou 2024-02-02 15:49:04 +01:00 committed by GitHub
parent 7dffbc6e47
commit af8a582055
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 1054 additions and 52 deletions

View File

@ -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";

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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());

View File

@ -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");
}

View File

@ -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);

View File

@ -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);

View File

@ -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') {

View File

@ -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', {

View File

@ -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;

View File

@ -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

View File

@ -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 ");
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}