├── examples ├── null │ ├── README.md │ ├── setup.sh │ └── null.yaml ├── run_hoverd.sh ├── switch │ ├── switch.yaml │ ├── setup.sh │ └── README.md ├── nat_router │ ├── arp.sh │ ├── setup.sh │ ├── nat_router.yaml │ └── README.md ├── nat-one-to-one_router │ ├── arp.sh │ ├── setup.sh │ ├── nat_router.yaml │ └── README.md ├── dhcp │ ├── setup.sh │ ├── dhcp.yaml │ └── README.md ├── run_iovisorovnd_file.sh ├── router │ ├── setup.sh │ ├── router.yaml │ └── README.md └── README.md ├── images ├── pictures.pptx ├── dhcp_tutorial.png ├── router_tutorial.png ├── switch_tutorial.png ├── iovisor-ovn-overview.png └── iovisor-ovn-architecture.png ├── docs └── 2016-11-07 - OvS Conference.pdf ├── iomodules ├── null │ ├── null.go │ └── nullAPI.go ├── l2switch │ ├── README.md │ ├── switch.go │ └── switchAPI.go ├── dhcp │ ├── README.md │ ├── server.go │ ├── dhcp_slowpath.go │ ├── dhcp.go │ └── dhcpAPI.go ├── nat │ ├── README.md │ └── natAPI.go ├── onetoonenat │ ├── README.md │ ├── natAPI.go │ └── nat.go ├── iomodules.go ├── README.md ├── utils.go └── router │ ├── README.md │ ├── router.go │ └── slowpath.go ├── README_OPENSTACK.md ├── common └── log.go ├── ARCHITECTURE.md ├── install_alone.sh ├── hover ├── hoverapi.go ├── printFormat.go └── controller.go ├── iovisorovnd └── main.go ├── cli ├── help.go └── cli.go ├── README.md ├── ovnmonitor └── print.go ├── README_STANDALONE.md ├── config └── config.go ├── mainlogic ├── print.go └── mainlogic.go ├── servicetopology └── servicetopology.go └── LICENSE.txt /examples/null/README.md: -------------------------------------------------------------------------------- 1 | TBD! 2 | -------------------------------------------------------------------------------- /images/pictures.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/iovisor-ovn/HEAD/images/pictures.pptx -------------------------------------------------------------------------------- /images/dhcp_tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/iovisor-ovn/HEAD/images/dhcp_tutorial.png -------------------------------------------------------------------------------- /images/router_tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/iovisor-ovn/HEAD/images/router_tutorial.png -------------------------------------------------------------------------------- /images/switch_tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/iovisor-ovn/HEAD/images/switch_tutorial.png -------------------------------------------------------------------------------- /images/iovisor-ovn-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/iovisor-ovn/HEAD/images/iovisor-ovn-overview.png -------------------------------------------------------------------------------- /docs/2016-11-07 - OvS Conference.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/iovisor-ovn/HEAD/docs/2016-11-07 - OvS Conference.pdf -------------------------------------------------------------------------------- /images/iovisor-ovn-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/iovisor-ovn/HEAD/images/iovisor-ovn-architecture.png -------------------------------------------------------------------------------- /examples/run_hoverd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | #This script (install) and launches Hocer daemon, the framework used to deploy IOModules 6 | 7 | #Install Hover daemon 8 | #go install github.com/iovisor/iomodules/hover/hoverd 9 | 10 | #Launch Hover daemon 11 | sudo $GOPATH/bin/hoverd -listen 127.0.0.1:5002 12 | -------------------------------------------------------------------------------- /examples/switch/switch.yaml: -------------------------------------------------------------------------------- 1 | # This is a simple service topology composed only by a switch. 2 | # No configuration is passed to the switch and it is connected to two external 3 | # interfaces 4 | 5 | modules: 6 | - name: myswitch 7 | type: switch 8 | 9 | external_interfaces: 10 | - module: myswitch 11 | iface: veth1 12 | - module: myswitch 13 | iface: veth2 14 | -------------------------------------------------------------------------------- /examples/nat_router/arp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | sudo ip netns exec ns1 sudo arping -c 1 10.0.1.254 6 | sudo ip netns exec ns2 sudo arping -c 1 10.0.2.254 7 | sudo ip netns exec ns3 sudo arping -c 1 10.10.1.100 8 | 9 | sudo ip netns exec ns1 sudo ping -c 1 10.0.1.254 10 | sudo ip netns exec ns2 sudo ping -c 1 10.0.2.254 11 | sudo ip netns exec ns3 sudo ping -c 1 10.10.1.100 12 | -------------------------------------------------------------------------------- /examples/nat-one-to-one_router/arp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | sudo ip netns exec ns1 sudo arping -c 1 10.0.1.254 6 | sudo ip netns exec ns2 sudo arping -c 1 10.0.1.254 7 | sudo ip netns exec ns3 sudo arping -c 1 10.10.1.254 8 | 9 | sudo ip netns exec ns1 sudo ping -c 1 10.0.1.254 10 | sudo ip netns exec ns2 sudo ping -c 1 10.0.1.254 11 | sudo ip netns exec ns3 sudo ping -c 1 10.10.1.254 12 | -------------------------------------------------------------------------------- /examples/dhcp/setup.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -x 4 | 5 | for i in `seq 1 3`; 6 | do 7 | # remove ns and veth pairs if already created 8 | sudo ip netns del ns${i} 9 | sudo ip link del veth${i} 10 | 11 | # create ns and veth pairs 12 | sudo ip netns add ns${i} 13 | sudo ip link add veth${i}_ type veth peer name veth${i} 14 | sudo ip link set veth${i}_ netns ns${i} 15 | sudo ip netns exec ns${i} ip link set dev veth${i}_ up 16 | sudo ip link set dev veth${i} up 17 | done 18 | -------------------------------------------------------------------------------- /examples/null/setup.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -x 4 | 5 | for i in `seq 1 3`; 6 | do 7 | # remove ns and veth pairs if already created 8 | sudo ip netns del ns${i} 9 | sudo ip link del veth${i} 10 | 11 | # create ns and veth pairs 12 | sudo ip netns add ns${i} 13 | sudo ip link add veth${i}_ type veth peer name veth${i} 14 | sudo ip link set veth${i}_ netns ns${i} 15 | sudo ip netns exec ns${i} ip link set dev veth${i}_ up 16 | sudo ip link set dev veth${i} up 17 | done 18 | -------------------------------------------------------------------------------- /examples/switch/setup.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -x 4 | 5 | for i in `seq 1 2`; 6 | do 7 | sudo ip netns del ns${i} > /dev/null 2>&1 # remove ns if already existed 8 | sudo ip link del veth${i} > /dev/null 2>&1 9 | 10 | sudo ip netns add ns${i} 11 | sudo ip link add veth${i}_ type veth peer name veth${i} 12 | sudo ip link set veth${i}_ netns ns${i} 13 | sudo ip netns exec ns${i} ip link set dev veth${i}_ up 14 | sudo ip link set dev veth${i} up 15 | sudo ip netns exec ns${i} ifconfig veth${i}_ 10.0.0.${i}/24 16 | done 17 | -------------------------------------------------------------------------------- /examples/run_iovisorovnd_file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #set -x 4 | 5 | #This script (install) and launches iovisor-ovn daemon in standalone mode. 6 | #The standalone mode allows the user to deploy a single or a chain of IOModules 7 | #using a YAML configuration file. 8 | #The -file parameter starts the Standalone mode 9 | 10 | echo "FILE" 11 | echo $1 12 | echo "" 13 | 14 | set -x 15 | 16 | #Install iovisorovnd 17 | #go install github.com/iovisor/iovisor-ovn/iovisorovnd 18 | 19 | #Launch iovisorovndn using file parameter 20 | sudo $GOPATH/bin/iovisorovnd -file $1 -hover http://127.0.0.1:5002 21 | -------------------------------------------------------------------------------- /examples/null/null.yaml: -------------------------------------------------------------------------------- 1 | #modules: 2 | # - name: myswitch 3 | # type: switch 4 | # - name: mynull1 5 | # type: null_node 6 | # - name: mynull2 7 | # type: null_node 8 | # - name: mynull3 9 | # type: null_node 10 | # 11 | #links: 12 | # - from: myswitch 13 | # to: mynull1 14 | # - from: myswitch 15 | # to: mynull2 16 | # - from: myswitch 17 | # to: mynull3 18 | # 19 | #external_interfaces: 20 | # - module: myswitch 21 | # iface: veth1 22 | 23 | modules: 24 | - name: mynull1 25 | type: null_node 26 | 27 | external_interfaces: 28 | - module: mynull1 29 | iface: veth1 30 | 31 | -------------------------------------------------------------------------------- /examples/router/setup.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -x 4 | 5 | for i in `seq 1 3`; 6 | do 7 | # remove ns and veth pairs if already created 8 | sudo ip netns del ns${i} 9 | sudo ip link del veth${i} 10 | sudo ip link del veth${i} 11 | 12 | # create ns and veth pairs 13 | sudo ip netns add ns${i} 14 | sudo ip link add veth${i}_ type veth peer name veth${i} 15 | sudo ip link set veth${i}_ netns ns${i} 16 | sudo ip netns exec ns${i} ip link set dev veth${i}_ up 17 | sudo ip link set dev veth${i} address 82:73:8d:f3:62:${i} up 18 | sudo ip netns exec ns${i} ifconfig veth${i}_ 10.0.${i}.100/24 19 | 20 | sudo ip netns exec ns${i} route add default gw 10.0.${i}.1 veth${i}_ 21 | done 22 | -------------------------------------------------------------------------------- /examples/router/router.yaml: -------------------------------------------------------------------------------- 1 | modules: 2 | - name: myrouter 3 | type: router 4 | config: 5 | interfaces: 6 | - name: veth1 7 | ip: 10.0.1.1 8 | netmask: 255.255.255.0 9 | mac: "82:73:8d:f3:62:01" 10 | 11 | - name: veth2 12 | ip: 10.0.2.1 13 | netmask: 255.255.255.0 14 | mac: "82:73:8d:f3:62:02" 15 | 16 | - name: veth3 17 | ip: 10.0.3.1 18 | netmask: 255.255.255.0 19 | mac: "82:73:8d:f3:62:03" 20 | 21 | external_interfaces: 22 | - module: myrouter 23 | iface: veth1 24 | - module: myrouter 25 | iface: veth2 26 | - module: myrouter 27 | iface: veth3 28 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This folder contains a few examples that illustrate the capabilities of 4 | iovisor-ovn. 5 | 6 | Each subfolder contains a different example, usually on these there are three files 7 | 8 | * README.md: presents the details of the example 9 | * setup.sh: prepares the environment 10 | * *.yaml: contains the configuration of the IOModules and its connections. 11 | 12 | Before running the examples it is necessary to install some components. 13 | Please see [Readme Standalone](../README_STANDALONE.md). 14 | 15 | ## Available Examples 16 | * [switch](switch): L2 switch connected to two virtual network interfaces 17 | * [router](router): L3 router connected to three virtual network interfaces 18 | * [dhcp](dhcp): DHCP IOModule connected to a switch 19 | * [Nat and Router](nat_router): Nat iomodule connected to a Router 20 | * [Nat One-to-One and Router](nat-one-to-one_router): Nat One-to-One connected to a Router 21 | -------------------------------------------------------------------------------- /examples/nat_router/setup.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -x 4 | 5 | for i in `seq 1 3`; 6 | do 7 | # remove ns and veth pairs if already created 8 | sudo ip netns del ns${i} 9 | sudo ip link del veth${i} 10 | done 11 | 12 | for i in `seq 1 3`; 13 | do 14 | # create ns and veth pairs 15 | sudo ip netns add ns${i} 16 | sudo ip link add veth${i}_ type veth peer name veth${i} 17 | sudo ip link set veth${i}_ netns ns${i} 18 | sudo ip netns exec ns${i} ip link set dev veth${i}_ up 19 | sudo ip link set dev veth${i} up 20 | sudo ip netns exec ns${i} ifconfig veth${i}_ 10.0.${i}.1/24 21 | 22 | sudo ethtool --offload veth${i} rx off tx off 23 | sudo ethtool -K veth${i} gso off 24 | done 25 | 26 | sudo ip netns exec ns3 ifconfig veth3_ 10.10.1.1/24 27 | 28 | sudo ip netns exec ns1 sudo route add default gw 10.0.1.254 veth1_ 29 | sudo ip netns exec ns2 sudo route add default gw 10.0.2.254 veth2_ 30 | sudo ip netns exec ns3 sudo route add default gw 10.10.1.100 veth3_ 31 | -------------------------------------------------------------------------------- /examples/dhcp/dhcp.yaml: -------------------------------------------------------------------------------- 1 | # This example describes a network topology with a DHCP server and a 2 | # switch. 3 | # The DHCP server and three network interfaces are connected to the switch. 4 | # Please note that currently it is not possible to add a router to 5 | # this topology because the switch is not able to broadcast a packet to 6 | # more than an IOModule 7 | 8 | modules: 9 | - name: myswitch 10 | type: switch 11 | 12 | - name: mydhcp 13 | type: dhcp 14 | config: 15 | netmask: 255.255.255.0 16 | addr_low: 192.168.1.100 # first address assigned by the dhcp 17 | addr_high: 192.168.1.150 # last address assigned by the dhcp 18 | dns: 8.8.8.8 19 | router: 192.168.1.1 20 | lease_time: 3600 21 | server_ip: 192.168.1.250 22 | server_mac: "b6:87:f8:5a:40:23" 23 | 24 | links: 25 | - from: myswitch 26 | to: mydhcp 27 | 28 | external_interfaces: 29 | - module: myswitch 30 | iface: veth1 31 | - module: myswitch 32 | iface: veth2 33 | - module: myswitch 34 | iface: veth3 35 | -------------------------------------------------------------------------------- /examples/nat-one-to-one_router/setup.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -x 4 | 5 | for i in `seq 1 3`; 6 | do 7 | # remove ns and veth pairs if already created 8 | sudo ip netns del ns${i} 9 | sudo ip link del veth${i} 10 | done 11 | 12 | for i in `seq 1 3`; 13 | do 14 | # create ns and veth pairs 15 | sudo ip netns add ns${i} 16 | sudo ip link add veth${i}_ type veth peer name veth${i} 17 | sudo ip link set veth${i}_ netns ns${i} 18 | sudo ip netns exec ns${i} ip link set dev veth${i}_ up 19 | sudo ip link set dev veth${i} up 20 | sudo ip netns exec ns${i} ifconfig veth${i}_ 10.0.1.${i}/24 21 | 22 | sudo ethtool --offload veth${i} rx off tx off 23 | sudo ethtool -K veth${i} gso off 24 | done 25 | 26 | sudo ip netns exec ns3 ifconfig veth3_ 10.10.1.1/24 27 | 28 | sudo ip netns exec ns1 sudo route add default gw 10.0.1.254 veth1_ 29 | sudo ip netns exec ns2 sudo route add default gw 10.0.1.254 veth2_ 30 | sudo ip netns exec ns3 sudo route add default gw 10.10.1.254 veth3_ 31 | 32 | # 33 | # sudo ip netns exec ns1 sudo arping 10.0.1.254 34 | # sudo ip netns exec ns2 sudo arping 10.0.1.254 35 | # sudo ip netns exec ns3 sudo arping 10.10.1.254 36 | -------------------------------------------------------------------------------- /examples/nat-one-to-one_router/nat_router.yaml: -------------------------------------------------------------------------------- 1 | modules: 2 | - name: Switch1 3 | type: switch 4 | config: 5 | 6 | # - name: Switch2 7 | # type: switch 8 | # config: 9 | 10 | - name: Nat 11 | type: onetoonenat 12 | config: 13 | nat_entries: 14 | - internal_ip: 10.0.1.1 15 | external_ip: 130.192.1.1 16 | 17 | - internal_ip: 10.0.1.2 18 | external_ip: 130.192.1.2 19 | 20 | - name: Router 21 | type: router 22 | config: 23 | interfaces: 24 | - name: Switch1 25 | ip: 10.0.1.254 26 | netmask: 255.255.255.0 27 | mac: "7e:ee:c2:01:01:01" 28 | 29 | - name: Nat 30 | ip: 10.10.1.254 31 | netmask: 255.255.255.0 32 | mac: "7e:ee:c2:03:03:03" 33 | 34 | static_routes: 35 | - network: 0.0.0.0 36 | netmask: 0.0.0.0 37 | interface: Nat 38 | #next_hop: "0" 39 | 40 | links: 41 | - from: Switch1 42 | to: Router 43 | - from: Router 44 | to: Nat 45 | 46 | external_interfaces: 47 | - module: Switch1 48 | iface: veth1 49 | - module: Switch1 50 | iface: veth2 51 | - module: Nat 52 | iface: veth3 53 | -------------------------------------------------------------------------------- /examples/nat_router/nat_router.yaml: -------------------------------------------------------------------------------- 1 | modules: 2 | - name: Switch1 3 | type: switch 4 | config: 5 | 6 | - name: Switch2 7 | type: switch 8 | config: 9 | 10 | - name: Nat 11 | type: nat 12 | config: 13 | public_ip: 10.10.1.100 14 | public_port_ip: 10.10.1.100 15 | public_port_mac: "7e:ee:c2:cf:3c:dc" 16 | 17 | - name: Router 18 | type: router 19 | config: 20 | interfaces: 21 | - name: Switch1 22 | ip: 10.0.1.254 23 | netmask: 255.255.255.0 24 | mac: "7e:ee:c2:01:01:01" 25 | 26 | - name: Switch2 27 | ip: 10.0.2.254 28 | netmask: 255.255.255.0 29 | mac: "7e:ee:c2:02:02:02" 30 | 31 | - name: Nat 32 | ip: 10.10.1.100 33 | netmask: 255.255.255.0 34 | mac: "7e:ee:c2:03:03:03" 35 | 36 | static_routes: 37 | - network: 0.0.0.0 38 | netmask: 0.0.0.0 39 | interface: Nat 40 | #next_hop: "0" 41 | 42 | links: 43 | - from: Switch1 44 | to: Router 45 | - from: Switch2 46 | to: Router 47 | - from: Router 48 | to: Nat 49 | 50 | external_interfaces: 51 | - module: Switch1 52 | iface: veth1 53 | - module: Switch2 54 | iface: veth2 55 | - module: Nat 56 | iface: veth3 57 | -------------------------------------------------------------------------------- /iomodules/null/null.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package null 15 | 16 | var null_code = ` 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | static int handle_rx(void *skb, struct metadata *md) { 29 | 30 | bpf_trace_printk("[null-%d]: in_fc: %d\n", md->module_id, md->in_ifc); 31 | 32 | //return RX_DROP; 33 | 34 | //pkt_redirect(skb, md, md->in_ifc); 35 | //return RX_REDIRECT; 36 | 37 | //pkt_controller(skb, md, 700); 38 | return RX_CONTROLLER; 39 | } 40 | ` 41 | -------------------------------------------------------------------------------- /README_OPENSTACK.md: -------------------------------------------------------------------------------- 1 | # IOVisor-OVN Installation Guide with DevStack 2 | 3 | This guide presents the steps to install a testing deployment with OpenStack, OVN and IOVisor-OVN. 4 | If you want to test IOVisor-OVN without OpenStack please see [README_STANDALONE](./README_STANDALONE.md). 5 | 6 | ## Installation 7 | In order to create a test environment with a single compute node you should perform the following steps. 8 | 9 | 1. Install a test system. We recommend to use Ubuntu 16.04, which represents our main developed platform. Please consider that DevStack performs deep changes on the system, so the system should only be dedicated to this purpose. 10 | 11 | 2. Install DevStack with IOVisor-OVN as network provider, typing the following commands: 12 | 13 | ``` 14 | git clone http://git.openstack.org/openstack-dev/devstack.git --branch stable/newton 15 | git clone https://github.com/netgroup-polito/networking-ovn.git --branch stable/newton 16 | cd devstack 17 | cp ../networking-ovn/devstack/local.conf.sample local.conf 18 | ./stack.sh 19 | ``` 20 | When devstack finishes it shows something like: 21 | 22 | ``` 23 | This is your host ip: 192.168.122.8 24 | Horizon is now available at http://192.168.122.8/ 25 | Keystone is serving at http://192.168.122.8:5000/ 26 | The default users are: admin and demo 27 | The password: password 28 | 2016-11-24 19:34:06.116 | stack.sh completed in 1334 seconds. 29 | ``` 30 | 31 | At this point, your new OpenStack newton instance will use IOVisor-OVN as network provider. 32 | 33 | Currently L2 and L3 networks are supported, the IP addresses in the VMs must be configured manually (the DHCP service has not been integrated yet). 34 | -------------------------------------------------------------------------------- /common/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package common 15 | 16 | import ( 17 | "os" 18 | 19 | "github.com/iovisor/iovisor-ovn/config" 20 | l "github.com/op/go-logging" 21 | ) 22 | 23 | // var format = l.MustStringFormatter(`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`) 24 | var format = l.MustStringFormatter(`%{color}%{time:15:04:05} %{level:.4s} >%{color:reset} %{message}`) 25 | 26 | //Logger initialization 27 | func LogInit() { 28 | 29 | backend := l.NewLogBackend(os.Stderr, "", 0) 30 | 31 | if config.Debug == true { 32 | format = l.MustStringFormatter(`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`) 33 | } 34 | 35 | backendFormatter := l.NewBackendFormatter(backend, format) 36 | 37 | backendLeveled := l.SetBackend(backendFormatter) 38 | 39 | backendLeveled.SetLevel(l.NOTICE, "") 40 | 41 | if config.Info == true { 42 | backendLeveled.SetLevel(l.INFO, "") 43 | } 44 | 45 | if config.Debug == true { 46 | backendLeveled.SetLevel(l.DEBUG, "") 47 | } 48 | 49 | l.SetBackend(backendLeveled) 50 | } 51 | -------------------------------------------------------------------------------- /examples/switch/README.md: -------------------------------------------------------------------------------- 1 | # Switch 2 | 3 | In this example a switch module is deployed and two virtual network interfaces 4 | in different network namespaces are connected to it. 5 | Connectivity is tested by pinging between the different namespaces 6 | 7 |
8 | 9 | ## Preparing the network namespaces 10 | 11 | In order to work, it is necessary to create two network namespaces and set two 12 | pairs of veth interfaces. 13 | 14 | Execute the [setup.sh](./setup.sh) script: 15 | 16 | ```bash 17 | sudo ./setup.sh 18 | ``` 19 | 20 | ## Launching hover 21 | 22 | Before deploying the switch, it is necessary to launch the hover daemon. 23 | Please note to kill other instances of hover previously running. 24 | 25 | ```bash 26 | export GOPATH=$HOME/go 27 | sudo $GOPATH/bin/hoverd -listen 127.0.0.1:5002 28 | ``` 29 | 30 | ## Deploying the switch 31 | 32 | The [switch.yaml](./switch.yaml) contains the configuration for this example. 33 | 34 | In order to launch it please execute: 35 | 36 | ```bash 37 | export GOPATH=$HOME/go 38 | cd $GOPATH/src/github.com/iovisor/iovisor-ovn/examples/switch 39 | $GOPATH/bin/iovisorovnd -file switch.yaml -hover http://127.0.0.1:5002 40 | ``` 41 | 42 | ## Testing connectivity 43 | 44 | Now you are able to test the connectivity pinging between the network interfaces 45 | in the different network spaces 46 | 47 | ```bash 48 | sudo ip netns exec ns1 ping 10.0.0.2 49 | ``` 50 | 51 | ## Debugging 52 | 53 | In order to see the debug output generated by the IOModule we suggest to see the result of the print on the trace_pipe. 54 | 55 | ```bash 56 | sudo su 57 | cd /sys/kernel/debug/tracing/ 58 | cat trace_pipe 59 | ``` 60 | -------------------------------------------------------------------------------- /iomodules/l2switch/README.md: -------------------------------------------------------------------------------- 1 | # Switch IOModule 2 | 3 | This module is an Ethernet Switch that implements the MAC learning algorithm. 4 | 5 | ## API 6 | 7 | - **AddForwardingTableEntry(mac string, ifaceName string)** 8 | Adds a static entry in the forwarding table of the switch 9 | -- mac: MAC address. It must be in the "xx:yy:zz:xx:xx:xx" format 10 | -- ifaceName: name of the port where MAC can be reached 11 | 12 | ## How to use 13 | 14 | Using iovisor-ovn daemon in standalone mode, the user can deploy and configure a single or a chain of IOModules. 15 | The entire setup can be deployed starting from a YAML configuration file. 16 | 17 | ```bash 18 | $GOPATH/bin/iovisorovnd -file 19 | ``` 20 | 21 | Some examples are available in [/examples](./../../examples/) folder: 22 | * [Switch](./../../examples/switch/) 23 | 24 | ### YAML Configuration Format 25 | 26 | The following is an example of the configuration of a switch: 27 | ``` 28 | [...] 29 | - name: myswitch 30 | type: switch 31 | config: 32 | forwarding_table: 33 | - port: veth1 34 | mac: "b2:1b:34:5d:9b:2d" 35 | - port: veth2 36 | mac: "b2:1b:34:5d:9b:2e" 37 | [...] 38 | ``` 39 | 40 | - **forwarding _table**: defines static entries for the forwarding table of the switch. Please note that this configuration parameter is optional. It's useful when the programmer wants to force entries in the forwarding table. 41 | 42 | ## Limitations 43 | 44 | - There is not a mechanishm to clean up the forwarding table, all the entries remain there until the module is unloaded. This behavior could cause issues when the number of entries reaches the maximum. 45 | This issue is not on the immediate roadmap, however could be solved using some sort of timeout mechanishm provided by the eBPF maps. 46 | -------------------------------------------------------------------------------- /ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | # IOVisor-OVN Architecture 2 | 3 | IOVisor-OVN sits on side of the traditional OVN architecture, it intercepts the contents of the different databases and it deploys the required network services using the IOVisor technology, as shown in the Figure below. 4 | 5 |
6 | 7 | ## IOVisor-OVN daemon 8 | 9 | Daemon is the running daemon that coordinates all other modules. 10 | The daemon is a central system that coordinates the deployment of network services 11 | based on the contents of the OVN databases. 12 | 13 | It is made by the following elements. 14 | 15 | * **OVN Monitor**. Uses the [libovsdb](https://github.com/socketplane/libovsdb) to monitor all the databases of OVN. (northbound, southbound and the local ovsdb on each compute node) 16 | 17 | * **Logger**. It is a simple system that prints info on screen and, optionally, can dump the messages on disk as well. 18 | 19 | * **CLI**. The command line interface allows to the user to interact with the system and it is used mainly for troubleshooting and debugging purposes. It provides an easy way to access the actual status of the modules on the hypervisors, and shows the current status of the mapping between OVN network elements and IOModules. 20 | 21 | * **Main Logic**. This module implements the logic for deploying the network services across the different compute nodes. It receives a new service network request from OVN, process it and then uses the hover ctrl interface to deploy those services in the different compute nodes. 22 | 23 | * **Hover Controller**. The hover controller is a wrapper to send command to the hover instances using a RESTful API. 24 | 25 | * **IOModules Repository**. This module is a local Repository that contains the implementation of the different IOModules. 26 | -------------------------------------------------------------------------------- /iomodules/dhcp/README.md: -------------------------------------------------------------------------------- 1 | # DHCP Server IOModule 2 | 3 | This module implements a basic version of a DHCP server in user space. 4 | 5 | 6 | ## API 7 | 8 | - **ConfigureParameters(...)** 9 | Configures the parameters required to the operation of the DHCP server. 10 | The received arguments are the same used in the YAML configuration file. 11 | 12 | ## How to use 13 | 14 | Using iovisor-ovn daemon in standalone mode, the user can deploy and configure a single or a chain of IOModules. 15 | The entire setup can be deployed starting from a YAML configuration file. 16 | 17 | ```bash 18 | $GOPATH/bin/iovisorovnd -file 19 | ``` 20 | 21 | Some examples are available in [/examples](./../../examples/) folder: 22 | * [DHCP](./../../examples/dhcp/) 23 | 24 | ### YAML Configuration Format 25 | 26 | The following is an example of the configuration of a dhcp server: 27 | ``` 28 | [...] 29 | - name: mydhcp 30 | type: dhcp 31 | config: 32 | netmask: 255.255.255.0 33 | addr_low: 192.168.1.100 34 | addr_high: 192.168.1.150 35 | dns: 8.8.8.8 36 | router: 192.168.1.1 37 | lease_time: 3600 38 | server_ip: 192.168.1.250 39 | server_mac: "b6:87:f8:5a:40:23" 40 | [...] 41 | ``` 42 | 43 | - **netmask**: mask of the network segment where the DHCP server is. 44 | - **addr_low**: first ip address that the server can assign. 45 | - **addr_high**: last ip address that the server can assign. 46 | - **dns**: DNS ip address that the server offers to the clients. 47 | - **router**: default gateway assigned to clients. 48 | - **lease_time**: default time that an address is leased to a client. 49 | - **server_ip**: IP address of the DHCP server. 50 | - **mac_ip**: MAC address of the DHCP server. 51 | 52 | ## Limitations 53 | 54 | - This module only can be connected to a single port 55 | - Packets are always sent to the L2 broadcast address, this is supported 56 | by some client implementations but is not full protocol compliant. 57 | -------------------------------------------------------------------------------- /install_alone.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | _pwd=$(pwd) 4 | 5 | # install bcc and dependencies 6 | echo "installing bcc dependencies" 7 | sudo apt-get install -y bison build-essential cmake flex git libedit-dev \ 8 | libllvm3.7 llvm-3.7-dev libclang-3.7-dev python zlib1g-dev libelf-dev 9 | 10 | # echo installing bcc 11 | cd $DATA_DIR 12 | rm -rf bcc # be sure that there is not al already git clone of bcc 13 | git clone https://github.com/iovisor/bcc.git 14 | cd bcc 15 | # bcc master compatibility with gobpf is broken, use an old version while 16 | # the issue is solved 17 | git checkout b79b589a2dc663431e3a9489178c1ada6f12e8b7 18 | mkdir build; cd build 19 | cmake .. -DCMAKE_INSTALL_PREFIX=/usr 20 | make -j 21 | sudo make install 22 | 23 | echo "installing go" 24 | curl -O https://storage.googleapis.com/golang/go1.6.2.linux-amd64.tar.gz 25 | tar -xvf go1.6.2.linux-amd64.tar.gz 26 | 27 | sudo rm -rf /usr/local/go 28 | sudo mv go /usr/local 29 | 30 | sudo rm -f /usr/bin/go 31 | sudo ln -s /usr/local/go/bin/go /usr/bin 32 | 33 | export GOPATH=$HOME/go 34 | 35 | # Hover 36 | echo "installing hover dependencies" 37 | go get github.com/vishvananda/netns 38 | go get github.com/willf/bitset 39 | go get github.com/gorilla/mux 40 | # to pull customized fork of netlink 41 | go get github.com/vishvananda/netlink 42 | cd $HOME/go/src/github.com/vishvananda/netlink 43 | git remote add drzaeus77 https://github.com/drzaeus77/netlink 44 | git fetch drzaeus77 45 | git reset --hard drzaeus77/master 46 | 47 | go get github.com/iovisor/gobpf 48 | go get github.com/songgao/water 49 | 50 | echo "installing hover" 51 | go get -d github.com/iovisor/iomodules/hover 52 | # use custom version of hover 53 | cd $GOPATH/src/github.com/iovisor/iomodules 54 | git remote add mvbpolito https://github.com/mvbpolito/iomodules 55 | git fetch mvbpolito 56 | git reset --hard mvbpolito/master 57 | go install github.com/iovisor/iomodules/hover/hoverd 58 | 59 | echo "installing iovisor ovn" 60 | go get github.com/iovisor/iovisor-ovn/iovisorovnd 61 | 62 | cd $_pwd 63 | -------------------------------------------------------------------------------- /iomodules/nat/README.md: -------------------------------------------------------------------------------- 1 | # NAT IOModule 2 | 3 | This module is a NAT that implements source address translation. In particular PAT algorithm is applied: source IP Address and Ports are changed in order to hide intern private ip addresses and exit on the internet (or another network) with only one public IP address. 4 | 5 | *notes*: 6 | * first port is always attached to internal network, second port to external one. 7 | * nat iomodule should be part of the code of the router. This is not possible for framework issues (hover does not allow to use 1+ eBPF programs inside the same iomodule). 8 | * this is a *transparent nat*: 9 | * always attach a nat to a router. 10 | * the layer 2 (arp request, layer 2 rewrite) is managed by the router. 11 | * the nat only modifies packet layers 3-4 12 | 13 | ## API: 14 | 15 | * **SetPublicIp(ip string)**: Set public ip address 16 | * ip: public ip address. (e.g. 10.10.1.100) 17 | 18 | ## How to use 19 | 20 | Using iovisor-ovn daemon in standalone mode, the user can deploy and configure a single or a chain of IOModules. 21 | The entire setup can be deployed starting from a YAML configuration file. 22 | 23 | ```bash 24 | $GOPATH/bin/iovisorovnd -file 25 | ``` 26 | 27 | Some examples are available in [/examples](./../../examples/) folder: 28 | * [Nat and Router](./../../examples/nat_router/) 29 | 30 | Please note that NAT IOModule Must be deployed attached to a Router. 31 | 32 | ### YAML Configuration Format 33 | 34 | The following is an example of the configuration of a NAT: 35 | ``` 36 | [...] 37 | - name: Nat 38 | type: nat 39 | config: 40 | public_ip: 10.10.1.100 41 | 42 | [...] 43 | ``` 44 | 45 | * **public_ip**: defines public ip address. 46 | 47 | ## Limitations 48 | 49 | * The first port of the nat is always attached to the internal network. 50 | * The second port of the nat is always attached to the public network. 51 | * No cleanup is performed on the nat tables entries 52 | * The mechanism to choose the source port is incremental starting from port 1025. 53 | -------------------------------------------------------------------------------- /examples/nat-one-to-one_router/README.md: -------------------------------------------------------------------------------- 1 | # Router 2 | 3 | In this example a router module is deployed and three virtual network interfaces 4 | in different network namespaces are connected to it. 5 | 6 | *configuration:* 7 | * ns1 (ip 10.0.1.1/24 dg 10.0.1.254) 8 | * ns2 (ip 10.0.1.2/24 dg 10.0.1.254) 9 | * ns3 (ip 10.10.1.1/24 dg 10.10.1.254) 10 | 11 | ns1,ns2 <--> Switch1 <--> (10.0.1.254/24)--Router--(10.10.1.254) <--> OneToOne_Nat <--> ns3 12 | 13 | *NAT Translation rules* 14 | * 10.0.1.1 -> 130.192.1.1 15 | * 10.0.1.2 -> 130.192.1.2 16 | 17 | ## Preparing the network namespaces 18 | 19 | Execute the [setup.sh](./setup.sh) script: 20 | 21 | ```bash 22 | sudo ./setup.sh 23 | ``` 24 | 25 | ## Launching hover 26 | 27 | Before deploying the router, it is necessary to launch the hover daemon. 28 | Please note to kill other instances of hover previously running. 29 | 30 | ```bash 31 | export GOPATH=$HOME/go 32 | sudo $GOPATH/bin/hoverd -listen 127.0.0.1:5002 33 | ``` 34 | 35 | ## Deploying the router 36 | 37 | The [nat_router.yaml](./nat_router.yaml) file contains the configuration of the router 38 | and its connections to the external interfaces. 39 | 40 | To launch the example please execute: 41 | 42 | ```bash 43 | export GOPATH=$HOME/go 44 | cd $GOPATH/src/github.com/iovisor/iovisor-ovn/examples/switch 45 | $GOPATH/bin/iovisorovnd -file nat_router.yaml -hover http://127.0.0.1:5002 46 | ``` 47 | 48 | ## Testing connectivity 49 | 50 | In order to allow the arp tables to be correctly completed, please run [arp.sh](./arp.sh) script: 51 | 52 | ```bash 53 | sudo ./arp.sh 54 | ``` 55 | 56 | Now you are able to test the connectivity TCP and UDP using netcat (nc). Example: 57 | 58 | ```bash 59 | # Launch netcat server in ns3 60 | sudo ip netns exec ns3 nc -l 8000 61 | # Launch netcat client in ns1 62 | sudo ip netns exec ns1 nc 10.10.1.1 8000 63 | # Launch netcat client in ns2 64 | sudo ip netns exec ns2 nc 10.10.1.1 8000 65 | ``` 66 | 67 | ## Debugging 68 | 69 | In order to see the debug output generated by the IOModule we suggest to see the result of the print on the trace_pipe. 70 | 71 | ```bash 72 | sudo su 73 | cd /sys/kernel/debug/tracing/ 74 | cat trace_pipe 75 | ``` 76 | -------------------------------------------------------------------------------- /iomodules/onetoonenat/README.md: -------------------------------------------------------------------------------- 1 | # NAT IOModule 2 | 3 | This module is a One to One NAT that implements internal to external and reverse natting. 4 | In particular each internal ip address (if mapped), is translated into a new external address. 5 | 6 | *notes*: 7 | * first port is always attached to internal network, second port to external one. 8 | * nat iomodule should be part of the code of the router. This is not possible for framework issues (hover does not allow to use 1+ eBPF programs inside the same iomodule). 9 | * this is a *transparent nat*: 10 | * always attach a nat to a router. 11 | * the layer 2 (arp request, layer 2 rewrite) is managed by the router. 12 | * the nat only modifies packet layers 3 13 | 14 | ## API: 15 | 16 | * **SetAddressAssociation(internal_ip string, external_ip string)**: Set the NAT rules. 17 | * internal_ip: internal ip address. 18 | * external_ip: external ip address. 19 | 20 | 21 | ## How to use 22 | 23 | Using iovisor-ovn daemon in standalone mode, the user can deploy and configure a single or a chain of IOModules. 24 | The entire setup can be deployed starting from a YAML configuration file. 25 | 26 | ```bash 27 | $GOPATH/bin/iovisorovnd -file 28 | ``` 29 | 30 | Some examples are available in [/examples](./../../examples/) folder: 31 | * [Nat One-to-One and Router](./../../examples/nat-one-to-one_router/) 32 | 33 | Please note that NAT One-to-One IOModule Must be deployed attached to a Router. 34 | 35 | ### YAML Configuration Format 36 | 37 | The following is an example of the configuration of a NAT: 38 | ``` 39 | [...] 40 | - name: Nat 41 | type: onetoonenat 42 | config: 43 | nat_entries: 44 | - internal_ip: 10.0.1.1 45 | external_ip: 130.192.1.1 46 | 47 | - internal_ip: 10.0.1.2 48 | external_ip: 130.192.1.2 49 | 50 | [...] 51 | ``` 52 | 53 | * **nat_entries**: defines the ip mapping. 54 | * **internl_ip**: is the internal IP address. 55 | * **external_ip**: is the correspondent external IP address. 56 | 57 | ## Limitations 58 | 59 | * The first port of the nat is always attached to the internal network. 60 | * The second port of the nat is always attached to the public network. 61 | -------------------------------------------------------------------------------- /examples/nat_router/README.md: -------------------------------------------------------------------------------- 1 | # Router and NAT 2 | 3 | In this example we use a router with a NAT. 4 | In particular we have: 5 | - 2 switches 6 | - a router connected to a NAT 7 | 8 | *configuration:* 9 | * ns1 (ip 10.0.1.1/24 dg 10.0.1.254) 10 | * ns2 (ip 10.0.2.1/24 dg 10.0.2.254) 11 | * ns3 (ip 10.10.1.1/24 dg 10.10.1.100) 12 | 13 | *topology* 14 | ```bash 15 | ns1 <--> Switch1 16 | | 17 | | 18 | (10.0.1.254/24) 19 | | 20 | Router <--> Nat (10.10.1.100) <--> ns3 21 | | 22 | (10.0.2.254/24) 23 | | 24 | | 25 | ns2 <--> Switch2 26 | ``` 27 | 28 | ## Preparing the network namespaces 29 | 30 | Execute the [setup.sh](./setup.sh) script: 31 | 32 | ```bash 33 | sudo ./setup.sh 34 | ``` 35 | 36 | ## Launching hover 37 | 38 | Before deploying the router, it is necessary to launch the hover daemon. 39 | Please note to kill other instances of hover previously running. 40 | 41 | ```bash 42 | export GOPATH=$HOME/go 43 | sudo $GOPATH/bin/hoverd -listen 127.0.0.1:5002 44 | ``` 45 | 46 | ## Deploying the topology 47 | 48 | The [nat_router.yaml](./nat_router.yaml) file contains the configuration script. 49 | To launch the example please execute: 50 | 51 | ```bash 52 | export GOPATH=$HOME/go 53 | cd $GOPATH/src/github.com/iovisor/iovisor-ovn/examples/switch 54 | $GOPATH/bin/iovisorovnd -file nat_router.yaml -hover http://127.0.0.1:5002 55 | ``` 56 | 57 | ## Testing connectivity 58 | 59 | In order to allow the arp tables to be correctly completed, please run [arp.sh](./arp.sh) script: 60 | 61 | ```bash 62 | sudo ./arp.sh 63 | ``` 64 | 65 | Now you are able to test the connectivity TCP and UDP using netcat (nc). Example: 66 | 67 | ```bash 68 | # Launch netcat server in ns3 69 | sudo ip netns exec ns3 nc -l 8000 70 | # Launch netcat client in ns1 71 | sudo ip netns exec ns1 nc 10.10.1.1 8000 72 | # Launch netcat client in ns2 73 | sudo ip netns exec ns2 nc 10.10.1.1 8000 74 | ``` 75 | 76 | ## Debugging 77 | 78 | In order to see the debug output generated by the IOModule we suggest to see the result of the print on the trace_pipe. 79 | 80 | ```bash 81 | sudo su 82 | cd /sys/kernel/debug/tracing/ 83 | cat trace_pipe 84 | ``` 85 | -------------------------------------------------------------------------------- /examples/router/README.md: -------------------------------------------------------------------------------- 1 | # Router 2 | 3 | In this example a router module is deployed and three virtual network interfaces 4 | in different network namespaces are connected to it. 5 | In this case the three virtual network interfaces are configured in different 6 | subnets, then a router mechanishm is needed to allow them to exchange data packets. 7 | Connectivity is tested by pinging between the different namespaces 8 | 9 |
10 | 11 | ## Preparing the network namespaces 12 | 13 | The configuration of the network namespaces and the virtual ethernet interfaces 14 | is very similar to the switch case. 15 | The main difference here is that the three network interfaces are in different 16 | subnets and a default route is configured on each namespace. 17 | 18 | Execute the [setup.sh](./setup.sh) script: 19 | 20 | ```bash 21 | sudo ./setup.sh 22 | ``` 23 | 24 | ## Launching hover 25 | 26 | Before deploying the router, it is necessary to launch the hover daemon. 27 | Please note to kill other instances of hover previously running. 28 | 29 | ```bash 30 | export GOPATH=$HOME/go 31 | sudo $GOPATH/bin/hoverd -listen 127.0.0.1:5002 32 | ``` 33 | 34 | ## Deploying the router 35 | 36 | The [router.yaml](./router.yaml) file contains the configuration of the router 37 | and its connections to the external interfaces. 38 | 39 | To launch the example please execute: 40 | 41 | ```bash 42 | export GOPATH=$HOME/go 43 | cd $GOPATH/src/github.com/iovisor/iovisor-ovn/examples/router 44 | $GOPATH/bin/iovisorovnd -file router.yaml -hover http://127.0.0.1:5002 45 | ``` 46 | 47 | ## Testing connectivity 48 | 49 | Now you are able to test the connectivity pinging between the network interfaces 50 | in the different network spaces, for example: 51 | 52 | ```bash 53 | # ping ns2 from ns1 54 | sudo ip netns exec ns1 ping 10.0.2.100 55 | # ping ns3 from ns1 56 | sudo ip netns exec ns1 ping 10.0.3.100 57 | # ping ns1 from ns3 58 | sudo ip netns exec ns3 ping 10.0.1.100 59 | ``` 60 | 61 | ## Debugging 62 | 63 | In order to see the debug output generated by the IOModule we suggest to see the result of the print on the trace_pipe. 64 | 65 | ```bash 66 | sudo su 67 | cd /sys/kernel/debug/tracing/ 68 | cat trace_pipe 69 | ``` 70 | -------------------------------------------------------------------------------- /iomodules/iomodules.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package iomodules 15 | 16 | import ( 17 | "github.com/iovisor/iovisor-ovn/hover" 18 | l "github.com/op/go-logging" 19 | ) 20 | 21 | var log = l.MustGetLogger("iomodules") 22 | 23 | type IoModule interface { 24 | GetModuleId() string 25 | Deploy() (err error) 26 | Destroy() (err error) 27 | AttachExternalInterface(name string) (err error) 28 | DetachExternalInterface(name string) (err error) 29 | AttachToIoModule(IfaceId int, name string) (err error) 30 | DetachFromIoModule(name string) (err error) 31 | Configure(conf interface{}) (err error) 32 | } 33 | 34 | // this function attaches to modules together. It performs the reques to hover 35 | // and then it calls the AttachToIoModule of each module to register the interface 36 | func AttachIoModules(c *hover.Client, 37 | m1 IoModule, ifaceName1 string, m2 IoModule, ifaceName2 string) (err error) { 38 | 39 | link_err, link := c.LinkPOST(m1.GetModuleId(), m2.GetModuleId()) 40 | if link_err != nil { 41 | log.Errorf("%s", link_err) 42 | return 43 | } 44 | 45 | // hover does not guarantee that the order of the link is conserved, then 46 | // it is necessary to check it explicitly to realize the interface id 47 | // inside each module 48 | m1id := -1 49 | m2id := -1 50 | 51 | if link.From == m1.GetModuleId() { 52 | m1id = link.FromId 53 | m2id = link.ToId 54 | } else { 55 | m1id = link.ToId 56 | m2id = link.FromId 57 | } 58 | 59 | if err := m1.AttachToIoModule(m1id, ifaceName1); err != nil { 60 | log.Errorf("%s", err) 61 | return err 62 | } 63 | 64 | if err := m2.AttachToIoModule(m2id, ifaceName2); err != nil { 65 | log.Errorf("%s", err) 66 | return err 67 | } 68 | 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /hover/hoverapi.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package hover 15 | 16 | import ( 17 | _ "net" 18 | "net/http" 19 | ) 20 | 21 | type Client struct { 22 | client *http.Client 23 | baseUrl string 24 | id string 25 | controller *Controller 26 | } 27 | 28 | func NewClient() *Client { 29 | client := &http.Client{} 30 | controller := &Controller{} 31 | d := &Client{ 32 | client: client, 33 | controller: controller, 34 | } 35 | 36 | return d 37 | } 38 | 39 | func (d *Client) Init(baseUrl string) error { 40 | d.baseUrl = baseUrl 41 | 42 | err := d.controller.Init("0.0.0.0:7777") 43 | if err != nil { 44 | log.Error("Error initializing controller: ", err) 45 | return err 46 | } 47 | 48 | go d.controller.Run() 49 | 50 | err = d.ControllerPOST("127.0.0.1:7777") 51 | if err != nil { 52 | log.Error("Error in ControllerPOST", err) 53 | return err 54 | } 55 | 56 | return nil 57 | } 58 | 59 | func (d *Client) GetController() *Controller { 60 | return d.controller 61 | } 62 | 63 | type Module struct { 64 | Id string `json:"id"` 65 | ModuleType string `json:"module_type"` 66 | DisplayName string `json:"display_name"` 67 | Perm string `json:"permissions"` 68 | Config map[string]interface{} `json:"config"` 69 | } 70 | 71 | type ExternalInterface struct { 72 | Id string `json:"id"` 73 | Name string `json:"name"` 74 | } 75 | 76 | type Link struct { 77 | Id string `json:"id"` 78 | From string `json:"from"` 79 | To string `json:"to"` 80 | FromId int `json:"from-id"` 81 | ToId int `json:"to-id"` 82 | } 83 | 84 | type TableEntry struct { 85 | Key string `json:"key"` 86 | Value string `json:"value"` 87 | } 88 | -------------------------------------------------------------------------------- /hover/printFormat.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package hover 15 | 16 | import ( 17 | "fmt" 18 | "strings" 19 | ) 20 | 21 | func LinkPrint(link Link) { 22 | fmt.Printf("link-id:%15s from: %10s (%d) to: %10s (%d)\n", link.Id, link.From, link.FromId, link.To, link.ToId) 23 | } 24 | 25 | func LinkListPrint(linkList map[string]Link) { 26 | for _, link := range linkList { 27 | LinkPrint(link) 28 | } 29 | } 30 | 31 | func ModulePrint(module Module) { 32 | fmt.Printf("module-id:%8s display_name:%8s module_type:%10s\n", module.Id, module.DisplayName, module.ModuleType) 33 | // if module.Config["code"] != nil { 34 | // PrintFirstNLines(module.Config["code"].(string), 10) 35 | // fmt.Printf("[...]\n\n") 36 | // } 37 | } 38 | 39 | func ModuleListPrint(moduleList map[string]Module) { 40 | for _, module := range moduleList { 41 | ModulePrint(module) 42 | } 43 | } 44 | 45 | func ExternalInterfacePrint(externalInterface ExternalInterface) { 46 | fmt.Printf("interface-id:%15s id: %5s\n", externalInterface.Name, externalInterface.Id) 47 | } 48 | 49 | func ExternalInterfacesListPrint(externalInterfacesList map[string]ExternalInterface) { 50 | for _, externalInterface := range externalInterfacesList { 51 | ExternalInterfacePrint(externalInterface) 52 | } 53 | } 54 | 55 | func TablePrint(table map[string]TableEntry) { 56 | for _, tableEntry := range table { 57 | TableEntryPrint(tableEntry) 58 | } 59 | } 60 | 61 | func TableEntryPrint(tableEntry TableEntry) { 62 | fmt.Printf("key: %15s value: %15s\n", tableEntry.Key, tableEntry.Value) 63 | } 64 | 65 | func PrintFirstNLines(s string, nlines int) { 66 | stringArray := strings.Split(s, "\n") 67 | for i := 0; i < nlines && i < len(stringArray); i++ { 68 | fmt.Printf("%s\n", stringArray[i]) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /iomodules/README.md: -------------------------------------------------------------------------------- 1 | # IOModules 2 | 3 | This folder contains the different IOModules that are implemented. 4 | Each IOModule is composed of two source files, the `xxx.go` file contains the datapath implementation while the `xxxAPI.go` contains the management functions. 5 | 6 | ## IOModules API 7 | 8 | Inside each IOModule package there is defined the `Create()` function that returns an instance of such module. That instance implements the functions defined in the `IOModule` interface (defined in `iomodules.go`) that can be used to perform actions over the module: 9 | 10 | - **GetModuleId()** 11 | Returns the ID assgined to the module by Hover. 12 | 13 | - **Deploy()** 14 | Loads the IOModule into the system. 15 | 16 | - **Destroy()** 17 | Unloads the IOModule. 18 | Before calling this function all the links to external interfaces and other modules should be removed. 19 | 20 | - **AttachExternalInterface(name string)** 21 | Attaches the IOModule to a given external interface. 22 | 23 | - **DetachExternalInterface(name string)** 24 | Dettaches the IOModule from a network interface. 25 | 26 | - **AttachToIoModule(IfaceId int, name string)** 27 | This function is used by a upper layer to create a link between two IOModules. The creation of the link in this case is responsability of the upper layer, the module should only configure its internal data structures. 28 | 29 | - **DetachFromIoModule(name string)** 30 | Removes the connection to another IOModule. 31 | 32 | - **Configure(conf interface{})** 33 | Performs the configuration of the parameters of the IOModule. 34 | The data structure to be passed is defined by each IOModule. 35 | 36 | Additional to those functions, the `iomodules` package defines: 37 | 38 | - **AttachIoModules()** 39 | Creates a link between the two modules and then calls the `AttachToIoModule()` function on each module to update its internal sctructures. 40 | 41 | - **DetachIoModules()** 42 | TBD 43 | 44 | #How to use 45 | 46 | IOModules can be deployed in two different ways: 47 | 48 | * Using **IOModules APIs**, and importing the correspondent package it's possible to write your own program that exploit the APIs to Deploy, Destroy, Attach, Configure an IOModule. 49 | 50 | * Using **iovisorovnd in standalone mode** it's possible to deploy a single or a chain of IOModules using a YAML configuration file. 51 | Please read [README_STANDALONE](./../README_STANDALONE.md#how-to-use). 52 | -------------------------------------------------------------------------------- /iomodules/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package iomodules 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "net" 21 | ) 22 | 23 | func MacToHexadecimalString(mac net.HardwareAddr) string { 24 | s := mac.String() 25 | 26 | var buffer bytes.Buffer 27 | buffer.WriteString("0x") 28 | buffer.WriteString(s[0:2]) 29 | buffer.WriteString(s[3:5]) 30 | buffer.WriteString(s[6:8]) 31 | buffer.WriteString(s[9:11]) 32 | buffer.WriteString(s[12:14]) 33 | buffer.WriteString(s[15:17]) 34 | 35 | return buffer.String() 36 | } 37 | 38 | func MacToHexadecimalStringBigEndian(mac net.HardwareAddr) string { 39 | s := mac.String() 40 | 41 | var buffer bytes.Buffer 42 | buffer.WriteString("0x") 43 | buffer.WriteString(s[15:17]) 44 | buffer.WriteString(s[12:14]) 45 | buffer.WriteString(s[9:11]) 46 | buffer.WriteString(s[6:8]) 47 | buffer.WriteString(s[3:5]) 48 | buffer.WriteString(s[0:2]) 49 | 50 | return buffer.String() 51 | } 52 | 53 | func IpToHex(ip net.IP) string { 54 | if ip.To4() != nil { 55 | ba := []byte(ip.To4()) 56 | ipv4HexStr := fmt.Sprintf("0x%02x%02x%02x%02x", 57 | ba[0], ba[1], ba[2], ba[3]) 58 | return ipv4HexStr 59 | } 60 | 61 | return "" 62 | } 63 | 64 | func IpToHexBigEndian(ip net.IP) string { 65 | if ip.To4() != nil { 66 | ba := []byte(ip.To4()) 67 | ipv4HexStr := fmt.Sprintf("0x%02x%02x%02x%02x", 68 | ba[3], ba[2], ba[1], ba[0]) 69 | return ipv4HexStr 70 | } 71 | 72 | return "" 73 | } 74 | 75 | func ParseIPv4Mask(s string) net.IPMask { 76 | mask := net.ParseIP(s) 77 | if mask == nil { 78 | return nil 79 | } 80 | return net.IPv4Mask(mask[12], mask[13], mask[14], mask[15]) 81 | } 82 | 83 | func NetmaskToHexBigEndian(netmask net.IPMask) string { 84 | //ip := net.ParseIP(netmask.String()) 85 | return IpToHexBigEndian(net.IP(netmask)) 86 | } 87 | -------------------------------------------------------------------------------- /examples/dhcp/README.md: -------------------------------------------------------------------------------- 1 | # DHCP Server 2 | 3 | In this example a dhcp and a switch modules are deployed, three virtual network interfaces are connected to the switch. 4 | 5 | The three virtual network interfaces are in different network namespaces and they do not have an IP address, this is provided then by the dhcp server. 6 | 7 |
8 | 9 | ## Preparing the network namespaces 10 | 11 | Execute the [setup.sh](./setup.sh) script to create the different network namespace and veth pairs: 12 | 13 | ```bash 14 | sudo ./setup.sh 15 | ``` 16 | 17 | ## Launching hover 18 | 19 | Before deploying the router, it is necessary to launch the hover daemon. 20 | Please note to kill other instances of hover previously running. 21 | 22 | ```bash 23 | export GOPATH=$HOME/go 24 | sudo $GOPATH/bin/hoverd -listen 127.0.0.1:5002 25 | ``` 26 | 27 | ## Deploying the DHCP server 28 | 29 | The [dhcp.yaml](./dhcp.yaml) file contains the configuration of the dhcp and switch modules and their connections to the external interfaces. 30 | 31 | To launch the example please execute: 32 | 33 | ```bash 34 | export GOPATH=$HOME/go 35 | cd $GOPATH/src/github.com/iovisor/iovisor-ovn/examples/dhcp 36 | $GOPATH/bin/iovisorovnd -file dhcp.yaml -hover http://127.0.0.1:5002 37 | ``` 38 | 39 | ## Testing connectivity 40 | 41 | Before pinging between the network interfaces, they have to receive an IP address from the DHCP client. 42 | Execute the following commands to request ip addresses to the server: 43 | 44 | ```bash 45 | sudo ip netns exec ns1 dhclient 46 | sudo ip netns exec ns2 dhclient 47 | sudo ip netns exec ns3 dhclient 48 | ``` 49 | 50 | In order to know the IP address assigned to each interface execute: 51 | 52 | ```bash 53 | sudo ip netns exec ns1 ifconfig 54 | sudo ip netns exec ns2 ifconfig 55 | sudo ip netns exec ns3 ifconfig 56 | ``` 57 | 58 | Now you are able to test the connectivity pinging between the network interfaces 59 | in the different network spaces, for example: 60 | 61 | ```bash 62 | # ping ns2 from ns1 63 | sudo ip netns exec ns1 ping 64 | # ping ns3 from ns1 65 | sudo ip netns exec ns1 ping 66 | # ping ns1 from ns3 67 | sudo ip netns exec ns3 ping 68 | ``` 69 | 70 | ## Debugging 71 | 72 | In order to see the debug output generated by the IOModule we suggest to see the result of the print on the trace_pipe. 73 | 74 | ```bash 75 | sudo su 76 | cd /sys/kernel/debug/tracing/ 77 | cat trace_pipe 78 | ``` 79 | -------------------------------------------------------------------------------- /iomodules/dhcp/server.go: -------------------------------------------------------------------------------- 1 | // This file is a slight modified version of https://github.com/krolaw/dhcp4/blob/master/example_test.go 2 | package dhcp 3 | 4 | import ( 5 | dhcp "github.com/mvbpolito/dhcp4" 6 | 7 | "math/rand" 8 | "net" 9 | "time" 10 | ) 11 | 12 | type lease struct { 13 | nic string // Client's CHAddr 14 | expiry time.Time // When the lease expires 15 | } 16 | 17 | type DHCPHandler struct { 18 | ip net.IP // Server IP to use 19 | options dhcp.Options // Options to send to DHCP Clients 20 | start net.IP // Start of IP range to distribute 21 | leaseRange int // Number of IPs to distribute (starting from start) 22 | leaseDuration time.Duration // Lease period 23 | leases map[int]lease // Map to keep track of leases 24 | } 25 | 26 | func (h *DHCPHandler) ServeDHCP(p dhcp.Packet, msgType dhcp.MessageType, options dhcp.Options) (d dhcp.Packet) { 27 | 28 | switch msgType { 29 | 30 | case dhcp.Discover: 31 | free, nic := -1, p.CHAddr().String() 32 | for i, v := range h.leases { // Find previous lease 33 | if v.nic == nic { 34 | free = i 35 | goto reply 36 | } 37 | } 38 | if free = h.freeLease(); free == -1 { 39 | return 40 | } 41 | reply: 42 | return dhcp.ReplyPacket(p, dhcp.Offer, h.ip, dhcp.IPAdd(h.start, free), h.leaseDuration, 43 | h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList])) 44 | 45 | case dhcp.Request: 46 | if server, ok := options[dhcp.OptionServerIdentifier]; ok && !net.IP(server).Equal(h.ip) { 47 | return nil // Message not for this dhcp server 48 | } 49 | reqIP := net.IP(options[dhcp.OptionRequestedIPAddress]) 50 | if reqIP == nil { 51 | reqIP = net.IP(p.CIAddr()) 52 | } 53 | 54 | if len(reqIP) == 4 && !reqIP.Equal(net.IPv4zero) { 55 | if leaseNum := dhcp.IPRange(h.start, reqIP) - 1; leaseNum >= 0 && leaseNum < h.leaseRange { 56 | if l, exists := h.leases[leaseNum]; !exists || l.nic == p.CHAddr().String() { 57 | h.leases[leaseNum] = lease{nic: p.CHAddr().String(), expiry: time.Now().Add(h.leaseDuration)} 58 | return dhcp.ReplyPacket(p, dhcp.ACK, h.ip, reqIP, h.leaseDuration, 59 | h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList])) 60 | } 61 | } 62 | } 63 | return dhcp.ReplyPacket(p, dhcp.NAK, h.ip, nil, 0, nil) 64 | 65 | case dhcp.Release, dhcp.Decline: 66 | nic := p.CHAddr().String() 67 | for i, v := range h.leases { 68 | if v.nic == nic { 69 | delete(h.leases, i) 70 | break 71 | } 72 | } 73 | } 74 | return nil 75 | } 76 | 77 | func (h *DHCPHandler) freeLease() int { 78 | now := time.Now() 79 | b := rand.Intn(h.leaseRange) // Try random first 80 | for _, v := range [][]int{[]int{b, h.leaseRange}, []int{0, b}} { 81 | for i := v[0]; i < v[1]; i++ { 82 | if l, ok := h.leases[i]; !ok || l.expiry.Before(now) { 83 | return i 84 | } 85 | } 86 | } 87 | return -1 88 | } 89 | -------------------------------------------------------------------------------- /hover/controller.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package hover 15 | 16 | import ( 17 | "encoding/gob" 18 | "fmt" 19 | "net" 20 | "io" 21 | ) 22 | 23 | type SlowPathCallBack func(*PacketIn) (error) 24 | 25 | type PacketInMd struct { 26 | Module_id uint16 27 | Port_id uint16 28 | Packet_len uint32 29 | Reason uint16 30 | Metadata [3]uint32 31 | } 32 | 33 | type PacketIn struct { 34 | Md PacketInMd 35 | Data []byte 36 | } 37 | 38 | const ( 39 | INGRESS = 0 40 | EGRESS = 1 41 | ) 42 | 43 | type PacketOut struct { 44 | Module_id uint16 45 | Port_id uint16 46 | Sense uint16 /* ingress = 0, egress = 1 */ 47 | Data []byte 48 | } 49 | 50 | func (p *PacketIn) ToString() string { 51 | return fmt.Sprintf("Module_id: %d\nPort_id: %d\nPacket_len: %d\nReason: %d\n", 52 | p.Md.Module_id, p.Md.Port_id, p.Md.Packet_len, p.Md.Reason) 53 | } 54 | 55 | type Controller struct { 56 | callbacks map[uint16]SlowPathCallBack 57 | listenaddr string 58 | conn net.Conn 59 | } 60 | 61 | func (c *Controller) Init(listenaddr string) (err error) { 62 | c.callbacks = make(map[uint16]SlowPathCallBack) 63 | c.listenaddr = listenaddr 64 | return nil 65 | } 66 | 67 | func (c *Controller) RegisterCallBack(id uint16, cb SlowPathCallBack) (err error) { 68 | c.callbacks[id] = cb 69 | return nil 70 | } 71 | 72 | func (c *Controller) Run() (err error) { 73 | ln, err1 := net.Listen("tcp", c.listenaddr) 74 | if err1 != nil { 75 | return err1 76 | } 77 | 78 | c.conn, err = ln.Accept() 79 | if err != nil { 80 | return 81 | } 82 | 83 | dec := gob.NewDecoder(c.conn) 84 | 85 | log.Infof("Controller: New client connected") 86 | 87 | for { 88 | p := &PacketIn{} 89 | err1 := dec.Decode(p) 90 | if err1 == io.EOF { 91 | log.Info("Controller: Client Disconnected") 92 | return nil 93 | } else if err1 != nil { 94 | continue 95 | } 96 | //log.Info("Packet arrived: ", p.ToString()) 97 | cb, ok := c.callbacks[p.Md.Module_id] 98 | if !ok { 99 | log.Infof("Controller: Callback not found") 100 | continue 101 | } 102 | cb(p) 103 | } 104 | } 105 | 106 | func (c *Controller) SendPacketOut(p *PacketOut) (err error) { 107 | encoder := gob.NewEncoder(c.conn) 108 | encoder.Encode(p) 109 | return 110 | } 111 | -------------------------------------------------------------------------------- /iovisorovnd/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package main 15 | 16 | import ( 17 | "bufio" 18 | "flag" 19 | "os" 20 | "time" 21 | 22 | "github.com/iovisor/iovisor-ovn/cli" 23 | "github.com/iovisor/iovisor-ovn/config" 24 | l "github.com/op/go-logging" 25 | 26 | "github.com/iovisor/iovisor-ovn/common" 27 | "github.com/iovisor/iovisor-ovn/mainlogic" 28 | "github.com/iovisor/iovisor-ovn/servicetopology" 29 | ) 30 | 31 | var log = l.MustGetLogger("iovisor-ovn-daemon") 32 | 33 | func init() { 34 | 35 | flag.StringVar(&config.File, "file", "", "path to configuration file") 36 | 37 | flag.StringVar(&config.Nb, "nb", config.Nb, "nb db address:port") 38 | flag.StringVar(&config.Sb, "sb", config.Sb, "sb db address:port") 39 | flag.StringVar(&config.Ovs, "ovs", config.Ovs, "ovs db address:port") 40 | 41 | flag.StringVar(&config.NbSock, "nbsock", config.NbSock, "nb db local .sock file") 42 | flag.StringVar(&config.SbSock, "sbsock", config.SbSock, "sb db local .sock file") 43 | flag.StringVar(&config.OvsSock, "ovssock", config.OvsSock, "ovs db local .sock file") 44 | 45 | flag.BoolVar(&config.Sandbox, "sandbox", false, "connect to sandbox with local .sock files") 46 | flag.BoolVar(&config.TestEnv, "testenv", false, "enable testenv") 47 | 48 | flag.BoolVar(&config.Debug, "debug", false, "enable DEBUG level in logger") 49 | flag.BoolVar(&config.Info, "info", true, "enable INFO level in logger") 50 | 51 | flag.StringVar(&config.Hover, "hover", config.Hover, "hover url") 52 | } 53 | 54 | //Start iovisor-ovn Daemon 55 | func main() { 56 | 57 | //Parse Cmdline args 58 | flag.Parse() 59 | 60 | //Init Logger 61 | common.LogInit() 62 | 63 | if config.File != "" { 64 | // file has been passed, deploy this without connecting to OVN 65 | err := servicetopology.DeployTopology(config.File) 66 | if err != nil { 67 | log.Errorf("Error deploying topology, please verify your topology file"); 68 | } 69 | log.Infof("Press enter to close"); 70 | bufio.NewReader(os.Stdin).ReadBytes('\n') 71 | // TODO: defer call to servicetopologyUndeploy 72 | } else { 73 | // topologyFile argument was not passed, then connect to OVN 74 | config.PrintConfig() 75 | //Monitors started here! 76 | mainlogic.MainLogic() 77 | 78 | // Cli start 79 | time.Sleep(1 * time.Second) 80 | go cli.Cli(mainlogic.GetHoverClient()) 81 | 82 | quit := make(chan bool) 83 | <-quit 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /cli/help.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // Inline command line interface for debug purposes 15 | // in future this cli will be a separate go program that connects to the main iovisor-ovn daemon 16 | // in future this cli will use a cli go library (e.g. github.com/urfave/cli ) 17 | 18 | package cli 19 | 20 | import "fmt" 21 | 22 | func PrintTableUsage() { 23 | fmt.Printf("\nTable Usage\n\n") 24 | fmt.Printf(" table get\n") 25 | fmt.Printf(" table get \n") 26 | fmt.Printf(" table get \n") 27 | fmt.Printf(" table get \n") 28 | fmt.Printf(" table put \n") 29 | fmt.Printf(" table delete \n") 30 | } 31 | 32 | func PrintLinksUsage() { 33 | fmt.Printf("\nLinks Usage\n\n") 34 | fmt.Printf(" links get\n") 35 | fmt.Printf(" links get \n") 36 | fmt.Printf(" links post \n") 37 | fmt.Printf(" links delete \n") 38 | } 39 | 40 | func PrintModulesUsage() { 41 | fmt.Printf("\nModules Usage\n\n") 42 | fmt.Printf(" modules get\n") 43 | fmt.Printf(" modules get \n") 44 | fmt.Printf(" modules post \n") 45 | fmt.Printf(" modules delete \n") 46 | } 47 | 48 | func PrintMainLogicUsage() { 49 | fmt.Printf("\nMainLogic Usage\n\n") 50 | fmt.Printf(" mainlogic (-v)\n") 51 | fmt.Printf(" mainlogic switch ()\n") 52 | fmt.Printf(" mainlogic router ()\n") 53 | } 54 | 55 | func PrintOvnMonitorUsage() { 56 | fmt.Printf("\nOvnMonitor Usage\n\n") 57 | fmt.Printf(" ovnmonitor (-v)\n") 58 | fmt.Printf(" ovnmonitor switch ()\n") 59 | fmt.Printf(" ovnmonitor router ()\n") 60 | fmt.Printf(" ovnmonitor interface\n") 61 | } 62 | 63 | func PrintHelp() { 64 | fmt.Printf("\n") 65 | fmt.Printf("IOVisor-OVN Command Line Interface HELP\n\n") 66 | fmt.Printf(" interfaces, i prints /external_interfaces/\n") 67 | fmt.Printf(" modules, m prints /modules/\n") 68 | fmt.Printf(" links, l prints /links/\n") 69 | fmt.Printf(" table, t prints tables\n\n") 70 | fmt.Printf(" mainlogic, ml prints mainlogic\n") 71 | fmt.Printf(" ovnmonitor, ovn prints ovnmonitor\n\n") 72 | 73 | fmt.Printf(" help, h print help\n") 74 | fmt.Printf("\n") 75 | PrintModulesUsage() 76 | PrintLinksUsage() 77 | PrintTableUsage() 78 | PrintMainLogicUsage() 79 | PrintOvnMonitorUsage() 80 | } 81 | -------------------------------------------------------------------------------- /iomodules/router/README.md: -------------------------------------------------------------------------------- 1 | # Router IOModule 2 | 3 | This module is a Router that implements a simplified version of routing algorithm, port management and arp responder. 4 | 5 | ## API: 6 | * **AddRoutingTableEntry(network string, netmask string, outputIface string, nexthop string)**: Adds a routing table entry in the routing table of the router 7 | * network: network address to reach. (e.g. 10.0.1.0) 8 | * netmask: network address netmask. (e.g 255.255.255.0) 9 | * outputIface: output interface (router port). 10 | * nexthop: represents the next hop (e.g. 130.192.123.123). If 0 the network is locally reachable. 11 | 12 | * **AddRoutingTableEntryLocal(network string, netmask string, outputIface string)**: As previous API, but forces the nexthop parameter to 0 and force the network to be local to a router port. 13 | 14 | 15 | * **ConfigureInterface(ifaceName string, ip string, netmask string, mac string)**: Configure the interface Ip and Mac address of the router port. In addition set the correspondent routing table entry to reach the local network attachet to the port. 16 | * ifaceName: string that identifies the interface name (e.g. if the interface was previously attached to Switch1 use "Switch1") 17 | * ip: ip address of the interface 18 | * netmask: netmask address 19 | * mac: mac address of the port 20 | 21 | ## How to use 22 | 23 | Using iovisor-ovn daemon in standalone mode, the user can deploy and configure a single or a chain of IOModules. 24 | The entire setup can be deployed starting from a YAML configuration file. 25 | 26 | ```bash 27 | $GOPATH/bin/iovisorovnd -file 28 | ``` 29 | 30 | Some examples are available in [/examples](./../../examples/) folder: 31 | * [Router](./../../examples/router/) 32 | 33 | 34 | ### YAML Configuration Format 35 | 36 | The following is an example of the configuration of a router: 37 | ``` 38 | [...] 39 | - name: myrouter 40 | type: router 41 | config: 42 | interfaces: 43 | - name: Switch1 44 | ip: 10.0.1.254 45 | netmask: 255.255.255.0 46 | mac: "7e:ee:c2:01:01:01" 47 | 48 | - name: Switch2 49 | ip: 10.0.2.254 50 | netmask: 255.255.255.0 51 | mac: "7e:ee:c2:02:02:02" 52 | 53 | - name: Nat 54 | ip: 0.0.0.0 55 | netmask: 0.0.0.0 56 | mac: "7e:ee:c2:03:03:03" 57 | 58 | static_routes: 59 | - network: 130.192.0.0 60 | netmask: 255.255.0.0 61 | interface: Switch3 62 | next_hop: 10.2.2.254 63 | 64 | - network: 10.1.1.0 65 | netmask: 255.255.255.0 66 | interface: Switch4 67 | #If no next_hop set, route of type local 68 | 69 | [...] 70 | ``` 71 | 72 | - **interfaces**: defines local interfaces of the router. This primitive set automatically also the routing table entries of type local in order to reach hosts belonging to the defined network. 73 | 74 | ## Limitations 75 | 76 | * The control plane must put ordered entries into the routing table, from longest to shortest prefix. 77 | * Routing table contains maximum 10 entries. 78 | * When the router has to forward a packet to an ip address without know the mac address, sent it in broadcast. When someone performs an arp request with that address router arp table is updated and l2 address is successfully completed. 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IOVisor-OVN 2 | 3 | IOVisor-OVN extends the current [Open Virtual Networking (OVN) project](https://github.com/openvswitch/ovs/) with a new backend based on the [IOVisor](https://www.iovisor.org/) technology. 4 | In a nutshell, IOVisor-ION defines a new data plane that is semantically equivalent to the original one, mostly based on Open vSwitch. The new data plane exploits the eBPF virtual machine (also known as IOVisor) and in future it could be integrated with the eXpress Data Path (XDP) technology for improved performance. 5 | 6 | ### Why? 7 | 8 | - Sophisticated and efficient virtualized network services are becoming important, and cannot directly be realized using the match/action paradigm implemented by current virtual switches; 9 | - Complex services cannot be carried out with only OpenFlow-based switches (as OvS), and the current model that mixes different technologies (Linux containers, openFlow switches with the associated controller for the control plane, virtual machines, and more) to setup a complex network service, is hard to manage; 10 | - eBPF is integrated in the Linux kernel and allows to create and deploy (i.e., *inject*) new functions at runtime, without having to upgrade/modify anything in the hosting server. 11 | 12 | ### How? 13 | 14 | - We replace the current backend of OVN with a new implementation based on IOVisor. This proposal maintains the current OVN architecture that handles orchestration across a datacenter-wise environment, and keeps compatibility with current Cloud Management Systems as OpenStack, Apache Mesos, and other. 15 | 16 | ## Architecture 17 | 18 |
19 | 20 | IOVisor-OVN sits on side of the traditional OVN architecture, it intercepts the contents of the different databases and deploys the required network services using the IOVisor technology. 21 | 22 | For more details about the architecture please see [ARCHITECTURE](./ARCHITECTURE.md). 23 | 24 | ## Getting Started 25 | 26 | IOVisor-OVN can be used in two different ways: 27 | 28 | - **Default**: provides a network backend, based on IOVisor and IOModules, for OVN + OpenStack. 29 | Please read [README_OPENSTACK](./README_OPENSTACK.md). 30 | - **Standalone**: allows to test a single or a chain of IOModules, without the OVN integration. The entire chain can be setup with a simple yaml configuration file. 31 | Please read [README_STANDALONE](./README_STANDALONE.md). 32 | 33 | ## Examples 34 | 35 | Some examples intended to provide a step-by-step guide to playing with the existing IOModules are available in the [examples folder](/examples) and can be used in *standalone* mode. 36 | 37 | 38 | ## Repository Organization: 39 | 40 | * **iomodules**: contains eBPF code (i.e., available IOModules). 41 | * **cli**: tool that implements the command line interface of IOVisor-OVN daemon. 42 | * **config**: contains a file with the default configuration parameters used when the daemon starts. 43 | * **iovisorovnd**: contains the daemon main program entry point. 44 | * **docs**: documentation about this project, presentations, talks. 45 | * **hoverctl**: hover restful API wrapper and utilities. 46 | * **mainlogic**: tool that performs the mapping between the network configuration of OVN and IOModules. 47 | * **ovnmonitor**: monitors for OVN northbound database, southbound database and local OvS database. Implemented using libovsdb. 48 | * **examples**: examples using IOModules in the repository. 49 | 50 | ## Presentations 51 | 52 | * OVS Fall Conference, San Jose, Nov 2016: [Slides](http://openvswitch.org/support/ovscon2016/7/1245-bertrone.pdf), [Video](https://www.youtube.com/watch?v=9cmR2NuAGz0) 53 | 54 | ## Licence 55 | 56 | The IOVisor-OVN is licensed under the [Apache License, Version 2.0](./LICENSE.txt). 57 | -------------------------------------------------------------------------------- /ovnmonitor/print.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package ovnmonitor 15 | 16 | import ( 17 | "fmt" 18 | "os" 19 | "strconv" 20 | 21 | "github.com/olekukonko/tablewriter" 22 | ) 23 | 24 | func PrintLogicalSwitchByName(name string, db *OvnDB) { 25 | if sw, ok := db.Switches[name]; ok { 26 | table := tablewriter.NewWriter(os.Stdout) 27 | table.SetHeader([]string{"SWITCH-UUID", "NAME", "MODIFIED"}) 28 | table.Append([]string{sw.uuid, sw.Name, strconv.FormatBool(sw.Modified)}) 29 | table.Render() 30 | 31 | table = tablewriter.NewWriter(os.Stdout) 32 | table.SetHeader([]string{"PORTS-UUID", "NAME", "IFACE", "TYPE", "RPORT", "MODIFIED"}) 33 | for _, port := range sw.Ports { 34 | table.Append([]string{port.uuid, port.Name, port.IfaceName, port.Type, port.RouterPort, strconv.FormatBool(port.Modified)}) 35 | } 36 | table.Render() 37 | } 38 | } 39 | 40 | func PrintLogicalRouterByName(name string, db *OvnDB) { 41 | table := tablewriter.NewWriter(os.Stdout) 42 | if r, ok := db.Routers[name]; ok { 43 | table.SetHeader([]string{"ROUTERS-UUID", "NAME", "MODIFIED", "ENABLED"}) 44 | table.Append([]string{r.uuid, r.Name, strconv.FormatBool(r.Modified), strconv.FormatBool(r.Enabled)}) 45 | table.Render() 46 | 47 | table = tablewriter.NewWriter(os.Stdout) 48 | table.SetHeader([]string{"PORTS-UUID", "NAME", "NETWORKS", "MAC", "MODIFIED", "ENABLED"}) 49 | for _, port := range r.Ports { 50 | table.Append([]string{port.uuid, port.Name, port.Networks, port.Mac, strconv.FormatBool(port.Modified), strconv.FormatBool(port.Enabled)}) 51 | } 52 | table.Render() 53 | } 54 | } 55 | 56 | func PrintOvsInterfaces(db *OvnDB) { 57 | table := tablewriter.NewWriter(os.Stdout) 58 | table.SetHeader([]string{"INTERFACES-UUID", "NAME", "EXTERNAL-IDS"}) 59 | for _, iface := range db.ovsInterfaces { 60 | table.Append([]string{iface.uuid, iface.Name, iface.ExternalIdIface}) 61 | } 62 | table.Render() 63 | } 64 | 65 | func PrintLogicalSwitches(verbose bool, db *OvnDB) { 66 | table := tablewriter.NewWriter(os.Stdout) 67 | table.SetHeader([]string{"SWITCHES-UUID", "NAME", "MODIFIED"}) 68 | for _, sw := range db.Switches { 69 | table.Append([]string{sw.uuid, sw.Name, strconv.FormatBool(sw.Modified)}) 70 | } 71 | table.Render() 72 | 73 | if verbose { 74 | for swname, _ := range db.Switches { 75 | fmt.Printf("\n") 76 | PrintLogicalSwitchByName(swname, db) 77 | } 78 | } 79 | } 80 | 81 | func PrintLogicalRouters(verbose bool, db *OvnDB) { 82 | table := tablewriter.NewWriter(os.Stdout) 83 | table.SetHeader([]string{"ROUTERS-UUID", "NAME", "MODIFIED", "ENABLED"}) 84 | for _, r := range db.Routers { 85 | table.Append([]string{r.uuid, r.Name, strconv.FormatBool(r.Modified), strconv.FormatBool(r.Enabled)}) 86 | } 87 | table.Render() 88 | 89 | if verbose { 90 | for rname, _ := range db.Routers { 91 | fmt.Printf("\n") 92 | PrintLogicalRouterByName(rname, db) 93 | } 94 | } 95 | } 96 | 97 | func PrintOvnMonitor(verbose bool, db *OvnDB) { 98 | fmt.Printf("\nLogical Switches\n\n") 99 | PrintLogicalSwitches(verbose, db) 100 | fmt.Printf("\nLogical Routers\n\n") 101 | PrintLogicalRouters(verbose, db) 102 | fmt.Printf("\nInterfaces\n\n") 103 | PrintOvsInterfaces(db) 104 | } 105 | -------------------------------------------------------------------------------- /README_STANDALONE.md: -------------------------------------------------------------------------------- 1 | # IOVisor-OVN Standalone 2 | 3 | The **Standalone Mode**, allows to test a **single or a chain of IOModules**, without the OVN integration. The entire chain can be setup with a simple **YAML configuration file**. 4 | 5 | ## Installation 6 | This guide includes the instructions to install *bcc, go, hover* and *iovisor-ovn*. 7 | A Linux kernel with a version **4.9 or newer** is required, we recommend to use Ubuntu as all the examples have been tested on that platform. 8 | 9 | The simplest set of instructions for the upgrade to kernel version 4.9 is listed below. 10 | 11 | ```bash 12 | mkdir /tmp/kernel-4.9/ 13 | cd /tmp/kernel-4.9/ 14 | wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.9/linux-headers-4.9.0-040900_4.9.0-040900.201612111631_all.deb 15 | wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.9/linux-headers-4.9.0-040900-generic_4.9.0-040900.201612111631_amd64.deb 16 | wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.9/linux-image-4.9.0-040900-generic_4.9.0-040900.201612111631_amd64.deb 17 | sudo dpkg -i *.deb 18 | #Now reboot your system 19 | sudo reboot 20 | ``` 21 | 22 | ### Automatic Installation 23 | 24 | In order to automatically install all the required components please execute the following commands, please consider that they only work on Debian based distros. 25 | 26 | ```bash 27 | git clone https://github.com/iovisor/iovisor-ovn.git 28 | cd iovisor-ovn/ 29 | ./install_alone.sh 30 | ``` 31 | 32 | ### Manual Installation 33 | 34 | Please follow these steps to install the required components manually. 35 | 36 | #### bcc 37 | 38 | Follow the steps indicated in [BCC Installation Guide](https://github.com/iovisor/bcc/blob/master/INSTALL.md) in order to install bcc, please consider that the version 0.2.0 is required. 39 | 40 | #### go 41 | 42 | Please follow the [Go Installation Guide](https://golang.org/doc/install), Go 1.4 or higher is required 43 | 44 | #### hover 45 | In order to install hover please follow the steps in [Installing Hover](https://github.com/iovisor/iomodules/#installing-hover) 46 | 47 | Please be sure that this [patch](https://github.com/mvbpolito/iomodules/commit/7409078fcb158263dcc2b6b58b508e7033865d5f) is applied before the installation. 48 | 49 | #### iovisor-ovn 50 | 51 | Installing iovisor-ovn is very easy, just use the *go get* command: 52 | 53 | ```bash 54 | go get github.com/iovisor/iovisor-ovn/iovisorovnd 55 | ``` 56 | 57 | ## How to use? 58 | 59 | In this case the configuration and topology of the service topology to be deployed is passed through a .yaml file. 60 | 61 | ### Launching IOVisor-OVN in Standalone Mode 62 | 63 | In order to deploy a service topology from a file, the -file parameter should be passed to the IOVisor-OVN daemon. 64 | 65 | ``` 66 | export GOPATH=$HOME/go 67 | cd $GOPATH/src/github.com/iovisor/iovisor-ovn/examples/switch 68 | $GOPATH/bin/iovisorovnd -file -hover 69 | ``` 70 | 71 | ### Configuration File 72 | 73 | ``` 74 | # list of modules to be deployed 75 | modules: 76 | - name: modulename 77 | type: moduletype 78 | config: 79 | # module configuration 80 | 81 | # links between modules 82 | links: 83 | - from: moduleName 84 | to: moduleName 85 | 86 | # connection to network interfaces 87 | external_interfaces: 88 | - module: moduleName 89 | iface: interface name 90 | ``` 91 | The file is composed of three sections: modules, links and external_interfaces. 92 | 93 | 1. **modules**: This section contains the modules to be deployed. 94 | The name and type are mandatory, while the configuration is optional and different for each kind of IOModules. 95 | Please see the documentation of each single IOModule to get information about the configuration parameters. 96 | 97 | 2. **links**: These are the links between the different IOModules, "from" and "to" must correspond to the name of modules in the "modules" section. 98 | 99 | 3. **external_interfaces**: The connection to the network interfaces are defined in this section. Module should be a module defined in the "modules" section and iface should be the name of the interface on the system. 100 | 101 | ### Examples 102 | 103 | Some examples with a complete explanation are provided in [examples](./examples) 104 | -------------------------------------------------------------------------------- /iomodules/dhcp/dhcp_slowpath.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package dhcp 16 | 17 | import ( 18 | "errors" 19 | "net" 20 | "strconv" 21 | 22 | "github.com/google/gopacket" 23 | "github.com/google/gopacket/layers" 24 | 25 | "github.com/iovisor/iovisor-ovn/hover" 26 | ) 27 | 28 | func (m *DhcpModule) ProcessPacket(p *hover.PacketIn) (err error) { 29 | m.c <- p 30 | return nil 31 | } 32 | 33 | // the dhcp library needs an object that implements the dhcp.ServeConn to 34 | // be able to receive and send packets. In this case this interface is 35 | // directly implemented in the dhcp module. 36 | 37 | // This function is called from the dhcp library, it waits on a channel 38 | // until the ProcessPacket function injects new arrived packets there. 39 | func (m *DhcpModule) ReadFrom(b []byte) (n int, addr net.Addr, err error) { 40 | 41 | for p := range m.c { 42 | packet := gopacket.NewPacket(p.Data, layers.LayerTypeEthernet, gopacket.Lazy) 43 | 44 | ethLayer := packet.Layer(layers.LayerTypeEthernet) 45 | if ethLayer == nil { 46 | log.Errorf("Error parsing packet: Ethernet") 47 | err = errors.New("Error parsing packet: Ethernet") 48 | continue 49 | } 50 | 51 | ipLayer := packet.Layer(layers.LayerTypeIPv4) 52 | if ipLayer == nil { 53 | log.Errorf("Error parsing packet: ipv4") 54 | err = errors.New("Error parsing packet: ipv4") 55 | continue 56 | } 57 | 58 | udpLayer := packet.Layer(layers.LayerTypeUDP) 59 | if udpLayer == nil { 60 | log.Errorf("Error parsing packet: udp") 61 | err = errors.New("Error parsing packet: udp") 62 | continue 63 | } 64 | 65 | eth, _ := ethLayer.(*layers.Ethernet) 66 | ip, _ := ipLayer.(*layers.IPv4) 67 | udp, _ := udpLayer.(*layers.UDP) 68 | 69 | _ = eth 70 | 71 | udpAddr := &net.UDPAddr{ip.SrcIP, int(udp.SrcPort), ""} 72 | addr = udpAddr 73 | 74 | copy(b, udp.LayerPayload()) 75 | n = len(udp.LayerPayload()) 76 | return 77 | } 78 | 79 | return 80 | } 81 | 82 | // WriteTo in this case means send the packet to the dataplane, it requires 83 | // assemble the whole packet layers 84 | func (m *DhcpModule) WriteTo(b []byte, addr net.Addr) (n int, err error) { 85 | 86 | // TODO: For now all the packets are sent to the broadcast mac address, 87 | // this is ok for some dhclient implementations but it is not full c 88 | // complaint with the protocol specification 89 | eth := &layers.Ethernet { 90 | SrcMAC: m.mac, 91 | DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // FIXME 92 | EthernetType: layers.EthernetTypeIPv4, 93 | } 94 | 95 | ipStr, portStr, err1 := net.SplitHostPort(addr.String()) 96 | if err1 != nil { 97 | return 98 | } 99 | 100 | port, _ := strconv.Atoi(portStr) 101 | 102 | ip := &layers.IPv4{ 103 | SrcIP: m.ip, 104 | DstIP: net.ParseIP(ipStr), 105 | Protocol: layers.IPProtocolUDP, 106 | Version: 4, 107 | TTL: 64, 108 | } 109 | 110 | udp := &layers.UDP{ 111 | SrcPort: 68, 112 | DstPort: layers.UDPPort(port), 113 | } 114 | 115 | buf := gopacket.NewSerializeBuffer() 116 | opts := gopacket.SerializeOptions{ 117 | FixLengths: true, 118 | ComputeChecksums: true, 119 | } 120 | 121 | udp.SetNetworkLayerForChecksum(ip) 122 | 123 | err = gopacket.SerializeLayers(buf, opts, eth, ip, udp, gopacket.Payload(b)) 124 | if err != nil { 125 | log.Infof("Error in SerializeLayers: %s", err) 126 | return 127 | } 128 | 129 | p := &hover.PacketOut{} 130 | id, _ := strconv.Atoi(m.ModuleId[2:]) 131 | p.Module_id = uint16(id) 132 | p.Port_id = 1 133 | p.Sense = hover.EGRESS 134 | p.Data = buf.Bytes() 135 | 136 | m.hc.GetController().SendPacketOut(p) 137 | 138 | return len(b), nil 139 | } 140 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package config 15 | 16 | import ( 17 | "fmt" 18 | "net" 19 | "strconv" 20 | "time" 21 | 22 | l "github.com/op/go-logging" 23 | ) 24 | 25 | var Nb = "127.0.0.1:6641" 26 | var NbSock = "/home/matteo/ovs/tutorial/sandbox/ovnnb_db.sock" 27 | var Sb = "127.0.0.1:6642" 28 | var SbSock = "/home/matteo/ovs/tutorial/sandbox/ovnsb_db.sock" 29 | var Ovs = "127.0.0.1:6640" 30 | var OvsSock = "/home/matteo/ovs/tutorial/sandbox/db.sock" 31 | 32 | var Sandbox = false 33 | var Hover = "http://localhost:5002" 34 | var TestEnv = false 35 | 36 | //Constant 37 | const SwitchPortsNumber = 32 38 | 39 | var SleepTime = 2000 * time.Millisecond 40 | var FlushTime = 30000 * time.Millisecond 41 | var FlushEnabled = false 42 | var SwitchSecurityPolicy = true 43 | 44 | var Debug = false 45 | var Info = true 46 | 47 | //for debug purposes, print Notification on single changes 48 | var PrintOvnNbChanges = false 49 | var PrintOvnSbChanges = false 50 | var PrintOvsChanges = false 51 | 52 | //for debug purposes, print the whole db on changes notification 53 | var PrintOvnNbFilteredTables = false 54 | var PrintOvnSbFilteredTables = false 55 | var PrintOvsFilteredTables = false 56 | 57 | //for debug purposes, print ALL the database 58 | var PrintOvnNb = false 59 | var PrintOvnSb = false 60 | var PrintOvs = false 61 | 62 | // file with the iomodules configuration to the used, if empty the deamon starts 63 | // in "OpenStack/ovn mode" 64 | var File = "" 65 | 66 | var log = l.MustGetLogger("iovisor-ovn-daemon") 67 | 68 | func PrintConfigCli() { 69 | fmt.Printf("***************CONFIGURATION*******************\n") 70 | // fmt.Printf("%30s:%d\n", "SleepTime", SleepTime) 71 | // fmt.Printf("%30s:%d\n", "FlushTime", FlushTime) 72 | // fmt.Printf("%30s:%d\n\n", "FlushEnabled", FlushEnabled) 73 | fmt.Printf("%30s: %t\n", "PrintOvnNbChanges", PrintOvnNbChanges) 74 | fmt.Printf("%30s: %t\n", "PrintOvnSbChanges", PrintOvnSbChanges) 75 | fmt.Printf("%30s: %t\n\n", "PrintOvsChanges", PrintOvsChanges) 76 | fmt.Printf("%30s: %t\n", "PrintOvnNbFilteredTables", PrintOvnNbFilteredTables) 77 | fmt.Printf("%30s: %t\n", "PrintOvnSbFilteredTables", PrintOvnSbFilteredTables) 78 | fmt.Printf("%30s: %t\n\n", "PrintOvsFilteredTables", PrintOvsFilteredTables) 79 | fmt.Printf("%30s: %t\n", "PrintOvnNb", PrintOvnNb) 80 | fmt.Printf("%30s: %t\n", "PrintOvnSb", PrintOvnSb) 81 | fmt.Printf("%30s: %t\n", "PrintOvs", PrintOvs) 82 | 83 | fmt.Printf("************************************************\n") 84 | } 85 | 86 | func PrintConfig() { 87 | fmt.Printf("-----IOVisor-OVN Daemon---------------------------------------\n") 88 | fmt.Printf("starting configuration\n") 89 | 90 | if !Sandbox { 91 | fmt.Printf("attaching to Openstack\n") 92 | fmt.Printf("%8s:%s\n", "Nb", Nb) 93 | fmt.Printf("%8s:%s\n", "Sb", Sb) 94 | fmt.Printf("%8s:%s\n", "Ovs", Ovs) 95 | } else { 96 | fmt.Printf("attaching to Sandbox\n") 97 | fmt.Printf("%8s:%s\n", "NbSock", NbSock) 98 | fmt.Printf("%8s:%s\n", "SbSock", SbSock) 99 | fmt.Printf("%8s:%s\n", "OvsSock", OvsSock) 100 | } 101 | fmt.Printf("%8s:%s\n", "Hover", Hover) 102 | // fmt.Printf("%8s:%t\n", "TestEnv", TestEnv) 103 | fmt.Printf("--------------------------------------------------------------\n\n") 104 | } 105 | 106 | func FromStringToIpPort(s string) (string, int) { 107 | host, portStr, err := net.SplitHostPort(s) 108 | if err != nil { 109 | log.Errorf("Error in parsing address %s : %s\n", s, err) 110 | return "", -1 111 | } 112 | port, errp := strconv.Atoi(portStr) 113 | if errp != nil { 114 | log.Errorf("Error in converting port %s : %s\n", portStr, err) 115 | return "", -1 116 | } 117 | return host, port 118 | } 119 | -------------------------------------------------------------------------------- /iomodules/dhcp/dhcp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package dhcp 15 | 16 | // This module perform a filtering on the packets that arrive, only 17 | // DHCP packets destinated to the server IP and MAC addressed (or to 18 | // brodcast address) are sent to the control plane 19 | 20 | var DhcpServer = ` 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | //#define BPF_TRACE 34 | 35 | #define ETHERNET_BROADCAST 0xffffffffffffULL 36 | 37 | /* See RFC 2131 */ 38 | struct dhcp_packet { 39 | uint8_t op; 40 | uint8_t htype; 41 | uint8_t hlen; 42 | uint8_t hops; 43 | uint32_t xid; 44 | uint16_t secs; 45 | uint16_t flags; 46 | uint32_t ciaddr; 47 | uint32_t yiaddr; 48 | uint32_t siaddr_nip; 49 | uint32_t gateway_nip; 50 | uint8_t chaddr[16]; 51 | uint8_t sname[64]; 52 | uint8_t file[128]; 53 | uint32_t cookie; 54 | } __attribute__((packed)); 55 | 56 | struct config_t { 57 | __be32 server_ip; 58 | __be64 server_mac; 59 | }; 60 | 61 | /* config: contains the configuration of the module */ 62 | BPF_TABLE("array", unsigned int, struct config_t, config, 1); 63 | 64 | struct eth_hdr { 65 | __be64 dst:48; 66 | __be64 src:48; 67 | __be16 proto; 68 | } __attribute__((packed)); 69 | 70 | static int handle_rx(void *skb, struct metadata *md) { 71 | struct __sk_buff *skb2 = (struct __sk_buff *)skb; 72 | void *data = (void *)(long)skb2->data; 73 | void *data_end = (void *)(long)skb2->data_end; 74 | 75 | unsigned int zero = 0; 76 | 77 | /* get server configuration */ 78 | struct config_t *cfg = config.lookup(&zero); 79 | if (!cfg) { 80 | #ifdef BPF_TRACE 81 | bpf_trace_printk("[dhcp] no config found\n"); 82 | #endif 83 | goto DROP; 84 | } 85 | 86 | struct eth_hdr *eth = data; 87 | 88 | if (data + sizeof(*eth) > data_end) 89 | goto DROP; 90 | 91 | /* check that the packet is a dhcp packet*/ 92 | 93 | if (eth->dst != ETHERNET_BROADCAST && eth->dst != cfg->server_mac) { 94 | #ifdef BPF_TRACE 95 | bpf_trace_printk("[dhcp] no dst mac\n"); 96 | #endif 97 | goto DROP; 98 | } 99 | 100 | if (eth->proto != bpf_htons(ETH_P_IP)) { 101 | #ifdef BPF_TRACE 102 | bpf_trace_printk("[dhcp] no IP\n"); 103 | #endif 104 | goto DROP; 105 | } 106 | 107 | struct iphdr *ip = data + sizeof(*eth); 108 | if (data + sizeof(*eth) + sizeof(*ip) > data_end) 109 | goto DROP; 110 | 111 | if (ip->daddr != INADDR_BROADCAST && ip->daddr != cfg->server_ip) { 112 | goto DROP; 113 | } 114 | 115 | // is the packet UDP? 116 | if (ip->protocol != IPPROTO_UDP) { 117 | #ifdef BPF_TRACE 118 | bpf_trace_printk("[dhcp] packet is not udp\n"); 119 | #endif 120 | goto DROP; 121 | } 122 | 123 | struct udphdr *udp = data + sizeof(*eth) + sizeof(*ip); 124 | if (data + sizeof(*eth) + sizeof(*ip) + sizeof(*udp) > data_end) 125 | goto DROP; 126 | 127 | if (udp->dest != bpf_htons(67) || udp->source != bpf_htons(68)) { 128 | #ifdef BPF_TRACE 129 | bpf_trace_printk("[dhcp] packet has invalid port numbers\n"); 130 | #endif 131 | goto DROP; 132 | } 133 | 134 | struct dhcp_packet *dhcp = data + sizeof(*eth) + sizeof(*ip) + sizeof(*udp); 135 | if (data + sizeof(*eth) + sizeof(*ip) + sizeof(*udp) + sizeof(*dhcp) > data_end) 136 | goto DROP; 137 | 138 | if (dhcp->op != 1) { 139 | #ifdef BPF_TRACE 140 | bpf_trace_printk("[dhcp] dhcp packet has dhcp->op != 1\n"); 141 | #endif 142 | goto DROP; 143 | } 144 | #ifdef BPF_TRACE 145 | bpf_trace_printk("[dhcp] send packet to controller\n"); 146 | #endif 147 | return RX_CONTROLLER; 148 | 149 | DROP: 150 | #ifdef BPF_TRACE 151 | bpf_trace_printk("[dhcp] dropping packet\n"); 152 | #endif 153 | return RX_DROP; 154 | } 155 | ` 156 | -------------------------------------------------------------------------------- /mainlogic/print.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package mainlogic 15 | 16 | import ( 17 | "fmt" 18 | "os" 19 | "strconv" 20 | 21 | "github.com/olekukonko/tablewriter" 22 | ) 23 | 24 | func PrintL2Switch(name string) { 25 | if sw, ok := switches[name]; ok { 26 | table := tablewriter.NewWriter(os.Stdout) 27 | if sw.swIomodule != nil { 28 | // table = tablewriter.NewWriter(os.Stdout) 29 | table.SetHeader([]string{"SWITCH", "MODULE-ID", "PORTS#"}) 30 | table.Append([]string{sw.Name, sw.swIomodule.ModuleId, strconv.Itoa(len(sw.swIomodule.Interfaces))}) 31 | table.Render() 32 | } else { 33 | // table = tablewriter.NewWriter(os.Stdout) 34 | table.SetHeader([]string{"SWITCH", "MODULE-ID", "PORTS#"}) 35 | table.Append([]string{sw.Name, sw.swIomodule.ModuleId, " "}) 36 | table.Render() 37 | } 38 | 39 | table = tablewriter.NewWriter(os.Stdout) 40 | table.SetHeader([]string{"SWITCH", "NAME", "IFACE"}) 41 | for _, swp := range sw.ports { 42 | table.Append([]string{sw.Name, swp.Name, swp.IfaceName}) 43 | } 44 | table.Render() 45 | 46 | if sw.swIomodule != nil { 47 | table = tablewriter.NewWriter(os.Stdout) 48 | table.SetHeader([]string{"SWITCH", "NAME", "LINK", "REDIRECT"}) 49 | for _, iface := range sw.swIomodule.Interfaces { 50 | table.Append([]string{sw.Name, iface.IfaceName, iface.LinkIdHover, strconv.Itoa(iface.IfaceId)}) 51 | } 52 | table.Render() 53 | } 54 | //TODO We need to print ports array? 55 | //spew.Dump(sw.swIomodule.PortsArray) 56 | } 57 | } 58 | 59 | func PrintL2Switches(verbose bool) { 60 | table := tablewriter.NewWriter(os.Stdout) 61 | table.SetHeader([]string{"SWITCHES", "MODULE-ID", "PORTS#"}) 62 | for swname, sw := range switches { 63 | table.Append([]string{swname, sw.swIomodule.ModuleId, strconv.Itoa(len(sw.swIomodule.Interfaces))}) 64 | } 65 | table.Render() 66 | 67 | if verbose { 68 | for swname, _ := range switches { 69 | fmt.Printf("\n") 70 | PrintL2Switch(swname) 71 | } 72 | } 73 | } 74 | 75 | func PrintRouter(name string) { 76 | if _, ok := routers[name]; ok { 77 | 78 | if r, ok := routers[name]; ok { 79 | table := tablewriter.NewWriter(os.Stdout) 80 | if r.rIoModule != nil { 81 | // table = tablewriter.NewWriter(os.Stdout) 82 | table.SetHeader([]string{"ROUTER", "MODULE-ID", "PORTS#"}) 83 | table.Append([]string{r.Name, r.rIoModule.ModuleId, strconv.Itoa(len(r.rIoModule.Interfaces))}) 84 | table.Render() 85 | } else { 86 | // table = tablewriter.NewWriter(os.Stdout) 87 | table.SetHeader([]string{"ROUTER", "MODULE-ID", "PORTS#"}) 88 | table.Append([]string{r.Name, r.rIoModule.ModuleId, " "}) 89 | table.Render() 90 | } 91 | 92 | table = tablewriter.NewWriter(os.Stdout) 93 | table.SetHeader([]string{"ROUTER", "NAME", "IP", "NETMASK", "MAC"}) 94 | for _, rp := range r.ports { 95 | table.Append([]string{r.Name, rp.Name, rp.IP, rp.Mask, rp.Mac}) 96 | } 97 | table.Render() 98 | 99 | if r.rIoModule != nil { 100 | table = tablewriter.NewWriter(os.Stdout) 101 | table.SetHeader([]string{"ROUTER", "NAME", "LINK", "FD", "REDIRECT", "IP", "NETMASK", "MAC"}) 102 | for _, iface := range r.rIoModule.Interfaces { 103 | table.Append([]string{r.Name, iface.IfaceName, iface.LinkIdHover, strconv.Itoa(iface.IfaceIdRedirectHover), iface.IP.String(), iface.Netmask.String(), iface.IP.String()}) 104 | } 105 | table.Render() 106 | } 107 | } 108 | } 109 | } 110 | 111 | func PrintRouters(verbose bool) { 112 | table := tablewriter.NewWriter(os.Stdout) 113 | table.SetHeader([]string{"ROUTER", "MODULE-ID", "PORTS#"}) 114 | for _, r := range routers { 115 | table.Append([]string{r.Name, r.rIoModule.ModuleId, strconv.Itoa(len(r.rIoModule.Interfaces))}) 116 | } 117 | table.Render() 118 | 119 | if verbose { 120 | for routername, _ := range routers { 121 | fmt.Printf("\n") 122 | PrintRouter(routername) 123 | } 124 | } 125 | } 126 | 127 | func PrintMainLogic(verbose bool) { 128 | fmt.Printf("\nSwitches\n\n") 129 | PrintL2Switches(verbose) 130 | fmt.Printf("\nRouters\n\n") 131 | PrintRouters(verbose) 132 | } 133 | -------------------------------------------------------------------------------- /iomodules/null/nullAPI.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package null 15 | 16 | import ( 17 | "errors" 18 | "fmt" 19 | "strconv" 20 | "net" 21 | 22 | "github.com/iovisor/iovisor-ovn/hover" 23 | l "github.com/op/go-logging" 24 | ) 25 | 26 | var log = l.MustGetLogger("iomodules-null") 27 | 28 | type NullModule struct { 29 | ModuleId string 30 | 31 | linkIdHover string 32 | ifaceName string 33 | 34 | deployed bool 35 | hc *hover.Client // used to send commands to hover 36 | 37 | mac net.HardwareAddr 38 | ip net.IP 39 | } 40 | 41 | func Create(hc *hover.Client) *NullModule { 42 | 43 | if hc == nil { 44 | log.Errorf("Dataplane is not valid") 45 | return nil 46 | } 47 | 48 | x := new(NullModule) 49 | x.hc = hc 50 | x.deployed = false 51 | 52 | x.mac, _ = net.ParseMAC("6e:59:d3:54:75:2d") 53 | x.ip = net.ParseIP("8.8.8.7") 54 | return x 55 | } 56 | 57 | func (m *NullModule) GetModuleId() string { 58 | return m.ModuleId 59 | } 60 | 61 | func (m *NullModule) Deploy() (err error) { 62 | 63 | if m.deployed { 64 | return nil 65 | } 66 | 67 | nullError, nullHover := m.hc.ModulePOST("bpf", "null", null_code) 68 | if nullError != nil { 69 | log.Errorf("Error in POST null IOModule: %s\n", nullError) 70 | return nullError 71 | } 72 | 73 | log.Noticef("POST NULL IOModule %s\n", nullHover.Id) 74 | m.ModuleId = nullHover.Id 75 | m.deployed = true 76 | 77 | id, _ := strconv.Atoi(m.ModuleId[2:]) 78 | m.hc.GetController().RegisterCallBack(uint16(id), m.ProcessPacket) 79 | 80 | //go m.sendPacketOut() 81 | 82 | return nil 83 | } 84 | 85 | func (m *NullModule) Destroy() (err error) { 86 | 87 | if !m.deployed { 88 | return nil 89 | } 90 | 91 | // TODO: 92 | // All interfaces must be detached before destroying the module. 93 | // Should it be done automatically here, or should be the application responsible for that? 94 | 95 | moduleDeleteError, _ := m.hc.ModuleDELETE(m.ModuleId) 96 | if moduleDeleteError != nil { 97 | log.Errorf("Error in destrying DHCP IOModule: %s\n", moduleDeleteError) 98 | return moduleDeleteError 99 | } 100 | 101 | m.ModuleId = "" 102 | m.deployed = false 103 | 104 | return nil 105 | } 106 | 107 | func (m *NullModule) AttachExternalInterface(ifaceName string) (err error) { 108 | 109 | if !m.deployed { 110 | errString := "Trying to attach port in undeployed module" 111 | log.Errorf(errString) 112 | return errors.New(errString) 113 | } 114 | 115 | linkError, linkHover := m.hc.LinkPOST("i:"+ifaceName, m.ModuleId) 116 | if linkError != nil { 117 | log.Errorf("Error in POSTing the Link: %s\n", linkError) 118 | return linkError 119 | } 120 | 121 | m.linkIdHover = linkHover.Id 122 | m.ifaceName = ifaceName 123 | 124 | return nil 125 | } 126 | 127 | func (m *NullModule) DetachExternalInterface(ifaceName string) (err error) { 128 | 129 | if !m.deployed { 130 | errString := "Trying to detach port in undeployed module" 131 | log.Errorf(errString) 132 | return errors.New(errString) 133 | } 134 | 135 | if m.ifaceName != ifaceName { 136 | errString := fmt.Sprintf("Iface '%s' is not present in module '%s'\n", 137 | ifaceName, m.ModuleId) 138 | log.Warningf(errString) 139 | return errors.New(errString) 140 | } 141 | 142 | linkDeleteError, _ := m.hc.LinkDELETE(m.linkIdHover) 143 | 144 | if linkDeleteError != nil { 145 | log.Warningf("Problem removing iface '%s' from module '%s'\n", 146 | ifaceName, m.ModuleId) 147 | return linkDeleteError 148 | } 149 | 150 | m.linkIdHover = "" 151 | m.ifaceName = "" 152 | return nil 153 | } 154 | 155 | func (m *NullModule) AttachToIoModule(ifaceId int, ifaceName string) (err error) { 156 | 157 | if !m.deployed { 158 | errString := "Trying to attach port in undeployed module" 159 | log.Errorf(errString) 160 | return errors.New(errString) 161 | } 162 | 163 | m.ifaceName = ifaceName 164 | m.linkIdHover = "" 165 | 166 | return nil 167 | } 168 | 169 | func (m *NullModule) DetachFromIoModule(ifaceName string) (err error) { 170 | if !m.deployed { 171 | errString := "Trying to detach port in undeployed module" 172 | log.Errorf(errString) 173 | return errors.New(errString) 174 | } 175 | 176 | m.linkIdHover = "" 177 | m.ifaceName = "" 178 | return nil 179 | } 180 | 181 | func (m *NullModule) Configure(conf interface{}) (err error) { 182 | _ = conf 183 | return nil 184 | } 185 | 186 | func (m *NullModule) ProcessPacket(p *hover.PacketIn) (err error) { 187 | 188 | log.Infof("packet with len: %s\n", p.Md.Packet_len) 189 | 190 | return nil 191 | } 192 | 193 | 194 | -------------------------------------------------------------------------------- /iomodules/l2switch/switch.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package l2switch 15 | 16 | var SwitchSecurityPolicy = ` 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define BPF_TRACE 29 | 30 | #define IP_SECURITY_INGRESS 31 | #define MAC_SECURITY_INGRESS 32 | #define MAC_SECURITY_EGRESS 33 | 34 | #define MAX_PORTS 32 35 | 36 | #define PRINT_MAC(x) (bpf_htonll(x)>>16) 37 | 38 | /* 39 | Fowarding Table: MAC Address to port association. This table is filled 40 | in the learning phase and then is used in the forwarding phase to decide 41 | where to send a packet 42 | */ 43 | BPF_TABLE("hash", __be64, u32, fwdtable, 1024); 44 | 45 | /* 46 | The Security Mac Table (securitymac) associate to each port the allowed mac 47 | address. If no entry is associated with the port, the port security is not 48 | applied to the port. 49 | */ 50 | BPF_TABLE("hash", u32, __be64, securitymac, MAX_PORTS); 51 | 52 | /* 53 | The Security Ip Table (securityip) associates to each port the allowed ip 54 | address. If no entry is associated with the port, the port security is not 55 | applied to the port. 56 | */ 57 | BPF_TABLE("hash", u32, __be32, securityip, MAX_PORTS); 58 | 59 | struct eth_hdr { 60 | __be64 dst:48; 61 | __be64 src:48; 62 | __be16 proto; 63 | } __attribute__((packed)); 64 | 65 | static int handle_rx(void *skb, struct metadata *md) { 66 | struct __sk_buff *skb2 = (struct __sk_buff *)skb; 67 | void *data = (void *)(long)skb2->data; 68 | void *data_end = (void *)(long)skb2->data_end; 69 | struct eth_hdr *eth = data; 70 | 71 | if (data + sizeof(*eth) > data_end) 72 | return RX_DROP; 73 | 74 | u32 in_ifc = md->in_ifc; 75 | 76 | #ifdef BPF_TRACE 77 | bpf_trace_printk("[switch-%d]: in_ifc=%d\n", md->module_id, in_ifc); 78 | #endif 79 | 80 | // port security on source mac 81 | #ifdef MAC_SECURITY_INGRESS 82 | __be64 *mac_lookup = securitymac.lookup(&in_ifc); 83 | if (mac_lookup) 84 | if (eth->src != *mac_lookup) { 85 | #ifdef BPF_TRACE 86 | bpf_trace_printk("[switch-%d]: mac INGRESS %lx mismatch %lx -> DROP\n", 87 | md->module_id, PRINT_MAC(eth->src), PRINT_MAC(*mac_lookup)); 88 | #endif 89 | return RX_DROP; 90 | } 91 | #endif 92 | 93 | // port security on source ip 94 | #ifdef IP_SECURITY_INGRESS 95 | if (eth->proto == bpf_htons(ETH_P_IP)) { 96 | __be32 *ip_lookup = securityip.lookup(&in_ifc); 97 | if (ip_lookup) { 98 | struct ip_t *ip = data + sizeof(*eth); 99 | if (data + sizeof(*eth) + sizeof(*ip) > data_end) 100 | return RX_DROP; 101 | 102 | if (ip->src != *ip_lookup) { 103 | #ifdef BPF_TRACE 104 | bpf_trace_printk("[switch-%d]: IP INGRESS %x mismatch %x -> DROP\n", 105 | md->module_id, bpf_htonl(ip->src), bpf_htonl(*ip_lookup)); 106 | #endif 107 | return RX_DROP; 108 | } 109 | } 110 | } 111 | #endif 112 | 113 | #ifdef BPF_TRACE 114 | bpf_trace_printk("[switch-%d]: mac src:%lx dst:%lx\n", 115 | md->module_id, PRINT_MAC(eth->src), PRINT_MAC(eth->dst)); 116 | #endif 117 | 118 | //LEARNING PHASE: mapping in_ifc with src_interface 119 | __be64 src_key = eth->src; 120 | 121 | //lookup in fwdtable. if no key present initialize with interface 122 | u32 *interface_lookup = fwdtable.lookup_or_init(&src_key, &in_ifc); 123 | 124 | //if the same mac has changed interface, update it 125 | if (*interface_lookup != in_ifc) 126 | *interface_lookup = in_ifc; 127 | 128 | //FORWARDING PHASE: select interface(s) to send the packet 129 | __be64 dst_mac = eth->dst; 130 | 131 | //lookup in forwarding table fwdtable 132 | u32 *dst_interface = fwdtable.lookup(&dst_mac); 133 | 134 | if (dst_interface) { 135 | //HIT in forwarding table 136 | //redirect packet to dst_interface 137 | 138 | #ifdef MAC_SECURITY_EGRESS 139 | u32 out_iface = *dst_interface; 140 | __be64 *mac_lookup = securitymac.lookup(&out_iface); 141 | if (mac_lookup) 142 | if (eth->dst != *mac_lookup){ 143 | #ifdef BPF_TRACE 144 | bpf_trace_printk("[switch-%d]: mac EGRESS %lx mismatch %lx -> DROP\n", 145 | md->module_id, PRINT_MAC(eth->dst), PRINT_MAC(*mac_lookup)); 146 | #endif 147 | return RX_DROP; 148 | } 149 | #endif 150 | 151 | /* do not send packet back on the ingress interface */ 152 | if (*dst_interface == in_ifc) 153 | return RX_DROP; 154 | 155 | pkt_redirect(skb, md, *dst_interface); 156 | 157 | #ifdef BPF_TRACE 158 | bpf_trace_printk("[switch-%d]: redirect out_ifc=%d\n", md->module_id, *dst_interface); 159 | #endif 160 | 161 | return RX_REDIRECT; 162 | 163 | } else { 164 | #ifdef BPF_TRACE 165 | bpf_trace_printk("[switch-%d]: Broadcast\n", md->module_id); 166 | #endif 167 | pkt_controller(skb, md, PKT_BROADCAST); 168 | return RX_CONTROLLER; 169 | } 170 | } 171 | ` 172 | -------------------------------------------------------------------------------- /iomodules/nat/natAPI.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package nat 15 | 16 | import ( 17 | "errors" 18 | "fmt" 19 | "net" 20 | "strconv" 21 | 22 | "github.com/mvbpolito/gosexy/to" 23 | 24 | "github.com/iovisor/iovisor-ovn/hover" 25 | "github.com/iovisor/iovisor-ovn/iomodules" 26 | l "github.com/op/go-logging" 27 | ) 28 | 29 | var log = l.MustGetLogger("iomodules-nat") 30 | 31 | type NatModule struct { 32 | ModuleId string 33 | PortsCount int //number of allocated ports 34 | Interfaces map[string]*NatModuleInterface 35 | 36 | deployed bool 37 | hc *hover.Client // used to send commands to hover 38 | } 39 | 40 | type NatModuleInterface struct { 41 | IfaceIdRedirectHover int //Iface id inside hover (relative to the m:1234 the interface is attached to ...) and provided my the extended hover /links/ API 42 | IfaceFd int //Interface Fd inside External_Ids (42, etc...) 43 | LinkIdHover string //iomodules Link Id 44 | IfaceName string 45 | } 46 | 47 | func Create(hc *hover.Client) *NatModule { 48 | 49 | if hc == nil { 50 | log.Errorf("HoverClient is not valid") 51 | return nil 52 | } 53 | 54 | n := new(NatModule) 55 | n.Interfaces = make(map[string]*NatModuleInterface) 56 | n.hc = hc 57 | n.deployed = false 58 | return n 59 | } 60 | 61 | func (n *NatModule) GetModuleId() string { 62 | return n.ModuleId 63 | } 64 | 65 | func (n *NatModule) Deploy() (err error) { 66 | 67 | if n.deployed { 68 | return nil 69 | } 70 | 71 | natError, natHover := n.hc.ModulePOST("bpf", "Nat", NatCode) 72 | if natError != nil { 73 | log.Errorf("Error in POST Nat IOModule: %s\n", natError) 74 | return natError 75 | } 76 | 77 | log.Noticef("POST Nat IOModule %s\n", natHover.Id) 78 | n.ModuleId = natHover.Id 79 | n.deployed = true 80 | 81 | return nil 82 | } 83 | 84 | func (n *NatModule) Destroy() (err error) { 85 | 86 | if !n.deployed { 87 | return nil 88 | } 89 | 90 | // TODO: 91 | // All interfaces must be detached before destroying the module. 92 | // Should it be done automatically here, or should be the application responsible for that? 93 | 94 | moduleDeleteError, _ := n.hc.ModuleDELETE(n.ModuleId) 95 | if moduleDeleteError != nil { 96 | log.Errorf("Error in destrying Nat IOModule: %s\n", moduleDeleteError) 97 | return moduleDeleteError 98 | } 99 | 100 | n.ModuleId = "" 101 | n.deployed = false 102 | 103 | return nil 104 | } 105 | 106 | func (n *NatModule) AttachExternalInterface(ifaceName string) (err error) { 107 | 108 | if !n.deployed { 109 | errString := "Trying to attach port in undeployed nat" 110 | log.Errorf(errString) 111 | return errors.New(errString) 112 | } 113 | 114 | if n.PortsCount == 2 { 115 | errString := "There are not free ports in the nat\n" 116 | log.Errorf(errString) 117 | return errors.New(errString) 118 | } 119 | 120 | linkError, linkHover := n.hc.LinkPOST("i:"+ifaceName, n.ModuleId) 121 | if linkError != nil { 122 | log.Errorf("Error in POSTing the Link: %s\n", linkError) 123 | return linkError 124 | } 125 | 126 | _, external_interfaces := n.hc.ExternalInterfacesListGET() 127 | 128 | n.PortsCount++ 129 | 130 | // Saving IfaceIdRedirectHover for this port. The number will be used by security policies 131 | ifacenumber := -1 132 | if linkHover.From[0:2] == "m:" { 133 | ifacenumber = linkHover.FromId 134 | } 135 | if linkHover.To[0:2] == "m:" { 136 | ifacenumber = linkHover.ToId 137 | } 138 | if ifacenumber == -1 { 139 | log.Warningf("IfaceIdRedirectHover == -1 something wrong happened...\n") 140 | } 141 | 142 | iface := new(NatModuleInterface) 143 | 144 | iface.IfaceFd, _ = strconv.Atoi(external_interfaces[ifaceName].Id) 145 | iface.IfaceIdRedirectHover = ifacenumber 146 | iface.LinkIdHover = linkHover.Id 147 | iface.IfaceName = ifaceName 148 | 149 | n.Interfaces[ifaceName] = iface 150 | 151 | return nil 152 | } 153 | 154 | func (n *NatModule) DetachExternalInterface(ifaceName string) (err error) { 155 | 156 | if !n.deployed { 157 | errString := "Trying to detach port in undeployed nat" 158 | log.Errorf(errString) 159 | return errors.New(errString) 160 | } 161 | 162 | iface, ok := n.Interfaces[ifaceName] 163 | 164 | if !ok { 165 | errString := fmt.Sprintf("Iface '%s' is not present in nat '%s'\n", 166 | ifaceName, n.ModuleId) 167 | log.Warningf(errString) 168 | return errors.New(errString) 169 | } 170 | 171 | linkDeleteError, _ := n.hc.LinkDELETE(iface.LinkIdHover) 172 | 173 | if linkDeleteError != nil { 174 | //log.Debug("REMOVE Interface %s %s (1/1) LINK REMOVED\n", currentInterface.Name, currentInterface.IfaceIdExternalIds) 175 | log.Warningf("Problem removing iface '%s' from nat '%s'\n", 176 | ifaceName, n.ModuleId) 177 | return linkDeleteError 178 | } 179 | 180 | delete(n.Interfaces, ifaceName) 181 | 182 | return nil 183 | } 184 | 185 | func (n *NatModule) AttachToIoModule(ifaceId int, ifaceName string) (err error) { 186 | if !n.deployed { 187 | errString := "Trying to attach port in undeployed nat" 188 | log.Errorf(errString) 189 | return errors.New(errString) 190 | } 191 | 192 | if n.PortsCount == 2 { 193 | errString := "There are not free ports in the nat" 194 | log.Errorf(errString) 195 | return errors.New(errString) 196 | } 197 | 198 | iface := new(NatModuleInterface) 199 | 200 | iface.IfaceFd = -1 201 | iface.IfaceIdRedirectHover = ifaceId 202 | iface.LinkIdHover = "" 203 | iface.IfaceName = ifaceName 204 | 205 | n.Interfaces[ifaceName] = iface 206 | 207 | return nil 208 | } 209 | 210 | func (n *NatModule) SetPublicIp(ip net.IP) (err error) { 211 | if !n.deployed { 212 | errString := "undeployed nat" 213 | log.Errorf(errString) 214 | return errors.New(errString) 215 | } 216 | 217 | n.hc.TableEntryPUT(n.ModuleId, "public_ip", "0", iomodules.IpToHexBigEndian(ip.To4())) 218 | 219 | return nil 220 | } 221 | 222 | func (n *NatModule) DetachFromIoModule(ifaceName string) (err error) { 223 | return errors.New("Not implemented") 224 | } 225 | 226 | func (n *NatModule) Configure(conf interface{}) (err error) { 227 | 228 | log.Infof("Configuring NAT module") 229 | confMap := to.Map(conf) 230 | 231 | public_ip, ok1 := confMap["public_ip"] 232 | if !ok1 { 233 | return errors.New("Missing public_ip") 234 | } 235 | 236 | err = n.SetPublicIp(net.ParseIP(public_ip.(string))) 237 | if err != nil { 238 | return 239 | } 240 | 241 | return nil 242 | } 243 | -------------------------------------------------------------------------------- /iomodules/onetoonenat/natAPI.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package onetoonenat 15 | 16 | import ( 17 | "errors" 18 | "fmt" 19 | "net" 20 | "strconv" 21 | 22 | "github.com/mvbpolito/gosexy/to" 23 | 24 | "github.com/iovisor/iovisor-ovn/hover" 25 | "github.com/iovisor/iovisor-ovn/iomodules" 26 | l "github.com/op/go-logging" 27 | ) 28 | 29 | var log = l.MustGetLogger("iomodules-nat") 30 | 31 | type NatModule struct { 32 | ModuleId string 33 | PortsCount int //number of allocated ports 34 | Interfaces map[string]*NatModuleInterface 35 | 36 | deployed bool 37 | hc *hover.Client // used to send commands to hover 38 | } 39 | 40 | type NatModuleInterface struct { 41 | IfaceIdRedirectHover int //Iface id inside hover (relative to the m:1234 the interface is attached to ...) and provided my the extended hover /links/ API 42 | IfaceFd int //Interface Fd inside External_Ids (42, etc...) 43 | LinkIdHover string //iomodules Link Id 44 | IfaceName string 45 | } 46 | 47 | func Create(hc *hover.Client) *NatModule { 48 | 49 | if hc == nil { 50 | log.Errorf("Dataplane is not valid") 51 | return nil 52 | } 53 | 54 | n := new(NatModule) 55 | n.Interfaces = make(map[string]*NatModuleInterface) 56 | n.hc = hc 57 | n.deployed = false 58 | return n 59 | } 60 | 61 | func (n *NatModule) GetModuleId() string { 62 | return n.ModuleId 63 | } 64 | 65 | func (n *NatModule) Deploy() (err error) { 66 | 67 | if n.deployed { 68 | return nil 69 | } 70 | 71 | natError, natHover := n.hc.ModulePOST("bpf", "Nat", NatCode) 72 | if natError != nil { 73 | log.Errorf("Error in POST Nat IOModule: %s\n", natError) 74 | return natError 75 | } 76 | 77 | log.Noticef("POST Nat IOModule %s\n", natHover.Id) 78 | n.ModuleId = natHover.Id 79 | n.deployed = true 80 | 81 | return nil 82 | } 83 | 84 | func (n *NatModule) Destroy() (err error) { 85 | 86 | if !n.deployed { 87 | return nil 88 | } 89 | 90 | // TODO: 91 | // All interfaces must be detached before destroying the module. 92 | // Should it be done automatically here, or should be the application responsible for that? 93 | 94 | moduleDeleteError, _ := n.hc.ModuleDELETE(n.ModuleId) 95 | if moduleDeleteError != nil { 96 | log.Errorf("Error in destrying Nat IOModule: %s\n", moduleDeleteError) 97 | return moduleDeleteError 98 | } 99 | 100 | n.ModuleId = "" 101 | n.deployed = false 102 | 103 | return nil 104 | } 105 | 106 | func (n *NatModule) AttachExternalInterface(ifaceName string) (err error) { 107 | 108 | if !n.deployed { 109 | errString := "Trying to attach port in undeployed nat" 110 | log.Errorf(errString) 111 | return errors.New(errString) 112 | } 113 | 114 | if n.PortsCount == 2 { 115 | errString := "There are not free ports in the nat\n" 116 | log.Errorf(errString) 117 | return errors.New(errString) 118 | } 119 | 120 | linkError, linkHover := n.hc.LinkPOST("i:"+ifaceName, n.ModuleId) 121 | if linkError != nil { 122 | log.Errorf("Error in POSTing the Link: %s\n", linkError) 123 | return linkError 124 | } 125 | 126 | _, external_interfaces := n.hc.ExternalInterfacesListGET() 127 | 128 | n.PortsCount++ 129 | 130 | // Saving IfaceIdRedirectHover for this port. The number will be used by security policies 131 | ifacenumber := -1 132 | if linkHover.From[0:2] == "m:" { 133 | ifacenumber = linkHover.FromId 134 | } 135 | if linkHover.To[0:2] == "m:" { 136 | ifacenumber = linkHover.ToId 137 | } 138 | if ifacenumber == -1 { 139 | log.Warningf("IfaceIdRedirectHover == -1 something wrong happened...\n") 140 | } 141 | 142 | iface := new(NatModuleInterface) 143 | 144 | iface.IfaceFd, _ = strconv.Atoi(external_interfaces[ifaceName].Id) 145 | iface.IfaceIdRedirectHover = ifacenumber 146 | iface.LinkIdHover = linkHover.Id 147 | iface.IfaceName = ifaceName 148 | 149 | n.Interfaces[ifaceName] = iface 150 | 151 | return nil 152 | } 153 | 154 | func (n *NatModule) DetachExternalInterface(ifaceName string) (err error) { 155 | 156 | if !n.deployed { 157 | errString := "Trying to detach port in undeployed nat" 158 | log.Errorf(errString) 159 | return errors.New(errString) 160 | } 161 | 162 | iface, ok := n.Interfaces[ifaceName] 163 | 164 | if !ok { 165 | errString := fmt.Sprintf("Iface '%s' is not present in nat '%s'\n", 166 | ifaceName, n.ModuleId) 167 | log.Warningf(errString) 168 | return errors.New(errString) 169 | } 170 | 171 | linkDeleteError, _ := n.hc.LinkDELETE(iface.LinkIdHover) 172 | 173 | if linkDeleteError != nil { 174 | log.Warningf("Problem removing iface '%s' from nat '%s'\n", 175 | ifaceName, n.ModuleId) 176 | return linkDeleteError 177 | } 178 | 179 | delete(n.Interfaces, ifaceName) 180 | 181 | return nil 182 | } 183 | 184 | func (n *NatModule) AttachToIoModule(ifaceId int, ifaceName string) (err error) { 185 | if !n.deployed { 186 | errString := "Trying to attach port in undeployed nat" 187 | log.Errorf(errString) 188 | return errors.New(errString) 189 | } 190 | 191 | if n.PortsCount == 2 { 192 | errString := "There are not free ports in the nat" 193 | log.Errorf(errString) 194 | return errors.New(errString) 195 | } 196 | 197 | iface := new(NatModuleInterface) 198 | 199 | iface.IfaceFd = -1 200 | iface.IfaceIdRedirectHover = ifaceId 201 | iface.LinkIdHover = "" 202 | iface.IfaceName = ifaceName 203 | 204 | n.Interfaces[ifaceName] = iface 205 | 206 | return nil 207 | } 208 | 209 | func (n *NatModule) SetAddressAssociation(internal_ip net.IP, external_ip net.IP) (err error) { 210 | if !n.deployed { 211 | errString := "undeployed nat" 212 | log.Errorf(errString) 213 | return errors.New(errString) 214 | } 215 | 216 | n.hc.TableEntryPOST(n.ModuleId, "egress_nat_table", 217 | "{"+iomodules.IpToHexBigEndian(internal_ip.To4())+"}", iomodules.IpToHexBigEndian(external_ip.To4())) 218 | n.hc.TableEntryPOST(n.ModuleId, "reverse_nat_table", 219 | "{"+iomodules.IpToHexBigEndian(external_ip.To4())+"}", iomodules.IpToHexBigEndian(internal_ip.To4())) 220 | 221 | return nil 222 | } 223 | 224 | func (n *NatModule) DetachFromIoModule(ifaceName string) (err error) { 225 | return errors.New("Not implemented") 226 | } 227 | 228 | func (n *NatModule) Configure(conf interface{}) (err error) { 229 | // conf is a map that contains: 230 | // public_ip: ip that is put as src address on the ongoing packets 231 | 232 | log.Infof("Configuring NAT module") 233 | confMap := to.Map(conf) 234 | 235 | //configure nat_entries 236 | if nat_entries, ok := confMap["nat_entries"]; ok { 237 | for _, entry := range to.List(nat_entries) { 238 | entryMap := to.Map(entry) 239 | 240 | internal_ip, ok1 := entryMap["internal_ip"] 241 | if !ok1 { 242 | return errors.New("Missing internal_ip") 243 | } 244 | 245 | external_ip, ok1 := entryMap["external_ip"] 246 | if !ok1 { 247 | return errors.New("Missing external_ip") 248 | } 249 | 250 | err = n.SetAddressAssociation(net.ParseIP(internal_ip.(string)), net.ParseIP(external_ip.(string))) 251 | if err != nil { 252 | return 253 | } 254 | 255 | } 256 | } 257 | 258 | return nil 259 | } 260 | -------------------------------------------------------------------------------- /servicetopology/servicetopology.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package servicetopology 15 | 16 | import ( 17 | "errors" 18 | "fmt" 19 | 20 | "github.com/mvbpolito/gosexy/to" 21 | "github.com/mvbpolito/gosexy/yaml" 22 | 23 | "github.com/iovisor/iovisor-ovn/config" 24 | "github.com/iovisor/iovisor-ovn/hover" 25 | 26 | "github.com/iovisor/iovisor-ovn/iomodules" 27 | "github.com/iovisor/iovisor-ovn/iomodules/dhcp" 28 | "github.com/iovisor/iovisor-ovn/iomodules/l2switch" 29 | "github.com/iovisor/iovisor-ovn/iomodules/nat" 30 | "github.com/iovisor/iovisor-ovn/iomodules/null" 31 | "github.com/iovisor/iovisor-ovn/iomodules/onetoonenat" 32 | "github.com/iovisor/iovisor-ovn/iomodules/router" 33 | 34 | l "github.com/op/go-logging" 35 | ) 36 | 37 | var log = l.MustGetLogger("service-topology") 38 | 39 | var dataplane *hover.Client 40 | 41 | // List of deployed modules (Indexed by module name) 42 | var modules map[string]iomodules.IoModule 43 | 44 | // List of configuration for modules (Indexed by module name) 45 | var modulesConfig map[string]interface{} 46 | 47 | func deployModules(modulesRequested []interface{}) error { 48 | for _, i := range modulesRequested { 49 | iMap := to.Map(i) 50 | 51 | name_, nameok := iMap["name"] 52 | if !nameok { 53 | return errors.New("name is missing in module") 54 | } 55 | name := name_.(string) 56 | 57 | mtype_, mtypeok := iMap["type"] 58 | if !mtypeok { 59 | errString := fmt.Sprintf("type is missing in module '%s'", name) 60 | return errors.New(errString) 61 | } 62 | mtype := mtype_.(string) 63 | config_, configok := iMap["config"] 64 | if !configok { 65 | log.Warningf("config is missing in module '%s'", name) 66 | } 67 | config := to.Map(config_) 68 | 69 | var m iomodules.IoModule 70 | 71 | switch mtype { 72 | case "dhcp": 73 | m = dhcp.Create(dataplane) 74 | case "router": 75 | m = router.Create(dataplane) 76 | case "switch": 77 | m = l2switch.Create(dataplane) 78 | case "nat": 79 | m = nat.Create(dataplane) 80 | case "onetoonenat": 81 | m = onetoonenat.Create(dataplane) 82 | case "null_node": // "null" can not be used because it causes an issue 83 | m = null.Create(dataplane) 84 | default: 85 | errString := fmt.Sprintf("Invalid module type '%s' for module '%s'", 86 | mtype, name) 87 | return errors.New(errString) 88 | } 89 | 90 | if err := m.Deploy(); err != nil { 91 | errString := fmt.Sprintf("Error deploying module '%s': %s", name, err) 92 | return errors.New(errString) 93 | } 94 | 95 | modules[name] = m 96 | if configok { 97 | modulesConfig[name] = config 98 | } 99 | } 100 | 101 | return nil 102 | } 103 | 104 | func linkModules(linksRequested []interface{}) error { 105 | for _, i := range linksRequested { 106 | iMap := to.Map(i) 107 | from_, fromok := iMap["from"] 108 | if !fromok { 109 | return errors.New("from is not present on link request") 110 | } 111 | from := from_.(string) 112 | 113 | to_, took := iMap["to"] 114 | if !took { 115 | return errors.New("to is not present on link request") 116 | } 117 | to := to_.(string) 118 | 119 | log.Noticef("Linking modules: '%s' -> '%s'", to, from) 120 | 121 | _, ok1 := modules[to] 122 | if !ok1 { 123 | errString := fmt.Sprintf("Module '%s' does not exist", to) 124 | return errors.New(errString) 125 | } 126 | 127 | _, ok2 := modules[from] 128 | if !ok2 { 129 | errString := fmt.Sprintf("Module '%s' does not exist", from) 130 | return errors.New(errString) 131 | } 132 | 133 | // this call is a little trickly, it receives 134 | // (dataplame, iomodule1, name1, iomodule2, name2), by definition if two 135 | // modules are connected together the name of those interface on each 136 | // module is the name of the peer module. 137 | err := iomodules.AttachIoModules(dataplane, modules[from], to, modules[to], from) 138 | if err != nil { 139 | log.Error(err) 140 | errString := fmt.Sprintf("Error linking Modules '%s' -> '%s'", from, to) 141 | return errors.New(errString) 142 | } 143 | } 144 | 145 | return nil 146 | } 147 | 148 | func linkModulesToInterfaces(links []interface{}) error { 149 | for _, i := range links { 150 | iMap := to.Map(i) 151 | 152 | module_, moduleok := iMap["module"] 153 | if !moduleok { 154 | return errors.New("'module' is missing in link to interface") 155 | } 156 | module := module_.(string) 157 | 158 | iface_, ifaceok := iMap["iface"] 159 | if !ifaceok { 160 | return errors.New("'iface' is missing in link to interface") 161 | } 162 | iface := iface_.(string) 163 | 164 | m, mok := modules[module] 165 | if !mok { 166 | errString := fmt.Sprintf("Module '%s' does not exist", module) 167 | return errors.New(errString) 168 | } 169 | 170 | err := m.AttachExternalInterface(iface) 171 | if err != nil { 172 | log.Errorf("%s", err) 173 | return err 174 | } 175 | } 176 | 177 | return nil 178 | } 179 | 180 | func undeployModules() { 181 | for name, m := range modules { 182 | err := m.Destroy() 183 | if err != nil { 184 | log.Errorf("Error destroying module '%s': %s", name, err) 185 | // In this case it is a bad idea to stop, it is better to try to 186 | // remove all the modules 187 | } 188 | } 189 | } 190 | 191 | func DeployTopology(path string) error { 192 | 193 | dataplane = hover.NewClient() 194 | 195 | // Connect to hover and initialize HoverDataplane 196 | if err := dataplane.Init(config.Hover); err != nil { 197 | log.Errorf("unable to conect to Hover in '%s': %s", config.Hover, err) 198 | return err 199 | } 200 | 201 | modules = make(map[string]iomodules.IoModule) 202 | modulesConfig = make(map[string]interface{}) 203 | 204 | conf, err := yaml.Open(path) 205 | if err != nil { 206 | log.Errorf("Failed to open configuraton fail '%s'", path) 207 | return err 208 | } 209 | 210 | // deploy iomodules 211 | log.Noticef("Deploying Modules") 212 | modulesRequested := conf.Get("modules") 213 | if modulesRequested == nil { 214 | return errors.New("No modules present on tolology file") 215 | } 216 | if err := deployModules(to.List(modulesRequested)); err != nil { 217 | log.Errorf("Error deploying modules: %s", err) 218 | // TODO: undeploy modules 219 | return err 220 | } 221 | 222 | // link modules together 223 | log.Noticef("Linking Modules") 224 | linksRequested := conf.Get("links") 225 | if linksRequested != nil { 226 | if err := linkModules(to.List(linksRequested)); err != nil { 227 | log.Errorf("%s", err) 228 | // TODO: remove links and undeploy modules 229 | return err 230 | } 231 | } 232 | 233 | // link modules to external interfaces 234 | log.Noticef("Connecting external interfaces to Modules") 235 | externalInterfacesRequested := conf.Get("external_interfaces") 236 | if externalInterfacesRequested != nil { 237 | if err := linkModulesToInterfaces(to.List(externalInterfacesRequested)); err != nil { 238 | log.Errorf("%s", err) 239 | // TODO: remove links and undeploy modules 240 | return err 241 | } 242 | } 243 | 244 | // configure modules 245 | log.Noticef("Configuring Modules") 246 | for name, m := range modules { 247 | if conf, ok := modulesConfig[name]; ok { 248 | err := m.Configure(conf) 249 | if err != nil { 250 | log.Errorf("Error configuring Module '%s': %s", name, err) 251 | return err 252 | } 253 | } else { 254 | log.Warningf("sikipping module '%s' without configuration", name) 255 | } 256 | } 257 | 258 | return nil 259 | } 260 | 261 | func UndeployTopology() { 262 | log.Errorf("Not Implemented") 263 | } 264 | -------------------------------------------------------------------------------- /iomodules/l2switch/switchAPI.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package l2switch 15 | 16 | import ( 17 | "errors" 18 | "fmt" 19 | "net" 20 | "strconv" 21 | 22 | "github.com/mvbpolito/gosexy/to" 23 | 24 | "github.com/iovisor/iovisor-ovn/iomodules" 25 | 26 | "github.com/iovisor/iovisor-ovn/hover" 27 | l "github.com/op/go-logging" 28 | ) 29 | 30 | var log = l.MustGetLogger("iomodules-switch") 31 | 32 | type L2SwitchModule struct { 33 | ModuleId string 34 | 35 | Interfaces map[string]*L2SwitchModuleInterface 36 | 37 | deployed bool 38 | hc *hover.Client // used to send commands to hover 39 | } 40 | 41 | type L2SwitchModuleInterface struct { 42 | IfaceId int // Iface id inside hover 43 | LinkIdHover string // iomodules Link Id 44 | IfaceName string 45 | } 46 | 47 | func Create(hc *hover.Client) *L2SwitchModule { 48 | 49 | if hc == nil { 50 | log.Errorf("HoverClient is not valid") 51 | return nil 52 | } 53 | 54 | x := new(L2SwitchModule) 55 | x.Interfaces = make(map[string]*L2SwitchModuleInterface) 56 | x.hc = hc 57 | x.deployed = false 58 | return x 59 | } 60 | 61 | func (sw *L2SwitchModule) GetModuleId() string { 62 | return sw.ModuleId 63 | } 64 | 65 | func (sw *L2SwitchModule) Deploy() (err error) { 66 | 67 | if sw.deployed { 68 | return nil 69 | } 70 | 71 | switchError, switchHover := sw.hc.ModulePOST("bpf", 72 | "Switch", SwitchSecurityPolicy) 73 | if switchError != nil { 74 | log.Errorf("Error in POST Switch IOModule: %s\n", switchError) 75 | return switchError 76 | } 77 | 78 | log.Noticef("POST Switch IOModule %s\n", switchHover.Id) 79 | sw.ModuleId = switchHover.Id 80 | sw.deployed = true 81 | 82 | return nil 83 | } 84 | 85 | func (sw *L2SwitchModule) Destroy() (err error) { 86 | 87 | if !sw.deployed { 88 | return nil 89 | } 90 | 91 | moduleDeleteError, _ := sw.hc.ModuleDELETE(sw.ModuleId) 92 | if moduleDeleteError != nil { 93 | log.Errorf("Error in destrying Switch IOModule: %s\n", moduleDeleteError) 94 | return moduleDeleteError 95 | } 96 | 97 | sw.ModuleId = "" 98 | sw.deployed = false 99 | 100 | return nil 101 | } 102 | 103 | func (sw *L2SwitchModule) AttachExternalInterface(ifaceName string) (err error) { 104 | 105 | if !sw.deployed { 106 | errString := "Trying to attach port in undeployed switch" 107 | log.Errorf(errString) 108 | return errors.New(errString) 109 | } 110 | 111 | linkError, linkHover := sw.hc.LinkPOST("i:"+ifaceName, sw.ModuleId) 112 | if linkError != nil { 113 | log.Errorf("Error in POSTing the Link: %s\n", linkError) 114 | return linkError 115 | } 116 | 117 | if err != nil { 118 | log.Errorf("Error in finding free port: %s\n", err) 119 | return err 120 | } 121 | 122 | // get interface id 123 | ifacenumber := -1 124 | if linkHover.From[0:2] == "m:" { 125 | ifacenumber = linkHover.FromId 126 | } 127 | if linkHover.To[0:2] == "m:" { 128 | ifacenumber = linkHover.ToId 129 | } 130 | if ifacenumber == -1 { 131 | log.Warningf("IfaceId == -1 something wrong happened...\n") 132 | } 133 | 134 | iface := new(L2SwitchModuleInterface) 135 | 136 | iface.IfaceId = ifacenumber 137 | iface.LinkIdHover = linkHover.Id 138 | iface.IfaceName = ifaceName 139 | sw.Interfaces[ifaceName] = iface 140 | 141 | // TODO: security policies 142 | 143 | return nil 144 | } 145 | 146 | func (sw *L2SwitchModule) DetachExternalInterface(ifaceName string) (err error) { 147 | 148 | if !sw.deployed { 149 | errString := "Trying to detach port in undeployed switch" 150 | log.Errorf(errString) 151 | return errors.New(errString) 152 | } 153 | 154 | iface, ok := sw.Interfaces[ifaceName] 155 | 156 | if !ok { 157 | errString := fmt.Sprintf("Iface '%s' is not present in switch '%s'\n", 158 | ifaceName, sw.ModuleId) 159 | log.Warningf(errString) 160 | return errors.New(errString) 161 | } 162 | 163 | linkDeleteError, _ := sw.hc.LinkDELETE(iface.LinkIdHover) 164 | 165 | if linkDeleteError != nil { 166 | log.Warningf("Problem removing iface '%s' from switch '%s'\n", 167 | ifaceName, sw.ModuleId) 168 | return linkDeleteError 169 | } 170 | 171 | // TODO: clean up port security tables 172 | delete(sw.Interfaces, ifaceName) 173 | 174 | return nil 175 | } 176 | 177 | func (sw *L2SwitchModule) AttachToIoModule(ifaceId int, ifaceName string) (err error) { 178 | if !sw.deployed { 179 | log.Errorf("Trying to attach port in undeployed switch\n") 180 | return errors.New("Trying to attach port in undeployed switch") 181 | } 182 | 183 | iface := new(L2SwitchModuleInterface) 184 | 185 | iface.IfaceId = ifaceId 186 | iface.IfaceName = ifaceName 187 | 188 | sw.Interfaces[ifaceName] = iface 189 | 190 | // TODO: security policies 191 | return nil 192 | } 193 | 194 | func (sw *L2SwitchModule) DetachFromIoModule(ifaceName string) (err error) { 195 | if !sw.deployed { 196 | log.Errorf("Trying to detach port in undeployed switch\n") 197 | return errors.New("Trying to detach port in undeployed switch") 198 | } 199 | 200 | _, ok := sw.Interfaces[ifaceName] 201 | 202 | if !ok { 203 | errString := fmt.Sprintf("Iface '%s' is not present in switch '%s'\n", 204 | ifaceName, sw.ModuleId) 205 | log.Warningf(errString) 206 | return errors.New(errString) 207 | } 208 | 209 | // TODO: clean up port security tables 210 | 211 | delete(sw.Interfaces, ifaceName) 212 | 213 | return nil 214 | } 215 | 216 | // adds a entry in the forwarding table of the switch 217 | func (sw *L2SwitchModule) AddForwardingTableEntry(mac net.HardwareAddr, ifaceName string) (err error) { 218 | 219 | swIface, ok := sw.Interfaces[ifaceName] 220 | if !ok { 221 | errString := fmt.Sprintf("Iface '%s' is not present in switch '%s'\n", 222 | ifaceName, sw.ModuleId) 223 | log.Warningf(errString) 224 | return errors.New(errString) 225 | } 226 | 227 | macString := "{" + iomodules.MacToHexadecimalStringBigEndian(mac) + "}" 228 | 229 | sw.hc.TableEntryPOST(sw.ModuleId, "fwdtable", macString, 230 | strconv.Itoa(swIface.IfaceId)) 231 | 232 | return nil 233 | } 234 | 235 | func (sw *L2SwitchModule) AddPortSecurityMac(mac net.HardwareAddr, ifaceName string) (err error) { 236 | 237 | swIface, ok := sw.Interfaces[ifaceName] 238 | if !ok { 239 | errString := fmt.Sprintf("Iface '%s' is not present in switch '%s'\n", 240 | ifaceName, sw.ModuleId) 241 | log.Warningf(errString) 242 | return errors.New(errString) 243 | } 244 | 245 | macString := iomodules.MacToHexadecimalStringBigEndian(mac) 246 | 247 | sw.hc.TableEntryPOST(sw.ModuleId, "securitymac", 248 | "{0x"+strconv.Itoa(swIface.IfaceId)+"}", macString) 249 | return nil 250 | } 251 | 252 | func (sw *L2SwitchModule) Configure(conf interface{}) (err error) { 253 | // The interface is a map with the following elements: 254 | // forwarding_table: a list of maps, each one has: 255 | // port: the port where mac can be reached 256 | // mac: the mac itself 257 | // TODO: support for port security policies 258 | log.Infof("Configuring Switch") 259 | confMap := to.Map(conf) 260 | if fwd_table, ok := confMap["forwarding_table"]; ok { 261 | for _, entry := range to.List(fwd_table) { 262 | entryMap := to.Map(entry) 263 | 264 | port_, ok1 := entryMap["port"] 265 | mac_, ok2 := entryMap["mac"] 266 | if !ok1 || !ok2 { 267 | log.Errorf("Skipping non valid forwarding table entry") 268 | continue 269 | } 270 | 271 | log.Infof("Adding forwardig table entry '%s' -> '%s'", 272 | mac_.(string), port_.(string)) 273 | 274 | mac, err1 := net.ParseMAC(mac_.(string)) 275 | if err1 != nil { 276 | log.Errorf("'%s' is not a valid mac address", mac_) 277 | continue 278 | } 279 | 280 | err := sw.AddForwardingTableEntry(mac, port_.(string)) 281 | if err != nil { 282 | return err 283 | } 284 | } 285 | } 286 | return nil 287 | } 288 | 289 | // TODO: port security policies 290 | -------------------------------------------------------------------------------- /cli/cli.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // Inline command line interface for debug purposes 15 | // in future this cli will be a separate go program that connects to the main iovisor-ovn daemon 16 | // in future this cli will use a cli go library (e.g. github.com/urfave/cli ) 17 | 18 | package cli 19 | 20 | import ( 21 | "bufio" 22 | "fmt" 23 | "os" 24 | "strings" 25 | 26 | "github.com/iovisor/iovisor-ovn/hover" 27 | "github.com/iovisor/iovisor-ovn/iomodules/l2switch" 28 | "github.com/iovisor/iovisor-ovn/mainlogic" 29 | "github.com/iovisor/iovisor-ovn/ovnmonitor" 30 | ) 31 | 32 | func Cli(c *hover.Client) { 33 | db := &mainlogic.Mon.DB 34 | hc := c 35 | for { 36 | reader := bufio.NewReader(os.Stdin) 37 | fmt.Print("cli@iov-ovn$ ") 38 | line, _ := reader.ReadString('\n') 39 | 40 | line = TrimSuffix(line, "\n") 41 | args := strings.Split(line, " ") 42 | 43 | if len(args) >= 1 { 44 | switch args[0] { 45 | case "mainlogic", "ml": 46 | if len(args) >= 2 { 47 | switch args[1] { 48 | case "-v": 49 | fmt.Printf("\nMainLogic (verbose)\n\n") 50 | mainlogic.PrintMainLogic(true) 51 | case "switch": 52 | if len(args) >= 3 { 53 | fmt.Printf("\nMainLogic Switch %s\n\n", args[2]) 54 | mainlogic.PrintL2Switch(args[2]) 55 | } else { 56 | fmt.Printf("\nMainLogic Switches \n\n") 57 | mainlogic.PrintL2Switches(true) 58 | } 59 | case "router": 60 | if len(args) >= 3 { 61 | fmt.Printf("\nMainLogic Router %s\n\n", args[2]) 62 | mainlogic.PrintRouter(args[2]) 63 | } else { 64 | fmt.Printf("\nMainLogic Routers \n\n") 65 | mainlogic.PrintRouters(true) 66 | } 67 | } 68 | } else { 69 | fmt.Printf("\nMainLogic\n\n") 70 | mainlogic.PrintMainLogic(false) 71 | } 72 | 73 | case "ovnmonitor", "ovn", "o": 74 | if len(args) >= 2 { 75 | switch args[1] { 76 | case "-v": 77 | fmt.Printf("\nOvnMonitor (verbose)\n\n") 78 | ovnmonitor.PrintOvnMonitor(true, db) 79 | case "switch", "s": 80 | if len(args) >= 3 { 81 | fmt.Printf("\nOvnMonitor Logical Switch %s\n\n", args[2]) 82 | ovnmonitor.PrintLogicalSwitchByName(args[2], db) 83 | } else { 84 | fmt.Printf("\nOvnMonitor Logical Switches \n\n") 85 | ovnmonitor.PrintLogicalSwitches(true, db) 86 | } 87 | case "router", "r": 88 | if len(args) >= 3 { 89 | fmt.Printf("\nOvnMonitor Logical Router %s\n\n", args[2]) 90 | ovnmonitor.PrintLogicalRouterByName(args[2], db) 91 | } else { 92 | fmt.Printf("\nOvnMonitor Logical Routers \n\n") 93 | ovnmonitor.PrintLogicalRouters(true, db) 94 | } 95 | case "interface", "i": 96 | fmt.Printf("\nOvnMonitor Ovs Interfaces\n\n") 97 | ovnmonitor.PrintOvsInterfaces(db) 98 | } 99 | } else { 100 | fmt.Printf("\nOvn Monitor\n\n") 101 | ovnmonitor.PrintOvnMonitor(false, db) 102 | } 103 | 104 | case "interfaces", "i": 105 | fmt.Printf("\nInterfaces\n\n") 106 | _, external_interfaces := hc.ExternalInterfacesListGET() 107 | hover.ExternalInterfacesListPrint(external_interfaces) 108 | case "modules", "m": 109 | if len(args) >= 2 { 110 | switch args[1] { 111 | case "get": 112 | switch len(args) { 113 | case 2: 114 | fmt.Printf("\nModules GET\n\n") 115 | _, modules := hc.ModuleListGET() 116 | hover.ModuleListPrint(modules) 117 | case 3: 118 | fmt.Printf("\nModules GET\n\n") 119 | _, module := hc.ModuleGET(args[2]) 120 | hover.ModulePrint(module) 121 | default: 122 | PrintModulesUsage() 123 | } 124 | case "post": 125 | switch len(args) { 126 | case 3: 127 | fmt.Printf("\nModules POST\n\n") 128 | if args[2] == "switch" { 129 | _, module := hc.ModulePOST("bpf", "Switch", l2switch.SwitchSecurityPolicy) 130 | hover.ModulePrint(module) 131 | } else { 132 | //TODO Print modules list 133 | } 134 | default: 135 | PrintModulesUsage() 136 | } 137 | case "delete": 138 | switch len(args) { 139 | case 3: 140 | fmt.Printf("\nModules DELETE\n\n") 141 | hc.ModuleDELETE(args[2]) 142 | default: 143 | PrintModulesUsage() 144 | } 145 | default: 146 | PrintModulesUsage() 147 | } 148 | } else { 149 | PrintModulesUsage() 150 | } 151 | case "links", "l": 152 | if len(args) >= 2 { 153 | switch args[1] { 154 | case "get": 155 | switch len(args) { 156 | case 2: 157 | fmt.Printf("\nLinks GET\n\n") 158 | _, links := hc.LinkListGet() 159 | hover.LinkListPrint(links) 160 | case 3: 161 | fmt.Printf("\nLinks GET\n\n") 162 | _, link := hc.LinkGET(args[2]) 163 | hover.LinkPrint(link) 164 | default: 165 | PrintLinksUsage() 166 | } 167 | case "post": 168 | switch len(args) { 169 | case 4: 170 | fmt.Printf("\nLinks POST\n\n") 171 | _, link := hc.LinkPOST(args[2], args[3]) 172 | hover.LinkPrint(link) 173 | default: 174 | PrintLinksUsage() 175 | } 176 | case "delete": 177 | switch len(args) { 178 | case 3: 179 | fmt.Printf("\nLinks DELETE\n\n") 180 | hc.LinkDELETE(args[2]) 181 | default: 182 | PrintLinksUsage() 183 | } 184 | default: 185 | PrintLinksUsage() 186 | } 187 | } else { 188 | PrintLinksUsage() 189 | } 190 | case "table", "t": 191 | if len(args) >= 2 { 192 | switch args[1] { 193 | case "get": 194 | switch len(args) { 195 | case 2: 196 | fmt.Printf("\nTable GET\n\n") 197 | _, modules := hc.ModuleListGET() 198 | for moduleName, _ := range modules { 199 | fmt.Printf("**MODULE** -> %s\n", moduleName) 200 | _, tables := hc.TableListGET(moduleName) 201 | for _, tablename := range tables { 202 | fmt.Printf("Table *%s*\n", tablename) 203 | _, table := hc.TableGET(moduleName, tablename) 204 | hover.TablePrint(table) 205 | } 206 | } 207 | case 3: 208 | fmt.Printf("\nTable GET\n\n") 209 | _, tables := hc.TableListGET(args[2]) 210 | for _, tablename := range tables { 211 | fmt.Printf("Table *%s*\n", tablename) 212 | _, table := hc.TableGET(args[2], tablename) 213 | hover.TablePrint(table) 214 | } 215 | case 4: 216 | fmt.Printf("\nTable GET\n\n") 217 | _, table := hc.TableGET(args[2], args[3]) 218 | hover.TablePrint(table) 219 | case 5: 220 | fmt.Printf("\nTable GET\n\n") 221 | _, tableEntry := hc.TableEntryGET(args[2], args[3], args[4]) 222 | hover.TableEntryPrint(tableEntry) 223 | default: 224 | PrintTableUsage() 225 | } 226 | case "put": 227 | if len(args) == 6 { 228 | fmt.Printf("\nTable PUT\n\n") 229 | _, tableEntry := hc.TableEntryPUT(args[2], args[3], args[4], args[5]) 230 | hover.TableEntryPrint(tableEntry) 231 | } else { 232 | PrintTableUsage() 233 | } 234 | case "post": 235 | if len(args) == 6 { 236 | fmt.Printf("\nTable POST\n\n") 237 | _, tableEntry := hc.TableEntryPOST(args[2], args[3], args[4], args[5]) 238 | hover.TableEntryPrint(tableEntry) 239 | } else { 240 | PrintTableUsage() 241 | } 242 | case "delete": 243 | if len(args) == 5 { 244 | fmt.Printf("\nTable DELETE\n\n") 245 | hc.TableEntryDELETE(args[2], args[3], args[4]) 246 | } else { 247 | PrintTableUsage() 248 | } 249 | default: 250 | PrintTableUsage() 251 | } 252 | } else { 253 | PrintTableUsage() 254 | } 255 | case "help", "h": 256 | PrintHelp() 257 | 258 | case "": 259 | default: 260 | fmt.Printf("\nInvalid Command\n\n") 261 | PrintHelp() 262 | } 263 | } 264 | fmt.Printf("\n") 265 | } 266 | } 267 | 268 | func TrimSuffix(s, suffix string) string { 269 | if strings.HasSuffix(s, suffix) { 270 | s = s[:len(s)-len(suffix)] 271 | } 272 | return s 273 | } 274 | -------------------------------------------------------------------------------- /mainlogic/mainlogic.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package mainlogic 15 | 16 | import ( 17 | "encoding/hex" 18 | "fmt" 19 | "net" 20 | "os" 21 | 22 | "github.com/iovisor/iovisor-ovn/config" 23 | "github.com/iovisor/iovisor-ovn/hover" 24 | "github.com/iovisor/iovisor-ovn/iomodules" 25 | "github.com/iovisor/iovisor-ovn/iomodules/l2switch" 26 | "github.com/iovisor/iovisor-ovn/iomodules/router" 27 | "github.com/iovisor/iovisor-ovn/ovnmonitor" 28 | l "github.com/op/go-logging" 29 | ) 30 | 31 | const brint = "br-int" 32 | 33 | var log = l.MustGetLogger("mainlogic") 34 | 35 | type L2Switch struct { 36 | Name string 37 | swIomodule *l2switch.L2SwitchModule 38 | ports map[string]*L2SwitchPort 39 | } 40 | 41 | type L2SwitchPort struct { 42 | Name string 43 | IfaceName string 44 | } 45 | 46 | type Router struct { 47 | Name string 48 | rIoModule *router.RouterModule 49 | ports map[string]*RouterPort 50 | } 51 | 52 | type RouterPort struct { 53 | Name string 54 | IP string // TODO: change this to a better data structure 55 | Mask string 56 | Mac string 57 | } 58 | 59 | /* 60 | * Contains the switches that have been created. 61 | * They are indexed by a name, in this case is the OVN name 62 | */ 63 | var switches map[string]*L2Switch 64 | 65 | // Contains the routers that haven been created. Indexed by named 66 | var routers map[string]*Router 67 | 68 | var hc *hover.Client 69 | var Mon *ovnmonitor.OVNMonitor 70 | 71 | func GetHoverClient() *hover.Client { 72 | return hc 73 | } 74 | 75 | func MainLogic() { 76 | 77 | Mon = ovnmonitor.CreateMonitor() 78 | db, err := Mon.Connect() 79 | 80 | if err == false { // it is a quite odd that false means error 81 | log.Errorf("Error connecting to OVN databases\n") 82 | return 83 | } 84 | 85 | switches = make(map[string]*L2Switch) 86 | routers = make(map[string]*Router) 87 | 88 | hc = hover.NewClient() 89 | if err := hc.Init(config.Hover); err != nil { 90 | log.Errorf("unable to conect to Hover %s\n%s\n", config.Hover, err) 91 | os.Exit(1) 92 | } 93 | 94 | var notifier MyNotifier 95 | notifier.Update(db) // I think that there is an instant of time where the info could be lost 96 | Mon.Register(¬ifier) 97 | 98 | } 99 | 100 | type MyNotifier struct { 101 | } 102 | 103 | func (m *MyNotifier) Update(db *ovnmonitor.OvnDB) { 104 | 105 | log.Noticef("Mainlogic update() init") 106 | 107 | // detect removed routers 108 | for name, r := range routers { 109 | if _, ok := db.Routers[name]; !ok { 110 | removeRouter(r) 111 | } 112 | } 113 | 114 | // detect new routers 115 | for name, lr := range db.Routers { 116 | if _, ok := routers[name]; !ok { 117 | addRouter(lr) 118 | } 119 | } 120 | 121 | // process modified routers 122 | for _, lr := range db.Routers { 123 | if lr.Modified { 124 | updateRouter(lr) 125 | } 126 | } 127 | /* detect removed switches*/ 128 | for name, sw := range switches { 129 | if _, ok := db.Switches[name]; !ok { 130 | removeSwitch(sw) 131 | } 132 | } 133 | 134 | /* detect new switches */ 135 | for name, lsw := range db.Switches { 136 | if _, ok := switches[name]; !ok { 137 | addSwitch(lsw) 138 | } 139 | } 140 | 141 | /* process modified switches */ 142 | for _, lsw := range db.Switches { 143 | if lsw.Modified { 144 | updateSwitch(lsw) 145 | } 146 | } 147 | 148 | log.Noticef("Mainlogic update() finished") 149 | } 150 | 151 | func removeRouter(r *Router) { 152 | r.rIoModule.Destroy() 153 | delete(routers, r.Name) 154 | } 155 | 156 | func addRouter(lr *ovnmonitor.LogicalRouter) { 157 | r := new(Router) 158 | r.Name = lr.Name 159 | r.ports = make(map[string]*RouterPort) 160 | r.rIoModule = router.Create(hc) 161 | routers[r.Name] = r 162 | 163 | r.rIoModule.Deploy() 164 | } 165 | 166 | func updateRouter(lr *ovnmonitor.LogicalRouter) { 167 | r := routers[lr.Name] 168 | // look for deleted ports 169 | for name, port := range r.ports { 170 | if _, ok := lr.Ports[name]; !ok { 171 | removeRouterPort(r, port) 172 | } 173 | } 174 | 175 | // look for added ports 176 | for name, lrp := range lr.Ports { 177 | if _, ok := r.ports[name]; !ok { 178 | addRouterPort(r, lrp) 179 | } 180 | } 181 | 182 | // look for modified ports 183 | for _, lrp := range lr.Ports { 184 | if lrp.Modified { 185 | updateRouterPort(r, lrp) 186 | } 187 | } 188 | 189 | lr.Modified = false 190 | } 191 | 192 | func removeRouterPort(r *Router, port *RouterPort) { 193 | // TODO: what else should be done here? 194 | delete(r.ports, port.Name) 195 | } 196 | 197 | func addRouterPort(r *Router, lrp *ovnmonitor.LogicalRouterPort) { 198 | port := new(RouterPort) 199 | port.Name = lrp.Name 200 | ip, ipnet, _ := net.ParseCIDR(lrp.Networks) 201 | if ip.To4() != nil { 202 | port.IP = ip.String() 203 | a, _ := hex.DecodeString(ipnet.Mask.String()) 204 | port.Mask = fmt.Sprintf("%v.%v.%v.%v", a[0], a[1], a[2], a[3]) 205 | } 206 | port.Mac = lrp.Mac 207 | r.ports[port.Name] = port 208 | } 209 | 210 | func updateRouterPort(r *Router, lrp *ovnmonitor.LogicalRouterPort) { 211 | //port := r.ports[lrp.Name] 212 | 213 | // TODO: Another thing to be done :( 214 | } 215 | 216 | func removeSwitch(sw *L2Switch) { 217 | /* TODO: remove ports already present on the switch*/ 218 | sw.swIomodule.Destroy() 219 | delete(switches, sw.Name) 220 | } 221 | 222 | func addSwitch(lsw *ovnmonitor.LogicalSwitch) { 223 | sw := new(L2Switch) 224 | sw.Name = lsw.Name 225 | sw.ports = make(map[string]*L2SwitchPort) 226 | sw.swIomodule = l2switch.Create(hc) 227 | switches[sw.Name] = sw 228 | } 229 | 230 | func updateSwitch(lsw *ovnmonitor.LogicalSwitch) { 231 | sw := switches[lsw.Name] 232 | /* look for deleted ports*/ 233 | for name, port := range sw.ports { 234 | if _, ok := lsw.Ports[name]; !ok { 235 | removePort(sw, port) 236 | } 237 | } 238 | 239 | /* look for added ports */ 240 | for name, lport := range lsw.Ports { 241 | if _, ok := sw.ports[name]; !ok { 242 | addPort(sw, lport) 243 | } 244 | } 245 | 246 | /* process modified ports*/ 247 | for _, lport := range lsw.Ports { 248 | if lport.Modified { 249 | updatePort(sw, lport) 250 | } 251 | } 252 | 253 | lsw.Modified = false 254 | } 255 | 256 | func removePort(sw *L2Switch, port *L2SwitchPort) { 257 | if port.IfaceName != "" { 258 | sw.swIomodule.DetachExternalInterface(port.IfaceName) 259 | } 260 | 261 | delete(sw.ports, port.Name) 262 | } 263 | 264 | func addPort(sw *L2Switch, lport *ovnmonitor.LogicalSwitchPort) { 265 | port := new(L2SwitchPort) 266 | port.Name = lport.Name 267 | sw.ports[port.Name] = port 268 | } 269 | 270 | func updatePort(sw *L2Switch, lport *ovnmonitor.LogicalSwitchPort) { 271 | port := sw.ports[lport.Name] 272 | 273 | if lport.Type == "" { 274 | /* is IfaceChanged? */ 275 | if port.IfaceName != lport.IfaceName { 276 | /* if it was connected to an iface */ 277 | if port.IfaceName != "" { 278 | sw.swIomodule.DetachExternalInterface(port.IfaceName) 279 | } 280 | 281 | port.IfaceName = lport.IfaceName 282 | 283 | // should this port be bound to a VIF interface? 284 | if port.IfaceName != "" { 285 | if len(sw.swIomodule.Interfaces) == 0 { 286 | sw.swIomodule.Deploy() 287 | } 288 | sw.swIomodule.AttachExternalInterface(port.IfaceName) 289 | } 290 | } 291 | } else if lport.Type == "router" { // is that port connected to a switch? 292 | 293 | r := findRouterByPortName(lport.RouterPort) 294 | if r == nil { 295 | log.Errorf("Unable to find router...") 296 | } else { 297 | lrp := r.ports[lport.RouterPort] 298 | 299 | log.Noticef("lsp '%s' is connected to lrp '%s'", port.Name, lrp.Name) 300 | 301 | if len(sw.swIomodule.Interfaces) == 0 { 302 | sw.swIomodule.Deploy() 303 | } 304 | 305 | // attach both iomodules 306 | if err := iomodules.AttachIoModules(hc, 307 | sw.swIomodule, port.Name, r.rIoModule, lrp.Name); err != nil { 308 | 309 | log.Errorf("Unable attach router to switch") 310 | } 311 | 312 | if lrp.IP != "" { 313 | // configure router 314 | ip := net.ParseIP(lrp.IP) 315 | mac, err := net.ParseMAC(lrp.Mac) 316 | if err != nil { 317 | log.Errorf("Error configuring router: Non valid mac") 318 | return 319 | } 320 | 321 | mask := iomodules.ParseIPv4Mask(lrp.Mask) 322 | 323 | err = r.rIoModule.ConfigureInterface(lrp.Name, ip, mask, mac) 324 | if err != nil { 325 | log.Errorf("Error configuring router") 326 | } 327 | 328 | // the mac table of the switch is setted statically in order to 329 | // avoid problems with the broadcast. 330 | // (this issue will be solved soon) 331 | // Mac address of the router is present through this interface 332 | sw.swIomodule.AddForwardingTableEntry(mac, port.Name) 333 | } 334 | } 335 | } 336 | 337 | lport.Modified = false 338 | } 339 | 340 | func findRouterByPortName(lrp string) *Router { 341 | for _, router := range routers { 342 | if _, ok := router.ports[lrp]; ok { 343 | return router 344 | } 345 | } 346 | 347 | return nil 348 | } 349 | -------------------------------------------------------------------------------- /iomodules/dhcp/dhcpAPI.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package dhcp 15 | 16 | import ( 17 | "errors" 18 | "fmt" 19 | "net" 20 | "strconv" 21 | "time" 22 | 23 | // We use this packet that is a fork of "github.com/krolaw/dhcp4" 24 | // to avoid any problem due to an API change 25 | dhcp "github.com/mvbpolito/dhcp4" 26 | 27 | "github.com/mvbpolito/gosexy/to" 28 | 29 | "github.com/iovisor/iovisor-ovn/iomodules" 30 | "github.com/iovisor/iovisor-ovn/hover" 31 | l "github.com/op/go-logging" 32 | ) 33 | 34 | var log = l.MustGetLogger("iomodules-dhcp-user") 35 | 36 | type DhcpModule struct { 37 | ModuleId string 38 | 39 | linkIdHover string 40 | ifaceName string 41 | 42 | mac net.HardwareAddr 43 | ip net.IP 44 | 45 | handler dhcp.Handler 46 | // channel to synchronize ProcessPacket and ReadFrom functions 47 | c chan *hover.PacketIn 48 | 49 | deployed bool 50 | hc *hover.Client // used to send commands to hover 51 | } 52 | 53 | func Create(hc *hover.Client) *DhcpModule { 54 | 55 | if hc == nil { 56 | log.Errorf("HoverClient is not valid") 57 | return nil 58 | } 59 | 60 | x := new(DhcpModule) 61 | x.hc = hc 62 | x.deployed = false 63 | x.c = make(chan *hover.PacketIn, 10) 64 | return x 65 | } 66 | 67 | func (m *DhcpModule) GetModuleId() string { 68 | return m.ModuleId 69 | } 70 | 71 | func (m *DhcpModule) Deploy() (err error) { 72 | 73 | if m.deployed { 74 | return nil 75 | } 76 | 77 | dhcpError, dhcpHover := m.hc.ModulePOST("bpf", "DHCP", DhcpServer) 78 | if dhcpError != nil { 79 | log.Errorf("Error in POST dhcp IOModule: %s\n", dhcpError) 80 | return dhcpError 81 | } 82 | 83 | log.Noticef("POST DHCP IOModule %s\n", dhcpHover.Id) 84 | m.ModuleId = dhcpHover.Id 85 | m.deployed = true 86 | 87 | id, _ := strconv.Atoi(m.ModuleId[2:]) 88 | m.hc.GetController().RegisterCallBack(uint16(id), m.ProcessPacket) 89 | 90 | return nil 91 | } 92 | 93 | func (m *DhcpModule) Destroy() (err error) { 94 | 95 | if !m.deployed { 96 | return nil 97 | } 98 | 99 | // TODO: 100 | // All interfaces must be detached before destroying the module. 101 | // Should it be done automatically here, or should be the application responsible for that? 102 | 103 | moduleDeleteError, _ := m.hc.ModuleDELETE(m.ModuleId) 104 | if moduleDeleteError != nil { 105 | log.Errorf("Error in destrying DHCP IOModule: %s\n", moduleDeleteError) 106 | return moduleDeleteError 107 | } 108 | 109 | m.ModuleId = "" 110 | m.deployed = false 111 | 112 | return nil 113 | } 114 | 115 | func (m *DhcpModule) AttachExternalInterface(ifaceName string) (err error) { 116 | 117 | if !m.deployed { 118 | errString := "Trying to attach port in undeployed module" 119 | log.Errorf(errString) 120 | return errors.New(errString) 121 | } 122 | 123 | if m.ifaceName != "" { 124 | errString := fmt.Sprintf("Module '%s' is already connected to interface '%s'\n", 125 | m.ModuleId, ifaceName) 126 | log.Errorf(errString) 127 | return errors.New(errString) 128 | } 129 | 130 | linkError, linkHover := m.hc.LinkPOST("i:"+ifaceName, m.ModuleId) 131 | if linkError != nil { 132 | log.Errorf("Error in POSTing the Link: %s\n", linkError) 133 | return linkError 134 | } 135 | 136 | m.linkIdHover = linkHover.Id 137 | m.ifaceName = ifaceName 138 | 139 | return nil 140 | } 141 | 142 | func (m *DhcpModule) DetachExternalInterface(ifaceName string) (err error) { 143 | 144 | if !m.deployed { 145 | errString := "Trying to detach port in undeployed module" 146 | log.Errorf(errString) 147 | return errors.New(errString) 148 | } 149 | 150 | if m.ifaceName != ifaceName { 151 | errString := fmt.Sprintf("Iface '%s' is not present in module '%s'\n", 152 | ifaceName, m.ModuleId) 153 | log.Warningf(errString) 154 | return errors.New(errString) 155 | } 156 | 157 | linkDeleteError, _ := m.hc.LinkDELETE(m.linkIdHover) 158 | 159 | if linkDeleteError != nil { 160 | log.Warningf("Problem removing iface '%s' from module '%s'\n", 161 | ifaceName, m.ModuleId) 162 | return linkDeleteError 163 | } 164 | 165 | m.linkIdHover = "" 166 | m.ifaceName = "" 167 | return nil 168 | } 169 | 170 | func (m *DhcpModule) AttachToIoModule(ifaceId int, ifaceName string) (err error) { 171 | 172 | if !m.deployed { 173 | errString := "Trying to attach port in undeployed module" 174 | log.Errorf(errString) 175 | return errors.New(errString) 176 | } 177 | 178 | if m.ifaceName != "" { 179 | errString := fmt.Sprintf("Module '%s' is already connected to interface '%s'\n", 180 | m.ModuleId, ifaceName) 181 | log.Errorf(errString) 182 | return errors.New(errString) 183 | } 184 | 185 | m.ifaceName = ifaceName 186 | m.linkIdHover = "" 187 | 188 | return nil 189 | } 190 | 191 | func (m *DhcpModule) DetachFromIoModule(ifaceName string) (err error) { 192 | if !m.deployed { 193 | errString := "Trying to detach port in undeployed module" 194 | log.Errorf(errString) 195 | return errors.New(errString) 196 | } 197 | 198 | if m.ifaceName != ifaceName { 199 | errString := fmt.Sprintf("Iface '%s' is not present in module '%s'\n", 200 | ifaceName, m.ModuleId) 201 | log.Warningf(errString) 202 | return errors.New(errString) 203 | } 204 | 205 | m.linkIdHover = "" 206 | m.ifaceName = "" 207 | return nil 208 | } 209 | 210 | // TODO: this function should be split on smaller pieces. 211 | func (m *DhcpModule) ConfigureParameters(netmask net.IPMask, 212 | addr_low net.IP, 213 | addr_high net.IP, 214 | dns net.IP, 215 | router net.IP, 216 | leaseTime uint32, 217 | serverMAC net.HardwareAddr, 218 | serverIP net.IP) (err error) { 219 | if !m.deployed { 220 | errString := "Trying to configure undeployed module" 221 | log.Errorf(errString) 222 | return errors.New(errString) 223 | } 224 | 225 | m.handler = &DHCPHandler{ 226 | ip: serverIP[12:16], 227 | leaseDuration: time.Duration(leaseTime)*time.Second, 228 | start: addr_low, 229 | leaseRange: dhcp.IPRange(addr_low, addr_high), 230 | leases: make(map[int]lease, 10), // TODO: what is this "10" for? 231 | options: dhcp.Options{ 232 | dhcp.OptionSubnetMask: []byte(netmask), 233 | dhcp.OptionRouter: []byte(router[12:16]), 234 | dhcp.OptionDomainNameServer: []byte(dns[12:16]), 235 | }, 236 | } 237 | 238 | m.mac = serverMAC 239 | m.ip = serverIP 240 | 241 | // mac and ip addresses are used in the dataplane to decide if a packet 242 | // has to be sent to the controller or not. 243 | serverIpHex := iomodules.IpToHexBigEndian(serverIP) 244 | serverMacHex := iomodules.MacToHexadecimalStringBigEndian(serverMAC) 245 | 246 | var toSend string 247 | toSend = "{" + serverIpHex + " " + serverMacHex + "}" 248 | 249 | m.hc.TableEntryPUT(m.ModuleId, "config", "0", toSend) 250 | 251 | go dhcp.Serve(m, m.handler) 252 | 253 | return nil 254 | } 255 | 256 | func (m *DhcpModule) Configure(conf interface{}) (err error) { 257 | // conf is a map that contains: 258 | // pool: CIDR notation of the address pool 259 | // dns: ip address of the DNS given to clients 260 | // gw: ip of the default routers given to clients 261 | // lease_time: default lease_time 262 | // server_ip: ip address of the dhcp server 263 | // server_mac: mac address of the dhcp server 264 | 265 | log.Infof("Configuring DHCP server") 266 | confMap := to.Map(conf) 267 | 268 | netmask_, netmask_ok := confMap["netmask"] 269 | addr_low_, addr_low_ok := confMap["addr_low"] 270 | addr_high_, addr_high_ok := confMap["addr_high"] 271 | dns_, dns_ok := confMap["dns"] 272 | router_, router_ok := confMap["router"] 273 | lease_time_, lease_time_ok := confMap["lease_time"] 274 | server_ip_, server_ip_ok := confMap["server_ip"] 275 | server_mac_, server_mac_ok := confMap["server_mac"] 276 | 277 | // TODO: some of these fields could be optional and have a default value 278 | if !netmask_ok { 279 | return errors.New("Missing netmask") 280 | } 281 | 282 | if !addr_low_ok { 283 | return errors.New("Missing addr_low") 284 | } 285 | 286 | if !addr_high_ok { 287 | return errors.New("Missing addr_high") 288 | } 289 | 290 | if !dns_ok { 291 | return errors.New("Missing dns") 292 | } 293 | 294 | if !router_ok { 295 | return errors.New("Missing router") 296 | } 297 | 298 | if !lease_time_ok { 299 | return errors.New("Missing lease_time") 300 | } 301 | 302 | if !server_ip_ok { 303 | return errors.New("Missing server_ip") 304 | } 305 | 306 | if !server_mac_ok { 307 | return errors.New("Missing server_mac") 308 | } 309 | 310 | netmask := iomodules.ParseIPv4Mask(netmask_.(string)) 311 | addr_low := net.ParseIP(addr_low_.(string)) 312 | addr_high := net.ParseIP(addr_high_.(string)) 313 | dns := net.ParseIP(dns_.(string)) 314 | router := net.ParseIP(router_.(string)) 315 | var lease_time uint32 = uint32(lease_time_.(int)) 316 | mac_server, err := net.ParseMAC(server_mac_.(string)) 317 | if err != nil { 318 | return errors.New("server_mac is not valid") 319 | } 320 | ip_server := net.ParseIP(server_ip_.(string)) 321 | 322 | return m.ConfigureParameters(netmask, addr_low, addr_high, dns, 323 | router, lease_time, mac_server, ip_server) 324 | } 325 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /iomodules/router/router.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package router 15 | 16 | var RouterCode = ` 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | /* The following flags control the debubbing output of the eBPF program. 32 | * Please note that because of some eBPF limitations all of them cannot 33 | * be activated at the same time 34 | */ 35 | 36 | //#define BPF_TRACE // global trace control (should be comment to get better performance) 37 | 38 | #ifdef BPF_TRACE 39 | //#define BPF_TRACE_TTL 40 | //#define BPF_TRACE_INPUT 41 | //#define BPF_TRACE_OUTPUT 42 | //#define BPF_TRACE_ROUTING 43 | //#define BPF_TRACE_ARP 44 | //#define BPF_TRACE_ICMP_ECHO_REPLY 45 | #endif 46 | 47 | //#define CHECK_MAC_DST 48 | 49 | #define ROUTING_TABLE_DIM 6 50 | #define ROUTER_PORT_N 32 51 | #define ARP_TABLE_DIM 32 52 | 53 | #define IP_CSUM_OFFSET (sizeof(struct eth_hdr) + offsetof(struct iphdr, check)) 54 | #define ICMP_CSUM_OFFSET (sizeof(struct eth_hdr) + sizeof(struct iphdr) + offsetof(struct icmphdr, checksum)) 55 | 56 | #define MAC_MULTICAST_MASK 0x10ULL // network byte order 57 | 58 | enum { 59 | SLOWPATH_ARP_REPLY = 1, 60 | SLOWPATH_ARP_LOOKUP_MISS, 61 | SLOWPATH_TTL_EXCEEDED 62 | }; 63 | 64 | /* Routing Table Entry */ 65 | struct rt_entry { 66 | __be32 network; 67 | __be32 netmask; 68 | u16 port; 69 | __be32 nexthop; // ip address of next hop, 0 is locally reachable 70 | }; 71 | 72 | /* Router Port */ 73 | struct r_port { 74 | __be32 ip; 75 | __be32 netmask; 76 | __be64 mac:48; 77 | }; 78 | 79 | /* 80 | The Routing table is implemented as an array of struct rt_entry (Routing Table Entry) 81 | the longest prefix matching algorithm (at least a simplified version) 82 | is implemented performing a bounded loop over the entries of the routing table. 83 | We assume that the control plane puts entry ordered from the longest netmask 84 | to the shortest one. 85 | */ 86 | BPF_TABLE("array", u32, struct rt_entry, routing_table, ROUTING_TABLE_DIM); 87 | 88 | /* 89 | Router Port table provides a way to simulate the physical interface of the router 90 | The ip address is used to answer to the arp request (TO IMPLEMENT) 91 | The mac address is used as mac_scr for the outcoming packet on that interface, 92 | and as mac address contained in the arp reply 93 | */ 94 | BPF_TABLE("hash", u16, struct r_port, router_port, ROUTER_PORT_N); 95 | 96 | /* 97 | Arp Table implements a mapping between ip and mac addresses. 98 | */ 99 | BPF_TABLE("hash", u32, u64, arp_table, ARP_TABLE_DIM); 100 | 101 | #define PRINT_MAC(x) (bpf_htonll(x)>>16) 102 | 103 | struct eth_hdr { 104 | __be64 dst:48; 105 | __be64 src:48; 106 | __be16 proto; 107 | } __attribute__((packed)); 108 | 109 | struct arp_hdr { 110 | __be16 ar_hrd; /* format of hardware address */ 111 | __be16 ar_pro; /* format of protocol address */ 112 | unsigned char ar_hln; /* length of hardware address */ 113 | unsigned char ar_pln; /* length of protocol address */ 114 | __be16 ar_op; /* ARP opcode (command) */ 115 | 116 | __be64 ar_sha:48; /* sender hardware address */ 117 | __be32 ar_sip; /* sender IP address */ 118 | __be64 ar_tha:48; /* target hardware address */ 119 | __be32 ar_tip; /* target IP address */ 120 | } __attribute__((packed)); 121 | 122 | static int handle_rx(void *skb, struct metadata *md) { 123 | struct __sk_buff *skb2 = (struct __sk_buff *)skb; 124 | void *data = (void *)(long)skb2->data; 125 | void *data_end = (void *)(long)skb2->data_end; 126 | 127 | struct eth_hdr *eth = data; 128 | 129 | if (data + sizeof(*eth) > data_end) 130 | goto DROP; 131 | 132 | #ifdef BPF_TRACE_INPUT 133 | bpf_trace_printk("[router-%d]: in_ifc:%d\n", md->module_id, md->in_ifc); 134 | //bpf_trace_printk("[router-%d]: eth_type:%x mac_scr:%lx mac_dst:%lx\n", 135 | // md->module_id, bpf_htons(eth->proto), PRINT_MAC(eth->src), PRINT_MAC(eth->dst)); 136 | #endif 137 | 138 | struct r_port *in_port = router_port.lookup(&md->in_ifc); 139 | if (!in_port) { 140 | #ifdef BPF_TRACE_INPUT 141 | bpf_trace_printk("[router-%d]: received packet from non valid port : '%d'\n", 142 | md->module_id, md->in_ifc); 143 | #endif 144 | goto DROP; 145 | } 146 | 147 | /* 148 | Check if the mac destination of the packet is multicast, broadcast, or the 149 | unicast address of the router port. If not, drop the packet. 150 | */ 151 | #ifdef CHECK_MAC_DST 152 | if (eth->dst != in_port->mac && !(eth->dst & (__be64) MAC_MULTICAST_MASK)) { 153 | #ifdef BPF_TRACE_INPUT 154 | bpf_trace_printk("[router-%d]: mac destination %lx MISMATCH %lx\n", 155 | md->module_id, PRINT_MAC(eth->dst), PRINT_MAC(in_port->mac)); 156 | #endif 157 | goto DROP; 158 | } 159 | #endif 160 | 161 | switch (eth->proto) { 162 | case htons(ETH_P_IP): goto IP; // ipv4 packet 163 | case htons(ETH_P_ARP): goto ARP; // arp packet 164 | default: goto DROP; 165 | } 166 | 167 | IP: ; // ipv4 packet 168 | __be32 l3sum = 0; 169 | struct iphdr *ip = data + sizeof(*eth); 170 | if (data + sizeof(*eth) + sizeof(*ip) > data_end) 171 | goto DROP; 172 | 173 | #ifdef BPF_TRACE_TTL 174 | bpf_trace_printk("[router-%d]: ttl: %u\n", md->module_id, ip->ttl); 175 | #endif 176 | 177 | /* ICMP Echo Responder for router ports */ 178 | if (ip->protocol == IPPROTO_ICMP) { 179 | __be32 l4sum = 0; 180 | struct icmphdr *icmp = data + sizeof(*eth) + sizeof(*ip); 181 | if (data + sizeof(*eth) + sizeof(*ip) + sizeof(*icmp) > data_end) 182 | goto DROP; 183 | 184 | /* Only manage ICMP Request */ 185 | if (icmp->type == ICMP_ECHO && in_port->ip == ip->daddr) { 186 | // Reply to ICMP Echo request 187 | __u32 old_type = icmp->type; 188 | __u32 new_type = ICMP_ECHOREPLY; 189 | l4sum = bpf_csum_diff(&old_type, 4, &new_type, 4, l4sum); 190 | icmp->type = (__u8) new_type; 191 | 192 | __be32 old_src = ip->saddr; 193 | __be32 old_dst = ip->daddr; 194 | 195 | ip->daddr = old_src; 196 | ip->saddr = old_dst; 197 | 198 | __be64 old_src_mac = eth->src; 199 | __be64 old_dst_mac = eth->dst; 200 | eth->src = old_dst_mac; 201 | eth->dst = old_src_mac; 202 | 203 | #ifdef BPF_TRACE_ICMP_ECHO_REPLY 204 | bpf_trace_printk("[router-%d]: ICMP ECHO Request from 0x%x port %d. Generating Reply ...\n", 205 | md->module_id, bpf_htonl(ip->saddr), md->in_ifc); 206 | #endif 207 | 208 | bpf_l4_csum_replace(skb2, ICMP_CSUM_OFFSET, 0, l4sum, 0); 209 | pkt_redirect(skb, md, md->in_ifc); 210 | return RX_REDIRECT; 211 | } 212 | } 213 | 214 | if (ip->ttl == 1) { 215 | #ifdef BPF_TRACE_TTL 216 | bpf_trace_printk("[router-%d]: packet DROP (ttl = 0)\n", md->module_id); 217 | #endif 218 | 219 | // Set router port ip address as metadata[0] 220 | u32 mdata[3]; 221 | mdata[0] = bpf_htonl(in_port->ip); 222 | pkt_set_metadata(skb, mdata); 223 | 224 | // Send packet to slowpath 225 | pkt_controller(skb, md, SLOWPATH_TTL_EXCEEDED); 226 | return RX_CONTROLLER; 227 | } 228 | 229 | /* 230 | ROUTING ALGORITHM (simplified) 231 | 232 | for each item in the routing table (upbounded loop) 233 | apply the netmask on dst_ip_address 234 | (possible optimization, not recompute if at next iteration the netmask is the same) 235 | if masked address == network in the routing table 236 | 1- change src mac to otuput port mac 237 | 2- change dst mac to lookup arp table (or send to fffffffffffff) 238 | 3- forward the packet to dst port 239 | */ 240 | 241 | int i = 0; 242 | struct rt_entry *rt_entry_p = 0; 243 | 244 | #pragma unroll 245 | for (i = 0; i < ROUTING_TABLE_DIM; i++) { 246 | u32 t = i; 247 | rt_entry_p = routing_table.lookup(&t); 248 | if (rt_entry_p) { 249 | if ((ip->daddr & rt_entry_p->netmask) == rt_entry_p->network) { 250 | goto FORWARD; 251 | } 252 | } 253 | } 254 | 255 | #ifdef BPF_TRACE_ROUTING 256 | bpf_trace_printk("[router-%d]: no routing table match for %x\n", 257 | md->module_id, bpf_htonl(ip->daddr)); 258 | #endif 259 | 260 | goto DROP; 261 | 262 | FORWARD: ; 263 | #ifdef BPF_TRACE_ROUTING 264 | bpf_trace_printk("[router-%d]: routing table match (#%d) network: %x\n", 265 | md->module_id, i, bpf_htonl(rt_entry_p->network)); 266 | #endif 267 | 268 | // Select out interface 269 | u16 out_port = rt_entry_p->port; 270 | struct r_port *r_port_p = router_port.lookup(&out_port); 271 | if (!r_port_p) { 272 | #ifdef BPF_TRACE_ROUTING 273 | bpf_trace_printk("[router-%d]: Out port '%d' not found\n", 274 | md->module_id, out_port); 275 | #endif 276 | goto DROP; 277 | } 278 | 279 | __be32 dst_ip = 0; 280 | if (rt_entry_p->nexthop == 0) { 281 | // Next Hop is local, directly lookup in arp table for the destination ip. 282 | dst_ip = ip->daddr; 283 | } else { 284 | // Next Hop not local, lookup in arp table for the next hop ip address. 285 | dst_ip = rt_entry_p->nexthop; 286 | } 287 | 288 | __be64 *mac_entry = arp_table.lookup(&dst_ip); 289 | if (!mac_entry) { 290 | #ifdef BPF_TRACE_ARP 291 | bpf_trace_printk("[router-%d]: arp lookup failed. Send to controller", md->module_id); 292 | #endif 293 | 294 | // Set metadata and send packet to slowpath 295 | u32 mdata[3]; 296 | mdata[0] = bpf_htonl(dst_ip); 297 | mdata[1] = out_port; 298 | mdata[2] = bpf_htonl(r_port_p->ip); 299 | 300 | pkt_set_metadata(skb, mdata); 301 | pkt_controller(skb, md, SLOWPATH_ARP_LOOKUP_MISS); 302 | return RX_CONTROLLER; 303 | } 304 | 305 | #ifdef BPF_TRACE_OUTPUT 306 | bpf_trace_printk("[router-%d]: in: %d out: %d REDIRECT\n", 307 | md->module_id, md->in_ifc, out_port); 308 | #endif 309 | 310 | eth->dst = *mac_entry; 311 | eth->src = r_port_p->mac; 312 | 313 | /* Decrement TTL and update checksum */ 314 | __u32 old_ttl = ip->ttl; 315 | __u32 new_ttl = ip->ttl - 1; 316 | l3sum = bpf_csum_diff(&old_ttl, 4, &new_ttl, 4, l3sum); 317 | ip->ttl = (__u8) new_ttl; 318 | 319 | bpf_l3_csum_replace(skb2, IP_CSUM_OFFSET, 0, l3sum, 0); 320 | 321 | pkt_redirect(skb,md,out_port); 322 | return RX_REDIRECT; 323 | 324 | ARP: ; // arp packet 325 | struct arp_hdr *arp = data + sizeof(*eth); 326 | if (data + sizeof(*eth) + sizeof(*arp) > data_end) 327 | goto DROP; 328 | if (arp->ar_op == bpf_htons(ARPOP_REQUEST) && arp->ar_tip == in_port->ip) { // arp request? 329 | #ifdef BPF_TRACE_ARP 330 | bpf_trace_printk("[arp]: Somebody is asking for my address\n"); 331 | #endif 332 | 333 | __be64 remotemac = arp->ar_sha; 334 | __be32 remoteip = arp->ar_sip; 335 | 336 | arp->ar_op = bpf_htons(ARPOP_REPLY); 337 | arp->ar_tha = remotemac; 338 | arp->ar_sha = in_port->mac; 339 | arp->ar_tip = remoteip; 340 | arp->ar_sip = in_port->ip; 341 | 342 | eth->dst = remotemac; 343 | eth->src = in_port->mac; 344 | 345 | /* register the requesting mac and ip */ 346 | arp_table.update(&remoteip, &remotemac); 347 | 348 | pkt_redirect(skb, md, md->in_ifc); 349 | return RX_REDIRECT; 350 | } else if (arp->ar_op == bpf_htons(ARPOP_REPLY)) { //arp reply 351 | #ifdef BPF_TRACE_ARP 352 | bpf_trace_printk("[router-%d]: packet is arp reply\n", md->module_id); 353 | #endif 354 | 355 | __be64 mac_ = arp->ar_sha; 356 | __be32 ip_ = arp->ar_sip; 357 | arp_table.update(&ip_, &mac_); 358 | 359 | // notify the slowpath. New arp reply received. 360 | pkt_controller(skb, md, SLOWPATH_ARP_REPLY); 361 | return RX_CONTROLLER; 362 | } 363 | return RX_DROP; 364 | 365 | DROP: 366 | #ifdef BPF_TRACE_OUTPUT 367 | bpf_trace_printk("[router-%d]: in: %d out: -- DROP\n", md->module_id, md->in_ifc); 368 | #endif 369 | return RX_DROP; 370 | } 371 | ` 372 | -------------------------------------------------------------------------------- /iomodules/router/slowpath.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package router 15 | 16 | import ( 17 | "encoding/binary" 18 | "encoding/hex" 19 | "net" 20 | "time" 21 | 22 | "github.com/foize/go.fifo" 23 | "github.com/google/gopacket" 24 | "github.com/google/gopacket/layers" 25 | "github.com/iovisor/iovisor-ovn/hover" 26 | ) 27 | 28 | const SLOWPATH_ARP_REPLY = 1 29 | const SLOWPATH_ARP_LOOKUP_MISS = 2 30 | const SLOWPATH_TTL_EXCEEDED = 3 31 | 32 | const CLEANUP_EVERY_N_PACKETS = 5 33 | 34 | const MAX_ENTRY_AGE = 15 * time.Second 35 | const MAX_QUEUE_LEN = 10 36 | 37 | type BufferQueue struct { 38 | queue *fifo.Queue 39 | last_access time.Time 40 | } 41 | 42 | func (r *RouterModule) ProcessPacket(p *hover.PacketIn) (err error) { 43 | 44 | log.Infof("[router-%d]: Packet arrived from dataplane", p.Md.Module_id) 45 | log.Infof("[router-%d]: pkt_len(%d) port_id(%d) reason(%d)\n", p.Md.Module_id, p.Md.Packet_len, p.Md.Port_id, p.Md.Reason) 46 | log.Infof("[router-%d]: next_hop: %x out_port:%d out_port_ip:%x\n", p.Md.Module_id, p.Md.Metadata[0], p.Md.Metadata[1], p.Md.Metadata[2]) 47 | 48 | log.Debugf("[router-%d]: -----PKT RECEIVED----\n%s\n", p.Md.Module_id, hex.Dump(p.Data[0:p.Md.Packet_len])) 49 | 50 | //How to decode packets? 51 | packet := gopacket.NewPacket(p.Data[0:p.Md.Packet_len], layers.LayerTypeEthernet, gopacket.Default) 52 | 53 | // if ethlayer := packet.Layer(layers.LayerTypeEthernet); ethlayer != nil { 54 | // log.Infof("ETHERNET \n") 55 | // eth, _ := ethlayer.(*layers.Ethernet) 56 | // log.Infof("srcmac:%s dstmac:%s eth_type:%s \n", eth.SrcMAC.String(), eth.DstMAC.String(), eth.EthernetType.String()) 57 | // } 58 | // 59 | // if arplayer := packet.Layer(layers.LayerTypeARP); arplayer != nil { 60 | // log.Infof("ARP \n") 61 | // arp, _ := arplayer.(*layers.ARP) 62 | // log.Infof("op: %d (%d-REQ %d-REPLY) src_ha:%s dst_ha:%s src_ip:%s dst_ip:%s\n", arp.Operation, layers.ARPRequest, layers.ARPReply, hex.EncodeToString(arp.SourceHwAddress), hex.EncodeToString(arp.DstHwAddress), hex.EncodeToString(arp.SourceProtAddress), hex.EncodeToString(arp.DstProtAddress)) 63 | // } 64 | // 65 | // if iplayer := packet.Layer(layers.LayerTypeIPv4); iplayer != nil { 66 | // log.Infof("IP \n") 67 | // ip, _ := iplayer.(*layers.IPv4) 68 | // log.Infof("srcIP:%s dstIP:%s\n", ip.SrcIP.String(), ip.DstIP.String()) 69 | // } 70 | // 71 | // if icmplayer := packet.Layer(layers.LayerTypeICMPv4); icmplayer != nil { 72 | // log.Infof("ICMP \n") 73 | // icmp, _ := icmplayer.(*layers.ICMPv4) 74 | // log.Infof("icmp seq_num:%d\n", icmp.Seq) 75 | // } 76 | 77 | switch p.Md.Reason { 78 | case SLOWPATH_TTL_EXCEEDED: 79 | log.Infof("[router-%d]: Reason -> SLOWPATH_TTL_EXCEEDED\n", p.Md.Module_id) 80 | 81 | pkt, _ := buildIcmpTtlExceeded(p.Data[:p.Md.Packet_len], p.Md) 82 | 83 | icmp_pkt := hover.PacketOut{} 84 | icmp_pkt.Data = pkt 85 | icmp_pkt.Module_id = p.Md.Module_id 86 | icmp_pkt.Sense = hover.INGRESS 87 | icmp_pkt.Port_id = p.Md.Port_id 88 | 89 | log.Infof("[router-%d]: sending ICMP TTL Exceeded' \n", p.Md.Module_id) 90 | ctrl := r.hc.GetController() 91 | ctrl.SendPacketOut(&icmp_pkt) 92 | 93 | case SLOWPATH_ARP_LOOKUP_MISS: 94 | log.Infof("[router-%d]: Reason -> ARP_LOOKUP_MISS\n", p.Md.Module_id) 95 | 96 | //enqueue packet_out, indexed for next_hop. 97 | pkt_to_queue := hover.PacketOut{} 98 | pkt_to_queue.Data = p.Data[0:p.Md.Packet_len] 99 | pkt_to_queue.Module_id = p.Md.Module_id 100 | pkt_to_queue.Sense = hover.EGRESS 101 | port_out := uint16(p.Md.Metadata[1]) 102 | pkt_to_queue.Port_id = port_out 103 | 104 | // log.Infof("generate pkt_out module_id:%d Egress port_out:%d\n%s\n", pkt_to_queue.Module_id, pkt_to_queue.Port_id, pkt_to_queue.Data) 105 | 106 | //init queue if not initialized 107 | if _, ok := r.OutputBuffer[p.Md.Metadata[0]]; !ok { 108 | //miss 109 | bufQueue := BufferQueue{} 110 | 111 | newqueue := fifo.NewQueue() 112 | bufQueue.queue = newqueue 113 | bufQueue.last_access = time.Now() 114 | r.OutputBuffer[p.Md.Metadata[0]] = &bufQueue 115 | } 116 | 117 | //lookup for queue of packets, indexed for next_hop_ip 118 | if bufQueue, ok := r.OutputBuffer[p.Md.Metadata[0]]; ok { 119 | //hit 120 | bufQueue.queue.Add(&pkt_to_queue) 121 | bufQueue.last_access = time.Now() 122 | } 123 | 124 | // Generating ARP Request (after pkt queueing) 125 | if ethlayer := packet.Layer(layers.LayerTypeEthernet); ethlayer != nil { 126 | eth, _ := ethlayer.(*layers.Ethernet) 127 | dstmac, _ := net.ParseMAC("ff:ff:ff:ff:ff:ff") 128 | 129 | arppkt, arperr := buildArpPacket(eth.SrcMAC, dstmac, int2ip(p.Md.Metadata[2]), int2ip(p.Md.Metadata[0]), layers.ARPRequest) 130 | if arperr != nil { 131 | log.Error(arperr) 132 | return nil 133 | } 134 | 135 | p_out := hover.PacketOut{} 136 | p_out.Data = arppkt 137 | p_out.Module_id = p.Md.Module_id 138 | p_out.Sense = hover.EGRESS 139 | port_out := uint16(p.Md.Metadata[1]) 140 | p_out.Port_id = port_out 141 | 142 | log.Infof("[router-%d]: sending arp request on port (%d) 'who has %x ? tell %x' \n", p.Md.Module_id, p_out.Port_id, p.Md.Metadata[0], p.Md.Metadata[2]) 143 | ctrl := r.hc.GetController() 144 | ctrl.SendPacketOut(&p_out) 145 | } 146 | 147 | case SLOWPATH_ARP_REPLY: 148 | log.Infof("[router-%d]: Reason -> ARP_REPLY\n", p.Md.Module_id) 149 | 150 | if arplayer := packet.Layer(layers.LayerTypeARP); arplayer != nil { 151 | arp, _ := arplayer.(*layers.ARP) 152 | //reply? 153 | if arp.Operation != layers.ARPReply { 154 | log.Warningf("[router-%d]: No arp reply received!\n", p.Md.Module_id) 155 | } 156 | 157 | log.Infof("[router-%d]: ARP reply '%s is at %s'\n", p.Md.Module_id, net.HardwareAddr(arp.SourceHwAddress).String(), net.IP(arp.SourceProtAddress).To4().String()) 158 | 159 | next_hop_ip_int := ip2int(net.IP(arp.SourceProtAddress)) 160 | 161 | if BufQueue, ok := r.OutputBuffer[next_hop_ip_int]; ok { 162 | // log.Infof("Output Buffer Lookup HIT (%x)\n", next_hop_ip_int) 163 | if BufQueue != nil { 164 | if BufQueue.queue.Len() == 0 { 165 | // log.Infof("Delete empty queue\n") 166 | delete(r.OutputBuffer, next_hop_ip_int) 167 | } 168 | } 169 | if BufQueue != nil { 170 | if BufQueue.queue.Len() > 0 { 171 | // log.Infof("Output queue not empty. Send Packets out ...\n") 172 | //send out packets enqueued 173 | for BufQueue.queue.Len() > 0 { 174 | item := BufQueue.queue.Next() 175 | pkt := item.(*hover.PacketOut) 176 | packet := gopacket.NewPacket(pkt.Data, layers.LayerTypeEthernet, gopacket.Default) 177 | 178 | if ethlayer := packet.Layer(layers.LayerTypeEthernet); ethlayer != nil { 179 | eth, _ := ethlayer.(*layers.Ethernet) 180 | eth.DstMAC = arp.SourceHwAddress 181 | 182 | buf := gopacket.NewSerializeBuffer() 183 | opts := gopacket.SerializeOptions{} 184 | gopacket.SerializeLayers(buf, opts, eth, gopacket.Payload(pkt.Data[14:])) 185 | 186 | log.Infof("[router-%d]: send out packet from buffer. (current buffer size = %d)\n", p.Md.Module_id, BufQueue.queue.Len()) 187 | pkt.Data = buf.Bytes() 188 | 189 | // log.Infof("Sending PacketOUT\n") 190 | ctrl := r.hc.GetController() 191 | ctrl.SendPacketOut(pkt) 192 | } 193 | } 194 | } 195 | } 196 | } 197 | } 198 | } 199 | /* CLEANUP */ 200 | r.PktCounter++ 201 | //Perform OutputBuffer Cleanup every CLEANUP_EVERY_N_PACKETS received by slowpath 202 | if r.PktCounter%CLEANUP_EVERY_N_PACKETS == 0 { 203 | log.Infof("[router-%d]: Perform cleanup.\n", p.Md.Module_id) 204 | for next_hop_ip, outbuffer := range r.OutputBuffer { 205 | age := time.Now().Sub(outbuffer.last_access) 206 | // Delete all queue with last_access older than MAX_ENTRY_AGE 207 | if age > MAX_ENTRY_AGE { 208 | // Free queue 209 | log.Infof("[router-%d]: queue [%x] (size=%d) age %s > %s (MAX_AGE)\n", p.Md.Module_id, next_hop_ip, outbuffer.queue.Len(), age, MAX_ENTRY_AGE) 210 | for outbuffer.queue.Len() > 0 { 211 | outbuffer.queue.Next() 212 | } 213 | // Delete map entry 214 | delete(r.OutputBuffer, next_hop_ip) 215 | } else { 216 | // Delete old entries, maintain max queue size to MAX_QUEUE_LEN 217 | if outbuffer.queue.Len() > MAX_QUEUE_LEN { 218 | log.Infof("[router-%d]: queue [%x] size %d > %d (MAX_SIZE)\n", p.Md.Module_id, next_hop_ip, outbuffer.queue.Len(), MAX_QUEUE_LEN) 219 | for outbuffer.queue.Len() > MAX_QUEUE_LEN { 220 | outbuffer.queue.Next() 221 | } 222 | log.Infof("[router-%d]: queue [%x] size %d == %d (MAX_SIZE) CLEANUP ENDED...\n", p.Md.Module_id, next_hop_ip, outbuffer.queue.Len(), MAX_QUEUE_LEN) 223 | } 224 | } 225 | // Delete Map entries with empty queue 226 | if outbuffer.queue.Len() == 0 { 227 | log.Infof("[router-%d]: queue [%x] size %d DELETING...\n", p.Md.Module_id, next_hop_ip, outbuffer.queue.Len()) 228 | delete(r.OutputBuffer, next_hop_ip) 229 | } 230 | } 231 | } 232 | return nil 233 | } 234 | 235 | func buildIcmpTtlExceeded(pkt []byte, md hover.PacketInMd) ([]byte, error) { 236 | 237 | packet := gopacket.NewPacket(pkt, layers.LayerTypeEthernet, gopacket.Default) 238 | 239 | if ethlayer := packet.Layer(layers.LayerTypeEthernet); ethlayer != nil { 240 | // log.Infof("ETHERNET \n") 241 | eth, _ := ethlayer.(*layers.Ethernet) 242 | // log.Infof("srcmac:%s dstmac:%s eth_type:%s \n", eth.SrcMAC.String(), eth.DstMAC.String(), eth.EthernetType.String()) 243 | 244 | if iplayer := packet.Layer(layers.LayerTypeIPv4); iplayer != nil { 245 | // log.Infof("IP \n") 246 | ip, _ := iplayer.(*layers.IPv4) 247 | // log.Infof("srcIP:%s dstIP:%s\n", ip.SrcIP.String(), ip.DstIP.String()) 248 | 249 | eth_ := layers.Ethernet{ 250 | EthernetType: layers.EthernetTypeIPv4, 251 | SrcMAC: eth.DstMAC, /*Computed by Dataplane l2 forwarding*/ 252 | DstMAC: eth.SrcMAC, /*Computed by Dataplane l2 forwarding*/ 253 | } 254 | 255 | // Get Ip address of router port 256 | new_src_ip := int2ip(md.Metadata[0]) 257 | 258 | ip_ := layers.IPv4{ 259 | SrcIP: new_src_ip, /* It's the router address port */ 260 | DstIP: ip.SrcIP, /* It's the source ip of the packet */ 261 | Version: 4, 262 | TTL: 64, 263 | Protocol: layers.IPProtocolICMPv4, 264 | } 265 | 266 | icmp_ := layers.ICMPv4{ 267 | TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeTimeExceeded, layers.ICMPv4CodeTTLExceeded), 268 | } 269 | 270 | buf := gopacket.NewSerializeBuffer() 271 | opts := gopacket.SerializeOptions{ 272 | FixLengths: true, 273 | ComputeChecksums: true, 274 | } 275 | 276 | gopacket.SerializeLayers(buf, opts, 277 | ð_, 278 | &ip_, 279 | &icmp_, 280 | ip, 281 | gopacket.Payload(pkt[34:42]), 282 | ) 283 | 284 | log.Debugf("\nGenerated ICMP Packet\n%s\n", hex.Dump(buf.Bytes())) 285 | return buf.Bytes(), nil 286 | } 287 | } 288 | return nil, nil 289 | } 290 | 291 | func buildArpPacket(srcmac net.HardwareAddr, dstmac net.HardwareAddr, srcip net.IP, dstip net.IP, operation uint16) ([]byte, error) { 292 | eth, arp, err := buildArpPacketLayers(srcmac, dstmac, srcip, dstip, operation) 293 | if err != nil { 294 | log.Error(err) 295 | return nil, err 296 | } 297 | 298 | buf := gopacket.NewSerializeBuffer() 299 | opts := gopacket.SerializeOptions{} 300 | gopacket.SerializeLayers(buf, opts, ð, &arp) 301 | // log.Debugf("\nGenerated ARP Packet\n%s\n", hex.Dump(buf.Bytes())) 302 | 303 | return buf.Bytes(), nil 304 | } 305 | 306 | // buildArpPacket creates an template ARP packet with the given source and 307 | // destination. 308 | func buildArpPacketLayers(srcmac net.HardwareAddr, dstmac net.HardwareAddr, srcip net.IP, dstip net.IP, operation uint16) (layers.Ethernet, layers.ARP, error) { 309 | ether := layers.Ethernet{ 310 | EthernetType: layers.EthernetTypeARP, 311 | 312 | SrcMAC: srcmac, 313 | DstMAC: dstmac, 314 | } 315 | arp := layers.ARP{ 316 | AddrType: layers.LinkTypeEthernet, 317 | Protocol: layers.EthernetTypeIPv4, 318 | 319 | Operation: operation, 320 | 321 | HwAddressSize: 6, 322 | ProtAddressSize: 4, 323 | 324 | SourceHwAddress: []byte(srcmac), 325 | SourceProtAddress: []byte(srcip.To4()), 326 | 327 | DstHwAddress: []byte(dstmac), 328 | DstProtAddress: []byte(dstip.To4()), 329 | } 330 | return ether, arp, nil 331 | } 332 | 333 | func ip2int(ip net.IP) uint32 { 334 | if len(ip) == 16 { 335 | return binary.BigEndian.Uint32(ip[12:16]) 336 | } 337 | return binary.BigEndian.Uint32(ip) 338 | } 339 | 340 | func int2ip(nn uint32) net.IP { 341 | ip := make(net.IP, 4) 342 | binary.BigEndian.PutUint32(ip, nn) 343 | return ip 344 | } 345 | -------------------------------------------------------------------------------- /iomodules/onetoonenat/nat.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Politecnico di Torino 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package onetoonenat 15 | 16 | var NatCode = ` 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | // the following flags control the debubbing output of the eBPF program. 33 | // please note that because of some eBPF limitations all of them cannot 34 | // be activated at the same time 35 | 36 | // #define BPF_TRACE /* global trace control (should be commented to get better performance) */ 37 | 38 | #ifdef BPF_TRACE 39 | // #define BPF_TRACE_INGRESS 40 | // #define BPF_TRACE_UDP 41 | // #define BPF_TRACE_EGRESS_UDP 42 | // #define BPF_TRACE_REVERSE_UDP 43 | // #define BPF_TRACE_TCP 44 | // #define BPF_TRACE_EGRESS_TCP 45 | // #define BPF_TRACE_REVERSE_TCP 46 | // #define BPF_TRACE_ICMP 47 | // #define BPF_TRACE_EGRESS_ICMP 48 | // #define BPF_TRACE_REVERSE_ICMP 49 | // #define BPF_TRACE_DROP 50 | #endif 51 | 52 | #define EGRESS_NAT_TABLE_DIM 1024 53 | #define REVERSE_NAT_TABLE_DIM 1024 54 | 55 | #define IN_IFC 1 56 | #define OUT_IFC 2 57 | 58 | #define IP_CSUM_OFFSET (sizeof(struct eth_hdr) + offsetof(struct iphdr, check)) 59 | #define UDP_CSUM_OFFSET (sizeof(struct eth_hdr) + sizeof(struct iphdr) + offsetof(struct udphdr, check)) 60 | #define TCP_CSUM_OFFSET (sizeof(struct eth_hdr) + sizeof(struct iphdr) + offsetof(struct tcphdr, check)) 61 | 62 | #define IS_PSEUDO 0x10 63 | 64 | #define PRINT_MAC(x) (bpf_htonll(x)>>16) 65 | 66 | // egress nat key 67 | struct egress_nat_key{ 68 | __be32 ip_src; 69 | }; 70 | 71 | // egress nat value 72 | struct egress_nat_value{ 73 | __be32 ip_src_new; 74 | }; 75 | 76 | // reverse nat key 77 | struct reverse_nat_key{ 78 | __be32 ip_dst; 79 | }; 80 | 81 | // reverse nat value 82 | struct reverse_nat_value{ 83 | __be32 ip_dst_new; 84 | }; 85 | 86 | // egress nat table 87 | BPF_TABLE("hash", struct egress_nat_key, struct egress_nat_value, egress_nat_table, EGRESS_NAT_TABLE_DIM); 88 | 89 | // reverse nat table 90 | BPF_TABLE("hash", struct reverse_nat_key, struct reverse_nat_value, reverse_nat_table, REVERSE_NAT_TABLE_DIM); 91 | 92 | struct eth_hdr { 93 | __be64 dst:48; 94 | __be64 src:48; 95 | __be16 proto; 96 | } __attribute__((packed)); 97 | 98 | // given ip_src returns the correspondent association in 99 | // the nat table 100 | static inline struct egress_nat_value * get_egress_value(__be32 ip_src) { 101 | // lookup in the egress_nat_table 102 | struct egress_nat_key egress_key = {}; 103 | egress_key.ip_src = ip_src; 104 | struct egress_nat_value *egress_value_p = egress_nat_table.lookup(&egress_key); 105 | return egress_value_p; 106 | } 107 | 108 | static int handle_rx(void *skb, struct metadata *md) { 109 | struct __sk_buff *skb2 = (struct __sk_buff *)skb; 110 | void *data = (void *)(long)skb2->data; 111 | void *data_end = (void *)(long)skb2->data_end; 112 | 113 | struct eth_hdr *eth = data; 114 | 115 | if (data + sizeof(*eth) > data_end) 116 | goto DROP; 117 | 118 | #ifdef BPF_TRACE_INGRESS 119 | bpf_trace_printk("[nat-%d]: in_ifc:%d eth_type:%x\n", md->module_id, md->in_ifc, bpf_ntohs(eth->proto)); 120 | bpf_trace_printk("[nat-%d]: mac_src:%lx mac_dst:%lx\n", md->module_id, PRINT_MAC(eth->src), PRINT_MAC(eth->dst)); 121 | #endif 122 | 123 | switch (eth->proto) { 124 | case htons(ETH_P_IP): goto ip; 125 | case htons(ETH_P_ARP): goto arp; 126 | default: goto EOP; 127 | } 128 | 129 | ip: ; 130 | __be32 l3sum = 0; 131 | struct iphdr *ip = data + sizeof(*eth); 132 | if (data + sizeof(*eth) + sizeof(*ip) > data_end) 133 | goto DROP; 134 | 135 | switch (ip->protocol) { 136 | case IPPROTO_UDP: goto udp; 137 | case IPPROTO_TCP: goto tcp; 138 | case IPPROTO_ICMP: goto icmp; 139 | default: goto EOP; 140 | } 141 | 142 | // nat is transparent for arp packets, this is the reason a nat must always 143 | // be deployed attached to a Router, that manages layer 2 144 | arp: { 145 | if (md->in_ifc == OUT_IFC) { 146 | pkt_redirect(skb, md, IN_IFC); 147 | return RX_REDIRECT; 148 | } else if (md->in_ifc == IN_IFC) { 149 | pkt_redirect(skb, md, OUT_IFC); 150 | return RX_REDIRECT; 151 | } 152 | return RX_DROP; 153 | } 154 | 155 | icmp: { 156 | // BEGIN ICMP 157 | __be32 l4sum = 0; 158 | struct icmphdr *icmp = data + sizeof(*eth) + sizeof(*ip); 159 | 160 | if (data + sizeof(*eth) + sizeof(*ip) + sizeof(*icmp) > data_end) 161 | return RX_DROP; 162 | 163 | #ifdef BPF_TRACE_ICMP 164 | bpf_trace_printk("[nat-%d]: ICMP packet type: %d code: %d\n", md->module_id, icmp->type, icmp->code); 165 | bpf_trace_printk("[nat-%d]: ICMP packet id: %d seq: %d\n", md->module_id, icmp->un.echo.id, icmp->un.echo.sequence); 166 | #endif 167 | 168 | switch (md->in_ifc) { 169 | case IN_IFC: goto EGRESS_ICMP; 170 | case OUT_IFC: goto REVERSE_ICMP; 171 | } 172 | goto DROP; 173 | 174 | EGRESS_ICMP: ; 175 | // packet exiting the nat, apply nat translation 176 | struct egress_nat_value *egress_value_p = get_egress_value(ip->saddr); 177 | if (egress_value_p) { 178 | #ifdef BPF_TRACE_EGRESS_ICMP 179 | bpf_trace_printk("[nat-%d]: EGRESS NAT ICMP. ip->saddr translation: 0x%x->0x%x\n", md->module_id, bpf_ntohl(ip->saddr), bpf_ntohl(egress_value_p->ip_src_new)); 180 | #endif 181 | 182 | // change ip saddr 183 | __be32 old_ip = ip->saddr; 184 | __be32 new_ip = egress_value_p->ip_src_new; 185 | l3sum = bpf_csum_diff(&old_ip, 4, &new_ip, 4, l3sum); 186 | ip->saddr = new_ip; 187 | 188 | // update checksum(s) 189 | bpf_l3_csum_replace(skb2, IP_CSUM_OFFSET ,0, l3sum, 0); 190 | 191 | // redirect packet 192 | pkt_redirect(skb, md, OUT_IFC); 193 | return RX_REDIRECT; 194 | } else { 195 | goto DROP; 196 | } 197 | 198 | REVERSE_ICMP: ; 199 | // packet coming back, apply reverse nat translaton 200 | struct reverse_nat_key reverse_key = {}; 201 | reverse_key.ip_dst = ip->daddr; 202 | 203 | struct reverse_nat_value *reverse_value_p = reverse_nat_table.lookup(&reverse_key); 204 | if (reverse_value_p) { 205 | #ifdef BPF_TRACE_REVERSE_ICMP 206 | bpf_trace_printk("[nat-%d]: REVERSE NAT ICMP. ip->daddr translation: 0x%x->0x%x\n", md->module_id, bpf_ntohl(ip->daddr), bpf_ntohl(reverse_value_p->ip_dst_new)); 207 | #endif 208 | 209 | // change ip daddr 210 | __be32 old_ip = ip->daddr; 211 | __be32 new_ip = reverse_value_p->ip_dst_new; 212 | l3sum = bpf_csum_diff(&old_ip, 4, &new_ip, 4, l3sum); 213 | ip->daddr = new_ip; 214 | 215 | // update checksum(s) 216 | bpf_l3_csum_replace(skb2, IP_CSUM_OFFSET ,0, l3sum, 0); 217 | 218 | // redirect packet 219 | pkt_redirect(skb, md, IN_IFC); 220 | return RX_REDIRECT; 221 | } else { 222 | goto DROP; 223 | } 224 | // END ICMP 225 | goto EOP; 226 | } 227 | 228 | udp: { 229 | // BEGIN UDP 230 | __be32 l4sum = 0; 231 | struct udphdr *udp = data + sizeof(*eth) + sizeof(*ip); 232 | 233 | if (data + sizeof(*eth) + sizeof(*ip) + sizeof(*udp) > data_end) 234 | return RX_DROP; 235 | 236 | #ifdef BPF_TRACE_UDP 237 | bpf_trace_printk("[nat-%d]: UDP packet. source:%d dest:%d\n", md->module_id, bpf_ntohs(udp->source), bpf_ntohs(udp->dest)); 238 | #endif 239 | 240 | switch (md->in_ifc) { 241 | case IN_IFC: goto EGRESS_UDP; 242 | case OUT_IFC: goto REVERSE_UDP; 243 | } 244 | goto DROP; 245 | 246 | EGRESS_UDP: ; 247 | // packet exiting the nat, apply nat translation 248 | struct egress_nat_value *egress_value_p = get_egress_value(ip->saddr); 249 | if (egress_value_p) { 250 | #ifdef BPF_TRACE_EGRESS_UDP 251 | bpf_trace_printk("[nat-%d]: EGRESS NAT UDP. ip->saddr translation: 0x%x->0x%x\n", md->module_id, bpf_ntohl(ip->saddr), bpf_ntohl(egress_value_p->ip_src_new)); 252 | #endif 253 | 254 | // change ip 255 | __be32 old_ip = ip->saddr; 256 | __be32 new_ip = egress_value_p->ip_src_new; 257 | l3sum = bpf_csum_diff(&old_ip, 4, &new_ip, 4, l3sum); 258 | ip->saddr = new_ip; 259 | 260 | // update checksum(s) 261 | bpf_l4_csum_replace(skb2, UDP_CSUM_OFFSET, 0, l3sum, IS_PSEUDO | 0); 262 | bpf_l3_csum_replace(skb2, IP_CSUM_OFFSET , 0, l3sum, 0); 263 | 264 | // redirect packet 265 | pkt_redirect(skb, md, OUT_IFC); 266 | return RX_REDIRECT; 267 | } else { 268 | goto DROP; 269 | } 270 | 271 | REVERSE_UDP: ; 272 | // packet coming back, apply reverse nat translaton 273 | struct reverse_nat_key reverse_key = {}; 274 | reverse_key.ip_dst = ip->daddr; 275 | 276 | struct reverse_nat_value *reverse_value_p = reverse_nat_table.lookup(&reverse_key); 277 | if (reverse_value_p) { 278 | #ifdef BPF_TRACE_REVERSE_UDP 279 | bpf_trace_printk("[nat-%d]: REVERSE NAT UDP. ip->daddr translation: 0x%x->0x%x\n", md->module_id, bpf_ntohl(ip->daddr), bpf_ntohl(reverse_value_p->ip_dst_new)); 280 | #endif 281 | 282 | // change ip 283 | __be32 old_ip = ip->daddr; 284 | __be32 new_ip = reverse_value_p->ip_dst_new; 285 | l3sum = bpf_csum_diff(&old_ip, 4, &new_ip, 4, l3sum); 286 | ip->daddr = new_ip; 287 | 288 | // update checksum(s) 289 | bpf_l4_csum_replace(skb2, UDP_CSUM_OFFSET, 0, l3sum, IS_PSEUDO | 0); 290 | bpf_l3_csum_replace(skb2, IP_CSUM_OFFSET , 0, l3sum, 0); 291 | 292 | // redirect packet 293 | pkt_redirect(skb, md, IN_IFC); 294 | return RX_REDIRECT; 295 | } else { 296 | goto DROP; 297 | } 298 | // END UDP 299 | goto EOP; 300 | } 301 | 302 | tcp: { 303 | // BEGIN TCP 304 | __be32 l4sum = 0; 305 | struct tcphdr *tcp = data + sizeof(*eth) + sizeof(*ip); 306 | 307 | if (data + sizeof(*eth) + sizeof(*ip) + sizeof(*tcp) > data_end) 308 | return RX_DROP; 309 | 310 | #ifdef BPF_TRACE_TCP 311 | bpf_trace_printk("[nat-%d]: TCP packet. source:%d dest:%d\n", md->module_id, bpf_ntohs(tcp->source), bpf_ntohs(tcp->dest)); 312 | #endif 313 | 314 | switch (md->in_ifc) { 315 | case IN_IFC: goto EGRESS_TCP; 316 | case OUT_IFC: goto REVERSE_TCP; 317 | } 318 | goto DROP; 319 | 320 | EGRESS_TCP: ; 321 | // packet exiting the nat, apply nat translation 322 | struct egress_nat_value *egress_value_p = get_egress_value(ip->saddr); 323 | if (egress_value_p) { 324 | #ifdef BPF_TRACE_EGRESS_TCP 325 | bpf_trace_printk("[nat-%d]: EGRESS NAT TCP. ip->saddr translation: 0x%x->0x%x\n", md->module_id, bpf_ntohl(ip->saddr), bpf_ntohl(egress_value_p->ip_src_new)); 326 | #endif 327 | 328 | // change ip 329 | __be32 old_ip = ip->saddr; 330 | __be32 new_ip = egress_value_p->ip_src_new; 331 | l3sum = bpf_csum_diff(&old_ip, 4, &new_ip, 4, l3sum); 332 | ip->saddr = new_ip; 333 | 334 | // update checksum(s) 335 | bpf_l4_csum_replace(skb2, TCP_CSUM_OFFSET, 0, l3sum, IS_PSEUDO | 0); 336 | bpf_l3_csum_replace(skb2, IP_CSUM_OFFSET , 0, l3sum, 0); 337 | 338 | // redirect packet 339 | pkt_redirect(skb, md, OUT_IFC); 340 | return RX_REDIRECT; 341 | } else { 342 | goto DROP; 343 | } 344 | 345 | REVERSE_TCP: ; 346 | // packet coming back, apply reverse nat translaton 347 | struct reverse_nat_key reverse_key = {}; 348 | reverse_key.ip_dst = ip->daddr; 349 | 350 | struct reverse_nat_value *reverse_value_p = reverse_nat_table.lookup(&reverse_key); 351 | if (reverse_value_p) { 352 | #ifdef BPF_TRACE_REVERSE_TCP 353 | bpf_trace_printk("[nat-%d]: REVERSE NAT TCP. ip->daddr translation: 0x%x->0x%x\n", md->module_id, bpf_ntohl(ip->daddr), bpf_ntohl(reverse_value_p->ip_dst_new)); 354 | #endif 355 | 356 | // change ip 357 | __be32 old_ip = ip->daddr; 358 | __be32 new_ip = reverse_value_p->ip_dst_new; 359 | l3sum = bpf_csum_diff(&old_ip, 4, &new_ip, 4, l3sum); 360 | ip->daddr = new_ip; 361 | 362 | // update checksum(s) 363 | bpf_l4_csum_replace(skb2, TCP_CSUM_OFFSET, 0, l3sum, IS_PSEUDO | 0); 364 | bpf_l3_csum_replace(skb2, IP_CSUM_OFFSET , 0, l3sum, 0); 365 | 366 | // redirect packet 367 | pkt_redirect(skb, md, IN_IFC); 368 | return RX_REDIRECT; 369 | } else { 370 | goto DROP; 371 | } 372 | // END TCP 373 | goto EOP; 374 | } 375 | 376 | DROP: ; 377 | 378 | EOP: ; 379 | #ifdef BPF_TRACE_DROP 380 | bpf_trace_printk("[nat-%d]: DROP packet.\n", md->module_id); 381 | #endif 382 | return RX_DROP; 383 | } 384 | ` 385 | --------------------------------------------------------------------------------