Short spew:

How does one take a netfilter-queued-to-userspace packet and turn it
into a Perl "Net::Frame::Layer::ETH" object?

No matter how I try to call "new(raw => $something)" it seems to ignore
the "raw" attribute and create a default-value'd object.

I did post this over in the Netfilter Users Mailing list but it really
isn't a netfilter question (the nfqueue part of things does work but
it's the Perl part that I'm ignorant about).

This is perhaps my 5th or 6th copy-and-hack perl file so answers such
as "go read such-and-such document/web page" are quite welcome.

#-------------------

Long spew:

#---------

I did indeed manage to get all of this working with Perl "NetPacket::IP"
objects, but apparently that does IPv4 only and not IPv6.

What *does* work:

1) nftables "ct state { new } udp dport { domain } queue num 53 bypass",
2) Perl nfqueue-bindings (https://github.com/chifflier/nfqueue-bindings,
   "libnfqueue-perl" package on Ubuntu),
3) Perl NetPacket::IP (https://metacpan.org/pod/NetPacket::IP,
   "libnetpacket-perl" package on Ubuntu), and
4) Perl Net::DNS::Packet (https://metacpan.org/pod/Net::DNS::Packet,
   "libnet-dns-perl" package on Ubuntu)

together allow me to inspect DNS packets (UDP, so far) and issue
NF_ACCEPT or NF_DROP verdicts as I wish (so I can drop requests for
recursion, or not for my domains, and so forth).

And I can even add the (IPv4) source IPs as elements to an nftables
IP address set, complete with timeouts.

So far, so good.  In fact more like "totally awesome".  It eliminates
a lot of crap coming at me.

Except that "NetPacket::IP" appears to handle IPv4 packets only,
not IPv6.

If someone tells me that "NetPacket::IP" does indeed do IPv6 and points
me at the detail that I'm missing then that would be great.

#---------

So I have tried flipping over to "Net::Frame::Layer::ETH"
(https://metacpan.org/release/Net-Frame, "libnet-frame-perl" on Ubuntu)
in the hopes that it can both tell me if it's an IPv4 or IPv6 packet
and then let me work my way up from there.

What *doesn't* work (following is one of four attempts... scroll down
this email for all four together):

 $layer = Net::Frame::Layer::ETH->new(raw => $payload);

 ... that just seems to cheerfully ignore the "raw" attribute and
produce a default-valued result as described by the documentation.

Following is:

a) apparently-default-result from Net::Frame::Layer::ETH,
b) my Perl code attempt to use Net::Frame::Layer::ETH, and
c) the NetPacket::IP that does work (except doesn't do IPv6)

#---------

Got a layer:
ETH: dst:ff:ff:ff:ff:ff:ff  src:00:00:00:00:00:00  type:0x0800
Got a layer:
ETH: dst:ff:ff:ff:ff:ff:ff  src:00:00:00:00:00:00  type:0x0800
Got a layer:
ETH: dst:ff:ff:ff:ff:ff:ff  src:00:00:00:00:00:00  type:0x0800
Got a layer:
ETH: dst:ff:ff:ff:ff:ff:ff  src:00:00:00:00:00:00  type:0x0800

#---------

#!/usr/bin/perl -w

use strict;
use nfqueue;
use Socket qw(AF_INET AF_INET6);
use Net::Frame::Layer::ETH;

my $q;

sub cleanup() {
 $q->unbind(AF_INET);
 $q->close();
}

sub cb() {
 my $payload;
 my $data;
 my $layer;

 ($payload) = @_;
 if ($payload) {
  $data = $payload->get_data();
  $layer = Net::Frame::Layer::ETH->new(raw => $payload);
  if ($layer) {
   print("Got a layer:\n");
   print($layer->print,"\n");
  };
  $layer = Net::Frame::Layer::ETH->new(raw => $data);
  if ($layer) {
   print("Got a layer:\n");
   print($layer->print,"\n");
  };
  $layer = Net::Frame::Layer::ETH->new(raw => \$payload);
  if ($layer) {
   print("Got a layer:\n");
   print($layer->print,"\n");
  };
  $layer = Net::Frame::Layer::ETH->new(raw => \$data);
  if ($layer) {
   print("Got a layer:\n");
   print($layer->print,"\n");
  };
  print "---\n";
  $payload->set_verdict($nfqueue::NF_ACCEPT);
 }
}

$q = new nfqueue::queue();

$SIG{INT} = "cleanup";

$q->set_callback(\&cb);
$q->fast_open(53, AF_INET);
$q->set_queue_maxlen(5000);
$q->try_run();

#---------

#!/usr/bin/perl -w

use strict;
use nfqueue;
use Socket qw(AF_INET AF_INET6);
use NetPacket::IP qw(IP_PROTO_UDP IP_PROTO_TCP);
use NetPacket::UDP;
use NetPacket::TCP;
use Net::DNS::Packet;

my $q;

sub cleanup() {
 $q->unbind(AF_INET);
 $q->close();
}

sub cb() {
 my $payload;
 my $ip_obj;
 my $udp_obj;
 my $tcp_obj;
 my $data_obj;
 my $dns_obj;
 my $dns_hdr;
 my @dns_qs;
 my $drop = "no";

 ($payload) = @_;
 if ($payload) {
  $ip_obj = NetPacket::IP->decode($payload->get_data());
  if ($ip_obj->{proto}==IP_PROTO_UDP) {
   $udp_obj = NetPacket::UDP->decode($ip_obj->{data});
   $data_obj = $udp_obj->{data};
  }
  if ($ip_obj->{proto}==IP_PROTO_TCP) {
   $tcp_obj = NetPacket::TCP->decode($ip_obj->{data});
   $data_obj = $tcp_obj->{data};
  }
  $dns_obj = Net::DNS::Packet->new(\$data_obj);
  $dns_hdr = $dns_obj->header;
  @dns_qs = $dns_obj->question;
  if ($dns_hdr->rd) {
   $drop = "yes";
  }
  if ($dns_hdr->qdcount == 1) {
   if ($dns_qs[0]->qname !~ /(firstdomain|seconddomain)\.com)$/i) {
    $drop = "yes";
   }
   if ($dns_qs[0]->qclass ne "IN") {
    $drop = "yes";
   }
  }
  if ($drop eq "yes") {
   $payload->set_verdict($nfqueue::NF_DROP);
  } else {
   $payload->set_verdict($nfqueue::NF_ACCEPT);
  }
 }
}

$q = new nfqueue::queue();

$SIG{INT} = "cleanup";

$q->set_callback(\&cb);
$q->fast_open(53, AF_INET);
$q->set_queue_maxlen(5000);
$q->try_run();

#---------

--

 - James

--
To unsubscribe, e-mail: beginners-unsubscr...@perl.org
For additional commands, e-mail: beginners-h...@perl.org
http://learn.perl.org/


Reply via email to