xref: /openbmc/linux/net/core/flow_offload.c (revision 91db9311)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #include <linux/kernel.h>
3 #include <linux/slab.h>
4 #include <net/flow_offload.h>
5 
6 struct flow_rule *flow_rule_alloc(unsigned int num_actions)
7 {
8 	struct flow_rule *rule;
9 
10 	rule = kzalloc(struct_size(rule, action.entries, num_actions),
11 		       GFP_KERNEL);
12 	if (!rule)
13 		return NULL;
14 
15 	rule->action.num_entries = num_actions;
16 
17 	return rule;
18 }
19 EXPORT_SYMBOL(flow_rule_alloc);
20 
21 #define FLOW_DISSECTOR_MATCH(__rule, __type, __out)				\
22 	const struct flow_match *__m = &(__rule)->match;			\
23 	struct flow_dissector *__d = (__m)->dissector;				\
24 										\
25 	(__out)->key = skb_flow_dissector_target(__d, __type, (__m)->key);	\
26 	(__out)->mask = skb_flow_dissector_target(__d, __type, (__m)->mask);	\
27 
28 void flow_rule_match_meta(const struct flow_rule *rule,
29 			  struct flow_match_meta *out)
30 {
31 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_META, out);
32 }
33 EXPORT_SYMBOL(flow_rule_match_meta);
34 
35 void flow_rule_match_basic(const struct flow_rule *rule,
36 			   struct flow_match_basic *out)
37 {
38 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_BASIC, out);
39 }
40 EXPORT_SYMBOL(flow_rule_match_basic);
41 
42 void flow_rule_match_control(const struct flow_rule *rule,
43 			     struct flow_match_control *out)
44 {
45 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CONTROL, out);
46 }
47 EXPORT_SYMBOL(flow_rule_match_control);
48 
49 void flow_rule_match_eth_addrs(const struct flow_rule *rule,
50 			       struct flow_match_eth_addrs *out)
51 {
52 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS, out);
53 }
54 EXPORT_SYMBOL(flow_rule_match_eth_addrs);
55 
56 void flow_rule_match_vlan(const struct flow_rule *rule,
57 			  struct flow_match_vlan *out)
58 {
59 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_VLAN, out);
60 }
61 EXPORT_SYMBOL(flow_rule_match_vlan);
62 
63 void flow_rule_match_cvlan(const struct flow_rule *rule,
64 			   struct flow_match_vlan *out)
65 {
66 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CVLAN, out);
67 }
68 EXPORT_SYMBOL(flow_rule_match_cvlan);
69 
70 void flow_rule_match_ipv4_addrs(const struct flow_rule *rule,
71 				struct flow_match_ipv4_addrs *out)
72 {
73 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS, out);
74 }
75 EXPORT_SYMBOL(flow_rule_match_ipv4_addrs);
76 
77 void flow_rule_match_ipv6_addrs(const struct flow_rule *rule,
78 				struct flow_match_ipv6_addrs *out)
79 {
80 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS, out);
81 }
82 EXPORT_SYMBOL(flow_rule_match_ipv6_addrs);
83 
84 void flow_rule_match_ip(const struct flow_rule *rule,
85 			struct flow_match_ip *out)
86 {
87 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IP, out);
88 }
89 EXPORT_SYMBOL(flow_rule_match_ip);
90 
91 void flow_rule_match_ports(const struct flow_rule *rule,
92 			   struct flow_match_ports *out)
93 {
94 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_PORTS, out);
95 }
96 EXPORT_SYMBOL(flow_rule_match_ports);
97 
98 void flow_rule_match_tcp(const struct flow_rule *rule,
99 			 struct flow_match_tcp *out)
100 {
101 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_TCP, out);
102 }
103 EXPORT_SYMBOL(flow_rule_match_tcp);
104 
105 void flow_rule_match_icmp(const struct flow_rule *rule,
106 			  struct flow_match_icmp *out)
107 {
108 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ICMP, out);
109 }
110 EXPORT_SYMBOL(flow_rule_match_icmp);
111 
112 void flow_rule_match_mpls(const struct flow_rule *rule,
113 			  struct flow_match_mpls *out)
114 {
115 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_MPLS, out);
116 }
117 EXPORT_SYMBOL(flow_rule_match_mpls);
118 
119 void flow_rule_match_enc_control(const struct flow_rule *rule,
120 				 struct flow_match_control *out)
121 {
122 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_CONTROL, out);
123 }
124 EXPORT_SYMBOL(flow_rule_match_enc_control);
125 
126 void flow_rule_match_enc_ipv4_addrs(const struct flow_rule *rule,
127 				    struct flow_match_ipv4_addrs *out)
128 {
129 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, out);
130 }
131 EXPORT_SYMBOL(flow_rule_match_enc_ipv4_addrs);
132 
133 void flow_rule_match_enc_ipv6_addrs(const struct flow_rule *rule,
134 				    struct flow_match_ipv6_addrs *out)
135 {
136 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, out);
137 }
138 EXPORT_SYMBOL(flow_rule_match_enc_ipv6_addrs);
139 
140 void flow_rule_match_enc_ip(const struct flow_rule *rule,
141 			    struct flow_match_ip *out)
142 {
143 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IP, out);
144 }
145 EXPORT_SYMBOL(flow_rule_match_enc_ip);
146 
147 void flow_rule_match_enc_ports(const struct flow_rule *rule,
148 			       struct flow_match_ports *out)
149 {
150 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_PORTS, out);
151 }
152 EXPORT_SYMBOL(flow_rule_match_enc_ports);
153 
154 void flow_rule_match_enc_keyid(const struct flow_rule *rule,
155 			       struct flow_match_enc_keyid *out)
156 {
157 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_KEYID, out);
158 }
159 EXPORT_SYMBOL(flow_rule_match_enc_keyid);
160 
161 void flow_rule_match_enc_opts(const struct flow_rule *rule,
162 			      struct flow_match_enc_opts *out)
163 {
164 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_OPTS, out);
165 }
166 EXPORT_SYMBOL(flow_rule_match_enc_opts);
167 
168 struct flow_block_cb *flow_block_cb_alloc(flow_setup_cb_t *cb,
169 					  void *cb_ident, void *cb_priv,
170 					  void (*release)(void *cb_priv))
171 {
172 	struct flow_block_cb *block_cb;
173 
174 	block_cb = kzalloc(sizeof(*block_cb), GFP_KERNEL);
175 	if (!block_cb)
176 		return ERR_PTR(-ENOMEM);
177 
178 	block_cb->cb = cb;
179 	block_cb->cb_ident = cb_ident;
180 	block_cb->cb_priv = cb_priv;
181 	block_cb->release = release;
182 
183 	return block_cb;
184 }
185 EXPORT_SYMBOL(flow_block_cb_alloc);
186 
187 void flow_block_cb_free(struct flow_block_cb *block_cb)
188 {
189 	if (block_cb->release)
190 		block_cb->release(block_cb->cb_priv);
191 
192 	kfree(block_cb);
193 }
194 EXPORT_SYMBOL(flow_block_cb_free);
195 
196 struct flow_block_cb *flow_block_cb_lookup(struct flow_block *block,
197 					   flow_setup_cb_t *cb, void *cb_ident)
198 {
199 	struct flow_block_cb *block_cb;
200 
201 	list_for_each_entry(block_cb, &block->cb_list, list) {
202 		if (block_cb->cb == cb &&
203 		    block_cb->cb_ident == cb_ident)
204 			return block_cb;
205 	}
206 
207 	return NULL;
208 }
209 EXPORT_SYMBOL(flow_block_cb_lookup);
210 
211 void *flow_block_cb_priv(struct flow_block_cb *block_cb)
212 {
213 	return block_cb->cb_priv;
214 }
215 EXPORT_SYMBOL(flow_block_cb_priv);
216 
217 void flow_block_cb_incref(struct flow_block_cb *block_cb)
218 {
219 	block_cb->refcnt++;
220 }
221 EXPORT_SYMBOL(flow_block_cb_incref);
222 
223 unsigned int flow_block_cb_decref(struct flow_block_cb *block_cb)
224 {
225 	return --block_cb->refcnt;
226 }
227 EXPORT_SYMBOL(flow_block_cb_decref);
228 
229 bool flow_block_cb_is_busy(flow_setup_cb_t *cb, void *cb_ident,
230 			   struct list_head *driver_block_list)
231 {
232 	struct flow_block_cb *block_cb;
233 
234 	list_for_each_entry(block_cb, driver_block_list, driver_list) {
235 		if (block_cb->cb == cb &&
236 		    block_cb->cb_ident == cb_ident)
237 			return true;
238 	}
239 
240 	return false;
241 }
242 EXPORT_SYMBOL(flow_block_cb_is_busy);
243 
244 int flow_block_cb_setup_simple(struct flow_block_offload *f,
245 			       struct list_head *driver_block_list,
246 			       flow_setup_cb_t *cb,
247 			       void *cb_ident, void *cb_priv,
248 			       bool ingress_only)
249 {
250 	struct flow_block_cb *block_cb;
251 
252 	if (ingress_only &&
253 	    f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
254 		return -EOPNOTSUPP;
255 
256 	f->driver_block_list = driver_block_list;
257 
258 	switch (f->command) {
259 	case FLOW_BLOCK_BIND:
260 		if (flow_block_cb_is_busy(cb, cb_ident, driver_block_list))
261 			return -EBUSY;
262 
263 		block_cb = flow_block_cb_alloc(cb, cb_ident, cb_priv, NULL);
264 		if (IS_ERR(block_cb))
265 			return PTR_ERR(block_cb);
266 
267 		flow_block_cb_add(block_cb, f);
268 		list_add_tail(&block_cb->driver_list, driver_block_list);
269 		return 0;
270 	case FLOW_BLOCK_UNBIND:
271 		block_cb = flow_block_cb_lookup(f->block, cb, cb_ident);
272 		if (!block_cb)
273 			return -ENOENT;
274 
275 		flow_block_cb_remove(block_cb, f);
276 		list_del(&block_cb->driver_list);
277 		return 0;
278 	default:
279 		return -EOPNOTSUPP;
280 	}
281 }
282 EXPORT_SYMBOL(flow_block_cb_setup_simple);
283