mirror of https://gitee.com/openkylin/linux.git
net: tun: convert to hw_features
This changes offload setting behaviour to what I think is correct: - offloads set via ethtool mean what admin wants to use (by default he wants 'em all) - offloads set via ioctl() mean what userspace is expecting to get (this limits which admin wishes are granted) - TUN_NOCHECKSUM is ignored, as it might cause broken packets when forwarded (ip_summed == CHECKSUM_UNNECESSARY means that checksum was verified, not that it can be ignored) If TUN_NOCHECKSUM is implemented, it should set skb->csum_* and skb->ip_summed (= CHECKSUM_PARTIAL) for known protocols and let others be verified by kernel when necessary. TUN_NOCHECKSUM handling was introduced by commit f43798c27684ab925adde7d8acc34c78c6e50df8: tun: Allow GSO using virtio_net_hdr Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
47103041e9
commit
8825537521
|
@ -123,6 +123,9 @@ struct tun_struct {
|
|||
gid_t group;
|
||||
|
||||
struct net_device *dev;
|
||||
u32 set_features;
|
||||
#define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \
|
||||
NETIF_F_TSO6|NETIF_F_UFO)
|
||||
struct fasync_struct *fasync;
|
||||
|
||||
struct tap_filter txflt;
|
||||
|
@ -451,12 +454,20 @@ tun_net_change_mtu(struct net_device *dev, int new_mtu)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 tun_net_fix_features(struct net_device *dev, u32 features)
|
||||
{
|
||||
struct tun_struct *tun = netdev_priv(dev);
|
||||
|
||||
return (features & tun->set_features) | (features & ~TUN_USER_FEATURES);
|
||||
}
|
||||
|
||||
static const struct net_device_ops tun_netdev_ops = {
|
||||
.ndo_uninit = tun_net_uninit,
|
||||
.ndo_open = tun_net_open,
|
||||
.ndo_stop = tun_net_close,
|
||||
.ndo_start_xmit = tun_net_xmit,
|
||||
.ndo_change_mtu = tun_net_change_mtu,
|
||||
.ndo_fix_features = tun_net_fix_features,
|
||||
};
|
||||
|
||||
static const struct net_device_ops tap_netdev_ops = {
|
||||
|
@ -465,6 +476,7 @@ static const struct net_device_ops tap_netdev_ops = {
|
|||
.ndo_stop = tun_net_close,
|
||||
.ndo_start_xmit = tun_net_xmit,
|
||||
.ndo_change_mtu = tun_net_change_mtu,
|
||||
.ndo_fix_features = tun_net_fix_features,
|
||||
.ndo_set_multicast_list = tun_net_mclist,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
|
@ -628,8 +640,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun,
|
|||
kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (tun->flags & TUN_NOCHECKSUM)
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
}
|
||||
|
||||
switch (tun->flags & TUN_TYPE_MASK) {
|
||||
case TUN_TUN_DEV:
|
||||
|
@ -1094,6 +1105,10 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
|
|||
goto err_free_sk;
|
||||
}
|
||||
|
||||
dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST |
|
||||
TUN_USER_FEATURES;
|
||||
dev->features = dev->hw_features;
|
||||
|
||||
err = register_netdevice(tun->dev);
|
||||
if (err < 0)
|
||||
goto err_free_sk;
|
||||
|
@ -1158,18 +1173,12 @@ static int tun_get_iff(struct net *net, struct tun_struct *tun,
|
|||
|
||||
/* This is like a cut-down ethtool ops, except done via tun fd so no
|
||||
* privs required. */
|
||||
static int set_offload(struct net_device *dev, unsigned long arg)
|
||||
static int set_offload(struct tun_struct *tun, unsigned long arg)
|
||||
{
|
||||
u32 old_features, features;
|
||||
|
||||
old_features = dev->features;
|
||||
/* Unset features, set them as we chew on the arg. */
|
||||
features = (old_features & ~(NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST
|
||||
|NETIF_F_TSO_ECN|NETIF_F_TSO|NETIF_F_TSO6
|
||||
|NETIF_F_UFO));
|
||||
u32 features = 0;
|
||||
|
||||
if (arg & TUN_F_CSUM) {
|
||||
features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST;
|
||||
features |= NETIF_F_HW_CSUM;
|
||||
arg &= ~TUN_F_CSUM;
|
||||
|
||||
if (arg & (TUN_F_TSO4|TUN_F_TSO6)) {
|
||||
|
@ -1195,9 +1204,8 @@ static int set_offload(struct net_device *dev, unsigned long arg)
|
|||
if (arg)
|
||||
return -EINVAL;
|
||||
|
||||
dev->features = features;
|
||||
if (old_features != dev->features)
|
||||
netdev_features_change(dev);
|
||||
tun->set_features = features;
|
||||
netdev_update_features(tun->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1262,12 +1270,9 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
|
|||
|
||||
case TUNSETNOCSUM:
|
||||
/* Disable/Enable checksum */
|
||||
if (arg)
|
||||
tun->flags |= TUN_NOCHECKSUM;
|
||||
else
|
||||
tun->flags &= ~TUN_NOCHECKSUM;
|
||||
|
||||
tun_debug(KERN_INFO, tun, "checksum %s\n",
|
||||
/* [unimplemented] */
|
||||
tun_debug(KERN_INFO, tun, "ignored: set checksum %s\n",
|
||||
arg ? "disabled" : "enabled");
|
||||
break;
|
||||
|
||||
|
@ -1316,7 +1321,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
|
|||
break;
|
||||
#endif
|
||||
case TUNSETOFFLOAD:
|
||||
ret = set_offload(tun->dev, arg);
|
||||
ret = set_offload(tun, arg);
|
||||
break;
|
||||
|
||||
case TUNSETTXFILTER:
|
||||
|
@ -1595,30 +1600,12 @@ static void tun_set_msglevel(struct net_device *dev, u32 value)
|
|||
#endif
|
||||
}
|
||||
|
||||
static u32 tun_get_rx_csum(struct net_device *dev)
|
||||
{
|
||||
struct tun_struct *tun = netdev_priv(dev);
|
||||
return (tun->flags & TUN_NOCHECKSUM) == 0;
|
||||
}
|
||||
|
||||
static int tun_set_rx_csum(struct net_device *dev, u32 data)
|
||||
{
|
||||
struct tun_struct *tun = netdev_priv(dev);
|
||||
if (data)
|
||||
tun->flags &= ~TUN_NOCHECKSUM;
|
||||
else
|
||||
tun->flags |= TUN_NOCHECKSUM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ethtool_ops tun_ethtool_ops = {
|
||||
.get_settings = tun_get_settings,
|
||||
.get_drvinfo = tun_get_drvinfo,
|
||||
.get_msglevel = tun_get_msglevel,
|
||||
.set_msglevel = tun_set_msglevel,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_rx_csum = tun_get_rx_csum,
|
||||
.set_rx_csum = tun_set_rx_csum
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue