18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/** -*- linux-c -*- *********************************************************** 38c2ecf20Sopenharmony_ci * Linux PPP over X/Ethernet (PPPoX/PPPoE) Sockets 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * PPPoX --- Generic PPP encapsulation socket family 68c2ecf20Sopenharmony_ci * PPPoE --- PPP over Ethernet (RFC 2516) 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Version: 0.5.2 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Author: Michal Ostrowski <mostrows@speakeasy.net> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * 051000 : Initialization cleanup 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * License: 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/string.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/compat.h> 218c2ecf20Sopenharmony_ci#include <linux/errno.h> 228c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 238c2ecf20Sopenharmony_ci#include <linux/net.h> 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci#include <linux/if_pppox.h> 268c2ecf20Sopenharmony_ci#include <linux/ppp_defs.h> 278c2ecf20Sopenharmony_ci#include <linux/ppp-ioctl.h> 288c2ecf20Sopenharmony_ci#include <linux/ppp_channel.h> 298c2ecf20Sopenharmony_ci#include <linux/kmod.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <net/sock.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const struct pppox_proto *pppox_protos[PX_MAX_PROTO + 1]; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ciint register_pppox_proto(int proto_num, const struct pppox_proto *pp) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci if (proto_num < 0 || proto_num > PX_MAX_PROTO) 408c2ecf20Sopenharmony_ci return -EINVAL; 418c2ecf20Sopenharmony_ci if (pppox_protos[proto_num]) 428c2ecf20Sopenharmony_ci return -EALREADY; 438c2ecf20Sopenharmony_ci pppox_protos[proto_num] = pp; 448c2ecf20Sopenharmony_ci return 0; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_civoid unregister_pppox_proto(int proto_num) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci if (proto_num >= 0 && proto_num <= PX_MAX_PROTO) 508c2ecf20Sopenharmony_ci pppox_protos[proto_num] = NULL; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_civoid pppox_unbind_sock(struct sock *sk) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci /* Clear connection to ppp device, if attached. */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED)) { 588c2ecf20Sopenharmony_ci ppp_unregister_channel(&pppox_sk(sk)->chan); 598c2ecf20Sopenharmony_ci sk->sk_state = PPPOX_DEAD; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_pppox_proto); 648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_pppox_proto); 658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pppox_unbind_sock); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ciint pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct sock *sk = sock->sk; 708c2ecf20Sopenharmony_ci struct pppox_sock *po = pppox_sk(sk); 718c2ecf20Sopenharmony_ci int rc; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci lock_sock(sk); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci switch (cmd) { 768c2ecf20Sopenharmony_ci case PPPIOCGCHAN: { 778c2ecf20Sopenharmony_ci int index; 788c2ecf20Sopenharmony_ci rc = -ENOTCONN; 798c2ecf20Sopenharmony_ci if (!(sk->sk_state & PPPOX_CONNECTED)) 808c2ecf20Sopenharmony_ci break; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci rc = -EINVAL; 838c2ecf20Sopenharmony_ci index = ppp_channel_index(&po->chan); 848c2ecf20Sopenharmony_ci if (put_user(index , (int __user *) arg)) 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci rc = 0; 888c2ecf20Sopenharmony_ci sk->sk_state |= PPPOX_BOUND; 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci default: 928c2ecf20Sopenharmony_ci rc = pppox_protos[sk->sk_protocol]->ioctl ? 938c2ecf20Sopenharmony_ci pppox_protos[sk->sk_protocol]->ioctl(sock, cmd, arg) : -ENOTTY; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci release_sock(sk); 978c2ecf20Sopenharmony_ci return rc; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pppox_ioctl); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 1038c2ecf20Sopenharmony_ciint pppox_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci if (cmd == PPPOEIOCSFWD32) 1068c2ecf20Sopenharmony_ci cmd = PPPOEIOCSFWD; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return pppox_ioctl(sock, cmd, (unsigned long)compat_ptr(arg)); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pppox_compat_ioctl); 1128c2ecf20Sopenharmony_ci#endif 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int pppox_create(struct net *net, struct socket *sock, int protocol, 1158c2ecf20Sopenharmony_ci int kern) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci int rc = -EPROTOTYPE; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (protocol < 0 || protocol > PX_MAX_PROTO) 1208c2ecf20Sopenharmony_ci goto out; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci rc = -EPROTONOSUPPORT; 1238c2ecf20Sopenharmony_ci if (!pppox_protos[protocol]) 1248c2ecf20Sopenharmony_ci request_module("net-pf-%d-proto-%d", PF_PPPOX, protocol); 1258c2ecf20Sopenharmony_ci if (!pppox_protos[protocol] || 1268c2ecf20Sopenharmony_ci !try_module_get(pppox_protos[protocol]->owner)) 1278c2ecf20Sopenharmony_ci goto out; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci rc = pppox_protos[protocol]->create(net, sock, kern); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci module_put(pppox_protos[protocol]->owner); 1328c2ecf20Sopenharmony_ciout: 1338c2ecf20Sopenharmony_ci return rc; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic const struct net_proto_family pppox_proto_family = { 1378c2ecf20Sopenharmony_ci .family = PF_PPPOX, 1388c2ecf20Sopenharmony_ci .create = pppox_create, 1398c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int __init pppox_init(void) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci return sock_register(&pppox_proto_family); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void __exit pppox_exit(void) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci sock_unregister(PF_PPPOX); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cimodule_init(pppox_init); 1538c2ecf20Sopenharmony_cimodule_exit(pppox_exit); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>"); 1568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)"); 1578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1588c2ecf20Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_PPPOX); 159