1*ac6360cbSflorian /* $OpenBSD: dhcrelay.c,v 1.67 2024/08/21 10:35:12 florian Exp $ */ 23dd2010fShenning 348be18b4Shenning /* 413be1508Shenning * Copyright (c) 2004 Henning Brauer <henning@cvs.openbsd.org> 548be18b4Shenning * Copyright (c) 1997, 1998, 1999 The Internet Software Consortium. 648be18b4Shenning * All rights reserved. 748be18b4Shenning * 848be18b4Shenning * Redistribution and use in source and binary forms, with or without 948be18b4Shenning * modification, are permitted provided that the following conditions 1048be18b4Shenning * are met: 1148be18b4Shenning * 1248be18b4Shenning * 1. Redistributions of source code must retain the above copyright 1348be18b4Shenning * notice, this list of conditions and the following disclaimer. 1448be18b4Shenning * 2. Redistributions in binary form must reproduce the above copyright 1548be18b4Shenning * notice, this list of conditions and the following disclaimer in the 1648be18b4Shenning * documentation and/or other materials provided with the distribution. 1748be18b4Shenning * 3. Neither the name of The Internet Software Consortium nor the names 1848be18b4Shenning * of its contributors may be used to endorse or promote products derived 1948be18b4Shenning * from this software without specific prior written permission. 2048be18b4Shenning * 2148be18b4Shenning * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 2248be18b4Shenning * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 2348be18b4Shenning * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 2448be18b4Shenning * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 2548be18b4Shenning * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 2648be18b4Shenning * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2748be18b4Shenning * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2848be18b4Shenning * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2948be18b4Shenning * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 3048be18b4Shenning * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 3148be18b4Shenning * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 3248be18b4Shenning * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3348be18b4Shenning * SUCH DAMAGE. 3448be18b4Shenning * 3548be18b4Shenning * This software has been written for the Internet Software Consortium 3648be18b4Shenning * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 3748be18b4Shenning * Enterprises. To learn more about the Internet Software Consortium, 3848be18b4Shenning * see ``http://www.vix.com/isc''. To learn more about Vixie 3948be18b4Shenning * Enterprises, see ``http://www.vix.com''. 4048be18b4Shenning */ 4148be18b4Shenning 42f70ef60cSkrw #include <sys/types.h> 43b0e46094Sclaudio #include <sys/ioctl.h> 44f70ef60cSkrw #include <sys/socket.h> 45f70ef60cSkrw 46f70ef60cSkrw #include <arpa/inet.h> 47f70ef60cSkrw 48f70ef60cSkrw #include <net/if.h> 49f70ef60cSkrw 50f70ef60cSkrw #include <errno.h> 517307f9faSjca #include <fcntl.h> 52f70ef60cSkrw #include <netdb.h> 53f70ef60cSkrw #include <paths.h> 54f70ef60cSkrw #include <pwd.h> 55f70ef60cSkrw #include <stdio.h> 56f70ef60cSkrw #include <stdlib.h> 57f70ef60cSkrw #include <string.h> 58f70ef60cSkrw #include <syslog.h> 59579e3f2dSguenther #include <time.h> 60f70ef60cSkrw #include <unistd.h> 61f70ef60cSkrw 62f70ef60cSkrw #include "dhcp.h" 63f70ef60cSkrw #include "dhcpd.h" 64986dbb4cSkrw #include "log.h" 6548be18b4Shenning 66d8188208Shenning void usage(void); 677307f9faSjca int rdaemon(int); 6848be18b4Shenning void relay(struct interface_info *, struct dhcp_packet *, int, 69fa3d4f89Srzalamena struct packet_ctx *); 70e4b49721Srzalamena void l2relay(struct interface_info *, struct dhcp_packet *, int, 71e4b49721Srzalamena struct packet_ctx *); 7248be18b4Shenning char *print_hw_addr(int, int, unsigned char *); 73c3d07ec0Sclaudio void got_response(struct protocol *); 74b0e46094Sclaudio int get_rdomain(char *); 7548be18b4Shenning 762936f4ebSrzalamena void relay_agentinfo(struct packet_ctx *, struct interface_info *, int); 77e4b49721Srzalamena 78e4b49721Srzalamena int relay_agentinfo_cmp(struct packet_ctx *pc, uint8_t *, int); 79e4b49721Srzalamena ssize_t relay_agentinfo_append(struct packet_ctx *, struct dhcp_packet *, 80e4b49721Srzalamena size_t); 81e4b49721Srzalamena ssize_t relay_agentinfo_remove(struct packet_ctx *, struct dhcp_packet *, 82e4b49721Srzalamena size_t); 834be048dcSreyk 8448be18b4Shenning time_t cur_time; 8548be18b4Shenning 8648be18b4Shenning struct interface_info *interfaces = NULL; 87f51f0ea7Srzalamena struct server_list *servers; 88962cae8eSrzalamena struct iflist intflist; 894be048dcSreyk int server_fd; 904be048dcSreyk int oflag; 9148be18b4Shenning 92e4b49721Srzalamena enum dhcp_relay_mode drm = DRM_UNKNOWN; 93e4b49721Srzalamena const char *rai_circuit = NULL; 94e4b49721Srzalamena const char *rai_remote = NULL; 95c4c35e14Srzalamena int rai_replace = 0; 96e4b49721Srzalamena 9713be1508Shenning int 9813be1508Shenning main(int argc, char *argv[]) 9948be18b4Shenning { 1007307f9faSjca int ch, devnull = -1, daemonize, opt, rdomain; 1010d2d9a15Shenning struct server_list *sp = NULL; 102a0ba4c4aShenning struct passwd *pw; 103c3d07ec0Sclaudio struct sockaddr_in laddr; 104e4b49721Srzalamena int optslen; 10548be18b4Shenning 106f36a5097Sjca daemonize = 1; 107f36a5097Sjca 108986dbb4cSkrw log_init(1, LOG_DAEMON); /* log to stderr until daemonized */ 10948be18b4Shenning 110962cae8eSrzalamena setup_iflist(); 111962cae8eSrzalamena 112c4c35e14Srzalamena while ((ch = getopt(argc, argv, "aC:di:oR:r")) != -1) { 1130d2d9a15Shenning switch (ch) { 114e4b49721Srzalamena case 'C': 115e4b49721Srzalamena rai_circuit = optarg; 116e4b49721Srzalamena break; 1170d2d9a15Shenning case 'd': 118f36a5097Sjca daemonize = 0; 1190d2d9a15Shenning break; 1200d2d9a15Shenning case 'i': 1210d2d9a15Shenning if (interfaces != NULL) 122d8188208Shenning usage(); 123ffc715d7Srzalamena 124962cae8eSrzalamena interfaces = iflist_getbyname(optarg); 125a29cd94bSrzalamena if (interfaces == NULL) 126986dbb4cSkrw fatalx("interface '%s' not found", optarg); 1270d2d9a15Shenning break; 1284be048dcSreyk case 'o': 1294be048dcSreyk /* add the relay agent information option */ 1304be048dcSreyk oflag++; 1314be048dcSreyk break; 132e4b49721Srzalamena case 'R': 133e4b49721Srzalamena rai_remote = optarg; 134e4b49721Srzalamena break; 135c4c35e14Srzalamena case 'r': 136c4c35e14Srzalamena rai_replace = 1; 137c4c35e14Srzalamena break; 1384be048dcSreyk 1390d2d9a15Shenning default: 140d8188208Shenning usage(); 1410d2d9a15Shenning /* not reached */ 14248be18b4Shenning } 14348be18b4Shenning } 1440d2d9a15Shenning 1450d2d9a15Shenning argc -= optind; 1460d2d9a15Shenning argv += optind; 1470d2d9a15Shenning 1480d2d9a15Shenning if (argc < 1) 1490d2d9a15Shenning usage(); 1500d2d9a15Shenning 151e4b49721Srzalamena if (rai_remote != NULL && rai_circuit == NULL) 152986dbb4cSkrw fatalx("you must specify a circuit-id with a remote-id"); 153e4b49721Srzalamena 154e4b49721Srzalamena /* Validate that we have space for all suboptions. */ 155e4b49721Srzalamena if (rai_circuit != NULL) { 156e4b49721Srzalamena optslen = 2 + strlen(rai_circuit); 157e4b49721Srzalamena if (rai_remote != NULL) 158e4b49721Srzalamena optslen += 2 + strlen(rai_remote); 159e4b49721Srzalamena 160e4b49721Srzalamena if (optslen > DHCP_OPTION_MAXLEN) 161986dbb4cSkrw fatalx("relay agent information is too long"); 162e4b49721Srzalamena } 163e4b49721Srzalamena 1640d2d9a15Shenning while (argc > 0) { 165*ac6360cbSflorian struct addrinfo hints, *res; 1660d2d9a15Shenning struct in_addr ia, *iap = NULL; 1670d2d9a15Shenning 168e4b49721Srzalamena if ((sp = calloc(1, sizeof(*sp))) == NULL) 169986dbb4cSkrw fatalx("calloc"); 170e4b49721Srzalamena 171*ac6360cbSflorian memset(&hints, 0, sizeof(hints)); 172*ac6360cbSflorian hints.ai_family = AF_INET; 173*ac6360cbSflorian 174962cae8eSrzalamena if ((sp->intf = register_interface(argv[0], got_one, 175962cae8eSrzalamena 1)) != NULL) { 176e4b49721Srzalamena if (drm == DRM_LAYER3) 177986dbb4cSkrw fatalx("don't mix interfaces with hosts"); 178e4b49721Srzalamena 179e4b49721Srzalamena if (sp->intf->hw_address.htype == HTYPE_IPSEC_TUNNEL) 18044c85a19Smpi fatalx("can't use IPsec with layer 2"); 181e4b49721Srzalamena 182e4b49721Srzalamena sp->next = servers; 183e4b49721Srzalamena servers = sp; 184e4b49721Srzalamena 185e4b49721Srzalamena drm = DRM_LAYER2; 186e4b49721Srzalamena argc--; 187e4b49721Srzalamena argv++; 188e4b49721Srzalamena continue; 189e4b49721Srzalamena } 190e4b49721Srzalamena 191*ac6360cbSflorian if (getaddrinfo(argv[0], NULL, &hints, &res) != 0) 192986dbb4cSkrw log_warnx("%s: host unknown", argv[0]); 193*ac6360cbSflorian else { 194*ac6360cbSflorian ia = ((struct sockaddr_in *)res->ai_addr)->sin_addr; 195*ac6360cbSflorian iap = &ia; 196*ac6360cbSflorian freeaddrinfo(res); 1970d2d9a15Shenning } 198*ac6360cbSflorian 19948be18b4Shenning if (iap) { 200e4b49721Srzalamena if (drm == DRM_LAYER2) 201986dbb4cSkrw fatalx("don't mix interfaces with hosts"); 202e4b49721Srzalamena 203e4b49721Srzalamena drm = DRM_LAYER3; 20448be18b4Shenning sp->next = servers; 20548be18b4Shenning servers = sp; 206f51f0ea7Srzalamena memcpy(&ss2sin(&sp->to)->sin_addr, iap, sizeof(*iap)); 207e4b49721Srzalamena } else 208e4b49721Srzalamena free(sp); 209e4b49721Srzalamena 2100d2d9a15Shenning argc--; 2110d2d9a15Shenning argv++; 21248be18b4Shenning } 21348be18b4Shenning 2147307f9faSjca if (daemonize) { 215b7041c07Sderaadt devnull = open(_PATH_DEVNULL, O_RDWR); 2167307f9faSjca if (devnull == -1) 217d8cc3220Skrw fatal("open(%s)", _PATH_DEVNULL); 2187307f9faSjca } 21948be18b4Shenning 220962cae8eSrzalamena if (interfaces == NULL || 221962cae8eSrzalamena register_interface(interfaces->name, got_one, 0) == NULL) 222986dbb4cSkrw fatalx("no interface given"); 223962cae8eSrzalamena 224e4b49721Srzalamena /* We need an address for running layer 3 mode. */ 225e4b49721Srzalamena if (drm == DRM_LAYER3 && 226699fe671Srzalamena (interfaces->hw_address.htype != HTYPE_IPSEC_TUNNEL && 227699fe671Srzalamena interfaces->primary_address.s_addr == 0)) 228986dbb4cSkrw fatalx("interface '%s' does not have an address", 229a29cd94bSrzalamena interfaces->name); 23048be18b4Shenning 23148be18b4Shenning /* We need at least one server. */ 23213be1508Shenning if (!sp) 233d8188208Shenning usage(); 23448be18b4Shenning 235b0e46094Sclaudio rdomain = get_rdomain(interfaces->name); 236b0e46094Sclaudio 2374be048dcSreyk /* Enable the relay agent option by default for enc0 */ 2384be048dcSreyk if (interfaces->hw_address.htype == HTYPE_IPSEC_TUNNEL) 2394be048dcSreyk oflag++; 2404be048dcSreyk 241c3d07ec0Sclaudio bzero(&laddr, sizeof laddr); 242c3d07ec0Sclaudio laddr.sin_len = sizeof laddr; 243c3d07ec0Sclaudio laddr.sin_family = AF_INET; 244c28564c4Srzalamena laddr.sin_port = htons(SERVER_PORT); 245c3d07ec0Sclaudio laddr.sin_addr.s_addr = interfaces->primary_address.s_addr; 24648be18b4Shenning /* Set up the server sockaddrs. */ 24748be18b4Shenning for (sp = servers; sp; sp = sp->next) { 248e4b49721Srzalamena if (sp->intf != NULL) 249e4b49721Srzalamena break; 250e4b49721Srzalamena 251f51f0ea7Srzalamena ss2sin(&sp->to)->sin_port = htons(SERVER_PORT); 252f51f0ea7Srzalamena ss2sin(&sp->to)->sin_family = AF_INET; 253f51f0ea7Srzalamena ss2sin(&sp->to)->sin_len = sizeof(struct sockaddr_in); 254c3d07ec0Sclaudio sp->fd = socket(AF_INET, SOCK_DGRAM, 0); 255c3d07ec0Sclaudio if (sp->fd == -1) 256d8cc3220Skrw fatal("socket"); 257c3d07ec0Sclaudio opt = 1; 258c3d07ec0Sclaudio if (setsockopt(sp->fd, SOL_SOCKET, SO_REUSEPORT, 259c3d07ec0Sclaudio &opt, sizeof(opt)) == -1) 260d8cc3220Skrw fatal("setsockopt"); 26113ae2b57Smikeb if (setsockopt(sp->fd, SOL_SOCKET, SO_RTABLE, &rdomain, 262b0e46094Sclaudio sizeof(rdomain)) == -1) 263d8cc3220Skrw fatal("setsockopt"); 2644971ba0eSkrw if (bind(sp->fd, (struct sockaddr *)&laddr, sizeof laddr) == 2654971ba0eSkrw -1) 266d8cc3220Skrw fatal("bind"); 267c3d07ec0Sclaudio if (connect(sp->fd, (struct sockaddr *)&sp->to, 268f51f0ea7Srzalamena sp->to.ss_len) == -1) 269d8cc3220Skrw fatal("connect"); 270c3d07ec0Sclaudio add_protocol("server", sp->fd, got_response, sp); 27148be18b4Shenning } 27248be18b4Shenning 273b0e46094Sclaudio /* Socket used to forward packets to the DHCP client */ 2744be048dcSreyk if (interfaces->hw_address.htype == HTYPE_IPSEC_TUNNEL) { 2754be048dcSreyk laddr.sin_addr.s_addr = INADDR_ANY; 2764be048dcSreyk server_fd = socket(AF_INET, SOCK_DGRAM, 0); 2774be048dcSreyk if (server_fd == -1) 278d8cc3220Skrw fatal("socket"); 2794be048dcSreyk opt = 1; 2804be048dcSreyk if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, 2814be048dcSreyk &opt, sizeof(opt)) == -1) 282d8cc3220Skrw fatal("setsockopt"); 28313ae2b57Smikeb if (setsockopt(server_fd, SOL_SOCKET, SO_RTABLE, &rdomain, 284b0e46094Sclaudio sizeof(rdomain)) == -1) 285d8cc3220Skrw fatal("setsockopt"); 2864be048dcSreyk if (bind(server_fd, (struct sockaddr *)&laddr, 2874be048dcSreyk sizeof(laddr)) == -1) 288d8cc3220Skrw fatal("bind"); 2894be048dcSreyk } 2904be048dcSreyk 291e4a93d04Shenning tzset(); 292e4a93d04Shenning 29348be18b4Shenning time(&cur_time); 294e4b49721Srzalamena if (drm == DRM_LAYER3) 29548be18b4Shenning bootp_packet_handler = relay; 296e4b49721Srzalamena else 297e4b49721Srzalamena bootp_packet_handler = l2relay; 29848be18b4Shenning 299a0ba4c4aShenning if ((pw = getpwnam("_dhcp")) == NULL) 300986dbb4cSkrw fatalx("user \"_dhcp\" not found"); 3018552a089Skrw if (chroot(pw->pw_dir) == -1) 302d8cc3220Skrw fatal("chroot"); 303a0ba4c4aShenning if (chdir("/") == -1) 304d8cc3220Skrw fatal("chdir(\"/\")"); 305a0ba4c4aShenning if (setgroups(1, &pw->pw_gid) || 3063e7173e3Sdjm setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 3073e7173e3Sdjm setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 308d8cc3220Skrw fatal("can't drop privileges"); 309a0ba4c4aShenning 3107307f9faSjca if (daemonize) { 3117307f9faSjca if (rdaemon(devnull) == -1) 312d8cc3220Skrw fatal("rdaemon"); 3132c3d0355Sreyk 3142c3d0355Sreyk log_init(0, LOG_DAEMON); /* stop logging to stderr */ 3157307f9faSjca } 3167307f9faSjca 317d3290b0cSreyk if (pledge("stdio route", NULL) == -1) 318986dbb4cSkrw fatalx("pledge"); 319d3290b0cSreyk 32048be18b4Shenning dispatch(); 32113be1508Shenning /* not reached */ 32248be18b4Shenning 32313be1508Shenning exit(0); 32448be18b4Shenning } 32548be18b4Shenning 32613be1508Shenning void 32713be1508Shenning relay(struct interface_info *ip, struct dhcp_packet *packet, int length, 328fa3d4f89Srzalamena struct packet_ctx *pc) 32948be18b4Shenning { 33048be18b4Shenning struct server_list *sp; 33148be18b4Shenning struct sockaddr_in to; 33248be18b4Shenning 33348be18b4Shenning if (packet->hlen > sizeof packet->chaddr) { 334986dbb4cSkrw log_info("Discarding packet with invalid hlen."); 33548be18b4Shenning return; 33648be18b4Shenning } 33748be18b4Shenning 33848be18b4Shenning /* If it's a bootreply, forward it to the client. */ 33948be18b4Shenning if (packet->op == BOOTREPLY) { 34012c372e5Srzalamena /* Filter packet that were not meant for us. */ 34112c372e5Srzalamena if (packet->giaddr.s_addr != 34212c372e5Srzalamena interfaces->primary_address.s_addr) 34312c372e5Srzalamena return; 34412c372e5Srzalamena 34513be1508Shenning bzero(&to, sizeof(to)); 34648be18b4Shenning if (!(packet->flags & htons(BOOTP_BROADCAST))) { 34748be18b4Shenning to.sin_addr = packet->yiaddr; 348c28564c4Srzalamena to.sin_port = htons(CLIENT_PORT); 34948be18b4Shenning } else { 35048be18b4Shenning to.sin_addr.s_addr = htonl(INADDR_BROADCAST); 351c28564c4Srzalamena to.sin_port = htons(CLIENT_PORT); 35248be18b4Shenning } 35348be18b4Shenning to.sin_family = AF_INET; 35448be18b4Shenning to.sin_len = sizeof to; 355fa3d4f89Srzalamena *ss2sin(&pc->pc_dst) = to; 35648be18b4Shenning 35799e009caSpatrick /* 35899e009caSpatrick * Set up the hardware destination address. If it's a reply 35999e009caSpatrick * with the BROADCAST flag set, we should send an L2 broad- 36099e009caSpatrick * cast as well. 36199e009caSpatrick */ 36299e009caSpatrick if (!(packet->flags & htons(BOOTP_BROADCAST))) { 363fa3d4f89Srzalamena pc->pc_hlen = packet->hlen; 364fa3d4f89Srzalamena if (pc->pc_hlen > CHADDR_SIZE) 365fa3d4f89Srzalamena pc->pc_hlen = CHADDR_SIZE; 366fa3d4f89Srzalamena memcpy(pc->pc_dmac, packet->chaddr, pc->pc_hlen); 367fa3d4f89Srzalamena pc->pc_htype = packet->htype; 36899e009caSpatrick } else { 369fa3d4f89Srzalamena memset(pc->pc_dmac, 0xff, sizeof(pc->pc_dmac)); 37099e009caSpatrick } 37148be18b4Shenning 3722936f4ebSrzalamena relay_agentinfo(pc, interfaces, packet->op); 373e4b49721Srzalamena if ((length = relay_agentinfo_remove(pc, packet, 374e4b49721Srzalamena length)) == -1) { 375986dbb4cSkrw log_info("ignoring BOOTREPLY with invalid " 3764be048dcSreyk "relay agent information"); 3774be048dcSreyk return; 3784be048dcSreyk } 3794be048dcSreyk 38066698dbcSdlg /* 38166698dbcSdlg * VMware PXE "ROMs" confuse the DHCP gateway address 38266698dbcSdlg * with the IP gateway address. This is a problem if your 38366698dbcSdlg * DHCP relay is running on something that's not your 38466698dbcSdlg * network gateway. 38566698dbcSdlg * 38666698dbcSdlg * It is purely informational from the relay to the client 38766698dbcSdlg * so we can safely clear it. 38866698dbcSdlg */ 38966698dbcSdlg packet->giaddr.s_addr = 0x0; 39066698dbcSdlg 391fa3d4f89Srzalamena ss2sin(&pc->pc_src)->sin_addr = interfaces->primary_address; 392fa3d4f89Srzalamena if (send_packet(interfaces, packet, length, pc) != -1) 393986dbb4cSkrw log_debug("forwarded BOOTREPLY for %s to %s", 39448be18b4Shenning print_hw_addr(packet->htype, packet->hlen, 39513be1508Shenning packet->chaddr), inet_ntoa(to.sin_addr)); 39648be18b4Shenning return; 39748be18b4Shenning } 39848be18b4Shenning 399c3d07ec0Sclaudio if (ip == NULL) { 400986dbb4cSkrw log_info("ignoring non BOOTREPLY from server"); 401c3d07ec0Sclaudio return; 402c3d07ec0Sclaudio } 403c3d07ec0Sclaudio 404b5f87b06Spatrick if (packet->hops > 16) { 405986dbb4cSkrw log_info("ignoring BOOTREQUEST with hop count of %d", 406b5f87b06Spatrick packet->hops); 40748be18b4Shenning return; 40848be18b4Shenning } 409b5f87b06Spatrick packet->hops++; 41048be18b4Shenning 411b5f87b06Spatrick /* 412b5f87b06Spatrick * Set the giaddr so the server can figure out what net it's 413b5f87b06Spatrick * from and so that we can later forward the response to the 414b5f87b06Spatrick * correct net. The RFC specifies that we have to keep the 415b5f87b06Spatrick * initial giaddr (in case we relay over multiple hops). 416b5f87b06Spatrick */ 417b5f87b06Spatrick if (!packet->giaddr.s_addr) 41848be18b4Shenning packet->giaddr = ip->primary_address; 41948be18b4Shenning 4202936f4ebSrzalamena relay_agentinfo(pc, interfaces, packet->op); 421e4b49721Srzalamena if ((length = relay_agentinfo_append(pc, packet, length)) == -1) { 422986dbb4cSkrw log_info("ignoring BOOTREQUEST with invalid " 4234be048dcSreyk "relay agent information"); 4244be048dcSreyk return; 4254be048dcSreyk } 4264be048dcSreyk 42748be18b4Shenning /* Otherwise, it's a BOOTREQUEST, so forward it to all the 42848be18b4Shenning servers. */ 42948be18b4Shenning for (sp = servers; sp; sp = sp->next) { 430c3d07ec0Sclaudio if (send(sp->fd, packet, length, 0) != -1) { 431986dbb4cSkrw log_debug("forwarded BOOTREQUEST for %s to %s", 43248be18b4Shenning print_hw_addr(packet->htype, packet->hlen, 433f51f0ea7Srzalamena packet->chaddr), 434f51f0ea7Srzalamena inet_ntoa(ss2sin(&sp->to)->sin_addr)); 43548be18b4Shenning } 43648be18b4Shenning } 43713be1508Shenning 43848be18b4Shenning } 43948be18b4Shenning 440d8188208Shenning void 441d8188208Shenning usage(void) 44248be18b4Shenning { 443d8188208Shenning extern char *__progname; 44448be18b4Shenning 445c4c35e14Srzalamena fprintf(stderr, "usage: %s [-dor] [-C circuit-id] [-R remote-id] " 446bf61b054Sjmc "-i interface\n\tdestination ...\n", 4470b52b90cSsobrado __progname); 448d8188208Shenning exit(1); 44948be18b4Shenning } 45048be18b4Shenning 4517307f9faSjca int 4527307f9faSjca rdaemon(int devnull) 4537307f9faSjca { 454ec8c1742Sjca if (devnull == -1) { 455ec8c1742Sjca errno = EBADF; 456ec8c1742Sjca return (-1); 457ec8c1742Sjca } 458ec8c1742Sjca if (fcntl(devnull, F_GETFL) == -1) 459ec8c1742Sjca return (-1); 4607307f9faSjca 4617307f9faSjca switch (fork()) { 4627307f9faSjca case -1: 4637307f9faSjca return (-1); 4647307f9faSjca case 0: 4657307f9faSjca break; 4667307f9faSjca default: 4677307f9faSjca _exit(0); 4687307f9faSjca } 4697307f9faSjca 4707307f9faSjca if (setsid() == -1) 4717307f9faSjca return (-1); 4727307f9faSjca 4737307f9faSjca (void)dup2(devnull, STDIN_FILENO); 4747307f9faSjca (void)dup2(devnull, STDOUT_FILENO); 4757307f9faSjca (void)dup2(devnull, STDERR_FILENO); 4767307f9faSjca if (devnull > 2) 4777307f9faSjca (void)close(devnull); 4787307f9faSjca 4797307f9faSjca return (0); 4807307f9faSjca } 4817307f9faSjca 48213be1508Shenning char * 48313be1508Shenning print_hw_addr(int htype, int hlen, unsigned char *data) 48448be18b4Shenning { 48548be18b4Shenning static char habuf[49]; 486fd395675Sderaadt char *s = habuf; 487fd395675Sderaadt int i, j, slen = sizeof(habuf); 48848be18b4Shenning 489fd395675Sderaadt if (htype == 0 || hlen == 0) { 49048be18b4Shenning bad: 49148be18b4Shenning strlcpy(habuf, "<null>", sizeof habuf); 49248be18b4Shenning return habuf; 493fd395675Sderaadt } 49448be18b4Shenning 495fd395675Sderaadt for (i = 0; i < hlen; i++) { 496fd395675Sderaadt j = snprintf(s, slen, "%02x", data[i]); 497fd395675Sderaadt if (j <= 0 || j >= slen) 498fd395675Sderaadt goto bad; 499fd395675Sderaadt j = strlen (s); 500fd395675Sderaadt s += j; 501fd395675Sderaadt slen -= (j + 1); 502fd395675Sderaadt *s++ = ':'; 503fd395675Sderaadt } 504fd395675Sderaadt *--s = '\0'; 505fd395675Sderaadt return habuf; 50648be18b4Shenning } 507c3d07ec0Sclaudio 508c3d07ec0Sclaudio void 509c3d07ec0Sclaudio got_response(struct protocol *l) 510c3d07ec0Sclaudio { 511fa3d4f89Srzalamena struct packet_ctx pc; 512c3d07ec0Sclaudio ssize_t result; 513c3d07ec0Sclaudio union { 514c3d07ec0Sclaudio /* 515c3d07ec0Sclaudio * Packet input buffer. Must be as large as largest 516c3d07ec0Sclaudio * possible MTU. 517c3d07ec0Sclaudio */ 518c3d07ec0Sclaudio unsigned char packbuf[4095]; 519c3d07ec0Sclaudio struct dhcp_packet packet; 520c3d07ec0Sclaudio } u; 521c3d07ec0Sclaudio struct server_list *sp = l->local; 522c3d07ec0Sclaudio 5234be048dcSreyk memset(&u, DHO_END, sizeof(u)); 524c3d07ec0Sclaudio if ((result = recv(l->fd, u.packbuf, sizeof(u), 0)) == -1 && 525c3d07ec0Sclaudio errno != ECONNREFUSED) { 526c3d07ec0Sclaudio /* 527abda2296Sbenno * Ignore ECONNREFUSED as too many dhcp servers send a bogus 528c3d07ec0Sclaudio * icmp unreach for every request. 529c3d07ec0Sclaudio */ 530f51f0ea7Srzalamena log_warn("recv failed for %s", 531f51f0ea7Srzalamena inet_ntoa(ss2sin(&sp->to)->sin_addr)); 532c3d07ec0Sclaudio return; 533c3d07ec0Sclaudio } 5349db3f500Sstevesk if (result == -1 && errno == ECONNREFUSED) 5359db3f500Sstevesk return; 5369db3f500Sstevesk 537c3d07ec0Sclaudio if (result == 0) 538c3d07ec0Sclaudio return; 539c3d07ec0Sclaudio 540c3d07ec0Sclaudio if (result < BOOTP_MIN_LEN) { 541986dbb4cSkrw log_info("Discarding packet with invalid size."); 542c3d07ec0Sclaudio return; 543c3d07ec0Sclaudio } 544c3d07ec0Sclaudio 545fa3d4f89Srzalamena memset(&pc, 0, sizeof(pc)); 546fa3d4f89Srzalamena pc.pc_src.ss_family = AF_INET; 547fa3d4f89Srzalamena pc.pc_src.ss_len = sizeof(struct sockaddr_in); 548f51f0ea7Srzalamena memcpy(&ss2sin(&pc.pc_src)->sin_addr, &ss2sin(&sp->to)->sin_addr, 549fa3d4f89Srzalamena sizeof(ss2sin(&pc.pc_src)->sin_addr)); 550c28564c4Srzalamena ss2sin(&pc.pc_src)->sin_port = htons(SERVER_PORT); 551c3d07ec0Sclaudio 552fa3d4f89Srzalamena pc.pc_dst.ss_family = AF_INET; 553fa3d4f89Srzalamena pc.pc_dst.ss_len = sizeof(struct sockaddr_in); 554c28564c4Srzalamena ss2sin(&pc.pc_dst)->sin_port = htons(CLIENT_PORT); 555fa3d4f89Srzalamena 556fa3d4f89Srzalamena if (bootp_packet_handler) 557fa3d4f89Srzalamena (*bootp_packet_handler)(NULL, &u.packet, result, &pc); 558c3d07ec0Sclaudio } 5594be048dcSreyk 560e4b49721Srzalamena void 5612936f4ebSrzalamena relay_agentinfo(struct packet_ctx *pc, struct interface_info *intf, 5622936f4ebSrzalamena int bootop) 5634be048dcSreyk { 564e4b49721Srzalamena static u_int8_t buf[8]; 5652936f4ebSrzalamena struct sockaddr_in *sin; 5664be048dcSreyk 567e4b49721Srzalamena if (oflag == 0) 568e4b49721Srzalamena return; 5694be048dcSreyk 570e4b49721Srzalamena if (rai_remote != NULL) { 571b2b4b45fSreyk pc->pc_remote = rai_remote; 572e4b49721Srzalamena pc->pc_remotelen = strlen(rai_remote); 573e4b49721Srzalamena } else 574e4b49721Srzalamena pc->pc_remotelen = 0; 5754be048dcSreyk 576e4b49721Srzalamena if (rai_circuit == NULL) { 577e4b49721Srzalamena buf[0] = (uint8_t)(intf->index << 8); 578e4b49721Srzalamena buf[1] = intf->index & 0xff; 579e4b49721Srzalamena pc->pc_circuit = buf; 580e4b49721Srzalamena pc->pc_circuitlen = 2; 5814be048dcSreyk 582e4b49721Srzalamena if (rai_remote == NULL) { 5832936f4ebSrzalamena if (bootop == BOOTREPLY) 5842936f4ebSrzalamena sin = ss2sin(&pc->pc_dst); 5852936f4ebSrzalamena else 5862936f4ebSrzalamena sin = ss2sin(&pc->pc_src); 5872936f4ebSrzalamena 588e4b49721Srzalamena pc->pc_remote = 5892936f4ebSrzalamena (uint8_t *)&sin->sin_addr; 590e4b49721Srzalamena pc->pc_remotelen = 5912936f4ebSrzalamena sizeof(sin->sin_addr); 592e4b49721Srzalamena } 593e4b49721Srzalamena } else { 594b2b4b45fSreyk pc->pc_circuit = rai_circuit; 595e4b49721Srzalamena pc->pc_circuitlen = strlen(rai_circuit); 596e4b49721Srzalamena } 5974be048dcSreyk } 5984be048dcSreyk 599e4b49721Srzalamena int 600e4b49721Srzalamena relay_agentinfo_cmp(struct packet_ctx *pc, uint8_t *p, int plen) 601e4b49721Srzalamena { 602e4b49721Srzalamena int len; 603e4b49721Srzalamena char buf[256]; 6044be048dcSreyk 605e4b49721Srzalamena if (oflag == 0) 606e4b49721Srzalamena return (-1); 607e4b49721Srzalamena 608e4b49721Srzalamena len = *(p + 1); 609e4b49721Srzalamena if (len > plen) 610e4b49721Srzalamena return (-1); 611e4b49721Srzalamena 612e4b49721Srzalamena switch (*p) { 613e4b49721Srzalamena case RAI_CIRCUIT_ID: 614e4b49721Srzalamena if (pc->pc_circuit == NULL) 615e4b49721Srzalamena return (-1); 616e4b49721Srzalamena if (pc->pc_circuitlen != len) 617e4b49721Srzalamena return (-1); 618e4b49721Srzalamena 619e4b49721Srzalamena memcpy(buf, p + DHCP_OPTION_HDR_LEN, len); 620e4b49721Srzalamena return (memcmp(pc->pc_circuit, buf, len)); 621e4b49721Srzalamena 622e4b49721Srzalamena case RAI_REMOTE_ID: 623e4b49721Srzalamena if (pc->pc_remote == NULL) 624e4b49721Srzalamena return (-1); 625e4b49721Srzalamena if (pc->pc_remotelen != len) 626e4b49721Srzalamena return (-1); 627e4b49721Srzalamena 628e4b49721Srzalamena memcpy(buf, p + DHCP_OPTION_HDR_LEN, len); 629e4b49721Srzalamena return (memcmp(pc->pc_remote, buf, len)); 630e4b49721Srzalamena 631e4b49721Srzalamena default: 632e4b49721Srzalamena /* Unmatched type */ 633986dbb4cSkrw log_info("unmatched relay info %d", *p); 634e4b49721Srzalamena return (0); 635e4b49721Srzalamena } 636e4b49721Srzalamena } 637e4b49721Srzalamena 638e4b49721Srzalamena ssize_t 639e4b49721Srzalamena relay_agentinfo_append(struct packet_ctx *pc, struct dhcp_packet *dp, 640e4b49721Srzalamena size_t dplen) 641e4b49721Srzalamena { 642e4b49721Srzalamena uint8_t *p, *startp; 643e4b49721Srzalamena ssize_t newtotal = dplen; 644e4b49721Srzalamena int opttotal, optlen, i, hasinfo = 0; 645e4b49721Srzalamena int maxlen, neededlen; 646e4b49721Srzalamena 647e4b49721Srzalamena /* Only append when enabled. */ 648e4b49721Srzalamena if (oflag == 0) 649e4b49721Srzalamena return (dplen); 650e4b49721Srzalamena 651e4b49721Srzalamena startp = (uint8_t *)dp; 652e4b49721Srzalamena p = (uint8_t *)&dp->options; 653e4b49721Srzalamena if (memcmp(p, DHCP_OPTIONS_COOKIE, DHCP_OPTIONS_COOKIE_LEN)) { 654986dbb4cSkrw log_info("invalid dhcp options cookie"); 6554be048dcSreyk return (-1); 6564be048dcSreyk } 657e4b49721Srzalamena 658e4b49721Srzalamena p += DHCP_OPTIONS_COOKIE_LEN; 659e4b49721Srzalamena opttotal = dplen - DHCP_FIXED_NON_UDP - DHCP_OPTIONS_COOKIE_LEN; 660e4b49721Srzalamena maxlen = DHCP_MTU_MAX - DHCP_FIXED_LEN - DHCP_OPTIONS_COOKIE_LEN - 1; 661e4b49721Srzalamena if (maxlen < 1 || opttotal < 1) 662e4b49721Srzalamena return (dplen); 663e4b49721Srzalamena 664e4b49721Srzalamena for (i = 0; i < opttotal && *p != DHO_END;) { 665e4b49721Srzalamena if (*p == DHO_PAD) 666e4b49721Srzalamena optlen = 1; 667e4b49721Srzalamena else 668e4b49721Srzalamena optlen = p[1] + DHCP_OPTION_HDR_LEN; 669e4b49721Srzalamena 670e4b49721Srzalamena if ((i + optlen) > opttotal) { 671986dbb4cSkrw log_info("truncated dhcp options"); 672e4b49721Srzalamena return (-1); 6734be048dcSreyk } 6744be048dcSreyk 675e4b49721Srzalamena if (*p == DHO_RELAY_AGENT_INFORMATION) { 676c4c35e14Srzalamena if (rai_replace) { 677c4c35e14Srzalamena memmove(p, p + optlen, opttotal - i); 678c4c35e14Srzalamena opttotal -= optlen; 679c4c35e14Srzalamena optlen = 0; 680c4c35e14Srzalamena } else 681e4b49721Srzalamena hasinfo = 1; 6824be048dcSreyk } 6834be048dcSreyk 684e4b49721Srzalamena p += optlen; 685e4b49721Srzalamena i += optlen; 6864be048dcSreyk 687e4b49721Srzalamena /* We reached the end, append the relay agent info. */ 688e4b49721Srzalamena if (i < opttotal && *p == DHO_END) { 689e4b49721Srzalamena /* We already have the Relay Agent Info, skip it. */ 690e4b49721Srzalamena if (hasinfo) 691e4b49721Srzalamena continue; 692e4b49721Srzalamena 693e4b49721Srzalamena /* Calculate needed length to append new data. */ 694e4b49721Srzalamena neededlen = newtotal + DHCP_OPTION_HDR_LEN; 695e4b49721Srzalamena if (pc->pc_circuitlen > 0) 696e4b49721Srzalamena neededlen += DHCP_OPTION_HDR_LEN + 697e4b49721Srzalamena pc->pc_circuitlen; 698e4b49721Srzalamena if (pc->pc_remotelen > 0) 699e4b49721Srzalamena neededlen += DHCP_OPTION_HDR_LEN + 700e4b49721Srzalamena pc->pc_remotelen; 701e4b49721Srzalamena 702e4b49721Srzalamena /* Save one byte for DHO_END. */ 703e4b49721Srzalamena neededlen += 1; 704e4b49721Srzalamena 7054971ba0eSkrw /* Check if we have space for the new options. */ 706e4b49721Srzalamena if (neededlen > maxlen) { 707986dbb4cSkrw log_warnx("no space for relay agent info"); 708e4b49721Srzalamena return (newtotal); 709e4b49721Srzalamena } 710e4b49721Srzalamena 711e4b49721Srzalamena /* New option header: 2 bytes. */ 712e4b49721Srzalamena newtotal += DHCP_OPTION_HDR_LEN; 713e4b49721Srzalamena 714e4b49721Srzalamena *p++ = DHO_RELAY_AGENT_INFORMATION; 715e4b49721Srzalamena *p = 0; 716e4b49721Srzalamena if (pc->pc_circuitlen > 0) { 717e4b49721Srzalamena newtotal += DHCP_OPTION_HDR_LEN + 718e4b49721Srzalamena pc->pc_circuitlen; 719e4b49721Srzalamena *p = (*p) + DHCP_OPTION_HDR_LEN + 720e4b49721Srzalamena pc->pc_circuitlen; 721e4b49721Srzalamena } 722e4b49721Srzalamena 723e4b49721Srzalamena if (pc->pc_remotelen > 0) { 724e4b49721Srzalamena newtotal += DHCP_OPTION_HDR_LEN + 725e4b49721Srzalamena pc->pc_remotelen; 726e4b49721Srzalamena *p = (*p) + DHCP_OPTION_HDR_LEN + 727e4b49721Srzalamena pc->pc_remotelen; 728e4b49721Srzalamena } 729e4b49721Srzalamena 730e4b49721Srzalamena p++; 731e4b49721Srzalamena 732e4b49721Srzalamena /* Sub-option circuit-id header plus value. */ 733e4b49721Srzalamena if (pc->pc_circuitlen > 0) { 734e4b49721Srzalamena *p++ = RAI_CIRCUIT_ID; 735e4b49721Srzalamena *p++ = pc->pc_circuitlen; 736e4b49721Srzalamena memcpy(p, pc->pc_circuit, pc->pc_circuitlen); 737e4b49721Srzalamena 738e4b49721Srzalamena p += pc->pc_circuitlen; 739e4b49721Srzalamena } 740e4b49721Srzalamena 741e4b49721Srzalamena /* Sub-option remote-id header plus value. */ 742e4b49721Srzalamena if (pc->pc_remotelen > 0) { 743e4b49721Srzalamena *p++ = RAI_REMOTE_ID; 744e4b49721Srzalamena *p++ = pc->pc_remotelen; 745e4b49721Srzalamena memcpy(p, pc->pc_remote, pc->pc_remotelen); 746e4b49721Srzalamena 747e4b49721Srzalamena p += pc->pc_remotelen; 748e4b49721Srzalamena } 7494be048dcSreyk 7504be048dcSreyk *p = DHO_END; 7514be048dcSreyk } 7524be048dcSreyk } 7534be048dcSreyk 754e4b49721Srzalamena /* Zero the padding so we don't leak anything. */ 755e4b49721Srzalamena p++; 756e4b49721Srzalamena if (p < (startp + maxlen)) 757e4b49721Srzalamena memset(p, 0, (startp + maxlen) - p); 758e4b49721Srzalamena 759e4b49721Srzalamena return (newtotal); 760e4b49721Srzalamena } 761e4b49721Srzalamena 762e4b49721Srzalamena ssize_t 763e4b49721Srzalamena relay_agentinfo_remove(struct packet_ctx *pc, struct dhcp_packet *dp, 764e4b49721Srzalamena size_t dplen) 765e4b49721Srzalamena { 766e4b49721Srzalamena uint8_t *p, *np, *startp, *endp; 767e4b49721Srzalamena int opttotal, optleft; 768e4b49721Srzalamena int suboptlen, optlen, i; 769e4b49721Srzalamena int maxlen, remaining, matched = 0; 770e4b49721Srzalamena 771e4b49721Srzalamena startp = (uint8_t *)dp; 772e4b49721Srzalamena p = (uint8_t *)&dp->options; 773e4b49721Srzalamena if (memcmp(p, DHCP_OPTIONS_COOKIE, DHCP_OPTIONS_COOKIE_LEN)) { 774986dbb4cSkrw log_info("invalid dhcp options cookie"); 775e4b49721Srzalamena return (-1); 776e4b49721Srzalamena } 777e4b49721Srzalamena 778e4b49721Srzalamena maxlen = DHCP_MTU_MAX - DHCP_FIXED_LEN - DHCP_OPTIONS_COOKIE_LEN - 1; 779e4b49721Srzalamena opttotal = dplen - DHCP_FIXED_NON_UDP - DHCP_OPTIONS_COOKIE_LEN; 780e4b49721Srzalamena optleft = opttotal; 781e4b49721Srzalamena 782e4b49721Srzalamena p += DHCP_OPTIONS_COOKIE_LEN; 783e4b49721Srzalamena endp = p + opttotal; 784e4b49721Srzalamena 785e4b49721Srzalamena for (i = 0; i < opttotal && *p != DHO_END;) { 786e4b49721Srzalamena if (*p == DHO_PAD) 787e4b49721Srzalamena optlen = 1; 788e4b49721Srzalamena else 789e4b49721Srzalamena optlen = p[1] + DHCP_OPTION_HDR_LEN; 790e4b49721Srzalamena 791e4b49721Srzalamena if ((i + optlen) > opttotal) { 792986dbb4cSkrw log_info("truncated dhcp options"); 793e4b49721Srzalamena return (-1); 794e4b49721Srzalamena } 795e4b49721Srzalamena 796e4b49721Srzalamena if (*p == DHO_RELAY_AGENT_INFORMATION) { 797e4b49721Srzalamena /* Fast case: there is no next option. */ 798e4b49721Srzalamena np = p + optlen; 799e4b49721Srzalamena if (*np == DHO_END) { 800e4b49721Srzalamena *p = *np; 801e4b49721Srzalamena endp = p + 1; 802e4b49721Srzalamena /* Zero the padding so we don't leak data. */ 803e4b49721Srzalamena if (endp < (startp + maxlen)) 804e4b49721Srzalamena memset(endp, 0, 805e4b49721Srzalamena (startp + maxlen) - endp); 806e4b49721Srzalamena 807e4b49721Srzalamena return (dplen); 808e4b49721Srzalamena } 809e4b49721Srzalamena 810e4b49721Srzalamena remaining = optlen; 811e4b49721Srzalamena while (remaining > 0) { 812e4b49721Srzalamena suboptlen = *(p + 1); 813e4b49721Srzalamena remaining -= DHCP_OPTION_HDR_LEN + suboptlen; 814e4b49721Srzalamena 815e4b49721Srzalamena matched = 1; 816e4b49721Srzalamena if (relay_agentinfo_cmp(pc, p, suboptlen) == 0) 817e4b49721Srzalamena continue; 818e4b49721Srzalamena 819e4b49721Srzalamena matched = 0; 820e4b49721Srzalamena break; 821e4b49721Srzalamena } 822e4b49721Srzalamena /* It is not ours Relay Agent Info, don't remove it. */ 823e4b49721Srzalamena if (matched == 0) 824e4b49721Srzalamena break; 825e4b49721Srzalamena 826e4b49721Srzalamena /* Move the other options on top of this one. */ 827e4b49721Srzalamena optleft -= optlen; 828e4b49721Srzalamena endp -= optlen; 829e4b49721Srzalamena 830e4b49721Srzalamena /* Replace the old agent relay info. */ 831e4b49721Srzalamena memmove(p, dp, optleft); 832e4b49721Srzalamena 833e4b49721Srzalamena endp++; 834e4b49721Srzalamena /* Zero the padding so we don't leak data. */ 835e4b49721Srzalamena if (endp < (startp + maxlen)) 836e4b49721Srzalamena memset(endp, 0, 837e4b49721Srzalamena (startp + maxlen) - endp); 838e4b49721Srzalamena 839e4b49721Srzalamena return (endp - startp); 840e4b49721Srzalamena } 841e4b49721Srzalamena 842e4b49721Srzalamena p += optlen; 843e4b49721Srzalamena i += optlen; 844e4b49721Srzalamena optleft -= optlen; 845e4b49721Srzalamena } 846e4b49721Srzalamena 847e4b49721Srzalamena return (endp - startp); 8484be048dcSreyk } 849b0e46094Sclaudio 850b0e46094Sclaudio int 851b0e46094Sclaudio get_rdomain(char *name) 852b0e46094Sclaudio { 853b0e46094Sclaudio int rv = 0, s; 854b0e46094Sclaudio struct ifreq ifr; 855b0e46094Sclaudio 856b0e46094Sclaudio if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 857d8cc3220Skrw fatal("get_rdomain socket"); 858b0e46094Sclaudio 859b0e46094Sclaudio bzero(&ifr, sizeof(ifr)); 860b0e46094Sclaudio strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); 8618bb39f08Sguenther if (ioctl(s, SIOCGIFRDOMAIN, (caddr_t)&ifr) != -1) 862b0e46094Sclaudio rv = ifr.ifr_rdomainid; 863b0e46094Sclaudio 864b0e46094Sclaudio close(s); 865b0e46094Sclaudio return rv; 866b0e46094Sclaudio } 867e4b49721Srzalamena 868e4b49721Srzalamena void 869e4b49721Srzalamena l2relay(struct interface_info *ip, struct dhcp_packet *dp, int length, 870e4b49721Srzalamena struct packet_ctx *pc) 871e4b49721Srzalamena { 872e4b49721Srzalamena struct server_list *sp; 873e4b49721Srzalamena ssize_t dplen; 874e4b49721Srzalamena 875e4b49721Srzalamena if (dp->hlen > sizeof(dp->chaddr)) { 876986dbb4cSkrw log_info("Discarding packet with invalid hlen."); 877e4b49721Srzalamena return; 878e4b49721Srzalamena } 879e4b49721Srzalamena 8802936f4ebSrzalamena relay_agentinfo(pc, ip, dp->op); 881e4b49721Srzalamena 882e4b49721Srzalamena switch (dp->op) { 883e4b49721Srzalamena case BOOTREQUEST: 884e4b49721Srzalamena /* Add the relay agent info asked by the user. */ 885e4b49721Srzalamena if ((dplen = relay_agentinfo_append(pc, dp, length)) == -1) 886e4b49721Srzalamena return; 887e4b49721Srzalamena 888e4b49721Srzalamena /* 889e4b49721Srzalamena * Re-send the packet to every interface except the one 890e4b49721Srzalamena * it came in. 891e4b49721Srzalamena */ 892e4b49721Srzalamena for (sp = servers; sp != NULL; sp = sp->next) { 893e4b49721Srzalamena if (sp->intf == ip) 894e4b49721Srzalamena continue; 895e4b49721Srzalamena 896986dbb4cSkrw log_debug("forwarded BOOTREQUEST for %s to %s", 897e4b49721Srzalamena print_hw_addr(pc->pc_htype, pc->pc_hlen, 898e4b49721Srzalamena pc->pc_smac), sp->intf->name); 899e4b49721Srzalamena 900e4b49721Srzalamena send_packet(sp->intf, dp, dplen, pc); 901e4b49721Srzalamena } 902e4b49721Srzalamena if (ip != interfaces) { 903986dbb4cSkrw log_debug("forwarded BOOTREQUEST for %s to %s", 904e4b49721Srzalamena print_hw_addr(pc->pc_htype, pc->pc_hlen, 905e4b49721Srzalamena pc->pc_smac), interfaces->name); 906e4b49721Srzalamena 907e4b49721Srzalamena send_packet(interfaces, dp, dplen, pc); 908e4b49721Srzalamena } 909e4b49721Srzalamena break; 910e4b49721Srzalamena 911e4b49721Srzalamena case BOOTREPLY: 912e4b49721Srzalamena /* Remove relay agent info on offer. */ 913e4b49721Srzalamena if ((dplen = relay_agentinfo_remove(pc, dp, length)) == -1) 914e4b49721Srzalamena return; 915e4b49721Srzalamena 916e4b49721Srzalamena if (ip != interfaces) { 917986dbb4cSkrw log_debug("forwarded BOOTREPLY for %s to %s", 918e4b49721Srzalamena print_hw_addr(pc->pc_htype, pc->pc_hlen, 919e4b49721Srzalamena pc->pc_dmac), interfaces->name); 920e4b49721Srzalamena send_packet(interfaces, dp, dplen, pc); 921e4b49721Srzalamena } 922e4b49721Srzalamena break; 923e4b49721Srzalamena 924e4b49721Srzalamena default: 925986dbb4cSkrw log_debug("invalid operation type '%d'", dp->op); 926e4b49721Srzalamena return; 927e4b49721Srzalamena } 928e4b49721Srzalamena } 929