From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:48857) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YECjH-0008P4-P1 for qemu-devel@nongnu.org; Thu, 22 Jan 2015 03:03:07 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YECjE-0005sp-24 for qemu-devel@nongnu.org; Thu, 22 Jan 2015 03:03:03 -0500 Received: from mail-pa0-x235.google.com ([2607:f8b0:400e:c03::235]:35422) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YECjD-0005sY-Hh for qemu-devel@nongnu.org; Thu, 22 Jan 2015 03:03:00 -0500 Received: by mail-pa0-f53.google.com with SMTP id kx10so206338pab.12 for ; Thu, 22 Jan 2015 00:02:59 -0800 (PST) From: sfeldma@gmail.com Date: Thu, 22 Jan 2015 00:03:56 -0800 Message-Id: <1421913839-22448-8-git-send-email-sfeldma@gmail.com> In-Reply-To: <1421913839-22448-1-git-send-email-sfeldma@gmail.com> References: <1421913839-22448-1-git-send-email-sfeldma@gmail.com> Subject: [Qemu-devel] [PATCH v5 07/10] qmp: add rocker device support List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org, jiri@resnulli.us, roopa@cumulusnetworks.com, john.fastabend@gmail.com, eblake@redhat.com, pbonzini@redhat.com, stefanha@gmail.com, dsahern@gmail.com, jasowang@redhat.com From: Scott Feldman Add QMP/HMP support for rocker devices. This is mostly for debugging purposes to see inside the device's tables and port configurations. Some examples: (qemu) info rocker sw1 name: sw1 id: 0x0000013512005452 ports: 4 (qemu) info rocker-ports sw1 ena/ speed/ auto port link duplex neg? sw1.1 up 10G FD No sw1.2 up 10G FD No sw1.3 !ena 10G FD No sw1.4 !ena 10G FD No (qemu) info rocker-of-dpa-flows sw1 prio tbl hits key(mask) --> actions 2 60 lport 1 vlan 1 LLDP src 00:02:00:00:02:00 dst 01:80:c2:00:00:0e 2 60 lport 1 vlan 1 ARP src 00:02:00:00:02:00 dst 00:02:00:00:03:00 2 60 lport 2 vlan 2 IPv6 src 00:02:00:00:03:00 dst 33:33:ff:00:00:02 proto 58 3 50 vlan 2 dst 33:33:ff:00:00:02 --> write group 0x32000001 goto tbl 60 2 60 lport 2 vlan 2 IPv6 src 00:02:00:00:03:00 dst 33:33:ff:00:03:00 proto 58 3 50 1 vlan 2 dst 33:33:ff:00:03:00 --> write group 0x32000001 goto tbl 60 2 60 lport 2 vlan 2 ARP src 00:02:00:00:03:00 dst 00:02:00:00:02:00 3 50 2 vlan 2 dst 00:02:00:00:02:00 --> write group 0x02000001 goto tbl 60 2 60 1 lport 2 vlan 2 IP src 00:02:00:00:03:00 dst 00:02:00:00:02:00 proto 1 3 50 2 vlan 1 dst 00:02:00:00:03:00 --> write group 0x01000002 goto tbl 60 2 60 1 lport 1 vlan 1 IP src 00:02:00:00:02:00 dst 00:02:00:00:03:00 proto 1 2 60 lport 1 vlan 1 IPv6 src 00:02:00:00:02:00 dst 33:33:ff:00:00:01 proto 58 3 50 vlan 1 dst 33:33:ff:00:00:01 --> write group 0x31000000 goto tbl 60 2 60 lport 1 vlan 1 IPv6 src 00:02:00:00:02:00 dst 33:33:ff:00:02:00 proto 58 3 50 1 vlan 1 dst 33:33:ff:00:02:00 --> write group 0x31000000 goto tbl 60 1 60 173 lport 2 vlan 2 LLDP src dst 01:80:c2:00:00:0e --> write group 0x02000000 1 60 6 lport 2 vlan 2 IPv6 src dst --> write group 0x02000000 1 60 174 lport 1 vlan 1 LLDP src dst 01:80:c2:00:00:0e --> write group 0x01000000 1 60 174 lport 2 vlan 2 IP src dst --> write group 0x02000000 1 60 6 lport 1 vlan 1 IPv6 src dst --> write group 0x01000000 1 60 181 lport 2 vlan 2 ARP src dst --> write group 0x02000000 1 10 715 lport 2 --> apply new vlan 2 goto tbl 20 1 60 177 lport 1 vlan 1 ARP src dst --> write group 0x01000000 1 60 174 lport 1 vlan 1 IP src dst --> write group 0x01000000 1 10 717 lport 1 --> apply new vlan 1 goto tbl 20 1 0 1432 lport 0(0xffff) --> goto tbl 10 (qemu) info rocker-of-dpa-groups sw1 id (decode) --> buckets 0x32000001 (type L2 multicast vlan 2 index 1) --> groups [0x02000001,0x02000000] 0x02000001 (type L2 interface vlan 2 lport 1) --> pop vlan out lport 1 0x01000002 (type L2 interface vlan 1 lport 2) --> pop vlan out lport 2 0x02000000 (type L2 interface vlan 2 lport 0) --> pop vlan out lport 0 0x01000000 (type L2 interface vlan 1 lport 0) --> pop vlan out lport 0 0x31000000 (type L2 multicast vlan 1 index 0) --> groups [0x01000002,0x01000000] Signed-off-by: Scott Feldman Signed-off-by: Jiri Pirko --- hmp-commands.hx | 24 ++++ hmp.c | 303 ++++++++++++++++++++++++++++++++++++++++ hmp.h | 4 + hw/net/rocker/rocker.c | 45 ++++++ hw/net/rocker/rocker_fp.c | 10 ++ hw/net/rocker/rocker_fp.h | 1 + hw/net/rocker/rocker_of_dpa.c | 308 +++++++++++++++++++++++++++++++++++++++++ monitor.c | 28 ++++ qapi-schema.json | 3 + qapi/rocker.json | 259 ++++++++++++++++++++++++++++++++++ qmp-commands.hx | 97 +++++++++++++ 11 files changed, 1082 insertions(+) create mode 100644 qapi/rocker.json diff --git a/hmp-commands.hx b/hmp-commands.hx index e37bc8b..6873a3a 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1787,5 +1787,29 @@ show available trace events and their state ETEXI STEXI +@item rocker @var{name} +@findex rocker +Show Rocker(s) +ETEXI + +STEXI +@item rocker_ports @var{name} +@findex rocker_ports +Show Rocker ports +ETEXI + +STEXI +@item rocker_of_dpa_flows @var{name} [@var{tbl_id}] +@findex rocker_of_dpa_flows +Show Rocker OF-DPA flow tables +ETEXI + +STEXI +@item rocker_of_dpa_groups @var{name} [@var{type}] +@findex rocker_of_dpa_groups +Show Rocker OF-DPA groups +ETEXI + +STEXI @end table ETEXI diff --git a/hmp.c b/hmp.c index 481be80..949d43c 100644 --- a/hmp.c +++ b/hmp.c @@ -15,6 +15,7 @@ #include "hmp.h" #include "net/net.h" +#include "net/eth.h" #include "sysemu/char.h" #include "qemu/option.h" #include "qemu/timer.h" @@ -1813,3 +1814,305 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) qapi_free_MemoryDeviceInfoList(info_list); } + +void hmp_rocker(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_str(qdict, "name"); + RockerSwitch *rocker; + Error *errp = NULL; + + rocker = qmp_rocker(name, &errp); + if (errp != NULL) { + hmp_handle_error(mon, &errp); + return; + } + + monitor_printf(mon, "name: %s\n", rocker->name); + monitor_printf(mon, "id: 0x%016lx\n", rocker->id); + monitor_printf(mon, "ports: %d\n", rocker->ports); + + qapi_free_RockerSwitch(rocker); +} + +void hmp_rocker_ports(Monitor *mon, const QDict *qdict) +{ + RockerPortList *list, *port; + const char *name = qdict_get_str(qdict, "name"); + Error *errp = NULL; + + list = qmp_rocker_ports(name, &errp); + if (errp != NULL) { + hmp_handle_error(mon, &errp); + return; + } + + monitor_printf(mon, " ena/ speed/ auto\n"); + monitor_printf(mon, " port link duplex neg?\n"); + + for (port = list; port; port = port->next) + monitor_printf(mon, "%10s %-4s %-3s %2s %-3s\n", + port->value->name, + port->value->enabled ? port->value->link_up ? + "up" : "down" : "!ena", + port->value->speed == 10000 ? "10G" : "??", + port->value->duplex ? "FD" : "HD", + port->value->autoneg ? "Yes" : "No"); + + qapi_free_RockerPortList(list); +} + +void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict) +{ + RockerOfDpaFlowList *list, *info; + const char *name = qdict_get_str(qdict, "name"); + uint32_t tbl_id = qdict_get_try_int(qdict, "tbl_id", -1); + Error *errp = NULL; + + list = qmp_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &errp); + if (errp != NULL) { + hmp_handle_error(mon, &errp); + return; + } + + monitor_printf(mon, "prio tbl hits key(mask) --> actions\n"); + + for (info = list; info; info = info->next) { + RockerOfDpaFlow *flow = info->value; + RockerOfDpaFlowKey *key = flow->key; + RockerOfDpaFlowMask *mask = flow->mask; + RockerOfDpaFlowAction *action = flow->action; + + if (flow->hits) { + monitor_printf(mon, "%-4d %-3d %-4ld", + key->priority, key->tbl_id, flow->hits); + } else { + monitor_printf(mon, "%-4d %-3d ", + key->priority, key->tbl_id); + } + + if (key->has_in_pport) { + monitor_printf(mon, " pport %d", key->in_pport); + if (mask->has_in_pport) { + monitor_printf(mon, "(0x%x)", mask->in_pport); + } + } + + if (key->has_vlan_id) { + monitor_printf(mon, " vlan %d", + key->vlan_id & VLAN_VID_MASK); + if (mask->has_vlan_id) { + monitor_printf(mon, "(0x%x)", mask->vlan_id); + } + } + + if (key->has_tunnel_id) { + monitor_printf(mon, " tunnel %d", key->tunnel_id); + if (mask->has_tunnel_id) { + monitor_printf(mon, "(0x%x)", mask->tunnel_id); + } + } + + if (key->has_eth_type) { + switch (key->eth_type) { + case 0x0806: + monitor_printf(mon, " ARP"); + break; + case 0x0800: + monitor_printf(mon, " IP"); + break; + case 0x86dd: + monitor_printf(mon, " IPv6"); + break; + case 0x8809: + monitor_printf(mon, " LACP"); + break; + case 0x88cc: + monitor_printf(mon, " LLDP"); + break; + default: + monitor_printf(mon, " eth type 0x%04x", key->eth_type); + break; + } + } + + if (key->has_eth_src) { + if ((strcmp(key->eth_src, "01:00:00:00:00:00") == 0) && + (mask->has_eth_src) && + (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " src "); + } else if ((strcmp(key->eth_src, "00:00:00:00:00:00") == 0) && + (mask->has_eth_src) && + (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " src "); + } else { + monitor_printf(mon, " src %s", key->eth_src); + if (mask->has_eth_src) { + monitor_printf(mon, "(%s)", mask->eth_src); + } + } + } + + if (key->has_eth_dst) { + if ((strcmp(key->eth_dst, "01:00:00:00:00:00") == 0) && + (mask->has_eth_dst) && + (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " dst "); + } else if ((strcmp(key->eth_dst, "00:00:00:00:00:00") == 0) && + (mask->has_eth_dst) && + (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " dst "); + } else { + monitor_printf(mon, " dst %s", key->eth_dst); + if (mask->has_eth_dst) { + monitor_printf(mon, "(%s)", mask->eth_dst); + } + } + } + + if (key->has_ip_proto) { + monitor_printf(mon, " proto %d", key->ip_proto); + if (mask->has_ip_proto) { + monitor_printf(mon, "(0x%x)", mask->ip_proto); + } + } + + if (key->has_ip_tos) { + monitor_printf(mon, " TOS %d", key->ip_tos); + if (mask->has_ip_tos) { + monitor_printf(mon, "(0x%x)", mask->ip_tos); + } + } + + if (key->has_ip_dst) { + monitor_printf(mon, " dst %s", key->ip_dst); + } + + if (action->has_goto_tbl || action->has_group_id || + action->has_new_vlan_id) { + monitor_printf(mon, " -->"); + } + + if (action->has_new_vlan_id) { + monitor_printf(mon, " apply new vlan %d", + ntohs(action->new_vlan_id)); + } + + if (action->has_group_id) { + monitor_printf(mon, " write group 0x%08x", action->group_id); + } + + if (action->has_goto_tbl) { + monitor_printf(mon, " goto tbl %d", action->goto_tbl); + } + + monitor_printf(mon, "\n"); + } + + qapi_free_RockerOfDpaFlowList(list); +} + +void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) +{ + RockerOfDpaGroupList *list, *g; + const char *name = qdict_get_str(qdict, "name"); + uint8_t type = qdict_get_try_int(qdict, "type", 9); + Error *errp = NULL; + bool set = false; + + list = qmp_rocker_of_dpa_groups(name, type != 9, type, &errp); + if (errp != NULL) { + hmp_handle_error(mon, &errp); + return; + } + + monitor_printf(mon, "id (decode) --> buckets\n"); + + for (g = list; g; g = g->next) { + RockerOfDpaGroup *group = g->value; + + monitor_printf(mon, "0x%08x", group->id); + + monitor_printf(mon, " (type %s", group->type == 0 ? "L2 interface" : + group->type == 1 ? "L2 rewrite" : + group->type == 2 ? "L3 unicast" : + group->type == 3 ? "L2 multicast" : + group->type == 4 ? "L2 flood" : + group->type == 5 ? "L3 interface" : + group->type == 6 ? "L3 multicast" : + group->type == 7 ? "L3 ECMP" : + group->type == 8 ? "L2 overlay" : + "unknown"); + + if (group->has_vlan_id) { + monitor_printf(mon, " vlan %d", group->vlan_id); + } + + if (group->has_pport) { + monitor_printf(mon, " pport %d", group->pport); + } + + if (group->has_index) { + monitor_printf(mon, " index %d", group->index); + } + + monitor_printf(mon, ") -->"); + + if (group->has_set_vlan_id && group->set_vlan_id) { + set = true; + monitor_printf(mon, " set vlan %d", + group->set_vlan_id & VLAN_VID_MASK); + } + + if (group->has_set_eth_src) { + if (!set) { + set = true; + monitor_printf(mon, " set"); + } + monitor_printf(mon, " src %s", group->set_eth_src); + } + + if (group->has_set_eth_dst) { + if (!set) { + set = true; + monitor_printf(mon, " set"); + } + monitor_printf(mon, " dst %s", group->set_eth_dst); + } + + set = false; + + if (group->has_ttl_check && group->ttl_check) { + monitor_printf(mon, " check TTL"); + } + + if (group->has_group_id && group->group_id) { + monitor_printf(mon, " group id 0x%08x", group->group_id); + } + + if (group->has_pop_vlan && group->pop_vlan) { + monitor_printf(mon, " pop vlan"); + } + + if (group->has_out_pport) { + monitor_printf(mon, " out pport %d", group->out_pport); + } + + if (group->has_group_ids) { + struct uint32List *id; + + monitor_printf(mon, " groups ["); + for (id = group->group_ids; id; id = id->next) { + monitor_printf(mon, "0x%08x", id->value); + if (id->next) { + monitor_printf(mon, ","); + } + } + monitor_printf(mon, "]"); + } + + monitor_printf(mon, "\n"); + } + + qapi_free_RockerOfDpaGroupList(list); +} + diff --git a/hmp.h b/hmp.h index 4bb5dca..dc8a15c 100644 --- a/hmp.h +++ b/hmp.h @@ -116,5 +116,9 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str); void delvm_completion(ReadLineState *rs, int nb_args, const char *str); void loadvm_completion(ReadLineState *rs, int nb_args, const char *str); +void hmp_rocker(Monitor *mon, const QDict *qdict); +void hmp_rocker_ports(Monitor *mon, const QDict *qdict); +void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict); +void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict); #endif diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index d8a1a8e..2cfb975 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -94,6 +94,51 @@ World *rocker_get_world(Rocker *r, enum rocker_world_type type) return NULL; } +RockerSwitch *qmp_rocker(const char *name, Error **errp) +{ + RockerSwitch *rocker = g_malloc0(sizeof(*rocker)); + Rocker *r; + + r = rocker_find(name); + if (!r) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s not found", name); + return NULL; + } + + rocker->name = g_strdup(r->name); + rocker->id = r->switch_id; + rocker->ports = r->fp_ports; + + return rocker; +} + +RockerPortList *qmp_rocker_ports(const char *name, Error **errp) +{ + RockerPortList *list = NULL; + Rocker *r; + int i; + + r = rocker_find(name); + if (!r) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s not found", name); + return NULL; + } + + for (i = r->fp_ports - 1; i >= 0; i--) { + RockerPortList *info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + struct fp_port *port = r->fp_port[i]; + + fp_port_get_info(port, info); + info->next = list; + list = info; + } + + return list; +} + uint32_t rocker_fp_ports(Rocker *r) { return r->fp_ports; diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c index 8d41190..0bcc93c 100644 --- a/hw/net/rocker/rocker_fp.c +++ b/hw/net/rocker/rocker_fp.c @@ -46,6 +46,16 @@ bool fp_port_get_link_up(FpPort *port) return !qemu_get_queue(port->nic)->link_down; } +void fp_port_get_info(FpPort *port, RockerPortList *info) +{ + info->value->name = g_strdup(port->name); + info->value->enabled = port->enabled; + info->value->link_up = fp_port_get_link_up(port); + info->value->speed = port->speed; + info->value->duplex = port->duplex; + info->value->autoneg = port->autoneg; +} + void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr) { memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a)); diff --git a/hw/net/rocker/rocker_fp.h b/hw/net/rocker/rocker_fp.h index f827f8d..564c4ce 100644 --- a/hw/net/rocker/rocker_fp.h +++ b/hw/net/rocker/rocker_fp.h @@ -29,6 +29,7 @@ typedef struct world World; int fp_port_eg(FpPort *port, const struct iovec *iov, int iovcnt); bool fp_port_get_link_up(FpPort *port); +void fp_port_get_info(FpPort *port, RockerPortList *info); void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr); void fp_port_set_macaddr(FpPort *port, MACAddr *macaddr); uint8_t fp_port_get_learning(FpPort *port); diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c index 1cf7e4e..3c14e3f 100644 --- a/hw/net/rocker/rocker_of_dpa.c +++ b/hw/net/rocker/rocker_of_dpa.c @@ -2301,6 +2301,314 @@ static void of_dpa_uninit(World *world) g_hash_table_destroy(of_dpa->flow_tbl); } +struct of_dpa_flow_fill_context { + RockerOfDpaFlowList *list; + uint32_t tbl_id; +}; + +static void of_dpa_flow_fill(void *cookie, void *value, void *user_data) +{ + struct of_dpa_flow *flow = value; + struct of_dpa_flow_key *key = &flow->key; + struct of_dpa_flow_key *mask = &flow->mask; + struct of_dpa_flow_fill_context *flow_context = user_data; + RockerOfDpaFlowList *new; + RockerOfDpaFlow *nflow; + RockerOfDpaFlowKey *nkey; + RockerOfDpaFlowMask *nmask; + RockerOfDpaFlowAction *naction; + + if (flow_context->tbl_id != -1 && + flow_context->tbl_id != key->tbl_id) { + return; + } + + new = g_malloc0(sizeof(*new)); + nflow = new->value = g_malloc0(sizeof(*nflow)); + nkey = nflow->key = g_malloc0(sizeof(*nkey)); + nmask = nflow->mask = g_malloc0(sizeof(*nmask)); + naction = nflow->action = g_malloc0(sizeof(*naction)); + + nflow->cookie = flow->cookie; + nflow->hits = flow->stats.hits; + nkey->priority = flow->priority; + nkey->tbl_id = key->tbl_id; + + if (key->in_pport || mask->in_pport) { + nkey->has_in_pport = true; + nkey->in_pport = key->in_pport; + } + + if (nkey->has_in_pport && mask->in_pport != 0xffffffff) { + nmask->has_in_pport = true; + nmask->in_pport = mask->in_pport; + } + + if (key->eth.vlan_id || mask->eth.vlan_id) { + nkey->has_vlan_id = true; + nkey->vlan_id = ntohs(key->eth.vlan_id); + } + + if (nkey->has_vlan_id && mask->eth.vlan_id != 0xffff) { + nmask->has_vlan_id = true; + nmask->vlan_id = ntohs(mask->eth.vlan_id); + } + + if (key->tunnel_id || mask->tunnel_id) { + nkey->has_tunnel_id = true; + nkey->tunnel_id = key->tunnel_id; + } + + if (nkey->has_tunnel_id && mask->tunnel_id != 0xffffffff) { + nmask->has_tunnel_id = true; + nmask->tunnel_id = mask->tunnel_id; + } + + if (memcmp(key->eth.src.a, zero_mac.a, ETH_ALEN) || + memcmp(mask->eth.src.a, zero_mac.a, ETH_ALEN)) { + nkey->has_eth_src = true; + nkey->eth_src = qemu_mac_strdup_printf(key->eth.src.a); + } + + if (nkey->has_eth_src && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) { + nmask->has_eth_src = true; + nmask->eth_src = qemu_mac_strdup_printf(mask->eth.src.a); + } + + if (memcmp(key->eth.dst.a, zero_mac.a, ETH_ALEN) || + memcmp(mask->eth.dst.a, zero_mac.a, ETH_ALEN)) { + nkey->has_eth_dst = true; + nkey->eth_dst = qemu_mac_strdup_printf(key->eth.dst.a); + } + + if (nkey->has_eth_dst && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) { + nmask->has_eth_dst = true; + nmask->eth_dst = qemu_mac_strdup_printf(mask->eth.dst.a); + } + + if (key->eth.type) { + + nkey->has_eth_type = true; + nkey->eth_type = ntohs(key->eth.type); + + switch (ntohs(key->eth.type)) { + case 0x0800: + case 0x86dd: + if (key->ip.proto || mask->ip.proto) { + nkey->has_ip_proto = true; + nkey->ip_proto = key->ip.proto; + } + if (nkey->has_ip_proto && mask->ip.proto != 0xff) { + nmask->has_ip_proto = true; + nmask->ip_proto = mask->ip.proto; + } + if (key->ip.tos || mask->ip.tos) { + nkey->has_ip_tos = true; + nkey->ip_tos = key->ip.tos; + } + if (nkey->has_ip_tos && mask->ip.tos != 0xff) { + nmask->has_ip_tos = true; + nmask->ip_tos = mask->ip.tos; + } + break; + } + + switch (ntohs(key->eth.type)) { + case 0x0800: + if (key->ipv4.addr.dst || mask->ipv4.addr.dst) { + char *dst = inet_ntoa(*(struct in_addr *)&key->ipv4.addr.dst); + int dst_len = of_dpa_mask2prefix(mask->ipv4.addr.dst); + nkey->has_ip_dst = true; + nkey->ip_dst = g_strdup_printf("%s/%d", dst, dst_len); + } + break; + } + } + + if (flow->action.goto_tbl) { + naction->has_goto_tbl = true; + naction->goto_tbl = flow->action.goto_tbl; + } + + if (flow->action.write.group_id) { + naction->has_group_id = true; + naction->group_id = flow->action.write.group_id; + } + + if (flow->action.apply.new_vlan_id) { + naction->has_new_vlan_id = true; + naction->new_vlan_id = flow->action.apply.new_vlan_id; + } + + new->next = flow_context->list; + flow_context->list = new; +} + +RockerOfDpaFlowList *qmp_rocker_of_dpa_flows(const char *name, bool has_tbl_id, + uint32_t tbl_id, Error **errp) +{ + struct rocker *r; + struct world *w; + struct of_dpa *of_dpa; + struct of_dpa_flow_fill_context fill_context = { + .list = NULL, + .tbl_id = tbl_id, + }; + + r = rocker_find(name); + if (!r) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s not found", name); + return NULL; + } + + w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA); + if (!w) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s doesn't have OF-DPA world", name); + return NULL; + } + + of_dpa = world_private(w); + + g_hash_table_foreach(of_dpa->flow_tbl, of_dpa_flow_fill, &fill_context); + + return fill_context.list; +} + +struct of_dpa_group_fill_context { + RockerOfDpaGroupList *list; + uint8_t type; +}; + +static void of_dpa_group_fill(void *key, void *value, void *user_data) +{ + struct of_dpa_group *group = value; + struct of_dpa_group_fill_context *flow_context = user_data; + RockerOfDpaGroupList *new; + RockerOfDpaGroup *ngroup; + struct uint32List *id; + int i; + + if (flow_context->type != 9 && + flow_context->type != ROCKER_GROUP_TYPE_GET(group->id)) { + return; + } + + new = g_malloc0(sizeof(*new)); + ngroup = new->value = g_malloc0(sizeof(*ngroup)); + + ngroup->id = group->id; + + ngroup->type = ROCKER_GROUP_TYPE_GET(group->id); + + switch (ngroup->type) { + case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE: + ngroup->has_vlan_id = true; + ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id); + ngroup->has_pport = true; + ngroup->pport = ROCKER_GROUP_PORT_GET(group->id); + ngroup->has_out_pport = true; + ngroup->out_pport = group->l2_interface.out_pport; + ngroup->has_pop_vlan = true; + ngroup->pop_vlan = group->l2_interface.pop_vlan; + break; + case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE: + ngroup->has_index = true; + ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id); + ngroup->has_group_id = true; + ngroup->group_id = group->l2_rewrite.group_id; + if (group->l2_rewrite.vlan_id) { + ngroup->has_set_vlan_id = true; + ngroup->set_vlan_id = ntohs(group->l2_rewrite.vlan_id); + } + break; + if (memcmp(group->l2_rewrite.src_mac.a, zero_mac.a, ETH_ALEN)) { + ngroup->has_set_eth_src = true; + ngroup->set_eth_src = + qemu_mac_strdup_printf(group->l2_rewrite.src_mac.a); + } + if (memcmp(group->l2_rewrite.dst_mac.a, zero_mac.a, ETH_ALEN)) { + ngroup->has_set_eth_dst = true; + ngroup->set_eth_dst = + qemu_mac_strdup_printf(group->l2_rewrite.dst_mac.a); + } + case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD: + case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST: + ngroup->has_vlan_id = true; + ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id); + ngroup->has_index = true; + ngroup->index = ROCKER_GROUP_INDEX_GET(group->id); + for (i = 0; i < group->l2_flood.group_count; i++) { + ngroup->has_group_ids = true; + id = g_malloc0(sizeof(*id)); + id->value = group->l2_flood.group_ids[i]; + id->next = ngroup->group_ids; + ngroup->group_ids = id; + } + break; + case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST: + ngroup->has_index = true; + ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id); + ngroup->has_group_id = true; + ngroup->group_id = group->l3_unicast.group_id; + if (group->l3_unicast.vlan_id) { + ngroup->has_set_vlan_id = true; + ngroup->set_vlan_id = ntohs(group->l3_unicast.vlan_id); + } + if (memcmp(group->l3_unicast.src_mac.a, zero_mac.a, ETH_ALEN)) { + ngroup->has_set_eth_src = true; + ngroup->set_eth_src = + qemu_mac_strdup_printf(group->l3_unicast.src_mac.a); + } + if (memcmp(group->l3_unicast.dst_mac.a, zero_mac.a, ETH_ALEN)) { + ngroup->has_set_eth_dst = true; + ngroup->set_eth_dst = + qemu_mac_strdup_printf(group->l3_unicast.dst_mac.a); + } + if (group->l3_unicast.ttl_check) { + ngroup->has_ttl_check = true; + ngroup->ttl_check = group->l3_unicast.ttl_check; + } + break; + } + + new->next = flow_context->list; + flow_context->list = new; +} + +RockerOfDpaGroupList *qmp_rocker_of_dpa_groups(const char *name, bool has_type, + uint8_t type, Error **errp) +{ + struct rocker *r; + struct world *w; + struct of_dpa *of_dpa; + struct of_dpa_group_fill_context fill_context = { + .list = NULL, + .type = type, + }; + + r = rocker_find(name); + if (!r) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s not found", name); + return NULL; + } + + w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA); + if (!w) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s doesn't have OF-DPA world", name); + return NULL; + } + + of_dpa = world_private(w); + + g_hash_table_foreach(of_dpa->group_tbl, of_dpa_group_fill, &fill_context); + + return fill_context.list; +} + static WorldOps of_dpa_ops = { .init = of_dpa_init, .uninit = of_dpa_uninit, diff --git a/monitor.c b/monitor.c index 7e4f605..79b8ebf 100644 --- a/monitor.c +++ b/monitor.c @@ -2935,6 +2935,34 @@ static mon_cmd_t info_cmds[] = { .mhandler.cmd = hmp_info_memory_devices, }, { + .name = "rocker", + .args_type = "name:s", + .params = "name", + .help = "Show rocker switch", + .mhandler.cmd = hmp_rocker, + }, + { + .name = "rocker-ports", + .args_type = "name:s", + .params = "name", + .help = "Show rocker ports", + .mhandler.cmd = hmp_rocker_ports, + }, + { + .name = "rocker-of-dpa-flows", + .args_type = "name:s,tbl_id:i?", + .params = "name [tbl_id]", + .help = "Show rocker OF-DPA flow tables", + .mhandler.cmd = hmp_rocker_of_dpa_flows, + }, + { + .name = "rocker-of-dpa-groups", + .args_type = "name:s,type:i?", + .params = "name [type]", + .help = "Show rocker OF-DPA groups", + .mhandler.cmd = hmp_rocker_of_dpa_groups, + }, + { .name = NULL, }, }; diff --git a/qapi-schema.json b/qapi-schema.json index fbfc52f..c6d04ca 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3523,3 +3523,6 @@ # Since: 2.1 ## { 'command': 'rtc-reset-reinjection' } + +# Rocker ethernet network switch +{ 'include': 'qapi/rocker.json' } diff --git a/qapi/rocker.json b/qapi/rocker.json new file mode 100644 index 0000000..326c6c7 --- /dev/null +++ b/qapi/rocker.json @@ -0,0 +1,259 @@ +## +# @Rocker: +# +# Rocker switch information. +# +# @name: switch name +# +# @id: switch ID +# +# @ports: number of front-panel ports +## +{ 'type': 'RockerSwitch', + 'data': { 'name': 'str', 'id': 'uint64', 'ports': 'uint32' } } + +## +# @rocker: +# +# Return rocker switch information. +# +# Returns: @Rocker information +# +# Since: 2.3 +## +{ 'command': 'rocker', + 'data': { 'name': 'str' }, + 'returns': 'RockerSwitch' } + +## +# @RockerPortDuplex: +# +# An eumeration of port duplex states. +# +# @half: half duplex +# +# @full: full duplex +## +{ 'enum': 'RockerPortDuplex', 'data': [ 'half', 'full' ] } + +## +# @RockerPortAutoneg: +# +# An eumeration of port autoneg states. +# +# @off: autoneg is off +# +# @on: autoneg is on +## +{ 'enum': 'RockerPortAutoneg', 'data': [ 'off', 'on' ] } + +## +# @RockerPort: +# +# Rocker switch port information. +# +# @name: port name +# +# @enabled: port is enabled for I/O +# +# @link-up: physical link is UP on port +# +# @speed: port link speed in Mbps +# +# @duplex: port link duplex +# +# @autoneg: port link autoneg +## +{ 'type': 'RockerPort', + 'data': { 'name': 'str', 'enabled': 'bool', 'link-up': 'bool', + 'speed': 'uint32', 'duplex': 'RockerPortDuplex', + 'autoneg': 'RockerPortAutoneg' } } + +## +# @rocker-ports: +# +# Return rocker switch information. +# +# Returns: @Rocker information +# +# Since: 2.3 +## +{ 'command': 'rocker-ports', + 'data': { 'name': 'str' }, + 'returns': ['RockerPort'] } + +## +# @RockerOfDpaFlowKey: +# +# Rocker switch OF-DPA flow key +# +# @priority: key priority, 0 being lowest priority +# +# @tbl-id: flow table ID +# +# @in-pport: physical input port +# +# @tunnel-id: tunnel ID +# +# @vlan-id: VLAN ID +# +# @eth-type: Ethernet header type +# +# @eth-src: Ethernet header source MAC address +# +# @eth-dst: Ethernet header destination MAC address +# +# @ip-proto: IP Header protocol field +# +# @ip-tos: IP header TOS field +# +# @ip-dst: IP header destination address +## +{ 'type': 'RockerOfDpaFlowKey', + 'data' : { 'priority': 'uint32', 'tbl-id': 'uint32', '*in-pport': 'uint32', + '*tunnel-id': 'uint32', '*vlan-id': 'uint16', + '*eth-type': 'uint16', '*eth-src': 'str', '*eth-dst': 'str', + '*ip-proto': 'uint8', '*ip-tos': 'uint8', '*ip-dst': 'str' } } + +## +# @RockerOfDpaFlowMask: +# +# Rocker switch OF-DPA flow mask +# +# @in-pport: physical input port +# +# @tunnel-id: tunnel ID +# +# @vlan-id: VLAN ID +# +# @eth-src: Ethernet header source MAC address +# +# @eth-dst: Ethernet header destination MAC address +# +# @ip-proto: IP Header protocol field +# +# @ip-tos: IP header TOS field +## +{ 'type': 'RockerOfDpaFlowMask', + 'data' : { '*in-pport': 'uint32', '*tunnel-id': 'uint32', + '*vlan-id': 'uint16', '*eth-src': 'str', '*eth-dst': 'str', + '*ip-proto': 'uint8', '*ip-tos': 'uint8' } } + +## +# @RockerOfDpaFlowAction: +# +# Rocker switch OF-DPA flow action +# +# @goto-tbl: next table ID +# +# @group-id: group ID +# +# @tunnel-lport: tunnel logical port ID +# +# @vlan-id: VLAN ID +# +# @new-vlan-id: new VLAN ID +# +# @out-pport: physical output port +## +{ 'type': 'RockerOfDpaFlowAction', + 'data' : { '*goto-tbl': 'uint32', '*group-id': 'uint32', + '*tunnel-lport': 'uint32', '*vlan-id': 'uint16', + '*new-vlan-id': 'uint16', '*out-pport': 'uint32' } } + +## +# @RockerOfDpaFlow: +# +# Rocker switch OF-DPA flow +# +# @cookie: flow unique cookie ID +# +# @hits: count of matches (hits) on flow +# +# @key: flow key +# +# @mask: flow mask +# +# @action: flow action +# +# Since: 2.3 +## +{ 'type': 'RockerOfDpaFlow', + 'data': { 'cookie': 'uint64', 'hits': 'uint64', 'key': 'RockerOfDpaFlowKey', + 'mask': 'RockerOfDpaFlowMask', 'action': 'RockerOfDpaFlowAction' } } + +## +# @rocker-of-dpa-flows: +# +# Return rocker OF-DPA flow information. +# +# @name: switch name +# +# @tbl-id: (optional) flow table ID. If tbl-id is not specified, returns +# flow information for all tables. +# +# Returns: @Rocker OF-DPA flow information +# +# Since: 2.3 +## +{ 'command': 'rocker-of-dpa-flows', + 'data': { 'name': 'str', '*tbl-id': 'uint32' }, + 'returns': ['RockerOfDpaFlow'] } + +## +# @RockerOfDpaGroup: +# +# Rocker switch OF-DPA group +# +# @id: group unique ID +# +# @type: group type +# +# @vlan-id: VLAN ID +# +# @pport: physical port number +# +# @index: group index, unique with group type +# +# @out-pport: output physical port number +# +# @group-id: next group ID +# +# @set-vlan-id: VLAN ID to set +# +# @pop-vlan: pop VLAN headr from packet +# +# @group-ids: list of next group IDs +# +# @set-eth-src: set source MAC address in Ethernet header +# +# @set-eth-dst: set destination MAC address in Ethernet header +# +# @ttl-check: perform TTL check +## +{ 'type': 'RockerOfDpaGroup', + 'data': { 'id': 'uint32', 'type': 'uint8', '*vlan-id': 'uint16', + '*pport': 'uint32', '*index': 'uint32', '*out-pport': 'uint32', + '*group-id': 'uint32', '*set-vlan-id': 'uint16', + '*pop-vlan': 'uint8', '*group-ids': ['uint32'], + '*set-eth-src': 'str', '*set-eth-dst': 'str', + '*ttl-check': 'uint8' } } + +## +# @rocker-of-dpa-groups: +# +# Return rocker OF-DPA group information. +# +# @name: switch name +# +# @type: (optional) group type. If type is not specified, returns +# group information for all group types. +# +# Returns: @Rocker OF-DPA group information +# +# Since: 2.3 +## +{ 'command': 'rocker-of-dpa-groups', + 'data': { 'name': 'str', '*type': 'uint8' }, + 'returns': ['RockerOfDpaGroup'] } + diff --git a/qmp-commands.hx b/qmp-commands.hx index 8957201..9007c12 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -3902,3 +3902,100 @@ Move mouse pointer to absolute coordinates (20000, 400). <- { "return": {} } EQMP + + { + .name = "query-rocker", + .args_type = "name:s", + .mhandler.cmd_new = qmp_marshal_input_rocker, + }, + +SQMP +Show rocker switch +------------------ + +Arguments: + +- "name": switch name + +Example: + +-> { "execute": "query-rocker", "arguments": { "name": "sw1" } } +<- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}} + +EQMP + + { + .name = "query-rocker-ports", + .args_type = "name:s", + .mhandler.cmd_new = qmp_marshal_input_rocker_ports, + }, + +SQMP +Show rocker switch ports +------------------------ + +Arguments: + +- "name": switch name + +Example: + +-> { "execute": "query-rocker-ports", "arguments": { "name": "sw1" } } +<- { "return": [ {"duplex": "full", "enabled": true, "name": "sw1.1", "autoneg": "off", "link-up": true, "speed": 10000}, + {"duplex": "full", "enabled": true, "name": "sw1.2", "autoneg": "off", "link-up": true, "speed": 10000} + ]} + +EQMP + + { + .name = "query-rocker-of-dpa-flows", + .args_type = "name:s,tbl-id:i?", + .mhandler.cmd_new = qmp_marshal_input_rocker_of_dpa_flows, + }, + +SQMP +Show rocker switch OF-DPA flow tables +------------------------------------- + +Arguments: + +- "name": switch name +- "tbl-id": (optional) flow table ID + +Example: + +-> { "execute": "query-rocker-of-dpa-flows", "arguments": { "name": "sw1" } } +<- { "return": [ {"key": {"in-pport": 0, "priority": 1, "tbl-id": 0}, + "hits": 138, + "cookie": 0, + "action": {"goto-tbl": 10}, + "mask": {"in-pport": 4294901760} + }, + {...more...}, + ]} + +EQMP + + { + .name = "query-rocker-of-dpa-groups", + .args_type = "name:s,type:i?", + .mhandler.cmd_new = qmp_marshal_input_rocker_of_dpa_groups, + }, + +SQMP +Show rocker OF-DPA group tables +------------------------------- + +Arguments: + +- "name": switch name +- "type": (optional) group type + +Example: + +-> { "execute": "query-rocker-of-dpa-groups", "arguments": { "name": "sw1" } } +<- { "return": [ {"type": 0, "out-pport": 2, "pport": 2, "vlan-id": 3841, "pop-vlan": 1, "id": 251723778}, + {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3841, "pop-vlan": 1, "id": 251723776}, + {"type": 0, "out-pport": 1, "pport": 1, "vlan-id": 3840, "pop-vlan": 1, "id": 251658241}, + {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3840, "pop-vlan": 1, "id": 251658240} + ]} -- 1.7.10.4