00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <sys/types.h>
00025 #include <unistd.h>
00026 #include <sys/wait.h>
00027 #include <sys/mman.h>
00028 #include <dhcp_nic.h>
00029 #include <string.h>
00030 #include <malloc.h>
00031 #include <errno.h>
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034
00035 static
00036 int dhc_log( LIBDHCP_Control *ctl, int priority, char *fmt, ... )
00037 {
00038 if( (ctl==0) || (ctl->eh == 0) )
00039 return 0;
00040 va_list va;
00041 va_start(va,fmt);
00042 int r = ctl->eh(ctl, priority, fmt, va);
00043 va_end(va);
00044 return r;
00045 }
00046
00047 static
00048 int dhc_log_noctl( LIBDHCP_Error_Handler eh, int log_level, int priority, char *fmt, ... )
00049 {
00050 LIBDHCP_Control ctl;
00051 if( eh == 0 )
00052 return 0;
00053 ctl.log_level = log_level;
00054 va_list va;
00055 va_start(va,fmt);
00056 int r = eh(&ctl, priority, fmt, va);
00057 va_end(va);
00058 return r;
00059 }
00060
00061 static int ifup( LIBDHCP_Control *control, NIC_t nic )
00062 {
00063
00064 uint32_t iff = nic_get_flags(nic);
00065 dhc_log
00066 ( control, LOG_INFO,
00067 "DHCP %s - bringing link UP - %x %d",
00068 nic_get_name(nic),
00069 iff, iff & (IFF_UP | IFF_RUNNING)
00070 );
00071 if ( ( iff & (IFF_UP | IFF_RUNNING) ) != (IFF_UP | IFF_RUNNING) )
00072 {
00073 nic_set_flags(nic, iff | IFF_UP | IFF_RUNNING );
00074 if ( nic_update(nic) != NIC_OK )
00075 {
00076 dhc_log
00077 ( control, LOG_ERR,
00078 "DHCP %s - bringing link UP failed.",
00079 nic_get_name(nic)
00080 );
00081 return 1;
00082 }
00083 }
00084 return 0;
00085 }
00086
00087 static
00088 DHCP_nic *dhcp_nic_va
00089 (
00090 NLH_t nh,
00091 DHCP_Preference preference,
00092 char *ethX,
00093 LIBDHCP_Capability dhc_cap,
00094 time_t timeout,
00095 LIBDHCP_Error_Handler error_handler,
00096 uint8_t log_level,
00097 va_list dhclient_va
00098 )
00099 {
00100 DHCP_nic *nic = calloc(1,sizeof(DHCP_nic));
00101 if( nic == 0L )
00102 {
00103 dhc_log_noctl(error_handler, log_level, LOG_FATAL, "dhcp_nic: out of memory");
00104 return 0L;
00105 }
00106
00107 nic->preference = preference;
00108
00109 nic->nh = nh;
00110
00111 NIC_t eth = nic_by_name(nh, ethX);
00112
00113 if( eth == 0L )
00114 {
00115 dhc_log_noctl(error_handler, log_level, LOG_FATAL, "dhcp_nic: net_get_by_name(%s) failed", ethX);
00116 free(nic);
00117 return 0L;
00118 }
00119
00120 LIBDHCP_Control *dhc6ctl=0;
00121 DHCPv4_control *dhc4ctl=0;
00122
00123 if ( (preference & DHCPv4_DISABLE) == 0 )
00124 {
00125 dhc4ctl =
00126 dhcpv4_control_va
00127 (nh, ethX, dhc_cap, timeout, error_handler, log_level, dhclient_va);
00128 if ( dhc4ctl == 0L )
00129 {
00130 dhc_log_noctl(error_handler, log_level, LOG_FATAL,"dhcp_nic: dhcpv4_control failed");
00131 free(nic);
00132 return 0;
00133 }
00134 nic->dhc4ctl = dhc4ctl;
00135 }
00136
00137 if ( (preference & DHCPv6_DISABLE) == 0 )
00138 {
00139 dhc6ctl =
00140 libdhcp_control_new
00141 ( dhcp6_nic_callback,
00142 0,
00143 timeout,
00144 0,
00145 error_handler, log_level
00146 );
00147
00148 if ( dhc6ctl == 0L )
00149 {
00150 dhc_log_noctl(error_handler, log_level, LOG_FATAL,"dhcp_nic: libdhcp_control_new failed");
00151 free(nic);
00152 if (dhc4ctl)
00153 dhcpv4_control_free(dhc4ctl);
00154 return 0;
00155 }
00156
00157 nic->dhc6ctl = dhc6ctl;
00158 }
00159
00160 if ( ( dhc6ctl == 0L ) && (dhc4ctl == 0) )
00161 {
00162 dhc_log_noctl(error_handler, log_level, LOG_FATAL, "dhcp_nic: both DISABLE_DHCPv6 and DISABLE_DHCv4 set.");
00163 free(nic);
00164 return 0;
00165 }
00166
00167 if( error_handler )
00168 {
00169 nic_set_va_logger(nh, (NIC_VA_Error_Handler_t)error_handler, dhc6ctl);
00170 nic_set_loglevel( nh, log_level );
00171 }
00172
00173 if( ifup(dhc6ctl, eth) != 0 )
00174 {
00175 dhc_log_noctl(error_handler, log_level, LOG_FATAL, "dhcp_nic: ifup(%s) failed", ethX);
00176 if (dhc4ctl)
00177 dhcpv4_control_free(dhc4ctl);
00178 if (dhc6ctl)
00179 libdhcp_control_free(dhc6ctl);
00180 free(nic);
00181 return 0L;
00182 }
00183
00184 DHCPv4_nic *nic4=0;
00185 DHCPv6_nic *nic6=0;
00186
00187 if( (preference & DHCPv6_DISABLE) == 0 )
00188 {
00189
00190
00191
00192
00193 int ipv6_sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
00194 close(ipv6_sock);
00195 }
00196
00197 if( timeout > 0 )
00198 {
00199 if ( preference & IPv6_PREFERENCE )
00200 {
00201 if ( (preference & DHCPv6_DISABLE) == 0 )
00202 nic6 = do_dhcpv6(nic->dhc6ctl, nh, ethX);
00203
00204 if ( (preference & DHCPv4_DISABLE) == 0 )
00205 nic4 = do_dhcpv4(nic->dhc4ctl);
00206
00207 }else
00208 {
00209 if ( (preference & DHCPv4_DISABLE) == 0 )
00210 nic4 = do_dhcpv4(nic->dhc4ctl);
00211
00212 if ( (preference & DHCPv6_DISABLE) == 0 )
00213 nic6 = do_dhcpv6(nic->dhc6ctl, nh, ethX);
00214 }
00215 }else
00216 {
00217
00218
00219
00220
00221
00222
00223 pid_t
00224 dhc4_pid=-1,
00225 dhc6_pid=-1;
00226
00227 uint8_t *sma =
00228 mmap( 0, 16 * 8192,
00229 PROT_READ | PROT_WRITE,
00230 MAP_SHARED | MAP_ANONYMOUS,
00231 -1, 0
00232 );
00233
00234 if( sma == 0 )
00235 {
00236 dhc_log_noctl(error_handler, log_level, LOG_FATAL,
00237 "dhcp_nic: timeout==0 and cannot obtain shared memory area - %d %s.",
00238 errno, strerror(errno)
00239 );
00240 if (dhc4ctl)
00241 dhcpv4_control_free(dhc4ctl);
00242 if (dhc6ctl)
00243 libdhcp_control_free(dhc6ctl);
00244 free(nic);
00245 return 0L;
00246 }
00247
00248 uint8_t
00249 *dhc4_buf = sma,
00250 *dhc6_buf = sma + (14 * 8192);
00251
00252
00253 if( (preference & DHCPv4_DISABLE) == 0) {
00254 if ((dhc4_pid = fork()) == 0 ) {
00255 nic4 = do_dhcpv4(nic->dhc4ctl);
00256
00257 if (nic4 && nic4->lease != 0)
00258 dhcpv4_pack_lease(nic4->lease, dhc4_buf, (14 * 8192));
00259
00260 if (dhc4ctl)
00261 dhcpv4_control_free(dhc4ctl);
00262 if (dhc6ctl)
00263 libdhcp_control_free(dhc6ctl);
00264 free(nic);
00265 exit(0);
00266 }
00267 if ( dhc4_pid == -1 ) {
00268 dhc_log_noctl(error_handler, log_level, LOG_FATAL,
00269 "dhcp_nic: timeout==0 and fork() failed - %d %s.",
00270 errno, strerror(errno)
00271 );
00272
00273 if (dhc4ctl)
00274 dhcpv4_control_free(dhc4ctl);
00275 if (dhc6ctl)
00276 libdhcp_control_free(dhc6ctl);
00277 free(nic);
00278 return 0L;
00279 }
00280 }
00281
00282 if( (preference & DHCPv6_DISABLE) == 0) {
00283 if( (dhc6_pid = fork()) == 0 ) {
00284 nic6 = do_dhcpv6(nic->dhc6ctl, nh, ethX);
00285 if (nic6 && nic6->lease)
00286 dhcpv6_pack_lease(nic6->lease, dhc6_buf, (2 * 8192));
00287
00288 if (dhc4ctl)
00289 dhcpv4_control_free(dhc4ctl);
00290 if (dhc6ctl)
00291 libdhcp_control_free(dhc6ctl);
00292 free(nic);
00293 exit(0);
00294 }
00295
00296 if( dhc6_pid == -1 ) {
00297 dhc_log_noctl(error_handler, log_level, LOG_FATAL,
00298 "dhcp_nic: timeout==0 and fork() failed - %d %s.",
00299 errno, strerror(errno)
00300 );
00301 if (dhc4ctl)
00302 dhcpv4_control_free(dhc4ctl);
00303 if (dhc6ctl)
00304 libdhcp_control_free(dhc6ctl);
00305 free(nic);
00306 return 0L;
00307 }
00308 }
00309
00310 pid_t wait_pid = 0;
00311 int status, dhc4_status=0, dhc6_status=0;
00312
00313 dhc_client_wait:
00314 dhc_log_noctl(error_handler, log_level, LOG_DEBUG, "DHCP: wait - %d %d",
00315 dhc4_status, dhc6_status
00316 );
00317 if ((dhc4_status == 0 || dhc6_status == 0) ||
00318 (dhc6_pid == -1 && dhc4_pid == -1))
00319 goto dhc_clients_finished;
00320
00321 wait_pid = waitpid(-1, &status, 0);
00322
00323 if( wait_pid == -1 )
00324 goto dhc_client_wait;
00325
00326 if( wait_pid == dhc4_pid )
00327 {
00328 DHCPv4_lease *lease4 = dhcpv4_unpack_lease( dhc4_buf );
00329
00330 dhc4_status = dhc4_pid;
00331
00332 dhc_log_noctl(error_handler, log_level, LOG_DEBUG,
00333 "DHCPv4 process finished - %p", lease4);
00334
00335 if ( lease4 )
00336 nic4 = dhcp4_set_lease( dhc4ctl, lease4 );
00337
00338 if( dhc6_status == 0 )
00339 {
00340 if( lease4 && (preference & DHCP_ACCEPT_FIRST_LEASE) )
00341 {
00342 kill(dhc6_pid, SIGKILL);
00343 waitpid(dhc6_pid,0,WNOHANG);
00344 }else
00345 goto dhc_client_wait;
00346 }
00347 }else
00348 if( wait_pid == dhc6_pid )
00349 {
00350 DHCPv6_lease *lease6 = dhcpv6_unpack_lease( dhc6_buf );
00351
00352 dhc6_status = dhc6_pid;
00353
00354 dhc_log_noctl(error_handler, log_level, LOG_DEBUG,
00355 "DHCPv6 process finished - %p", lease6);
00356
00357 if( lease6 )
00358 nic6 = dhcp6_nic_from_lease(dhc6ctl, nh, lease6, eth);
00359
00360 if( dhc4_status == 0 )
00361 {
00362 if( lease6 && (preference & DHCP_ACCEPT_FIRST_LEASE) )
00363 {
00364 kill(dhc4_pid, SIGKILL);
00365 waitpid(dhc4_pid,0,WNOHANG);
00366 }else
00367 goto dhc_client_wait;
00368 }
00369 }else
00370 goto dhc_client_wait;
00371
00372 dhc_clients_finished:
00373 munmap( sma, 16 * 8192 );
00374
00375 }
00376
00377
00378 if (!dhc4ctl) {
00379 nic4 = NULL;
00380 nic->dhc4ctl = NULL;
00381 }
00382 if (nic4 && !dhcp4_process_lease(dhc4ctl)) {
00383 dhc_log_noctl(error_handler, log_level, LOG_ERR,
00384 "DHCP: empty DHCPv4 lease."
00385 );
00386 dhcpv4_control_free(dhc4ctl);
00387 dhc4ctl = nic->dhc4ctl = NULL;
00388 nic4 = NULL;
00389 }
00390 if (nic6 && !dhcp6_process_lease(dhc6ctl, nic6)) {
00391 dhc_log_noctl(error_handler, log_level, LOG_ERR,
00392 "DHCP: empty DHCPv6 lease."
00393 );
00394 dhcpv6_nic_free(nic6);
00395 nic6 = NULL;
00396 free(dhc6ctl);
00397 nic->dhc6ctl = dhc6ctl = NULL;
00398 }
00399
00400 if (!nic6 && !nic4) {
00401 #if 0
00402 if (dhc4ctl)
00403 dhcpv4_control_free(dhc4ctl);
00404 if (dhc6ctl)
00405 libdhcp_control_free(dhc6ctl);
00406 #endif
00407 free(nic);
00408 return NULL;
00409 }
00410
00411 nic->dhcpv4 = (DHCP_config*)nic4;
00412 nic->dhcpv6 = (DHCP_config*)nic6;
00413 return nic;
00414 }
00415
00416 DHCP_nic *dhcp_nic
00417 (
00418 NLH_t nh,
00419 DHCP_Preference preference,
00420 char *ethX,
00421 LIBDHCP_Capability dhc_cap,
00422 time_t timeout,
00423 LIBDHCP_Error_Handler error_handler,
00424 uint8_t log_level,
00425 ...
00426 )
00427 {
00428 va_list dhclient_va;
00429 va_start(dhclient_va, log_level);
00430 DHCP_nic
00431 *nic =
00432 dhcp_nic_va
00433 ( nh,
00434 preference,
00435 ethX,
00436 dhc_cap,
00437 timeout,
00438 error_handler,
00439 log_level,
00440 dhclient_va
00441 );
00442 va_end(dhclient_va);
00443 return nic;
00444 }
00445
00446 void dhcp_nic_free( DHCP_nic *nic )
00447 {
00448 if( nic->dhc4ctl )
00449 {
00450 dhcpv4_control_free ( nic->dhc4ctl );
00451 nic->dhc4ctl = 0L;
00452 }
00453 if( nic->dhc6ctl )
00454 {
00455 free(nic->dhc6ctl);
00456 nic->dhc6ctl = 0L;
00457 }
00458 if( nic->dhcpv6 )
00459 {
00460 dhcpv6_nic_free( (DHCPv6_nic*) nic->dhcpv6 );
00461 nic->dhcpv6 = 0L;
00462 }
00463 if (nic->nh)
00464 nic_close(&nic->nh);
00465 free(nic);
00466 }
00467
00468 NIC_Res_t do_dhcp
00469 (
00470 DHCP_Preference preference,
00471 char *eth_if_name,
00472 LIBDHCP_Capability dhc_cap,
00473 time_t timeout,
00474 LIBDHCP_Error_Handler error_handler,
00475 uint8_t log_level,
00476 ...
00477
00478
00479 )
00480 {
00481 va_list dhclient_va;
00482
00483 va_start(dhclient_va, log_level);
00484
00485 NLH_t nh = nic_open(0);
00486
00487 if ( nh == 0L )
00488 {
00489 dhc_log_noctl(error_handler, log_level, LOG_FATAL, "do_dhcp: nic_open() failed");
00490 nic_close(&nh);
00491 return 0L;
00492 }
00493
00494 DHCP_nic
00495 *nic =
00496 dhcp_nic_va
00497 ( nh,
00498 preference,
00499 eth_if_name,
00500 dhc_cap,
00501 timeout,
00502 error_handler,
00503 log_level,
00504 dhclient_va
00505 );
00506 va_end(dhclient_va);
00507
00508 if ( nic == 0L )
00509 {
00510 dhc_log_noctl(error_handler, log_level, LOG_FATAL, "do_dhcp: no leases obtained.");
00511 nic_close(&nh);
00512 return NIC_FAIL;
00513 }
00514
00515 NIC_Res_t r = dhcp_nic_configure ( nic );
00516
00517 dhcp_nic_free(nic);
00518 nic_close(&nh);
00519
00520 return r;
00521 }
00522
00523 NIC_Res_t
00524 dhcp_nic_configure( DHCP_nic *nic )
00525 {
00526 if (nic == 0)
00527 return NIC_FAIL;
00528
00529 LIBDHCP_Control *ctl = nic->dhc6ctl;
00530 NIC_Res_t res = NIC_FAIL;
00531 char order[3] = { 0, 0, 0 };
00532 char res_order[3] = { 0, 0, 0 };
00533 IPaddr_list_t *dns;
00534 char *search_list = 0, sl_len = 0;
00535 int i;
00536 IPaddr_list_node_t *n=0, *nn=0;
00537
00538 if (nic->dhcpv6) {
00539 if ((!(nic->preference & DHCPv6_DISABLE_RESOLVER)) &&
00540 !STAILQ_EMPTY(&nic->dhcpv6->dns_list))
00541 res_order[0] = 6;
00542 order[0] = 6;
00543 }
00544
00545 if (nic->dhcpv4) {
00546 if ((!(nic->preference & DHCPv6_DISABLE_RESOLVER)) &&
00547 !STAILQ_EMPTY(&nic->dhcpv4->dns_list))
00548 res_order[1] = 4;
00549 order[1] = 4;
00550 }
00551
00552 sl_len = 2;
00553 search_list = calloc(sl_len, sizeof (char));
00554
00555 if (!(nic->preference & IPv6_PREFERENCE)) {
00556 char a = order[0];
00557 order[0] = order[1];
00558 order[1] = a;
00559
00560 a = res_order[0];
00561 res_order[0] = res_order[1];
00562 res_order[1] = res_order[0];
00563 }
00564
00565 for (i = 0; i < 2; i++) {
00566 if (!order[i])
00567 order[i] = order[i+1];
00568 if (!res_order[i])
00569 res_order[i] = res_order[i+1];
00570 }
00571
00572 dns = calloc(1, sizeof(IPaddr_list_t));
00573 STAILQ_INIT(dns);
00574
00575 for (i = 0; res_order[i]; i++) {
00576 switch (res_order[i]) {
00577 case 6:
00578 if (!nic->dhcpv6)
00579 continue;
00580 STAILQ_FOREACH(n, &(nic->dhcpv6->dns_list), link) {
00581 nn = calloc(1, sizeof(IPaddr_list_node_t));
00582 nn->addr = n->addr;
00583 STAILQ_INSERT_TAIL(dns, nn, link);
00584 }
00585
00586 if (nic->dhcpv6->search_list) {
00587 sl_len += strlen(nic->dhcpv6->search_list);
00588 search_list = realloc(search_list, sl_len);
00589 if (search_list[0])
00590 strcat(search_list, " ");
00591 strcat(search_list, nic->dhcpv6->search_list);
00592 }
00593 break;
00594 case 4:
00595 if (!nic->dhcpv4)
00596 continue;
00597 STAILQ_FOREACH(n, &(nic->dhcpv4->dns_list), link) {
00598 nn = calloc(1, sizeof(IPaddr_list_node_t));
00599 nn->addr = n->addr;
00600 STAILQ_INSERT_TAIL(dns, nn, link);
00601 }
00602
00603 if (nic->dhcpv4->search_list) {
00604 sl_len += strlen(nic->dhcpv4->search_list);
00605 search_list = realloc(search_list, sl_len);
00606 if (search_list[0])
00607 strcat(search_list, " ");
00608 strcat(search_list, nic->dhcpv4->search_list);
00609 }
00610 break;
00611 }
00612 }
00613
00614 for (i = 0; order[i]; i++) {
00615 switch (order[i]) {
00616 case 6:
00617 if (!nic->dhcpv6)
00618 continue;
00619
00620 res = nic_configure(nic->nh, nic->dhcpv6->nic,
00621 (nic->preference & DHCPv6_DISABLE_ADDRESSES) ?
00622 0 : &nic->dhcpv6->address_list,
00623 0,
00624 dns, search_list,
00625 0
00626 );
00627 if (res == NIC_OK)
00628 dhc_log(ctl, LOG_INFO, "DHCPv6 interface configuration succeeded.");
00629 else
00630 dhc_log(ctl, LOG_ERR, "DHCPv6 interface configuration failed.");
00631 break;
00632 case 4:
00633 if (!nic->dhcpv4)
00634 continue;
00635
00636
00637 if (nic->dhc4ctl && dhcpv4_mtu_option(nic->dhc4ctl)) {
00638 nic_set_mtu(nic->dhcpv4->nic,
00639 dhcpv4_mtu_option(nic->dhc4ctl));
00640
00641 if (nic_update(nic->dhcpv4->nic) != NIC_OK)
00642 dhc_log(ctl, LOG_ERR, "DHCPv4 MTU set failed.");
00643 }
00644 res=nic_configure(nic->nh, nic->dhcpv4->nic,
00645 (nic->preference & DHCPv4_DISABLE_ADDRESSES) ?
00646 0 : &nic->dhcpv4->address_list,
00647 (nic->preference & DHCPv4_DISABLE_ROUTES) ?
00648 0 : &nic->dhcpv4->route_list,
00649 dns, search_list,
00650 (nic->preference & DHCPv4_DISABLE_HOSTNAME_SET) ?
00651 0 : nic->dhcpv4->host_name
00652 );
00653 if (res == NIC_OK)
00654 dhc_log(ctl, LOG_INFO, "DHCPv4 interface configuration succeeded.");
00655 else
00656 dhc_log(ctl, LOG_ERR, "DHCPv4 interface configuration failed.");
00657 break;
00658 }
00659 }
00660
00661 if (!STAILQ_EMPTY(dns)) {
00662 nn = STAILQ_FIRST( dns );
00663 while ((n = nn)) {
00664 nn = STAILQ_NEXT(n, link);
00665 free(n);
00666 }
00667 }
00668 if (dns)
00669 free(dns);
00670 if (search_list)
00671 free(search_list);
00672 return res;
00673 }
00674
00675
00676
00677