dhcp4_nic.c

00001 /* dhcp4_nic.c
00002  *
00003  * Network Interface Configurator for the ISC DHCP IPv4 client library.
00004  *
00005  * Copyright (C) 2006  Red Hat, Inc. All rights reserved.
00006  *
00007  * This copyrighted material is made available to anyone wishing to use,
00008  * modify, copy, or redistribute it subject to the terms and conditions of
00009  * the GNU General Public License v.2.  This program is distributed in the
00010  * hope that it will be useful, but WITHOUT ANY WARRANTY expressed or
00011  * implied, including the implied warranties of MERCHANTABILITY or FITNESS
00012  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
00013  * details.  You should have received a copy of the GNU General Public
00014  * License along with this program; if not, write to the Free Software
00015  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
00016  * USA. Any Red Hat trademarks that are incorporated in the source code or
00017  * documentation are not subject to the GNU General Public License and may
00018  * only be used or replicated with the express permission of Red Hat, Inc.
00019  *
00020  * Red Hat Author(s): Jason Vas Dias
00021  *                    David Cantrell
00022  */
00023 #include <sys/types.h>
00024 #include <unistd.h>
00025 #include <netinet/in.h>
00026 #include <sys/socket.h>
00027 #include <string.h>
00028 #include <time.h>
00029 #include <errno.h>
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 
00033 #include <dhcp4_nic.h>
00034 #include <dhcp4client.h>
00035 #include <isc_dhcp/dhcpd.h>
00036 
00037 struct dhcpv4_control_s
00038 {
00039 /*{ this structure MUST be the same as in libdhcp_control.h for DHCP clients */
00040     LIBDHCP_Callback    callback;       /* the DHCP clients' main loop calls this on state changes */
00041     uint16_t            capability;     /* LIBDHCP_Capability bits to enable                       */
00042     uint8_t             finished;       /* set to one to make clients exit their main loop         */
00043     uint8_t             decline;        /* set to one to decline the lease (DHCPv4 only)           */
00044     time_t              timeout;        /* (timeout+now) == time after which clients MUST return   */
00045     time_t              now;            /* clients set this to time(0) on entering main loop       */
00046     void               *arg;            /* user data pointer                                       */
00047     LIBDHCP_Error_Handler eh;
00048 /*} end of fields known to DHCP clients */
00049     uint8_t             log_level;      /* maximum log level (LOG_FATAL excluded)                  */    
00050 /* struct DHCPv4_nic { */
00051     NLH_t               nh;
00052     NIC_t               nic;
00053     IPaddr_list_t       addr_list;
00054     IProute_list_t      route_list;
00055     IPaddr_list_t       dns_list;
00056     char               *search_list;
00057     char               *host_name;
00058     DHCPv4_lease       *lease;
00059 /*                   } */
00060     IPaddr_t           ip4;
00061     uint32_t           mtu;
00062     char               *if_name;
00063     char              **argv;
00064     int                 argc;
00065 };
00066 
00067 static void
00068 dhc4_log( DHCPv4_control *ctl, int priority, char *fmt, ...)
00069 {
00070     va_list va;
00071     if((ctl == 0)|| ((ctl->eh == 0) || (ctl->log_level < priority)))
00072         return;
00073     va_start(va, fmt);
00074     ctl->eh((LIBDHCP_Control*)ctl, priority, fmt, va);
00075     va_end(va);
00076 }
00077 
00078 DHCPv4_control*
00079 dhcpv4_control_va
00080 ( 
00081     NLH_t                  nh,
00082     char                  *eth_if_name,
00083     LIBDHCP_Capability     dhc_cap, 
00084     time_t                 timeout,
00085     LIBDHCP_Error_Handler  error_handler,
00086     uint8_t                log_level,
00087     va_list                va
00088 )
00089 {
00090     DHCPv4_control *dhc = calloc( 1, sizeof( DHCPv4_control ) ); 
00091     char **argv, *a[32]={strdup("dhclient")}, *p;
00092     int argc,i;
00093     uint8_t d_needed=1, if_needed=1;
00094 
00095     if( dhc == 0L )
00096         return 0;
00097 
00098     dhc->nh = nh;
00099 
00100     if( (dhc->nic = nic_by_name(dhc->nh, eth_if_name)) == 0L ) {
00101         free(dhc);
00102         return 0;
00103     }
00104 
00105     dhc->capability = dhc_cap;
00106     dhc->callback = dhcp4_nic_callback;
00107     dhc->timeout = timeout;
00108     dhc->arg = 0;
00109     dhc->eh = error_handler;
00110     dhc->log_level = log_level;
00111     dhc->if_name = strdup(eth_if_name);
00112     
00113     for(argc=1, argv=&(a[1]); (argc < 31) && (p = va_arg(va, char *)); argc++, argv++)
00114     {
00115         *argv = strdup(p);
00116         if( strcmp(p,"-d") == 0)
00117             d_needed = 0;
00118         if( strcmp(p,eth_if_name) == 0)
00119             if_needed = 0;      
00120     }
00121 
00122     if ( d_needed )
00123     {
00124         if ( argc > 31 )
00125         {
00126             dhcpv4_control_free(dhc);
00127             return 0L;
00128         }
00129         a[argc++] = strdup("-d");
00130     }
00131 
00132     if ( if_needed )
00133     {
00134         if ( argc > 31 )
00135         {
00136             dhcpv4_control_free(dhc);
00137             return 0L;
00138         }
00139         a[argc++] = strdup(eth_if_name);
00140     }
00141 
00142     a[argc] = 0L;
00143     argv = calloc(argc+1, sizeof(char*));
00144     if( argv == 0 )
00145     {
00146         dhcpv4_control_free(dhc);
00147         return 0L;
00148     }
00149     dhc->argc = argc;    
00150     for(i=0; i < argc; i++)
00151         argv[i] = a[i];
00152     argv[i]=0;
00153     dhc->argv = argv;
00154     STAILQ_INIT( &(dhc->addr_list) );
00155     STAILQ_INIT( &(dhc->route_list) );
00156     STAILQ_INIT( &(dhc->dns_list) );    
00157     return dhc;
00158 }
00159 
00160 DHCPv4_control*
00161 dhcpv4_control
00162 ( 
00163     NLH_t                  nh,
00164     char                  *eth_if_name,
00165     LIBDHCP_Capability     dhc_cap, 
00166     time_t                 timeout,
00167     LIBDHCP_Error_Handler  error_handler,
00168     uint8_t                log_level,
00169     ...
00170 )
00171 {
00172     va_list va;
00173     va_start( va, log_level );
00174     DHCPv4_control * dhc =
00175     dhcpv4_control_va
00176     ( 
00177         nh,
00178         eth_if_name,
00179         dhc_cap, 
00180         timeout,
00181         error_handler,
00182         log_level,
00183         va
00184     );   
00185     va_end(va);
00186     return(dhc);
00187 }
00188 
00189 void dhcpv4_control_free( DHCPv4_control *dhc )
00190 {
00191     if(dhc->lease)
00192     {
00193         dhcpv4_lease_free(dhc->lease);
00194         dhc->lease = NULL;
00195     }
00196     if(dhc->if_name)
00197     {
00198         free(dhc->if_name);
00199         dhc->if_name = NULL;
00200     }
00201     if( dhc->argv )
00202     {
00203         char **p;
00204         for(p=dhc->argv; *p; p++)
00205             free(*p);
00206         free(dhc->argv);
00207         dhc->argv = 0;
00208     }
00209     if( !STAILQ_EMPTY( &(dhc->addr_list) ) )
00210         nic_address_list_free(&(dhc->addr_list));
00211     if( !STAILQ_EMPTY( &(dhc->dns_list) ) )
00212         nic_address_list_free(&(dhc->dns_list));
00213     if( !STAILQ_EMPTY( &(dhc->route_list) ) )
00214         nic_route_list_free(&(dhc->route_list));
00215     
00216     if( dhc->search_list )
00217         free(dhc->search_list);
00218     if( dhc->host_name )
00219         free(dhc->host_name);
00220 
00221     free(dhc);
00222 }
00223 
00224 extern char **environ;
00225 
00226 DHCPv4_nic *do_dhcpv4( DHCPv4_control *dh4c )
00227 {
00228     dhcpv4_client((LIBDHCP_Control*)dh4c, dh4c->argc, dh4c->argv, environ);
00229 
00230     if(dh4c->lease == 0)
00231     { /* empty lease / unable to contact server */
00232         dhcpv4_control_free( dh4c );
00233         return 0;
00234     }
00235     return (DHCPv4_nic*)&(dh4c->nh);
00236 }
00237 
00238 NIC_Res_t dhcpv4_nic(NLH_t nh, DHCPv4_nic *nic )
00239 {
00240     return 
00241         nic_configure
00242         (
00243             nh,
00244             nic->nic,
00245             &(nic->address_list),
00246             &(nic->route_list),
00247             &(nic->dns_list),
00248             nic->search_list,
00249             nic->host_name
00250         );
00251 }
00252 
00253 DHCPv4_nic *dhcp4_set_lease(DHCPv4_control *ctl, DHCPv4_lease *lease)
00254 {
00255     ctl->lease = lease;
00256     return (DHCPv4_nic*)&(ctl->nh);
00257 }
00258 
00259 int dhcp4_nic_callback
00260 (   LIBDHCP_Control *cp,
00261     DHCP_State state, 
00262     void *arg
00263 )
00264 {
00265     DHCPv4_control *control = (DHCPv4_control *) cp;
00266     char buf[32];
00267     dhc4_log
00268     (   control, LOG_DEBUG,
00269         "DHCPv4 %s  - state: %s",
00270         control->if_name,
00271         libdhcp_state_string(state,&(buf[0]))
00272     );
00273     switch( state )
00274     {
00275     case DHC_TIMEDOUT:
00276         dhc4_log
00277         (   control, LOG_INFO,
00278             "DHCPv4 %s  - TIMED OUT.",
00279             control->if_name,
00280             libdhcp_state_string(state,&(buf[0]))
00281         );
00282         control -> finished = 1;
00283         break;
00284         
00285     case DHC4_PREINIT:
00286     {   
00287     }   break;
00288 
00289     case DHC4_BOUND:
00290     case DHC4_REBOOT:
00291     case DHC4_RENEW:
00292     case DHC4_REBIND:
00293     case DHC4_TIMEOUT: /* Cannot currently contact server, but we have an active lease */
00294     {    
00295         control -> lease = dhcpv4_lease( arg );
00296         dhc4_log
00297         (   control, LOG_DEBUG,
00298             "DHCPv4 %s - BOUND: %p", control->if_name, dhcpv4_lease
00299         );
00300         control -> finished = 1;
00301         break;
00302     }
00303 
00304     case DHC4_RELEASE:
00305     case DHC4_EXPIRE:
00306     case DHC4_FAIL:
00307     case DHC4_STOP:
00308         /* unconfigure existing lease */
00309         /* XXX - should do something here!*/
00310         control -> finished = 1;
00311         break;
00312 
00313     default:
00314         dhc4_log
00315         (   control, LOG_ERR,
00316             "DHCPv4 %s - entered unhandled state.",
00317             control->if_name
00318         );
00319     }
00320     return 0;
00321 }
00322 
00323 int dhcp4_process_lease(DHCPv4_control *ctl)
00324 {
00325     if( ( ctl == 0L ) || (ctl->lease == 0L) ) return(0);
00326     /* process new lease */
00327     DHCPv4_lease *lease = ctl->lease;
00328     char buf[32];
00329     ip_addr_t lease_address;
00330         
00331     ctl->lease = lease;
00332     
00333     lease_address = ip_addr_in( &lease->address );
00334     
00335     dhc4_log
00336     (   ctl, LOG_INFO,
00337         "DHCPv4 %s - obtained lease %s",
00338         ctl->if_name,
00339         ip_text_addr(&lease_address, buf, 32)
00340     );
00341     ctl->lease = lease;
00342     ctl->ip4 = nic_addr(ctl->nh, lease_address );
00343     
00344     IPaddr_list_node_t *n = calloc(1,sizeof(IPaddr_list_node_t));
00345     n->addr = ctl->ip4;
00346     STAILQ_INSERT_TAIL( &(ctl->addr_list), n, link );
00347 
00348     dhcpv4_process_options(lease, dhcp4_nic_option_handler, ctl);
00349 
00350     return ((!STAILQ_EMPTY(&(ctl->addr_list))) || (ctl->lease->options != 0));
00351 }
00352 
00353 uint32_t dhcpv4_mtu_option( DHCPv4_control *ctl )
00354 {
00355     return ctl->mtu;
00356 }
00357 
00358 void dhcp4_nic_option_handler( DHCPv4_option *option, void *arg )
00359 {
00360     DHCPv4_control *control = arg;
00361     char buf[32];
00362     
00363     if ( option -> unicode == DHC_DHCP_Universe )
00364     {
00365         switch( option -> code )
00366         {
00367         case DHCO_SUBNET_MASK:
00368         {
00369             dhc4_log
00370             (   control, LOG_INFO, "DHCPv4 %s - option subnet-mask: %s", 
00371                 control->if_name,
00372                 inet_ntop( AF_INET, (struct in_addr*)&(option->value[0]), &(buf[0]), sizeof(buf))
00373             );
00374             ip_addr_t ip4 = nic_ip_addr(control->ip4);
00375             uint32_t sm=0;
00376             uint32_t  i=32, b=1;
00377             memcpy(&sm, &(option->value[0]), sizeof(uint32_t));
00378             sm = ntohl(sm);
00379             for(; i && ((sm & b) != b); i-=1, b <<= 1);
00380             nic_addr_set_prefix( control->ip4, i);
00381             ip_addr_t ip4_broadcast = ip_v4_broadcast( &ip4, i );
00382             nic_addr_set_broadcast( control->ip4, ip4_broadcast );
00383             dhc4_log
00384             (   control, LOG_INFO, "DHCPv4 %s - option subnet-mask - prefix_len: %d broadcast: %s", 
00385                 control->if_name, i, ip_text_addr(&ip4_broadcast, buf, 32)              
00386             );
00387         }
00388         break;
00389 
00390         case DHCO_ROUTERS:
00391         {
00392             int i;
00393             struct in_addr *routers = (struct in_addr*) &(option->value[0]);
00394             dhc4_log
00395             (   control, LOG_INFO, "DHCPv4 %s - option routers:", 
00396                 control->if_name
00397             );
00398         
00399             for( i = 0; i < option->n_elements; i++, routers++ )
00400             {
00401                 ip_addr_t gw = ip_addr_in( routers );
00402 
00403                 dhc4_log
00404                 (   control, LOG_DEBUG, "DHCPv4 %s - option routers default gateway %d: %s", 
00405                     control->if_name, i,
00406                     ip_text_addr(&gw,&(buf[0]),32)
00407                 );
00408 
00409                 IProute_t route =
00410                     nic_route_new
00411                     (   control->nh,
00412                         nic_get_index(control->nic),
00413                         0L,0,                      /* DEFAULT ROUTE ! */
00414                         &gw,                       /* gateway */
00415                         -1,                        /* -1: default: RTN_UNICAST */
00416                         -1,                        /* -1: default: RTPROT_BOOT */
00417                         -1,                        /* default scope: UNIVERSE */
00418                         (i > 0) ? (int8_t)i : -1,  /* priority: increasing if more than one */
00419                         -1,                        /* table: default (local) */
00420                         0L/*no iif*/, 0L, 0/*no src*/               
00421                     );          
00422                 IProute_list_node_t *n = calloc(1,sizeof(IProute_list_node_t));
00423                 n->route = route;
00424                 STAILQ_INSERT_TAIL(&(control->route_list),n,link);
00425             }
00426         }
00427         break;  
00428         case DHCO_STATIC_ROUTES:
00429         {
00430             dhc4_log
00431             (   control, LOG_DEBUG, "DHCPv4 %s - option static-routes:", 
00432                 control->if_name
00433             );
00434 
00435             char buf2[32];
00436             int i;
00437             struct { struct in_addr dst, gw; } *routes = (void*)&(option->value[0]);
00438             IProute_t route;
00439 
00440             for( i = 0; i < option->n_elements; i++, routes++ )
00441             {
00442                 ip_addr_t 
00443                     dst=ip_addr_in( &(routes->dst) ),
00444                     gw=ip_addr_in( &(routes->gw) );
00445                     
00446                 dhc4_log
00447                 (   control, LOG_DEBUG, "DHCPv4 %s - option static-routes - route %d: %s via %s",
00448                     control->if_name, 
00449                     ip_text_addr( &dst, &(buf[0]), sizeof(buf)),
00450                     ip_text_addr( &gw , &(buf2[0]),sizeof(buf))
00451                 );
00452 
00453                 uint32_t dip= ip_v4_addr(&dst);
00454                 uint32_t  dst_len=32, b=1;
00455                 for(; dst_len && ((dip & b) != b); dst_len--, b <<= 1);
00456                 route = 
00457                     nic_route_new
00458                     (   control->nh,
00459                         nic_get_index(control->nic),
00460                         &dst, dst_len,
00461                         &gw,                       /* gateway */
00462                         -1,                        /* -1: default: RTN_UNICAST */
00463                         -1,                        /* -1: default: RTPROT_BOOT */
00464                         -1,                        /* default scope: global */
00465                         (i > 0) ? (int8_t)i : -1,  /* priority: increasing if more than one */
00466                         -1,                        /* table: default (local) */
00467                         0L/*no iif*/, 0L,0/*no src*/                
00468                     );                         
00469                 IProute_list_node_t *n = calloc(1,sizeof(IProute_list_node_t));
00470                 n->route = route;
00471                 STAILQ_INSERT_TAIL(&(control->route_list),n,link);
00472             }
00473         }
00474         break;
00475 
00476         case DHCO_DOMAIN_NAME:
00477             dhc4_log
00478             (   control, LOG_DEBUG, "DHCPv4 %s - option domain-name: %s",
00479                 control->if_name, (char*)(option->value)
00480             );
00481             control->search_list = strdup( (char*)option->value );
00482             break;
00483 
00484         case DHCO_HOST_NAME:
00485             dhc4_log
00486             (   control, LOG_DEBUG, "DHCPv4 %s - option host-name: %s",
00487                 control->if_name, (char*)(option->value)
00488             );
00489             control->host_name = strdup( (char*)option->value );
00490             break;
00491 
00492         case DHCO_DOMAIN_NAME_SERVERS:
00493         {
00494             dhc4_log
00495             (   control, LOG_DEBUG, "DHCPv4 %s - option domain-name-servers:",
00496                 control->if_name
00497             );
00498             int i;
00499             struct in_addr *dns = (struct in_addr*) &(option->value[0]);
00500             for( i = 0; i < option->n_elements; i++, dns++ )
00501             {
00502                 dhc4_log
00503                 (   control, LOG_DEBUG, "DHCPv4 %s - domain-name-server %d: %s",
00504                     control->if_name, i,
00505                     inet_ntop(AF_INET, dns, buf, sizeof(buf))
00506                 );
00507                 IPaddr_t dnsIP = nic_addr(control->nh, ip_addr_in(dns));
00508                 IPaddr_list_node_t *n = calloc(1,sizeof(IPaddr_list_node_t));
00509                 n->addr = dnsIP;
00510                 STAILQ_INSERT_TAIL(&(control->dns_list), n, link);
00511             }
00512         }
00513         break;
00514         case DHCO_INTERFACE_MTU:
00515         {
00516             dhc4_log
00517             (   control, LOG_DEBUG, "DHCPv4 %s - option interface-mtu: %d",
00518                 control->if_name, *((uint32_t*)&(option->value[0]))
00519             );
00520             control->mtu =  *((uint32_t*)&(option->value[0]));
00521         }
00522         break;
00523         default:
00524             /* XXX: many more options to do something about! */
00525             break;
00526         }
00527     }
00528 }

Generated on Mon Aug 14 17:25:56 2006 for libdhcp by  doxygen 1.4.7