Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 135 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,137 @@
# evpn-connector

The main task of this daemon is to interact with the gobgp and ovs to provide a connection using the EVPN protocol
[![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)

A service for automating the management distributed switch or router based on EVPN standards


## Key Features

* **EVPN Standards Support:**
* RFC 7432: BGP MPLS-Based Ethernet VPN
* RFC 8365: Network Virtualization Overlay
* RFC 7988: Ingress Replication Tunnels in Multicast VPN
* RFC 9135: Integrated Routing and Bridging in Ethernet VPN
* RFC 9136: IP Prefix Advertisement in Ethernet VPN
* **Hardware Integration:** Interaction with the hardware switch and routers via EVPN
* **GoBGP Integration:** Interaction with the gobgpd daemon to pull or push EVPN annouces
* **OpenvSwitch Management:** Direct management of bridges and flows within the OpenvSwitch system
* **Reconciliation Loop Design:** Built on a closed-loop control architecture to ensure fault tolerance and reduce the impact of external factors
* **Configurability:** Flexible setup via INI daemon configuration files and JSON clients configuration files

## Documentation
The details of the EVPN's operation and evpn-connector daemon workflow are described in the [presentation](https://vkvideo.ru/video-164978780_456239752) (in Russian only).

## Installation & Quick Start

### Prerequisites

* **Python 3.8

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Python version requirement is listed as Python 3.8, which is incomplete and could be misinterpreted as only version 3.8 is supported. The badge at the top of the file says python-3.8+, which is more accurate. Please update this line to be consistent and clear.

Suggested change
* **Python 3.8
* **Python 3.8+**

* **System Dependencies:**
* `gobgp` (for interaction via BGP within the EVPN control plane)
* `openvswitch-switch` (for interaction via VXLAN within the EVPN data plane)
* **Permissions:** Requires `root` privileges for interaction with OpenvSwitch


1. **Install dependencies:**

On Ubuntu/Debian:
```bash
sudo apt update
sudo apt install openvswitch-switch openvswitch-common gobgpd
```
On CentOS/RHEL:
```bash
sudo yum install gobgp openvswitch
# or for newer versions:
# sudo dnf install gobgp openvswitch
```
2. **(Optional) Install latest GoBGP from binary release:**
```bash
wget https://github.com/osrg/gobgp/releases/download/v3.34.0/gobgp_3.34.0_linux_amd64.tar.gz
mkdir gobgp
tar -xf gobgp_3.34.0_linux_amd64.tar.gz -C gobgp/
```
3. **(Recommended) Create a virtual environment:**
Recommended installation gobgp versions >= v3.34
```bash
python3 -m venv evpn
source evpn/bin/activate
pip install --upgrade pip setuptools
```
4. **Install evpn-connector:**
```bash
pip install evpn-connector
```
### Configuration

Before the first run, you need to create a configuration file.

1. **GoBGP config:** Copy the example configuration file and adapt it to your environment.
```bash
cp etc/gobgpd/gobgp.conf.sample /etc/gobgpd/gobgp.conf
```
It is necessary to configure gobgp so that all nodes can exchange announces with afi-safi **"l2evpn-evpn"**

2. **evpn-connector config:** Copy the example configuration file and adapt it to your environment.
```bash
mkdir /etc/evpn_connector/
cp etc/evpn_connector/logging.yaml /etc/evpn_connector/logging.yaml
cp etc/evpn_connector/evpn_connector.cfg.sample /etc/evpn_connector/evpn_connector.conf
```
3. **Edit `/etc/evpn_connector/evpn_connector.conf`:** Specify the necessary parameters:
* `[gobgp] section`: Settings for connecting to the GoBGP daemon
* `[gobgp] source_ip`: Source IP address for all VXLAN packets
* `[ovs] section`: Settings for OpenvSwitch dataplane parameters
* `[ovs] switch_name`: Name of switch created in OvS
* `[daemon] section`: Settings for evpn_connector daemon
* `[daemon] configs_dir`: Path to client configs

4. **Client configs:** Create clients configs. Example config:
* For L2 connectivity
```json
{
"cfg_type": "l2", // Config type for L2 connectivity use "l2"
"mac": "36:e7:a5:7e:0c:81", // MAC address of client
"ip": "10.0.0.1", // IP address of client
"vni": 10, // VXLAN segment identifier
"ofport": 1000, // OpenFlow port number in current OpenvSwitch switch
"type": "flat", // OpenvSwitch port type. May be "flat" and "vlan"
"tag": 0, // OpenvSwitch port segment identifier. Ignored on "flat"
"imp_rt": ["65000:10"], // List of imported BGP Route Targets
"exp_rt": ["65000:10"] // List of exported BGP Route Targets
}
Comment on lines +94 to +104

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The JSON example for L2 connectivity contains // style comments. This makes it invalid JSON, and it will be rejected by standard JSON parsers. Please remove the comments to ensure the example is valid and can be copied directly by users. You can add explanations about the fields in the text surrounding the code block.

Suggested change
{
"cfg_type": "l2", // Config type for L2 connectivity use "l2"
"mac": "36:e7:a5:7e:0c:81", // MAC address of client
"ip": "10.0.0.1", // IP address of client
"vni": 10, // VXLAN segment identifier
"ofport": 1000, // OpenFlow port number in current OpenvSwitch switch
"type": "flat", // OpenvSwitch port type. May be "flat" and "vlan"
"tag": 0, // OpenvSwitch port segment identifier. Ignored on "flat"
"imp_rt": ["65000:10"], // List of imported BGP Route Targets
"exp_rt": ["65000:10"] // List of exported BGP Route Targets
}
{
"cfg_type": "l2",
"mac": "36:e7:a5:7e:0c:81",
"ip": "10.0.0.1",
"vni": 10,
"ofport": 1000,
"type": "flat",
"tag": 0,
"imp_rt": ["65000:10"],
"exp_rt": ["65000:10"]
}

```
* For L3 connectivity
```json
{
"cfg_type": "l3", // Config type for L3 connectivity use "l3"
"mac": "36:e7:a5:7e:0c:81", // MAC address of client
"routes": ["10.0.0.1/32"], // List of CIDR prefixes for this client
"vni": 10, // VXLAN segment identifier
"ofport": 1000, // OpenFlow port number in current OpenvSwitch switch
"type": "flat", // OpenvSwitch port type. May be "flat" and "vlan"
"tag": 0, // OpenvSwitch port segment identifier. Ignored on "flat"
"imp_rt": ["65000:10"], // List of imported BGP Route Targets
"exp_rt": ["65000:10"] // List of exported BGP Route Targets
}
Comment on lines +108 to +118

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The JSON example for L3 connectivity contains // style comments. This makes it invalid JSON, and it will be rejected by standard JSON parsers. Please remove the comments to ensure the example is valid and can be copied directly by users. You can add explanations about the fields in the text surrounding the code block.

Suggested change
{
"cfg_type": "l3", // Config type for L3 connectivity use "l3"
"mac": "36:e7:a5:7e:0c:81", // MAC address of client
"routes": ["10.0.0.1/32"], // List of CIDR prefixes for this client
"vni": 10, // VXLAN segment identifier
"ofport": 1000, // OpenFlow port number in current OpenvSwitch switch
"type": "flat", // OpenvSwitch port type. May be "flat" and "vlan"
"tag": 0, // OpenvSwitch port segment identifier. Ignored on "flat"
"imp_rt": ["65000:10"], // List of imported BGP Route Targets
"exp_rt": ["65000:10"] // List of exported BGP Route Targets
}
{
"cfg_type": "l3",
"mac": "36:e7:a5:7e:0c:81",
"routes": ["10.0.0.1/32"],
"vni": 10,
"ofport": 1000,
"type": "flat",
"tag": 0,
"imp_rt": ["65000:10"],
"exp_rt": ["65000:10"]
}

```
Need create json config for all clients in **configs_dir**
```bash
mkdir /var/lib/evpn_connector/client_configs/
vim /var/lib/evpn_connector/client_configs/vm1.json
```
### Running the Service

Start the service by specifying the path to your configuration file:

1. **Run GoBGP:**
```bash
sudo gobgpd -f /etc/gobgpd/gobgp.conf
```
2. **Run evpn-connector**
```bash
source evpn/bin/activate
evpn-connector --config-file ~/evpn/config/evpn_connector.cfg --daemon-configs_dir "/var/lib/evpn_connector/client_configs/"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The example command to run the service is confusing. The path to the config file --config-file ~/evpn/config/evpn_connector.cfg is used, but the previous steps do not show how this file is created. Step 2 under "Configuration" instructs to copy the sample config to /etc/evpn_connector/evpn_connector.conf. The command should probably use that path for consistency and to avoid user confusion.

Suggested change
evpn-connector --config-file ~/evpn/config/evpn_connector.cfg --daemon-configs_dir "/var/lib/evpn_connector/client_configs/"
evpn-connector --config-file /etc/evpn_connector/evpn_connector.conf --daemon-configs_dir "/var/lib/evpn_connector/client_configs/"

```
62 changes: 62 additions & 0 deletions docs/demo/CONFIGURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
Configuring demo env:

1. Download Debian 12 nocloud image:
```bash
wget https://cdimage.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-amd64.qcow2
```
2. Run virtual machines via virt-manager or qemu:
```
Hostname: compute1, ip_address: 10.10.10.1
Hostname: compute2, ip_address: 10.10.10.2
```
3. Install packages:
```bash
sudo apt update
sudo apt install build-essential zlib1g-dev libffi-dev libssl-dev libbz2-dev libreadline-dev libsqlite3-dev liblzma-dev libncurses-dev screen tcpdump openvswitch-switch openvswitch-common jq curl git wget
```
4. Install pyenv:
```bash
curl -fsSL https://pyenv.run | bash
```
5. Add to end of `.bashrc` and reenter to bash:
```bash
export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init - bash)"
export PATH="$HOME/gobgp:$PATH"
bold=$(tput bold)
red=$(tput setaf 1)
green=$(tput setaf 2)
blue=$(tput setaf 4)
reset=$(tput sgr0)
PS1='\[$red\]\h\[$reset\]:\[$bold\]\w\[$reset\]\$ '
```
6. Install python3.8 for evpn-connector:
```bash
pyenv install 3.8.20
pyenv global system 3.8

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The command pyenv global system 3.8 is invalid. pyenv global accepts only one version name. To set the Python version installed in the previous step (3.8.20) as the global default for the current user, the command should be pyenv global 3.8.20.

Suggested change
pyenv global system 3.8
pyenv global 3.8.20

```
7. Create venv for evpn-connector and activate:
```bash
python3.8 -m venv ~/evpn/
source evpn/bin/activate
```
8. Install evpn-connector from pip
```bash
pip install --upgrade pip setuptools
pip install evpn-connector
```
9. Download and install GoBGP from binary:
```bash
wget https://github.com/osrg/gobgp/releases/download/v3.34.0/gobgp_3.34.0_linux_amd64.tar.gz
mkdir gobgp
tar -xf gobgp_3.34.0_linux_amd64.tar.gz -C gobgp/
rm gobgp_3.34.0_linux_amd64.tar.gz
```
10. Clone evpn-connector git repo:
```bash
git clone https://github.com/vktechdev/evpn_connector
```
11. Copy configs for all daemons from repo (for each compute host separately):
```bash
cp -r evpn_connector/docs/source/demo/compute1/* ~/

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The path evpn_connector/docs/source/demo/compute1/* seems incorrect. Based on the file structure in this pull request, the demo files are located in docs/demo/, not docs/source/demo/. The source directory component should be removed for the command to work correctly.

Suggested change
cp -r evpn_connector/docs/source/demo/compute1/* ~/
cp -r evpn_connector/docs/demo/compute1/* ~/

39 changes: 39 additions & 0 deletions docs/demo/compute1/evpn_connector.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[DEFAULT]
verbose = True
debug = True

[logging]
config = logging.yaml

[agent]
sync_period = 10

[victoria_metrics]
enabled = False
host = "127.0.0.1"
port = 8126
prefix = "apps.evpn_connector.dev"
obsender_host = "sprut.compute.i"

[sentry]
enabled = False
dsn = "http://01b92236b6514577812280415d631354@192.168.220.220:9000/2"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The Sentry DSN is hardcoded with what appears to be a real or example credential. It is a security risk to commit secrets to version control, even in demo files. Please replace it with a placeholder and add a comment instructing the user to replace it with their actual DSN.

dsn = "http://<key>@<sentry_host>:<port>/<project_id>"

traces_sample_rate = 1.0
env = devenv

[gobgp]
gobgp_channel = "localhost:50051"
grpc_timeout_sec = 100
# Don't change this param without gobgp restart
source_ip = "10.10.10.1"
as_number = 1

[ovs]
switch_name = "evpn"
vxlan_udp_port = 4789
enable_sudo = True
ovs_vsctl_bin_path="/usr/bin/ovs-vsctl"
ovs_ofctl_bin_path="/usr/bin/ovs-ofctl"

[anycast]
anycast_status_file = /tmp/anycast_status_file
15 changes: 15 additions & 0 deletions docs/demo/compute1/gobgp.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[global.config]
as = 65100
router-id = "10.10.10.1"
local-address-list = [ "10.10.10.1" ]

[[neighbors]]
[neighbors.config]
peer-as = 65200
neighbor-address = "10.10.10.2"
[neighbors.ebgp-multihop.config]
enabled = true
multihop-ttl = 7
[[neighbors.afi-safis]]
[neighbors.afi-safis.config]
afi-safi-name = "l2vpn-evpn"
32 changes: 32 additions & 0 deletions docs/demo/compute1/logging.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
version: 1
formatters:
aardvark:
format: '%(asctime)15s.%(msecs)03d %(processName)s pid:%(process)d tid:%(thread)d %(levelname)s %(name)s:%(lineno)d %(message)s'
datefmt: '%Y-%m-%dT%H:%M:%S'
# this one should only be used by handlers that support topology_id attribute
# and since it may be used by multiple modules, actual_module attribute
# should also be added (module will give a short name, pathname is too long)

handlers:
console:
class : logging.StreamHandler
formatter: aardvark
stream : ext://sys.stdout

loggers:
# by default all existing loggers are disabled upon the application
# of this config. To re-enable a logger and it's childer just add it
# to the loggers section with any even empty fields.
obsender:
handlers: [console]
level: WARNING
propagate: False

loopster:
level: WARNING

evpn_connector: {}

root:
handlers: [console]
level: INFO
11 changes: 11 additions & 0 deletions docs/demo/compute1/vm_conf/vm1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"cfg_type": "l3",
"mac": "36:e7:a5:7e:0c:81",
"routes": ["172.16.0.1/32"],
"vni": 10,
"ofport": 1001,
"type": "flat",
"tag": 0,
"imp_rt": ["65000:10"],
"exp_rt": ["65000:10"]
}
11 changes: 11 additions & 0 deletions docs/demo/compute1/vm_conf/vm2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"cfg_type": "l3",
"mac": "36:e7:a5:7e:0c:82",
"routes": ["172.16.0.2/32"],
"vni": 10,
"ofport": 1002,
"type": "flat",
"tag": 0,
"imp_rt": ["65000:10"],
"exp_rt": ["65000:10"]
}
39 changes: 39 additions & 0 deletions docs/demo/compute2/evpn_connector.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[DEFAULT]
verbose = True
debug = True

[logging]
config = logging.yaml

[agent]
sync_period = 10

[victoria_metrics]
enabled = False
host = "127.0.0.1"
port = 8126
prefix = "apps.evpn_connector.dev"
obsender_host = "sprut.compute.i"

[sentry]
enabled = False
dsn = "http://01b92236b6514577812280415d631354@192.168.220.220:9000/2"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The Sentry DSN is hardcoded with what appears to be a real or example credential. It is a security risk to commit secrets to version control, even in demo files. Please replace it with a placeholder and add a comment instructing the user to replace it with their actual DSN.

dsn = "http://<key>@<sentry_host>:<port>/<project_id>"

traces_sample_rate = 1.0
env = devenv

[gobgp]
gobgp_channel = "localhost:50051"
grpc_timeout_sec = 100
# Don't change this param without gobgp restart
source_ip = "10.10.10.2"
as_number = 1

[ovs]
switch_name = "evpn"
vxlan_udp_port = 4789
enable_sudo = True
ovs_vsctl_bin_path="/usr/bin/ovs-vsctl"
ovs_ofctl_bin_path="/usr/bin/ovs-ofctl"

[anycast]
anycast_status_file = /tmp/anycast_status_file
15 changes: 15 additions & 0 deletions docs/demo/compute2/gobgp.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[global.config]
as = 65200
router-id = "10.10.10.2"
local-address-list = [ "10.10.10.2" ]

[[neighbors]]
[neighbors.config]
peer-as = 65100
neighbor-address = "10.10.10.1"
[neighbors.ebgp-multihop.config]
enabled = true
multihop-ttl = 7
[[neighbors.afi-safis]]
[neighbors.afi-safis.config]
afi-safi-name = "l2vpn-evpn"
Loading