162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Generic PPP layer for Linux.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 1999-2002 Paul Mackerras.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * The generic PPP layer handles the PPP network interfaces, the
862306a36Sopenharmony_ci * /dev/ppp device, packet and VJ compression, and multilink.
962306a36Sopenharmony_ci * It talks to PPP `channels' via the interface defined in
1062306a36Sopenharmony_ci * include/linux/ppp_channel.h.  Channels provide the basic means for
1162306a36Sopenharmony_ci * sending and receiving PPP frames on some kind of communications
1262306a36Sopenharmony_ci * channel.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Part of the code in this driver was inspired by the old async-only
1562306a36Sopenharmony_ci * PPP driver, written by Michael Callahan and Al Longyear, and
1662306a36Sopenharmony_ci * subsequently hacked by Paul Mackerras.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * ==FILEVERSION 20041108==
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/module.h>
2262306a36Sopenharmony_ci#include <linux/kernel.h>
2362306a36Sopenharmony_ci#include <linux/sched/signal.h>
2462306a36Sopenharmony_ci#include <linux/kmod.h>
2562306a36Sopenharmony_ci#include <linux/init.h>
2662306a36Sopenharmony_ci#include <linux/list.h>
2762306a36Sopenharmony_ci#include <linux/idr.h>
2862306a36Sopenharmony_ci#include <linux/netdevice.h>
2962306a36Sopenharmony_ci#include <linux/poll.h>
3062306a36Sopenharmony_ci#include <linux/ppp_defs.h>
3162306a36Sopenharmony_ci#include <linux/filter.h>
3262306a36Sopenharmony_ci#include <linux/ppp-ioctl.h>
3362306a36Sopenharmony_ci#include <linux/ppp_channel.h>
3462306a36Sopenharmony_ci#include <linux/ppp-comp.h>
3562306a36Sopenharmony_ci#include <linux/skbuff.h>
3662306a36Sopenharmony_ci#include <linux/rtnetlink.h>
3762306a36Sopenharmony_ci#include <linux/if_arp.h>
3862306a36Sopenharmony_ci#include <linux/ip.h>
3962306a36Sopenharmony_ci#include <linux/tcp.h>
4062306a36Sopenharmony_ci#include <linux/spinlock.h>
4162306a36Sopenharmony_ci#include <linux/rwsem.h>
4262306a36Sopenharmony_ci#include <linux/stddef.h>
4362306a36Sopenharmony_ci#include <linux/device.h>
4462306a36Sopenharmony_ci#include <linux/mutex.h>
4562306a36Sopenharmony_ci#include <linux/slab.h>
4662306a36Sopenharmony_ci#include <linux/file.h>
4762306a36Sopenharmony_ci#include <asm/unaligned.h>
4862306a36Sopenharmony_ci#include <net/slhc_vj.h>
4962306a36Sopenharmony_ci#include <linux/atomic.h>
5062306a36Sopenharmony_ci#include <linux/refcount.h>
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#include <linux/nsproxy.h>
5362306a36Sopenharmony_ci#include <net/net_namespace.h>
5462306a36Sopenharmony_ci#include <net/netns/generic.h>
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define PPP_VERSION	"2.4.2"
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * Network protocols we support.
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_ci#define NP_IP	0		/* Internet Protocol V4 */
6262306a36Sopenharmony_ci#define NP_IPV6	1		/* Internet Protocol V6 */
6362306a36Sopenharmony_ci#define NP_IPX	2		/* IPX protocol */
6462306a36Sopenharmony_ci#define NP_AT	3		/* Appletalk protocol */
6562306a36Sopenharmony_ci#define NP_MPLS_UC 4		/* MPLS unicast */
6662306a36Sopenharmony_ci#define NP_MPLS_MC 5		/* MPLS multicast */
6762306a36Sopenharmony_ci#define NUM_NP	6		/* Number of NPs. */
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define MPHDRLEN	6	/* multilink protocol header length */
7062306a36Sopenharmony_ci#define MPHDRLEN_SSN	4	/* ditto with short sequence numbers */
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#define PPP_PROTO_LEN	2
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * An instance of /dev/ppp can be associated with either a ppp
7662306a36Sopenharmony_ci * interface unit or a ppp channel.  In both cases, file->private_data
7762306a36Sopenharmony_ci * points to one of these.
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_cistruct ppp_file {
8062306a36Sopenharmony_ci	enum {
8162306a36Sopenharmony_ci		INTERFACE=1, CHANNEL
8262306a36Sopenharmony_ci	}		kind;
8362306a36Sopenharmony_ci	struct sk_buff_head xq;		/* pppd transmit queue */
8462306a36Sopenharmony_ci	struct sk_buff_head rq;		/* receive queue for pppd */
8562306a36Sopenharmony_ci	wait_queue_head_t rwait;	/* for poll on reading /dev/ppp */
8662306a36Sopenharmony_ci	refcount_t	refcnt;		/* # refs (incl /dev/ppp attached) */
8762306a36Sopenharmony_ci	int		hdrlen;		/* space to leave for headers */
8862306a36Sopenharmony_ci	int		index;		/* interface unit / channel number */
8962306a36Sopenharmony_ci	int		dead;		/* unit/channel has been shut down */
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define PF_TO_X(pf, X)		container_of(pf, X, file)
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define PF_TO_PPP(pf)		PF_TO_X(pf, struct ppp)
9562306a36Sopenharmony_ci#define PF_TO_CHANNEL(pf)	PF_TO_X(pf, struct channel)
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * Data structure to hold primary network stats for which
9962306a36Sopenharmony_ci * we want to use 64 bit storage.  Other network stats
10062306a36Sopenharmony_ci * are stored in dev->stats of the ppp strucute.
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistruct ppp_link_stats {
10362306a36Sopenharmony_ci	u64 rx_packets;
10462306a36Sopenharmony_ci	u64 tx_packets;
10562306a36Sopenharmony_ci	u64 rx_bytes;
10662306a36Sopenharmony_ci	u64 tx_bytes;
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/*
11062306a36Sopenharmony_ci * Data structure describing one ppp unit.
11162306a36Sopenharmony_ci * A ppp unit corresponds to a ppp network interface device
11262306a36Sopenharmony_ci * and represents a multilink bundle.
11362306a36Sopenharmony_ci * It can have 0 or more ppp channels connected to it.
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_cistruct ppp {
11662306a36Sopenharmony_ci	struct ppp_file	file;		/* stuff for read/write/poll 0 */
11762306a36Sopenharmony_ci	struct file	*owner;		/* file that owns this unit 48 */
11862306a36Sopenharmony_ci	struct list_head channels;	/* list of attached channels 4c */
11962306a36Sopenharmony_ci	int		n_channels;	/* how many channels are attached 54 */
12062306a36Sopenharmony_ci	spinlock_t	rlock;		/* lock for receive side 58 */
12162306a36Sopenharmony_ci	spinlock_t	wlock;		/* lock for transmit side 5c */
12262306a36Sopenharmony_ci	int __percpu	*xmit_recursion; /* xmit recursion detect */
12362306a36Sopenharmony_ci	int		mru;		/* max receive unit 60 */
12462306a36Sopenharmony_ci	unsigned int	flags;		/* control bits 64 */
12562306a36Sopenharmony_ci	unsigned int	xstate;		/* transmit state bits 68 */
12662306a36Sopenharmony_ci	unsigned int	rstate;		/* receive state bits 6c */
12762306a36Sopenharmony_ci	int		debug;		/* debug flags 70 */
12862306a36Sopenharmony_ci	struct slcompress *vj;		/* state for VJ header compression */
12962306a36Sopenharmony_ci	enum NPmode	npmode[NUM_NP];	/* what to do with each net proto 78 */
13062306a36Sopenharmony_ci	struct sk_buff	*xmit_pending;	/* a packet ready to go out 88 */
13162306a36Sopenharmony_ci	struct compressor *xcomp;	/* transmit packet compressor 8c */
13262306a36Sopenharmony_ci	void		*xc_state;	/* its internal state 90 */
13362306a36Sopenharmony_ci	struct compressor *rcomp;	/* receive decompressor 94 */
13462306a36Sopenharmony_ci	void		*rc_state;	/* its internal state 98 */
13562306a36Sopenharmony_ci	unsigned long	last_xmit;	/* jiffies when last pkt sent 9c */
13662306a36Sopenharmony_ci	unsigned long	last_recv;	/* jiffies when last pkt rcvd a0 */
13762306a36Sopenharmony_ci	struct net_device *dev;		/* network interface device a4 */
13862306a36Sopenharmony_ci	int		closing;	/* is device closing down? a8 */
13962306a36Sopenharmony_ci#ifdef CONFIG_PPP_MULTILINK
14062306a36Sopenharmony_ci	int		nxchan;		/* next channel to send something on */
14162306a36Sopenharmony_ci	u32		nxseq;		/* next sequence number to send */
14262306a36Sopenharmony_ci	int		mrru;		/* MP: max reconst. receive unit */
14362306a36Sopenharmony_ci	u32		nextseq;	/* MP: seq no of next packet */
14462306a36Sopenharmony_ci	u32		minseq;		/* MP: min of most recent seqnos */
14562306a36Sopenharmony_ci	struct sk_buff_head mrq;	/* MP: receive reconstruction queue */
14662306a36Sopenharmony_ci#endif /* CONFIG_PPP_MULTILINK */
14762306a36Sopenharmony_ci#ifdef CONFIG_PPP_FILTER
14862306a36Sopenharmony_ci	struct bpf_prog *pass_filter;	/* filter for packets to pass */
14962306a36Sopenharmony_ci	struct bpf_prog *active_filter; /* filter for pkts to reset idle */
15062306a36Sopenharmony_ci#endif /* CONFIG_PPP_FILTER */
15162306a36Sopenharmony_ci	struct net	*ppp_net;	/* the net we belong to */
15262306a36Sopenharmony_ci	struct ppp_link_stats stats64;	/* 64 bit network stats */
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/*
15662306a36Sopenharmony_ci * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC,
15762306a36Sopenharmony_ci * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ, SC_COMP_TCP, SC_REJ_COMP_TCP,
15862306a36Sopenharmony_ci * SC_MUST_COMP
15962306a36Sopenharmony_ci * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
16062306a36Sopenharmony_ci * Bits in xstate: SC_COMP_RUN
16162306a36Sopenharmony_ci */
16262306a36Sopenharmony_ci#define SC_FLAG_BITS	(SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC \
16362306a36Sopenharmony_ci			 |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ \
16462306a36Sopenharmony_ci			 |SC_COMP_TCP|SC_REJ_COMP_TCP|SC_MUST_COMP)
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/*
16762306a36Sopenharmony_ci * Private data structure for each channel.
16862306a36Sopenharmony_ci * This includes the data structure used for multilink.
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_cistruct channel {
17162306a36Sopenharmony_ci	struct ppp_file	file;		/* stuff for read/write/poll */
17262306a36Sopenharmony_ci	struct list_head list;		/* link in all/new_channels list */
17362306a36Sopenharmony_ci	struct ppp_channel *chan;	/* public channel data structure */
17462306a36Sopenharmony_ci	struct rw_semaphore chan_sem;	/* protects `chan' during chan ioctl */
17562306a36Sopenharmony_ci	spinlock_t	downl;		/* protects `chan', file.xq dequeue */
17662306a36Sopenharmony_ci	struct ppp	*ppp;		/* ppp unit we're connected to */
17762306a36Sopenharmony_ci	struct net	*chan_net;	/* the net channel belongs to */
17862306a36Sopenharmony_ci	netns_tracker	ns_tracker;
17962306a36Sopenharmony_ci	struct list_head clist;		/* link in list of channels per unit */
18062306a36Sopenharmony_ci	rwlock_t	upl;		/* protects `ppp' and 'bridge' */
18162306a36Sopenharmony_ci	struct channel __rcu *bridge;	/* "bridged" ppp channel */
18262306a36Sopenharmony_ci#ifdef CONFIG_PPP_MULTILINK
18362306a36Sopenharmony_ci	u8		avail;		/* flag used in multilink stuff */
18462306a36Sopenharmony_ci	u8		had_frag;	/* >= 1 fragments have been sent */
18562306a36Sopenharmony_ci	u32		lastseq;	/* MP: last sequence # received */
18662306a36Sopenharmony_ci	int		speed;		/* speed of the corresponding ppp channel*/
18762306a36Sopenharmony_ci#endif /* CONFIG_PPP_MULTILINK */
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistruct ppp_config {
19162306a36Sopenharmony_ci	struct file *file;
19262306a36Sopenharmony_ci	s32 unit;
19362306a36Sopenharmony_ci	bool ifname_is_set;
19462306a36Sopenharmony_ci};
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci/*
19762306a36Sopenharmony_ci * SMP locking issues:
19862306a36Sopenharmony_ci * Both the ppp.rlock and ppp.wlock locks protect the ppp.channels
19962306a36Sopenharmony_ci * list and the ppp.n_channels field, you need to take both locks
20062306a36Sopenharmony_ci * before you modify them.
20162306a36Sopenharmony_ci * The lock ordering is: channel.upl -> ppp.wlock -> ppp.rlock ->
20262306a36Sopenharmony_ci * channel.downl.
20362306a36Sopenharmony_ci */
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic DEFINE_MUTEX(ppp_mutex);
20662306a36Sopenharmony_cistatic atomic_t ppp_unit_count = ATOMIC_INIT(0);
20762306a36Sopenharmony_cistatic atomic_t channel_count = ATOMIC_INIT(0);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/* per-net private data for this module */
21062306a36Sopenharmony_cistatic unsigned int ppp_net_id __read_mostly;
21162306a36Sopenharmony_cistruct ppp_net {
21262306a36Sopenharmony_ci	/* units to ppp mapping */
21362306a36Sopenharmony_ci	struct idr units_idr;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/*
21662306a36Sopenharmony_ci	 * all_ppp_mutex protects the units_idr mapping.
21762306a36Sopenharmony_ci	 * It also ensures that finding a ppp unit in the units_idr
21862306a36Sopenharmony_ci	 * map and updating its file.refcnt field is atomic.
21962306a36Sopenharmony_ci	 */
22062306a36Sopenharmony_ci	struct mutex all_ppp_mutex;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* channels */
22362306a36Sopenharmony_ci	struct list_head all_channels;
22462306a36Sopenharmony_ci	struct list_head new_channels;
22562306a36Sopenharmony_ci	int last_channel_index;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/*
22862306a36Sopenharmony_ci	 * all_channels_lock protects all_channels and
22962306a36Sopenharmony_ci	 * last_channel_index, and the atomicity of find
23062306a36Sopenharmony_ci	 * a channel and updating its file.refcnt field.
23162306a36Sopenharmony_ci	 */
23262306a36Sopenharmony_ci	spinlock_t all_channels_lock;
23362306a36Sopenharmony_ci};
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci/* Get the PPP protocol number from a skb */
23662306a36Sopenharmony_ci#define PPP_PROTO(skb)	get_unaligned_be16((skb)->data)
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/* We limit the length of ppp->file.rq to this (arbitrary) value */
23962306a36Sopenharmony_ci#define PPP_MAX_RQLEN	32
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/*
24262306a36Sopenharmony_ci * Maximum number of multilink fragments queued up.
24362306a36Sopenharmony_ci * This has to be large enough to cope with the maximum latency of
24462306a36Sopenharmony_ci * the slowest channel relative to the others.  Strictly it should
24562306a36Sopenharmony_ci * depend on the number of channels and their characteristics.
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_ci#define PPP_MP_MAX_QLEN	128
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci/* Multilink header bits. */
25062306a36Sopenharmony_ci#define B	0x80		/* this fragment begins a packet */
25162306a36Sopenharmony_ci#define E	0x40		/* this fragment ends a packet */
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/* Compare multilink sequence numbers (assumed to be 32 bits wide) */
25462306a36Sopenharmony_ci#define seq_before(a, b)	((s32)((a) - (b)) < 0)
25562306a36Sopenharmony_ci#define seq_after(a, b)		((s32)((a) - (b)) > 0)
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/* Prototypes. */
25862306a36Sopenharmony_cistatic int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
25962306a36Sopenharmony_ci			struct file *file, unsigned int cmd, unsigned long arg);
26062306a36Sopenharmony_cistatic void ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb);
26162306a36Sopenharmony_cistatic void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
26262306a36Sopenharmony_cistatic void ppp_push(struct ppp *ppp);
26362306a36Sopenharmony_cistatic void ppp_channel_push(struct channel *pch);
26462306a36Sopenharmony_cistatic void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb,
26562306a36Sopenharmony_ci			      struct channel *pch);
26662306a36Sopenharmony_cistatic void ppp_receive_error(struct ppp *ppp);
26762306a36Sopenharmony_cistatic void ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb);
26862306a36Sopenharmony_cistatic struct sk_buff *ppp_decompress_frame(struct ppp *ppp,
26962306a36Sopenharmony_ci					    struct sk_buff *skb);
27062306a36Sopenharmony_ci#ifdef CONFIG_PPP_MULTILINK
27162306a36Sopenharmony_cistatic void ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb,
27262306a36Sopenharmony_ci				struct channel *pch);
27362306a36Sopenharmony_cistatic void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb);
27462306a36Sopenharmony_cistatic struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp);
27562306a36Sopenharmony_cistatic int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb);
27662306a36Sopenharmony_ci#endif /* CONFIG_PPP_MULTILINK */
27762306a36Sopenharmony_cistatic int ppp_set_compress(struct ppp *ppp, struct ppp_option_data *data);
27862306a36Sopenharmony_cistatic void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound);
27962306a36Sopenharmony_cistatic void ppp_ccp_closed(struct ppp *ppp);
28062306a36Sopenharmony_cistatic struct compressor *find_compressor(int type);
28162306a36Sopenharmony_cistatic void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st);
28262306a36Sopenharmony_cistatic int ppp_create_interface(struct net *net, struct file *file, int *unit);
28362306a36Sopenharmony_cistatic void init_ppp_file(struct ppp_file *pf, int kind);
28462306a36Sopenharmony_cistatic void ppp_destroy_interface(struct ppp *ppp);
28562306a36Sopenharmony_cistatic struct ppp *ppp_find_unit(struct ppp_net *pn, int unit);
28662306a36Sopenharmony_cistatic struct channel *ppp_find_channel(struct ppp_net *pn, int unit);
28762306a36Sopenharmony_cistatic int ppp_connect_channel(struct channel *pch, int unit);
28862306a36Sopenharmony_cistatic int ppp_disconnect_channel(struct channel *pch);
28962306a36Sopenharmony_cistatic void ppp_destroy_channel(struct channel *pch);
29062306a36Sopenharmony_cistatic int unit_get(struct idr *p, void *ptr, int min);
29162306a36Sopenharmony_cistatic int unit_set(struct idr *p, void *ptr, int n);
29262306a36Sopenharmony_cistatic void unit_put(struct idr *p, int n);
29362306a36Sopenharmony_cistatic void *unit_find(struct idr *p, int n);
29462306a36Sopenharmony_cistatic void ppp_setup(struct net_device *dev);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic const struct net_device_ops ppp_netdev_ops;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic struct class *ppp_class;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/* per net-namespace data */
30162306a36Sopenharmony_cistatic inline struct ppp_net *ppp_pernet(struct net *net)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	return net_generic(net, ppp_net_id);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci/* Translates a PPP protocol number to a NP index (NP == network protocol) */
30762306a36Sopenharmony_cistatic inline int proto_to_npindex(int proto)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	switch (proto) {
31062306a36Sopenharmony_ci	case PPP_IP:
31162306a36Sopenharmony_ci		return NP_IP;
31262306a36Sopenharmony_ci	case PPP_IPV6:
31362306a36Sopenharmony_ci		return NP_IPV6;
31462306a36Sopenharmony_ci	case PPP_IPX:
31562306a36Sopenharmony_ci		return NP_IPX;
31662306a36Sopenharmony_ci	case PPP_AT:
31762306a36Sopenharmony_ci		return NP_AT;
31862306a36Sopenharmony_ci	case PPP_MPLS_UC:
31962306a36Sopenharmony_ci		return NP_MPLS_UC;
32062306a36Sopenharmony_ci	case PPP_MPLS_MC:
32162306a36Sopenharmony_ci		return NP_MPLS_MC;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	return -EINVAL;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci/* Translates an NP index into a PPP protocol number */
32762306a36Sopenharmony_cistatic const int npindex_to_proto[NUM_NP] = {
32862306a36Sopenharmony_ci	PPP_IP,
32962306a36Sopenharmony_ci	PPP_IPV6,
33062306a36Sopenharmony_ci	PPP_IPX,
33162306a36Sopenharmony_ci	PPP_AT,
33262306a36Sopenharmony_ci	PPP_MPLS_UC,
33362306a36Sopenharmony_ci	PPP_MPLS_MC,
33462306a36Sopenharmony_ci};
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci/* Translates an ethertype into an NP index */
33762306a36Sopenharmony_cistatic inline int ethertype_to_npindex(int ethertype)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	switch (ethertype) {
34062306a36Sopenharmony_ci	case ETH_P_IP:
34162306a36Sopenharmony_ci		return NP_IP;
34262306a36Sopenharmony_ci	case ETH_P_IPV6:
34362306a36Sopenharmony_ci		return NP_IPV6;
34462306a36Sopenharmony_ci	case ETH_P_IPX:
34562306a36Sopenharmony_ci		return NP_IPX;
34662306a36Sopenharmony_ci	case ETH_P_PPPTALK:
34762306a36Sopenharmony_ci	case ETH_P_ATALK:
34862306a36Sopenharmony_ci		return NP_AT;
34962306a36Sopenharmony_ci	case ETH_P_MPLS_UC:
35062306a36Sopenharmony_ci		return NP_MPLS_UC;
35162306a36Sopenharmony_ci	case ETH_P_MPLS_MC:
35262306a36Sopenharmony_ci		return NP_MPLS_MC;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci	return -1;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci/* Translates an NP index into an ethertype */
35862306a36Sopenharmony_cistatic const int npindex_to_ethertype[NUM_NP] = {
35962306a36Sopenharmony_ci	ETH_P_IP,
36062306a36Sopenharmony_ci	ETH_P_IPV6,
36162306a36Sopenharmony_ci	ETH_P_IPX,
36262306a36Sopenharmony_ci	ETH_P_PPPTALK,
36362306a36Sopenharmony_ci	ETH_P_MPLS_UC,
36462306a36Sopenharmony_ci	ETH_P_MPLS_MC,
36562306a36Sopenharmony_ci};
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci/*
36862306a36Sopenharmony_ci * Locking shorthand.
36962306a36Sopenharmony_ci */
37062306a36Sopenharmony_ci#define ppp_xmit_lock(ppp)	spin_lock_bh(&(ppp)->wlock)
37162306a36Sopenharmony_ci#define ppp_xmit_unlock(ppp)	spin_unlock_bh(&(ppp)->wlock)
37262306a36Sopenharmony_ci#define ppp_recv_lock(ppp)	spin_lock_bh(&(ppp)->rlock)
37362306a36Sopenharmony_ci#define ppp_recv_unlock(ppp)	spin_unlock_bh(&(ppp)->rlock)
37462306a36Sopenharmony_ci#define ppp_lock(ppp)		do { ppp_xmit_lock(ppp); \
37562306a36Sopenharmony_ci				     ppp_recv_lock(ppp); } while (0)
37662306a36Sopenharmony_ci#define ppp_unlock(ppp)		do { ppp_recv_unlock(ppp); \
37762306a36Sopenharmony_ci				     ppp_xmit_unlock(ppp); } while (0)
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci/*
38062306a36Sopenharmony_ci * /dev/ppp device routines.
38162306a36Sopenharmony_ci * The /dev/ppp device is used by pppd to control the ppp unit.
38262306a36Sopenharmony_ci * It supports the read, write, ioctl and poll functions.
38362306a36Sopenharmony_ci * Open instances of /dev/ppp can be in one of three states:
38462306a36Sopenharmony_ci * unattached, attached to a ppp unit, or attached to a ppp channel.
38562306a36Sopenharmony_ci */
38662306a36Sopenharmony_cistatic int ppp_open(struct inode *inode, struct file *file)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	/*
38962306a36Sopenharmony_ci	 * This could (should?) be enforced by the permissions on /dev/ppp.
39062306a36Sopenharmony_ci	 */
39162306a36Sopenharmony_ci	if (!ns_capable(file->f_cred->user_ns, CAP_NET_ADMIN))
39262306a36Sopenharmony_ci		return -EPERM;
39362306a36Sopenharmony_ci	return 0;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic int ppp_release(struct inode *unused, struct file *file)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct ppp_file *pf = file->private_data;
39962306a36Sopenharmony_ci	struct ppp *ppp;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (pf) {
40262306a36Sopenharmony_ci		file->private_data = NULL;
40362306a36Sopenharmony_ci		if (pf->kind == INTERFACE) {
40462306a36Sopenharmony_ci			ppp = PF_TO_PPP(pf);
40562306a36Sopenharmony_ci			rtnl_lock();
40662306a36Sopenharmony_ci			if (file == ppp->owner)
40762306a36Sopenharmony_ci				unregister_netdevice(ppp->dev);
40862306a36Sopenharmony_ci			rtnl_unlock();
40962306a36Sopenharmony_ci		}
41062306a36Sopenharmony_ci		if (refcount_dec_and_test(&pf->refcnt)) {
41162306a36Sopenharmony_ci			switch (pf->kind) {
41262306a36Sopenharmony_ci			case INTERFACE:
41362306a36Sopenharmony_ci				ppp_destroy_interface(PF_TO_PPP(pf));
41462306a36Sopenharmony_ci				break;
41562306a36Sopenharmony_ci			case CHANNEL:
41662306a36Sopenharmony_ci				ppp_destroy_channel(PF_TO_CHANNEL(pf));
41762306a36Sopenharmony_ci				break;
41862306a36Sopenharmony_ci			}
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci	return 0;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic ssize_t ppp_read(struct file *file, char __user *buf,
42562306a36Sopenharmony_ci			size_t count, loff_t *ppos)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct ppp_file *pf = file->private_data;
42862306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
42962306a36Sopenharmony_ci	ssize_t ret;
43062306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
43162306a36Sopenharmony_ci	struct iovec iov;
43262306a36Sopenharmony_ci	struct iov_iter to;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	ret = count;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (!pf)
43762306a36Sopenharmony_ci		return -ENXIO;
43862306a36Sopenharmony_ci	add_wait_queue(&pf->rwait, &wait);
43962306a36Sopenharmony_ci	for (;;) {
44062306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
44162306a36Sopenharmony_ci		skb = skb_dequeue(&pf->rq);
44262306a36Sopenharmony_ci		if (skb)
44362306a36Sopenharmony_ci			break;
44462306a36Sopenharmony_ci		ret = 0;
44562306a36Sopenharmony_ci		if (pf->dead)
44662306a36Sopenharmony_ci			break;
44762306a36Sopenharmony_ci		if (pf->kind == INTERFACE) {
44862306a36Sopenharmony_ci			/*
44962306a36Sopenharmony_ci			 * Return 0 (EOF) on an interface that has no
45062306a36Sopenharmony_ci			 * channels connected, unless it is looping
45162306a36Sopenharmony_ci			 * network traffic (demand mode).
45262306a36Sopenharmony_ci			 */
45362306a36Sopenharmony_ci			struct ppp *ppp = PF_TO_PPP(pf);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci			ppp_recv_lock(ppp);
45662306a36Sopenharmony_ci			if (ppp->n_channels == 0 &&
45762306a36Sopenharmony_ci			    (ppp->flags & SC_LOOP_TRAFFIC) == 0) {
45862306a36Sopenharmony_ci				ppp_recv_unlock(ppp);
45962306a36Sopenharmony_ci				break;
46062306a36Sopenharmony_ci			}
46162306a36Sopenharmony_ci			ppp_recv_unlock(ppp);
46262306a36Sopenharmony_ci		}
46362306a36Sopenharmony_ci		ret = -EAGAIN;
46462306a36Sopenharmony_ci		if (file->f_flags & O_NONBLOCK)
46562306a36Sopenharmony_ci			break;
46662306a36Sopenharmony_ci		ret = -ERESTARTSYS;
46762306a36Sopenharmony_ci		if (signal_pending(current))
46862306a36Sopenharmony_ci			break;
46962306a36Sopenharmony_ci		schedule();
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci	set_current_state(TASK_RUNNING);
47262306a36Sopenharmony_ci	remove_wait_queue(&pf->rwait, &wait);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (!skb)
47562306a36Sopenharmony_ci		goto out;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	ret = -EOVERFLOW;
47862306a36Sopenharmony_ci	if (skb->len > count)
47962306a36Sopenharmony_ci		goto outf;
48062306a36Sopenharmony_ci	ret = -EFAULT;
48162306a36Sopenharmony_ci	iov.iov_base = buf;
48262306a36Sopenharmony_ci	iov.iov_len = count;
48362306a36Sopenharmony_ci	iov_iter_init(&to, ITER_DEST, &iov, 1, count);
48462306a36Sopenharmony_ci	if (skb_copy_datagram_iter(skb, 0, &to, skb->len))
48562306a36Sopenharmony_ci		goto outf;
48662306a36Sopenharmony_ci	ret = skb->len;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci outf:
48962306a36Sopenharmony_ci	kfree_skb(skb);
49062306a36Sopenharmony_ci out:
49162306a36Sopenharmony_ci	return ret;
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic ssize_t ppp_write(struct file *file, const char __user *buf,
49562306a36Sopenharmony_ci			 size_t count, loff_t *ppos)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	struct ppp_file *pf = file->private_data;
49862306a36Sopenharmony_ci	struct sk_buff *skb;
49962306a36Sopenharmony_ci	ssize_t ret;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (!pf)
50262306a36Sopenharmony_ci		return -ENXIO;
50362306a36Sopenharmony_ci	/* All PPP packets should start with the 2-byte protocol */
50462306a36Sopenharmony_ci	if (count < PPP_PROTO_LEN)
50562306a36Sopenharmony_ci		return -EINVAL;
50662306a36Sopenharmony_ci	ret = -ENOMEM;
50762306a36Sopenharmony_ci	skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL);
50862306a36Sopenharmony_ci	if (!skb)
50962306a36Sopenharmony_ci		goto out;
51062306a36Sopenharmony_ci	skb_reserve(skb, pf->hdrlen);
51162306a36Sopenharmony_ci	ret = -EFAULT;
51262306a36Sopenharmony_ci	if (copy_from_user(skb_put(skb, count), buf, count)) {
51362306a36Sopenharmony_ci		kfree_skb(skb);
51462306a36Sopenharmony_ci		goto out;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	switch (pf->kind) {
51862306a36Sopenharmony_ci	case INTERFACE:
51962306a36Sopenharmony_ci		ppp_xmit_process(PF_TO_PPP(pf), skb);
52062306a36Sopenharmony_ci		break;
52162306a36Sopenharmony_ci	case CHANNEL:
52262306a36Sopenharmony_ci		skb_queue_tail(&pf->xq, skb);
52362306a36Sopenharmony_ci		ppp_channel_push(PF_TO_CHANNEL(pf));
52462306a36Sopenharmony_ci		break;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	ret = count;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci out:
53062306a36Sopenharmony_ci	return ret;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci/* No kernel lock - fine */
53462306a36Sopenharmony_cistatic __poll_t ppp_poll(struct file *file, poll_table *wait)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct ppp_file *pf = file->private_data;
53762306a36Sopenharmony_ci	__poll_t mask;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (!pf)
54062306a36Sopenharmony_ci		return 0;
54162306a36Sopenharmony_ci	poll_wait(file, &pf->rwait, wait);
54262306a36Sopenharmony_ci	mask = EPOLLOUT | EPOLLWRNORM;
54362306a36Sopenharmony_ci	if (skb_peek(&pf->rq))
54462306a36Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
54562306a36Sopenharmony_ci	if (pf->dead)
54662306a36Sopenharmony_ci		mask |= EPOLLHUP;
54762306a36Sopenharmony_ci	else if (pf->kind == INTERFACE) {
54862306a36Sopenharmony_ci		/* see comment in ppp_read */
54962306a36Sopenharmony_ci		struct ppp *ppp = PF_TO_PPP(pf);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		ppp_recv_lock(ppp);
55262306a36Sopenharmony_ci		if (ppp->n_channels == 0 &&
55362306a36Sopenharmony_ci		    (ppp->flags & SC_LOOP_TRAFFIC) == 0)
55462306a36Sopenharmony_ci			mask |= EPOLLIN | EPOLLRDNORM;
55562306a36Sopenharmony_ci		ppp_recv_unlock(ppp);
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return mask;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci#ifdef CONFIG_PPP_FILTER
56262306a36Sopenharmony_cistatic struct bpf_prog *get_filter(struct sock_fprog *uprog)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct sock_fprog_kern fprog;
56562306a36Sopenharmony_ci	struct bpf_prog *res = NULL;
56662306a36Sopenharmony_ci	int err;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (!uprog->len)
56962306a36Sopenharmony_ci		return NULL;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/* uprog->len is unsigned short, so no overflow here */
57262306a36Sopenharmony_ci	fprog.len = uprog->len;
57362306a36Sopenharmony_ci	fprog.filter = memdup_user(uprog->filter,
57462306a36Sopenharmony_ci				   uprog->len * sizeof(struct sock_filter));
57562306a36Sopenharmony_ci	if (IS_ERR(fprog.filter))
57662306a36Sopenharmony_ci		return ERR_CAST(fprog.filter);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	err = bpf_prog_create(&res, &fprog);
57962306a36Sopenharmony_ci	kfree(fprog.filter);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	return err ? ERR_PTR(err) : res;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic struct bpf_prog *ppp_get_filter(struct sock_fprog __user *p)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct sock_fprog uprog;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (copy_from_user(&uprog, p, sizeof(struct sock_fprog)))
58962306a36Sopenharmony_ci		return ERR_PTR(-EFAULT);
59062306a36Sopenharmony_ci	return get_filter(&uprog);
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
59462306a36Sopenharmony_cistruct sock_fprog32 {
59562306a36Sopenharmony_ci	unsigned short len;
59662306a36Sopenharmony_ci	compat_caddr_t filter;
59762306a36Sopenharmony_ci};
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci#define PPPIOCSPASS32		_IOW('t', 71, struct sock_fprog32)
60062306a36Sopenharmony_ci#define PPPIOCSACTIVE32		_IOW('t', 70, struct sock_fprog32)
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic struct bpf_prog *compat_ppp_get_filter(struct sock_fprog32 __user *p)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	struct sock_fprog32 uprog32;
60562306a36Sopenharmony_ci	struct sock_fprog uprog;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	if (copy_from_user(&uprog32, p, sizeof(struct sock_fprog32)))
60862306a36Sopenharmony_ci		return ERR_PTR(-EFAULT);
60962306a36Sopenharmony_ci	uprog.len = uprog32.len;
61062306a36Sopenharmony_ci	uprog.filter = compat_ptr(uprog32.filter);
61162306a36Sopenharmony_ci	return get_filter(&uprog);
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci#endif
61462306a36Sopenharmony_ci#endif
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci/* Bridge one PPP channel to another.
61762306a36Sopenharmony_ci * When two channels are bridged, ppp_input on one channel is redirected to
61862306a36Sopenharmony_ci * the other's ops->start_xmit handler.
61962306a36Sopenharmony_ci * In order to safely bridge channels we must reject channels which are already
62062306a36Sopenharmony_ci * part of a bridge instance, or which form part of an existing unit.
62162306a36Sopenharmony_ci * Once successfully bridged, each channel holds a reference on the other
62262306a36Sopenharmony_ci * to prevent it being freed while the bridge is extant.
62362306a36Sopenharmony_ci */
62462306a36Sopenharmony_cistatic int ppp_bridge_channels(struct channel *pch, struct channel *pchb)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	write_lock_bh(&pch->upl);
62762306a36Sopenharmony_ci	if (pch->ppp ||
62862306a36Sopenharmony_ci	    rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl))) {
62962306a36Sopenharmony_ci		write_unlock_bh(&pch->upl);
63062306a36Sopenharmony_ci		return -EALREADY;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci	refcount_inc(&pchb->file.refcnt);
63362306a36Sopenharmony_ci	rcu_assign_pointer(pch->bridge, pchb);
63462306a36Sopenharmony_ci	write_unlock_bh(&pch->upl);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	write_lock_bh(&pchb->upl);
63762306a36Sopenharmony_ci	if (pchb->ppp ||
63862306a36Sopenharmony_ci	    rcu_dereference_protected(pchb->bridge, lockdep_is_held(&pchb->upl))) {
63962306a36Sopenharmony_ci		write_unlock_bh(&pchb->upl);
64062306a36Sopenharmony_ci		goto err_unset;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci	refcount_inc(&pch->file.refcnt);
64362306a36Sopenharmony_ci	rcu_assign_pointer(pchb->bridge, pch);
64462306a36Sopenharmony_ci	write_unlock_bh(&pchb->upl);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	return 0;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cierr_unset:
64962306a36Sopenharmony_ci	write_lock_bh(&pch->upl);
65062306a36Sopenharmony_ci	/* Re-read pch->bridge with upl held in case it was modified concurrently */
65162306a36Sopenharmony_ci	pchb = rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl));
65262306a36Sopenharmony_ci	RCU_INIT_POINTER(pch->bridge, NULL);
65362306a36Sopenharmony_ci	write_unlock_bh(&pch->upl);
65462306a36Sopenharmony_ci	synchronize_rcu();
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	if (pchb)
65762306a36Sopenharmony_ci		if (refcount_dec_and_test(&pchb->file.refcnt))
65862306a36Sopenharmony_ci			ppp_destroy_channel(pchb);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	return -EALREADY;
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic int ppp_unbridge_channels(struct channel *pch)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	struct channel *pchb, *pchbb;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	write_lock_bh(&pch->upl);
66862306a36Sopenharmony_ci	pchb = rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl));
66962306a36Sopenharmony_ci	if (!pchb) {
67062306a36Sopenharmony_ci		write_unlock_bh(&pch->upl);
67162306a36Sopenharmony_ci		return -EINVAL;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci	RCU_INIT_POINTER(pch->bridge, NULL);
67462306a36Sopenharmony_ci	write_unlock_bh(&pch->upl);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/* Only modify pchb if phcb->bridge points back to pch.
67762306a36Sopenharmony_ci	 * If not, it implies that there has been a race unbridging (and possibly
67862306a36Sopenharmony_ci	 * even rebridging) pchb.  We should leave pchb alone to avoid either a
67962306a36Sopenharmony_ci	 * refcount underflow, or breaking another established bridge instance.
68062306a36Sopenharmony_ci	 */
68162306a36Sopenharmony_ci	write_lock_bh(&pchb->upl);
68262306a36Sopenharmony_ci	pchbb = rcu_dereference_protected(pchb->bridge, lockdep_is_held(&pchb->upl));
68362306a36Sopenharmony_ci	if (pchbb == pch)
68462306a36Sopenharmony_ci		RCU_INIT_POINTER(pchb->bridge, NULL);
68562306a36Sopenharmony_ci	write_unlock_bh(&pchb->upl);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	synchronize_rcu();
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (pchbb == pch)
69062306a36Sopenharmony_ci		if (refcount_dec_and_test(&pch->file.refcnt))
69162306a36Sopenharmony_ci			ppp_destroy_channel(pch);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (refcount_dec_and_test(&pchb->file.refcnt))
69462306a36Sopenharmony_ci		ppp_destroy_channel(pchb);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	return 0;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	struct ppp_file *pf;
70262306a36Sopenharmony_ci	struct ppp *ppp;
70362306a36Sopenharmony_ci	int err = -EFAULT, val, val2, i;
70462306a36Sopenharmony_ci	struct ppp_idle32 idle32;
70562306a36Sopenharmony_ci	struct ppp_idle64 idle64;
70662306a36Sopenharmony_ci	struct npioctl npi;
70762306a36Sopenharmony_ci	int unit, cflags;
70862306a36Sopenharmony_ci	struct slcompress *vj;
70962306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
71062306a36Sopenharmony_ci	int __user *p = argp;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	mutex_lock(&ppp_mutex);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	pf = file->private_data;
71562306a36Sopenharmony_ci	if (!pf) {
71662306a36Sopenharmony_ci		err = ppp_unattached_ioctl(current->nsproxy->net_ns,
71762306a36Sopenharmony_ci					   pf, file, cmd, arg);
71862306a36Sopenharmony_ci		goto out;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	if (cmd == PPPIOCDETACH) {
72262306a36Sopenharmony_ci		/*
72362306a36Sopenharmony_ci		 * PPPIOCDETACH is no longer supported as it was heavily broken,
72462306a36Sopenharmony_ci		 * and is only known to have been used by pppd older than
72562306a36Sopenharmony_ci		 * ppp-2.4.2 (released November 2003).
72662306a36Sopenharmony_ci		 */
72762306a36Sopenharmony_ci		pr_warn_once("%s (%d) used obsolete PPPIOCDETACH ioctl\n",
72862306a36Sopenharmony_ci			     current->comm, current->pid);
72962306a36Sopenharmony_ci		err = -EINVAL;
73062306a36Sopenharmony_ci		goto out;
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (pf->kind == CHANNEL) {
73462306a36Sopenharmony_ci		struct channel *pch, *pchb;
73562306a36Sopenharmony_ci		struct ppp_channel *chan;
73662306a36Sopenharmony_ci		struct ppp_net *pn;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci		pch = PF_TO_CHANNEL(pf);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		switch (cmd) {
74162306a36Sopenharmony_ci		case PPPIOCCONNECT:
74262306a36Sopenharmony_ci			if (get_user(unit, p))
74362306a36Sopenharmony_ci				break;
74462306a36Sopenharmony_ci			err = ppp_connect_channel(pch, unit);
74562306a36Sopenharmony_ci			break;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		case PPPIOCDISCONN:
74862306a36Sopenharmony_ci			err = ppp_disconnect_channel(pch);
74962306a36Sopenharmony_ci			break;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci		case PPPIOCBRIDGECHAN:
75262306a36Sopenharmony_ci			if (get_user(unit, p))
75362306a36Sopenharmony_ci				break;
75462306a36Sopenharmony_ci			err = -ENXIO;
75562306a36Sopenharmony_ci			pn = ppp_pernet(current->nsproxy->net_ns);
75662306a36Sopenharmony_ci			spin_lock_bh(&pn->all_channels_lock);
75762306a36Sopenharmony_ci			pchb = ppp_find_channel(pn, unit);
75862306a36Sopenharmony_ci			/* Hold a reference to prevent pchb being freed while
75962306a36Sopenharmony_ci			 * we establish the bridge.
76062306a36Sopenharmony_ci			 */
76162306a36Sopenharmony_ci			if (pchb)
76262306a36Sopenharmony_ci				refcount_inc(&pchb->file.refcnt);
76362306a36Sopenharmony_ci			spin_unlock_bh(&pn->all_channels_lock);
76462306a36Sopenharmony_ci			if (!pchb)
76562306a36Sopenharmony_ci				break;
76662306a36Sopenharmony_ci			err = ppp_bridge_channels(pch, pchb);
76762306a36Sopenharmony_ci			/* Drop earlier refcount now bridge establishment is complete */
76862306a36Sopenharmony_ci			if (refcount_dec_and_test(&pchb->file.refcnt))
76962306a36Sopenharmony_ci				ppp_destroy_channel(pchb);
77062306a36Sopenharmony_ci			break;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		case PPPIOCUNBRIDGECHAN:
77362306a36Sopenharmony_ci			err = ppp_unbridge_channels(pch);
77462306a36Sopenharmony_ci			break;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci		default:
77762306a36Sopenharmony_ci			down_read(&pch->chan_sem);
77862306a36Sopenharmony_ci			chan = pch->chan;
77962306a36Sopenharmony_ci			err = -ENOTTY;
78062306a36Sopenharmony_ci			if (chan && chan->ops->ioctl)
78162306a36Sopenharmony_ci				err = chan->ops->ioctl(chan, cmd, arg);
78262306a36Sopenharmony_ci			up_read(&pch->chan_sem);
78362306a36Sopenharmony_ci		}
78462306a36Sopenharmony_ci		goto out;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (pf->kind != INTERFACE) {
78862306a36Sopenharmony_ci		/* can't happen */
78962306a36Sopenharmony_ci		pr_err("PPP: not interface or channel??\n");
79062306a36Sopenharmony_ci		err = -EINVAL;
79162306a36Sopenharmony_ci		goto out;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	ppp = PF_TO_PPP(pf);
79562306a36Sopenharmony_ci	switch (cmd) {
79662306a36Sopenharmony_ci	case PPPIOCSMRU:
79762306a36Sopenharmony_ci		if (get_user(val, p))
79862306a36Sopenharmony_ci			break;
79962306a36Sopenharmony_ci		ppp->mru = val;
80062306a36Sopenharmony_ci		err = 0;
80162306a36Sopenharmony_ci		break;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	case PPPIOCSFLAGS:
80462306a36Sopenharmony_ci		if (get_user(val, p))
80562306a36Sopenharmony_ci			break;
80662306a36Sopenharmony_ci		ppp_lock(ppp);
80762306a36Sopenharmony_ci		cflags = ppp->flags & ~val;
80862306a36Sopenharmony_ci#ifdef CONFIG_PPP_MULTILINK
80962306a36Sopenharmony_ci		if (!(ppp->flags & SC_MULTILINK) && (val & SC_MULTILINK))
81062306a36Sopenharmony_ci			ppp->nextseq = 0;
81162306a36Sopenharmony_ci#endif
81262306a36Sopenharmony_ci		ppp->flags = val & SC_FLAG_BITS;
81362306a36Sopenharmony_ci		ppp_unlock(ppp);
81462306a36Sopenharmony_ci		if (cflags & SC_CCP_OPEN)
81562306a36Sopenharmony_ci			ppp_ccp_closed(ppp);
81662306a36Sopenharmony_ci		err = 0;
81762306a36Sopenharmony_ci		break;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	case PPPIOCGFLAGS:
82062306a36Sopenharmony_ci		val = ppp->flags | ppp->xstate | ppp->rstate;
82162306a36Sopenharmony_ci		if (put_user(val, p))
82262306a36Sopenharmony_ci			break;
82362306a36Sopenharmony_ci		err = 0;
82462306a36Sopenharmony_ci		break;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	case PPPIOCSCOMPRESS:
82762306a36Sopenharmony_ci	{
82862306a36Sopenharmony_ci		struct ppp_option_data data;
82962306a36Sopenharmony_ci		if (copy_from_user(&data, argp, sizeof(data)))
83062306a36Sopenharmony_ci			err = -EFAULT;
83162306a36Sopenharmony_ci		else
83262306a36Sopenharmony_ci			err = ppp_set_compress(ppp, &data);
83362306a36Sopenharmony_ci		break;
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci	case PPPIOCGUNIT:
83662306a36Sopenharmony_ci		if (put_user(ppp->file.index, p))
83762306a36Sopenharmony_ci			break;
83862306a36Sopenharmony_ci		err = 0;
83962306a36Sopenharmony_ci		break;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	case PPPIOCSDEBUG:
84262306a36Sopenharmony_ci		if (get_user(val, p))
84362306a36Sopenharmony_ci			break;
84462306a36Sopenharmony_ci		ppp->debug = val;
84562306a36Sopenharmony_ci		err = 0;
84662306a36Sopenharmony_ci		break;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	case PPPIOCGDEBUG:
84962306a36Sopenharmony_ci		if (put_user(ppp->debug, p))
85062306a36Sopenharmony_ci			break;
85162306a36Sopenharmony_ci		err = 0;
85262306a36Sopenharmony_ci		break;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	case PPPIOCGIDLE32:
85562306a36Sopenharmony_ci                idle32.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
85662306a36Sopenharmony_ci                idle32.recv_idle = (jiffies - ppp->last_recv) / HZ;
85762306a36Sopenharmony_ci                if (copy_to_user(argp, &idle32, sizeof(idle32)))
85862306a36Sopenharmony_ci			break;
85962306a36Sopenharmony_ci		err = 0;
86062306a36Sopenharmony_ci		break;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	case PPPIOCGIDLE64:
86362306a36Sopenharmony_ci		idle64.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
86462306a36Sopenharmony_ci		idle64.recv_idle = (jiffies - ppp->last_recv) / HZ;
86562306a36Sopenharmony_ci		if (copy_to_user(argp, &idle64, sizeof(idle64)))
86662306a36Sopenharmony_ci			break;
86762306a36Sopenharmony_ci		err = 0;
86862306a36Sopenharmony_ci		break;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	case PPPIOCSMAXCID:
87162306a36Sopenharmony_ci		if (get_user(val, p))
87262306a36Sopenharmony_ci			break;
87362306a36Sopenharmony_ci		val2 = 15;
87462306a36Sopenharmony_ci		if ((val >> 16) != 0) {
87562306a36Sopenharmony_ci			val2 = val >> 16;
87662306a36Sopenharmony_ci			val &= 0xffff;
87762306a36Sopenharmony_ci		}
87862306a36Sopenharmony_ci		vj = slhc_init(val2+1, val+1);
87962306a36Sopenharmony_ci		if (IS_ERR(vj)) {
88062306a36Sopenharmony_ci			err = PTR_ERR(vj);
88162306a36Sopenharmony_ci			break;
88262306a36Sopenharmony_ci		}
88362306a36Sopenharmony_ci		ppp_lock(ppp);
88462306a36Sopenharmony_ci		if (ppp->vj)
88562306a36Sopenharmony_ci			slhc_free(ppp->vj);
88662306a36Sopenharmony_ci		ppp->vj = vj;
88762306a36Sopenharmony_ci		ppp_unlock(ppp);
88862306a36Sopenharmony_ci		err = 0;
88962306a36Sopenharmony_ci		break;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	case PPPIOCGNPMODE:
89262306a36Sopenharmony_ci	case PPPIOCSNPMODE:
89362306a36Sopenharmony_ci		if (copy_from_user(&npi, argp, sizeof(npi)))
89462306a36Sopenharmony_ci			break;
89562306a36Sopenharmony_ci		err = proto_to_npindex(npi.protocol);
89662306a36Sopenharmony_ci		if (err < 0)
89762306a36Sopenharmony_ci			break;
89862306a36Sopenharmony_ci		i = err;
89962306a36Sopenharmony_ci		if (cmd == PPPIOCGNPMODE) {
90062306a36Sopenharmony_ci			err = -EFAULT;
90162306a36Sopenharmony_ci			npi.mode = ppp->npmode[i];
90262306a36Sopenharmony_ci			if (copy_to_user(argp, &npi, sizeof(npi)))
90362306a36Sopenharmony_ci				break;
90462306a36Sopenharmony_ci		} else {
90562306a36Sopenharmony_ci			ppp->npmode[i] = npi.mode;
90662306a36Sopenharmony_ci			/* we may be able to transmit more packets now (??) */
90762306a36Sopenharmony_ci			netif_wake_queue(ppp->dev);
90862306a36Sopenharmony_ci		}
90962306a36Sopenharmony_ci		err = 0;
91062306a36Sopenharmony_ci		break;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci#ifdef CONFIG_PPP_FILTER
91362306a36Sopenharmony_ci	case PPPIOCSPASS:
91462306a36Sopenharmony_ci	case PPPIOCSACTIVE:
91562306a36Sopenharmony_ci	{
91662306a36Sopenharmony_ci		struct bpf_prog *filter = ppp_get_filter(argp);
91762306a36Sopenharmony_ci		struct bpf_prog **which;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci		if (IS_ERR(filter)) {
92062306a36Sopenharmony_ci			err = PTR_ERR(filter);
92162306a36Sopenharmony_ci			break;
92262306a36Sopenharmony_ci		}
92362306a36Sopenharmony_ci		if (cmd == PPPIOCSPASS)
92462306a36Sopenharmony_ci			which = &ppp->pass_filter;
92562306a36Sopenharmony_ci		else
92662306a36Sopenharmony_ci			which = &ppp->active_filter;
92762306a36Sopenharmony_ci		ppp_lock(ppp);
92862306a36Sopenharmony_ci		if (*which)
92962306a36Sopenharmony_ci			bpf_prog_destroy(*which);
93062306a36Sopenharmony_ci		*which = filter;
93162306a36Sopenharmony_ci		ppp_unlock(ppp);
93262306a36Sopenharmony_ci		err = 0;
93362306a36Sopenharmony_ci		break;
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci#endif /* CONFIG_PPP_FILTER */
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci#ifdef CONFIG_PPP_MULTILINK
93862306a36Sopenharmony_ci	case PPPIOCSMRRU:
93962306a36Sopenharmony_ci		if (get_user(val, p))
94062306a36Sopenharmony_ci			break;
94162306a36Sopenharmony_ci		ppp_recv_lock(ppp);
94262306a36Sopenharmony_ci		ppp->mrru = val;
94362306a36Sopenharmony_ci		ppp_recv_unlock(ppp);
94462306a36Sopenharmony_ci		err = 0;
94562306a36Sopenharmony_ci		break;
94662306a36Sopenharmony_ci#endif /* CONFIG_PPP_MULTILINK */
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	default:
94962306a36Sopenharmony_ci		err = -ENOTTY;
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ciout:
95362306a36Sopenharmony_ci	mutex_unlock(&ppp_mutex);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	return err;
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
95962306a36Sopenharmony_cistruct ppp_option_data32 {
96062306a36Sopenharmony_ci	compat_uptr_t		ptr;
96162306a36Sopenharmony_ci	u32			length;
96262306a36Sopenharmony_ci	compat_int_t		transmit;
96362306a36Sopenharmony_ci};
96462306a36Sopenharmony_ci#define PPPIOCSCOMPRESS32	_IOW('t', 77, struct ppp_option_data32)
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_cistatic long ppp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	struct ppp_file *pf;
96962306a36Sopenharmony_ci	int err = -ENOIOCTLCMD;
97062306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	mutex_lock(&ppp_mutex);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	pf = file->private_data;
97562306a36Sopenharmony_ci	if (pf && pf->kind == INTERFACE) {
97662306a36Sopenharmony_ci		struct ppp *ppp = PF_TO_PPP(pf);
97762306a36Sopenharmony_ci		switch (cmd) {
97862306a36Sopenharmony_ci#ifdef CONFIG_PPP_FILTER
97962306a36Sopenharmony_ci		case PPPIOCSPASS32:
98062306a36Sopenharmony_ci		case PPPIOCSACTIVE32:
98162306a36Sopenharmony_ci		{
98262306a36Sopenharmony_ci			struct bpf_prog *filter = compat_ppp_get_filter(argp);
98362306a36Sopenharmony_ci			struct bpf_prog **which;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci			if (IS_ERR(filter)) {
98662306a36Sopenharmony_ci				err = PTR_ERR(filter);
98762306a36Sopenharmony_ci				break;
98862306a36Sopenharmony_ci			}
98962306a36Sopenharmony_ci			if (cmd == PPPIOCSPASS32)
99062306a36Sopenharmony_ci				which = &ppp->pass_filter;
99162306a36Sopenharmony_ci			else
99262306a36Sopenharmony_ci				which = &ppp->active_filter;
99362306a36Sopenharmony_ci			ppp_lock(ppp);
99462306a36Sopenharmony_ci			if (*which)
99562306a36Sopenharmony_ci				bpf_prog_destroy(*which);
99662306a36Sopenharmony_ci			*which = filter;
99762306a36Sopenharmony_ci			ppp_unlock(ppp);
99862306a36Sopenharmony_ci			err = 0;
99962306a36Sopenharmony_ci			break;
100062306a36Sopenharmony_ci		}
100162306a36Sopenharmony_ci#endif /* CONFIG_PPP_FILTER */
100262306a36Sopenharmony_ci		case PPPIOCSCOMPRESS32:
100362306a36Sopenharmony_ci		{
100462306a36Sopenharmony_ci			struct ppp_option_data32 data32;
100562306a36Sopenharmony_ci			if (copy_from_user(&data32, argp, sizeof(data32))) {
100662306a36Sopenharmony_ci				err = -EFAULT;
100762306a36Sopenharmony_ci			} else {
100862306a36Sopenharmony_ci				struct ppp_option_data data = {
100962306a36Sopenharmony_ci					.ptr = compat_ptr(data32.ptr),
101062306a36Sopenharmony_ci					.length = data32.length,
101162306a36Sopenharmony_ci					.transmit = data32.transmit
101262306a36Sopenharmony_ci				};
101362306a36Sopenharmony_ci				err = ppp_set_compress(ppp, &data);
101462306a36Sopenharmony_ci			}
101562306a36Sopenharmony_ci			break;
101662306a36Sopenharmony_ci		}
101762306a36Sopenharmony_ci		}
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci	mutex_unlock(&ppp_mutex);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	/* all other commands have compatible arguments */
102262306a36Sopenharmony_ci	if (err == -ENOIOCTLCMD)
102362306a36Sopenharmony_ci		err = ppp_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	return err;
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci#endif
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_cistatic int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
103062306a36Sopenharmony_ci			struct file *file, unsigned int cmd, unsigned long arg)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	int unit, err = -EFAULT;
103362306a36Sopenharmony_ci	struct ppp *ppp;
103462306a36Sopenharmony_ci	struct channel *chan;
103562306a36Sopenharmony_ci	struct ppp_net *pn;
103662306a36Sopenharmony_ci	int __user *p = (int __user *)arg;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	switch (cmd) {
103962306a36Sopenharmony_ci	case PPPIOCNEWUNIT:
104062306a36Sopenharmony_ci		/* Create a new ppp unit */
104162306a36Sopenharmony_ci		if (get_user(unit, p))
104262306a36Sopenharmony_ci			break;
104362306a36Sopenharmony_ci		err = ppp_create_interface(net, file, &unit);
104462306a36Sopenharmony_ci		if (err < 0)
104562306a36Sopenharmony_ci			break;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci		err = -EFAULT;
104862306a36Sopenharmony_ci		if (put_user(unit, p))
104962306a36Sopenharmony_ci			break;
105062306a36Sopenharmony_ci		err = 0;
105162306a36Sopenharmony_ci		break;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	case PPPIOCATTACH:
105462306a36Sopenharmony_ci		/* Attach to an existing ppp unit */
105562306a36Sopenharmony_ci		if (get_user(unit, p))
105662306a36Sopenharmony_ci			break;
105762306a36Sopenharmony_ci		err = -ENXIO;
105862306a36Sopenharmony_ci		pn = ppp_pernet(net);
105962306a36Sopenharmony_ci		mutex_lock(&pn->all_ppp_mutex);
106062306a36Sopenharmony_ci		ppp = ppp_find_unit(pn, unit);
106162306a36Sopenharmony_ci		if (ppp) {
106262306a36Sopenharmony_ci			refcount_inc(&ppp->file.refcnt);
106362306a36Sopenharmony_ci			file->private_data = &ppp->file;
106462306a36Sopenharmony_ci			err = 0;
106562306a36Sopenharmony_ci		}
106662306a36Sopenharmony_ci		mutex_unlock(&pn->all_ppp_mutex);
106762306a36Sopenharmony_ci		break;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	case PPPIOCATTCHAN:
107062306a36Sopenharmony_ci		if (get_user(unit, p))
107162306a36Sopenharmony_ci			break;
107262306a36Sopenharmony_ci		err = -ENXIO;
107362306a36Sopenharmony_ci		pn = ppp_pernet(net);
107462306a36Sopenharmony_ci		spin_lock_bh(&pn->all_channels_lock);
107562306a36Sopenharmony_ci		chan = ppp_find_channel(pn, unit);
107662306a36Sopenharmony_ci		if (chan) {
107762306a36Sopenharmony_ci			refcount_inc(&chan->file.refcnt);
107862306a36Sopenharmony_ci			file->private_data = &chan->file;
107962306a36Sopenharmony_ci			err = 0;
108062306a36Sopenharmony_ci		}
108162306a36Sopenharmony_ci		spin_unlock_bh(&pn->all_channels_lock);
108262306a36Sopenharmony_ci		break;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	default:
108562306a36Sopenharmony_ci		err = -ENOTTY;
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	return err;
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cistatic const struct file_operations ppp_device_fops = {
109262306a36Sopenharmony_ci	.owner		= THIS_MODULE,
109362306a36Sopenharmony_ci	.read		= ppp_read,
109462306a36Sopenharmony_ci	.write		= ppp_write,
109562306a36Sopenharmony_ci	.poll		= ppp_poll,
109662306a36Sopenharmony_ci	.unlocked_ioctl	= ppp_ioctl,
109762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
109862306a36Sopenharmony_ci	.compat_ioctl	= ppp_compat_ioctl,
109962306a36Sopenharmony_ci#endif
110062306a36Sopenharmony_ci	.open		= ppp_open,
110162306a36Sopenharmony_ci	.release	= ppp_release,
110262306a36Sopenharmony_ci	.llseek		= noop_llseek,
110362306a36Sopenharmony_ci};
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_cistatic __net_init int ppp_init_net(struct net *net)
110662306a36Sopenharmony_ci{
110762306a36Sopenharmony_ci	struct ppp_net *pn = net_generic(net, ppp_net_id);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	idr_init(&pn->units_idr);
111062306a36Sopenharmony_ci	mutex_init(&pn->all_ppp_mutex);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	INIT_LIST_HEAD(&pn->all_channels);
111362306a36Sopenharmony_ci	INIT_LIST_HEAD(&pn->new_channels);
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	spin_lock_init(&pn->all_channels_lock);
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	return 0;
111862306a36Sopenharmony_ci}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic __net_exit void ppp_exit_net(struct net *net)
112162306a36Sopenharmony_ci{
112262306a36Sopenharmony_ci	struct ppp_net *pn = net_generic(net, ppp_net_id);
112362306a36Sopenharmony_ci	struct net_device *dev;
112462306a36Sopenharmony_ci	struct net_device *aux;
112562306a36Sopenharmony_ci	struct ppp *ppp;
112662306a36Sopenharmony_ci	LIST_HEAD(list);
112762306a36Sopenharmony_ci	int id;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	rtnl_lock();
113062306a36Sopenharmony_ci	for_each_netdev_safe(net, dev, aux) {
113162306a36Sopenharmony_ci		if (dev->netdev_ops == &ppp_netdev_ops)
113262306a36Sopenharmony_ci			unregister_netdevice_queue(dev, &list);
113362306a36Sopenharmony_ci	}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	idr_for_each_entry(&pn->units_idr, ppp, id)
113662306a36Sopenharmony_ci		/* Skip devices already unregistered by previous loop */
113762306a36Sopenharmony_ci		if (!net_eq(dev_net(ppp->dev), net))
113862306a36Sopenharmony_ci			unregister_netdevice_queue(ppp->dev, &list);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	unregister_netdevice_many(&list);
114162306a36Sopenharmony_ci	rtnl_unlock();
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	mutex_destroy(&pn->all_ppp_mutex);
114462306a36Sopenharmony_ci	idr_destroy(&pn->units_idr);
114562306a36Sopenharmony_ci	WARN_ON_ONCE(!list_empty(&pn->all_channels));
114662306a36Sopenharmony_ci	WARN_ON_ONCE(!list_empty(&pn->new_channels));
114762306a36Sopenharmony_ci}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic struct pernet_operations ppp_net_ops = {
115062306a36Sopenharmony_ci	.init = ppp_init_net,
115162306a36Sopenharmony_ci	.exit = ppp_exit_net,
115262306a36Sopenharmony_ci	.id   = &ppp_net_id,
115362306a36Sopenharmony_ci	.size = sizeof(struct ppp_net),
115462306a36Sopenharmony_ci};
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_cistatic int ppp_unit_register(struct ppp *ppp, int unit, bool ifname_is_set)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	struct ppp_net *pn = ppp_pernet(ppp->ppp_net);
115962306a36Sopenharmony_ci	int ret;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	mutex_lock(&pn->all_ppp_mutex);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (unit < 0) {
116462306a36Sopenharmony_ci		ret = unit_get(&pn->units_idr, ppp, 0);
116562306a36Sopenharmony_ci		if (ret < 0)
116662306a36Sopenharmony_ci			goto err;
116762306a36Sopenharmony_ci		if (!ifname_is_set) {
116862306a36Sopenharmony_ci			while (1) {
116962306a36Sopenharmony_ci				snprintf(ppp->dev->name, IFNAMSIZ, "ppp%i", ret);
117062306a36Sopenharmony_ci				if (!netdev_name_in_use(ppp->ppp_net, ppp->dev->name))
117162306a36Sopenharmony_ci					break;
117262306a36Sopenharmony_ci				unit_put(&pn->units_idr, ret);
117362306a36Sopenharmony_ci				ret = unit_get(&pn->units_idr, ppp, ret + 1);
117462306a36Sopenharmony_ci				if (ret < 0)
117562306a36Sopenharmony_ci					goto err;
117662306a36Sopenharmony_ci			}
117762306a36Sopenharmony_ci		}
117862306a36Sopenharmony_ci	} else {
117962306a36Sopenharmony_ci		/* Caller asked for a specific unit number. Fail with -EEXIST
118062306a36Sopenharmony_ci		 * if unavailable. For backward compatibility, return -EEXIST
118162306a36Sopenharmony_ci		 * too if idr allocation fails; this makes pppd retry without
118262306a36Sopenharmony_ci		 * requesting a specific unit number.
118362306a36Sopenharmony_ci		 */
118462306a36Sopenharmony_ci		if (unit_find(&pn->units_idr, unit)) {
118562306a36Sopenharmony_ci			ret = -EEXIST;
118662306a36Sopenharmony_ci			goto err;
118762306a36Sopenharmony_ci		}
118862306a36Sopenharmony_ci		ret = unit_set(&pn->units_idr, ppp, unit);
118962306a36Sopenharmony_ci		if (ret < 0) {
119062306a36Sopenharmony_ci			/* Rewrite error for backward compatibility */
119162306a36Sopenharmony_ci			ret = -EEXIST;
119262306a36Sopenharmony_ci			goto err;
119362306a36Sopenharmony_ci		}
119462306a36Sopenharmony_ci	}
119562306a36Sopenharmony_ci	ppp->file.index = ret;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	if (!ifname_is_set)
119862306a36Sopenharmony_ci		snprintf(ppp->dev->name, IFNAMSIZ, "ppp%i", ppp->file.index);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	mutex_unlock(&pn->all_ppp_mutex);
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	ret = register_netdevice(ppp->dev);
120362306a36Sopenharmony_ci	if (ret < 0)
120462306a36Sopenharmony_ci		goto err_unit;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	atomic_inc(&ppp_unit_count);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	return 0;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cierr_unit:
121162306a36Sopenharmony_ci	mutex_lock(&pn->all_ppp_mutex);
121262306a36Sopenharmony_ci	unit_put(&pn->units_idr, ppp->file.index);
121362306a36Sopenharmony_cierr:
121462306a36Sopenharmony_ci	mutex_unlock(&pn->all_ppp_mutex);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	return ret;
121762306a36Sopenharmony_ci}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_cistatic int ppp_dev_configure(struct net *src_net, struct net_device *dev,
122062306a36Sopenharmony_ci			     const struct ppp_config *conf)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	struct ppp *ppp = netdev_priv(dev);
122362306a36Sopenharmony_ci	int indx;
122462306a36Sopenharmony_ci	int err;
122562306a36Sopenharmony_ci	int cpu;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	ppp->dev = dev;
122862306a36Sopenharmony_ci	ppp->ppp_net = src_net;
122962306a36Sopenharmony_ci	ppp->mru = PPP_MRU;
123062306a36Sopenharmony_ci	ppp->owner = conf->file;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	init_ppp_file(&ppp->file, INTERFACE);
123362306a36Sopenharmony_ci	ppp->file.hdrlen = PPP_HDRLEN - 2; /* don't count proto bytes */
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	for (indx = 0; indx < NUM_NP; ++indx)
123662306a36Sopenharmony_ci		ppp->npmode[indx] = NPMODE_PASS;
123762306a36Sopenharmony_ci	INIT_LIST_HEAD(&ppp->channels);
123862306a36Sopenharmony_ci	spin_lock_init(&ppp->rlock);
123962306a36Sopenharmony_ci	spin_lock_init(&ppp->wlock);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	ppp->xmit_recursion = alloc_percpu(int);
124262306a36Sopenharmony_ci	if (!ppp->xmit_recursion) {
124362306a36Sopenharmony_ci		err = -ENOMEM;
124462306a36Sopenharmony_ci		goto err1;
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci	for_each_possible_cpu(cpu)
124762306a36Sopenharmony_ci		(*per_cpu_ptr(ppp->xmit_recursion, cpu)) = 0;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci#ifdef CONFIG_PPP_MULTILINK
125062306a36Sopenharmony_ci	ppp->minseq = -1;
125162306a36Sopenharmony_ci	skb_queue_head_init(&ppp->mrq);
125262306a36Sopenharmony_ci#endif /* CONFIG_PPP_MULTILINK */
125362306a36Sopenharmony_ci#ifdef CONFIG_PPP_FILTER
125462306a36Sopenharmony_ci	ppp->pass_filter = NULL;
125562306a36Sopenharmony_ci	ppp->active_filter = NULL;
125662306a36Sopenharmony_ci#endif /* CONFIG_PPP_FILTER */
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	err = ppp_unit_register(ppp, conf->unit, conf->ifname_is_set);
125962306a36Sopenharmony_ci	if (err < 0)
126062306a36Sopenharmony_ci		goto err2;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	conf->file->private_data = &ppp->file;
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	return 0;
126562306a36Sopenharmony_cierr2:
126662306a36Sopenharmony_ci	free_percpu(ppp->xmit_recursion);
126762306a36Sopenharmony_cierr1:
126862306a36Sopenharmony_ci	return err;
126962306a36Sopenharmony_ci}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_cistatic const struct nla_policy ppp_nl_policy[IFLA_PPP_MAX + 1] = {
127262306a36Sopenharmony_ci	[IFLA_PPP_DEV_FD]	= { .type = NLA_S32 },
127362306a36Sopenharmony_ci};
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cistatic int ppp_nl_validate(struct nlattr *tb[], struct nlattr *data[],
127662306a36Sopenharmony_ci			   struct netlink_ext_ack *extack)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	if (!data)
127962306a36Sopenharmony_ci		return -EINVAL;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	if (!data[IFLA_PPP_DEV_FD])
128262306a36Sopenharmony_ci		return -EINVAL;
128362306a36Sopenharmony_ci	if (nla_get_s32(data[IFLA_PPP_DEV_FD]) < 0)
128462306a36Sopenharmony_ci		return -EBADF;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	return 0;
128762306a36Sopenharmony_ci}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_cistatic int ppp_nl_newlink(struct net *src_net, struct net_device *dev,
129062306a36Sopenharmony_ci			  struct nlattr *tb[], struct nlattr *data[],
129162306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
129262306a36Sopenharmony_ci{
129362306a36Sopenharmony_ci	struct ppp_config conf = {
129462306a36Sopenharmony_ci		.unit = -1,
129562306a36Sopenharmony_ci		.ifname_is_set = true,
129662306a36Sopenharmony_ci	};
129762306a36Sopenharmony_ci	struct file *file;
129862306a36Sopenharmony_ci	int err;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	file = fget(nla_get_s32(data[IFLA_PPP_DEV_FD]));
130162306a36Sopenharmony_ci	if (!file)
130262306a36Sopenharmony_ci		return -EBADF;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	/* rtnl_lock is already held here, but ppp_create_interface() locks
130562306a36Sopenharmony_ci	 * ppp_mutex before holding rtnl_lock. Using mutex_trylock() avoids
130662306a36Sopenharmony_ci	 * possible deadlock due to lock order inversion, at the cost of
130762306a36Sopenharmony_ci	 * pushing the problem back to userspace.
130862306a36Sopenharmony_ci	 */
130962306a36Sopenharmony_ci	if (!mutex_trylock(&ppp_mutex)) {
131062306a36Sopenharmony_ci		err = -EBUSY;
131162306a36Sopenharmony_ci		goto out;
131262306a36Sopenharmony_ci	}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	if (file->f_op != &ppp_device_fops || file->private_data) {
131562306a36Sopenharmony_ci		err = -EBADF;
131662306a36Sopenharmony_ci		goto out_unlock;
131762306a36Sopenharmony_ci	}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	conf.file = file;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	/* Don't use device name generated by the rtnetlink layer when ifname
132262306a36Sopenharmony_ci	 * isn't specified. Let ppp_dev_configure() set the device name using
132362306a36Sopenharmony_ci	 * the PPP unit identifer as suffix (i.e. ppp<unit_id>). This allows
132462306a36Sopenharmony_ci	 * userspace to infer the device name using to the PPPIOCGUNIT ioctl.
132562306a36Sopenharmony_ci	 */
132662306a36Sopenharmony_ci	if (!tb[IFLA_IFNAME] || !nla_len(tb[IFLA_IFNAME]) || !*(char *)nla_data(tb[IFLA_IFNAME]))
132762306a36Sopenharmony_ci		conf.ifname_is_set = false;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	err = ppp_dev_configure(src_net, dev, &conf);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ciout_unlock:
133262306a36Sopenharmony_ci	mutex_unlock(&ppp_mutex);
133362306a36Sopenharmony_ciout:
133462306a36Sopenharmony_ci	fput(file);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	return err;
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_cistatic void ppp_nl_dellink(struct net_device *dev, struct list_head *head)
134062306a36Sopenharmony_ci{
134162306a36Sopenharmony_ci	unregister_netdevice_queue(dev, head);
134262306a36Sopenharmony_ci}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_cistatic size_t ppp_nl_get_size(const struct net_device *dev)
134562306a36Sopenharmony_ci{
134662306a36Sopenharmony_ci	return 0;
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_cistatic int ppp_nl_fill_info(struct sk_buff *skb, const struct net_device *dev)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	return 0;
135262306a36Sopenharmony_ci}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_cistatic struct net *ppp_nl_get_link_net(const struct net_device *dev)
135562306a36Sopenharmony_ci{
135662306a36Sopenharmony_ci	struct ppp *ppp = netdev_priv(dev);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	return ppp->ppp_net;
135962306a36Sopenharmony_ci}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_cistatic struct rtnl_link_ops ppp_link_ops __read_mostly = {
136262306a36Sopenharmony_ci	.kind		= "ppp",
136362306a36Sopenharmony_ci	.maxtype	= IFLA_PPP_MAX,
136462306a36Sopenharmony_ci	.policy		= ppp_nl_policy,
136562306a36Sopenharmony_ci	.priv_size	= sizeof(struct ppp),
136662306a36Sopenharmony_ci	.setup		= ppp_setup,
136762306a36Sopenharmony_ci	.validate	= ppp_nl_validate,
136862306a36Sopenharmony_ci	.newlink	= ppp_nl_newlink,
136962306a36Sopenharmony_ci	.dellink	= ppp_nl_dellink,
137062306a36Sopenharmony_ci	.get_size	= ppp_nl_get_size,
137162306a36Sopenharmony_ci	.fill_info	= ppp_nl_fill_info,
137262306a36Sopenharmony_ci	.get_link_net	= ppp_nl_get_link_net,
137362306a36Sopenharmony_ci};
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci#define PPP_MAJOR	108
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci/* Called at boot time if ppp is compiled into the kernel,
137862306a36Sopenharmony_ci   or at module load time (from init_module) if compiled as a module. */
137962306a36Sopenharmony_cistatic int __init ppp_init(void)
138062306a36Sopenharmony_ci{
138162306a36Sopenharmony_ci	int err;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	pr_info("PPP generic driver version " PPP_VERSION "\n");
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	err = register_pernet_device(&ppp_net_ops);
138662306a36Sopenharmony_ci	if (err) {
138762306a36Sopenharmony_ci		pr_err("failed to register PPP pernet device (%d)\n", err);
138862306a36Sopenharmony_ci		goto out;
138962306a36Sopenharmony_ci	}
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
139262306a36Sopenharmony_ci	if (err) {
139362306a36Sopenharmony_ci		pr_err("failed to register PPP device (%d)\n", err);
139462306a36Sopenharmony_ci		goto out_net;
139562306a36Sopenharmony_ci	}
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	ppp_class = class_create("ppp");
139862306a36Sopenharmony_ci	if (IS_ERR(ppp_class)) {
139962306a36Sopenharmony_ci		err = PTR_ERR(ppp_class);
140062306a36Sopenharmony_ci		goto out_chrdev;
140162306a36Sopenharmony_ci	}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	err = rtnl_link_register(&ppp_link_ops);
140462306a36Sopenharmony_ci	if (err) {
140562306a36Sopenharmony_ci		pr_err("failed to register rtnetlink PPP handler\n");
140662306a36Sopenharmony_ci		goto out_class;
140762306a36Sopenharmony_ci	}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	/* not a big deal if we fail here :-) */
141062306a36Sopenharmony_ci	device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL, "ppp");
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	return 0;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ciout_class:
141562306a36Sopenharmony_ci	class_destroy(ppp_class);
141662306a36Sopenharmony_ciout_chrdev:
141762306a36Sopenharmony_ci	unregister_chrdev(PPP_MAJOR, "ppp");
141862306a36Sopenharmony_ciout_net:
141962306a36Sopenharmony_ci	unregister_pernet_device(&ppp_net_ops);
142062306a36Sopenharmony_ciout:
142162306a36Sopenharmony_ci	return err;
142262306a36Sopenharmony_ci}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci/*
142562306a36Sopenharmony_ci * Network interface unit routines.
142662306a36Sopenharmony_ci */
142762306a36Sopenharmony_cistatic netdev_tx_t
142862306a36Sopenharmony_cippp_start_xmit(struct sk_buff *skb, struct net_device *dev)
142962306a36Sopenharmony_ci{
143062306a36Sopenharmony_ci	struct ppp *ppp = netdev_priv(dev);
143162306a36Sopenharmony_ci	int npi, proto;
143262306a36Sopenharmony_ci	unsigned char *pp;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	npi = ethertype_to_npindex(ntohs(skb->protocol));
143562306a36Sopenharmony_ci	if (npi < 0)
143662306a36Sopenharmony_ci		goto outf;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	/* Drop, accept or reject the packet */
143962306a36Sopenharmony_ci	switch (ppp->npmode[npi]) {
144062306a36Sopenharmony_ci	case NPMODE_PASS:
144162306a36Sopenharmony_ci		break;
144262306a36Sopenharmony_ci	case NPMODE_QUEUE:
144362306a36Sopenharmony_ci		/* it would be nice to have a way to tell the network
144462306a36Sopenharmony_ci		   system to queue this one up for later. */
144562306a36Sopenharmony_ci		goto outf;
144662306a36Sopenharmony_ci	case NPMODE_DROP:
144762306a36Sopenharmony_ci	case NPMODE_ERROR:
144862306a36Sopenharmony_ci		goto outf;
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	/* Put the 2-byte PPP protocol number on the front,
145262306a36Sopenharmony_ci	   making sure there is room for the address and control fields. */
145362306a36Sopenharmony_ci	if (skb_cow_head(skb, PPP_HDRLEN))
145462306a36Sopenharmony_ci		goto outf;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	pp = skb_push(skb, 2);
145762306a36Sopenharmony_ci	proto = npindex_to_proto[npi];
145862306a36Sopenharmony_ci	put_unaligned_be16(proto, pp);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	skb_scrub_packet(skb, !net_eq(ppp->ppp_net, dev_net(dev)));
146162306a36Sopenharmony_ci	ppp_xmit_process(ppp, skb);
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	return NETDEV_TX_OK;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci outf:
146662306a36Sopenharmony_ci	kfree_skb(skb);
146762306a36Sopenharmony_ci	++dev->stats.tx_dropped;
146862306a36Sopenharmony_ci	return NETDEV_TX_OK;
146962306a36Sopenharmony_ci}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_cistatic int
147262306a36Sopenharmony_cippp_net_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
147362306a36Sopenharmony_ci		       void __user *addr, int cmd)
147462306a36Sopenharmony_ci{
147562306a36Sopenharmony_ci	struct ppp *ppp = netdev_priv(dev);
147662306a36Sopenharmony_ci	int err = -EFAULT;
147762306a36Sopenharmony_ci	struct ppp_stats stats;
147862306a36Sopenharmony_ci	struct ppp_comp_stats cstats;
147962306a36Sopenharmony_ci	char *vers;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	switch (cmd) {
148262306a36Sopenharmony_ci	case SIOCGPPPSTATS:
148362306a36Sopenharmony_ci		ppp_get_stats(ppp, &stats);
148462306a36Sopenharmony_ci		if (copy_to_user(addr, &stats, sizeof(stats)))
148562306a36Sopenharmony_ci			break;
148662306a36Sopenharmony_ci		err = 0;
148762306a36Sopenharmony_ci		break;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	case SIOCGPPPCSTATS:
149062306a36Sopenharmony_ci		memset(&cstats, 0, sizeof(cstats));
149162306a36Sopenharmony_ci		if (ppp->xc_state)
149262306a36Sopenharmony_ci			ppp->xcomp->comp_stat(ppp->xc_state, &cstats.c);
149362306a36Sopenharmony_ci		if (ppp->rc_state)
149462306a36Sopenharmony_ci			ppp->rcomp->decomp_stat(ppp->rc_state, &cstats.d);
149562306a36Sopenharmony_ci		if (copy_to_user(addr, &cstats, sizeof(cstats)))
149662306a36Sopenharmony_ci			break;
149762306a36Sopenharmony_ci		err = 0;
149862306a36Sopenharmony_ci		break;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	case SIOCGPPPVER:
150162306a36Sopenharmony_ci		vers = PPP_VERSION;
150262306a36Sopenharmony_ci		if (copy_to_user(addr, vers, strlen(vers) + 1))
150362306a36Sopenharmony_ci			break;
150462306a36Sopenharmony_ci		err = 0;
150562306a36Sopenharmony_ci		break;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	default:
150862306a36Sopenharmony_ci		err = -EINVAL;
150962306a36Sopenharmony_ci	}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	return err;
151262306a36Sopenharmony_ci}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_cistatic void
151562306a36Sopenharmony_cippp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64)
151662306a36Sopenharmony_ci{
151762306a36Sopenharmony_ci	struct ppp *ppp = netdev_priv(dev);
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	ppp_recv_lock(ppp);
152062306a36Sopenharmony_ci	stats64->rx_packets = ppp->stats64.rx_packets;
152162306a36Sopenharmony_ci	stats64->rx_bytes   = ppp->stats64.rx_bytes;
152262306a36Sopenharmony_ci	ppp_recv_unlock(ppp);
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	ppp_xmit_lock(ppp);
152562306a36Sopenharmony_ci	stats64->tx_packets = ppp->stats64.tx_packets;
152662306a36Sopenharmony_ci	stats64->tx_bytes   = ppp->stats64.tx_bytes;
152762306a36Sopenharmony_ci	ppp_xmit_unlock(ppp);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	stats64->rx_errors        = dev->stats.rx_errors;
153062306a36Sopenharmony_ci	stats64->tx_errors        = dev->stats.tx_errors;
153162306a36Sopenharmony_ci	stats64->rx_dropped       = dev->stats.rx_dropped;
153262306a36Sopenharmony_ci	stats64->tx_dropped       = dev->stats.tx_dropped;
153362306a36Sopenharmony_ci	stats64->rx_length_errors = dev->stats.rx_length_errors;
153462306a36Sopenharmony_ci}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_cistatic int ppp_dev_init(struct net_device *dev)
153762306a36Sopenharmony_ci{
153862306a36Sopenharmony_ci	struct ppp *ppp;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	netdev_lockdep_set_classes(dev);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	ppp = netdev_priv(dev);
154362306a36Sopenharmony_ci	/* Let the netdevice take a reference on the ppp file. This ensures
154462306a36Sopenharmony_ci	 * that ppp_destroy_interface() won't run before the device gets
154562306a36Sopenharmony_ci	 * unregistered.
154662306a36Sopenharmony_ci	 */
154762306a36Sopenharmony_ci	refcount_inc(&ppp->file.refcnt);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	return 0;
155062306a36Sopenharmony_ci}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_cistatic void ppp_dev_uninit(struct net_device *dev)
155362306a36Sopenharmony_ci{
155462306a36Sopenharmony_ci	struct ppp *ppp = netdev_priv(dev);
155562306a36Sopenharmony_ci	struct ppp_net *pn = ppp_pernet(ppp->ppp_net);
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	ppp_lock(ppp);
155862306a36Sopenharmony_ci	ppp->closing = 1;
155962306a36Sopenharmony_ci	ppp_unlock(ppp);
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	mutex_lock(&pn->all_ppp_mutex);
156262306a36Sopenharmony_ci	unit_put(&pn->units_idr, ppp->file.index);
156362306a36Sopenharmony_ci	mutex_unlock(&pn->all_ppp_mutex);
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	ppp->owner = NULL;
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	ppp->file.dead = 1;
156862306a36Sopenharmony_ci	wake_up_interruptible(&ppp->file.rwait);
156962306a36Sopenharmony_ci}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_cistatic void ppp_dev_priv_destructor(struct net_device *dev)
157262306a36Sopenharmony_ci{
157362306a36Sopenharmony_ci	struct ppp *ppp;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	ppp = netdev_priv(dev);
157662306a36Sopenharmony_ci	if (refcount_dec_and_test(&ppp->file.refcnt))
157762306a36Sopenharmony_ci		ppp_destroy_interface(ppp);
157862306a36Sopenharmony_ci}
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_cistatic int ppp_fill_forward_path(struct net_device_path_ctx *ctx,
158162306a36Sopenharmony_ci				 struct net_device_path *path)
158262306a36Sopenharmony_ci{
158362306a36Sopenharmony_ci	struct ppp *ppp = netdev_priv(ctx->dev);
158462306a36Sopenharmony_ci	struct ppp_channel *chan;
158562306a36Sopenharmony_ci	struct channel *pch;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	if (ppp->flags & SC_MULTILINK)
158862306a36Sopenharmony_ci		return -EOPNOTSUPP;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	if (list_empty(&ppp->channels))
159162306a36Sopenharmony_ci		return -ENODEV;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	pch = list_first_entry(&ppp->channels, struct channel, clist);
159462306a36Sopenharmony_ci	chan = pch->chan;
159562306a36Sopenharmony_ci	if (!chan->ops->fill_forward_path)
159662306a36Sopenharmony_ci		return -EOPNOTSUPP;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	return chan->ops->fill_forward_path(ctx, path, chan);
159962306a36Sopenharmony_ci}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_cistatic const struct net_device_ops ppp_netdev_ops = {
160262306a36Sopenharmony_ci	.ndo_init	 = ppp_dev_init,
160362306a36Sopenharmony_ci	.ndo_uninit      = ppp_dev_uninit,
160462306a36Sopenharmony_ci	.ndo_start_xmit  = ppp_start_xmit,
160562306a36Sopenharmony_ci	.ndo_siocdevprivate = ppp_net_siocdevprivate,
160662306a36Sopenharmony_ci	.ndo_get_stats64 = ppp_get_stats64,
160762306a36Sopenharmony_ci	.ndo_fill_forward_path = ppp_fill_forward_path,
160862306a36Sopenharmony_ci};
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_cistatic struct device_type ppp_type = {
161162306a36Sopenharmony_ci	.name = "ppp",
161262306a36Sopenharmony_ci};
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_cistatic void ppp_setup(struct net_device *dev)
161562306a36Sopenharmony_ci{
161662306a36Sopenharmony_ci	dev->netdev_ops = &ppp_netdev_ops;
161762306a36Sopenharmony_ci	SET_NETDEV_DEVTYPE(dev, &ppp_type);
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	dev->features |= NETIF_F_LLTX;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	dev->hard_header_len = PPP_HDRLEN;
162262306a36Sopenharmony_ci	dev->mtu = PPP_MRU;
162362306a36Sopenharmony_ci	dev->addr_len = 0;
162462306a36Sopenharmony_ci	dev->tx_queue_len = 3;
162562306a36Sopenharmony_ci	dev->type = ARPHRD_PPP;
162662306a36Sopenharmony_ci	dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
162762306a36Sopenharmony_ci	dev->priv_destructor = ppp_dev_priv_destructor;
162862306a36Sopenharmony_ci	netif_keep_dst(dev);
162962306a36Sopenharmony_ci}
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci/*
163262306a36Sopenharmony_ci * Transmit-side routines.
163362306a36Sopenharmony_ci */
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci/* Called to do any work queued up on the transmit side that can now be done */
163662306a36Sopenharmony_cistatic void __ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb)
163762306a36Sopenharmony_ci{
163862306a36Sopenharmony_ci	ppp_xmit_lock(ppp);
163962306a36Sopenharmony_ci	if (!ppp->closing) {
164062306a36Sopenharmony_ci		ppp_push(ppp);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci		if (skb)
164362306a36Sopenharmony_ci			skb_queue_tail(&ppp->file.xq, skb);
164462306a36Sopenharmony_ci		while (!ppp->xmit_pending &&
164562306a36Sopenharmony_ci		       (skb = skb_dequeue(&ppp->file.xq)))
164662306a36Sopenharmony_ci			ppp_send_frame(ppp, skb);
164762306a36Sopenharmony_ci		/* If there's no work left to do, tell the core net
164862306a36Sopenharmony_ci		   code that we can accept some more. */
164962306a36Sopenharmony_ci		if (!ppp->xmit_pending && !skb_peek(&ppp->file.xq))
165062306a36Sopenharmony_ci			netif_wake_queue(ppp->dev);
165162306a36Sopenharmony_ci		else
165262306a36Sopenharmony_ci			netif_stop_queue(ppp->dev);
165362306a36Sopenharmony_ci	} else {
165462306a36Sopenharmony_ci		kfree_skb(skb);
165562306a36Sopenharmony_ci	}
165662306a36Sopenharmony_ci	ppp_xmit_unlock(ppp);
165762306a36Sopenharmony_ci}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_cistatic void ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb)
166062306a36Sopenharmony_ci{
166162306a36Sopenharmony_ci	local_bh_disable();
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	if (unlikely(*this_cpu_ptr(ppp->xmit_recursion)))
166462306a36Sopenharmony_ci		goto err;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	(*this_cpu_ptr(ppp->xmit_recursion))++;
166762306a36Sopenharmony_ci	__ppp_xmit_process(ppp, skb);
166862306a36Sopenharmony_ci	(*this_cpu_ptr(ppp->xmit_recursion))--;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	local_bh_enable();
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	return;
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_cierr:
167562306a36Sopenharmony_ci	local_bh_enable();
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	kfree_skb(skb);
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	if (net_ratelimit())
168062306a36Sopenharmony_ci		netdev_err(ppp->dev, "recursion detected\n");
168162306a36Sopenharmony_ci}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_cistatic inline struct sk_buff *
168462306a36Sopenharmony_cipad_compress_skb(struct ppp *ppp, struct sk_buff *skb)
168562306a36Sopenharmony_ci{
168662306a36Sopenharmony_ci	struct sk_buff *new_skb;
168762306a36Sopenharmony_ci	int len;
168862306a36Sopenharmony_ci	int new_skb_size = ppp->dev->mtu +
168962306a36Sopenharmony_ci		ppp->xcomp->comp_extra + ppp->dev->hard_header_len;
169062306a36Sopenharmony_ci	int compressor_skb_size = ppp->dev->mtu +
169162306a36Sopenharmony_ci		ppp->xcomp->comp_extra + PPP_HDRLEN;
169262306a36Sopenharmony_ci	new_skb = alloc_skb(new_skb_size, GFP_ATOMIC);
169362306a36Sopenharmony_ci	if (!new_skb) {
169462306a36Sopenharmony_ci		if (net_ratelimit())
169562306a36Sopenharmony_ci			netdev_err(ppp->dev, "PPP: no memory (comp pkt)\n");
169662306a36Sopenharmony_ci		return NULL;
169762306a36Sopenharmony_ci	}
169862306a36Sopenharmony_ci	if (ppp->dev->hard_header_len > PPP_HDRLEN)
169962306a36Sopenharmony_ci		skb_reserve(new_skb,
170062306a36Sopenharmony_ci			    ppp->dev->hard_header_len - PPP_HDRLEN);
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	/* compressor still expects A/C bytes in hdr */
170362306a36Sopenharmony_ci	len = ppp->xcomp->compress(ppp->xc_state, skb->data - 2,
170462306a36Sopenharmony_ci				   new_skb->data, skb->len + 2,
170562306a36Sopenharmony_ci				   compressor_skb_size);
170662306a36Sopenharmony_ci	if (len > 0 && (ppp->flags & SC_CCP_UP)) {
170762306a36Sopenharmony_ci		consume_skb(skb);
170862306a36Sopenharmony_ci		skb = new_skb;
170962306a36Sopenharmony_ci		skb_put(skb, len);
171062306a36Sopenharmony_ci		skb_pull(skb, 2);	/* pull off A/C bytes */
171162306a36Sopenharmony_ci	} else if (len == 0) {
171262306a36Sopenharmony_ci		/* didn't compress, or CCP not up yet */
171362306a36Sopenharmony_ci		consume_skb(new_skb);
171462306a36Sopenharmony_ci		new_skb = skb;
171562306a36Sopenharmony_ci	} else {
171662306a36Sopenharmony_ci		/*
171762306a36Sopenharmony_ci		 * (len < 0)
171862306a36Sopenharmony_ci		 * MPPE requires that we do not send unencrypted
171962306a36Sopenharmony_ci		 * frames.  The compressor will return -1 if we
172062306a36Sopenharmony_ci		 * should drop the frame.  We cannot simply test
172162306a36Sopenharmony_ci		 * the compress_proto because MPPE and MPPC share
172262306a36Sopenharmony_ci		 * the same number.
172362306a36Sopenharmony_ci		 */
172462306a36Sopenharmony_ci		if (net_ratelimit())
172562306a36Sopenharmony_ci			netdev_err(ppp->dev, "ppp: compressor dropped pkt\n");
172662306a36Sopenharmony_ci		kfree_skb(skb);
172762306a36Sopenharmony_ci		consume_skb(new_skb);
172862306a36Sopenharmony_ci		new_skb = NULL;
172962306a36Sopenharmony_ci	}
173062306a36Sopenharmony_ci	return new_skb;
173162306a36Sopenharmony_ci}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci/*
173462306a36Sopenharmony_ci * Compress and send a frame.
173562306a36Sopenharmony_ci * The caller should have locked the xmit path,
173662306a36Sopenharmony_ci * and xmit_pending should be 0.
173762306a36Sopenharmony_ci */
173862306a36Sopenharmony_cistatic void
173962306a36Sopenharmony_cippp_send_frame(struct ppp *ppp, struct sk_buff *skb)
174062306a36Sopenharmony_ci{
174162306a36Sopenharmony_ci	int proto = PPP_PROTO(skb);
174262306a36Sopenharmony_ci	struct sk_buff *new_skb;
174362306a36Sopenharmony_ci	int len;
174462306a36Sopenharmony_ci	unsigned char *cp;
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	skb->dev = ppp->dev;
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	if (proto < 0x8000) {
174962306a36Sopenharmony_ci#ifdef CONFIG_PPP_FILTER
175062306a36Sopenharmony_ci		/* check if we should pass this packet */
175162306a36Sopenharmony_ci		/* the filter instructions are constructed assuming
175262306a36Sopenharmony_ci		   a four-byte PPP header on each packet */
175362306a36Sopenharmony_ci		*(u8 *)skb_push(skb, 2) = 1;
175462306a36Sopenharmony_ci		if (ppp->pass_filter &&
175562306a36Sopenharmony_ci		    bpf_prog_run(ppp->pass_filter, skb) == 0) {
175662306a36Sopenharmony_ci			if (ppp->debug & 1)
175762306a36Sopenharmony_ci				netdev_printk(KERN_DEBUG, ppp->dev,
175862306a36Sopenharmony_ci					      "PPP: outbound frame "
175962306a36Sopenharmony_ci					      "not passed\n");
176062306a36Sopenharmony_ci			kfree_skb(skb);
176162306a36Sopenharmony_ci			return;
176262306a36Sopenharmony_ci		}
176362306a36Sopenharmony_ci		/* if this packet passes the active filter, record the time */
176462306a36Sopenharmony_ci		if (!(ppp->active_filter &&
176562306a36Sopenharmony_ci		      bpf_prog_run(ppp->active_filter, skb) == 0))
176662306a36Sopenharmony_ci			ppp->last_xmit = jiffies;
176762306a36Sopenharmony_ci		skb_pull(skb, 2);
176862306a36Sopenharmony_ci#else
176962306a36Sopenharmony_ci		/* for data packets, record the time */
177062306a36Sopenharmony_ci		ppp->last_xmit = jiffies;
177162306a36Sopenharmony_ci#endif /* CONFIG_PPP_FILTER */
177262306a36Sopenharmony_ci	}
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	++ppp->stats64.tx_packets;
177562306a36Sopenharmony_ci	ppp->stats64.tx_bytes += skb->len - PPP_PROTO_LEN;
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	switch (proto) {
177862306a36Sopenharmony_ci	case PPP_IP:
177962306a36Sopenharmony_ci		if (!ppp->vj || (ppp->flags & SC_COMP_TCP) == 0)
178062306a36Sopenharmony_ci			break;
178162306a36Sopenharmony_ci		/* try to do VJ TCP header compression */
178262306a36Sopenharmony_ci		new_skb = alloc_skb(skb->len + ppp->dev->hard_header_len - 2,
178362306a36Sopenharmony_ci				    GFP_ATOMIC);
178462306a36Sopenharmony_ci		if (!new_skb) {
178562306a36Sopenharmony_ci			netdev_err(ppp->dev, "PPP: no memory (VJ comp pkt)\n");
178662306a36Sopenharmony_ci			goto drop;
178762306a36Sopenharmony_ci		}
178862306a36Sopenharmony_ci		skb_reserve(new_skb, ppp->dev->hard_header_len - 2);
178962306a36Sopenharmony_ci		cp = skb->data + 2;
179062306a36Sopenharmony_ci		len = slhc_compress(ppp->vj, cp, skb->len - 2,
179162306a36Sopenharmony_ci				    new_skb->data + 2, &cp,
179262306a36Sopenharmony_ci				    !(ppp->flags & SC_NO_TCP_CCID));
179362306a36Sopenharmony_ci		if (cp == skb->data + 2) {
179462306a36Sopenharmony_ci			/* didn't compress */
179562306a36Sopenharmony_ci			consume_skb(new_skb);
179662306a36Sopenharmony_ci		} else {
179762306a36Sopenharmony_ci			if (cp[0] & SL_TYPE_COMPRESSED_TCP) {
179862306a36Sopenharmony_ci				proto = PPP_VJC_COMP;
179962306a36Sopenharmony_ci				cp[0] &= ~SL_TYPE_COMPRESSED_TCP;
180062306a36Sopenharmony_ci			} else {
180162306a36Sopenharmony_ci				proto = PPP_VJC_UNCOMP;
180262306a36Sopenharmony_ci				cp[0] = skb->data[2];
180362306a36Sopenharmony_ci			}
180462306a36Sopenharmony_ci			consume_skb(skb);
180562306a36Sopenharmony_ci			skb = new_skb;
180662306a36Sopenharmony_ci			cp = skb_put(skb, len + 2);
180762306a36Sopenharmony_ci			cp[0] = 0;
180862306a36Sopenharmony_ci			cp[1] = proto;
180962306a36Sopenharmony_ci		}
181062306a36Sopenharmony_ci		break;
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	case PPP_CCP:
181362306a36Sopenharmony_ci		/* peek at outbound CCP frames */
181462306a36Sopenharmony_ci		ppp_ccp_peek(ppp, skb, 0);
181562306a36Sopenharmony_ci		break;
181662306a36Sopenharmony_ci	}
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	/* try to do packet compression */
181962306a36Sopenharmony_ci	if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state &&
182062306a36Sopenharmony_ci	    proto != PPP_LCP && proto != PPP_CCP) {
182162306a36Sopenharmony_ci		if (!(ppp->flags & SC_CCP_UP) && (ppp->flags & SC_MUST_COMP)) {
182262306a36Sopenharmony_ci			if (net_ratelimit())
182362306a36Sopenharmony_ci				netdev_err(ppp->dev,
182462306a36Sopenharmony_ci					   "ppp: compression required but "
182562306a36Sopenharmony_ci					   "down - pkt dropped.\n");
182662306a36Sopenharmony_ci			goto drop;
182762306a36Sopenharmony_ci		}
182862306a36Sopenharmony_ci		skb = pad_compress_skb(ppp, skb);
182962306a36Sopenharmony_ci		if (!skb)
183062306a36Sopenharmony_ci			goto drop;
183162306a36Sopenharmony_ci	}
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	/*
183462306a36Sopenharmony_ci	 * If we are waiting for traffic (demand dialling),
183562306a36Sopenharmony_ci	 * queue it up for pppd to receive.
183662306a36Sopenharmony_ci	 */
183762306a36Sopenharmony_ci	if (ppp->flags & SC_LOOP_TRAFFIC) {
183862306a36Sopenharmony_ci		if (ppp->file.rq.qlen > PPP_MAX_RQLEN)
183962306a36Sopenharmony_ci			goto drop;
184062306a36Sopenharmony_ci		skb_queue_tail(&ppp->file.rq, skb);
184162306a36Sopenharmony_ci		wake_up_interruptible(&ppp->file.rwait);
184262306a36Sopenharmony_ci		return;
184362306a36Sopenharmony_ci	}
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	ppp->xmit_pending = skb;
184662306a36Sopenharmony_ci	ppp_push(ppp);
184762306a36Sopenharmony_ci	return;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci drop:
185062306a36Sopenharmony_ci	kfree_skb(skb);
185162306a36Sopenharmony_ci	++ppp->dev->stats.tx_errors;
185262306a36Sopenharmony_ci}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci/*
185562306a36Sopenharmony_ci * Try to send the frame in xmit_pending.
185662306a36Sopenharmony_ci * The caller should have the xmit path locked.
185762306a36Sopenharmony_ci */
185862306a36Sopenharmony_cistatic void
185962306a36Sopenharmony_cippp_push(struct ppp *ppp)
186062306a36Sopenharmony_ci{
186162306a36Sopenharmony_ci	struct list_head *list;
186262306a36Sopenharmony_ci	struct channel *pch;
186362306a36Sopenharmony_ci	struct sk_buff *skb = ppp->xmit_pending;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	if (!skb)
186662306a36Sopenharmony_ci		return;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	list = &ppp->channels;
186962306a36Sopenharmony_ci	if (list_empty(list)) {
187062306a36Sopenharmony_ci		/* nowhere to send the packet, just drop it */
187162306a36Sopenharmony_ci		ppp->xmit_pending = NULL;
187262306a36Sopenharmony_ci		kfree_skb(skb);
187362306a36Sopenharmony_ci		return;
187462306a36Sopenharmony_ci	}
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	if ((ppp->flags & SC_MULTILINK) == 0) {
187762306a36Sopenharmony_ci		/* not doing multilink: send it down the first channel */
187862306a36Sopenharmony_ci		list = list->next;
187962306a36Sopenharmony_ci		pch = list_entry(list, struct channel, clist);
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci		spin_lock(&pch->downl);
188262306a36Sopenharmony_ci		if (pch->chan) {
188362306a36Sopenharmony_ci			if (pch->chan->ops->start_xmit(pch->chan, skb))
188462306a36Sopenharmony_ci				ppp->xmit_pending = NULL;
188562306a36Sopenharmony_ci		} else {
188662306a36Sopenharmony_ci			/* channel got unregistered */
188762306a36Sopenharmony_ci			kfree_skb(skb);
188862306a36Sopenharmony_ci			ppp->xmit_pending = NULL;
188962306a36Sopenharmony_ci		}
189062306a36Sopenharmony_ci		spin_unlock(&pch->downl);
189162306a36Sopenharmony_ci		return;
189262306a36Sopenharmony_ci	}
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci#ifdef CONFIG_PPP_MULTILINK
189562306a36Sopenharmony_ci	/* Multilink: fragment the packet over as many links
189662306a36Sopenharmony_ci	   as can take the packet at the moment. */
189762306a36Sopenharmony_ci	if (!ppp_mp_explode(ppp, skb))
189862306a36Sopenharmony_ci		return;
189962306a36Sopenharmony_ci#endif /* CONFIG_PPP_MULTILINK */
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	ppp->xmit_pending = NULL;
190262306a36Sopenharmony_ci	kfree_skb(skb);
190362306a36Sopenharmony_ci}
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci#ifdef CONFIG_PPP_MULTILINK
190662306a36Sopenharmony_cistatic bool mp_protocol_compress __read_mostly = true;
190762306a36Sopenharmony_cimodule_param(mp_protocol_compress, bool, 0644);
190862306a36Sopenharmony_ciMODULE_PARM_DESC(mp_protocol_compress,
190962306a36Sopenharmony_ci		 "compress protocol id in multilink fragments");
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci/*
191262306a36Sopenharmony_ci * Divide a packet to be transmitted into fragments and
191362306a36Sopenharmony_ci * send them out the individual links.
191462306a36Sopenharmony_ci */
191562306a36Sopenharmony_cistatic int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
191662306a36Sopenharmony_ci{
191762306a36Sopenharmony_ci	int len, totlen;
191862306a36Sopenharmony_ci	int i, bits, hdrlen, mtu;
191962306a36Sopenharmony_ci	int flen;
192062306a36Sopenharmony_ci	int navail, nfree, nzero;
192162306a36Sopenharmony_ci	int nbigger;
192262306a36Sopenharmony_ci	int totspeed;
192362306a36Sopenharmony_ci	int totfree;
192462306a36Sopenharmony_ci	unsigned char *p, *q;
192562306a36Sopenharmony_ci	struct list_head *list;
192662306a36Sopenharmony_ci	struct channel *pch;
192762306a36Sopenharmony_ci	struct sk_buff *frag;
192862306a36Sopenharmony_ci	struct ppp_channel *chan;
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	totspeed = 0; /*total bitrate of the bundle*/
193162306a36Sopenharmony_ci	nfree = 0; /* # channels which have no packet already queued */
193262306a36Sopenharmony_ci	navail = 0; /* total # of usable channels (not deregistered) */
193362306a36Sopenharmony_ci	nzero = 0; /* number of channels with zero speed associated*/
193462306a36Sopenharmony_ci	totfree = 0; /*total # of channels available and
193562306a36Sopenharmony_ci				  *having no queued packets before
193662306a36Sopenharmony_ci				  *starting the fragmentation*/
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
193962306a36Sopenharmony_ci	i = 0;
194062306a36Sopenharmony_ci	list_for_each_entry(pch, &ppp->channels, clist) {
194162306a36Sopenharmony_ci		if (pch->chan) {
194262306a36Sopenharmony_ci			pch->avail = 1;
194362306a36Sopenharmony_ci			navail++;
194462306a36Sopenharmony_ci			pch->speed = pch->chan->speed;
194562306a36Sopenharmony_ci		} else {
194662306a36Sopenharmony_ci			pch->avail = 0;
194762306a36Sopenharmony_ci		}
194862306a36Sopenharmony_ci		if (pch->avail) {
194962306a36Sopenharmony_ci			if (skb_queue_empty(&pch->file.xq) ||
195062306a36Sopenharmony_ci				!pch->had_frag) {
195162306a36Sopenharmony_ci					if (pch->speed == 0)
195262306a36Sopenharmony_ci						nzero++;
195362306a36Sopenharmony_ci					else
195462306a36Sopenharmony_ci						totspeed += pch->speed;
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci					pch->avail = 2;
195762306a36Sopenharmony_ci					++nfree;
195862306a36Sopenharmony_ci					++totfree;
195962306a36Sopenharmony_ci				}
196062306a36Sopenharmony_ci			if (!pch->had_frag && i < ppp->nxchan)
196162306a36Sopenharmony_ci				ppp->nxchan = i;
196262306a36Sopenharmony_ci		}
196362306a36Sopenharmony_ci		++i;
196462306a36Sopenharmony_ci	}
196562306a36Sopenharmony_ci	/*
196662306a36Sopenharmony_ci	 * Don't start sending this packet unless at least half of
196762306a36Sopenharmony_ci	 * the channels are free.  This gives much better TCP
196862306a36Sopenharmony_ci	 * performance if we have a lot of channels.
196962306a36Sopenharmony_ci	 */
197062306a36Sopenharmony_ci	if (nfree == 0 || nfree < navail / 2)
197162306a36Sopenharmony_ci		return 0; /* can't take now, leave it in xmit_pending */
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	/* Do protocol field compression */
197462306a36Sopenharmony_ci	p = skb->data;
197562306a36Sopenharmony_ci	len = skb->len;
197662306a36Sopenharmony_ci	if (*p == 0 && mp_protocol_compress) {
197762306a36Sopenharmony_ci		++p;
197862306a36Sopenharmony_ci		--len;
197962306a36Sopenharmony_ci	}
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	totlen = len;
198262306a36Sopenharmony_ci	nbigger = len % nfree;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	/* skip to the channel after the one we last used
198562306a36Sopenharmony_ci	   and start at that one */
198662306a36Sopenharmony_ci	list = &ppp->channels;
198762306a36Sopenharmony_ci	for (i = 0; i < ppp->nxchan; ++i) {
198862306a36Sopenharmony_ci		list = list->next;
198962306a36Sopenharmony_ci		if (list == &ppp->channels) {
199062306a36Sopenharmony_ci			i = 0;
199162306a36Sopenharmony_ci			break;
199262306a36Sopenharmony_ci		}
199362306a36Sopenharmony_ci	}
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	/* create a fragment for each channel */
199662306a36Sopenharmony_ci	bits = B;
199762306a36Sopenharmony_ci	while (len > 0) {
199862306a36Sopenharmony_ci		list = list->next;
199962306a36Sopenharmony_ci		if (list == &ppp->channels) {
200062306a36Sopenharmony_ci			i = 0;
200162306a36Sopenharmony_ci			continue;
200262306a36Sopenharmony_ci		}
200362306a36Sopenharmony_ci		pch = list_entry(list, struct channel, clist);
200462306a36Sopenharmony_ci		++i;
200562306a36Sopenharmony_ci		if (!pch->avail)
200662306a36Sopenharmony_ci			continue;
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci		/*
200962306a36Sopenharmony_ci		 * Skip this channel if it has a fragment pending already and
201062306a36Sopenharmony_ci		 * we haven't given a fragment to all of the free channels.
201162306a36Sopenharmony_ci		 */
201262306a36Sopenharmony_ci		if (pch->avail == 1) {
201362306a36Sopenharmony_ci			if (nfree > 0)
201462306a36Sopenharmony_ci				continue;
201562306a36Sopenharmony_ci		} else {
201662306a36Sopenharmony_ci			pch->avail = 1;
201762306a36Sopenharmony_ci		}
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci		/* check the channel's mtu and whether it is still attached. */
202062306a36Sopenharmony_ci		spin_lock(&pch->downl);
202162306a36Sopenharmony_ci		if (pch->chan == NULL) {
202262306a36Sopenharmony_ci			/* can't use this channel, it's being deregistered */
202362306a36Sopenharmony_ci			if (pch->speed == 0)
202462306a36Sopenharmony_ci				nzero--;
202562306a36Sopenharmony_ci			else
202662306a36Sopenharmony_ci				totspeed -= pch->speed;
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci			spin_unlock(&pch->downl);
202962306a36Sopenharmony_ci			pch->avail = 0;
203062306a36Sopenharmony_ci			totlen = len;
203162306a36Sopenharmony_ci			totfree--;
203262306a36Sopenharmony_ci			nfree--;
203362306a36Sopenharmony_ci			if (--navail == 0)
203462306a36Sopenharmony_ci				break;
203562306a36Sopenharmony_ci			continue;
203662306a36Sopenharmony_ci		}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci		/*
203962306a36Sopenharmony_ci		*if the channel speed is not set divide
204062306a36Sopenharmony_ci		*the packet evenly among the free channels;
204162306a36Sopenharmony_ci		*otherwise divide it according to the speed
204262306a36Sopenharmony_ci		*of the channel we are going to transmit on
204362306a36Sopenharmony_ci		*/
204462306a36Sopenharmony_ci		flen = len;
204562306a36Sopenharmony_ci		if (nfree > 0) {
204662306a36Sopenharmony_ci			if (pch->speed == 0) {
204762306a36Sopenharmony_ci				flen = len/nfree;
204862306a36Sopenharmony_ci				if (nbigger > 0) {
204962306a36Sopenharmony_ci					flen++;
205062306a36Sopenharmony_ci					nbigger--;
205162306a36Sopenharmony_ci				}
205262306a36Sopenharmony_ci			} else {
205362306a36Sopenharmony_ci				flen = (((totfree - nzero)*(totlen + hdrlen*totfree)) /
205462306a36Sopenharmony_ci					((totspeed*totfree)/pch->speed)) - hdrlen;
205562306a36Sopenharmony_ci				if (nbigger > 0) {
205662306a36Sopenharmony_ci					flen += ((totfree - nzero)*pch->speed)/totspeed;
205762306a36Sopenharmony_ci					nbigger -= ((totfree - nzero)*pch->speed)/
205862306a36Sopenharmony_ci							totspeed;
205962306a36Sopenharmony_ci				}
206062306a36Sopenharmony_ci			}
206162306a36Sopenharmony_ci			nfree--;
206262306a36Sopenharmony_ci		}
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci		/*
206562306a36Sopenharmony_ci		 *check if we are on the last channel or
206662306a36Sopenharmony_ci		 *we exceded the length of the data to
206762306a36Sopenharmony_ci		 *fragment
206862306a36Sopenharmony_ci		 */
206962306a36Sopenharmony_ci		if ((nfree <= 0) || (flen > len))
207062306a36Sopenharmony_ci			flen = len;
207162306a36Sopenharmony_ci		/*
207262306a36Sopenharmony_ci		 *it is not worth to tx on slow channels:
207362306a36Sopenharmony_ci		 *in that case from the resulting flen according to the
207462306a36Sopenharmony_ci		 *above formula will be equal or less than zero.
207562306a36Sopenharmony_ci		 *Skip the channel in this case
207662306a36Sopenharmony_ci		 */
207762306a36Sopenharmony_ci		if (flen <= 0) {
207862306a36Sopenharmony_ci			pch->avail = 2;
207962306a36Sopenharmony_ci			spin_unlock(&pch->downl);
208062306a36Sopenharmony_ci			continue;
208162306a36Sopenharmony_ci		}
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci		/*
208462306a36Sopenharmony_ci		 * hdrlen includes the 2-byte PPP protocol field, but the
208562306a36Sopenharmony_ci		 * MTU counts only the payload excluding the protocol field.
208662306a36Sopenharmony_ci		 * (RFC1661 Section 2)
208762306a36Sopenharmony_ci		 */
208862306a36Sopenharmony_ci		mtu = pch->chan->mtu - (hdrlen - 2);
208962306a36Sopenharmony_ci		if (mtu < 4)
209062306a36Sopenharmony_ci			mtu = 4;
209162306a36Sopenharmony_ci		if (flen > mtu)
209262306a36Sopenharmony_ci			flen = mtu;
209362306a36Sopenharmony_ci		if (flen == len)
209462306a36Sopenharmony_ci			bits |= E;
209562306a36Sopenharmony_ci		frag = alloc_skb(flen + hdrlen + (flen == 0), GFP_ATOMIC);
209662306a36Sopenharmony_ci		if (!frag)
209762306a36Sopenharmony_ci			goto noskb;
209862306a36Sopenharmony_ci		q = skb_put(frag, flen + hdrlen);
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci		/* make the MP header */
210162306a36Sopenharmony_ci		put_unaligned_be16(PPP_MP, q);
210262306a36Sopenharmony_ci		if (ppp->flags & SC_MP_XSHORTSEQ) {
210362306a36Sopenharmony_ci			q[2] = bits + ((ppp->nxseq >> 8) & 0xf);
210462306a36Sopenharmony_ci			q[3] = ppp->nxseq;
210562306a36Sopenharmony_ci		} else {
210662306a36Sopenharmony_ci			q[2] = bits;
210762306a36Sopenharmony_ci			q[3] = ppp->nxseq >> 16;
210862306a36Sopenharmony_ci			q[4] = ppp->nxseq >> 8;
210962306a36Sopenharmony_ci			q[5] = ppp->nxseq;
211062306a36Sopenharmony_ci		}
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci		memcpy(q + hdrlen, p, flen);
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_ci		/* try to send it down the channel */
211562306a36Sopenharmony_ci		chan = pch->chan;
211662306a36Sopenharmony_ci		if (!skb_queue_empty(&pch->file.xq) ||
211762306a36Sopenharmony_ci			!chan->ops->start_xmit(chan, frag))
211862306a36Sopenharmony_ci			skb_queue_tail(&pch->file.xq, frag);
211962306a36Sopenharmony_ci		pch->had_frag = 1;
212062306a36Sopenharmony_ci		p += flen;
212162306a36Sopenharmony_ci		len -= flen;
212262306a36Sopenharmony_ci		++ppp->nxseq;
212362306a36Sopenharmony_ci		bits = 0;
212462306a36Sopenharmony_ci		spin_unlock(&pch->downl);
212562306a36Sopenharmony_ci	}
212662306a36Sopenharmony_ci	ppp->nxchan = i;
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	return 1;
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci noskb:
213162306a36Sopenharmony_ci	spin_unlock(&pch->downl);
213262306a36Sopenharmony_ci	if (ppp->debug & 1)
213362306a36Sopenharmony_ci		netdev_err(ppp->dev, "PPP: no memory (fragment)\n");
213462306a36Sopenharmony_ci	++ppp->dev->stats.tx_errors;
213562306a36Sopenharmony_ci	++ppp->nxseq;
213662306a36Sopenharmony_ci	return 1;	/* abandon the frame */
213762306a36Sopenharmony_ci}
213862306a36Sopenharmony_ci#endif /* CONFIG_PPP_MULTILINK */
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci/* Try to send data out on a channel */
214162306a36Sopenharmony_cistatic void __ppp_channel_push(struct channel *pch)
214262306a36Sopenharmony_ci{
214362306a36Sopenharmony_ci	struct sk_buff *skb;
214462306a36Sopenharmony_ci	struct ppp *ppp;
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	spin_lock(&pch->downl);
214762306a36Sopenharmony_ci	if (pch->chan) {
214862306a36Sopenharmony_ci		while (!skb_queue_empty(&pch->file.xq)) {
214962306a36Sopenharmony_ci			skb = skb_dequeue(&pch->file.xq);
215062306a36Sopenharmony_ci			if (!pch->chan->ops->start_xmit(pch->chan, skb)) {
215162306a36Sopenharmony_ci				/* put the packet back and try again later */
215262306a36Sopenharmony_ci				skb_queue_head(&pch->file.xq, skb);
215362306a36Sopenharmony_ci				break;
215462306a36Sopenharmony_ci			}
215562306a36Sopenharmony_ci		}
215662306a36Sopenharmony_ci	} else {
215762306a36Sopenharmony_ci		/* channel got deregistered */
215862306a36Sopenharmony_ci		skb_queue_purge(&pch->file.xq);
215962306a36Sopenharmony_ci	}
216062306a36Sopenharmony_ci	spin_unlock(&pch->downl);
216162306a36Sopenharmony_ci	/* see if there is anything from the attached unit to be sent */
216262306a36Sopenharmony_ci	if (skb_queue_empty(&pch->file.xq)) {
216362306a36Sopenharmony_ci		ppp = pch->ppp;
216462306a36Sopenharmony_ci		if (ppp)
216562306a36Sopenharmony_ci			__ppp_xmit_process(ppp, NULL);
216662306a36Sopenharmony_ci	}
216762306a36Sopenharmony_ci}
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_cistatic void ppp_channel_push(struct channel *pch)
217062306a36Sopenharmony_ci{
217162306a36Sopenharmony_ci	read_lock_bh(&pch->upl);
217262306a36Sopenharmony_ci	if (pch->ppp) {
217362306a36Sopenharmony_ci		(*this_cpu_ptr(pch->ppp->xmit_recursion))++;
217462306a36Sopenharmony_ci		__ppp_channel_push(pch);
217562306a36Sopenharmony_ci		(*this_cpu_ptr(pch->ppp->xmit_recursion))--;
217662306a36Sopenharmony_ci	} else {
217762306a36Sopenharmony_ci		__ppp_channel_push(pch);
217862306a36Sopenharmony_ci	}
217962306a36Sopenharmony_ci	read_unlock_bh(&pch->upl);
218062306a36Sopenharmony_ci}
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci/*
218362306a36Sopenharmony_ci * Receive-side routines.
218462306a36Sopenharmony_ci */
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_cistruct ppp_mp_skb_parm {
218762306a36Sopenharmony_ci	u32		sequence;
218862306a36Sopenharmony_ci	u8		BEbits;
218962306a36Sopenharmony_ci};
219062306a36Sopenharmony_ci#define PPP_MP_CB(skb)	((struct ppp_mp_skb_parm *)((skb)->cb))
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_cistatic inline void
219362306a36Sopenharmony_cippp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
219462306a36Sopenharmony_ci{
219562306a36Sopenharmony_ci	ppp_recv_lock(ppp);
219662306a36Sopenharmony_ci	if (!ppp->closing)
219762306a36Sopenharmony_ci		ppp_receive_frame(ppp, skb, pch);
219862306a36Sopenharmony_ci	else
219962306a36Sopenharmony_ci		kfree_skb(skb);
220062306a36Sopenharmony_ci	ppp_recv_unlock(ppp);
220162306a36Sopenharmony_ci}
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci/**
220462306a36Sopenharmony_ci * __ppp_decompress_proto - Decompress protocol field, slim version.
220562306a36Sopenharmony_ci * @skb: Socket buffer where protocol field should be decompressed. It must have
220662306a36Sopenharmony_ci *	 at least 1 byte of head room and 1 byte of linear data. First byte of
220762306a36Sopenharmony_ci *	 data must be a protocol field byte.
220862306a36Sopenharmony_ci *
220962306a36Sopenharmony_ci * Decompress protocol field in PPP header if it's compressed, e.g. when
221062306a36Sopenharmony_ci * Protocol-Field-Compression (PFC) was negotiated. No checks w.r.t. skb data
221162306a36Sopenharmony_ci * length are done in this function.
221262306a36Sopenharmony_ci */
221362306a36Sopenharmony_cistatic void __ppp_decompress_proto(struct sk_buff *skb)
221462306a36Sopenharmony_ci{
221562306a36Sopenharmony_ci	if (skb->data[0] & 0x01)
221662306a36Sopenharmony_ci		*(u8 *)skb_push(skb, 1) = 0x00;
221762306a36Sopenharmony_ci}
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci/**
222062306a36Sopenharmony_ci * ppp_decompress_proto - Check skb data room and decompress protocol field.
222162306a36Sopenharmony_ci * @skb: Socket buffer where protocol field should be decompressed. First byte
222262306a36Sopenharmony_ci *	 of data must be a protocol field byte.
222362306a36Sopenharmony_ci *
222462306a36Sopenharmony_ci * Decompress protocol field in PPP header if it's compressed, e.g. when
222562306a36Sopenharmony_ci * Protocol-Field-Compression (PFC) was negotiated. This function also makes
222662306a36Sopenharmony_ci * sure that skb data room is sufficient for Protocol field, before and after
222762306a36Sopenharmony_ci * decompression.
222862306a36Sopenharmony_ci *
222962306a36Sopenharmony_ci * Return: true - decompressed successfully, false - not enough room in skb.
223062306a36Sopenharmony_ci */
223162306a36Sopenharmony_cistatic bool ppp_decompress_proto(struct sk_buff *skb)
223262306a36Sopenharmony_ci{
223362306a36Sopenharmony_ci	/* At least one byte should be present (if protocol is compressed) */
223462306a36Sopenharmony_ci	if (!pskb_may_pull(skb, 1))
223562306a36Sopenharmony_ci		return false;
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	__ppp_decompress_proto(skb);
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	/* Protocol field should occupy 2 bytes when not compressed */
224062306a36Sopenharmony_ci	return pskb_may_pull(skb, 2);
224162306a36Sopenharmony_ci}
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci/* Attempt to handle a frame via. a bridged channel, if one exists.
224462306a36Sopenharmony_ci * If the channel is bridged, the frame is consumed by the bridge.
224562306a36Sopenharmony_ci * If not, the caller must handle the frame by normal recv mechanisms.
224662306a36Sopenharmony_ci * Returns true if the frame is consumed, false otherwise.
224762306a36Sopenharmony_ci */
224862306a36Sopenharmony_cistatic bool ppp_channel_bridge_input(struct channel *pch, struct sk_buff *skb)
224962306a36Sopenharmony_ci{
225062306a36Sopenharmony_ci	struct channel *pchb;
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	rcu_read_lock();
225362306a36Sopenharmony_ci	pchb = rcu_dereference(pch->bridge);
225462306a36Sopenharmony_ci	if (!pchb)
225562306a36Sopenharmony_ci		goto out_rcu;
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	spin_lock(&pchb->downl);
225862306a36Sopenharmony_ci	if (!pchb->chan) {
225962306a36Sopenharmony_ci		/* channel got unregistered */
226062306a36Sopenharmony_ci		kfree_skb(skb);
226162306a36Sopenharmony_ci		goto outl;
226262306a36Sopenharmony_ci	}
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci	skb_scrub_packet(skb, !net_eq(pch->chan_net, pchb->chan_net));
226562306a36Sopenharmony_ci	if (!pchb->chan->ops->start_xmit(pchb->chan, skb))
226662306a36Sopenharmony_ci		kfree_skb(skb);
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_cioutl:
226962306a36Sopenharmony_ci	spin_unlock(&pchb->downl);
227062306a36Sopenharmony_ciout_rcu:
227162306a36Sopenharmony_ci	rcu_read_unlock();
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	/* If pchb is set then we've consumed the packet */
227462306a36Sopenharmony_ci	return !!pchb;
227562306a36Sopenharmony_ci}
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_civoid
227862306a36Sopenharmony_cippp_input(struct ppp_channel *chan, struct sk_buff *skb)
227962306a36Sopenharmony_ci{
228062306a36Sopenharmony_ci	struct channel *pch = chan->ppp;
228162306a36Sopenharmony_ci	int proto;
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	if (!pch) {
228462306a36Sopenharmony_ci		kfree_skb(skb);
228562306a36Sopenharmony_ci		return;
228662306a36Sopenharmony_ci	}
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	/* If the channel is bridged, transmit via. bridge */
228962306a36Sopenharmony_ci	if (ppp_channel_bridge_input(pch, skb))
229062306a36Sopenharmony_ci		return;
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	read_lock_bh(&pch->upl);
229362306a36Sopenharmony_ci	if (!ppp_decompress_proto(skb)) {
229462306a36Sopenharmony_ci		kfree_skb(skb);
229562306a36Sopenharmony_ci		if (pch->ppp) {
229662306a36Sopenharmony_ci			++pch->ppp->dev->stats.rx_length_errors;
229762306a36Sopenharmony_ci			ppp_receive_error(pch->ppp);
229862306a36Sopenharmony_ci		}
229962306a36Sopenharmony_ci		goto done;
230062306a36Sopenharmony_ci	}
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_ci	proto = PPP_PROTO(skb);
230362306a36Sopenharmony_ci	if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) {
230462306a36Sopenharmony_ci		/* put it on the channel queue */
230562306a36Sopenharmony_ci		skb_queue_tail(&pch->file.rq, skb);
230662306a36Sopenharmony_ci		/* drop old frames if queue too long */
230762306a36Sopenharmony_ci		while (pch->file.rq.qlen > PPP_MAX_RQLEN &&
230862306a36Sopenharmony_ci		       (skb = skb_dequeue(&pch->file.rq)))
230962306a36Sopenharmony_ci			kfree_skb(skb);
231062306a36Sopenharmony_ci		wake_up_interruptible(&pch->file.rwait);
231162306a36Sopenharmony_ci	} else {
231262306a36Sopenharmony_ci		ppp_do_recv(pch->ppp, skb, pch);
231362306a36Sopenharmony_ci	}
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_cidone:
231662306a36Sopenharmony_ci	read_unlock_bh(&pch->upl);
231762306a36Sopenharmony_ci}
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci/* Put a 0-length skb in the receive queue as an error indication */
232062306a36Sopenharmony_civoid
232162306a36Sopenharmony_cippp_input_error(struct ppp_channel *chan, int code)
232262306a36Sopenharmony_ci{
232362306a36Sopenharmony_ci	struct channel *pch = chan->ppp;
232462306a36Sopenharmony_ci	struct sk_buff *skb;
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	if (!pch)
232762306a36Sopenharmony_ci		return;
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci	read_lock_bh(&pch->upl);
233062306a36Sopenharmony_ci	if (pch->ppp) {
233162306a36Sopenharmony_ci		skb = alloc_skb(0, GFP_ATOMIC);
233262306a36Sopenharmony_ci		if (skb) {
233362306a36Sopenharmony_ci			skb->len = 0;		/* probably unnecessary */
233462306a36Sopenharmony_ci			skb->cb[0] = code;
233562306a36Sopenharmony_ci			ppp_do_recv(pch->ppp, skb, pch);
233662306a36Sopenharmony_ci		}
233762306a36Sopenharmony_ci	}
233862306a36Sopenharmony_ci	read_unlock_bh(&pch->upl);
233962306a36Sopenharmony_ci}
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci/*
234262306a36Sopenharmony_ci * We come in here to process a received frame.
234362306a36Sopenharmony_ci * The receive side of the ppp unit is locked.
234462306a36Sopenharmony_ci */
234562306a36Sopenharmony_cistatic void
234662306a36Sopenharmony_cippp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
234762306a36Sopenharmony_ci{
234862306a36Sopenharmony_ci	/* note: a 0-length skb is used as an error indication */
234962306a36Sopenharmony_ci	if (skb->len > 0) {
235062306a36Sopenharmony_ci		skb_checksum_complete_unset(skb);
235162306a36Sopenharmony_ci#ifdef CONFIG_PPP_MULTILINK
235262306a36Sopenharmony_ci		/* XXX do channel-level decompression here */
235362306a36Sopenharmony_ci		if (PPP_PROTO(skb) == PPP_MP)
235462306a36Sopenharmony_ci			ppp_receive_mp_frame(ppp, skb, pch);
235562306a36Sopenharmony_ci		else
235662306a36Sopenharmony_ci#endif /* CONFIG_PPP_MULTILINK */
235762306a36Sopenharmony_ci			ppp_receive_nonmp_frame(ppp, skb);
235862306a36Sopenharmony_ci	} else {
235962306a36Sopenharmony_ci		kfree_skb(skb);
236062306a36Sopenharmony_ci		ppp_receive_error(ppp);
236162306a36Sopenharmony_ci	}
236262306a36Sopenharmony_ci}
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_cistatic void
236562306a36Sopenharmony_cippp_receive_error(struct ppp *ppp)
236662306a36Sopenharmony_ci{
236762306a36Sopenharmony_ci	++ppp->dev->stats.rx_errors;
236862306a36Sopenharmony_ci	if (ppp->vj)
236962306a36Sopenharmony_ci		slhc_toss(ppp->vj);
237062306a36Sopenharmony_ci}
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_cistatic void
237362306a36Sopenharmony_cippp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
237462306a36Sopenharmony_ci{
237562306a36Sopenharmony_ci	struct sk_buff *ns;
237662306a36Sopenharmony_ci	int proto, len, npi;
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci	/*
237962306a36Sopenharmony_ci	 * Decompress the frame, if compressed.
238062306a36Sopenharmony_ci	 * Note that some decompressors need to see uncompressed frames
238162306a36Sopenharmony_ci	 * that come in as well as compressed frames.
238262306a36Sopenharmony_ci	 */
238362306a36Sopenharmony_ci	if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN) &&
238462306a36Sopenharmony_ci	    (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)
238562306a36Sopenharmony_ci		skb = ppp_decompress_frame(ppp, skb);
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci	if (ppp->flags & SC_MUST_COMP && ppp->rstate & SC_DC_FERROR)
238862306a36Sopenharmony_ci		goto err;
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	/* At this point the "Protocol" field MUST be decompressed, either in
239162306a36Sopenharmony_ci	 * ppp_input(), ppp_decompress_frame() or in ppp_receive_mp_frame().
239262306a36Sopenharmony_ci	 */
239362306a36Sopenharmony_ci	proto = PPP_PROTO(skb);
239462306a36Sopenharmony_ci	switch (proto) {
239562306a36Sopenharmony_ci	case PPP_VJC_COMP:
239662306a36Sopenharmony_ci		/* decompress VJ compressed packets */
239762306a36Sopenharmony_ci		if (!ppp->vj || (ppp->flags & SC_REJ_COMP_TCP))
239862306a36Sopenharmony_ci			goto err;
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci		if (skb_tailroom(skb) < 124 || skb_cloned(skb)) {
240162306a36Sopenharmony_ci			/* copy to a new sk_buff with more tailroom */
240262306a36Sopenharmony_ci			ns = dev_alloc_skb(skb->len + 128);
240362306a36Sopenharmony_ci			if (!ns) {
240462306a36Sopenharmony_ci				netdev_err(ppp->dev, "PPP: no memory "
240562306a36Sopenharmony_ci					   "(VJ decomp)\n");
240662306a36Sopenharmony_ci				goto err;
240762306a36Sopenharmony_ci			}
240862306a36Sopenharmony_ci			skb_reserve(ns, 2);
240962306a36Sopenharmony_ci			skb_copy_bits(skb, 0, skb_put(ns, skb->len), skb->len);
241062306a36Sopenharmony_ci			consume_skb(skb);
241162306a36Sopenharmony_ci			skb = ns;
241262306a36Sopenharmony_ci		}
241362306a36Sopenharmony_ci		else
241462306a36Sopenharmony_ci			skb->ip_summed = CHECKSUM_NONE;
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci		len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);
241762306a36Sopenharmony_ci		if (len <= 0) {
241862306a36Sopenharmony_ci			netdev_printk(KERN_DEBUG, ppp->dev,
241962306a36Sopenharmony_ci				      "PPP: VJ decompression error\n");
242062306a36Sopenharmony_ci			goto err;
242162306a36Sopenharmony_ci		}
242262306a36Sopenharmony_ci		len += 2;
242362306a36Sopenharmony_ci		if (len > skb->len)
242462306a36Sopenharmony_ci			skb_put(skb, len - skb->len);
242562306a36Sopenharmony_ci		else if (len < skb->len)
242662306a36Sopenharmony_ci			skb_trim(skb, len);
242762306a36Sopenharmony_ci		proto = PPP_IP;
242862306a36Sopenharmony_ci		break;
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	case PPP_VJC_UNCOMP:
243162306a36Sopenharmony_ci		if (!ppp->vj || (ppp->flags & SC_REJ_COMP_TCP))
243262306a36Sopenharmony_ci			goto err;
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci		/* Until we fix the decompressor need to make sure
243562306a36Sopenharmony_ci		 * data portion is linear.
243662306a36Sopenharmony_ci		 */
243762306a36Sopenharmony_ci		if (!pskb_may_pull(skb, skb->len))
243862306a36Sopenharmony_ci			goto err;
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_ci		if (slhc_remember(ppp->vj, skb->data + 2, skb->len - 2) <= 0) {
244162306a36Sopenharmony_ci			netdev_err(ppp->dev, "PPP: VJ uncompressed error\n");
244262306a36Sopenharmony_ci			goto err;
244362306a36Sopenharmony_ci		}
244462306a36Sopenharmony_ci		proto = PPP_IP;
244562306a36Sopenharmony_ci		break;
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	case PPP_CCP:
244862306a36Sopenharmony_ci		ppp_ccp_peek(ppp, skb, 1);
244962306a36Sopenharmony_ci		break;
245062306a36Sopenharmony_ci	}
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci	++ppp->stats64.rx_packets;
245362306a36Sopenharmony_ci	ppp->stats64.rx_bytes += skb->len - 2;
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	npi = proto_to_npindex(proto);
245662306a36Sopenharmony_ci	if (npi < 0) {
245762306a36Sopenharmony_ci		/* control or unknown frame - pass it to pppd */
245862306a36Sopenharmony_ci		skb_queue_tail(&ppp->file.rq, skb);
245962306a36Sopenharmony_ci		/* limit queue length by dropping old frames */
246062306a36Sopenharmony_ci		while (ppp->file.rq.qlen > PPP_MAX_RQLEN &&
246162306a36Sopenharmony_ci		       (skb = skb_dequeue(&ppp->file.rq)))
246262306a36Sopenharmony_ci			kfree_skb(skb);
246362306a36Sopenharmony_ci		/* wake up any process polling or blocking on read */
246462306a36Sopenharmony_ci		wake_up_interruptible(&ppp->file.rwait);
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	} else {
246762306a36Sopenharmony_ci		/* network protocol frame - give it to the kernel */
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci#ifdef CONFIG_PPP_FILTER
247062306a36Sopenharmony_ci		/* check if the packet passes the pass and active filters */
247162306a36Sopenharmony_ci		/* the filter instructions are constructed assuming
247262306a36Sopenharmony_ci		   a four-byte PPP header on each packet */
247362306a36Sopenharmony_ci		if (ppp->pass_filter || ppp->active_filter) {
247462306a36Sopenharmony_ci			if (skb_unclone(skb, GFP_ATOMIC))
247562306a36Sopenharmony_ci				goto err;
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci			*(u8 *)skb_push(skb, 2) = 0;
247862306a36Sopenharmony_ci			if (ppp->pass_filter &&
247962306a36Sopenharmony_ci			    bpf_prog_run(ppp->pass_filter, skb) == 0) {
248062306a36Sopenharmony_ci				if (ppp->debug & 1)
248162306a36Sopenharmony_ci					netdev_printk(KERN_DEBUG, ppp->dev,
248262306a36Sopenharmony_ci						      "PPP: inbound frame "
248362306a36Sopenharmony_ci						      "not passed\n");
248462306a36Sopenharmony_ci				kfree_skb(skb);
248562306a36Sopenharmony_ci				return;
248662306a36Sopenharmony_ci			}
248762306a36Sopenharmony_ci			if (!(ppp->active_filter &&
248862306a36Sopenharmony_ci			      bpf_prog_run(ppp->active_filter, skb) == 0))
248962306a36Sopenharmony_ci				ppp->last_recv = jiffies;
249062306a36Sopenharmony_ci			__skb_pull(skb, 2);
249162306a36Sopenharmony_ci		} else
249262306a36Sopenharmony_ci#endif /* CONFIG_PPP_FILTER */
249362306a36Sopenharmony_ci			ppp->last_recv = jiffies;
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci		if ((ppp->dev->flags & IFF_UP) == 0 ||
249662306a36Sopenharmony_ci		    ppp->npmode[npi] != NPMODE_PASS) {
249762306a36Sopenharmony_ci			kfree_skb(skb);
249862306a36Sopenharmony_ci		} else {
249962306a36Sopenharmony_ci			/* chop off protocol */
250062306a36Sopenharmony_ci			skb_pull_rcsum(skb, 2);
250162306a36Sopenharmony_ci			skb->dev = ppp->dev;
250262306a36Sopenharmony_ci			skb->protocol = htons(npindex_to_ethertype[npi]);
250362306a36Sopenharmony_ci			skb_reset_mac_header(skb);
250462306a36Sopenharmony_ci			skb_scrub_packet(skb, !net_eq(ppp->ppp_net,
250562306a36Sopenharmony_ci						      dev_net(ppp->dev)));
250662306a36Sopenharmony_ci			netif_rx(skb);
250762306a36Sopenharmony_ci		}
250862306a36Sopenharmony_ci	}
250962306a36Sopenharmony_ci	return;
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci err:
251262306a36Sopenharmony_ci	kfree_skb(skb);
251362306a36Sopenharmony_ci	ppp_receive_error(ppp);
251462306a36Sopenharmony_ci}
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_cistatic struct sk_buff *
251762306a36Sopenharmony_cippp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
251862306a36Sopenharmony_ci{
251962306a36Sopenharmony_ci	int proto = PPP_PROTO(skb);
252062306a36Sopenharmony_ci	struct sk_buff *ns;
252162306a36Sopenharmony_ci	int len;
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci	/* Until we fix all the decompressor's need to make sure
252462306a36Sopenharmony_ci	 * data portion is linear.
252562306a36Sopenharmony_ci	 */
252662306a36Sopenharmony_ci	if (!pskb_may_pull(skb, skb->len))
252762306a36Sopenharmony_ci		goto err;
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci	if (proto == PPP_COMP) {
253062306a36Sopenharmony_ci		int obuff_size;
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci		switch(ppp->rcomp->compress_proto) {
253362306a36Sopenharmony_ci		case CI_MPPE:
253462306a36Sopenharmony_ci			obuff_size = ppp->mru + PPP_HDRLEN + 1;
253562306a36Sopenharmony_ci			break;
253662306a36Sopenharmony_ci		default:
253762306a36Sopenharmony_ci			obuff_size = ppp->mru + PPP_HDRLEN;
253862306a36Sopenharmony_ci			break;
253962306a36Sopenharmony_ci		}
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci		ns = dev_alloc_skb(obuff_size);
254262306a36Sopenharmony_ci		if (!ns) {
254362306a36Sopenharmony_ci			netdev_err(ppp->dev, "ppp_decompress_frame: "
254462306a36Sopenharmony_ci				   "no memory\n");
254562306a36Sopenharmony_ci			goto err;
254662306a36Sopenharmony_ci		}
254762306a36Sopenharmony_ci		/* the decompressor still expects the A/C bytes in the hdr */
254862306a36Sopenharmony_ci		len = ppp->rcomp->decompress(ppp->rc_state, skb->data - 2,
254962306a36Sopenharmony_ci				skb->len + 2, ns->data, obuff_size);
255062306a36Sopenharmony_ci		if (len < 0) {
255162306a36Sopenharmony_ci			/* Pass the compressed frame to pppd as an
255262306a36Sopenharmony_ci			   error indication. */
255362306a36Sopenharmony_ci			if (len == DECOMP_FATALERROR)
255462306a36Sopenharmony_ci				ppp->rstate |= SC_DC_FERROR;
255562306a36Sopenharmony_ci			kfree_skb(ns);
255662306a36Sopenharmony_ci			goto err;
255762306a36Sopenharmony_ci		}
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci		consume_skb(skb);
256062306a36Sopenharmony_ci		skb = ns;
256162306a36Sopenharmony_ci		skb_put(skb, len);
256262306a36Sopenharmony_ci		skb_pull(skb, 2);	/* pull off the A/C bytes */
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci		/* Don't call __ppp_decompress_proto() here, but instead rely on
256562306a36Sopenharmony_ci		 * corresponding algo (mppe/bsd/deflate) to decompress it.
256662306a36Sopenharmony_ci		 */
256762306a36Sopenharmony_ci	} else {
256862306a36Sopenharmony_ci		/* Uncompressed frame - pass to decompressor so it
256962306a36Sopenharmony_ci		   can update its dictionary if necessary. */
257062306a36Sopenharmony_ci		if (ppp->rcomp->incomp)
257162306a36Sopenharmony_ci			ppp->rcomp->incomp(ppp->rc_state, skb->data - 2,
257262306a36Sopenharmony_ci					   skb->len + 2);
257362306a36Sopenharmony_ci	}
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	return skb;
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci err:
257862306a36Sopenharmony_ci	ppp->rstate |= SC_DC_ERROR;
257962306a36Sopenharmony_ci	ppp_receive_error(ppp);
258062306a36Sopenharmony_ci	return skb;
258162306a36Sopenharmony_ci}
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci#ifdef CONFIG_PPP_MULTILINK
258462306a36Sopenharmony_ci/*
258562306a36Sopenharmony_ci * Receive a multilink frame.
258662306a36Sopenharmony_ci * We put it on the reconstruction queue and then pull off
258762306a36Sopenharmony_ci * as many completed frames as we can.
258862306a36Sopenharmony_ci */
258962306a36Sopenharmony_cistatic void
259062306a36Sopenharmony_cippp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
259162306a36Sopenharmony_ci{
259262306a36Sopenharmony_ci	u32 mask, seq;
259362306a36Sopenharmony_ci	struct channel *ch;
259462306a36Sopenharmony_ci	int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci	if (!pskb_may_pull(skb, mphdrlen + 1) || ppp->mrru == 0)
259762306a36Sopenharmony_ci		goto err;		/* no good, throw it away */
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci	/* Decode sequence number and begin/end bits */
260062306a36Sopenharmony_ci	if (ppp->flags & SC_MP_SHORTSEQ) {
260162306a36Sopenharmony_ci		seq = ((skb->data[2] & 0x0f) << 8) | skb->data[3];
260262306a36Sopenharmony_ci		mask = 0xfff;
260362306a36Sopenharmony_ci	} else {
260462306a36Sopenharmony_ci		seq = (skb->data[3] << 16) | (skb->data[4] << 8)| skb->data[5];
260562306a36Sopenharmony_ci		mask = 0xffffff;
260662306a36Sopenharmony_ci	}
260762306a36Sopenharmony_ci	PPP_MP_CB(skb)->BEbits = skb->data[2];
260862306a36Sopenharmony_ci	skb_pull(skb, mphdrlen);	/* pull off PPP and MP headers */
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci	/*
261162306a36Sopenharmony_ci	 * Do protocol ID decompression on the first fragment of each packet.
261262306a36Sopenharmony_ci	 * We have to do that here, because ppp_receive_nonmp_frame() expects
261362306a36Sopenharmony_ci	 * decompressed protocol field.
261462306a36Sopenharmony_ci	 */
261562306a36Sopenharmony_ci	if (PPP_MP_CB(skb)->BEbits & B)
261662306a36Sopenharmony_ci		__ppp_decompress_proto(skb);
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci	/*
261962306a36Sopenharmony_ci	 * Expand sequence number to 32 bits, making it as close
262062306a36Sopenharmony_ci	 * as possible to ppp->minseq.
262162306a36Sopenharmony_ci	 */
262262306a36Sopenharmony_ci	seq |= ppp->minseq & ~mask;
262362306a36Sopenharmony_ci	if ((int)(ppp->minseq - seq) > (int)(mask >> 1))
262462306a36Sopenharmony_ci		seq += mask + 1;
262562306a36Sopenharmony_ci	else if ((int)(seq - ppp->minseq) > (int)(mask >> 1))
262662306a36Sopenharmony_ci		seq -= mask + 1;	/* should never happen */
262762306a36Sopenharmony_ci	PPP_MP_CB(skb)->sequence = seq;
262862306a36Sopenharmony_ci	pch->lastseq = seq;
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci	/*
263162306a36Sopenharmony_ci	 * If this packet comes before the next one we were expecting,
263262306a36Sopenharmony_ci	 * drop it.
263362306a36Sopenharmony_ci	 */
263462306a36Sopenharmony_ci	if (seq_before(seq, ppp->nextseq)) {
263562306a36Sopenharmony_ci		kfree_skb(skb);
263662306a36Sopenharmony_ci		++ppp->dev->stats.rx_dropped;
263762306a36Sopenharmony_ci		ppp_receive_error(ppp);
263862306a36Sopenharmony_ci		return;
263962306a36Sopenharmony_ci	}
264062306a36Sopenharmony_ci
264162306a36Sopenharmony_ci	/*
264262306a36Sopenharmony_ci	 * Reevaluate minseq, the minimum over all channels of the
264362306a36Sopenharmony_ci	 * last sequence number received on each channel.  Because of
264462306a36Sopenharmony_ci	 * the increasing sequence number rule, we know that any fragment
264562306a36Sopenharmony_ci	 * before `minseq' which hasn't arrived is never going to arrive.
264662306a36Sopenharmony_ci	 * The list of channels can't change because we have the receive
264762306a36Sopenharmony_ci	 * side of the ppp unit locked.
264862306a36Sopenharmony_ci	 */
264962306a36Sopenharmony_ci	list_for_each_entry(ch, &ppp->channels, clist) {
265062306a36Sopenharmony_ci		if (seq_before(ch->lastseq, seq))
265162306a36Sopenharmony_ci			seq = ch->lastseq;
265262306a36Sopenharmony_ci	}
265362306a36Sopenharmony_ci	if (seq_before(ppp->minseq, seq))
265462306a36Sopenharmony_ci		ppp->minseq = seq;
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci	/* Put the fragment on the reconstruction queue */
265762306a36Sopenharmony_ci	ppp_mp_insert(ppp, skb);
265862306a36Sopenharmony_ci
265962306a36Sopenharmony_ci	/* If the queue is getting long, don't wait any longer for packets
266062306a36Sopenharmony_ci	   before the start of the queue. */
266162306a36Sopenharmony_ci	if (skb_queue_len(&ppp->mrq) >= PPP_MP_MAX_QLEN) {
266262306a36Sopenharmony_ci		struct sk_buff *mskb = skb_peek(&ppp->mrq);
266362306a36Sopenharmony_ci		if (seq_before(ppp->minseq, PPP_MP_CB(mskb)->sequence))
266462306a36Sopenharmony_ci			ppp->minseq = PPP_MP_CB(mskb)->sequence;
266562306a36Sopenharmony_ci	}
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci	/* Pull completed packets off the queue and receive them. */
266862306a36Sopenharmony_ci	while ((skb = ppp_mp_reconstruct(ppp))) {
266962306a36Sopenharmony_ci		if (pskb_may_pull(skb, 2))
267062306a36Sopenharmony_ci			ppp_receive_nonmp_frame(ppp, skb);
267162306a36Sopenharmony_ci		else {
267262306a36Sopenharmony_ci			++ppp->dev->stats.rx_length_errors;
267362306a36Sopenharmony_ci			kfree_skb(skb);
267462306a36Sopenharmony_ci			ppp_receive_error(ppp);
267562306a36Sopenharmony_ci		}
267662306a36Sopenharmony_ci	}
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	return;
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci err:
268162306a36Sopenharmony_ci	kfree_skb(skb);
268262306a36Sopenharmony_ci	ppp_receive_error(ppp);
268362306a36Sopenharmony_ci}
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci/*
268662306a36Sopenharmony_ci * Insert a fragment on the MP reconstruction queue.
268762306a36Sopenharmony_ci * The queue is ordered by increasing sequence number.
268862306a36Sopenharmony_ci */
268962306a36Sopenharmony_cistatic void
269062306a36Sopenharmony_cippp_mp_insert(struct ppp *ppp, struct sk_buff *skb)
269162306a36Sopenharmony_ci{
269262306a36Sopenharmony_ci	struct sk_buff *p;
269362306a36Sopenharmony_ci	struct sk_buff_head *list = &ppp->mrq;
269462306a36Sopenharmony_ci	u32 seq = PPP_MP_CB(skb)->sequence;
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	/* N.B. we don't need to lock the list lock because we have the
269762306a36Sopenharmony_ci	   ppp unit receive-side lock. */
269862306a36Sopenharmony_ci	skb_queue_walk(list, p) {
269962306a36Sopenharmony_ci		if (seq_before(seq, PPP_MP_CB(p)->sequence))
270062306a36Sopenharmony_ci			break;
270162306a36Sopenharmony_ci	}
270262306a36Sopenharmony_ci	__skb_queue_before(list, p, skb);
270362306a36Sopenharmony_ci}
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_ci/*
270662306a36Sopenharmony_ci * Reconstruct a packet from the MP fragment queue.
270762306a36Sopenharmony_ci * We go through increasing sequence numbers until we find a
270862306a36Sopenharmony_ci * complete packet, or we get to the sequence number for a fragment
270962306a36Sopenharmony_ci * which hasn't arrived but might still do so.
271062306a36Sopenharmony_ci */
271162306a36Sopenharmony_cistatic struct sk_buff *
271262306a36Sopenharmony_cippp_mp_reconstruct(struct ppp *ppp)
271362306a36Sopenharmony_ci{
271462306a36Sopenharmony_ci	u32 seq = ppp->nextseq;
271562306a36Sopenharmony_ci	u32 minseq = ppp->minseq;
271662306a36Sopenharmony_ci	struct sk_buff_head *list = &ppp->mrq;
271762306a36Sopenharmony_ci	struct sk_buff *p, *tmp;
271862306a36Sopenharmony_ci	struct sk_buff *head, *tail;
271962306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
272062306a36Sopenharmony_ci	int lost = 0, len = 0;
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_ci	if (ppp->mrru == 0)	/* do nothing until mrru is set */
272362306a36Sopenharmony_ci		return NULL;
272462306a36Sopenharmony_ci	head = __skb_peek(list);
272562306a36Sopenharmony_ci	tail = NULL;
272662306a36Sopenharmony_ci	skb_queue_walk_safe(list, p, tmp) {
272762306a36Sopenharmony_ci	again:
272862306a36Sopenharmony_ci		if (seq_before(PPP_MP_CB(p)->sequence, seq)) {
272962306a36Sopenharmony_ci			/* this can't happen, anyway ignore the skb */
273062306a36Sopenharmony_ci			netdev_err(ppp->dev, "ppp_mp_reconstruct bad "
273162306a36Sopenharmony_ci				   "seq %u < %u\n",
273262306a36Sopenharmony_ci				   PPP_MP_CB(p)->sequence, seq);
273362306a36Sopenharmony_ci			__skb_unlink(p, list);
273462306a36Sopenharmony_ci			kfree_skb(p);
273562306a36Sopenharmony_ci			continue;
273662306a36Sopenharmony_ci		}
273762306a36Sopenharmony_ci		if (PPP_MP_CB(p)->sequence != seq) {
273862306a36Sopenharmony_ci			u32 oldseq;
273962306a36Sopenharmony_ci			/* Fragment `seq' is missing.  If it is after
274062306a36Sopenharmony_ci			   minseq, it might arrive later, so stop here. */
274162306a36Sopenharmony_ci			if (seq_after(seq, minseq))
274262306a36Sopenharmony_ci				break;
274362306a36Sopenharmony_ci			/* Fragment `seq' is lost, keep going. */
274462306a36Sopenharmony_ci			lost = 1;
274562306a36Sopenharmony_ci			oldseq = seq;
274662306a36Sopenharmony_ci			seq = seq_before(minseq, PPP_MP_CB(p)->sequence)?
274762306a36Sopenharmony_ci				minseq + 1: PPP_MP_CB(p)->sequence;
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci			if (ppp->debug & 1)
275062306a36Sopenharmony_ci				netdev_printk(KERN_DEBUG, ppp->dev,
275162306a36Sopenharmony_ci					      "lost frag %u..%u\n",
275262306a36Sopenharmony_ci					      oldseq, seq-1);
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_ci			goto again;
275562306a36Sopenharmony_ci		}
275662306a36Sopenharmony_ci
275762306a36Sopenharmony_ci		/*
275862306a36Sopenharmony_ci		 * At this point we know that all the fragments from
275962306a36Sopenharmony_ci		 * ppp->nextseq to seq are either present or lost.
276062306a36Sopenharmony_ci		 * Also, there are no complete packets in the queue
276162306a36Sopenharmony_ci		 * that have no missing fragments and end before this
276262306a36Sopenharmony_ci		 * fragment.
276362306a36Sopenharmony_ci		 */
276462306a36Sopenharmony_ci
276562306a36Sopenharmony_ci		/* B bit set indicates this fragment starts a packet */
276662306a36Sopenharmony_ci		if (PPP_MP_CB(p)->BEbits & B) {
276762306a36Sopenharmony_ci			head = p;
276862306a36Sopenharmony_ci			lost = 0;
276962306a36Sopenharmony_ci			len = 0;
277062306a36Sopenharmony_ci		}
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci		len += p->len;
277362306a36Sopenharmony_ci
277462306a36Sopenharmony_ci		/* Got a complete packet yet? */
277562306a36Sopenharmony_ci		if (lost == 0 && (PPP_MP_CB(p)->BEbits & E) &&
277662306a36Sopenharmony_ci		    (PPP_MP_CB(head)->BEbits & B)) {
277762306a36Sopenharmony_ci			if (len > ppp->mrru + 2) {
277862306a36Sopenharmony_ci				++ppp->dev->stats.rx_length_errors;
277962306a36Sopenharmony_ci				netdev_printk(KERN_DEBUG, ppp->dev,
278062306a36Sopenharmony_ci					      "PPP: reconstructed packet"
278162306a36Sopenharmony_ci					      " is too long (%d)\n", len);
278262306a36Sopenharmony_ci			} else {
278362306a36Sopenharmony_ci				tail = p;
278462306a36Sopenharmony_ci				break;
278562306a36Sopenharmony_ci			}
278662306a36Sopenharmony_ci			ppp->nextseq = seq + 1;
278762306a36Sopenharmony_ci		}
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci		/*
279062306a36Sopenharmony_ci		 * If this is the ending fragment of a packet,
279162306a36Sopenharmony_ci		 * and we haven't found a complete valid packet yet,
279262306a36Sopenharmony_ci		 * we can discard up to and including this fragment.
279362306a36Sopenharmony_ci		 */
279462306a36Sopenharmony_ci		if (PPP_MP_CB(p)->BEbits & E) {
279562306a36Sopenharmony_ci			struct sk_buff *tmp2;
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci			skb_queue_reverse_walk_from_safe(list, p, tmp2) {
279862306a36Sopenharmony_ci				if (ppp->debug & 1)
279962306a36Sopenharmony_ci					netdev_printk(KERN_DEBUG, ppp->dev,
280062306a36Sopenharmony_ci						      "discarding frag %u\n",
280162306a36Sopenharmony_ci						      PPP_MP_CB(p)->sequence);
280262306a36Sopenharmony_ci				__skb_unlink(p, list);
280362306a36Sopenharmony_ci				kfree_skb(p);
280462306a36Sopenharmony_ci			}
280562306a36Sopenharmony_ci			head = skb_peek(list);
280662306a36Sopenharmony_ci			if (!head)
280762306a36Sopenharmony_ci				break;
280862306a36Sopenharmony_ci		}
280962306a36Sopenharmony_ci		++seq;
281062306a36Sopenharmony_ci	}
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci	/* If we have a complete packet, copy it all into one skb. */
281362306a36Sopenharmony_ci	if (tail != NULL) {
281462306a36Sopenharmony_ci		/* If we have discarded any fragments,
281562306a36Sopenharmony_ci		   signal a receive error. */
281662306a36Sopenharmony_ci		if (PPP_MP_CB(head)->sequence != ppp->nextseq) {
281762306a36Sopenharmony_ci			skb_queue_walk_safe(list, p, tmp) {
281862306a36Sopenharmony_ci				if (p == head)
281962306a36Sopenharmony_ci					break;
282062306a36Sopenharmony_ci				if (ppp->debug & 1)
282162306a36Sopenharmony_ci					netdev_printk(KERN_DEBUG, ppp->dev,
282262306a36Sopenharmony_ci						      "discarding frag %u\n",
282362306a36Sopenharmony_ci						      PPP_MP_CB(p)->sequence);
282462306a36Sopenharmony_ci				__skb_unlink(p, list);
282562306a36Sopenharmony_ci				kfree_skb(p);
282662306a36Sopenharmony_ci			}
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ci			if (ppp->debug & 1)
282962306a36Sopenharmony_ci				netdev_printk(KERN_DEBUG, ppp->dev,
283062306a36Sopenharmony_ci					      "  missed pkts %u..%u\n",
283162306a36Sopenharmony_ci					      ppp->nextseq,
283262306a36Sopenharmony_ci					      PPP_MP_CB(head)->sequence-1);
283362306a36Sopenharmony_ci			++ppp->dev->stats.rx_dropped;
283462306a36Sopenharmony_ci			ppp_receive_error(ppp);
283562306a36Sopenharmony_ci		}
283662306a36Sopenharmony_ci
283762306a36Sopenharmony_ci		skb = head;
283862306a36Sopenharmony_ci		if (head != tail) {
283962306a36Sopenharmony_ci			struct sk_buff **fragpp = &skb_shinfo(skb)->frag_list;
284062306a36Sopenharmony_ci			p = skb_queue_next(list, head);
284162306a36Sopenharmony_ci			__skb_unlink(skb, list);
284262306a36Sopenharmony_ci			skb_queue_walk_from_safe(list, p, tmp) {
284362306a36Sopenharmony_ci				__skb_unlink(p, list);
284462306a36Sopenharmony_ci				*fragpp = p;
284562306a36Sopenharmony_ci				p->next = NULL;
284662306a36Sopenharmony_ci				fragpp = &p->next;
284762306a36Sopenharmony_ci
284862306a36Sopenharmony_ci				skb->len += p->len;
284962306a36Sopenharmony_ci				skb->data_len += p->len;
285062306a36Sopenharmony_ci				skb->truesize += p->truesize;
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_ci				if (p == tail)
285362306a36Sopenharmony_ci					break;
285462306a36Sopenharmony_ci			}
285562306a36Sopenharmony_ci		} else {
285662306a36Sopenharmony_ci			__skb_unlink(skb, list);
285762306a36Sopenharmony_ci		}
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_ci		ppp->nextseq = PPP_MP_CB(tail)->sequence + 1;
286062306a36Sopenharmony_ci	}
286162306a36Sopenharmony_ci
286262306a36Sopenharmony_ci	return skb;
286362306a36Sopenharmony_ci}
286462306a36Sopenharmony_ci#endif /* CONFIG_PPP_MULTILINK */
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_ci/*
286762306a36Sopenharmony_ci * Channel interface.
286862306a36Sopenharmony_ci */
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci/* Create a new, unattached ppp channel. */
287162306a36Sopenharmony_ciint ppp_register_channel(struct ppp_channel *chan)
287262306a36Sopenharmony_ci{
287362306a36Sopenharmony_ci	return ppp_register_net_channel(current->nsproxy->net_ns, chan);
287462306a36Sopenharmony_ci}
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ci/* Create a new, unattached ppp channel for specified net. */
287762306a36Sopenharmony_ciint ppp_register_net_channel(struct net *net, struct ppp_channel *chan)
287862306a36Sopenharmony_ci{
287962306a36Sopenharmony_ci	struct channel *pch;
288062306a36Sopenharmony_ci	struct ppp_net *pn;
288162306a36Sopenharmony_ci
288262306a36Sopenharmony_ci	pch = kzalloc(sizeof(struct channel), GFP_KERNEL);
288362306a36Sopenharmony_ci	if (!pch)
288462306a36Sopenharmony_ci		return -ENOMEM;
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_ci	pn = ppp_pernet(net);
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_ci	pch->ppp = NULL;
288962306a36Sopenharmony_ci	pch->chan = chan;
289062306a36Sopenharmony_ci	pch->chan_net = get_net_track(net, &pch->ns_tracker, GFP_KERNEL);
289162306a36Sopenharmony_ci	chan->ppp = pch;
289262306a36Sopenharmony_ci	init_ppp_file(&pch->file, CHANNEL);
289362306a36Sopenharmony_ci	pch->file.hdrlen = chan->hdrlen;
289462306a36Sopenharmony_ci#ifdef CONFIG_PPP_MULTILINK
289562306a36Sopenharmony_ci	pch->lastseq = -1;
289662306a36Sopenharmony_ci#endif /* CONFIG_PPP_MULTILINK */
289762306a36Sopenharmony_ci	init_rwsem(&pch->chan_sem);
289862306a36Sopenharmony_ci	spin_lock_init(&pch->downl);
289962306a36Sopenharmony_ci	rwlock_init(&pch->upl);
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci	spin_lock_bh(&pn->all_channels_lock);
290262306a36Sopenharmony_ci	pch->file.index = ++pn->last_channel_index;
290362306a36Sopenharmony_ci	list_add(&pch->list, &pn->new_channels);
290462306a36Sopenharmony_ci	atomic_inc(&channel_count);
290562306a36Sopenharmony_ci	spin_unlock_bh(&pn->all_channels_lock);
290662306a36Sopenharmony_ci
290762306a36Sopenharmony_ci	return 0;
290862306a36Sopenharmony_ci}
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci/*
291162306a36Sopenharmony_ci * Return the index of a channel.
291262306a36Sopenharmony_ci */
291362306a36Sopenharmony_ciint ppp_channel_index(struct ppp_channel *chan)
291462306a36Sopenharmony_ci{
291562306a36Sopenharmony_ci	struct channel *pch = chan->ppp;
291662306a36Sopenharmony_ci
291762306a36Sopenharmony_ci	if (pch)
291862306a36Sopenharmony_ci		return pch->file.index;
291962306a36Sopenharmony_ci	return -1;
292062306a36Sopenharmony_ci}
292162306a36Sopenharmony_ci
292262306a36Sopenharmony_ci/*
292362306a36Sopenharmony_ci * Return the PPP unit number to which a channel is connected.
292462306a36Sopenharmony_ci */
292562306a36Sopenharmony_ciint ppp_unit_number(struct ppp_channel *chan)
292662306a36Sopenharmony_ci{
292762306a36Sopenharmony_ci	struct channel *pch = chan->ppp;
292862306a36Sopenharmony_ci	int unit = -1;
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci	if (pch) {
293162306a36Sopenharmony_ci		read_lock_bh(&pch->upl);
293262306a36Sopenharmony_ci		if (pch->ppp)
293362306a36Sopenharmony_ci			unit = pch->ppp->file.index;
293462306a36Sopenharmony_ci		read_unlock_bh(&pch->upl);
293562306a36Sopenharmony_ci	}
293662306a36Sopenharmony_ci	return unit;
293762306a36Sopenharmony_ci}
293862306a36Sopenharmony_ci
293962306a36Sopenharmony_ci/*
294062306a36Sopenharmony_ci * Return the PPP device interface name of a channel.
294162306a36Sopenharmony_ci */
294262306a36Sopenharmony_cichar *ppp_dev_name(struct ppp_channel *chan)
294362306a36Sopenharmony_ci{
294462306a36Sopenharmony_ci	struct channel *pch = chan->ppp;
294562306a36Sopenharmony_ci	char *name = NULL;
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci	if (pch) {
294862306a36Sopenharmony_ci		read_lock_bh(&pch->upl);
294962306a36Sopenharmony_ci		if (pch->ppp && pch->ppp->dev)
295062306a36Sopenharmony_ci			name = pch->ppp->dev->name;
295162306a36Sopenharmony_ci		read_unlock_bh(&pch->upl);
295262306a36Sopenharmony_ci	}
295362306a36Sopenharmony_ci	return name;
295462306a36Sopenharmony_ci}
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci/*
295862306a36Sopenharmony_ci * Disconnect a channel from the generic layer.
295962306a36Sopenharmony_ci * This must be called in process context.
296062306a36Sopenharmony_ci */
296162306a36Sopenharmony_civoid
296262306a36Sopenharmony_cippp_unregister_channel(struct ppp_channel *chan)
296362306a36Sopenharmony_ci{
296462306a36Sopenharmony_ci	struct channel *pch = chan->ppp;
296562306a36Sopenharmony_ci	struct ppp_net *pn;
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	if (!pch)
296862306a36Sopenharmony_ci		return;		/* should never happen */
296962306a36Sopenharmony_ci
297062306a36Sopenharmony_ci	chan->ppp = NULL;
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci	/*
297362306a36Sopenharmony_ci	 * This ensures that we have returned from any calls into
297462306a36Sopenharmony_ci	 * the channel's start_xmit or ioctl routine before we proceed.
297562306a36Sopenharmony_ci	 */
297662306a36Sopenharmony_ci	down_write(&pch->chan_sem);
297762306a36Sopenharmony_ci	spin_lock_bh(&pch->downl);
297862306a36Sopenharmony_ci	pch->chan = NULL;
297962306a36Sopenharmony_ci	spin_unlock_bh(&pch->downl);
298062306a36Sopenharmony_ci	up_write(&pch->chan_sem);
298162306a36Sopenharmony_ci	ppp_disconnect_channel(pch);
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci	pn = ppp_pernet(pch->chan_net);
298462306a36Sopenharmony_ci	spin_lock_bh(&pn->all_channels_lock);
298562306a36Sopenharmony_ci	list_del(&pch->list);
298662306a36Sopenharmony_ci	spin_unlock_bh(&pn->all_channels_lock);
298762306a36Sopenharmony_ci
298862306a36Sopenharmony_ci	ppp_unbridge_channels(pch);
298962306a36Sopenharmony_ci
299062306a36Sopenharmony_ci	pch->file.dead = 1;
299162306a36Sopenharmony_ci	wake_up_interruptible(&pch->file.rwait);
299262306a36Sopenharmony_ci
299362306a36Sopenharmony_ci	if (refcount_dec_and_test(&pch->file.refcnt))
299462306a36Sopenharmony_ci		ppp_destroy_channel(pch);
299562306a36Sopenharmony_ci}
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_ci/*
299862306a36Sopenharmony_ci * Callback from a channel when it can accept more to transmit.
299962306a36Sopenharmony_ci * This should be called at BH/softirq level, not interrupt level.
300062306a36Sopenharmony_ci */
300162306a36Sopenharmony_civoid
300262306a36Sopenharmony_cippp_output_wakeup(struct ppp_channel *chan)
300362306a36Sopenharmony_ci{
300462306a36Sopenharmony_ci	struct channel *pch = chan->ppp;
300562306a36Sopenharmony_ci
300662306a36Sopenharmony_ci	if (!pch)
300762306a36Sopenharmony_ci		return;
300862306a36Sopenharmony_ci	ppp_channel_push(pch);
300962306a36Sopenharmony_ci}
301062306a36Sopenharmony_ci
301162306a36Sopenharmony_ci/*
301262306a36Sopenharmony_ci * Compression control.
301362306a36Sopenharmony_ci */
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci/* Process the PPPIOCSCOMPRESS ioctl. */
301662306a36Sopenharmony_cistatic int
301762306a36Sopenharmony_cippp_set_compress(struct ppp *ppp, struct ppp_option_data *data)
301862306a36Sopenharmony_ci{
301962306a36Sopenharmony_ci	int err = -EFAULT;
302062306a36Sopenharmony_ci	struct compressor *cp, *ocomp;
302162306a36Sopenharmony_ci	void *state, *ostate;
302262306a36Sopenharmony_ci	unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	if (data->length > CCP_MAX_OPTION_LENGTH)
302562306a36Sopenharmony_ci		goto out;
302662306a36Sopenharmony_ci	if (copy_from_user(ccp_option, data->ptr, data->length))
302762306a36Sopenharmony_ci		goto out;
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci	err = -EINVAL;
303062306a36Sopenharmony_ci	if (data->length < 2 || ccp_option[1] < 2 || ccp_option[1] > data->length)
303162306a36Sopenharmony_ci		goto out;
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_ci	cp = try_then_request_module(
303462306a36Sopenharmony_ci		find_compressor(ccp_option[0]),
303562306a36Sopenharmony_ci		"ppp-compress-%d", ccp_option[0]);
303662306a36Sopenharmony_ci	if (!cp)
303762306a36Sopenharmony_ci		goto out;
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_ci	err = -ENOBUFS;
304062306a36Sopenharmony_ci	if (data->transmit) {
304162306a36Sopenharmony_ci		state = cp->comp_alloc(ccp_option, data->length);
304262306a36Sopenharmony_ci		if (state) {
304362306a36Sopenharmony_ci			ppp_xmit_lock(ppp);
304462306a36Sopenharmony_ci			ppp->xstate &= ~SC_COMP_RUN;
304562306a36Sopenharmony_ci			ocomp = ppp->xcomp;
304662306a36Sopenharmony_ci			ostate = ppp->xc_state;
304762306a36Sopenharmony_ci			ppp->xcomp = cp;
304862306a36Sopenharmony_ci			ppp->xc_state = state;
304962306a36Sopenharmony_ci			ppp_xmit_unlock(ppp);
305062306a36Sopenharmony_ci			if (ostate) {
305162306a36Sopenharmony_ci				ocomp->comp_free(ostate);
305262306a36Sopenharmony_ci				module_put(ocomp->owner);
305362306a36Sopenharmony_ci			}
305462306a36Sopenharmony_ci			err = 0;
305562306a36Sopenharmony_ci		} else
305662306a36Sopenharmony_ci			module_put(cp->owner);
305762306a36Sopenharmony_ci
305862306a36Sopenharmony_ci	} else {
305962306a36Sopenharmony_ci		state = cp->decomp_alloc(ccp_option, data->length);
306062306a36Sopenharmony_ci		if (state) {
306162306a36Sopenharmony_ci			ppp_recv_lock(ppp);
306262306a36Sopenharmony_ci			ppp->rstate &= ~SC_DECOMP_RUN;
306362306a36Sopenharmony_ci			ocomp = ppp->rcomp;
306462306a36Sopenharmony_ci			ostate = ppp->rc_state;
306562306a36Sopenharmony_ci			ppp->rcomp = cp;
306662306a36Sopenharmony_ci			ppp->rc_state = state;
306762306a36Sopenharmony_ci			ppp_recv_unlock(ppp);
306862306a36Sopenharmony_ci			if (ostate) {
306962306a36Sopenharmony_ci				ocomp->decomp_free(ostate);
307062306a36Sopenharmony_ci				module_put(ocomp->owner);
307162306a36Sopenharmony_ci			}
307262306a36Sopenharmony_ci			err = 0;
307362306a36Sopenharmony_ci		} else
307462306a36Sopenharmony_ci			module_put(cp->owner);
307562306a36Sopenharmony_ci	}
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ci out:
307862306a36Sopenharmony_ci	return err;
307962306a36Sopenharmony_ci}
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_ci/*
308262306a36Sopenharmony_ci * Look at a CCP packet and update our state accordingly.
308362306a36Sopenharmony_ci * We assume the caller has the xmit or recv path locked.
308462306a36Sopenharmony_ci */
308562306a36Sopenharmony_cistatic void
308662306a36Sopenharmony_cippp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
308762306a36Sopenharmony_ci{
308862306a36Sopenharmony_ci	unsigned char *dp;
308962306a36Sopenharmony_ci	int len;
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci	if (!pskb_may_pull(skb, CCP_HDRLEN + 2))
309262306a36Sopenharmony_ci		return;	/* no header */
309362306a36Sopenharmony_ci	dp = skb->data + 2;
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	switch (CCP_CODE(dp)) {
309662306a36Sopenharmony_ci	case CCP_CONFREQ:
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci		/* A ConfReq starts negotiation of compression
309962306a36Sopenharmony_ci		 * in one direction of transmission,
310062306a36Sopenharmony_ci		 * and hence brings it down...but which way?
310162306a36Sopenharmony_ci		 *
310262306a36Sopenharmony_ci		 * Remember:
310362306a36Sopenharmony_ci		 * A ConfReq indicates what the sender would like to receive
310462306a36Sopenharmony_ci		 */
310562306a36Sopenharmony_ci		if(inbound)
310662306a36Sopenharmony_ci			/* He is proposing what I should send */
310762306a36Sopenharmony_ci			ppp->xstate &= ~SC_COMP_RUN;
310862306a36Sopenharmony_ci		else
310962306a36Sopenharmony_ci			/* I am proposing to what he should send */
311062306a36Sopenharmony_ci			ppp->rstate &= ~SC_DECOMP_RUN;
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci		break;
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ci	case CCP_TERMREQ:
311562306a36Sopenharmony_ci	case CCP_TERMACK:
311662306a36Sopenharmony_ci		/*
311762306a36Sopenharmony_ci		 * CCP is going down, both directions of transmission
311862306a36Sopenharmony_ci		 */
311962306a36Sopenharmony_ci		ppp->rstate &= ~SC_DECOMP_RUN;
312062306a36Sopenharmony_ci		ppp->xstate &= ~SC_COMP_RUN;
312162306a36Sopenharmony_ci		break;
312262306a36Sopenharmony_ci
312362306a36Sopenharmony_ci	case CCP_CONFACK:
312462306a36Sopenharmony_ci		if ((ppp->flags & (SC_CCP_OPEN | SC_CCP_UP)) != SC_CCP_OPEN)
312562306a36Sopenharmony_ci			break;
312662306a36Sopenharmony_ci		len = CCP_LENGTH(dp);
312762306a36Sopenharmony_ci		if (!pskb_may_pull(skb, len + 2))
312862306a36Sopenharmony_ci			return;		/* too short */
312962306a36Sopenharmony_ci		dp += CCP_HDRLEN;
313062306a36Sopenharmony_ci		len -= CCP_HDRLEN;
313162306a36Sopenharmony_ci		if (len < CCP_OPT_MINLEN || len < CCP_OPT_LENGTH(dp))
313262306a36Sopenharmony_ci			break;
313362306a36Sopenharmony_ci		if (inbound) {
313462306a36Sopenharmony_ci			/* we will start receiving compressed packets */
313562306a36Sopenharmony_ci			if (!ppp->rc_state)
313662306a36Sopenharmony_ci				break;
313762306a36Sopenharmony_ci			if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len,
313862306a36Sopenharmony_ci					ppp->file.index, 0, ppp->mru, ppp->debug)) {
313962306a36Sopenharmony_ci				ppp->rstate |= SC_DECOMP_RUN;
314062306a36Sopenharmony_ci				ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR);
314162306a36Sopenharmony_ci			}
314262306a36Sopenharmony_ci		} else {
314362306a36Sopenharmony_ci			/* we will soon start sending compressed packets */
314462306a36Sopenharmony_ci			if (!ppp->xc_state)
314562306a36Sopenharmony_ci				break;
314662306a36Sopenharmony_ci			if (ppp->xcomp->comp_init(ppp->xc_state, dp, len,
314762306a36Sopenharmony_ci					ppp->file.index, 0, ppp->debug))
314862306a36Sopenharmony_ci				ppp->xstate |= SC_COMP_RUN;
314962306a36Sopenharmony_ci		}
315062306a36Sopenharmony_ci		break;
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	case CCP_RESETACK:
315362306a36Sopenharmony_ci		/* reset the [de]compressor */
315462306a36Sopenharmony_ci		if ((ppp->flags & SC_CCP_UP) == 0)
315562306a36Sopenharmony_ci			break;
315662306a36Sopenharmony_ci		if (inbound) {
315762306a36Sopenharmony_ci			if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN)) {
315862306a36Sopenharmony_ci				ppp->rcomp->decomp_reset(ppp->rc_state);
315962306a36Sopenharmony_ci				ppp->rstate &= ~SC_DC_ERROR;
316062306a36Sopenharmony_ci			}
316162306a36Sopenharmony_ci		} else {
316262306a36Sopenharmony_ci			if (ppp->xc_state && (ppp->xstate & SC_COMP_RUN))
316362306a36Sopenharmony_ci				ppp->xcomp->comp_reset(ppp->xc_state);
316462306a36Sopenharmony_ci		}
316562306a36Sopenharmony_ci		break;
316662306a36Sopenharmony_ci	}
316762306a36Sopenharmony_ci}
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci/* Free up compression resources. */
317062306a36Sopenharmony_cistatic void
317162306a36Sopenharmony_cippp_ccp_closed(struct ppp *ppp)
317262306a36Sopenharmony_ci{
317362306a36Sopenharmony_ci	void *xstate, *rstate;
317462306a36Sopenharmony_ci	struct compressor *xcomp, *rcomp;
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_ci	ppp_lock(ppp);
317762306a36Sopenharmony_ci	ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP);
317862306a36Sopenharmony_ci	ppp->xstate = 0;
317962306a36Sopenharmony_ci	xcomp = ppp->xcomp;
318062306a36Sopenharmony_ci	xstate = ppp->xc_state;
318162306a36Sopenharmony_ci	ppp->xc_state = NULL;
318262306a36Sopenharmony_ci	ppp->rstate = 0;
318362306a36Sopenharmony_ci	rcomp = ppp->rcomp;
318462306a36Sopenharmony_ci	rstate = ppp->rc_state;
318562306a36Sopenharmony_ci	ppp->rc_state = NULL;
318662306a36Sopenharmony_ci	ppp_unlock(ppp);
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_ci	if (xstate) {
318962306a36Sopenharmony_ci		xcomp->comp_free(xstate);
319062306a36Sopenharmony_ci		module_put(xcomp->owner);
319162306a36Sopenharmony_ci	}
319262306a36Sopenharmony_ci	if (rstate) {
319362306a36Sopenharmony_ci		rcomp->decomp_free(rstate);
319462306a36Sopenharmony_ci		module_put(rcomp->owner);
319562306a36Sopenharmony_ci	}
319662306a36Sopenharmony_ci}
319762306a36Sopenharmony_ci
319862306a36Sopenharmony_ci/* List of compressors. */
319962306a36Sopenharmony_cistatic LIST_HEAD(compressor_list);
320062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(compressor_list_lock);
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_cistruct compressor_entry {
320362306a36Sopenharmony_ci	struct list_head list;
320462306a36Sopenharmony_ci	struct compressor *comp;
320562306a36Sopenharmony_ci};
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_cistatic struct compressor_entry *
320862306a36Sopenharmony_cifind_comp_entry(int proto)
320962306a36Sopenharmony_ci{
321062306a36Sopenharmony_ci	struct compressor_entry *ce;
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	list_for_each_entry(ce, &compressor_list, list) {
321362306a36Sopenharmony_ci		if (ce->comp->compress_proto == proto)
321462306a36Sopenharmony_ci			return ce;
321562306a36Sopenharmony_ci	}
321662306a36Sopenharmony_ci	return NULL;
321762306a36Sopenharmony_ci}
321862306a36Sopenharmony_ci
321962306a36Sopenharmony_ci/* Register a compressor */
322062306a36Sopenharmony_ciint
322162306a36Sopenharmony_cippp_register_compressor(struct compressor *cp)
322262306a36Sopenharmony_ci{
322362306a36Sopenharmony_ci	struct compressor_entry *ce;
322462306a36Sopenharmony_ci	int ret;
322562306a36Sopenharmony_ci	spin_lock(&compressor_list_lock);
322662306a36Sopenharmony_ci	ret = -EEXIST;
322762306a36Sopenharmony_ci	if (find_comp_entry(cp->compress_proto))
322862306a36Sopenharmony_ci		goto out;
322962306a36Sopenharmony_ci	ret = -ENOMEM;
323062306a36Sopenharmony_ci	ce = kmalloc(sizeof(struct compressor_entry), GFP_ATOMIC);
323162306a36Sopenharmony_ci	if (!ce)
323262306a36Sopenharmony_ci		goto out;
323362306a36Sopenharmony_ci	ret = 0;
323462306a36Sopenharmony_ci	ce->comp = cp;
323562306a36Sopenharmony_ci	list_add(&ce->list, &compressor_list);
323662306a36Sopenharmony_ci out:
323762306a36Sopenharmony_ci	spin_unlock(&compressor_list_lock);
323862306a36Sopenharmony_ci	return ret;
323962306a36Sopenharmony_ci}
324062306a36Sopenharmony_ci
324162306a36Sopenharmony_ci/* Unregister a compressor */
324262306a36Sopenharmony_civoid
324362306a36Sopenharmony_cippp_unregister_compressor(struct compressor *cp)
324462306a36Sopenharmony_ci{
324562306a36Sopenharmony_ci	struct compressor_entry *ce;
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci	spin_lock(&compressor_list_lock);
324862306a36Sopenharmony_ci	ce = find_comp_entry(cp->compress_proto);
324962306a36Sopenharmony_ci	if (ce && ce->comp == cp) {
325062306a36Sopenharmony_ci		list_del(&ce->list);
325162306a36Sopenharmony_ci		kfree(ce);
325262306a36Sopenharmony_ci	}
325362306a36Sopenharmony_ci	spin_unlock(&compressor_list_lock);
325462306a36Sopenharmony_ci}
325562306a36Sopenharmony_ci
325662306a36Sopenharmony_ci/* Find a compressor. */
325762306a36Sopenharmony_cistatic struct compressor *
325862306a36Sopenharmony_cifind_compressor(int type)
325962306a36Sopenharmony_ci{
326062306a36Sopenharmony_ci	struct compressor_entry *ce;
326162306a36Sopenharmony_ci	struct compressor *cp = NULL;
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_ci	spin_lock(&compressor_list_lock);
326462306a36Sopenharmony_ci	ce = find_comp_entry(type);
326562306a36Sopenharmony_ci	if (ce) {
326662306a36Sopenharmony_ci		cp = ce->comp;
326762306a36Sopenharmony_ci		if (!try_module_get(cp->owner))
326862306a36Sopenharmony_ci			cp = NULL;
326962306a36Sopenharmony_ci	}
327062306a36Sopenharmony_ci	spin_unlock(&compressor_list_lock);
327162306a36Sopenharmony_ci	return cp;
327262306a36Sopenharmony_ci}
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_ci/*
327562306a36Sopenharmony_ci * Miscelleneous stuff.
327662306a36Sopenharmony_ci */
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_cistatic void
327962306a36Sopenharmony_cippp_get_stats(struct ppp *ppp, struct ppp_stats *st)
328062306a36Sopenharmony_ci{
328162306a36Sopenharmony_ci	struct slcompress *vj = ppp->vj;
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci	memset(st, 0, sizeof(*st));
328462306a36Sopenharmony_ci	st->p.ppp_ipackets = ppp->stats64.rx_packets;
328562306a36Sopenharmony_ci	st->p.ppp_ierrors = ppp->dev->stats.rx_errors;
328662306a36Sopenharmony_ci	st->p.ppp_ibytes = ppp->stats64.rx_bytes;
328762306a36Sopenharmony_ci	st->p.ppp_opackets = ppp->stats64.tx_packets;
328862306a36Sopenharmony_ci	st->p.ppp_oerrors = ppp->dev->stats.tx_errors;
328962306a36Sopenharmony_ci	st->p.ppp_obytes = ppp->stats64.tx_bytes;
329062306a36Sopenharmony_ci	if (!vj)
329162306a36Sopenharmony_ci		return;
329262306a36Sopenharmony_ci	st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed;
329362306a36Sopenharmony_ci	st->vj.vjs_compressed = vj->sls_o_compressed;
329462306a36Sopenharmony_ci	st->vj.vjs_searches = vj->sls_o_searches;
329562306a36Sopenharmony_ci	st->vj.vjs_misses = vj->sls_o_misses;
329662306a36Sopenharmony_ci	st->vj.vjs_errorin = vj->sls_i_error;
329762306a36Sopenharmony_ci	st->vj.vjs_tossed = vj->sls_i_tossed;
329862306a36Sopenharmony_ci	st->vj.vjs_uncompressedin = vj->sls_i_uncompressed;
329962306a36Sopenharmony_ci	st->vj.vjs_compressedin = vj->sls_i_compressed;
330062306a36Sopenharmony_ci}
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci/*
330362306a36Sopenharmony_ci * Stuff for handling the lists of ppp units and channels
330462306a36Sopenharmony_ci * and for initialization.
330562306a36Sopenharmony_ci */
330662306a36Sopenharmony_ci
330762306a36Sopenharmony_ci/*
330862306a36Sopenharmony_ci * Create a new ppp interface unit.  Fails if it can't allocate memory
330962306a36Sopenharmony_ci * or if there is already a unit with the requested number.
331062306a36Sopenharmony_ci * unit == -1 means allocate a new number.
331162306a36Sopenharmony_ci */
331262306a36Sopenharmony_cistatic int ppp_create_interface(struct net *net, struct file *file, int *unit)
331362306a36Sopenharmony_ci{
331462306a36Sopenharmony_ci	struct ppp_config conf = {
331562306a36Sopenharmony_ci		.file = file,
331662306a36Sopenharmony_ci		.unit = *unit,
331762306a36Sopenharmony_ci		.ifname_is_set = false,
331862306a36Sopenharmony_ci	};
331962306a36Sopenharmony_ci	struct net_device *dev;
332062306a36Sopenharmony_ci	struct ppp *ppp;
332162306a36Sopenharmony_ci	int err;
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	dev = alloc_netdev(sizeof(struct ppp), "", NET_NAME_ENUM, ppp_setup);
332462306a36Sopenharmony_ci	if (!dev) {
332562306a36Sopenharmony_ci		err = -ENOMEM;
332662306a36Sopenharmony_ci		goto err;
332762306a36Sopenharmony_ci	}
332862306a36Sopenharmony_ci	dev_net_set(dev, net);
332962306a36Sopenharmony_ci	dev->rtnl_link_ops = &ppp_link_ops;
333062306a36Sopenharmony_ci
333162306a36Sopenharmony_ci	rtnl_lock();
333262306a36Sopenharmony_ci
333362306a36Sopenharmony_ci	err = ppp_dev_configure(net, dev, &conf);
333462306a36Sopenharmony_ci	if (err < 0)
333562306a36Sopenharmony_ci		goto err_dev;
333662306a36Sopenharmony_ci	ppp = netdev_priv(dev);
333762306a36Sopenharmony_ci	*unit = ppp->file.index;
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci	rtnl_unlock();
334062306a36Sopenharmony_ci
334162306a36Sopenharmony_ci	return 0;
334262306a36Sopenharmony_ci
334362306a36Sopenharmony_cierr_dev:
334462306a36Sopenharmony_ci	rtnl_unlock();
334562306a36Sopenharmony_ci	free_netdev(dev);
334662306a36Sopenharmony_cierr:
334762306a36Sopenharmony_ci	return err;
334862306a36Sopenharmony_ci}
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci/*
335162306a36Sopenharmony_ci * Initialize a ppp_file structure.
335262306a36Sopenharmony_ci */
335362306a36Sopenharmony_cistatic void
335462306a36Sopenharmony_ciinit_ppp_file(struct ppp_file *pf, int kind)
335562306a36Sopenharmony_ci{
335662306a36Sopenharmony_ci	pf->kind = kind;
335762306a36Sopenharmony_ci	skb_queue_head_init(&pf->xq);
335862306a36Sopenharmony_ci	skb_queue_head_init(&pf->rq);
335962306a36Sopenharmony_ci	refcount_set(&pf->refcnt, 1);
336062306a36Sopenharmony_ci	init_waitqueue_head(&pf->rwait);
336162306a36Sopenharmony_ci}
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_ci/*
336462306a36Sopenharmony_ci * Free the memory used by a ppp unit.  This is only called once
336562306a36Sopenharmony_ci * there are no channels connected to the unit and no file structs
336662306a36Sopenharmony_ci * that reference the unit.
336762306a36Sopenharmony_ci */
336862306a36Sopenharmony_cistatic void ppp_destroy_interface(struct ppp *ppp)
336962306a36Sopenharmony_ci{
337062306a36Sopenharmony_ci	atomic_dec(&ppp_unit_count);
337162306a36Sopenharmony_ci
337262306a36Sopenharmony_ci	if (!ppp->file.dead || ppp->n_channels) {
337362306a36Sopenharmony_ci		/* "can't happen" */
337462306a36Sopenharmony_ci		netdev_err(ppp->dev, "ppp: destroying ppp struct %p "
337562306a36Sopenharmony_ci			   "but dead=%d n_channels=%d !\n",
337662306a36Sopenharmony_ci			   ppp, ppp->file.dead, ppp->n_channels);
337762306a36Sopenharmony_ci		return;
337862306a36Sopenharmony_ci	}
337962306a36Sopenharmony_ci
338062306a36Sopenharmony_ci	ppp_ccp_closed(ppp);
338162306a36Sopenharmony_ci	if (ppp->vj) {
338262306a36Sopenharmony_ci		slhc_free(ppp->vj);
338362306a36Sopenharmony_ci		ppp->vj = NULL;
338462306a36Sopenharmony_ci	}
338562306a36Sopenharmony_ci	skb_queue_purge(&ppp->file.xq);
338662306a36Sopenharmony_ci	skb_queue_purge(&ppp->file.rq);
338762306a36Sopenharmony_ci#ifdef CONFIG_PPP_MULTILINK
338862306a36Sopenharmony_ci	skb_queue_purge(&ppp->mrq);
338962306a36Sopenharmony_ci#endif /* CONFIG_PPP_MULTILINK */
339062306a36Sopenharmony_ci#ifdef CONFIG_PPP_FILTER
339162306a36Sopenharmony_ci	if (ppp->pass_filter) {
339262306a36Sopenharmony_ci		bpf_prog_destroy(ppp->pass_filter);
339362306a36Sopenharmony_ci		ppp->pass_filter = NULL;
339462306a36Sopenharmony_ci	}
339562306a36Sopenharmony_ci
339662306a36Sopenharmony_ci	if (ppp->active_filter) {
339762306a36Sopenharmony_ci		bpf_prog_destroy(ppp->active_filter);
339862306a36Sopenharmony_ci		ppp->active_filter = NULL;
339962306a36Sopenharmony_ci	}
340062306a36Sopenharmony_ci#endif /* CONFIG_PPP_FILTER */
340162306a36Sopenharmony_ci
340262306a36Sopenharmony_ci	kfree_skb(ppp->xmit_pending);
340362306a36Sopenharmony_ci	free_percpu(ppp->xmit_recursion);
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	free_netdev(ppp->dev);
340662306a36Sopenharmony_ci}
340762306a36Sopenharmony_ci
340862306a36Sopenharmony_ci/*
340962306a36Sopenharmony_ci * Locate an existing ppp unit.
341062306a36Sopenharmony_ci * The caller should have locked the all_ppp_mutex.
341162306a36Sopenharmony_ci */
341262306a36Sopenharmony_cistatic struct ppp *
341362306a36Sopenharmony_cippp_find_unit(struct ppp_net *pn, int unit)
341462306a36Sopenharmony_ci{
341562306a36Sopenharmony_ci	return unit_find(&pn->units_idr, unit);
341662306a36Sopenharmony_ci}
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_ci/*
341962306a36Sopenharmony_ci * Locate an existing ppp channel.
342062306a36Sopenharmony_ci * The caller should have locked the all_channels_lock.
342162306a36Sopenharmony_ci * First we look in the new_channels list, then in the
342262306a36Sopenharmony_ci * all_channels list.  If found in the new_channels list,
342362306a36Sopenharmony_ci * we move it to the all_channels list.  This is for speed
342462306a36Sopenharmony_ci * when we have a lot of channels in use.
342562306a36Sopenharmony_ci */
342662306a36Sopenharmony_cistatic struct channel *
342762306a36Sopenharmony_cippp_find_channel(struct ppp_net *pn, int unit)
342862306a36Sopenharmony_ci{
342962306a36Sopenharmony_ci	struct channel *pch;
343062306a36Sopenharmony_ci
343162306a36Sopenharmony_ci	list_for_each_entry(pch, &pn->new_channels, list) {
343262306a36Sopenharmony_ci		if (pch->file.index == unit) {
343362306a36Sopenharmony_ci			list_move(&pch->list, &pn->all_channels);
343462306a36Sopenharmony_ci			return pch;
343562306a36Sopenharmony_ci		}
343662306a36Sopenharmony_ci	}
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_ci	list_for_each_entry(pch, &pn->all_channels, list) {
343962306a36Sopenharmony_ci		if (pch->file.index == unit)
344062306a36Sopenharmony_ci			return pch;
344162306a36Sopenharmony_ci	}
344262306a36Sopenharmony_ci
344362306a36Sopenharmony_ci	return NULL;
344462306a36Sopenharmony_ci}
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_ci/*
344762306a36Sopenharmony_ci * Connect a PPP channel to a PPP interface unit.
344862306a36Sopenharmony_ci */
344962306a36Sopenharmony_cistatic int
345062306a36Sopenharmony_cippp_connect_channel(struct channel *pch, int unit)
345162306a36Sopenharmony_ci{
345262306a36Sopenharmony_ci	struct ppp *ppp;
345362306a36Sopenharmony_ci	struct ppp_net *pn;
345462306a36Sopenharmony_ci	int ret = -ENXIO;
345562306a36Sopenharmony_ci	int hdrlen;
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_ci	pn = ppp_pernet(pch->chan_net);
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ci	mutex_lock(&pn->all_ppp_mutex);
346062306a36Sopenharmony_ci	ppp = ppp_find_unit(pn, unit);
346162306a36Sopenharmony_ci	if (!ppp)
346262306a36Sopenharmony_ci		goto out;
346362306a36Sopenharmony_ci	write_lock_bh(&pch->upl);
346462306a36Sopenharmony_ci	ret = -EINVAL;
346562306a36Sopenharmony_ci	if (pch->ppp ||
346662306a36Sopenharmony_ci	    rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl)))
346762306a36Sopenharmony_ci		goto outl;
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_ci	ppp_lock(ppp);
347062306a36Sopenharmony_ci	spin_lock_bh(&pch->downl);
347162306a36Sopenharmony_ci	if (!pch->chan) {
347262306a36Sopenharmony_ci		/* Don't connect unregistered channels */
347362306a36Sopenharmony_ci		spin_unlock_bh(&pch->downl);
347462306a36Sopenharmony_ci		ppp_unlock(ppp);
347562306a36Sopenharmony_ci		ret = -ENOTCONN;
347662306a36Sopenharmony_ci		goto outl;
347762306a36Sopenharmony_ci	}
347862306a36Sopenharmony_ci	spin_unlock_bh(&pch->downl);
347962306a36Sopenharmony_ci	if (pch->file.hdrlen > ppp->file.hdrlen)
348062306a36Sopenharmony_ci		ppp->file.hdrlen = pch->file.hdrlen;
348162306a36Sopenharmony_ci	hdrlen = pch->file.hdrlen + 2;	/* for protocol bytes */
348262306a36Sopenharmony_ci	if (hdrlen > ppp->dev->hard_header_len)
348362306a36Sopenharmony_ci		ppp->dev->hard_header_len = hdrlen;
348462306a36Sopenharmony_ci	list_add_tail(&pch->clist, &ppp->channels);
348562306a36Sopenharmony_ci	++ppp->n_channels;
348662306a36Sopenharmony_ci	pch->ppp = ppp;
348762306a36Sopenharmony_ci	refcount_inc(&ppp->file.refcnt);
348862306a36Sopenharmony_ci	ppp_unlock(ppp);
348962306a36Sopenharmony_ci	ret = 0;
349062306a36Sopenharmony_ci
349162306a36Sopenharmony_ci outl:
349262306a36Sopenharmony_ci	write_unlock_bh(&pch->upl);
349362306a36Sopenharmony_ci out:
349462306a36Sopenharmony_ci	mutex_unlock(&pn->all_ppp_mutex);
349562306a36Sopenharmony_ci	return ret;
349662306a36Sopenharmony_ci}
349762306a36Sopenharmony_ci
349862306a36Sopenharmony_ci/*
349962306a36Sopenharmony_ci * Disconnect a channel from its ppp unit.
350062306a36Sopenharmony_ci */
350162306a36Sopenharmony_cistatic int
350262306a36Sopenharmony_cippp_disconnect_channel(struct channel *pch)
350362306a36Sopenharmony_ci{
350462306a36Sopenharmony_ci	struct ppp *ppp;
350562306a36Sopenharmony_ci	int err = -EINVAL;
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_ci	write_lock_bh(&pch->upl);
350862306a36Sopenharmony_ci	ppp = pch->ppp;
350962306a36Sopenharmony_ci	pch->ppp = NULL;
351062306a36Sopenharmony_ci	write_unlock_bh(&pch->upl);
351162306a36Sopenharmony_ci	if (ppp) {
351262306a36Sopenharmony_ci		/* remove it from the ppp unit's list */
351362306a36Sopenharmony_ci		ppp_lock(ppp);
351462306a36Sopenharmony_ci		list_del(&pch->clist);
351562306a36Sopenharmony_ci		if (--ppp->n_channels == 0)
351662306a36Sopenharmony_ci			wake_up_interruptible(&ppp->file.rwait);
351762306a36Sopenharmony_ci		ppp_unlock(ppp);
351862306a36Sopenharmony_ci		if (refcount_dec_and_test(&ppp->file.refcnt))
351962306a36Sopenharmony_ci			ppp_destroy_interface(ppp);
352062306a36Sopenharmony_ci		err = 0;
352162306a36Sopenharmony_ci	}
352262306a36Sopenharmony_ci	return err;
352362306a36Sopenharmony_ci}
352462306a36Sopenharmony_ci
352562306a36Sopenharmony_ci/*
352662306a36Sopenharmony_ci * Free up the resources used by a ppp channel.
352762306a36Sopenharmony_ci */
352862306a36Sopenharmony_cistatic void ppp_destroy_channel(struct channel *pch)
352962306a36Sopenharmony_ci{
353062306a36Sopenharmony_ci	put_net_track(pch->chan_net, &pch->ns_tracker);
353162306a36Sopenharmony_ci	pch->chan_net = NULL;
353262306a36Sopenharmony_ci
353362306a36Sopenharmony_ci	atomic_dec(&channel_count);
353462306a36Sopenharmony_ci
353562306a36Sopenharmony_ci	if (!pch->file.dead) {
353662306a36Sopenharmony_ci		/* "can't happen" */
353762306a36Sopenharmony_ci		pr_err("ppp: destroying undead channel %p !\n", pch);
353862306a36Sopenharmony_ci		return;
353962306a36Sopenharmony_ci	}
354062306a36Sopenharmony_ci	skb_queue_purge(&pch->file.xq);
354162306a36Sopenharmony_ci	skb_queue_purge(&pch->file.rq);
354262306a36Sopenharmony_ci	kfree(pch);
354362306a36Sopenharmony_ci}
354462306a36Sopenharmony_ci
354562306a36Sopenharmony_cistatic void __exit ppp_cleanup(void)
354662306a36Sopenharmony_ci{
354762306a36Sopenharmony_ci	/* should never happen */
354862306a36Sopenharmony_ci	if (atomic_read(&ppp_unit_count) || atomic_read(&channel_count))
354962306a36Sopenharmony_ci		pr_err("PPP: removing module but units remain!\n");
355062306a36Sopenharmony_ci	rtnl_link_unregister(&ppp_link_ops);
355162306a36Sopenharmony_ci	unregister_chrdev(PPP_MAJOR, "ppp");
355262306a36Sopenharmony_ci	device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0));
355362306a36Sopenharmony_ci	class_destroy(ppp_class);
355462306a36Sopenharmony_ci	unregister_pernet_device(&ppp_net_ops);
355562306a36Sopenharmony_ci}
355662306a36Sopenharmony_ci
355762306a36Sopenharmony_ci/*
355862306a36Sopenharmony_ci * Units handling. Caller must protect concurrent access
355962306a36Sopenharmony_ci * by holding all_ppp_mutex
356062306a36Sopenharmony_ci */
356162306a36Sopenharmony_ci
356262306a36Sopenharmony_ci/* associate pointer with specified number */
356362306a36Sopenharmony_cistatic int unit_set(struct idr *p, void *ptr, int n)
356462306a36Sopenharmony_ci{
356562306a36Sopenharmony_ci	int unit;
356662306a36Sopenharmony_ci
356762306a36Sopenharmony_ci	unit = idr_alloc(p, ptr, n, n + 1, GFP_KERNEL);
356862306a36Sopenharmony_ci	if (unit == -ENOSPC)
356962306a36Sopenharmony_ci		unit = -EINVAL;
357062306a36Sopenharmony_ci	return unit;
357162306a36Sopenharmony_ci}
357262306a36Sopenharmony_ci
357362306a36Sopenharmony_ci/* get new free unit number and associate pointer with it */
357462306a36Sopenharmony_cistatic int unit_get(struct idr *p, void *ptr, int min)
357562306a36Sopenharmony_ci{
357662306a36Sopenharmony_ci	return idr_alloc(p, ptr, min, 0, GFP_KERNEL);
357762306a36Sopenharmony_ci}
357862306a36Sopenharmony_ci
357962306a36Sopenharmony_ci/* put unit number back to a pool */
358062306a36Sopenharmony_cistatic void unit_put(struct idr *p, int n)
358162306a36Sopenharmony_ci{
358262306a36Sopenharmony_ci	idr_remove(p, n);
358362306a36Sopenharmony_ci}
358462306a36Sopenharmony_ci
358562306a36Sopenharmony_ci/* get pointer associated with the number */
358662306a36Sopenharmony_cistatic void *unit_find(struct idr *p, int n)
358762306a36Sopenharmony_ci{
358862306a36Sopenharmony_ci	return idr_find(p, n);
358962306a36Sopenharmony_ci}
359062306a36Sopenharmony_ci
359162306a36Sopenharmony_ci/* Module/initialization stuff */
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_cimodule_init(ppp_init);
359462306a36Sopenharmony_cimodule_exit(ppp_cleanup);
359562306a36Sopenharmony_ci
359662306a36Sopenharmony_ciEXPORT_SYMBOL(ppp_register_net_channel);
359762306a36Sopenharmony_ciEXPORT_SYMBOL(ppp_register_channel);
359862306a36Sopenharmony_ciEXPORT_SYMBOL(ppp_unregister_channel);
359962306a36Sopenharmony_ciEXPORT_SYMBOL(ppp_channel_index);
360062306a36Sopenharmony_ciEXPORT_SYMBOL(ppp_unit_number);
360162306a36Sopenharmony_ciEXPORT_SYMBOL(ppp_dev_name);
360262306a36Sopenharmony_ciEXPORT_SYMBOL(ppp_input);
360362306a36Sopenharmony_ciEXPORT_SYMBOL(ppp_input_error);
360462306a36Sopenharmony_ciEXPORT_SYMBOL(ppp_output_wakeup);
360562306a36Sopenharmony_ciEXPORT_SYMBOL(ppp_register_compressor);
360662306a36Sopenharmony_ciEXPORT_SYMBOL(ppp_unregister_compressor);
360762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
360862306a36Sopenharmony_ciMODULE_ALIAS_CHARDEV(PPP_MAJOR, 0);
360962306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("ppp");
361062306a36Sopenharmony_ciMODULE_ALIAS("devname:ppp");
3611