Does lwip in SDK 1.2 support multicast messages?
If so, does anybody know any good examples to set this up?
Hi David,
KSDK1.2 includes the lwIP 1.4.1 source code which features IGMP
(Internet Group Management Protocol) for multicast traffic management.
lwip 1.4.1 can be downloaded from this link: download.savannah.gnu.org/releases/lwip/
Regards,
Carlos
Hi Carlos,
Thanks for the link, I had already found that on Google already. I will try to hit up their forums to see what I can find. I am just wondering if this is a Kinetis specific problem.
Here is what I have done in the past few days to try to get this working. To simplify I am working inside of the example project of projects provided with KSDK 1.2:
C:\Freescale\KSDK_1.2.0\examples\twrk60d100m\demo_apps\lwip\lwip_udpecho_demo\udpecho_rtos\udpecho_mqx\kds\lwip_udpecho_demo_mqx.wsd
-imported this into a blank workspace.
-everything compiles as usual.
-I can ping the tower board and send UDP packets which are echoed as expected.
I then reconfigured this example to receive multicast messages:
-Added the following line to the udpecho_thread function in main.c:
err = netconn_join_leave_group(conn, &multicast_ip_address, &local_ip_address, NETCONN_JOIN);
-Calling this requires that LWIP_IGMP be defined as 1
-Also we must define LWIP_RAND()be defined so we added the following in main.c:
#define LWIP_RAND get_random_value
uint32_t get_random_value(void)
{
uint32_t num;
uint8_t seed_value = 17;
srand(seed_value);
num = rand();
return num;
}
Everything seems to work fine and looking at the debug dialog and wireshark we are successful at broadcasting the multicast join message.
However, we could still not receive messages.
I temporarily enabled promiscuous mode (ENET_RCR[PROM]) and this works, messages come through as expected.
So I guess my question at this point is how to implement the function to filter MAC address to allow the multicast messages through?
I am specifically talking about the area in igmp.c, around line 500:
/* If first use of the group, allow the group at the MAC level */
if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
ip_addr_debug_print(IGMP_DEBUG, groupaddr);
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
}
Any ideas?
Thanks,
David
Just ran into this and I'm working my way through it. You need to add the hooks for the join/leave operations.
In ethernetif_init (ethernetif.c) I added:
netif_set_igmp_mac_filter(netif, ethernetif_igmp_mac_filter);
and defined:
static err_t ethernetif_igmp_mac_filter(struct netif *netif, ip_addr_t *group, u8_t action) {
uint32_t devNumber = 0;
uint32_t hash;
#if LWIP_HAVE_LOOPIF
devNumber = netif->num - 1;
#else
devNumber = netif->num;
#endif
switch (action) {
case IGMP_ADD_MAC_FILTER:
return ENET_DRV_AddMulticastGroup(devNumber, (uint8_t *) group, &hash);
case IGMP_DEL_MAC_FILTER:
return ENET_DRV_LeaveMulticastGroup(devNumber, (uint8_t *) group);
default:
return ERR_IF;
}
}
to get the ball rolling. I can send out multicast packets and I see the IGMP membership notifications on wireshark. Still trying to figure out why I'm not receiving any yet.
Figured it out... the driver expects a multicast mac address while the hook is sending an ip address. You also need to tell netif that that interface is igmp aware:
in ethernetif.c:
Add to low_level_init():
#if LWIP_IGMP
netif->flags |= NETIF_FLAG_IGMP;
#endif
and the above code changes slightly to:
static err_t multicast_ip_to_mac(ip_addr_t *ip_addr, uint8_t *mac_addr) {
if ((NULL == ip_addr) || (NULL == mac_addr)) {
return ERR_ARG;
}
mac_addr[0] = 0x01;
mac_addr[1] = 0x00;
mac_addr[2] = 0x53;
mac_addr[3] = ip4_addr2_16(ip_addr) & ~0x80;
mac_addr[4] = ip4_addr3_16(ip_addr);
mac_addr[5] = ip4_addr4_16(ip_addr);
return ENET_OK;
}
static err_t ethernetif_igmp_mac_filter(struct netif *netif, ip_addr_t *group, u8_t action) {
uint32_t devNumber = 0;
uint32_t hash;
#if LWIP_HAVE_LOOPIF
devNumber = netif->num - 1;
#else
devNumber = netif->num;
#endif
uint8_t multicastMacAddr[kEnetMacAddrLen];
multicast_ip_to_mac(group, multicastMacAddr);
//LWIP_DEBUGF(IGMP_DEBUG, ("mac filtering ip: %d hw: % hash: %d", );
switch (action) {
case IGMP_ADD_MAC_FILTER:
return ENET_DRV_AddMulticastGroup(devNumber, (uint8_t *) multicastMacAddr, &hash);
case IGMP_DEL_MAC_FILTER:
return ENET_DRV_LeaveMulticastGroup(devNumber, (uint8_t *) multicastMacAddr);
default:
return ERR_IF;
}
}
OMG, I been struggling so much with this. Finally got it receiving but there was a slight error in your code posted above. The mac_addr[2] should equal 0x5E not 0x53. The following site explains this conversion quite well: Mapping IP Multicast to MAC-Layer Multicast
So my code looks as follows:
err_t ethernetif_init(struct netif *netif)
{
...
...
...
#if LWIP_IGMP
netif_set_igmp_mac_filter(netif, ethernetif_igmp_mac_filter);
#endif
return result;
}
static err_t multicast_ip_to_mac(ip_addr_t *ip_addr, uint8_t *mac_addr)
{
if ((NULL == ip_addr) || (NULL == mac_addr)) {
return ERR_ARG;
}
mac_addr[0] = 0x01;
mac_addr[1] = 0x00;
mac_addr[2] = 0x5E;
mac_addr[3] = ip4_addr2_16(ip_addr) & ~0x80;
mac_addr[4] = ip4_addr3_16(ip_addr);
mac_addr[5] = ip4_addr4_16(ip_addr);
return ENET_OK;
}
static err_t ethernetif_igmp_mac_filter(struct netif *netif, ip_addr_t *group, u8_t action)
{
uint32_t devNumber = 0;
uint32_t hash;
#if LWIP_HAVE_LOOPIF
devNumber = netif->num - 1;
#else
devNumber = netif->num;
#endif
uint8_t multicastMacAddr[kEnetMacAddrLen];
multicast_ip_to_mac(group, multicastMacAddr);
//LWIP_DEBUGF(IGMP_DEBUG, ("mac filtering ip: %d hw: % hash: %d", );
switch (action)
{
case IGMP_ADD_MAC_FILTER:
return ENET_DRV_AddMulticastGroup(devNumber, (uint8_t *) multicastMacAddr, &hash);
case IGMP_DEL_MAC_FILTER:
return ENET_DRV_LeaveMulticastGroup(devNumber, (uint8_t *) multicastMacAddr);
default:
return ERR_IF;
}
}
Good catch. That was a typo that I forgot to fix in my post.
FYI - you still have to make the modifications in the 2.0 KDSK with some slight changes (so far -- haven't gotten everything working just yet)
#define IPE_ADDR_MC(addr, ip) \
do { \
(addr)[0] = 0x01; \
(addr)[1] = 0x00; \
(addr)[2] = 0x5E; \
(addr)[3] = ((ip) >> 8) & 0x7F; \
(addr)[4] = ((ip) >> 16) & 0xFF; \
(addr)[5] = ((ip) >> 24) & 0xFF; \
} while (0)
static err_t ethernetif_igmp_mac_filter(struct netif *netif, ip_addr_t *group, u8_t action) {
uint8_t multicastMacAddr[kEnetMacAddrLen];
IPE_ADDR_MC(multicastMacAddr, group->addr);
LWIP_DEBUGF(IGMP_DEBUG, ("Filtering multicast mac:"));
mac_addr_debug_print(IGMP_DEBUG, multicastMacAddr);
switch (action) {
case IGMP_ADD_MAC_FILTER:
ENET_AddMulticastGroup(ENET, multicastMacAddr);
return ERR_OK;
case IGMP_DEL_MAC_FILTER:
ENET_LeaveMulticastGroup(ENET, multicastMacAddr);
return ERR_OK;
default:
return ERR_IF;
}
}
low_level_init changes are the same.
David
It sounds as though these functions have never been used or tested before since the multicast filter configuration must be missing. This is HW dependent and so the lwip part is possibly just calling a dummy function that needs to be replaced for the platform. Since IPv6 also needs mulicast operation it is probable that IPv6 link-local operations will not function either.
I have attached the Kinetis Ethernet driver from the uTasker project so that you can take the multicast filter parts in case you don't find a quick solution from the other communities.
Just search for
external: fnModifyMulticastFilter()
static: fnDisableMulticastHash()
static: fnEnableMulticastHash()
static:fnCalculateHash()
For multicast/IGMP overview you can look at
www. utasker.com/docs/uTasker/uTasker_IGMP.pdf
Regards
Mark