Instrukcja do laboratorium 4 Wprowadzenie do tworzenia własnego modułu w kontrolerze Floodlight. 1. Cel ćwiczenia Celem ćwiczenia jest implementacja własnego modułu sterownika Floodlight w języku Java. Moduł ten nasłuchiwał będzie pakietów OpenFlow typu Packet_IN, rozpakowywał je i wyświetlał na konsolę informacje w nich zawarte. 2. Przygotowanie środowiska 2.1 Instalacja wymaganych zależności Przed zbudowaniem i uruchomieniem środowiska należy zainstalować potrzebne repozytoria poleceniem: $ sudo apt-get update $ sudo apt-get install build-essential ant maven python-dev eclipse 2.2 Pobieranie i budowanie kontrolera Proszę pobrać kontroler (version 1.2) ze strony projektu https://github.com/floodlight/floodlight/archive/v1.2.tar.gz a następnie rozpakować go, np. poleceniem: $ tar -xf floodlight* Następnie należy przejść do rozpakowanego katalogu i zbudować kontroler ze źródeł poleceniem $ ant eclipse 2.3 Import projektu do środowiska Eclipse IDE Proszę uruchomić środowisko Eclipse i utworzyć nowe Workspace. W celu importu projektu proszę wybrać kolejno: File -> Import -> General -> Existing Projects into Workspace Kliknąć na przycisk Browse obok Select root directory i odnaleźć katalog, do którego został pobrany wcześniej projekt Floodlight W oknie Projects zaznaczyć Floodlight Kliknąć przycisk Finish. Po tym procesie cały projekt powinien zostać zaimportowany. W lewej części środowiska Eclipse w oknie Package Explorer powinno pojawić się drzewo katalogów projektu Floodlight. 2.4 Budowanie i uruchamianie projektu w środowsku Eclipse Aby zbudować projekt (np. po zmianach w kodzie źródłowym) należy utworzyć cel: Wybrać Run->External Tools->External Tools Configurations Wybrać Ant->New Configuration W polu nazwy wpisać FloodlightLaunch W polu Buildfile wybrać plik build.xml W polu Base directory wybrać floodlight W zakładce Targets wybrać run Kliknąć przycisk Apply 1
3. Implementacja 3.1 Przygotowanie klasy nasłuchującej (Listenera) pakietów OpenFlow Przed przystąpieniem implementacji klas i metod należy utworzyć własną paczkę, w której to nowe klasy będą umieszczone. W tym celu proszę kliknąć prawym przyciskiem myszy na src/main/java i wybrać New->Package. Proszę nazwać nową paczkę pl.edu.agh.kt tak jak na rysunku 1. Rysunek 1. Dodawanie paczki Następnie klikając prawym przyciskiem myszy na paczkę pl.edu.agh.kt proszę wybrać New->Class. W polu nazwa klasy proszę wpisać SdnLabListener. W ramce interfejsów proszę wybrać przycisk Add i dodać następujące interfejsy: IOFMessageListener oraz IFloodlightModule. Okno dodawania klasy powinno wyglądać tak jak na rysunku 2. Zanim zaczną Państwo implementować kolejne metody warto dodać wymagane zależności. Oczywiście środowisko Eclipse upraszcza sposób importowania zależności (włącznie z automatyzacją tego procesu) jednak, aby uniknąć błędów proszę ręcznie w sekcji importów dodać następujące wpisy: import net.floodlightcontroller.core.ifloodlightproviderservice; import java.util.arraylist; import org.slf4j.logger; import org.slf4j.loggerfactory; Następnie w ciele klasy SdnLabListener proszę dodać kilka zmiennych, które będą wykorzystywane w tej klasie. Ponieważ nasłuchiwać będziemy wiadomości protokołu OpenFlow należy zarejestrować się w klasie FloodlightProvider (usługa IFloodlightProviderService). Potrzebujemy także dodać zmienną logger, za pomocą której logowane będą informację na wyjście (konsolę). protected IFloodlightProviderService floodlightprovider; protected static Logger logger; 2
Rysunek 2. Okno dodawania nowej klasy do projektu Następnie należy zaimplementować metodę pozwalającą systemowi ładowania modułów znaleźć naszą klasę. W tym celu należy dodać następującą implementację metody getmoduledependencies(). public Collection<Class<? extends IFloodlightService>> getmoduledependencies() { Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); l.add(ifloodlightproviderservice.class); return l; 3
Następnie należy zaimplementować metodę Init(), która jest wywoływana podczas startu kontrolera. Służy ona do ładowania wszystkich zależności oraz inicjacji struktur danych. public void init(floodlightmodulecontext context) throws FloodlightModuleException { floodlightprovider = context.getserviceimpl(ifloodlightproviderservice.class); logger = LoggerFactory.getLogger(SdnLabListener.class); Należy także zaimplementować metodę zwracającą nazwę naszej klasy: getname(). public String getname() { return SdnLabListener.class.getSimpleName(); 3.2 Obsługa wiadomości PacketIN protokołu OpenFlow W celu obsługi wiadomości PACKET_IN protokołu OpenFlow należy dodać naszą klasę do Listenera tych wiadomości. Dodatkowo dodajemy prostą informację do loggera, dzięki której w przyszłości będziemy widzieć, że nasz moduł wystartował. W tym celu należy w metodzie startup() dokonać następującej implementacji. public void startup(floodlightmodulecontext context) throws FloodlightModuleException { floodlightprovider.addofmessagelistener(oftype.packet_in, this); logger.info("******************* START **************************"); Następnie należy dodać obsługę otrzymanych wiadomości (metoda receive()). Prezentowany poniżej przykład po odbiorze nowego pakiety typu PACKET_IN wyświetla stosowny komunikat. Proszę zwrócić uwagę na ostatnią linię metody receive(). Komenda CONTINUE pozwala, aby kolejne klasy obsługujące wiadomości typu PACKET_IN mogły je obsłużyć. public net.floodlightcontroller.core.ilistener.command receive(iofswitch sw, OFMessage msg, FloodlightContext cntx) { logger.info("************* NEW PACKET IN *************"); return Command.CONTINUE; 4
3.3 Rejestracja modułu Utworzona klasa jest już gotowa. Aby można było w niej odbierać pakiety, moduł należy zarejestrować w kontrolerze. W tym celu w plikach: src/main/resources/meta- INF/services/net.floodlightcontroller.core.module.IFloodlightModule należy dodać następującą linijkę: pl.edu.agh.kt.sdnlablistener src/main/resources/floodlightdefault.properties dodać moduł pl.edu.agh.kt.sdnlablistener tak jak na rysunku 3 (linia 20, zmiany w linii 19). Rysunek3. Rejestracja modułu w kontrolerze - plik floodlightdefault.properties 3.4 Uruchomienie i testy Proszę zbudować projekt. Można go od razu uruchomić. Kontroler w konsoli powinien powiadamiać, że nasłuchuje przełączników OpenFlow. W celu przetestowania należy uruchomić środowisko Mininet na przykład z podstawową topologią. Jednocześnie należy wskazać, że wykorzystany zostanie zewnętrzny kontroler: $ sudo mn --controller=remote,ip=<controller_ip>,port=6653 gdzie jako controller_ip można podać wartość 127.0.0.1 (korzystamy z tej samej maszyny do uruchomienia zarówno przełączników OpenFlow jak i kontrolera) 4. Wypakowywanie zawartości otrzymywanych pakietów W tej części zajęć laboratoryjnych utworzymy nową klasę, której zadaniem będzie wypakowywanie oraz wyświetlanie zawartości pakietów widzianych przez kontroler (pakietów protokołu OpenFlow). Proszę utworzyć nową klasę o nazwie PacketExtractor w paczce pl.edu.agh.kt. Na starcie proszę dodać do ciała klasy kilka zmiennych, które później będą wykorzystywane podczas implementacji kolejnych metod. private static final Logger logger = LoggerFactory.getLogger(PacketExtractor.class); private FloodlightContext cntx; private OFMessage msg; protected IFloodlightProviderService floodlightprovider; private Ethernet eth; private IPv4 ipv4; private ARP arp; private TCP tcp; private UDP udp; 5
Uwaga: Środowisko samo powinno dodać potrzebne importy. W razie problemów należy kliknąć na symbol białego krzyżyka na czerwonym w lewej części kodu, gdzie znajdują się numery linii kodu, a program sam zaproponuje potrzebne importy. Proszę zwykle wybierać te, które należą do projektu Floodlight, np. dla TCP paczka net.floodlightcontroller.packet.tcp. Poniżej zamieszczam gotową listę: import org.projectfloodlight.openflow.protocol.ofmessage; import org.projectfloodlight.openflow.types.ethtype; import org.slf4j.logger; import org.slf4j.loggerfactory; import net.floodlightcontroller.core.floodlightcontext; import net.floodlightcontroller.core.ifloodlightproviderservice; import net.floodlightcontroller.packet.arp; import net.floodlightcontroller.packet.ethernet; import net.floodlightcontroller.packet.ipv4; import net.floodlightcontroller.packet.tcp; import net.floodlightcontroller.packet.udp; Pierwszą wymaganą metodą jest metoda kontruktora klasy. Metoda ta jest pusta. Proszę ją dodać: public PacketExtractor() { Kolejną metodą jest metoda packetextract(), która za argument przyjmuje odebrany kontekst, tj. pakiet. Z niego wyciągane będą wartości pól nagłówków w poszczególnych warstwach. Proszę zauważyć, że wewnątrz tej metody jest odwołanie do kolejnej: extracteth(). public void packetextract(floodlightcontext cntx) { this.cntx = cntx; extracteth(); Następnie należy zaimplementować metodę extracteth() odpowiedzialną za wyświetlanie zawartości poszczególnych pól nagłówka ramki ethernetowej. Przykładowa (proponowana implementacja) znajduje się poniżej: public void extracteth() { eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); logger.info("frame: src mac {", eth.getsourcemacaddress()); logger.info("frame: dst mac {", eth.getdestinationmacaddress()); logger.info("frame: ether_type {", eth.getethertype()); if (eth.getethertype() == EthType.ARP) { arp = (ARP) eth.getpayload(); //extractarp(); if (eth.getethertype() == EthType.IPv4) { ipv4 = (IPv4) eth.getpayload(); //extractip(); 6
Proszę zauważyć, że w metodzie tej wyświetlany jest źródłowy/docelowy adres MAC otrzymanej ramki oraz wartość pola EtherType. Następnie w zależności od wartości pola EtherType wypakowywany jest kolejny nagłówek. W prezentowanym przykładzie ARP lub IPv4. W wyszarzonych liniach (komentarz) zaproponowano implementację kolejnych metod w zależności od typu pakietu (patrz zadania). Uwaga: Aby wyświetlanie zawartości ramki ethernetowej działało w klasie SdnLabListener należy doimplementować tworzenie nowego obiektu klasy PacketExtractor w metodzie receive(). Poniżej prezentowana jest pełna implementacja metody receive(). public net.floodlightcontroller.core.ilistener.command receive(iofswitch sw, OFMessage msg, FloodlightContext cntx) { logger.info("************* NEW PACKET IN *************"); PacketExtractor extractor = new PacketExtractor(); extractor.packetextract(cntx); return Command.CONTINUE; Zadanie 1. Proszę własnoręcznie dokończyć implementację metod extractarp() oraz extractip(). Ich zadaniem jest wyświetlanie zawartości poszczególnych pól nagłówków. Zadanie 2. Proszę zaproponować implementację metod wyświetlających zawartość pól nagłówków TCP, UDP oraz ICMP (niekoniecznie wszystkich, tych które uznane zostaną za ważne). Metody te mają być wykonywane, jeśli pakiet danego typu pojawi się w kontrolerze. Uwaga: Do wykonania obu zadań przydatny może być poradnik umieszczony pod adresem: https://floodlight.atlassian.net/wiki/display/floodlightcontroller/how+to+process+a+packet- In+Message 7