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