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