dhcp.c

Go to the documentation of this file.
00001 /*##############################################################################
00002 
00003 nIP - nano IP stack
00004 
00005 File        : dhcp.c
00006 
00007 Description : Dynamic Host Configuration Protocol Client
00008 
00009 Copyright notice:
00010 
00011 Copyright (C) 2005 -
00012 Andreas Dittrich, dittrich@informatik.hu-berlin.de
00013 Jon Kowal, kowal@informatik.hu-berlin.de
00014 
00015 This program is free software; you can redistribute it and/or
00016 modify it under the terms of the GNU General Public License
00017 as published by the Free Software Foundation; either version 2
00018 of the License, or (at your option) any later version.
00019 
00020 This program is distributed in the hope that it will be useful,
00021 but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00023 GNU General Public License for more details.
00024 
00025 You should have received a copy of the GNU General Public License
00026 along with this program; if not, write to the Free Software
00027 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00028 
00029 ##############################################################################*/
00030 
00031 #include "nip_init.h"
00032 #include "dispatcher.h"
00033 #include "inet.h"
00034 #include "os_core.h"
00035 #include "nip_error.h"
00036 #include "net/net_if.h"
00037 #include "net/udp.h"
00038 #include "app/dhcp.h"
00039 #include "app/ports.h"
00040 
00041 
00042 #if (NIP_DHCP_ENABLE==1)
00043 
00044 #define DHCPMSGSIZE sizeof( struct nip_dhcp_packet )
00045 
00046 /** dhcp configuration variables for each interface */
00047 
00048 #if NIP_NET_IF_COUNT > 1
00049         struct nip_dhcp_config nip_dhcp_conf[ NIP_NET_IF_COUNT ];
00050         #define NIP_DHCP_CONF( if_id ) nip_dhcp_conf[if_id]
00051 #else
00052         struct nip_dhcp_config nip_dhcp_conf;
00053         #define NIP_DHCP_CONF( if_id ) nip_dhcp_conf
00054 #endif
00055 nip_udp_sock_id_t      nip_dhcp_sock = NIP_UDP_NO_SOCKET;
00056 
00057 
00058 /** enable dhcp for given network interface */
00059 void nip_dhcp_start( nip_net_if_id_t net_if )
00060 {
00061         NIP_DHCP_CONF( net_if ).state = NIP_DHCP_STAT_INIT;
00062 
00063         nip_disp_notify_if( net_if, NIP_DISP_IF_DHCP );
00064 }
00065 
00066 /** disable dhcp for given network interface */
00067 void nip_dhcp_disable( nip_net_if_id_t net_if )
00068 {
00069         NIP_DHCP_CONF( net_if ).state = NIP_DHCP_STAT_DISABLED;
00070 }
00071 
00072 /** Dispatcher function to initialize DHCP UDP socket.
00073  *
00074  * On successfull socket setup the function will set the next-pointer to
00075  * nip_dhcp_disp_check(). The socket setup may fail if the nip stack is out of
00076  * space for UDP sockets. In that case nip_dhcp_disp_check() will not be called.
00077  */
00078 void nip_dhcp_disp_sock_init( void )
00079 {
00080         struct nip_udp_sock_addr addr;
00081 
00082         // Set socket to listen DHCP CLIENT port on all interfaces
00083         addr.port = NIP_UDP_PORT_DHCPCLIENT;
00084         addr.ip[0] = 0;
00085         addr.ip[1] = 0;
00086         addr.ip[2] = 0;
00087         addr.ip[3] = 0;
00088 
00089         nip_dhcp_sock = nip_udp_socket(
00090                 &addr,
00091                 NIP_UDP_SOCK_FLG_LISTENING | NIP_UDP_SOCK_FLG_NON_BLOCK
00092         );
00093 
00094         // if socket was created successfully, continue with nip_dhcp_disp_check()
00095 /*      if ( nip_dhcp_sock != NIP_UDP_NO_SOCKET )
00096         {
00097                 NIP_CURR_CMD = &nip_dhcp_disp_check;
00098         }*/
00099 }
00100 
00101 /** @return pointer to option in DHCP message or NULL if option was not found. */
00102 struct nip_dhcp_option *nip_dhcp_find_option( uint8_t opttype, struct nip_dhcp_packet *msg, nip_mem_size_t msg_size )
00103 {
00104         uint8_t  i = 0;
00105 
00106 
00107         while ( msg->options[i] != DHCP_OPTION_END )
00108         {
00109                 // out of space (buffer overflow?)
00110                 if ( &msg->options[i+msg->options[i+1]+2] > &((uint8_t*)msg)[msg_size] )
00111                         break;
00112 
00113                 // padding?
00114                 if ( msg->options[i] == DHCP_OPTION_PADDING )
00115                 {
00116                         i++;
00117                         continue;
00118                 }
00119 
00120                 // check option at point i
00121                 if (msg->options[i]==opttype){
00122                         // search was successful
00123                         return (struct nip_dhcp_option*) &msg->options[i];
00124                 } else{
00125                         // advance to next option
00126                         i+=msg->options[i+1]+2;
00127                 }
00128         }
00129 
00130         return NULL;
00131 }
00132 
00133 /** Dispatcher function to be called to check dhcp status.
00134  */
00135 void nip_dhcp_disp_check( void )
00136 {
00137         nip_net_if_id_t         if_id  = NIP_CURR_IF;
00138         nip_net_if_t           *net_if = & nip_net_if_list[ if_id ];
00139 
00140         // use different mechanisms to access dhcp configuration for the single and
00141         // the multi interface cases to minimize use of program memory in either case
00142 #if NIP_NET_IF_COUNT > 1
00143         struct nip_dhcp_config *conf   = & NIP_DHCP_CONF( if_id );
00144         #define CONF_ATTR(attr) conf-> attr
00145 #else
00146         #define CONF_ATTR(attr) nip_dhcp_conf.attr
00147 #endif
00148 
00149         struct nip_dhcp_packet *msg;
00150         struct nip_dhcp_option *opt;
00151         nip_mem_size_t         msg_size;
00152         uint8_t                i;
00153         uint8_t                optcode;
00154         void                   *addr;
00155         nip_time_t             tc = nip_tickcount();
00156 
00157         // ***********************************************************************
00158         // C h e c k   i n c o m i n g   m e s s a g e s
00159         while ( 0 < (msg_size = nip_udp_recvfrom( nip_dhcp_sock, NULL, 0, &CONF_ATTR(serv_addr) )) )
00160         {
00161 
00162                 // get pointer to packet data
00163                 msg = nip_udp_data_ptr( nip_dhcp_sock, NIP_UDP_RX_BUF );
00164 
00165                 if ( msg != NULL )
00166                 {
00167                         opt = nip_dhcp_find_option( DHCP_OPTION_MSGTYPE, msg, msg_size );
00168 
00169                         // check message xid, message op-code and type
00170                         if ( msg->xid == CONF_ATTR(xid)
00171                                 && msg->op == NIP_DHCP_OP_REPLY
00172                                 && opt != NULL )
00173                         {
00174                                 switch ( opt->value.msgtype )
00175                                 {
00176                                         case DHCP_OPTION_MSGTYPE_OFFER:
00177                                                 if ( CONF_ATTR(state) == NIP_DHCP_STAT_SELECTING )
00178                                                 {
00179                                                         // build Request
00180                                                         opt->value.msgtype = DHCP_OPTION_MSGTYPE_REQUEST;
00181                                                         nip_memcpy( msg->ciaddr, msg->yiaddr, 4 );
00182                                                         nip_memset( msg->yiaddr, 0, 4 );
00183 
00184                                                         // forward msg to outgoing queue. The msg pointer stays the
00185                                                         // same but now stands for the transmission buffer and will
00186                                                         // be freed later, before transmission.
00187                                                         nip_udp_forward( nip_dhcp_sock );
00188 
00189                                                         CONF_ATTR(state) = NIP_DHCP_STAT_REQUESTING;
00190                                                         goto transmit;
00191                                                 }
00192                                                 break;
00193                                         case DHCP_OPTION_MSGTYPE_ACK:
00194 
00195                                                 // check if current state accepts ACK messages
00196                                                 if ( CONF_ATTR(state) == NIP_DHCP_STAT_REQUESTING
00197                                                         || CONF_ATTR(state) == NIP_DHCP_STAT_REBINDING
00198                                                         || CONF_ATTR(state) == NIP_DHCP_STAT_RENEWING )
00199                                                 {
00200                                                         // configure network interface
00201                                                         // set IP address
00202                                                         nip_ip_addr_set( if_id, msg->yiaddr );
00203                                                         // set Subnet mask, if that option has been set by server
00204                                                         opt = nip_dhcp_find_option( DHCP_OPTION_SUBNETMASK, msg, msg_size );
00205                                                         if ( opt != NULL && opt->valsize == 4  )
00206                                                                 nip_ip_subnet_set( if_id, opt->value.ipaddr );
00207 
00208                                                         // read options for router, nameserver and lease times
00209                                                         i = 1;
00210                                                         do
00211                                                         {
00212                                                                 // save condition for i==1
00213                                                                 addr    = net_if->ip_conf.std_gateway;
00214                                                                 optcode = DHCP_OPTION_ROUTER;
00215 
00216                                                                 if ( i==2 )
00217                                                                 {
00218 #if NIP_DNS_ENABLE == 1
00219                                                                         addr    = net_if->ip_conf.name_server;
00220                                                                         optcode = DHCP_OPTION_DNS;
00221 #else
00222                                                                         i++;
00223 #endif
00224                                                                 }
00225                                                                 if ( i==3 )
00226                                                                 {
00227                                                                         addr    = &CONF_ATTR(leasetime);
00228                                                                         optcode = DHCP_OPTION_LEASETIME;
00229                                                                 }
00230                                                                 if ( i==4 )
00231                                                                 {
00232                                                                         addr    = &CONF_ATTR(t1);
00233                                                                         optcode = DHCP_OPTION_RENEWAL_TIME;
00234                                                                 }
00235                                                                 if ( i==5 )
00236                                                                 {
00237                                                                         addr    = &CONF_ATTR(t2);
00238                                                                         optcode = DHCP_OPTION_REBINDING_TIME;
00239                                                                 }
00240                                                                 opt = nip_dhcp_find_option( optcode, msg, msg_size );
00241                                                                 if ( opt != NULL && opt->valsize >= 4 )
00242                                                                 {
00243                                                                         if ( i<3 )
00244                                                                                 nip_memcpy( addr, opt->value.ipaddr, 4 );
00245                                                                         else
00246                                                                                 *(nip_time_t*)addr = tc + ntohl( opt->value.time );
00247                                                                 } else
00248                                                                 {
00249                                                                         nip_memset( addr, 0, 4 );
00250                                                                 }
00251                                                         }
00252                                                         while ( ++i < 6 );
00253 
00254                                                         CONF_ATTR(state) = NIP_DHCP_STAT_BOUND;
00255 
00256                                                 }
00257                                                 break;
00258 
00259                                         case DHCP_OPTION_MSGTYPE_NAK:
00260                                                 // reset to INIT state to discover other server
00261                                                 CONF_ATTR(state) = NIP_DHCP_STAT_INIT;
00262                                                 break;
00263                                 }
00264                         }
00265 
00266                         nip_udp_ptr_release( nip_dhcp_sock, NIP_UDP_RX_BUF );
00267                 }
00268 
00269                 // done with message? -> discard
00270                 nip_udp_cancel_recv( nip_dhcp_sock );
00271 
00272                 // close UDP socket when in bound state
00273                 if ( CONF_ATTR(state) == NIP_DHCP_STAT_BOUND )
00274                 {
00275                         nip_udp_close( nip_dhcp_sock );
00276                         nip_dhcp_sock = NIP_UDP_NO_SOCKET;
00277                 }
00278 
00279         }
00280 
00281 
00282         // ***********************************************************************
00283         // C h e c k   D H C P   t i m e o u t s
00284         switch ( CONF_ATTR(state) )
00285         {
00286                 case NIP_DHCP_STAT_BOUND:
00287 
00288                         // check timer T1
00289                         if ( CONF_ATTR(t1) < tc )
00290                         {
00291                                 CONF_ATTR(state) = NIP_DHCP_STAT_RENEWING;
00292                                 // send request
00293                                 goto prepare;
00294                         }
00295                         goto quit;
00296 
00297                 case NIP_DHCP_STAT_RENEWING:
00298 
00299                         // check timer T2
00300                         if ( CONF_ATTR(t2) < tc )
00301                         {
00302                                 CONF_ATTR(state) = NIP_DHCP_STAT_REBINDING;
00303                                 // send request
00304                                 goto prepare;
00305                         }
00306                         break;
00307 
00308                 case NIP_DHCP_STAT_REBINDING:
00309                         // lease expired?
00310                         if ( CONF_ATTR(leasetime) < tc )
00311                         {
00312                                 // restart IP address retrieval process
00313                                 // DHCP state will be reset within the nnextip_dhcp_start() function
00314 //                              CONF_ATTR(state) = NIP_DHCP_STAT_INIT;
00315                                 nip_ip_addr_autoconf( if_id );
00316                                 goto prepare;
00317                         }
00318                         break;
00319 
00320                 case NIP_DHCP_STAT_REQUESTING:
00321                 case NIP_DHCP_STAT_SELECTING:
00322                         break;
00323 
00324                 case NIP_DHCP_STAT_INIT:
00325                         goto prepare;
00326 
00327                 default:
00328                                 // unhandled state -> quit
00329                         goto quit;
00330                         break;
00331         }
00332 
00333         // all the above handled states will jump some place else if something
00334         // special is to happen. -> if we get here, retransmissions have to be checked
00335         goto retrans;
00336 
00337 
00338 
00339         // ***********************************************************************
00340         // M e s s a g e   B u i l d i n g
00341 prepare:
00342 
00343                 // restart exponential backoff algorithm
00344                 CONF_ATTR(backoff_delay) = NIP_DHCP_INIT_BACKOFF;
00345                 CONF_ATTR(ts) = 0;
00346 
00347 retrans:
00348 
00349                 if ( tc < CONF_ATTR(ts) )
00350                 {
00351                         return;
00352                 }
00353 
00354                 // Init socket
00355                 if ( nip_dhcp_sock == NIP_UDP_NO_SOCKET )
00356                 {
00357                         NIP_CURR_CMD = &nip_dhcp_disp_sock_init;
00358                         goto quit;
00359                 }
00360 
00361                 // initialize send buffer
00362                 if ( NIP_E_OK != nip_udp_init_send( nip_dhcp_sock, DHCPMSGSIZE ) )
00363                         goto quit;
00364 
00365                 // allocate uninitialized memory
00366                 if ( NIP_E_OK != nip_udp_write( nip_dhcp_sock, NULL, DHCPMSGSIZE ) )
00367                 {
00368                         nip_udp_cancel_send( nip_dhcp_sock );
00369                         goto quit;
00370                 }
00371 
00372                 // Requests aus dem REQUESTING State können nicht neugesendet werden
00373                 // da das Offer nicht gespeichert wird. -> Es wird wieder beim Discover
00374                 // begonnen. Gleichermaßen wird auf das Timeout im SELECTING state
00375                 // reagiert, welcher bedeutet, dass es kein offer gab.
00376                 if ( CONF_ATTR(state) == NIP_DHCP_STAT_REQUESTING
00377                   || CONF_ATTR(state) == NIP_DHCP_STAT_SELECTING  )
00378                         CONF_ATTR(state) = NIP_DHCP_STAT_INIT;
00379 
00380                 if ( CONF_ATTR(state) == NIP_DHCP_STAT_INIT
00381                   || CONF_ATTR(state) == NIP_DHCP_STAT_REBINDING )
00382                 {
00383                         // Set server address to broadcast and set server port
00384                         nip_memset( CONF_ATTR(serv_addr).ip, 255, 4 );
00385                         CONF_ATTR(serv_addr).port = NIP_UDP_PORT_DHCPSERVER;
00386                 }
00387 
00388                 if ( CONF_ATTR(state) == NIP_DHCP_STAT_RENEWING
00389                   || CONF_ATTR(state) == NIP_DHCP_STAT_REBINDING )
00390                 {
00391                         optcode     = DHCP_OPTION_MSGTYPE_REQUEST;
00392                 }
00393                 else
00394                 //if ( CONF_ATTR(state) == NIP_DHCP_STAT_INIT )
00395                 {
00396                         CONF_ATTR(state) = NIP_DHCP_STAT_SELECTING;
00397                         optcode     = DHCP_OPTION_MSGTYPE_DISCOVER;
00398                         // reset interface's IP address
00399                         // commented, because it seems we don't have to and actually should not
00400                         // reset the IP address, to not interfear with autoIP. Moved this to
00401                         // nip_ip_addr_autoconf() function.
00402                         //nip_memset( net_if->ip_conf.addr, 0, 4 );
00403                 }
00404 
00405                 // initialize DHCP Transaction ID with interface id
00406                 /// @todo use some unique number (part of mac address?)
00407                 CONF_ATTR(xid) = net_if->phy_conf.hw_addr[net_if->phy_conf.hw_addr_size-1] + (tc<<8);
00408 
00409                 // get pointer to packet data
00410                 msg = nip_udp_data_ptr( nip_dhcp_sock, NIP_UDP_TX_BUF );
00411 
00412                 // fill msg with data
00413                 nip_memset( msg, 0, DHCPMSGSIZE );
00414 
00415                 msg->htype = net_if->phy_conf.type;
00416                 msg->hlen  = net_if->phy_conf.hw_addr_size;
00417                 msg->xid   = CONF_ATTR(xid);
00418 
00419                 // fill in client internet address
00420                 nip_memcpy(
00421                         msg->ciaddr,
00422                         net_if->ip_conf.addr,
00423                         4
00424                 );
00425                 msg->magic_cookie = NIP_DHCP_MAGIC_COOKIE;
00426 
00427                 // Set Options
00428                 msg->options[0] = DHCP_OPTION_MSGTYPE;
00429                 msg->options[1] = 1;
00430                 msg->options[2] = optcode;
00431                 msg->options[3] = DHCP_OPTION_END;
00432 
00433 
00434 
00435 transmit:
00436                 // make sure the msg-pointer has been obtained before jumping here
00437 
00438                 // set values unique to all client messages
00439                 msg->op    = NIP_DHCP_OP_REQUEST;
00440 
00441                 // fill in client hardware address
00442                 nip_memcpy(
00443                         msg->chaddr,
00444                         net_if->phy_conf.hw_addr,
00445                         net_if->phy_conf.hw_addr_size
00446                 );
00447 
00448                 // set broadcast flag, because our IP implementation won't accept
00449                 // non-matching unicasts
00450                 msg->flags[0]=0xF0;
00451 
00452                 // release pointer to packet data
00453                 nip_udp_ptr_release( nip_dhcp_sock, NIP_UDP_TX_BUF );
00454 
00455                 // send DHCP message over given network interface to configured server addr
00456                 nip_udp_transmit( nip_dhcp_sock, &CONF_ATTR(serv_addr), if_id );
00457 
00458                 // double backoff delay
00459                 CONF_ATTR(ts) = tc + CONF_ATTR(backoff_delay);
00460                 CONF_ATTR(backoff_delay) <<= 1;
00461                 if ( CONF_ATTR(backoff_delay) > NIP_DHCP_MAX_BACKOFF )
00462                         CONF_ATTR(backoff_delay) = NIP_DHCP_MAX_BACKOFF;
00463 
00464 quit:
00465                 return;
00466 
00467 }
00468 
00469 
00470 
00471 
00472 
00473 
00474 #endif // (NIP_DHCP_ENABLE==1)

Generated on Thu Jul 10 01:09:29 2008 for NIP by  doxygen 1.5.5