net: diag: allow socket bytecode filters to match socket marks
authorLorenzo Colitti <lorenzo@google.com>
Wed, 24 Aug 2016 06:46:26 +0000 (15:46 +0900)
committerLorenzo Colitti <lorenzo@google.com>
Sun, 18 Sep 2016 16:51:21 +0000 (01:51 +0900)
This allows a privileged process to filter by socket mark when
dumping sockets via INET_DIAG_BY_FAMILY. This is useful on
systems that use mark-based routing such as Android.

The ability to filter socket marks requires CAP_NET_ADMIN, which
is consistent with other privileged operations allowed by the
SOCK_DIAG interface such as the ability to destroy sockets and
the ability to inspect BPF filters attached to packet sockets.

[cherry-pick of a52e95abf772b43c9226e9a72d3c1353903ba96f]

Change-Id: I8b90b814264d9808bda050cdba8f104943bdb9a8
Tested: https://android-review.googlesource.com/261350
Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Acked-by: David Ahern <dsa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/inet_diag.h
net/ipv4/inet_diag.c

index b49bfb6ea22a90d760186cb06b579f76d6398ce8..35435c348c606123c29719c340b4c49238afa653 100644 (file)
@@ -73,6 +73,7 @@ enum {
        INET_DIAG_BC_S_COND,
        INET_DIAG_BC_D_COND,
        INET_DIAG_BC_DEV_COND,   /* u32 ifindex */
+       INET_DIAG_BC_MARK_COND,
 };
 
 struct inet_diag_hostcond {
@@ -82,6 +83,11 @@ struct inet_diag_hostcond {
        __be32  addr[0];
 };
 
+struct inet_diag_markcond {
+       __u32 mark;
+       __u32 mask;
+};
+
 /* Base info structure. It contains socket identity (addrs/ports/cookie)
  * and, alas, the information shown by netstat. */
 struct inet_diag_msg {
index 75f6d4bde2733811df4da2e4223804ca8004b059..5202149cbce0cf0d971e448a2eae52c45cdc95c1 100644 (file)
@@ -45,6 +45,7 @@ struct inet_diag_entry {
        u16 family;
        u16 userlocks;
        u32 ifindex;
+       u32 mark;
 };
 
 static DEFINE_MUTEX(inet_diag_table_mutex);
@@ -561,6 +562,14 @@ static int inet_diag_bc_run(const struct nlattr *_bc,
                                yes = 0;
                        break;
                }
+               case INET_DIAG_BC_MARK_COND: {
+                       struct inet_diag_markcond *cond;
+
+                       cond = (struct inet_diag_markcond *)(op + 1);
+                       if ((entry->mark & cond->mask) != cond->mark)
+                               yes = 0;
+                       break;
+               }
                }
 
                if (yes) {
@@ -605,6 +614,12 @@ int inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk)
        entry.dport = ntohs(inet->inet_dport);
        entry.ifindex = sk->sk_bound_dev_if;
        entry.userlocks = sk_fullsock(sk) ? sk->sk_userlocks : 0;
+       if (sk_fullsock(sk))
+               entry.mark = sk->sk_mark;
+       else if (sk->sk_state == TCP_NEW_SYN_RECV)
+               entry.mark = inet_rsk(inet_reqsk(sk))->ir_mark;
+       else
+               entry.mark = 0;
 
        return inet_diag_bc_run(bc, &entry);
 }
@@ -687,8 +702,17 @@ static bool valid_port_comparison(const struct inet_diag_bc_op *op,
        return true;
 }
 
-static int inet_diag_bc_audit(const struct nlattr *attr)
+static bool valid_markcond(const struct inet_diag_bc_op *op, int len,
+                          int *min_len)
 {
+       *min_len += sizeof(struct inet_diag_markcond);
+       return len >= *min_len;
+}
+
+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;
 
@@ -719,6 +743,12 @@ static int inet_diag_bc_audit(const struct nlattr *attr)
                        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:
@@ -1011,7 +1041,7 @@ static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
 
                        attr = nlmsg_find_attr(nlh, hdrlen,
                                               INET_DIAG_REQ_BYTECODE);
-                       err = inet_diag_bc_audit(attr);
+                       err = inet_diag_bc_audit(attr, skb);
                        if (err)
                                return err;
                }
@@ -1042,7 +1072,7 @@ static int inet_diag_handler_cmd(struct sk_buff *skb, struct nlmsghdr *h)
 
                        attr = nlmsg_find_attr(h, hdrlen,
                                               INET_DIAG_REQ_BYTECODE);
-                       err = inet_diag_bc_audit(attr);
+                       err = inet_diag_bc_audit(attr, skb);
                        if (err)
                                return err;
                }