mirror of https://github.com/xSmurf/oz.git
parent
33484fd11b
commit
3b6169d8df
@ -0,0 +1,200 @@
|
||||
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")
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
//Builtin
|
||||
"net"
|
||||
"strings"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
ozDefaultInterfaceBridgeBase = "oz"
|
||||
ozDefaultInterfaceBridge = ozDefaultInterfaceBridgeBase + "0"
|
||||
ozDefaultInterfacePrefix = "veth"
|
||||
ozDefaultInterfaceInternal = "eth0"
|
||||
ozMaxRandTries = 3
|
||||
)
|
||||
|
||||
type HostNetwork struct {
|
||||
// Host bridge IP address
|
||||
hostip net.IP
|
||||
// Gateway ip (bridge ip)
|
||||
gateway net.IP
|
||||
// Bridge netmask
|
||||
netmask net.IP
|
||||
// Broadcast ip
|
||||
broadcast net.IP
|
||||
// IP class (ie: /24)
|
||||
class string
|
||||
// Minimum longip available ip
|
||||
min uint64
|
||||
// Maximum longip available ip
|
||||
max uint64
|
||||
|
||||
}
|
||||
|
||||
type SandboxNetwork struct {
|
||||
// Name of the veth in the host
|
||||
vethHost string
|
||||
// Temporary name of the guest' veth in the host
|
||||
vethGuest string
|
||||
// Guest ip address
|
||||
ip string
|
||||
|
||||
host *HostNetwork
|
||||
}
|
||||
|
||||
var privateNetworkRanges []string
|
||||
|
||||
func init() {
|
||||
privateNetworkRanges = []string{
|
||||
// RFC1918 Private ranges
|
||||
"10.0.0.0/8",
|
||||
"172.16.0.0/12",
|
||||
"192.168.0.0/16",
|
||||
// Documentation / Testnet 2
|
||||
"192.51.100.0/24",
|
||||
// Documentation/ Testnet
|
||||
"192.0.2.0/24",
|
||||
// Inter-network communication
|
||||
"192.18.0.0/15",
|
||||
// Documentation / Testnet 3
|
||||
"203.0.113.0/24",
|
||||
// Carrier grade NAT
|
||||
"100.64.0.0/10",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Convert longip to net.IP
|
||||
func inet_ntoa(ipnr uint64) net.IP {
|
||||
var bytes [4]byte
|
||||
bytes[0] = byte(ipnr & 0xFF)
|
||||
bytes[1] = byte((ipnr >> 8) & 0xFF)
|
||||
bytes[2] = byte((ipnr >> 16) & 0xFF)
|
||||
bytes[3] = byte((ipnr >> 24) & 0xFF)
|
||||
|
||||
return net.IPv4(bytes[3], bytes[2], bytes[1], bytes[0])
|
||||
}
|
||||
|
||||
// Convert net.IP to longip
|
||||
func inet_aton(ipnr net.IP) uint64 {
|
||||
bits := strings.Split(ipnr.String(), ".")
|
||||
|
||||
b0, _ := strconv.Atoi(bits[0])
|
||||
b1, _ := strconv.Atoi(bits[1])
|
||||
b2, _ := strconv.Atoi(bits[2])
|
||||
b3, _ := strconv.Atoi(bits[3])
|
||||
|
||||
var sum uint64
|
||||
|
||||
sum += uint64(b0) << 24
|
||||
sum += uint64(b1) << 16
|
||||
sum += uint64(b2) << 8
|
||||
sum += uint64(b3)
|
||||
|
||||
return sum
|
||||
}
|
||||
|
||||
func net_getbroadcast(bIP net.IP, ipMask net.IPMask) net.IP {
|
||||
bMask := []byte(ipMask)
|
||||
byteIP := bIP.To4()
|
||||
|
||||
return net.IPv4(
|
||||
byteIP[0]|(bMask[0]^0xFF),
|
||||
byteIP[1]|(bMask[1]^0xFF),
|
||||
byteIP[2]|(bMask[2]^0xFF),
|
||||
byteIP[3]|(bMask[3]^0xFF))
|
||||
|
||||
}
|
@ -0,0 +1,216 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
//Builtin
|
||||
"errors"
|
||||
"fmt"
|
||||
//"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
//External
|
||||
"github.com/j-keck/arping"
|
||||
"github.com/milosgajdos83/tenus"
|
||||
"github.com/op/go-logging"
|
||||
)
|
||||
|
||||
func NetInit(log *logging.Logger, htn *HostNetwork, childPid int) (*SandboxNetwork, error) {
|
||||
if os.Getpid() == 1 {
|
||||
panic(errors.New("Cannot use netSetup from child."))
|
||||
}
|
||||
|
||||
stn := new(SandboxNetwork)
|
||||
|
||||
stn.vethHost = tenus.MakeNetInterfaceName(ozDefaultInterfacePrefix)
|
||||
stn.vethGuest = stn.vethHost + "1"
|
||||
|
||||
// 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", stn.vethHost)
|
||||
|
||||
// Fetch the bridge from the ifname
|
||||
br, err := tenus.BridgeFromName(ozDefaultInterfaceBridge)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to attach to bridge interface %, %s.", ozDefaultInterfaceBridge, err)
|
||||
}
|
||||
|
||||
// Create the veth pair
|
||||
veth, err := tenus.NewVethPairWithOptions(stn.vethHost, tenus.VethOptions{PeerName: stn.vethGuest})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to create veth pair %s, %s.", stn.vethHost, err)
|
||||
}
|
||||
|
||||
// Fetch the newly created hostside veth
|
||||
vethIf, err := net.InterfaceByName(stn.vethHost)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to fetch veth pair %s, %s.", stn.vethHost, err)
|
||||
}
|
||||
|
||||
// Add the host side veth to the bridge
|
||||
if err = br.AddSlaveIfc(vethIf); err != nil {
|
||||
return nil, fmt.Errorf("Unable to add veth pair %s to bridge, %s.", stn.vethHost, err)
|
||||
}
|
||||
|
||||
// Bring the host side veth interface up
|
||||
if err = veth.SetLinkUp(); err != nil {
|
||||
return nil, fmt.Errorf("Unable to bring veth pair %s up, %s.", stn.vethHost, err)
|
||||
}
|
||||
|
||||
// Assign the veth path to the namespace
|
||||
pid := childPid
|
||||
if err := veth.SetPeerLinkNsPid(pid); err != nil {
|
||||
return nil, fmt.Errorf("Unable to add veth pair %s to namespace, %s.", stn.vethHost, err)
|
||||
}
|
||||
|
||||
// Allocate a new IP address
|
||||
stn.ip = getFreshIP(htn.min, htn.max, log)
|
||||
if stn.ip == "" {
|
||||
return nil, errors.New("Unable to acquire random IP")
|
||||
}
|
||||
|
||||
// Parse the ip/class into the the appropriate formats
|
||||
vethGuestIp, vethGuestIpNet, err := net.ParseCIDR(stn.ip + "/" + htn.class)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to parse ip %s, %s.", stn.ip, err)
|
||||
}
|
||||
|
||||
// Set interface address in the namespace
|
||||
if err := veth.SetPeerLinkNetInNs(pid, vethGuestIp, vethGuestIpNet, nil); err != nil {
|
||||
return nil, fmt.Errorf("Unable to parse ip link in namespace, %s.", err)
|
||||
}
|
||||
|
||||
return stn, nil
|
||||
|
||||
}
|
||||
|
||||
// Setup the networking inside the child
|
||||
// Namely setup the loopback interface
|
||||
// and the veth interface if requested
|
||||
func NetSetup(stn *SandboxNetwork, htn *HostNetwork) error {
|
||||
if os.Getpid() != 1 {
|
||||
panic(errors.New("Cannot use netChildSetup from child."))
|
||||
}
|
||||
|
||||
// Bring loopback interface up
|
||||
lo, err := tenus.NewLinkFrom("lo")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to fetch loopback interface, %s.", err)
|
||||
}
|
||||
|
||||
// Bring the link up
|
||||
err = lo.SetLinkUp()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to bring loopback interface up, %s.", err)
|
||||
}
|
||||
|
||||
// If required configure veth
|
||||
if stn.vethGuest != "" {
|
||||
ifc, err := tenus.NewLinkFrom(stn.vethGuest)
|
||||
if err == nil {
|
||||
// Bring the link down to prepare for renaming
|
||||
if err = ifc.SetLinkDown(); err != nil {
|
||||
return fmt.Errorf("Unable to bring interface %s down, %s.", stn.vethGuest, err)
|
||||
}
|
||||
|
||||
// Rename the interface to a standard eth0 (not really a necessity)
|
||||
if err = tenus.RenameInterface(stn.vethGuest, ozDefaultInterfaceInternal); err != nil {
|
||||
return fmt.Errorf("Unable to rename interface %s, %s.", stn.vethGuest, err)
|
||||
}
|
||||
|
||||
// Refetch the interface again as it has changed
|
||||
ifc, err = tenus.NewLinkFrom(ozDefaultInterfaceInternal)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to fetch interface %s, %s.", ozDefaultInterfaceInternal, err)
|
||||
}
|
||||
|
||||
// Bring the link back up
|
||||
if err = ifc.SetLinkUp(); err != nil {
|
||||
return fmt.Errorf("Unable to bring interface %s up, %s.", ozDefaultInterfaceInternal, err)
|
||||
}
|
||||
|
||||
// Set the link's default gateway
|
||||
if err = ifc.SetLinkDefaultGw(&htn.gateway); err != nil {
|
||||
return fmt.Errorf("Unable to set default route %s.", err)
|
||||
}
|
||||
|
||||
} else {
|
||||
return fmt.Errorf("Unable to fetch inteface %s, %s.", stn.vethGuest, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// Try to find an unassigned IP address
|
||||
// Do this by first trying two 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 ""
|
||||
|
||||
}
|
Loading…
Reference in new issue