700 lines · 15.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *  Point-to-Point Tunneling Protocol for Linux * *	Authors: Dmitry Kozlov <xeb@mail.ru> */ #include <linux/string.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/netdevice.h>#include <linux/net.h>#include <linux/skbuff.h>#include <linux/vmalloc.h>#include <linux/init.h>#include <linux/ppp_channel.h>#include <linux/ppp_defs.h>#include <linux/if_pppox.h>#include <linux/ppp-ioctl.h>#include <linux/notifier.h>#include <linux/file.h>#include <linux/in.h>#include <linux/ip.h>#include <linux/rcupdate.h>#include <linux/security.h>#include <linux/spinlock.h> #include <net/sock.h>#include <net/protocol.h>#include <net/ip.h>#include <net/icmp.h>#include <net/route.h>#include <net/gre.h>#include <net/pptp.h> #include <linux/uaccess.h> #define PPTP_DRIVER_VERSION "0.8.5" #define MAX_CALLID 65535 static DECLARE_BITMAP(callid_bitmap, MAX_CALLID + 1);static struct pppox_sock __rcu **callid_sock; static DEFINE_SPINLOCK(chan_lock); static struct proto pptp_sk_proto __read_mostly;static const struct ppp_channel_ops pptp_chan_ops;static const struct proto_ops pptp_ops; static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr){	struct pppox_sock *sock;	struct pptp_opt *opt; 	rcu_read_lock();	sock = rcu_dereference(callid_sock[call_id]);	if (sock) {		opt = &sock->proto.pptp;		if (opt->dst_addr.sin_addr.s_addr != s_addr)			sock = NULL;		else			sock_hold(&sock->sk);	}	rcu_read_unlock(); 	return sock;} static int lookup_chan_dst(u16 call_id, __be32 d_addr){	struct pppox_sock *sock;	struct pptp_opt *opt;	int i; 	rcu_read_lock();	i = 1;	for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) {		sock = rcu_dereference(callid_sock[i]);		if (!sock)			continue;		opt = &sock->proto.pptp;		if (opt->dst_addr.call_id == call_id &&			  opt->dst_addr.sin_addr.s_addr == d_addr)			break;	}	rcu_read_unlock(); 	return i < MAX_CALLID;} static int add_chan(struct pppox_sock *sock,		    struct pptp_addr *sa){	static int call_id; 	spin_lock(&chan_lock);	if (!sa->call_id)	{		call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, call_id + 1);		if (call_id == MAX_CALLID) {			call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, 1);			if (call_id == MAX_CALLID)				goto out_err;		}		sa->call_id = call_id;	} else if (test_bit(sa->call_id, callid_bitmap)) {		goto out_err;	} 	sock->proto.pptp.src_addr = *sa;	set_bit(sa->call_id, callid_bitmap);	rcu_assign_pointer(callid_sock[sa->call_id], sock);	spin_unlock(&chan_lock); 	return 0; out_err:	spin_unlock(&chan_lock);	return -1;} static void del_chan(struct pppox_sock *sock){	spin_lock(&chan_lock);	clear_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap);	RCU_INIT_POINTER(callid_sock[sock->proto.pptp.src_addr.call_id], NULL);	spin_unlock(&chan_lock);} static struct rtable *pptp_route_output(const struct pppox_sock *po,					struct flowi4 *fl4){	const struct sock *sk = &po->sk;	struct net *net; 	net = sock_net(sk);	flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, 0,			   RT_SCOPE_UNIVERSE, IPPROTO_GRE, 0,			   po->proto.pptp.dst_addr.sin_addr.s_addr,			   po->proto.pptp.src_addr.sin_addr.s_addr,			   0, 0, sock_net_uid(net, sk));	security_sk_classify_flow(sk, flowi4_to_flowi_common(fl4)); 	return ip_route_output_flow(net, fl4, sk);} static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb){	struct sock *sk = chan->private;	struct pppox_sock *po = pppox_sk(sk);	struct net *net = sock_net(sk);	struct pptp_opt *opt = &po->proto.pptp;	struct pptp_gre_header *hdr;	unsigned int header_len = sizeof(*hdr);	struct flowi4 fl4;	int islcp;	int len;	unsigned char *data;	__u32 seq_recv;	struct rtable *rt;	struct net_device *tdev;	struct iphdr  *iph;	int    max_headroom; 	if (po->sk.sk_state & PPPOX_DEAD)		goto tx_drop; 	rt = pptp_route_output(po, &fl4);	if (IS_ERR(rt))		goto tx_drop; 	tdev = rt->dst.dev; 	max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(*iph) + sizeof(*hdr) + 2; 	if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) {		struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); 		if (!new_skb)			goto tx_error; 		if (skb->sk)			skb_set_owner_w(new_skb, skb->sk);		consume_skb(skb);		skb = new_skb;	} 	/* Ensure we can safely access protocol field and LCP code */	if (!pskb_may_pull(skb, 3))		goto tx_error; 	data = skb->data;	islcp = ((data[0] << 8) + data[1]) == PPP_LCP && 1 <= data[2] && data[2] <= 7; 	/* compress protocol field */	if ((opt->ppp_flags & SC_COMP_PROT) && data[0] == 0 && !islcp)		skb_pull(skb, 1); 	/* Put in the address/control bytes if necessary */	if ((opt->ppp_flags & SC_COMP_AC) == 0 || islcp) {		data = skb_push(skb, 2);		data[0] = PPP_ALLSTATIONS;		data[1] = PPP_UI;	} 	len = skb->len; 	seq_recv = opt->seq_recv; 	if (opt->ack_sent == seq_recv)		header_len -= sizeof(hdr->ack); 	/* Push down and install GRE header */	skb_push(skb, header_len);	hdr = (struct pptp_gre_header *)(skb->data); 	hdr->gre_hd.flags = GRE_KEY | GRE_VERSION_1 | GRE_SEQ;	hdr->gre_hd.protocol = GRE_PROTO_PPP;	hdr->call_id = htons(opt->dst_addr.call_id); 	hdr->seq = htonl(++opt->seq_sent);	if (opt->ack_sent != seq_recv)	{		/* send ack with this message */		hdr->gre_hd.flags |= GRE_ACK;		hdr->ack  = htonl(seq_recv);		opt->ack_sent = seq_recv;	}	hdr->payload_len = htons(len); 	/*	Push down and install the IP header. */ 	skb_reset_transport_header(skb);	skb_push(skb, sizeof(*iph));	skb_reset_network_header(skb);	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));	IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED); 	iph =	ip_hdr(skb);	iph->version =	4;	iph->ihl =	sizeof(struct iphdr) >> 2;	if (ip_dont_fragment(sk, &rt->dst))		iph->frag_off	=	htons(IP_DF);	else		iph->frag_off	=	0;	iph->protocol = IPPROTO_GRE;	iph->tos      = 0;	iph->daddr    = fl4.daddr;	iph->saddr    = fl4.saddr;	iph->ttl      = ip4_dst_hoplimit(&rt->dst);	iph->tot_len  = htons(skb->len); 	skb_dst_drop(skb);	skb_dst_set(skb, &rt->dst); 	nf_reset_ct(skb); 	skb->ip_summed = CHECKSUM_NONE;	ip_select_ident(net, skb, NULL);	ip_send_check(iph); 	ip_local_out(net, skb->sk, skb);	return 1; tx_error:	ip_rt_put(rt);tx_drop:	kfree_skb(skb);	return 1;} static int pptp_rcv_core(struct sock *sk, struct sk_buff *skb){	struct pppox_sock *po = pppox_sk(sk);	struct pptp_opt *opt = &po->proto.pptp;	int headersize, payload_len, seq;	__u8 *payload;	struct pptp_gre_header *header; 	if (!(sk->sk_state & PPPOX_CONNECTED))		goto drop; 	header = (struct pptp_gre_header *)(skb->data);	headersize  = sizeof(*header); 	/* test if acknowledgement present */	if (GRE_IS_ACK(header->gre_hd.flags)) {		__u32 ack; 		if (!pskb_may_pull(skb, headersize))			goto drop;		header = (struct pptp_gre_header *)(skb->data); 		/* ack in different place if S = 0 */		ack = GRE_IS_SEQ(header->gre_hd.flags) ? ntohl(header->ack) :							 ntohl(header->seq);		if (ack > opt->ack_recv)			opt->ack_recv = ack;		/* also handle sequence number wrap-around  */		if (WRAPPED(ack, opt->ack_recv))			opt->ack_recv = ack;	} else {		headersize -= sizeof(header->ack);	}	/* test if payload present */	if (!GRE_IS_SEQ(header->gre_hd.flags))		goto drop; 	payload_len = ntohs(header->payload_len);	seq         = ntohl(header->seq); 	/* check for incomplete packet (length smaller than expected) */	if (!pskb_may_pull(skb, headersize + payload_len))		goto drop; 	payload = skb->data + headersize;	/* check for expected sequence number */	if (seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq)) {		if ((payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) &&				(PPP_PROTOCOL(payload) == PPP_LCP) &&				((payload[4] == PPP_LCP_ECHOREQ) || (payload[4] == PPP_LCP_ECHOREP)))			goto allow_packet;	} else {		opt->seq_recv = seq;allow_packet:		skb_pull(skb, headersize); 		if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI) {			/* chop off address/control */			if (skb->len < 3)				goto drop;			skb_pull(skb, 2);		} 		skb->ip_summed = CHECKSUM_NONE;		skb_set_network_header(skb, skb->head-skb->data);		ppp_input(&po->chan, skb); 		return NET_RX_SUCCESS;	}drop:	kfree_skb(skb);	return NET_RX_DROP;} static int pptp_rcv(struct sk_buff *skb){	struct pppox_sock *po;	struct pptp_gre_header *header;	struct iphdr *iph; 	if (skb->pkt_type != PACKET_HOST)		goto drop; 	if (!pskb_may_pull(skb, 12))		goto drop; 	iph = ip_hdr(skb); 	header = (struct pptp_gre_header *)skb->data; 	if (header->gre_hd.protocol != GRE_PROTO_PPP || /* PPTP-GRE protocol for PPTP */		GRE_IS_CSUM(header->gre_hd.flags) ||    /* flag CSUM should be clear */		GRE_IS_ROUTING(header->gre_hd.flags) || /* flag ROUTING should be clear */		!GRE_IS_KEY(header->gre_hd.flags) ||    /* flag KEY should be set */		(header->gre_hd.flags & GRE_FLAGS))     /* flag Recursion Ctrl should be clear */		/* if invalid, discard this packet */		goto drop; 	po = lookup_chan(ntohs(header->call_id), iph->saddr);	if (po) {		skb_dst_drop(skb);		nf_reset_ct(skb);		return sk_receive_skb(&po->sk, skb, 0);	}drop:	kfree_skb(skb);	return NET_RX_DROP;} static int pptp_bind(struct socket *sock, struct sockaddr_unsized *uservaddr,		     int sockaddr_len){	struct sock *sk = sock->sk;	struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;	struct pppox_sock *po = pppox_sk(sk);	int error = 0; 	if (sockaddr_len < sizeof(struct sockaddr_pppox))		return -EINVAL; 	lock_sock(sk); 	if (sk->sk_state & PPPOX_DEAD) {		error = -EALREADY;		goto out;	} 	if (sk->sk_state & PPPOX_BOUND) {		error = -EBUSY;		goto out;	} 	if (add_chan(po, &sp->sa_addr.pptp))		error = -EBUSY;	else		sk->sk_state |= PPPOX_BOUND; out:	release_sock(sk);	return error;} static int pptp_connect(struct socket *sock, struct sockaddr_unsized *uservaddr,			int sockaddr_len, int flags){	struct sock *sk = sock->sk;	struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;	struct pppox_sock *po = pppox_sk(sk);	struct pptp_opt *opt = &po->proto.pptp;	struct rtable *rt;	struct flowi4 fl4;	int error = 0; 	if (sockaddr_len < sizeof(struct sockaddr_pppox))		return -EINVAL; 	if (sp->sa_protocol != PX_PROTO_PPTP)		return -EINVAL; 	if (lookup_chan_dst(sp->sa_addr.pptp.call_id, sp->sa_addr.pptp.sin_addr.s_addr))		return -EALREADY; 	lock_sock(sk);	/* Check for already bound sockets */	if (sk->sk_state & PPPOX_CONNECTED) {		error = -EBUSY;		goto end;	} 	/* Check for already disconnected sockets, on attempts to disconnect */	if (sk->sk_state & PPPOX_DEAD) {		error = -EALREADY;		goto end;	} 	if (!opt->src_addr.sin_addr.s_addr || !sp->sa_addr.pptp.sin_addr.s_addr) {		error = -EINVAL;		goto end;	} 	po->chan.private = sk;	po->chan.ops = &pptp_chan_ops; 	rt = pptp_route_output(po, &fl4);	if (IS_ERR(rt)) {		error = -EHOSTUNREACH;		goto end;	}	sk_setup_caps(sk, &rt->dst); 	po->chan.mtu = dst_mtu(&rt->dst);	if (!po->chan.mtu)		po->chan.mtu = PPP_MRU;	po->chan.mtu -= PPTP_HEADER_OVERHEAD; 	po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header);	po->chan.direct_xmit = true;	error = ppp_register_channel(&po->chan);	if (error) {		pr_err("PPTP: failed to register PPP channel (%d)\n", error);		goto end;	} 	opt->dst_addr = sp->sa_addr.pptp;	sk->sk_state |= PPPOX_CONNECTED;  end:	release_sock(sk);	return error;} static int pptp_getname(struct socket *sock, struct sockaddr *uaddr,	int peer){	int len = sizeof(struct sockaddr_pppox);	struct sockaddr_pppox sp; 	memset(&sp.sa_addr, 0, sizeof(sp.sa_addr)); 	sp.sa_family    = AF_PPPOX;	sp.sa_protocol  = PX_PROTO_PPTP;	sp.sa_addr.pptp = pppox_sk(sock->sk)->proto.pptp.src_addr; 	memcpy(uaddr, &sp, len); 	return len;} 

Lines 1–500 of 700.