|
@@ -744,21 +744,6 @@ static struct rtnl_link_ops gtp_link_ops __read_mostly = {
|
|
|
.fill_info = gtp_fill_info,
|
|
|
};
|
|
|
|
|
|
-static struct net *gtp_genl_get_net(struct net *src_net, struct nlattr *tb[])
|
|
|
-{
|
|
|
- struct net *net;
|
|
|
-
|
|
|
- /* Examine the link attributes and figure out which network namespace
|
|
|
- * we are talking about.
|
|
|
- */
|
|
|
- if (tb[GTPA_NET_NS_FD])
|
|
|
- net = get_net_ns_by_fd(nla_get_u32(tb[GTPA_NET_NS_FD]));
|
|
|
- else
|
|
|
- net = get_net(src_net);
|
|
|
-
|
|
|
- return net;
|
|
|
-}
|
|
|
-
|
|
|
static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize)
|
|
|
{
|
|
|
int i;
|
|
@@ -872,16 +857,30 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static struct net_device *gtp_find_dev(struct net *net, int ifindex)
|
|
|
+static struct gtp_dev *gtp_find_dev(struct net *src_net, struct nlattr *nla[])
|
|
|
{
|
|
|
- struct gtp_net *gn = net_generic(net, gtp_net_id);
|
|
|
- struct gtp_dev *gtp;
|
|
|
+ struct gtp_dev *gtp = NULL;
|
|
|
+ struct net_device *dev;
|
|
|
+ struct net *net;
|
|
|
|
|
|
- list_for_each_entry_rcu(gtp, &gn->gtp_dev_list, list) {
|
|
|
- if (ifindex == gtp->dev->ifindex)
|
|
|
- return gtp->dev;
|
|
|
- }
|
|
|
- return NULL;
|
|
|
+ /* Examine the link attributes and figure out which network namespace
|
|
|
+ * we are talking about.
|
|
|
+ */
|
|
|
+ if (nla[GTPA_NET_NS_FD])
|
|
|
+ net = get_net_ns_by_fd(nla_get_u32(nla[GTPA_NET_NS_FD]));
|
|
|
+ else
|
|
|
+ net = get_net(src_net);
|
|
|
+
|
|
|
+ if (IS_ERR(net))
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /* Check if there's an existing gtpX device to configure */
|
|
|
+ dev = dev_get_by_index_rcu(net, nla_get_u32(nla[GTPA_LINK]));
|
|
|
+ if (dev->netdev_ops == >p_netdev_ops)
|
|
|
+ gtp = netdev_priv(dev);
|
|
|
+
|
|
|
+ put_net(net);
|
|
|
+ return gtp;
|
|
|
}
|
|
|
|
|
|
static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
|
|
@@ -911,9 +910,9 @@ 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 gtp_dev *gtp, struct genl_info *info)
|
|
|
{
|
|
|
- struct gtp_dev *gtp = netdev_priv(dev);
|
|
|
+ struct net_device *dev = gtp->dev;
|
|
|
u32 hash_ms, hash_tid = 0;
|
|
|
struct pdp_ctx *pctx;
|
|
|
bool found = false;
|
|
@@ -990,8 +989,8 @@ static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info)
|
|
|
|
|
|
static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
|
|
|
{
|
|
|
- struct net_device *dev;
|
|
|
- struct net *net;
|
|
|
+ struct gtp_dev *gtp;
|
|
|
+ int err;
|
|
|
|
|
|
if (!info->attrs[GTPA_VERSION] ||
|
|
|
!info->attrs[GTPA_LINK] ||
|
|
@@ -1015,77 +1014,79 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- net = gtp_genl_get_net(sock_net(skb->sk), info->attrs);
|
|
|
- if (IS_ERR(net))
|
|
|
- return PTR_ERR(net);
|
|
|
+ rcu_read_lock();
|
|
|
|
|
|
- /* Check if there's an existing gtpX device to configure */
|
|
|
- dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK]));
|
|
|
- if (dev == NULL) {
|
|
|
- put_net(net);
|
|
|
- return -ENODEV;
|
|
|
+ gtp = gtp_find_dev(sock_net(skb->sk), info->attrs);
|
|
|
+ if (!gtp) {
|
|
|
+ err = -ENODEV;
|
|
|
+ goto out_unlock;
|
|
|
}
|
|
|
- put_net(net);
|
|
|
|
|
|
- return ipv4_pdp_add(dev, info);
|
|
|
+ err = ipv4_pdp_add(gtp, info);
|
|
|
+
|
|
|
+out_unlock:
|
|
|
+ rcu_read_unlock();
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info)
|
|
|
{
|
|
|
- struct net_device *dev;
|
|
|
struct pdp_ctx *pctx;
|
|
|
struct gtp_dev *gtp;
|
|
|
- struct net *net;
|
|
|
+ int err = 0;
|
|
|
|
|
|
if (!info->attrs[GTPA_VERSION] ||
|
|
|
!info->attrs[GTPA_LINK])
|
|
|
return -EINVAL;
|
|
|
|
|
|
- net = gtp_genl_get_net(sock_net(skb->sk), info->attrs);
|
|
|
- if (IS_ERR(net))
|
|
|
- return PTR_ERR(net);
|
|
|
+ rcu_read_lock();
|
|
|
|
|
|
- /* Check if there's an existing gtpX device to configure */
|
|
|
- dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK]));
|
|
|
- if (dev == NULL) {
|
|
|
- put_net(net);
|
|
|
- return -ENODEV;
|
|
|
+ gtp = gtp_find_dev(sock_net(skb->sk), info->attrs);
|
|
|
+ if (!gtp) {
|
|
|
+ err = -ENODEV;
|
|
|
+ goto out_unlock;
|
|
|
}
|
|
|
- put_net(net);
|
|
|
-
|
|
|
- gtp = netdev_priv(dev);
|
|
|
|
|
|
switch (nla_get_u32(info->attrs[GTPA_VERSION])) {
|
|
|
case GTP_V0:
|
|
|
- if (!info->attrs[GTPA_TID])
|
|
|
- return -EINVAL;
|
|
|
+ if (!info->attrs[GTPA_TID]) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
pctx = gtp0_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_TID]));
|
|
|
break;
|
|
|
case GTP_V1:
|
|
|
- if (!info->attrs[GTPA_I_TEI])
|
|
|
- return -EINVAL;
|
|
|
+ if (!info->attrs[GTPA_I_TEI]) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
pctx = gtp1_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_I_TEI]));
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
- return -EINVAL;
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out_unlock;
|
|
|
}
|
|
|
|
|
|
- if (pctx == NULL)
|
|
|
- return -ENOENT;
|
|
|
+ if (!pctx) {
|
|
|
+ err = -ENOENT;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
|
|
|
if (pctx->gtp_version == GTP_V0)
|
|
|
- netdev_dbg(dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n",
|
|
|
+ netdev_dbg(gtp->dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n",
|
|
|
pctx->u.v0.tid, pctx);
|
|
|
else if (pctx->gtp_version == GTP_V1)
|
|
|
- netdev_dbg(dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n",
|
|
|
+ netdev_dbg(gtp->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n",
|
|
|
pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx);
|
|
|
|
|
|
hlist_del_rcu(&pctx->hlist_tid);
|
|
|
hlist_del_rcu(&pctx->hlist_addr);
|
|
|
kfree_rcu(pctx, rcu_head);
|
|
|
|
|
|
- return 0;
|
|
|
+out_unlock:
|
|
|
+ rcu_read_unlock();
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
static struct genl_family gtp_genl_family;
|
|
@@ -1129,11 +1130,9 @@ nla_put_failure:
|
|
|
static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info)
|
|
|
{
|
|
|
struct pdp_ctx *pctx = NULL;
|
|
|
- struct net_device *dev;
|
|
|
struct sk_buff *skb2;
|
|
|
struct gtp_dev *gtp;
|
|
|
u32 gtp_version;
|
|
|
- struct net *net;
|
|
|
int err;
|
|
|
|
|
|
if (!info->attrs[GTPA_VERSION] ||
|
|
@@ -1149,21 +1148,14 @@ static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- net = gtp_genl_get_net(sock_net(skb->sk), info->attrs);
|
|
|
- if (IS_ERR(net))
|
|
|
- return PTR_ERR(net);
|
|
|
+ rcu_read_lock();
|
|
|
|
|
|
- /* Check if there's an existing gtpX device to configure */
|
|
|
- dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK]));
|
|
|
- if (dev == NULL) {
|
|
|
- put_net(net);
|
|
|
- return -ENODEV;
|
|
|
+ gtp = gtp_find_dev(sock_net(skb->sk), info->attrs);
|
|
|
+ if (!gtp) {
|
|
|
+ err = -ENODEV;
|
|
|
+ goto err_unlock;
|
|
|
}
|
|
|
- put_net(net);
|
|
|
-
|
|
|
- gtp = netdev_priv(dev);
|
|
|
|
|
|
- rcu_read_lock();
|
|
|
if (gtp_version == GTP_V0 &&
|
|
|
info->attrs[GTPA_TID]) {
|
|
|
u64 tid = nla_get_u64(info->attrs[GTPA_TID]);
|