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