Solved

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

Posted on 1997-08-25
13
2,062 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
0
Comment
Question by:cpetty
  • 5
  • 3
  • 3
  • +1
13 Comments
 
LVL 51

Expert Comment

by:ahoffmann
ID: 1205636
try
setsockopt(SOCKET, $IPPROTO_IP, $IP_ADD_MEMBERSHIP, *mreq)
0
 

Author Comment

by:cpetty
ID: 1205637
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
 
LVL 51

Expert Comment

by:ahoffmann
ID: 1205638
Sorry for lazy reading.
remove $ from IP*, I'm not shure about $mreq or *mreq.
0
Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 

Author Comment

by:cpetty
ID: 1205639
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
 
LVL 51

Expert Comment

by:ahoffmann
ID: 1205640
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
 

Author Comment

by:cpetty
ID: 1205641
I appreciate your effort, but I that still does't answer my question.
Anyone else want to give it a try?
0
 
LVL 84

Expert Comment

by:ozo
ID: 1205642
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
 
LVL 84

Expert Comment

by:ozo
ID: 1205643
(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
 
LVL 5

Accepted Solution

by:
julio011597 earned 100 total points
ID: 1205644
 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
 
LVL 5

Expert Comment

by:julio011597
ID: 1205645
Ah, another thing:

keep in mind ozo's comment as well :)

-julio
0
 

Author Comment

by:cpetty
ID: 1205646
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
 
LVL 84

Expert Comment

by:ozo
ID: 1205647
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
 

Author Comment

by:cpetty
ID: 1205648
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

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I've just discovered very important differences between Windows an Unix formats in Perl,at least 5.xx.. MOST IMPORTANT: Use Unix file format while saving Your script. otherwise it will have ^M s or smth likely weird in the EOL, Then DO NOT use m…
Many time we need to work with multiple files all together. If its windows system then we can use some GUI based editor to accomplish our task. But what if you are on putty or have only CLI(Command Line Interface) as an option to  edit your files. I…
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question