static int netlink_release(struct socket *sock) { struct sock *sk = sock->sk; if (!sk) return 0; netlink_remove(sk); spin_lock(&sk->protinfo.af_netlink->cb_lock); if (sk->protinfo.af_netlink->cb) { sk->protinfo.af_netlink->cb->done(sk->protinfo.af_netlink->cb); netlink_destroy_callback(sk->protinfo.af_netlink->cb); sk->protinfo.af_netlink->cb = NULL; __sock_put(sk); } spin_unlock(&sk->protinfo.af_netlink->cb_lock); /* OK. Socket is unlinked, and, therefore, no new packets will arrive */ sock_orphan(sk); sock->sk = NULL; wake_up_interruptible_all(&sk->protinfo.af_netlink->wait); skb_queue_purge(&sk->write_queue); sock_put(sk); return 0; }
int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, struct nlmsghdr *nlh, int (*dump)(struct sk_buff *skb, struct netlink_callback*), int (*done)(struct netlink_callback*)) { struct netlink_callback *cb; struct sock *sk; struct netlink_opt *nlk; cb = kmalloc(sizeof(*cb), GFP_KERNEL); if (cb == NULL) return -ENOBUFS; memset(cb, 0, sizeof(*cb)); cb->dump = dump; cb->done = done; cb->nlh = nlh; atomic_inc(&skb->users); cb->skb = skb; sk = netlink_lookup(ssk->sk_protocol, NETLINK_CB(skb).pid); if (sk == NULL) { netlink_destroy_callback(cb); return -ECONNREFUSED; } nlk = nlk_sk(sk); /* A dump is in progress... */ spin_lock(&nlk->cb_lock); if (nlk->cb) { spin_unlock(&nlk->cb_lock); netlink_destroy_callback(cb); sock_put(sk); return -EBUSY; } nlk->cb = cb; spin_unlock(&nlk->cb_lock); netlink_dump(sk); return 0; }
static int netlink_dump(struct sock *sk) { struct netlink_opt *nlk = nlk_sk(sk); struct netlink_callback *cb; struct sk_buff *skb; struct nlmsghdr *nlh; int len; skb = sock_rmalloc(sk, NLMSG_GOODSIZE, 0, GFP_KERNEL); if (!skb) return -ENOBUFS; spin_lock(&nlk->cb_lock); cb = nlk->cb; if (cb == NULL) { spin_unlock(&nlk->cb_lock); kfree_skb(skb); return -EINVAL; } len = cb->dump(skb, cb); if (len > 0) { spin_unlock(&nlk->cb_lock); skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_data_ready(sk, len); return 0; } nlh = __nlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLMSG_DONE, sizeof(int)); nlh->nlmsg_flags |= NLM_F_MULTI; memcpy(NLMSG_DATA(nlh), &len, sizeof(len)); skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_data_ready(sk, skb->len); cb->done(cb); nlk->cb = NULL; spin_unlock(&nlk->cb_lock); netlink_destroy_callback(cb); sock_put(sk); return 0; }
static int netlink_release(struct socket *sock) { struct sock *sk = sock->sk; if (!sk) return 0; netlink_remove(sk); spin_lock(&sk->protinfo.af_netlink->cb_lock); if (sk->protinfo.af_netlink->cb) { sk->protinfo.af_netlink->cb->done(sk->protinfo.af_netlink->cb); netlink_destroy_callback(sk->protinfo.af_netlink->cb); sk->protinfo.af_netlink->cb = NULL; __sock_put(sk); } spin_unlock(&sk->protinfo.af_netlink->cb_lock); /* OK. Socket is unlinked, and, therefore, no new packets will arrive */ sock_orphan(sk); sock->sk = NULL; wake_up_interruptible_all(&sk->protinfo.af_netlink->wait); skb_queue_purge(&sk->write_queue); if (sk->protinfo.af_netlink->pid && !sk->protinfo.af_netlink->groups) { struct netlink_notify n = { protocol:sk->protocol, pid:sk->protinfo.af_netlink->pid }; notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); } sock_put(sk); return 0; }
static int netlink_release(struct socket *sock) { struct sock *sk = sock->sk; struct netlink_opt *nlk; if (!sk) return 0; netlink_remove(sk); nlk = nlk_sk(sk); spin_lock(&nlk->cb_lock); if (nlk->cb) { nlk->cb->done(nlk->cb); netlink_destroy_callback(nlk->cb); nlk->cb = NULL; __sock_put(sk); } spin_unlock(&nlk->cb_lock); /* OK. Socket is unlinked, and, therefore, no new packets will arrive */ sock_orphan(sk); sock->sk = NULL; wake_up_interruptible_all(&nlk->wait); skb_queue_purge(&sk->sk_write_queue); if (nlk->pid && !nlk->groups) { struct netlink_notify n = { .protocol = sk->sk_protocol, .pid = nlk->pid, }; notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); } sock_put(sk); return 0; } static int netlink_autobind(struct socket *sock) { struct sock *sk = sock->sk; struct nl_pid_hash *hash = &nl_table[sk->sk_protocol].hash; struct hlist_head *head; struct sock *osk; struct hlist_node *node; s32 pid = current->pid; int err; static s32 rover = -4097; retry: cond_resched(); netlink_table_grab(); head = nl_pid_hashfn(hash, pid); sk_for_each(osk, node, head) { if (nlk_sk(osk)->pid == pid) { /* Bind collision, search negative pid values. */ pid = rover--; if (rover > -4097) rover = -4097; netlink_table_ungrab(); goto retry; } } netlink_table_ungrab(); err = netlink_insert(sk, pid); if (err == -EADDRINUSE) goto retry; nlk_sk(sk)->groups = 0; return 0; } static inline int netlink_capable(struct socket *sock, unsigned int flag) { return (nl_nonroot[sock->sk->sk_protocol] & flag) || capable(CAP_NET_ADMIN); } static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { struct sock *sk = sock->sk; struct netlink_opt *nlk = nlk_sk(sk); struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; int err; if (nladdr->nl_family != AF_NETLINK) return -EINVAL; /* Only superuser is allowed to listen multicasts */ if (nladdr->nl_groups && !netlink_capable(sock, NL_NONROOT_RECV)) return -EPERM; if (nlk->pid) { if (nladdr->nl_pid != nlk->pid) return -EINVAL; } else { err = nladdr->nl_pid ? netlink_insert(sk, nladdr->nl_pid) : netlink_autobind(sock); if (err) return err; } if (!nladdr->nl_groups && !nlk->groups) return 0; netlink_table_grab(); if (nlk->groups && !nladdr->nl_groups) __sk_del_bind_node(sk); else if (!nlk->groups && nladdr->nl_groups) sk_add_bind_node(sk, &nl_table[sk->sk_protocol].mc_list); nlk->groups = nladdr->nl_groups; netlink_table_ungrab(); return 0; } static int netlink_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { int err = 0; struct sock *sk = sock->sk; struct netlink_opt *nlk = nlk_sk(sk); struct sockaddr_nl *nladdr=(struct sockaddr_nl*)addr; if (addr->sa_family == AF_UNSPEC) { sk->sk_state = NETLINK_UNCONNECTED; nlk->dst_pid = 0; nlk->dst_groups = 0; return 0; } if (addr->sa_family != AF_NETLINK) return -EINVAL; /* Only superuser is allowed to send multicasts */ if (nladdr->nl_groups && !netlink_capable(sock, NL_NONROOT_SEND)) return -EPERM; if (!nlk->pid) err = netlink_autobind(sock); if (err == 0) { sk->sk_state = NETLINK_CONNECTED; nlk->dst_pid = nladdr->nl_pid; nlk->dst_groups = nladdr->nl_groups; } return err; }