⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dhcp6c.c

📁 IPv6环境下的DHCP实现
💻 C
📖 第 1 页 / 共 3 页
字号:
  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 + -