162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* net/atm/signaling.c - ATM signaling */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/errno.h>	/* error codes */
962306a36Sopenharmony_ci#include <linux/kernel.h>	/* printk */
1062306a36Sopenharmony_ci#include <linux/skbuff.h>
1162306a36Sopenharmony_ci#include <linux/wait.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>	/* jiffies and HZ */
1362306a36Sopenharmony_ci#include <linux/atm.h>		/* ATM stuff */
1462306a36Sopenharmony_ci#include <linux/atmsap.h>
1562306a36Sopenharmony_ci#include <linux/atmsvc.h>
1662306a36Sopenharmony_ci#include <linux/atmdev.h>
1762306a36Sopenharmony_ci#include <linux/bitops.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "resources.h"
2162306a36Sopenharmony_ci#include "signaling.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct atm_vcc *sigd = NULL;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic void sigd_put_skb(struct sk_buff *skb)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	if (!sigd) {
2862306a36Sopenharmony_ci		pr_debug("atmsvc: no signaling daemon\n");
2962306a36Sopenharmony_ci		kfree_skb(skb);
3062306a36Sopenharmony_ci		return;
3162306a36Sopenharmony_ci	}
3262306a36Sopenharmony_ci	atm_force_charge(sigd, skb->truesize);
3362306a36Sopenharmony_ci	skb_queue_tail(&sk_atm(sigd)->sk_receive_queue, skb);
3462306a36Sopenharmony_ci	sk_atm(sigd)->sk_data_ready(sk_atm(sigd));
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic void modify_qos(struct atm_vcc *vcc, struct atmsvc_msg *msg)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct sk_buff *skb;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
4262306a36Sopenharmony_ci	    !test_bit(ATM_VF_READY, &vcc->flags))
4362306a36Sopenharmony_ci		return;
4462306a36Sopenharmony_ci	msg->type = as_error;
4562306a36Sopenharmony_ci	if (!vcc->dev->ops->change_qos)
4662306a36Sopenharmony_ci		msg->reply = -EOPNOTSUPP;
4762306a36Sopenharmony_ci	else {
4862306a36Sopenharmony_ci		/* should lock VCC */
4962306a36Sopenharmony_ci		msg->reply = vcc->dev->ops->change_qos(vcc, &msg->qos,
5062306a36Sopenharmony_ci						       msg->reply);
5162306a36Sopenharmony_ci		if (!msg->reply)
5262306a36Sopenharmony_ci			msg->type = as_okay;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci	/*
5562306a36Sopenharmony_ci	 * Should probably just turn around the old skb. But then, the buffer
5662306a36Sopenharmony_ci	 * space accounting needs to follow the change too. Maybe later.
5762306a36Sopenharmony_ci	 */
5862306a36Sopenharmony_ci	while (!(skb = alloc_skb(sizeof(struct atmsvc_msg), GFP_KERNEL)))
5962306a36Sopenharmony_ci		schedule();
6062306a36Sopenharmony_ci	*(struct atmsvc_msg *)skb_put(skb, sizeof(struct atmsvc_msg)) = *msg;
6162306a36Sopenharmony_ci	sigd_put_skb(skb);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct atmsvc_msg *msg;
6762306a36Sopenharmony_ci	struct atm_vcc *session_vcc;
6862306a36Sopenharmony_ci	struct sock *sk;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	msg = (struct atmsvc_msg *) skb->data;
7162306a36Sopenharmony_ci	WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc));
7262306a36Sopenharmony_ci	vcc = *(struct atm_vcc **) &msg->vcc;
7362306a36Sopenharmony_ci	pr_debug("%d (0x%lx)\n", (int)msg->type, (unsigned long)vcc);
7462306a36Sopenharmony_ci	sk = sk_atm(vcc);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	switch (msg->type) {
7762306a36Sopenharmony_ci	case as_okay:
7862306a36Sopenharmony_ci		sk->sk_err = -msg->reply;
7962306a36Sopenharmony_ci		clear_bit(ATM_VF_WAITING, &vcc->flags);
8062306a36Sopenharmony_ci		if (!*vcc->local.sas_addr.prv && !*vcc->local.sas_addr.pub) {
8162306a36Sopenharmony_ci			vcc->local.sas_family = AF_ATMSVC;
8262306a36Sopenharmony_ci			memcpy(vcc->local.sas_addr.prv,
8362306a36Sopenharmony_ci			       msg->local.sas_addr.prv, ATM_ESA_LEN);
8462306a36Sopenharmony_ci			memcpy(vcc->local.sas_addr.pub,
8562306a36Sopenharmony_ci			       msg->local.sas_addr.pub, ATM_E164_LEN + 1);
8662306a36Sopenharmony_ci		}
8762306a36Sopenharmony_ci		session_vcc = vcc->session ? vcc->session : vcc;
8862306a36Sopenharmony_ci		if (session_vcc->vpi || session_vcc->vci)
8962306a36Sopenharmony_ci			break;
9062306a36Sopenharmony_ci		session_vcc->itf = msg->pvc.sap_addr.itf;
9162306a36Sopenharmony_ci		session_vcc->vpi = msg->pvc.sap_addr.vpi;
9262306a36Sopenharmony_ci		session_vcc->vci = msg->pvc.sap_addr.vci;
9362306a36Sopenharmony_ci		if (session_vcc->vpi || session_vcc->vci)
9462306a36Sopenharmony_ci			session_vcc->qos = msg->qos;
9562306a36Sopenharmony_ci		break;
9662306a36Sopenharmony_ci	case as_error:
9762306a36Sopenharmony_ci		clear_bit(ATM_VF_REGIS, &vcc->flags);
9862306a36Sopenharmony_ci		clear_bit(ATM_VF_READY, &vcc->flags);
9962306a36Sopenharmony_ci		sk->sk_err = -msg->reply;
10062306a36Sopenharmony_ci		clear_bit(ATM_VF_WAITING, &vcc->flags);
10162306a36Sopenharmony_ci		break;
10262306a36Sopenharmony_ci	case as_indicate:
10362306a36Sopenharmony_ci		vcc = *(struct atm_vcc **)&msg->listen_vcc;
10462306a36Sopenharmony_ci		sk = sk_atm(vcc);
10562306a36Sopenharmony_ci		pr_debug("as_indicate!!!\n");
10662306a36Sopenharmony_ci		lock_sock(sk);
10762306a36Sopenharmony_ci		if (sk_acceptq_is_full(sk)) {
10862306a36Sopenharmony_ci			sigd_enq(NULL, as_reject, vcc, NULL, NULL);
10962306a36Sopenharmony_ci			dev_kfree_skb(skb);
11062306a36Sopenharmony_ci			goto as_indicate_complete;
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci		sk_acceptq_added(sk);
11362306a36Sopenharmony_ci		skb_queue_tail(&sk->sk_receive_queue, skb);
11462306a36Sopenharmony_ci		pr_debug("waking sk_sleep(sk) 0x%p\n", sk_sleep(sk));
11562306a36Sopenharmony_ci		sk->sk_state_change(sk);
11662306a36Sopenharmony_cias_indicate_complete:
11762306a36Sopenharmony_ci		release_sock(sk);
11862306a36Sopenharmony_ci		return 0;
11962306a36Sopenharmony_ci	case as_close:
12062306a36Sopenharmony_ci		set_bit(ATM_VF_RELEASED, &vcc->flags);
12162306a36Sopenharmony_ci		vcc_release_async(vcc, msg->reply);
12262306a36Sopenharmony_ci		goto out;
12362306a36Sopenharmony_ci	case as_modify:
12462306a36Sopenharmony_ci		modify_qos(vcc, msg);
12562306a36Sopenharmony_ci		break;
12662306a36Sopenharmony_ci	case as_addparty:
12762306a36Sopenharmony_ci	case as_dropparty:
12862306a36Sopenharmony_ci		WRITE_ONCE(sk->sk_err_soft, -msg->reply);
12962306a36Sopenharmony_ci					/* < 0 failure, otherwise ep_ref */
13062306a36Sopenharmony_ci		clear_bit(ATM_VF_WAITING, &vcc->flags);
13162306a36Sopenharmony_ci		break;
13262306a36Sopenharmony_ci	default:
13362306a36Sopenharmony_ci		pr_alert("bad message type %d\n", (int)msg->type);
13462306a36Sopenharmony_ci		return -EINVAL;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	sk->sk_state_change(sk);
13762306a36Sopenharmony_ciout:
13862306a36Sopenharmony_ci	dev_kfree_skb(skb);
13962306a36Sopenharmony_ci	return 0;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_civoid sigd_enq2(struct atm_vcc *vcc, enum atmsvc_msg_type type,
14362306a36Sopenharmony_ci	       struct atm_vcc *listen_vcc, const struct sockaddr_atmpvc *pvc,
14462306a36Sopenharmony_ci	       const struct sockaddr_atmsvc *svc, const struct atm_qos *qos,
14562306a36Sopenharmony_ci	       int reply)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct sk_buff *skb;
14862306a36Sopenharmony_ci	struct atmsvc_msg *msg;
14962306a36Sopenharmony_ci	static unsigned int session = 0;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	pr_debug("%d (0x%p)\n", (int)type, vcc);
15262306a36Sopenharmony_ci	while (!(skb = alloc_skb(sizeof(struct atmsvc_msg), GFP_KERNEL)))
15362306a36Sopenharmony_ci		schedule();
15462306a36Sopenharmony_ci	msg = skb_put_zero(skb, sizeof(struct atmsvc_msg));
15562306a36Sopenharmony_ci	msg->type = type;
15662306a36Sopenharmony_ci	*(struct atm_vcc **) &msg->vcc = vcc;
15762306a36Sopenharmony_ci	*(struct atm_vcc **) &msg->listen_vcc = listen_vcc;
15862306a36Sopenharmony_ci	msg->reply = reply;
15962306a36Sopenharmony_ci	if (qos)
16062306a36Sopenharmony_ci		msg->qos = *qos;
16162306a36Sopenharmony_ci	if (vcc)
16262306a36Sopenharmony_ci		msg->sap = vcc->sap;
16362306a36Sopenharmony_ci	if (svc)
16462306a36Sopenharmony_ci		msg->svc = *svc;
16562306a36Sopenharmony_ci	if (vcc)
16662306a36Sopenharmony_ci		msg->local = vcc->local;
16762306a36Sopenharmony_ci	if (pvc)
16862306a36Sopenharmony_ci		msg->pvc = *pvc;
16962306a36Sopenharmony_ci	if (vcc) {
17062306a36Sopenharmony_ci		if (type == as_connect && test_bit(ATM_VF_SESSION, &vcc->flags))
17162306a36Sopenharmony_ci			msg->session = ++session;
17262306a36Sopenharmony_ci			/* every new pmp connect gets the next session number */
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci	sigd_put_skb(skb);
17562306a36Sopenharmony_ci	if (vcc)
17662306a36Sopenharmony_ci		set_bit(ATM_VF_REGIS, &vcc->flags);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_civoid sigd_enq(struct atm_vcc *vcc, enum atmsvc_msg_type type,
18062306a36Sopenharmony_ci	      struct atm_vcc *listen_vcc, const struct sockaddr_atmpvc *pvc,
18162306a36Sopenharmony_ci	      const struct sockaddr_atmsvc *svc)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	sigd_enq2(vcc, type, listen_vcc, pvc, svc, vcc ? &vcc->qos : NULL, 0);
18462306a36Sopenharmony_ci	/* other ISP applications may use "reply" */
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic void purge_vcc(struct atm_vcc *vcc)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	if (sk_atm(vcc)->sk_family == PF_ATMSVC &&
19062306a36Sopenharmony_ci	    !test_bit(ATM_VF_META, &vcc->flags)) {
19162306a36Sopenharmony_ci		set_bit(ATM_VF_RELEASED, &vcc->flags);
19262306a36Sopenharmony_ci		clear_bit(ATM_VF_REGIS, &vcc->flags);
19362306a36Sopenharmony_ci		vcc_release_async(vcc, -EUNATCH);
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void sigd_close(struct atm_vcc *vcc)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct sock *s;
20062306a36Sopenharmony_ci	int i;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	pr_debug("\n");
20362306a36Sopenharmony_ci	sigd = NULL;
20462306a36Sopenharmony_ci	if (skb_peek(&sk_atm(vcc)->sk_receive_queue))
20562306a36Sopenharmony_ci		pr_err("closing with requests pending\n");
20662306a36Sopenharmony_ci	skb_queue_purge(&sk_atm(vcc)->sk_receive_queue);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	read_lock(&vcc_sklist_lock);
20962306a36Sopenharmony_ci	for (i = 0; i < VCC_HTABLE_SIZE; ++i) {
21062306a36Sopenharmony_ci		struct hlist_head *head = &vcc_hash[i];
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		sk_for_each(s, head) {
21362306a36Sopenharmony_ci			vcc = atm_sk(s);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci			purge_vcc(vcc);
21662306a36Sopenharmony_ci		}
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci	read_unlock(&vcc_sklist_lock);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic const struct atmdev_ops sigd_dev_ops = {
22262306a36Sopenharmony_ci	.close = sigd_close,
22362306a36Sopenharmony_ci	.send =	sigd_send
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic struct atm_dev sigd_dev = {
22762306a36Sopenharmony_ci	.ops =		&sigd_dev_ops,
22862306a36Sopenharmony_ci	.type =		"sig",
22962306a36Sopenharmony_ci	.number =	999,
23062306a36Sopenharmony_ci	.lock =		__SPIN_LOCK_UNLOCKED(sigd_dev.lock)
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ciint sigd_attach(struct atm_vcc *vcc)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	if (sigd)
23662306a36Sopenharmony_ci		return -EADDRINUSE;
23762306a36Sopenharmony_ci	pr_debug("\n");
23862306a36Sopenharmony_ci	sigd = vcc;
23962306a36Sopenharmony_ci	vcc->dev = &sigd_dev;
24062306a36Sopenharmony_ci	vcc_insert_socket(sk_atm(vcc));
24162306a36Sopenharmony_ci	set_bit(ATM_VF_META, &vcc->flags);
24262306a36Sopenharmony_ci	set_bit(ATM_VF_READY, &vcc->flags);
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ci}
245