static NetClientInfo net_tap_info = {

    .type = NET_CLIENT_OPTIONS_KIND_TAP,

    .size = sizeof(TAPState),

    .receive = tap_receive,

    .receive_raw = tap_receive_raw,

    .receive_iov = tap_receive_iov,

    .poll = tap_poll,

    .cleanup = tap_cleanup,

};

 

finally all functions for network will call receive_raw or receive. as qemu_deliver_packet function.

ssize_t qemu_deliver_packet(NetClientState *sender,

                            unsigned flags,

                            const uint8_t *data,

                            size_t size,

                            void *opaque)

{

    NetClientState *nc = opaque; // this is a peer's state.

    ssize_t ret;

 

    if (nc->link_down) {

        return size;

    }

 

    if (nc->receive_disabled) {

        return 0;

    }

 

    if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) {

        ret = nc->info->receive_raw(nc, data, size);

    } else {

        ret = nc->info->receive(nc, data, size);

    }

 

    if (ret == 0) {

        nc->receive_disabled = 1;

    };

 

    return ret;

}

 

both tap_receive for receive and tap_receive_raw for receive_raw will call tap_write_packet function.

 

static ssize_t tap_receive_raw(NetClientState *nc, const uint8_t *buf, size_t size)

{

    TAPState *s = DO_UPCAST(TAPState, nc, nc);

    struct iovec iov[2];

    int iovcnt = 0;

    struct virtio_net_hdr_mrg_rxbuf hdr = { };

 

    if (s->host_vnet_hdr_len) {

        iov[iovcnt].iov_base = &hdr;

        iov[iovcnt].iov_len  = s->host_vnet_hdr_len;

        iovcnt++;

    }

 

    iov[iovcnt].iov_base = (char *)buf;

    iov[iovcnt].iov_len  = size;

    iovcnt++;

 

    return tap_write_packet(s, iov, iovcnt);

}

 

static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size)

{

    TAPState *s = DO_UPCAST(TAPState, nc, nc);

    struct iovec iov[1];

 

    if (s->host_vnet_hdr_len && !s->using_vnet_hdr) {

        return tap_receive_raw(nc, buf, size);

    }

 

    iov[0].iov_base = (char *)buf;

    iov[0].iov_len  = size;

 

    return tap_write_packet(s, iov, 1);

}

static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt)

{

    ssize_t len;

 

    do {

        len = writev(s->fd, iov, iovcnt);

    } while (len == -1 && errno == EINTR);

 

    if (len == -1 && errno == EAGAIN) {

        tap_write_poll(s, true);

        return 0;

    }

 

    return len;

}

 

static ssize_t readv_writev(int fd, const struct iovec *iov, int iov_cnt, bool do_write)

{

    unsigned i = 0;

    ssize_t ret = 0;

    while (i < iov_cnt) {

        ssize_t r = do_write

            ? write(fd, iov[i].iov_base, iov[i].iov_len)

            : read(fd, iov[i].iov_base, iov[i].iov_len);

        if (r > 0) {

            ret += r;

        } else if (!r) {

            break;

        } else if (errno == EINTR) {

            continue;

        } else {

            /* else it is some "other" error,

             * only return if there was no data processed. */

            if (ret == 0) {

                ret = -1;

            }

            break;

        }

        i++;

    }

    return ret;

}

 

finally we found file descriptor network bridge use!!.

Posted by MeatNBrew
l

here is a function for net_client_init_fun structure for function pointers.

static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(

    const NetClientOptions *opts,

    const char *name,

    NetClientState *peer) = {

        [NET_CLIENT_OPTIONS_KIND_NIC]       = net_init_nic,

#ifdef CONFIG_SLIRP

        [NET_CLIENT_OPTIONS_KIND_USER]      = net_init_slirp,

#endif

        [NET_CLIENT_OPTIONS_KIND_TAP]       = net_init_tap,

        [NET_CLIENT_OPTIONS_KIND_SOCKET]    = net_init_socket,

#ifdef CONFIG_VDE

        [NET_CLIENT_OPTIONS_KIND_VDE]       = net_init_vde,

#endif

        [NET_CLIENT_OPTIONS_KIND_DUMP]      = net_init_dump,

#ifdef CONFIG_NET_BRIDGE

        [NET_CLIENT_OPTIONS_KIND_BRIDGE]    = net_init_bridge,

#endif

        [NET_CLIENT_OPTIONS_KIND_HUBPORT]   = net_init_hubport,

};

 

im going to follow a function, net_init_bridge one of them above.

 

int net_init_bridge(const NetClientOptions *opts, const char *name,

                    NetClientState *peer)

{

    const NetdevBridgeOptions *bridge;

    const char *helper, *br;

 

    TAPState *s;

    int fd, vnet_hdr;

 

    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE);

    bridge = opts->bridge;

 

    helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER;

    br     = bridge->has_br     ? bridge->br     : DEFAULT_BRIDGE_INTERFACE;

 

    fd = net_bridge_run_helper(helper, br);

    if (fd == -1) {

        return -1;

    }

 

    fcntl(fd, F_SETFL, O_NONBLOCK);

 

    vnet_hdr = tap_probe_vnet_hdr(fd);

 

    s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr);

    if (!s) {

        close(fd);

        return -1;

    }

 

    snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s", helper,

             br);

 

    return 0;

}

static TAPState *net_tap_fd_init(NetClientState *peer,

                                 const char *model,

                                 const char *name,

                                 int fd,

                                 int vnet_hdr)

{

    NetClientState *nc;

    TAPState *s;

 

    nc = qemu_new_net_client(&net_tap_info, peer, model, name);

 

    s = DO_UPCAST(TAPState, nc, nc);

 

    s->fd = fd;

    s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;

    s->using_vnet_hdr = false;

    s->has_ufo = tap_probe_has_ufo(s->fd);

    s->enabled = true;

    tap_set_offload(&s->nc, 0, 0, 0, 0, 0);

    /*

     * Make sure host header length is set correctly in tap:

     * it might have been modified by another instance of qemu.

     */

    if (tap_probe_vnet_hdr_len(s->fd, s->host_vnet_hdr_len)) {

        tap_fd_set_vnet_hdr_len(s->fd, s->host_vnet_hdr_len);

    }

    tap_read_poll(s, true);

    s->vhost_net = NULL;

    return s;

}

static NetClientInfo net_tap_info = {

    .type = NET_CLIENT_OPTIONS_KIND_TAP,

    .size = sizeof(TAPState),

    .receive = tap_receive,

    .receive_raw = tap_receive_raw,

    .receive_iov = tap_receive_iov,

    .poll = tap_poll,

    .cleanup = tap_cleanup,

};

 

 

 

static void tap_read_poll(TAPState *s, bool enable)

{

    s->read_poll = enable;

    tap_update_fd_handler(s);

}

static void tap_update_fd_handler(TAPState *s)

{

    qemu_set_fd_handler2(s->fd,

                         s->read_poll && s->enabled ? tap_can_send : NULL,

                         s->read_poll && s->enabled ? tap_send     : NULL,

                         s->write_poll && s->enabled ? tap_writable : NULL,

                         s);

}

int qemu_set_fd_handler2(int fd,

                         IOCanReadHandler *fd_read_poll,

                         IOHandler *fd_read,

                         IOHandler *fd_write,

                         void *opaque)

{

    IOHandlerRecord *ioh;

 

    assert(fd >= 0);

 

    if (!fd_read && !fd_write) {

        QLIST_FOREACH(ioh, &io_handlers, next) {

            if (ioh->fd == fd) {

                ioh->deleted = 1;

                break;

            }

        }

    } else {

        QLIST_FOREACH(ioh, &io_handlers, next) {

            if (ioh->fd == fd)

                goto found;

        }

        ioh = g_malloc0(sizeof(IOHandlerRecord));

        QLIST_INSERT_HEAD(&io_handlers, ioh, next);

    found:

        ioh->fd = fd;

        ioh->fd_read_poll = fd_read_poll;

        ioh->fd_read = fd_read;

      ioh->fd_write = fd_write;

       ioh->opaque = opaque;

        ioh->pollfds_idx = -1;

        ioh->deleted = 0;

        qemu_notify_event();

    }

    return 0;

}

so,

tap_can_send,  tap_send, tap_writable, these are for read and write for network bridge which qemu made for communicating with guest OS.

now, we are curious about where ioh would be called.

The answer is main_loop in vl.c

 

static void main_loop(void)

{

    bool nonblocking;

    int last_io = 0;

#ifdef CONFIG_PROFILER

    int64_t ti;

#endif

    do {

        nonblocking = !kvm_enabled() && !xen_enabled() && last_io > 0;

#ifdef CONFIG_PROFILER

        ti = profile_getclock();

#endif

        last_io = main_loop_wait(nonblocking);

#ifdef CONFIG_PROFILER

        dev_time += profile_getclock() - ti;

#endif

    } while (!main_loop_should_exit());

}

 

int main_loop_wait(int nonblocking)

{

    int ret;

    uint32_t timeout = UINT32_MAX;

    int64_t timeout_ns;

 

    if (nonblocking) {

        timeout = 0;

    }

 

    /* poll any events */

    g_array_set_size(gpollfds, 0); /* reset for new iteration */

    /* XXX: separate device handlers from system ones */

#ifdef CONFIG_SLIRP

    slirp_pollfds_fill(gpollfds, &timeout);

#endif

    qemu_iohandler_fill(gpollfds);

 

    if (timeout == UINT32_MAX) {

        timeout_ns = -1;

    } else {

        timeout_ns = (uint64_t)timeout * (int64_t)(SCALE_MS);

    }

 

    timeout_ns = qemu_soonest_timeout(timeout_ns,

                                      timerlistgroup_deadline_ns(

                                          &main_loop_tlg));

 

    ret = os_host_main_loop_wait(timeout_ns);

    qemu_iohandler_poll(gpollfds, ret);

#ifdef CONFIG_SLIRP

    slirp_pollfds_poll(gpollfds, (ret < 0));

#endif

 

    qemu_clock_run_all_timers();

 

    return ret;

 

}

void qemu_iohandler_poll(GArray *pollfds, int ret)

{

    if (ret > 0) {

        IOHandlerRecord *pioh, *ioh;

 

        QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {

            int revents = 0;

 

            if (!ioh->deleted && ioh->pollfds_idx != -1) {

                GPollFD *pfd = &g_array_index(pollfds, GPollFD,

                                              ioh->pollfds_idx);

                revents = pfd->revents;

            }

 

            if (!ioh->deleted && ioh->fd_read &&

                (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) {

                ioh->fd_read(ioh->opaque);

            }

            if (!ioh->deleted && ioh->fd_write &&

                (revents & (G_IO_OUT | G_IO_ERR))) {

                ioh->fd_write(ioh->opaque);

            }

 

            /* Do this last in case read/write handlers marked it for deletion */

            if (ioh->deleted) {

                QLIST_REMOVE(ioh, next);

                g_free(ioh);

            }

        }

    }

}

Posted by MeatNBrew
l

you can find this code at 4119 line of vl.c

   if (net_init_clients() < 0) {

        exit(1);

    }

and also can find this code  at 188 line of qemu/hw/i386/pc_piix.c

    pc_nic_init(isa_bus, pci_bus); // network card initialization


Those two parts are key function to start initializing Network of Guest and Hypervisor.

Then, let's follow those functions.

First, we are starting with net_init_clients() function.

int net_init_clients(void)

{

    QemuOptsList *net = qemu_find_opts("net");


    if (default_net) {

        /* if no clients, we use a default config */

        qemu_opts_set(net, NULL, "type", "nic");

#ifdef CONFIG_SLIRP

        qemu_opts_set(net, NULL, "type", "user");

#endif

    }


    QTAILQ_INIT(&net_clients);


    if (qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, 1) == -1)

        return -1;


    if (qemu_opts_foreach(net, net_init_client, NULL, 1) == -1) {

        return -1;

    }


    return 0;

}

those function both net_init_netdev and net_init_client are doing same thing, so we follow net_init_netdev function.( finally both will call net_client_init function.

static int net_init_netdev(QemuOpts *opts, void *dummy)

{

    Error *local_err = NULL;

    int ret;


    ret = net_client_init(opts, 1, &local_err);

    if (error_is_set(&local_err)) {

        qerror_report_err(local_err);

        error_free(local_err);

        return -1;

    }

    return ret;

}

int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)

{

    void *object = NULL;

    Error *err = NULL;

    int ret = -1;


    {

        OptsVisitor *ov = opts_visitor_new(opts);


        net_visit(opts_get_visitor(ov), is_netdev, &object, &err);

        opts_visitor_cleanup(ov);

    }


    if (!err) {

        ret = net_client_init1(object, is_netdev, &err);

    }


    if (object) {

        QapiDeallocVisitor *dv = qapi_dealloc_visitor_new();


        net_visit(qapi_dealloc_get_visitor(dv), is_netdev, &object, NULL);

        qapi_dealloc_visitor_cleanup(dv);

    }


    error_propagate(errp, err);

    return ret;

}


static int net_client_init1(const void *object, int is_netdev, Error **errp)

{

    union {

        const Netdev    *netdev;

        const NetLegacy *net;

    } u;

    const NetClientOptions *opts;

    const char *name;


    if (is_netdev) {

        u.netdev = object;

        opts = u.netdev->opts;

        name = u.netdev->id;


        switch (opts->kind) {

#ifdef CONFIG_SLIRP

        case NET_CLIENT_OPTIONS_KIND_USER:

#endif

        case NET_CLIENT_OPTIONS_KIND_TAP:

        case NET_CLIENT_OPTIONS_KIND_SOCKET:

#ifdef CONFIG_VDE

        case NET_CLIENT_OPTIONS_KIND_VDE:

#endif

#ifdef CONFIG_NET_BRIDGE

        case NET_CLIENT_OPTIONS_KIND_BRIDGE:

#endif

        case NET_CLIENT_OPTIONS_KIND_HUBPORT:

            break;


        default:

            error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",

                      "a netdev backend type");

            return -1;

        }

    } else {

        u.net = object;

        opts = u.net->opts;

        /* missing optional values have been initialized to "all bits zero" */

        name = u.net->has_id ? u.net->id : u.net->name;

    }


    if (net_client_init_fun[opts->kind]) {

        NetClientState *peer = NULL;


        /* Do not add to a vlan if it's a -netdev or a nic with a netdev=

         * parameter. */

        if (!is_netdev &&

            (opts->kind != NET_CLIENT_OPTIONS_KIND_NIC ||

             !opts->nic->has_netdev)) {

            peer = net_hub_add_port(u.net->has_vlan ? u.net->vlan : 0, NULL);

        }


        if (net_client_init_fun[opts->kind](opts, name, peer) < 0) {

            /* TODO push error reporting into init() methods */

            error_set(errp, QERR_DEVICE_INIT_FAILED,

                      NetClientOptionsKind_lookup[opts->kind]);

            return -1;

        }

    }

    return 0;

}

finally we found function pointers, here are functions for that pointer structure.

static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(

    const NetClientOptions *opts,

    const char *name,

    NetClientState *peer) = {

        [NET_CLIENT_OPTIONS_KIND_NIC]       = net_init_nic,

#ifdef CONFIG_SLIRP

        [NET_CLIENT_OPTIONS_KIND_USER]      = net_init_slirp,

#endif

        [NET_CLIENT_OPTIONS_KIND_TAP]       = net_init_tap,

        [NET_CLIENT_OPTIONS_KIND_SOCKET]    = net_init_socket,

#ifdef CONFIG_VDE

        [NET_CLIENT_OPTIONS_KIND_VDE]       = net_init_vde,

#endif

        [NET_CLIENT_OPTIONS_KIND_DUMP]      = net_init_dump,

#ifdef CONFIG_NET_BRIDGE

        [NET_CLIENT_OPTIONS_KIND_BRIDGE]    = net_init_bridge,

#endif

        [NET_CLIENT_OPTIONS_KIND_HUBPORT]   = net_init_hubport,

};


Posted by MeatNBrew
l