pppol2tp.c

来自「linux 内核源代码」· C语言 代码 · 共 2,472 行 · 第 1/5 页

C
2,472
字号
/***************************************************************************** * Linux PPP over L2TP (PPPoX/PPPoL2TP) Sockets * * PPPoX    --- Generic PPP encapsulation socket family * PPPoL2TP --- PPP over L2TP (RFC 2661) * * Version:	1.0.0 * * Authors:	Martijn van Oosterhout <kleptog@svana.org> *		James Chapman (jchapman@katalix.com) * Contributors: *		Michal Ostrowski <mostrows@speakeasy.net> *		Arnaldo Carvalho de Melo <acme@xconectiva.com.br> *		David S. Miller (davem@redhat.com) * * License: *		This program is free software; you can redistribute it and/or *		modify it under the terms of the GNU General Public License *		as published by the Free Software Foundation; either version *		2 of the License, or (at your option) any later version. * *//* This driver handles only L2TP data frames; control frames are handled by a * userspace application. * * To send data in an L2TP session, userspace opens a PPPoL2TP socket and * attaches it to a bound UDP socket with local tunnel_id / session_id and * peer tunnel_id / session_id set. Data can then be sent or received using * regular socket sendmsg() / recvmsg() calls. Kernel parameters of the socket * can be read or modified using ioctl() or [gs]etsockopt() calls. * * When a PPPoL2TP socket is connected with local and peer session_id values * zero, the socket is treated as a special tunnel management socket. * * Here's example userspace code to create a socket for sending/receiving data * over an L2TP session:- * *	struct sockaddr_pppol2tp sax; *	int fd; *	int session_fd; * *	fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP); * *	sax.sa_family = AF_PPPOX; *	sax.sa_protocol = PX_PROTO_OL2TP; *	sax.pppol2tp.fd = tunnel_fd;	// bound UDP socket *	sax.pppol2tp.addr.sin_addr.s_addr = addr->sin_addr.s_addr; *	sax.pppol2tp.addr.sin_port = addr->sin_port; *	sax.pppol2tp.addr.sin_family = AF_INET; *	sax.pppol2tp.s_tunnel  = tunnel_id; *	sax.pppol2tp.s_session = session_id; *	sax.pppol2tp.d_tunnel  = peer_tunnel_id; *	sax.pppol2tp.d_session = peer_session_id; * *	session_fd = connect(fd, (struct sockaddr *)&sax, sizeof(sax)); * * A pppd plugin that allows PPP traffic to be carried over L2TP using * this driver is available from the OpenL2TP project at * http://openl2tp.sourceforge.net. */#include <linux/module.h>#include <linux/version.h>#include <linux/string.h>#include <linux/list.h>#include <asm/uaccess.h>#include <linux/kernel.h>#include <linux/spinlock.h>#include <linux/kthread.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/jiffies.h>#include <linux/netdevice.h>#include <linux/net.h>#include <linux/inetdevice.h>#include <linux/skbuff.h>#include <linux/init.h>#include <linux/ip.h>#include <linux/udp.h>#include <linux/if_pppox.h>#include <linux/if_pppol2tp.h>#include <net/sock.h>#include <linux/ppp_channel.h>#include <linux/ppp_defs.h>#include <linux/if_ppp.h>#include <linux/file.h>#include <linux/hash.h>#include <linux/sort.h>#include <linux/proc_fs.h>#include <net/net_namespace.h>#include <net/dst.h>#include <net/ip.h>#include <net/udp.h>#include <net/xfrm.h>#include <asm/byteorder.h>#include <asm/atomic.h>#define PPPOL2TP_DRV_VERSION	"V1.0"/* L2TP header constants */#define L2TP_HDRFLAG_T	   0x8000#define L2TP_HDRFLAG_L	   0x4000#define L2TP_HDRFLAG_S	   0x0800#define L2TP_HDRFLAG_O	   0x0200#define L2TP_HDRFLAG_P	   0x0100#define L2TP_HDR_VER_MASK  0x000F#define L2TP_HDR_VER	   0x0002/* Space for UDP, L2TP and PPP headers */#define PPPOL2TP_HEADER_OVERHEAD	40/* Just some random numbers */#define L2TP_TUNNEL_MAGIC	0x42114DDA#define L2TP_SESSION_MAGIC	0x0C04EB7D#define PPPOL2TP_HASH_BITS	4#define PPPOL2TP_HASH_SIZE	(1 << PPPOL2TP_HASH_BITS)/* Default trace flags */#define PPPOL2TP_DEFAULT_DEBUG_FLAGS	0#define PRINTK(_mask, _type, _lvl, _fmt, args...)			\	do {								\		if ((_mask) & (_type))					\			printk(_lvl "PPPOL2TP: " _fmt, ##args);		\	} while(0)/* Number of bytes to build transmit L2TP headers. * Unfortunately the size is different depending on whether sequence numbers * are enabled. */#define PPPOL2TP_L2TP_HDR_SIZE_SEQ		10#define PPPOL2TP_L2TP_HDR_SIZE_NOSEQ		6struct pppol2tp_tunnel;/* Describes a session. It is the sk_user_data field in the PPPoL2TP * socket. Contains information to determine incoming packets and transmit * outgoing ones. */struct pppol2tp_session{	int			magic;		/* should be						 * L2TP_SESSION_MAGIC */	int			owner;		/* pid that opened the socket */	struct sock		*sock;		/* Pointer to the session						 * PPPoX socket */	struct sock		*tunnel_sock;	/* Pointer to the tunnel UDP						 * socket */	struct pppol2tp_addr	tunnel_addr;	/* Description of tunnel */	struct pppol2tp_tunnel	*tunnel;	/* back pointer to tunnel						 * context */	char			name[20];	/* "sess xxxxx/yyyyy", where						 * x=tunnel_id, y=session_id */	int			mtu;	int			mru;	int			flags;		/* accessed by PPPIOCGFLAGS.						 * Unused. */	unsigned		recv_seq:1;	/* expect receive packets with						 * sequence numbers? */	unsigned		send_seq:1;	/* send packets with sequence						 * numbers? */	unsigned		lns_mode:1;	/* behave as LNS? LAC enables						 * sequence numbers under						 * control of LNS. */	int			debug;		/* bitmask of debug message						 * categories */	int			reorder_timeout; /* configured reorder timeout						  * (in jiffies) */	u16			nr;		/* session NR state (receive) */	u16			ns;		/* session NR state (send) */	struct sk_buff_head	reorder_q;	/* receive reorder queue */	struct pppol2tp_ioc_stats stats;	struct hlist_node	hlist;		/* Hash list node */};/* The sk_user_data field of the tunnel's UDP socket. It contains info to track * all the associated sessions so incoming packets can be sorted out */struct pppol2tp_tunnel{	int			magic;		/* Should be L2TP_TUNNEL_MAGIC */	rwlock_t		hlist_lock;	/* protect session_hlist */	struct hlist_head	session_hlist[PPPOL2TP_HASH_SIZE];						/* hashed list of sessions,						 * hashed by id */	int			debug;		/* bitmask of debug message						 * categories */	char			name[12];	/* "tunl xxxxx" */	struct pppol2tp_ioc_stats stats;	void (*old_sk_destruct)(struct sock *);	struct sock		*sock;		/* Parent socket */	struct list_head	list;		/* Keep a list of all open						 * prepared sockets */	atomic_t		ref_count;};/* Private data stored for received packets in the skb. */struct pppol2tp_skb_cb {	u16			ns;	u16			nr;	u16			has_seq;	u16			length;	unsigned long		expires;};#define PPPOL2TP_SKB_CB(skb)	((struct pppol2tp_skb_cb *) &skb->cb[sizeof(struct inet_skb_parm)])static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb);static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel);static atomic_t pppol2tp_tunnel_count;static atomic_t pppol2tp_session_count;static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL };static struct proto_ops pppol2tp_ops;static LIST_HEAD(pppol2tp_tunnel_list);static DEFINE_RWLOCK(pppol2tp_tunnel_list_lock);/* Helpers to obtain tunnel/session contexts from sockets. */static inline struct pppol2tp_session *pppol2tp_sock_to_session(struct sock *sk){	struct pppol2tp_session *session;	if (sk == NULL)		return NULL;	session = (struct pppol2tp_session *)(sk->sk_user_data);	if (session == NULL)		return NULL;	BUG_ON(session->magic != L2TP_SESSION_MAGIC);	return session;}static inline struct pppol2tp_tunnel *pppol2tp_sock_to_tunnel(struct sock *sk){	struct pppol2tp_tunnel *tunnel;	if (sk == NULL)		return NULL;	tunnel = (struct pppol2tp_tunnel *)(sk->sk_user_data);	if (tunnel == NULL)		return NULL;	BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC);	return tunnel;}/* Tunnel reference counts. Incremented per session that is added to * the tunnel. */static inline void pppol2tp_tunnel_inc_refcount(struct pppol2tp_tunnel *tunnel){	atomic_inc(&tunnel->ref_count);}static inline void pppol2tp_tunnel_dec_refcount(struct pppol2tp_tunnel *tunnel){	if (atomic_dec_and_test(&tunnel->ref_count))		pppol2tp_tunnel_free(tunnel);}/* Session hash list. * The session_id SHOULD be random according to RFC2661, but several * L2TP implementations (Cisco and Microsoft) use incrementing * session_ids.  So we do a real hash on the session_id, rather than a * simple bitmask. */static inline struct hlist_head *pppol2tp_session_id_hash(struct pppol2tp_tunnel *tunnel, u16 session_id){	unsigned long hash_val = (unsigned long) session_id;	return &tunnel->session_hlist[hash_long(hash_val, PPPOL2TP_HASH_BITS)];}/* Lookup a session by id */static struct pppol2tp_session *pppol2tp_session_find(struct pppol2tp_tunnel *tunnel, u16 session_id){	struct hlist_head *session_list =		pppol2tp_session_id_hash(tunnel, session_id);	struct pppol2tp_session *session;	struct hlist_node *walk;	read_lock(&tunnel->hlist_lock);	hlist_for_each_entry(session, walk, session_list, hlist) {		if (session->tunnel_addr.s_session == session_id) {			read_unlock(&tunnel->hlist_lock);			return session;		}	}	read_unlock(&tunnel->hlist_lock);	return NULL;}/* Lookup a tunnel by id */static struct pppol2tp_tunnel *pppol2tp_tunnel_find(u16 tunnel_id){	struct pppol2tp_tunnel *tunnel = NULL;	read_lock(&pppol2tp_tunnel_list_lock);	list_for_each_entry(tunnel, &pppol2tp_tunnel_list, list) {		if (tunnel->stats.tunnel_id == tunnel_id) {			read_unlock(&pppol2tp_tunnel_list_lock);			return tunnel;		}	}	read_unlock(&pppol2tp_tunnel_list_lock);	return NULL;}/***************************************************************************** * Receive data handling *****************************************************************************//* Queue a skb in order. We come here only if the skb has an L2TP sequence * number. */static void pppol2tp_recv_queue_skb(struct pppol2tp_session *session, struct sk_buff *skb){	struct sk_buff *skbp;	u16 ns = PPPOL2TP_SKB_CB(skb)->ns;	spin_lock(&session->reorder_q.lock);	skb_queue_walk(&session->reorder_q, skbp) {		if (PPPOL2TP_SKB_CB(skbp)->ns > ns) {			__skb_insert(skb, skbp->prev, skbp, &session->reorder_q);			PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,			       "%s: pkt %hu, inserted before %hu, reorder_q len=%d\n",			       session->name, ns, PPPOL2TP_SKB_CB(skbp)->ns,			       skb_queue_len(&session->reorder_q));			session->stats.rx_oos_packets++;			goto out;		}	}	__skb_queue_tail(&session->reorder_q, skb);out:	spin_unlock(&session->reorder_q.lock);}/* Dequeue a single skb. */static void pppol2tp_recv_dequeue_skb(struct pppol2tp_session *session, struct sk_buff *skb){	struct pppol2tp_tunnel *tunnel = session->tunnel;	int length = PPPOL2TP_SKB_CB(skb)->length;	struct sock *session_sock = NULL;	/* We're about to requeue the skb, so unlink it and return resources	 * to its current owner (a socket receive buffer).	 */	skb_unlink(skb, &session->reorder_q);	skb_orphan(skb);	tunnel->stats.rx_packets++;	tunnel->stats.rx_bytes += length;	session->stats.rx_packets++;	session->stats.rx_bytes += length;	if (PPPOL2TP_SKB_CB(skb)->has_seq) {		/* Bump our Nr */		session->nr++;		PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,		       "%s: updated nr to %hu\n", session->name, session->nr);	}	/* If the socket is bound, send it in to PPP's input queue. Otherwise	 * queue it on the session socket.	 */	session_sock = session->sock;	if (session_sock->sk_state & PPPOX_BOUND) {		struct pppox_sock *po;		PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG,		       "%s: recv %d byte data frame, passing to ppp\n",		       session->name, length);		/* We need to forget all info related to the L2TP packet		 * gathered in the skb as we are going to reuse the same		 * skb for the inner packet.		 * Namely we need to:		 * - reset xfrm (IPSec) information as it applies to		 *   the outer L2TP packet and not to the inner one		 * - release the dst to force a route lookup on the inner		 *   IP packet since skb->dst currently points to the dst		 *   of the UDP tunnel		 * - reset netfilter information as it doesn't apply		 *   to the inner packet either		 */		secpath_reset(skb);		dst_release(skb->dst);		skb->dst = NULL;		nf_reset(skb);		po = pppox_sk(session_sock);		ppp_input(&po->chan, skb);	} else {		PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_INFO,		       "%s: socket not bound\n", session->name);		/* Not bound. Nothing we can do, so discard. */		session->stats.rx_errors++;		kfree_skb(skb);	}	sock_put(session->sock);}/* Dequeue skbs from the session's reorder_q, subject to packet order. * Skbs that have been in the queue for too long are simply discarded. */static void pppol2tp_recv_dequeue(struct pppol2tp_session *session){	struct sk_buff *skb;	struct sk_buff *tmp;	/* If the pkt at the head of the queue has the nr that we	 * expect to send up next, dequeue it and any other	 * in-sequence packets behind it.	 */	spin_lock(&session->reorder_q.lock);	skb_queue_walk_safe(&session->reorder_q, skb, tmp) {		if (time_after(jiffies, PPPOL2TP_SKB_CB(skb)->expires)) {			session->stats.rx_seq_discards++;			session->stats.rx_errors++;			PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,			       "%s: oos pkt %hu len %d discarded (too old), "			       "waiting for %hu, reorder_q_len=%d\n",			       session->name, PPPOL2TP_SKB_CB(skb)->ns,			       PPPOL2TP_SKB_CB(skb)->length, session->nr,			       skb_queue_len(&session->reorder_q));			__skb_unlink(skb, &session->reorder_q);			kfree_skb(skb);			continue;		}		if (PPPOL2TP_SKB_CB(skb)->has_seq) {			if (PPPOL2TP_SKB_CB(skb)->ns != session->nr) {				PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,				       "%s: holding oos pkt %hu len %d, "				       "waiting for %hu, reorder_q_len=%d\n",				       session->name, PPPOL2TP_SKB_CB(skb)->ns,				       PPPOL2TP_SKB_CB(skb)->length, session->nr,				       skb_queue_len(&session->reorder_q));				goto out;			}		}		spin_unlock(&session->reorder_q.lock);		pppol2tp_recv_dequeue_skb(session, skb);		spin_lock(&session->reorder_q.lock);	}out:	spin_unlock(&session->reorder_q.lock);}/* Internal receive frame. Do the real work of receiving an L2TP data frame * here. The skb is not on a list when we get here. * Returns 0 if the packet was a data packet and was successfully passed on. * Returns 1 if the packet was not a good data packet and could not be * forwarded.  All such packets are passed up to userspace to deal with. */static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb){	struct pppol2tp_session *session = NULL;	struct pppol2tp_tunnel *tunnel;	unsigned char *ptr, *optr;	u16 hdrflags;	u16 tunnel_id, session_id;	int length;	int offset;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?