arp.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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1990 Mentat Inc. */
#pragma ident "%Z%%M% %I% %E% SMI"
/* AR - Address Resolution Protocol */
#define ARP_DEBUG
#define _SUN_TPI_VERSION 2
#include <sys/ethernet.h>
#include <inet/arp_impl.h>
#ifdef ARP_DEBUG
#else
#define arp0dbg(a) /* */
#define arp1dbg(a) /* */
#define arp2dbg(a) /* */
#define arp3dbg(a) /* */
#endif
#define AR_SNMP_MSG T_OPTMGMT_ACK
#define AR_DRAINING (void *)0x11
/*
* Check if the command needs to be enqueued by seeing if there are other
* commands ahead of us or if some DLPI response is being awaited. Usually
* there would be an enqueued command in the latter case, however if the
* stream that originated the command has closed, the close would have
* cleaned up the enqueued command. AR_DRAINING signifies that the command
* at the head of the arl_queue has been internally dequeued on completion
* of the previous command and is being called from ar_dlpi_done
*/
/* Ugly check to determine whether the module below is IP */
#define MODULE_BELOW_IS_IP(q) \
/* ARP Cache Entry */
typedef struct ace_s {
int ace_publish_count;
} ace_t;
#define ACE_EXTERNAL_FLAGS_MASK \
#define ARH_FIXED_LEN 8
/*
* MAC-specific intelligence. Shouldn't be needed, but the DL_INFO_ACK
* doesn't quite do it for us.
*/
typedef struct ar_m_s {
} ar_m_t;
/* Named Dispatch Parameter Management Structure */
typedef struct arpparam_s {
char *arp_param_name;
} arpparam_t;
typedef struct ar_snmp_hashb {
struct ar_snmp_hashb *ar_next_entry;
static int ar_snmp_hash_size = 64;
typedef struct msg2_args {
} msg2_args_t;
extern ire_stats_t ire_stats_v4;
static void ar_cleanup(void);
static void ar_ll_cleanup_arl_queue(queue_t *q);
static void ar_ll_clear_defaults(arl_t *);
static void ar_snmp_msg2(ace_t *, void *);
static void ar_timer_init(queue_t *q);
#if 0
#endif
/*
* at run time. arp_publish_interval and arp_publish_count are
* set by default to 2 seconds and 5 respectively. This is
* packets are not lost. Assumed that it does not affect the
* normal operations.
*/
static arpparam_t arp_param_arr[] = {
/* min max value name */
{ 0, 10, 0, "arp_debug"},
{ 30000, 3600000, 300000, "arp_cleanup_interval"},
{ 1000, 20000, 2000, "arp_publish_interval"},
{ 1, 20, 5, "arp_publish_count"},
};
static struct module_info info = {
};
};
};
};
static void *ar_g_head; /* AR Instance Data List Head */
/*
* TODO: we need a better mechanism to set the ARP hardware type since
* the DLPI mac type does not include enough prodefined values.
*/
};
/* ARP Cache Entry Hash Table */
/*
* Note that all routines which need to queue the message for later
* processing have to be ioctl_aware to be able to queue the complete message.
* Following are command entry flags in arct_flags
*/
static arct_t ar_cmd_tbl[] = {
};
/*
* ARP Cache Entry creation routine.
* Cache entries are allocated within timer messages and inserted into
* the global hash list based on protocol and protocol address.
*/
static int
{
return (EINVAL);
if (flags & ACE_F_MYADDR)
flags |= ACE_F_PUBLISH;
if (!hw_addr && hw_addr_len == 0) {
/* 224.0.0.0 to zero length address */
flags |= ACE_F_RESOLVED;
} else { /* local address and unresolved case */
if (flags & ACE_F_PUBLISH)
flags |= ACE_F_RESOLVED;
}
} else {
flags |= ACE_F_RESOLVED;
}
if (!proto_addr || proto_addr_len == 0 ||
return (EINVAL);
/* Handle hw_addr_len == 0 for DL_ENABMULTI_REQ etc. */
if (hw_addr_len && !hw_addr)
return (EINVAL);
return (EINVAL);
return (EINVAL);
/*
* Allocate the timer block to hold the ace.
* (ace + proto_addr + proto_addr_mask + proto_extract_mask + hw_addr)
*/
if (!mp)
return (ENOMEM);
dst += proto_addr_len;
/*
* The proto_mask allows us to add entries which will let us respond
* to requests for a group of addresses. This makes it easy to provide
* proxy ARP service for machines that don't understand about the local
* subnet structure, if, for example, there are BSD4.2 systems lurking.
*/
if (proto_mask) {
dst += proto_addr_len;
} else {
while (proto_addr_len--)
}
if (proto_extract_mask) {
} else {
}
if (hw_addr) {
dst += hw_addr_len;
}
ace->ace_proto_addr_length)) {
} else
return (0);
}
/* Delete a cache entry. */
static void
{
/* Get out of the hash list. */
/* Mark it dying in case we have a timer about to fire. */
/* Complete any outstanding queries immediately. */
/* Free the timer, immediately, or when it fires. */
}
/*
* ar_ce_walk routine. Delete the ace if it is associated with the arl
* that is going away.
*/
static void
{
}
}
/* Cache entry hash routine, based on protocol and protocol address. */
static ace_t **
{
int len = proto_addr_length;
while (--len >= 0)
}
/* Cache entry lookup. Try to find an ace matching the parameters passed. */
ace_t *
{
if (!ace)
return (ace);
}
/*
* Cache entry lookup. Try to find an ace matching the parameters passed.
* Look only for exact entries (no mappings)
*/
static ace_t *
{
if (!proto_addr)
return (NULL);
int i1 = proto_addr_length;
/*
* Note that the ace_proto_mask is applied to the
* proto_addr before comparing to the ace_addr.
*/
do {
if (--i1 < 0)
return (ace);
}
}
return (ace);
}
/*
* Extract cache entry lookup parameters from an external command message, then
* call the supplied match function.
*/
static ace_t *
{
if (!proto_addr)
return (NULL);
}
/*
* Cache entry lookup. Try to find an ace matching the parameters passed.
* Look only for mappings.
*/
static ace_t *
{
if (!proto_addr)
return (NULL);
int i1 = proto_addr_length;
/*
* Note that the ace_proto_mask is applied to the
* proto_addr before comparing to the ace_addr.
*/
do {
if (--i1 < 0)
return (ace);
}
}
return (ace);
}
/*
* Look for a permanent entry for proto_addr across all interfaces.
* This is used for sending ARP requests out. Requests may come from
* IP on le0 with the source address of le1 and we need to send out
* the request on le1 so that ARP does not think that somebody else
* is using its PERMANENT address. If le0 and le1 are sitting on
* the same wire, the same IP -> ethernet mapping might exist on
* both the interfaces. But we should look for the permanent
* mapping to avoid arp interpreting it as a duplicate.
*/
static ace_t *
{
continue;
int i1 = proto_addr_length;
/*
* Note that the ace_proto_mask is applied to the
* proto_addr before comparing to the ace_addr.
*/
do {
if (--i1 < 0)
return (ace);
}
}
return (ace);
}
/*
* Pass a cache report back out via NDD.
* TODO: Right now this report assumes IP proto address formatting.
*/
/* ARGSUSED */
static int
{
(void) mi_mpprintf(mp,
"ifname proto addr proto mask hardware addr flags");
/* abcdefgh xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx xx:xx:xx:xx:xx:xx */
return (0);
}
/*
* Add a single line to the ARP Cache Entry Report.
* TODO: Right now this report assumes IP proto address formatting.
*/
static void
{
char *name = "unknown";
if (!p)
p = zero_array;
if (!h)
h = zero_array;
if (!m)
m = zero_array;
(void) mi_mpprintf(mp,
"%8s %03d.%03d.%03d.%03d "
"%03d.%03d.%03d.%03d %02x:%02x:%02x:%02x:%02x:%02x",
name,
p[0] & 0xFF, p[1] & 0xFF, p[2] & 0xFF, p[3] & 0xFF,
m[0] & 0xFF, m[1] & 0xFF, m[2] & 0xFF, m[3] & 0xFF,
h[0] & 0xFF, h[1] & 0xFF, h[2] & 0xFF, h[3] & 0xFF,
h[4] & 0xFF, h[5] & 0xFF);
if (flags & ACE_F_PERMANENT)
if (flags & ACE_F_PUBLISH)
if (flags & ACE_F_DYING)
if (!(flags & ACE_F_RESOLVED))
if (flags & ACE_F_MAPPING)
if (flags & ACE_F_MYADDR)
}
/*
* ar_ce_resolve is called when a response comes in to an outstanding
* request.
*/
static void
{
if (ace->ace_hw_addr)
/*
* ar_query_reply() blows away soft entries.
* Do not call it unless something is waiting.
*/
if (ace->ace_query_mp)
}
}
/*
* There are 2 functions performed by this function.
* 1. Resolution of unresolved entries and update of resolved entries.
* 2. Detection of hosts with (duplicate) our own IP address
*
* Resolution of unresolved entries and update of resolved entries.
*
* case A. The packet has been received on the same interface as this ace's
* arl. We blindly call ar_ce_resolve(). The relevant checks for duplicate
* detection (ACE_F_MYADDR) and trying to update published entries have
* already happened in ar_rput(). Both resolved and unresolved entries are
* updated now. This allows a published entry to be updated by an arp
* request, from the node for which we are a proxy arp server, as for eg.
* when a mobile node returns home.
*
* case B. The interface on which the packet arrived does not match the
* ace's arl. In this case we update only unresolved entries.
* Look whether we have an unresolved entry for src_paddr and if so
* resolve it. We need to look at all the aces that matches the
* src_haddr because with ill groups we could have unresolved ace
* across the whole group. As we don't have knowledge of groups,
* look across all of them. Note that this logic does not update published
* arp entries, as for eg. when we proxy arp across 2 subnets with
* differing subnet masks.
*
* Detection of hosts with (duplicate) our own IP address.
*
* case A is handled in ar_rput(). case B is handled here. We return AR_BOGON,
* if we detect duplicate, and caller will send BOGON message to IP.
* If hme0 and hme1 are in a IPMP group. hme1 will receive broadcast arp
* packets sent from hme0. Both IP address and Hardware address of the
* packet match the ace. So we return AR_LOOPBACK.
*
* Return Values below
*/
static int
{
/*
* Note that the ace_proto_mask is applied to the
* proto_addr before comparing to the ace_addr.
*/
do {
if (--i1 < 0) {
/*
* Limit updating across other
* ills to unresolved entries only.
* We don't want to inadvertently
* update published entries or our
* own entries.
*/
(!ACE_RESOLVED(ace))) {
} else {
/*
* If both IP addr and hardware
* address match our's then this
* is a broadcast packet emitted by
* one of our interfaces, reflected
* by the switch, and received on
* another interface. We return
* AR_LOOPBACK. If only IP addr.
* matches our's then some other node
* is using our IP addr, return
* AR_BOGON.
*/
ace->ace_hw_addr_length) != 0) {
return (AR_BOGON);
} else {
return (AR_LOOPBACK);
}
}
}
break;
}
}
}
return (AR_NORMAL);
}
/* Pass arg1 to the pfi supplied, along with each ace in existence. */
static void
{
/*
* We walk the hash chain in a way that allows the current
* ace to get blown off by the called routine.
*/
}
}
}
}
/* Free the ND tables if the last ar has gone away. */
static void
ar_cleanup(void)
{
if (!ar_g_head)
}
/*
* Send a copy of interesting packets to the corresponding IP instance.
* The corresponding IP instance is the ARP-IP-DEV instance for this
* DEV (i.e. ARL).
*/
static void
{
/* Looks like the association disappeared */
return;
}
/* ar is the corresponding ARP-IP instance for this ARL */
return;
}
}
/* ARP module close routine. */
static int
{
"arp_close: q %p", q);
/*
* If this is the <ARP-IP-Driver> stream send down
* a closing message to IP and wait for IP to send
* an ack. This helps to make sure that messages
* that are currently being sent up by IP are not lost.
*/
if (MODULE_BELOW_IS_IP(q)) {
while (!ar->ar_ip_acked_close)
/* If we are interrupted break out */
if (qwait_sig(q) == 0)
break;
}
}
/* Delete all our pending queries, 'arl' is not dereferenced */
/*
* The request could be pending on some arl_queue also. This
* happens if the arl is not yet bound, and bind is pending.
*/
} else {
/*
* If this is the control stream for an arl, delete anything
* hanging off our arl.
*/
/* Free any messages waiting for a bind_ack */
/* Get the arl out of the chain. */
break;
}
}
}
/* Let's break the association between an ARL and IP instance */
}
if (WR(q) == ar_timer_queue) {
/* We were using this one for the garbage collection timer. */
break;
if (arl) {
/* Ask mi_timer to switch to the new queue. */
} else {
ar_timer_mp = NULL;
}
}
/* mi_close_comm frees the instance data. */
(void) mi_close_comm(&ar_g_head, q);
ar_cleanup();
qprocsoff(q);
return (0);
}
/*
* Dispatch routine for ARP commands. This routine can be called out of
* either ar_wput or ar_rput, in response to IOCTLs or M_PROTO messages.
*/
/* TODO: error reporting for M_PROTO case */
static int
{
if (!mp)
return (ENOENT);
/* We get both M_PROTO and M_IOCTL messages, so watch out! */
if (!mp)
return (ENOENT);
}
return (ENOENT);
return (ENOENT);
break;
}
/*
* If the command is exclusive to ARP, we return EINVAL,
* else we need to pass the command downstream, so return
* ENOENT
*/
}
int error;
B_FALSE)) != 0)
return (error);
}
}
/* Allocate and do common initializations for DLPI messages. */
static mblk_t *
{
return (NULL);
/*
* DLPIv2 says that DL_INFO_REQ and DL_TOKEN_REQ (the latter
* of which we don't seem to use) are sent with M_PCPROTO, and
* that other DLPI are M_PROTO.
*/
return (mp);
}
/*
* The following two functions serialize DLPI messages to the driver, much
* along the lines of ill_dlpi_send and ill_dlpi_done in IP. Basically,
* we wait for a DLPI message, sent downstream, to be acked before sending
* the next. If there are DLPI messages that have not yet been sent, queue
* this message (mp), else send it downstream.
*/
static void
{
union DL_primitives *dlp;
/* Must queue message. Tail insertion */
arp1dbg(("ar_dlpi_send: deferring DLPI message arl %p %x\n",
return;
}
dlp->dl_primitive));
}
/*
* Called when an DLPI control message has been acked; send down the next
* queued message (if any).
* The DLPI messages of interest being bind, attach, unbind and detach since
* these are the only ones sent by ARP via ar_dlpi_send.
*/
static void
{
union DL_primitives *dlp;
arp0dbg(("ar_dlpi_done: spurious response arl %p\n",
(void *)arl));
return;
}
return;
}
arp1dbg(("ar_dlpi_done: sending DLPI message arl %p %x\n",
}
static void
{
int cmd;
int err;
queue_t *q;
/*
* If the current operation was initiated by IP there must be
* an op enqueued in arl_queue. But if ar_close has sent down
* stream has closed the cleanup would be done and there won't be any mp
*/
return;
cmd &= ~CMD_IN_PROGRESS;
if (cmd == AR_INTERFACE_UP) {
/*
* There is an ioctl waiting for us...
*/
err = 0;
else
if (dlpi_op_done_mp != NULL) {
/*
* Better performance if we send the response
* after the potential MAPPING_ADDs command
* that are likely to follow. (Do it below the
* while loop, instead of putnext right now)
*/
}
if (err == 0) {
/*
* Now that we have the ARL instance
* corresponding to the IP instance let's make
* the association here.
*/
}
}
ar_freemsg(mp);
}
/*
* Run the commands that have been enqueued while we were waiting
* for the last command (AR_INTERFACE_UP or AR_INTERFACE_DOWN)
* to complete.
*/
/*
* Don't call put(q, mp) since it can lead to reorder of
* messages by sending the current messages to the end of
* arp's syncq
*/
else
goto done; /* no work to do */
/*
* The current command is an AR_INTERFACE_UP or
* AR_INTERFACE_DOWN and is waiting for a DLPI ack
* from the driver. Return. We can't make progress now.
*/
goto done;
}
}
done:
if (dlpi_op_done_mp != NULL) {
arp1dbg(("ar_dlpi_done: ardlpiopdone arl %p to q %p err %d\n",
}
}
/*
* Queue all arp commands coming from clients. Typically these commands
* come from IP, but could also come from other clients. The commands
* are serviced in FIFO order. Some commands need to wait and restart
* after the DLPI response from the driver is received. Typically
* AR_INTERFACE_UP and AR_INTERFACE_DOWN. ar_dlpi_done restarts
* the command and then dequeues the queue at arl_queue and calls ar_rput
* or ar_wput for each enqueued command. AR_DRAINING is used to signify
* that the command is being executed thru a drain from ar_dlpi_done.
* Functions handling the individual commands such as ar_entry_add
* check for this flag in b_prev to determine whether the command has
* to be enqueued for later processing or must be processed now.
*
* b_next used to thread the enqueued command mblks
* b_queue used to identify the queue of the originating request(client)
* b_prev used to store the command itself for easy parsing.
*/
static void
{
(void *)q, cmd));
} else if (tail_insert) {
} else {
/* head insert */
}
}
static mblk_t *
{
return (NULL);
}
return (mp);
}
/*
* Process entry add requests from external messages.
* It is also called by ip_rput_dlpi_writer() through
* ipif_resolver_up() to change hardware address when
* an asynchronous hardware address change notification
* arrives from the driver.
*/
static int
{
int err;
/* We handle both M_IOCTL and M_PROTO messages. */
return (EINVAL);
/*
* Newly received commands from clients go to the tail of the queue.
*/
arp1dbg(("ar_entry_add: enqueue cmd on q %p \n", (void *)q));
return (EINPROGRESS);
}
/* If this is a replacement, ditch the original. */
/* Extract parameters from the message. */
if (!proto_mask)
return (EINVAL);
err = ar_ce_create(
arl,
NULL,
(uint32_t)0,
if (err)
return (err);
/*
* Transmit an arp request for this address to flush stale
* information froma arp caches.
*/
/*
* If hardware address changes, then make sure
* that the hardware address and hardware
* address length fields in arl_t get updated
* too. Otherwise, they will continue carrying
* the old hardware address information.
*/
}
/*
* If MYADDR is set - it is not a proxy arp entry. In that
* case we send more than one copy, so that if this is
* a case of failover, we send out multiple entries in case
* the switch is very slow.
*/
/* Account for the xmit we just did */
ace->ace_publish_count--;
if (ace->ace_publish_count != 0) {
}
}
}
return (0);
}
/* Process entry delete requests from external messages. */
static int
{
/* We handle both M_IOCTL and M_PROTO messages. */
return (EINVAL);
/*
* Newly received commands from clients go to the tail of the queue.
*/
arp1dbg(("ar_entry_delete: enqueue on q %p\n", (void *)q));
return (EINPROGRESS);
}
/*
* Need to know if it is a mapping or an exact match. Check exact
* match first.
*/
if (ace) {
return (0);
}
return (ENXIO);
}
/*
* Process entry query requests from external messages.
* Bump up the ire_stats_freed for all errors except
* EINPROGRESS - which means the packet has been queued.
* For all other errors the packet is going to be freed
* and hence we account for ire being freed if it
* is a M_PROTO message.
*/
static int
{
int err;
/* We handle both M_IOCTL and M_PROTO messages. */
}
goto err_ret;
}
/*
* Newly received commands from clients go to the tail of the queue.
*/
arp1dbg(("ar_entry_query: enqueue on q %p\n", (void *)q));
return (EINPROGRESS);
}
if (proto_addr == 0) {
goto err_ret;
}
/* Stash the reply queue pointer for later use. */
if (areq->areq_xmit_interval == 0)
if (ace) {
/*
* There is already a cache entry. This means there is either
* a permanent entry, or address resolution is in progress.
* If the latter, there should be one or more queries queued
* up. We link the current one in at the end, if there aren't
* too many outstanding.
*/
goto err_ret;
}
}
/* Put us on the list. */
if (count != 0) {
/*
* If a query was already queued up, then we must not
* have an answer yet.
*/
return (EINPROGRESS);
}
if (ACE_RESOLVED(ace)) {
/*
* We have an answer already.
* Keep a dup of mp since proto_addr points to it
* and mp has been placed on the ace_query_mp list.
*/
return (EINPROGRESS);
}
/* Should never happen */
arp0dbg(("ar_entry_query: unresolved mapping\n"));
goto err_ret;
}
/* Can't get help if we don't know how. */
goto err_ret;
}
} else {
/* No ace yet. Make one now. (This is the common case.) */
if (areq->areq_xmit_count == 0 ||
goto err_ret;
}
/*
* Check for sender addr being NULL or not before
* we create the ace. It is easy to cleanup later.
*/
if (sender_addr == NULL) {
goto err_ret;
}
areq->areq_flags);
if (err) {
goto err_ret;
}
/* Shouldn't happen! */
goto err_ret;
}
/*
* We don't have group information here. But if the sender
* address belongs to a different arl, we might as well
* search the other arl for a resolved ACE. If we find one,
* we resolve it rather than sending out a ARP request.
*/
printf("ar_entry_query: Could not find the ace for "
"source address %d.%d.%d.%d\n",
sender_addr[3]);
/*
* ar_query_reply has already freed the mp.
* Return EINPROGRESS, so that caller won't attempt
* to free the 'mp' again.
*/
return (EINPROGRESS);
}
/*
* Check for a resolved entry in the src_ace->ace_arl.
*/
return (EINPROGRESS);
}
}
}
if (ms == 0) {
/* Immediate reply requested. */
} else {
}
return (EINPROGRESS);
if (is_mproto)
return (err);
}
/* Handle simple query requests. */
static int
{
int proto_addr_len;
return (EINVAL);
/*
* Newly received commands from clients go to the tail of the queue.
*/
arp1dbg(("ar_entry_squery: enqueue on q %p\n", (void *)q));
return (EINPROGRESS);
}
/* Extract parameters from the request message. */
if (!proto_addr || !hw_addr)
return (EINVAL);
if (!ace)
return (ENXIO);
return (EINVAL);
if (ACE_RESOLVED(ace)) {
/* Got it, prepare the response. */
} else {
/*
* We have an incomplete entry. Set the length to zero and
* just return out the flags.
*/
area->area_hw_addr_length = 0;
}
/* Non-ioctl case */
/* TODO: change message type? */
arp1dbg(("ar_entry_squery: qreply\n"));
return (EINPROGRESS);
}
return (0);
}
/* Make sure b_next and b_prev are null and then free the message */
static void
{
}
}
/* Process an interface down causing us to detach and unbind. */
/* ARGSUSED */
static int
{
arp1dbg(("ar_interface_down q %p\n", (void *)q));
arp1dbg(("ar_interface_down: no arl q %p \n", (void *)q));
return (EINVAL);
}
/*
* Newly received commands from clients go to the tail of the queue.
*/
return (EINPROGRESS);
}
/*
* The arl is already down, no work to do.
*/
/* ar_rput frees the mp */
return (0);
}
/*
* This command cannot complete in a single shot now itself.
* It has to be restarted after the receipt of the ack from
* the driver. So we need to enqueue the command (at the head).
*/
/* Free all arp entries for this interface */
/* Return EINPROGRESS so that ar_rput does not free the 'mp' */
return (EINPROGRESS);
}
/* Process an interface up causing the info req sequence to start. */
/* ARGSUSED */
static int
{
int err;
arp1dbg(("ar_interface_up q %p\n", (void *)q));
arp1dbg(("ar_interface_up: no arl %p\n", (void *)q));
goto done;
}
/*
* Newly received commands from clients go to the tail of the queue.
*/
return (EINPROGRESS);
}
/*
* The arl is already up. No work to do.
*/
err = 0;
goto done;
}
/*
* This command cannot complete in a single shot now itself.
* It has to be restarted after the receipt of the ack from
* the driver. So we need to enqueue the command (at the head).
*/
/* Return EINPROGRESS so that ar_rput does not free the 'mp' */
return (EINPROGRESS);
done:
/* caller frees 'mp' */
arp1dbg(("ar_interface_up: send resp err %d q %p\n",
err, (void *)q));
}
return (err);
}
/*
* Enable an interface to
* process of ARP_REQUEST and ARP_RESPONSE messages
*/
/* ARGSUSED */
static int
{
arp1dbg(("ar_interface_on\n"));
arp1dbg(("ar_interface_on: no arl\n"));
return (EINVAL);
}
/* Turn off the IFF_NOARP flag and activate ARP */
return (0);
}
/*
* Disable an interface from processing
* ARP_REQUEST and ARP_RESPONSE messages
*/
/* ARGSUSED */
static int
{
arp1dbg(("ar_interface_off\n"));
arp1dbg(("ar_interface_off: no arl\n"));
return (EINVAL);
}
/* Turn on the IFF_NOARP flag and deactivate ARP */
return (0);
}
/*
* The queue 'q' is closing. Walk all the arl's and free any message
* pending in the arl_queue if it originated from the closing q.
* Also cleanup the ip_pending_queue, if the arp-IP stream is closing.
*/
static void
{
else
}
ar_freemsg(mp);
} else {
}
}
}
}
/*
* Look up a lower level tap by name. Note that the name_length includes
* the null terminator.
*/
static arl_t *
{
int i1 = name_length;
if (--i1 == 0)
return (arl);
}
}
}
return (NULL);
}
/*
* Look up a lower level tap using parameters extracted from the common
* portion of the ARP command.
*/
static arl_t *
{
if (!name)
return (NULL);
}
static void
{
return;
}
/*
* This routine is called during module initialization when the DL_INFO_ACK
* comes back from the device. We set up defaults for all the device dependent
* doo-dads we are going to need. This will leave us ready to roll if we are
* attempting auto-configuration. Alternatively, these defaults can be
* overidden by initialization procedures possessing higher intelligence.
*/
static void
{
int hw_addr_length;
int i1;
/* Sanity check... */
return;
/*
* If we receive multiple DL_INFO_ACkS make sure there are no
* leaks by clearing the defaults now
*/
/*
* We initialize based on parameters in the (currently) not too
* exhaustive ar_m_tbl.
*/
} else {
}
goto bad;
/*
* Someday DLPI will provide the multicast address? Meanwhile we
* assume an address of all ones, known to work on some popular
* networks.
*/
goto bad;
up += hw_addr_length;
/*
* TODO Note that sap_length can be 0 before binding according
* to the DLPI spec.
*/
} else {
*up++ = (char)~0;
}
/*
* The hardware address will be filled in when we see the DL_BIND_ACK.
* We reserve space for it here, and make arl_hw_addr point to it.
*/
/*
* Make us a template DL_UNITDATA_REQ message which we will use for
* broadcasting resolution requests, and which we will clone to hand
* back as responses to the protocols.
*/
goto bad;
/* Note the destination address offset permanently in the arl. */
if (arl->arl_sap_length < 0) {
} else {
/* The sap is first in the address */
+ arl->arl_sap_length;
}
return;
bad:
}
static void
{
arl->arl_sap_length = 0;
}
if (arl->arl_xmit_template) {
}
}
static void
{
/* Let's break the association between an ARL and IP instance */
}
}
}
static int
{
goto bad;
goto bad;
}
goto bad;
/* Allocate and initialize a bind message. */
goto bad;
goto bad;
}
return (0);
bad:
return (ENOMEM);
}
/* Process mapping add requests from external messages. */
static int
{
arp1dbg(("ar_mapping_add\n"));
/* We handle both M_IOCTL and M_PROTO messages. */
return (EINVAL);
/*
* Newly received commands from clients go to the tail of the queue.
*/
arp1dbg(("ar_mapping_add: enqueue on %p q\n", (void *)q));
return (EINPROGRESS);
}
if (!proto_mask || !proto_extract_mask) {
arp0dbg(("ar_mapping_add: not masks\n"));
return (EINVAL);
}
return (ar_ce_create(
arl,
}
static boolean_t
{
return (B_TRUE);
while (mask_len-- > 0) {
if (*mask++ != 0xFF) {
return (B_FALSE);
}
}
return (B_TRUE);
}
/* Find an entry for a particular MAC type in the ar_m_tbl. */
static ar_m_t *
{
return (arm);
}
return (NULL);
}
/* Respond to Named Dispatch requests. */
static int
{
return (0);
return (ENOENT);
}
/* ARP module open routine. */
static int
{
int err;
"arp_open: q %p", q);
/* Allow a reopen. */
return (0);
}
/* Load up the Named Dispatch tables, if not already done. */
if (!ar_g_nd &&
NULL) ||
ar_cleanup();
return (ENOMEM);
}
/* mi_open_comm allocates the instance data structure, etc. */
credp);
if (err) {
ar_cleanup();
return (err);
}
/*
* We are D_MTPERMOD so it is safe to do qprocson before
* the instance data has been initialized.
*/
qprocson(q);
q = WR(q);
if (!ar_timer_mp)
ar_timer_init(q);
/*
* Probe for the DLPI info if we are not pushed on IP. Wait for
* the reply. In case of error call ar_close() which will take
* care of doing everything required to close this instance, such
* as freeing the arl, restarting the timer on a different queue etc.
*/
/*
* We are pushed directly on top of IP. There is no need to
* send down a DL_INFO_REQ. Return success. This could
* either be an ill stream (i.e. <arp-IP-Driver> stream)
* (i.e. <arp-IP> stream). Note that we don't support
* pushing some module in between arp and IP.
*/
return (0);
}
tmp_q = q;
/* Get the driver's queue */
/*
* We don't support pushing ARP arbitrarily on an
* IP driver stream. ARP has to be pushed directly above IP
*/
return (ENOTSUP);
} else {
/*
* Send down a DL_INFO_REQ so we can find out what we are
* talking to.
*/
if (!mp) {
return (ENOMEM);
}
return (EINTR);
}
}
}
return (0);
}
/* Get current value of Named Dispatch item. */
/* ARGSUSED */
static int
{
return (0);
}
/*
* Walk through the param array specified registering each element with the
* named dispatch handler.
*/
static boolean_t
{
return (B_FALSE);
}
}
}
return (B_TRUE);
}
/* Set new value of Named Dispatch item. */
/* ARGSUSED */
static int
{
long new_value;
return (EINVAL);
}
return (0);
}
/*
* Process an I_PLINK ioctl. If the lower stream is an arp device stream,
* append another mblk to the chain, that will carry the device name,
* and the muxid. IP uses this info to lookup the corresponding ill, and
* set the ill_arp_muxid atomically, as part of the I_PLINK, instead of
* waiting for the SIOCSLIFMUXID. (which may never happen if ifconfig is
* killed, and this has the bad effect of not being able to unplumb
* subsequently)
*/
static int
{
char *name;
/*
* Allocate a new mblk which will hold an ipmx_s and chain it to
* the M_IOCTL chain. The final chain will consist of 3 mblks,
* namely the M_IOCTL, followed by the linkblk, followed by the ipmx_s
*/
return (ENOMEM);
ipmxp->ipmx_arpdev_stream = 0;
/*
* The l_qbot represents the uppermost write queue of the
* lower stream. Walk down this stream till we hit ARP.
* We can safely walk, since STREAMS has made sure the stream
* cannot close till the IOCACK goes up, and is not interruptible.
*/
/*
* Beware of broken modules like logsubr.c that
* may not have a q_qinfo or qi_minfo.
*/
break;
}
}
/*
* Check if arpwq corresponds to an arp device stream, by walking
* the mi list. If it does, then add the muxid and device name info
* for use by IP. IP will send the M_IOCACK.
*/
break;
}
}
}
return (0);
}
/*
* ar_ce_walk routine to delete any outstanding queries for an ar that is
* going away.
*/
static int
{
if (!mp)
return (0);
do {
/* The response queue was stored in the query b_prev. */
}
ar_freemsg(mp);
} else {
}
return (0);
}
/*
* This routine is called either when an address resolution has just been
* found, or when it is time to give, or in some other error situation.
* If a non-zero ret_val is provided, any outstanding queries for the
* specified ace will be completed using that error value. Otherwise,
* the completion status will depend on whether the address has been
* resolved.
*/
static void
{
/* Cancel any outstanding timer. */
/* Establish the return value appropriate. */
if (ret_val == 0) {
}
/* Terminate all outstanding queries. */
/* The response queue was saved in b_prev. */
/*
* If we have the answer, attempt to get a copy of the xmit
* template to prepare for the client.
*/
if (ret_val == 0 &&
/* Too bad, buy more memory. */
}
/* Complete the response based on how the request arrived. */
if (ret_val != 0) {
continue;
}
/*
* Return the xmit template out with the successful
* IOCTL.
*/
/* Remove the areq mblk from the IOCTL. */
} else {
if (ret_val != 0) {
/* TODO: find some way to let the guy know? */
ar_freemsg(mp);
continue;
}
/*
* In the M_PROTO case, the areq message is followed by
* a message chain to be returned to the protocol. ARP
* doesn't know (or care) what is in this chain, but in
* the event that the reader is pondering the
* relationship between ARP and IP (for example), the
* areq is followed by an incipient IRE, and then the
* original outbound packet. Here we detach the areq.
*/
}
/*
* Copy the SAP type specified in the request into
* the xmit template.
*/
}
/* Done with the areq message. */
/*
* Copy the resolved hardware address into the xmit template
* or perform the mapping operation.
*/
/*
* Now insert the xmit template after the response message. In
* the M_IOCTL case, it will be the returned data block. In
* the M_PROTO case, (again using IP as an example) it will
* appear after the IRE and before the outbound packet.
*/
}
/*
* Unless we are responding from a permanent cache entry, delete
* the ace.
*/
}
}
/*
* Returns number of milliseconds after which we should either rexmit or abort.
* Return of zero means we should abort. src_ace is the ace corresponding
* to the source address in the areq sent by IP.
*/
static clock_t
{
if (!mp)
return (0);
if (areq->areq_xmit_count == 0)
return (0);
areq->areq_xmit_count--;
/*
* Get the source h/w address for the sender addr. With interface
* groups, IP sends us source address belonging to a different
* interface.
*/
printf("ar_query_xmit: Could not find the ace\n");
return (0);
}
}
/*
* Transmit on src_arl. We should transmit on src_arl. Otherwise
* the switch will send back a copy on other interfaces of the
* same group and as we could be using somebody else's source
* address + hardware address, ARP will treat this as a bogon.
*/
return (areq->areq_xmit_interval);
}
/* Our read side put procedure. */
static void
{
int err;
int op;
"arp_rput_start: q %p", q);
/*
* We handle ARP commands from below both in M_IOCTL and M_PROTO
* messages. Actual ARP requests and responses will show up as
* M_PROTO messages containing DL_UNITDATA_IND blocks.
*/
case M_IOCTL:
switch (err) {
case ENOENT:
/*
* Collapse the data as a note to the
* originator.
*/
}
break;
case EINPROGRESS:
"arp_rput_end: q %p (%S)", q, "ioctl/inprogress");
return;
default:
break;
}
else
"arp_rput_end: q %p (%S)", q, "ioctl");
return;
case M_CTL:
/*
* IP is acking the AR_ARP_CLOSING message that we sent
* in ar_close.
*/
}
return;
case M_PCPROTO:
case M_PROTO:
switch (err) {
case ENOENT:
break;
case EINPROGRESS:
"arp_rput_end: q %p (%S)", q, "proto");
return;
default:
ar_freemsg(mp);
return;
}
!= DL_UNITDATA_IND) {
/* Miscellaneous DLPI messages get shuffled off. */
ar_rput_dlpi(q, mp);
return;
}
/* DL_UNITDATA_IND */
/* Real messages from the wire! */
break;
}
/* FALLTHRU */
default:
"arp_rput_end: q %p (%S)", q, "default");
return;
}
/*
* If the IFF_NOARP flag is on, then do not process any
* incoming ARP_REQUEST or incoming ARP_RESPONSE.
*/
"arp_rput_end: q %p (%S)", q, "interface has IFF_NOARP set");
return;
}
/*
* What we should have at this point is a DL_UNITDATA_IND message
* followed by an ARP packet. We do some initial checks and then
* get to work.
*/
if (!mp1) {
"arp_rput_end: q %p (%S)", q, "baddlpi");
return;
}
/* No fooling around with funny messages. */
"arp_rput_end: q %p (%S)", q, "pullupmsgfail");
return;
}
}
"arp_rput_end: q %p (%S)", q, "short");
return;
}
arp1dbg(("ar_rput: bogus arh\n"));
return;
}
/* Now see if we have a cache entry for the source address. */
/*
* If so, and it is the entry for one of our IP addresses,
* we really don't expect to see this packet, so pretend we didn't.
* Tell IP that we received a bogon.
*
* If is a "published" (proxy arp) entry we can receive requests
* FROM the node but we should never see an ARP_RESPONSE. In this case
* we process the response but also inform IP.
*/
if (src_ace) {
"arp_rput_end: q %p (%S)", q, "pubentry");
return;
}
op == ARP_RESPONSE) {
}
}
}
switch (op) {
case ARP_REQUEST:
/*
* If we know the answer, and it is "published", send out
* the response.
*/
ACE_RESOLVED(dst_ace)) {
}
/*
* Now fall through to the response side, and add a cache entry
* for the sender so we will have it when we need it.
*/
/* FALLTHRU */
case ARP_RESPONSE:
/*
* With ill groups, we need to look for request across
* all the ills in the group. The request itself may
* not be queued on this arl. See ar_query_xmit() for
* details.
*/
/*
* Some other host has our IP address. Send a
* BOGON message to IP.
*/
"arp_rput_end: q %p (%S)", q, "pubentry");
return;
}
/*
* We may need this one sooner or later. The AR_LOOPBACK
* check above ensures, that we don't create arp
* entries for our own IP address, on another arl.
*/
(uint32_t)0);
}
/* Let's see if this is a system ARPing itself. */
do {
break;
} while (--plen);
if (plen == 0) {
/*
* An ARP message with identical src and dst
* protocol addresses. This guy is trying to
* tell us something that our clients might
* find interesting.Essentially such packets are
* sent when a m/c comes up or changes its h/w
* address, so before notifying our client check the
* h/w address if there is a cache entry and notify
* the client only if the addresses differ.
*/
if (hwaddr_changed) {
} else {
/* Just discard it. */
}
"arp_rput_end: q %p (%S)", q, "duplicate");
return;
}
/*
* A broadcast response may also be interesting.
*/
return;
}
break;
default:
break;
}
"arp_rput_end: q %p (%S)", q, "end");
}
/* DLPI messages, other than DL_UNITDATA_IND are handled here. */
static void
{
char *err_str;
return;
}
switch (dloa->dl_primitive) {
case DL_ERROR_ACK:
switch (dlea->dl_error_primitive) {
case DL_UNBIND_REQ:
err_str = "DL_UNBIND_REQ";
break;
case DL_DETACH_REQ:
err_str = "DL_DETACH_REQ";
break;
case DL_ATTACH_REQ:
err_str = "DL_ATTACH_REQ";
break;
case DL_BIND_REQ:
err_str = "DL_BIND_REQ";
break;
default:
err_str = "?";
break;
}
arp0dbg(("ar_rput_dlpi: "
"%s (%d) failed, dl_errno %d, dl_unix_errno %d\n",
"ar_rput_dlpi: %s failed, dl_errno %d, dl_unix_errno %d",
break;
case DL_INFO_ACK:
/*
* We have a response back from the driver. Go set up transmit
* defaults.
*/
} else {
}
/* Kick off any awaiting messages */
break;
case DL_OK_ACK:
arp1dbg(("ar_rput_dlpi: arl %p DL_OK_ACK for %d\n",
switch (dloa->dl_correct_primitive) {
case DL_UNBIND_REQ:
break;
case DL_DETACH_REQ:
break;
case DL_ATTACH_REQ:
break;
}
break;
case DL_BIND_ACK:
if (arl->arl_sap_length < 0)
else
break;
case DL_UDERROR_IND:
"ar_rput_dlpi: "
"DL_UDERROR_IND, dl_dest_addr_length %d dl_errno %d",
return;
default:
arp1dbg(("ar_rput_dlpi: default, primitive %d\n",
(int)dloa->dl_primitive));
return;
}
}
static void
{
int len;
if (!ace->ace_hw_addr)
return;
proto_addr != NULL &&
arp1dbg(("ar_set_address: MAPPING\n"));
while (len-- > 0)
}
}
static int
{
int newlength;
arp1dbg(("ar_slifname\n"));
if (MODULE_BELOW_IS_IP(q)) {
/*
* This command is for IP, since it is coming down
* the <arp-IP-driver> stream. Return ENOENT so that
* it will be sent downstream by the caller
*/
return (ENOENT);
}
/* We handle both M_IOCTL and M_PROTO messages */
/*
* If the interface was just opened and
* the info ack has not yet come back from the driver
*/
arp1dbg(("ar_slifname no arl - queued\n"));
return (EINPROGRESS);
}
return (EALREADY);
return (ENXIO);
/* Check whether the name is already in use. */
return (EEXIST);
}
/* The ppa is sent down by ifconfig */
arp1dbg(("ar_slifname: name is now %s, ppa %d\n",
/* Chain in the new arl. */
arl_g_head = arl;
return (0);
}
static int
{
int ppa;
char *cp;
arp1dbg(("ar_set_ppa\n"));
if (MODULE_BELOW_IS_IP(q)) {
/*
* This command is for IP, since it is coming down
* the <arp-IP-driver> stream. Return ENOENT so that
* it will be sent downstream by the caller
*/
return (ENOENT);
}
/* We handle both M_IOCTL and M_PROTO messages. */
/*
* If the interface was just opened and
* the info ack has not yet come back from the driver.
*/
arp1dbg(("ar_set_ppa: no arl - queued\n"));
return (EINPROGRESS);
}
return (EALREADY);
do {
q = q->q_next;
} while (q->q_next);
/* Make it a null string again */
return (EBUSY);
}
/* Chain in the new arl. */
arl_g_head = arl;
return (0);
}
/*
* create hash table for comparison.
* The data recvd from IP is hashed on IP address for fast matching.
*/
static ar_snmp_hashb_t *
{
int entries;
if (ar_snmp_hash_tbl == NULL)
return (NULL);
next_entry++;
}
return (ar_snmp_hash_tbl);
}
static int
{
if (!mp)
return (0);
/*
* ar_cmd_dispatch() already checked for us that "mp->b_cont" is valid
* in case of an M_IOCTL message.
*/
return (EINPROGRESS);
}
/*
* this is an ipNetToMediaTable msg from IP that needs (unique)
* arp cache entries appended...
*/
arp0dbg(("ar_snmp_msg: b_cont == NULL for MIB2_IP msg\n"));
return (EINVAL);
}
if (ar_snmp_hash_tbl != NULL) {
/*
* if a new entry was added link it with the list passed in.
*/
}
return (EINPROGRESS); /* so that rput() exits doing nothing... */
}
static uchar_t *
{
if (!mp)
return (NULL);
if (oldptr)
else
if (!mp)
return (NULL);
return (NULL);
}
return (oldptr);
}
static void
{
char *name = "unknown";
/*
* Append this arp entry only if not already there...
* entry.
* entries within arp cache are unique, so match only with entries
* passed in.
*/
/*
* get the first entry.
*/
if (np->ipNetToMediaNetAddress ==
/* permanent arp entries are "static" */
}
return;
}
}
/*
* Allocate the first structure, the rest will be allocated
* by snmp_append_data.
*/
BPRI_HI);
arp1dbg(("ar_snmp_msg2:allocb failed\n"));
return;
}
}
/*
* ace-> is a new entry to append
*/
}
/* Start up the garbage collection timer on the queue provided. */
static void
ar_timer_init(queue_t *q)
{
if (ar_timer_mp)
return;
ar_timer_mp = mi_timer_alloc(0);
if (!ar_timer_mp)
return;
ar_timer_queue = q;
}
/* ar_ce_walk routine to trash all non-permanent resolved entries. */
/* ARGSUSED */
static int
{
return (0);
}
/* Write side put procedure. */
static void
{
int err;
"arp_wput_start: q %p", q);
/*
* Here we handle ARP commands coming from controlling processes
* either in the form of M_IOCTL messages, or M_PROTO messages.
*/
case M_IOCTL:
case ENOENT:
/*
* If it is an I_PLINK, process it. Otherwise
* we don't recognize it, so pass it down.
* Since ARP is a module there is always someone
* below.
*/
"arp_wput_end: q %p (%S)",
return;
}
if (err == 0) {
return;
}
break;
case EINPROGRESS:
/*
* If the request resulted in an attempt to resolve
* an address, we return out here. The IOCTL will
* be completed in ar_rput if something comes back,
* or as a result of the timer expiring.
*/
"arp_wput_end: q %p (%S)", q, "inprog");
return;
default:
break;
}
else
"arp_wput_end: q %p (%S)", q, "ioctl");
return;
case M_FLUSH:
"arp_wput_end: q %p (%S)", q, "flush");
return;
}
/*
* The normal behaviour of a STREAMS module should be
* to pass down M_FLUSH messages. However there is a
* can cause DLPI messages in the driver's queue to be
* flushed. So we don't send down M_FLUSH. This has been
* reported for some drivers (Eg. le) that send up an M_FLUSH
* in response to unbind request which will eventually be
* looped back at the mux head and sent down. Since IP
* does not queue messages in a module instance queue
* of IP, nothing is lost by not sending down the flush.
*/
return;
case M_PROTO:
case M_PCPROTO:
/*
* Commands in the form of PROTO messages are handled very
* much the same as IOCTLs, but no response is returned.
*/
case ENOENT:
if (q->q_next) {
"arp_wput_end: q %p (%S)", q,
return;
}
break;
case EINPROGRESS:
return;
default:
break;
}
break;
case M_IOCDATA:
/*
* We pass M_IOCDATA downstream because it could be as a
* upstream.
*/
/* FALLTHRU */
case M_CTL:
/*
* We also send any M_CTL downstream as it could
* contain control information for a module downstream.
*/
return;
default:
break;
}
/* Free any message we don't understand */
"arp_wput_end: q %p (%S)", q, "end");
}
/*
* Write side service routine. The only action here is delivery of transmit
* timer events and delayed messages while waiting for the info_ack (ar_arl
* not yet set).
*/
static void
{
"arp_wsrv_start: q %p", q);
case M_PCSIG:
if (!mi_timer_valid(mp))
continue;
if (mp == ar_timer_mp) {
/* Garbage collection time. */
continue;
}
/*
* Finish the job that we started in
* ar_entry_add.
*/
ace->ace_publish_count--;
if (ace->ace_publish_count != 0 &&
arp_publish_interval != 0) {
}
continue;
}
if (!ace->ace_query_mp)
continue;
/*
* ar_query_xmit returns the number of milliseconds to
* wait following this transmit. If the number of
* allowed transmissions has been exhausted, it will
* return zero without transmitting. If that happens
* we complete the operation with a failure indication.
* Otherwise, we restart the timer.
*/
if (ms == 0)
else
continue;
default:
continue;
}
}
"arp_wsrv_end: q %p", q);
}
/* ar_xmit is called to transmit an ARP Request or Response. */
static void
{
char *cp;
/* IFF_NOARP flag is set. Do not send an arp request */
return;
}
return;
return;
}
/*
* Figure out where the target hardware address goes in the
* DL_UNITDATA_REQ header, and copy it in.
*/
hlen);
if (!cp) {
return;
}
/* Fill in the ARP header. */
cp += ARH_FIXED_LEN;
/* Ship it out. */
else
}
/*
* Handle an external request to broadcast an ARP request. This is used
* by configuration programs to broadcast a request advertising our own
* hardware and protocol addresses.
*/
static int
{
/* We handle both M_IOCTL and M_PROTO messages. */
return (EINVAL);
/*
* Newly received commands from clients go to the tail of the queue.
*/
arp1dbg(("ar_xmit_request: enqueue on q %p\n", (void *)q));
return (EINPROGRESS);
}
return (EINVAL);
return (0);
}
/*
* Handle an external request to broadcast an ARP response. This is used
* by configuration programs to broadcast a response advertising our own
* hardware and protocol addresses.
*/
static int
{
/* We handle both M_IOCTL and M_PROTO messages. */
return (EINVAL);
/*
* Newly received commands from clients go to the tail of the queue.
*/
arp1dbg(("ar_xmit_response: enqueue on q %p \n", (void *)q));
return (EINPROGRESS);
}
return (EINVAL);
return (0);
}
#if 0
/*
* Debug routine to display a particular ARP Cache Entry with an
* accompanying text message.
*/
static void
{
if (msg)
printf("\tace_next 0x%p, ace_ptpn 0x%p, ace_arl 0x%p\n",
printf("\tace_proto_addr %x %x %x %x, len %d\n",
if (ace->ace_proto_mask)
printf("\tace_proto_mask %x %x %x %x\n",
printf("\tace_hw_addr %x %x %x %x %x %x, len %d\n",
printf("\tace_query_count %d, ace_query_mp 0x%x\n",
}
/* Debug routine to display an ARP packet with an accompanying text message. */
static void
{
int len;
char fmt[64];
char buf[128];
char *op;
if (len < 8) {
return;
}
case ARP_REQUEST:
op = "ARP request";
break;
case ARP_RESPONSE:
op = "ARP response";
break;
case RARP_REQUEST:
op = "RARP request";
break;
case RARP_RESPONSE:
op = "RARP response";
break;
default:
op = "unknown";
break;
}
printf("len %d, hardware %d, proto %d, hlen %d, plen %d, op %s\n",
up += 8;
if (proto == 0x800) {
printf("sender proto address %d.%d.%d.%d\n",
} else {
}
if (proto == 0x800) {
printf("target proto address %d.%d.%d.%d\n",
} else {
}
}
#endif
static mblk_t *
{
char *cp;
/* For now only one type of command is accepted */
if (cmd != AR_DLPIOP_DONE)
return (NULL);
if (!mp)
return (NULL);
len = sizeof (int);
if (!mp1) {
return (NULL);
}
/* Initialize the error code */
return (mp);
}