go.ligato.io/vpp-agent/v3@v3.5.0/plugins/linux/nsplugin/linuxcalls/namespace_linuxcalls.go (about) 1 // Copyright (c) 2018 Cisco and/or its affiliates. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at: 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build !windows && !darwin 16 17 package linuxcalls 18 19 import ( 20 "os" 21 "path" 22 "strconv" 23 "syscall" 24 25 "github.com/pkg/errors" 26 "github.com/vishvananda/netns" 27 28 "go.ligato.io/cn-infra/v2/logging" 29 ) 30 31 const ( 32 // Network namespace mount directory. 33 netNsMountDir = "/var/run/netns" 34 ) 35 36 // CreateNamedNetNs creates a new named Linux network namespace. 37 // It does exactly the same thing as the command "ip netns add NAMESPACE". 38 func (nh *namedNetNsHandler) CreateNamedNetNs(ctx NamespaceMgmtCtx, nsName string) (netns.NsHandle, error) { 39 // Lock the OS Thread so we don't accidentally switch namespaces. 40 ctx.LockOSThread() 41 defer ctx.UnlockOSThread() 42 43 // Save the current network namespace. 44 origns, err := nh.sysHandler.GetCurrentNamespace() 45 if err != nil { 46 return netns.None(), errors.Errorf("failed to get original namespace: %v", err) 47 } 48 defer origns.Close() 49 50 // Create directory for namespace mounts. 51 err = nh.sysHandler.MkDirAll(netNsMountDir, 0755) 52 if err != nil { 53 return netns.None(), errors.Errorf("failed to create directory for namespace mounts: %v", err) 54 } 55 56 /* Make it possible for network namespace mounts to propagate between 57 mount namespaces. This makes it likely that unmounting a network 58 namespace file in one namespace will unmount the network namespace 59 file in all namespaces allowing the network namespace to be freed 60 sooner. 61 */ 62 mountedNetnsDir := false 63 for { 64 err = nh.sysHandler.Mount("", netNsMountDir, "none", syscall.MS_SHARED|syscall.MS_REC, "") 65 if err == nil { 66 break 67 } 68 if e, ok := err.(syscall.Errno); !ok || e != syscall.EINVAL || mountedNetnsDir { 69 return netns.None(), errors.Errorf("%s mount --make-shared failed: %v", nsName, err) 70 } 71 /* Upgrade netNsMountDir to a mount point */ 72 err = nh.sysHandler.Mount(netNsMountDir, netNsMountDir, "none", syscall.MS_BIND, "") 73 if err != nil { 74 return netns.None(), errors.Errorf("%s mount --bind failed: %v", nsName, err) 75 } 76 mountedNetnsDir = true 77 } 78 79 // Create file path for the mount. 80 netnsMountFile := path.Join(netNsMountDir, nsName) 81 file, err := nh.sysHandler.OpenFile(netnsMountFile, os.O_RDONLY|os.O_CREATE|os.O_EXCL, 0444) 82 if err != nil { 83 return netns.None(), errors.Errorf("failed to create destination path for the namespace %s mount: %v", 84 nsName, err) 85 } 86 file.Close() 87 88 // Create and switch to a new namespace. 89 newNsHandle, err := nh.sysHandler.NewNetworkNamespace() 90 if err != nil { 91 nh.log.WithFields(logging.Fields{"namespace": nsName}). 92 Error("failed to create namespace") 93 return netns.None(), errors.Errorf("failed to create namespace %s: %v", nsName, err) 94 } 95 err = nh.sysHandler.SetNamespace(newNsHandle) 96 if err != nil { 97 newNsHandle.Close() 98 nh.log.WithFields(logging.Fields{"namespace": nsName}). 99 Error("failed to set namespace") 100 return netns.None(), errors.Errorf("failed to set namespace %s: %v", nsName, err) 101 } 102 103 // Create a bind-mount for the namespace. 104 tid := syscall.Gettid() 105 err = nh.sysHandler.Mount("/proc/self/task/"+strconv.Itoa(tid)+"/ns/net", netnsMountFile, "none", syscall.MS_BIND, "") 106 if err != nil { 107 newNsHandle.Close() 108 return netns.None(), errors.Errorf("failed to create namespace %s bind-mount: %v", nsName, err) 109 } 110 111 // Switch back to the original namespace. 112 err = nh.sysHandler.SetNamespace(origns) 113 if err != nil { 114 newNsHandle.Close() 115 nh.log.WithFields(logging.Fields{"namespace": nsName}). 116 Error("failed to set namespace") 117 return netns.None(), errors.Errorf("failed to set namespace %s: %v", nsName, err) 118 } 119 120 return newNsHandle, nil 121 } 122 123 // DeleteNamedNetNs deletes an existing named Linux network namespace. 124 // It does exactly the same thing as the command "ip netns del NAMESPACE". 125 func (nh *namedNetNsHandler) DeleteNamedNetNs(nsName string) error { 126 // Unmount the namespace. 127 netnsMountFile := path.Join(netNsMountDir, nsName) 128 err := nh.sysHandler.Unmount(netnsMountFile, syscall.MNT_DETACH) 129 if err != nil { 130 return errors.Errorf("failed to unmount namespace %s: %v", nsName, err) 131 } 132 133 // Remove file path used for the mount. 134 err = nh.sysHandler.Remove(netnsMountFile) 135 if err != nil { 136 return errors.Errorf("failed to remove namespace %s: %v", nsName, err) 137 } 138 139 return err 140 } 141 142 // NamedNetNsExists checks whether named namespace exists. 143 func (nh *namedNetNsHandler) NamedNetNsExists(nsName string) (bool, error) { 144 netnsMountFile := path.Join(netNsMountDir, nsName) 145 return nh.sysHandler.FileExists(netnsMountFile) 146 }