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