1/** -*- linux-c -*- ***********************************************************
2 * Linux PPP over X/Ethernet (PPPoX/PPPoE) Sockets
3 *
4 * PPPoX --- Generic PPP encapsulation socket family
5 * PPPoE --- PPP over Ethernet (RFC 2516)
6 *
7 *
8 * Version:	0.5.2
9 *
10 * Author:	Michal Ostrowski <mostrows@speakeasy.net>
11 *
12 * 051000 :	Initialization cleanup
13 *
14 * License:
15 *		This program is free software; you can redistribute it and/or
16 *		modify it under the terms of the GNU General Public License
17 *		as published by the Free Software Foundation; either version
18 *		2 of the License, or (at your option) any later version.
19 *
20 */
21
22#include <linux/string.h>
23#include <linux/module.h>
24
25#include <asm/uaccess.h>
26
27#include <linux/kernel.h>
28#include <linux/sched.h>
29#include <linux/slab.h>
30#include <linux/errno.h>
31
32#include <linux/netdevice.h>
33#include <linux/net.h>
34#include <linux/init.h>
35#include <linux/if_pppox.h>
36#include <net/sock.h>
37#include <linux/ppp_defs.h>
38#include <linux/if_ppp.h>
39#include <linux/ppp_channel.h>
40
41static struct pppox_proto *proto[PX_MAX_PROTO+1];
42
43int register_pppox_proto(int proto_num, struct pppox_proto *pp)
44{
45	if (proto_num < 0 || proto_num > PX_MAX_PROTO) {
46		return -EINVAL;
47	}
48
49	if (proto[proto_num])
50		return -EALREADY;
51
52	MOD_INC_USE_COUNT;
53
54	proto[proto_num] = pp;
55	return 0;
56}
57
58void unregister_pppox_proto(int proto_num)
59{
60	if (proto_num >= 0 && proto_num <= PX_MAX_PROTO) {
61	    proto[proto_num] = NULL;
62	    MOD_DEC_USE_COUNT;
63	}
64}
65
66void pppox_unbind_sock(struct sock *sk)
67{
68	/* Clear connection to ppp device, if attached. */
69
70	if (sk->state & (PPPOX_BOUND|PPPOX_ZOMBIE)) {
71		ppp_unregister_channel(&sk->protinfo.pppox->chan);
72		sk->state = PPPOX_DEAD;
73	}
74}
75
76EXPORT_SYMBOL(register_pppox_proto);
77EXPORT_SYMBOL(unregister_pppox_proto);
78EXPORT_SYMBOL(pppox_unbind_sock);
79
80int pppox_ioctl(struct socket* sock, unsigned int cmd,
81		       unsigned long arg)
82{
83	struct sock *sk = sock->sk;
84	struct pppox_opt *po;
85	int err = 0;
86
87	po = sk->protinfo.pppox;
88
89	lock_sock(sk);
90
91	switch (cmd) {
92	case PPPIOCGCHAN:{
93		int index;
94		err = -ENOTCONN;
95		if (!(sk->state & PPPOX_CONNECTED))
96			break;
97
98		err = -EINVAL;
99		index = ppp_channel_index(&po->chan);
100		if (put_user(index , (int *) arg))
101			break;
102
103		err = 0;
104		sk->state |= PPPOX_BOUND;
105		break;
106	}
107	default:
108		if (proto[sk->protocol]->ioctl)
109			err = (*proto[sk->protocol]->ioctl)(sock, cmd, arg);
110
111		break;
112	};
113
114	release_sock(sk);
115	return err;
116}
117
118EXPORT_SYMBOL(pppox_ioctl);
119
120static int pppox_create(struct socket *sock, int protocol)
121{
122	int err = 0;
123
124	if (protocol < 0 || protocol > PX_MAX_PROTO)
125		return -EPROTOTYPE;
126
127#ifdef CONFIG_KMOD
128	if (proto[protocol] == NULL) {
129		char buffer[32];
130		sprintf(buffer, "pppox-proto-%d", protocol);
131		request_module(buffer);
132	}
133#endif
134	if (proto[protocol] == NULL)
135		return -EPROTONOSUPPORT;
136
137	err = (*proto[protocol]->create)(sock);
138
139	if (err == 0) {
140		/* We get to set the ioctl handler. */
141		/* For everything else, pppox is just a shell. */
142		sock->ops->ioctl = pppox_ioctl;
143	}
144
145	return err;
146}
147
148static struct net_proto_family pppox_proto_family = {
149	PF_PPPOX,
150	pppox_create
151};
152
153static int __init pppox_init(void)
154{
155	int err = 0;
156
157	err = sock_register(&pppox_proto_family);
158
159	return err;
160}
161
162static void __exit pppox_exit(void)
163{
164	sock_unregister(PF_PPPOX);
165}
166
167module_init(pppox_init);
168module_exit(pppox_exit);
169
170MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
171MODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)");
172MODULE_LICENSE("GPL");
173