Skip to content

Commit 289e3d6

Browse files
committed
test: new test, verify GRE TTL
Signed-off-by: Joachim Wiberg <[email protected]>
1 parent 1c847d1 commit 289e3d6

File tree

6 files changed

+437
-0
lines changed

6 files changed

+437
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test.adoc
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 tunnels use a fixed TTL (default 64) for encapsulated
8+
frames instead of inheriting the TTL from inner packets. Critical for
9+
protocols like OSPF that use TTL=1 for their packets.
10+
11+
The test setup creates a GRE tunnel between R1 and R3 so that injecting
12+
a frame with TTL=3 from PC:west, routing it through the GRE 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+
`=== GRE ==='
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: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
#!/usr/bin/env python3
2+
"""GRE Tunnel TTL Verification
3+
4+
Verify that GRE tunnels use a fixed TTL (default 64) for encapsulated
5+
frames instead of inheriting the TTL from inner packets. Critical for
6+
protocols like OSPF that use TTL=1 for their packets.
7+
8+
The test setup creates a GRE tunnel between R1 and R3 so that injecting
9+
a frame with TTL=3 from PC:west, routing it through the GRE 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+
`=== GRE ==='
15+
16+
"""
17+
18+
import infamy
19+
20+
# IP address plan
21+
# PC:west subnet
22+
PC_WEST_R1 = "192.168.10.1"
23+
PC_WEST_HOST = "192.168.10.2"
24+
PC_WEST_NET = "192.168.10.0/24"
25+
26+
# R1-R2 underlay subnet
27+
R1_R2_R1 = "192.168.50.1"
28+
R1_R2_R2 = "192.168.50.2"
29+
R1_R2_NET = "192.168.50.0/24"
30+
31+
# R2-R3 underlay subnet
32+
R2_R3_R2 = "192.168.60.1"
33+
R2_R3_R3 = "192.168.60.2"
34+
R2_R3_NET = "192.168.60.0/24"
35+
36+
# PC:east subnet
37+
PC_EAST_R3 = "192.168.70.1"
38+
PC_EAST_HOST = "192.168.70.2"
39+
PC_EAST_NET = "192.168.70.0/24"
40+
41+
# GRE tunnel subnet
42+
GRE_R1 = "10.255.0.1"
43+
GRE_R3 = "10.255.0.2"
44+
GRE_NET = "10.255.0.0/30"
45+
46+
# Prefix lengths
47+
PREFIX_24 = 24
48+
PREFIX_30 = 30
49+
50+
# Test TTL value
51+
TEST_TTL = 3
52+
53+
54+
with infamy.Test() as test:
55+
with test.step("Set up topology and attach to target DUTs"):
56+
env = infamy.Env()
57+
r1 = env.attach("R1", "mgmt")
58+
r2 = env.attach("R2", "mgmt")
59+
r3 = env.attach("R3", "mgmt")
60+
61+
with test.step("Configure R1 with GRE tunnel to R3"):
62+
# R1: Entry point, west facing PC, east facing R2, GRE tunnel to R3
63+
r1.put_config_dicts({"ietf-interfaces": {
64+
"interfaces": {
65+
"interface": [{
66+
"name": r1["west"],
67+
"ipv4": {
68+
"address": [{
69+
"ip": PC_WEST_R1,
70+
"prefix-length": PREFIX_24
71+
}],
72+
"forwarding": True
73+
}
74+
}, {
75+
"name": r1["east"],
76+
"ipv4": {
77+
"address": [{
78+
"ip": R1_R2_R1,
79+
"prefix-length": PREFIX_24
80+
}],
81+
"forwarding": True
82+
}
83+
}, {
84+
"name": "gre0",
85+
"type": "infix-if-type:gre",
86+
"ipv4": {
87+
"address": [{
88+
"ip": GRE_R1,
89+
"prefix-length": PREFIX_30
90+
}],
91+
"forwarding": True
92+
},
93+
"gre": {
94+
"local": R1_R2_R1,
95+
"remote": R2_R3_R3, # R3's west IP
96+
# ttl defaults to 64 in YANG model (the fix!)
97+
}
98+
}]
99+
}
100+
}, "ietf-routing": {
101+
"routing": {
102+
"control-plane-protocols": {
103+
"control-plane-protocol": [{
104+
"type": "infix-routing:static",
105+
"name": "default",
106+
"static-routes": {
107+
"ipv4": {
108+
"route": [{
109+
"destination-prefix": R2_R3_NET,
110+
"next-hop": {
111+
"next-hop-address": R1_R2_R2
112+
}
113+
}, {
114+
"destination-prefix": PC_EAST_NET,
115+
"next-hop": {
116+
"next-hop-address": GRE_R3
117+
}
118+
}]
119+
}
120+
}
121+
}]
122+
}
123+
}
124+
}})
125+
126+
with test.step("Configure R2 as intermediate router (underlay forwarding)"):
127+
# R2: Intermediate router, just forwards packets between west and east
128+
r2.put_config_dicts({"ietf-interfaces": {
129+
"interfaces": {
130+
"interface": [{
131+
"name": r2["west"],
132+
"ipv4": {
133+
"address": [{
134+
"ip": R1_R2_R2,
135+
"prefix-length": PREFIX_24
136+
}],
137+
"forwarding": True
138+
}
139+
}, {
140+
"name": r2["east"],
141+
"ipv4": {
142+
"address": [{
143+
"ip": R2_R3_R2,
144+
"prefix-length": PREFIX_24
145+
}],
146+
"forwarding": True
147+
}
148+
}]
149+
}
150+
}})
151+
152+
with test.step("Configure R3 with GRE tunnel to R1"):
153+
# R3: Exit point, west facing R2, east facing PC, GRE tunnel to R1
154+
r3.put_config_dicts({"ietf-interfaces": {
155+
"interfaces": {
156+
"interface": [{
157+
"name": r3["west"],
158+
"ipv4": {
159+
"address": [{
160+
"ip": R2_R3_R3,
161+
"prefix-length": PREFIX_24
162+
}],
163+
"forwarding": True
164+
}
165+
}, {
166+
"name": r3["east"],
167+
"ipv4": {
168+
"address": [{
169+
"ip": PC_EAST_R3,
170+
"prefix-length": PREFIX_24
171+
}],
172+
"forwarding": True
173+
}
174+
}, {
175+
"name": "gre0",
176+
"type": "infix-if-type:gre",
177+
"ipv4": {
178+
"address": [{
179+
"ip": GRE_R3,
180+
"prefix-length": PREFIX_30
181+
}],
182+
"forwarding": True
183+
},
184+
"gre": {
185+
"local": R2_R3_R3,
186+
"remote": R1_R2_R1, # R1's east IP
187+
}
188+
}]
189+
}
190+
}, "ietf-routing": {
191+
"routing": {
192+
"control-plane-protocols": {
193+
"control-plane-protocol": [{
194+
"type": "infix-routing:static",
195+
"name": "default",
196+
"static-routes": {
197+
"ipv4": {
198+
"route": [{
199+
"destination-prefix": R1_R2_NET,
200+
"next-hop": {
201+
"next-hop-address": R2_R3_R2
202+
}
203+
}, {
204+
"destination-prefix": PC_WEST_NET,
205+
"next-hop": {
206+
"next-hop-address": GRE_R1
207+
}
208+
}]
209+
}
210+
}
211+
}]
212+
}
213+
}
214+
}})
215+
216+
with test.step("Send ping from PC:west to PC:east with low TTL"):
217+
_, pc_east = env.ltop.xlate("PC", "east")
218+
219+
with infamy.IsolatedMacVlan(pc_east) as east_ns:
220+
east_ns.addip(PC_EAST_HOST)
221+
east_ns.addroute("default", PC_EAST_R3)
222+
223+
pcap = east_ns.pcap("icmp")
224+
with pcap:
225+
_, pc_west = env.ltop.xlate("PC", "west")
226+
with infamy.IsolatedMacVlan(pc_west) as west_ns:
227+
west_ns.addip(PC_WEST_HOST)
228+
west_ns.addroute("default", PC_WEST_R1)
229+
230+
# Send 3 pings with TTL=3, TTL is decremented before each
231+
# router hop. So at PC:west (TTL=3) -> R1 routed to GRE
232+
# tunnel (TTL=2) -> frame egresses tunnel -> R3 where it
233+
# is routed to PC:east (TTL=1).
234+
#
235+
# If outer TTL was inherited (TTL=2), packet would be
236+
# dropped at R3.
237+
west_ns.runsh(f"ping -c 3 -t {TEST_TTL} {PC_EAST_HOST}")
238+
239+
with test.step("Verify packets arrived at PC:east"):
240+
packets = pcap.tcpdump()
241+
print("Captured packets on PC:east:")
242+
print(packets)
243+
244+
pings = [line for line in packets.splitlines()
245+
if f"{PC_WEST_HOST} > {PC_EAST_HOST}: ICMP echo request" in line]
246+
247+
assert len(pings) >= 1, f"Expected at least 1 ping, got {len(pings)}."
248+
249+
test.succeed()
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)