diff options
author | Michał Mirosław <mirq-linux@rere.qmqm.pl> | 2011-04-19 02:13:10 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-04-20 04:30:45 -0400 |
commit | 882553752196605bf27057e7adb298ecae8058c4 (patch) | |
tree | b8c0cf62ff19b1982ecb42a8c5e877266453f405 /drivers/net/tun.c | |
parent | 47103041e91794acdbc6165da0ae288d844c820b (diff) |
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>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 63 |
1 files changed, 25 insertions, 38 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index f5e9ac00a07b..ade3cf9cd326 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -123,6 +123,9 @@ struct tun_struct { | |||
123 | gid_t group; | 123 | gid_t group; |
124 | 124 | ||
125 | struct net_device *dev; | 125 | struct net_device *dev; |
126 | u32 set_features; | ||
127 | #define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \ | ||
128 | NETIF_F_TSO6|NETIF_F_UFO) | ||
126 | struct fasync_struct *fasync; | 129 | struct fasync_struct *fasync; |
127 | 130 | ||
128 | struct tap_filter txflt; | 131 | struct tap_filter txflt; |
@@ -451,12 +454,20 @@ tun_net_change_mtu(struct net_device *dev, int new_mtu) | |||
451 | return 0; | 454 | return 0; |
452 | } | 455 | } |
453 | 456 | ||
457 | static u32 tun_net_fix_features(struct net_device *dev, u32 features) | ||
458 | { | ||
459 | struct tun_struct *tun = netdev_priv(dev); | ||
460 | |||
461 | return (features & tun->set_features) | (features & ~TUN_USER_FEATURES); | ||
462 | } | ||
463 | |||
454 | static const struct net_device_ops tun_netdev_ops = { | 464 | static const struct net_device_ops tun_netdev_ops = { |
455 | .ndo_uninit = tun_net_uninit, | 465 | .ndo_uninit = tun_net_uninit, |
456 | .ndo_open = tun_net_open, | 466 | .ndo_open = tun_net_open, |
457 | .ndo_stop = tun_net_close, | 467 | .ndo_stop = tun_net_close, |
458 | .ndo_start_xmit = tun_net_xmit, | 468 | .ndo_start_xmit = tun_net_xmit, |
459 | .ndo_change_mtu = tun_net_change_mtu, | 469 | .ndo_change_mtu = tun_net_change_mtu, |
470 | .ndo_fix_features = tun_net_fix_features, | ||
460 | }; | 471 | }; |
461 | 472 | ||
462 | static const struct net_device_ops tap_netdev_ops = { | 473 | static const struct net_device_ops tap_netdev_ops = { |
@@ -465,6 +476,7 @@ static const struct net_device_ops tap_netdev_ops = { | |||
465 | .ndo_stop = tun_net_close, | 476 | .ndo_stop = tun_net_close, |
466 | .ndo_start_xmit = tun_net_xmit, | 477 | .ndo_start_xmit = tun_net_xmit, |
467 | .ndo_change_mtu = tun_net_change_mtu, | 478 | .ndo_change_mtu = tun_net_change_mtu, |
479 | .ndo_fix_features = tun_net_fix_features, | ||
468 | .ndo_set_multicast_list = tun_net_mclist, | 480 | .ndo_set_multicast_list = tun_net_mclist, |
469 | .ndo_set_mac_address = eth_mac_addr, | 481 | .ndo_set_mac_address = eth_mac_addr, |
470 | .ndo_validate_addr = eth_validate_addr, | 482 | .ndo_validate_addr = eth_validate_addr, |
@@ -628,8 +640,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, | |||
628 | kfree_skb(skb); | 640 | kfree_skb(skb); |
629 | return -EINVAL; | 641 | return -EINVAL; |
630 | } | 642 | } |
631 | } else if (tun->flags & TUN_NOCHECKSUM) | 643 | } |
632 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
633 | 644 | ||
634 | switch (tun->flags & TUN_TYPE_MASK) { | 645 | switch (tun->flags & TUN_TYPE_MASK) { |
635 | case TUN_TUN_DEV: | 646 | case TUN_TUN_DEV: |
@@ -1094,6 +1105,10 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) | |||
1094 | goto err_free_sk; | 1105 | goto err_free_sk; |
1095 | } | 1106 | } |
1096 | 1107 | ||
1108 | dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | | ||
1109 | TUN_USER_FEATURES; | ||
1110 | dev->features = dev->hw_features; | ||
1111 | |||
1097 | err = register_netdevice(tun->dev); | 1112 | err = register_netdevice(tun->dev); |
1098 | if (err < 0) | 1113 | if (err < 0) |
1099 | goto err_free_sk; | 1114 | goto err_free_sk; |
@@ -1158,18 +1173,12 @@ static int tun_get_iff(struct net *net, struct tun_struct *tun, | |||
1158 | 1173 | ||
1159 | /* This is like a cut-down ethtool ops, except done via tun fd so no | 1174 | /* This is like a cut-down ethtool ops, except done via tun fd so no |
1160 | * privs required. */ | 1175 | * privs required. */ |
1161 | static int set_offload(struct net_device *dev, unsigned long arg) | 1176 | static int set_offload(struct tun_struct *tun, unsigned long arg) |
1162 | { | 1177 | { |
1163 | u32 old_features, features; | 1178 | u32 features = 0; |
1164 | |||
1165 | old_features = dev->features; | ||
1166 | /* Unset features, set them as we chew on the arg. */ | ||
1167 | features = (old_features & ~(NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST | ||
1168 | |NETIF_F_TSO_ECN|NETIF_F_TSO|NETIF_F_TSO6 | ||
1169 | |NETIF_F_UFO)); | ||
1170 | 1179 | ||
1171 | if (arg & TUN_F_CSUM) { | 1180 | if (arg & TUN_F_CSUM) { |
1172 | features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; | 1181 | features |= NETIF_F_HW_CSUM; |
1173 | arg &= ~TUN_F_CSUM; | 1182 | arg &= ~TUN_F_CSUM; |
1174 | 1183 | ||
1175 | if (arg & (TUN_F_TSO4|TUN_F_TSO6)) { | 1184 | if (arg & (TUN_F_TSO4|TUN_F_TSO6)) { |
@@ -1195,9 +1204,8 @@ static int set_offload(struct net_device *dev, unsigned long arg) | |||
1195 | if (arg) | 1204 | if (arg) |
1196 | return -EINVAL; | 1205 | return -EINVAL; |
1197 | 1206 | ||
1198 | dev->features = features; | 1207 | tun->set_features = features; |
1199 | if (old_features != dev->features) | 1208 | netdev_update_features(tun->dev); |
1200 | netdev_features_change(dev); | ||
1201 | 1209 | ||
1202 | return 0; | 1210 | return 0; |
1203 | } | 1211 | } |
@@ -1262,12 +1270,9 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, | |||
1262 | 1270 | ||
1263 | case TUNSETNOCSUM: | 1271 | case TUNSETNOCSUM: |
1264 | /* Disable/Enable checksum */ | 1272 | /* Disable/Enable checksum */ |
1265 | if (arg) | ||
1266 | tun->flags |= TUN_NOCHECKSUM; | ||
1267 | else | ||
1268 | tun->flags &= ~TUN_NOCHECKSUM; | ||
1269 | 1273 | ||
1270 | tun_debug(KERN_INFO, tun, "checksum %s\n", | 1274 | /* [unimplemented] */ |
1275 | tun_debug(KERN_INFO, tun, "ignored: set checksum %s\n", | ||
1271 | arg ? "disabled" : "enabled"); | 1276 | arg ? "disabled" : "enabled"); |
1272 | break; | 1277 | break; |
1273 | 1278 | ||
@@ -1316,7 +1321,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, | |||
1316 | break; | 1321 | break; |
1317 | #endif | 1322 | #endif |
1318 | case TUNSETOFFLOAD: | 1323 | case TUNSETOFFLOAD: |
1319 | ret = set_offload(tun->dev, arg); | 1324 | ret = set_offload(tun, arg); |
1320 | break; | 1325 | break; |
1321 | 1326 | ||
1322 | case TUNSETTXFILTER: | 1327 | case TUNSETTXFILTER: |
@@ -1595,30 +1600,12 @@ static void tun_set_msglevel(struct net_device *dev, u32 value) | |||
1595 | #endif | 1600 | #endif |
1596 | } | 1601 | } |
1597 | 1602 | ||
1598 | static u32 tun_get_rx_csum(struct net_device *dev) | ||
1599 | { | ||
1600 | struct tun_struct *tun = netdev_priv(dev); | ||
1601 | return (tun->flags & TUN_NOCHECKSUM) == 0; | ||
1602 | } | ||
1603 | |||
1604 | static int tun_set_rx_csum(struct net_device *dev, u32 data) | ||
1605 | { | ||
1606 | struct tun_struct *tun = netdev_priv(dev); | ||
1607 | if (data) | ||
1608 | tun->flags &= ~TUN_NOCHECKSUM; | ||
1609 | else | ||
1610 | tun->flags |= TUN_NOCHECKSUM; | ||
1611 | return 0; | ||
1612 | } | ||
1613 | |||
1614 | static const struct ethtool_ops tun_ethtool_ops = { | 1603 | static const struct ethtool_ops tun_ethtool_ops = { |
1615 | .get_settings = tun_get_settings, | 1604 | .get_settings = tun_get_settings, |
1616 | .get_drvinfo = tun_get_drvinfo, | 1605 | .get_drvinfo = tun_get_drvinfo, |
1617 | .get_msglevel = tun_get_msglevel, | 1606 | .get_msglevel = tun_get_msglevel, |
1618 | .set_msglevel = tun_set_msglevel, | 1607 | .set_msglevel = tun_set_msglevel, |
1619 | .get_link = ethtool_op_get_link, | 1608 | .get_link = ethtool_op_get_link, |
1620 | .get_rx_csum = tun_get_rx_csum, | ||
1621 | .set_rx_csum = tun_set_rx_csum | ||
1622 | }; | 1609 | }; |
1623 | 1610 | ||
1624 | 1611 | ||