162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/** -*- linux-c -*- ***********************************************************
362306a36Sopenharmony_ci * Linux PPP over X/Ethernet (PPPoX/PPPoE) Sockets
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * PPPoX --- Generic PPP encapsulation socket family
662306a36Sopenharmony_ci * PPPoE --- PPP over Ethernet (RFC 2516)
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Version:	0.5.2
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Author:	Michal Ostrowski <mostrows@speakeasy.net>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * 051000 :	Initialization cleanup
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * License:
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/string.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/kernel.h>
2062306a36Sopenharmony_ci#include <linux/compat.h>
2162306a36Sopenharmony_ci#include <linux/errno.h>
2262306a36Sopenharmony_ci#include <linux/netdevice.h>
2362306a36Sopenharmony_ci#include <linux/net.h>
2462306a36Sopenharmony_ci#include <linux/init.h>
2562306a36Sopenharmony_ci#include <linux/if_pppox.h>
2662306a36Sopenharmony_ci#include <linux/ppp_defs.h>
2762306a36Sopenharmony_ci#include <linux/ppp-ioctl.h>
2862306a36Sopenharmony_ci#include <linux/ppp_channel.h>
2962306a36Sopenharmony_ci#include <linux/kmod.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <net/sock.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <linux/uaccess.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic const struct pppox_proto *pppox_protos[PX_MAX_PROTO + 1];
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ciint register_pppox_proto(int proto_num, const struct pppox_proto *pp)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	if (proto_num < 0 || proto_num > PX_MAX_PROTO)
4062306a36Sopenharmony_ci		return -EINVAL;
4162306a36Sopenharmony_ci	if (pppox_protos[proto_num])
4262306a36Sopenharmony_ci		return -EALREADY;
4362306a36Sopenharmony_ci	pppox_protos[proto_num] = pp;
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_civoid unregister_pppox_proto(int proto_num)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	if (proto_num >= 0 && proto_num <= PX_MAX_PROTO)
5062306a36Sopenharmony_ci		pppox_protos[proto_num] = NULL;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_civoid pppox_unbind_sock(struct sock *sk)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	/* Clear connection to ppp device, if attached. */
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED)) {
5862306a36Sopenharmony_ci		ppp_unregister_channel(&pppox_sk(sk)->chan);
5962306a36Sopenharmony_ci		sk->sk_state = PPPOX_DEAD;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ciEXPORT_SYMBOL(register_pppox_proto);
6462306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_pppox_proto);
6562306a36Sopenharmony_ciEXPORT_SYMBOL(pppox_unbind_sock);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ciint pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct sock *sk = sock->sk;
7062306a36Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
7162306a36Sopenharmony_ci	int rc;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	lock_sock(sk);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	switch (cmd) {
7662306a36Sopenharmony_ci	case PPPIOCGCHAN: {
7762306a36Sopenharmony_ci		int index;
7862306a36Sopenharmony_ci		rc = -ENOTCONN;
7962306a36Sopenharmony_ci		if (!(sk->sk_state & PPPOX_CONNECTED))
8062306a36Sopenharmony_ci			break;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		rc = -EINVAL;
8362306a36Sopenharmony_ci		index = ppp_channel_index(&po->chan);
8462306a36Sopenharmony_ci		if (put_user(index , (int __user *) arg))
8562306a36Sopenharmony_ci			break;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci		rc = 0;
8862306a36Sopenharmony_ci		sk->sk_state |= PPPOX_BOUND;
8962306a36Sopenharmony_ci		break;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	default:
9262306a36Sopenharmony_ci		rc = pppox_protos[sk->sk_protocol]->ioctl ?
9362306a36Sopenharmony_ci			pppox_protos[sk->sk_protocol]->ioctl(sock, cmd, arg) : -ENOTTY;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	release_sock(sk);
9762306a36Sopenharmony_ci	return rc;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ciEXPORT_SYMBOL(pppox_ioctl);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
10362306a36Sopenharmony_ciint pppox_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	if (cmd == PPPOEIOCSFWD32)
10662306a36Sopenharmony_ci		cmd = PPPOEIOCSFWD;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return pppox_ioctl(sock, cmd, (unsigned long)compat_ptr(arg));
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ciEXPORT_SYMBOL(pppox_compat_ioctl);
11262306a36Sopenharmony_ci#endif
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic int pppox_create(struct net *net, struct socket *sock, int protocol,
11562306a36Sopenharmony_ci			int kern)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	int rc = -EPROTOTYPE;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (protocol < 0 || protocol > PX_MAX_PROTO)
12062306a36Sopenharmony_ci		goto out;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	rc = -EPROTONOSUPPORT;
12362306a36Sopenharmony_ci	if (!pppox_protos[protocol])
12462306a36Sopenharmony_ci		request_module("net-pf-%d-proto-%d", PF_PPPOX, protocol);
12562306a36Sopenharmony_ci	if (!pppox_protos[protocol] ||
12662306a36Sopenharmony_ci	    !try_module_get(pppox_protos[protocol]->owner))
12762306a36Sopenharmony_ci		goto out;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	rc = pppox_protos[protocol]->create(net, sock, kern);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	module_put(pppox_protos[protocol]->owner);
13262306a36Sopenharmony_ciout:
13362306a36Sopenharmony_ci	return rc;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic const struct net_proto_family pppox_proto_family = {
13762306a36Sopenharmony_ci	.family	= PF_PPPOX,
13862306a36Sopenharmony_ci	.create	= pppox_create,
13962306a36Sopenharmony_ci	.owner	= THIS_MODULE,
14062306a36Sopenharmony_ci};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int __init pppox_init(void)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	return sock_register(&pppox_proto_family);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic void __exit pppox_exit(void)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	sock_unregister(PF_PPPOX);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cimodule_init(pppox_init);
15362306a36Sopenharmony_cimodule_exit(pppox_exit);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ciMODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
15662306a36Sopenharmony_ciMODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)");
15762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
15862306a36Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_PPPOX);
159