mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			478 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			478 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
--- a/include/linux/if_pppox.h
 | 
						|
+++ b/include/linux/if_pppox.h
 | 
						|
@@ -36,6 +36,7 @@ struct pptp_opt {
 | 
						|
 	u32 ack_sent, ack_recv;
 | 
						|
 	u32 seq_sent, seq_recv;
 | 
						|
 	int ppp_flags;
 | 
						|
+	bool pptp_offload_mode;
 | 
						|
 };
 | 
						|
 #include <net/sock.h>
 | 
						|
 
 | 
						|
@@ -100,8 +101,40 @@ struct pppoe_channel_ops {
 | 
						|
 	int (*get_addressing)(struct ppp_channel *, struct pppoe_opt *);
 | 
						|
 };
 | 
						|
 
 | 
						|
+/* PPTP client callback */
 | 
						|
+typedef int (*pptp_gre_seq_offload_callback_t)(struct sk_buff *skb,
 | 
						|
+					       struct net_device *pptp_dev);
 | 
						|
+
 | 
						|
 /* Return PPPoE channel specific addressing information */
 | 
						|
 extern int pppoe_channel_addressing_get(struct ppp_channel *chan,
 | 
						|
 					 struct pppoe_opt *addressing);
 | 
						|
 
 | 
						|
+/* Lookup PPTP session info and return PPTP session using sip, dip and local call id */
 | 
						|
+extern int pptp_session_find_by_src_callid(struct pptp_opt *opt, __be16 src_call_id,
 | 
						|
+			 __be32 daddr, __be32 saddr);
 | 
						|
+
 | 
						|
+/* Lookup PPTP session info and return PPTP session using dip and peer call id */
 | 
						|
+extern int pptp_session_find(struct pptp_opt *opt, __be16 peer_call_id,
 | 
						|
+			     __be32 peer_ip_addr);
 | 
						|
+
 | 
						|
+/* Return PPTP session information given the channel */
 | 
						|
+extern void pptp_channel_addressing_get(struct pptp_opt *opt,
 | 
						|
+					struct ppp_channel *chan);
 | 
						|
+
 | 
						|
+/* Enable the PPTP session offload flag */
 | 
						|
+extern int pptp_session_enable_offload_mode(__be16 peer_call_id,
 | 
						|
+					    __be32 peer_ip_addr);
 | 
						|
+
 | 
						|
+/* Disable the PPTP session offload flag */
 | 
						|
+extern int pptp_session_disable_offload_mode(__be16 peer_call_id,
 | 
						|
+					     __be32 peer_ip_addr);
 | 
						|
+
 | 
						|
+/* Register the PPTP GRE packets sequence number offload callback */
 | 
						|
+extern int
 | 
						|
+pptp_register_gre_seq_offload_callback(pptp_gre_seq_offload_callback_t
 | 
						|
+				       pptp_client_cb);
 | 
						|
+
 | 
						|
+/* Unregister the PPTP GRE packets sequence number offload callback */
 | 
						|
+extern void pptp_unregister_gre_seq_offload_callback(void);
 | 
						|
+
 | 
						|
 #endif /* !(__LINUX_IF_PPPOX_H) */
 | 
						|
--- a/drivers/net/ppp/ppp_generic.c
 | 
						|
+++ b/drivers/net/ppp/ppp_generic.c
 | 
						|
@@ -2973,6 +2973,20 @@ char *ppp_dev_name(struct ppp_channel *c
 | 
						|
 	return name;
 | 
						|
 }
 | 
						|
 
 | 
						|
+/* Return the PPP net device index */
 | 
						|
+int ppp_dev_index(struct ppp_channel *chan)
 | 
						|
+{
 | 
						|
+	struct channel *pch = chan->ppp;
 | 
						|
+	int ifindex = 0;
 | 
						|
+
 | 
						|
+	if (pch) {
 | 
						|
+		read_lock_bh(&pch->upl);
 | 
						|
+		if (pch->ppp && pch->ppp->dev)
 | 
						|
+			ifindex = pch->ppp->dev->ifindex;
 | 
						|
+		read_unlock_bh(&pch->upl);
 | 
						|
+	}
 | 
						|
+	return ifindex;
 | 
						|
+}
 | 
						|
 
 | 
						|
 /*
 | 
						|
  * Disconnect a channel from the generic layer.
 | 
						|
@@ -3681,6 +3695,28 @@ void ppp_update_stats(struct net_device
 | 
						|
 	ppp_recv_unlock(ppp);
 | 
						|
 }
 | 
						|
 
 | 
						|
+/* Returns true if Compression is enabled on PPP device
 | 
						|
+ */
 | 
						|
+bool ppp_is_cp_enabled(struct net_device *dev)
 | 
						|
+{
 | 
						|
+	struct ppp *ppp;
 | 
						|
+	bool flag = false;
 | 
						|
+
 | 
						|
+	if (!dev)
 | 
						|
+		return false;
 | 
						|
+
 | 
						|
+	if (dev->type != ARPHRD_PPP)
 | 
						|
+		return false;
 | 
						|
+
 | 
						|
+	ppp = netdev_priv(dev);
 | 
						|
+	ppp_lock(ppp);
 | 
						|
+	flag = !!(ppp->xstate & SC_COMP_RUN) || !!(ppp->rstate & SC_DECOMP_RUN);
 | 
						|
+	ppp_unlock(ppp);
 | 
						|
+
 | 
						|
+	return flag;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL(ppp_is_cp_enabled);
 | 
						|
+
 | 
						|
 /* Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 if
 | 
						|
  * the device is not PPP.
 | 
						|
  */
 | 
						|
@@ -3872,6 +3908,7 @@ EXPORT_SYMBOL(ppp_unregister_channel);
 | 
						|
 EXPORT_SYMBOL(ppp_channel_index);
 | 
						|
 EXPORT_SYMBOL(ppp_unit_number);
 | 
						|
 EXPORT_SYMBOL(ppp_dev_name);
 | 
						|
+EXPORT_SYMBOL(ppp_dev_index);
 | 
						|
 EXPORT_SYMBOL(ppp_input);
 | 
						|
 EXPORT_SYMBOL(ppp_input_error);
 | 
						|
 EXPORT_SYMBOL(ppp_output_wakeup);
 | 
						|
--- a/include/linux/ppp_channel.h
 | 
						|
+++ b/include/linux/ppp_channel.h
 | 
						|
@@ -84,6 +84,9 @@ extern void ppp_unregister_channel(struc
 | 
						|
 /* Get the channel number for a channel */
 | 
						|
 extern int ppp_channel_index(struct ppp_channel *);
 | 
						|
 
 | 
						|
+/* Get the device index  associated with a channel, or 0, if none */
 | 
						|
+extern int ppp_dev_index(struct ppp_channel *);
 | 
						|
+
 | 
						|
 /* Get the unit number associated with a channel, or -1 if none */
 | 
						|
 extern int ppp_unit_number(struct ppp_channel *);
 | 
						|
 
 | 
						|
@@ -116,6 +119,7 @@ extern int ppp_hold_channels(struct net_
 | 
						|
 /* Test if ppp xmit lock is locked */
 | 
						|
 extern bool ppp_is_xmit_locked(struct net_device *dev);
 | 
						|
 
 | 
						|
+bool ppp_is_cp_enabled(struct net_device *dev);
 | 
						|
 /* Test if the ppp device is a multi-link ppp device */
 | 
						|
 extern int ppp_is_multilink(struct net_device *dev);
 | 
						|
 
 | 
						|
--- a/drivers/net/ppp/pptp.c
 | 
						|
+++ b/drivers/net/ppp/pptp.c
 | 
						|
@@ -50,6 +50,8 @@ static struct proto pptp_sk_proto __read
 | 
						|
 static const struct ppp_channel_ops pptp_chan_ops;
 | 
						|
 static const struct proto_ops pptp_ops;
 | 
						|
 
 | 
						|
+static pptp_gre_seq_offload_callback_t __rcu pptp_gre_offload_xmit_cb;
 | 
						|
+
 | 
						|
 static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr)
 | 
						|
 {
 | 
						|
 	struct pppox_sock *sock;
 | 
						|
@@ -91,6 +93,79 @@ static int lookup_chan_dst(u16 call_id,
 | 
						|
 	return i < MAX_CALLID;
 | 
						|
 }
 | 
						|
 
 | 
						|
+/* Search a pptp session based on local call id, local and remote ip address */
 | 
						|
+static int lookup_session_src(struct pptp_opt *opt, u16 call_id, __be32 daddr, __be32 saddr)
 | 
						|
+{
 | 
						|
+	struct pppox_sock *sock;
 | 
						|
+	int i = 1;
 | 
						|
+
 | 
						|
+	rcu_read_lock();
 | 
						|
+	for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) {
 | 
						|
+		sock = rcu_dereference(callid_sock[i]);
 | 
						|
+		if (!sock)
 | 
						|
+			continue;
 | 
						|
+
 | 
						|
+		if (sock->proto.pptp.src_addr.call_id == call_id &&
 | 
						|
+		    sock->proto.pptp.dst_addr.sin_addr.s_addr == daddr &&
 | 
						|
+		    sock->proto.pptp.src_addr.sin_addr.s_addr == saddr) {
 | 
						|
+			sock_hold(sk_pppox(sock));
 | 
						|
+			memcpy(opt, &sock->proto.pptp, sizeof(struct pptp_opt));
 | 
						|
+			sock_put(sk_pppox(sock));
 | 
						|
+			rcu_read_unlock();
 | 
						|
+			return 0;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	rcu_read_unlock();
 | 
						|
+	return -EINVAL;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* Search a pptp session based on peer call id and peer ip address */
 | 
						|
+static int lookup_session_dst(struct pptp_opt *opt, u16 call_id, __be32 d_addr)
 | 
						|
+{
 | 
						|
+	struct pppox_sock *sock;
 | 
						|
+	int i = 1;
 | 
						|
+
 | 
						|
+	rcu_read_lock();
 | 
						|
+	for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) {
 | 
						|
+		sock = rcu_dereference(callid_sock[i]);
 | 
						|
+		if (!sock)
 | 
						|
+			continue;
 | 
						|
+
 | 
						|
+		if (sock->proto.pptp.dst_addr.call_id == call_id &&
 | 
						|
+		    sock->proto.pptp.dst_addr.sin_addr.s_addr == d_addr) {
 | 
						|
+			sock_hold(sk_pppox(sock));
 | 
						|
+			memcpy(opt, &sock->proto.pptp, sizeof(struct pptp_opt));
 | 
						|
+			sock_put(sk_pppox(sock));
 | 
						|
+			rcu_read_unlock();
 | 
						|
+			return 0;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	rcu_read_unlock();
 | 
						|
+	return -EINVAL;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* If offload mode set then this function sends all packets to
 | 
						|
+ * offload module instead of network stack
 | 
						|
+ */
 | 
						|
+static int pptp_client_skb_xmit(struct sk_buff *skb,
 | 
						|
+				struct net_device *pptp_dev)
 | 
						|
+{
 | 
						|
+	pptp_gre_seq_offload_callback_t pptp_gre_offload_cb_f;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	rcu_read_lock();
 | 
						|
+	pptp_gre_offload_cb_f = rcu_dereference(pptp_gre_offload_xmit_cb);
 | 
						|
+
 | 
						|
+	if (!pptp_gre_offload_cb_f) {
 | 
						|
+		rcu_read_unlock();
 | 
						|
+		return -1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ret = pptp_gre_offload_cb_f(skb, pptp_dev);
 | 
						|
+	rcu_read_unlock();
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
 static int add_chan(struct pppox_sock *sock,
 | 
						|
 		    struct pptp_addr *sa)
 | 
						|
 {
 | 
						|
@@ -136,7 +211,7 @@ static struct rtable *pptp_route_output(
 | 
						|
 	struct net *net;
 | 
						|
 
 | 
						|
 	net = sock_net(sk);
 | 
						|
-	flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, 0,
 | 
						|
+	flowi4_init_output(fl4, 0, 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,
 | 
						|
@@ -163,8 +238,11 @@ static int pptp_xmit(struct ppp_channel
 | 
						|
 
 | 
						|
 	struct rtable *rt;
 | 
						|
 	struct net_device *tdev;
 | 
						|
+	struct net_device *pptp_dev;
 | 
						|
 	struct iphdr  *iph;
 | 
						|
 	int    max_headroom;
 | 
						|
+	int    pptp_ifindex;
 | 
						|
+	int    ret;
 | 
						|
 
 | 
						|
 	if (sk_pppox(po)->sk_state & PPPOX_DEAD)
 | 
						|
 		goto tx_error;
 | 
						|
@@ -258,7 +336,32 @@ static int pptp_xmit(struct ppp_channel
 | 
						|
 	ip_select_ident(net, skb, NULL);
 | 
						|
 	ip_send_check(iph);
 | 
						|
 
 | 
						|
-	ip_local_out(net, skb->sk, skb);
 | 
						|
+	pptp_ifindex = ppp_dev_index(chan);
 | 
						|
+
 | 
						|
+	/* set incoming interface as the ppp interface */
 | 
						|
+	if (skb->skb_iif)
 | 
						|
+		skb->skb_iif = pptp_ifindex;
 | 
						|
+
 | 
						|
+	/* If the PPTP GRE seq number offload module is not enabled yet
 | 
						|
+	 * then sends all PPTP GRE packets through linux network stack
 | 
						|
+	 */
 | 
						|
+	if (!opt->pptp_offload_mode) {
 | 
						|
+		ip_local_out(net, skb->sk, skb);
 | 
						|
+		return 1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	pptp_dev = dev_get_by_index(&init_net, pptp_ifindex);
 | 
						|
+	if (!pptp_dev)
 | 
						|
+		goto tx_error;
 | 
						|
+
 | 
						|
+	 /* If PPTP offload module is enabled then forward all PPTP GRE
 | 
						|
+	  * packets to PPTP GRE offload module
 | 
						|
+	  */
 | 
						|
+	ret = pptp_client_skb_xmit(skb, pptp_dev);
 | 
						|
+	dev_put(pptp_dev);
 | 
						|
+	if (ret < 0)
 | 
						|
+		goto tx_error;
 | 
						|
+
 | 
						|
 	return 1;
 | 
						|
 
 | 
						|
 tx_error:
 | 
						|
@@ -314,6 +417,13 @@ static int pptp_rcv_core(struct sock *sk
 | 
						|
 		goto drop;
 | 
						|
 
 | 
						|
 	payload = skb->data + headersize;
 | 
						|
+
 | 
						|
+	 /* If offload is enabled, we expect the offload module
 | 
						|
+	  * to handle PPTP GRE sequence number checks
 | 
						|
+	  */
 | 
						|
+	if (opt->pptp_offload_mode)
 | 
						|
+		goto allow_packet;
 | 
						|
+
 | 
						|
 	/* 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) &&
 | 
						|
@@ -371,6 +481,7 @@ static int pptp_rcv(struct sk_buff *skb)
 | 
						|
 	if (po) {
 | 
						|
 		skb_dst_drop(skb);
 | 
						|
 		nf_reset_ct(skb);
 | 
						|
+		skb->skb_iif = ppp_dev_index(&po->chan);
 | 
						|
 		return sk_receive_skb(sk_pppox(po), skb, 0);
 | 
						|
 	}
 | 
						|
 drop:
 | 
						|
@@ -473,7 +584,7 @@ static int pptp_connect(struct socket *s
 | 
						|
 
 | 
						|
 	opt->dst_addr = sp->sa_addr.pptp;
 | 
						|
 	sk->sk_state |= PPPOX_CONNECTED;
 | 
						|
-
 | 
						|
+	opt->pptp_offload_mode = false;
 | 
						|
  end:
 | 
						|
 	release_sock(sk);
 | 
						|
 	return error;
 | 
						|
@@ -603,9 +714,169 @@ static int pptp_ppp_ioctl(struct ppp_cha
 | 
						|
 	return err;
 | 
						|
 }
 | 
						|
 
 | 
						|
+/* pptp_channel_addressing_get()
 | 
						|
+ *	Return PPTP channel specific addressing information.
 | 
						|
+ */
 | 
						|
+void pptp_channel_addressing_get(struct pptp_opt *opt, struct ppp_channel *chan)
 | 
						|
+{
 | 
						|
+	struct sock *sk;
 | 
						|
+	struct pppox_sock *po;
 | 
						|
+
 | 
						|
+	if (!opt)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	sk = (struct sock *)chan->private;
 | 
						|
+	if (!sk)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	sock_hold(sk);
 | 
						|
+
 | 
						|
+	/* This is very unlikely, but check the socket is connected state */
 | 
						|
+	if (unlikely(sock_flag(sk, SOCK_DEAD) ||
 | 
						|
+		     !(sk->sk_state & PPPOX_CONNECTED))) {
 | 
						|
+		sock_put(sk);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	po = pppox_sk(sk);
 | 
						|
+	memcpy(opt, &po->proto.pptp, sizeof(struct pptp_opt));
 | 
						|
+	sock_put(sk);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL(pptp_channel_addressing_get);
 | 
						|
+
 | 
						|
+/* pptp_session_find()
 | 
						|
+ *	Search and return a PPTP session info based on peer callid and IP
 | 
						|
+ *	address. The function accepts the parameters in network byte order.
 | 
						|
+ */
 | 
						|
+int pptp_session_find(struct pptp_opt *opt, __be16 peer_call_id,
 | 
						|
+		      __be32 peer_ip_addr)
 | 
						|
+{
 | 
						|
+	if (!opt)
 | 
						|
+		return -EINVAL;
 | 
						|
+
 | 
						|
+	return lookup_session_dst(opt, ntohs(peer_call_id), peer_ip_addr);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL(pptp_session_find);
 | 
						|
+
 | 
						|
+/* pptp_session_find_by_src_callid()
 | 
						|
+ *	Search and return a PPTP session info based on src callid and IP
 | 
						|
+ *	address. The function accepts the parameters in network byte order.
 | 
						|
+ */
 | 
						|
+int pptp_session_find_by_src_callid(struct pptp_opt *opt, __be16 src_call_id,
 | 
						|
+		      __be32 daddr, __be32 saddr)
 | 
						|
+{
 | 
						|
+	if (!opt)
 | 
						|
+		return -EINVAL;
 | 
						|
+
 | 
						|
+	return lookup_session_src(opt, ntohs(src_call_id), daddr, saddr);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL(pptp_session_find_by_src_callid);
 | 
						|
+
 | 
						|
+ /* Function to change the offload mode true/false for a PPTP session */
 | 
						|
+static int pptp_set_offload_mode(bool accel_mode,
 | 
						|
+				 __be16 peer_call_id, __be32 peer_ip_addr)
 | 
						|
+{
 | 
						|
+	struct pppox_sock *sock;
 | 
						|
+	int i = 1;
 | 
						|
+
 | 
						|
+	rcu_read_lock();
 | 
						|
+	for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) {
 | 
						|
+		sock = rcu_dereference(callid_sock[i]);
 | 
						|
+		if (!sock)
 | 
						|
+			continue;
 | 
						|
+
 | 
						|
+		if (sock->proto.pptp.dst_addr.call_id == peer_call_id &&
 | 
						|
+		    sock->proto.pptp.dst_addr.sin_addr.s_addr == peer_ip_addr) {
 | 
						|
+			sock_hold(sk_pppox(sock));
 | 
						|
+			sock->proto.pptp.pptp_offload_mode = accel_mode;
 | 
						|
+			sock_put(sk_pppox(sock));
 | 
						|
+			rcu_read_unlock();
 | 
						|
+			return 0;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	rcu_read_unlock();
 | 
						|
+	return -EINVAL;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* Enable the PPTP session offload flag */
 | 
						|
+int pptp_session_enable_offload_mode(__be16 peer_call_id, __be32 peer_ip_addr)
 | 
						|
+{
 | 
						|
+	return pptp_set_offload_mode(true, peer_call_id, peer_ip_addr);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL(pptp_session_enable_offload_mode);
 | 
						|
+
 | 
						|
+/* Disable the PPTP session offload flag */
 | 
						|
+int pptp_session_disable_offload_mode(__be16 peer_call_id, __be32 peer_ip_addr)
 | 
						|
+{
 | 
						|
+	return pptp_set_offload_mode(false, peer_call_id, peer_ip_addr);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL(pptp_session_disable_offload_mode);
 | 
						|
+
 | 
						|
+/* Register the offload callback function on behalf of the module which
 | 
						|
+ * will own the sequence and acknowledgment number updates for all
 | 
						|
+ * PPTP GRE packets. All PPTP GRE packets are then transmitted to this
 | 
						|
+ * module after encapsulation in order to ensure the correct seq/ack
 | 
						|
+ * fields are set in the packets before transmission. This is required
 | 
						|
+ * when PPTP flows are offloaded to acceleration engines, in-order to
 | 
						|
+ * ensure consistency in sequence and ack numbers between PPTP control
 | 
						|
+ * (PPP LCP) and data packets
 | 
						|
+ */
 | 
						|
+int pptp_register_gre_seq_offload_callback(pptp_gre_seq_offload_callback_t
 | 
						|
+					   pptp_gre_offload_cb)
 | 
						|
+{
 | 
						|
+	pptp_gre_seq_offload_callback_t pptp_gre_offload_cb_f;
 | 
						|
+
 | 
						|
+	rcu_read_lock();
 | 
						|
+	pptp_gre_offload_cb_f = rcu_dereference(pptp_gre_offload_xmit_cb);
 | 
						|
+
 | 
						|
+	if (pptp_gre_offload_cb_f) {
 | 
						|
+		rcu_read_unlock();
 | 
						|
+		return -1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	rcu_assign_pointer(pptp_gre_offload_xmit_cb, pptp_gre_offload_cb);
 | 
						|
+	rcu_read_unlock();
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL(pptp_register_gre_seq_offload_callback);
 | 
						|
+
 | 
						|
+/* Unregister the PPTP GRE packets sequence number offload callback */
 | 
						|
+void pptp_unregister_gre_seq_offload_callback(void)
 | 
						|
+{
 | 
						|
+	rcu_assign_pointer(pptp_gre_offload_xmit_cb, NULL);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL(pptp_unregister_gre_seq_offload_callback);
 | 
						|
+
 | 
						|
+/* pptp_hold_chan() */
 | 
						|
+static void pptp_hold_chan(struct ppp_channel *chan)
 | 
						|
+{
 | 
						|
+	struct sock *sk = (struct sock *)chan->private;
 | 
						|
+
 | 
						|
+	sock_hold(sk);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* pptp_release_chan() */
 | 
						|
+static void pptp_release_chan(struct ppp_channel *chan)
 | 
						|
+{
 | 
						|
+	struct sock *sk = (struct sock *)chan->private;
 | 
						|
+
 | 
						|
+	sock_put(sk);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* pptp_get_channel_protocol()
 | 
						|
+ *     Return the protocol type of the PPTP over PPP protocol
 | 
						|
+ */
 | 
						|
+static int pptp_get_channel_protocol(struct ppp_channel *chan)
 | 
						|
+{
 | 
						|
+	return PX_PROTO_PPTP;
 | 
						|
+}
 | 
						|
+
 | 
						|
 static const struct ppp_channel_ops pptp_chan_ops = {
 | 
						|
 	.start_xmit = pptp_xmit,
 | 
						|
 	.ioctl      = pptp_ppp_ioctl,
 | 
						|
+	.get_channel_protocol = pptp_get_channel_protocol,
 | 
						|
+	.hold = pptp_hold_chan,
 | 
						|
+	.release = pptp_release_chan,
 | 
						|
 };
 | 
						|
 
 | 
						|
 static struct proto pptp_sk_proto __read_mostly = {
 |