| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package netstack |
| |
| import ( |
| "context" |
| "fmt" |
| "strings" |
| "sync" |
| |
| "syslog" |
| |
| "netstack/dns" |
| "netstack/fidlconv" |
| "netstack/filter" |
| "netstack/link" |
| "netstack/link/bridge" |
| "netstack/link/eth" |
| "netstack/routes" |
| "netstack/util" |
| |
| "fidl/fuchsia/devicesettings" |
| "fidl/fuchsia/hardware/ethernet" |
| "fidl/fuchsia/net" |
| "fidl/fuchsia/netstack" |
| |
| "github.com/google/netstack/dhcp" |
| "github.com/google/netstack/tcpip" |
| "github.com/google/netstack/tcpip/header" |
| "github.com/google/netstack/tcpip/link/loopback" |
| "github.com/google/netstack/tcpip/link/sniffer" |
| "github.com/google/netstack/tcpip/network/arp" |
| "github.com/google/netstack/tcpip/network/ipv4" |
| "github.com/google/netstack/tcpip/network/ipv6" |
| "github.com/google/netstack/tcpip/stack" |
| ) |
| |
| const ( |
| deviceSettingsManagerNodenameKey = "DeviceName" |
| defaultNodename = "fuchsia-unset-device-name" |
| |
| defaultInterfaceMetric routes.Metric = 100 |
| |
| metricNotSet routes.Metric = 0 |
| |
| lowPriorityRoute routes.Metric = 99999 |
| |
| ipv4Loopback tcpip.Address = "\x7f\x00\x00\x01" |
| ipv6Loopback tcpip.Address = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" |
| |
| // Values used to indicate no IP is assigned to an interface. |
| zeroIpAddr tcpip.Address = header.IPv4Any |
| zeroIpMask tcpip.AddressMask = "\xff\xff\xff\xff" |
| ) |
| |
| // A Netstack tracks all of the running state of the network stack. |
| type Netstack struct { |
| arena *eth.Arena |
| |
| deviceSettings *devicesettings.DeviceSettingsManagerInterface |
| dnsClient *dns.Client |
| |
| mu struct { |
| sync.Mutex |
| stack *stack.Stack |
| routeTable routes.RouteTable |
| transactionRequest *netstack.RouteTableTransactionInterfaceRequest |
| countNIC tcpip.NICID |
| ifStates map[tcpip.NICID]*ifState |
| } |
| nodename string |
| sniff bool |
| |
| filter *filter.Filter |
| |
| OnInterfacesChanged func([]netstack.NetInterface2) |
| } |
| |
| // Each ifState tracks the state of a network interface. |
| type ifState struct { |
| ns *Netstack |
| eth link.Controller |
| nicid tcpip.NICID |
| // features can include any value that's valid in fuchsia.hardware.ethernet.Info.features. |
| features uint32 |
| mu struct { |
| sync.Mutex |
| state link.State |
| hasDynamicAddr bool |
| name string |
| // metric is used by default for routes that originate from this NIC. |
| metric routes.Metric |
| dnsServers []tcpip.Address |
| dhcp struct { |
| *dhcp.Client |
| // running must not be nil. |
| running func() bool |
| // cancel must not be nil. |
| cancel context.CancelFunc |
| // Used to restart the DHCP client when we go from link.StateDown to |
| // link.StateStarted. |
| enabled bool |
| } |
| } |
| |
| // The "outermost" LinkEndpoint implementation (the composition of link |
| // endpoint functionality happens by wrapping other link endpoints). |
| endpoint stack.LinkEndpoint |
| |
| bridgeable *bridge.BridgeableEndpoint |
| |
| filterEndpoint *filter.FilterEndpoint |
| } |
| |
| // defaultRoutes returns the IPv4 and IPv6 default routes. |
| func defaultRoutes(nicid tcpip.NICID, gateway tcpip.Address) []tcpip.Route { |
| return []tcpip.Route{ |
| { |
| Destination: tcpip.Address(strings.Repeat("\x00", 4)), |
| Mask: tcpip.AddressMask(strings.Repeat("\x00", 4)), |
| Gateway: gateway, |
| NIC: nicid, |
| }, |
| { |
| Destination: tcpip.Address(strings.Repeat("\x00", 16)), |
| Mask: tcpip.AddressMask(strings.Repeat("\x00", 16)), |
| NIC: nicid, |
| }, |
| } |
| } |
| |
| func subnetRoute(addr tcpip.Address, mask tcpip.AddressMask, nicid tcpip.NICID) tcpip.Route { |
| return tcpip.Route{ |
| Destination: util.ApplyMask(addr, mask), |
| Mask: tcpip.AddressMask(mask), |
| Gateway: tcpip.Address(""), |
| NIC: nicid, |
| } |
| } |
| |
| // AddRoute adds a single route to the route table in a sorted fashion. This |
| // takes the lock. |
| func (ns *Netstack) AddRoute(r tcpip.Route, metric routes.Metric, dynamic bool) error { |
| syslog.Infof("adding route %+v metric:%d dynamic=%v", r, metric, dynamic) |
| ns.mu.Lock() |
| defer ns.mu.Unlock() |
| return ns.AddRouteLocked(r, metric, dynamic) |
| } |
| |
| // AddRouteLocked adds a single route to the route table in a sorted fashion. It |
| // assumes the lock has already been taken. |
| func (ns *Netstack) AddRouteLocked(r tcpip.Route, metric routes.Metric, dynamic bool) error { |
| return ns.AddRoutesLocked([]tcpip.Route{r}, metric, dynamic) |
| } |
| |
| // AddRoutesLocked adds one or more routes to the route table in a sorted |
| // fashion. It assumes the lock has already been taken. |
| func (ns *Netstack) AddRoutesLocked(rs []tcpip.Route, metric routes.Metric, dynamic bool) error { |
| metricTracksInterface := false |
| if metric == metricNotSet { |
| metricTracksInterface = true |
| } |
| |
| for _, r := range rs { |
| // If we don't have an interface set, find it using the gateway address. |
| if r.NIC == 0 { |
| nic, err := ns.mu.routeTable.FindNIC(r.Gateway) |
| if err != nil { |
| return fmt.Errorf("error finding NIC for gateway %v: %s", r.Gateway, err) |
| } |
| r.NIC = nic |
| } |
| |
| ifs, ok := ns.mu.ifStates[r.NIC] |
| if !ok { |
| return fmt.Errorf("error getting ifState for NIC %d, not in map", r.NIC) |
| } |
| |
| enabled := ifs.mu.state == link.StateStarted |
| if metricTracksInterface { |
| metric = ifs.mu.metric |
| } |
| |
| ns.mu.routeTable.AddRoute(r, metric, metricTracksInterface, dynamic, enabled) |
| } |
| ns.mu.stack.SetRouteTable(ns.mu.routeTable.GetNetstackTable()) |
| return nil |
| } |
| |
| // DelRoute deletes a single route from the route table. This takes the lock. |
| func (ns *Netstack) DelRoute(r tcpip.Route) error { |
| syslog.Infof("deleting route %+v", r) |
| ns.mu.Lock() |
| defer ns.mu.Unlock() |
| return ns.DelRouteLocked(r) |
| } |
| |
| // DelRoute deletes a single route from the route table. It assumes the lock has |
| // already been taken. |
| func (ns *Netstack) DelRouteLocked(r tcpip.Route) error { |
| if err := ns.mu.routeTable.DelRoute(r); err != nil { |
| return fmt.Errorf("error deleting route, %s", err) |
| } |
| ns.mu.stack.SetRouteTable(ns.mu.routeTable.GetNetstackTable()) |
| return nil |
| } |
| |
| // GetExtendedRouteTable returns a copy of the current extended route table. |
| // This takes the lock. |
| func (ns *Netstack) GetExtendedRouteTable() []routes.ExtendedRoute { |
| ns.mu.Lock() |
| defer ns.mu.Unlock() |
| return ns.mu.routeTable.GetExtendedRouteTable() |
| } |
| |
| // UpdateRoutesByInterfaceLocked applies update actions to the routes for a |
| // given interface. It assumes the lock has already been taken. |
| func (ns *Netstack) UpdateRoutesByInterfaceLocked(nicid tcpip.NICID, action routes.Action) { |
| ns.mu.routeTable.UpdateRoutesByInterface(nicid, action) |
| ns.mu.stack.SetRouteTable(ns.mu.routeTable.GetNetstackTable()) |
| } |
| |
| // UpdateInterfaceMetric changes the metric for an interface and updates all |
| // routes tracking that interface metric. This takes the lock. |
| func (ns *Netstack) UpdateInterfaceMetric(nicid tcpip.NICID, metric routes.Metric) error { |
| syslog.Infof("update interface metric for NIC %d to metric=%d", nicid, metric) |
| |
| ns.mu.Lock() |
| defer ns.mu.Unlock() |
| |
| ifState, ok := ns.mu.ifStates[tcpip.NICID(nicid)] |
| if !ok { |
| return fmt.Errorf("error getting ifState for NIC %d, not in map", nicid) |
| } |
| ifState.updateMetric(metric) |
| |
| ns.mu.routeTable.UpdateMetricByInterface(nicid, metric) |
| ns.mu.stack.SetRouteTable(ns.mu.routeTable.GetNetstackTable()) |
| return nil |
| } |
| |
| func (ns *Netstack) removeInterfaceAddress(nic tcpip.NICID, protocol tcpip.NetworkProtocolNumber, addr tcpip.Address, prefixLen uint8) error { |
| subnet, err := toSubnet(addr, prefixLen) |
| if err != nil { |
| return fmt.Errorf("error parsing subnet format for NIC ID %d: %s", nic, err) |
| } |
| route := subnetRoute(addr, subnet.Mask(), nic) |
| syslog.Infof("removing static IP %v/%d from NIC %d, deleting subnet route %+v", addr, prefixLen, nic, route) |
| |
| ns.mu.Lock() |
| if err := func() error { |
| addresses, subnets := ns.getAddressesLocked(nic) |
| allOnes := int(prefixLen) == 8*len(addr) |
| hasAddr := containsAddress(addresses, protocol, addr) |
| hasSubnet := containsSubnet(subnets, addr, prefixLen) |
| if !hasAddr && !hasSubnet { |
| return fmt.Errorf("neither address nor subnet %s/%d exists on NIC ID %d", addr, prefixLen, nic) |
| } |
| |
| if !allOnes { |
| if hasSubnet { |
| if err := ns.mu.stack.RemoveSubnet(nic, subnet); err == tcpip.ErrUnknownNICID { |
| panic(fmt.Sprintf("stack.RemoveSubnet(_): NIC [%d] not found", nic)) |
| } else if err != nil { |
| return fmt.Errorf("error removing subnet %+v from NIC ID %d: %s", subnet, nic, err) |
| } |
| } else { |
| return fmt.Errorf("no such subnet %+v for NIC ID %d", subnet, nic) |
| } |
| } |
| |
| ns.DelRouteLocked(route) |
| |
| if allOnes && hasAddr { |
| if err := ns.mu.stack.RemoveAddress(nic, addr); err == tcpip.ErrUnknownNICID { |
| panic(fmt.Sprintf("stack.RemoveAddress(_): NIC [%d] not found", nic)) |
| } else if err != nil { |
| return fmt.Errorf("error removing address %s from NIC ID %d: %s", addr, nic, err) |
| } |
| } |
| |
| newAddr := zeroIpAddr |
| newNetmask := zeroIpMask |
| // Check if the NIC still has other primary IPs and use that one. |
| if mainAddr, subnet, err := ns.mu.stack.GetMainNICAddress(nic, protocol); err == nil { |
| newAddr = mainAddr |
| newNetmask = subnet.Mask() |
| if newNetmask == "" { |
| addressSize := len(newAddr) * 8 |
| newNetmask = util.CIDRMask(addressSize, addressSize) |
| } |
| } |
| return nil |
| }(); err != nil { |
| ns.mu.Unlock() |
| return err |
| } |
| |
| interfaces := ns.getNetInterfaces2Locked() |
| ns.mu.Unlock() |
| ns.OnInterfacesChanged(interfaces) |
| return nil |
| } |
| |
| func toSubnet(address tcpip.Address, prefixLen uint8) (tcpip.Subnet, error) { |
| m := util.CIDRMask(int(prefixLen), int(len(address)*8)) |
| return tcpip.NewSubnet(util.ApplyMask(address, m), m) |
| } |
| |
| func (ns *Netstack) addInterfaceAddress(nic tcpip.NICID, protocol tcpip.NetworkProtocolNumber, addr tcpip.Address, prefixLen uint8) error { |
| subnet, err := toSubnet(addr, prefixLen) |
| if err != nil { |
| return fmt.Errorf("error parsing subnet format for NIC ID %d: %s", nic, err) |
| } |
| route := subnetRoute(addr, subnet.Mask(), nic) |
| syslog.Infof("adding static IP %v/%d to NIC %d, creating subnet route %+v with metric=<not-set>, dynamic=false", addr, prefixLen, nic, route) |
| |
| ns.mu.Lock() |
| if err := func() error { |
| addresses, subnets := ns.getAddressesLocked(nic) |
| hasAddr := containsAddress(addresses, protocol, addr) |
| hasSubnet := containsSubnet(subnets, addr, prefixLen) |
| if hasAddr && hasSubnet { |
| return fmt.Errorf("address/prefix combination %s/%d already exists on NIC ID %d", addr, prefixLen, nic) |
| } |
| if !hasAddr { |
| if err := ns.mu.stack.AddAddress(nic, protocol, addr); err != nil { |
| return fmt.Errorf("error adding address %s to NIC ID %d: %s", addr, nic, err) |
| } |
| } |
| |
| if !hasSubnet { |
| if err := ns.mu.stack.AddSubnet(nic, protocol, subnet); err != nil { |
| return fmt.Errorf("error adding subnet %+v to NIC ID %d: %s", subnet, nic, err) |
| } |
| } |
| |
| if err := ns.AddRouteLocked(route, metricNotSet, false); err != nil { |
| return fmt.Errorf("error adding subnet route %v to NIC ID %d: %s", route, nic, err) |
| } |
| return nil |
| }(); err != nil { |
| ns.mu.Unlock() |
| return err |
| } |
| |
| interfaces := ns.getNetInterfaces2Locked() |
| ns.mu.Unlock() |
| ns.OnInterfacesChanged(interfaces) |
| return nil |
| } |
| |
| func (ifs *ifState) updateMetric(metric routes.Metric) { |
| ifs.mu.Lock() |
| ifs.mu.metric = metric |
| ifs.mu.Unlock() |
| } |
| |
| func (ifs *ifState) dhcpAcquired(oldAddr, newAddr tcpip.Address, config dhcp.Config) { |
| if oldAddr != "" && oldAddr != newAddr { |
| syslog.Infof("NIC %s: DHCP IP %s expired", ifs.mu.name, oldAddr) |
| } |
| if config.Error != nil { |
| syslog.Errorf("%v", config.Error) |
| return |
| } |
| if newAddr == "" { |
| syslog.Errorf("NIC %s: DHCP could not acquire address", ifs.mu.name) |
| return |
| } |
| syslog.Infof("NIC %s: DHCP acquired IP %s for %s", ifs.mu.name, newAddr, config.LeaseLength) |
| syslog.Infof("NIC %s: Adding DNS servers: %v", ifs.mu.name, config.DNS) |
| |
| ifs.mu.Lock() |
| ifs.mu.hasDynamicAddr = true |
| ifs.mu.dnsServers = config.DNS |
| ifs.mu.Unlock() |
| |
| // Add a default route and a route for the local subnet. |
| rs := defaultRoutes(ifs.nicid, config.Gateway) |
| rs = append(rs, subnetRoute(newAddr, config.SubnetMask, ifs.nicid)) |
| syslog.Infof("adding routes %+v with metric=<not-set> dynamic=true", rs) |
| |
| ifs.ns.mu.Lock() |
| if err := ifs.ns.AddRoutesLocked(rs, metricNotSet, true /* dynamic */); err != nil { |
| syslog.Infof("error adding routes for DHCP address/gateway: %v", err) |
| } |
| interfaces := ifs.ns.getNetInterfaces2Locked() |
| ifs.ns.mu.Unlock() |
| |
| ifs.ns.dnsClient.SetRuntimeServers(ifs.ns.getRuntimeDNSServerRefs()) |
| ifs.ns.OnInterfacesChanged(interfaces) |
| } |
| |
| func (ifs *ifState) setDHCPStatusLocked(enabled bool) { |
| ifs.mu.dhcp.enabled = enabled |
| ifs.mu.dhcp.cancel() |
| if ifs.mu.dhcp.enabled && ifs.mu.state == link.StateStarted { |
| ifs.runDHCPLocked() |
| } |
| } |
| |
| // Runs the DHCP client with a fresh context and initializes ifs.mu.dhcp.cancel. |
| // Call the old cancel function before calling this function. |
| func (ifs *ifState) runDHCPLocked() { |
| ctx, cancel := context.WithCancel(context.Background()) |
| ifs.mu.dhcp.cancel = cancel |
| ifs.mu.dhcp.running = func() bool { |
| return ctx.Err() == nil |
| } |
| if c := ifs.mu.dhcp.Client; c != nil { |
| c.Run(ctx) |
| } else { |
| panic(fmt.Sprintf("nil DHCP client on interface %s", ifs.mu.name)) |
| } |
| } |
| |
| func (ifs *ifState) dhcpEnabled() bool { |
| ifs.mu.Lock() |
| defer ifs.mu.Unlock() |
| return ifs.mu.dhcp.enabled |
| } |
| |
| func (ifs *ifState) stateChange(s link.State) { |
| ifs.ns.mu.Lock() |
| ifs.mu.Lock() |
| switch s { |
| case link.StateClosed: |
| delete(ifs.ns.mu.ifStates, ifs.nicid) |
| fallthrough |
| case link.StateDown: |
| syslog.Infof("NIC %s: stopped", ifs.mu.name) |
| ifs.mu.dhcp.cancel() |
| |
| // TODO(crawshaw): more cleanup to be done here: |
| // - remove link endpoint |
| // - reclaim NICID? |
| |
| if ifs.mu.hasDynamicAddr || s == link.StateClosed { |
| syslog.Infof("removing IP from NIC %d", ifs.nicid) |
| ifs.mu.dnsServers = nil |
| } |
| |
| if s == link.StateClosed { |
| // The interface is removed, force all of its routes to be removed. |
| ifs.ns.UpdateRoutesByInterfaceLocked(ifs.nicid, routes.ActionDeleteAll) |
| } else { |
| // The interface is down, delete dynamic routes, disable static ones. |
| ifs.ns.UpdateRoutesByInterfaceLocked(ifs.nicid, routes.ActionDeleteDynamicDisableStatic) |
| } |
| |
| case link.StateStarted: |
| syslog.Infof("NIC %s: starting", ifs.mu.name) |
| // Re-enable static routes out this interface. |
| ifs.ns.UpdateRoutesByInterfaceLocked(ifs.nicid, routes.ActionEnableStatic) |
| if ifs.mu.dhcp.enabled { |
| ifs.mu.dhcp.cancel() |
| ifs.runDHCPLocked() |
| } |
| // TODO(ckuiper): Remove this, as we shouldn't create default routes w/o a |
| // gateway given. Before doing so make sure nothing is still relying on |
| // this. |
| // Update the state before adding the routes, so they are properly enabled. |
| ifs.mu.state = s |
| if err := ifs.ns.AddRoutesLocked(defaultRoutes(ifs.nicid, ""), lowPriorityRoute, true /* dynamic */); err != nil { |
| syslog.Infof("error adding default routes: %v", err) |
| } |
| } |
| ifs.mu.state = s |
| ifs.mu.Unlock() |
| |
| interfaces := ifs.ns.getNetInterfaces2Locked() |
| ifs.ns.mu.Unlock() |
| |
| ifs.ns.dnsClient.SetRuntimeServers(ifs.ns.getRuntimeDNSServerRefs()) |
| ifs.ns.OnInterfacesChanged(interfaces) |
| } |
| |
| // Return a slice of references to each NIC's DNS servers. |
| // The caller takes ownership of the returned slice. |
| func (ns *Netstack) getRuntimeDNSServerRefs() []*[]tcpip.Address { |
| ns.mu.Lock() |
| defer ns.mu.Unlock() |
| |
| refs := make([]*[]tcpip.Address, 0, len(ns.mu.ifStates)) |
| for _, ifs := range ns.mu.ifStates { |
| ifs.mu.Lock() |
| refs = append(refs, &ifs.mu.dnsServers) |
| ifs.mu.Unlock() |
| } |
| return refs |
| } |
| |
| func (ns *Netstack) getdnsServers() []tcpip.Address { |
| defaultServers := ns.dnsClient.GetDefaultServers() |
| uniqServers := make(map[tcpip.Address]struct{}) |
| |
| ns.mu.Lock() |
| for _, ifs := range ns.mu.ifStates { |
| ifs.mu.Lock() |
| for _, server := range ifs.mu.dnsServers { |
| uniqServers[server] = struct{}{} |
| } |
| ifs.mu.Unlock() |
| } |
| ns.mu.Unlock() |
| |
| out := make([]tcpip.Address, 0, len(defaultServers)+len(uniqServers)) |
| out = append(out, defaultServers...) |
| for server := range uniqServers { |
| out = append(out, server) |
| } |
| return out |
| } |
| |
| func (ns *Netstack) getNodeName() string { |
| nodename, status, err := ns.deviceSettings.GetString(deviceSettingsManagerNodenameKey) |
| if err != nil { |
| syslog.Warnf("getNodeName: error accessing device settings: %s", err) |
| return defaultNodename |
| } |
| if status != devicesettings.StatusOk { |
| var reportStatus string |
| switch status { |
| case devicesettings.StatusErrNotSet: |
| reportStatus = "key not set" |
| case devicesettings.StatusErrInvalidSetting: |
| reportStatus = "invalid setting" |
| case devicesettings.StatusErrRead: |
| reportStatus = "error reading key" |
| case devicesettings.StatusErrIncorrectType: |
| reportStatus = "value type was incorrect" |
| case devicesettings.StatusErrUnknown: |
| reportStatus = "unknown" |
| default: |
| reportStatus = fmt.Sprintf("unknown status code: %d", status) |
| } |
| syslog.Warnf("getNodeName: device settings error: %s", reportStatus) |
| return defaultNodename |
| } |
| return nodename |
| } |
| |
| // TODO(stijlist): figure out a way to make it impossible to accidentally |
| // enable DHCP on loopback interfaces. |
| func (ns *Netstack) addLoopback() error { |
| ifs, err := ns.addEndpoint(func(tcpip.NICID) string { |
| return "lo" |
| }, stack.FindLinkEndpoint(loopback.New()), link.NewLoopbackController(), false, defaultInterfaceMetric, ethernet.InfoFeatureLoopback) |
| if err != nil { |
| return err |
| } |
| |
| ifs.mu.Lock() |
| ifs.mu.state = link.StateStarted |
| nicid := ifs.nicid |
| ifs.mu.Unlock() |
| |
| if err := ns.mu.stack.AddAddress(nicid, ipv4.ProtocolNumber, ipv4Loopback); err != nil { |
| return fmt.Errorf("loopback: adding ipv4 address failed: %v", err) |
| } |
| if err := ns.mu.stack.AddAddress(nicid, ipv6.ProtocolNumber, ipv6Loopback); err != nil { |
| return fmt.Errorf("loopback: adding ipv6 address failed: %v", err) |
| } |
| |
| if err := ns.AddRoutesLocked( |
| []tcpip.Route{ |
| { |
| Destination: ipv4Loopback, |
| Mask: tcpip.AddressMask(strings.Repeat("\xff", 4)), |
| NIC: nicid, |
| }, |
| { |
| Destination: ipv6Loopback, |
| Mask: tcpip.AddressMask(strings.Repeat("\xff", 16)), |
| NIC: nicid, |
| }, |
| }, |
| metricNotSet, /* use interface metric */ |
| false, /* dynamic */ |
| ); err != nil { |
| return fmt.Errorf("loopback: adding routes failed: %v", err) |
| } |
| |
| return nil |
| } |
| |
| func (ns *Netstack) Bridge(nics []tcpip.NICID) (*ifState, error) { |
| links := make([]*bridge.BridgeableEndpoint, 0, len(nics)) |
| ns.mu.Lock() |
| for _, nicid := range nics { |
| ifs, ok := ns.mu.ifStates[nicid] |
| if !ok { |
| panic("NIC known by netstack not in interface table") |
| } |
| if err := ifs.eth.SetPromiscuousMode(true); err != nil { |
| return nil, err |
| } |
| links = append(links, ifs.bridgeable) |
| } |
| ns.mu.Unlock() |
| |
| b := bridge.New(links) |
| return ns.addEndpoint(func(nicid tcpip.NICID) string { |
| return fmt.Sprintf("br%d", nicid) |
| }, b, b, false, defaultInterfaceMetric, 0) |
| } |
| |
| func (ns *Netstack) addEth(topological_path string, config netstack.InterfaceConfig, device ethernet.Device) (*ifState, error) { |
| client, err := eth.NewClient("netstack", topological_path, device, ns.arena) |
| if err != nil { |
| return nil, err |
| } |
| |
| return ns.addEndpoint(func(nicid tcpip.NICID) string { |
| if len(config.Name) == 0 { |
| return fmt.Sprintf("eth%d", nicid) |
| } |
| return config.Name |
| }, eth.NewLinkEndpoint(client), client, true, routes.Metric(config.Metric), client.Info.Features) |
| } |
| |
| func (ns *Netstack) addEndpoint( |
| nameFn func(nicid tcpip.NICID) string, |
| ep stack.LinkEndpoint, |
| controller link.Controller, |
| doFilter bool, |
| metric routes.Metric, |
| features uint32, |
| ) (*ifState, error) { |
| ifs := &ifState{ |
| ns: ns, |
| eth: controller, |
| features: features, |
| } |
| ifs.mu.state = link.StateUnknown |
| ifs.mu.metric = metric |
| ifs.mu.dhcp.running = func() bool { return false } |
| ifs.mu.dhcp.cancel = func() {} |
| |
| ifs.eth.SetOnStateChange(ifs.stateChange) |
| linkID := stack.RegisterLinkEndpoint(ep) |
| |
| // LinkEndpoint chains: |
| // Put sniffer as close as the NIC. |
| if ns.sniff { |
| // A wrapper LinkEndpoint should encapsulate the underlying |
| // one, and manifest itself to 3rd party netstack. |
| linkID = sniffer.New(linkID) |
| } |
| |
| if doFilter { |
| linkID, ifs.filterEndpoint = filter.NewFilterEndpoint(ns.filter, linkID) |
| } |
| linkID, ifs.bridgeable = bridge.NewEndpoint(linkID) |
| ifs.endpoint = ifs.bridgeable |
| |
| ns.mu.Lock() |
| defer ns.mu.Unlock() |
| |
| ifs.nicid = ns.mu.countNIC + 1 |
| name := nameFn(ifs.nicid) |
| ns.mu.ifStates[ifs.nicid] = ifs |
| ns.mu.countNIC++ |
| |
| syslog.Infof("NIC %s added [sniff = %t]", name, ns.sniff) |
| |
| if err := ns.mu.stack.CreateNIC(ifs.nicid, linkID); err != nil { |
| return nil, fmt.Errorf("NIC %s: could not create NIC: %v", ifs.mu.name, err) |
| } |
| if ep.Capabilities()&stack.CapabilityResolutionRequired > 0 { |
| if err := ns.mu.stack.AddAddress(ifs.nicid, arp.ProtocolNumber, arp.ProtocolAddress); err != nil { |
| return nil, fmt.Errorf("NIC %s: adding arp address failed: %v", ifs.mu.name, err) |
| } |
| } |
| |
| ifs.mu.Lock() |
| defer ifs.mu.Unlock() |
| |
| if linkAddr := ep.LinkAddress(); len(linkAddr) > 0 { |
| lladdr := header.LinkLocalAddr(linkAddr) |
| if err := ns.mu.stack.AddAddress(ifs.nicid, ipv6.ProtocolNumber, lladdr); err != nil { |
| return nil, fmt.Errorf("NIC %s: adding link-local IPv6 %v failed: %v", ifs.mu.name, lladdr, err) |
| } |
| snaddr := header.SolicitedNodeAddr(lladdr) |
| if err := ns.mu.stack.AddAddress(ifs.nicid, ipv6.ProtocolNumber, snaddr); err != nil { |
| return nil, fmt.Errorf("NIC %s: adding solicited-node IPv6 %v (link-local IPv6 %v) failed: %v", ifs.mu.name, snaddr, lladdr, err) |
| } |
| |
| ifs.mu.dhcp.Client = dhcp.NewClient(ns.mu.stack, ifs.nicid, linkAddr, ifs.dhcpAcquired) |
| |
| syslog.Infof("NIC %s: link-local IPv6: %v", name, lladdr) |
| } |
| |
| ifs.mu.name = name |
| |
| return ifs, nil |
| } |
| |
| func (ns *Netstack) validateInterfaceAddress(address net.IpAddress, prefixLen uint8) (tcpip.NetworkProtocolNumber, tcpip.Address, netstack.NetErr) { |
| var protocol tcpip.NetworkProtocolNumber |
| switch address.Which() { |
| case net.IpAddressIpv4: |
| protocol = ipv4.ProtocolNumber |
| case net.IpAddressIpv6: |
| return 0, "", netstack.NetErr{Status: netstack.StatusIpv4Only, Message: "IPv6 not yet supported"} |
| } |
| |
| addr := fidlconv.ToTCPIPAddress(address) |
| |
| if (8 * len(addr)) < int(prefixLen) { |
| return 0, "", netstack.NetErr{Status: netstack.StatusParseError, Message: "prefix length exceeds address length"} |
| } |
| |
| return protocol, addr, netstack.NetErr{Status: netstack.StatusOk} |
| } |
| |
| func (ns *Netstack) getAddressesLocked(nic tcpip.NICID) ([]tcpip.ProtocolAddress, []tcpip.Subnet) { |
| nicInfo := ns.mu.stack.NICInfo() |
| nicSubnets := ns.mu.stack.NICSubnets() |
| |
| info, ok := nicInfo[nic] |
| if !ok { |
| panic(fmt.Sprintf("NIC [%d] not found in %+v", nic, nicInfo)) |
| } |
| subnets, ok := nicSubnets[nic] |
| if !ok { |
| panic(fmt.Sprintf("NIC [%d] not found in %+v", nic, nicSubnets)) |
| } |
| |
| return info.ProtocolAddresses, subnets |
| } |
| |
| func containsAddress(addresses []tcpip.ProtocolAddress, protocol tcpip.NetworkProtocolNumber, addr tcpip.Address) bool { |
| for _, a := range addresses { |
| if a.Protocol == protocol && a.Address == addr { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func containsSubnet(subnets []tcpip.Subnet, addr tcpip.Address, prefixLen uint8) bool { |
| for _, s := range subnets { |
| if s.ID() == util.ApplyMask(addr, util.CIDRMask(int(prefixLen), 8*len(addr))) && uint8(s.Prefix()) == prefixLen { |
| return true |
| } |
| } |
| return false |
| } |