Exemplo n.º 1
0
static void nfnetlink_rcv(struct sk_buff *skb)
{
	struct nlmsghdr *nlh = nlmsg_hdr(skb);
	int msglen;

	if (nlh->nlmsg_len < NLMSG_HDRLEN ||
	    skb->len < nlh->nlmsg_len)
		return;

	if (!netlink_net_capable(skb, CAP_NET_ADMIN)) {
		netlink_ack(skb, nlh, -EPERM);
		return;
	}

	if (nlh->nlmsg_type == NFNL_MSG_BATCH_BEGIN) {
		struct nfgenmsg *nfgenmsg;

		msglen = NLMSG_ALIGN(nlh->nlmsg_len);
		if (msglen > skb->len)
			msglen = skb->len;

		if (nlh->nlmsg_len < NLMSG_HDRLEN ||
		    skb->len < NLMSG_HDRLEN + sizeof(struct nfgenmsg))
			return;

		nfgenmsg = nlmsg_data(nlh);
		skb_pull(skb, msglen);
		nfnetlink_rcv_batch(skb, nlh, nfgenmsg->res_id);
	} else {
		netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
	}
}
Exemplo n.º 2
0
static int tipc_nl_compat_recv(struct sk_buff *skb, struct genl_info *info)
{
	int err;
	int len;
	struct tipc_nl_compat_msg msg;
	struct nlmsghdr *req_nlh;
	struct nlmsghdr *rep_nlh;
	struct tipc_genlmsghdr *req_userhdr = info->userhdr;
	struct net *net = genl_info_net(info);

	memset(&msg, 0, sizeof(msg));

	req_nlh = (struct nlmsghdr *)skb->data;
	msg.req = nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN;
	msg.cmd = req_userhdr->cmd;
	msg.dst_sk = info->dst_sk;

	if ((msg.cmd & 0xC000) && (!netlink_net_capable(skb, CAP_NET_ADMIN))) {
		msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_NET_ADMIN);
		err = -EACCES;
		goto send;
	}

	len = nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN);
	if (TLV_GET_LEN(msg.req) && !TLV_OK(msg.req, len)) {
		msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED);
		err = -EOPNOTSUPP;
		goto send;
	}

	err = tipc_nl_compat_handle(&msg);
	if (err == -EOPNOTSUPP)
		msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED);
	else if (err == -EINVAL)
		msg.rep = tipc_get_err_tlv(TIPC_CFG_TLV_ERROR);
send:
	if (!msg.rep)
		return err;

	len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
	skb_push(msg.rep, len);
	rep_nlh = nlmsg_hdr(msg.rep);
	memcpy(rep_nlh, info->nlhdr, len);
	rep_nlh->nlmsg_len = msg.rep->len;
	genlmsg_unicast(net, msg.rep, NETLINK_CB(skb).portid);

	return err;
}
Exemplo n.º 3
0
static int inet_diag_bc_audit(const struct nlattr *attr,
                              const struct sk_buff *skb)
{
    bool net_admin = netlink_net_capable(skb, CAP_NET_ADMIN);
    const void *bytecode, *bc;
    int bytecode_len, len;

    if (!attr || nla_len(attr) < sizeof(struct inet_diag_bc_op))
        return -EINVAL;

    bytecode = bc = nla_data(attr);
    len = bytecode_len = nla_len(attr);

    while (len > 0) {
        int min_len = sizeof(struct inet_diag_bc_op);
        const struct inet_diag_bc_op *op = bc;

        switch (op->code) {
        case INET_DIAG_BC_S_COND:
        case INET_DIAG_BC_D_COND:
            if (!valid_hostcond(bc, len, &min_len))
                return -EINVAL;
            break;
        case INET_DIAG_BC_DEV_COND:
            if (!valid_devcond(bc, len, &min_len))
                return -EINVAL;
            break;
        case INET_DIAG_BC_S_GE:
        case INET_DIAG_BC_S_LE:
        case INET_DIAG_BC_D_GE:
        case INET_DIAG_BC_D_LE:
            if (!valid_port_comparison(bc, len, &min_len))
                return -EINVAL;
            break;
        case INET_DIAG_BC_MARK_COND:
            if (!net_admin)
                return -EPERM;
            if (!valid_markcond(bc, len, &min_len))
                return -EINVAL;
            break;
        case INET_DIAG_BC_AUTO:
        case INET_DIAG_BC_JMP:
        case INET_DIAG_BC_NOP:
            break;
        default:
            return -EINVAL;
        }

        if (op->code != INET_DIAG_BC_NOP) {
            if (op->no < min_len || op->no > len + 4 || op->no & 3)
                return -EINVAL;
            if (op->no < len &&
                    !valid_cc(bytecode, bytecode_len, len - op->no))
                return -EINVAL;
        }

        if (op->yes < min_len || op->yes > len + 4 || op->yes & 3)
            return -EINVAL;
        bc  += op->yes;
        len -= op->yes;
    }
    return len == 0 ? 0 : -EINVAL;
}
Exemplo n.º 4
0
int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
                      struct sk_buff *skb, const struct inet_diag_req_v2 *req,
                      struct user_namespace *user_ns,
                      u32 portid, u32 seq, u16 nlmsg_flags,
                      const struct nlmsghdr *unlh,
                      bool net_admin)
{
    const struct tcp_congestion_ops *ca_ops;
    const struct inet_diag_handler *handler;
    int ext = req->idiag_ext;
    struct inet_diag_msg *r;
    struct nlmsghdr  *nlh;
    struct nlattr *attr;
    void *info = NULL;

    handler = inet_diag_table[req->sdiag_protocol];
    BUG_ON(!handler);

    nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r),
                    nlmsg_flags);
    if (!nlh)
        return -EMSGSIZE;

    r = nlmsg_data(nlh);
    BUG_ON(!sk_fullsock(sk));

    inet_diag_msg_common_fill(r, sk);
    r->idiag_state = sk->sk_state;
    r->idiag_timer = 0;
    r->idiag_retrans = 0;

    if (inet_diag_msg_attrs_fill(sk, skb, r, ext, user_ns, net_admin))
        goto errout;

    if (ext & (1 << (INET_DIAG_MEMINFO - 1))) {
        struct inet_diag_meminfo minfo = {
            .idiag_rmem = sk_rmem_alloc_get(sk),
            .idiag_wmem = sk->sk_wmem_queued,
            .idiag_fmem = sk->sk_forward_alloc,
            .idiag_tmem = sk_wmem_alloc_get(sk),
        };

        if (nla_put(skb, INET_DIAG_MEMINFO, sizeof(minfo), &minfo) < 0)
            goto errout;
    }

    if (ext & (1 << (INET_DIAG_SKMEMINFO - 1)))
        if (sock_diag_put_meminfo(sk, skb, INET_DIAG_SKMEMINFO))
            goto errout;

    if (!icsk) {
        handler->idiag_get_info(sk, r, NULL);
        goto out;
    }

    if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
            icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
            icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
        r->idiag_timer = 1;
        r->idiag_retrans = icsk->icsk_retransmits;
        r->idiag_expires =
            jiffies_to_msecs(icsk->icsk_timeout - jiffies);
    } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
        r->idiag_timer = 4;
        r->idiag_retrans = icsk->icsk_probes_out;
        r->idiag_expires =
            jiffies_to_msecs(icsk->icsk_timeout - jiffies);
    } else if (timer_pending(&sk->sk_timer)) {
        r->idiag_timer = 2;
        r->idiag_retrans = icsk->icsk_probes_out;
        r->idiag_expires =
            jiffies_to_msecs(sk->sk_timer.expires - jiffies);
    } else {
        r->idiag_timer = 0;
        r->idiag_expires = 0;
    }

    if ((ext & (1 << (INET_DIAG_INFO - 1))) && handler->idiag_info_size) {
        attr = nla_reserve_64bit(skb, INET_DIAG_INFO,
                                 handler->idiag_info_size,
                                 INET_DIAG_PAD);
        if (!attr)
            goto errout;

        info = nla_data(attr);
    }

    if (ext & (1 << (INET_DIAG_CONG - 1))) {
        int err = 0;

        rcu_read_lock();
        ca_ops = READ_ONCE(icsk->icsk_ca_ops);
        if (ca_ops)
            err = nla_put_string(skb, INET_DIAG_CONG, ca_ops->name);
        rcu_read_unlock();
        if (err < 0)
            goto errout;
    }

    handler->idiag_get_info(sk, r, info);

    if (sk->sk_state < TCP_TIME_WAIT) {
        union tcp_cc_info info;
        size_t sz = 0;
        int attr;

        rcu_read_lock();
        ca_ops = READ_ONCE(icsk->icsk_ca_ops);
        if (ca_ops && ca_ops->get_info)
            sz = ca_ops->get_info(sk, ext, &attr, &info);
        rcu_read_unlock();
        if (sz && nla_put(skb, attr, sz, &info) < 0)
            goto errout;
    }

out:
    nlmsg_end(skb, nlh);
    return 0;

errout:
    nlmsg_cancel(skb, nlh);
    return -EMSGSIZE;
}
EXPORT_SYMBOL_GPL(inet_sk_diag_fill);

static int inet_csk_diag_fill(struct sock *sk,
                              struct sk_buff *skb,
                              const struct inet_diag_req_v2 *req,
                              struct user_namespace *user_ns,
                              u32 portid, u32 seq, u16 nlmsg_flags,
                              const struct nlmsghdr *unlh,
                              bool net_admin)
{
    return inet_sk_diag_fill(sk, inet_csk(sk), skb, req, user_ns,
                             portid, seq, nlmsg_flags, unlh, net_admin);
}

static int inet_twsk_diag_fill(struct sock *sk,
                               struct sk_buff *skb,
                               u32 portid, u32 seq, u16 nlmsg_flags,
                               const struct nlmsghdr *unlh)
{
    struct inet_timewait_sock *tw = inet_twsk(sk);
    struct inet_diag_msg *r;
    struct nlmsghdr *nlh;
    long tmo;

    nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r),
                    nlmsg_flags);
    if (!nlh)
        return -EMSGSIZE;

    r = nlmsg_data(nlh);
    BUG_ON(tw->tw_state != TCP_TIME_WAIT);

    tmo = tw->tw_timer.expires - jiffies;
    if (tmo < 0)
        tmo = 0;

    inet_diag_msg_common_fill(r, sk);
    r->idiag_retrans      = 0;

    r->idiag_state	      = tw->tw_substate;
    r->idiag_timer	      = 3;
    r->idiag_expires      = jiffies_to_msecs(tmo);
    r->idiag_rqueue	      = 0;
    r->idiag_wqueue	      = 0;
    r->idiag_uid	      = 0;
    r->idiag_inode	      = 0;

    nlmsg_end(skb, nlh);
    return 0;
}

static int inet_req_diag_fill(struct sock *sk, struct sk_buff *skb,
                              u32 portid, u32 seq, u16 nlmsg_flags,
                              const struct nlmsghdr *unlh, bool net_admin)
{
    struct request_sock *reqsk = inet_reqsk(sk);
    struct inet_diag_msg *r;
    struct nlmsghdr *nlh;
    long tmo;

    nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r),
                    nlmsg_flags);
    if (!nlh)
        return -EMSGSIZE;

    r = nlmsg_data(nlh);
    inet_diag_msg_common_fill(r, sk);
    r->idiag_state = TCP_SYN_RECV;
    r->idiag_timer = 1;
    r->idiag_retrans = reqsk->num_retrans;

    BUILD_BUG_ON(offsetof(struct inet_request_sock, ir_cookie) !=
                 offsetof(struct sock, sk_cookie));

    tmo = inet_reqsk(sk)->rsk_timer.expires - jiffies;
    r->idiag_expires = (tmo >= 0) ? jiffies_to_msecs(tmo) : 0;
    r->idiag_rqueue	= 0;
    r->idiag_wqueue	= 0;
    r->idiag_uid	= 0;
    r->idiag_inode	= 0;

    if (net_admin && nla_put_u32(skb, INET_DIAG_MARK,
                                 inet_rsk(reqsk)->ir_mark))
        return -EMSGSIZE;

    nlmsg_end(skb, nlh);
    return 0;
}

static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
                        const struct inet_diag_req_v2 *r,
                        struct user_namespace *user_ns,
                        u32 portid, u32 seq, u16 nlmsg_flags,
                        const struct nlmsghdr *unlh, bool net_admin)
{
    if (sk->sk_state == TCP_TIME_WAIT)
        return inet_twsk_diag_fill(sk, skb, portid, seq,
                                   nlmsg_flags, unlh);

    if (sk->sk_state == TCP_NEW_SYN_RECV)
        return inet_req_diag_fill(sk, skb, portid, seq,
                                  nlmsg_flags, unlh, net_admin);

    return inet_csk_diag_fill(sk, skb, r, user_ns, portid, seq,
                              nlmsg_flags, unlh, net_admin);
}

struct sock *inet_diag_find_one_icsk(struct net *net,
                                     struct inet_hashinfo *hashinfo,
                                     const struct inet_diag_req_v2 *req)
{
    struct sock *sk;

    rcu_read_lock();
    if (req->sdiag_family == AF_INET)
        sk = inet_lookup(net, hashinfo, NULL, 0, req->id.idiag_dst[0],
                         req->id.idiag_dport, req->id.idiag_src[0],
                         req->id.idiag_sport, req->id.idiag_if);
#if IS_ENABLED(CONFIG_IPV6)
    else if (req->sdiag_family == AF_INET6) {
        if (ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_dst) &&
                ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_src))
            sk = inet_lookup(net, hashinfo, NULL, 0, req->id.idiag_dst[3],
                             req->id.idiag_dport, req->id.idiag_src[3],
                             req->id.idiag_sport, req->id.idiag_if);
        else
            sk = inet6_lookup(net, hashinfo, NULL, 0,
                              (struct in6_addr *)req->id.idiag_dst,
                              req->id.idiag_dport,
                              (struct in6_addr *)req->id.idiag_src,
                              req->id.idiag_sport,
                              req->id.idiag_if);
    }
#endif
    else {
        rcu_read_unlock();
        return ERR_PTR(-EINVAL);
    }
    rcu_read_unlock();
    if (!sk)
        return ERR_PTR(-ENOENT);

    if (sock_diag_check_cookie(sk, req->id.idiag_cookie)) {
        sock_gen_put(sk);
        return ERR_PTR(-ENOENT);
    }

    return sk;
}
EXPORT_SYMBOL_GPL(inet_diag_find_one_icsk);

int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
                            struct sk_buff *in_skb,
                            const struct nlmsghdr *nlh,
                            const struct inet_diag_req_v2 *req)
{
    struct net *net = sock_net(in_skb->sk);
    struct sk_buff *rep;
    struct sock *sk;
    int err;

    sk = inet_diag_find_one_icsk(net, hashinfo, req);
    if (IS_ERR(sk))
        return PTR_ERR(sk);

    rep = nlmsg_new(inet_sk_attr_size(), GFP_KERNEL);
    if (!rep) {
        err = -ENOMEM;
        goto out;
    }

    err = sk_diag_fill(sk, rep, req,
                       sk_user_ns(NETLINK_CB(in_skb).sk),
                       NETLINK_CB(in_skb).portid,
                       nlh->nlmsg_seq, 0, nlh,
                       netlink_net_capable(in_skb, CAP_NET_ADMIN));
    if (err < 0) {
        WARN_ON(err == -EMSGSIZE);
        nlmsg_free(rep);
        goto out;
    }
    err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid,
                          MSG_DONTWAIT);
    if (err > 0)
        err = 0;

out:
    if (sk)
        sock_gen_put(sk);

    return err;
}
EXPORT_SYMBOL_GPL(inet_diag_dump_one_icsk);

static int inet_diag_cmd_exact(int cmd, struct sk_buff *in_skb,
                               const struct nlmsghdr *nlh,
                               const struct inet_diag_req_v2 *req)
{
    const struct inet_diag_handler *handler;
    int err;

    handler = inet_diag_lock_handler(req->sdiag_protocol);
    if (IS_ERR(handler))
        err = PTR_ERR(handler);
    else if (cmd == SOCK_DIAG_BY_FAMILY)
        err = handler->dump_one(in_skb, nlh, req);
    else if (cmd == SOCK_DESTROY && handler->destroy)
        err = handler->destroy(in_skb, req);
    else
        err = -EOPNOTSUPP;
    inet_diag_unlock_handler(handler);

    return err;
}

static int bitstring_match(const __be32 *a1, const __be32 *a2, int bits)
{
    int words = bits >> 5;

    bits &= 0x1f;

    if (words) {
        if (memcmp(a1, a2, words << 2))
            return 0;
    }
    if (bits) {
        __be32 w1, w2;
        __be32 mask;

        w1 = a1[words];
        w2 = a2[words];

        mask = htonl((0xffffffff) << (32 - bits));

        if ((w1 ^ w2) & mask)
            return 0;
    }

    return 1;
}