xref: /openbmc/qemu/net/filter.c (revision 54aa3de72ea2aaa2e903e7e879a4f3dda515a00e)
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  
217  static 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  
306  static 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  
323  static 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  
336  static 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  
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