Vývoj aplikací pro HP VAN SDN kontroler Část první – REST rozhraní a Python Tomáš Kubica
HP VAN SDN Controller 2.0.0 Dokument verze 1.02 Březen 2014
Úvod Následující lab guide není oficiálním dokumentem, ani jej nelze považovat za best practice. Doporučuji absolvovat HP ExpertONE školení v oblasti SDN vývoje aplikací. Tento text je zaměřen na první kroky s SDN aplikacemi a stručně shrnuje základy OpenFlow a popisuje kontroler a jeho REST API. Následně se soustředí na vývoj jednoduchých aplikací, či chcete-li skriptů, v jazyce Python. Na tento text navazuje podobný dokument zaměřený na nativní vývoj aplikací (Java moduly pro kontroler). Materiál je vytvořen nad HP VAN SDN Controller 2.0.0, který si můžete stáhnout z sdndevcenter.hp.com. Pro jednoduché testování lze místo fyzických prvků použít simulovanou síť Mininet (www.mininet.org). Můžete použít připravené VM nebo si sestavit vlastní dle návodu v příloze. Přeji vám hodně zdaru a hlavně inovativní inspirace! Tomáš Kubica
2|HP SDN kontroler
Obsah Úvod ........................................................................................................................................................ 2 OpenFlow protokol ................................................................................................................................. 5 Základní fungování .............................................................................................................................. 5 OpenFlow zprávy ................................................................................................................................ 5 Packet IN a Packet OUT ................................................................................................................... 5 Flow-mod ........................................................................................................................................ 5 Statistiky .......................................................................................................................................... 5 Další asynchronní události .............................................................................................................. 5 OpenFlow akce a instrukce ................................................................................................................. 6 Instrukce.......................................................................................................................................... 6 Akce Output a typy portů................................................................................................................ 6 Akce Set-field, Push-Tag/Pop-Tag, Change TTL .............................................................................. 7 Akce Set-queue ............................................................................................................................... 7 Akce Group...................................................................................................................................... 7 Tabulky ................................................................................................................................................ 7 Skupiny (Group table) ......................................................................................................................... 7 Měřáky a čítače ................................................................................................................................... 8 Čítače (Counters)............................................................................................................................. 8 Měřáky (Meter table)...................................................................................................................... 8 Implementace v hardware .................................................................................................................. 8 Vlastnosti ProVision ........................................................................................................................ 9 Vlastnosti Comware platforem ....................................................................................................... 9 Služby kontroleru .................................................................................................................................... 9 Externí (REST API) vs. nativní (Java API) programování ...................................................................... 9 Core služby ........................................................................................................................................ 10 Zabudované služby............................................................................................................................ 10 Link Manager................................................................................................................................. 10 Topology Manager ........................................................................................................................ 11 Node Manager .............................................................................................................................. 11 Path Daemon ................................................................................................................................ 11 Path Diagnostics ............................................................................................................................ 11 Topology Viewer ........................................................................................................................... 11 Služby v hybridním režimu (kontroler 2.1.0 a vyšší) ......................................................................... 11 RESTful API HP SDN kontroleru ............................................................................................................. 12 Příprava prostředí ............................................................................................................................. 12 RESTful a JSON .................................................................................................................................. 12 3|HP SDN kontroler
Živá dokumentace API SDN kontroleru............................................................................................. 13 Offline REST API dokumentace ......................................................................................................... 21 Instalace pravidla .............................................................................................................................. 21 Python ................................................................................................................................................... 25 První kroky v Python živě .................................................................................................................. 25 První Python skript ............................................................................................................................ 29 Zabiják ............................................................................................................................................... 30 Koncové stanice v GUI....................................................................................................................... 31 Zabiják s GUI...................................................................................................................................... 32 Vložení inline služby (např. IPS inspekce) ......................................................................................... 33 Použití ........................................................................................................................................... 33 Logika aplikace .............................................................................................................................. 34 Kód ................................................................................................................................................ 34 Otestování ..................................................................................................................................... 37 Možnost dalšího rozvoje ............................................................................................................... 42 Závěr ..................................................................................................................................................... 43 Příloha – jak vytvořit používané VM ..................................................................................................... 44 VM pro kontroler .............................................................................................................................. 44 VM pro vývojáře................................................................................................................................ 45 Příloha – hotové skripty ........................................................................................................................ 49 where.py ........................................................................................................................................... 49 kill.py ................................................................................................................................................. 50 stanice.py .......................................................................................................................................... 51 killGUI.py ........................................................................................................................................... 52 inlinegui.py ........................................................................................................................................ 53 inlinetopo.py ..................................................................................................................................... 55
4|HP SDN kontroler
OpenFlow protokol Základní fungování Protokol OpenFlow je obvykle označován jako „jižní API“, tedy z pohledu kontroleru je to rozhraní, které mluví s infrastrukturou. Za severní API se pak označují REST interface a tak podobně. Zatím málo prozkoumané je ještě západo-východní API pro komunikaci s kontrolery z různých domén (určitým příkladem může být federované řešení HP SDN s VMware NSX). OpenFlow, „assembler“ programovatelných sítí, umožňuje oddělit control plane (operační systém) od data plane (to, co šoupe pakety zleva doprava). Je velmi mocný – tak například kontroler může vstříknout do sítě libovolný paket do libovolného místa (switche, portu) a stejně tak si ukrást, přesměrovat přímo v síti nechat jako v továrně zreplikovat pakety. Z prvních pokusů na Stanfordské univerzitě za přispění HP Labs (2007, projekt Ethan) se postupně stala standardizovaná specifikace OpenFlow protokolu dnes ve verzi 1.3. Jazyk OpenFlow umožňuje oddělit control a data plane a není tak pokračováním protokolů jako je SNMP. Nicméně i v oblasti konfiguračních rozhranní (next-gen SNMP) je k dispozici řada modernějších a efektivnějších přístupů – NETCONF, OF-CONTROL, OVSDB, SOAP či REST. Tyto mohou být vhodným doplňkem OpenFlow (například pro konfiguraci tunelů či provisioning napojení na kontroler apod.) a nejsou předmětem tohoto dokumentu.
OpenFlow zprávy Představme některé OpenFlow zprávy důležité z pohledu vývoje aplikací.
Packet IN a Packet OUT Toto jsou dvě velmi důležité zprávy zejména z pohledu reaktivního fungování. Prvek (DPID) může podle instrukcí (explicitně nebo v případě nenalezení lepší možnosti) poslat uživatelův paket do kontroleru zabalený v Packet IN zprávě. Ve skutečnosti je možné specifikovat, zda se pošle paket kompletní nebo jen jeho úvodní část a celý paket je uložen v bufferu zařízení a označený identifikátorem. Opačný proces je Packet OUT. Kontroler může vygenerovat libovolný paket a odeslat jej vybraným OpenFlow prvkem konkrétním portem ven. Případně samozřejmě odkázat na uložený paket (dle buffer ID) či specifikovat logické porty (a tom viz dále - například flood nebo normal).
Flow-mod Zásadní pro SDN je vlastní schopnost programovat datovou cestu v síti. Zpráva Flow-mod vždy obsahuje "match", tedy třídu dat (fyzický port, L2-L4 informace, MPLS label apod.), pro které další části zprávy platí. Verze OpenFlow 1.0 byla v tomto směru poněkud omezená, ale později se přidala podpora MPLS, IPv6, PBB a jiných tunelů nebo rozšiřitelný formát pro libovolná políčka. Kromě match obsahuje samozřejmě akce a instrukce (o tom později), ale také cookie (volitelný identifikátor pro snadnější nalezení záznamu), idle time out (pokud nedojde k match po tuto dobu záznam se vymaže), hard time out (po vypršení se záznam vymaže), prioritu (v tomto pořadí se flow mod vyhodnocují). Co se týče OpenFlow 1.3 a instrukcí a setu akcí říká se ve zprávě, jestli se má pravidlo přidat, odebrat nebo pozměnit.
Statistiky Zprávy se mohou týkat vyžádání statistik - například pro jednotlivé flow mod (chcete-li flow, toky), tabulky, fronty nebo celá zařízení. Podle implementace v zařízení mohou být k dispozici informace jako počet paketů či přenesený objem (bytes).
Další asynchronní události OpenFlow protokol obsahuje řadu asynchronních zpráv. Například při instalaci Flow mod lze prvek požádat o informování kontroleru v případě, že záznam bude odstraněn (například vyprší timeout). Další možnosti se týkají zařízení jako takových (objevení jejich schopností apod.). Je také možné 5|HP SDN kontroler
poslat zprávu Barrier request, tedy chceme od prvku dokončení zpracování všech dosavadních úkolů a informaci, až to bude. Například při reaktivním flow nastavíme potřebná pravidla (Flow Mod) a s odesláním paketu (Packet OUT) počkáme, až budou plně nastavena. Do této skupiny patří i další operace (například definice skupiny).
OpenFlow akce a instrukce OpenFlow 1.0 má jednoduše akce a sety akcí, verze 1.3 pracuje s instrukcemi a akce jsou jejich součástí. Pro podrobnosti prosím prostudujte přímo OpenFlow specifikace - jsou stručné a dobře pochopitelné.
Instrukce V OpenFlow 1.3 jsou základem instrukce, které mohou obsahovat akční sety a akce. Nejčastější instrukce jsou zapsat akce, modifikovat akce a vymazat akce. Další zahrnují práci s metadaty, tato přidávat, číst a modifikovat a využívat je zejména k distribuci přídavných informací mezi jednotlivými tabulkami (tedy v rámci zpracování paketu). To celkem dobře odpovídá implementacím v prvku. Například v chassis vstupní karta provede některé operace (ACL apod.) a v metadatech provádí signalizaci (třeba pro VoQ apod.). Ta cestuje s paketem přes backplane a je využita na výstupní kartě. Jde tedy o informace o paketu, které jsou jiné, než vlastní obsah hlaviček viditelných v externím světě. Další instrukce umožňuje přiřazení měřáku pro práci s pásmem. Poslední důležitá akce je Goto-Table, tedy skok do jiné OpenFlow tabulky.
Akce Output a typy portů Nejpoužívanější akcí je určitě příkaz k odeslání paketů (odpovídajících match ve Flow mod) do odchozího portu (nebo portů). Porty lze v zásadě dělit na tři skupiny. Tou první jsou porty "fyzické" uvozovky používám z toho důvodu, že prvek samotný lze virtualizovat (mít víc OpenFlow instancí), nicméně tento typ portu je s nějakým fyzickým skutečně svázán. Druhý typ je logický - tedy není tam přímé mapování na konkrétní fyzický port. Typickým příkladem (podporovaným v HP HW) je linková agregace (LACP). Pokud na prvku vytvoříte (klasickým postupem) agregaci (trk, BAGG, ...) a přiřadíte k OpenFlow instaci, tak se pro kontroler tento konstrukt jeví jako jeden port (tedy kontroler neví a ani nepotřebuje vědět z jakých fyzických portů je složen). Třetí případ jsou rezervovaná čísla portů ta slouží pro speciální akce, například odeslání do kontroleru. Pár podrobností k akcím: Fyzický port - výstup na konkrétní port Logické porty - typicky linková agregace (podporováno v HP HW), v budoucnu na některém hardware a běžně v softwarových prvcích) třeba klasicky definovaný tunel (GRE, VXLAN, NVGRE, L2TP, ...) Rezervované porty (některé): o ALL - reprezentuje všechny porty o FLOOD - všechny porty v broadcast stromu o CONTROLLER - poslání do kontroleru; lze specifikovat, zda bufferovat (případně kolik bytů) či nikoli o NORMAL - velmi důležitá akce, která říká, že pro daný match se má rámec zpracovat lokálním control plane zařízení (tedy prvek použije svou klasicky získanou přepínací a směrovací tabulku apod.) o TABLE - poslání do jiné tabulky pro další zpracování o IN_PORT - stejný port, ze kterého paket přišel o LOCAL - poslat přímo do lokálního CPU (používá se pro inband management uvnitř OpenFlow VLAN) Pokud akce není uvedena je výsledkem zahození paketu (DROP) - to tedy nemá žádnou explicitní akci.
6|HP SDN kontroler
Akce Set-field, Push-Tag/Pop-Tag, Change TTL Součástí sady akcí mohou být modifikace paketů. Zejména v této oblasti je dobré seznámit se detailně s vlastnostmi hardware, který použijete. OpenFlow protokol je univerzálním jazykem, který umožňuje měnit cokoli. Switche dokáží jen podmnožinu (VLAN políčka, změny MAC adres, DSCP, ...), routery víc (změny L3/L4), softwarové implementace mohou teoreticky cokoli. Modifikace se tedy zejména v případě OpenFlow 1.3 může týkat jakéhokoli políčka v L2/L3/L4 hlavičce, ale díky rozšiřitelnosti i jiného, dosud nedefinovaného. Další operací je POP/PUSH, tedy přidání či naopak odstřihnutí tagu (VLAN tag, MPLS label apod.). Existuje i možnost zabalení do tunelu (PBB, VXLAN, ...).
Akce Set-queue Akce, která paketům přiřazuje ID fronty. Vytváření nebo správa front (typu jak se vyprazdňují, například WRR, DWRR, WFQ, SP, SP+WRR nebo jak se řeší přeplnění typu tail-drop nebo WRED) je mimo doménu OpenFlow (je to per-port v prvku, postaru CLI nebo SNMP, moderněji třeba NETCONF, REST, …). To je rozdíl oproti měřákům (meter table), které jsou vytvářeny a spravovány přímo OpenFlow doménou (a ve skutečnosti je v hardware implementuje jiný obvod).
Akce Group Přiřazení skupiny (viz dále).
Tabulky OpenFlow tabulky vám mohou na první poslech připadat jako zbytečná komplikace. A také v původním standardu 1.0 nebyly, byly zavedeny od verze 1.1. Nicméně jde o extrémně důležitou věc. Postranním efektem jsou věci jako jednodušší udržování složitých pravidel (třebas oddělení forwarding a QoS pravidel), ale to klíčové je vytěžit více z hardwaru. Na rozdíl od software (například VSR virtuální router nebo MSR platforma) je v hardwarovém zařízení práce s pakety v obvodech typu ASIC, TCAM, BCAM, FIB apod. V případě OpenFlow 1.0 byla jen jedna tabulka a tak výrobci použili tu nejmocnější, ale zároveň tu velmi drahou a významně omezenou co do kapacity - TCAM. Ten se tradičně používá pro ACL, QoS a tak podobně. Ostatní levnější obvody s vyšší kapacitou jsou omezené co do funkcí. Tak například forwarding tabulka zná jen cílové MAC adresy a VLAN. FIB tabulka pracuje s cílovou IP adresou. Na druhou stranu tyto obvody mohou být pro SDN aplikace důležité, protože mají podstatně větší kapacitu pravidel v porovnání s TCAM (srovnejte počet ACL zdrojů vs. MAC tabulka vs. FIB u jakéhokoli prvku). Vícero tabulek tak umožňuje využít všech zdrojů v HW. Z jedné tabulky lze skočit do další. To už záleží na konkrétní situaci daného hardware ... a o tom později.
Skupiny (Group table) OpenFlow Group je chytrý koncept, ale zatím není hardware, který by podporoval všechny jeho varianty přímo v železe. Nicméně některé možnosti už jsou a navíc ne vždy se musíme v SDN světě omezovat železem (typicky v případě virtuálního switche nebo routeru). Akce, o kterých jsme mluvili, mohou jako svůj výstup specifikovat místo portu (ať už fyzického, logického nebo rezervovaného) skupinu, tedy groupu. Existují čtyři typy skupin s odlišnými vlastnostmi: ALL - paket se replikuje na všechny porty, které jsou součástí skupiny; typické použití bude u aplikací typu multicast kdy místo nutnosti specifikovat v každém match všechny porty dle nějakého klíče (distribučního stromu) lze odkazovat na skupinu - změny ve skupině neovlivňují implementaci vlastních Flow mod pravidel SELECT - paket se pošle na jeden z portů ve skupině a rozhodnutí, který to bude, je ponechán na koncovém zařízení; zpravidla se použije HASH podobně jako u linkové agregace - ostatně to je také jeden ze scénářů jak tento typ využít - místo použití logického portu pro agregaci (kdy jeho vytvoření a nastavení typu LACP leží mimo OpenFlow doménu)
7|HP SDN kontroler
INDIRECT - je v zásadě proměnná či chcete-li makro; ve Flow mod pravidlech se lze odkazovat "nepřímo" přes tuto skupinu, a pokud se změní rozhodnutí o odchozím portu, tak jej stačí modifikovat ve skupině místo přepsání mnoha Flow mod - dalo by se použít při routingu (next hop) FAST FAILOVER - paket je poslán prvním živým portem ve skupině; v případě jeho selhání se pakety odesílají jiným portem ve skupině, aniž by to znamenalo nutnost přeprogramovat existující Flow mod; v zásadě se dá říci, že jde o obdobu FRR v klasickém světě
Měřáky a čítače Čítače (Counters) OpenFlow specifikuje způsob sbírání statistik a většinu z nich označuje za volitelnou – je tedy otázkou konkrétní implementace v zařízení co všechno dokáže odečítat. Zajímavé jsou určitě perflow statistiky, tedy pro jednotlivý Flow-mod (match) informace o čase, počtu paketů a datech (bytes). Některé tyto hodnoty mohou podléhat nutné podpoře v hardware, ale nemají důsledky na využití zdrojů (například počet paketů na ProVision čipech v2). Přesné měření dat (bytes) často vyžaduje extra zdroje z TCAM (např. u 5900 nebo 5500 EI/HI). Jindy třeba zase data nejsou měřena (podporována), ale hodnoty jsou odhadovány samplingem podobným sFlow procesu (ProVision). Zkrátka čítače jsou hardwarově specifické. Další typy čítačů jsou per-table, per-port, per-queue, per-meter a per-group – opět dle konkrétního železa a jeho specifikace. Takřka vždy jsou dostupné minimálně per-table a per-port.
Měřáky (Meter table) Jedna z klasických implementací hraní si s provozem (QoS policing) je trojbarevné schéma s jedním nebo dvěma rate. Obdobou tohoto v SDN světě jsou měřáky, meters. Jejich počet a schopnosti jsou závislé na implementaci, OpenFlow coby jazyk má hodně možností. V zásadě se vytvoří měřák s jedním nebo více pásmy (např. 100 Kbps a 500 Kbps) a pro každé pásmo se definuje typ (DSCP remark nebo drop). Tímto způsobem lze například říci, že pro daný Flow mod nebo i jejich větší počet mířící do meter ID (čili třídu provozu aka L2/L3/L4 informace) bude tato řešena s vysokou prioritou, pokud je do 100 Kbps (v praxi je to tak, že pravidlo v pásmu není žádné, takže se použije to ve Flow mod). Pro provoz vyšší jak 100 Kbps, ale menší, nežli 500 Kbps bude řešena s normální prioritou (DSCP remark typ pásma). Provoz přes 500 Kbps bude dropován (drop typ pásma, tedy rate limit). V rámci jedné OpenFlow tabulky bude jen jeden jeden match na instrukce, tedy jeden typ paketu bude zpracován jen jednou instrukční sadou, tedy má maximálně jedno meter ID (kde ale může být vícero pásem). OpenFlow protokol ale dovoluje ve zpracování pokračovat v další OpenFlow tabulce. Tímto způsobem je možné dosahovat hierarchických efektů aka HQoS – nicméně to podléhá implementačním možnostem prvku a HQoS je z pohledu hardwaru nesmírně komplikovaná, takže na běžných prvcích typu switch ji obvykle nenajdete.
Implementace v hardware Pokud bereme programování pro API kontroleru jako analogii k programovacím jazykům vyšší kategorie a vlastní OpenFlow protokol jako assembler, tak je z toho patrné, že OpenFlow sahá přímo na hardware a obnažuje jeho vlastnosti a rozdíly. Každý čip se tak bude chovat jinak z pohledu vlastností, možností a kapacit a to zejména mezi výrobci čipů. HP má ve svém portfoliu ASIC vlastní výroby (ProVision) a ten díky své programovatelnosti přináší řadu unikátních výhod i přes to, že se poprvé na trhu objevil před několika lety (v2) nebo dokonce před šesti (v1). Na druhou stranu prvky postavené na merchant silicon jako je switch 5900 mají relativně nové a velmi výkonné ASIC – jejich set funkcí je jednoduše jiný. Nedá se říci, že by jedna implementace byla ve všem lepší, nežli druhá – jsou to zkrátka jiné technologie designu nízko-úrovňových obvodů.
8|HP SDN kontroler
Vlastnosti ProVision Všechny prvky ProVision podporují OpenFlow 1.3.1, konkrétně 3500, 3800, 5400, 6200, 6600 a 8200 a stejně tak odlehčená verze čipu v 2920. Starší verze (v1 moduly a 3500/6200) mají méně možností (chybí některé match situace, přístupný je pouze TCAM, nejsou měřáky). Velmi krátce a stručně k hlavním možnostem v2 modulů, 3800 a 2920: Tři hardwarové tabulky: o IP (4/6) whitelist (zdrojová i cílová IP adresa !), typicky pro security, velká kapacita (36k u v2 a 3800, 16k u 2920), akce pouze goto-table o IP blacklist (dtto) o Policy table (TCAM), plné možnosti match (12-tuple), akce (modifikace MAC, VLAN, 802.1p, DSCP, …), cca 4k pravidel na čip, u 2920 jistá omezení (chybí match dstMAC) Podpora (velmi) pomalé SW tabulky (včetně třeba rewrite IP nebo TCP) Měřáky v hardware V budoucnu podpora tunelů jako logických portů (verze OS 15.15) HW replikace paketů (v action setu)
Vlastnosti Comware platforem Možnosti prvků s operačním systémem Comware záleží samozřejmě na typu použitého čipu. Následující text je zaměřen na prvky s existující podporou: 5900, 5500 EI a 5500 HI. Podobné vlastnosti lze čekávat v budoucnu (pokud bude uvedeno) u prvků 5800 nebo 7500 a podobné byť vyšší co do možností a kapacity u platforem 10500, 11900, 12900. Trochu jiné vlastnosti mohou být u 12500 a dramaticky jiné u routerů (díky méně omezením na straně hardware). Uvedené informace jsou spekulativní odhady, nikoli roadmapa HP. V následující části se proto věnujeme produktům s již existující podporou, typicky HP 5900. Dvě hardwarové tabulky: o MAC/IP tabulka podporující VLAN a cílovou MAC (pak cca stovky tisíc) nebo k tomu ještě cílovou IP (pak kapacita 16k u 5900) s podporou OUTPUT akcí a next-table o Extensibility tabulka (TCAM) s podporou 13-tuple match, akce (modifikace MAC, VLAN, 802.1p, DSCP, …), cca 1k pravidel u 5900, 3k na čip u 5500 Nepodporuje SW tabulku Měřáky v hardware Countery včetně bytů v hardware (spotřeba TCAM zdrojů pokud je použito) Některé groupy v hardware (grouptype ALL) HW replikace paketů (group all)
Služby kontroleru Externí (REST API) vs. nativní (Java API) programování Kontroler nabízí dva způsoby programování aplikací. Externí forma využívá REST API rozhraní, tedy jednoduchých webových volání, které lze realizovat z externí aplikaci. Použití REST umožňuje programovat tyto aplikace v libovolném jazyce v libovolném prostředí nebo OS. Ideální je pro úkoly, které nevyžadují reakci sítě v reálném čase ani asynchronní události. Příklady zahrnují proaktivní programování flow (pravidel pro provoz), získávání informací (flow statistiky, koncové stanice, linky, topologie), vkládání služeb, statické rozdělování zátěže či řízení kvality služby na základě aplikačních informací mimo SDN rámec (podobně jak to dělá HP Network Optimizer). Druhý typ programování je nativní a přináší možnost vytvoření modulu v rámci OSGi architektury, který je možné za běhu nahrát do kontroleru. Vyžaduje programování v Java nebo jiném binárně kompatibilním jazyce. Zahrnuje všechny možnosti zmíněné výše (přes Java API), ale navíc celou řadu dalších. Například schopnost rozšířit GUI kontroleru o nové ovládací prvky, rozšířit REST API o další 9|HP SDN kontroler
aplikačně specifická volání a reakce v reálném čase na události sítě včetně asynchronních situací. Aplikace tak může reagovat na Packet_In, generovat FlowMod a Packet_Out, dostávat oznámení o novém zařízení, novém flow a tak podobně. Příklad aplikace využívající tento způsob programování je HP Network Protector. Oba výše uvedené přístupy lze kombinovat – je tak možné napsat část aplikace jako OSGi modul běžící přímo v kontroleru, který se vypořádá s real-time požadavky aplikace a rozšíří REST vrstvu kontroleru o nová volání. Ta lze pak využívat v druhé části aplikace (napsané nad REST vrstvou), která je pak platformně zcela nezávislá.
Core služby Kontroler sám o sobě přináší OSGi rámec pro nativní Java aplikace a jejich interakci se sítí (packet sequencer pro přihlášení k odběru Packet_In zpráv, datapathlistener pro příjem zpráv z OpenFlow prvků, interakce a pořadí aplikací a tak podobně). Dále zahrnuje OpenFlow knihovnu, která abstrahuje rozdíly mezi odlišnými formáty různých OpenFlow verzí (konkrétně 1.0 a 1.3), řeší komunikaci s prvky, sběr flow statistik apod. Uvedené je nad rámec tohoto dokumentu, který se soustředí na externí programování. Kontroler z vnějšího pohledu přináší zejména možnost volat funkce pro programování zařízení OpenFlow protokolem – například vložení, smazání či úpravu pravidla (FlowMod), odečet statistik, vytvoření meteringu a frontování, vytvoření group pravidel a tak podobně.
Zabudované služby HP VAN SDN kontroler přichází s již vytvořenými aplikacemi, které zajišťují základní síťové funkce. Tyto jsou vytvořeny jako OSGi moduly, tedy stejným způsobem, jak lze přidávat další nativní Java moduly. To umožňuje například vypnout některou z aplikací a díky publikovaným Java API ji v rámci OSGi nahradit vlastní implementací. Tyto služby jsou:
Link Manager První aplikace je zodpovědná za objevování linek mezi OpenFlow prvky v síti. Aplikace v pravidelných intervalech generuje speciální pakety a vstřikuje je do prvků v síti (skrze OpenFlow Packet_Out rámce) a vkládá do nich identifikátor prvku (DPID). Pokud přijde do OpenFlow prvku, kontroler jej
10 | H P S D N k o n t r o l e r
instruuje k přeposlání paketu do kontroleru (Packet_In). Tímto způsobem dojde k odhalení linky mezi dvěma prvky. Link Manager vytváří databázi linek mezi prvky.
Topology Manager Následující modul jako vstup bere informace z Link Manager a také asynchronní hlášení z prvků (typicky link down události). Jeho úkolem je poskládat mapu (graf) sítě a nabízí služby jako je počítání nejkratší trasy mezi dvěma uzly.
Node Manager Tento modul má na starost to, co je v běžných prvcích reprezentováno MAC forwardovací tabulkou a ARP tabulkou. Poslouchá ARP, DHCP (jdou vždy přes kontroler u čistého pojetí nebo přijde minimálně jejich kopie u hybridního přístupu) a IP pakety (které se dostanou do kontroleru, typicky reaktivní čistě OF přístup) a na základě nich udržuje informace o návaznosti switch/port/MAC/IP, tedy kde je která koncová stanice připojena a jakou má MAC a IP.
Path Daemon Za tvorbu reaktivních pravidel je odpovědný Path Daemon. Pokud se prvek implicitně (table miss) nebo explicitně (pravidlo) rozhodne požádat kontroler o pomoc a zabalit paket (nebo jeho začátek + buffer ID dle nastavení pravidla a možností prvku) Path Daemon vytvoří spojení. Podívá se do Packet_In události, z node manageru zjistí, kde se nachází cílová stanice, z topology manageru získá nejkratší cestu a naprogramuje jednotlivé prvky na cestě pro toto nové flow (session, komunikační tok). Ve výchozí konfiguraci používá L2 informace do match políček, ale je možné nastavit i IP. Tento engine je typicky využívám u OpenFlow v reaktivním režimu, při hybridním nasazení (třeba v Campus) je vlastní forwarding realizován klasicky nebo proaktivně.
Path Diagnostics Kontroler je vybaven nástrojem pro diagnostiku sítě – trochu s podobným výsledkem jako třeba IP SLA či HP NQA. Do libovolného místa v síti (switch, port) lze vstřikovat uměle vytvořené pakety s identifikátorem, libovolnou zdrojovou a cílovou adresou, IP, TCP, UDP apod. Jak testovací paket prochází sítí lze definovat observer point (klidně každý prvek v cestě), který následně informuje kontroler o úspěšném průchodu paketu. Skrz SDN kontroler tak lze získat informace podobné využití „L2 traceroute“ a IP SLA (ale ne jen mezi dvěma body, ale i průběžně po cestě).
Topology Viewer Modul odpovědný za grafickou reprezentaci topologie v kontroleru.
Služby v hybridním režimu (kontroler 2.1.0 a vyšší) Kontroler ve verzi 2.0.0 je zaměřen na sítě postavené kompletně na OpenFlow, spíše, než pro hybridní switche. Kontroler tak předpokládá, že není-li řečeno jinak, o provozu se dozví (tedy pravidlo poslední záchrany na konci poslední tabulky v prvku je „forward CONTROLLER“). V hybridním prostředí je ale tímto posledním příkazem „forward NORMAL“, tedy řešit komunikaci klasickými prostředky bez jakékoli účasti SDN. Čisté OpenFlow má svůj velký smysl v situacích jako je výzkum a vývoj, overlay technologie a virtualizované prostředí (OpenFlow na openvswitch v KVM například) a tak podobně. V mnoha dalších aplikacích budou zákazníci preferovat co nejmenší zásahy do klasického chování sítě tak, jak to řeší třeba HP Network Protector nebo HP Network Optimizer. Kontroler 2.0.0 plně podporuje HW implementace nejen HP a to včetně hybridních režimů, ale některé služby, které kontroler nabízí, mohou být v tomto režimu dotčeny (například objevování koncových stanic). To v některých případech není vůbec problém (např. u Network Protector), ale pro vývojáře by bylo příjemné, pokud by základní služby kontroleru byly plně využitelné i v situaci hybrid.
11 | H P S D N k o n t r o l e r
Verze 2.3.0 přišla s režimem „hybrid“ (resp. bylo to už u 2.1.0, ale později přišla vylepšení), který je výchozím stavu zapnutý (ale lze jej vypnout). Využívá různá dodatečná pravidla, kterými zajišťuje, že síť může využívat klasických postupů (forward NORMAL) a přesto je SDN kontroler dostatečně informován. Ve verzi 2.3.0 jsou plně podporovány prvky ProVision, ve verze 2.4.0 se plánuje plná podpora Comware prvků s OpenFlow.
RESTful API HP SDN kontroleru Příprava prostředí Pokud nemáte k dispozici připravené VM pro vývoj a VM pro kontroler jednoduše si vytvořte vlastní, není to těžké. V příloze najdete doporučený postup jak na to. Máte-li přístup k hotovým VM tak je překopírujte do svého VMware Workstation prostředí. Výchozí stav jsou 2 virtuální CPU a 2 GB paměti v každé VM. To přizpůsobte skutečnosti vašeho počítače (snižte paměť na 1,5 GB a dejte 1 CPU, pokud nemůžete lépe nebo naopak přidělte víc). Síťové adaptéry jsou nastaveny takto: První adaptér je host only a vede na eth0 (kontroler má 10.10.10.180, vývojář 10.10.10.170) Druhý adaptér je typu NAT pro přístup na internet. V Python labech nebudeme potřebovat. Při otevření VM se vás může VMware dotázat zda chcete Move nebo Copy – doporučuji dát Move. Nabootujte obě VM a ujistěte se, že na sebe vzájemně vidí – tedy např. že z vývojáře funguje ping na 10.10.10.180.
RESTful a JSON Nejdřív snad co je RESTful – jde o bezestavové velmi jednoduché API postavené na webovém volání. Jednodušší už to být nemůže. V zásadě pro přečtení nějaké informace se stačí zeptat jako HTTP GET příslušné URL na serveru a zpátky přijde odpověď – čitelná ve webovém prohlížeči, jakémkoli HTTP klientu a v jakémkoli programovacím jazyce. Stejně jako u prohlížeče je možné současně poslat i sadu argumentů (například chci vidět jen stanice ve VLANě X). Pokud chceme něco měnit, řeší se to metodou HTTP POST (podobně jako při odeslální formuláře na webu), případně PUT a DELETE. Nepotřebujeme žádný stavový stroj ani na jedné straně, nic nemusí zůstávat v paměti apod. Komunikace může být, stejně jako web, zabezpečena SSL kanálem. K autentifikaci se dostaneme později. Pro výměnu údajů se nejčastěji používá formát JSON nebo XML – pro HP SDN kontroler byl zvolen formát JSON (dnes stále populárnější a efektivnější). Jde o jednoduchý textový řetězec, jímž lze zakódovat i velmi složité datové struktury. Dvojtečka označuje vztah klíč:hodnota (například “barva“: “modrá“). Složená závorka reprezentuje objekt včetně vnořování a hranatá závorka značí pole. Pár příkladů: {"clovek": { "jmeno":"Tomas", "prijmeni":"Kubica” } }
Přidejme vnořené objekty a vnořené pole 12 | H P S D N k o n t r o l e r
{"clovek": { "jmeno":"Tomas", "prijmeni":"Kubica", "hlava":{ "oci":"zelene", "vlasy":"blond" }, "znalosti":["STP", "LACP", "OSPF", "BGP", "MPLS"] } }
A pak třeba ještě takhle {"firma":[ {"clovek": { "jmeno":"Tomas", "prijmeni":"Kubica", "hlava":{ "oci":"zelene", "vlasy":"blond" }, "znalosti":["STP", "LACP", "OSPF", "BGP", "MPLS"] } }, {"clovek": { "jmeno":"Nekdo", "prijmeni":"Dalsi", } } ] }
Potřebujete víc? Doporučuji přečíst RFC 7159
Živá dokumentace API SDN kontroleru Otevřete živou dokumentaci na IP adrese kontroleru /api (např. https://10.10.10.180:8443/api)
13 | H P S D N k o n t r o l e r
V horní záložce lze vybrat konkrétní API implementaci – v našem případě jsou tam jen API kontroleru, ale i ostatní aplikace (ať už vaše nebo například HP Network Protector) tam mohou mít svá rozhraní. Prvním úkolem bude autentizovat se. Přístup k samotnému API lze provozovat zabezpečeným SSL, ale další věc je autentizace uživatele. HP SDN kontroler používá architekturu Keystone z projektu OpenStack, konkrétně implementaci s tokeny. Aby se nemuselo jméno a heslo ověřovat při každém volání (což by zatěžovalo obě strany a navíc bylo nepohodlné) dojde k ověření pouze jednou. Při úspěchu je vygenerován token – kód platný 24 hodin, kterým se následná volání prokazují (speciální položkou v hlavičce). Najděte POST API volání /auth a vložte tento JSON řetězec: {"login":{"user":"sdn","password":"skyline"}}
14 | H P S D N k o n t r o l e r
Výsledkem bude vrácený token řetězec – zkopírujte ho a zadejte do políčka nahoře a klikněte na Explore. Od této doby jste přihlášeni a můžete si živou dokumentaci prohlížet.
Připojte ke kontroleru síť – fyzickou nebo virtuální. Následující screenshoty jsou sebrány z této Mininet topologie: sudo mn --controller=remote,ip=10.10.10.180 --topo=tree,depth=3
Vyzkoušejte si některá GET volání. Například seznam prvků:
15 | H P S D N k o n t r o l e r
Seznam portů
16 | H P S D N k o n t r o l e r
Vygenerujte traffic (pingall v Mininet, ujistěte se, že kontroler má vypnutý hybrid režim pokud máte verzi vyšší, než 2.0.0) a zobrazte si seznam aktuálních pravidel
17 | H P S D N k o n t r o l e r
Nebo seznam připojených koncových stanic s jejich umístěním
18 | H P S D N k o n t r o l e r
Nechte si spočítat nejkratší cestu mezi dvěma prvky
19 | H P S D N k o n t r o l e r
Nebo si projděte seznam linek mezi prvky
20 | H P S D N k o n t r o l e r
Experimentujte s dalšími částmi API
Offline REST API dokumentace Živá dokumentace je ideální pro zkoušení GET příkazů i posílání POST zpráv. Pro konkrétní popis jednotlivých voleb v rámci POST příkazů je ideální nahlédnout do offline REST API dokumentace: http://h20565.www2.hp.com/portal/site/hpsc/template.BINARYPORTLET/public/kb/docDisplay/res ource.process/?spf_p.tpst=kbDocDisplay_ws_BI&spf_p.rid_kbDocDisplay=docDisplayResURL&javax. portlet.begCacheTok=com.vignette.cachetoken&spf_p.rst_kbDocDisplay=wsrpresourceState%3DdocId%253Demr_na-c040039721%257CdocLocale%253Dcs_CZ&javax.portlet.endCacheTok=com.vignette.cachetoken
Instalace pravidla V Mininet topologii zapněte na jedné z koncových stanic (h5) webový server tímto příkazem mininet> h5 python -m SimpleHTTPServer 80 &
Vyzkoušejte, že jste schopni stáhnout webovou stránku a také, že ping prochází. mininet> h1 wget h5 --2014-02-21 18:24:15--
http://10.0.0.5/
21 | H P S D N k o n t r o l e r
Connecting to 10.0.0.5:80... connected. HTTP request sent, awaiting response... 200 OK Length: 748 [text/html] Saving to: ‘index.html.2’ 0K
100% 68.9M=0s
2014-02-21 18:24:15 (68.9 MB/s) - ‘index.html.2’ saved [748/748] mininet> mininet> h1 ping h5 PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data. 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.026 ms 64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.080 ms ^C --- 10.0.0.1 ping statistics --2 packets transmitted, 2 received, 0% packet loss, time 1000ms rtt min/avg/max/mdev = 0.026/0.053/0.080/0.027 ms mininet>
Pojďme na prvku, kde je stanice 10.0.0.1 připojena zadat pravidlo zakazující provoz na portu TCP 80 směřující na adresu h5 (tedy 10.0.0.5). Bude to něco jako globální ACL pravidlo. Dělá se to tak, že se pošle FlowMod (pravidlo programování data path). Nevyplníme u něj action (poslat někam, přepsat nějakou hlavičku, omezit rychlost, zafrontovat, …), což znamená, že výsledkem bude drop. Co bude FlowMod obsahovat?
Match na tento typ paketu, tedy EtherType IP, dstIP, Protocol TCP a dstTCP 80 Hard timeout – zakážeme provoz jen na 30 vteřin, pak se pravidlo samo smaže Priorita – zvolíme vyšší, než jsou výchozí pravidla programovaná zabudovanými aplikacemi kontroleru (29999), tedy například 30000 Volitelně další atributy (cookie, abychom naše pravidlo snadno poznali, idle timeout
Výsledný JSON vypadá takhle: {"flow": { "priority": 30000, "hard_timeout": 30, "match": [ { "tcp_dst": "80" }, { "ip_proto": "tcp" }, { "ipv4_dst": "10.0.0.5" }, { "eth_type": "ipv4" } ]}}
Pošleme ho do DPID 00:00:00:00:00:00:00:01 22 | H P S D N k o n t r o l e r
Vyzkoušejte komunikaci v Mininet – jak webovou, tak ping mininet> h1 wget h5 --2014-02-21 18:48:09-- http://10.0.0.5/ Connecting to 10.0.0.5:80... ^Cmininet> mininet> h1 ping h5 PING 10.0.0.5 (10.0.0.5) 56(84) bytes of data. 64 bytes from 10.0.0.5: icmp_seq=1 ttl=64 time=47.5 ms 64 bytes from 10.0.0.5: icmp_seq=2 ttl=64 time=0.131 ms
Jak je vidět ping prochází, ale webový provoz ne – naše pravidlo tedy funguje. Ještě, než vyprší, můžete se na něj podívat v GUI kontroleru.
23 | H P S D N k o n t r o l e r
24 | H P S D N k o n t r o l e r
Python První kroky v Python živě Python je velmi jednoduchý programovací a skriptovací jazyk. Na rozdíl od jazyků typu Java není potřeba proměnné dopředu deklarovat, není nutné striktně dodržovat typy a vlastně je ani netřeba předem definovat. Pro co nejjednodušší práci s voláním kontroleru doporučuji využívat open source knihovnu hp-sdnclient. Dokumentace je k dispozici zde: http://hp-sdn-client.readthedocs.org/ Instalace knihovny je jednoduchá (na vaší VM už je nainstalováno), pokud použijete pip nebo easy install: pip install hp-sdn-client
nebo easy_install hp-sdn-client
Začněme v živém interpretačním prostředí – jednoduše napište příkaz python a dostanete se do něj. sdn@ubuntu:~$ python Python 2.7.5+ (default, Sep 19 2013, 13:48:49) [GCC 4.8.1] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>>
Nejdřív si naimportujeme HP SDN client knihovny, aby práce s kontrolerem byla co nejjednodušší. Současně vytvoříme autentizační objekt a natvrdo (zatím) zadáme přihlašovací údaje pro kontroler. Následně vytvoříme objekt api, přes který se pak dostaneme ke všemu, co budeme potřebovat. import hpsdnclient as hp auth = hp.XAuthToken(user='sdn',password='skyline',server='10.10.10.180') api = hp.Api(controller='10.10.10.180',auth=auth)
Ujistěte se, že máte spuštěnou virtuální síť v Mininet nebo připojeny fyzické prvky. Následující příklady jsou z této topologie: sudo mn --controller=remote,ip=10.10.10.180 --topo=tree,depth=3
Pojďme si vyžádat seznam zařízení, která kontroler vidí (v OpenFlow terminologii data paths). Všimněte si, že není potřeba proměnnou předem deklarovat ani určovat její typ – jednoduše přiřaďte api volání get_datapaths. Výsledkem je python dictionary, tedy slovník podobný JSON řetězcům, ale zpracovaný pro jednoduché použití v Python. Začněme tím, že si vytisneme první záznam v pořadí. >>> dpids = api.get_datapaths() >>> print dpids[0]
25 | H P S D N k o n t r o l e r
{ "capabilities": [ "flow_stats", "table_stats", "port_stats", "queue_stats", "arp_match_ip" ], "device_ip": "10.10.10.170", "device_port": 48669, "dpid": "00:00:00:00:00:00:00:01", "last_message": "2014-02-21T10:11:36.324Z", "negotiated_version": "1.0.0", "num_buffers": 256, "num_tables": 254, "ready": "2014-02-21T09:33:11.576Z" } >>>
Pokud chcete jen nějaký konkrétní klíč nebo objekt lze se na něj odkázat za tečkou. >>> print dpids[0].dpid 00:00:00:00:00:00:00:01 >>>
Co kdybychom si teď nechali vytisknout seznam DPID identifikátorů? Procházení celého slovníku je velmi jednoduché. Řekneme, že pro každou proměnnou dpid ze slovníku dpids (tedy pro každý prvek) vytiskneme jeho dpid hodnotu. >>> for dpid in dpids: ... print dpid.dpid ... 00:00:00:00:00:00:00:01 00:00:00:00:00:00:00:02 00:00:00:00:00:00:00:03 00:00:00:00:00:00:00:04 00:00:00:00:00:00:00:05 00:00:00:00:00:00:00:06 00:00:00:00:00:00:00:07 >>>
Zkusme další api – vygenerujte ve své síti nějaký provoz (v Mininet dejte pingall). Pokud máte kontroler vyšší, než 2.0.0 (např. 2.3.0) je možné, že pracujete v hybridním režimu. V tomto se pro normální provoz používá „forward normal“ místo reaktivního programování flow-mod. Pro účely tohoto labu můžete hybridní režim vypnout. Zkuste tohle: >>> flows = api.get_flows('00:00:00:00:00:00:00:01') >>> print flows[0] { "actions": [ { "output": 1
26 | H P S D N k o n t r o l e r
} ], "byte_count": 98, "cookie": "0x2328", "duration_nsec": 99000000, "duration_sec": 2, "idle_timeout": 60, "match": [ { "eth_dst": "2a:11:12:1c:59:24" }, { "eth_src": "0a:96:fe:dc:05:cc" }, { "eth_type": "ipv4" }, { "in_port": 2 } ], "packet_count": 1, "priority": 29999 } >>>
A co dostat seznam portů? >>> ports = api.get_ports('00:00:00:00:00:00:00:01') >>> print ports[0] { "current_features": [ "rate_10gb_fd", "copper" ], "id": 1, "mac": "b2:75:ce:b6:75:71", "name": "s1-eth1", "state": [ "stp_listen" ] } >>>
Zkusme teď něco složitějšího. Objekt plný prvků už máme (dpids), tak bychom ho mohli procházet a při té příležitosti si zjistit seznam jeho portů a tyto informace vytisknout na obrazovku. >>> for dpid in dpids: ... switch=dpid.dpid ... portlist=api.get_ports(switch) ... for port in portlist: ... print switch + ' ' + port.name ... 00:00:00:00:00:00:00:01 s1-eth1 00:00:00:00:00:00:00:01 s1-eth2 00:00:00:00:00:00:00:01 s1
27 | H P S D N k o n t r o l e r
00:00:00:00:00:00:00:02 00:00:00:00:00:00:00:02 00:00:00:00:00:00:00:02 00:00:00:00:00:00:00:02 00:00:00:00:00:00:00:03 00:00:00:00:00:00:00:03 00:00:00:00:00:00:00:03 00:00:00:00:00:00:00:03 00:00:00:00:00:00:00:04 00:00:00:00:00:00:00:04 00:00:00:00:00:00:00:04 00:00:00:00:00:00:00:04 00:00:00:00:00:00:00:05 00:00:00:00:00:00:00:05 00:00:00:00:00:00:00:05 00:00:00:00:00:00:00:05 00:00:00:00:00:00:00:06 00:00:00:00:00:00:00:06 00:00:00:00:00:00:00:06 00:00:00:00:00:00:00:06 00:00:00:00:00:00:00:07 00:00:00:00:00:00:00:07 00:00:00:00:00:00:00:07 00:00:00:00:00:00:00:07 >>>
s2-eth1 s2-eth2 s2-eth3 s2 s3-eth1 s3-eth2 s3-eth3 s3 s4-eth1 s4-eth2 s4-eth3 s4 s5-eth1 s5-eth2 s5-eth3 s5 s6-eth1 s6-eth2 s6-eth3 s6 s7-eth1 s7-eth2 s7-eth3 s7
Jedno z volání kontroleru umožňuje získat seznam koncových stanic, které kontroler zná (drží si něco na způsob ARP/IP/MAC tabulky). Mohlo by nás tedy zajímat, kde je nějaká IP adresa připojená (který switch a který port) a jakou má MAC adresu. Abychom se k tomuto úkolu mohli vracet, vyzkoušíme si definici funkce. Jejím vstupním argumentem bude IP adresa. Následně si z kontroleru sebereme všechny připojené stanice (lze přímo ve volání filtrovat – ale pro vzdělávací účely nebudeme používat). Následně začneme tento seznam procházet, a pokud se IP adresa shoduje s tou, kterou jsme funkci předali, tak nalezený uzel vrátíme. Pak stačí jen vytisknout výstup funkce a máme hotovo.
>>> def whereis(ip): ... nodes = api.get_nodes() ... for node in nodes: ... if node.ip==ip: ... return node ... >>> print whereis('10.0.0.5') { "dpid": "00:00:00:00:00:00:00:06", "ip": "10.0.0.5", "mac": "52:39:68:10:e7:e2", "port": 1 }
28 | H P S D N k o n t r o l e r
První Python skript Pojďme teď funkci, která vyhledává koncovou stanici v síti přepsat do formy samostatně spustitelného skriptu. Vytvořte nový soubor (třeba z gedit – já použil soubor where.py) a pustíme se do toho. Nejprve naimportujeme knihovnu a vytvoříme globální objekty na api do kontroleru – opět necháváme přihlašovací údaje zabudované, to můžeme později změnit. #!/usr/bin/env python import argparse import hpsdnclient as hp auth = hp.XAuthToken(user='sdn',password='skyline',server='10.10.10.180') api = hp.Api(controller='10.10.10.180',auth=auth)
Důvod proč importujeme argparse je, abychom dokázali snadno pracovat se vstupními argumenty a jednoduše vytvářeli nápovědu a tak podobně. Základem každého skriptu je hlavní funkce – ta u nás bude vypadat takto: def main(): parser = argparse.ArgumentParser() parser.add_argument('--ip', type=str, help="The IP Address to find", required=True) args = parser.parse_args() print whereis(args.ip)
Tedy parseru vstupních argumentů říkáme, že očekáváme povinný argument --ip a definujeme řetězec pro nápovědu. Následně jednoduše vytiskneme hodnotu, kterou nám vrátí funkce whereis, které jako argument předáváme ip. Funkci whereis už dobře známe: def whereis(ip): nodes = api.get_nodes() for node in nodes: if node.ip==ip: return node
Na závěr souboru ještě připište následující text – ten v zásadě říká, že tento skript je samostatně spustitelný (trochu zjednodušuji, ale to nevadí). if __name__ == "__main__": main()
No a takhle to pak vypadá… sdn@ubuntu:~/scripts$ python where.py usage: where.py [-h] --ip IP where.py: error: argument --ip is required
29 | H P S D N k o n t r o l e r
sdn@ubuntu:~/scripts$ python where.py -h usage: where.py [-h] --ip IP optional arguments: -h, --help show this help message and exit --ip IP The IP Address to find sdn@ubuntu:~/scripts$ python where.py --ip=10.0.0.5 { "dpid": "00:00:00:00:00:00:00:06", "ip": "10.0.0.5", "mac": "52:39:68:10:e7:e2", "port": 1 } sdn@ubuntu:~/scripts$
Zabiják V druhé „aplikaci“ vyjdeme z toho, co už máme a nabídneme možnost nejen vyhledat koncovou stanici v síti, ale rovnou ji i na určený počet vteřin odříznout z provozu. Nebude to vůbec nic složitého. Zkopírujte soubor where.py do dalšího, například kill.py. Přidáme si teď novou funkci kill. def kill(node, timeout): match = hp.datatypes.Match(in_port=node.port,eth_type="ipv4",ipv4_src=node.ip) flow = hp.datatypes.Flow(priority=30000, match=match, hard_timeout=timeout) api.add_flows(node.dpid, flow)
Připravujeme si FlowMod, tedy OpenFlow řídící informaci o modifikaci datového toku resp. nastavení hardware. V prvním řádku definujeme match, tedy pro jaký typ komunikace ho budeme aplikovat. Z informace o uzlu (známe z whereis funkce) vyplníme vstupní port, EtherType dáme na IPv4 a doplníme zdrojovou IP adresu (tu co chceme zneškodnit). V druhém kroku definujeme pravidlo (flow) – za normálních okolností by zde nechyběla akce (poslat na port, do kontroleru, do meteru pro omezení propustnosti, manipulace s hlavičkou apod.), ale my nezadáme žádnou. To je způsob jak říct, že paket má být zahozen (drop) – samozřejmě přímo v hardware. Určujeme prioritu tohoto pravidla (výchozí hodnota pro reaktivní flow z kontroleru je 29999) – čím vyšší číslo, tím dříve je pravidlo zkoumáno. Dále přiřadíme match a hard_timeout – po vypršení tohoto času bude pravidlo automaticky zařízením vymazáno (0 = pravidlo zůstává trvale). Pro srovnání idle_timeout určuje za jak dlouho se má pravidlo vymazat, pokud není viděn žádný provoz odpovídající match. Pro naše účely (penalizace stanice) je hard_timeout to, co potřebujeme. Pak už stačí jen pravidlo poslat do správného prvku. Přidáme si jeden vstupní argument (penalty) s výchozí hodnotou 0 a zavoláme funkci kill (uvnitř voláme ještě whereis pro nalezení umístění hledané stanice). def main(): parser = argparse.ArgumentParser() parser.add_argument('--ip', type=str, help="The IP Address to find", required=True) parser.add_argument('--penalty', type=int, help="Penalty time in seconds (optional)", required=False, default=0)
30 | H P S D N k o n t r o l e r
args = parser.parse_args() kill(whereis(args.ip),args.penalty) print args.ip + ' has been killed!'
No a to je vše – vaše druhá aplikace je hotová! Stačí vyzkoušet. Například v Mininet rozjeďte dva pingy a jednu ze stanic na třeba 20 vteřin zablokujte vaší aplikací. sdn@ubuntu:~/scripts$ python kill.py --ip=10.0.0.5 --penalty=20 10.0.0.5 has been killed!
Koncové stanice v GUI Python je vybaven jednoduchou knihovnou Tkinter pro grafické rozhraní (pokud vám chybí, nainstalujte si sudo apt-get install python-tk). Existuje i celá řada krásnějších a modernějších a mnoho aplikací dnes sází na webové technologie, zejména HTML5 a JQuery (tak jako HP SDN VAN kontroler). Nicméně pro náš lab chceme něco extrémně jednoduchého na hraní. Mimochodem první kroky můžete absolvovat i v živém Python (import Tkinter a pak hned můžete vykreslovat okno a živě do něj přidávat objekty). Vytvořte nový soubor – třeba stanice.py. Přidejte prvních pár řádků s importy: #!/usr/bin/env python import argparse import hpsdnclient as hp from Tkinter import *
Následují objekty pro práci s kontrolerem – ty už známe a také si stáhneme seznam koncových stanic. auth = hp.XAuthToken(user='sdn',password='skyline',server='10.10.10.180') api = hp.Api(controller='10.10.10.180',auth=auth) nodes = api.get_nodes()
Teď vytvoříme hlavní okno aplikace. Nic složitého: root = Tk()
No a do tohoto okna budeme přidávat primitivní objekty Label (nápis) a uspořádávat je do tabulky (grid). Nejprve si vytvoříme „hlavičku“: Label(root, Label(root, Label(root, Label(root,
text="MAC", borderwidth=2).grid(row=0,column=0) text="IP", borderwidth=2).grid(row=0,column=1) text="DPID", borderwidth=2).grid(row=0,column=2) text="PORT", borderwidth=2).grid(row=0,column=3)
Teď zbývá procházet jednotlivé záznamy v nodes (to už jsme dělali, takže víme jak na to) a tyto vykreslovat. Před tím si vytvoříme proměnou myrow, podle které budeme automaticky zvyšovat číslo řádku. Úplně na závěr skočíme do grafického systému (mainloop):
31 | H P S D N k o n t r o l e r
myrow=1 for node in nodes: Label(root, text=node.mac, borderwidth=2).grid(row=myrow,column=0) Label(root, text=node.ip, borderwidth=2).grid(row=myrow,column=1) Label(root, text=node.dpid, borderwidth=2).grid(row=myrow,column=2) Label(root, text=node.port, borderwidth=2).grid(row=myrow,column=3) myrow += 1 root.mainloop()
Spusťte si Mininet síť a proveďte pingall. Otevřete naší aplikaci: sdn@ubuntu:~/scripts$ python stanice.py
Tak – a máme hotovu první grafickou SDN aplikaci.
Zabiják s GUI Překopírujte kód z naší první grafické aplikace do dalšího souboru, třeba s názvem killguy.py. Nejprve si rozšíříme vykreslování tabulky koncových zařízení o další sloupeček s tlačítkem Kill (přidejte ten tučný text, je to v jednom řádku). Co se tam děje? Říkáme, že do okna vykreslujeme další objekt (tlačítko) a umisťujeme jej do dalšího sloupečku v gridu. V sekci command nemáme jen jednoduché volání funkce z toho důvodu, že jí potřebujeme předat argument – konkrétně node, který chceme zabít. Proto se použije lambda. Pak následuje node=node:, což říká, že chceme s tlačítkem přiřadit aktuální obsah proměnné node (tedy nikoli proměnnou samotnou). No a zbývá zavolat funkci kill a té předat příslušný node. for node in nodes: Label(root, text=node.mac, borderwidth=2).grid(row=myrow,column=0) Label(root, text=node.ip, borderwidth=2).grid(row=myrow,column=1) Label(root, text=node.dpid, borderwidth=2).grid(row=myrow,column=2) Label(root, text=node.port, borderwidth=2).grid(row=myrow,column=3) Button(root, text="Kill", command=lambda node=node: kill(node)).grid(row=myrow,column=4) myrow += 1
Chybí nám ještě funkce kill, kterou už ovšem dobře známe. Jediným rozdílem bude, že ji nebudeme předávat timeout jako argument, ale tato funkce si o něj přímo řekne. Nejprve nahoře v sekci import přidejte tkSimpleDialog: 32 | H P S D N k o n t r o l e r
import tkSimpleDialog
A pak těsně před spuštění mainloop dejte definici funkce kill (viz níže). Rozdíl oproti kill, který jsme používali dříve, je v tom, že na hodnotu timeout se zeptáme dialogem. Používáme askinteger a pro možnost rychlé akce předvyplňujeme hodnotou 30, kterou lze ale samozřejmě změnit. def kill(node): timeout = tkSimpleDialog.askinteger("Kill penalty", "Kill penalty", initialvalue=30) match = hp.datatypes.Match(in_port=node.port,eth_type="ipv4",ipv4_src=node.ip) flow = hp.datatypes.Flow(priority=30000, match=match, hard_timeout=timeout) api.add_flows(node.dpid, flow)
Ujistěte se, že běží Mininet síť, dejte pingall a pak spusťte naší aplikaci a vyzkoušejte.
Vložení inline služby (např. IPS inspekce) Následující aplikace už je trochu složitější, ale víc jak polovina kódu jde na vrub GUI, takže vlastní logika je relativně jednoduchá. Půjde o příklad, který má omezené reálné použití (bude jen pro IPS připojenou do prvku, přes který jde provoz, co chceme sledovat), ale můžete začít přemýšlet o rozšíření (k tomu se ještě vrátím).
Použití SDN je ideálním řešením pro vkládání služeb do vybraného typu provozu a to se skrývá za různými názvy jako je service insertion, service chaining, network TAP, inline inspection a tak podobně. Náš příklad je podmnožinou určenou k přesměrování komunikačního toku (podle pravidel, které si řeknete) do analyzátoru či IPS. Traffic lze volit podle libovolných match parametrů, které OpenFlow a 33 | H P S D N k o n t r o l e r
váš fyzický switch podporuje – fyzický port, EtherType, MAC adresy, IP adresy, TCP/UDP porty apod. Toto přesměrování se provádí SDN principy, tedy nevyžaduje klasická řešení typu PBR apod. V našem příkladu se zaměřujeme na odklonění provozu – další velkou kapitolou je network TAP, tedy kopie provozu (alternativa k mirroringu).
Logika aplikace SDN kontroler plánuje nejkratší trasu v síti (ve verzi 2.0.0 podle počtu hopů) a je relativně snadné toto změnit a cestovat jinudy (tedy přes jiné datapath, rozuměj OpenFlow instance). Totéž platí pro hybridní režim, kde ve výchozím stavu cestu plánuje síť klasickým způsobem, ale můžeme proaktivně přidat naší jinou přes OpenFlow. Nicméně IPS nebo jiný inline systém nebude ještě podporovat OpenFlow (tedy nebude se chovat jako OpenFlow switch). Proto musíme cestu rozdělit na dvě. Cesta od zdroje provozu k prvnímu portu IPS a z druhého portu IPS do cílové stanice. A pochopitelně totéž v opačném směru. V našem případě jde o jeden switch, takže to dá celkem 4 pravidla, která nainstalujeme.
Kód Tentokrát nepůjdeme řádek po řádku, ale tady máte kompletní aplikaci a následně rozebereme některé její části. #!/usr/bin/env python import argparse import hpsdnclient as hp from Tkinter import * import tkSimpleDialog auth = hp.XAuthToken(user='sdn',password='skyline',server='10.10.10.180') api = hp.Api(controller='10.10.10.180',auth=auth) ports = None nodes = None dpid = None def dpidSelect(): global dpid global ports global nodes dpid = dpidLB.get(dpidLB.curselection()[0]) ports = api.get_ports(dpid) analyzer1.delete(0, END) analyzer2.delete(0, END) for port in ports: analyzer1.insert(END, port.name) analyzer2.insert(END, port.name) nodes = api.get_nodes(dpid=dpid) node1.delete(0, END) node2.delete(0, END) for node in nodes: node1.insert(END, node.ip) node2.insert(END, node.ip) def insert(): global ports global nodes global dpid port1 = ports[int(analyzer1.curselection()[0])].id port2 = ports[int(analyzer2.curselection()[0])].id
34 | H P S D N k o n t r o l e r
traffic1_inport = nodes[int(node1.curselection()[0])].port traffic2_inport = nodes[int(node2.curselection()[0])].port traffic1_ip = nodes[int(node1.curselection()[0])].ip traffic2_ip = nodes[int(node2.curselection()[0])].ip timeout = timeScale.get() outputFwdAn = hp.datatypes.Action(output=port1) matchFwdAn = hp.datatypes.Match(in_port=traffic1_inport,eth_type="ipv4",ipv4_dst=traffic2_ip) flowFwdAn = hp.datatypes.Flow(priority=40000, match=matchFwdAn, hard_timeout=timeout, actions=outputFwdAn) api.add_flows(dpid, flowFwdAn) outputFwdDst = hp.datatypes.Action(output=traffic2_inport) matchFwdDst = hp.datatypes.Match(in_port=port2,eth_type="ipv4",ipv4_dst=traffic2_ip) flowFwdDst = hp.datatypes.Flow(priority=40000, match=matchFwdDst, hard_timeout=timeout, actions=outputFwdDst) api.add_flows(dpid, flowFwdDst) outputRevAn = hp.datatypes.Action(output=port2) matchRevAn = hp.datatypes.Match(in_port=traffic2_inport,eth_type="ipv4",ipv4_dst=traffic1_ip) flowRevAn = hp.datatypes.Flow(priority=40000, match=matchRevAn, hard_timeout=timeout, actions=outputRevAn) api.add_flows(dpid, flowRevAn) outputRevDst = hp.datatypes.Action(output=traffic1_inport) matchRevDst = hp.datatypes.Match(in_port=port1,eth_type="ipv4",ipv4_dst=traffic1_ip) flowRevDst = hp.datatypes.Flow(priority=40000, match=matchRevDst, hard_timeout=timeout, actions=outputRevDst) api.add_flows(dpid, flowRevDst) root = Tk() dpidLB = Listbox(root, borderwidth=2, exportselection=0) dpidLB.grid(row=1,column=0) datapaths = api.get_datapaths() for datapath in datapaths: dpidLB.insert(END, datapath.dpid) Label(root, text="Select switch").grid(row=0,column=0) Button(root, text="Select", command=dpidSelect).grid(row=1,column=1) Label(root, text="").grid(row=2,column=0) Label(root, text="Select analyzer ports").grid(row=3,column=0,columnspan=2) analyzer1 = Listbox(root, borderwidth=2, exportselection=0) analyzer1.grid(row=4,column=0) analyzer2 = Listbox(root, borderwidth=2, exportselection=0) analyzer2.grid(row=4,column=1) Label(root, text="Select traffic nodes").grid(row=3,column=2,columnspan=2) node1 = Listbox(root, borderwidth=2, exportselection=0) node1.grid(row=4,column=2) node2 = Listbox(root, borderwidth=2, exportselection=0) node2.grid(row=4,column=3)
35 | H P S D N k o n t r o l e r
Label(root, text="").grid(row=5,column=0) Label(root, text="Hard timeout (0=infinite)").grid(row=6,column=0) timeScale = Scale(root, from_=0, to=120, orient=HORIZONTAL) timeScale.set(60) timeScale.grid(row=7,column=0) Button(root, text="Insert", command=insert).grid(row=7,column=1) root.mainloop()
Podobně, jako v předchozích případech, se připojujeme ke kontroleru. Přeskočme prozatím dvě definované funkce a koukněme se na hlavní tělo. V něm se odehrávají hlavně věci kolem grafického rozhranní. Tak například na začátku si vytvoříme ListBox a naplníme jej seznamem DPID, která kontroler vidí: root = Tk() dpidLB = Listbox(root, borderwidth=2, exportselection=0) dpidLB.grid(row=1,column=0) datapaths = api.get_datapaths() for datapath in datapaths: dpidLB.insert(END, datapath.dpid)
Pak následuje několik dalších ovládacích prvků a list boxů – v hlavním těle jde spíše o jejich definici, naplníme je jinde. Všimněte si dvou tlačítek: Button(root, text="Select", command=dpidSelect).grid(row=1,column=1) Button(root, text="Insert", command=insert).grid(row=7,column=1
První slouží k výběru prvku (DPID) a volá funkci dpidSelect. To druhé je k vložení inline služby a volá funkci insert. Podívejme se na ně podrobněji: def dpidSelect(): global dpid global ports global nodes
Slovíčko „global“ říká, že v této funkci pokud budeme mluvit o proměnné ports, tak máme na mysli tu stejnou, která je v hlavním těle. Jinak by vznikla nová proměnná s lokální platností ve funkci. Pak naplňujeme grafické prvky, například takto: nodes = api.get_nodes(dpid=dpid) node1.delete(0, END) node2.delete(0, END) for node in nodes: node1.insert(END, node.ip) node2.insert(END, node.ip)
Prozkoumejme hlavní logiku aplikace a tou je funkce insert. def insert(): global ports global nodes global dpid
36 | H P S D N k o n t r o l e r
port1 = ports[int(analyzer1.curselection()[0])].id port2 = ports[int(analyzer2.curselection()[0])].id ..
Následuje sesbírání informací z GUI o tom, co bylo zvoleno. Jdeme na to tak, že pořadí v ListBoxech je stejné, jako pořadí ve stažených strukturách typu ports. Tím pádem GUI portů ukazuje jejich hezké jméno, my z pozice v seznamu vyhledáme patřičný záznam v ports a přečteme si id portu (které potřebujeme). Podobně je to s dalšími grafickými prvky. A tím se dostáváme k tomu hlavnímu – vytvoření flow-modů. Potřebujeme udělat následující 4 pravidla: Z prvního zdroje (match inport, kde je 10.0.0.1 + IPv4 EtherType + dstIP 10.0.0.2) do prvního portu IPS Z druhého portu IPS (match inport IPS_2 + IPv4 EtherType + dstIP 10.0.0.2) do portu, kde je cíl Z portu cíle (match inport, kde je 10.0.0.2 + IPv4 EtherType + dstIP 10.0.0.1) do druhého portu IPS Z prvního portu IPS (match inport IPS_1 + IPv4 EtherType + dstIP 10.0.0.1) do portu, kde byl zdroj V kódu je to takhle: outputFwdAn = hp.datatypes.Action(output=port1) matchFwdAn = hp.datatypes.Match(in_port=traffic1_inport,eth_type="ipv4",ipv4_dst=traffic2_ip) flowFwdAn = hp.datatypes.Flow(priority=40000, match=matchFwdAn, hard_timeout=timeout, actions=outputFwdAn) api.add_flows(dpid, flowFwdAn) outputFwdDst = hp.datatypes.Action(output=traffic2_inport) matchFwdDst = hp.datatypes.Match(in_port=port2,eth_type="ipv4",ipv4_dst=traffic2_ip) flowFwdDst = hp.datatypes.Flow(priority=40000, match=matchFwdDst, hard_timeout=timeout, actions=outputFwdDst) api.add_flows(dpid, flowFwdDst) outputRevAn = hp.datatypes.Action(output=port2) matchRevAn = hp.datatypes.Match(in_port=traffic2_inport,eth_type="ipv4",ipv4_dst=traffic1_ip) flowRevAn = hp.datatypes.Flow(priority=40000, match=matchRevAn, hard_timeout=timeout, actions=outputRevAn) api.add_flows(dpid, flowRevAn) outputRevDst = hp.datatypes.Action(output=traffic1_inport) matchRevDst = hp.datatypes.Match(in_port=port1,eth_type="ipv4",ipv4_dst=traffic1_ip) flowRevDst = hp.datatypes.Flow(priority=40000, match=matchRevDst, hard_timeout=timeout, actions=outputRevDst) api.add_flows(dpid, flowRevDst)
Otestování Inline zařízení budeme simulovat v Mininet – budete potřebovat mít nainstalované balíčky bridgeutils a ebtables (na dodaných VM to je). Použijte tento skript pro nastartování sítě, který rovnou nastaví stanici h3 (která je připravena do dvou portů) do inline režimu. K tomu použijeme bridge v Linux. Současně naše odklánění provozu bude na IP vrstvě (lze změnit na fyzické porty, MAC adresy, subnety, TCP/UDP), takže inline systém má potenciál udělat broadcast smyčku. Proto použijeme ebtables a broadcast/multicast budeme blokovat. Na vaší VM jsou tyto balíčky nainstalovány, pokud vytváříte vlastní, přidejte je: sudo apt-get install ebtables tshark bridge-utils
Tady je mininet skript, najdete ho také na vaší VM: #!/usr/bin/python from mininet.net import Mininet from mininet.node import Controller, RemoteController, OVSSwitch, UserSwitch
37 | H P S D N k o n t r o l e r
from mininet.cli import CLI from mininet.link import Intf from mininet.log import setLogLevel, info import sys def myNetwork(): net = Mininet( topo=None, build=False) info( '*** Add controller ' + sys.argv[1] + '\n') net.addController('c0', controller=RemoteController, ip=sys.argv[1]) info( '*** Add switches\n') s1 = net.addSwitch('s1', switch=UserSwitch) info( '*** Add hosts\n') h1 = net.addHost('h1', ip='10.0.0.1') h2 = net.addHost('h2', ip='10.0.0.2') h3 = net.addHost('h3', ip='0.0.0.0') info( '*** Add links\n') net.addLink(h1, s1) net.addLink(h2, s1) net.addLink(h3, s1) net.addLink(h3, s1) info( '*** Starting network\n') net.start() info( '*** Configure h3 to simulate IPS analyzer\n') h3.cmdPrint('brctl addbr br0') h3.cmdPrint('brctl addif br0 h3-eth0') h3.cmdPrint('brctl addif br0 h3-eth1') h3.cmdPrint('ifconfig br0 up') h3.cmdPrint('ebtables -I FORWARD -d Multicast -j DROP') info( '*** To whatch inspected traffic use tshark -P ***\n') info( '*** (you may use -f to filter, for example -f "icmp") ***\n') CLI(net) net.stop()
if __name__ == '__main__': setLogLevel( 'info' ) myNetwork()
Spusťte síť příkazem: sdn@ubuntu:~/scripts$ sudo python inlineTopo.py 10.10.10.180
Otevřete si inline stanici: mininet> xterm h3
38 | H P S D N k o n t r o l e r
Otevře se vám terminálové okno h3. V něm zapněte tshark (odchytávač paketů) s tiskem na obrazovku a filtrací jen na icmp provoz: tshark –P –f „icmp“
Jděte zpět do okna s Mininet a nejprve zadejte ping all. Následně rozjeďte ping mezi stanicí h1 a h2: mininet> pingall *** Ping: testing ping reachability h1 -> h2 h3 h2 -> h1 h3 h3 -> X X *** Results: 33% dropped (4/6 received) mininet> h1 ping h2 PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data. 64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.051 ms 64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.069 ms 64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.075 ms
Vidíme, že ping mezi stanicemi funguje – koukněte do analyzátoru a zjistíte, že žádné pakety nevidí, tedy komunikace jde napřímo.
39 | H P S D N k o n t r o l e r
Otevřete nové terminálové okno a spusťte naší aplikaci. sdn@ubuntu:~/scripts$ python inlineGUI.py
Zvolte náš switch (DPID) a klikněte select. Následně zvolte porty inline analyzátoru a naše dvě stanice. Snižte hard timeout třeba na 5 vteřin, ale na insert ještě neklikejte.
40 | H P S D N k o n t r o l e r
Srovnejte si okno s Mininetem (kde běží ping) tak, ať současně vidíte okno analyzátoru. Pak v naší aplikaci klikněte na Insert. V ten okamžik uvidíte, že po následujících 5 vteřin jde provoz přes naši „inline krabičku“ a po pěti vteřinách se vrátí k přímé cestě. Všimněte si, že z pohledu klienta je to transparentní (není výpadek).
41 | H P S D N k o n t r o l e r
Možnost dalšího rozvoje Ve zbývajícím čase se můžete zamýšlet třeba nad následujícími vylepšeními: Přidejte možnost přesměrovat jen konkrétní TCP/UDP port Přidejte možnost přesměrovat jen na základě fyzických portů Dolaďte GUI, aby bylo hezčí Přidejte podporu pro fyzické HP prvky: o Použít hybridní režim (výchozí akce bude Forward Normal) o OpenFlow 1.3 tzn. akci dát do instrukce o Ověřit, že je pravidlo zpracováváno v hardware, v jaké tabulce apod. Promýšlejte možnosti při více hopech (jednoduché řešení by bylo klasický tunel jako virtuální OpenFlow port – může se objevit na ProVision prvcích v blízké budoucnosti – jinak využít Topology Manager pro plánování cest od a z analyzátoru)
42 | H P S D N k o n t r o l e r
Závěr Gratuluji, že se vám podařilo dostat až na konec labu! Doufám, že vás bude inspirovat k dalšímu zkoumání a seznamování s fascinujícím světem Software-defined Networking. Sledujte stránky http://sdndevcenter.hp.com a také fórum na adrese http://www.hp.com/networking/sdnforum a nezapomínejte i na český http://www.netsvet.cz, kde najdete články, videa, ukázky či dokumenty apod. NetSvět můžete následovat i na Twitteru na @NetSvetCZ. Pokud se nechcete stát programátorem, doporučuji zkoumat svět SDN z pohledu konkrétních aplikací (např. HP Network Protector), architektury a designu řešení. Programování v Python není o moc složitější, než práce s klasickou příkazovou řádkou – jde o jednoduchý a intuitivní jazyk, který mimochodem začíná HP zabudovávat i do klasického CLI v prvcích (například u 5900 už je)! Díky hpsdn-client knihovně do Python je to snadné a můžete připravit zajímavá zákaznická řešení. A mimochodem – moderní REST API má i HP nástroj pro správu sítě iMC (licence se jmenuje eAPI nebo je součást verze Enterprise zdarma). Vaše skriptovací možnosti jsou tak obrovské! Nebo chcete víc? Chcete těsnější kontrolu nad sítí, asynchronní události, generovat si Packet_In, Packet_Out, vyzobávat pakety ze sítě nebo do ní vstřikovat vámi vytvořené? Chcete, aby vaše aplikace byla přímo integrována s kontrolerem a jeho GUI a chcete ji nabídnout na HP SDN App Store? Pak doporučuji ponořit se do obtížnějších, ale velmi mocných hlubin jazyka Java. I pro základní seznámení s tím, jak napsat Java aplikaci pro kontroler, vám rád pomůžu – existuje pokračování tohoto dokumentu zaměřené právě na to. Ať už tedy budete HP SDN prodávat, navrhovat, implementovat, integrovat přes API s dalšími řešeními nebo přímo vyvíjet aplikaci pro HP SDN App Store přeji vám mnoho zdaru a držím palce! Tomáš Kubica Email:
[email protected] Twitter: @tkubica
43 | H P S D N k o n t r o l e r
Příloha – jak vytvořit používané VM VM pro kontroler Pro kontroler začněte z čisté instalace Ubuntu server 12.04.4 amd64. Po instalaci bude potřeba mít přístup na Internet pro stažení dalších balíčků. Můžete například mít dvě rozhraní – jedno interní (pro switche) a jedno pro Internet. Pro začátek doporučuji update zdrojů a instalaci SSH: sudo apt-get update sudo apt-get install openssh-server
Naisntalujte pár základních služeb: sudo apt-get install python-software-properties ubuntu-cloud-keyring
Přidejte repository a aktualizujte: sudo add-apt-repository "deb http://ubuntu-cloud.archive.canonical.com/ubuntu precise-updates/folsom main" sudo apt-get update
Nainstalujte další balíčky: sudo apt-get install openjdk-7-jre-headless postgresql keystone keystone-doc python-keystone iptables unzip curl
Nakopírujte stažený .deb balíček z HP stránek do kontroleru, například využitím WinSCP. Spusťte instalaci kontroleru: sudo dpkg -i hp-sdn-ctl_2.0.0.4253_amd64.deb
Dejte tomu chvilku a ověřte, že kontroler funguje: sdn@ubuntu:~$ dpkg -l | grep hp-sdn-ctl ii hp-sdn-ctl 2.0.0.4253
HP VAN SDN Controller
sdn@ubuntu:~$ service sdnc status sdnc start/running, process 10854
44 | H P S D N k o n t r o l e r
Výchozí login je sdn / skyline
Pokud budete pro kontroler psát i nativní Java aplikace (není nutné pro programování přes REST API) vypněte ověřování signatur. Editujte konfigurační soubor kontroleru: sudo nano /etc/init/sdnc.conf
Najděte definice proměnné JAVA_OPTS a na její konec přidejte -Dsdn.signedJar=none
Zmáčněte CTRL+O a CTRL+X pro uložení a vyskočení ven. Restartujte kontroler: sudo service sdnc restart
VM pro vývojáře Pro vývojářskou VM začněte z čisté instalace Ubuntu desktop 12.04.4 amd64. Po instalaci bude potřeba mít přístup na Internet pro stažení dalších balíčků. Můžete například mít dvě rozhraní – jedno interní (pro komunikaci s kontrolerem) a jedno pro Internet. Otevřete terminál (např. CTRL + ALT + T), aktualizujte repozitář a nainstalujte SSH. sudo apt-get update sudo apt-get install openssh-server
Nainstalujeme pár balíčků vhodných pro vývoj aplikací v Python a potřebných pro instalaci dalších komponent, jako je Mininet nebo OVS sudo apt-get install python-pip python-tk wget curl git
Pro vývoj v Java nainstalujte sudo apt-get install openjdk-7-jdk eclipse maven
Pokud budete chtít kromě fyzických prvků používat i testovací virtuální prostředí nainstalujeme OpenvSwitch a Mininet. Pro jednoduchost využijeme instalační skripty: git clone git://github.com/mininet/mininet.git mininet/util/install.sh -fnv
45 | H P S D N k o n t r o l e r
Abychom mohli sledovat OpenFlow komunikaci mezi kontrolerem a Mininet nainstalujeme Wireshark. Stávající stabilní verze zatím neobsahuje OpenFlow dissector (nerozumí protokolu) – lze doinstalovat z třetích stran, nicméně Wireshark plánuje zabudovanou podporu ve verzi 1.12. Protože tato zatím není uvolněna, použijeme repozitář na devel verzi 1.11 sudo add-apt-repository ppa:dreibh/ppa sudo apt-get update sudo apt-get install wireshark tshark
Následně umožníme zachytávat pakety i non-root uživatelům sudo setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' /usr/bin/dumpcap
A jako poslední specifikujeme na jakém TCP portu se má aplikovat. Provedeme editaci souboru: mkdir ~/.wireshark sudo gedit ~/.wireshark/preferences
a vložíme toto: # openflow TCP port if other than the default # A decimal number openflow.tcp.port: 6633
Ověříme, že Wireshark funguje. Zapněte wireshark: wireshark &
Dejte zachytávat pakety na interface směřujícím ke kontroleru a spusťte Mininet síť napojenou na kontroler, například: sudo mn –controller=remote,ip=10.10.10.180 pingall
Měli byste vidět OpenFlow pakety a rozumět jim
46 | H P S D N k o n t r o l e r
V Mininet 2.1 je možné spouštět wireshark i ve virtuálním host. V Mininet konzoli napište h1 wireshark &
47 | H P S D N k o n t r o l e r
Pokud budeme pracovat v Python možná je ideální čas stáhnout knihovny pro zjednodušení práce. sudo pip install hp-sdn-client
Chcete-li vyvíjet nativní Java aplikace je správná chvíle nainstalovat SDK podle lab guide pro Java vývoj. Pro vývoj GUI component je k dispozici živá dokumentace pro SKI framework (HP GUI rámec postavený na JQuery). Soubor s příponou war najdete v SDK souboru. Stačí nainstalovat si tomcat, soubor přejmenovat na něco jednoduššího a soubor nakopírovat do patřičného adresáře. sudo apt-get install tomcat7 sudo mv hp-util-ski-ui-6.29.0.war /var/lib/tomcat7/webapps/guidoc.war
Dokumentaci najdete na adrese 127.0.0.1:8080/guidoc
48 | H P S D N k o n t r o l e r
Příloha – hotové skripty where.py /usr/bin/env python import argparse import hpsdnclient as hp auth = hp.XAuthToken(user='sdn',password='skyline',server='10.10.10.180') api = hp.Api(controller='10.10.10.180',auth=auth) def main(): parser = argparse.ArgumentParser() parser.add_argument('--ip', type=str, help="The IP Address to find", required=True) args = parser.parse_args() print whereis(args.ip)
def whereis(ip): nodes = api.get_nodes() for node in nodes: if node.ip==ip: return node
if __name__ == "__main__": main()
49 | H P S D N k o n t r o l e r
kill.py #!/usr/bin/env python import argparse import hpsdnclient as hp auth = hp.XAuthToken(user='sdn',password='skyline',server='10.10.10.180') api = hp.Api(controller='10.10.10.180',auth=auth) def main(): parser = argparse.ArgumentParser() parser.add_argument('--ip', type=str, help="The IP Address to find", required=True) parser.add_argument('--penalty', type=int, help="Penalty time in seconds (optional)", required=False, default=0) args = parser.parse_args() kill(whereis(args.ip),args.penalty) print args.ip + ' has been killed!'
def whereis(ip): nodes = api.get_nodes() for node in nodes: if node.ip==ip: return node def kill(node, timeout): match = hp.datatypes.Match(in_port=node.port,eth_type="ipv4",ipv4_src=node.ip) flow = hp.datatypes.Flow(priority=30000, match=match, hard_timeout=timeout) api.add_flows(node.dpid, flow)
if __name__ == "__main__": main()
50 | H P S D N k o n t r o l e r
stanice.py #!/usr/bin/env python import argparse import hpsdnclient as hp from Tkinter import * auth = hp.XAuthToken(user='sdn',password='skyline',server='10.10.10.180') api = hp.Api(controller='10.10.10.180',auth=auth) root = Tk() nodes = api.get_nodes() Label(root, text="MAC", borderwidth=2).grid(row=0,column=0) Label(root, text="IP", borderwidth=2).grid(row=0,column=1) Label(root, text="DPID", borderwidth=2).grid(row=0,column=2) Label(root, text="PORT", borderwidth=2).grid(row=0,column=3) myrow=1 for node in nodes: Label(root, text=node.mac, borderwidth=2).grid(row=myrow,column=0) Label(root, text=node.ip, borderwidth=2).grid(row=myrow,column=1) Label(root, text=node.dpid, borderwidth=2).grid(row=myrow,column=2) Label(root, text=node.port, borderwidth=2).grid(row=myrow,column=3) myrow += 1 root.mainloop()
51 | H P S D N k o n t r o l e r
killGUI.py #!/usr/bin/env python import argparse import hpsdnclient as hp from Tkinter import * import tkSimpleDialog auth = hp.XAuthToken(user='sdn',password='skyline',server='10.10.10.180') api = hp.Api(controller='10.10.10.180',auth=auth) root = Tk() nodes = api.get_nodes() Label(root, text="MAC", borderwidth=2).grid(row=0,column=0) Label(root, text="IP", borderwidth=2).grid(row=0,column=1) Label(root, text="DPID", borderwidth=2).grid(row=0,column=2) Label(root, text="PORT", borderwidth=2).grid(row=0,column=3) myrow=1 for node in nodes: Label(root, text=node.mac, borderwidth=2).grid(row=myrow,column=0) Label(root, text=node.ip, borderwidth=2).grid(row=myrow,column=1) Label(root, text=node.dpid, borderwidth=2).grid(row=myrow,column=2) Label(root, text=node.port, borderwidth=2).grid(row=myrow,column=3) Button(root, text="Kill", command=lambda node=node: kill(node)).grid(row=myrow,column=4) myrow += 1 def kill(node): timeout = tkSimpleDialog.askinteger("Kill penalty", "Kill penalty", initialvalue=30) match = hp.datatypes.Match(in_port=node.port,eth_type="ipv4",ipv4_src=node.ip) flow = hp.datatypes.Flow(priority=30000, match=match, hard_timeout=timeout) api.add_flows(node.dpid, flow) root.mainloop()
52 | H P S D N k o n t r o l e r
inlinegui.py #!/usr/bin/env python import argparse import hpsdnclient as hp from Tkinter import * import tkSimpleDialog auth = hp.XAuthToken(user='sdn',password='skyline',server='10.10.10.180') api = hp.Api(controller='10.10.10.180',auth=auth) ports = None nodes = None dpid = None def dpidSelect(): global dpid global ports global nodes dpid = dpidLB.get(dpidLB.curselection()[0]) ports = api.get_ports(dpid) analyzer1.delete(0, END) analyzer2.delete(0, END) for port in ports: analyzer1.insert(END, port.name) analyzer2.insert(END, port.name) nodes = api.get_nodes(dpid=dpid) node1.delete(0, END) node2.delete(0, END) for node in nodes: node1.insert(END, node.ip) node2.insert(END, node.ip) def insert(): global ports global nodes global dpid port1 = ports[int(analyzer1.curselection()[0])].id port2 = ports[int(analyzer2.curselection()[0])].id traffic1_inport = nodes[int(node1.curselection()[0])].port traffic2_inport = nodes[int(node2.curselection()[0])].port traffic1_ip = nodes[int(node1.curselection()[0])].ip traffic2_ip = nodes[int(node2.curselection()[0])].ip timeout = timeScale.get() outputFwdAn = hp.datatypes.Action(output=port1) matchFwdAn = hp.datatypes.Match(in_port=traffic1_inport,eth_type="ipv4",ipv4_dst=traffic2_ip) flowFwdAn = hp.datatypes.Flow(priority=40000, match=matchFwdAn, hard_timeout=timeout, actions=outputFwdAn) api.add_flows(dpid, flowFwdAn) outputFwdDst = hp.datatypes.Action(output=traffic2_inport) matchFwdDst = hp.datatypes.Match(in_port=port2,eth_type="ipv4",ipv4_dst=traffic2_ip)
53 | H P S D N k o n t r o l e r
flowFwdDst = hp.datatypes.Flow(priority=40000, match=matchFwdDst, hard_timeout=timeout, actions=outputFwdDst) api.add_flows(dpid, flowFwdDst) outputRevAn = hp.datatypes.Action(output=port2) matchRevAn = hp.datatypes.Match(in_port=traffic2_inport,eth_type="ipv4",ipv4_dst=traffic1_ip) flowRevAn = hp.datatypes.Flow(priority=40000, match=matchRevAn, hard_timeout=timeout, actions=outputRevAn) api.add_flows(dpid, flowRevAn) outputRevDst = hp.datatypes.Action(output=traffic1_inport) matchRevDst = hp.datatypes.Match(in_port=port1,eth_type="ipv4",ipv4_dst=traffic1_ip) flowRevDst = hp.datatypes.Flow(priority=40000, match=matchRevDst, hard_timeout=timeout, actions=outputRevDst) api.add_flows(dpid, flowRevDst) root = Tk() dpidLB = Listbox(root, borderwidth=2, exportselection=0) dpidLB.grid(row=1,column=0) datapaths = api.get_datapaths() for datapath in datapaths: dpidLB.insert(END, datapath.dpid) Label(root, text="Select switch").grid(row=0,column=0) Button(root, text="Select", command=dpidSelect).grid(row=1,column=1) Label(root, text="").grid(row=2,column=0) Label(root, text="Select analyzer ports").grid(row=3,column=0,columnspan=2) analyzer1 = Listbox(root, borderwidth=2, exportselection=0) analyzer1.grid(row=4,column=0) analyzer2 = Listbox(root, borderwidth=2, exportselection=0) analyzer2.grid(row=4,column=1) Label(root, text="Select traffic nodes").grid(row=3,column=2,columnspan=2) node1 = Listbox(root, borderwidth=2, exportselection=0) node1.grid(row=4,column=2) node2 = Listbox(root, borderwidth=2, exportselection=0) node2.grid(row=4,column=3) Label(root, text="").grid(row=5,column=0) Label(root, text="Hard timeout (0=infinite)").grid(row=6,column=0) timeScale = Scale(root, from_=0, to=120, orient=HORIZONTAL) timeScale.set(60) timeScale.grid(row=7,column=0) Button(root, text="Insert", command=insert).grid(row=7,column=1) root.mainloop()
54 | H P S D N k o n t r o l e r
inlinetopo.py #!/usr/bin/python from mininet.net import Mininet from mininet.node import Controller, RemoteController, OVSSwitch, UserSwitch from mininet.cli import CLI from mininet.link import Intf from mininet.log import setLogLevel, info import sys def myNetwork(): net = Mininet( topo=None, build=False) info( '*** Add controller ' + sys.argv[1] + '\n') net.addController('c0', controller=RemoteController, ip=sys.argv[1]) info( '*** Add switches\n') s1 = net.addSwitch('s1', switch=UserSwitch) info( '*** Add hosts\n') h1 = net.addHost('h1', ip='10.0.0.1') h2 = net.addHost('h2', ip='10.0.0.2') h3 = net.addHost('h3', ip='0.0.0.0') info( '*** Add links\n') net.addLink(h1, s1) net.addLink(h2, s1) net.addLink(h3, s1) net.addLink(h3, s1) info( '*** Starting network\n') net.start() info( '*** Configure h3 to simulate IPS analyzer\n') h3.cmdPrint('brctl addbr br0') h3.cmdPrint('brctl addif br0 h3-eth0') h3.cmdPrint('brctl addif br0 h3-eth1') h3.cmdPrint('ifconfig br0 up') h3.cmdPrint('ebtables -I FORWARD -d Multicast -j DROP') info( '*** To whatch inspected traffic use tshark -P ***\n') info( '*** (you may use -f to filter, for example -f "icmp") ***\n') CLI(net) net.stop()
if __name__ == '__main__': setLogLevel( 'info' ) myNetwork()
55 | H P S D N k o n t r o l e r