📄 dhcp6s.c
字号:
struct iovec iov; char cmsgbuf[BUFSIZ]; struct cmsghdr *cm; struct in6_pktinfo *pi = NULL; struct dhcp6_if *ifp; struct dhcp6 *dh6; struct dhcp6_optinfo optinfo; memset(&iov, 0, sizeof(iov)); memset(&mhdr, 0, sizeof(mhdr)); iov.iov_base = rdatabuf; iov.iov_len = sizeof(rdatabuf); mhdr.msg_name = &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 -1; } fromlen = mhdr.msg_namelen; 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 -1; } dprintf(LOG_DEBUG, "received message packet info addr is %s, scope id (%d)", in6addr2str(&pi->ipi6_addr, 0), (unsigned int)pi->ipi6_ifindex); if ((ifp = find_ifconfbyid((unsigned int)pi->ipi6_ifindex)) == NULL) { dprintf(LOG_INFO, "%s" "unexpected interface (%d)", FNAME, (unsigned int)pi->ipi6_ifindex); return -1; } if (len < sizeof(*dh6)) { dprintf(LOG_INFO, "%s" "short packet", FNAME); return -1; } dh6 = (struct dhcp6 *)rdatabuf; dprintf(LOG_DEBUG, "%s" "received %s from %s", FNAME, dhcp6msgstr(dh6->dh6_msgtype), addr2str((struct sockaddr *)&from)); /* * parse and validate options in the request */ dhcp6_init_options(&optinfo); if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1), (struct dhcp6opt *)(rdatabuf + len), &optinfo) < 0) { dprintf(LOG_INFO, "%s" "failed to parse options", FNAME); return -1; } /* check host decl first */ host = dhcp6_allocate_host(ifp, globalgroup, &optinfo); /* ToDo: allocate subnet after relay agent done * now assume client is on the same link as server * if the subnet couldn't be found return status code NotOnLink to client */ subnet = dhcp6_allocate_link(ifp, globalgroup, NULL); if (!(DH6_VALID_MESSAGE(dh6->dh6_msgtype))) dprintf(LOG_INFO, "%s" "unknown or unsupported msgtype %s", FNAME, dhcp6msgstr(dh6->dh6_msgtype)); else server6_react_message(ifp, pi, dh6, &optinfo, (struct sockaddr *)&from, fromlen); dhcp6_clear_options(&optinfo); return 0;}static intserver6_react_message(ifp, pi, dh6, optinfo, from, fromlen) struct dhcp6_if *ifp; struct in6_pktinfo *pi; struct dhcp6 *dh6; struct dhcp6_optinfo *optinfo; struct sockaddr *from; int fromlen;{ struct dhcp6_optinfo roptinfo; int addr_flag = 0; int addr_request = 0; int resptype = DH6_REPLY; int num = DH6OPT_STCODE_SUCCESS; int sending_hint = 0; /* message validation according to Section 18.2 of dhcpv6-28 */ /* the message must include a Client Identifier option */ if (optinfo->clientID.duid_len == 0) { dprintf(LOG_INFO, "%s" "no server ID option", FNAME); return -1; } else { dprintf(LOG_DEBUG, "%s" "client ID %s", FNAME, duidstr(&optinfo->clientID)); } /* the message must include a Server Identifier option in below messages*/ switch (dh6->dh6_msgtype) { case DH6_REQUEST: case DH6_RENEW: case DH6_DECLINE: case DH6_RELEASE: if (optinfo->serverID.duid_len == 0) { dprintf(LOG_INFO, "%s" "no server ID option", FNAME); return -1; } /* the contents of the Server Identifier option must match ours */ if (duidcmp(&optinfo->serverID, &server_duid)) { dprintf(LOG_INFO, "server ID %s mismatch %s", duidstr(&optinfo->serverID), duidstr(&server_duid)); return -1; } break; default: break; } /* * configure necessary options based on the options in request. */ dhcp6_init_options(&roptinfo); /* server information option */ if (duidcpy(&roptinfo.serverID, &server_duid)) { dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME); goto fail; } /* copy client information back */ if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) { dprintf(LOG_ERR, "%s" "failed to copy client ID", FNAME); goto fail; } /* if the client is not on the link */ if (host == NULL && subnet == NULL) { num = DH6OPT_STCODE_NOTONLINK; /* Draft-28 18.2.2, drop the message if NotOnLink */ if (dh6->dh6_msgtype == DH6_CONFIRM || dh6->dh6_msgtype == DH6_REBIND) goto fail; else goto send; } if (subnet) { roptinfo.pref = subnet->linkscope.server_pref; roptinfo.flags = (optinfo->flags & subnet->linkscope.allow_flags) | subnet->linkscope.send_flags; dnslist = subnet->linkscope.dnslist; } if (host) { roptinfo.pref = host->hostscope.server_pref; roptinfo.flags = (optinfo->flags & host->hostscope.allow_flags) | host->hostscope.send_flags; dnslist = host->hostscope.dnslist; } /* prohibit a mixture of old and new style of DNS server config */ if (!TAILQ_EMPTY(&arg_dnslist.addrlist)) { if (!TAILQ_EMPTY(&dnslist.addrlist)) { dprintf(LOG_INFO, "%s" "do not specify DNS servers " "both by command line and by configuration file.", FNAME); exit(1); } dnslist = arg_dnslist; TAILQ_INIT(&arg_dnslist.addrlist); } dprintf(LOG_DEBUG, "server preference is %2x", roptinfo.pref); if (roptinfo.flags & DHCIFF_UNICAST) { /* todo find the right server unicast address to client*/ /* get_linklocal(device, &roptinfo.server_addr) */ memcpy(&roptinfo.server_addr, &ifp->linklocal, sizeof(roptinfo.server_addr)); dprintf(LOG_DEBUG, "%s" "server address is %s", FNAME, in6addr2str(&roptinfo.server_addr, 0)); } /* * When the server receives a Request message via unicast from a * client to which the server has not sent a unicast option, the server * discards the Request message and responds with a Reply message * containing a Status Code option with value UseMulticast, a Server * Identifier option containing the server's DUID, the Client * Identifier option from the client message and no other options. * [dhcpv6-26 18.2.1] */ switch (dh6->dh6_msgtype) { case DH6_REQUEST: case DH6_RENEW: case DH6_DECLINE: if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) { if (!(roptinfo.flags & DHCIFF_UNICAST)) { num = DH6OPT_STCODE_USEMULTICAST; goto send; } else break; } default: if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) { num = DH6OPT_STCODE_USEMULTICAST; goto send; } break; } switch (dh6->dh6_msgtype) { case DH6_SOLICIT: /* * If the client has included a Rapid Commit option and the * server has been configured to respond with committed address * assignments and other resources, responds to the Solicit * with a Reply message. * [dhcpv6-28 Section 17.2.1] * [dhcpv6-28 Section 17.2.2] * If Solicit has IA option, responds to Solicit with a Advertise * message. */ if (optinfo->iaidinfo.iaid != 0 && !(roptinfo.flags & DHCIFF_INFO_ONLY)) { memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo, sizeof(roptinfo.iaidinfo)); roptinfo.type = optinfo->type; dprintf(LOG_DEBUG, "option type is %d", roptinfo.type); addr_request = 1; if (roptinfo.flags & DHCIFF_RAPID_COMMIT) { resptype = DH6_REPLY; } else { resptype = DH6_ADVERTISE; /* giving hint ?? */ sending_hint = 1; } } break; case DH6_INFORM_REQ: /* don't response to info-req if there is any IA option */ if (optinfo->iaidinfo.iaid != 0) goto fail; /* DNS server */ if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) { dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME); goto fail; } roptinfo.dns_list.domainlist = dnslist.domainlist; break; case DH6_REQUEST: /* get iaid for that request client for that interface */ if (optinfo->iaidinfo.iaid != 0 && !(roptinfo.flags & DHCIFF_INFO_ONLY)) { memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo, sizeof(roptinfo.iaidinfo)); roptinfo.type = optinfo->type; addr_request = 1; } break; /* * Locates the client's binding and verifies that the information * from the client matches the information stored for that client. */ case DH6_RENEW: case DH6_REBIND: case DH6_DECLINE: case DH6_RELEASE: case DH6_CONFIRM: roptinfo.type = optinfo->type; /* XXX: how server knows the difference between rebind_confirm and rebind * for prefix delegation ?*/ if (dh6->dh6_msgtype == DH6_RENEW || dh6->dh6_msgtype == DH6_REBIND) addr_flag = ADDR_UPDATE; if (dh6->dh6_msgtype == DH6_RELEASE) addr_flag = ADDR_REMOVE; if (dh6->dh6_msgtype == DH6_CONFIRM) { /* DNS server */ addr_flag = ADDR_VALIDATE; if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) { dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME); goto fail; } roptinfo.dns_list.domainlist = dnslist.domainlist; } if (dh6->dh6_msgtype == DH6_DECLINE) addr_flag = ADDR_ABANDON; if (optinfo->iaidinfo.iaid != 0) { if (!TAILQ_EMPTY(&optinfo->addr_list) && resptype != DH6_ADVERTISE) { struct dhcp6_iaidaddr *iaidaddr; memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo, sizeof(roptinfo.iaidinfo)); roptinfo.type = optinfo->type; /* find bindings */ if ((iaidaddr = dhcp6_find_iaidaddr(&roptinfo)) == NULL) { if (dh6->dh6_msgtype == DH6_REBIND) goto fail; num = DH6OPT_STCODE_NOBINDING; dprintf(LOG_INFO, "%s" "Nobinding for client %s iaid %u", FNAME, duidstr(&optinfo->clientID), optinfo->iaidinfo.iaid); break; } if (addr_flag != ADDR_UPDATE) { dhcp6_copy_list(&roptinfo.addr_list, &optinfo->addr_list); } else { /* get static host configuration */ if (host) dhcp6_get_hostconf(&roptinfo, optinfo, iaidaddr, host); /* allow dynamic address assginment for the host too */ if (optinfo->type == IAPD) dhcp6_create_prefixlist(&roptinfo, optinfo, iaidaddr, subnet); else dhcp6_create_addrlist(&roptinfo, optinfo, iaidaddr, subnet); /* in case there is not bindings available */ if (TAILQ_EMPTY(&roptinfo.addr_list)) { num = DH6OPT_STCODE_NOBINDING; dprintf(LOG_INFO, "%s" "Bindings are not on link for client %s iaid %u", FNAME, duidstr(&optinfo->clientID), roptinfo.iaidinfo.iaid); break; } } if (addr_flag == ADDR_VALIDATE) { if (dhcp6_validate_bindings(&roptinfo, iaidaddr)) num = DH6OPT_STCODE_NOBINDING; break; } else { /* do update if this is not a confirm */ if (dhcp6_update_iaidaddr(&roptinfo, addr_flag) != 0) { dprintf(LOG_INFO, "%s" "bindings failed for client %s iaid %u", FNAME, duidstr(&optinfo->clientID), roptinfo.iaidinfo.iaid); num = DH6OPT_STCODE_UNSPECFAIL; break; } } num = DH6OPT_STCODE_SUCCESS; } else num = DH6OPT_STCODE_NOADDRAVAIL; } else dprintf(LOG_ERR, "invalid message type"); break; default: break; } /* * XXX: see if we have information for requested options, and if so, * configure corresponding options. */ /* * If the Request message contained an Option Request option, the * server MUST include options in the Reply message for any options in * the Option Request option the server is configured to return to the * client. * [dhcpv6-26 18.2.1] * Note: our current implementation always includes all information * that we can provide. So we do not have to check the option request * options. */ if (addr_request == 1) { int found_binding = 0; struct dhcp6_iaidaddr *iaidaddr; /* find bindings */ if ((iaidaddr = dhcp6_find_iaidaddr(&roptinfo)) != NULL) { found_binding = 1; addr_flag = ADDR_UPDATE; } if (host) dhcp6_get_hostconf(&roptinfo, optinfo, iaidaddr, host); /* valid and create addresses list */ if (optinfo->type == IAPD) dhcp6_create_prefixlist(&roptinfo, optinfo, iaidaddr, subnet); else dhcp6_create_addrlist(&roptinfo, optinfo, iaidaddr, subnet); if (TAILQ_EMPTY(&roptinfo.addr_list)) { num = DH6OPT_STCODE_NOADDRAVAIL; } else if (sending_hint == 0) { /* valid client request address list */ if (found_binding) { if (dhcp6_update_iaidaddr(&roptinfo, addr_flag) != 0) { dprintf(LOG_ERR, "assigned ipv6address for client iaid %u failed", roptinfo.iaidinfo.iaid); num = DH6OPT_STCODE_UNSPECFAIL; } else num = DH6OPT_STCODE_SUCCESS; } else { if (dhcp6_add_iaidaddr(&roptinfo) != 0) { dprintf(LOG_ERR, "assigned ipv6address for client iaid %u failed", roptinfo.iaidinfo.iaid); num = DH6OPT_STCODE_UNSPECFAIL; } else num = DH6OPT_STCODE_SUCCESS; } } /* DNS server */ if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) { dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME); goto fail; } roptinfo.dns_list.domainlist = dnslist.domainlist; } /* add address status code */ send: dprintf(LOG_DEBUG, " status code: %s", dhcp6_stcodestr(num)); if (dhcp6_add_listval(&roptinfo.stcode_list, &num, DHCP6_LISTVAL_NUM) == NULL) { dprintf(LOG_ERR, "%s" "failed to copy " "status code", FNAME); goto fail; } /* send a reply message. */ (void)server6_send(resptype, ifp, dh6, optinfo, from, fromlen, &roptinfo); dhcp6_clear_options(&roptinfo); return 0; fail: dhcp6_clear_options(&roptinfo); return -1;}static intserver6_send(type, ifp, origmsg, optinfo, from, fromlen, roptinfo) int type; struct dhcp6_if *ifp; struct dhcp6 *origmsg; struct dhcp6_optinfo *optinfo, *roptinfo; struct sockaddr *from; int fromlen;{ char replybuf[BUFSIZ]; struct sockaddr_in6 dst; int len, optlen; struct dhcp6 *dh6; if (sizeof(struct dhcp6) > sizeof(replybuf)) { dprintf(LOG_ERR, "%s" "buffer size assumption failed", FNAME); return (-1); } dh6 = (struct dhcp6 *)replybuf; len = sizeof(*dh6); memset(dh6, 0, sizeof(*dh6)); dh6->dh6_msgtypexid = origmsg->dh6_msgtypexid; dh6->dh6_msgtype = (u_int8_t)type; /* set options in the reply message */ if ((optlen = dhcp6_set_options((struct dhcp6opt *)(dh6 + 1), (struct dhcp6opt *)(replybuf + sizeof(replybuf)), roptinfo)) < 0) { dprintf(LOG_INFO, "%s" "failed to construct reply options", FNAME); return (-1); } len += optlen; /* specify the destination and send the reply */ dst = *sa6_any_downstream; dst.sin6_addr = ((struct sockaddr_in6 *)from)->sin6_addr; dst.sin6_scope_id = ((struct sockaddr_in6 *)from)->sin6_scope_id; dprintf(LOG_DEBUG, "send destination address is %s, scope id is %d", addr2str((struct sockaddr *)&dst), dst.sin6_scope_id); if (transmit_sa(outsock, &dst, replybuf, len) != 0) { dprintf(LOG_ERR, "%s" "transmit %s to %s failed", FNAME, dhcp6msgstr(type), addr2str((struct sockaddr *)&dst)); return (-1); } dprintf(LOG_DEBUG, "%s" "transmit %s to %s", FNAME, dhcp6msgstr(type), addr2str((struct sockaddr *)&dst)); return 0;}static struct dhcp6_timer*check_lease_file_timo(void *arg){ double d; struct timeval timo; struct stat buf; FILE *file; stat(PATH_SERVER6_LEASE, &buf); strcpy(server6_lease_temp, PATH_SERVER6_LEASE); strcat(server6_lease_temp, "XXXXXX"); if (buf.st_size > MAX_FILE_SIZE) { file = sync_leases(server6_lease_file, PATH_SERVER6_LEASE, server6_lease_temp); if (file != NULL) server6_lease_file = file; } d = DHCP6_SYNCFILE_TIME; timo.tv_sec = (long)d; timo.tv_usec = 0; dhcp6_set_timer(&timo, sync_lease_timer); return sync_lease_timer;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -