Adding reloading of profiles on SIGHUP, cleanup network code preparation for reconfigure routines

master
xSmurf 9 years ago
parent b72d77b891
commit 93ef716d94

@ -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 netinit from child."))
panic(errors.New("Cannot use BridgeInit 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(pid, vethGuestIp, vethGuestIpNet, nil); err != nil {
if err := veth.SetPeerLinkNetInNs(childPid, 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 ""
}

@ -8,6 +8,7 @@ import (
"strings"
"github.com/op/go-logging"
"github.com/milosgajdos83/tenus"
)
const (
@ -20,13 +21,15 @@ const (
type NetType string
const (
TYPE_HOST NetType = "host"
TYPE_EMPTY NetType = "empty"
const(
TYPE_HOST NetType = "host"
TYPE_EMPTY NetType = "empty"
TYPE_BRIDGE NetType = "bridge"
)
type HostNetwork struct {
// Bridge interface
Interface tenus.Bridger
// Gateway ip (bridge ip)
Gateway net.IP
// Gateway ip (bridge ip)
@ -43,11 +46,13 @@ type HostNetwork struct {
Max uint64
// Bridge interface MAC Address
BridgeMAC string
//
// The type of network configuration
Nettype NetType
}
type SandboxNetwork struct {
// veth interface is present
Interface tenus.Linker
// Name of the veth in the host
VethHost string
// Temporary name of the guest' veth in the host
@ -58,8 +63,10 @@ type SandboxNetwork struct {
Gateway net.IP
// IP class (ie: /24)
Class string
//
// The type of network configuration
Nettype NetType
// Host side virtual interface
Veth tenus.Vether
}
var privateNetworkRanges []string
@ -100,6 +107,9 @@ func NetPrint(log *logging.Logger) {
log.Info(strHeader)
for _, netif := range ifs {
if strings.HasPrefix(netif.Name, ozDefaultInterfacePrefix) {
continue
}
addrs, _ := netif.Addrs()
strLine = fmt.Sprintf("%-15.14s", netif.Name)

@ -4,15 +4,12 @@ import (
//Builtin
"errors"
"fmt"
"math/rand"
"os"
"time"
// Internal
"github.com/op/go-logging"
//"github.com/op/go-logging"
//External
"github.com/j-keck/arping"
"github.com/milosgajdos83/tenus"
)
@ -21,7 +18,7 @@ import (
// and the veth interface if requested
func NetSetup(stn *SandboxNetwork) error {
if os.Getpid() != 1 {
panic(errors.New("Cannot use NetSetip from parent."))
panic(errors.New("Cannot use NetSetup from parent."))
}
if err := setupLoopback(stn); err != nil {
@ -38,6 +35,19 @@ func NetSetup(stn *SandboxNetwork) error {
return nil
}
func (stn *SandboxNetwork) NetReconfigure() error {
if os.Getpid() != 1 {
panic(errors.New("Cannot use NetReconfigure from parent."))
}
// Set the link's default gateway
if err := stn.Interface.SetLinkDefaultGw(&stn.Gateway); err != nil {
return fmt.Errorf("Unable to set default route %s.", err)
}
return nil
}
func setupLoopback(stn *SandboxNetwork) error {
// Bring loopback interface up
lo, err := tenus.NewLinkFrom("lo")
@ -87,75 +97,7 @@ func setupVEth(stn *SandboxNetwork) error {
return fmt.Errorf("Unable to set default route %s.", 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 ""
stn.Interface = ifc
return nil
}

@ -1,6 +1,6 @@
package network
import (
import(
//Builtin
"fmt"
"io"
@ -16,16 +16,16 @@ import (
type ProxyType string
const (
const(
PROXY_CLIENT ProxyType = "client"
PROXY_SERVER ProxyType = "server"
)
type ProtoType string
const (
PROTO_TCP ProtoType = "tcp"
PROTO_UDP ProtoType = "udp"
const(
PROTO_TCP ProtoType = "tcp"
PROTO_UDP ProtoType = "udp"
PROTO_SOCKET ProtoType = "socket"
)
@ -146,7 +146,7 @@ func nsSocketListener(fd uintptr, proto ProtoType, lAddr string) (net.Listener,
/**
* Connect/Server
**/
func proxyServerConn(pid int, conn *net.Conn, proto ProtoType, rAddr string, log *logging.Logger, ready sync.WaitGroup) error {
func proxyServerConn(pid int, conn *net.Conn, proto ProtoType, rAddr string, log *logging.Logger, ready sync.WaitGroup) (error) {
rConn, err := socketConnect(pid, proto, rAddr)
if err != nil {
log.Error("Socket: %+v.", err)
@ -159,7 +159,7 @@ func proxyServerConn(pid int, conn *net.Conn, proto ProtoType, rAddr string, log
return nil
}
func newProxyServer(pid int, proto ProtoType, dest string, port int, log *logging.Logger, ready sync.WaitGroup) error {
func newProxyServer(pid int, proto ProtoType, dest string, port int, log *logging.Logger, ready sync.WaitGroup) (error) {
if dest == "" {
dest = "127.0.0.1"
}

@ -2,6 +2,9 @@ package daemon
import (
"fmt"
"os"
"os/signal"
"path"
"strings"
"syscall"
@ -10,8 +13,6 @@ import (
"github.com/subgraph/oz/network"
"github.com/op/go-logging"
"os"
"path"
)
type daemonState struct {
@ -39,31 +40,27 @@ func Main() {
d.handleLogs,
)
if err != nil {
d.log.Warning("Error running server: %v", err)
d.log.Error("Error running server: %v", err)
}
}
func initialize() *daemonState {
sigs := make(chan os.Signal)
signal.Notify(sigs, syscall.SIGHUP, syscall.SIGUSR1)
d := &daemonState{}
d.initializeLogging()
var config *oz.Config
config, err := oz.LoadConfig(oz.DefaultConfigPath)
config, err := d.loadConfig()
if err != nil {
if os.IsNotExist(err) {
d.log.Info("Configuration file (%s) is missing, using defaults.", oz.DefaultConfigPath)
config = oz.NewDefaultConfig()
} else {
d.log.Error("Could not load configuration: %s", oz.DefaultConfigPath, err)
os.Exit(1)
}
d.log.Error("Could not load configuration: %s", oz.DefaultConfigPath, err)
os.Exit(1)
}
d.log.Info("Oz Global Config: %+v", config)
d.config = config
ps, err := oz.LoadProfiles(config.ProfileDir)
ps, err := d.loadProfiles(d.config.ProfileDir)
if err != nil {
d.log.Fatalf("Failed to load profiles: %v", err)
os.Exit(1)
}
d.Debug("%d profiles loaded", len(ps))
d.profiles = ps
oz.ReapChildProcs(d.log, d.handleChildExit)
d.nextSboxId = 1
@ -91,9 +88,55 @@ func initialize() *daemonState {
d.log.Fatalf("Failed to create sockets directory: %v", err)
}
go d.processSignals(sigs)
return d
}
func (d *daemonState) loadConfig() (*oz.Config, error) {
config, err := oz.LoadConfig(oz.DefaultConfigPath)
if err != nil {
if os.IsNotExist(err) {
d.log.Info("Configuration file (%s) is missing, using defaults.", oz.DefaultConfigPath)
config = oz.NewDefaultConfig()
} else {
return nil, err
}
}
d.log.Info("Oz Global Config: %+v", config)
return config, nil
}
func (d *daemonState) loadProfiles(profileDir string) (oz.Profiles, error) {
ps, err := oz.LoadProfiles(profileDir)
if err != nil {
return nil, err
}
d.Debug("%d profiles loaded", len(ps))
return ps, nil
}
func (d *daemonState) processSignals(c <-chan os.Signal) {
for {
sig := <-c
switch sig {
case syscall.SIGHUP:
d.log.Notice("Received HUP signal, reloading profiles.")
ps, err := d.loadProfiles(d.config.ProfileDir)
if err != nil {
d.log.Error("Failed to reload profiles: %v", err)
return
}
d.profiles = ps
case syscall.SIGUSR1:
d.handleNetworkReconfigure()
}
}
}
func (d *daemonState) handleChildExit(pid int, wstatus syscall.WaitStatus) {
d.Debug("Child process pid=%d exited with status %d", pid, wstatus.ExitStatus())
@ -111,6 +154,7 @@ func runServer(log *logging.Logger, args ...interface{}) error {
if err != nil {
return err
}
return s.Run()
}
@ -288,3 +332,37 @@ func (d *daemonState) handleLogs(logs *LogsMsg, msg *ipc.Message) error {
msg.Respond(&OkMsg{})
return nil
}
func (d *daemonState) handleNetworkReconfigure() {
brIP, brNet, err := network.FindEmptyRange()
if err != nil {
return
}
if brIP.Equal(d.network.Gateway) {
d.log.Notice("Range is still available, not reconfiguring.")
return
}
d.log.Notice("Network has changed, reconfiguring with %s %s", brIP.String(), brNet.String())
if err := d.network.BridgeReconfigure(d.log); err != nil {
d.log.Error("Unable to reconfigure bridge network: %v", err)
return
}
/*
for _, sbox := range d.sandboxes {
if sbox.profile.Networking.Nettype == network.TYPE_BRIDGE {
sbox.network, err := network.PrepareSandboxNetwork(d.network, d.log)
if err != nil {
d.log.Error("Unable to prepare reconfigure of sandbox `%s` networking: %v", sbox.profile.Name, err)
continue
}
if err := d.network.NetReconfigure(d.network, sbox.network, sbox.Pid, d.log); err != nil {
d.log.Error("Unable to reconfigure sandbox `%s` networking: %v", sbox.profile.Name, err)
continue
}
// TODO: Reconfigure default gateway inside sandbox
}
}
*/
return
}

@ -188,6 +188,14 @@ func (d *daemonState) launch(p *oz.Profile, msg *LaunchMsg, uid, gid uint32, log
}()
}
cmd2 := exec.Command("/bin/mount")
stdout, err := cmd2.Output()
if err != nil {
println(err.Error())
}
log.Debug(string(stdout))
d.nextSboxId += 1
d.sandboxes = append(d.sandboxes, sbox)
return sbox, nil

@ -207,6 +207,7 @@ func handleKill(c *cli.Context) {
fmt.Fprintf(os.Stderr, "Kill command failed:", err)
os.Exit(1)
}
return
}
id, err := strconv.Atoi(c.Args()[0])
if err != nil {

Loading…
Cancel
Save