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
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.
13 Signed-off-by: Felix Fietkau <nbd@nbd.name>
16 --- a/include/linux/netdevice.h
17 +++ b/include/linux/netdevice.h
18 @@ -930,6 +930,7 @@ struct tlsdev_ops;
22 +struct flow_offload_hw_path;
24 enum flow_offload_type {
26 @@ -1168,8 +1169,15 @@ enum flow_offload_type {
27 * int (*ndo_bridge_dellink)(struct net_device *dev, struct nlmsghdr *nlh,
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.
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.
42 * int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier);
43 @@ -1419,8 +1427,11 @@ struct net_device_ops {
44 int (*ndo_bridge_dellink)(struct net_device *dev,
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,
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 @@ -88,6 +88,21 @@ struct flow_offload {
62 +#define FLOW_OFFLOAD_PATH_ETHERNET BIT(0)
63 +#define FLOW_OFFLOAD_PATH_VLAN BIT(1)
64 +#define FLOW_OFFLOAD_PATH_PPPOE BIT(2)
66 +struct flow_offload_hw_path {
67 + struct net_device *dev;
70 + u8 eth_src[ETH_ALEN];
71 + u8 eth_dest[ETH_ALEN];
77 #define NF_FLOW_TIMEOUT (30 * HZ)
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;
86 - possible_net_t flow_hw_net;
88 + struct flow_offload_hw_path src;
89 + struct flow_offload_hw_path dest;
92 -static int do_flow_offload_hw(struct net *net, struct flow_offload *flow,
94 +static void flow_offload_check_ethernet(struct flow_offload_tuple *tuple,
95 + struct dst_entry *dst,
96 + struct flow_offload_hw_path *path)
98 - struct net_device *indev;
100 + struct net_device *dev = path->dev;
101 + struct neighbour *n;
103 - ifindex = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.iifidx;
104 - indev = dev_get_by_index(net, ifindex);
105 - if (WARN_ON(!indev))
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)
115 + memcpy(path->eth_src, path->dev->dev_addr, ETH_ALEN);
116 + n = dst_neigh_lookup(dst, &tuple->src_v4);
121 + memcpy(path->eth_dest, n->ha, ETH_ALEN);
122 + path->flags |= FLOW_OFFLOAD_PATH_ETHERNET;
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)
134 + struct net_device *dev;
136 - if (nf_ct_is_dying(offload->ct))
138 + dev = dev_get_by_index_rcu(net, tuple->iifidx);
143 + flow_offload_check_ethernet(tuple, dst, path);
145 - net = read_pnet(&offload->flow_hw_net);
146 - ret = do_flow_offload_hw(net, offload->flow, FLOW_OFFLOAD_ADD);
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);
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)
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;
163 + ret = src_dev->netdev_ops->ndo_flow_offload(offload->type,
168 + /* restore devices in case the driver mangled them */
169 + offload->src.dev = src_dev;
170 + offload->dest.dev = dest_dev;
172 - do_flow_offload_hw(net, offload->flow, FLOW_OFFLOAD_DEL);
176 +static void flow_offload_hw_free(struct flow_offload_hw *offload)
178 + dev_put(offload->src.dev);
179 + dev_put(offload->dest.dev);
181 + nf_conntrack_put(&offload->ct->ct_general);
182 + list_del(&offload->list);
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);
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))
198 + if (do_flow_offload_hw(offload) >= 0)
199 + offload->flow->flags |= FLOW_OFFLOAD_HW;
201 case FLOW_OFFLOAD_DEL:
202 - flow_offload_hw_work_del(offload);
203 + do_flow_offload_hw(offload);
207 - nf_conntrack_put(&offload->ct->ct_general);
208 - list_del(&offload->list);
210 + mutex_unlock(&nf_flow_offload_hw_mutex);
212 + flow_offload_hw_free(offload);
216 @@ -97,20 +130,56 @@ static void flow_offload_queue_work(stru
217 schedule_work(&nf_flow_offload_hw_work);
220 +static struct flow_offload_hw *
221 +flow_offload_hw_prepare(struct net *net, struct flow_offload *flow)
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;
228 + rcu_read_lock_bh();
230 + tuple_s = &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple;
231 + tuple_d = &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple;
233 + if (flow_offload_check_path(net, tuple_s, tuple_d->dst_cache, &src))
236 + if (flow_offload_check_path(net, tuple_d, tuple_s->dst_cache, &dest))
239 + if (!src.dev->netdev_ops->ndo_flow_offload)
242 + offload = kzalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
247 + dev_hold(dest.dev);
248 + offload->src = src;
249 + offload->dest = dest;
250 + offload->flow = flow;
253 + rcu_read_unlock_bh();
258 static void flow_offload_hw_add(struct net *net, struct flow_offload *flow,
261 struct flow_offload_hw *offload;
263 - offload = kmalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
264 + offload = flow_offload_hw_prepare(net, flow);
268 nf_conntrack_get(&ct->ct_general);
269 offload->type = FLOW_OFFLOAD_ADD;
271 - offload->flow = flow;
272 - write_pnet(&offload->flow_hw_net, net);
274 flow_offload_queue_work(offload);
276 @@ -119,14 +188,11 @@ static void flow_offload_hw_del(struct n
278 struct flow_offload_hw *offload;
280 - offload = kmalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
281 + offload = flow_offload_hw_prepare(net, flow);
285 offload->type = FLOW_OFFLOAD_DEL;
286 - offload->ct = NULL;
287 - offload->flow = flow;
288 - write_pnet(&offload->flow_hw_net, net);
290 flow_offload_queue_work(offload);
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);
296 - list_for_each_entry_safe(offload, next, &hw_offload_pending, list) {
298 - nf_conntrack_put(&offload->ct->ct_general);
299 - list_del(&offload->list);
302 + list_for_each_entry_safe(offload, next, &hw_offload_pending, list)
303 + flow_offload_hw_free(offload);
306 module_init(nf_flow_table_hw_module_init);