Respostas:
Não, você só pode obter esse lado do servidor de informações do servidor DHCP. Esta informação está contida no arquivo .lease do servidor DHCP:, /var/lib/dhcpd/dhcpd.leases
se você estiver usando o servidor DHCP do ISC.
$ more /var/lib/dhcpd/dhcpd.leases
# All times in this file are in UTC (GMT), not your local timezone. This is
# not a bug, so please don't ask about it. There is no portable way to
# store leases in the local timezone, so please don't request this as a
# feature. If this is inconvenient or confusing to you, we sincerely
# apologize. Seriously, though - don't ask.
# The format of this file is documented in the dhcpd.leases(5) manual page.
# This lease file was written by isc-dhcp-V3.0.5-RedHat
lease 192.168.1.100 {
starts 4 2011/09/22 20:27:28;
ends 1 2011/09/26 20:27:28;
tstp 1 2011/09/26 20:27:28;
binding state free;
hardware ethernet 00:1b:77:93:a1:69;
uid "\001\000\033w\223\241i";
}
...
...
isc-dhcpd
A versão do pacote 4.3.1
possui este comando para listar concessões:
dhcp-lease-list --lease PATH_TO_LEASE_FILE
Este é um script de script perl simples que também suporta versões mais antigas do DHCP. Você pode ver uma cópia no código fonte da Debian ou na distribuição oficial do DHCP (in contrib/
) também.
A saída é bonita:
$ perl contrib/dhcp-lease-list.pl --lease /var/db/dhcpd/dhcpd.leases
To get manufacturer names please download http://standards.ieee.org/regauth/oui/oui.txt to /usr/local/etc/oui.txt
MAC IP hostname valid until manufacturer
===============================================================================================
90:27:e4:f9:9d:d7 192.168.0.182 iMac-de-mac 2015-12-12 01:37:06 -NA-
d8:a2:5e:94:40:81 192.168.0.178 foo-2 2015-12-12 01:04:56 -NA-
e8:9a:8f:6e:0f:60 192.168.0.127 angela 2015-12-11 23:55:32 -NA-
ec:55:f9:c5:f2:55 192.168.0.179 angela 2015-12-11 23:54:56 -NA-
f0:4f:7c:3f:9e:dc 192.168.0.183 kindle-1234567 2015-12-11 23:54:31 -NA-
f4:ec:38:e2:f9:67 192.168.0.185 -NA- 2015-12-11 23:55:40 -NA-
f8:d1:11:b7:5a:62 192.168.0.184 -NA- 2015-12-11 23:57:34 -NA-
É mais bonito se você baixar o oui.txt
arquivo conforme sugerido, mas a saída pode ficar distorcida, a menos que você aplique o seguinte patch:
--- dhcp-lease-list.pl.orig 2015-12-12 12:30:00.000000000 -0500
+++ dhcp-lease-list.pl 2015-12-12 12:54:31.000000000 -0500
@@ -41,7 +41,7 @@
if (defined $oui) {
$manu = join('-', ($_[0] =~ /^(..):(..):(..):/));
$manu = `grep -i '$manu' $oui | cut -f3`;
- chomp($manu);
+ $manu =~ s/^\s+|\s+$//g;
}
return $manu;
@@ -142,7 +142,7 @@
}
foreach (@leases) {
if ($opt_format eq 'human') {
- printf("%-19s%-16s%-15s%-20s%-20s\n",
+ printf("%-19s%-16s%-14.14s %-20s%-20s\n",
$_->{'mac'}, # MAC
$_->{'ip'}, # IP address
$_->{'hostname'}, # hostname
Este patch foi enviado como ISC-Bugs # 41288 e aguarda revisão.
O comando egrep pode ser usado para obter uma saída:
egrep "lease|hostname|hardware|\}" /var/lib/dhcpd/dhcpd.leases
Saída:
lease 192.168.11.10 {
hardware ethernet 20:6a:8a:55:19:0a;
client-hostname "Maryam-PC";
}
lease 192.168.11.7 {
hardware ethernet 00:16:ea:51:d3:12;
client-hostname "parsoon";
}
lease 192.168.11.3 {
hardware ethernet 00:17:c4:3f:84:e3;
client-hostname "zahra-ubuntu";
}
lease 192.168.11.5 {
hardware ethernet 58:b0:35:f1:31:2f;
}
A maioria das respostas acima é parcial. E para ser sincero, não existe uma solução simples. 1) Você pode analisar o arquivo de banco de dados dhcpd.leases e obter informações sobre concessões ativas, mas não obterá informações sobre nenhum endereço FIXO (atribuído por uma linha como:
host switch1 { hardware ethernet a1:b2:c3:d7:2f:bc ; fixed-address switch1.mydomain.com; }
E isso também não fornece informações sobre quando foi a última vez que um dhcp ack foi enviado para a máquina.
2) por outro lado, você pode analisar o arquivo dhcpd.log para procurar linhas de confirmação (elas se parecem com isso):
2017-03-12T08:44:52.421114+01:00, Linuxx, info, dhcpd: DHCPREQUEST for 10.0.0.63 from 68:ab:35:59:9c:a1 via 10.0.0.1
2017-03-12T08:44:52.421174+01:00, Linuxx, info, dhcpd: DHCPACK on 10.0.0.63 to 68:ab:35:59:9c:a1 via 10.0.0.1
Mas o que você realmente deve fazer é fazer AMBOS. Primeiro analise o arquivo de log e, em seguida, atualize o arquivo com as informações obtidas do arquivo dhcpd.leases com o banco de dados para obter informações ausentes, como início e fim da concessão etc.
Agora: joguei por cerca de 2 dias úteis completos até ter criado uma solução que cria uma tabela HTML com TODAS as concessões ativas, FIXAS e dinâmicas. Aqui está o código que você pode colocar na sua pasta cgi-bin ou em qualquer outro lugar.
#!/usr/bin/perl
#####################################################################################
# list dhcpd active leases
# - both "fixed" addresses which are normally not placed into leases database
# - and dynamically given leases which are present in leases DB
# working for isc-dhcpd-server service but should also work for other compatible
# dhcpd servers.
# produces HTML or CSV list of leases
#
# written by Marcin Gosiewski, BV Grupa s.c. Poland <marcin@gosiewski.pl>
# based on portions of code by Jason Antman <jason@jasonantman.com>
#
# to make it work change the $logfilename and $leasedbname below and modify
# the regexp in second part of code (see below) to match your log lines format
# also you can optionally turn off reverse dns lookup (see below) which speeds up the process
# of table creation and is useless unless you have reverse dns populated for
# your fixed or dynamic leases
#
# CHANGELOG:
# 2017-03-13: initial version
use Socket;
use strict;
use warnings;
no warnings 'uninitialized';
# adjust this to match your files location: both log file and leases
# database. We use 2 last log files from logrotate, but you can add as many as you want
my @logfilenames = ( "/var/log/LOCALAPP.dhcpd.log.1", "/var/log/LOCALAPP.dhcpd.log" );
my $leasedbname = "/var/lib/dhcp/dhcpd.leases";
my %data = ();
# optional, can be modified to produce local time
use Time::Local;
use POSIX 'strftime';
my $now = time();
# local variables, lease information stored here
my $ip="";
my $status="";
my $interface="";
my $sdate=""; # beginning of lease
my $stime="";
my $edate=""; # end of lease
my $etime="";
my $adate=""; # last update (ACK) sent to requesting server
my $atime="";
my $mac="";
my $hostname="";
my $dnsname=""; # reverse dns lookup for host
#######################################################################
# first gather data from logfile for all ACK actions
#######################################################################
# collect all lines from log files into memory...
my @lines = (); my @loglines=();
foreach my $logfilename (@logfilenames)
{
open LOGFILE, '<', $logfilename;
chomp(@loglines = <LOGFILE>);
#printf "LINES1: " . scalar @loglines . " in " .$logfilename . "\n";
push(@lines, @loglines);
close(LOGFILE);
}
@loglines=();
#printf "TOTAL LINES: " . scalar @lines . "\n";
foreach my $line (@lines)
{
if ( $line !~ m/dhcpd: DHCPACK/) { next;}
#printf "LINE: $line\n";
###############################
# Modify the following line to make regexp capture 6 groups from log line:
# 1 - date
# 2 - time
# 3 - ip
# 4 - mac
# 5 - hostname if available
# 6 - interface
#$line =~ m/(^.{10})T(.{8}).+,\ dhcpd: DHCPACK on (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) to ((?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}.*) via (.+)/;
$line =~m/(^.{10})T(.{8}).+,\ dhcpd: DHCPACK on (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) to ((?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}) (.*)via (.+)/;
# process the input
$adate="$1";
$atime="$2";
$ip="$3";
$mac="$4";
$hostname="$5";
$interface="$6";
#add some 'known' facts:
$status="ACK";
$sdate=""; #"FOREVER";
$stime="";
$edate="";
$etime="";
#create/update record for this mac_addr
#you can add extra check here if the IP address is not duplicated within
#ack history and choose only the newer one.
$data{"$mac"}->{'ip'} = "$ip";
$data{"$mac"}->{'status'} = "$status";
$data{"$mac"}->{'interface'} = "$interface";
$data{"$mac"}->{'adate'} = "$adate";
$data{"$mac"}->{'atime'} = "$atime";
$data{"$mac"}->{'sdate'} = "$sdate";
$data{"$mac"}->{'stime'} = "$stime";
$data{"$mac"}->{'edate'} = "$edate";
$data{"$mac"}->{'etime'} = "$etime";
$data{"$mac"}->{'mac'} = "$mac";
$data{"$mac"}->{'hostname'} = "$hostname";
}
#close(LOGFILE);
#######################################################################
# gather data from lease database for dynamic addresses
# update the records (for existing) or add new records
#######################################################################
my $isdata = 0;
my $type = "";
#this information is not present in leases database so we just set
#it to default values
$interface="dhcpd";
$status="ACTIVE";
$adate="-";
$atime="";
open LEASEDB, $leasedbname or die $!;
foreach my $line (<LEASEDB>)
{
chomp($line);
$isdata = 1 if $line =~ /^lease /;
$isdata = 0 if $line =~ /^}/;
if ($isdata)
{
if ($line =~ /^lease/)
{
$ip = (split(" ", $line))[1];
}
elsif ($line =~ /^ starts/)
{
($sdate, $stime) = (split(" ", $line))[2,3];
$sdate =~ s/\//-/g;
$stime =~ s/;//;
}
elsif ($line =~ /^ ends/)
{
($type, $edate, $etime) = (split(" ", $line))[1,2,3];
if($type eq "never;")
{
$edate="forever";
$etime=" ";
}
else
{
$edate =~ s/\//-/g;
$etime =~ s/;//;
}
}
elsif ($line =~ /^ hardware ethernet/)
{
$mac = (split(" ", $line))[2];
$mac =~ s/;//;
}
elsif ($line =~ /^ client-hostname/)
{
$hostname = (split(/\"/, $line))[1];
}
elsif($mac ne "")
{
#we have parsed the whole record, no more matching entries
#data is collected to variables. now push the record.
#now let's decide if we are updating the record or creating
#new record
# check against lease date, do not add expired leases
# convert lease end time to local time/date and compare with $now
my $y=0; my $m=0; my $d=0; my $H=0; my $M=0; my $S=0;
my $edatetime = $now;
($y, $m, $d) = split("-", $edate);
($H, $M, $S) = split(":", $etime);
$edatetime = timelocal($S,$M,$H,$d,$m-1,$y);
if($edatetime >= $now)
{
# now check if record exists
if(!defined($data{"$mac"}->{'mac'}))
{
#record does not exist, fill up default data
$data{"$mac"}->{'mac'} = "$mac";
$data{"$mac"}->{'interface'} = "$interface";
$data{"$mac"}->{'ip'} = "$ip";
$data{"$mac"}->{'hostname'} = "$hostname";
}
# record exists, let's check if we should update
$data{"$mac"}->{'status'} = "$status";
$data{"$mac"}->{'sdate'} = "$sdate";
$data{"$mac"}->{'stime'} = "$stime";
$data{"$mac"}->{'edate'} = "$edate";
$data{"$mac"}->{'etime'} = "$etime";
$data{"$mac"}->{'hostname'} = "$hostname";
#we do NOT update ACK time because we do not have it
#do NOT uncomment below
#$data{"$mac"}->{'adate'} = "$adate";
#$data{"$mac"}->{'atime'} = "$atime";
}
}
}
}
close(LEASEDB);
#######################################################################
# sort data
#######################################################################
#we sort by IP but you can sort by anything.
my @sorted = sort { ($data{$a}{'ip'}) cmp ($data{$b}{'ip'}) } %data;
#######################################################################
# Print out everything to the HTML table
#######################################################################
my $hostnamelong="";
printf "Content-type: text/html\n\n";
printf "<html><head><title>Aktywne dzierzawy DHCP</title></head>\n";
printf "<style> table, th, td { border: 1px solid lightgray; border-collapse: collapse; padding: 3px; } ";
printf "tr:nth-child(even) { background-color: #dddddd; } ";
printf "</style>\n";
printf "<body>\n";
printf "<table border='1' cellpadding='6'>\n";
printf "<tr><th>IP</th><th>Status</th><th>Interface</th><th>Lease time</th><th>ACK time</th><th>Mac</th><th>Host</th></tr>\n";
foreach my $key (@sorted) {
if($data{$key}{'mac'} eq "") { next ; }
# BEGIN reverse dns lookup
# can optionally turn off reverse dns lookup (comment out below lines) which speeds up the process
# of table creation and is useless unless you have reverse dns populated for
# your fixed or dynamic leases uncomment single line below instead:
#
# version without reverse dns lookup:
# $hostnamelong = $data{$key}{'hostname'};
#
# version with reverse dns lookup:
# BEGIN
$dnsname = gethostbyaddr(inet_aton($data{$key}{'ip'}), AF_INET);
if($data{$key}{'hostname'} ne "")
{
$hostnamelong = $data{$key}{'hostname'} . " | " . $dnsname;
}
else
{
$hostnamelong = $dnsname;
}
$dnsname = "";
# END
printf "<tr>";
printf "<td>" . $data{$key}{'ip'} ."</td>";
printf "<td>" . $data{$key}{'status'} ."</td>";
printf "<td>" . $data{$key}{'interface'} ."</td>";
printf "<td>" . $data{$key}{'sdate'} . " " . $data{$key}{'stime'} ." - ";
printf $data{$key}{'edate'} . " " . $data{$key}{'etime'} ."</td>";
printf "<td>" . $data{$key}{'adate'} . " " . $data{$key}{'atime'} . "</td>";
printf "<td>" . $data{$key}{'mac'} ."</td>";
printf "<td>" . $hostnamelong ."</td>";
printf "</tr>\n";
}
printf "</table>\n";
printf "</body></html>\n";
# END of programm
Observe que: 1) o script acima precisa de pequenas modificações antes de executar no SEU ambiente, você precisa modificar os locais dos arquivos e uma regex, dependendo do formato do arquivo de log. Veja o comentário no script. 2) o script acima não está verificando se o IP não se repete na tabela ACK, se duas máquinas diferentes obtiveram o mesmo endereço nos últimos dias. Isso ocorre por design (o que eu pessoalmente precisava para ver cada endereço MAC que estava presente na minha rede nos últimos dias) - você pode modificá-lo facilmente, há uma seção pronta para isso no código, basta adicionar uma condição.
Espero que você goste.
O formato dos arquivos de concessão foi alterado ou, pelo menos, é diferente ao usar dhcpcd5
. Para visualizar o contrato de arrendamento que tem no wlan0
para a rede Wi-Fi MyNetwork
, você tem que olhar para esta imagem (ou algo parecido): /var/lib/dhcpcd5/dhcpcd-wlan0-MyNetwork.lease
.
Esse arquivo é um arquivo binário. (Por quê? Eu não sei. Talvez para salvar alguns preciosos ciclos da CPU ao analisá-lo? Blech.) Para visualizá-lo, use o dhcpcd --dumplease
, que analisa o binário do STDIN e gera uma versão legível por humanos:
cat /var/lib/dhcpcd5/dhcpcd-wlan0-MyNetwork.lease | dhcpcd --dumplease
Por outro lado, se você quiser apenas ver qual é o contrato atual atribuído wlan0
, você pode simplesmente fazer:
dhcpcd --dumplease wlan0
Na verdade, eu escrevi algo no bash para tentar conseguir isso. Ele grava todos os endereços IP no mesmo arquivo de nome; portanto, se outro aparecer novamente, substituirá o arquivo anterior; portanto, não haverá duplicatas. Ele também usará o oui.txt para encontrar o fabricante do endereço MAC em questão.
Veja se você pode usá-lo.
#!/bin/bash
pid=$$
while read i
do
echo $i | egrep -qi '^lease|hardware|starts|ends|hostname' || continue
if [[ $i =~ [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} ]]; then
ip=$(echo $i | awk '{print $2}')
printf "IP Address: " > /root/$ip.$pid
echo $ip >> /root/$ip.$pid
elif [[ $i =~ starts ]]; then
printf "\tLease start: " >> /root/$ip.$pid
echo $i | awk '{print $3,$4}' | tr -d ';' >> /root/$ip.$pid
elif [[ $i =~ ends ]]; then
printf "\tLease ends: " >> /root/$ip.$pid
echo $i | awk '{print $3,$4}' | tr -d ';' >> /root/$ip.$pid
elif [[ $i =~ ethernet ]]; then
mac=$(echo -n $i | awk '{print $3}' | tr -d ';')
oui=$(echo -n $mac | tr ':' '-' | cut -c1-8)
printf "\tMAC Address: " >> /root/$ip.$pid
echo $mac >> /root/$ip.$pid
printf "\tManufacturer: " >> /root/$ip.$pid
grep -i $oui /usr/share/hwdata/oui.txt | awk '{print $3,$4,$5,$6}' >> /root/$ip.$pid
printf "\n" >> /root/$ip.$pid
elif [[ $i =~ hostname ]]; then
printf "\tHostname: " >> /root/$ip.$pid
echo $i | awk '{print $2}' | tr -d ';' >> /root/$ip.$pid
fi
done < /var/lib/dhcpd/dhcpd.leases
cat /root/*.$pid
echo "Total leased: $(ls -l /root/*.$pid | wc -l)"
rm -rf /root/*.$pid
/var/lib/dhcp/dhcpd.leases
(ou seja, nãod
no final do primeirodhcp
...)