📄 dhcp6relay.c
字号:
} ssock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (ssock < 0) { err(1, "socket(outsock)"); /* NOTREACHED */ } if (ssock > maxfd) maxfd = ssock; if (setsockopt(ssock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) { err(1, "setsockopt(ssock, SO_REUSEPORT)"); /* NOTREACHED */ } if (setsockopt(ssock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { err(1, "setsockopt(ssock, IPV6_V6ONLY)"); /* NOTREACHED */ } if (bind(ssock, res->ai_addr, res->ai_addrlen) < 0) { err(1, "bind(ssock)"); /* NOTREACHED */ } memcpy(&sa6_client, res->ai_addr, sizeof(sa6_client)); freeaddrinfo(res); if (setsockopt(ssock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mhops, sizeof(mhops)) < 0) { err(1, "setsockopt(ssock, IPV6_MULTICAST_HOPS(%d))", mhops); /* NOTREACHED */ }#ifdef IPV6_RECVPKTINFO if (setsockopt(ssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) { err(1, "setsockopt(IPV6_RECVPKTINFO)"); /* NOTREACHED */ }#else if (setsockopt(ssock, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)) < 0) { err(1, "setsockopt(IPV6_PKTINFO)"); /* NOTREACHED */ }#endif /* * Setup a socket to receive ICMPv6 errors. */ if ((icmp6sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { err(1, "socket(ICMPV6)"); /* NOTREACHED */ } if (icmp6sock > maxfd) maxfd = icmp6sock; /* set filter to receive error messages only */ ICMP6_FILTER_SETBLOCKALL(&filt); for (type = 0; type < 128; type++) ICMP6_FILTER_SETPASS(type, &filt); if (setsockopt(icmp6sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt)) < 0) err(1, "setsockopt(ICMP6_FILTER)");}static voidrelay6_loop(){ fd_set readfds; char rdev[IF_NAMESIZE]; int e, cc; while(1) { /* we'd rather use FD_COPY here, but it's not POSIX friendly */ FD_ZERO(&readfds); FD_SET(csock, &readfds); FD_SET(ssock, &readfds); FD_SET(icmp6sock, &readfds); e = select(maxfd + 1, &readfds, NULL, NULL, NULL); switch(e) { case 0: /* impossible in our situation */ errx(1, "select returned 0"); /* NOTREACHED */ case -1: err(1, "select"); /* NOTREACHED */ default: break; } if (FD_ISSET(csock, &readfds) && (cc = relay6_recv(csock, rdev)) > 0) relay6_react(cc, rdatabuf, rdev, 1); if (FD_ISSET(ssock, &readfds) && (cc = relay6_recv(ssock, rdev)) > 0) relay6_react(cc, rdatabuf, rdev, 0); if (FD_ISSET(icmp6sock, &readfds) && (cc = relay6_recv(icmp6sock, NULL)) > 0) {#ifdef notyet icmp6_react();#endif } }}static intrelay6_recv(s, rdevice) int s; char *rdevice;{ ssize_t len; struct sockaddr_storage from; struct in6_pktinfo *pi = NULL; struct cmsghdr *cm; rmh.msg_control = (caddr_t)rmsgctlbuf; rmh.msg_controllen = rmsgctllen; rmh.msg_name = (caddr_t)&from; rmh.msg_namelen = sizeof(from); if ((len = recvmsg(s, &rmh, 0)) < 0) { dprintf(LOG_WARNING, "recvmsg: %s", strerror(errno)); return (-1); /* should assert? */ } dprintf(LOG_DEBUG, "relay6_recv: from %s, size %d", addr2str((struct sockaddr *)&from), len); /* get optional information as ancillary data (if available) */ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rmh); cm; cm = (struct cmsghdr *)CMSG_NXTHDR(&rmh, cm)) { if (cm->cmsg_level != IPPROTO_IPV6) continue; switch(cm->cmsg_type) { case IPV6_PKTINFO: pi = (struct in6_pktinfo *)CMSG_DATA(cm); break; } } if (rdevice) { if (pi == NULL) { dprintf(LOG_WARNING, "relay6_recv: " "failed to get the arrival interface"); return (-1); } if (if_indextoname(pi->ipi6_ifindex, rdevice) == NULL) { dprintf(LOG_WARNING, "if_indextoname(id = %d): %s", pi->ipi6_ifindex, strerror(errno)); return (-1); } } return (len);}static voidrelay6_react(siz, buf, dev, fromclient) size_t siz; int fromclient; char *buf, *dev;{ union dhcp6 *dh6; if (siz < 1) { /* we need at least 1 byte to check type */ dprintf(LOG_DEBUG, "relay6_react: short packet"); return; } dh6 = (union dhcp6 *)buf; dprintf(LOG_DEBUG, "relay6_react: msgtype=%d", dh6->dh6_msgtype); if (fromclient) { switch (dh6->dh6_msgtype) { case DH6_SOLICIT: relay6_react_solicit(buf, siz, dev); break; case DH6_REQUEST:#ifdef notyet relay6_react_request(buf, siz, dev);#endif break; default: fprintf(stderr, "invalid msgtype (%d) from client", dh6->dh6_msgtype); break; } } else { switch (dh6->dh6_msgtype) { case DH6_ADVERT: relay6_react_advert(buf, siz, dev); break; case DH6_REPLY:#ifdef notyet relay6_react_reply(buf, siz, dev);#endif break; default: fprintf(stderr, "invalid msgtype (%d) from server", dh6->dh6_msgtype); break; } }} static voidrelay6_react_solicit(buf, siz, dev) char *buf, *dev; size_t siz;{ struct dhcp6_solicit *dh6s; struct prefix_list *p; static struct iovec iov[2]; struct in6_addr myaddr; int plen; u_char *cp_plen; dprintf(LOG_DEBUG, "relay6_react_solicit"); if (siz < sizeof(*dh6s)) { dprintf(LOG_INFO, "relay6_react_solicit: short packet (size = %d)", siz); return; } dh6s = (struct dhcp6_solicit *)buf; if (!IN6_IS_ADDR_LINKLOCAL(&dh6s->dh6sol_cliaddr)) { dprintf(LOG_INFO, "relay6_react_solicit: client address (%s) is not " "link-local", in6addr2str(&dh6s->dh6sol_cliaddr, 0)); return; } /* * When sending a DHCP Solicit message, a client MUST set the Relay * Address field to 16 octets of zeros, and zero the prefix-size * field. * But we just warn if the client did not conformed to these rules. */ if (!IN6_IS_ADDR_UNSPECIFIED(&dh6s->dh6sol_relayaddr)) { dprintf(LOG_INFO, "relay6_react_solicit: relay address (%s) is not " "the Unspecified address (ignored)", in6addr2str(&dh6s->dh6sol_relayaddr, 0)); } /* find a non-link-local address and fill in the relay address field */ memset(&myaddr, 0, sizeof(myaddr)); for (p = TAILQ_FIRST(&global_prefixes); p; p = TAILQ_NEXT(p, plink)) { if (getifaddr(&myaddr, dev, &p->paddr.sin6_addr, p->plen, 0, IN6_IFF_INVALID) == 0) /* found */ break; } if (IN6_IS_ADDR_UNSPECIFIED(&myaddr)) { dprintf(LOG_WARNING, "relay6_react_solicit: can't find a non-link-local " "address on %s", dev); return; } dh6s->dh6sol_relayaddr = myaddr; /* * Also places the number of bits of that make up the subnet prefix * for this address in the ``prefix-len'' field of the Solicit. * XXX: we blindly assume the length is 64 for now... */ cp_plen = (u_char *)&dh6s->dh6sol_plen_id; plen = 64; /* XXX */ *cp_plen &= 0x01; /* clear prefix-len for safety */ *cp_plen |= ((plen & 0xff) << 1); /* set the source address and the outgoing interface */ memset(spktinfo, 0, sizeof(*spktinfo)); spktinfo->ipi6_addr = myaddr; if ((spktinfo->ipi6_ifindex = if_nametoindex(dev)) == 0) { /* this must not occur, so we might have to assert here */ dprintf(LOG_WARNING, "relay6_react_solicit: invlalid interface: %s", dev); return; } /* forward the solictation to servers */ smh.msg_name = (caddr_t)&sa6_all_servers; smh.msg_namelen = sizeof(sa6_all_servers); iov[0].iov_base = buf; iov[0].iov_len = siz; smh.msg_iov = iov; smh.msg_iovlen = 1; if (sendmsg(ssock, &smh, 0) != siz) dprintf(LOG_WARNING, "relay6_react_solicit: sendmsg failed " "(ifid = %d, src = %s): %s", spktinfo->ipi6_ifindex, in6addr2str(&spktinfo->ipi6_addr, 0), strerror(errno));}static voidrelay6_react_advert(buf, siz, dev) char *buf, *dev; size_t siz;{ struct dhcp6_advert *dh6a; struct sockaddr_in6 sa6_relay; const char *sdev; dprintf(LOG_DEBUG, "relay6_react_advert"); if (siz < sizeof(*dh6a)) { dprintf(LOG_INFO, "relay6_react_advert: short packet (size = %d)", siz); return; } dh6a = (struct dhcp6_advert *)buf; memset(&sa6_relay, 0, sizeof(sa6_relay)); sa6_relay.sin6_family = AF_INET6; sa6_relay.sin6_len = sizeof(sa6_relay); sa6_relay.sin6_addr = dh6a->dh6adv_relayaddr; sa6_relay.sin6_scope_id = in6_addrscopebyif(&dh6a->dh6adv_relayaddr, dev); if (!IN6_IS_ADDR_LINKLOCAL(&dh6a->dh6adv_cliaddr)) { dprintf(LOG_INFO, "relay6_react_advert: client address (%s) is not " "link-local", in6addr2str(&dh6a->dh6adv_cliaddr, 0)); return; } /* * Note that sdev is necessarily equal to dev, if we take the weak * host model. */ if ((sdev = getdev(&sa6_relay)) == NULL) { dprintf(LOG_WARNING, "relay6_react_advert: can't detect interface from %s", addr2str((struct sockaddr *)&sa6_relay)); return; } relay6_forward_response(buf, siz, &dh6a->dh6adv_cliaddr, sdev);}static voidrelay6_forward_response(buf, siz, cliaddr, dev) char *buf; size_t siz; struct in6_addr *cliaddr; const char *dev;{ static struct iovec iov[2]; dprintf(LOG_DEBUG, "relay6_forward_response"); sa6_client.sin6_addr = *cliaddr; smh.msg_name = (caddr_t)&sa6_client; smh.msg_namelen = sizeof(sa6_client); iov[0].iov_base = buf; iov[0].iov_len = siz; smh.msg_iov = iov; smh.msg_iovlen = 1; memset(spktinfo, 0, sizeof(*spktinfo)); /* set the outgoing interface */ if ((spktinfo->ipi6_ifindex = if_nametoindex(dev)) == 0) { /* this must not occur, so we might have to assert here */ dprintf(LOG_ERR, "relay6_forward_response: invlalid interface: %s", dev); return; } if (sendmsg(ssock, &smh, 0) != siz) dprintf(LOG_WARNING, "relay6_forward_response: sendmsg failed on %s :%s", dev, strerror(errno));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -