📄 dhcp6c.c
字号:
FNAME, strerror(errno)); exit(1); }}static voidclient6_ifinit(char *device){ struct dhcp6_if *ifp = dhcp6_if; struct dhcp6_event *ev; char iaidstr[20]; dhcp6_init_iaidaddr(); /* get iaid for each interface */ if (num_device == 0) { if ((num_device = create_iaid(&iaidtab[0], num_device)) < 0) exit(1); ifp->iaidinfo.iaid = get_iaid(ifp->ifname, &iaidtab[0], num_device); if (ifp->iaidinfo.iaid == 0) { dprintf(LOG_DEBUG, "%s" "interface %s iaid failed to be created", FNAME, ifp->ifname); exit(1); } dprintf(LOG_DEBUG, "%s" "interface %s iaid is %u", FNAME, ifp->ifname, ifp->iaidinfo.iaid); } client6_iaidaddr.ifp = ifp; memcpy(&client6_iaidaddr.client6_info.iaidinfo, &ifp->iaidinfo, sizeof(client6_iaidaddr.client6_info.iaidinfo)); duidcpy(&client6_iaidaddr.client6_info.clientid, &client_duid); /* parse the lease file */ strcpy(leasename, PATH_CLIENT6_LEASE); sprintf(iaidstr, "%u", ifp->iaidinfo.iaid); strcat(leasename, iaidstr); if ((client6_lease_file = init_leases(leasename)) == NULL) { dprintf(LOG_ERR, "%s" "failed to parse lease file", FNAME); exit(1); } strcpy(client6_lease_temp, leasename); strcat(client6_lease_temp, "XXXXXX"); client6_lease_file = sync_leases(client6_lease_file, leasename, client6_lease_temp); if (client6_lease_file == NULL) exit(1); if (!TAILQ_EMPTY(&client6_iaidaddr.lease_list)) { struct dhcp6_lease *cl; struct dhcp6_listval *lv; if (!(client6_request_flag & CLIENT6_REQUEST_ADDR) && !(client6_request_flag & CLIENT6_RELEASE_ADDR)) client6_request_flag |= CLIENT6_CONFIRM_ADDR; if (TAILQ_EMPTY(&request_list)) { if (create_request_list(1) < 0) exit(1); } else if (client6_request_flag & CLIENT6_RELEASE_ADDR) { for (lv = TAILQ_FIRST(&request_list); lv; lv = TAILQ_NEXT(lv, link)) { if (dhcp6_find_lease(&client6_iaidaddr, &lv->val_dhcp6addr) == NULL) { dprintf(LOG_INFO, "this address %s is not" " leased by this client", in6addr2str(&lv->val_dhcp6addr.addr,0)); exit(0); } } } } else if (client6_request_flag & CLIENT6_RELEASE_ADDR) { dprintf(LOG_INFO, "no ipv6 addresses are leased by client"); exit(0); } setup_interface(ifp->ifname); ifp->link_flag |= IFF_RUNNING; /* get addrconf prefix from kernel */ (void)get_if_rainfo(ifp); /* set up check link timer and sync file timer */ if ((ifp->link_timer = dhcp6_add_timer(check_link_timo, ifp)) < 0) { dprintf(LOG_ERR, "%s" "failed to create a timer", FNAME); exit(1); } if ((ifp->sync_timer = dhcp6_add_timer(check_lease_file_timo, ifp)) < 0) { dprintf(LOG_ERR, "%s" "failed to create a timer", FNAME); exit(1); } /* DAD timer set up after getting the address */ ifp->dad_timer = NULL; /* create an event for the initial delay */ if ((ev = dhcp6_create_event(ifp, DHCP6S_INIT)) == NULL) { dprintf(LOG_ERR, "%s" "failed to create an event", FNAME); exit(1); } ifp->servers = NULL; ev->ifp->current_server = NULL; TAILQ_INSERT_TAIL(&ifp->event_list, ev, link); if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) { dprintf(LOG_ERR, "%s" "failed to add a timer for %s", FNAME, ifp->ifname); exit(1); } dhcp6_reset_timer(ev);}static voidfree_resources(struct dhcp6_if *ifp){ struct dhcp6_event *ev, *ev_next; struct dhcp6_lease *sp, *sp_next; struct stat buf; if (client6_iaidaddr.client6_info.type == IAPD && !TAILQ_EMPTY(&client6_iaidaddr.lease_list)) radvd_parse(&client6_iaidaddr, ADDR_REMOVE); else { for (sp = TAILQ_FIRST(&client6_iaidaddr.lease_list); sp; sp = sp_next) { sp_next = TAILQ_NEXT(sp, link); if (client6_ifaddrconf(IFADDRCONF_REMOVE, &sp->lease_addr) != 0) dprintf(LOG_INFO, "%s" "deconfiging address %s failed", FNAME, in6addr2str(&sp->lease_addr.addr, 0)); } } dprintf(LOG_DEBUG, "%s" " remove all events on interface", FNAME); /* cancel all outstanding events for each interface */ for (ev = TAILQ_FIRST(&ifp->event_list); ev; ev = ev_next) { ev_next = TAILQ_NEXT(ev, link); dhcp6_remove_event(ev); } /* XXX: check the last dhcpv6 client daemon to restore the original file */ { /* restore /etc/radv.conf.bak back to /etc/radvd.conf */ if (!lstat(RADVD_CONF_BAK_FILE, &buf)) rename(RADVD_CONF_BAK_FILE, RADVD_CONF_FILE); /* restore /etc/resolv.conf.dhcpv6.bak back to /etc/resolv.conf */ if (!lstat(RESOLV_CONF_BAK_FILE, &buf)) { if (rename(RESOLV_CONF_BAK_FILE, RESOLV_CONF_FILE)) dprintf(LOG_ERR, "%s" " failed to backup resolv.conf", FNAME); } } free_servers(ifp);}static voidprocess_signals(){ struct stat buf; if ((sig_flags & SIGF_TERM)) { dprintf(LOG_INFO, FNAME "exiting"); free_resources(dhcp6_if); unlink(DHCP6C_PIDFILE); exit(0); } if ((sig_flags & SIGF_HUP)) { dprintf(LOG_INFO, FNAME "restarting"); free_resources(dhcp6_if); client6_ifinit(device); } if ((sig_flags & SIGF_CLEAN)) { free_resources(dhcp6_if); exit(0); } sig_flags = 0;}static voidclient6_mainloop(){ struct timeval *w; int ret; fd_set r; while(1) { if (sig_flags) process_signals(); w = dhcp6_check_timer(); FD_ZERO(&r); FD_SET(insock, &r); ret = select(insock + 1, &r, NULL, NULL, w); switch (ret) { case -1: if (errno != EINTR) { dprintf(LOG_ERR, "%s" "select: %s", FNAME, strerror(errno)); exit(1); } break; case 0: /* timeout */ break; /* dhcp6_check_timer() will treat the case */ default: /* received a packet */ client6_recv(); } }}struct dhcp6_timer *client6_timo(arg) void *arg;{ struct dhcp6_event *ev = (struct dhcp6_event *)arg; struct dhcp6_if *ifp; struct timeval now; struct ra_info *rainfo; ifp = ev->ifp; ev->timeouts++; gettimeofday(&now, NULL); if ((ev->max_retrans_cnt && ev->timeouts >= ev->max_retrans_cnt) || (ev->max_retrans_dur && (now.tv_sec - ev->start_time.tv_sec) >= ev->max_retrans_dur)) { /* XXX: check up the duration time for renew & rebind */ dprintf(LOG_INFO, "%s" "no responses were received", FNAME); dhcp6_remove_event(ev); /* XXX: should free event data? */ return (NULL); } switch(ev->state) { case DHCP6S_INIT: /* From INIT state client could * go to CONFIRM state if the client reboots; * go to RELEASE state if the client issues a release; * go to INFOREQ state if the client requests info-only; * go to SOLICIT state if the client requests addresses; */ ev->timeouts = 0; /* indicate to generate a new XID. */ /* * three cases client send information request: * 1. configuration file includes information-only * 2. command line includes -I * 3. check interface flags if managed bit isn't set and * if otherconf bit set by RA * and information-only, conmand line -I are not set. */ if ((ifp->send_flags & DHCIFF_INFO_ONLY) || (client6_request_flag & CLIENT6_INFO_REQ) || (!(ifp->ra_flag & IF_RA_MANAGED) && (ifp->ra_flag & IF_RA_OTHERCONF))) ev->state = DHCP6S_INFOREQ; else if (client6_request_flag & CLIENT6_RELEASE_ADDR) /* do release */ ev->state = DHCP6S_RELEASE; else if (client6_request_flag & CLIENT6_CONFIRM_ADDR) { struct dhcp6_listval *lv; /* do confirm for reboot for IANA, IATA*/ if (client6_iaidaddr.client6_info.type == IAPD) ev->state = DHCP6S_REBIND; else ev->state = DHCP6S_CONFIRM; for (lv = TAILQ_FIRST(&request_list); lv; lv = TAILQ_NEXT(lv, link)) { lv->val_dhcp6addr.preferlifetime = 0; lv->val_dhcp6addr.validlifetime = 0; } } else ev->state = DHCP6S_SOLICIT; dhcp6_set_timeoparam(ev); case DHCP6S_SOLICIT: if (ifp->servers) { ifp->current_server = select_server(ifp); if (ifp->current_server == NULL) { /* this should not happen! */ dprintf(LOG_ERR, "%s" "can't find a server", FNAME); exit(1); /* XXX */ } /* if get the address assginment break */ if (!TAILQ_EMPTY(&client6_iaidaddr.lease_list)) { dhcp6_remove_event(ev); return (NULL); } ev->timeouts = 0; ev->state = DHCP6S_REQUEST; dhcp6_set_timeoparam(ev); } case DHCP6S_INFOREQ: case DHCP6S_REQUEST: client6_send(ev); break; case DHCP6S_RELEASE: case DHCP6S_DECLINE: case DHCP6S_CONFIRM: case DHCP6S_RENEW: case DHCP6S_REBIND: if (!TAILQ_EMPTY(&request_list)) client6_send(ev); else { dprintf(LOG_INFO, "%s" "all information to be updated were canceled", FNAME); dhcp6_remove_event(ev); return (NULL); } break; default: break; } dhcp6_reset_timer(ev); return (ev->timer);}static struct dhcp6_serverinfo *select_server(ifp) struct dhcp6_if *ifp;{ struct dhcp6_serverinfo *s; /* * pick the best server according to dhcpv6-26 Section 17.1.3 * XXX: we currently just choose the one that is active and has the * highest preference. */ for (s = ifp->servers; s; s = s->next) { if (s->active) { dprintf(LOG_DEBUG, "%s" "picked a server (ID: %s)", FNAME, duidstr(&s->optinfo.serverID)); return (s); } } return (NULL);}static voidclient6_signal(sig) int sig;{ dprintf(LOG_INFO, FNAME "received a signal (%d)", sig); switch (sig) { case SIGTERM: sig_flags |= SIGF_TERM; break; case SIGHUP: sig_flags |= SIGF_HUP; break; case SIGINT: case SIGKILL: sig_flags |= SIGF_CLEAN; break; default: break; }}voidclient6_send(ev) struct dhcp6_event *ev;{ struct dhcp6_if *ifp; char buf[BUFSIZ]; struct sockaddr_in6 dst; struct dhcp6 *dh6; struct dhcp6_optinfo optinfo; ssize_t optlen, len; struct timeval duration, now; ifp = ev->ifp; dh6 = (struct dhcp6 *)buf; memset(dh6, 0, sizeof(*dh6)); switch(ev->state) { case DHCP6S_SOLICIT: dh6->dh6_msgtype = DH6_SOLICIT; break; case DHCP6S_REQUEST: if (ifp->current_server == NULL) { dprintf(LOG_ERR, "%s" "assumption failure", FNAME); exit(1); /* XXX */ } dh6->dh6_msgtype = DH6_REQUEST; break; case DHCP6S_RENEW: if (ifp->current_server == NULL) { dprintf(LOG_ERR, "%s" "assumption failure", FNAME); exit(1); /* XXX */ } dh6->dh6_msgtype = DH6_RENEW; break; case DHCP6S_DECLINE: if (ifp->current_server == NULL) { dprintf(LOG_ERR, "%s" "assumption failure", FNAME); exit(1); /* XXX */ } dh6->dh6_msgtype = DH6_DECLINE; break; case DHCP6S_INFOREQ: dh6->dh6_msgtype = DH6_INFORM_REQ; break; case DHCP6S_REBIND: dh6->dh6_msgtype = DH6_REBIND; break; case DHCP6S_CONFIRM: dh6->dh6_msgtype = DH6_CONFIRM; break; case DHCP6S_RELEASE: dh6->dh6_msgtype = DH6_RELEASE; break; default: dprintf(LOG_ERR, "%s" "unexpected state %d", FNAME, ev->state); exit(1); /* XXX */ } /* * construct options */ dhcp6_init_options(&optinfo); if (ev->timeouts == 0) { gettimeofday(&ev->start_time, NULL); optinfo.elapsed_time = 0; /* * A client SHOULD generate a random number that cannot easily * be guessed or predicted to use as the transaction ID for * each new message it sends. * * A client MUST leave the transaction-ID unchanged in * retransmissions of a message. [dhcpv6-26 15.1] */ ev->xid = random() & DH6_XIDMASK; dprintf(LOG_DEBUG, "%s" "ifp %p event %p a new XID (%x) is generated", FNAME, ifp, ev, ev->xid); } else { unsigned int etime; gettimeofday(&now, NULL); timeval_sub(&now, &(ev->start_time), &duration); optinfo.elapsed_time = etime = (duration.tv_sec) * 100 + (duration.tv_usec) / 10000; if (etime > DHCP6_ELAPSEDTIME_MAX) optinfo.elapsed_time = DHCP6_ELAPSEDTIME_MAX; else optinfo.elapsed_time = etime; } dh6->dh6_xid &= ~ntohl(DH6_XIDMASK); dh6->dh6_xid |= htonl(ev->xid); len = sizeof(*dh6); /* server ID */ switch(ev->state) { case DHCP6S_REQUEST: case DHCP6S_RENEW: case DHCP6S_DECLINE: if (&ifp->current_server->optinfo == NULL) exit(1); dprintf(LOG_DEBUG, "current server ID %s", duidstr(&ifp->current_server->optinfo.serverID)); if (duidcpy(&optinfo.serverID, &ifp->current_server->optinfo.serverID)) { dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME); goto end; } break; case DHCP6S_RELEASE:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -