Controller - UofG-netlab/BPFabric GitHub Wiki
The controller is a centralised application that accepts connections from the switches and using the Southbound API emits requests to the switches and receive requests from the switches.
The controller is logically centralised but to avoid a single point of failure multiple controllers can operate in the network and through a distributed database the state share amongst all of them to have a logically centralised and physically distributed controller.
The controller is necessary to install and configure function, however once installed the connection to the controller is not necessary and the switch will continue operating even if the connection to the controller is terminated.
The controller can send the following requests, see the switch documentation to see how each request is processed:
Hello
FunctionAddRequest
FunctionRemoveRequest
FunctionListRequest
TablesListRequest
TableListRequest
TableEntryGetRequest
TableEntryInsertRequest
TableEntryDeleteRequest
PacketOut
And processes the following event requests from the agent:
Hello
PacketIn
Notify
As part of BPFabric a simple python framework has been provided to allow any controller function to be implemented.
A custom controller can be implemented by extending the eBPFCoreApplication
class. This class will perform the Hello
handshake with the switch and nothing else. It is your responsibility to install the functions when necessary, update the tables if needed and react to incoming PacketIn
and Notify
events. Event handlers can be registered using the @set_event_handler
decorator.
Example of a basic controller that only install the learning switch function a index 0 of the pipeline on handshake
from core import eBPFCoreApplication, set_event_handler, FLOOD
from core.packets import *
class LearningSwitchApplication(eBPFCoreApplication):
@set_event_handler(Header.HELLO)
def hello(self, connection, pkt):
self.mac_to_port = {}
with open('../examples/learningswitch.o', 'rb') as f:
print("Installing the eBPF ELF")
connection.send(FunctionAddRequest(name="learningswitch", index=0, elf=f.read()))
if __name__ == '__main__':
LearningSwitchApplication().run()
This example controller waits for the Hello
event from the switch, once received it loads the learningswitch.o
eBPF program from disk and send a FunctionAddRequest
for the function named learningswitch
at index 0
in the pipeline.
You can react to Notify
events emitted by the switches using the following snippet. connection
will identify the switch that emitted the notification and pkt
will be the notification event with an id
for the event and some data.
@set_event_handler(Header.NOTIFY)
def notify_event(self, connection, pkt):
print(pkt.id, pkt.data.encode('hex'))
Finally you can react to PacketIn
events similarly. For instance if the pipeline emits a CONTROLLER
action the packet will be handled by the handler below. In this example the getPort
function is responsible for finding on which port this packet should be sent and resent the packet to the switch to be transmitted.
@set_event_handler(Header.PACKET_IN)
def packet_in(self, connection, pkt):
connection.send(PacketOut(data=pkt.data, out_port=getPort(pkt)))
A Command Line Interface controller is provided that can send all the requests defined in BPFabric Southbound API except PacketOut
. The CLI can be useful to test controller logic and inspect the state of the switches by listing the functions and tables installed in the switch.
The logic for PacketIn
and Notify
is specific to the function installed and requires a controller designed to support those messages. The CLI will not perform any action on receiving one of those event but will print in hexadecimal the packet or notification received.
The mapping between CLI command and Southbound API request is the following:
CLI | Southbound API | Example |
---|---|---|
<dpid> list | FunctionListRequest() | 1 list |
<dpid> add <index> <name> <path> | FunctionAddRequest(name, index, elf) | 1 add 0 learningswitch ../examples/learningswitch.o |
<dpid> remove <index> | FunctionRemoveRequest(index) | 1 remove 0 |
<dpid> table <index> list | TablesListRequest | 1 table 0 list |
<dpid> table <index> <table_name> list | TableListRequest(index, table_name) | 1 table 0 inports list |
<dpid> table <index> <table_name> get hex:key | TableEntryGetRequest(index, table_name, key) | 1 table table 0 inports get 00e0b45aadd5 |
<dpid> table <index> <table_name> update hex:key hex:value | TableEntryInsertRequest(index, table_name, key, value) | 1 table 0 inports update 00e0b45aadd6 01 |
<dpid> table <index> <table_name> delete hex:key | TableEntryDeleteRequest(index, table_name, key) | 1 table 0 inports delete 00e0b45aadd6 |