int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n) { nfnl_lock(n->subsys_id); if (table[n->subsys_id].subsys) { nfnl_unlock(n->subsys_id); return -EBUSY; } rcu_assign_pointer(table[n->subsys_id].subsys, n); nfnl_unlock(n->subsys_id); return 0; }
int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n) { nfnl_lock(); if (subsys_table[n->subsys_id]) { nfnl_unlock(); return -EBUSY; } RCU_INIT_POINTER(subsys_table[n->subsys_id], n); nfnl_unlock(); return 0; }
int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n) { nfnl_lock(); if (subsys_table[n->subsys_id]) { nfnl_unlock(); return -EBUSY; } subsys_table[n->subsys_id] = n; nfnl_unlock(); return 0; }
int nfnetlink_subsys_register(struct nfnetlink_subsystem *n) { DEBUGP("registering subsystem ID %u\n", n->subsys_id); nfnl_lock(); if (subsys_table[n->subsys_id]) { nfnl_unlock(); return -EBUSY; } subsys_table[n->subsys_id] = n; nfnl_unlock(); return 0; }
int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n) { nfnl_lock(n->subsys_id); table[n->subsys_id].subsys = NULL; nfnl_unlock(n->subsys_id); synchronize_rcu(); return 0; }
int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n) { nfnl_lock(); subsys_table[n->subsys_id] = NULL; nfnl_unlock(); return 0; }
/* Process one complete nfnetlink message. */ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { const struct nfnl_callback *nc; const struct nfnetlink_subsystem *ss; int type, err; #if 0 if (security_netlink_recv(skb, CAP_NET_ADMIN)) return -EPERM; #endif /* All the messages must at least contain nfgenmsg */ if (nlh->nlmsg_len < NLMSG_SPACE(sizeof(struct nfgenmsg))) return 0; type = nlh->nlmsg_type; replay: ss = nfnetlink_get_subsys(type); if (!ss) { #ifdef CONFIG_MODULES nfnl_unlock(); request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type)); nfnl_lock(); ss = nfnetlink_get_subsys(type); if (!ss) #endif return -EINVAL; } nc = nfnetlink_find_client(type, ss); if (!nc) return -EINVAL; { int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type); u_int16_t attr_count = ss->cb[cb_id].attr_count; struct nlattr *cda[attr_count+1]; if (likely(nlh->nlmsg_len >= min_len)) { struct nlattr *attr = (void *)nlh + NLMSG_ALIGN(min_len); int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len); err = nla_parse(cda, attr_count, attr, attrlen, ss->cb[cb_id].policy); if (err < 0) return err; } else return -EINVAL; err = nc->call(nfnl, skb, nlh, (struct nfattr **)cda); if (err == -EAGAIN) goto replay; return err; } }
int nfnetlink_subsys_unregister(struct nfnetlink_subsystem *n) { DEBUGP("unregistering subsystem ID %u\n", n->subsys_id); nfnl_lock(); subsys_table[n->subsys_id] = NULL; nfnl_unlock(); return 0; }
/* Process one complete nfnetlink message. */ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { struct net *net = sock_net(skb->sk); const struct nfnl_callback *nc; const struct nfnetlink_subsystem *ss; int type, err; /* All the messages must at least contain nfgenmsg */ if (nlmsg_len(nlh) < sizeof(struct nfgenmsg)) return 0; type = nlh->nlmsg_type; replay: rcu_read_lock(); ss = nfnetlink_get_subsys(type); if (!ss) { #ifdef CONFIG_MODULES rcu_read_unlock(); request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type)); rcu_read_lock(); ss = nfnetlink_get_subsys(type); if (!ss) #endif { rcu_read_unlock(); return -EINVAL; } } nc = nfnetlink_find_client(type, ss); if (!nc) { rcu_read_unlock(); return -EINVAL; } { int min_len = nlmsg_total_size(sizeof(struct nfgenmsg)); u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type); struct nlattr *cda[ss->cb[cb_id].attr_count + 1]; struct nlattr *attr = (void *)nlh + min_len; int attrlen = nlh->nlmsg_len - min_len; __u8 subsys_id = NFNL_SUBSYS_ID(type); err = nla_parse(cda, ss->cb[cb_id].attr_count, attr, attrlen, ss->cb[cb_id].policy); if (err < 0) { rcu_read_unlock(); return err; } if (nc->call_rcu) { err = nc->call_rcu(net->nfnl, skb, nlh, (const struct nlattr **)cda); rcu_read_unlock(); } else { rcu_read_unlock(); nfnl_lock(subsys_id); if (rcu_dereference_protected(table[subsys_id].subsys, lockdep_is_held(&table[subsys_id].mutex)) != ss || nfnetlink_find_client(type, ss) != nc) err = -EAGAIN; else if (nc->call) err = nc->call(net->nfnl, skb, nlh, (const struct nlattr **)cda); else err = -EINVAL; nfnl_unlock(subsys_id); } if (err == -EAGAIN) goto replay; return err; } }
static void nfnetlink_rcv(struct sk_buff *skb) { nfnl_lock(); netlink_rcv_skb(skb, &nfnetlink_rcv_msg); nfnl_unlock(); }
static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, u_int16_t subsys_id) { struct sk_buff *nskb, *oskb = skb; struct net *net = sock_net(skb->sk); const struct nfnetlink_subsystem *ss; const struct nfnl_callback *nc; bool success = true, done = false; int err; if (subsys_id >= NFNL_SUBSYS_COUNT) return netlink_ack(skb, nlh, -EINVAL); replay: nskb = netlink_skb_clone(oskb, GFP_KERNEL); if (!nskb) return netlink_ack(oskb, nlh, -ENOMEM); nskb->sk = oskb->sk; skb = nskb; nfnl_lock(subsys_id); ss = rcu_dereference_protected(table[subsys_id].subsys, lockdep_is_held(&table[subsys_id].mutex)); if (!ss) { #ifdef CONFIG_MODULES nfnl_unlock(subsys_id); request_module("nfnetlink-subsys-%d", subsys_id); nfnl_lock(subsys_id); ss = rcu_dereference_protected(table[subsys_id].subsys, lockdep_is_held(&table[subsys_id].mutex)); if (!ss) #endif { nfnl_unlock(subsys_id); netlink_ack(skb, nlh, -EOPNOTSUPP); return kfree_skb(nskb); } } if (!ss->commit || !ss->abort) { nfnl_unlock(subsys_id); netlink_ack(skb, nlh, -EOPNOTSUPP); return kfree_skb(skb); } while (skb->len >= nlmsg_total_size(0)) { int msglen, type; nlh = nlmsg_hdr(skb); err = 0; if (nlh->nlmsg_len < NLMSG_HDRLEN) { err = -EINVAL; goto ack; } /* Only requests are handled by the kernel */ if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) { err = -EINVAL; goto ack; } type = nlh->nlmsg_type; if (type == NFNL_MSG_BATCH_BEGIN) { /* Malformed: Batch begin twice */ success = false; goto done; } else if (type == NFNL_MSG_BATCH_END) { done = true; goto done; } else if (type < NLMSG_MIN_TYPE) { err = -EINVAL; goto ack; } /* We only accept a batch with messages for the same * subsystem. */ if (NFNL_SUBSYS_ID(type) != subsys_id) { err = -EINVAL; goto ack; } nc = nfnetlink_find_client(type, ss); if (!nc) { err = -EINVAL; goto ack; } { int min_len = nlmsg_total_size(sizeof(struct nfgenmsg)); u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type); struct nlattr *cda[ss->cb[cb_id].attr_count + 1]; struct nlattr *attr = (void *)nlh + min_len; int attrlen = nlh->nlmsg_len - min_len; err = nla_parse(cda, ss->cb[cb_id].attr_count, attr, attrlen, ss->cb[cb_id].policy); if (err < 0) goto ack; if (nc->call_batch) { err = nc->call_batch(net->nfnl, skb, nlh, (const struct nlattr **)cda); } /* The lock was released to autoload some module, we * have to abort and start from scratch using the * original skb. */ if (err == -EAGAIN) { ss->abort(skb); nfnl_unlock(subsys_id); kfree_skb(nskb); goto replay; } } ack: if (nlh->nlmsg_flags & NLM_F_ACK || err) { /* We don't stop processing the batch on errors, thus, * userspace gets all the errors that the batch * triggers. */ netlink_ack(skb, nlh, err); if (err) success = false; } msglen = NLMSG_ALIGN(nlh->nlmsg_len); if (msglen > skb->len) msglen = skb->len; skb_pull(skb, msglen); } done: if (success && done) ss->commit(skb); else ss->abort(skb); nfnl_unlock(subsys_id); kfree_skb(nskb); }