xref: /openbmc/linux/net/core/flow_offload.c (revision 2127c01b)
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(struct net *net, tc_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->net = net;
179 	block_cb->cb = cb;
180 	block_cb->cb_ident = cb_ident;
181 	block_cb->cb_priv = cb_priv;
182 	block_cb->release = release;
183 
184 	return block_cb;
185 }
186 EXPORT_SYMBOL(flow_block_cb_alloc);
187 
188 void flow_block_cb_free(struct flow_block_cb *block_cb)
189 {
190 	if (block_cb->release)
191 		block_cb->release(block_cb->cb_priv);
192 
193 	kfree(block_cb);
194 }
195 EXPORT_SYMBOL(flow_block_cb_free);
196 
197 struct flow_block_cb *flow_block_cb_lookup(struct flow_block_offload *f,
198 					   tc_setup_cb_t *cb, void *cb_ident)
199 {
200 	struct flow_block_cb *block_cb;
201 
202 	list_for_each_entry(block_cb, f->driver_block_list, driver_list) {
203 		if (block_cb->net == f->net &&
204 		    block_cb->cb == cb &&
205 		    block_cb->cb_ident == cb_ident)
206 			return block_cb;
207 	}
208 
209 	return NULL;
210 }
211 EXPORT_SYMBOL(flow_block_cb_lookup);
212 
213 void *flow_block_cb_priv(struct flow_block_cb *block_cb)
214 {
215 	return block_cb->cb_priv;
216 }
217 EXPORT_SYMBOL(flow_block_cb_priv);
218 
219 void flow_block_cb_incref(struct flow_block_cb *block_cb)
220 {
221 	block_cb->refcnt++;
222 }
223 EXPORT_SYMBOL(flow_block_cb_incref);
224 
225 unsigned int flow_block_cb_decref(struct flow_block_cb *block_cb)
226 {
227 	return --block_cb->refcnt;
228 }
229 EXPORT_SYMBOL(flow_block_cb_decref);
230 
231 bool flow_block_cb_is_busy(tc_setup_cb_t *cb, void *cb_ident,
232 			   struct list_head *driver_block_list)
233 {
234 	struct flow_block_cb *block_cb;
235 
236 	list_for_each_entry(block_cb, driver_block_list, driver_list) {
237 		if (block_cb->cb == cb &&
238 		    block_cb->cb_ident == cb_ident)
239 			return true;
240 	}
241 
242 	return false;
243 }
244 EXPORT_SYMBOL(flow_block_cb_is_busy);
245 
246 int flow_block_cb_setup_simple(struct flow_block_offload *f,
247 			       struct list_head *driver_block_list,
248 			       tc_setup_cb_t *cb, void *cb_ident, void *cb_priv,
249 			       bool ingress_only)
250 {
251 	struct flow_block_cb *block_cb;
252 
253 	if (ingress_only &&
254 	    f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
255 		return -EOPNOTSUPP;
256 
257 	f->driver_block_list = driver_block_list;
258 
259 	switch (f->command) {
260 	case FLOW_BLOCK_BIND:
261 		if (flow_block_cb_is_busy(cb, cb_ident, driver_block_list))
262 			return -EBUSY;
263 
264 		block_cb = flow_block_cb_alloc(f->net, cb, cb_ident,
265 					       cb_priv, NULL);
266 		if (IS_ERR(block_cb))
267 			return PTR_ERR(block_cb);
268 
269 		flow_block_cb_add(block_cb, f);
270 		list_add_tail(&block_cb->driver_list, driver_block_list);
271 		return 0;
272 	case FLOW_BLOCK_UNBIND:
273 		block_cb = flow_block_cb_lookup(f, cb, cb_ident);
274 		if (!block_cb)
275 			return -ENOENT;
276 
277 		flow_block_cb_remove(block_cb, f);
278 		list_del(&block_cb->driver_list);
279 		return 0;
280 	default:
281 		return -EOPNOTSUPP;
282 	}
283 }
284 EXPORT_SYMBOL(flow_block_cb_setup_simple);
285