arp.c revision 35c0ca7e36fa9e428666351e49c6d6cf2151e87c
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2006 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 _SUN_TPI_VERSION 2
#include <sys/ethernet.h>
#include <sys/hook_event.h>
#include <inet/arp_impl.h>
/*
* ARP entry life time and design notes
* ------------------------------------
*
* ARP entries (ACEs) must last at least as long as IP knows about a given
* MAC-IP translation (i.e., as long as the IRE cache entry exists). It's ok
* if the ARP entry lasts longer, but not ok if it is removed before the IP
* entry. The reason for this is that if ARP doesn't have an entry, we will be
* unable to detect the difference between an ARP broadcast that represents no
* change (same, known address of sender) and one that represents a change (new
* address for existing entry). In the former case, we must not notify IP, or
* we can suffer hurricane attack. In the latter case, we must notify IP, or
* IP will drift out of sync with the network.
*
* Note that IP controls the lifetime of entries, not ARP.
*
* We don't attempt to reconfirm aging entries. If the system is no longer
* talking to a given peer, then it doesn't matter if we have the right mapping
* for that peer. It would be possible to send queries on aging entries that
* are active, but this isn't done.
*/
/*
* This is used when scanning for "old" (least recently broadcast) ACEs. We
* don't want to have to walk the list for every single one, so we gather up
* batches at a time.
*/
#define ACE_RESCHED_LIST_LEN 8
typedef struct {
#define ACE_NONPERM(ace) \
#define AR_SNMP_MSG T_OPTMGMT_ACK
#define AR_DRAINING (void *)0x11
/*
* The IPv4 Link Local address space is special; we do extra duplicate checking
* there, as the entire assignment mechanism rests on random numbers.
*/
/*
* 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
*/
#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 *);
/*
* 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 */
{ 30000, 3600000, 300000, "arp_cleanup_interval"},
{ 1000, 20000, 2000, "arp_publish_interval"},
{ 1, 20, 5, "arp_publish_count"},
{ 0, 20000, 1000, "arp_probe_delay"},
{ 10, 20000, 1500, "arp_probe_interval"},
{ 0, 20, 3, "arp_probe_count"},
{ 0, 20000, 100, "arp_fastprobe_delay"},
{ 10, 20000, 150, "arp_fastprobe_interval"},
{ 0, 20, 3, "arp_fastprobe_count"},
{ 0, 3600000, 300000, "arp_defend_interval"},
{ 0, 20000, 100, "arp_defend_rate"},
{ 0, 3600000, 15000, "arp_broadcast_interval"},
{ 5, 86400, 3600, "arp_defend_period"}
};
static struct module_info info = {
};
};
};
};
static void *ar_g_head; /* AR Instance Data List Head */
/*
* With the introduction of netinfo (neti kernel module), it is now possible
* to access data structures in the ARP module without the code being
* executed in the context of the IP module, thus there is no locking being
* enforced through the use of STREAMS.
*
*
*/
/*
* TODO: we need a better mechanism to set the ARP hardware type since
* the DLPI mac type does not include enough predefined values.
*/
};
/* ARP Cache Entry Hash Table */
static uint32_t arp_counter_wrapped = 0;
/*
* 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
*/
/* ARP Cmd Table entry */
typedef struct arct_s {
int arct_min_len;
int arct_priv_req; /* Privilege required for this cmd */
const char *arct_txt;
} arct_t;
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)
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);
/*
* if we're working with the IPv4 169.254.0.0/16 Link Local Address
* space, then don't use the fast timers. Otherwise, use them.
*/
if (arl->arl_notifies &&
flags |= ACE_F_FAST;
}
/*
* 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 != NULL) {
dst += proto_addr_len;
} else {
while (proto_addr_len-- > 0)
}
if (proto_extract_mask != NULL) {
} else {
}
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);
}
/*
* ar_ce_resolve is called when a response comes in to an outstanding request.
* Returns 'true' if the address has changed and we need to tell the client.
* (We don't need to tell the client if there's still an outstanding query.)
*/
static boolean_t
{
hw_addr_length) != 0;
if (hwchanged)
/*
* No need to bother with ar_query_reply if no queries are
* waiting.
*/
else if (hwchanged)
return (B_TRUE);
}
return (B_FALSE);
}
/*
* There are 2 functions performed by this function.
* 1. Resolution of unresolved entries and update of resolved entries.
* 2. Detection of nodes with our own IP address (duplicates).
*
* This is complicated by ill groups. We don't currently have knowledge of ill
* groups, so we can't distinguish between a packet that comes in on one of the
* arls that's part of the group versus one that's on an unrelated arl. Thus,
* we take a conservative approach. If the arls match, then we update resolved
* and unresolved entries alike. If they don't match, then we update only
* unresolved entries.
*
* For all entries, we first check to see if this is a duplicate (probable
* loopback) message. If so, then just ignore it.
*
* Next, check to see if the entry has completed DAD. If not, then we've
* failed, because someone is already using the address. Notify IP of the DAD
* failure and remove the broken ace.
*
* Next, we check if we're the authority for this address. If so, then it's
* time to defend it, because the other node is a duplicate. Report it as a
* 'bogon' and let IP decide how to defend.
*
* Finally, if it's unresolved or if the arls match, we just update the MAC
* address. This allows a published 'static' entry to be updated by an ARP
* request from the node for which we're a proxy ARP server -- e.g., when a
* mobile node returns home. If the address has changed, then tell IP.
*
* Note that this logic does not update published ARP entries for mismatched
* arls, as for example when we proxy arp across 2 subnets with differing
* subnet masks.
*
* Return Values below
*/
static int
{
int i1;
int retv = AR_NOTFOUND;
/* ar_ce_resolve may delete the ace; fetch next pointer now */
continue;
}
/*
* Note that the ace_proto_mask is applied to the proto_addr
* before comparing to the ace_addr.
*/
while (--i1 >= 0) {
break;
}
if (i1 >= 0)
continue;
/*
* If both IP addr and hardware address match what we already
* have, 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.
*/
ace->ace_hw_addr_length) == 0) {
return (AR_LOOPBACK);
}
/*
* If the entry is unverified, then we've just verified that
* someone else already owns this address, because this is a
* message with the same protocol address but different
* hardware address.
*/
return (AR_FAILED);
}
/*
* If the IP address matches ours and we're authoritative for
* this entry, then some other node is using our IP addr, so
* return AR_BOGON. Also reset the transmit count to zero so
* that, if we're currently in initial announcement mode, we
* switch back to the lazier defense mode. Knowing that
* there's at least one duplicate out there, we ought not
* blindly announce.
*/
ace->ace_xmit_count = 0;
return (AR_BOGON);
}
/*
* Limit updating across other ills to unresolved
* entries only. We don't want to inadvertently update
* published entries.
*/
retv = AR_CHANGED;
else if (retv == AR_NOTFOUND)
}
}
return (retv);
}
/* 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;
}
}
/*
* Send a delete-notify message down to IP. We've determined that IP doesn't
* have a cache entry for the IP address itself, but it may have other cache
* entries with the same hardware address, and we don't want to see those grow
* stale. (The alternative is sending down updates for every ARP message we
* get that doesn't match an existing ace. That's much more expensive than an
* occasional delete and reload.)
*/
static void
{
return;
}
/* ARP module close routine. */
static int
{
int index;
"arp_close: q %p", q);
index = 0;
/*
* 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 (ar->ar_on_ill_stream) {
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 */
}
/* mi_close_comm frees the instance data. */
(void) mi_close_comm(&ar_g_head, q);
ar_cleanup();
qprocsoff(q);
if (index != 0) {
}
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
{
/* Must queue message. Tail insertion */
return;
}
}
/*
* 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
{
t_uscalar_t, prim);
return;
}
return;
}
}
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.
*/
}
}
}
/*
* 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) {
}
}
/*
* 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
{
} else if (tail_insert) {
} else {
/* head insert */
}
}
static mblk_t *
{
return (NULL);
}
return (mp);
}
/*
* Standard ACE timer handling: compute 'fuzz' around a central value or from 0
* up to a value, and then set the timer. The randomization is necessary to
* prevent groups of systems from falling into synchronization on the network
* and producing ARP packet storms.
*/
static void
{
/* Note that clock_t is signed; must chop off bits */
if (initial_time) {
/* Set intv to be anywhere in the [1 .. intv] range */
if (intv <= 0)
intv = 1;
else
} else {
/* Compute 'frac' as 20% of the configured interval */
frac = 2;
/* Set intv randomly in the range [intv-frac .. intv+frac] */
intv = 1;
}
}
/*
* 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.
*/
return (EINPROGRESS);
}
/*
* If this is a replacement, ditch the original, but remember the
* duplicate address detection state. If it's a new entry, then we're
* obligated to do duplicate address detection now.
*/
} else {
}
/* Allow client to request DAD restart */
if (aflags & ACE_F_UNVERIFIED)
unverified = B_TRUE;
/* Extract parameters from the message. */
if (proto_mask == NULL) {
return (EINVAL);
}
err = ar_ce_create(
arl,
NULL,
(uint32_t)0,
if (err != 0) {
int, err);
return (err);
}
if (aflags & ACE_F_PUBLISH) {
} else if (aflags & ACE_F_MYADDR) {
/*
* 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.
*/
}
} else {
}
/*
* If the user has disabled duplicate address detection for
* this kind of interface (fast or slow) by setting the probe
* count to zero, then pretend as if we've verified the
* address, and go right to address defense mode.
*/
if (ace->ace_xmit_count == 0)
/*
* If we need to do duplicate address detection, then kick that
* off. Otherwise, send out a gratuitous ARP message in order
* to update everyone's caches with the new hardware address.
*/
if (unverified) {
if (ace->ace_xmit_interval == 0) {
/*
* User has configured us to send the first
* probe right away. Do so, and set up for
* the subsequent probes.
*/
proto_addr, NULL);
ace->ace_xmit_count--;
} else {
/* Regular delay before initial probe */
}
} else {
/*
* If AUTHORITY is set, it is not just a proxy arp
* entry; we believe we're the authority for this
* entry. In that case, and if we're not just doing
* one-off defense of the address, we send more than
* one copy, so that if this is an IPMP failover, we'll
* still have a good chance of updating everyone even
* when there's a packet loss or two.
*/
if ((aflags & ACE_F_AUTHORITY) &&
!(aflags & ACE_F_DEFEND) &&
arp_publish_count > 0) {
/* Account for the xmit we just did */
if (ace->ace_xmit_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.
*/
return (EINPROGRESS);
}
/*
* Need to know if it is a mapping or an exact match. Check exact
* match first.
*/
/*
* If it's a permanent entry, then the client is the one who
* told us to delete it, so there's no reason to notify.
*/
if (ACE_NONPERM(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.
*/
return (EINPROGRESS);
}
if (proto_addr == NULL) {
goto err_ret;
}
/* Stash the reply queue pointer for later use. */
if (areq->areq_xmit_interval == 0)
/*
* This is a potentially stale entry that IP's asking about.
* Since IP is asking, it must not have an answer anymore,
* either due to periodic ARP flush or due to SO_DONTROUTE.
* Rather than go forward with what we've got, restart
* resolution.
*/
}
/*
* 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 */
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 != 0) {
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.
*/
/*
* 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.
*/
return (EINPROGRESS);
}
/* Extract parameters from the request message. */
return (EINVAL);
}
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? */
return (EINPROGRESS);
}
return (0);
}
/* Process an interface down causing us to detach and unbind. */
/* ARGSUSED */
static int
{
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;
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' */
q = WR(q);
int, err);
}
return (err);
}
/*
* Enable an interface to
* process of ARP_REQUEST and ARP_RESPONSE messages
*/
/* ARGSUSED */
static int
{
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
{
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
}
} else {
}
}
}
}
/*
* Look up a lower level tap by name.
*/
static arl_t *
ar_ll_lookup_by_name(const char *name)
{
return (arl);
}
}
return (NULL);
}
/*
* Look up a lower level tap using parameters extracted from the common
* portion of the ARP command.
*/
static arl_t *
{
return (NULL);
return (ar_ll_lookup_by_name((char *)name));
}
static void
{
return;
/*
* a number uniquely identify an ARP instance can be removed and the
* ifindex from IP used. Rather than try and reinvent or copy the
* code used by IP for the purpose of allocating an index number
* (and trying to keep the number small), just allocate it in an
* ever increasing manner. This index number isn't ever exposed to
* users directly, its only use is for providing the pfhooks interface
* with a number it can use to uniquely identify an interface in time.
*
* Using a 32bit counter, over 136 plumbs would need to be done every
* second of every day (non-leap year) for it to wrap around and the
* for() loop below to kick in as a performance concern.
*/
if (arp_counter_wrapped) {
do {
if (arp_index_counter == 0) {
arp_index_counter = 1;
}
break;
}
} else {
}
if (arp_index_counter == 0) {
arp_index_counter = 1;
}
}
/*
* 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;
goto bad;
}
return (0);
bad:
return (ENOMEM);
}
/* Process mapping add 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.
*/
return (EINPROGRESS);
}
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. */
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);
/*
* 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.
*
* Tell IP, though, that we're an extended implementation, so
* it knows to expect a DAD response after bringing an
* interface up. Old ATM drivers won't do this, and IP will
* just bring the interface up immediately.
*/
if (!ar->ar_on_ill_stream)
return (0);
return (ENOMEM);
}
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.
*/
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 void
{
/* The response queue was stored in the query b_prev. */
}
} else {
}
}
}
/*
* 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? */
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, start the
* cleanup timer or (on error) delete the entry.
*/
/*
* No need to notify IP here, because the entry was
* never resolved, so IP can't have any cached copies
* of the address.
*/
} else {
}
}
}
/*
* 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.
*/
return (0);
}
}
/*
* If we haven't yet finished duplicate address checking on this source
* address, then do *not* use it on the wire. Doing so will corrupt
* the world's caches. Just allow the timer to restart. Note that
* duplicate address checking will eventually complete one way or the
* other, so this cannot go on "forever."
*/
areq->areq_xmit_count++;
return (areq->areq_xmit_interval);
}
/*
* 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;
int i;
"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:
/* Real messages from the wire! */
break;
}
"arp_rput_end: q %p (%S)", q, "default");
return;
}
switch (err) {
case ENOENT:
/* Miscellaneous DLPI messages get shuffled off. */
ar_rput_dlpi(q, mp);
break;
case EINPROGRESS:
"arp_rput_end: q %p (%S)", q, "proto");
break;
default:
break;
}
return;
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.
*/
"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;
}
/*
* hlen 0 is used for RFC 1868 UnARP.
*
* Note that the rest of the code checks that hlen is what we expect
* for this hardware address type, so might as well discard packets
* here that don't match.
*/
return;
}
/*
* Historically, Solaris has been lenient about hardware type numbers.
* We should check here, but don't.
*/
return;
/* Determine if this is just a probe */
for (i = 0; i < plen; i++)
if (src_paddr[i] != 0)
break;
/*
* RFC 826: first check if the <protocol, sender protocol address> is
* in the cache, if there is a sender protocol address. Note that this
* step also handles resolutions based on source.
*/
if (is_probe)
err = AR_NOTFOUND;
else
plen);
switch (err) {
case AR_BOGON:
break;
case AR_FAILED:
break;
case AR_LOOPBACK:
break;
}
"arp_rput_end: q %p (%S)", q, "unneeded");
return;
}
/*
* Now look up the destination address. By RFC 826, we ignore the
* packet at this step if the target isn't one of our addresses. This
* is true even if the target is something we're trying to resolve and
* the packet is a response.
*
* Note that in order to do this correctly, we need to know when to
* notify IP of a change implied by the source address of the ARP
* message. That implies that the local ARP table has entries for all
* of the resolved entries cached in the client. This is why we must
* notify IP when we delete a resolved entry and we know that IP may
* have cached answers.
*/
/*
* Let the client know if the source mapping has changed, even
* if the destination provides no useful information for the
* client.
*/
if (err == AR_CHANGED)
else
"arp_rput_end: q %p (%S)", q, "nottarget");
return;
}
/*
* If the target is unverified by DAD, then one of two things is true:
* either it's someone else claiming this address (on a probe or an
* announcement) or it's just a regular request. The former is
* failure, but a regular request is not.
*/
/*
* Check for a reflection. Some misbehaving bridges will
* reflect our own transmitted packets back to us.
*/
"arp_rput_end: q %p (%S)", q, "reflection");
return;
}
} else if (err == AR_CHANGED) {
} else {
}
"arp_rput_end: q %p (%S)", q, "unverified");
return;
}
/*
* If it's a request, then we reply to this, and if we think the
* sender's unknown, then we create an entry to avoid unnecessary ARPs.
* The design assumption is that someone ARPing us is likely to send us
* a packet soon, and that we'll want to reply to it.
*/
if (op == ARP_REQUEST) {
/*
* This implements periodic address defense based on a modified
* version of the RFC 3927 requirements. Instead of sending a
* broadcasted reply every time, as demanded by the RFC, we
* send at most one broadcast reply per arp_broadcast_interval.
*/
now = ddi_get_lbolt();
/*
* If this is one of the long-suffering entries, then
* pull it out now. It no longer needs separate
* defense, because we're doing now that with this
* broadcasted reply.
*/
}
}
}
if (err == AR_CHANGED) {
"arp_rput_end: q %p (%S)", q, "reqchange");
} else {
"arp_rput_end: q %p (%S)", q, "end");
}
}
static void
{
/*
* Slight cheat here: we don't use the initial probe delay
* in this obscure case.
*/
} else {
}
}
}
/* DLPI messages, other than DL_UNITDATA_IND are handled here. */
static void
{
union DL_primitives *dlp;
const char *err_str;
return;
}
switch (dlp->dl_primitive) {
case DL_ERROR_ACK:
/*
* ce is confused about how DLPI works, so we have to interpret
* an "error" on DL_NOTIFY_ACK (which we never could have sent)
* as really meaning an error on DL_NOTIFY_REQ.
*
* Note that supporting DL_NOTIFY_REQ is optional, so printing
* out an error message on the console isn't warranted except
* for debug.
*/
return;
}
case DL_UNBIND_REQ:
break;
case DL_DETACH_REQ:
case DL_BIND_REQ:
break;
case DL_ATTACH_REQ:
break;
default:
/* If it's anything else, we didn't send it. */
return;
}
"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:
case DL_UNBIND_REQ:
break;
case DL_DETACH_REQ:
break;
case DL_ATTACH_REQ:
break;
default:
return;
}
break;
case DL_NOTIFY_ACK:
/*
* We mostly care about interface-up transitions, as this is
* when we need to redo duplicate address detection.
*/
arl->arl_notifies =
break;
case DL_BIND_ACK:
if (arl->arl_sap_length < 0)
else
break;
case DL_NOTIFY_IND:
case DL_NOTE_LINK_UP:
break;
case DL_NOTE_LINK_DOWN:
break;
}
break;
case DL_UDERROR_IND:
"ar_rput_dlpi: "
"DL_UDERROR_IND, dl_dest_addr_length %d dl_errno %d",
return;
default:
union DL_primitives *, dlp);
return;
}
}
static void
{
int len;
proto_addr != NULL &&
while (len-- > 0)
}
}
static int
{
if (ar->ar_on_ill_stream) {
/*
* 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
*/
return (EINPROGRESS);
}
}
return (EALREADY);
}
return (ENXIO);
}
/* Check whether the name is already in use. */
return (EEXIST);
}
/* Make a copy of the message so we can send it downstream. */
return (ENOMEM);
}
/* The ppa is sent down by ifconfig */
/*
* A network device is not considered to be fully plumb'd until
* its name has been set using SIOCSLIFNAME. Once it has
* been set, it cannot be set again (see code above), so there
* is currently no danger in this function causing two NE_PLUMB
* events without an intervening NE_UNPLUMB.
*/
/* Chain in the new arl. */
arl_g_head = arl;
/*
* Send along a copy of the ioctl; this is just for hitbox. Use
* M_CTL to avoid confusing anyone else who might be listening.
*/
return (0);
}
static int
{
int ppa;
char *cp;
if (ar->ar_on_ill_stream) {
/*
* 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.
*/
return (EINPROGRESS);
}
return (EALREADY);
}
do {
q = 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...
*/
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
{
const 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);
return;
}
}
/*
* ace-> is a new entry to append
*/
}
/* 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;
}
if (err != 0)
}
"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");
}
static boolean_t
{
/* skip a beat on allocation trouble */
return (B_FALSE);
}
/* Tell IP address is now usable */
return (B_TRUE);
}
/*
* Pick the longest-waiting aces for defense.
*/
static void
{
return;
/*
* Only published entries that are ready for announcement are eligible.
*/
ACE_F_DELAYED)) != ACE_F_PUBLISH) {
return;
}
} else {
}
}
}
}
/*
* Reschedule the ARP defense of any long-waiting ACEs. It's assumed that this
* doesn't happen very often (if at all), and thus it needn't be highly
* optimized. (Note, though, that it's actually O(N) complexity, because the
* outer loop is bounded by a constant rather than by the length of the list.)
*/
static void
{
int i;
i = arl->arl_defend_count;
arl->arl_defend_count = 0;
/* If none could be sitting around, then don't reschedule */
if (i < arp_defend_rate) {
return;
}
break;
}
break;
}
}
/*
* 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;
continue;
/*
* If the link is down, give up for now. IP
* will give us the go-ahead to try again when
* the link restarts.
*/
if (!arl->arl_link_up) {
continue;
}
if (ace->ace_xmit_count > 0) {
ace->ace_xmit_count--;
continue;
}
if (!arp_say_ready(ace))
continue;
if (ace->ace_xmit_count == 0)
ace->ace_xmit_count++;
}
/*
* If an hour has passed, then free up the
* entries that need defense by rescheduling
* them.
*/
now = ddi_get_lbolt();
if (arp_defend_rate > 0 &&
}
/*
* Finish the job that we started in
* ar_entry_add. When we get to zero
* announcement retransmits left, switch to
* address defense.
*/
if (ace->ace_xmit_count > 0) {
ace->ace_xmit_count--;
/*
* This guy was rescheduled as one of
* the really old entries needing
* on-going defense. Let him through
* now.
*/
} else if (arp_defend_rate > 0 &&
++arl->arl_defend_count >=
arp_defend_rate)) {
/*
* If we're no longer allowed to send
* unbidden defense messages, then just
* wait for rescheduling.
*/
continue;
} else {
}
if (ace->ace_xmit_count == 0)
if (ace->ace_xmit_interval != 0)
continue;
}
/*
* If this is a non-permanent (regular) resolved ARP
* entry, then it's now time to check if it can be
* retired. As an optimization, we check with IP
* first, and just restart the timer if the address is
* still in use.
*/
if (ACE_NONPERM(ace)) {
ace->ace_proto_addr)) {
} else {
}
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
{
/* IFF_NOARP flag is set or interface down: do not send arp messages */
return;
return;
return;
}
/* Get the L2 destination address for the message */
/*
* Figure out where the target hardware address goes in the
* DL_UNITDATA_REQ header, and copy it in.
*/
return;
}
/* Fill in the ARP header. */
cp += ARH_FIXED_LEN;
else
else
return;
/* Ship it out. */
else
}
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);
}