Skip to content

Commit 72e0ea4

Browse files
authored
Merge pull request #30 from open-traffic-generator/scapy
Dear snappi, please meet Scapy!
2 parents 4e21c8e + b592a15 commit 72e0ea4

File tree

12 files changed

+703
-19
lines changed

12 files changed

+703
-19
lines changed

.github/workflows/ci.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ on:
1616
- 'docker-compose/cpdp-frr/**'
1717
- '!docker-compose/cpdp-frr/**.md'
1818
- '!docker-compose/cpdp-frr/**.png'
19+
- 'clab/ixia-c-b2b/**'
20+
- '!clab/ixia-c-b2b/**.md'
21+
- '!clab/ixia-c-b2b/**.png'
1922
- 'clab/rtbh/**'
2023
- '!clab/rtbh/**.md'
2124
- '!clab/rtbh/**.png'
@@ -34,6 +37,9 @@ on:
3437
- 'docker-compose/cpdp-frr/**'
3538
- '!docker-compose/cpdp-frr/**.md'
3639
- '!docker-compose/cpdp-frr/**.png'
40+
- 'clab/ixia-c-b2b/**'
41+
- '!clab/ixia-c-b2b/**.md'
42+
- '!clab/ixia-c-b2b/**.png'
3743
- 'clab/rtbh/**'
3844
- '!clab/rtbh/**.md'
3945
- '!clab/rtbh/**.png'
@@ -49,6 +55,7 @@ jobs:
4955
docker-compose-cpdp-b2b: ${{ steps.changes.outputs.docker-compose-cpdp-b2b }}
5056
docker-compose-cpdp-frr: ${{ steps.changes.outputs.docker-compose-cpdp-frr }}
5157
clab-cpdp-frr: ${{ steps.changes.outputs.clab-cpdp-frr }}
58+
clab-ixia-c-b2b: ${{ steps.changes.outputs.clab-ixia-c-b2b }}
5259
clab-rtbh: ${{ steps.changes.outputs.clab-rtbh }}
5360
steps:
5461
- uses: actions/checkout@v3
@@ -68,6 +75,8 @@ jobs:
6875
- 'docker-compose/cpdp-frr/**'
6976
clab-cpdp-frr:
7077
- 'docker-compose/cpdp-frr/**'
78+
clab-ixia-c-b2b:
79+
- 'clab/ixia-c-b2b/**'
7180
clab-rtbh:
7281
- 'clab/rtbh/**'
7382
- name: docker-compose b2b
@@ -85,6 +94,9 @@ jobs:
8594
- name: clab cpdp-frr
8695
if: steps.changes.outputs.clab-cpdp-frr == 'true'
8796
run: echo "Run clab cpdp-frr CI"
97+
- name: clab ixia-c-b2b
98+
if: steps.changes.outputs.clab-ixia-c-b2b == 'true'
99+
run: echo "Run Clab ixia-c-b2b CI"
88100
- name: clab rtbh
89101
if: steps.changes.outputs.clab-rtbh == 'true'
90102
run: echo "Run Clab RTBH CI"
@@ -182,6 +194,20 @@ jobs:
182194
password: ${{ secrets.ENV_GITHUB_PAT }}
183195
- name: Run all-clab
184196
run: make all-clab
197+
clab-ixia-c-b2b:
198+
name: Run Clab ixia-c-b2b CI
199+
runs-on: ubuntu-20.04
200+
needs: paths-filter
201+
defaults:
202+
run:
203+
working-directory: ./clab/ixia-c-b2b
204+
if: needs.paths-filter.outputs.clab-ixia-c-b2b == 'true'
205+
steps:
206+
- uses: actions/checkout@v3
207+
with:
208+
submodules: 'true'
209+
- name: Run all
210+
run: make all
185211
clab-rtbh:
186212
name: Run Clab RTBH CI
187213
runs-on: ubuntu-20.04

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,4 @@ clab/srl-oper-group/opergroup-lab/
141141
*.bak
142142
*.bkp
143143
*.dtmp
144+
*.pcapng

README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,16 @@ Some of the lab examples include Github Action workflow for executing OTG tests
4242

4343
## Reference
4444

45-
| Lab | OTG Tool | DUT | Client | Infrastructure | CI |
46-
| ------------------------------------------------------------------------------------------------------------------------- | ----------- | ---- | ---------------- | -------------- | --- |
47-
| [B2B Ixia-c Traffic](https://github.com/open-traffic-generator/otg-examples/blob/main/docker-compose/b2b) | Ixia-c TE | B2B | `otgen` | Compose | yes |
48-
| [FRR Ixia-c Traffic](https://github.com/open-traffic-generator/otg-examples/blob/main/clab/ixia-c-te-frr) | Ixia-c TE | FRR | `otgen` | Containerlab | no |
49-
| [3xB2B KENG Traffic](https://github.com/open-traffic-generator/otg-examples/blob/main/docker-compose/b2b-3pair) | KENG TE | B2B | `otgen` | Compose | yes |
50-
| [B2B KENG BGP and traffic](https://github.com/open-traffic-generator/otg-examples/blob/main/docker-compose/cpdp-b2b) | KENG PE+TE | B2B | `gosnappi` | Compose | yes |
51-
| [FRR KENG ARP, BGP and traffic](https://github.com/open-traffic-generator/otg-examples/blob/main/docker-compose/cpdp-frr) | KENG PE+TE | FRR | `curl` & `otgen` | Compose & Clab | yes |
52-
| [Hello, snappi! Welcome to the Clab!](https://github.com/open-traffic-generator/otg-examples/blob/main/clab/ixia-c-b2b) | Ixia-c-one | B2B | `snappi` | Containerlab | no |
53-
| [RTBH](https://github.com/open-traffic-generator/otg-examples/blob/main/clab/rtbh) | Ixia-c-one | FRR | `gosnappi` | Containerlab | yes |
45+
| Lab | OTG Tool | DUT | Client | Infrastructure | CI |
46+
| ---------------------------------------------------------------------------------------------------------------------------- | ----------- | ---- | ------------------ | -------------- | --- |
47+
| [B2B Ixia-c Traffic](https://github.com/open-traffic-generator/otg-examples/blob/main/docker-compose/b2b) | Ixia-c TE | B2B | `otgen` | Compose | yes |
48+
| [FRR Ixia-c Traffic](https://github.com/open-traffic-generator/otg-examples/blob/main/clab/ixia-c-te-frr) | Ixia-c TE | FRR | `otgen` | Containerlab | no |
49+
| [3xB2B KENG Traffic](https://github.com/open-traffic-generator/otg-examples/blob/main/docker-compose/b2b-3pair) | KENG TE | B2B | `otgen` | Compose | yes |
50+
| [B2B KENG BGP and traffic](https://github.com/open-traffic-generator/otg-examples/blob/main/docker-compose/cpdp-b2b) | KENG PE+TE | B2B | `gosnappi` | Compose | yes |
51+
| [FRR KENG ARP, BGP and traffic](https://github.com/open-traffic-generator/otg-examples/blob/main/docker-compose/cpdp-frr) | KENG PE+TE | FRR | `curl` & `otgen` | Compose & Clab | yes |
52+
| [Hello, snappi! Welcome to the Clab!](https://github.com/open-traffic-generator/otg-examples/blob/main/clab/ixia-c-b2b) | Ixia-c-one | B2B | `snappi` | Containerlab | yes |
53+
| [Dear snappi, please meet Scapy!](https://github.com/open-traffic-generator/otg-examples/blob/main/clab/ixia-c-b2b/SCAPY.md) | Ixia-c-one | B2B | `scapy` & `snappi` | Containerlab | yes |
54+
| [RTBH](https://github.com/open-traffic-generator/otg-examples/blob/main/clab/rtbh) | Ixia-c-one | FRR | `gosnappi` | Containerlab | yes |
5455

5556

5657
## Lab Descriptions
@@ -79,6 +80,10 @@ KENG ARP, BGP and traffic with FRR as a DUT. This lab demonstrates validation of
7980

8081
Basics of creating a Python program to control Ixia-c-one node, all packaged in a Containerlab topology.
8182

83+
### [Dear snappi, please meet Scapy!](clab/ixia-c-b2b/SCAPY.md)
84+
85+
Joint use of Scapy packet crafting Python module with snappi, to generate custom DNS flows via Ixia-c-one node.
86+
8287
### [RTBH](clab/rtbh)
8388

8489
Remote Triggered Black Hole (RTBH) is a common DDoS mitigation technique which uses BGP announcements to request an ISP to drop all traffic to an IP address under a DDoS attack.

clab/ixia-c-b2b/Makefile

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
SHELL = /bin/bash
2+
3+
.PHONY: all
4+
all: install build deploy run
5+
6+
.PHONY: clean
7+
clean: remove-clab
8+
9+
.PHONY: clean-all
10+
clean-all: clean build-clean install-clean
11+
12+
###############################
13+
# Install components
14+
###############################
15+
16+
.PHONY: install
17+
install: install-clab
18+
19+
install-clab: /usr/bin/containerlab
20+
/usr/bin/containerlab:
21+
bash -c "$$(curl -sL https://get.containerlab.dev)"
22+
23+
install-clean:
24+
-sudo apt remove containerlab -y
25+
26+
###############################
27+
# Build components
28+
###############################
29+
30+
.PHONY: build
31+
build: build-snappi
32+
33+
build-snappi:
34+
sudo docker build -t snappi:local .
35+
36+
build-clean:
37+
-sudo docker rmi snappi:local
38+
39+
###############################
40+
# Deploy lab
41+
###############################
42+
43+
.PHONY: deploy
44+
deploy: deploy-clab
45+
46+
deploy-clab:
47+
sudo -E containerlab deploy -t topo.yml --reconfigure
48+
49+
remove-clab:
50+
sudo containerlab destroy -t topo.yml --cleanup
51+
52+
###############################
53+
# Run tests
54+
###############################
55+
56+
.PHONY: run
57+
run: run-snappi run-scapy run-scapy-port
58+
59+
run-snappi:
60+
sudo docker exec clab-ixcb2b-snappi bash -c "python otg.py"
61+
62+
run-scapy:
63+
sudo docker exec clab-ixcb2b-snappi bash -c "OTG_API='https://clab-ixcb2b-ixia-c:8443' OTG_LOCATION_P1=eth1 OTG_LOCATION_P2=eth2 python scapy2otg.py"
64+
65+
run-scapy-port:
66+
sudo docker exec clab-ixcb2b-snappi bash -c "OTG_API='https://clab-ixcb2b-ixia-c:8443' OTG_LOCATION_P1=eth1 OTG_LOCATION_P2=eth2 python scapy2otg-port.py"

clab/ixia-c-b2b/SCAPY.md

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Dear snappi, please meet Scapy!
2+
3+
## Overview
4+
5+
As [Scapy Project](https://scapy.net/) puts it:
6+
7+
> Scapy is a powerful interactive packet manipulation program. It is able to forge or decode packets of a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more.
8+
9+
In other words, Scapy allows you to craft any packet you want, including L2-4 headers as well as L7 payload. It can also send these packets into a network, as is.
10+
11+
Meanwhile, the [Open Traffic Generator API](https://otg.dev) with its Python client library [snappi](https://snappi.dev), is really great with scaling up the task of putting the packets onto a wire by leveraging OTG-compliant traffic generators, like [Ixia-c](https://ixia-c.dev). The OTG supports the notion of flows, with precise capabilities to schedule packet transmission - rate, interval, duration. It also has rich capabilities to iterate over ranges of MAC and IP addresses, TCP/UDP ports and other parameters.
12+
13+
Wouldn't it be nice if these two could meet and work as a team?
14+
15+
## How would it work?
16+
17+
![Scapy as a custom payload for snappi](scapy-snappi.svg)
18+
19+
Let's assume you want to stress-test a network device with a large number of specific packets. For example, DNS requests & replies. With Scapy, it is easy to craft such payload. Note, how Scapy allows you to create a subset of packet layers. In this case, we're skipping Ethernet, IP and UDP, as OTG would take care of them.
20+
21+
```Python
22+
from scapy.all import *
23+
24+
# create custom DNS request payloads with Scapy
25+
requests = [DNS(id=0, rd=1, qr=0, qd=DNSQR(qtype="A", qname="example.com")),
26+
DNS(id=1, rd=1, qr=0, qd=DNSQR(qtype="AAAA", qname="example.com"))]
27+
```
28+
29+
Now, with snappi, we can use these payloads to create a dedicated flow for each Scapy packet, with duration and rate we need.
30+
31+
```Python
32+
import snappi
33+
34+
api = snappi.api(location=OTG_API, verify=False)
35+
cfg = api.config()
36+
packet_count = 10 # send 10 packets per each flow
37+
38+
# flows for requests
39+
for i in range(len(requests)):
40+
n = "request" + str(i)
41+
f = cfg.flows.flow(name=n)[-1]
42+
# will use UDP with custom payload
43+
eth, ip, udp, payload = f.packet.ethernet().ipv4().udp().custom()
44+
eth.src.value, eth.dst.value = "02:00:00:00:01:AA", "02:00:00:00:02:AA"
45+
ip.src.value, ip.dst.value = "192.0.2.1", "192.0.2.2"
46+
# increment UDP source port number for each packet
47+
udp.src_port.increment.start = 1024
48+
udp.src_port.increment.step = 1
49+
udp.src_port.increment.count = requests_count
50+
udp.dst_port.value = 53
51+
# copy a payload from Scapy packet into a snappi flow
52+
payload.bytes = requests[i].build().hex()
53+
# number of packets to transmit
54+
f.duration.fixed_packets.packets = requests_count
55+
# delay between flows to simulate a sequence of packets: 1ms
56+
f.duration.fixed_packets.delay.microseconds = 1000 * i
57+
```
58+
59+
Some details above are omitted, see [scapy2otg.py](scapy2otg.py) for more.
60+
61+
As a result, the produced OTG configuration of the first flow of the DNS requests will have a custom payload after the UDP layer (see the very end of the YAML below):
62+
63+
```Yaml
64+
flows:
65+
- duration:
66+
choice: fixed_packets
67+
fixed_packets:
68+
delay:
69+
choice: microseconds
70+
microseconds: 0
71+
gap: 12
72+
packets: 10
73+
metrics:
74+
enable: true
75+
name: request0
76+
packet:
77+
- choice: ethernet
78+
ethernet:
79+
dst:
80+
choice: value
81+
value: 02:00:00:00:02:AA
82+
src:
83+
choice: value
84+
value: 02:00:00:00:01:AA
85+
- choice: ipv4
86+
ipv4:
87+
dst:
88+
choice: value
89+
value: 192.0.2.2
90+
src:
91+
choice: value
92+
value: 192.0.2.1
93+
- choice: udp
94+
udp:
95+
dst_port:
96+
choice: value
97+
value: 53
98+
src_port:
99+
choice: increment
100+
increment:
101+
count: 10
102+
start: 1024
103+
step: 1
104+
- choice: custom
105+
custom:
106+
bytes: 000001000001000000000000076578616d706c6503636f6d0000010001
107+
```
108+
109+
## Captured & Framed
110+
111+
When captured, the packet frames generated by Ixia-c look as DNS queries in Wireshark, with the exception of additional data signature Ixia-c adds at the end of each packet. The signature is needed to identify each packet at the receiving side, and measure latency, packet loss and other metrics. If you look into [scapy2otg.py](scapy2otg.py), the following line instructs Ixia-c to add the signature: `f.metrics.enable = True`.
112+
113+
![DNS Requests Capture](p2_pcap.png)
114+
115+
An alternative implementation that uses port-level metrics instead of packet signatures can be found in [scapy2otg-port.py](scapy2otg-port.py)
116+
117+
## Giving it a try
118+
119+
### Prerequisites
120+
121+
* Linux host or VM with sudo permissions and Docker support. See [some ready-to-use options](README.md#options-for-linux-vm-deployment-for-containerlab)
122+
* `git` - how to install depends on your Linux distro.
123+
* [Docker](https://docs.docker.com/engine/install/)
124+
* [Containerlab](https://containerlab.dev/install/)
125+
* Clone of the repository
126+
127+
```Shell
128+
git clone --recurse-submodules https://github.com/open-traffic-generator/otg-examples.git
129+
cd otg-examples/clab/ixia-c-b2b
130+
```
131+
### TLDR version
132+
133+
```Shell
134+
make install build deploy run-scapy clean
135+
```
136+
137+
Open `p1.pcap` and `p2.pcap` to inspect captured packets.
138+
139+
Otherwise, follow a step-by-step guide:
140+
141+
### Prepare a `snappi` container image
142+
143+
Run the following only once, to build a container image where `snappi` program will execute:
144+
145+
```Shell
146+
sudo docker build -t snappi:local .
147+
```
148+
149+
### Deploy a lab
150+
151+
```Shell
152+
sudo -E containerlab deploy -t topo.yml
153+
```
154+
155+
### Run scapy2otg test
156+
157+
```Shell
158+
sudo docker exec -it clab-ixcb2b-snappi bash -c "OTG_API='https://clab-ixcb2b-ixia-c:8443' OTG_LOCATION_P1=eth1 OTG_LOCATION_P2=eth2 python scapy2otg.py"
159+
```
160+
161+
### Destroy the lab
162+
163+
```Shell
164+
sudo -E containerlab destroy -t topo.yml
165+
```
166+

0 commit comments

Comments
 (0)