Tutorial: Automate GTP-U tunnel creation using Go

Guillaume Belanger
2 min readNov 26, 2023

In the last tutorial, we created a GTP-U tunnel on a Ubuntu virtual machine using Open Vswitch. Here we will take the next step and write a Go program that programatically creates such tunnels.

Pre-requisites

  • a Ubuntu 22.04 machine

Install OVS:

sudo apt update
sudo apt install openvswitch-switch

Create a folder named tunnel-go :

mkdit tunnel-go
cd tunnel-go

Create a new go module:

go mod init tunnel-go

Create a main.go file with an empty main function:

package main

func main() {}

Import Digital Ocean’s OVS Go library as well as the log standard library:

package main

import (
"log"

"github.com/digitalocean/go-openvswitch/ovs"
)

func main() {
}

Use the OVS library to create a client:

ovsClient := ovs.New(
ovs.Sudo(),
)

With this client, create a bridge named br0 :

if err := ovsClient.VSwitch.AddBridge(ovsBridge); err != nil {
log.Fatalf("failed to add bridge: %v", err)
}

Use the netdev datapath:

if err := ovsClient.VSwitch.Set.Bridge(ovsBridge, ovs.BridgeOptions{
DataPathType: ovs.DataPathTypeNetDev,
}); err != nil {
log.Fatalf("failed to set bridge: %v", err)
}

Create the enp6s0 and enp7s0 ports:

if err := ovsClient.VSwitch.AddPort(ovsBridge, ranPort); err != nil {
log.Fatalf("failed to add port: %v", err)
}

if err := ovsClient.VSwitch.AddPort(ovsBridge, dataPort); err != nil {
log.Fatalf("failed to add port: %v", err)
}

Create the GTP-U tunnel:

if err := ovsClient.VSwitch.AddPort(ovsBridge, gtpuPort); err != nil {
log.Fatalf("failed to add port: %v", err)
}

if err := ovsClient.VSwitch.Set.Interface(gtpuPort, ovs.InterfaceOptions{
Type: "gtpu",
RemoteIP: gtpuRemoteIP,
Key: gtpuTEID,
}); err != nil {
log.Fatalf("failed to set interface: %v", err)
}

There you go, you have created a program that can create GTP-U tunnels! 🎊

Here’s the complete code:

package main

import (
"log"

"github.com/digitalocean/go-openvswitch/ovs"
)

func main() {
CreateGTPUTunnel()
}

func CreateGTPUTunnel() {
var ovsBridge string = "br0"
var ranPort string = "ran"
var dataPort string = "data"
var gtpuPort string = "gtpu0"
var gtpuRemoteIP string = "172.31.1.1"
var gtpuTEID string = "5000"

ovsClient := ovs.New(
ovs.Sudo(),
)

// sudo ovs-vsctl add-br br0
if err := ovsClient.VSwitch.AddBridge(ovsBridge); err != nil {
log.Fatalf("failed to add bridge: %v", err)
}

// sudo ovs-vsctl set bridge br0 datapath_type=netdev
if err := ovsClient.VSwitch.Set.Bridge(ovsBridge, ovs.BridgeOptions{
DataPathType: ovs.DataPathTypeNetDev,
}); err != nil {
log.Fatalf("failed to set bridge: %v", err)
}

// sudo ovs-vsctl add-port br0 enp6s0
if err := ovsClient.VSwitch.AddPort(ovsBridge, ranPort); err != nil {
log.Fatalf("failed to add port: %v", err)
}

// sudo ovs-vsctl add-port br0 enp7s0
if err := ovsClient.VSwitch.AddPort(ovsBridge, dataPort); err != nil {
log.Fatalf("failed to add port: %v", err)
}

// sudo ovs-vsctl add-port br0 gtpu0
if err := ovsClient.VSwitch.AddPort(ovsBridge, gtpuPort); err != nil {
log.Fatalf("failed to add port: %v", err)
}

// sudo ovs-vsctl set int gtpu0 type=gtpu options:key=5000 options:remote_ip=172.31.1.1
if err := ovsClient.VSwitch.Set.Interface(gtpuPort, ovs.InterfaceOptions{
Type: "gtpu",
RemoteIP: gtpuRemoteIP,
Key: gtpuTEID,
}); err != nil {
log.Fatalf("failed to set interface: %v", err)
}

}

--

--

Guillaume Belanger

Guillaume is a software developer from Montreal who writes about bip bop stuff.