Skip to content

Commit e49f9ad

Browse files
committed
test: new test, verify TTL on GRE/VXLAN tunnels
Signed-off-by: Joachim Wiberg <[email protected]>
1 parent eb1daf0 commit e49f9ad

File tree

10 files changed

+507
-0
lines changed

10 files changed

+507
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
include::gre.adoc[]
2+
3+
<<<
4+
5+
include::vxlan.adoc[]
6+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
=== GRE Tunnel TTL verification
2+
3+
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/ietf_interfaces/tunnel_ttl]
4+
5+
==== Description
6+
7+
Verify that GRE and VXLAN tunnels use a fixed TTL (default 64) for
8+
encapsulated frames instead of inheriting the TTL from inner packets.
9+
Critical for protocols like OSPF that use TTL=1 for their packets.
10+
11+
The test setup creates a tunnel between R1 and R3 so that injecting
12+
a frame with TTL=3 from PC:west, routing it through the tunnel, it
13+
would still reach PC:east. (Had it been routed via R2 it would be too
14+
many hops and the TTL would reach zero before the last routing step.)
15+
16+
PC:west -- R1 -- R2 -- R3 -- PC:east
17+
`== Tunnel =='
18+
19+
==== Topology
20+
21+
image::topology.svg[GRE Tunnel TTL verification topology, align=center, scaledwidth=75%]
22+
23+
==== Sequence
24+
25+
. Set up topology and attach to target DUTs
26+
. Configure R1 with gre tunnel to R3
27+
. Configure R2 as intermediate router (underlay forwarding)
28+
. Configure R3 with gre tunnel to R1
29+
. Send ping from PC:west to PC:east with low TTL
30+
. Verify packets arrived at PC:east
31+
32+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test.py
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
#!/usr/bin/env python3
2+
"""Tunnel TTL Verification
3+
4+
Verify that GRE and VXLAN tunnels use a fixed TTL (default 64) for
5+
encapsulated frames instead of inheriting the TTL from inner packets.
6+
Critical for protocols like OSPF that use TTL=1 for their packets.
7+
8+
The test setup creates a tunnel between R1 and R3 so that injecting
9+
a frame with TTL=3 from PC:west, routing it through the tunnel, it
10+
would still reach PC:east. (Had it been routed via R2 it would be too
11+
many hops and the TTL would reach zero before the last routing step.)
12+
13+
PC:west -- R1 -- R2 -- R3 -- PC:east
14+
`== Tunnel =='
15+
16+
"""
17+
18+
import infamy
19+
20+
21+
class ArgumentParser(infamy.ArgumentParser):
22+
def __init__(self):
23+
super().__init__()
24+
self.add_argument("--tunnel")
25+
26+
# IP address plan
27+
# PC:west subnet
28+
PC_WEST_R1 = "192.168.10.1"
29+
PC_WEST_HOST = "192.168.10.2"
30+
PC_WEST_NET = "192.168.10.0/24"
31+
32+
# R1-R2 underlay subnet
33+
R1_R2_R1 = "192.168.50.1"
34+
R1_R2_R2 = "192.168.50.2"
35+
R1_R2_NET = "192.168.50.0/24"
36+
37+
# R2-R3 underlay subnet
38+
R2_R3_R2 = "192.168.60.1"
39+
R2_R3_R3 = "192.168.60.2"
40+
R2_R3_NET = "192.168.60.0/24"
41+
42+
# PC:east subnet
43+
PC_EAST_R3 = "192.168.70.1"
44+
PC_EAST_HOST = "192.168.70.2"
45+
PC_EAST_NET = "192.168.70.0/24"
46+
47+
# Tunnel subnet
48+
TUNNEL_R1 = "10.255.0.1"
49+
TUNNEL_R3 = "10.255.0.2"
50+
TUNNEL_NET = "10.255.0.0/30"
51+
52+
# Prefix lengths
53+
PREFIX_24 = 24
54+
PREFIX_30 = 30
55+
56+
# Test TTL value
57+
TEST_TTL = 3
58+
59+
# VXLAN VNI
60+
VNI = 10
61+
62+
63+
with infamy.Test() as test:
64+
with test.step("Set up topology and attach to target DUTs"):
65+
arg = ArgumentParser()
66+
env = infamy.Env(args=arg)
67+
tunnel = env.args.tunnel
68+
r1 = env.attach("R1", "mgmt")
69+
r2 = env.attach("R2", "mgmt")
70+
r3 = env.attach("R3", "mgmt")
71+
72+
with test.step(f"Configure R1 with {tunnel} tunnel to R3"):
73+
# R1: Entry point, west facing PC, east facing R2, tunnel to R3
74+
75+
# Build tunnel container configs
76+
container_r1 = {
77+
"local": R1_R2_R1,
78+
"remote": R2_R3_R3
79+
}
80+
container_r3 = {
81+
"local": R2_R3_R3,
82+
"remote": R1_R2_R1
83+
}
84+
85+
CONTAINER_TYPE = tunnel
86+
if tunnel == "vxlan":
87+
container_r1["vni"] = VNI
88+
container_r3["vni"] = VNI
89+
90+
r1.put_config_dicts({"ietf-interfaces": {
91+
"interfaces": {
92+
"interface": [{
93+
"name": r1["west"],
94+
"ipv4": {
95+
"address": [{
96+
"ip": PC_WEST_R1,
97+
"prefix-length": PREFIX_24
98+
}],
99+
"forwarding": True
100+
}
101+
}, {
102+
"name": r1["east"],
103+
"ipv4": {
104+
"address": [{
105+
"ip": R1_R2_R1,
106+
"prefix-length": PREFIX_24
107+
}],
108+
"forwarding": True
109+
}
110+
}, {
111+
"name": f"{tunnel}0",
112+
"type": f"infix-if-type:{tunnel}",
113+
"ipv4": {
114+
"address": [{
115+
"ip": TUNNEL_R1,
116+
"prefix-length": PREFIX_30
117+
}],
118+
"forwarding": True
119+
},
120+
CONTAINER_TYPE: container_r1
121+
}]
122+
}
123+
}, "ietf-routing": {
124+
"routing": {
125+
"control-plane-protocols": {
126+
"control-plane-protocol": [{
127+
"type": "infix-routing:static",
128+
"name": "default",
129+
"static-routes": {
130+
"ipv4": {
131+
"route": [{
132+
"destination-prefix": R2_R3_NET,
133+
"next-hop": {
134+
"next-hop-address": R1_R2_R2
135+
}
136+
}, {
137+
"destination-prefix": PC_EAST_NET,
138+
"next-hop": {
139+
"next-hop-address": TUNNEL_R3
140+
}
141+
}]
142+
}
143+
}
144+
}]
145+
}
146+
}
147+
}})
148+
149+
with test.step("Configure R2 as intermediate router (underlay forwarding)"):
150+
# R2: Intermediate router, just forwards packets between west and east
151+
r2.put_config_dicts({"ietf-interfaces": {
152+
"interfaces": {
153+
"interface": [{
154+
"name": r2["west"],
155+
"ipv4": {
156+
"address": [{
157+
"ip": R1_R2_R2,
158+
"prefix-length": PREFIX_24
159+
}],
160+
"forwarding": True
161+
}
162+
}, {
163+
"name": r2["east"],
164+
"ipv4": {
165+
"address": [{
166+
"ip": R2_R3_R2,
167+
"prefix-length": PREFIX_24
168+
}],
169+
"forwarding": True
170+
}
171+
}]
172+
}
173+
}})
174+
175+
with test.step(f"Configure R3 with {tunnel} tunnel to R1"):
176+
# R3: Exit point, west facing R2, east facing PC, tunnel to R1
177+
r3.put_config_dicts({"ietf-interfaces": {
178+
"interfaces": {
179+
"interface": [{
180+
"name": r3["west"],
181+
"ipv4": {
182+
"address": [{
183+
"ip": R2_R3_R3,
184+
"prefix-length": PREFIX_24
185+
}],
186+
"forwarding": True
187+
}
188+
}, {
189+
"name": r3["east"],
190+
"ipv4": {
191+
"address": [{
192+
"ip": PC_EAST_R3,
193+
"prefix-length": PREFIX_24
194+
}],
195+
"forwarding": True
196+
}
197+
}, {
198+
"name": f"{tunnel}0",
199+
"type": f"infix-if-type:{tunnel}",
200+
"ipv4": {
201+
"address": [{
202+
"ip": TUNNEL_R3,
203+
"prefix-length": PREFIX_30
204+
}],
205+
"forwarding": True
206+
},
207+
CONTAINER_TYPE: container_r3
208+
}]
209+
}
210+
}, "ietf-routing": {
211+
"routing": {
212+
"control-plane-protocols": {
213+
"control-plane-protocol": [{
214+
"type": "infix-routing:static",
215+
"name": "default",
216+
"static-routes": {
217+
"ipv4": {
218+
"route": [{
219+
"destination-prefix": R1_R2_NET,
220+
"next-hop": {
221+
"next-hop-address": R2_R3_R2
222+
}
223+
}, {
224+
"destination-prefix": PC_WEST_NET,
225+
"next-hop": {
226+
"next-hop-address": TUNNEL_R1
227+
}
228+
}]
229+
}
230+
}
231+
}]
232+
}
233+
}
234+
}})
235+
236+
with test.step("Send ping from PC:west to PC:east with low TTL"):
237+
_, pc_east = env.ltop.xlate("PC", "east")
238+
239+
with infamy.IsolatedMacVlan(pc_east) as east_ns:
240+
east_ns.addip(PC_EAST_HOST)
241+
east_ns.addroute("default", PC_EAST_R3)
242+
243+
pcap = east_ns.pcap("icmp")
244+
with pcap:
245+
_, pc_west = env.ltop.xlate("PC", "west")
246+
with infamy.IsolatedMacVlan(pc_west) as west_ns:
247+
west_ns.addip(PC_WEST_HOST)
248+
west_ns.addroute("default", PC_WEST_R1)
249+
250+
# Send 3 pings with TTL=3, TTL is decremented before each
251+
# router hop. So at PC:west (TTL=3) -> R1 routed to GRE
252+
# tunnel (TTL=2) -> frame egresses tunnel -> R3 where it
253+
# is routed to PC:east (TTL=1).
254+
#
255+
# If outer TTL was inherited (TTL=2), packet would be
256+
# dropped at R3.
257+
west_ns.runsh(f"ping -c 3 -t {TEST_TTL} {PC_EAST_HOST}")
258+
259+
with test.step("Verify packets arrived at PC:east"):
260+
packets = pcap.tcpdump()
261+
print("Captured packets on PC:east:")
262+
print(packets)
263+
264+
pings = [line for line in packets.splitlines()
265+
if f"{PC_WEST_HOST} > {PC_EAST_HOST}: ICMP echo request" in line]
266+
267+
assert len(pings) >= 1, f"Expected at least 1 ping, got {len(pings)}."
268+
269+
test.succeed()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
- settings:
3+
test-spec: <case>.adoc
4+
5+
- name: GRE Tunnel TTL verification
6+
case: gre.py
7+
opts: ["--tunnel", "gre"]
8+
9+
- name: VXLAN Tunnel TTL verification
10+
case: vxlan.py
11+
opts: ["--tunnel", "vxlan"]
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
graph "tunnel-ttl-3dut" {
2+
layout="neato";
3+
overlap="false";
4+
esep="+60";
5+
6+
node [shape=record, fontname="DejaVu Sans Mono, Book"];
7+
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
8+
9+
PC [
10+
label="<mgmt1> mgmt1 | <west> west | <mgmt2> mgmt2 | <mgmt3> mgmt3 | <east> east | { PC }"
11+
pos="10,0!",
12+
requires="controller",
13+
];
14+
15+
R1 [
16+
label="R1 | <mgmt> mgmt | <west> west | <east> east",
17+
pos="0, -5!",
18+
requires="infix",
19+
];
20+
21+
R2 [
22+
label="<west> west | R2 | <mgmt> mgmt | <east> east",
23+
pos="10, -5!",
24+
requires="infix",
25+
];
26+
27+
R3 [
28+
label="<west> west | R3 | <mgmt> mgmt | <east> east",
29+
pos="20, -5!",
30+
requires="infix",
31+
];
32+
33+
PC:mgmt1 -- R1:mgmt [requires="mgmt", color="lightgray"]
34+
PC:mgmt2 -- R2:mgmt [requires="mgmt", color="lightgray"]
35+
PC:mgmt3 -- R3:mgmt [requires="mgmt", color="lightgray"]
36+
37+
PC:west -- R1:west [headlabel=".1", label="192.168.10.0/24", taillabel=".2", labeldistance=2, fontcolor="black", color="black"]
38+
R1:east -- R2:west [headlabel=".1", label="192.168.50.0/24", taillabel=".2", labeldistance=2, fontcolor="black", color="blue"]
39+
R2:east -- R3:west [headlabel=".1", label="192.168.60.0/24", taillabel=".2", labeldistance=2, fontcolor="black", color="blue"]
40+
R3:east -- PC:east [headlabel=".1", label="192.168.70.0/24", taillabel=".2", labeldistance=2, fontcolor="black", color="black"]
41+
}

0 commit comments

Comments
 (0)