xref: /openbmc/qemu/net/filter.c (revision 87c9b5e0)
1 /*
2  * Copyright (c) 2015 FUJITSU LIMITED
3  * Author: Yang Hongyang <yanghy@cn.fujitsu.com>
4  *
5  * This work is licensed under the terms of the GNU GPL, version 2 or
6  * later.  See the COPYING file in the top-level directory.
7  */
8 
9 #include "qemu-common.h"
10 #include "qapi/qmp/qerror.h"
11 #include "qemu/error-report.h"
12 
13 #include "net/filter.h"
14 #include "net/net.h"
15 #include "net/vhost_net.h"
16 #include "qom/object_interfaces.h"
17 #include "qemu/iov.h"
18 
19 ssize_t qemu_netfilter_receive(NetFilterState *nf,
20                                NetFilterDirection direction,
21                                NetClientState *sender,
22                                unsigned flags,
23                                const struct iovec *iov,
24                                int iovcnt,
25                                NetPacketSent *sent_cb)
26 {
27     if (nf->direction == direction ||
28         nf->direction == NET_FILTER_DIRECTION_ALL) {
29         return NETFILTER_GET_CLASS(OBJECT(nf))->receive_iov(
30                                    nf, sender, flags, iov, iovcnt, sent_cb);
31     }
32 
33     return 0;
34 }
35 
36 static NetFilterState *netfilter_next(NetFilterState *nf,
37                                       NetFilterDirection dir)
38 {
39     NetFilterState *next;
40 
41     if (dir == NET_FILTER_DIRECTION_TX) {
42         /* forward walk through filters */
43         next = QTAILQ_NEXT(nf, next);
44     } else {
45         /* reverse order */
46         next = QTAILQ_PREV(nf, NetFilterHead, next);
47     }
48 
49     return next;
50 }
51 
52 ssize_t qemu_netfilter_pass_to_next(NetClientState *sender,
53                                     unsigned flags,
54                                     const struct iovec *iov,
55                                     int iovcnt,
56                                     void *opaque)
57 {
58     int ret = 0;
59     int direction;
60     NetFilterState *nf = opaque;
61     NetFilterState *next = NULL;
62 
63     if (!sender || !sender->peer) {
64         /* no receiver, or sender been deleted, no need to pass it further */
65         goto out;
66     }
67 
68     if (nf->direction == NET_FILTER_DIRECTION_ALL) {
69         if (sender == nf->netdev) {
70             /* This packet is sent by netdev itself */
71             direction = NET_FILTER_DIRECTION_TX;
72         } else {
73             direction = NET_FILTER_DIRECTION_RX;
74         }
75     } else {
76         direction = nf->direction;
77     }
78 
79     next = netfilter_next(nf, direction);
80     while (next) {
81         /*
82          * if qemu_netfilter_pass_to_next been called, means that
83          * the packet has been hold by filter and has already retured size
84          * to the sender, so sent_cb shouldn't be called later, just
85          * pass NULL to next.
86          */
87         ret = qemu_netfilter_receive(next, direction, sender, flags, iov,
88                                      iovcnt, NULL);
89         if (ret) {
90             return ret;
91         }
92         next = netfilter_next(next, direction);
93     }
94 
95     /*
96      * We have gone through all filters, pass it to receiver.
97      * Do the valid check again incase sender or receiver been
98      * deleted while we go through filters.
99      */
100     if (sender && sender->peer) {
101         qemu_net_queue_send_iov(sender->peer->incoming_queue,
102                                 sender, flags, iov, iovcnt, NULL);
103     }
104 
105 out:
106     /* no receiver, or sender been deleted */
107     return iov_size(iov, iovcnt);
108 }
109 
110 static char *netfilter_get_netdev_id(Object *obj, Error **errp)
111 {
112     NetFilterState *nf = NETFILTER(obj);
113 
114     return g_strdup(nf->netdev_id);
115 }
116 
117 static void netfilter_set_netdev_id(Object *obj, const char *str, Error **errp)
118 {
119     NetFilterState *nf = NETFILTER(obj);
120 
121     nf->netdev_id = g_strdup(str);
122 }
123 
124 static int netfilter_get_direction(Object *obj, Error **errp G_GNUC_UNUSED)
125 {
126     NetFilterState *nf = NETFILTER(obj);
127     return nf->direction;
128 }
129 
130 static void netfilter_set_direction(Object *obj, int direction, Error **errp)
131 {
132     NetFilterState *nf = NETFILTER(obj);
133     nf->direction = direction;
134 }
135 
136 static void netfilter_init(Object *obj)
137 {
138     object_property_add_str(obj, "netdev",
139                             netfilter_get_netdev_id, netfilter_set_netdev_id,
140                             NULL);
141     object_property_add_enum(obj, "queue", "NetFilterDirection",
142                              NetFilterDirection_lookup,
143                              netfilter_get_direction, netfilter_set_direction,
144                              NULL);
145 }
146 
147 static void netfilter_complete(UserCreatable *uc, Error **errp)
148 {
149     NetFilterState *nf = NETFILTER(uc);
150     NetClientState *ncs[MAX_QUEUE_NUM];
151     NetFilterClass *nfc = NETFILTER_GET_CLASS(uc);
152     int queues;
153     Error *local_err = NULL;
154 
155     if (!nf->netdev_id) {
156         error_setg(errp, "Parameter 'netdev' is required");
157         return;
158     }
159 
160     queues = qemu_find_net_clients_except(nf->netdev_id, ncs,
161                                           NET_CLIENT_OPTIONS_KIND_NIC,
162                                           MAX_QUEUE_NUM);
163     if (queues < 1) {
164         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "netdev",
165                    "a network backend id");
166         return;
167     } else if (queues > 1) {
168         error_setg(errp, "multiqueue is not supported");
169         return;
170     }
171 
172     if (get_vhost_net(ncs[0])) {
173         error_setg(errp, "Vhost is not supported");
174         return;
175     }
176 
177     nf->netdev = ncs[0];
178 
179     if (nfc->setup) {
180         nfc->setup(nf, &local_err);
181         if (local_err) {
182             error_propagate(errp, local_err);
183             return;
184         }
185     }
186     QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next);
187 }
188 
189 static void netfilter_finalize(Object *obj)
190 {
191     NetFilterState *nf = NETFILTER(obj);
192     NetFilterClass *nfc = NETFILTER_GET_CLASS(obj);
193 
194     if (nfc->cleanup) {
195         nfc->cleanup(nf);
196     }
197 
198     if (nf->netdev && !QTAILQ_EMPTY(&nf->netdev->filters)) {
199         QTAILQ_REMOVE(&nf->netdev->filters, nf, next);
200     }
201     g_free(nf->netdev_id);
202 }
203 
204 static void netfilter_class_init(ObjectClass *oc, void *data)
205 {
206     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
207 
208     ucc->complete = netfilter_complete;
209 }
210 
211 static const TypeInfo netfilter_info = {
212     .name = TYPE_NETFILTER,
213     .parent = TYPE_OBJECT,
214     .abstract = true,
215     .class_size = sizeof(NetFilterClass),
216     .class_init = netfilter_class_init,
217     .instance_size = sizeof(NetFilterState),
218     .instance_init = netfilter_init,
219     .instance_finalize = netfilter_finalize,
220     .interfaces = (InterfaceInfo[]) {
221         { TYPE_USER_CREATABLE },
222         { }
223     }
224 };
225 
226 static void register_types(void)
227 {
228     type_register_static(&netfilter_info);
229 }
230 
231 type_init(register_types);
232