diff options
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.lib/mipagent/agentNet.c')
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.lib/mipagent/agentNet.c | 2035 |
1 files changed, 2035 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentNet.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentNet.c new file mode 100644 index 0000000000..defae18ba6 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentNet.c @@ -0,0 +1,2035 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 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 <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/fcntl.h> +#include <sys/poll.h> +#include <sys/sockio.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <netinet/udp.h> +#include <arpa/inet.h> +#include <net/route.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 tqTp messageQueue = NULL; +static rwlock_t msgQueueLock; +static MessageHdr *msgHdrQueue = NULL; +static int allocatedMsgHdr = 0; +static pthread_t dispatchThreadId = 0; +static pthread_t DynamicThreadId = 0; +static fd_set saved_fdvec; +static struct rt_msghdr *rt_msg; +static struct if_msghdr *ifm; +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 boolean_t faBusy; +extern int visitorEntryHighWaterMark; +extern int visitorEntryLowWaterMark; + +static int lookup_existing_entries(ushort_t, ipaddr_t); +static void *processMsgHdr(); +static void *getAndDispatchNetworkPacket(void); +static void *doDynamicInterfaceProcess(void *); +static void process_rtsock_msg(int); +static void processIncomingMessage(HashTable *, fd_set *); +static void *find_ancillary(struct msghdr *msg, int cmsg_type); +static void delmaAdvConfigEntry(uint32_t); +static DynamicIfaceTypeEntry *match_dynamic_table(char *); + +extern char *ntoa(uint32_t, char *); +extern void maSendAdvertisement(MaAdvConfigEntry *entry, ipaddr_t dst, + int advType, boolean_t faBusy); +extern void FAprocessRegRequest(MessageHdr *, MaAdvConfigEntry *, ipaddr_t *); +extern void HAprocessRegRequest(MessageHdr *, MaAdvConfigEntry *, ipaddr_t *); +extern void FAprocessRegReply(MessageHdr *, MaAdvConfigEntry *); + +extern void rejectFromICMPToMN(MessageHdr *, ipaddr_t, int); + +extern void HAdispatchRadius(MessageHdr *messageHdr, MaAdvConfigEntry *entry, + ipaddr_t *inAddr); + +/* + * 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 +AllocateMessageHdrBlock() +{ + MessageHdr *messageHdr; + int i; + + /* + * Now we create the message control blocks... + */ +#if 0 + msgHdrQueue = (MessageHdr *)calloc(1, sizeof (MessageHdr) * + MAX_MSG_HDR_NUM); +#else + msgHdrQueue = (MessageHdr *)calloc(MAX_MSG_HDR_NUM, + sizeof (MessageHdr)); +#endif + + if (msgHdrQueue == NULL) { + syslog(LOG_CRIT, "Unable to allocate message header queue"); + return (_B_TRUE); + } + + for (i = 0, messageHdr = msgHdrQueue; i < MAX_MSG_HDR_NUM; i++) { + messageHdr->next = messageHdr + 1; + } + messageHdr->next = NULL; + + 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. + */ +MessageHdr * +AllocateMessageHdr() +{ + MessageHdr *messageHdr = NULL; + + /* + * 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()) { + syslog(LOG_CRIT, "Unable to allocate more message queues"); + return (NULL); + } + } + + /* + * Now get the message header to return + */ + messageHdr = msgHdrQueue; + msgHdrQueue = msgHdrQueue->next; + allocatedMsgHdr++; + + /* + * Unlock the queue + */ + (void) rw_unlock(&msgQueueLock); + + /* + * Initialize the NAI stuff in the message header. + */ + messageHdr->mnNAILen = 0; + messageHdr->mnNAI = NULL; + + messageHdr->faNAILen = 0; + messageHdr->faNAI = NULL; + + messageHdr->dontDeleteNow = _B_FALSE; + +#ifdef KEY_DISTRIBUTION + /* + * KEY_DISTRIBUTION MUST ONLY BE COMPILED FOR TESTING!!! + * + * This version of mipagent supports a AAA/DIAMETER + * 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. + * + * Since DIAMETER/AAA is not currently a product, and key + * 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; + messageHdr->kdcKeysPresent = _B_FALSE; +#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 +FreeMessageHdr(MessageHdr *messageHdr) +{ + /* + * 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 */ + if (messageHdr->faNAI != NULL) + free(messageHdr->faNAI); + /* + * Lock and put the item back on the queue + */ + (void) rw_wrlock(&msgQueueLock); + messageHdr->next = msgHdrQueue; + msgHdrQueue = messageHdr; + allocatedMsgHdr--; + (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 +startDispatcherTaskThread(void) +{ + pthread_attr_t pthreadAttribute; + int result; + + messageQueue = tq_alloc(processMsgHdr, NULL, + (void *) NULL, NULL, MIP_MAX_THREADS, MIP_MIN_THREADS, FALSE); + + if (!messageQueue) { + syslog(LOG_CRIT, "Unable to create thread queue"); + return (-1); + } + + (void) rw_wrlock(&msgQueueLock); + + if (AllocateMessageHdrBlock()) { + (void) rw_unlock(&msgQueueLock); + syslog(LOG_CRIT, "Unable to allocate message Queues"); + tq_shutdown(messageQueue, 1); + return (-1); + } + + (void) rw_unlock(&msgQueueLock); + + result = pthread_attr_init(&pthreadAttribute); + + if (result) { + syslog(LOG_CRIT, "Error Initializing pthread."); + tq_shutdown(messageQueue, 1); + return (-1); + } + + /* + * We now create a thread to deal with all periodic task. + */ + result = pthread_create(&dispatchThreadId, &pthreadAttribute, + (void *(*)()) getAndDispatchNetworkPacket, + (void *)NULL); + + if (result) { + syslog(LOG_CRIT, "pthread_create() failed."); + tq_shutdown(messageQueue, 1); + 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. + */ + result = pthread_detach(dispatchThreadId); + + if (result) { + syslog(LOG_CRIT, "pthread_detach() failed."); + (void) pthread_cancel(dispatchThreadId); + tq_shutdown(messageQueue, 1); + 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 +killDispatcherTaskThread() +{ + int result; + + if (dispatchThreadId) { + /* + * Next we need to kill the dispatching thread. + */ + result = pthread_cancel(dispatchThreadId); + + if (result) { + /* + * Well, there's not much we can do here.. + */ + syslog(LOG_CRIT, "Unable to kill dispatching thread"); + } + } + + if (messageQueue) { + /* + * First we kill all of the message handling threads. + */ + tq_shutdown(messageQueue, 1); + } + + 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 +startDynamicInterfaceThread(void) +{ + pthread_attr_t pthreadAttribute; + int result; + + result = pthread_attr_init(&pthreadAttribute); + + if (result != 0) { + syslog(LOG_CRIT, "Error Initializing pthread: %m"); + return (-1); + } + + /* + * We now create a thread to deal with processing new interfaces + */ + result = pthread_create(&DynamicThreadId, &pthreadAttribute, + doDynamicInterfaceProcess, NULL); + + if (result != 0) { + syslog(LOG_CRIT, "pthread_create() failed: %m"); + 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. + */ + result = pthread_detach(DynamicThreadId); + + if (result != 0) { + syslog(LOG_CRIT, "pthread_detach() failed: %m"); + return (-1); + } + + return (0); +} + + + +/* + * Function: killDynamicInterfaceThread + * Arguments: + * Description: This function is used for thread cleanup + * Returns 0 on success + */ +int +killDynamicInterfaceThread(void) +{ + int result; + + if (DynamicThreadId) { + /* + * Next we need to kill the dispatching thread. + */ + result = pthread_cancel(DynamicThreadId); + + if (result != 0) { + /* + * Well, there's not much we can do here.. + */ + syslog(LOG_CRIT, "Unable to kill Dynamic thread: %m"); + 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 +dispatchMsgToThread(MessageHdr **messageHdr) +{ + + if (*messageHdr == NULL) { + return (-1); + } + + if (tq_queue(messageQueue, *messageHdr)) { + syslog(LOG_CRIT, "Unable to dispatch message to thread"); + 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 +sendUDPmessage(int sock, unsigned char *pkt, int pktLen, + ipaddr_t dst, in_port_t dstPort) +{ + struct sockaddr_in sa; + + sa.sin_family = AF_INET; + sa.sin_port = htons(dstPort); + sa.sin_addr.s_addr = dst; + + /* send the message */ + if (sendto(sock, (char *)pkt, pktLen, 0, (struct sockaddr *)&sa, + sizeof (struct sockaddr_in)) < 0) { + syslog(LOG_ERR, "sendto() Sendto failed in sendUDPmessage."); + 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 +sendICMPmessage(int s, ipaddr_t dst, + unsigned char data[], int len) +{ + struct sockaddr_in sa; + + sa.sin_family = AF_INET; + sa.sin_port = 0; + sa.sin_addr.s_addr = dst; + + /* send the message */ + if (sendto(s, (char *)data, len, 0, (struct sockaddr *)&sa, + sizeof (struct sockaddr_in)) < 0) { + syslog(LOG_ERR, "sendto() Sendto failed in sendICMPmessage."); + } +} + + +/* + * 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 +screenICMPpkt(MessageHdr *messageHdr) +{ + icmph *icmpPtr; + struct ip *ipPtr, *inneripPtr; /* ICMPs outer and inner IP headers */ + struct udphdr *innerudpPtr; /* perchance the innerIP is UDP */ + char addrstr1[INET_ADDRSTRLEN]; + char addrstr2[INET_ADDRSTRLEN]; + uint16_t ipHdrLen, iError; + ipaddr_t *ipDst; + + if (messageHdr->pktLen < sizeof (struct ip) + sizeof (icmph)) { + syslog(LOG_ERR, "ICMP Packet received too small"); + return; + } + + /* LINTED E_BAD_PTR_CAST_ALIGN */ + ipPtr = (struct ip *)&messageHdr->pkt; + + ipHdrLen = (uint16_t)(ipPtr->ip_hl << 2); + + if (messageHdr->pktLen < ipHdrLen + sizeof (icmph)) { + syslog(LOG_ERR, "Packet received is smaller than reported"); + return; + } + + /* LINTED E_BAD_PTR_CAST_ALIGN */ + icmpPtr = (icmph *)((char *)messageHdr->pkt + ipHdrLen); + + ipDst = (ipaddr_t *)&ipPtr->ip_dst; + + /* + * Note: we don't have to check the ICMP size, and/or the ICMP + * 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. + */ + + if (icmpPtr->type == ICMP_ROUTERSOLICIT) { + mipverbose(( + "Got ICMP solicitation <type %d, code %d> from %s to %s.\n", + icmpPtr->type, icmpPtr->code, + ntoa(messageHdr->src, addrstr1), + ntoa(*ipDst, addrstr2))); + + if (icmpPtr->code == 0) { + /* Currently, the only code for type 10 solicits. */ + commonCounters.maSolicitationsRecvdCnt++; + + /* + * Restrict advertisement to the particular interface + * on which the solicitation was received. + */ + if (((messageHdr->ifEntry->maIfaceFlags & + IFF_POINTOPOINT) == 0) || + (messageHdr->src == INADDR_ANY)) { + /* + * Since we don't have ARP information at this + * point, advertise on Bcast or Mcast addr. + */ + maSendAdvertisement(messageHdr->ifEntry, + messageHdr->ifEntry->maAdvAddr, + SOLICITED_ADV, faBusy); + } else { + /* + * Solicitation from PPP interface with a + * non-zero source address. + */ + maSendAdvertisement(messageHdr->ifEntry, + messageHdr->src, SOLICITED_ADV, faBusy); + } + + /* + * should actually be manipulated inside + * maSendAdvertisement() + */ + commonCounters.maAdvSentForSolicitationsCnt++; + } + + /* 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... + */ + if (ipPtr->ip_dst.s_addr != messageHdr->ifEntry->maIfaceAddr) { + /* 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. + */ + if (messageHdr->pktLen < sizeof (struct ip) + sizeof (icmph) + + sizeof (struct udphdr)) { + /* if we can't get to the udp port info, we can't continue */ + syslog(LOG_ERR, + "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. */ + inneripPtr = (struct ip *)(icmpPtr+1); + + /* We should be the sender of the inner packet */ + if (inneripPtr->ip_src.s_addr != messageHdr->ifEntry->maIfaceAddr) { + /* We're not responsible for sending that packet */ + return; + } + + /* UDP? */ + if (inneripPtr->ip_p != IPPROTO_UDP) { + /* protocol in returned IP header isn't UDP - <zap> */ + return; + } + + /* OK, the encased IP packet allegedly carries UDP info - go there */ + ipHdrLen = (uint16_t)(inneripPtr->ip_hl << 2); + /* LINTED E_BAD_PTR_CAST_ALIGN */ + innerudpPtr = (struct udphdr *)((char *)inneripPtr + ipHdrLen); + + /* + * Swapping madness for the port value because this piece is RAW off + * the network. Note: htons() is a no-op on SPARC. + */ + if (htons(innerudpPtr->uh_dport) != MIP_PORT) { + /* 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. + */ + if (icmpPtr->type == ICMP_UNREACH) { + + /* All that info implies it was for something we sent out */ + mipverbose(( + "Received ICMP error in response to regreQ from MN: ")); + mipverbose(("Type: UNREACH, ICMPcode: %d.", icmpPtr->code)); + + /* The error to the MN is based on the ICMP code... */ + switch (icmpPtr->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")); + iError = FA_HA_NET_UNREACHABLE; + break; + + case ICMP_UNREACH_HOST: + case ICMP_UNREACH_HOST_UNKNOWN: + case ICMP_UNREACH_HOST_PROHIB: + /* return error 81 home agent host unreachable */ + mipverbose(("Home Agent IP Unreachable.\n")); + iError = FA_HA_HOST_UNREACHABLE; + break; + + case ICMP_UNREACH_PORT: + /* return error 82 home agent port unreachable */ + mipverbose(("Home Agent Port Unreachable.\n")); + iError = FA_HA_PORT_UNREACHABLE; + 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")); + iError = FA_HA_UNREACHABLE; + 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")); + iError = FA_HA_UNREACHABLE; + break; + + } /* switch(icmpPtr->code) */ + + /* bump our counters */ + faCounters.faRegRepliesICMPUnreachCnt++; + + /* only one ICMP type, so... */ + (void) rejectFromICMPToMN(messageHdr, + inneripPtr->ip_dst.s_addr, iError); + + /* There can be only one reason, we're done */ + return; + } + + /* Don't forget timeouts */ + if (icmpPtr->type == ICMP_TIMXCEED) { + mipverbose(( + "Received ICMP error in response to regreQ from MN: ")); + + mipverbose(("Type: TIMEOUT, ICMPcode: %d.", icmpPtr->code)); + + /* The error is based on the ICMP message... */ + switch (icmpPtr->code) { + case ICMP_TIMXCEED_INTRANS: + case ICMP_TIMXCEED_REASS: + mipverbose(("timeout-88: Home Agent Unreachable.\n")); + iError = FA_HA_UNREACHABLE; + break; + + default: + /* Not the format of an ICMP_TIMXCEED, but still... */ + mipverbose(("timeout: default" + "88: Home Agent Unreachable.\n")); + iError = FA_HA_UNREACHABLE; + break; + } + + /* bump our counters */ + faCounters.faRegRepliesICMPTimxceedCnt++; + + /* only one reason to reject... */ + (void) rejectFromICMPToMN(messageHdr, + inneripPtr->ip_dst.s_addr, iError); + } + + /* + * 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 +screenUDPpkt(MessageHdr *messageHdr) +{ + regRequest *regReqPtr; + unsigned char *cp; + char addrstr1[INET_ADDRSTRLEN]; + char addrstr2[INET_ADDRSTRLEN]; + ipaddr_t inAddr; + boolean_t ha_match, fa_match; + boolean_t is_ha, is_fa; + + switch (messageHdr->ifType) { + case ON_UNICAST_SOCK: + inAddr = messageHdr->ifEntry->maIfaceAddr; + break; + + case ON_MCAST_SOCK: + inAddr = inet_addr(LINK_MCAST_REG_ADDR); + break; + + case ON_BCAST_SOCK: + inAddr = GENERATE_NET_BROADCAST_ADDR(messageHdr->ifEntry); + break; + default: + syslog(LOG_ERR, "screenUDPpkt: Unknown socket type \n"); + break; + + } + + cp = messageHdr->pkt; + + switch (*cp) { + + case REG_REQUEST_TYPE: + + if (messageHdr->pktLen < sizeof (regRequest)) { + syslog(LOG_ERR, "Received registration request is " \ + "too short."); + return; + } + /* LINTED E_BAD_PTR_CAST_ALIGN */ + regReqPtr = (regRequest *) messageHdr->pkt; + + /* + * 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. + */ + is_ha = (messageHdr->ifEntry->maAdvServiceFlags & + ADV_IS_HOME_AGENT); + ha_match = (is_ha && ((regReqPtr->haAddr == inAddr) || + (messageHdr->ifType == ON_BCAST_SOCK))); + + is_fa = (messageHdr->ifEntry->maAdvServiceFlags & + ADV_IS_FOREIGN_AGENT); + fa_match = (is_fa && ((regReqPtr->COAddr == inAddr) || + (messageHdr->ifType == ON_MCAST_SOCK))); + + mipverbose(("req.haddr=0x%08x, inAddr=0x%08x, coAddr=0x%08x, ", + regReqPtr->haAddr, inAddr, regReqPtr->COAddr)); + mipverbose(("HAFlag = %d, FAFlag = %d, ", is_ha ? 1 : 0, + 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. + */ + if (messageHdr->pktSource == MIP_PKT_FROM_RADIUS) { + /* + * 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. + */ + regReqPtr->haAddr = messageHdr->ifEntry->maIfaceAddr; + (void) HAdispatchRadius(messageHdr, + messageHdr->ifEntry, &inAddr); + } else if (fa_match || (is_fa && !ha_match)) { + /* process as FA */ + FAprocessRegRequest(messageHdr, messageHdr->ifEntry, + &inAddr); + } else if (ha_match || (is_ha && !fa_match)) { + /* process as HA */ + HAprocessRegRequest(messageHdr, messageHdr->ifEntry, + &inAddr); + } else if (messageHdr->pktSource == MIP_PKT_FROM_AAA) { + /* + * 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. + */ + regReqPtr->haAddr = messageHdr->ifEntry->maIfaceAddr; + HAprocessRegRequest(messageHdr, messageHdr->ifEntry, + &inAddr); + } else { + syslog(LOG_ERR, + "Not configured to handle this reg req on addr " \ + "%s from %s.", + ntoa(inAddr, addrstr1), ntoa(messageHdr->src, + addrstr2)); + } + break; + + case REG_REPLY_TYPE: + if (messageHdr->pktLen < sizeof (regReply)) { + syslog(LOG_ERR, "Received registration reply is too " \ + "short."); + return; + } + FAprocessRegReply(messageHdr, messageHdr->ifEntry); + break; + + default: + syslog(LOG_ERR, + "Unknown UDP message (first byte 0x%x) from %s.", + *cp, ntoa(messageHdr->src, addrstr1)); + } +} + +/* + * 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 * +processMsgHdr() +{ + MessageHdr *messageHdr; + + /* + * Main while loop... should never exit + */ + /* CONSTCOND */ + while (_B_TRUE) { + messageHdr = (MessageHdr *) tq_dequeue(messageQueue, 0); + + if (messageHdr == NULL) + continue; + + switch (messageHdr->pktType) { + case PKT_UDP: + screenUDPpkt(messageHdr); + break; + case PKT_ICMP: + screenICMPpkt(messageHdr); + break; + default: + syslog(LOG_ERR, "processMsgHdr: Unknown Pkt type\n"); + break; + } + + FreeMessageHdr(messageHdr); + } + + 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 * +getAndDispatchNetworkPacket() +{ + int i, nentry; + int result; + static fd_set fdvec; + static struct timeval tv; + time_t nextAdvTime = 0; + time_t currentTime; + time_t lowestNextAdvTime = 0; + struct hash_table *htbl; + MaAdvConfigEntry *entry; + struct hash_entry *p; + + htbl = &maAdvConfigHash; + + /* + * Setup the initial advertisement time. + */ + GET_TIME(currentTime); + nextAdvTime = currentTime; + + /* + * 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; + i < HASH_TBL_SIZE && (nentry < htbl->size); i++) { + p = htbl->buckets[i]; + while (p != NULL) { + nentry++; + entry = (MaAdvConfigEntry *)p->data; + FD_SET(entry->maIfaceUnicastSock, &saved_fdvec); + if ((entry->maIfaceFlags & IFF_POINTOPOINT) == 0) { + FD_SET(entry->maIfaceDirBcastSock, + &saved_fdvec); + FD_SET(entry->maIfaceBcastSock, + &saved_fdvec); + } + FD_SET(entry->maIfaceAdvMulticastSock, &saved_fdvec); + FD_SET(entry->maIfaceRegMulticastSock, &saved_fdvec); + FD_SET(entry->maIfaceIcmpSock, &saved_fdvec); + /* Update maNextAdvtime to advertise now */ + entry->maNextAdvTime = currentTime; + p = p->next; + } + } + + for (;;) { + /* + * Take a snapshot of the file descriptors. + */ + fdvec = saved_fdvec; + + /* + * What time is it? + */ + GET_TIME(currentTime); + + /* + * Wait on select for next adv or input or when + * there is no static interface entry in the config file + */ + if ((currentTime < nextAdvTime) || (nentry == 0)) { + tv.tv_sec = nextAdvTime - currentTime; + tv.tv_usec = 0; + + /* + * Caveat: Currently, if one static/dynamic interface + * 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. + */ + result = select(FD_SETSIZE, + &fdvec, NULL, NULL, &tv); + + if (result < 0) { + if ((errno != EINTR) && (errno != ERESTART) && + (errno != EBADF)) { + /* + * 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. + */ + syslog(LOG_ERR, + "select failed with error: %m"); + } + continue; + } + + /* + * Now that we've slept, let's get our current time. + */ + GET_TIME(currentTime); + } 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; + i < HASH_TBL_SIZE && (nentry < htbl->size); i++) { + p = htbl->buckets[i]; + while (p) { + nentry++; + entry = (MaAdvConfigEntry *)p->data; + (void) rw_rdlock( + &entry->maIfaceNodeLock); + if (currentTime >= + entry->maNextAdvTime) { + /* Advertise now */ + if (faVisitorHash.size <= + visitorEntryLowWaterMark) { + faBusy = _B_FALSE; + } else if (faVisitorHash.size >= + visitorEntryHighWaterMark) { + faBusy = _B_TRUE; + } + if (entry->maAdvInitCount > 0) { + maSendAdvertisement( + entry, + entry->maAdvAddr, + UNSOLICITED_ADV, + faBusy); + } + entry->maNextAdvTime = + currentTime + + entry->maAdvInterval; + } + /* + * 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) { + nextAdvTime = + entry->maNextAdvTime; + + if (lowestNextAdvTime == 0) { + /* First pass */ + lowestNextAdvTime = + nextAdvTime; + } + if (nextAdvTime > + lowestNextAdvTime) { + nextAdvTime = + lowestNextAdvTime; + } else { + lowestNextAdvTime = + nextAdvTime; + } + } + 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) { + nextAdvTime = currentTime + + DEFAULT_MIN_INTERVAL; + } + } else if (result > 0) { + processIncomingMessage(htbl, &fdvec); + } + } + /* 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 * +find_ancillary(struct msghdr *msg, int cmsg_type) +{ + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == cmsg_type) { + return (CMSG_DATA(cmsg)); + } + } + 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 +recvNetworkPacket(int socket, MaAdvConfigEntry *entry, uint32_t ifType, + uint32_t packetType) +{ + MessageHdr *messageHdr; + struct sockaddr_in from; + static uint64_t in_packet[(MAX_PKT_SIZE + 1)/8]; + static uint64_t ancillary_data[(MAX_PKT_SIZE + 1)/8]; + struct msghdr msg; + struct iovec iov; + uchar_t *opt; + + iov.iov_base = (char *)in_packet; + iov.iov_len = sizeof (in_packet); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = (struct sockaddr *)&from; + msg.msg_namelen = sizeof (from); + msg.msg_control = ancillary_data; + msg.msg_controllen = sizeof (ancillary_data); + + /* + * Allocate a Message Header. + */ + if ((messageHdr = AllocateMessageHdr()) == NULL) { + syslog(LOG_CRIT, + "Unable to allocate a message header"); + return; + } + + if ((messageHdr->pktLen = recvmsg(socket, &msg, 0)) + == (unsigned int) (-1)) { + FreeMessageHdr(messageHdr); + syslog(LOG_ERR, + "recvmsg failed for UDP on " + "%s socket...%s", ifTypeName[ifType], strerror(errno)); + } else { + unsigned char *cp; + + (void) memcpy(messageHdr->pkt, in_packet, MAX_PKT_SIZE); + + cp = messageHdr->pkt; + /* + * 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 ((packetType == PKT_UDP) && (*cp == REG_REQUEST_TYPE)) { + + /* + * If this is an PPP interface, don't attempt to + * get the slla. + */ + if (entry->maIfaceFlags & IFF_POINTOPOINT) { + messageHdr->isSllaValid = _B_FALSE; + } else { + /* try to extract slla from the packet */ + opt = find_ancillary(&msg, IP_RECVSLLA); + if (opt == NULL) { + syslog(LOG_ERR, + "receving IP_RECVSLLA ancillary" + "failed...%s", strerror(errno)); + /* + * 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 + */ + messageHdr->isSllaValid = _B_FALSE; + } else { + messageHdr->isSllaValid = _B_TRUE; + bcopy(opt, &messageHdr->slla, + sizeof (struct sockaddr_dl)); + } + } + + opt = find_ancillary(&msg, IP_RECVIF); + if (opt == NULL) { + syslog(LOG_ERR, "receving IP_RECVIF ancillary" + "failed"); + FreeMessageHdr(messageHdr); + return; + } + + /* LINTED BAD_PTR_CAST_ALIGN */ + messageHdr->inIfindex = *(uint_t *)opt; + + opt = find_ancillary(&msg, IP_RECVTTL); + if (opt == NULL) { + syslog(LOG_ERR, "receving IP_RECVTTL ancillary" + "failed"); + FreeMessageHdr(messageHdr); + return; + } + + messageHdr->ttl = *(uint8_t *)opt; + } else { + + /* + * Make sure they are initialized, just in case + */ + messageHdr->inIfindex = 0; + messageHdr->isSllaValid = _B_FALSE; + } + + messageHdr->src = + from.sin_addr.s_addr; + messageHdr->srcPort = + ntohs(from.sin_port); + messageHdr->ifEntry = entry; + messageHdr->ifType = ifType; + messageHdr->pktType = packetType; + messageHdr->pktSource = MIP_PKT_FROM_FA; + assert(!messageHdr->mnNAILen); + (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 * + */ +MaAdvConfigEntry * +getFirstInterface() +{ + HashTable *htbl; + MaAdvConfigEntry *entry; + HashEntry *p; + int i, nentry; + + /* + * for each mobility supporting interface ... + */ + htbl = &maAdvConfigHash; + + for (i = 0, nentry = 0; + i < HASH_TBL_SIZE && (nentry < htbl->size); i++) { + p = htbl->buckets[i]; + if (p) { + nentry++; + entry = (MaAdvConfigEntry *)p->data; + 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) + */ +ipaddr_t +/* LINTED E_FUNC_ARG_UNUSED */ +getClosestInterfaceAddr(ipaddr_t dest) +{ + MaAdvConfigEntry *entry; + + /* + * 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 +processIncomingMessage(HashTable *htbl, fd_set *fdvec) +{ + MaAdvConfigEntry *entry; + HashEntry *p; + int i, nentry; + + /* + * for each mobility supporting interface ... + */ + htbl = &maAdvConfigHash; + + for (i = 0, nentry = 0; + i < HASH_TBL_SIZE && (nentry < htbl->size); i++) { + p = htbl->buckets[i]; + while (p) { + nentry++; + entry = (MaAdvConfigEntry *)p->data; + + /* + * process data received on ICMP socket + */ + if (FD_ISSET(entry->maIfaceIcmpSock, fdvec)) { + recvNetworkPacket(entry->maIfaceIcmpSock, + entry, ON_BCAST_SOCK, PKT_ICMP); + } + + if (FD_ISSET(entry->maIfaceAdvMulticastSock, fdvec)) { + recvNetworkPacket( + entry->maIfaceAdvMulticastSock, + entry, ON_MCAST_SOCK, PKT_ICMP); + } + + /* + * ... process data on UDP socket bound to + * unicast address + */ + if (FD_ISSET(entry->maIfaceUnicastSock, fdvec)) { + recvNetworkPacket(entry->maIfaceUnicastSock, + entry, ON_UNICAST_SOCK, PKT_UDP); + } + + /* + * mipagent doesn't listen to broadcast sockets if + * this is a PPP interface. + */ + if ((entry->maIfaceFlags & IFF_POINTOPOINT) == 0) { + /* + * ... process data on UDP socket bound to + * directed broadcast address + */ + if (FD_ISSET(entry->maIfaceDirBcastSock, + fdvec)) { + recvNetworkPacket( + entry->maIfaceDirBcastSock, + entry, ON_BCAST_SOCK, PKT_UDP); + } + + /* + * ... process data on UDP socket bound to + * broadcast address + */ + if (FD_ISSET(entry->maIfaceBcastSock, fdvec)) { + recvNetworkPacket( + entry->maIfaceBcastSock, + entry, ON_BCAST_SOCK, PKT_UDP); + } + } + + /* + * ... process data on UDP socket bound to + * multicast address + */ + if (FD_ISSET(entry->maIfaceRegMulticastSock, fdvec)) { + recvNetworkPacket( + entry->maIfaceRegMulticastSock, + entry, ON_MCAST_SOCK, PKT_UDP); + } + 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; + struct pollfd pollfds[1]; + + /* Open a socket to send ICOTL cmd down by rtsock_process_msg */ + ioc_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (ioc_sock < 0) { + syslog(LOG_CRIT, "Can't open IOC socket: %m"); + return (NULL); + } + s = socket(PF_ROUTE, SOCK_RAW, AF_INET); + if (s == -1) { + syslog(LOG_CRIT, + "unable to open Routing socket for dynamic interface: %m"); + (void) close(ioc_sock); + return (NULL); + } + ret = fcntl(s, F_SETFL, O_NDELAY|O_NONBLOCK); + if (ret < 0) { + syslog(LOG_CRIT, + "fcntl failed on routing socket: %m"); + (void) close(ioc_sock); + (void) close(s); + return (NULL); + } + + pollfds->fd = s; + pollfds->events = POLLIN; + + for (;;) { + if (poll(pollfds, 1, INFTIM) < 0 || + (pollfds->revents & POLLERR)) { + if (errno == EINTR) + continue; + syslog(LOG_CRIT, + "Poll failed: %m"); + (void) close(s); + (void) close(ioc_sock); + return (NULL); + } + if (!pollfds->revents & POLLIN) + continue; + if (pollfds->fd == s) { + mipverbose(("doDynamicInterfaceProcess: " + "received message on RTsocket\n")); + process_rtsock_msg(s); + } + } + /* 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 + + int64_t msg[RTSOCK_MAX_MSG / sizeof (int64_t)]; + int num; + struct lifreq lifr; + ipaddr_t addr; + struct sockaddr_in *sin; + DynamicIfaceTypeEntry *d_entry; + MaAdvConfigEntry *ifentry; + uint64_t ifflags; + time_t currentTime; + + num = (int)read(rtsock, msg, sizeof (msg)); + if (num <= 0) { + /* No messages */ + return; + } + rt_msg = (struct rt_msghdr *)msg; + if (rt_msg->rtm_version != RTM_VERSION) { + syslog(LOG_CRIT, "Bad RTM version: %d", rt_msg->rtm_version); + return; + } + + if (rt_msg->rtm_type != RTM_IFINFO) + return; + + /* Now process the RTM_INFO message */ + ifm = (struct if_msghdr *)rt_msg; + mipverbose(("process_rtsock_msg: RTM_IFINFO for if_index %d\n", + ifm->ifm_index)); + + (void) memset(&lifr, 0, sizeof (struct lifreq)); + if (if_indextoname(ifm->ifm_index, lifr.lifr_name) == NULL) { + /* + * Interface unplumbed ? + * Make sure we delete any unused entry. + */ + (void) delmaAdvConfigEntry(ifm->ifm_index); + return; + } + /* Found the ifname, check if it's new */ + + if (strchr(lifr.lifr_name, ':') != NULL) { + /* We don't support logical interfaces */ + mipverbose(("process_rtsock_msg: logical interface %s\n", + lifr.lifr_name)); + 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. + */ + if (existingStaticInterface(lifr.lifr_name)) + return; + + /* Check for IPv4 and multicast flag */ + if ((int)ioctl(ioc_sock, SIOCGLIFFLAGS, (char *)&lifr) < 0) { + /* Interface disappeared ? */ + mipverbose(("process_rtsock_msg: SIOCGLIFFLAGS failed\n")); + return; + } + if (!(lifr.lifr_flags & IFF_IPV4) || + !(lifr.lifr_flags & IFF_MULTICAST)) { + mipverbose(("process_rtsock_msg: flag not IPV4 MULTICAST\n")); + return; + } + + ifflags = lifr.lifr_flags; + + if (ioctl(ioc_sock, SIOCGLIFADDR, (caddr_t)&lifr) < 0) { + syslog(LOG_ERR, "Could not read IP address for" + "%s: %m", lifr.lifr_name); + return; + } + sin = (struct sockaddr_in *)&lifr.lifr_addr; + addr = sin->sin_addr.s_addr; + + /* Now lookup for existing dynamic interface entries */ + num = lookup_existing_entries(ifm->ifm_index, addr); + /* + * 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) { + if (!(ifflags & IFF_UP)) { + /* The entry is down ? Is it unplumbed ? */ + (void) delmaAdvConfigEntry(ifm->ifm_index); + } + return; + } + if (!(ifflags & IFF_UP)) { + mipverbose(("process_rtsock_msg: Interface is down?\n")); + return; + } + + if ((d_entry = match_dynamic_table(lifr.lifr_name)) != NULL) { + /* + * Create the dynamic interface entry into the + * ConfigHash table + */ + + if (CreateInterfaceEntry(lifr.lifr_name, + d_entry->RegLifetime, d_entry->advertiseOnBcast, + DEFAULT_MIN_INTERVAL, DEFAULT_MAX_INTERVAL, + d_entry->AdvLifetime, 0, d_entry->AdvServiceflag, + d_entry->AdvPrefixflag, d_entry->RevtunAllowed, + d_entry->RevtunReqd, d_entry->AdvLimitUnsolicited, + d_entry->AdvInitCount, d_entry->AdvInterval, + _B_TRUE) != 0) { + syslog(LOG_ERR, + "Unable to create dynamic Interface: %m"); + return; + } + /* Find my entry */ + + if ((ifentry = (MaAdvConfigEntry *)findHashTableEntryUint( + &maAdvConfigHash, addr, LOCK_WRITE, + ConfigEntryHashLookup, (uint32_t)ifm->ifm_index, 0, + 0)) == NULL) { + syslog(LOG_CRIT, + "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) { + syslog(LOG_CRIT, "InitSockets failed for dynamic" + " Interface %s", ifentry->maIfaceName); + (void) rw_unlock(&ifentry->maIfaceNodeLock); + return; + } + /* Now set the FDs for advertisements */ + GET_TIME(currentTime); + ifentry->maNextAdvTime = currentTime; + FD_SET(ifentry->maIfaceUnicastSock, &saved_fdvec); + if (!(ifflags & IFF_POINTOPOINT)) { + FD_SET(ifentry->maIfaceDirBcastSock, &saved_fdvec); + FD_SET(ifentry->maIfaceBcastSock, &saved_fdvec); + } + FD_SET(ifentry->maIfaceAdvMulticastSock, &saved_fdvec); + FD_SET(ifentry->maIfaceRegMulticastSock, &saved_fdvec); + FD_SET(ifentry->maIfaceIcmpSock, &saved_fdvec); + (void) rw_unlock(&ifentry->maIfaceNodeLock); + return; + } + mipverbose(("process_rtsock_msg: entry %s not found\n", + lifr.lifr_name)); +} + + +/* + * 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 +lookup_existing_entries(ushort_t index, ipaddr_t ifaddr) +{ + MaAdvConfigEntry * ifentry; + + ifentry = (MaAdvConfigEntry *)findHashTableEntryUint( + &maAdvConfigHash, ifaddr, LOCK_READ, ConfigEntryHashLookup, + (uint32_t)index, 0, 0); + + if (ifentry == NULL) { + mipverbose(("lookup_existing_entries: " + "Can't find entry in Hash table for index %d\n", + index)); + return (-1); + } + /* found an existing entry */ + if (ifentry->maAdvDynamicInterface == _B_TRUE) { + (void) rw_unlock(&ifentry->maIfaceNodeLock); + return (0); + } else { + (void) rw_unlock(&ifentry->maIfaceNodeLock); + 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) +{ + DynamicIfaceTypeEntry *dynentry; + int typelen; + + + dynentry = dynamicIfaceHead; + while (dynentry != NULL) { + typelen = strlen(dynentry->dynamicIfcetype); + if ((strncmp(ifname, dynentry->dynamicIfcetype, + (size_t)typelen) == 0) && + ((int)isdigit(ifname[typelen]) != 0)) { + /* Matches devicename with dynamicIfacetype */ + mipverbose(("match_dynamic_table: matched entry\n")); + return (dynentry); + } + dynentry = dynentry->next; + } + + return (NULL); +} + + +/* + * Function: delmaAdvConfigEntry + * Argument: Interface index + * Description: It can only take index, as there may be situation when + * it's too late to find name/addr info because the interface + * 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 +delmaAdvConfigEntry(uint32_t index) +{ + + MaAdvConfigEntry *ifentry; + struct hash_entry *hash_entry; + struct hash_table *conf_htbl; + int count; + boolean_t found = _B_FALSE; + ipaddr_t addr; + + + conf_htbl = &maAdvConfigHash; + + /* + * 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. + */ + + for (count = 0; count < HASH_TBL_SIZE && !found; count++) { + hash_entry = conf_htbl->buckets[count]; + while (hash_entry != NULL) { + if (ConfigEntryHashLookup(hash_entry->data, + index, 0, 0)) { + (void) rw_wrlock((rwlock_t *)hash_entry->data); + found = _B_TRUE; + break; + } + hash_entry = hash_entry->next; + } + } + if (found) { + ifentry = hash_entry->data; + addr = ifentry->maIfaceAddr; + } else { + mipverbose(("delmaAdvConfigEntry: " + "Can't find dynamic entry in Hash table\n")); + return; + } + /* close all sockets */ + FD_CLR(ifentry->maIfaceUnicastSock, &saved_fdvec); + FD_CLR(ifentry->maIfaceAdvMulticastSock, &saved_fdvec); + FD_CLR(ifentry->maIfaceRegMulticastSock, &saved_fdvec); + FD_CLR(ifentry->maIfaceIcmpSock, &saved_fdvec); + (void) close(ifentry->maIfaceUnicastSock); + if (ifentry->maIfaceDirBcastSock > -1) { + FD_CLR(ifentry->maIfaceDirBcastSock, &saved_fdvec); + (void) close(ifentry->maIfaceDirBcastSock); + } + if (ifentry->maIfaceBcastSock > -1) { + FD_CLR(ifentry->maIfaceBcastSock, &saved_fdvec); + (void) close(ifentry->maIfaceBcastSock); + } + (void) close(ifentry->maIfaceAdvMulticastSock); + (void) close(ifentry->maIfaceRegMulticastSock); + (void) close(ifentry->maIfaceIcmpSock); + + if (delHashTableEntryUint(&maAdvConfigHash, ifentry, addr, + LOCK_NONE)) { + /* Success: Entry has been taken out of the table */ + (void) rw_unlock(&(ifentry->maIfaceNodeLock)); + (void) rwlock_destroy(&(ifentry->maIfaceNodeLock)); + free(ifentry); + mipverbose(("delmaAdvConfigEntry: deleted config entry\n")); + } +} |