/* * * =================================== * 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: src/sys/netatm/atm_socket.c,v 1.4 1999/08/28 00:48:37 peter Exp $ * @(#) $DragonFly: src/sys/netproto/atm/atm_socket.c,v 1.7 2005/02/01 00:51:50 joerg Exp $ */ /* * Core ATM Services * ----------------- * * ATM common socket protocol processing * */ #include "kern_include.h" /* * Local functions */ /* * Local variables */ static struct sp_info atm_pcb_pool = { "atm pcb pool", /* si_name */ sizeof(Atm_pcb), /* si_blksiz */ 10, /* si_blkcnt */ 100 /* si_maxallow */ }; static struct t_atm_cause atm_sock_cause = { T_ATM_ITU_CODING, T_ATM_LOC_USER, T_ATM_CAUSE_UNSPECIFIED_NORMAL, {0, 0, 0, 0} }; /* * Allocate resources for a new ATM socket * * Called at splnet. * * Arguments: * so pointer to socket * send socket send buffer maximum * recv socket receive buffer maximum * * Returns: * 0 attach successful * errno attach failed - reason indicated * */ int atm_sock_attach(so, send, recv, rl) struct socket *so; u_long send; u_long recv; struct rlimit *rl; { Atm_pcb *atp = sotoatmpcb(so); int err; /* * Make sure initialization has happened */ if (!atm_init) atm_initialize(); /* * Make sure we're not already attached */ if (atp) return (EISCONN); /* * Reserve socket buffer space, if not already done */ if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) { err = soreserve(so, send, recv, rl); if (err) return (err); } /* * Allocate and initialize our control block */ atp = (Atm_pcb *)atm_allocate(&atm_pcb_pool); if (atp == NULL) return (ENOMEM); atp->atp_socket = so; so->so_pcb = (caddr_t)atp; return (0); } /* * Detach from socket and free resources * * Called at splnet. * * Arguments: * so pointer to socket * * Returns: * 0 detach successful * errno detach failed - reason indicated * */ int atm_sock_detach(so) struct socket *so; { Atm_pcb *atp = sotoatmpcb(so); /* * Make sure we're still attached */ if (atp == NULL) return (ENOTCONN); /* * Terminate any (possibly pending) connection */ if (atp->atp_conn) { (void) atm_sock_disconnect(so); } /* * Break links and free control blocks */ so->so_pcb = NULL; sofree(so); atm_free((caddr_t)atp); return (0); } /* * Bind local address to socket * * Called at splnet. * * Arguments: * so pointer to socket * addr pointer to protocol address * * Returns: * 0 request processed * errno error processing request - reason indicated * */ int atm_sock_bind(so, addr) struct socket *so; struct sockaddr *addr; { Atm_pcb *atp = sotoatmpcb(so); Atm_attributes attr; struct sockaddr_atm *satm; struct t_atm_sap_addr *sapadr; struct t_atm_sap_layer2 *sapl2; struct t_atm_sap_layer3 *sapl3; struct t_atm_sap_appl *sapapl; /* * Make sure we're still attached */ if (atp == NULL) return (ENOTCONN); /* * Can't change local address once we've started connection process */ if (atp->atp_conn != NULL) return (EADDRNOTAVAIL); /* * Validate requested local address */ satm = (struct sockaddr_atm *)addr; if (satm->satm_family != AF_ATM) return (EAFNOSUPPORT); sapadr = &satm->satm_addr.t_atm_sap_addr; if (sapadr->SVE_tag_addr == T_ATM_PRESENT) { if (sapadr->address_format == T_ATM_ENDSYS_ADDR) { if (sapadr->SVE_tag_selector != T_ATM_PRESENT) return (EINVAL); } else if (sapadr->address_format == T_ATM_E164_ADDR) { if (sapadr->SVE_tag_selector != T_ATM_ABSENT) return (EINVAL); } else return (EINVAL); } else if ((sapadr->SVE_tag_addr != T_ATM_ABSENT) && (sapadr->SVE_tag_addr != T_ATM_ANY)) return (EINVAL); if (sapadr->address_length > ATM_ADDR_LEN) return (EINVAL); sapl2 = &satm->satm_addr.t_atm_sap_layer2; if (sapl2->SVE_tag == T_ATM_PRESENT) { if ((sapl2->ID_type != T_ATM_SIMPLE_ID) && (sapl2->ID_type != T_ATM_USER_ID)) return (EINVAL); } else if ((sapl2->SVE_tag != T_ATM_ABSENT) && (sapl2->SVE_tag != T_ATM_ANY)) return (EINVAL); sapl3 = &satm->satm_addr.t_atm_sap_layer3; if (sapl3->SVE_tag == T_ATM_PRESENT) { if ((sapl3->ID_type != T_ATM_SIMPLE_ID) && (sapl3->ID_type != T_ATM_IPI_ID) && (sapl3->ID_type != T_ATM_SNAP_ID) && (sapl3->ID_type != T_ATM_USER_ID)) return (EINVAL); } else if ((sapl3->SVE_tag != T_ATM_ABSENT) && (sapl3->SVE_tag != T_ATM_ANY)) return (EINVAL); sapapl = &satm->satm_addr.t_atm_sap_appl; if (sapapl->SVE_tag == T_ATM_PRESENT) { if ((sapapl->ID_type != T_ATM_ISO_APP_ID) && (sapapl->ID_type != T_ATM_USER_APP_ID) && (sapapl->ID_type != T_ATM_VENDOR_APP_ID)) return (EINVAL); } else if ((sapapl->SVE_tag != T_ATM_ABSENT) && (sapapl->SVE_tag != T_ATM_ANY)) return (EINVAL); /* * Create temporary attributes list so that we can check out the * new bind parameters before we modify the socket's values; */ attr = atp->atp_attr; attr.called.tag = sapadr->SVE_tag_addr; KM_COPY(&sapadr->address_format, &attr.called.addr, sizeof(Atm_addr)); attr.blli.tag_l2 = sapl2->SVE_tag; if (sapl2->SVE_tag == T_ATM_PRESENT) { attr.blli.v.layer_2_protocol.ID_type = sapl2->ID_type; KM_COPY(&sapl2->ID, &attr.blli.v.layer_2_protocol.ID, sizeof(attr.blli.v.layer_2_protocol.ID)); } attr.blli.tag_l3 = sapl3->SVE_tag; if (sapl3->SVE_tag == T_ATM_PRESENT) { attr.blli.v.layer_3_protocol.ID_type = sapl3->ID_type; KM_COPY(&sapl3->ID, &attr.blli.v.layer_3_protocol.ID, sizeof(attr.blli.v.layer_3_protocol.ID)); } attr.bhli.tag = sapapl->SVE_tag; if (sapapl->SVE_tag == T_ATM_PRESENT) { attr.bhli.v.ID_type = sapapl->ID_type; KM_COPY(&sapapl->ID, &attr.bhli.v.ID, sizeof(attr.bhli.v.ID)); } /* * Make sure we have unique listening attributes */ if (atm_cm_match(&attr, NULL) != NULL) return (EADDRINUSE); /* * Looks good, save new attributes */ atp->atp_attr = attr; return (0); } /* * Listen for incoming connections * * Called at splnet. * * Arguments: * so pointer to socket * epp pointer to endpoint definition structure * * Returns: * 0 request processed * errno error processing request - reason indicated * */ int atm_sock_listen(so, epp) struct socket *so; Atm_endpoint *epp; { Atm_pcb *atp = sotoatmpcb(so); /* * Make sure we're still attached */ if (atp == NULL) return (ENOTCONN); /* * Start listening for incoming calls */ return (atm_cm_listen(epp, atp, &atp->atp_attr, &atp->atp_conn)); } /* * Connect socket to peer * * Called at splnet. * * Arguments: * so pointer to socket * addr pointer to protocol address * epp pointer to endpoint definition structure * * Returns: * 0 request processed * errno error processing request - reason indicated * */ int atm_sock_connect(so, addr, epp) struct socket *so; struct sockaddr *addr; Atm_endpoint *epp; { Atm_pcb *atp = sotoatmpcb(so); struct sockaddr_atm *satm; struct t_atm_sap_addr *sapadr; struct t_atm_sap_layer2 *sapl2; struct t_atm_sap_layer3 *sapl3; struct t_atm_sap_appl *sapapl; int err; /* * Make sure we're still attached */ if (atp == NULL) return (ENOTCONN); /* * Validate requested peer address */ satm = (struct sockaddr_atm *)addr; if (satm->satm_family != AF_ATM) return (EAFNOSUPPORT); sapadr = &satm->satm_addr.t_atm_sap_addr; if (sapadr->SVE_tag_addr != T_ATM_PRESENT) return (EINVAL); if (sapadr->address_format == T_ATM_ENDSYS_ADDR) { if (sapadr->SVE_tag_selector != T_ATM_PRESENT) return (EINVAL); } else if (sapadr->address_format == T_ATM_E164_ADDR) { if (sapadr->SVE_tag_selector != T_ATM_ABSENT) return (EINVAL); } else if (sapadr->address_format == T_ATM_PVC_ADDR) { if (sapadr->SVE_tag_selector != T_ATM_ABSENT) return (EINVAL); } else return (EINVAL); if (sapadr->address_length > ATM_ADDR_LEN) return (EINVAL); sapl2 = &satm->satm_addr.t_atm_sap_layer2; if (sapl2->SVE_tag == T_ATM_PRESENT) { if ((sapl2->ID_type != T_ATM_SIMPLE_ID) && (sapl2->ID_type != T_ATM_USER_ID)) return (EINVAL); } else if (sapl2->SVE_tag != T_ATM_ABSENT) return (EINVAL); sapl3 = &satm->satm_addr.t_atm_sap_layer3; if (sapl3->SVE_tag == T_ATM_PRESENT) { if ((sapl3->ID_type != T_ATM_SIMPLE_ID) && (sapl3->ID_type != T_ATM_IPI_ID) && (sapl3->ID_type != T_ATM_SNAP_ID) && (sapl3->ID_type != T_ATM_USER_ID)) return (EINVAL); } else if (sapl3->SVE_tag != T_ATM_ABSENT) return (EINVAL); sapapl = &satm->satm_addr.t_atm_sap_appl; if (sapapl->SVE_tag == T_ATM_PRESENT) { if ((sapapl->ID_type != T_ATM_ISO_APP_ID) && (sapapl->ID_type != T_ATM_USER_APP_ID) && (sapapl->ID_type != T_ATM_VENDOR_APP_ID)) return (EINVAL); } else if (sapapl->SVE_tag != T_ATM_ABSENT) return (EINVAL); /* * Select an outgoing network interface */ if (atp->atp_attr.nif == NULL) { struct atm_pif *pip; for (pip = atm_interface_head; pip != NULL; pip = pip->pif_next) { if (pip->pif_nif != NULL) { atp->atp_attr.nif = pip->pif_nif; break; } } if (atp->atp_attr.nif == NULL) return (ENXIO); } /* * Set supplied connection attributes */ atp->atp_attr.called.tag = T_ATM_PRESENT; KM_COPY(&sapadr->address_format, &atp->atp_attr.called.addr, sizeof(Atm_addr)); atp->atp_attr.blli.tag_l2 = sapl2->SVE_tag; if (sapl2->SVE_tag == T_ATM_PRESENT) { atp->atp_attr.blli.v.layer_2_protocol.ID_type = sapl2->ID_type; KM_COPY(&sapl2->ID, &atp->atp_attr.blli.v.layer_2_protocol.ID, sizeof(atp->atp_attr.blli.v.layer_2_protocol.ID)); } atp->atp_attr.blli.tag_l3 = sapl3->SVE_tag; if (sapl3->SVE_tag == T_ATM_PRESENT) { atp->atp_attr.blli.v.layer_3_protocol.ID_type = sapl3->ID_type; KM_COPY(&sapl3->ID, &atp->atp_attr.blli.v.layer_3_protocol.ID, sizeof(atp->atp_attr.blli.v.layer_3_protocol.ID)); } atp->atp_attr.bhli.tag = sapapl->SVE_tag; if (sapapl->SVE_tag == T_ATM_PRESENT) { atp->atp_attr.bhli.v.ID_type = sapapl->ID_type; KM_COPY(&sapapl->ID, &atp->atp_attr.bhli.v.ID, sizeof(atp->atp_attr.bhli.v.ID)); } /* * We're finally ready to initiate the ATM connection */ soisconnecting(so); atm_sock_stat.as_connreq[atp->atp_type]++; err = atm_cm_connect(epp, atp, &atp->atp_attr, &atp->atp_conn); if (err == 0) { /* * Connection is setup */ atm_sock_stat.as_conncomp[atp->atp_type]++; soisconnected(so); } else if (err == EINPROGRESS) { /* * We've got to wait for a connected event */ err = 0; } else { /* * Call failed... */ atm_sock_stat.as_connfail[atp->atp_type]++; soisdisconnected(so); } return (err); } /* * Disconnect connected socket * * Called at splnet. * * Arguments: * so pointer to socket * * Returns: * 0 request processed * errno error processing request - reason indicated * */ int atm_sock_disconnect(so) struct socket *so; { Atm_pcb *atp = sotoatmpcb(so); struct t_atm_cause *cause; int err; /* * Make sure we're still attached */ if (atp == NULL) return (ENOTCONN); /* * Release the ATM connection */ if (atp->atp_conn) { if (atp->atp_attr.cause.tag == T_ATM_PRESENT) cause = &atp->atp_attr.cause.v; else cause = &atm_sock_cause; err = atm_cm_release(atp->atp_conn, cause); if (err) log(LOG_ERR, "atm_sock_disconnect: release fail (%d)\n", err); atm_sock_stat.as_connrel[atp->atp_type]++; atp->atp_conn = NULL; } soisdisconnected(so); return (0); } /* * Retrieve local socket address * * Called at splnet. * * Arguments: * so pointer to socket * addr pointer to pointer to contain protocol address * * Returns: * 0 request processed * errno error processing request - reason indicated * */ int atm_sock_sockaddr(so, addr) struct socket *so; struct sockaddr **addr; { struct sockaddr_atm *satm; struct t_atm_sap_addr *saddr; Atm_pcb *atp = sotoatmpcb(so); /* * Return local interface address, if known */ satm = KM_ALLOC(sizeof *satm, M_SONAME, M_WAITOK); if (satm == NULL) return (ENOMEM); KM_ZERO(satm, sizeof(*satm)); satm->satm_family = AF_ATM; satm->satm_len = sizeof(*satm); saddr = &satm->satm_addr.t_atm_sap_addr; if (atp->atp_attr.nif && atp->atp_attr.nif->nif_pif->pif_siginst) { saddr->SVE_tag_addr = T_ATM_PRESENT; ATM_ADDR_SEL_COPY( &atp->atp_attr.nif->nif_pif->pif_siginst->si_addr, atp->atp_attr.nif->nif_sel, saddr); if (saddr->address_format == T_ATM_ENDSYS_ADDR) saddr->SVE_tag_selector = T_ATM_PRESENT; else saddr->SVE_tag_selector = T_ATM_ABSENT; } else { saddr->SVE_tag_addr = T_ATM_ABSENT; saddr->SVE_tag_selector = T_ATM_ABSENT; saddr->address_format = T_ATM_ABSENT; } satm->satm_addr.t_atm_sap_layer2.SVE_tag = T_ATM_ABSENT; satm->satm_addr.t_atm_sap_layer3.SVE_tag = T_ATM_ABSENT; satm->satm_addr.t_atm_sap_appl.SVE_tag = T_ATM_ABSENT; *addr = (struct sockaddr *)satm; return (0); } /* * Retrieve peer socket address * * Called at splnet. * * Arguments: * so pointer to socket * addr pointer to pointer to contain protocol address * * Returns: * 0 request processed * errno error processing request - reason indicated * */ int atm_sock_peeraddr(so, addr) struct socket *so; struct sockaddr **addr; { struct sockaddr_atm *satm; struct t_atm_sap_addr *saddr; Atm_pcb *atp = sotoatmpcb(so); Atm_connvc *cvp; /* * Return remote address, if known */ satm = KM_ALLOC(sizeof *satm, M_SONAME, M_WAITOK); if (satm == NULL) return (ENOMEM); KM_ZERO(satm, sizeof(*satm)); satm->satm_family = AF_ATM; satm->satm_len = sizeof(*satm); saddr = &satm->satm_addr.t_atm_sap_addr; if (so->so_state & SS_ISCONNECTED) { cvp = atp->atp_conn->co_connvc; saddr->SVE_tag_addr = T_ATM_PRESENT; if (cvp->cvc_flags & CVCF_CALLER) { ATM_ADDR_COPY(&cvp->cvc_attr.called.addr, saddr); } else { if (cvp->cvc_attr.calling.tag == T_ATM_PRESENT) { ATM_ADDR_COPY(&cvp->cvc_attr.calling.addr, saddr); } else { saddr->SVE_tag_addr = T_ATM_ABSENT; saddr->address_format = T_ATM_ABSENT; } } if (saddr->address_format == T_ATM_ENDSYS_ADDR) saddr->SVE_tag_selector = T_ATM_PRESENT; else saddr->SVE_tag_selector = T_ATM_ABSENT; } else { saddr->SVE_tag_addr = T_ATM_ABSENT; saddr->SVE_tag_selector = T_ATM_ABSENT; saddr->address_format = T_ATM_ABSENT; } satm->satm_addr.t_atm_sap_layer2.SVE_tag = T_ATM_ABSENT; satm->satm_addr.t_atm_sap_layer3.SVE_tag = T_ATM_ABSENT; satm->satm_addr.t_atm_sap_appl.SVE_tag = T_ATM_ABSENT; *addr = (struct sockaddr *)satm; return (0); } /* * Common setsockopt processing * * Called at splnet. * * Arguments: * so pointer to socket * sopt pointer to socket option info * atp pointer to ATM PCB * * Returns: * 0 request processed * errno error processing request - reason indicated * */ int atm_sock_setopt(so, sopt, atp) struct socket *so; struct sockopt *sopt; Atm_pcb *atp; { int err = 0; union { struct t_atm_aal5 aal5; struct t_atm_traffic trf; struct t_atm_bearer brr; struct t_atm_bhli bhl; struct t_atm_blli bll; Atm_addr addr; struct t_atm_cause cau; struct t_atm_qos qos; struct t_atm_transit trn; struct t_atm_net_intf nif; struct t_atm_llc llc; struct t_atm_app_name appn; } p; #define MAXVAL(bits) ((1 << bits) - 1) #define MAXMASK(bits) (~MAXVAL(bits)) switch (sopt->sopt_name) { case T_ATM_AAL5: err = sooptcopyin(sopt, &p.aal5, sizeof p.aal5, sizeof p.aal5); if (err) break; if ((p.aal5.forward_max_SDU_size != T_ATM_ABSENT) && (p.aal5.forward_max_SDU_size & MAXMASK(16))) return (EINVAL); if ((p.aal5.backward_max_SDU_size != T_ATM_ABSENT) && (p.aal5.backward_max_SDU_size & MAXMASK(16))) return (EINVAL); if ((p.aal5.SSCS_type != T_ATM_ABSENT) && (p.aal5.SSCS_type != T_ATM_NULL) && (p.aal5.SSCS_type != T_ATM_SSCS_SSCOP_REL) && (p.aal5.SSCS_type != T_ATM_SSCS_SSCOP_UNREL) && (p.aal5.SSCS_type != T_ATM_SSCS_FR)) return (EINVAL); if ((p.aal5.forward_max_SDU_size == T_ATM_ABSENT) && (p.aal5.backward_max_SDU_size == T_ATM_ABSENT) && (p.aal5.SSCS_type == T_ATM_ABSENT)) atp->atp_attr.aal.tag = T_ATM_ABSENT; else { atp->atp_attr.aal.tag = T_ATM_PRESENT; atp->atp_attr.aal.type = ATM_AAL5; atp->atp_attr.aal.v.aal5 = p.aal5; } break; case T_ATM_TRAFFIC: err = sooptcopyin(sopt, &p.trf, sizeof p.trf, sizeof p.trf); if (err) break; if ((p.trf.forward.PCR_high_priority != T_ATM_ABSENT) && (p.trf.forward.PCR_high_priority & MAXMASK(24))) return (EINVAL); if (p.trf.forward.PCR_all_traffic & MAXMASK(24)) return (EINVAL); if ((p.trf.forward.SCR_high_priority != T_ATM_ABSENT) && (p.trf.forward.SCR_high_priority & MAXMASK(24))) return (EINVAL); if ((p.trf.forward.SCR_all_traffic != T_ATM_ABSENT) && (p.trf.forward.SCR_all_traffic & MAXMASK(24))) return (EINVAL); if ((p.trf.forward.MBS_high_priority != T_ATM_ABSENT) && (p.trf.forward.MBS_high_priority & MAXMASK(24))) return (EINVAL); if ((p.trf.forward.MBS_all_traffic != T_ATM_ABSENT) && (p.trf.forward.MBS_all_traffic & MAXMASK(24))) return (EINVAL); if ((p.trf.forward.tagging != T_YES) && (p.trf.forward.tagging != T_NO)) return (EINVAL); if ((p.trf.backward.PCR_high_priority != T_ATM_ABSENT) && (p.trf.backward.PCR_high_priority & MAXMASK(24))) return (EINVAL); if (p.trf.backward.PCR_all_traffic & MAXMASK(24)) return (EINVAL); if ((p.trf.backward.SCR_high_priority != T_ATM_ABSENT) && (p.trf.backward.SCR_high_priority & MAXMASK(24))) return (EINVAL); if ((p.trf.backward.SCR_all_traffic != T_ATM_ABSENT) && (p.trf.backward.SCR_all_traffic & MAXMASK(24))) return (EINVAL); if ((p.trf.backward.MBS_high_priority != T_ATM_ABSENT) && (p.trf.backward.MBS_high_priority & MAXMASK(24))) return (EINVAL); if ((p.trf.backward.MBS_all_traffic != T_ATM_ABSENT) && (p.trf.backward.MBS_all_traffic & MAXMASK(24))) return (EINVAL); if ((p.trf.backward.tagging != T_YES) && (p.trf.backward.tagging != T_NO)) return (EINVAL); if ((p.trf.best_effort != T_YES) && (p.trf.best_effort != T_NO)) return (EINVAL); atp->atp_attr.traffic.tag = T_ATM_PRESENT; atp->atp_attr.traffic.v = p.trf; break; case T_ATM_BEARER_CAP: err = sooptcopyin(sopt, &p.brr, sizeof p.brr, sizeof p.brr); if (err) break; if ((p.brr.bearer_class != T_ATM_CLASS_A) && (p.brr.bearer_class != T_ATM_CLASS_C) && (p.brr.bearer_class != T_ATM_CLASS_X)) return (EINVAL); if ((p.brr.traffic_type != T_ATM_NULL) && (p.brr.traffic_type != T_ATM_CBR) && (p.brr.traffic_type != T_ATM_VBR)) return (EINVAL); if ((p.brr.timing_requirements != T_ATM_NULL) && (p.brr.timing_requirements != T_ATM_END_TO_END) && (p.brr.timing_requirements != T_ATM_NO_END_TO_END)) return (EINVAL); if ((p.brr.clipping_susceptibility != T_NO) && (p.brr.clipping_susceptibility != T_YES)) return (EINVAL); if ((p.brr.connection_configuration != T_ATM_1_TO_1) && (p.brr.connection_configuration != T_ATM_1_TO_MANY)) return (EINVAL); atp->atp_attr.bearer.tag = T_ATM_PRESENT; atp->atp_attr.bearer.v = p.brr; break; case T_ATM_BHLI: err = sooptcopyin(sopt, &p.bhl, sizeof p.bhl, sizeof p.bhl); if (err) break; if ((p.bhl.ID_type != T_ATM_ABSENT) && (p.bhl.ID_type != T_ATM_ISO_APP_ID) && (p.bhl.ID_type != T_ATM_USER_APP_ID) && (p.bhl.ID_type != T_ATM_VENDOR_APP_ID)) return (EINVAL); if (p.bhl.ID_type == T_ATM_ABSENT) atp->atp_attr.bhli.tag = T_ATM_ABSENT; else { atp->atp_attr.bhli.tag = T_ATM_PRESENT; atp->atp_attr.bhli.v = p.bhl; } break; case T_ATM_BLLI: err = sooptcopyin(sopt, &p.bll, sizeof p.bll, sizeof p.bll); if (err) break; if ((p.bll.layer_2_protocol.ID_type != T_ATM_ABSENT) && (p.bll.layer_2_protocol.ID_type != T_ATM_SIMPLE_ID) && (p.bll.layer_2_protocol.ID_type != T_ATM_USER_ID)) return (EINVAL); if ((p.bll.layer_2_protocol.mode != T_ATM_ABSENT) && (p.bll.layer_2_protocol.mode != T_ATM_BLLI_NORMAL_MODE) && (p.bll.layer_2_protocol.mode != T_ATM_BLLI_EXTENDED_MODE)) return (EINVAL); if ((p.bll.layer_2_protocol.window_size != T_ATM_ABSENT) && (p.bll.layer_2_protocol.window_size < 1)) return (EINVAL); if ((p.bll.layer_3_protocol.ID_type != T_ATM_ABSENT) && (p.bll.layer_3_protocol.ID_type != T_ATM_SIMPLE_ID) && (p.bll.layer_3_protocol.ID_type != T_ATM_IPI_ID) && (p.bll.layer_3_protocol.ID_type != T_ATM_SNAP_ID) && (p.bll.layer_3_protocol.ID_type != T_ATM_USER_ID)) return (EINVAL); if ((p.bll.layer_3_protocol.mode != T_ATM_ABSENT) && (p.bll.layer_3_protocol.mode != T_ATM_BLLI_NORMAL_MODE) && (p.bll.layer_3_protocol.mode != T_ATM_BLLI_EXTENDED_MODE)) return (EINVAL); if ((p.bll.layer_3_protocol.packet_size != T_ATM_ABSENT) && (p.bll.layer_3_protocol.packet_size & MAXMASK(4))) return (EINVAL); if ((p.bll.layer_3_protocol.window_size != T_ATM_ABSENT) && (p.bll.layer_3_protocol.window_size < 1)) return (EINVAL); if (p.bll.layer_2_protocol.ID_type == T_ATM_ABSENT) atp->atp_attr.blli.tag_l2 = T_ATM_ABSENT; else atp->atp_attr.blli.tag_l2 = T_ATM_PRESENT; if (p.bll.layer_3_protocol.ID_type == T_ATM_ABSENT) atp->atp_attr.blli.tag_l3 = T_ATM_ABSENT; else atp->atp_attr.blli.tag_l3 = T_ATM_PRESENT; if ((atp->atp_attr.blli.tag_l2 == T_ATM_PRESENT) || (atp->atp_attr.blli.tag_l3 == T_ATM_PRESENT)) atp->atp_attr.blli.v = p.bll; break; case T_ATM_DEST_ADDR: err = sooptcopyin(sopt, &p.addr, sizeof p.addr, sizeof p.addr); if (err) break; if ((p.addr.address_format != T_ATM_ENDSYS_ADDR) && (p.addr.address_format != T_ATM_E164_ADDR)) return (EINVAL); if (p.addr.address_length > ATM_ADDR_LEN) return (EINVAL); atp->atp_attr.called.tag = T_ATM_PRESENT; atp->atp_attr.called.addr = p.addr; break; case T_ATM_DEST_SUB: err = sooptcopyin(sopt, &p.addr, sizeof p.addr, sizeof p.addr); if (err) break; if ((p.addr.address_format != T_ATM_ABSENT) && (p.addr.address_format != T_ATM_NSAP_ADDR)) return (EINVAL); if (p.addr.address_length > ATM_ADDR_LEN) return (EINVAL); /* T_ATM_DEST_ADDR controls tag */ atp->atp_attr.called.subaddr = p.addr; break; case T_ATM_ORIG_ADDR: return (EACCES); case T_ATM_ORIG_SUB: return (EACCES); case T_ATM_CALLER_ID: return (EACCES); case T_ATM_CAUSE: err = sooptcopyin(sopt, &p.cau, sizeof p.cau, sizeof p.cau); if (err) break; if ((p.cau.coding_standard != T_ATM_ABSENT) && (p.cau.coding_standard != T_ATM_ITU_CODING) && (p.cau.coding_standard != T_ATM_NETWORK_CODING)) return (EINVAL); if ((p.cau.location != T_ATM_LOC_USER) && (p.cau.location != T_ATM_LOC_LOCAL_PRIVATE_NET) && (p.cau.location != T_ATM_LOC_LOCAL_PUBLIC_NET) && (p.cau.location != T_ATM_LOC_TRANSIT_NET) && (p.cau.location != T_ATM_LOC_REMOTE_PUBLIC_NET) && (p.cau.location != T_ATM_LOC_REMOTE_PRIVATE_NET) && (p.cau.location != T_ATM_LOC_INTERNATIONAL_NET) && (p.cau.location != T_ATM_LOC_BEYOND_INTERWORKING)) return (EINVAL); if (p.cau.coding_standard == T_ATM_ABSENT) atp->atp_attr.cause.tag = T_ATM_ABSENT; else { atp->atp_attr.cause.tag = T_ATM_PRESENT; atp->atp_attr.cause.v = p.cau; } break; case T_ATM_QOS: err = sooptcopyin(sopt, &p.qos, sizeof p.qos, sizeof p.qos); if (err) break; if ((p.qos.coding_standard != T_ATM_ABSENT) && (p.qos.coding_standard != T_ATM_ITU_CODING) && (p.qos.coding_standard != T_ATM_NETWORK_CODING)) return (EINVAL); if ((p.qos.forward.qos_class != T_ATM_QOS_CLASS_0) && (p.qos.forward.qos_class != T_ATM_QOS_CLASS_1) && (p.qos.forward.qos_class != T_ATM_QOS_CLASS_2) && (p.qos.forward.qos_class != T_ATM_QOS_CLASS_3) && (p.qos.forward.qos_class != T_ATM_QOS_CLASS_4)) return (EINVAL); if ((p.qos.backward.qos_class != T_ATM_QOS_CLASS_0) && (p.qos.backward.qos_class != T_ATM_QOS_CLASS_1) && (p.qos.backward.qos_class != T_ATM_QOS_CLASS_2) && (p.qos.backward.qos_class != T_ATM_QOS_CLASS_3) && (p.qos.backward.qos_class != T_ATM_QOS_CLASS_4)) return (EINVAL); if (p.qos.coding_standard == T_ATM_ABSENT) atp->atp_attr.qos.tag = T_ATM_ABSENT; else { atp->atp_attr.qos.tag = T_ATM_PRESENT; atp->atp_attr.qos.v = p.qos; } break; case T_ATM_TRANSIT: err = sooptcopyin(sopt, &p.trn, sizeof p.trn, sizeof p.trn); if (err) break; if (p.trn.length > T_ATM_MAX_NET_ID) return (EINVAL); if (p.trn.length == 0) atp->atp_attr.transit.tag = T_ATM_ABSENT; else { atp->atp_attr.transit.tag = T_ATM_PRESENT; atp->atp_attr.transit.v = p.trn; } break; case T_ATM_ADD_LEAF: return (EPROTONOSUPPORT); /* XXX */ case T_ATM_DROP_LEAF: return (EPROTONOSUPPORT); /* XXX */ case T_ATM_NET_INTF: err = sooptcopyin(sopt, &p.nif, sizeof p.nif, sizeof p.nif); if (err) break; atp->atp_attr.nif = atm_nifname(p.nif.net_intf); if (atp->atp_attr.nif == NULL) return (ENXIO); break; case T_ATM_LLC: err = sooptcopyin(sopt, &p.llc, sizeof p.llc, sizeof p.llc); if (err) break; if ((p.llc.llc_len < T_ATM_LLC_MIN_LEN) || (p.llc.llc_len > T_ATM_LLC_MAX_LEN)) return (EINVAL); atp->atp_attr.llc.tag = T_ATM_PRESENT; atp->atp_attr.llc.v = p.llc; break; case T_ATM_APP_NAME: err = sooptcopyin(sopt, &p.appn, sizeof p.appn, sizeof p.appn); if (err) break; strncpy(atp->atp_name, p.appn.app_name, T_ATM_APP_NAME_LEN); break; default: return (ENOPROTOOPT); } return (err); } /* * Common getsockopt processing * * Called at splnet. * * Arguments: * so pointer to socket * sopt pointer to socket option info * atp pointer to ATM PCB * * Returns: * 0 request processed * errno error processing request - reason indicated * */ int atm_sock_getopt(so, sopt, atp) struct socket *so; struct sockopt *sopt; Atm_pcb *atp; { Atm_attributes *ap; /* * If socket is connected, return attributes for the VCC in use, * otherwise just return what the user has setup so far. */ if (so->so_state & SS_ISCONNECTED) ap = &atp->atp_conn->co_connvc->cvc_attr; else ap = &atp->atp_attr; switch (sopt->sopt_name) { case T_ATM_AAL5: if ((ap->aal.tag == T_ATM_PRESENT) && (ap->aal.type == ATM_AAL5)) { return (sooptcopyout(sopt, &ap->aal.v.aal5, sizeof ap->aal.v.aal5)); } else { return (ENOENT); } break; case T_ATM_TRAFFIC: if (ap->traffic.tag == T_ATM_PRESENT) { return (sooptcopyout(sopt, &ap->traffic.v, sizeof ap->traffic.v)); } else { return (ENOENT); } break; case T_ATM_BEARER_CAP: if (ap->bearer.tag == T_ATM_PRESENT) { return (sooptcopyout(sopt, &ap->bearer.v, sizeof ap->bearer.v)); } else { return (ENOENT); } break; case T_ATM_BHLI: if (ap->bhli.tag == T_ATM_PRESENT) { return (sooptcopyout(sopt, &ap->bhli.v, sizeof ap->bhli.v)); } else { return (ENOENT); } break; case T_ATM_BLLI: if ((ap->blli.tag_l2 == T_ATM_PRESENT) || (ap->blli.tag_l3 == T_ATM_PRESENT)) { return (sooptcopyout(sopt, &ap->blli.v, sizeof ap->blli.v)); } else { return (ENOENT); } break; case T_ATM_DEST_ADDR: if (ap->called.tag == T_ATM_PRESENT) { return (sooptcopyout(sopt, &ap->called.addr, sizeof ap->called.addr)); } else { return (ENOENT); } break; case T_ATM_DEST_SUB: if (ap->called.tag == T_ATM_PRESENT) { return (sooptcopyout(sopt, &ap->called.subaddr, sizeof ap->called.subaddr)); } else { return (ENOENT); } break; case T_ATM_ORIG_ADDR: if (ap->calling.tag == T_ATM_PRESENT) { return (sooptcopyout(sopt, &ap->calling.addr, sizeof ap->calling.addr)); } else { return (ENOENT); } break; case T_ATM_ORIG_SUB: if (ap->calling.tag == T_ATM_PRESENT) { return (sooptcopyout(sopt, &ap->calling.subaddr, sizeof ap->calling.subaddr)); } else { return (ENOENT); } break; case T_ATM_CALLER_ID: if (ap->calling.tag == T_ATM_PRESENT) { return (sooptcopyout(sopt, &ap->calling.cid, sizeof ap->calling.cid)); } else { return (ENOENT); } break; case T_ATM_CAUSE: if (ap->cause.tag == T_ATM_PRESENT) { return (sooptcopyout(sopt, &ap->cause.v, sizeof ap->cause.v)); } else { return (ENOENT); } break; case T_ATM_QOS: if (ap->qos.tag == T_ATM_PRESENT) { return (sooptcopyout(sopt, &ap->qos.v, sizeof ap->qos.v)); } else { return (ENOENT); } break; case T_ATM_TRANSIT: if (ap->transit.tag == T_ATM_PRESENT) { return (sooptcopyout(sopt, &ap->transit.v, sizeof ap->transit.v)); } else { return (ENOENT); } break; case T_ATM_LEAF_IND: return (EPROTONOSUPPORT); /* XXX */ case T_ATM_NET_INTF: if (ap->nif) { struct t_atm_net_intf netif; struct ifnet *ifp; ifp = &ap->nif->nif_if; (void) snprintf(netif.net_intf, sizeof(netif.net_intf), "%s", ifp->if_xname); return (sooptcopyout(sopt, &netif, sizeof netif)); } else { return (ENOENT); } break; case T_ATM_LLC: if (ap->llc.tag == T_ATM_PRESENT) { return (sooptcopyout(sopt, &ap->llc.v, sizeof ap->llc.v)); } else { return (ENOENT); } break; default: return (ENOPROTOOPT); } return (0); } /* * Process Socket VCC Connected Notification * * Arguments: * toku owner's connection token (atm_pcb protocol block) * * Returns: * none * */ void atm_sock_connected(toku) void *toku; { Atm_pcb *atp = (Atm_pcb *)toku; /* * Connection is setup */ atm_sock_stat.as_conncomp[atp->atp_type]++; soisconnected(atp->atp_socket); } /* * Process Socket VCC Cleared Notification * * Arguments: * toku owner's connection token (atm_pcb protocol block) * cause pointer to cause code * * Returns: * none * */ void atm_sock_cleared(toku, cause) void *toku; struct t_atm_cause *cause; { Atm_pcb *atp = (Atm_pcb *)toku; struct socket *so; so = atp->atp_socket; /* * Save call clearing cause */ atp->atp_attr.cause.tag = T_ATM_PRESENT; atp->atp_attr.cause.v = *cause; /* * Set user error code */ if (so->so_state & SS_ISCONNECTED) { so->so_error = ECONNRESET; atm_sock_stat.as_connclr[atp->atp_type]++; } else { so->so_error = ECONNREFUSED; atm_sock_stat.as_connfail[atp->atp_type]++; } /* * Connection is gone */ atp->atp_conn = NULL; soisdisconnected(so); /* * Cleanup failed incoming connection setup */ if (so->so_state & SS_NOFDREF) { (void) atm_sock_detach(so); } }