Example #1
0
TEST(ArpTest, ArpTableSerialization) {
  auto sw = setupSwitch();

  VlanID vlanID(1);
  IPAddressV4 senderIP = IPAddressV4("10.0.0.1");
  IPAddressV4 targetIP = IPAddressV4("10.0.0.2");

  EXPECT_HW_CALL(sw, stateChanged(_)).Times(0);
  auto vlan = sw->getState()->getVlans()->getVlanIf(vlanID);
  EXPECT_NE(vlan, nullptr);
  auto arpTable = vlan->getArpTable();
  EXPECT_NE(arpTable, nullptr);
  EXPECT_EQ(arpTable->hasPendingEntries(), false);
  auto serializedArpTable = arpTable->toFollyDynamic();
  auto unserializedArpTable = arpTable->fromFollyDynamic(serializedArpTable);
  EXPECT_EQ(unserializedArpTable->hasPendingEntries(), false);
  waitForStateUpdates(sw.get());

  // Cache the current stats
  CounterCache counters(sw.get());

  testSendArpRequest(sw, vlanID, senderIP, targetIP);

  waitForStateUpdates(sw.get());
  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);

  vlan = sw->getState()->getVlans()->getVlanIf(vlanID);
  EXPECT_NE(vlan, nullptr);
  arpTable = vlan->getArpTable();
  EXPECT_NE(arpTable, nullptr);
  EXPECT_EQ(arpTable->hasPendingEntries(), true);
  serializedArpTable = arpTable->toFollyDynamic();
  unserializedArpTable = arpTable->fromFollyDynamic(serializedArpTable);

  // Pending flag should still be set
  EXPECT_EQ(unserializedArpTable->hasPendingEntries(), true);

  // Should also see a pending entry
  auto entry = sw->getState()->getVlans()->getVlanIf(vlanID)->getArpTable()
    ->getEntryIf(targetIP);

  EXPECT_NE(sw, nullptr);
}
Example #2
0
TEST(ArpTest, PendingArpCleanup) {
  std::chrono::seconds arpTimeout(1);
  std::chrono::seconds arpAgerInterval(1);
  auto sw = setupSwitch(arpTimeout, arpAgerInterval);

  VlanID vlanID(1);
  IPAddressV4 senderIP = IPAddressV4("10.0.0.1");
  IPAddressV4 targetIP = IPAddressV4("10.0.0.2");

  // Cache the current stats
  CounterCache counters(sw.get());

  testSendArpRequest(sw, vlanID, senderIP, targetIP);

  waitForStateUpdates(sw.get());
  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);

  // Should see a pending entry now
  auto entry = sw->getState()->getVlans()->getVlanIf(vlanID)->getArpTable()
    ->getEntryIf(targetIP);
  EXPECT_NE(entry, nullptr);
  EXPECT_EQ(entry->isPending(), true);

  // Send an Arp request for a different neighbor
  IPAddressV4 targetIP2 = IPAddressV4("10.0.0.3");

  testSendArpRequest(sw, vlanID, senderIP, targetIP2);

  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);

  // Should see another pending entry now
  waitForStateUpdates(sw.get());
  entry = sw->getState()->getVlans()->getVlanIf(vlanID)->getArpTable()
    ->getEntryIf(targetIP2);
  EXPECT_NE(entry, nullptr);
  EXPECT_EQ(entry->isPending(), true);
}
Example #3
0
TEST(ArpTest, PendingArp) {
  auto sw = setupSwitch();

  VlanID vlanID(1);
  IPAddressV4 senderIP = IPAddressV4("10.0.0.1");
  IPAddressV4 targetIP = IPAddressV4("10.0.0.2");

  // Cache the current stats
  CounterCache counters(sw.get());

  testSendArpRequest(sw, vlanID, senderIP, targetIP);

  waitForStateUpdates(sw.get());
  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);

  // Create an IP pkt for 10.0.0.10
  auto pkt = MockRxPacket::fromHex(
    // dst mac, src mac
    "02 00 01 00 00 01  02 00 02 01 02 03"
    // 802.1q, VLAN 1
    "81 00 00 01"
    // IPv4
    "08 00"
    // Version(4), IHL(5), DSCP(0), ECN(0), Total Length(20)
    "45  00  00 14"
    // Identification(0), Flags(0), Fragment offset(0)
    "00 00  00 00"
    // TTL(31), Protocol(6), Checksum (0, fake)
    "1F  06  00 00"
    // Source IP (1.2.3.4)
    "01 02 03 04"
    // Destination IP (10.0.0.10)
    "0a 00 00 0a"
  );
  pkt->padToLength(68);
  pkt->setSrcPort(PortID(1));
  pkt->setSrcVlan(vlanID);

  // Receiving this packet should trigger an ARP request out,
  // and the state should now include a pending arp entry.
  EXPECT_HW_CALL(sw, stateChanged(_)).Times(1);
  EXPECT_PKT(sw, "ARP request",
             checkArpRequest(senderIP, MacAddress("00:02:00:00:00:01"),
                             IPAddressV4("10.0.0.10"), vlanID));

  sw->packetReceived(pkt->clone());

  // Should see a pending entry now
  waitForStateUpdates(sw.get());
  auto entry = sw->getState()->getVlans()->getVlanIf(vlanID)->getArpTable()
    ->getEntryIf(IPAddressV4("10.0.0.10"));
  EXPECT_NE(entry, nullptr);
  EXPECT_EQ(entry->isPending(), true);

  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.pkts.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.arp.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.drops.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.error.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.ipv4.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "ipv4.nexthop.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "ipv4.no_arp.sum", 0);

  // Receiving this duplicate packet should NOT trigger an ARP request out,
  // and no state update for now.
  EXPECT_HW_CALL(sw, stateChanged(_)).Times(0);

  sw->packetReceived(pkt->clone());

  // Should still see a pending entry now
  waitForStateUpdates(sw.get());
  entry = sw->getState()->getVlans()->getVlanIf(vlanID)->getArpTable()
    ->getEntryIf(IPAddressV4("10.0.0.10"));
  EXPECT_NE(entry, nullptr);
  EXPECT_EQ(entry->isPending(), true);

  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.pkts.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.arp.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.drops.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.error.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.ipv4.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "ipv4.nexthop.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "ipv4.no_arp.sum", 0);

  EXPECT_HW_CALL(sw, stateChanged(_)).Times(1);
  // Receive an arp reply for our pending entry
  sendArpReply(sw.get(), "10.0.0.10", "02:10:20:30:40:22", 1);

  // The entry should now be valid instead of pending
  waitForStateUpdates(sw.get());
  entry = sw->getState()->getVlans()->getVlanIf(vlanID)->getArpTable()
    ->getEntryIf(IPAddressV4("10.0.0.10"));
  EXPECT_NE(entry, nullptr);
  EXPECT_EQ(entry->isPending(), false);

  // Verify that we don't ever overwrite a valid entry with a pending one.
  // Receive the same packet again, no state update and the entry should still
  // be valid
  EXPECT_HW_CALL(sw, stateChanged(_)).Times(0);

  sw->packetReceived(pkt->clone());
  waitForStateUpdates(sw.get());
  entry = sw->getState()->getVlans()->getVlanIf(vlanID)->getArpTable()
    ->getEntryIf(IPAddressV4("10.0.0.10"));
  EXPECT_NE(entry, nullptr);
  EXPECT_EQ(entry->isPending(), false);
};
Example #4
0
TEST(ArpTest, FlushEntry) {
  auto sw = setupSwitch();

  // Send ARP replies from several nodes to populate the table
  EXPECT_HW_CALL(sw, stateChanged(_)).Times(testing::AtLeast(1));
  sendArpReply(sw.get(), "10.0.0.11", "02:10:20:30:40:11", 2);
  sendArpReply(sw.get(), "10.0.0.15", "02:10:20:30:40:15", 3);
  sendArpReply(sw.get(), "10.0.0.7", "02:10:20:30:40:07", 1);
  sendArpReply(sw.get(), "10.0.0.22", "02:10:20:30:40:22", 4);

  // Wait for all state updates from the ARP replies to be applied
  waitForStateUpdates(sw.get());

  // Use the thrift API to confirm that the ARP table is populated as expected.
  // This also serves to test the thrift handler functionality.
  ThriftHandler thriftHandler(sw.get());

  std::vector<ArpEntryThrift> arpTable;
  thriftHandler.getArpTable(arpTable);
  ASSERT_EQ(arpTable.size(), 4);
  // The results should always be sorted first by VLAN and then by IP,
  // so we can check the exact ordering here.
  auto checkEntry = [&](int idx, StringPiece ip, StringPiece mac, int port) {
    SCOPED_TRACE(folly::to<string>("index ", idx));
    EXPECT_EQ(arpTable[idx].vlanID, 1);
    EXPECT_EQ(arpTable[idx].vlanName, "Vlan1");
    EXPECT_EQ(toIPAddress(arpTable[idx].ip).str(), ip);
    EXPECT_EQ(arpTable[idx].mac, mac);
    EXPECT_EQ(arpTable[idx].port, port);
  };
  checkEntry(0, "10.0.0.7", "02:10:20:30:40:07", 1);
  checkEntry(1, "10.0.0.11", "02:10:20:30:40:11", 2);
  checkEntry(2, "10.0.0.15", "02:10:20:30:40:15", 3);
  checkEntry(3, "10.0.0.22", "02:10:20:30:40:22", 4);

  // Via the thrift API, flush the ARP entry for 10.0.0.11
  EXPECT_HW_CALL(sw, stateChanged(_)).Times(1);
  auto binAddr = toBinaryAddress(IPAddressV4("10.0.0.11"));
  auto numFlushed = thriftHandler.flushNeighborEntry(
      make_unique<BinaryAddress>(binAddr), 1);
  EXPECT_EQ(numFlushed, 1);

  // Now check the table again
  arpTable.clear();
  thriftHandler.getArpTable(arpTable);
  ASSERT_EQ(arpTable.size(), 3);
  checkEntry(0, "10.0.0.7", "02:10:20:30:40:07", 1);
  checkEntry(1, "10.0.0.15", "02:10:20:30:40:15", 3);
  checkEntry(2, "10.0.0.22", "02:10:20:30:40:22", 4);

  // Calling flushNeighborEntry() with an IP that isn't present in the table
  // should do nothing and return 0
  EXPECT_HW_CALL(sw, stateChanged(_)).Times(0);
  binAddr = toBinaryAddress(IPAddressV4("10.0.0.254"));
  numFlushed = thriftHandler.flushNeighborEntry(
      make_unique<BinaryAddress>(binAddr), 1);
  EXPECT_EQ(numFlushed, 0);
  binAddr = toBinaryAddress(IPAddressV4("1.2.3.4"));
  numFlushed = thriftHandler.flushNeighborEntry(
      make_unique<BinaryAddress>(binAddr), 1);
  EXPECT_EQ(numFlushed, 0);

  // Calling flushNeighborEntry() with a bogus VLAN ID should throw an error.
  binAddr = toBinaryAddress(IPAddressV4("1.2.3.4"));
  auto binAddrPtr = make_unique<BinaryAddress>(binAddr);
  EXPECT_THROW(thriftHandler.flushNeighborEntry(std::move(binAddrPtr), 123),
               FbossError);
}
Example #5
0
TEST(ArpTest, TableUpdates) {
  auto sw = setupSwitch();
  VlanID vlanID(1);

  // Create an ARP request for 10.0.0.1 from an unreachable source
  auto pkt = MockRxPacket::fromHex(
    // dst mac, src mac
    "ff ff ff ff ff ff  00 02 00 01 02 03"
    // 802.1q, VLAN 1
    "81 00  00 01"
    // ARP, htype: ethernet, ptype: IPv4, hlen: 6, plen: 4
    "08 06  00 01  08 00  06  04"
    // ARP Request
    "00 01"
    // Sender MAC
    "00 02 00 01 02 03"
    // Sender IP: 10.0.1.15
    "0a 00 01 0f"
    // Target MAC
    "00 00 00 00 00 00"
    // Target IP: 10.0.0.1
    "0a 00 00 01"
  );
  pkt->padToLength(68);
  pkt->setSrcPort(PortID(1));
  pkt->setSrcVlan(vlanID);

  // Cache the current stats
  CounterCache counters(sw.get());

  // Sending the ARP request to the switch should not trigger an update to the
  // ArpTable for VLAN 1, but will send a reply packet
  EXPECT_HW_CALL(sw, stateChanged(_)).Times(0);
  EXPECT_PKT(sw, "ARP reply",
             checkArpReply("10.0.0.1", "00:02:00:00:00:01",
                           "10.0.1.15", "00:02:00:01:02:03",
                           vlanID));

  // Inform the SwSwitch of the ARP request
  sw->packetReceived(pkt->clone());

  // Check the new ArpTable does not have any entry
  auto arpTable = sw->getState()->getVlans()->getVlan(vlanID)->getArpTable();
  EXPECT_EQ(0, arpTable->getAllNodes().size());

  // Create an ARP request for 10.0.0.1
  pkt = MockRxPacket::fromHex(
    // dst mac, src mac
    "ff ff ff ff ff ff  00 02 00 01 02 03"
    // 802.1q, VLAN 1
    "81 00  00 01"
    // ARP, htype: ethernet, ptype: IPv4, hlen: 6, plen: 4
    "08 06  00 01  08 00  06  04"
    // ARP Request
    "00 01"
    // Sender MAC
    "00 02 00 01 02 03"
    // Sender IP: 10.0.0.15
    "0a 00 00 0f"
    // Target MAC
    "00 00 00 00 00 00"
    // Target IP: 10.0.0.1
    "0a 00 00 01"
  );
  pkt->padToLength(68);
  pkt->setSrcPort(PortID(1));
  pkt->setSrcVlan(vlanID);

  // update counters
  counters.update();

  // Sending the ARP request to the switch should trigger an update to the
  // ArpTable for VLAN 1, and will then send a reply packet
  EXPECT_HW_CALL(sw, stateChanged(_));
  EXPECT_PKT(sw, "ARP reply",
             checkArpReply("10.0.0.1", "00:02:00:00:00:01",
                           "10.0.0.15", "00:02:00:01:02:03",
                           vlanID));

  // Inform the SwSwitch of the ARP request
  sw->packetReceived(pkt->clone());

  // Wait for any updates triggered by the packet to complete.
  waitForStateUpdates(sw.get());

  // Check the new ArpTable contents
  arpTable = sw->getState()->getVlans()->getVlan(vlanID)->getArpTable();
  EXPECT_EQ(1, arpTable->getAllNodes().size());
  auto entry = arpTable->getEntry(IPAddressV4("10.0.0.15"));
  EXPECT_EQ(MacAddress("00:02:00:01:02:03"), entry->getMac());
  EXPECT_EQ(PortID(1), entry->getPort());
  EXPECT_EQ(InterfaceID(1), entry->getIntfID());

  // Check the new stats
  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.pkts.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.arp.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.drops.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.error.sum", 0);

  // Sending the same packet again should generate another response,
  // but not another table update.
  EXPECT_HW_CALL(sw, stateChanged(_)).Times(0);
  EXPECT_PKT(sw, "ARP reply",
             checkArpReply("10.0.0.1", "00:02:00:00:00:01",
                           "10.0.0.15", "00:02:00:01:02:03",
                           vlanID));
  sw->packetReceived(pkt->clone());

  // Check the counters again
  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.pkts.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.arp.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.drops.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.error.sum", 0);

  // An ARP request from an unknown node that isn't destined for us shouldn't
  // generate a reply or a table update.
  pkt = MockRxPacket::fromHex(
    // dst mac, src mac
    "ff ff ff ff ff ff  00 02 00 01 02 04"
    // 802.1q, VLAN 1
    "81 00  00 01"
    // ARP, htype: ethernet, ptype: IPv4, hlen: 6, plen: 4
    "08 06  00 01  08 00  06  04"
    // ARP Request
    "00 01"
    // Sender MAC
    "00 02 00 01 02 04"
    // Sender IP: 10.0.0.16
    "0a 00 00 10"
    // Target MAC
    "00 00 00 00 00 00"
    // Target IP: 10.0.0.50
    "0a 00 00 32"
  );
  pkt->padToLength(68);
  pkt->setSrcPort(PortID(1));
  pkt->setSrcVlan(vlanID);

  EXPECT_HW_CALL(sw, stateChanged(_)).Times(0);
  EXPECT_HW_CALL(sw, sendPacketSwitched_(_)).Times(0);
  sw->packetReceived(pkt->clone());
  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.pkts.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.arp.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.not_mine.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.drops.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.error.sum", 0);

  // A request from a node we already have in the arp table, but isn't for us,
  // should generate a table update if the MAC is different from what we
  // already have.
  pkt = MockRxPacket::fromHex(
    // dst mac, src mac
    "ff ff ff ff ff ff  00 02 00 01 02 08"
    // 802.1q, VLAN 1
    "81 00  00 01"
    // ARP, htype: ethernet, ptype: IPv4, hlen: 6, plen: 4
    "08 06  00 01  08 00  06  04"
    // ARP Request
    "00 01"
    // Sender MAC
    "00 02 00 01 02 08"
    // Sender IP: 10.0.0.15
    "0a 00 00 0f"
    // Target MAC
    "00 00 00 00 00 00"
    // Target IP: 10.0.0.50
    "0a 00 00 32"
  );
  pkt->padToLength(68);
  pkt->setSrcPort(PortID(2));
  pkt->setSrcVlan(vlanID);

  EXPECT_HW_CALL(sw, stateChanged(_)).Times(1);
  EXPECT_HW_CALL(sw, sendPacketSwitched_(_)).Times(0);
  sw->packetReceived(pkt->clone());
  waitForStateUpdates(sw.get());
  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.pkts.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.arp.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.not_mine.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.drops.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.error.sum", 0);

  arpTable = sw->getState()->getVlans()->getVlan(vlanID)->getArpTable();
  EXPECT_EQ(1, arpTable->getAllNodes().size());
  entry = arpTable->getEntry(IPAddressV4("10.0.0.15"));
  EXPECT_EQ(MacAddress("00:02:00:01:02:08"), entry->getMac());
  EXPECT_EQ(PortID(2), entry->getPort());
  EXPECT_EQ(InterfaceID(1), entry->getIntfID());

  // Try another request for us from a node we haven't seen yet.
  // This should generate a reply and a table update
  pkt = MockRxPacket::fromHex(
    // dst mac, src mac
    "ff ff ff ff ff ff  00 02 00 01 02 23"
    // 802.1q, VLAN 1
    "81 00  00 01"
    // ARP, htype: ethernet, ptype: IPv4, hlen: 6, plen: 4
    "08 06  00 01  08 00  06  04"
    // ARP Request
    "00 01"
    // Sender MAC
    "00 02 00 01 02 23"
    // Sender IP: 10.0.0.20
    "0a 00 00 14"
    // Target MAC
    "00 00 00 00 00 00"
    // Target IP: 10.0.0.1
    "0a 00 00 01"
  );
  pkt->padToLength(68);
  pkt->setSrcPort(PortID(1));
  pkt->setSrcVlan(vlanID);

  EXPECT_HW_CALL(sw, stateChanged(_)).Times(1);
  EXPECT_PKT(sw, "ARP reply",
             checkArpReply("10.0.0.1", "00:02:00:00:00:01",
                           "10.0.0.20", "00:02:00:01:02:23",
                           vlanID));
  sw->packetReceived(pkt->clone());
  waitForStateUpdates(sw.get());

  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.pkts.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.arp.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.drops.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.error.sum", 0);

  arpTable = sw->getState()->getVlans()->getVlan(vlanID)->getArpTable();
  EXPECT_EQ(2, arpTable->getAllNodes().size());
  entry = arpTable->getEntry(IPAddressV4("10.0.0.15"));
  EXPECT_EQ(MacAddress("00:02:00:01:02:08"), entry->getMac());
  EXPECT_EQ(PortID(2), entry->getPort());
  EXPECT_EQ(InterfaceID(1), entry->getIntfID());
  entry = arpTable->getEntry(IPAddressV4("10.0.0.20"));
  EXPECT_EQ(MacAddress("00:02:00:01:02:23"), entry->getMac());
  EXPECT_EQ(PortID(1), entry->getPort());
  EXPECT_EQ(InterfaceID(1), entry->getIntfID());

  // Try a request for an IP on another interface, to make sure
  // it generates an ARP entry with the correct interface ID.
  pkt = MockRxPacket::fromHex(
    // dst mac, src mac
    "ff ff ff ff ff ff  00 02 00 55 66 77"
    // 802.1q, VLAN 1
    "81 00  00 01"
    // ARP, htype: ethernet, ptype: IPv4, hlen: 6, plen: 4
    "08 06  00 01  08 00  06  04"
    // ARP Request
    "00 01"
    // Sender MAC
    "00 02 00 55 66 77"
    // Sender IP: 192.168.0.20
    "c0 a8 00 14"
    // Target MAC
    "00 00 00 00 00 00"
    // Target IP: 192.168.0.1
    "c0 a8 00 01"
  );
  pkt->padToLength(68);
  pkt->setSrcPort(PortID(5));
  pkt->setSrcVlan(vlanID);

  EXPECT_HW_CALL(sw, stateChanged(_)).Times(1);
  EXPECT_PKT(sw, "ARP reply",
             checkArpReply("192.168.0.1", "00:02:00:00:00:01",
                           "192.168.0.20", "00:02:00:55:66:77",
                           vlanID));
  sw->packetReceived(pkt->clone());
  waitForStateUpdates(sw.get());

  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.pkts.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.arp.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.drops.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.error.sum", 0);

  arpTable = sw->getState()->getVlans()->getVlan(vlanID)->getArpTable();
  EXPECT_EQ(3, arpTable->getAllNodes().size());
  entry = arpTable->getEntry(IPAddressV4("10.0.0.15"));
  EXPECT_EQ(MacAddress("00:02:00:01:02:08"), entry->getMac());
  EXPECT_EQ(PortID(2), entry->getPort());
  EXPECT_EQ(InterfaceID(1), entry->getIntfID());
  entry = arpTable->getEntry(IPAddressV4("10.0.0.20"));
  EXPECT_EQ(MacAddress("00:02:00:01:02:23"), entry->getMac());
  EXPECT_EQ(PortID(1), entry->getPort());
  EXPECT_EQ(InterfaceID(1), entry->getIntfID());
  entry = arpTable->getEntry(IPAddressV4("192.168.0.20"));
  EXPECT_EQ(MacAddress("00:02:00:55:66:77"), entry->getMac());
  EXPECT_EQ(PortID(5), entry->getPort());
  EXPECT_EQ(InterfaceID(1), entry->getIntfID());
}
Example #6
0
TEST(ArpTest, SendRequest) {
  auto sw = setupSwitch();
  VlanID vlanID(1);
  IPAddressV4 senderIP = IPAddressV4("10.0.0.1");
  IPAddressV4 targetIP = IPAddressV4("10.0.0.2");

  // Cache the current stats
  CounterCache counters(sw.get());

  testSendArpRequest(sw, vlanID, senderIP, targetIP);

  waitForStateUpdates(sw.get());
  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);

  // Create an IP pkt for 10.0.0.10
  auto pkt = MockRxPacket::fromHex(
    // dst mac, src mac
    "02 00 01 00 00 01  02 00 02 01 02 03"
    // 802.1q, VLAN 1
    "81 00 00 01"
    // IPv4
    "08 00"
    // Version(4), IHL(5), DSCP(0), ECN(0), Total Length(20)
    "45  00  00 14"
    // Identification(0), Flags(0), Fragment offset(0)
    "00 00  00 00"
    // TTL(31), Protocol(6), Checksum (0, fake)
    "1F  06  00 00"
    // Source IP (1.2.3.4)
    "01 02 03 04"
    // Destination IP (10.0.0.10)
    "0a 00 00 0a"
  );
  pkt->padToLength(68);
  pkt->setSrcPort(PortID(1));
  pkt->setSrcVlan(vlanID);

  // Receiving this packet should trigger an ARP request out.
  // The state will also update because a pending arp entry will be created
  EXPECT_HW_CALL(sw, stateChanged(_)).Times(1);
  EXPECT_PKT(sw, "ARP request",
             checkArpRequest(senderIP, MacAddress("00:02:00:00:00:01"),
                             IPAddressV4("10.0.0.10"), vlanID));

  sw->packetReceived(pkt->clone());
  waitForStateUpdates(sw.get());

  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.pkts.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.arp.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.drops.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.error.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.ipv4.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "ipv4.nexthop.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "ipv4.no_arp.sum", 0);

  // Create an IP pkt for 10.0.10.10
  pkt = MockRxPacket::fromHex(
    // dst mac, src mac
    "02 00 01 00 00 01  02 00 02 01 02 03"
    // 802.1q, VLAN 1
    "81 00 00 01"
    // IPv4
    "08 00"
    // Version(4), IHL(5), DSCP(0), ECN(0), Total Length(20)
    "45  00  00 14"
    // Identification(0), Flags(0), Fragment offset(0)
    "00 00  00 00"
    // TTL(31), Protocol(6), Checksum (0, fake)
    "1F  06  00 00"
    // Source IP (1.2.3.4)
    "01 02 03 04"
    // Destination IP (10.0.10.10)
    "0a 00 0a 0a"
  );
  pkt->padToLength(68);
  pkt->setSrcPort(PortID(1));
  pkt->setSrcVlan(vlanID);

  // Receiving this packet should not trigger a ARP request out,
  // because no interface is able to reach that subnet
  EXPECT_HW_CALL(sw, stateChanged(_)).Times(0);
  EXPECT_HW_CALL(sw, sendPacketSwitched_(_)).Times(0);

  sw->packetReceived(pkt->clone());

  waitForStateUpdates(sw.get());
  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.pkts.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.arp.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.drops.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.error.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.ipv4.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "ipv4.nexthop.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "ipv4.no_arp.sum", 1);

  // Create an IP pkt for 10.1.1.10, reachable through 10.0.0.22 and 10.0.0.23
  pkt = MockRxPacket::fromHex(
    // dst mac, src mac
    "02 00 01 00 00 01  02 00 02 01 02 03"
    // 802.1q, VLAN 1
    "81 00 00 01"
    // IPv4
    "08 00"
    // Version(4), IHL(5), DSCP(0), ECN(0), Total Length(20)
    "45  00  00 14"
    // Identification(0), Flags(0), Fragment offset(0)
    "00 00  00 00"
    // TTL(31), Protocol(6), Checksum (0, fake)
    "1F  06  00 00"
    // Source IP (1.2.3.4)
    "01 02 03 04"
    // Destination IP (10.1.1.10)
    "0a 01 01 0a"
  );
  pkt->padToLength(68);
  pkt->setSrcPort(PortID(1));
  pkt->setSrcVlan(vlanID);

  // Receiving this packet should trigger an ARP request to 10.0.0.22 and
  // 10.0.0.23, which causes pending arp entries to be added to the state.
  EXPECT_HW_CALL(sw, stateChanged(_)).Times(testing::AtLeast(1));
  EXPECT_PKT(sw, "ARP request",
             checkArpRequest(senderIP, MacAddress("00:02:00:00:00:01"),
                             IPAddressV4("10.0.0.22"), vlanID));
  EXPECT_PKT(sw, "ARP request",
             checkArpRequest(senderIP, MacAddress("00:02:00:00:00:01"),
                             IPAddressV4("10.0.0.23"), vlanID));

  sw->packetReceived(pkt->clone());

  waitForStateUpdates(sw.get());
  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.pkts.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.arp.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 2);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.drops.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.error.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "trapped.ipv4.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "ipv4.nexthop.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "ipv4.no_arp.sum", 0);
}
Example #7
0
TEST(StaticRoutes, configureUnconfigure) {
  auto platform = createMockPlatform();
  auto stateV0 = make_shared<SwitchState>();
  auto tablesV0 = stateV0->getRouteTables();

  cfg::SwitchConfig config;
  config.__isset.staticRoutesToNull = true;
  config.__isset.staticRoutesToCPU = true;
  config.staticRoutesToNull.resize(2);
  config.staticRoutesToNull[0].prefix = "1.1.1.1/32";
  config.staticRoutesToNull[1].prefix = "2001::1/128";
  config.staticRoutesToCPU.resize(2);
  config.staticRoutesToCPU[0].prefix = "2.2.2.2/32";
  config.staticRoutesToCPU[1].prefix = "2001::2/128";
  config.__isset.staticRoutesWithNhops = true;
  config.staticRoutesWithNhops.resize(4);
  config.staticRoutesWithNhops[0].prefix = "3.3.3.3/32";
  config.staticRoutesWithNhops[0].nexthops.resize(1);
  config.staticRoutesWithNhops[0].nexthops[0] = "1.1.1.1";
  config.staticRoutesWithNhops[1].prefix = "4.4.4.4/32";
  config.staticRoutesWithNhops[1].nexthops.resize(1);
  config.staticRoutesWithNhops[1].nexthops[0] = "2.2.2.2";
  // Now add v6 recursive routes
  config.staticRoutesWithNhops[2].prefix = "2001::3/128";
  config.staticRoutesWithNhops[2].nexthops.resize(1);
  config.staticRoutesWithNhops[2].nexthops[0] = "2001::1";
  config.staticRoutesWithNhops[3].prefix = "2001::4/128";
  config.staticRoutesWithNhops[3].nexthops.resize(1);
  config.staticRoutesWithNhops[3].nexthops[0] = "2001::2";


  auto stateV1 = publishAndApplyConfig(stateV0, &config, platform.get());
  ASSERT_NE(nullptr, stateV1);
  RouterID rid0(0);
  auto t1 = stateV1->getRouteTables()->getRouteTableIf(rid0);
  RouteV4::Prefix prefix1v4{IPAddressV4("1.1.1.1"), 32};
  RouteV4::Prefix prefix2v4{IPAddressV4("2.2.2.2"), 32};
  RouteV4::Prefix prefix3v4{IPAddressV4("3.3.3.3"), 32};
  RouteV4::Prefix prefix4v4{IPAddressV4("4.4.4.4"), 32};

  RouteV6::Prefix prefix1v6{IPAddressV6("2001::1"), 128};
  RouteV6::Prefix prefix2v6{IPAddressV6("2001::2"), 128};
  RouteV6::Prefix prefix3v6{IPAddressV6("2001::3"), 128};
  RouteV6::Prefix prefix4v6{IPAddressV6("2001::4"), 128};

  auto rib1v4 = t1->getRibV4();
  auto r1v4 = rib1v4->exactMatch(prefix1v4);
  ASSERT_NE(nullptr, r1v4);
  EXPECT_TRUE(r1v4->isResolved());
  EXPECT_FALSE(r1v4->isUnresolvable());
  EXPECT_FALSE(r1v4->isConnected());
  EXPECT_FALSE(r1v4->needResolve());
  EXPECT_EQ(
      r1v4->getForwardInfo(),
      RouteNextHopEntry(DROP, AdminDistance::MAX_ADMIN_DISTANCE));

  auto r2v4 = rib1v4->exactMatch(prefix2v4);
  ASSERT_NE(nullptr, r2v4);
  EXPECT_TRUE(r2v4->isResolved());
  EXPECT_FALSE(r2v4->isUnresolvable());
  EXPECT_FALSE(r2v4->isConnected());
  EXPECT_FALSE(r2v4->needResolve());
  EXPECT_EQ(
      r2v4->getForwardInfo(),
      RouteNextHopEntry(TO_CPU, AdminDistance::MAX_ADMIN_DISTANCE));

  // Recursive resolution to DROP
  auto r3v4 = rib1v4->exactMatch(prefix3v4);
  ASSERT_NE(nullptr, r3v4);
  EXPECT_TRUE(r3v4->isResolved());
  EXPECT_FALSE(r3v4->isUnresolvable());
  EXPECT_FALSE(r3v4->isConnected());
  EXPECT_FALSE(r3v4->needResolve());
  EXPECT_EQ(
      r3v4->getForwardInfo(),
      RouteNextHopEntry(DROP, AdminDistance::MAX_ADMIN_DISTANCE));

  // Recursive resolution to CPU
  auto r4v4 = rib1v4->exactMatch(prefix4v4);
  ASSERT_NE(nullptr, r4v4);
  EXPECT_TRUE(r4v4->isResolved());
  EXPECT_FALSE(r4v4->isUnresolvable());
  EXPECT_FALSE(r4v4->isConnected());
  EXPECT_FALSE(r4v4->needResolve());
  EXPECT_EQ(
      r4v4->getForwardInfo(),
      RouteNextHopEntry(TO_CPU, AdminDistance::MAX_ADMIN_DISTANCE));

  auto rib1v6 = t1->getRibV6();
  auto r1v6 = rib1v6->exactMatch(prefix1v6);
  ASSERT_NE(nullptr, r1v6);
  EXPECT_TRUE(r1v6->isResolved());
  EXPECT_FALSE(r1v6->isUnresolvable());
  EXPECT_FALSE(r1v6->isConnected());
  EXPECT_FALSE(r1v6->needResolve());
  EXPECT_EQ(
      r1v6->getForwardInfo(),
      RouteNextHopEntry(DROP, AdminDistance::MAX_ADMIN_DISTANCE));

  auto r2v6 = rib1v6->exactMatch(prefix2v6);
  ASSERT_NE(nullptr, r2v6);
  EXPECT_TRUE(r2v6->isResolved());
  EXPECT_FALSE(r2v6->isUnresolvable());
  EXPECT_FALSE(r2v6->isConnected());
  EXPECT_FALSE(r2v6->needResolve());
  EXPECT_EQ(
      r2v6->getForwardInfo(),
      RouteNextHopEntry(TO_CPU, AdminDistance::MAX_ADMIN_DISTANCE));

  // Recursive resolution to DROP
  auto r3v6 = rib1v6->exactMatch(prefix3v6);
  ASSERT_NE(nullptr, r3v6);
  EXPECT_TRUE(r3v6->isResolved());
  EXPECT_FALSE(r3v6->isUnresolvable());
  EXPECT_FALSE(r3v6->isConnected());
  EXPECT_FALSE(r3v6->needResolve());
  EXPECT_EQ(
      r3v6->getForwardInfo(),
      RouteNextHopEntry(DROP, AdminDistance::MAX_ADMIN_DISTANCE));

  // Recursive resolution to CPU
  auto r4v6 = rib1v6->exactMatch(prefix4v6);
  ASSERT_NE(nullptr, r4v6);
  EXPECT_TRUE(r4v6->isResolved());
  EXPECT_FALSE(r4v6->isUnresolvable());
  EXPECT_FALSE(r4v6->isConnected());
  EXPECT_FALSE(r4v6->needResolve());
  EXPECT_EQ(
      r4v6->getForwardInfo(),
      RouteNextHopEntry(TO_CPU, AdminDistance::MAX_ADMIN_DISTANCE));

  // Now blow away the static routes from config.
  cfg::SwitchConfig emptyConfig;
  auto stateV2 = publishAndApplyConfig(stateV1, &emptyConfig, platform.get(),
      &config);
  ASSERT_NE(nullptr, stateV2);
  auto t2 = stateV2->getRouteTables()->getRouteTableIf(rid0);
  // No routes and hence no routing table
  ASSERT_EQ(nullptr, t2);
}
Example #8
0
TEST(Vlan, applyConfig) {
  MockPlatform platform;
  auto stateV0 = make_shared<SwitchState>();
  auto vlanV0 = make_shared<Vlan>(VlanID(1234), kVlan1234);
  stateV0->addVlan(vlanV0);
  stateV0->registerPort(PortID(1), "port1");
  stateV0->registerPort(PortID(99), "port99");

  NodeID nodeID = vlanV0->getNodeID();
  EXPECT_EQ(0, vlanV0->getGeneration());
  EXPECT_FALSE(vlanV0->isPublished());
  EXPECT_EQ(VlanID(1234), vlanV0->getID());
  Vlan::MemberPorts emptyPorts;
  EXPECT_EQ(emptyPorts, vlanV0->getPorts());

  vlanV0->publish();
  EXPECT_TRUE(vlanV0->isPublished());

  cfg::SwitchConfig config;
  config.ports.resize(2);
  config.ports[0].logicalID = 1;
  config.ports[0].state = cfg::PortState::UP;
  config.ports[1].logicalID = 99;
  config.ports[1].state = cfg::PortState::UP;
  config.vlans.resize(1);
  config.vlans[0].id = 1234;
  config.vlans[0].name = kVlan1234;
  config.vlans[0].dhcpRelayOverridesV4["02:00:00:00:00:02"] = "1.2.3.4";
  config.vlans[0].dhcpRelayOverridesV6["02:00:00:00:00:02"] =
    "2a03:2880:10:1f07:face:b00c:0:0";
  config.vlans[0].intfID = 1;
  config.vlans[0].__isset.intfID = true;
  config.vlanPorts.resize(2);
  config.vlanPorts[0].logicalPort = 1;
  config.vlanPorts[0].vlanID = 1234;
  config.vlanPorts[0].emitTags = false;
  config.vlanPorts[1].logicalPort = 99;
  config.vlanPorts[1].vlanID = 1234;
  config.vlanPorts[1].emitTags = true;

  Vlan::MemberPorts expectedPorts;
  expectedPorts.insert(make_pair(PortID(1), Vlan::PortInfo(false)));
  expectedPorts.insert(make_pair(PortID(99), Vlan::PortInfo(true)));

  auto stateV1 = publishAndApplyConfig(stateV0, &config, &platform);
  auto vlanV1 = stateV1->getVlans()->getVlan(VlanID(1234));
  ASSERT_NE(nullptr, vlanV1);
  auto vlanV1_byName = stateV1->getVlans()->getVlanSlow(kVlan1234);
  EXPECT_EQ(vlanV1, vlanV1_byName);
  EXPECT_EQ(nodeID, vlanV1->getNodeID());
  EXPECT_EQ(1, vlanV1->getGeneration());
  EXPECT_FALSE(vlanV1->isPublished());
  EXPECT_EQ(VlanID(1234), vlanV1->getID());
  EXPECT_EQ(kVlan1234, vlanV1->getName());
  EXPECT_EQ(expectedPorts, vlanV1->getPorts());
  EXPECT_EQ(0, vlanV1->getArpResponseTable()->getTable().size());
  EXPECT_EQ(InterfaceID(1), vlanV1->getInterfaceID());

  auto map4 = vlanV1->getDhcpV4RelayOverrides();
  EXPECT_EQ(IPAddressV4("1.2.3.4"),
            IPAddressV4(map4[MacAddress("02:00:00:00:00:02")]));
  auto map6 = vlanV1->getDhcpV6RelayOverrides();
  EXPECT_EQ(IPAddressV6("2a03:2880:10:1f07:face:b00c:0:0"),
            IPAddressV6(map6[MacAddress("02:00:00:00:00:02")]));

  // Applying the same config again should return null
  EXPECT_EQ(nullptr, publishAndApplyConfig(stateV1, &config, &platform));

  // Add an interface
  config.interfaces.resize(1);
  config.interfaces[0].intfID = 1;
  config.interfaces[0].routerID = 0;
  config.interfaces[0].vlanID = 1234;
  config.interfaces[0].ipAddresses.resize(2);
  config.interfaces[0].ipAddresses[0] = "10.1.1.1/24";
  config.interfaces[0].ipAddresses[1] = "2a03:2880:10:1f07:face:b00c:0:0/96";
  MacAddress platformMac("82:02:00:ab:cd:ef");
  EXPECT_CALL(platform, getLocalMac()).WillRepeatedly(Return(platformMac));

  auto stateV2 = publishAndApplyConfig(stateV1, &config, &platform);
  auto vlanV2 = stateV2->getVlans()->getVlan(VlanID(1234));
  EXPECT_EQ(nodeID, vlanV2->getNodeID());
  EXPECT_EQ(2, vlanV2->getGeneration());
  EXPECT_FALSE(vlanV2->isPublished());
  EXPECT_EQ(VlanID(1234), vlanV2->getID());
  EXPECT_EQ(kVlan1234, vlanV2->getName());
  EXPECT_EQ(expectedPorts, vlanV2->getPorts());
  EXPECT_EQ(InterfaceID(1), vlanV2->getInterfaceID());
  // Check the ArpResponseTable
  auto arpRespTable = vlanV2->getArpResponseTable();
  ArpResponseTable expectedArpResp;
  expectedArpResp.setEntry(IPAddressV4("10.1.1.1"), platformMac,
                         InterfaceID(1));
  EXPECT_EQ(expectedArpResp.getTable(), arpRespTable->getTable());
  // Check the NdpResponseTable
  auto ndpRespTable = vlanV2->getNdpResponseTable();
  NdpResponseTable expectedNdpResp;
  expectedNdpResp.setEntry(IPAddressV6("2a03:2880:10:1f07:face:b00c:0:0"),
                           platformMac, InterfaceID(1));
  // The link-local IPv6 address should also have been automatically added
  // to the NDP response table.
  expectedNdpResp.setEntry(IPAddressV6("fe80::8002:00ff:feab:cdef"),
                           platformMac, InterfaceID(1));
  EXPECT_EQ(expectedNdpResp.getTable(), ndpRespTable->getTable());

  // Add another vlan and interface
  config.vlans.resize(2);
  config.vlans[1].id = 1299;
  config.vlans[1].name = kVlan1299;
  config.vlans[1].intfID = 2;
  config.interfaces.resize(2);
  config.interfaces[1].intfID = 2;
  config.interfaces[1].routerID = 0;
  config.interfaces[1].vlanID = 1299;
  config.interfaces[1].ipAddresses.resize(2);
  config.interfaces[1].ipAddresses[0] = "10.1.10.1/24";
  config.interfaces[1].ipAddresses[1] = "192.168.0.1/31";
  MacAddress intf2Mac("02:01:02:ab:cd:78");
  config.interfaces[1].mac = intf2Mac.toString();
  config.interfaces[1].__isset.mac = true;
  auto stateV3 = publishAndApplyConfig(stateV2, &config, &platform);
  auto vlanV3 = stateV3->getVlans()->getVlan(VlanID(1299));
  EXPECT_EQ(0, vlanV3->getGeneration());
  EXPECT_FALSE(vlanV3->isPublished());
  EXPECT_EQ(VlanID(1299), vlanV3->getID());
  EXPECT_EQ(kVlan1299, vlanV3->getName());
  EXPECT_EQ(InterfaceID(2), vlanV3->getInterfaceID());

  // Check the ArpResponseTable
  arpRespTable = vlanV3->getArpResponseTable();
  ArpResponseTable expectedTable2;
  expectedTable2.setEntry(IPAddressV4("10.1.10.1"), intf2Mac,
                          InterfaceID(2));
  expectedTable2.setEntry(IPAddressV4("192.168.0.1"), intf2Mac,
                          InterfaceID(2));
  EXPECT_EQ(expectedTable2.getTable(), arpRespTable->getTable());
  // The new interface has no IPv6 address, but the NDP table should still
  // be updated with the link-local address.
  NdpResponseTable expectedNdpResp2;
  expectedNdpResp2.setEntry(IPAddressV6("fe80::1:02ff:feab:cd78"),
                           intf2Mac, InterfaceID(2));
  EXPECT_EQ(expectedNdpResp2.getTable(),
            vlanV3->getNdpResponseTable()->getTable());

  // Add a new VLAN with an ArpResponseTable that needs to be set up
  // when the VLAN is first created
  config.vlans.resize(3);
  config.vlans[2].id = 99;
  config.vlans[2].name = kVlan99;
  config.vlans[2].intfID = 3;
  config.interfaces.resize(3);
  config.interfaces[2].intfID = 3;
  config.interfaces[2].routerID = 1;
  config.interfaces[2].vlanID = 99;
  config.interfaces[2].ipAddresses.resize(2);
  config.interfaces[2].ipAddresses[0] = "1.2.3.4/24";
  config.interfaces[2].ipAddresses[1] = "10.0.0.1/9";
  auto stateV4 = publishAndApplyConfig(stateV3, &config, &platform);
  ASSERT_NE(nullptr, stateV4);
  // VLAN 1234 should be unchanged
  EXPECT_EQ(vlanV2, stateV4->getVlans()->getVlan(VlanID(1234)));
  auto vlan99 = stateV4->getVlans()->getVlan(VlanID(99));
  auto vlan99_byName = stateV4->getVlans()->getVlanSlow(kVlan99);
  ASSERT_NE(nullptr, vlan99);
  EXPECT_EQ(vlan99, vlan99_byName);
  EXPECT_EQ(0, vlan99->getGeneration());
  EXPECT_EQ(InterfaceID(3), vlan99->getInterfaceID());

  ArpResponseTable expectedTable99;
  expectedTable99.setEntry(IPAddressV4("1.2.3.4"), platformMac,
                           InterfaceID(3));
  expectedTable99.setEntry(IPAddressV4("10.0.0.1"), platformMac,
                           InterfaceID(3));
  EXPECT_EQ(expectedTable99.getTable(),
            vlan99->getArpResponseTable()->getTable());

  // Check vlan congfig with no intfID set
  config.vlans.resize(4);
  config.vlans[3].id = 100;
  config.vlans[3].__isset.intfID = false;
  config.interfaces.resize(4);
  config.interfaces[3].intfID = 4;
  config.interfaces[3].routerID = 0;
  config.interfaces[3].vlanID = 100;
  config.interfaces[3].ipAddresses.resize(2);
  config.interfaces[3].ipAddresses[0] = "10.50.3.7/24";
  config.interfaces[3].ipAddresses[1] = "10.50.0.3/9";
  auto stateV5 = publishAndApplyConfig(stateV4, &config, &platform);
  ASSERT_NE(nullptr, stateV5);
  auto vlan100 = stateV5->getVlans()->getVlan(VlanID(100));
  EXPECT_EQ(InterfaceID(4), vlan100->getInterfaceID());
}
Example #9
0
TEST(ArpTest, PendingArpCleanup) {
  std::chrono::seconds arpTimeout(1);
  std::chrono::seconds arpAgerInterval(1);
  auto sw = setupSwitch(arpTimeout, arpAgerInterval);

  VlanID vlanID(1);
  IPAddressV4 senderIP = IPAddressV4("10.0.0.1");
  IPAddressV4 targetIP = IPAddressV4("10.0.0.2");

  // Cache the current stats
  CounterCache counters(sw.get());

  testSendArpRequest(sw, vlanID, senderIP, targetIP);

  waitForStateUpdates(sw.get());
  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);

  // Should see a pending entry now
  auto entry = sw->getState()->getVlans()->getVlanIf(vlanID)->getArpTable()
    ->getEntryIf(targetIP);
  EXPECT_NE(entry, nullptr);
  EXPECT_EQ(entry->isPending(), true);

  // Send an Arp request for a different neighbor
  IPAddressV4 targetIP2 = IPAddressV4("10.0.0.3");

  testSendArpRequest(sw, vlanID, senderIP, targetIP2);

  counters.update();
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.tx.sum", 1);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.request.rx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.tx.sum", 0);
  counters.checkDelta(SwitchStats::kCounterPrefix + "arp.reply.rx.sum", 0);

  // Should see another pending entry now
  waitForStateUpdates(sw.get());
  entry = sw->getState()->getVlans()->getVlanIf(vlanID)->getArpTable()
    ->getEntryIf(targetIP2);
  EXPECT_NE(entry, nullptr);
  EXPECT_EQ(entry->isPending(), true);

 // Wait for pending entries to expire
  EXPECT_HW_CALL(sw, stateChanged(_)).Times(testing::AtLeast(1));
  std::promise<bool> done;
  auto* evb = sw->getBackgroundEVB();
  evb->runInEventBaseThread([&]() {
      evb->tryRunAfterDelay([&]() {
        done.set_value(true);
      }, 1050);
    });
  done.get_future().wait();

  // Entries should be removed
  entry = sw->getState()->getVlans()->getVlanIf(vlanID)->getArpTable()
    ->getEntryIf(targetIP);
  auto entry2 = sw->getState()->getVlans()->getVlanIf(vlanID)->getArpTable()
    ->getEntryIf(targetIP2);
  EXPECT_EQ(entry, nullptr);
  EXPECT_EQ(entry2, nullptr);
}