freebsd-skq/sys/netatm/ipatm/ipatm_vcm.c
harti 3988ab54c6 Add support for VBR and CBR PVCs for IP over ATM.
Submitted by:	Vincent Jardin <vjardin@wanadoo.fr>
MFC after:	2 weeks
2003-07-25 08:35:26 +00:00

1346 lines
26 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.
*/
/*
* IP Over ATM Support
* -------------------
*
* Virtual Channel Manager
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netatm/port.h>
#include <netatm/queue.h>
#include <netatm/atm.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_sap.h>
#include <netatm/atm_cm.h>
#include <netatm/atm_if.h>
#include <netatm/atm_stack.h>
#include <netatm/atm_pcb.h>
#include <netatm/atm_var.h>
#include <netatm/ipatm/ipatm.h>
#include <netatm/ipatm/ipatm_var.h>
#include <netatm/ipatm/ipatm_serv.h>
Atm_attributes ipatm_aal5llc = {
NULL, /* nif */
CMAPI_CPCS, /* api */
0, /* api_init */
0, /* headin */
0, /* headout */
{ /* aal */
T_ATM_PRESENT,
ATM_AAL5
},
{ /* traffic */
T_ATM_PRESENT,
{
{
T_ATM_ABSENT,
0,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_NO
},
{
T_ATM_ABSENT,
0,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_NO
},
T_YES
},
},
{ /* bearer */
T_ATM_PRESENT,
{
T_ATM_CLASS_X,
T_ATM_NULL,
T_ATM_NULL,
T_NO,
T_ATM_1_TO_1
}
},
{ /* bhli */
T_ATM_ABSENT
},
{ /* blli */
T_ATM_PRESENT,
T_ATM_ABSENT,
{
{
T_ATM_SIMPLE_ID,
},
{
T_ATM_ABSENT
}
}
},
{ /* llc */
T_ATM_PRESENT,
{
T_ATM_LLC_SHARING,
IPATM_LLC_LEN,
IPATM_LLC_HDR
}
},
{ /* called */
T_ATM_PRESENT,
},
{ /* calling */
T_ATM_ABSENT
},
{ /* qos */
T_ATM_PRESENT,
{
T_ATM_NETWORK_CODING,
{
T_ATM_QOS_CLASS_0,
},
{
T_ATM_QOS_CLASS_0
}
}
},
{ /* transit */
T_ATM_ABSENT
},
{ /* cause */
T_ATM_ABSENT
}
};
Atm_attributes ipatm_aal5null = {
NULL, /* nif */
CMAPI_CPCS, /* api */
0, /* api_init */
sizeof(struct ifnet *), /* headin */
0, /* headout */
{ /* aal */
T_ATM_PRESENT,
ATM_AAL5
},
{ /* traffic */
T_ATM_PRESENT,
{
{
T_ATM_ABSENT,
0,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_NO
},
{
T_ATM_ABSENT,
0,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_NO
},
T_YES
},
},
{ /* bearer */
T_ATM_PRESENT,
{
T_ATM_CLASS_X,
T_ATM_NULL,
T_ATM_NULL,
T_NO,
T_ATM_1_TO_1
}
},
{ /* bhli */
T_ATM_ABSENT
},
{ /* blli */
T_ATM_ABSENT,
T_ATM_ABSENT
},
{ /* llc */
T_ATM_ABSENT
},
{ /* called */
T_ATM_PRESENT,
},
{ /* calling */
T_ATM_ABSENT
},
{ /* qos */
T_ATM_PRESENT,
{
T_ATM_NETWORK_CODING,
{
T_ATM_QOS_CLASS_0,
},
{
T_ATM_QOS_CLASS_0
}
}
},
{ /* transit */
T_ATM_ABSENT
},
{ /* cause */
T_ATM_ABSENT
}
};
Atm_attributes ipatm_aal4null = {
NULL, /* nif */
CMAPI_CPCS, /* api */
0, /* api_init */
sizeof(struct ifnet *), /* headin */
0, /* headout */
{ /* aal */
T_ATM_PRESENT,
ATM_AAL3_4
},
{ /* traffic */
T_ATM_PRESENT,
{
{
T_ATM_ABSENT,
0,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_NO
},
{
T_ATM_ABSENT,
0,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_NO
},
T_YES
},
},
{ /* bearer */
T_ATM_PRESENT,
{
T_ATM_CLASS_X,
T_ATM_NULL,
T_ATM_NULL,
T_NO,
T_ATM_1_TO_1
}
},
{ /* bhli */
T_ATM_ABSENT
},
{ /* blli */
T_ATM_ABSENT,
T_ATM_ABSENT
},
{ /* llc */
T_ATM_ABSENT
},
{ /* called */
T_ATM_PRESENT,
},
{ /* calling */
T_ATM_ABSENT
},
{ /* qos */
T_ATM_PRESENT,
{
T_ATM_NETWORK_CODING,
{
T_ATM_QOS_CLASS_0,
},
{
T_ATM_QOS_CLASS_0
}
}
},
{ /* transit */
T_ATM_ABSENT
},
{ /* cause */
T_ATM_ABSENT
}
};
static struct t_atm_cause ipatm_cause = {
T_ATM_ITU_CODING,
T_ATM_LOC_USER,
0,
{0, 0, 0, 0}
};
/*
* Open an IP PVC
*
* This function will perform all actions necessary to activate a
* PVC for IP usage. In particular, it will allocate control blocks,
* open the PVC, initialize PVC stack, and initiate whatever ARP
* procedures are required.
*
* Arguments:
* pvp pointer to PVC parameter structure
* sivp address to return pointer to IP PVC control block
*
* Returns:
* 0 PVC was successfully opened
* errno open failed - reason indicated
*
*/
int
ipatm_openpvc(pvp, sivp)
struct ipatmpvc *pvp;
struct ipvcc **sivp;
{
struct ipvcc *ivp = NULL; /* XXX pacify gcc-3.1 */
Atm_attributes *ap;
Atm_addr_pvc *pvcp;
struct atm_nif *nip;
struct ip_nif *inp;
int s, err = 0;
inp = pvp->ipp_ipnif;
nip = inp->inf_nif;
/*
* Make sure interface is ready to go
*/
if (inp->inf_state != IPNIF_ACTIVE) {
err = ENETDOWN;
goto done;
}
/*
* Validate fixed destination IP address
*/
if (pvp->ipp_dst.sin_addr.s_addr != INADDR_ANY) {
if (in_broadcast(pvp->ipp_dst.sin_addr, &nip->nif_if) ||
IN_MULTICAST(ntohl(pvp->ipp_dst.sin_addr.s_addr)) ||
ipatm_chknif(pvp->ipp_dst.sin_addr, inp)) {
err = EINVAL;
goto done;
}
}
/*
* Allocate IP VCC block
*/
ivp = uma_zalloc(ipatm_vc_zone, M_WAITOK);
if (ivp == NULL) {
err = ENOMEM;
goto done;
}
/*
* Initialize the PVC
*/
ivp->iv_flags = IVF_PVC;
if (pvp->ipp_encaps == ATM_ENC_LLC)
ivp->iv_flags |= IVF_LLC;
/*
* Fill out connection attributes
* Make a temporary copy of the attributes here so that we
* do not change the default attributes for SVCs. Otherwise this
* will give trouble in a mixed SVC/PVC case.
*/
ap = malloc(sizeof(*ap), M_TEMP, M_NOWAIT);
if (ap == NULL) {
err = ENOMEM;
goto done;
}
if (pvp->ipp_aal == ATM_AAL5) {
if (pvp->ipp_encaps == ATM_ENC_LLC)
*ap = ipatm_aal5llc;
else
*ap = ipatm_aal5null;
} else {
*ap = ipatm_aal4null;
}
/*
* Build the ATM attributes
*/
ap->nif = nip;
ap->bearer.v.traffic_type = pvp->ipp_traffic_type;
switch(ap->bearer.v.traffic_type) {
case T_ATM_UBR:
case T_ATM_CBR:
/*
* PCR=0 means `use up to the PIF's PCR'
*/
if (pvp->ipp_traffic.forward.PCR_all_traffic == 0)
ap->traffic.v.forward.PCR_all_traffic =
nip->nif_pif->pif_pcr;
else
ap->traffic.v.forward.PCR_all_traffic =
pvp->ipp_traffic.forward.PCR_all_traffic;
if (pvp->ipp_traffic.forward.PCR_high_priority == 0)
ap->traffic.v.forward.PCR_high_priority =
nip->nif_pif->pif_pcr;
else
ap->traffic.v.forward.PCR_high_priority =
pvp->ipp_traffic.forward.PCR_high_priority;
if (pvp->ipp_traffic.backward.PCR_all_traffic == 0)
ap->traffic.v.backward.PCR_all_traffic =
nip->nif_pif->pif_pcr;
else
ap->traffic.v.backward.PCR_all_traffic =
pvp->ipp_traffic.backward.PCR_all_traffic;
if (pvp->ipp_traffic.backward.PCR_high_priority == 0)
ap->traffic.v.backward.PCR_high_priority =
nip->nif_pif->pif_pcr;
else
ap->traffic.v.backward.PCR_high_priority =
pvp->ipp_traffic.backward.PCR_high_priority;
break;
case T_ATM_VBR:
ap->traffic.v.forward.PCR_all_traffic =
pvp->ipp_traffic.forward.PCR_all_traffic;
ap->traffic.v.forward.PCR_high_priority =
pvp->ipp_traffic.forward.PCR_high_priority;
ap->traffic.v.forward.SCR_all_traffic =
pvp->ipp_traffic.forward.SCR_all_traffic;
ap->traffic.v.forward.SCR_high_priority =
pvp->ipp_traffic.forward.SCR_high_priority;
ap->traffic.v.forward.MBS_all_traffic =
pvp->ipp_traffic.forward.MBS_all_traffic;
ap->traffic.v.forward.MBS_high_priority =
pvp->ipp_traffic.forward.MBS_high_priority;
ap->traffic.v.backward.PCR_all_traffic =
pvp->ipp_traffic.backward.PCR_all_traffic;
ap->traffic.v.backward.PCR_high_priority =
pvp->ipp_traffic.backward.PCR_high_priority;
ap->traffic.v.backward.SCR_all_traffic =
pvp->ipp_traffic.backward.SCR_all_traffic;
ap->traffic.v.backward.SCR_high_priority =
pvp->ipp_traffic.backward.SCR_high_priority;
ap->traffic.v.backward.MBS_all_traffic =
pvp->ipp_traffic.backward.MBS_all_traffic;
ap->traffic.v.backward.MBS_high_priority =
pvp->ipp_traffic.backward.MBS_high_priority;
break;
case T_ATM_NULL:
/*
* No traffic type
*/
/* FALLTHRU */
default:
ap->traffic.v.forward.PCR_all_traffic =
nip->nif_pif->pif_pcr;
ap->traffic.v.backward.PCR_all_traffic =
nip->nif_pif->pif_pcr;
break;
}
ap->called.addr.address_format = T_ATM_PVC_ADDR;
ap->called.addr.address_length = sizeof(Atm_addr_pvc);
pvcp = (Atm_addr_pvc *)ap->called.addr.address;
ATM_PVC_SET_VPI(pvcp, pvp->ipp_vpi);
ATM_PVC_SET_VCI(pvcp, pvp->ipp_vci);
ap->called.subaddr.address_format = T_ATM_ABSENT;
ap->called.subaddr.address_length = 0;
/*
* Create PVC
*/
err = atm_cm_connect(&ipatm_endpt, ivp, ap, &ivp->iv_conn);
if (err) {
free(ap, M_TEMP);
uma_zfree(ipatm_vc_zone, ivp);
goto done;
}
/*
* Save PVC information and link in VCC
*/
/* ivp->iv_ = ap->headout; */
free(ap, M_TEMP);
/*
* Queue VCC onto its network interface
*/
s = splnet();
ipatm_vccnt++;
ENQUEUE(ivp, struct ipvcc, iv_elem, inp->inf_vcq);
ivp->iv_ipnif = inp;
(void) splx(s);
/*
* Set destination IP address and IPVCC state
*/
if (pvp->ipp_dst.sin_addr.s_addr == INADDR_ANY) {
/*
* Initiate ARP processing
*/
switch ((*inp->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
*/
ivp->iv_state = IPVCC_ACTPENT;
IPVCC_TIMER(ivp, 1 * ATM_HZ);
break;
default:
panic("ipatm_openpvc: invalid arp_pvcopen return");
}
} else {
/*
* Use configured IP destination
*/
ivp->iv_dst.s_addr = pvp->ipp_dst.sin_addr.s_addr;
ivp->iv_state = IPVCC_ACTIVE;
ivp->iv_flags |= IVF_MAPOK;
}
done:
if (err)
*sivp = NULL;
else
*sivp = ivp;
return (err);
}
/*
* Create an IP SVC
*
* This function will initiate the creation of an IP SVC. The IP VCC
* control block will be initialized and, if required, we will initiate
* ARP processing in order to resolve the destination's ATM address. Once
* the destination ATM address is known, ipatm_opensvc() will be called.
*
* Arguments:
* ifp pointer to destination ifnet structure
* daf destination address family type
* dst pointer to destination address
* sivp address to return pointer to IP SVC control block
*
* Returns:
* 0 SVC creation was successfully initiated
* errno creation failed - reason indicated
*
*/
int
ipatm_createsvc(ifp, daf, dst, sivp)
struct ifnet *ifp;
u_short daf;
caddr_t dst;
struct ipvcc **sivp;
{
struct atm_nif *nip = (struct atm_nif *)ifp;
struct ip_nif *inp;
struct ipvcc *ivp = NULL; /* XXX pacify gcc-3.1 */
struct in_addr *ip;
Atm_addr *atm;
int s, err = 0;
/*
* Get IP interface and make sure its ready
*/
for (inp = ipatm_nif_head; inp; inp = inp->inf_next) {
if (inp->inf_nif == nip)
break;
}
if (inp == NULL) {
err = ENXIO;
goto done;
}
if (inp->inf_state != IPNIF_ACTIVE) {
err = ENETDOWN;
goto done;
}
/*
* Validate destination address
*/
if (daf == AF_INET) {
/*
* Destination is IP address
*/
ip = (struct in_addr *)dst;
atm = NULL;
if (ip->s_addr == INADDR_ANY) {
err = EADDRNOTAVAIL;
goto done;
}
} else if (daf == AF_ATM) {
/*
* Destination is ATM address
*/
atm = (Atm_addr *)dst;
ip = NULL;
if (atm->address_format == T_ATM_ABSENT) {
err = EINVAL;
goto done;
}
} else {
err = EINVAL;
goto done;
}
/*
* Make sure we have services provider and ARP support
*/
if ((inp->inf_serv == NULL) ||
(inp->inf_serv->is_arp_svcout == NULL)) {
err = ENETDOWN;
goto done;
}
/*
* Allocate IP VCC
* May be called from timeout - don't wait.
*/
ivp = uma_zalloc(ipatm_vc_zone, M_NOWAIT);
if (ivp == NULL) {
err = ENOMEM;
goto done;
}
/*
* Initialize SVC
*/
ivp->iv_flags = IVF_SVC;
ivp->iv_ipnif = inp;
/*
* Get destination ATM address
*/
if (daf == AF_INET) {
/*
* ARP is the way...
*/
ivp->iv_dst.s_addr = ip->s_addr;
switch ((*inp->inf_serv->is_arp_svcout)(ivp, ip)) {
case MAP_PROCEEDING:
/*
* Wait for answer
*/
ivp->iv_state = IPVCC_PMAP;
IPVCC_TIMER(ivp, IPATM_ARP_TIME);
break;
case MAP_VALID:
/*
* We've got our answer already, so open SVC
*/
ivp->iv_flags |= IVF_MAPOK;
err = ipatm_opensvc(ivp);
if (err) {
(*inp->inf_serv->is_arp_close)(ivp);
uma_zfree(ipatm_vc_zone, ivp);
goto done;
}
break;
case MAP_FAILED:
/*
* So sorry...come again
*/
uma_zfree(ipatm_vc_zone, ivp);
err = ENETDOWN;
goto done;
default:
panic("ipatm_createsvc: invalid arp_svcout return");
}
} else {
/*
* We were given the ATM address, so open the SVC
*
* Create temporary arp map entry so that opensvc() works.
* Caller must set up a permanent entry immediately! (yuk)
*/
struct arpmap map;
ATM_ADDR_COPY(atm, &map.am_dstatm);
map.am_dstatmsub.address_format = T_ATM_ABSENT;
map.am_dstatmsub.address_length = 0;
ivp->iv_arpent = &map;
err = ipatm_opensvc(ivp);
if (err) {
uma_zfree(ipatm_vc_zone, ivp);
goto done;
}
ivp->iv_arpent = NULL;
}
/*
* Queue VCC onto its network interface
*/
s = splnet();
ipatm_vccnt++;
ENQUEUE(ivp, struct ipvcc, iv_elem, inp->inf_vcq);
(void) splx(s);
done:
if (err)
*sivp = NULL;
else
*sivp = ivp;
return (err);
}
/*
* Open an IP SVC
*
* This function will continue the IP SVC creation process. Here, we
* will issue an SVC open to the signalling manager and then wait for
* the final SVC setup results.
*
* Arguments:
* ivp pointer to IP SVC to open
*
* Returns:
* 0 SVC open was successfully initiated
* errno open failed - reason indicated
*
*/
int
ipatm_opensvc(ivp)
struct ipvcc *ivp;
{
struct ip_nif *inp = ivp->iv_ipnif;
Atm_attributes *ap;
int err = 0, i;
/*
* Cancel possible arp timeout
*/
IPVCC_CANCEL(ivp);
/*
* Fill out connection attributes
*/
i = ivp->iv_parmx;
if (inp->inf_serv->is_vccparm[i].ivc_aal == ATM_AAL5) {
if (inp->inf_serv->is_vccparm[i].ivc_encaps == ATM_ENC_LLC) {
ap = &ipatm_aal5llc;
ivp->iv_flags |= IVF_LLC;
} else {
ap = &ipatm_aal5null;
ivp->iv_flags &= ~IVF_LLC;
}
} else {
ap = &ipatm_aal4null;
ivp->iv_flags &= ~IVF_LLC;
}
ap->nif = inp->inf_nif;
ap->traffic.v.forward.PCR_all_traffic = inp->inf_nif->nif_pif->pif_pcr;
ap->traffic.v.backward.PCR_all_traffic = inp->inf_nif->nif_pif->pif_pcr;
ATM_ADDR_COPY(&ivp->iv_arpent->am_dstatm, &ap->called.addr);
ATM_ADDR_COPY(&ivp->iv_arpent->am_dstatmsub, &ap->called.subaddr);
/*
* Initiate SVC open
*/
err = atm_cm_connect(&ipatm_endpt, ivp, ap, &ivp->iv_conn);
switch (err) {
case EINPROGRESS:
/*
* Call is progressing
*/
/* ivp->iv_ = ap->headout; */
/*
* Now we just wait for a CALL_CONNECTED event
*/
ivp->iv_state = IPVCC_POPEN;
IPVCC_TIMER(ivp, IPATM_SVC_TIME);
err = 0;
break;
case 0:
/*
* We've been hooked up with a shared VCC
*/
/* ivp->iv_ = ap->headout; */
ipatm_activate(ivp);
break;
}
return (err);
}
/*
* Retry an IP SVC Open
*
* This function will attempt to retry a failed SVC open request. The IP
* interface service provider specifies a list of possible VCC parameters
* for IP to use. We will try each set of parameters in turn until either
* an open succeeds or we reach the end of the list.
*
* Arguments:
* ivp pointer to IP SVC
*
* Returns:
* 0 SVC (re)open was successfully initiated
* else retry failed
*
*/
int
ipatm_retrysvc(ivp)
struct ipvcc *ivp;
{
struct ip_nif *inp = ivp->iv_ipnif;
/*
* If there isn't another set of vcc parameters to try, return
*/
if ((++ivp->iv_parmx >= IPATM_VCCPARMS) ||
(inp->inf_serv->is_vccparm[ivp->iv_parmx].ivc_aal == 0))
return (1);
/*
* Okay, now initiate open with a new set of parameters
*/
return (ipatm_opensvc(ivp));
}
/*
* Finish IP SVC Activation
*
* Arguments:
* ivp pointer to IP SVC
*
* Returns:
* none
*
*/
void
ipatm_activate(ivp)
struct ipvcc *ivp;
{
/*
* Connection is now active
*/
ivp->iv_state = IPVCC_ACTIVE;
IPVCC_CANCEL(ivp);
/*
* Tell ARP module that connection is active
*/
if ((*ivp->iv_ipnif->inf_serv->is_arp_svcact)(ivp)) {
(void) ipatm_closevc(ivp, T_ATM_CAUSE_TEMPORARY_FAILURE);
return;
}
/*
* Send any queued packet
*/
if ((ivp->iv_flags & IVF_MAPOK) && ivp->iv_queue) {
struct sockaddr_in sin;
struct ifnet *ifp;
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;
}
}
/*
* Process Incoming Calls
*
* This function will receive control when an incoming call has been matched
* to one of our registered listen parameter blocks. Assuming the call passes
* acceptance criteria and all required resources are available, we will
* create an IP SVC and notify the connection manager of our decision. We
* will then await notification of the final SVC setup results. If any
* problems are encountered, we will just tell the connection manager to
* reject the call.
*
* Called at splnet.
*
* Arguments:
* tok owner's matched listening token
* cop pointer to incoming call's connection block
* ap pointer to incoming call's attributes
* tokp pointer to location to store our connection token
*
* Returns:
* 0 call is accepted
* errno call rejected - reason indicated
*
*/
int
ipatm_incoming(tok, cop, ap, tokp)
void *tok;
Atm_connection *cop;
Atm_attributes *ap;
void **tokp;
{
struct atm_nif *nip = ap->nif;
struct ip_nif *inp;
struct ipvcc *ivp = NULL;
int err, cause;
int usellc = 0, mtu = ATM_NIF_MTU;
/*
* Get IP interface and make sure its ready
*/
for (inp = ipatm_nif_head; inp; inp = inp->inf_next) {
if (inp->inf_nif == nip)
break;
}
if ((inp == NULL) || (inp->inf_state != IPNIF_ACTIVE)) {
err = ENETUNREACH;
cause = T_ATM_CAUSE_SERVICE_OR_OPTION_UNAVAILABLE;
goto reject;
}
/*
* Make sure we have services provider and ARP support
*/
if ((inp->inf_serv == NULL) ||
(inp->inf_serv->is_arp_svcin == NULL)) {
err = ENETUNREACH;
cause = T_ATM_CAUSE_SERVICE_OR_OPTION_UNAVAILABLE;
goto reject;
}
/*
* Check for LLC encapsulation
*/
if ((ap->blli.tag_l2 == T_ATM_PRESENT) &&
(ap->blli.v.layer_2_protocol.ID_type == T_ATM_SIMPLE_ID) &&
(ap->blli.v.layer_2_protocol.ID.simple_ID == T_ATM_BLLI2_I8802)) {
usellc = 1;
mtu += IPATM_LLC_LEN;
}
/*
* Verify requested 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)) {
err = ENETUNREACH;
cause = T_ATM_CAUSE_AAL_PARAMETERS_NOT_SUPPORTED;
goto reject;
}
} else {
if ((ap->aal.v.aal4.forward_max_SDU_size > mtu) ||
(ap->aal.v.aal4.backward_max_SDU_size < mtu)) {
err = ENETUNREACH;
cause = T_ATM_CAUSE_AAL_PARAMETERS_NOT_SUPPORTED;
goto reject;
}
}
/*
* Allocate IP VCC
* May be called from timeout - don't wait.
*/
ivp = uma_zalloc(ipatm_vc_zone, M_NOWAIT);
if (ivp == NULL) {
err = ENOMEM;
cause = T_ATM_CAUSE_UNSPECIFIED_RESOURCE_UNAVAILABLE;
goto reject;
}
/*
* Initialize SVC
*/
ivp->iv_flags = IVF_SVC;
ivp->iv_ipnif = inp;
if (usellc)
ivp->iv_flags |= IVF_LLC;
/*
* Lookup ARP entry for destination
*/
switch ((*inp->inf_serv->is_arp_svcin)
(ivp, &ap->calling.addr, &ap->calling.subaddr)) {
case MAP_PROCEEDING:
/*
* We'll be (hopefully) notified later
*/
break;
case MAP_VALID:
/*
* We've got our answer already
*/
ivp->iv_flags |= IVF_MAPOK;
ivp->iv_dst.s_addr = ivp->iv_arpent->am_dstip.s_addr;
break;
case MAP_FAILED:
/*
* So sorry...come again
*/
err = ENETUNREACH;
cause = T_ATM_CAUSE_SERVICE_OR_OPTION_UNAVAILABLE;
goto reject;
default:
panic("ipatm_incoming: invalid arp_svcin return");
}
/*
* Accept SVC connection
*/
ivp->iv_state = IPVCC_PACCEPT;
/*
* Save VCC information
*/
ivp->iv_conn = cop;
*tokp = ivp;
/* ivp->iv_ = ap->headout; */
/*
* Queue VCC onto its network interface
*/
ipatm_vccnt++;
ENQUEUE(ivp, struct ipvcc, iv_elem, inp->inf_vcq);
/*
* Wait for a CALL_CONNECTED event
*/
IPVCC_TIMER(ivp, IPATM_SVC_TIME);
return (0);
reject:
/*
* Clean up after call failure
*/
if (ivp) {
(*inp->inf_serv->is_arp_close)(ivp);
uma_zfree(ipatm_vc_zone, ivp);
}
ap->cause.tag = T_ATM_PRESENT;
ap->cause.v = ipatm_cause;
ap->cause.v.cause_value = cause;
return (err);
}
/*
* Close an IP VCC
*
* This function will close an IP VCC (PVC or SVC), including notifying
* the signalling and ARP subsystems of the VCC's demise and cleaning
* up memory after ourselves.
*
* Arguments:
* ivp pointer to VCC
* code cause code
*
* Returns:
* 0 VCC successfully closed
* errno close failed - reason indicated
*
*/
int
ipatm_closevc(ivp, code)
struct ipvcc *ivp;
int code;
{
struct ip_nif *inp = ivp->iv_ipnif;
int s, err;
/*
* Make sure VCC hasn't been through here already
*/
switch (ivp->iv_state) {
case IPVCC_FREE:
return (EALREADY);
}
/*
* Reset lookup cache
*/
if (last_map_ipvcc == ivp) {
last_map_ipvcc = NULL;
last_map_ipdst = 0;
}
/*
* Tell ARP about SVCs and dynamic PVCs
*/
if (inp->inf_serv &&
((ivp->iv_flags & IVF_SVC) || inp->inf_serv->is_arp_pvcopen)) {
(*inp->inf_serv->is_arp_close)(ivp);
}
/*
* Free queued packets
*/
if (ivp->iv_queue)
KB_FREEALL(ivp->iv_queue);
/*
* Cancel any timers
*/
IPVCC_CANCEL(ivp);
/*
* Close VCC
*/
switch (ivp->iv_state) {
case IPVCC_PMAP:
break;
case IPVCC_POPEN:
case IPVCC_PACCEPT:
case IPVCC_ACTPENT:
case IPVCC_ACTIVE:
ipatm_cause.cause_value = code;
err = atm_cm_release(ivp->iv_conn, &ipatm_cause);
if (err) {
log(LOG_ERR,
"ipatm_closevc: release fail: err=%d\n", err);
}
break;
case IPVCC_CLOSED:
break;
default:
log(LOG_ERR,
"ipatm_closevc: unknown state: ivp=%p, state=%d\n",
ivp, ivp->iv_state);
}
/*
* Remove VCC from network i/f
*/
s = splnet();
DEQUEUE(ivp, struct ipvcc, iv_elem, inp->inf_vcq);
/*
* Reset state just to be sure
*/
ivp->iv_state = IPVCC_FREE;
/*
* If ARP module is done with VCC too, then free it
*/
if (ivp->iv_arpconn == NULL)
uma_zfree(ipatm_vc_zone, ivp);
ipatm_vccnt--;
(void) splx(s);
return (0);
}
/*
* Check if IP address is valid on a Network Interface
*
* Checks whether the supplied IP address is allowed to be assigned to
* the supplied IP network interface.
*
* Arguments:
* in IP address
* inp pointer to IP network interface
*
* Returns:
* 0 - OK to assign
* 1 - not valid to assign
*
*/
int
ipatm_chknif(in, inp)
struct in_addr in;
struct ip_nif *inp;
{
struct in_ifaddr *ia;
u_long i;
/*
* Make sure there's an interface requested
*/
if (inp == NULL)
return (1);
/*
* Make sure we have an IP address
*/
i = ntohl(in.s_addr);
if (i == 0)
return (1);
/*
* Make sure an interface address is set
*/
ia = inp->inf_addr;
if (ia == NULL)
return (1);
/*
* Make sure we're on the right subnet
*/
if ((i & ia->ia_subnetmask) != ia->ia_subnet)
return (1);
return (0);
}
/*
* Map an IP Address to an IP VCC
*
* Given a destination IP address, this function will return a pointer
* to the appropriate output IP VCC to which to send the packet.
* This is currently implemented using a one-behind cache containing the
* last successful mapping result. If the cache lookup fails, then a
* simple linear search of all IP VCCs on the destination network interface
* is performed. This is obviously an area to look at for performance
* improvements.
*
* Arguments:
* dst pointer to destination IP address
* nip pointer to destination network interface
*
* Returns:
* addr pointer to located IP VCC
* 0 no such mapping exists
*
*/
struct ipvcc *
ipatm_iptovc(dst, nip)
struct sockaddr_in *dst;
struct atm_nif *nip;
{
struct ip_nif *inp;
struct ipvcc *ivp;
u_long dstip = dst->sin_addr.s_addr;
int s;
/*
* Look in cache first
*/
if (last_map_ipdst == dstip)
return (last_map_ipvcc);
/*
* Oh well, we've got to search for it...first find the interface
*/
s = splnet();
for (inp = ipatm_nif_head; inp; inp = inp->inf_next) {
if (inp->inf_nif == nip)
break;
}
if (inp == NULL) {
(void) splx(s);
return (NULL);
}
/*
* Now home in on the VCC
*/
for (ivp = Q_HEAD(inp->inf_vcq, struct ipvcc); ivp;
ivp = Q_NEXT(ivp, struct ipvcc, iv_elem)) {
if (ivp->iv_dst.s_addr == dstip)
break;
}
/*
* Update lookup cache
*/
if (ivp) {
last_map_ipdst = dstip;
last_map_ipvcc = ivp;
}
(void) splx(s);
return (ivp);
}