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 }