Skip to content

Commit

Permalink
Merge pull request #244 from model-driven-devops/icmp_augment
Browse files Browse the repository at this point in the history
feat: icmp msg types and codes support
  • Loading branch information
stmosher authored Jun 27, 2023
2 parents 21d1c22 + ac1d135 commit cc7193f
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 31 deletions.
99 changes: 74 additions & 25 deletions mdd/python/translation/openconfig_xe/xe_acls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,69 @@
from translation.common import prefix_to_network_and_mask
from translation.common import get_interface_type_and_number

regex_ports = re.compile(r'(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5][0-9]{4}|[0-9]{1,4})\.\.(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5][0-9]{4}|[0-9]{1,4})')
regex_ports = re.compile(
r'(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5][0-9]{4}|[0-9]{1,4})\.\.(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5][0-9]{4}|[0-9]{1,4})')


def xe_acls_program_service(self, nso_props) -> None:
"""
Program service for xe NED features
"""
protocols_oc_to_xe = {1: 'icmp',
'oc-pkt-match-types:IP_ICMP': 'icmp',
2: 'igmp',
'oc-pkt-match-types:IP_IGMP': 'igmp',
4: 'ipinip',
'oc-pkt-match-types:IP_IN_IP': 'ipinip',
6: 'tcp',
'oc-pkt-match-types:IP_TCP': 'tcp',
17: 'udp',
'oc-pkt-match-types:IP_UDP': 'udp',
47: 'gre',
'oc-pkt-match-types:IP_GRE': 'gre',
50: 'esp',
'oc-pkt-match-types-ext:IP_ESP': 'esp',
51: 'ahp',
'oc-pkt-match-types:IP_AUTH': 'ahp',
103: 'pim',
'oc-pkt-match-types:IP_PIM': 'pim'}

protocols_oc_to_xe = {
1: 'icmp',
'oc-pkt-match-types:IP_ICMP': 'icmp',
2: 'igmp',
'oc-pkt-match-types:IP_IGMP': 'igmp',
4: 'ipinip',
'oc-pkt-match-types:IP_IN_IP': 'ipinip',
6: 'tcp',
'oc-pkt-match-types:IP_TCP': 'tcp',
17: 'udp',
'oc-pkt-match-types:IP_UDP': 'udp',
47: 'gre',
'oc-pkt-match-types:IP_GRE': 'gre',
50: 'esp',
'oc-pkt-match-types-ext:IP_ESP': 'esp',
51: 'ahp',
'oc-pkt-match-types:IP_AUTH': 'ahp',
103: 'pim',
'oc-pkt-match-types:IP_PIM': 'pim'}
icmp_types_to_names = {
(0, 0): "echo-reply",
(3, 0): "unreachable",
(3, 1): "host-unreachable",
(3, 2): "protocol-unreachable",
(3, 3): "port-unreachable",
(3, 4): "packet-too-big",
(3, 5): "source-route-failed",
(3, 6): "network-unknown",
(3, 7): "host-unknown",
(3, 9): "dod-net-prohibited",
(3, 10): "dod-host-prohibited",
(3, 11): "net-tos-unreachable",
(3, 12): "host-tos-unreachable",
(3, 13): "administratively-prohibited",
(4, 0): "source-quench",
(5, 0): "net-redirect",
(5, 1): "host-redirect",
(5, 2): "net-tos-redirect",
(5, 3): "host-tos-redirect",
(6, 0): "alternate-address",
(8, 0): "echo",
(11, 0): "time-exceeded",
(11, 1): "reassembly-timeout",
(12, 0): "parameter-problem",
(12, 1): "option-missing",
(12, 2): "no-room-for-option",
(13, 0): "timestamp-request",
(14, 0): "timestamp-reply",
(15, 0): "information-request",
(16, 0): "information-reply",
(17, 0): "mask-request",
(18, 0): "mask-reply",
(0, 0): "echo-reply",
(11, 0): "time-exceeded",
(12, 0): "general-parameter-problem"}
actions_oc_to_xe = {'oc-acl:ACCEPT': 'permit',
'oc-acl:DROP': 'deny',
'oc-acl:REJECT': 'deny'}
Expand Down Expand Up @@ -98,6 +135,13 @@ def xe_acls_program_service(self, nso_props) -> None:
('TCP_ACK' in i.transport.config.tcp_flags) and \
('TCP_RST' in i.transport.config.tcp_flags):
rule += 'established '
if i.ipv4.config.protocol == 'oc-pkt-match-types:IP_ICMP':
if isinstance(i.icmp_v4.config.type, int) and isinstance(i.icmp_v4.config.code, int):
icmp_message = icmp_types_to_names.get((i.icmp_v4.config.type, i.icmp_v4.config.code))
if icmp_message:
rule += f'{icmp_message} '
else:
rule += f'{i.icmp_v4.config.type} {i.icmp_v4.config.code} '
if i.actions.config.log_action:
if i.actions.config.log_action == 'oc-acl:LOG_SYSLOG':
rule += 'log-input'
Expand Down Expand Up @@ -147,7 +191,8 @@ def xe_acls_interfaces_program_service(self, nso_props) -> None:
interface_cdb = class_attribute[
f'{interface_number}.{service_acl_interface.interface_ref.config.subinterface}']
elif interface_type == 'Port_channel':
interface_cdb = nso_props.root.devices.device[nso_props.device_name].config.ios__interface.Port_channel_subinterface.Port_channel[
interface_cdb = nso_props.root.devices.device[
nso_props.device_name].config.ios__interface.Port_channel_subinterface.Port_channel[
f'{interface_number}.{service_acl_interface.interface_ref.config.subinterface}']

# Apply ACLs TODO add other ACL types
Expand All @@ -157,24 +202,28 @@ def xe_acls_interfaces_program_service(self, nso_props) -> None:
if not interface_cdb.ip.access_group.exists('out'):
interface_cdb.ip.access_group.create('out')
interface_cdb.ip.access_group['out'].access_list = acl.set_name
self.log.info(f'{nso_props.device_name} ACL {acl.set_name} added to interface {service_acl_interface.id} egress')
self.log.info(
f'{nso_props.device_name} ACL {acl.set_name} added to interface {service_acl_interface.id} egress')
if acl.type == 'oc-acl-ext:ACL_IPV4_STANDARD':
if not interface_cdb.ip.access_group.exists('out'):
interface_cdb.ip.access_group.create('out')
interface_cdb.ip.access_group['out'].access_list = acl.set_name
self.log.info(f'{nso_props.device_name} ACL {acl.set_name} added to interface {service_acl_interface.id} egress')
self.log.info(
f'{nso_props.device_name} ACL {acl.set_name} added to interface {service_acl_interface.id} egress')
if service_acl_interface.ingress_acl_sets:
for acl in service_acl_interface.ingress_acl_sets.ingress_acl_set:
if acl.type == 'oc-acl:ACL_IPV4':
if not interface_cdb.ip.access_group.exists('in'):
interface_cdb.ip.access_group.create('in')
interface_cdb.ip.access_group['in'].access_list = acl.set_name
self.log.info(f'{nso_props.device_name} ACL {acl.set_name} added to interface {service_acl_interface.id} ingress')
self.log.info(
f'{nso_props.device_name} ACL {acl.set_name} added to interface {service_acl_interface.id} ingress')
if acl.type == 'oc-acl-ext:ACL_IPV4_STANDARD':
if not interface_cdb.ip.access_group.exists('in'):
interface_cdb.ip.access_group.create('in')
interface_cdb.ip.access_group['in'].access_list = acl.set_name
self.log.info(f'{nso_props.device_name} ACL {acl.set_name} added to interface {service_acl_interface.id} ingress')
self.log.info(
f'{nso_props.device_name} ACL {acl.set_name} added to interface {service_acl_interface.id} ingress')


def xe_acls_lines_program_service(self, nso_props) -> None:
Expand Down
24 changes: 23 additions & 1 deletion mdd/src/yang/extensions/openconfig-acl-ext.yang
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ module openconfig-acl-ext{

description
"Augmentation to openconfig acl model.";

revision "2023-06-23" {
description
"Added support for ICMP message types and codes";
}
revision "2022-09-27" {
description
"Added config container and fixed typo";
Expand All @@ -47,6 +50,25 @@ module openconfig-acl-ext{
}
}
}
augment /ncs:devices/ncs:device/mdd:openconfig/oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry {
when "./oc-acl:ipv4/oc-acl:config/oc-acl:protocol='oc-pkt-match-types:IP_ICMP'" ;
container icmp-v4 {
container config {
description
"Configuration ICMPv4 types and codes";
leaf type {
type uint8;
description
"ICMP message type.";
}
leaf code {
type uint8;
description
"ICMP message code.";
}
}
}
}
augment /ncs:devices/ncs:device/mdd:openconfig/oc-acl:acl {
container ntp {
description
Expand Down
60 changes: 58 additions & 2 deletions package_nso_to_oc/xe/xe_acls.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,40 @@
"esp": "IP_ESP",
"pim": "IP_PIM"
}
icmp_names_to_types = {
'administratively-prohibited': (3, 13),
'alternate-address': (6, 0),
'dod-host-prohibited': (3, 10),
'dod-net-prohibited': (3, 9),
'echo': (8, 0),
'echo-reply': (0, 0),
'general-parameter-problem': (12, 0),
'host-redirect': (5, 1),
'host-tos-redirect': (5, 3),
'host-tos-unreachable': (3, 12),
'host-unknown': (3, 7),
'host-unreachable': (3, 1),
'information-reply': (16, 0),
'information-request': (15, 0),
'mask-reply': (18, 0),
'mask-request': (17, 0),
'net-redirect': (5, 0),
'net-tos-redirect': (5, 2),
'net-tos-unreachable': (3, 11),
'net-unreachable': (3, 0),
'network-unknown': (3, 6),
'no-room-for-option': (12, 2),
'option-missing': (12, 1),
'packet-too-big': (3, 4),
'port-unreachable': (3, 3),
'protocol-unreachable': (3, 2),
'reassembly-timeout': (11, 1),
'source-quench': (4, 0),
'source-route-failed': (3, 5),
'time-exceeded': (11, 0),
'timestamp-reply': (14, 0),
'timestamp-request': (13, 0),
'unreachable': (3, 0)}
# OC has an additional forwarding action, "DROP", which also translates to "deny" in XE.
actions_xe_to_oc = {
"permit": "ACCEPT",
Expand Down Expand Up @@ -266,6 +300,8 @@ def __set_ip_and_port(self, rule_parts, current_index, entry, is_source, index):

if rule_parts[index + 1] == "tcp" or rule_parts[index + 1] == "udp":
current_index = self.__set_port(rule_parts, current_index, entry, is_source)
elif rule_parts[index + 1] == "icmp" and not is_source:
current_index = self.__set_icmp(rule_parts, current_index, entry)

return current_index

Expand All @@ -285,7 +321,6 @@ def __set_ip_and_network(self, rule_parts, current_index, entry, is_source):
else:
self.__get_ipv4_config(entry)[
"openconfig-acl:destination-address"] = f"{rule_parts[current_index + 1]}/32"

return current_index + 2
elif (rule_parts[0].isdigit() and len(rule_parts) == 3) \
or (rule_parts[0].isdigit() and len(rule_parts) == 4 and rule_parts[-1] == "log") \
Expand Down Expand Up @@ -323,7 +358,6 @@ def __set_ip_and_network(self, rule_parts, current_index, entry, is_source):
self.__get_ipv4_config(entry)[self._src_addr_key] = f"{ip}/{prefixlen}"
else:
self.__get_ipv4_config(entry)["openconfig-acl:destination-address"] = f"{ip}/{prefixlen}"

return current_index + 2

def __set_port(self, rule_parts, current_index, entry, is_source):
Expand Down Expand Up @@ -387,6 +421,28 @@ def __set_port(self, rule_parts, current_index, entry, is_source):

return current_index + 2

def __set_icmp(self, rule_parts, current_index, entry):
if len(rule_parts) <= current_index:
# end of the rule or there's messages specified
return current_index
elif rule_parts[current_index] in icmp_names_to_types:
msg, code = icmp_names_to_types[rule_parts[current_index]]
entry['openconfig-acl-ext:icmp-v4'] = {'openconfig-acl-ext:config':
{'openconfig-acl-ext:type': msg,
'openconfig-acl-ext:code': code}}
return current_index + 1
elif rule_parts[current_index].isdigit():
entry['openconfig-acl-ext:icmp-v4'] = {'openconfig-acl-ext:config':
{'openconfig-acl-ext:type': rule_parts[current_index],
'openconfig-acl-ext:code': 0}}
if current_index + 1 < len(rule_parts) and rule_parts[current_index + 1].isdigit():
entry['openconfig-acl-ext:icmp-v4']['openconfig-acl-ext:config']['openconfig-acl-ext:code'] = rule_parts[current_index + 1]
return current_index + 2
return current_index + 1
else:
return current_index


def __set_tcp_flags(self, rule_parts, current_index, entry):
if len(rule_parts) <= current_index or not rule_parts[current_index] in ["ack", "rst", "established"]:
return current_index
Expand Down
2 changes: 1 addition & 1 deletion test/requirements.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
collections:
- name: https://github.com/ciscodevnet/ansible-cml.git
type: git
- name: https://github.com/model-driven-devops/ansible-mdd.git,1.1.3
- name: https://github.com/model-driven-devops/ansible-mdd.git,1.2.5
type: git
- name: https://github.com/model-driven-devops/ansible-nso.git
type: git
74 changes: 74 additions & 0 deletions test/tests/xe/xe_acls_extended.yml
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,80 @@
rollback: false
assertion_ignore_errors: false

- name: test acl_type_extended_icmp configurations
tags:
- acl_type_extended_icmp
import_role:
name: nso-openconfig-test
vars:
content: |
mdd:openconfig:
openconfig-acl:acl:
openconfig-acl:acl-sets:
openconfig-acl:acl-set:
- openconfig-acl:name: 'icmp'
openconfig-acl:type: 'ACL_IPV4'
openconfig-acl:config:
openconfig-acl:description: 'icmp message types and codes'
openconfig-acl:name: 'icmp'
openconfig-acl:type: 'ACL_IPV4'
openconfig-acl:acl-entries:
openconfig-acl:acl-entry:
- openconfig-acl:sequence-id: 10
openconfig-acl:actions:
openconfig-acl:config:
openconfig-acl:forwarding-action: 'ACCEPT'
openconfig-acl:log-action: 'LOG_NONE'
openconfig-acl:config:
openconfig-acl:description: 'test type and code 10'
openconfig-acl:sequence-id: 10
openconfig-acl:ipv4:
openconfig-acl:config:
openconfig-acl:destination-address: '0.0.0.0/0'
openconfig-acl:protocol: 'IP_ICMP'
openconfig-acl:source-address: '0.0.0.0/0'
openconfig-acl-ext:icmp-v4:
openconfig-acl-ext:config:
openconfig-acl-ext:type: 222
openconfig-acl-ext:code: 223
- openconfig-acl:sequence-id: 20
openconfig-acl:actions:
openconfig-acl:config:
openconfig-acl:forwarding-action: 'ACCEPT'
openconfig-acl:log-action: 'LOG_NONE'
openconfig-acl:config:
openconfig-acl:description: 'test type and code 20'
openconfig-acl:sequence-id: 20
openconfig-acl:ipv4:
openconfig-acl:config:
openconfig-acl:destination-address: '0.0.0.0/0'
openconfig-acl:protocol: 'IP_ICMP'
openconfig-acl:source-address: '0.0.0.0/0'
openconfig-acl-ext:icmp-v4:
openconfig-acl-ext:config:
openconfig-acl-ext:type: 3
openconfig-acl-ext:code: 4
- openconfig-acl:sequence-id: 30
openconfig-acl:actions:
openconfig-acl:config:
openconfig-acl:forwarding-action: 'REJECT'
openconfig-acl:log-action: 'LOG_SYSLOG'
openconfig-acl:config:
openconfig-acl:description: 'drop log other packets'
openconfig-acl:sequence-id: 30
openconfig-acl:ipv4:
openconfig-acl:config:
openconfig-acl:destination-address: '0.0.0.0/0'
openconfig-acl:source-address: '0.0.0.0/0'
assertions:
- "'+ip access-list extended icmp:' in changes"
- "'+ 10 permit icmp any any 222 223:' in changes"
- "'+ 20 permit icmp any any packet-too-big:' in changes"
- "'+ 30 deny ip any any log-input:' in changes"
api_method: PATCH
rollback: false
assertion_ignore_errors: false

- hosts: nso
connection: local
gather_facts: no
Expand Down
Loading

0 comments on commit cc7193f

Please sign in to comment.