aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
authorMichał Mirosław <mirq-linux@rere.qmqm.pl>2011-04-19 02:13:10 -0400
committerDavid S. Miller <davem@davemloft.net>2011-04-20 04:30:45 -0400
commit882553752196605bf27057e7adb298ecae8058c4 (patch)
treeb8c0cf62ff19b1982ecb42a8c5e877266453f405 /drivers/net/tun.c
parent47103041e91794acdbc6165da0ae288d844c820b (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.c63
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
457static 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
454static const struct net_device_ops tun_netdev_ops = { 464static 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
462static const struct net_device_ops tap_netdev_ops = { 473static 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. */
1161static int set_offload(struct net_device *dev, unsigned long arg) 1176static 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
1598static 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
1604static 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
1614static const struct ethtool_ops tun_ethtool_ops = { 1603static 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