1 | /* |
2 | * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. |
3 | * Copyright (c) 2016 FUJITSU LIMITED |
4 | * Copyright (c) 2016 Intel Corporation |
5 | * |
6 | * Author: Zhang Chen <zhangchen.fnst@cn.fujitsu.com> |
7 | * |
8 | * This work is licensed under the terms of the GNU GPL, version 2 or |
9 | * later. See the COPYING file in the top-level directory. |
10 | */ |
11 | |
12 | #include "qemu/osdep.h" |
13 | #include "net/filter.h" |
14 | #include "net/net.h" |
15 | #include "qapi/error.h" |
16 | #include "qom/object.h" |
17 | #include "qemu/main-loop.h" |
18 | #include "qemu/error-report.h" |
19 | #include "trace.h" |
20 | #include "chardev/char-fe.h" |
21 | #include "qemu/iov.h" |
22 | #include "qemu/sockets.h" |
23 | |
24 | #define FILTER_MIRROR(obj) \ |
25 | OBJECT_CHECK(MirrorState, (obj), TYPE_FILTER_MIRROR) |
26 | |
27 | #define FILTER_REDIRECTOR(obj) \ |
28 | OBJECT_CHECK(MirrorState, (obj), TYPE_FILTER_REDIRECTOR) |
29 | |
30 | #define TYPE_FILTER_MIRROR "filter-mirror" |
31 | #define TYPE_FILTER_REDIRECTOR "filter-redirector" |
32 | #define REDIRECTOR_MAX_LEN NET_BUFSIZE |
33 | |
34 | typedef struct MirrorState { |
35 | NetFilterState parent_obj; |
36 | char *indev; |
37 | char *outdev; |
38 | CharBackend chr_in; |
39 | CharBackend chr_out; |
40 | SocketReadState rs; |
41 | bool vnet_hdr; |
42 | } MirrorState; |
43 | |
44 | static int filter_send(MirrorState *s, |
45 | const struct iovec *iov, |
46 | int iovcnt) |
47 | { |
48 | NetFilterState *nf = NETFILTER(s); |
49 | int ret = 0; |
50 | ssize_t size = 0; |
51 | uint32_t len = 0; |
52 | char *buf; |
53 | |
54 | size = iov_size(iov, iovcnt); |
55 | if (!size) { |
56 | return 0; |
57 | } |
58 | |
59 | len = htonl(size); |
60 | ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len)); |
61 | if (ret != sizeof(len)) { |
62 | goto err; |
63 | } |
64 | |
65 | if (s->vnet_hdr) { |
66 | /* |
67 | * If vnet_hdr = on, we send vnet header len to make other |
68 | * module(like colo-compare) know how to parse net |
69 | * packet correctly. |
70 | */ |
71 | ssize_t vnet_hdr_len; |
72 | |
73 | vnet_hdr_len = nf->netdev->vnet_hdr_len; |
74 | |
75 | len = htonl(vnet_hdr_len); |
76 | ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len)); |
77 | if (ret != sizeof(len)) { |
78 | goto err; |
79 | } |
80 | } |
81 | |
82 | buf = g_malloc(size); |
83 | iov_to_buf(iov, iovcnt, 0, buf, size); |
84 | ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)buf, size); |
85 | g_free(buf); |
86 | if (ret != size) { |
87 | goto err; |
88 | } |
89 | |
90 | return 0; |
91 | |
92 | err: |
93 | return ret < 0 ? ret : -EIO; |
94 | } |
95 | |
96 | static void redirector_to_filter(NetFilterState *nf, |
97 | const uint8_t *buf, |
98 | int len) |
99 | { |
100 | struct iovec iov = { |
101 | .iov_base = (void *)buf, |
102 | .iov_len = len, |
103 | }; |
104 | |
105 | if (nf->direction == NET_FILTER_DIRECTION_ALL || |
106 | nf->direction == NET_FILTER_DIRECTION_TX) { |
107 | qemu_netfilter_pass_to_next(nf->netdev, 0, &iov, 1, nf); |
108 | } |
109 | |
110 | if (nf->direction == NET_FILTER_DIRECTION_ALL || |
111 | nf->direction == NET_FILTER_DIRECTION_RX) { |
112 | qemu_netfilter_pass_to_next(nf->netdev->peer, 0, &iov, 1, nf); |
113 | } |
114 | } |
115 | |
116 | static int redirector_chr_can_read(void *opaque) |
117 | { |
118 | return REDIRECTOR_MAX_LEN; |
119 | } |
120 | |
121 | static void redirector_chr_read(void *opaque, const uint8_t *buf, int size) |
122 | { |
123 | NetFilterState *nf = opaque; |
124 | MirrorState *s = FILTER_REDIRECTOR(nf); |
125 | int ret; |
126 | |
127 | ret = net_fill_rstate(&s->rs, buf, size); |
128 | |
129 | if (ret == -1) { |
130 | qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, |
131 | NULL, NULL, NULL, true); |
132 | } |
133 | } |
134 | |
135 | static void redirector_chr_event(void *opaque, int event) |
136 | { |
137 | NetFilterState *nf = opaque; |
138 | MirrorState *s = FILTER_REDIRECTOR(nf); |
139 | |
140 | switch (event) { |
141 | case CHR_EVENT_CLOSED: |
142 | qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, |
143 | NULL, NULL, NULL, true); |
144 | break; |
145 | default: |
146 | break; |
147 | } |
148 | } |
149 | |
150 | static ssize_t filter_mirror_receive_iov(NetFilterState *nf, |
151 | NetClientState *sender, |
152 | unsigned flags, |
153 | const struct iovec *iov, |
154 | int iovcnt, |
155 | NetPacketSent *sent_cb) |
156 | { |
157 | MirrorState *s = FILTER_MIRROR(nf); |
158 | int ret; |
159 | |
160 | ret = filter_send(s, iov, iovcnt); |
161 | if (ret) { |
162 | error_report("filter mirror send failed(%s)" , strerror(-ret)); |
163 | } |
164 | |
165 | /* |
166 | * we don't hope this error interrupt the normal |
167 | * path of net packet, so we always return zero. |
168 | */ |
169 | return 0; |
170 | } |
171 | |
172 | static ssize_t filter_redirector_receive_iov(NetFilterState *nf, |
173 | NetClientState *sender, |
174 | unsigned flags, |
175 | const struct iovec *iov, |
176 | int iovcnt, |
177 | NetPacketSent *sent_cb) |
178 | { |
179 | MirrorState *s = FILTER_REDIRECTOR(nf); |
180 | int ret; |
181 | |
182 | if (qemu_chr_fe_backend_connected(&s->chr_out)) { |
183 | ret = filter_send(s, iov, iovcnt); |
184 | if (ret) { |
185 | error_report("filter redirector send failed(%s)" , strerror(-ret)); |
186 | } |
187 | return iov_size(iov, iovcnt); |
188 | } else { |
189 | return 0; |
190 | } |
191 | } |
192 | |
193 | static void filter_mirror_cleanup(NetFilterState *nf) |
194 | { |
195 | MirrorState *s = FILTER_MIRROR(nf); |
196 | |
197 | qemu_chr_fe_deinit(&s->chr_out, false); |
198 | } |
199 | |
200 | static void filter_redirector_cleanup(NetFilterState *nf) |
201 | { |
202 | MirrorState *s = FILTER_REDIRECTOR(nf); |
203 | |
204 | qemu_chr_fe_deinit(&s->chr_in, false); |
205 | qemu_chr_fe_deinit(&s->chr_out, false); |
206 | } |
207 | |
208 | static void filter_mirror_setup(NetFilterState *nf, Error **errp) |
209 | { |
210 | MirrorState *s = FILTER_MIRROR(nf); |
211 | Chardev *chr; |
212 | |
213 | if (s->outdev == NULL) { |
214 | error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "filter-mirror parameter" \ |
215 | " 'outdev' cannot be empty" ); |
216 | return; |
217 | } |
218 | |
219 | chr = qemu_chr_find(s->outdev); |
220 | if (chr == NULL) { |
221 | error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, |
222 | "Device '%s' not found" , s->outdev); |
223 | return; |
224 | } |
225 | |
226 | qemu_chr_fe_init(&s->chr_out, chr, errp); |
227 | } |
228 | |
229 | static void redirector_rs_finalize(SocketReadState *rs) |
230 | { |
231 | MirrorState *s = container_of(rs, MirrorState, rs); |
232 | NetFilterState *nf = NETFILTER(s); |
233 | |
234 | redirector_to_filter(nf, rs->buf, rs->packet_len); |
235 | } |
236 | |
237 | static void filter_redirector_setup(NetFilterState *nf, Error **errp) |
238 | { |
239 | MirrorState *s = FILTER_REDIRECTOR(nf); |
240 | Chardev *chr; |
241 | |
242 | if (!s->indev && !s->outdev) { |
243 | error_setg(errp, "filter redirector needs 'indev' or " |
244 | "'outdev' at least one property set" ); |
245 | return; |
246 | } else if (s->indev && s->outdev) { |
247 | if (!strcmp(s->indev, s->outdev)) { |
248 | error_setg(errp, "'indev' and 'outdev' could not be same " |
249 | "for filter redirector" ); |
250 | return; |
251 | } |
252 | } |
253 | |
254 | net_socket_rs_init(&s->rs, redirector_rs_finalize, s->vnet_hdr); |
255 | |
256 | if (s->indev) { |
257 | chr = qemu_chr_find(s->indev); |
258 | if (chr == NULL) { |
259 | error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, |
260 | "IN Device '%s' not found" , s->indev); |
261 | return; |
262 | } |
263 | |
264 | if (!qemu_chr_fe_init(&s->chr_in, chr, errp)) { |
265 | return; |
266 | } |
267 | |
268 | qemu_chr_fe_set_handlers(&s->chr_in, redirector_chr_can_read, |
269 | redirector_chr_read, redirector_chr_event, |
270 | NULL, nf, NULL, true); |
271 | } |
272 | |
273 | if (s->outdev) { |
274 | chr = qemu_chr_find(s->outdev); |
275 | if (chr == NULL) { |
276 | error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, |
277 | "OUT Device '%s' not found" , s->outdev); |
278 | return; |
279 | } |
280 | if (!qemu_chr_fe_init(&s->chr_out, chr, errp)) { |
281 | return; |
282 | } |
283 | } |
284 | } |
285 | |
286 | static void filter_mirror_class_init(ObjectClass *oc, void *data) |
287 | { |
288 | NetFilterClass *nfc = NETFILTER_CLASS(oc); |
289 | |
290 | nfc->setup = filter_mirror_setup; |
291 | nfc->cleanup = filter_mirror_cleanup; |
292 | nfc->receive_iov = filter_mirror_receive_iov; |
293 | } |
294 | |
295 | static void filter_redirector_class_init(ObjectClass *oc, void *data) |
296 | { |
297 | NetFilterClass *nfc = NETFILTER_CLASS(oc); |
298 | |
299 | nfc->setup = filter_redirector_setup; |
300 | nfc->cleanup = filter_redirector_cleanup; |
301 | nfc->receive_iov = filter_redirector_receive_iov; |
302 | } |
303 | |
304 | static char *filter_redirector_get_indev(Object *obj, Error **errp) |
305 | { |
306 | MirrorState *s = FILTER_REDIRECTOR(obj); |
307 | |
308 | return g_strdup(s->indev); |
309 | } |
310 | |
311 | static void filter_redirector_set_indev(Object *obj, |
312 | const char *value, |
313 | Error **errp) |
314 | { |
315 | MirrorState *s = FILTER_REDIRECTOR(obj); |
316 | |
317 | g_free(s->indev); |
318 | s->indev = g_strdup(value); |
319 | } |
320 | |
321 | static char *filter_mirror_get_outdev(Object *obj, Error **errp) |
322 | { |
323 | MirrorState *s = FILTER_MIRROR(obj); |
324 | |
325 | return g_strdup(s->outdev); |
326 | } |
327 | |
328 | static void filter_mirror_set_outdev(Object *obj, |
329 | const char *value, |
330 | Error **errp) |
331 | { |
332 | MirrorState *s = FILTER_MIRROR(obj); |
333 | |
334 | g_free(s->outdev); |
335 | s->outdev = g_strdup(value); |
336 | if (!s->outdev) { |
337 | error_setg(errp, "filter mirror needs 'outdev' " |
338 | "property set" ); |
339 | return; |
340 | } |
341 | } |
342 | |
343 | static bool filter_mirror_get_vnet_hdr(Object *obj, Error **errp) |
344 | { |
345 | MirrorState *s = FILTER_MIRROR(obj); |
346 | |
347 | return s->vnet_hdr; |
348 | } |
349 | |
350 | static void filter_mirror_set_vnet_hdr(Object *obj, bool value, Error **errp) |
351 | { |
352 | MirrorState *s = FILTER_MIRROR(obj); |
353 | |
354 | s->vnet_hdr = value; |
355 | } |
356 | |
357 | static char *filter_redirector_get_outdev(Object *obj, Error **errp) |
358 | { |
359 | MirrorState *s = FILTER_REDIRECTOR(obj); |
360 | |
361 | return g_strdup(s->outdev); |
362 | } |
363 | |
364 | static void filter_redirector_set_outdev(Object *obj, |
365 | const char *value, |
366 | Error **errp) |
367 | { |
368 | MirrorState *s = FILTER_REDIRECTOR(obj); |
369 | |
370 | g_free(s->outdev); |
371 | s->outdev = g_strdup(value); |
372 | } |
373 | |
374 | static bool filter_redirector_get_vnet_hdr(Object *obj, Error **errp) |
375 | { |
376 | MirrorState *s = FILTER_REDIRECTOR(obj); |
377 | |
378 | return s->vnet_hdr; |
379 | } |
380 | |
381 | static void filter_redirector_set_vnet_hdr(Object *obj, |
382 | bool value, |
383 | Error **errp) |
384 | { |
385 | MirrorState *s = FILTER_REDIRECTOR(obj); |
386 | |
387 | s->vnet_hdr = value; |
388 | } |
389 | |
390 | static void filter_mirror_init(Object *obj) |
391 | { |
392 | MirrorState *s = FILTER_MIRROR(obj); |
393 | |
394 | object_property_add_str(obj, "outdev" , filter_mirror_get_outdev, |
395 | filter_mirror_set_outdev, NULL); |
396 | |
397 | s->vnet_hdr = false; |
398 | object_property_add_bool(obj, "vnet_hdr_support" , |
399 | filter_mirror_get_vnet_hdr, |
400 | filter_mirror_set_vnet_hdr, NULL); |
401 | } |
402 | |
403 | static void filter_redirector_init(Object *obj) |
404 | { |
405 | MirrorState *s = FILTER_REDIRECTOR(obj); |
406 | |
407 | object_property_add_str(obj, "indev" , filter_redirector_get_indev, |
408 | filter_redirector_set_indev, NULL); |
409 | object_property_add_str(obj, "outdev" , filter_redirector_get_outdev, |
410 | filter_redirector_set_outdev, NULL); |
411 | |
412 | s->vnet_hdr = false; |
413 | object_property_add_bool(obj, "vnet_hdr_support" , |
414 | filter_redirector_get_vnet_hdr, |
415 | filter_redirector_set_vnet_hdr, NULL); |
416 | } |
417 | |
418 | static void filter_mirror_fini(Object *obj) |
419 | { |
420 | MirrorState *s = FILTER_MIRROR(obj); |
421 | |
422 | g_free(s->outdev); |
423 | } |
424 | |
425 | static void filter_redirector_fini(Object *obj) |
426 | { |
427 | MirrorState *s = FILTER_REDIRECTOR(obj); |
428 | |
429 | g_free(s->indev); |
430 | g_free(s->outdev); |
431 | } |
432 | |
433 | static const TypeInfo filter_redirector_info = { |
434 | .name = TYPE_FILTER_REDIRECTOR, |
435 | .parent = TYPE_NETFILTER, |
436 | .class_init = filter_redirector_class_init, |
437 | .instance_init = filter_redirector_init, |
438 | .instance_finalize = filter_redirector_fini, |
439 | .instance_size = sizeof(MirrorState), |
440 | }; |
441 | |
442 | static const TypeInfo filter_mirror_info = { |
443 | .name = TYPE_FILTER_MIRROR, |
444 | .parent = TYPE_NETFILTER, |
445 | .class_init = filter_mirror_class_init, |
446 | .instance_init = filter_mirror_init, |
447 | .instance_finalize = filter_mirror_fini, |
448 | .instance_size = sizeof(MirrorState), |
449 | }; |
450 | |
451 | static void register_types(void) |
452 | { |
453 | type_register_static(&filter_mirror_info); |
454 | type_register_static(&filter_redirector_info); |
455 | } |
456 | |
457 | type_init(register_types); |
458 | |