508 lines
11 KiB
C
508 lines
11 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
|
|
* -------------------
|
|
*
|
|
* Process user requests
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/types.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.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_vc.h>
|
|
#include <netatm/atm_ioctl.h>
|
|
#include <netatm/atm_stack.h>
|
|
#include <netatm/atm_pcb.h>
|
|
#include <netatm/atm_var.h>
|
|
|
|
#include <netatm/ipatm/ipatm_var.h>
|
|
#include <netatm/ipatm/ipatm_serv.h>
|
|
|
|
/*
|
|
* Process IP PF_ATM ioctls
|
|
*
|
|
* Called at splnet.
|
|
*
|
|
* Arguments:
|
|
* code PF_ATM sub-operation code
|
|
* data pointer to code specific parameter data area
|
|
* arg1 pointer to code specific argument
|
|
*
|
|
* Returns:
|
|
* 0 request procesed
|
|
* errno error processing request - reason indicated
|
|
*
|
|
*/
|
|
int
|
|
ipatm_ioctl(code, data, arg1)
|
|
int code;
|
|
caddr_t data;
|
|
caddr_t arg1;
|
|
{
|
|
struct atmaddreq *aap;
|
|
struct atmdelreq *adp;
|
|
struct atminfreq *aip;
|
|
struct air_ip_vcc_rsp aivr;
|
|
struct atm_nif *nip;
|
|
struct ip_nif *inp;
|
|
struct ipvcc *ivp;
|
|
struct vccb *vcp;
|
|
struct ipatmpvc pv;
|
|
caddr_t cp;
|
|
struct in_addr ip;
|
|
int err = 0;
|
|
size_t space;
|
|
struct t_atm_traffic *traf;
|
|
|
|
|
|
switch (code) {
|
|
|
|
case AIOCS_ADD_PVC:
|
|
/*
|
|
* Add an IP PVC
|
|
*/
|
|
aap = (struct atmaddreq *)data;
|
|
|
|
/*
|
|
* Find the IP network interface
|
|
*/
|
|
if ((nip = atm_nifname(aap->aar_pvc_intf)) == NULL) {
|
|
err = ENXIO;
|
|
break;
|
|
}
|
|
|
|
for (inp = ipatm_nif_head; inp; inp = inp->inf_next) {
|
|
if (inp->inf_nif == nip)
|
|
break;
|
|
}
|
|
if (inp == NULL) {
|
|
err = ENXIO;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Validate PVC params
|
|
*/
|
|
if (aap->aar_pvc_aal == ATM_AAL5) {
|
|
if ((aap->aar_pvc_encaps != ATM_ENC_LLC) &&
|
|
(aap->aar_pvc_encaps != ATM_ENC_NULL)) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
} else if (aap->aar_pvc_aal == ATM_AAL3_4) {
|
|
if (aap->aar_pvc_encaps != ATM_ENC_NULL) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
} else {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (aap->aar_pvc_flags & PVC_DYN) {
|
|
/*
|
|
* For dynamic PVC destination addressing, the
|
|
* network interface must have support for this
|
|
*/
|
|
if ((inp->inf_serv == NULL) ||
|
|
(inp->inf_serv->is_arp_pvcopen == NULL)) {
|
|
err = EDESTADDRREQ;
|
|
break;
|
|
}
|
|
} else {
|
|
u_long dst = ((struct sockaddr_in *)&aap->aar_pvc_dst)
|
|
->sin_addr.s_addr;
|
|
|
|
if (dst == INADDR_ANY) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Validate PVC traffic
|
|
*/
|
|
#define MAXVAL(bits) ((1 << bits) - 1)
|
|
#define MAXMASK(bits) (~MAXVAL(bits))
|
|
traf = &aap->aar_pvc_traffic;
|
|
switch (aap->aar_pvc_traffic_type) {
|
|
|
|
case T_ATM_CBR:
|
|
case T_ATM_UBR:
|
|
/*
|
|
* PCR is a value between 0 to the PIF's PCR
|
|
*/
|
|
if (traf->forward.PCR_high_priority == T_ATM_ABSENT ||
|
|
(traf->forward.PCR_high_priority & MAXMASK(24))) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
if (traf->forward.PCR_all_traffic == T_ATM_ABSENT ||
|
|
(traf->forward.PCR_all_traffic & MAXMASK(24))) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (traf->backward.PCR_high_priority == T_ATM_ABSENT ||
|
|
(traf->backward.PCR_high_priority & MAXMASK(24))) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
if (traf->backward.PCR_all_traffic == T_ATM_ABSENT ||
|
|
(traf->backward.PCR_all_traffic & MAXMASK(24))) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case T_ATM_VBR:
|
|
/*
|
|
* PCR, SCR and MBS are required
|
|
*/
|
|
if (traf->forward.PCR_high_priority == T_ATM_ABSENT ||
|
|
(traf->forward.PCR_high_priority & MAXMASK(24)) ||
|
|
traf->forward.PCR_all_traffic == T_ATM_ABSENT ||
|
|
(traf->forward.PCR_all_traffic & MAXMASK(24))) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
if (traf->forward.SCR_high_priority == T_ATM_ABSENT ||
|
|
(traf->forward.SCR_high_priority & MAXMASK(24)) ||
|
|
traf->forward.SCR_all_traffic == T_ATM_ABSENT ||
|
|
(traf->forward.SCR_all_traffic & MAXMASK(24))) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
if (traf->forward.MBS_high_priority == T_ATM_ABSENT ||
|
|
(traf->forward.MBS_high_priority & MAXMASK(24)) ||
|
|
traf->forward.MBS_all_traffic == T_ATM_ABSENT ||
|
|
(traf->forward.MBS_all_traffic & MAXMASK(24))) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (traf->backward.PCR_high_priority == T_ATM_ABSENT ||
|
|
(traf->backward.PCR_high_priority & MAXMASK(24)) ||
|
|
traf->backward.PCR_all_traffic == T_ATM_ABSENT ||
|
|
(traf->backward.PCR_all_traffic & MAXMASK(24))) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
if (traf->backward.SCR_high_priority == T_ATM_ABSENT ||
|
|
(traf->backward.SCR_high_priority & MAXMASK(24)) ||
|
|
traf->backward.SCR_all_traffic == T_ATM_ABSENT ||
|
|
(traf->backward.SCR_all_traffic & MAXMASK(24))) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
if (traf->backward.MBS_high_priority == T_ATM_ABSENT ||
|
|
(traf->backward.MBS_high_priority & MAXMASK(24)) ||
|
|
traf->backward.MBS_all_traffic == T_ATM_ABSENT ||
|
|
(traf->backward.MBS_all_traffic & MAXMASK(24))) {
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case T_ATM_NULL:
|
|
/*
|
|
* No PVC traffic type
|
|
*/
|
|
break;
|
|
|
|
default:
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
if (err != 0)
|
|
break;
|
|
|
|
/*
|
|
* Build connection request
|
|
*/
|
|
pv.ipp_ipnif = inp;
|
|
pv.ipp_vpi = aap->aar_pvc_vpi;
|
|
pv.ipp_vci = aap->aar_pvc_vci;
|
|
pv.ipp_traffic_type = aap->aar_pvc_traffic_type;
|
|
pv.ipp_traffic = aap->aar_pvc_traffic;
|
|
pv.ipp_encaps = aap->aar_pvc_encaps;
|
|
pv.ipp_aal = aap->aar_pvc_aal;
|
|
if (aap->aar_pvc_flags & PVC_DYN) {
|
|
pv.ipp_dst.sin_addr.s_addr = INADDR_ANY;
|
|
} else
|
|
pv.ipp_dst = *(struct sockaddr_in *)&aap->aar_pvc_dst;
|
|
|
|
/*
|
|
* Open a new VCC
|
|
*/
|
|
err = ipatm_openpvc(&pv, &ivp);
|
|
break;
|
|
|
|
case AIOCS_ADD_ARP:
|
|
/*
|
|
* Add an ARP mapping
|
|
*/
|
|
aap = (struct atmaddreq *)data;
|
|
|
|
/*
|
|
* Validate IP address
|
|
*/
|
|
if (aap->aar_arp_dst.sa_family != AF_INET) {
|
|
err = EAFNOSUPPORT;
|
|
break;
|
|
}
|
|
ip = SATOSIN(&aap->aar_arp_dst)->sin_addr;
|
|
|
|
if (aap->aar_arp_intf[0] == '\0') {
|
|
/*
|
|
* Find the IP network interface associated with
|
|
* the supplied IP address
|
|
*/
|
|
for (inp = ipatm_nif_head; inp; inp = inp->inf_next) {
|
|
if (ipatm_chknif(ip, inp) == 0)
|
|
break;
|
|
}
|
|
if (inp == NULL) {
|
|
err = EADDRNOTAVAIL;
|
|
break;
|
|
}
|
|
} else {
|
|
/*
|
|
* Find the specified IP network interface
|
|
*/
|
|
if ((nip = atm_nifname(aap->aar_arp_intf)) == NULL) {
|
|
err = ENXIO;
|
|
break;
|
|
}
|
|
for (inp = ipatm_nif_head; inp; inp = inp->inf_next) {
|
|
if (inp->inf_nif == nip)
|
|
break;
|
|
}
|
|
if (inp == NULL) {
|
|
err = ENXIO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((ip.s_addr == INADDR_ANY) ||
|
|
in_broadcast(ip, &inp->inf_nif->nif_if) ||
|
|
IN_MULTICAST(ntohl(ip.s_addr))) {
|
|
err = EADDRNOTAVAIL;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Notify the responsible ARP service
|
|
*
|
|
* XXX: if there is one. No idea how this happens, but at
|
|
* least don't panic on a NULL pointer if it does.
|
|
*/
|
|
if (inp->inf_serv == NULL) {
|
|
err = ENXIO;
|
|
break;
|
|
}
|
|
err = (*inp->inf_serv->is_ioctl)(code, data, inp->inf_isintf);
|
|
break;
|
|
|
|
case AIOCS_DEL_ARP:
|
|
/*
|
|
* Delete an ARP mapping
|
|
*/
|
|
adp = (struct atmdelreq *)data;
|
|
|
|
/*
|
|
* Validate IP address
|
|
*/
|
|
if (adp->adr_arp_dst.sa_family != AF_INET) {
|
|
err = EAFNOSUPPORT;
|
|
break;
|
|
}
|
|
ip = SATOSIN(&adp->adr_arp_dst)->sin_addr;
|
|
|
|
if (adp->adr_arp_intf[0] == '\0') {
|
|
/*
|
|
* Find the IP network interface associated with
|
|
* the supplied IP address
|
|
*/
|
|
for (inp = ipatm_nif_head; inp; inp = inp->inf_next) {
|
|
if (ipatm_chknif(ip, inp) == 0)
|
|
break;
|
|
}
|
|
if (inp == NULL) {
|
|
err = EADDRNOTAVAIL;
|
|
break;
|
|
}
|
|
} else {
|
|
/*
|
|
* Find the specified IP network interface
|
|
*/
|
|
if ((nip = atm_nifname(adp->adr_arp_intf)) == NULL) {
|
|
err = ENXIO;
|
|
break;
|
|
}
|
|
for (inp = ipatm_nif_head; inp; inp = inp->inf_next) {
|
|
if (inp->inf_nif == nip)
|
|
break;
|
|
}
|
|
if (inp == NULL) {
|
|
err = ENXIO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((ip.s_addr == INADDR_ANY) ||
|
|
in_broadcast(ip, &inp->inf_nif->nif_if) ||
|
|
IN_MULTICAST(ntohl(ip.s_addr))) {
|
|
err = EADDRNOTAVAIL;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Notify the responsible ARP service
|
|
*/
|
|
err = (*inp->inf_serv->is_ioctl)(code, data, inp->inf_isintf);
|
|
break;
|
|
|
|
case AIOCS_INF_IPM:
|
|
/*
|
|
* Get IP VCC information
|
|
*/
|
|
aip = (struct atminfreq *)data;
|
|
|
|
if (aip->air_ip_addr.sa_family != AF_INET)
|
|
break;
|
|
ip = SATOSIN(&aip->air_ip_addr)->sin_addr;
|
|
|
|
cp = aip->air_buf_addr;
|
|
space = aip->air_buf_len;
|
|
|
|
/*
|
|
* Loop through all our interfaces
|
|
*/
|
|
for (inp = ipatm_nif_head; inp; inp = inp->inf_next) {
|
|
/*
|
|
* Check out each VCC
|
|
*/
|
|
for (ivp = Q_HEAD(inp->inf_vcq, struct ipvcc); ivp;
|
|
ivp = Q_NEXT(ivp, struct ipvcc, iv_elem)) {
|
|
|
|
if ((ip.s_addr != INADDR_ANY) &&
|
|
(ip.s_addr != ivp->iv_dst.s_addr))
|
|
continue;
|
|
|
|
/*
|
|
* Make sure there's room in user buffer
|
|
*/
|
|
if (space < sizeof(aivr)) {
|
|
err = ENOSPC;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Fill in info to be returned
|
|
*/
|
|
bzero((caddr_t)&aivr, sizeof(aivr));
|
|
SATOSIN(&aivr.aip_dst_addr)->sin_family =
|
|
AF_INET;
|
|
SATOSIN(&aivr.aip_dst_addr)->sin_addr.s_addr =
|
|
ivp->iv_dst.s_addr;
|
|
strlcpy(aivr.aip_intf,
|
|
inp->inf_nif->nif_if.if_xname,
|
|
sizeof(aivr.aip_intf));
|
|
if ((ivp->iv_conn) &&
|
|
(ivp->iv_conn->co_connvc) &&
|
|
(vcp = ivp->iv_conn->co_connvc->cvc_vcc)) {
|
|
aivr.aip_vpi = vcp->vc_vpi;
|
|
aivr.aip_vci = vcp->vc_vci;
|
|
aivr.aip_sig_proto = vcp->vc_proto;
|
|
}
|
|
aivr.aip_flags = ivp->iv_flags;
|
|
aivr.aip_state = ivp->iv_state;
|
|
|
|
/*
|
|
* Copy data to user buffer and
|
|
* update buffer controls
|
|
*/
|
|
err = copyout((caddr_t)&aivr, cp, sizeof(aivr));
|
|
if (err)
|
|
break;
|
|
cp += sizeof(aivr);
|
|
space -= sizeof(aivr);
|
|
}
|
|
if (err)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Update buffer pointer/count
|
|
*/
|
|
aip->air_buf_addr = cp;
|
|
aip->air_buf_len = space;
|
|
break;
|
|
|
|
default:
|
|
err = EOPNOTSUPP;
|
|
}
|
|
|
|
return (err);
|
|
}
|
|
|
|
|
|
/*
|
|
* Get Connection's Application/Owner Name
|
|
*
|
|
* Arguments:
|
|
* tok ipatm connection token (pointer to ipvcc)
|
|
*
|
|
* Returns:
|
|
* addr pointer to string containing our name
|
|
*
|
|
*/
|
|
caddr_t
|
|
ipatm_getname(tok)
|
|
void *tok;
|
|
{
|
|
return ("IP");
|
|
}
|
|
|