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 vhost_kernel_tap_set_offload(int fd, uint64_t features) 46 { 47 unsigned int offload = 0; 48 49 if (features & (1ULL << VIRTIO_NET_F_GUEST_CSUM)) { 50 offload |= TUN_F_CSUM; 51 if (features & (1ULL << VIRTIO_NET_F_GUEST_TSO4)) 52 offload |= TUN_F_TSO4; 53 if (features & (1ULL << VIRTIO_NET_F_GUEST_TSO6)) 54 offload |= TUN_F_TSO6; 55 if (features & ((1ULL << VIRTIO_NET_F_GUEST_TSO4) | 56 (1ULL << VIRTIO_NET_F_GUEST_TSO6)) && 57 (features & (1ULL << VIRTIO_NET_F_GUEST_ECN))) 58 offload |= TUN_F_TSO_ECN; 59 if (features & (1ULL << VIRTIO_NET_F_GUEST_UFO)) 60 offload |= TUN_F_UFO; 61 } 62 63 /* Check if our kernel supports TUNSETOFFLOAD */ 64 if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) { 65 PMD_DRV_LOG(ERR, "Kernel doesn't support TUNSETOFFLOAD\n"); 66 return -ENOTSUP; 67 } 68 69 if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { 70 offload &= ~TUN_F_UFO; 71 if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { 72 PMD_DRV_LOG(ERR, "TUNSETOFFLOAD ioctl() failed: %s\n", 73 strerror(errno)); 74 return -1; 75 } 76 } 77 78 return 0; 79 } 80 81 int 82 vhost_kernel_tap_set_queue(int fd, bool attach) 83 { 84 struct ifreq ifr = { 85 .ifr_flags = attach ? IFF_ATTACH_QUEUE : IFF_DETACH_QUEUE, 86 }; 87 88 return ioctl(fd, TUNSETQUEUE, &ifr); 89 } 90 91 int 92 vhost_kernel_open_tap(char **p_ifname, int hdr_size, int req_mq, 93 const char *mac, uint64_t features) 94 { 95 unsigned int tap_features; 96 char *tap_name = NULL; 97 int sndbuf = INT_MAX; 98 struct ifreq ifr; 99 int tapfd; 100 int ret; 101 102 /* TODO: 103 * 1. verify we can get/set vnet_hdr_len, tap_probe_vnet_hdr_len 104 * 2. get number of memory regions from vhost module parameter 105 * max_mem_regions, supported in newer version linux kernel 106 */ 107 tapfd = open(PATH_NET_TUN, O_RDWR); 108 if (tapfd < 0) { 109 PMD_DRV_LOG(ERR, "fail to open %s: %s", 110 PATH_NET_TUN, strerror(errno)); 111 return -1; 112 } 113 114 /* Construct ifr */ 115 memset(&ifr, 0, sizeof(ifr)); 116 ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 117 118 if (ioctl(tapfd, TUNGETFEATURES, &tap_features) == -1) { 119 PMD_DRV_LOG(ERR, "TUNGETFEATURES failed: %s", strerror(errno)); 120 goto error; 121 } 122 if (tap_features & IFF_ONE_QUEUE) 123 ifr.ifr_flags |= IFF_ONE_QUEUE; 124 125 /* Let tap instead of vhost-net handle vnet header, as the latter does 126 * not support offloading. And in this case, we should not set feature 127 * bit VHOST_NET_F_VIRTIO_NET_HDR. 128 */ 129 if (tap_features & IFF_VNET_HDR) { 130 ifr.ifr_flags |= IFF_VNET_HDR; 131 } else { 132 PMD_DRV_LOG(ERR, "TAP does not support IFF_VNET_HDR"); 133 goto error; 134 } 135 136 if (req_mq) 137 ifr.ifr_flags |= IFF_MULTI_QUEUE; 138 139 if (*p_ifname) 140 strncpy(ifr.ifr_name, *p_ifname, IFNAMSIZ - 1); 141 else 142 strncpy(ifr.ifr_name, "tap%d", IFNAMSIZ - 1); 143 if (ioctl(tapfd, TUNSETIFF, (void *)&ifr) == -1) { 144 PMD_DRV_LOG(ERR, "TUNSETIFF failed: %s", strerror(errno)); 145 goto error; 146 } 147 148 tap_name = strdup(ifr.ifr_name); 149 if (!tap_name) { 150 PMD_DRV_LOG(ERR, "strdup ifname failed: %s", strerror(errno)); 151 goto error; 152 } 153 154 if (fcntl(tapfd, F_SETFL, O_NONBLOCK) < 0) { 155 PMD_DRV_LOG(ERR, "fcntl tapfd failed: %s", strerror(errno)); 156 goto error; 157 } 158 159 if (ioctl(tapfd, TUNSETVNETHDRSZ, &hdr_size) < 0) { 160 PMD_DRV_LOG(ERR, "TUNSETVNETHDRSZ failed: %s", strerror(errno)); 161 goto error; 162 } 163 164 if (ioctl(tapfd, TUNSETSNDBUF, &sndbuf) < 0) { 165 PMD_DRV_LOG(ERR, "TUNSETSNDBUF failed: %s", strerror(errno)); 166 goto error; 167 } 168 169 ret = vhost_kernel_tap_set_offload(tapfd, features); 170 if (ret < 0 && ret != -ENOTSUP) 171 goto error; 172 173 memset(&ifr, 0, sizeof(ifr)); 174 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; 175 memcpy(ifr.ifr_hwaddr.sa_data, mac, RTE_ETHER_ADDR_LEN); 176 if (ioctl(tapfd, SIOCSIFHWADDR, (void *)&ifr) == -1) { 177 PMD_DRV_LOG(ERR, "SIOCSIFHWADDR failed: %s", strerror(errno)); 178 goto error; 179 } 180 181 free(*p_ifname); 182 *p_ifname = tap_name; 183 184 return tapfd; 185 error: 186 free(tap_name); 187 close(tapfd); 188 return -1; 189 } 190