📄 dhcp6c.c
字号:
if (duidcpy(&optinfo.serverID, &client6_iaidaddr.client6_info.serverid)) { dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME); goto end; } break; } /* client ID */ if (duidcpy(&optinfo.clientID, &client_duid)) { dprintf(LOG_ERR, "%s" "failed to copy client ID", FNAME); goto end; } /* option request options */ if (dhcp6_copy_list(&optinfo.reqopt_list, &ifp->reqopt_list)) { dprintf(LOG_ERR, "%s" "failed to copy requested options", FNAME); goto end; } switch(ev->state) { case DHCP6S_SOLICIT: /* rapid commit */ if (ifp->send_flags & DHCIFF_RAPID_COMMIT) optinfo.flags |= DHCIFF_RAPID_COMMIT; if (!(ifp->send_flags & DHCIFF_INFO_ONLY) || (client6_request_flag & CLIENT6_REQUEST_ADDR)) { memcpy(&optinfo.iaidinfo, &client6_iaidaddr.client6_info.iaidinfo, sizeof(optinfo.iaidinfo)); if (ifp->send_flags & DHCIFF_PREFIX_DELEGATION) optinfo.type = IAPD; else if (ifp->send_flags & DHCIFF_TEMP_ADDRS) optinfo.type = IATA; else optinfo.type = IANA; } /* support for client preferred ipv6 address */ if (client6_request_flag & CLIENT6_REQUEST_ADDR) { if (dhcp6_copy_list(&optinfo.addr_list, &request_list)) goto end; } break; case DHCP6S_REQUEST: if (!(ifp->send_flags & DHCIFF_INFO_ONLY)) { memcpy(&optinfo.iaidinfo, &client6_iaidaddr.client6_info.iaidinfo, sizeof(optinfo.iaidinfo)); dprintf(LOG_DEBUG, "%s IAID is %u", FNAME, optinfo.iaidinfo.iaid); if (ifp->send_flags & DHCIFF_TEMP_ADDRS) optinfo.type = IATA; else if (ifp->send_flags & DHCIFF_PREFIX_DELEGATION) optinfo.type = IAPD; else optinfo.type = IANA; } break; case DHCP6S_RENEW: case DHCP6S_REBIND: case DHCP6S_RELEASE: case DHCP6S_CONFIRM: case DHCP6S_DECLINE: memcpy(&optinfo.iaidinfo, &client6_iaidaddr.client6_info.iaidinfo, sizeof(optinfo.iaidinfo)); optinfo.type = client6_iaidaddr.client6_info.type; if (ev->state == DHCP6S_CONFIRM) { optinfo.iaidinfo.renewtime = 0; optinfo.iaidinfo.rebindtime = 0; } if (!TAILQ_EMPTY(&request_list)) { /* XXX: ToDo: seperate to prefix list and address list */ if (dhcp6_copy_list(&optinfo.addr_list, &request_list)) goto end; } else { if (ev->state == DHCP6S_RELEASE) { dprintf(LOG_INFO, "release empty address list"); exit(1); } /* XXX: allow the other emtpy list ?? */ } if (client6_request_flag & CLIENT6_RELEASE_ADDR) { if (dhcp6_update_iaidaddr(&optinfo, ADDR_REMOVE)) { dprintf(LOG_INFO, "client release failed"); exit(1); } if (client6_iaidaddr.client6_info.type == IAPD) radvd_parse(&client6_iaidaddr, ADDR_REMOVE); } break; default: break; } /* 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. */ switch(ev->state) { case DHCP6S_REQUEST: case DHCP6S_RENEW: case DHCP6S_DECLINE: case DHCP6S_RELEASE: if (ifp->current_server && !IN6_IS_ADDR_UNSPECIFIED(&ifp->current_server->server_addr)) { struct addrinfo hints, *res; int error; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; error = getaddrinfo(in6addr2str(&ifp->current_server->server_addr,0), DH6PORT_UPSTREAM, &hints, &res); if (error) { dprintf(LOG_ERR, "%s" "getaddrinfo: %s", FNAME, gai_strerror(error)); exit(1); } memcpy(&dst, res->ai_addr, res->ai_addrlen); break; } default: dst = *sa6_allagent; break; } dst.sin6_scope_id = ifp->linkid; dprintf(LOG_DEBUG, "send dst if %s addr is %s scope id is %d", ifp->ifname, addr2str((struct sockaddr *)&dst), ifp->linkid); if (sendto(ifp->outsock, buf, len, MSG_DONTROUTE, (struct sockaddr *)&dst, sizeof(dst)) == -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 voidclient6_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(pi->ipi6_ifindex)) == NULL) { dprintf(LOG_INFO, "%s" "unexpected interface (%d)", FNAME, (unsigned int)pi->ipi6_ifindex); return; } dprintf(LOG_DEBUG, "receive packet info ifname %s, addr is %s scope id is %d", ifp->ifname, in6addr2str(&pi->ipi6_addr, 0), pi->ipi6_ifindex); dh6 = (struct dhcp6 *)rbuf; dprintf(LOG_DEBUG, "%s" "receive %s from %s scope id %d %s", FNAME, dhcp6msgstr(dh6->dh6_msgtype), addr2str((struct sockaddr *)&from), ((struct sockaddr_in6 *)&from)->sin6_scope_id, 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);#ifdef TEST return;#endif } 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; } 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; struct dhcp6_event *ev; struct dhcp6_listval *lv; /* 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 server policy doesn't allow rapid commit if (ev->state != DHCP6S_SOLICIT || (ifp->send_flags & DHCIFF_RAPID_COMMIT)) { */ if (ev->state != DHCP6S_SOLICIT) { 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=%2x", 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 any error. */ for (lv = TAILQ_FIRST(&optinfo0->stcode_list); lv; lv = TAILQ_NEXT(lv, link)) { dprintf(LOG_INFO, "%s" "status code: %s", FNAME, dhcp6_stcodestr(lv->val_num)); if (lv->val_num != DH6OPT_STCODE_SUCCESS) { return (-1); } } /* 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; } newserver = allocate_newserver(ifp, optinfo0); if (newserver == NULL) return (-1); /* 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; dhcp6_set_timeoparam(ev); dhcp6_reset_timer(ev); client6_send(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); } /* if the client send preferred addresses reqeust in SOLICIT */ /* XXX: client might have some local policy to select the addresses */ if (!TAILQ_EMPTY(&optinfo0->addr_list)) dhcp6_copy_list(&request_list, &optinfo0->addr_list); 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 struct dhcp6_serverinfo *allocate_newserver(ifp, optinfo) struct dhcp6_if *ifp; struct dhcp6_optinfo *optinfo;{ struct dhcp6_serverinfo *newserver, **sp; /* keep the server */ if ((newserver = malloc(sizeof(*newserver))) == NULL) { dprintf(LOG_ERR, "%s" "memory allocation failed for server", FNAME); return (NULL); } memset(newserver, 0, sizeof(*newserver)); dhcp6_init_options(&newserver->optinfo); if (dhcp6_copy_options(&newserver->optinfo, optinfo)) { dprintf(LOG_ERR, "%s" "failed to copy options", FNAME); free(newserver); return (NULL); } dprintf(LOG_DEBUG, "%s" "new server DUID %s, len %d ", FNAME, duidstr(&newserver->optinfo.serverID), newserver->optinfo.serverID.duid_len); if (optinfo->pref != DH6OPT_PREF_UNDEF) newserver->pref = optinfo->pref; if (optinfo->flags & DHCIFF_UNICAST) memcpy(&newserver->server_addr, &optinfo->server_addr, sizeof(newserver->server_addr)); 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; return newserver;}voidfree_servers(ifp) struct dhcp6_if *ifp;{ struct dhcp6_serverinfo *sp, *sp_next; /* free all servers we've seen so far */ for (sp = ifp->servers; sp; sp = sp_next) { sp_next = sp->next; dprintf(LOG_DEBUG, "%s" "removing server (ID: %s)", FNAME, duidstr(&sp->optinfo.serverID)); dhcp6_clear_options(&sp->optinfo); free(sp); } ifp->servers = NULL; ifp->current_server = 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; int addr_status_code = DH6OPT_STCODE_UNSPECFAIL; struct dhcp6_serverinfo *newserver; int newstate = 0; /* find the corresponding event based on the received xid */ dprintf(LOG_DEBUG, "%s" "reply message XID is (%x)", FNAME, ntohl(dh6->dh6_xid) & DH6_XIDMASK); ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK); if (ev == NULL) { dprintf(LOG_INFO, "%s" "XID mismatch", FNAME); return -1; } if (!(DHCP6S_VALID_REPLY(ev->state)) && (ev->state != DHCP6S_SOLICIT || !(optinfo->flags & DHCIFF_RAPID_COMMIT))) { dprintf(LOG_INFO, "%s" "unexpected reply", FNAME); return -1; } dhcp6_clear_list(&request_list); /* 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; } dprintf(LOG_DEBUG, "%s" "serverID is %s len is %d", FNAME, duidstr(&optinfo->serverID), optinfo->serverID.duid_len);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -