xref: /kernel/linux/linux-6.6/drivers/net/ppp/pppoe.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/** -*- linux-c -*- ***********************************************************
362306a36Sopenharmony_ci * Linux PPP over 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.7.0
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * 070228 :	Fix to allow multiple sessions with same remote MAC and same
1162306a36Sopenharmony_ci *		session id by including the local device ifindex in the
1262306a36Sopenharmony_ci *		tuple identifying a session. This also ensures packets can't
1362306a36Sopenharmony_ci *		be injected into a session from interfaces other than the one
1462306a36Sopenharmony_ci *		specified by userspace. Florian Zumbiehl <florz@florz.de>
1562306a36Sopenharmony_ci *		(Oh, BTW, this one is YYMMDD, in case you were wondering ...)
1662306a36Sopenharmony_ci * 220102 :	Fix module use count on failure in pppoe_create, pppox_sk -acme
1762306a36Sopenharmony_ci * 030700 :	Fixed connect logic to allow for disconnect.
1862306a36Sopenharmony_ci * 270700 :	Fixed potential SMP problems; we must protect against
1962306a36Sopenharmony_ci *		simultaneous invocation of ppp_input
2062306a36Sopenharmony_ci *		and ppp_unregister_channel.
2162306a36Sopenharmony_ci * 040800 :	Respect reference count mechanisms on net-devices.
2262306a36Sopenharmony_ci * 200800 :	fix kfree(skb) in pppoe_rcv (acme)
2362306a36Sopenharmony_ci *		Module reference count is decremented in the right spot now,
2462306a36Sopenharmony_ci *		guards against sock_put not actually freeing the sk
2562306a36Sopenharmony_ci *		in pppoe_release.
2662306a36Sopenharmony_ci * 051000 :	Initialization cleanup.
2762306a36Sopenharmony_ci * 111100 :	Fix recvmsg.
2862306a36Sopenharmony_ci * 050101 :	Fix PADT processing.
2962306a36Sopenharmony_ci * 140501 :	Use pppoe_rcv_core to handle all backlog. (Alexey)
3062306a36Sopenharmony_ci * 170701 :	Do not lock_sock with rwlock held. (DaveM)
3162306a36Sopenharmony_ci *		Ignore discovery frames if user has socket
3262306a36Sopenharmony_ci *		locked. (DaveM)
3362306a36Sopenharmony_ci *		Ignore return value of dev_queue_xmit in __pppoe_xmit
3462306a36Sopenharmony_ci *		or else we may kfree an SKB twice. (DaveM)
3562306a36Sopenharmony_ci * 190701 :	When doing copies of skb's in __pppoe_xmit, always delete
3662306a36Sopenharmony_ci *		the original skb that was passed in on success, never on
3762306a36Sopenharmony_ci *		failure.  Delete the copy of the skb on failure to avoid
3862306a36Sopenharmony_ci *		a memory leak.
3962306a36Sopenharmony_ci * 081001 :	Misc. cleanup (licence string, non-blocking, prevent
4062306a36Sopenharmony_ci *		reference of device on close).
4162306a36Sopenharmony_ci * 121301 :	New ppp channels interface; cannot unregister a channel
4262306a36Sopenharmony_ci *		from interrupts.  Thus, we mark the socket as a ZOMBIE
4362306a36Sopenharmony_ci *		and do the unregistration later.
4462306a36Sopenharmony_ci * 081002 :	seq_file support for proc stuff -acme
4562306a36Sopenharmony_ci * 111602 :	Merge all 2.4 fixes into 2.5/2.6 tree.  Label 2.5/2.6
4662306a36Sopenharmony_ci *		as version 0.7.  Spacing cleanup.
4762306a36Sopenharmony_ci * Author:	Michal Ostrowski <mostrows@speakeasy.net>
4862306a36Sopenharmony_ci * Contributors:
4962306a36Sopenharmony_ci * 		Arnaldo Carvalho de Melo <acme@conectiva.com.br>
5062306a36Sopenharmony_ci *		David S. Miller (davem@redhat.com)
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * License:
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#include <linux/string.h>
5662306a36Sopenharmony_ci#include <linux/module.h>
5762306a36Sopenharmony_ci#include <linux/kernel.h>
5862306a36Sopenharmony_ci#include <linux/slab.h>
5962306a36Sopenharmony_ci#include <linux/errno.h>
6062306a36Sopenharmony_ci#include <linux/netdevice.h>
6162306a36Sopenharmony_ci#include <linux/net.h>
6262306a36Sopenharmony_ci#include <linux/inetdevice.h>
6362306a36Sopenharmony_ci#include <linux/etherdevice.h>
6462306a36Sopenharmony_ci#include <linux/skbuff.h>
6562306a36Sopenharmony_ci#include <linux/init.h>
6662306a36Sopenharmony_ci#include <linux/if_ether.h>
6762306a36Sopenharmony_ci#include <linux/if_pppox.h>
6862306a36Sopenharmony_ci#include <linux/ppp_channel.h>
6962306a36Sopenharmony_ci#include <linux/ppp_defs.h>
7062306a36Sopenharmony_ci#include <linux/ppp-ioctl.h>
7162306a36Sopenharmony_ci#include <linux/notifier.h>
7262306a36Sopenharmony_ci#include <linux/file.h>
7362306a36Sopenharmony_ci#include <linux/proc_fs.h>
7462306a36Sopenharmony_ci#include <linux/seq_file.h>
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#include <linux/nsproxy.h>
7762306a36Sopenharmony_ci#include <net/net_namespace.h>
7862306a36Sopenharmony_ci#include <net/netns/generic.h>
7962306a36Sopenharmony_ci#include <net/sock.h>
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#include <linux/uaccess.h>
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define PPPOE_HASH_BITS CONFIG_PPPOE_HASH_BITS
8462306a36Sopenharmony_ci#define PPPOE_HASH_SIZE (1 << PPPOE_HASH_BITS)
8562306a36Sopenharmony_ci#define PPPOE_HASH_MASK	(PPPOE_HASH_SIZE - 1)
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int __pppoe_xmit(struct sock *sk, struct sk_buff *skb);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic const struct proto_ops pppoe_ops;
9062306a36Sopenharmony_cistatic const struct ppp_channel_ops pppoe_chan_ops;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* per-net private data for this module */
9362306a36Sopenharmony_cistatic unsigned int pppoe_net_id __read_mostly;
9462306a36Sopenharmony_cistruct pppoe_net {
9562306a36Sopenharmony_ci	/*
9662306a36Sopenharmony_ci	 * we could use _single_ hash table for all
9762306a36Sopenharmony_ci	 * nets by injecting net id into the hash but
9862306a36Sopenharmony_ci	 * it would increase hash chains and add
9962306a36Sopenharmony_ci	 * a few additional math comparisons messy
10062306a36Sopenharmony_ci	 * as well, moreover in case of SMP less locking
10162306a36Sopenharmony_ci	 * controversy here
10262306a36Sopenharmony_ci	 */
10362306a36Sopenharmony_ci	struct pppox_sock *hash_table[PPPOE_HASH_SIZE];
10462306a36Sopenharmony_ci	rwlock_t hash_lock;
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * PPPoE could be in the following stages:
10962306a36Sopenharmony_ci * 1) Discovery stage (to obtain remote MAC and Session ID)
11062306a36Sopenharmony_ci * 2) Session stage (MAC and SID are known)
11162306a36Sopenharmony_ci *
11262306a36Sopenharmony_ci * Ethernet frames have a special tag for this but
11362306a36Sopenharmony_ci * we use simpler approach based on session id
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_cistatic inline bool stage_session(__be16 sid)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	return sid != 0;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic inline struct pppoe_net *pppoe_pernet(struct net *net)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	return net_generic(net, pppoe_net_id);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic inline int cmp_2_addr(struct pppoe_addr *a, struct pppoe_addr *b)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	return a->sid == b->sid && ether_addr_equal(a->remote, b->remote);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic inline int cmp_addr(struct pppoe_addr *a, __be16 sid, char *addr)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	return a->sid == sid && ether_addr_equal(a->remote, addr);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#if 8 % PPPOE_HASH_BITS
13662306a36Sopenharmony_ci#error 8 must be a multiple of PPPOE_HASH_BITS
13762306a36Sopenharmony_ci#endif
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int hash_item(__be16 sid, unsigned char *addr)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	unsigned char hash = 0;
14262306a36Sopenharmony_ci	unsigned int i;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	for (i = 0; i < ETH_ALEN; i++)
14562306a36Sopenharmony_ci		hash ^= addr[i];
14662306a36Sopenharmony_ci	for (i = 0; i < sizeof(sid_t) * 8; i += 8)
14762306a36Sopenharmony_ci		hash ^= (__force __u32)sid >> i;
14862306a36Sopenharmony_ci	for (i = 8; (i >>= 1) >= PPPOE_HASH_BITS;)
14962306a36Sopenharmony_ci		hash ^= hash >> i;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return hash & PPPOE_HASH_MASK;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/**********************************************************************
15562306a36Sopenharmony_ci *
15662306a36Sopenharmony_ci *  Set/get/delete/rehash items  (internal versions)
15762306a36Sopenharmony_ci *
15862306a36Sopenharmony_ci **********************************************************************/
15962306a36Sopenharmony_cistatic struct pppox_sock *__get_item(struct pppoe_net *pn, __be16 sid,
16062306a36Sopenharmony_ci				unsigned char *addr, int ifindex)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	int hash = hash_item(sid, addr);
16362306a36Sopenharmony_ci	struct pppox_sock *ret;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	ret = pn->hash_table[hash];
16662306a36Sopenharmony_ci	while (ret) {
16762306a36Sopenharmony_ci		if (cmp_addr(&ret->pppoe_pa, sid, addr) &&
16862306a36Sopenharmony_ci		    ret->pppoe_ifindex == ifindex)
16962306a36Sopenharmony_ci			return ret;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		ret = ret->next;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return NULL;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic int __set_item(struct pppoe_net *pn, struct pppox_sock *po)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote);
18062306a36Sopenharmony_ci	struct pppox_sock *ret;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	ret = pn->hash_table[hash];
18362306a36Sopenharmony_ci	while (ret) {
18462306a36Sopenharmony_ci		if (cmp_2_addr(&ret->pppoe_pa, &po->pppoe_pa) &&
18562306a36Sopenharmony_ci		    ret->pppoe_ifindex == po->pppoe_ifindex)
18662306a36Sopenharmony_ci			return -EALREADY;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci		ret = ret->next;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	po->next = pn->hash_table[hash];
19262306a36Sopenharmony_ci	pn->hash_table[hash] = po;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void __delete_item(struct pppoe_net *pn, __be16 sid,
19862306a36Sopenharmony_ci					char *addr, int ifindex)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	int hash = hash_item(sid, addr);
20162306a36Sopenharmony_ci	struct pppox_sock *ret, **src;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	ret = pn->hash_table[hash];
20462306a36Sopenharmony_ci	src = &pn->hash_table[hash];
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	while (ret) {
20762306a36Sopenharmony_ci		if (cmp_addr(&ret->pppoe_pa, sid, addr) &&
20862306a36Sopenharmony_ci		    ret->pppoe_ifindex == ifindex) {
20962306a36Sopenharmony_ci			*src = ret->next;
21062306a36Sopenharmony_ci			break;
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		src = &ret->next;
21462306a36Sopenharmony_ci		ret = ret->next;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/**********************************************************************
21962306a36Sopenharmony_ci *
22062306a36Sopenharmony_ci *  Set/get/delete/rehash items
22162306a36Sopenharmony_ci *
22262306a36Sopenharmony_ci **********************************************************************/
22362306a36Sopenharmony_cistatic inline struct pppox_sock *get_item(struct pppoe_net *pn, __be16 sid,
22462306a36Sopenharmony_ci					unsigned char *addr, int ifindex)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct pppox_sock *po;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	read_lock_bh(&pn->hash_lock);
22962306a36Sopenharmony_ci	po = __get_item(pn, sid, addr, ifindex);
23062306a36Sopenharmony_ci	if (po)
23162306a36Sopenharmony_ci		sock_hold(sk_pppox(po));
23262306a36Sopenharmony_ci	read_unlock_bh(&pn->hash_lock);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return po;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic inline struct pppox_sock *get_item_by_addr(struct net *net,
23862306a36Sopenharmony_ci						struct sockaddr_pppox *sp)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct net_device *dev;
24162306a36Sopenharmony_ci	struct pppoe_net *pn;
24262306a36Sopenharmony_ci	struct pppox_sock *pppox_sock = NULL;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	int ifindex;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	rcu_read_lock();
24762306a36Sopenharmony_ci	dev = dev_get_by_name_rcu(net, sp->sa_addr.pppoe.dev);
24862306a36Sopenharmony_ci	if (dev) {
24962306a36Sopenharmony_ci		ifindex = dev->ifindex;
25062306a36Sopenharmony_ci		pn = pppoe_pernet(net);
25162306a36Sopenharmony_ci		pppox_sock = get_item(pn, sp->sa_addr.pppoe.sid,
25262306a36Sopenharmony_ci				sp->sa_addr.pppoe.remote, ifindex);
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci	rcu_read_unlock();
25562306a36Sopenharmony_ci	return pppox_sock;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic inline void delete_item(struct pppoe_net *pn, __be16 sid,
25962306a36Sopenharmony_ci					char *addr, int ifindex)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	write_lock_bh(&pn->hash_lock);
26262306a36Sopenharmony_ci	__delete_item(pn, sid, addr, ifindex);
26362306a36Sopenharmony_ci	write_unlock_bh(&pn->hash_lock);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/***************************************************************************
26762306a36Sopenharmony_ci *
26862306a36Sopenharmony_ci *  Handler for device events.
26962306a36Sopenharmony_ci *  Certain device events require that sockets be unconnected.
27062306a36Sopenharmony_ci *
27162306a36Sopenharmony_ci **************************************************************************/
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void pppoe_flush_dev(struct net_device *dev)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct pppoe_net *pn;
27662306a36Sopenharmony_ci	int i;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	pn = pppoe_pernet(dev_net(dev));
27962306a36Sopenharmony_ci	write_lock_bh(&pn->hash_lock);
28062306a36Sopenharmony_ci	for (i = 0; i < PPPOE_HASH_SIZE; i++) {
28162306a36Sopenharmony_ci		struct pppox_sock *po = pn->hash_table[i];
28262306a36Sopenharmony_ci		struct sock *sk;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		while (po) {
28562306a36Sopenharmony_ci			while (po && po->pppoe_dev != dev) {
28662306a36Sopenharmony_ci				po = po->next;
28762306a36Sopenharmony_ci			}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci			if (!po)
29062306a36Sopenharmony_ci				break;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci			sk = sk_pppox(po);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci			/* We always grab the socket lock, followed by the
29562306a36Sopenharmony_ci			 * hash_lock, in that order.  Since we should hold the
29662306a36Sopenharmony_ci			 * sock lock while doing any unbinding, we need to
29762306a36Sopenharmony_ci			 * release the lock we're holding.  Hold a reference to
29862306a36Sopenharmony_ci			 * the sock so it doesn't disappear as we're jumping
29962306a36Sopenharmony_ci			 * between locks.
30062306a36Sopenharmony_ci			 */
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci			sock_hold(sk);
30362306a36Sopenharmony_ci			write_unlock_bh(&pn->hash_lock);
30462306a36Sopenharmony_ci			lock_sock(sk);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci			if (po->pppoe_dev == dev &&
30762306a36Sopenharmony_ci			    sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
30862306a36Sopenharmony_ci				pppox_unbind_sock(sk);
30962306a36Sopenharmony_ci				sk->sk_state_change(sk);
31062306a36Sopenharmony_ci				po->pppoe_dev = NULL;
31162306a36Sopenharmony_ci				dev_put(dev);
31262306a36Sopenharmony_ci			}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci			release_sock(sk);
31562306a36Sopenharmony_ci			sock_put(sk);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci			/* Restart the process from the start of the current
31862306a36Sopenharmony_ci			 * hash chain. We dropped locks so the world may have
31962306a36Sopenharmony_ci			 * change from underneath us.
32062306a36Sopenharmony_ci			 */
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci			BUG_ON(pppoe_pernet(dev_net(dev)) == NULL);
32362306a36Sopenharmony_ci			write_lock_bh(&pn->hash_lock);
32462306a36Sopenharmony_ci			po = pn->hash_table[i];
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci	write_unlock_bh(&pn->hash_lock);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int pppoe_device_event(struct notifier_block *this,
33162306a36Sopenharmony_ci			      unsigned long event, void *ptr)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	/* Only look at sockets that are using this specific device. */
33662306a36Sopenharmony_ci	switch (event) {
33762306a36Sopenharmony_ci	case NETDEV_CHANGEADDR:
33862306a36Sopenharmony_ci	case NETDEV_CHANGEMTU:
33962306a36Sopenharmony_ci		/* A change in mtu or address is a bad thing, requiring
34062306a36Sopenharmony_ci		 * LCP re-negotiation.
34162306a36Sopenharmony_ci		 */
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	case NETDEV_GOING_DOWN:
34462306a36Sopenharmony_ci	case NETDEV_DOWN:
34562306a36Sopenharmony_ci		/* Find every socket on this device and kill it. */
34662306a36Sopenharmony_ci		pppoe_flush_dev(dev);
34762306a36Sopenharmony_ci		break;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	default:
35062306a36Sopenharmony_ci		break;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return NOTIFY_DONE;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic struct notifier_block pppoe_notifier = {
35762306a36Sopenharmony_ci	.notifier_call = pppoe_device_event,
35862306a36Sopenharmony_ci};
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci/************************************************************************
36162306a36Sopenharmony_ci *
36262306a36Sopenharmony_ci * Do the real work of receiving a PPPoE Session frame.
36362306a36Sopenharmony_ci *
36462306a36Sopenharmony_ci ***********************************************************************/
36562306a36Sopenharmony_cistatic int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
36862306a36Sopenharmony_ci	struct pppox_sock *relay_po;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* Backlog receive. Semantics of backlog rcv preclude any code from
37162306a36Sopenharmony_ci	 * executing in lock_sock()/release_sock() bounds; meaning sk->sk_state
37262306a36Sopenharmony_ci	 * can't change.
37362306a36Sopenharmony_ci	 */
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (skb->pkt_type == PACKET_OTHERHOST)
37662306a36Sopenharmony_ci		goto abort_kfree;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (sk->sk_state & PPPOX_BOUND) {
37962306a36Sopenharmony_ci		ppp_input(&po->chan, skb);
38062306a36Sopenharmony_ci	} else if (sk->sk_state & PPPOX_RELAY) {
38162306a36Sopenharmony_ci		relay_po = get_item_by_addr(sock_net(sk),
38262306a36Sopenharmony_ci					    &po->pppoe_relay);
38362306a36Sopenharmony_ci		if (relay_po == NULL)
38462306a36Sopenharmony_ci			goto abort_kfree;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		if ((sk_pppox(relay_po)->sk_state & PPPOX_CONNECTED) == 0)
38762306a36Sopenharmony_ci			goto abort_put;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		if (!__pppoe_xmit(sk_pppox(relay_po), skb))
39062306a36Sopenharmony_ci			goto abort_put;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		sock_put(sk_pppox(relay_po));
39362306a36Sopenharmony_ci	} else {
39462306a36Sopenharmony_ci		if (sock_queue_rcv_skb(sk, skb))
39562306a36Sopenharmony_ci			goto abort_kfree;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return NET_RX_SUCCESS;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ciabort_put:
40162306a36Sopenharmony_ci	sock_put(sk_pppox(relay_po));
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ciabort_kfree:
40462306a36Sopenharmony_ci	kfree_skb(skb);
40562306a36Sopenharmony_ci	return NET_RX_DROP;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci/************************************************************************
40962306a36Sopenharmony_ci *
41062306a36Sopenharmony_ci * Receive wrapper called in BH context.
41162306a36Sopenharmony_ci *
41262306a36Sopenharmony_ci ***********************************************************************/
41362306a36Sopenharmony_cistatic int pppoe_rcv(struct sk_buff *skb, struct net_device *dev,
41462306a36Sopenharmony_ci		     struct packet_type *pt, struct net_device *orig_dev)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct pppoe_hdr *ph;
41762306a36Sopenharmony_ci	struct pppox_sock *po;
41862306a36Sopenharmony_ci	struct pppoe_net *pn;
41962306a36Sopenharmony_ci	int len;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
42262306a36Sopenharmony_ci	if (!skb)
42362306a36Sopenharmony_ci		goto out;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (skb_mac_header_len(skb) < ETH_HLEN)
42662306a36Sopenharmony_ci		goto drop;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
42962306a36Sopenharmony_ci		goto drop;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	ph = pppoe_hdr(skb);
43262306a36Sopenharmony_ci	len = ntohs(ph->length);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	skb_pull_rcsum(skb, sizeof(*ph));
43562306a36Sopenharmony_ci	if (skb->len < len)
43662306a36Sopenharmony_ci		goto drop;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (pskb_trim_rcsum(skb, len))
43962306a36Sopenharmony_ci		goto drop;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	ph = pppoe_hdr(skb);
44262306a36Sopenharmony_ci	pn = pppoe_pernet(dev_net(dev));
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/* Note that get_item does a sock_hold(), so sk_pppox(po)
44562306a36Sopenharmony_ci	 * is known to be safe.
44662306a36Sopenharmony_ci	 */
44762306a36Sopenharmony_ci	po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
44862306a36Sopenharmony_ci	if (!po)
44962306a36Sopenharmony_ci		goto drop;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return sk_receive_skb(sk_pppox(po), skb, 0);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cidrop:
45462306a36Sopenharmony_ci	kfree_skb(skb);
45562306a36Sopenharmony_ciout:
45662306a36Sopenharmony_ci	return NET_RX_DROP;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic void pppoe_unbind_sock_work(struct work_struct *work)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	struct pppox_sock *po = container_of(work, struct pppox_sock,
46262306a36Sopenharmony_ci					     proto.pppoe.padt_work);
46362306a36Sopenharmony_ci	struct sock *sk = sk_pppox(po);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	lock_sock(sk);
46662306a36Sopenharmony_ci	if (po->pppoe_dev) {
46762306a36Sopenharmony_ci		dev_put(po->pppoe_dev);
46862306a36Sopenharmony_ci		po->pppoe_dev = NULL;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci	pppox_unbind_sock(sk);
47162306a36Sopenharmony_ci	release_sock(sk);
47262306a36Sopenharmony_ci	sock_put(sk);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci/************************************************************************
47662306a36Sopenharmony_ci *
47762306a36Sopenharmony_ci * Receive a PPPoE Discovery frame.
47862306a36Sopenharmony_ci * This is solely for detection of PADT frames
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci ***********************************************************************/
48162306a36Sopenharmony_cistatic int pppoe_disc_rcv(struct sk_buff *skb, struct net_device *dev,
48262306a36Sopenharmony_ci			  struct packet_type *pt, struct net_device *orig_dev)
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct pppoe_hdr *ph;
48662306a36Sopenharmony_ci	struct pppox_sock *po;
48762306a36Sopenharmony_ci	struct pppoe_net *pn;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
49062306a36Sopenharmony_ci	if (!skb)
49162306a36Sopenharmony_ci		goto out;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (skb->pkt_type != PACKET_HOST)
49462306a36Sopenharmony_ci		goto abort;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
49762306a36Sopenharmony_ci		goto abort;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	ph = pppoe_hdr(skb);
50062306a36Sopenharmony_ci	if (ph->code != PADT_CODE)
50162306a36Sopenharmony_ci		goto abort;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	pn = pppoe_pernet(dev_net(dev));
50462306a36Sopenharmony_ci	po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
50562306a36Sopenharmony_ci	if (po)
50662306a36Sopenharmony_ci		if (!schedule_work(&po->proto.pppoe.padt_work))
50762306a36Sopenharmony_ci			sock_put(sk_pppox(po));
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ciabort:
51062306a36Sopenharmony_ci	kfree_skb(skb);
51162306a36Sopenharmony_ciout:
51262306a36Sopenharmony_ci	return NET_RX_SUCCESS; /* Lies... :-) */
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic struct packet_type pppoes_ptype __read_mostly = {
51662306a36Sopenharmony_ci	.type	= cpu_to_be16(ETH_P_PPP_SES),
51762306a36Sopenharmony_ci	.func	= pppoe_rcv,
51862306a36Sopenharmony_ci};
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic struct packet_type pppoed_ptype __read_mostly = {
52162306a36Sopenharmony_ci	.type	= cpu_to_be16(ETH_P_PPP_DISC),
52262306a36Sopenharmony_ci	.func	= pppoe_disc_rcv,
52362306a36Sopenharmony_ci};
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic struct proto pppoe_sk_proto __read_mostly = {
52662306a36Sopenharmony_ci	.name	  = "PPPOE",
52762306a36Sopenharmony_ci	.owner	  = THIS_MODULE,
52862306a36Sopenharmony_ci	.obj_size = sizeof(struct pppox_sock),
52962306a36Sopenharmony_ci};
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci/***********************************************************************
53262306a36Sopenharmony_ci *
53362306a36Sopenharmony_ci * Initialize a new struct sock.
53462306a36Sopenharmony_ci *
53562306a36Sopenharmony_ci **********************************************************************/
53662306a36Sopenharmony_cistatic int pppoe_create(struct net *net, struct socket *sock, int kern)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct sock *sk;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppoe_sk_proto, kern);
54162306a36Sopenharmony_ci	if (!sk)
54262306a36Sopenharmony_ci		return -ENOMEM;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	sock_init_data(sock, sk);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	sock->state	= SS_UNCONNECTED;
54762306a36Sopenharmony_ci	sock->ops	= &pppoe_ops;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	sk->sk_backlog_rcv	= pppoe_rcv_core;
55062306a36Sopenharmony_ci	sk->sk_state		= PPPOX_NONE;
55162306a36Sopenharmony_ci	sk->sk_type		= SOCK_STREAM;
55262306a36Sopenharmony_ci	sk->sk_family		= PF_PPPOX;
55362306a36Sopenharmony_ci	sk->sk_protocol		= PX_PROTO_OE;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	INIT_WORK(&pppox_sk(sk)->proto.pppoe.padt_work,
55662306a36Sopenharmony_ci		  pppoe_unbind_sock_work);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return 0;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic int pppoe_release(struct socket *sock)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	struct sock *sk = sock->sk;
56462306a36Sopenharmony_ci	struct pppox_sock *po;
56562306a36Sopenharmony_ci	struct pppoe_net *pn;
56662306a36Sopenharmony_ci	struct net *net = NULL;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (!sk)
56962306a36Sopenharmony_ci		return 0;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	lock_sock(sk);
57262306a36Sopenharmony_ci	if (sock_flag(sk, SOCK_DEAD)) {
57362306a36Sopenharmony_ci		release_sock(sk);
57462306a36Sopenharmony_ci		return -EBADF;
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	po = pppox_sk(sk);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (po->pppoe_dev) {
58062306a36Sopenharmony_ci		dev_put(po->pppoe_dev);
58162306a36Sopenharmony_ci		po->pppoe_dev = NULL;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	pppox_unbind_sock(sk);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	/* Signal the death of the socket. */
58762306a36Sopenharmony_ci	sk->sk_state = PPPOX_DEAD;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	net = sock_net(sk);
59062306a36Sopenharmony_ci	pn = pppoe_pernet(net);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	/*
59362306a36Sopenharmony_ci	 * protect "po" from concurrent updates
59462306a36Sopenharmony_ci	 * on pppoe_flush_dev
59562306a36Sopenharmony_ci	 */
59662306a36Sopenharmony_ci	delete_item(pn, po->pppoe_pa.sid, po->pppoe_pa.remote,
59762306a36Sopenharmony_ci		    po->pppoe_ifindex);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	sock_orphan(sk);
60062306a36Sopenharmony_ci	sock->sk = NULL;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	skb_queue_purge(&sk->sk_receive_queue);
60362306a36Sopenharmony_ci	release_sock(sk);
60462306a36Sopenharmony_ci	sock_put(sk);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return 0;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
61062306a36Sopenharmony_ci		  int sockaddr_len, int flags)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct sock *sk = sock->sk;
61362306a36Sopenharmony_ci	struct sockaddr_pppox *sp = (struct sockaddr_pppox *)uservaddr;
61462306a36Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
61562306a36Sopenharmony_ci	struct net_device *dev = NULL;
61662306a36Sopenharmony_ci	struct pppoe_net *pn;
61762306a36Sopenharmony_ci	struct net *net = NULL;
61862306a36Sopenharmony_ci	int error;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	lock_sock(sk);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	error = -EINVAL;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (sockaddr_len != sizeof(struct sockaddr_pppox))
62562306a36Sopenharmony_ci		goto end;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if (sp->sa_protocol != PX_PROTO_OE)
62862306a36Sopenharmony_ci		goto end;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	/* Check for already bound sockets */
63162306a36Sopenharmony_ci	error = -EBUSY;
63262306a36Sopenharmony_ci	if ((sk->sk_state & PPPOX_CONNECTED) &&
63362306a36Sopenharmony_ci	     stage_session(sp->sa_addr.pppoe.sid))
63462306a36Sopenharmony_ci		goto end;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/* Check for already disconnected sockets, on attempts to disconnect */
63762306a36Sopenharmony_ci	error = -EALREADY;
63862306a36Sopenharmony_ci	if ((sk->sk_state & PPPOX_DEAD) &&
63962306a36Sopenharmony_ci	     !stage_session(sp->sa_addr.pppoe.sid))
64062306a36Sopenharmony_ci		goto end;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	error = 0;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	/* Delete the old binding */
64562306a36Sopenharmony_ci	if (stage_session(po->pppoe_pa.sid)) {
64662306a36Sopenharmony_ci		pppox_unbind_sock(sk);
64762306a36Sopenharmony_ci		pn = pppoe_pernet(sock_net(sk));
64862306a36Sopenharmony_ci		delete_item(pn, po->pppoe_pa.sid,
64962306a36Sopenharmony_ci			    po->pppoe_pa.remote, po->pppoe_ifindex);
65062306a36Sopenharmony_ci		if (po->pppoe_dev) {
65162306a36Sopenharmony_ci			dev_put(po->pppoe_dev);
65262306a36Sopenharmony_ci			po->pppoe_dev = NULL;
65362306a36Sopenharmony_ci		}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci		po->pppoe_ifindex = 0;
65662306a36Sopenharmony_ci		memset(&po->pppoe_pa, 0, sizeof(po->pppoe_pa));
65762306a36Sopenharmony_ci		memset(&po->pppoe_relay, 0, sizeof(po->pppoe_relay));
65862306a36Sopenharmony_ci		memset(&po->chan, 0, sizeof(po->chan));
65962306a36Sopenharmony_ci		po->next = NULL;
66062306a36Sopenharmony_ci		po->num = 0;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		sk->sk_state = PPPOX_NONE;
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	/* Re-bind in session stage only */
66662306a36Sopenharmony_ci	if (stage_session(sp->sa_addr.pppoe.sid)) {
66762306a36Sopenharmony_ci		error = -ENODEV;
66862306a36Sopenharmony_ci		net = sock_net(sk);
66962306a36Sopenharmony_ci		dev = dev_get_by_name(net, sp->sa_addr.pppoe.dev);
67062306a36Sopenharmony_ci		if (!dev)
67162306a36Sopenharmony_ci			goto err_put;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		po->pppoe_dev = dev;
67462306a36Sopenharmony_ci		po->pppoe_ifindex = dev->ifindex;
67562306a36Sopenharmony_ci		pn = pppoe_pernet(net);
67662306a36Sopenharmony_ci		if (!(dev->flags & IFF_UP)) {
67762306a36Sopenharmony_ci			goto err_put;
67862306a36Sopenharmony_ci		}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci		memcpy(&po->pppoe_pa,
68162306a36Sopenharmony_ci		       &sp->sa_addr.pppoe,
68262306a36Sopenharmony_ci		       sizeof(struct pppoe_addr));
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci		write_lock_bh(&pn->hash_lock);
68562306a36Sopenharmony_ci		error = __set_item(pn, po);
68662306a36Sopenharmony_ci		write_unlock_bh(&pn->hash_lock);
68762306a36Sopenharmony_ci		if (error < 0)
68862306a36Sopenharmony_ci			goto err_put;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci		po->chan.hdrlen = (sizeof(struct pppoe_hdr) +
69162306a36Sopenharmony_ci				   dev->hard_header_len);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci		po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr) - 2;
69462306a36Sopenharmony_ci		po->chan.private = sk;
69562306a36Sopenharmony_ci		po->chan.ops = &pppoe_chan_ops;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci		error = ppp_register_net_channel(dev_net(dev), &po->chan);
69862306a36Sopenharmony_ci		if (error) {
69962306a36Sopenharmony_ci			delete_item(pn, po->pppoe_pa.sid,
70062306a36Sopenharmony_ci				    po->pppoe_pa.remote, po->pppoe_ifindex);
70162306a36Sopenharmony_ci			goto err_put;
70262306a36Sopenharmony_ci		}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		sk->sk_state = PPPOX_CONNECTED;
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	po->num = sp->sa_addr.pppoe.sid;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ciend:
71062306a36Sopenharmony_ci	release_sock(sk);
71162306a36Sopenharmony_ci	return error;
71262306a36Sopenharmony_cierr_put:
71362306a36Sopenharmony_ci	if (po->pppoe_dev) {
71462306a36Sopenharmony_ci		dev_put(po->pppoe_dev);
71562306a36Sopenharmony_ci		po->pppoe_dev = NULL;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci	goto end;
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic int pppoe_getname(struct socket *sock, struct sockaddr *uaddr,
72162306a36Sopenharmony_ci		  int peer)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	int len = sizeof(struct sockaddr_pppox);
72462306a36Sopenharmony_ci	struct sockaddr_pppox sp;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	sp.sa_family	= AF_PPPOX;
72762306a36Sopenharmony_ci	sp.sa_protocol	= PX_PROTO_OE;
72862306a36Sopenharmony_ci	memcpy(&sp.sa_addr.pppoe, &pppox_sk(sock->sk)->pppoe_pa,
72962306a36Sopenharmony_ci	       sizeof(struct pppoe_addr));
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	memcpy(uaddr, &sp, len);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	return len;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic int pppoe_ioctl(struct socket *sock, unsigned int cmd,
73762306a36Sopenharmony_ci		unsigned long arg)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct sock *sk = sock->sk;
74062306a36Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
74162306a36Sopenharmony_ci	int val;
74262306a36Sopenharmony_ci	int err;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	switch (cmd) {
74562306a36Sopenharmony_ci	case PPPIOCGMRU:
74662306a36Sopenharmony_ci		err = -ENXIO;
74762306a36Sopenharmony_ci		if (!(sk->sk_state & PPPOX_CONNECTED))
74862306a36Sopenharmony_ci			break;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci		err = -EFAULT;
75162306a36Sopenharmony_ci		if (put_user(po->pppoe_dev->mtu -
75262306a36Sopenharmony_ci			     sizeof(struct pppoe_hdr) -
75362306a36Sopenharmony_ci			     PPP_HDRLEN,
75462306a36Sopenharmony_ci			     (int __user *)arg))
75562306a36Sopenharmony_ci			break;
75662306a36Sopenharmony_ci		err = 0;
75762306a36Sopenharmony_ci		break;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	case PPPIOCSMRU:
76062306a36Sopenharmony_ci		err = -ENXIO;
76162306a36Sopenharmony_ci		if (!(sk->sk_state & PPPOX_CONNECTED))
76262306a36Sopenharmony_ci			break;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		err = -EFAULT;
76562306a36Sopenharmony_ci		if (get_user(val, (int __user *)arg))
76662306a36Sopenharmony_ci			break;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci		if (val < (po->pppoe_dev->mtu
76962306a36Sopenharmony_ci			   - sizeof(struct pppoe_hdr)
77062306a36Sopenharmony_ci			   - PPP_HDRLEN))
77162306a36Sopenharmony_ci			err = 0;
77262306a36Sopenharmony_ci		else
77362306a36Sopenharmony_ci			err = -EINVAL;
77462306a36Sopenharmony_ci		break;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	case PPPIOCSFLAGS:
77762306a36Sopenharmony_ci		err = -EFAULT;
77862306a36Sopenharmony_ci		if (get_user(val, (int __user *)arg))
77962306a36Sopenharmony_ci			break;
78062306a36Sopenharmony_ci		err = 0;
78162306a36Sopenharmony_ci		break;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	case PPPOEIOCSFWD:
78462306a36Sopenharmony_ci	{
78562306a36Sopenharmony_ci		struct pppox_sock *relay_po;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		err = -EBUSY;
78862306a36Sopenharmony_ci		if (sk->sk_state & (PPPOX_BOUND | PPPOX_DEAD))
78962306a36Sopenharmony_ci			break;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		err = -ENOTCONN;
79262306a36Sopenharmony_ci		if (!(sk->sk_state & PPPOX_CONNECTED))
79362306a36Sopenharmony_ci			break;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		/* PPPoE address from the user specifies an outbound
79662306a36Sopenharmony_ci		   PPPoE address which frames are forwarded to */
79762306a36Sopenharmony_ci		err = -EFAULT;
79862306a36Sopenharmony_ci		if (copy_from_user(&po->pppoe_relay,
79962306a36Sopenharmony_ci				   (void __user *)arg,
80062306a36Sopenharmony_ci				   sizeof(struct sockaddr_pppox)))
80162306a36Sopenharmony_ci			break;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci		err = -EINVAL;
80462306a36Sopenharmony_ci		if (po->pppoe_relay.sa_family != AF_PPPOX ||
80562306a36Sopenharmony_ci		    po->pppoe_relay.sa_protocol != PX_PROTO_OE)
80662306a36Sopenharmony_ci			break;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci		/* Check that the socket referenced by the address
80962306a36Sopenharmony_ci		   actually exists. */
81062306a36Sopenharmony_ci		relay_po = get_item_by_addr(sock_net(sk), &po->pppoe_relay);
81162306a36Sopenharmony_ci		if (!relay_po)
81262306a36Sopenharmony_ci			break;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci		sock_put(sk_pppox(relay_po));
81562306a36Sopenharmony_ci		sk->sk_state |= PPPOX_RELAY;
81662306a36Sopenharmony_ci		err = 0;
81762306a36Sopenharmony_ci		break;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	case PPPOEIOCDFWD:
82162306a36Sopenharmony_ci		err = -EALREADY;
82262306a36Sopenharmony_ci		if (!(sk->sk_state & PPPOX_RELAY))
82362306a36Sopenharmony_ci			break;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci		sk->sk_state &= ~PPPOX_RELAY;
82662306a36Sopenharmony_ci		err = 0;
82762306a36Sopenharmony_ci		break;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	default:
83062306a36Sopenharmony_ci		err = -ENOTTY;
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	return err;
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic int pppoe_sendmsg(struct socket *sock, struct msghdr *m,
83762306a36Sopenharmony_ci			 size_t total_len)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	struct sk_buff *skb;
84062306a36Sopenharmony_ci	struct sock *sk = sock->sk;
84162306a36Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
84262306a36Sopenharmony_ci	int error;
84362306a36Sopenharmony_ci	struct pppoe_hdr hdr;
84462306a36Sopenharmony_ci	struct pppoe_hdr *ph;
84562306a36Sopenharmony_ci	struct net_device *dev;
84662306a36Sopenharmony_ci	char *start;
84762306a36Sopenharmony_ci	int hlen;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	lock_sock(sk);
85062306a36Sopenharmony_ci	if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) {
85162306a36Sopenharmony_ci		error = -ENOTCONN;
85262306a36Sopenharmony_ci		goto end;
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	hdr.ver = 1;
85662306a36Sopenharmony_ci	hdr.type = 1;
85762306a36Sopenharmony_ci	hdr.code = 0;
85862306a36Sopenharmony_ci	hdr.sid = po->num;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	dev = po->pppoe_dev;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	error = -EMSGSIZE;
86362306a36Sopenharmony_ci	if (total_len > (dev->mtu + dev->hard_header_len))
86462306a36Sopenharmony_ci		goto end;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	hlen = LL_RESERVED_SPACE(dev);
86762306a36Sopenharmony_ci	skb = sock_wmalloc(sk, hlen + sizeof(*ph) + total_len +
86862306a36Sopenharmony_ci			   dev->needed_tailroom, 0, GFP_KERNEL);
86962306a36Sopenharmony_ci	if (!skb) {
87062306a36Sopenharmony_ci		error = -ENOMEM;
87162306a36Sopenharmony_ci		goto end;
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	/* Reserve space for headers. */
87562306a36Sopenharmony_ci	skb_reserve(skb, hlen);
87662306a36Sopenharmony_ci	skb_reset_network_header(skb);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	skb->dev = dev;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	skb->priority = sk->sk_priority;
88162306a36Sopenharmony_ci	skb->protocol = cpu_to_be16(ETH_P_PPP_SES);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	ph = skb_put(skb, total_len + sizeof(struct pppoe_hdr));
88462306a36Sopenharmony_ci	start = (char *)&ph->tag[0];
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	error = memcpy_from_msg(start, m, total_len);
88762306a36Sopenharmony_ci	if (error < 0) {
88862306a36Sopenharmony_ci		kfree_skb(skb);
88962306a36Sopenharmony_ci		goto end;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	error = total_len;
89362306a36Sopenharmony_ci	dev_hard_header(skb, dev, ETH_P_PPP_SES,
89462306a36Sopenharmony_ci			po->pppoe_pa.remote, NULL, total_len);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	memcpy(ph, &hdr, sizeof(struct pppoe_hdr));
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	ph->length = htons(total_len);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	dev_queue_xmit(skb);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ciend:
90362306a36Sopenharmony_ci	release_sock(sk);
90462306a36Sopenharmony_ci	return error;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci/************************************************************************
90862306a36Sopenharmony_ci *
90962306a36Sopenharmony_ci * xmit function for internal use.
91062306a36Sopenharmony_ci *
91162306a36Sopenharmony_ci ***********************************************************************/
91262306a36Sopenharmony_cistatic int __pppoe_xmit(struct sock *sk, struct sk_buff *skb)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
91562306a36Sopenharmony_ci	struct net_device *dev = po->pppoe_dev;
91662306a36Sopenharmony_ci	struct pppoe_hdr *ph;
91762306a36Sopenharmony_ci	int data_len = skb->len;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	/* The higher-level PPP code (ppp_unregister_channel()) ensures the PPP
92062306a36Sopenharmony_ci	 * xmit operations conclude prior to an unregistration call.  Thus
92162306a36Sopenharmony_ci	 * sk->sk_state cannot change, so we don't need to do lock_sock().
92262306a36Sopenharmony_ci	 * But, we also can't do a lock_sock since that introduces a potential
92362306a36Sopenharmony_ci	 * deadlock as we'd reverse the lock ordering used when calling
92462306a36Sopenharmony_ci	 * ppp_unregister_channel().
92562306a36Sopenharmony_ci	 */
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
92862306a36Sopenharmony_ci		goto abort;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (!dev)
93162306a36Sopenharmony_ci		goto abort;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	/* Copy the data if there is no space for the header or if it's
93462306a36Sopenharmony_ci	 * read-only.
93562306a36Sopenharmony_ci	 */
93662306a36Sopenharmony_ci	if (skb_cow_head(skb, LL_RESERVED_SPACE(dev) + sizeof(*ph)))
93762306a36Sopenharmony_ci		goto abort;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	__skb_push(skb, sizeof(*ph));
94062306a36Sopenharmony_ci	skb_reset_network_header(skb);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	ph = pppoe_hdr(skb);
94362306a36Sopenharmony_ci	ph->ver	= 1;
94462306a36Sopenharmony_ci	ph->type = 1;
94562306a36Sopenharmony_ci	ph->code = 0;
94662306a36Sopenharmony_ci	ph->sid	= po->num;
94762306a36Sopenharmony_ci	ph->length = htons(data_len);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	skb->protocol = cpu_to_be16(ETH_P_PPP_SES);
95062306a36Sopenharmony_ci	skb->dev = dev;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	dev_hard_header(skb, dev, ETH_P_PPP_SES,
95362306a36Sopenharmony_ci			po->pppoe_pa.remote, NULL, data_len);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	dev_queue_xmit(skb);
95662306a36Sopenharmony_ci	return 1;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ciabort:
95962306a36Sopenharmony_ci	kfree_skb(skb);
96062306a36Sopenharmony_ci	return 1;
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci/************************************************************************
96462306a36Sopenharmony_ci *
96562306a36Sopenharmony_ci * xmit function called by generic PPP driver
96662306a36Sopenharmony_ci * sends PPP frame over PPPoE socket
96762306a36Sopenharmony_ci *
96862306a36Sopenharmony_ci ***********************************************************************/
96962306a36Sopenharmony_cistatic int pppoe_xmit(struct ppp_channel *chan, struct sk_buff *skb)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	struct sock *sk = chan->private;
97262306a36Sopenharmony_ci	return __pppoe_xmit(sk, skb);
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic int pppoe_fill_forward_path(struct net_device_path_ctx *ctx,
97662306a36Sopenharmony_ci				   struct net_device_path *path,
97762306a36Sopenharmony_ci				   const struct ppp_channel *chan)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	struct sock *sk = chan->private;
98062306a36Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
98162306a36Sopenharmony_ci	struct net_device *dev = po->pppoe_dev;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	if (sock_flag(sk, SOCK_DEAD) ||
98462306a36Sopenharmony_ci	    !(sk->sk_state & PPPOX_CONNECTED) || !dev)
98562306a36Sopenharmony_ci		return -1;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	path->type = DEV_PATH_PPPOE;
98862306a36Sopenharmony_ci	path->encap.proto = htons(ETH_P_PPP_SES);
98962306a36Sopenharmony_ci	path->encap.id = be16_to_cpu(po->num);
99062306a36Sopenharmony_ci	memcpy(path->encap.h_dest, po->pppoe_pa.remote, ETH_ALEN);
99162306a36Sopenharmony_ci	memcpy(ctx->daddr, po->pppoe_pa.remote, ETH_ALEN);
99262306a36Sopenharmony_ci	path->dev = ctx->dev;
99362306a36Sopenharmony_ci	ctx->dev = dev;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	return 0;
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cistatic const struct ppp_channel_ops pppoe_chan_ops = {
99962306a36Sopenharmony_ci	.start_xmit = pppoe_xmit,
100062306a36Sopenharmony_ci	.fill_forward_path = pppoe_fill_forward_path,
100162306a36Sopenharmony_ci};
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic int pppoe_recvmsg(struct socket *sock, struct msghdr *m,
100462306a36Sopenharmony_ci			 size_t total_len, int flags)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	struct sock *sk = sock->sk;
100762306a36Sopenharmony_ci	struct sk_buff *skb;
100862306a36Sopenharmony_ci	int error = 0;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (sk->sk_state & PPPOX_BOUND) {
101162306a36Sopenharmony_ci		error = -EIO;
101262306a36Sopenharmony_ci		goto end;
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	skb = skb_recv_datagram(sk, flags, &error);
101662306a36Sopenharmony_ci	if (error < 0)
101762306a36Sopenharmony_ci		goto end;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (skb) {
102062306a36Sopenharmony_ci		total_len = min_t(size_t, total_len, skb->len);
102162306a36Sopenharmony_ci		error = skb_copy_datagram_msg(skb, 0, m, total_len);
102262306a36Sopenharmony_ci		if (error == 0) {
102362306a36Sopenharmony_ci			consume_skb(skb);
102462306a36Sopenharmony_ci			return total_len;
102562306a36Sopenharmony_ci		}
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	kfree_skb(skb);
102962306a36Sopenharmony_ciend:
103062306a36Sopenharmony_ci	return error;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
103462306a36Sopenharmony_cistatic int pppoe_seq_show(struct seq_file *seq, void *v)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	struct pppox_sock *po;
103762306a36Sopenharmony_ci	char *dev_name;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
104062306a36Sopenharmony_ci		seq_puts(seq, "Id       Address              Device\n");
104162306a36Sopenharmony_ci		goto out;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	po = v;
104562306a36Sopenharmony_ci	dev_name = po->pppoe_pa.dev;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	seq_printf(seq, "%08X %pM %8s\n",
104862306a36Sopenharmony_ci		po->pppoe_pa.sid, po->pppoe_pa.remote, dev_name);
104962306a36Sopenharmony_ciout:
105062306a36Sopenharmony_ci	return 0;
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic inline struct pppox_sock *pppoe_get_idx(struct pppoe_net *pn, loff_t pos)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	struct pppox_sock *po;
105662306a36Sopenharmony_ci	int i;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	for (i = 0; i < PPPOE_HASH_SIZE; i++) {
105962306a36Sopenharmony_ci		po = pn->hash_table[i];
106062306a36Sopenharmony_ci		while (po) {
106162306a36Sopenharmony_ci			if (!pos--)
106262306a36Sopenharmony_ci				goto out;
106362306a36Sopenharmony_ci			po = po->next;
106462306a36Sopenharmony_ci		}
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ciout:
106862306a36Sopenharmony_ci	return po;
106962306a36Sopenharmony_ci}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_cistatic void *pppoe_seq_start(struct seq_file *seq, loff_t *pos)
107262306a36Sopenharmony_ci	__acquires(pn->hash_lock)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
107562306a36Sopenharmony_ci	loff_t l = *pos;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	read_lock_bh(&pn->hash_lock);
107862306a36Sopenharmony_ci	return l ? pppoe_get_idx(pn, --l) : SEQ_START_TOKEN;
107962306a36Sopenharmony_ci}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_cistatic void *pppoe_seq_next(struct seq_file *seq, void *v, loff_t *pos)
108262306a36Sopenharmony_ci{
108362306a36Sopenharmony_ci	struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
108462306a36Sopenharmony_ci	struct pppox_sock *po;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	++*pos;
108762306a36Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
108862306a36Sopenharmony_ci		po = pppoe_get_idx(pn, 0);
108962306a36Sopenharmony_ci		goto out;
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci	po = v;
109262306a36Sopenharmony_ci	if (po->next)
109362306a36Sopenharmony_ci		po = po->next;
109462306a36Sopenharmony_ci	else {
109562306a36Sopenharmony_ci		int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci		po = NULL;
109862306a36Sopenharmony_ci		while (++hash < PPPOE_HASH_SIZE) {
109962306a36Sopenharmony_ci			po = pn->hash_table[hash];
110062306a36Sopenharmony_ci			if (po)
110162306a36Sopenharmony_ci				break;
110262306a36Sopenharmony_ci		}
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ciout:
110662306a36Sopenharmony_ci	return po;
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic void pppoe_seq_stop(struct seq_file *seq, void *v)
111062306a36Sopenharmony_ci	__releases(pn->hash_lock)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
111362306a36Sopenharmony_ci	read_unlock_bh(&pn->hash_lock);
111462306a36Sopenharmony_ci}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_cistatic const struct seq_operations pppoe_seq_ops = {
111762306a36Sopenharmony_ci	.start		= pppoe_seq_start,
111862306a36Sopenharmony_ci	.next		= pppoe_seq_next,
111962306a36Sopenharmony_ci	.stop		= pppoe_seq_stop,
112062306a36Sopenharmony_ci	.show		= pppoe_seq_show,
112162306a36Sopenharmony_ci};
112262306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_cistatic const struct proto_ops pppoe_ops = {
112562306a36Sopenharmony_ci	.family		= AF_PPPOX,
112662306a36Sopenharmony_ci	.owner		= THIS_MODULE,
112762306a36Sopenharmony_ci	.release	= pppoe_release,
112862306a36Sopenharmony_ci	.bind		= sock_no_bind,
112962306a36Sopenharmony_ci	.connect	= pppoe_connect,
113062306a36Sopenharmony_ci	.socketpair	= sock_no_socketpair,
113162306a36Sopenharmony_ci	.accept		= sock_no_accept,
113262306a36Sopenharmony_ci	.getname	= pppoe_getname,
113362306a36Sopenharmony_ci	.poll		= datagram_poll,
113462306a36Sopenharmony_ci	.listen		= sock_no_listen,
113562306a36Sopenharmony_ci	.shutdown	= sock_no_shutdown,
113662306a36Sopenharmony_ci	.sendmsg	= pppoe_sendmsg,
113762306a36Sopenharmony_ci	.recvmsg	= pppoe_recvmsg,
113862306a36Sopenharmony_ci	.mmap		= sock_no_mmap,
113962306a36Sopenharmony_ci	.ioctl		= pppox_ioctl,
114062306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
114162306a36Sopenharmony_ci	.compat_ioctl	= pppox_compat_ioctl,
114262306a36Sopenharmony_ci#endif
114362306a36Sopenharmony_ci};
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_cistatic const struct pppox_proto pppoe_proto = {
114662306a36Sopenharmony_ci	.create	= pppoe_create,
114762306a36Sopenharmony_ci	.ioctl	= pppoe_ioctl,
114862306a36Sopenharmony_ci	.owner	= THIS_MODULE,
114962306a36Sopenharmony_ci};
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_cistatic __net_init int pppoe_init_net(struct net *net)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	struct pppoe_net *pn = pppoe_pernet(net);
115462306a36Sopenharmony_ci	struct proc_dir_entry *pde;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	rwlock_init(&pn->hash_lock);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	pde = proc_create_net("pppoe", 0444, net->proc_net,
115962306a36Sopenharmony_ci			&pppoe_seq_ops, sizeof(struct seq_net_private));
116062306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
116162306a36Sopenharmony_ci	if (!pde)
116262306a36Sopenharmony_ci		return -ENOMEM;
116362306a36Sopenharmony_ci#endif
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	return 0;
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_cistatic __net_exit void pppoe_exit_net(struct net *net)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	remove_proc_entry("pppoe", net->proc_net);
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_cistatic struct pernet_operations pppoe_net_ops = {
117462306a36Sopenharmony_ci	.init = pppoe_init_net,
117562306a36Sopenharmony_ci	.exit = pppoe_exit_net,
117662306a36Sopenharmony_ci	.id   = &pppoe_net_id,
117762306a36Sopenharmony_ci	.size = sizeof(struct pppoe_net),
117862306a36Sopenharmony_ci};
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_cistatic int __init pppoe_init(void)
118162306a36Sopenharmony_ci{
118262306a36Sopenharmony_ci	int err;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	err = register_pernet_device(&pppoe_net_ops);
118562306a36Sopenharmony_ci	if (err)
118662306a36Sopenharmony_ci		goto out;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	err = proto_register(&pppoe_sk_proto, 0);
118962306a36Sopenharmony_ci	if (err)
119062306a36Sopenharmony_ci		goto out_unregister_net_ops;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	err = register_pppox_proto(PX_PROTO_OE, &pppoe_proto);
119362306a36Sopenharmony_ci	if (err)
119462306a36Sopenharmony_ci		goto out_unregister_pppoe_proto;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	dev_add_pack(&pppoes_ptype);
119762306a36Sopenharmony_ci	dev_add_pack(&pppoed_ptype);
119862306a36Sopenharmony_ci	register_netdevice_notifier(&pppoe_notifier);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	return 0;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ciout_unregister_pppoe_proto:
120362306a36Sopenharmony_ci	proto_unregister(&pppoe_sk_proto);
120462306a36Sopenharmony_ciout_unregister_net_ops:
120562306a36Sopenharmony_ci	unregister_pernet_device(&pppoe_net_ops);
120662306a36Sopenharmony_ciout:
120762306a36Sopenharmony_ci	return err;
120862306a36Sopenharmony_ci}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cistatic void __exit pppoe_exit(void)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	unregister_netdevice_notifier(&pppoe_notifier);
121362306a36Sopenharmony_ci	dev_remove_pack(&pppoed_ptype);
121462306a36Sopenharmony_ci	dev_remove_pack(&pppoes_ptype);
121562306a36Sopenharmony_ci	unregister_pppox_proto(PX_PROTO_OE);
121662306a36Sopenharmony_ci	proto_unregister(&pppoe_sk_proto);
121762306a36Sopenharmony_ci	unregister_pernet_device(&pppoe_net_ops);
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cimodule_init(pppoe_init);
122162306a36Sopenharmony_cimodule_exit(pppoe_exit);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ciMODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
122462306a36Sopenharmony_ciMODULE_DESCRIPTION("PPP over Ethernet driver");
122562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
122662306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_OE);
1227