Discussion:
Sending UDP Multicast packages
(too old to reply)
louis.mayencourt@arm.com [nuttx]
2017-09-21 13:27:05 UTC
Permalink
Hi,
I'm trying to send some UDP packages with a multicast address (239.255.0.1) with the following code :



void send(void)
{
struct sockaddr_in addr;
int fd;
char *message="Hello, World!";



/* create an UDP socket */
if ((fd=socket(PF_INET,SOCK_DGRAM,0)) < 0) {
perror("socket");
exit(1);
}


/* set up destination address */

addr.sin_addr.s_addr=HTONL(0xefff0001);
addr.sin_port=HTONS(7400);

addr.sin_family=AF_INET;


/* now just sendto() our destination! */
while (1) {
if (sendto(fd,message,sizeof(message),0,(struct sockaddr *) &addr,
sizeof(addr)) < 0) {
perror("sendto");
exit(1);
}
sleep(1);
}
}


When I run it I got :
psock_udp_sendto: ERROR: udp_find_raddr_device failed
psock_sendto: ERROR: Family-specific send failed: -114
sendto: Error 114


Which make sense according to the code because "netdev_findby_ipv4addr" only check for broadcast IP and the multicast 239.255.0.1 don't matches with any netdevice in "netdev_finddevice_ipv4addr"...


What I'm missing ?


regards,
Louis
spudarnia@yahoo.com [nuttx]
2017-09-21 14:50:10 UTC
Permalink
This looks like the same example as here: http://ntrg.cs.tcd.ie/undergrad/4ba2/multicast/antony/example.html so I suppose it should work. However, I do not see any logic in place that could handle this case. There is quite a few multicast transfer in net/igmp but these do not follow the typical model.


I wonder how it should work in the general case where there are multiple network adapters? Should broadcast and multicast packets be replicated and sent on ALL networks? If so, then additional logic would be required to perform the send on all adapters. If not, then how would you select the network to send the multicast packet on?


For the latter case, one solution would be to add a routing table entry for 239.255.0.1 that identifies the IP address of the adapter to use. In that case:


- udp_psock_sendto() will call udp_find_raddr_device()
- udp_find_raddr_device() will call netdev_findby_ipv4addr()
- if CONFIG_NET_ROUTE=y, then netdev_findby_ipv4addr() will look up the route for the multicast address to get the router address.
- Then it should find the device based on that router address.
spudarnia@yahoo.com [nuttx]
2017-09-21 15:07:41 UTC
Permalink
Assuming that a routing table entry solves that issue, I see another minor issue in net/arp/arp_send.c: There is already logic there now to handle the Ethernet MAC header for outgoing multicast packet, but that logic is conditioned on CONFIG_NET_IGMP. For testing, you many need to make that unconditional.


Same issue in net/arp/arp_send.c: The outgoing multicast logic currently depends on IGMP. You might also want to review the selections for the multicast destination MAC addresses. Those are based on IGMPv2 specs.
spudarnia@yahoo.com [nuttx]
2017-09-21 16:03:03 UTC
Permalink
Post by ***@yahoo.com [nuttx]
Assuming that a routing table entry solves that issue, I see another minor issue in net/arp/arp_send.c: There is already logic there now to handle the Ethernet MAC header for outgoing multicast packet, but that logic is conditioned on CONFIG_NET_IGMP. For testing, you many need to make that unconditional.
Same issue in net/arp/arp_send.c: The outgoing multicast logic currently depends on IGMP. You might also want to review the selections for the multicast destination MAC addresses. Those are based on IGMPv2 specs.
My proposal is that (1) add the multicast routing info to the routing table, (2) generalize the multicast ARP info above to meet you needs, (3) send me a patch. I will review and incorporate your changes.
louis.mayencourt@arm.com [nuttx]
2017-09-21 16:29:36 UTC
Permalink
Thanks for the pointer, I will have a look to the routing table. CONFIG_NET_IGMP is not a problem since I need IGMP to receive multicast packages.


What I don't understand is that according to the code I trying to use (https://github.com/ros2/ros2_embedded_nuttx#limitations https://github.com/ros2/ros2_embedded_nuttx#limitations) UDP multicast is supposed to work...






https://github.com/ros2/ros2_embedded_nuttx#limitations
spudarnia@yahoo.com [nuttx]
2017-09-21 17:01:05 UTC
Permalink
Post by ***@arm.com [nuttx]
What I don't understand is that according to the code I trying to use (https://github.com/ros2/ros2_embedded_nuttx#limitations https://github.com/ros2/ros2_embedded_nuttx#limitations) UDP multicast is supposed to work...
With an entry in the routing table as I described and with CONFIG_NET_IGMP enabled, it should work.


There is a difference between the current Nuttx networking code and the old code used in that ROS2 port: With the current code, it is required a routing table entry be provided just as for Linux. There used to be a special space-saving kludge to support the tiny embedded case where there is only a single network adaptor. In that case, the code did a very dumb and dangerous case: It just send everything out the single network adaptor without any regard for routing rules.


That has been "fixed." The special support for platforms with a single network adaptor has been removed and now you must always follow the same rules as for platforms with multiple network adaptors: Now if the IP address is not specifically on the sub-net served by the IP address (and netmask) assigned to the adaptor, you must have and entry in the routing table that indicates that the adaptor can route the destination IP address. Otherwise the packet will be dropped.


Greg








https://github.com/ros2/ros2_embedded_nuttx#limitations
louis.mayencourt@arm.com [nuttx]
2017-09-22 10:17:44 UTC
Permalink
Thanks a lot for the details ! I will give a try !

Louis
spudarnia@yahoo.com [nuttx]
2017-09-21 16:30:09 UTC
Permalink
Okay... looking at Linux networking documentation it is clear that you must add a route in the routing table to handle routing of multicast packets. See Linux documentation (old) at http://www.tldp.org/HOWTO/Adv-Routing-HOWTO/lartc.multicast.html

The example there uses the Linux route command to add a route for the multicast address like:

$ ip route add 224.0.0.0/4 dev eth0

The corresponding command in NSH is the addroute command which would look like:

nsh> addroute 224.0.0.0/4 xx.xx.xx.xx

Where xx.xx.xx.xx is the IP address assigned to the device "eth0". The NSH version of the command is not smart enough to just use the device name.

The ARP related issues that I mentioned still must be addressed.

Greg
louis.mayencourt@arm.com [nuttx]
2017-09-25 10:50:52 UTC
Permalink
Hi Greg,

With the entry in the routing table and the CONFIG_NET_IGMP flag, I'm able to send multicast udp packages !


Now I'm facing some issue with the reception of multicast packages....
Fist I found out that the igmp messages have a checksum issue.
spudarnia@yahoo.com [nuttx]
2017-09-25 12:57:18 UTC
Permalink
Change committed. Thanks. There was a name change on that line at commit c5fc24e1101a6a4ef0c3c4ab5471ce23a38507d, but it looks like the length used for the checksum calculation has always been wrong.


Does this then resolve all multicast-related issues?


Greg
louis.mayencourt@arm.com [nuttx]
2017-09-25 13:35:29 UTC
Permalink
Good !

no I'm still not able to receive multicast packages... here is my code :


void recv_server(void)
{
struct sockaddr_in server;
in_addr_t tmpaddr;

unsigned char inbuf[1024];
int sockfd;
int nbytes;
int offset;
int ret;
socklen_t addrlen;
struct ip_mreq mc_req;


/* Create a new UDP socket */
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
printf("server: socket failure\n");
return;
}


/* Bind the socket to a local address */
server.sin_family = AF_INET;
server.sin_port = HTONS(PORTNO);
server.sin_addr.s_addr = HTONL(0xefff0001);


if (bind(sockfd, (struct sockaddr*)&server, sizeof(struct sockaddr_in)) < 0)
{
printf("server: bind failure\n");
exit(1);
}


/* Join a multicast group */
struct in_addr inaddr;
inaddr.s_addr = HTONL(0xefff0001);


/* Join a multicast group using ioctl calls */
printf("Join group...\n");
ret = ipmsfilter("eth0", &inaddr, MCAST_INCLUDE) ;
if (ret != 0)
{
printf("server: ipmsfilter failure %d\n", ret);
exit(1);
}


/* Then receive up to 256 packets of data */
for (offset = 0; offset < 256; offset++)
{
printf("server: %d. Receiving up 1024 bytes in port: %d\n", offset, PORTNO);
addrlen = sizeof(struct sockaddr_in);
nbytes = recvfrom(sockfd, inbuf, 1024, 0,
(struct sockaddr*)&server, &addrlen);


tmpaddr = ntohl(server.sin_addr.s_addr);
printf("server: %d. Received %d bytes from %d.%d.%d.%d:%d\n",
offset, nbytes,
tmpaddr >> 24, (tmpaddr >> 16) & 0xff,
(tmpaddr >> 8) & 0xff, tmpaddr & 0xff,
ntohs(server.sin_port));

// Print the content received
printf("Content (hex):\n");
int i;
for (i = 0; i<nbytes; i++){
printf(" %2x", inbuf[i]);
}
printf("\n");
printf("Content:\n");
printf("%s\n", inbuf);


if (nbytes < 0)
{
printf("server: %d. recv failed\n", offset);
close(sockfd);
exit(1);
}
}
close(sockfd);


}


regards
Louis
louis.mayencourt@arm.com [nuttx]
2017-09-25 17:09:21 UTC
Permalink
I have inspected the network and the IGMP msg look good. But I still don't have any frame with the multicast address received in ipv4_input...
Any suggestion on where to investigate ?
spudarnia@yahoo.com [nuttx]
2017-09-25 17:31:23 UTC
Permalink
ipv4_input is called from the Network adapter. You are using STM32? So that would be from arch/arch/src/stm32_eth.c.


Know, of course, that you must configure multicast address filtering in the Ethernet driver or it will discard all incoming packets that don't match the MAC address assigned to the adaptor.


Filtering is performed on MAC addresses, no IP addresses. There is a special set of MAC addresses used for IPv4 multicast. The hardware must be configured to accept these.


Greg
Gregory Nutt spudarnia@yahoo.com [nuttx]
2017-09-25 17:52:49 UTC
Permalink
Adding the IGMP address filtering is performed using the SIOCSIPMSFILTER IOCTL command. There is a helper function in apps/netutils/netlib/netlib_ipmsfilter.c that will perform the IOCTL call on your behalf (convenient, but not so efficient).

Here is the logic path. The IOCTL handling in netdev/netdev_ioctl.c calls igmp_joingroup():

1033 case SIOCSIPMSFILTER: /* Set source filter content */
1034 {
1035 dev = netdev_imsfdev(imsf);
1036 if (dev)
1037 {
1038 if (imsf->imsf_fmode == MCAST_INCLUDE)
1039 {
1040 ret = igmp_joingroup(dev, &imsf->imsf_multiaddr);
1041 }
1042 else
1043 {
1044 DEBUGASSERT(imsf->imsf_fmode == MCAST_EXCLUDE);
1045 ret = igmp_leavegroup(dev, &imsf->imsf_multiaddr);
1046 }
1047 }
1048 }
1049 break;

igmp_joingroup() in net/igmp/igmp_join.c calls igmp_addmcastmac():

116 int igmp_joingroup(struct net_driver_s *dev, FAR const struct in_addr *grpaddr)
....
142 /* Add the group (MAC) address to the ether drivers MAC filter list */
143
144 igmp_addmcastmac(dev, (FAR in_addr_t *)&grpaddr->s_addr);

igmp_addmcastmac() in igmp/igmp_mcastmac.c then gets the MAC address associated with the multicase address and then calls into the Network driver to setu the MAC address filter to accept packets destined to that MAC address:

98 void igmp_addmcastmac(FAR struct net_driver_s *dev, FAR in_addr_t *ip)
99 {
100 uint8_t mcastmac[6];
101
102 ninfo("Adding: IP %08x\n", *ip);
103 if (dev->d_addmac)
104 {
105 igmp_mcastmac(ip, mcastmac);
106 dev->d_addmac(dev, mcastmac);
107 }
108 }
louis.mayencourt@arm.com [nuttx]
2017-09-28 10:19:31 UTC
Permalink
Hi Greg,

Thanks for the pointers !


I found out that the IGMP Ethernet address computation is wrong ! Only the 23 lower bits of the IPv4 address should be used to create the multicast mac address : https://technet.microsoft.com/en-us/library/cc957928.aspx https://technet.microsoft.com/en-us/library/cc957928.aspx


With this change the multicast reception is working properly and the same for the DDS communication !


Thanks a lot for your help !
regards
Louis
Gregory Nutt spudarnia@yahoo.com [nuttx]
2017-09-28 13:27:09 UTC
Permalink
Good news. So you were just unlucky because the 23rd bit was set in the destination IP address. Thanks for the fix. I have committed the change.

The existing memcpy’s bothered me. First of all, calling memcpy() to copy 3 bytes is inefficient. But then even less efficient if we then have to modify one of the bytes after memcpy’ing it. So I changed the memcpy’s into inline assignments. ARM is very efficient in simple indexed loads and stores. Hope that is okay.

Greg

Sent from Mail for Windows 10

From: ***@arm.com [nuttx]
Sent: Thursday, September 28, 2017 4:20 AM
To: ***@yahoogroups.com
Subject: [nuttx] Re: Sending UDP Multicast packages [1 Attachment]

 
[Attachment(s) from ***@arm.com [nuttx] included below]
Hi Greg,

Thanks for the pointers ! 

I found out that the IGMP Ethernet address computation is wrong ! Only the 23 lower bits of the IPv4 address should be used to create the multicast mac address : https://technet.microsoft.com/en-us/library/cc957928.aspx

With this change the multicast reception is working properly and the same for the DDS communication !

Thanks a lot for your help !
regards 
Louis
 
louis.mayencourt@arm.com [nuttx]
2018-01-02 17:07:20 UTC
Permalink
Hi Greg,

I just realise that the inline assignments are incorrect. Please find here the required modifications.


regards,
Louis
spudarnia@yahoo.com [nuttx]
2018-01-02 17:20:22 UTC
Permalink
Looks good. Thanks! Change committed.


Greg

louis.mayencourt@arm.com [nuttx]
2017-09-25 17:11:01 UTC
Permalink
I have inspected the network and the IGMP msg look good. But I still don't have any frame with the multicast address received in ipv4_input...
Any suggestion on where to investigate ?
Loading...