Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Intercepting UDP broadcasts

by Abigail-II (Bishop)
on Jan 30, 2004 at 15:15 UTC ( [id://325248]=perlquestion: print w/replies, xml ) Need Help??

Abigail-II has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to catch UDP broadcasts (this means the receiver address is set to 255.255.255.255), on a specific port. I've the following program:
#!/usr/bin/perl use strict; use warnings; use IO::Socket::INET; my $port = getservbyname 'bootps', 'udp'; my $socket = IO::Socket::INET -> new (LocalPort => $port, Broadcast => 1, Proto => 'udp') or die "Failed to bind to socket: $@"; my $mess; while ($socket -> recv ($mess, 1024)) { print "Saw a bootp request.\n"; } __END__
If I run this program, it creates the socket (the output of netstat -a inet confirms that:
udp 0 0 *:bootps *:*
), but it gets blocked in the recv call - the while body never gets executed. tcpdump however shows that many requests come by.

I've looked in the documentation and on the web, and while I've found examples of UDP servers in Perl, I haven't found an example of a server that listens to broadcasts.

What am I doing wrong here?

Abigail

Replies are listed 'Best First'.
Re: Intercepting UDP broadcasts
by duff (Parson) on Jan 30, 2004 at 17:10 UTC

    This is probably no help whatsoever, but your code works just fine for me. RedHat 9, perl 5.8.0

    Perhaps if recv() is blocking you should use IO::Select? (rampant straw grasping here :)

    #!/usr/bin/perl use strict; use warnings; use IO::Socket::INET; use IO::Select; my $sel = IO::Select->new; my $port = getservbyname 'bootps', 'udp'; my $socket = IO::Socket::INET -> new (LocalPort => $port, Broadcast => 1, Proto => 'udp') or die "Failed to bind to socket: $@"; $sel->add($socket); my $n = 10; my $mess; while (1) { my @r = $sel->can_read($n); unless (@r) { print "Nothing after $n seconds\n"; next; } $socket -> recv ($mess, 1024); print "Saw a bootp request.\n"; } __END__

Re: Intercepting UDP broadcasts
by Fletch (Bishop) on Jan 30, 2004 at 15:34 UTC

    Not a direct solution, but worst comes to worst you could use Net::Pcap (and/or POE::Component::Pcap) to sniff the packets from the wire directly.

Re: Intercepting UDP broadcasts
by monktim (Friar) on Jan 30, 2004 at 17:11 UTC
    I get bootp requests with this code.

    Update I don't know if the additional parameters had anything to do with it. I thought restarting the network printer was sending the msgs but apparently that isn't the case. I just got lucky and caught a few boots.

    FYI: This is perl, v5.8.0 built for MSWin32-x86-multi-thread
    Binary build 806 provided by ActiveState Corp.http://www.ActiveState.com
    Built 00:45:44 Mar 31 2003
    Running on Windows 2000 Workstation.
    @ISA = qw(IO::Socket);
    $VERSION = "1.26";

    Update2: Abigail-II your code works stright up on my machine. I just got to test it now.

    use strict; use warnings; use IO::Socket::INET; my $port = getservbyname 'bootps', 'udp'; my $socket = IO::Socket::INET -> new (LocalPort => $port, Broadcast => 1, Proto => 'udp', ReuseAddr => 1, Type => SOCK_DGRAM) or die "Failed to bind to socket: $@"; my $mess; $socket -> recv ($mess, 1024); if (defined $mess) { print "Saw a bootp request.\n$mess\n"; } else { print "No bootp request.\n"; }

      So in other words the type parameter isn't set by the protocol by default... ooo.. bad doc, baaaad doc. *cackle*

Re: Intercepting UDP broadcasts
by jdtoronto (Prior) on Jan 30, 2004 at 16:08 UTC
    I have never written a 'snooper' type programme in Perl, but I recall that recently I was scanning through a book titled "Programming the Network in Perl" (from a guy in Ireland, not the Lincoln Stein book). I saw some code in there called EtherSnooper. The only thing I could find in a search was this: http://users.telenet.be/jurgen.kobierczynski/support/ethersnooper.pl.txt

    Looking at the source code it would seem that a series of modules in the NetPacket namespace would make it a soda to write something which would acheive the result, but not as elegantly as a direct implementation.

    I don't know if this info will be useful, but I hope so!

    jdtoronto

      I'm not interested in snooping, certainly not on the ethernet level. I want to write a prototype for a special purpose DHCP server. Having the kernel push all ethernet (or all UDP) traffic into userland is not an option.

      But I'll look at the reference.

      Abigail

        Fair enough too. Now you explain what you are doing then I am completely lost - at least in Perl land!

        ...john

Re: Intercepting UDP broadcasts
by Limbic~Region (Chancellor) on Jan 30, 2004 at 16:52 UTC
    Abigail,
    I wasn't going to reply because I have a sum total of 0 experience with socket/network programming. Since other people have wagered guesses and possibilities - I will too:

    From the documentation, which I know you said you read, the example that looks most similar to yours is a bit different:

    $sock = IO::Socket::INET->new ( PeerPort => 9999, PeerAddr => inet_ntoa(INADDR_BROADCAST), Proto => udp, LocalAddr => 'localhost', Broadcast => 1 ) or die "Can't bind : $@\n";
    In particular, the information concerning the peer. I am wondering if setting the PeerAddr to the broadcast does something special even though technically what you have should be picking up from any address. Unfortunately, I do not have the means to test presently.

    Cheers - L~R

      I tried that, and it doesn't make a difference.

      Abigail

Re: Intercepting UDP broadcasts
by sfink (Deacon) on Jan 30, 2004 at 18:10 UTC
    This also works for me on RedHat 7.3 Linux. Here is the relevant portion of the strace output; perhaps it will be useful to compare against on whatever you're running on:
    socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP) = 4 fcntl64(4, F_GETFL) = 0x2 (flags O_RDWR) fstat64(4, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, + 0) = 0x40112000 _llseek(4, 0, 0xbfffe900, SEEK_CUR) = -1 ESPIPE (Illegal seek) fcntl64(4, F_GETFL) = 0x2 (flags O_RDWR) fstat64(4, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, + 0) = 0x40113000 _llseek(4, 0, 0xbfffe900, SEEK_CUR) = -1 ESPIPE (Illegal seek) fcntl64(4, F_SETFD, FD_CLOEXEC) = 0 brk(0x818e000) = 0x818e000 setsockopt(4, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0 bind(4, {sin_family=AF_INET, sin_port=htons(67), sin_addr=inet_addr("0 +.0.0.0")}}, 16) = 0 recvfrom(4, "\1\1\6\0ZeH\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0". +.., 1024, 0, {sin_family=AF_INET, sin_port=htons(68), sin_addr=inet_a +ddr("0.0.0.0")}}, [16]) = 300 fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, + 0) = 0x40114000 write(1, "Saw a bootp request.\n", 21) = 21
Re: Intercepting UDP broadcasts
by coreolyn (Parson) on Jan 30, 2004 at 16:27 UTC

    I think BrowserUK is on the right track. While slightly out my area I was comparing your code to the camel and the thing that struck me as missing was the Listen parameter. Which is defined as the "Queue size for listen" (IO::SOCKET::INET). The type should be set by the protocol, so I doubt that's in your way. I also noticed a reuse parameter was also not present in your code.

    Not sure if it helps but as I've so rarely seen you actually ASK for help I figured I'd give it a shot.

      There is no queue size for UDP. If I add a Listen option to the constructor, it dies:
      Failed to bind to socket: IO::Socket::INET: Operation not supported +at ./server line 9.
      when trying to create the socket.

      Adding a Reuse option doesn't help either.

      Abigail

Re: Intercepting UDP broadcasts
by coreolyn (Parson) on Jan 30, 2004 at 17:16 UTC

    Well that leaves your $socket->recv as the culprit. I'm suspecting your are not getting 1024 bytes due to fragmentation of the packet. This info (Section 5.8) may be of assistance ( long story short a minimum datagram could be as small as 28 bytes ( IP + UDP header )) due to fragmentation.

      The length argument is just the maximum that will be read. It doesn't even work if you set it to 1.

      Abigail

Re: Intercepting UDP broadcasts
by Anonymous Monk on Jan 30, 2004 at 16:33 UTC
    Just a shot in the dark. Would Reuse => 1 help?
Re: Intercepting UDP broadcasts
by Anonymous Monk on Jan 30, 2004 at 16:47 UTC
    Do you have to specify the socket type to be datagram? I'd think that was the default. Type => SOCK_DGRAM
      IO::Socket::INET will use a default type of SOCK_DGRAM for UDP sockets.

      Abigail

Re: Intercepting UDP broadcasts
by Plankton (Vicar) on Jan 30, 2004 at 16:54 UTC
    Have you tried writing a test "broadcaster"? Maybe bootps is doing something unexpected.

    Plankton: 1% Evil, 99% Hot Gas.
Re: Intercepting UDP broadcasts
by Abigail-II (Bishop) on Feb 02, 2004 at 16:41 UTC
    Well, it turned out that RedHat (the distro the box was using) doesn't trust itself to not run servers with security holes, as was blocking all incoming requests on low numbered UDP ports.

    Abigail

      Running into the same issues. How did you turn off redhats security to allow UDP connections on the low ports?
Re: Intercepting UDP broadcasts
by traveler (Parson) on Jan 30, 2004 at 17:20 UTC
    If you are not running this as root, try doing so. I have seen systems where the socket is created, but the data is not delivered to the app unless it is being run by (as) root. What kernel are you using? Also, are there any filters (iptables) blocking the data getting to the port?

    --traveler

      If you run the code as non-root, creating the socket fails. Non-root processes can't bind to ports less than 1024.

      Abigail

        Not to nit-pick, but non root users can bind to ports less than 1024 on Linux if they have the appropriate capability (CAP_NET_BIND_SERVICE) set. I think Solaris has a similar capability, but I don't have the docs here.

        --traveler

Re: Intercepting UDP broadcasts
by samtregar (Abbot) on Jan 30, 2004 at 19:23 UTC
    Do have a firewall on the box you're testing on? That might explain why tcpdump can see the packets, but your code can't.

    -sam

      Nope. No firewall on the box. There's nothing special running on the box - a plain Redhat 7.3 distro.

      I'll have to think it over during the weekend - I won't be back in the office to work on it till Monday.

      Abigail

        By default (unless you asked for "no security" or something similar during the install) RH7.3 sets up an ipchains firewall which will block incoming UDP packets for port <= 1023, among others. Is there any chance that's tripping you up?

        After reading some more 'nix socket threads you might want to try binding to 0.0.0.0 instead of 255.255.255.255

Re: Intercepting UDP broadcasts
by JamesNC (Chaplain) on Jan 31, 2004 at 05:29 UTC
    This worked for me on Win32, AS 5.8:
    use strict; use warnings; use IO::Socket::INET; $|++; my $port = getservbyname 'bootps', 'udp'; print "Listening on UDP port: $port\n"; my $socket = IO::Socket::INET -> new ( LocalPort => $port, Broadcast => 1, Proto => 'udp', Blocking => 1 ) or die "Failed to bind to socket: $@"; my $mess; while ($socket -> recv ($mess, 1024)) { print "Saw: \n$mess\n"; }
    ... To test it, I just made a dhcp request which also uses BOOTP
    c:\> ipconfig /release c:\> ipconfig /renew

    I check to be certain I was on the same lan segment as the other device sending the BOOTP request?
    JamesNC
    Update: I released and renewed the address on a second machine on my home network and indeed saw both the request and response. I am curious as to how to decode the DHCP message. I started to unpack the message, but I got lazy: See RFC 2131 for the message format.
    Here is a link to it: RFC 2131
Re: Intercepting UDP broadcasts
by BrowserUk (Patriarch) on Jan 30, 2004 at 16:09 UTC

    Update: UDP not TCP!

    Don't you need to listen() and accept() before you can recv()? Or is that all taken care of under the covers of IO::Socket?


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    Timing (and a little luck) are everything!

      No, this is a UDP socket. With UDP, you don't have end-to-end connection (like with TCP) - just datagrams being delivered. So there are no listen() or accept() steps involved. In fact, if you include a Listen option in the creation of the socket, the creation will fail.

      Abigail

Re: Intercepting UDP broadcasts
by welchavw (Pilgrim) on Jan 31, 2004 at 15:11 UTC

    This is completely off the top of my head, but I think it has a shot at being helpful. Sometimes NICs have to be placed into "promiscuous" mode to pull packets that aren't addressed to the bound address. It may be that this is the step that is not being performed? Good luck.

    ,welchavw

A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://325248]
Approved by Limbic~Region
Front-paged by broquaint
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (5)
As of 2024-04-19 06:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found