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/osdep.h" |
10 | #include "qapi/error.h" |
11 | #include "qapi/qmp/qerror.h" |
12 | #include "qemu/error-report.h" |
13 | |
14 | #include "net/filter.h" |
15 | #include "net/net.h" |
16 | #include "net/vhost_net.h" |
17 | #include "qom/object_interfaces.h" |
18 | #include "qemu/iov.h" |
19 | #include "qemu/module.h" |
20 | #include "net/colo.h" |
21 | #include "migration/colo.h" |
22 | |
23 | static inline bool qemu_can_skip_netfilter(NetFilterState *nf) |
24 | { |
25 | return !nf->on; |
26 | } |
27 | |
28 | ssize_t qemu_netfilter_receive(NetFilterState *nf, |
29 | NetFilterDirection direction, |
30 | NetClientState *sender, |
31 | unsigned flags, |
32 | const struct iovec *iov, |
33 | int iovcnt, |
34 | NetPacketSent *sent_cb) |
35 | { |
36 | if (qemu_can_skip_netfilter(nf)) { |
37 | return 0; |
38 | } |
39 | if (nf->direction == direction || |
40 | nf->direction == NET_FILTER_DIRECTION_ALL) { |
41 | return NETFILTER_GET_CLASS(OBJECT(nf))->receive_iov( |
42 | nf, sender, flags, iov, iovcnt, sent_cb); |
43 | } |
44 | |
45 | return 0; |
46 | } |
47 | |
48 | static NetFilterState *netfilter_next(NetFilterState *nf, |
49 | NetFilterDirection dir) |
50 | { |
51 | NetFilterState *next; |
52 | |
53 | if (dir == NET_FILTER_DIRECTION_TX) { |
54 | /* forward walk through filters */ |
55 | next = QTAILQ_NEXT(nf, next); |
56 | } else { |
57 | /* reverse order */ |
58 | next = QTAILQ_PREV(nf, next); |
59 | } |
60 | |
61 | return next; |
62 | } |
63 | |
64 | ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, |
65 | unsigned flags, |
66 | const struct iovec *iov, |
67 | int iovcnt, |
68 | void *opaque) |
69 | { |
70 | int ret = 0; |
71 | int direction; |
72 | NetFilterState *nf = opaque; |
73 | NetFilterState *next = NULL; |
74 | |
75 | if (!sender || !sender->peer) { |
76 | /* no receiver, or sender been deleted, no need to pass it further */ |
77 | goto out; |
78 | } |
79 | |
80 | if (nf->direction == NET_FILTER_DIRECTION_ALL) { |
81 | if (sender == nf->netdev) { |
82 | /* This packet is sent by netdev itself */ |
83 | direction = NET_FILTER_DIRECTION_TX; |
84 | } else { |
85 | direction = NET_FILTER_DIRECTION_RX; |
86 | } |
87 | } else { |
88 | direction = nf->direction; |
89 | } |
90 | |
91 | next = netfilter_next(nf, direction); |
92 | while (next) { |
93 | /* |
94 | * if qemu_netfilter_pass_to_next been called, means that |
95 | * the packet has been hold by filter and has already retured size |
96 | * to the sender, so sent_cb shouldn't be called later, just |
97 | * pass NULL to next. |
98 | */ |
99 | ret = qemu_netfilter_receive(next, direction, sender, flags, iov, |
100 | iovcnt, NULL); |
101 | if (ret) { |
102 | return ret; |
103 | } |
104 | next = netfilter_next(next, direction); |
105 | } |
106 | |
107 | /* |
108 | * We have gone through all filters, pass it to receiver. |
109 | * Do the valid check again incase sender or receiver been |
110 | * deleted while we go through filters. |
111 | */ |
112 | if (sender && sender->peer) { |
113 | qemu_net_queue_send_iov(sender->peer->incoming_queue, |
114 | sender, flags, iov, iovcnt, NULL); |
115 | } |
116 | |
117 | out: |
118 | /* no receiver, or sender been deleted */ |
119 | return iov_size(iov, iovcnt); |
120 | } |
121 | |
122 | static char *netfilter_get_netdev_id(Object *obj, Error **errp) |
123 | { |
124 | NetFilterState *nf = NETFILTER(obj); |
125 | |
126 | return g_strdup(nf->netdev_id); |
127 | } |
128 | |
129 | static void netfilter_set_netdev_id(Object *obj, const char *str, Error **errp) |
130 | { |
131 | NetFilterState *nf = NETFILTER(obj); |
132 | |
133 | nf->netdev_id = g_strdup(str); |
134 | } |
135 | |
136 | static int netfilter_get_direction(Object *obj, Error **errp G_GNUC_UNUSED) |
137 | { |
138 | NetFilterState *nf = NETFILTER(obj); |
139 | return nf->direction; |
140 | } |
141 | |
142 | static void netfilter_set_direction(Object *obj, int direction, Error **errp) |
143 | { |
144 | NetFilterState *nf = NETFILTER(obj); |
145 | nf->direction = direction; |
146 | } |
147 | |
148 | static char *netfilter_get_status(Object *obj, Error **errp) |
149 | { |
150 | NetFilterState *nf = NETFILTER(obj); |
151 | |
152 | return nf->on ? g_strdup("on" ) : g_strdup("off" ); |
153 | } |
154 | |
155 | static void netfilter_set_status(Object *obj, const char *str, Error **errp) |
156 | { |
157 | NetFilterState *nf = NETFILTER(obj); |
158 | NetFilterClass *nfc = NETFILTER_GET_CLASS(obj); |
159 | |
160 | if (strcmp(str, "on" ) && strcmp(str, "off" )) { |
161 | error_setg(errp, "Invalid value for netfilter status, " |
162 | "should be 'on' or 'off'" ); |
163 | return; |
164 | } |
165 | if (nf->on == !strcmp(str, "on" )) { |
166 | return; |
167 | } |
168 | nf->on = !nf->on; |
169 | if (nf->netdev && nfc->status_changed) { |
170 | nfc->status_changed(nf, errp); |
171 | } |
172 | } |
173 | |
174 | static void netfilter_init(Object *obj) |
175 | { |
176 | NetFilterState *nf = NETFILTER(obj); |
177 | |
178 | nf->on = true; |
179 | |
180 | object_property_add_str(obj, "netdev" , |
181 | netfilter_get_netdev_id, netfilter_set_netdev_id, |
182 | NULL); |
183 | object_property_add_enum(obj, "queue" , "NetFilterDirection" , |
184 | &NetFilterDirection_lookup, |
185 | netfilter_get_direction, netfilter_set_direction, |
186 | NULL); |
187 | object_property_add_str(obj, "status" , |
188 | netfilter_get_status, netfilter_set_status, |
189 | NULL); |
190 | } |
191 | |
192 | static void netfilter_complete(UserCreatable *uc, Error **errp) |
193 | { |
194 | NetFilterState *nf = NETFILTER(uc); |
195 | NetClientState *ncs[MAX_QUEUE_NUM]; |
196 | NetFilterClass *nfc = NETFILTER_GET_CLASS(uc); |
197 | int queues; |
198 | Error *local_err = NULL; |
199 | |
200 | if (!nf->netdev_id) { |
201 | error_setg(errp, "Parameter 'netdev' is required" ); |
202 | return; |
203 | } |
204 | |
205 | queues = qemu_find_net_clients_except(nf->netdev_id, ncs, |
206 | NET_CLIENT_DRIVER_NIC, |
207 | MAX_QUEUE_NUM); |
208 | if (queues < 1) { |
209 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "netdev" , |
210 | "a network backend id" ); |
211 | return; |
212 | } else if (queues > 1) { |
213 | error_setg(errp, "multiqueue is not supported" ); |
214 | return; |
215 | } |
216 | |
217 | if (get_vhost_net(ncs[0])) { |
218 | error_setg(errp, "Vhost is not supported" ); |
219 | return; |
220 | } |
221 | |
222 | nf->netdev = ncs[0]; |
223 | |
224 | if (nfc->setup) { |
225 | nfc->setup(nf, &local_err); |
226 | if (local_err) { |
227 | error_propagate(errp, local_err); |
228 | return; |
229 | } |
230 | } |
231 | QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next); |
232 | } |
233 | |
234 | static void netfilter_finalize(Object *obj) |
235 | { |
236 | NetFilterState *nf = NETFILTER(obj); |
237 | NetFilterClass *nfc = NETFILTER_GET_CLASS(obj); |
238 | |
239 | if (nfc->cleanup) { |
240 | nfc->cleanup(nf); |
241 | } |
242 | |
243 | if (nf->netdev && !QTAILQ_EMPTY(&nf->netdev->filters) && |
244 | QTAILQ_IN_USE(nf, next)) { |
245 | QTAILQ_REMOVE(&nf->netdev->filters, nf, next); |
246 | } |
247 | g_free(nf->netdev_id); |
248 | } |
249 | |
250 | static void default_handle_event(NetFilterState *nf, int event, Error **errp) |
251 | { |
252 | switch (event) { |
253 | case COLO_EVENT_CHECKPOINT: |
254 | break; |
255 | case COLO_EVENT_FAILOVER: |
256 | object_property_set_str(OBJECT(nf), "off" , "status" , errp); |
257 | break; |
258 | default: |
259 | break; |
260 | } |
261 | } |
262 | |
263 | static void netfilter_class_init(ObjectClass *oc, void *data) |
264 | { |
265 | UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); |
266 | NetFilterClass *nfc = NETFILTER_CLASS(oc); |
267 | |
268 | ucc->complete = netfilter_complete; |
269 | nfc->handle_event = default_handle_event; |
270 | } |
271 | |
272 | static const TypeInfo netfilter_info = { |
273 | .name = TYPE_NETFILTER, |
274 | .parent = TYPE_OBJECT, |
275 | .abstract = true, |
276 | .class_size = sizeof(NetFilterClass), |
277 | .class_init = netfilter_class_init, |
278 | .instance_size = sizeof(NetFilterState), |
279 | .instance_init = netfilter_init, |
280 | .instance_finalize = netfilter_finalize, |
281 | .interfaces = (InterfaceInfo[]) { |
282 | { TYPE_USER_CREATABLE }, |
283 | { } |
284 | } |
285 | }; |
286 | |
287 | static void register_types(void) |
288 | { |
289 | type_register_static(&netfilter_info); |
290 | } |
291 | |
292 | type_init(register_types); |
293 | |