/*
* click-align.cc -- alignment enforcer for Click configurations
* Eddie Kohler
*
* Copyright (c) 1999-2000 Massachusetts Institute of Technology
*
* 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.
*/
#include <click/config.h>
#include <click/error.hh>
#include <click/confparse.hh>
#include <click/straccum.hh>
#include <click/driver.hh>
#include "lexert.hh"
#include "routert.hh"
#include "alignment.hh"
#include "alignclass.hh"
#include "toolutils.hh"
#include <click/clp.h>
#include <stdio.h>
#include <ctype.h>
struct RouterAlign {
RouterT *_router;
Vector<int> _icount;
Vector<int> _ocount;
Vector<int> _ioffset;
Vector<int> _ooffset;
Vector<Alignment> _ialign;
Vector<Alignment> _oalign;
Vector<Aligner *> _aligners;
RouterAlign(RouterT *, ErrorHandler *);
int iindex_eindex(int) const;
int iindex_port(int) const;
int oindex_eindex(int) const;
int oindex_port(int) const;
bool have_input();
void have_output();
void want_input();
bool want_output();
void adjust();
void print(FILE *);
};
RouterAlign::RouterAlign(RouterT *r, ErrorHandler *errh)
{
_router = r;
int ne = r->nelements();
int id = 0, od = 0;
for (int i = 0; i < ne; i++) {
_ioffset.push_back(id);
_ooffset.push_back(od);
ElementT *e = r->element(i);
_icount.push_back(e->ninputs());
_ocount.push_back(e->noutputs());
id += e->ninputs();
od += e->noutputs();
}
_ioffset.push_back(id);
_ooffset.push_back(od);
// set alignments
_ialign.assign(id, Alignment());
_oalign.assign(od, Alignment());
// find aligners
_aligners.assign(_router->nelements(), default_aligner());
for (RouterT::iterator x = _router->begin_elements(); x; x++) {
AlignClass *eclass = (AlignClass *)x->type()->cast("AlignClass");
if (eclass)
_aligners[x->eindex()] = eclass->create_aligner(x, _router, errh);
}
}
int
RouterAlign::iindex_eindex(int ii) const
{
int ne = _icount.size();
for (int i = 0; i < ne; i++)
if (ii < _ioffset[i+1])
return i;
return -1;
}
int
RouterAlign::iindex_port(int ii) const
{
int ne = _icount.size();
for (int i = 0; i < ne; i++)
if (ii < _ioffset[i+1])
return ii - _ioffset[i];
return -1;
}
int
RouterAlign::oindex_eindex(int oi) const
{
int ne = _icount.size();
for (int i = 0; i < ne; i++)
if (oi < _ooffset[i+1])
return i;
return -1;
}
int
RouterAlign::oindex_port(int oi) const
{
int ne = _icount.size();
for (int i = 0; i < ne; i++)
if (oi < _ooffset[i+1])
return oi - _ooffset[i];
return -1;
}
void
RouterAlign::have_output()
{
int ne = _icount.size();
for (int i = 0; i < ne; i++)
_aligners[i]->have_flow(_ialign, _ioffset[i], _icount[i],
_oalign, _ooffset[i], _ocount[i]);
}
bool
RouterAlign::have_input()
{
const Vector<ConnectionT> &conn = _router->connections();
int nh = conn.size();
int nialign = _ialign.size();
Vector<Alignment> new_ialign(nialign, Alignment());
for (int i = 0; i < nh; i++)
if (conn[i].live()) {
int ioff = _ioffset[conn[i].to_eindex()] + conn[i].to_port();
int ooff = _ooffset[conn[i].from_eindex()] + conn[i].from_port();
new_ialign[ioff] |= _oalign[ooff];
}
// see if anything happened
bool changed = false;
for (int i = 0; i < nialign && !changed; i++)
if (new_ialign[i] != _ialign[i])
changed = true;
_ialign.swap(new_ialign);
return changed;
}
void
RouterAlign::want_input()
{
int ne = _icount.size();
for (int i = 0; i < ne; i++)
_aligners[i]->want_flow(_ialign, _ioffset[i], _icount[i],
_oalign, _ooffset[i], _ocount[i]);
}
bool
RouterAlign::want_output()
{
const Vector<ConnectionT> &conn = _router->connections();
int nh = conn.size();
int noalign = _oalign.size();
Vector<Alignment> new_oalign(noalign, Alignment());
for (int i = 0; i < nh; i++)
if (conn[i].live()) {
int ioff = _ioffset[conn[i].to_eindex()] + conn[i].to_port();
int ooff = _ooffset[conn[i].from_eindex()] + conn[i].from_port();
new_oalign[ooff] &= _ialign[ioff];
}
/* for (int i = 0; i < noalign; i++)
if (new_oalign[i].bad())
new_oalign[i] = Alignment(); */
// see if anything happened
bool changed = false;
for (int i = 0; i < noalign && !changed; i++)
if (new_oalign[i] != _oalign[i]) {
/* fprintf(stderr, "%s[%d] %s <- %s\n", _router->ename(oindex_eindex(i)).c_str(), oindex_port(i), new_oalign[i].s().c_str(), _oalign[i].s().c_str()); */
changed = true;
}
_oalign.swap(new_oalign);
return changed;
}
void
RouterAlign::adjust()
{
int ne = _icount.size();
for (int i = 0; i < ne; i++)
_aligners[i]->adjust_flow(_ialign, _ioffset[i], _icount[i],
_oalign, _ooffset[i], _ocount[i]);
}
void
RouterAlign::print(FILE *f)
{
for (RouterT::iterator x = _router->begin_elements(); x; x++) {
int i = x->eindex();
fprintf(f, "%s :", x->name_c_str());
for (int j = 0; j < _icount[i]; j++) {
const Alignment &a = _ialign[ _ioffset[i] + j ];
fprintf(f, " %d/%d", a.chunk(), a.offset());
}
fprintf(f, " -");
for (int j = 0; j < _ocount[i]; j++) {
const Alignment &a = _oalign[ _ooffset[i] + j ];
fprintf(f, " %d/%d", a.chunk(), a.offset());
}
fprintf(f, "\n");
}
fprintf(f, "\n");
}
void
prepare_classes()
{
// specialized classes
ElementClassT::set_base_type(new AlignAlignClass);
ElementClassT::set_base_type(new StripAlignClass);
ElementClassT::set_base_type(new CheckIPHeaderAlignClass("CheckIPHeader", 1));
ElementClassT::set_base_type(new CheckIPHeaderAlignClass("CheckIPHeader2", 1));
ElementClassT::set_base_type(new CheckIPHeaderAlignClass("MarkIPHeader", 0));
ElementClassT::set_base_type(new AlignClass("Classifier", new ClassifierAligner));
ElementClassT::set_base_type(new AlignClass("EtherEncap", new ShifterAligner(-14)));
Aligner *a2 = new GeneratorAligner(Alignment(4, 2));
ElementClassT::set_base_type(new AlignClass("SR2SetChecksum", a2));
ElementClassT::set_base_type(new AlignClass("SR2CheckHeader", a2));
ElementClassT::set_base_type(new AlignClass("SetSRChecksum", a2));
ElementClassT::set_base_type(new AlignClass("CheckSRHeader", a2));
ElementClassT::set_base_type(new AlignClass("IPInputCombo",
new CombinedAligner(new ShifterAligner(14),
new WantAligner(Alignment(4, 2)))));
ElementClassT::set_base_type(new AlignClass("GridEncap",
new CombinedAligner(new ShifterAligner(98),
new WantAligner(Alignment(4, 0)))));
// no alignment requirements, and do not transmit packets between input and
// output
Aligner *a = new NullAligner;
ElementClassT::set_base_type(new AlignClass("Idle", a));
ElementClassT::set_base_type(new AlignClass("Discard", a));
// generate alignment 4/2
a = new GeneratorAligner(Alignment(4, 2));
ElementClassT::set_base_type(new AlignClass("FromDevice", a));
ElementClassT::set_base_type(new AlignClass("PollDevice", a));
ElementClassT::set_base_type(new AlignClass("FromLinux", a));
// generate alignment 4/0
a = new GeneratorAligner(Alignment(4, 0));
ElementClassT::set_base_type(new AlignClass("RatedSource", a));
ElementClassT::set_base_type(new AlignClass("ICMPError", a));
// want alignment 4/2
a = new WantAligner(Alignment(4, 2));
ElementClassT::set_base_type(new AlignClass("ToLinux", a));
// want alignment 4/0
a = new WantAligner(Alignment(4, 0));
ElementClassT::set_base_type(new AlignClass("IPEncap", a));
ElementClassT::set_base_type(new AlignClass("UDPIPEncap", a));
ElementClassT::set_base_type(new AlignClass("ICMPPingEncap", a));
ElementClassT::set_base_type(new AlignClass("RandomUDPIPEncap", a));
ElementClassT::set_base_type(new AlignClass("RoundRobinUDPIPEncap", a));
ElementClassT::set_base_type(new AlignClass("RoundRobinTCPIPEncap", a));
// want alignment 2/0
a = new WantAligner(Alignment(2, 0));
ElementClassT::set_base_type(new AlignClass("ARPResponder", a));
ElementClassT::set_base_type(new AlignClass("ARPQuerier", a));
}
#define HELP_OPT 300
#define VERSION_OPT 301
#define ROUTER_OPT 303
#define EXPRESSION_OPT 304
#define OUTPUT_OPT 305
static Clp_Option options[] = {
{ "expression", 'e', EXPRESSION_OPT, Clp_ArgString, 0 },
{ "file", 'f', ROUTER_OPT, Clp_ArgString, 0 },
{ "help", 0, HELP_OPT, 0, 0 },
{ "output", 'o', OUTPUT_OPT, Clp_ArgString, 0 },
{ "version", 'v', VERSION_OPT, 0, 0 },
};
static const char *program_name;
void
short_usage()
{
fprintf(stderr, "Usage: %s [OPTION]... [ROUTERFILE]\n\
Try '%s --help' for more information.\n",
program_name, program_name);
}
void
usage()
{
printf("\
'Click-align' adds any required 'Align' elements to a Click router\n\
configuration. The resulting router will work on machines that don't allow\n\
unaligned accesses. Its configuration is written to the standard output.\n\
\n\
Usage: %s [OPTION]... [ROUTERFILE]\n\
\n\
Options:\n\
-f, --file FILE Read router configuration from FILE.\n\
-e, --expression EXPR Use EXPR as router configuration.\n\
-o, --output FILE Write output to FILE.\n\
--help Print this message and exit.\n\
-v, --version Print version number and exit.\n\
\n\
Report bugs to <click@pdos.lcs.mit.edu>.\n", program_name);
}
inline String
aligner_name(int anonymizer)
{
return String("Align@click_align@") + String(anonymizer);
}
int
main(int argc, char **argv)
{
click_static_initialize();
CLICK_DEFAULT_PROVIDES;
ErrorHandler *errh = ErrorHandler::default_handler();
// read command line arguments
Clp_Parser *clp =
Clp_NewParser(argc, argv, sizeof(options) / sizeof(options[0]), options);
Clp_SetOptionChar(clp, '+', Clp_ShortNegated);
program_name = Clp_ProgramName(clp);
const char *router_file = 0;
bool file_is_expr = false;
const char *output_file = 0;
while (1) {
int opt = Clp_Next(clp);
switch (opt) {
case HELP_OPT:
usage();
exit(0);
break;
case VERSION_OPT:
printf("click-align (Click) %s\n", CLICK_VERSION);
printf("Copyright (C) 1999 Massachusetts Institute of Technology\n\
This is free software; see the source for copying conditions.\n\
There is NO warranty, not even for merchantability or fitness for a\n\
particular purpose.\n");
exit(0);
break;
case ROUTER_OPT:
case EXPRESSION_OPT:
case Clp_NotOption:
if (router_file) {
errh->error("router configuration specified twice");
goto bad_option;
}
router_file = clp->arg;
file_is_expr = (opt == EXPRESSION_OPT);
break;
case OUTPUT_OPT:
if (output_file) {
errh->error("output file specified twice");
goto bad_option;
}
output_file = clp->arg;
break;
bad_option:
case Clp_BadOption:
short_usage();
exit(1);
break;
case Clp_Done:
goto done;
}
}
done:
prepare_classes();
RouterT *router = read_router(router_file, file_is_expr, errh);
if (router)
router->flatten(errh);
if (!router || errh->nerrors() > 0)
exit(1);
ElementClassT *align_class = ElementClassT::base_type("Align");
assert(align_class);
int original_nelements = router->nelements();
int anonymizer = original_nelements + 1;
while (router->eindex(aligner_name(anonymizer)) >= 0)
anonymizer++;
/*
* Add Align elements for required alignments
*/
int num_aligns_added = 0;
{
// calculate current alignment
RouterAlign ral(router, errh);
do {
ral.have_output();
} while (ral.have_input());
// calculate desired alignment
RouterAlign want_ral(router, errh);
do {
want_ral.want_input();
} while (want_ral.want_output());
// add required aligns
for (int i = 0; i < original_nelements; i++)
for (int j = 0; j < ral._icount[i]; j++) {
Alignment have = ral._ialign[ ral._ioffset[i] + j ];
Alignment want = want_ral._ialign[ ral._ioffset[i] + j ];
if (have <= want || want.bad())
/* do nothing */;
else {
ElementT *e = router->get_element
(aligner_name(anonymizer), align_class,
String(want.chunk()) + ", " + String(want.offset()),
"<click-align>");
router->insert_before(e, PortT(router->element(i), j));
anonymizer++;
num_aligns_added++;
}
}
}
/*
* remove duplicate Aligns (Align_1 -> Align_2)
*/
{
const Vector<ConnectionT> &conn = router->connections();
int nhook = conn.size();
for (int i = 0; i < nhook; i++)
if (conn[i].live() && conn[i].to_element()->type() == align_class
&& conn[i].from_element()->type() == align_class) {
// skip over hf[i]
Vector<PortT> above, below;
router->find_connections_to(conn[i].from(), above);
router->find_connections_from(conn[i].from(), below);
if (below.size() == 1) {
for (int j = 0; j < nhook; j++)
if (conn[j].to() == conn[i].from())
router->change_connection_to(j, conn[i].to());
} else if (above.size() == 1) {
for (int j = 0; j < nhook; j++)
if (conn[j].to() == conn[i].to())
router->change_connection_from(j, above[0]);
}
}
}
/*
* Add Aligns required for adjustment alignments
*
* Classifier is an example: it can cope with any alignment that is
* consistent and has a chunk >= 4. Rather than require a specific alignment
* above, we handle Classifier here; required alignments may have generated
* an alignment we can deal with.
*/
{
// calculate current alignment
RouterAlign ral(router, errh);
do {
ral.have_output();
} while (ral.have_input());
// calculate adjusted alignment
RouterAlign want_ral(ral);
want_ral.adjust();
// add required aligns
for (int i = 0; i < original_nelements; i++)
for (int j = 0; j < ral._icount[i]; j++) {
Alignment have = ral._ialign[ ral._ioffset[i] + j ];
Alignment want = want_ral._ialign[ ral._ioffset[i] + j ];
if (have <= want || want.bad())
/* do nothing */;
else {
ElementT *e = router->get_element
(aligner_name(anonymizer), align_class,
String(want.chunk()) + ", " + String(want.offset()),
"<click-align>");
router->insert_before(e, PortT(router->element(i), j));
anonymizer++;
num_aligns_added++;
}
}
}
while (1) {
// calculate current alignment
RouterAlign ral(router, errh);
do {
ral.have_output();
} while (ral.have_input());
bool changed = false;
// skip redundant Aligns
const Vector<ConnectionT> &conn = router->connections();
int nhook = conn.size();
for (int i = 0; i < nhook; i++)
if (conn[i].live() && conn[i].to_element()->type() == align_class) {
Alignment have = ral._oalign[ ral._ooffset[conn[i].from_eindex()] + conn[i].from_port() ];
Alignment want = ral._oalign[ ral._ooffset[conn[i].to_eindex()] ];
if (have <= want) {
changed = true;
Vector<PortT> align_dest;
router->find_connections_from(conn[i].to(), align_dest);
for (int j = 0; j < align_dest.size(); j++)
router->add_connection(conn[i].from(), align_dest[j]);
router->kill_connection(i);
}
}
if (!changed) break;
router->remove_duplicate_connections();
}
// remove unused Aligns (they have no input) and old AlignmentInfos
ElementClassT *aligninfo_class = ElementClassT::base_type("AlignmentInfo");
{
for (RouterT::iterator x = router->begin_elements(); x; x++)
if (x->type() == align_class && (x->ninputs() == 0 || x->noutputs() == 0)) {
x->kill();
num_aligns_added--;
} else if (x->type() == aligninfo_class)
x->kill();
router->remove_dead_elements();
}
// make the AlignmentInfo element
{
RouterAlign ral(router, errh);
do {
ral.have_output();
} while (ral.have_input());
// collect alignment information, delete old AlignmentInfos
StringAccum sa;
for (RouterT::iterator x = router->begin_elements(); x; x++) {
if (sa.length())
sa << ",\n ";
sa << x->name();
int i = x->eindex();
for (int j = 0; j < ral._icount[i]; j++) {
const Alignment &a = ral._ialign[ ral._ioffset[i] + j ];
sa << " " << a.chunk() << " " << a.offset();
}
}
router->get_element(String("AlignmentInfo@click_align@")
+ String(router->nelements() + 1),
aligninfo_class, sa.take_string(),
"<click-align>");
}
// warn if added aligns
if (num_aligns_added > 0)
errh->warning((num_aligns_added > 1 ? "added %d Align elements" : "added %d Align element"), num_aligns_added);
// write result
if (write_router_file(router, output_file, errh) < 0)
exit(1);
return 0;
}
// generate Vector template instance
#include <click/vector.cc>
template class Vector<Alignment>;
syntax highlighted by Code2HTML, v. 0.9.1