Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
96b45cbd CW |
2 | #include <linux/kmod.h> |
3 | #include <linux/netdevice.h> | |
b0e99d03 | 4 | #include <linux/inetdevice.h> |
96b45cbd CW |
5 | #include <linux/etherdevice.h> |
6 | #include <linux/rtnetlink.h> | |
7 | #include <linux/net_tstamp.h> | |
fd770e85 | 8 | #include <linux/phylib_stubs.h> |
96b45cbd | 9 | #include <linux/wireless.h> |
ad2f99ae | 10 | #include <linux/if_bridge.h> |
5a178186 | 11 | #include <net/dsa_stubs.h> |
96b45cbd CW |
12 | #include <net/wext.h> |
13 | ||
6264f58c JK |
14 | #include "dev.h" |
15 | ||
96b45cbd CW |
16 | /* |
17 | * Map an interface index to its name (SIOCGIFNAME) | |
18 | */ | |
19 | ||
20 | /* | |
21 | * We need this ioctl for efficient implementation of the | |
22 | * if_indextoname() function required by the IPv6 API. Without | |
23 | * it, we would have to search all the interfaces to find a | |
24 | * match. --pb | |
25 | */ | |
26 | ||
44c02a2c | 27 | static int dev_ifname(struct net *net, struct ifreq *ifr) |
96b45cbd | 28 | { |
44c02a2c AV |
29 | ifr->ifr_name[IFNAMSIZ-1] = 0; |
30 | return netdev_get_name(net, ifr->ifr_name, ifr->ifr_ifindex); | |
96b45cbd CW |
31 | } |
32 | ||
96b45cbd CW |
33 | /* |
34 | * Perform a SIOCGIFCONF call. This structure will change | |
35 | * size eventually, and there is nothing I can do about it. | |
36 | * Thus we will need a 'compatibility mode'. | |
37 | */ | |
876f0bf9 | 38 | int dev_ifconf(struct net *net, struct ifconf __user *uifc) |
96b45cbd | 39 | { |
96b45cbd | 40 | struct net_device *dev; |
876f0bf9 AB |
41 | void __user *pos; |
42 | size_t size; | |
43 | int len, total = 0, done; | |
96b45cbd | 44 | |
876f0bf9 AB |
45 | /* both the ifconf and the ifreq structures are slightly different */ |
46 | if (in_compat_syscall()) { | |
47 | struct compat_ifconf ifc32; | |
96b45cbd | 48 | |
876f0bf9 AB |
49 | if (copy_from_user(&ifc32, uifc, sizeof(struct compat_ifconf))) |
50 | return -EFAULT; | |
96b45cbd | 51 | |
876f0bf9 AB |
52 | pos = compat_ptr(ifc32.ifcbuf); |
53 | len = ifc32.ifc_len; | |
54 | size = sizeof(struct compat_ifreq); | |
55 | } else { | |
56 | struct ifconf ifc; | |
57 | ||
58 | if (copy_from_user(&ifc, uifc, sizeof(struct ifconf))) | |
59 | return -EFAULT; | |
96b45cbd | 60 | |
876f0bf9 AB |
61 | pos = ifc.ifc_buf; |
62 | len = ifc.ifc_len; | |
63 | size = sizeof(struct ifreq); | |
64 | } | |
65 | ||
66 | /* Loop over the interfaces, and write an info block for each. */ | |
67 | rtnl_lock(); | |
96b45cbd | 68 | for_each_netdev(net, dev) { |
b0e99d03 AB |
69 | if (!pos) |
70 | done = inet_gifconf(dev, NULL, 0, size); | |
71 | else | |
72 | done = inet_gifconf(dev, pos + total, | |
73 | len - total, size); | |
876f0bf9 AB |
74 | if (done < 0) { |
75 | rtnl_unlock(); | |
b0e99d03 | 76 | return -EFAULT; |
876f0bf9 | 77 | } |
b0e99d03 | 78 | total += done; |
96b45cbd | 79 | } |
876f0bf9 | 80 | rtnl_unlock(); |
96b45cbd | 81 | |
876f0bf9 | 82 | return put_user(total, &uifc->ifc_len); |
96b45cbd CW |
83 | } |
84 | ||
709566d7 AB |
85 | static int dev_getifmap(struct net_device *dev, struct ifreq *ifr) |
86 | { | |
87 | struct ifmap *ifmap = &ifr->ifr_map; | |
88 | ||
89 | if (in_compat_syscall()) { | |
90 | struct compat_ifmap *cifmap = (struct compat_ifmap *)ifmap; | |
91 | ||
92 | cifmap->mem_start = dev->mem_start; | |
93 | cifmap->mem_end = dev->mem_end; | |
94 | cifmap->base_addr = dev->base_addr; | |
95 | cifmap->irq = dev->irq; | |
96 | cifmap->dma = dev->dma; | |
97 | cifmap->port = dev->if_port; | |
98 | ||
99 | return 0; | |
100 | } | |
101 | ||
102 | ifmap->mem_start = dev->mem_start; | |
103 | ifmap->mem_end = dev->mem_end; | |
104 | ifmap->base_addr = dev->base_addr; | |
105 | ifmap->irq = dev->irq; | |
106 | ifmap->dma = dev->dma; | |
107 | ifmap->port = dev->if_port; | |
108 | ||
109 | return 0; | |
110 | } | |
111 | ||
112 | static int dev_setifmap(struct net_device *dev, struct ifreq *ifr) | |
113 | { | |
114 | struct compat_ifmap *cifmap = (struct compat_ifmap *)&ifr->ifr_map; | |
115 | ||
116 | if (!dev->netdev_ops->ndo_set_config) | |
117 | return -EOPNOTSUPP; | |
118 | ||
119 | if (in_compat_syscall()) { | |
120 | struct ifmap ifmap = { | |
121 | .mem_start = cifmap->mem_start, | |
122 | .mem_end = cifmap->mem_end, | |
123 | .base_addr = cifmap->base_addr, | |
124 | .irq = cifmap->irq, | |
125 | .dma = cifmap->dma, | |
126 | .port = cifmap->port, | |
127 | }; | |
128 | ||
129 | return dev->netdev_ops->ndo_set_config(dev, &ifmap); | |
130 | } | |
131 | ||
132 | return dev->netdev_ops->ndo_set_config(dev, &ifr->ifr_map); | |
133 | } | |
134 | ||
96b45cbd CW |
135 | /* |
136 | * Perform the SIOCxIFxxx calls, inside rcu_read_lock() | |
137 | */ | |
138 | static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cmd) | |
139 | { | |
140 | int err; | |
141 | struct net_device *dev = dev_get_by_name_rcu(net, ifr->ifr_name); | |
142 | ||
143 | if (!dev) | |
144 | return -ENODEV; | |
145 | ||
146 | switch (cmd) { | |
147 | case SIOCGIFFLAGS: /* Get interface flags */ | |
148 | ifr->ifr_flags = (short) dev_get_flags(dev); | |
149 | return 0; | |
150 | ||
151 | case SIOCGIFMETRIC: /* Get the metric on the interface | |
152 | (currently unused) */ | |
153 | ifr->ifr_metric = 0; | |
154 | return 0; | |
155 | ||
156 | case SIOCGIFMTU: /* Get the MTU of a device */ | |
157 | ifr->ifr_mtu = dev->mtu; | |
158 | return 0; | |
159 | ||
96b45cbd CW |
160 | case SIOCGIFSLAVE: |
161 | err = -EINVAL; | |
162 | break; | |
163 | ||
164 | case SIOCGIFMAP: | |
709566d7 | 165 | return dev_getifmap(dev, ifr); |
96b45cbd CW |
166 | |
167 | case SIOCGIFINDEX: | |
168 | ifr->ifr_ifindex = dev->ifindex; | |
169 | return 0; | |
170 | ||
171 | case SIOCGIFTXQLEN: | |
172 | ifr->ifr_qlen = dev->tx_queue_len; | |
173 | return 0; | |
174 | ||
175 | default: | |
176 | /* dev_ioctl() should ensure this case | |
177 | * is never reached | |
178 | */ | |
179 | WARN_ON(1); | |
180 | err = -ENOTTY; | |
181 | break; | |
182 | ||
183 | } | |
184 | return err; | |
185 | } | |
186 | ||
c4bffeaa | 187 | static int net_hwtstamp_validate(const struct kernel_hwtstamp_config *cfg) |
96b45cbd | 188 | { |
96b45cbd CW |
189 | enum hwtstamp_tx_types tx_type; |
190 | enum hwtstamp_rx_filters rx_filter; | |
191 | int tx_type_valid = 0; | |
192 | int rx_filter_valid = 0; | |
193 | ||
d5d5fd8f | 194 | if (cfg->flags & ~HWTSTAMP_FLAG_MASK) |
96b45cbd CW |
195 | return -EINVAL; |
196 | ||
d5d5fd8f VO |
197 | tx_type = cfg->tx_type; |
198 | rx_filter = cfg->rx_filter; | |
96b45cbd CW |
199 | |
200 | switch (tx_type) { | |
201 | case HWTSTAMP_TX_OFF: | |
202 | case HWTSTAMP_TX_ON: | |
203 | case HWTSTAMP_TX_ONESTEP_SYNC: | |
b6fd7b96 | 204 | case HWTSTAMP_TX_ONESTEP_P2P: |
96b45cbd CW |
205 | tx_type_valid = 1; |
206 | break; | |
f76510b4 MK |
207 | case __HWTSTAMP_TX_CNT: |
208 | /* not a real value */ | |
209 | break; | |
96b45cbd CW |
210 | } |
211 | ||
212 | switch (rx_filter) { | |
213 | case HWTSTAMP_FILTER_NONE: | |
214 | case HWTSTAMP_FILTER_ALL: | |
215 | case HWTSTAMP_FILTER_SOME: | |
216 | case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: | |
217 | case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: | |
218 | case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: | |
219 | case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: | |
220 | case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: | |
221 | case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: | |
222 | case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: | |
223 | case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: | |
224 | case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: | |
225 | case HWTSTAMP_FILTER_PTP_V2_EVENT: | |
226 | case HWTSTAMP_FILTER_PTP_V2_SYNC: | |
227 | case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: | |
b8210a9e | 228 | case HWTSTAMP_FILTER_NTP_ALL: |
e3412575 | 229 | rx_filter_valid = 1; |
b8210a9e | 230 | break; |
f76510b4 MK |
231 | case __HWTSTAMP_FILTER_CNT: |
232 | /* not a real value */ | |
233 | break; | |
96b45cbd CW |
234 | } |
235 | ||
236 | if (!tx_type_valid || !rx_filter_valid) | |
237 | return -ERANGE; | |
238 | ||
239 | return 0; | |
240 | } | |
241 | ||
a7605370 AB |
242 | static int dev_eth_ioctl(struct net_device *dev, |
243 | struct ifreq *ifr, unsigned int cmd) | |
aad74d84 FF |
244 | { |
245 | const struct net_device_ops *ops = dev->netdev_ops; | |
4ee58e1e VO |
246 | |
247 | if (!ops->ndo_eth_ioctl) | |
248 | return -EOPNOTSUPP; | |
249 | ||
250 | if (!netif_device_present(dev)) | |
251 | return -ENODEV; | |
252 | ||
253 | return ops->ndo_eth_ioctl(dev, ifr, cmd); | |
254 | } | |
255 | ||
fd770e85 VO |
256 | /** |
257 | * dev_get_hwtstamp_phylib() - Get hardware timestamping settings of NIC | |
258 | * or of attached phylib PHY | |
259 | * @dev: Network device | |
260 | * @cfg: Timestamping configuration structure | |
261 | * | |
289354f2 JK |
262 | * Helper for enforcing a common policy that phylib timestamping, if available, |
263 | * should take precedence in front of hardware timestamping provided by the | |
264 | * netdev. | |
fd770e85 VO |
265 | * |
266 | * Note: phy_mii_ioctl() only handles SIOCSHWTSTAMP (not SIOCGHWTSTAMP), and | |
267 | * there only exists a phydev->mii_ts->hwtstamp() method. So this will return | |
268 | * -EOPNOTSUPP for phylib for now, which is still more accurate than letting | |
269 | * the netdev handle the GET request. | |
270 | */ | |
271 | static int dev_get_hwtstamp_phylib(struct net_device *dev, | |
272 | struct kernel_hwtstamp_config *cfg) | |
273 | { | |
289354f2 | 274 | if (phy_has_hwtstamp(dev->phydev)) |
fd770e85 VO |
275 | return phy_hwtstamp_get(dev->phydev, cfg); |
276 | ||
289354f2 | 277 | return dev->netdev_ops->ndo_hwtstamp_get(dev, cfg); |
fd770e85 VO |
278 | } |
279 | ||
4ee58e1e VO |
280 | static int dev_get_hwtstamp(struct net_device *dev, struct ifreq *ifr) |
281 | { | |
66f72230 MG |
282 | const struct net_device_ops *ops = dev->netdev_ops; |
283 | struct kernel_hwtstamp_config kernel_cfg = {}; | |
284 | struct hwtstamp_config cfg; | |
285 | int err; | |
286 | ||
287 | if (!ops->ndo_hwtstamp_get) | |
288 | return dev_eth_ioctl(dev, ifr, SIOCGHWTSTAMP); /* legacy */ | |
289 | ||
290 | if (!netif_device_present(dev)) | |
291 | return -ENODEV; | |
292 | ||
e47d01fe | 293 | kernel_cfg.ifr = ifr; |
fd770e85 | 294 | err = dev_get_hwtstamp_phylib(dev, &kernel_cfg); |
66f72230 MG |
295 | if (err) |
296 | return err; | |
297 | ||
e47d01fe MG |
298 | /* If the request was resolved through an unconverted driver, omit |
299 | * the copy_to_user(), since the implementation has already done that | |
300 | */ | |
301 | if (!kernel_cfg.copied_to_user) { | |
302 | hwtstamp_config_from_kernel(&cfg, &kernel_cfg); | |
66f72230 | 303 | |
e47d01fe MG |
304 | if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg))) |
305 | return -EFAULT; | |
306 | } | |
66f72230 MG |
307 | |
308 | return 0; | |
4ee58e1e VO |
309 | } |
310 | ||
fd770e85 VO |
311 | /** |
312 | * dev_set_hwtstamp_phylib() - Change hardware timestamping of NIC | |
313 | * or of attached phylib PHY | |
314 | * @dev: Network device | |
315 | * @cfg: Timestamping configuration structure | |
316 | * @extack: Netlink extended ack message structure, for error reporting | |
317 | * | |
289354f2 JK |
318 | * Helper for enforcing a common policy that phylib timestamping, if available, |
319 | * should take precedence in front of hardware timestamping provided by the | |
320 | * netdev. If the netdev driver needs to perform specific actions even for PHY | |
fd770e85 VO |
321 | * timestamping to work properly (a switch port must trap the timestamped |
322 | * frames and not forward them), it must set IFF_SEE_ALL_HWTSTAMP_REQUESTS in | |
323 | * dev->priv_flags. | |
324 | */ | |
011dd3b3 KM |
325 | int dev_set_hwtstamp_phylib(struct net_device *dev, |
326 | struct kernel_hwtstamp_config *cfg, | |
327 | struct netlink_ext_ack *extack) | |
fd770e85 VO |
328 | { |
329 | const struct net_device_ops *ops = dev->netdev_ops; | |
289354f2 | 330 | bool phy_ts = phy_has_hwtstamp(dev->phydev); |
fd770e85 VO |
331 | struct kernel_hwtstamp_config old_cfg = {}; |
332 | bool changed = false; | |
333 | int err; | |
334 | ||
289354f2 | 335 | cfg->source = phy_ts ? HWTSTAMP_SOURCE_PHYLIB : HWTSTAMP_SOURCE_NETDEV; |
fd770e85 | 336 | |
289354f2 | 337 | if (phy_ts && (dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS)) { |
fd770e85 VO |
338 | err = ops->ndo_hwtstamp_get(dev, &old_cfg); |
339 | if (err) | |
340 | return err; | |
c35e927c | 341 | } |
fd770e85 | 342 | |
289354f2 | 343 | if (!phy_ts || (dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS)) { |
fd770e85 VO |
344 | err = ops->ndo_hwtstamp_set(dev, cfg, extack); |
345 | if (err) { | |
346 | if (extack->_msg) | |
347 | netdev_err(dev, "%s\n", extack->_msg); | |
348 | return err; | |
349 | } | |
c35e927c | 350 | } |
fd770e85 | 351 | |
289354f2 | 352 | if (phy_ts && (dev->priv_flags & IFF_SEE_ALL_HWTSTAMP_REQUESTS)) |
fd770e85 | 353 | changed = kernel_hwtstamp_config_changed(&old_cfg, cfg); |
fd770e85 | 354 | |
289354f2 | 355 | if (phy_ts) { |
fd770e85 VO |
356 | err = phy_hwtstamp_set(dev->phydev, cfg, extack); |
357 | if (err) { | |
358 | if (changed) | |
359 | ops->ndo_hwtstamp_set(dev, &old_cfg, NULL); | |
360 | return err; | |
361 | } | |
362 | } | |
363 | ||
364 | return 0; | |
365 | } | |
011dd3b3 | 366 | EXPORT_SYMBOL_GPL(dev_set_hwtstamp_phylib); |
fd770e85 | 367 | |
4ee58e1e VO |
368 | static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr) |
369 | { | |
66f72230 | 370 | const struct net_device_ops *ops = dev->netdev_ops; |
e47d01fe | 371 | struct kernel_hwtstamp_config kernel_cfg = {}; |
88c0a6b5 | 372 | struct netlink_ext_ack extack = {}; |
d5d5fd8f | 373 | struct hwtstamp_config cfg; |
1ca47431 | 374 | int err; |
aad74d84 | 375 | |
d5d5fd8f VO |
376 | if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) |
377 | return -EFAULT; | |
378 | ||
c4bffeaa | 379 | hwtstamp_config_to_kernel(&kernel_cfg, &cfg); |
e47d01fe | 380 | kernel_cfg.ifr = ifr; |
c4bffeaa VO |
381 | |
382 | err = net_hwtstamp_validate(&kernel_cfg); | |
4ee58e1e | 383 | if (err) |
3369afba FF |
384 | return err; |
385 | ||
6ca80638 | 386 | err = dsa_conduit_hwtstamp_validate(dev, &kernel_cfg, &extack); |
88c0a6b5 VO |
387 | if (err) { |
388 | if (extack._msg) | |
389 | netdev_err(dev, "%s\n", extack._msg); | |
4ee58e1e | 390 | return err; |
88c0a6b5 | 391 | } |
aad74d84 | 392 | |
66f72230 MG |
393 | if (!ops->ndo_hwtstamp_set) |
394 | return dev_eth_ioctl(dev, ifr, SIOCSHWTSTAMP); /* legacy */ | |
395 | ||
396 | if (!netif_device_present(dev)) | |
397 | return -ENODEV; | |
398 | ||
fd770e85 VO |
399 | err = dev_set_hwtstamp_phylib(dev, &kernel_cfg, &extack); |
400 | if (err) | |
66f72230 | 401 | return err; |
66f72230 MG |
402 | |
403 | /* The driver may have modified the configuration, so copy the | |
404 | * updated version of it back to user space | |
405 | */ | |
e47d01fe MG |
406 | if (!kernel_cfg.copied_to_user) { |
407 | hwtstamp_config_from_kernel(&cfg, &kernel_cfg); | |
66f72230 | 408 | |
e47d01fe MG |
409 | if (copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg))) |
410 | return -EFAULT; | |
411 | } | |
412 | ||
413 | return 0; | |
414 | } | |
415 | ||
416 | static int generic_hwtstamp_ioctl_lower(struct net_device *dev, int cmd, | |
417 | struct kernel_hwtstamp_config *kernel_cfg) | |
418 | { | |
419 | struct ifreq ifrr; | |
420 | int err; | |
421 | ||
422 | strscpy_pad(ifrr.ifr_name, dev->name, IFNAMSIZ); | |
423 | ifrr.ifr_ifru = kernel_cfg->ifr->ifr_ifru; | |
424 | ||
425 | err = dev_eth_ioctl(dev, &ifrr, cmd); | |
426 | if (err) | |
427 | return err; | |
428 | ||
429 | kernel_cfg->ifr->ifr_ifru = ifrr.ifr_ifru; | |
430 | kernel_cfg->copied_to_user = true; | |
66f72230 MG |
431 | |
432 | return 0; | |
aad74d84 FF |
433 | } |
434 | ||
e47d01fe MG |
435 | int generic_hwtstamp_get_lower(struct net_device *dev, |
436 | struct kernel_hwtstamp_config *kernel_cfg) | |
437 | { | |
438 | const struct net_device_ops *ops = dev->netdev_ops; | |
439 | ||
440 | if (!netif_device_present(dev)) | |
441 | return -ENODEV; | |
442 | ||
443 | if (ops->ndo_hwtstamp_get) | |
fd770e85 | 444 | return dev_get_hwtstamp_phylib(dev, kernel_cfg); |
e47d01fe MG |
445 | |
446 | /* Legacy path: unconverted lower driver */ | |
447 | return generic_hwtstamp_ioctl_lower(dev, SIOCGHWTSTAMP, kernel_cfg); | |
448 | } | |
449 | EXPORT_SYMBOL(generic_hwtstamp_get_lower); | |
450 | ||
451 | int generic_hwtstamp_set_lower(struct net_device *dev, | |
452 | struct kernel_hwtstamp_config *kernel_cfg, | |
453 | struct netlink_ext_ack *extack) | |
454 | { | |
455 | const struct net_device_ops *ops = dev->netdev_ops; | |
456 | ||
457 | if (!netif_device_present(dev)) | |
458 | return -ENODEV; | |
459 | ||
460 | if (ops->ndo_hwtstamp_set) | |
fd770e85 | 461 | return dev_set_hwtstamp_phylib(dev, kernel_cfg, extack); |
e47d01fe MG |
462 | |
463 | /* Legacy path: unconverted lower driver */ | |
464 | return generic_hwtstamp_ioctl_lower(dev, SIOCSHWTSTAMP, kernel_cfg); | |
465 | } | |
466 | EXPORT_SYMBOL(generic_hwtstamp_set_lower); | |
467 | ||
3d9d00bd | 468 | static int dev_siocbond(struct net_device *dev, |
a7605370 AB |
469 | struct ifreq *ifr, unsigned int cmd) |
470 | { | |
471 | const struct net_device_ops *ops = dev->netdev_ops; | |
472 | ||
3d9d00bd | 473 | if (ops->ndo_siocbond) { |
a7605370 | 474 | if (netif_device_present(dev)) |
3d9d00bd | 475 | return ops->ndo_siocbond(dev, ifr, cmd); |
a7605370 AB |
476 | else |
477 | return -ENODEV; | |
478 | } | |
479 | ||
480 | return -EOPNOTSUPP; | |
481 | } | |
482 | ||
a554bf96 AB |
483 | static int dev_siocdevprivate(struct net_device *dev, struct ifreq *ifr, |
484 | void __user *data, unsigned int cmd) | |
b9067f5d AB |
485 | { |
486 | const struct net_device_ops *ops = dev->netdev_ops; | |
b9067f5d AB |
487 | |
488 | if (ops->ndo_siocdevprivate) { | |
489 | if (netif_device_present(dev)) | |
490 | return ops->ndo_siocdevprivate(dev, ifr, data, cmd); | |
491 | else | |
492 | return -ENODEV; | |
493 | } | |
494 | ||
88fc023f | 495 | return -EOPNOTSUPP; |
b9067f5d AB |
496 | } |
497 | ||
ad7eab2a AB |
498 | static int dev_siocwandev(struct net_device *dev, struct if_settings *ifs) |
499 | { | |
500 | const struct net_device_ops *ops = dev->netdev_ops; | |
501 | ||
502 | if (ops->ndo_siocwandev) { | |
503 | if (netif_device_present(dev)) | |
504 | return ops->ndo_siocwandev(dev, ifs); | |
505 | else | |
506 | return -ENODEV; | |
507 | } | |
508 | ||
509 | return -EOPNOTSUPP; | |
510 | } | |
511 | ||
96b45cbd CW |
512 | /* |
513 | * Perform the SIOCxIFxxx calls, inside rtnl_lock() | |
514 | */ | |
a554bf96 AB |
515 | static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data, |
516 | unsigned int cmd) | |
96b45cbd CW |
517 | { |
518 | int err; | |
519 | struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name); | |
520 | const struct net_device_ops *ops; | |
14ed029b | 521 | netdevice_tracker dev_tracker; |
96b45cbd CW |
522 | |
523 | if (!dev) | |
524 | return -ENODEV; | |
525 | ||
526 | ops = dev->netdev_ops; | |
527 | ||
528 | switch (cmd) { | |
529 | case SIOCSIFFLAGS: /* Set interface flags */ | |
567c5e13 | 530 | return dev_change_flags(dev, ifr->ifr_flags, NULL); |
96b45cbd CW |
531 | |
532 | case SIOCSIFMETRIC: /* Set the metric on the interface | |
533 | (currently unused) */ | |
534 | return -EOPNOTSUPP; | |
535 | ||
536 | case SIOCSIFMTU: /* Set the MTU of a device */ | |
537 | return dev_set_mtu(dev, ifr->ifr_mtu); | |
538 | ||
539 | case SIOCSIFHWADDR: | |
0254e0c6 WC |
540 | if (dev->addr_len > sizeof(struct sockaddr)) |
541 | return -EINVAL; | |
3b23a32a | 542 | return dev_set_mac_address_user(dev, &ifr->ifr_hwaddr, NULL); |
96b45cbd CW |
543 | |
544 | case SIOCSIFHWBROADCAST: | |
545 | if (ifr->ifr_hwaddr.sa_family != dev->type) | |
546 | return -EINVAL; | |
547 | memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, | |
b5f0de6d | 548 | min(sizeof(ifr->ifr_hwaddr.sa_data_min), |
54aeba7f | 549 | (size_t)dev->addr_len)); |
96b45cbd CW |
550 | call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); |
551 | return 0; | |
552 | ||
553 | case SIOCSIFMAP: | |
709566d7 | 554 | return dev_setifmap(dev, ifr); |
96b45cbd CW |
555 | |
556 | case SIOCADDMULTI: | |
557 | if (!ops->ndo_set_rx_mode || | |
558 | ifr->ifr_hwaddr.sa_family != AF_UNSPEC) | |
559 | return -EINVAL; | |
560 | if (!netif_device_present(dev)) | |
561 | return -ENODEV; | |
562 | return dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data); | |
563 | ||
564 | case SIOCDELMULTI: | |
565 | if (!ops->ndo_set_rx_mode || | |
566 | ifr->ifr_hwaddr.sa_family != AF_UNSPEC) | |
567 | return -EINVAL; | |
568 | if (!netif_device_present(dev)) | |
569 | return -ENODEV; | |
570 | return dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data); | |
571 | ||
572 | case SIOCSIFTXQLEN: | |
573 | if (ifr->ifr_qlen < 0) | |
574 | return -EINVAL; | |
8dd30201 | 575 | return dev_change_tx_queue_len(dev, ifr->ifr_qlen); |
96b45cbd CW |
576 | |
577 | case SIOCSIFNAME: | |
578 | ifr->ifr_newname[IFNAMSIZ-1] = '\0'; | |
579 | return dev_change_name(dev, ifr->ifr_newname); | |
580 | ||
ad7eab2a AB |
581 | case SIOCWANDEV: |
582 | return dev_siocwandev(dev, &ifr->ifr_settings); | |
583 | ||
ad2f99ae AB |
584 | case SIOCBRADDIF: |
585 | case SIOCBRDELIF: | |
586 | if (!netif_device_present(dev)) | |
587 | return -ENODEV; | |
9384eacd NA |
588 | if (!netif_is_bridge_master(dev)) |
589 | return -EOPNOTSUPP; | |
d62607c3 | 590 | netdev_hold(dev, &dev_tracker, GFP_KERNEL); |
893b1958 NA |
591 | rtnl_unlock(); |
592 | err = br_ioctl_call(net, netdev_priv(dev), cmd, ifr, NULL); | |
d62607c3 | 593 | netdev_put(dev, &dev_tracker); |
893b1958 NA |
594 | rtnl_lock(); |
595 | return err; | |
ad2f99ae | 596 | |
00d521b3 VO |
597 | case SIOCDEVPRIVATE ... SIOCDEVPRIVATE + 15: |
598 | return dev_siocdevprivate(dev, ifr, data, cmd); | |
599 | ||
96b45cbd | 600 | case SIOCSHWTSTAMP: |
4ee58e1e | 601 | return dev_set_hwtstamp(dev, ifr); |
96b45cbd | 602 | |
00d521b3 | 603 | case SIOCGHWTSTAMP: |
4ee58e1e VO |
604 | return dev_get_hwtstamp(dev, ifr); |
605 | ||
00d521b3 VO |
606 | case SIOCGMIIPHY: |
607 | case SIOCGMIIREG: | |
608 | case SIOCSMIIREG: | |
609 | return dev_eth_ioctl(dev, ifr, cmd); | |
610 | ||
611 | case SIOCBONDENSLAVE: | |
612 | case SIOCBONDRELEASE: | |
613 | case SIOCBONDSETHWADDR: | |
614 | case SIOCBONDSLAVEINFOQUERY: | |
615 | case SIOCBONDINFOQUERY: | |
616 | case SIOCBONDCHANGEACTIVE: | |
617 | return dev_siocbond(dev, ifr, cmd); | |
618 | ||
619 | /* Unknown ioctl */ | |
96b45cbd | 620 | default: |
00d521b3 | 621 | err = -EINVAL; |
96b45cbd CW |
622 | } |
623 | return err; | |
624 | } | |
625 | ||
626 | /** | |
627 | * dev_load - load a network module | |
628 | * @net: the applicable net namespace | |
629 | * @name: name of interface | |
630 | * | |
631 | * If a network interface is not present and the process has suitable | |
632 | * privileges this function loads the module. If module loading is not | |
633 | * available in this kernel then it becomes a nop. | |
634 | */ | |
635 | ||
636 | void dev_load(struct net *net, const char *name) | |
637 | { | |
638 | struct net_device *dev; | |
639 | int no_module; | |
640 | ||
641 | rcu_read_lock(); | |
642 | dev = dev_get_by_name_rcu(net, name); | |
643 | rcu_read_unlock(); | |
644 | ||
645 | no_module = !dev; | |
646 | if (no_module && capable(CAP_NET_ADMIN)) | |
647 | no_module = request_module("netdev-%s", name); | |
e020836d DB |
648 | if (no_module && capable(CAP_SYS_MODULE)) |
649 | request_module("%s", name); | |
96b45cbd CW |
650 | } |
651 | EXPORT_SYMBOL(dev_load); | |
652 | ||
653 | /* | |
654 | * This function handles all "interface"-type I/O control requests. The actual | |
655 | * 'doing' part of this is dev_ifsioc above. | |
656 | */ | |
657 | ||
658 | /** | |
659 | * dev_ioctl - network device ioctl | |
660 | * @net: the applicable net namespace | |
661 | * @cmd: command to issue | |
b3c0fd61 | 662 | * @ifr: pointer to a struct ifreq in user space |
e5b42483 | 663 | * @data: data exchanged with userspace |
b3c0fd61 | 664 | * @need_copyout: whether or not copy_to_user() should be called |
96b45cbd CW |
665 | * |
666 | * Issue ioctl functions to devices. This is normally called by the | |
667 | * user space syscall interfaces but can sometimes be useful for | |
668 | * other purposes. The return value is the return from the syscall if | |
669 | * positive or a negative errno code on error. | |
670 | */ | |
671 | ||
a554bf96 AB |
672 | int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, |
673 | void __user *data, bool *need_copyout) | |
96b45cbd | 674 | { |
96b45cbd CW |
675 | int ret; |
676 | char *colon; | |
677 | ||
44c02a2c AV |
678 | if (need_copyout) |
679 | *need_copyout = true; | |
96b45cbd | 680 | if (cmd == SIOCGIFNAME) |
44c02a2c | 681 | return dev_ifname(net, ifr); |
96b45cbd | 682 | |
44c02a2c | 683 | ifr->ifr_name[IFNAMSIZ-1] = 0; |
96b45cbd | 684 | |
44c02a2c | 685 | colon = strchr(ifr->ifr_name, ':'); |
96b45cbd CW |
686 | if (colon) |
687 | *colon = 0; | |
688 | ||
689 | /* | |
690 | * See which interface the caller is talking about. | |
691 | */ | |
692 | ||
693 | switch (cmd) { | |
3b23a32a CW |
694 | case SIOCGIFHWADDR: |
695 | dev_load(net, ifr->ifr_name); | |
696 | ret = dev_get_mac_address(&ifr->ifr_hwaddr, net, ifr->ifr_name); | |
697 | if (colon) | |
698 | *colon = ':'; | |
699 | return ret; | |
96b45cbd CW |
700 | /* |
701 | * These ioctl calls: | |
702 | * - can be done by all. | |
703 | * - atomic and do not require locking. | |
704 | * - return a value | |
705 | */ | |
706 | case SIOCGIFFLAGS: | |
707 | case SIOCGIFMETRIC: | |
708 | case SIOCGIFMTU: | |
96b45cbd CW |
709 | case SIOCGIFSLAVE: |
710 | case SIOCGIFMAP: | |
711 | case SIOCGIFINDEX: | |
712 | case SIOCGIFTXQLEN: | |
b51f26b1 | 713 | dev_load(net, ifr->ifr_name); |
96b45cbd | 714 | rcu_read_lock(); |
44c02a2c | 715 | ret = dev_ifsioc_locked(net, ifr, cmd); |
96b45cbd | 716 | rcu_read_unlock(); |
44c02a2c AV |
717 | if (colon) |
718 | *colon = ':'; | |
96b45cbd CW |
719 | return ret; |
720 | ||
721 | case SIOCETHTOOL: | |
b51f26b1 | 722 | dev_load(net, ifr->ifr_name); |
a554bf96 | 723 | ret = dev_ethtool(net, ifr, data); |
44c02a2c AV |
724 | if (colon) |
725 | *colon = ':'; | |
96b45cbd CW |
726 | return ret; |
727 | ||
728 | /* | |
729 | * These ioctl calls: | |
730 | * - require superuser power. | |
731 | * - require strict serialization. | |
732 | * - return a value | |
733 | */ | |
734 | case SIOCGMIIPHY: | |
735 | case SIOCGMIIREG: | |
736 | case SIOCSIFNAME: | |
b51f26b1 | 737 | dev_load(net, ifr->ifr_name); |
96b45cbd CW |
738 | if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
739 | return -EPERM; | |
96b45cbd | 740 | rtnl_lock(); |
a554bf96 | 741 | ret = dev_ifsioc(net, ifr, data, cmd); |
96b45cbd | 742 | rtnl_unlock(); |
44c02a2c AV |
743 | if (colon) |
744 | *colon = ':'; | |
96b45cbd CW |
745 | return ret; |
746 | ||
747 | /* | |
748 | * These ioctl calls: | |
749 | * - require superuser power. | |
750 | * - require strict serialization. | |
751 | * - do not return a value | |
752 | */ | |
753 | case SIOCSIFMAP: | |
754 | case SIOCSIFTXQLEN: | |
755 | if (!capable(CAP_NET_ADMIN)) | |
756 | return -EPERM; | |
df561f66 | 757 | fallthrough; |
96b45cbd CW |
758 | /* |
759 | * These ioctl calls: | |
760 | * - require local superuser power. | |
761 | * - require strict serialization. | |
762 | * - do not return a value | |
763 | */ | |
764 | case SIOCSIFFLAGS: | |
765 | case SIOCSIFMETRIC: | |
766 | case SIOCSIFMTU: | |
767 | case SIOCSIFHWADDR: | |
768 | case SIOCSIFSLAVE: | |
769 | case SIOCADDMULTI: | |
770 | case SIOCDELMULTI: | |
771 | case SIOCSIFHWBROADCAST: | |
772 | case SIOCSMIIREG: | |
773 | case SIOCBONDENSLAVE: | |
774 | case SIOCBONDRELEASE: | |
775 | case SIOCBONDSETHWADDR: | |
776 | case SIOCBONDCHANGEACTIVE: | |
777 | case SIOCBRADDIF: | |
778 | case SIOCBRDELIF: | |
779 | case SIOCSHWTSTAMP: | |
780 | if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) | |
781 | return -EPERM; | |
df561f66 | 782 | fallthrough; |
96b45cbd CW |
783 | case SIOCBONDSLAVEINFOQUERY: |
784 | case SIOCBONDINFOQUERY: | |
b51f26b1 | 785 | dev_load(net, ifr->ifr_name); |
96b45cbd | 786 | rtnl_lock(); |
a554bf96 | 787 | ret = dev_ifsioc(net, ifr, data, cmd); |
96b45cbd | 788 | rtnl_unlock(); |
44c02a2c AV |
789 | if (need_copyout) |
790 | *need_copyout = false; | |
96b45cbd CW |
791 | return ret; |
792 | ||
793 | case SIOCGIFMEM: | |
794 | /* Get the per device memory space. We can add this but | |
795 | * currently do not support it */ | |
796 | case SIOCSIFMEM: | |
797 | /* Set the per device memory buffer space. | |
798 | * Not applicable in our case */ | |
799 | case SIOCSIFLINK: | |
800 | return -ENOTTY; | |
801 | ||
802 | /* | |
803 | * Unknown or private ioctl. | |
804 | */ | |
805 | default: | |
806 | if (cmd == SIOCWANDEV || | |
fd468c74 | 807 | cmd == SIOCGHWTSTAMP || |
96b45cbd CW |
808 | (cmd >= SIOCDEVPRIVATE && |
809 | cmd <= SIOCDEVPRIVATE + 15)) { | |
b51f26b1 | 810 | dev_load(net, ifr->ifr_name); |
96b45cbd | 811 | rtnl_lock(); |
a554bf96 | 812 | ret = dev_ifsioc(net, ifr, data, cmd); |
96b45cbd | 813 | rtnl_unlock(); |
96b45cbd CW |
814 | return ret; |
815 | } | |
96b45cbd CW |
816 | return -ENOTTY; |
817 | } | |
818 | } |