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 }