void conf_rdomain(FILE *output, int ifs, char *ifname) { int rdomainid; rdomainid = get_rdomain(ifs, ifname); if (rdomainid > 0) fprintf(output, " rdomain %d\n", rdomainid); }
int main(int argc, char *argv[]) { int ch, no_daemon = 0, opt, rdomain; extern char *__progname; struct server_list *sp = NULL; struct passwd *pw; struct sockaddr_in laddr; /* Initially, log errors to stderr as well as to syslogd. */ openlog(__progname, LOG_NDELAY, DHCPD_LOG_FACILITY); setlogmask(LOG_UPTO(LOG_INFO)); while ((ch = getopt(argc, argv, "adi:o")) != -1) { switch (ch) { case 'd': no_daemon = 1; break; case 'i': if (interfaces != NULL) usage(); if ((interfaces = calloc(1, sizeof(struct interface_info))) == NULL) error("calloc"); strlcpy(interfaces->name, optarg, sizeof(interfaces->name)); break; case 'o': /* add the relay agent information option */ oflag++; break; default: usage(); /* not reached */ } } argc -= optind; argv += optind; if (argc < 1) usage(); while (argc > 0) { struct hostent *he; struct in_addr ia, *iap = NULL; if (inet_aton(argv[0], &ia)) iap = &ia; else { he = gethostbyname(argv[0]); if (!he) warning("%s: host unknown", argv[0]); else iap = ((struct in_addr *)he->h_addr_list[0]); } if (iap) { if ((sp = calloc(1, sizeof *sp)) == NULL) error("calloc"); sp->next = servers; servers = sp; memcpy(&sp->to.sin_addr, iap, sizeof *iap); } argc--; argv++; } if (!no_daemon) log_perror = 0; if (interfaces == NULL) error("no interface given"); /* Default DHCP/BOOTP ports. */ server_port = htons(SERVER_PORT); client_port = htons(CLIENT_PORT); /* We need at least one server. */ if (!sp) usage(); discover_interfaces(interfaces); rdomain = get_rdomain(interfaces->name); /* Enable the relay agent option by default for enc0 */ if (interfaces->hw_address.htype == HTYPE_IPSEC_TUNNEL) oflag++; bzero(&laddr, sizeof laddr); laddr.sin_len = sizeof laddr; laddr.sin_family = AF_INET; laddr.sin_port = server_port; laddr.sin_addr.s_addr = interfaces->primary_address.s_addr; /* Set up the server sockaddrs. */ for (sp = servers; sp; sp = sp->next) { sp->to.sin_port = server_port; sp->to.sin_family = AF_INET; sp->to.sin_len = sizeof sp->to; sp->fd = socket(AF_INET, SOCK_DGRAM, 0); if (sp->fd == -1) error("socket: %m"); opt = 1; if (setsockopt(sp->fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) == -1) error("setsockopt: %m"); if (setsockopt(sp->fd, SOL_SOCKET, SO_RTABLE, &rdomain, sizeof(rdomain)) == -1) error("setsockopt: %m"); if (bind(sp->fd, (struct sockaddr *)&laddr, sizeof laddr) == -1) error("bind: %m"); if (connect(sp->fd, (struct sockaddr *)&sp->to, sizeof sp->to) == -1) error("connect: %m"); add_protocol("server", sp->fd, got_response, sp); } /* Socket used to forward packets to the DHCP client */ if (interfaces->hw_address.htype == HTYPE_IPSEC_TUNNEL) { laddr.sin_addr.s_addr = INADDR_ANY; server_fd = socket(AF_INET, SOCK_DGRAM, 0); if (server_fd == -1) error("socket: %m"); opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) == -1) error("setsockopt: %m"); if (setsockopt(server_fd, SOL_SOCKET, SO_RTABLE, &rdomain, sizeof(rdomain)) == -1) error("setsockopt: %m"); if (bind(server_fd, (struct sockaddr *)&laddr, sizeof(laddr)) == -1) error("bind: %m"); } tzset(); time(&cur_time); bootp_packet_handler = relay; if (!no_daemon) daemon(0, 0); if ((pw = getpwnam("_dhcp")) == NULL) error("user \"_dhcp\" not found"); if (chroot(_PATH_VAREMPTY) == -1) error("chroot: %m"); if (chdir("/") == -1) error("chdir(\"/\"): %m"); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) error("can't drop privileges: %m"); dispatch(); /* not reached */ exit(0); }
int subnet_exists(struct client_lease *l) { struct option_data *opt; struct ifaddrs *ifap, *ifa; struct in_addr mymask, myaddr, mynet, hismask, hisaddr, hisnet; int myrdomain, hisrdomain; opt = &l->options[DHO_SUBNET_MASK]; if (opt->len == sizeof(mymask)) mymask.s_addr = ((struct in_addr *)opt->data)->s_addr; else mymask.s_addr = INADDR_ANY; myaddr.s_addr = l->address.s_addr; mynet.s_addr = mymask.s_addr & myaddr.s_addr; myrdomain = get_rdomain(ifi->name); if (getifaddrs(&ifap) != 0) error("getifaddrs failed"); for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { if (strcmp(ifi->name, ifa->ifa_name) == 0) continue; if (ifa->ifa_addr->sa_family != AF_INET) continue; hisrdomain = get_rdomain(ifa->ifa_name); if (hisrdomain != myrdomain) continue; memcpy(&hismask, &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr, sizeof(hismask)); memcpy(&hisaddr, &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, sizeof(hisaddr)); hisnet.s_addr = hisaddr.s_addr & hismask.s_addr; if (hisnet.s_addr == INADDR_ANY) continue; /* Would his packets go out *my* interface? */ if (mynet.s_addr == (hisaddr.s_addr & mymask.s_addr)) { note("interface %s already has the offered subnet!", ifa->ifa_name); return (1); } /* Would my packets go out *his* interface? */ if (hisnet.s_addr == (myaddr.s_addr & hismask.s_addr)) { note("interface %s already has the offered subnet!", ifa->ifa_name); return (1); } } freeifaddrs(ifap); return (0); }
void discover_interfaces(int *rdomain) { struct interface_info *tmp; struct interface_info *last, *next; struct subnet *subnet; struct shared_network *share; struct sockaddr_in foo; int ir = 0, ird; struct ifreq *tif; struct ifaddrs *ifap, *ifa; if (getifaddrs(&ifap) != 0) error("getifaddrs failed"); /* * If we already have a list of interfaces, the interfaces were * requested. */ if (interfaces != NULL) ir = 1; else /* must specify an interface when rdomains are used */ *rdomain = 0; /* Cycle through the list of interfaces looking for IP addresses. */ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { /* * See if this is the sort of interface we want to * deal with. Skip loopback, point-to-point and down * interfaces, except don't skip down interfaces if we're * trying to get a list of configurable interfaces. */ if ((ifa->ifa_flags & IFF_LOOPBACK) || (ifa->ifa_flags & IFF_POINTOPOINT) || (!(ifa->ifa_flags & IFF_UP)) || (!(ifa->ifa_flags & IFF_BROADCAST))) continue; /* See if we've seen an interface that matches this one. */ for (tmp = interfaces; tmp; tmp = tmp->next) if (!strcmp(tmp->name, ifa->ifa_name)) break; /* If we are looking for specific interfaces, ignore others. */ if (tmp == NULL && ir) continue; ird = get_rdomain(ifa->ifa_name); if (*rdomain == -1) *rdomain = ird; else if (*rdomain != ird && ir) error("Interface %s is not in rdomain %d", tmp->name, *rdomain); else if (*rdomain != ird && !ir) continue; /* If there isn't already an interface by this name, allocate one. */ if (tmp == NULL) { tmp = calloc(1, sizeof *tmp); if (!tmp) error("Insufficient memory to %s %s", "record interface", ifa->ifa_name); strlcpy(tmp->name, ifa->ifa_name, sizeof(tmp->name)); tmp->next = interfaces; tmp->noifmedia = tmp->dead = tmp->errors = 0; interfaces = tmp; } /* If we have the capability, extract link information and record it in a linked list. */ if (ifa->ifa_addr->sa_family == AF_LINK) { struct sockaddr_dl *foo = ((struct sockaddr_dl *)(ifa->ifa_addr)); tmp->index = foo->sdl_index; tmp->hw_address.hlen = foo->sdl_alen; tmp->hw_address.htype = HTYPE_ETHER; /* XXX */ memcpy(tmp->hw_address.haddr, LLADDR(foo), foo->sdl_alen); } else if (ifa->ifa_addr->sa_family == AF_INET) { struct iaddr addr; /* Get a pointer to the address... */ memcpy(&foo, ifa->ifa_addr, sizeof(foo)); /* We don't want the loopback interface. */ if (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK)) continue; /* If this is the first real IP address we've found, keep a pointer to ifreq structure in which we found it. */ if (!tmp->ifp) { int len = (IFNAMSIZ + ifa->ifa_addr->sa_len); tif = (struct ifreq *)malloc(len); if (!tif) error("no space to remember ifp."); strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ); memcpy(&tif->ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len); tmp->ifp = tif; tmp->primary_address = foo.sin_addr; } /* Grab the address... */ addr.len = 4; memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len); /* If there's a registered subnet for this address, connect it together... */ if ((subnet = find_subnet(addr))) { /* If this interface has multiple aliases on the same subnet, ignore all but the first we encounter. */ if (!subnet->interface) { subnet->interface = tmp; subnet->interface_address = addr; } else if (subnet->interface != tmp) { warning("Multiple %s %s: %s %s", "interfaces match the", "same subnet", subnet->interface->name, tmp->name); } share = subnet->shared_network; if (tmp->shared_network && tmp->shared_network != share) { warning("Interface %s matches %s", tmp->name, "multiple shared networks"); } else { tmp->shared_network = share; } if (!share->interface) { share->interface = tmp; } else if (share->interface != tmp) { warning("Multiple %s %s: %s %s", "interfaces match the", "same shared network", share->interface->name, tmp->name); } } } } /* Discard interfaces we can't listen on. */ last = NULL; for (tmp = interfaces; tmp; tmp = next) { next = tmp->next; if (!tmp->ifp) { warning("Can't listen on %s - it has no IP address.", tmp->name); /* Remove tmp from the list of interfaces. */ if (!last) interfaces = interfaces->next; else last->next = tmp->next; continue; } memcpy(&foo, &tmp->ifp->ifr_addr, sizeof tmp->ifp->ifr_addr); if (!tmp->shared_network) { warning("Can't listen on %s - dhcpd.conf has no subnet " "declaration for %s.", tmp->name, inet_ntoa(foo.sin_addr)); /* Remove tmp from the list of interfaces. */ if (!last) interfaces = interfaces->next; else last->next = tmp->next; continue; } last = tmp; /* Find subnets that don't have valid interface addresses. */ for (subnet = (tmp->shared_network ? tmp->shared_network->subnets : NULL); subnet; subnet = subnet->next_sibling) { if (!subnet->interface_address.len) { /* * Set the interface address for this subnet * to the first address we found. */ subnet->interface_address.len = 4; memcpy(subnet->interface_address.iabuf, &foo.sin_addr.s_addr, 4); } } /* Register the interface... */ if_register_receive(tmp); if_register_send(tmp); note("Listening on %s (%s).", tmp->name, inet_ntoa(foo.sin_addr)); } if (interfaces == NULL) error("No interfaces to listen on."); /* Now register all the remaining interfaces as protocols. */ for (tmp = interfaces; tmp; tmp = tmp->next) add_protocol(tmp->name, tmp->rfdesc, got_one, tmp); freeifaddrs(ifap); }
/* * Loop waiting for packets, timeouts or routing messages. */ void dispatch(void) { int count, to_msec; struct pollfd fds[3]; time_t cur_time, howlong; void (*func)(void); while (quit == 0) { /* * Call expired timeout, and then if there's still * a timeout registered, time out the select call then. */ another: if (!ifi) { warning("No interfaces available"); quit = INTERNALSIG; continue; } if (ifi->rdomain != get_rdomain(ifi->name)) { warning("Interface %s:" " rdomain changed out from under us", ifi->name); quit = INTERNALSIG; continue; } if (timeout.func) { time(&cur_time); if (timeout.when <= cur_time) { func = timeout.func; cancel_timeout(); (*(func))(); goto another; } /* * Figure timeout in milliseconds, and check for * potential overflow, so we can cram into an * int for poll, while not polling with a * negative timeout and blocking indefinitely. */ howlong = timeout.when - cur_time; if (howlong > INT_MAX / 1000) howlong = INT_MAX / 1000; to_msec = howlong * 1000; } else to_msec = -1; /* Set up the descriptors to be polled. */ if (!ifi || ifi->rfdesc == -1) { warning("No live interface to poll on"); quit = INTERNALSIG; continue; } fds[0].fd = ifi->rfdesc; fds[1].fd = routefd; /* Could be -1, which will be ignored. */ fds[2].fd = unpriv_ibuf->fd; fds[0].events = fds[1].events = fds[2].events = POLLIN; if (unpriv_ibuf->w.queued) fds[2].events |= POLLOUT; /* Wait for a packet or a timeout or unpriv_ibuf->fd. XXX */ count = poll(fds, 3, to_msec); /* Not likely to be transitory. */ if (count == -1) { if (errno == EAGAIN || errno == EINTR) { continue; } else { warning("poll: %s", strerror(errno)); quit = INTERNALSIG; continue; } } if ((fds[0].revents & (POLLIN | POLLHUP))) { if (ifi && ifi->linkstat && ifi->rfdesc != -1) got_one(); } if ((fds[1].revents & (POLLIN | POLLHUP))) { if (ifi) routehandler(); } if (fds[2].revents & POLLOUT) { if (msgbuf_write(&unpriv_ibuf->w) <= 0 && errno != EAGAIN) { warning("pipe write error to [priv]"); quit = INTERNALSIG; continue; } } if ((fds[2].revents & (POLLIN | POLLHUP))) { /* Pipe to [priv] closed. Assume it emitted error. */ quit = INTERNALSIG; continue; } } if (quit == SIGHUP) { /* Tell [priv] process that HUP has occurred. */ sendhup(client->active); warning("%s; restarting", strsignal(quit)); exit (0); } else if (quit != INTERNALSIG) { warning("%s; exiting", strsignal(quit)); exit(1); } }