tools headers UAPI: Sync linux/prctl.h with the kernel sources
[linux-2.6-microblaze.git] / net / netfilter / nft_flow_offload.c
index 8392b1a..4843dd2 100644 (file)
@@ -66,19 +66,30 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
 struct nft_forward_info {
        const struct net_device *indev;
        const struct net_device *outdev;
+       const struct net_device *hw_outdev;
        struct id {
                __u16   id;
                __be16  proto;
        } encap[NF_FLOW_TABLE_ENCAP_MAX];
        u8 num_encaps;
+       u8 ingress_vlans;
        u8 h_source[ETH_ALEN];
        u8 h_dest[ETH_ALEN];
        enum flow_offload_xmit_type xmit_type;
 };
 
+static bool nft_is_valid_ether_device(const struct net_device *dev)
+{
+       if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER ||
+           dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr))
+               return false;
+
+       return true;
+}
+
 static void nft_dev_path_info(const struct net_device_path_stack *stack,
                              struct nft_forward_info *info,
-                             unsigned char *ha)
+                             unsigned char *ha, struct nf_flowtable *flowtable)
 {
        const struct net_device_path *path;
        int i;
@@ -89,15 +100,21 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack,
                path = &stack->path[i];
                switch (path->type) {
                case DEV_PATH_ETHERNET:
+               case DEV_PATH_DSA:
                case DEV_PATH_VLAN:
+               case DEV_PATH_PPPOE:
                        info->indev = path->dev;
                        if (is_zero_ether_addr(info->h_source))
                                memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
 
                        if (path->type == DEV_PATH_ETHERNET)
                                break;
+                       if (path->type == DEV_PATH_DSA) {
+                               i = stack->num_paths;
+                               break;
+                       }
 
-                       /* DEV_PATH_VLAN */
+                       /* DEV_PATH_VLAN and DEV_PATH_PPPOE */
                        if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
                                info->indev = NULL;
                                break;
@@ -106,11 +123,28 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack,
                        info->encap[info->num_encaps].id = path->encap.id;
                        info->encap[info->num_encaps].proto = path->encap.proto;
                        info->num_encaps++;
+                       if (path->type == DEV_PATH_PPPOE)
+                               memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN);
                        break;
                case DEV_PATH_BRIDGE:
                        if (is_zero_ether_addr(info->h_source))
                                memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
 
+                       switch (path->bridge.vlan_mode) {
+                       case DEV_PATH_BR_VLAN_UNTAG_HW:
+                               info->ingress_vlans |= BIT(info->num_encaps - 1);
+                               break;
+                       case DEV_PATH_BR_VLAN_TAG:
+                               info->encap[info->num_encaps].id = path->bridge.vlan_id;
+                               info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
+                               info->num_encaps++;
+                               break;
+                       case DEV_PATH_BR_VLAN_UNTAG:
+                               info->num_encaps--;
+                               break;
+                       case DEV_PATH_BR_VLAN_KEEP:
+                               break;
+                       }
                        info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
                        break;
                default:
@@ -120,6 +154,12 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack,
        }
        if (!info->outdev)
                info->outdev = info->indev;
+
+       info->hw_outdev = info->indev;
+
+       if (nf_flowtable_hw_offload(flowtable) &&
+           nft_is_valid_ether_device(info->indev))
+               info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
 }
 
 static bool nft_flowtable_find_dev(const struct net_device *dev,
@@ -151,7 +191,7 @@ static void nft_dev_forward_path(struct nf_flow_route *route,
        int i;
 
        if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
-               nft_dev_path_info(&stack, &info, ha);
+               nft_dev_path_info(&stack, &info, ha, &ft->data);
 
        if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
                return;
@@ -162,11 +202,13 @@ static void nft_dev_forward_path(struct nf_flow_route *route,
                route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
        }
        route->tuple[!dir].in.num_encaps = info.num_encaps;
+       route->tuple[!dir].in.ingress_vlans = info.ingress_vlans;
 
        if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
                memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
                memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN);
                route->tuple[dir].out.ifindex = info.outdev->ifindex;
+               route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex;
                route->tuple[dir].xmit_type = info.xmit_type;
        }
 }