Networking import and addition to daemon initialization...

master
xSmurf 9 years ago
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 ""
}

@ -2,13 +2,15 @@ package daemon
import (
"fmt"
"os/user"
"syscall"
"github.com/op/go-logging"
"github.com/subgraph/oz"
"github.com/subgraph/oz/fs"
"github.com/subgraph/oz/ipc"
"os/user"
"syscall"
"github.com/subgraph/oz/network"
"github.com/op/go-logging"
)
type daemonState struct {
@ -20,6 +22,7 @@ type daemonState struct {
nextDisplay int
memBackend *logging.ChannelMemoryBackend
backends []logging.Backend
network *network.HostNetwork
}
func Main() {
@ -30,6 +33,7 @@ func Main() {
d.handlePing,
d.handleListProfiles,
d.handleLaunch,
d.handleInitBridgeNetwork,
d.handleListSandboxes,
d.handleClean,
d.handleLogs,
@ -59,6 +63,15 @@ func initialize() *daemonState {
oz.ReapChildProcs(d.log, d.handleChildExit)
d.nextSboxId = 1
d.nextDisplay = 100
d.log.Info("Initializing bridge networking")
htn, err := network.BridgeInit(d.log)
if err != nil {
d.log.Fatalf("Failed to initialize bridge networking: %+v", err)
}
d.network = htn
return d
}
@ -112,6 +125,12 @@ func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error {
return m.Respond(&OkMsg{})
}
func (d *daemonState) handleInitBridgeNetwork(msg *InitNetworkMsg, m *ipc.Message) error {
d.Debug("Network bridge init message received: %+v", msg)
return m.Respond(&OkMsg{})
}
func (d *daemonState) getProfileByIdxOrName(index int, name string) (*oz.Profile, error) {
if len(name) == 0 {
if index < 1 || index > len(d.profiles) {

@ -6,6 +6,7 @@ import (
"github.com/subgraph/oz"
"github.com/subgraph/oz/fs"
"github.com/subgraph/oz/xpra"
"github.com/subgraph/oz/network"
"io"
"os"
"os/exec"
@ -29,6 +30,7 @@ type Sandbox struct {
addr string
xpra *xpra.Xpra
ready sync.WaitGroup
network *network.SandboxNetwork
}
/*

@ -35,6 +35,11 @@ type LaunchMsg struct {
Name string
}
type InitNetworkMsg struct {
Index int "NetworkBridge"
Name string
}
type ListSandboxesMsg struct {
_ string "ListSandboxes"
}

Loading…
Cancel
Save