1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1996-2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <stdarg.h> 32 #include <sys/types.h> 33 #include <assert.h> 34 #include <sys/socket.h> 35 #include <sys/sockio.h> 36 #include <net/if.h> 37 #include <errno.h> 38 #include <netdb.h> 39 #include <string.h> 40 #include <sys/syslog.h> 41 #include <netinet/in.h> 42 #include <arpa/inet.h> 43 #include <netinet/dhcp.h> 44 #include "dhcpd.h" 45 #include "per_dnet.h" 46 #include "interfaces.h" 47 #include <locale.h> 48 49 #define MAX_RELAY_IP 5 /* Maximum of destinations */ 50 51 static struct in_addr relay_ip[MAX_RELAY_IP]; /* IPs of targets */ 52 static struct in_addr relay_net[MAX_RELAY_IP]; /* target nets */ 53 static int relay_reply(IF *, PKT_LIST *); 54 static int relay_request(IF *, PKT_LIST *); 55 56 /* 57 * This file contains the code which implements the BOOTP relay agent. 58 */ 59 60 /* 61 * Parse arguments. If an agument begins with a digit, then it's 62 * an IP address, otherwise it's a hostname which needs to be 63 * resolved into an IP address. 64 * 65 * Use the arguments to fill in relay_ip array. 66 * 67 * Only callable by main thread. MT UNSAFE 68 */ 69 int 70 relay_agent_init(char *args) 71 { 72 int i; 73 struct in_addr *ip; 74 struct hostent *hp; 75 char ntoab[INET_ADDRSTRLEN]; 76 77 for (i = 0; i <= MAX_RELAY_IP; i++) { 78 if ((args = strtok(args, ",")) == NULL) 79 break; /* done */ 80 81 /* 82 * If there's more than MAX_RELAY_IP addresses 83 * specified that's an error. If we can't 84 * resolve the host name, that's an error. 85 */ 86 if (i == MAX_RELAY_IP) { 87 (void) fprintf(stderr, 88 gettext("Too many relay agent destinations.\n")); 89 return (E2BIG); 90 } 91 92 if ((hp = gethostbyname(args)) == NULL) { 93 (void) fprintf(stderr, gettext( 94 "Invalid relay agent destination name: %s\n"), 95 args); 96 return (EINVAL); 97 } 98 /* LINTED [will be lw aligned] */ 99 ip = (struct in_addr *)hp->h_addr; 100 101 /* 102 * Note: no way to guess at destination subnet mask, 103 * and verify that it's not a new broadcast addr. 104 */ 105 if (ip->s_addr == INADDR_ANY || 106 ip->s_addr == INADDR_LOOPBACK || 107 ip->s_addr == INADDR_BROADCAST) { 108 (void) fprintf(stderr, gettext("Relay destination \ 109 cannot be 0, loopback, or broadcast address.\n")); 110 return (EINVAL); 111 } 112 113 relay_ip[i].s_addr = ip->s_addr; 114 115 ip = &relay_ip[i]; 116 get_netmask(ip, &relay_net[i]); 117 relay_net[i].s_addr &= ip->s_addr; 118 if (verbose) { 119 (void) fprintf(stdout, 120 gettext("Relay destination: %s (%s)"), 121 inet_ntop(AF_INET, &relay_ip[i], 122 ntoab, sizeof (ntoab)), args); 123 (void) fprintf(stdout, gettext("\t\tnetwork: %s\n"), 124 inet_ntop(AF_INET, &relay_net[i], 125 ntoab, sizeof (ntoab))); 126 } 127 args = NULL; /* for next call to strtok() */ 128 } 129 if (i == 0) { 130 /* 131 * Gotta specify at least one IP addr. 132 */ 133 (void) fprintf(stderr, 134 gettext("Specify at least one relay agent destination.\n")); 135 return (ENOENT); 136 } 137 if (i < MAX_RELAY_IP) 138 relay_ip[i].s_addr = NULL; /* terminate the list */ 139 140 return (0); 141 } 142 143 /* 144 * Note: if_head_mtx must be held by caller, as the interface list is 145 * walked in relay_reply. 146 * 147 * MT SAFE 148 */ 149 int 150 relay_agent(IF *ifp, PKT_LIST *plp) 151 { 152 if (plp->pkt->op == BOOTREQUEST) 153 return (relay_request(ifp, plp)); 154 155 return (relay_reply(ifp, plp)); 156 } 157 158 /* 159 * MT SAFE 160 */ 161 static int 162 relay_request(IF *ifp, PKT_LIST *plp) 163 { 164 PKT *pkp; 165 struct sockaddr_in to; 166 struct in_addr ifnet, any; 167 int i; 168 char ntoab[INET_ADDRSTRLEN]; 169 char buf[DN_MAX_CID_LEN], *msg; 170 171 pkp = plp->pkt; 172 if (pkp->giaddr.s_addr == 0L) 173 pkp->giaddr.s_addr = ifp->addr.s_addr; 174 pkp->hops++; 175 176 /* 177 * Send it on to the next relay(s)/servers 178 */ 179 to.sin_port = htons(IPPORT_BOOTPS + port_offset); 180 any.s_addr = htonl(INADDR_ANY); 181 (void) disp_cid(plp, buf, sizeof (buf)); 182 183 for (i = 0; i < MAX_RELAY_IP; i++) { 184 if (relay_ip[i].s_addr == 0L) 185 break; /* we're done */ 186 187 ifnet.s_addr = ifp->addr.s_addr & ifp->mask.s_addr; 188 if (relay_net[i].s_addr == ifnet.s_addr) { 189 if (verbose) { 190 dhcpmsg(LOG_INFO, "Target's network: \ 191 %1$s is the same as client %2$s network, ignored.\n", 192 inet_ntop(AF_INET, &relay_net[i], 193 ntoab, sizeof (ntoab)), 194 buf); 195 } 196 continue; /* skip this target */ 197 } 198 199 to.sin_addr.s_addr = relay_ip[i].s_addr; 200 201 if (to.sin_port == htons(IPPORT_BOOTPS + port_offset)) 202 msg = "Relaying request %1$s to %2$s, server port.\n"; 203 else 204 msg = "Relaying request %1$s to %2$s, client port.\n"; 205 206 if (debug) { 207 dhcpmsg(LOG_INFO, msg, buf, 208 inet_ntop(AF_INET, &to.sin_addr, 209 ntoab, sizeof (ntoab))); 210 } 211 212 if (write_interface(ifp, pkp, plp->len, &to)) { 213 dhcpmsg(LOG_INFO, "Cannot relay request %1$s to %2$s\n", 214 buf, inet_ntop(AF_INET, &to.sin_addr, 215 ntoab, sizeof (ntoab))); 216 } else { 217 logtrans(P_BOOTP, L_RELAY_REQ, 0, any, to.sin_addr, 218 plp); 219 } 220 } 221 return (0); 222 } 223 224 /* 225 * Note: if_head_mtx must be held by caller, as the interface list is 226 * walked here. 227 */ 228 static int 229 relay_reply(IF *ifp, PKT_LIST *plp) 230 { 231 int err; 232 IF *tifp; 233 PKT *pkp = plp->pkt; 234 struct in_addr to; 235 char buf[DHCP_MAX_OPT_SIZE]; 236 char ntoab_a[INET_ADDRSTRLEN], ntoab_b[INET_ADDRSTRLEN]; 237 238 assert(MUTEX_HELD(&if_head_mtx)); 239 240 if (pkp->giaddr.s_addr == 0L) { 241 /* 242 * Somehow we picked up a reply packet from a DHCP server 243 * on this net intended for a client on this net. Drop it. 244 */ 245 if (verbose) { 246 dhcpmsg(LOG_INFO, 247 "Reply packet without giaddr set ignored.\n"); 248 } 249 return (0); 250 } 251 252 /* 253 * We can assume that the message came directly from a dhcp/bootp 254 * server to us, and we are to address it directly to the client. 255 */ 256 if (pkp->giaddr.s_addr != ifp->addr.s_addr) { 257 /* 258 * It is possible that this is a multihomed host. We'll 259 * check to see if this is the case, and handle it 260 * appropriately. 261 */ 262 for (tifp = if_head; tifp != NULL; tifp = tifp->next) { 263 if (tifp->addr.s_addr == pkp->giaddr.s_addr) 264 break; 265 } 266 267 if (tifp == NULL) { 268 if (verbose) { 269 dhcpmsg(LOG_INFO, "Received relayed reply \ 270 not intended for this interface: %1$s giaddr: %2$s\n", 271 inet_ntop(AF_INET, &ifp->addr, 272 ntoab_a, sizeof (ntoab_a)), 273 inet_ntop(AF_INET, &pkp->giaddr, 274 ntoab_b, sizeof (ntoab_b))); 275 } 276 return (0); 277 } else 278 ifp = tifp; 279 } 280 281 (void) disp_cid(plp, buf, sizeof (buf)); 282 pkp->hops++; 283 284 if (debug) 285 dhcpmsg(LOG_INFO, "Relaying reply to client %s\n", buf); 286 287 if ((ntohs(pkp->flags) & BCAST_MASK) == 0) { 288 if (pkp->yiaddr.s_addr == htonl(INADDR_ANY)) { 289 if (pkp->ciaddr.s_addr == htonl(INADDR_ANY)) { 290 dhcpmsg(LOG_INFO, "No destination IP \ 291 address or network IP address; cannot send reply to client: %s.\n", buf); 292 return (0); 293 } 294 to.s_addr = pkp->ciaddr.s_addr; 295 } else 296 to.s_addr = pkp->yiaddr.s_addr; 297 } 298 299 if ((err = send_reply(ifp, pkp, plp->len, &to)) == 0) 300 logtrans(P_BOOTP, L_RELAY_REP, 0, ifp->addr, to, plp); 301 302 return (err); 303 }