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.nexthop.isV4()) { auto nhop4 = nhop.nexthop.asV4(); auto arpEntry = vlan->getArpTable()->getEntryIf(nhop4); if (!arpEntry || arpEntry->getPort() == 0) { VLOG(2) <<" Sending probe for unresolved next hop: " << nhop4; ArpHandler::sendArpRequest(sw_, vlan, nhop4); } } else { auto nhop6 = nhop.nexthop.asV6(); auto ndpEntry = vlan->getNdpTable()->getEntryIf(nhop6); if (!ndpEntry || ndpEntry->getPort() == 0) { VLOG(2) <<" Sending probe for unresolved next hop: " << nhop6; IPv6Handler::sendNeighborSolicitation(sw_, nhop6, vlan); } } } } scheduleTimeout(interval_); }
void IPv4Handler::handlePacket(unique_ptr<RxPacket> pkt, MacAddress dst, MacAddress src, Cursor cursor) { SwitchStats* stats = sw_->stats(); PortID port = pkt->getSrcPort(); const uint32_t l3Len = pkt->getLength() - (cursor - Cursor(pkt->buf())); stats->port(port)->ipv4Rx(); IPv4Hdr v4Hdr(cursor); VLOG(4) << "Rx IPv4 packet (" << l3Len << " bytes) " << v4Hdr.srcAddr.str() << " --> " << v4Hdr.dstAddr.str() << " proto: 0x" << std::hex << static_cast<int>(v4Hdr.protocol); // retrieve the current switch state auto state = sw_->getState(); // Need to check if the packet is for self or not. We store our IP // in the ARP response table. Use that for now. auto vlan = state->getVlans()->getVlanIf(pkt->getSrcVlan()); if (!vlan) { stats->port(port)->pktDropped(); return; } if (v4Hdr.protocol == IPPROTO_UDP) { Cursor udpCursor(cursor); UDPHeader udpHdr; udpHdr.parse(sw_, port, &udpCursor); VLOG(4) << "UDP packet, Source port :" << udpHdr.srcPort << " destination port: " << udpHdr.dstPort; if (DHCPv4Handler::isDHCPv4Packet(udpHdr)) { DHCPv4Handler::handlePacket(sw_, std::move(pkt), src, dst, v4Hdr, udpHdr, udpCursor); return; } } auto dstIP = v4Hdr.dstAddr; // Handle packets destined for us // TODO: assume vrf 0 now if (state->getInterfaces()->getInterfaceIf(RouterID(0), IPAddress(dstIP))) { // TODO: Also check to see if this is the broadcast address for one of the // interfaces on this VLAN. We should probably build up a more efficient // data structure to look up this information. stats->port(port)->ipv4Mine(); // Anything not handled by the controller, we will forward it to the host, // i.e. ping, ssh, bgp... // FixME: will do another diff to set length in RxPacket, so that it // can be reused here. if (sw_->sendPacketToHost(std::move(pkt))) { stats->port(port)->pktToHost(l3Len); } else { stats->port(port)->pktDropped(); } return; } // if packet is not for us, check the ttl exceed if (v4Hdr.ttl <= 1) { VLOG(4) << "Rx IPv4 Packet with TTL expired"; stats->port(port)->pktDropped(); stats->port(port)->ipv4TtlExceeded(); // Look up cpu mac from platform MacAddress cpuMac = sw_->getPlatform()->getLocalMac(); sendICMPTimeExceeded(pkt->getSrcVlan(), cpuMac, cpuMac, v4Hdr, cursor); return; } // Handle broadcast packets. // TODO: Also check to see if this is the broadcast address for one of the // interfaces on this VLAN. We should probably build up a more efficient // data structure to look up this information. if (dstIP.isLinkLocalBroadcast()) { stats->port(port)->pktDropped(); return; } // TODO: check the reason of punt, for now, assume it is for // resolving the address // We will need to manage the rate somehow. Either from HW // or a SW control here stats->port(port)->ipv4Nexthop(); if (!resolveMac(state.get(), dstIP)) { stats->port(port)->ipv4NoArp(); VLOG(3) << "Cannot find the interface to send out ARP request for " << dstIP.str(); } // TODO: ideally, we need to store this packet until the ARP is done and // then send this pkt out. For now, just drop it. stats->port(port)->pktDropped(); }
shared_ptr<SwitchState> ThriftConfigApplier::run() { auto newState = orig_->clone(); bool changed = false; processVlanPorts(); { auto newPorts = updatePorts(); if (newPorts) { newState->resetPorts(std::move(newPorts)); changed = true; } } { auto newIntfs = updateInterfaces(); if (newIntfs) { newState->resetIntfs(std::move(newIntfs)); changed = true; } } // Note: updateInterfaces() must be called before updateVlans(), // as updateInterfaces() populates the vlanInterfaces_ data structure. { auto newVlans = updateVlans(); if (newVlans) { newState->resetVlans(std::move(newVlans)); changed = true; } } // Note: updateInterfaces() must be called before updateRouteTables(), // as updateInterfaces() populates the intfRouteTables_ data structure. { auto newTables = updateRouteTables(); if (newTables) { newState->resetRouteTables(std::move(newTables)); changed = true; } } // Make sure all interfaces refer to valid VLANs. auto newVlans = newState->getVlans(); for (const auto& vlanInfo : vlanInterfaces_) { if (newVlans->getVlanIf(vlanInfo.first) == nullptr) { throw FbossError("Interface ", *(vlanInfo.second.interfaces.begin()), " refers to non-existent VLAN ", vlanInfo.first); } } VlanID dfltVlan(cfg_->defaultVlan); if (orig_->getDefaultVlan() != dfltVlan) { if (newVlans->getVlanIf(dfltVlan) == nullptr) { throw FbossError("Default VLAN ", dfltVlan, " does not exist"); } newState->setDefaultVlan(dfltVlan); changed = true; } std::chrono::seconds arpAgerInterval(cfg_->arpAgerInterval); if (orig_->getArpAgerInterval() != arpAgerInterval) { newState->setArpAgerInterval(arpAgerInterval); changed = true; } if (!changed) { return nullptr; } return newState; }