📄 dhcp6s.c
字号:
/* $Id: dhcp6s.c,v 1.19 2004/03/15 22:03:21 shemminger Exp $ *//* ported from KAME: dhcp6s.c,v 1.91 2002/09/24 14:20:50 itojun 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/stat.h>#include <sys/socket.h>#include <linux/sockios.h>#include <sys/ioctl.h>#include <sys/file.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 <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 "queue.h"#include "timer.h"#include "dhcp6.h"#include "config.h"#include "common.h"#include "server6_conf.h"#include "lease.h"typedef enum { DHCP6_CONFINFO_PREFIX, DHCP6_CONFINFO_ADDRS } 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 char *device[100];static int num_device = 0;static int debug = 0;const dhcp6_mode_t dhcp6_mode = DHCP6_MODE_SERVER;int insock; /* inbound udp port */int outsock; /* outbound udp port */extern FILE *server6_lease_file;char server6_lease_temp[100];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 dns_list arg_dnslist;static struct dhcp6_timer *sync_lease_timer;struct link_decl *subnet = NULL;struct host_decl *host = NULL;struct rootgroup *globalgroup = NULL;#define DUID_FILE "/var/lib/dhcpv6/dhcp6s_duid"#define DHCP6S_CONF "/etc/dhcp6s.conf"#define DH6_VALID_MESSAGE(a) \ (a == DH6_SOLICIT || a == DH6_REQUEST || a == DH6_RENEW || \ a == DH6_REBIND || a == DH6_CONFIRM || a == DH6_RELEASE || \ a == DH6_DECLINE || a == DH6_INFORM_REQ)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_message __P((struct dhcp6_if *, struct in6_pktinfo *, 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 struct dhcp6_timer *check_lease_file_timo __P((void *arg));extern struct link_decl *dhcp6_allocate_link __P((struct dhcp6_if *, struct rootgroup *, struct in6_addr *));extern struct host_decl *dhcp6_allocate_host __P((struct dhcp6_if *, struct rootgroup *, struct dhcp6_optinfo *));extern int dhcp6_get_hostconf __P((struct dhcp6_optinfo *, struct dhcp6_optinfo *, struct dhcp6_iaidaddr *, struct host_decl *)); static void random_init(void) { int f, n; unsigned int seed = time(NULL) & getpid(); char rand_state[256]; f = open("/dev/urandom", O_RDONLY); if (f > 0) { n = read(f, rand_state, sizeof(rand_state)); close(f); if (n > 32) { initstate(seed, rand_state, n); return; } } srandom(seed);}intmain(argc, argv) int argc; char **argv;{ int ch, i = 0; 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.addrlist); random_init(); 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.addrlist, dlv, link); break; default: usage(); /* NOTREACHED */ } } while (optind < argc) { device[num_device] = argv[optind++]; num_device += 1; } if (foreground == 0) { if (daemon(0, 0) < 0) err(1, "daemon"); openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); } setloglevel(debug); server6_init(); if ((server6_lease_file = init_leases(PATH_SERVER6_LEASE)) == NULL) { dprintf(LOG_ERR, "%s" "failed to parse lease file", FNAME); exit(1); } strcpy(server6_lease_temp, PATH_SERVER6_LEASE); strcat(server6_lease_temp, "XXXXXX"); server6_lease_file = sync_leases(server6_lease_file, PATH_SERVER6_LEASE, server6_lease_temp); if (server6_lease_file == NULL) exit(1); globalgroup = (struct rootgroup *)malloc(sizeof(struct rootgroup)); if (globalgroup == NULL) { dprintf(LOG_ERR, "failed to allocate memory %s", strerror(errno)); exit(1); } memset(globalgroup, 0, sizeof(*globalgroup)); TAILQ_INIT(&globalgroup->scope.dnslist.addrlist); if ((sfparse(conffile)) != 0) { dprintf(LOG_ERR, "%s" "failed to parse addr configuration file", FNAME); exit(1); } server6_mainloop(); exit(0);}static voidusage(){ fprintf(stderr, "usage: dhcp6s [-c configfile] [-dDf] [interface]\n"); exit(0);}/*------------------------------------------------------------*/voidserver6_init(){ struct addrinfo hints; struct addrinfo *res, *res2; int error, skfd, i; int on = 1; int ifidx[MAX_DEVICE]; struct ipv6_mreq mreq6; static struct iovec iov; static struct sockaddr_in6 sa6_any_downstream_storage; char buff[1024]; struct ifconf ifc; struct ifreq *ifr; double d; struct timeval timo; /* initialize inbound 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); }#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); /* initiallize outbound interface */ hints.ai_flags = AI_PASSIVE; 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); } if (bind(outsock, res->ai_addr, res->ai_addrlen) < 0) { dprintf(LOG_ERR, "%s" "bind(outsock): %s", FNAME, strerror(errno)); 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); /* 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); } if (num_device != 0) { for (i = 0; i < num_device; i++) { ifidx[i] = if_nametoindex(device[i]); if (ifidx[i] == 0) { dprintf(LOG_ERR, "%s" "invalid interface %s", FNAME, device[0]); exit(1); } ifinit(device[i]); } if (get_duid(DUID_FILE, device[0], &server_duid)) { dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME); exit(1); } } else { /* all the interfaces join multicast group */ ifc.ifc_len = sizeof(buff); ifc.ifc_buf = buff; if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0) { dprintf(LOG_ERR, "new socket failed"); exit(1); } if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) { dprintf(LOG_ERR, "SIOCGIFCONF: %s\n", strerror(errno)); exit(1); } ifr = ifc.ifc_req; for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { dprintf(LOG_DEBUG, "found device %s", ifr->ifr_name); ifidx[num_device] = if_nametoindex(ifr->ifr_name); if (ifidx[num_device] < 0) { dprintf(LOG_ERR, "%s: unknown interface.\n", ifr->ifr_name); continue; } dprintf(LOG_DEBUG, "if %s index is %d", ifr->ifr_name, ifidx[num_device]);#ifdef mshirley if (((ifr->ifr_flags & IFF_UP) == 0)) continue;#endif if (strcmp(ifr->ifr_name, "lo")) { /* get our DUID */ if (get_duid(DUID_FILE, ifr->ifr_name, &server_duid)) { dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME); exit(1); } } ifinit(ifr->ifr_name); num_device += 1; } } for (i = 0; i < num_device; i++) { 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[i]; 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(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[i]; 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); /* set outgoing interface of multicast packets for DHCP reconfig */ if (setsockopt(outsock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifidx[i], sizeof(ifidx[i])) < 0) { dprintf(LOG_ERR, "%s" "setsockopt(outsock, IPV6_MULTICAST_IF): %s", FNAME, strerror(errno)); exit(1); } } /* set up sync lease file timer */ sync_lease_timer = dhcp6_add_timer(check_lease_file_timo, NULL); d = DHCP6_SYNCFILE_TIME; timo.tv_sec = (long)d; timo.tv_usec = 0; dprintf(LOG_DEBUG, "set timer for syncing file ..."); dhcp6_set_timer(&timo, sync_lease_timer); return;}static voidserver6_mainloop(){ struct timeval *w; int ret; fd_set r; 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -