Skip to content

Commit

Permalink
gtp: Allow to create GTP device without FDs
Browse files Browse the repository at this point in the history
Currently, when the user wants to create GTP device, he has to
provide file handles to the sockets created in userspace (IFLA_GTP_FD0,
IFLA_GTP_FD1). This behaviour is not ideal, considering the option of
adding support for GTP device creation through ip link. Ip link
application is not a good place to create such sockets.

This patch allows to create GTP device without providing
IFLA_GTP_FD0 and IFLA_GTP_FD1 arguments. If the user sets
IFLA_GTP_CREATE_SOCKETS attribute, then GTP module takes care
of creating UDP sockets by itself. Sockets are created with the
commonly known UDP ports used for GTP protocol (GTP0_PORT and
GTP1U_PORT). In this case we don't have to provide encap_destroy
because no extra deinitialization is needed, everything is covered
by udp_tunnel_sock_release.

Note: GTP instance created with only this change applied, does
not handle GTP Echo Requests. This is implemented in the following
patch.

Signed-off-by: Wojciech Drewek <wojciech.drewek@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
  • Loading branch information
Wojciech Drewek authored and Tony Nguyen committed Mar 11, 2022
1 parent 59d5923 commit b20dc3c
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 17 deletions.
101 changes: 84 additions & 17 deletions drivers/net/gtp.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ struct gtp_dev {

struct sock *sk0;
struct sock *sk1u;
u8 sk_created;

struct net_device *dev;
struct net *net;

unsigned int role;
unsigned int hash_size;
Expand Down Expand Up @@ -320,8 +322,16 @@ static void gtp_encap_disable_sock(struct sock *sk)

static void gtp_encap_disable(struct gtp_dev *gtp)
{
gtp_encap_disable_sock(gtp->sk0);
gtp_encap_disable_sock(gtp->sk1u);
if (gtp->sk_created) {
udp_tunnel_sock_release(gtp->sk0->sk_socket);
udp_tunnel_sock_release(gtp->sk1u->sk_socket);
gtp->sk_created = false;
gtp->sk0 = NULL;
gtp->sk1u = NULL;
} else {
gtp_encap_disable_sock(gtp->sk0);
gtp_encap_disable_sock(gtp->sk1u);
}
}

/* UDP encapsulation receive handler. See net/ipv4/udp.c.
Expand Down Expand Up @@ -656,17 +666,69 @@ static void gtp_destructor(struct net_device *dev)
kfree(gtp->tid_hash);
}

static struct sock *gtp_create_sock(int type, struct gtp_dev *gtp)
{
struct udp_tunnel_sock_cfg tuncfg = {};
struct udp_port_cfg udp_conf = {
.local_ip.s_addr = htonl(INADDR_ANY),
.family = AF_INET,
};
struct net *net = gtp->net;
struct socket *sock;
int err;

if (type == UDP_ENCAP_GTP0)
udp_conf.local_udp_port = htons(GTP0_PORT);
else if (type == UDP_ENCAP_GTP1U)
udp_conf.local_udp_port = htons(GTP1U_PORT);
else
return ERR_PTR(-EINVAL);

err = udp_sock_create(net, &udp_conf, &sock);
if (err)
return ERR_PTR(err);

tuncfg.sk_user_data = gtp;
tuncfg.encap_type = type;
tuncfg.encap_rcv = gtp_encap_recv;
tuncfg.encap_destroy = NULL;

setup_udp_tunnel_sock(net, sock, &tuncfg);

return sock->sk;
}

static int gtp_create_sockets(struct gtp_dev *gtp, struct nlattr *data[])
{
struct sock *sk1u = NULL;
struct sock *sk0 = NULL;

sk0 = gtp_create_sock(UDP_ENCAP_GTP0, gtp);
if (IS_ERR(sk0))
return PTR_ERR(sk0);

sk1u = gtp_create_sock(UDP_ENCAP_GTP1U, gtp);
if (IS_ERR(sk1u)) {
udp_tunnel_sock_release(sk0->sk_socket);
return PTR_ERR(sk1u);
}

gtp->sk_created = true;
gtp->sk0 = sk0;
gtp->sk1u = sk1u;

return 0;
}

static int gtp_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
unsigned int role = GTP_ROLE_GGSN;
struct gtp_dev *gtp;
struct gtp_net *gn;
int hashsize, err;

if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1])
return -EINVAL;

gtp = netdev_priv(dev);

if (!data[IFLA_GTP_PDP_HASHSIZE]) {
Expand All @@ -677,11 +739,23 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
hashsize = 1024;
}

if (data[IFLA_GTP_ROLE]) {
role = nla_get_u32(data[IFLA_GTP_ROLE]);
if (role > GTP_ROLE_SGSN)
return -EINVAL;
}
gtp->role = role;

gtp->net = src_net;

err = gtp_hashtable_new(gtp, hashsize);
if (err < 0)
return err;

err = gtp_encap_enable(gtp, data);
if (data[IFLA_GTP_CREATE_SOCKETS])
err = gtp_create_sockets(gtp, data);
else
err = gtp_encap_enable(gtp, data);
if (err < 0)
goto out_hashtable;

Expand Down Expand Up @@ -726,6 +800,7 @@ static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = {
[IFLA_GTP_FD1] = { .type = NLA_U32 },
[IFLA_GTP_PDP_HASHSIZE] = { .type = NLA_U32 },
[IFLA_GTP_ROLE] = { .type = NLA_U32 },
[IFLA_GTP_CREATE_SOCKETS] = { .type = NLA_U8 },
};

static int gtp_validate(struct nlattr *tb[], struct nlattr *data[],
Expand Down Expand Up @@ -848,7 +923,9 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
{
struct sock *sk1u = NULL;
struct sock *sk0 = NULL;
unsigned int role = GTP_ROLE_GGSN;

if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1])
return -EINVAL;

if (data[IFLA_GTP_FD0]) {
u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]);
Expand All @@ -868,18 +945,8 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
}
}

if (data[IFLA_GTP_ROLE]) {
role = nla_get_u32(data[IFLA_GTP_ROLE]);
if (role > GTP_ROLE_SGSN) {
gtp_encap_disable_sock(sk0);
gtp_encap_disable_sock(sk1u);
return -EINVAL;
}
}

gtp->sk0 = sk0;
gtp->sk1u = sk1u;
gtp->role = role;

return 0;
}
Expand Down
1 change: 1 addition & 0 deletions include/uapi/linux/if_link.h
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,7 @@ enum {
IFLA_GTP_FD1,
IFLA_GTP_PDP_HASHSIZE,
IFLA_GTP_ROLE,
IFLA_GTP_CREATE_SOCKETS,
__IFLA_GTP_MAX,
};
#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
Expand Down

0 comments on commit b20dc3c

Please sign in to comment.