/*
* fastudpsource.{cc,hh} -- fast udp source, a benchmark tool
*
* Copyright (c) 1999-2001 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 <clicknet/ip.h>
#include "fastudpsrc.hh"
#include <click/confparse.hh>
#include <click/error.hh>
#include <click/glue.hh>
#include <click/standard/alignmentinfo.hh>
#include <machine/limits.h>
FastUDPSource::FastUDPSource()
: _m(0)
{
_rate_limited = true;
_first = _last = 0;
_count = 0;
}
FastUDPSource::~FastUDPSource()
{
}
int
FastUDPSource::configure(Vector<String> &conf, ErrorHandler *errh)
{
_cksum = true;
_active = true;
_interval = 0;
unsigned sp, dp;
unsigned rate;
int limit;
if (cp_va_parse(conf, this, errh,
cpUnsigned, "send rate", &rate,
cpInteger, "limit", &limit,
cpUnsigned, "packet length", &_len,
cpEthernetAddress, "src eth address", &_ethh.ether_shost,
cpIPAddress, "src ip address", &_sipaddr,
cpUnsigned, "src port", &sp,
cpEthernetAddress, "dst eth address", &_ethh.ether_dhost,
cpIPAddress, "dst ip address", &_dipaddr,
cpUnsigned, "dst port", &dp,
cpOptional,
cpBool, "do UDP checksum?", &_cksum,
cpUnsigned, "interval", &_interval,
cpBool, "active?", &_active,
cpEnd) < 0)
return -1;
if (sp >= 0x10000 || dp >= 0x10000)
return errh->error("source or destination port too large");
if (_len < 60) {
click_chatter("warning: packet length < 60, defaulting to 60");
_len = 60;
}
_ethh.ether_type = htons(0x0800);
_sport = sp;
_dport = dp;
if (rate != 0){
_rate_limited = true;
_rate.set_rate(rate, errh);
} else
_rate_limited = false;
_limit = (limit >= 0 ? limit : NO_LIMIT);
return 0;
}
void
FastUDPSource::incr_ports()
{
click_ip *ip = reinterpret_cast<click_ip *>(_m->m_data+14);
click_udp *udp = reinterpret_cast<click_udp *>(ip + 1);
_incr++;
udp->uh_sport = htons(_sport+_incr);
udp->uh_dport = htons(_dport+_incr);
make_checksum(udp);
}
int
FastUDPSource::initialize(ErrorHandler *)
{
// Create an mbuf with an mbuf cluster so copies are quick
// (we just add a reference to the cluster rather than doing
// a memory copy).
MGETHDR(_m, M_WAIT, MT_DATA);
if (_m == NULL) {
click_chatter("unable to get mbuf for FastUDPSource");
return -1;
}
MCLGET(_m, M_WAIT);
if ((_m->m_flags & M_EXT) == 0) {
m_freem(_m);
click_chatter("unable to get mbuf cluster for FastUDPSource");
return -1;
}
_m->m_len = _m->m_pkthdr.len = _len;
_count = 0;
_incr = 0;
memcpy(_m->m_data, &_ethh, 14);
click_ip *ip = reinterpret_cast<click_ip *>(_m->m_data+14);
click_udp *udp = reinterpret_cast<click_udp *>(ip + 1);
// set up IP header
ip->ip_v = 4;
ip->ip_hl = sizeof(click_ip) >> 2;
ip->ip_len = htons(_len-14);
ip->ip_id = 0;
ip->ip_p = IP_PROTO_UDP;
ip->ip_src = _sipaddr;
ip->ip_dst = _dipaddr;
ip->ip_tos = 0;
ip->ip_off = 0;
ip->ip_ttl = 250;
ip->ip_sum = 0;
ip->ip_sum = click_in_cksum((unsigned char *)ip, sizeof(click_ip));
// set up UDP header
udp->uh_sport = htons(_sport);
udp->uh_dport = htons(_dport);
make_checksum(udp);
return 0;
}
void
FastUDPSource::cleanup(CleanupStage)
{
if (_m) {
m_freem(_m);
_m = 0;
}
}
Packet *
FastUDPSource::pull(int)
{
Packet *p = 0;
struct mbuf *m;
if (!_active || (_limit != NO_LIMIT && _count >= _limit))
return 0;
// Ensure we can safely m_copypacket(_m) without overflowing
// the 8-bit refcount.
if ((_m->m_flags & M_EXT) == 0) {
static char _mcl_lost = 0; // XXX should be in object ?
if (!_mcl_lost) {
click_chatter("mbuf lost cluster!\n");
_mcl_lost = 1;
}
return 0;
}
if (mclrefcnt[mtocl(_m->m_ext.ext_buf)] >= SCHAR_MAX) {
caddr_t mcl, mcl0;
MCLALLOC(mcl, M_WAIT);
if (!mcl) {
click_chatter("failure to allocate new mbuf cluster\n");
return 0;
}
bcopy(_m->m_data, mcl, _m->m_len);
mcl0 = _m->m_ext.ext_buf;
_m->m_data = mcl;
_m->m_ext.ext_buf = mcl;
MCLFREE(mcl0);
}
bool need_packet = false;
if (_rate_limited) {
struct timeval now;
click_gettimeofday(&now);
if (_rate.need_update(now)) {
_rate.update();
need_packet = true;
}
} else
need_packet = true;
if (!need_packet)
return 0; // nothing to pull
m = m_copypacket(_m, M_WAIT);
if (!m) {
click_chatter("unable to m_copypacket\n");
return 0; // bad luck.
}
p = Packet::make(m);
if (!p) {
click_chatter("unable to make packet\n");
return 0; // bad luck.
}
_count++;
if (_count == 1)
_first = click_jiffies();
if (_limit != NO_LIMIT && _count >= _limit)
_last = click_jiffies();
if (_interval>0 && !(_count % _interval))
incr_ports();
return(p);
}
void
FastUDPSource::reset()
{
_count = 0;
_first = 0;
_last = 0;
_incr = 0;
}
static String
FastUDPSource_read_count_handler(Element *e, void *)
{
FastUDPSource *c = (FastUDPSource *)e;
return String(c->count());
}
static String
FastUDPSource_read_rate_handler(Element *e, void *)
{
FastUDPSource *c = (FastUDPSource *)e;
if (c->last() != 0) {
int d = c->last() - c->first();
if (d < 1)
d = 1;
int rate = c->count() * CLICK_HZ / d;
return String(rate);
} else
return String("0");
}
static int
FastUDPSource_reset_write_handler
(const String &, Element *e, void *, ErrorHandler *)
{
FastUDPSource *c = (FastUDPSource *)e;
c->reset();
return 0;
}
static int
FastUDPSource_limit_write_handler
(const String &in_s, Element *e, void *, ErrorHandler *errh)
{
FastUDPSource *c = (FastUDPSource *)e;
String s = cp_uncomment(in_s);
unsigned limit;
if (!cp_unsigned(s, &limit))
return errh->error("limit parameter must be integer >= 0");
c->_limit = (limit >= 0 ? limit : c->NO_LIMIT);
return 0;
}
static int
FastUDPSource_rate_write_handler
(const String &in_s, Element *e, void *, ErrorHandler *errh)
{
FastUDPSource *c = (FastUDPSource *)e;
String s = cp_uncomment(in_s);
unsigned rate;
if (!cp_unsigned(s, &rate))
return errh->error("rate parameter must be integer >= 0");
if (rate > GapRate::MAX_RATE) // report error rather than pin to max
return errh->error("rate too large; max is %u", GapRate::MAX_RATE);
c->_rate.set_rate(rate);
return 0;
}
static int
FastUDPSource_active_write_handler
(const String &in_s, Element *e, void *, ErrorHandler *errh)
{
FastUDPSource *c = (FastUDPSource *)e;
String s = cp_uncomment(in_s);
bool active;
if (!cp_bool(s, &active))
return errh->error("active parameter must be boolean");
c->_active = active;
if (active)
c->reset();
return 0;
}
void
FastUDPSource::add_handlers()
{
add_read_handler("count", FastUDPSource_read_count_handler, 0);
add_read_handler("rate", FastUDPSource_read_rate_handler, 0);
add_write_handler("rate", FastUDPSource_rate_write_handler, 0);
add_write_handler("reset", FastUDPSource_reset_write_handler, 0);
add_write_handler("active", FastUDPSource_active_write_handler, 0);
add_write_handler("limit", FastUDPSource_limit_write_handler, 0);
}
ELEMENT_REQUIRES(bsdmodule)
EXPORT_ELEMENT(FastUDPSource)
syntax highlighted by Code2HTML, v. 0.9.1