github.com/psiphon-labs/goarista@v0.0.0-20160825065156-d002785f4c67/netns/netns.go (about) 1 // Copyright (C) 2016 Arista Networks, Inc. 2 // Use of this source code is governed by the Apache License 2.0 3 // that can be found in the COPYING file. 4 5 // Package netns provides a utility function that allows a user to 6 // perform actions in a different network namespace 7 package netns 8 9 import ( 10 "fmt" 11 "os" 12 "runtime" 13 ) 14 15 const ( 16 netNsRunDir = "/var/run/netns/" 17 selfNsFile = "/proc/self/ns/net" 18 ) 19 20 // Callback is a function that gets called in a given network namespace. 21 // The user needs to check any errors from any calls inside this function. 22 type Callback func() 23 24 // File descriptor interface so we can mock for testing 25 type handle interface { 26 close() error 27 fd() int 28 } 29 30 // The file descriptor associated with a network namespace 31 type nsHandle int 32 33 // setNsByName wraps setNs, allowing specification of the network namespace by name. 34 // It returns the file descriptor mapped to the given network namespace. 35 func setNsByName(nsName string) error { 36 netPath := netNsRunDir + nsName 37 handle, err := getNs(netPath) 38 if err != nil { 39 return fmt.Errorf("Failed to getNs: %s", err) 40 } 41 err = setNs(handle) 42 handle.close() 43 if err != nil { 44 return fmt.Errorf("Failed to setNs: %s", err) 45 } 46 return nil 47 } 48 49 // Do takes a function which it will call in the network namespace specified by nsName. 50 // The goroutine that calls this will lock itself to its current OS thread, hop 51 // namespaces, call the given function, hop back to its original namespace, and then 52 // unlock itself from its current OS thread. 53 // Do returns an error if an error occurs at any point besides in the invocation of 54 // the given function. 55 // The caller should check both the error of Do and any errors from the given function call. 56 func Do(nsName string, cb Callback) error { 57 // If destNS is empty, the function is called in the caller's namespace 58 if nsName == "" { 59 cb() 60 return nil 61 } 62 63 // Get the file descriptor to the current namespace 64 currNsFd, err := getNs(selfNsFile) 65 if os.IsNotExist(err) { 66 return fmt.Errorf("File descriptor to current namespace does not exist: %s", err) 67 } else if err != nil { 68 return fmt.Errorf("Failed to open %s: %s", selfNsFile, err) 69 } 70 defer currNsFd.close() 71 72 runtime.LockOSThread() 73 defer runtime.UnlockOSThread() 74 75 // Jump to the new network namespace 76 if err := setNsByName(nsName); err != nil { 77 return fmt.Errorf("Failed to set the namespace to %s: %s", nsName, err) 78 } 79 80 // Call the given function 81 cb() 82 83 // Come back to the original namespace 84 if err = setNs(currNsFd); err != nil { 85 return fmt.Errorf("Failed to return to the original namespace: %s", err) 86 } 87 88 return nil 89 }