1// +build linux 2 3package libcontainer 4 5import ( 6 "fmt" 7 "io/ioutil" 8 "net" 9 "path/filepath" 10 "strconv" 11 "strings" 12 13 "github.com/opencontainers/runc/libcontainer/configs" 14 "github.com/opencontainers/runc/libcontainer/utils" 15 "github.com/vishvananda/netlink" 16) 17 18var strategies = map[string]networkStrategy{ 19 "veth": &veth{}, 20 "loopback": &loopback{}, 21} 22 23// networkStrategy represents a specific network configuration for 24// a container's networking stack 25type networkStrategy interface { 26 create(*network, int) error 27 initialize(*network) error 28 detach(*configs.Network) error 29 attach(*configs.Network) error 30} 31 32// getStrategy returns the specific network strategy for the 33// provided type. 34func getStrategy(tpe string) (networkStrategy, error) { 35 s, exists := strategies[tpe] 36 if !exists { 37 return nil, fmt.Errorf("unknown strategy type %q", tpe) 38 } 39 return s, nil 40} 41 42// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo. 43func getNetworkInterfaceStats(interfaceName string) (*NetworkInterface, error) { 44 out := &NetworkInterface{Name: interfaceName} 45 // This can happen if the network runtime information is missing - possible if the 46 // container was created by an old version of libcontainer. 47 if interfaceName == "" { 48 return out, nil 49 } 50 type netStatsPair struct { 51 // Where to write the output. 52 Out *uint64 53 // The network stats file to read. 54 File string 55 } 56 // Ingress for host veth is from the container. Hence tx_bytes stat on the host veth is actually number of bytes received by the container. 57 netStats := []netStatsPair{ 58 {Out: &out.RxBytes, File: "tx_bytes"}, 59 {Out: &out.RxPackets, File: "tx_packets"}, 60 {Out: &out.RxErrors, File: "tx_errors"}, 61 {Out: &out.RxDropped, File: "tx_dropped"}, 62 63 {Out: &out.TxBytes, File: "rx_bytes"}, 64 {Out: &out.TxPackets, File: "rx_packets"}, 65 {Out: &out.TxErrors, File: "rx_errors"}, 66 {Out: &out.TxDropped, File: "rx_dropped"}, 67 } 68 for _, netStat := range netStats { 69 data, err := readSysfsNetworkStats(interfaceName, netStat.File) 70 if err != nil { 71 return nil, err 72 } 73 *(netStat.Out) = data 74 } 75 return out, nil 76} 77 78// Reads the specified statistics available under /sys/class/net/<EthInterface>/statistics 79func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) { 80 data, err := ioutil.ReadFile(filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile)) 81 if err != nil { 82 return 0, err 83 } 84 return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64) 85} 86 87// loopback is a network strategy that provides a basic loopback device 88type loopback struct { 89} 90 91func (l *loopback) create(n *network, nspid int) error { 92 return nil 93} 94 95func (l *loopback) initialize(config *network) error { 96 return netlink.LinkSetUp(&netlink.Device{LinkAttrs: netlink.LinkAttrs{Name: "lo"}}) 97} 98 99func (l *loopback) attach(n *configs.Network) (err error) { 100 return nil 101} 102 103func (l *loopback) detach(n *configs.Network) (err error) { 104 return nil 105} 106 107// veth is a network strategy that uses a bridge and creates 108// a veth pair, one that is attached to the bridge on the host and the other 109// is placed inside the container's namespace 110type veth struct { 111} 112 113func (v *veth) detach(n *configs.Network) (err error) { 114 return netlink.LinkSetMaster(&netlink.Device{LinkAttrs: netlink.LinkAttrs{Name: n.HostInterfaceName}}, nil) 115} 116 117// attach a container network interface to an external network 118func (v *veth) attach(n *configs.Network) (err error) { 119 brl, err := netlink.LinkByName(n.Bridge) 120 if err != nil { 121 return err 122 } 123 br, ok := brl.(*netlink.Bridge) 124 if !ok { 125 return fmt.Errorf("Wrong device type %T", brl) 126 } 127 host, err := netlink.LinkByName(n.HostInterfaceName) 128 if err != nil { 129 return err 130 } 131 132 if err := netlink.LinkSetMaster(host, br); err != nil { 133 return err 134 } 135 if err := netlink.LinkSetMTU(host, n.Mtu); err != nil { 136 return err 137 } 138 if n.HairpinMode { 139 if err := netlink.LinkSetHairpin(host, true); err != nil { 140 return err 141 } 142 } 143 if err := netlink.LinkSetUp(host); err != nil { 144 return err 145 } 146 147 return nil 148} 149 150func (v *veth) create(n *network, nspid int) (err error) { 151 tmpName, err := v.generateTempPeerName() 152 if err != nil { 153 return err 154 } 155 n.TempVethPeerName = tmpName 156 if n.Bridge == "" { 157 return fmt.Errorf("bridge is not specified") 158 } 159 veth := &netlink.Veth{ 160 LinkAttrs: netlink.LinkAttrs{ 161 Name: n.HostInterfaceName, 162 TxQLen: n.TxQueueLen, 163 }, 164 PeerName: n.TempVethPeerName, 165 } 166 if err := netlink.LinkAdd(veth); err != nil { 167 return err 168 } 169 defer func() { 170 if err != nil { 171 netlink.LinkDel(veth) 172 } 173 }() 174 if err := v.attach(&n.Network); err != nil { 175 return err 176 } 177 child, err := netlink.LinkByName(n.TempVethPeerName) 178 if err != nil { 179 return err 180 } 181 return netlink.LinkSetNsPid(child, nspid) 182} 183 184func (v *veth) generateTempPeerName() (string, error) { 185 return utils.GenerateRandomName("veth", 7) 186} 187 188func (v *veth) initialize(config *network) error { 189 peer := config.TempVethPeerName 190 if peer == "" { 191 return fmt.Errorf("peer is not specified") 192 } 193 child, err := netlink.LinkByName(peer) 194 if err != nil { 195 return err 196 } 197 if err := netlink.LinkSetDown(child); err != nil { 198 return err 199 } 200 if err := netlink.LinkSetName(child, config.Name); err != nil { 201 return err 202 } 203 // get the interface again after we changed the name as the index also changes. 204 if child, err = netlink.LinkByName(config.Name); err != nil { 205 return err 206 } 207 if config.MacAddress != "" { 208 mac, err := net.ParseMAC(config.MacAddress) 209 if err != nil { 210 return err 211 } 212 if err := netlink.LinkSetHardwareAddr(child, mac); err != nil { 213 return err 214 } 215 } 216 ip, err := netlink.ParseAddr(config.Address) 217 if err != nil { 218 return err 219 } 220 if err := netlink.AddrAdd(child, ip); err != nil { 221 return err 222 } 223 if config.IPv6Address != "" { 224 ip6, err := netlink.ParseAddr(config.IPv6Address) 225 if err != nil { 226 return err 227 } 228 if err := netlink.AddrAdd(child, ip6); err != nil { 229 return err 230 } 231 } 232 if err := netlink.LinkSetMTU(child, config.Mtu); err != nil { 233 return err 234 } 235 if err := netlink.LinkSetUp(child); err != nil { 236 return err 237 } 238 if config.Gateway != "" { 239 gw := net.ParseIP(config.Gateway) 240 if err := netlink.RouteAdd(&netlink.Route{ 241 Scope: netlink.SCOPE_UNIVERSE, 242 LinkIndex: child.Attrs().Index, 243 Gw: gw, 244 }); err != nil { 245 return err 246 } 247 } 248 if config.IPv6Gateway != "" { 249 gw := net.ParseIP(config.IPv6Gateway) 250 if err := netlink.RouteAdd(&netlink.Route{ 251 Scope: netlink.SCOPE_UNIVERSE, 252 LinkIndex: child.Attrs().Index, 253 Gw: gw, 254 }); err != nil { 255 return err 256 } 257 } 258 return nil 259} 260