@ -11,15 +11,16 @@ import (
"time"
//Internal
"github.com/op/go-logging"
//External
"github.com/op/go-logging"
"github.com/j-keck/arping"
"github.com/milosgajdos83/tenus"
)
func BridgeInit ( bridgeMAC string , nmIgnoreFile string , log * logging . Logger ) ( * HostNetwork , error ) {
if os . Getpid ( ) == 1 {
panic ( errors . New ( "Cannot use neti nit from child.") )
panic ( errors . New ( "Cannot use BridgeI nit from child.") )
}
htn := & HostNetwork {
@ -39,8 +40,9 @@ func BridgeInit(bridgeMAC string, nmIgnoreFile string, log *logging.Logger) (*Ho
return nil , fmt . Errorf ( "Unable to create bridge %+v" , err )
}
}
htn . Interface = br
if err := htn . configureBridgeInterface ( br, log) ; err != nil {
if err := htn . configureBridgeInterface ( log) ; err != nil {
return nil , err
}
@ -56,7 +58,17 @@ func BridgeInit(bridgeMAC string, nmIgnoreFile string, log *logging.Logger) (*Ho
}
return htn , nil
}
func ( htn * HostNetwork ) BridgeReconfigure ( log * logging . Logger ) error {
if os . Getpid ( ) == 1 {
panic ( errors . New ( "Cannot use BridgeReconfigure from child." ) )
}
htn . Gateway = nil
return htn . configureBridgeInterface ( log )
// TODO: Reconfigure guest networks
}
func PrepareSandboxNetwork ( htn * HostNetwork , log * logging . Logger ) ( * SandboxNetwork , error ) {
@ -79,25 +91,25 @@ func PrepareSandboxNetwork(htn *HostNetwork, log *logging.Logger) (*SandboxNetwo
func NetInit ( stn * SandboxNetwork , htn * HostNetwork , childPid int , log * logging . Logger ) error {
if os . Getpid ( ) == 1 {
panic ( errors . New ( "Cannot use netSetup from child.") )
panic ( errors . New ( "Cannot use NetInit from child.") )
}
// Seed random number generator (poorly but we're not doing crypto)
rand . Seed ( time . Now ( ) . Unix ( ) ^ int64 ( ( os . Getpid ( ) + childPid ) ) )
log . Info ( "Configuring host veth pair '%s' with: %s" , stn . VethHost , stn . Ip + "/" + htn . Class )
/ *
// Fetch the bridge from the ifname
br , err := tenus . BridgeFromName ( ozDefaultInterfaceBridge )
if err != nil {
return fmt . Errorf ( "Unable to attach to bridge interface %, %s." , ozDefaultInterfaceBridge , err )
}
* /
// Make sure the bridge is configured and the link is up
// This really shouldn't be needed, but Network-Manager is a PITA
// and even if you actualy ignore the interface there's a race
// between the interface being created and setting it's hwaddr
//if err := htn.configureBridgeInterface( br, log); err != nil {
//if err := htn.configureBridgeInterface( log); err != nil {
// return fmt.Errorf("Unable to reconfigure bridge: %+v", err)
//}
@ -114,7 +126,7 @@ func NetInit(stn *SandboxNetwork, htn *HostNetwork, childPid int, log *logging.L
}
// Add the host side veth to the bridge
if err := br . AddSlaveIfc ( vethIf ) ; err != nil {
if err := htn. Interface . AddSlaveIfc ( vethIf ) ; err != nil {
return fmt . Errorf ( "Unable to add veth pair %s to bridge, %s." , stn . VethHost , err )
}
@ -124,8 +136,7 @@ func NetInit(stn *SandboxNetwork, htn *HostNetwork, childPid int, log *logging.L
}
// Assign the veth path to the namespace
pid := childPid
if err := veth . SetPeerLinkNsPid ( pid ) ; err != nil {
if err := veth . SetPeerLinkNsPid ( childPid ) ; err != nil {
return fmt . Errorf ( "Unable to add veth pair %s to namespace, %s." , stn . VethHost , err )
}
@ -136,14 +147,35 @@ func NetInit(stn *SandboxNetwork, htn *HostNetwork, childPid int, log *logging.L
}
// Set interface address in the namespace
if err := veth . SetPeerLinkNetInNs ( p id, vethGuestIp , vethGuestIpNet , nil ) ; err != nil {
if err := veth . SetPeerLinkNetInNs ( childP id, vethGuestIp , vethGuestIpNet , nil ) ; err != nil {
return fmt . Errorf ( "Unable to parse ip link in namespace, %s." , err )
}
stn . Veth = veth
return nil
}
func NetReconfigure ( stn * SandboxNetwork , htn * HostNetwork , childPid int , log * logging . Logger ) error {
if os . Getpid ( ) == 1 {
panic ( errors . New ( "Cannot use NetInit from child." ) )
}
// Parse the ip/class into the the appropriate formats
vethGuestIp , vethGuestIpNet , err := net . ParseCIDR ( stn . Ip + "/" + htn . Class )
if err != nil {
return fmt . Errorf ( "Unable to parse ip %s, %s." , stn . Ip , err )
}
// Set interface address in the namespace
if err := stn . Veth . SetPeerLinkNetInNs ( childPid , vethGuestIp , vethGuestIpNet , nil ) ; err != nil {
return fmt . Errorf ( "Unable to parse ip link in namespace, %s." , err )
}
return nil
}
func ( stn * SandboxNetwork ) Cleanup ( log * logging . Logger ) {
if os . Getpid ( ) == 1 {
panic ( errors . New ( "Cannot use Cleanup from child." ) )
@ -157,10 +189,10 @@ func (stn *SandboxNetwork) Cleanup(log *logging.Logger) {
tenus . DeleteLink ( stn . VethHost )
}
func ( htn * HostNetwork ) configureBridgeInterface ( br tenus . Bridger , log * logging . Logger ) error {
func ( htn * HostNetwork ) configureBridgeInterface ( log * logging . Logger ) error {
// Set the bridge mac address so it can be fucking ignored by Network-Manager.
if htn . BridgeMAC != "" {
if err := br . SetLinkMacAddress ( htn . BridgeMAC ) ; err != nil {
if err := htn. Interface . SetLinkMacAddress ( htn . BridgeMAC ) ; err != nil {
return fmt . Errorf ( "Unable to set MAC address for gateway" , err )
}
}
@ -176,7 +208,7 @@ func (htn *HostNetwork) configureBridgeInterface(br tenus.Bridger, log *logging.
log . Info ( "Found available range: %+v" , htn . GatewayNet . String ( ) )
}
if err := br . SetLinkIp ( htn . Gateway , htn . GatewayNet ) ; err != nil {
if err := htn. Interface . SetLinkIp ( htn . Gateway , htn . GatewayNet ) ; err != nil {
if os . IsExist ( err ) {
log . Info ( "Bridge IP appears to be already assigned" )
} else {
@ -185,7 +217,7 @@ func (htn *HostNetwork) configureBridgeInterface(br tenus.Bridger, log *logging.
}
// Bridge the interface up
if err := br . SetLinkUp ( ) ; err != nil {
if err := htn. Interface . SetLinkUp ( ) ; err != nil {
return fmt . Errorf ( "Unable to bring bridge '%+v' up: %+v" , ozDefaultInterfaceBridge , err )
}
@ -227,6 +259,10 @@ func (htn *HostNetwork) buildBridgeNetwork(addrs []net.Addr) error {
return nil
}
func FindEmptyRange ( ) ( net . IP , * net . IPNet , error ) {
return findEmptyRange ( )
}
// Look at all the assigned IP address and try to find an available range
// Returns a ip range in the CIDR form if found or an empty string
func findEmptyRange ( ) ( net . IP , * net . IPNet , error ) {
@ -246,7 +282,8 @@ func findEmptyRange() (net.IP, *net.IPNet, error) {
for _ , netif := range ifs {
// Disable loopback and our bridge
if netif . Name == ozDefaultInterfaceBridge ||
strings . HasPrefix ( netif . Name , "lo" ) {
strings . HasPrefix ( netif . Name , "lo" ) ||
strings . HasPrefix ( netif . Name , ozDefaultInterfacePrefix ) {
continue
}
@ -307,3 +344,73 @@ func findEmptyRange() (net.IP, *net.IPNet, error) {
return nil , nil , errors . New ( "Could not find an available range" )
}
// Try to find an unassigned IP address
// Do this by first trying ozMaxRandTries random IPs or, if that fails, sequentially
func getFreshIP ( min , max uint64 , log * logging . Logger ) string {
var newIP string
for i := 0 ; i < ozMaxRandTries ; i ++ {
newIP = getRandIP ( min , max )
if newIP != "" {
break
}
}
if newIP == "" {
log . Notice ( "Random IP lookup failed %d times, reverting to sequential select" , ozMaxRandTries )
newIP = getScanIP ( min , max )
}
return newIP
}
// Generate a random ip and arping it to see if it is available
// Returns the ip on success or an ip string is the ip is already taken
func getRandIP ( min , max uint64 ) string {
if min > max {
return ""
}
dstIP := inet_ntoa ( uint64 ( rand . Int63n ( int64 ( max - min ) ) ) + min )
arping . SetTimeout ( time . Millisecond * 150 )
_ , _ , err := arping . PingOverIfaceByName ( dstIP , ozDefaultInterfaceBridge )
if err == arping . ErrTimeout {
return dstIP . String ( )
} else if err != nil {
return dstIP . String ( )
}
return ""
}
// Go through all possible ips between min and max
// and arping each one until a free one is found
func getScanIP ( min , max uint64 ) string {
if min > max {
return ""
}
for i := min ; i < max ; i ++ {
dstIP := inet_ntoa ( i )
arping . SetTimeout ( time . Millisecond * 150 )
_ , _ , err := arping . PingOverIfaceByName ( dstIP , ozDefaultInterfaceBridge )
if err == arping . ErrTimeout {
return dstIP . String ( )
} else if err != nil {
return dstIP . String ( )
}
}
return ""
}