// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-

// Copyright (c) 2001-2007 International Computer Science Institute
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software")
// to deal in the Software without restriction, subject to the conditions
// listed in the XORP LICENSE file. These conditions include: you must
// preserve this copyright notice, and you cannot mention the copyright
// holders in advertising related to the Software without their permission.
// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
// notice is a summary of the XORP LICENSE file; the license in that file is
// legally binding.

#ident "$XORP: xorp/fea/test_fti.cc,v 1.11 2007/02/16 22:45:50 pavlin Exp $"

#include <list>

#include "fea_module.h"

#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"

#include "fte.hh"

bool debug = false;

/**
 * Pretty print a forwarding table entry.
 */
void
pp_fte(Fte4& fte)
{
 	printf("dst = %-16s", fte.net().str().c_str());
 	printf("nexthop = %-16s ", fte.nexthop().str().c_str());
	printf("if = %s", fte.vifname().c_str());
	printf("\n");
}

void
lookup_route_by_dest(Fti *fti, const char *addr)
{
    Fte4 fte;
    struct in_addr in;

    printf("Lookup route %s\n", addr);

    in.s_addr = inet_addr(addr);
    if (fti->lookup_route_by_dest4(in.s_addr, fte))
	pp_fte(fte);
    else
	printf("lookup failed\n");
}

int
print_routing_table(Fti *fti)
{
    if (!fti->start_transaction()) {
	XLOG_ERROR("failed to start a transaction");
	return -1;
    }

    if (!fti->start_reading4()) {
	XLOG_ERROR("failed to start reading");
	return -1;
    }

    Fte4 fte;

    printf("Routing table *********\n");
    while (fti->read_entry4(fte)) {
	pp_fte(fte);
    }
    printf("Routing table *********\n");


    if (!fti->abort_transaction()) {
	XLOG_ERROR("failed abort transaction");
	return -1;
    }

    return 0;
}

int
add(Fti* fti, char *network, char *nexthop, char *ifname)
{
    IPv4Net n;
    try {
	n = IPv4Net(network);
    } catch (const InvalidString& ) {
	XLOG_ERROR("Bad network: %s, expected <xx.xx.xx.xx>/<nn>", network);
	return -1;
    }

    IPv4 g;
    try {
	g = IPv4(nexthop);
    } catch (const InvalidString& ) {
	XLOG_ERROR("Bad nexthop: %s, expected xx.xx.xx.xx", nexthop);
	return -1;
    }


    if (!fti->start_transaction()) {
	XLOG_ERROR("failed to start a transaction");
	return -1;
    }

    Fte4 fte(n, g, ifname);

    if (false == fti->add_entry4(fte)) {
	XLOG_ERROR("add_entry failed");
	return -1;
    }
    if (!fti->commit_transaction()) {
	XLOG_ERROR("commit transaction failed");
	return -1;
    }

    return 0;
}

int
del(Fti* fti, char *network)
{
    if (0 == fti)
	return -1;

    IPv4Net n;
    try {
	n = IPv4Net(network);
    } catch (const InvalidString&) {
	XLOG_ERROR("Invalid network: %s\n", network);
	return -1;
    }

    if (!fti->start_transaction()) {
	XLOG_ERROR("failed to start a transaction");
	return -1;
    }

    Fte4 fte(n);

    if (false == fti->delete_entry4(fte)) {
	XLOG_ERROR("del_entry failed");
    }

    if (!fti->commit_transaction()) {
	XLOG_ERROR("commit transaction failed");
	return -1;
    }

    return 0;
}

void
save(Fti* fti, list<Fte4>& rt)
{
    if (!fti->start_reading4()) {
	XLOG_ERROR("failed to start reading");
	return;
    }

    Fte4 fte;

    while (fti->read_entry4(fte)) {
	rt.push_back(fte);
    }
}

void
restore(Fti* fti, list<Fte4>& rt)
{
    /* XXX
    ** This could loop forever
    */
    while (!fti->start_transaction()) {
	XLOG_ERROR("failed to start a transaction");

	/*
	** Abort any current transaction.
	*/
	if (!fti->abort_transaction()) {
	    XLOG_ERROR("failed to abort transaction");
	    return;
	}
    }

    /*
    ** Empty the routing table and then add back the saved routes.
    */
    if (!fti->delete_all_entries4()) {
	XLOG_ERROR("failed to delete all entries");
	/* Carry on anyway */
    }

    for (list<Fte4>::iterator e = rt.begin(); e != rt.end(); e++) {
	if (false == fti->add_entry4(*e)) {
	    XLOG_ERROR("add_entry failed");
	}
    }

    if (!fti->commit_transaction()) {
	XLOG_ERROR("commit transaction failed");
	return;
    }
}

/**
 * test1
 * Save and restore the routing table. The restore is implemented by
 * deleting all entries in the table and then putting them back so this
 * is a good test for additions and deletions.
 */
void
test1(Fti* fti)
{
    list<Fte4> rt;

    save(fti, rt);
    restore(fti, rt);
}

/**
 * test2
 * Test the delete all method.
 */
void
test2(Fti* fti)
{
    if (0 == fti)
	return;

    /* XXX
    ** This could loop forever
    */
    while (!fti->start_transaction()) {
	XLOG_ERROR("failed to start a transaction");

	/*
	** Abort any current transaction.
	*/
	if (!fti->abort_transaction()) {
	    XLOG_ERROR("failed to abort transaction");
	    return;
	}
    }

    list<Fte4> rt;
    save(fti, rt);

    /*
    ** Empty the routing table and then add back the saved routes.
    */
    if (!fti->delete_all_entries4()) {
	XLOG_ERROR("failed to delete all entries");
	/* Carry on anyway */
    }

    if (!fti->commit_transaction()) {
	XLOG_ERROR("commit transaction failed");
    }

    restore(fti, rt);
}

void
usage(char *name)
{
    fprintf (stderr,
"usage: %s [\n"
"(routing table)\t -r\n"
"(add)\t\t -a <network> <nexthop> <interface>\n"
"(delete)\t -d <network> \n"
"(zap)\t\t -z\n"
"(lookup)\t -l <dst>\n"
"(test) \t\t -T <test number>\n"
"(debug)\t\t -D\n"
"]\n",
	     name);

    exit(-1);
}

int
main(int argc, char *argv[])
{
    int c;
    bool prt = false;
    char *lookup = 0;

    /*
    ** Initialize and start xlog
    */
    xlog_init(argv[0], NULL);
    xlog_set_verbose(XLOG_VERBOSE_LOW);		// Least verbose messages
    // XXX: verbosity of the error messages temporary increased
    xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH);
    xlog_add_default_output();
    xlog_start();

    /* BSD specific contortions to create an Fti */
    EventLoop e;
    RoutingSocket rs(e);
    Iflist iflist(rs);
    FreeBSDFti fti(iflist);

    while ((c = getopt (argc, argv, "ra:d:zl:T:D")) != EOF) {
	switch (c) {
	case 'r':
	    prt = true;
	    break;
	case 'a':
	    /*
	    ** Check we have enough arguments.
	    */
	    if (optind + 2 > argc)
		usage(argv[0]);
	    add(&fti, optarg, argv[optind], argv[optind + 1]);
	    optind += 2;
	    break;
	case 'd':
	    del(&fti, optarg);
	    optind += 1;
	    break;
	case 'z':	/* zap */
	    {
	    list<Fte4> rt;
	    restore(&fti, rt);
	    break;
	    }
	case 'l':
	    lookup = optarg;
	    break;
	case 'T':
	    switch (atoi(optarg)) {
	    case 1:
		test1(&fti);
		break;
	    case 2:
		test2(&fti);
		break;
	    default:
		XLOG_ERROR("No test %s", optarg);
	    }
	    break;
	case 'D':
	    debug = true;
	    break;
	case '?':
	    usage(argv[0]);
        }
    }

    if (prt)
	print_routing_table(&fti);

    /*
    ** Lookup some routes
    */
    if (lookup)
	lookup_route_by_dest(&fti, lookup);

#if	0
    lookup_route_by_dest(fti, "192.150.187.11");
    lookup_route_by_dest(fti, "128.16.64.16");
    lookup_route_by_dest(fti, "10.0.3.2");
#endif

    /*
    ** Gracefully stop and exit xlog
    */
    xlog_stop();
    xlog_exit();

    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1