// Return true if we successfully sent an ARP request, false otherwise bool IPv4Handler::resolveMac(SwitchState* state, IPAddressV4 dest) { // need to find out our own IP and MAC addresses so that we can send the // ARP request out. Since the request will be broadcast, there is no need to // worry about which port to send the packet out. // TODO: assume vrf 0 now auto routeTable = state->getRouteTables()->getRouteTableIf(RouterID(0)); if (!routeTable) { throw FbossError("No routing tables found"); } auto route = routeTable->getRibV4()->longestMatch(dest); if (!route || !route->isResolved()) { // No way to reach dest return false; } auto intfs = state->getInterfaces(); auto nhs = route->getForwardInfo().getNexthops(); for (auto nh : nhs) { auto intf = intfs->getInterfaceIf(nh.intf); if (intf) { auto source = intf->getAddressToReach(nh.nexthop)->first.asV4(); auto target = route->isConnected() ? dest : nh.nexthop.asV4(); if (source == target) { // This packet is for us. Don't send ARP requess for our own IP. continue; } auto vlanID = intf->getVlanID(); auto vlan = state->getVlans()->getVlanIf(vlanID); if (vlan) { auto entry = vlan->getArpTable()->getEntryIf(target); if (entry == nullptr) { // No entry in ARP table, send ARP request auto mac = intf->getMac(); ArpHandler::sendArpRequest(sw_, vlanID, mac, source, target); // Notify the updater that we sent an arp request sw_->getNeighborUpdater()->sentArpRequest(vlanID, target); } else { VLOG(4) << "not sending arp for " << target.str() << ", " << ((entry->isPending()) ? "pending " : "") << "entry already exists"; } } } } return true; }
void UnresolvedNhopsProber::timeoutExpired() noexcept { std::lock_guard<std::mutex> g(lock_); auto state = sw_->getState(); for (const auto& ridAndNhopsRefCounts : nhops2RouteCount_) { for (const auto& nhopAndRefCount : ridAndNhopsRefCounts.second) { const auto& nhop = nhopAndRefCount.first; auto intf = state->getInterfaces()->getInterfaceIf(nhop.intf()); if (!intf) { continue; // interface got unconfigured } // Probe all nexthops for which either don't have a L2 entry // or the entry is not resolved (port == 0). Note that we do // not exclude pending entries here since in case of recursive // routes we might get packets with destination set to prefix // that needs to be resolved recursively. In ARP and NDP code // we do not do route lookup when deciding to send ARP/NDP requests. // So we would only try to ARP/NDP for the destination if it // is in one of the interface subnets (which it won't be else // we won't have needed recursive resolution). So ARP/NDP for // all unresolved next hops. We could also consider doing route // lookups in ARP/NDP code, but by probing all unresolved next // hops we effectively do the same thing, since the next hops // probed come from after the route was (recursively) resolved. auto vlan = state->getVlans()->getVlanIf(intf->getVlanID()); CHECK(vlan); // must have vlan for configrued inteface if (nhop.addr().isV4()) { auto nhop4 = nhop.addr().asV4(); auto arpEntry = vlan->getArpTable()->getEntryIf(nhop4); if (!arpEntry || arpEntry->getPort() == PortID(0)) { VLOG(3) <<" Sending probe for unresolved next hop: " << nhop4; ArpHandler::sendArpRequest(sw_, vlan, nhop4); } } else { auto nhop6 = nhop.addr().asV6(); auto ndpEntry = vlan->getNdpTable()->getEntryIf(nhop6); if (!ndpEntry || ndpEntry->getPort() == PortID(0)) { VLOG(3) <<" Sending probe for unresolved next hop: " << nhop6; IPv6Handler::sendNeighborSolicitation(sw_, nhop6, vlan); } } } } scheduleTimeout(interval_); }