11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/** -*- linux-c -*- ***********************************************************
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Linux PPP over X/Ethernet (PPPoX/PPPoE) Sockets
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * PPPoX --- Generic PPP encapsulation socket family
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * PPPoE --- PPP over Ethernet (RFC 2516)
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version:	0.5.2
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Author:	Michal Ostrowski <mostrows@speakeasy.net>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 051000 :	Initialization cleanup
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * License:
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		This program is free software; you can redistribute it and/or
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		modify it under the terms of the GNU General Public License
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		as published by the Free Software Foundation; either version
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		2 of the License, or (at your option) any later version.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/net.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/if_pppox.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ppp_defs.h>
314b32da2bcf1de2b7a196a0e48389d231b4472c36Paul Mackerras#include <linux/ppp-ioctl.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ppp_channel.h>
3365def812ab25d7565756e5748d91e22e302197eeJames Chapman#include <linux/kmod.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/sock.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
39756e64a0b106f1a2ca96889c39ea0d48131105c0Eric Dumazetstatic const struct pppox_proto *pppox_protos[PX_MAX_PROTO + 1];
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
41756e64a0b106f1a2ca96889c39ea0d48131105c0Eric Dumazetint register_pppox_proto(int proto_num, const struct pppox_proto *pp)
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (proto_num < 0 || proto_num > PX_MAX_PROTO)
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (pppox_protos[proto_num])
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EALREADY;
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pppox_protos[proto_num] = pp;
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid unregister_pppox_proto(int proto_num)
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (proto_num >= 0 && proto_num <= PX_MAX_PROTO)
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pppox_protos[proto_num] = NULL;
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid pppox_unbind_sock(struct sock *sk)
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Clear connection to ppp device, if attached. */
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
61202a03acf9994076055df40ae093a5c5474ad0bdFlorian Zumbiehl	if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) {
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ppp_unregister_channel(&pppox_sk(sk)->chan);
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sk->sk_state = PPPOX_DEAD;
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(register_pppox_proto);
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(unregister_pppox_proto);
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(pppox_unbind_sock);
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7117ba15fb6264f27374bc87f4c3f8519b80289d85David S. Millerint pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sock *sk = sock->sk;
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct pppox_sock *po = pppox_sk(sk);
7586c1dcfc96a778433ebc6e9b1d3e80a126cb80f2Florian Zumbiehl	int rc;
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	lock_sock(sk);
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case PPPIOCGCHAN: {
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int index;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -ENOTCONN;
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!(sk->sk_state & PPPOX_CONNECTED))
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = -EINVAL;
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		index = ppp_channel_index(&po->chan);
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (put_user(index , (int __user *) arg))
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = 0;
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sk->sk_state |= PPPOX_BOUND;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
9686c1dcfc96a778433ebc6e9b1d3e80a126cb80f2Florian Zumbiehl		rc = pppox_protos[sk->sk_protocol]->ioctl ?
9786c1dcfc96a778433ebc6e9b1d3e80a126cb80f2Florian Zumbiehl			pppox_protos[sk->sk_protocol]->ioctl(sock, cmd, arg) : -ENOTTY;
9886c1dcfc96a778433ebc6e9b1d3e80a126cb80f2Florian Zumbiehl	}
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_sock(sk);
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10417ba15fb6264f27374bc87f4c3f8519b80289d85David S. MillerEXPORT_SYMBOL(pppox_ioctl);
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1063f378b684453f2a028eda463ce383370545d9cc9Eric Parisstatic int pppox_create(struct net *net, struct socket *sock, int protocol,
1073f378b684453f2a028eda463ce383370545d9cc9Eric Paris			int kern)
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = -EPROTOTYPE;
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (protocol < 0 || protocol > PX_MAX_PROTO)
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = -EPROTONOSUPPORT;
115a65e5d782f9db2a61a914dc01a329e0c2dcf92a1Johannes Berg	if (!pppox_protos[protocol])
116a65e5d782f9db2a61a914dc01a329e0c2dcf92a1Johannes Berg		request_module("pppox-proto-%d", protocol);
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!pppox_protos[protocol] ||
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    !try_module_get(pppox_protos[protocol]->owner))
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1211b8d7ae42d02e483ad94035cca851e4f7fbecb40Eric W. Biederman	rc = pppox_protos[protocol]->create(net, sock);
12217ba15fb6264f27374bc87f4c3f8519b80289d85David S. Miller
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	module_put(pppox_protos[protocol]->owner);
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
128ec1b4cf74c81bfd0fbe5bf62bafc86c45917e72fStephen Hemmingerstatic const struct net_proto_family pppox_proto_family = {
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.family	= PF_PPPOX,
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.create	= pppox_create,
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner	= THIS_MODULE,
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init pppox_init(void)
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return sock_register(&pppox_proto_family);
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit pppox_exit(void)
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sock_unregister(PF_PPPOX);
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(pppox_init);
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(pppox_exit);
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)");
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
150