* [PATCH net-next 1/4] gtp: move TEID hash to per socket structure
2017-03-14 11:25 [PATCH net-next 0/4] gtp: support multiple APN's per GTP endpoint Andreas Schultz
@ 2017-03-14 11:25 ` Andreas Schultz
2017-03-14 11:33 ` Pablo Neira Ayuso
2017-03-14 11:25 ` [PATCH net-next 2/4] gtp: add genl cmd to enable GTP encapsulation on UDP socket Andreas Schultz
` (3 subsequent siblings)
4 siblings, 1 reply; 15+ messages in thread
From: Andreas Schultz @ 2017-03-14 11:25 UTC (permalink / raw)
To: Harald Welte, Pablo Neira Ayuso; +Cc: osmocom-net-gprs, netdev, Lionel Gauthier
Untangele the TEID information from the network device and move
it into a per socket structure.
The removeal of the TEID form the netdevice also means that the
gtp genl API for retriving tunnel information and removing tunnels
needs to be adjusted.
Before this change it was possible to change a GTP tunnel using
the gtp netdevice id and the teid. The teid is no longer unique
per gtp netdevice. So after this change it has to be either the
netdevice and MS IP or the GTP socket and teid.
Fortunatly, libgtpnl has always populated the Link Id, TEID,
GSN Peer IP and MS IP. So, the library interface has ensured that
all information that is mandatory after this change is guranteed
to be present. The only project that doesn't use libgtpnl (OpenAir-CN)
is also populating all of those values.
Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
drivers/net/gtp.c | 145 +++++++++++++++++++++++++++++-------------------------
1 file changed, 78 insertions(+), 67 deletions(-)
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 3e1854f..66616f7 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -75,10 +75,15 @@ struct gtp_dev {
struct net_device *dev;
unsigned int hash_size;
- struct hlist_head *tid_hash;
struct hlist_head *addr_hash;
};
+/* One instance of the GTP socket. */
+struct gtp_sock {
+ unsigned int hash_size;
+ struct hlist_head tid_hash[];
+};
+
static unsigned int gtp_net_id __read_mostly;
struct gtp_net {
@@ -106,12 +111,12 @@ static inline u32 ipv4_hashfn(__be32 ip)
}
/* Resolve a PDP context structure based on the 64bit TID. */
-static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid)
+static struct pdp_ctx *gtp0_pdp_find(struct gtp_sock *gsk, u64 tid)
{
struct hlist_head *head;
struct pdp_ctx *pdp;
- head = >p->tid_hash[gtp0_hashfn(tid) % gtp->hash_size];
+ head = &gsk->tid_hash[gtp0_hashfn(tid) % gsk->hash_size];
hlist_for_each_entry_rcu(pdp, head, hlist_tid) {
if (pdp->gtp_version == GTP_V0 &&
@@ -122,12 +127,12 @@ static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid)
}
/* Resolve a PDP context structure based on the 32bit TEI. */
-static struct pdp_ctx *gtp1_pdp_find(struct gtp_dev *gtp, u32 tid)
+static struct pdp_ctx *gtp1_pdp_find(struct gtp_sock *gsk, u32 tid)
{
struct hlist_head *head;
struct pdp_ctx *pdp;
- head = >p->tid_hash[gtp1u_hashfn(tid) % gtp->hash_size];
+ head = &gsk->tid_hash[gtp1u_hashfn(tid) % gsk->hash_size];
hlist_for_each_entry_rcu(pdp, head, hlist_tid) {
if (pdp->gtp_version == GTP_V1 &&
@@ -215,7 +220,7 @@ static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen
}
/* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */
-static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
+static int gtp0_udp_encap_recv(struct gtp_sock *gsk, struct sk_buff *skb)
{
unsigned int hdrlen = sizeof(struct udphdr) +
sizeof(struct gtp0_header);
@@ -233,16 +238,16 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
if (gtp0->type != GTP_TPDU)
return 1;
- pctx = gtp0_pdp_find(gtp, be64_to_cpu(gtp0->tid));
+ pctx = gtp0_pdp_find(gsk, be64_to_cpu(gtp0->tid));
if (!pctx) {
- netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
+ pr_debug("No PDP ctx to decap skb=%p\n", skb);
return 1;
}
return gtp_rx(pctx, skb, hdrlen);
}
-static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
+static int gtp1u_udp_encap_recv(struct gtp_sock *gsk, struct sk_buff *skb)
{
unsigned int hdrlen = sizeof(struct udphdr) +
sizeof(struct gtp1_header);
@@ -275,9 +280,9 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
- pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid));
+ pctx = gtp1_pdp_find(gsk, ntohl(gtp1->tid));
if (!pctx) {
- netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
+ pr_debug("No PDP ctx to decap skb=%p\n", skb);
return 1;
}
@@ -286,13 +291,21 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
static void gtp_encap_destroy(struct sock *sk)
{
- struct gtp_dev *gtp;
+ struct gtp_sock *gsk;
+ struct pdp_ctx *pctx;
+ int i;
- gtp = rcu_dereference_sk_user_data(sk);
- if (gtp) {
+ gsk = rcu_dereference_sk_user_data(sk);
+ if (gsk) {
udp_sk(sk)->encap_type = 0;
rcu_assign_sk_user_data(sk, NULL);
- sock_put(sk);
+
+ for (i = 0; i < gsk->hash_size; i++)
+ hlist_for_each_entry_rcu(pctx, &gsk->tid_hash[i], hlist_tid)
+ pdp_context_delete(pctx);
+
+ synchronize_rcu();
+ kfree(gsk);
}
}
@@ -315,23 +328,23 @@ static void gtp_encap_disable(struct gtp_dev *gtp)
*/
static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
{
- struct gtp_dev *gtp;
+ struct gtp_sock *gsk;
int ret = 0;
- gtp = rcu_dereference_sk_user_data(sk);
- if (!gtp)
+ gsk = rcu_dereference_sk_user_data(sk);
+ if (!gsk)
return 1;
- netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk);
+ pr_debug("encap_recv sk=%p\n", sk);
switch (udp_sk(sk)->encap_type) {
case UDP_ENCAP_GTP0:
- netdev_dbg(gtp->dev, "received GTP0 packet\n");
- ret = gtp0_udp_encap_recv(gtp, skb);
+ pr_debug("received GTP0 packet\n");
+ ret = gtp0_udp_encap_recv(gsk, skb);
break;
case UDP_ENCAP_GTP1U:
- netdev_dbg(gtp->dev, "received GTP1U packet\n");
- ret = gtp1u_udp_encap_recv(gtp, skb);
+ pr_debug("received GTP1U packet\n");
+ ret = gtp1u_udp_encap_recv(gsk, skb);
break;
default:
ret = -1; /* Shouldn't happen. */
@@ -339,12 +352,12 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
switch (ret) {
case 1:
- netdev_dbg(gtp->dev, "pass up to the process\n");
+ pr_debug("pass up to the process\n");
break;
case 0:
break;
case -1:
- netdev_dbg(gtp->dev, "GTP packet has been dropped\n");
+ pr_debug("GTP packet has been dropped\n");
kfree_skb(skb);
ret = 0;
break;
@@ -624,7 +637,8 @@ static void gtp_link_setup(struct net_device *dev)
static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize);
static void gtp_hashtable_free(struct gtp_dev *gtp);
-static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]);
+static int gtp_encap_enable(struct gtp_dev *gtp, int hsize,
+ struct nlattr *data[]);
static int gtp_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
@@ -638,15 +652,15 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
gtp = netdev_priv(dev);
- err = gtp_encap_enable(gtp, data);
- if (err < 0)
- return err;
-
if (!data[IFLA_GTP_PDP_HASHSIZE])
hashsize = 1024;
else
hashsize = nla_get_u32(data[IFLA_GTP_PDP_HASHSIZE]);
+ err = gtp_encap_enable(gtp, hashsize, data);
+ if (err < 0)
+ return err;
+
err = gtp_hashtable_new(gtp, hashsize);
if (err < 0)
goto out_encap;
@@ -734,20 +748,12 @@ static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize)
if (gtp->addr_hash == NULL)
return -ENOMEM;
- gtp->tid_hash = kmalloc(sizeof(struct hlist_head) * hsize, GFP_KERNEL);
- if (gtp->tid_hash == NULL)
- goto err1;
-
gtp->hash_size = hsize;
- for (i = 0; i < hsize; i++) {
+ for (i = 0; i < hsize; i++)
INIT_HLIST_HEAD(>p->addr_hash[i]);
- INIT_HLIST_HEAD(>p->tid_hash[i]);
- }
+
return 0;
-err1:
- kfree(gtp->addr_hash);
- return -ENOMEM;
}
static void gtp_hashtable_free(struct gtp_dev *gtp)
@@ -756,21 +762,20 @@ static void gtp_hashtable_free(struct gtp_dev *gtp)
int i;
for (i = 0; i < gtp->hash_size; i++)
- hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid)
+ hlist_for_each_entry_rcu(pctx, >p->addr_hash[i], hlist_addr)
pdp_context_delete(pctx);
synchronize_rcu();
kfree(gtp->addr_hash);
- kfree(gtp->tid_hash);
}
-static struct sock *gtp_encap_enable_socket(int fd, int type,
- struct gtp_dev *gtp)
+static struct sock *gtp_encap_enable_socket(int fd, int type, int hsize)
{
struct udp_tunnel_sock_cfg tuncfg = {NULL};
+ struct gtp_sock *gsk;
struct socket *sock;
struct sock *sk;
- int err;
+ int err, i;
pr_debug("enable gtp on %d, %d\n", fd, type);
@@ -791,10 +796,20 @@ static struct sock *gtp_encap_enable_socket(int fd, int type,
goto out_sock;
}
+ gsk = kzalloc(sizeof(*gsk) + sizeof(struct hlist_head) * hsize, GFP_KERNEL);
+ if (!gsk) {
+ sk = ERR_PTR(-ENOMEM);
+ goto out_sock;
+ }
+
+ gsk->hash_size = hsize;
+ for (i = 0; i < hsize; i++)
+ INIT_HLIST_HEAD(&gsk->tid_hash[i]);
+
sk = sock->sk;
sock_hold(sk);
- tuncfg.sk_user_data = gtp;
+ tuncfg.sk_user_data = gsk;
tuncfg.encap_type = type;
tuncfg.encap_rcv = gtp_encap_recv;
tuncfg.encap_destroy = gtp_encap_destroy;
@@ -806,7 +821,8 @@ static struct sock *gtp_encap_enable_socket(int fd, int type,
return sk;
}
-static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
+static int gtp_encap_enable(struct gtp_dev *gtp, int hsize,
+ struct nlattr *data[])
{
struct sock *sk1u = NULL;
struct sock *sk0 = NULL;
@@ -814,7 +830,7 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
if (data[IFLA_GTP_FD0]) {
u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]);
- sk0 = gtp_encap_enable_socket(fd0, UDP_ENCAP_GTP0, gtp);
+ sk0 = gtp_encap_enable_socket(fd0, UDP_ENCAP_GTP0, hsize);
if (IS_ERR(sk0))
return PTR_ERR(sk0);
}
@@ -822,7 +838,7 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
if (data[IFLA_GTP_FD1]) {
u32 fd1 = nla_get_u32(data[IFLA_GTP_FD1]);
- sk1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, gtp);
+ sk1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, hsize);
if (IS_ERR(sk1u)) {
if (sk0)
gtp_encap_disable_sock(sk0);
@@ -895,9 +911,14 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk,
struct net_device *dev = gtp->dev;
u32 hash_ms, hash_tid = 0;
struct pdp_ctx *pctx;
+ struct gtp_sock *gsk;
bool found = false;
__be32 ms_addr;
+ gsk = rcu_dereference_sk_user_data(sk);
+ if (!gsk)
+ return -ENODEV;
+
ms_addr = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
hash_ms = ipv4_hashfn(ms_addr) % gtp->hash_size;
@@ -944,15 +965,15 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk,
* situation in which this doesn't unambiguosly identify the
* PDP context.
*/
- hash_tid = gtp0_hashfn(pctx->u.v0.tid) % gtp->hash_size;
+ hash_tid = gtp0_hashfn(pctx->u.v0.tid) % gsk->hash_size;
break;
case GTP_V1:
- hash_tid = gtp1u_hashfn(pctx->u.v1.i_tei) % gtp->hash_size;
+ hash_tid = gtp1u_hashfn(pctx->u.v1.i_tei) % gsk->hash_size;
break;
}
hlist_add_head_rcu(&pctx->hlist_addr, >p->addr_hash[hash_ms]);
- hlist_add_head_rcu(&pctx->hlist_tid, >p->tid_hash[hash_tid]);
+ hlist_add_head_rcu(&pctx->hlist_tid, &gsk->tid_hash[hash_tid]);
switch (pctx->gtp_version) {
case GTP_V0:
@@ -1048,24 +1069,14 @@ static struct pdp_ctx *gtp_find_pdp_by_link(struct net *net,
{
struct gtp_dev *gtp;
+ if (!nla[GTPA_MS_ADDRESS])
+ return ERR_PTR(-EINVAL);
+
gtp = gtp_find_dev(net, nla);
if (!gtp)
return ERR_PTR(-ENODEV);
- if (nla[GTPA_MS_ADDRESS]) {
- __be32 ip = nla_get_be32(nla[GTPA_MS_ADDRESS]);
-
- return ipv4_pdp_find(gtp, ip);
- } else if (nla[GTPA_VERSION]) {
- u32 gtp_version = nla_get_u32(nla[GTPA_VERSION]);
-
- if (gtp_version == GTP_V0 && nla[GTPA_TID])
- return gtp0_pdp_find(gtp, nla_get_u64(nla[GTPA_TID]));
- else if (gtp_version == GTP_V1 && nla[GTPA_I_TEI])
- return gtp1_pdp_find(gtp, nla_get_u32(nla[GTPA_I_TEI]));
- }
-
- return ERR_PTR(-EINVAL);
+ return ipv4_pdp_find(gtp, nla_get_be32(nla[GTPA_MS_ADDRESS]));
}
static struct pdp_ctx *gtp_find_pdp(struct net *net, struct nlattr *nla[])
@@ -1209,7 +1220,7 @@ static int gtp_genl_dump_pdp(struct sk_buff *skb,
last_gtp = NULL;
for (i = k; i < gtp->hash_size; i++) {
- hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) {
+ hlist_for_each_entry_rcu(pctx, >p->addr_hash[i], hlist_addr) {
if (tid && tid != pctx->u.tid)
continue;
else
--
2.10.2
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH net-next 2/4] gtp: add genl cmd to enable GTP encapsulation on UDP socket
2017-03-14 11:25 [PATCH net-next 0/4] gtp: support multiple APN's per GTP endpoint Andreas Schultz
2017-03-14 11:25 ` [PATCH net-next 1/4] gtp: move TEID hash to per socket structure Andreas Schultz
@ 2017-03-14 11:25 ` Andreas Schultz
2017-03-14 11:43 ` Pablo Neira Ayuso
2017-03-14 11:25 ` [PATCH net-next 3/4] gtp: add support to select a GTP socket during PDP context creation Andreas Schultz
` (2 subsequent siblings)
4 siblings, 1 reply; 15+ messages in thread
From: Andreas Schultz @ 2017-03-14 11:25 UTC (permalink / raw)
To: Harald Welte, Pablo Neira Ayuso; +Cc: osmocom-net-gprs, netdev, Lionel Gauthier
Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
drivers/net/gtp.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
include/uapi/linux/gtp.h | 4 ++++
2 files changed, 51 insertions(+)
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 66616f7..c4cf1b9 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -1244,6 +1244,45 @@ static int gtp_genl_dump_pdp(struct sk_buff *skb,
return skb->len;
}
+static int gtp_genl_enable_socket(struct sk_buff *skb, struct genl_info *info)
+{
+ u32 version, fd, hashsize;
+ struct sock *sk;
+
+ if (!info->attrs[GTPA_VERSION] ||
+ !info->attrs[GTPA_FD])
+ return -EINVAL;
+
+ if (!info->attrs[GTPA_PDP_HASHSIZE])
+ hashsize = 1024;
+ else
+ hashsize = nla_get_u32(info->attrs[IFLA_GTP_PDP_HASHSIZE]);
+
+ version = nla_get_u32(info->attrs[GTPA_VERSION]);
+ fd = nla_get_u32(info->attrs[GTPA_FD]);
+
+ switch (version) {
+ case GTP_V0:
+ sk = gtp_encap_enable_socket(fd, UDP_ENCAP_GTP0, hashsize);
+ break;
+
+ case GTP_V1:
+ sk = gtp_encap_enable_socket(fd, UDP_ENCAP_GTP1U, hashsize);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (!sk)
+ return -EINVAL;
+
+ if (IS_ERR(sk))
+ return PTR_ERR(sk);
+
+ return 0;
+}
+
static struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = {
[GTPA_LINK] = { .type = NLA_U32, },
[GTPA_VERSION] = { .type = NLA_U32, },
@@ -1254,6 +1293,8 @@ static struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = {
[GTPA_NET_NS_FD] = { .type = NLA_U32, },
[GTPA_I_TEI] = { .type = NLA_U32, },
[GTPA_O_TEI] = { .type = NLA_U32, },
+ [GTPA_PDP_HASHSIZE] = { .type = NLA_U32, },
+ [GTPA_FD] = { .type = NLA_U32, },
};
static const struct genl_ops gtp_genl_ops[] = {
@@ -1276,6 +1317,12 @@ static const struct genl_ops gtp_genl_ops[] = {
.policy = gtp_genl_policy,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = GTP_CMD_ENABLE_SOCKET,
+ .doit = gtp_genl_enable_socket,
+ .policy = gtp_genl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
};
static struct genl_family gtp_genl_family __ro_after_init = {
diff --git a/include/uapi/linux/gtp.h b/include/uapi/linux/gtp.h
index 72a04a0..a9e9fe0 100644
--- a/include/uapi/linux/gtp.h
+++ b/include/uapi/linux/gtp.h
@@ -6,6 +6,8 @@ enum gtp_genl_cmds {
GTP_CMD_DELPDP,
GTP_CMD_GETPDP,
+ GTP_CMD_ENABLE_SOCKET,
+
GTP_CMD_MAX,
};
@@ -26,6 +28,8 @@ enum gtp_attrs {
GTPA_I_TEI, /* for GTPv1 only */
GTPA_O_TEI, /* for GTPv1 only */
GTPA_PAD,
+ GTPA_PDP_HASHSIZE,
+ GTPA_FD,
__GTPA_MAX,
};
#define GTPA_MAX (__GTPA_MAX + 1)
--
2.10.2
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH net-next 3/4] gtp: add support to select a GTP socket during PDP context creation
2017-03-14 11:25 [PATCH net-next 0/4] gtp: support multiple APN's per GTP endpoint Andreas Schultz
2017-03-14 11:25 ` [PATCH net-next 1/4] gtp: move TEID hash to per socket structure Andreas Schultz
2017-03-14 11:25 ` [PATCH net-next 2/4] gtp: add genl cmd to enable GTP encapsulation on UDP socket Andreas Schultz
@ 2017-03-14 11:25 ` Andreas Schultz
2017-03-14 11:25 ` [PATCH net-next 4/4] Extend Kernel GTP-U tunneling documentation Andreas Schultz
2017-03-14 11:45 ` [PATCH net-next 0/4] gtp: support multiple APN's per GTP endpoint Pablo Neira Ayuso
4 siblings, 0 replies; 15+ messages in thread
From: Andreas Schultz @ 2017-03-14 11:25 UTC (permalink / raw)
To: Harald Welte, Pablo Neira Ayuso; +Cc: osmocom-net-gprs, netdev, Lionel Gauthier
For each new PDP a separate socket can be selected. The per netdevice
default sockets are no longer mandatory.
This means also that multiple gtp netdevices can share the same default
socket and that therefore the destruction of a gtp netdevice can no
longer automatically disable the gtp encapsulation on it's sockets.
Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
drivers/net/gtp.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 68 insertions(+), 11 deletions(-)
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index c4cf1b9..afa043d 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -381,9 +381,6 @@ static int gtp_dev_init(struct net_device *dev)
static void gtp_dev_uninit(struct net_device *dev)
{
- struct gtp_dev *gtp = netdev_priv(dev);
-
- gtp_encap_disable(gtp);
free_percpu(dev->tstats);
}
@@ -647,9 +644,6 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
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])
@@ -689,8 +683,11 @@ static void gtp_dellink(struct net_device *dev, struct list_head *head)
{
struct gtp_dev *gtp = netdev_priv(dev);
- gtp_encap_disable(gtp);
gtp_hashtable_free(gtp);
+ if (gtp->sk0)
+ sock_put(gtp->sk0);
+ if (gtp->sk1u)
+ sock_put(gtp->sk1u);
list_del_rcu(>p->list);
unregister_netdevice_queue(dev, head);
}
@@ -1008,9 +1005,10 @@ static void pdp_context_delete(struct pdp_ctx *pctx)
static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
{
+ struct socket *sock = NULL;
+ struct sock *sk = NULL;
unsigned int version;
struct gtp_dev *gtp;
- struct sock *sk;
int err;
if (!info->attrs[GTPA_VERSION] ||
@@ -1045,12 +1043,14 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
goto out_unlock;
}
- if (version == GTP_V0)
+ if (info->attrs[GTPA_FD]) {
+ sock = sockfd_lookup(nla_get_u32(info->attrs[GTPA_FD]), &err);
+ if (sock)
+ sk = sock->sk;
+ } else if (version == GTP_V0)
sk = gtp->sk0;
else if (version == GTP_V1)
sk = gtp->sk1u;
- else
- sk = NULL;
if (!sk) {
err = -ENODEV;
@@ -1059,6 +1059,9 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
err = ipv4_pdp_add(gtp, sk, info);
+ if (sock)
+ sockfd_put(sock);
+
out_unlock:
rcu_read_unlock();
return err;
@@ -1079,12 +1082,66 @@ static struct pdp_ctx *gtp_find_pdp_by_link(struct net *net,
return ipv4_pdp_find(gtp, nla_get_be32(nla[GTPA_MS_ADDRESS]));
}
+static struct pdp_ctx *gtp_genl_find_pdp_by_socket(struct net *net,
+ struct nlattr *nla[])
+{
+ struct socket *sock;
+ struct gtp_sock *gsk;
+ struct pdp_ctx *pctx;
+ int fd, err = 0;
+
+ if (!nla[GTPA_FD])
+ return ERR_PTR(-EINVAL);
+
+ fd = nla_get_u32(nla[GTPA_FD]);
+ sock = sockfd_lookup(fd, &err);
+ if (!sock) {
+ pr_debug("gtp socket fd=%d not found\n", fd);
+ return ERR_PTR(-EBADF);
+ }
+
+ gsk = rcu_dereference_sk_user_data(sock->sk);
+ if (!gsk) {
+ pctx = ERR_PTR(-EINVAL);
+ goto out_sock;
+ }
+
+ switch (nla_get_u32(nla[GTPA_VERSION])) {
+ case GTP_V0:
+ if (!nla[GTPA_TID]) {
+ pctx = ERR_PTR(-EINVAL);
+ break;
+ }
+ pctx = gtp0_pdp_find(gsk, nla_get_u64(nla[GTPA_TID]));
+ break;
+
+ case GTP_V1:
+ if (!nla[GTPA_I_TEI]) {
+ pctx = ERR_PTR(-EINVAL);
+ break;
+ }
+ pctx = gtp1_pdp_find(gsk, nla_get_u64(nla[GTPA_I_TEI]));
+ break;
+
+ default:
+ pctx = ERR_PTR(-EINVAL);
+ break;
+ }
+
+out_sock:
+ sockfd_put(sock);
+ return pctx;
+}
+
static struct pdp_ctx *gtp_find_pdp(struct net *net, struct nlattr *nla[])
{
struct pdp_ctx *pctx;
if (nla[GTPA_LINK])
pctx = gtp_find_pdp_by_link(net, nla);
+ else if (nla[GTPA_FD])
+ pctx = gtp_genl_find_pdp_by_socket(net, nla);
+
else
pctx = ERR_PTR(-EINVAL);
--
2.10.2
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH net-next 4/4] Extend Kernel GTP-U tunneling documentation
2017-03-14 11:25 [PATCH net-next 0/4] gtp: support multiple APN's per GTP endpoint Andreas Schultz
` (2 preceding siblings ...)
2017-03-14 11:25 ` [PATCH net-next 3/4] gtp: add support to select a GTP socket during PDP context creation Andreas Schultz
@ 2017-03-14 11:25 ` Andreas Schultz
2017-03-14 11:45 ` [PATCH net-next 0/4] gtp: support multiple APN's per GTP endpoint Pablo Neira Ayuso
4 siblings, 0 replies; 15+ messages in thread
From: Andreas Schultz @ 2017-03-14 11:25 UTC (permalink / raw)
To: Harald Welte, Pablo Neira Ayuso; +Cc: osmocom-net-gprs, netdev, Lionel Gauthier
* clarify specification references for v0/v1
* add section "APN vs. Network device"
* add section "Local GTP-U entity and tunnel identification"
Signed-off-by: Andreas Schultz <aschultz@tpip.net>
Signed-off-by: Harald Welte <laforge@gnumonks.org>
---
Documentation/networking/gtp.txt | 103 +++++++++++++++++++++++++++++++++++++--
1 file changed, 99 insertions(+), 4 deletions(-)
diff --git a/Documentation/networking/gtp.txt b/Documentation/networking/gtp.txt
index 93e9675..0d9c18f 100644
--- a/Documentation/networking/gtp.txt
+++ b/Documentation/networking/gtp.txt
@@ -1,6 +1,7 @@
The Linux kernel GTP tunneling module
======================================================================
-Documentation by Harald Welte <laforge@gnumonks.org>
+Documentation by Harald Welte <laforge@gnumonks.org> and
+ Andreas Schultz <aschultz@tpip.net>
In 'drivers/net/gtp.c' you are finding a kernel-level implementation
of a GTP tunnel endpoint.
@@ -91,9 +92,13 @@ http://git.osmocom.org/libgtpnl/
== Protocol Versions ==
-There are two different versions of GTP-U: v0 and v1. Both are
-implemented in the Kernel GTP module. Version 0 is a legacy version,
-and deprecated from recent 3GPP specifications.
+There are two different versions of GTP-U: v0 [GSM TS 09.60] and v1
+[3GPP TS 29.281]. Both are implemented in the Kernel GTP module.
+Version 0 is a legacy version, and deprecated from recent 3GPP
+specifications.
+
+GTP-U uses UDP for transporting PDUs. The receiving UDP port is 2151
+for GTPv1-U and 3386 for GTPv0-U.
There are three versions of GTP-C: v0, v1, and v2. As the kernel
doesn't implement GTP-C, we don't have to worry about this. It's the
@@ -133,3 +138,93 @@ doe to a lack of user interest, it never got merged.
In 2015, Andreas Schultz came to the rescue and fixed lots more bugs,
extended it with new features and finally pushed all of us to get it
mainline, where it was merged in 4.7.0.
+
+== Architectural Details ==
+
+=== Local GTP-U entity and tunnel identification ===
+
+GTP-U uses UDP for transporting PDU's. The receiving UDP port is 2152
+for GTPv1-U and 3386 for GTPv0-U.
+
+There is only one GTP-U entity (and therefor SGSN/GGSN/S-GW/PDN-GW
+instance) per IP address. Tunnel Endpoint Identifier (TEID) are unique
+per GTP-U entity.
+
+A specific tunnel is only defined by the destination entity. Since the
+destination port is constant, only the destination IP and TEID define
+a tunnel. The source IP and Port have no meaning for the tunnel.
+
+Therefore:
+
+ * when sending, the remote entity is defined by the remote IP and
+ the tunnel endpoint id. The source IP and port have no meaning and
+ can be changed at any time.
+
+ * when receiving the local entity is defined by the local
+ destination IP and the tunnel endpoint id. The source IP and port
+ have no meaning and can change at any time.
+
+[3GPP TS 29.281] Section 4.3.0 defines this so:
+
+> The TEID in the GTP-U header is used to de-multiplex traffic
+> incoming from remote tunnel endpoints so that it is delivered to the
+> User plane entities in a way that allows multiplexing of different
+> users, different packet protocols and different QoS levels.
+> Therefore no two remote GTP-U endpoints shall send traffic to a
+> GTP-U protocol entity using the same TEID value except
+> for data forwarding as part of mobility procedures.
+
+The definition above only defines that two remote GTP-U endpoints
+*should not* send to the same TEID, it *does not* forbid or exclude
+such a scenario. In fact, the mentioned mobility procedures make it
+necessary that the GTP-U entity accepts traffic for TEIDs from
+multiple or unknown peers.
+
+Therefore, the receiving side identifies tunnels exclusively based on
+TEIDs, not based on the source IP!
+
+== APN vs. Network Device ==
+
+The GTP-U driver creates a Linux network device for each Gi/SGi
+interface.
+
+[3GPP TS 29.281] calls the Gi/SGi reference point an interface. This
+may lead to the impression that the GGSN/P-GW can have only one such
+interface.
+
+Correct is that the Gi/SGi reference point defines the interworking
+between +the 3GPP packet domain (PDN) based on GTP-U tunnel and IP
+based networks.
+
+There is no provision in any of the 3GPP documents that limits the
+number of Gi/SGi interfaces implemented by a GGSN/P-GW.
+
+[3GPP TS 29.061] Section 11.3 makes it clear that the selection of a
+specific Gi/SGi interfaces is made through the Access Point Name
+(APN):
+
+> 2. each private network manages its own addressing. In general this
+> will result in different private networks having overlapping
+> address ranges. A logically separate connection (e.g. an IP in IP
+> tunnel or layer 2 virtual circuit) is used between the GGSN/P-GW
+> and each private network.
+>
+> In this case the IP address alone is not necessarily unique. The
+> pair of values, Access Point Name (APN) and IPv4 address and/or
+> IPv6 prefixes, is unique.
+
+In order to support the overlapping address range use case, each APN
+is mapped to a separate Gi/SGi interface (network device).
+
+NOTE: The Access Point Name is purely a control plane (GTP-C) concept.
+At the GTP-U level, only Tunnel Endpoint Identifiers are present in
+GTP-U packets and network devices are known
+
+Therefore for a given UE the mapping in IP to PDN network is:
+ * network device + MS IP -> Peer IP + Peer TEID,
+
+and from PDN to IP network:
+ * local GTP-U IP + TEID -> network device
+
+Furthermore, before a received T-PDU is injected into the network
+device the MS IP is checked against the IP recorded in PDP context.
--
2.10.2
^ permalink raw reply related [flat|nested] 15+ messages in thread