Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
[cascardo/linux.git] / net / sched / cls_flower.c
index 5060801..f6f40fb 100644 (file)
 #include <net/ip.h>
 #include <net/flow_dissector.h>
 
+#include <net/dst.h>
+#include <net/dst_metadata.h>
+
 struct fl_flow_key {
        int     indev_ifindex;
        struct flow_dissector_key_control control;
+       struct flow_dissector_key_control enc_control;
        struct flow_dissector_key_basic basic;
        struct flow_dissector_key_eth_addrs eth;
-       struct flow_dissector_key_addrs ipaddrs;
+       struct flow_dissector_key_vlan vlan;
        union {
                struct flow_dissector_key_ipv4_addrs ipv4;
                struct flow_dissector_key_ipv6_addrs ipv6;
        };
        struct flow_dissector_key_ports tp;
+       struct flow_dissector_key_keyid enc_key_id;
+       union {
+               struct flow_dissector_key_ipv4_addrs enc_ipv4;
+               struct flow_dissector_key_ipv6_addrs enc_ipv6;
+       };
 } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
 
 struct fl_flow_mask_range {
@@ -123,11 +132,31 @@ static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp,
        struct cls_fl_filter *f;
        struct fl_flow_key skb_key;
        struct fl_flow_key skb_mkey;
+       struct ip_tunnel_info *info;
 
        if (!atomic_read(&head->ht.nelems))
                return -1;
 
        fl_clear_masked_range(&skb_key, &head->mask);
+
+       info = skb_tunnel_info(skb);
+       if (info) {
+               struct ip_tunnel_key *key = &info->key;
+
+               switch (ip_tunnel_info_af(info)) {
+               case AF_INET:
+                       skb_key.enc_ipv4.src = key->u.ipv4.src;
+                       skb_key.enc_ipv4.dst = key->u.ipv4.dst;
+                       break;
+               case AF_INET6:
+                       skb_key.enc_ipv6.src = key->u.ipv6.src;
+                       skb_key.enc_ipv6.dst = key->u.ipv6.dst;
+                       break;
+               }
+
+               skb_key.enc_key_id.keyid = tunnel_id_to_key32(key->tun_id);
+       }
+
        skb_key.indev_ifindex = skb->skb_iif;
        /* skb_flow_dissect() does not set n_proto in case an unknown protocol,
         * so do it rather here.
@@ -212,7 +241,8 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
        tc.type = TC_SETUP_CLSFLOWER;
        tc.cls_flower = &offload;
 
-       err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, &tc);
+       err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol,
+                                           &tc);
 
        if (tc_skip_sw(flags))
                return err;
@@ -293,6 +323,22 @@ static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = {
        [TCA_FLOWER_KEY_TCP_DST]        = { .type = NLA_U16 },
        [TCA_FLOWER_KEY_UDP_SRC]        = { .type = NLA_U16 },
        [TCA_FLOWER_KEY_UDP_DST]        = { .type = NLA_U16 },
+       [TCA_FLOWER_KEY_VLAN_ID]        = { .type = NLA_U16 },
+       [TCA_FLOWER_KEY_VLAN_PRIO]      = { .type = NLA_U8 },
+       [TCA_FLOWER_KEY_VLAN_ETH_TYPE]  = { .type = NLA_U16 },
+       [TCA_FLOWER_KEY_ENC_KEY_ID]     = { .type = NLA_U32 },
+       [TCA_FLOWER_KEY_ENC_IPV4_SRC]   = { .type = NLA_U32 },
+       [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] = { .type = NLA_U32 },
+       [TCA_FLOWER_KEY_ENC_IPV4_DST]   = { .type = NLA_U32 },
+       [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NLA_U32 },
+       [TCA_FLOWER_KEY_ENC_IPV6_SRC]   = { .len = sizeof(struct in6_addr) },
+       [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) },
+       [TCA_FLOWER_KEY_ENC_IPV6_DST]   = { .len = sizeof(struct in6_addr) },
+       [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) },
+       [TCA_FLOWER_KEY_TCP_SRC_MASK]   = { .type = NLA_U16 },
+       [TCA_FLOWER_KEY_TCP_DST_MASK]   = { .type = NLA_U16 },
+       [TCA_FLOWER_KEY_UDP_SRC_MASK]   = { .type = NLA_U16 },
+       [TCA_FLOWER_KEY_UDP_DST_MASK]   = { .type = NLA_U16 },
 };
 
 static void fl_set_key_val(struct nlattr **tb,
@@ -308,9 +354,29 @@ static void fl_set_key_val(struct nlattr **tb,
                memcpy(mask, nla_data(tb[mask_type]), len);
 }
 
+static void fl_set_key_vlan(struct nlattr **tb,
+                           struct flow_dissector_key_vlan *key_val,
+                           struct flow_dissector_key_vlan *key_mask)
+{
+#define VLAN_PRIORITY_MASK     0x7
+
+       if (tb[TCA_FLOWER_KEY_VLAN_ID]) {
+               key_val->vlan_id =
+                       nla_get_u16(tb[TCA_FLOWER_KEY_VLAN_ID]) & VLAN_VID_MASK;
+               key_mask->vlan_id = VLAN_VID_MASK;
+       }
+       if (tb[TCA_FLOWER_KEY_VLAN_PRIO]) {
+               key_val->vlan_priority =
+                       nla_get_u8(tb[TCA_FLOWER_KEY_VLAN_PRIO]) &
+                       VLAN_PRIORITY_MASK;
+               key_mask->vlan_priority = VLAN_PRIORITY_MASK;
+       }
+}
+
 static int fl_set_key(struct net *net, struct nlattr **tb,
                      struct fl_flow_key *key, struct fl_flow_key *mask)
 {
+       __be16 ethertype;
 #ifdef CONFIG_NET_CLS_IND
        if (tb[TCA_FLOWER_INDEV]) {
                int err = tcf_change_indev(net, tb[TCA_FLOWER_INDEV]);
@@ -328,9 +394,20 @@ static int fl_set_key(struct net *net, struct nlattr **tb,
                       mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK,
                       sizeof(key->eth.src));
 
-       fl_set_key_val(tb, &key->basic.n_proto, TCA_FLOWER_KEY_ETH_TYPE,
-                      &mask->basic.n_proto, TCA_FLOWER_UNSPEC,
-                      sizeof(key->basic.n_proto));
+       if (tb[TCA_FLOWER_KEY_ETH_TYPE]) {
+               ethertype = nla_get_be16(tb[TCA_FLOWER_KEY_ETH_TYPE]);
+
+               if (ethertype == htons(ETH_P_8021Q)) {
+                       fl_set_key_vlan(tb, &key->vlan, &mask->vlan);
+                       fl_set_key_val(tb, &key->basic.n_proto,
+                                      TCA_FLOWER_KEY_VLAN_ETH_TYPE,
+                                      &mask->basic.n_proto, TCA_FLOWER_UNSPEC,
+                                      sizeof(key->basic.n_proto));
+               } else {
+                       key->basic.n_proto = ethertype;
+                       mask->basic.n_proto = cpu_to_be16(~0);
+               }
+       }
 
        if (key->basic.n_proto == htons(ETH_P_IP) ||
            key->basic.n_proto == htons(ETH_P_IPV6)) {
@@ -359,20 +436,54 @@ static int fl_set_key(struct net *net, struct nlattr **tb,
 
        if (key->basic.ip_proto == IPPROTO_TCP) {
                fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC,
-                              &mask->tp.src, TCA_FLOWER_UNSPEC,
+                              &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK,
                               sizeof(key->tp.src));
                fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST,
-                              &mask->tp.dst, TCA_FLOWER_UNSPEC,
+                              &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK,
                               sizeof(key->tp.dst));
        } else if (key->basic.ip_proto == IPPROTO_UDP) {
                fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC,
-                              &mask->tp.src, TCA_FLOWER_UNSPEC,
+                              &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK,
                               sizeof(key->tp.src));
                fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST,
-                              &mask->tp.dst, TCA_FLOWER_UNSPEC,
+                              &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK,
                               sizeof(key->tp.dst));
        }
 
+       if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] ||
+           tb[TCA_FLOWER_KEY_ENC_IPV4_DST]) {
+               key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
+               fl_set_key_val(tb, &key->enc_ipv4.src,
+                              TCA_FLOWER_KEY_ENC_IPV4_SRC,
+                              &mask->enc_ipv4.src,
+                              TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,
+                              sizeof(key->enc_ipv4.src));
+               fl_set_key_val(tb, &key->enc_ipv4.dst,
+                              TCA_FLOWER_KEY_ENC_IPV4_DST,
+                              &mask->enc_ipv4.dst,
+                              TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,
+                              sizeof(key->enc_ipv4.dst));
+       }
+
+       if (tb[TCA_FLOWER_KEY_ENC_IPV6_SRC] ||
+           tb[TCA_FLOWER_KEY_ENC_IPV6_DST]) {
+               key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
+               fl_set_key_val(tb, &key->enc_ipv6.src,
+                              TCA_FLOWER_KEY_ENC_IPV6_SRC,
+                              &mask->enc_ipv6.src,
+                              TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,
+                              sizeof(key->enc_ipv6.src));
+               fl_set_key_val(tb, &key->enc_ipv6.dst,
+                              TCA_FLOWER_KEY_ENC_IPV6_DST,
+                              &mask->enc_ipv6.dst,
+                              TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,
+                              sizeof(key->enc_ipv6.dst));
+       }
+
+       fl_set_key_val(tb, &key->enc_key_id.keyid, TCA_FLOWER_KEY_ENC_KEY_ID,
+                      &mask->enc_key_id.keyid, TCA_FLOWER_UNSPEC,
+                      sizeof(key->enc_key_id.keyid));
+
        return 0;
 }
 
@@ -404,12 +515,10 @@ static int fl_init_hashtable(struct cls_fl_head *head,
 
 #define FL_KEY_MEMBER_OFFSET(member) offsetof(struct fl_flow_key, member)
 #define FL_KEY_MEMBER_SIZE(member) (sizeof(((struct fl_flow_key *) 0)->member))
-#define FL_KEY_MEMBER_END_OFFSET(member)                                       \
-       (FL_KEY_MEMBER_OFFSET(member) + FL_KEY_MEMBER_SIZE(member))
 
-#define FL_KEY_IN_RANGE(mask, member)                                          \
-        (FL_KEY_MEMBER_OFFSET(member) <= (mask)->range.end &&                  \
-         FL_KEY_MEMBER_END_OFFSET(member) >= (mask)->range.start)
+#define FL_KEY_IS_MASKED(mask, member)                                         \
+       memchr_inv(((char *)mask) + FL_KEY_MEMBER_OFFSET(member),               \
+                  0, FL_KEY_MEMBER_SIZE(member))                               \
 
 #define FL_KEY_SET(keys, cnt, id, member)                                      \
        do {                                                                    \
@@ -418,9 +527,9 @@ static int fl_init_hashtable(struct cls_fl_head *head,
                cnt++;                                                          \
        } while(0);
 
-#define FL_KEY_SET_IF_IN_RANGE(mask, keys, cnt, id, member)                    \
+#define FL_KEY_SET_IF_MASKED(mask, keys, cnt, id, member)                      \
        do {                                                                    \
-               if (FL_KEY_IN_RANGE(mask, member))                              \
+               if (FL_KEY_IS_MASKED(mask, member))                             \
                        FL_KEY_SET(keys, cnt, id, member);                      \
        } while(0);
 
@@ -432,14 +541,16 @@ static void fl_init_dissector(struct cls_fl_head *head,
 
        FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_CONTROL, control);
        FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_BASIC, basic);
-       FL_KEY_SET_IF_IN_RANGE(mask, keys, cnt,
-                              FLOW_DISSECTOR_KEY_ETH_ADDRS, eth);
-       FL_KEY_SET_IF_IN_RANGE(mask, keys, cnt,
-                              FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4);
-       FL_KEY_SET_IF_IN_RANGE(mask, keys, cnt,
-                              FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6);
-       FL_KEY_SET_IF_IN_RANGE(mask, keys, cnt,
-                              FLOW_DISSECTOR_KEY_PORTS, tp);
+       FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt,
+                            FLOW_DISSECTOR_KEY_ETH_ADDRS, eth);
+       FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt,
+                            FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4);
+       FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt,
+                            FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6);
+       FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt,
+                            FLOW_DISSECTOR_KEY_PORTS, tp);
+       FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt,
+                            FLOW_DISSECTOR_KEY_VLAN, vlan);
 
        skb_flow_dissector_init(&head->dissector, keys, cnt);
 }
@@ -478,10 +589,12 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp,
        struct tcf_exts e;
        int err;
 
-       tcf_exts_init(&e, TCA_FLOWER_ACT, 0);
-       err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
+       err = tcf_exts_init(&e, TCA_FLOWER_ACT, 0);
        if (err < 0)
                return err;
+       err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
+       if (err < 0)
+               goto errout;
 
        if (tb[TCA_FLOWER_CLASSID]) {
                f->res.classid = nla_get_u32(tb[TCA_FLOWER_CLASSID]);
@@ -550,7 +663,9 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
        if (!fnew)
                return -ENOBUFS;
 
-       tcf_exts_init(&fnew->exts, TCA_FLOWER_ACT, 0);
+       err = tcf_exts_init(&fnew->exts, TCA_FLOWER_ACT, 0);
+       if (err < 0)
+               goto errout;
 
        if (!handle) {
                handle = fl_grab_new_handle(tp, head);
@@ -614,6 +729,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
        return 0;
 
 errout:
+       tcf_exts_destroy(&fnew->exts);
        kfree(fnew);
        return err;
 }
@@ -668,6 +784,29 @@ static int fl_dump_key_val(struct sk_buff *skb,
        return 0;
 }
 
+static int fl_dump_key_vlan(struct sk_buff *skb,
+                           struct flow_dissector_key_vlan *vlan_key,
+                           struct flow_dissector_key_vlan *vlan_mask)
+{
+       int err;
+
+       if (!memchr_inv(vlan_mask, 0, sizeof(*vlan_mask)))
+               return 0;
+       if (vlan_mask->vlan_id) {
+               err = nla_put_u16(skb, TCA_FLOWER_KEY_VLAN_ID,
+                                 vlan_key->vlan_id);
+               if (err)
+                       return err;
+       }
+       if (vlan_mask->vlan_priority) {
+               err = nla_put_u8(skb, TCA_FLOWER_KEY_VLAN_PRIO,
+                                vlan_key->vlan_priority);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
 static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
                   struct sk_buff *skb, struct tcmsg *t)
 {
@@ -712,6 +851,10 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
                            &mask->basic.n_proto, TCA_FLOWER_UNSPEC,
                            sizeof(key->basic.n_proto)))
                goto nla_put_failure;
+
+       if (fl_dump_key_vlan(skb, &key->vlan, &mask->vlan))
+               goto nla_put_failure;
+
        if ((key->basic.n_proto == htons(ETH_P_IP) ||
             key->basic.n_proto == htons(ETH_P_IPV6)) &&
            fl_dump_key_val(skb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO,
@@ -738,21 +881,48 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
 
        if (key->basic.ip_proto == IPPROTO_TCP &&
            (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC,
-                            &mask->tp.src, TCA_FLOWER_UNSPEC,
+                            &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK,
                             sizeof(key->tp.src)) ||
             fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST,
-                            &mask->tp.dst, TCA_FLOWER_UNSPEC,
+                            &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK,
                             sizeof(key->tp.dst))))
                goto nla_put_failure;
        else if (key->basic.ip_proto == IPPROTO_UDP &&
                 (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC,
-                                 &mask->tp.src, TCA_FLOWER_UNSPEC,
+                                 &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK,
                                  sizeof(key->tp.src)) ||
                  fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST,
-                                 &mask->tp.dst, TCA_FLOWER_UNSPEC,
+                                 &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK,
                                  sizeof(key->tp.dst))))
                goto nla_put_failure;
 
+       if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS &&
+           (fl_dump_key_val(skb, &key->enc_ipv4.src,
+                           TCA_FLOWER_KEY_ENC_IPV4_SRC, &mask->enc_ipv4.src,
+                           TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,
+                           sizeof(key->enc_ipv4.src)) ||
+            fl_dump_key_val(skb, &key->enc_ipv4.dst,
+                            TCA_FLOWER_KEY_ENC_IPV4_DST, &mask->enc_ipv4.dst,
+                            TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,
+                            sizeof(key->enc_ipv4.dst))))
+               goto nla_put_failure;
+       else if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS &&
+                (fl_dump_key_val(skb, &key->enc_ipv6.src,
+                           TCA_FLOWER_KEY_ENC_IPV6_SRC, &mask->enc_ipv6.src,
+                           TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,
+                           sizeof(key->enc_ipv6.src)) ||
+                fl_dump_key_val(skb, &key->enc_ipv6.dst,
+                                TCA_FLOWER_KEY_ENC_IPV6_DST,
+                                &mask->enc_ipv6.dst,
+                                TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,
+                           sizeof(key->enc_ipv6.dst))))
+               goto nla_put_failure;
+
+       if (fl_dump_key_val(skb, &key->enc_key_id, TCA_FLOWER_KEY_ENC_KEY_ID,
+                           &mask->enc_key_id, TCA_FLOWER_UNSPEC,
+                           sizeof(key->enc_key_id)))
+               goto nla_put_failure;
+
        nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags);
 
        if (tcf_exts_dump(skb, &f->exts))