455 lines
8.8 KiB
C
455 lines
8.8 KiB
C
/*
|
|
*
|
|
* ===================================
|
|
* HARP | Host ATM Research Platform
|
|
* ===================================
|
|
*
|
|
*
|
|
* This Host ATM Research Platform ("HARP") file (the "Software") is
|
|
* made available by Network Computing Services, Inc. ("NetworkCS")
|
|
* "AS IS". NetworkCS does not provide maintenance, improvements or
|
|
* support of any kind.
|
|
*
|
|
* NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
|
|
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
|
|
* SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
|
|
* In no event shall NetworkCS be responsible for any damages, including
|
|
* but not limited to consequential damages, arising from or relating to
|
|
* any use of the Software or related support.
|
|
*
|
|
* Copyright 1994-1998 Network Computing Services, Inc.
|
|
*
|
|
* Copies of this Software may be made, however, the above copyright
|
|
* notice must be reproduced on all copies.
|
|
*
|
|
* @(#) $FreeBSD$
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* IP Over ATM Support
|
|
* -------------------
|
|
*
|
|
* IP VCC event handler
|
|
*
|
|
*/
|
|
|
|
#include <netatm/kern_include.h>
|
|
|
|
#include <netatm/ipatm/ipatm.h>
|
|
#include <netatm/ipatm/ipatm_var.h>
|
|
#include <netatm/ipatm/ipatm_serv.h>
|
|
|
|
#ifndef lint
|
|
__RCSID("@(#) $FreeBSD$");
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Process an IP VCC timeout
|
|
*
|
|
* Called when a previously scheduled ipvcc control block timer expires.
|
|
* Processing will be based on the current ipvcc state.
|
|
*
|
|
* Called at splnet.
|
|
*
|
|
* Arguments:
|
|
* tip pointer to ipvcc timer control block
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
ipatm_timeout(tip)
|
|
struct atm_time *tip;
|
|
{
|
|
struct ipvcc *ivp;
|
|
|
|
/*
|
|
* Back-off to ipvcc control block
|
|
*/
|
|
ivp = (struct ipvcc *)
|
|
((caddr_t)tip - (int)(&((struct ipvcc *)0)->iv_time));
|
|
|
|
/*
|
|
* Process timeout based on protocol state
|
|
*/
|
|
switch (ivp->iv_state) {
|
|
|
|
case IPVCC_PMAP:
|
|
/*
|
|
* Give up waiting for arp response
|
|
*/
|
|
(void) ipatm_closevc(ivp, T_ATM_CAUSE_TEMPORARY_FAILURE);
|
|
break;
|
|
|
|
case IPVCC_POPEN:
|
|
case IPVCC_PACCEPT:
|
|
/*
|
|
* Give up waiting for signalling manager response
|
|
*/
|
|
(void) ipatm_closevc(ivp, T_ATM_CAUSE_TEMPORARY_FAILURE);
|
|
break;
|
|
|
|
case IPVCC_ACTPENT:
|
|
/*
|
|
* Try again to get an ARP entry
|
|
*/
|
|
switch ((*ivp->iv_ipnif->inf_serv->is_arp_pvcopen)(ivp)) {
|
|
|
|
case MAP_PROCEEDING:
|
|
/*
|
|
* Wait for answer
|
|
*/
|
|
ivp->iv_state = IPVCC_ACTIVE;
|
|
break;
|
|
|
|
case MAP_VALID:
|
|
/*
|
|
* We've got our answer already
|
|
*/
|
|
ivp->iv_state = IPVCC_ACTIVE;
|
|
ivp->iv_flags |= IVF_MAPOK;
|
|
ivp->iv_dst.s_addr = ivp->iv_arpent->am_dstip.s_addr;
|
|
break;
|
|
|
|
case MAP_FAILED:
|
|
/*
|
|
* Try again later
|
|
*/
|
|
IPVCC_TIMER(ivp, 5 * ATM_HZ);
|
|
break;
|
|
|
|
default:
|
|
panic("ipatm_timeout: invalid am_pvcopen return");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
log(LOG_ERR, "ipatm: invalid timer state: ivp=%p, state=%d\n",
|
|
ivp, ivp->iv_state);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Process IP VCC Connected Notification
|
|
*
|
|
* Arguments:
|
|
* toku owner's connection token (ipvcc protocol block)
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
ipatm_connected(toku)
|
|
void *toku;
|
|
{
|
|
struct ipvcc *ivp = (struct ipvcc *)toku;
|
|
|
|
/*
|
|
* SVC is connected
|
|
*/
|
|
if ((ivp->iv_state != IPVCC_POPEN) &&
|
|
(ivp->iv_state != IPVCC_PACCEPT)) {
|
|
log(LOG_ERR, "ipatm: invalid CALL_CONNECTED state=%d\n",
|
|
ivp->iv_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Verify possible negotiated parameter values
|
|
*/
|
|
if (ivp->iv_state == IPVCC_POPEN) {
|
|
Atm_attributes *ap = &ivp->iv_conn->co_connvc->cvc_attr;
|
|
int mtu = (ivp->iv_flags & IVF_LLC) ?
|
|
ATM_NIF_MTU + IPATM_LLC_LEN :
|
|
ATM_NIF_MTU;
|
|
|
|
/*
|
|
* Verify final MTU
|
|
*/
|
|
if (ap->aal.type == ATM_AAL5) {
|
|
if ((ap->aal.v.aal5.forward_max_SDU_size < mtu) ||
|
|
(ap->aal.v.aal5.backward_max_SDU_size > mtu)) {
|
|
(void) ipatm_closevc(ivp,
|
|
T_ATM_CAUSE_AAL_PARAMETERS_NOT_SUPPORTED);
|
|
return;
|
|
}
|
|
} else {
|
|
if ((ap->aal.v.aal4.forward_max_SDU_size < mtu) ||
|
|
(ap->aal.v.aal4.backward_max_SDU_size > mtu)) {
|
|
(void) ipatm_closevc(ivp,
|
|
T_ATM_CAUSE_AAL_PARAMETERS_NOT_SUPPORTED);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Finish up VCC activation
|
|
*/
|
|
ipatm_activate(ivp);
|
|
}
|
|
|
|
|
|
/*
|
|
* Process IP VCC Cleared Notification
|
|
*
|
|
* Arguments:
|
|
* toku owner's connection token (ipvcc protocol block)
|
|
* cause pointer to cause code
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
ipatm_cleared(toku, cause)
|
|
void *toku;
|
|
struct t_atm_cause *cause;
|
|
{
|
|
struct ipvcc *ivp = (struct ipvcc *)toku;
|
|
|
|
|
|
/*
|
|
* VCC has been cleared, so figure out what's next
|
|
*/
|
|
ivp->iv_conn = NULL;
|
|
|
|
switch (ivp->iv_state) {
|
|
|
|
case IPVCC_POPEN:
|
|
/*
|
|
* Call setup failed, see if there is another
|
|
* set of vcc parameters to try
|
|
*/
|
|
ivp->iv_state = IPVCC_CLOSED;
|
|
if (ipatm_retrysvc(ivp)) {
|
|
(void) ipatm_closevc(ivp, cause->cause_value);
|
|
}
|
|
break;
|
|
|
|
case IPVCC_PACCEPT:
|
|
case IPVCC_ACTPENT:
|
|
case IPVCC_ACTIVE:
|
|
ivp->iv_state = IPVCC_CLOSED;
|
|
(void) ipatm_closevc(ivp, cause->cause_value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Process an ARP Event Notification
|
|
*
|
|
* Arguments:
|
|
* ivp pointer to IP VCC control block
|
|
* event ARP event type
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
ipatm_arpnotify(ivp, event)
|
|
struct ipvcc *ivp;
|
|
int event;
|
|
{
|
|
struct sockaddr_in sin;
|
|
struct ifnet *ifp;
|
|
|
|
/*
|
|
* Process event
|
|
*/
|
|
switch (event) {
|
|
|
|
case MAP_VALID:
|
|
switch (ivp->iv_state) {
|
|
|
|
case IPVCC_PMAP:
|
|
/*
|
|
* We've got our destination, however, first we'll
|
|
* check to make sure no other VCC to our destination
|
|
* has also had it's ARP table entry completed.
|
|
* If we don't find a better VCC to use, then we'll
|
|
* go ahead and open this SVC.
|
|
*/
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_addr.s_addr = ivp->iv_dst.s_addr;
|
|
if (ipatm_iptovc(&sin, ivp->iv_ipnif->inf_nif) != ivp) {
|
|
/*
|
|
* We found a better VCC, so use it and
|
|
* get rid of this VCC
|
|
*/
|
|
if (ivp->iv_queue) {
|
|
ifp = (struct ifnet *)
|
|
ivp->iv_ipnif->inf_nif;
|
|
(void) ipatm_ifoutput(ifp,
|
|
ivp->iv_queue,
|
|
(struct sockaddr *)&sin);
|
|
ivp->iv_queue = NULL;
|
|
}
|
|
(void) ipatm_closevc(ivp,
|
|
T_ATM_CAUSE_UNSPECIFIED_NORMAL);
|
|
|
|
} else {
|
|
/*
|
|
* We like this VCC...
|
|
*/
|
|
ivp->iv_flags |= IVF_MAPOK;
|
|
if (ipatm_opensvc(ivp)) {
|
|
(void) ipatm_closevc(ivp,
|
|
T_ATM_CAUSE_TEMPORARY_FAILURE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IPVCC_POPEN:
|
|
case IPVCC_PACCEPT:
|
|
case IPVCC_ACTIVE:
|
|
/*
|
|
* Everything looks good, so accept new mapping
|
|
*/
|
|
ivp->iv_flags |= IVF_MAPOK;
|
|
ivp->iv_dst.s_addr = ivp->iv_arpent->am_dstip.s_addr;
|
|
|
|
/*
|
|
* Send queued packet
|
|
*/
|
|
if ((ivp->iv_state == IPVCC_ACTIVE) && ivp->iv_queue) {
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_addr.s_addr = ivp->iv_dst.s_addr;
|
|
ifp = (struct ifnet *)ivp->iv_ipnif->inf_nif;
|
|
(void) ipatm_ifoutput(ifp, ivp->iv_queue,
|
|
(struct sockaddr *)&sin);
|
|
ivp->iv_queue = NULL;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case MAP_INVALID:
|
|
switch (ivp->iv_state) {
|
|
|
|
case IPVCC_POPEN:
|
|
case IPVCC_PACCEPT:
|
|
case IPVCC_ACTIVE:
|
|
|
|
/*
|
|
* Mapping has gone stale, so we cant use this VCC
|
|
* until the mapping is refreshed
|
|
*/
|
|
ivp->iv_flags &= ~IVF_MAPOK;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case MAP_FAILED:
|
|
/*
|
|
* ARP lookup failed, just trash it all
|
|
*/
|
|
(void) ipatm_closevc(ivp, T_ATM_CAUSE_TEMPORARY_FAILURE);
|
|
break;
|
|
|
|
case MAP_CHANGED:
|
|
/*
|
|
* ARP mapping has changed
|
|
*/
|
|
if (ivp->iv_flags & IVF_PVC) {
|
|
/*
|
|
* For PVCs, just reset lookup cache if needed
|
|
*/
|
|
if (last_map_ipvcc == ivp) {
|
|
last_map_ipdst = 0;
|
|
last_map_ipvcc = NULL;
|
|
}
|
|
} else {
|
|
/*
|
|
* Close SVC if it has already used this mapping
|
|
*/
|
|
switch (ivp->iv_state) {
|
|
|
|
case IPVCC_POPEN:
|
|
case IPVCC_ACTIVE:
|
|
(void) ipatm_closevc(ivp,
|
|
T_ATM_CAUSE_UNSPECIFIED_NORMAL);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
log(LOG_ERR, "ipatm: unknown arp event %d, ivp=%p\n",
|
|
event, ivp);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Process an IP VCC idle timer tick
|
|
*
|
|
* This function is called every IPATM_IDLE_TIME seconds, in order to
|
|
* scan for idle IP VCC's. If an active VCC reaches the idle time limit,
|
|
* then it will be closed.
|
|
*
|
|
* Called at splnet.
|
|
*
|
|
* Arguments:
|
|
* tip pointer to the VCC idle timer control block
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
ipatm_itimeout(tip)
|
|
struct atm_time *tip;
|
|
{
|
|
struct ipvcc *ivp, *inext;
|
|
struct ip_nif *inp;
|
|
|
|
|
|
/*
|
|
* Schedule next timeout
|
|
*/
|
|
atm_timeout(&ipatm_itimer, IPATM_IDLE_TIME, ipatm_itimeout);
|
|
|
|
/*
|
|
* Check for disabled idle timeout
|
|
*/
|
|
if (ipatm_vcidle == 0)
|
|
return;
|
|
|
|
/*
|
|
* Check out all IP VCCs
|
|
*/
|
|
for (inp = ipatm_nif_head; inp; inp = inp->inf_next) {
|
|
for (ivp = Q_HEAD(inp->inf_vcq, struct ipvcc); ivp;
|
|
ivp = inext) {
|
|
|
|
inext = Q_NEXT(ivp, struct ipvcc, iv_elem);
|
|
|
|
/*
|
|
* Looking for active, idle SVCs
|
|
*/
|
|
if (ivp->iv_flags & (IVF_PVC | IVF_NOIDLE))
|
|
continue;
|
|
if (ivp->iv_state != IPVCC_ACTIVE)
|
|
continue;
|
|
if (++ivp->iv_idle < ipatm_vcidle)
|
|
continue;
|
|
|
|
/*
|
|
* OK, we found one - close the VCC
|
|
*/
|
|
(void) ipatm_closevc(ivp,
|
|
T_ATM_CAUSE_UNSPECIFIED_NORMAL);
|
|
}
|
|
}
|
|
}
|
|
|