1 /* $OpenBSD: dhcpd.c,v 1.45 2014/07/11 09:42:27 yasuoka Exp $ */ 2 3 /* 4 * Copyright (c) 2004 Henning Brauer <henning@cvs.openbsd.org> 5 * Copyright (c) 1995, 1996, 1997, 1998, 1999 6 * The Internet Software Consortium. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of The Internet Software Consortium nor the names 18 * of its contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 22 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 26 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 29 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * This software has been written for the Internet Software Consortium 36 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 37 * Enterprises. To learn more about the Internet Software Consortium, 38 * see ``http://www.vix.com/isc''. To learn more about Vixie 39 * Enterprises, see ``http://www.vix.com''. 40 */ 41 42 #include "dhcpd.h" 43 #include "sync.h" 44 45 #include <err.h> 46 #include <pwd.h> 47 48 void usage(void); 49 50 time_t cur_time; 51 struct group root_group; 52 53 u_int16_t server_port; 54 u_int16_t client_port; 55 56 struct passwd *pw; 57 int log_priority; 58 int log_perror = 0; 59 int pfpipe[2]; 60 int gotpipe = 0; 61 int syncrecv; 62 int syncsend; 63 pid_t pfproc_pid = -1; 64 char *path_dhcpd_conf = _PATH_DHCPD_CONF; 65 char *path_dhcpd_db = _PATH_DHCPD_DB; 66 char *abandoned_tab = NULL; 67 char *changedmac_tab = NULL; 68 char *leased_tab = NULL; 69 struct syslog_data sdata = SYSLOG_DATA_INIT; 70 71 int 72 main(int argc, char *argv[]) 73 { 74 int ch, cftest = 0, daemonize = 1, rdomain = -1, udpsockmode = 0; 75 extern char *__progname; 76 char *sync_iface = NULL; 77 char *sync_baddr = NULL; 78 u_short sync_port = 0; 79 struct servent *ent; 80 struct in_addr udpaddr; 81 82 /* Initially, log errors to stderr as well as to syslogd. */ 83 openlog_r(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY, &sdata); 84 85 opterr = 0; 86 while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nu::Y:y:")) != -1) 87 switch (ch) { 88 case 'Y': 89 syncsend = 1; 90 break; 91 case 'y': 92 syncrecv = 1; 93 break; 94 } 95 if (syncsend || syncrecv) { 96 if ((ent = getservbyname("dhcpd-sync", "udp")) == NULL) 97 errx(1, "Can't find service \"dhcpd-sync\" in " 98 "/etc/services"); 99 sync_port = ntohs(ent->s_port); 100 } 101 102 udpaddr.s_addr = htonl(INADDR_BROADCAST); 103 104 optreset = optind = opterr = 1; 105 while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nu::Y:y:")) != -1) 106 switch (ch) { 107 case 'A': 108 abandoned_tab = optarg; 109 break; 110 case 'C': 111 changedmac_tab = optarg; 112 break; 113 case 'L': 114 leased_tab = optarg; 115 break; 116 case 'c': 117 path_dhcpd_conf = optarg; 118 break; 119 case 'd': 120 daemonize = 0; 121 log_perror = 1; 122 break; 123 case 'f': 124 daemonize = 0; 125 break; 126 case 'l': 127 path_dhcpd_db = optarg; 128 break; 129 case 'n': 130 daemonize = 0; 131 cftest = 1; 132 log_perror = 1; 133 break; 134 case 'u': 135 udpsockmode = 1; 136 if (optarg != NULL) { 137 if (inet_aton(optarg, &udpaddr) != 1) 138 errx(1, "Cannot parse binding IP " 139 "address: %s", optarg); 140 } 141 break; 142 case 'Y': 143 if (sync_addhost(optarg, sync_port) != 0) 144 sync_iface = optarg; 145 syncsend = 1; 146 break; 147 case 'y': 148 sync_baddr = optarg; 149 syncrecv = 1; 150 break; 151 default: 152 usage(); 153 } 154 155 argc -= optind; 156 argv += optind; 157 158 while (argc > 0) { 159 struct interface_info *tmp = calloc(1, sizeof(*tmp)); 160 if (!tmp) 161 error("calloc"); 162 strlcpy(tmp->name, argv[0], sizeof(tmp->name)); 163 tmp->next = interfaces; 164 interfaces = tmp; 165 argc--; 166 argv++; 167 } 168 169 /* Default DHCP/BOOTP ports. */ 170 server_port = htons(SERVER_PORT); 171 client_port = htons(CLIENT_PORT); 172 173 tzset(); 174 175 time(&cur_time); 176 if (!readconf()) 177 error("Configuration file errors encountered"); 178 179 if (cftest) 180 exit(0); 181 182 db_startup(); 183 if (!udpsockmode || argc > 0) 184 discover_interfaces(&rdomain); 185 186 if (rdomain != -1) 187 if (setrtable(rdomain) == -1) 188 error("setrtable (%m)"); 189 190 if (udpsockmode) 191 udpsock_startup(udpaddr); 192 icmp_startup(1, lease_pinged); 193 194 if (syncsend || syncrecv) { 195 syncfd = sync_init(sync_iface, sync_baddr, sync_port); 196 if (syncfd == -1) 197 err(1, "sync init"); 198 } 199 200 if ((pw = getpwnam("_dhcp")) == NULL) 201 error("user \"_dhcp\" not found"); 202 203 if (daemonize) 204 daemon(0, 0); 205 206 /* don't go near /dev/pf unless we actually intend to use it */ 207 if ((abandoned_tab != NULL) || 208 (changedmac_tab != NULL) || 209 (leased_tab != NULL)){ 210 if (pipe(pfpipe) == -1) 211 error("pipe (%m)"); 212 switch (pfproc_pid = fork()){ 213 case -1: 214 error("fork (%m)"); 215 /* NOTREACHED */ 216 exit(1); 217 case 0: 218 /* child process. start up table engine */ 219 pftable_handler(); 220 /* NOTREACHED */ 221 exit(1); 222 default: 223 gotpipe = 1; 224 break; 225 } 226 } 227 228 if (chroot(_PATH_VAREMPTY) == -1) 229 error("chroot %s: %m", _PATH_VAREMPTY); 230 if (chdir("/") == -1) 231 error("chdir(\"/\"): %m"); 232 if (setgroups(1, &pw->pw_gid) || 233 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 234 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 235 error("can't drop privileges: %m"); 236 237 add_timeout(cur_time + 5, periodic_scan, NULL); 238 dispatch(); 239 240 /* not reached */ 241 exit(0); 242 } 243 244 void 245 usage(void) 246 { 247 extern char *__progname; 248 249 fprintf(stderr, "usage: %s [-dfn] [-A abandoned_ip_table]", __progname); 250 fprintf(stderr, " [-C changed_ip_table]\n"); 251 fprintf(stderr, "\t[-c config-file] [-L leased_ip_table]"); 252 fprintf(stderr, " [-l lease-file] [-u[bind_address]]\n"); 253 fprintf(stderr, "\t[-Y synctarget] [-y synclisten] [if0 [... ifN]]\n"); 254 exit(1); 255 } 256 257 void 258 lease_pinged(struct iaddr from, u_int8_t *packet, int length) 259 { 260 struct lease *lp; 261 262 /* 263 * Don't try to look up a pinged lease if we aren't trying to 264 * ping one - otherwise somebody could easily make us churn by 265 * just forging repeated ICMP EchoReply packets for us to look 266 * up. 267 */ 268 if (!outstanding_pings) 269 return; 270 271 lp = find_lease_by_ip_addr(from); 272 273 if (!lp) { 274 note("unexpected ICMP Echo Reply from %s", piaddr(from)); 275 return; 276 } 277 278 if (!lp->state && !lp->releasing) { 279 warning("ICMP Echo Reply for %s arrived late or is spurious.", 280 piaddr(from)); 281 return; 282 } 283 284 /* At this point it looks like we pinged a lease and got a 285 * response, which shouldn't have happened. 286 * if it did it's either one of two two cases: 287 * 1 - we pinged this lease before offering it and 288 * something answered, so we abandon it. 289 * 2 - we pinged this lease before releasing it 290 * and something answered, so we don't release it. 291 */ 292 if (lp->releasing) { 293 warning("IP address %s answers a ping after sending a release", 294 piaddr(lp->ip_addr)); 295 warning("Possible release spoof - Not releasing address %s", 296 piaddr(lp->ip_addr)); 297 lp->releasing = 0; 298 } else { 299 free_lease_state(lp->state, "lease_pinged"); 300 lp->state = NULL; 301 abandon_lease(lp, "pinged before offer"); 302 } 303 cancel_timeout(lease_ping_timeout, lp); 304 --outstanding_pings; 305 } 306 307 void 308 lease_ping_timeout(void *vlp) 309 { 310 struct lease *lp = vlp; 311 312 --outstanding_pings; 313 if (lp->releasing) { 314 lp->releasing = 0; 315 release_lease(lp); 316 } else 317 dhcp_reply(lp); 318 } 319 320 /* from memory.c - needed to be able to walk the lease table */ 321 extern struct subnet *subnets; 322 323 void 324 periodic_scan(void *p) 325 { 326 time_t x, y; 327 struct subnet *n; 328 struct group *g; 329 struct shared_network *s; 330 struct lease *l; 331 332 /* find the shortest lease this server gives out */ 333 x = MIN(root_group.default_lease_time, root_group.max_lease_time); 334 for (n = subnets; n; n = n->next_subnet) 335 for (g = n->group; g; g = g->next) 336 x = MIN(x, g->default_lease_time); 337 338 /* use half of the shortest lease as the scan interval */ 339 y = x / 2; 340 if (y < 1) 341 y = 1; 342 343 /* walk across all leases to find the exired ones */ 344 for (n = subnets; n; n = n->next_subnet) 345 for (g = n->group; g; g = g->next) 346 for (s = g->shared_network; s; s = s->next) 347 for (l = s->leases; l && l->ends; l = l->next) 348 if (cur_time >= l->ends){ 349 release_lease(l); 350 pfmsg('R', l); 351 } 352 353 add_timeout(cur_time + y, periodic_scan, NULL); 354 } 355