1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * This header is BSD licensed so anyone can use the definitions to implement 5 * compatible drivers/servers. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of IBM nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD$ 31 */ 32 33 #ifndef _VIRTIO_NET_H 34 #define _VIRTIO_NET_H 35 36 /* The feature bitmap for virtio net */ 37 #define VIRTIO_NET_F_CSUM 0x000001 /* Host handles pkts w/ partial csum */ 38 #define VIRTIO_NET_F_GUEST_CSUM 0x000002 /* Guest handles pkts w/ partial csum*/ 39 #define VIRTIO_NET_F_CTRL_GUEST_OFFLOADS 0x000004 /* Dynamic offload configuration. */ 40 #define VIRTIO_NET_F_MTU 0x000008 /* Initial MTU advice */ 41 #define VIRTIO_NET_F_MAC 0x000020 /* Host has given MAC address. */ 42 #define VIRTIO_NET_F_GSO 0x000040 /* Host handles pkts w/ any GSO type */ 43 #define VIRTIO_NET_F_GUEST_TSO4 0x000080 /* Guest can handle TSOv4 in. */ 44 #define VIRTIO_NET_F_GUEST_TSO6 0x000100 /* Guest can handle TSOv6 in. */ 45 #define VIRTIO_NET_F_GUEST_ECN 0x000200 /* Guest can handle TSO[6] w/ ECN in. */ 46 #define VIRTIO_NET_F_GUEST_UFO 0x000400 /* Guest can handle UFO in. */ 47 #define VIRTIO_NET_F_HOST_TSO4 0x000800 /* Host can handle TSOv4 in. */ 48 #define VIRTIO_NET_F_HOST_TSO6 0x001000 /* Host can handle TSOv6 in. */ 49 #define VIRTIO_NET_F_HOST_ECN 0x002000 /* Host can handle TSO[6] w/ ECN in. */ 50 #define VIRTIO_NET_F_HOST_UFO 0x004000 /* Host can handle UFO in. */ 51 #define VIRTIO_NET_F_MRG_RXBUF 0x008000 /* Host can merge receive buffers. */ 52 #define VIRTIO_NET_F_STATUS 0x010000 /* virtio_net_config.status available*/ 53 #define VIRTIO_NET_F_CTRL_VQ 0x020000 /* Control channel available */ 54 #define VIRTIO_NET_F_CTRL_RX 0x040000 /* Control channel RX mode support */ 55 #define VIRTIO_NET_F_CTRL_VLAN 0x080000 /* Control channel VLAN filtering */ 56 #define VIRTIO_NET_F_CTRL_RX_EXTRA 0x100000 /* Extra RX mode control support */ 57 #define VIRTIO_NET_F_GUEST_ANNOUNCE 0x200000 /* Announce device on network */ 58 #define VIRTIO_NET_F_MQ 0x400000 /* Device supports Receive Flow Steering */ 59 #define VIRTIO_NET_F_CTRL_MAC_ADDR 0x800000 /* Set MAC address */ 60 #define VIRTIO_NET_F_SPEED_DUPLEX (1ULL << 63) /* Device set linkspeed and duplex */ 61 62 #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ 63 #define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */ 64 65 struct virtio_net_config { 66 /* The config defining mac address (if VIRTIO_NET_F_MAC) */ 67 uint8_t mac[ETHER_ADDR_LEN]; 68 /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ 69 uint16_t status; 70 /* Maximum number of each of transmit and receive queues; 71 * see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ. 72 * Legal values are between 1 and 0x8000. 73 */ 74 uint16_t max_virtqueue_pairs; 75 /* Default maximum transmit unit advice */ 76 uint16_t mtu; 77 /* 78 * speed, in units of 1Mb. All values 0 to INT_MAX are legal. 79 * Any other value stands for unknown. 80 */ 81 uint32_t speed; 82 /* 83 * 0x00 - half duplex 84 * 0x01 - full duplex 85 * Any other value stands for unknown. 86 */ 87 uint8_t duplex; 88 } __packed; 89 90 /* 91 * This header comes first in the scatter-gather list. If you don't 92 * specify GSO or CSUM features, you can simply ignore the header. 93 * 94 * This is bitwise-equivalent to the legacy struct virtio_net_hdr_mrg_rxbuf, 95 * only flattened. 96 */ 97 struct virtio_net_hdr_v1 { 98 #define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* Use csum_start, csum_offset */ 99 #define VIRTIO_NET_HDR_F_DATA_VALID 2 /* Csum is valid */ 100 uint8_t flags; 101 #define VIRTIO_NET_HDR_GSO_NONE 0 /* Not a GSO frame */ 102 #define VIRTIO_NET_HDR_GSO_TCPV4 1 /* GSO frame, IPv4 TCP (TSO) */ 103 #define VIRTIO_NET_HDR_GSO_UDP 3 /* GSO frame, IPv4 UDP (UFO) */ 104 #define VIRTIO_NET_HDR_GSO_TCPV6 4 /* GSO frame, IPv6 TCP */ 105 #define VIRTIO_NET_HDR_GSO_ECN 0x80 /* TCP has ECN set */ 106 uint8_t gso_type; 107 uint16_t hdr_len; /* Ethernet + IP + tcp/udp hdrs */ 108 uint16_t gso_size; /* Bytes to append to hdr_len per frame */ 109 uint16_t csum_start; /* Position to start checksumming from */ 110 uint16_t csum_offset; /* Offset after that to place checksum */ 111 uint16_t num_buffers; /* Number of merged rx buffers */ 112 }; 113 114 /* 115 * This header comes first in the scatter-gather list. 116 * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must 117 * be the first element of the scatter-gather list. If you don't 118 * specify GSO or CSUM features, you can simply ignore the header. 119 */ 120 struct virtio_net_hdr { 121 /* See VIRTIO_NET_HDR_F_* */ 122 uint8_t flags; 123 /* See VIRTIO_NET_HDR_GSO_* */ 124 uint8_t gso_type; 125 uint16_t hdr_len; /* Ethernet + IP + tcp/udp hdrs */ 126 uint16_t gso_size; /* Bytes to append to hdr_len per frame */ 127 uint16_t csum_start; /* Position to start checksumming from */ 128 uint16_t csum_offset; /* Offset after that to place checksum */ 129 }; 130 131 /* 132 * This is the version of the header to use when the MRG_RXBUF 133 * feature has been negotiated. 134 */ 135 struct virtio_net_hdr_mrg_rxbuf { 136 struct virtio_net_hdr hdr; 137 uint16_t num_buffers; /* Number of merged rx buffers */ 138 }; 139 140 /* 141 * Control virtqueue data structures 142 * 143 * The control virtqueue expects a header in the first sg entry 144 * and an ack/status response in the last entry. Data for the 145 * command goes in between. 146 */ 147 struct virtio_net_ctrl_hdr { 148 uint8_t class; 149 uint8_t cmd; 150 } __packed; 151 152 #define VIRTIO_NET_OK 0 153 #define VIRTIO_NET_ERR 1 154 155 /* 156 * Control the RX mode, ie. promiscuous, allmulti, etc... 157 * All commands require an "out" sg entry containing a 1 byte 158 * state value, zero = disable, non-zero = enable. Commands 159 * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature. 160 * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA. 161 */ 162 #define VIRTIO_NET_CTRL_RX 0 163 #define VIRTIO_NET_CTRL_RX_PROMISC 0 164 #define VIRTIO_NET_CTRL_RX_ALLMULTI 1 165 #define VIRTIO_NET_CTRL_RX_ALLUNI 2 166 #define VIRTIO_NET_CTRL_RX_NOMULTI 3 167 #define VIRTIO_NET_CTRL_RX_NOUNI 4 168 #define VIRTIO_NET_CTRL_RX_NOBCAST 5 169 170 /* 171 * Control the MAC filter table. 172 * 173 * The MAC filter table is managed by the hypervisor, the guest should 174 * assume the size is infinite. Filtering should be considered 175 * non-perfect, ie. based on hypervisor resources, the guest may 176 * received packets from sources not specified in the filter list. 177 * 178 * In addition to the class/cmd header, the TABLE_SET command requires 179 * two out scatterlists. Each contains a 4 byte count of entries followed 180 * by a concatenated byte stream of the ETH_ALEN MAC addresses. The 181 * first sg list contains unicast addresses, the second is for multicast. 182 * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature 183 * is available. 184 * 185 * The ADDR_SET command requests one out scatterlist, it contains a 186 * 6 bytes MAC address. This functionality is present if the 187 * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available. 188 */ 189 struct virtio_net_ctrl_mac { 190 uint32_t entries; 191 uint8_t macs[][ETHER_ADDR_LEN]; 192 } __packed; 193 194 #define VIRTIO_NET_CTRL_MAC 1 195 #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 196 #define VIRTIO_NET_CTRL_MAC_ADDR_SET 1 197 198 /* 199 * Control VLAN filtering 200 * 201 * The VLAN filter table is controlled via a simple ADD/DEL interface. 202 * VLAN IDs not added may be filtered by the hypervisor. Del is the 203 * opposite of add. Both commands expect an out entry containing a 2 204 * byte VLAN ID. VLAN filtering is available with the 205 * VIRTIO_NET_F_CTRL_VLAN feature bit. 206 */ 207 #define VIRTIO_NET_CTRL_VLAN 2 208 #define VIRTIO_NET_CTRL_VLAN_ADD 0 209 #define VIRTIO_NET_CTRL_VLAN_DEL 1 210 211 /* 212 * Control link announce acknowledgement 213 * 214 * The command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that 215 * driver has recevied the notification; device would clear the 216 * VIRTIO_NET_S_ANNOUNCE bit in the status field after it receives 217 * this command. 218 */ 219 #define VIRTIO_NET_CTRL_ANNOUNCE 3 220 #define VIRTIO_NET_CTRL_ANNOUNCE_ACK 0 221 222 /* 223 * Control Receive Flow Steering 224 * 225 * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET enables Receive Flow 226 * Steering, specifying the number of the transmit and receive queues 227 * that will be used. After the command is consumed and acked by the 228 * device, the device will not steer new packets on receive virtqueues 229 * other than specified nor read from transmit virtqueues other than 230 * specified. Accordingly, driver should not transmit new packets on 231 * virtqueues other than specified. 232 */ 233 struct virtio_net_ctrl_mq { 234 uint16_t virtqueue_pairs; 235 } __packed; 236 237 #define VIRTIO_NET_CTRL_MQ 4 238 #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0 239 #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1 240 #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 241 242 /* 243 * Control network offloads 244 * 245 * Reconfigures the network offloads that Guest can handle. 246 * 247 * Available with the VIRTIO_NET_F_CTRL_GUEST_OFFLOADS feature bit. 248 * 249 * Command data format matches the feature bit mask exactly. 250 * 251 * See VIRTIO_NET_F_GUEST_* for the list of offloads 252 * that can be enabled/disabled. 253 */ 254 #define VIRTIO_NET_CTRL_GUEST_OFFLOADS 5 255 #define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET 0 256 257 /* 258 * Use the checksum offset in the VirtIO header to set the 259 * correct CSUM_* flags. 260 */ 261 static inline int 262 virtio_net_rx_csum_by_offset(struct mbuf *m, uint16_t eth_type, int ip_start, 263 struct virtio_net_hdr *hdr) 264 { 265 #if defined(INET) || defined(INET6) 266 int offset = hdr->csum_start + hdr->csum_offset; 267 #endif 268 269 /* Only do a basic sanity check on the offset. */ 270 switch (eth_type) { 271 #if defined(INET) 272 case ETHERTYPE_IP: 273 if (__predict_false(offset < ip_start + sizeof(struct ip))) 274 return (1); 275 break; 276 #endif 277 #if defined(INET6) 278 case ETHERTYPE_IPV6: 279 if (__predict_false(offset < ip_start + sizeof(struct ip6_hdr))) 280 return (1); 281 break; 282 #endif 283 default: 284 /* Here we should increment the rx_csum_bad_ethtype counter. */ 285 return (1); 286 } 287 288 /* 289 * Use the offset to determine the appropriate CSUM_* flags. This is 290 * a bit dirty, but we can get by with it since the checksum offsets 291 * happen to be different. We assume the host host does not do IPv4 292 * header checksum offloading. 293 */ 294 switch (hdr->csum_offset) { 295 case offsetof(struct udphdr, uh_sum): 296 case offsetof(struct tcphdr, th_sum): 297 m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 298 m->m_pkthdr.csum_data = 0xFFFF; 299 break; 300 default: 301 /* Here we should increment the rx_csum_bad_offset counter. */ 302 return (1); 303 } 304 305 return (0); 306 } 307 308 static inline int 309 virtio_net_rx_csum_by_parse(struct mbuf *m, uint16_t eth_type, int ip_start, 310 struct virtio_net_hdr *hdr) 311 { 312 int offset, proto; 313 314 switch (eth_type) { 315 #if defined(INET) 316 case ETHERTYPE_IP: { 317 struct ip *ip; 318 if (__predict_false(m->m_len < ip_start + sizeof(struct ip))) 319 return (1); 320 ip = (struct ip *)(m->m_data + ip_start); 321 proto = ip->ip_p; 322 offset = ip_start + (ip->ip_hl << 2); 323 break; 324 } 325 #endif 326 #if defined(INET6) 327 case ETHERTYPE_IPV6: 328 if (__predict_false(m->m_len < ip_start + 329 sizeof(struct ip6_hdr))) 330 return (1); 331 offset = ip6_lasthdr(m, ip_start, IPPROTO_IPV6, &proto); 332 if (__predict_false(offset < 0)) 333 return (1); 334 break; 335 #endif 336 default: 337 /* Here we should increment the rx_csum_bad_ethtype counter. */ 338 return (1); 339 } 340 341 switch (proto) { 342 case IPPROTO_TCP: 343 if (__predict_false(m->m_len < offset + sizeof(struct tcphdr))) 344 return (1); 345 m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 346 m->m_pkthdr.csum_data = 0xFFFF; 347 break; 348 case IPPROTO_UDP: 349 if (__predict_false(m->m_len < offset + sizeof(struct udphdr))) 350 return (1); 351 m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 352 m->m_pkthdr.csum_data = 0xFFFF; 353 break; 354 default: 355 /* 356 * For the remaining protocols, FreeBSD does not support 357 * checksum offloading, so the checksum will be recomputed. 358 */ 359 #if 0 360 if_printf(ifp, "cksum offload of unsupported " 361 "protocol eth_type=%#x proto=%d csum_start=%d " 362 "csum_offset=%d\n", __func__, eth_type, proto, 363 hdr->csum_start, hdr->csum_offset); 364 #endif 365 break; 366 } 367 368 return (0); 369 } 370 371 /* 372 * Set the appropriate CSUM_* flags. Unfortunately, the information 373 * provided is not directly useful to us. The VirtIO header gives the 374 * offset of the checksum, which is all Linux needs, but this is not 375 * how FreeBSD does things. We are forced to peek inside the packet 376 * a bit. 377 * 378 * It would be nice if VirtIO gave us the L4 protocol or if FreeBSD 379 * could accept the offsets and let the stack figure it out. 380 */ 381 static inline int 382 virtio_net_rx_csum(struct mbuf *m, struct virtio_net_hdr *hdr) 383 { 384 struct ether_header *eh; 385 struct ether_vlan_header *evh; 386 uint16_t eth_type; 387 int offset, error; 388 389 if ((hdr->flags & (VIRTIO_NET_HDR_F_NEEDS_CSUM | 390 VIRTIO_NET_HDR_F_DATA_VALID)) == 0) { 391 return (0); 392 } 393 394 eh = mtod(m, struct ether_header *); 395 eth_type = ntohs(eh->ether_type); 396 if (eth_type == ETHERTYPE_VLAN) { 397 /* BMV: We should handle nested VLAN tags too. */ 398 evh = mtod(m, struct ether_vlan_header *); 399 eth_type = ntohs(evh->evl_proto); 400 offset = sizeof(struct ether_vlan_header); 401 } else 402 offset = sizeof(struct ether_header); 403 404 if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) 405 error = virtio_net_rx_csum_by_offset(m, eth_type, offset, hdr); 406 else 407 error = virtio_net_rx_csum_by_parse(m, eth_type, offset, hdr); 408 409 return (error); 410 } 411 412 static inline int 413 virtio_net_tx_offload_ctx(struct mbuf *m, int *etype, int *proto, int *start) 414 { 415 struct ether_vlan_header *evh; 416 #if defined(INET) || defined(INET6) 417 int offset; 418 #endif 419 420 evh = mtod(m, struct ether_vlan_header *); 421 if (evh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { 422 /* BMV: We should handle nested VLAN tags too. */ 423 *etype = ntohs(evh->evl_proto); 424 #if defined(INET) || defined(INET6) 425 offset = sizeof(struct ether_vlan_header); 426 #endif 427 } else { 428 *etype = ntohs(evh->evl_encap_proto); 429 #if defined(INET) || defined(INET6) 430 offset = sizeof(struct ether_header); 431 #endif 432 } 433 434 switch (*etype) { 435 #if defined(INET) 436 case ETHERTYPE_IP: { 437 struct ip *ip, iphdr; 438 if (__predict_false(m->m_len < offset + sizeof(struct ip))) { 439 m_copydata(m, offset, sizeof(struct ip), 440 (caddr_t) &iphdr); 441 ip = &iphdr; 442 } else 443 ip = (struct ip *)(m->m_data + offset); 444 *proto = ip->ip_p; 445 *start = offset + (ip->ip_hl << 2); 446 break; 447 } 448 #endif 449 #if defined(INET6) 450 case ETHERTYPE_IPV6: 451 *proto = -1; 452 *start = ip6_lasthdr(m, offset, IPPROTO_IPV6, proto); 453 /* Assert the network stack sent us a valid packet. */ 454 KASSERT(*start > offset, 455 ("%s: mbuf %p start %d offset %d proto %d", __func__, m, 456 *start, offset, *proto)); 457 break; 458 #endif 459 default: 460 /* Here we should increment the tx_csum_bad_ethtype counter. */ 461 return (EINVAL); 462 } 463 464 return (0); 465 } 466 467 static inline int 468 virtio_net_tx_offload_tso(if_t ifp, struct mbuf *m, int eth_type, 469 int offset, bool allow_ecn, struct virtio_net_hdr *hdr) 470 { 471 static struct timeval lastecn; 472 static int curecn; 473 struct tcphdr *tcp, tcphdr; 474 475 if (__predict_false(m->m_len < offset + sizeof(struct tcphdr))) { 476 m_copydata(m, offset, sizeof(struct tcphdr), (caddr_t) &tcphdr); 477 tcp = &tcphdr; 478 } else 479 tcp = (struct tcphdr *)(m->m_data + offset); 480 481 hdr->hdr_len = offset + (tcp->th_off << 2); 482 hdr->gso_size = m->m_pkthdr.tso_segsz; 483 hdr->gso_type = eth_type == ETHERTYPE_IP ? VIRTIO_NET_HDR_GSO_TCPV4 : 484 VIRTIO_NET_HDR_GSO_TCPV6; 485 486 if (tcp->th_flags & TH_CWR) { 487 /* 488 * Drop if VIRTIO_NET_F_HOST_ECN was not negotiated. In FreeBSD, 489 * ECN support is not on a per-interface basis, but globally via 490 * the net.inet.tcp.ecn.enable sysctl knob. The default is off. 491 */ 492 if (!allow_ecn) { 493 if (ppsratecheck(&lastecn, &curecn, 1)) 494 if_printf(ifp, 495 "TSO with ECN not negotiated with host\n"); 496 return (ENOTSUP); 497 } 498 hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN; 499 } 500 501 /* Here we should increment tx_tso counter. */ 502 503 return (0); 504 } 505 506 static inline struct mbuf * 507 virtio_net_tx_offload(if_t ifp, struct mbuf *m, bool allow_ecn, 508 struct virtio_net_hdr *hdr) 509 { 510 int flags, etype, csum_start, proto, error; 511 512 flags = m->m_pkthdr.csum_flags; 513 514 error = virtio_net_tx_offload_ctx(m, &etype, &proto, &csum_start); 515 if (error) 516 goto drop; 517 518 if ((etype == ETHERTYPE_IP && (flags & (CSUM_TCP | CSUM_UDP))) || 519 (etype == ETHERTYPE_IPV6 && 520 (flags & (CSUM_TCP_IPV6 | CSUM_UDP_IPV6)))) { 521 /* 522 * We could compare the IP protocol vs the CSUM_ flag too, 523 * but that really should not be necessary. 524 */ 525 hdr->flags |= VIRTIO_NET_HDR_F_NEEDS_CSUM; 526 hdr->csum_start = csum_start; 527 hdr->csum_offset = m->m_pkthdr.csum_data; 528 /* Here we should increment the tx_csum counter. */ 529 } 530 531 if (flags & CSUM_TSO) { 532 if (__predict_false(proto != IPPROTO_TCP)) { 533 /* Likely failed to correctly parse the mbuf. 534 * Here we should increment the tx_tso_not_tcp 535 * counter. */ 536 goto drop; 537 } 538 539 KASSERT(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM, 540 ("%s: mbuf %p TSO without checksum offload %#x", 541 __func__, m, flags)); 542 543 error = virtio_net_tx_offload_tso(ifp, m, etype, csum_start, 544 allow_ecn, hdr); 545 if (error) 546 goto drop; 547 } 548 549 return (m); 550 551 drop: 552 m_freem(m); 553 return (NULL); 554 } 555 556 #endif /* _VIRTIO_NET_H */ 557