Skip to content

Module TiNet

sat edited this page Aug 12, 2026 · 12 revisions

Module: TiNET

This page describes the TiNET module for dot2net.

What is TiNET?

TiNET (Tiny Network) is a lightweight container-based network emulation tool. It uses Docker containers connected via Linux network namespaces and veth pairs to create virtual network topologies.

What dot2net Generates

The TiNET module generates spec.yaml, the specification file for TiNET:

nodes:
  - name: r1
    image: quay.io/frrouting/frr:8.5.4
    interfaces: [{name: eth0, type: direct, args: r2#eth0}]
    mounts: [$PWD/r1/etc/frr/frr.conf:/etc/frr/frr.conf, $PWD/r1/etc/frr/daemons:/etc/frr/daemons]

node_configs:
  - name: r1
    cmds:
      - cmd: vtysh -b

Generated Sections

Section Source Description
nodes DOT nodes Node definitions with image, interfaces, mounts
switches deploy: platform nodes Shared media TiNET realizes as bridges. The section is left out entirely when a scenario has none
node_configs startup block Commands to run on each node

Automatic Features

  • interfaces: Automatically generated from DOT edge definitions
  • mounts: Generated from FileDefinitions that have path set. They start with $PWD/: TiNET passes the string to docker run -v, which refuses a relative source, and the output of tinet up is meant to be piped to a shell that expands it — against the directory the lab is brought up from, which is where the spec file sits
  • cmds: Generated from user-defined startup config block

One spec file per machine

When a scenario declares worker groups, the spec file becomes group-scoped: each machine gets its own, holding its nodes and the links it can wire itself. A link that leaves a machine appears in neither, since no spec file can make it — see Placing nodes on machines. Mount paths become relative to the machine's directory.

Entry script

module_config:
  tinet:
    generate_scripts: true

writes tinet.sh, which carries the parts that are easy to get wrong: TiNET brings a lab up in two steps (up creates the nodes, conf applies node_configs), its output is a shell script meant to be piped, and it prints one line that is not a command when an interface attaches to a switch — which the pipe has to drop or the shell stops there.

See Command Reference.

Required Parameters

Each non-virtual node must have this parameter defined:

Parameter Description Example
image Container image quay.io/frrouting/frr:8.5.0

Define this in your NodeClass:

nodeclass:
  - name: router
    values:
      image: quay.io/frrouting/frr:8.5.0

Optional: startup Config Block

The startup config block defines commands to run inside the container. This is used to generate the cmds section in node_configs.

nodeclass:
  - name: router
    config:
      - name: startup
        template:
          - "vtysh -b"
          - "ip addr add {{ .ip_loopback }}/32 dev lo"

Behavior:

  • If startup exists and is non-empty → node_configs entry is generated
  • If startup is missing or empty → no node_configs entry for that node

Optional: File Mounts

Files with path defined in FileDefinition are automatically mounted into containers:

file:
  - name: frr.conf
    path: /etc/frr/frr.conf    # This triggers mount generation
  - name: daemons
    path: /etc/frr/daemons

Generated mounts:

mounts:
  - { src: ./r1/etc/frr/frr.conf, dst: /etc/frr/frr.conf }
  - { src: ./r1/daemons, dst: /etc/frr/daemons }

Complete Example

input.yaml

name: ospf_lab
module:
  - tinet
  - frr

file:
  - name: frr.conf
    path: /etc/frr/frr.conf
  - name: daemons
    path: /etc/frr/daemons

layer:
  - name: ip
    default_connect: true
    policy:
      - name: p2p
        range: 10.0.0.0/16
        prefix: 30

nodeclass:
  - name: router
    values:
      image: quay.io/frrouting/frr:8.5.0
    config:
      - file: frr.conf
        template:
          - "hostname {{ .name }}"
          - "!"
          - "router ospf"
          - " router-id {{ .ip_loopback }}"
      - file: daemons
        sourcefile: ./daemons
      - name: startup
        template:
          - "vtysh -b"

input.dot

digraph {
  r1 [class="router"]
  r2 [class="router"]
  r1 -- r2
}

Generated spec.yaml

nodes:
  - name: r1
    image: quay.io/frrouting/frr:8.5.0
    interfaces:
      - { name: eth0, type: direct, args: r2#eth0 }
    sysctls:
      - sysctl: net.ipv4.ip_forward=1
    mounts:
      - { src: ./r1/etc/frr/frr.conf, dst: /etc/frr/frr.conf }
      - { src: ./r1/daemons, dst: /etc/frr/daemons }
  - name: r2
    image: quay.io/frrouting/frr:8.5.0
    interfaces:
      - { name: eth0, type: direct, args: r1#eth0 }
    sysctls:
      - sysctl: net.ipv4.ip_forward=1
    mounts:
      - { src: ./r2/frr.conf, dst: /etc/frr/frr.conf }
      - { src: ./r2/daemons, dst: /etc/frr/daemons }

switches: []

node_configs:
  - name: r1
    cmds:
      - cmd: vtysh -b
  - name: r2
    cmds:
      - cmd: vtysh -b

Running the Lab

After generating files with dot2net:

# Generate configuration files
dot2net build -c input.yaml input.dot

# Deploy the lab
sudo tinet up -c spec.yaml | sudo sh -x

# Execute commands on nodes
sudo tinet conf -c spec.yaml | sudo sh -x

# Destroy the lab
sudo tinet down -c spec.yaml | sudo sh -x

Comparison with Containerlab

Feature TiNET Containerlab
Complexity Simpler, lightweight More features
Node types Docker containers Multiple kinds (SR Linux, cEOS, etc.)
Execution Shell script output Direct deployment
Interface naming Configurable Fixed pattern

See Also

Clone this wiki locally