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 + -
显示快捷键?