What do I pass to setsockopt() to get my program to join a muticast group?

I've got a program that sends messages to a multicast address, and I
want to create a "listener" program to read those messages.

I'm using multicast so I can put just one packet on the network, and
be able to receive it from one or more listening workstations.

I'm not quite sure what to pass to getsockopt().
In a c example I've got they pass a pointer to a struct ip_mreq.
Can it be done in Perl?

Here's what I've got so far.

----------------------------------
#!/usr/local/bin/perl
use strict;
require 5.002;
use Socket;
use Sys::Hostname;
$| = 1;

my ( $count, $hisiaddr, $hispaddr, $histime, $buffer,
        $host, $iaddr, $paddr, $port, $proto, $mreq );

my($IP_ADD_MEMBERSHIP) = 0x13;
my($IPPROTO_IP)        = 0x0;

#$iaddr = gethostbyname(hostname());
$iaddr = inet_aton(0.0.0.0);
$proto = getprotobyname('udp');
$paddr = sockaddr_in(5010, $iaddr);
socket(SOCKET, PF_INET, SOCK_DGRAM, $proto) || die "socket: $!";
bind(SOCKET, $paddr)  || die "bind: $!";

# join multicast group
$mreq = join '', inet_aton(225.100.1.18), INADDR_ANY;

#Program always dies here
setsockopt(SOCKET, $IPPROTO_IP, $IP_ADD_MEMBERSHIP, $mreq)
        or die "error joining multicast group: $!";

for (;;) {
        $buffer = '';
        ($hispaddr = recv(SOCKET, $buffer, 1000, 0)) || die "recv:
$!";
        ($port, $hisiaddr) = sockaddr_in($hispaddr);
        $host = gethostbyaddr($hisiaddr, AF_INET);
        print "received from $host:  $buffer\n";
        sleep 1;
}
--------------------------------------

Craig Petty
cpetty@westin.com
cpettyAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

ahoffmannCommented:
try
setsockopt(SOCKET, $IPPROTO_IP, $IP_ADD_MEMBERSHIP, *mreq)
0
cpettyAuthor Commented:
Pass a typeglob you say?  That didn't work.  I got the same error message "Invalid argument" when I ran the program.
I'm not sure that my $mreq variable is right.  For that matter I'm not sure the $IPPROTO_IP & $IP_ADD_MEMBERSHIP variables are right either; I got them straight out of /usr/include/netinet/in.h.
0
ahoffmannCommented:
Sorry for lazy reading.
remove $ from IP*, I'm not shure about $mreq or *mreq.
0
OWASP Proactive Controls

Learn the most important control and control categories that every architect and developer should include in their projects.

cpettyAuthor Commented:
Tried setsockopt(SOCKET, IPPROTO_IP, IP_ADD_MEMBERSHIP, *mreq)
Script won't compile now.  'IPPROTO_IP' and 'IP_ADD_MEMBERSHIP' become bare words without the '$'.  My 'use strict;' statement won't let me compile with barewords; I took away 'use strict', but still get the error message at run-time "Invalid argument to setsockopt()".  Those constants aren't provided the Socket module, so I defined them myself using the info in the c header files.  I don't know whether I defined them correctly or not.  Expert, what do you think?  I'd be glad to supply more info if you need it.
0
ahoffmannCommented:
I'm not shure either about your constants: [gs]etsockopt need the  SOL_SOCKET  and  SO_*  options defined in socket.h.
So, I suggest, that you test these constants (just for syntax, not for your functionality; means that the message 'Invalid argument ..' should disaper).

I'm going confused about the syntax too, 'cause I checked the Camel book (1'st edition) and there are 3 different notations for those parameters: SO_TYPE  or  $SO_TYPE  or  &SO_TYPE
My opinion is that the first one (SO_TYPE) is the right one.
0
cpettyAuthor Commented:
I appreciate your effort, but I that still does't answer my question.
Anyone else want to give it a try?
0
ozoCommented:
Well, one apparent problem with the code is that you have
      inet_aton(225.100.1.18)
where I would have thought you would have meant
      inet_aton("225.100.1.18")

0
ozoCommented:
(225.100.1.18) of course parsing as (225.100) . (1.18) eq '255.1' . '1.18' eq '225.11.18'

and (0.0.0.0) eq (0.0) . (0.0) eq '0' . '0' eq '00'
but inet_aton('00') eq inet_aton('0.0.0.0') so you got lucky there

0
julio011597Commented:
 Hello,

said that i've no experience with multicasting, neither know if your join statement may work, i may suggest this:

1. your setsockopt() must come _before_ the bind() - which has nothing to do with your error, but wouldn't let your code work anyway;

2. opening file "in.h" (on my system is /usr/include/netinet/in.h) i've found that:

IPPROTO_IP = 0x0, as you wrote, _but_

IP_ADD_MEMBERSHIP = 0x0C (12 decimal), while you wrote 0x13 (19 decimal) which corresponds to IP_PROXY, surely not what you meant.

Unless our systems differ on this, this could be your problem.

BTW, why didn't you define INADDR_ANY (=0x0), as well?

3. "in.h" defines ip_mreq to be (keep in mind: your OS may differ):

struct ip_mreq {
  struct in_addr imr_multiaddr;
  struct in_addr imr_interface;
};

and, in_addr is just an unsigned int;

you should check wether your join() can deal with struct memeber's alignment issues - this is bread for Perl gurus!


4. regarding the syntax of setsockopt(), your way seems the good one (no pointers in Perl!):

setsockopt(SOCKET, $IPPROTO_IP, $IP_ADD_MEMBERSHIP, $mreq);

HTH, julio

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
julio011597Commented:
Ah, another thing:

keep in mind ozo's comment as well :)

-julio
0
cpettyAuthor Commented:
It works!  Many thanks to those who helped me.  It turned out to be a combo of problems.  First I wasn't using quotes around my ip addresses (thanks ozo).  Second I was defining IP_ADD_MEMBERSHIP incorrectly.  I was using 0x23 when I should have used 23.  (on another platform I have it's in hex).  Thanks julio for making me double check that.

I didn't define INADDR_ANY since it's provided by socket.h.

Here's my final working code for those who care.
-----------begin------------
#!/usr/local/bin/perl
use strict 'vars';
require 5.002;
use Socket;
use Sys::Hostname;
$| = 1;

my ( $count, $hisiaddr, $hispaddr, $histime, $buffer,
        $host, $iaddr, $paddr, $port, $proto, $mreq );

#my($IP_ADD_MEMBERSHIP) = 0x23;
my($IP_ADD_MEMBERSHIP) = 23;
my($IPPROTO_IP)        = 0x0;

#$iaddr = gethostbyname(hostname());
$iaddr = inet_aton("0.0.0.0");
$proto = getprotobyname('udp');
$paddr = sockaddr_in(5010, $iaddr); # 0 means let kernel pick
socket(SOCKET, PF_INET, SOCK_DGRAM, $proto)   || die "socket: $!";
bind(SOCKET, $paddr)                          || die "bind: $!";

# join multicast group
$mreq = join '', inet_aton("225.100.1.18"), INADDR_ANY;
setsockopt(SOCKET, $IPPROTO_IP, $IP_ADD_MEMBERSHIP, $mreq)
        or die "error joining multicast group: $!";

for (;;) {
        $buffer = '';
        ($hispaddr = recv(SOCKET, $buffer, 1000, 0))        || die "recv: $!";
        ($port, $hisiaddr) = sockaddr_in($hispaddr);
        $host = gethostbyaddr($hisiaddr, AF_INET);
        print "received from $host:  $buffer\n";
        sleep 1;
}
--------end-------------

Multicasting is pretty cool when you can get it to work!  You should check it out!
0
ozoCommented:
Looks like you didn't find it neccesary to do setsockopt before your bind?
join is fine, although
$mreq = inet_aton("225.100.1.18") . INADDR_ANY;
may be a simpler equivalent.
Just one more comment, since I now have to pay points just to come back here;
You might make your IP_ADD_MEMBERSHIP usage look more like the provided PF_INET etc
by declareing it as
sub IP_ADD_MEMBERSHIP {23}
(which has an additional advantage that it's harder to accidentally change it)
Or, with the latest Perl, (which may mean changing your require 5.002)
use constant IP_ADD_MEMBERSHIP => 23;

0
cpettyAuthor Commented:
I like your suggestion about making a constant declaration.  I've never know how to do that.  I can't find the "use constant" syntax anywhere in the Perl documentation.  Is it in the Camel book?  If not where can I find it?
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Perl

From novice to tech pro — start learning today.