github.com/looshlee/cilium@v1.6.12/plugins/cilium-docker/driver/driver.go (about) 1 // Copyright 2016-2017 Authors of Cilium 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 driver 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "net" 21 "net/http" 22 "reflect" 23 "time" 24 25 "github.com/cilium/cilium/api/v1/models" 26 "github.com/cilium/cilium/pkg/client" 27 "github.com/cilium/cilium/pkg/datapath/link" 28 "github.com/cilium/cilium/pkg/datapath/linux/route" 29 "github.com/cilium/cilium/pkg/endpoint/connector" 30 endpointIDPkg "github.com/cilium/cilium/pkg/endpoint/id" 31 "github.com/cilium/cilium/pkg/lock" 32 "github.com/cilium/cilium/pkg/logging" 33 "github.com/cilium/cilium/pkg/logging/logfields" 34 "github.com/cilium/cilium/pkg/option" 35 36 "github.com/docker/libnetwork/drivers/remote/api" 37 lnTypes "github.com/docker/libnetwork/types" 38 "github.com/gorilla/mux" 39 "github.com/sirupsen/logrus" 40 "github.com/vishvananda/netlink" 41 ) 42 43 var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "cilium-docker-driver") 44 45 const ( 46 // ContainerInterfacePrefix is the container's internal interface name prefix. 47 ContainerInterfacePrefix = "cilium" 48 ) 49 50 // Driver interface that listens for docker requests. 51 type Driver interface { 52 Listen(string) error 53 } 54 55 type driver struct { 56 mutex lock.RWMutex 57 client *client.Client 58 conf models.DaemonConfigurationStatus 59 routes []api.StaticRoute 60 gatewayIPv6 string 61 gatewayIPv4 string 62 } 63 64 func endpointID(id string) string { 65 return endpointIDPkg.NewID(endpointIDPkg.DockerEndpointPrefix, id) 66 } 67 68 func newLibnetworkRoute(route route.Route) api.StaticRoute { 69 rt := api.StaticRoute{ 70 Destination: route.Prefix.String(), 71 RouteType: lnTypes.CONNECTED, 72 } 73 74 if route.Nexthop != nil { 75 rt.NextHop = route.Nexthop.String() 76 rt.RouteType = lnTypes.NEXTHOP 77 } 78 79 return rt 80 } 81 82 // NewDriver creates and returns a new Driver for the given API URL. 83 // If url is nil then use SockPath provided by CILIUM_SOCK 84 // or the cilium default SockPath 85 func NewDriver(url string) (Driver, error) { 86 87 if url == "" { 88 url = client.DefaultSockPath() 89 } 90 91 scopedLog := log.WithField("url", url) 92 c, err := client.NewClient(url) 93 if err != nil { 94 scopedLog.WithError(err).Fatal("Error while starting cilium-client") 95 } 96 97 d := &driver{client: c} 98 99 for tries := 0; tries < 24; tries++ { 100 if res, err := c.ConfigGet(); err != nil { 101 if tries == 23 { 102 scopedLog.WithError(err).Fatal("Unable to connect to cilium daemon") 103 } else { 104 scopedLog.Info("Waiting for cilium daemon to start up...") 105 } 106 time.Sleep(time.Duration(tries) * time.Second) 107 } else { 108 if res.Status.Addressing == nil || (res.Status.Addressing.IPV4 == nil && res.Status.Addressing.IPV6 == nil) { 109 scopedLog.Fatal("Invalid addressing information from daemon") 110 } 111 112 d.conf = *res.Status 113 break 114 } 115 } 116 117 if err := connector.SufficientAddressing(d.conf.Addressing); err != nil { 118 scopedLog.WithError(err).Fatal("Insufficient addressing") 119 } 120 121 d.updateRoutes(nil) 122 123 log.Infof("Cilium Docker plugin ready") 124 125 return d, nil 126 } 127 128 func (driver *driver) updateRoutes(addressing *models.NodeAddressing) { 129 driver.mutex.Lock() 130 defer driver.mutex.Unlock() 131 132 if addressing != nil { 133 driver.conf.Addressing = addressing 134 } 135 136 driver.routes = []api.StaticRoute{} 137 138 if driver.conf.Addressing.IPV6 != nil && driver.conf.Addressing.IPV6.Enabled { 139 if routes, err := connector.IPv6Routes(driver.conf.Addressing, int(driver.conf.RouteMTU)); err != nil { 140 log.Fatalf("Unable to generate IPv6 routes: %s", err) 141 } else { 142 for _, r := range routes { 143 driver.routes = append(driver.routes, newLibnetworkRoute(r)) 144 } 145 } 146 147 driver.gatewayIPv6 = connector.IPv6Gateway(driver.conf.Addressing) 148 } 149 150 if driver.conf.Addressing.IPV4 != nil && driver.conf.Addressing.IPV4.Enabled { 151 if routes, err := connector.IPv4Routes(driver.conf.Addressing, int(driver.conf.RouteMTU)); err != nil { 152 log.Fatalf("Unable to generate IPv4 routes: %s", err) 153 } else { 154 for _, r := range routes { 155 driver.routes = append(driver.routes, newLibnetworkRoute(r)) 156 } 157 } 158 159 driver.gatewayIPv4 = connector.IPv4Gateway(driver.conf.Addressing) 160 } 161 } 162 163 // Listen listens for docker requests on a particular set of endpoints on the given 164 // socket path. 165 func (driver *driver) Listen(socket string) error { 166 router := mux.NewRouter() 167 router.NotFoundHandler = http.HandlerFunc(notFound) 168 169 handleMethod := func(method string, h http.HandlerFunc) { 170 router.Methods("POST").Path(fmt.Sprintf("/%s", method)).HandlerFunc(h) 171 } 172 173 handleMethod("Plugin.Activate", driver.handshake) 174 handleMethod("NetworkDriver.GetCapabilities", driver.capabilities) 175 handleMethod("NetworkDriver.CreateNetwork", driver.createNetwork) 176 handleMethod("NetworkDriver.DeleteNetwork", driver.deleteNetwork) 177 handleMethod("NetworkDriver.CreateEndpoint", driver.createEndpoint) 178 handleMethod("NetworkDriver.DeleteEndpoint", driver.deleteEndpoint) 179 handleMethod("NetworkDriver.EndpointOperInfo", driver.infoEndpoint) 180 handleMethod("NetworkDriver.Join", driver.joinEndpoint) 181 handleMethod("NetworkDriver.Leave", driver.leaveEndpoint) 182 handleMethod("IpamDriver.GetCapabilities", driver.ipamCapabilities) 183 handleMethod("IpamDriver.GetDefaultAddressSpaces", driver.getDefaultAddressSpaces) 184 handleMethod("IpamDriver.RequestPool", driver.requestPool) 185 handleMethod("IpamDriver.ReleasePool", driver.releasePool) 186 handleMethod("IpamDriver.RequestAddress", driver.requestAddress) 187 handleMethod("IpamDriver.ReleaseAddress", driver.releaseAddress) 188 189 listener, err := net.Listen("unix", socket) 190 if err != nil { 191 return err 192 } 193 return http.Serve(listener, router) 194 } 195 196 func notFound(w http.ResponseWriter, r *http.Request) { 197 log.WithField(logfields.Object, logfields.Repr(r)).Warn("plugin Not found") 198 http.NotFound(w, r) 199 } 200 201 func sendError(w http.ResponseWriter, msg string, code int) { 202 log.WithFields(logrus.Fields{ 203 "code": code, 204 "msg": msg, 205 }).Error("Sending error") 206 http.Error(w, msg, code) 207 } 208 209 func objectResponse(w http.ResponseWriter, obj interface{}) { 210 if err := json.NewEncoder(w).Encode(obj); err != nil { 211 sendError(w, "Could not JSON encode response", http.StatusInternalServerError) 212 return 213 } 214 } 215 216 func emptyResponse(w http.ResponseWriter) { 217 json.NewEncoder(w).Encode(map[string]string{}) 218 } 219 220 type handshakeResp struct { 221 Implements []string 222 } 223 224 func (driver *driver) handshake(w http.ResponseWriter, r *http.Request) { 225 err := json.NewEncoder(w).Encode(&handshakeResp{ 226 []string{"NetworkDriver", "IpamDriver"}, 227 }) 228 if err != nil { 229 log.WithError(err).Fatal("handshake encode") 230 sendError(w, "encode error", http.StatusInternalServerError) 231 return 232 } 233 log.Debug("Handshake completed") 234 } 235 236 func (driver *driver) capabilities(w http.ResponseWriter, r *http.Request) { 237 err := json.NewEncoder(w).Encode(&api.GetCapabilityResponse{ 238 Scope: "local", 239 }) 240 if err != nil { 241 log.WithError(err).Fatal("capabilities encode") 242 sendError(w, "encode error", http.StatusInternalServerError) 243 return 244 } 245 log.Debug("NetworkDriver capabilities exchange complete") 246 } 247 248 func (driver *driver) createNetwork(w http.ResponseWriter, r *http.Request) { 249 var create api.CreateNetworkRequest 250 err := json.NewDecoder(r.Body).Decode(&create) 251 if err != nil { 252 sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 253 return 254 } 255 log.WithField(logfields.Request, logfields.Repr(&create)).Debug("Network Create Called") 256 emptyResponse(w) 257 } 258 259 func (driver *driver) deleteNetwork(w http.ResponseWriter, r *http.Request) { 260 var delete api.DeleteNetworkRequest 261 if err := json.NewDecoder(r.Body).Decode(&delete); err != nil { 262 sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 263 return 264 } 265 log.WithField(logfields.Request, logfields.Repr(&delete)).Debug("Delete network request") 266 emptyResponse(w) 267 } 268 269 // CreateEndpointRequest is the as api.CreateEndpointRequest but with 270 // json.RawMessage type for Options map 271 type CreateEndpointRequest struct { 272 NetworkID string 273 EndpointID string 274 Interface api.EndpointInterface 275 Options map[string]json.RawMessage 276 } 277 278 func (driver *driver) createEndpoint(w http.ResponseWriter, r *http.Request) { 279 var create CreateEndpointRequest 280 var err error 281 if err := json.NewDecoder(r.Body).Decode(&create); err != nil { 282 sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 283 return 284 } 285 log.WithField(logfields.Request, logfields.Repr(&create)).Debug("Create endpoint request") 286 287 if create.Interface.Address == "" && create.Interface.AddressIPv6 == "" { 288 sendError(w, "No IPv4 or IPv6 address provided (required)", http.StatusBadRequest) 289 return 290 } 291 292 endpoint := &models.EndpointChangeRequest{ 293 SyncBuildEndpoint: true, 294 State: models.EndpointStateWaitingForIdentity, 295 DockerEndpointID: create.EndpointID, 296 DockerNetworkID: create.NetworkID, 297 Addressing: &models.AddressPair{ 298 IPV6: create.Interface.AddressIPv6, 299 IPV4: create.Interface.Address, 300 }, 301 } 302 303 removeLinkOnErr := func(link netlink.Link) { 304 if err != nil && link != nil && !reflect.ValueOf(link).IsNil() { 305 if err := netlink.LinkDel(link); err != nil { 306 log.WithError(err).WithFields(logrus.Fields{ 307 logfields.DatapathMode: driver.conf.DatapathMode, 308 logfields.Device: link.Attrs().Name, 309 }).Warn("failed to clean up") 310 } 311 } 312 } 313 314 switch driver.conf.DatapathMode { 315 case option.DatapathModeVeth: 316 var veth *netlink.Veth 317 veth, _, _, err = connector.SetupVeth(create.EndpointID, int(driver.conf.DeviceMTU), endpoint) 318 defer removeLinkOnErr(veth) 319 case option.DatapathModeIpvlan: 320 var ipvlan *netlink.IPVlan 321 ipvlan, _, _, err = connector.CreateIpvlanSlave( 322 create.EndpointID, int(driver.conf.DeviceMTU), 323 int(driver.conf.IpvlanConfiguration.MasterDeviceIndex), 324 driver.conf.IpvlanConfiguration.OperationMode, endpoint, 325 ) 326 defer removeLinkOnErr(ipvlan) 327 } 328 if err != nil { 329 sendError(w, 330 fmt.Sprintf("Error while setting up %s mode: %s", driver.conf.DatapathMode, err), 331 http.StatusBadRequest) 332 return 333 } 334 335 // FIXME: Translate port mappings to RuleL4 policy elements 336 337 if err = driver.client.EndpointCreate(endpoint); err != nil { 338 sendError(w, fmt.Sprintf("Error creating endpoint %s", err), http.StatusBadRequest) 339 return 340 } 341 342 log.WithField(logfields.EndpointID, create.EndpointID).Debug("Created new endpoint") 343 344 respIface := &api.EndpointInterface{ 345 // Fixme: the lxcmac is an empty string at this point and we only know the 346 // mac address at the end of joinEndpoint 347 // There's no problem in the setup but docker inspect will show an empty mac address 348 MacAddress: "", 349 } 350 351 resp := &api.CreateEndpointResponse{ 352 Interface: respIface, 353 } 354 log.WithField(logfields.Response, logfields.Repr(resp)).Debug("Create endpoint response") 355 objectResponse(w, resp) 356 } 357 358 func (driver *driver) deleteEndpoint(w http.ResponseWriter, r *http.Request) { 359 var del api.DeleteEndpointRequest 360 var ifName string 361 if err := json.NewDecoder(r.Body).Decode(&del); err != nil { 362 sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest) 363 return 364 } 365 log.WithField(logfields.Request, logfields.Repr(&del)).Debug("Delete endpoint request") 366 367 switch driver.conf.DatapathMode { 368 case option.DatapathModeVeth: 369 ifName = connector.Endpoint2IfName(del.EndpointID) 370 case option.DatapathModeIpvlan: 371 ifName = connector.Endpoint2TempIfName(del.EndpointID) 372 } 373 374 if err := link.DeleteByName(ifName); err != nil { 375 log.WithError(err).Warn("Error while deleting link") 376 } 377 378 emptyResponse(w) 379 } 380 381 func (driver *driver) infoEndpoint(w http.ResponseWriter, r *http.Request) { 382 var info api.EndpointInfoRequest 383 if err := json.NewDecoder(r.Body).Decode(&info); err != nil { 384 sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest) 385 return 386 } 387 log.WithField(logfields.Request, logfields.Repr(&info)).Debug("Endpoint info request") 388 objectResponse(w, &api.EndpointInfoResponse{Value: map[string]interface{}{}}) 389 log.WithField(logfields.Response, info.EndpointID).Debug("Endpoint info") 390 } 391 392 func (driver *driver) joinEndpoint(w http.ResponseWriter, r *http.Request) { 393 var ( 394 j api.JoinRequest 395 err error 396 ) 397 398 driver.mutex.RLock() 399 defer driver.mutex.RUnlock() 400 401 if err := json.NewDecoder(r.Body).Decode(&j); err != nil { 402 sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest) 403 return 404 } 405 log.WithField(logfields.Request, logfields.Repr(&j)).Debug("Join request") 406 407 old, err := driver.client.EndpointGet(endpointID(j.EndpointID)) 408 if err != nil { 409 sendError(w, fmt.Sprintf("Error retrieving endpoint %s", err), http.StatusBadRequest) 410 return 411 } 412 413 log.WithField(logfields.Object, old).Debug("Existing endpoint") 414 415 res := &api.JoinResponse{ 416 InterfaceName: &api.InterfaceName{ 417 SrcName: connector.Endpoint2TempIfName(j.EndpointID), 418 DstPrefix: ContainerInterfacePrefix, 419 }, 420 StaticRoutes: driver.routes, 421 DisableGatewayService: true, 422 GatewayIPv6: driver.gatewayIPv6, 423 //GatewayIPv4: driver.gatewayIPv4, 424 } 425 426 // FIXME? Having the following code results on a runtime error: docker: Error 427 // response from daemon: oci runtime error: process_linux.go:334: running prestart 428 // hook 0 caused "exit status 1: time=\"2016-10-26T06:33:17-07:00\" level=fatal 429 // msg=\"failed to set gateway while updating gateway: file exists\" \n" 430 // 431 // If empty, it works as expected without docker runtime errors 432 // res.Gateway = connector.IPv4Gateway(addr) 433 434 log.WithField(logfields.Response, logfields.Repr(res)).Debug("Join response") 435 objectResponse(w, res) 436 } 437 438 func (driver *driver) leaveEndpoint(w http.ResponseWriter, r *http.Request) { 439 var l api.LeaveRequest 440 if err := json.NewDecoder(r.Body).Decode(&l); err != nil { 441 sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest) 442 return 443 } 444 log.WithField(logfields.Request, logfields.Repr(&l)).Debug("Leave request") 445 446 if err := driver.client.EndpointDelete(endpointID(l.EndpointID)); err != nil { 447 log.WithError(err).Warn("Leaving the endpoint failed") 448 } 449 450 emptyResponse(w) 451 }