text: /*
   * ===================================
   * 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: src/sys/netatm/ipatm/ipatm_usrreq.c,v 1.14 2003/10/31 18:32:10 brooks Exp $");
  
  #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");
  }
  
  