nettle-frp-0.1: src/Examples/AFRPControl/LearningNetwork.hs
{-# LANGUAGE Arrows, TransformListComp, RecordWildCards #-}
module Main where
import Nettle.FRPControl.NettleSF
import Nettle.FRPControl.NetInfo
import Nettle.IPv4.IPPacket
import Nettle.IPv4.IPAddress
import Nettle.OpenFlow.Match
import Nettle.OpenFlow.Packet
import Nettle.OpenFlow.Action
import Nettle.OpenFlow.Statistics
import Nettle.OpenFlow.Switch
import Nettle.Ethernet.EthernetFrame
import Data.Maybe (fromMaybe)
import qualified Data.Map as Map
{-
A learning switch, which installs flow table entries at the switches to
to avoid switch-to-controller messages.
-}
-- Warning: Only works for loop-free networks; On loopy networks, a single packet will loop indefinitely
-- through the network.
main :: IO ()
main = simpleNettleDriver 2525 (controller)
monitorPolicy = NetworkMonitorPolicy { switchFeaturesRefreshPeriod = 20, -- seconds
portStatisticsRefreshPeriod = 20 -- seconds
}
controller :: SF (Event (SwitchID, SwitchMessage)) (Event SwitchCommand, Event String)
controller = proc i -> do
netInfoCmdE <- networkInfoRequester monitorPolicy -< i
initCmd <- switchInitializer' -< i
hostDirMap <- hostDirectionsSF -< i
let tableMods = mapFilterE (updateTableCommands hostDirMap) (packetInE i)
returnA -< (mergeEventsBy (<+>) [netInfoCmdE, initCmd, tableMods], noEvent)
updateTableCommands hostDirMap (sw, pktInfo@PacketInfo {..}) =
case packetInFrame pktInfo of
Left msg -> Nothing
Right ethFrame ->
let pred = fromMatch (frameToExactMatch receivedOnPort ethFrame)
s = sourceAddress ethFrame
r = destAddress ethFrame
cmd = do ps <- Map.lookup (sw,s) hostDirMap
pr <- Map.lookup (sw,r) hostDirMap
case bufferID of
Nothing -> return (addFlowRule (((pred ==> sendOnPort pr) `expiringAfter` 240) `withPriority` 1) sw
<+> sendPacket sw (receivedPacketOut pktInfo flood)
)
Just bufid -> return (addFlowRule' (((pred ==> sendOnPort pr) `expiringAfter` 240) `withPriority` 1) bufid sw)
in case cmd of
Nothing -> Just (sendPacket sw (receivedPacketOut pktInfo flood))
Just cmd' -> Just cmd'
{-
updateTableCommands hostDirMap (sw,pktInfo) =
case packetInFrame pktInfo of
Left msg -> Nothing
Right ethFrame ->
let s = sourceAddress ethFrame
r = destAddress ethFrame
in do ps <- Map.lookup (sw,s) hostDirMap
pr <- Map.lookup (sw,r) hostDirMap
return (deleteFlowRules (ethSourceDestAre s r <|> ethSourceDestAre r s) sw <+>
addFlowRules [(1, flowFromTo s ps r pr `expiringAfter` 240) ,
(1, flowFromTo r pr s ps `expiringAfter` 240)] sw)
where flowFromTo s ps r pr = inPortIs ps <&> ethSourceDestAre s r ==> sendOnPort pr
-}
switchInitializer' :: SF (Event (SwitchID, SwitchMessage)) (Event SwitchCommand)
switchInitializer' = proc i -> do
returnA -< liftE (\(swid,_) -> clearTables swid <+>
addFlowRule ((arp ==> flood) `withPriority` 1) swid) (arrivalE i)
packetSender :: SF (Event (SwitchID, SwitchMessage)) (Event SwitchCommand)
packetSender = proc i -> do
returnA -< liftE (\(switchID, pktIn) -> sendPacket switchID (receivedPacketOut pktIn flood)) (packetInE i)