interfaces.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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
* 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 1993-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 <unistd.h>
#include <stdarg.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <netinet/in_systm.h>
#include <netinet/if_ether.h>
#include <stropts.h>
#include <stdio.h>
#include <ctype.h>
#include <syslog.h>
#include <dhcp_symbol.h>
#include "dhcpd.h"
#include "per_dnet.h"
#include "interfaces.h"
#include <v4_sum_impl.h>
#include <locale.h>
/*
* Network interface configuration. This file contains routines which
* are handled by identifying explicitly each interface, and creating a
* stream for each. If only one usable interface exists, then a "normal"
* UDP socket is used for simplicity's sake.
*/
char *interfaces; /* user specified interfaces */
static int num_interfaces; /* # of usable interfaces on the system */
static char *
{
char *rp;
switch (stype) {
case DSRVR_LBCAST:
rp = "limited broadcast";
break;
case DSRVR_DBCAST:
rp = "directed broadcast";
break;
case DSRVR_UCAST:
rp = "unicast";
break;
}
return (rp);
}
/*
* Given two packets, match them based on BOOTP header operation, packet len,
* hardware type, flags, ciaddr, DHCP type, client id, or chaddr.
* Returns B_TRUE if they match, B_FALSE otherwise.
*/
static boolean_t
{
DHCP_OPT *a, *b;
return (B_FALSE); /* not even the same BOOTP type. */
#ifdef DEBUG
"%04d match_plp: giaddr mismatch on 0x%x, 0x%x\n",
thr_self());
}
#endif /* DEBUG */
/* bootp */
return (B_TRUE);
/* dhcp - packet types match. */
return (B_TRUE);
} else {
return (B_TRUE);
}
}
}
return (B_FALSE);
}
/*
* Given a packet, searches for a later packet in the
* interface's client list. If the search is successful, the argument
* packet is deleted, and the later packet is returned with the appropriate
*
* Matches are based on match_plp(). The list is scanned until the final packet
* which "matches" is found. The last match replaces
* the argument plp. Duplicates are deleted.
*
* General Notes: After the first candidate is found, the list is checked to
* the tail of the list for other matches. For each packet which is deleted.
* the duplicate statistic is incremented for each one. If no candidate is
* found, then the argument plp is returned.
*
* Caveats: What about length and contents of packets? By definition, a
* client is not supposed to be altering this between frames, so we should
* be ok. Since the argument plp may be destroyed, it is assumed to be
* detached.
*/
PKT_LIST *
{
/*
* Note that tplp, retplp can be synonyms for
* wplp. The synonyms are used because moldy plp's
* will be nuked, and the plp to return will be
* detached.
*/
/* moldy duplicates */
}
} else {
}
}
else {
if (debug) {
"%04d: Refreshed (0x%x) to (0x%x)\n",
}
}
return (retplp);
}
/*
* Queries the IP transport layer for configured interfaces. Those that
* are acceptable for use by our daemon have these characteristics:
*
* Not loopback
* Is UP
*
* Sets num_interfaces global to number of valid, selected interfaces.
*
* Returns: 0 for success, the appropriate errno on fatal failure.
*
* Notes: Code gleaned from the in.rarpd, solaris 2.2.
*/
static int
find_interfaces(void)
{
struct sockaddr_in *sin;
char **user_if;
return (1);
}
"Error discovering number of network interfaces: %s\n",
return (1);
}
"Error getting network interface information: %s\n",
return (1);
}
/*
* Verify that user specified interfaces are valid.
*/
if (interfaces != NULL) {
for (i = 0; i < numifs; i++) {
break; /* we're done */
break;
}
}
if (!found) {
"Invalid network interface: %s\n",
user_if[i]);
return (1);
}
}
if (i < numifs)
} else
/*
* For each interface, build an interface structure. Ignore any
* LOOPBACK or down interfaces.
*/
ifr++) {
"Error encountered getting interface: %s flags: %s\n",
continue;
}
continue;
num_interfaces++; /* all possible interfaces counted */
/*
* If the user specified a list of interfaces,
* we'll only consider the ones specified.
*/
for (i = 0; i < numifs; i++) {
break; /* skip this interface */
break; /* user wants this one */
}
continue; /* skip this interface */
continue; /* skip virtual interfaces */
for (k = 0; k < DSRVR_NUM_DESC; k++)
/*
* Broadcast address. Not valid for POINTOPOINT
* connections.
*/
continue;
}
/* LINTED [alignment ok] */
} else
ENC_COPY);
}
/* Subnet mask */
continue;
}
/* LINTED [alignment ok] */
/* Address */
continue;
}
/* LINTED [alignment ok] */
/* MTU */
continue;
}
/* Attach to interface list */
if (!if_tail) {
(void) mutex_lock(&if_head_mtx);
(void) mutex_unlock(&if_head_mtx);
} else {
(void) mutex_lock(&if_head_mtx);
(void) mutex_unlock(&if_head_mtx);
}
}
num_interfaces = 0;
(void) mutex_destroy(&if_head_mtx);
return (EINVAL);
}
return (0);
}
/*
* Destroy an *uninitialized* IF structure - returns next ifp.
*/
static IF *
{
if (*ifp_prevpp == ifp) {
*ifp_prevpp = if_head;
} else
return (tifp);
}
/*
* Monitor thread function. Poll on interface descriptors. Add valid BOOTP
* packets to interfaces PKT_LIST.
*
* this by setting the 'offset' field to the length of the two headers so that
* free_plp() can "do the right thing"
*
* Monitor the given interface. Signals are handled by sig_client thread.
*
* We make some attempt to deal with marginal interfaces as follows. We
* keep track of system errors (errors) and protocol errors (ifp->errors).
* If we encounter more than DHCP_MON_SYSERRS in DHCP_MON_ERRINTVL,
* then the interface thread will put itself to sleep for DHCP_MON_SLEEP
* minutes.
*
* MT SAFE
*/
static void *
monitor_interface(void *argp)
{
int open_ret;
if (debug) {
}
if (verbose)
errors = 0;
while (time_to_go == 0) {
if (errors > DHCP_MON_SYSERRS) {
"Monitor (%04d/%s): Too many system errors (%d), pausing for %d minute(s)...\n",
(void) sleep(DHCP_MON_SLEEP);
}
errors = 0;
}
"Monitor (%04d/%s) Polling error: (%s).\n",
errors++;
continue;
}
/*
* See if we are to exit. We can't be holding any locks...
*/
if (debug) {
"Monitor (%04d/%s): exiting.\n",
}
break;
}
/* examine each socket for packets in turn */
for (i = 0; i < DSRVR_NUM_DESC; i++) {
continue;
dsrvr_socktype(i));
errors++;
continue;
}
dsrvr_socktype(i),
errors++;
continue;
}
"datagrams on %s(%s)\n",
dsrvr_socktype(i));
errors++;
continue;
} else
if (debug) {
"Datagram received on network device: "
}
if (verify_len < BASE_PKT_SIZE) {
if (verbose) {
"%d on %s(%s) ignored\n",
verify_len, sizeof (PKT),
}
continue;
}
/* LINTED [alignment ok] */
if (verbose) {
"dropped: too many hops: %d\n",
}
continue;
}
/* validate hardware len */
}
if (!server_mode) {
/*
* Relay agent mode. No further processing
* required ; we'll handle it here.
*/
(void) mutex_lock(&if_head_mtx);
(void) mutex_unlock(&if_head_mtx);
if (err != 0) {
"failed: %d (%s) on: %s(%s)\n",
dsrvr_socktype(i));
errors++; /* considered system error */
} else {
/* update statistics */
}
continue;
}
/* ============ Packets destined for bootp and dhcp server modules ========== */
/*
* Allow packets without RFC1048 magic cookies.
* Just don't do an options scan on them,
* thus we treat them as plain BOOTP packets.
* The BOOTP server can deal with requests of
* this type.
*/
sizeof (magic_cookie)) != 0) {
if (verbose) {
"using non-RFC1048 BOOTP cookie.\n",
}
} else {
/*
* Scan the options in the packet and fill in
* the opts and vs fields in the * clientlist
* structure. If there's a DHCP message type
* in the packet then it's a DHCP packet;
* otherwise it's a BOOTP packet. Standard
* options are RFC1048 style.
*/
"packet received on: %s(%s)\n",
continue;
}
}
/*
* Link the new packet to the list of packets
* since it isn't visible outside this function yet.
*/
"on %s(%s), BOOTP server port. Ignored.\n",
continue;
}
DSVC_SUCCESS) {
"%s dhcp-network table for DHCP "
dsrvr_socktype(i),
}
continue;
}
/* Find client */
continue;
}
/*
* DOS via Packet flooding: ensure that each client's
* PKT_LIST never exceeds DHCP_MON_THRESHOLD pkts in
* length. If it does, we prune it from the head of
* the list, dropping sequential packets. Note that
* since DHCP is a multi-transaction protocol, we would
* like to be sure not to discard a REQUEST for an OFFER
* we've extended.
*
* TODO: we are still vulnerable to flooding attacks
* where bogus client ids are presented. This can be
* manually controlled via the MAX_CLIENTS and
* MAX_THREADS config file knobs.
*/
}
}
else {
}
/*
* Manage worker threads and deferred thread work list.
*/
/*
* Restart a suspended thread.
*/
} else if (max_threads != -1 &&
/*
* Add client once to deferred work
* list, to keep track of future work.
*/
workp = (dsvc_pendclnt_t *)
sizeof (dsvc_pendclnt_t));
&workp->pnd_cid_len);
else {
}
}
if (open_ret == DSVC_SUCCESS)
continue;
}
(dsvc_thr_t *)
smalloc(sizeof (dsvc_thr_t));
USYNC_THREAD, 0);
/* Fire up a client thread. */
0) {
"Error %s starting client "
"monitor thread.\n",
(void) mutex_lock(
(void) mutex_unlock(
}
}
/*
* Continue the new or reused thread.
* Let it close the client.
*/
if (existing_allocation) {
(void) cond_signal(
} else {
(void) thr_continue(tid);
}
}
}
if (open_ret == DSVC_SUCCESS)
}
}
}
return (NULL);
}
/*
* close interface sockets
*/
static void
int i;
for (i = 0; i < DSRVR_NUM_DESC; i++) {
continue;
}
}
/*
* initialize interface sockets.
*
* Returns: 0 for success, -1 otherwise.
*/
static int
{
int i, soptbuf = 1;
struct sockaddr_in sin;
for (i = 0; i < DSRVR_NUM_DESC; i++) {
"receiving UDP datagrams: %s\n",
return (-1);
}
"to allow reuse on send descriptor failed: %s\n",
return (-1);
}
switch (i) {
case DSRVR_LBCAST:
(int)sizeof (char *)) < 0) {
"Bind to index failed on %s: %s\n",
return (-1);
}
break;
case DSRVR_DBCAST:
break;
case DSRVR_UCAST:
/* We send out the unicast socket */
(int)sizeof (soptbuf)) < 0) {
"option on %s to allow broadcast "
"on send descriptor failed: %s\n",
return (-1);
}
break;
}
"Error binding to UDP socket on %s(%s): %s\n",
return (-1);
}
}
return (0);
}
/*
* Based on the list generated by find_interfaces(), possibly modified by
* user arguments, open a stream for each valid / requested interface.
*
* If:
*
* 1) Only one interface exists, open a standard bidirectional UDP
* socket. Note that this is different than if only ONE
* interface is requested (but more exist).
*
* 2) If more than one valid interface exists, then attach to the
* datalink layer, push on the packet filter and buffering
* modules, and wait for fragment 0 IP packets that contain
* UDP packets with port 67 (server port).
*
* Comments:
* Using DLPI to identify the interface thru which BOOTP
* packets pass helps in providing the correct response.
* Note that I will open a socket for use in transmitting
* responses, suitably specifying the destination relay agent
* or host. Note that if I'm unicasting to the client (broadcast
* flag not set), that somehow I have to clue the IP layer about
* the client's hw address. The only way I can see doing this is
* making the appropriate ARP table entry.
*
* The only remaining unknown is dealing with clients that
* require broadcasting, and multiple interfaces exist. I assume
* that if I specify the interface's source address when
* opening the socket, that a limited broadcast will be
* directed to the correct net, and only the correct net.
*
* Returns: 0 for success, non-zero for failure.
*/
int
open_interfaces(void)
{
/* Uncover list of valid, user-selected interfaces to monitor */
if ((err = find_interfaces()) != 0)
return (err);
(void) mutex_lock(&if_head_mtx);
/*
* Setup valid interfaces.
*/
if (init_sockets(ifp) < 0) {
continue;
}
/* Accounting */
/* ifp structure lock */
/* fire up monitor thread */
continue;
}
inum++;
}
(void) mutex_unlock(&if_head_mtx);
/*
* We must succeed in configuring at least one interface
* to be considered successful.
*/
if (num_interfaces == 0) {
}
return (err);
}
/*
* Detach the referenced plp from the client list.
*/
void
{
} else
else {
}
}
/*
* Write a packet to an interface.
*
* Returns 0 on success otherwise non-zero.
*/
int
{
int err;
return (err);
}
return (0);
}
/*
* Pop any packet filters, buffering modules, close stream, free encode
* list, terminate monitor thread, free ifp. Return ifp next ptr.
*/
static IF *
{
int err;
/*
* Wait for the thread to exit. We release the if_head_mtx
* lock, since the monitor thread(s) need to acquire it to traverse
* the list - and we don't want to deadlock. Once the monitor thread
* notices the thr_exit flag, it'll be gone anyway. Note that if_head
* is changing (in close_interfaces()). At this point, only monitor
* threads that haven't been reaped could be walking the interface
* list. They will "see" the change in if_head.
*/
(void) mutex_unlock(&if_head_mtx);
"Error %d while waiting for monitor %d of %s\n",
}
(void) mutex_lock(&if_head_mtx);
/*
* Note: clients and their associated packet lists are freed prior
* to interfaces being closed.
*/
/* free encode list */
/* display statistics */
return (tifp);
}
/*
* Close all interfaces, freeing up associated resources.
* This should only be called from main() during final exit.
*/
void
close_interfaces(void)
{
(void) mutex_lock(&if_head_mtx);
if (verbose) {
}
}
(void) mutex_unlock(&if_head_mtx);
(void) mutex_destroy(&if_head_mtx);
}
/*
* display IF info. Must be MT Safe - called from monitor threads.
*/
static void
{
char ntoab[INET_ADDRSTRLEN];
}
/*
* Display IF statistics.
*/
void
{
}
/*
* Setup the arp cache so that IP address 'ia' will be temporarily
* bound to hardware address 'ha' of length 'len'. 'ia' is expected in
* network order.
*
* Returns: 0 if the arp entry was made, 1 otherwise.
*/
int
{
struct sockaddr_in *si;
int err = 0;
char scratch[DHCP_SCRATCH];
char ntoab[INET_ADDRSTRLEN];
switch (flags) {
case DHCP_ARP_ADD:
if (debug) {
scratch_len = sizeof (scratch);
&scratch_len) != 0) {
request to ASCII: %s: len: %d\n",
len);
} else {
"Adding ARP entry: %s == %s\n",
scratch);
}
}
"ADD: Cannot modify ARP table to add: %s\n",
err = 1;
}
break;
case DHCP_ARP_DEL:
/* give it a good effort, but don't worry... */
break;
default:
err = 1;
break;
}
return (err);
}
/*
* Address and send a BOOTP reply packet appropriately. Does right thing
* based on BROADCAST flag. Also checks if giaddr field is set, and
* WE are the relay agent...
*
* Returns: 0 for success, nonzero otherwise (fatal)
*/
int
{
struct sockaddr_in to;
char ntoab[INET_ADDRSTRLEN];
/* Going thru a relay agent */
} else {
/*
* TODO - what should we do if broadcast
* flag is set, but ptp connection?
*/
if (debug)
"Sending datagram to broadcast address.\n");
} else {
/*
* By default, we assume unicast!
*/
if (debug) {
"Unicasting datagram to %s address.\n",
}
/*
* No doubt a reply packet which we, as
* the relay agent, are supposed to deliver.
* Local Delivery!
*/
} else {
/*
* We can't use the giaddr field to
* determine whether the client is local
* or remote. Use the client's address,
* our interface's address, and our
* interface's netmask to make this
* determination.
*/
}
if (local) {
/*
* Local delivery. If we can make an
* ARP entry we'll unicast. But only in
* cases when we do have the chaddr handy.
* RFC2855 and IPoIB are cases that do not
* send chaddr and set hlen = 0. Identify
* such media by their htype, and rely on
* in-kernel ARP for them.
*/
DHCP_ARP_ADD) == 0))) {
} else {
}
}
}
}
}
/*
* Free pkts
*/
void
{
}
}