// -*- c-basic-offset: 4; related-file-name: "../include/click/router.hh" -*-
/*
* router.{cc,hh} -- a Click router configuration
* Eddie Kohler
*
* Copyright (c) 1999-2000 Massachusetts Institute of Technology
* Copyright (c) 2000 Mazu Networks, Inc.
* Copyright (c) 2004-2005 Regents of the University of California
*
* 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 Click 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 Click LICENSE file; the license in that file is
* legally binding.
*/
#define WANT_MOD_USE_COUNT 1
#include <click/config.h>
#include <click/router.hh>
#include <click/routerthread.hh>
#include <click/bitvector.hh>
#include <click/error.hh>
#include <click/straccum.hh>
#include <click/elemfilter.hh>
#include <click/confparse.hh>
#include <click/timer.hh>
#include <click/master.hh>
#include <click/notifier.hh>
#include <click/nameinfo.hh>
#include <click/bighashmap_arena.hh>
#include <click/standard/errorelement.hh>
#include <click/standard/threadsched.hh>
#include <stdarg.h>
#ifdef CLICK_USERLEVEL
# include <unistd.h>
#endif
#ifdef CLICK_NS
#include "../elements/ns/fromsimdevice.hh"
#endif
CLICK_DECLS
/** @class Router
* @brief A router configuration.
*/
const Handler* Handler::the_blank_handler;
static Handler* globalh;
static int nglobalh;
static int globalh_cap;
Router::Router(const String &configuration, Master *m)
: _master(0), _state(ROUTER_NEW), _have_connections(false),
_running(RUNNING_INACTIVE),
_handler_bufs(0), _nhandlers_bufs(0), _free_handler(-1),
_root_element(0),
_configuration(configuration),
_notifier_signals(0), _n_notifier_signals(0),
_arena_factory(new HashMap_ArenaFactory),
_hotswap_router(0), _thread_sched(0), _name_info(0), _next_router(0)
{
_refcount = 0;
_runcount = 0;
_root_element = new ErrorElement;
_root_element->attach_router(this, -1);
m->register_router(this);
}
Router::~Router()
{
if (_refcount != 0)
click_chatter("deleting router while ref count = %d", _refcount.value());
// unuse the hotswap router
if (_hotswap_router)
_hotswap_router->unuse();
// Delete the ArenaFactory, which detaches the Arenas
delete _arena_factory;
// Clean up elements in reverse configuration order
if (_state == ROUTER_LIVE) {
// Unschedule tasks and timers
_master->kill_router(this);
for (int ord = _elements.size() - 1; ord >= 0; ord--)
_elements[ _element_configure_order[ord] ]->cleanup(Element::CLEANUP_ROUTER_INITIALIZED);
} else if (_state != ROUTER_DEAD) {
assert(_element_configure_order.size() == 0 && _state <= ROUTER_PRECONFIGURE);
for (int i = _elements.size() - 1; i >= 0; i--)
_elements[i]->cleanup(Element::CLEANUP_NO_ROUTER);
}
// Delete elements in reverse configuration order
if (_element_configure_order.size())
for (int ord = _elements.size() - 1; ord >= 0; ord--)
delete _elements[ _element_configure_order[ord] ];
else
for (int i = 0; i < _elements.size(); i++)
delete _elements[i];
delete _root_element;
#if CLICK_LINUXMODULE
// decrement module use counts
for (struct module **m = _modules.begin(); m < _modules.end(); m++) {
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
module_put(*m);
# else
__MOD_DEC_USE_COUNT(*m);
# endif
}
#endif
for (int i = 0; i < _nhandlers_bufs; i += HANDLER_BUFSIZ)
delete[] _handler_bufs[i / HANDLER_BUFSIZ];
delete[] _handler_bufs;
delete[] _notifier_signals;
delete _name_info;
if (_master)
_master->unregister_router(this);
}
void
Router::unuse()
{
if (_refcount.dec_and_test())
delete this;
}
// ACCESS
Element *
Router::find(const String &name, String prefix, ErrorHandler *errh) const
{
while (1) {
int got = -1;
String n = prefix + name;
for (int i = 0; i < _elements.size(); i++)
if (_element_names[i] == n) {
if (got >= 0) {
if (errh) errh->error("more than one element named '%s'", n.c_str());
return 0;
} else
got = i;
}
if (got >= 0)
return _elements[got];
if (!prefix)
break;
int slash = prefix.find_right('/', prefix.length() - 2);
prefix = (slash >= 0 ? prefix.substring(0, slash + 1) : String());
}
if (errh)
errh->error("no element named '%s'", name.c_str());
return 0;
}
Element *
Router::find(const String &name, Element *context, ErrorHandler *errh) const
{
String prefix = ename(context->eindex());
int slash = prefix.find_right('/');
return find(name, (slash >= 0 ? prefix.substring(0, slash + 1) : String()), errh);
}
Element *
Router::element(const Router *r, int ei)
{
if (r && ei >= 0 && ei < r->nelements())
return r->_elements[ei];
else if (r && ei == -1)
return r->root_element();
else
return 0;
}
const String &
Router::ename(int ei) const
{
if (ei < 0 || ei >= nelements())
return String::empty_string();
else
return _element_names[ei];
}
const String &
Router::default_configuration_string(int ei) const
{
if (ei < 0 || ei >= nelements())
return String::empty_string();
else
return _element_configurations[ei];
}
void
Router::set_default_configuration_string(int ei, const String &s)
{
if (ei >= 0 && ei < nelements())
_element_configurations[ei] = s;
}
const String &
Router::elandmark(int ei) const
{
if (ei < 0 || ei >= nelements())
return String::empty_string();
else
return _element_landmarks[ei];
}
// CREATION
#if CLICK_LINUXMODULE
int
Router::add_module_ref(struct module *module)
{
if (!module)
return 0;
for (struct module **m = _modules.begin(); m < _modules.end(); m++)
if (*m == module)
return 0;
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 9)
if (try_module_get(module) == 0)
return -1;
# else
__MOD_INC_USE_COUNT(module);
# endif
_modules.push_back(module);
return 0;
}
#endif
int
Router::add_element(Element *e, const String &ename, const String &conf, const String &landmark)
{
if (_state != ROUTER_NEW || !e || e->router())
return -1;
_elements.push_back(e);
_element_names.push_back(ename);
_element_landmarks.push_back(landmark);
_element_configurations.push_back(conf);
int i = _elements.size() - 1;
e->attach_router(this, i);
// router now owns the element
return i;
}
int
Router::add_connection(int from_idx, int from_port, int to_idx, int to_port)
{
assert(from_idx >= 0 && from_port >= 0 && to_idx >= 0 && to_port >= 0);
if (_state != ROUTER_NEW)
return -1;
Hookup hfrom(from_idx, from_port);
Hookup hto(to_idx, to_port);
// only add new connections
for (int i = 0; i < _hookup_from.size(); i++)
if (_hookup_from[i] == hfrom && _hookup_to[i] == hto)
return 0;
_hookup_from.push_back(hfrom);
_hookup_to.push_back(hto);
return 0;
}
void
Router::add_requirement(const String &r)
{
assert(cp_is_word(r));
_requirements.push_back(r);
}
// CHECKING HOOKUP
void
Router::remove_hookup(int c)
{
_hookup_from[c] = _hookup_from.back();
_hookup_from.pop_back();
_hookup_to[c] = _hookup_to.back();
_hookup_to.pop_back();
}
void
Router::hookup_error(const Hookup &h, bool is_from, const char *message,
ErrorHandler *errh)
{
bool is_output = is_from;
const char *kind = (is_output ? "output" : "input");
element_lerror(errh, _elements[h.idx], message,
_elements[h.idx], kind, h.port);
}
int
Router::check_hookup_elements(ErrorHandler *errh)
{
if (!errh)
errh = ErrorHandler::default_handler();
int before = errh->nerrors();
// Check each hookup to ensure it connects valid elements
for (int c = 0; c < _hookup_from.size(); c++) {
Hookup &hfrom = _hookup_from[c];
Hookup &hto = _hookup_to[c];
int before = errh->nerrors();
if (hfrom.idx < 0 || hfrom.idx >= nelements() || !_elements[hfrom.idx])
errh->error("bad element number '%d'", hfrom.idx);
if (hto.idx < 0 || hto.idx >= nelements() || !_elements[hto.idx])
errh->error("bad element number '%d'", hto.idx);
if (hfrom.port < 0)
errh->error("bad port number '%d'", hfrom.port);
if (hto.port < 0)
errh->error("bad port number '%d'", hto.port);
// remove the connection if there were errors
if (errh->nerrors() != before) {
remove_hookup(c);
c--;
}
}
return (errh->nerrors() == before ? 0 : -1);
}
void
Router::notify_hookup_range(ErrorHandler *errh)
{
// Count inputs and outputs, and notify elements how many they have
Vector<int> nin(nelements(), -1);
Vector<int> nout(nelements(), -1);
for (int c = 0; c < _hookup_from.size(); c++) {
if (_hookup_from[c].port > nout[_hookup_from[c].idx])
nout[_hookup_from[c].idx] = _hookup_from[c].port;
if (_hookup_to[c].port > nin[_hookup_to[c].idx])
nin[_hookup_to[c].idx] = _hookup_to[c].port;
}
for (int f = 0; f < nelements(); f++)
_elements[f]->notify_nports(nin[f] + 1, nout[f] + 1, errh);
}
int
Router::check_hookup_range(ErrorHandler *errh, bool check_only)
{
int before_all = errh->nerrors();
// Check each hookup to ensure its port numbers are within range
for (int c = 0; c < _hookup_from.size(); c++) {
Hookup &hfrom = _hookup_from[c];
Hookup &hto = _hookup_to[c];
int before = errh->nerrors();
if (hfrom.port >= _elements[hfrom.idx]->noutputs())
hookup_error(hfrom, true, "'%{element}' has no %s %d", errh);
if (hto.port >= _elements[hto.idx]->ninputs())
hookup_error(hto, false, "'%{element}' has no %s %d", errh);
// remove the connection if there were errors
if (errh->nerrors() == before)
/* do nothing */;
else if (!check_only) {
remove_hookup(c);
c--;
}
}
return errh->nerrors() == before_all ? 0 : -1;
}
int
Router::check_hookup_completeness(ErrorHandler *errh, bool check_only)
{
Bitvector used_outputs(ngports(true), false);
Bitvector used_inputs(ngports(false), false);
int before_all = errh->nerrors();
// Check each hookup to ensure it doesn't reuse a port.
// Completely duplicate connections never got into the Router
for (int c = 0; c < _hookup_from.size(); c++) {
Hookup &hfrom = _hookup_from[c];
Hookup &hto = _hookup_to[c];
int before = errh->nerrors();
int fromg = gport(true, hfrom);
int tog = gport(false, hto);
if (used_outputs[fromg]
&& _elements[hfrom.idx]->output_is_push(hfrom.port))
hookup_error(hfrom, true, "can't reuse '%{element}' push %s %d", errh);
else if (used_inputs[tog]
&& _elements[hto.idx]->input_is_pull(hto.port))
hookup_error(hto, false, "can't reuse '%{element}' pull %s %d", errh);
// remove the connection if there were errors
if (errh->nerrors() == before) {
used_outputs[fromg] = true;
used_inputs[tog] = true;
} else if (!check_only) {
remove_hookup(c);
c--;
}
}
// Check for unused inputs and outputs.
for (int g = 0; g < ngports(false); g++)
if (!used_inputs[g])
hookup_error(gport_hookup(false, g), false, "'%{element}' %s %d unused", errh);
for (int g = 0; g < ngports(true); g++)
if (!used_outputs[g])
hookup_error(gport_hookup(true, g), true, "'%{element}' %s %d unused", errh);
return errh->nerrors() == before_all ? 0 : -1;
}
// PORT INDEXES
void
Router::make_gports()
{
_gports[0].e2g.assign(1, 0);
_gports[1].e2g.assign(1, 0);
_gports[0].g2e.clear();
_gports[1].g2e.clear();
for (int i = 0; i < _elements.size(); i++) {
Element *e = _elements[i];
_gports[0].e2g.push_back(_gports[0].e2g.back() + e->ninputs());
_gports[1].e2g.push_back(_gports[1].e2g.back() + e->noutputs());
for (int j = 0; j < e->ninputs(); j++)
_gports[0].g2e.push_back(i);
for (int j = 0; j < e->noutputs(); j++)
_gports[1].g2e.push_back(i);
}
}
inline int
Router::gport(bool isoutput, const Hookup &h) const
{
return _gports[isoutput].e2g[h.idx] + h.port;
}
inline Router::Hookup
Router::gport_hookup(bool isoutput, int g) const
{
int e = _gports[isoutput].g2e[g];
return Hookup(e, g - _gports[isoutput].e2g[e]);
}
void
Router::gport_list_elements(bool isoutput, const Bitvector& bv, Vector<Element*>& results) const
{
const Gport& gport = _gports[isoutput];
int ng = gport.size();
assert(bv.size() == ng);
const int* gp = gport.e2g.begin();
for (int g = 0; g < ng; ) {
while (g >= gp[1])
gp++;
if (bv[g]) {
results.push_back(_elements[gp - gport.e2g.begin()]);
g = gp[1];
} else
g++;
}
}
void
Router::make_hookup_gports()
{
if (_hookup_gports[0].size() != _hookup_to.size()) {
for (int c = 0; c < _hookup_to.size(); c++) {
_hookup_gports[0].push_back(gport(false, _hookup_to[c]));
_hookup_gports[1].push_back(gport(true, _hookup_from[c]));
}
assert(_hookup_gports[0].size() == _hookup_to.size());
}
}
// PROCESSING
int
Router::processing_error(const Hookup &hfrom, const Hookup &hto, bool aggie,
int processing_from, ErrorHandler *errh)
{
const char *type1 = (processing_from == Element::VPUSH ? "push" : "pull");
const char *type2 = (processing_from == Element::VPUSH ? "pull" : "push");
if (!aggie)
errh->error("'%{element}' %s output %d connected to '%{element}' %s input %d",
_elements[hfrom.idx], type1, hfrom.port,
_elements[hto.idx], type2, hto.port);
else
errh->error("agnostic '%{element}' in mixed context: %s input %d, %s output %d",
_elements[hfrom.idx], type2, hto.port, type1, hfrom.port);
return -1;
}
int
Router::check_push_and_pull(ErrorHandler *errh)
{
if (!errh)
errh = ErrorHandler::default_handler();
// set up processing vectors
Vector<int> input_pers(ngports(false), 0);
Vector<int> output_pers(ngports(true), 0);
for (int e = 0; e < nelements(); e++)
_elements[e]->processing_vector(input_pers.begin() + _gports[0].e2g[e], output_pers.begin() + _gports[1].e2g[e], errh);
// add fake connections for agnostics
Vector<Hookup> hookup_from = _hookup_from;
Vector<Hookup> hookup_to = _hookup_to;
Bitvector bv;
for (int* inputp = input_pers.begin(); inputp < input_pers.end(); inputp++)
if (*inputp == Element::VAGNOSTIC) {
Hookup h = gport_hookup(false, inputp - input_pers.begin());
_elements[h.idx]->port_flow(false, h.port, &bv);
int og = _gports[1].e2g[h.idx];
for (int j = 0; j < bv.size(); j++)
if (bv[j] && output_pers[og+j] == Element::VAGNOSTIC) {
hookup_from.push_back(Hookup(h.idx, j));
hookup_to.push_back(Hookup(h.idx, h.port));
}
}
int before = errh->nerrors();
int first_agnostic = _hookup_from.size();
// spread personalities
while (true) {
bool changed = false;
for (int c = 0; c < hookup_from.size(); c++) {
if (hookup_from[c].idx < 0)
continue;
int gf = gport(true, hookup_from[c]);
int gt = gport(false, hookup_to[c]);
int pf = output_pers[gf];
int pt = input_pers[gt];
switch (pt) {
case Element::VAGNOSTIC:
if (pf != Element::VAGNOSTIC) {
input_pers[gt] = pf;
changed = true;
}
break;
case Element::VPUSH:
case Element::VPULL:
if (pf == Element::VAGNOSTIC) {
output_pers[gf] = pt;
changed = true;
} else if (pf != pt) {
processing_error(hookup_from[c], hookup_to[c], c >= first_agnostic, pf, errh);
hookup_from[c].idx = -1;
}
break;
}
}
if (!changed)
break;
}
if (errh->nerrors() != before)
return -1;
for (int e = 0; e < nelements(); e++)
_elements[e]->initialize_ports(input_pers.begin() + _gports[0].e2g[e], output_pers.begin() + _gports[1].e2g[e]);
return 0;
}
// SET CONNECTIONS
int
Router::element_lerror(ErrorHandler *errh, Element *e,
const char *format, ...) const
{
va_list val;
va_start(val, format);
errh->verror(ErrorHandler::ERR_ERROR, e->landmark(), format, val);
va_end(val);
return -1;
}
void
Router::set_connections()
{
// actually assign ports
for (int c = 0; c < _hookup_from.size(); c++) {
Hookup& hfrom = _hookup_from[c];
Element* frome = _elements[hfrom.idx];
Hookup& hto = _hookup_to[c];
Element* toe = _elements[hto.idx];
frome->connect_port(true, hfrom.port, toe, hto.port);
toe->connect_port(false, hto.port, frome, hfrom.port);
}
_have_connections = true;
}
// RUNCOUNT
void
Router::set_runcount(int32_t x)
{
_runcount = x;
if (x <= 0) {
_master->set_stopper(1);
// ensure that at least one thread is awake to handle the stop event
_master->_threads[2]->wake();
}
}
void
Router::adjust_runcount(int32_t delta)
{
// beware of overflow
int32_t old_value, new_value;
do {
old_value = _runcount;
if (delta > 0 && old_value > 0x7FFFFFFF - delta)
new_value = 0x7FFFFFFF;
else if (delta < 0 && old_value < STOP_RUNCOUNT - delta)
new_value = STOP_RUNCOUNT;
else
new_value = old_value + delta;
} while (!_runcount.compare_and_swap(old_value, new_value));
if (new_value <= 0) {
_master->set_stopper(1);
// ensure that at least one thread is awake to handle the stop event
_master->_threads[2]->wake();
}
}
// FLOWS
int
Router::global_port_flow(bool forward, Element* first_element, int first_port, ElementFilter* stop_filter, Bitvector& results)
{
if (!_have_connections || first_element->router() != this)
return -1;
make_hookup_gports();
int nresult = ngports(!forward);
int nsource = ngports(forward);
Bitvector old_results(nresult, false);
results.assign(nresult, false);
Bitvector diff, scratch;
Bitvector source(nsource, false);
int first_gport = _gports[forward].e2g[first_element->eindex()];
if (first_port < 0)
for (int i = 0; i < first_element->nports(forward); i++)
source[first_gport + i] = true;
else if (first_port < first_element->nports(forward))
source[first_gport + first_port] = true;
while (true) {
old_results = results;
for (const int* gfrom = _hookup_gports[forward].begin(); gfrom < _hookup_gports[forward].end(); gfrom++)
if (source[*gfrom]) {
int i = gfrom - _hookup_gports[forward].begin();
results[_hookup_gports[!forward][i]] = true;
}
diff = results - old_results;
if (diff.zero())
break;
source.assign(nsource, false);
for (int g = 0; g < nresult; g++)
if (diff[g]) {
Hookup h = gport_hookup(!forward, g);
if (!stop_filter || !stop_filter->match_port(_elements[h.idx], !forward, h.port)) {
_elements[h.idx]->port_flow(!forward, h.port, &scratch);
source.or_at(scratch, _gports[forward].e2g[h.idx]);
}
}
}
return 0;
}
int
Router::downstream_elements(Element* first_element, int first_output, ElementFilter* stop_filter, Vector<Element*>& results)
{
Bitvector bv;
if (global_port_flow(true, first_element, first_output, stop_filter, bv) < 0)
return -1;
gport_list_elements(false, bv, results);
return 0;
}
int
Router::downstream_elements(Element* first_element, int first_output,
Vector<Element*>& results)
{
return downstream_elements(first_element, first_output, 0, results);
}
int
Router::downstream_elements(Element* first_element, Vector<Element*>& results)
{
return downstream_elements(first_element, -1, 0, results);
}
int
Router::upstream_elements(Element* first_element, int first_input, ElementFilter* stop_filter, Vector<Element*>& results)
{
Bitvector bv;
if (global_port_flow(false, first_element, first_input, stop_filter, bv) < 0)
return -1;
gport_list_elements(true, bv, results);
return 0;
}
int
Router::upstream_elements(Element* first_element, int first_input, Vector<Element*>& results)
{
return upstream_elements(first_element, first_input, 0, results);
}
int
Router::upstream_elements(Element* first_element, Vector<Element*>& results)
{
return upstream_elements(first_element, -1, 0, results);
}
// INITIALIZATION
String
Router::context_message(int element_no, const char* message) const
{
Element* e = _elements[element_no];
StringAccum sa;
if (e->landmark())
sa << e->landmark() << ": ";
sa << message << " '" << e->declaration() << "':";
return sa.take_string();
}
static int
configure_order_compar(const void *athunk, const void *bthunk, void *copthunk)
{
const int* a = (const int*) athunk, *b = (const int*) bthunk;
const int* configure_order_phase = (const int*) copthunk;
return configure_order_phase[*a] - configure_order_phase[*b];
}
inline Handler*
Router::xhandler(int hi) const
{
return &_handler_bufs[hi / HANDLER_BUFSIZ][hi % HANDLER_BUFSIZ];
}
void
Router::initialize_handlers(bool defaults, bool specifics)
{
_ehandler_first_by_element.assign(nelements(), -1);
_ehandler_to_handler.clear();
_ehandler_next.clear();
_handler_first_by_name.clear();
for (int i = 0; i < _nhandlers_bufs; i += HANDLER_BUFSIZ)
delete[] _handler_bufs[i / HANDLER_BUFSIZ];
_nhandlers_bufs = 0;
_free_handler = -1;
if (defaults)
for (int i = 0; i < _elements.size(); i++)
_elements[i]->add_default_handlers(specifics);
if (specifics)
for (int i = 0; i < _elements.size(); i++)
_elements[i]->add_handlers();
}
int
Router::initialize(ErrorHandler *errh)
{
if (_state != ROUTER_NEW)
return errh->error("second attempt to initialize router");
_state = ROUTER_PRECONFIGURE;
// initialize handlers to empty
initialize_handlers(false, false);
// clear attachments
_attachment_names.clear();
_attachments.clear();
if (check_hookup_elements(errh) < 0)
return -1;
_runcount = 1;
_master->prepare_router(this);
// set up configuration order
_element_configure_order.assign(nelements(), 0);
if (_element_configure_order.size()) {
Vector<int> configure_phase(nelements(), 0);
for (int i = 0; i < _elements.size(); i++) {
configure_phase[i] = _elements[i]->configure_phase();
_element_configure_order[i] = i;
}
click_qsort(&_element_configure_order[0], _element_configure_order.size(), sizeof(int), configure_order_compar, configure_phase.begin());
}
// notify elements of hookup range
// XXX Note: When set_ninputs/set_noutputs is fully removed, this should
// change to report errors and bail out if necessary.
notify_hookup_range(errh);
if (check_hookup_range(ErrorHandler::silent_handler(), true) >= 0) {
make_gports();
if (check_push_and_pull(ErrorHandler::silent_handler()) >= 0
&& check_hookup_completeness(ErrorHandler::silent_handler(), true) >= 0)
set_connections();
}
// Configure all elements in configure order. Remember the ones that failed
Vector<int> element_stage(nelements(), Element::CLEANUP_CONFIGURED);
bool all_ok = true;
Vector<String> conf;
#if CLICK_DMALLOC
char dmalloc_buf[12];
#endif
for (int ord = 0; ord < _elements.size(); ord++) {
int i = _element_configure_order[ord];
#if CLICK_DMALLOC
sprintf(dmalloc_buf, "c%d ", i);
CLICK_DMALLOC_REG(dmalloc_buf);
#endif
ContextErrorHandler cerrh
(errh, context_message(i, "While configuring"));
int before = cerrh.nerrors();
conf.clear();
cp_argvec(_element_configurations[i], conf);
if (_elements[i]->configure(conf, &cerrh) < 0) {
element_stage[i] = Element::CLEANUP_CONFIGURE_FAILED;
all_ok = false;
if (cerrh.nerrors() == before)
cerrh.error("unspecified error");
}
}
#if CLICK_DMALLOC
CLICK_DMALLOC_REG("iHoo");
#endif
int before = errh->nerrors();
if (!_have_connections) {
check_hookup_range(errh, false);
make_gports();
check_push_and_pull(errh);
check_hookup_completeness(errh, false);
set_connections();
}
_state = ROUTER_PREINITIALIZE;
if (before != errh->nerrors())
all_ok = false;
// Initialize elements if OK so far.
if (all_ok) {
initialize_handlers(true, true);
for (int ord = 0; all_ok && ord < _elements.size(); ord++) {
int i = _element_configure_order[ord];
assert(element_stage[i] == Element::CLEANUP_CONFIGURED);
#if CLICK_DMALLOC
sprintf(dmalloc_buf, "i%d ", i);
CLICK_DMALLOC_REG(dmalloc_buf);
#endif
ContextErrorHandler cerrh
(errh, context_message(i, "While initializing"));
int before = cerrh.nerrors();
if (_elements[i]->initialize(&cerrh) >= 0)
element_stage[i] = Element::CLEANUP_INITIALIZED;
else {
// don't report 'unspecified error' for ErrorElements:
// keep error messages clean
if (cerrh.nerrors() == before && !_elements[i]->cast("Error"))
cerrh.error("unspecified error");
element_stage[i] = Element::CLEANUP_INITIALIZE_FAILED;
all_ok = false;
}
}
}
#if CLICK_DMALLOC
CLICK_DMALLOC_REG("iXXX");
#endif
// If there were errors, uninitialize any elements that we initialized
// successfully and return -1 (error). Otherwise, we're all set!
if (all_ok) {
_state = ROUTER_LIVE;
#ifdef CLICK_NAMEDB_CHECK
NameInfo::check(_root_element, errh);
#endif
return 0;
} else {
_state = ROUTER_DEAD;
errh->verror_text(ErrorHandler::ERR_CONTEXT_ERROR, "", "Router could not be initialized!");
// Unschedule tasks and timers
master()->kill_router(this);
// Clean up elements
for (int ord = _elements.size() - 1; ord >= 0; ord--) {
int i = _element_configure_order[ord];
_elements[i]->cleanup((Element::CleanupStage) element_stage[i]);
}
// Remove element-specific handlers
initialize_handlers(true, false);
_runcount = 0;
return -1;
}
}
void
Router::activate(bool foreground, ErrorHandler *errh)
{
if (_state != ROUTER_LIVE || _running != RUNNING_PREPARING)
return;
// Take state if appropriate
if (_hotswap_router && _hotswap_router->_state == ROUTER_LIVE) {
// Unschedule tasks and timers
master()->kill_router(_hotswap_router);
for (int i = 0; i < _elements.size(); i++) {
Element *e = _elements[_element_configure_order[i]];
if (Element *other = e->hotswap_element()) {
ContextErrorHandler cerrh
(errh, context_message(i, "While hot-swapping state into"));
e->take_state(other, &cerrh);
}
}
}
if (_hotswap_router) {
_hotswap_router->unuse();
_hotswap_router = 0;
}
// Activate router
master()->run_router(this, foreground);
// sets _running to RUNNING_BACKGROUND or RUNNING_ACTIVE
}
// steal state
void
Router::set_hotswap_router(Router *r)
{
assert(_state == ROUTER_NEW && !_hotswap_router && (!r || r->initialized()));
_hotswap_router = r;
if (_hotswap_router)
_hotswap_router->use();
}
// HANDLERS
/** @class Handler
@brief Represents a router's handlers.
Each handler is represented by a Handler object. Handlers are not
attached to specific elements, allowing a single handler object to be
shared among multiple elements with the same basic handler definition.
Handlers cannot be created directly; to create one, call methods such as
Router::add_read_handler(), Router::add_write_handler(),
Router::set_handler(), Element::add_read_handler(),
Element::add_write_handler(), and Element::set_handler(). */
/** @brief Call a read handler, possibly with parameters.
@param e element on which to call the handler
@param param parameters, or an empty string if no parameters
@param raw true iff value is raw text (see raw())
@param errh error handler
The element must be nonnull; to call a global handler, pass the relevant
router's Router::root_element(). @a errh may be null, in which case
errors are reported to ErrorHandler::silent_handler(). */
String
Handler::call_read(Element* e, const String& param, bool raw, ErrorHandler* errh) const
{
if (!errh)
errh = ErrorHandler::silent_handler();
if (param && !(_flags & READ_PARAM))
errh->error("read handler '%s' does not take parameters", unparse_name(e).c_str());
else if ((_flags & (ONE_HOOK | OP_READ)) == OP_READ)
return _hook.rw.r(e, _thunk1);
else if (_flags & OP_READ) {
String s(param);
if ((_flags & RAW) && !raw)
s = cp_unquote(s);
if (_hook.h(OP_READ, s, e, this, errh) >= 0)
return s;
} else
errh->error("'%s' not a read handler", unparse_name(e).c_str());
// error cases get here
return String();
}
/** @brief Call a write handler.
@param s value to write to the handler
@param e element on which to call the handler
@param raw true iff value is raw text (see raw())
@param errh error handler
The element must be nonnull; to call a global handler, pass the relevant
router's Router::root_element(). @a errh may be null, in which case
errors are reported to ErrorHandler::silent_handler(). */
int
Handler::call_write(const String& s, Element* e, bool raw, ErrorHandler* errh) const
{
if (!errh)
errh = ErrorHandler::silent_handler();
String s_copy(s);
if ((_flags & RAW) && !raw)
s_copy = cp_unquote(s_copy);
if ((_flags & (ONE_HOOK | OP_WRITE)) == OP_WRITE)
return _hook.rw.w(s_copy, e, _thunk2, errh);
else if (_flags & OP_WRITE)
return _hook.h(OP_WRITE, s_copy, e, this, errh);
else {
errh->error("'%s' not a write handler", unparse_name(e).c_str());
return -EACCES;
}
}
/** @brief Unparses this handler's name, including */
String
Handler::unparse_name(Element *e, const String &hname)
{
if (e && e != e->router()->root_element())
return e->name() + "." + hname;
else
return hname;
}
/** @brief Unparses this handler's name.
@param e the relevant element
If @a e is an actual element, then returns "ENAME.NAME", where I@a e->name() + "." + */
String
Handler::unparse_name(Element *e) const
{
if (this == the_blank_handler)
return _name;
else
return unparse_name(e, _name);
}
// Private functions for finding and storing handlers
// 11.Jul.2000 - We had problems with handlers for medium-sized configurations
// (~400 elements): the Linux kernel would crash with a "kmalloc too large".
// The solution: Observe that most handlers are shared. For example, all
// 'name' handlers can share a Handler structure, since they share the same
// read function, read thunk, write function (0), write thunk, and name. This
// introduced a bunch of structure to go from elements to handler indices and
// from handler names to handler indices, but it was worth it: it reduced the
// amount of space required by a normal set of Handlers by about a factor of
// 100 -- there used to be 2998 Handlers, now there are 30. (Some of this
// space is still not available for system use -- it gets used up by the
// indexing structures, particularly _ehandlers. Every element has its own
// list of "element handlers", even though most elements with element class C
// could share one such list. The space cost is about (48 bytes * # of
// elements) more or less. Detecting this sharing would be harder to
// implement.)
int
Router::find_ehandler(int eindex, const String& name, bool allow_star) const
{
int eh = _ehandler_first_by_element[eindex];
int star_h = -1;
while (eh >= 0) {
int h = _ehandler_to_handler[eh];
const String &hname = xhandler(h)->name();
if (hname == name)
return eh;
else if (hname.length() == 1 && hname[0] == '*')
star_h = h;
eh = _ehandler_next[eh];
}
if (allow_star && star_h >= 0 && xhandler(star_h)->writable()) {
if (xhandler(star_h)->call_write(name, element(eindex), true, ErrorHandler::default_handler()) >= 0)
eh = find_ehandler(eindex, name, false);
}
return eh;
}
inline Handler
Router::fetch_handler(const Element* e, const String& name)
{
if (const Handler* h = handler(e, name))
return *h;
else
return Handler(name);
}
void
Router::store_local_handler(int eindex, const Handler& to_store)
{
int old_eh = find_ehandler(eindex, to_store.name(), false);
if (old_eh >= 0)
xhandler(_ehandler_to_handler[old_eh])->_use_count--;
// find the offset in _name_handlers
int name_index;
for (name_index = 0;
name_index < _handler_first_by_name.size();
name_index++) {
int h = _handler_first_by_name[name_index];
if (xhandler(h)->_name == to_store._name)
break;
}
if (name_index == _handler_first_by_name.size())
_handler_first_by_name.push_back(-1);
// find a similar handler, if any exists
int* prev_h = &_handler_first_by_name[name_index];
int h = *prev_h;
int* blank_prev_h = 0;
int blank_h = -1;
int stored_h = -1;
while (h >= 0) {
Handler* han = xhandler(h);
if (han->compatible(to_store))
stored_h = h;
else if (han->_use_count == 0)
blank_h = h, blank_prev_h = prev_h;
prev_h = &han->_next_by_name;
h = *prev_h;
}
// if none exists, assign this one to a blank spot
if (stored_h < 0 && blank_h >= 0) {
stored_h = blank_h;
*xhandler(stored_h) = to_store;
xhandler(stored_h)->_use_count = 0;
}
// if no blank spot, add a handler
if (stored_h < 0) {
if (_free_handler < 0) {
int n_handler_bufs = _nhandlers_bufs / HANDLER_BUFSIZ;
Handler** new_handler_bufs = new Handler*[n_handler_bufs + 1];
Handler* new_handler_buf = new Handler[HANDLER_BUFSIZ];
if (!new_handler_buf || !new_handler_bufs) { // out of memory
delete[] new_handler_bufs;
delete[] new_handler_buf;
if (old_eh >= 0) // restore use count
xhandler(_ehandler_to_handler[old_eh])->_use_count++;
return;
}
for (int i = 0; i < HANDLER_BUFSIZ - 1; i++)
new_handler_buf[i]._next_by_name = _nhandlers_bufs + i + 1;
_free_handler = _nhandlers_bufs;
memcpy(new_handler_bufs, _handler_bufs, sizeof(Handler*) * n_handler_bufs);
new_handler_bufs[n_handler_bufs] = new_handler_buf;
delete[] _handler_bufs;
_handler_bufs = new_handler_bufs;
_nhandlers_bufs += HANDLER_BUFSIZ;
}
stored_h = _free_handler;
_free_handler = xhandler(stored_h)->_next_by_name;
*xhandler(stored_h) = to_store;
xhandler(stored_h)->_use_count = 0;
xhandler(stored_h)->_next_by_name = _handler_first_by_name[name_index];
_handler_first_by_name[name_index] = stored_h;
}
// point ehandler list at new handler
if (old_eh >= 0)
_ehandler_to_handler[old_eh] = stored_h;
else {
int new_eh = _ehandler_to_handler.size();
_ehandler_to_handler.push_back(stored_h);
_ehandler_next.push_back(_ehandler_first_by_element[eindex]);
_ehandler_first_by_element[eindex] = new_eh;
}
// increment use count
xhandler(stored_h)->_use_count++;
// perhaps free blank_h
if (blank_h >= 0 && xhandler(blank_h)->_use_count == 0) {
*blank_prev_h = xhandler(blank_h)->_next_by_name;
xhandler(blank_h)->_next_by_name = _free_handler;
_free_handler = blank_h;
}
}
void
Router::store_global_handler(const Handler &h)
{
for (int i = 0; i < nglobalh; i++)
if (globalh[i]._name == h._name) {
globalh[i] = h;
globalh[i]._use_count = 1;
return;
}
if (nglobalh >= globalh_cap) {
int n = (globalh_cap ? 2 * globalh_cap : 4);
Handler *hs = new Handler[n];
if (!hs) // out of memory
return;
for (int i = 0; i < nglobalh; i++)
hs[i] = globalh[i];
delete[] globalh;
globalh = hs;
globalh_cap = n;
}
globalh[nglobalh] = h;
globalh[nglobalh]._use_count = 1;
nglobalh++;
}
inline void
Router::store_handler(const Element* e, const Handler& to_store)
{
if (e)
e->router()->store_local_handler(e->eindex(), to_store);
else
store_global_handler(to_store);
}
// Public functions for finding handlers
const Handler*
Router::handler(const Router* r, int hi)
{
if (r && hi >= 0 && hi < r->_nhandlers_bufs)
return r->xhandler(hi);
else if (hi >= FIRST_GLOBAL_HANDLER && hi < FIRST_GLOBAL_HANDLER + nglobalh)
return &globalh[hi - FIRST_GLOBAL_HANDLER];
else
return 0;
}
const Handler *
Router::handler(const Element* e, const String& hname)
{
if (e && e != e->router()->_root_element) {
const Router *r = e->router();
int eh = r->find_ehandler(e->eindex(), hname, true);
if (eh >= 0)
return r->xhandler(r->_ehandler_to_handler[eh]);
} else { // global handler
for (int i = 0; i < nglobalh; i++)
if (globalh[i]._name == hname)
return &globalh[i];
}
return 0;
}
int
Router::hindex(const Element* e, const String& hname)
{
if (e && e != e->router()->_root_element) {
const Router *r = e->router();
int eh = r->find_ehandler(e->eindex(), hname, true);
if (eh >= 0)
return r->_ehandler_to_handler[eh];
} else { // global handler
for (int i = 0; i < nglobalh; i++)
if (globalh[i]._name == hname)
return FIRST_GLOBAL_HANDLER + i;
}
return -1;
}
void
Router::element_hindexes(const Element* e, Vector<int>& hindexes)
{
if (e && e != e->router()->_root_element) {
const Router *r = e->router();
for (int eh = r->_ehandler_first_by_element[e->eindex()];
eh >= 0;
eh = r->_ehandler_next[eh])
hindexes.push_back(r->_ehandler_to_handler[eh]);
} else {
for (int i = 0; i < nglobalh; i++)
hindexes.push_back(FIRST_GLOBAL_HANDLER + i);
}
}
// Public functions for storing handlers
void
Router::add_read_handler(const Element* e, const String& name, ReadHandlerHook hook, void* thunk)
{
Handler to_add = fetch_handler(e, name);
if (to_add._flags & Handler::ONE_HOOK) {
to_add._hook.rw.w = 0;
to_add._thunk2 = 0;
to_add._flags &= ~Handler::PRIVATE_MASK;
}
to_add._hook.rw.r = hook;
to_add._thunk1 = thunk;
to_add._flags |= Handler::OP_READ;
store_handler(e, to_add);
}
void
Router::add_write_handler(const Element* e, const String& name, WriteHandlerHook hook, void* thunk)
{
Handler to_add = fetch_handler(e, name);
if (to_add._flags & Handler::ONE_HOOK) {
to_add._hook.rw.r = 0;
to_add._thunk1 = 0;
to_add._flags &= ~Handler::PRIVATE_MASK;
}
to_add._hook.rw.w = hook;
to_add._thunk2 = thunk;
to_add._flags |= Handler::OP_WRITE;
store_handler(e, to_add);
}
void
Router::set_handler(const Element* e, const String& name, int flags, HandlerHook hook, void* thunk1, void* thunk2)
{
Handler to_add(name);
to_add._hook.h = hook;
to_add._thunk1 = thunk1;
to_add._thunk2 = thunk2;
to_add._flags = flags | Handler::ONE_HOOK;
store_handler(e, to_add);
}
int
Router::change_handler_flags(const Element* e, const String& name,
uint32_t clear_flags, uint32_t set_flags)
{
Handler to_add = fetch_handler(e, name);
if (to_add._use_count > 0) { // only modify existing handlers
clear_flags &= ~Handler::PRIVATE_MASK;
set_flags &= ~Handler::PRIVATE_MASK;
to_add._flags = (to_add._flags & ~clear_flags) | set_flags;
store_handler(e, to_add);
return 0;
} else
return -1;
}
// ATTACHMENTS
void*
Router::attachment(const String &name) const
{
for (int i = 0; i < _attachments.size(); i++)
if (_attachment_names[i] == name)
return _attachments[i];
return 0;
}
void*&
Router::force_attachment(const String &name)
{
for (int i = 0; i < _attachments.size(); i++)
if (_attachment_names[i] == name)
return _attachments[i];
_attachment_names.push_back(name);
_attachments.push_back(0);
return _attachments.back();
}
void *
Router::set_attachment(const String &name, void *value)
{
for (int i = 0; i < _attachments.size(); i++)
if (_attachment_names[i] == name) {
void *v = _attachments[i];
_attachments[i] = value;
return v;
}
_attachment_names.push_back(name);
_attachments.push_back(value);
return 0;
}
ErrorHandler *
Router::chatter_channel(const String &name) const
{
if (!name || name == "default")
return ErrorHandler::default_handler();
else if (void *v = attachment("ChatterChannel." + name))
return (ErrorHandler *)v;
else
return ErrorHandler::silent_handler();
}
/** @brief Create a new basic signal.
* @param[out] signal the new signal
*
* Creates a new basic NotifierSignal and stores it in @a signal. The signal
* is initially active.
*
* @return >= 0 on success, < 0 on failure
* @sa NotifierSignal
*/
int
Router::new_notifier_signal(NotifierSignal &signal)
{
if (!_notifier_signals)
_notifier_signals = new atomic_uint32_t[NOTIFIER_SIGNALS_CAPACITY / 32];
if (_n_notifier_signals >= NOTIFIER_SIGNALS_CAPACITY)
return -1;
else {
signal = NotifierSignal(&_notifier_signals[_n_notifier_signals / 32], 1 << (_n_notifier_signals % 32));
signal.set_active(true);
_n_notifier_signals++;
return 0;
}
}
int
ThreadSched::initial_home_thread_id(Task *, bool)
{
return 0;
}
NameInfo*
Router::force_name_info()
{
if (!_name_info)
_name_info = new NameInfo;
return _name_info;
}
// PRINTING
void
Router::unparse_requirements(StringAccum &sa, const String &indent) const
{
// requirements
if (_requirements.size())
sa << indent << "require(" << cp_unargvec(_requirements) << ");\n\n";
}
void
Router::unparse_classes(StringAccum &, const String &) const
{
// there are never any compound element classes here
}
void
Router::unparse_declarations(StringAccum &sa, const String &indent) const
{
// element classes
Vector<String> conf;
for (int i = 0; i < nelements(); i++) {
sa << indent << _element_names[i] << " :: " << _elements[i]->class_name();
String conf = (initialized() ? _elements[i]->configuration() : _element_configurations[i]);
if (conf.length())
sa << "(" << conf << ")";
sa << ";\n";
}
if (nelements() > 0)
sa << "\n";
}
void
Router::unparse_connections(StringAccum &sa, const String &indent) const
{
int nhookup = _hookup_from.size();
Vector<int> next(nhookup, -1);
Bitvector startchain(nhookup, true);
for (int c = 0; c < nhookup; c++) {
const Hookup &ht = _hookup_to[c];
if (ht.port != 0) continue;
int result = -1;
for (int d = 0; d < nhookup; d++)
if (d != c && _hookup_from[d] == ht) {
result = d;
if (_hookup_to[d].port == 0)
break;
}
if (result >= 0) {
next[c] = result;
startchain[result] = false;
}
}
// print hookup
Bitvector used(nhookup, false);
bool done = false;
while (!done) {
// print chains
for (int c = 0; c < nhookup; c++) {
if (used[c] || !startchain[c]) continue;
const Hookup &hf = _hookup_from[c];
sa << indent << _element_names[hf.idx];
if (hf.port)
sa << " [" << hf.port << "]";
int d = c;
while (d >= 0 && !used[d]) {
if (d == c) sa << " -> ";
else sa << "\n" << indent << " -> ";
const Hookup &ht = _hookup_to[d];
if (ht.port)
sa << "[" << ht.port << "] ";
sa << _element_names[ht.idx];
used[d] = true;
d = next[d];
}
sa << ";\n";
}
// add new chains to include cycles
done = true;
for (int c = 0; c < nhookup && done; c++)
if (!used[c])
startchain[c] = true, done = false;
}
}
void
Router::unparse(StringAccum &sa, const String &indent) const
{
unparse_requirements(sa, indent);
unparse_classes(sa, indent);
unparse_declarations(sa, indent);
unparse_connections(sa, indent);
}
String
Router::element_ports_string(int ei) const
{
if (ei < 0 || ei >= nelements())
return String();
StringAccum sa;
Element *e = _elements[ei];
Vector<int> pers(e->ninputs() + e->noutputs(), 0);
int *in_pers = pers.begin();
int *out_pers = pers.begin() + e->ninputs();
e->processing_vector(in_pers, out_pers, 0);
sa << e->ninputs() << (e->ninputs() == 1 ? " input\n" : " inputs\n");
for (int i = 0; i < e->ninputs(); i++) {
// processing
const char *persid = (e->input_is_pull(i) ? "pull" : "push");
if (in_pers[i] == Element::VAGNOSTIC)
sa << persid << "~\t";
else
sa << persid << "\t";
// counts
#if CLICK_STATS >= 1
if (e->input_is_pull(i) || CLICK_STATS >= 2)
sa << e->input(i).npackets() << "\t";
else
#endif
sa << "-\t";
// connections
Hookup h(ei, i);
const char *sep = "";
for (int c = 0; c < _hookup_from.size(); c++)
if (_hookup_to[c] == h) {
sa << sep << _element_names[_hookup_from[c].idx]
<< " [" << _hookup_from[c].port << "]";
sep = ", ";
}
sa << "\n";
}
sa << e->noutputs() << (e->noutputs() == 1 ? " output\n" : " outputs\n");
for (int i = 0; i < e->noutputs(); i++) {
// processing
const char *persid = (e->output_is_push(i) ? "push" : "pull");
if (out_pers[i] == Element::VAGNOSTIC)
sa << persid << "~\t";
else
sa << persid << "\t";
// counts
#if CLICK_STATS >= 1
if (e->output_is_push(i) || CLICK_STATS >= 2)
sa << e->output(i).npackets() << "\t";
else
#endif
sa << "-\t";
// hookup
Hookup h(ei, i);
const char *sep = "";
for (int c = 0; c < _hookup_from.size(); c++)
if (_hookup_from[c] == h) {
sa << sep << "[" << _hookup_to[c].port << "] "
<< _element_names[_hookup_to[c].idx];
sep = ", ";
}
sa << "\n";
}
return sa.take_string();
}
// STATIC INITIALIZATION, DEFAULT GLOBAL HANDLERS
enum { GH_VERSION, GH_CONFIG, GH_FLATCONFIG, GH_LIST, GH_REQUIREMENTS };
String
Router::router_read_handler(Element *e, void *thunk)
{
Router *r = (e ? e->router() : 0);
switch (reinterpret_cast<intptr_t>(thunk)) {
case GH_VERSION:
return String(CLICK_VERSION "\n");
case GH_CONFIG:
if (r)
return r->configuration_string();
break;
case GH_FLATCONFIG:
if (r) {
StringAccum sa;
r->unparse(sa);
return sa.take_string();
}
break;
case GH_LIST:
if (r) {
StringAccum sa;
sa << r->nelements() << "\n";
for (int i = 0; i < r->nelements(); i++)
sa << r->_element_names[i] << "\n";
return sa.take_string();
}
break;
case GH_REQUIREMENTS:
if (r) {
StringAccum sa;
for (int i = 0; i < r->_requirements.size(); i++)
sa << r->_requirements[i] << "\n";
return sa.take_string();
}
break;
default:
return "<error>\n";
}
return String();
}
static int
stop_global_handler(const String &s, Element *e, void *, ErrorHandler *errh)
{
if (e) {
int n = 1;
(void) cp_integer(cp_uncomment(s), &n);
e->router()->adjust_runcount(-n);
} else
errh->message("no router to stop");
return 0;
}
void
Router::static_initialize()
{
if (!nglobalh) {
Handler::the_blank_handler = new Handler("<bad handler>");
add_read_handler(0, "version", router_read_handler, (void *)GH_VERSION);
add_read_handler(0, "config", router_read_handler, (void *)GH_CONFIG);
add_read_handler(0, "flatconfig", router_read_handler, (void *)GH_FLATCONFIG);
add_read_handler(0, "list", router_read_handler, (void *)GH_LIST);
add_read_handler(0, "requirements", router_read_handler, (void *)GH_REQUIREMENTS);
add_write_handler(0, "stop", stop_global_handler, 0);
}
}
void
Router::static_cleanup()
{
delete[] globalh;
globalh = 0;
nglobalh = globalh_cap = 0;
delete Handler::the_blank_handler;
}
#ifdef CLICK_NS
int
Router::sim_get_ifid(const char* ifname) {
return simclick_sim_ifid_from_name(_master->siminst(), ifname);
}
Vector<int> *
Router::sim_listenvec(int ifid) {
for (int i = 0; i < _listenvecs.size(); i++)
if (_listenvecs[i]->at(0) == ifid)
return _listenvecs[i];
Vector<int> *new_vec = new Vector<int>(1, ifid);
if (new_vec)
_listenvecs.push_back(new_vec);
return new_vec;
}
int
Router::sim_listen(int ifid, int element) {
if (Vector<int> *vec = sim_listenvec(ifid)) {
for (int i = 1; i < vec->size(); i++)
if ((*vec)[i] == element)
return 0;
vec->push_back(element);
return 0;
} else
return -1;
}
int
Router::sim_write(int ifid,int ptype,const unsigned char* data,int len,
simclick_simpacketinfo* pinfo) {
return simclick_sim_send_to_if(_master->siminst(),_master->clickinst(),ifid,ptype,data,len,pinfo);
}
int
Router::sim_if_ready(int ifid) {
return simclick_sim_if_ready(_master->siminst(),_master->clickinst(),ifid);
}
int
Router::sim_incoming_packet(int ifid, int ptype, const unsigned char* data,
int len, simclick_simpacketinfo* pinfo) {
if (Vector<int> *vec = sim_listenvec(ifid))
for (int i = 1; i < vec->size(); i++)
((FromSimDevice *)element((*vec)[i]))->incoming_packet(ifid, ptype, data,
len, pinfo);
return 0;
}
void
Router::sim_trace(const char* event) {
simclick_sim_trace(_master->siminst(), _master->clickinst(), event);
}
int
Router::sim_get_node_id() {
return simclick_sim_get_node_id(_master->siminst(), _master->clickinst());
}
int
Router::sim_get_next_pkt_id() {
return simclick_sim_get_next_pkt_id(_master->siminst(), _master->clickinst());
}
#endif // CLICK_NS
#if CLICK_USERLEVEL
// Vector template instance
# include <click/vector.cc>
#endif
CLICK_ENDDECLS
syntax highlighted by Code2HTML, v. 0.9.1