shared_ptr<Interface> ThriftConfigApplier::updateInterface( const shared_ptr<Interface>& orig, const cfg::Interface* config, const Interface::Addresses& addrs) { CHECK_EQ(orig->getID(), config->intfID); cfg::NdpConfig ndp; if (config->__isset.ndp) { ndp = config->ndp; } auto name = getInterfaceName(config); auto mac = getInterfaceMac(config); auto mtu = config->__isset.mtu ? config->mtu : Interface::kDefaultMtu; if (orig->getRouterID() == RouterID(config->routerID) && orig->getVlanID() == VlanID(config->vlanID) && orig->getName() == name && orig->getMac() == mac && orig->getAddresses() == addrs && orig->getNdpConfig() == ndp && orig->getMtu() == mtu) { // No change return nullptr; } auto newIntf = orig->clone(); newIntf->setRouterID(RouterID(config->routerID)); newIntf->setVlanID(VlanID(config->vlanID)); newIntf->setName(name); newIntf->setMac(mac); newIntf->setAddresses(addrs); newIntf->setNdpConfig(ndp); newIntf->setMtu(mtu); return newIntf; }
Interface::Addresses ThriftConfigApplier::getInterfaceAddresses( const cfg::Interface* config) { Interface::Addresses addrs; for (const auto& addr : config->ipAddresses) { auto intfAddr = IPAddress::createNetwork(addr, -1, false); auto ret = addrs.insert(intfAddr); if (!ret.second) { throw FbossError("Duplicate network IP address ", addr, " in interface ", config->intfID); } auto ret2 = intfRouteTables_[RouterID(config->routerID)].emplace( IPAddress::createNetwork(addr), std::make_pair(InterfaceID(config->intfID), intfAddr.first)); if (!ret2.second) { // we get same network, only allow it if that is from the same interface auto other = ret2.first->second.first; if (other != InterfaceID(config->intfID)) { throw FbossError("Duplicate network address ", addr, " of interface ", config->intfID, " as interface ", other, " in VRF ", config->routerID); } } } return addrs; }
// 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; }
InterfaceFields InterfaceFields::fromFollyDynamic(const folly::dynamic& json) { auto intfFields = InterfaceFields(InterfaceID(json[kIntfId].asInt()), RouterID(json[kRouterId].asInt()), VlanID(json[kVlanId].asInt()), json[kName].asString(), MacAddress(json[kMac].asString())); ThriftSerializerJson<cfg::NdpConfig> serializer; for (const auto& addr: json[kAddresses]) { auto cidr = IPAddress::createNetwork(addr.asString(), -1 /*use /32 for v4 and /128 for v6*/, false /*don't apply mask*/); intfFields.addrs[cidr.first] = cidr.second; } serializer.deserialize(toJson(json[kNdpConfig]), &intfFields.ndp); return intfFields; }
shared_ptr<Interface> ThriftConfigApplier::createInterface( const cfg::Interface* config, const Interface::Addresses& addrs) { auto name = getInterfaceName(config); auto mac = getInterfaceMac(config); auto mtu = config->__isset.mtu ? config->mtu : Interface::kDefaultMtu; auto intf = make_shared<Interface>(InterfaceID(config->intfID), RouterID(config->routerID), VlanID(config->vlanID), name, mac, mtu); intf->setAddresses(addrs); if (config->__isset.ndp) { intf->setNdpConfig(config->ndp); } return intf; }
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(); }
RouterID TunIntf::getRidFromName(const char *name) { if (!isTunIntf(name)) { throw FbossError(name, " is not a valid tun interface"); } return RouterID(atoi(name + prefixLen)); }