レイバンのサングラス

謙虚に100億PVを目指します、よろしくお願いします

基礎的SDNに処理を加える

こちらは #インフラ勉強会 Advent Calendar 2018 18日目の記事です。今18日の29時前なので滑り込みセーフですね

adventar.org
 

先日の記事はkusuwadaさんでした。
kusuwada.hatenablog.com


加藤(suminofu_3)と申します、研究室でSDN(Software Defined Networking)の研究をしている学生です。

自分の整理も兼ねて、RyuとOVSを使って環境構築した後、比較的簡単に遊べることについて説明したいと思います。最近資格試験にも出題されることが増えてきており、とりあえず組んだ上でちょっといじりたい〜という方には参考になるかなと思っています。

用意したもの

・(Guest)OS : Ubuntu 14.04 or 16.04
・Rapberry Pi 3B × 複数台

 
SDNを試すならMininetで仮想的に構築するのが経済的な感じがありますが、物理的に組んだ方がやれることも増えるのでラズパイがあればぜひ。

構成


 f:id:kato770:20181219043140p:plain

コントローラーのRyu、そしてRasberry Piに仮想スイッチ「Open vSwitch」をインストールしたものを利用します。
OVS間では接続が2本入っております。本来ならループまったなしなのですが、持ち前のフロー制御によってあらかじめ防いでおくことができます。フラッディングをドロップさせるフローを優先度を下げて敷いておけばとりあえずは事故りません。
 

ここからの方針をいくつか紹介します。

パケット統計取得機能

Ryuは大変ありがたいことにサンプルコードをいくつか配布し、なおかつ日本語のリファレンスも存在しています。OpenFlowコントローラーの中でも入門にオススメされることが多い理由ですね。

その中で、パケットの統計情報を定期的にOVSから取ってくることができる機能があります。

simple_monitor_13.py(一部抜粋)

def _port_stats_reply_handler(self, ev):
        body = ev.msg.body

        self.logger.info('datapath         port     '
                         'rx-pkts  rx-bytes rx-error '
                         'tx-pkts  tx-bytes tx-error')
        self.logger.info('---------------- -------- '
                         '-------- -------- -------- '
                         '-------- -------- --------')
        for stat in sorted(body, key=attrgetter('port_no')):
            self.logger.info('6x %8x � � � � � �',
                             ev.msg.datapath.id, stat.port_no,
                             stat.rx_packets, stat.rx_bytes, stat.rx_errors,
                             stat.tx_packets, stat.tx_bytes, stat.tx_errors)

OVSごとに通過しているパケットの統計情報がわかるようになります。
下記は少し改変したもので、同じような結果が出力されます。

datapath          in-port   eth-dst            out-port  packets
------------------ -------- ----------------- ---------- --------
0000000000000001    1       aa:aa:aa:aa:aa:aa   3            439
0000000000000001    2       bb:bb:bb:bb:bb:bb   1            574
0000000000000001    3       cc:cc:cc:cc:cc:cc   2           1134

プロトコルごとの処理

TCPUDPなどのプロトコルや、ポート番号を判別するためのコーディングです。
APIを参考に組んでみました。
Ryu API Reference — Ryu 4.30 documentation

@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def _packet_in_handler(self, ev):

        if ev.msg.msg_len < ev.msg.total_len:
            self.logger.debug("packet truncated: only %s of %s bytes", ev.msg.msg_len, ev.msg.total_len)
        msg = ev.msg
        datapath = msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
        in_port = msg.match['in_port']

        pkt = packet.Packet(msg.data)
	ip4 = pkt.get_protocol(ipv4.ipv4)
	tcpheader = pkt.get_protocol(tcp.tcp)
	udpheader = pkt.get_protocol(udp.udp)

        dst = ip4.dst
        src = ip4.src
	if tcpheader:
	    dstport = tcpheader.dst_port
            srcport = tcpheader.src_port
	elif udpheader:
	    dstport = udpheader.dst_port
	    srcport = udpheader.src_port

こちらはPacketInイベントの際にTCPUDPのパケットを判別できるようにしたものです。

上記二つを組み合わせればプロトコルやポート番号によってOVS間の通過するルートを変更させるネットワークの構築が可能です!
よければ実践してみてください〜。


お次(本日...)はAnorlondo448さんです!