// -*- 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_fea.cc,v 1.16 2007/02/16 22:45:50 pavlin Exp $"
/* TODO: XXX: THIS CODE NEEDS UPDATING AS XRL INTERFACE HAS CHANGED */
#include "fea_module.h"
#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"
#include "libxorp/eventloop.hh"
#include "libxorp/callback.hh"
#include "libxipc/xrl_std_router.hh"
#include "fte.hh"
typedef void (*func)(const XrlError&, XrlRouter&, const Xrl&,
XrlArgs*, void*);
static const char *server = "fea";
static const char *client = "test fea";
class routing_cookie {
public:
routing_cookie(func ns = 0) {
_next_send = ns;
_completed = false;
_error = false;
_crank = 0;
};
void set_next_send(func ns) {
_next_send = ns;
};
func next_send() {
return _next_send;
};
void xrl(Xrl xrl) {
_xrl = xrl;
};
Xrl& xrl() {
return _xrl;
};
void completed(bool b) {
_completed = b;
};
bool completed() {
return _completed;
};
void error(bool b) {
_error = b;
};
bool error() {
return _error;
};
void crank(int c) {
_crank = c;
};
int crank() {
return _crank;
};
void transactionid(const string& tid) {
_tid.set(tid);
};
const string transactionid() {
return string(_tid);
};
void thunk(void * t) {
_thunk = t;
};
bool thunk() {
return _thunk;
};
private:
func _next_send; /* The next response function in the chain */
Xrl _xrl; /* The prebuilt xrl */
bool _completed;
bool _error;
int _crank; /* How many times have we been through the
* transaction complete.
*/
TransactionID _tid;
void *_thunk; /* just in case we didn't think of something. */
};
/**
* response function for reading a routing table entry.
*/
static
void
read_entry(const XrlError& e, XrlRouter& xrlrouter,
const Xrl& debug_use(request),
XrlArgs* response, void* cookie)
{
XLOG_ASSERT(e == XrlError::OKAY());
debug_msg("%s %s\n", request.command(), string(*response).c_str());
routing_cookie *rc = reinterpret_cast<routing_cookie *>(cookie);
try {
if (false == response->get_int32("result")) {
rc->completed(true);
return;
}
} catch(XrlArgs::XrlAtomNotFound) {
XLOG_ERROR("Failed to extract argument result");
rc->completed(true);
rc->error(true);
return;
}
Fte4 fte;
const char *current;
try {
#define getter(ret, name) current = name;fte.ret = response->get_ipv4(name)
getter(dst, "dst");
getter(nexthop, "nexthop");
getter(netmask, "netmask");
#undef getter
current = "ifname"; fte.ifname = response->get_string(current);
} catch(XrlArgs::XrlAtomNotFound) {
XLOG_ERROR("Failed to extract argument %s", current);
rc->completed(true);
rc->error(true);
return;
}
printf("%s\n", fte.str().c_str());
Xrl x(server, "read_entry");
x.args().add_string("TransactionID", rc->transactionid());
debug_msg("request %s response read_entry\n", string(x).c_str());
xrlrouter.send(x, read_entry, rc);
}
/**
* response function for starting to read a routing table entry.
*/
static
void
start_reading(const XrlError& e, XrlRouter& xrlrouter,
const Xrl& debug_use(request),
XrlArgs* response, void* cookie)
{
XLOG_ASSERT(e == XrlError::OKAY());
debug_msg("%s %s\n", request.command(), string(*response).c_str());
routing_cookie *rc = reinterpret_cast<routing_cookie *>(cookie);
const char *current;
try {
current = "result";
if (false == response->get_int32(current)) {
rc->completed(true);
rc->error(true);
return;
}
current = "TransactionID";
rc->transactionid(response->get_string(current));
} catch(XrlArgs::XrlAtomNotFound) {
XLOG_ERROR("Failed to extract argument %s", current);
rc->completed(true);
rc->error(true);
return;
}
Xrl x(server, "read_entry");
x.args().add_string("TransactionID", rc->transactionid());
debug_msg("request %s response read_entry\n", string(x).c_str());
xrlrouter.send(x, read_entry, rc);
}
/**
* response function for starting a transaction.
*/
static
void
start_transaction(const XrlError& e, XrlRouter& xrlrouter,
const Xrl& /* request */, XrlArgs* response,
void* cookie)
{
XLOG_ASSERT(e == XrlError::OKAY());
routing_cookie *rc = reinterpret_cast<routing_cookie *>(cookie);
const char *current;
try {
current = "result";
if (false == response->get_int32("result")) {
XLOG_ERROR("false == result");
rc->completed(true);
rc->error(true);
return;
}
current = "TransactionID";
rc->transactionid(response->get_string(current));
rc->xrl().args().add_string("TransactionID", rc->transactionid());
} catch(XrlArgs::XrlAtomNotFound) {
XLOG_ERROR("Failed to extract argument %s", current);
rc->completed(true);
rc->error(true);
return;
}
/*
** Start transaction can be called from many contexts. So in the
** cookie we stash the precomputed xrl and the next response
** function. This way we don't have to write a response function
** for add, delete and delete_all.
**
*/
debug_msg("request %s\n", string(rc->xrl()).c_str());
xrlrouter.send(rc->xrl(), rc->next_send(), rc);
}
/**
* response function after adding/deleting an entry/entries
*/
static
void
response_complete(const XrlError& e, XrlRouter& xrlrouter,
const Xrl& debug_use(request),
XrlArgs* response, void* cookie)
{
debug_msg("%s %s\n", request.command(), string(*response).c_str());
XLOG_ASSERT(e == XrlError::OKAY());
routing_cookie *rc = reinterpret_cast<routing_cookie *>(cookie);
try {
if (false == response->get_int32("result")) {
XLOG_ERROR("false == result");
/*
** Abort the transaction here
*/
rc->error(true);
rc->crank(1);
Xrl x(server, "abort_transaction");
x.args().add_string("TransactionID", rc->transactionid());
debug_msg("request %s\n", string(x).c_str());
xrlrouter.send(x, response_complete, rc);
return;
}
} catch(XrlArgs::XrlAtomNotFound) {
XLOG_ERROR("Failed to extract argument result");
rc->completed(true);
rc->error(true);
return;
}
debug_msg("crank %d\n", rc->crank());
switch (rc->crank()) {
case 0: {
rc->crank(1);
Xrl x(server, "commit_transaction");
x.args().add_string("TransactionID", rc->transactionid());
debug_msg("request %s response response_complete\n",
string(x).c_str());
xrlrouter.send(x, response_complete, rc);
return;
break;
}
case 1:
rc->completed(true);
break;
default:
XLOG_ERROR("unexpected crank %d", rc->crank());
rc->completed(true);
break;
}
}
void
print_routing_table(const char *host)
{
EventLoop eventloop;
string hostname = host;
if (0 == host)
hostname = FinderConstants::FINDER_DEFAULT_HOST().str();
XrlStdRouter test_fea(eventloop, client, hostname.c_str());
routing_cookie rc;
Xrl x(server, "start_reading");
debug_msg("request %s response start_reading\n", string(x).c_str());
test_fea.send(x, start_reading, (void *)&rc);
while(rc.completed() == false)
eventloop.run();
}
/**
* Example of how to add a routing table entry using XRL's
*
* @param host where the finder is
* @param tt transaction type Fti::SYNC or Fti::ASYNC
* @param dst destination address
* @param nexthop the next-hop router to which packets should be sent
* @param netmask netmask associated with the destination address
* @param ifname interface through which the packets should leave
*
* @return true on a success
*/
bool
add(const char *host, Fti::TransactionType tt, char *dst, char *nexthop,
char *netmask, char *ifname)
{
EventLoop eventloop;
if (0 == host)
host = FinderConstants::FINDER_DEFAULT_HOST().str().c_str();
XrlStdRouter test_fea(eventloop, client, host);
/*
** The response function to the send is common for all transaction
** starts (transaction_start). Therefore the cookie contains a
** prebuilt XRL for the next send command to be executed by the
** transaction_start. As well as a common transaction_complete
** response function.
*/
routing_cookie rc;
Xrl xadd(server, "add_entry_old");
xadd.args().
add_ipv4("dst", dst).
add_ipv4("nexthop", nexthop).
add_ipv4("netmask", netmask).
add_string("ifname", ifname);
rc.xrl(xadd);
rc.set_next_send(response_complete);
/*
** Load up the XRL arguments for this call.
*/
Xrl x(server, "start_transaction");
x.args().add_int32("TransactionType", tt);
debug_msg("request %s response start_transaction\n", string(x).c_str());
test_fea.send(x, start_transaction, &rc);
while(rc.completed() == false)
eventloop.run();
return rc.error();
}
/**
* Example of how to delete a routing table entry using XRL's
*
* @param host where the finder is
* @param tt transaction type Fti::SYNC or Fti::ASYNC
* @param dst destination address
* @param netmask netmask associated with the destination address
*
* @return true on a success
*/
bool
del(const char *host, Fti::TransactionType tt, char *dst, char *netmask)
{
EventLoop eventloop;
if (0 == host)
host = FinderConstants::FINDER_DEFAULT_HOST().str().c_str();
XrlStdRouter test_fea(eventloop, client, host);
/*
** begin - set up the routing cookie with the next call.
*/
routing_cookie rc;
Xrl xadd(server, "delete_entry_old");
xadd.args().
add_ipv4("dst", dst).
add_ipv4("netmask", netmask);
rc.xrl(xadd);
rc.set_next_send(response_complete);
/*
** end
*/
Xrl x(server, "start_transaction");
x.args().add_int32("TransactionType", tt);
debug_msg("request %s response start_transaction\n", string(x).c_str());
test_fea.send(x, start_transaction, &rc);
while(rc.completed() == false)
eventloop.run();
return rc.error();
}
class DeviceCallback {
public:
DeviceCallback() : _completed(false),
_error(false)
{}
/*
** Create interface response method.
*/
void
create_interface(const XrlError& e, XrlRouter& /*xrlrouter*/,
const Xrl& /* request */, XrlArgs* response)
{
XLOG_ASSERT(e == XrlError::OKAY());
const char *current;
try {
current = "result";
if (false == response->get_int32("result")) {
XLOG_ERROR("false == result %s",
response->get_string("error").c_str());
_completed = true;
_error = true;
return;
}
} catch(XrlArgs::XrlAtomNotFound) {
XLOG_ERROR("Failed to extract argument %s", current);
_completed = true;
_error = true;
return;
}
_completed = true;
}
bool completed() {
return _completed;
}
private:
bool _completed;
bool _error;
};
bool
create_interface(const char *host, char *interface)
{
EventLoop eventloop;
if (0 == host)
host = FinderConstants::FINDER_DEFAULT_HOST().str().c_str();
XrlStdRouter test_fea(eventloop, client, host);
Xrl x(server, "create_interface");
x.args().add_string("ifname", interface);
debug_msg("request %s response create_interface\n", string(x).c_str());
DeviceCallback dcb;
test_fea.send(x, callback(dcb, &DeviceCallback::create_interface));
while(dcb.completed() == false)
eventloop.run();
return true;
}
void
usage(char *name)
{
fprintf (stderr,
"usage: %s [\n"
"(routing table)\t -r\n"
"(remote host)\t -h\n"
"(trans type)\t -t sync|async\n"
"(add)\t\t -a <dst> <nexthop> <netmask> <interface>\n"
"(delete)\t -d <dst> <netmask> \n"
"(interface)\t -i create_interface le0 | -i delete_interface le0"
#if LATER
"(zap)\t\t -z\n"
"(lookup)\t -l <dst>\n"
"(test) \t\t -T <test number>\n"
"(debug)\t\t -D\n"
#endif
"]\n",
name);
exit(-1);
}
int
main(int argc, char *argv[])
{
int c;
bool prt = false;
Fti::TransactionType tt = Fti::SYNC;
char *host = 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();
while((c = getopt (argc, argv, "rh:t:a:d:i:")) != EOF) {
switch (c) {
case 'r':
prt = true;
break;
case 'h':
host = optarg;
break;
case 't':
if (0 == strcmp(optarg, "sync")) {
tt = Fti::SYNC;
} else if (0 == strcmp(optarg, "async")) {
tt = Fti::ASYNC;
} else
usage(argv[0]);
break;
case 'a':
/*
** Check we have enough arguments.
*/
if (optind + 3 > argc)
usage(argv[0]);
add(host, tt, optarg, argv[optind], argv[optind + 1],
argv[optind + 2]);
optind += 3;
break;
case 'd':
/*
** Check we have enough arguments.
*/
if (optind + 1 > argc)
usage(argv[0]);
del(host, tt, optarg, argv[optind]);
optind += 1;
break;
case 'i':
if (0 == strcmp(optarg, "create_interface")) {
create_interface(host, argv[optind]);
} else if (0 == strcmp(optarg, "delete_interface")) {
// delete_interface(host, optarg);
} else
usage(argv[0]);
break;
case '?':
usage(argv[0]);
}
}
if (prt)
print_routing_table(host);
/*
** Gracefully stop and exit xlog
*/
xlog_stop();
xlog_exit();
exit (0);
}
syntax highlighted by Code2HTML, v. 0.9.1