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