Instrukcja do laboratorium 5 Implementacja podstawowych reguł

Transkrypt

Instrukcja do laboratorium 5 Implementacja podstawowych reguł
PRZEDMIOT:
PROWADZĄCY:
ROK STUDIÓW :
SEMESTR:
KIERUNEK:
Sieci Sterowane Programowo
mgr inż. Grzegorz Rzym ([email protected])
I (2 stopień)
zimowy
Teleinformatyka
Instrukcja do laboratorium 5
Implementacja
podstawowych
reguł
z wykorzystaniem
kontrolera
Floodlight.
przepłwywów.
protokołu
Reaktywna
OpenFlow
instalacja
1. Cel ćwiczenia
Celem ćwiczenia jest implementacja własnego modułu sterownika Floodlight. Moduł ten nasłuchiwał
będzie wiadomości protokołu OpenFlow typu Packet_In, rozpakowywał je, a następnie, zgodnie
z zadaną logiką, instalował potrzebne przepływy w przełącznikach OpenFlow.
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 ze strony: http://www.kt.agh.edu.pl/~rzym/lectures/TI-SDN/floodlight-1.2lab5.zip a następnie rozpakować go.
Kontroler zawiera już kilka klas w paczce pl.edu.agh.kt, m.in. moduł SdnLabListener, klasę
PacketExtractor, oraz nową klasę Flows.java, która będzie wykorzystywana podczas dzisiejszych
zajęć.
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->Run Configurations
• Prawym przyciskiem myszy wybrać Java Application->New
• W polu nazwy wpisać FloodlightLaunch
• W polu Project wybrać Floodlight
• W polu Main Class wybrać net.floodlightcontroller.core.Main
• Kliknąć przycisk Apply
1
PRZEDMIOT:
PROWADZĄCY:
ROK STUDIÓW :
SEMESTR:
KIERUNEK:
Sieci Sterowane Programowo
mgr inż. Grzegorz Rzym ([email protected])
I (2 stopień)
zimowy
Teleinformatyka
3. Implementacja wiadomości PacketOut
Wiadomość PacketOut może zostać użyta w celu wstrzyknięcia pakietu z kontrolera do płaszczyzny
danych przełącznika i przesłania go dalej (wysłania pakietu z kontrolera, z płaszczyzny sterowania,
który pojawi się w sieci, w płaszczyźnie danych). Taka sytuacja może mieć miejsce np. gdy kontroler
jest też odpowiedzialny za przyznawanie adresów IP w sieci (moduł serwera DHCP), rozwiązywanie
nazw domenowych (moduł DNS) itp.
Uwaga: Wiadomość PacketOut zwykle jest odpowiedzią na wiadomość PacketIn i nie instaluje ona
przepływu w tablicy przepływów przełączników OpenFlow. Służy tylko do wstrzyknięcia pakietów
bezpośrednio z kontrolera lub do nadania akcji pakietom, które zostały zapisane w buforze
przełącznika.
Proces przygotowania pakietu i wstrzyknięcia go do sieci następuje w następującej kolejności:
• Przygotowanie nagłówków kolejnych warstw, ustawienie ich pól,
• Ustawienie jako ładunku danych danej warstwy nagłówka kolejnej warstwy (np. ładunkiem dla
warstwy drugiej jest nagłówek IP, itd.),
• Serializacja,
• Utworzenie wiadomości PacketOut, w której jako dane znajdują się zserializowane nagłówki
oraz dane aplikacyjne,
• Ustawienie portów wejściowych i wyjściowych przełącznika,
• Wysłanie pakietu do przełącznika.
warstw zdefiniowana jest w paczce
Uwaga: Większość nagłówków kolejnych
net.floodlightcontroller.packet, przygotowane klasy oraz ich metody w łatwy sposób pozwalają na
komponowanie własnych pakietów.
Uwaga2: Wiadomość PacketOut bardzo często budowana jest z ładunku wiadomości PacketIn, np. dla
pierwszego pakietu danego przepływu, który do czasu podjęcia decyzji przez kontroler o utworzeniu
nowego przepływu w tablicy przełącznika specyfikuje, co zrobić z buforowanym pakietem.
W ramach tego ćwiczenia przygotowany zostanie (od podstaw) datagram UDP wraz z kolejnymi
(niższymi) warstwami oraz losowymi danymi, a następnie wysłany zostanie jako pakiet do
przełącznika, który z kolei wyśle je do wszystkich podłączonych hostów - jako port wyjściowy
zostanie ustawiony interfejs FLOOD, co oznacza, że pakiet zostanie wysłany na wszystkie interfejsy
oprócz tego, z którego przyszedł.
W ramach ćwiczenia rozbudowywane będzie ciało metody sendPacketOut() klasy Flows.java
3.1 Warstwa druga
Zadanie rozpoczęte zostanie od utworzenia nowego nagłówka ethernetowego oraz ustawienia kilku
jego pól. Proszę zwrócić uwagę na EtherType ustawiony na IPv4, co oznacza że ramka niosła będzie
pakiet IP w wersji 4.
// Ethernet
Ethernet l2 = new Ethernet();
l2.setSourceMACAddress(MacAddress.of("00:00:00:00:00:01"));
l2.setDestinationMACAddress(MacAddress.BROADCAST);
l2.setEtherType(EthType.IPv4);
2
PRZEDMIOT:
PROWADZĄCY:
ROK STUDIÓW :
SEMESTR:
KIERUNEK:
Sieci Sterowane Programowo
mgr inż. Grzegorz Rzym ([email protected])
I (2 stopień)
zimowy
Teleinformatyka
3.2 Warstwa trzecia
Nowy pakiet IPv4 jest utworzony jako nośnik datagramów UDP (IpProtocol.UDP). Nadane mu zostają
adresy IP źródła i celu oraz ustawiona zostaje domyślna wartość pola TTL. Można spróbować ustawić
także inne pola nagłówka IP.
// IP
IPv4 l3 = new IPv4();
l3.setSourceAddress(IPv4Address.of("192.168.1.1"));
l3.setDestinationAddress(IPv4Address.of("192.168.1.255"));
l3.setTtl((byte) 64);
l3.setProtocol(IpProtocol.UDP);
3.3 Warstwa czwarta
Następnie tworzony jest nowy nagłówek UDP wraz z ustawieniem portów źródłowego i docelowego
(w tym przykładzie port usługi DNS).
// UDP
UDP l4 = new UDP();
l4.setSourcePort(TransportPort.of(65003));
l4.setDestinationPort(TransportPort.of(53));
3.4 Warstwa siódma (dane)
Jeśli datagram UDP powinien zawierać kolejne nagłówki, powinny one zostać dodane tak jak w
powyższych przykładach. Na potrzeby zajęć załóżmy, że to już tyle nagłówków (sam kontroler nie
dostarcza także klasy z nagłówkiem DNS, można znaleźć zewnętrzne biblioteki pozwalające na łatwą
integrację). W tworzonym przykładzie utworzymy 1kB losowych danych, służących jako ostateczny
payload pakietu. Oczywiście każdy rodzaj danych/payloadu może zostać utworzony za pomocą klasy
net.floodlightcontroller.packet.Data lub manualnie.
// Layer 7 data
Data l7 = new Data();
l7.setData(new byte[1000]);
3.5 Ustawianie ładunków i serializacja danych
Do tej pory każdy z nagłówków został stworzony jako osobny obiekt. Kolejnym krokiem jest
ustawienie kolejnych nagłówków (ewentualnie danych końcowych) jako ładunek warstwy niższej.
// set the payloads of each layer
l2.setPayload(l3);
l3.setPayload(l4);
l4.setPayload(l7);
A następnie serializcja całego nagłówka warstwy drugiej (już wraz z kolejnymi nagłówkami
i losowymi danymi).
// serialize
byte[] serializedData = l2.serialize();
3
PRZEDMIOT:
PROWADZĄCY:
ROK STUDIÓW :
SEMESTR:
KIERUNEK:
Sieci Sterowane Programowo
mgr inż. Grzegorz Rzym ([email protected])
I (2 stopień)
zimowy
Teleinformatyka
3.6 Utworzenie pakietu typu PacketOut i wysłanie go do przełącznika
Końcowym etapem jest utworzenie wiadomości PacketOut i wysłanie go do przełącznika. Proces ten
polega na:
• ustawieniu jako payloadu wiadomości PacketOut danych, którego do tej pory zostały
stworzone,
• wyspecyfikowaniu porty wyjściowego spośród dostępnych portów i przypisanie mu akcji,
• wyspecyfikowaniu portu wejściowego (w tym wypadku portu łączącego kontroler z
przełącznikiem),
• zbudowaniu obiektu,
• wysłaniu pakietu do przełącznika.
// Create Packet-Out and Write to Switch
OFPacketOut po = sw.getOFFactory()
.buildPacketOut()
.setData(serializedData)
.setActions(Collections.singletonList((OFAction)sw.getOFFactory().actions().output(
OFPort.FLOOD, 0xffFFffFF)))
.setInPort(OFPort.CONTROLLER).build();
sw.write(po);
3.7 Specjalizacja warunków wysłania wiadomości
W tym etapie należy wyspecyfikować, kiedy przygotowany przez nas pakiet ma zostać wysłany do
przełącznika. Może on być wysyłany tylko raz podczas startu kontrolera, albo np. za każdym razem,
gdy pojawi się specyficzny pakiet typu PacketIn (np. po sprawdzeniu, że było to nowe zapytanie o
DNS w naszym module). Do sprawdzenia tych przypadków może posłużyć klasa PacketExtractor
budowana podczas zajęć nr 4.
Na potrzeby naszych zajęć uprzednio utworzoną wiadomością reagować będziemy na każdy nowy
PacketIn.
W tym celu metodzie receive() klasy pl.edu.agh.kt.SdnLabListener należy doimplementować
poniższy kod. Spowoduje to, że każdy nowy PacketIn wyzwoli utworzenie i wysłanie przygotowanej
wiadomości PacketOut.
//sending PacketOut
Flows.sendPacketOut(sw);
Zadanie1: Proszę uruchomić topologię minimalną poleceniem:
$ sudo mn --controller=remote,ip=<controller_ip>,port=6653
a następnie przetestować działanie sieci (zaobserwować w niej utworzony pakiet). Proszę dokonać
modyfikacji pakietu (wybranych pól wybranych nagłówków) i sprawdzić, czy otrzymywane pakiety
zawierają zmiany.
Podpowiedź: W tym celu można uruchomić konsolę xmterm jednego z hostów, a na niej uruchomić
program tcpdump nasłuchujący datagramów UDP.
Uwaga:
W
przygotowanym
projekcie
wyłączony
został
moduł
net.floodlightcontroller.forwarding.Forwarding odpowiedzialny za tworzenie przepływów
w przełączniku. Dlatego też, każdy pakiet wyzwala wiadomość PacketIn.
4
PRZEDMIOT:
PROWADZĄCY:
ROK STUDIÓW :
SEMESTR:
KIERUNEK:
Sieci Sterowane Programowo
mgr inż. Grzegorz Rzym ([email protected])
I (2 stopień)
zimowy
Teleinformatyka
4. Implementacja wiadomości FlowAdd
Wiadomość FlowAdd służy do dodawania wpisów to tablicy przepływów przełączników OpenFlow.
Oprócz tej wiadomości dostępne są m.in.: FlowDelete, FlowDeleteStrict, FlowModify,
FlowModifyStrict, FlowStatsEntry, FlowStatsRequest, FlowStatsReplay. Ponieważ znaczenia tych
wiadomości są zawarte w ich nazwie (i proste do rozszyfrowania), dalsza część instrukcji skupi się
tylko na wiadomości FlowAdd.
Kontroler Floodlight wspiera protokół OpenFlow w wersjach 1.0-1.5 (z tym, że ostatnia wersja 1.5,
która jest obecna w trakcie pisania tej instrukcji, zaimplementowana została dopiero w kodzie
developerskim, który można pobrać bezpośrednio z GitHuba). W kodzie kontrolera istnieje
uniwersalne API pozwalające budować wsparcie dla wielu różnych wersji protokołu jednocześnie.
W tej części pokazane zostanie jak dodać prosty moduł obsługujący minimalną topologię sieci
środowiska Mininet (tj. jeden przełącznik i dwa hosty). Moduł ten sprawdzał będzie, z którego
interfejsu przyszedł nowy pakiet i instalował odpowiedni przepływ przekierowywujący każdy pakiet z
danego interfejsu na drugi dostępny interfejs, tj. jeśli pakiet przyszedł z interfejsu nr 1 zostanie
wysłany na interfejs nr 2 i odwrotnie.
Implementacja dokonana zostanie w metodzie simpleAdd() klasy pl.edu.agh.kt.Flows.
Uwaga: Na potrzeby laboratorium instrukcja została zbudowana zgodnie z wersją 1.0.0 protokołu
OpenFlow.
4.1 Przygotowanie pakietu FlowMod
Na początku przygotowujemy sobie builder pakietu FlowMod korzystając z dostępnego API
kontrolera. Do niego wpisywane następnie zostaną: dopasowanie oraz lista akcji.
// FlowModBuilder
OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd();
4.2 Przygotowywanie dopasowania
Korzystając z dostępnego API tworzone jest nowe dopasowanie (match). W poniższym kodzie
wyciągana jest wartość portu wejściowego, na którym pokazał się pakiet typu PacketIn i wpisywany
jest do tworzonego dopasowania. Takie dopasowanie spowoduje, że wszystkim pakietom, które
pojawią sie na danym porcie przełącznika zostanie przypisana akcja, która zostanie utworzona
w kolejnym punkcie instrukcji.
Uwaga: Dostępne API samo tworzy odpowiednią maskę do zadanego dopasowania. Istnieje
możliwość ręcznej ingerencji i ustawienie własnej maski.
// match
Match.Builder mb = sw.getOFFactory().buildMatch();
mb.setExact(MatchField.IN_PORT, pin.getInPort());
Match m = mb.build();
4.3 Tworzenie listy akcji
Ponieważ do danego przepływu może zostać przypisana więcej niż jedna akcja (np. zmień docelowy
adres IP na 192.168.6.57 i wyślij pakiet na port nr 2), tworzona jest lista akcji. W naszym przykładzie
5
PRZEDMIOT:
PROWADZĄCY:
ROK STUDIÓW :
SEMESTR:
KIERUNEK:
Sieci Sterowane Programowo
mgr inż. Grzegorz Rzym ([email protected])
I (2 stopień)
zimowy
Teleinformatyka
będziemy chcieli wysłać pakiet pasujący do danego dopasowania na odpowiedni port (argument
outPort tworzonej metody).
// actions
OFActionOutput.Builder aob = sw.getOFFactory().actions().buildOutput();
List<OFAction> actions = new ArrayList<OFAction>();
aob.setPort(outPort);
aob.setMaxLen(Integer.MAX_VALUE);
actions.add(aob.build());
4.4 Wpisanie dopasowania i listy akcji do buildera pakietu FlowAdd
Następnie należy wpisać do buildera pakietu FlowAdd utworzone dopasowania oraz listę akcji.
Ponadto należy także ustawić takie wartości jak wartości liczników Hard oraz Idle Timeout, ID
buffora, w którym znajduje się kopia pakietu, który został wysłany do kontrolera (jemu też trzeba
nadać odpowiednie akcje - w tym wypadku wysłać go na odpowiedni port wyjściowy) oraz priorytet
wpisywanego przepływu. Całość w kodzie jak poniżej.
fmb.setMatch(m)
.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
.setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
.setBufferId(pin.getBufferId())
.setOutPort(outPort)
.setPriority(FLOWMOD_DEFAULT_PRIORITY);
fmb.setActions(actions);
4.5 Wysłanie wiadomości do przełącznika
Ostatnim etapem jest wysłanie stworzonej wiadomości do przełącznika. W poniższym przykładzie
stosowana jest przechwytywanie wyjątków, tzn. w razie niepowodzenia na ekran zostanie
wyświetlony wyjątek z kodem błędu. Powodzenie wpisania przepływu skutkuje wyświetleniem
informacji o dodanym przepływie.
// write flow to switch
try {
sw.write(fmb.build());
logger.info("Flow from port {} forwarded to port {}; match: {}",
new Object[] { pin.getInPort().getPortNumber(), outPort.getPortNumber(),
m.toString() });
} catch (Exception e) {
logger.error("error {}", e);
}
4.6 Modyfikacja metody receive() klasy pl.edu.agh.kt.SdnLabListener
Ostatnim etapem jest modyfikacja metody receive() klasy pl.edu.agh.kt.SdnLabListener, w której
należy dodać wywołanie dodania przepływu w następstwie pojawienia się wiadomości PacketIn.
Ponieważ jednym z parametrów metody simpleAdd() jest port wyjściowy, na który zostanie wysłany
6
PRZEDMIOT:
PROWADZĄCY:
ROK STUDIÓW :
SEMESTR:
KIERUNEK:
Sieci Sterowane Programowo
mgr inż. Grzegorz Rzym ([email protected])
I (2 stopień)
zimowy
Teleinformatyka
odebrany pakiet stworzona zostanie prosta konstrukcja warunkowa powodująca, że jeżeli pakiet został
odebrany na porcie nr 1 to zostanie wysłany na port nr 2, i vice versa. Port wyjściowy pierwotnie
został ustawiony na wartość równą 0, aby zapobiec przypadkowemu błędowi. Należy dodać
następujący kod:
OFPacketIn pin = (OFPacketIn) msg;
OFPort outPort=OFPort.of(0);
if (pin.getInPort() == OFPort.of(1)){
outPort=OFPort.of(2);
} else
outPort=OFPort.of(1);
Flows.simpleAdd(sw, pin, cntx, outPort);
Zadanie 2: Proszę przetestować działanie kodu. Jeśli nie ma żadnych błędów ping pomiędzy hostami
h1 a h2 w konfiguracji minimalnej powinien działać. Zaobserwowane powinno zostać także w logach
dodawanie odpowiednich przepływów.
Zadanie 3: Proszę wylistować zainstalowane przepływy z poziomu przełącznika.
Uwaga: Ponieważ idle timeout ustawiony jest w stałych klasy na wartość równą 5, przepływy te
czyszczone są po 5 sekundach od zaobserwowania ostatniego pakietu z danego przepływu.
Zadanie4: Korzystając z dostarczonej metody createMatchFromPacket() klasy pl.edu.agh.kt.Flows
proszę tak zmienić implementację metody simpleAdd(), aby dopasowywanie następowało nie tylko na
podstawie portu wejściowego, ale także zawartości pól kolejnych nagłówków modelu OSI-ISO.
7

Podobne dokumenty