common.c

来自「IPv6环境下的DHCP实现」· C语言 代码 · 共 1,486 行 · 第 1/3 页

C
1,486
字号
/* return IPv6 address scope type. caller assumes that smaller is narrower. */intin6_scope(addr)	struct in6_addr *addr;{	int scope;	if (addr->s6_addr[0] == 0xfe) {		scope = addr->s6_addr[1] & 0xc0;		switch (scope) {		case 0x80:			return 2; /* link-local */			break;		case 0xc0:			return 5; /* site-local */			break;		default:			return 14; /* global: just in case */			break;		}	}	/* multicast scope. just return the scope field */	if (addr->s6_addr[0] == 0xff)		return(addr->s6_addr[1] & 0x0f);	if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) {		if (addr->s6_addr[15] == 1) /* loopback */			return 1;		if (addr->s6_addr[15] == 0) /* unspecified */			return 0; /* XXX: good value? */	}	return 14;		/* global */}static intin6_matchflags(addr, ifnam, flags)	struct sockaddr *addr;	char *ifnam;	int flags;{	int s;	struct in6_ifreq ifr6;	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {		warn("in6_matchflags: socket(DGRAM6)");		return(-1);	}	memset(&ifr6, 0, sizeof(ifr6));	strncpy(ifr6.ifr_name, ifnam, sizeof(ifr6.ifr_name));	ifr6.ifr_addr = *(struct sockaddr_in6 *)addr;	if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {		warn("in6_matchflags: ioctl(SIOCGIFAFLAG_IN6, %s)",		     addr2str(addr));		close(s);		return(-1);	}	close(s);	return(ifr6.ifr_ifru.ifru_flags6 & flags);}intget_duid(idfile, duid)	char *idfile;	struct duid *duid;{	FILE *fp = NULL;	u_int16_t len = 0, hwtype;	struct dhcp6_duid_type1 *dp; /* we only support the type1 DUID */	char tmpbuf[256];	/* DUID should be no more than 256 bytes */	if ((fp = fopen(idfile, "r")) == NULL && errno != ENOENT)		dprintf(LOG_NOTICE, "%s" "failed to open DUID file: %s",		    FNAME, idfile);	if (fp) {		/* decode length */		if (fread(&len, sizeof(len), 1, fp) != 1) {			dprintf(LOG_ERR, "%s" "DUID file corrupted", FNAME);			goto fail;		}	} else {		int l;		if ((l = gethwid(tmpbuf, sizeof(tmpbuf), NULL, &hwtype)) < 0) {			dprintf(LOG_INFO, "%s"			    "failed to get a hardware address", FNAME);			goto fail;		}		len = l + sizeof(struct dhcp6_duid_type1);	}	memset(duid, 0, sizeof(*duid));	duid->duid_len = len;	if ((duid->duid_id = (char *)malloc(len)) == NULL) {		dprintf(LOG_ERR, "%s" "failed to allocate memory", FNAME);		goto fail;	}	/* copy (and fill) the ID */	if (fp) {		if (fread(duid->duid_id, len, 1, fp) != 1) {			dprintf(LOG_ERR, "%s" "DUID file corrupted", FNAME);			goto fail;		}		dprintf(LOG_DEBUG, "%s"		    "extracted an existing DUID from %s: %s", FNAME,		    idfile, duidstr(duid));	} else {		u_int64_t t64;		dp = (struct dhcp6_duid_type1 *)duid->duid_id;		dp->dh6duid1_type = htons(1); /* type 1 */		dp->dh6duid1_hwtype = htons(hwtype);		/* time is Jan 1, 2000 (UTC), modulo 2^32 */		t64 = (u_int64_t)(time(NULL) - 946684800);		dp->dh6duid1_time = htonl((u_long)(t64 & 0xffffffff));		memcpy((void *)(dp + 1), tmpbuf, (len - sizeof(*dp)));		dprintf(LOG_DEBUG, "%s" "generated a new DUID: %s", FNAME,			duidstr(duid));	}	/* save the (new) ID to the file for next time */	if (!fp) {		if ((fp = fopen(idfile, "w+")) == NULL) {			dprintf(LOG_ERR, "%s"			    "failed to open DUID file for save", FNAME);			goto fail;		}		if ((fwrite(&len, sizeof(len), 1, fp)) != 1) {			dprintf(LOG_ERR, "%s" "failed to save DUID", FNAME);			goto fail;		}		if ((fwrite(duid->duid_id, len, 1, fp)) != 1) {			dprintf(LOG_ERR, "%s" "failed to save DUID", FNAME);			goto fail;		}		dprintf(LOG_DEBUG, "%s" "saved generated DUID to %s", FNAME,			idfile);	}	if (fp)		fclose(fp);	return(0);  fail:	if (fp)		fclose(fp);	if (duid->duid_id) {		free(duid->duid_id);		duid->duid_id = NULL; /* for safety */	}	return(-1);}static ssize_tgethwid(buf, len, ifname, hwtypep)	char *buf;	int len;	const char *ifname;	u_int16_t *hwtypep;{	struct ifaddrs *ifa, *ifap;	struct sockaddr_dl *sdl;	ssize_t l;	if (getifaddrs(&ifap) < 0)		return -1;	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {		if (ifname && strcmp(ifa->ifa_name, ifname) != 0)			continue;		if (ifa->ifa_addr->sa_family != AF_LINK)			continue;		sdl = (struct sockaddr_dl *)ifa->ifa_addr;		if (len < 2 + sdl->sdl_alen)			goto fail;		/*		 * translate interface type to hardware type based on		 * http://www.iana.org/assignments/arp-parameters		 */		switch(sdl->sdl_type) {		case IFT_ETHER:#ifdef IFT_IEEE80211		case IFT_IEEE80211:#endif			*hwtypep = ARPHRD_ETHER;			break;		default:			continue; /* XXX */		}		dprintf(LOG_DEBUG, "%s" "found an interface %s for DUID",			FNAME, ifa->ifa_name);		memcpy(buf, LLADDR(sdl), sdl->sdl_alen);		l = sdl->sdl_alen; /* sdl will soon be freed */		freeifaddrs(ifap);		return l;	}  fail:	freeifaddrs(ifap);	return -1;}voiddhcp6_init_options(optinfo)	struct dhcp6_optinfo *optinfo;{	memset(optinfo, 0, sizeof(*optinfo));	optinfo->pref = DH6OPT_PREF_UNDEF;	TAILQ_INIT(&optinfo->reqopt_list);	TAILQ_INIT(&optinfo->stcode_list);	TAILQ_INIT(&optinfo->dns_list);	TAILQ_INIT(&optinfo->prefix_list);}voiddhcp6_clear_options(optinfo)	struct dhcp6_optinfo *optinfo;{	struct dhcp6_optconf *ropt, *ropt_next;	struct dnslist *d, *dn;	struct delegated_prefix *p, *pn;	duidfree(&optinfo->clientID);	duidfree(&optinfo->serverID);	dhcp6_clear_list(&optinfo->reqopt_list);	dhcp6_clear_list(&optinfo->stcode_list);	dhcp6_clear_list(&optinfo->dns_list);	dhcp6_clear_list(&optinfo->prefix_list);	dhcp6_init_options(optinfo);}intdhcp6_copy_options(dst, src)	struct dhcp6_optinfo *dst, *src;{	if (duidcpy(&dst->clientID, &src->clientID))		goto fail;	if (duidcpy(&dst->serverID, &src->serverID))		goto fail;	dst->rapidcommit = src->rapidcommit;	if (dhcp6_copy_list(&dst->reqopt_list, &src->reqopt_list))		goto fail;	if (dhcp6_copy_list(&dst->stcode_list, &src->stcode_list))		goto fail;	if (dhcp6_copy_list(&dst->dns_list, &src->dns_list))		goto fail;	if (dhcp6_copy_list(&dst->prefix_list, &src->prefix_list))		goto fail;	dst->pref = src->pref;	return 0;  fail:	/* cleanup temporary resources */	dhcp6_clear_options(dst);	return -1;}intdhcp6_get_options(p, ep, optinfo)	struct dhcp6opt *p, *ep;	struct dhcp6_optinfo *optinfo;{	struct dhcp6opt *np, opth;	struct dhcp6_listval *lv;	int i, opt, optlen, reqopts, num;	char *cp, *val;	u_int16_t val16;	for (; p + 1 <= ep; p = np) {		struct duid duid0;		/*		 * get the option header.  XXX: since there is no guarantee		 * about the header alignment, we need to make a local copy.		 */		memcpy(&opth, p, sizeof(opth));		optlen = ntohs(opth.dh6opt_len);		opt = ntohs(opth.dh6opt_type);		cp = (char *)(p + 1);		np = (struct dhcp6opt *)(cp + optlen);		dprintf(LOG_DEBUG, "%s" "get DHCP option %s, len %d",		    FNAME, dhcp6optstr(opt), optlen);		/* option length field overrun */		if (np > ep) {			dprintf(LOG_INFO,			    "%s" "malformed DHCP options", FNAME);			return -1;		}		switch (opt) {		case DH6OPT_CLIENTID:			if (optlen == 0)				goto malformed;			duid0.duid_len = optlen;			duid0.duid_id = cp;			dprintf(LOG_DEBUG, "  DUID: %s", duidstr(&duid0));			if (duidcpy(&optinfo->clientID, &duid0)) {				dprintf(LOG_ERR, "%s" "failed to copy DUID",					FNAME);				goto fail;			}			break;		case DH6OPT_SERVERID:			if (optlen == 0)				goto malformed;			duid0.duid_len = optlen;			duid0.duid_id = cp;			dprintf(LOG_DEBUG, "  DUID: %s", duidstr(&duid0));			if (duidcpy(&optinfo->serverID, &duid0)) {				dprintf(LOG_ERR, "%s" "failed to copy DUID",					FNAME);				goto fail;			}			break;		case DH6OPT_STATUS_CODE:			if (optlen < sizeof(u_int16_t))				goto malformed;			memcpy(&val16, cp, sizeof(val16));			num = ntohs(val16);			dprintf(LOG_DEBUG, "  status code: %s",			    dhcp6_stcodestr(num));			/* need to check duplication? */			if (dhcp6_add_listval(&optinfo->stcode_list,			    &num, DHCP6_LISTVAL_NUM) == NULL) {				dprintf(LOG_ERR, "%s" "failed to copy "				    "status code", FNAME);				goto fail;			}			break;		case DH6OPT_ORO:			if ((optlen % 2) != 0 || optlen == 0)				goto malformed;			reqopts = optlen / 2;			for (i = 0, val = cp; i < reqopts;			     i++, val += sizeof(u_int16_t)) {				struct dhcp6_listval *opt;				u_int16_t opttype;				memcpy(&opttype, val, sizeof(u_int16_t));				num = ntohs(opttype);				dprintf(LOG_DEBUG, "  requested option: %s",					dhcp6optstr(num));				if (dhcp6_find_listval(&optinfo->reqopt_list,				    &num, DHCP6_LISTVAL_NUM)) {					dprintf(LOG_INFO, "%s" "duplicated "					    "option type (%s)", FNAME,					    dhcp6optstr(opttype));					goto nextoption;				}				if (dhcp6_add_listval(&optinfo->reqopt_list,				    &num, DHCP6_LISTVAL_NUM) == NULL) {					dprintf(LOG_ERR, "%s" "failed to copy "					    "requested option", FNAME);					goto fail;				}			  nextoption:			}			break;		case DH6OPT_PREFERENCE:			if (optlen != 1)				goto malformed;			optinfo->pref = (int)*(u_char *)cp;			break;		case DH6OPT_RAPID_COMMIT:			if (optlen != 0)				goto malformed;			optinfo->rapidcommit = 1;			break;		case DH6OPT_DNS:			if (optlen % sizeof(struct in6_addr) || optlen == 0)				goto malformed;			for (val = cp; val < cp + optlen;			     val += sizeof(struct in6_addr)) {				if (dhcp6_find_listval(&optinfo->dns_list,				    &num, DHCP6_LISTVAL_ADDR6)) {					dprintf(LOG_INFO, "%s" "duplicated "					    "DNS address (%s)", FNAME,					    in6addr2str((struct in6_addr *)val,						0));					goto nextdns;				}				if (dhcp6_add_listval(&optinfo->dns_list,				    val, DHCP6_LISTVAL_ADDR6) == NULL) {					dprintf(LOG_ERR, "%s" "failed to copy "					    "DNS address", FNAME);					goto fail;				}			  nextdns:			}			break;		case DH6OPT_PREFIX_DELEGATION:			if (get_delegated_prefixes(cp, cp + optlen, optinfo))				goto fail;			break;		default:			/* no option specific behavior */			dprintf(LOG_INFO, "%s"			    "unknown or unexpected DHCP6 option %s, len %d",			    FNAME, dhcp6optstr(opt), optlen);			break;		}	}	return(0);  malformed:	dprintf(LOG_INFO, "%s" "malformed DHCP option: type %d, len %d",	    FNAME, opt, optlen);  fail:	dhcp6_clear_options(optinfo);	return(-1);}static intget_delegated_prefixes(p, ep, optinfo)	char *p, *ep;	struct dhcp6_optinfo *optinfo;{	char *np, *cp;	struct dhcp6opt opth;	struct dhcp6_prefix_info pi;	struct dhcp6_prefix prefix;	struct dhcp6_listval *dp;	int optlen, opt;	for (; p + sizeof(struct dhcp6opt) <= ep; p = np) {		/* XXX: alignment issue */		memcpy(&opth, p, sizeof(opth));		optlen =  ntohs(opth.dh6opt_len);		opt = ntohs(opth.dh6opt_type);		cp = p + sizeof(opth);		np = cp + optlen;		dprintf(LOG_DEBUG, "  prefix delegation option: %s, "			"len %d", dhcp6optstr(opt), optlen);		if (np > ep) {			dprintf(LOG_INFO, "%s" "malformed DHCP options",			    FNAME);			return -1;		}		switch(opt) {		case DH6OPT_PREFIX_INFORMATION:			if (optlen != sizeof(pi) - 4)				goto malformed;			memcpy(&pi, p, sizeof(pi));			if (pi.dh6_pi_plen > 128) {				dprintf(LOG_INFO, "%s" "invalid prefix length "				    "(%d)", FNAME, pi.dh6_pi_plen);				goto malformed;			}			/* clear padding bits in the prefix address */			prefix6_mask(&pi.dh6_pi_paddr, pi.dh6_pi_plen);			/* copy the information into internal format */			memset(&prefix, 0, sizeof(prefix));			prefix.addr = pi.dh6_pi_paddr;			prefix.plen = pi.dh6_pi_plen;			prefix.duration = ntohl(pi.dh6_pi_duration);			if (prefix.duration != DHCP6_DURATITION_INFINITE) {				dprintf(LOG_DEBUG, "  prefix information: "				    "%s/%d duration %ld",				    in6addr2str(&prefix.addr, 0),				    prefix.plen, prefix.duration);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?