📄 dhcp6c.c
字号:
end: dhcp6_clear_options(&optinfo); return;}voidclient6_send_rebind(ev) struct dhcp6_event *ev;{ struct dhcp6_if *ifp; struct dhcp6_eventdata *evd; struct dhcp6_optinfo optinfo; struct dhcp6_listval *dlv; struct dhcp6 *dh6; char buf[BUFSIZ]; ssize_t optlen, len; struct sockaddr_in6 dst; ifp = ev->ifp; dh6 = (struct dhcp6 *)buf; memset(dh6, 0, sizeof(*dh6)); dh6->dh6_msgtype = DH6_REBIND; if (ev->timeouts == 0) {#ifdef HAVE_ARC4RANDOM ev->xid = arc4random() & DH6_XIDMASK;#else ev->xid = random() & DH6_XIDMASK;#endif dprintf(LOG_DEBUG, "%s" "a new XID (%x) is generated", FNAME, ev->xid); } dh6->dh6_xid &= ~ntohl(DH6_XIDMASK); dh6->dh6_xid |= htonl(ev->xid); len = sizeof(*dh6); /* * construct options */ dhcp6_init_options(&optinfo); /* client ID */ if (duidcpy(&optinfo.clientID, &client_duid)) { dprintf(LOG_ERR, "%s" "failed to copy client ID", FNAME); goto end; } /* configuration information to be rebound */ for (evd = TAILQ_FIRST(&ev->data_list); evd; evd = TAILQ_NEXT(evd, link)) { switch(evd->type) { case DHCP6_DATA_PREFIX: if (dhcp6_add_listval(&optinfo.prefix_list, &((struct dhcp6_siteprefix *)evd->data)->prefix, DHCP6_LISTVAL_PREFIX6) == NULL) { dprintf(LOG_ERR, "%s" "failed to add a " "prefix", FNAME); goto end; } break; default: dprintf(LOG_ERR, "%s" "unexpected event data (type %d)", FNAME, evd->type); exit(1); } } /* set options in the message */ if ((optlen = dhcp6_set_options((struct dhcp6opt *)(dh6 + 1), (struct dhcp6opt *)(buf + sizeof(buf)), &optinfo)) < 0) { dprintf(LOG_INFO, "%s" "failed to construct options", FNAME); goto end; } len += optlen; /* * Unless otherwise specified, a client sends DHCP messages to the * All_DHCP_Relay_Agents_and_Servers or the DHCP_Anycast address. * [dhcpv6-26 Section 13.] * Our current implementation always follows the case. */ dst = *sa6_allagent; dst.sin6_scope_id = ifp->linkid; if (sendto(ifp->outsock, buf, len, 0, (struct sockaddr *)&dst, ((struct sockaddr *)&dst)->sa_len) == -1) { dprintf(LOG_ERR, FNAME "transmit failed: %s", strerror(errno)); goto end; } dprintf(LOG_DEBUG, "%s" "send %s to %s", FNAME, dhcp6msgstr(dh6->dh6_msgtype), addr2str((struct sockaddr *)&dst)); end: dhcp6_clear_options(&optinfo); return;}static intclient6_recv(){ char rbuf[BUFSIZ], cmsgbuf[BUFSIZ]; struct msghdr mhdr; struct iovec iov; struct sockaddr_storage from; struct dhcp6_if *ifp; struct dhcp6opt *p, *ep; struct dhcp6_optinfo optinfo; ssize_t len; struct dhcp6 *dh6; struct cmsghdr *cm; struct in6_pktinfo *pi = NULL; memset(&iov, 0, sizeof(iov)); memset(&mhdr, 0, sizeof(mhdr)); iov.iov_base = (caddr_t)rbuf; iov.iov_len = sizeof(rbuf); mhdr.msg_name = (caddr_t)&from; mhdr.msg_namelen = sizeof(from); mhdr.msg_iov = &iov; mhdr.msg_iovlen = 1; mhdr.msg_control = (caddr_t)cmsgbuf; mhdr.msg_controllen = sizeof(cmsgbuf); if ((len = recvmsg(insock, &mhdr, 0)) < 0) { dprintf(LOG_ERR, "%s" "recvmsg: %s", FNAME, strerror(errno)); return; } /* detect receiving interface */ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&mhdr); cm; cm = (struct cmsghdr *)CMSG_NXTHDR(&mhdr, cm)) { if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO && cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); } } if (pi == NULL) { dprintf(LOG_NOTICE, "%s" "failed to get packet info", FNAME); return; } if ((ifp = find_ifconfbyid((unsigned int)pi->ipi6_ifindex)) == NULL) { dprintf(LOG_INFO, "%s" "unexpected interface (%d)", FNAME, (unsigned int)pi->ipi6_ifindex); return; } if (len < sizeof(*dh6)) { dprintf(LOG_INFO, "%s" "short packet", FNAME); return; } dh6 = (struct dhcp6 *)rbuf; dprintf(LOG_DEBUG, "%s" "receive %s from %s on %s", FNAME, dhcp6msgstr(dh6->dh6_msgtype), addr2str((struct sockaddr *)&from), ifp->ifname); /* get options */ dhcp6_init_options(&optinfo); p = (struct dhcp6opt *)(dh6 + 1); ep = (struct dhcp6opt *)((char *)dh6 + len); if (dhcp6_get_options(p, ep, &optinfo) < 0) { dprintf(LOG_INFO, "%s" "failed to parse options", FNAME); return; } switch(dh6->dh6_msgtype) { case DH6_ADVERTISE: (void)client6_recvadvert(ifp, dh6, len, &optinfo); break; case DH6_REPLY: (void)client6_recvreply(ifp, dh6, len, &optinfo); break; default: dprintf(LOG_INFO, "%s" "received an unexpected message (%s) " "from %s", FNAME, dhcp6msgstr(dh6->dh6_msgtype), addr2str((struct sockaddr *)&from)); break; } end: dhcp6_clear_options(&optinfo); return;}static intclient6_recvadvert(ifp, dh6, len, optinfo0) struct dhcp6_if *ifp; struct dhcp6 *dh6; ssize_t len; struct dhcp6_optinfo *optinfo0;{ struct dhcp6_serverinfo *newserver, *s, **sp; struct dhcp6_event *ev; /* find the corresponding event based on the received xid */ ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK); if (ev == NULL) { dprintf(LOG_INFO, "%s" "XID mismatch", FNAME); return -1; } if (ev->state != DHCP6S_SOLICIT || (ifp->send_flags & DHCIFF_RAPID_COMMIT)) { dprintf(LOG_INFO, "%s" "unexpected advertise", FNAME); return -1; } /* packet validation based on Section 15.3 of dhcpv6-26. */ if (optinfo0->serverID.duid_len == 0) { dprintf(LOG_INFO, "%s" "no server ID option", FNAME); return -1; } else { dprintf(LOG_DEBUG, "%s" "server ID: %s, pref=%d", FNAME, duidstr(&optinfo0->serverID), optinfo0->pref); } if (optinfo0->clientID.duid_len == 0) { dprintf(LOG_INFO, "%s" "no client ID option", FNAME); return -1; } if (duidcmp(&optinfo0->clientID, &client_duid)) { dprintf(LOG_INFO, "%s" "client DUID mismatch", FNAME); return -1; } /* * The client MUST ignore any Advertise message that includes a Status * Code option containing the value NoAddrsAvail. * [dhcpv6-26, Section 17.1.3]. * XXX: we should not follow this when we do not need addresses!! */ ; /* ignore the server if it is known */ if (find_server(ifp, &optinfo0->serverID)) { dprintf(LOG_INFO, "%s" "duplicated server (ID: %s)", FNAME, duidstr(&optinfo0->serverID)); return -1; } /* keep the server */ if ((newserver = malloc(sizeof(*newserver))) == NULL) { dprintf(LOG_ERR, "%s" "memory allocation failed for server", FNAME); return -1; } memset(newserver, 0, sizeof(*newserver)); dhcp6_init_options(&newserver->optinfo); if (dhcp6_copy_options(&newserver->optinfo, optinfo0)) { dprintf(LOG_ERR, "%s" "failed to copy options", FNAME); free(newserver); return -1; } if (optinfo0->pref != DH6OPT_PREF_UNDEF) newserver->pref = optinfo0->pref; newserver->active = 1; for (sp = &ifp->servers; *sp; sp = &(*sp)->next) { if ((*sp)->pref != DH6OPT_PREF_MAX && (*sp)->pref < newserver->pref) { break; } } newserver->next = *sp; *sp = newserver; /* if the server has an extremely high preference, just use it. */ if (newserver->pref == DH6OPT_PREF_MAX) { ev->timeouts = 0; ev->state = DHCP6S_REQUEST; ifp->current_server = newserver; client6_send(ev); dhcp6_set_timeoparam(ev); dhcp6_reset_timer(ev); } else if (ifp->servers->next == NULL) { struct timeval *rest, elapsed, tv_rt, tv_irt, timo; /* * If this is the first advertise, adjust the timer so that * the client can collect other servers until IRT elapses. * XXX: we did not want to do such "low level" timer * calculation here. */ rest = dhcp6_timer_rest(ev->timer); tv_rt.tv_sec = (ev->retrans * 1000) / 1000000; tv_rt.tv_usec = (ev->retrans * 1000) % 1000000; tv_irt.tv_sec = (ev->init_retrans * 1000) / 1000000; tv_irt.tv_usec = (ev->init_retrans * 1000) % 1000000; timeval_sub(&tv_rt, rest, &elapsed); if (TIMEVAL_LEQ(elapsed, tv_irt)) timeval_sub(&tv_irt, &elapsed, &timo); else timo.tv_sec = timo.tv_usec = 0; dprintf(LOG_DEBUG, "%s" "reset timer for %s to %d.%06d", FNAME, ifp->ifname, (int)timo.tv_sec, (int)timo.tv_usec); dhcp6_set_timer(&timo, ev->timer); } return 0;}static struct dhcp6_serverinfo *find_server(ifp, duid) struct dhcp6_if *ifp; struct duid *duid;{ struct dhcp6_serverinfo *s; for (s = ifp->servers; s; s = s->next) { if (duidcmp(&s->optinfo.serverID, duid) == 0) return(s); } return(NULL);}static intclient6_recvreply(ifp, dh6, len, optinfo) struct dhcp6_if *ifp; struct dhcp6 *dh6; ssize_t len; struct dhcp6_optinfo *optinfo;{ struct dhcp6_listval *lv; struct dhcp6_event *ev; /* find the corresponding event based on the received xid */ ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK); if (ev == NULL) { dprintf(LOG_INFO, "%s" "XID mismatch", FNAME); return -1; } if (ev->state != DHCP6S_INFOREQ && ev->state != DHCP6S_REQUEST && ev->state != DHCP6S_RENEW && ev->state != DHCP6S_REBIND && (ev->state != DHCP6S_SOLICIT || !(ifp->send_flags & DHCIFF_RAPID_COMMIT))) { dprintf(LOG_INFO, "%s" "unexpected reply", FNAME); return -1; } /* A Reply message must contain a Server ID option */ if (optinfo->serverID.duid_len == 0) { dprintf(LOG_INFO, "%s" "no server ID option", FNAME); return -1; } /* * DUID in the Client ID option (which must be contained for our * client implementation) must match ours. */ if (optinfo->clientID.duid_len == 0) { dprintf(LOG_INFO, "%s" "no client ID option", FNAME); return -1; } if (duidcmp(&optinfo->clientID, &client_duid)) { dprintf(LOG_INFO, "%s" "client DUID mismatch", FNAME); return -1; } /* * The client MAY choose to report any status code or message from the * status code option in the Reply message. * [dhcpv6-26 Section 18.1.8] */ for (lv = TAILQ_FIRST(&optinfo->stcode_list); lv; lv = TAILQ_NEXT(lv, link)) { dprintf(LOG_INFO, "%s" "status code: %s", FNAME, dhcp6_stcodestr(lv->val_num)); } if (!TAILQ_EMPTY(&optinfo->dns_list)) { struct dhcp6_listval *d; int i = 0; for (d = TAILQ_FIRST(&optinfo->dns_list); d; d = TAILQ_NEXT(d, link), i++) { dprintf(LOG_DEBUG, "%s" "nameserver[%d] %s", FNAME, i, in6addr2str(&d->val_addr6, 0)); } } if (ev->state == DHCP6S_RENEW || ev->state == DHCP6S_REBIND) { /* * Update configuration information to be renewed or rebound. * Note that the returned list may be empty, in which case * the waiting information should be removed. */ prefix6_update(ev, &optinfo->prefix_list, &optinfo->serverID); } else { for (lv = TAILQ_FIRST(&optinfo->prefix_list); lv; lv = TAILQ_NEXT(lv, link)) { prefix6_add(ifp, &lv->val_prefix6, &optinfo->serverID); } } dhcp6_remove_event(ev); dprintf(LOG_DEBUG, "%s" "got an expected reply, sleeping.", FNAME); return 0;}static struct dhcp6_event *find_event_withid(ifp, xid) struct dhcp6_if *ifp; u_int32_t xid;{ struct dhcp6_event *ev; for (ev = TAILQ_FIRST(&ifp->event_list); ev; ev = TAILQ_NEXT(ev, link)) { if (ev->xid == xid) return(ev); } return(NULL);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -