text: /*
   * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
   * Copyright (c) 2016 FUJITSU LIMITED
   * Copyright (c) 2016 Intel Corporation
   *
   * Author: Zhang Chen <zhangchen.fnst@cn.fujitsu.com>
   *
   * This work is licensed under the terms of the GNU GPL, version 2 or
   * later.  See the COPYING file in the top-level directory.
   */
  
  #include "qemu/osdep.h"
  #include "net/filter.h"
  #include "net/net.h"
  #include "qemu-common.h"
  #include "qapi/error.h"
  #include "qapi/qmp/qerror.h"
  #include "qapi-visit.h"
  #include "qom/object.h"
  #include "qemu/main-loop.h"
  #include "qemu/error-report.h"
  #include "trace.h"
  #include "sysemu/char.h"
  #include "qemu/iov.h"
  #include "qemu/sockets.h"
  
  #define FILTER_MIRROR(obj) \
      OBJECT_CHECK(MirrorState, (obj), TYPE_FILTER_MIRROR)
  
  #define FILTER_REDIRECTOR(obj) \
      OBJECT_CHECK(MirrorState, (obj), TYPE_FILTER_REDIRECTOR)
  
  #define TYPE_FILTER_MIRROR "filter-mirror"
  #define TYPE_FILTER_REDIRECTOR "filter-redirector"
  #define REDIRECTOR_MAX_LEN NET_BUFSIZE
  
  typedef struct MirrorState {
      NetFilterState parent_obj;
      char *indev;
      char *outdev;
      CharBackend chr_in;
      CharBackend chr_out;
      SocketReadState rs;
  } MirrorState;
  
  static int filter_mirror_send(CharBackend *chr_out,
                                const struct iovec *iov,
                                int iovcnt)
  {
      int ret = 0;
      ssize_t size = 0;
      uint32_t len =  0;
      char *buf;
  
      size = iov_size(iov, iovcnt);
      if (!size) {
          return 0;
      }
  
      len = htonl(size);
      ret = qemu_chr_fe_write_all(chr_out, (uint8_t *)&len, sizeof(len));
      if (ret != sizeof(len)) {
          goto err;
      }
  
      buf = g_malloc(size);
      iov_to_buf(iov, iovcnt, 0, buf, size);
      ret = qemu_chr_fe_write_all(chr_out, (uint8_t *)buf, size);
      g_free(buf);
      if (ret != size) {
          goto err;
      }
  
      return 0;
  
  err:
      return ret < 0 ? ret : -EIO;
  }
  
  static void
  redirector_to_filter(NetFilterState *nf, const uint8_t *buf, int len)
  {
      struct iovec iov = {
          .iov_base = (void *)buf,
          .iov_len = len,
      };
  
      if (nf->direction == NET_FILTER_DIRECTION_ALL ||
          nf->direction == NET_FILTER_DIRECTION_TX) {
          qemu_netfilter_pass_to_next(nf->netdev, 0, &iov, 1, nf);
      }
  
      if (nf->direction == NET_FILTER_DIRECTION_ALL ||
          nf->direction == NET_FILTER_DIRECTION_RX) {
          qemu_netfilter_pass_to_next(nf->netdev->peer, 0, &iov, 1, nf);
       }
  }
  
  static int redirector_chr_can_read(void *opaque)
  {
      return REDIRECTOR_MAX_LEN;
  }
  
  static void redirector_chr_read(void *opaque, const uint8_t *buf, int size)
  {
      NetFilterState *nf = opaque;
      MirrorState *s = FILTER_REDIRECTOR(nf);
      int ret;
  
      ret = net_fill_rstate(&s->rs, buf, size);
  
      if (ret == -1) {
          qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL,
                                   NULL, NULL, true);
      }
  }
  
  static void redirector_chr_event(void *opaque, int event)
  {
      NetFilterState *nf = opaque;
      MirrorState *s = FILTER_REDIRECTOR(nf);
  
      switch (event) {
      case CHR_EVENT_CLOSED:
          qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL,
                                   NULL, NULL, true);
          break;
      default:
          break;
      }
  }
  
  static ssize_t filter_mirror_receive_iov(NetFilterState *nf,
                                           NetClientState *sender,
                                           unsigned flags,
                                           const struct iovec *iov,
                                           int iovcnt,
                                           NetPacketSent *sent_cb)
  {
      MirrorState *s = FILTER_MIRROR(nf);
      int ret;
  
      ret = filter_mirror_send(&s->chr_out, iov, iovcnt);
      if (ret) {
          error_report("filter_mirror_send failed(%s)", strerror(-ret));
      }
  
      /*
       * we don't hope this error interrupt the normal
       * path of net packet, so we always return zero.
       */
      return 0;
  }
  
  static ssize_t filter_redirector_receive_iov(NetFilterState *nf,
                                               NetClientState *sender,
                                               unsigned flags,
                                               const struct iovec *iov,
                                               int iovcnt,
                                               NetPacketSent *sent_cb)
  {
      MirrorState *s = FILTER_REDIRECTOR(nf);
      int ret;
  
      if (qemu_chr_fe_get_driver(&s->chr_out)) {
          ret = filter_mirror_send(&s->chr_out, iov, iovcnt);
          if (ret) {
              error_report("filter_mirror_send failed(%s)", strerror(-ret));
          }
          return iov_size(iov, iovcnt);
      } else {
          return 0;
      }
  }
  
  static void filter_mirror_cleanup(NetFilterState *nf)
  {
      MirrorState *s = FILTER_MIRROR(nf);
  
      qemu_chr_fe_deinit(&s->chr_out);
  }
  
  static void filter_redirector_cleanup(NetFilterState *nf)
  {
      MirrorState *s = FILTER_REDIRECTOR(nf);
  
      qemu_chr_fe_deinit(&s->chr_in);
      qemu_chr_fe_deinit(&s->chr_out);
  }
  
  static void filter_mirror_setup(NetFilterState *nf, Error **errp)
  {
      MirrorState *s = FILTER_MIRROR(nf);
      CharDriverState *chr;
  
      if (!s->outdev) {
          error_setg(errp, "filter mirror needs 'outdev' "
                     "property set");
          return;
      }
  
      chr = qemu_chr_find(s->outdev);
      if (chr == NULL) {
          error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                    "Device '%s' not found", s->outdev);
          return;
      }
  
      qemu_chr_fe_init(&s->chr_out, chr, errp);
  }
  
  static void redirector_rs_finalize(SocketReadState *rs)
  {
      MirrorState *s = container_of(rs, MirrorState, rs);
      NetFilterState *nf = NETFILTER(s);
  
      redirector_to_filter(nf, rs->buf, rs->packet_len);
  }
  
  static void filter_redirector_setup(NetFilterState *nf, Error **errp)
  {
      MirrorState *s = FILTER_REDIRECTOR(nf);
      CharDriverState *chr;
  
      if (!s->indev && !s->outdev) {
          error_setg(errp, "filter redirector needs 'indev' or "
                     "'outdev' at least one property set");
          return;
      } else if (s->indev && s->outdev) {
          if (!strcmp(s->indev, s->outdev)) {
              error_setg(errp, "'indev' and 'outdev' could not be same "
                         "for filter redirector");
              return;
          }
      }
  
      net_socket_rs_init(&s->rs, redirector_rs_finalize);
  
      if (s->indev) {
          chr = qemu_chr_find(s->indev);
          if (chr == NULL) {
              error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                        "IN Device '%s' not found", s->indev);
              return;
          }
  
          if (!qemu_chr_fe_init(&s->chr_in, chr, errp)) {
              return;
          }
  
          qemu_chr_fe_set_handlers(&s->chr_in, redirector_chr_can_read,
                                   redirector_chr_read, redirector_chr_event,
                                   nf, NULL, true);
      }
  
      if (s->outdev) {
          chr = qemu_chr_find(s->outdev);
          if (chr == NULL) {
              error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                        "OUT Device '%s' not found", s->outdev);
              return;
          }
          if (!qemu_chr_fe_init(&s->chr_out, chr, errp)) {
              return;
          }
      }
  }
  
  static void filter_mirror_class_init(ObjectClass *oc, void *data)
  {
      NetFilterClass *nfc = NETFILTER_CLASS(oc);
  
      nfc->setup = filter_mirror_setup;
      nfc->cleanup = filter_mirror_cleanup;
      nfc->receive_iov = filter_mirror_receive_iov;
  }
  
  static void filter_redirector_class_init(ObjectClass *oc, void *data)
  {
      NetFilterClass *nfc = NETFILTER_CLASS(oc);
  
      nfc->setup = filter_redirector_setup;
      nfc->cleanup = filter_redirector_cleanup;
      nfc->receive_iov = filter_redirector_receive_iov;
  }
  
  static char *filter_redirector_get_indev(Object *obj, Error **errp)
  {
      MirrorState *s = FILTER_REDIRECTOR(obj);
  
      return g_strdup(s->indev);
  }
  
  static void
  filter_redirector_set_indev(Object *obj, const char *value, Error **errp)
  {
      MirrorState *s = FILTER_REDIRECTOR(obj);
  
      g_free(s->indev);
      s->indev = g_strdup(value);
  }
  
  static char *filter_mirror_get_outdev(Object *obj, Error **errp)
  {
      MirrorState *s = FILTER_MIRROR(obj);
  
      return g_strdup(s->outdev);
  }
  
  static void
  filter_mirror_set_outdev(Object *obj, const char *value, Error **errp)
  {
      MirrorState *s = FILTER_MIRROR(obj);
  
      g_free(s->outdev);
      s->outdev = g_strdup(value);
      if (!s->outdev) {
          error_setg(errp, "filter mirror needs 'outdev' "
                     "property set");
          return;
      }
  }
  
  static char *filter_redirector_get_outdev(Object *obj, Error **errp)
  {
      MirrorState *s = FILTER_REDIRECTOR(obj);
  
      return g_strdup(s->outdev);
  }
  
  static void
  filter_redirector_set_outdev(Object *obj, const char *value, Error **errp)
  {
      MirrorState *s = FILTER_REDIRECTOR(obj);
  
      g_free(s->outdev);
      s->outdev = g_strdup(value);
  }
  
  static void filter_mirror_init(Object *obj)
  {
      object_property_add_str(obj, "outdev", filter_mirror_get_outdev,
                              filter_mirror_set_outdev, NULL);
  }
  
  static void filter_redirector_init(Object *obj)
  {
      object_property_add_str(obj, "indev", filter_redirector_get_indev,
                              filter_redirector_set_indev, NULL);
      object_property_add_str(obj, "outdev", filter_redirector_get_outdev,
                              filter_redirector_set_outdev, NULL);
  }
  
  static void filter_mirror_fini(Object *obj)
  {
      MirrorState *s = FILTER_MIRROR(obj);
  
      g_free(s->outdev);
  }
  
  static void filter_redirector_fini(Object *obj)
  {
      MirrorState *s = FILTER_REDIRECTOR(obj);
  
      g_free(s->indev);
      g_free(s->outdev);
  }
  
  static const TypeInfo filter_redirector_info = {
      .name = TYPE_FILTER_REDIRECTOR,
      .parent = TYPE_NETFILTER,
      .class_init = filter_redirector_class_init,
      .instance_init = filter_redirector_init,
      .instance_finalize = filter_redirector_fini,
      .instance_size = sizeof(MirrorState),
  };
  
  static const TypeInfo filter_mirror_info = {
      .name = TYPE_FILTER_MIRROR,
      .parent = TYPE_NETFILTER,
      .class_init = filter_mirror_class_init,
      .instance_init = filter_mirror_init,
      .instance_finalize = filter_mirror_fini,
      .instance_size = sizeof(MirrorState),
  };
  
  static void register_types(void)
  {
      type_register_static(&filter_mirror_info);
      type_register_static(&filter_redirector_info);
  }
  
  type_init(register_types);
  