From c099d887afb6b4ae5420fc0f90acaee7f71029be Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 10 Dec 2024 13:23:50 +0100 Subject: [PATCH] Add support for GRE tunnels Basic support GRE and GRETAP tunnels both for IPv4 and IPv6. Limited support right now, only possible to configure local ip address and remote ip address. --- doc/ChangeLog.md | 1 + doc/tunnels.md | 16 ++ src/confd/src/Makefile.am | 1 + src/confd/src/ietf-interfaces.c | 11 ++ src/confd/src/ietf-interfaces.h | 6 +- src/confd/src/infix-if-gre.c | 41 ++++ src/confd/yang/infix-if-bridge.yang | 3 +- src/confd/yang/infix-if-gre.yang | 35 ++++ src/confd/yang/infix-if-gre@2024-12-20.yang | 1 + src/confd/yang/infix-if-type.yang | 6 + src/confd/yang/infix-interfaces.yang | 21 ++ src/statd/python/cli_pretty/cli_pretty.py | 5 + src/statd/python/yanger/yanger.py | 24 ++- test/case/ietf_interfaces/Readme.adoc | 3 + .../ietf_interfaces/gre_basic/Readme.adoc | 26 +++ test/case/ietf_interfaces/gre_basic/test.py | 186 ++++++++++++++++++ .../ietf_interfaces/gre_basic/topology.dot | 35 ++++ .../ietf_interfaces/gre_basic/topology.svg | 72 +++++++ .../gretap_bridged/Readme.adoc | 24 +++ .../ietf_interfaces/gretap_bridged/test.py | 106 ++++++++++ .../gretap_bridged/topology.dot | 35 ++++ .../gretap_bridged/topology.svg | 70 +++++++ .../case/ietf_interfaces/ietf_interfaces.yaml | 6 + 23 files changed, 729 insertions(+), 5 deletions(-) create mode 100644 doc/tunnels.md create mode 100644 src/confd/src/infix-if-gre.c create mode 100644 src/confd/yang/infix-if-gre.yang create mode 120000 src/confd/yang/infix-if-gre@2024-12-20.yang create mode 100644 test/case/ietf_interfaces/gre_basic/Readme.adoc create mode 100755 test/case/ietf_interfaces/gre_basic/test.py create mode 100644 test/case/ietf_interfaces/gre_basic/topology.dot create mode 100644 test/case/ietf_interfaces/gre_basic/topology.svg create mode 100644 test/case/ietf_interfaces/gretap_bridged/Readme.adoc create mode 100755 test/case/ietf_interfaces/gretap_bridged/test.py create mode 100644 test/case/ietf_interfaces/gretap_bridged/topology.dot create mode 100644 test/case/ietf_interfaces/gretap_bridged/topology.svg diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md index 7f5f37e6..7507412e 100644 --- a/doc/ChangeLog.md +++ b/doc/ChangeLog.md @@ -17,6 +17,7 @@ All notable changes to the project are documented in this file. RPC and add boot order to operational datastore. - SSH Server is now configurable, issue #441 SSH Server and NETCONF Server now uses the same SSH hostkey in factory-config + - Add support for GRE/GRETAP tunnels ### Fixes diff --git a/doc/tunnels.md b/doc/tunnels.md new file mode 100644 index 00000000..fc1ffc4a --- /dev/null +++ b/doc/tunnels.md @@ -0,0 +1,16 @@ +# Tunnel configuration + +Tunnel traffic from point A to point B + + +## Generic Routing Encapsulation (GRE) + +The support for GRE tunnels includes IPv4 and IPv6 tunnels both in GRE +(IP) and GRETAP (MAC) modes. +``` +admin@example:/config/> edit interface gre1 +admin@example:/config/interface/gre1/> set type gretap +admin@example:/config/interface/gre1/> set gre local 192.168.3.1 remote 192.168.3.2 +admin@example:/config/interface/gre1/> leave +admin@example:/> +``` diff --git a/src/confd/src/Makefile.am b/src/confd/src/Makefile.am index 6a4a64a9..101183d0 100644 --- a/src/confd/src/Makefile.am +++ b/src/confd/src/Makefile.am @@ -34,6 +34,7 @@ confd_plugin_la_SOURCES = \ infix-if-bridge-port.c \ infix-if-veth.c \ infix-if-vlan.c \ + infix-if-gre.c \ ietf-keystore.c \ ietf-system.c \ ietf-syslog.c \ diff --git a/src/confd/src/ietf-interfaces.c b/src/confd/src/ietf-interfaces.c index f2928e25..4cd6028f 100644 --- a/src/confd/src/ietf-interfaces.c +++ b/src/confd/src/ietf-interfaces.c @@ -87,6 +87,10 @@ static int ifchange_cand_infer_type(sr_session_ctx_t *session, const char *path) inferred.data.string_val = "infix-if-type:vlan"; else if (!fnmatch("*.+([0-9])", ifname, FNM_EXTMATCH)) inferred.data.string_val = "infix-if-type:vlan"; + else if (!fnmatch("gre+([0-9])", ifname, FNM_EXTMATCH)) + inferred.data.string_val = "infix-if-type:gre"; + else if (!fnmatch("gretap+([0-9])", ifname, FNM_EXTMATCH)) + inferred.data.string_val = "infix-if-type:gretap"; free(ifname); @@ -395,6 +399,8 @@ static int netdag_gen_afspec_add(sr_session_ctx_t *session, struct dagger *net, sr_session_set_error_message(net->session, "Cannot create fixed Ethernet interface %s," " wrong type or name.", ifname); return -ENOENT; + } else if (!strcmp(iftype, "infix-if-type:gre") || !strcmp(iftype, "infix-if-type:gretap")) { + err = gre_gen(net, NULL, cif, ip); } else { sr_session_set_error_message(net->session, "%s: unsupported interface type \"%s\"", ifname, iftype); return -ENOSYS; @@ -420,6 +426,8 @@ static int netdag_gen_afspec_set(sr_session_ctx_t *session, struct dagger *net, return netdag_gen_vlan(net, dif, cif, ip); if (!strcmp(iftype, "infix-if-type:veth")) return 0; + if (!strcmp(iftype, "infix-if-type:gretap")) + return 0; ERROR("%s: unsupported interface type \"%s\"", ifname, iftype); return -ENOSYS; @@ -451,6 +459,9 @@ static bool netdag_must_del(struct lyd_node *dif, struct lyd_node *cif) } else if (!strcmp(iftype, "infix-if-type:veth")) { if (lydx_get_descendant(lyd_child(dif), "veth", NULL)) return true; + } else if (!strcmp(iftype, "infix-if-type:gre") || !strcmp(iftype, "infix-if-type:gretap")) { + if (lydx_get_descendant(lyd_child(dif), "gre", NULL)) + return true; /* } else if (!strcmp(iftype, "infix-if-type:lag")) { if (is_phys_addr_deleted(dif)) diff --git a/src/confd/src/ietf-interfaces.h b/src/confd/src/ietf-interfaces.h index 585a0d9a..7c733950 100644 --- a/src/confd/src/ietf-interfaces.h +++ b/src/confd/src/ietf-interfaces.h @@ -73,6 +73,8 @@ int ifchange_cand_infer_vlan(sr_session_ctx_t *session, const char *path); int netdag_gen_vlan(struct dagger *net, struct lyd_node *dif, struct lyd_node *cif, FILE *ip); +/* infix-if-gre.c */ +int gre_gen(struct dagger *net, struct lyd_node *dif, + struct lyd_node *cif, FILE *ip); + #endif /* CONFD_IETF_INTERFACES_H_ */ - - diff --git a/src/confd/src/infix-if-gre.c b/src/confd/src/infix-if-gre.c new file mode 100644 index 00000000..9b078ba7 --- /dev/null +++ b/src/confd/src/infix-if-gre.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#include + +#include "ietf-interfaces.h" + +int gre_gen(struct dagger *net, struct lyd_node *dif, + struct lyd_node *cif, FILE *ip) +{ + const char *ifname, *iftype, *local, *remote, *mac = NULL; + struct lyd_node *node = NULL; + char gretype[10] = ""; + int ipv6; + + ifname = lydx_get_cattr(cif, "name"); + iftype = lydx_get_cattr(cif, "type"); + + node = lydx_get_descendant(lyd_child(cif), "gre", NULL); + if (!node) + return -EINVAL; + + local = lydx_get_cattr(node, "local"); + remote = lydx_get_cattr(node, "remote"); + ipv6 = !!strstr(local, ":"); + + if (!strcmp(iftype, "infix-if-type:gre")) { + snprintf(gretype, sizeof(gretype), "%sgre", ipv6 ? "ip6" : ""); + + } + else if (!strcmp(iftype, "infix-if-type:gretap")) { + snprintf(gretype, sizeof(gretype), "%sgretap", ipv6 ? "ip6" : ""); + mac = get_phys_addr(cif, NULL); + } + + fprintf(ip, "link add name %s type %s local %s remote %s", ifname, gretype, local, remote); + if (mac) + fprintf(ip, "address %s\n", mac); + else + fprintf(ip, "\n"); + + return 0; +} diff --git a/src/confd/yang/infix-if-bridge.yang b/src/confd/yang/infix-if-bridge.yang index a141aa13..4a594b41 100644 --- a/src/confd/yang/infix-if-bridge.yang +++ b/src/confd/yang/infix-if-bridge.yang @@ -525,7 +525,8 @@ submodule infix-if-bridge { "derived-from-or-self(if:type,'ianaift:ethernetCsmacd') or "+ "derived-from-or-self(if:type,'ianaift:ieee8023adLag') or "+ "derived-from-or-self(if:type,'ianaift:l2vlan') or "+ - "derived-from-or-self(if:type,'ianaift:ilan')" { + "derived-from-or-self(if:type,'ianaift:ilan') or "+ + "derived-from-or-self(if:type,'infix-ift:gretap')" { description "Applies when a Bridge interface exists."; } diff --git a/src/confd/yang/infix-if-gre.yang b/src/confd/yang/infix-if-gre.yang new file mode 100644 index 00000000..f8f9b3ef --- /dev/null +++ b/src/confd/yang/infix-if-gre.yang @@ -0,0 +1,35 @@ +submodule infix-if-gre { + yang-version 1.1; + belongs-to infix-interfaces { + prefix infix-if; + } + + import ietf-interfaces { + prefix if; + } + + import infix-if-type { + prefix infixift; + } + + organization "KernelKit"; + contact "kernelkit@googlegroups.com"; + description "GRE and GRETAP tunnel extension for ietf-interfaces"; + + revision 2024-12-20 { + description "Initial revision."; + reference "internal"; + } + + augment "/if:interfaces/if:interface" { + when "derived-from-or-self(if:type, 'infixift:gre') or"+ + "derived-from-or-self(if:type, 'infixift:gretap')" { + description "Only shown for if:type infixift:gre"; + } + + description "Augments the interface model with GRE tunnels."; + container gre { + uses local-remote; + } + } +} diff --git a/src/confd/yang/infix-if-gre@2024-12-20.yang b/src/confd/yang/infix-if-gre@2024-12-20.yang new file mode 120000 index 00000000..93196ca8 --- /dev/null +++ b/src/confd/yang/infix-if-gre@2024-12-20.yang @@ -0,0 +1 @@ +infix-if-gre.yang \ No newline at end of file diff --git a/src/confd/yang/infix-if-type.yang b/src/confd/yang/infix-if-type.yang index 179358d7..d26ba58f 100644 --- a/src/confd/yang/infix-if-type.yang +++ b/src/confd/yang/infix-if-type.yang @@ -64,6 +64,12 @@ module infix-if-type { description "Interface with properties resembling Ethernet"; reference "RFC 3635"; } + identity gre { + base infix-interface-type; + } + identity gretap { + base infix-interface-type; + } identity lag { base infix-interface-type; base ianaift:ieee8023adLag; diff --git a/src/confd/yang/infix-interfaces.yang b/src/confd/yang/infix-interfaces.yang index a97c2b20..90978e07 100644 --- a/src/confd/yang/infix-interfaces.yang +++ b/src/confd/yang/infix-interfaces.yang @@ -12,12 +12,16 @@ module infix-interfaces { import ietf-yang-types { prefix yang; } + import ietf-inet-types { + prefix inet; + } include infix-if-base; include infix-if-bridge; include infix-if-container; include infix-if-veth; include infix-if-vlan; + include infix-if-gre; organization "KernelKit"; contact "kernelkit@googlegroups.com"; @@ -80,6 +84,23 @@ module infix-interfaces { reference "internal"; } + grouping local-remote { + description "Local address to use as source address"; + leaf local { + type inet:ip-address; + mandatory true; + } + leaf remote { + description "Peer address"; + type inet:ip-address; + must "(contains(../local, ':') and contains(., ':')) + or (not(contains(../local, ':')) and not(contains(., ':')))" { + error-message + "Local and remote must be both IPv4 or both IPv6 addresses."; + } + mandatory true; + } + } /* * Data Nodes */ diff --git a/src/statd/python/cli_pretty/cli_pretty.py b/src/statd/python/cli_pretty/cli_pretty.py index f485fada..acb08c1b 100755 --- a/src/statd/python/cli_pretty/cli_pretty.py +++ b/src/statd/python/cli_pretty/cli_pretty.py @@ -304,6 +304,8 @@ class Iface: else: self.ipv6_addr = [] + if self.data.get('infix-interfaces:gre'): + self.gre = self.data['infix-interfaces:gre'] if self.data.get('infix-interfaces:vlan'): self.lower_if = self.data.get('infix-interfaces:vlan', None).get('lower-layer-if',None) @@ -323,6 +325,9 @@ class Iface: def is_veth(self): return self.data['type'] == "infix-if-type:veth" + def is_gre(self): + return self.data['type'] == "infix-if-type:gre" or 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": diff --git a/src/statd/python/yanger/yanger.py b/src/statd/python/yanger/yanger.py index 401d6bdc..62fdc125 100755 --- a/src/statd/python/yanger/yanger.py +++ b/src/statd/python/yanger/yanger.py @@ -38,6 +38,9 @@ def json_get_yang_type(iface_in): if iface_in['link_type'] == "loopback": return "infix-if-type:loopback" + if iface_in['link_type'] in ("gre", "gre6"): + return "infix-if-type:gre" + if iface_in['link_type'] != "ether": return "infix-if-type:other" @@ -53,6 +56,9 @@ def json_get_yang_type(iface_in): if iface_in['linkinfo']['info_kind'] == "veth": return "infix-if-type:veth" + if iface_in['linkinfo']['info_kind'] in ("gretap", "ip6gretap"): + return "infix-if-type:gretap" + if iface_in['linkinfo']['info_kind'] == "vlan": return "infix-if-type:vlan" @@ -715,6 +721,19 @@ def add_bridge_port_lower(ifname, iface_in, iface_out): multicast = get_brport_multicast(ifname) insert(iface_out, "infix-interfaces:bridge-port", "multicast", multicast) + +def add_gre(iface_in, iface_out): + if 'link_type' in iface_in: + val = json_get_yang_type(iface_in) + if val != "infix-if-type:gre" and val != "infix-if-type:gretap": + return; + gre={} + info_data=iface_in.get("linkinfo", {}).get("info_data", {}) + gre["local"] = info_data.get("local") + gre["remote"] = info_data.get("remote") + insert(iface_out, "infix-interfaces:gre", gre) + + def add_ip_link(ifname, iface_in, iface_out): if 'ifname' in iface_in: iface_out['name'] = ifname @@ -725,14 +744,14 @@ def add_ip_link(ifname, iface_in, iface_out): if 'ifalias' in iface_in: iface_out['description'] = iface_in['ifalias'] - if 'address' in iface_in: + if 'address' in iface_in and not "POINTOPOINT" in iface_in["flags"]: iface_out['phys-address'] = iface_in['address'] add_bridge_port_common(ifname, iface_in, iface_out) add_bridge_port_lower(ifname, iface_in, iface_out) if not iface_is_dsa(iface_in): - if 'link' in iface_in: + if iface_in.get('link'): insert(iface_out, "infix-interfaces:vlan", "lower-layer-if", iface_in['link']) elif 'link_index' in iface_in: # 'link_index' is the only reference we have if the link iface is in a namespace @@ -758,6 +777,7 @@ def add_ip_link(ifname, iface_in, iface_out): if 'link_type' in iface_in: val = json_get_yang_type(iface_in) iface_out['type'] = val + add_gre(iface_in, iface_out) val = lookup(iface_in, "stats64", "rx", "bytes") if val is not None: diff --git a/test/case/ietf_interfaces/Readme.adoc b/test/case/ietf_interfaces/Readme.adoc index 712d9573..61b861fa 100644 --- a/test/case/ietf_interfaces/Readme.adoc +++ b/test/case/ietf_interfaces/Readme.adoc @@ -47,3 +47,6 @@ include::veth_delete/Readme.adoc[] include::vlan_iface_termination/Readme.adoc[] +include::gre_basic/Readme.adoc[] + +include::gretap_bridged/Readme.adoc[] diff --git a/test/case/ietf_interfaces/gre_basic/Readme.adoc b/test/case/ietf_interfaces/gre_basic/Readme.adoc new file mode 100644 index 00000000..f197f2f4 --- /dev/null +++ b/test/case/ietf_interfaces/gre_basic/Readme.adoc @@ -0,0 +1,26 @@ +=== Basic IP GRE test +==== Description +Test setting up IP GRE tunnels using IPv4 and IPv6, +and then a connectivity test. + +==== Topology +ifdef::topdoc[] +image::../../test/case/ietf_interfaces/gre_basic/topology.svg[Basic IP GRE test topology] +endif::topdoc[] +ifndef::topdoc[] +ifdef::testgroup[] +image::gre_basic/topology.svg[Basic IP GRE test topology] +endif::testgroup[] +ifndef::testgroup[] +image::topology.svg[Basic IP GRE test topology] +endif::testgroup[] +endif::topdoc[] +==== Test sequence +. Set up topology and attach to target DUTs +. Configure DUTs +. Test connectivity host:data to gre 10.0.0.2 +. Test connectivity host:data to gre on right 2001:db8::c0a8:0a02 + + +<<< + diff --git a/test/case/ietf_interfaces/gre_basic/test.py b/test/case/ietf_interfaces/gre_basic/test.py new file mode 100755 index 00000000..1a1a0ef7 --- /dev/null +++ b/test/case/ietf_interfaces/gre_basic/test.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +""" +Basic IP GRE test + +Test setting up IP GRE tunnels using IPv4 and IPv6, +and then a connectivity test. +""" + +import infamy + +with infamy.Test() as test: + with test.step("Set up topology and attach to target DUTs"): + env = infamy.Env() + left = env.attach("left", "mgmt") + right = env.attach("right", "mgmt") + _, leftlink = env.ltop.xlate("left", "link") + _, leftdata = env.ltop.xlate("left", "data") + _, rightlink = env.ltop.xlate("right", "link") + + + with test.step("Configure DUTs"): + left.put_config_dicts({ "ietf-interfaces": { + "interfaces": { + "interface": [ + { + "name": leftlink, + "ipv4": { + "address": [{ + "ip": "192.168.50.1", + "prefix-length": 24 + }], + "forwarding": True + }, + "ipv6": { + "address": [{ + "ip": "2001:db8:3c4d:50::1", + "prefix-length": 64 + }], + "forwarding": True + } + }, + { + "name": leftdata, + "ipv4": { + "address": [{ + "ip": "192.168.10.1", + "prefix-length": 24 + }], + "forwarding": True + }, + "ipv6": { + "address": [{ + "ip": "2001:db8:3c4d:10::1", + "prefix-length": 64 + }], + "forwarding": True + } + }, + { + "name": "gre0", + "type": "infix-if-type:gre", + "ipv4": { + "address": [{ + "ip": "192.168.30.1", + "prefix-length": 24 + }], + "forwarding": True + }, + "gre": { + "local": "192.168.50.1", + "remote": "192.168.50.2" + } + + }, + { + "name": "gre6", + "type": "infix-if-type:gre", + "ipv6": { + "address": [{ + "ip": "2001:db8:3c4d:30::1", + "prefix-length": 64 + }] + }, + "gre": { + "local": "2001:db8:3c4d:50::1", + "remote": "2001:db8:3c4d:50::2", + } + }] + } + } + }) + + right.put_config_dicts({ + "ietf-interfaces": { + "interfaces": { + "interface": [ + { + "name": rightlink, + "ipv4": { + "address": [{ + "ip": "192.168.50.2", + "prefix-length": 24 + }], + "forwarding": True + }, + "ipv6": { + "address": [{ + "ip": "2001:db8:3c4d:50::2", + "prefix-length": 64 + }] + } + }, + { + "name": "gre1", + "type": "infix-if-type:gre", + "ipv4": { + "address": [{ + "ip": "192.168.30.2", + "prefix-length": 24 + }], + "forwarding": True + }, + "gre": { + "local": "192.168.50.2", + "remote": "192.168.50.1" + } + }, + { + "name": "gre6", + "type": "infix-if-type:gre", + "ipv6": { + "address": [{ + "ip": "2001:db8:3c4d:30::2", + "prefix-length": 64 + }] + }, + "gre": { + "local": "2001:db8:3c4d:50::2", + "remote": "2001:db8:3c4d:50::1", + } + }] + } + }, + "ietf-routing": { + "routing": { + "control-plane-protocols": { + "control-plane-protocol": [{ + "type": "infix-routing:static", + "name": "default", + "static-routes": { + "ipv4": { + "route": [{ + "destination-prefix": "192.168.10.0/24", + "next-hop": { + "next-hop-address": "192.168.30.1" + } + }] + }, + "ipv6": { + "route": [{ + "destination-prefix": "2001:db8:3c4d:10::/64", + "next-hop": { + "next-hop-address": "2001:db8:3c4d:30::1" + } + }] + } + } + }] + } + } + } + }) + _, hport = env.ltop.xlate("host", "data") + with test.step("Test connectivity host:data to gre 10.0.0.2"): + with infamy.IsolatedMacVlan(hport) as ns0: + ns0.addip("192.168.10.2") + ns0.addroute("192.168.30.0/24", "192.168.10.1") + ns0.must_reach("192.168.30.2") + with test.step("Test connectivity host:data to gre on right 2001:db8::c0a8:0a02"): + with infamy.IsolatedMacVlan(hport) as ns0: + ns0.addip("2001:db8:3c4d:10::2", prefix_length=64, proto="ipv6") + ns0.addroute("2001:db8:3c4d:30::/64", "2001:db8:3c4d:10::1", proto="ipv6") + #breakpoint() + ns0.must_reach("2001:db8:3c4d:30::2") + + test.succeed() diff --git a/test/case/ietf_interfaces/gre_basic/topology.dot b/test/case/ietf_interfaces/gre_basic/topology.dot new file mode 100644 index 00000000..ed68c3dd --- /dev/null +++ b/test/case/ietf_interfaces/gre_basic/topology.dot @@ -0,0 +1,35 @@ +graph "gre-basic" { + layout="neato"; + overlap="false"; + esep="+40"; + + node [shape=record, fontname="DejaVu Sans Mono, Book"]; + edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; + + host [ + label="host | { mgmt1 } | { data } | { mgmt2 }" + pos="3,0!", + kind="controller", + ]; + + left [ + label="{ left } | { mgmt } | { data } | { link }", + pos="0, -3!", + + kind="infix", + ]; + + right [ + label="{ link } | { mgmt } | { right }", + pos="8,-3!", + + kind="infix", + ]; + + host:mgmt1 -- left:mgmt [kind=mgmt, color="lightgray"] + host:data -- left:data [headlabel=".1", label="192.168.10.0/24" taillabel=".2 ", labeldistance=1, fontcolor="black", color="black"] + host:mgmt2 -- right:mgmt [kind=mgmt, color="lightgray"] + + + left:link -- right:link [headlabel=".1\n\n", label="192.168.50.0/24", taillabel="\n.2", labeldistance=1, fontcolor="black", color="black"] +} \ No newline at end of file diff --git a/test/case/ietf_interfaces/gre_basic/topology.svg b/test/case/ietf_interfaces/gre_basic/topology.svg new file mode 100644 index 00000000..4a86ba43 --- /dev/null +++ b/test/case/ietf_interfaces/gre_basic/topology.svg @@ -0,0 +1,72 @@ + + + + + + +gre-basic + + + +host + +host + +mgmt1 + +data + +mgmt2 + + + +left + +left + +mgmt + +data + +link + + + +host:mgmt1--left:mgmt + + + + +host:data--left:data + +192.168.10.0/24 +.1 +.2  + + + +right + +link + +mgmt + +right + + + +host:mgmt2--right:mgmt + + + + +left:link--right:link + +192.168.50.0/24 +.1 +.2 + + + diff --git a/test/case/ietf_interfaces/gretap_bridged/Readme.adoc b/test/case/ietf_interfaces/gretap_bridged/Readme.adoc new file mode 100644 index 00000000..6060218a --- /dev/null +++ b/test/case/ietf_interfaces/gretap_bridged/Readme.adoc @@ -0,0 +1,24 @@ +=== GRETAP interface bridged with physical +==== Description +Test that GRETAP works as it should and that it possible to bridge it. + +==== Topology +ifdef::topdoc[] +image::../../test/case/ietf_interfaces/gretap_bridged/topology.svg[GRETAP interface bridged with physical topology] +endif::topdoc[] +ifndef::topdoc[] +ifdef::testgroup[] +image::gretap_bridged/topology.svg[GRETAP interface bridged with physical topology] +endif::testgroup[] +ifndef::testgroup[] +image::topology.svg[GRETAP interface bridged with physical topology] +endif::testgroup[] +endif::topdoc[] +==== Test sequence +. Set up topology and attach to target DUTs +. Configure DUTs +. Test connectivity host:data to right:gre0 at 192.168.10.2 + + +<<< + diff --git a/test/case/ietf_interfaces/gretap_bridged/test.py b/test/case/ietf_interfaces/gretap_bridged/test.py new file mode 100755 index 00000000..2ec73c7f --- /dev/null +++ b/test/case/ietf_interfaces/gretap_bridged/test.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +""" +GRETAP interface bridged with physical + +Test that GRETAP works as it should and that it possible to bridge it. + +""" + +import infamy + +with infamy.Test() as test: + with test.step("Set up topology and attach to target DUTs"): + env = infamy.Env() + left = env.attach("left", "mgmt") + right = env.attach("right", "mgmt") + _, leftlink = env.ltop.xlate("left", "link") + _, leftdata = env.ltop.xlate("left", "data") + _, rightlink = env.ltop.xlate("right", "link") + + + with test.step("Configure DUTs"): + left.put_config_dicts({ "ietf-interfaces": { + "interfaces": { + "interface": [ + { + "name": leftlink, + "ipv4": { + "address": [{ + "ip": "192.168.50.1", + "prefix-length": 24 + }], + "forwarding": True + } + }, + { + "name": leftdata, + "bridge-port": { + "bridge": "br0" + } + }, + { + "name": "br0", + "type": "infix-if-type:bridge" + }, + { + "name": "gre0", + "type": "infix-if-type:gretap", + "gre": { + "local": "192.168.50.1", + "remote": "192.168.50.2" + }, + "bridge-port": { + "bridge": "br0" + } + + } + ] + } + } + }) + + right.put_config_dicts({ + "ietf-interfaces": { + "interfaces": { + "interface": [ + { + "name": rightlink, + "ipv4": { + "address": [{ + "ip": "192.168.50.2", + "prefix-length": 24 + }], + "forwarding": True + }, + "ipv6": { + "address": [{ + "ip": "2001:db8:3c4d:50::2", + "prefix-length": 64 + }] + } + }, + { + "name": "gre0", + "type": "infix-if-type:gretap", + "ipv4": { + "address": [{ + "ip": "192.168.10.2", + "prefix-length": 24 + }], + "forwarding": True + }, + "gre": { + "local": "192.168.50.2", + "remote": "192.168.50.1" + } + }] + } + } + }) + _, hport = env.ltop.xlate("host", "data") + with test.step("Test connectivity host:data to right:gre0 at 192.168.10.2"): + with infamy.IsolatedMacVlan(hport) as ns0: + ns0.addip("192.168.10.1") + ns0.must_reach("192.168.10.2") + + test.succeed() diff --git a/test/case/ietf_interfaces/gretap_bridged/topology.dot b/test/case/ietf_interfaces/gretap_bridged/topology.dot new file mode 100644 index 00000000..d3dd36aa --- /dev/null +++ b/test/case/ietf_interfaces/gretap_bridged/topology.dot @@ -0,0 +1,35 @@ +graph "gre-basic" { + layout="neato"; + overlap="false"; + esep="+40"; + + node [shape=record, fontname="DejaVu Sans Mono, Book"]; + edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; + + host [ + label="host | { mgmt1 } | { data } | { mgmt2 }" + pos="3,0!", + kind="controller", + ]; + + left [ + label="{ left } | { mgmt } | { data } | { link }", + pos="0, -3!", + + kind="infix", + ]; + + right [ + label="{ link } | { mgmt } | { right }", + pos="8,-3!", + + kind="infix", + ]; + + host:mgmt1 -- left:mgmt [kind=mgmt, color="lightgray"] + host:data -- left:data [taillabel="192.168.10.2/24    ", fontcolor="black", color="black"] + host:mgmt2 -- right:mgmt [kind=mgmt, color="lightgray"] + + + left:link -- right:link [headlabel=".1\n\n", label="192.168.50.0/24", taillabel="\n.2", labeldistance=1, fontcolor="black", color="black"] +} \ No newline at end of file diff --git a/test/case/ietf_interfaces/gretap_bridged/topology.svg b/test/case/ietf_interfaces/gretap_bridged/topology.svg new file mode 100644 index 00000000..f6737ac4 --- /dev/null +++ b/test/case/ietf_interfaces/gretap_bridged/topology.svg @@ -0,0 +1,70 @@ + + + + + + +gre-basic + + + +host + +host + +mgmt1 + +data + +mgmt2 + + + +left + +left + +mgmt + +data + +link + + + +host:mgmt1--left:mgmt + + + + +host:data--left:data + +192.168.10.2/24     + + + +right + +link + +mgmt + +right + + + +host:mgmt2--right:mgmt + + + + +left:link--right:link + +192.168.50.0/24 +.1 +.2 + + + diff --git a/test/case/ietf_interfaces/ietf_interfaces.yaml b/test/case/ietf_interfaces/ietf_interfaces.yaml index b1418d10..532a8de5 100644 --- a/test/case/ietf_interfaces/ietf_interfaces.yaml +++ b/test/case/ietf_interfaces/ietf_interfaces.yaml @@ -64,3 +64,9 @@ - name: vlan_iface_termination case: vlan_iface_termination/test.py + +- name: gre_basic + case: gre_basic/test.py + +- name: gretap_bridged + case: gretap_bridged/test.py