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         );
00276         control -> finished = 1;
00277         break;
00278         
00279     case DHC4_PREINIT:
00280     {   
00281     }   break;
00282 
00283     case DHC4_BOUND:
00284     case DHC4_REBOOT:
00285     case DHC4_RENEW:
00286     case DHC4_REBIND:
00287     case DHC4_TIMEOUT: /* Cannot currently contact server, but we have an active lease */
00288     {    
00289         control -> lease = dhcpv4_lease( arg );
00290         dhc4_log
00291         (   control, LOG_DEBUG,
00292             "DHCPv4 %s - BOUND: %p", control->if_name, dhcpv4_lease
00293         );
00294         control -> finished = 1;
00295         break;
00296     }
00297 
00298     case DHC4_RELEASE:
00299     case DHC4_EXPIRE:
00300     case DHC4_FAIL:
00301     case DHC4_STOP:
00302         /* unconfigure existing lease */
00303         /* XXX - should do something here!*/
00304         control -> finished = 1;
00305         break;
00306 
00307     default:
00308         dhc4_log
00309         (   control, LOG_ERR,
00310             "DHCPv4 %s - entered unhandled state.",
00311             control->if_name
00312         );
00313     }
00314     return 0;
00315 }
00316 
00317 int dhcp4_process_lease(DHCPv4_control *ctl)
00318 {
00319     if( ( ctl == 0L ) || (ctl->lease == 0L) ) return(0);
00320     /* process new lease */
00321     DHCPv4_lease *lease = ctl->lease;
00322     char buf[32];
00323     ip_addr_t lease_address;
00324         
00325     ctl->lease = lease;
00326     
00327     lease_address = ip_addr_in( &lease->address );
00328     
00329     dhc4_log
00330     (   ctl, LOG_INFO,
00331         "DHCPv4 %s - obtained lease %s",
00332         ctl->if_name,
00333         ip_text_addr(&lease_address, buf, 32)
00334     );
00335     ctl->lease = lease;
00336     ctl->ip4 = nic_addr(ctl->nh, lease_address );
00337     
00338     IPaddr_list_node_t *n = calloc(1,sizeof(IPaddr_list_node_t));
00339     n->addr = ctl->ip4;
00340     STAILQ_INSERT_TAIL( &(ctl->addr_list), n, link );
00341 
00342     dhcpv4_process_options(lease, dhcp4_nic_option_handler, ctl);
00343 
00344     return ((!STAILQ_EMPTY(&(ctl->addr_list))) || (ctl->lease->options != 0));
00345 }
00346 
00347 uint32_t dhcpv4_mtu_option( DHCPv4_control *ctl )
00348 {
00349     return ctl->mtu;
00350 }
00351 
00352 void dhcp4_nic_option_handler( DHCPv4_option *option, void *arg )
00353 {
00354     DHCPv4_control *control = arg;
00355     char buf[32];
00356     
00357     if ( option -> unicode == DHC_DHCP_Universe )
00358     {
00359         switch( option -> code )
00360         {
00361         case DHCO_SUBNET_MASK:
00362         {
00363             dhc4_log
00364             (   control, LOG_INFO, "DHCPv4 %s - option subnet-mask: %s", 
00365                 control->if_name,
00366                 inet_ntop( AF_INET, (struct in_addr*)&(option->value[0]), &(buf[0]), sizeof(buf))
00367             );
00368             ip_addr_t ip4 = nic_ip_addr(control->ip4);
00369             uint32_t sm=0;
00370             uint32_t  i=32, b=1;
00371             memcpy(&sm, &(option->value[0]), sizeof(uint32_t));
00372             sm = ntohl(sm);
00373             for(; i && ((sm & b) != b); i-=1, b <<= 1);
00374             nic_addr_set_prefix( control->ip4, i);
00375             ip_addr_t ip4_broadcast = ip_v4_broadcast( &ip4, i );
00376             nic_addr_set_broadcast( control->ip4, ip4_broadcast );
00377             dhc4_log
00378             (   control, LOG_INFO, "DHCPv4 %s - option subnet-mask - prefix_len: %d broadcast: %s", 
00379                 control->if_name, i, ip_text_addr(&ip4_broadcast, buf, 32)              
00380             );
00381         }
00382         break;
00383 
00384         case DHCO_ROUTERS:
00385         {
00386             int i;
00387             struct in_addr *routers = (struct in_addr*) &(option->value[0]);
00388             dhc4_log
00389             (   control, LOG_INFO, "DHCPv4 %s - option routers:", 
00390                 control->if_name
00391             );
00392         
00393             for( i = 0; i < option->n_elements; i++, routers++ )
00394             {
00395                 ip_addr_t gw = ip_addr_in( routers );
00396 
00397                 dhc4_log
00398                 (   control, LOG_DEBUG, "DHCPv4 %s - option routers default gateway %d: %s", 
00399                     control->if_name, i,
00400                     ip_text_addr(&gw,&(buf[0]),32)
00401                 );
00402 
00403                 IProute_t route =
00404                     nic_route_new
00405                     (   control->nh,
00406                         nic_get_index(control->nic),
00407                         0L,0,                      /* DEFAULT ROUTE ! */
00408                         &gw,                       /* gateway */
00409                         -1,                        /* -1: default: RTN_UNICAST */
00410                         -1,                        /* -1: default: RTPROT_BOOT */
00411                         -1,                        /* default scope: UNIVERSE */
00412                         (i > 0) ? (int8_t)i : -1,  /* priority: increasing if more than one */
00413                         -1,                        /* table: default (local) */
00414                         0L/*no iif*/, 0L, 0/*no src*/               
00415                     );          
00416                 IProute_list_node_t *n = calloc(1,sizeof(IProute_list_node_t));
00417                 n->route = route;
00418                 STAILQ_INSERT_TAIL(&(control->route_list),n,link);
00419             }
00420         }
00421         break;  
00422         case DHCO_STATIC_ROUTES:
00423         {
00424             dhc4_log
00425             (   control, LOG_DEBUG, "DHCPv4 %s - option static-routes:", 
00426                 control->if_name
00427             );
00428 
00429             char buf2[32];
00430             int i;
00431             struct { struct in_addr dst, gw; } *routes = (void*)&(option->value[0]);
00432             IProute_t route;
00433 
00434             for( i = 0; i < option->n_elements; i++, routes++ )
00435             {
00436                 ip_addr_t 
00437                     dst=ip_addr_in( &(routes->dst) ),
00438                     gw=ip_addr_in( &(routes->gw) );
00439                     
00440                 dhc4_log
00441                 (   control, LOG_DEBUG, "DHCPv4 %s - option static-routes - route %d: %s via %s",
00442                     control->if_name, 
00443                     ip_text_addr( &dst, &(buf[0]), sizeof(buf)),
00444                     ip_text_addr( &gw , &(buf2[0]),sizeof(buf))
00445                 );
00446 
00447                 uint32_t dip= ip_v4_addr(&dst);
00448                 uint32_t  dst_len=32, b=1;
00449                 for(; dst_len && ((dip & b) != b); dst_len--, b <<= 1);
00450                 route = 
00451                     nic_route_new
00452                     (   control->nh,
00453                         nic_get_index(control->nic),
00454                         &dst, dst_len,
00455                         &gw,                       /* gateway */
00456                         -1,                        /* -1: default: RTN_UNICAST */
00457                         -1,                        /* -1: default: RTPROT_BOOT */
00458                         -1,                        /* default scope: global */
00459                         (i > 0) ? (int8_t)i : -1,  /* priority: increasing if more than one */
00460                         -1,                        /* table: default (local) */
00461                         0L/*no iif*/, 0L,0/*no src*/                
00462                     );                         
00463                 IProute_list_node_t *n = calloc(1,sizeof(IProute_list_node_t));
00464                 n->route = route;
00465                 STAILQ_INSERT_TAIL(&(control->route_list),n,link);
00466             }
00467         }
00468         break;
00469 
00470         case DHCO_DOMAIN_NAME:
00471             dhc4_log
00472             (   control, LOG_DEBUG, "DHCPv4 %s - option domain-name: %s",
00473                 control->if_name, (char*)(option->value)
00474             );
00475             control->search_list = strdup( (char*)option->value );
00476             break;
00477 
00478         case DHCO_HOST_NAME:
00479             dhc4_log
00480             (   control, LOG_DEBUG, "DHCPv4 %s - option host-name: %s",
00481                 control->if_name, (char*)(option->value)
00482             );
00483             control->host_name = strdup( (char*)option->value );
00484             break;
00485 
00486         case DHCO_DOMAIN_NAME_SERVERS:
00487         {
00488             dhc4_log
00489             (   control, LOG_DEBUG, "DHCPv4 %s - option domain-name-servers:",
00490                 control->if_name
00491             );
00492             int i;
00493             struct in_addr *dns = (struct in_addr*) &(option->value[0]);
00494             for( i = 0; i < option->n_elements; i++, dns++ )
00495             {
00496                 dhc4_log
00497                 (   control, LOG_DEBUG, "DHCPv4 %s - domain-name-server %d: %s",
00498                     control->if_name, i,
00499                     inet_ntop(AF_INET, dns, buf, sizeof(buf))
00500                 );
00501                 IPaddr_t dnsIP = nic_addr(control->nh, ip_addr_in(dns));
00502                 IPaddr_list_node_t *n = calloc(1,sizeof(IPaddr_list_node_t));
00503                 n->addr = dnsIP;
00504                 STAILQ_INSERT_TAIL(&(control->dns_list), n, link);
00505             }
00506         }
00507         break;
00508         case DHCO_INTERFACE_MTU:
00509         {
00510             dhc4_log
00511             (   control, LOG_DEBUG, "DHCPv4 %s - option interface-mtu: %d",
00512                 control->if_name, *((uint32_t*)&(option->value[0]))
00513             );
00514             control->mtu =  *((uint32_t*)&(option->value[0]));
00515         }
00516         break;
00517         default:
00518             /* XXX: many more options to do something about! */
00519             break;
00520         }
00521     }
00522 }

Generated on Thu Aug 10 21:26:25 2006 for libdhcp by  doxygen 1.4.7