Skip to content

Commit

Permalink
Linuxmodule FromDevice: Work in unmodified kernels (--enable-fixinclu…
Browse files Browse the repository at this point in the history
…des).

This code makes use of existing hooks for Ethernet bridging, and will only
work if your kernel was compiled with CONFIG_BRIDGE or
CONFIG_BRIDGE_MODULE.  It replaces the bridge hook with Click code.  When
using Click, it's probably safest to use CONFIG_BRIDGE_MODULE and then not
actually load the module.

Required some refactoring, which simplified the code.

ToDevice also compiles in unmodified kernels, but haven't tested yet if it
runs.
  • Loading branch information
kohler committed Jan 28, 2010
1 parent d4ad754 commit ae76f68
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 125 deletions.
102 changes: 56 additions & 46 deletions elements/linuxmodule/anydevice.cc
Original file line number Diff line number Diff line change
Expand Up @@ -108,106 +108,116 @@ AnyDevice::alter_promiscuity(int delta)
#endif
}

int
AnyDevice::find_device(AnyDeviceMap *adm, ErrorHandler *errh)
void
AnyDevice::alter_from_device(int delta)
{
_dev = get_by_name(_devname.c_str());
_devname_exists = (bool) _dev;
if (!_dev)
_dev = get_by_ether_address(_devname, this);

if (!_dev && !_allow_nonexistent)
return errh->error("unknown device '%s'", _devname.c_str());
else if (!_dev && !_quiet)
errh->warning("unknown device '%s'", _devname.c_str());
else if (_dev && !(_dev->flags & IFF_UP)) {
if (!_quiet)
errh->warning("device '%s' is down", _devname.c_str());
dev_put(_dev);
_dev = 0;
#if !HAVE_CLICK_KERNEL && (defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
fake_bridge *fb = reinterpret_cast<fake_bridge *>(_dev->br_port);
if (fb && fb->magic != fake_bridge::click_magic) {
printk("<1>%s: appears to be owned by the bridge module!", _devname.c_str());
return;
}

if (_dev && _promisc)
alter_promiscuity(1);
#if HAVE_NET_ENABLE_TIMESTAMP
if (_dev && _timestamp)
net_enable_timestamp();
if (delta < 0 && fb && atomic_dec_and_test(&fb->refcount)) {
delete fb;
rcu_assign_pointer(_dev->br_port, NULL);
} else if (delta > 0 && fb)
atomic_inc(&fb->refcount);
else if (delta > 0) {
fb = new fake_bridge;
fb->magic = fake_bridge::click_magic;
atomic_set(&fb->refcount, 1);
rcu_assign_pointer(_dev->br_port, reinterpret_cast<struct net_bridge_port *>(fb));
}
#else
(void) delta;
#endif
_carrier_ok = (_dev && netif_carrier_ok(_dev));
if (adm)
adm->insert(this, false);
}

return 0;
net_device *
AnyDevice::lookup_device(ErrorHandler *errh)
{
net_device *dev = get_by_name(_devname.c_str());
_devname_exists = (bool) dev;
if (!dev)
dev = get_by_ether_address(_devname, this);

if (!dev && !_allow_nonexistent)
errh->error("unknown device %<%s%>", _devname.c_str());
else if (!dev && !_quiet)
errh->warning("unknown device %<%s%>", _devname.c_str());
else if (dev && !(dev->flags & IFF_UP)) {
if (!_quiet)
errh->warning("device %<%s%> is down", _devname.c_str());
dev_put(dev);
dev = 0;
}
return dev;
}

void
AnyDevice::set_device(net_device *dev, AnyDeviceMap *adm, bool locked)
AnyDevice::set_device(net_device *dev, AnyDeviceMap *adm, int flags)
{
if (_dev == dev) { // no device change == carrier sense only
bool carrier_ok = (_dev && netif_carrier_ok(_dev));
if (carrier_ok != _carrier_ok) {
_carrier_ok = carrier_ok;
if (_down_call && !_carrier_ok)
if (_down_call && !_carrier_ok && (flags & anydev_change))
_down_call->call_write(ErrorHandler::default_handler());
if (_up_call && _carrier_ok)
if (_up_call && _carrier_ok && (flags & anydev_change))
_up_call->call_write(ErrorHandler::default_handler());
}
return;
}

// call going-down notifiers
if (_dev) {
if (_down_call && _carrier_ok)
if (_down_call && _carrier_ok && (flags & anydev_change))
_down_call->call_write(ErrorHandler::default_handler());
if (!_down_call && !_quiet)
click_chatter("%s: device '%s' went down", declaration().c_str(), _devname.c_str());
click_chatter("%s: device %<%s%> went down", declaration().c_str(), _devname.c_str());
}

if (_dev && _promisc)
alter_promiscuity(-1);
#if HAVE_NET_ENABLE_TIMESTAMP
if (_dev && _timestamp)
net_disable_timestamp();
#endif
clear_device(adm, flags);

if (adm && _in_map)
adm->remove(this, locked);
if (_dev)
dev_put(_dev);
_dev = dev;
if (_dev)
if (_dev && (flags & anydev_change))
dev_hold(_dev);
if (adm)
adm->insert(this, locked);
adm->insert(this, flags & anydev_change);

if (_dev && _promisc)
alter_promiscuity(1);
#if HAVE_NET_ENABLE_TIMESTAMP
if (_dev && _timestamp)
net_enable_timestamp();
#endif
if (_dev && (flags & anydev_from_device))
alter_from_device(1);
_carrier_ok = (_dev && netif_carrier_ok(_dev));

// call going-up notifiers
if (_dev) {
if (_dev && (flags & anydev_change)) {
if (_up_call && _carrier_ok)
_up_call->call_write(ErrorHandler::default_handler());
if (!_up_call && !_quiet)
click_chatter("%s: device '%s' came up", declaration().c_str(), _devname.c_str());
click_chatter("%s: device %<%s%> came up", declaration().c_str(), _devname.c_str());
}
}

void
AnyDevice::clear_device(AnyDeviceMap *adm)
AnyDevice::clear_device(AnyDeviceMap *adm, int flags)
{
if (_dev && _promisc)
alter_promiscuity(-1);
#if HAVE_NET_ENABLE_TIMESTAMP
if (_dev && _timestamp)
net_disable_timestamp();
#endif
if (_dev && (flags & anydev_from_device))
alter_from_device(-1);
if (adm && _in_map)
adm->remove(this, false);
adm->remove(this, flags & anydev_change);
if (_dev)
dev_put(_dev);
_dev = 0;
Expand Down
31 changes: 23 additions & 8 deletions elements/linuxmodule/anydevice.hh
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,21 @@ class AnyDevice : public Element { public:
net_device *device() const { return _dev; }
int ifindex() const { return _dev ? _dev->ifindex : -1; }

bool allow_nonexistent() const { return _allow_nonexistent; }
bool promisc() const { return _promisc; }
bool timestamp() const { return _timestamp; }

int configure_keywords(Vector<String> &conf, ErrorHandler *errh,
bool is_reader);
int initialize_keywords(ErrorHandler *errh);

int find_device(AnyDeviceMap *, ErrorHandler *);
void set_device(net_device *, AnyDeviceMap *, bool locked = false);
void clear_device(AnyDeviceMap *);
net_device *lookup_device(ErrorHandler *errh);
enum {
anydev_change = 1,
anydev_from_device = 2
};
void set_device(net_device *dev, AnyDeviceMap *map, int flags);
void clear_device(AnyDeviceMap *map, int flags);

static inline net_device *get_by_name(const char *name) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
Expand All @@ -105,6 +110,15 @@ class AnyDevice : public Element { public:

static net_device *get_by_ether_address(const String &name, Element *context);

// used for FromDevice
struct fake_bridge {
int magic;
atomic_t refcount;
enum {
click_magic = 0x817A10A7
};
};

protected:

String _devname;
Expand All @@ -124,6 +138,7 @@ class AnyDevice : public Element { public:
HandlerCall *_down_call;

void alter_promiscuity(int delta);
void alter_from_device(int delta);

friend class AnyDeviceMap;

Expand Down Expand Up @@ -178,11 +193,11 @@ class AnyDeviceMap { public:
void initialize();
inline void lock(bool write, unsigned long &flags);
inline void unlock(bool write, unsigned long flags);
inline AnyDevice *lookup(net_device *, AnyDevice *) const;
AnyDevice *lookup_unknown(net_device *, AnyDevice *) const;
int lookup_all(net_device *, bool known, AnyDevice **dev_store, int ndev) const;
void insert(AnyDevice *, bool locked);
void remove(AnyDevice *, bool locked);
inline AnyDevice *lookup(net_device *dev, AnyDevice *last) const;
AnyDevice *lookup_unknown(net_device *dev, AnyDevice *last) const;
int lookup_all(net_device *dev, bool known, AnyDevice **develt_store, int ndev) const;
void insert(AnyDevice *develt, bool locked);
void remove(AnyDevice *develt, bool locked);

private:

Expand Down
73 changes: 60 additions & 13 deletions elements/linuxmodule/fromdevice.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,30 @@

static AnyDeviceMap from_device_map;
static int registered_readers;
#ifdef HAVE_CLICK_KERNEL
#if HAVE_CLICK_KERNEL
static struct notifier_block packet_notifier;
#endif
static struct notifier_block device_notifier;

#if !HAVE_CLICK_KERNEL && (defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE))
# define CLICK_FROMDEVICE_USE_BRIDGE 1
#endif

#if CLICK_FROMDEVICE_USE_BRIDGE
# include <click/cxxprotect.h>
CLICK_CXX_PROTECT
# include <linux/if_bridge.h>
CLICK_CXX_UNPROTECT
# include <click/cxxunprotect.h>
#endif


extern "C" {
#ifdef HAVE_CLICK_KERNEL
#if HAVE_CLICK_KERNEL
static int packet_notifier_hook(struct notifier_block *nb, unsigned long val, void *v);
#elif CLICK_FROMDEVICE_USE_BRIDGE
static struct sk_buff *click_br_handle_frame_hook(struct net_bridge_port *p, struct sk_buff *skb);
static struct sk_buff *(*real_br_handle_frame_hook)(struct net_bridge_port *p, struct sk_buff *skb);
#endif
static int device_notifier_hook(struct notifier_block *nb, unsigned long val, void *v);
}
Expand All @@ -49,7 +65,7 @@ void
FromDevice::static_initialize()
{
from_device_map.initialize();
#ifdef HAVE_CLICK_KERNEL
#if HAVE_CLICK_KERNEL
packet_notifier.notifier_call = packet_notifier_hook;
packet_notifier.priority = 1;
packet_notifier.next = 0;
Expand All @@ -63,9 +79,12 @@ FromDevice::static_initialize()
void
FromDevice::static_cleanup()
{
#ifdef HAVE_CLICK_KERNEL
#if HAVE_CLICK_KERNEL
if (registered_readers)
unregister_net_in(&packet_notifier);
#elif CLICK_FROMDEVICE_USE_BRIDGE
if (br_handle_frame_hook == click_br_handle_frame_hook)
br_handle_frame_hook = real_br_handle_frame_hook;
#endif
unregister_netdevice_notifier(&device_notifier);
}
Expand Down Expand Up @@ -106,7 +125,10 @@ FromDevice::configure(Vector<String> &conf, ErrorHandler *errh)
// make queue look full so packets sent to us are ignored
_head = _tail = _capacity = 0;

return find_device(&from_device_map, errh);
int before = errh->nerrors();
net_device *dev = lookup_device(errh);
set_device(dev, &from_device_map, anydev_from_device);
return errh->nerrors() == before ? 0 : -1;
}

/*
Expand All @@ -127,15 +149,18 @@ FromDevice::initialize(ErrorHandler *errh)
used = this;
}

if (!registered_readers) {
#ifdef HAVE_CLICK_KERNEL
if (registered_readers == 0) {
#if HAVE_CLICK_KERNEL
packet_notifier.next = 0;
register_net_in(&packet_notifier);
#elif CLICK_FROMDEVICE_USE_BRIDGE
real_br_handle_frame_hook = br_handle_frame_hook;
br_handle_frame_hook = click_br_handle_frame_hook;
#else
errh->warning("can't get packets: not compiled for a Click kernel");
#endif
}
registered_readers++;
++registered_readers;

reset_counts();

Expand All @@ -156,14 +181,17 @@ void
FromDevice::cleanup(CleanupStage stage)
{
if (stage >= CLEANUP_INITIALIZED) {
registered_readers--;
#ifdef HAVE_CLICK_KERNEL
--registered_readers;
#if HAVE_CLICK_KERNEL
if (registered_readers == 0)
unregister_net_in(&packet_notifier);
#elif CLICK_FROMDEVICE_USE_BRIDGE
if (registered_readers == 0)
br_handle_frame_hook = real_br_handle_frame_hook;
#endif
}

clear_device(&from_device_map);
clear_device(&from_device_map, anydev_from_device);

if (stage >= CLEANUP_INITIALIZED)
for (unsigned i = _head; i != _tail; i = next_i(i))
Expand Down Expand Up @@ -203,7 +231,7 @@ FromDevice::take_state(Element *e, ErrorHandler *errh)
*/
extern "C" {

#ifdef HAVE_CLICK_KERNEL
#if HAVE_CLICK_KERNEL
static int
packet_notifier_hook(struct notifier_block *nb, unsigned long backlog_len, void *v)
{
Expand All @@ -217,6 +245,24 @@ packet_notifier_hook(struct notifier_block *nb, unsigned long backlog_len, void
from_device_map.unlock(false, lock_flags);
return (stolen ? NOTIFY_STOP_MASK : 0);
}
#elif CLICK_FROMDEVICE_USE_BRIDGE
static struct sk_buff *
click_br_handle_frame_hook(struct net_bridge_port *p, struct sk_buff *skb)
{
int stolen = 0;
FromDevice *fd = 0;
unsigned long lock_flags;
from_device_map.lock(false, lock_flags);
while (stolen == 0 && (fd = (FromDevice *)from_device_map.lookup(skb->dev, fd)))
stolen = fd->got_skb(skb);
from_device_map.unlock(false, lock_flags);
if (stolen)
return 0;
else if (real_br_handle_frame_hook)
return real_br_handle_frame_hook(p, skb);
else
return skb;
}
#endif

static int
Expand All @@ -234,7 +280,7 @@ device_notifier_hook(struct notifier_block *nb, unsigned long flags, void *v)
AnyDevice *es[8];
int nes = from_device_map.lookup_all(dev, exists, es, 8);
for (int i = 0; i < nes; i++)
((FromDevice*)(es[i]))->set_device(flags == NETDEV_DOWN ? 0 : dev, &from_device_map, true);
((FromDevice*)(es[i]))->set_device(flags == NETDEV_DOWN ? 0 : dev, &from_device_map, AnyDevice::anydev_change | AnyDevice::anydev_from_device);
from_device_map.unlock(true, lock_flags);
}
return 0;
Expand Down Expand Up @@ -398,5 +444,6 @@ FromDevice::add_handlers()
add_write_handler("reset_counts", write_handler, H_RESET_COUNTS, Handler::BUTTON);
}

#undef CLICK_FROMDEVICE_USE_BRIDGE
ELEMENT_REQUIRES(AnyDevice linuxmodule)
EXPORT_ELEMENT(FromDevice)
2 changes: 1 addition & 1 deletion elements/linuxmodule/fromhost.cc
Original file line number Diff line number Diff line change
Expand Up @@ -484,5 +484,5 @@ FromHost::add_handlers()
add_data_handlers("drops", Handler::OP_READ, &_drops);
}

ELEMENT_REQUIRES(AnyDevice linuxmodule)
ELEMENT_REQUIRES(AnyDevice linuxmodule false)
EXPORT_ELEMENT(FromHost)
Loading

0 comments on commit ae76f68

Please sign in to comment.