static int add_address(struct dhcp6_list *addr_list, struct dhcp6_addr *v6addr) { struct dhcp6_listval *lv, *val; /* avoid invalid addresses */ if (IN6_IS_ADDR_RESERVED(&v6addr->addr)) { dprintf(LOG_ERR, "%s" "invalid address: %s", FNAME, in6addr2str(&v6addr->addr, 0)); return (-1); } /* address duplication check */ for (lv = TAILQ_FIRST(addr_list); lv; lv = TAILQ_NEXT(lv, link)) { if (IN6_ARE_ADDR_EQUAL(&lv->val_dhcp6addr.addr, &v6addr->addr) && lv->val_dhcp6addr.plen == v6addr->plen) { dprintf(LOG_ERR, "%s" "duplicated address: %s/%d ", FNAME, in6addr2str(&v6addr->addr, 0), v6addr->plen); return (-1); } } if ((val = (struct dhcp6_listval *)malloc(sizeof(*val))) == NULL) dprintf(LOG_ERR, "%s" "memory allocation failed", FNAME); memset(val, 0, sizeof(*val)); memcpy(&val->val_dhcp6addr, v6addr, sizeof(val->val_dhcp6addr)); dprintf(LOG_DEBUG, "%s" "add address: %s", FNAME, in6addr2str(&v6addr->addr, 0)); TAILQ_INSERT_TAIL(addr_list, val, link); return (0); }
int configure_global_option(void) { struct cf_list *cl; /* DNS servers */ if (cf_dns_list && dhcp6_mode != DHCP6_MODE_SERVER) { dprintf(LOG_INFO, "%s" "%s:%d server-only configuration", FNAME, configfilename, cf_dns_list->line); goto bad; } TAILQ_INIT(&dnslist0); for (cl = cf_dns_list; cl; cl = cl->next) { /* duplication check */ if (dhcp6_find_listval(&dnslist0, cl->ptr, DHCP6_LISTVAL_ADDR6)) { dprintf(LOG_INFO, "%s" "%s:%d duplicated DNS server: %s", FNAME, configfilename, cl->line, in6addr2str((struct in6_addr *)cl->ptr, 0)); goto bad; } if (dhcp6_add_listval(&dnslist0, cl->ptr, DHCP6_LISTVAL_ADDR6) == NULL) { dprintf(LOG_ERR, "%s" "failed to add a DNS server", FNAME); goto bad; } } return 0; bad: return -1; }
int dad_parse(const char *file, struct dhcp6_list *dad_list) { int i = 0; int len = 0; int ret = 0; FILE *fp = NULL; char buf[55]; /* max line length in /proc/net/if_inet6 */ char addrbuf[64]; char *tmp = NULL; struct in6_addr addr6; struct ifproc_info *ifinfo = NULL; if (file == NULL) { dhcpv6_dprintf(LOG_ERR, "dad_parse: NULL filename"); return -1; } memset(&buf, '\0', sizeof(buf)); memset(&addr6, 0, sizeof(addr6)); if ((fp = fopen(file, "r")) == NULL) { if (errno == ENOENT) { return 0; } dhcpv6_dprintf(LOG_ERR, "dad_parse: fopen(%s): %s", file, strerror(errno)); return -1; } while (fgets(buf, sizeof(buf), fp) != NULL) { /* read address */ if ((tmp = strtok(buf, " \n")) == NULL) { continue; } len = 0; for (i = 0; i < 32; i += 4) { strncpy(addrbuf + len, &tmp[i], 4); len += 4; if (i < 28) { strcpy(addrbuf + len, ":"); len += 1; } else { strcpy(addrbuf + len, "\0"); } } if (inet_pton(AF_INET6, addrbuf, &addr6) < 1) { dhcpv6_dprintf(LOG_ERR, "failed to parse %s from %s", addrbuf, file); abort(); } if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) { dhcpv6_dprintf(LOG_ERR, "memory allocation failure"); abort(); } memcpy(&ifinfo->addr, &addr6, sizeof(ifinfo->addr)); /* read the index */ if ((tmp = strtok(NULL, " \n")) == NULL) { continue; } ifinfo->index = strtol(tmp, NULL, 16); if ((errno == EINVAL) || (errno == ERANGE)) { dhcpv6_dprintf(LOG_ERR, "error reading index from %s", file); goto fail; } /* read the prefix length */ if ((tmp = strtok(NULL, " \n")) == NULL) { continue; } ifinfo->plen = strtol(tmp, NULL, 16); if ((errno == EINVAL) || (errno == ERANGE)) { dhcpv6_dprintf(LOG_ERR, "error reading prefix length from %s", file); goto fail; } /* read the scope */ if ((tmp = strtok(NULL, " \n")) == NULL) { continue; } ifinfo->scope = strtol(tmp, NULL, 16); if ((errno == EINVAL) || (errno == ERANGE)) { dhcpv6_dprintf(LOG_ERR, "error reading scope from %s", file); goto fail; } /* read the flags */ if ((tmp = strtok(NULL, " \n")) == NULL) { continue; } ifinfo->flags = strtol(tmp, NULL, 16); if ((errno == EINVAL) || (errno == ERANGE)) { dhcpv6_dprintf(LOG_ERR, "error reading flags from %s", file); goto fail; } if (ifinfo->flags == DAD_FLAGS) { dhcpv6_dprintf(LOG_INFO, "duplicated IPv6 address %s detected", in6addr2str(&ifinfo->addr, 0)); } else { free(ifinfo); ifinfo = NULL; continue; } /* read the interface name */ if ((tmp = strtok(NULL, " \n")) == NULL) { continue; } if (strcmp(tmp, dhcp6_if->ifname)) { free(ifinfo); ifinfo = NULL; continue; } else { struct dhcp6_listval *lv; strncpy(ifinfo->name, tmp, IF_NAMESIZE); ifinfo->next = NULL; /* check address on client6_iaidaddr list */ if ((lv = malloc(sizeof(*lv))) == NULL) { dhcpv6_dprintf(LOG_ERR, "memory allocation failure"); abort(); } memcpy(&lv->val_dhcp6addr.addr, &ifinfo->addr, sizeof(lv->val_dhcp6addr.addr)); lv->val_dhcp6addr.type = IANA; lv->val_dhcp6addr.plen = ifinfo->plen; lv->val_dhcp6addr.status_code = DH6OPT_STCODE_UNDEFINE; lv->val_dhcp6addr.preferlifetime = 0; lv->val_dhcp6addr.validlifetime = 0; TAILQ_INSERT_TAIL(dad_list, lv, link); } } out: if (fclose(fp) == EOF) { fprintf(stderr, "%s (%d): %s\n", __func__, __LINE__, strerror(errno)); fflush(stderr); abort(); } return ret; fail: dhcp6_clear_list(dad_list); ret = -1; goto out; }
int client6_script( char *scriptpath, int state, struct dhcp6_optinfo *optinfo ) { int i, dnsservers, ntpservers, dnsnamelen, envc, elen, ret = 0; int sipservers, sipnamelen; int nisservers, nisnamelen; int nispservers, nispnamelen; int bcmcsservers, bcmcsnamelen; char **envp, *s; char reason[] = "REASON=NBI"; struct dhcp6_listval *v; pid_t pid, wpid; /* if a script is not specified, do nothing */ if (scriptpath == NULL || strlen(scriptpath) == 0) return -1; /* initialize counters */ dnsservers = 0; ntpservers = 0; dnsnamelen = 0; sipservers = 0; sipnamelen = 0; nisservers = 0; nisnamelen = 0; nispservers = 0; nispnamelen = 0; bcmcsservers = 0; bcmcsnamelen = 0; envc = 2; /* we at least include the reason and the terminator */ /* count the number of variables */ for (v = TAILQ_FIRST(&optinfo->dns_list); v; v = TAILQ_NEXT(v, link)) dnsservers++; envc += dnsservers ? 1 : 0; for (v = TAILQ_FIRST(&optinfo->dnsname_list); v; v = TAILQ_NEXT(v, link)) { dnsnamelen += v->val_vbuf.dv_len; } envc += dnsnamelen ? 1 : 0; for (v = TAILQ_FIRST(&optinfo->ntp_list); v; v = TAILQ_NEXT(v, link)) ntpservers++; envc += ntpservers ? 1 : 0; for (v = TAILQ_FIRST(&optinfo->sip_list); v; v = TAILQ_NEXT(v, link)) sipservers++; envc += sipservers ? 1 : 0; for (v = TAILQ_FIRST(&optinfo->sipname_list); v; v = TAILQ_NEXT(v, link)) { sipnamelen += v->val_vbuf.dv_len; } envc += sipnamelen ? 1 : 0; for (v = TAILQ_FIRST(&optinfo->nis_list); v; v = TAILQ_NEXT(v, link)) nisservers++; envc += nisservers ? 1 : 0; for (v = TAILQ_FIRST(&optinfo->nisname_list); v; v = TAILQ_NEXT(v, link)) { nisnamelen += v->val_vbuf.dv_len; } envc += nisnamelen ? 1 : 0; for (v = TAILQ_FIRST(&optinfo->nisp_list); v; v = TAILQ_NEXT(v, link)) nispservers++; envc += nispservers ? 1 : 0; for (v = TAILQ_FIRST(&optinfo->nispname_list); v; v = TAILQ_NEXT(v, link)) { nispnamelen += v->val_vbuf.dv_len; } envc += nispnamelen ? 1 : 0; for (v = TAILQ_FIRST(&optinfo->bcmcs_list); v; v = TAILQ_NEXT(v, link)) bcmcsservers++; envc += bcmcsservers ? 1 : 0; for (v = TAILQ_FIRST(&optinfo->bcmcsname_list); v; v = TAILQ_NEXT(v, link)) { bcmcsnamelen += v->val_vbuf.dv_len; } envc += bcmcsnamelen ? 1 : 0; /* allocate an environments array */ if ((envp = malloc(sizeof (char *) * envc)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate environment buffer"); return -1; } memset(envp, 0, sizeof (char *) * envc); /* * Copy the parameters as environment variables */ i = 0; /* reason */ if ((envp[i++] = strdup(reason)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate reason strings"); ret = -1; goto clean; } /* "var=addr1 addr2 ... addrN" + null char for termination */ if (dnsservers) { elen = sizeof (dnsserver_str) + (INET6_ADDRSTRLEN + 1) * dnsservers + 1; if ((s = envp[i++] = malloc(elen)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate strings for DNS servers"); ret = -1; goto clean; } memset(s, 0, elen); snprintf(s, elen, "%s=", dnsserver_str); for (v = TAILQ_FIRST(&optinfo->dns_list); v; v = TAILQ_NEXT(v, link)) { char *addr; addr = in6addr2str(&v->val_addr6, 0); strlcat(s, addr, elen); strlcat(s, " ", elen); } } if (ntpservers) { elen = sizeof (ntpserver_str) + (INET6_ADDRSTRLEN + 1) * ntpservers + 1; if ((s = envp[i++] = malloc(elen)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate strings for NTP servers"); ret = -1; goto clean; } memset(s, 0, elen); snprintf(s, elen, "%s=", ntpserver_str); for (v = TAILQ_FIRST(&optinfo->ntp_list); v; v = TAILQ_NEXT(v, link)) { char *addr; addr = in6addr2str(&v->val_addr6, 0); strlcat(s, addr, elen); strlcat(s, " ", elen); } } if (dnsnamelen) { elen = sizeof (dnsname_str) + dnsnamelen + 1; if ((s = envp[i++] = malloc(elen)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate strings for DNS name"); ret = -1; goto clean; } memset(s, 0, elen); snprintf(s, elen, "%s=", dnsname_str); for (v = TAILQ_FIRST(&optinfo->dnsname_list); v; v = TAILQ_NEXT(v, link)) { strlcat(s, v->val_vbuf.dv_buf, elen); strlcat(s, " ", elen); } } if (sipservers) { elen = sizeof (sipserver_str) + (INET6_ADDRSTRLEN + 1) * sipservers + 1; if ((s = envp[i++] = malloc(elen)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate strings for SIP servers"); ret = -1; goto clean; } memset(s, 0, elen); snprintf(s, elen, "%s=", sipserver_str); for (v = TAILQ_FIRST(&optinfo->sip_list); v; v = TAILQ_NEXT(v, link)) { char *addr; addr = in6addr2str(&v->val_addr6, 0); strlcat(s, addr, elen); strlcat(s, " ", elen); } } if (sipnamelen) { elen = sizeof (sipname_str) + sipnamelen + 1; if ((s = envp[i++] = malloc(elen)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate strings for SIP domain name"); ret = -1; goto clean; } memset(s, 0, elen); snprintf(s, elen, "%s=", sipname_str); for (v = TAILQ_FIRST(&optinfo->sipname_list); v; v = TAILQ_NEXT(v, link)) { strlcat(s, v->val_vbuf.dv_buf, elen); strlcat(s, " ", elen); } } if (nisservers) { elen = sizeof (nisserver_str) + (INET6_ADDRSTRLEN + 1) * nisservers + 1; if ((s = envp[i++] = malloc(elen)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate strings for NIS servers"); ret = -1; goto clean; } memset(s, 0, elen); snprintf(s, elen, "%s=", nisserver_str); for (v = TAILQ_FIRST(&optinfo->nis_list); v; v = TAILQ_NEXT(v, link)) { char *addr; addr = in6addr2str(&v->val_addr6, 0); strlcat(s, addr, elen); strlcat(s, " ", elen); } } if (nisnamelen) { elen = sizeof (nisname_str) + nisnamelen + 1; if ((s = envp[i++] = malloc(elen)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate strings for NIS domain name"); ret = -1; goto clean; } memset(s, 0, elen); snprintf(s, elen, "%s=", nisname_str); for (v = TAILQ_FIRST(&optinfo->nisname_list); v; v = TAILQ_NEXT(v, link)) { strlcat(s, v->val_vbuf.dv_buf, elen); strlcat(s, " ", elen); } } if (nispservers) { elen = sizeof (nispserver_str) + (INET6_ADDRSTRLEN + 1) * nispservers + 1; if ((s = envp[i++] = malloc(elen)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate strings for NIS+ servers"); ret = -1; goto clean; } memset(s, 0, elen); snprintf(s, elen, "%s=", nispserver_str); for (v = TAILQ_FIRST(&optinfo->nisp_list); v; v = TAILQ_NEXT(v, link)) { char *addr; addr = in6addr2str(&v->val_addr6, 0); strlcat(s, addr, elen); strlcat(s, " ", elen); } } if (nispnamelen) { elen = sizeof (nispname_str) + nispnamelen + 1; if ((s = envp[i++] = malloc(elen)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate strings for NIS+ domain name"); ret = -1; goto clean; } memset(s, 0, elen); snprintf(s, elen, "%s=", nispname_str); for (v = TAILQ_FIRST(&optinfo->nispname_list); v; v = TAILQ_NEXT(v, link)) { strlcat(s, v->val_vbuf.dv_buf, elen); strlcat(s, " ", elen); } } if (bcmcsservers) { elen = sizeof (bcmcsserver_str) + (INET6_ADDRSTRLEN + 1) * bcmcsservers + 1; if ((s = envp[i++] = malloc(elen)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate strings for BCMC servers"); ret = -1; goto clean; } memset(s, 0, elen); snprintf(s, elen, "%s=", bcmcsserver_str); for (v = TAILQ_FIRST(&optinfo->bcmcs_list); v; v = TAILQ_NEXT(v, link)) { char *addr; addr = in6addr2str(&v->val_addr6, 0); strlcat(s, addr, elen); strlcat(s, " ", elen); } } if (bcmcsnamelen) { elen = sizeof (bcmcsname_str) + bcmcsnamelen + 1; if ((s = envp[i++] = malloc(elen)) == NULL) { dprintf(LOG_NOTICE, FNAME, "failed to allocate strings for BCMC domain name"); ret = -1; goto clean; } memset(s, 0, elen); snprintf(s, elen, "%s=", bcmcsname_str); for (v = TAILQ_FIRST(&optinfo->bcmcsname_list); v; v = TAILQ_NEXT(v, link)) { strlcat(s, v->val_vbuf.dv_buf, elen); strlcat(s, " ", elen); } } /* launch the script */ pid = fork(); if (pid < 0) { dprintf(LOG_ERR, FNAME, "failed to fork: %s", strerror(errno)); ret = -1; goto clean; } else if (pid) { int wstatus; do { wpid = wait(&wstatus); } while (wpid != pid && wpid > 0); if (wpid < 0) dprintf(LOG_ERR, FNAME, "wait: %s", strerror(errno)); else { dprintf(LOG_DEBUG, FNAME, "script \"%s\" terminated", scriptpath); } } else { char *argv[2]; int fd; argv[0] = scriptpath; argv[1] = NULL; if (safefile(scriptpath)) { dprintf(LOG_ERR, FNAME, "script \"%s\" cannot be executed safely", scriptpath); exit(1); } if (foreground == 0 && (fd = open("/dev/null", O_RDWR)) != -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); if (fd > STDERR_FILENO) close(fd); } execve(scriptpath, argv, envp); dprintf(LOG_ERR, FNAME, "child: exec failed: %s", strerror(errno)); exit(0); } clean: for (i = 0; i < envc; i++) free(envp[i]); free(envp); return ret; }
//void //update_ia(iatype, ialist, ifp, serverid, authparam) void update_ia( iatype_t iatype, struct dhcp6_list *ialist, struct dhcp6_if *ifp, struct duid *serverid, struct authparam *authparam) { struct ia *ia; struct ia_conf *iac; struct iapd_conf *iapdc; struct iana_conf *ianac; struct dhcp6_listval *iav, *siav; struct timeval timo; /* iav指向的是一个具体的节点,该节点的内容则是在iav->val_ia中给出 */ for (iav = TAILQ_FIRST(ialist); iav; iav = TAILQ_NEXT(iav, link)) { /* if we're not interested in this IA, ignore it. */ /* 根据配置文件中的配置来判断是否这个iac存在 */ if ((iac = find_iaconf(&ifp->iaconf_list, iatype, iav->val_ia.iaid)) == NULL) { continue; } /* validate parameters */ /* * If a client receives an IA_NA with T1 greater than T2, and * both T1 and T2 are greater than 0, the client discards the * IA_NA option and processes the remainder of the message as * though the server had not included the invalid IA_NA option. * [RFC3315 22.4] * We apply the same rule to IA_PD as well. */ if (iav->val_ia.t2 != 0 && iav->val_ia.t1 > iav->val_ia.t2) { dprintf(LOG_INFO, FNAME, "invalid IA: T1(%lu) > T2(%lu)", iav->val_ia.t1, iav->val_ia.t2); continue; } /* locate the local IA or make a new one */ ia = get_ia(iatype, ifp, iac, iav, serverid); if (ia == NULL) { dprintf(LOG_WARNING, FNAME, "failed to get an IA " "type: %s, ID: %u", iastr(iac->type), iac->iaid); continue; } /* update authentication parameters */ if (update_authparam(ia, authparam)) { dprintf(LOG_WARNING, FNAME, "failed to update " "authentication param for IA " "type: %s, ID: %u", iastr(iac->type), iac->iaid); remove_ia(ia); continue; } /* update IA configuration information */ for (siav = TAILQ_FIRST(&iav->sublist); siav; siav = TAILQ_NEXT(siav, link)) { switch (siav->type) { case DHCP6_LISTVAL_PREFIX6: /* add or update the prefix */ iapdc = (struct iapd_conf *)iac; if (update_prefix(ia, &siav->val_prefix6, &iapdc->iapd_pif_list, ifp, &ia->ctl, callback)) { dprintf(LOG_NOTICE, FNAME, "failed to update a prefix %s/%d", in6addr2str(&siav->val_prefix6.addr, 0), siav->val_prefix6.plen); } break; case DHCP6_LISTVAL_STATEFULADDR6: ianac = (struct iana_conf *)iac; if (update_address(ia, &siav->val_statefuladdr6, ifp, &ia->ctl, callback)) { dprintf(LOG_NOTICE, FNAME, "failed to update an address %s", in6addr2str(&siav->val_statefuladdr6.addr, 0)); } break; case DHCP6_LISTVAL_STCODE: dprintf(LOG_INFO, FNAME, "status code for %s-%lu: %s", iastr(iatype), iav->val_ia.iaid, dhcp6_stcodestr(siav->val_num16)); if ((ia->state == IAS_RENEW || ia->state == IAS_REBIND) && siav->val_num16 == DH6OPT_STCODE_NOBINDING) { /* * For each IA in the original Renew or * Rebind message, the client * sends a Request message if the IA * contained a Status Code option * with the NoBinding status. * [RFC3315 18.1.8] * XXX: what about the PD case? */ dprintf(LOG_INFO, FNAME, "receive NoBinding against " "renew/rebind for %s-%lu", iastr(ia->conf->type), ia->conf->iaid); reestablish_ia(ia); goto nextia; } break; default: dprintf(LOG_ERR, FNAME, "impossible case"); goto nextia; } } /* see if this IA is still valid. if not, remove it. */ if (ia->ctl == NULL || !(*ia->ctl->isvalid)(ia->ctl)) { dprintf(LOG_DEBUG, FNAME, "IA %s-%lu is invalidated", iastr(ia->conf->type), ia->conf->iaid); remove_ia(ia); continue; } /* if T1 or T2 is 0, determine appropriate values locally. */ if (ia->t1 == 0 || ia->t2 == 0) { u_int32_t duration; if (ia->ctl && ia->ctl->duration) duration = (*ia->ctl->duration)(ia->ctl); else duration = 1800; /* 30min. XXX: no rationale */ if (ia->t1 == 0) { if (duration == DHCP6_DURATION_INFINITE) ia->t1 = DHCP6_DURATION_INFINITE; else ia->t1 = duration / 2; } if (ia->t2 == 0) { if (duration == DHCP6_DURATION_INFINITE) ia->t2 = DHCP6_DURATION_INFINITE; else ia->t2 = duration * 4 / 5; } /* make sure T1 <= T2 */ if (ia->t1 > ia->t2) ia->t1 = ia->t2 * 5 / 8; dprintf(LOG_INFO, FNAME, "T1(%lu) and/or T2(%lu) " "is locally determined", ia->t1, ia->t2); } /* * Be proactive for too-small timeout values. Note that * the adjusted values may make some information expire * without renewal. */ if (ia->t2 < DHCP6_DURATION_MIN) { dprintf(LOG_INFO, FNAME, "T1 (%lu) or T2 (%lu) " "is too small", ia->t1, ia->t2); ia->t2 = DHCP6_DURATION_MIN; ia->t1 = ia->t2 * 5 / 8; dprintf(LOG_INFO, "", " adjusted to %lu and %lu", ia->t1, ia->t2); } /* set up a timer for this IA. */ if (ia->t1 == DHCP6_DURATION_INFINITE) { if (ia->timer) dhcp6_remove_timer(&ia->timer); } else { if (ia->timer == NULL) ia->timer = dhcp6_add_timer(ia_timo, ia); if (ia->timer == NULL) { dprintf(LOG_ERR, FNAME, "failed to add IA timer"); remove_ia(ia); /* XXX */ continue; } timo.tv_sec = ia->t1; timo.tv_usec = 0; loginfo(FUNC, "set renew time: %d second", timo.tv_sec); dhcp6_set_timer(&timo, ia->timer); } /* 更新这个ia的状态为ACTIVE,接下来就可以使用renew来更新了 */ ia->state = IAS_ACTIVE; nextia: ; } }