agentNet.c revision 5c0b7edee9bd9fad49038456b16972ff28fa4187
/*
* 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 1999-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* file: agentNet.c
*
* This file contains all routines used to interact with the
* network, such as reading and writing.
*
* This file also contains the event dispatcher, which submits
* packets for processing to a pool of threads.
*/
#include <stdio.h>
#include <unistd.h>
#include <netinet/in_systm.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include "mip.h"
#include "agent.h"
#include "thq.h"
#include "setup.h"
#include "agentKernelIntfce.h"
#define MIN_IP_HDR_LEN 20
#define INFTIM -1
/*
* The following is the max number of Message Headers we will
* allocate in a single chunk. The bigger the value, the more
* memory we allocate, the smaller, the more often we malloc().
*/
#define MAX_MSG_HDR_NUM 512
#define MIP_MAX_THREADS 64
#define MIP_MIN_THREADS 4
static char *ifTypeName[] = {
"Unicast",
"Broadcast",
"Multicast"
};
static rwlock_t msgQueueLock;
static int allocatedMsgHdr = 0;
static pthread_t dispatchThreadId = 0;
static pthread_t DynamicThreadId = 0;
static fd_set saved_fdvec;
static int ioc_sock;
/* Counters common to all Mobility Agents */
extern CommonCounters commonCounters;
/* Foreign Agent specific data structures. */
extern struct hash_table faVisitorHash;
extern ForeignAgentCounters faCounters;
extern int logVerbosity;
extern struct hash_table maAdvConfigHash;
extern int visitorEntryHighWaterMark;
extern int visitorEntryLowWaterMark;
static void *processMsgHdr();
static void *getAndDispatchNetworkPacket(void);
static void *doDynamicInterfaceProcess(void *);
static void process_rtsock_msg(int);
static void delmaAdvConfigEntry(uint32_t);
static DynamicIfaceTypeEntry *match_dynamic_table(char *);
/*
* Function: AllocateMessageHdrBlock
*
* Arguments:
*
* Description: This function will pre-allocate a block of
* MAX_MSG_HDR_NUM Message Control Blocks.
*
* Returns: int - 0 if successful.
*/
static boolean_t
{
int i;
/*
* Now we create the message control blocks...
*/
#if 0
#else
sizeof (MessageHdr));
#endif
if (msgHdrQueue == NULL) {
return (_B_TRUE);
}
}
return (_B_FALSE);
}
/*
* Function: AllocateMessageHdr
*
* Arguments:
*
* Description: This function will return one of the Message
* Control Blocks from the queue. If none are
* available, we will attempt to allocate another
* chunk of control blocks.
*
* Returns: Pointer to Messge Control Block. NULL if failed.
*/
{
/*
* Lock the queue
*/
(void) rw_wrlock(&msgQueueLock);
/*
* If there are no items on the queue, we will try to allocate
* another chunk.
*/
if (msgHdrQueue == NULL) {
if (AllocateMessageHdrBlock()) {
return (NULL);
}
}
/*
* Now get the message header to return
*/
/*
* Unlock the queue
*/
(void) rw_unlock(&msgQueueLock);
/*
* Initialize the NAI stuff in the message header.
*/
messageHdr->mnNAILen = 0;
messageHdr->faNAILen = 0;
#ifdef KEY_DISTRIBUTION
/*
* KEY_DISTRIBUTION MUST ONLY BE COMPILED FOR TESTING!!!
*
* interface. The DIAMETER server generates keying
* material that is sent to the Home Agent. The keys
* sent are both for the Home Agent, and for the Mobile
* Node. The keys for the Mobile Nodes are added to the
* registration reply, and the keys for the Home Agent
* cause the Home Agent to create a local SA.
*
* distribution must still be tested, we have added some
* test code in mipagent. When KEY_DISTRIBUTION is enabled,
* the home agent creates and encrypts session keys for
* the Mobile Node (mimicking DIAMETER), and creates local
* SAs. Further, since the session keys MUST also be sent
* to the Foreign Agent, the session keys are sent in the
* clear to the Foreign Agent through Vendor Specific
* extensions.
*
* Again, this code is for testing purpose only and must not
* be enabled for production code, since it hasn't been
* fully tested.
*/
messageHdr->mnHaKeyLen = 0;
messageHdr->mnFaKeyLen = 0;
messageHdr->faHaKeyLen = 0;
#endif /* KEY_DISTRIBUTION */
return (messageHdr);
}
/*
* Function: FreeMessageHdr
*
* Arguments: messageHdr - Pointer to a pointer to a
* Message Control Block
*
* Description: Puts a Message Control Block back on the
* free list.
*
* Returns:
*/
void
{
/*
* When FA uses Radius to authenticate the RegReq, it sends a reg
* to Radius server and waits for the reply. Meanwhile, we want to
* preserve the messageHdr, because FA is going to need it. So,
* let's prevent messageHdr from being deleted, until FA is done and
* it explicitely calls FreeMessageHdr() with dontDeleteNow == FALSE
*/
if (messageHdr->dontDeleteNow)
return;
/* Free up faNAI space malloc-ed in aaa.c */
/*
* Lock and put the item back on the queue
*/
(void) rw_wrlock(&msgQueueLock);
(void) rw_unlock(&msgQueueLock);
}
/*
* Function: startDispatcherTaskThread
*
* Arguments:
*
* Description: This function will allocate the thread queue,
* which is used to dispatch objects to be processed
* by threads created by the thread management module
* (thq.c). The function will also allocate the initial
* chunk of MAX_MSG_HDR_NUM Message Control Blocks and
* start the dispatching thread.
*
* Returns: int - 0 if successful.
*/
int
{
int result;
if (!messageQueue) {
return (-1);
}
(void) rw_wrlock(&msgQueueLock);
if (AllocateMessageHdrBlock()) {
(void) rw_unlock(&msgQueueLock);
return (-1);
}
(void) rw_unlock(&msgQueueLock);
if (result) {
return (-1);
}
/*
* We now create a thread to deal with all periodic task.
*/
(void *(*)()) getAndDispatchNetworkPacket,
(void *)NULL);
if (result) {
return (-1);
}
/*
* In order for system resources to be properly cleaned up,
* we need to detach the thread. Otherwise, we need to wait for
* a pthread_join(), which we do not want.
*/
if (result) {
(void) pthread_cancel(dispatchThreadId);
return (-1);
}
return (0);
}
/*
* Function: killDispatcherTaskThread
*
* Arguments:
*
* Description: This function is used to kill the dispatch thread,
* the threads that are created by the thread queue
* management sub-system as well as the thread queue.
*
* Returns: int - 0 if sucessful.
*/
int
{
int result;
if (dispatchThreadId) {
/*
* Next we need to kill the dispatching thread.
*/
if (result) {
/*
* Well, there's not much we can do here..
*/
}
}
if (messageQueue) {
/*
* First we kill all of the message handling threads.
*/
}
return (0);
}
/*
* Function: startDynamicInterfaceThread
* Arguments: None
* Description:
* This function is called only when
* the configuration file indicates dynamic interface
* and DynamicInterface variable is true.
*
* Returns 0 on success.
*/
int
{
int result;
if (result != 0) {
return (-1);
}
/*
* We now create a thread to deal with processing new interfaces
*/
if (result != 0) {
return (-1);
}
/*
* In order for system resources the be properly cleaned up,
* we need to detach the thread. Otherwise, we need to wait for
* a pthread_join(), which we do not want.
*/
if (result != 0) {
return (-1);
}
return (0);
}
/*
* Function: killDynamicInterfaceThread
* Arguments:
* Description: This function is used for thread cleanup
* Returns 0 on success
*/
int
{
int result;
if (DynamicThreadId) {
/*
* Next we need to kill the dispatching thread.
*/
if (result != 0) {
/*
* Well, there's not much we can do here..
*/
return (-1);
}
DynamicThreadId = 0;
}
return (0);
}
/*
* Function: dispatchMsgToThread
*
* Arguments: messageHdr - Pointer to a pointer to a
* Message Control Block
*
* Description: This function will submit a Message Control
* Block for processing to a child thread using
* the thread management sub-system.
*
* Returns: int - 0 if successfully dispatched to thread.
*/
int
{
if (*messageHdr == NULL) {
return (-1);
}
return (-1);
}
/*
* Since we've queued the packet for a thread, we will
* NULL out the pointer so that it does not get re-used.
*/
*messageHdr = NULL;
return (0);
}
/*
* Function: sendUDPmessage
*
* Arguments: sock - Socket
* pkt - Packet to send
* pktLen - packet Length
* dst - Destination IP Address
* dstPort - Destination IP Port
*
* Description: Send a UDP message from sock, to dst address and port
* dstPort. The pkt is contained in pkt and is of length
* pktLen. dst is already in network byte order.
*
* Returns:
*/
int
{
struct sockaddr_in sa;
/* send the message */
sizeof (struct sockaddr_in)) < 0) {
return (-1);
}
return (0);
}
/*
* Function: sendICMPmessage
*
* Arguments: s - socket
* dst - Destination Address
* data - Data to send
* len - length of data to send
*
* Description: Send an ICMP message contained in data to dst
* from src
*
* Returns:
*/
void
{
struct sockaddr_in sa;
/* send the message */
sizeof (struct sockaddr_in)) < 0) {
}
}
/*
* Function: screenICMPpkt
*
* Arguments: messageHdr - Pointer to a Message Control Block
*
* Description: Pkt contains a complete IP datagram(including IP
* header) received on a socket monitoring ICMP packets.
*
* Returns:
*/
static void
{
char addrstr1[INET_ADDRSTRLEN];
char addrstr2[INET_ADDRSTRLEN];
return;
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
return;
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
/*
* checksum because the kernel takes care of this. As far as dst
* addr goes, the understanding is traffic is checked for forwarding
* before the kernel's parser mechanism gets it for passing up.
*/
"Got ICMP solicitation <type %d, code %d> from %s to %s.\n",
/* Currently, the only code for type 10 solicits. */
/*
* Restrict advertisement to the particular interface
* on which the solicitation was received.
*/
IFF_POINTOPOINT) == 0) ||
/*
* Since we don't have ARP information at this
* point, advertise on Bcast or Mcast addr.
*/
} else {
/*
* Solicitation from PPP interface with a
* non-zero source address.
*/
}
/*
* should actually be manipulated inside
* maSendAdvertisement()
*/
}
/* There can be only one reason we're in here, so we're done. */
return;
}
/*
* If this ICMP is in response to a forwarded registration request,
* the IP dst addr should be ours...
*/
/* not for us */
return;
}
/*
* A response to a packet we sent, check if it's a undelivered regreQ.
* We'd better have gotten enough for a returned UDP header.
*/
sizeof (struct udphdr)) {
/* if we can't get to the udp port info, we can't continue */
"Recieved ICMP error allegedly for a packet we sent,"
" but it's too small to do anything with!\n");
return;
}
/*
* Note: the belief is before the kernel sends up the ICMP, it's
* checked things like length, checksum, etc.
*/
/* It's time to look at the returned IP packet. */
/* We should be the sender of the inner packet */
/* We're not responsible for sending that packet */
return;
}
/* UDP? */
/* protocol in returned IP header isn't UDP - <zap> */
return;
}
/* OK, the encased IP packet allegedly carries UDP info - go there */
/* LINTED E_BAD_PTR_CAST_ALIGN */
/*
* Swapping madness for the port value because this piece is RAW off
* the network. Note: htons() is a no-op on SPARC.
*/
/* dst port of returned UDP packet isn't MIP_PORT <zzzt>. */
return;
}
/*
* Looks like we were returned a UDP packet to port 434, so lets
* find out why delivery failed, then let the mobile node know.
*
* There are two reasons we could be geting an ICMP unreachable. The
* most obvious comes from bad addresses, but another possibility is
* the simple timeout.
*/
/* All that info implies it was for something we sent out */
"Received ICMP error in response to regreQ from MN: "));
/* The error to the MN is based on the ICMP code... */
case ICMP_UNREACH_NET:
case ICMP_UNREACH_NET_UNKNOWN:
case ICMP_UNREACH_NET_PROHIB:
/* return error 80 home network unreachable */
mipverbose(("Home Network Unreachable.\n"));
break;
case ICMP_UNREACH_HOST:
case ICMP_UNREACH_HOST_PROHIB:
/* return error 81 home agent host unreachable */
mipverbose(("Home Agent IP Unreachable.\n"));
break;
case ICMP_UNREACH_PORT:
/* return error 82 home agent port unreachable */
mipverbose(("Home Agent Port Unreachable.\n"));
break;
case ICMP_UNREACH_ISOLATED:
case ICMP_SOURCEQUENCH:
/*
* Notes for the case of sourceQuench - the packet
* was lost somewhere (what else can we assume?)
* Two choices: send an unreachable error to the
* MN, or resend the registration request, but it's
* NOT contained in the ICMP error message. If it
* cares, the MN will regenerate, perhaps to a
* fallback HA.
*
* Return error 88 home agent unreachable.
*/
mipverbose(("Home Agent Unreachable.\n"));
break;
default:
/*
* E.g. ICMP_UNREACH_PROTOCOL, ICMP_UNREACH_NEEDFRAG,
* ICMP_UNREACH_SRCFAIL - catch them here (OK, the last
* two are a bit of a reach). Also, anything "new" to
* ICMP will have to return something generic for now.
*
* Return error 88 home agent unreachable.
*/
mipverbose(("Unreachable default:"
" Home Agent Unreachable.\n"));
break;
} /* switch(icmpPtr->code) */
/* bump our counters */
/* only one ICMP type, so... */
(void) rejectFromICMPToMN(messageHdr,
/* There can be only one reason, we're done */
return;
}
/* Don't forget timeouts */
"Received ICMP error in response to regreQ from MN: "));
/* The error is based on the ICMP message... */
case ICMP_TIMXCEED_INTRANS:
case ICMP_TIMXCEED_REASS:
mipverbose(("timeout-88: Home Agent Unreachable.\n"));
break;
default:
/* Not the format of an ICMP_TIMXCEED, but still... */
mipverbose(("timeout: default"
"88: Home Agent Unreachable.\n"));
break;
}
/* bump our counters */
/* only one reason to reject... */
(void) rejectFromICMPToMN(messageHdr,
}
/*
* Think: ICMP_REDIRECT{NET,HOST,TOSNET,TOSHOST} should be handled
* in-stack before we see it. ^^^^^^ ^^^^^^^
*/
/* If we made it here, we don't care about this ICMP type; done... */
return;
} /* screenICMPpkt() */
/*
* Function: screenUDPpkt
*
* Arguments: messageHdr - Pointer to a Message Control Block
*
* Description: Pkt contains a complete UDP datagram received on
* a socket monitoring UDP packets.
*
* Returns:
*/
static void
{
unsigned char *cp;
char addrstr1[INET_ADDRSTRLEN];
char addrstr2[INET_ADDRSTRLEN];
switch (messageHdr->ifType) {
case ON_UNICAST_SOCK:
break;
case ON_MCAST_SOCK:
break;
case ON_BCAST_SOCK:
break;
default:
break;
}
switch (*cp) {
case REG_REQUEST_TYPE:
"too short.");
return;
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
/*
* Here is what we need to do. If the Home Agent Address
* matches our interface address, or the packet is a broadcast,
* then we process it as a Home Agent. However, if the packet's
* care of address matches our address, or if it is a
* multicast, then we process the packet as a Foreign Agent.
*/
mipverbose(("req.haddr=0x%08x, inAddr=0x%08x, coAddr=0x%08x, ",
is_fa ? 1 : 0));
if (ha_match)
mipverbose((" we are *the* HA\n"));
if (fa_match)
mipverbose((" we are *the* FA\n"));
/*
* Check out the matching criterias to determine if we are
* acting as the HA or FA for this request. If we match the
* request as the HA, process it as HA. If we match the request
* as the FA, process it as FA. In case we are acting as HA but
* the request doesn't match neither our HA nor FA, let's go
* ahead and process this request as an HA and deny it.
* Similarly, in case we are acting as FA but the request
* doesn't match neither our FA nor HA, let's go ahead and
* process this request as an FA and deny it.
*/
/*
* This occurs when the HA address in the registration
* request is invalid, which occurs when AAA is used
* and the Home Agent's address is set to either zero
* or 0xffffffff in the registration Request. In this
* case, we need to update the Home Agent's address
* in the Registration Reply.
*
* MIP_PKT_FROM_RADIUS is Radius specific. In this
* case a call is made to HAprocessRegRequestRadius.
*/
(void) HAdispatchRadius(messageHdr,
/* process as FA */
&inAddr);
/* process as HA */
&inAddr);
/*
* MIP_PKT_FROM_AAA - for all other aaa protocols.
* This also occurs when the HA address in the reg
* request is invalid, which occurs when AAA is used
* and the Home Agent's address is set to either zero
* or 0xffffffff in the registration Request. In this
* case, we need to update the Home Agent's address
* in the Registration Reply.
*/
&inAddr);
} else {
"Not configured to handle this reg req on addr " \
"%s from %s.",
addrstr2));
}
break;
case REG_REPLY_TYPE:
"short.");
return;
}
break;
default:
"Unknown UDP message (first byte 0x%x) from %s.",
}
}
/*
* Function: processMsgHdr
*
* Description: This is the child thread that is called by
* the thread management sub-system to handle
* a specific Message Control Block. This function
* will remain in a while loop waiting for messages
* to handle.
*
* This function never returns, but can be killed by
* another thread calling tq_shutdown().
*
* Returns: void pointer of NULL. Note this is not really necessary,
* but makes thq.c/h prototypes happy.
*/
static void *
{
/*
* Main while loop... should never exit
*/
/* CONSTCOND */
while (_B_TRUE) {
if (messageHdr == NULL)
continue;
switch (messageHdr->pktType) {
case PKT_UDP:
break;
case PKT_ICMP:
break;
default:
break;
}
}
return (NULL);
}
/*
* Function: getAndDispatchNetworkPacket
*
* Arguments:
*
* Description: This is the main dispatch thread. This
* function has two main functions. First it will
* use select to determine if any data has been
* received on our sockets. Second, the select
* function is called with a timeout, which is
* used to determine when we need to send router
* advertisements.
*
* This function never returns, but will be killed
* if killDispatcherTaskThread() is called.
*
* Returns:
*/
static void *
{
int i, nentry;
int result;
time_t nextAdvTime = 0;
time_t lowestNextAdvTime = 0;
struct hash_table *htbl;
struct hash_entry *p;
htbl = &maAdvConfigHash;
/*
* Setup the initial advertisement time.
*/
/*
* We will save the file descriptor vector for future use.
* This saves us the trouble of having to skip through the
* while interface entry each time.
*
* NOTE: Since this block of code is called during initial static
* interface configuration, we do not need to lock the config
* entries here as the sequence at startup is startDispatcherTaskThread
* followed by startDynamicInterfaceThread.
*/
for (i = 0, nentry = 0;
while (p != NULL) {
nentry++;
&saved_fdvec);
&saved_fdvec);
}
/* Update maNextAdvtime to advertise now */
p = p->next;
}
}
for (;;) {
/*
* Take a snapshot of the file descriptors.
*/
fdvec = saved_fdvec;
/*
* What time is it?
*/
/*
* Wait on select for next adv or input or when
* there is no static interface entry in the config file
*/
/*
* adv n sec later, then advertisement for new
* interface waits until select times out. So, in
* some cases, the first adv does not happen instantly.
* Recommended AdvFrequency is 2-3 sec for dynamic
* interfaces.
*/
if (result < 0) {
/*
* EBADF can be often set
* from select in situations when
* the socket fd's corresponding
* to the dynamic interface is gone
* while select was in sleep.
* This situation can happen often
* as the connection comes and goes.
* Assuming the mipagent sets
* FD_SET correctly, at this point
* it is best not to print syslog
* error for the EBADF case to avoid
* plenty of such messages in the
* console.
*/
"select failed with error: %m");
}
continue;
}
/*
* Now that we've slept, let's get our current time.
*/
} else {
/*
* It looks like it's time to advertise, so we set
* result to zero. This will ensure that we will not
* attempt to receive a network packet.
*/
result = 0;
}
/*
* Let's check if it is time to start advertising
*/
if (currentTime >= nextAdvTime) {
/*
* Check each entry to see if it's time for at least
* one entry to advertise. lowestNextAdvTime keeps
* track of lowest NextAdvTime value of the traversed
* entries. During eachtime we check the entries to
* advertise, lowestNextAdvTime is updated to a
* lower value if the current entry's nextAdvTime is
* lower than the nextAdvTime local variable.
*/
lowestNextAdvTime = 0;
for (i = 0, nentry = 0;
while (p) {
nentry++;
(void) rw_rdlock(
&entry->maIfaceNodeLock);
if (currentTime >=
entry->maNextAdvTime) {
/* Advertise now */
if (faVisitorHash.size <=
} else if (faVisitorHash.size >=
}
if (entry->maAdvInitCount > 0) {
faBusy);
}
}
/*
* Set next timeout equal to the minimum
* time left for next advertisement for
* an entry. The following comparison is
* true when nextAdvTime is updated by
* interval and there is another entry
* in the list which needs to advertise
* before nextAdvTime.
*/
if (entry->maAdvInitCount > 0) {
if (lowestNextAdvTime == 0) {
/* First pass */
}
if (nextAdvTime >
} else {
}
}
p = p->next;
(void) rw_unlock(
&entry->maIfaceNodeLock);
}
}
/*
* If for some reason there is no entry in the
* Confighash table and nextAdvTime was not
* updated at all, then set the nextAdvTime to
* to default value to avoid looping. This block
* can also be exercized if there is no periodic
* advertisment is done as we may have set
* AdvLimitUnsolicited to all advertising interfaces.
*/
if (currentTime >= nextAdvTime) {
}
} else if (result > 0) {
}
}
/* LINTED E_STMT_NOT_REACHED */
return (NULL);
}
/*
* Function: find_ancillary
* Arguments: msg - contains the ancillary information
* cmsg_type - type of ancillary data
* Description: Return a pointer to the specified option buffer.
* If not found return NULL.
*/
static void *
{
}
}
return (NULL);
}
/*
* Function: recvNetworkPacket
*
* Arguments: socket - Socket on which data is pending
* entry - Interface on which the data is pending
* ifType - Whether the interface if UNICAST, BCAST or MCAST
* packetType - Wether the packet is UDP or ICMP
*
* Description: This function is called if data is pending
* on one of our sockets. This function will
* allocate a Messge Control Block, receive
* the data and call the function that dispatches
* the control block to a thread.
*
* Returns:
*/
static void
{
struct sockaddr_in from;
/*
* Allocate a Message Header.
*/
"Unable to allocate a message header");
return;
}
== (unsigned int) (-1)) {
"recvmsg failed for UDP on "
} else {
unsigned char *cp;
/*
* If it's a registration request then we need to get the
* ancillary information.
* TODO: 1. Fix this section of code to IP_RECVIF
* for ICMP packets too.
* 2. Add kernel support for IP_RECVSLLA for ICMP
* packets. This is required for response to
* unicast address for agent solicitation
*/
/*
* If this is an PPP interface, don't attempt to
* get the slla.
*/
} else {
/* try to extract slla from the packet */
"receving IP_RECVSLLA ancillary"
/*
* IP_RECVSLLA could fail for various
* reasons:
* a> the interface is no-resolver type
* (eg PPP)
* b> kernel ran out of memory
* c> the information the driver passed
* to IP is bogus
* we could return from here but this
* info (i.e SLLA) is non-critical, so
* we don't. Instead we mark the entry
* as invalid so it's not used in the
* code. Currently the entry is used to
* prevent the FA from broadcast ARPing
*/
} else {
sizeof (struct sockaddr_dl));
}
}
"failed");
return;
}
/* LINTED BAD_PTR_CAST_ALIGN */
"failed");
return;
}
} else {
/*
* Make sure they are initialized, just in case
*/
messageHdr->inIfindex = 0;
}
messageHdr->src =
(void) dispatchMsgToThread(&messageHdr);
}
}
/*
* Function: getFirstInterface
*
* Arguments: none
*
* Description: This function will return the first interface in the hash
* table. Todo: Pick the closest interface
*
* Returns: MaAdvConfigEntry *
*/
{
HashEntry *p;
int i, nentry;
/*
* for each mobility supporting interface ...
*/
htbl = &maAdvConfigHash;
for (i = 0, nentry = 0;
if (p) {
nentry++;
return (entry);
}
}
return (NULL);
} /* getFirstInterface */
/*
* Function: getClosestInterfaceAddr
*
* Arguments: ipaddr_t dest
*
* Description: This routine will return the nerest interface for reaching
* the dest. (For now, it will simply lookup the ip address
* of our first mobility interface.) Todo!
*
* Returns: ipaddr_t (0 on error)
*/
/* LINTED E_FUNC_ARG_UNUSED */
{
/*
* for each mobility supporting interface ...
*/
entry = getFirstInterface();
if (entry)
return (entry->maIfaceAddr);
else
return (0);
} /* getClosestInterfaceAddr */
/*
* Function: processIncomingMessage
*
* Arguments: htbl - Pointer to the Hash Table
* fdvec - Pointer to file descriptor
*
* Description: This function is called if data is pending
* on one of our sockets. This function loop
* through the interfaces looking for the socket
* on which data is pending.
*
* Returns:
*/
static void
{
HashEntry *p;
int i, nentry;
/*
* for each mobility supporting interface ...
*/
htbl = &maAdvConfigHash;
for (i = 0, nentry = 0;
while (p) {
nentry++;
/*
* process data received on ICMP socket
*/
}
}
/*
* ... process data on UDP socket bound to
* unicast address
*/
}
/*
* mipagent doesn't listen to broadcast sockets if
* this is a PPP interface.
*/
/*
* ... process data on UDP socket bound to
* directed broadcast address
*/
fdvec)) {
}
/*
* ... process data on UDP socket bound to
* broadcast address
*/
}
}
/*
* ... process data on UDP socket bound to
* multicast address
*/
}
p = p->next;
}
}
}
/*
* Function: doDynamicInterfaceProcess
* Argument:
* Description:
* This process is launched by dynamic interface thread. It's purpose
* is to hang around and poll to check if any new interface comes up.
* It calls process_rtsock_msg() for any valid RTM_INFO.
*
* Returns : NULL
*/
/* ARGSUSED */
static void *
doDynamicInterfaceProcess(void * arg)
{
int s; /* Routing socket id */
int ret;
/* Open a socket to send ICOTL cmd down by rtsock_process_msg */
if (ioc_sock < 0) {
return (NULL);
}
if (s == -1) {
"unable to open Routing socket for dynamic interface: %m");
return (NULL);
}
if (ret < 0) {
"fcntl failed on routing socket: %m");
(void) close(s);
return (NULL);
}
for (;;) {
continue;
"Poll failed: %m");
(void) close(s);
return (NULL);
}
continue;
mipverbose(("doDynamicInterfaceProcess: "
"received message on RTsocket\n"));
}
}
/* LINTED E_STMT_NOT_REACHED */
return (NULL); /* Never reached */
}
/*
* Function: process_rtsock_msg
* Argument : int
* Description :
* process_rtsock_msg processes the RTM_INFO messages. It checks
* if the flag is IFF_UP for the specified interface index. It also
* checks that this is IPV4, non-loopback, non-logical interface. It
* makes all other necessary checks before establishing the new
* interface as mobility interface. When an interface is plumbed it
* receives two RTM_IFINFO messages per interface.
*/
static void
process_rtsock_msg(int rtsock)
{
#define RTSOCK_MAX_MSG 2048
int num;
struct sockaddr_in *sin;
if (num <= 0) {
/* No messages */
return;
}
return;
}
return;
/* Now process the RTM_INFO message */
mipverbose(("process_rtsock_msg: RTM_IFINFO for if_index %d\n",
/*
* Interface unplumbed ?
* Make sure we delete any unused entry.
*/
return;
}
/* Found the ifname, check if it's new */
/* We don't support logical interfaces */
mipverbose(("process_rtsock_msg: logical interface %s\n",
return;
}
/*
* Check if this entry belongs to any existing interfaces
* that were already configured at the time of mipagent
* startup. If any of those interfaces ifconfiged down and then
* ifconfig'ed up, we don't consider them as dynamic interface.
*/
return;
/* Check for IPv4 and multicast flag */
/* Interface disappeared ? */
mipverbose(("process_rtsock_msg: SIOCGLIFFLAGS failed\n"));
return;
}
mipverbose(("process_rtsock_msg: flag not IPV4 MULTICAST\n"));
return;
}
return;
}
/* Now lookup for existing dynamic interface entries */
/*
* num = 1 means this entry is a static one.
* num = 0 means this entry is a dynamic existing entry
* We only care about dynamic entries here.
*/
if (num == 1) {
mipverbose(("process_rtsock_msg: Uninteresting "
"static entry\n"));
return;
} else if (num == 0) {
/* The entry is down ? Is it unplumbed ? */
}
return;
}
mipverbose(("process_rtsock_msg: Interface is down?\n"));
return;
}
/*
* Create the dynamic interface entry into the
* ConfigHash table
*/
_B_TRUE) != 0) {
"Unable to create dynamic Interface: %m");
return;
}
/* Find my entry */
0)) == NULL) {
"Can't find dynamic entry in Hash table");
mipverbose(("process_rtsock_msg: Can't find dynamic "
"entry in Hash table\n"));
return;
}
if (InitSockets(ifentry) != 0) {
return;
}
/* Now set the FDs for advertisements */
if (!(ifflags & IFF_POINTOPOINT)) {
}
return;
}
mipverbose(("process_rtsock_msg: entry %s not found\n",
}
/*
* Function: Lookup_existing_entries
* Description: Looks for any existing entry in config table.
* returns : 1 if finds a static entry
* 0 if finds a dynamic entry
* -1 if none found
*/
static int
{
mipverbose(("lookup_existing_entries: "
"Can't find entry in Hash table for index %d\n",
index));
return (-1);
}
/* found an existing entry */
return (0);
} else {
return (1);
}
}
/*
* Function: match_dynamic_table
* Argument : interface name
*
* Description:
* Match the dynamic interface table to see if this interface
* is valid to become a dynamic mobility interface.
* dynamicIface list is handled by a single thread, thus
* we are not locking here.
* returns : DynamicIfceTypeEntry pointer
*/
static DynamicIfaceTypeEntry *
match_dynamic_table(char *ifname)
{
int typelen;
/* Matches devicename with dynamicIfacetype */
mipverbose(("match_dynamic_table: matched entry\n"));
return (dynentry);
}
}
return (NULL);
}
/*
* Function: delmaAdvConfigEntry
* Argument: Interface index
* Description: It can only take index, as there may be situation when
* is gone (ENXIO error). So, in those cases we have no other
* info in hand and thus we have to go through the whole
* table and delete the entry.
*/
static void
{
struct hash_entry *hash_entry;
struct hash_table *conf_htbl;
int count;
/*
* The following search does not provide high performance
* result. The confighash table needs to be hashed with key
* <interface-index>. Currently all hashtables are hashed by
* address. But when delmaAdvConfigEntry() is called from
* process_rtsock_msg() routine, interface might be unplumbed
* already and thus the addr cannot be known. So we have to do
* a long search to compare the interface index for each entry.
* There could be two solutions in future to address this:
* 1. hash configHash table with interface index
* 2. Or modify routing socket to return addr and flag etc with
* the routing socket message.
*/
while (hash_entry != NULL) {
index, 0, 0)) {
break;
}
}
}
if (found) {
} else {
mipverbose(("delmaAdvConfigEntry: "
"Can't find dynamic entry in Hash table\n"));
return;
}
/* close all sockets */
}
}
LOCK_NONE)) {
/* Success: Entry has been taken out of the table */
mipverbose(("delmaAdvConfigEntry: deleted config entry\n"));
}
}