#ifndef CONFIG_NETAPP_HWDD #define CONFIG_NETAPP_HWDD 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hwddgbeinc.h" #define _PATH_PROCNET_DEV "/proc/net/dev" #define BUFSIZE 512 static char *hwdd_supported_nic_drvs[] = { HWDD_SUPPORTED_DRIVER_IGB, HWDD_SUPPORTED_DRIVER_IXGBE, HWDD_SUPPORTED_DRIVER_E1000E, NULL, }; static char *get_name(char *name, char *p); int hwdd_get_next_cpu() { int fh, err; struct hwdd_pktgen_uarg uarg; fh = open(CTRLBASENAME, O_RDONLY | O_NONBLOCK); if (-1 == fh) { return -1; } memset(&uarg, 0, sizeof(struct hwdd_pktgen_uarg)); uarg.cpu = 0xfefe; err = ioctl(fh, PKTGEN_IOCTL_GET_CPU, &uarg); if (-1 >= err) { close(fh); return -1; } close(fh); if (0xfefe == uarg.cpu) { return -1; } return uarg.cpu; } int hwdd_get_ifdevs(struct hwdd_ifdev *devarr, int ntapnames) { FILE *fh; char buf[BUFSIZE], name[IFNAMSIZ]; int i = 0; char *drv; fh = fopen(_PATH_PROCNET_DEV, "r"); if (!fh) { return -1; } /* get rid of un-need first 2 lines */ fgets(buf, sizeof buf, fh); fgets(buf, sizeof buf, fh); while (fgets(buf, sizeof buf, fh)) { get_name(name, buf); if (!hwdd_is_ethdev(name, ntapnames)) continue; drv = hwdd_supported_nic(name); if (!drv) continue; strncpy(devarr[i].name, name, IFNAMSIZ); strncpy(devarr[i].drv, drv, strlen(drv)); ++i; } fclose(fh); return i; } static char* get_name(char *name, char *p) { char *dot, *dotname; while (isspace(*p)) p++; while (*p) { if (isspace(*p)) break; if (*p == ':') { /* could be an alias */ dot = p, dotname = name; *name++ = *p++; while (isdigit(*p)) *name++ = *p++; if (*p != ':') { /* it wasn't alias, backup */ p = dot; name = dotname; } if (*p == '\0') return NULL; p++; break; } *name++ = *p++; } *name++ = '\0'; return p; } int hwdd_is_ethdev(char *name, int ntapnames) { if (!ntapnames) { if (strstr(name, "eth")) { return 1; } else { return 0; } } else { /* * ntap nic port names in the format eNm where e is the literal * e, N is a single digit, and m is a letter in either lower or * upper case */ if ('e' != name[0]) { return 0; } if (!isdigit(name[1])) { return 0; } if (!isalpha(name[2])) { return 0; } } return 1; } int hwdd_get_macaddr(char *ifname, struct ethtool_perm_addr *epaddr) { struct ifreq ifr; int fd, err; if (!ifname) return 1; if (!epaddr) return 1; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); fd = socket(AF_INET, SOCK_DGRAM, 0); if (0 > fd) return 1; epaddr->cmd = ETHTOOL_GPERMADDR; epaddr->size = MAX_ADDR_LEN; ifr.ifr_data = (caddr_t)epaddr; err = ioctl(fd, SIOCETHTOOL, &ifr); if (0 > err) return 1; return 0; } int hwdd_get_drvinfo(char *ifname, struct ethtool_drvinfo *info) { struct ifreq ifr; int fd, err; if (!ifname) return 1; if (!info) return 1; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); fd = socket(AF_INET, SOCK_DGRAM, 0); if (0 > fd) return 1; info->cmd = ETHTOOL_GDRVINFO; ifr.ifr_data = (caddr_t)info; err = ioctl(fd, SIOCETHTOOL, &ifr); if (0 > err) return 1; return 0; } int hwdd_get_settings(char *ifname, struct ethtool_cmd *ecmd) { struct ifreq ifr; int fd, err; if (!ifname) return 1; if (!ecmd) return 1; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); fd = socket(AF_INET, SOCK_DGRAM, 0); if (0 > fd) return 1; ecmd->cmd = ETHTOOL_GSET; ifr.ifr_data = (caddr_t)ecmd; err = ioctl(fd, SIOCETHTOOL, &ifr); if (0 > err) return 1; return 0; } int hwdd_probbed_invalid_macaddr(char *ifname) { int err; struct ethtool_drvinfo info; if (!ifname) return 1; err = hwdd_get_drvinfo(ifname, &info); if (err) return 1; if (info.invalid_macaddr) { return 1; } return 0; } char* hwdd_supported_nic(char *ifname) { int err, i; struct ethtool_drvinfo info; if (!ifname) return NULL; err = hwdd_get_drvinfo(ifname, &info); if (err) return NULL; i = 0; while (hwdd_supported_nic_drvs[i]) { if ( !strncmp(info.driver, hwdd_supported_nic_drvs[i], strlen(hwdd_supported_nic_drvs[i])) ) { return hwdd_supported_nic_drvs[i]; } ++i; } return NULL; } void hwdd_init_p2pdev(struct hwdd_pktgen_uarg *dev) { /* the following are used to configure test behavior */ memset(dev->ifdev, 0, sizeof dev->ifdev); dev->cpu = 0; dev->clone_skb = 0; dev->pkt_size = 1500; dev->min_pkt_size = 64; dev->max_pkt_size = 9000; // max it to jumbo frame payload size dev->frags = -1; memset(dev->dest_ip, 0, sizeof dev->dest_ip); memset(dev->src_ip, 0, sizeof dev->dest_ip); memset(dev->dest_mac, 0, sizeof dev->dest_mac); memset(dev->src_mac, 0, sizeof dev->dest_mac); dev->delay = 0; dev->pkt_count = HWDD_GBE_P2P_PKT_COUNT; /* the following are stats returned from the test, just uninit here */ dev->lflow = 0; dev->cflows = 0; dev->flags = -1; dev->idle = 0; dev->pkt_sofar = 0; dev->errors = 0; dev->started_at = 0; dev->stopped_at = 0; dev->nflows = 0; dev->tx_bytes = 0; // receive stats dev->rx.packets = 0; dev->rx.bytes = 0; dev->rx.elapsed = 0; dev->rx.pps = 0; dev->rx.bps = 0; dev->rx.mbps = 0; } int hwdd_config_tx_dev(struct hwdd_pktgen_uarg dev) { int err; if (0 > dev.cpu || !dev.ifdev || !dev.am_sender) { return 1; } /* bind the sending dev to the specified cpu */ err = hwdd_add_tx_dev(dev); if (err) { return 1; } /* setup destination dev hw addr */ if (dev.dest_mac) { hwdd_setattr_p2pdev(dev, "dst_mac", -1, dev.dest_mac); } else if (dev.dest_ip) { hwdd_setattr_p2pdev(dev, "dst", -1, dev.dest_ip); } else { return 1; } hwdd_setattr_p2pdev(dev, "clone_skb", dev.clone_skb, NULL); /* * note the -4, 4 bytes of info is added in the NIC for CRC so pkt_size * must be offset by -4 */ hwdd_setattr_p2pdev(dev, "pkt_size", dev.pkt_size - 4, NULL); hwdd_setattr_p2pdev(dev, "count", dev.pkt_count, NULL); hwdd_setattr_p2pdev(dev, "delay", dev.delay, NULL); return 0; } int hwdd_add_tx_dev(struct hwdd_pktgen_uarg dev) { int fh; int err = 1; if (0 > dev.cpu || !dev.ifdev || !dev.am_sender) { return 1; } fh = open(CTRLBASENAME, O_RDWR | O_NONBLOCK); if (0 > fh) { return 1; } err = ioctl(fh, PKTGEN_IOCTL_ADD_TX_DEV, &dev); close(fh); return err; } int hwdd_remove_rx_dev(struct hwdd_pktgen_uarg dev) { int fh; int err = 1; if (0 > dev.cpu || !dev.ifdev || dev.am_sender) { return 1; } fh = open(CTRLBASENAME, O_RDWR | O_NONBLOCK); if (0 > fh) { return 1; } err = ioctl(fh, PKTGEN_IOCTL_REMOVE_RX_DEV, &dev); close(fh); return err; } int hwdd_remove_tx_dev(struct hwdd_pktgen_uarg dev) { int fh; int err = 0; if (0 > dev.cpu || !dev.ifdev || !dev.am_sender) { return 1; } fh = open(CTRLBASENAME, O_RDWR | O_NONBLOCK); if (0 > fh) { return 1; } err = ioctl(fh, PKTGEN_IOCTL_REMOVE_TX_DEV, &dev); close(fh); return err; } int hwdd_setattr_p2pdev(struct hwdd_pktgen_uarg dev, char *attr, unsigned long decval, unsigned char *strval) { int count, fh; char cmd[128] = {'\0'}; char procifdev[128] = {'\0'}; if (!dev.ifdev) return 1; if (!strncmp(attr, "dst_mac", strlen(attr))) { if (!strval) return 1; snprintf(cmd, (sizeof cmd) - 1, "%s %02x:%02x:%02x:%02x:%02x:%02x", attr, strval[0], strval[1], strval[2], strval[3], strval[4], strval[5]); } else if (!strncmp(attr, "dst", strlen(attr))) { if (!strval) return 1; /* we need to think about how to get the IP as it may * not have a fixed length like the mac addr does */ } else { if (!strval) { snprintf(cmd, (sizeof cmd) - 1, "%s %lu", attr, decval); } else { snprintf(cmd, (sizeof cmd) - 1, "%s %s", attr, strval); } } snprintf(procifdev, (sizeof procifdev) - 1, "%s%s", DEVBASENAME, dev.ifdev); fh = open(procifdev, O_RDWR | O_NONBLOCK); if (0 > fh) return 1; count = write(fh, cmd, strlen(cmd)); if (0 >= count) return 1; close(fh); return 0; } int hwdd_p2pthread_ctrl(int cmd, int cpu) { struct hwdd_pktgen_uarg ctrl; int err = 0; if (0 > cpu) { return 1; } int fh = open(CTRLBASENAME, O_RDWR | O_NONBLOCK); if (0 > fh) return 1; ctrl.cpu = cpu; switch(cmd) { case PKTGEN_START: err = ioctl(fh, PKTGEN_IOCTL_TEST_START, &ctrl); break; case PKTGEN_STOP: err = ioctl(fh, PKTGEN_IOCTL_TEST_STOP, &ctrl); break; case PKTGEN_RST: err = ioctl(fh, PKTGEN_IOCTL_TEST_RESET, &ctrl); break; default: err = 1; break; } close(fh); return err; } int hwdd_p2p_results(struct hwdd_pktgen_uarg *rlst) { int fh; int err = 0; if (!rlst->ifdev || 0 > rlst->cpu) { return 1; } fh = open(CTRLBASENAME, O_RDWR | O_NONBLOCK); if (0 > fh) { return 1; } err = ioctl(fh, PKTGEN_IOCTL_GET_TX_STATS, rlst); if (0 > err) { err = 1; } close(fh); return err; } /* * adds the name of receiving port that the kernel should keep track of * for statistics purposes during data transfer */ int hwdd_p2p_add_rx_dev(struct hwdd_pktgen_uarg dev) { int err = 0; int fh = -1; if (0 > dev.cpu || !dev.ifdev || dev.am_sender) { return 1; } fh = open(CTRLBASENAME, O_RDWR | O_NONBLOCK); if (-1 >= fh) { return 1; } err = ioctl(fh, PKTGEN_IOCTL_ADD_RX_DEV, &dev); close (fh); return err; } int hwdd_p2p_get_rx_stats(struct hwdd_pktgen_uarg *rlst) { int err, fh; fh = open(CTRLBASENAME, O_RDWR | O_NONBLOCK); if (0 > fh) { return 1; } err = ioctl(fh, PKTGEN_IOCTL_GET_RX_STATS, rlst); if (0 > err) { err = 1; } close(fh); return err; } int hwdd_get_link(char *ifname) { int err, fd; struct ethtool_value edata; struct ifreq ifr; if (!ifname) return -1; fd = socket(AF_INET, SOCK_DGRAM, 0); if (0 > fd) return -1; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); edata.cmd = ETHTOOL_GLINK; ifr.ifr_data = (caddr_t)&edata; err = ioctl(fd, SIOCETHTOOL, &ifr); if (0 > err) return -1; return edata.data; } int hwdd_change_port_state(char *ifname, int state) { struct ifreq ifr; int fd, err; if (!ifname) return 1; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); fd = socket(AF_INET, SOCK_DGRAM, 0); if (0 > fd) return 1; /* get initial flags */ err = ioctl(fd, SIOCGIFFLAGS, &ifr); if (0 > err) return 1; if (state) { ifr.ifr_flags |= IFF_UP | IFF_RUNNING; } else { ifr.ifr_flags &= ~IFF_UP; } /* set flags */ err = ioctl(fd, SIOCSIFFLAGS, &ifr); if (0 > err) return 1; return 0; }