static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,

                                 struct iovec *iov, unsigned int iov_cnt)

{

    struct virtio_net_ctrl_mac mac_data;

    size_t s;

    NetClientState *nc = qemu_get_queue(n->nic);



    if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) {

        if (iov_size(iov, iov_cnt) != sizeof(n->mac)) {

            return VIRTIO_NET_ERR;

        }

        s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac));

        assert(s == sizeof(n->mac));

        qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);

        rxfilter_notify(nc);



        return VIRTIO_NET_OK;

    }



    if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET) {

        return VIRTIO_NET_ERR;

    }



    int in_use = 0;

    int first_multi = 0;

    uint8_t uni_overflow = 0;

    uint8_t multi_overflow = 0;

    uint8_t *macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);



    s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,

                   sizeof(mac_data.entries));

    mac_data.entries = ldl_p(&mac_data.entries);

    if (s != sizeof(mac_data.entries)) {

        goto error;

    }

    iov_discard_front(&iov, &iov_cnt, s);



    if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) {

        goto error;

    }



    if (mac_data.entries <= MAC_TABLE_ENTRIES) {

        s = iov_to_buf(iov, iov_cnt, 0, macs,

                       mac_data.entries * ETH_ALEN);

        if (s != mac_data.entries * ETH_ALEN) {

            goto error;

        }

        in_use += mac_data.entries;

    } else {

        uni_overflow = 1;

    }



    iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN);



    first_multi = in_use;



    s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,

                   sizeof(mac_data.entries));

    mac_data.entries = ldl_p(&mac_data.entries);

    if (s != sizeof(mac_data.entries)) {

        goto error;

    }



    iov_discard_front(&iov, &iov_cnt, s);



    if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) {

        goto error;

    }



    if (in_use + mac_data.entries <= MAC_TABLE_ENTRIES) {

        s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN],

                       mac_data.entries * ETH_ALEN);

        if (s != mac_data.entries * ETH_ALEN) {

            goto error;

        }

        in_use += mac_data.entries;

    } else {

        multi_overflow = 1;

    }



    n->mac_table.in_use = in_use;

    n->mac_table.first_multi = first_multi;

    n->mac_table.uni_overflow = uni_overflow;

    n->mac_table.multi_overflow = multi_overflow;

    memcpy(n->mac_table.macs, macs, MAC_TABLE_ENTRIES * ETH_ALEN);

    g_free(macs);

    rxfilter_notify(nc);



    return VIRTIO_NET_OK;



error:

    g_free(macs);

    return VIRTIO_NET_ERR;

}
