#!/usr/bin/perl -w

use strict;
use NF2::RegressLib;
use NF2::PacketLib;
use RegressRouterLib;

use reg_defines_reference_router;

use constant NUM_PKTS => 10;

my @interfaces = ("nf2c0", "nf2c1", "nf2c2", "nf2c3", "eth1", "eth2");
nftest_init(\@ARGV,\@interfaces,);
nftest_start(\@interfaces,);

my $routerMAC0 = "00:ca:fe:00:00:01";
my $routerMAC1 = "00:ca:fe:00:00:02";
my $routerMAC2 = "00:ca:fe:00:00:03";
my $routerMAC3 = "00:ca:fe:00:00:04";

my $routerIP0 = "192.168.0.40";
my $routerIP1 = "192.168.1.40";
my $routerIP2 = "192.168.2.40";
my $routerIP3 = "192.168.3.40";

my $dstIP0 = "192.168.0.50";
my $dstIP1 = "192.168.1.50";
my $dstIP2 = "192.168.2.50";
my $dstIP3 = "192.168.3.50";

my $dstMac0 = "aa:bb:cc:dd:ee:01";
my $dstMac1 = "aa:bb:cc:dd:ee:02";
my $dstMac2 = "aa:bb:cc:dd:ee:03";
my $dstMac3 = "aa:bb:cc:dd:ee:04";

my $ALLSPFRouters = "224.0.0.5";

# clear LPM table
for (my $i = 0; $i < 32; $i++)
{
        nftest_invalidate_LPM_table_entry('nf2c0', $i);
}

# clear ARP table
for (my $i = 0; $i < 32; $i++)
{
        nftest_invalidate_ARP_table_entry('nf2c0', $i);
}

######### You should skip this section for tests with router SCONE
# Write the mac and IP addresses doesn't matter which of the nf2c0..3 you write to.
nftest_add_dst_ip_filter_entry ('nf2c0', 0, $routerIP0);
nftest_add_dst_ip_filter_entry ('nf2c0', 1, $routerIP1);
nftest_add_dst_ip_filter_entry ('nf2c0', 2, $routerIP2);
nftest_add_dst_ip_filter_entry ('nf2c0', 3, $routerIP3);
nftest_add_dst_ip_filter_entry ('nf2c0', 4, $ALLSPFRouters);

# For these it does matter which interface you write to
nftest_set_router_MAC ('nf2c0', $routerMAC0);
nftest_set_router_MAC ('nf2c1', $routerMAC1);
nftest_set_router_MAC ('nf2c2', $routerMAC2);
nftest_set_router_MAC ('nf2c3', $routerMAC3);
#########

# Put the two ports in loopback mode. Pkts going out will come back in on
# the same port
nftest_phy_loopback('nf2c2');
nftest_phy_loopback('nf2c3');

nftest_regread_expect('nf2c0', MDIO_PHY_0_CONTROL_REG(), 0x1140);
nftest_regread_expect('nf2c0', MDIO_PHY_1_CONTROL_REG(), 0x1140);
nftest_regread_expect('nf2c0', MDIO_PHY_2_CONTROL_REG(), 0x5140);
nftest_regread_expect('nf2c0', MDIO_PHY_3_CONTROL_REG(), 0x5140);

# set parameters
my $DA = $routerMAC0;
my $SA = "aa:bb:cc:dd:ee:ff";
my $TTL = 64;
my $DST_IP = "192.168.1.1";
my $SRC_IP = "192.168.0.1";;
my $nextHopMAC = "dd:55:dd:66:dd:77";

# create mac header
my $MAC_hdr = NF2::Ethernet_hdr->new(DA => $DA,
                                     SA => $SA,
                                     Ethertype => 0x800
                                    );

#create IP header
my $IP_hdr = NF2::IP_hdr->new(ttl => $TTL,
                              src_ip => $SRC_IP,
                              dst_ip => $DST_IP
                             );

$IP_hdr->checksum(0);  # make sure its zero before we calculate it.
$IP_hdr->checksum($IP_hdr->calc_checksum);

my $num_precreated = 100;
my $start_val = $MAC_hdr->length_in_bytes() + $IP_hdr->length_in_bytes()+1;

# precreate random sized packets
$MAC_hdr->DA($routerMAC0);
my @precreated0 = nftest_precreate_pkts($num_precreated,
                                        $MAC_hdr->packed . $IP_hdr->packed);
$MAC_hdr->DA($routerMAC1);
my @precreated1 = nftest_precreate_pkts($num_precreated,
                                        $MAC_hdr->packed . $IP_hdr->packed);
$MAC_hdr->DA($routerMAC2);
my @precreated2 = nftest_precreate_pkts($num_precreated,
                                        $MAC_hdr->packed . $IP_hdr->packed);
$MAC_hdr->DA($routerMAC3);
my @precreated3 = nftest_precreate_pkts($num_precreated,
                                        $MAC_hdr->packed . $IP_hdr->packed);

# clear counter values for MAC output queues only
nftest_regwrite("nf2c0", OQ_QUEUE_0_NUM_PKTS_DROPPED_REG(), 0);
nftest_regwrite("nf2c0", OQ_QUEUE_2_NUM_PKTS_DROPPED_REG(), 0);
nftest_regwrite("nf2c0", OQ_QUEUE_4_NUM_PKTS_DROPPED_REG(), 0);
nftest_regwrite("nf2c0", OQ_QUEUE_6_NUM_PKTS_DROPPED_REG(), 0);

print "Sending now: \n";
my $pkt;
my @totalPktLengths = (0, 0, 0, 0);

for(my $i=0; $i<NUM_PKTS; $i++){
  print "$i \r";
  $pkt = $precreated0[int(rand($num_precreated))];
  nftest_send('nf2c0', $pkt);
  nftest_expect('eth1', $pkt);
	`usleep 100`;
  $pkt = $precreated1[int(rand($num_precreated))];
  nftest_send('nf2c1', $pkt);
  nftest_expect('eth2', $pkt);
	`usleep 100`;
  # packets are looped back and will be sent to the CPU due to an LPM miss
  $pkt = $precreated2[int(rand($num_precreated))];
  nftest_send('nf2c2', $pkt);
  nftest_expect('nf2c2', $pkt);
	`usleep 100`;
  $pkt = $precreated3[int(rand($num_precreated))];
  nftest_send('nf2c3', $pkt);
  nftest_expect('nf2c3', $pkt);
	`usleep 100`;
}
print "\n";

sleep 2;

print "Disabling servicing output queues\n";
nftest_regwrite("nf2c0", OQ_QUEUE_0_CTRL_REG(), 0);
nftest_regwrite("nf2c0", OQ_QUEUE_2_CTRL_REG(), 0);
nftest_regwrite("nf2c0", OQ_QUEUE_4_CTRL_REG(), 0);
nftest_regwrite("nf2c0", OQ_QUEUE_6_CTRL_REG(), 0);

print "Setting max number of pkts in queue to " . NUM_PKTS . "\n";
nftest_regwrite("nf2c0", OQ_QUEUE_0_MAX_PKTS_IN_Q_REG(), NUM_PKTS);
nftest_regwrite("nf2c0", OQ_QUEUE_2_MAX_PKTS_IN_Q_REG(), NUM_PKTS);
nftest_regwrite("nf2c0", OQ_QUEUE_4_MAX_PKTS_IN_Q_REG(), NUM_PKTS);
nftest_regwrite("nf2c0", OQ_QUEUE_6_MAX_PKTS_IN_Q_REG(), NUM_PKTS);

print "Resending packets.\n";
for(my $i=0; $i<NUM_PKTS; $i++){
  print "$i \r";
  $pkt = $precreated0[int(rand($num_precreated))];
  nftest_send('nf2c0', $pkt);
  nftest_expect('eth1', $pkt);
	`usleep 100`;
  $pkt = $precreated1[int(rand($num_precreated))];
  nftest_send('nf2c1', $pkt);
  nftest_expect('eth2', $pkt);
	`usleep 100`;
  # packets are looped back and will be sent to the CPU due to an LPM miss
  $pkt = $precreated2[int(rand($num_precreated))];
  nftest_send('nf2c2', $pkt);
  nftest_expect('nf2c2', $pkt);
	`usleep 100`;
  $pkt = $precreated3[int(rand($num_precreated))];
  nftest_send('nf2c3', $pkt);
  nftest_expect('nf2c3', $pkt);
	`usleep 100`;
}
print "\n";

sleep 1;

print "Verifying that the packets are stored in the output queues.\n";
nftest_regread_expect("nf2c0", OQ_QUEUE_0_NUM_PKTS_IN_Q_REG(), NUM_PKTS);
nftest_regread_expect("nf2c0", OQ_QUEUE_2_NUM_PKTS_IN_Q_REG(), NUM_PKTS);
nftest_regread_expect("nf2c0", OQ_QUEUE_4_NUM_PKTS_IN_Q_REG(), NUM_PKTS);
nftest_regread_expect("nf2c0", OQ_QUEUE_6_NUM_PKTS_IN_Q_REG(), NUM_PKTS);

print "Sending more packets that should be dropped.\n";
for(my $i=0; $i<NUM_PKTS; $i++){
  print "$i \r";
  $pkt = $precreated0[int(rand($num_precreated))];
  nftest_send('nf2c0', $pkt);
	`usleep 100`;
  $pkt = $precreated1[int(rand($num_precreated))];
  nftest_send('nf2c1', $pkt);
	`usleep 100`;
  # packets are looped back and will be sent to the CPU due to an LPM miss
  $pkt = $precreated2[int(rand($num_precreated))];
  nftest_send('nf2c2', $pkt);
	`usleep 100`;
  $pkt = $precreated3[int(rand($num_precreated))];
  nftest_send('nf2c3', $pkt);
	`usleep 100`;
}
print "\n";
sleep 1;

print "Verifying dropped pkts counter.\n";
nftest_regread_expect("nf2c0", OQ_QUEUE_0_NUM_PKTS_DROPPED_REG(), NUM_PKTS);
nftest_regread_expect("nf2c0", OQ_QUEUE_2_NUM_PKTS_DROPPED_REG(), NUM_PKTS);
nftest_regread_expect("nf2c0", OQ_QUEUE_4_NUM_PKTS_DROPPED_REG(), NUM_PKTS);
nftest_regread_expect("nf2c0", OQ_QUEUE_6_NUM_PKTS_DROPPED_REG(), NUM_PKTS);

print "Start servicing the output queues again. Packets should be sent out.\n";
nftest_regwrite("nf2c0", OQ_QUEUE_0_CTRL_REG(), 1 << OQ_ENABLE_SEND_BIT_NUM());
nftest_regwrite("nf2c0", OQ_QUEUE_2_CTRL_REG(), 1 << OQ_ENABLE_SEND_BIT_NUM());
nftest_regwrite("nf2c0", OQ_QUEUE_4_CTRL_REG(), 1 << OQ_ENABLE_SEND_BIT_NUM());
nftest_regwrite("nf2c0", OQ_QUEUE_6_CTRL_REG(), 1 << OQ_ENABLE_SEND_BIT_NUM());

print "Reset max number of pkts in queues.\n";
nftest_regwrite("nf2c0", OQ_QUEUE_0_MAX_PKTS_IN_Q_REG(), 0xffffffff);
nftest_regwrite("nf2c0", OQ_QUEUE_2_MAX_PKTS_IN_Q_REG(), 0xffffffff);
nftest_regwrite("nf2c0", OQ_QUEUE_4_MAX_PKTS_IN_Q_REG(), 0xffffffff);
nftest_regwrite("nf2c0", OQ_QUEUE_6_MAX_PKTS_IN_Q_REG(), 0xffffffff);

sleep 1;

my $unmatched_hoh = nftest_finish();
nftest_reset_phy();

my $total_errors = 0;

print "Checking pkt errors\n";
$total_errors += nftest_print_errors($unmatched_hoh);

my @badReads = nftest_get_badReads();
$total_errors += (0+@badReads);

if ($total_errors==0) {
  print "Test PASSES\n";
  exit 0;
}
else {
  print "Test FAILED: $total_errors errors\n";
  exit 1;
}
