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