aboutsummaryrefslogtreecommitdiffstats
path: root/net/core/ethtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/core/ethtool.c')
-rw-r--r--net/core/ethtool.c149
1 files changed, 115 insertions, 34 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index a0f4964033d2..7a85367b3c2f 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -144,31 +144,13 @@ u32 ethtool_op_get_flags(struct net_device *dev)
144} 144}
145EXPORT_SYMBOL(ethtool_op_get_flags); 145EXPORT_SYMBOL(ethtool_op_get_flags);
146 146
147int ethtool_op_set_flags(struct net_device *dev, u32 data) 147int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported)
148{ 148{
149 const struct ethtool_ops *ops = dev->ethtool_ops; 149 if (data & ~supported)
150 unsigned long features = dev->features; 150 return -EINVAL;
151
152 if (data & ETH_FLAG_LRO)
153 features |= NETIF_F_LRO;
154 else
155 features &= ~NETIF_F_LRO;
156
157 if (data & ETH_FLAG_NTUPLE) {
158 if (!ops->set_rx_ntuple)
159 return -EOPNOTSUPP;
160 features |= NETIF_F_NTUPLE;
161 } else {
162 /* safe to clear regardless */
163 features &= ~NETIF_F_NTUPLE;
164 }
165
166 if (data & ETH_FLAG_RXHASH)
167 features |= NETIF_F_RXHASH;
168 else
169 features &= ~NETIF_F_RXHASH;
170 151
171 dev->features = features; 152 dev->features = ((dev->features & ~flags_dup_features) |
153 (data & flags_dup_features));
172 return 0; 154 return 0;
173} 155}
174EXPORT_SYMBOL(ethtool_op_set_flags); 156EXPORT_SYMBOL(ethtool_op_set_flags);
@@ -318,23 +300,33 @@ out:
318} 300}
319 301
320static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, 302static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
321 void __user *useraddr) 303 u32 cmd, void __user *useraddr)
322{ 304{
323 struct ethtool_rxnfc cmd; 305 struct ethtool_rxnfc info;
306 size_t info_size = sizeof(info);
324 307
325 if (!dev->ethtool_ops->set_rxnfc) 308 if (!dev->ethtool_ops->set_rxnfc)
326 return -EOPNOTSUPP; 309 return -EOPNOTSUPP;
327 310
328 if (copy_from_user(&cmd, useraddr, sizeof(cmd))) 311 /* struct ethtool_rxnfc was originally defined for
312 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
313 * members. User-space might still be using that
314 * definition. */
315 if (cmd == ETHTOOL_SRXFH)
316 info_size = (offsetof(struct ethtool_rxnfc, data) +
317 sizeof(info.data));
318
319 if (copy_from_user(&info, useraddr, info_size))
329 return -EFAULT; 320 return -EFAULT;
330 321
331 return dev->ethtool_ops->set_rxnfc(dev, &cmd); 322 return dev->ethtool_ops->set_rxnfc(dev, &info);
332} 323}
333 324
334static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, 325static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
335 void __user *useraddr) 326 u32 cmd, void __user *useraddr)
336{ 327{
337 struct ethtool_rxnfc info; 328 struct ethtool_rxnfc info;
329 size_t info_size = sizeof(info);
338 const struct ethtool_ops *ops = dev->ethtool_ops; 330 const struct ethtool_ops *ops = dev->ethtool_ops;
339 int ret; 331 int ret;
340 void *rule_buf = NULL; 332 void *rule_buf = NULL;
@@ -342,13 +334,22 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
342 if (!ops->get_rxnfc) 334 if (!ops->get_rxnfc)
343 return -EOPNOTSUPP; 335 return -EOPNOTSUPP;
344 336
345 if (copy_from_user(&info, useraddr, sizeof(info))) 337 /* struct ethtool_rxnfc was originally defined for
338 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
339 * members. User-space might still be using that
340 * definition. */
341 if (cmd == ETHTOOL_GRXFH)
342 info_size = (offsetof(struct ethtool_rxnfc, data) +
343 sizeof(info.data));
344
345 if (copy_from_user(&info, useraddr, info_size))
346 return -EFAULT; 346 return -EFAULT;
347 347
348 if (info.cmd == ETHTOOL_GRXCLSRLALL) { 348 if (info.cmd == ETHTOOL_GRXCLSRLALL) {
349 if (info.rule_cnt > 0) { 349 if (info.rule_cnt > 0) {
350 rule_buf = kmalloc(info.rule_cnt * sizeof(u32), 350 if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32))
351 GFP_USER); 351 rule_buf = kmalloc(info.rule_cnt * sizeof(u32),
352 GFP_USER);
352 if (!rule_buf) 353 if (!rule_buf)
353 return -ENOMEM; 354 return -ENOMEM;
354 } 355 }
@@ -359,7 +360,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
359 goto err_out; 360 goto err_out;
360 361
361 ret = -EFAULT; 362 ret = -EFAULT;
362 if (copy_to_user(useraddr, &info, sizeof(info))) 363 if (copy_to_user(useraddr, &info, info_size))
363 goto err_out; 364 goto err_out;
364 365
365 if (rule_buf) { 366 if (rule_buf) {
@@ -376,6 +377,80 @@ err_out:
376 return ret; 377 return ret;
377} 378}
378 379
380static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
381 void __user *useraddr)
382{
383 struct ethtool_rxfh_indir *indir;
384 u32 table_size;
385 size_t full_size;
386 int ret;
387
388 if (!dev->ethtool_ops->get_rxfh_indir)
389 return -EOPNOTSUPP;
390
391 if (copy_from_user(&table_size,
392 useraddr + offsetof(struct ethtool_rxfh_indir, size),
393 sizeof(table_size)))
394 return -EFAULT;
395
396 if (table_size >
397 (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index))
398 return -ENOMEM;
399 full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size;
400 indir = kmalloc(full_size, GFP_USER);
401 if (!indir)
402 return -ENOMEM;
403
404 indir->cmd = ETHTOOL_GRXFHINDIR;
405 indir->size = table_size;
406 ret = dev->ethtool_ops->get_rxfh_indir(dev, indir);
407 if (ret)
408 goto out;
409
410 if (copy_to_user(useraddr, indir, full_size))
411 ret = -EFAULT;
412
413out:
414 kfree(indir);
415 return ret;
416}
417
418static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
419 void __user *useraddr)
420{
421 struct ethtool_rxfh_indir *indir;
422 u32 table_size;
423 size_t full_size;
424 int ret;
425
426 if (!dev->ethtool_ops->set_rxfh_indir)
427 return -EOPNOTSUPP;
428
429 if (copy_from_user(&table_size,
430 useraddr + offsetof(struct ethtool_rxfh_indir, size),
431 sizeof(table_size)))
432 return -EFAULT;
433
434 if (table_size >
435 (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index))
436 return -ENOMEM;
437 full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size;
438 indir = kmalloc(full_size, GFP_USER);
439 if (!indir)
440 return -ENOMEM;
441
442 if (copy_from_user(indir, useraddr, full_size)) {
443 ret = -EFAULT;
444 goto out;
445 }
446
447 ret = dev->ethtool_ops->set_rxfh_indir(dev, indir);
448
449out:
450 kfree(indir);
451 return ret;
452}
453
379static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list, 454static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list,
380 struct ethtool_rx_ntuple_flow_spec *spec, 455 struct ethtool_rx_ntuple_flow_spec *spec,
381 struct ethtool_rx_ntuple_flow_spec_container *fsc) 456 struct ethtool_rx_ntuple_flow_spec_container *fsc)
@@ -1516,12 +1591,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
1516 case ETHTOOL_GRXCLSRLCNT: 1591 case ETHTOOL_GRXCLSRLCNT:
1517 case ETHTOOL_GRXCLSRULE: 1592 case ETHTOOL_GRXCLSRULE:
1518 case ETHTOOL_GRXCLSRLALL: 1593 case ETHTOOL_GRXCLSRLALL:
1519 rc = ethtool_get_rxnfc(dev, useraddr); 1594 rc = ethtool_get_rxnfc(dev, ethcmd, useraddr);
1520 break; 1595 break;
1521 case ETHTOOL_SRXFH: 1596 case ETHTOOL_SRXFH:
1522 case ETHTOOL_SRXCLSRLDEL: 1597 case ETHTOOL_SRXCLSRLDEL:
1523 case ETHTOOL_SRXCLSRLINS: 1598 case ETHTOOL_SRXCLSRLINS:
1524 rc = ethtool_set_rxnfc(dev, useraddr); 1599 rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);
1525 break; 1600 break;
1526 case ETHTOOL_GGRO: 1601 case ETHTOOL_GGRO:
1527 rc = ethtool_get_gro(dev, useraddr); 1602 rc = ethtool_get_gro(dev, useraddr);
@@ -1544,6 +1619,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
1544 case ETHTOOL_GSSET_INFO: 1619 case ETHTOOL_GSSET_INFO:
1545 rc = ethtool_get_sset_info(dev, useraddr); 1620 rc = ethtool_get_sset_info(dev, useraddr);
1546 break; 1621 break;
1622 case ETHTOOL_GRXFHINDIR:
1623 rc = ethtool_get_rxfh_indir(dev, useraddr);
1624 break;
1625 case ETHTOOL_SRXFHINDIR:
1626 rc = ethtool_set_rxfh_indir(dev, useraddr);
1627 break;
1547 default: 1628 default:
1548 rc = -EOPNOTSUPP; 1629 rc = -EOPNOTSUPP;
1549 } 1630 }