Date: Mon, 19 Nov 2007 22:05:48 +0200 From: Cristian KLEIN Subject: dhcp_probe on FreeBSD The attached patch works like this. Detecting FreeBSD is done from configure, which #defines FreeBSD. Then, the code that changes is #ifdefed. Except if compiled on FreeBSD, no code has changed. I also added a doc/FreeBSD folder, where a startup script lies and the FreeBSD adapted notification scripts. For some reason, my patch is unable to create this folder, so please take this into account when applying the patch. --------------------------------------------------------------------------- diff -urN dhcp_probe-1.2.0.orig/config.h.in dhcp_probe-1.2.0/config.h.in --- dhcp_probe-1.2.0.orig/config.h.in Wed Mar 14 16:25:47 2007 +++ dhcp_probe-1.2.0/config.h.in Mon Nov 19 21:21:36 2007 @@ -1,5 +1,8 @@ /* config.h.in. Generated from configure.ac by autoheader. */ +/* Compiling on FreeBSD */ +#undef FreeBSD + /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_INET_H diff -urN dhcp_probe-1.2.0.orig/configure dhcp_probe-1.2.0/configure --- dhcp_probe-1.2.0.orig/configure Wed Mar 14 16:24:26 2007 +++ dhcp_probe-1.2.0/configure Mon Nov 19 21:21:37 2007 @@ -3648,6 +3648,13 @@ fi ;; +*freebsd*) + +cat >>confdefs.h <<\_ACEOF +#define FreeBSD 1 +_ACEOF + + ;; esac @@ -6405,8 +6412,8 @@ fi - CPPFLAGS="$CPPFLAGS -I${IST_REQUIRE_LIBNET_INCLUDE}" - LDFLAGS="$LDFLAGS -L${IST_REQUIRE_LIBNET_LIB}" + CPPFLAGS="$CPPFLAGS -I${IST_REQUIRE_LIBNET_INCLUDE} -I/usr/local/include/libnet11" + LDFLAGS="$LDFLAGS -L${IST_REQUIRE_LIBNET_LIB} -L/usr/local/lib/libnet11" for ac_header in libnet.h diff -urN dhcp_probe-1.2.0.orig/configure.ac dhcp_probe-1.2.0/configure.ac --- dhcp_probe-1.2.0.orig/configure.ac Wed Mar 14 16:24:03 2007 +++ dhcp_probe-1.2.0/configure.ac Mon Nov 19 21:21:12 2007 @@ -23,6 +23,9 @@ AC_CHECK_LIB(socket, socket, , AC_MSG_ERROR(Can't find libsocket.)) AC_CHECK_LIB(nsl, inet_ntoa, , AC_MSG_ERROR(Can't find libnsl.)) ;; +*freebsd*) + AC_DEFINE([FreeBSD], [1], [Compiling on FreeBSD]) + ;; esac dnl ########################################################################## diff -urN dhcp_probe-1.2.0.orig/doc/FreeBSD/README dhcp_probe-1.2.0/doc/FreeBSD/README --- dhcp_probe-1.2.0.orig/doc/FreeBSD/README Thu Jan 1 02:00:00 1970 +++ dhcp_probe-1.2.0/doc/FreeBSD/README Mon Nov 19 21:41:07 2007 @@ -0,0 +1,13 @@ +dhcp_probe.sh is a rcNG script. Place it in /usr/local/etc/rc.d/ then enable it in /etc/rc.conf: + +dhcp_probe_enable="YES" +dhcp_probe_flags="" + +The other files are handy notification scripts. Place them in /usr/local/lib/dhcp_probe. They work +perfectly with mailwrapper(8). + +In order to have manpages installed correctly, do like this: + +./configure --mandir=/usr/local/man +make +make install diff -urN dhcp_probe-1.2.0.orig/doc/FreeBSD/dhcp_probe.sh dhcp_probe-1.2.0/doc/FreeBSD/dhcp_probe.sh --- dhcp_probe-1.2.0.orig/doc/FreeBSD/dhcp_probe.sh Thu Jan 1 02:00:00 1970 +++ dhcp_probe-1.2.0/doc/FreeBSD/dhcp_probe.sh Sun Nov 18 23:52:44 2007 @@ -0,0 +1,17 @@ +#!/bin/sh + +# PROVIDE: dhcp_probe +# REQUIRE: LOGIN + +. /etc/rc.subr + +name="dhcp_probe" +rcvar=`set_rcvar` + +load_rc_config $name + +: ${dhcp_probe__enable="NO"} + +command=/usr/local/sbin/dhcp_probe + +run_rc_command "$1" diff -urN dhcp_probe-1.2.0.orig/doc/FreeBSD/dhcp_probe_notify dhcp_probe-1.2.0/doc/FreeBSD/dhcp_probe_notify --- dhcp_probe-1.2.0.orig/doc/FreeBSD/dhcp_probe_notify Thu Jan 1 02:00:00 1970 +++ dhcp_probe-1.2.0/doc/FreeBSD/dhcp_probe_notify Mon Nov 19 21:37:27 2007 @@ -0,0 +1,115 @@ +#!/usr/local/bin/perl -w + +# dhcp_probe_notify calling_prog_name interface_name IPaddress MACaddress +# +# External program called by dhcp_probe upon response of a response from an unexpected BootP/DHCP server. +# +# Called via specification of 'alert_program_name' in /etc/dhcp_probe.cf file. +# +# Arguments: +# calling_prog_name the name of the calling program (e.g. 'dhcp_probe') +# interface_name the name of the interface on which the unexpected response packet arrived (e.g. 'qfe0') +# IPaddress the IP source address of the unexpected response packet (e.g. '192.168.0.1') +# MACaddress the Ethernet source address of the unexpected response packet (e.g. '0:1:2:3:4:5') +# +# May send email subject to throttling. +# May send page subject to throttling. +# +# You will need to edit the definitions below. +# +# Irwin Tillman + + +use Sys::Hostname; +use Sys::Syslog qw(:DEFAULT setlogsock); +use Time::HiRes qw(gettimeofday); # from CPAN +use strict; + + +my $SYSLOG_FACILITY="daemon"; # name of facility to use if syslogging +my $SYSLOG_OPT = 'pid,cons'; # syslog options to use if syslogging, ignored otherwise + +use vars qw($VERBOSE); +$VERBOSE = 1; # produce more verbose messages + +# we may send one piece of mail this way, subject to frequency throttling. +# Use these for regular email (not a page). +use vars qw($THROTTLE_MAIL_CMD $THROTTLE_MAIL_TIMEOUT $THROTTLE_MAIL_FROM $THROTTLE_MAIL_RECIPIENT $THROTTLE_MAIL_RECIPIENT_TEST $THROTTLE_MAIL_SUBJECT); +$THROTTLE_MAIL_CMD = "/usr/local/lib/dhcp_probe/mail-throttled"; # set to "" to disable +$THROTTLE_MAIL_TIMEOUT = 600; # seconds +$THROTTLE_MAIL_FROM = "root"; +$THROTTLE_MAIL_RECIPIENT = "root"; +$THROTTLE_MAIL_SUBJECT = "unexpected BootP/DHCP server"; + + +# we may also send another piece of email this way, subject to frequency throttling. +# Use these for paging. +use vars qw($THROTTLE_PAGE_CMD $THROTTLE_PAGE_TIMEOUT $THROTTLE_PAGE_RECIPIENT); +$THROTTLE_PAGE_CMD = ""; # set to "" to disable +$THROTTLE_PAGE_TIMEOUT = 600; # seconds +$THROTTLE_PAGE_RECIPIENT = "root someoneelse\@example.com"; + + +# You shouldn't need to edit anything below + + +(my $prog = $0) =~ s/.*\///; + +# init our use of syslog +# setlogsock('unix'); # talk to syslog with UNIX domain socket, not INET domain. XXX causes failure in Solaris 7 +openlog($prog, $SYSLOG_OPT, $SYSLOG_FACILITY); + +my $hostname = hostname(); + +my ($calling_program, $ifname, $ip_src, $ether_src) = @ARGV; + +my ($seconds, $microseconds) = gettimeofday; # from Time::HiRes on CPAN +my $timestamp = scalar(localtime($seconds)); +$timestamp =~ s/(\d\d:\d\d:\d\d)/$1.$microseconds/; # glue microsends to end of seconds + + +if ($THROTTLE_MAIL_CMD) { + # This command suppresses the message if it's sent a message to 'key' within 'throttle_seconds' + # I use the calling program's name and the offender's hardware address as the key. + + open(THROTTLE_MAIL, "| $THROTTLE_MAIL_CMD -k ${calling_program}_mail_$ether_src -t $THROTTLE_MAIL_TIMEOUT -f \"$THROTTLE_MAIL_FROM\" -r \"$THROTTLE_MAIL_RECIPIENT\" -s\"$THROTTLE_MAIL_SUBJECT (MAC=${ether_src}, IP=${ip_src})\"") + or my_message('LOG_ERR', "${prog}: can't execute '${THROTTLE_MAIL_CMD}': $!"), exit 1; + + print THROTTLE_MAIL + $timestamp, "\n", + "\n", + "$calling_program detected an unexpected BootP/DHCP server.\n", + "$hostname interface=${ifname}, IP source=${ip_src}, Ethernet source=${ether_src}\n"; + print THROTTLE_MAIL "\nThis means there *is* a rogue BootP/DHCP server operating.\n" if $VERBOSE; + + close(THROTTLE_MAIL) or my_message('LOG_ERR', "${prog}: can't execute '${THROTTLE_MAIL_CMD}': $!"), exit 1; +} + + + + +if ($THROTTLE_PAGE_CMD) { + # This command suppresses the message if it's sent a message to 'key' within 'throttle_seconds' + # I use the calling program's name and the offender's hardware address as the key. + + open(THROTTLE_PAGE, "| $THROTTLE_PAGE_CMD -k ${calling_program}_page_$ether_src -t $THROTTLE_PAGE_TIMEOUT -r \"$THROTTLE_PAGE_RECIPIENT\"") + or die "${prog}: can't execute '${THROTTLE_PAGE_CMD}': $!\n"; + + print THROTTLE_PAGE "rogue DHCP server IP=$ip_src MAC=$ether_src seen via $hostname interface $ifname\n"; + print THROTTLE_PAGE "This means there *is* a rogue BootP/DHCP server operating.\n" if $VERBOSE; + + close(THROTTLE_PAGE) or die "${prog}: can't execute '${THROTTLE_PAGE_CMD}': $!\n"; +} + +exit 0; + + +sub my_message { + # Call with a syslog priority constant and a message string. + # We write the message to syslog, using the specified priority. + # Your message should not contain a newline. + + my($priority, $msg) = @_; + syslog($priority, $msg); + return; +} diff -urN dhcp_probe-1.2.0.orig/doc/FreeBSD/mail-throttled dhcp_probe-1.2.0/doc/FreeBSD/mail-throttled --- dhcp_probe-1.2.0.orig/doc/FreeBSD/mail-throttled Thu Jan 1 02:00:00 1970 +++ dhcp_probe-1.2.0/doc/FreeBSD/mail-throttled Mon Nov 19 21:32:27 2007 @@ -0,0 +1,91 @@ +#!/usr/local/bin/perl5 -w + +# $Header: /usr/local/etc/RCS/mail-throttled,v 1.5 2003/05/20 03:15:42 root Exp $ + +# mail-throttled [-D dbm_file] -k key -t throttle_seconds [-f from] -r recipient [-d] [-s subject] +# +# Sends mail body (read from STDIN) to 'recipient', but avoids doing so "too frequently." +# +# You provide a 'key', which is an arbitrary string used to identify this notification. +# You also provide 'throttle_seconds', an integer. If we've sent anything that +# specified this 'key' within the last 'throttle_seconds', we do not send the message. +# Otherwise, we send the message, and the remember that we've sent a message for this 'key' +# at the current time. +# +# This key/timesent tuples are stored on-disk, in a dbm. As a result, the 'key' +# you supply must satisfy the syntactic requirements for dbm keys. We never +# clean this dbm. You can safely erase it entirely, if you don't mind losing +# the state. +# +# The 'recipient' should be a valid email address. Naturally, it should not +# be one that will cause any ack or bounce mail to return to us! +# If there are several addresses (delimited by spaces), be sure to quote them as a single arg. +# +# If a subject is specified, be sure to quote it if it contains any spaces or other shell +# metachars. +# +# Irwin Tillman + +use Getopt::Std; +use strict; + +use vars qw($DBM_FILE_DEFAULT $MAILCMD $MAILCMD_OPTS $FROM_DEFAULT); +$DBM_FILE_DEFAULT = '/var/db/mail-throttled'; +$MAILCMD = '/usr/sbin/sendmail'; +# $MAILCMD_OPTS = "-t -ODeliveryMode=queueonly"; +$MAILCMD_OPTS = "-t"; +$FROM_DEFAULT = "root"; + +(my $prog = $0) =~ s/.*\///; + +use vars qw($opt_f $opt_D $opt_d $opt_k $opt_r $opt_t $opt_s); # placate use strict +&getopts('dD:f:k:r:s:t:'); + +my $debug = $opt_d || ""; +my $dbm_file = $opt_D || $DBM_FILE_DEFAULT; +my $key = $opt_k || ""; +my $from = $opt_f || $FROM_DEFAULT; +my $recipient = $opt_r || ""; +my $throttle_secs = $opt_t || 1; +my $subject = $opt_s || ""; +print STDERR "${prog}:\nkey=$key\nthrottle_secs=$throttle_secs\nfrom=$from\nrecipient=$recipient\nsubject=$subject\n" if $debug; + +&Usage() unless ($key && $throttle_secs && $recipient); + +my %last_sent = (); +dbmopen(%last_sent, $dbm_file, 0644) or die "${prog}: dbmopen(${dbm_file}): $!\n"; + +my @mailbody = ""; +@mailbody = ; # read it even if we decide not to send it + +my $now = time; + +print STDERR "now = $now\n" if $debug; + +$last_sent{$key} = 0 unless defined($last_sent{$key}); # so its defined before we use it in subtraction (placate use strict) + +if ($now - $last_sent{$key} >= $throttle_secs) { + print STDERR "last_sent = $last_sent{$key}, will send\n" if $debug; + open(MAIL, "| $MAILCMD $MAILCMD_OPTS -f\"$from\"") or die "${prog}: can't execute '${MAILCMD}': $!\n"; + print MAIL "From: $from\n", + "To: $recipient\n", + ($subject ? "Subject: $subject\n" : "") , + "\n", + @mailbody; + close(MAIL) or die "${prog}: can't execute '${MAILCMD}': $!\n"; + + $last_sent{$key} = $now; +} else { + print STDERR "last_sent = $last_sent{$key}, suppressing\n" if $debug; +} + +dbmclose %last_sent; + +exit 0; + + + +sub Usage { + print STDERR "Usage: $prog [-D dbm_file] -k key -t throttle_seconds [-f from] -r recipient [-s subject]\n"; + exit 1; +} diff -urN dhcp_probe-1.2.0.orig/m4/ist_require_libnet.m4 dhcp_probe-1.2.0/m4/ist_require_libnet.m4 --- dhcp_probe-1.2.0.orig/m4/ist_require_libnet.m4 Thu Nov 4 23:02:16 2004 +++ dhcp_probe-1.2.0/m4/ist_require_libnet.m4 Mon Nov 19 21:21:12 2007 @@ -65,8 +65,8 @@ ) dnl Modify CPPFLAGS and LDFLAGS to add dirs needed to find libnet header and library. - CPPFLAGS="$CPPFLAGS -I${IST_REQUIRE_LIBNET_INCLUDE}" - LDFLAGS="$LDFLAGS -L${IST_REQUIRE_LIBNET_LIB}" + CPPFLAGS="$CPPFLAGS -I${IST_REQUIRE_LIBNET_INCLUDE} -I/usr/local/include/libnet11" + LDFLAGS="$LDFLAGS -L${IST_REQUIRE_LIBNET_LIB} -L/usr/local/lib/libnet11" dnl we don't add -lnet to LIBS here, since ac_check_lib does that later dnl LIBS="$LIBS -lnet" diff -urN dhcp_probe-1.2.0.orig/src/bootp.c dhcp_probe-1.2.0/src/bootp.c --- dhcp_probe-1.2.0.orig/src/bootp.c Thu Sep 22 01:13:32 2005 +++ dhcp_probe-1.2.0/src/bootp.c Mon Nov 19 21:21:12 2007 @@ -251,7 +251,11 @@ /* build the Ethernet 802.1Q header */ if (libnet_build_802_1q( ether_bcast_eaddr, /* ether_dst */ +#ifdef FreeBSD + GetEther_src()->octet, /* ether_src */ +#else GetEther_src()->ether_addr_octet, /* ether_src */ +#endif ETHERTYPE_VLAN, /* TPI */ VLAN_PRIORITY, /* priority (0-7) */ VLAN_CFI_FLAG, /* CFI flag */ @@ -269,7 +273,11 @@ /* build the Ethernet header */ if (libnet_build_ethernet( ether_bcast_eaddr, /* ether_dst */ +#ifdef FreeBSD + GetEther_src()->octet, /* ether_src */ +#else GetEther_src()->ether_addr_octet, /* ether_src */ +#endif ETHERTYPE_IP, /* ethertype */ NULL, 0, /* no optional payload */ l, /* libnet context */ diff -urN dhcp_probe-1.2.0.orig/src/defaults.h dhcp_probe-1.2.0/src/defaults.h --- dhcp_probe-1.2.0.orig/src/defaults.h Sun Jan 21 00:27:33 2001 +++ dhcp_probe-1.2.0/src/defaults.h Mon Nov 19 21:21:12 2007 @@ -61,10 +61,18 @@ /* override each of them from the commandline. */ /* Absolute path to pidfile */ +#ifdef FreeBSD +#define PID_FILE "/var/run/dhcp_probe.pid" +#else #define PID_FILE "/etc/dhcp_probe.pid" +#endif /* Absolute path to config file */ +#ifdef FreeBSD +#define CONFIG_FILE "/usr/local/etc/dhcp_probe.cf" +#else #define CONFIG_FILE "/etc/dhcp_probe.cf" +#endif /* Absolute path to current working directory */ #define CWD "/" diff -urN dhcp_probe-1.2.0.orig/src/defs.h dhcp_probe-1.2.0/src/defs.h --- dhcp_probe-1.2.0.orig/src/defs.h Wed Nov 22 23:13:47 2006 +++ dhcp_probe-1.2.0/src/defs.h Mon Nov 19 21:21:12 2007 @@ -114,6 +114,8 @@ #define PASTE(a,b) a ## b +#ifndef FreeBSD + /* Prototypes for these routines are missing from some systems. */ #ifndef HAVE_ETHER_NTOA_PROTO char *ether_ntoa (struct ether_addr *e); @@ -128,5 +130,9 @@ int ether_hostton (char *hostname, struct ether_addr *e); #endif +#else +#include +#include +#endif #endif /* not DEFS_H */ diff -urN dhcp_probe-1.2.0.orig/src/get_myeaddr.c dhcp_probe-1.2.0/src/get_myeaddr.c --- dhcp_probe-1.2.0.orig/src/get_myeaddr.c Fri Oct 29 21:11:09 2004 +++ dhcp_probe-1.2.0/src/get_myeaddr.c Mon Nov 19 21:21:12 2007 @@ -33,6 +33,74 @@ #include "get_myeaddr.h" #include "report.h" +#ifdef FreeBSD + +#include +#include +#include +#include + +int +get_myeaddr(int sockfd, struct in_addr *my_ipaddr, struct ether_addr *my_eaddr, const char *ifname) +{ + /* The following is a "copy/paste" from ifconfig(8) */ + int ifindex; + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + char *buf = 0; + size_t needed; + int mib[6]; + + /* Get Interface index */ + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + goto fail; + + /* Prepare sysctl MIB */ + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_LINK; + mib[4] = NET_RT_IFLIST; + mib[5] = ifindex; + + /* Compute the size of the buffer */ + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + goto fail; + /* Allocate buffer */ + if ((buf = malloc(needed)) == NULL) + goto fail; + /* Get results */ + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + goto fail; + + /* Parse response */ + ifm = (struct if_msghdr *)buf; + if (ifm->ifm_type == RTM_IFINFO) { + if (ifm->ifm_data.ifi_datalen == 0) + ifm->ifm_data.ifi_datalen = sizeof(struct if_data); + sdl = (struct sockaddr_dl *)((char *)ifm + sizeof(struct if_msghdr) - + sizeof(struct if_data) + ifm->ifm_data.ifi_datalen); + } else { + goto fail; + } + + /* Extract ethernet address */ + if (sdl->sdl_alen > 0) { + if (sdl->sdl_type == IFT_ETHER && + sdl->sdl_alen == ETHER_ADDR_LEN) + bcopy(LLADDR(sdl), my_eaddr, sizeof(struct ether_addr)); + } + + free(buf); + return 0; +fail: + if (buf) + free(buf); + return -1; +} + +#else /* FreeBSD */ int get_myeaddr(int sockfd, struct in_addr *my_ipaddr, struct ether_addr *my_eaddr, const char *ifname) @@ -104,3 +172,5 @@ bcopy(arpreq.arp_ha.sa_data, my_eaddr, sizeof (struct ether_addr)); return(0); /* success */ } + +#endif /* FreeBSD */ diff -urN dhcp_probe-1.2.0.orig/src/get_myipaddr.c dhcp_probe-1.2.0/src/get_myipaddr.c --- dhcp_probe-1.2.0.orig/src/get_myipaddr.c Thu Feb 15 18:08:32 2001 +++ dhcp_probe-1.2.0/src/get_myipaddr.c Mon Nov 19 21:21:12 2007 @@ -29,6 +29,11 @@ #include "report.h" #include "utils.h" +#ifdef FreeBSD +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#endif int get_myipaddr(int sockfd, char *ifname, struct in_addr *my_ipaddr)