Skip to content

Commit

Permalink
l2tp: Add netlink control API for L2TP
Browse files Browse the repository at this point in the history
In L2TPv3, we need to create/delete/modify/query L2TP tunnel and
session contexts. The number of parameters is significant. So let's
use netlink. Userspace uses this API to control L2TP tunnel/session
contexts in the kernel.

The previous pppol2tp driver was managed using [gs]etsockopt(). This
API is retained for backwards compatibility. Unlike L2TPv2 which
carries only PPP frames, L2TPv3 can carry raw ethernet frames or other
frame types and these do not always have an associated socket
family. Therefore, we need a way to use L2TP sessions that doesn't
require a socket type for each supported frame type. Hence netlink is
used.

Signed-off-by: James Chapman <jchapman@katalix.com>
Reviewed-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
j-c-h authored and davem330 committed Apr 3, 2010
1 parent f408e0c commit 309795f
Show file tree
Hide file tree
Showing 6 changed files with 1,169 additions and 44 deletions.
125 changes: 125 additions & 0 deletions include/linux/l2tp.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#ifdef __KERNEL__
#include <linux/socket.h>
#include <linux/in.h>
#else
#include <netinet/in.h>
#endif

#define IPPROTO_L2TP 115
Expand All @@ -21,6 +23,7 @@
* @l2tp_addr: protocol specific address information
* @l2tp_conn_id: connection id of tunnel
*/
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_l2tpip {
/* The first fields must match struct sockaddr_in */
sa_family_t l2tp_family; /* AF_INET */
Expand All @@ -35,4 +38,126 @@ struct sockaddr_l2tpip {
sizeof(__u32)];
};

/*****************************************************************************
* NETLINK_GENERIC netlink family.
*****************************************************************************/

/*
* Commands.
* Valid TLVs of each command are:-
* TUNNEL_CREATE - CONN_ID, pw_type, netns, ifname, ipinfo, udpinfo, udpcsum, vlanid
* TUNNEL_DELETE - CONN_ID
* TUNNEL_MODIFY - CONN_ID, udpcsum
* TUNNEL_GETSTATS - CONN_ID, (stats)
* TUNNEL_GET - CONN_ID, (...)
* SESSION_CREATE - SESSION_ID, PW_TYPE, offset, data_seq, cookie, peer_cookie, offset, l2spec
* SESSION_DELETE - SESSION_ID
* SESSION_MODIFY - SESSION_ID, data_seq
* SESSION_GET - SESSION_ID, (...)
* SESSION_GETSTATS - SESSION_ID, (stats)
*
*/
enum {
L2TP_CMD_NOOP,
L2TP_CMD_TUNNEL_CREATE,
L2TP_CMD_TUNNEL_DELETE,
L2TP_CMD_TUNNEL_MODIFY,
L2TP_CMD_TUNNEL_GET,
L2TP_CMD_SESSION_CREATE,
L2TP_CMD_SESSION_DELETE,
L2TP_CMD_SESSION_MODIFY,
L2TP_CMD_SESSION_GET,
__L2TP_CMD_MAX,
};

#define L2TP_CMD_MAX (__L2TP_CMD_MAX - 1)

/*
* ATTR types defined for L2TP
*/
enum {
L2TP_ATTR_NONE, /* no data */
L2TP_ATTR_PW_TYPE, /* u16, enum l2tp_pwtype */
L2TP_ATTR_ENCAP_TYPE, /* u16, enum l2tp_encap_type */
L2TP_ATTR_OFFSET, /* u16 */
L2TP_ATTR_DATA_SEQ, /* u16 */
L2TP_ATTR_L2SPEC_TYPE, /* u8, enum l2tp_l2spec_type */
L2TP_ATTR_L2SPEC_LEN, /* u8, enum l2tp_l2spec_type */
L2TP_ATTR_PROTO_VERSION, /* u8 */
L2TP_ATTR_IFNAME, /* string */
L2TP_ATTR_CONN_ID, /* u32 */
L2TP_ATTR_PEER_CONN_ID, /* u32 */
L2TP_ATTR_SESSION_ID, /* u32 */
L2TP_ATTR_PEER_SESSION_ID, /* u32 */
L2TP_ATTR_UDP_CSUM, /* u8 */
L2TP_ATTR_VLAN_ID, /* u16 */
L2TP_ATTR_COOKIE, /* 0, 4 or 8 bytes */
L2TP_ATTR_PEER_COOKIE, /* 0, 4 or 8 bytes */
L2TP_ATTR_DEBUG, /* u32 */
L2TP_ATTR_RECV_SEQ, /* u8 */
L2TP_ATTR_SEND_SEQ, /* u8 */
L2TP_ATTR_LNS_MODE, /* u8 */
L2TP_ATTR_USING_IPSEC, /* u8 */
L2TP_ATTR_RECV_TIMEOUT, /* msec */
L2TP_ATTR_FD, /* int */
L2TP_ATTR_IP_SADDR, /* u32 */
L2TP_ATTR_IP_DADDR, /* u32 */
L2TP_ATTR_UDP_SPORT, /* u16 */
L2TP_ATTR_UDP_DPORT, /* u16 */
L2TP_ATTR_MTU, /* u16 */
L2TP_ATTR_MRU, /* u16 */
L2TP_ATTR_STATS, /* nested */
__L2TP_ATTR_MAX,
};

#define L2TP_ATTR_MAX (__L2TP_ATTR_MAX - 1)

/* Nested in L2TP_ATTR_STATS */
enum {
L2TP_ATTR_STATS_NONE, /* no data */
L2TP_ATTR_TX_PACKETS, /* u64 */
L2TP_ATTR_TX_BYTES, /* u64 */
L2TP_ATTR_TX_ERRORS, /* u64 */
L2TP_ATTR_RX_PACKETS, /* u64 */
L2TP_ATTR_RX_BYTES, /* u64 */
L2TP_ATTR_RX_SEQ_DISCARDS, /* u64 */
L2TP_ATTR_RX_OOS_PACKETS, /* u64 */
L2TP_ATTR_RX_ERRORS, /* u64 */
__L2TP_ATTR_STATS_MAX,
};

#define L2TP_ATTR_STATS_MAX (__L2TP_ATTR_STATS_MAX - 1)

enum l2tp_pwtype {
L2TP_PWTYPE_NONE = 0x0000,
L2TP_PWTYPE_ETH_VLAN = 0x0004,
L2TP_PWTYPE_ETH = 0x0005,
L2TP_PWTYPE_PPP = 0x0007,
L2TP_PWTYPE_PPP_AC = 0x0008,
L2TP_PWTYPE_IP = 0x000b,
__L2TP_PWTYPE_MAX
};

enum l2tp_l2spec_type {
L2TP_L2SPECTYPE_NONE,
L2TP_L2SPECTYPE_DEFAULT,
};

enum l2tp_encap_type {
L2TP_ENCAPTYPE_UDP,
L2TP_ENCAPTYPE_IP,
};

enum l2tp_seqmode {
L2TP_SEQ_NONE = 0,
L2TP_SEQ_IP = 1,
L2TP_SEQ_ALL = 2,
};

/*
* NETLINK_GENERIC related info
*/
#define L2TP_GENL_NAME "l2tp"
#define L2TP_GENL_VERSION 0x1

#endif
1 change: 1 addition & 0 deletions net/l2tp/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ obj-$(CONFIG_L2TP) += l2tp_core.o
# Build l2tp as modules if L2TP is M
obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_PPPOL2TP)) += l2tp_ppp.o
obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP)) += l2tp_ip.o
obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_V3)) += l2tp_netlink.o
61 changes: 60 additions & 1 deletion net/l2tp/l2tp_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include <net/dst.h>
#include <net/ip.h>
#include <net/udp.h>
#include <net/inet_common.h>
#include <net/xfrm.h>
#include <net/protocol.h>

Expand Down Expand Up @@ -214,6 +215,32 @@ struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth)
}
EXPORT_SYMBOL_GPL(l2tp_session_find_nth);

/* Lookup a session by interface name.
* This is very inefficient but is only used by management interfaces.
*/
struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname)
{
struct l2tp_net *pn = l2tp_pernet(net);
int hash;
struct hlist_node *walk;
struct l2tp_session *session;

read_lock_bh(&pn->l2tp_session_hlist_lock);
for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++) {
hlist_for_each_entry(session, walk, &pn->l2tp_session_hlist[hash], global_hlist) {
if (!strcmp(session->ifname, ifname)) {
read_unlock_bh(&pn->l2tp_session_hlist_lock);
return session;
}
}
}

read_unlock_bh(&pn->l2tp_session_hlist_lock);

return NULL;
}
EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname);

/* Lookup a tunnel by id
*/
struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id)
Expand Down Expand Up @@ -758,7 +785,7 @@ int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,

/* Find the session context */
session = l2tp_session_find(tunnel->l2tp_net, tunnel, session_id);
if (!session) {
if (!session || !session->recv_skb) {
/* Not found? Pass to userspace to deal with */
PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_INFO,
"%s: no session found (%u/%u). Passing up.\n",
Expand Down Expand Up @@ -1305,6 +1332,23 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
}
EXPORT_SYMBOL_GPL(l2tp_tunnel_create);

/* This function is used by the netlink TUNNEL_DELETE command.
*/
int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel)
{
int err = 0;

/* Force the tunnel socket to close. This will eventually
* cause the tunnel to be deleted via the normal socket close
* mechanisms when userspace closes the tunnel socket.
*/
if ((tunnel->sock != NULL) && (tunnel->sock->sk_socket != NULL))
err = inet_shutdown(tunnel->sock->sk_socket, 2);

return err;
}
EXPORT_SYMBOL_GPL(l2tp_tunnel_delete);

/* Really kill the session.
*/
void l2tp_session_free(struct l2tp_session *session)
Expand Down Expand Up @@ -1349,6 +1393,21 @@ void l2tp_session_free(struct l2tp_session *session)
}
EXPORT_SYMBOL_GPL(l2tp_session_free);

/* This function is used by the netlink SESSION_DELETE command and by
pseudowire modules.
*/
int l2tp_session_delete(struct l2tp_session *session)
{
if (session->session_close != NULL)
(*session->session_close)(session);

l2tp_session_dec_refcount(session);

return 0;
}
EXPORT_SYMBOL_GPL(l2tp_session_delete);


/* We come here whenever a session's send_seq, cookie_len or
* l2specific_len parameters are set.
*/
Expand Down
34 changes: 14 additions & 20 deletions net/l2tp/l2tp_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,6 @@ enum {
L2TP_MSG_DATA = (1 << 3), /* data packets */
};

enum l2tp_pwtype {
L2TP_PWTYPE_NONE = 0x0000,
L2TP_PWTYPE_ETH_VLAN = 0x0004,
L2TP_PWTYPE_ETH = 0x0005,
L2TP_PWTYPE_PPP = 0x0007,
L2TP_PWTYPE_PPP_AC = 0x0008,
L2TP_PWTYPE_IP = 0x000b,
__L2TP_PWTYPE_MAX
};

enum l2tp_l2spec_type {
L2TP_L2SPECTYPE_NONE,
L2TP_L2SPECTYPE_DEFAULT,
};

enum l2tp_encap_type {
L2TP_ENCAPTYPE_UDP,
L2TP_ENCAPTYPE_IP,
};

struct sk_buff;

struct l2tp_stats {
Expand Down Expand Up @@ -87,6 +67,7 @@ struct l2tp_session_cfg {
* control of LNS. */
int debug; /* bitmask of debug message
* categories */
u16 vlan_id; /* VLAN pseudowire only */
u16 offset; /* offset to payload */
u16 l2specific_len; /* Layer 2 specific length */
u16 l2specific_type; /* Layer 2 specific type */
Expand All @@ -98,6 +79,7 @@ struct l2tp_session_cfg {
* (in jiffies) */
int mtu;
int mru;
char *ifname;
};

struct l2tp_session {
Expand All @@ -124,6 +106,7 @@ struct l2tp_session {
atomic_t ref_count;

char name[32]; /* for logging */
char ifname[IFNAMSIZ];
unsigned data_seq:2; /* data sequencing level
* 0 => none, 1 => IP only,
* 2 => all
Expand Down Expand Up @@ -192,6 +175,11 @@ struct l2tp_tunnel {
uint8_t priv[0]; /* private data */
};

struct l2tp_nl_cmd_ops {
int (*session_create)(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg);
int (*session_delete)(struct l2tp_session *session);
};

static inline void *l2tp_tunnel_priv(struct l2tp_tunnel *tunnel)
{
return &tunnel->priv[0];
Expand Down Expand Up @@ -224,11 +212,14 @@ static inline struct l2tp_tunnel *l2tp_sock_to_tunnel(struct sock *sk)

extern struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id);
extern struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth);
extern struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname);
extern struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id);
extern struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth);

extern int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp);
extern int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel);
extern struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg);
extern int l2tp_session_delete(struct l2tp_session *session);
extern void l2tp_tunnel_free(struct l2tp_tunnel *tunnel);
extern void l2tp_session_free(struct l2tp_session *session);
extern void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, unsigned char *ptr, unsigned char *optr, u16 hdrflags, int length, int (*payload_hook)(struct sk_buff *skb));
Expand All @@ -241,6 +232,9 @@ extern void l2tp_tunnel_destruct(struct sock *sk);
extern void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel);
extern void l2tp_session_set_header_len(struct l2tp_session *session, int version);

extern int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops);
extern void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type);

/* Tunnel reference counts. Incremented per session that is added to
* the tunnel.
*/
Expand Down
Loading

0 comments on commit 309795f

Please sign in to comment.