From d660afc8fd83a630d0a223dd2796003024ce4f11 Mon Sep 17 00:00:00 2001 From: patacongo Date: Wed, 12 Sep 2012 18:42:07 +0000 Subject: Network discover utility from Max Holtzberg git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5137 42af7a65-404d-4744-a932-0658087f49c3 --- apps/netutils/discover/Kconfig | 39 ++++ apps/netutils/discover/Makefile | 100 +++++++++ apps/netutils/discover/README.txt | 9 + apps/netutils/discover/discover.c | 456 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 604 insertions(+) create mode 100644 apps/netutils/discover/Kconfig create mode 100644 apps/netutils/discover/Makefile create mode 100644 apps/netutils/discover/README.txt create mode 100644 apps/netutils/discover/discover.c (limited to 'apps/netutils/discover') diff --git a/apps/netutils/discover/Kconfig b/apps/netutils/discover/Kconfig new file mode 100644 index 000000000..336550511 --- /dev/null +++ b/apps/netutils/discover/Kconfig @@ -0,0 +1,39 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +config NETUTILS_DISCOVER + bool "Network Discovery Utility" + default n + depends on NET_UDP + ---help--- + Tool for discovering devices on the local network per UDP broadcast. + +if NETUTILS_DISCOVER + +DISCOVER_STACK_SIZE + int "Discover Daemon Stack Size" + default 1024 + +DISCOVER_PRIORITY + int "Discover Daemon Priority" + default 50 + +DISCOVER_PORT + int "Discover Daemon Port Number" + default 96 + +DISCOVER_INTERFACE + int "Network Interface Name" + default "eth0" + +DISCOVER_DEVICE_CLASS + hex "Network Discovery Class" + default 0xff + +CONFIG_DISCOVER_DESCR + string "Discoverer Description" + default "NuttX" + +endif diff --git a/apps/netutils/discover/Makefile b/apps/netutils/discover/Makefile new file mode 100644 index 000000000..52099b439 --- /dev/null +++ b/apps/netutils/discover/Makefile @@ -0,0 +1,100 @@ +############################################################################ +# apps/netutils/discover/Makefile +# +# Copyright (C) 2012 Max Holtzberg. All rights reserved. +# Copyright (C) 2011-2012 Gregory Nutt. All rights reserved. +# +# Authors: Max Holtzberg +# Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +# Telnet daemon + +ASRCS = +CSRCS = + +ifeq ($(CONFIG_NET_UDP),y) +CSRCS += discover.c +endif + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) + +ifeq ($(WINTOOL),y) + BIN = "${shell cygpath -w $(APPDIR)/libapps$(LIBEXT)}" +else + BIN = "$(APPDIR)/libapps$(LIBEXT)" +endif + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: context depend clean distclean + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + @( for obj in $(OBJS) ; do \ + $(call ARCHIVE, $(BIN), $${obj}); \ + done ; ) + @touch .built + +context: + +.depend: Makefile $(SRCS) + @$(MKDEP) $(ROOTDEPPATH) $(CC) -- $(CFLAGS) -- $(SRCS) >Make.dep + @touch $@ + +depend: .depend + +clean: + @rm -f *.o *~ .*.swp .built + $(call CLEAN) + +distclean: clean + @rm -f Make.dep .depend + +-include Make.dep diff --git a/apps/netutils/discover/README.txt b/apps/netutils/discover/README.txt new file mode 100644 index 000000000..11aab8bf3 --- /dev/null +++ b/apps/netutils/discover/README.txt @@ -0,0 +1,9 @@ +apps/netutils/discover README.txt +================================= + +This daemon is useful for discovering devices in local networks, especially +with DHCP configured devices. It listens for UDP broadcasts which also can +include a device class so that groups of devices can be discovered. It is +also possible to address all classes with a kind of broadcast discover. + +See nuttx/tools/discover.py for a client example. diff --git a/apps/netutils/discover/discover.c b/apps/netutils/discover/discover.c new file mode 100644 index 000000000..67df02b3f --- /dev/null +++ b/apps/netutils/discover/discover.c @@ -0,0 +1,456 @@ +/**************************************************************************** + * examples/discover/main.c + * + * Copyright (C) 2012 Max Holtzberg. All rights reserved. + * Copyright (C) 2008, 2011-2012 Gregory Nutt. All rights reserved. + * + * Authors: Max Holtzberg + * Gregory Nutt + * + * This code is derived from the netutils/dhcpd code. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ + +#ifndef CONFIG_DISCOVER_STACK_SIZE +# define CONFIG_DISCOVER_STACK_SIZE 1024 +#endif + +#ifndef CONFIG_DISCOVER_PRIORITY +# define CONFIG_DISCOVER_PRIORITY SCHED_PRIORITY_DEFAULT +#endif + +#ifndef CONFIG_DISCOVER_PORT +# define CONFIG_DISCOVER_PORT 96 +#endif + +#ifndef CONFIG_DISCOVER_INTERFACE +# define CONFIG_DISCOVER_INTERFACE "eth0" +#endif + +#ifndef CONFIG_DISCOVER_DEVICE_CLASS +# define CONFIG_DISCOVER_DEVICE_CLASS DISCOVER_ALL +#endif + +#ifndef CONFIG_DISCOVER_DESCR +# define CONFIG_DISCOVER_DESCR CONFIG_ARCH_BOARD +#endif + +/* Internal Definitions *****************************************************/ +/* Discover request packet format: + * Byte Description + * 0 Protocol indentifier (0x99) + * 1 Request command 0x01 + * 2 Destination device class (For querying subsets of available devices) + * 0xff for all devices + * 3 Checksum (Byte 0 - Byte 1 - Byte n) & 0xff + */ + +/* Discover response packet format: + * Byte Description + * 0 Protocol indentifier (0x99) + * 1 Reponse command (0x02) + * 2-33 Device description string with 0 bytes filled + * 34 Checksum (Byte 0 - Byte 1 - Byte n) & 0xff + */ + +#define DISCOVER_PROTO_ID 0x99 +#define DISCOVER_REQUEST 0x01 +#define DISCOVER_RESPONSE 0x02 +#define DISCOVER_ALL 0xff +#define DISCOVER_REQUEST_SIZE 4 +#define DISCOVER_RESPONSE_SIZE 35 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef uint8_t request_t[DISCOVER_REQUEST_SIZE]; +typedef uint8_t response_t[DISCOVER_RESPONSE_SIZE]; + +struct discover_state_s +{ + in_addr_t serverip; + request_t request; + response_t response; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +struct discover_state_s g_state; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int discover_daemon(int argc, char *argv[]); +static inline int discover_socket(void); +static inline int discover_openlistener(void); +static inline int discover_openresponder(void); +static inline int discover_parse(request_t packet); +static inline int discover_respond(in_addr_t *ipaddr); +static inline void discover_initresponse(void); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline void discover_initresponse() +{ + int chk = 0; + int i; + + g_state.response[0] = DISCOVER_PROTO_ID; + g_state.response[1] = DISCOVER_RESPONSE; + + strncpy((char*) &g_state.response[2], CONFIG_DISCOVER_DESCR, + DISCOVER_RESPONSE_SIZE-3); + + for (i = 0; i < DISCOVER_RESPONSE_SIZE-1; i++) + { + chk -= g_state.response[i]; + } + + /* Append check sum */ + + g_state.response[DISCOVER_RESPONSE_SIZE-1] = chk & 0xff; +} + +static int discover_daemon(int argc, char *argv[]) +{ + int sockfd = -1; + int nbytes; + int addrlen = sizeof(struct sockaddr_in); + struct sockaddr_in srcaddr; + + memset(&g_state, 0, sizeof(struct discover_state_s)); + discover_initresponse(); + + nvdbg("Started\n"); + + for (;;) + { + /* Create a socket to listen for requests from DHCP clients */ + + if (sockfd < 0) + { + sockfd = discover_openlistener(); + if (sockfd < 0) + { + ndbg("Failed to create socket\n"); + break; + } + } + + /* Read the next packet */ + + nbytes = recvfrom(sockfd, &g_state.request, sizeof(g_state.request), 0, + (struct sockaddr*) &srcaddr, + (socklen_t *) &addrlen); + if (nbytes < 0) + { + /* On errors (other EINTR), close the socket and try again */ + + ndbg("recv failed: %d\n", errno); + if (errno != EINTR) + { + close(sockfd); + sockfd = -1; + } + continue; + } + + if (discover_parse(g_state.request) != OK) + { + continue; + } + + ndbg("Received discover from %08lx'\n", srcaddr.sin_addr.s_addr); + + discover_respond(&srcaddr.sin_addr.s_addr); + } + + return OK; +} + +static inline int discover_parse(request_t packet) +{ + int i; + uint8_t chk = 0; + + if (packet[0] != DISCOVER_PROTO_ID) + { + ndbg("Wrong protocol id: %d\n", packet[0]); + return ERROR; + } + + if (packet[1] != DISCOVER_REQUEST) + { + ndbg("Wrong command: %d\n", packet[1]); + return ERROR; + } + + if (packet[2] == 0xff || packet[2] == CONFIG_DISCOVER_DEVICE_CLASS) + { + for (i = 0; i < DISCOVER_REQUEST_SIZE-1; i++) + chk -= packet[i]; + + if ((chk & 0xff) != packet[3]) + { + ndbg("Checksum does not match: %d\n", packet[3]); + return ERROR; + } + else + { + return OK; + } + } + return ERROR; +} + +static inline int discover_respond(in_addr_t *ipaddr) +{ + struct sockaddr_in addr; + int sockfd; + int ret; + + sockfd = discover_openresponder(); + if (sockfd >= 0) + { + /* Then send the reponse to the DHCP client port at that address */ + + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = HTONS(CONFIG_DISCOVER_PORT); + addr.sin_addr.s_addr = *ipaddr; + + ret = sendto(sockfd, &g_state.response, sizeof(g_state.response), 0, + (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); + if (ret < 0) + { + ndbg("Could not send discovery response: %d\n", errno); + } + + close(sockfd); + } + + return ret; +} + +static inline int discover_socket() +{ + int sockfd; +#if defined(HAVE_SO_REUSEADDR) || defined(HAVE_SO_BROADCAST) + int optval; + int ret; +#endif + + /* Create a socket to listen for requests from DHCP clients */ + + sockfd = socket(PF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + { + ndbg("socket failed: %d\n", errno); + return ERROR; + } + + /* Configure the socket */ + +#ifdef HAVE_SO_REUSEADDR + optval = 1; + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(int)); + if (ret < 0) + { + ndbg("setsockopt SO_REUSEADDR failed: %d\n", errno); + close(sockfd); + return ERROR; + } +#endif + +#ifdef HAVE_SO_BROADCAST + optval = 1; + ret = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (void*)&optval, sizeof(int)); + if (ret < 0) + { + ndbg("setsockopt SO_BROADCAST failed: %d\n", errno); + close(sockfd); + return ERROR; + } +#endif + + return sockfd; +} + +static inline int discover_openlistener() +{ + struct sockaddr_in addr; + struct ifreq req; + int sockfd; + int ret; + + /* Create a socket to listen for requests from DHCP clients */ + + sockfd = discover_socket(); + if (sockfd < 0) + { + ndbg("socket failed: %d\n", errno); + return ERROR; + } + + /* Get the IP address of the selected device */ + + strncpy(req.ifr_name, CONFIG_DISCOVER_INTERFACE, IFNAMSIZ); + ret = ioctl(sockfd, SIOCGIFADDR, (unsigned long)&req); + if (ret < 0) + { + ndbg("setsockopt SIOCGIFADDR failed: %d\n", errno); + close(sockfd); + return ERROR; + } + g_state.serverip = ((struct sockaddr_in*)&req.ifr_addr)->sin_addr.s_addr; + nvdbg("serverip: %08lx\n", ntohl(g_state.serverip)); + + /* Bind the socket to a local port. We have to bind to INADDRY_ANY to + * receive broadcast messages. + */ + + addr.sin_family = AF_INET; + addr.sin_port = htons(CONFIG_DISCOVER_PORT); + addr.sin_addr.s_addr = INADDR_ANY; + + ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); + if (ret < 0) + { + ndbg("bind failed, port=%d addr=%08lx: %d\n", + addr.sin_port, (long)addr.sin_addr.s_addr, errno); + close(sockfd); + return ERROR; + } + + return sockfd; +} + +static inline int discover_openresponder(void) +{ + struct sockaddr_in addr; + int sockfd; + int ret; + + /* Create a socket for responding to discovery message */ + + sockfd = discover_socket(); + if (sockfd < 0) + { + ndbg("socket failed: %d\n", errno); + return ERROR; + } + + /* Bind the socket to a local port.*/ + + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = g_state.serverip; + + ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); + if (ret < 0) + { + ndbg("bind failed, port=%d addr=%08lx: %d\n", + addr.sin_port, (long)addr.sin_addr.s_addr, errno); + close(sockfd); + return ERROR; + } + + return sockfd; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: discover_start + * + * Description: + * Start the discover daemon. + * + * Return: + * The process ID (pid) of the new discover daemon is returned on + * success; A negated errno is returned if the daemon was not successfully + * started. + * + ****************************************************************************/ + +int discover_start() +{ + pid_t pid; + + /* Then start the new daemon */ + + pid = TASK_CREATE("Discover daemon", CONFIG_DISCOVER_PRIORITY, + CONFIG_DISCOVER_STACK_SIZE, discover_daemon, NULL); + if (pid < 0) + { + int errval = errno; + ndbg("Failed to start the discover daemon: %d\n", errval); + return -errval; + } + + /* Return success */ + + return pid; +} -- cgit v1.2.3