@@ -58,6 +58,8 @@ struct pdp_ctx {
struct in_addr ms_addr_ip4;
struct in_addr sgsn_addr_ip4;
+ struct sock *sk;
+
atomic_t tx_seq;
struct rcu_head rcu_head;
};
@@ -353,8 +355,9 @@ static void gtp_dev_uninit(struct net_device *dev)
free_percpu(dev->tstats);
}
-static struct rtable *ip4_route_output_gtp(struct net *net, struct flowi4 *fl4,
- const struct sock *sk, __be32 daddr)
+static struct rtable *ip4_route_output_gtp(struct flowi4 *fl4,
+ const struct sock *sk,
+ __be32 daddr)
{
memset(fl4, 0, sizeof(*fl4));
fl4->flowi4_oif = sk->sk_bound_dev_if;
@@ -363,7 +366,7 @@ static struct rtable *ip4_route_output_gtp(struct net *net, struct flowi4 *fl4,
fl4->flowi4_tos = RT_CONN_FLAGS(sk);
fl4->flowi4_proto = sk->sk_protocol;
- return ip_route_output_key(net, fl4);
+ return ip_route_output_key(sock_net(sk), fl4);
}
static inline void gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx)
@@ -452,7 +455,6 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
struct rtable *rt;
struct flowi4 fl4;
struct iphdr *iph;
- struct sock *sk;
__be16 df;
int mtu;
@@ -468,30 +470,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
}
netdev_dbg(dev, "found PDP context %p\n", pctx);
- switch (pctx->gtp_version) {
- case GTP_V0:
- if (gtp->sock0)
- sk = gtp->sock0->sk;
- else
- sk = NULL;
- break;
- case GTP_V1:
- if (gtp->sock1u)
- sk = gtp->sock1u->sk;
- else
- sk = NULL;
- break;
- default:
- return -ENOENT;
- }
-
- if (!sk) {
- netdev_dbg(dev, "no userspace socket is available, skip\n");
- return -ENOENT;
- }
-
- rt = ip4_route_output_gtp(sock_net(sk), &fl4, gtp->sock0->sk,
- pctx->sgsn_addr_ip4.s_addr);
+ rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->sgsn_addr_ip4.s_addr);
if (IS_ERR(rt)) {
netdev_dbg(dev, "no route to SSGN %pI4\n",
&pctx->sgsn_addr_ip4.s_addr);
@@ -536,7 +515,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
goto err_rt;
}
- gtp_set_pktinfo_ipv4(pktinfo, sk, iph, pctx, rt, &fl4, dev);
+ gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph, pctx, rt, &fl4, dev);
gtp_push_header(skb, pktinfo);
return 0;
@@ -906,7 +885,8 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
}
}
-static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info)
+static int ipv4_pdp_add(struct net_device *dev, struct sock *sk,
+ struct genl_info *info)
{
struct gtp_dev *gtp = netdev_priv(dev);
u32 hash_ms, hash_tid = 0;
@@ -947,6 +927,8 @@ static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info)
if (pctx == NULL)
return -ENOMEM;
+ sock_hold(sk);
+ pctx->sk = sk;
ipv4_pdp_fill(pctx, info);
atomic_set(&pctx->tx_seq, 0);
@@ -987,6 +969,7 @@ static void pdp_context_free(struct rcu_head *head)
{
struct pdp_ctx *pctx = container_of(head, struct pdp_ctx, rcu_head);
+ sock_put(pctx->sk);
kfree(pctx);
}
@@ -997,10 +980,27 @@ static void pdp_context_delete(struct pdp_ctx *pctx)
call_rcu(&pctx->rcu_head, pdp_context_free);
}
+static struct socket *gtp_genl_new_pdp_select_socket(int version,
+ struct net_device *dev)
+{
+ struct gtp_dev *gtp = netdev_priv(dev);
+
+ switch (version) {
+ case GTP_V0:
+ return gtp->sock0;
+ case GTP_V1:
+ return gtp->sock1u;
+ default:
+ return NULL;
+ }
+}
+
static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
{
+ unsigned int version;
struct net_device *dev;
struct net *net;
+ struct socket *sock;
if (!info->attrs[GTPA_VERSION] ||
!info->attrs[GTPA_LINK] ||
@@ -1008,7 +1008,9 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
!info->attrs[GTPA_MS_ADDRESS])
return -EINVAL;
- switch (nla_get_u32(info->attrs[GTPA_VERSION])) {
+ version = nla_get_u32(info->attrs[GTPA_VERSION]);
+
+ switch (version) {
case GTP_V0:
if (!info->attrs[GTPA_TID] ||
!info->attrs[GTPA_FLOW])
@@ -1036,7 +1038,11 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
}
put_net(net);
- return ipv4_pdp_add(dev, info);
+ sock = gtp_genl_new_pdp_select_socket(version, dev);
+ if (!sock)
+ return -ENODEV;
+
+ return ipv4_pdp_add(dev, sock->sk, info);
}
static struct pdp_ctx *gtp_genl_find_pdp_by_link(struct sk_buff *skb,
Having the socket present in context simplifies the sending logic. It also fixes the invalid assumtion that we have to use the same sending socket for all client IP's on a specific gtp interface. Signed-off-by: Andreas Schultz <aschultz@tpip.net> --- drivers/net/gtp.c | 70 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 32 deletions(-)