From a80058dc5fac3c1d7569209cc0d441eef4bdebc3 Mon Sep 17 00:00:00 2001 From: Emilio Monti Date: Fri, 1 Mar 2013 17:02:35 +0000 Subject: [PATCH] Add multicast and broadcast support --- .../EthernetInterface/EthernetInterface.cpp | 18 ++++++++-- .../net/EthernetInterface/EthernetInterface.h | 12 +++++-- .../lwip-eth/arch/lpc17_emac.c | 2 ++ libraries/net/lwip/Socket/Endpoint.cpp | 34 +++++++++++++------ libraries/net/lwip/Socket/Endpoint.h | 2 +- libraries/net/lwip/Socket/Socket.cpp | 8 +++++ libraries/net/lwip/Socket/Socket.h | 18 ++++++++++ libraries/net/lwip/Socket/UDPSocket.cpp | 15 ++++++++ libraries/net/lwip/Socket/UDPSocket.h | 11 ++++++ libraries/net/lwip/lwip/core/ipv4/igmp.c | 12 ------- libraries/net/lwip/lwip/lwipopts.h | 5 +++ .../net/helloworld/broadcast_receive/main.cpp | 23 +++++++++++++ .../net/helloworld/broadcast_send/main.cpp | 25 ++++++++++++++ .../net/helloworld/multicast_receive/main.cpp | 27 +++++++++++++++ .../net/helloworld/multicast_send/main.cpp | 24 +++++++++++++ .../host_tests/example/BroadcastReceive.py | 9 +++++ .../host_tests/example/BroadcastSend.py | 14 ++++++++ .../host_tests/example/MulticastReceive.py | 15 ++++++++ .../host_tests/example/MulticastSend.py | 14 ++++++++ workspace_tools/tests.py | 24 +++++++++++++ 20 files changed, 284 insertions(+), 28 deletions(-) create mode 100644 libraries/tests/net/helloworld/broadcast_receive/main.cpp create mode 100644 libraries/tests/net/helloworld/broadcast_send/main.cpp create mode 100644 libraries/tests/net/helloworld/multicast_receive/main.cpp create mode 100644 libraries/tests/net/helloworld/multicast_send/main.cpp create mode 100644 workspace_tools/host_tests/example/BroadcastReceive.py create mode 100644 workspace_tools/host_tests/example/BroadcastSend.py create mode 100644 workspace_tools/host_tests/example/MulticastReceive.py create mode 100644 workspace_tools/host_tests/example/MulticastSend.py diff --git a/libraries/net/EthernetInterface/EthernetInterface.cpp b/libraries/net/EthernetInterface/EthernetInterface.cpp index 83a780a77e..9824507f7d 100644 --- a/libraries/net/EthernetInterface/EthernetInterface.cpp +++ b/libraries/net/EthernetInterface/EthernetInterface.cpp @@ -31,7 +31,8 @@ /* TCP/IP and Network Interface Initialisation */ static struct netif lpcNetif; -static char ip_addr[16] = "\0"; +static char mac_addr[19]; +static char ip_addr[17] = "\0"; static bool use_dhcp = false; static Semaphore tcpip_inited(0); @@ -67,14 +68,23 @@ static void init_netif(ip_addr_t *ipaddr, ip_addr_t *netmask, ip_addr_t *gw) { netif_set_status_callback(&lpcNetif, netif_status_callback); } +static void set_mac_address(void) { + char mac[6]; + mbed_mac_address(mac); + snprintf(mac_addr, 19, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} + int EthernetInterface::init() { use_dhcp = true; + set_mac_address(); init_netif(NULL, NULL, NULL); return 0; } int EthernetInterface::init(const char* ip, const char* mask, const char* gateway) { use_dhcp = false; + + set_mac_address(); strcpy(ip_addr, ip); ip_addr_t ip_n, mask_n, gateway_n; @@ -94,7 +104,7 @@ int EthernetInterface::connect(unsigned int timeout_ms) { if (use_dhcp) { dhcp_start(&lpcNetif); - // Wait for an IP + // Wait for an IP Address // -1: error, 0: timeout inited = netif_up.wait(timeout_ms); } else { @@ -120,6 +130,10 @@ int EthernetInterface::disconnect() { return 0; } +char* EthernetInterface::getMACAddress() { + return mac_addr; +} + char* EthernetInterface::getIPAddress() { return ip_addr; } diff --git a/libraries/net/EthernetInterface/EthernetInterface.h b/libraries/net/EthernetInterface/EthernetInterface.h index 05c6a97b33..90324b27a6 100644 --- a/libraries/net/EthernetInterface/EthernetInterface.h +++ b/libraries/net/EthernetInterface/EthernetInterface.h @@ -49,10 +49,10 @@ public: /** Connect * Bring the interface up, start DHCP if needed. - * \param timeout_ms timeout in ms (default: (10)s). + * \param timeout_ms timeout in ms (default: (15)s). * \return 0 on success, a negative number on failure */ - static int connect(unsigned int timeout_ms=10000); + static int connect(unsigned int timeout_ms=15000); /** Disconnect * Bring the interface down @@ -60,6 +60,14 @@ public: */ static int disconnect(); + /** Get the MAC address of your Ethernet interface + * \return a pointer to a string containing the MAC address + */ + static char* getMACAddress(); + + /** Get the IP address of your Ethernet interface + * \return a pointer to a string containing the IP address + */ static char* getIPAddress(); }; diff --git a/libraries/net/EthernetInterface/lwip-eth/arch/lpc17_emac.c b/libraries/net/EthernetInterface/lwip-eth/arch/lpc17_emac.c index 264571dd87..9f456edad5 100644 --- a/libraries/net/EthernetInterface/lwip-eth/arch/lpc17_emac.c +++ b/libraries/net/EthernetInterface/lwip-eth/arch/lpc17_emac.c @@ -402,6 +402,8 @@ static struct pbuf *lpc_low_level_input(struct netif *netif) LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE, ("lpc_low_level_input: Packet dropped with errors (0x%x)\n", lpc_enetif->prxs[idx].statusinfo)); + + p = NULL; } else { /* A packet is waiting, get length */ length = (lpc_enetif->prxs[idx].statusinfo & 0x7FF) + 1; diff --git a/libraries/net/lwip/Socket/Endpoint.cpp b/libraries/net/lwip/Socket/Endpoint.cpp index fd42835913..25688d0e29 100644 --- a/libraries/net/lwip/Socket/Endpoint.cpp +++ b/libraries/net/lwip/Socket/Endpoint.cpp @@ -18,8 +18,7 @@ #include "Socket/Socket.h" #include "Socket/Endpoint.h" #include - -using std::memset; +#include Endpoint::Endpoint() { reset_address(); @@ -27,21 +26,34 @@ Endpoint::Endpoint() { Endpoint::~Endpoint() {} void Endpoint::reset_address(void) { - memset(&_remoteHost, 0, sizeof(struct sockaddr_in)); + std::memset(&_remoteHost, 0, sizeof(struct sockaddr_in)); _ipAddress[0] = '\0'; } +#include "stdio.h" + int Endpoint::set_address(const char* host, const int port) { - //Resolve DNS address or populate hard-coded IP address - struct hostent *server = ::gethostbyname(host); - if (server == NULL) - return -1; //Could not resolve address - reset_address(); - // Set IP address - std::memcpy((char*) &_remoteHost.sin_addr.s_addr, - (char*) server->h_addr_list[0], server->h_length); + // IP Address + char address[5]; + char *p_address = address; + + // Dot-decimal notation + int result = std::sscanf(host, "%3u.%3u.%3u.%3u", + (unsigned int*)&address[0], (unsigned int*)&address[1], + (unsigned int*)&address[2], (unsigned int*)&address[3]); + + if (result != 4) { + // Resolve address with DNS + struct hostent *host_address = lwip_gethostbyname(host); + if (host_address == NULL) + return -1; //Could not resolve address + p_address = (char*)host_address->h_addr_list[0]; + } + std::memcpy((char*)&_remoteHost.sin_addr.s_addr, p_address, 4); + + // Address family _remoteHost.sin_family = AF_INET; // Set port diff --git a/libraries/net/lwip/Socket/Endpoint.h b/libraries/net/lwip/Socket/Endpoint.h index d90d2150ea..788a0a8217 100644 --- a/libraries/net/lwip/Socket/Endpoint.h +++ b/libraries/net/lwip/Socket/Endpoint.h @@ -55,7 +55,7 @@ public: int get_port(void); protected: - char _ipAddress[16]; + char _ipAddress[17]; struct sockaddr_in _remoteHost; }; diff --git a/libraries/net/lwip/Socket/Socket.cpp b/libraries/net/lwip/Socket/Socket.cpp index 13580ff3d5..5288487f72 100644 --- a/libraries/net/lwip/Socket/Socket.cpp +++ b/libraries/net/lwip/Socket/Socket.cpp @@ -41,6 +41,14 @@ int Socket::init_socket(int type) { return 0; } +int Socket::set_option(int level, int optname, const void *optval, socklen_t optlen) { + return lwip_setsockopt(_sock_fd, level, optname, optval, optlen); +} + +int Socket::get_option(int level, int optname, void *optval, socklen_t *optlen) { + return lwip_getsockopt(_sock_fd, level, optname, optval, optlen); +} + int Socket::select(struct timeval *timeout, bool read, bool write) { fd_set fdSet; FD_ZERO(&fdSet); diff --git a/libraries/net/lwip/Socket/Socket.h b/libraries/net/lwip/Socket/Socket.h index 95c55f3b4d..0cd8c8aa69 100644 --- a/libraries/net/lwip/Socket/Socket.h +++ b/libraries/net/lwip/Socket/Socket.h @@ -47,6 +47,24 @@ public: */ void set_blocking(bool blocking, unsigned int timeout=1500); + /** Set socket options + \param level stack level (see: lwip/sockets.h) + \param optname option ID + \param optval option value + \param socklen_t length of the option value + \return 0 on success, -1 on failure + */ + int set_option(int level, int optname, const void *optval, socklen_t optlen); + + /** Get socket options + \param level stack level (see: lwip/sockets.h) + \param optname option ID + \param optval buffer pointer where to write the option value + \param socklen_t length of the option value + \return 0 on success, -1 on failure + */ + int get_option(int level, int optname, void *optval, socklen_t *optlen); + /** Close the socket file descriptor */ int close(); diff --git a/libraries/net/lwip/Socket/UDPSocket.cpp b/libraries/net/lwip/Socket/UDPSocket.cpp index 43eac9dcf8..653894cbe3 100644 --- a/libraries/net/lwip/Socket/UDPSocket.cpp +++ b/libraries/net/lwip/Socket/UDPSocket.cpp @@ -49,6 +49,21 @@ int UDPSocket::bind(int port) { return 0; } +int UDPSocket::join_multicast_group(const char* address) { + struct ip_mreq mreq; + + // Set up group address + mreq.imr_multiaddr.s_addr = inet_addr(address); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + + return set_option(IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); +} + +int UDPSocket::set_broadcasting(void) { + int option = 1; + return set_option(SOL_SOCKET, SO_BROADCAST, &option, sizeof(option)); +} + // -1 if unsuccessful, else number of bytes written int UDPSocket::sendTo(Endpoint &remote, char *packet, int length) { if (_sock_fd < 0) diff --git a/libraries/net/lwip/Socket/UDPSocket.h b/libraries/net/lwip/Socket/UDPSocket.h index a4c723bffa..a46198ffa8 100644 --- a/libraries/net/lwip/Socket/UDPSocket.h +++ b/libraries/net/lwip/Socket/UDPSocket.h @@ -45,6 +45,17 @@ public: */ int bind(int port); + /** Join the multicast group at the given address + \param address The address of the multicast group + \return 0 on success, -1 on failure. + */ + int join_multicast_group(const char* address); + + /** Set the socket in broadcasting mode + \return 0 on success, -1 on failure. + */ + int set_broadcasting(void); + /** Send a packet to a remote endpoint \param remote The remote endpoint \param packet The packet to be sent diff --git a/libraries/net/lwip/lwip/core/ipv4/igmp.c b/libraries/net/lwip/lwip/core/ipv4/igmp.c index 4e4405e16f..45bb5d95c2 100644 --- a/libraries/net/lwip/lwip/core/ipv4/igmp.c +++ b/libraries/net/lwip/lwip/core/ipv4/igmp.c @@ -139,7 +139,6 @@ static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr); static err_t igmp_remove_group(struct igmp_group *group); static void igmp_timeout( struct igmp_group *group); static void igmp_start_timer(struct igmp_group *group, u8_t max_time); -static void igmp_stop_timer(struct igmp_group *group); static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp); static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif); static void igmp_send(struct igmp_group *group, u8_t type); @@ -706,17 +705,6 @@ igmp_start_timer(struct igmp_group *group, u8_t max_time) group->timer = (LWIP_RAND() % (max_time - 1)) + 1; } -/** - * Stop a timer for an igmp_group - * - * @param group the igmp_group for which to stop the timer - */ -static void -igmp_stop_timer(struct igmp_group *group) -{ - group->timer = 0; -} - /** * Delaying membership report for a group if necessary * diff --git a/libraries/net/lwip/lwip/lwipopts.h b/libraries/net/lwip/lwip/lwipopts.h index 2454ef6aff..42e36f1112 100644 --- a/libraries/net/lwip/lwip/lwipopts.h +++ b/libraries/net/lwip/lwip/lwipopts.h @@ -59,6 +59,11 @@ #define LWIP_DHCP 1 #define LWIP_DNS 1 +// Support Multicast +#include "stdlib.h" +#define LWIP_IGMP 1 +#define LWIP_RAND() rand() + #define LWIP_COMPAT_SOCKETS 0 #define LWIP_POSIX_SOCKETS_IO_NAMES 0 #define LWIP_SO_RCVTIMEO 1 diff --git a/libraries/tests/net/helloworld/broadcast_receive/main.cpp b/libraries/tests/net/helloworld/broadcast_receive/main.cpp new file mode 100644 index 0000000000..c8d266f9e3 --- /dev/null +++ b/libraries/tests/net/helloworld/broadcast_receive/main.cpp @@ -0,0 +1,23 @@ +#include "mbed.h" +#include "EthernetInterface.h" + +const int BROADCAST_PORT = 58083; + +int main() { + EthernetInterface eth; + eth.init(); //Use DHCP + eth.connect(); + + UDPSocket socket; + socket.bind(BROADCAST_PORT); + socket.set_broadcasting(); + + Endpoint broadcaster; + char buffer[256]; + while (true) { + printf("\nWait for packet...\n"); + int n = socket.receiveFrom(broadcaster, buffer, sizeof(buffer)); + buffer[n] = '\0'; + printf("Packet from \"%s\": %s\n", broadcaster.get_address(), buffer); + } +} diff --git a/libraries/tests/net/helloworld/broadcast_send/main.cpp b/libraries/tests/net/helloworld/broadcast_send/main.cpp new file mode 100644 index 0000000000..77b5b64d02 --- /dev/null +++ b/libraries/tests/net/helloworld/broadcast_send/main.cpp @@ -0,0 +1,25 @@ +#include "mbed.h" +#include "EthernetInterface.h" + +const int BROADCAST_PORT = 58083; + +int main() { + EthernetInterface eth; + eth.init(); //Use DHCP + eth.connect(); + + UDPSocket sock; + sock.init(); + sock.set_broadcasting(); + + Endpoint broadcast; + broadcast.set_address("255.255.255.255", BROADCAST_PORT); + + char out_buffer[] = "very important data"; + + while (true) { + printf("Broadcasting...\n"); + sock.sendTo(broadcast, out_buffer, sizeof(out_buffer)); + Thread::wait(1000); + } +} diff --git a/libraries/tests/net/helloworld/multicast_receive/main.cpp b/libraries/tests/net/helloworld/multicast_receive/main.cpp new file mode 100644 index 0000000000..eeb1527c63 --- /dev/null +++ b/libraries/tests/net/helloworld/multicast_receive/main.cpp @@ -0,0 +1,27 @@ +#include "mbed.h" +#include "EthernetInterface.h" + +const char* MCAST_GRP = "224.1.1.1"; +const int MCAST_PORT = 5007; + +int main() { + EthernetInterface eth; + eth.init(); //Use DHCP + eth.connect(); + + UDPSocket server; + server.bind(MCAST_PORT); + if (server.join_multicast_group(MCAST_GRP) != 0) { + printf("Error joining the multicast group\n"); + while (true) {} + } + + Endpoint client; + char buffer[256]; + while (true) { + printf("\nWait for packet...\n"); + int n = server.receiveFrom(client, buffer, sizeof(buffer)); + + printf("Packet from \"%s\": %s\n", client.get_address(), buffer); + } +} diff --git a/libraries/tests/net/helloworld/multicast_send/main.cpp b/libraries/tests/net/helloworld/multicast_send/main.cpp new file mode 100644 index 0000000000..773ea6105a --- /dev/null +++ b/libraries/tests/net/helloworld/multicast_send/main.cpp @@ -0,0 +1,24 @@ +#include "mbed.h" +#include "EthernetInterface.h" + +const char* MCAST_GRP = "224.1.1.1"; +const int MCAST_PORT = 5007; + +int main() { + EthernetInterface eth; + eth.init(); //Use DHCP + eth.connect(); + + UDPSocket sock; + sock.init(); + + Endpoint multicast_group; + multicast_group.set_address(MCAST_GRP, MCAST_PORT); + + char out_buffer[] = "very important data"; + while (true) { + printf("Multicast to group: %s\n", MCAST_GRP); + sock.sendTo(multicast_group, out_buffer, sizeof(out_buffer)); + Thread::wait(1000); + } +} diff --git a/workspace_tools/host_tests/example/BroadcastReceive.py b/workspace_tools/host_tests/example/BroadcastReceive.py new file mode 100644 index 0000000000..1e4e580864 --- /dev/null +++ b/workspace_tools/host_tests/example/BroadcastReceive.py @@ -0,0 +1,9 @@ +import socket + +BROADCAST_PORT = 58083 + +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +s.bind(('0.0.0.0', BROADCAST_PORT)) + +while True: + print s.recvfrom(256) diff --git a/workspace_tools/host_tests/example/BroadcastSend.py b/workspace_tools/host_tests/example/BroadcastSend.py new file mode 100644 index 0000000000..f5ed6cf207 --- /dev/null +++ b/workspace_tools/host_tests/example/BroadcastSend.py @@ -0,0 +1,14 @@ +import socket +from time import sleep, time + +BROADCAST_PORT = 58083 + +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +s.bind(('', 0)) +s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + +while True: + print "Broadcasting..." + data = 'Hello World: ' + repr(time()) + '\n' + s.sendto(data, ('', BROADCAST_PORT)) + sleep(1) diff --git a/workspace_tools/host_tests/example/MulticastReceive.py b/workspace_tools/host_tests/example/MulticastReceive.py new file mode 100644 index 0000000000..0610131dcc --- /dev/null +++ b/workspace_tools/host_tests/example/MulticastReceive.py @@ -0,0 +1,15 @@ +import socket +import struct + +MCAST_GRP = '224.1.1.1' +MCAST_PORT = 5007 + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) +sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +sock.bind(('', MCAST_PORT)) +mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) + +sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + +while True: + print sock.recv(10240) diff --git a/workspace_tools/host_tests/example/MulticastSend.py b/workspace_tools/host_tests/example/MulticastSend.py new file mode 100644 index 0000000000..d89a28e2dd --- /dev/null +++ b/workspace_tools/host_tests/example/MulticastSend.py @@ -0,0 +1,14 @@ +import socket +from time import sleep, time + +MCAST_GRP = '224.1.1.1' +MCAST_PORT = 5007 + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) +sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) + +while True: + print "Multicast to group: %s\n" % MCAST_GRP + data = 'Hello World: ' + repr(time()) + '\n' + sock.sendto(data, (MCAST_GRP, MCAST_PORT)) + sleep(1) diff --git a/workspace_tools/tests.py b/workspace_tools/tests.py index 09e6e8c717..a0f1f47ac4 100644 --- a/workspace_tools/tests.py +++ b/workspace_tools/tests.py @@ -402,6 +402,30 @@ TESTS = [ "dependencies": [MBED_LIBRARIES, RTOS_LIBRARIES, ETH_LIBRARY], "supported": CORTEX_ARM_SUPPORT, }, + { + "id": "NET_9", "description": "NET: Multicast Send", + "source_dir": join(TEST_DIR, "net", "helloworld", "multicast_send"), + "dependencies": [MBED_LIBRARIES, RTOS_LIBRARIES, ETH_LIBRARY], + "supported": CORTEX_ARM_SUPPORT, + }, + { + "id": "NET_10", "description": "NET: Multicast Receive", + "source_dir": join(TEST_DIR, "net", "helloworld", "multicast_receive"), + "dependencies": [MBED_LIBRARIES, RTOS_LIBRARIES, ETH_LIBRARY], + "supported": CORTEX_ARM_SUPPORT, + }, + { + "id": "NET_11", "description": "NET: Broadcast Send", + "source_dir": join(TEST_DIR, "net", "helloworld", "broadcast_send"), + "dependencies": [MBED_LIBRARIES, RTOS_LIBRARIES, ETH_LIBRARY], + "supported": CORTEX_ARM_SUPPORT, + }, + { + "id": "NET_12", "description": "NET: Broadcast Receive", + "source_dir": join(TEST_DIR, "net", "helloworld", "broadcast_receive"), + "dependencies": [MBED_LIBRARIES, RTOS_LIBRARIES, ETH_LIBRARY], + "supported": CORTEX_ARM_SUPPORT, + }, # Vodafone tests {