1fe8739ba6d51f9f329f935f7ef5450e06055fe3
[openwrt/staging/nbd.git] /
1 From: Pablo Neira Ayuso <pablo@netfilter.org>
2 Date: Wed, 24 Mar 2021 02:30:39 +0100
3 Subject: [PATCH] netfilter: flowtable: use dev_fill_forward_path() to
4 obtain ingress device
5
6 Obtain the ingress device in the tuple from the route in the reply
7 direction. Use dev_fill_forward_path() instead to get the real ingress
8 device for this flow.
9
10 Fall back to use the ingress device that the IP forwarding route
11 provides if:
12
13 - dev_fill_forward_path() finds no real ingress device.
14 - the ingress device that is obtained is not part of the flowtable
15 devices.
16 - this route has a xfrm policy.
17
18 Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
19 ---
20
21 --- a/include/net/netfilter/nf_flow_table.h
22 +++ b/include/net/netfilter/nf_flow_table.h
23 @@ -165,6 +165,9 @@ static inline __s32 nf_flow_timeout_delt
24 struct nf_flow_route {
25 struct {
26 struct dst_entry *dst;
27 + struct {
28 + u32 ifindex;
29 + } in;
30 enum flow_offload_xmit_type xmit_type;
31 } tuple[FLOW_OFFLOAD_DIR_MAX];
32 };
33 --- a/net/netfilter/nf_flow_table_core.c
34 +++ b/net/netfilter/nf_flow_table_core.c
35 @@ -79,7 +79,6 @@ static int flow_offload_fill_route(struc
36 enum flow_offload_tuple_dir dir)
37 {
38 struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
39 - struct dst_entry *other_dst = route->tuple[!dir].dst;
40 struct dst_entry *dst = route->tuple[dir].dst;
41
42 if (!dst_hold_safe(route->tuple[dir].dst))
43 @@ -94,7 +93,7 @@ static int flow_offload_fill_route(struc
44 break;
45 }
46
47 - flow_tuple->iifidx = other_dst->dev->ifindex;
48 + flow_tuple->iifidx = route->tuple[dir].in.ifindex;
49 flow_tuple->xmit_type = route->tuple[dir].xmit_type;
50 flow_tuple->dst_cache = dst;
51
52 --- a/net/netfilter/nft_flow_offload.c
53 +++ b/net/netfilter/nft_flow_offload.c
54 @@ -31,14 +31,104 @@ static void nft_default_forward_path(str
55 struct dst_entry *dst_cache,
56 enum ip_conntrack_dir dir)
57 {
58 + route->tuple[!dir].in.ifindex = dst_cache->dev->ifindex;
59 route->tuple[dir].dst = dst_cache;
60 route->tuple[dir].xmit_type = nft_xmit_type(dst_cache);
61 }
62
63 +static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
64 + const struct dst_entry *dst_cache,
65 + const struct nf_conn *ct,
66 + enum ip_conntrack_dir dir,
67 + struct net_device_path_stack *stack)
68 +{
69 + const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
70 + struct net_device *dev = dst_cache->dev;
71 + unsigned char ha[ETH_ALEN];
72 + struct neighbour *n;
73 + u8 nud_state;
74 +
75 + n = dst_neigh_lookup(dst_cache, daddr);
76 + if (!n)
77 + return -1;
78 +
79 + read_lock_bh(&n->lock);
80 + nud_state = n->nud_state;
81 + ether_addr_copy(ha, n->ha);
82 + read_unlock_bh(&n->lock);
83 + neigh_release(n);
84 +
85 + if (!(nud_state & NUD_VALID))
86 + return -1;
87 +
88 + return dev_fill_forward_path(dev, ha, stack);
89 +}
90 +
91 +struct nft_forward_info {
92 + const struct net_device *indev;
93 +};
94 +
95 +static void nft_dev_path_info(const struct net_device_path_stack *stack,
96 + struct nft_forward_info *info)
97 +{
98 + const struct net_device_path *path;
99 + int i;
100 +
101 + for (i = 0; i < stack->num_paths; i++) {
102 + path = &stack->path[i];
103 + switch (path->type) {
104 + case DEV_PATH_ETHERNET:
105 + info->indev = path->dev;
106 + break;
107 + case DEV_PATH_VLAN:
108 + case DEV_PATH_BRIDGE:
109 + default:
110 + info->indev = NULL;
111 + break;
112 + }
113 + }
114 +}
115 +
116 +static bool nft_flowtable_find_dev(const struct net_device *dev,
117 + struct nft_flowtable *ft)
118 +{
119 + struct nft_hook *hook;
120 + bool found = false;
121 +
122 + list_for_each_entry_rcu(hook, &ft->hook_list, list) {
123 + if (hook->ops.dev != dev)
124 + continue;
125 +
126 + found = true;
127 + break;
128 + }
129 +
130 + return found;
131 +}
132 +
133 +static void nft_dev_forward_path(struct nf_flow_route *route,
134 + const struct nf_conn *ct,
135 + enum ip_conntrack_dir dir,
136 + struct nft_flowtable *ft)
137 +{
138 + const struct dst_entry *dst = route->tuple[dir].dst;
139 + struct net_device_path_stack stack;
140 + struct nft_forward_info info = {};
141 +
142 + if (nft_dev_fill_forward_path(route, dst, ct, dir, &stack) >= 0)
143 + nft_dev_path_info(&stack, &info);
144 +
145 + if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
146 + return;
147 +
148 + route->tuple[!dir].in.ifindex = info.indev->ifindex;
149 +}
150 +
151 static int nft_flow_route(const struct nft_pktinfo *pkt,
152 const struct nf_conn *ct,
153 struct nf_flow_route *route,
154 - enum ip_conntrack_dir dir)
155 + enum ip_conntrack_dir dir,
156 + struct nft_flowtable *ft)
157 {
158 struct dst_entry *this_dst = skb_dst(pkt->skb);
159 struct dst_entry *other_dst = NULL;
160 @@ -63,6 +153,12 @@ static int nft_flow_route(const struct n
161 nft_default_forward_path(route, this_dst, dir);
162 nft_default_forward_path(route, other_dst, !dir);
163
164 + if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH &&
165 + route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) {
166 + nft_dev_forward_path(route, ct, dir, ft);
167 + nft_dev_forward_path(route, ct, !dir, ft);
168 + }
169 +
170 return 0;
171 }
172
173 @@ -90,8 +186,8 @@ static void nft_flow_offload_eval(const
174 struct nft_flow_offload *priv = nft_expr_priv(expr);
175 struct nf_flowtable *flowtable = &priv->flowtable->data;
176 struct tcphdr _tcph, *tcph = NULL;
177 + struct nf_flow_route route = {};
178 enum ip_conntrack_info ctinfo;
179 - struct nf_flow_route route;
180 struct flow_offload *flow;
181 enum ip_conntrack_dir dir;
182 struct nf_conn *ct;
183 @@ -128,7 +224,7 @@ static void nft_flow_offload_eval(const
184 goto out;
185
186 dir = CTINFO2DIR(ctinfo);
187 - if (nft_flow_route(pkt, ct, &route, dir) < 0)
188 + if (nft_flow_route(pkt, ct, &route, dir, priv->flowtable) < 0)
189 goto err_flow_route;
190
191 flow = flow_offload_alloc(ct);