mirror of https://github.com/xSmurf/oz.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
201 lines
4.3 KiB
201 lines
4.3 KiB
package network
|
|
|
|
import(
|
|
//Builtin
|
|
"errors"
|
|
"fmt"
|
|
//"math"
|
|
"net"
|
|
"os"
|
|
//"os/exec"
|
|
"strings"
|
|
|
|
//Internal
|
|
|
|
//External
|
|
"github.com/milosgajdos83/tenus"
|
|
"github.com/op/go-logging"
|
|
)
|
|
|
|
|
|
func BridgeInit(log *logging.Logger) (*HostNetwork, error) {
|
|
htn := new(HostNetwork)
|
|
|
|
if os.Getpid() == 1 {
|
|
panic(errors.New("Cannot use netinit from child."))
|
|
}
|
|
|
|
// Fetch the bridge interface by ifname
|
|
brL, err := net.InterfaceByName(ozDefaultInterfaceBridge)
|
|
if err != nil {
|
|
log.Info("Bridge not found, attempting to create a new one")
|
|
|
|
_, err = createNewBridge(log)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unable to create bridge %+v", err)
|
|
}
|
|
|
|
// Load the new interface
|
|
brL, _ = net.InterfaceByName(ozDefaultInterfaceBridge)
|
|
} else {
|
|
log.Info("Bridge already exists attempting to reuse it")
|
|
}
|
|
|
|
// Lookup the bridge ip addresses
|
|
addrs, _ := brL.Addrs()
|
|
if len(addrs) == 0 {
|
|
return nil, errors.New("Host bridge does not have an IP address assigned")
|
|
}
|
|
|
|
// Try to build the network config from the bridge's address
|
|
addrIndex := -1
|
|
for i, addr := range addrs {
|
|
bIP, brIP, _ := net.ParseCIDR(addr.String())
|
|
|
|
// Discard IPv6 (TODO...)
|
|
if bIP.To4() != nil {
|
|
addrIndex = i
|
|
|
|
bMask := []byte(brIP.Mask)
|
|
|
|
htn.netmask = net.IPv4(bMask[0], bMask[1], bMask[2], bMask[3])
|
|
htn.gateway = net.ParseIP(strings.Split(addr.String(), "/")[0])
|
|
htn.class = strings.Split(addr.String(), "/")[1]
|
|
htn.broadcast = net_getbroadcast(bIP, brIP.Mask)
|
|
|
|
htn.min = inet_aton(bIP)
|
|
htn.min++
|
|
|
|
htn.max = inet_aton(htn.broadcast)
|
|
htn.max--
|
|
|
|
break
|
|
}
|
|
|
|
}
|
|
|
|
if addrIndex < 0 {
|
|
return nil, errors.New("Could not find IPv4 for bridge interface")
|
|
}
|
|
|
|
return htn, nil
|
|
}
|
|
|
|
|
|
// Create a new bridge on the host
|
|
// Assign it an unused range
|
|
// And bring the interface up
|
|
func createNewBridge(log *logging.Logger) (tenus.Bridger, error) {
|
|
if os.Getpid() == 1 {
|
|
panic(errors.New("Cannot use netinit from child."))
|
|
}
|
|
|
|
// Create the bridge
|
|
br, err := tenus.NewBridgeWithName(ozDefaultInterfaceBridge)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Lookup an empty ip range
|
|
brIp, brIpNet, err := findEmptyRange(log)
|
|
if err != nil {
|
|
return nil, errors.New("Could not find an ip range to assign to the bridge")
|
|
}
|
|
|
|
// Setup the bridge's address
|
|
if err := br.SetLinkIp(brIp, brIpNet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Bridge the interface up
|
|
if err = br.SetLinkUp(); err != nil {
|
|
return nil, fmt.Errorf("Unable to bring bridge '%+v' up: %+v", ozDefaultInterfaceBridge, err)
|
|
}
|
|
|
|
return br, nil
|
|
|
|
}
|
|
|
|
|
|
// 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(log *logging.Logger) (net.IP, *net.IPNet, error) {
|
|
type localNet struct {
|
|
min uint64
|
|
max uint64
|
|
}
|
|
|
|
var (
|
|
localNets []localNet
|
|
availableRange string
|
|
)
|
|
|
|
// List all the available interfaces and their addresses
|
|
// and calulate their network's min and max values
|
|
ifs, _ := net.Interfaces()
|
|
for _, netif := range ifs {
|
|
// Disable loopback and our bridge
|
|
if netif.Name == ozDefaultInterfaceBridge ||
|
|
strings.HasPrefix(netif.Name, "lo") {
|
|
continue
|
|
}
|
|
|
|
// Go through each address on the interface
|
|
addrs, _ := netif.Addrs()
|
|
for _, addr := range addrs {
|
|
bIP, brIP, _ := net.ParseCIDR(addr.String())
|
|
|
|
// Discard non IPv4 addresses
|
|
if bIP.To4() != nil {
|
|
min := inet_aton(brIP.IP)
|
|
min++
|
|
|
|
max := inet_aton(net_getbroadcast(bIP, brIP.Mask))
|
|
max--
|
|
|
|
localNets = append(localNets, localNet{min: min, max: max})
|
|
}
|
|
}
|
|
}
|
|
|
|
// Go through the list of private network ranges and
|
|
// look for one in which we cannot find a local network
|
|
for _, ipRange := range privateNetworkRanges {
|
|
bIP, brIP, err := net.ParseCIDR(ipRange)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
bMin := inet_aton(bIP)
|
|
bMax := inet_aton(net_getbroadcast(bIP, brIP.Mask))
|
|
|
|
alreadyUsed := false
|
|
for _, add := range localNets {
|
|
if add.min >= bMin && add.min < bMax &&
|
|
add.max > bMin && add.max <= bMax {
|
|
alreadyUsed = true
|
|
break
|
|
|
|
}
|
|
}
|
|
|
|
// If the range is available, grab a small slice
|
|
if alreadyUsed == false {
|
|
bRange := bMax - bMin
|
|
if bRange > 0xFF {
|
|
bMin = bMax - 0xFE
|
|
}
|
|
|
|
availableRange = inet_ntoa(bMin).String() + "/24"
|
|
|
|
log.Info("Found available range: %+v", availableRange)
|
|
|
|
return net.ParseCIDR(availableRange)
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil, errors.New("Could not find an available range")
|
|
|
|
}
|