Configuring GRE Tunnels

This document describes the device data model parameters one should consider when creating a GRE tunnel configuration template. GRE tunnels are implemented as a plugin (tunnel.gre) rather than a core module; the plugin uses shared utility functions from the netsim/extra/tunnel package.

Overview

The tunnel.gre plugin supports point-to-point GRE tunnels with IPv4 or IPv6 transport encapsulation.

Notes:

  • The configuration template (in Jinja2 format) should be stored in netsim/extra/tunnel/gre/<nos>.j2 with nos being the value of netlab_device_type or ansible_network_os variable (see Using Your Devices with Ansible Playbooks for more details).

  • Some devices require an initial configuration template to create the tunnel interface before other modules (e.g., routing) can reference it. Those templates are stored as <nos>.initial.j2 alongside the main template and are executed during the initial configuration phase.

  • Most of the data model attributes are optional. Use if sth is defined, sth|default(value) or if 'sth' in intf in your Jinja2 templates.

  • To use GRE tunnels, add plugin: [ tunnel.gre ] to the lab topology.

Data Model

GRE tunnel attributes are defined in netsim/extra/tunnel/gre/defaults.yml within the attributes.link.tunnel namespace. Every link with tunnel.mode: gre becomes a tunnel link.

The tunnel.source Dictionary

When tunnel.source is specified, it can contain:

  • type (string) – source interface type. Currently, only loopback is supported as a valid value.

  • link.name (string) – select the source interface by its link name.

  • link.role (string) – select the source interface by its link role.

  • ifindex (integer) – specify the source interface ifindex directly (rarely needed).

Source Interface Selection

netlab automatically selects the best underlay source interface by iterating over the node’s interfaces and applying these filters (in order):

  1. If tunnel.source.type is not set, interfaces of type loopback or tunnel are excluded.

  2. If tunnel.vrf is not set, VRF interfaces are excluded. Otherwise, only interfaces in tunnel.vrf VRF are considered.

  3. If tunnel.source.link.name is set, the interface name must match.

  4. If tunnel.source.link.role is set, the interface or its link role must match.

  5. If tunnel.af is set, only interfaces with a numbered address in that address family are considered.

  6. Otherwise, only interfaces with at least one numbered IPv4 or IPv6 address are considered.

The first matching interface is used as the tunnel source.

Transformed Attributes (set by the plugin)

After the post_transform phase, each tunnel interface receives:

  • tunnel._source.ifname (string) – name of the selected source interface.

  • tunnel._source.mtu (integer) – MTU of the source interface (defaults to node MTU, usually 1500).

  • tunnel._source.ipv4 / tunnel._source.ipv6 (string) – IP address of the source interface for the tunnel transport AF.

  • tunnel._destination.ipv4 / tunnel._destination.ipv6 (string) – IP address of the remote tunnel endpoint (copied from the neighbor’s tunnel._source).

The interface also gets:

  • type set to tunnel during pre_transform.

  • virtual_interface set to True during link transformation.

  • An interface description enhanced with [GRE tunnel] during post_transform.

Device Features

Device support for GRE tunnels is declared in device YAML files under features.tunnel.gre. The feature value is a list of supported capabilities; each device advertises which transport options it supports:

features:
  tunnel:
    gre: [ vrf, ipv4, ipv6 ]     # Supports all options

Common capability combinations:

Device

GRE Support

FRR

[ vrf, ipv4, ipv6 ]

Cisco IOS/IOS-XE

[ vrf, ipv4, ipv6 ]

Juniper vJunos Router

[ vrf, ipv4 ]

OpenBSD

[ ipv4 ]

The check_feature function in netsim/extra/tunnel/__init__.py validates device support by checking for the presence of entries like ipv4, ipv6, or vrf in the device’s features.tunnel.gre list. If a device does not support the requested feature (e.g., GRE over IPv6 when only [ipv4] is listed), an error is reported.

Tunnel Interface Naming

Tunnel interfaces get ifindex values starting at 20000 (the tunnel type is the second entry in VIRTUAL_INTERFACE_TYPES, at offset index 1, so (1 + 1) * 10000 = 20000).

Each device defines a tunnel_interface_name template that controls the tunnel interface name format, for example:

Device

Interface Name Template

FRR, VyOS, Cumulus

tun{ifindex}

Cisco IOS, Arista EOS

Tunnel{ifindex}

RouterOS 7

tunnel{ifindex}

OpenBSD

gre{ifindex}

Juniper vJunos Router

gr-0/0/10.{ifindex}

Juniper vSRX

gr-0/0/0.{ifindex}

Aruba CX

tunnel {ifindex}

Containerlab Kernel Modules

When GRE tunnels are used with the clab provider, the plugin declares the required kernel modules in defaults.yml:

providers:
  clab:
    kmods:
      tunnel@gre: [ ip_gre, ip6_gre ]

The @ serves as a dot replacement in module names for kmod lookup. These modules must be loaded on the container host to support GRE tunneling.

Transformation Process

The plugin hooks into the topology transformation pipeline via two hooks defined in netsim/extra/tunnel/gre/__init__.py:

pre_transform

  1. _tunnel.set_tunnel_type(topology) – marks all links with tunnel.mode: gre as type: tunnel.

  2. Validates P2P topology – every GRE tunnel link must have exactly two nodes. An error is reported otherwise.

post_transform

  1. _p2p.feature_check(topology, ...) – iterates over all nodes, finds those with GRE tunnel interfaces, and validates device feature support (basic GRE, transport AF, and VRF). Calls api.node_config(ndata, 'tunnel.gre') for each node that passes all checks.

  2. _p2p.tunnel_source(topology, node_iflist, ...) – for each node with tunnel interfaces, resolves the source interface using _tunnel.get_tunnel_source() and populates tunnel._source via _tunnel.set_tunnel_source(). Sets tunnel.af to ipv4 as default when not specified.

  3. _p2p.tunnel_destination(topology, node_iflist, ...) – for each tunnel interface, copies the neighbor’s tunnel._source to the local tunnel._destination.

If any errors are detected during source resolution, the destination resolution step is skipped.

Configuration Templates

FRR / Linux

Creates GRE or ip6gre tunnels using the ip tunnel command. Selects gre or ip6gre mode based on the tunnel.af attribute:

{%- for intf in netlab_interfaces if intf.tunnel.mode|default('') in ['gre'] %}
{%   set af = intf.tunnel.af|default('ipv4') %}
{%   set gre_mode = 'ip6gre' if af == 'ipv6' else 'gre' %}
ip tunnel add {{ intf.ifname }} mode {{ gre_mode }} \
  local {{ intf.tunnel._source[af] }} remote {{ intf.tunnel._destination[af] }} \
  dev {{ intf.tunnel._source.ifname }} ttl 255
{{ linux_intf_sysctl(intf,role) }}
{% endfor %}

Cisco IOS / IOS-XE

Configures the tunnel mode (gre ip or gre ipv6), optional transport VRF, source interface, and destination address:

{% for intf in netlab_interfaces if intf.tunnel.mode|default('') in ['gre'] %}
interface {{ intf.ifname }}
  tunnel mode {{ intf.tunnel.mode }} {{ intf.tunnel.af.replace('ipv4','ip') }}
{%   if intf.tunnel.vrf is defined %}
  tunnel vrf {{ intf.tunnel.vrf }}
{%   endif %}
  tunnel source {{ intf.tunnel._source.ifname }}
  tunnel destination {{ intf.tunnel._destination[intf.tunnel.af] }}
{% endfor %}

The tunnel.af.replace('ipv4','ip') expression maps ipv4 to ip and leaves ipv6 as-is, matching IOS syntax.

VyOS

Requires two templates:

Initial (vyos.initial.j2) – creates the tunnel interface with encapsulation, source-address, and remote address:

{% for intf in netlab_interfaces if intf.tunnel.mode|default('') in ['gre'] %}
set interfaces tunnel {{ intf.ifname }} encapsulation 'gre'
{%   if intf.tunnel.af == 'ipv6' %}
set interfaces tunnel {{ intf.ifname }} encapsulation 'ip6gre'
{%   endif %}
set interfaces tunnel {{ intf.ifname }} source-address {{ intf.tunnel._source[intf.tunnel.af] }}
set interfaces tunnel {{ intf.ifname }} remote {{ intf.tunnel._destination[intf.tunnel.af] }}
{% endfor %}

Main (vyos.j2) – sets the source interface, which also controls outer VRF selection:

{% for intf in netlab_interfaces if intf.tunnel.mode|default('') in ['gre'] %}
set interfaces tunnel {{ intf.ifname }} source-interface {{ intf.tunnel._source.ifname }}
{% endfor %}

The netsim/extra/tunnel/gre/vyos.initial.j2 template is included in the initial configuration template via the extra_initial macro:

{% from '_extra_initial.j2' import extra_initial with context %}
{#
    Include interface-related parts of module/plugin configuration, including
    VLANs, VRFs, and tunnels
#}
{{ extra_initial() }}

Test Cases

Integration tests in tests/integration/tunnel/:

  • 01-gre.yml – GRE tunnels over IPv4 and IPv6 transport with OSPFv2/OSPFv3 running over the tunnels. Uses FRR containers to verify OSPF adjacencies, prefix propagation, and end-to-end ping.

  • 02-gre-vrf.yml – GRE tunnels with VRF transport underlay, verifying that GRE works over a VRF-based transport network.

Coverage tests in tests/coverage/:

  • input/gre-source.yml – tests automatic tunnel source interface selection with tunnel.source.link.name and tunnel.source.link.role options.

  • errors/gre-support.yml – tests error conditions: unsupported device, unsupported transport AF, unsupported VRF, and missing tunnel source.