These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / sched / cls_bpf.c
index c0b86f2..5faaa54 100644 (file)
@@ -38,6 +38,7 @@ struct cls_bpf_prog {
        struct bpf_prog *filter;
        struct list_head link;
        struct tcf_result res;
+       bool exts_integrated;
        struct tcf_exts exts;
        u32 handle;
        union {
@@ -52,6 +53,7 @@ struct cls_bpf_prog {
 
 static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = {
        [TCA_BPF_CLASSID]       = { .type = NLA_U32 },
+       [TCA_BPF_FLAGS]         = { .type = NLA_U32 },
        [TCA_BPF_FD]            = { .type = NLA_U32 },
        [TCA_BPF_NAME]          = { .type = NLA_NUL_STRING, .len = CLS_BPF_NAME_LEN },
        [TCA_BPF_OPS_LEN]       = { .type = NLA_U16 },
@@ -59,11 +61,30 @@ static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = {
                                    .len = sizeof(struct sock_filter) * BPF_MAXINSNS },
 };
 
+static int cls_bpf_exec_opcode(int code)
+{
+       switch (code) {
+       case TC_ACT_OK:
+       case TC_ACT_SHOT:
+       case TC_ACT_STOLEN:
+       case TC_ACT_REDIRECT:
+       case TC_ACT_UNSPEC:
+               return code;
+       default:
+               return TC_ACT_UNSPEC;
+       }
+}
+
 static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
                            struct tcf_result *res)
 {
        struct cls_bpf_head *head = rcu_dereference_bh(tp->root);
        struct cls_bpf_prog *prog;
+#ifdef CONFIG_NET_CLS_ACT
+       bool at_ingress = G_TC_AT(skb->tc_verd) & AT_INGRESS;
+#else
+       bool at_ingress = false;
+#endif
        int ret = -1;
 
        if (unlikely(!skb_mac_header_was_set(skb)))
@@ -72,7 +93,28 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
        /* Needed here for accessing maps. */
        rcu_read_lock();
        list_for_each_entry_rcu(prog, &head->plist, link) {
-               int filter_res = BPF_PROG_RUN(prog->filter, skb);
+               int filter_res;
+
+               qdisc_skb_cb(skb)->tc_classid = prog->res.classid;
+
+               if (at_ingress) {
+                       /* It is safe to push/pull even if skb_shared() */
+                       __skb_push(skb, skb->mac_len);
+                       filter_res = BPF_PROG_RUN(prog->filter, skb);
+                       __skb_pull(skb, skb->mac_len);
+               } else {
+                       filter_res = BPF_PROG_RUN(prog->filter, skb);
+               }
+
+               if (prog->exts_integrated) {
+                       res->class = prog->res.class;
+                       res->classid = qdisc_skb_cb(skb)->tc_classid;
+
+                       ret = cls_bpf_exec_opcode(filter_res);
+                       if (ret == TC_ACT_UNSPEC)
+                               continue;
+                       break;
+               }
 
                if (filter_res == 0)
                        continue;
@@ -181,8 +223,7 @@ static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle)
        return ret;
 }
 
-static int cls_bpf_prog_from_ops(struct nlattr **tb,
-                                struct cls_bpf_prog *prog, u32 classid)
+static int cls_bpf_prog_from_ops(struct nlattr **tb, struct cls_bpf_prog *prog)
 {
        struct sock_filter *bpf_ops;
        struct sock_fprog_kern fprog_tmp;
@@ -216,15 +257,13 @@ static int cls_bpf_prog_from_ops(struct nlattr **tb,
        prog->bpf_ops = bpf_ops;
        prog->bpf_num_ops = bpf_num_ops;
        prog->bpf_name = NULL;
-
        prog->filter = fp;
-       prog->res.classid = classid;
 
        return 0;
 }
 
-static int cls_bpf_prog_from_efd(struct nlattr **tb,
-                                struct cls_bpf_prog *prog, u32 classid)
+static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog,
+                                const struct tcf_proto *tp)
 {
        struct bpf_prog *fp;
        char *name = NULL;
@@ -254,9 +293,10 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb,
        prog->bpf_ops = NULL;
        prog->bpf_fd = bpf_fd;
        prog->bpf_name = name;
-
        prog->filter = fp;
-       prog->res.classid = classid;
+
+       if (fp->dst_needed)
+               netif_keep_dst(qdisc_dev(tp->q));
 
        return 0;
 }
@@ -266,16 +306,13 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
                                   unsigned long base, struct nlattr **tb,
                                   struct nlattr *est, bool ovr)
 {
+       bool is_bpf, is_ebpf, have_exts = false;
        struct tcf_exts exts;
-       bool is_bpf, is_ebpf;
-       u32 classid;
        int ret;
 
        is_bpf = tb[TCA_BPF_OPS_LEN] && tb[TCA_BPF_OPS];
        is_ebpf = tb[TCA_BPF_FD];
-
-       if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf) ||
-           !tb[TCA_BPF_CLASSID])
+       if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf))
                return -EINVAL;
 
        tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE);
@@ -283,18 +320,32 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
        if (ret < 0)
                return ret;
 
-       classid = nla_get_u32(tb[TCA_BPF_CLASSID]);
+       if (tb[TCA_BPF_FLAGS]) {
+               u32 bpf_flags = nla_get_u32(tb[TCA_BPF_FLAGS]);
+
+               if (bpf_flags & ~TCA_BPF_FLAG_ACT_DIRECT) {
+                       tcf_exts_destroy(&exts);
+                       return -EINVAL;
+               }
+
+               have_exts = bpf_flags & TCA_BPF_FLAG_ACT_DIRECT;
+       }
+
+       prog->exts_integrated = have_exts;
 
-       ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog, classid) :
-                      cls_bpf_prog_from_efd(tb, prog, classid);
+       ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog) :
+                      cls_bpf_prog_from_efd(tb, prog, tp);
        if (ret < 0) {
                tcf_exts_destroy(&exts);
                return ret;
        }
 
-       tcf_bind_filter(tp, &prog->res, base);
-       tcf_exts_change(tp, &prog->exts, &exts);
+       if (tb[TCA_BPF_CLASSID]) {
+               prog->res.classid = nla_get_u32(tb[TCA_BPF_CLASSID]);
+               tcf_bind_filter(tp, &prog->res, base);
+       }
 
+       tcf_exts_change(tp, &prog->exts, &exts);
        return 0;
 }
 
@@ -415,6 +466,7 @@ static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
 {
        struct cls_bpf_prog *prog = (struct cls_bpf_prog *) fh;
        struct nlattr *nest;
+       u32 bpf_flags = 0;
        int ret;
 
        if (prog == NULL)
@@ -426,7 +478,8 @@ static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
        if (nest == NULL)
                goto nla_put_failure;
 
-       if (nla_put_u32(skb, TCA_BPF_CLASSID, prog->res.classid))
+       if (prog->res.classid &&
+           nla_put_u32(skb, TCA_BPF_CLASSID, prog->res.classid))
                goto nla_put_failure;
 
        if (cls_bpf_is_ebpf(prog))
@@ -439,6 +492,11 @@ static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
        if (tcf_exts_dump(skb, &prog->exts) < 0)
                goto nla_put_failure;
 
+       if (prog->exts_integrated)
+               bpf_flags |= TCA_BPF_FLAG_ACT_DIRECT;
+       if (bpf_flags && nla_put_u32(skb, TCA_BPF_FLAGS, bpf_flags))
+               goto nla_put_failure;
+
        nla_nest_end(skb, nest);
 
        if (tcf_exts_dump_stats(skb, &prog->exts) < 0)