1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /** -*- linux-c -*- *********************************************************** |
3 | * Linux PPP over X/Ethernet (PPPoX/PPPoE) Sockets |
4 | * |
5 | * PPPoX --- Generic PPP encapsulation socket family |
6 | * PPPoE --- PPP over Ethernet (RFC 2516) |
7 | * |
8 | * Version: 0.5.2 |
9 | * |
10 | * Author: Michal Ostrowski <mostrows@speakeasy.net> |
11 | * |
12 | * 051000 : Initialization cleanup |
13 | * |
14 | * License: |
15 | */ |
16 | |
17 | #include <linux/string.h> |
18 | #include <linux/module.h> |
19 | #include <linux/kernel.h> |
20 | #include <linux/compat.h> |
21 | #include <linux/errno.h> |
22 | #include <linux/netdevice.h> |
23 | #include <linux/net.h> |
24 | #include <linux/init.h> |
25 | #include <linux/if_pppox.h> |
26 | #include <linux/ppp_defs.h> |
27 | #include <linux/ppp-ioctl.h> |
28 | #include <linux/ppp_channel.h> |
29 | #include <linux/kmod.h> |
30 | |
31 | #include <net/sock.h> |
32 | |
33 | #include <linux/uaccess.h> |
34 | |
35 | static const struct pppox_proto *pppox_protos[PX_MAX_PROTO + 1]; |
36 | |
37 | int register_pppox_proto(int proto_num, const struct pppox_proto *pp) |
38 | { |
39 | if (proto_num < 0 || proto_num > PX_MAX_PROTO) |
40 | return -EINVAL; |
41 | if (pppox_protos[proto_num]) |
42 | return -EALREADY; |
43 | pppox_protos[proto_num] = pp; |
44 | return 0; |
45 | } |
46 | |
47 | void unregister_pppox_proto(int proto_num) |
48 | { |
49 | if (proto_num >= 0 && proto_num <= PX_MAX_PROTO) |
50 | pppox_protos[proto_num] = NULL; |
51 | } |
52 | |
53 | void pppox_unbind_sock(struct sock *sk) |
54 | { |
55 | /* Clear connection to ppp device, if attached. */ |
56 | |
57 | if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED)) { |
58 | ppp_unregister_channel(&pppox_sk(sk)->chan); |
59 | sk->sk_state = PPPOX_DEAD; |
60 | } |
61 | } |
62 | |
63 | EXPORT_SYMBOL(register_pppox_proto); |
64 | EXPORT_SYMBOL(unregister_pppox_proto); |
65 | EXPORT_SYMBOL(pppox_unbind_sock); |
66 | |
67 | int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) |
68 | { |
69 | struct sock *sk = sock->sk; |
70 | struct pppox_sock *po = pppox_sk(sk); |
71 | int rc; |
72 | |
73 | lock_sock(sk); |
74 | |
75 | switch (cmd) { |
76 | case PPPIOCGCHAN: { |
77 | int index; |
78 | rc = -ENOTCONN; |
79 | if (!(sk->sk_state & PPPOX_CONNECTED)) |
80 | break; |
81 | |
82 | rc = -EINVAL; |
83 | index = ppp_channel_index(&po->chan); |
84 | if (put_user(index , (int __user *) arg)) |
85 | break; |
86 | |
87 | rc = 0; |
88 | sk->sk_state |= PPPOX_BOUND; |
89 | break; |
90 | } |
91 | default: |
92 | rc = pppox_protos[sk->sk_protocol]->ioctl ? |
93 | pppox_protos[sk->sk_protocol]->ioctl(sock, cmd, arg) : -ENOTTY; |
94 | } |
95 | |
96 | release_sock(sk); |
97 | return rc; |
98 | } |
99 | |
100 | EXPORT_SYMBOL(pppox_ioctl); |
101 | |
102 | #ifdef CONFIG_COMPAT |
103 | int pppox_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) |
104 | { |
105 | if (cmd == PPPOEIOCSFWD32) |
106 | cmd = PPPOEIOCSFWD; |
107 | |
108 | return pppox_ioctl(sock, cmd, (unsigned long)compat_ptr(uptr: arg)); |
109 | } |
110 | |
111 | EXPORT_SYMBOL(pppox_compat_ioctl); |
112 | #endif |
113 | |
114 | static int pppox_create(struct net *net, struct socket *sock, int protocol, |
115 | int kern) |
116 | { |
117 | int rc = -EPROTOTYPE; |
118 | |
119 | if (protocol < 0 || protocol > PX_MAX_PROTO) |
120 | goto out; |
121 | |
122 | rc = -EPROTONOSUPPORT; |
123 | if (!pppox_protos[protocol]) |
124 | request_module("net-pf-%d-proto-%d" , PF_PPPOX, protocol); |
125 | if (!pppox_protos[protocol] || |
126 | !try_module_get(module: pppox_protos[protocol]->owner)) |
127 | goto out; |
128 | |
129 | rc = pppox_protos[protocol]->create(net, sock, kern); |
130 | |
131 | module_put(module: pppox_protos[protocol]->owner); |
132 | out: |
133 | return rc; |
134 | } |
135 | |
136 | static const struct net_proto_family pppox_proto_family = { |
137 | .family = PF_PPPOX, |
138 | .create = pppox_create, |
139 | .owner = THIS_MODULE, |
140 | }; |
141 | |
142 | static int __init pppox_init(void) |
143 | { |
144 | return sock_register(fam: &pppox_proto_family); |
145 | } |
146 | |
147 | static void __exit pppox_exit(void) |
148 | { |
149 | sock_unregister(PF_PPPOX); |
150 | } |
151 | |
152 | module_init(pppox_init); |
153 | module_exit(pppox_exit); |
154 | |
155 | MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>" ); |
156 | MODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)" ); |
157 | MODULE_LICENSE("GPL" ); |
158 | MODULE_ALIAS_NETPROTO(PF_PPPOX); |
159 | |