xref: /openbmc/qemu/net/filter-buffer.c (revision 69e92bd558d71fdbd0c1989391b20edcc700daa9)
17dbb11c8SYang Hongyang /*
27dbb11c8SYang Hongyang  * Copyright (c) 2015 FUJITSU LIMITED
37dbb11c8SYang Hongyang  * Author: Yang Hongyang <yanghy@cn.fujitsu.com>
47dbb11c8SYang Hongyang  *
57dbb11c8SYang Hongyang  * This work is licensed under the terms of the GNU GPL, version 2 or
67dbb11c8SYang Hongyang  * later.  See the COPYING file in the top-level directory.
77dbb11c8SYang Hongyang  */
87dbb11c8SYang Hongyang 
92744d920SPeter Maydell #include "qemu/osdep.h"
107dbb11c8SYang Hongyang #include "net/filter.h"
117dbb11c8SYang Hongyang #include "net/queue.h"
12da34e65cSMarkus Armbruster #include "qapi/error.h"
137dbb11c8SYang Hongyang #include "qemu/timer.h"
147dbb11c8SYang Hongyang #include "qemu/iov.h"
15eb815e24SMarkus Armbruster #include "qapi/qapi-builtin-visit.h"
167dbb11c8SYang Hongyang #include "qapi/qmp/qerror.h"
177dbb11c8SYang Hongyang #include "qom/object.h"
187dbb11c8SYang Hongyang 
197dbb11c8SYang Hongyang #define TYPE_FILTER_BUFFER "filter-buffer"
207dbb11c8SYang Hongyang 
218063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(FilterBufferState, FILTER_BUFFER)
227dbb11c8SYang Hongyang 
23db1015e9SEduardo Habkost struct FilterBufferState {
247dbb11c8SYang Hongyang     NetFilterState parent_obj;
257dbb11c8SYang Hongyang 
267dbb11c8SYang Hongyang     NetQueue *incoming_queue;
277dbb11c8SYang Hongyang     uint32_t interval;
287dbb11c8SYang Hongyang     QEMUTimer release_timer;
29db1015e9SEduardo Habkost };
307dbb11c8SYang Hongyang 
filter_buffer_flush(NetFilterState * nf)317dbb11c8SYang Hongyang static void filter_buffer_flush(NetFilterState *nf)
327dbb11c8SYang Hongyang {
337dbb11c8SYang Hongyang     FilterBufferState *s = FILTER_BUFFER(nf);
347dbb11c8SYang Hongyang 
357dbb11c8SYang Hongyang     if (!qemu_net_queue_flush(s->incoming_queue)) {
367dbb11c8SYang Hongyang         /* Unable to empty the queue, purge remaining packets */
377dbb11c8SYang Hongyang         qemu_net_queue_purge(s->incoming_queue, nf->netdev);
387dbb11c8SYang Hongyang     }
397dbb11c8SYang Hongyang }
407dbb11c8SYang Hongyang 
filter_buffer_release_timer(void * opaque)417dbb11c8SYang Hongyang static void filter_buffer_release_timer(void *opaque)
427dbb11c8SYang Hongyang {
437dbb11c8SYang Hongyang     NetFilterState *nf = opaque;
447dbb11c8SYang Hongyang     FilterBufferState *s = FILTER_BUFFER(nf);
457dbb11c8SYang Hongyang 
467dbb11c8SYang Hongyang     /*
477dbb11c8SYang Hongyang      * Note: filter_buffer_flush() drops packets that can't be sent
487dbb11c8SYang Hongyang      * TODO: We should leave them queued.  But currently there's no way
497dbb11c8SYang Hongyang      * for the next filter or receiver to notify us that it can receive
507dbb11c8SYang Hongyang      * more packets.
517dbb11c8SYang Hongyang      */
527dbb11c8SYang Hongyang     filter_buffer_flush(nf);
537dbb11c8SYang Hongyang     /* Timer rearmed to fire again in s->interval microseconds. */
547dbb11c8SYang Hongyang     timer_mod(&s->release_timer,
557dbb11c8SYang Hongyang               qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + s->interval);
567dbb11c8SYang Hongyang }
577dbb11c8SYang Hongyang 
587dbb11c8SYang Hongyang /* filter APIs */
filter_buffer_receive_iov(NetFilterState * nf,NetClientState * sender,unsigned flags,const struct iovec * iov,int iovcnt,NetPacketSent * sent_cb)597dbb11c8SYang Hongyang static ssize_t filter_buffer_receive_iov(NetFilterState *nf,
607dbb11c8SYang Hongyang                                          NetClientState *sender,
617dbb11c8SYang Hongyang                                          unsigned flags,
627dbb11c8SYang Hongyang                                          const struct iovec *iov,
637dbb11c8SYang Hongyang                                          int iovcnt,
647dbb11c8SYang Hongyang                                          NetPacketSent *sent_cb)
657dbb11c8SYang Hongyang {
667dbb11c8SYang Hongyang     FilterBufferState *s = FILTER_BUFFER(nf);
677dbb11c8SYang Hongyang 
687dbb11c8SYang Hongyang     /*
697dbb11c8SYang Hongyang      * We return size when buffer a packet, the sender will take it as
707dbb11c8SYang Hongyang      * a already sent packet, so sent_cb should not be called later.
717dbb11c8SYang Hongyang      *
727dbb11c8SYang Hongyang      * FIXME: Even if the guest can't receive packets for some reasons,
737dbb11c8SYang Hongyang      * the filter can still accept packets until its internal queue is full.
747dbb11c8SYang Hongyang      * For example:
757dbb11c8SYang Hongyang      *   For some reason, receiver could not receive more packets
76b8c4b67eSPhilippe Mathieu-Daudé      * (.can_receive() returns false). Without a filter, at most one packet
777dbb11c8SYang Hongyang      * will be queued in incoming queue and sender's poll will be disabled
787dbb11c8SYang Hongyang      * unit its sent_cb() was called. With a filter, it will keep receiving
797dbb11c8SYang Hongyang      * the packets without caring about the receiver. This is suboptimal.
807dbb11c8SYang Hongyang      * May need more thoughts (e.g keeping sent_cb).
817dbb11c8SYang Hongyang      */
827dbb11c8SYang Hongyang     qemu_net_queue_append_iov(s->incoming_queue, sender, flags,
837dbb11c8SYang Hongyang                               iov, iovcnt, NULL);
847dbb11c8SYang Hongyang     return iov_size(iov, iovcnt);
857dbb11c8SYang Hongyang }
867dbb11c8SYang Hongyang 
filter_buffer_cleanup(NetFilterState * nf)877dbb11c8SYang Hongyang static void filter_buffer_cleanup(NetFilterState *nf)
887dbb11c8SYang Hongyang {
897dbb11c8SYang Hongyang     FilterBufferState *s = FILTER_BUFFER(nf);
907dbb11c8SYang Hongyang 
917dbb11c8SYang Hongyang     if (s->interval) {
927dbb11c8SYang Hongyang         timer_del(&s->release_timer);
937dbb11c8SYang Hongyang     }
947dbb11c8SYang Hongyang 
957dbb11c8SYang Hongyang     /* flush packets */
967dbb11c8SYang Hongyang     if (s->incoming_queue) {
977dbb11c8SYang Hongyang         filter_buffer_flush(nf);
987dbb11c8SYang Hongyang         g_free(s->incoming_queue);
997dbb11c8SYang Hongyang     }
1007dbb11c8SYang Hongyang }
1017dbb11c8SYang Hongyang 
filter_buffer_setup_timer(NetFilterState * nf)102f1b2bc60Szhanghailiang static void filter_buffer_setup_timer(NetFilterState *nf)
103f1b2bc60Szhanghailiang {
104f1b2bc60Szhanghailiang     FilterBufferState *s = FILTER_BUFFER(nf);
105f1b2bc60Szhanghailiang 
106f1b2bc60Szhanghailiang     if (s->interval) {
107f1b2bc60Szhanghailiang         timer_init_us(&s->release_timer, QEMU_CLOCK_VIRTUAL,
108f1b2bc60Szhanghailiang                       filter_buffer_release_timer, nf);
109f1b2bc60Szhanghailiang         /* Timer armed to fire in s->interval microseconds. */
110f1b2bc60Szhanghailiang         timer_mod(&s->release_timer,
111f1b2bc60Szhanghailiang                   qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + s->interval);
112f1b2bc60Szhanghailiang     }
113f1b2bc60Szhanghailiang }
114f1b2bc60Szhanghailiang 
filter_buffer_setup(NetFilterState * nf,Error ** errp)1157dbb11c8SYang Hongyang static void filter_buffer_setup(NetFilterState *nf, Error **errp)
1167dbb11c8SYang Hongyang {
1177dbb11c8SYang Hongyang     FilterBufferState *s = FILTER_BUFFER(nf);
1187dbb11c8SYang Hongyang 
1197dbb11c8SYang Hongyang     /*
1207dbb11c8SYang Hongyang      * We may want to accept zero interval when VM FT solutions like MC
1217dbb11c8SYang Hongyang      * or COLO use this filter to release packets on demand.
1227dbb11c8SYang Hongyang      */
1237dbb11c8SYang Hongyang     if (!s->interval) {
1247dbb11c8SYang Hongyang         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "interval",
1257dbb11c8SYang Hongyang                    "a non-zero interval");
1267dbb11c8SYang Hongyang         return;
1277dbb11c8SYang Hongyang     }
1287dbb11c8SYang Hongyang 
1297dbb11c8SYang Hongyang     s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf);
130f1b2bc60Szhanghailiang     filter_buffer_setup_timer(nf);
131f1b2bc60Szhanghailiang }
132f1b2bc60Szhanghailiang 
filter_buffer_status_changed(NetFilterState * nf,Error ** errp)133f1b2bc60Szhanghailiang static void filter_buffer_status_changed(NetFilterState *nf, Error **errp)
134f1b2bc60Szhanghailiang {
135f1b2bc60Szhanghailiang     FilterBufferState *s = FILTER_BUFFER(nf);
136f1b2bc60Szhanghailiang 
137f1b2bc60Szhanghailiang     if (!nf->on) {
1387dbb11c8SYang Hongyang         if (s->interval) {
139f1b2bc60Szhanghailiang             timer_del(&s->release_timer);
140f1b2bc60Szhanghailiang         }
141f1b2bc60Szhanghailiang         filter_buffer_flush(nf);
142f1b2bc60Szhanghailiang     } else {
143f1b2bc60Szhanghailiang         filter_buffer_setup_timer(nf);
1447dbb11c8SYang Hongyang     }
1457dbb11c8SYang Hongyang }
1467dbb11c8SYang Hongyang 
filter_buffer_get_interval(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)147d7bce999SEric Blake static void filter_buffer_get_interval(Object *obj, Visitor *v,
148d7bce999SEric Blake                                        const char *name, void *opaque,
149d7bce999SEric Blake                                        Error **errp)
1507dbb11c8SYang Hongyang {
1517dbb11c8SYang Hongyang     FilterBufferState *s = FILTER_BUFFER(obj);
1527dbb11c8SYang Hongyang     uint32_t value = s->interval;
1537dbb11c8SYang Hongyang 
15451e72bc1SEric Blake     visit_type_uint32(v, name, &value, errp);
1557dbb11c8SYang Hongyang }
1567dbb11c8SYang Hongyang 
filter_buffer_set_interval(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)157d7bce999SEric Blake static void filter_buffer_set_interval(Object *obj, Visitor *v,
158d7bce999SEric Blake                                        const char *name, void *opaque,
159d7bce999SEric Blake                                        Error **errp)
1607dbb11c8SYang Hongyang {
1617dbb11c8SYang Hongyang     FilterBufferState *s = FILTER_BUFFER(obj);
1627dbb11c8SYang Hongyang     uint32_t value;
1637dbb11c8SYang Hongyang 
164668f62ecSMarkus Armbruster     if (!visit_type_uint32(v, name, &value, errp)) {
165dcfe4805SMarkus Armbruster         return;
1667dbb11c8SYang Hongyang     }
1677dbb11c8SYang Hongyang     if (!value) {
168dcfe4805SMarkus Armbruster         error_setg(errp, "Property '%s.%s' requires a positive value",
1697dbb11c8SYang Hongyang                    object_get_typename(obj), name);
170dcfe4805SMarkus Armbruster         return;
1717dbb11c8SYang Hongyang     }
1727dbb11c8SYang Hongyang     s->interval = value;
1737dbb11c8SYang Hongyang }
1747dbb11c8SYang Hongyang 
filter_buffer_class_init(ObjectClass * oc,void * data)1756d11ea6dSEduardo Habkost static void filter_buffer_class_init(ObjectClass *oc, void *data)
1766d11ea6dSEduardo Habkost {
1776d11ea6dSEduardo Habkost     NetFilterClass *nfc = NETFILTER_CLASS(oc);
1786d11ea6dSEduardo Habkost 
179*f0e34a06SEduardo Habkost     object_class_property_add(oc, "interval", "uint32",
180*f0e34a06SEduardo Habkost                               filter_buffer_get_interval,
181*f0e34a06SEduardo Habkost                               filter_buffer_set_interval, NULL, NULL);
182*f0e34a06SEduardo Habkost 
1836d11ea6dSEduardo Habkost     nfc->setup = filter_buffer_setup;
1846d11ea6dSEduardo Habkost     nfc->cleanup = filter_buffer_cleanup;
1856d11ea6dSEduardo Habkost     nfc->receive_iov = filter_buffer_receive_iov;
1866d11ea6dSEduardo Habkost     nfc->status_changed = filter_buffer_status_changed;
1876d11ea6dSEduardo Habkost }
1886d11ea6dSEduardo Habkost 
1897dbb11c8SYang Hongyang static const TypeInfo filter_buffer_info = {
1907dbb11c8SYang Hongyang     .name = TYPE_FILTER_BUFFER,
1917dbb11c8SYang Hongyang     .parent = TYPE_NETFILTER,
1927dbb11c8SYang Hongyang     .class_init = filter_buffer_class_init,
1937dbb11c8SYang Hongyang     .instance_size = sizeof(FilterBufferState),
1947dbb11c8SYang Hongyang };
1957dbb11c8SYang Hongyang 
register_types(void)1967dbb11c8SYang Hongyang static void register_types(void)
1977dbb11c8SYang Hongyang {
1987dbb11c8SYang Hongyang     type_register_static(&filter_buffer_info);
1997dbb11c8SYang Hongyang }
2007dbb11c8SYang Hongyang 
2017dbb11c8SYang Hongyang type_init(register_types);
202