network-builder-0.1.0: Network/Builder/Namespace.hs
{-#LANGUAGE OverloadedStrings#-}
{-#LANGUAGE TemplateHaskell#-}
{-#LANGUAGE QuasiQuotes#-}
{-#LANGUAGE ExtendedDefaultRules #-}
{-#LANGUAGE RecordWildCards #-}
{-#LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
module Network.Builder.Namespace where
import Prelude hiding (FilePath)
import Shelly hiding (command)
import qualified Data.Text as T
import Data.Monoid
import Control.Monad
import Control.Exception
import Data.Char
import qualified Data.Aeson.TH as A
default (T.Text)
data Bridge = Bridge {
bname :: T.Text
, bip :: T.Text
}
deriving (Show,Eq)
$(A.deriveJSON A.defaultOptions{A.fieldLabelModifier = drop 1,A.constructorTagModifier = map toLower} ''Bridge)
data Veth = Veth {
vname :: T.Text
, vip :: T.Text
}
deriving (Show,Eq)
$(A.deriveJSON A.defaultOptions{A.fieldLabelModifier = drop 1, A.constructorTagModifier = map toLower} ''Veth)
data NetworkNameSpace =
NetworkNameSpace {
nname :: T.Text
, nnss :: Maybe [(Bridge,[(Veth,NetworkNameSpace)])]
}
deriving (Show,Eq)
$(A.deriveJSON A.defaultOptions{A.fieldLabelModifier = drop 1, A.constructorTagModifier = map toLower} ''NetworkNameSpace)
data HostServer =
HostServer {
hnss :: Maybe [(Bridge,[(Veth,NetworkNameSpace)])]
}
deriving (Show,Eq)
$(A.deriveJSON A.defaultOptions{A.fieldLabelModifier = drop 1, A.constructorTagModifier = map toLower} ''HostServer)
class VirtualServer a where
name :: a -> T.Text
nss :: a -> [(Bridge,[(Veth,NetworkNameSpace)])]
isHost :: a -> Bool
runNs :: a -> T.Text -> [T.Text] -> Sh T.Text
instance VirtualServer HostServer where
name _ = "localhost"
nss host = maybe [] id $ hnss host
isHost _ = True
runNs (HostServer _) cmd' args = do
run "sudo" $ [cmd'] ++ args
instance VirtualServer NetworkNameSpace where
name = nname
nss ns = maybe [] id $ nnss ns
isHost _ = False
runNs (NetworkNameSpace name' _) cmd' args = do
run "sudo" $ ["ip", "netns", "exec", name', cmd'] ++ args
delMask :: T.Text -> T.Text
delMask = T.takeWhile (/= '/')
-- sampleNS :: HostServer
-- sampleNS =
-- HostServer{
-- hnss = [
-- (Bridge {
-- bname = "br1"
-- , bip = "192.168.10.1/24"
-- } ,
-- [
-- (Veth {
-- vname = "veth-2"
-- , vip = "192.168.10.2/24"
-- } ,
-- NetworkNameSpace {
-- nname = "server2"
-- , nnss = [
-- (Bridge {
-- bname = "br1"
-- , bip = "192.168.11.1/24"
-- } ,
-- [
-- (Veth {
-- vname = "veth-3"
-- , vip = "192.168.11.4/24"
-- } ,
-- NetworkNameSpace {
-- nname = "server4"
-- , nnss = []
-- })
-- ])
-- ]
-- }),
-- (Veth {
-- vname = "veth-3"
-- , vip = "192.168.10.3/24"
-- } ,
-- NetworkNameSpace {
-- nname = "server3"
-- , nnss = []
-- })
-- ])
-- ]
-- }
createNetworkNameSpaces :: HostServer -> Sh ()
createNetworkNameSpaces host = createNetworkNameSpaces' host host
createNetworkNameSpaces' :: VirtualServer a => HostServer -> a -> Sh ()
createNetworkNameSpaces' host ns = do
forM_ (nss ns) $ \(br,vs) -> do
-- create bridge
_<- runNs ns "ip" ["link", "add", "name", bname br, "type", "bridge"]
_<- runNs ns "ip" ["addr", "add" , bip br, "dev", bname br]
_<- runNs ns "ip" ["link", "set", "dev", bname br, "up"]
-- setup ip masquerade
_<- runNs ns "iptables" ["-t","nat", "-A", "POSTROUTING", "-s", bip br, "!", "-o", bname br, "-j", "MASQUERADE"]
forM_ vs $ \(veth,nss') -> do
-- create namespace
_<- runNs host "ip" ["netns", "add", nname nss']
-- create veth pair
_<- runNs host "ip" ["link", "add", vname veth <> "-0", "type", "veth", "peer", "name", vname veth <> "-1"]
-- assign veth pair for namespace
when (isHost ns == False) $ do
void $ runNs host "ip" ["link", "set", vname veth <> "-0", "netns", name ns]
void $ runNs host "ip" ["link", "set", vname veth <> "-1", "netns", name nss']
-- up veth of upper side sserver
_<- runNs ns "ip" ["link", "set", "dev", vname veth <> "-0", "up"]
-- connect veth to bridge
_<- runNs ns "ip" ["link", "set", "dev", vname veth <> "-0", "master", bname br]
-- setup lo
_<- runNs nss' "ip" ["addr", "add" , "127.0.0.1", "dev", "lo"]
_<- runNs nss' "ip" ["link", "set" , "dev", "lo", "up"]
-- up veth of lower side server
_<- runNs nss' "ip" ["addr", "add" , vip veth, "dev", vname veth <> "-1"]
_<- runNs nss' "ip" ["link", "set" , "dev", vname veth <> "-1", "up"]
-- setup gateway
_<- runNs nss' "ip" ["route", "add", "default", "via",delMask (bip br), "dev", vname veth <> "-1"]
-- create lower network
_<- createNetworkNameSpaces' host nss'
return ()
deleteNetworkNameSpaces :: HostServer -> Sh ()
deleteNetworkNameSpaces host = deleteNetworkNameSpaces' host host
deleteNetworkNameSpaces' :: VirtualServer a => HostServer -> a -> Sh ()
deleteNetworkNameSpaces' host ns = do
forM_ (nss ns) $ \(br,vs) -> do
forM_ vs $ \(veth,nss') -> do
_<- deleteNetworkNameSpaces' host nss'
ignore $ runNs ns "ip" ["link", "set", "dev", vname veth <> "-0", "down"]
ignore $ runNs ns "ip" ["link", "set", "dev", vname veth <> "-0", "nomaster"]
ignore $ runNs host "ip" ["netns", "del", nname nss']
return ()
ignore $ runNs ns "iptables" ["-t","nat", "-D", "POSTROUTING", "-s", bip br, "!", "-o", bname br, "-j", "MASQUERADE"]
ignore $ runNs ns "ip" ["link", "delete", bname br, "type", "bridge"]
return ()
where
ignore act = (void $ act) `catch_sh` \(err :: SomeException) -> liftIO $ putStrLn $ show err