xref: /kernel/linux/linux-6.6/drivers/net/ppp/pptp.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Point-to-Point Tunneling Protocol for Linux
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Authors: Dmitry Kozlov <xeb@mail.ru>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/string.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/errno.h>
1362306a36Sopenharmony_ci#include <linux/netdevice.h>
1462306a36Sopenharmony_ci#include <linux/net.h>
1562306a36Sopenharmony_ci#include <linux/skbuff.h>
1662306a36Sopenharmony_ci#include <linux/vmalloc.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/ppp_channel.h>
1962306a36Sopenharmony_ci#include <linux/ppp_defs.h>
2062306a36Sopenharmony_ci#include <linux/if_pppox.h>
2162306a36Sopenharmony_ci#include <linux/ppp-ioctl.h>
2262306a36Sopenharmony_ci#include <linux/notifier.h>
2362306a36Sopenharmony_ci#include <linux/file.h>
2462306a36Sopenharmony_ci#include <linux/in.h>
2562306a36Sopenharmony_ci#include <linux/ip.h>
2662306a36Sopenharmony_ci#include <linux/rcupdate.h>
2762306a36Sopenharmony_ci#include <linux/security.h>
2862306a36Sopenharmony_ci#include <linux/spinlock.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <net/sock.h>
3162306a36Sopenharmony_ci#include <net/protocol.h>
3262306a36Sopenharmony_ci#include <net/ip.h>
3362306a36Sopenharmony_ci#include <net/icmp.h>
3462306a36Sopenharmony_ci#include <net/route.h>
3562306a36Sopenharmony_ci#include <net/gre.h>
3662306a36Sopenharmony_ci#include <net/pptp.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <linux/uaccess.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define PPTP_DRIVER_VERSION "0.8.5"
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define MAX_CALLID 65535
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic DECLARE_BITMAP(callid_bitmap, MAX_CALLID + 1);
4562306a36Sopenharmony_cistatic struct pppox_sock __rcu **callid_sock;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(chan_lock);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic struct proto pptp_sk_proto __read_mostly;
5062306a36Sopenharmony_cistatic const struct ppp_channel_ops pptp_chan_ops;
5162306a36Sopenharmony_cistatic const struct proto_ops pptp_ops;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct pppox_sock *sock;
5662306a36Sopenharmony_ci	struct pptp_opt *opt;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	rcu_read_lock();
5962306a36Sopenharmony_ci	sock = rcu_dereference(callid_sock[call_id]);
6062306a36Sopenharmony_ci	if (sock) {
6162306a36Sopenharmony_ci		opt = &sock->proto.pptp;
6262306a36Sopenharmony_ci		if (opt->dst_addr.sin_addr.s_addr != s_addr)
6362306a36Sopenharmony_ci			sock = NULL;
6462306a36Sopenharmony_ci		else
6562306a36Sopenharmony_ci			sock_hold(sk_pppox(sock));
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci	rcu_read_unlock();
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return sock;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int lookup_chan_dst(u16 call_id, __be32 d_addr)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct pppox_sock *sock;
7562306a36Sopenharmony_ci	struct pptp_opt *opt;
7662306a36Sopenharmony_ci	int i;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	rcu_read_lock();
7962306a36Sopenharmony_ci	i = 1;
8062306a36Sopenharmony_ci	for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) {
8162306a36Sopenharmony_ci		sock = rcu_dereference(callid_sock[i]);
8262306a36Sopenharmony_ci		if (!sock)
8362306a36Sopenharmony_ci			continue;
8462306a36Sopenharmony_ci		opt = &sock->proto.pptp;
8562306a36Sopenharmony_ci		if (opt->dst_addr.call_id == call_id &&
8662306a36Sopenharmony_ci			  opt->dst_addr.sin_addr.s_addr == d_addr)
8762306a36Sopenharmony_ci			break;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	rcu_read_unlock();
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return i < MAX_CALLID;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int add_chan(struct pppox_sock *sock,
9562306a36Sopenharmony_ci		    struct pptp_addr *sa)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	static int call_id;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	spin_lock(&chan_lock);
10062306a36Sopenharmony_ci	if (!sa->call_id)	{
10162306a36Sopenharmony_ci		call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, call_id + 1);
10262306a36Sopenharmony_ci		if (call_id == MAX_CALLID) {
10362306a36Sopenharmony_ci			call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, 1);
10462306a36Sopenharmony_ci			if (call_id == MAX_CALLID)
10562306a36Sopenharmony_ci				goto out_err;
10662306a36Sopenharmony_ci		}
10762306a36Sopenharmony_ci		sa->call_id = call_id;
10862306a36Sopenharmony_ci	} else if (test_bit(sa->call_id, callid_bitmap)) {
10962306a36Sopenharmony_ci		goto out_err;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	sock->proto.pptp.src_addr = *sa;
11362306a36Sopenharmony_ci	set_bit(sa->call_id, callid_bitmap);
11462306a36Sopenharmony_ci	rcu_assign_pointer(callid_sock[sa->call_id], sock);
11562306a36Sopenharmony_ci	spin_unlock(&chan_lock);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return 0;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ciout_err:
12062306a36Sopenharmony_ci	spin_unlock(&chan_lock);
12162306a36Sopenharmony_ci	return -1;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void del_chan(struct pppox_sock *sock)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	spin_lock(&chan_lock);
12762306a36Sopenharmony_ci	clear_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap);
12862306a36Sopenharmony_ci	RCU_INIT_POINTER(callid_sock[sock->proto.pptp.src_addr.call_id], NULL);
12962306a36Sopenharmony_ci	spin_unlock(&chan_lock);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic struct rtable *pptp_route_output(const struct pppox_sock *po,
13362306a36Sopenharmony_ci					struct flowi4 *fl4)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	const struct sock *sk = &po->sk;
13662306a36Sopenharmony_ci	struct net *net;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	net = sock_net(sk);
13962306a36Sopenharmony_ci	flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, 0,
14062306a36Sopenharmony_ci			   RT_SCOPE_UNIVERSE, IPPROTO_GRE, 0,
14162306a36Sopenharmony_ci			   po->proto.pptp.dst_addr.sin_addr.s_addr,
14262306a36Sopenharmony_ci			   po->proto.pptp.src_addr.sin_addr.s_addr,
14362306a36Sopenharmony_ci			   0, 0, sock_net_uid(net, sk));
14462306a36Sopenharmony_ci	security_sk_classify_flow(sk, flowi4_to_flowi_common(fl4));
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return ip_route_output_flow(net, fl4, sk);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct sock *sk = chan->private;
15262306a36Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
15362306a36Sopenharmony_ci	struct net *net = sock_net(sk);
15462306a36Sopenharmony_ci	struct pptp_opt *opt = &po->proto.pptp;
15562306a36Sopenharmony_ci	struct pptp_gre_header *hdr;
15662306a36Sopenharmony_ci	unsigned int header_len = sizeof(*hdr);
15762306a36Sopenharmony_ci	struct flowi4 fl4;
15862306a36Sopenharmony_ci	int islcp;
15962306a36Sopenharmony_ci	int len;
16062306a36Sopenharmony_ci	unsigned char *data;
16162306a36Sopenharmony_ci	__u32 seq_recv;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	struct rtable *rt;
16562306a36Sopenharmony_ci	struct net_device *tdev;
16662306a36Sopenharmony_ci	struct iphdr  *iph;
16762306a36Sopenharmony_ci	int    max_headroom;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (sk_pppox(po)->sk_state & PPPOX_DEAD)
17062306a36Sopenharmony_ci		goto tx_error;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	rt = pptp_route_output(po, &fl4);
17362306a36Sopenharmony_ci	if (IS_ERR(rt))
17462306a36Sopenharmony_ci		goto tx_error;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	tdev = rt->dst.dev;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(*iph) + sizeof(*hdr) + 2;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) {
18162306a36Sopenharmony_ci		struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
18262306a36Sopenharmony_ci		if (!new_skb) {
18362306a36Sopenharmony_ci			ip_rt_put(rt);
18462306a36Sopenharmony_ci			goto tx_error;
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci		if (skb->sk)
18762306a36Sopenharmony_ci			skb_set_owner_w(new_skb, skb->sk);
18862306a36Sopenharmony_ci		consume_skb(skb);
18962306a36Sopenharmony_ci		skb = new_skb;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	data = skb->data;
19362306a36Sopenharmony_ci	islcp = ((data[0] << 8) + data[1]) == PPP_LCP && 1 <= data[2] && data[2] <= 7;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* compress protocol field */
19662306a36Sopenharmony_ci	if ((opt->ppp_flags & SC_COMP_PROT) && data[0] == 0 && !islcp)
19762306a36Sopenharmony_ci		skb_pull(skb, 1);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* Put in the address/control bytes if necessary */
20062306a36Sopenharmony_ci	if ((opt->ppp_flags & SC_COMP_AC) == 0 || islcp) {
20162306a36Sopenharmony_ci		data = skb_push(skb, 2);
20262306a36Sopenharmony_ci		data[0] = PPP_ALLSTATIONS;
20362306a36Sopenharmony_ci		data[1] = PPP_UI;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	len = skb->len;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	seq_recv = opt->seq_recv;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (opt->ack_sent == seq_recv)
21162306a36Sopenharmony_ci		header_len -= sizeof(hdr->ack);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* Push down and install GRE header */
21462306a36Sopenharmony_ci	skb_push(skb, header_len);
21562306a36Sopenharmony_ci	hdr = (struct pptp_gre_header *)(skb->data);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	hdr->gre_hd.flags = GRE_KEY | GRE_VERSION_1 | GRE_SEQ;
21862306a36Sopenharmony_ci	hdr->gre_hd.protocol = GRE_PROTO_PPP;
21962306a36Sopenharmony_ci	hdr->call_id = htons(opt->dst_addr.call_id);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	hdr->seq = htonl(++opt->seq_sent);
22262306a36Sopenharmony_ci	if (opt->ack_sent != seq_recv)	{
22362306a36Sopenharmony_ci		/* send ack with this message */
22462306a36Sopenharmony_ci		hdr->gre_hd.flags |= GRE_ACK;
22562306a36Sopenharmony_ci		hdr->ack  = htonl(seq_recv);
22662306a36Sopenharmony_ci		opt->ack_sent = seq_recv;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci	hdr->payload_len = htons(len);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/*	Push down and install the IP header. */
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	skb_reset_transport_header(skb);
23362306a36Sopenharmony_ci	skb_push(skb, sizeof(*iph));
23462306a36Sopenharmony_ci	skb_reset_network_header(skb);
23562306a36Sopenharmony_ci	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
23662306a36Sopenharmony_ci	IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	iph =	ip_hdr(skb);
23962306a36Sopenharmony_ci	iph->version =	4;
24062306a36Sopenharmony_ci	iph->ihl =	sizeof(struct iphdr) >> 2;
24162306a36Sopenharmony_ci	if (ip_dont_fragment(sk, &rt->dst))
24262306a36Sopenharmony_ci		iph->frag_off	=	htons(IP_DF);
24362306a36Sopenharmony_ci	else
24462306a36Sopenharmony_ci		iph->frag_off	=	0;
24562306a36Sopenharmony_ci	iph->protocol = IPPROTO_GRE;
24662306a36Sopenharmony_ci	iph->tos      = 0;
24762306a36Sopenharmony_ci	iph->daddr    = fl4.daddr;
24862306a36Sopenharmony_ci	iph->saddr    = fl4.saddr;
24962306a36Sopenharmony_ci	iph->ttl      = ip4_dst_hoplimit(&rt->dst);
25062306a36Sopenharmony_ci	iph->tot_len  = htons(skb->len);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	skb_dst_drop(skb);
25362306a36Sopenharmony_ci	skb_dst_set(skb, &rt->dst);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	nf_reset_ct(skb);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	skb->ip_summed = CHECKSUM_NONE;
25862306a36Sopenharmony_ci	ip_select_ident(net, skb, NULL);
25962306a36Sopenharmony_ci	ip_send_check(iph);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	ip_local_out(net, skb->sk, skb);
26262306a36Sopenharmony_ci	return 1;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_citx_error:
26562306a36Sopenharmony_ci	kfree_skb(skb);
26662306a36Sopenharmony_ci	return 1;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic int pptp_rcv_core(struct sock *sk, struct sk_buff *skb)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
27262306a36Sopenharmony_ci	struct pptp_opt *opt = &po->proto.pptp;
27362306a36Sopenharmony_ci	int headersize, payload_len, seq;
27462306a36Sopenharmony_ci	__u8 *payload;
27562306a36Sopenharmony_ci	struct pptp_gre_header *header;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (!(sk->sk_state & PPPOX_CONNECTED)) {
27862306a36Sopenharmony_ci		if (sock_queue_rcv_skb(sk, skb))
27962306a36Sopenharmony_ci			goto drop;
28062306a36Sopenharmony_ci		return NET_RX_SUCCESS;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	header = (struct pptp_gre_header *)(skb->data);
28462306a36Sopenharmony_ci	headersize  = sizeof(*header);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* test if acknowledgement present */
28762306a36Sopenharmony_ci	if (GRE_IS_ACK(header->gre_hd.flags)) {
28862306a36Sopenharmony_ci		__u32 ack;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		if (!pskb_may_pull(skb, headersize))
29162306a36Sopenharmony_ci			goto drop;
29262306a36Sopenharmony_ci		header = (struct pptp_gre_header *)(skb->data);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		/* ack in different place if S = 0 */
29562306a36Sopenharmony_ci		ack = GRE_IS_SEQ(header->gre_hd.flags) ? ntohl(header->ack) :
29662306a36Sopenharmony_ci							 ntohl(header->seq);
29762306a36Sopenharmony_ci		if (ack > opt->ack_recv)
29862306a36Sopenharmony_ci			opt->ack_recv = ack;
29962306a36Sopenharmony_ci		/* also handle sequence number wrap-around  */
30062306a36Sopenharmony_ci		if (WRAPPED(ack, opt->ack_recv))
30162306a36Sopenharmony_ci			opt->ack_recv = ack;
30262306a36Sopenharmony_ci	} else {
30362306a36Sopenharmony_ci		headersize -= sizeof(header->ack);
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci	/* test if payload present */
30662306a36Sopenharmony_ci	if (!GRE_IS_SEQ(header->gre_hd.flags))
30762306a36Sopenharmony_ci		goto drop;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	payload_len = ntohs(header->payload_len);
31062306a36Sopenharmony_ci	seq         = ntohl(header->seq);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/* check for incomplete packet (length smaller than expected) */
31362306a36Sopenharmony_ci	if (!pskb_may_pull(skb, headersize + payload_len))
31462306a36Sopenharmony_ci		goto drop;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	payload = skb->data + headersize;
31762306a36Sopenharmony_ci	/* check for expected sequence number */
31862306a36Sopenharmony_ci	if (seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq)) {
31962306a36Sopenharmony_ci		if ((payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) &&
32062306a36Sopenharmony_ci				(PPP_PROTOCOL(payload) == PPP_LCP) &&
32162306a36Sopenharmony_ci				((payload[4] == PPP_LCP_ECHOREQ) || (payload[4] == PPP_LCP_ECHOREP)))
32262306a36Sopenharmony_ci			goto allow_packet;
32362306a36Sopenharmony_ci	} else {
32462306a36Sopenharmony_ci		opt->seq_recv = seq;
32562306a36Sopenharmony_ciallow_packet:
32662306a36Sopenharmony_ci		skb_pull(skb, headersize);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI) {
32962306a36Sopenharmony_ci			/* chop off address/control */
33062306a36Sopenharmony_ci			if (skb->len < 3)
33162306a36Sopenharmony_ci				goto drop;
33262306a36Sopenharmony_ci			skb_pull(skb, 2);
33362306a36Sopenharmony_ci		}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_NONE;
33662306a36Sopenharmony_ci		skb_set_network_header(skb, skb->head-skb->data);
33762306a36Sopenharmony_ci		ppp_input(&po->chan, skb);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		return NET_RX_SUCCESS;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_cidrop:
34262306a36Sopenharmony_ci	kfree_skb(skb);
34362306a36Sopenharmony_ci	return NET_RX_DROP;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic int pptp_rcv(struct sk_buff *skb)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct pppox_sock *po;
34962306a36Sopenharmony_ci	struct pptp_gre_header *header;
35062306a36Sopenharmony_ci	struct iphdr *iph;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (skb->pkt_type != PACKET_HOST)
35362306a36Sopenharmony_ci		goto drop;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (!pskb_may_pull(skb, 12))
35662306a36Sopenharmony_ci		goto drop;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	iph = ip_hdr(skb);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	header = (struct pptp_gre_header *)skb->data;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (header->gre_hd.protocol != GRE_PROTO_PPP || /* PPTP-GRE protocol for PPTP */
36362306a36Sopenharmony_ci		GRE_IS_CSUM(header->gre_hd.flags) ||    /* flag CSUM should be clear */
36462306a36Sopenharmony_ci		GRE_IS_ROUTING(header->gre_hd.flags) || /* flag ROUTING should be clear */
36562306a36Sopenharmony_ci		!GRE_IS_KEY(header->gre_hd.flags) ||    /* flag KEY should be set */
36662306a36Sopenharmony_ci		(header->gre_hd.flags & GRE_FLAGS))     /* flag Recursion Ctrl should be clear */
36762306a36Sopenharmony_ci		/* if invalid, discard this packet */
36862306a36Sopenharmony_ci		goto drop;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	po = lookup_chan(ntohs(header->call_id), iph->saddr);
37162306a36Sopenharmony_ci	if (po) {
37262306a36Sopenharmony_ci		skb_dst_drop(skb);
37362306a36Sopenharmony_ci		nf_reset_ct(skb);
37462306a36Sopenharmony_ci		return sk_receive_skb(sk_pppox(po), skb, 0);
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_cidrop:
37762306a36Sopenharmony_ci	kfree_skb(skb);
37862306a36Sopenharmony_ci	return NET_RX_DROP;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic int pptp_bind(struct socket *sock, struct sockaddr *uservaddr,
38262306a36Sopenharmony_ci	int sockaddr_len)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct sock *sk = sock->sk;
38562306a36Sopenharmony_ci	struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
38662306a36Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
38762306a36Sopenharmony_ci	int error = 0;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (sockaddr_len < sizeof(struct sockaddr_pppox))
39062306a36Sopenharmony_ci		return -EINVAL;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	lock_sock(sk);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (sk->sk_state & PPPOX_DEAD) {
39562306a36Sopenharmony_ci		error = -EALREADY;
39662306a36Sopenharmony_ci		goto out;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (sk->sk_state & PPPOX_BOUND) {
40062306a36Sopenharmony_ci		error = -EBUSY;
40162306a36Sopenharmony_ci		goto out;
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (add_chan(po, &sp->sa_addr.pptp))
40562306a36Sopenharmony_ci		error = -EBUSY;
40662306a36Sopenharmony_ci	else
40762306a36Sopenharmony_ci		sk->sk_state |= PPPOX_BOUND;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ciout:
41062306a36Sopenharmony_ci	release_sock(sk);
41162306a36Sopenharmony_ci	return error;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic int pptp_connect(struct socket *sock, struct sockaddr *uservaddr,
41562306a36Sopenharmony_ci	int sockaddr_len, int flags)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct sock *sk = sock->sk;
41862306a36Sopenharmony_ci	struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
41962306a36Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
42062306a36Sopenharmony_ci	struct pptp_opt *opt = &po->proto.pptp;
42162306a36Sopenharmony_ci	struct rtable *rt;
42262306a36Sopenharmony_ci	struct flowi4 fl4;
42362306a36Sopenharmony_ci	int error = 0;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (sockaddr_len < sizeof(struct sockaddr_pppox))
42662306a36Sopenharmony_ci		return -EINVAL;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (sp->sa_protocol != PX_PROTO_PPTP)
42962306a36Sopenharmony_ci		return -EINVAL;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (lookup_chan_dst(sp->sa_addr.pptp.call_id, sp->sa_addr.pptp.sin_addr.s_addr))
43262306a36Sopenharmony_ci		return -EALREADY;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	lock_sock(sk);
43562306a36Sopenharmony_ci	/* Check for already bound sockets */
43662306a36Sopenharmony_ci	if (sk->sk_state & PPPOX_CONNECTED) {
43762306a36Sopenharmony_ci		error = -EBUSY;
43862306a36Sopenharmony_ci		goto end;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/* Check for already disconnected sockets, on attempts to disconnect */
44262306a36Sopenharmony_ci	if (sk->sk_state & PPPOX_DEAD) {
44362306a36Sopenharmony_ci		error = -EALREADY;
44462306a36Sopenharmony_ci		goto end;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (!opt->src_addr.sin_addr.s_addr || !sp->sa_addr.pptp.sin_addr.s_addr) {
44862306a36Sopenharmony_ci		error = -EINVAL;
44962306a36Sopenharmony_ci		goto end;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	po->chan.private = sk;
45362306a36Sopenharmony_ci	po->chan.ops = &pptp_chan_ops;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	rt = pptp_route_output(po, &fl4);
45662306a36Sopenharmony_ci	if (IS_ERR(rt)) {
45762306a36Sopenharmony_ci		error = -EHOSTUNREACH;
45862306a36Sopenharmony_ci		goto end;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci	sk_setup_caps(sk, &rt->dst);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	po->chan.mtu = dst_mtu(&rt->dst);
46362306a36Sopenharmony_ci	if (!po->chan.mtu)
46462306a36Sopenharmony_ci		po->chan.mtu = PPP_MRU;
46562306a36Sopenharmony_ci	po->chan.mtu -= PPTP_HEADER_OVERHEAD;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header);
46862306a36Sopenharmony_ci	error = ppp_register_channel(&po->chan);
46962306a36Sopenharmony_ci	if (error) {
47062306a36Sopenharmony_ci		pr_err("PPTP: failed to register PPP channel (%d)\n", error);
47162306a36Sopenharmony_ci		goto end;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	opt->dst_addr = sp->sa_addr.pptp;
47562306a36Sopenharmony_ci	sk->sk_state |= PPPOX_CONNECTED;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci end:
47862306a36Sopenharmony_ci	release_sock(sk);
47962306a36Sopenharmony_ci	return error;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic int pptp_getname(struct socket *sock, struct sockaddr *uaddr,
48362306a36Sopenharmony_ci	int peer)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	int len = sizeof(struct sockaddr_pppox);
48662306a36Sopenharmony_ci	struct sockaddr_pppox sp;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	memset(&sp.sa_addr, 0, sizeof(sp.sa_addr));
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	sp.sa_family    = AF_PPPOX;
49162306a36Sopenharmony_ci	sp.sa_protocol  = PX_PROTO_PPTP;
49262306a36Sopenharmony_ci	sp.sa_addr.pptp = pppox_sk(sock->sk)->proto.pptp.src_addr;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	memcpy(uaddr, &sp, len);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return len;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic int pptp_release(struct socket *sock)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct sock *sk = sock->sk;
50262306a36Sopenharmony_ci	struct pppox_sock *po;
50362306a36Sopenharmony_ci	int error = 0;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	if (!sk)
50662306a36Sopenharmony_ci		return 0;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	lock_sock(sk);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (sock_flag(sk, SOCK_DEAD)) {
51162306a36Sopenharmony_ci		release_sock(sk);
51262306a36Sopenharmony_ci		return -EBADF;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	po = pppox_sk(sk);
51662306a36Sopenharmony_ci	del_chan(po);
51762306a36Sopenharmony_ci	synchronize_rcu();
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	pppox_unbind_sock(sk);
52062306a36Sopenharmony_ci	sk->sk_state = PPPOX_DEAD;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	sock_orphan(sk);
52362306a36Sopenharmony_ci	sock->sk = NULL;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	release_sock(sk);
52662306a36Sopenharmony_ci	sock_put(sk);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return error;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic void pptp_sock_destruct(struct sock *sk)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	if (!(sk->sk_state & PPPOX_DEAD)) {
53462306a36Sopenharmony_ci		del_chan(pppox_sk(sk));
53562306a36Sopenharmony_ci		pppox_unbind_sock(sk);
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci	skb_queue_purge(&sk->sk_receive_queue);
53862306a36Sopenharmony_ci	dst_release(rcu_dereference_protected(sk->sk_dst_cache, 1));
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic int pptp_create(struct net *net, struct socket *sock, int kern)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	int error = -ENOMEM;
54462306a36Sopenharmony_ci	struct sock *sk;
54562306a36Sopenharmony_ci	struct pppox_sock *po;
54662306a36Sopenharmony_ci	struct pptp_opt *opt;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pptp_sk_proto, kern);
54962306a36Sopenharmony_ci	if (!sk)
55062306a36Sopenharmony_ci		goto out;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	sock_init_data(sock, sk);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	sock->state = SS_UNCONNECTED;
55562306a36Sopenharmony_ci	sock->ops   = &pptp_ops;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	sk->sk_backlog_rcv = pptp_rcv_core;
55862306a36Sopenharmony_ci	sk->sk_state       = PPPOX_NONE;
55962306a36Sopenharmony_ci	sk->sk_type        = SOCK_STREAM;
56062306a36Sopenharmony_ci	sk->sk_family      = PF_PPPOX;
56162306a36Sopenharmony_ci	sk->sk_protocol    = PX_PROTO_PPTP;
56262306a36Sopenharmony_ci	sk->sk_destruct    = pptp_sock_destruct;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	po = pppox_sk(sk);
56562306a36Sopenharmony_ci	opt = &po->proto.pptp;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	opt->seq_sent = 0; opt->seq_recv = 0xffffffff;
56862306a36Sopenharmony_ci	opt->ack_recv = 0; opt->ack_sent = 0xffffffff;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	error = 0;
57162306a36Sopenharmony_ciout:
57262306a36Sopenharmony_ci	return error;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic int pptp_ppp_ioctl(struct ppp_channel *chan, unsigned int cmd,
57662306a36Sopenharmony_ci	unsigned long arg)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct sock *sk = chan->private;
57962306a36Sopenharmony_ci	struct pppox_sock *po = pppox_sk(sk);
58062306a36Sopenharmony_ci	struct pptp_opt *opt = &po->proto.pptp;
58162306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
58262306a36Sopenharmony_ci	int __user *p = argp;
58362306a36Sopenharmony_ci	int err, val;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	err = -EFAULT;
58662306a36Sopenharmony_ci	switch (cmd) {
58762306a36Sopenharmony_ci	case PPPIOCGFLAGS:
58862306a36Sopenharmony_ci		val = opt->ppp_flags;
58962306a36Sopenharmony_ci		if (put_user(val, p))
59062306a36Sopenharmony_ci			break;
59162306a36Sopenharmony_ci		err = 0;
59262306a36Sopenharmony_ci		break;
59362306a36Sopenharmony_ci	case PPPIOCSFLAGS:
59462306a36Sopenharmony_ci		if (get_user(val, p))
59562306a36Sopenharmony_ci			break;
59662306a36Sopenharmony_ci		opt->ppp_flags = val & ~SC_RCV_BITS;
59762306a36Sopenharmony_ci		err = 0;
59862306a36Sopenharmony_ci		break;
59962306a36Sopenharmony_ci	default:
60062306a36Sopenharmony_ci		err = -ENOTTY;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	return err;
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic const struct ppp_channel_ops pptp_chan_ops = {
60762306a36Sopenharmony_ci	.start_xmit = pptp_xmit,
60862306a36Sopenharmony_ci	.ioctl      = pptp_ppp_ioctl,
60962306a36Sopenharmony_ci};
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cistatic struct proto pptp_sk_proto __read_mostly = {
61262306a36Sopenharmony_ci	.name     = "PPTP",
61362306a36Sopenharmony_ci	.owner    = THIS_MODULE,
61462306a36Sopenharmony_ci	.obj_size = sizeof(struct pppox_sock),
61562306a36Sopenharmony_ci};
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic const struct proto_ops pptp_ops = {
61862306a36Sopenharmony_ci	.family     = AF_PPPOX,
61962306a36Sopenharmony_ci	.owner      = THIS_MODULE,
62062306a36Sopenharmony_ci	.release    = pptp_release,
62162306a36Sopenharmony_ci	.bind       = pptp_bind,
62262306a36Sopenharmony_ci	.connect    = pptp_connect,
62362306a36Sopenharmony_ci	.socketpair = sock_no_socketpair,
62462306a36Sopenharmony_ci	.accept     = sock_no_accept,
62562306a36Sopenharmony_ci	.getname    = pptp_getname,
62662306a36Sopenharmony_ci	.listen     = sock_no_listen,
62762306a36Sopenharmony_ci	.shutdown   = sock_no_shutdown,
62862306a36Sopenharmony_ci	.sendmsg    = sock_no_sendmsg,
62962306a36Sopenharmony_ci	.recvmsg    = sock_no_recvmsg,
63062306a36Sopenharmony_ci	.mmap       = sock_no_mmap,
63162306a36Sopenharmony_ci	.ioctl      = pppox_ioctl,
63262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
63362306a36Sopenharmony_ci	.compat_ioctl = pppox_compat_ioctl,
63462306a36Sopenharmony_ci#endif
63562306a36Sopenharmony_ci};
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic const struct pppox_proto pppox_pptp_proto = {
63862306a36Sopenharmony_ci	.create = pptp_create,
63962306a36Sopenharmony_ci	.owner  = THIS_MODULE,
64062306a36Sopenharmony_ci};
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic const struct gre_protocol gre_pptp_protocol = {
64362306a36Sopenharmony_ci	.handler = pptp_rcv,
64462306a36Sopenharmony_ci};
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic int __init pptp_init_module(void)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	int err = 0;
64962306a36Sopenharmony_ci	pr_info("PPTP driver version " PPTP_DRIVER_VERSION "\n");
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	callid_sock = vzalloc(array_size(sizeof(void *), (MAX_CALLID + 1)));
65262306a36Sopenharmony_ci	if (!callid_sock)
65362306a36Sopenharmony_ci		return -ENOMEM;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	err = gre_add_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
65662306a36Sopenharmony_ci	if (err) {
65762306a36Sopenharmony_ci		pr_err("PPTP: can't add gre protocol\n");
65862306a36Sopenharmony_ci		goto out_mem_free;
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	err = proto_register(&pptp_sk_proto, 0);
66262306a36Sopenharmony_ci	if (err) {
66362306a36Sopenharmony_ci		pr_err("PPTP: can't register sk_proto\n");
66462306a36Sopenharmony_ci		goto out_gre_del_protocol;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	err = register_pppox_proto(PX_PROTO_PPTP, &pppox_pptp_proto);
66862306a36Sopenharmony_ci	if (err) {
66962306a36Sopenharmony_ci		pr_err("PPTP: can't register pppox_proto\n");
67062306a36Sopenharmony_ci		goto out_unregister_sk_proto;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	return 0;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ciout_unregister_sk_proto:
67662306a36Sopenharmony_ci	proto_unregister(&pptp_sk_proto);
67762306a36Sopenharmony_ciout_gre_del_protocol:
67862306a36Sopenharmony_ci	gre_del_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
67962306a36Sopenharmony_ciout_mem_free:
68062306a36Sopenharmony_ci	vfree(callid_sock);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	return err;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_cistatic void __exit pptp_exit_module(void)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	unregister_pppox_proto(PX_PROTO_PPTP);
68862306a36Sopenharmony_ci	proto_unregister(&pptp_sk_proto);
68962306a36Sopenharmony_ci	gre_del_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
69062306a36Sopenharmony_ci	vfree(callid_sock);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cimodule_init(pptp_init_module);
69462306a36Sopenharmony_cimodule_exit(pptp_exit_module);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ciMODULE_DESCRIPTION("Point-to-Point Tunneling Protocol");
69762306a36Sopenharmony_ciMODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
69862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
69962306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_PPTP);
700