1 /* $OpenBSD: dhcrelay.c,v 1.31 2008/07/09 20:08:13 sobrado Exp $ */ 2 3 /* 4 * Copyright (c) 2004 Henning Brauer <henning@cvs.openbsd.org> 5 * Copyright (c) 1997, 1998, 1999 The Internet Software Consortium. 6 * 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 44 void usage(void); 45 void relay(struct interface_info *, struct dhcp_packet *, int, 46 unsigned int, struct iaddr, struct hardware *); 47 char *print_hw_addr(int, int, unsigned char *); 48 void got_response(struct protocol *); 49 50 time_t cur_time; 51 52 int log_perror = 1; 53 54 u_int16_t server_port; 55 u_int16_t client_port; 56 int log_priority; 57 struct interface_info *interfaces = NULL; 58 59 struct server_list { 60 struct server_list *next; 61 struct sockaddr_in to; 62 int fd; 63 } *servers; 64 65 int 66 main(int argc, char *argv[]) 67 { 68 int ch, no_daemon = 0, opt; 69 extern char *__progname; 70 struct server_list *sp = NULL; 71 struct passwd *pw; 72 struct sockaddr_in laddr; 73 74 /* Initially, log errors to stderr as well as to syslogd. */ 75 openlog(__progname, LOG_NDELAY, DHCPD_LOG_FACILITY); 76 setlogmask(LOG_UPTO(LOG_INFO)); 77 78 while ((ch = getopt(argc, argv, "di:")) != -1) { 79 switch (ch) { 80 case 'd': 81 no_daemon = 1; 82 break; 83 case 'i': 84 if (interfaces != NULL) 85 usage(); 86 if ((interfaces = calloc(1, 87 sizeof(struct interface_info))) == NULL) 88 error("calloc"); 89 strlcpy(interfaces->name, optarg, 90 sizeof(interfaces->name)); 91 break; 92 default: 93 usage(); 94 /* not reached */ 95 } 96 } 97 98 argc -= optind; 99 argv += optind; 100 101 if (argc < 1) 102 usage(); 103 104 while (argc > 0) { 105 struct hostent *he; 106 struct in_addr ia, *iap = NULL; 107 108 if (inet_aton(argv[0], &ia)) 109 iap = &ia; 110 else { 111 he = gethostbyname(argv[0]); 112 if (!he) 113 warning("%s: host unknown", argv[0]); 114 else 115 iap = ((struct in_addr *)he->h_addr_list[0]); 116 } 117 if (iap) { 118 if ((sp = calloc(1, sizeof *sp)) == NULL) 119 error("calloc"); 120 sp->next = servers; 121 servers = sp; 122 memcpy(&sp->to.sin_addr, iap, sizeof *iap); 123 } 124 argc--; 125 argv++; 126 } 127 128 log_perror = 0; 129 130 if (interfaces == NULL) 131 error("no interface given"); 132 133 /* Default DHCP/BOOTP ports. */ 134 server_port = htons(SERVER_PORT); 135 client_port = htons(CLIENT_PORT); 136 137 /* We need at least one server. */ 138 if (!sp) 139 usage(); 140 141 discover_interfaces(interfaces); 142 143 bzero(&laddr, sizeof laddr); 144 laddr.sin_len = sizeof laddr; 145 laddr.sin_family = AF_INET; 146 laddr.sin_port = server_port; 147 laddr.sin_addr.s_addr = interfaces->primary_address.s_addr; 148 /* Set up the server sockaddrs. */ 149 for (sp = servers; sp; sp = sp->next) { 150 sp->to.sin_port = server_port; 151 sp->to.sin_family = AF_INET; 152 sp->to.sin_len = sizeof sp->to; 153 sp->fd = socket(AF_INET, SOCK_DGRAM, 0); 154 if (sp->fd == -1) 155 error("socket: %m"); 156 opt = 1; 157 if (setsockopt(sp->fd, SOL_SOCKET, SO_REUSEPORT, 158 &opt, sizeof(opt)) == -1) 159 error("setsockopt: %m"); 160 if (bind(sp->fd, (struct sockaddr *)&laddr, sizeof laddr) == -1) 161 error("bind: %m"); 162 if (connect(sp->fd, (struct sockaddr *)&sp->to, 163 sizeof sp->to) == -1) 164 error("connect: %m"); 165 add_protocol("server", sp->fd, got_response, sp); 166 } 167 168 tzset(); 169 170 time(&cur_time); 171 bootp_packet_handler = relay; 172 if (!no_daemon) 173 daemon(0, 0); 174 175 if ((pw = getpwnam("_dhcp")) == NULL) 176 error("user \"_dhcp\" not found"); 177 if (chroot(_PATH_VAREMPTY) == -1) 178 error("chroot: %m"); 179 if (chdir("/") == -1) 180 error("chdir(\"/\"): %m"); 181 if (setgroups(1, &pw->pw_gid) || 182 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 183 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 184 error("can't drop privileges: %m"); 185 186 dispatch(); 187 /* not reached */ 188 189 exit(0); 190 } 191 192 void 193 relay(struct interface_info *ip, struct dhcp_packet *packet, int length, 194 unsigned int from_port, struct iaddr from, struct hardware *hfrom) 195 { 196 struct server_list *sp; 197 struct sockaddr_in to; 198 struct hardware hto; 199 200 if (packet->hlen > sizeof packet->chaddr) { 201 note("Discarding packet with invalid hlen."); 202 return; 203 } 204 205 /* If it's a bootreply, forward it to the client. */ 206 if (packet->op == BOOTREPLY) { 207 bzero(&to, sizeof(to)); 208 if (!(packet->flags & htons(BOOTP_BROADCAST))) { 209 to.sin_addr = packet->yiaddr; 210 to.sin_port = client_port; 211 } else { 212 to.sin_addr.s_addr = htonl(INADDR_BROADCAST); 213 to.sin_port = client_port; 214 } 215 to.sin_family = AF_INET; 216 to.sin_len = sizeof to; 217 218 /* Set up the hardware destination address. */ 219 hto.hlen = packet->hlen; 220 if (hto.hlen > sizeof hto.haddr) 221 hto.hlen = sizeof hto.haddr; 222 memcpy(hto.haddr, packet->chaddr, hto.hlen); 223 hto.htype = packet->htype; 224 225 if (send_packet(interfaces, packet, length, 226 interfaces->primary_address, &to, &hto) != -1) 227 debug("forwarded BOOTREPLY for %s to %s", 228 print_hw_addr(packet->htype, packet->hlen, 229 packet->chaddr), inet_ntoa(to.sin_addr)); 230 return; 231 } 232 233 if (ip == NULL) { 234 note("ignoring non BOOTREPLY from server"); 235 return; 236 } 237 238 /* If giaddr is set on a BOOTREQUEST, ignore it - it's already 239 been gatewayed. */ 240 if (packet->giaddr.s_addr) { 241 note("ignoring BOOTREQUEST with giaddr of %s", 242 inet_ntoa(packet->giaddr)); 243 return; 244 } 245 246 /* Set the giaddr so the server can figure out what net it's 247 from and so that we can later forward the response to the 248 correct net. */ 249 packet->giaddr = ip->primary_address; 250 251 /* Otherwise, it's a BOOTREQUEST, so forward it to all the 252 servers. */ 253 for (sp = servers; sp; sp = sp->next) { 254 if (send(sp->fd, packet, length, 0) != -1) { 255 debug("forwarded BOOTREQUEST for %s to %s", 256 print_hw_addr(packet->htype, packet->hlen, 257 packet->chaddr), inet_ntoa(sp->to.sin_addr)); 258 } 259 } 260 261 } 262 263 void 264 usage(void) 265 { 266 extern char *__progname; 267 268 fprintf(stderr, "usage: %s [-d] -i interface server1 [... serverN]\n", 269 __progname); 270 exit(1); 271 } 272 273 char * 274 print_hw_addr(int htype, int hlen, unsigned char *data) 275 { 276 static char habuf[49]; 277 char *s = habuf; 278 int i, j, slen = sizeof(habuf); 279 280 if (htype == 0 || hlen == 0) { 281 bad: 282 strlcpy(habuf, "<null>", sizeof habuf); 283 return habuf; 284 } 285 286 for (i = 0; i < hlen; i++) { 287 j = snprintf(s, slen, "%02x", data[i]); 288 if (j <= 0 || j >= slen) 289 goto bad; 290 j = strlen (s); 291 s += j; 292 slen -= (j + 1); 293 *s++ = ':'; 294 } 295 *--s = '\0'; 296 return habuf; 297 } 298 299 void 300 got_response(struct protocol *l) 301 { 302 ssize_t result; 303 struct iaddr ifrom; 304 union { 305 /* 306 * Packet input buffer. Must be as large as largest 307 * possible MTU. 308 */ 309 unsigned char packbuf[4095]; 310 struct dhcp_packet packet; 311 } u; 312 struct server_list *sp = l->local; 313 314 if ((result = recv(l->fd, u.packbuf, sizeof(u), 0)) == -1 && 315 errno != ECONNREFUSED) { 316 /* 317 * Ignore ECONNREFUSED as to many dhcp server send a bogus 318 * icmp unreach for every request. 319 */ 320 warning("recv failed for %s: %m", 321 inet_ntoa(sp->to.sin_addr)); 322 return; 323 } 324 if (result == -1 && errno == ECONNREFUSED) 325 return; 326 327 if (result == 0) 328 return; 329 330 if (result < BOOTP_MIN_LEN) { 331 note("Discarding packet with invalid size."); 332 return; 333 } 334 335 if (bootp_packet_handler) { 336 ifrom.len = 4; 337 memcpy(ifrom.iabuf, &sp->to.sin_addr, ifrom.len); 338 339 (*bootp_packet_handler)(NULL, &u.packet, result, 340 sp->to.sin_port, ifrom, NULL); 341 } 342 } 343