dhcp4_nic.c

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

Generated on Fri Oct 13 18:20:33 2006 for libdhcp by  doxygen 1.4.7