今回はMeter Tableを使ってPolicingということで、より実践的にDoS/Worm Mitigationをやってみた。
port 5003宛で500kbpsを超えるトラッフィクは異常と見なし、DSCP値を24から8(CS 1:Scavenger)へとMarkdownし、DSCP:24の帯域を確保するシナリオとした。(実践的では無いかもしれないが。。)
コントローラはRyuを使い、OpenFlow Switchはofsoftswitch13、ネットワークシュミレータにはMininet2.0を使用している。
コントローラの実装はCloudy Switchを使用。
ネットワーク構成について
以下の構成で実験を行った。
[Host1] - [OFS1] - [OFS2] - [Host2]
リンクスピードは6Mbpsとなるようにofsoftswitch13の
lib/netdev.cのnetdev->speedを変更している。
Mininetのスクリプトは以下のとおり
from mininet.net import Mininet
from mininet.cli import CLI
from mininet.node import UserSwitch as Switch
from mininet.node import RemoteController
from sample_topology import MyTopo
def run(net):
s1 = net.getNodeByName('s1')
s1.cmdPrint('dpctl unix:/tmp/s1 queue-mod 1 1 100')
s1.cmdPrint('dpctl unix:/tmp/s1 queue-mod 1 2 300')
s1.cmdPrint('dpctl unix:/tmp/s1 queue-mod 1 3 600')
s1.cmdPrint('dpctl unix:/tmp/s1 queue-mod 1 1 100')
s1.cmdPrint('dpctl unix:/tmp/s1 queue-mod 1 2 300')
s1.cmdPrint('dpctl unix:/tmp/s1 queue-mod 1 3 600')
s2 = net.getNodeByName('s2')
s2.cmdPrint('dpctl unix:/tmp/s2 queue-mod 1 1 100')
s2.cmdPrint('dpctl unix:/tmp/s2 queue-mod 1 2 300')
s2.cmdPrint('dpctl unix:/tmp/s2 queue-mod 1 3 600')
s2.cmdPrint('dpctl unix:/tmp/s2 queue-mod 2 1 100')
s2.cmdPrint('dpctl unix:/tmp/s2 queue-mod 2 2 300')
s2.cmdPrint('dpctl unix:/tmp/s2 queue-mod 2 3 600')
def genericTest(topo):
net = Mininet(topo=topo, switch=Switch, controller=RemoteController)
net.start()
run(net)
CLI(net)
net.stop()
def main():
topo = MyTopo()
genericTest(topo)
コントローラ側の処理について
まず、Meter Tableのエントリーは以下のとおり
def send_meter_entry(self, datapath):
parser = datapath.ofproto_parser
ofproto = datapath.ofproto
band = parser.OFPMeterBandDscpRemark(rate=500, burst_size=5, prec_level=2)
req = parser.OFPMeterMod(datapath, ofproto.OFPMC_ADD,
ofproto.OFPMF_KBPS, 1, [band])
datapath.send_msg(req)
eth_IP = ether.ETH_TYPE_IP
match = parser.OFPMatch(eth_type=eth_IP, ip_dscp=24)
instructions = [parser.OFPInstructionMeter(1),
parser.OFPInstructionGotoTable(2)]
flow_mod = self.create_flow_mod(datapath, 0, 1,
match, instructions)
datapath.send_msg(flow_mod)
# Policing for Scavenger class
band = parser.OFPMeterBandDrop(rate=100,
burst_size=5)
req = parser.OFPMeterMod(datapath, ofproto.OFPMC_ADD,
ofproto.OFPMF_KBPS, 2, [band])
datapath.send_msg(req)
def process_end_hw_addr_flows(self, port):
""" Default flow entry for end switch"""
eth_IP = ether.ETH_TYPE_IP
target_switch = self.switches[port[0]]
datapath = target_switch.switch.dp
dscp_to_queue_values = {0:1, 8:1, 16:1, 24:2, 32:2, 40:2, 48:3, 56:3}
SCAVENGER = 8
for dscp, queue in dscp_to_queue_values.items():
hw_addr = port[2]
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
actions = [parser.OFPActionSetQueue(queue),
parser.OFPActionOutput(port[1])]
match = datapath.ofproto_parser.OFPMatch(eth_type=eth_IP,
ip_dscp=dscp,
eth_dst=hw_addr)
inst = [parser.OFPInstructionActions(
ofproto.OFPIT_APPLY_ACTIONS, actions)]
if dscp == SCAVENGER:
inst.insert(0, parser.OFPInstructionMeter(2))
flow_mod = self.create_flow_mod(datapath, 100, 7, match, inst)
datapath.send_msg(flow_mod)
ここでポイントなのが、
[Host1] - [OFS1] - [OFS2] - [Host2]
上記構成で、OFS1でMeter Table(Meter id:1)を用いてMarkdownしているが、更に全てのOFSの入力ポートでMeter Table(Meter id:2)でScavenger Class(DSCP:8)のトラフィックに対して、100kbps以上の流量があるとパケットをドロップするMeter Entryを登録している。
これによってScavenger ClassのトラフィックでBest Effortトラフィックの帯域を確保することにしている。
また、DSCP:56、48はQueue 3(3.6Mbps)へ、DSCP:40、32、24はQueue 2(1.8Mbps)、DSCP:16、8、0はQueue 1(0.6Mbps)へQueingされるようにFlow Entryは登録されている。
実行
今回は以下のようなマッチング条件でマーキングを行った。
Table 1はMeter Flow用のTableとしている。
s1 dpctl unix:/tmp/s1 flow-mod cmd=add,table=0 eth_type=0x800,ip_proto=6,tcp_dst=5001 apply:set_field=ip_dscp:56 goto:2
s1 dpctl unix:/tmp/s1 flow-mod cmd=add,table=0 eth_type=0x800,ip_proto=6,tcp_dst=5002 apply:set_field=ip_dscp:24 goto:2
s1 dpctl unix:/tmp/s1 flow-mod cmd=add,table=0 eth_type=0x800,ip_proto=6,tcp_dst=5003 apply:set_field=ip_dscp:24 goto:1
Port 5001宛はDSCP 56、Port 5002宛はDSCP 24、Port 5003宛はMeter対象でDSCP 24、Port 5004宛はBest Effort
結果
以下の通り、概ね期待通りとなったのだが、
Port 5003宛のトラフィックがBest Effortと同じ位の流量になってしまっている。これは、Markdownされるまでの流量とMarkdownされた後の流量(100kbpsまで)が混じることによって、BestEffortトラフィックと同じくらいになってしまったからであろう。
Meter Tableの統計は以下の通り
s1 dpctl unix:/tmp/s1 stats-meter
SENDING:
stat_req{type="mstats", flags="0x0"{meter_id= ffffffff"}
RECEIVED:
stat_repl{type="mstats", flags="0x0", stats=[{meter= 1"", flow_cnt="1", pkt_in_cnt="6250", byte_in_cnt="3684544", duration_sec="141", duration_nsec="19000", bands=[{pkt_band_cnt="542", byte_band_cnt="319780"}]}, {meter= 2"", flow_cnt="1", pkt_in_cnt="0", byte_in_cnt="0", duration_sec="141", duration_nsec="19000", bands=[{pkt_band_cnt="0", byte_band_cnt="0"}]}]}
s2 dpctl unix:/tmp/s2 stats-meter
SENDING:
stat_req{type="mstats", flags="0x0"{meter_id= ffffffff"}
RECEIVED:
stat_repl{type="mstats", flags="0x0", stats=[{meter= 1"", flow_cnt="1", pkt_in_cnt="0", byte_in_cnt="0", duration_sec="115", duration_nsec="100000", bands=[{pkt_band_cnt="0", byte_band_cnt="0"}]}, {meter= 2"", flow_cnt="1", pkt_in_cnt="539", byte_in_cnt="318010", duration_sec="115", duration_nsec="100000", bands=[{pkt_band_cnt="202", byte_band_cnt="119180"}]}]}
今回はMeter TableでQoSを実践してみたが、DoS/Worm Mitigation StrategyとしてはMeter Tableは有効な方法だと思った。
更にOpenFlowでQoSを実施する方法について考えていきたいと思う。
もっとQoSの理解も深めていきたいところだ。(^^;
Comments
Add Comment