diff options
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/relay.c')
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/relay.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/relay.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/relay.c new file mode 100644 index 0000000000..33f5ff620a --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/relay.c @@ -0,0 +1,303 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1996-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <sys/types.h> +#include <assert.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <errno.h> +#include <netdb.h> +#include <string.h> +#include <sys/syslog.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/dhcp.h> +#include "dhcpd.h" +#include "per_dnet.h" +#include "interfaces.h" +#include <locale.h> + +#define MAX_RELAY_IP 5 /* Maximum of destinations */ + +static struct in_addr relay_ip[MAX_RELAY_IP]; /* IPs of targets */ +static struct in_addr relay_net[MAX_RELAY_IP]; /* target nets */ +static int relay_reply(IF *, PKT_LIST *); +static int relay_request(IF *, PKT_LIST *); + +/* + * This file contains the code which implements the BOOTP relay agent. + */ + +/* + * Parse arguments. If an agument begins with a digit, then it's + * an IP address, otherwise it's a hostname which needs to be + * resolved into an IP address. + * + * Use the arguments to fill in relay_ip array. + * + * Only callable by main thread. MT UNSAFE + */ +int +relay_agent_init(char *args) +{ + int i; + struct in_addr *ip; + struct hostent *hp; + char ntoab[INET_ADDRSTRLEN]; + + for (i = 0; i <= MAX_RELAY_IP; i++) { + if ((args = strtok(args, ",")) == NULL) + break; /* done */ + + /* + * If there's more than MAX_RELAY_IP addresses + * specified that's an error. If we can't + * resolve the host name, that's an error. + */ + if (i == MAX_RELAY_IP) { + (void) fprintf(stderr, + gettext("Too many relay agent destinations.\n")); + return (E2BIG); + } + + if ((hp = gethostbyname(args)) == NULL) { + (void) fprintf(stderr, gettext( + "Invalid relay agent destination name: %s\n"), + args); + return (EINVAL); + } + /* LINTED [will be lw aligned] */ + ip = (struct in_addr *)hp->h_addr; + + /* + * Note: no way to guess at destination subnet mask, + * and verify that it's not a new broadcast addr. + */ + if (ip->s_addr == INADDR_ANY || + ip->s_addr == INADDR_LOOPBACK || + ip->s_addr == INADDR_BROADCAST) { + (void) fprintf(stderr, gettext("Relay destination \ +cannot be 0, loopback, or broadcast address.\n")); + return (EINVAL); + } + + relay_ip[i].s_addr = ip->s_addr; + + ip = &relay_ip[i]; + get_netmask(ip, &relay_net[i]); + relay_net[i].s_addr &= ip->s_addr; + if (verbose) { + (void) fprintf(stdout, + gettext("Relay destination: %s (%s)"), + inet_ntop(AF_INET, &relay_ip[i], + ntoab, sizeof (ntoab)), args); + (void) fprintf(stdout, gettext("\t\tnetwork: %s\n"), + inet_ntop(AF_INET, &relay_net[i], + ntoab, sizeof (ntoab))); + } + args = NULL; /* for next call to strtok() */ + } + if (i == 0) { + /* + * Gotta specify at least one IP addr. + */ + (void) fprintf(stderr, + gettext("Specify at least one relay agent destination.\n")); + return (ENOENT); + } + if (i < MAX_RELAY_IP) + relay_ip[i].s_addr = NULL; /* terminate the list */ + + return (0); +} + +/* + * Note: if_head_mtx must be held by caller, as the interface list is + * walked in relay_reply. + * + * MT SAFE + */ +int +relay_agent(IF *ifp, PKT_LIST *plp) +{ + if (plp->pkt->op == BOOTREQUEST) + return (relay_request(ifp, plp)); + + return (relay_reply(ifp, plp)); +} + +/* + * MT SAFE + */ +static int +relay_request(IF *ifp, PKT_LIST *plp) +{ + PKT *pkp; + struct sockaddr_in to; + struct in_addr ifnet, any; + int i; + char ntoab[INET_ADDRSTRLEN]; + char buf[DN_MAX_CID_LEN], *msg; + + pkp = plp->pkt; + if (pkp->giaddr.s_addr == 0L) + pkp->giaddr.s_addr = ifp->addr.s_addr; + pkp->hops++; + + /* + * Send it on to the next relay(s)/servers + */ + to.sin_port = htons(IPPORT_BOOTPS + port_offset); + any.s_addr = htonl(INADDR_ANY); + (void) disp_cid(plp, buf, sizeof (buf)); + + for (i = 0; i < MAX_RELAY_IP; i++) { + if (relay_ip[i].s_addr == 0L) + break; /* we're done */ + + ifnet.s_addr = ifp->addr.s_addr & ifp->mask.s_addr; + if (relay_net[i].s_addr == ifnet.s_addr) { + if (verbose) { + dhcpmsg(LOG_INFO, "Target's network: \ +%1$s is the same as client %2$s network, ignored.\n", + inet_ntop(AF_INET, &relay_net[i], + ntoab, sizeof (ntoab)), + buf); + } + continue; /* skip this target */ + } + + to.sin_addr.s_addr = relay_ip[i].s_addr; + + if (to.sin_port == htons(IPPORT_BOOTPS + port_offset)) + msg = "Relaying request %1$s to %2$s, server port.\n"; + else + msg = "Relaying request %1$s to %2$s, client port.\n"; + + if (debug) { + dhcpmsg(LOG_INFO, msg, buf, + inet_ntop(AF_INET, &to.sin_addr, + ntoab, sizeof (ntoab))); + } + + if (write_interface(ifp, pkp, plp->len, &to)) { + dhcpmsg(LOG_INFO, "Cannot relay request %1$s to %2$s\n", + buf, inet_ntop(AF_INET, &to.sin_addr, + ntoab, sizeof (ntoab))); + } else { + logtrans(P_BOOTP, L_RELAY_REQ, 0, any, to.sin_addr, + plp); + } + } + return (0); +} + +/* + * Note: if_head_mtx must be held by caller, as the interface list is + * walked here. + */ +static int +relay_reply(IF *ifp, PKT_LIST *plp) +{ + int err; + IF *tifp; + PKT *pkp = plp->pkt; + struct in_addr to; + char buf[DHCP_MAX_OPT_SIZE]; + char ntoab_a[INET_ADDRSTRLEN], ntoab_b[INET_ADDRSTRLEN]; + + assert(MUTEX_HELD(&if_head_mtx)); + + if (pkp->giaddr.s_addr == 0L) { + /* + * Somehow we picked up a reply packet from a DHCP server + * on this net intended for a client on this net. Drop it. + */ + if (verbose) { + dhcpmsg(LOG_INFO, + "Reply packet without giaddr set ignored.\n"); + } + return (0); + } + + /* + * We can assume that the message came directly from a dhcp/bootp + * server to us, and we are to address it directly to the client. + */ + if (pkp->giaddr.s_addr != ifp->addr.s_addr) { + /* + * It is possible that this is a multihomed host. We'll + * check to see if this is the case, and handle it + * appropriately. + */ + for (tifp = if_head; tifp != NULL; tifp = tifp->next) { + if (tifp->addr.s_addr == pkp->giaddr.s_addr) + break; + } + + if (tifp == NULL) { + if (verbose) { + dhcpmsg(LOG_INFO, "Received relayed reply \ +not intended for this interface: %1$s giaddr: %2$s\n", + inet_ntop(AF_INET, &ifp->addr, + ntoab_a, sizeof (ntoab_a)), + inet_ntop(AF_INET, &pkp->giaddr, + ntoab_b, sizeof (ntoab_b))); + } + return (0); + } else + ifp = tifp; + } + + (void) disp_cid(plp, buf, sizeof (buf)); + pkp->hops++; + + if (debug) + dhcpmsg(LOG_INFO, "Relaying reply to client %s\n", buf); + + if ((ntohs(pkp->flags) & BCAST_MASK) == 0) { + if (pkp->yiaddr.s_addr == htonl(INADDR_ANY)) { + if (pkp->ciaddr.s_addr == htonl(INADDR_ANY)) { + dhcpmsg(LOG_INFO, "No destination IP \ +address or network IP address; cannot send reply to client: %s.\n", buf); + return (0); + } + to.s_addr = pkp->ciaddr.s_addr; + } else + to.s_addr = pkp->yiaddr.s_addr; + } + + if ((err = send_reply(ifp, pkp, plp->len, &to)) == 0) + logtrans(P_BOOTP, L_RELAY_REP, 0, ifp->addr, to, plp); + + return (err); +} |