📄 todevice_rahulc
字号:
/* * todevice.{cc,hh} -- element writes packets to network via pcap library * Douglas S. J. De Couto, Eddie Kohler, John Jannotti * * Copyright (c) 1999-2000 Massachusetts Institute of Technology * Copyright (c) 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. */#include <click/config.h>#include "todevice.hh"#include <click/error.hh>#include <click/etheraddress.hh>#include <click/confparse.hh>#include <click/router.hh>#include <click/standard/scheduleinfo.hh>#include <click/packet_anno.hh>#include <click/straccum.hh>#include <stdio.h>#include <unistd.h>#if TODEVICE_BSD_DEV_BPF# include <fcntl.h># include <sys/types.h># include <sys/socket.h># include <sys/ioctl.h># include <net/if.h>#elif TODEVICE_LINUX# include <sys/socket.h># include <sys/ioctl.h># include <net/if.h># include <net/if_packet.h># include <features.h># if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1# include <netpacket/packet.h># else# include <linux/if_packet.h># endif#endifCLICK_DECLSToDevice::ToDevice() : Element(1, 0), _task(this), _timer(this), _fd(-1), _my_fd(false), _q(0), _pulls(0), _so_sndbuf(0), _so_sndbuf_set(false){}ToDevice::~ToDevice(){}voidToDevice::notify_noutputs(int n){ set_noutputs(n <= 2 ? n : 0);}intToDevice::configure(Vector<String> &conf, ErrorHandler *errh){ uint32_t tmp_so_sndbuf = 0; if (cp_va_parse(conf, this, errh, cpString, "interface name", &_ifname, cpKeywords, "DEBUG", cpBool, "debug", &_debug, "SO_SNDBUF", cpUnsigned, "so_sndbuf", &tmp_so_sndbuf, cpEnd) < 0) return -1; if (!_ifname) return errh->error("interface not set"); click_chatter("tmp_so_sndbuf is set to %d", tmp_so_sndbuf); if (tmp_so_sndbuf != 0) { _so_sndbuf_set = true; _so_sndbuf = tmp_so_sndbuf; } return 0;}intToDevice::initialize(ErrorHandler *errh){ _timer.initialize(this); _fd = -1;#if TODEVICE_BSD_DEV_BPF /* pcap_open_live() doesn't open for writing. */ for (int i = 0; i < 16 && _fd < 0; i++) { char tmp[64]; sprintf(tmp, "/dev/bpf%d", i); _fd = open(tmp, 1); } if (_fd < 0) return(errh->error("open /dev/bpf* for write: %s", strerror(errno))); struct ifreq ifr; strncpy(ifr.ifr_name, _ifname.c_str(), sizeof(ifr.ifr_name)); ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = 0; if (ioctl(_fd, BIOCSETIF, (caddr_t)&ifr) < 0) return errh->error("BIOCSETIF %s failed", ifr.ifr_name);# ifdef BIOCSHDRCMPLT int yes = 1; if (ioctl(_fd, BIOCSHDRCMPLT, (caddr_t)&yes) < 0) errh->warning("BIOCSHDRCMPLT %s failed", ifr.ifr_name);# endif _my_fd = true;#elif TODEVICE_LINUX || TODEVICE_PCAP // find a FromDevice and reuse its socket if possible for (int ei = 0; ei < router()->nelements() && _fd < 0; ei++) { Element *e = router()->element(ei); FromDevice *fdev = (FromDevice *)e->cast("FromDevice"); if (fdev && fdev->ifname() == _ifname && fdev->fd() >= 0) { _fd = fdev->fd(); _my_fd = false; } } if (_fd < 0) {# if TODEVICE_LINUX _fd = FromDevice::open_packet_socket(_ifname, errh); _my_fd = true;# else return errh->error("ToDevice requires an initialized FromDevice on this platform") ;# endif } if (_fd < 0) return -1; #else return errh->error("ToDevice is not supported on this platform"); #endif click_chatter("%s: fd is %d, my_fd is %d", id().cc(), _fd, _my_fd); // rahul's change if (_so_sndbuf_set) { click_chatter("%s: setting socket sndbuf to %u", id().cc(), _so_sndbuf); if (setsockopt(_fd, SOL_SOCKET, SO_SNDBUF, &_so_sndbuf, sizeof(_so_sndbuf)) < 0) { return errh->error("failure setting SO_SNDBUF for socket %d", _fd); } } // getsockopt just to check int tmp = -1; socklen_t tmplen = sizeof(tmp); getsockopt(_fd, SOL_SOCKET, SO_SNDBUF, &tmp, &tmplen); click_chatter("%s: value read by getsockopt is %d", id().cc(), tmp); // check for duplicate writers void *&used = router()->force_attachment("device_writer_" + _ifname); if (used) return errh->error("duplicate writer for device `%s'", _ifname.cc()); used = this; ScheduleInfo::join_scheduler(this, &_task, errh); _signal = Notifier::upstream_empty_signal(this, 0, &_task); return 0;}voidToDevice::cleanup(CleanupStage){ if (_fd >= 0 && _my_fd) close(_fd); _fd = -1;}/* * Linux select marks datagram fd's as writeable when the socket * buffer has enough space to do a send (sock_writeable() in * sock.h). BSD select always marks datagram fd's as writeable * (bpf_poll() in sys/net/bpf.c) This function should behave * appropriately under both. It makes use of select if it correctly * tells us when buffers are available, and it schedules a backoff * timer if buffers are not available. * --jbicket */voidToDevice::selected(int) { Packet *p; if (_q) { p = _q; _q = 0; } else { p = input(0).pull(); _pulls++; } if (p) { int retval; const char *syscall; #if TODEVICE_WRITE retval = ((uint32_t) write(_fd, p->data(), p->length()) == p->length() ? 0 : -1); syscall = "write";#elif TODEVICE_SEND retval = send(_fd, p->data(), p->length(), 0); syscall = "send";#else retval = 0;#endif if (retval < 0) { if (errno == ENOBUFS || errno == EAGAIN) { assert(!_q); _q = p; /* we should backoff */ remove_select(_fd, SELECT_WRITE); _backoff = (!_backoff) ? 1 : _backoff*2; _timer.schedule_after(Timestamp::make_usec(_backoff)); if (_debug) { Timestamp now = Timestamp::now(); click_chatter("%{element} backing off for %d at %{timestamp}\n", this, _backoff, &now); } return; } else { click_chatter("ToDevice(%s) %s: %s", _ifname.cc(), syscall, strerror(errno)); checked_output_push(1, p); } } else { _backoff = 0; checked_output_push(0, p); } } if (!_q && !p && !_signal) { if (remove_select(_fd, SELECT_WRITE) < 0) { click_chatter("%s %{element} remove_select failed %d\n", Timestamp::now().unparse().cc(), this, _fd); } }}voidToDevice::run_timer(){ if (_debug) { click_chatter("%s %{element}::%s\n", Timestamp::now().unparse().cc(), this, __func__); } if (_q || _signal) { if (add_select(_fd, SELECT_WRITE) < 0) { click_chatter("%s %{element}::%s add_select failed %d\n", Timestamp::now().unparse().cc(), this, __func__, _fd); } selected(_fd); }}boolToDevice::run_task(){ if (_q || _signal) { if (add_select(_fd, SELECT_WRITE) < 0) { click_chatter("%s %{element}::%s add_select failed %d\n", Timestamp::now().unparse().cc(), this, __func__, _fd); } selected(_fd); return true; } return false;}enum {H_DEBUG, H_SIGNAL, H_PULLS, H_Q};StringToDevice::read_param(Element *e, void *thunk){ ToDevice *td = (ToDevice *)e; switch((uintptr_t) thunk) { case H_DEBUG: return String(td->_debug) + "\n"; case H_SIGNAL: return String(td->_signal) + "\n"; case H_PULLS: return String(td->_pulls) + "\n"; case H_Q: return String((bool) td->_q) + "\n"; default: return String(); }}int ToDevice::write_param(const String &in_s, Element *e, void *vparam, ErrorHandler *errh){ ToDevice *td = (ToDevice *)e; String s = cp_uncomment(in_s); switch((int)vparam) { case H_DEBUG: { bool debug; if (!cp_bool(s, &debug)) return errh->error("debug parameter must be boolean"); td->_debug = debug; break; } } return 0;}voidToDevice::add_handlers(){ add_task_handlers(&_task); add_read_handler("debug", read_param, (void *) H_DEBUG); add_read_handler("pulls", read_param, (void *) H_PULLS); add_read_handler("signal", read_param, (void *) H_SIGNAL); add_read_handler("q", read_param, (void *) H_Q); add_write_handler("debug", write_param, (void *) H_DEBUG);}CLICK_ENDDECLSELEMENT_REQUIRES(FromDevice userlevel)EXPORT_ELEMENT(ToDevice)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -