diff options
-rw-r--r-- | fastd.c | 48 |
1 files changed, 39 insertions, 9 deletions
@@ -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); |