/* * 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-2002 Sun Microsystems, Inc. * All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * file: agentPeriodic.c * * This file includes the routines that are periodically called * for agent advertisement and garbage collection of data structures * such as visitor entries, mobile node entries, security * assocations, etc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "mip.h" #include "agent.h" #include "pool.h" #include "agentKernelIntfce.h" #include "conflib.h" #include "mipagentstat_door.h" static pthread_t periodicThreadId = 0; /* global variables declared here */ boolean_t faBusy = _B_FALSE; /* * if need to disable mobility service, set * advBusy so that agent advertisement B(usy) * Bit is set. This informs MNs not to make * a registration request per RFC2002. */ boolean_t advBusy = _B_FALSE; extern boolean_t faNAIadv; /* Determines whether we advertise */ /* our NAI */ extern boolean_t faChallengeAdv; /* Determines whether we advertise */ /* challenges */ /* * Common to all mobility agents */ extern struct hash_table maAdvConfigHash; /* Counters common to all Mobility Agents */ extern CommonCounters commonCounters; extern char maNai[MAX_NAI_LENGTH]; /* Foreign Agent specific data structures. */ extern struct hash_table faVisitorHash; /* Counters maintained by Foreign Agents */ extern ForeignAgentCounters faCounters; /* The last two challenges advertised */ extern char faLastChallengeIssued[2][ADV_CHALLENGE_LENGTH]; /* Home Agent specific data structures. */ extern struct hash_table haMobileNodeHash; /* * This table has one entry for each known Mobility Agent */ extern struct hash_table mipAgentHash; /* * This table stores all of the Security Associations */ extern HashTable mipSecAssocHash; #ifdef FIREWALL_SUPPORT extern DomainInfo domainInfo; #endif /* FIREWALL_SUPPORT */ /* Other external declarations */ extern int logVerbosity; extern int advLifetime; extern int periodicInterval; extern AAA_Protocol_Code aaaProtocol; extern unsigned short inChecksum(unsigned short *, int); extern char *ntoa(uint32_t, char *); extern void sendICMPmessage(int, ipaddr_t, unsigned char *, int); extern char *err2str(int); #ifdef FIREWALL_SUPPORT extern boolean_t isOutsideProtectedDomain(uint32_t); #endif /* FIREWALL_SUPPORT */ extern uint32_t getRandomValue(); extern int prefixLen(uint32_t); extern int arpIfdel(ipaddr_t, char *, uint32_t); extern int gettunnelno(ipaddr_t, ipaddr_t); extern MobilityAgentEntry *findMaeFromIp(ipaddr_t, int); extern int removeIPsecPolicy(char *); #define LOWEST_ROUTER_PRIORITY 0x80000000 static void doPeriodicTask(); void maSendAdvertisement(MaAdvConfigEntry *, ipaddr_t, int, boolean_t faBusy); /* * Function: disableService * * Arguments: * * * Description: Function will set a flag to indicate * that all agent advertisements on all * interfaces will have the B(usy) Bit * set. In addition, the Agent Advertisement * sequence number for all interfaces will * be set to zero. This function is called * to disable mobility service while keeping * the mobility agent up and running. NOTE: A * corresponding function enableService() * is called to reenable mobility service. * * Returns: */ void disableService(struct hash_table *htbl) { int index, nentry; MaAdvConfigEntry *entry; struct hash_entry *p; advBusy = _B_TRUE; for (index = 0, nentry = 0; index < HASH_TBL_SIZE && (nentry < htbl->size); index++) { p = htbl->buckets[index]; while (p) { nentry++; entry = (MaAdvConfigEntry *)p->data; entry->maAdvSeqNum = 0; p = p->next; } } } /* * Function: enableService * * Arguments: * * Description: Function will set a flag to indicate * that all agent advertisements on all * interfaces should not have the * B(usy) bit set. * * Returns: */ void enableService() { advBusy = _B_FALSE; } /* * Function: maSendAdvertisement * * Arguments: entry - Pointer to MaAdvConfigEntry * dst - Destination addr of the advertisement * AdvType - Type of advertisement -solicited or * unsolicited * faBusy - boolean dictating whether we are * busy. * * Description: Prepare and send agent advertisement on * specified interface. * * Returns: */ void maSendAdvertisement(MaAdvConfigEntry *entry, ipaddr_t dst, int advType, boolean_t faBusy) { int j; static unsigned char advPkt[512]; int naiLength; uint32_t randomValue; unsigned char *randomValuePtr; int len; icmph *icmpPtr; ipaddr_t *addrPtr; advExt *advExtPtr; unsigned char *cp; /* We don't do unsolicited adv if AdvInitCount = 0 */ if (entry->maAdvInitCount == 0 && advType == UNSOLICITED_ADV) { mipverbose(("maSendAdvertisement: zero InitCount\n")); return; } /* Fill out the ICMP header for a router advertisement */ /* LINTED BAD_PTR_CAST_ALIGN */ icmpPtr = (icmph *) advPkt; icmpPtr->type = ICMP_ROUTERADVERT; icmpPtr->code = 16; icmpPtr->icmpAdvNumAddr = 0; icmpPtr->icmpAdvAddrEntrySize = 2; icmpPtr->icmpAdvLifetime = htons((uint16_t)advLifetime); /* create advertisement ... */ len = sizeof (icmph); /* LINTED BAD_PTR_CAST_ALIGN */ addrPtr = (uint32_t *)(advPkt + sizeof (icmph)); /* * ... fill (optional) addresses in the RFC 1256 portion * we only fill out one address. */ icmpPtr->icmpAdvNumAddr = 1; icmpPtr->icmpAdvAddrEntrySize = 2; *addrPtr++ = htonl(entry->maIfaceAddr); len += sizeof (uint32_t); *addrPtr++ = htonl(LOWEST_ROUTER_PRIORITY); len += sizeof (uint32_t); /* * ... fill out the mobility agent advertisement extension * we only advertise one address */ /* LINTED BAD_PTR_CAST_ALIGN */ advExtPtr = (advExt *) (advPkt + len); advExtPtr->type = ADV_EXT_TYPE; advExtPtr->length = 10; advExtPtr->seqNum = htons(entry->maAdvSeqNum); /* * We set the advertisement sequence number. Note that this * value wraps around at 255. This is used by Mobile Nodes * to determine whether the Foreign Agent has rebooted. */ if (++entry->maAdvSeqNum == 0) entry->maAdvSeqNum = 256; advExtPtr->regLifetime = htons(entry->maAdvMaxRegLifetime); advExtPtr->advFlags = entry->maAdvServiceFlags; /* set the busy bit, if appropriate */ if ((advBusy) || (faBusy && (advExtPtr->advFlags & ADV_IS_FOREIGN_AGENT))) { faCounters.faIsBusyCnt++; advExtPtr->advFlags |= ADV_IS_BUSY; } advExtPtr->reserved = 0; len += sizeof (advExt); /* fill out the advertised care-of address */ /* LINTED BAD_PTR_CAST_ALIGN */ addrPtr = (uint32_t *)(advPkt + len); *addrPtr++ = htonl(entry->maIfaceAddr); len += sizeof (uint32_t); /* * We only advertise our NAI if we were * configured to do so. */ if (faNAIadv == _B_TRUE) { /* Add the Mobility Agent NAI */ naiLength = strlen(maNai); cp = advPkt + len; *cp++ = ADV_AGENT_NAI_EXT_TYPE; *cp++ = (uchar_t)naiLength; len += 2; (void) strncpy((char *)cp, maNai, naiLength); len += naiLength; } /* * We only advertise challenges * if we were configured to do so. */ if (faChallengeAdv == _B_TRUE) { if (advType == UNSOLICITED_ADV) { /* Add the Foreign Agent Challenge Value */ cp = advPkt + len; *cp++ = ADV_CHALLENGE_EXT_TYPE; *cp++ = ADV_CHALLENGE_LENGTH; len += 2; /* * Save the last Challenge issued */ (void) memcpy(faLastChallengeIssued[1], faLastChallengeIssued[0], ADV_CHALLENGE_LENGTH); randomValuePtr = cp; /* We want a 16 octet (128 bit) challenge */ for (j = 0; j < ADV_CHALLENGE_LENGTH; j += 4) { randomValue = getRandomValue(); (void) strncpy((char *)cp, (char *)&randomValue, sizeof (randomValue)); len += sizeof (randomValue); cp += sizeof (randomValue); } /* We want a 16 octet (128 bit) challenge */ /* * Save the New Challenge issued */ (void) memcpy(faLastChallengeIssued[0], randomValuePtr, ADV_CHALLENGE_LENGTH); } else if (advType == SOLICITED_ADV) { /* Add the Foreign Agent Challenge Value */ cp = advPkt + len; *cp++ = ADV_CHALLENGE_EXT_TYPE; *cp++ = ADV_CHALLENGE_LENGTH; len += 2; /* We use the last advertised challenge */ (void) memcpy(cp, faLastChallengeIssued[0], ADV_CHALLENGE_LENGTH); len += ADV_CHALLENGE_LENGTH; cp += ADV_CHALLENGE_LENGTH; } } /* fill out prefix length extension, if required */ if ((entry->maAdvPrefixLenInclusion == _B_TRUE) && icmpPtr->icmpAdvNumAddr) { cp = advPkt + len; *cp++ = ADV_PREFIX_EXT_TYPE; *cp++ = icmpPtr->icmpAdvNumAddr; len += 2; /* fill out prefix len for each addr in RFC1256 portion */ for (j = 0; j < icmpPtr->icmpAdvNumAddr; j++) { *cp++ = prefixLen(entry->maIfaceNetmask); len++; } } /* pad ICMP message to even length, if required */ if (len % 2) { *cp++ = ADV_PADDING_EXT_TYPE; len++; } /* fill out ICMP checksum */ icmpPtr->checksum = 0; icmpPtr->checksum = inChecksum((unsigned short *) icmpPtr, len); /* ship it! */ commonCounters.maAdvSentCnt++; /* * IP_XMIT_IF or IP_MULTICAST_IF are set on this socket * during initiation time in InitSockets(). */ sendICMPmessage(entry->maIfaceIcmpSock, dst, advPkt, len); /* * Now update maAdvInitCount for limited unsolicited advertisement * case. Note, we need to update this value to make sure that * we don't send unsolicited advertisements beyond maAdvInitCount * value specified in the mipagent.conf file. * I am updating this value, otherwise we need to keep another * data structure for each dynamic interface, which can grow large * with the number of interfaces. Besides, there is no reason I find * at present, to preserve it's initial value. */ if (entry->maAdvLimitUnsolicited && entry->maAdvInitCount > 0) { /* update maAdvInitCount */ entry->maAdvInitCount--; mipverbose(("maSendAdvertisement: updating maAdvInitCount " " to %d\n", entry->maAdvInitCount)); } /* * Note that the mipverbose format is different here. * We want to just print the interface index with each * advertisement. */ mipverbose(("+[%d]", entry->maIfindex)); } /* * Function: delHABEent * * Arguments: mnePtr - Pointer to a Mobile Node Entry * entry - Pointer to a Binding Entry * * Description: This function will delete a binding entry. * If a Home Address was assigned to the Mobile * Node via a local pool, and the number of * bindings is set to zero (0), we will free * the Home Address. * * Note that the caller MUST have locked the * Mobile Node Entry prior to calling this * function. * * Returns: */ void delHABEent(HaMobileNodeEntry *mnePtr, HaBindingEntry *entry) { ipaddr_t mnAddr, coAddr; char addrstr1[INET_ADDRSTRLEN]; char addrstr2[INET_ADDRSTRLEN]; int val; time_t localTime; time_t sessionLifeTime; int result; mnAddr = entry->haBindingMN; coAddr = entry->haBindingCOA; /* * Disable encapsulation to this coaddr. Decrease * binding count for MN. If zero, cancel proxy ARP & route */ #ifdef FIREWALL_SUPPORT /* * If the care-of-address was outside protected domain remove * the encapsulation through the firewall's internal address. * TODO: This works only if we assume that a single external * COAddr is never shared by multiple mobile nodes. For now, * that's always the case. */ if (isOutsideProtectedDomain(coAddr)) { mipverbose(("Removing tunnel for %s through FW %s\n", ntoa(coAddr, addrstr1), ntoa(domainInfo.fwAddr[0], addrstr2))); if ((val = encaprem(coAddr)) < 0) { syslog(LOG_ERR, "encaprem failed ... %s", err2str(val)); } } #endif /* FIREWALL_SUPPORT */ /* * Terminate encapsulation and decapsulation of MN's * packets to COA. For now, this also handles reverse * tunneling */ mipverbose(("Removing tunnel for %s through %s\n", ntoa(mnAddr, addrstr1), ntoa(coAddr, addrstr2))); if ((val = encaprem(mnAddr)) < 0) { syslog(LOG_ERR, "encaprem failed ... %s", err2str(val)); } /* if encaprem() returned 0, there are no more MNs using this tunnel */ if (val == 0) { /* kill off any ipsec SA we have with this agent-peer */ MobilityAgentEntry *mae; /* note: this should work for colocated MNs too! */ if ((mae = findMaeFromIp(coAddr, LOCK_READ)) != NULL) { /* * We're the HA, so we look for IPSEC_REPLY_APPLY to * remove. NOTE: we NEVER remove IPSEC_REQUEST_PERMIT, * (unless shutting down), as these come unannounced. * See agentInit.c: deleteIPsecSAHashHelper() */ if (mae->maIPsecFlags & IPSEC_REPLY_APPLY) { /* off it */ if (removeIPsecPolicy( mae->maIPsecReply[IPSEC_APPLY]) < 0) { /* whine about it */ char peerAddr[IPv4_ADDR_LEN]; (void) ntoa(coAddr, peerAddr); syslog(LOG_CRIT, "Could not remove %s" "ipsec regisration reply apply.", peerAddr); } else /* it's gone, unset the flag bit */ mae->maIPsecFlags &= ~IPSEC_REPLY_APPLY; } /* The tunnels are down, so just unset those bits */ mae->maIPsecFlags &= ~IPSEC_TUNNEL_APPLY; mae->maIPsecFlags &= ~IPSEC_REVERSE_TUNNEL_PERMIT; /* more importantly, this is no longer an agent peer */ mae->maPeerFlags &= ~FA_PEER; /* unlock */ (void) rw_unlock(&mae->maNodeLock); } } if (--mnePtr->haMnBindingCnt == 0) { /* * Notify AAA server that the MN is going away. */ if ((aaaProtocol == RADIUS || aaaProtocol == DIAMETER)) { GET_TIME(localTime); sessionLifeTime = localTime - entry->haBindingTimeGranted; result = sendAccountingRecord( MOBILE_IP_ACCOUNTING_STOP_REQUEST, (unsigned char *)mnePtr->haMnNAI, mnePtr->haMnNAILen, mnAddr, coAddr, entry->haBindingHaAddr, sessionLifeTime, MN_DEREGISTERED); if (result) { syslog(LOG_ERR, "Unable to send accounting " "stop record"); } } mipverbose(("Terminating arp service for %s\n", ntoa(mnAddr, addrstr1))); /* * TODO: * Solaris gets confused if it has two ARP entries * for the same host on different interfaces. This * needs further looking into(at least it tries to * keep a separate ARP table for each interface). */ mipverbose(("Removing proxy ARP entry for %s\n", ntoa(mnAddr, addrstr1))); if ((val = arpdel(mnAddr)) < 0) { syslog(LOG_ERR, "arpdel failed ... %s", err2str(val)); } /* * Do we have an assigned Home Address that we need * to free? */ if (mnePtr->haPoolIdentifier && mnePtr->haMnAddr) { if (freeAddressFromPool(mnePtr->haPoolIdentifier, mnePtr->haMnAddr) != _B_TRUE) { syslog(LOG_ERR, "Unable to free address from pool %d", mnePtr->haPoolIdentifier); } mnePtr->haMnAddr = 0; } } mipverbose(( "HA binding removed for %s@%s at entry %p (Binding Cnt %d).\n", ntoa(mnAddr, addrstr1), ntoa(coAddr, addrstr2), (void *)entry, mnePtr->haMnBindingCnt)); } /* * Function: delFAVEptr * * Arguments: entry - Foreign Agent Visitor Entry * sendASR - whether to send Accounting Stop Request * * Description: This function will delete a Foreign Agent's * Visitor entry. If the visitor entry was in * the ACCEPTED state (meaning that service was * provided to the Mobile Node), we must remove * the route, delete the tunnel interface and * the MN's ARP entry. * * Note that the caller MUST have locked the * Mobile Node Entry prior to calling this * function. * * Returns: */ void delFAVEptr(FaVisitorEntry *entry, boolean_t sendASR, uint32_t releaseindicator) { int val; int tun_num; int in_index; char tun_name[LIFNAMSIZ]; char addrstr1[INET_ADDRSTRLEN]; char addrstr2[INET_ADDRSTRLEN]; int result; time_t localTime; time_t sessionLifeTime; #if 0 /* * Of course, this causes us a heart ache if we are called from * an interrupt handler, since we will not be locking */ /* * Let's just make sure that the caller did indeed * lock the visitor entry... */ if (rw_trywrlock(&entry->nodeLock) == 0) { assert(0); } #endif mipverbose(("FA expired %s visitor entry for %s\n", (entry->faVisitorRegIsAccepted ? "accepted" : "pending"), ntoa(entry->faVisitorHomeAddr, addrstr1))); /* For accepted requests, kill route and disable decapsulation. */ if (entry->faVisitorRegIsAccepted == _B_TRUE) { tun_num = gettunnelno(entry->faVisitorHomeAgentAddr, entry->faVisitorIfaceAddr); if (tun_num < 0) { syslog(LOG_ERR, "gettunnelno returns -1"); return; } (void) snprintf(tun_name, sizeof (tun_name), "ip.tun%d", tun_num); in_index = if_nametoindex(tun_name); if (in_index == 0) { /* if_nametoindex fails... */ syslog(LOG_ERR, "if_nametoindex failed for tunnel %s", tun_name); } mipverbose(( "Removing direct, local route for visitor %s thru %s\n", ntoa(entry->faVisitorHomeAddr, addrstr1), ntoa(entry->faVisitorIfaceAddr, addrstr2))); if ((val = routedel(entry->faVisitorHomeAddr, entry->faVisitorIfaceAddr, 0, in_index, entry->faVisitorInIfindex)) < 0) { syslog(LOG_ERR, "routedel failed ...for visitor %s: %s\n", ntoa(entry->faVisitorHomeAddr, addrstr1), err2str(val)); } if (entry->faVisitorRegFlags & REG_REVERSE_TUNNEL) { /* delete Reverse Tunnel route */ mipverbose(( "Removing Reverse tunnel route for visitor %s at" " interface index %d\n", ntoa(entry->faVisitorHomeAddr, addrstr2), entry->faVisitorInIfindex)); if ((val = routedel(0, 0, entry->faVisitorHomeAddr, entry->faVisitorInIfindex, in_index)) < 0) { syslog(LOG_ERR, "Reverse Tunnel route delete failed: %s:" " for visitor %s at interface index %d", err2str(val), ntoa(entry->faVisitorHomeAddr, addrstr1), entry->faVisitorInIfindex); } } /* * Notify AAA server that the MN is going away. * In the case we are here because of a Close Session Request * received from AAA, AAA already knows it should stop * accounting. */ if ((aaaProtocol == RADIUS || aaaProtocol == DIAMETER) && (sendASR == _B_TRUE)) { GET_TIME(localTime); sessionLifeTime = localTime - entry->faVisitorTimeGranted; result = sendAccountingRecord( MOBILE_IP_ACCOUNTING_STOP_REQUEST, (unsigned char *)entry->faVisitorMnNAI, entry->faVisitorMnNAILen, entry->faVisitorHomeAddr, entry->faVisitorCOAddr, entry->faVisitorHomeAgentAddr, sessionLifeTime, releaseindicator); if (result) { syslog(LOG_ERR, "Unable to send accounting " "stop record"); } } /* * TODO: this behavior is incorrect if multiple coaddrs can be * registered for a mobile node at a FA. Decaprem should occur * ONLY after a mobile node has NO accepted registrations. */ mipverbose(( "Disabling decapsulation of inner pkts sent to %s\n", ntoa(entry->faVisitorHomeAddr, addrstr1))); if ((val = decaprem(entry->faVisitorHomeAgentAddr, entry->faVisitorIfaceAddr)) < 0) { syslog(LOG_ERR, "decaprem failed: %s", err2str(val)); } /* if decaprem() returns 0 there are no MNs using the tunnel */ if (val == 0) { /* kill any tunnel ipsec SAs we have with this peer */ MobilityAgentEntry *mae; if ((mae = findMaeFromIp(entry->faVisitorHomeAgentAddr, LOCK_READ)) != NULL) { /* * We're the FA, so we look for * IPSEC_REPLY_PERMIT, and * IPSEC_REQUEST_APPLY policies. */ if (mae->maIPsecFlags & IPSEC_REPLY_PERMIT) { /* off it */ if (removeIPsecPolicy( mae->maIPsecReply[IPSEC_PERMIT]) < 0) { /* whine about it */ char peerAddr[IPv4_ADDR_LEN]; (void) ntoa(entry-> faVisitorHomeAgentAddr, peerAddr); syslog(LOG_CRIT, "Could not remove %s's " "ipsec reply permit.", peerAddr); } else /* unset the flag bit */ mae->maIPsecFlags &= ~IPSEC_REPLY_PERMIT; } /* * The tunnel policy itself is 'freed' * when the tunnel is unplumbed, but * we need to unset the flag bits. * * We don't care if *this* MN's got a reverse * tunnel, but if *any* happenned to get one. */ mae->maIPsecFlags &= ~IPSEC_TUNNEL_PERMIT; mae->maIPsecFlags &= ~IPSEC_REVERSE_TUNNEL_APPLY; /* this is no longer an agent peer */ mae->maPeerFlags &= ~HA_PEER; /* unlock */ (void) rw_unlock(&mae->maNodeLock); } } /* * If the entry is accepted we should have set up an ARP * cache entry (marked with the permanent flag). Since this * is the common entry point to deleting the FAVE, we delete * the ARP entry here too. The other deletions are done in * the registration request and reply processing code on the * FA side. The ARP cache entry was set up to prevent the FA * from broadcast ARPing. PVTODO: May need some special * handling for 2 hosts with same addr (private overlapping * address) on the same interface index, eg: specify link * layer address here too. May need to modify ARP to delete * based on ether addr too. */ if (entry->faVisitorIsSllaValid && ((val = arpIfdel(entry->faVisitorHomeAddr, entry->faVisitorSlla.sdl_data, entry->faVisitorInIfindex)) < 0)) { /* * If deletion failed bcos there was'nt an entry * mipagent need not report it */ if (val != (-1)*ENXIO) syslog(LOG_ERR, "arpIfdel failed ...%s\n", err2str(val)); } } } /* * Function: haAgeBindingsHashHelper * * Arguments: entry - Pointer to Mobile Node Entry * p1 - First parameter to match (current time) * * Description: This function is used as the Hash Table Helper routine * for haAgeBinding() when looking for binding entries * in the Hash Table that have expired, and will be * called by getAllHashTableEntries(). * * Returns: _B_TRUE if the entry matches the desired criteria, * otherwise _B_FALSE. */ static boolean_t haAgeBindingsHashHelper(void *entry, uint32_t p1) { HaMobileNodeEntry *hentry = entry; HaBindingEntry *bindingEntry; HaBindingEntry *prev_entry = NULL; HaBindingEntry *next_entry; bindingEntry = hentry->bindingEntries; while (bindingEntry) { next_entry = bindingEntry->next; if (bindingEntry->haBindingTimeExpires > p1) { /* * We want to keep this one, so setup the pointer. */ prev_entry = bindingEntry; } else { if (prev_entry) { prev_entry->next = bindingEntry->next; } else { hentry->bindingEntries = bindingEntry->next; } delHABEent(hentry, bindingEntry); free(bindingEntry); } bindingEntry = next_entry; } /* * We need to delete the Mobile Node Entry if the * entry was dynamic and has no more bindings. */ if (hentry->haMnBindingCnt == 0 && hentry->haMnIsEntryDynamic == TRUE) { (void) rw_unlock(&hentry->haMnNodeLock); (void) rwlock_destroy(&hentry->haMnNodeLock); free(hentry); /* * Returning _B_FALSE here informs the caller that * the entry was freed. */ return (_B_FALSE); } return (_B_TRUE); } /* * Function: haAgeBindings * * Arguments: htbl - Pointer to Hash Table * currentTime - The current Time (absolute) * * Description: This function will step through each * Mobile Node's Binding Entries and will * delete expired entries. * * If the Mobile Node Entry is marked as a * DYNAMIC entry, and the number of binding * entries is set to zero (0), we will free * the Mobile Node Entry as well. * * Returns: */ static void haAgeBindings(struct hash_table *htbl, time_t currentTime) { getAllHashTableEntries(htbl, haAgeBindingsHashHelper, LOCK_WRITE, currentTime, _B_FALSE); } /* * Function: faAgeVisitorsHashHelper * * Arguments: entry - Pointer to Mobile Node Entry * p1 - First parameter to match (current time) * * Description: This function is used as the Hash Table Helper routine * for faAgeVisitors() when looking for visitor entries * in the Hash Table that have expired, and will be * called by getAllHashTableEntries(). * * Returns: _B_TRUE if the entry matches the desired criteria, * otherwise _B_FALSE. */ static boolean_t faAgeVisitorsHashHelper(void *entry, uint32_t p1) { FaVisitorEntry *faEntry = entry; if (faEntry->faVisitorTimeExpires <= p1) { (void) fprintf(stderr, "Deleting visitor entry!!\n"); /* Expired */ delFAVEptr(faEntry, _B_TRUE, REG_EXPIRED); (void) rw_unlock(&faEntry->faVisitorNodeLock); (void) rwlock_destroy(&faEntry->faVisitorNodeLock); free(faEntry); return (_B_FALSE); } return (_B_TRUE); } /* * Function: faAgeVisitors * * Arguments: htbl - Pointer to Hash Table * currentTime - The current Time (absolute) * * Description: This function will step through each * Foreign Agent Visitor Entries and will * delete expired entries. * * Returns: */ static void faAgeVisitors(struct hash_table *htbl, time_t currentTime) { getAllHashTableEntries(htbl, faAgeVisitorsHashHelper, LOCK_WRITE, currentTime, _B_FALSE); } /* * Function: mipAgeSecAssocHashHelper * * Arguments: entry - Pointer to Mobile Node Entry * p1 - First parameter to match (current time) * * Description: This function is used as the Hash Table Helper routine * for mipAgeSecAssoc() when looking for visitor entries * in the Hash Table that have expired, and will be * called by getAllHashTableEntries(). * * Returns: _B_TRUE if the entry matches the desired criteria, * otherwise _B_FALSE. */ static boolean_t mipAgeSecAssocHashHelper(void *entry, uint32_t p1) { MipSecAssocEntry *saEntry = entry; if (saEntry->mipSecIsEntryDynamic == TRUE && saEntry->mipSecKeyLifetime <= p1) { (void) fprintf(stderr, "Deleting Security Assocation entry!!\n"); /* Expired */ (void) rw_unlock(&saEntry->mipSecNodeLock); (void) rwlock_destroy(&saEntry->mipSecNodeLock); free(saEntry); return (_B_FALSE); } return (_B_TRUE); } /* * Function: mipAgeSecAssoc * * Arguments: htbl - Pointer to Hash Table * currentTime - The current Time (absolute) * * Description: This function will step through each * Security Association Entries and will * delete the SAs marked as DYNAMIC that * have expired. * * Returns: */ static void mipAgeSecAssoc(struct hash_table *htbl, time_t currentTime) { getAllHashTableEntries(htbl, mipAgeSecAssocHashHelper, LOCK_WRITE, currentTime, _B_FALSE); } /* * Function: mipAgeMobilityAgentsHashHelper * * Arguments: entry - Pointer to Mobile Node Entry * p1 - First parameter to match (current time) * * Description: This function is used as the Hash Table Helper routine * for mipAgeMobilityAgents() when looking for visitor entries * in the Hash Table that have expired, and will be * called by getAllHashTableEntries(). * * Returns: _B_TRUE if the entry matches the desired criteria, * otherwise _B_FALSE. */ static boolean_t mipAgeMobilityAgentsHashHelper(void *entry, uint32_t p1) { MobilityAgentEntry *maEntry = entry; if (maEntry->maIsEntryDynamic == TRUE && maEntry->maExpiration <= p1) { (void) fprintf(stderr, "Deleting Mobility Agent entry!!\n"); /* Expired */ (void) rw_unlock(&maEntry->maNodeLock); (void) rwlock_destroy(&maEntry->maNodeLock); free(maEntry); return (_B_FALSE); } return (_B_TRUE); } /* * Function: mipAgeMobilityAgents * * Arguments: htbl - Pointer to Hash Table * currentTime - The current Time (absolute) * * Description: This function will step through each * Mobility Agent Entries and will * delete the ones marked as DYNAMIC that * have expired. * * Returns: */ static void mipAgeMobilityAgents(struct hash_table *htbl, time_t currentTime) { getAllHashTableEntries(htbl, mipAgeMobilityAgentsHashHelper, LOCK_WRITE, currentTime, _B_FALSE); } /* * Function: startPeriodicTaskThread * * Arguments: * * Description: This function is called to start the * periodic task handling thread. * * Returns: int, 0 if successful. */ int startPeriodicTaskThread(void) { pthread_attr_t pthreadAttribute; int result; result = pthread_attr_init(&pthreadAttribute); if (result) { syslog(LOG_CRIT, "Error Initializing pthread."); return (-1); } /* * We now create a thread to deal with all periodic task. */ result = pthread_create(&periodicThreadId, &pthreadAttribute, (void *(*)()) doPeriodicTask, (void *)NULL); if (result) { syslog(LOG_CRIT, "pthread_create() failed."); 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(periodicThreadId); if (result) { syslog(LOG_CRIT, "pthread_detach() failed."); return (-1); } return (0); } /* * Function: killPeriodicTaskThread * * Arguments: * * Description: This function is called during the agent * shutdown procedure in order to kill the * periodic task handling thread. * * Returns: */ int killPeriodicTaskThread() { int result; if (periodicThreadId) { /* * Next we need to kill the dispatching thread. */ result = pthread_cancel(periodicThreadId); if (result) { /* * Well, there's not much we can do here.. */ syslog(LOG_CRIT, "Unable to kill periodic thread"); return (-1); } } return (0); } /* * Function: doPeriodicTask * * Arguments: void * * Description: This function is called by a thread * and will periodically call the functions * that free any expired control blocks, such * as visitor entries, bindings, etc. * * The frequency that these clean up tasks * get called is configurable through the * configuration file, but this feature is * undocumented. * * This function never returns, but will be * killed if the master thread calls * killPeriodicTaskThread() * * Returns: never! */ static void doPeriodicTask(void) { struct timeval tv; time_t nextPeriodicTime = 0; time_t currentTime; /* CONSTCOND */ while (_B_TRUE) { GET_TIME(currentTime); if (currentTime < nextPeriodicTime) { tv.tv_sec = nextPeriodicTime - currentTime; tv.tv_usec = 0; (void) select(FD_SETSIZE, NULL, NULL, NULL, &tv); GET_TIME(currentTime); } if (currentTime >= nextPeriodicTime) { haAgeBindings(&haMobileNodeHash, currentTime); faAgeVisitors(&faVisitorHash, currentTime); (void) mipAgeSecAssoc(&mipSecAssocHash, currentTime); mipAgeMobilityAgents(&mipAgentHash, currentTime); nextPeriodicTime = currentTime + periodicInterval; mipverbose(("*")); (void) fflush(stdout); } } }