├── .gitignore ├── example-network.json ├── network-lab.png ├── network-lab.sh ├── network-lab.svg ├── package.json └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.pid -------------------------------------------------------------------------------- /example-network.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "1": { 4 | "ip": "1.0.0.1" 5 | }, 6 | "2": { 7 | "ip": "1.0.0.2" 8 | }, 9 | "3": { 10 | "ip": "1.0.0.3" 11 | } 12 | }, 13 | "edges": [ 14 | { 15 | "nodes": ["1", "2"], 16 | "->": "delay 10ms 20ms distribution normal", 17 | "<-": "delay 500ms 20ms distribution normal" 18 | }, 19 | { 20 | "nodes": ["2", "3"], 21 | "->": "delay 10ms 20ms distribution normal", 22 | "<-": "delay 500ms 20ms distribution normal" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /network-lab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudomesh/network-lab/f4632bfc60a57c7154632a539b252ff5be921c76/network-lab.png -------------------------------------------------------------------------------- /network-lab.sh: -------------------------------------------------------------------------------- 1 | #!bash 2 | stdin=$(cat) 3 | input=$stdin 4 | 5 | # clear namespaces 6 | ip -all netns delete || true 7 | 8 | # add namespaces 9 | echo "adding nodes" 10 | for node in $(jq '.nodes | keys[]' <<< "$input") 11 | do 12 | ip netns add "netlab-${node:1:-1}" 13 | done 14 | 15 | # iterate over edges array 16 | echo "adding edges" 17 | length=$(jq '.edges | length' <<< "$input") 18 | for ((i=0; i<$length; i++)); do 19 | 20 | # get names of nodes 21 | A=$(jq '.edges['$i'].nodes[0]' <<< "$input") 22 | B=$(jq '.edges['$i'].nodes[1]' <<< "$input") 23 | A=${A:1:-1} 24 | B=${B:1:-1} 25 | 26 | # create veth to link them 27 | ip link add "veth-$A-$B" type veth peer name "veth-$B-$A" 28 | 29 | # assign each side of the veth to one of the nodes namespaces 30 | ip link set "veth-$A-$B" netns "netlab-$A" 31 | ip link set "veth-$B-$A" netns "netlab-$B" 32 | 33 | # add ip addresses on each side 34 | ipA=$(jq '.nodes["'$A'"].ip' <<< "$input") 35 | ipB=$(jq '.nodes["'$B'"].ip' <<< "$input") 36 | ip netns exec "netlab-$A" ip addr add ${ipA:1:-1} dev "veth-$A-$B" 37 | ip netns exec "netlab-$B" ip addr add ${ipB:1:-1} dev "veth-$B-$A" 38 | 39 | # bring the interfaces up 40 | ip netns exec "netlab-$A" ip link set dev "veth-$A-$B" up 41 | ip netns exec "netlab-$B" ip link set dev "veth-$B-$A" up 42 | 43 | # add some connection quality issues 44 | AtoB=$(jq '.edges['$i']["->"]' <<< "$input") 45 | BtoA=$(jq '.edges['$i']["<-"]' <<< "$input") 46 | 47 | ip netns exec "netlab-$A" tc qdisc add dev "veth-$A-$B" root netem ${AtoB:1:-1} 48 | ip netns exec "netlab-$B" tc qdisc add dev "veth-$B-$A" root netem ${BtoA:1:-1} 49 | done 50 | -------------------------------------------------------------------------------- /network-lab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Produced by OmniGraffle 6.6.2 2017-04-21 03:11:38 +0000Canvas 1Layer 1 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "network-lab", 3 | "version": "0.0.1", 4 | "description": "Lightweight network sim", 5 | "scripts": [ "network-lab.sh" ] 6 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Network lab 2 | 3 | This script creates a set of network namespaces, linked by virtual eth connections. It is entirely driven by a JSON network graph configuration format. Additionally, it uses the [tc netem](http://man7.org/linux/man-pages/man8/tc-netem.8.html) traffic shaper to simulate various kinds of faulty connections, packet loss, and latency on the links. This tool was created to experiment with and test routing protocols, but it could have many other uses. 4 | 5 | ![JSON to network diagram](/network-lab.png) 6 | 7 | ## Dependencies 8 | 9 | Your system must have network namespace support. For example, Ubuntu 16.04 will work. Also, [jq](https://stedolan.github.io/jq/) must be installed. 10 | 11 | ## Configuration 12 | 13 | Network lab has a JSON configuration format: 14 | 15 | ```json 16 | { 17 | "nodes": { 18 | "1": { 19 | "ip": "1.0.0.1" 20 | }, 21 | "2": { 22 | "ip": "1.0.0.2" 23 | }, 24 | "3": { 25 | "ip": "1.0.0.3" 26 | } 27 | }, 28 | "edges": [ 29 | { 30 | "nodes": ["1", "2"], 31 | "->": "delay 10ms 20ms distribution normal", 32 | "<-": "delay 500ms 20ms distribution normal" 33 | }, 34 | { 35 | "nodes": ["2", "3"], 36 | "->": "delay 10ms 20ms distribution normal", 37 | "<-": "delay 500ms 20ms distribution normal" 38 | } 39 | ] 40 | } 41 | ``` 42 | 43 | The `nodes` object has all the nodes, indexed by name. 44 | 45 | * The `ip` property will be set as the node's IP addres 46 | 47 | The `edges` object contains an array of edges. 48 | 49 | * The `nodes` array contains the node names of the 2 sides of the edge. 50 | * The `->` and `<-` properties contain arguments to be given to the [tc netem](http://man7.org/linux/man-pages/man8/tc-netem.8.html) command to degrade the connection from the left node to the right node and vice vera. 51 | 52 | ## Usage 53 | 54 | ```bash 55 | sudo su 56 | source ./network-lab.sh << EOF 57 | { 58 | "nodes": { 59 | "1": { "ip": "1.0.0.1" }, 60 | "2": { "ip": "1.0.0.2" }, 61 | "3": { "ip": "1.0.0.3" } 62 | 63 | }, 64 | "edges": [ 65 | { 66 | "nodes": ["1", "2"], 67 | "->": "loss random 0%", 68 | "<-": "loss random 0%" 69 | }, 70 | { 71 | "nodes": ["2", "3"], 72 | "->": "loss random 0%", 73 | "<-": "loss random 0%" 74 | } 75 | ] 76 | } 77 | EOF 78 | 79 | ip netns exec netlab-3 ip address 80 | ``` 81 | --------------------------------------------------------------------------------