]>
Commit | Line | Data |
---|---|---|
fdccce45 YH |
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" | |
7ef7bc85 | 17 | #include "qemu/iov.h" |
a4960f52 | 18 | #include "qapi/string-output-visitor.h" |
fdccce45 | 19 | |
e64c770d YH |
20 | ssize_t qemu_netfilter_receive(NetFilterState *nf, |
21 | NetFilterDirection direction, | |
22 | NetClientState *sender, | |
23 | unsigned flags, | |
24 | const struct iovec *iov, | |
25 | int iovcnt, | |
26 | NetPacketSent *sent_cb) | |
27 | { | |
28 | if (nf->direction == direction || | |
29 | nf->direction == NET_FILTER_DIRECTION_ALL) { | |
30 | return NETFILTER_GET_CLASS(OBJECT(nf))->receive_iov( | |
31 | nf, sender, flags, iov, iovcnt, sent_cb); | |
32 | } | |
33 | ||
34 | return 0; | |
35 | } | |
36 | ||
7ef7bc85 YH |
37 | ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, |
38 | unsigned flags, | |
39 | const struct iovec *iov, | |
40 | int iovcnt, | |
41 | void *opaque) | |
42 | { | |
43 | int ret = 0; | |
44 | int direction; | |
45 | NetFilterState *nf = opaque; | |
46 | NetFilterState *next = QTAILQ_NEXT(nf, next); | |
47 | ||
48 | if (!sender || !sender->peer) { | |
49 | /* no receiver, or sender been deleted, no need to pass it further */ | |
50 | goto out; | |
51 | } | |
52 | ||
53 | if (nf->direction == NET_FILTER_DIRECTION_ALL) { | |
54 | if (sender == nf->netdev) { | |
55 | /* This packet is sent by netdev itself */ | |
56 | direction = NET_FILTER_DIRECTION_TX; | |
57 | } else { | |
58 | direction = NET_FILTER_DIRECTION_RX; | |
59 | } | |
60 | } else { | |
61 | direction = nf->direction; | |
62 | } | |
63 | ||
64 | while (next) { | |
65 | /* | |
66 | * if qemu_netfilter_pass_to_next been called, means that | |
67 | * the packet has been hold by filter and has already retured size | |
68 | * to the sender, so sent_cb shouldn't be called later, just | |
69 | * pass NULL to next. | |
70 | */ | |
71 | ret = qemu_netfilter_receive(next, direction, sender, flags, iov, | |
72 | iovcnt, NULL); | |
73 | if (ret) { | |
74 | return ret; | |
75 | } | |
76 | next = QTAILQ_NEXT(next, next); | |
77 | } | |
78 | ||
79 | /* | |
80 | * We have gone through all filters, pass it to receiver. | |
81 | * Do the valid check again incase sender or receiver been | |
82 | * deleted while we go through filters. | |
83 | */ | |
84 | if (sender && sender->peer) { | |
85 | qemu_net_queue_send_iov(sender->peer->incoming_queue, | |
86 | sender, flags, iov, iovcnt, NULL); | |
87 | } | |
88 | ||
89 | out: | |
90 | /* no receiver, or sender been deleted */ | |
91 | return iov_size(iov, iovcnt); | |
92 | } | |
93 | ||
fdccce45 YH |
94 | static char *netfilter_get_netdev_id(Object *obj, Error **errp) |
95 | { | |
96 | NetFilterState *nf = NETFILTER(obj); | |
97 | ||
98 | return g_strdup(nf->netdev_id); | |
99 | } | |
100 | ||
101 | static void netfilter_set_netdev_id(Object *obj, const char *str, Error **errp) | |
102 | { | |
103 | NetFilterState *nf = NETFILTER(obj); | |
104 | ||
105 | nf->netdev_id = g_strdup(str); | |
106 | } | |
107 | ||
108 | static int netfilter_get_direction(Object *obj, Error **errp G_GNUC_UNUSED) | |
109 | { | |
110 | NetFilterState *nf = NETFILTER(obj); | |
111 | return nf->direction; | |
112 | } | |
113 | ||
114 | static void netfilter_set_direction(Object *obj, int direction, Error **errp) | |
115 | { | |
116 | NetFilterState *nf = NETFILTER(obj); | |
117 | nf->direction = direction; | |
118 | } | |
119 | ||
120 | static void netfilter_init(Object *obj) | |
121 | { | |
122 | object_property_add_str(obj, "netdev", | |
123 | netfilter_get_netdev_id, netfilter_set_netdev_id, | |
124 | NULL); | |
125 | object_property_add_enum(obj, "queue", "NetFilterDirection", | |
126 | NetFilterDirection_lookup, | |
127 | netfilter_get_direction, netfilter_set_direction, | |
128 | NULL); | |
129 | } | |
130 | ||
131 | static void netfilter_complete(UserCreatable *uc, Error **errp) | |
132 | { | |
133 | NetFilterState *nf = NETFILTER(uc); | |
134 | NetClientState *ncs[MAX_QUEUE_NUM]; | |
135 | NetFilterClass *nfc = NETFILTER_GET_CLASS(uc); | |
136 | int queues; | |
137 | Error *local_err = NULL; | |
a4960f52 YH |
138 | char *str, *info; |
139 | ObjectProperty *prop; | |
7746abd8 | 140 | ObjectPropertyIterator iter; |
a4960f52 | 141 | StringOutputVisitor *ov; |
fdccce45 YH |
142 | |
143 | if (!nf->netdev_id) { | |
144 | error_setg(errp, "Parameter 'netdev' is required"); | |
145 | return; | |
146 | } | |
147 | ||
148 | queues = qemu_find_net_clients_except(nf->netdev_id, ncs, | |
149 | NET_CLIENT_OPTIONS_KIND_NIC, | |
150 | MAX_QUEUE_NUM); | |
151 | if (queues < 1) { | |
152 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "netdev", | |
153 | "a network backend id"); | |
154 | return; | |
155 | } else if (queues > 1) { | |
156 | error_setg(errp, "multiqueue is not supported"); | |
157 | return; | |
158 | } | |
159 | ||
160 | if (get_vhost_net(ncs[0])) { | |
161 | error_setg(errp, "Vhost is not supported"); | |
162 | return; | |
163 | } | |
164 | ||
165 | nf->netdev = ncs[0]; | |
166 | ||
167 | if (nfc->setup) { | |
168 | nfc->setup(nf, &local_err); | |
169 | if (local_err) { | |
170 | error_propagate(errp, local_err); | |
171 | return; | |
172 | } | |
173 | } | |
174 | QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next); | |
a4960f52 YH |
175 | |
176 | /* generate info str */ | |
7746abd8 DB |
177 | object_property_iter_init(&iter, OBJECT(nf)); |
178 | while ((prop = object_property_iter_next(&iter))) { | |
a4960f52 YH |
179 | if (!strcmp(prop->name, "type")) { |
180 | continue; | |
181 | } | |
182 | ov = string_output_visitor_new(false); | |
183 | object_property_get(OBJECT(nf), string_output_get_visitor(ov), | |
184 | prop->name, errp); | |
185 | str = string_output_get_string(ov); | |
186 | string_output_visitor_cleanup(ov); | |
187 | info = g_strdup_printf(",%s=%s", prop->name, str); | |
188 | g_strlcat(nf->info_str, info, sizeof(nf->info_str)); | |
189 | g_free(str); | |
190 | g_free(info); | |
191 | } | |
fdccce45 YH |
192 | } |
193 | ||
194 | static void netfilter_finalize(Object *obj) | |
195 | { | |
196 | NetFilterState *nf = NETFILTER(obj); | |
197 | NetFilterClass *nfc = NETFILTER_GET_CLASS(obj); | |
198 | ||
199 | if (nfc->cleanup) { | |
200 | nfc->cleanup(nf); | |
201 | } | |
202 | ||
203 | if (nf->netdev && !QTAILQ_EMPTY(&nf->netdev->filters)) { | |
204 | QTAILQ_REMOVE(&nf->netdev->filters, nf, next); | |
205 | } | |
671f66f8 | 206 | g_free(nf->netdev_id); |
fdccce45 YH |
207 | } |
208 | ||
209 | static void netfilter_class_init(ObjectClass *oc, void *data) | |
210 | { | |
211 | UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); | |
212 | ||
213 | ucc->complete = netfilter_complete; | |
214 | } | |
215 | ||
216 | static const TypeInfo netfilter_info = { | |
217 | .name = TYPE_NETFILTER, | |
218 | .parent = TYPE_OBJECT, | |
219 | .abstract = true, | |
220 | .class_size = sizeof(NetFilterClass), | |
221 | .class_init = netfilter_class_init, | |
222 | .instance_size = sizeof(NetFilterState), | |
223 | .instance_init = netfilter_init, | |
224 | .instance_finalize = netfilter_finalize, | |
225 | .interfaces = (InterfaceInfo[]) { | |
226 | { TYPE_USER_CREATABLE }, | |
227 | { } | |
228 | } | |
229 | }; | |
230 | ||
231 | static void register_types(void) | |
232 | { | |
233 | type_register_static(&netfilter_info); | |
234 | } | |
235 | ||
236 | type_init(register_types); |