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 char *netfilter_get_position(Object *obj, Error **errp) 175 { 176 NetFilterState *nf = NETFILTER(obj); 177 178 return g_strdup(nf->position); 179 } 180 181 static void netfilter_set_position(Object *obj, const char *str, Error **errp) 182 { 183 NetFilterState *nf = NETFILTER(obj); 184 185 nf->position = g_strdup(str); 186 } 187 188 static char *netfilter_get_insert(Object *obj, Error **errp) 189 { 190 NetFilterState *nf = NETFILTER(obj); 191 192 return nf->insert_before_flag ? g_strdup("before") : g_strdup("behind"); 193 } 194 195 static void netfilter_set_insert(Object *obj, const char *str, Error **errp) 196 { 197 NetFilterState *nf = NETFILTER(obj); 198 199 if (strcmp(str, "before") && strcmp(str, "behind")) { 200 error_setg(errp, "Invalid value for netfilter insert, " 201 "should be 'before' or 'behind'"); 202 return; 203 } 204 205 nf->insert_before_flag = !strcmp(str, "before"); 206 } 207 208 static void netfilter_init(Object *obj) 209 { 210 NetFilterState *nf = NETFILTER(obj); 211 212 nf->on = true; 213 nf->insert_before_flag = false; 214 nf->position = g_strdup("tail"); 215 216 object_property_add_str(obj, "netdev", 217 netfilter_get_netdev_id, netfilter_set_netdev_id); 218 object_property_add_enum(obj, "queue", "NetFilterDirection", 219 &NetFilterDirection_lookup, 220 netfilter_get_direction, netfilter_set_direction); 221 object_property_add_str(obj, "status", 222 netfilter_get_status, netfilter_set_status); 223 object_property_add_str(obj, "position", 224 netfilter_get_position, netfilter_set_position); 225 object_property_add_str(obj, "insert", 226 netfilter_get_insert, netfilter_set_insert); 227 } 228 229 static void netfilter_complete(UserCreatable *uc, Error **errp) 230 { 231 NetFilterState *nf = NETFILTER(uc); 232 NetFilterState *position = NULL; 233 NetClientState *ncs[MAX_QUEUE_NUM]; 234 NetFilterClass *nfc = NETFILTER_GET_CLASS(uc); 235 int queues; 236 Error *local_err = NULL; 237 238 if (!nf->netdev_id) { 239 error_setg(errp, "Parameter 'netdev' is required"); 240 return; 241 } 242 243 queues = qemu_find_net_clients_except(nf->netdev_id, ncs, 244 NET_CLIENT_DRIVER_NIC, 245 MAX_QUEUE_NUM); 246 if (queues < 1) { 247 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "netdev", 248 "a network backend id"); 249 return; 250 } else if (queues > 1) { 251 error_setg(errp, "multiqueue is not supported"); 252 return; 253 } 254 255 if (get_vhost_net(ncs[0])) { 256 error_setg(errp, "Vhost is not supported"); 257 return; 258 } 259 260 if (strcmp(nf->position, "head") && strcmp(nf->position, "tail")) { 261 Object *container; 262 Object *obj; 263 char *position_id; 264 265 if (!g_str_has_prefix(nf->position, "id=")) { 266 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "position", 267 "'head', 'tail' or 'id=<id>'"); 268 return; 269 } 270 271 /* get the id from the string */ 272 position_id = g_strndup(nf->position + 3, strlen(nf->position) - 3); 273 274 /* Search for the position to insert before/behind */ 275 container = object_get_objects_root(); 276 obj = object_resolve_path_component(container, position_id); 277 if (!obj) { 278 error_setg(errp, "filter '%s' not found", position_id); 279 g_free(position_id); 280 return; 281 } 282 283 position = NETFILTER(obj); 284 285 if (position->netdev != ncs[0]) { 286 error_setg(errp, "filter '%s' belongs to a different netdev", 287 position_id); 288 g_free(position_id); 289 return; 290 } 291 292 g_free(position_id); 293 } 294 295 nf->netdev = ncs[0]; 296 297 if (nfc->setup) { 298 nfc->setup(nf, &local_err); 299 if (local_err) { 300 error_propagate(errp, local_err); 301 return; 302 } 303 } 304 305 if (position) { 306 if (nf->insert_before_flag) { 307 QTAILQ_INSERT_BEFORE(position, nf, next); 308 } else { 309 QTAILQ_INSERT_AFTER(&nf->netdev->filters, position, nf, next); 310 } 311 } else if (!strcmp(nf->position, "head")) { 312 QTAILQ_INSERT_HEAD(&nf->netdev->filters, nf, next); 313 } else if (!strcmp(nf->position, "tail")) { 314 QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next); 315 } 316 } 317 318 static void netfilter_finalize(Object *obj) 319 { 320 NetFilterState *nf = NETFILTER(obj); 321 NetFilterClass *nfc = NETFILTER_GET_CLASS(obj); 322 323 if (nfc->cleanup) { 324 nfc->cleanup(nf); 325 } 326 327 if (nf->netdev && !QTAILQ_EMPTY(&nf->netdev->filters) && 328 QTAILQ_IN_USE(nf, next)) { 329 QTAILQ_REMOVE(&nf->netdev->filters, nf, next); 330 } 331 g_free(nf->netdev_id); 332 g_free(nf->position); 333 } 334 335 static void default_handle_event(NetFilterState *nf, int event, Error **errp) 336 { 337 switch (event) { 338 case COLO_EVENT_CHECKPOINT: 339 break; 340 case COLO_EVENT_FAILOVER: 341 object_property_set_str(OBJECT(nf), "off", "status", errp); 342 break; 343 default: 344 break; 345 } 346 } 347 348 static void netfilter_class_init(ObjectClass *oc, void *data) 349 { 350 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); 351 NetFilterClass *nfc = NETFILTER_CLASS(oc); 352 353 ucc->complete = netfilter_complete; 354 nfc->handle_event = default_handle_event; 355 } 356 357 static const TypeInfo netfilter_info = { 358 .name = TYPE_NETFILTER, 359 .parent = TYPE_OBJECT, 360 .abstract = true, 361 .class_size = sizeof(NetFilterClass), 362 .class_init = netfilter_class_init, 363 .instance_size = sizeof(NetFilterState), 364 .instance_init = netfilter_init, 365 .instance_finalize = netfilter_finalize, 366 .interfaces = (InterfaceInfo[]) { 367 { TYPE_USER_CREATABLE }, 368 { } 369 } 370 }; 371 372 static void register_types(void) 373 { 374 type_register_static(&netfilter_info); 375 } 376 377 type_init(register_types); 378