discover.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00034 /*
00035  * $Log: discover.c,v $
00036  * Revision 1.2  2008/08/11 07:00:35  haraldkipp
00037  * BSD types replaced by stdint types (feature request #1282721).
00038  *
00039  * Revision 1.1  2006/09/07 09:06:17  haraldkipp
00040  * Discovery service added.
00041  *
00042  */
00043 
00044 #include <sys/confnet.h>
00045 #include <sys/confos.h>
00046 #include <sys/thread.h>
00047 #include <sys/timer.h>
00048 #include <sys/socket.h>
00049 
00050 #include <netinet/in.h>
00051 #include <net/if_var.h>
00052 
00053 #include <stdlib.h>
00054 #include <string.h>
00055 
00056 #include <pro/discover.h>
00057 
00062 
00063 #ifndef NUT_THREAD_DISTSTACK
00064 #if defined(__AVR__)
00065 #define NUT_THREAD_DISTSTACK  384
00066 #else
00067 #define NUT_THREAD_DISTSTACK  512
00068 #endif
00069 #endif
00070 
00071 #ifndef DISCOVERY_PORT
00072 #define DISCOVERY_PORT  9806
00073 #endif
00074 
00075 typedef struct {
00076     uint32_t disopt_ipmask;
00077     uint16_t disopt_port;
00078     u_int disopt_flags;
00079 } DISCOVERY_OPTIONS;
00080 
00081 static DISCOVERY_OPTIONS disopt;
00082 static uint32_t xid;
00083 
00084 static int NutDiscoveryHandler(uint32_t ip, uint16_t port, DISCOVERY_TELE * dist, int len);
00085 static NutDiscoveryCallback discovery_callback = NutDiscoveryHandler;
00086 
00094 int NutDiscoveryAnnTele(DISCOVERY_TELE * dist)
00095 {
00096     memset(dist, 0, sizeof(DISCOVERY_TELE));
00097     dist->dist_xid = xid;
00098     dist->dist_type = DIST_ANNOUNCE;
00099     dist->dist_ver = DISCOVERY_VERSION;
00100     memcpy(dist->dist_mac, confnet.cdn_mac, sizeof(dist->dist_mac));
00101     dist->dist_ip_addr = confnet.cdn_ip_addr;
00102     dist->dist_ip_mask = confnet.cdn_ip_mask;
00103     dist->dist_gateway = confnet.cdn_gateway;
00104     dist->dist_cip_addr = confnet.cdn_cip_addr;
00105     memcpy(dist->dist_hostname, confos.hostname, sizeof(dist->dist_hostname));
00106 
00107     return sizeof(DISCOVERY_TELE) - sizeof(dist->dist_custom);
00108 }
00109 
00117 int NutDiscoveryAppConf(DISCOVERY_TELE * dist)
00118 {
00119     memcpy(confos.hostname, dist->dist_hostname, sizeof(confos.hostname));
00120     NutSaveConfig();
00121 
00122     memcpy(confnet.cdn_mac, dist->dist_mac, sizeof(confnet.cdn_mac));
00123     confnet.cdn_ip_addr = dist->dist_ip_addr;
00124     confnet.cdn_ip_mask = dist->dist_ip_mask;
00125     confnet.cdn_gateway = dist->dist_gateway;
00126     confnet.cdn_cip_addr = dist->dist_cip_addr;
00127 
00128     return NutNetSaveConfig();
00129 }
00130 
00131 /*
00132  * \brief Default discovery datagram handler.
00133  *
00134  * \param ip    Sender's IP address.
00135  * \param port  Sender's UDP port.
00136  * \param dtel  Pointer to the UDP telegram buffer.
00137  * \param len   UDP telegram size.
00138  */
00139 static int NutDiscoveryHandler(uint32_t ip, uint16_t port, DISCOVERY_TELE * dist, int len)
00140 {
00141     int rc = -1;
00142 
00143     /* Check minimum datagram length. */
00144     if (len >= sizeof(DISCOVERY_TELE) - sizeof(dist->dist_custom)) {
00145         if (dist->dist_type == DIST_REQUEST) {
00146             /* Respond to requests. */
00147             rc = NutDiscoveryAnnTele(dist);
00148         } else if (dist->dist_type == DIST_APPLY        /* Apply telegram. */
00149                    && dist->dist_xid == xid     /* Check exchange ID. */
00150                    && dist->dist_ver >= DISCOVERY_VERSION) {    /* Minimum version required. */
00151             xid += NutGetTickCount();
00152             /* Store configuration. */
00153             rc = NutDiscoveryAppConf(dist);
00154         }
00155     }
00156     return rc;
00157 }
00158 
00159 THREAD(DiscoveryResponder, arg)
00160 {
00161     UDPSOCKET *sock;
00162     DISCOVERY_TELE *dist;
00163     uint32_t raddr;
00164     uint16_t rport;
00165     int len;
00166 
00167     /* Insist on allocating a datagram buffer. */
00168     while ((dist = malloc(sizeof(DISCOVERY_TELE))) == NULL) {
00169         NutSleep(1000);
00170     }
00171 
00172     /* Insist on creating a socket. */
00173     while ((sock = NutUdpCreateSocket(disopt.disopt_port)) == NULL) {
00174         NutSleep(1000);
00175     }
00176 
00177     /* Nut/Net doesn't provide UDP datagram buffering by default. */
00178     {
00179         uint16_t max_ms = sizeof(DISCOVERY_TELE) * 3;
00180 
00181         NutUdpSetSockOpt(sock, SO_RCVBUF, &max_ms, sizeof(max_ms));
00182     }
00183 
00184     /* Create a pseudo random telegram identifier. */
00185     memcpy(&xid, &confnet.cdn_mac[2], sizeof(xid));
00186     xid += NutGetTickCount();
00187 
00188     /* Optionally send initial announce datagram. */
00189     if (disopt.disopt_flags & DISF_INITAL_ANN) {
00190         /* Variable sleep avoids broadcast storms in large installations. */
00191         NutSleep(500 + (xid & 0x7FF));
00192         if ((len = NutDiscoveryAnnTele(dist)) > 0) {
00193             NutUdpSendTo(sock, INADDR_BROADCAST, disopt.disopt_port, dist, len);
00194         }
00195     }
00196 
00197     /* Handle discovery telegrams in an endless loop. */
00198     for (;;) {
00199         len = NutUdpReceiveFrom(sock, &raddr, &rport, dist, sizeof(DISCOVERY_TELE), 0);
00200         /* Updates may be filtered by an IP mask. */
00201         if ((raddr & disopt.disopt_ipmask) == raddr && len > 0 && discovery_callback) {
00202             if ((len = discovery_callback(raddr, rport, dist, len)) > 0) {
00203                 /* Broadcast response if destination is unreachable. */
00204                 if ((raddr & confnet.cdn_ip_mask) != (confnet.cdn_ip_addr & confnet.cdn_ip_mask)) {
00205                     raddr = INADDR_BROADCAST;
00206                 }
00207                 NutUdpSendTo(sock, raddr, disopt.disopt_port, dist, len);
00208             }
00209         }
00210         /* Avoid system blocking by datagram storms. */
00211         NutSleep(100);
00212     }
00213 }
00214 
00229 NutDiscoveryCallback NutRegisterDiscoveryCallback(NutDiscoveryCallback func)
00230 {
00231     NutDiscoveryCallback rc = discovery_callback;
00232 
00233     discovery_callback = func;
00234 
00235     return rc;
00236 }
00237 
00255 int NutRegisterDiscovery(uint32_t ipmask, uint16_t port, u_int flags)
00256 {
00257     static HANDLE tid = NULL;
00258 
00259     if (tid == NULL) {
00260         disopt.disopt_ipmask = ipmask;
00261         disopt.disopt_port = port ? port : DISCOVERY_PORT;
00262         disopt.disopt_flags = flags;
00263         tid = NutThreadCreate("udisc", DiscoveryResponder, NULL, NUT_THREAD_DISTSTACK);
00264         if (tid) {
00265             return 0;
00266         }
00267     }
00268     return -1;
00269 }
00270 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/