diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 360635bb4..4d45c0df8 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -18,8 +18,13 @@ jobs: env: PKG_CONFIG_PATH: /data/dpdk/dpdklib/lib64/pkgconfig steps: - - uses: actions/checkout@v2 - - name: make + - name: Checkout Code + uses: actions/checkout@v2 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + - name: build run: make -j build-all: @@ -27,8 +32,13 @@ jobs: env: PKG_CONFIG_PATH: /data/dpdk/dpdklib/lib64/pkgconfig steps: - - uses: actions/checkout@v2 - - name: config + - name: Checkout Code + uses: actions/checkout@v2 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Config run: sed -i 's/=n$/=y/' config.mk - - name: make + - name: build run: make -j diff --git a/.github/workflows/run.yaml b/.github/workflows/run.yaml index 29b1a5423..b85d8b911 100644 --- a/.github/workflows/run.yaml +++ b/.github/workflows/run.yaml @@ -18,10 +18,15 @@ jobs: env: PKG_CONFIG_PATH: /data/dpdk/dpdklib/lib64/pkgconfig steps: - - uses: actions/checkout@v2 - - name: make + - name: Checkout Code + uses: actions/checkout@v2 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Build run: make -j - - name: install + - name: Install run: make install - - name: run-dpvs + - name: Run DPVS run: sudo dpvsci $(pwd)/bin/dpvs diff --git a/include/conf/inetaddr.h b/include/conf/inetaddr.h index f97994f86..7a82b0d94 100644 --- a/include/conf/inetaddr.h +++ b/include/conf/inetaddr.h @@ -100,4 +100,17 @@ struct inet_addr_front { }; #endif /* CONFIG_DPVS_AGENT */ +struct inet_maddr_entry { + char ifname[IFNAMSIZ]; + union inet_addr maddr; + int af; + uint32_t flags; + uint32_t refcnt; +} __attribute__((__packed__)); + +struct inet_maddr_array { + int nmaddr; + struct inet_maddr_entry maddrs[0]; +} __attribute__((__packed__)); + #endif /* __DPVS_INETADDR_CONF_H__ */ diff --git a/include/conf/netif_addr.h b/include/conf/netif_addr.h new file mode 100644 index 000000000..1a127e871 --- /dev/null +++ b/include/conf/netif_addr.h @@ -0,0 +1,37 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2021 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __DPVS_NETIF_ADDR_CONF_H__ +#define __DPVS_NETIF_ADDR_CONF_H__ + +enum { + HW_ADDR_F_FROM_KNI = 1, // from linux kni device in local layer +}; + +struct netif_hw_addr_entry { + char addr[18]; + uint32_t refcnt; + uint16_t flags; + uint16_t sync_cnt; +} __attribute__((__packed__)); + +struct netif_hw_addr_array { + int count; + struct netif_hw_addr_entry entries[0]; +} __attribute__((__packed__)); + +#endif diff --git a/include/conf/sockopts.h b/include/conf/sockopts.h index 2e3cd7595..83d02ccc9 100644 --- a/include/conf/sockopts.h +++ b/include/conf/sockopts.h @@ -84,6 +84,7 @@ DPVSMSG(SOCKOPT_SET_IFADDR_SET) \ DPVSMSG(SOCKOPT_SET_IFADDR_FLUSH) \ DPVSMSG(SOCKOPT_GET_IFADDR_SHOW) \ + DPVSMSG(SOCKOPT_GET_IFMADDR_SHOW) \ \ DPVSMSG(SOCKOPT_NETIF_SET_LCORE) \ DPVSMSG(SOCKOPT_NETIF_SET_PORT) \ @@ -98,6 +99,7 @@ DPVSMSG(SOCKOPT_NETIF_GET_PORT_XSTATS) \ DPVSMSG(SOCKOPT_NETIF_GET_PORT_EXT_INFO) \ DPVSMSG(SOCKOPT_NETIF_GET_BOND_STATUS) \ + DPVSMSG(SOCKOPT_NETIF_GET_MADDR)\ DPVSMSG(SOCKOPT_NETIF_GET_MAX) \ \ DPVSMSG(SOCKOPT_SET_NEIGH_ADD) \ diff --git a/include/inetaddr.h b/include/inetaddr.h index 2a85e7611..44276659f 100644 --- a/include/inetaddr.h +++ b/include/inetaddr.h @@ -31,10 +31,12 @@ struct inet_device { struct list_head ifa_list[DPVS_MAX_LCORE]; /* inet_ifaddr list */ struct list_head ifm_list[DPVS_MAX_LCORE]; /* inet_ifmcaddr list*/ uint32_t ifa_cnt[DPVS_MAX_LCORE]; + uint32_t ifm_cnt[DPVS_MAX_LCORE]; rte_atomic32_t refcnt; /* not used yet */ #define this_ifa_list ifa_list[rte_lcore_id()] #define this_ifm_list ifm_list[rte_lcore_id()] #define this_ifa_cnt ifa_cnt[rte_lcore_id()] +#define this_ifm_cnt ifm_cnt[rte_lcore_id()] }; /* diff --git a/include/netif_addr.h b/include/netif_addr.h index c90da57cc..e1d98aa8e 100644 --- a/include/netif_addr.h +++ b/include/netif_addr.h @@ -24,9 +24,7 @@ #ifndef __DPVS_NETIF_ADDR_H__ #define __DPVS_NETIF_ADDR_H__ -enum { - HW_ADDR_F_FROM_KNI = 1, // from linux kni device in local layer -}; +#include "conf/netif_addr.h" struct netif_hw_addr { struct list_head list; @@ -55,6 +53,7 @@ int netif_mc_add(struct netif_port *dev, const struct rte_ether_addr *addr); int netif_mc_del(struct netif_port *dev, const struct rte_ether_addr *addr); void netif_mc_flush(struct netif_port *dev); void netif_mc_init(struct netif_port *dev); + int __netif_mc_dump(struct netif_port *dev, uint16_t filter_flags, struct rte_ether_addr *addrs, size_t *naddr); int netif_mc_dump(struct netif_port *dev, uint16_t filter_flags, @@ -74,6 +73,8 @@ int netif_mc_sync_multiple(struct netif_port *to, struct netif_port *from, int s int __netif_mc_unsync_multiple(struct netif_port *to, struct netif_port *from, int sync_cnt); int netif_mc_unsync_multiple(struct netif_port *to, struct netif_port *from, int sync_cnt); +int netif_get_multicast_addrs(struct netif_port *dev, void **out, size_t *outlen); + static inline int eth_addr_equal(const struct rte_ether_addr *addr1, const struct rte_ether_addr *addr2) { diff --git a/src/inetaddr.c b/src/inetaddr.c index c5f13e0b6..077f9d844 100644 --- a/src/inetaddr.c +++ b/src/inetaddr.c @@ -91,6 +91,7 @@ static inline void imc_hash(struct inet_ifmcaddr *imc, struct inet_device *idev) { list_add(&imc->d_list, &idev->this_ifm_list); ++imc->refcnt; + ++idev->this_ifm_cnt; } static inline void imc_unhash(struct inet_ifmcaddr *imc) @@ -99,6 +100,7 @@ static inline void imc_unhash(struct inet_ifmcaddr *imc) list_del(&imc->d_list); --imc->refcnt; + --imc->idev->this_ifm_cnt; } static struct inet_ifmcaddr *imc_lookup(int af, const struct inet_device *idev, @@ -1833,6 +1835,39 @@ static int ifaddr_get_verbose(struct inet_device *idev, struct inet_addr_data_ar return err; } +static int ifmaddr_fill_entries(struct inet_device *idev, struct inet_maddr_array **parray, int *plen) +{ + lcoreid_t cid; + int ifm_cnt, len, off; + struct inet_ifmcaddr *ifm; + struct inet_maddr_array *array; + + cid = rte_lcore_id(); + ifm_cnt = idev->ifm_cnt[cid]; + + len = sizeof(struct inet_maddr_array) + ifm_cnt * sizeof(struct inet_maddr_entry); + array = rte_calloc(NULL, 1, len, RTE_CACHE_LINE_SIZE); + if (unlikely(!array)) + return EDPVS_NOMEM; + + off = 0; + list_for_each_entry(ifm, &idev->ifm_list[cid], d_list) { + strncpy(array->maddrs[off].ifname, ifm->idev->dev->name, + sizeof(array->maddrs[off].ifname) - 1); + array->maddrs[off].maddr = ifm->addr; + array->maddrs[off].af = ifm->af; + array->maddrs[off].flags = ifm->flags; + array->maddrs[off].refcnt = ifm->refcnt; + if (++off >= ifm_cnt) + break; + } + array->nmaddr = off; + + *parray = array; + *plen = len; + return EDPVS_OK; +} + static int ifa_sockopt_set(sockoptid_t opt, const void *conf, size_t size) { struct netif_port *dev; @@ -1967,60 +2002,92 @@ static int ifa_sockopt_get(sockoptid_t opt, const void *conf, size_t size, int err, len = 0; struct netif_port *dev; struct inet_device *idev = NULL; + struct inet_addr_data_array *array = NULL; const struct inet_addr_param *param = conf; - if (!conf || size < sizeof(struct inet_addr_param) || !out || !outsize) + struct inet_maddr_array *marray = NULL; + const char *ifname = conf; + + if (!conf || !out || !outsize) return EDPVS_INVAL; - if (opt != SOCKOPT_GET_IFADDR_SHOW) - return EDPVS_NOTSUPP; + switch (opt) { + case SOCKOPT_GET_IFADDR_SHOW: + if (size < sizeof(struct inet_addr_param) || param->ifa_ops != INET_ADDR_GET) + return EDPVS_INVAL; - if (param->ifa_ops != INET_ADDR_GET) - return EDPVS_INVAL; + if (param->ifa_entry.af != AF_INET && + param->ifa_entry.af != AF_INET6 && + param->ifa_entry.af != AF_UNSPEC) + return EDPVS_NOTSUPP; - if (param->ifa_entry.af != AF_INET && - param->ifa_entry.af != AF_INET6 && - param->ifa_entry.af != AF_UNSPEC) - return EDPVS_NOTSUPP; + if (strlen(param->ifa_entry.ifname)) { + dev = netif_port_get_by_name(param->ifa_entry.ifname); + if (!dev) { + RTE_LOG(WARNING, IFA, "%s: no such device: %s\n", + __func__, param->ifa_entry.ifname); + return EDPVS_NOTEXIST; + } + idev = dev_get_idev(dev); + if (!idev) + return EDPVS_RESOURCE; + } + + if (param->ifa_ops_flags & IFA_F_OPS_VERBOSE) + err = ifaddr_get_verbose(idev, &array, &len); + else if (param->ifa_ops_flags & IFA_F_OPS_STATS) + err = ifaddr_get_stats(idev, &array, &len); + else + err = ifaddr_get_basic(idev, &array, &len); - if (strlen(param->ifa_entry.ifname)) { - dev = netif_port_get_by_name(param->ifa_entry.ifname); + if (err != EDPVS_OK) { + RTE_LOG(WARNING, IFA, "%s: fail to get inet addresses -- %s!\n", + __func__, dpvs_strerror(err)); + return err; + } + + if (idev) + idev_put(idev); + + if (array) { + array->ops = INET_ADDR_GET; + array->ops_flags = param->ifa_ops_flags; + } + + *out = array; + *outsize = len; + break; + case SOCKOPT_GET_IFMADDR_SHOW: + if (!size || strlen(ifname) == 0) + return EDPVS_INVAL; + + dev = netif_port_get_by_name(ifname); if (!dev) { - RTE_LOG(WARNING, IFA, "%s: no such device: %s\n", - __func__, param->ifa_entry.ifname); + RTE_LOG(WARNING, IFA, "%s: no such device: %s\n", __func__, ifname); return EDPVS_NOTEXIST; } - idev = dev_get_idev(dev); if (!idev) return EDPVS_RESOURCE; - } - - if (param->ifa_ops_flags & IFA_F_OPS_VERBOSE) - err = ifaddr_get_verbose(idev, &array, &len); - else if (param->ifa_ops_flags & IFA_F_OPS_STATS) - err = ifaddr_get_stats(idev, &array, &len); - else - err = ifaddr_get_basic(idev, &array, &len); - if (err != EDPVS_OK) { - RTE_LOG(WARNING, IFA, "%s: fail to get inet addresses -- %s!\n", - __func__, dpvs_strerror(err)); - return err; - } + err = ifmaddr_fill_entries(idev, &marray, &len); + if (err != EDPVS_OK) { + RTE_LOG(WARNING, IFA, "%s: fail to get inet maddresses -- %s!\n", + __func__, dpvs_strerror(err)); + return err; + } - if (idev) idev_put(idev); - if (array) { - array->ops = INET_ADDR_GET; - array->ops_flags = param->ifa_ops_flags; + *out = marray; + *outsize = len; + break; + default: + *out = NULL; + *outsize = 0; + return EDPVS_NOTSUPP; } - - *out = array; - *outsize = len; - return EDPVS_OK; } @@ -2067,7 +2134,7 @@ static struct dpvs_sockopts ifa_sockopts = { .set_opt_max = SOCKOPT_SET_IFADDR_FLUSH, .set = ifa_sockopt_set, .get_opt_min = SOCKOPT_GET_IFADDR_SHOW, - .get_opt_max = SOCKOPT_GET_IFADDR_SHOW, + .get_opt_max = SOCKOPT_GET_IFMADDR_SHOW, .get = ifa_sockopt_get, }; diff --git a/src/kni.c b/src/kni.c index b6af78ceb..185371d42 100644 --- a/src/kni.c +++ b/src/kni.c @@ -34,7 +34,7 @@ #include "conf/common.h" #include "dpdk.h" #include "netif.h" -#include "netif_addr.h" +#include "conf/netif_addr.h" #include "ctrl.h" #include "kni.h" #include "vlan.h" diff --git a/src/netif.c b/src/netif.c index 4bc01a1bb..a40252f21 100644 --- a/src/netif.c +++ b/src/netif.c @@ -28,6 +28,7 @@ #include "conf/common.h" #include "netif.h" #include "netif_addr.h" +#include "conf/netif_addr.h" #include "vlan.h" #include "ctrl.h" #include "list.h" @@ -5180,6 +5181,15 @@ static int netif_sockopt_get(sockoptid_t opt, const void *in, size_t inlen, return EDPVS_NOTEXIST; ret = get_bond_status(port, out, outlen); break; + case SOCKOPT_NETIF_GET_MADDR: + if (!in) + return EDPVS_INVAL; + name = (char *)in; + port = netif_port_get_by_name(name); + if (!port) + return EDPVS_NOTEXIST; + ret = netif_get_multicast_addrs(port, out, outlen); + break; default: RTE_LOG(WARNING, NETIF, "[%s] invalid netif get cmd: %d\n", __func__, opt); diff --git a/src/netif_addr.c b/src/netif_addr.c index 425041ead..41bd0080f 100644 --- a/src/netif_addr.c +++ b/src/netif_addr.c @@ -23,6 +23,7 @@ */ #include "netif.h" #include "netif_addr.h" +#include "conf/netif_addr.h" #include "kni.h" int __netif_hw_addr_add(struct netif_hw_addr_list *list, @@ -279,16 +280,8 @@ int netif_mc_dump(struct netif_port *dev, uint16_t filter_flags, return err; } -/* only used in __netif_mc_dump_all */ -struct netif_hw_addr_entry { - struct rte_ether_addr addr; - uint32_t refcnt; - uint16_t flags; - uint16_t sync_cnt; -}; - static int __netif_mc_dump_all(struct netif_port *dev, uint16_t filter_flags, - struct netif_hw_addr_entry *addrs, size_t *naddr) + struct netif_hw_addr_entry *addrs, int *naddr) { struct netif_hw_addr *ha; int off = 0; @@ -297,7 +290,7 @@ static int __netif_mc_dump_all(struct netif_port *dev, uint16_t filter_flags, return EDPVS_NOROOM; list_for_each_entry(ha, &dev->mc.addrs, list) { - rte_ether_addr_copy(&ha->addr, &addrs[off].addr); + eth_addr_dump(&ha->addr, addrs[off].addr, sizeof(addrs[off].addr)); addrs[off].refcnt = rte_atomic32_read(&ha->refcnt); addrs[off].flags = ha->flags; addrs[off].sync_cnt = ha->sync_cnt; @@ -312,7 +305,7 @@ int __netif_mc_print(struct netif_port *dev, char *buf, int *len, int *pnaddr) { struct netif_hw_addr_entry addrs[NETIF_MAX_HWADDR]; - size_t naddr = NELEMS(addrs); + int naddr = NELEMS(addrs); int err, i; int strlen = 0; @@ -322,10 +315,8 @@ int __netif_mc_print(struct netif_port *dev, for (i = 0; i < naddr && *len > strlen; i++) { err = snprintf(buf + strlen, *len - strlen, - " link %02x:%02x:%02x:%02x:%02x:%02x %srefcnt %d, synced %d\n", - addrs[i].addr.addr_bytes[0], addrs[i].addr.addr_bytes[1], - addrs[i].addr.addr_bytes[2], addrs[i].addr.addr_bytes[3], - addrs[i].addr.addr_bytes[4], addrs[i].addr.addr_bytes[5], + " link %s %srefcnt %d, synced %d\n", + addrs[i].addr, addrs[i].flags & HW_ADDR_F_FROM_KNI ? "(kni) ": "", addrs[i].refcnt, addrs[i].sync_cnt); if (err < 0) { @@ -346,6 +337,33 @@ int __netif_mc_print(struct netif_port *dev, return err; } +int netif_get_multicast_addrs(struct netif_port *dev, void **out, size_t *outlen) +{ + int err; + size_t len; + struct netif_hw_addr_array *array; + + rte_rwlock_read_lock(&dev->dev_lock); + len = sizeof(*array) + dev->mc.count * sizeof(struct netif_hw_addr_entry); + array = rte_zmalloc(NULL, len, RTE_CACHE_LINE_SIZE); + if (unlikely(!array)) { + err = EDPVS_NOMEM; + } else { + array->count = dev->mc.count; + err = __netif_mc_dump_all(dev, 0, array->entries, &array->count); + } + rte_rwlock_read_unlock(&dev->dev_lock); + + if (err != EDPVS_OK) { + *out = NULL; + *outlen = 0; + } else { + *out = array; + *outlen = len; + } + return err; +} + int netif_mc_print(struct netif_port *dev, char *buf, int *len, int *pnaddr) { diff --git a/tools/dpip/Makefile b/tools/dpip/Makefile index e1bbe21e4..4dc648e38 100644 --- a/tools/dpip/Makefile +++ b/tools/dpip/Makefile @@ -39,7 +39,7 @@ DEFS = -D DPVS_MAX_LCORE=64 -D DPIP_VERSION=\"$(VERSION_STRING)\" CFLAGS += $(DEFS) -OBJS = ipset.o dpip.o utils.o route.o addr.o neigh.o link.o vlan.o \ +OBJS = ipset.o dpip.o utils.o route.o addr.o neigh.o link.o vlan.o maddr.o \ qsch.o cls.o tunnel.o ipset.o ipv6.o iftraf.o eal_mem.o flow.o \ ../../src/common.o ../keepalived/keepalived/check/sockopt.o diff --git a/tools/dpip/dpip.c b/tools/dpip/dpip.c index 596a534f5..a3b31f1c6 100644 --- a/tools/dpip/dpip.c +++ b/tools/dpip/dpip.c @@ -35,7 +35,7 @@ static void usage(void) " "DPIP_NAME" [OPTIONS] OBJECT { COMMAND | help }\n" "Parameters:\n" " OBJECT := { link | addr | route | neigh | vlan | tunnel | qsch | cls |\n" - " ipv6 | iftraf | eal-mem | ipset | flow }\n" + " ipv6 | iftraf | eal-mem | ipset | flow | maddr }\n" " COMMAND := { create | destroy | add | del | show (list) | set (change) |\n" " replace | flush | test | enable | disable }\n" "Options:\n" diff --git a/tools/dpip/link.c b/tools/dpip/link.c index f2d3f7798..fd3d5bd84 100644 --- a/tools/dpip/link.c +++ b/tools/dpip/link.c @@ -101,8 +101,10 @@ static inline int get_netif_port_list(void) if (g_nic_list) free(g_nic_list); g_nic_list = calloc(1, len); - if (!g_nic_list) + if (!g_nic_list) { + dpvs_sockopt_msg_free((void *)p_port_list); return EDPVS_NOMEM; + } memcpy(g_nic_list, p_port_list, len); @@ -501,6 +503,7 @@ static int dump_bond_status(char *name, int namelen) p_get->link_up_prop_delay); if (p_get->slave_nb > NETIF_MAX_BOND_SLAVES) { printf("too many slaves: %d\n", p_get->slave_nb); + dpvs_sockopt_msg_free(p_get); return EDPVS_INVAL; } for (i = 0; i < p_get->slave_nb; i++) { @@ -514,6 +517,7 @@ static int dump_bond_status(char *name, int namelen) } printf("\n"); + dpvs_sockopt_msg_free(p_get); return EDPVS_OK; } diff --git a/tools/dpip/maddr.c b/tools/dpip/maddr.c new file mode 100644 index 000000000..7bb82a544 --- /dev/null +++ b/tools/dpip/maddr.c @@ -0,0 +1,168 @@ +/* + * DPVS is a software load balancer (Virtual Server) based on DPDK. + * + * Copyright (C) 2021 iQIYI (www.iqiyi.com). + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include "dpip.h" +#include "conf/sockopts.h" +#include "conf/common.h" +#include "conf/netif.h" +#include "conf/netif_addr.h" +#include "conf/inetaddr.h" +#include "sockopt.h" + +static void maddr_help(void) +{ + fprintf(stderr, "Usage: dpip maddr show [dev STRING]\n"); +} + +static int maddr_parse_args(struct dpip_conf *conf, char *ifname, size_t len) +{ + while (conf->argc > 0) { + if (strcmp(conf->argv[0], "dev") == 0) { + NEXTARG_CHECK(conf, "dev"); + snprintf(ifname, len, "%s", conf->argv[0]); + } + NEXTARG(conf); + } + + if (conf->argc > 0) { + fprintf(stderr, "too many arguments\n"); + return -1; + } + + return 0; +} + +static int hwm_get_and_dump(const char *ifname, size_t len, bool verbose) +{ + int i, err; + size_t outlen; + struct netif_hw_addr_array *out; + struct netif_hw_addr_entry *entry; + + err = dpvs_getsockopt(SOCKOPT_NETIF_GET_MADDR, ifname, len, (void **)&out, &outlen); + if (err != EDPVS_OK || !out || !outlen) + return err; + + for (i = 0; i < out->count; i++) { + entry = &out->entries[i]; + if (verbose) { + printf("\tlink %s%s\t\trefcnt %u\t\tsync %d\n", + entry->addr, entry->flags & HW_ADDR_F_FROM_KNI ? " (+kni)" : "", + entry->refcnt, entry->sync_cnt); + } else { + printf("\tlink %s\n", entry->addr); + } + } + + dpvs_sockopt_msg_free(out); + return EDPVS_OK; +} + +static int ifm_get_and_dump(const char *ifname, size_t len, bool verbose) +{ + int i, err; + size_t outlen; + struct inet_maddr_array *out; + struct inet_maddr_entry *entry; + char ipbuf[64]; + + err = dpvs_getsockopt(SOCKOPT_GET_IFMADDR_SHOW, ifname, len, (void **)&out, &outlen); + if (err != EDPVS_OK || !out || !outlen) + return err; + + for (i = 0; i < out->nmaddr; i++) { + entry = &out->maddrs[i]; + if (verbose) { + printf("\t%5s %s\t\tflags 0x%x\t\trefcnt %u\n", entry->af == AF_INET6 ? "inet6" : "inet", + inet_ntop(entry->af, &entry->maddr, ipbuf, sizeof(ipbuf)) ? ipbuf : "unknown", + entry->flags, entry->refcnt); + } else { + printf("\t%5s %s\n", entry->af == AF_INET6 ? "inet6" : "inet", + inet_ntop(entry->af, &entry->maddr, ipbuf, sizeof(ipbuf)) ? ipbuf : "unknown"); + } + } + + dpvs_sockopt_msg_free(out); + return EDPVS_OK; +} + +static int maddr_get_and_dump(const char *ifname, size_t len, bool verbose) +{ + int err; + + err = hwm_get_and_dump(ifname, len, verbose); + if (err != EDPVS_OK) + return err; + + return ifm_get_and_dump(ifname, len, verbose); +} + +static int maddr_do_cmd(struct dpip_obj *obj, dpip_cmd_t cmd, + struct dpip_conf *conf) +{ + int i, err; + size_t len; + char ifname[IFNAMSIZ] = { 0 }; + netif_nic_list_get_t *ports; + + if (maddr_parse_args(conf, ifname, sizeof(ifname)) != 0) + return EDPVS_INVAL; + + switch (conf->cmd) { + case DPIP_CMD_SHOW: + if (strlen(ifname) > 0) { + printf("%s:\n", ifname); + return maddr_get_and_dump(ifname, sizeof(ifname), conf->verbose); + } + + /* list all devices */ + err = dpvs_getsockopt(SOCKOPT_NETIF_GET_PORT_LIST, NULL, 0, (void **)&ports, &len); + if (err != EDPVS_OK || !ports || !len) + return err; + for (i = 0; i < ports->nic_num && i < NETIF_MAX_PORTS; i++) { + printf("%d:\t%s\n", ports->idname[i].id + 1, ports->idname[i].name); + err = maddr_get_and_dump(ports->idname[i].name, + sizeof(ports->idname[i].name), conf->verbose); + if (err != EDPVS_OK) { + dpvs_sockopt_msg_free(ports); + return err; + } + } + dpvs_sockopt_msg_free(ports); + break; + default: + return EDPVS_NOTSUPP; + } + + return EDPVS_OK; +} + +struct dpip_obj dpip_maddr = { + .name = "maddr", + .help = maddr_help, + .do_cmd = maddr_do_cmd, +}; + +static void __init maddr_init(void) +{ + dpip_register_obj(&dpip_maddr); +} + +static void __exit maddr_exit(void) +{ + dpip_unregister_obj(&dpip_maddr); +}