static void netlink_remove(struct sock *sk) { netlink_table_grab(); nl_table[sk->sk_protocol].hash.entries--; sk_del_node_init(sk); if (nlk_sk(sk)->groups) __sk_del_bind_node(sk); netlink_table_ungrab(); }
/* * Get rid of any references to a local port held by the given sock. */ static void __inet_put_port(struct sock *sk) { struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; const int bhash = inet_bhashfn(inet_sk(sk)->num, hashinfo->bhash_size); struct inet_bind_hashbucket *head = &hashinfo->bhash[bhash]; struct inet_bind_bucket *tb; spin_lock(&head->lock); tb = inet_csk(sk)->icsk_bind_hash; __sk_del_bind_node(sk); inet_csk(sk)->icsk_bind_hash = NULL; inet_sk(sk)->num = 0; inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb); spin_unlock(&head->lock); }
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; }