18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/** -*- linux-c -*- ***********************************************************
38c2ecf20Sopenharmony_ci * Linux PPP over 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.7.0
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * 070228 :	Fix to allow multiple sessions with same remote MAC and same
118c2ecf20Sopenharmony_ci *		session id by including the local device ifindex in the
128c2ecf20Sopenharmony_ci *		tuple identifying a session. This also ensures packets can't
138c2ecf20Sopenharmony_ci *		be injected into a session from interfaces other than the one
148c2ecf20Sopenharmony_ci *		specified by userspace. Florian Zumbiehl <florz@florz.de>
158c2ecf20Sopenharmony_ci *		(Oh, BTW, this one is YYMMDD, in case you were wondering ...)
168c2ecf20Sopenharmony_ci * 220102 :	Fix module use count on failure in pppoe_create, pppox_sk -acme
178c2ecf20Sopenharmony_ci * 030700 :	Fixed connect logic to allow for disconnect.
188c2ecf20Sopenharmony_ci * 270700 :	Fixed potential SMP problems; we must protect against
198c2ecf20Sopenharmony_ci *		simultaneous invocation of ppp_input
208c2ecf20Sopenharmony_ci *		and ppp_unregister_channel.
218c2ecf20Sopenharmony_ci * 040800 :	Respect reference count mechanisms on net-devices.
228c2ecf20Sopenharmony_ci * 200800 :	fix kfree(skb) in pppoe_rcv (acme)
238c2ecf20Sopenharmony_ci *		Module reference count is decremented in the right spot now,
248c2ecf20Sopenharmony_ci *		guards against sock_put not actually freeing the sk
258c2ecf20Sopenharmony_ci *		in pppoe_release.
268c2ecf20Sopenharmony_ci * 051000 :	Initialization cleanup.
278c2ecf20Sopenharmony_ci * 111100 :	Fix recvmsg.
288c2ecf20Sopenharmony_ci * 050101 :	Fix PADT procesing.
298c2ecf20Sopenharmony_ci * 140501 :	Use pppoe_rcv_core to handle all backlog. (Alexey)
308c2ecf20Sopenharmony_ci * 170701 :	Do not lock_sock with rwlock held. (DaveM)
318c2ecf20Sopenharmony_ci *		Ignore discovery frames if user has socket
328c2ecf20Sopenharmony_ci *		locked. (DaveM)
338c2ecf20Sopenharmony_ci *		Ignore return value of dev_queue_xmit in __pppoe_xmit
348c2ecf20Sopenharmony_ci *		or else we may kfree an SKB twice. (DaveM)
358c2ecf20Sopenharmony_ci * 190701 :	When doing copies of skb's in __pppoe_xmit, always delete
368c2ecf20Sopenharmony_ci *		the original skb that was passed in on success, never on
378c2ecf20Sopenharmony_ci *		failure.  Delete the copy of the skb on failure to avoid
388c2ecf20Sopenharmony_ci *		a memory leak.
398c2ecf20Sopenharmony_ci * 081001 :	Misc. cleanup (licence string, non-blocking, prevent
408c2ecf20Sopenharmony_ci *		reference of device on close).
418c2ecf20Sopenharmony_ci * 121301 :	New ppp channels interface; cannot unregister a channel
428c2ecf20Sopenharmony_ci *		from interrupts.  Thus, we mark the socket as a ZOMBIE
438c2ecf20Sopenharmony_ci *		and do the unregistration later.
448c2ecf20Sopenharmony_ci * 081002 :	seq_file support for proc stuff -acme
458c2ecf20Sopenharmony_ci * 111602 :	Merge all 2.4 fixes into 2.5/2.6 tree.  Label 2.5/2.6
468c2ecf20Sopenharmony_ci *		as version 0.7.  Spacing cleanup.
478c2ecf20Sopenharmony_ci * Author:	Michal Ostrowski <mostrows@speakeasy.net>
488c2ecf20Sopenharmony_ci * Contributors:
498c2ecf20Sopenharmony_ci * 		Arnaldo Carvalho de Melo <acme@conectiva.com.br>
508c2ecf20Sopenharmony_ci *		David S. Miller (davem@redhat.com)
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci * License:
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#include <linux/string.h>
568c2ecf20Sopenharmony_ci#include <linux/module.h>
578c2ecf20Sopenharmony_ci#include <linux/kernel.h>
588c2ecf20Sopenharmony_ci#include <linux/slab.h>
598c2ecf20Sopenharmony_ci#include <linux/errno.h>
608c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
618c2ecf20Sopenharmony_ci#include <linux/net.h>
628c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
638c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
648c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
658c2ecf20Sopenharmony_ci#include <linux/init.h>
668c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
678c2ecf20Sopenharmony_ci#include <linux/if_pppox.h>
688c2ecf20Sopenharmony_ci#include <linux/ppp_channel.h>
698c2ecf20Sopenharmony_ci#include <linux/ppp_defs.h>
708c2ecf20Sopenharmony_ci#include <linux/ppp-ioctl.h>
718c2ecf20Sopenharmony_ci#include <linux/notifier.h>
728c2ecf20Sopenharmony_ci#include <linux/file.h>
738c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
748c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#include <linux/nsproxy.h>
778c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
788c2ecf20Sopenharmony_ci#include <net/netns/generic.h>
798c2ecf20Sopenharmony_ci#include <net/sock.h>
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define PPPOE_HASH_BITS 4
848c2ecf20Sopenharmony_ci#define PPPOE_HASH_SIZE (1 << PPPOE_HASH_BITS)
858c2ecf20Sopenharmony_ci#define PPPOE_HASH_MASK	(PPPOE_HASH_SIZE - 1)
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int __pppoe_xmit(struct sock *sk, struct sk_buff *skb);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic const struct proto_ops pppoe_ops;
908c2ecf20Sopenharmony_cistatic const struct ppp_channel_ops pppoe_chan_ops;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* per-net private data for this module */
938c2ecf20Sopenharmony_cistatic unsigned int pppoe_net_id __read_mostly;
948c2ecf20Sopenharmony_cistruct pppoe_net {
958c2ecf20Sopenharmony_ci	/*
968c2ecf20Sopenharmony_ci	 * we could use _single_ hash table for all
978c2ecf20Sopenharmony_ci	 * nets by injecting net id into the hash but
988c2ecf20Sopenharmony_ci	 * it would increase hash chains and add
998c2ecf20Sopenharmony_ci	 * a few additional math comparations messy
1008c2ecf20Sopenharmony_ci	 * as well, moreover in case of SMP less locking
1018c2ecf20Sopenharmony_ci	 * controversy here
1028c2ecf20Sopenharmony_ci	 */
1038c2ecf20Sopenharmony_ci	struct pppox_sock *hash_table[PPPOE_HASH_SIZE];
1048c2ecf20Sopenharmony_ci	rwlock_t hash_lock;
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/*
1088c2ecf20Sopenharmony_ci * PPPoE could be in the following stages:
1098c2ecf20Sopenharmony_ci * 1) Discovery stage (to obtain remote MAC and Session ID)
1108c2ecf20Sopenharmony_ci * 2) Session stage (MAC and SID are known)
1118c2ecf20Sopenharmony_ci *
1128c2ecf20Sopenharmony_ci * Ethernet frames have a special tag for this but
1138c2ecf20Sopenharmony_ci * we use simpler approach based on session id
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistatic inline bool stage_session(__be16 sid)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	return sid != 0;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic inline struct pppoe_net *pppoe_pernet(struct net *net)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	return net_generic(net, pppoe_net_id);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic inline int cmp_2_addr(struct pppoe_addr *a, struct pppoe_addr *b)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	return a->sid == b->sid && ether_addr_equal(a->remote, b->remote);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic inline int cmp_addr(struct pppoe_addr *a, __be16 sid, char *addr)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	return a->sid == sid && ether_addr_equal(a->remote, addr);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#if 8 % PPPOE_HASH_BITS
1368c2ecf20Sopenharmony_ci#error 8 must be a multiple of PPPOE_HASH_BITS
1378c2ecf20Sopenharmony_ci#endif
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int hash_item(__be16 sid, unsigned char *addr)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	unsigned char hash = 0;
1428c2ecf20Sopenharmony_ci	unsigned int i;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	for (i = 0; i < ETH_ALEN; i++)
1458c2ecf20Sopenharmony_ci		hash ^= addr[i];
1468c2ecf20Sopenharmony_ci	for (i = 0; i < sizeof(sid_t) * 8; i += 8)
1478c2ecf20Sopenharmony_ci		hash ^= (__force __u32)sid >> i;
1488c2ecf20Sopenharmony_ci	for (i = 8; (i >>= 1) >= PPPOE_HASH_BITS;)
1498c2ecf20Sopenharmony_ci		hash ^= hash >> i;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return hash & PPPOE_HASH_MASK;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/**********************************************************************
1558c2ecf20Sopenharmony_ci *
1568c2ecf20Sopenharmony_ci *  Set/get/delete/rehash items  (internal versions)
1578c2ecf20Sopenharmony_ci *
1588c2ecf20Sopenharmony_ci **********************************************************************/
1598c2ecf20Sopenharmony_cistatic struct pppox_sock *__get_item(struct pppoe_net *pn, __be16 sid,
1608c2ecf20Sopenharmony_ci				unsigned char *addr, int ifindex)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	int hash = hash_item(sid, addr);
1638c2ecf20Sopenharmony_ci	struct pppox_sock *ret;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	ret = pn->hash_table[hash];
1668c2ecf20Sopenharmony_ci	while (ret) {
1678c2ecf20Sopenharmony_ci		if (cmp_addr(&ret->pppoe_pa, sid, addr) &&
1688c2ecf20Sopenharmony_ci		    ret->pppoe_ifindex == ifindex)
1698c2ecf20Sopenharmony_ci			return ret;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		ret = ret->next;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return NULL;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int __set_item(struct pppoe_net *pn, struct pppox_sock *po)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote);
1808c2ecf20Sopenharmony_ci	struct pppox_sock *ret;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	ret = pn->hash_table[hash];
1838c2ecf20Sopenharmony_ci	while (ret) {
1848c2ecf20Sopenharmony_ci		if (cmp_2_addr(&ret->pppoe_pa, &po->pppoe_pa) &&
1858c2ecf20Sopenharmony_ci		    ret->pppoe_ifindex == po->pppoe_ifindex)
1868c2ecf20Sopenharmony_ci			return -EALREADY;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci		ret = ret->next;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	po->next = pn->hash_table[hash];
1928c2ecf20Sopenharmony_ci	pn->hash_table[hash] = po;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic void __delete_item(struct pppoe_net *pn, __be16 sid,
1988c2ecf20Sopenharmony_ci					char *addr, int ifindex)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	int hash = hash_item(sid, addr);
2018c2ecf20Sopenharmony_ci	struct pppox_sock *ret, **src;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	ret = pn->hash_table[hash];
2048c2ecf20Sopenharmony_ci	src = &pn->hash_table[hash];
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	while (ret) {
2078c2ecf20Sopenharmony_ci		if (cmp_addr(&ret->pppoe_pa, sid, addr) &&
2088c2ecf20Sopenharmony_ci		    ret->pppoe_ifindex == ifindex) {
2098c2ecf20Sopenharmony_ci			*src = ret->next;
2108c2ecf20Sopenharmony_ci			break;
2118c2ecf20Sopenharmony_ci		}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci		src = &ret->next;
2148c2ecf20Sopenharmony_ci		ret = ret->next;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/**********************************************************************
2198c2ecf20Sopenharmony_ci *
2208c2ecf20Sopenharmony_ci *  Set/get/delete/rehash items
2218c2ecf20Sopenharmony_ci *
2228c2ecf20Sopenharmony_ci **********************************************************************/
2238c2ecf20Sopenharmony_cistatic inline struct pppox_sock *get_item(struct pppoe_net *pn, __be16 sid,
2248c2ecf20Sopenharmony_ci					unsigned char *addr, int ifindex)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct pppox_sock *po;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	read_lock_bh(&pn->hash_lock);
2298c2ecf20Sopenharmony_ci	po = __get_item(pn, sid, addr, ifindex);
2308c2ecf20Sopenharmony_ci	if (po)
2318c2ecf20Sopenharmony_ci		sock_hold(sk_pppox(po));
2328c2ecf20Sopenharmony_ci	read_unlock_bh(&pn->hash_lock);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return po;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic inline struct pppox_sock *get_item_by_addr(struct net *net,
2388c2ecf20Sopenharmony_ci						struct sockaddr_pppox *sp)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct net_device *dev;
2418c2ecf20Sopenharmony_ci	struct pppoe_net *pn;
2428c2ecf20Sopenharmony_ci	struct pppox_sock *pppox_sock = NULL;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	int ifindex;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	rcu_read_lock();
2478c2ecf20Sopenharmony_ci	dev = dev_get_by_name_rcu(net, sp->sa_addr.pppoe.dev);
2488c2ecf20Sopenharmony_ci	if (dev) {
2498c2ecf20Sopenharmony_ci		ifindex = dev->ifindex;
2508c2ecf20Sopenharmony_ci		pn = pppoe_pernet(net);
2518c2ecf20Sopenharmony_ci		pppox_sock = get_item(pn, sp->sa_addr.pppoe.sid,
2528c2ecf20Sopenharmony_ci				sp->sa_addr.pppoe.remote, ifindex);
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci	rcu_read_unlock();
2558c2ecf20Sopenharmony_ci	return pppox_sock;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic inline void delete_item(struct pppoe_net *pn, __be16 sid,
2598c2ecf20Sopenharmony_ci					char *addr, int ifindex)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	write_lock_bh(&pn->hash_lock);
2628c2ecf20Sopenharmony_ci	__delete_item(pn, sid, addr, ifindex);
2638c2ecf20Sopenharmony_ci	write_unlock_bh(&pn->hash_lock);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci/***************************************************************************
2678c2ecf20Sopenharmony_ci *
2688c2ecf20Sopenharmony_ci *  Handler for device events.
2698c2ecf20Sopenharmony_ci *  Certain device events require that sockets be unconnected.
2708c2ecf20Sopenharmony_ci *
2718c2ecf20Sopenharmony_ci **************************************************************************/
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void pppoe_flush_dev(struct net_device *dev)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	struct pppoe_net *pn;
2768c2ecf20Sopenharmony_ci	int i;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	pn = pppoe_pernet(dev_net(dev));
2798c2ecf20Sopenharmony_ci	write_lock_bh(&pn->hash_lock);
2808c2ecf20Sopenharmony_ci	for (i = 0; i < PPPOE_HASH_SIZE; i++) {
2818c2ecf20Sopenharmony_ci		struct pppox_sock *po = pn->hash_table[i];
2828c2ecf20Sopenharmony_ci		struct sock *sk;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		while (po) {
2858c2ecf20Sopenharmony_ci			while (po && po->pppoe_dev != dev) {
2868c2ecf20Sopenharmony_ci				po = po->next;
2878c2ecf20Sopenharmony_ci			}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci			if (!po)
2908c2ecf20Sopenharmony_ci				break;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci			sk = sk_pppox(po);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci			/* We always grab the socket lock, followed by the
2958c2ecf20Sopenharmony_ci			 * hash_lock, in that order.  Since we should hold the
2968c2ecf20Sopenharmony_ci			 * sock lock while doing any unbinding, we need to
2978c2ecf20Sopenharmony_ci			 * release the lock we're holding.  Hold a reference to
2988c2ecf20Sopenharmony_ci			 * the sock so it doesn't disappear as we're jumping
2998c2ecf20Sopenharmony_ci			 * between locks.
3008c2ecf20Sopenharmony_ci			 */
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci			sock_hold(sk);
3038c2ecf20Sopenharmony_ci			write_unlock_bh(&pn->hash_lock);
3048c2ecf20Sopenharmony_ci			lock_sock(sk);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci			if (po->pppoe_dev == dev &&
3078c2ecf20Sopenharmony_ci			    sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
3088c2ecf20Sopenharmony_ci				pppox_unbind_sock(sk);
3098c2ecf20Sopenharmony_ci				sk->sk_state_change(sk);
3108c2ecf20Sopenharmony_ci				po->pppoe_dev = NULL;
3118c2ecf20Sopenharmony_ci				dev_put(dev);
3128c2ecf20Sopenharmony_ci			}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci			release_sock(sk);
3158c2ecf20Sopenharmony_ci			sock_put(sk);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci			/* Restart the process from the start of the current
3188c2ecf20Sopenharmony_ci			 * hash chain. We dropped locks so the world may have
3198c2ecf20Sopenharmony_ci			 * change from underneath us.
3208c2ecf20Sopenharmony_ci			 */
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci			BUG_ON(pppoe_pernet(dev_net(dev)) == NULL);
3238c2ecf20Sopenharmony_ci			write_lock_bh(&pn->hash_lock);
3248c2ecf20Sopenharmony_ci			po = pn->hash_table[i];
3258c2ecf20Sopenharmony_ci		}
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci	write_unlock_bh(&pn->hash_lock);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int pppoe_device_event(struct notifier_block *this,
3318c2ecf20Sopenharmony_ci			      unsigned long event, void *ptr)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* Only look at sockets that are using this specific device. */
3368c2ecf20Sopenharmony_ci	switch (event) {
3378c2ecf20Sopenharmony_ci	case NETDEV_CHANGEADDR:
3388c2ecf20Sopenharmony_ci	case NETDEV_CHANGEMTU:
3398c2ecf20Sopenharmony_ci		/* A change in mtu or address is a bad thing, requiring
3408c2ecf20Sopenharmony_ci		 * LCP re-negotiation.
3418c2ecf20Sopenharmony_ci		 */
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	case NETDEV_GOING_DOWN:
3448c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
3458c2ecf20Sopenharmony_ci		/* Find every socket on this device and kill it. */
3468c2ecf20Sopenharmony_ci		pppoe_flush_dev(dev);
3478c2ecf20Sopenharmony_ci		break;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	default:
3508c2ecf20Sopenharmony_ci		break;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic struct notifier_block pppoe_notifier = {
3578c2ecf20Sopenharmony_ci	.notifier_call = pppoe_device_event,
3588c2ecf20Sopenharmony_ci};
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci/************************************************************************
3618c2ecf20Sopenharmony_ci *
3628c2ecf20Sopenharmony_ci * Do the real work of receiving a PPPoE Session frame.
3638c2ecf20Sopenharmony_ci *
3648c2ecf20Sopenharmony_ci ***********************************************************************/
3658c2ecf20Sopenharmony_cistatic int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
3688c2ecf20Sopenharmony_ci	struct pppox_sock *relay_po;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	/* Backlog receive. Semantics of backlog rcv preclude any code from
3718c2ecf20Sopenharmony_ci	 * executing in lock_sock()/release_sock() bounds; meaning sk->sk_state
3728c2ecf20Sopenharmony_ci	 * can't change.
3738c2ecf20Sopenharmony_ci	 */
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (skb->pkt_type == PACKET_OTHERHOST)
3768c2ecf20Sopenharmony_ci		goto abort_kfree;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (sk->sk_state & PPPOX_BOUND) {
3798c2ecf20Sopenharmony_ci		ppp_input(&po->chan, skb);
3808c2ecf20Sopenharmony_ci	} else if (sk->sk_state & PPPOX_RELAY) {
3818c2ecf20Sopenharmony_ci		relay_po = get_item_by_addr(sock_net(sk),
3828c2ecf20Sopenharmony_ci					    &po->pppoe_relay);
3838c2ecf20Sopenharmony_ci		if (relay_po == NULL)
3848c2ecf20Sopenharmony_ci			goto abort_kfree;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		if ((sk_pppox(relay_po)->sk_state & PPPOX_CONNECTED) == 0)
3878c2ecf20Sopenharmony_ci			goto abort_put;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		if (!__pppoe_xmit(sk_pppox(relay_po), skb))
3908c2ecf20Sopenharmony_ci			goto abort_put;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		sock_put(sk_pppox(relay_po));
3938c2ecf20Sopenharmony_ci	} else {
3948c2ecf20Sopenharmony_ci		if (sock_queue_rcv_skb(sk, skb))
3958c2ecf20Sopenharmony_ci			goto abort_kfree;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return NET_RX_SUCCESS;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ciabort_put:
4018c2ecf20Sopenharmony_ci	sock_put(sk_pppox(relay_po));
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ciabort_kfree:
4048c2ecf20Sopenharmony_ci	kfree_skb(skb);
4058c2ecf20Sopenharmony_ci	return NET_RX_DROP;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci/************************************************************************
4098c2ecf20Sopenharmony_ci *
4108c2ecf20Sopenharmony_ci * Receive wrapper called in BH context.
4118c2ecf20Sopenharmony_ci *
4128c2ecf20Sopenharmony_ci ***********************************************************************/
4138c2ecf20Sopenharmony_cistatic int pppoe_rcv(struct sk_buff *skb, struct net_device *dev,
4148c2ecf20Sopenharmony_ci		     struct packet_type *pt, struct net_device *orig_dev)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct pppoe_hdr *ph;
4178c2ecf20Sopenharmony_ci	struct pppox_sock *po;
4188c2ecf20Sopenharmony_ci	struct pppoe_net *pn;
4198c2ecf20Sopenharmony_ci	int len;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
4228c2ecf20Sopenharmony_ci	if (!skb)
4238c2ecf20Sopenharmony_ci		goto out;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (skb_mac_header_len(skb) < ETH_HLEN)
4268c2ecf20Sopenharmony_ci		goto drop;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
4298c2ecf20Sopenharmony_ci		goto drop;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	ph = pppoe_hdr(skb);
4328c2ecf20Sopenharmony_ci	len = ntohs(ph->length);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	skb_pull_rcsum(skb, sizeof(*ph));
4358c2ecf20Sopenharmony_ci	if (skb->len < len)
4368c2ecf20Sopenharmony_ci		goto drop;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (pskb_trim_rcsum(skb, len))
4398c2ecf20Sopenharmony_ci		goto drop;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	ph = pppoe_hdr(skb);
4428c2ecf20Sopenharmony_ci	pn = pppoe_pernet(dev_net(dev));
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	/* Note that get_item does a sock_hold(), so sk_pppox(po)
4458c2ecf20Sopenharmony_ci	 * is known to be safe.
4468c2ecf20Sopenharmony_ci	 */
4478c2ecf20Sopenharmony_ci	po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
4488c2ecf20Sopenharmony_ci	if (!po)
4498c2ecf20Sopenharmony_ci		goto drop;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	return sk_receive_skb(sk_pppox(po), skb, 0);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cidrop:
4548c2ecf20Sopenharmony_ci	kfree_skb(skb);
4558c2ecf20Sopenharmony_ciout:
4568c2ecf20Sopenharmony_ci	return NET_RX_DROP;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic void pppoe_unbind_sock_work(struct work_struct *work)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct pppox_sock *po = container_of(work, struct pppox_sock,
4628c2ecf20Sopenharmony_ci					     proto.pppoe.padt_work);
4638c2ecf20Sopenharmony_ci	struct sock *sk = sk_pppox(po);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	lock_sock(sk);
4668c2ecf20Sopenharmony_ci	if (po->pppoe_dev) {
4678c2ecf20Sopenharmony_ci		dev_put(po->pppoe_dev);
4688c2ecf20Sopenharmony_ci		po->pppoe_dev = NULL;
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci	pppox_unbind_sock(sk);
4718c2ecf20Sopenharmony_ci	release_sock(sk);
4728c2ecf20Sopenharmony_ci	sock_put(sk);
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci/************************************************************************
4768c2ecf20Sopenharmony_ci *
4778c2ecf20Sopenharmony_ci * Receive a PPPoE Discovery frame.
4788c2ecf20Sopenharmony_ci * This is solely for detection of PADT frames
4798c2ecf20Sopenharmony_ci *
4808c2ecf20Sopenharmony_ci ***********************************************************************/
4818c2ecf20Sopenharmony_cistatic int pppoe_disc_rcv(struct sk_buff *skb, struct net_device *dev,
4828c2ecf20Sopenharmony_ci			  struct packet_type *pt, struct net_device *orig_dev)
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	struct pppoe_hdr *ph;
4868c2ecf20Sopenharmony_ci	struct pppox_sock *po;
4878c2ecf20Sopenharmony_ci	struct pppoe_net *pn;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
4908c2ecf20Sopenharmony_ci	if (!skb)
4918c2ecf20Sopenharmony_ci		goto out;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	if (skb->pkt_type != PACKET_HOST)
4948c2ecf20Sopenharmony_ci		goto abort;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
4978c2ecf20Sopenharmony_ci		goto abort;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	ph = pppoe_hdr(skb);
5008c2ecf20Sopenharmony_ci	if (ph->code != PADT_CODE)
5018c2ecf20Sopenharmony_ci		goto abort;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	pn = pppoe_pernet(dev_net(dev));
5048c2ecf20Sopenharmony_ci	po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
5058c2ecf20Sopenharmony_ci	if (po)
5068c2ecf20Sopenharmony_ci		if (!schedule_work(&po->proto.pppoe.padt_work))
5078c2ecf20Sopenharmony_ci			sock_put(sk_pppox(po));
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ciabort:
5108c2ecf20Sopenharmony_ci	kfree_skb(skb);
5118c2ecf20Sopenharmony_ciout:
5128c2ecf20Sopenharmony_ci	return NET_RX_SUCCESS; /* Lies... :-) */
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistatic struct packet_type pppoes_ptype __read_mostly = {
5168c2ecf20Sopenharmony_ci	.type	= cpu_to_be16(ETH_P_PPP_SES),
5178c2ecf20Sopenharmony_ci	.func	= pppoe_rcv,
5188c2ecf20Sopenharmony_ci};
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic struct packet_type pppoed_ptype __read_mostly = {
5218c2ecf20Sopenharmony_ci	.type	= cpu_to_be16(ETH_P_PPP_DISC),
5228c2ecf20Sopenharmony_ci	.func	= pppoe_disc_rcv,
5238c2ecf20Sopenharmony_ci};
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic struct proto pppoe_sk_proto __read_mostly = {
5268c2ecf20Sopenharmony_ci	.name	  = "PPPOE",
5278c2ecf20Sopenharmony_ci	.owner	  = THIS_MODULE,
5288c2ecf20Sopenharmony_ci	.obj_size = sizeof(struct pppox_sock),
5298c2ecf20Sopenharmony_ci};
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci/***********************************************************************
5328c2ecf20Sopenharmony_ci *
5338c2ecf20Sopenharmony_ci * Initialize a new struct sock.
5348c2ecf20Sopenharmony_ci *
5358c2ecf20Sopenharmony_ci **********************************************************************/
5368c2ecf20Sopenharmony_cistatic int pppoe_create(struct net *net, struct socket *sock, int kern)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	struct sock *sk;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppoe_sk_proto, kern);
5418c2ecf20Sopenharmony_ci	if (!sk)
5428c2ecf20Sopenharmony_ci		return -ENOMEM;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	sock_init_data(sock, sk);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	sock->state	= SS_UNCONNECTED;
5478c2ecf20Sopenharmony_ci	sock->ops	= &pppoe_ops;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	sk->sk_backlog_rcv	= pppoe_rcv_core;
5508c2ecf20Sopenharmony_ci	sk->sk_state		= PPPOX_NONE;
5518c2ecf20Sopenharmony_ci	sk->sk_type		= SOCK_STREAM;
5528c2ecf20Sopenharmony_ci	sk->sk_family		= PF_PPPOX;
5538c2ecf20Sopenharmony_ci	sk->sk_protocol		= PX_PROTO_OE;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	INIT_WORK(&pppox_sk(sk)->proto.pppoe.padt_work,
5568c2ecf20Sopenharmony_ci		  pppoe_unbind_sock_work);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	return 0;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic int pppoe_release(struct socket *sock)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
5648c2ecf20Sopenharmony_ci	struct pppox_sock *po;
5658c2ecf20Sopenharmony_ci	struct pppoe_net *pn;
5668c2ecf20Sopenharmony_ci	struct net *net = NULL;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (!sk)
5698c2ecf20Sopenharmony_ci		return 0;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	lock_sock(sk);
5728c2ecf20Sopenharmony_ci	if (sock_flag(sk, SOCK_DEAD)) {
5738c2ecf20Sopenharmony_ci		release_sock(sk);
5748c2ecf20Sopenharmony_ci		return -EBADF;
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	po = pppox_sk(sk);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	if (po->pppoe_dev) {
5808c2ecf20Sopenharmony_ci		dev_put(po->pppoe_dev);
5818c2ecf20Sopenharmony_ci		po->pppoe_dev = NULL;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	pppox_unbind_sock(sk);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	/* Signal the death of the socket. */
5878c2ecf20Sopenharmony_ci	sk->sk_state = PPPOX_DEAD;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	net = sock_net(sk);
5908c2ecf20Sopenharmony_ci	pn = pppoe_pernet(net);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	/*
5938c2ecf20Sopenharmony_ci	 * protect "po" from concurrent updates
5948c2ecf20Sopenharmony_ci	 * on pppoe_flush_dev
5958c2ecf20Sopenharmony_ci	 */
5968c2ecf20Sopenharmony_ci	delete_item(pn, po->pppoe_pa.sid, po->pppoe_pa.remote,
5978c2ecf20Sopenharmony_ci		    po->pppoe_ifindex);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	sock_orphan(sk);
6008c2ecf20Sopenharmony_ci	sock->sk = NULL;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	skb_queue_purge(&sk->sk_receive_queue);
6038c2ecf20Sopenharmony_ci	release_sock(sk);
6048c2ecf20Sopenharmony_ci	sock_put(sk);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	return 0;
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
6108c2ecf20Sopenharmony_ci		  int sockaddr_len, int flags)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
6138c2ecf20Sopenharmony_ci	struct sockaddr_pppox *sp = (struct sockaddr_pppox *)uservaddr;
6148c2ecf20Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
6158c2ecf20Sopenharmony_ci	struct net_device *dev = NULL;
6168c2ecf20Sopenharmony_ci	struct pppoe_net *pn;
6178c2ecf20Sopenharmony_ci	struct net *net = NULL;
6188c2ecf20Sopenharmony_ci	int error;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	lock_sock(sk);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	error = -EINVAL;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (sockaddr_len != sizeof(struct sockaddr_pppox))
6258c2ecf20Sopenharmony_ci		goto end;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (sp->sa_protocol != PX_PROTO_OE)
6288c2ecf20Sopenharmony_ci		goto end;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	/* Check for already bound sockets */
6318c2ecf20Sopenharmony_ci	error = -EBUSY;
6328c2ecf20Sopenharmony_ci	if ((sk->sk_state & PPPOX_CONNECTED) &&
6338c2ecf20Sopenharmony_ci	     stage_session(sp->sa_addr.pppoe.sid))
6348c2ecf20Sopenharmony_ci		goto end;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	/* Check for already disconnected sockets, on attempts to disconnect */
6378c2ecf20Sopenharmony_ci	error = -EALREADY;
6388c2ecf20Sopenharmony_ci	if ((sk->sk_state & PPPOX_DEAD) &&
6398c2ecf20Sopenharmony_ci	     !stage_session(sp->sa_addr.pppoe.sid))
6408c2ecf20Sopenharmony_ci		goto end;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	error = 0;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	/* Delete the old binding */
6458c2ecf20Sopenharmony_ci	if (stage_session(po->pppoe_pa.sid)) {
6468c2ecf20Sopenharmony_ci		pppox_unbind_sock(sk);
6478c2ecf20Sopenharmony_ci		pn = pppoe_pernet(sock_net(sk));
6488c2ecf20Sopenharmony_ci		delete_item(pn, po->pppoe_pa.sid,
6498c2ecf20Sopenharmony_ci			    po->pppoe_pa.remote, po->pppoe_ifindex);
6508c2ecf20Sopenharmony_ci		if (po->pppoe_dev) {
6518c2ecf20Sopenharmony_ci			dev_put(po->pppoe_dev);
6528c2ecf20Sopenharmony_ci			po->pppoe_dev = NULL;
6538c2ecf20Sopenharmony_ci		}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci		po->pppoe_ifindex = 0;
6568c2ecf20Sopenharmony_ci		memset(&po->pppoe_pa, 0, sizeof(po->pppoe_pa));
6578c2ecf20Sopenharmony_ci		memset(&po->pppoe_relay, 0, sizeof(po->pppoe_relay));
6588c2ecf20Sopenharmony_ci		memset(&po->chan, 0, sizeof(po->chan));
6598c2ecf20Sopenharmony_ci		po->next = NULL;
6608c2ecf20Sopenharmony_ci		po->num = 0;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci		sk->sk_state = PPPOX_NONE;
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	/* Re-bind in session stage only */
6668c2ecf20Sopenharmony_ci	if (stage_session(sp->sa_addr.pppoe.sid)) {
6678c2ecf20Sopenharmony_ci		error = -ENODEV;
6688c2ecf20Sopenharmony_ci		net = sock_net(sk);
6698c2ecf20Sopenharmony_ci		dev = dev_get_by_name(net, sp->sa_addr.pppoe.dev);
6708c2ecf20Sopenharmony_ci		if (!dev)
6718c2ecf20Sopenharmony_ci			goto err_put;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci		po->pppoe_dev = dev;
6748c2ecf20Sopenharmony_ci		po->pppoe_ifindex = dev->ifindex;
6758c2ecf20Sopenharmony_ci		pn = pppoe_pernet(net);
6768c2ecf20Sopenharmony_ci		if (!(dev->flags & IFF_UP)) {
6778c2ecf20Sopenharmony_ci			goto err_put;
6788c2ecf20Sopenharmony_ci		}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci		memcpy(&po->pppoe_pa,
6818c2ecf20Sopenharmony_ci		       &sp->sa_addr.pppoe,
6828c2ecf20Sopenharmony_ci		       sizeof(struct pppoe_addr));
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci		write_lock_bh(&pn->hash_lock);
6858c2ecf20Sopenharmony_ci		error = __set_item(pn, po);
6868c2ecf20Sopenharmony_ci		write_unlock_bh(&pn->hash_lock);
6878c2ecf20Sopenharmony_ci		if (error < 0)
6888c2ecf20Sopenharmony_ci			goto err_put;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci		po->chan.hdrlen = (sizeof(struct pppoe_hdr) +
6918c2ecf20Sopenharmony_ci				   dev->hard_header_len);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci		po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr) - 2;
6948c2ecf20Sopenharmony_ci		po->chan.private = sk;
6958c2ecf20Sopenharmony_ci		po->chan.ops = &pppoe_chan_ops;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci		error = ppp_register_net_channel(dev_net(dev), &po->chan);
6988c2ecf20Sopenharmony_ci		if (error) {
6998c2ecf20Sopenharmony_ci			delete_item(pn, po->pppoe_pa.sid,
7008c2ecf20Sopenharmony_ci				    po->pppoe_pa.remote, po->pppoe_ifindex);
7018c2ecf20Sopenharmony_ci			goto err_put;
7028c2ecf20Sopenharmony_ci		}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci		sk->sk_state = PPPOX_CONNECTED;
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	po->num = sp->sa_addr.pppoe.sid;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ciend:
7108c2ecf20Sopenharmony_ci	release_sock(sk);
7118c2ecf20Sopenharmony_ci	return error;
7128c2ecf20Sopenharmony_cierr_put:
7138c2ecf20Sopenharmony_ci	if (po->pppoe_dev) {
7148c2ecf20Sopenharmony_ci		dev_put(po->pppoe_dev);
7158c2ecf20Sopenharmony_ci		po->pppoe_dev = NULL;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci	goto end;
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cistatic int pppoe_getname(struct socket *sock, struct sockaddr *uaddr,
7218c2ecf20Sopenharmony_ci		  int peer)
7228c2ecf20Sopenharmony_ci{
7238c2ecf20Sopenharmony_ci	int len = sizeof(struct sockaddr_pppox);
7248c2ecf20Sopenharmony_ci	struct sockaddr_pppox sp;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	sp.sa_family	= AF_PPPOX;
7278c2ecf20Sopenharmony_ci	sp.sa_protocol	= PX_PROTO_OE;
7288c2ecf20Sopenharmony_ci	memcpy(&sp.sa_addr.pppoe, &pppox_sk(sock->sk)->pppoe_pa,
7298c2ecf20Sopenharmony_ci	       sizeof(struct pppoe_addr));
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	memcpy(uaddr, &sp, len);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	return len;
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic int pppoe_ioctl(struct socket *sock, unsigned int cmd,
7378c2ecf20Sopenharmony_ci		unsigned long arg)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
7408c2ecf20Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
7418c2ecf20Sopenharmony_ci	int val;
7428c2ecf20Sopenharmony_ci	int err;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	switch (cmd) {
7458c2ecf20Sopenharmony_ci	case PPPIOCGMRU:
7468c2ecf20Sopenharmony_ci		err = -ENXIO;
7478c2ecf20Sopenharmony_ci		if (!(sk->sk_state & PPPOX_CONNECTED))
7488c2ecf20Sopenharmony_ci			break;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci		err = -EFAULT;
7518c2ecf20Sopenharmony_ci		if (put_user(po->pppoe_dev->mtu -
7528c2ecf20Sopenharmony_ci			     sizeof(struct pppoe_hdr) -
7538c2ecf20Sopenharmony_ci			     PPP_HDRLEN,
7548c2ecf20Sopenharmony_ci			     (int __user *)arg))
7558c2ecf20Sopenharmony_ci			break;
7568c2ecf20Sopenharmony_ci		err = 0;
7578c2ecf20Sopenharmony_ci		break;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	case PPPIOCSMRU:
7608c2ecf20Sopenharmony_ci		err = -ENXIO;
7618c2ecf20Sopenharmony_ci		if (!(sk->sk_state & PPPOX_CONNECTED))
7628c2ecf20Sopenharmony_ci			break;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci		err = -EFAULT;
7658c2ecf20Sopenharmony_ci		if (get_user(val, (int __user *)arg))
7668c2ecf20Sopenharmony_ci			break;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci		if (val < (po->pppoe_dev->mtu
7698c2ecf20Sopenharmony_ci			   - sizeof(struct pppoe_hdr)
7708c2ecf20Sopenharmony_ci			   - PPP_HDRLEN))
7718c2ecf20Sopenharmony_ci			err = 0;
7728c2ecf20Sopenharmony_ci		else
7738c2ecf20Sopenharmony_ci			err = -EINVAL;
7748c2ecf20Sopenharmony_ci		break;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	case PPPIOCSFLAGS:
7778c2ecf20Sopenharmony_ci		err = -EFAULT;
7788c2ecf20Sopenharmony_ci		if (get_user(val, (int __user *)arg))
7798c2ecf20Sopenharmony_ci			break;
7808c2ecf20Sopenharmony_ci		err = 0;
7818c2ecf20Sopenharmony_ci		break;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	case PPPOEIOCSFWD:
7848c2ecf20Sopenharmony_ci	{
7858c2ecf20Sopenharmony_ci		struct pppox_sock *relay_po;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci		err = -EBUSY;
7888c2ecf20Sopenharmony_ci		if (sk->sk_state & (PPPOX_BOUND | PPPOX_DEAD))
7898c2ecf20Sopenharmony_ci			break;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci		err = -ENOTCONN;
7928c2ecf20Sopenharmony_ci		if (!(sk->sk_state & PPPOX_CONNECTED))
7938c2ecf20Sopenharmony_ci			break;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci		/* PPPoE address from the user specifies an outbound
7968c2ecf20Sopenharmony_ci		   PPPoE address which frames are forwarded to */
7978c2ecf20Sopenharmony_ci		err = -EFAULT;
7988c2ecf20Sopenharmony_ci		if (copy_from_user(&po->pppoe_relay,
7998c2ecf20Sopenharmony_ci				   (void __user *)arg,
8008c2ecf20Sopenharmony_ci				   sizeof(struct sockaddr_pppox)))
8018c2ecf20Sopenharmony_ci			break;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci		err = -EINVAL;
8048c2ecf20Sopenharmony_ci		if (po->pppoe_relay.sa_family != AF_PPPOX ||
8058c2ecf20Sopenharmony_ci		    po->pppoe_relay.sa_protocol != PX_PROTO_OE)
8068c2ecf20Sopenharmony_ci			break;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci		/* Check that the socket referenced by the address
8098c2ecf20Sopenharmony_ci		   actually exists. */
8108c2ecf20Sopenharmony_ci		relay_po = get_item_by_addr(sock_net(sk), &po->pppoe_relay);
8118c2ecf20Sopenharmony_ci		if (!relay_po)
8128c2ecf20Sopenharmony_ci			break;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci		sock_put(sk_pppox(relay_po));
8158c2ecf20Sopenharmony_ci		sk->sk_state |= PPPOX_RELAY;
8168c2ecf20Sopenharmony_ci		err = 0;
8178c2ecf20Sopenharmony_ci		break;
8188c2ecf20Sopenharmony_ci	}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	case PPPOEIOCDFWD:
8218c2ecf20Sopenharmony_ci		err = -EALREADY;
8228c2ecf20Sopenharmony_ci		if (!(sk->sk_state & PPPOX_RELAY))
8238c2ecf20Sopenharmony_ci			break;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci		sk->sk_state &= ~PPPOX_RELAY;
8268c2ecf20Sopenharmony_ci		err = 0;
8278c2ecf20Sopenharmony_ci		break;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	default:
8308c2ecf20Sopenharmony_ci		err = -ENOTTY;
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	return err;
8348c2ecf20Sopenharmony_ci}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_cistatic int pppoe_sendmsg(struct socket *sock, struct msghdr *m,
8378c2ecf20Sopenharmony_ci			 size_t total_len)
8388c2ecf20Sopenharmony_ci{
8398c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8408c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
8418c2ecf20Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
8428c2ecf20Sopenharmony_ci	int error;
8438c2ecf20Sopenharmony_ci	struct pppoe_hdr hdr;
8448c2ecf20Sopenharmony_ci	struct pppoe_hdr *ph;
8458c2ecf20Sopenharmony_ci	struct net_device *dev;
8468c2ecf20Sopenharmony_ci	char *start;
8478c2ecf20Sopenharmony_ci	int hlen;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	lock_sock(sk);
8508c2ecf20Sopenharmony_ci	if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) {
8518c2ecf20Sopenharmony_ci		error = -ENOTCONN;
8528c2ecf20Sopenharmony_ci		goto end;
8538c2ecf20Sopenharmony_ci	}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	hdr.ver = 1;
8568c2ecf20Sopenharmony_ci	hdr.type = 1;
8578c2ecf20Sopenharmony_ci	hdr.code = 0;
8588c2ecf20Sopenharmony_ci	hdr.sid = po->num;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	dev = po->pppoe_dev;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	error = -EMSGSIZE;
8638c2ecf20Sopenharmony_ci	if (total_len > (dev->mtu + dev->hard_header_len))
8648c2ecf20Sopenharmony_ci		goto end;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	hlen = LL_RESERVED_SPACE(dev);
8678c2ecf20Sopenharmony_ci	skb = sock_wmalloc(sk, hlen + sizeof(*ph) + total_len +
8688c2ecf20Sopenharmony_ci			   dev->needed_tailroom, 0, GFP_KERNEL);
8698c2ecf20Sopenharmony_ci	if (!skb) {
8708c2ecf20Sopenharmony_ci		error = -ENOMEM;
8718c2ecf20Sopenharmony_ci		goto end;
8728c2ecf20Sopenharmony_ci	}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	/* Reserve space for headers. */
8758c2ecf20Sopenharmony_ci	skb_reserve(skb, hlen);
8768c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	skb->dev = dev;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	skb->priority = sk->sk_priority;
8818c2ecf20Sopenharmony_ci	skb->protocol = cpu_to_be16(ETH_P_PPP_SES);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	ph = skb_put(skb, total_len + sizeof(struct pppoe_hdr));
8848c2ecf20Sopenharmony_ci	start = (char *)&ph->tag[0];
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	error = memcpy_from_msg(start, m, total_len);
8878c2ecf20Sopenharmony_ci	if (error < 0) {
8888c2ecf20Sopenharmony_ci		kfree_skb(skb);
8898c2ecf20Sopenharmony_ci		goto end;
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	error = total_len;
8938c2ecf20Sopenharmony_ci	dev_hard_header(skb, dev, ETH_P_PPP_SES,
8948c2ecf20Sopenharmony_ci			po->pppoe_pa.remote, NULL, total_len);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	memcpy(ph, &hdr, sizeof(struct pppoe_hdr));
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	ph->length = htons(total_len);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	dev_queue_xmit(skb);
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ciend:
9038c2ecf20Sopenharmony_ci	release_sock(sk);
9048c2ecf20Sopenharmony_ci	return error;
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci/************************************************************************
9088c2ecf20Sopenharmony_ci *
9098c2ecf20Sopenharmony_ci * xmit function for internal use.
9108c2ecf20Sopenharmony_ci *
9118c2ecf20Sopenharmony_ci ***********************************************************************/
9128c2ecf20Sopenharmony_cistatic int __pppoe_xmit(struct sock *sk, struct sk_buff *skb)
9138c2ecf20Sopenharmony_ci{
9148c2ecf20Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
9158c2ecf20Sopenharmony_ci	struct net_device *dev = po->pppoe_dev;
9168c2ecf20Sopenharmony_ci	struct pppoe_hdr *ph;
9178c2ecf20Sopenharmony_ci	int data_len = skb->len;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	/* The higher-level PPP code (ppp_unregister_channel()) ensures the PPP
9208c2ecf20Sopenharmony_ci	 * xmit operations conclude prior to an unregistration call.  Thus
9218c2ecf20Sopenharmony_ci	 * sk->sk_state cannot change, so we don't need to do lock_sock().
9228c2ecf20Sopenharmony_ci	 * But, we also can't do a lock_sock since that introduces a potential
9238c2ecf20Sopenharmony_ci	 * deadlock as we'd reverse the lock ordering used when calling
9248c2ecf20Sopenharmony_ci	 * ppp_unregister_channel().
9258c2ecf20Sopenharmony_ci	 */
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
9288c2ecf20Sopenharmony_ci		goto abort;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	if (!dev)
9318c2ecf20Sopenharmony_ci		goto abort;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	/* Copy the data if there is no space for the header or if it's
9348c2ecf20Sopenharmony_ci	 * read-only.
9358c2ecf20Sopenharmony_ci	 */
9368c2ecf20Sopenharmony_ci	if (skb_cow_head(skb, LL_RESERVED_SPACE(dev) + sizeof(*ph)))
9378c2ecf20Sopenharmony_ci		goto abort;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	__skb_push(skb, sizeof(*ph));
9408c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	ph = pppoe_hdr(skb);
9438c2ecf20Sopenharmony_ci	ph->ver	= 1;
9448c2ecf20Sopenharmony_ci	ph->type = 1;
9458c2ecf20Sopenharmony_ci	ph->code = 0;
9468c2ecf20Sopenharmony_ci	ph->sid	= po->num;
9478c2ecf20Sopenharmony_ci	ph->length = htons(data_len);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	skb->protocol = cpu_to_be16(ETH_P_PPP_SES);
9508c2ecf20Sopenharmony_ci	skb->dev = dev;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	dev_hard_header(skb, dev, ETH_P_PPP_SES,
9538c2ecf20Sopenharmony_ci			po->pppoe_pa.remote, NULL, data_len);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	dev_queue_xmit(skb);
9568c2ecf20Sopenharmony_ci	return 1;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ciabort:
9598c2ecf20Sopenharmony_ci	kfree_skb(skb);
9608c2ecf20Sopenharmony_ci	return 1;
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci/************************************************************************
9648c2ecf20Sopenharmony_ci *
9658c2ecf20Sopenharmony_ci * xmit function called by generic PPP driver
9668c2ecf20Sopenharmony_ci * sends PPP frame over PPPoE socket
9678c2ecf20Sopenharmony_ci *
9688c2ecf20Sopenharmony_ci ***********************************************************************/
9698c2ecf20Sopenharmony_cistatic int pppoe_xmit(struct ppp_channel *chan, struct sk_buff *skb)
9708c2ecf20Sopenharmony_ci{
9718c2ecf20Sopenharmony_ci	struct sock *sk = (struct sock *)chan->private;
9728c2ecf20Sopenharmony_ci	return __pppoe_xmit(sk, skb);
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_cistatic const struct ppp_channel_ops pppoe_chan_ops = {
9768c2ecf20Sopenharmony_ci	.start_xmit = pppoe_xmit,
9778c2ecf20Sopenharmony_ci};
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_cistatic int pppoe_recvmsg(struct socket *sock, struct msghdr *m,
9808c2ecf20Sopenharmony_ci			 size_t total_len, int flags)
9818c2ecf20Sopenharmony_ci{
9828c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
9838c2ecf20Sopenharmony_ci	struct sk_buff *skb;
9848c2ecf20Sopenharmony_ci	int error = 0;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	if (sk->sk_state & PPPOX_BOUND) {
9878c2ecf20Sopenharmony_ci		error = -EIO;
9888c2ecf20Sopenharmony_ci		goto end;
9898c2ecf20Sopenharmony_ci	}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
9928c2ecf20Sopenharmony_ci				flags & MSG_DONTWAIT, &error);
9938c2ecf20Sopenharmony_ci	if (error < 0)
9948c2ecf20Sopenharmony_ci		goto end;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	if (skb) {
9978c2ecf20Sopenharmony_ci		total_len = min_t(size_t, total_len, skb->len);
9988c2ecf20Sopenharmony_ci		error = skb_copy_datagram_msg(skb, 0, m, total_len);
9998c2ecf20Sopenharmony_ci		if (error == 0) {
10008c2ecf20Sopenharmony_ci			consume_skb(skb);
10018c2ecf20Sopenharmony_ci			return total_len;
10028c2ecf20Sopenharmony_ci		}
10038c2ecf20Sopenharmony_ci	}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	kfree_skb(skb);
10068c2ecf20Sopenharmony_ciend:
10078c2ecf20Sopenharmony_ci	return error;
10088c2ecf20Sopenharmony_ci}
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
10118c2ecf20Sopenharmony_cistatic int pppoe_seq_show(struct seq_file *seq, void *v)
10128c2ecf20Sopenharmony_ci{
10138c2ecf20Sopenharmony_ci	struct pppox_sock *po;
10148c2ecf20Sopenharmony_ci	char *dev_name;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
10178c2ecf20Sopenharmony_ci		seq_puts(seq, "Id       Address              Device\n");
10188c2ecf20Sopenharmony_ci		goto out;
10198c2ecf20Sopenharmony_ci	}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	po = v;
10228c2ecf20Sopenharmony_ci	dev_name = po->pppoe_pa.dev;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	seq_printf(seq, "%08X %pM %8s\n",
10258c2ecf20Sopenharmony_ci		po->pppoe_pa.sid, po->pppoe_pa.remote, dev_name);
10268c2ecf20Sopenharmony_ciout:
10278c2ecf20Sopenharmony_ci	return 0;
10288c2ecf20Sopenharmony_ci}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_cistatic inline struct pppox_sock *pppoe_get_idx(struct pppoe_net *pn, loff_t pos)
10318c2ecf20Sopenharmony_ci{
10328c2ecf20Sopenharmony_ci	struct pppox_sock *po;
10338c2ecf20Sopenharmony_ci	int i;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	for (i = 0; i < PPPOE_HASH_SIZE; i++) {
10368c2ecf20Sopenharmony_ci		po = pn->hash_table[i];
10378c2ecf20Sopenharmony_ci		while (po) {
10388c2ecf20Sopenharmony_ci			if (!pos--)
10398c2ecf20Sopenharmony_ci				goto out;
10408c2ecf20Sopenharmony_ci			po = po->next;
10418c2ecf20Sopenharmony_ci		}
10428c2ecf20Sopenharmony_ci	}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ciout:
10458c2ecf20Sopenharmony_ci	return po;
10468c2ecf20Sopenharmony_ci}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_cistatic void *pppoe_seq_start(struct seq_file *seq, loff_t *pos)
10498c2ecf20Sopenharmony_ci	__acquires(pn->hash_lock)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
10528c2ecf20Sopenharmony_ci	loff_t l = *pos;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	read_lock_bh(&pn->hash_lock);
10558c2ecf20Sopenharmony_ci	return l ? pppoe_get_idx(pn, --l) : SEQ_START_TOKEN;
10568c2ecf20Sopenharmony_ci}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_cistatic void *pppoe_seq_next(struct seq_file *seq, void *v, loff_t *pos)
10598c2ecf20Sopenharmony_ci{
10608c2ecf20Sopenharmony_ci	struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
10618c2ecf20Sopenharmony_ci	struct pppox_sock *po;
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	++*pos;
10648c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
10658c2ecf20Sopenharmony_ci		po = pppoe_get_idx(pn, 0);
10668c2ecf20Sopenharmony_ci		goto out;
10678c2ecf20Sopenharmony_ci	}
10688c2ecf20Sopenharmony_ci	po = v;
10698c2ecf20Sopenharmony_ci	if (po->next)
10708c2ecf20Sopenharmony_ci		po = po->next;
10718c2ecf20Sopenharmony_ci	else {
10728c2ecf20Sopenharmony_ci		int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci		po = NULL;
10758c2ecf20Sopenharmony_ci		while (++hash < PPPOE_HASH_SIZE) {
10768c2ecf20Sopenharmony_ci			po = pn->hash_table[hash];
10778c2ecf20Sopenharmony_ci			if (po)
10788c2ecf20Sopenharmony_ci				break;
10798c2ecf20Sopenharmony_ci		}
10808c2ecf20Sopenharmony_ci	}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ciout:
10838c2ecf20Sopenharmony_ci	return po;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cistatic void pppoe_seq_stop(struct seq_file *seq, void *v)
10878c2ecf20Sopenharmony_ci	__releases(pn->hash_lock)
10888c2ecf20Sopenharmony_ci{
10898c2ecf20Sopenharmony_ci	struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq));
10908c2ecf20Sopenharmony_ci	read_unlock_bh(&pn->hash_lock);
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_cistatic const struct seq_operations pppoe_seq_ops = {
10948c2ecf20Sopenharmony_ci	.start		= pppoe_seq_start,
10958c2ecf20Sopenharmony_ci	.next		= pppoe_seq_next,
10968c2ecf20Sopenharmony_ci	.stop		= pppoe_seq_stop,
10978c2ecf20Sopenharmony_ci	.show		= pppoe_seq_show,
10988c2ecf20Sopenharmony_ci};
10998c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_cistatic const struct proto_ops pppoe_ops = {
11028c2ecf20Sopenharmony_ci	.family		= AF_PPPOX,
11038c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
11048c2ecf20Sopenharmony_ci	.release	= pppoe_release,
11058c2ecf20Sopenharmony_ci	.bind		= sock_no_bind,
11068c2ecf20Sopenharmony_ci	.connect	= pppoe_connect,
11078c2ecf20Sopenharmony_ci	.socketpair	= sock_no_socketpair,
11088c2ecf20Sopenharmony_ci	.accept		= sock_no_accept,
11098c2ecf20Sopenharmony_ci	.getname	= pppoe_getname,
11108c2ecf20Sopenharmony_ci	.poll		= datagram_poll,
11118c2ecf20Sopenharmony_ci	.listen		= sock_no_listen,
11128c2ecf20Sopenharmony_ci	.shutdown	= sock_no_shutdown,
11138c2ecf20Sopenharmony_ci	.sendmsg	= pppoe_sendmsg,
11148c2ecf20Sopenharmony_ci	.recvmsg	= pppoe_recvmsg,
11158c2ecf20Sopenharmony_ci	.mmap		= sock_no_mmap,
11168c2ecf20Sopenharmony_ci	.ioctl		= pppox_ioctl,
11178c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
11188c2ecf20Sopenharmony_ci	.compat_ioctl	= pppox_compat_ioctl,
11198c2ecf20Sopenharmony_ci#endif
11208c2ecf20Sopenharmony_ci};
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_cistatic const struct pppox_proto pppoe_proto = {
11238c2ecf20Sopenharmony_ci	.create	= pppoe_create,
11248c2ecf20Sopenharmony_ci	.ioctl	= pppoe_ioctl,
11258c2ecf20Sopenharmony_ci	.owner	= THIS_MODULE,
11268c2ecf20Sopenharmony_ci};
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_cistatic __net_init int pppoe_init_net(struct net *net)
11298c2ecf20Sopenharmony_ci{
11308c2ecf20Sopenharmony_ci	struct pppoe_net *pn = pppoe_pernet(net);
11318c2ecf20Sopenharmony_ci	struct proc_dir_entry *pde;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	rwlock_init(&pn->hash_lock);
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	pde = proc_create_net("pppoe", 0444, net->proc_net,
11368c2ecf20Sopenharmony_ci			&pppoe_seq_ops, sizeof(struct seq_net_private));
11378c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
11388c2ecf20Sopenharmony_ci	if (!pde)
11398c2ecf20Sopenharmony_ci		return -ENOMEM;
11408c2ecf20Sopenharmony_ci#endif
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	return 0;
11438c2ecf20Sopenharmony_ci}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_cistatic __net_exit void pppoe_exit_net(struct net *net)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	remove_proc_entry("pppoe", net->proc_net);
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_cistatic struct pernet_operations pppoe_net_ops = {
11518c2ecf20Sopenharmony_ci	.init = pppoe_init_net,
11528c2ecf20Sopenharmony_ci	.exit = pppoe_exit_net,
11538c2ecf20Sopenharmony_ci	.id   = &pppoe_net_id,
11548c2ecf20Sopenharmony_ci	.size = sizeof(struct pppoe_net),
11558c2ecf20Sopenharmony_ci};
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_cistatic int __init pppoe_init(void)
11588c2ecf20Sopenharmony_ci{
11598c2ecf20Sopenharmony_ci	int err;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	err = register_pernet_device(&pppoe_net_ops);
11628c2ecf20Sopenharmony_ci	if (err)
11638c2ecf20Sopenharmony_ci		goto out;
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	err = proto_register(&pppoe_sk_proto, 0);
11668c2ecf20Sopenharmony_ci	if (err)
11678c2ecf20Sopenharmony_ci		goto out_unregister_net_ops;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	err = register_pppox_proto(PX_PROTO_OE, &pppoe_proto);
11708c2ecf20Sopenharmony_ci	if (err)
11718c2ecf20Sopenharmony_ci		goto out_unregister_pppoe_proto;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	dev_add_pack(&pppoes_ptype);
11748c2ecf20Sopenharmony_ci	dev_add_pack(&pppoed_ptype);
11758c2ecf20Sopenharmony_ci	register_netdevice_notifier(&pppoe_notifier);
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	return 0;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ciout_unregister_pppoe_proto:
11808c2ecf20Sopenharmony_ci	proto_unregister(&pppoe_sk_proto);
11818c2ecf20Sopenharmony_ciout_unregister_net_ops:
11828c2ecf20Sopenharmony_ci	unregister_pernet_device(&pppoe_net_ops);
11838c2ecf20Sopenharmony_ciout:
11848c2ecf20Sopenharmony_ci	return err;
11858c2ecf20Sopenharmony_ci}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_cistatic void __exit pppoe_exit(void)
11888c2ecf20Sopenharmony_ci{
11898c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&pppoe_notifier);
11908c2ecf20Sopenharmony_ci	dev_remove_pack(&pppoed_ptype);
11918c2ecf20Sopenharmony_ci	dev_remove_pack(&pppoes_ptype);
11928c2ecf20Sopenharmony_ci	unregister_pppox_proto(PX_PROTO_OE);
11938c2ecf20Sopenharmony_ci	proto_unregister(&pppoe_sk_proto);
11948c2ecf20Sopenharmony_ci	unregister_pernet_device(&pppoe_net_ops);
11958c2ecf20Sopenharmony_ci}
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_cimodule_init(pppoe_init);
11988c2ecf20Sopenharmony_cimodule_exit(pppoe_exit);
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
12018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PPP over Ethernet driver");
12028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
12038c2ecf20Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_OE);
1204