filter.c (10798B)
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 23static inline bool qemu_can_skip_netfilter(NetFilterState *nf) 24{ 25 return !nf->on; 26} 27 28ssize_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 48static 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 64ssize_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 117out: 118 /* no receiver, or sender been deleted */ 119 return iov_size(iov, iovcnt); 120} 121 122static 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 129static 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 136static int netfilter_get_direction(Object *obj, Error **errp G_GNUC_UNUSED) 137{ 138 NetFilterState *nf = NETFILTER(obj); 139 return nf->direction; 140} 141 142static void netfilter_set_direction(Object *obj, int direction, Error **errp) 143{ 144 NetFilterState *nf = NETFILTER(obj); 145 nf->direction = direction; 146} 147 148static 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 155static 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 174static char *netfilter_get_position(Object *obj, Error **errp) 175{ 176 NetFilterState *nf = NETFILTER(obj); 177 178 return g_strdup(nf->position); 179} 180 181static 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 188static 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 195static 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 208static 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 217static void netfilter_complete(UserCreatable *uc, Error **errp) 218{ 219 NetFilterState *nf = NETFILTER(uc); 220 NetFilterState *position = NULL; 221 NetClientState *ncs[MAX_QUEUE_NUM]; 222 NetFilterClass *nfc = NETFILTER_GET_CLASS(uc); 223 int queues; 224 Error *local_err = NULL; 225 226 if (!nf->netdev_id) { 227 error_setg(errp, "Parameter 'netdev' is required"); 228 return; 229 } 230 231 queues = qemu_find_net_clients_except(nf->netdev_id, ncs, 232 NET_CLIENT_DRIVER_NIC, 233 MAX_QUEUE_NUM); 234 if (queues < 1) { 235 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "netdev", 236 "a network backend id"); 237 return; 238 } else if (queues > 1) { 239 error_setg(errp, "multiqueue is not supported"); 240 return; 241 } 242 243 if (get_vhost_net(ncs[0])) { 244 error_setg(errp, "Vhost is not supported"); 245 return; 246 } 247 248 if (strcmp(nf->position, "head") && strcmp(nf->position, "tail")) { 249 Object *container; 250 Object *obj; 251 char *position_id; 252 253 if (!g_str_has_prefix(nf->position, "id=")) { 254 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "position", 255 "'head', 'tail' or 'id=<id>'"); 256 return; 257 } 258 259 /* get the id from the string */ 260 position_id = g_strndup(nf->position + 3, strlen(nf->position) - 3); 261 262 /* Search for the position to insert before/behind */ 263 container = object_get_objects_root(); 264 obj = object_resolve_path_component(container, position_id); 265 if (!obj) { 266 error_setg(errp, "filter '%s' not found", position_id); 267 g_free(position_id); 268 return; 269 } 270 271 position = NETFILTER(obj); 272 273 if (position->netdev != ncs[0]) { 274 error_setg(errp, "filter '%s' belongs to a different netdev", 275 position_id); 276 g_free(position_id); 277 return; 278 } 279 280 g_free(position_id); 281 } 282 283 nf->netdev = ncs[0]; 284 285 if (nfc->setup) { 286 nfc->setup(nf, &local_err); 287 if (local_err) { 288 error_propagate(errp, local_err); 289 return; 290 } 291 } 292 293 if (position) { 294 if (nf->insert_before_flag) { 295 QTAILQ_INSERT_BEFORE(position, nf, next); 296 } else { 297 QTAILQ_INSERT_AFTER(&nf->netdev->filters, position, nf, next); 298 } 299 } else if (!strcmp(nf->position, "head")) { 300 QTAILQ_INSERT_HEAD(&nf->netdev->filters, nf, next); 301 } else if (!strcmp(nf->position, "tail")) { 302 QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next); 303 } 304} 305 306static void netfilter_finalize(Object *obj) 307{ 308 NetFilterState *nf = NETFILTER(obj); 309 NetFilterClass *nfc = NETFILTER_GET_CLASS(obj); 310 311 if (nfc->cleanup) { 312 nfc->cleanup(nf); 313 } 314 315 if (nf->netdev && !QTAILQ_EMPTY(&nf->netdev->filters) && 316 QTAILQ_IN_USE(nf, next)) { 317 QTAILQ_REMOVE(&nf->netdev->filters, nf, next); 318 } 319 g_free(nf->netdev_id); 320 g_free(nf->position); 321} 322 323static void default_handle_event(NetFilterState *nf, int event, Error **errp) 324{ 325 switch (event) { 326 case COLO_EVENT_CHECKPOINT: 327 break; 328 case COLO_EVENT_FAILOVER: 329 object_property_set_str(OBJECT(nf), "status", "off", errp); 330 break; 331 default: 332 break; 333 } 334} 335 336static void netfilter_class_init(ObjectClass *oc, void *data) 337{ 338 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); 339 NetFilterClass *nfc = NETFILTER_CLASS(oc); 340 341 object_class_property_add_str(oc, "netdev", 342 netfilter_get_netdev_id, netfilter_set_netdev_id); 343 object_class_property_add_enum(oc, "queue", "NetFilterDirection", 344 &NetFilterDirection_lookup, 345 netfilter_get_direction, netfilter_set_direction); 346 object_class_property_add_str(oc, "status", 347 netfilter_get_status, netfilter_set_status); 348 object_class_property_add_str(oc, "position", 349 netfilter_get_position, netfilter_set_position); 350 object_class_property_add_str(oc, "insert", 351 netfilter_get_insert, netfilter_set_insert); 352 353 ucc->complete = netfilter_complete; 354 nfc->handle_event = default_handle_event; 355} 356 357static 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 372static void register_types(void) 373{ 374 type_register_static(&netfilter_info); 375} 376 377type_init(register_types);