tap = tap_dev_get_rcu(dev); if (!tap) return RX_HANDLER_PASS;
q = tap_get_queue(tap, skb); if (!q) return RX_HANDLER_PASS;
skb_push(skb, ETH_HLEN);
/* Apply the forward feature mask so that we perform segmentation *accordingtouserswishes.ThisonlyworksifVNET_HDRis *enabled.
*/ if (q->flags & IFF_VNET_HDR)
features |= tap->tap_features; if (netif_needs_gso(skb, features)) { struct sk_buff *segs = __skb_gso_segment(skb, features, false); struct sk_buff *next;
if (IS_ERR(segs)) {
drop_reason = SKB_DROP_REASON_SKB_GSO_SEG; goto drop;
}
if (!segs) { if (ptr_ring_produce(&q->ring, skb)) {
drop_reason = SKB_DROP_REASON_FULL_RING; goto drop;
} goto wake_up;
}
consume_skb(skb);
skb_list_walk_safe(segs, skb, next) {
skb_mark_not_on_list(skb); if (ptr_ring_produce(&q->ring, skb)) {
drop_reason = SKB_DROP_REASON_FULL_RING;
kfree_skb_reason(skb, drop_reason);
kfree_skb_list_reason(next, drop_reason); break;
}
}
} else { /* If we receive a partial checksum and the tap side *doesn'tsupportchecksumoffload,computethechecksum. *Note:itdoesn'tmatterwhichchecksumfeatureto *check,weeithersupportthemallornone.
*/ if (skb->ip_summed == CHECKSUM_PARTIAL &&
!(features & NETIF_F_CSUM_MASK) &&
skb_checksum_help(skb)) {
drop_reason = SKB_DROP_REASON_SKB_CSUM; goto drop;
} if (ptr_ring_produce(&q->ring, skb)) {
drop_reason = SKB_DROP_REASON_FULL_RING; goto drop;
}
}
rcu_read_lock();
tap_major = tap_get_major(major); if (!tap_major) {
tap = NULL; goto unlock;
}
spin_lock(&tap_major->minor_lock);
tap = idr_find(&tap_major->minor_idr, minor); if (tap) {
dev = tap->dev;
dev_hold(dev);
}
spin_unlock(&tap_major->minor_lock);
rcu_read_lock();
tap = rcu_dereference(q->tap); if (!tap) {
kfree_skb(skb);
rcu_read_unlock(); return total_len;
}
skb->dev = tap->dev;
if (vnet_hdr_len) {
err = tun_vnet_hdr_to_skb(q->flags, skb, &vnet_hdr); if (err) {
rcu_read_unlock();
drop_reason = SKB_DROP_REASON_DEV_HDR; goto err_kfree;
}
}
skb_probe_transport_header(skb);
/* Move network header to the right position for VLAN tagged packets */ if (eth_type_vlan(skb->protocol) &&
vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0)
skb_set_network_header(skb, depth);
/* copy skb_ubuf_info for callback when skb has no error */ if (zerocopy) {
skb_zcopy_init(skb, msg_control);
} elseif (msg_control) { struct ubuf_info *uarg = msg_control;
uarg->ops->complete(NULL, uarg, false);
}
/* Put packet to the user space buffer */ static ssize_t tap_put_user(struct tap_queue *q, conststruct sk_buff *skb, struct iov_iter *iter)
{ int ret; int vnet_hdr_len = 0; int vlan_offset = 0; int total;
if (q->flags & IFF_VNET_HDR) { struct virtio_net_hdr vnet_hdr;
vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz);
ret = tun_vnet_hdr_from_skb(q->flags, NULL, skb, &vnet_hdr); if (ret) return ret;
ret = tun_vnet_hdr_put(vnet_hdr_len, iter, &vnet_hdr); if (ret) return ret;
}
total = vnet_hdr_len;
total += skb->len;
vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto);
total += VLAN_HLEN;
ret = skb_copy_datagram_iter(skb, 0, iter, vlan_offset); if (ret || !iov_iter_count(iter)) goto done;
ret = copy_to_iter(&veth, sizeof(veth), iter); if (ret != sizeof(veth) || !iov_iter_count(iter)) goto done;
}
ret = skb_copy_datagram_iter(skb, vlan_offset, iter,
skb->len - vlan_offset);
done: return ret ? ret : total;
}
static ssize_t tap_do_read(struct tap_queue *q, struct iov_iter *to, int noblock, struct sk_buff *skb)
{
DEFINE_WAIT(wait);
ssize_t ret = 0;
if (!iov_iter_count(to)) {
kfree_skb(skb); return0;
}
if (skb) goto put;
while (1) { if (!noblock)
prepare_to_wait(sk_sleep(&q->sk), &wait,
TASK_INTERRUPTIBLE);
/* Read frames from the queue */
skb = ptr_ring_consume(&q->ring); if (skb) break; if (noblock) {
ret = -EAGAIN; break;
} if (signal_pending(current)) {
ret = -ERESTARTSYS; break;
} /* Nothing to read, let's sleep */
schedule();
} if (!noblock)
finish_wait(sk_sleep(&q->sk), &wait);
put: if (skb) {
ret = tap_put_user(q, skb, to); if (unlikely(ret < 0))
kfree_skb(skb); else
consume_skb(skb);
} return ret;
}
tap = rtnl_dereference(q->tap); if (!tap) return -ENOLINK;
features = tap->dev->features;
if (arg & TUN_F_CSUM) {
feature_mask = NETIF_F_HW_CSUM;
if (arg & (TUN_F_TSO4 | TUN_F_TSO6)) { if (arg & TUN_F_TSO_ECN)
feature_mask |= NETIF_F_TSO_ECN; if (arg & TUN_F_TSO4)
feature_mask |= NETIF_F_TSO; if (arg & TUN_F_TSO6)
feature_mask |= NETIF_F_TSO6;
}
/* TODO: for now USO4 and USO6 should work simultaneously */ if ((arg & (TUN_F_USO4 | TUN_F_USO6)) == (TUN_F_USO4 | TUN_F_USO6))
features |= NETIF_F_GSO_UDP_L4;
}
/* tun/tap driver inverts the usage for TSO offloads, where *settingtheTSObitmeansthattheuserspacewantsto *acceptTSOframesandturningitoffmeansthatuserspace *doesnotsupportTSO. *Fortap,wehavetoinvertittomeanthesamething. *WhenuserspaceturnsoffTSO,weturnoffGSO/LROsothat *user-spacewillnotreceiveTSOframes.
*/ if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6) ||
(feature_mask & (TUN_F_USO4 | TUN_F_USO6)) == (TUN_F_USO4 | TUN_F_USO6))
features |= RX_OFFLOADS; else
features &= ~RX_OFFLOADS;
/* tap_features are the same as features on tun/tap and *reflectuserexpectations.
*/
tap->tap_features = feature_mask; if (tap->update_features)
tap->update_features(tap, features);
switch (cmd) { case TUNSETIFF: /* ignore the name, just look at flags */ if (get_user(u, &ifr->ifr_flags)) return -EFAULT;
ret = 0; if ((u & ~TAP_IFFEATURES) != (IFF_NO_PI | IFF_TAP))
ret = -EINVAL; else
q->flags = (q->flags & ~TAP_IFFEATURES) | u;
return ret;
case TUNGETIFF:
rtnl_lock();
tap = tap_get_tap_dev(q); if (!tap) {
rtnl_unlock(); return -ENOLINK;
}
ret = 0;
u = q->flags; if (copy_to_user(&ifr->ifr_name, tap->dev->name, IFNAMSIZ) ||
put_user(u, &ifr->ifr_flags))
ret = -EFAULT;
tap_put_tap_dev(tap);
rtnl_unlock(); return ret;
case TUNSETQUEUE: if (get_user(u, &ifr->ifr_flags)) return -EFAULT;
rtnl_lock();
ret = tap_ioctl_set_queue(file, u);
rtnl_unlock(); return ret;
case TUNGETFEATURES: if (put_user(IFF_TAP | IFF_NO_PI | TAP_IFFEATURES, up)) return -EFAULT; return0;
case TUNSETSNDBUF: if (get_user(s, sp)) return -EFAULT; if (s <= 0) return -EINVAL;
q->sk.sk_sndbuf = s; return0;
case TUNSETOFFLOAD: /* let the user check for future flags */ if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
TUN_F_TSO_ECN | TUN_F_UFO |
TUN_F_USO4 | TUN_F_USO6)) return -EINVAL;
rtnl_lock();
ret = set_offload(q, arg);
rtnl_unlock(); return ret;
case SIOCGIFHWADDR:
rtnl_lock();
tap = tap_get_tap_dev(q); if (!tap) {
rtnl_unlock(); return -ENOLINK;
}
ret = 0;
netif_get_mac_address((struct sockaddr *)&ss, dev_net(tap->dev),
tap->dev->name); if (copy_to_user(&ifr->ifr_name, tap->dev->name, IFNAMSIZ) ||
copy_to_user(&ifr->ifr_hwaddr, &ss, sizeof(ifr->ifr_hwaddr)))
ret = -EFAULT;
tap_put_tap_dev(tap);
rtnl_unlock(); return ret;
case SIOCSIFHWADDR: if (copy_from_user(&ss, &ifr->ifr_hwaddr, sizeof(ifr->ifr_hwaddr))) return -EFAULT;
rtnl_lock();
tap = tap_get_tap_dev(q); if (!tap) {
rtnl_unlock(); return -ENOLINK;
} if (tap->dev->addr_len > sizeof(ifr->ifr_hwaddr))
ret = -EINVAL; else
ret = dev_set_mac_address_user(tap->dev, &ss, NULL);
tap_put_tap_dev(tap);
rtnl_unlock(); return ret;
if (vnet_hdr_len) {
err = tun_vnet_hdr_to_skb(q->flags, skb, gso); if (err) goto err_kfree;
}
/* Move network header to the right position for VLAN tagged packets */ if (eth_type_vlan(skb->protocol) &&
vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0)
skb_set_network_header(skb, depth);
rcu_read_lock();
tap = rcu_dereference(q->tap); if (tap) {
skb->dev = tap->dev;
skb_probe_transport_header(skb);
dev_queue_xmit(skb);
} else {
kfree_skb(skb);
}
rcu_read_unlock();
return0;
err_kfree:
kfree_skb(skb);
err:
rcu_read_lock();
tap = rcu_dereference(q->tap); if (tap && tap->count_tx_dropped)
tap->count_tx_dropped(tap);
rcu_read_unlock(); return err;
}
/* Ops structure to mimic raw sockets with tun */ staticconststruct proto_ops tap_socket_ops = {
.sendmsg = tap_sendmsg,
.recvmsg = tap_recvmsg,
.peek_len = tap_peek_len,
};
/* Get an underlying socket object from tun file. Returns error unless file is *attachedtoadevice.Thereturnedobjectworkslikeapacketsocket,it *canbeusedforsock_sendmsg/sock_recvmsg.Thecallerisresponsiblefor
* holding a reference to the file for as long as the socket is in use. */ struct socket *tap_get_socket(struct file *file)
{ struct tap_queue *q; if (file->f_op != &tap_fops) return ERR_PTR(-EINVAL);
q = file->private_data; if (!q) return ERR_PTR(-EBADFD); return &q->sock;
}
EXPORT_SYMBOL_GPL(tap_get_socket);
if (file->f_op != &tap_fops) return ERR_PTR(-EINVAL);
q = file->private_data; if (!q) return ERR_PTR(-EBADFD); return &q->ring;
}
EXPORT_SYMBOL_GPL(tap_get_ptr_ring);
int tap_queue_resize(struct tap_dev *tap)
{ struct net_device *dev = tap->dev; struct tap_queue *q; struct ptr_ring **rings; int n = tap->numqueues; int ret, i = 0;
rings = kmalloc_array(n, sizeof(*rings), GFP_KERNEL); if (!rings) return -ENOMEM;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.