diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md index 6358c837..acbbdb84 100644 --- a/doc/ChangeLog.md +++ b/doc/ChangeLog.md @@ -12,6 +12,10 @@ All notable changes to the project are documented in this file. - Upgrade netopeer2 (NETCONF) to 2.4.1 - Add documentation on Infix upgrading and downgrading, issue #1009 - Add HDMI and USB support for iMX8MP-evk +- Enforced strict format for LLDP destination MAC address: + - Only accepts colon-separated format: `01:80:C2:00:00:0E` +- Add `show lldp` command to show discovered neighbors per interface. +- Add configuration support for per-interface LLDP administrative status ### Fixes - Fix containers with multiple mounts @@ -128,6 +132,7 @@ All notable changes to the project are documented in this file. - Add support for GRE/GRETAP tunnels - Add support for STP/RSTP on bridges - Add support for VXLAN tunnels + - Add support for configuring global LLDP `message-tx-interval` ### Fixes diff --git a/doc/discovery.md b/doc/discovery.md index 4bfcbb28..83b16ab3 100644 --- a/doc/discovery.md +++ b/doc/discovery.md @@ -46,7 +46,7 @@ admin@infix-c0-ff-ee:~$ ## LLDP -Infix supports LLDP (IEEE 802.1AB). For a device with factory default +Infix supports LLDP (IEEE 802.1AB). For a device with factory default settings, the link-local IPv6 address can be read from the Management Address TLV using *tcpdump* or other sniffing tools[^1]: @@ -131,6 +131,10 @@ tcpdump: listening on tap0, link-type EN10MB (Ethernet), snapshot length 262144 linux-pc:# ``` +The following capabilities are available via NETCONF/RESTCONF or the Infix CLI. + +### LLDP Enable/Disable + The LLDP service can be disabled using the following commands. ``` @@ -140,6 +144,59 @@ admin@infix-c0-ff-ee:/config/> leave admin@infix-c0-ff-ee:/> ``` +To reenable it from the CLI config mode: + +``` +admin@test-00-01-00:/config/> set lldp enabled +admin@test-00-01-00:/config/> leave +``` + +### LLDP Message Transmission Interval + +By default, LLDP uses a `message-tx-interval` of 30 seconds, as defined +by the IEEE standard. Infix allows this value to be customized. +To change it using the CLI: + +``` +admin@test-00-01-00:/config/> set lldp message-tx-interval 1 +admin@test-00-01-00:/config/> leave +``` + +### LLDP Administrative Status per Interface + +Infix supports configuring the LLDP administrative status on a per-port +basis. The default mode is `tx-and-rx`, but the following options are +also supported: + +- `rx-only` – Receive LLDP packets only +- `tx-only` – Transmit LLDP packets only +- `disabled` – Disable LLDP on the interface + +Example configuration: + +``` +admin@test-00-01-00:/config/> set lldp port e8 dest-mac-address 01:80:C2:00:00:0E admin-status disabled +admin@test-00-01-00:/config/> set lldp port e5 dest-mac-address 01:80:C2:00:00:0E admin-status rx-only +admin@test-00-01-00:/config/> set lldp port e6 dest-mac-address 01:80:C2:00:00:0E admin-status tx-only +admin@test-00-01-00:/config/> leave +``` + +> [!NOTE] +> The destination MAC address must be the standard LLDP multicast +> address: `01:80:C2:00:00:0E`. + +### Displaying LLDP Neighbor Information + +In CLI mode, Infix also provides a convenient `show lldp` command to +list LLDP neighbors detected on each interface: + +``` +admin@test-00-01-00:/> show lldp +INTERFACE REM-IDX TIME CHASSIS-ID PORT-ID +e5 1 902 00:a0:85:00:04:01 00:a0:85:00:04:07 +e6 3 897 00:a0:85:00:03:01 00:a0:85:00:03:07 +e8 2 901 00:a0:85:00:02:01 00:a0:85:00:02:05 +``` ## mDNS-SD diff --git a/src/confd/yang/confd.inc b/src/confd/yang/confd.inc index 9aab145f..f6074cfa 100644 --- a/src/confd/yang/confd.inc +++ b/src/confd/yang/confd.inc @@ -25,7 +25,7 @@ MODULES=( "infix-if-type@2025-02-12.yang" "infix-routing@2024-11-27.yang" "ieee802-dot1ab-lldp@2022-03-15.yang" - "infix-lldp@2025-01-08.yang" + "infix-lldp@2025-05-05.yang" "infix-dhcp-common@2025-01-29.yang" "infix-dhcp-client@2025-01-29.yang" "infix-dhcp-server@2025-01-29.yang" diff --git a/src/confd/yang/confd/infix-lldp.yang b/src/confd/yang/confd/infix-lldp.yang index 375c3077..5a49f3d4 100644 --- a/src/confd/yang/confd/infix-lldp.yang +++ b/src/confd/yang/confd/infix-lldp.yang @@ -11,6 +11,13 @@ module infix-lldp { contact "kernelkit@googlegroups.com"; description "Infix augments and deviations to ieee-dot1ab-lldp."; + revision 2025-05-05 { + description + "Restrict 'dest-mac-address' to only allow the LLDP multicast MAC + address (01:80:C2:00:00:0E)."; + reference "internal"; + } + revision 2025-01-08 { description "Enable std. /lldp:lldp/message-tx-interval"; } @@ -32,6 +39,22 @@ module infix-lldp { } } + deviation "/lldp:lldp/lldp:port/lldp:dest-mac-address" { + deviate replace { + type string { + pattern "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}"; + } + } + } + + deviation "/lldp:lldp/lldp:port/lldp:dest-mac-address" { + deviate add { + must "translate(., 'abcdef', 'ABCDEF') = '01:80:C2:00:00:0E'" { + error-message "Only the LLDP Multicast MAC (01:80:C2:00:00:0E) is allowed."; + } + } + } + deviation "/lldp:lldp/lldp:message-fast-tx" { deviate not-supported; } diff --git a/src/confd/yang/confd/infix-lldp@2025-01-08.yang b/src/confd/yang/confd/infix-lldp@2025-05-05.yang similarity index 100% rename from src/confd/yang/confd/infix-lldp@2025-01-08.yang rename to src/confd/yang/confd/infix-lldp@2025-05-05.yang diff --git a/src/klish-plugin-infix/xml/infix.xml b/src/klish-plugin-infix/xml/infix.xml index f80f41ec..c10e1b27 100644 --- a/src/klish-plugin-infix/xml/infix.xml +++ b/src/klish-plugin-infix/xml/infix.xml @@ -373,6 +373,12 @@ + + + show lldp + + + show hardware |pager @@ -471,22 +477,22 @@ sysinfo --> - - cat /etc/version - - - - jq -C . /etc/factory-config.cfg |pager - - - - sysrepocfg -X -f json | jq -C . |pager - - - - jq -C . /cfg/startup-config.cfg |pager - + + cat /etc/version + + + jq -C . /etc/factory-config.cfg |pager + + + + sysrepocfg -X -f json | jq -C . |pager + + + + jq -C . /cfg/startup-config.cfg |pager + + /bin/yorn "This will restore the device to factory defaults" @@ -581,6 +587,7 @@ + diff --git a/src/show/show.py b/src/show/show.py index 6416a7ef..49d2a3e3 100755 --- a/src/show/show.py +++ b/src/show/show.py @@ -154,6 +154,15 @@ def routes(args: List[str]): return cli_pretty(data, "show-routing-table", "-i", ip_version) +def lldp(args: List[str]): + data = run_sysrepocfg("/ieee802-dot1ab-lldp:lldp") + if not data: + print("No lldp data retrieved.") + return + if RAW_OUTPUT: + print(json.dumps(data, indent=2)) + return + cli_pretty(data, "show-lldp") def execute_command(command: str, args: List[str]): command_mapping = { @@ -162,6 +171,7 @@ def execute_command(command: str, args: List[str]): 'interface': interface, 'ntp': ntp, 'routes': routes, + 'lldp': lldp, 'software' : software, 'stp': stp, } diff --git a/src/statd/python/cli_pretty/cli_pretty.py b/src/statd/python/cli_pretty/cli_pretty.py index 85f04961..19e64c54 100755 --- a/src/statd/python/cli_pretty/cli_pretty.py +++ b/src/statd/python/cli_pretty/cli_pretty.py @@ -20,6 +20,7 @@ class PadMdb: group = 20 ports = 45 + class PadStpPort: port = 12 id = 7 @@ -30,6 +31,7 @@ class PadStpPort: total = 12 + 7 + 12 + 12 + 6 + 31 + class PadRoute: dest = 30 pref = 8 @@ -57,6 +59,7 @@ class PadSoftware: state = 10 version = 23 + class PadDhcpServer: ip = 17 mac = 19 @@ -64,11 +67,13 @@ class PadDhcpServer: cid = 19 exp = 10 + class PadUsbPort: title = 30 name = 20 state = 10 + class PadNtpSource: address = 16 mode = 13 @@ -76,6 +81,7 @@ class PadNtpSource: stratum = 11 poll = 14 + class Decore(): @staticmethod def decorate(sgr, txt, restore="0"): @@ -109,11 +115,21 @@ class Decore(): def gray_bg(txt): return Decore.decorate("100", txt) + +class PadLldp: + interface = 16 + rem_idx = 10 + time = 12 + chassis_id = 20 + port_id = 20 + + def datetime_now(): if UNIT_TEST: return datetime(2023, 1, 1, 12, 0, 0, tzinfo=timezone.utc) return datetime.now(timezone.utc) + def get_json_data(default, indata, *args): data = indata for arg in args: @@ -124,12 +140,14 @@ def get_json_data(default, indata, *args): return data + def remove_yang_prefix(key): parts = key.split(":", 1) if len(parts) > 1: return parts[1] return key + class Date(datetime): def _pretty_delta(delta): assert(delta.total_seconds() > 0) @@ -179,7 +197,6 @@ class Date(datetime): return cls.strptime(f"{date}+{tz}", "%Y-%m-%dT%H:%M:%S%z") - class Route: def __init__(self, data, ip): self.data = data @@ -321,6 +338,7 @@ class Software: print(f"SHA-256 : {self.hash}") print(f"Installed : {self.date}") + class USBport: def __init__(self, data): self.data = data @@ -333,6 +351,7 @@ class USBport: row += f"{self.state:<{PadUsbPort.state}}" print(row) + class STPBridgeID: def __init__(self, id): self.id = id @@ -345,6 +364,7 @@ class STPBridgeID: ) return f"{prio:1x}.{sysid:03x}.{addr}" + class STPPortID: def __init__(self, id): self.id = id @@ -356,6 +376,7 @@ class STPPortID: ) return f"{prio:1x}.{pid:03x}" + class DhcpServer: def __init__(self, data): self.data = data @@ -533,6 +554,7 @@ class Iface: def is_gretap(self): return self.data['type'] == "infix-if-type:gretap" + def oper(self, detail=False): """Remap in brief overview to fit column widths.""" if not detail and self.oper_status == "lower-layer-down": @@ -719,7 +741,7 @@ class Iface: lower.pr_name(pipe) lower.pr_proto_lag() - def pr_veth(self, _ifaces): + def pr_veth(self): self.pr_name(pipe="") self.pr_proto_veth() self.pr_proto_ipv4() @@ -930,6 +952,25 @@ class Iface: print(f"{' port':<{20}}: {tc.get('port', 'UNKNOWN')}") +class LldpNeighbor: + def __init__(self, iface, data): + self.interface = iface + self.remote_index = data.get("remote-index", 0) + self.time_mark = data.get("time-mark", 0) + self.chassis_id = data.get("chassis-id", "unknown") + self.port_id = data.get("port-id", "unknown") + + def print(self): + row = ( + f"{self.interface:<{PadLldp.interface}}" + f"{self.remote_index:<{PadLldp.rem_idx}}" + f"{self.time_mark:<{PadLldp.time}}" + f"{self.chassis_id:<{PadLldp.chassis_id}}" + f"{self.port_id:<{PadLldp.port_id}}" + ) + print(row) + + def find_iface(_ifaces, name): for _iface in [Iface(data) for data in _ifaces]: if _iface.name == name: @@ -941,9 +982,11 @@ def find_iface(_ifaces, name): def version_sort(s): return [int(x) if x.isdigit() else x for x in re.split(r'(\d+)', s)] + def ifname_sort(iface): return version_sort(iface["name"]) + def brport_sort(iface): brname = iface.get("infix-interfaces:bridge-port", {}).get("bridge", "") return version_sort(brname) + version_sort(iface["name"]) @@ -987,7 +1030,7 @@ def pr_interface_list(json): continue if iface.is_veth(): - iface.pr_veth(ifaces) + iface.pr_veth() continue if iface.is_gre(): @@ -1057,7 +1100,6 @@ def show_bridge_mdb(json): iface.pr_vlans_mdb(iface.name) - def show_bridge_stp_port(ifname, brport): stp = brport["stp"] @@ -1091,6 +1133,7 @@ def show_bridge_stp_port(ifname, brport): ) print(row) + def show_bridge_stp(json): if not json.get("ietf-interfaces:interfaces"): print("Error, top level \"ietf-interfaces:interface\" missing") @@ -1198,6 +1241,7 @@ def show_software(json, name): if slot.is_rootfs(): slot.print() + def show_hardware(json): if not json.get("ietf-hardware:hardware"): print(f"Error, top level \"ietf-hardware:component\" missing") @@ -1216,6 +1260,7 @@ def show_hardware(json): port = USBport(component) port.print() + def show_ntp(json): if not json.get("ietf-system:system-state"): print("NTP client not enabled.") @@ -1236,6 +1281,7 @@ def show_ntp(json): row += f"{source['poll']:>{PadNtpSource.poll}}" print(row) + def show_dhcp_server(json, stats): data = json.get("infix-dhcp-server:dhcp-server") if not data: @@ -1255,6 +1301,37 @@ def show_dhcp_server(json, stats): print(Decore.invert(hdr)) server.print() + +def show_lldp(json): + if not json.get("ieee802-dot1ab-lldp:lldp"): + print("Error: No LLDP data available.") + sys.exit(1) + + lldp_ports = json["ieee802-dot1ab-lldp:lldp"].get("port", []) + + if not lldp_ports: + print("No LLDP neighbors found.") + return + + header = ( + f"{'INTERFACE':<{PadLldp.interface}}" + f"{'REM-IDX':<{PadLldp.rem_idx}}" + f"{'TIME':<{PadLldp.time}}" + f"{'CHASSIS-ID':<{PadLldp.chassis_id}}" + f"{'PORT-ID':<{PadLldp.port_id}}" + ) + + print(Decore.invert(header)) + + for port_data in lldp_ports: + port_name = port_data["name"] + neighbors = port_data.get("remote-systems-data", []) + + for neighbor in neighbors: + entry = LldpNeighbor(port_name, neighbor) + entry.print() + + def main(): global UNIT_TEST @@ -1272,47 +1349,51 @@ def main(): parser.add_argument('-t', '--test', action='store_true', help='Enable unit test mode') - parser_show_routing_table = subparsers.add_parser('show-routing-table', help='Show the routing table') - parser_show_routing_table.add_argument('-i', '--ip', required=True, help='IPv4 or IPv6 address') + subparsers.add_parser('show-boot-order', help='Show NTP sources') - parser_show_interfaces = subparsers.add_parser('show-interfaces', help='Show interfaces') - parser_show_interfaces.add_argument('-n', '--name', help='Interface name') + subparsers.add_parser('show-bridge-mdb', help='Show bridge MDB') - parser_show_bridge_mdb = subparsers.add_parser('show-bridge-mdb', help='Show bridge MDB') - parser_show_bridge_stp = subparsers.add_parser('show-bridge-stp', - help='Show spanning tree state') + subparsers.add_parser('show-bridge-stp', help='Show spanning tree state') - parser_show_software = subparsers.add_parser('show-software', help='Show software versions') - parser_show_software.add_argument('-n', '--name', help='Slotname') + subparsers.add_parser('show-dhcp-server', help='Show DHCP server') \ + .add_argument("-s", "--stats", action="store_true", help="Show server statistics") - parser_show_hardware = subparsers.add_parser('show-hardware', help='Show USB ports') + subparsers.add_parser('show-hardware', help='Show USB ports') - parser_show_ntp_sources = subparsers.add_parser('show-ntp', help='Show NTP sources') + subparsers.add_parser('show-interfaces', help='Show interfaces') \ + .add_argument('-n', '--name', help='Interface name') - parser_show_boot_order = subparsers.add_parser('show-boot-order', help='Show NTP sources') + subparsers.add_parser('show-lldp', help='Show LLDP neighbors') - parser_dhcp_srv = subparsers.add_parser('show-dhcp-server', help='Show DHCP server') - parser_dhcp_srv.add_argument("-s", "--stats", action="store_true", help="Show server statistics") + subparsers.add_parser('show-ntp', help='Show NTP sources') + + subparsers.add_parser('show-routing-table', help='Show the routing table') \ + .add_argument('-i', '--ip', required=True, help='IPv4 or IPv6 address') + + subparsers.add_parser('show-software', help='Show software versions') \ + .add_argument('-n', '--name', help='Slotname') args = parser.parse_args() UNIT_TEST = args.test - if args.command == "show-interfaces": + if args.command == "show-bridge-mdb": + show_bridge_mdb(json_data) + elif args.command == "show-bridge-stp": + show_bridge_stp(json_data) + elif args.command == "show-dhcp-server": + show_dhcp_server(json_data, args.stats) + elif args.command == "show-hardware": + show_hardware(json_data) + elif args.command == "show-interfaces": show_interfaces(json_data, args.name) + elif args.command == "show-lldp": + show_lldp(json_data) + elif args.command == "show-ntp": + show_ntp(json_data) elif args.command == "show-routing-table": show_routing_table(json_data, args.ip) elif args.command == "show-software": show_software(json_data, args.name) - elif args.command == "show-bridge-mdb": - show_bridge_mdb(json_data) - elif args.command == "show-bridge-stp": - show_bridge_stp(json_data) - elif args.command == "show-hardware": - show_hardware(json_data) - elif args.command == "show-ntp": - show_ntp(json_data) - elif args.command == "show-dhcp-server": - show_dhcp_server(json_data, args.stats) else: print(f"Error, unknown command '{args.command}'") sys.exit(1) diff --git a/src/statd/python/yanger/infix_lldp.py b/src/statd/python/yanger/infix_lldp.py index fcd5f5f5..2312c737 100644 --- a/src/statd/python/yanger/infix_lldp.py +++ b/src/statd/python/yanger/infix_lldp.py @@ -4,20 +4,35 @@ from collections import defaultdict def operational(): """Retrieve LLDP neighbor information and store in remote-systems-data under the correct port.""" - # Reference: https://www.ieee802.org/1/files/public/YANGs/ieee802-types.yang - subtype_mapping = { - "component": "chassis-component", + # https://www.ieee802.org/1/files/public/YANGs/ieee802-types.yang + # and subtypes are not supported in + # https://github.com/lldpd/lldpd until v1.0.19 + chassis_id_subtype_mapping = { + #"unhandled": "chassis-component", "ifalias": "interface-alias", - "port": "port-component", + #"unhandled": "port-component", "mac": "mac-address", "ip": "network-address", "ifname": "interface-name", "local": "local" } - DEFAULT_MAC = "00-00-00-00-00-00" + # https://www.ieee802.org/1/files/public/YANGs/ieee802-types.yang + # and subtypes are not supported in + # https://github.com/lldpd/lldpd until v1.0.19 + port_id_subtype_mapping = { + "ifalias": "interface-alias", + #"unhandled": "port-component", + "mac": "mac-address", + "ip": "network-address", + "ifname": "interface-name", + #"unhandled": "agent-circuit-id" + "local": "local" + } - port_data = defaultdict(lambda: {"remote-systems-data": [], "dest-mac-address": None}) + LLDP_MULTICAST_MAC = "01:80:C2:00:00:0E" + + port_data = defaultdict(lambda: {"remote-systems-data": [], "dest-mac-address": LLDP_MULTICAST_MAC}) data = HOST.run_json(["lldpcli", "show", "neighbors", "-f", "json"]) @@ -32,18 +47,12 @@ def operational(): time_mark = parse_time(iface_data.get("age")) chassis = iface_data.get("chassis", {}) - chassis_id_type, chassis_id_value = extract_chassis_id(chassis, subtype_mapping) + chassis_id_type, chassis_id_value = extract_chassis_id(chassis, chassis_id_subtype_mapping) port_info = iface_data.get("port", {}) - port_id_type = subtype_mapping.get(port_info.get("id", {}).get("type"), "unknown") + port_id_type = port_id_subtype_mapping.get(port_info.get("id", {}).get("type"), "unknown") port_id_value = port_info.get("id", {}).get("value", "") - dest_mac_address = ( - chassis_id_value.replace(":", "-") if chassis_id_type == "mac-address" else - port_id_value.replace(":", "-") if port_id_type == "mac-address" else - DEFAULT_MAC - ) - remote_entry = { "time-mark": time_mark, "remote-index": remote_index, @@ -55,9 +64,6 @@ def operational(): port_data[iface_name]["remote-systems-data"].append(remote_entry) - if port_data[iface_name]["dest-mac-address"] is None: - port_data[iface_name]["dest-mac-address"] = dest_mac_address - formatted_output = { "ieee802-dot1ab-lldp:lldp": { "port": [ diff --git a/test/case/ietf_interfaces/veth_delete/topology.dot b/test/case/ietf_interfaces/veth_delete/topology.dot deleted file mode 100644 index 10fb0dee..00000000 --- a/test/case/ietf_interfaces/veth_delete/topology.dot +++ /dev/null @@ -1,25 +0,0 @@ -graph "1x3" { - layout="neato"; - overlap="false"; - esep="+80"; - - node [shape=record, fontname="DejaVu Sans Mono, Book"]; - edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; - - host [ - label="host | { mgmt | data1 | data2 }", - pos="0,12!", - requires="controller", - ]; - - target [ - label="{ mgmt | data1 | data2 } | target", - pos="10,12!", - - requires="infix", - ]; - - host:mgmt -- target:mgmt [requires="mgmt", color=lightgrey] - host:data1 -- target:data1 [color=black] - host:data2 -- target:data2 [color=black] -} diff --git a/test/case/ietf_interfaces/veth_delete/topology.dot b/test/case/ietf_interfaces/veth_delete/topology.dot new file mode 120000 index 00000000..7689a677 --- /dev/null +++ b/test/case/ietf_interfaces/veth_delete/topology.dot @@ -0,0 +1 @@ +../../../infamy/topologies/1x3.dot \ No newline at end of file diff --git a/test/case/ietf_interfaces/veth_delete/topology.svg b/test/case/ietf_interfaces/veth_delete/topology.svg index e7d561fa..9517e639 100644 --- a/test/case/ietf_interfaces/veth_delete/topology.svg +++ b/test/case/ietf_interfaces/veth_delete/topology.svg @@ -35,7 +35,7 @@ host:mgmt--target:mgmt - + diff --git a/test/case/ietf_interfaces/vlan_iface_termination/test.py b/test/case/ietf_interfaces/vlan_iface_termination/test.py index e2c32c45..c26ae1ec 100755 --- a/test/case/ietf_interfaces/vlan_iface_termination/test.py +++ b/test/case/ietf_interfaces/vlan_iface_termination/test.py @@ -8,14 +8,14 @@ attached to a VLAN filtering bridge are always locally terminated. .---------------------------. | target | | | -| data0.10 br0 data1.10 | +| data1.10 br0 data2.10 | | \ / \ / | -'------data0-----data1------' +'------data1-----data2------' | | | | -.------data0-----data1------. +.------data1-----data2------. | / : \ | -| data0.10 : data1.10 | +| data1.10 : data2.10 | | : | | host | | : | @@ -23,7 +23,7 @@ attached to a VLAN filtering bridge are always locally terminated. .... In this setup, even though VLAN 10 is allowed to ingress and egress on -both `data0` and `data1`, _bridging_ of packets from one to the other +both `data1` and `data2`, _bridging_ of packets from one to the other must _not_ be allowed. """ @@ -35,10 +35,10 @@ with infamy.Test() as test: tgt = env.attach("target", "mgmt") with test.step("Configure bridge and VLAN interfaces on target"): - _, hdata0 = env.ltop.xlate( "host", "data0") _, hdata1 = env.ltop.xlate( "host", "data1") - _, ddata0 = env.ltop.xlate("target", "data0") + _, hdata2 = env.ltop.xlate( "host", "data2") _, ddata1 = env.ltop.xlate("target", "data1") + _, ddata2 = env.ltop.xlate("target", "data2") tgt.put_config_dicts({ "ietf-interfaces": { @@ -53,35 +53,12 @@ with infamy.Test() as test: "vlan": [ { "vid": 1, - "untagged": [ddata0, ddata1] + "untagged": [ddata1, ddata2] }, ] } } }, - { - "name": ddata0, - "infix-interfaces:bridge-port": { - "pvid": 1, - "bridge": "br0" - } - }, - { - "name": f"{ddata0}.10", - "type": "infix-if-type:vlan", - "vlan": { - "id": 10, - "lower-layer-if": ddata0, - }, - "ipv4": { - "address": [ - { - "ip": "10.10.1.2", - "prefix-length": 24, - } - ] - } - }, { "name": ddata1, "infix-interfaces:bridge-port": { @@ -96,6 +73,29 @@ with infamy.Test() as test: "id": 10, "lower-layer-if": ddata1, }, + "ipv4": { + "address": [ + { + "ip": "10.10.1.2", + "prefix-length": 24, + } + ] + } + }, + { + "name": ddata2, + "infix-interfaces:bridge-port": { + "pvid": 1, + "bridge": "br0" + } + }, + { + "name": f"{ddata2}.10", + "type": "infix-if-type:vlan", + "vlan": { + "id": 10, + "lower-layer-if": ddata2, + }, "ipv4": { "address": [ { @@ -110,8 +110,8 @@ with infamy.Test() as test: } }) - with infamy.IsolatedMacVlan(hdata0) as ns0, \ - infamy.IsolatedMacVlan(hdata1) as ns1: + with infamy.IsolatedMacVlan(hdata1) as ns0, \ + infamy.IsolatedMacVlan(hdata2) as ns1: with test.step("Configure IP addresses and VLAN interfaces on host"): ns0.addip("10.0.1.1") @@ -128,17 +128,17 @@ with infamy.Test() as test: ip addr add 10.10.2.1/24 dev vlan10 """) - with test.step("Verify that host:data0 reaches host:data1 with untagged packets"): + with test.step("Verify that host:data1 reaches host:data2 with untagged packets"): ns0.must_reach("10.0.1.2") - with test.step("Verify that traffic on VLAN 10 from host:data0 does not reach host:data1"): + with test.step("Verify that traffic on VLAN 10 from host:data1 does not reach host:data2"): infamy.parallel(lambda: ns0.runsh("timeout -s INT 5 ping -i 0.2 -b 10.10.1.255 || true"), lambda: ns1.must_not_receive("ip src 10.10.1.1")) - with test.step("Verify that host:data0 can reach target on VLAN 10"): + with test.step("Verify that host:data1 can reach target on VLAN 10"): ns0.must_reach("10.10.1.2") - with test.step("Verify that host:data1 can reach target on VLAN 10"): + with test.step("Verify that host:data2 can reach target on VLAN 10"): ns1.must_reach("10.10.2.2") test.succeed() diff --git a/test/case/ietf_interfaces/vlan_iface_termination/topology.svg b/test/case/ietf_interfaces/vlan_iface_termination/topology.svg index d2fdf4fa..9517e639 100644 --- a/test/case/ietf_interfaces/vlan_iface_termination/topology.svg +++ b/test/case/ietf_interfaces/vlan_iface_termination/topology.svg @@ -14,11 +14,11 @@ host -tgt +mgmt -data0 +data1 -data1 +data2 @@ -26,25 +26,25 @@ mgmt -data0 +data1 -data1 +data2 target -host:tgt--target:mgmt - +host:mgmt--target:mgmt + -host:data0--target:data0 +host:data1--target:data1 -host:data1--target:data1 +host:data2--target:data2 diff --git a/test/case/ietf_interfaces/vlan_iface_termination/vlan_iface_termination.adoc b/test/case/ietf_interfaces/vlan_iface_termination/vlan_iface_termination.adoc index 8fd73631..cc955c6b 100644 --- a/test/case/ietf_interfaces/vlan_iface_termination/vlan_iface_termination.adoc +++ b/test/case/ietf_interfaces/vlan_iface_termination/vlan_iface_termination.adoc @@ -7,14 +7,14 @@ attached to a VLAN filtering bridge are always locally terminated. .---------------------------. | target | | | -| data0.10 br0 data1.10 | +| data1.10 br0 data2.10 | | \ / \ / | -'------data0-----data1------' +'------data1-----data2------' | | | | -.------data0-----data1------. +.------data1-----data2------. | / : \ | -| data0.10 : data1.10 | +| data1.10 : data2.10 | | : | | host | | : | @@ -22,7 +22,7 @@ attached to a VLAN filtering bridge are always locally terminated. .... In this setup, even though VLAN 10 is allowed to ingress and egress on -both `data0` and `data1`, _bridging_ of packets from one to the other +both `data1` and `data2`, _bridging_ of packets from one to the other must _not_ be allowed. ==== Topology @@ -41,10 +41,10 @@ endif::topdoc[] . Set up topology and attach to target . Configure bridge and VLAN interfaces on target . Configure IP addresses and VLAN interfaces on host -. Verify that host:data0 reaches host:data1 with untagged packets -. Verify that traffic on VLAN 10 from host:data0 does not reach host:data1 -. Verify that host:data0 can reach target on VLAN 10 +. Verify that host:data1 reaches host:data2 with untagged packets +. Verify that traffic on VLAN 10 from host:data1 does not reach host:data2 . Verify that host:data1 can reach target on VLAN 10 +. Verify that host:data2 can reach target on VLAN 10 <<< diff --git a/test/case/infix_services/Readme.adoc b/test/case/infix_services/Readme.adoc index a0bdab82..67c61268 100644 --- a/test/case/infix_services/Readme.adoc +++ b/test/case/infix_services/Readme.adoc @@ -3,14 +3,16 @@ <<< -include::mdns_enable_disable/Readme.adoc[] +include::mdns/mdns_enable_disable/Readme.adoc[] -include::mdns_allow_deny/Readme.adoc[] +include::mdns/mdns_allow_deny/Readme.adoc[] -include::lldp_enable_disable/Readme.adoc[] +include::lldp/lldp_enable_disable/Readme.adoc[] -include::lldp_admin_status/Readme.adoc[] +include::lldp/lldp_admin_status/Readme.adoc[] -include::ssh_server_config/Readme.adoc[] +include::lldp/lldp_ieee_group_forward/Readme.adoc[] -include::ssh_key_authentication/Readme.adoc[] +include::ssh/ssh_server_config/Readme.adoc[] + +include::ssh/ssh_key_authentication/Readme.adoc[] diff --git a/test/case/infix_services/infix_services.yaml b/test/case/infix_services/infix_services.yaml index eb5568f6..43ab99f5 100644 --- a/test/case/infix_services/infix_services.yaml +++ b/test/case/infix_services/infix_services.yaml @@ -1,9 +1,9 @@ --- - name: lldp - suite: lldp.yaml + suite: lldp/lldp.yaml - name: mdns - suite: mdns.yaml + suite: mdns/mdns.yaml - name: ssh - suite: ssh.yaml + suite: ssh/ssh.yaml diff --git a/test/case/infix_services/lldp.yaml b/test/case/infix_services/lldp/lldp.yaml similarity index 63% rename from test/case/infix_services/lldp.yaml rename to test/case/infix_services/lldp/lldp.yaml index 4f0c30e6..af3ce73b 100644 --- a/test/case/infix_services/lldp.yaml +++ b/test/case/infix_services/lldp/lldp.yaml @@ -4,3 +4,6 @@ - name: lldp_admin_status case: lldp_admin_status/test.py + +- name: lldp_ieee_group_forward + case: lldp_ieee_group_forward/test.py diff --git a/test/case/infix_services/lldp_admin_status/Readme.adoc b/test/case/infix_services/lldp/lldp_admin_status/Readme.adoc similarity index 100% rename from test/case/infix_services/lldp_admin_status/Readme.adoc rename to test/case/infix_services/lldp/lldp_admin_status/Readme.adoc diff --git a/test/case/infix_services/lldp_admin_status/lldp_admin_status.adoc b/test/case/infix_services/lldp/lldp_admin_status/lldp_admin_status.adoc similarity index 75% rename from test/case/infix_services/lldp_admin_status/lldp_admin_status.adoc rename to test/case/infix_services/lldp/lldp_admin_status/lldp_admin_status.adoc index 9a865876..24d351fe 100644 --- a/test/case/infix_services/lldp_admin_status/lldp_admin_status.adoc +++ b/test/case/infix_services/lldp/lldp_admin_status/lldp_admin_status.adoc @@ -4,11 +4,11 @@ Verify that LLDP admin status is set properly by lldpd ==== Topology ifdef::topdoc[] -image::{topdoc}../../test/case/infix_services/lldp_admin_status/topology.svg[LLDP admin status topology] +image::{topdoc}../../test/case/infix_services/lldp/lldp_admin_status/topology.svg[LLDP admin status topology] endif::topdoc[] ifndef::topdoc[] ifdef::testgroup[] -image::lldp_admin_status/topology.svg[LLDP admin status topology] +image::lldp/lldp_admin_status/topology.svg[LLDP admin status topology] endif::testgroup[] ifndef::testgroup[] image::topology.svg[LLDP admin status topology] diff --git a/test/case/infix_services/lldp_admin_status/test.py b/test/case/infix_services/lldp/lldp_admin_status/test.py similarity index 95% rename from test/case/infix_services/lldp_admin_status/test.py rename to test/case/infix_services/lldp/lldp_admin_status/test.py index 2bb7e275..5f9befbb 100755 --- a/test/case/infix_services/lldp_admin_status/test.py +++ b/test/case/infix_services/lldp/lldp_admin_status/test.py @@ -22,7 +22,7 @@ def capture_traffic(iface, sec): return sniffer.output() def send_lldp_packet(iface, chassis_id, chassis_id_subtype, ttl=3): - eth = Ether(dst="01:80:c2:00:00:0e", type=0x88cc) + eth = Ether(src="02:01:02:03:04:05", dst="01:80:C2:00:00:0E", type=0x88cc) lldpdu = eth / LLDPDU() lldpdu /= LLDPDUChassisID(subtype=chassis_id_subtype, id=chassis_id) lldpdu /= LLDPDUPortID(subtype=5, id=iface) @@ -45,7 +45,7 @@ def verify_admin_status(test, target, port, admin_status, local_capture, remote_ "lldp": { "port": [{ "name": target["data"], - "dest-mac-address": "00-00-00-00-00-00", + "dest-mac-address": "01:80:C2:00:00:0E", "admin-status": admin_status }] } diff --git a/test/case/infix_services/lldp_admin_status/topology.dot b/test/case/infix_services/lldp/lldp_admin_status/topology.dot similarity index 100% rename from test/case/infix_services/lldp_admin_status/topology.dot rename to test/case/infix_services/lldp/lldp_admin_status/topology.dot diff --git a/test/case/infix_services/lldp_admin_status/topology.svg b/test/case/infix_services/lldp/lldp_admin_status/topology.svg similarity index 100% rename from test/case/infix_services/lldp_admin_status/topology.svg rename to test/case/infix_services/lldp/lldp_admin_status/topology.svg diff --git a/test/case/infix_services/lldp_enable_disable/Readme.adoc b/test/case/infix_services/lldp/lldp_enable_disable/Readme.adoc similarity index 100% rename from test/case/infix_services/lldp_enable_disable/Readme.adoc rename to test/case/infix_services/lldp/lldp_enable_disable/Readme.adoc diff --git a/test/case/infix_services/lldp_enable_disable/lldp_enable_disable.adoc b/test/case/infix_services/lldp/lldp_enable_disable/lldp_enable_disable.adoc similarity index 75% rename from test/case/infix_services/lldp_enable_disable/lldp_enable_disable.adoc rename to test/case/infix_services/lldp/lldp_enable_disable/lldp_enable_disable.adoc index 7d333f04..1884dedc 100644 --- a/test/case/infix_services/lldp_enable_disable/lldp_enable_disable.adoc +++ b/test/case/infix_services/lldp/lldp_enable_disable/lldp_enable_disable.adoc @@ -5,11 +5,11 @@ Operation and non-operation are confirmed using tcpdump. ==== Topology ifdef::topdoc[] -image::{topdoc}../../test/case/infix_services/lldp_enable_disable/topology.svg[LLDP enable disable topology] +image::{topdoc}../../test/case/infix_services/lldp/lldp_enable_disable/topology.svg[LLDP enable disable topology] endif::topdoc[] ifndef::topdoc[] ifdef::testgroup[] -image::lldp_enable_disable/topology.svg[LLDP enable disable topology] +image::lldp/lldp_enable_disable/topology.svg[LLDP enable disable topology] endif::testgroup[] ifndef::testgroup[] image::topology.svg[LLDP enable disable topology] diff --git a/test/case/infix_services/lldp_enable_disable/test.py b/test/case/infix_services/lldp/lldp_enable_disable/test.py similarity index 100% rename from test/case/infix_services/lldp_enable_disable/test.py rename to test/case/infix_services/lldp/lldp_enable_disable/test.py diff --git a/test/case/infix_services/lldp_enable_disable/topology.dot b/test/case/infix_services/lldp/lldp_enable_disable/topology.dot similarity index 100% rename from test/case/infix_services/lldp_enable_disable/topology.dot rename to test/case/infix_services/lldp/lldp_enable_disable/topology.dot diff --git a/test/case/infix_services/lldp_enable_disable/topology.svg b/test/case/infix_services/lldp/lldp_enable_disable/topology.svg similarity index 100% rename from test/case/infix_services/lldp_enable_disable/topology.svg rename to test/case/infix_services/lldp/lldp_enable_disable/topology.svg diff --git a/test/case/infix_services/lldp/lldp_ieee_group_forward/Readme.adoc b/test/case/infix_services/lldp/lldp_ieee_group_forward/Readme.adoc new file mode 120000 index 00000000..f718858e --- /dev/null +++ b/test/case/infix_services/lldp/lldp_ieee_group_forward/Readme.adoc @@ -0,0 +1 @@ +lldp_ieee_group_forward.adoc \ No newline at end of file diff --git a/test/case/infix_services/lldp/lldp_ieee_group_forward/lldp_ieee_group_forward.adoc b/test/case/infix_services/lldp/lldp_ieee_group_forward/lldp_ieee_group_forward.adoc new file mode 100644 index 00000000..7d9df613 --- /dev/null +++ b/test/case/infix_services/lldp/lldp_ieee_group_forward/lldp_ieee_group_forward.adoc @@ -0,0 +1,27 @@ +=== LLDP IEEE Group Forward +==== Description +Verify that LLDP packets can be flooded to all ports on a bridge. +Operation and non-operation are confirmed using tcpdump. + +==== Topology +ifdef::topdoc[] +image::{topdoc}../../test/case/infix_services/lldp/lldp_ieee_group_forward/topology.svg[LLDP IEEE Group Forward topology] +endif::topdoc[] +ifndef::topdoc[] +ifdef::testgroup[] +image::lldp/lldp_ieee_group_forward/topology.svg[LLDP IEEE Group Forward topology] +endif::testgroup[] +ifndef::testgroup[] +image::topology.svg[LLDP IEEE Group Forward topology] +endif::testgroup[] +endif::topdoc[] +==== Test sequence +. Set up topology and attach to target DUT +. Configure interfaces and disable LLDP daemon +. Verify LLDP absence on host:data2 +. Enable LLDP flooding by setting group forward +. Verify LLDP arrival on host:data2 + + +<<< + diff --git a/test/case/infix_services/lldp/lldp_ieee_group_forward/test.py b/test/case/infix_services/lldp/lldp_ieee_group_forward/test.py new file mode 100755 index 00000000..3e20017b --- /dev/null +++ b/test/case/infix_services/lldp/lldp_ieee_group_forward/test.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 + +"""LLDP IEEE Group Forward + +Verify that LLDP packets can be flooded to all ports on a bridge. +Operation and non-operation are confirmed using tcpdump. +""" + +import time +import threading +import infamy + +from scapy.all import Ether, sendp +from scapy.contrib.lldp import ( + LLDPDU, LLDPDUChassisID, LLDPDUPortID, + LLDPDUTimeToLive, LLDPDUEndOfLLDPDU +) + +SNIFFING_DURATION = 3 + +LLDP_INTERVAL = 1 +LLDP_TTL = 3 +LLDP_MULTICAST_MAC = "01:80:C2:00:00:0E" +LLDP_ETHERTYPE = 0x88cc + +CHASSIS_ID = "Chassis-Test" +SRC_MAC = "02:01:02:03:04:05" + +BRIDGE = "br0" + +def send_lldp_packet(iface): + eth = Ether(src=SRC_MAC, dst=LLDP_MULTICAST_MAC, type=LLDP_ETHERTYPE) + lldpdu = eth / LLDPDU() + lldpdu /= LLDPDUChassisID(subtype=7, id=CHASSIS_ID) + lldpdu /= LLDPDUPortID(subtype=5, id=iface) + lldpdu /= LLDPDUTimeToLive(ttl=LLDP_TTL) + lldpdu /= LLDPDUEndOfLLDPDU() + sendp(lldpdu, iface=iface, verbose=False) + +def send_lldp_packets_continuously(iface, stop_event): + time.sleep(LLDP_INTERVAL/2) + while not stop_event.is_set(): + send_lldp_packet(iface) + time.sleep(LLDP_INTERVAL) + +def capture_traffic(iface): + with infamy.IsolatedMacVlan(iface) as netns: + sniffer = infamy.Sniffer(netns, f"ether proto {LLDP_ETHERTYPE}") + with sniffer: + time.sleep(SNIFFING_DURATION) + return sniffer.output() + +def start_lldp_sender_thread(iface): + stop = threading.Event() + thread = threading.Thread( + target=send_lldp_packets_continuously, + args=(iface, stop) + ) + thread.start() + return stop, thread + +with infamy.Test() as test: + with test.step("Set up topology and attach to target DUT"): + env = infamy.Env() + target = env.attach("target", "mgmt") + _, hdata1 = env.ltop.xlate("host", "data1") + _, hdata2 = env.ltop.xlate("host", "data2") + + with test.step("Configure interfaces and disable LLDP daemon"): + target.put_config_dicts({ + "ietf-interfaces": { + "interfaces": { + "interface": [ + { + "name": BRIDGE, + "type": "infix-if-type:bridge", + "enabled": True, + }, + { + "name": target["data1"], + "enabled": True, + "infix-interfaces:bridge-port": { + "bridge": "br0" + } + }, + { + "name": target["data2"], + "enabled": True, + "infix-interfaces:bridge-port": { + "bridge": "br0" + } + } + ] + } + }, + "ieee802-dot1ab-lldp": { + "lldp": { + "enabled": False + } + } + }) + + stop_event, sender_thread = start_lldp_sender_thread(hdata1) + + try: + with test.step("Verify LLDP absence on host:data2"): + captured_traffic = capture_traffic(hdata2) + if "LLDP" in captured_traffic.stdout: + test.fail() + + finally: + stop_event.set() + sender_thread.join() + + with test.step("Enable LLDP flooding by setting group forward"): + target.put_config_dicts({ + "ietf-interfaces": { + "interfaces": { + "interface": [ + { + "name": BRIDGE, + "infix-interfaces:bridge": { + "ieee-group-forward": [ + "lldp" + ] + } + } + ] + } + } + }) + + stop_event, sender_thread = start_lldp_sender_thread(hdata1) + + try: + with test.step("Verify LLDP arrival on host:data2"): + captured_traffic = capture_traffic(hdata2) + if "LLDP" not in captured_traffic.stdout: + test.fail() + + finally: + stop_event.set() + sender_thread.join() + + test.succeed() diff --git a/test/case/infix_services/lldp/lldp_ieee_group_forward/topology.dot b/test/case/infix_services/lldp/lldp_ieee_group_forward/topology.dot new file mode 100644 index 00000000..3db4cf17 --- /dev/null +++ b/test/case/infix_services/lldp/lldp_ieee_group_forward/topology.dot @@ -0,0 +1,24 @@ +graph "1x3" { + layout="neato"; + overlap="false"; + esep="+80"; + + node [shape=record, fontname="DejaVu Sans Mono, Book"]; + edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; + + host [ + label="host | { mgmt | data1 | data2 }", + pos="0,12!", + requires="controller", + ]; + + target [ + label="{ mgmt | data1 | data2 } | target", + pos="10,12!", + requires="infix", + ]; + + host:mgmt -- target:mgmt [requires="mgmt", color="lightgray"] + host:data1 -- target:data1 [color="black", requires="ieee-mc"] + host:data2 -- target:data2 [color="black", requires="ieee-mc"] +} diff --git a/test/case/infix_services/ssh_server_config/topology.svg b/test/case/infix_services/lldp/lldp_ieee_group_forward/topology.svg similarity index 100% rename from test/case/infix_services/ssh_server_config/topology.svg rename to test/case/infix_services/lldp/lldp_ieee_group_forward/topology.svg diff --git a/test/case/infix_services/mdns.yaml b/test/case/infix_services/mdns/mdns.yaml similarity index 100% rename from test/case/infix_services/mdns.yaml rename to test/case/infix_services/mdns/mdns.yaml diff --git a/test/case/infix_services/mdns_allow_deny/Readme.adoc b/test/case/infix_services/mdns/mdns_allow_deny/Readme.adoc similarity index 100% rename from test/case/infix_services/mdns_allow_deny/Readme.adoc rename to test/case/infix_services/mdns/mdns_allow_deny/Readme.adoc diff --git a/test/case/infix_services/mdns_allow_deny/mdns_allow_deny.adoc b/test/case/infix_services/mdns/mdns_allow_deny/mdns_allow_deny.adoc similarity index 80% rename from test/case/infix_services/mdns_allow_deny/mdns_allow_deny.adoc rename to test/case/infix_services/mdns/mdns_allow_deny/mdns_allow_deny.adoc index ee1cadb9..6fcb2732 100644 --- a/test/case/infix_services/mdns_allow_deny/mdns_allow_deny.adoc +++ b/test/case/infix_services/mdns/mdns_allow_deny/mdns_allow_deny.adoc @@ -10,11 +10,11 @@ with three scenarios: ==== Topology ifdef::topdoc[] -image::{topdoc}../../test/case/infix_services/mdns_allow_deny/topology.svg[mDNS allow/deny interfaces topology] +image::{topdoc}../../test/case/infix_services/mdns/mdns_allow_deny/topology.svg[mDNS allow/deny interfaces topology] endif::topdoc[] ifndef::topdoc[] ifdef::testgroup[] -image::mdns_allow_deny/topology.svg[mDNS allow/deny interfaces topology] +image::mdns/mdns_allow_deny/topology.svg[mDNS allow/deny interfaces topology] endif::testgroup[] ifndef::testgroup[] image::topology.svg[mDNS allow/deny interfaces topology] diff --git a/test/case/infix_services/mdns_allow_deny/test.py b/test/case/infix_services/mdns/mdns_allow_deny/test.py similarity index 100% rename from test/case/infix_services/mdns_allow_deny/test.py rename to test/case/infix_services/mdns/mdns_allow_deny/test.py diff --git a/test/case/infix_services/mdns_allow_deny/topology.dot b/test/case/infix_services/mdns/mdns_allow_deny/topology.dot similarity index 100% rename from test/case/infix_services/mdns_allow_deny/topology.dot rename to test/case/infix_services/mdns/mdns_allow_deny/topology.dot diff --git a/test/case/infix_services/mdns_allow_deny/topology.svg b/test/case/infix_services/mdns/mdns_allow_deny/topology.svg similarity index 100% rename from test/case/infix_services/mdns_allow_deny/topology.svg rename to test/case/infix_services/mdns/mdns_allow_deny/topology.svg diff --git a/test/case/infix_services/mdns_enable_disable/Readme.adoc b/test/case/infix_services/mdns/mdns_enable_disable/Readme.adoc similarity index 100% rename from test/case/infix_services/mdns_enable_disable/Readme.adoc rename to test/case/infix_services/mdns/mdns_enable_disable/Readme.adoc diff --git a/test/case/infix_services/mdns_enable_disable/mdns_enable_disable.adoc b/test/case/infix_services/mdns/mdns_enable_disable/mdns_enable_disable.adoc similarity index 77% rename from test/case/infix_services/mdns_enable_disable/mdns_enable_disable.adoc rename to test/case/infix_services/mdns/mdns_enable_disable/mdns_enable_disable.adoc index 5816200f..9a7b7ae0 100644 --- a/test/case/infix_services/mdns_enable_disable/mdns_enable_disable.adoc +++ b/test/case/infix_services/mdns/mdns_enable_disable/mdns_enable_disable.adoc @@ -5,11 +5,11 @@ Operation and non-operation are confirmed using tcpdump. ==== Topology ifdef::topdoc[] -image::{topdoc}../../test/case/infix_services/mdns_enable_disable/topology.svg[mDNS enable disable topology] +image::{topdoc}../../test/case/infix_services/mdns/mdns_enable_disable/topology.svg[mDNS enable disable topology] endif::topdoc[] ifndef::topdoc[] ifdef::testgroup[] -image::mdns_enable_disable/topology.svg[mDNS enable disable topology] +image::mdns/mdns_enable_disable/topology.svg[mDNS enable disable topology] endif::testgroup[] ifndef::testgroup[] image::topology.svg[mDNS enable disable topology] diff --git a/test/case/infix_services/mdns_enable_disable/test.py b/test/case/infix_services/mdns/mdns_enable_disable/test.py similarity index 100% rename from test/case/infix_services/mdns_enable_disable/test.py rename to test/case/infix_services/mdns/mdns_enable_disable/test.py diff --git a/test/case/infix_services/mdns_enable_disable/topology.dot b/test/case/infix_services/mdns/mdns_enable_disable/topology.dot similarity index 100% rename from test/case/infix_services/mdns_enable_disable/topology.dot rename to test/case/infix_services/mdns/mdns_enable_disable/topology.dot diff --git a/test/case/infix_services/mdns_enable_disable/topology.svg b/test/case/infix_services/mdns/mdns_enable_disable/topology.svg similarity index 100% rename from test/case/infix_services/mdns_enable_disable/topology.svg rename to test/case/infix_services/mdns/mdns_enable_disable/topology.svg diff --git a/test/case/infix_services/ssh.yaml b/test/case/infix_services/ssh/ssh.yaml similarity index 100% rename from test/case/infix_services/ssh.yaml rename to test/case/infix_services/ssh/ssh.yaml diff --git a/test/case/infix_services/ssh_key_authentication/Readme.adoc b/test/case/infix_services/ssh/ssh_key_authentication/Readme.adoc similarity index 100% rename from test/case/infix_services/ssh_key_authentication/Readme.adoc rename to test/case/infix_services/ssh/ssh_key_authentication/Readme.adoc diff --git a/test/case/infix_services/ssh_key_authentication/ssh_key_authentication.adoc b/test/case/infix_services/ssh/ssh_key_authentication/ssh_key_authentication.adoc similarity index 74% rename from test/case/infix_services/ssh_key_authentication/ssh_key_authentication.adoc rename to test/case/infix_services/ssh/ssh_key_authentication/ssh_key_authentication.adoc index b5c664b1..188121fb 100644 --- a/test/case/infix_services/ssh_key_authentication/ssh_key_authentication.adoc +++ b/test/case/infix_services/ssh/ssh_key_authentication/ssh_key_authentication.adoc @@ -4,11 +4,11 @@ Verify that 'guest' user can fetch data using only the 'public' key ==== Topology ifdef::topdoc[] -image::{topdoc}../../test/case/infix_services/ssh_key_authentication/topology.svg[Generate ssh key pair topology] +image::{topdoc}../../test/case/infix_services/ssh/ssh_key_authentication/topology.svg[Generate ssh key pair topology] endif::topdoc[] ifndef::topdoc[] ifdef::testgroup[] -image::ssh_key_authentication/topology.svg[Generate ssh key pair topology] +image::ssh/ssh_key_authentication/topology.svg[Generate ssh key pair topology] endif::testgroup[] ifndef::testgroup[] image::topology.svg[Generate ssh key pair topology] diff --git a/test/case/infix_services/ssh_key_authentication/test.py b/test/case/infix_services/ssh/ssh_key_authentication/test.py similarity index 100% rename from test/case/infix_services/ssh_key_authentication/test.py rename to test/case/infix_services/ssh/ssh_key_authentication/test.py diff --git a/test/case/infix_services/ssh/ssh_key_authentication/topology.dot b/test/case/infix_services/ssh/ssh_key_authentication/topology.dot new file mode 120000 index 00000000..54a4185e --- /dev/null +++ b/test/case/infix_services/ssh/ssh_key_authentication/topology.dot @@ -0,0 +1 @@ +../../../../infamy/topologies/1x1.dot \ No newline at end of file diff --git a/test/case/infix_services/ssh_key_authentication/topology.svg b/test/case/infix_services/ssh/ssh_key_authentication/topology.svg similarity index 100% rename from test/case/infix_services/ssh_key_authentication/topology.svg rename to test/case/infix_services/ssh/ssh_key_authentication/topology.svg diff --git a/test/case/infix_services/ssh_server_config/Readme.adoc b/test/case/infix_services/ssh/ssh_server_config/Readme.adoc similarity index 100% rename from test/case/infix_services/ssh_server_config/Readme.adoc rename to test/case/infix_services/ssh/ssh_server_config/Readme.adoc diff --git a/test/case/infix_services/ssh_server_config/ssh_server_config.adoc b/test/case/infix_services/ssh/ssh_server_config/ssh_server_config.adoc similarity index 78% rename from test/case/infix_services/ssh_server_config/ssh_server_config.adoc rename to test/case/infix_services/ssh/ssh_server_config/ssh_server_config.adoc index ed0f3b7f..e5890611 100644 --- a/test/case/infix_services/ssh_server_config/ssh_server_config.adoc +++ b/test/case/infix_services/ssh/ssh_server_config/ssh_server_config.adoc @@ -7,11 +7,11 @@ Test SSH server functionality with pre-defined key pair: ==== Topology ifdef::topdoc[] -image::{topdoc}../../test/case/infix_services/ssh_server_config/topology.svg[SSH server configuration topology] +image::{topdoc}../../test/case/infix_services/ssh/ssh_server_config/topology.svg[SSH server configuration topology] endif::topdoc[] ifndef::topdoc[] ifdef::testgroup[] -image::ssh_server_config/topology.svg[SSH server configuration topology] +image::ssh/ssh_server_config/topology.svg[SSH server configuration topology] endif::testgroup[] ifndef::testgroup[] image::topology.svg[SSH server configuration topology] diff --git a/test/case/infix_services/ssh_server_config/test.py b/test/case/infix_services/ssh/ssh_server_config/test.py similarity index 100% rename from test/case/infix_services/ssh_server_config/test.py rename to test/case/infix_services/ssh/ssh_server_config/test.py diff --git a/test/case/infix_services/ssh/ssh_server_config/topology.dot b/test/case/infix_services/ssh/ssh_server_config/topology.dot new file mode 120000 index 00000000..bcb3e9b6 --- /dev/null +++ b/test/case/infix_services/ssh/ssh_server_config/topology.dot @@ -0,0 +1 @@ +../../../../infamy/topologies/1x3.dot \ No newline at end of file diff --git a/test/case/infix_services/ssh/ssh_server_config/topology.svg b/test/case/infix_services/ssh/ssh_server_config/topology.svg new file mode 100644 index 00000000..9517e639 --- /dev/null +++ b/test/case/infix_services/ssh/ssh_server_config/topology.svg @@ -0,0 +1,51 @@ + + + + + + +1x3 + + + +host + +host + +mgmt + +data1 + +data2 + + + +target + +mgmt + +data1 + +data2 + +target + + + +host:mgmt--target:mgmt + + + + +host:data1--target:data1 + + + + +host:data2--target:data2 + + + + diff --git a/test/case/infix_services/ssh_key_authentication/topology.dot b/test/case/infix_services/ssh_key_authentication/topology.dot deleted file mode 120000 index 02b78869..00000000 --- a/test/case/infix_services/ssh_key_authentication/topology.dot +++ /dev/null @@ -1 +0,0 @@ -../../../infamy/topologies/1x1.dot \ No newline at end of file diff --git a/test/case/infix_services/ssh_server_config/topology.dot b/test/case/infix_services/ssh_server_config/topology.dot deleted file mode 100644 index 671ed493..00000000 --- a/test/case/infix_services/ssh_server_config/topology.dot +++ /dev/null @@ -1,24 +0,0 @@ -graph "1x3" { - layout="neato"; - overlap="false"; - esep="+80"; - - node [shape=record, fontname="DejaVu Sans Mono, Book"]; - edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; - - host [ - label="host | { mgmt | data1 | data2 }", - pos="0,12!", - requires="controller", - ]; - - target [ - label="{ mgmt | data1 | data2 } | target", - pos="10,12!", - requires="infix", - ]; - - host:mgmt -- target:mgmt [requires="mgmt", color="lightgray"] - host:data1 -- target:data1 [color="black"] - host:data2 -- target:data2 [color="black"] -} diff --git a/test/infamy/topologies/1x3.dot b/test/infamy/topologies/1x3.dot index 2ba60559..f288f2f9 100644 --- a/test/infamy/topologies/1x3.dot +++ b/test/infamy/topologies/1x3.dot @@ -4,22 +4,21 @@ graph "1x3" { esep="+80"; node [shape=record, fontname="DejaVu Sans Mono, Book"]; - edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; + edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; host [ - label="host | { tgt | data0 | data1 }", - pos="0,12!", - requires="controller", + label="host | { mgmt | data1 | data2 }", + pos="0,12!", + requires="controller", ]; - target [ - label="{ mgmt | data0 | data1 } | target", - pos="10,12!", - - requires="infix", + target [ + label="{ mgmt | data1 | data2 } | target", + pos="10,12!", + requires="infix", ]; - host:tgt -- target:mgmt [requires="mgmt"] - host:data0 -- target:data0 [color=black] - host:data1 -- target:data1 [color=black] -} \ No newline at end of file + host:mgmt -- target:mgmt [requires="mgmt", color="lightgray"] + host:data1 -- target:data1 [color="black"] + host:data2 -- target:data2 [color="black"] +}