// -*- mode: c++; c-basic-offset: 4 -*-
/*
* anydevice.{cc,hh} -- support Linux device interaction
* Eddie Kohler
*
* Copyright (c) 2001 International Computer Science Institute
* Copyright (c) 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.
*/
#include <click/config.h>
#include <click/glue.hh>
#include "anydevice.hh"
#include <click/confparse.hh>
#include <click/error.hh>
#include <clicknet/wifi.h>
#include <click/cxxprotect.h>
CLICK_CXX_PROTECT
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
#include <linux/if_arp.h>
#endif
#include <linux/smp_lock.h>
CLICK_CXX_UNPROTECT
#include <click/cxxunprotect.h>
AnyDevice::AnyDevice()
: _dev(0), _promisc(false), _in_map(false), _next(0)
{
}
AnyDevice::~AnyDevice()
{
if (_in_map || _dev)
click_chatter("%s: bad device destructor!", name().c_str());
}
int
AnyDevice::find_device(bool allow_nonexistent, AnyDeviceMap *adm,
ErrorHandler *errh)
{
_dev = dev_get_by_name(_devname.c_str());
if (!_dev)
_dev = dev_get_by_ether_address(_devname, this);
if (!_dev) {
if (!allow_nonexistent)
return errh->error("unknown device '%s'", _devname.c_str());
else
errh->warning("unknown device '%s'", _devname.c_str());
}
if (_dev && !(_dev->flags & IFF_UP)) {
errh->warning("device '%s' is down", _devname.c_str());
dev_put(_dev);
_dev = 0;
}
if (_dev && _promisc)
dev_set_promiscuity(_dev, 1);
if (adm)
adm->insert(this);
return 0;
}
void
AnyDevice::set_device(net_device *dev, AnyDeviceMap *adm)
{
if (_dev == dev) // changing to the same device is a noop
return;
if (_dev)
click_chatter("%s: device '%s' went down", declaration().c_str(), _devname.c_str());
if (dev)
click_chatter("%s: device '%s' came up", declaration().c_str(), _devname.c_str());
if (_dev && _promisc)
dev_set_promiscuity(_dev, -1);
if (adm && _in_map)
adm->remove(this);
if (_dev)
dev_put(_dev);
_dev = dev;
if (_dev)
dev_hold(_dev);
if (adm)
adm->insert(this);
if (_dev && _promisc)
dev_set_promiscuity(_dev, 1);
}
void
AnyDevice::clear_device(AnyDeviceMap *adm)
{
if (_dev && _promisc)
dev_set_promiscuity(_dev, -1);
if (adm)
adm->remove(this);
if (_dev)
dev_put(_dev);
_dev = 0;
}
AnyTaskDevice::AnyTaskDevice()
: _task(this), _idles(0)
{
}
void
AnyDeviceMap::initialize()
{
_unknown_map = 0;
for (int i = 0; i < MAP_SIZE; i++)
_map[i] = 0;
}
void
AnyDeviceMap::insert(AnyDevice *d)
{
// lock whole kernel when manipulating device map
lock_kernel();
// put new devices last on list
int ifi = d->ifindex();
AnyDevice **pprev = (ifi >= 0 ? &_map[ifi % MAP_SIZE] : &_unknown_map);
AnyDevice *trav = *pprev;
while (trav) {
pprev = &trav->_next;
trav = *pprev;
}
d->_next = 0;
*pprev = d;
d->_in_map = true;
unlock_kernel();
}
void
AnyDeviceMap::move_to_front(AnyDevice *d)
{
// lock whole kernel when manipulating device map
lock_kernel();
// put new devices last on list
int ifi = d->ifindex();
AnyDevice **pprev = (ifi >= 0 ? &_map[ifi % MAP_SIZE] : &_unknown_map);
AnyDevice **head = pprev;
AnyDevice *trav = *pprev;
while (trav && trav != d) {
pprev = &trav->_next;
trav = *pprev;
}
if (trav)
*pprev = d->_next;
d->_next = *head;
*head = d;
d->_in_map = true;
unlock_kernel();
}
void
AnyDeviceMap::remove(AnyDevice *d)
{
lock_kernel();
int ifi = d->ifindex();
AnyDevice **pprev = (ifi >= 0 ? &_map[ifi % MAP_SIZE] : &_unknown_map);
AnyDevice *trav = *pprev;
while (trav && trav != d) {
pprev = &trav->_next;
trav = *pprev;
}
if (trav)
*pprev = d->_next;
d->_in_map = false;
unlock_kernel();
}
AnyDevice *
AnyDeviceMap::lookup_unknown(net_device *dev, AnyDevice *last)
{
// make sure device is valid
if (!dev)
return 0;
// look by device name and Ethernet address
String dev_name = dev->name;
unsigned char en[6];
for (AnyDevice *d = (last ? last->_next : _unknown_map); d; d = d->_next)
if (d->devname() == dev_name)
return d;
else if ((dev->type == ARPHRD_ETHER || dev->type == ARPHRD_80211)
&& cp_ethernet_address(d->devname(), en, d)
&& memcmp(en, dev->dev_addr, 6) == 0)
return d;
return 0;
}
void
AnyDeviceMap::lookup_all(net_device *dev, bool known, Vector<AnyDevice *> &v)
{
if (known)
for (AnyDevice *d = 0; d = lookup(dev, d); v.push_back(d))
/* nada */;
else
for (AnyDevice *d = 0; d = lookup_unknown(dev, d); v.push_back(d))
/* nada */;
}
net_device *
dev_get_by_ether_address(const String &name, Element *context)
{
unsigned char en[6];
if (!cp_ethernet_address(name, en, context))
return 0;
for (net_device *dev = dev_base; dev; dev = dev->next)
if ((dev->type == ARPHRD_ETHER || dev->type == ARPHRD_80211) && memcmp(en, dev->dev_addr, 6) == 0) {
dev_hold(dev); // dev_get_by_name does dev_hold; so
// should we
return dev;
}
return 0;
}
ELEMENT_REQUIRES(linuxmodule)
ELEMENT_PROVIDES(AnyDevice)
syntax highlighted by Code2HTML, v. 0.9.1