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.1  2006/09/07 09:06:17  haraldkipp
00037  * Discovery service added.
00038  *
00039  */
00040 
00041 #include <sys/confnet.h>
00042 #include <sys/confos.h>
00043 #include <sys/thread.h>
00044 #include <sys/timer.h>
00045 #include <sys/socket.h>
00046 
00047 #include <netinet/in.h>
00048 #include <net/if_var.h>
00049 
00050 #include <stdlib.h>
00051 #include <string.h>
00052 
00053 #include <pro/discover.h>
00054 
00059 
00060 #ifndef NUT_THREAD_DISTSTACK
00061 #if defined(__AVR__)
00062 #define NUT_THREAD_DISTSTACK  384
00063 #else
00064 #define NUT_THREAD_DISTSTACK  512
00065 #endif
00066 #endif
00067 
00068 #ifndef DISCOVERY_PORT
00069 #define DISCOVERY_PORT  9806
00070 #endif
00071 
00072 typedef struct {
00073     u_long disopt_ipmask;
00074     u_short disopt_port;
00075     u_int disopt_flags;
00076 } DISCOVERY_OPTIONS;
00077 
00078 static DISCOVERY_OPTIONS disopt;
00079 static u_long xid;
00080 
00081 static int NutDiscoveryHandler(u_long ip, u_short port, DISCOVERY_TELE * dist, int len);
00082 static NutDiscoveryCallback discovery_callback = NutDiscoveryHandler;
00083 
00091 int NutDiscoveryAnnTele(DISCOVERY_TELE * dist)
00092 {
00093     memset(dist, 0, sizeof(DISCOVERY_TELE));
00094     dist->dist_xid = xid;
00095     dist->dist_type = DIST_ANNOUNCE;
00096     dist->dist_ver = DISCOVERY_VERSION;
00097     memcpy(dist->dist_mac, confnet.cdn_mac, sizeof(dist->dist_mac));
00098     dist->dist_ip_addr = confnet.cdn_ip_addr;
00099     dist->dist_ip_mask = confnet.cdn_ip_mask;
00100     dist->dist_gateway = confnet.cdn_gateway;
00101     dist->dist_cip_addr = confnet.cdn_cip_addr;
00102     memcpy(dist->dist_hostname, confos.hostname, sizeof(dist->dist_hostname));
00103 
00104     return sizeof(DISCOVERY_TELE) - sizeof(dist->dist_custom);
00105 }
00106 
00114 int NutDiscoveryAppConf(DISCOVERY_TELE * dist)
00115 {
00116     memcpy(confos.hostname, dist->dist_hostname, sizeof(confos.hostname));
00117     NutSaveConfig();
00118 
00119     memcpy(confnet.cdn_mac, dist->dist_mac, sizeof(confnet.cdn_mac));
00120     confnet.cdn_ip_addr = dist->dist_ip_addr;
00121     confnet.cdn_ip_mask = dist->dist_ip_mask;
00122     confnet.cdn_gateway = dist->dist_gateway;
00123     confnet.cdn_cip_addr = dist->dist_cip_addr;
00124 
00125     return NutNetSaveConfig();
00126 }
00127 
00128 /*
00129  * \brief Default discovery datagram handler.
00130  *
00131  * \param ip    Sender's IP address.
00132  * \param port  Sender's UDP port.
00133  * \param dtel  Pointer to the UDP telegram buffer.
00134  * \param len   UDP telegram size.
00135  */
00136 static int NutDiscoveryHandler(u_long ip, u_short port, DISCOVERY_TELE * dist, int len)
00137 {
00138     int rc = -1;
00139 
00140     /* Check minimum datagram length. */
00141     if (len >= sizeof(DISCOVERY_TELE) - sizeof(dist->dist_custom)) {
00142         if (dist->dist_type == DIST_REQUEST) {
00143             /* Respond to requests. */
00144             rc = NutDiscoveryAnnTele(dist);
00145         } else if (dist->dist_type == DIST_APPLY        /* Apply telegram. */
00146                    && dist->dist_xid == xid     /* Check exchange ID. */
00147                    && dist->dist_ver >= DISCOVERY_VERSION) {    /* Minimum version required. */
00148             xid += NutGetTickCount();
00149             /* Store configuration. */
00150             rc = NutDiscoveryAppConf(dist);
00151         }
00152     }
00153     return rc;
00154 }
00155 
00156 THREAD(DiscoveryResponder, arg)
00157 {
00158     UDPSOCKET *sock;
00159     DISCOVERY_TELE *dist;
00160     u_long raddr;
00161     u_short rport;
00162     int len;
00163 
00164     /* Insist on allocating a datagram buffer. */
00165     while ((dist = malloc(sizeof(DISCOVERY_TELE))) == NULL) {
00166         NutSleep(1000);
00167     }
00168 
00169     /* Insist on creating a socket. */
00170     while ((sock = NutUdpCreateSocket(disopt.disopt_port)) == NULL) {
00171         NutSleep(1000);
00172     }
00173 
00174     /* Nut/Net doesn't provide UDP datagram buffering by default. */
00175     {
00176         u_short max_ms = sizeof(DISCOVERY_TELE) * 3;
00177 
00178         NutUdpSetSockOpt(sock, SO_RCVBUF, &max_ms, sizeof(max_ms));
00179     }
00180 
00181     /* Create a pseudo random telegram identifier. */
00182     memcpy(&xid, &confnet.cdn_mac[2], sizeof(xid));
00183     xid += NutGetTickCount();
00184 
00185     /* Optionally send initial announce datagram. */
00186     if (disopt.disopt_flags & DISF_INITAL_ANN) {
00187         /* Variable sleep avoids broadcast storms in large installations. */
00188         NutSleep(500 + (xid & 0x7FF));
00189         if ((len = NutDiscoveryAnnTele(dist)) > 0) {
00190             NutUdpSendTo(sock, INADDR_BROADCAST, disopt.disopt_port, dist, len);
00191         }
00192     }
00193 
00194     /* Handle discovery telegrams in an endless loop. */
00195     for (;;) {
00196         len = NutUdpReceiveFrom(sock, &raddr, &rport, dist, sizeof(DISCOVERY_TELE), 0);
00197         /* Updates may be filtered by an IP mask. */
00198         if ((raddr & disopt.disopt_ipmask) == raddr && len > 0 && discovery_callback) {
00199             if ((len = discovery_callback(raddr, rport, dist, len)) > 0) {
00200                 /* Broadcast response if destination is unreachable. */
00201                 if ((raddr & confnet.cdn_ip_mask) != (confnet.cdn_ip_addr & confnet.cdn_ip_mask)) {
00202                     raddr = INADDR_BROADCAST;
00203                 }
00204                 NutUdpSendTo(sock, raddr, disopt.disopt_port, dist, len);
00205             }
00206         }
00207         /* Avoid system blocking by datagram storms. */
00208         NutSleep(100);
00209     }
00210 }
00211 
00226 NutDiscoveryCallback NutRegisterDiscoveryCallback(NutDiscoveryCallback func)
00227 {
00228     NutDiscoveryCallback rc = discovery_callback;
00229 
00230     discovery_callback = func;
00231 
00232     return rc;
00233 }
00234 
00252 int NutRegisterDiscovery(u_long ipmask, u_short port, u_int flags)
00253 {
00254     static HANDLE tid = NULL;
00255 
00256     if (tid == NULL) {
00257         disopt.disopt_ipmask = ipmask;
00258         disopt.disopt_port = port ? port : DISCOVERY_PORT;
00259         disopt.disopt_flags = flags;
00260         tid = NutThreadCreate("udisc", DiscoveryResponder, NULL, NUT_THREAD_DISTSTACK);
00261         if (tid) {
00262             return 0;
00263         }
00264     }
00265     return -1;
00266 }
00267 

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