| /* |
| * (C) Copyright 2015 Google, Inc. |
| * All rights reserved. |
| * |
| */ |
| |
| #include <arpa/inet.h> |
| #include <errno.h> |
| #include <linux/if_packet.h> |
| #include <linux/types.h> |
| #include <net/if.h> |
| #include <netdb.h> |
| #include <netinet/ether.h> |
| #include <netinet/in.h> |
| #include <signal.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <unistd.h> |
| #include "../common/util.h" |
| #include "common.h" |
| #include "gpio.h" |
| #include "mdio.h" |
| |
| #define DEFAULT_TST_IF "lan0" |
| #define LAN_PORT_NAME "lan0" |
| #define WAN_PORT_NAME "wan0" |
| #define MAX_NET_IF 2 |
| #define BUF_SIZ 1536 |
| #define ETH_TEST_MAX_CMD 4096 |
| #define ETH_TEST_MAX_RSP 4096 |
| #define ETH_TRAFFIC_PORT "wan0" |
| #define ETH_TRAFFIC_DST_PORT "lan0" |
| #define ETH_TRAFFIC_REPORT_PERIOD 60 |
| #define ETH_TRAFFIC_MAX_REPORT_PERIOD 300 |
| #define ETH_TRAFFIC_TEST_PERIOD_SYMBOL "-p" |
| // 100 Mb/s |
| #define ETH_TRAFFIC_PER_PERIOD_MAX \ |
| (((unsigned int)ETH_TRAFFIC_MAX_REPORT_PERIOD) * ((unsigned int)13107200)) |
| |
| #define SERVER_PORT 8888 |
| #define MAX_CMD_SIZE 256 |
| #define SCAN_CMD_FORMAT "%256s" |
| #define MAX_INT 0x7FFFFFFF |
| |
| #define ETH_SEND_DELAY_IN_USEC 1000 |
| #define ETH_MAX_LAN_PORTS 2 |
| #define ETH_WAIT_AFTER_LOOPBACK_SET 5 |
| #define ETH_PKTS_SENT_BEFORE_WAIT 0xFF |
| #define ETH_PKTS_LEN_DEFAULT 128 |
| #define ETH_BUFFER_SIZE (ETH_PKTS_SENT_BEFORE_WAIT * ETH_PKTS_LEN_DEFAULT) |
| #define ETH_LOOPBACK_PASS_FACTOR 0.8 // 80% |
| #define ETH_TEST_FLUSH_NUM 5 |
| |
| #define ETH_RX_NAME "RX" |
| #define ETH_TX_NAME "TX" |
| #define ETH_PACKETS_NAME "packets:" |
| #define ETH_ERRORS_NAME "errors:" |
| #define ETH_BYTES_NAME "bytes:" |
| #define ONE_MEG (1024 * 1024) |
| |
| #define ETH_DEBUG_PORT_ADDR_REG 0x1D |
| #define ETH_DEBUG_PORT_DATA_REG 0x1E |
| #define ETH_EXT_LPBK_PORT_ADDR_OFFSET 0xB |
| #define ETH_EXT_LPBK_PORT_SET_DATA 0x3C40 |
| #define ETH_EXT_LPBK_PORT_CLEAR_DATA 0xBC00 |
| #define ETH_STAT_CLEAR_CMD "ifstat > /dev/null" |
| #define ETH_STAT_CMD "ifstat %s | sed '1,3d;5d'" |
| #define ETH_STAT_RX_POS 5 |
| #define ETH_STAT_TX_POS 7 |
| #define ETH_STAT_WAIT_PERIOD 1 // sec |
| #define ETH_STAT_PERCENT_MARGIN 95 |
| |
| // WindCharger external loopback is set and cleared via Port Debug Registers. |
| // Debug port address offset register is 0x1D and RW port is 0x1E. It needs to |
| // First set the address offset, then read/write the data RW port. The |
| // following are functions to set up/take down external loopback. |
| |
| int eth_set_debug_reg(char *if_name, unsigned short addr, unsigned short data) { |
| int rc; |
| |
| mdio_init(); |
| mdio_set_interface(if_name); |
| rc = mdio_write(addr, data); |
| mdio_done(); |
| |
| return rc; |
| } |
| |
| int eth_external_loopback(char *if_name, bool set_not_clear) { |
| unsigned short data = ETH_EXT_LPBK_PORT_SET_DATA; |
| int rc; |
| |
| if (!set_not_clear) data = ETH_EXT_LPBK_PORT_CLEAR_DATA; |
| |
| rc = eth_set_debug_reg(if_name, ETH_DEBUG_PORT_ADDR_REG, |
| ETH_EXT_LPBK_PORT_ADDR_OFFSET); |
| if (rc < 0) return rc; |
| rc = eth_set_debug_reg(if_name, ETH_DEBUG_PORT_DATA_REG, data); |
| return rc; |
| } |
| |
| void send_mac_pkt(char *if_name, char *out_name, unsigned int xfer_len, |
| unsigned int xfer_wait, int n, |
| const unsigned char *dst_mac1) { |
| int sockfd, i; |
| struct ifreq if_idx; |
| struct ifreq if_mac, out_mac; |
| int tx_len = 0; |
| char sendbuf[BUF_SIZ]; |
| struct ether_header *eh = (struct ether_header *)sendbuf; |
| struct sockaddr_ll socket_address; |
| unsigned char dst_mac[6] = {0, 0, 0, 0, 0, 0}; |
| |
| /* Open RAW socket to send on */ |
| if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1) { |
| perror("socket"); |
| } |
| |
| /* Get the index of the interface to send on */ |
| memset(&if_idx, 0, sizeof(if_idx)); |
| safe_strncpy(if_idx.ifr_name, if_name, IFNAMSIZ - 1); |
| if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) { |
| perror("SIOCGIFINDEX"); |
| } |
| /* Get the MAC address of the interface to send on */ |
| memset(&out_mac, 0, sizeof(out_mac)); |
| if (out_name != NULL) { |
| safe_strncpy(out_mac.ifr_name, out_name, IFNAMSIZ - 1); |
| if (ioctl(sockfd, SIOCGIFHWADDR, &out_mac) < 0) { |
| perror("out SIOCGIFHWADDR"); |
| } |
| } |
| memset(&if_mac, 0, sizeof(if_mac)); |
| safe_strncpy(if_mac.ifr_name, if_name, IFNAMSIZ - 1); |
| if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0) { |
| perror("SIOCGIFHWADDR"); |
| } |
| if (out_name != NULL) { |
| dst_mac[0] = ((uint8_t *)&out_mac.ifr_hwaddr.sa_data)[0]; |
| dst_mac[1] = ((uint8_t *)&out_mac.ifr_hwaddr.sa_data)[1]; |
| dst_mac[2] = ((uint8_t *)&out_mac.ifr_hwaddr.sa_data)[2]; |
| dst_mac[3] = ((uint8_t *)&out_mac.ifr_hwaddr.sa_data)[3]; |
| dst_mac[4] = ((uint8_t *)&out_mac.ifr_hwaddr.sa_data)[4]; |
| dst_mac[5] = ((uint8_t *)&out_mac.ifr_hwaddr.sa_data)[5]; |
| } else if (dst_mac1 != NULL) { |
| dst_mac[0] = dst_mac1[0]; |
| dst_mac[1] = dst_mac1[1]; |
| dst_mac[2] = dst_mac1[2]; |
| dst_mac[3] = dst_mac1[3]; |
| dst_mac[4] = dst_mac1[4]; |
| dst_mac[5] = dst_mac1[5]; |
| } else { |
| printf("Invalid out_name and dst_mac.\n"); |
| return; |
| } |
| |
| /* Construct the Ethernet header */ |
| // memset(sendbuf, 0, BUF_SIZ); |
| for (i = 0; i < BUF_SIZ; ++i) { |
| sendbuf[i] = 0xA5; |
| } |
| /* Ethernet header */ |
| eh->ether_shost[0] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[0]; |
| eh->ether_shost[1] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[1]; |
| eh->ether_shost[2] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[2]; |
| eh->ether_shost[3] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[3]; |
| eh->ether_shost[4] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[4]; |
| eh->ether_shost[5] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[5]; |
| eh->ether_dhost[0] = dst_mac[0]; |
| eh->ether_dhost[1] = dst_mac[1]; |
| eh->ether_dhost[2] = dst_mac[2]; |
| eh->ether_dhost[3] = dst_mac[3]; |
| eh->ether_dhost[4] = dst_mac[4]; |
| eh->ether_dhost[5] = dst_mac[5]; |
| /* Ethertype field */ |
| eh->ether_type = htons(ETH_P_IP); |
| tx_len += sizeof(struct ether_header); |
| |
| /* Packet data */ |
| sendbuf[tx_len++] = 0xde; |
| sendbuf[tx_len++] = 0xad; |
| sendbuf[tx_len++] = 0xbe; |
| sendbuf[tx_len++] = 0xef; |
| |
| /* Index of the network device */ |
| socket_address.sll_ifindex = if_idx.ifr_ifindex; |
| /* Address length*/ |
| socket_address.sll_halen = ETH_ALEN; |
| /* Destination MAC */ |
| socket_address.sll_addr[0] = dst_mac[0]; |
| socket_address.sll_addr[1] = dst_mac[1]; |
| socket_address.sll_addr[2] = dst_mac[2]; |
| socket_address.sll_addr[3] = dst_mac[3]; |
| socket_address.sll_addr[4] = dst_mac[4]; |
| socket_address.sll_addr[5] = dst_mac[5]; |
| |
| /* Send packet */ |
| if (n < 0) { |
| while (1) { |
| if (sendto(sockfd, sendbuf, xfer_len, 0, |
| (struct sockaddr *)&socket_address, |
| sizeof(struct sockaddr_ll)) < 0) { |
| printf("Send failed at msg %d\n", i); |
| break; |
| } |
| if (xfer_wait > 0) { |
| if ((i & ETH_PKTS_SENT_BEFORE_WAIT) == 0) { |
| usleep(xfer_wait); |
| } |
| } |
| } |
| } else { |
| for (i = 0; i < n; ++i) { |
| if (sendto(sockfd, sendbuf, xfer_len, 0, |
| (struct sockaddr *)&socket_address, |
| sizeof(struct sockaddr_ll)) < 0) { |
| printf("Send failed at msg %d\n", i); |
| break; |
| } |
| if (xfer_wait > 0) { |
| if ((i & ETH_PKTS_SENT_BEFORE_WAIT) == 0) { |
| usleep(xfer_wait); |
| } |
| } |
| } |
| } |
| close(sockfd); |
| } |
| |
| static void send_ip_usage(void) { |
| printf("send_ip <address> <port> <num>\n"); |
| printf("Example:\n"); |
| printf("send_ip 192.168.1.1 10000 1\n"); |
| printf("send 1 msg to ip address 192.168.1.1 port 10000\n"); |
| } |
| |
| int send_ip(int argc, char *argv[]) { |
| int sockfd, portno, i, n; |
| struct sockaddr_in serv_addr; |
| char *my_msg = "This is a test"; |
| unsigned int ipaddr[4]; |
| uint32_t ia; |
| |
| if (argc != 4) { |
| send_ip_usage(); |
| return -1; |
| } |
| |
| sscanf(argv[1], "%u.%u.%u.%u", &(ipaddr[0]), &(ipaddr[1]), &(ipaddr[2]), |
| &(ipaddr[3])); |
| ia = (ipaddr[3] << 24) | (ipaddr[2] << 16) | (ipaddr[1] << 8) | ipaddr[0]; |
| portno = strtoul(argv[2], NULL, 0); |
| n = strtoul(argv[3], NULL, 0); |
| serv_addr.sin_family = AF_INET; |
| serv_addr.sin_port = portno; |
| serv_addr.sin_addr.s_addr = (__be32)(ia); |
| |
| sockfd = socket(AF_INET, SOCK_DGRAM, 0); |
| |
| if (sockfd < 0) { |
| printf("Cannot create socket. sockfd = %d\n", sockfd); |
| return -1; |
| } |
| |
| for (i = 0; i < n; ++i) { |
| if (sendto(sockfd, my_msg, strlen(my_msg), 0, (struct sockaddr *)&serv_addr, |
| sizeof(serv_addr)) < 0) { |
| printf("Cannot send msg to socket %s\n", my_msg); |
| return -1; |
| } |
| } |
| |
| printf("send %d packets to %u.%u.%u.%u:0x%08x port %d\n", n, ipaddr[0], |
| ipaddr[1], ipaddr[2], ipaddr[3], serv_addr.sin_addr.s_addr, portno); |
| |
| return 0; |
| } |
| |
| /* If extra is not NULL, rsp is returned as the string followed extra */ |
| int scan_command(char *command, char *rsp, char *extra) { |
| FILE *fp; |
| fp = popen(command, "r"); |
| if (fp != NULL) { |
| if (extra != NULL) { |
| while (fscanf(fp, "%s", rsp) != EOF) { |
| if (!strcmp(rsp, extra)) { |
| if (fscanf(fp, "%s", rsp) <= 0) |
| return -1; |
| else |
| return 0; |
| } |
| } |
| } else { |
| fscanf(fp, SCAN_CMD_FORMAT, rsp); |
| } |
| pclose(fp); |
| } else { |
| return -1; |
| } |
| return 0; |
| } |
| |
| int net_stat(unsigned int *rx_bytes, unsigned int *tx_bytes, char *name) { |
| static const char *kIfName[MAX_NET_IF] = {LAN_PORT_NAME, WAN_PORT_NAME}; |
| static unsigned int tx_stat[MAX_NET_IF] = {0, 0}; |
| static unsigned int rx_stat[MAX_NET_IF] = {0, 0}; |
| char command[MAX_CMD_SIZE], rsp[MAX_CMD_SIZE]; |
| unsigned int index, tmp; |
| |
| for (index = 0; index < MAX_NET_IF; ++index) { |
| if (strcmp(name, kIfName[index]) == 0) { |
| break; |
| } |
| } |
| if (index >= MAX_NET_IF) return -1; |
| |
| snprintf(command, sizeof(command), |
| "cat /sys/class/net/%s/statistics/tx_bytes", name); |
| if (scan_command(command, rsp, NULL) == 0) *tx_bytes = strtoul(rsp, NULL, 10); |
| |
| snprintf(command, sizeof(command), |
| "cat /sys/class/net/%s/statistics/rx_bytes", name); |
| if (scan_command(command, rsp, NULL) == 0) *rx_bytes = strtoul(rsp, NULL, 10); |
| |
| if (*tx_bytes >= tx_stat[index]) { |
| *tx_bytes -= tx_stat[index]; |
| tx_stat[index] += *tx_bytes; |
| } else { |
| tmp = *tx_bytes; |
| // tx_bytes is uint. It will continue to increment till wrap around |
| // When it wraps around, the current value will be less than the |
| // previous one. That is why this logic kicked in. |
| *tx_bytes += (0xffffffff - tx_stat[index]); |
| tx_stat[index] = tmp; |
| } |
| |
| if (*rx_bytes >= rx_stat[index]) { |
| *rx_bytes -= rx_stat[index]; |
| rx_stat[index] += *rx_bytes; |
| } else { |
| tmp = *rx_bytes; |
| // rx_bytes is uint. It will continue to increment till wrap around |
| // When it wraps around, the current value will be less than the |
| // previous one. That is why this logic kicked in. |
| *rx_bytes += (0xffffffff - rx_stat[index]); |
| rx_stat[index] = tmp; |
| } |
| return 0; |
| } |
| |
| // Return 0 if lost carrier. Otherwise, 1 |
| int get_carrier_state(char *name) { |
| char command[MAX_CMD_SIZE], rsp[MAX_CMD_SIZE]; |
| |
| snprintf(command, sizeof(command), "cat /sys/class/net/%s/carrier", name); |
| if (scan_command(command, rsp, NULL) == 0) { |
| if (strcmp(rsp, "0") != 0) return 1; |
| } |
| return 0; |
| } |
| |
| // This is the same as sleep but monitor the link carrier every second |
| // Return true if the carrier is good every second. Otherwise false |
| bool sleep_and_check_carrier(int duration, char *if_name) { |
| bool good_carrier = true; |
| int i; |
| for (i = 0; i < duration; ++i) { |
| if (get_carrier_state(if_name) == 0) good_carrier = false; |
| sleep(1); |
| } |
| return good_carrier; |
| } |
| |
| int get_if_ip(char *name, unsigned int *ip) { |
| char command[ETH_TEST_MAX_CMD], rsp[ETH_TEST_MAX_RSP]; |
| bool found = false; |
| |
| snprintf(command, sizeof(command), "ip addr show %s", name); |
| if (scan_command(command, rsp, "inet") == 0) { |
| if (sscanf(rsp, "%u.%u.%u.%u", ip, (ip + 1), (ip + 2), (ip + 3)) <= 0) { |
| return -1; |
| } |
| found = true; |
| } |
| |
| if (!found) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static void phy_read_usage(void) { |
| printf("phy_read <ifname> <reg>\n"); |
| printf("Example:\n"); |
| printf("phy_read lan0 2\n"); |
| } |
| |
| int phy_read(int argc, char *argv[]) { |
| int reg, val; |
| |
| if (argc != 3) { |
| phy_read_usage(); |
| return -1; |
| } |
| |
| reg = strtol(argv[2], NULL, 0); |
| mdio_init(); |
| mdio_set_interface(argv[1]); |
| val = mdio_read(reg); |
| mdio_done(); |
| |
| if (val < 0) { |
| printf("Read PHY %s reg %d failed\n", argv[1], reg); |
| return -1; |
| } |
| printf("PHY %s Reg %d = 0x%x\n", argv[1], reg, val); |
| return 0; |
| } |
| |
| static void phy_write_usage(void) { |
| printf("phy_write <ifname> <reg> <val>\n"); |
| printf("Example:\n"); |
| printf("phy_write lan0 22 0x6\n"); |
| } |
| |
| int phy_write(int argc, char *argv[]) { |
| int reg, val, rc; |
| |
| if (argc != 4) { |
| phy_write_usage(); |
| return -1; |
| } |
| |
| reg = strtol(argv[2], NULL, 0); |
| val = strtol(argv[3], NULL, 16); |
| mdio_init(); |
| mdio_set_interface(argv[1]); |
| rc = mdio_write(reg, val); |
| mdio_done(); |
| |
| if (rc < 0) { |
| printf("Write PHY %s reg %d val 0x%x failed\n", argv[1], reg, val); |
| return -1; |
| } |
| printf("PHY %s Reg %d = 0x%x\n", argv[1], reg, val); |
| return 0; |
| } |
| |
| static void send_if_usage(void) { |
| printf("send_if <source if> <num> [-t <delay between pkts send>]\n"); |
| printf("Example:\n"); |
| printf("send_if lan0 100\n"); |
| printf("send 100 msg out of lan0\n"); |
| } |
| |
| int send_if(int argc, char *argv[]) { |
| int n; |
| char if_name[IFNAMSIZ]; |
| unsigned int xfer_wait = ETH_SEND_DELAY_IN_USEC; |
| unsigned char dst_mac[6] = {0, 0, 0, 0, 0, 0}; |
| |
| /* Get interface name */ |
| if (argc == 5) { |
| if (strcmp(argv[3], "-t") == 0) { |
| xfer_wait = strtoul(argv[4], NULL, 10); |
| } else { |
| send_if_usage(); |
| return -1; |
| } |
| } else if (argc != 3) { |
| send_if_usage(); |
| return -1; |
| } |
| |
| strcpy(if_name, argv[1]); |
| n = strtol(argv[2], NULL, 10); |
| |
| send_mac_pkt(if_name, NULL, BUF_SIZ, xfer_wait, n, dst_mac); |
| |
| printf("Sent %d pkt of size %d from %s to %s\n", n, BUF_SIZ, argv[1], |
| argv[2]); |
| |
| return 0; |
| } |
| |
| static void send_if_to_if_usage(void) { |
| printf( |
| "send_if_to_if <src if> <dest if> <secs> [-b <pkt byte size (max %d)>] " |
| "[-t <time delay in micro-second between pkt send>]\n", |
| BUF_SIZ); |
| printf("Example:\n"); |
| printf("send_if_to_if wan0 lan0 10\n"); |
| printf("send 10 seconds from interface wan0 to lan0\n"); |
| printf("send_if_to_if wan0 lan0 10 -b 256 -t 250\n"); |
| printf( |
| "send from interface wan0 to lan0 for 10 seconds of size 256 bytes and" |
| " 250 us delay\n"); |
| } |
| |
| int send_if_to_if(int argc, char *argv[]) { |
| int n; |
| unsigned int xfer_len = ETH_PKTS_LEN_DEFAULT, xfer_wait = 0; |
| char src_name[IFNAMSIZ], dst_name[IFNAMSIZ]; |
| unsigned int src_rx_bytes, src_tx_bytes, dst_rx_bytes, dst_tx_bytes; |
| int pid; |
| |
| /* Get interface name */ |
| if ((argc != 4) && (argc != 6) && (argc != 8)) { |
| send_if_to_if_usage(); |
| return -1; |
| } |
| |
| strcpy(src_name, argv[1]); |
| strcpy(dst_name, argv[2]); |
| |
| if (argc >= 6) { |
| unsigned int tmp; |
| tmp = strtoul(argv[5], NULL, 10); |
| if (strcmp(argv[4], "-b") == 0) { |
| if (tmp > BUF_SIZ) { |
| send_if_to_if_usage(); |
| return -1; |
| } |
| xfer_len = tmp; |
| |
| if (argc == 8) { |
| if (strcmp(argv[6], "-t") == 0) { |
| xfer_wait = strtoul(argv[7], NULL, 10); |
| } else { |
| send_if_to_if_usage(); |
| return -1; |
| } |
| } |
| } else if (strcmp(argv[4], "-t") == 0) { |
| xfer_wait = tmp; |
| if (argc == 8) { |
| if (strcmp(argv[6], "-b") == 0) { |
| xfer_len = strtoul(argv[7], NULL, 10); |
| if (xfer_len > BUF_SIZ) { |
| send_if_to_if_usage(); |
| return -1; |
| } |
| } else { |
| send_if_to_if_usage(); |
| return -1; |
| } |
| } |
| } else { |
| send_if_to_if_usage(); |
| return -1; |
| } |
| } |
| |
| n = strtoul(argv[3], NULL, 10); |
| |
| net_stat(&src_rx_bytes, &src_tx_bytes, src_name); |
| net_stat(&dst_rx_bytes, &dst_tx_bytes, dst_name); |
| pid = fork(); |
| if (pid < 0) { |
| printf("Server fork error %d, errno %d\n", pid, errno); |
| return -1; |
| } |
| if (pid == 0) { |
| // Child process |
| send_mac_pkt(src_name, dst_name, xfer_len, xfer_wait, -1, NULL); |
| exit(0); |
| } |
| // Parent process |
| sleep(n); |
| kill(pid, SIGKILL); |
| sleep(ETH_STAT_WAIT_PERIOD); |
| net_stat(&src_rx_bytes, &src_tx_bytes, src_name); |
| net_stat(&dst_rx_bytes, &dst_tx_bytes, dst_name); |
| |
| if (dst_rx_bytes >= src_tx_bytes) { |
| printf("Sent %d seconds from %s(%d) to %s(%d) rate %3.3f Mb/s\n", n, |
| src_name, src_tx_bytes, dst_name, dst_rx_bytes, |
| (((float)dst_rx_bytes) * 8.0) / ((float)(n * ONE_MEG))); |
| } else { |
| printf("%s Sent %d seconds from %s(%d) to %s(%d)\n", FAIL_TEXT, n, src_name, |
| src_tx_bytes, dst_name, dst_rx_bytes); |
| } |
| |
| return 0; |
| } |
| |
| static void send_if_to_mac_usage(void) { |
| printf( |
| "send_if_to_mac <if> <dest MAC> <num> [-b <pkt byte size (max %d)>] " |
| "[-t <time delay in micro-second between pkt send>]\n", |
| BUF_SIZ); |
| printf("Example:\n"); |
| printf("send_if_to_mac lan0 f8:8f:ca:00:16:04 100\n"); |
| printf("send 100 msg from interface lan0 to f8:8f:ca:00:16:04\n"); |
| printf("send_if_to_mac lan0 f8:8f:ca:00:16:04 100 -b 256 -t 250\n"); |
| printf( |
| "send to interface lan0 with 100 msgs of size 256 bytes and 250 us " |
| "delay\n"); |
| } |
| |
| int send_if_to_mac(int argc, char *argv[]) { |
| int n; |
| unsigned int xfer_len = 16, xfer_wait = 0; |
| char if_name[IFNAMSIZ]; |
| unsigned char dst_mac[6] = {0, 0, 0, 0, 0, 0}; |
| // int rx_bytes, tx_bytes, rx_errs, tx_errs; |
| |
| /* Get interface name */ |
| if ((argc != 4) && (argc != 6) && (argc != 8)) { |
| send_if_to_mac_usage(); |
| return -1; |
| } |
| |
| strcpy(if_name, argv[1]); |
| |
| dst_mac[0] = strtoul(&argv[2][0], NULL, 16); |
| dst_mac[1] = strtoul(&argv[2][3], NULL, 16); |
| dst_mac[2] = strtoul(&argv[2][6], NULL, 16); |
| dst_mac[3] = strtoul(&argv[2][9], NULL, 16); |
| dst_mac[4] = strtoul(&argv[2][12], NULL, 16); |
| dst_mac[5] = strtoul(&argv[2][15], NULL, 16); |
| |
| if (argc >= 6) { |
| unsigned int tmp; |
| tmp = strtoul(argv[5], NULL, 10); |
| if (strcmp(argv[4], "-b") == 0) { |
| if (tmp > BUF_SIZ) { |
| send_if_to_mac_usage(); |
| return -1; |
| } |
| xfer_len = tmp; |
| |
| if (argc == 8) { |
| if (strcmp(argv[6], "-t") == 0) { |
| xfer_wait = strtoul(argv[7], NULL, 10); |
| } else { |
| send_if_to_mac_usage(); |
| return -1; |
| } |
| } |
| } else if (strcmp(argv[4], "-t") == 0) { |
| xfer_wait = tmp; |
| if (argc == 8) { |
| if (strcmp(argv[6], "-b") == 0) { |
| xfer_len = strtoul(argv[7], NULL, 10); |
| if (xfer_len > BUF_SIZ) { |
| send_if_to_mac_usage(); |
| return -1; |
| } |
| } else { |
| send_if_to_mac_usage(); |
| return -1; |
| } |
| } |
| } else { |
| send_if_to_mac_usage(); |
| return -1; |
| } |
| } |
| |
| n = strtoul(argv[3], NULL, 10); |
| |
| send_mac_pkt(if_name, NULL, xfer_len, xfer_wait, n, dst_mac); |
| |
| printf( |
| "Sent %d packets from IF %s to " |
| "%02x:%02x:%02x:%02x:%02x:%02x\n", |
| n, if_name, dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3], dst_mac[4], |
| dst_mac[5]); |
| |
| return 0; |
| } |
| |
| static void test_both_ports_usage(void) { |
| printf("test_both_ports <duration in secs> [<%s print-period in secs>]\n", |
| ETH_TRAFFIC_TEST_PERIOD_SYMBOL); |
| printf("- duration >=1 or -1 (forever)\n"); |
| printf("- print-period >= 0 and <= %d\n", ETH_TRAFFIC_MAX_REPORT_PERIOD); |
| printf("- traffic sent between lan0 and wan0\n"); |
| printf("- print-period > 0 if duration > 0\n"); |
| printf("- print-period = 0 prints only the summary\n"); |
| } |
| |
| int test_both_ports(int argc, char *argv[]) { |
| int duration, num = -1, pid, pid1, pid2; |
| int print_period = ETH_TRAFFIC_REPORT_PERIOD; |
| unsigned int pkt_len = ETH_PKTS_LEN_DEFAULT; |
| unsigned int lan0_rx, lan0_tx; |
| unsigned int wan0_rx, wan0_tx; |
| bool print_every_period = true; |
| bool failed = false, overall_failed = false; |
| bool wan0_tx_passed_with_lost = false, lan0_tx_passed_with_lost = false; |
| bool wan0_tx_failed = false, lan0_tx_failed = false; |
| int residuals_wan0_tx = 0, residuals_lan0_tx = 0; |
| |
| if ((argc != 2) && (argc != 4)) { |
| test_both_ports_usage(); |
| return -1; |
| } |
| |
| duration = strtol(argv[1], NULL, 0); |
| if ((duration < -1) || (duration == 0)) { |
| test_both_ports_usage(); |
| return -1; |
| } |
| |
| if (argc == 4) { |
| if (strcmp(argv[2], ETH_TRAFFIC_TEST_PERIOD_SYMBOL) != 0) { |
| test_both_ports_usage(); |
| return -1; |
| } |
| |
| print_period = strtoul(argv[3], NULL, 0); |
| if (((print_period == 0) && (duration < 0)) || (print_period < 0) || |
| (print_period > ETH_TRAFFIC_MAX_REPORT_PERIOD)) { |
| test_both_ports_usage(); |
| return -1; |
| } |
| if (print_period == 0) { |
| print_every_period = false; |
| print_period = ETH_TRAFFIC_REPORT_PERIOD; |
| } |
| } |
| |
| net_stat(&wan0_rx, &wan0_tx, ETH_TRAFFIC_PORT); |
| net_stat(&lan0_rx, &lan0_tx, ETH_TRAFFIC_DST_PORT); |
| |
| pid = fork(); |
| if (pid < 0) { |
| printf("Server fork error %d, errno %d\n", pid, errno); |
| return -1; |
| } |
| if (pid == 0) { |
| // Child process |
| send_mac_pkt(ETH_TRAFFIC_PORT, ETH_TRAFFIC_DST_PORT, pkt_len, 0, num, NULL); |
| exit(0); |
| } |
| // Parent process |
| pid1 = pid; |
| |
| pid = fork(); |
| if (pid < 0) { |
| printf("Server fork error %d, errno %d\n", pid, errno); |
| return -1; |
| } |
| if (pid == 0) { |
| // Child process |
| send_mac_pkt(ETH_TRAFFIC_DST_PORT, ETH_TRAFFIC_PORT, pkt_len, 0, num, NULL); |
| exit(0); |
| } |
| // Parent process |
| pid2 = pid; |
| |
| while (duration != 0) { |
| if (duration >= 0) { |
| if (duration <= print_period) { |
| failed = !sleep_and_check_carrier(duration, ETH_TRAFFIC_PORT); |
| print_period = duration; |
| duration = 0; |
| kill(pid1, SIGKILL); |
| kill(pid2, SIGKILL); |
| // printf("Killed processes %d and %d\n", pid1, pid2); |
| } else { |
| duration -= print_period; |
| failed = !sleep_and_check_carrier(print_period, ETH_TRAFFIC_PORT); |
| } |
| } else { |
| failed = !sleep_and_check_carrier(print_period, ETH_TRAFFIC_PORT); |
| } |
| if (print_every_period) { |
| if (duration > 0) { |
| kill(pid1, SIGSTOP); |
| kill(pid2, SIGSTOP); |
| } |
| sleep(ETH_STAT_WAIT_PERIOD); |
| if (failed) printf("Failed carrier status check\n"); |
| net_stat(&wan0_rx, &wan0_tx, ETH_TRAFFIC_PORT); |
| net_stat(&lan0_rx, &lan0_tx, ETH_TRAFFIC_DST_PORT); |
| if ((lan0_rx == 0) || (wan0_rx == 0) || (lan0_tx == 0) || (wan0_tx == 0)) |
| failed = true; |
| // Due to two processes are stopped one after another, need some |
| // margin to compare RX vs TX. Set it to 1% for now |
| // NOTE: since these number is unsigned int, (x * 95) / 100 could |
| // overflow. Therefore, use (x / 100) * 95 to avoid overflow. |
| // We don't care about such small truncation during integer |
| // division. |
| if (lan0_rx < |
| (((wan0_tx - residuals_wan0_tx) / 100) * ETH_STAT_PERCENT_MARGIN)) { |
| if (!wan0_tx_failed && !wan0_tx_passed_with_lost) { |
| wan0_tx_passed_with_lost = true; |
| printf("LAN0 RX seen lost\n"); |
| } else { |
| wan0_tx_failed = true; |
| printf("LAN0 RX failed\n"); |
| } |
| } else { |
| wan0_tx_failed = false; |
| wan0_tx_passed_with_lost = false; |
| } |
| // See comment above about integer division |
| if (wan0_rx < |
| (((lan0_tx - residuals_lan0_tx) / 100) * ETH_STAT_PERCENT_MARGIN)) { |
| if (!lan0_tx_failed && !lan0_tx_passed_with_lost) { |
| lan0_tx_passed_with_lost = true; |
| printf("WAN0 RX seen lost\n"); |
| } else { |
| lan0_tx_failed = true; |
| printf("WAN0 RX failed\n"); |
| } |
| } else { |
| lan0_tx_failed = false; |
| lan0_tx_passed_with_lost = false; |
| } |
| if (lan0_tx_failed || wan0_tx_failed) failed = true; |
| residuals_wan0_tx += lan0_rx - wan0_tx; |
| residuals_lan0_tx += wan0_rx - lan0_tx; |
| // When the cable is disconnected and connected again, got bogus data |
| if ((lan0_rx > ETH_TRAFFIC_PER_PERIOD_MAX) || |
| (wan0_rx > ETH_TRAFFIC_PER_PERIOD_MAX)) |
| failed = true; |
| if (failed) { |
| printf("Failed: %s (%d,%d) <-> %s (%d,%d)\n", ETH_TRAFFIC_PORT, wan0_tx, |
| wan0_rx, ETH_TRAFFIC_DST_PORT, lan0_tx, lan0_rx); |
| residuals_wan0_tx = 0; |
| residuals_lan0_tx = 0; |
| } else { |
| printf("%s: %s %3.3f Mb/s (%d,%d) <-> %s %3.3f Mb/s (%d,%d)\n", |
| (wan0_tx_passed_with_lost || lan0_tx_passed_with_lost) |
| ? "Passed + Lost" |
| : "Passed", |
| ETH_TRAFFIC_PORT, |
| (((float)wan0_tx) * 8) / (float)(print_period * ONE_MEG), |
| wan0_tx, wan0_rx, ETH_TRAFFIC_DST_PORT, |
| (((float)lan0_tx) * 8) / (float)(print_period * ONE_MEG), |
| lan0_tx, lan0_rx); |
| } |
| overall_failed |= failed; |
| failed = false; |
| if (duration > 0) { |
| kill(pid1, SIGCONT); |
| kill(pid2, SIGCONT); |
| } |
| } |
| } |
| if (overall_failed) printf("%s Ethernet port test\n", FAIL_TEXT); |
| |
| return 0; |
| } |
| |
| static void loopback_test_usage(void) { |
| printf( |
| "loopback_test <interface> <duration in secs> " |
| "[<%s print-period in secs>]\n", |
| ETH_TRAFFIC_TEST_PERIOD_SYMBOL); |
| printf("- duration >=1 or -1 (forever)\n"); |
| printf("- print-period >= 0 and <= %d\n", ETH_TRAFFIC_MAX_REPORT_PERIOD); |
| printf("- print-period > 0 if duration > 0\n"); |
| printf("- print-period = 0 prints only the summary\n"); |
| } |
| |
| int loopback_test(int argc, char *argv[]) { |
| int duration, num = -1, collected_count = 0; |
| int pid, pid1, print_period = ETH_TRAFFIC_REPORT_PERIOD; |
| unsigned int pkt_len = ETH_PKTS_LEN_DEFAULT, rx_bytes, tx_bytes; |
| bool print_every_period = true, traffic_problem = false, problem = false; |
| float average_throughput = 0.0, throughput; |
| unsigned char dst_mac[6] = {0, 0, 0, 0, 0, 0}; |
| |
| if ((argc != 3) && (argc != 5)) { |
| loopback_test_usage(); |
| return -1; |
| } |
| |
| if ((strcmp(argv[1], LAN_PORT_NAME) != 0) && |
| (strcmp(argv[1], WAN_PORT_NAME) != 0)) { |
| printf("Invalid Ethernet Interface %s\n", argv[1]); |
| return -1; |
| } |
| |
| duration = strtol(argv[2], NULL, 0); |
| if ((duration < -1) || (duration == 0)) { |
| loopback_test_usage(); |
| return -1; |
| } |
| |
| if (argc == 5) { |
| if (strcmp(argv[3], ETH_TRAFFIC_TEST_PERIOD_SYMBOL) != 0) { |
| loopback_test_usage(); |
| return -1; |
| } |
| |
| print_period = strtoul(argv[4], NULL, 0); |
| if (((print_period == 0) && (duration < 0)) || (print_period < 0) || |
| (print_period > ETH_TRAFFIC_MAX_REPORT_PERIOD)) { |
| loopback_test_usage(); |
| return -1; |
| } |
| if (print_period == 0) { |
| print_every_period = false; |
| print_period = ETH_TRAFFIC_REPORT_PERIOD; |
| } |
| } |
| |
| eth_external_loopback(argv[1], true); |
| |
| net_stat(&rx_bytes, &tx_bytes, argv[1]); |
| |
| pid = fork(); |
| if (pid < 0) { |
| printf("Server fork error %d, errno %d\n", pid, errno); |
| return -1; |
| } |
| if (pid == 0) { |
| // Child process |
| send_mac_pkt(argv[1], NULL, pkt_len, 0, num, dst_mac); |
| exit(0); |
| } |
| // Parent process |
| pid1 = pid; |
| |
| while (duration != 0) { |
| if (duration >= 0) { |
| if (duration <= print_period) { |
| problem = !sleep_and_check_carrier(duration, argv[1]); |
| print_period = duration; |
| duration = 0; |
| kill(pid1, SIGKILL); |
| // printf("Killed processes %d and %d\n", pid1, pid2); |
| } else { |
| duration -= print_period; |
| problem = !sleep_and_check_carrier(print_period, argv[1]); |
| } |
| } else { |
| problem = !sleep_and_check_carrier(print_period, argv[1]); |
| } |
| |
| if (duration > 0) kill(pid1, SIGSTOP); |
| sleep(ETH_STAT_WAIT_PERIOD); |
| net_stat(&rx_bytes, &tx_bytes, argv[1]); |
| if (duration > 0) kill(pid1, SIGCONT); |
| ++collected_count; |
| // Give 1% margin |
| if ((rx_bytes == 0) || |
| (((tx_bytes / 100) * ETH_STAT_PERCENT_MARGIN) > rx_bytes)) { |
| problem = true; |
| } |
| if ((rx_bytes > ETH_TRAFFIC_PER_PERIOD_MAX) || |
| (tx_bytes > ETH_TRAFFIC_PER_PERIOD_MAX)) { |
| problem = true; |
| } |
| traffic_problem |= problem; |
| if (!problem) { |
| throughput = (((float)rx_bytes) * 8) / (float)(print_period * ONE_MEG); |
| average_throughput += throughput; |
| } else { |
| throughput = 0.0; |
| } |
| if (print_every_period) { |
| printf("%s %s: %3.3f Mb/s (%d:%d)\n", (problem) ? FAIL_TEXT : PASS_TEXT, |
| argv[1], throughput, tx_bytes, rx_bytes); |
| } |
| problem = false; |
| } |
| |
| eth_external_loopback(argv[1], false); |
| |
| average_throughput /= ((float)collected_count); |
| printf("%s overall %s: %3.3f Mb/s\n", |
| (traffic_problem) ? FAIL_TEXT : PASS_TEXT, argv[1], |
| average_throughput); |
| |
| return 0; |
| } |