12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2224cf5adSJeff Kirsher /** -*- linux-c -*- ***********************************************************
3224cf5adSJeff Kirsher * Linux PPP over X/Ethernet (PPPoX/PPPoE) Sockets
4224cf5adSJeff Kirsher *
5224cf5adSJeff Kirsher * PPPoX --- Generic PPP encapsulation socket family
6224cf5adSJeff Kirsher * PPPoE --- PPP over Ethernet (RFC 2516)
7224cf5adSJeff Kirsher *
8224cf5adSJeff Kirsher * Version: 0.5.2
9224cf5adSJeff Kirsher *
10224cf5adSJeff Kirsher * Author: Michal Ostrowski <mostrows@speakeasy.net>
11224cf5adSJeff Kirsher *
12224cf5adSJeff Kirsher * 051000 : Initialization cleanup
13224cf5adSJeff Kirsher *
14224cf5adSJeff Kirsher * License:
15224cf5adSJeff Kirsher */
16224cf5adSJeff Kirsher
17224cf5adSJeff Kirsher #include <linux/string.h>
18224cf5adSJeff Kirsher #include <linux/module.h>
19224cf5adSJeff Kirsher #include <linux/kernel.h>
20*055d8824SArnd Bergmann #include <linux/compat.h>
21224cf5adSJeff Kirsher #include <linux/errno.h>
22224cf5adSJeff Kirsher #include <linux/netdevice.h>
23224cf5adSJeff Kirsher #include <linux/net.h>
24224cf5adSJeff Kirsher #include <linux/init.h>
25224cf5adSJeff Kirsher #include <linux/if_pppox.h>
26224cf5adSJeff Kirsher #include <linux/ppp_defs.h>
274b32da2bSPaul Mackerras #include <linux/ppp-ioctl.h>
28224cf5adSJeff Kirsher #include <linux/ppp_channel.h>
29224cf5adSJeff Kirsher #include <linux/kmod.h>
30224cf5adSJeff Kirsher
31224cf5adSJeff Kirsher #include <net/sock.h>
32224cf5adSJeff Kirsher
337c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
34224cf5adSJeff Kirsher
35224cf5adSJeff Kirsher static const struct pppox_proto *pppox_protos[PX_MAX_PROTO + 1];
36224cf5adSJeff Kirsher
register_pppox_proto(int proto_num,const struct pppox_proto * pp)37224cf5adSJeff Kirsher int register_pppox_proto(int proto_num, const struct pppox_proto *pp)
38224cf5adSJeff Kirsher {
39224cf5adSJeff Kirsher if (proto_num < 0 || proto_num > PX_MAX_PROTO)
40224cf5adSJeff Kirsher return -EINVAL;
41224cf5adSJeff Kirsher if (pppox_protos[proto_num])
42224cf5adSJeff Kirsher return -EALREADY;
43224cf5adSJeff Kirsher pppox_protos[proto_num] = pp;
44224cf5adSJeff Kirsher return 0;
45224cf5adSJeff Kirsher }
46224cf5adSJeff Kirsher
unregister_pppox_proto(int proto_num)47224cf5adSJeff Kirsher void unregister_pppox_proto(int proto_num)
48224cf5adSJeff Kirsher {
49224cf5adSJeff Kirsher if (proto_num >= 0 && proto_num <= PX_MAX_PROTO)
50224cf5adSJeff Kirsher pppox_protos[proto_num] = NULL;
51224cf5adSJeff Kirsher }
52224cf5adSJeff Kirsher
pppox_unbind_sock(struct sock * sk)53224cf5adSJeff Kirsher void pppox_unbind_sock(struct sock *sk)
54224cf5adSJeff Kirsher {
55224cf5adSJeff Kirsher /* Clear connection to ppp device, if attached. */
56224cf5adSJeff Kirsher
57a8acce6aSGuillaume Nault if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED)) {
58224cf5adSJeff Kirsher ppp_unregister_channel(&pppox_sk(sk)->chan);
59224cf5adSJeff Kirsher sk->sk_state = PPPOX_DEAD;
60224cf5adSJeff Kirsher }
61224cf5adSJeff Kirsher }
62224cf5adSJeff Kirsher
63224cf5adSJeff Kirsher EXPORT_SYMBOL(register_pppox_proto);
64224cf5adSJeff Kirsher EXPORT_SYMBOL(unregister_pppox_proto);
65224cf5adSJeff Kirsher EXPORT_SYMBOL(pppox_unbind_sock);
66224cf5adSJeff Kirsher
pppox_ioctl(struct socket * sock,unsigned int cmd,unsigned long arg)67224cf5adSJeff Kirsher int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
68224cf5adSJeff Kirsher {
69224cf5adSJeff Kirsher struct sock *sk = sock->sk;
70224cf5adSJeff Kirsher struct pppox_sock *po = pppox_sk(sk);
71224cf5adSJeff Kirsher int rc;
72224cf5adSJeff Kirsher
73224cf5adSJeff Kirsher lock_sock(sk);
74224cf5adSJeff Kirsher
75224cf5adSJeff Kirsher switch (cmd) {
76224cf5adSJeff Kirsher case PPPIOCGCHAN: {
77224cf5adSJeff Kirsher int index;
78224cf5adSJeff Kirsher rc = -ENOTCONN;
79224cf5adSJeff Kirsher if (!(sk->sk_state & PPPOX_CONNECTED))
80224cf5adSJeff Kirsher break;
81224cf5adSJeff Kirsher
82224cf5adSJeff Kirsher rc = -EINVAL;
83224cf5adSJeff Kirsher index = ppp_channel_index(&po->chan);
84224cf5adSJeff Kirsher if (put_user(index , (int __user *) arg))
85224cf5adSJeff Kirsher break;
86224cf5adSJeff Kirsher
87224cf5adSJeff Kirsher rc = 0;
88224cf5adSJeff Kirsher sk->sk_state |= PPPOX_BOUND;
89224cf5adSJeff Kirsher break;
90224cf5adSJeff Kirsher }
91224cf5adSJeff Kirsher default:
92224cf5adSJeff Kirsher rc = pppox_protos[sk->sk_protocol]->ioctl ?
93224cf5adSJeff Kirsher pppox_protos[sk->sk_protocol]->ioctl(sock, cmd, arg) : -ENOTTY;
94224cf5adSJeff Kirsher }
95224cf5adSJeff Kirsher
96224cf5adSJeff Kirsher release_sock(sk);
97224cf5adSJeff Kirsher return rc;
98224cf5adSJeff Kirsher }
99224cf5adSJeff Kirsher
100224cf5adSJeff Kirsher EXPORT_SYMBOL(pppox_ioctl);
101224cf5adSJeff Kirsher
102*055d8824SArnd Bergmann #ifdef CONFIG_COMPAT
pppox_compat_ioctl(struct socket * sock,unsigned int cmd,unsigned long arg)103*055d8824SArnd Bergmann int pppox_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
104*055d8824SArnd Bergmann {
105*055d8824SArnd Bergmann if (cmd == PPPOEIOCSFWD32)
106*055d8824SArnd Bergmann cmd = PPPOEIOCSFWD;
107*055d8824SArnd Bergmann
108*055d8824SArnd Bergmann return pppox_ioctl(sock, cmd, (unsigned long)compat_ptr(arg));
109*055d8824SArnd Bergmann }
110*055d8824SArnd Bergmann
111*055d8824SArnd Bergmann EXPORT_SYMBOL(pppox_compat_ioctl);
112*055d8824SArnd Bergmann #endif
113*055d8824SArnd Bergmann
pppox_create(struct net * net,struct socket * sock,int protocol,int kern)114224cf5adSJeff Kirsher static int pppox_create(struct net *net, struct socket *sock, int protocol,
115224cf5adSJeff Kirsher int kern)
116224cf5adSJeff Kirsher {
117224cf5adSJeff Kirsher int rc = -EPROTOTYPE;
118224cf5adSJeff Kirsher
119224cf5adSJeff Kirsher if (protocol < 0 || protocol > PX_MAX_PROTO)
120224cf5adSJeff Kirsher goto out;
121224cf5adSJeff Kirsher
122224cf5adSJeff Kirsher rc = -EPROTONOSUPPORT;
123224cf5adSJeff Kirsher if (!pppox_protos[protocol])
124681b4d88SGuillaume Nault request_module("net-pf-%d-proto-%d", PF_PPPOX, protocol);
125224cf5adSJeff Kirsher if (!pppox_protos[protocol] ||
126224cf5adSJeff Kirsher !try_module_get(pppox_protos[protocol]->owner))
127224cf5adSJeff Kirsher goto out;
128224cf5adSJeff Kirsher
12911aa9c28SEric W. Biederman rc = pppox_protos[protocol]->create(net, sock, kern);
130224cf5adSJeff Kirsher
131224cf5adSJeff Kirsher module_put(pppox_protos[protocol]->owner);
132224cf5adSJeff Kirsher out:
133224cf5adSJeff Kirsher return rc;
134224cf5adSJeff Kirsher }
135224cf5adSJeff Kirsher
136224cf5adSJeff Kirsher static const struct net_proto_family pppox_proto_family = {
137224cf5adSJeff Kirsher .family = PF_PPPOX,
138224cf5adSJeff Kirsher .create = pppox_create,
139224cf5adSJeff Kirsher .owner = THIS_MODULE,
140224cf5adSJeff Kirsher };
141224cf5adSJeff Kirsher
pppox_init(void)142224cf5adSJeff Kirsher static int __init pppox_init(void)
143224cf5adSJeff Kirsher {
144224cf5adSJeff Kirsher return sock_register(&pppox_proto_family);
145224cf5adSJeff Kirsher }
146224cf5adSJeff Kirsher
pppox_exit(void)147224cf5adSJeff Kirsher static void __exit pppox_exit(void)
148224cf5adSJeff Kirsher {
149224cf5adSJeff Kirsher sock_unregister(PF_PPPOX);
150224cf5adSJeff Kirsher }
151224cf5adSJeff Kirsher
152224cf5adSJeff Kirsher module_init(pppox_init);
153224cf5adSJeff Kirsher module_exit(pppox_exit);
154224cf5adSJeff Kirsher
155224cf5adSJeff Kirsher MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
156224cf5adSJeff Kirsher MODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)");
157224cf5adSJeff Kirsher MODULE_LICENSE("GPL");
158681b4d88SGuillaume Nault MODULE_ALIAS_NETPROTO(PF_PPPOX);
159