summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fastd.c48
1 files changed, 39 insertions, 9 deletions
diff --git a/fastd.c b/fastd.c
index abdd17f..8a90c7a 100644
--- a/fastd.c
+++ b/fastd.c
@@ -384,6 +384,13 @@ static int fastd_cmd_bind(struct sk_buff *skb, struct genl_info *info)
sa_family_t af;
struct fastd_socket *socket;
struct fastd_struct *fastd;
+ struct sk_buff *ret;
+ void *skb_head;
+ unsigned char addrbuf[sizeof(struct sockaddr_in6)];
+ int addrlen = sizeof(addrbuf);
+
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+ return -EPERM;
if (!info->attrs[FASTD_A_LOCALADDR] ||
nla_len(info->attrs[FASTD_A_LOCALADDR]) < sizeof(sa_family_t))
@@ -397,9 +404,21 @@ static int fastd_cmd_bind(struct sk_buff *skb, struct genl_info *info)
if (!socket)
return -ENOMEM;
+ ret = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!ret) {
+ err = -ENOMEM;
+ goto err_free_socket;
+ }
+
+ skb_head = genlmsg_put(ret, 0, info->snd_seq, &fastd_nl_family, 0, FASTD_CMD_BIND);
+ if (!skb_head) {
+ err = -ENOMEM;
+ goto err_free_skb;
+ }
+
err = sock_create_kern(af, SOCK_DGRAM, IPPROTO_UDP, &socket->sock);
if (err)
- goto err_free_socket;
+ goto err_free_skb;
sk_change_net(socket->sock->sk, net);
@@ -409,19 +428,28 @@ static int fastd_cmd_bind(struct sk_buff *skb, struct genl_info *info)
if (err)
goto err_release_sock;
+ err = kernel_getsockname(socket->sock, (struct sockaddr*)addrbuf, &addrlen);
+ if (err < 0)
+ goto err_release_sock;
+
+ nla_put(ret, FASTD_A_LOCALADDR, addrlen, addrbuf);
+
rcu_read_lock();
fastd = fastd_find(net, info->snd_portid);
if (!fastd) {
+ rcu_read_unlock();
err = -EINVAL;
- goto err_unlock_rcu;
+ goto err_release_sock;
}
spin_lock_bh(&fastd->lock);
if (!fastd->owner) {
+ spin_unlock_bh(&fastd->lock);
+ rcu_read_unlock();
err = -EINVAL;
- goto err_unlock;
+ goto err_release_sock;
}
socket->sock->sk->sk_user_data = fastd;
@@ -431,17 +459,19 @@ static int fastd_cmd_bind(struct sk_buff *skb, struct genl_info *info)
rcu_read_unlock();
- return 0;
-
-err_unlock:
- spin_unlock_bh(&fastd->lock);
+ genlmsg_end(ret, skb_head);
+ err = genlmsg_reply(ret, info);
+ if (err)
+ return err;
-err_unlock_rcu:
- rcu_read_unlock();
+ return 0;
err_release_sock:
sk_release_kernel(socket->sock->sk);
+err_free_skb:
+ kfree_skb(ret);
+
err_free_socket:
kfree(socket);