18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* net/atm/signaling.c - ATM signaling */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/errno.h> /* error codes */ 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> /* printk */ 108c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 118c2ecf20Sopenharmony_ci#include <linux/wait.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> /* jiffies and HZ */ 138c2ecf20Sopenharmony_ci#include <linux/atm.h> /* ATM stuff */ 148c2ecf20Sopenharmony_ci#include <linux/atmsap.h> 158c2ecf20Sopenharmony_ci#include <linux/atmsvc.h> 168c2ecf20Sopenharmony_ci#include <linux/atmdev.h> 178c2ecf20Sopenharmony_ci#include <linux/bitops.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "resources.h" 218c2ecf20Sopenharmony_ci#include "signaling.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct atm_vcc *sigd = NULL; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic void sigd_put_skb(struct sk_buff *skb) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci if (!sigd) { 288c2ecf20Sopenharmony_ci pr_debug("atmsvc: no signaling daemon\n"); 298c2ecf20Sopenharmony_ci kfree_skb(skb); 308c2ecf20Sopenharmony_ci return; 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci atm_force_charge(sigd, skb->truesize); 338c2ecf20Sopenharmony_ci skb_queue_tail(&sk_atm(sigd)->sk_receive_queue, skb); 348c2ecf20Sopenharmony_ci sk_atm(sigd)->sk_data_ready(sk_atm(sigd)); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void modify_qos(struct atm_vcc *vcc, struct atmsvc_msg *msg) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct sk_buff *skb; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (test_bit(ATM_VF_RELEASED, &vcc->flags) || 428c2ecf20Sopenharmony_ci !test_bit(ATM_VF_READY, &vcc->flags)) 438c2ecf20Sopenharmony_ci return; 448c2ecf20Sopenharmony_ci msg->type = as_error; 458c2ecf20Sopenharmony_ci if (!vcc->dev->ops->change_qos) 468c2ecf20Sopenharmony_ci msg->reply = -EOPNOTSUPP; 478c2ecf20Sopenharmony_ci else { 488c2ecf20Sopenharmony_ci /* should lock VCC */ 498c2ecf20Sopenharmony_ci msg->reply = vcc->dev->ops->change_qos(vcc, &msg->qos, 508c2ecf20Sopenharmony_ci msg->reply); 518c2ecf20Sopenharmony_ci if (!msg->reply) 528c2ecf20Sopenharmony_ci msg->type = as_okay; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci /* 558c2ecf20Sopenharmony_ci * Should probably just turn around the old skb. But then, the buffer 568c2ecf20Sopenharmony_ci * space accounting needs to follow the change too. Maybe later. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci while (!(skb = alloc_skb(sizeof(struct atmsvc_msg), GFP_KERNEL))) 598c2ecf20Sopenharmony_ci schedule(); 608c2ecf20Sopenharmony_ci *(struct atmsvc_msg *)skb_put(skb, sizeof(struct atmsvc_msg)) = *msg; 618c2ecf20Sopenharmony_ci sigd_put_skb(skb); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct atmsvc_msg *msg; 678c2ecf20Sopenharmony_ci struct atm_vcc *session_vcc; 688c2ecf20Sopenharmony_ci struct sock *sk; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci msg = (struct atmsvc_msg *) skb->data; 718c2ecf20Sopenharmony_ci WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc)); 728c2ecf20Sopenharmony_ci vcc = *(struct atm_vcc **) &msg->vcc; 738c2ecf20Sopenharmony_ci pr_debug("%d (0x%lx)\n", (int)msg->type, (unsigned long)vcc); 748c2ecf20Sopenharmony_ci sk = sk_atm(vcc); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci switch (msg->type) { 778c2ecf20Sopenharmony_ci case as_okay: 788c2ecf20Sopenharmony_ci sk->sk_err = -msg->reply; 798c2ecf20Sopenharmony_ci clear_bit(ATM_VF_WAITING, &vcc->flags); 808c2ecf20Sopenharmony_ci if (!*vcc->local.sas_addr.prv && !*vcc->local.sas_addr.pub) { 818c2ecf20Sopenharmony_ci vcc->local.sas_family = AF_ATMSVC; 828c2ecf20Sopenharmony_ci memcpy(vcc->local.sas_addr.prv, 838c2ecf20Sopenharmony_ci msg->local.sas_addr.prv, ATM_ESA_LEN); 848c2ecf20Sopenharmony_ci memcpy(vcc->local.sas_addr.pub, 858c2ecf20Sopenharmony_ci msg->local.sas_addr.pub, ATM_E164_LEN + 1); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci session_vcc = vcc->session ? vcc->session : vcc; 888c2ecf20Sopenharmony_ci if (session_vcc->vpi || session_vcc->vci) 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci session_vcc->itf = msg->pvc.sap_addr.itf; 918c2ecf20Sopenharmony_ci session_vcc->vpi = msg->pvc.sap_addr.vpi; 928c2ecf20Sopenharmony_ci session_vcc->vci = msg->pvc.sap_addr.vci; 938c2ecf20Sopenharmony_ci if (session_vcc->vpi || session_vcc->vci) 948c2ecf20Sopenharmony_ci session_vcc->qos = msg->qos; 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci case as_error: 978c2ecf20Sopenharmony_ci clear_bit(ATM_VF_REGIS, &vcc->flags); 988c2ecf20Sopenharmony_ci clear_bit(ATM_VF_READY, &vcc->flags); 998c2ecf20Sopenharmony_ci sk->sk_err = -msg->reply; 1008c2ecf20Sopenharmony_ci clear_bit(ATM_VF_WAITING, &vcc->flags); 1018c2ecf20Sopenharmony_ci break; 1028c2ecf20Sopenharmony_ci case as_indicate: 1038c2ecf20Sopenharmony_ci vcc = *(struct atm_vcc **)&msg->listen_vcc; 1048c2ecf20Sopenharmony_ci sk = sk_atm(vcc); 1058c2ecf20Sopenharmony_ci pr_debug("as_indicate!!!\n"); 1068c2ecf20Sopenharmony_ci lock_sock(sk); 1078c2ecf20Sopenharmony_ci if (sk_acceptq_is_full(sk)) { 1088c2ecf20Sopenharmony_ci sigd_enq(NULL, as_reject, vcc, NULL, NULL); 1098c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1108c2ecf20Sopenharmony_ci goto as_indicate_complete; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci sk_acceptq_added(sk); 1138c2ecf20Sopenharmony_ci skb_queue_tail(&sk->sk_receive_queue, skb); 1148c2ecf20Sopenharmony_ci pr_debug("waking sk_sleep(sk) 0x%p\n", sk_sleep(sk)); 1158c2ecf20Sopenharmony_ci sk->sk_state_change(sk); 1168c2ecf20Sopenharmony_cias_indicate_complete: 1178c2ecf20Sopenharmony_ci release_sock(sk); 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci case as_close: 1208c2ecf20Sopenharmony_ci set_bit(ATM_VF_RELEASED, &vcc->flags); 1218c2ecf20Sopenharmony_ci vcc_release_async(vcc, msg->reply); 1228c2ecf20Sopenharmony_ci goto out; 1238c2ecf20Sopenharmony_ci case as_modify: 1248c2ecf20Sopenharmony_ci modify_qos(vcc, msg); 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci case as_addparty: 1278c2ecf20Sopenharmony_ci case as_dropparty: 1288c2ecf20Sopenharmony_ci sk->sk_err_soft = -msg->reply; 1298c2ecf20Sopenharmony_ci /* < 0 failure, otherwise ep_ref */ 1308c2ecf20Sopenharmony_ci clear_bit(ATM_VF_WAITING, &vcc->flags); 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci default: 1338c2ecf20Sopenharmony_ci pr_alert("bad message type %d\n", (int)msg->type); 1348c2ecf20Sopenharmony_ci return -EINVAL; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci sk->sk_state_change(sk); 1378c2ecf20Sopenharmony_ciout: 1388c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_civoid sigd_enq2(struct atm_vcc *vcc, enum atmsvc_msg_type type, 1438c2ecf20Sopenharmony_ci struct atm_vcc *listen_vcc, const struct sockaddr_atmpvc *pvc, 1448c2ecf20Sopenharmony_ci const struct sockaddr_atmsvc *svc, const struct atm_qos *qos, 1458c2ecf20Sopenharmony_ci int reply) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct sk_buff *skb; 1488c2ecf20Sopenharmony_ci struct atmsvc_msg *msg; 1498c2ecf20Sopenharmony_ci static unsigned int session = 0; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci pr_debug("%d (0x%p)\n", (int)type, vcc); 1528c2ecf20Sopenharmony_ci while (!(skb = alloc_skb(sizeof(struct atmsvc_msg), GFP_KERNEL))) 1538c2ecf20Sopenharmony_ci schedule(); 1548c2ecf20Sopenharmony_ci msg = skb_put_zero(skb, sizeof(struct atmsvc_msg)); 1558c2ecf20Sopenharmony_ci msg->type = type; 1568c2ecf20Sopenharmony_ci *(struct atm_vcc **) &msg->vcc = vcc; 1578c2ecf20Sopenharmony_ci *(struct atm_vcc **) &msg->listen_vcc = listen_vcc; 1588c2ecf20Sopenharmony_ci msg->reply = reply; 1598c2ecf20Sopenharmony_ci if (qos) 1608c2ecf20Sopenharmony_ci msg->qos = *qos; 1618c2ecf20Sopenharmony_ci if (vcc) 1628c2ecf20Sopenharmony_ci msg->sap = vcc->sap; 1638c2ecf20Sopenharmony_ci if (svc) 1648c2ecf20Sopenharmony_ci msg->svc = *svc; 1658c2ecf20Sopenharmony_ci if (vcc) 1668c2ecf20Sopenharmony_ci msg->local = vcc->local; 1678c2ecf20Sopenharmony_ci if (pvc) 1688c2ecf20Sopenharmony_ci msg->pvc = *pvc; 1698c2ecf20Sopenharmony_ci if (vcc) { 1708c2ecf20Sopenharmony_ci if (type == as_connect && test_bit(ATM_VF_SESSION, &vcc->flags)) 1718c2ecf20Sopenharmony_ci msg->session = ++session; 1728c2ecf20Sopenharmony_ci /* every new pmp connect gets the next session number */ 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci sigd_put_skb(skb); 1758c2ecf20Sopenharmony_ci if (vcc) 1768c2ecf20Sopenharmony_ci set_bit(ATM_VF_REGIS, &vcc->flags); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_civoid sigd_enq(struct atm_vcc *vcc, enum atmsvc_msg_type type, 1808c2ecf20Sopenharmony_ci struct atm_vcc *listen_vcc, const struct sockaddr_atmpvc *pvc, 1818c2ecf20Sopenharmony_ci const struct sockaddr_atmsvc *svc) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci sigd_enq2(vcc, type, listen_vcc, pvc, svc, vcc ? &vcc->qos : NULL, 0); 1848c2ecf20Sopenharmony_ci /* other ISP applications may use "reply" */ 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void purge_vcc(struct atm_vcc *vcc) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci if (sk_atm(vcc)->sk_family == PF_ATMSVC && 1908c2ecf20Sopenharmony_ci !test_bit(ATM_VF_META, &vcc->flags)) { 1918c2ecf20Sopenharmony_ci set_bit(ATM_VF_RELEASED, &vcc->flags); 1928c2ecf20Sopenharmony_ci clear_bit(ATM_VF_REGIS, &vcc->flags); 1938c2ecf20Sopenharmony_ci vcc_release_async(vcc, -EUNATCH); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void sigd_close(struct atm_vcc *vcc) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct sock *s; 2008c2ecf20Sopenharmony_ci int i; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci pr_debug("\n"); 2038c2ecf20Sopenharmony_ci sigd = NULL; 2048c2ecf20Sopenharmony_ci if (skb_peek(&sk_atm(vcc)->sk_receive_queue)) 2058c2ecf20Sopenharmony_ci pr_err("closing with requests pending\n"); 2068c2ecf20Sopenharmony_ci skb_queue_purge(&sk_atm(vcc)->sk_receive_queue); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci read_lock(&vcc_sklist_lock); 2098c2ecf20Sopenharmony_ci for (i = 0; i < VCC_HTABLE_SIZE; ++i) { 2108c2ecf20Sopenharmony_ci struct hlist_head *head = &vcc_hash[i]; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci sk_for_each(s, head) { 2138c2ecf20Sopenharmony_ci vcc = atm_sk(s); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci purge_vcc(vcc); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic const struct atmdev_ops sigd_dev_ops = { 2228c2ecf20Sopenharmony_ci .close = sigd_close, 2238c2ecf20Sopenharmony_ci .send = sigd_send 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic struct atm_dev sigd_dev = { 2278c2ecf20Sopenharmony_ci .ops = &sigd_dev_ops, 2288c2ecf20Sopenharmony_ci .type = "sig", 2298c2ecf20Sopenharmony_ci .number = 999, 2308c2ecf20Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(sigd_dev.lock) 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ciint sigd_attach(struct atm_vcc *vcc) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci if (sigd) 2368c2ecf20Sopenharmony_ci return -EADDRINUSE; 2378c2ecf20Sopenharmony_ci pr_debug("\n"); 2388c2ecf20Sopenharmony_ci sigd = vcc; 2398c2ecf20Sopenharmony_ci vcc->dev = &sigd_dev; 2408c2ecf20Sopenharmony_ci vcc_insert_socket(sk_atm(vcc)); 2418c2ecf20Sopenharmony_ci set_bit(ATM_VF_META, &vcc->flags); 2428c2ecf20Sopenharmony_ci set_bit(ATM_VF_READY, &vcc->flags); 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci} 245