mirror of
https://github.com/AMS-IX/arpsponge.git
synced 2024-09-21 15:57:09 +00:00
Checking in first arpswiffer.
This commit is contained in:
commit
596f74efa7
25
Makefile
Normal file
25
Makefile
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#!make
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
# Copyright (c) 1999-2002 Steven Bakker
|
||||||
|
# All rights reserved
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# All you'll probably ever need to edit is in `config.mk'
|
||||||
|
#
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
TOPDIR = .
|
||||||
|
|
||||||
|
include $(TOPDIR)/config.mk
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
default : all
|
||||||
|
|
||||||
|
all : sbin-all init.d-all man-all lib-all
|
||||||
|
|
||||||
|
install : sbin-install init.d-install man-install lib-install
|
||||||
|
|
||||||
|
clean : sbin-clean init.d-clean man-clean lib-clean
|
||||||
|
|
||||||
|
veryclean : sbin-veryclean init.d-veryclean man-veryclean lib-veryclean
|
80
config.mk
Normal file
80
config.mk
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#!make
|
||||||
|
# $Id$
|
||||||
|
# ============================================================================
|
||||||
|
#
|
||||||
|
# Copyright (c) 1999-2003 Steven Bakker
|
||||||
|
# All rights reserved
|
||||||
|
#
|
||||||
|
# CONFIG.MK: Installation preferences
|
||||||
|
#
|
||||||
|
# ============================================================================
|
||||||
|
#
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# MANDATORY CONFIG SECTION
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Where's perl on your system?
|
||||||
|
PERL = /usr/bin/perl
|
||||||
|
|
||||||
|
|
||||||
|
IFCONFIG = /sbin/ifconfig
|
||||||
|
DFL_RATE = 50
|
||||||
|
DFL_QUEUEDEPTH = 1000
|
||||||
|
DFL_ARP_AGE = 600
|
||||||
|
DFL_PENDING = 5
|
||||||
|
DFL_LOGLEVEL = info
|
||||||
|
|
||||||
|
SPONGE_OPTIONS = --age=$(DFL_ARP_AGE)
|
||||||
|
SPONGE_VAR = /var/run/arpswiffer
|
||||||
|
|
||||||
|
# ----------------------------
|
||||||
|
# --- Installation details ---
|
||||||
|
# ----------------------------
|
||||||
|
|
||||||
|
OWNER = root
|
||||||
|
GROUP = root
|
||||||
|
MODE = 644
|
||||||
|
BINMODE = 750
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# END MANDATORY SECTION
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# OPTIONAL SECTION
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------------------------------
|
||||||
|
# --- Locations for scripts, libraries, manual pages ---
|
||||||
|
# ------------------------------------------------------
|
||||||
|
|
||||||
|
# Prefix for most directories
|
||||||
|
DIRPREFIX = $(DESTDIR)/usr/local
|
||||||
|
|
||||||
|
BINPREFIX = $(DIRPREFIX)
|
||||||
|
DOCPREFIX = $(DIRPREFIX)/share
|
||||||
|
|
||||||
|
# Where to install perl scripts, jobs, library files and manual pages.
|
||||||
|
BINDIR = $(BINPREFIX)/sbin
|
||||||
|
LIBROOT = $(DIRPREFIX)/lib/site_perl
|
||||||
|
INSTLIB = $(LIBROOT)
|
||||||
|
MANDIR = $(DIRPREFIX)/man
|
||||||
|
DOCDIR = $(DOCPREFIX)/doc/$(NAME)-$(RELEASE)
|
||||||
|
|
||||||
|
# What section for the manual pages?
|
||||||
|
SECTION = 8
|
||||||
|
FILESECTION = 4
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# END OPTIONAL SECTION
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Don't change this. This is for people with a brain damaged csh(1).
|
||||||
|
# Well, you may change it, as long as it points to a Bourne-like shell.
|
||||||
|
SHELL = /bin/sh
|
||||||
|
|
||||||
|
LIBDIR = $(LIBROOT)
|
||||||
|
TOOLDIR = $(TOPDIR)/tools
|
||||||
|
AUTODIR = .
|
||||||
|
CURRDIR = .
|
40
init.d/Makefile
Normal file
40
init.d/Makefile
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#!make
|
||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
# (c) Copyright 2005 Steven Bakker, AMS-IX B.V.
|
||||||
|
#
|
||||||
|
# See the LICENSE file that came with this package.
|
||||||
|
#
|
||||||
|
include ../config.mk
|
||||||
|
|
||||||
|
TOPDIR = ..
|
||||||
|
|
||||||
|
INITDIR = $(DESTDIR)/etc/init.d
|
||||||
|
INSTALLDIRS = $(DESTDIR)/etc/init.d \
|
||||||
|
$(DESTDIR)/etc/rc0.d \
|
||||||
|
$(DESTDIR)/etc/rc1.d \
|
||||||
|
$(DESTDIR)/etc/rc2.d \
|
||||||
|
$(DESTDIR)/etc/rc3.d \
|
||||||
|
$(DESTDIR)/etc/rc4.d \
|
||||||
|
$(DESTDIR)/etc/rc5.d \
|
||||||
|
$(DESTDIR)/etc/rc6.d
|
||||||
|
|
||||||
|
INSTALLINKS = \
|
||||||
|
$(DESTDIR)/etc/rc0.d/K80arpswiffer:../init.d/arpswiffer \
|
||||||
|
$(DESTDIR)/etc/rc1.d/K80arpswiffer:../init.d/arpswiffer \
|
||||||
|
$(DESTDIR)/etc/rc2.d/S30arpswiffer:../init.d/arpswiffer \
|
||||||
|
$(DESTDIR)/etc/rc3.d/S30arpswiffer:../init.d/arpswiffer \
|
||||||
|
$(DESTDIR)/etc/rc4.d/S30arpswiffer:../init.d/arpswiffer \
|
||||||
|
$(DESTDIR)/etc/rc5.d/S30arpswiffer:../init.d/arpswiffer \
|
||||||
|
$(DESTDIR)/etc/rc6.d/K80arpswiffer:../init.d/arpswiffer
|
||||||
|
|
||||||
|
TARGETS = \
|
||||||
|
arpswiffer
|
||||||
|
|
||||||
|
INSTALLFILES = \
|
||||||
|
$(INITDIR)/arpswiffer
|
||||||
|
|
||||||
|
include ../rules.mk
|
||||||
|
|
||||||
|
# E.O.F. Makefile
|
115
init.d/arpsponge.sh
Normal file
115
init.d/arpsponge.sh
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
###############################################################################
|
||||||
|
# @(#) $Id$
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# Start-up script for the arpswiffer program.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
PATH=/sbin:/bin:/usr/bin:@BINDIR@
|
||||||
|
|
||||||
|
PROG=arpswiffer
|
||||||
|
SPONGE_VAR=@SPONGE_VAR@
|
||||||
|
SPONGE_OPTIONS="@SPONGE_OPTIONS@"
|
||||||
|
|
||||||
|
if test -f /etc/default/${PROG}/defaults ; then
|
||||||
|
. /etc/default/${PROG}/defaults
|
||||||
|
fi
|
||||||
|
|
||||||
|
start() {
|
||||||
|
[ "X$1" = "Xre-init" ] && re_init=true || re_init=false
|
||||||
|
|
||||||
|
SPONGES=`/bin/ls -1 /etc/default/${PROG}/eth* 2>/dev/null`
|
||||||
|
if [ -n "${SPONGES}" ]
|
||||||
|
then
|
||||||
|
echo "Starting ${PROG}(s):"
|
||||||
|
for file in ${SPONGES}
|
||||||
|
do
|
||||||
|
if=$(basename "${file}")
|
||||||
|
mkdir -p "${SPONGE_VAR}/${if}"
|
||||||
|
notify="${SPONGE_VAR}/${if}/notify"
|
||||||
|
status="${SPONGE_VAR}/${if}/status"
|
||||||
|
pidfile="${SPONGE_VAR}/${if}/pid"
|
||||||
|
|
||||||
|
# Create notification FIFO...
|
||||||
|
[ -p "${notify}" ] || /usr/bin/mkfifo --mode=644 "${notify}"
|
||||||
|
|
||||||
|
printf " %-10s " "${if}"
|
||||||
|
|
||||||
|
if ${re_init} && [ -f ${status} ]
|
||||||
|
then
|
||||||
|
init_arg="--re-init=${status}"
|
||||||
|
else
|
||||||
|
init_arg=''
|
||||||
|
fi
|
||||||
|
@BINDIR@/${PROG} ${SPONGE_OPTIONS} --daemon="${pidfile}" \
|
||||||
|
--notify="${notify}" --statusfile="${status}" \
|
||||||
|
${init_arg} \
|
||||||
|
$(/bin/cat "/etc/default/${PROG}/${if}") \
|
||||||
|
dev "${if}" 2>/dev/null \
|
||||||
|
&& echo "[Ok]" || echo "[FAILED]"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
echo "Stopping ${PROG}(s):"
|
||||||
|
pidfiles=`ls ${SPONGE_VAR}/*/pid 2>/dev/null`
|
||||||
|
for pf in ${pidfiles}
|
||||||
|
do
|
||||||
|
pid=$(cat ${pf})
|
||||||
|
iface=$(basename $(dirname ${pf}))
|
||||||
|
printf " interface=%-10s pid=%-6s " ${iface} ${pid}
|
||||||
|
kill -TERM ${pid}
|
||||||
|
sleep 1
|
||||||
|
if ps -p ${pid} > /dev/null 2>&1
|
||||||
|
then
|
||||||
|
kill -KILL ${pid}
|
||||||
|
echo KILLED
|
||||||
|
else
|
||||||
|
echo terminated
|
||||||
|
fi
|
||||||
|
/bin/rm -f ${SPONGE_VAR}/${iface}/notify
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
status() {
|
||||||
|
if [ "X$1" = "Xre-init" ]
|
||||||
|
then
|
||||||
|
echo "Saving state:"
|
||||||
|
else
|
||||||
|
echo "Dumping status:"
|
||||||
|
fi
|
||||||
|
pidfiles=`ls ${SPONGE_VAR}/*/pid 2>/dev/null`
|
||||||
|
for pf in ${pidfiles}
|
||||||
|
do
|
||||||
|
pid=$(cat ${pf})
|
||||||
|
iface=$(basename $(dirname ${pf}))
|
||||||
|
printf " interface=%-10s pid=%-6s " ${iface} ${pid}
|
||||||
|
kill -USR1 ${pid} 2>/dev/null && echo "[Ok]" || echo "[FAILED]"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
start
|
||||||
|
;;
|
||||||
|
restart|reload|force-reload)
|
||||||
|
status re-init
|
||||||
|
stop
|
||||||
|
start re-init
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {start|stop|restart|reload|force-reload}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
239
lib/M6/ARP/Queue.pm
Normal file
239
lib/M6/ARP/Queue.pm
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
##############################################################################
|
||||||
|
# @(#)$Id$
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# ARP Query Timestamp Queue
|
||||||
|
#
|
||||||
|
# (c) Copyright AMS-IX B.V. 2004-2005;
|
||||||
|
#
|
||||||
|
# See the LICENSE file that came with this package.
|
||||||
|
#
|
||||||
|
# A.Vijn, S.Bakker.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
package M6::ARP::Queue;
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
use Exporter;
|
||||||
|
our $Version = 1.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
M6::ARP::Queue - ARP query timestamp queue.
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use M6::ARP::Queue;
|
||||||
|
|
||||||
|
$q = new M6::ARP::Queue($max_depth);
|
||||||
|
|
||||||
|
$q->clear($some_ip);
|
||||||
|
$q->add($some_ip, $timestamp);
|
||||||
|
|
||||||
|
while ( ! $q->is_full($some_ip) ) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
$q_depth = $q->depth($some_ip);
|
||||||
|
$q_first = $q->get($some_ip, 0);
|
||||||
|
$q_last = $q->get($some_ip, -1);
|
||||||
|
|
||||||
|
$q_per_min = $q->rate($some_ip);
|
||||||
|
|
||||||
|
$listref = $q->get_queue($some_ip);
|
||||||
|
print "timestamps: ", join(", ", @{$listref}), "\n";
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This object class is mainly used by the L<M6::ARP::Sponge|M6::ARP::Sponge>
|
||||||
|
module to store timestamps for ARP queries.
|
||||||
|
|
||||||
|
The object holds a collection of circular buffers that are accessed by
|
||||||
|
unique keys (IP address strings in the typical usage scenario). Timestamps
|
||||||
|
are added to a queue until its size reaches the maximum depth, at which
|
||||||
|
point newly added values cause the oldest values to be shifted off the
|
||||||
|
queue.
|
||||||
|
|
||||||
|
Although primarily used for storing timestamps of ARP queries
|
||||||
|
for IP addresses, it can be used for more general work as well.
|
||||||
|
Any string can be used as a queue key and arbitrary data can be
|
||||||
|
added to the queues.
|
||||||
|
|
||||||
|
Only the L</rate|rate> method makes assumptions about the data
|
||||||
|
(i.e. that they are timestamps in seconds).
|
||||||
|
|
||||||
|
=head1 CONSTRUCTOR
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item X<new>B<new> ( I<MAXDEPTH> )
|
||||||
|
|
||||||
|
Create a new object instance. Each queue will have a maximum depth
|
||||||
|
of I<MAXDEPTH>. Returns a reference to the newly created object.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new($$) {
|
||||||
|
my $type = shift;
|
||||||
|
my $max_depth = shift;
|
||||||
|
|
||||||
|
if (ref $type) { $type = ref $type }
|
||||||
|
bless {'max_depth' => $max_depth}, $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 METHODS
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item X<clear>B<clear> ( I<IP> )
|
||||||
|
|
||||||
|
Clear the queue for I<IP>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub clear($$) { delete $_[0]->{$_[1]} }
|
||||||
|
|
||||||
|
=item X<depth>B<depth> ( I<IP> )
|
||||||
|
|
||||||
|
Return the depth of the queue for I<IP>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub depth($$) { $_[0]->{$_[1]} ? int(@{$_[0]->{$_[1]}}) : 0 }
|
||||||
|
|
||||||
|
=item X<rate>B<rate> ( I<IP> )
|
||||||
|
|
||||||
|
Return the (average) query rate (as a real number) for I<IP> in queries
|
||||||
|
per minute.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub rate($$) {
|
||||||
|
my $q = $_[0]->{$_[1]};
|
||||||
|
return undef if @$q < 2;
|
||||||
|
my $first = $q->[0];
|
||||||
|
my $last = $q->[$#$q];
|
||||||
|
my $time = ($first < $last) ? $last-$first : 1;
|
||||||
|
my $n = int(@$q);
|
||||||
|
return ($n / $time) * 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
=item X<max_depth>B<max_depth>
|
||||||
|
|
||||||
|
Return the maximum depth of the queues.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub max_depth($) { $_[0]->{'max_depth'} }
|
||||||
|
|
||||||
|
=item X<is_full>B<is_full> ( I<IP> )
|
||||||
|
|
||||||
|
Return whether or not the queue for I<IP> is full, i.e. is wrapping.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub is_full($$) { $_[0]->depth($_[1]) >= $_[0]->max_depth }
|
||||||
|
|
||||||
|
=item X<add>B<add> ( I<IP>, I<TIMESTAMP> )
|
||||||
|
|
||||||
|
Add I<TIMESTAMP to the queue for I<IP>, wrapping the buffer ring if
|
||||||
|
necessary. Returns the new queue depth.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub add($$$) {
|
||||||
|
my ($self, $ip, $val) = @_;
|
||||||
|
if ($self->depth($ip) >= $self->max_depth) {
|
||||||
|
shift @{$self->{$ip}};
|
||||||
|
}
|
||||||
|
push @{$self->{$ip}}, $val;
|
||||||
|
return int(@{$self->{$ip}});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=item X<get>B<get> ( I<IP> [, I<INDEX>] )
|
||||||
|
|
||||||
|
Return the data value at position I<INDEX> in the queue for I<IP>.
|
||||||
|
Zero (0) is the oldest; positive values for I<INDEX> give increasingly
|
||||||
|
more recent values. Negative numbers count from the end of the queue,
|
||||||
|
so C<-1> gives the most recently added value.
|
||||||
|
|
||||||
|
Compare:
|
||||||
|
|
||||||
|
QUEUE->get( IP, -n ) == QUEUE->get( IP, QUEUE->depth(IP) - n )
|
||||||
|
|
||||||
|
QUEUE->get( IP ) == QUEUE->get( IP, 0 );
|
||||||
|
|
||||||
|
Also:
|
||||||
|
|
||||||
|
QUEUE->get( IP, n ) == QUEUE->get-_queue( IP )->[n]
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub get($$;$) {
|
||||||
|
my ($self, $ip, $index) = @_;
|
||||||
|
|
||||||
|
$index = 0 unless defined($index);
|
||||||
|
if ($index < 0) {
|
||||||
|
$index = int(@{$self->{$ip}}) + $index;
|
||||||
|
$index = 0 if $index < 0;
|
||||||
|
}
|
||||||
|
return $self->{$ip}->[$index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=item X<get_queue>B<get_queue> ( I<IP> )
|
||||||
|
|
||||||
|
Return the timestamps for I<IP>.
|
||||||
|
I<NOTE:> this is a reference to the internal list of data, so be careful
|
||||||
|
not to inadvertently modify it.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub get_queue($$;$) {
|
||||||
|
return $self->{$ip};
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
__END__
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 EXAMPLE
|
||||||
|
|
||||||
|
use M6::ARP::Queue;
|
||||||
|
use Time::HiRes qw( usleep );
|
||||||
|
use POSIX qw( strftime );
|
||||||
|
|
||||||
|
my $some_ip = '10.1.1.1';
|
||||||
|
|
||||||
|
$q = new M6::ARP::Queue(100);
|
||||||
|
|
||||||
|
printf("filling queue for $some_ip (max %d)\n", $q->max_depth);
|
||||||
|
|
||||||
|
$q->clear($some_ip);
|
||||||
|
while (!$q->is_full($some_ip)) {
|
||||||
|
$q->add($some_ip, time);
|
||||||
|
print STDERR sprintf("\rdepth: %3d", $q->depth($some_ip));
|
||||||
|
usleep(rand(5e5));
|
||||||
|
}
|
||||||
|
printf("\rdepth: %3d\n", $q->depth($some_ip));
|
||||||
|
print strftime("first: %H:%M:%S\n", localtime($q->get($some_ip, 0)));
|
||||||
|
print strftime("last: %H:%M:%S\n", localtime($q->get($some_ip, -1)));
|
||||||
|
printf("rate: %0.2f queries/minute\n", $q->rate($some_ip));
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<perl(1)|perl>, L<M6::ARP::Sponge(3)|M6::ARP::Sponge>.
|
||||||
|
|
||||||
|
=head1 AUTHORS
|
||||||
|
|
||||||
|
Steven Bakker at AMS-IX (steven.bakker@ams-ix.net).
|
||||||
|
|
||||||
|
=cut
|
495
lib/M6/ARP/Sponge.pm
Normal file
495
lib/M6/ARP/Sponge.pm
Normal file
@ -0,0 +1,495 @@
|
|||||||
|
###############################################################################
|
||||||
|
# @(#)$Id$
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# ARP sponge
|
||||||
|
#
|
||||||
|
# (c) Copyright AMS-IX B.V. 2004-2005;
|
||||||
|
#
|
||||||
|
# See the LICENSE file that came with this package.
|
||||||
|
#
|
||||||
|
# A.Vijn, 2003-2004;
|
||||||
|
# S.Bakker, November 2004;
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
package M6::ARP::Sponge;
|
||||||
|
|
||||||
|
use M6::ARP::Queue;
|
||||||
|
use M6::ARP::Util qw( :all );
|
||||||
|
|
||||||
|
use Net::PcapUtils;
|
||||||
|
use POSIX qw( strftime );
|
||||||
|
use NetPacket::Ethernet qw( :types );
|
||||||
|
use NetPacket::ARP qw( ARP_OPCODE_REQUEST );
|
||||||
|
use NetPacket::IP;
|
||||||
|
use Net::ARP;
|
||||||
|
use Sys::Syslog;
|
||||||
|
use Net::IPv4Addr qw( :all );
|
||||||
|
use IO::File;
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
use Exporter;
|
||||||
|
our $Version = 1.01;
|
||||||
|
our @ISA = qw( Exporter );
|
||||||
|
|
||||||
|
my @states = qw( STATIC DEAD ALIVE PENDING );
|
||||||
|
my @log = qw( print_log print_notify );
|
||||||
|
|
||||||
|
our @EXPORT_OK = ( @states, @log );
|
||||||
|
our @EXPORT = ();
|
||||||
|
|
||||||
|
our %EXPORT_TAGS = (
|
||||||
|
'states' => \@states,
|
||||||
|
'log' => \@log,
|
||||||
|
'all' => [ @log, @states ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
use constant STATIC => -3;
|
||||||
|
use constant DEAD => -2;
|
||||||
|
use constant ALIVE => -1;
|
||||||
|
|
||||||
|
sub PENDING($;$) { 0 + $_[$#_] };
|
||||||
|
|
||||||
|
# $val = _getset($field, $sponge [, $val]);
|
||||||
|
#
|
||||||
|
# Help routine for setting/getting fields.
|
||||||
|
# When the object instance is a hash ref, simple object attributes
|
||||||
|
# are easily defined:
|
||||||
|
#
|
||||||
|
# sub myfield { _getset('myfield', @_) }
|
||||||
|
#
|
||||||
|
sub _getset($@) {
|
||||||
|
my $field = shift;
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my $v = $self->{$field};
|
||||||
|
if (@_) {
|
||||||
|
$self->{$field} = shift;
|
||||||
|
}
|
||||||
|
return $v;
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# Object Attributes
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub queue { $_[0]->{'queue'} }
|
||||||
|
sub device { $_[0]->{'device'} }
|
||||||
|
sub phys_device { $_[0]->{'phys_device'} }
|
||||||
|
|
||||||
|
sub syslog_ident { _getset('syslog_ident', @_) }
|
||||||
|
sub is_verbose { _getset('verbose', @_) }
|
||||||
|
sub is_dummy { _getset('dummy', @_) }
|
||||||
|
sub queuedepth { _getset('queuedepth', @_) }
|
||||||
|
sub my_ip { _getset('my_ip', @_) }
|
||||||
|
sub is_my_ip { $_[0]->{'ip_all'}->{$_[1]} }
|
||||||
|
sub my_mac { _getset('my_mac', @_) }
|
||||||
|
sub network { _getset('network', @_) }
|
||||||
|
sub netmask { _getset('netmask', @_) }
|
||||||
|
sub loglevel { _getset('loglevel', @_) }
|
||||||
|
sub max_pending { _getset('max_pending', @_) }
|
||||||
|
sub notify { _getset('notify', @_) }
|
||||||
|
sub max_rate { _getset('max_rate', @_) }
|
||||||
|
sub arp_age { _getset('arp_age', @_) }
|
||||||
|
sub gratuitous { _getset('gratuitous', @_) }
|
||||||
|
|
||||||
|
sub state_atime { $_[0]->{state_atime}->{$_[1]} }
|
||||||
|
sub set_state_atime { $_[0]->{state_atime}->{$_[1]} = $_[2] }
|
||||||
|
|
||||||
|
sub state_mtime { $_[0]->{state_mtime}->{$_[1]} }
|
||||||
|
sub set_state_mtime { $_[0]->{state_mtime}->{$_[1]} = $_[2] }
|
||||||
|
|
||||||
|
sub state_table { $_[0]->{state} }
|
||||||
|
sub get_state { $_[0]->{state}->{$_[1]} }
|
||||||
|
sub set_state {
|
||||||
|
$_[0]->{state_mtime}->{$_[1]} = $_[0]->{state_atime}->{$_[1]} = time;
|
||||||
|
$_[0]->{state}->{$_[1]} = $_[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $sponge->DESTROY
|
||||||
|
#
|
||||||
|
# Destructor. Called by Perl's garbage collection.
|
||||||
|
###############################################################################
|
||||||
|
sub DESTROY {
|
||||||
|
my $self = shift;
|
||||||
|
if ($self->notify) {
|
||||||
|
$self->notify->close;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $sponge = new M6::ARP::Sponge(ARG => VAL ...)
|
||||||
|
#
|
||||||
|
# Create a new Sponge object.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub new {
|
||||||
|
my $type = shift;
|
||||||
|
|
||||||
|
my $self = {};
|
||||||
|
while (@_ >= 2) {
|
||||||
|
my $k = shift @_;
|
||||||
|
my $v = shift @_;
|
||||||
|
$self->{lc $k} = $v;
|
||||||
|
|
||||||
|
}
|
||||||
|
bless $self, $type;
|
||||||
|
|
||||||
|
$self->{queuedepth} = '@DFL_QUEUEDEPTH@' unless $self->queuedepth;
|
||||||
|
unless (length $self->syslog_ident) {
|
||||||
|
my ($prog) = $0 =~ m|([^/]+)$|;
|
||||||
|
$self->syslog_ident($prog);
|
||||||
|
}
|
||||||
|
$self->{state} = {};
|
||||||
|
$self->{state_mtime} = {};
|
||||||
|
$self->{state_atime} = {};
|
||||||
|
$self->{queue} = new M6::ARP::Queue($self->queuedepth);
|
||||||
|
|
||||||
|
$self->my_ip( $self->get_ip );
|
||||||
|
$self->my_mac( $self->get_mac );
|
||||||
|
|
||||||
|
$self->{'ip_all'} = { map { $_ => 1 } $self->get_ip_all };
|
||||||
|
|
||||||
|
$self->loglevel('info') unless length $self->loglevel;
|
||||||
|
|
||||||
|
$self->{'arp_table'} = {
|
||||||
|
$self->my_ip => [ $self->my_mac, time ]
|
||||||
|
};
|
||||||
|
|
||||||
|
($self->{'phys_device'}) = split(/:/, $self->{'device'});
|
||||||
|
|
||||||
|
if ($self->is_verbose) {
|
||||||
|
$self->verbose(1, "Device: ", $self->device, "\n");
|
||||||
|
$self->verbose(1, "Device: ", $self->phys_device, "\n");
|
||||||
|
$self->verbose(1, "MAC: ", $self->my_mac, "\n");
|
||||||
|
$self->verbose(1, "IP: ", $self->my_ip, "\n");
|
||||||
|
}
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $table = $sponge->arp_table;
|
||||||
|
# ($mac, $time) = $sponge->arp_table($ip);
|
||||||
|
# ($mac, $time) = $sponge->arp_table($ip, $mac [, $time]);
|
||||||
|
#
|
||||||
|
# Perform a ARP table lookup, or update the ARP table.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub arp_table($;$$$) {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
return $self->{'arp_table'} unless @_;
|
||||||
|
|
||||||
|
my $ip = shift;
|
||||||
|
|
||||||
|
if (@_) {
|
||||||
|
my $mac = shift;
|
||||||
|
my $time = @_ ? shift : time;
|
||||||
|
$self->{'arp_table'}->{$ip} = [ $mac, $time ];
|
||||||
|
}
|
||||||
|
return @{$self->{'arp_table'}->{$ip}};
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $mac = $sponge->get_mac;
|
||||||
|
# $mac = $sponge->get_mac($device);
|
||||||
|
# $mac = get_mac($device);
|
||||||
|
#
|
||||||
|
# Return MAC address for device $device.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub get_mac($;$) {
|
||||||
|
my $dev = pop @_;
|
||||||
|
if (ref $dev) { $dev = $dev->device }
|
||||||
|
my $mac;
|
||||||
|
Net::ARP::get_mac($dev, $mac);
|
||||||
|
return mac2mac($mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# @ip = $sponge->get_ip_all;
|
||||||
|
#
|
||||||
|
# Return all IP addresses for physical device $device. This includes all
|
||||||
|
# addresses configured on "sub" interfaces.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub get_ip_all {
|
||||||
|
my @ip;
|
||||||
|
|
||||||
|
open(IFCONFIG, 'ifconfig -a 2>/dev/null|');
|
||||||
|
local($_);
|
||||||
|
while (<IFCONFIG>) {
|
||||||
|
if (/^.*inet addr:(\S+)/) {
|
||||||
|
push @ip, $1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close IFCONFIG;
|
||||||
|
return @ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $ip = $sponge->get_ip;
|
||||||
|
# $ip = $sponge->get_ip($device);
|
||||||
|
# $ip = get_ip($device);
|
||||||
|
#
|
||||||
|
# Return IP address for device $device, or '0.0.0.0' if none.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub get_ip($;$) {
|
||||||
|
my $dev = pop @_;
|
||||||
|
if (ref $dev) { $dev = $dev->device }
|
||||||
|
my $ip = `ifconfig $dev 2>/dev/null`;
|
||||||
|
|
||||||
|
unless ($ip =~ s/^.*inet addr:(\S+).*$/$1/s) {
|
||||||
|
$ip = '0.0.0.0';
|
||||||
|
}
|
||||||
|
return $ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $bool = $sponge->is_my_network($target_ip)
|
||||||
|
#
|
||||||
|
# Returns whether or not $target_ip is in the monitored
|
||||||
|
# network range(s).
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub is_my_network($$) {
|
||||||
|
my ($self, $target_ip) = @_;
|
||||||
|
return ipv4_in_network($self->network, $self->netmask, $target_ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $state = $sponge->set_pending($target_ip, $n);
|
||||||
|
#
|
||||||
|
# Set $target_ip's state to PENDING "$n". Returns new state.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub set_pending($$;$) {
|
||||||
|
my ($self, $target_ip, $n) = @_;
|
||||||
|
$self->set_state($target_ip, PENDING($n));
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $state = $sponge->incr_pending($target_ip);
|
||||||
|
#
|
||||||
|
# Increment $target_ip's PENDING state. Returns new state.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub incr_pending($$) {
|
||||||
|
my ($self, $target_ip) = @_;
|
||||||
|
$self->set_state($target_ip, $self->get_state($target_ip)+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $sponge->send_probe($target_ip);
|
||||||
|
#
|
||||||
|
# Send a (probe) ARP "WHO HAS $target_ip". This prevents us from
|
||||||
|
# erroneously sponging when there's a cretin sending ARP floods.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub send_probe($$) {
|
||||||
|
my ($self, $target_ip) = @_;
|
||||||
|
|
||||||
|
$self->verbose(2, "Probing [dev=", $self->phys_device, "]: $target_ip\n");
|
||||||
|
|
||||||
|
$self->set_state_atime($target_ip, time);
|
||||||
|
|
||||||
|
return if $self->is_dummy;
|
||||||
|
|
||||||
|
Net::ARP::send_packet($self->phys_device,
|
||||||
|
$self->my_ip, $target_ip,
|
||||||
|
$self->my_mac, 'ff:ff:ff:ff:ff:ff',
|
||||||
|
'request'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $sponge->gratuitous_arp($ip);
|
||||||
|
#
|
||||||
|
# Send a (sponge) ARP WHO HAS $ip TELL $ip".
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub gratuitous_arp($$) {
|
||||||
|
my ($self, $ip) = @_;
|
||||||
|
|
||||||
|
$self->verbose(1, "Gratuitous ARP [dev=", $self->phys_device, "]: $ip\n");
|
||||||
|
|
||||||
|
$self->set_state_atime($ip, time);
|
||||||
|
|
||||||
|
return if $self->is_dummy;
|
||||||
|
|
||||||
|
Net::ARP::send_packet($self->phys_device,
|
||||||
|
$ip, $ip,
|
||||||
|
$self->my_mac, 'ff:ff:ff:ff:ff:ff',
|
||||||
|
'request'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $sponge->send_reply($src_ip, $arp_obj);
|
||||||
|
#
|
||||||
|
# Send a (sponge) ARP "$src_ip IS AT" in reply to the $arp_obj request.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub send_reply($$$) {
|
||||||
|
my ($self, $src_ip, $arp_obj) = @_;
|
||||||
|
|
||||||
|
$self->set_state_atime($src_ip, time);
|
||||||
|
|
||||||
|
# Figure out where to send the reply...
|
||||||
|
my $dst_mac = hex2mac($arp_obj->{sha});
|
||||||
|
my $dst_ip = hex2ip($arp_obj->{spa});
|
||||||
|
$self->verbose(1, "$src_ip: sponge reply to $dst_ip\@$dst_mac\n");
|
||||||
|
return if $self->is_dummy;
|
||||||
|
Net::ARP::send_packet($self->phys_device, $src_ip, $dst_ip,
|
||||||
|
$self->my_mac, $dst_mac, 'reply'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $sponge->set_dead($target_ip);
|
||||||
|
#
|
||||||
|
# Set $target_ip's state to DEAD (i.e. "sponged").
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub set_dead($$) {
|
||||||
|
my ($self, $ip) = @_;
|
||||||
|
my $rate = $self->queue->rate($ip);
|
||||||
|
|
||||||
|
$self->print_log("ARP-rate for %s is %0.1f q/min", $ip, $rate);
|
||||||
|
$self->print_log("Sponging: %s", $ip);
|
||||||
|
$self->print_notify("action=sponge;ip=%s;mac=%s", $ip, $self->my_mac);
|
||||||
|
|
||||||
|
$self->gratuitous_arp($ip) if $self->gratuitous;
|
||||||
|
$self->set_state($ip, DEAD);
|
||||||
|
# This is the place where we could send a gratuitous ARP for
|
||||||
|
# the sponged address to shut up all other queriers.
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# set_alive($data, $target_ip, $target_mac);
|
||||||
|
#
|
||||||
|
# Unsponge the $target_ip, which is now seen from $target_mac.
|
||||||
|
# Update ARP cache and print appropriate notifications.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub set_alive($$$) {
|
||||||
|
my ($self, $ip, $mac) = @_;
|
||||||
|
|
||||||
|
return unless $self->is_my_network($ip);
|
||||||
|
|
||||||
|
if ($self->get_state($ip) == DEAD) {
|
||||||
|
$self->print_log("Unsponging: %s [found at %s]", $ip, $mac);
|
||||||
|
$self->print_notify("action=unsponge;ip=%s;mac=%s", $ip, $mac);
|
||||||
|
}
|
||||||
|
elsif ($self->queue->depth($ip) > 0) {
|
||||||
|
$self->verbose(1, "Clearing: $ip [found at $mac]\n");
|
||||||
|
$self->print_notify("action=clear;ip=%s;mac=%s", $ip, $mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->queue->clear($ip);
|
||||||
|
$self->set_state($ip, ALIVE);
|
||||||
|
|
||||||
|
my @arp = $self->arp_table($ip);
|
||||||
|
|
||||||
|
if (!@arp) {
|
||||||
|
$self->print_notify("action=learn;ip=%s;mac=%s", $ip, $mac);
|
||||||
|
}
|
||||||
|
elsif ($arp[0] ne $mac) {
|
||||||
|
$self->print_notify("action=flip;ip=%s;mac=%s", $ip, $mac);
|
||||||
|
}
|
||||||
|
elsif (time - $arp[1] > $self->arp_age) {
|
||||||
|
$self->print_notify("action=refresh;ip=%s;mac=%s",
|
||||||
|
$ip, $mac);
|
||||||
|
}
|
||||||
|
$self->arp_table($ip, $mac, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $sponge->verbose($level, $arg, ...);
|
||||||
|
# verbose($level, $arg, ...);
|
||||||
|
#
|
||||||
|
# Print the arguments to STDOUT if verbosity is at least $level.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
sub verbose($$;@) {
|
||||||
|
my ($self, $verbose);
|
||||||
|
|
||||||
|
if (UNIVERSAL::isa($_[0], 'M6::ARP::Sponge')) {
|
||||||
|
$self = shift;
|
||||||
|
$verbose = $self->is_verbose;
|
||||||
|
}
|
||||||
|
$verbose = $::opt_verbose unless $verbose;
|
||||||
|
|
||||||
|
my $level = shift;
|
||||||
|
|
||||||
|
if ($verbose >= $level) {
|
||||||
|
print STDOUT strftime("%Y-%m-%d %H:%M:%S ", localtime(time)), @_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $sponge->print_log_level($level, $format, ...);
|
||||||
|
# print_log_level($level, $format, ...);
|
||||||
|
###############################################################################
|
||||||
|
sub print_log_level {
|
||||||
|
my ($self, $syslog);
|
||||||
|
if (UNIVERSAL::isa($_[0], 'M6::ARP::Sponge')) {
|
||||||
|
$self = shift;
|
||||||
|
$syslog = $self->syslog_ident;
|
||||||
|
}
|
||||||
|
$syslog = $0 unless length $syslog;
|
||||||
|
|
||||||
|
my ($level, $format, @args) = @_;
|
||||||
|
if ($self->is_dummy || $self->is_verbose) {
|
||||||
|
print STDOUT strftime("%Y-%m-%d %H:%M:%S ", localtime(time));
|
||||||
|
print STDOUT $syslog, "[$$]: ";
|
||||||
|
chomp(my $msg = sprintf($format, @args));
|
||||||
|
print STDOUT $msg, "\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
openlog($syslog, 'cons,pid', 'user');
|
||||||
|
syslog($level, $format, @args);
|
||||||
|
closelog;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $sponge->print_log($format, ...);
|
||||||
|
#
|
||||||
|
# Log $format, ... to syslog. Syntax is identical to that of printf().
|
||||||
|
# Prints to STDOUT if verbose or dummy.
|
||||||
|
###############################################################################
|
||||||
|
sub print_log {
|
||||||
|
my ($self, $format, @args) = @_;
|
||||||
|
$self->print_log_level($self->loglevel, $format, @args);
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# $sponge->print_notify($format, ...);
|
||||||
|
# print_notify($fh, $format, ...);
|
||||||
|
#
|
||||||
|
# Notify of sponge actions on the notify handle.
|
||||||
|
###############################################################################
|
||||||
|
sub print_notify($$;@) {
|
||||||
|
my ($self, $fh);
|
||||||
|
if (UNIVERSAL::isa($_[0], 'M6::ARP::Sponge')) {
|
||||||
|
$self = shift;
|
||||||
|
$fh = $self->notify;
|
||||||
|
}
|
||||||
|
elsif (UNIVERSAL::isa($_[0], 'IO::Handle')) {
|
||||||
|
$fh = shift;
|
||||||
|
}
|
||||||
|
return unless defined $fh;
|
||||||
|
|
||||||
|
my $format = shift @_;
|
||||||
|
|
||||||
|
$fh->print(int(time), ";id=",
|
||||||
|
$self->syslog_ident, ";", sprintf($format, @_), "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
157
lib/M6/ARP/Table.pm
Normal file
157
lib/M6/ARP/Table.pm
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
##############################################################################
|
||||||
|
# @(#)$Id$
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# ARP Table
|
||||||
|
#
|
||||||
|
# (c) Copyright AMS-IX B.V. 2004-2005;
|
||||||
|
#
|
||||||
|
# See the LICENSE file that came with this package.
|
||||||
|
#
|
||||||
|
# S.Bakker, 2005
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
package M6::ARP::Table;
|
||||||
|
|
||||||
|
use Time::HiRes qw( time );
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
use Exporter;
|
||||||
|
our $Version = 1.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
M6::ARP::Table - keep a table of ARP entries
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use M6::ARP::Table;
|
||||||
|
|
||||||
|
$table = new M6::ARP::Table;
|
||||||
|
|
||||||
|
$table->clear($some_ip);
|
||||||
|
$table->add($some_ip, $some_mac);
|
||||||
|
|
||||||
|
$mac = $table->arp($some_ip);
|
||||||
|
$stamp = $table->mtime($some_ip);
|
||||||
|
@iplist = $table->rarp($mac);
|
||||||
|
|
||||||
|
@iplist = $table->ip_list;
|
||||||
|
@maclist = $table->mac_list;
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This object class can be used by network monitoring processes to keep
|
||||||
|
track of IP to MAC mappings.
|
||||||
|
|
||||||
|
=head1 CONSTRUCTOR
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item X<new>B<new>
|
||||||
|
|
||||||
|
Create a new object instance and return a reference to it.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new($$) {
|
||||||
|
my $type = shift;
|
||||||
|
my $max_depth = shift;
|
||||||
|
|
||||||
|
if (ref $type) { $type = ref $type }
|
||||||
|
bless { arp => {}, rarp => {} }, $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 METHODS
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item X<clear>B<clear> ( I<IP> )
|
||||||
|
|
||||||
|
Clear the ARP table for I<IP>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub clear($$) {
|
||||||
|
my ($self, $ip) = @_;
|
||||||
|
|
||||||
|
if (my $mac = $self->arp($ip)) {
|
||||||
|
delete $self->{rarp}->{$mac}->{$ip};
|
||||||
|
}
|
||||||
|
delete $self->{arp}->{$ip};
|
||||||
|
}
|
||||||
|
|
||||||
|
=item X<arp>B<arp> ( I<IP> )
|
||||||
|
|
||||||
|
Return the MAC address for I<IP>. Returns C<undef> if there is no
|
||||||
|
entry for I<IP>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub arp($$) { $_[0]->{'arp'}->{$_[1]} }
|
||||||
|
|
||||||
|
=item X<rarp>B<rarp> ( I<MAC> )
|
||||||
|
|
||||||
|
Return a sorted list of IP addresses that are mapped to I<MAC>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub rarp($$) { sort { ip_sort($a, $b) } keys %{$_[0]->{'rarp'}->{$_[1]}} }
|
||||||
|
|
||||||
|
=item X<ip_list>B<ip_list>
|
||||||
|
|
||||||
|
Return a sorted list of IP addresses that are present in the ARP table.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub ip_list($) { sort { ip_sort($a, $b) } keys %{$_[0]->{'arp'}} }
|
||||||
|
|
||||||
|
=item X<mac_list>B<mac_list>
|
||||||
|
|
||||||
|
Return a sorted list of MAC addresses that are present in the ARP table.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub mac_list($) { sort { ip_sort($a, $b) } keys %{$_[0]->{'rarp'}} }
|
||||||
|
|
||||||
|
=item X<add>B<add> ( I<IP>, I<MAC> [, I<TIMESTAMP>] )
|
||||||
|
|
||||||
|
Add I<IP> to I<MAC> mapping to the table. If I<TIMESTAMP> is given, use
|
||||||
|
it for the entry's timestamp, otherwise use the current time.
|
||||||
|
Returns the timestamp.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub add($$$;$) {
|
||||||
|
my ($self, $ip, $mac, $timestamp) = @_;
|
||||||
|
$timestamp = time unless defined($timestamp);
|
||||||
|
$self->clear($ip);
|
||||||
|
$self->{'arp'}->{$ip} = $mac;
|
||||||
|
$self->{'rarp'}->{$mac}->{$ip} = $timestamp;
|
||||||
|
return $timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
__END__
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 EXAMPLE
|
||||||
|
|
||||||
|
See the L</SYNOPSIS> section.
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<perl(1)|perl>, L<M6::ARP::Sponge(3)|M6::ARP::Sponge>.
|
||||||
|
|
||||||
|
=head1 AUTHORS
|
||||||
|
|
||||||
|
Steven Bakker at AMS-IX (steven.bakker@ams-ix.net).
|
||||||
|
|
||||||
|
=cut
|
195
lib/M6/ARP/Util.pm
Normal file
195
lib/M6/ARP/Util.pm
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
##############################################################################
|
||||||
|
# @(#)$Id$
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# ARP Stuff Utility routines
|
||||||
|
#
|
||||||
|
# (c) Copyright AMS-IX B.V. 2004-2005;
|
||||||
|
#
|
||||||
|
# See the LICENSE file that came with this package.
|
||||||
|
#
|
||||||
|
# S.Bakker.
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
package M6::ARP::Util;
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
use Exporter;
|
||||||
|
|
||||||
|
our $Version = 1.01;
|
||||||
|
our @ISA = qw( Exporter );
|
||||||
|
|
||||||
|
our @EXPORT_OK = qw( int2ip ip2int hex2ip ip2hex hex2mac mac2hex mac2mac );
|
||||||
|
our @EXPORT = ();
|
||||||
|
|
||||||
|
our %EXPORT_TAGS = (
|
||||||
|
'all' => [ @EXPORT_OK ]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
M6::ARP::Util - IP/MAC utility routines
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use M6::ARP::Util qw( :all );
|
||||||
|
|
||||||
|
$ip = int2ip( $num );
|
||||||
|
$num = ip2int( $ip );
|
||||||
|
$ip = hex2ip( $hex );
|
||||||
|
$hex = ip2hex( $ip );
|
||||||
|
$mac = hex2mac( $hex );
|
||||||
|
$hex = mac2hex( $mac );
|
||||||
|
$mac = mac2mac( $mac );
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This module defines a number of routines to convert IP and MAC
|
||||||
|
representations to and from various formats.
|
||||||
|
|
||||||
|
=head1 FUNCTIONS
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
=item X<int2ip>B<int2ip> ( I<num> )
|
||||||
|
|
||||||
|
Convert a (long) integer to a dotted decimal IP address. Return the
|
||||||
|
dotted decimal string.
|
||||||
|
|
||||||
|
Example: int2ip(3250751620) returns "193.194.136.132".
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub int2ip($) {
|
||||||
|
hex2ip(sprintf("%08x", shift @_));
|
||||||
|
};
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
=item X<ip2int>B<ip2int> ( I<IPSTRING> )
|
||||||
|
|
||||||
|
Dotted decimal IPv4 address to integer representation.
|
||||||
|
|
||||||
|
Example: ip2int("193.194.136.132") returns "3250751620".
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub ip2int($) {
|
||||||
|
hex(ip2hex(shift @_));
|
||||||
|
};
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
=item X<hex2ip>B<hex2ip> ( I<HEXSTRING> )
|
||||||
|
|
||||||
|
Hexadecimal IPv4 address to dotted decimal representation.
|
||||||
|
|
||||||
|
Example: hex2ip("c1c28884") returns "193.194.136.132".
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub hex2ip($) {
|
||||||
|
my $hex = shift;
|
||||||
|
|
||||||
|
$hex =~ /(..)(..)(..)(..)/;
|
||||||
|
my $ip = sprintf("%d.%d.%d.%d", hex($1), hex($2), hex($3), hex($4));
|
||||||
|
return $ip;
|
||||||
|
};
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
=item X<ip2hex>B<ip2hex> ( I<IPSTRING> )
|
||||||
|
|
||||||
|
Dotted decimal IPv4 address to hex representation.
|
||||||
|
|
||||||
|
Example: ip2hex("193.194.136.132")
|
||||||
|
returns "c1c28884".
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub ip2hex($) {
|
||||||
|
return sprintf("%02x%02x%02x%02x", split(/\./, shift));
|
||||||
|
};
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
=item X<hex2mac>B<hex2mac> ( I<HEXSTRING> )
|
||||||
|
|
||||||
|
Hexadecimal MAC address to colon-separated hex representation.
|
||||||
|
|
||||||
|
Example: hex2mac("a1b20304e5f6")
|
||||||
|
returns "a1:b2:03:04:e5:f6"
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub hex2mac($) {
|
||||||
|
my $hex = substr("000000000000".(shift @_), -12);
|
||||||
|
$hex =~ /(..)(..)(..)(..)(..)(..)/;
|
||||||
|
return sprintf("%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
hex($1), hex($2), hex($3), hex($4), hex($5), hex($6));
|
||||||
|
};
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
=item X<mac2hex>B<mac2hex> ( I<macstring> )
|
||||||
|
|
||||||
|
Any MAC address to hex representation.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
mac2hex("a1:b2:3:4:e5:f6")
|
||||||
|
returns "a1b20304e5f6".
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub mac2hex($) {
|
||||||
|
my @mac = split(/[\s\.\-:\-]/, shift);
|
||||||
|
return undef if 12 % int(@mac);
|
||||||
|
my $digits = int(12 / int(@mac));
|
||||||
|
my $hex;
|
||||||
|
my $pref = "0" x $digits;
|
||||||
|
foreach (@mac) { $hex .= substr($pref.$_, -$digits) }
|
||||||
|
return lc $hex;
|
||||||
|
};
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
=item X<mac2mac>B<mac2mac> ( I<MACSTRING> )
|
||||||
|
|
||||||
|
Any MAC address to colon-separated hex representation (6 groups of 2 digits).
|
||||||
|
|
||||||
|
Example: mac2mac("a1b2.304.e5f6")
|
||||||
|
returns "a1:b2:03:04:e5:f6"
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub mac2mac($) {
|
||||||
|
hex2mac(mac2hex($_[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
__END__
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 EXAMPLE
|
||||||
|
|
||||||
|
See the L</SYNOPSIS> section.
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<perl(1)|perl>, L<M6::ARP::Sponge(3)|M6::ARP::Sponge>.
|
||||||
|
|
||||||
|
=head1 AUTHORS
|
||||||
|
|
||||||
|
Steven Bakker at AMS-IX (steven.bakker@ams-ix.net).
|
||||||
|
|
||||||
|
=cut
|
28
man/Makefile
Normal file
28
man/Makefile
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#!make
|
||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
# (c) Copyright 2005 Steven Bakker, AMS-IX B.V.
|
||||||
|
#
|
||||||
|
# See the LICENSE file that came with this package.
|
||||||
|
#
|
||||||
|
include ../config.mk
|
||||||
|
|
||||||
|
TOPDIR = ..
|
||||||
|
|
||||||
|
SECTION = 8
|
||||||
|
|
||||||
|
TARGETS = \
|
||||||
|
arpswiffer.pod \
|
||||||
|
arpswiffer.$(SECTION)
|
||||||
|
|
||||||
|
INSTALLFILES = \
|
||||||
|
$(MANDIR)/man$(SECTION)/arpswiffer.$(SECTION)
|
||||||
|
|
||||||
|
include ../rules.mk
|
||||||
|
|
||||||
|
arpswiffer.pod: ../sbin/arpswiffer
|
||||||
|
$(RM) -f arpswiffer.pod
|
||||||
|
ln -s ../sbin/arpswiffer arpswiffer.pod
|
||||||
|
|
||||||
|
# E.O.F. Makefile
|
257
rules.mk
Normal file
257
rules.mk
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
#
|
||||||
|
#!make
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
# Copyright (c) 2002 Steven Bakker
|
||||||
|
# All rights reserved
|
||||||
|
|
||||||
|
default : all
|
||||||
|
|
||||||
|
RM = /bin/rm -f
|
||||||
|
MV = /bin/mv
|
||||||
|
|
||||||
|
RELEASE = 3.3
|
||||||
|
NAME = arpswiffer
|
||||||
|
PACKAGE = $(NAME)-$(RELEASE)
|
||||||
|
TOOLDIR = $(TOPDIR)/tools
|
||||||
|
|
||||||
|
INSTALL_LOG = $(TOPDIR)/installed.log
|
||||||
|
INSTALLPROG = $(TOOLDIR)/bsdinst -c -l $(INSTALL_LOG)
|
||||||
|
INSTALL = $(INSTALLPROG) -o $(OWNER) -g $(GROUP) -m $(MODE)
|
||||||
|
BININSTALL = $(INSTALLPROG) -o $(OWNER) -g $(GROUP) -m $(BINMODE)
|
||||||
|
|
||||||
|
MKDIR = $(TOOLDIR)/mkinstalldirs
|
||||||
|
RMDIR = $(TOOLDIR)/rminstalldirs
|
||||||
|
|
||||||
|
#
|
||||||
|
# Substitute configuration variables in files.
|
||||||
|
#
|
||||||
|
perlit= $(PERL) -p -e \
|
||||||
|
"s!\@LIBDIR@!$(LIBDIR)!g; \
|
||||||
|
s!\@BINDIR@!$(BINDIR)!g; \
|
||||||
|
s!\@DFL_PATH@!$(DFL_PATH)!g; \
|
||||||
|
\
|
||||||
|
s!\@NAME@!$(NAME)!g; \
|
||||||
|
s!\@UNAME@!\U$(NAME)\E!g; \
|
||||||
|
s!\@Uname@!\u$(NAME)!g; \
|
||||||
|
\
|
||||||
|
s!\@OWNER@!$(OWNER)!g; \
|
||||||
|
s!\@GROUP@!$(GROUP)!g; \
|
||||||
|
\
|
||||||
|
s!\@SECTION@!\U$(SECTION)\E!g; \
|
||||||
|
s!\@USECTION@!\U$(SECTION)\E!g; \
|
||||||
|
s!\@FILESECTION@!$(FILESECTION)!g; \
|
||||||
|
s!\@UFILESECTION@!\U$(FILESECTION)\E!g; \
|
||||||
|
\
|
||||||
|
s!\@RELEASE@!$(RELEASE)!g; \
|
||||||
|
s!\@SHELL@!$(SHELL)!g; \
|
||||||
|
s!\@PERL@!$(PERL)!g; \
|
||||||
|
\
|
||||||
|
s!\@SPONGE_VAR@!$(SPONGE_VAR)!g; \
|
||||||
|
s!\@SPONGE_OPTIONS@!$(SPONGE_OPTIONS)!g; \
|
||||||
|
\
|
||||||
|
s!\@IFCONFIG@!$(IFCONFIG)!g; \
|
||||||
|
s!\@DFL_RATE@!$(DFL_RATE)!g; \
|
||||||
|
s!\@DFL_ARP_AGE@!$(DFL_ARP_AGE)!g; \
|
||||||
|
s!\@DFL_QUEUEDEPTH@!$(DFL_QUEUEDEPTH)!g; \
|
||||||
|
s!\@DFL_PENDING@!$(DFL_PENDING)!g; \
|
||||||
|
s!\@DFL_LOGLEVEL@!$(DFL_LOGLEVEL)!g; \
|
||||||
|
"
|
||||||
|
|
||||||
|
.SUFFIXES: .al .pm .pmrsc .pl \
|
||||||
|
.src \
|
||||||
|
.sample \
|
||||||
|
.sh \
|
||||||
|
.txt .ps \
|
||||||
|
.$(SECTION) .pod .man .txt
|
||||||
|
|
||||||
|
% : %.sh Makefile
|
||||||
|
@echo building $@ from $<
|
||||||
|
@$(perlit) $< > $@
|
||||||
|
@chmod 755 $@
|
||||||
|
|
||||||
|
%.sample : %.sample.src Makefile
|
||||||
|
@echo building $@ from $<
|
||||||
|
@$(perlit) $< > $@
|
||||||
|
@chmod 644 $@
|
||||||
|
|
||||||
|
% : %.src Makefile
|
||||||
|
@echo building $@ from $<
|
||||||
|
@$(perlit) $< > $@
|
||||||
|
@chmod 644 $@
|
||||||
|
|
||||||
|
% : %.pl Makefile
|
||||||
|
@ echo building $@ from $<
|
||||||
|
@ $(perlit) $< > $@
|
||||||
|
@ chmod 755 $@
|
||||||
|
@ $(PERL) -wc $@ || $(RM) $@
|
||||||
|
|
||||||
|
%.$(SECTION) : %.pod
|
||||||
|
@echo building $@ from $<
|
||||||
|
@PERLLIB=$$PERLLIB:$(TOPDIR)/lib; export PERLLIB; \
|
||||||
|
pod2man \
|
||||||
|
--release="$(NAME)-$(RELEASE)" \
|
||||||
|
--date="`date`" \
|
||||||
|
--center="AMS-IX Management Utilities" \
|
||||||
|
--section=$(SECTION) \
|
||||||
|
--name="`echo $* | sed -e 's/\.\./::/g'`" \
|
||||||
|
$< > $@
|
||||||
|
|
||||||
|
%.html : %.pod
|
||||||
|
@echo building $@ from $<
|
||||||
|
@PERLLIB=$$PERLLIB:$(TOPDIR)/lib; export PERLLIB; \
|
||||||
|
$(TOOLDIR)/pod2html \
|
||||||
|
--name="`echo $* | sed -e 's/\.\./::/g'`" \
|
||||||
|
$< > $@
|
||||||
|
|
||||||
|
%.txt : %.$(SECTION)
|
||||||
|
@echo building $@ from $<
|
||||||
|
@$(perlit) $< | gnroff -mgan > $@
|
||||||
|
|
||||||
|
%.ps : %.$(SECTION)
|
||||||
|
@echo building $@ from $<
|
||||||
|
@$(perlit) $< | groff -Tps -mgan > $@
|
||||||
|
|
||||||
|
$(INITDIR)/% : %
|
||||||
|
@echo installing executable $< in $(INITDIR)
|
||||||
|
$(MKDIR) $(INITDIR) 2>&1 | sed -e 's/^mkdir //' >> $(INSTALL_LOG)
|
||||||
|
$(BININSTALL) $< $@
|
||||||
|
$(PERL) -pi -e 's|^(#!/.*) -I../lib|$$1|' $@
|
||||||
|
|
||||||
|
$(BINDIR)/% : %
|
||||||
|
@echo installing executable $< in $(BINDIR)
|
||||||
|
$(MKDIR) $(BINDIR) 2>&1 | sed -e 's/^mkdir //' >> $(INSTALL_LOG)
|
||||||
|
$(BININSTALL) $< $@
|
||||||
|
$(PERL) -pi -e 's|^(#!/.*) -I../lib|$$1|' $@
|
||||||
|
|
||||||
|
$(MANDIR)/man$(SECTION)/% : %
|
||||||
|
@echo installing $< in $(MANDIR)/man$(SECTION)
|
||||||
|
@$(MKDIR) $(MANDIR)/man$(SECTION) 2>&1 | sed -e 's/^mkdir //' >> $(INSTALL_LOG)
|
||||||
|
@$(INSTALL) $< $@
|
||||||
|
|
||||||
|
$(INSTDIR1)/% : %
|
||||||
|
@echo installing $< in $(INSTDIR1)
|
||||||
|
@$(MKDIR) $(INSTDIR1) 2>&1 | sed -e 's/^mkdir //' >> $(INSTALL_LOG)
|
||||||
|
@$(INSTALL) $< $@
|
||||||
|
|
||||||
|
$(INSTDIR2)/% : %
|
||||||
|
@echo installing $< in $(INSTDIR2)
|
||||||
|
@$(MKDIR) $(INSTDIR2) 2>&1 | sed -e 's/^mkdir //' >> $(INSTALL_LOG)
|
||||||
|
@$(INSTALL) $< $@
|
||||||
|
|
||||||
|
$(INSTDIR3)/% : %
|
||||||
|
@echo installing $< in $(INSTDIR3)
|
||||||
|
@$(MKDIR) $(INSTDIR3) 2>&1 | sed -e 's/^mkdir //' >> $(INSTALL_LOG)
|
||||||
|
@$(INSTALL) $< $@
|
||||||
|
|
||||||
|
$(INSTALLDIR)/% : %
|
||||||
|
@echo installing $< in $(INSTALLDIR)
|
||||||
|
@$(MKDIR) $(INSTALLDIR) 2>&1 | sed -e 's/^mkdir //' >> $(INSTALL_LOG)
|
||||||
|
@$(INSTALL) $< $@
|
||||||
|
|
||||||
|
%.sample : %
|
||||||
|
@echo building $@ from $<
|
||||||
|
@$(perlit) $< > $@
|
||||||
|
@chmod 644 $@
|
||||||
|
|
||||||
|
auto/$(AUTO)/%/autosplit.ix : $(AUTO)/%.pm
|
||||||
|
@echo autosplit $<;
|
||||||
|
@PERLLIB=$$PERLLIB:$(TOPDIR)/lib; export PERLLIB; \
|
||||||
|
$(TOOLDIR)/autosplit ./auto $<
|
||||||
|
|
||||||
|
auto/$(AUTO1)/%/autosplit.ix : $(AUTO1)/%.pm
|
||||||
|
@echo autosplit $<;
|
||||||
|
@PERLLIB=$$PERLLIB:$(TOPDIR)/lib; export PERLLIB; \
|
||||||
|
$(TOOLDIR)/autosplit ./auto $<
|
||||||
|
|
||||||
|
%-all : ; cd $* ; make ${MFLAGS} all
|
||||||
|
%-install : ; cd $* ; make ${MFLAGS} install
|
||||||
|
%-uninstall : ; cd $* ; make ${MFLAGS} uninstall
|
||||||
|
%-autosplit : ; cd $* ; make ${MFLAGS} autosplit
|
||||||
|
%-clean : ; cd $* ; make ${MFLAGS} clean
|
||||||
|
|
||||||
|
all : $(TARGETS)
|
||||||
|
|
||||||
|
install : all installdirs $(INSTALLFILES) install-links post-install
|
||||||
|
|
||||||
|
installdirs :
|
||||||
|
@echo "Checking/creating installation directories..."
|
||||||
|
@echo $(INSTALLDIRS)
|
||||||
|
@$(MKDIR) $(INSTALLDIRS) 2>&1 | sed -e 's/^mkdir //' >> $(INSTALL_LOG)
|
||||||
|
|
||||||
|
post-install :
|
||||||
|
@$(RM) $(INSTALL_LOG).tmp; \
|
||||||
|
sort -ru $(INSTALL_LOG) > $(INSTALL_LOG).tmp; \
|
||||||
|
$(MV) $(INSTALL_LOG).tmp $(INSTALL_LOG)
|
||||||
|
|
||||||
|
uninstall :
|
||||||
|
@echo "Removing installed files:" ; \
|
||||||
|
files=$(INSTALLFILES); \
|
||||||
|
if [ -f $(INSTALL_LOG) ]; then \
|
||||||
|
files="$$files `cat $(INSTALL_LOG)`"; \
|
||||||
|
fi; \
|
||||||
|
echo '** Warning: will remove the following files:'; \
|
||||||
|
echo $$files | $(PERL) -p -e '\
|
||||||
|
@x = split(" ", $$_); \
|
||||||
|
$$_ = " ".join("\n ", @x)."\n"'; \
|
||||||
|
if [ `echo "\c" | wc -c` -gt 0 ]; then \
|
||||||
|
echo -n "Are you sure [yn] y"; \
|
||||||
|
else \
|
||||||
|
echo "Are you sure [ny] n\c"; \
|
||||||
|
fi; \
|
||||||
|
read ans; \
|
||||||
|
case "$$ans" in \
|
||||||
|
y*|Y*) echo "Removing ..."; \
|
||||||
|
$(RM) $$files >/dev/null 2>&1; \
|
||||||
|
$(RMDIR) $$files >/dev/null 2>&1; \
|
||||||
|
$(RM) $(INSTALL_LOG); \
|
||||||
|
echo "Done"; \
|
||||||
|
true;; \
|
||||||
|
*) false;; \
|
||||||
|
esac
|
||||||
|
|
||||||
|
x-uninstall : ; $(RM) $(INSTALLFILES)
|
||||||
|
|
||||||
|
clean : ; @echo cleaning up
|
||||||
|
@$(RM) $(TARGETS) core 2>/dev/null \
|
||||||
|
$(NAME)_*.deb; \
|
||||||
|
true
|
||||||
|
|
||||||
|
install-links:
|
||||||
|
@for link in ._no $(INSTALLLINKS) $(INSTALLINKS); do \
|
||||||
|
[ $$link = ._no ] && continue; \
|
||||||
|
linkname=`echo $$link | cut -f1 -d:`; \
|
||||||
|
fname=`echo $$link | cut -f2 -d:`; \
|
||||||
|
if [ ! -f $$linkname ] || [ -h $$linkname ]; then \
|
||||||
|
target=`/bin/ls -l $$linkname 2>/dev/null | sed -e 's|^.*-> ||'`; \
|
||||||
|
if [ X$$target != X$$fname ]; then \
|
||||||
|
$(RM) $$linkname; \
|
||||||
|
ln -s $$fname $$linkname; \
|
||||||
|
echo $$linkname; \
|
||||||
|
fi; \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
|
||||||
|
veryclean : clean
|
||||||
|
@if [ -d ./SCCS ]; then sccs clean; fi
|
||||||
|
|
||||||
|
_debtemp := /tmp/deb.$(NAME).$(shell echo $$RANDOM)
|
||||||
|
|
||||||
|
dpkg:
|
||||||
|
mkdir -p $(_debtemp)
|
||||||
|
cp -rp . $(_debtemp)/$(NAME)-$(RELEASE)
|
||||||
|
cd $(_debtemp)/$(NAME)-$(RELEASE); \
|
||||||
|
(fakeroot debian/rules binary || true)
|
||||||
|
ls $(_debtemp)/$(NAME)_*.deb >/dev/null 2>&1; \
|
||||||
|
[ $$? = 0 ] && mv $(_debtemp)/$(NAME)_*.deb .
|
||||||
|
$(RM) -rf $(_debtemp)
|
||||||
|
|
||||||
|
#$(RM) -rf debian *.deb
|
||||||
|
#dh_make --single; \
|
||||||
|
#
|
||||||
|
# %: define.h %.c
|
||||||
|
# $@: target (wonkie)
|
||||||
|
# $^: dependencies (define.h wonkie.c)
|
||||||
|
# $<: primary source file (define.h)
|
||||||
|
# $?: out of date dependency (wonkie.c)
|
||||||
|
# $*: portion that matched the "%" (wonkie)
|
23
sbin/Makefile
Normal file
23
sbin/Makefile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#!make
|
||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
# (c) Copyright 2005 Steven Bakker, AMS-IX B.V.
|
||||||
|
#
|
||||||
|
# See the LICENSE file that came with this package.
|
||||||
|
#
|
||||||
|
include ../config.mk
|
||||||
|
|
||||||
|
TOPDIR = ..
|
||||||
|
|
||||||
|
INSTALLDIRS = $(BINDIR)
|
||||||
|
|
||||||
|
TARGETS = \
|
||||||
|
arpswiffer
|
||||||
|
|
||||||
|
INSTALLFILES = \
|
||||||
|
$(BINDIR)/arpswiffer
|
||||||
|
|
||||||
|
include ../rules.mk
|
||||||
|
|
||||||
|
# E.O.F. Makefile
|
1140
sbin/arpsponge.pl
Normal file
1140
sbin/arpsponge.pl
Normal file
File diff suppressed because it is too large
Load Diff
18
t/dosponge
Normal file
18
t/dosponge
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#IPADDR/PREFIXLEN dev IFNAME
|
||||||
|
|
||||||
|
rm -f swif.out swif.notify swif.status
|
||||||
|
mkfifo swif.notify
|
||||||
|
|
||||||
|
#./arpswiffer 10.1.1.0/25 dev eth0:1 \
|
||||||
|
#../sbin/arpswiffer 193.194.136.128/25 dev eth0 \
|
||||||
|
arpswiffer 193.194.136.128/25 dev eth0 \
|
||||||
|
--verbose=1 \
|
||||||
|
--queuedepth=20 \
|
||||||
|
--sweep=900/3600 \
|
||||||
|
--rate=50 \
|
||||||
|
--pending=3 \
|
||||||
|
--notify=swif.notify \
|
||||||
|
--statusfile=swif.status \
|
||||||
|
2>&1 | es -a -s swif.out &
|
||||||
|
# --dummy \
|
||||||
|
tail -f swif.out
|
146
tools/bsdinst
Executable file
146
tools/bsdinst
Executable file
@ -0,0 +1,146 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
# bsdinst.sh: BSD-like install program
|
||||||
|
#
|
||||||
|
# Not all BSD install options are supported, and one extra is added:
|
||||||
|
#
|
||||||
|
# -l logfile
|
||||||
|
|
||||||
|
prog=`basename $0`
|
||||||
|
|
||||||
|
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/ucb
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "$@" >&2
|
||||||
|
echo "usage: $prog [options] file ... destination" >&2
|
||||||
|
echo "options: [-cs] [-l logfile] [-g group] [-m mode] [-o owner]" >&2
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
verbose=false
|
||||||
|
go_on=true
|
||||||
|
copy=true
|
||||||
|
strip=false
|
||||||
|
|
||||||
|
mode=755
|
||||||
|
logfile=''
|
||||||
|
|
||||||
|
[ `whoami` = root ] && root=true || root=false
|
||||||
|
|
||||||
|
if $root; then
|
||||||
|
owner=root
|
||||||
|
group=staff
|
||||||
|
fi
|
||||||
|
|
||||||
|
while [ $# -gt 0 ] && $go_on
|
||||||
|
do
|
||||||
|
case X:$1 in
|
||||||
|
X:-s) shift
|
||||||
|
strip=true ;;
|
||||||
|
X:-c) shift
|
||||||
|
copy=true ;;
|
||||||
|
X:-cs |\
|
||||||
|
X:-sc) shift
|
||||||
|
copy=true
|
||||||
|
strip=true ;;
|
||||||
|
X:-l) shift
|
||||||
|
logfile=$1
|
||||||
|
shift ;;
|
||||||
|
X:-g) shift
|
||||||
|
if $root; then
|
||||||
|
group=$1
|
||||||
|
elif $verbose; then
|
||||||
|
echo "warning: \"-g\" flag ignored (not super-user)" >&2
|
||||||
|
fi
|
||||||
|
shift ;;
|
||||||
|
X:-m) shift
|
||||||
|
mode=$1
|
||||||
|
shift;;
|
||||||
|
X:-d) usage "\"-d\" flag is not supported (sorry)" ;;
|
||||||
|
X:-v) shift
|
||||||
|
verbose=true ;;
|
||||||
|
X:-o) shift
|
||||||
|
if $root; then
|
||||||
|
owner=$1
|
||||||
|
elif $verbose; then
|
||||||
|
echo "warning: \"-o\" flag ignored (not super-user)" >&2
|
||||||
|
fi
|
||||||
|
shift ;;
|
||||||
|
X:---) shift; go_on=false;;
|
||||||
|
X:-*) usage "unknown option \"$1\"" ;;
|
||||||
|
X:*) go_on=false ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $# -lt 2 ]; then
|
||||||
|
usage "too few arguments"
|
||||||
|
elif [ $# -gt 2 ]; then
|
||||||
|
multiple_src=true
|
||||||
|
else
|
||||||
|
multiple_src=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
while [ $# -gt 1 ]
|
||||||
|
do
|
||||||
|
files="$files $1"
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
destination=$1
|
||||||
|
|
||||||
|
[ -d "$destination" ] && dst_is_a_file=false || dst_is_a_file=true
|
||||||
|
|
||||||
|
if $multiple_src && $dst_is_a_file
|
||||||
|
then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
set $files
|
||||||
|
|
||||||
|
$copy && mvcp=cp || mvcp=mv
|
||||||
|
|
||||||
|
for file
|
||||||
|
do
|
||||||
|
$dst_is_a_file && dst_file=$destination || \
|
||||||
|
dst_file=$destination/`basename $file`
|
||||||
|
|
||||||
|
$verbose && echo + $mvcp $file $dst_file
|
||||||
|
rm -f $dst_file
|
||||||
|
$mvcp $file $dst_file || exit 1
|
||||||
|
|
||||||
|
if [ -n "$logfile" ]; then
|
||||||
|
echo $dst_file >> $logfile
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$mode" ]; then
|
||||||
|
chmod 600 $dst_file
|
||||||
|
$verbose && echo + chmod $mode $dst_file
|
||||||
|
chmod $mode $dst_file > /dev/null 2>&1
|
||||||
|
[ $? = 0 ] || \
|
||||||
|
echo "$dst_file: could not chmod (ignored)" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $strip; then
|
||||||
|
$verbose && echo + strip $dst_file
|
||||||
|
strip $dst_file >/dev/null 2>&1
|
||||||
|
[ $? = 0 ] || \
|
||||||
|
echo "$dst_file: could not strip (ignored)" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$owner" ]; then
|
||||||
|
$verbose && echo + chown $owner $dst_file
|
||||||
|
chown $owner $dst_file >/dev/null 2>&1
|
||||||
|
[ $? = 0 ] || \
|
||||||
|
echo "$dst_file: could not chown (ignored)" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$group" ]; then
|
||||||
|
$verbose && echo + chgrp $group $dst_file
|
||||||
|
chgrp $group $dst_file >/dev/null 2>&1
|
||||||
|
[ $? = 0 ] || \
|
||||||
|
echo "$dst_file: could not chgrp (ignored)" >&2
|
||||||
|
fi
|
||||||
|
done
|
40
tools/mkinstalldirs
Executable file
40
tools/mkinstalldirs
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
# @(#) $Id$
|
||||||
|
#
|
||||||
|
# mkinstalldirs --- make directory hierarchy
|
||||||
|
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
|
||||||
|
# Created: 1993-05-16
|
||||||
|
# LICENSE: Public domain
|
||||||
|
|
||||||
|
errstatus=0
|
||||||
|
|
||||||
|
for file
|
||||||
|
do
|
||||||
|
set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
|
||||||
|
shift
|
||||||
|
|
||||||
|
pathcomp=
|
||||||
|
for d
|
||||||
|
do
|
||||||
|
pathcomp="$pathcomp$d"
|
||||||
|
case "$pathcomp" in
|
||||||
|
-* ) pathcomp=./$pathcomp ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if test ! -d "$pathcomp"; then
|
||||||
|
echo "mkdir $pathcomp" 1>&2
|
||||||
|
|
||||||
|
mkdir "$pathcomp" || lasterr=$?
|
||||||
|
|
||||||
|
if test ! -d "$pathcomp"; then
|
||||||
|
errstatus=$lasterr
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
pathcomp="$pathcomp/"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
exit $errstatus
|
||||||
|
|
||||||
|
# mkinstalldirs ends here
|
75
tools/rminstalldirs
Executable file
75
tools/rminstalldirs
Executable file
@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
# rminstalldirs --- remove directory hierarchy
|
||||||
|
#
|
||||||
|
# Works (almost) similar to "rmdir -ps", except that the whole path
|
||||||
|
# needn't exist. For example,
|
||||||
|
#
|
||||||
|
# rminstalldirs /dir1/dir2/dir3/file1
|
||||||
|
#
|
||||||
|
# - will still remove the whole tree if "file1" does not exist. Of course
|
||||||
|
# it will not remove any directory that is not empty.
|
||||||
|
#
|
||||||
|
# Author: Steven Bakker <steven@monkey-mind.net>
|
||||||
|
# Created: 1998-06-24
|
||||||
|
|
||||||
|
PATH=/sbin:/bin:/usr/bin:/usr/sbin
|
||||||
|
OLDIFS="$IFS"
|
||||||
|
|
||||||
|
do_rmdir() {
|
||||||
|
if [ `echo "$1" | sed -e 's:^/::g'` = "$1" ]
|
||||||
|
then
|
||||||
|
dir="`pwd`/$1"
|
||||||
|
else
|
||||||
|
dir=$1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
|
||||||
|
cd /
|
||||||
|
path=''
|
||||||
|
|
||||||
|
IFS=/
|
||||||
|
set - $dir
|
||||||
|
IFS="$OLDIFS"
|
||||||
|
|
||||||
|
# Go down the path as far as possible, keeping track of
|
||||||
|
# the reverse path back up ($path).
|
||||||
|
for i in $*
|
||||||
|
do
|
||||||
|
[ -d $i ] || break
|
||||||
|
cd $i
|
||||||
|
path="$i $path"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Go up one directory, so we can "rmdir" it...
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Now travel back up the tree, removing subdirs as we go...
|
||||||
|
set - $path
|
||||||
|
goon=true
|
||||||
|
while $goon && [ $# -gt 0 ]
|
||||||
|
do
|
||||||
|
d=$1; shift
|
||||||
|
if [ $d = . -o $d = .. ]
|
||||||
|
then
|
||||||
|
# "." and ".." references in the path act as sentinels.
|
||||||
|
goon=false
|
||||||
|
else
|
||||||
|
if rmdir $d >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
echo rmdir `pwd`/$d
|
||||||
|
cd ..
|
||||||
|
else
|
||||||
|
# Failed "rmdir"; give up.
|
||||||
|
goon=false
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
for dir in "$@"
|
||||||
|
do
|
||||||
|
do_rmdir $dir
|
||||||
|
done
|
Loading…
Reference in New Issue
Block a user