generic: fix flow table hw offload
[librecmc/librecmc.git] / target / linux / generic / pending-4.14 / 641-netfilter-nf_flow_table-support-hw-offload-through-v.patch
1 From: Felix Fietkau <nbd@nbd.name>
2 Date: Thu, 15 Mar 2018 20:46:31 +0100
3 Subject: [PATCH] netfilter: nf_flow_table: support hw offload through
4  virtual interfaces
5
6 There are hardware offload devices that support offloading VLANs and
7 PPPoE devices. Additionally, it is useful to be able to offload packets
8 routed through bridge interfaces as well.
9 Add support for finding the path to the offload device through these
10 virtual interfaces, while collecting useful parameters for the offload
11 device, like VLAN ID/protocol, PPPoE session and Ethernet MAC address.
12
13 Signed-off-by: Felix Fietkau <nbd@nbd.name>
14 ---
15
16 --- a/include/linux/netdevice.h
17 +++ b/include/linux/netdevice.h
18 @@ -827,6 +827,7 @@ struct xfrmdev_ops {
19  #endif
20  
21  struct flow_offload;
22 +struct flow_offload_hw_path;
23  
24  enum flow_offload_type {
25         FLOW_OFFLOAD_ADD        = 0,
26 @@ -1064,8 +1065,15 @@ enum flow_offload_type {
27   * int (*ndo_bridge_dellink)(struct net_device *dev, struct nlmsghdr *nlh,
28   *                          u16 flags);
29   *
30 + * int (*ndo_flow_offload_check)(struct flow_offload_hw_path *path);
31 + *     For virtual devices like bridges, vlan, and pppoe, fill in the
32 + *     underlying network device that can be used for offloading connections.
33 + *     Return an error if offloading is not supported.
34 + *
35   * int (*ndo_flow_offload)(enum flow_offload_type type,
36 - *                        struct flow_offload *flow);
37 + *                        struct flow_offload *flow,
38 + *                        struct flow_offload_hw_path *src,
39 + *                        struct flow_offload_hw_path *dest);
40   *     Adds/deletes flow entry to/from net device flowtable.
41   *
42   * int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier);
43 @@ -1292,8 +1300,11 @@ struct net_device_ops {
44         int                     (*ndo_bridge_dellink)(struct net_device *dev,
45                                                       struct nlmsghdr *nlh,
46                                                       u16 flags);
47 +       int                     (*ndo_flow_offload_check)(struct flow_offload_hw_path *path);
48         int                     (*ndo_flow_offload)(enum flow_offload_type type,
49 -                                                   struct flow_offload *flow);
50 +                                                   struct flow_offload *flow,
51 +                                                   struct flow_offload_hw_path *src,
52 +                                                   struct flow_offload_hw_path *dest);
53         int                     (*ndo_change_carrier)(struct net_device *dev,
54                                                       bool new_carrier);
55         int                     (*ndo_get_phys_port_id)(struct net_device *dev,
56 --- a/include/net/netfilter/nf_flow_table.h
57 +++ b/include/net/netfilter/nf_flow_table.h
58 @@ -86,6 +86,21 @@ struct flow_offload {
59         };
60  };
61  
62 +#define FLOW_OFFLOAD_PATH_ETHERNET     BIT(0)
63 +#define FLOW_OFFLOAD_PATH_VLAN         BIT(1)
64 +#define FLOW_OFFLOAD_PATH_PPPOE                BIT(2)
65 +
66 +struct flow_offload_hw_path {
67 +       struct net_device *dev;
68 +       u32 flags;
69 +
70 +       u8 eth_src[ETH_ALEN];
71 +       u8 eth_dest[ETH_ALEN];
72 +       u16 vlan_proto;
73 +       u16 vlan_id;
74 +       u16 pppoe_sid;
75 +};
76 +
77  #define NF_FLOW_TIMEOUT (30 * HZ)
78  
79  struct nf_flow_route {
80 --- a/net/netfilter/nf_flow_table_hw.c
81 +++ b/net/netfilter/nf_flow_table_hw.c
82 @@ -19,48 +19,77 @@ struct flow_offload_hw {
83         enum flow_offload_type  type;
84         struct flow_offload     *flow;
85         struct nf_conn          *ct;
86 -       possible_net_t          flow_hw_net;
87 +
88 +       struct flow_offload_hw_path src;
89 +       struct flow_offload_hw_path dest;
90  };
91  
92 -static int do_flow_offload_hw(struct net *net, struct flow_offload *flow,
93 -                             int type)
94 +static void flow_offload_check_ethernet(struct flow_offload_tuple *tuple,
95 +                                       struct dst_entry *dst,
96 +                                       struct flow_offload_hw_path *path)
97  {
98 -       struct net_device *indev;
99 -       int ret, ifindex;
100 +       struct net_device *dev = path->dev;
101 +       struct neighbour *n;
102  
103 -       ifindex = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.iifidx;
104 -       indev = dev_get_by_index(net, ifindex);
105 -       if (WARN_ON(!indev))
106 -               return 0;
107 -
108 -       mutex_lock(&nf_flow_offload_hw_mutex);
109 -       ret = indev->netdev_ops->ndo_flow_offload(type, flow);
110 -       mutex_unlock(&nf_flow_offload_hw_mutex);
111 +       if (dev->type != ARPHRD_ETHER)
112 +               return;
113  
114 -       dev_put(indev);
115 +       memcpy(path->eth_src, path->dev->dev_addr, ETH_ALEN);
116 +       n = dst_neigh_lookup(dst, &tuple->src_v4);
117 +       if (!n)
118 +               return;
119  
120 -       return ret;
121 +       memcpy(path->eth_dest, n->ha, ETH_ALEN);
122 +       path->flags |= FLOW_OFFLOAD_PATH_ETHERNET;
123 +       neigh_release(n);
124  }
125  
126 -static void flow_offload_hw_work_add(struct flow_offload_hw *offload)
127 +static int flow_offload_check_path(struct net *net,
128 +                                  struct flow_offload_tuple *tuple,
129 +                                  struct dst_entry *dst,
130 +                                  struct flow_offload_hw_path *path)
131  {
132 -       struct net *net;
133 -       int ret;
134 +       struct net_device *dev;
135  
136 -       if (nf_ct_is_dying(offload->ct))
137 -               return;
138 +       dev = dev_get_by_index_rcu(net, tuple->iifidx);
139 +       if (!dev)
140 +               return -ENOENT;
141 +
142 +       path->dev = dev;
143 +       flow_offload_check_ethernet(tuple, dst, path);
144  
145 -       net = read_pnet(&offload->flow_hw_net);
146 -       ret = do_flow_offload_hw(net, offload->flow, FLOW_OFFLOAD_ADD);
147 -       if (ret >= 0)
148 -               offload->flow->flags |= FLOW_OFFLOAD_HW;
149 +       if (dev->netdev_ops->ndo_flow_offload_check)
150 +               return dev->netdev_ops->ndo_flow_offload_check(path);
151 +
152 +       return 0;
153  }
154  
155 -static void flow_offload_hw_work_del(struct flow_offload_hw *offload)
156 +static int do_flow_offload_hw(struct flow_offload_hw *offload)
157  {
158 -       struct net *net = read_pnet(&offload->flow_hw_net);
159 +       struct net_device *src_dev = offload->src.dev;
160 +       struct net_device *dest_dev = offload->dest.dev;
161 +       int ret;
162 +
163 +       ret = src_dev->netdev_ops->ndo_flow_offload(offload->type,
164 +                                                   offload->flow,
165 +                                                   &offload->src,
166 +                                                   &offload->dest);
167 +
168 +       /* restore devices in case the driver mangled them */
169 +       offload->src.dev = src_dev;
170 +       offload->dest.dev = dest_dev;
171  
172 -       do_flow_offload_hw(net, offload->flow, FLOW_OFFLOAD_DEL);
173 +       return ret;
174 +}
175 +
176 +static void flow_offload_hw_free(struct flow_offload_hw *offload)
177 +{
178 +       dev_put(offload->src.dev);
179 +       dev_put(offload->dest.dev);
180 +       if (offload->ct)
181 +               nf_conntrack_put(&offload->ct->ct_general);
182 +       list_del(&offload->list);
183 +       kfree(offload);
184  }
185  
186  static void flow_offload_hw_work(struct work_struct *work)
187 @@ -73,18 +102,22 @@ static void flow_offload_hw_work(struct
188         spin_unlock_bh(&flow_offload_hw_pending_list_lock);
189  
190         list_for_each_entry_safe(offload, next, &hw_offload_pending, list) {
191 +               mutex_lock(&nf_flow_offload_hw_mutex);
192                 switch (offload->type) {
193                 case FLOW_OFFLOAD_ADD:
194 -                       flow_offload_hw_work_add(offload);
195 +                       if (nf_ct_is_dying(offload->ct))
196 +                               break;
197 +
198 +                       if (do_flow_offload_hw(offload) >= 0)
199 +                               offload->flow->flags |= FLOW_OFFLOAD_HW;
200                         break;
201                 case FLOW_OFFLOAD_DEL:
202 -                       flow_offload_hw_work_del(offload);
203 +                       do_flow_offload_hw(offload);
204                         break;
205                 }
206 -               if (offload->ct)
207 -                       nf_conntrack_put(&offload->ct->ct_general);
208 -               list_del(&offload->list);
209 -               kfree(offload);
210 +               mutex_unlock(&nf_flow_offload_hw_mutex);
211 +
212 +               flow_offload_hw_free(offload);
213         }
214  }
215  
216 @@ -97,20 +130,56 @@ static void flow_offload_queue_work(stru
217         schedule_work(&nf_flow_offload_hw_work);
218  }
219  
220 +static struct flow_offload_hw *
221 +flow_offload_hw_prepare(struct net *net, struct flow_offload *flow)
222 +{
223 +       struct flow_offload_hw_path src = {};
224 +       struct flow_offload_hw_path dest = {};
225 +       struct flow_offload_tuple *tuple_s, *tuple_d;
226 +       struct flow_offload_hw *offload = NULL;
227 +
228 +       rcu_read_lock_bh();
229 +
230 +       tuple_s = &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple;
231 +       tuple_d = &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple;
232 +
233 +       if (flow_offload_check_path(net, tuple_s, tuple_d->dst_cache, &src))
234 +               goto out;
235 +
236 +       if (flow_offload_check_path(net, tuple_d, tuple_s->dst_cache, &dest))
237 +               goto out;
238 +
239 +       if (!src.dev->netdev_ops->ndo_flow_offload)
240 +               goto out;
241 +
242 +       offload = kzalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
243 +       if (!offload)
244 +               goto out;
245 +
246 +       dev_hold(src.dev);
247 +       dev_hold(dest.dev);
248 +       offload->src = src;
249 +       offload->dest = dest;
250 +       offload->flow = flow;
251 +
252 +out:
253 +       rcu_read_unlock_bh();
254 +
255 +       return offload;
256 +}
257 +
258  static void flow_offload_hw_add(struct net *net, struct flow_offload *flow,
259                                 struct nf_conn *ct)
260  {
261         struct flow_offload_hw *offload;
262  
263 -       offload = kmalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
264 +       offload = flow_offload_hw_prepare(net, flow);
265         if (!offload)
266                 return;
267  
268         nf_conntrack_get(&ct->ct_general);
269         offload->type = FLOW_OFFLOAD_ADD;
270         offload->ct = ct;
271 -       offload->flow = flow;
272 -       write_pnet(&offload->flow_hw_net, net);
273  
274         flow_offload_queue_work(offload);
275  }
276 @@ -119,14 +188,11 @@ static void flow_offload_hw_del(struct n
277  {
278         struct flow_offload_hw *offload;
279  
280 -       offload = kmalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
281 +       offload = flow_offload_hw_prepare(net, flow);
282         if (!offload)
283                 return;
284  
285         offload->type = FLOW_OFFLOAD_DEL;
286 -       offload->ct = NULL;
287 -       offload->flow = flow;
288 -       write_pnet(&offload->flow_hw_net, net);
289  
290         flow_offload_queue_work(offload);
291  }
292 @@ -153,12 +219,8 @@ static void __exit nf_flow_table_hw_modu
293         nf_flow_table_hw_unregister(&flow_offload_hw);
294         cancel_work_sync(&nf_flow_offload_hw_work);
295  
296 -       list_for_each_entry_safe(offload, next, &hw_offload_pending, list) {
297 -               if (offload->ct)
298 -                       nf_conntrack_put(&offload->ct->ct_general);
299 -               list_del(&offload->list);
300 -               kfree(offload);
301 -       }
302 +       list_for_each_entry_safe(offload, next, &hw_offload_pending, list)
303 +               flow_offload_hw_free(offload);
304  }
305  
306  module_init(nf_flow_table_hw_module_init);