diff --git a/libethernet/Makefile b/libethernet/Makefile index 545074d39e0a3e6c7b9d60bd9a8d83f7c730bb02..8bc1818f170d5a7709c173704380faa294af95bb 100644 --- a/libethernet/Makefile +++ b/libethernet/Makefile @@ -12,20 +12,27 @@ objs_lib = ethernet.o ifeq ($(PLATFORM),BROADCOM) objs_lib += bcm/bcm.o -else ifeq ($(PLATFORM),MEDIATEK) -objs_lib += ethsw.o -XXFLAGS += -Wl,-whole-archive -lsw -Wl,-no-whole-archive -else ifeq ($(PLATFORM),ECONET) +endif + +ifeq ($(PLATFORM),TEST) +CFLAGS += -DIOPSYS_TEST -I/usr/include/libnl3 +LIBETH_CFLAGS += $(DIAG_CFLAGS) -Werror +objs_lib += test_stub/stub.o +endif + +ifeq ($(PLATFORM),ECONET) CFLAGS += -Itest_stub LIBETH_CFLAGS += $(DIAG_CFLAGS) -Werror objs_lib += econet/econet.o econet/ecnt_prvt.o LIBS += -lapi_lib_switchmgr -else ifeq ($(PLATFORM),IPQ95XX) +endif + +ifeq ($(PLATFORM),IPQ95XX) objs_lib += ipq95xx/ipq95xx.o -else ifeq ($(PLATFORM),TEST) -CFLAGS += -DIOPSYS_TEST -I/usr/include/libnl3 -LIBETH_CFLAGS += $(DIAG_CFLAGS) -Werror -objs_lib += test_stub/stub.o +endif + +ifeq ($(PLATFORM),LINUX) +objs_lib += linux/linux_eth.o endif all: libethernet.so @@ -38,5 +45,4 @@ libethernet.so: $(objs_lib) -shared -o $@ $^ $(XXFLAGS) $(LIBS) clean: - rm -f bcm/*.o rm -f *.o *.so diff --git a/libethernet/ethernet.c b/libethernet/ethernet.c index 8a0b199b3eeee1493a6f53c5e721ddd10e711e58..67aeeb7d5315618598cbfeb87af51af32ff22946 100644 --- a/libethernet/ethernet.c +++ b/libethernet/ethernet.c @@ -38,20 +38,6 @@ #include "ethernet.h" -#if defined(IOPSYS_BROADCOM) -extern const struct eth_ops bcm_eth_ops; -#elif defined(IOPSYS_TEST) -extern const struct eth_ops test_eth_ops; -#elif defined(IOPSYS_ECONET) -extern const struct eth_ops econet_gen_eth_ops; -extern const struct eth_ops econet_nas_wan_eth_ops; -extern const struct eth_ops econet_ae_wan_eth_ops; -#elif defined(IPQ95XX) -extern const struct eth_ops ipq95xx_eth_ops; -#else -extern const struct eth_ops ethsw_ops; -#endif - const struct eth_ops *eth_ops[] = { #if defined(IOPSYS_BROADCOM) &bcm_eth_ops, @@ -63,6 +49,8 @@ const struct eth_ops *eth_ops[] = { &econet_ae_wan_eth_ops, #elif defined(IPQ95XX) &ipq95xx_eth_ops, +#elif defined(IOPSYS_LINUX) + &linux_eth_ops, #else ðsw_ops, /* FIXME */ #endif diff --git a/libethernet/ethernet.h b/libethernet/ethernet.h index 540aa0a734f44aa248e10c8deab70aa130eff5e7..96303a5055486151c38aec6877ed126425cf4c37 100644 --- a/libethernet/ethernet.h +++ b/libethernet/ethernet.h @@ -35,6 +35,23 @@ extern "C" { #define libethernet_info(...) pr_info("libethernet: " __VA_ARGS__) #define libethernet_dbg(...) pr_debug("libethernet: " __VA_ARGS__) +#if defined(IOPSYS_BROADCOM) +extern const struct eth_ops bcm_eth_ops; +#elif defined(IOPSYS_TEST) +extern const struct eth_ops test_eth_ops; +#elif defined(IOPSYS_ECONET) +extern const struct eth_ops econet_gen_eth_ops; +extern const struct eth_ops econet_nas_wan_eth_ops; +extern const struct eth_ops econet_ae_wan_eth_ops; +#elif defined(IPQ95XX) +extern const struct eth_ops ipq95xx_eth_ops; +#elif defined(IOPSYS_LINUX) +extern const struct eth_ops linux_eth_ops; +#else +extern const struct eth_ops ethsw_ops; +#endif + + /* enum eth_duplex - duplex modes */ enum eth_duplex { AUTO_DUPLEX, diff --git a/libethernet/linux/linux_eth.c b/libethernet/linux/linux_eth.c new file mode 100644 index 0000000000000000000000000000000000000000..918765d1bad94ad45a711de4325eff0fd8c1e1c3 --- /dev/null +++ b/libethernet/linux/linux_eth.c @@ -0,0 +1,248 @@ +/* + * linux_eth.c - Linux based statistics collection ubus methods. + * + * Copyright (C) 2023 iopsys Software Solutions AB. All rights reserved. + * + * Author: padmalochan.mohapatra@iopsys.eu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <ctype.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> +#include <time.h> +#include <syslog.h> +#include <unistd.h> +#include <stdlib.h> +#include <linux/ethtool.h> +#include <netlink/genl/genl.h> +#include <netlink/cache.h> +#include <netlink/utils.h> +#include <netlink/object.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <netlink/netlink.h> +#include <netlink/route/rtnl.h> +#include <netlink/route/link.h> +#include <netlink/genl/ctrl.h> +#include <netlink/netlink.h> +#include <netlink/route/rtnl.h> +#include <netlink/route/link.h> +#include <linux/sockios.h> +#include "easy.h" +#include "if_utils.h" +#include "../ethernet.h" + +#include "linux_eth.h" + +/************************************************************ + * Open a netlink socket connectivity with NETLINK_ROUTE + * to read the stats using designated API. + ************************************************************ + */ +static int if_openlnk(const char *ifname, struct nl_sock **s, + struct rtnl_link **l) +{ + struct rtnl_link *link; + struct nl_sock *sk; + int ret = 0; + + sk = nl_socket_alloc(); + if (sk == NULL) { + ret = -errno; + return ret; + } + + if (nl_connect(sk, NETLINK_ROUTE) < 0) { + syslog(LOG_ERR, "%s(%d) nl socket connection failed.", + __FUNCTION__, __LINE__); + ret = -1; + goto out; + } + + if (rtnl_link_get_kernel(sk, 0, ifname, &link) < 0) { + ret = -1; + goto out; + } + + *l = link; + *s = sk; + return 0; + +out: + nl_socket_free(sk); + return ret; +} + +/************************************************************ + * Close the socket and free the allocated nl objects. + ************************************************************ + */ + +static int if_closelnk(struct nl_sock *s, struct rtnl_link *l) +{ + rtnl_link_put(l); + nl_socket_free(s); + + return 0; +} + +static int get_ifstats(const char *ifname, struct eth_stats *s) +{ + struct rtnl_link *link; + struct nl_sock *sk; + int ret = 0; + + if (!s) + return -1; + + ret = if_openlnk(ifname, &sk, &link); + if (ret) + return -1; + + s->tx_bytes = rtnl_link_get_stat(link, RTNL_LINK_TX_BYTES); + s->tx_packets = rtnl_link_get_stat(link, RTNL_LINK_TX_PACKETS); + s->tx_errors = rtnl_link_get_stat(link, RTNL_LINK_TX_ERRORS); +/* Some counters set to zero, and will be populated cross referring + * the rmon stats structure. + */ + s->tx_ucast_packets = 0; + s->tx_mcast_packets = 0; + s->tx_bcast_packets = 0; + s->tx_discard_packets = 0; + s->rx_bytes = rtnl_link_get_stat(link, RTNL_LINK_RX_BYTES); + s->rx_packets = 0; + s->rx_errors = rtnl_link_get_stat(link, RTNL_LINK_RX_ERRORS); + s->rx_ucast_packets = 0; + s->rx_mcast_packets = rtnl_link_get_stat(link, RTNL_LINK_MULTICAST); + s->rx_bcast_packets = 0; + s->rx_discard_packets = 0; + s->rx_unknown_packets = 0; + + if_closelnk(sk, link); + + return 0; +} + +int get_ethtool_stats(const char *ifname) +{ + int ret = 0; + + stat.cmd = ETHTOOL_GSTATS; + if (0 != eth_ioctl(ifname, SIOCETHTOOL, &stat, sizeof(struct ethnic_stats))) + ret = -1; + + if (ret) + syslog(LOG_ERR, "%s(%d) nic_stats collection failed.", __FUNCTION__, __LINE__); + + return ret; +} + +int get_stats(const char *ifname) +{ + memset(&ifstats, 0, sizeof(struct eth_stats)); + memset(&stat, 0, sizeof(struct ethnic_stats)); + + syslog(LOG_INFO, "%s(%d): ifname is %s", __FUNCTION__, __LINE__, ifname); + + if (get_ifstats(ifname, &ifstats) < 0) + return -1; + if (get_ethtool_stats(ifname) < 0) + return -1; + + return 0; +} + + +int linux_eth_get_stats(const char *ifname, struct eth_stats *s) +{ + if (!s) + return -1; + + if (get_stats(ifname) < 0) + return -1; + + s->tx_bytes = ifstats.tx_bytes; + s->tx_packets = ifstats.tx_packets; + s->tx_errors = ifstats.tx_errors; + s->tx_ucast_packets = stat.data[TxUnicast]; + s->tx_mcast_packets = ifstats.tx_mcast_packets; + s->tx_bcast_packets = stat.data[TxBroadcast]; + s->tx_discard_packets = stat.data[TxDrop]; + s->rx_bytes = ifstats.rx_bytes; + s->rx_packets = stat.data[rx_packets]; + s->rx_errors = ifstats.rx_errors; + s->rx_ucast_packets = stat.data[RxUnicast]; + s->rx_mcast_packets = stat.data[RxMulticast]; + s->rx_bcast_packets = stat.data[RxBroadcast]; + s->rx_discard_packets = stat.data[RxDrop]; +/*FIXME: Doesn't exist a way to get the counter rx_unknown_packets.*/ + s->rx_unknown_packets = 0; + + return 0; +} + +int linux_eth_get_rmon_stats(const char *ifname, struct eth_rmon_stats *rmon) +{ + int ret = 0; + + if (get_stats(ifname) < 0) + ret = -1; + rmon->tx.packets = stat.data[tx_packets]; + rmon->tx.bytes = stat.data[tx_bytes]; + rmon->tx.crc_err_packets = stat.data[TxCrcErr]; +/* These two counters are marked zero because they dont + * hold much relevancy to Customer + */ + rmon->tx.under_sz_packets = 0; + rmon->tx.over_sz_packets = 0; + rmon->tx.packets_64bytes = stat.data[TxPktSz64]; + rmon->tx.packets_65to127bytes = stat.data[TxPktSz65To127]; + rmon->tx.packets_128to255bytes = stat.data[TxPktSz128To255]; + rmon->tx.packets_256to511bytes = stat.data[TxPktSz256To511]; + rmon->tx.packets_512to1023bytes = stat.data[TxPktSz512To1023]; + rmon->tx.packets_1024to1518bytes = stat.data[Tx1024ToMax]; + + rmon->rx.bytes = stat.data[RxBytes]; + rmon->rx.packets = stat.data[rx_packets]; + rmon->rx.bcast_packets = stat.data[RxBroadcast]; + rmon->rx.mcast_packets = stat.data[RxMulticast]; + rmon->rx.crc_err_packets = stat.data[RxCrcErr]; + rmon->rx.under_sz_packets = stat.data[RxUnderSizeErr]; + rmon->rx.over_sz_packets = stat.data[RxOverSzErr]; + rmon->rx.packets_64bytes = stat.data[RxPktSz64]; + rmon->rx.packets_65to127bytes = stat.data[RxPktSz65To127]; + rmon->rx.packets_128to255bytes = stat.data[RxPktSz128To255]; + rmon->rx.packets_256to511bytes = stat.data[RxPktSz256To511]; + rmon->rx.packets_512to1023bytes = stat.data[RxPktSz512To1023]; + rmon->rx.packets_1024to1518bytes = stat.data[RxPktSz1024ToMax]; + + return ret; +} + +const struct eth_ops linux_eth_ops = { + .ifname = "lan", + .get_stats = linux_eth_get_stats, + .get_rmon_stats = linux_eth_get_rmon_stats, +}; diff --git a/libethernet/linux/linux_eth.h b/libethernet/linux/linux_eth.h new file mode 100644 index 0000000000000000000000000000000000000000..e625e3d193f439e31f0a7544963b1a38fd86f9e6 --- /dev/null +++ b/libethernet/linux/linux_eth.h @@ -0,0 +1,72 @@ +#ifndef LINUX +#define LINUX + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + tx_packets = 0, + tx_bytes, + rx_packets, + rx_bytes, + TxDrop, + TxCrcErr, + TxUnicast, + TxMulticast, + TxBroadcast, + TxCollision, + TxSingleCollision, + TxMultipleCollision, + TxDeferred, + TxLateCollision, + TxExcessiveCollistion, + TxPause, + TxPktSz64, + TxPktSz65To127, + TxPktSz128To255, + TxPktSz256To511, + TxPktSz512To1023, + Tx1024ToMax, + TxBytes, + RxDrop, + RxFiltering, + RxUnicast, + RxMulticast, + RxBroadcast, + RxAlignErr, + RxCrcErr, + RxUnderSizeErr, + RxFragErr, + RxOverSzErr, + RxJabberErr, + RxPause, + RxPktSz64, + RxPktSz65To127, + RxPktSz128To255, + RxPktSz256To511, + RxPktSz512To1023, + RxPktSz1024ToMax, + RxBytes, + RxCtrlDrop, + RxIngressDrop, + RxArlDrop, + STATS_END, +} idx; + +static struct ethnic_stats { + __u32 cmd; + __u32 n_stats; + __u64 data[STATS_END]; +} stat; + +static struct eth_stats ifstats; + +int linux_eth_get_stats(const char *ifname, struct eth_stats *s); +int linux_eth_get_rmon_stats(const char *ifname, struct eth_rmon_stats *rmon); + +#ifdef __cplusplus +} +#endif + +#endif /* LINUX */