Ramon Casellas
2008-09-03 11:35:02 UTC
All,
I have been suggested to submit the question here. Apologies if this
question is answered. I found a related thread in 2007 covering these issues
(including particular Linux Multicast implementation details), but I do not
think the thread answers this question (
http://osdir.com/ml/lib.boost.asio.user/2007-04/msg00086.html )
Short Form question:
How can I force a socket to receive multicast udp packets only from a given
network interface (ideally portably with Asio) or, given a socket that
received a multicast packet, know the incoming interface?
Long Form question:
Consider a network node A, B... with several network interfaces, each
interfaces attached to a link. At the remote end of the link, there is a
neighbor. An application running in that node, which uses boost asio,
implements a given network protocol. The purpose of the protocol is to
manage control_channels (IP data communication channels to simplify) between
neighbor nodes. The protocol specifies that a node starts sending multicast
UDP messages over the interface, and when a node gets a multicast packet,
starts sending packets to the unicast address "from" the received multicast.
Current design is as follows: a class control_channel models a protocol
control channel. There exists a control_channel instance for each active
network interface (eth, to simplify), and there is a socket member. The
socket is used to send and receive multicast frames *over that interface*. I
cannot figure out how to use Boost.Asio to receive multicast frames that
were received on that specific interface. Either I do not receive multicast
packets or I receive a multicast packet on every socket, regardless of the
interface it was physically received on.
Node A Node B
gre21 gre23
o-----------------------------o-----------
10.0.5.12 10.0.5.21
On Node B
===================
tshark -i any -f"udp"
2.206040 10.0.5.12 -> 224.0.0.1 LMP Config Message.
2008-Sep-03 15:17:04.490236
----------------------------------------------------------------------------
-----------
- void lmp::node::control_channel::parse_message(size_t)
CONTROL_CHANNEL: Received Message on gre23 - type: 1 rcvd bytes: 40
declared size: 40
MESSAGE: CONFIG Length: 40
2008-Sep-03 15:17:04.490407
----------------------------------------------------------------------------
-----------
- void lmp::node::control_channel::parse_message(size_t)
CONTROL_CHANNEL: Received Message on gre21 - type: 1 rcvd bytes: 40
declared size: 40
MESSAGE: CONFIG Length: 40
namespace aio = boost::asio;
/// Socket for the control_channel.
aio::ip::udp::socket socket_;
/// if_addr_str is the nework address of the interface (IFA_LOCAL e.g.
10.0.5.21)
/// port_ is the same for all sockets, fixed by the protocol.
Code:
----------------------------
aio::ip::address if_address = aio::ip::address::from_string
(if_address_str);
aio::ip::address multicast_address = aio::ip::address::from_string
("224.0.0.1");
aio::ip::address listen_address = multicast_address;
// I.- Bind the UDP socket to this interface - discards packets not sent to
the interface,
// *including* multicast packets.
// ip::udp::endpoint listen_endpoint(if_address, port_);
//
// II.- Binding the UDP socket to the any (0.0.0.0) address receives
multicast packets arriving on other interfaces
// and supposedly other multicast addresses / groups too
// ip::udp::endpoint listen_endpoint(anyaddr, port_);
// III.- Binding the UDP socket to the multicast address, works, but we
receive packets sent to other interfaces too
// ip::udp::endpoint listen_endpoint(listen_address, port_);
aio::ip::udp::endpoint listen_endpoint(listen_address, port_);
socket_.open (listen_endpoint.protocol());
socket_.set_option (aio::ip::udp::socket::reuse_address(true));
socket_.bind (listen_endpoint);
// disable loopback (no copies of our packets)
socket_.set_option(aio::ip::multicast::enable_loopback(false));
// set oif - the socket will use this interface as outgoing interface
socket_.set_option(aio::ip::multicast::outbound_interface(if_address.to_v4()
));
// set mcast group - join group - If the network interface (the second
constructor parameter) is not
// specified when joining a multicast group, the system may select an
appropriate interface based on
// the routing table, probably the default route.
// socket_.set_option(aio::ip::multicast::join_group(multicast_address));
socket_.set_option(aio::ip::multicast::join_group(multicast_address.to_v4(),
if_address.to_v4()));
---------------------------------------------
// RX is scheduled to the io_service with:
socket_.async_receive_from (
aio::buffer(buffer_), recv_endpoint_,
boost::bind (&control_channel::handle_read,
shared_from_this(),
aio::placeholders::error,
aio::placeholders::bytes_transferred));
---------------------------------------------
// Transmission works, also with outbound_interface option.
remote_endpoint_(aio::ip::address::from_string("224.0.0.1"), port_)
socket_.send_to (
boost::asio::buffer (message_stream.str().c_str(),
msg->message_length()), remote_endpoint_);
When a node A sends a mcast packet to node B, the mcast packet is received
by all sockets open by node B. I would like to receive it *only* on the
socket "associated to" interface that is connected to A, but other than
bind, I don't know how. It seems that in Linux, bind does simply makes the
kernel drop packets not directed to the interface address. Can I do this
with asio?
I considered having a single multicast socket, I am not sure how to get the
real incoming interface portably, so I can map the source address of the
received packet to the neighbor at the other side of the link/interface. The
target system is a debian GNU/Linux 4.0, with a 2.6.24 kernel. The non
portable way would be to use IP_PKTINFO, I guess, or IP_RECVIF in other BSD
systems?
Thanks in advance for your comments or suggestions.
----
Ramon Casellas
I have been suggested to submit the question here. Apologies if this
question is answered. I found a related thread in 2007 covering these issues
(including particular Linux Multicast implementation details), but I do not
think the thread answers this question (
http://osdir.com/ml/lib.boost.asio.user/2007-04/msg00086.html )
Short Form question:
How can I force a socket to receive multicast udp packets only from a given
network interface (ideally portably with Asio) or, given a socket that
received a multicast packet, know the incoming interface?
Long Form question:
Consider a network node A, B... with several network interfaces, each
interfaces attached to a link. At the remote end of the link, there is a
neighbor. An application running in that node, which uses boost asio,
implements a given network protocol. The purpose of the protocol is to
manage control_channels (IP data communication channels to simplify) between
neighbor nodes. The protocol specifies that a node starts sending multicast
UDP messages over the interface, and when a node gets a multicast packet,
starts sending packets to the unicast address "from" the received multicast.
Current design is as follows: a class control_channel models a protocol
control channel. There exists a control_channel instance for each active
network interface (eth, to simplify), and there is a socket member. The
socket is used to send and receive multicast frames *over that interface*. I
cannot figure out how to use Boost.Asio to receive multicast frames that
were received on that specific interface. Either I do not receive multicast
packets or I receive a multicast packet on every socket, regardless of the
interface it was physically received on.
Node A Node B
gre21 gre23
o-----------------------------o-----------
10.0.5.12 10.0.5.21
On Node B
===================
tshark -i any -f"udp"
2.206040 10.0.5.12 -> 224.0.0.1 LMP Config Message.
2008-Sep-03 15:17:04.490236
----------------------------------------------------------------------------
-----------
- void lmp::node::control_channel::parse_message(size_t)
CONTROL_CHANNEL: Received Message on gre23 - type: 1 rcvd bytes: 40
declared size: 40
MESSAGE: CONFIG Length: 40
2008-Sep-03 15:17:04.490407
----------------------------------------------------------------------------
-----------
- void lmp::node::control_channel::parse_message(size_t)
CONTROL_CHANNEL: Received Message on gre21 - type: 1 rcvd bytes: 40
declared size: 40
MESSAGE: CONFIG Length: 40
namespace aio = boost::asio;
/// Socket for the control_channel.
aio::ip::udp::socket socket_;
/// if_addr_str is the nework address of the interface (IFA_LOCAL e.g.
10.0.5.21)
/// port_ is the same for all sockets, fixed by the protocol.
Code:
----------------------------
aio::ip::address if_address = aio::ip::address::from_string
(if_address_str);
aio::ip::address multicast_address = aio::ip::address::from_string
("224.0.0.1");
aio::ip::address listen_address = multicast_address;
// I.- Bind the UDP socket to this interface - discards packets not sent to
the interface,
// *including* multicast packets.
// ip::udp::endpoint listen_endpoint(if_address, port_);
//
// II.- Binding the UDP socket to the any (0.0.0.0) address receives
multicast packets arriving on other interfaces
// and supposedly other multicast addresses / groups too
// ip::udp::endpoint listen_endpoint(anyaddr, port_);
// III.- Binding the UDP socket to the multicast address, works, but we
receive packets sent to other interfaces too
// ip::udp::endpoint listen_endpoint(listen_address, port_);
aio::ip::udp::endpoint listen_endpoint(listen_address, port_);
socket_.open (listen_endpoint.protocol());
socket_.set_option (aio::ip::udp::socket::reuse_address(true));
socket_.bind (listen_endpoint);
// disable loopback (no copies of our packets)
socket_.set_option(aio::ip::multicast::enable_loopback(false));
// set oif - the socket will use this interface as outgoing interface
socket_.set_option(aio::ip::multicast::outbound_interface(if_address.to_v4()
));
// set mcast group - join group - If the network interface (the second
constructor parameter) is not
// specified when joining a multicast group, the system may select an
appropriate interface based on
// the routing table, probably the default route.
// socket_.set_option(aio::ip::multicast::join_group(multicast_address));
socket_.set_option(aio::ip::multicast::join_group(multicast_address.to_v4(),
if_address.to_v4()));
---------------------------------------------
// RX is scheduled to the io_service with:
socket_.async_receive_from (
aio::buffer(buffer_), recv_endpoint_,
boost::bind (&control_channel::handle_read,
shared_from_this(),
aio::placeholders::error,
aio::placeholders::bytes_transferred));
---------------------------------------------
// Transmission works, also with outbound_interface option.
remote_endpoint_(aio::ip::address::from_string("224.0.0.1"), port_)
socket_.send_to (
boost::asio::buffer (message_stream.str().c_str(),
msg->message_length()), remote_endpoint_);
When a node A sends a mcast packet to node B, the mcast packet is received
by all sockets open by node B. I would like to receive it *only* on the
socket "associated to" interface that is connected to A, but other than
bind, I don't know how. It seems that in Linux, bind does simply makes the
kernel drop packets not directed to the interface address. Can I do this
with asio?
I considered having a single multicast socket, I am not sure how to get the
real incoming interface portably, so I can map the source address of the
received packet to the neighbor at the other side of the link/interface. The
target system is a debian GNU/Linux 4.0, with a 2.6.24 kernel. The non
portable way would be to use IP_PKTINFO, I guess, or IP_RECVIF in other BSD
systems?
Thanks in advance for your comments or suggestions.
----
Ramon Casellas