1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2016 Intel Corporation 3 */ 4 5 #include <unistd.h> 6 #include <sys/types.h> 7 #include <sys/stat.h> 8 #include <fcntl.h> 9 #include <net/if.h> 10 #include <net/if_arp.h> 11 #include <errno.h> 12 #include <string.h> 13 #include <limits.h> 14 15 #include <rte_ether.h> 16 17 #include "vhost_kernel_tap.h" 18 #include "../virtio_logs.h" 19 #include "../virtio.h" 20 21 22 int 23 tap_support_features(unsigned int *tap_features) 24 { 25 int tapfd; 26 27 tapfd = open(PATH_NET_TUN, O_RDWR); 28 if (tapfd < 0) { 29 PMD_DRV_LOG(ERR, "fail to open %s: %s", 30 PATH_NET_TUN, strerror(errno)); 31 return -1; 32 } 33 34 if (ioctl(tapfd, TUNGETFEATURES, tap_features) == -1) { 35 PMD_DRV_LOG(ERR, "TUNGETFEATURES failed: %s", strerror(errno)); 36 close(tapfd); 37 return -1; 38 } 39 40 close(tapfd); 41 return 0; 42 } 43 44 int 45 tap_open(const char *ifname, bool multi_queue) 46 { 47 struct ifreq ifr; 48 int tapfd; 49 50 tapfd = open(PATH_NET_TUN, O_RDWR); 51 if (tapfd < 0) { 52 PMD_DRV_LOG(ERR, "fail to open %s: %s", PATH_NET_TUN, strerror(errno)); 53 return -1; 54 } 55 if (fcntl(tapfd, F_SETFL, O_NONBLOCK) < 0) { 56 PMD_DRV_LOG(ERR, "fcntl tapfd failed: %s", strerror(errno)); 57 close(tapfd); 58 return -1; 59 } 60 61 retry_mono_q: 62 memset(&ifr, 0, sizeof(ifr)); 63 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); 64 ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR; 65 if (multi_queue) 66 ifr.ifr_flags |= IFF_MULTI_QUEUE; 67 if (ioctl(tapfd, TUNSETIFF, (void *)&ifr) == -1) { 68 if (multi_queue) { 69 PMD_DRV_LOG(DEBUG, 70 "TUNSETIFF failed (will retry without IFF_MULTI_QUEUE): %s", 71 strerror(errno)); 72 multi_queue = false; 73 goto retry_mono_q; 74 } 75 76 PMD_DRV_LOG(ERR, "TUNSETIFF failed: %s", strerror(errno)); 77 close(tapfd); 78 tapfd = -1; 79 } 80 return tapfd; 81 } 82 83 int 84 tap_get_name(int tapfd, char **name) 85 { 86 struct ifreq ifr; 87 int ret; 88 89 memset(&ifr, 0, sizeof(ifr)); 90 if (ioctl(tapfd, TUNGETIFF, (void *)&ifr) == -1) { 91 PMD_DRV_LOG(ERR, "TUNGETIFF failed: %s", strerror(errno)); 92 return -1; 93 } 94 ret = asprintf(name, "%s", ifr.ifr_name); 95 if (ret != -1) 96 ret = 0; 97 return ret; 98 } 99 100 int 101 tap_get_flags(int tapfd, unsigned int *tap_flags) 102 { 103 struct ifreq ifr; 104 105 memset(&ifr, 0, sizeof(ifr)); 106 if (ioctl(tapfd, TUNGETIFF, (void *)&ifr) == -1) { 107 PMD_DRV_LOG(ERR, "TUNGETIFF failed: %s", strerror(errno)); 108 return -1; 109 } 110 *tap_flags = ifr.ifr_flags; 111 return 0; 112 } 113 114 int 115 tap_set_mac(int tapfd, uint8_t *mac) 116 { 117 struct ifreq ifr; 118 119 memset(&ifr, 0, sizeof(ifr)); 120 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; 121 memcpy(ifr.ifr_hwaddr.sa_data, mac, RTE_ETHER_ADDR_LEN); 122 if (ioctl(tapfd, SIOCSIFHWADDR, (void *)&ifr) == -1) { 123 PMD_DRV_LOG(ERR, "SIOCSIFHWADDR failed: %s", strerror(errno)); 124 return -1; 125 } 126 return 0; 127 } 128 129 static int 130 vhost_kernel_tap_set_offload(int fd, uint64_t features) 131 { 132 unsigned int offload = 0; 133 134 if (features & (1ULL << VIRTIO_NET_F_GUEST_CSUM)) { 135 offload |= TUN_F_CSUM; 136 if (features & (1ULL << VIRTIO_NET_F_GUEST_TSO4)) 137 offload |= TUN_F_TSO4; 138 if (features & (1ULL << VIRTIO_NET_F_GUEST_TSO6)) 139 offload |= TUN_F_TSO6; 140 if (features & ((1ULL << VIRTIO_NET_F_GUEST_TSO4) | 141 (1ULL << VIRTIO_NET_F_GUEST_TSO6)) && 142 (features & (1ULL << VIRTIO_NET_F_GUEST_ECN))) 143 offload |= TUN_F_TSO_ECN; 144 if (features & (1ULL << VIRTIO_NET_F_GUEST_UFO)) 145 offload |= TUN_F_UFO; 146 } 147 148 /* Check if our kernel supports TUNSETOFFLOAD */ 149 if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) { 150 PMD_DRV_LOG(ERR, "Kernel doesn't support TUNSETOFFLOAD"); 151 return -ENOTSUP; 152 } 153 154 if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { 155 offload &= ~TUN_F_UFO; 156 if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { 157 PMD_DRV_LOG(ERR, "TUNSETOFFLOAD ioctl() failed: %s", 158 strerror(errno)); 159 return -1; 160 } 161 } 162 163 return 0; 164 } 165 166 int 167 vhost_kernel_tap_setup(int tapfd, int hdr_size, uint64_t features) 168 { 169 int sndbuf = INT_MAX; 170 int ret; 171 172 /* TODO: 173 * 1. verify we can get/set vnet_hdr_len, tap_probe_vnet_hdr_len 174 * 2. get number of memory regions from vhost module parameter 175 * max_mem_regions, supported in newer version linux kernel 176 */ 177 if (ioctl(tapfd, TUNSETVNETHDRSZ, &hdr_size) < 0) { 178 PMD_DRV_LOG(ERR, "TUNSETVNETHDRSZ failed: %s", strerror(errno)); 179 return -1; 180 } 181 182 if (ioctl(tapfd, TUNSETSNDBUF, &sndbuf) < 0) { 183 PMD_DRV_LOG(ERR, "TUNSETSNDBUF failed: %s", strerror(errno)); 184 return -1; 185 } 186 187 ret = vhost_kernel_tap_set_offload(tapfd, features); 188 if (ret == -ENOTSUP) 189 ret = 0; 190 return ret; 191 } 192