diff --git a/network/daemon.go b/network/daemon.go index 0603d83..aae8765 100644 --- a/network/daemon.go +++ b/network/daemon.go @@ -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 "" + +} diff --git a/network/network.go b/network/network.go index 63e47bc..f5914bb 100644 --- a/network/network.go +++ b/network/network.go @@ -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) diff --git a/network/ozinit.go b/network/ozinit.go index fcf135d..fc149f0 100644 --- a/network/ozinit.go +++ b/network/ozinit.go @@ -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 } diff --git a/network/proxy.go b/network/proxy.go index 799482c..77fa55f 100644 --- a/network/proxy.go +++ b/network/proxy.go @@ -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" } diff --git a/oz-daemon/daemon.go b/oz-daemon/daemon.go index af79275..7f04134 100644 --- a/oz-daemon/daemon.go +++ b/oz-daemon/daemon.go @@ -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 +} diff --git a/oz-daemon/launch.go b/oz-daemon/launch.go index 4558852..14fa359 100644 --- a/oz-daemon/launch.go +++ b/oz-daemon/launch.go @@ -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 diff --git a/oz/main.go b/oz/main.go index b99d1f5..3107066 100644 --- a/oz/main.go +++ b/oz/main.go @@ -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 {