func (vb *VXLANBackend) handleL3Miss(miss *netlink.Neigh) { log.Infof("L3 miss: %v", miss.IP) rt := vb.rts.findByNetwork(ip.FromIP(miss.IP)) if rt == nil { log.Infof("Route for %v not found", miss.IP) return } if err := vb.dev.AddL3(neigh{IP: ip.FromIP(miss.IP), MAC: rt.vtepMAC}); err != nil { log.Errorf("AddL3 failed: %v", err) } else { log.Info("AddL3 succeeded") } }
func (rb *HostgwBackend) Init(extIface *net.Interface, extIaddr net.IP, extEaddr net.IP) (*backend.SubnetDef, error) { rb.extIface = extIface rb.extIaddr = extIaddr if !extIaddr.Equal(extEaddr) { return nil, fmt.Errorf("your PublicIP differs from interface IP, meaning that probably you're on a NAT, which is not supported by host-gw backend") } attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(extIaddr), BackendType: "host-gw", } l, err := rb.sm.AcquireLease(rb.ctx, rb.network, &attrs) switch err { case nil: rb.lease = l case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } /* NB: docker will create the local route to `sn` */ return &backend.SubnetDef{ Net: l.Subnet, MTU: extIface.MTU, }, nil }
func (be *HostgwBackend) RegisterNetwork(ctx context.Context, netname string, config *subnet.Config) (backend.Network, error) { n := &network{ name: netname, extIface: be.extIface, sm: be.sm, } attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(be.extIface.ExtAddr), BackendType: "host-gw", } l, err := be.sm.AcquireLease(ctx, netname, &attrs) switch err { case nil: n.lease = l case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } /* NB: docker will create the local route to `sn` */ be.networks[netname] = n return n, nil }
func (rb *HostgwBackend) Init(extIface *net.Interface, extIP net.IP) (*backend.SubnetDef, error) { rb.extIface = extIface rb.extIP = extIP attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(extIP), BackendType: "host-gw", } l, err := rb.sm.AcquireLease(rb.ctx, rb.network, &attrs) switch err { case nil: rb.lease = l case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } /* NB: docker will create the local route to `sn` */ return &backend.SubnetDef{ Net: l.Subnet, MTU: extIface.MTU, }, nil }
func New(sm subnet.Manager, extIface *net.Interface, extIaddr net.IP, extEaddr net.IP) (backend.Backend, error) { gb := GCEBackend{ sm: sm, publicIP: ip.FromIP(extEaddr), mtu: extIface.MTU, } return &gb, nil }
func (vb *VXLANBackend) handleInitialSubnetEvents(batch []subnet.Event) error { log.Infof("Handling initial subnet events") fdbTable, err := vb.dev.GetL2List() if err != nil { return fmt.Errorf("Error fetching L2 table: %v", err) } for _, fdbEntry := range fdbTable { log.Infof("fdb already populated with: %s %s ", fdbEntry.IP, fdbEntry.HardwareAddr) } evtMarker := make([]bool, len(batch)) leaseAttrsList := make([]vxlanLeaseAttrs, len(batch)) fdbEntryMarker := make([]bool, len(fdbTable)) for i, evt := range batch { if evt.Lease.Attrs.BackendType != "vxlan" { log.Warningf("Ignoring non-vxlan subnet: type=%v", evt.Lease.Attrs.BackendType) evtMarker[i] = true continue } if err := json.Unmarshal(evt.Lease.Attrs.BackendData, &leaseAttrsList[i]); err != nil { log.Error("Error decoding subnet lease JSON: ", err) evtMarker[i] = true continue } for j, fdbEntry := range fdbTable { if evt.Lease.Attrs.PublicIP.ToIP().Equal(fdbEntry.IP) && bytes.Equal([]byte(leaseAttrsList[i].VtepMAC), []byte(fdbEntry.HardwareAddr)) { evtMarker[i] = true fdbEntryMarker[j] = true break } } vb.rts.set(evt.Lease.Subnet, net.HardwareAddr(leaseAttrsList[i].VtepMAC)) } for j, marker := range fdbEntryMarker { if !marker && fdbTable[j].IP != nil { err := vb.dev.DelL2(neigh{IP: ip.FromIP(fdbTable[j].IP), MAC: fdbTable[j].HardwareAddr}) if err != nil { log.Error("Delete L2 failed: ", err) } } } for i, marker := range evtMarker { if !marker { err := vb.dev.AddL2(neigh{IP: batch[i].Lease.Attrs.PublicIP, MAC: net.HardwareAddr(leaseAttrsList[i].VtepMAC)}) if err != nil { log.Error("Add L2 failed: ", err) } } } return nil }
func (m *AllocBackend) Init(extIface *net.Interface, extIP net.IP, ipMasq bool) (*backend.SubnetDef, error) { attrs := subnet.BaseAttrs{ PublicIP: ip.FromIP(extIP), } sn, err := m.sm.AcquireLease(ip.FromIP(extIP), &attrs, m.stop) if err != nil { if err == task.ErrCanceled { return nil, err } else { return nil, fmt.Errorf("failed to acquire lease: %v", err) } } return &backend.SubnetDef{ Net: sn, MTU: extIface.MTU, }, nil }
func New(sm subnet.Manager, extIface *net.Interface, extIaddr net.IP, extEaddr net.IP) (backend.Backend, error) { be := UdpBackend{ sm: sm, publicIP: ip.FromIP(extEaddr), // TUN MTU will be smaller b/c of encap (IP+UDP hdrs) mtu: extIface.MTU - encapOverhead, } be.cfg.Port = defaultPort return &be, nil }
func (m *UdpBackend) Init(ctx context.Context, extIface *net.Interface, extIaddr net.IP, extEaddr net.IP) (*backend.SubnetDef, error) { // Parse our configuration if len(m.config.Backend) > 0 { if err := json.Unmarshal(m.config.Backend, &m.cfg); err != nil { return nil, fmt.Errorf("error decoding UDP backend config: %v", err) } } // Acquire the lease form subnet manager attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(extEaddr), } l, err := m.sm.AcquireLease(ctx, m.network, &attrs) switch err { case nil: m.lease = l case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } // Tunnel's subnet is that of the whole overlay network (e.g. /16) // and not that of the individual host (e.g. /24) m.tunNet = ip.IP4Net{ IP: l.Subnet.IP, PrefixLen: m.config.Network.PrefixLen, } // TUN MTU will be smaller b/c of encap (IP+UDP hdrs) m.mtu = extIface.MTU - encapOverhead if err = m.initTun(); err != nil { return nil, err } m.conn, err = net.ListenUDP("udp4", &net.UDPAddr{Port: m.cfg.Port}) if err != nil { return nil, fmt.Errorf("failed to start listening on UDP socket: %v", err) } m.ctl, m.ctl2, err = newCtlSockets() if err != nil { return nil, fmt.Errorf("failed to create control socket: %v", err) } return &backend.SubnetDef{ Lease: l, MTU: m.mtu, }, nil }
func parseSubnetKey(s string) *ip.IP4Net { if parts := subnetRegex.FindStringSubmatch(s); len(parts) == 3 { snIp := net.ParseIP(parts[1]).To4() prefixLen, err := strconv.ParseUint(parts[2], 10, 5) if snIp != nil && err == nil { return &ip.IP4Net{IP: ip.FromIP(snIp), PrefixLen: uint(prefixLen)} } } return nil }
func parseSubnetKey(s string) (ip.IP4Net, error) { if parts := subnetRegex.FindStringSubmatch(s); len(parts) == 3 { snIp := net.ParseIP(parts[1]).To4() prefixLen, err := strconv.ParseUint(parts[2], 10, 5) if snIp != nil && err == nil { return ip.IP4Net{IP: ip.FromIP(snIp), PrefixLen: uint(prefixLen)}, nil } } return ip.IP4Net{}, errors.New("Error parsing IP Subnet") }
func newSubnetAttrs(extEaddr net.IP, mac net.HardwareAddr) (*subnet.LeaseAttrs, error) { data, err := json.Marshal(&vxlanLeaseAttrs{hardwareAddr(mac)}) if err != nil { return nil, err } return &subnet.LeaseAttrs{ PublicIP: ip.FromIP(extEaddr), BackendType: "vxlan", BackendData: json.RawMessage(data), }, nil }
func (m *UdpBackend) Init(extIface *net.Interface, extIP net.IP, ipMasq bool) (*backend.SubnetDef, error) { // Parse our configuration if len(m.rawCfg) > 0 { if err := json.Unmarshal(m.rawCfg, &m.cfg); err != nil { return nil, fmt.Errorf("error decoding UDP backend config: %v", err) } } // Acquire the lease form subnet manager attrs := subnet.BaseAttrs{ PublicIP: ip.FromIP(extIP), } sn, err := m.sm.AcquireLease(attrs.PublicIP, &attrs, m.stop) if err != nil { if err == task.ErrCanceled { return nil, err } else { return nil, fmt.Errorf("failed to acquire lease: %v", err) } } // Tunnel's subnet is that of the whole overlay network (e.g. /16) // and not that of the individual host (e.g. /24) m.tunNet = ip.IP4Net{ IP: sn.IP, PrefixLen: m.sm.GetConfig().Network.PrefixLen, } // TUN MTU will be smaller b/c of encap (IP+UDP hdrs) m.mtu = extIface.MTU - encapOverhead if err = m.initTun(ipMasq); err != nil { return nil, err } m.conn, err = net.ListenUDP("udp4", &net.UDPAddr{Port: m.cfg.Port}) if err != nil { return nil, fmt.Errorf("failed to start listening on UDP socket: %v", err) } m.ctl, m.ctl2, err = newCtlSockets() if err != nil { return nil, fmt.Errorf("failed to create control socket: %v", err) } return &backend.SubnetDef{ Net: sn, MTU: m.mtu, }, nil }
func New(sm subnet.Manager, extIface *net.Interface, extIaddr net.IP, extEaddr net.IP) (backend.Backend, error) { if !extIaddr.Equal(extEaddr) { return nil, fmt.Errorf("your PublicIP differs from interface IP, meaning that probably you're on a NAT, which is not supported by host-gw backend") } b := &HostgwBackend{ sm: sm, publicIP: ip.FromIP(extEaddr), mtu: extIface.MTU, extIface: extIface, extIaddr: extIaddr, } return b, nil }
func (g *GCEBackend) RegisterNetwork(ctx context.Context, network string, config *subnet.Config) (backend.Network, error) { attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(g.extIface.ExtAddr), } l, err := g.sm.AcquireLease(ctx, network, &attrs) switch err { case nil: case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } if err = g.ensureAPI(); err != nil { return nil, err } found, err := g.handleMatchingRoute(l.Subnet.String()) if err != nil { return nil, fmt.Errorf("error handling matching route: %v", err) } if !found { operation, err := g.api.insertRoute(l.Subnet.String()) if err != nil { return nil, fmt.Errorf("error inserting route: %v", err) } err = g.api.pollOperationStatus(operation.Name) if err != nil { return nil, fmt.Errorf("insert operaiton failed: ", err) } } return &backend.SimpleNetwork{ SubnetLease: l, ExtIface: g.extIface, }, nil }
func (be *AllocBackend) RegisterNetwork(ctx context.Context, network string, config *subnet.Config) (backend.Network, error) { attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(be.extIface.ExtAddr), } l, err := be.sm.AcquireLease(ctx, network, &attrs) switch err { case nil: return &backend.SimpleNetwork{ SubnetLease: l, ExtIface: be.extIface, }, nil case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } }
func (m *AllocBackend) Init(extIface *net.Interface, extIP net.IP) (*backend.SubnetDef, error) { attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(extIP), } l, err := m.sm.AcquireLease(m.ctx, m.network, &attrs) switch err { case nil: return &backend.SubnetDef{ Net: l.Subnet, MTU: extIface.MTU, }, nil case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } }
func (be *UdpBackend) RegisterNetwork(ctx context.Context, netname string, config *subnet.Config) (backend.Network, error) { cfg := struct { Port int }{ Port: defaultPort, } // Parse our configuration if len(config.Backend) > 0 { if err := json.Unmarshal(config.Backend, &cfg); err != nil { return nil, fmt.Errorf("error decoding UDP backend config: %v", err) } } // Acquire the lease form subnet manager attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(be.extIface.ExtAddr), } l, err := be.sm.AcquireLease(ctx, netname, &attrs) switch err { case nil: case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } // Tunnel's subnet is that of the whole overlay network (e.g. /16) // and not that of the individual host (e.g. /24) tunNet := ip.IP4Net{ IP: l.Subnet.IP, PrefixLen: config.Network.PrefixLen, } return newNetwork(netname, be.sm, be.extIface, cfg.Port, tunNet, l) }
func (m *AwsVpcBackend) Init(extIface *net.Interface, extIaddr net.IP, extEaddr net.IP) (*backend.SubnetDef, error) { // Parse our configuration if len(m.config.Backend) > 0 { if err := json.Unmarshal(m.config.Backend, &m.cfg); err != nil { return nil, fmt.Errorf("error decoding VPC backend config: %v", err) } } // Acquire the lease form subnet manager attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(extEaddr), } l, err := m.sm.AcquireLease(m.ctx, m.network, &attrs) switch err { case nil: m.lease = l case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } // Figure out this machine's EC2 instance ID and region identity, err := getInstanceIdentity() if err != nil { return nil, fmt.Errorf("error getting EC2 instance identity: %v", err) } instanceID, ok := identity["instanceId"].(string) if !ok { return nil, fmt.Errorf("invalid EC2 instance ID: %v", identity["instanceId"]) } regionVal, _ := identity["region"].(string) region, ok := aws.Regions[regionVal] if !ok { return nil, fmt.Errorf("invalid AWS region: %v", identity["region"]) } // Setup the EC2 client auth, err := aws.GetAuth("", "") if err != nil { return nil, fmt.Errorf("error getting AWS credentials from environment: %v", err) } ec2c := ec2.New(auth, region) if _, err = m.disableSrcDestCheck(instanceID, ec2c); err != nil { log.Infof("Warning- disabling source destination check failed: %v", err) } if m.cfg.RouteTableID == "" { log.Infof("RouteTableID not passed as config parameter, detecting ...") if err := m.detectRouteTableID(instanceID, ec2c); err != nil { return nil, err } } log.Info("RouteRouteTableID: ", m.cfg.RouteTableID) matchingRouteFound, err := m.checkMatchingRoutes(instanceID, l.Subnet.String(), ec2c) if err != nil { log.Errorf("Error describing route tables: %v", err) if ec2Err, ok := err.(*ec2.Error); ok { if ec2Err.Code == "UnauthorizedOperation" { log.Errorf("Note: DescribeRouteTables permission cannot be bound to any resource") } } } if !matchingRouteFound { if _, err := ec2c.DeleteRoute(m.cfg.RouteTableID, l.Subnet.String()); err != nil { if ec2err, ok := err.(*ec2.Error); !ok || ec2err.Code != "InvalidRoute.NotFound" { // an error other than the route not already existing occurred return nil, fmt.Errorf("error deleting existing route for %s: %v", l.Subnet.String(), err) } } // Add the route for this machine's subnet if _, err := m.createRoute(instanceID, l.Subnet.String(), ec2c); err != nil { return nil, fmt.Errorf("unable to add route %s: %v", l.Subnet.String(), err) } } return &backend.SubnetDef{ Net: l.Subnet, MTU: extIface.MTU, }, nil }
func (m *AwsVpcBackend) Init(extIface *net.Interface, extIaddr net.IP, extEaddr net.IP) (*backend.SubnetDef, error) { // Parse our configuration if len(m.config.Backend) > 0 { if err := json.Unmarshal(m.config.Backend, &m.cfg); err != nil { return nil, fmt.Errorf("error decoding VPC backend config: %v", err) } } // Acquire the lease form subnet manager attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(extEaddr), } l, err := m.sm.AcquireLease(m.ctx, m.network, &attrs) switch err { case nil: m.lease = l case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } // Figure out this machine's EC2 instance ID and region metadataClient := ec2metadata.New(nil) region, err := metadataClient.Region() if err != nil { return nil, fmt.Errorf("error getting EC2 region name: %v", err) } instanceID, err := metadataClient.GetMetadata("instance-id") if err != nil { return nil, fmt.Errorf("error getting EC2 instance ID: %v", err) } ec2c := ec2.New(&aws.Config{Region: aws.String(region)}) if _, err = m.disableSrcDestCheck(instanceID, ec2c); err != nil { log.Infof("Warning- disabling source destination check failed: %v", err) } if m.cfg.RouteTableID == "" { log.Infof("RouteTableID not passed as config parameter, detecting ...") if err := m.detectRouteTableID(instanceID, ec2c); err != nil { return nil, err } } log.Info("RouteRouteTableID: ", m.cfg.RouteTableID) matchingRouteFound, err := m.checkMatchingRoutes(instanceID, l.Subnet.String(), ec2c) if err != nil { log.Errorf("Error describing route tables: %v", err) if ec2Err, ok := err.(awserr.Error); ok { if ec2Err.Code() == "UnauthorizedOperation" { log.Errorf("Note: DescribeRouteTables permission cannot be bound to any resource") } } } if !matchingRouteFound { cidrBlock := l.Subnet.String() deleteRouteInput := &ec2.DeleteRouteInput{RouteTableId: &m.cfg.RouteTableID, DestinationCidrBlock: &cidrBlock} if _, err := ec2c.DeleteRoute(deleteRouteInput); err != nil { if ec2err, ok := err.(awserr.Error); !ok || ec2err.Code() != "InvalidRoute.NotFound" { // an error other than the route not already existing occurred return nil, fmt.Errorf("error deleting existing route for %s: %v", l.Subnet.String(), err) } } // Add the route for this machine's subnet if _, err := m.createRoute(instanceID, l.Subnet.String(), ec2c); err != nil { return nil, fmt.Errorf("unable to add route %s: %v", l.Subnet.String(), err) } } return &backend.SubnetDef{ Net: l.Subnet, MTU: extIface.MTU, }, nil }
func (g *GCEBackend) Init(extIface *net.Interface, extIP net.IP) (*backend.SubnetDef, error) { attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(extIP), } l, err := g.sm.AcquireLease(g.ctx, g.network, &attrs) switch err { case nil: g.lease = l case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } client, err := serviceaccount.NewClient(&serviceaccount.Options{}) if err != nil { return nil, fmt.Errorf("error creating client: %v", err) } g.computeService, err = compute.New(client) if err != nil { return nil, fmt.Errorf("error creating compute service: %v", err) } networkName, err := networkFromMetadata() if err != nil { return nil, fmt.Errorf("error getting network metadata: %v", err) } g.project, err = projectFromMetadata() if err != nil { return nil, fmt.Errorf("error getting project: %v", err) } instanceName, err := instanceNameFromMetadata() if err != nil { return nil, fmt.Errorf("error getting instance name: %v", err) } instanceZone, err := instanceZoneFromMetadata() if err != nil { return nil, fmt.Errorf("error getting instance zone: %v", err) } g.gceNetwork, err = g.computeService.Networks.Get(g.project, networkName).Do() if err != nil { return nil, fmt.Errorf("error getting network from compute service: %v", err) } g.gceInstance, err = g.computeService.Instances.Get(g.project, instanceZone, instanceName).Do() if err != nil { return nil, fmt.Errorf("error getting instance from compute service: %v", err) } found, err := g.handleMatchingRoute(l.Subnet.String()) if err != nil { return nil, fmt.Errorf("error handling matching route: %v", err) } if !found { operation, err := g.insertRoute(l.Subnet.String()) if err != nil { return nil, fmt.Errorf("error inserting route: %v", err) } err = g.pollOperationStatus(operation.Name) if err != nil { return nil, fmt.Errorf("insert operaiton failed: ", err) } } return &backend.SubnetDef{ Net: l.Subnet, MTU: extIface.MTU, }, nil }
func (m *AwsVpcBackend) Init(extIface *net.Interface, extIP net.IP) (*backend.SubnetDef, error) { // Parse our configuration if len(m.config.Backend) > 0 { if err := json.Unmarshal(m.config.Backend, &m.cfg); err != nil { return nil, fmt.Errorf("error decoding VPC backend config: %v", err) } } // Acquire the lease form subnet manager attrs := subnet.LeaseAttrs{ PublicIP: ip.FromIP(extIP), } l, err := m.sm.AcquireLease(m.ctx, m.network, &attrs) switch err { case nil: m.lease = l case context.Canceled, context.DeadlineExceeded: return nil, err default: return nil, fmt.Errorf("failed to acquire lease: %v", err) } // Figure out this machine's EC2 instance ID and region identity, err := getInstanceIdentity() if err != nil { return nil, fmt.Errorf("error getting EC2 instance identity: %v", err) } instanceID, ok := identity["instanceId"].(string) if !ok { return nil, fmt.Errorf("invalid EC2 instance ID: %v", identity["instanceId"]) } regionVal, _ := identity["region"].(string) region, ok := aws.Regions[regionVal] if !ok { return nil, fmt.Errorf("invalid AWS region: %v", identity["region"]) } // Setup the EC2 client auth, err := aws.GetAuth("", "") if err != nil { return nil, fmt.Errorf("error getting AWS credentials from environment: %v", err) } ec2c := ec2.New(auth, region) if m.cfg.RouteTableID == "" { log.Infof("RouteTableID not passed as config parameter, attempting to detect") routeTableID, err := m.DetectRouteTableID(instanceID, ec2c) if err != nil { return nil, err } log.Info("Detected routeRouteTableID: ", routeTableID) m.cfg.RouteTableID = routeTableID } filter := ec2.NewFilter() filter.Add("route.destination-cidr-block", l.Subnet.String()) filter.Add("route.state", "active") resp, err := ec2c.DescribeRouteTables([]string{m.cfg.RouteTableID}, filter) if err != nil { log.Errorf("Error describing route tables: %v", err) if ec2Err, ok := err.(*ec2.Error); ok { if ec2Err.Code == "UnauthorizedOperation" { log.Errorf("Note: describeRouteTables permission cannot be bound to any resource") } } } else { for _, routeTable := range resp.RouteTables { for _, route := range routeTable.Routes { if l.Subnet.String() == route.DestinationCidrBlock && route.State == "active" { log.Errorf("Matching *active* entry to: %s that will be deleted: %s, %s \n", l.Subnet.String(), route.DestinationCidrBlock, route.GatewayId) } } } } // Delete route for this machine's subnet if it already exists if _, err := ec2c.DeleteRoute(m.cfg.RouteTableID, l.Subnet.String()); err != nil { if ec2err, ok := err.(*ec2.Error); !ok || ec2err.Code != "InvalidRoute.NotFound" { // an error other than the route not already existing occurred return nil, fmt.Errorf("error deleting existing route for %s: %v", l.Subnet.String(), err) } } // Add the route for this machine's subnet route := &ec2.CreateRoute{ RouteTableId: m.cfg.RouteTableID, InstanceId: instanceID, DestinationCidrBlock: l.Subnet.String(), } if _, err := ec2c.CreateRoute(route); err != nil { return nil, fmt.Errorf("unable to add route %+v: %v", route, err) } return &backend.SubnetDef{ Net: l.Subnet, MTU: extIface.MTU, }, nil }