📄 dhcp6s.c
字号:
/* $KAME: dhcp6s.c,v 1.90 2002/06/28 07:29:43 jinmei Exp $ *//* * Copyright (C) 1998 and 1999 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include <sys/types.h>#include <sys/socket.h>#include <sys/sockio.h>#include <sys/ioctl.h>#include <sys/queue.h>#include <sys/uio.h>#if TIME_WITH_SYS_TIME# include <sys/time.h># include <time.h>#else# if HAVE_SYS_TIME_H# include <sys/time.h># else# include <time.h># endif#endif#include <errno.h>#include <net/if.h>#if defined(__FreeBSD__) && __FreeBSD__ >= 3#include <net/if_var.h>#endif#include <netinet/in.h>#include <netinet6/in6_var.h>#include <arpa/inet.h>#include <stdio.h>#include <stdarg.h>#include <syslog.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <err.h>#include <netdb.h>#include <limits.h>#include <dhcp6.h>#include <config.h>#include <common.h>#include <timer.h>typedef enum { DHCP6_CONFINFO_PREFIX } dhcp6_conftype_t;struct dhcp6_binding { TAILQ_ENTRY(dhcp6_binding) link; dhcp6_conftype_t type; struct duid clientid; void *val; u_int32_t duration; struct dhcp6_timer *timer;};static TAILQ_HEAD(, dhcp6_binding) dhcp6_binding_head;static int debug = 0;const dhcp6_mode_t dhcp6_mode = DHCP6_MODE_SERVER;char *device = NULL;int insock; /* inbound udp port */int outsock; /* outbound udp port */static const struct sockaddr_in6 *sa6_any_downstream;static struct msghdr rmh;static char rdatabuf[BUFSIZ];static int rmsgctllen;static char *rmsgctlbuf;static struct duid server_duid;static struct dhcp6_list arg_dnslist;#define DUID_FILE "/etc/dhcp6s_duid"#define DHCP6S_CONF "/usr/local/v6/etc/dhcp6s.conf"static void usage __P((void));static void server6_init __P((void));static void server6_mainloop __P((void));static int server6_recv __P((int));static int server6_react_solicit __P((struct dhcp6_if *, struct dhcp6 *, struct dhcp6_optinfo *, struct sockaddr *, int));static int server6_react_request __P((struct dhcp6_if *, struct in6_pktinfo *, struct dhcp6 *, struct dhcp6_optinfo *, struct sockaddr *, int));static int server6_react_renew __P((struct dhcp6_if *, struct in6_pktinfo *, struct dhcp6 *, struct dhcp6_optinfo *, struct sockaddr *, int));static int server6_react_rebind __P((struct dhcp6_if *, struct dhcp6 *, struct dhcp6_optinfo *, struct sockaddr *, int));static int server6_react_informreq __P((struct dhcp6_if *, struct dhcp6 *, struct dhcp6_optinfo *, struct sockaddr *, int));static int server6_send __P((int, struct dhcp6_if *, struct dhcp6 *, struct dhcp6_optinfo *, struct sockaddr *, int, struct dhcp6_optinfo *));static int create_conflist __P((dhcp6_conftype_t, struct duid *, struct dhcp6_list *, struct dhcp6_list *, struct dhcp6_list *, int));static struct dhcp6_binding *add_binding __P((struct duid *, dhcp6_conftype_t, void *));static struct dhcp6_binding *find_binding __P((struct duid *, dhcp6_conftype_t, void *));static void update_binding __P((struct dhcp6_binding *));static void remove_binding __P((struct dhcp6_binding *));static struct dhcp6_timer *binding_timo __P((void *));static char *bindingstr __P((struct dhcp6_binding *));intmain(argc, argv) int argc; char **argv;{ int ch; struct in6_addr a; struct dhcp6_listval *dlv; char *progname, *conffile = DHCP6S_CONF; if ((progname = strrchr(*argv, '/')) == NULL) progname = *argv; else progname++; TAILQ_INIT(&arg_dnslist); srandom(time(NULL) & getpid()); while ((ch = getopt(argc, argv, "c:dDfn:")) != -1) { switch (ch) { case 'c': conffile = optarg; break; case 'd': debug = 1; break; case 'D': debug = 2; break; case 'f': foreground++; break; case 'n': warnx("-n dnsserv option was obsoleted. " "use configuration file."); if (inet_pton(AF_INET6, optarg, &a) != 1) { errx(1, "invalid DNS server %s", optarg); /* NOTREACHED */ } if ((dlv = malloc(sizeof *dlv)) == NULL) { errx(1, "malloc failed for a DNS server"); /* NOTREACHED */ } dlv->val_addr6 = a; TAILQ_INSERT_TAIL(&arg_dnslist, dlv, link); break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 1) { usage(); /* NOTREACHED */ } device = argv[0]; if (foreground == 0) { if (daemon(0, 0) < 0) err(1, "daemon"); openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); } setloglevel(debug); ifinit(device); if ((cfparse(conffile)) != 0) { dprintf(LOG_ERR, "%s" "failed to parse configuration file", FNAME); exit(1); } /* prohibit a mixture of old and new style of DNS server config */ if (!TAILQ_EMPTY(&arg_dnslist)) { if (!TAILQ_EMPTY(&dnslist)) { 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); } server6_init(); server6_mainloop(); exit(0);}static voidusage(){ fprintf(stderr, "usage: dhcp6s [-c configfile] [-dDf] intface\n"); exit(0);}/*------------------------------------------------------------*/voidserver6_init(){ struct addrinfo hints; struct addrinfo *res, *res2; int error; int ifidx; int on = 1; struct ipv6_mreq mreq6; static struct iovec iov; static struct sockaddr_in6 sa6_any_downstream_storage; TAILQ_INIT(&dhcp6_binding_head); ifidx = if_nametoindex(device); if (ifidx == 0) { dprintf(LOG_ERR, "%s" "invalid interface %s", FNAME, device); exit(1); } /* get our DUID */ if (get_duid(DUID_FILE, &server_duid)) { dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME); exit(1); } /* initialize send/receive buffer */ iov.iov_base = (caddr_t)rdatabuf; iov.iov_len = sizeof(rdatabuf); rmh.msg_iov = &iov; rmh.msg_iovlen = 1; rmsgctllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); if ((rmsgctlbuf = (char *)malloc(rmsgctllen)) == NULL) { dprintf(LOG_ERR, "%s" "memory allocation failed", FNAME); exit(1); } /* initialize socket */ memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_PASSIVE; error = getaddrinfo(NULL, DH6PORT_UPSTREAM, &hints, &res); if (error) { dprintf(LOG_ERR, "%s" "getaddrinfo: %s", FNAME, gai_strerror(error)); exit(1); } insock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (insock < 0) { dprintf(LOG_ERR, "%s" "socket(insock): %s", FNAME, strerror(errno)); exit(1); } if (setsockopt(insock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) { dprintf(LOG_ERR, "%s" "setsockopt(insock, SO_REUSEPORT): %s", FNAME, strerror(errno)); exit(1); } if (setsockopt(insock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { dprintf(LOG_ERR, "%s" "setsockopt(insock, SO_REUSEADDR): %s", FNAME, strerror(errno)); exit(1); }#ifdef IPV6_RECVPKTINFO if (setsockopt(insock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) { dprintf(LOG_ERR, "%s" "setsockopt(inbound, IPV6_RECVPKTINFO): %s", FNAME, strerror(errno)); exit(1); }#else if (setsockopt(insock, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)) < 0) { dprintf(LOG_ERR, "%s" "setsockopt(inbound, IPV6_PKTINFO): %s", FNAME, strerror(errno)); exit(1); }#endif if (bind(insock, res->ai_addr, res->ai_addrlen) < 0) { dprintf(LOG_ERR, "%s" "bind(insock): %s", FNAME, strerror(errno)); exit(1); } freeaddrinfo(res); hints.ai_flags = 0; error = getaddrinfo(DH6ADDR_ALLAGENT, DH6PORT_UPSTREAM, &hints, &res2); if (error) { dprintf(LOG_ERR, "%s" "getaddrinfo: %s", FNAME, gai_strerror(error)); exit(1); } memset(&mreq6, 0, sizeof(mreq6)); mreq6.ipv6mr_interface = ifidx; memcpy(&mreq6.ipv6mr_multiaddr, &((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr, sizeof(mreq6.ipv6mr_multiaddr)); if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6))) { dprintf(LOG_ERR, "%s" "setsockopt(insock, IPV6_JOIN_GROUP)", FNAME, strerror(errno)); exit(1); } freeaddrinfo(res2); hints.ai_flags = 0; error = getaddrinfo(DH6ADDR_ALLSERVER, DH6PORT_UPSTREAM, &hints, &res2); if (error) { dprintf(LOG_ERR, "%s" "getaddrinfo: %s", FNAME, gai_strerror(error)); exit(1); } memset(&mreq6, 0, sizeof(mreq6)); mreq6.ipv6mr_interface = ifidx; memcpy(&mreq6.ipv6mr_multiaddr, &((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr, sizeof(mreq6.ipv6mr_multiaddr)); if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6))) { dprintf(LOG_ERR, "%s" "setsockopt(insock, IPV6_JOIN_GROUP): %s", FNAME, strerror(errno)); exit(1); } freeaddrinfo(res2); hints.ai_flags = 0; error = getaddrinfo(NULL, DH6PORT_DOWNSTREAM, &hints, &res); if (error) { dprintf(LOG_ERR, "%s" "getaddrinfo: %s", FNAME, gai_strerror(error)); exit(1); } outsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (outsock < 0) { dprintf(LOG_ERR, "%s" "socket(outsock): %s", FNAME, strerror(errno)); exit(1); } /* set outgoing interface of multicast packets for DHCP reconfig */ if (setsockopt(outsock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifidx, sizeof(ifidx)) < 0) { dprintf(LOG_ERR, "%s" "setsockopt(outsock, IPV6_MULTICAST_IF): %s", FNAME, strerror(errno)); exit(1); } /* make the socket write-only */ if (shutdown(outsock, 0)) { dprintf(LOG_ERR, "%s" "shutdown(outbound, 0): %s", FNAME, strerror(errno)); exit(1); } freeaddrinfo(res); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; error = getaddrinfo("::", DH6PORT_DOWNSTREAM, &hints, &res); if (error) { dprintf(LOG_ERR, "%s" "getaddrinfo: %s", FNAME, gai_strerror(error)); exit(1); } memcpy(&sa6_any_downstream_storage, res->ai_addr, res->ai_addrlen); sa6_any_downstream = (const struct sockaddr_in6*)&sa6_any_downstream_storage; freeaddrinfo(res);}static voidserver6_mainloop(){ struct timeval *w; int ret; fd_set r; ssize_t l; while (1) { w = dhcp6_check_timer(); FD_ZERO(&r); FD_SET(insock, &r); ret = select(insock + 1, &r, NULL, NULL, w); switch (ret) { case -1: dprintf(LOG_ERR, "%s" "select: %s", FNAME, strerror(errno)); exit(1); /* NOTREACHED */ case 0: /* timeout */ break; default: break; } if (FD_ISSET(insock, &r)) server6_recv(insock); }}static intserver6_recv(s) int s;{ ssize_t len; struct sockaddr_storage from; int fromlen; struct msghdr mhdr; struct iovec iov; char cmsgbuf[BUFSIZ]; struct cmsghdr *cm; struct in6_pktinfo *pi = NULL; struct dhcp6_if *ifp; struct dhcp6 *dh6; struct dhcp6opt *opt, *eopt;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -