diff options
| -rw-r--r-- | iov.c | 35 | ||||
| -rw-r--r-- | iov.h | 2 | ||||
| -rw-r--r-- | tap.c | 15 |
3 files changed, 50 insertions, 2 deletions
@@ -450,3 +450,38 @@ ssize_t iov_tail_clone(struct iovec *dst_iov, size_t dst_iov_cnt, return j; } + +/** + * iov_tail_trim() - Limit a tail to @len bytes via a scratch iovec array + * @tail: Pointer to the iov_tail to trim; rebuilt on success to + * reference @scratch + * @len: Number of bytes to keep + * @scratch: Scratch iovec array backing the trimmed tail; must stay + * valid as long as the trimmed tail is in use + * @scratch_cnt: Number of elements in @scratch + * + * Return: true on success, false if @tail is shorter than @len or does + * not fit in @scratch (@tail is unchanged on failure) + */ +bool iov_tail_trim(struct iov_tail *tail, size_t len, + struct iovec *scratch, size_t scratch_cnt) +{ + ssize_t cnt = iov_tail_clone(scratch, scratch_cnt, tail); + size_t left = len; + unsigned int i; + + if (cnt < 0) + return false; + + for (i = 0; i < (size_t)cnt && left; i++) { + if (scratch[i].iov_len > left) + scratch[i].iov_len = left; + left -= scratch[i].iov_len; + } + + if (left) + return false; + + *tail = IOV_TAIL(scratch, i, 0); + return true; +} @@ -97,6 +97,8 @@ size_t iov_push_header_(struct iov_tail *tail, const void *v, size_t len); void *iov_remove_header_(struct iov_tail *tail, void *v, size_t len, size_t align); ssize_t iov_tail_clone(struct iovec *dst_iov, size_t dst_iov_cnt, struct iov_tail *tail); +bool iov_tail_trim(struct iov_tail *tail, size_t len, + struct iovec *scratch, size_t scratch_cnt); /** * IOV_PEEK_HEADER() - Get typed pointer to a header from an IOV tail @@ -716,7 +716,8 @@ static int tap4_handler(struct ctx *c, const struct pool *in, i = 0; resume: for (seq_count = 0, seq = NULL; i < in->count; i++) { - size_t l3len, hlen, l4len; + struct iovec trim_iov[UIO_MAXIOV]; + size_t l3len, hlen, l4len, check; struct ethhdr eh_storage; struct iphdr iph_storage; struct udphdr uh_storage; @@ -775,7 +776,17 @@ resume: if (!iov_drop_header(&data, hlen)) continue; - if (iov_tail_size(&data) != l4len) + + check = iov_tail_size(&data); + if (check < l4len) + continue; + + /* Drivers modelled on real hardware (Plan 9's virtio, for + * one) pad short frames to the 60 byte Ethernet minimum: + * trim trailing padding instead of dropping the packet. + */ + if (check > l4len && + !iov_tail_trim(&data, l4len, trim_iov, ARRAY_SIZE(trim_iov))) continue; if (iph->protocol == IPPROTO_ICMP) { |
