1package remote 2 3import ( 4 "fmt" 5 "net" 6 7 "github.com/sirupsen/logrus" 8 "github.com/docker/docker/pkg/plugins" 9 "github.com/docker/libnetwork/datastore" 10 "github.com/docker/libnetwork/discoverapi" 11 "github.com/docker/libnetwork/driverapi" 12 "github.com/docker/libnetwork/drivers/remote/api" 13 "github.com/docker/libnetwork/types" 14) 15 16type driver struct { 17 endpoint *plugins.Client 18 networkType string 19} 20 21type maybeError interface { 22 GetError() string 23} 24 25func newDriver(name string, client *plugins.Client) driverapi.Driver { 26 return &driver{networkType: name, endpoint: client} 27} 28 29// Init makes sure a remote driver is registered when a network driver 30// plugin is activated. 31func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { 32 // Unit test code is unaware of a true PluginStore. So we fall back to v1 plugins. 33 handleFunc := plugins.Handle 34 if pg := dc.GetPluginGetter(); pg != nil { 35 handleFunc = pg.Handle 36 } 37 handleFunc(driverapi.NetworkPluginEndpointType, func(name string, client *plugins.Client) { 38 // negotiate driver capability with client 39 d := newDriver(name, client) 40 c, err := d.(*driver).getCapabilities() 41 if err != nil { 42 logrus.Errorf("error getting capability for %s due to %v", name, err) 43 return 44 } 45 if err = dc.RegisterDriver(name, d, *c); err != nil { 46 logrus.Errorf("error registering driver for %s due to %v", name, err) 47 } 48 }) 49 return nil 50} 51 52// Get capability from client 53func (d *driver) getCapabilities() (*driverapi.Capability, error) { 54 var capResp api.GetCapabilityResponse 55 if err := d.call("GetCapabilities", nil, &capResp); err != nil { 56 return nil, err 57 } 58 59 c := &driverapi.Capability{} 60 switch capResp.Scope { 61 case "global": 62 c.DataScope = datastore.GlobalScope 63 case "local": 64 c.DataScope = datastore.LocalScope 65 default: 66 return nil, fmt.Errorf("invalid capability: expecting 'local' or 'global', got %s", capResp.Scope) 67 } 68 69 return c, nil 70} 71 72// Config is not implemented for remote drivers, since it is assumed 73// to be supplied to the remote process out-of-band (e.g., as command 74// line arguments). 75func (d *driver) Config(option map[string]interface{}) error { 76 return &driverapi.ErrNotImplemented{} 77} 78 79func (d *driver) call(methodName string, arg interface{}, retVal maybeError) error { 80 method := driverapi.NetworkPluginEndpointType + "." + methodName 81 err := d.endpoint.Call(method, arg, retVal) 82 if err != nil { 83 return err 84 } 85 if e := retVal.GetError(); e != "" { 86 return fmt.Errorf("remote: %s", e) 87 } 88 return nil 89} 90 91func (d *driver) NetworkAllocate(id string, options map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { 92 create := &api.AllocateNetworkRequest{ 93 NetworkID: id, 94 Options: options, 95 IPv4Data: ipV4Data, 96 IPv6Data: ipV6Data, 97 } 98 retVal := api.AllocateNetworkResponse{} 99 err := d.call("AllocateNetwork", create, &retVal) 100 return retVal.Options, err 101} 102 103func (d *driver) NetworkFree(id string) error { 104 fr := &api.FreeNetworkRequest{NetworkID: id} 105 return d.call("FreeNetwork", fr, &api.FreeNetworkResponse{}) 106} 107 108func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { 109} 110 111func (d *driver) CreateNetwork(id string, options map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { 112 create := &api.CreateNetworkRequest{ 113 NetworkID: id, 114 Options: options, 115 IPv4Data: ipV4Data, 116 IPv6Data: ipV6Data, 117 } 118 return d.call("CreateNetwork", create, &api.CreateNetworkResponse{}) 119} 120 121func (d *driver) DeleteNetwork(nid string) error { 122 delete := &api.DeleteNetworkRequest{NetworkID: nid} 123 return d.call("DeleteNetwork", delete, &api.DeleteNetworkResponse{}) 124} 125 126func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { 127 if ifInfo == nil { 128 return fmt.Errorf("must not be called with nil InterfaceInfo") 129 } 130 131 reqIface := &api.EndpointInterface{} 132 if ifInfo.Address() != nil { 133 reqIface.Address = ifInfo.Address().String() 134 } 135 if ifInfo.AddressIPv6() != nil { 136 reqIface.AddressIPv6 = ifInfo.AddressIPv6().String() 137 } 138 if ifInfo.MacAddress() != nil { 139 reqIface.MacAddress = ifInfo.MacAddress().String() 140 } 141 142 create := &api.CreateEndpointRequest{ 143 NetworkID: nid, 144 EndpointID: eid, 145 Interface: reqIface, 146 Options: epOptions, 147 } 148 var res api.CreateEndpointResponse 149 if err := d.call("CreateEndpoint", create, &res); err != nil { 150 return err 151 } 152 153 inIface, err := parseInterface(res) 154 if err != nil { 155 return err 156 } 157 if inIface == nil { 158 // Remote driver did not set any field 159 return nil 160 } 161 162 if inIface.MacAddress != nil { 163 if err := ifInfo.SetMacAddress(inIface.MacAddress); err != nil { 164 return errorWithRollback(fmt.Sprintf("driver modified interface MAC address: %v", err), d.DeleteEndpoint(nid, eid)) 165 } 166 } 167 if inIface.Address != nil { 168 if err := ifInfo.SetIPAddress(inIface.Address); err != nil { 169 return errorWithRollback(fmt.Sprintf("driver modified interface address: %v", err), d.DeleteEndpoint(nid, eid)) 170 } 171 } 172 if inIface.AddressIPv6 != nil { 173 if err := ifInfo.SetIPAddress(inIface.AddressIPv6); err != nil { 174 return errorWithRollback(fmt.Sprintf("driver modified interface address: %v", err), d.DeleteEndpoint(nid, eid)) 175 } 176 } 177 178 return nil 179} 180 181func errorWithRollback(msg string, err error) error { 182 rollback := "rolled back" 183 if err != nil { 184 rollback = "failed to roll back: " + err.Error() 185 } 186 return fmt.Errorf("%s; %s", msg, rollback) 187} 188 189func (d *driver) DeleteEndpoint(nid, eid string) error { 190 delete := &api.DeleteEndpointRequest{ 191 NetworkID: nid, 192 EndpointID: eid, 193 } 194 return d.call("DeleteEndpoint", delete, &api.DeleteEndpointResponse{}) 195} 196 197func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { 198 info := &api.EndpointInfoRequest{ 199 NetworkID: nid, 200 EndpointID: eid, 201 } 202 var res api.EndpointInfoResponse 203 if err := d.call("EndpointOperInfo", info, &res); err != nil { 204 return nil, err 205 } 206 return res.Value, nil 207} 208 209// Join method is invoked when a Sandbox is attached to an endpoint. 210func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { 211 join := &api.JoinRequest{ 212 NetworkID: nid, 213 EndpointID: eid, 214 SandboxKey: sboxKey, 215 Options: options, 216 } 217 var ( 218 res api.JoinResponse 219 err error 220 ) 221 if err = d.call("Join", join, &res); err != nil { 222 return err 223 } 224 225 ifaceName := res.InterfaceName 226 if iface := jinfo.InterfaceName(); iface != nil && ifaceName != nil { 227 if err := iface.SetNames(ifaceName.SrcName, ifaceName.DstPrefix); err != nil { 228 return errorWithRollback(fmt.Sprintf("failed to set interface name: %s", err), d.Leave(nid, eid)) 229 } 230 } 231 232 var addr net.IP 233 if res.Gateway != "" { 234 if addr = net.ParseIP(res.Gateway); addr == nil { 235 return fmt.Errorf(`unable to parse Gateway "%s"`, res.Gateway) 236 } 237 if jinfo.SetGateway(addr) != nil { 238 return errorWithRollback(fmt.Sprintf("failed to set gateway: %v", addr), d.Leave(nid, eid)) 239 } 240 } 241 if res.GatewayIPv6 != "" { 242 if addr = net.ParseIP(res.GatewayIPv6); addr == nil { 243 return fmt.Errorf(`unable to parse GatewayIPv6 "%s"`, res.GatewayIPv6) 244 } 245 if jinfo.SetGatewayIPv6(addr) != nil { 246 return errorWithRollback(fmt.Sprintf("failed to set gateway IPv6: %v", addr), d.Leave(nid, eid)) 247 } 248 } 249 if len(res.StaticRoutes) > 0 { 250 routes, err := parseStaticRoutes(res) 251 if err != nil { 252 return err 253 } 254 for _, route := range routes { 255 if jinfo.AddStaticRoute(route.Destination, route.RouteType, route.NextHop) != nil { 256 return errorWithRollback(fmt.Sprintf("failed to set static route: %v", route), d.Leave(nid, eid)) 257 } 258 } 259 } 260 if res.DisableGatewayService { 261 jinfo.DisableGatewayService() 262 } 263 return nil 264} 265 266// Leave method is invoked when a Sandbox detaches from an endpoint. 267func (d *driver) Leave(nid, eid string) error { 268 leave := &api.LeaveRequest{ 269 NetworkID: nid, 270 EndpointID: eid, 271 } 272 return d.call("Leave", leave, &api.LeaveResponse{}) 273} 274 275// ProgramExternalConnectivity is invoked to program the rules to allow external connectivity for the endpoint. 276func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { 277 data := &api.ProgramExternalConnectivityRequest{ 278 NetworkID: nid, 279 EndpointID: eid, 280 Options: options, 281 } 282 err := d.call("ProgramExternalConnectivity", data, &api.ProgramExternalConnectivityResponse{}) 283 if err != nil && plugins.IsNotFound(err) { 284 // It is not mandatory yet to support this method 285 return nil 286 } 287 return err 288} 289 290// RevokeExternalConnectivity method is invoked to remove any external connectivity programming related to the endpoint. 291func (d *driver) RevokeExternalConnectivity(nid, eid string) error { 292 data := &api.RevokeExternalConnectivityRequest{ 293 NetworkID: nid, 294 EndpointID: eid, 295 } 296 err := d.call("RevokeExternalConnectivity", data, &api.RevokeExternalConnectivityResponse{}) 297 if err != nil && plugins.IsNotFound(err) { 298 // It is not mandatory yet to support this method 299 return nil 300 } 301 return err 302} 303 304func (d *driver) Type() string { 305 return d.networkType 306} 307 308// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster 309func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { 310 if dType != discoverapi.NodeDiscovery { 311 return nil 312 } 313 notif := &api.DiscoveryNotification{ 314 DiscoveryType: dType, 315 DiscoveryData: data, 316 } 317 return d.call("DiscoverNew", notif, &api.DiscoveryResponse{}) 318} 319 320// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster 321func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { 322 if dType != discoverapi.NodeDiscovery { 323 return nil 324 } 325 notif := &api.DiscoveryNotification{ 326 DiscoveryType: dType, 327 DiscoveryData: data, 328 } 329 return d.call("DiscoverDelete", notif, &api.DiscoveryResponse{}) 330} 331 332func parseStaticRoutes(r api.JoinResponse) ([]*types.StaticRoute, error) { 333 var routes = make([]*types.StaticRoute, len(r.StaticRoutes)) 334 for i, inRoute := range r.StaticRoutes { 335 var err error 336 outRoute := &types.StaticRoute{RouteType: inRoute.RouteType} 337 338 if inRoute.Destination != "" { 339 if outRoute.Destination, err = types.ParseCIDR(inRoute.Destination); err != nil { 340 return nil, err 341 } 342 } 343 344 if inRoute.NextHop != "" { 345 outRoute.NextHop = net.ParseIP(inRoute.NextHop) 346 if outRoute.NextHop == nil { 347 return nil, fmt.Errorf("failed to parse nexthop IP %s", inRoute.NextHop) 348 } 349 } 350 351 routes[i] = outRoute 352 } 353 return routes, nil 354} 355 356// parseInterfaces validates all the parameters of an Interface and returns them. 357func parseInterface(r api.CreateEndpointResponse) (*api.Interface, error) { 358 var outIf *api.Interface 359 360 inIf := r.Interface 361 if inIf != nil { 362 var err error 363 outIf = &api.Interface{} 364 if inIf.Address != "" { 365 if outIf.Address, err = types.ParseCIDR(inIf.Address); err != nil { 366 return nil, err 367 } 368 } 369 if inIf.AddressIPv6 != "" { 370 if outIf.AddressIPv6, err = types.ParseCIDR(inIf.AddressIPv6); err != nil { 371 return nil, err 372 } 373 } 374 if inIf.MacAddress != "" { 375 if outIf.MacAddress, err = net.ParseMAC(inIf.MacAddress); err != nil { 376 return nil, err 377 } 378 } 379 } 380 381 return outIf, nil 382} 383