We help IT Professionals succeed at work.

We've partnered with Certified Experts, Carl Webster and Richard Faulkner, to bring you two Citrix podcasts. Learn about 2020 trends and get answers to your biggest Citrix questions!Listen Now

x

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

cpetty
cpetty asked
on
Medium Priority
2,565 Views
Last Modified: 2008-03-17
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
Comment
Watch Question

try
setsockopt(SOCKET, $IPPROTO_IP, $IP_ADD_MEMBERSHIP, *mreq)

Author

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.
Sorry for lazy reading.
remove $ from IP*, I'm not shure about $mreq or *mreq.

Author

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.
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.

Author

Commented:
I appreciate your effort, but I that still does't answer my question.
Anyone else want to give it a try?
ozo
CERTIFIED EXPERT
Most Valuable Expert 2014
Top Expert 2015

Commented:
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")

ozo
CERTIFIED EXPERT
Most Valuable Expert 2014
Top Expert 2015

Commented:
(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

 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

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts
Ah, another thing:

keep in mind ozo's comment as well :)

-julio

Author

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!
ozo
CERTIFIED EXPERT
Most Valuable Expert 2014
Top Expert 2015

Commented:
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;

Author

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?
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.