/* 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; } }
/* Process one complete nfnetlink message. */ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { struct nfnl_callback *nc; struct nfnetlink_subsystem *ss; int type, err; if (security_netlink_recv(skb, CAP_NET_ADMIN)) return -EPERM; /* All the messages must at least contain nfgenmsg */ if (nlh->nlmsg_len < NLMSG_SPACE(sizeof(struct nfgenmsg))) return 0; type = nlh->nlmsg_type; ss = nfnetlink_get_subsys(type); if (!ss) { #ifdef CONFIG_KMOD /* don't call nfnl_unlock, since it would reenter * with further packet processing */ __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; { u_int16_t attr_count = ss->cb[NFNL_MSG_TYPE(nlh->nlmsg_type)].attr_count; struct nfattr *cda[attr_count]; memset(cda, 0, sizeof(struct nfattr *) * attr_count); err = nfnetlink_check_attributes(ss, nlh, cda); if (err < 0) return err; return nc->call(nfnl, skb, nlh, cda); } }
static int nfnetlink_bind(int group) { const struct nfnetlink_subsystem *ss; int type = nfnl_group2type[group]; rcu_read_lock(); ss = nfnetlink_get_subsys(type); rcu_read_unlock(); if (!ss) request_module("nfnetlink-subsys-%d", type); 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; } }
/* Process one complete nfnetlink message. */ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp) { struct nfnl_callback *nc; struct nfnetlink_subsystem *ss; int type, err = 0; DEBUGP("entered; subsys=%u, msgtype=%u\n", NFNL_SUBSYS_ID(nlh->nlmsg_type), NFNL_MSG_TYPE(nlh->nlmsg_type)); if (security_netlink_recv(skb, CAP_NET_ADMIN)) { DEBUGP("missing CAP_NET_ADMIN\n"); *errp = -EPERM; return -1; } /* Only requests are handled by kernel now. */ if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) { DEBUGP("received non-request message\n"); return 0; } /* All the messages must at least contain nfgenmsg */ if (nlh->nlmsg_len < NLMSG_SPACE(sizeof(struct nfgenmsg))) { DEBUGP("received message was too short\n"); return 0; } type = nlh->nlmsg_type; ss = nfnetlink_get_subsys(type); if (!ss) { #ifdef CONFIG_KMOD /* don't call nfnl_shunlock, since it would reenter * with further packet processing */ up(&nfnl_sem); request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type)); nfnl_shlock(); ss = nfnetlink_get_subsys(type); if (!ss) #endif goto err_inval; } nc = nfnetlink_find_client(type, ss); if (!nc) { DEBUGP("unable to find client for type %d\n", type); goto err_inval; } { u_int16_t attr_count = ss->cb[NFNL_MSG_TYPE(nlh->nlmsg_type)].attr_count; struct nfattr *cda[attr_count]; memset(cda, 0, sizeof(struct nfattr *) * attr_count); err = nfnetlink_check_attributes(ss, nlh, cda); if (err < 0) goto err_inval; DEBUGP("calling handler\n"); err = nc->call(nfnl, skb, nlh, cda, errp); *errp = err; return err; } err_inval: DEBUGP("returning -EINVAL\n"); *errp = -EINVAL; return -1; }