github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/container-utils/containerutils.go (about) 1 // Copyright 2019-2022 The Inspektor Gadget authors 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 package containerutils 16 17 import ( 18 "encoding/json" 19 "errors" 20 "fmt" 21 "net" 22 "os" 23 "path/filepath" 24 "regexp" 25 "strings" 26 "syscall" 27 28 ocispec "github.com/opencontainers/runtime-spec/specs-go" 29 "github.com/vishvananda/netlink" 30 31 "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/containerd" 32 "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/crio" 33 "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/docker" 34 "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/podman" 35 runtimeclient "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/runtime-client" 36 containerutilsTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/types" 37 "github.com/inspektor-gadget/inspektor-gadget/pkg/netnsenter" 38 "github.com/inspektor-gadget/inspektor-gadget/pkg/types" 39 "github.com/inspektor-gadget/inspektor-gadget/pkg/utils/host" 40 ) 41 42 var AvailableRuntimes = []string{ 43 types.RuntimeNameDocker.String(), 44 types.RuntimeNameContainerd.String(), 45 types.RuntimeNameCrio.String(), 46 types.RuntimeNamePodman.String(), 47 } 48 49 var AvailableRuntimeProtocols = []string{ 50 containerutilsTypes.RuntimeProtocolInternal, 51 containerutilsTypes.RuntimeProtocolCRI, 52 } 53 54 func NewContainerRuntimeClient(runtime *containerutilsTypes.RuntimeConfig) (runtimeclient.ContainerRuntimeClient, error) { 55 switch runtime.Name { 56 case types.RuntimeNameDocker: 57 socketPath := runtime.SocketPath 58 if envsp := os.Getenv("INSPEKTOR_GADGET_DOCKER_SOCKETPATH"); envsp != "" && socketPath == "" { 59 socketPath = filepath.Join(host.HostRoot, envsp) 60 } 61 return docker.NewDockerClient(socketPath, runtime.RuntimeProtocol) 62 case types.RuntimeNameContainerd: 63 socketPath := runtime.SocketPath 64 if envsp := os.Getenv("INSPEKTOR_GADGET_CONTAINERD_SOCKETPATH"); envsp != "" && socketPath == "" { 65 socketPath = filepath.Join(host.HostRoot, envsp) 66 } 67 return containerd.NewContainerdClient(socketPath, runtime.RuntimeProtocol, &runtime.Extra) 68 case types.RuntimeNameCrio: 69 socketPath := runtime.SocketPath 70 if envsp := os.Getenv("INSPEKTOR_GADGET_CRIO_SOCKETPATH"); envsp != "" && socketPath == "" { 71 socketPath = filepath.Join(host.HostRoot, envsp) 72 } 73 return crio.NewCrioClient(socketPath) 74 case types.RuntimeNamePodman: 75 socketPath := runtime.SocketPath 76 if envsp := os.Getenv("INSPEKTOR_GADGET_PODMAN_SOCKETPATH"); envsp != "" && socketPath == "" { 77 socketPath = filepath.Join(host.HostRoot, envsp) 78 } 79 return podman.NewPodmanClient(socketPath), nil 80 default: 81 return nil, fmt.Errorf("unknown container runtime: %s (available %s)", 82 runtime.Name, strings.Join(AvailableRuntimes, ", ")) 83 } 84 } 85 86 func getNamespaceInode(pid int, nsType string) (uint64, error) { 87 fileinfo, err := os.Stat(filepath.Join(host.HostProcFs, fmt.Sprint(pid), "ns", nsType)) 88 if err != nil { 89 return 0, err 90 } 91 stat, ok := fileinfo.Sys().(*syscall.Stat_t) 92 if !ok { 93 return 0, errors.New("not a syscall.Stat_t") 94 } 95 return stat.Ino, nil 96 } 97 98 func GetMntNs(pid int) (uint64, error) { 99 return getNamespaceInode(pid, "mnt") 100 } 101 102 func GetNetNs(pid int) (uint64, error) { 103 return getNamespaceInode(pid, "net") 104 } 105 106 func ParseOCIState(stateBuf []byte) (id string, pid int, err error) { 107 ociState := &ocispec.State{} 108 err = json.Unmarshal(stateBuf, ociState) 109 if err != nil { 110 // Some versions of runc produce an invalid json... 111 // As a workaround, make it valid by trimming the invalid parts 112 fix := regexp.MustCompile(`(?ms)^(.*),"annotations":.*$`) 113 matches := fix.FindStringSubmatch(string(stateBuf)) 114 if len(matches) != 2 { 115 err = fmt.Errorf("parsing OCI state: matches=%+v\n %w\n%s", matches, err, string(stateBuf)) 116 return 117 } 118 err = json.Unmarshal([]byte(matches[1]+"}"), ociState) 119 if err != nil { 120 err = fmt.Errorf("parsing OCI state: %w\n%s", err, string(stateBuf)) 121 return 122 } 123 } 124 id = ociState.ID 125 pid = ociState.Pid 126 return 127 } 128 129 // GetIfacePeers returns the networking interfaces on the host side of the container where pid is 130 // running in. 131 func GetIfacePeers(pid int) ([]*net.Interface, error) { 132 var ifaceLinks []int 133 134 err := netnsenter.NetnsEnter(pid, func() error { 135 links, err := netlink.LinkList() 136 if err != nil { 137 return fmt.Errorf("getting links: %w", err) 138 } 139 140 for _, link := range links { 141 veth, ok := link.(*netlink.Veth) 142 if !ok { 143 continue 144 } 145 146 if veth.LinkAttrs.Flags&net.FlagUp == 0 { 147 continue 148 } 149 150 ifaceLink, err := netlink.VethPeerIndex(veth) 151 if err != nil { 152 return fmt.Errorf("getting veth's pair index: %w", err) 153 } 154 155 ifaceLinks = append(ifaceLinks, ifaceLink) 156 } 157 158 return nil 159 }) 160 if err != nil { 161 return nil, err 162 } 163 164 if len(ifaceLinks) == 0 { 165 return nil, fmt.Errorf("no interface found") 166 } 167 168 ifacesHost := make([]*net.Interface, 0, len(ifaceLinks)) 169 170 err = netnsenter.NetnsEnter(1, func() error { 171 for _, ifaceLink := range ifaceLinks { 172 ifaceHost, err := net.InterfaceByIndex(ifaceLink) 173 if err != nil { 174 return fmt.Errorf("getting interface by index: %w", err) 175 } 176 177 ifacesHost = append(ifacesHost, ifaceHost) 178 } 179 return nil 180 }) 181 182 return ifacesHost, err 183 }