diff options
| author | Al Viro <viro@zeniv.linux.org.uk> | 2017-06-26 13:19:16 -0400 |
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2018-01-24 19:13:45 -0500 |
| commit | 36fd633ec98acd2028585c22128fcaa3da6d5770 (patch) | |
| tree | 1fa77e8bf1662dea936b52107a0d39485110edb1 /net/ipv4 | |
| parent | be1b6e8b5470e8311bfa1a3dfd7bd59e85a99759 (diff) | |
net: separate SIOCGIFCONF handling from dev_ioctl()
Only two of dev_ioctl() callers may pass SIOCGIFCONF to it.
Separating that codepath from the rest of dev_ioctl() allows both
to simplify dev_ioctl() itself (all other cases work with struct ifreq *)
*and* seriously simplify the compat side of that beast: all it takes
is passing to inet_gifconf() an extra argument - the size of individual
records (sizeof(struct ifreq) or sizeof(struct compat_ifreq)). With
dev_ifconf() called directly from sock_do_ioctl()/compat_dev_ifconf()
that's easy to arrange.
As the result, compat side of SIOCGIFCONF doesn't need any
allocations, copy_in_user() back and forth, etc.
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'net/ipv4')
| -rw-r--r-- | net/ipv4/devinet.c | 16 |
1 files changed, 9 insertions, 7 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 7a93359fbc72..1771549d2438 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
| @@ -1188,22 +1188,25 @@ rarok: | |||
| 1188 | goto out; | 1188 | goto out; |
| 1189 | } | 1189 | } |
| 1190 | 1190 | ||
| 1191 | static int inet_gifconf(struct net_device *dev, char __user *buf, int len) | 1191 | static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size) |
| 1192 | { | 1192 | { |
| 1193 | struct in_device *in_dev = __in_dev_get_rtnl(dev); | 1193 | struct in_device *in_dev = __in_dev_get_rtnl(dev); |
| 1194 | struct in_ifaddr *ifa; | 1194 | struct in_ifaddr *ifa; |
| 1195 | struct ifreq ifr; | 1195 | struct ifreq ifr; |
| 1196 | int done = 0; | 1196 | int done = 0; |
| 1197 | 1197 | ||
| 1198 | if (WARN_ON(size > sizeof(struct ifreq))) | ||
| 1199 | goto out; | ||
| 1200 | |||
| 1198 | if (!in_dev) | 1201 | if (!in_dev) |
| 1199 | goto out; | 1202 | goto out; |
| 1200 | 1203 | ||
| 1201 | for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { | 1204 | for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { |
| 1202 | if (!buf) { | 1205 | if (!buf) { |
| 1203 | done += sizeof(ifr); | 1206 | done += size; |
| 1204 | continue; | 1207 | continue; |
| 1205 | } | 1208 | } |
| 1206 | if (len < (int) sizeof(ifr)) | 1209 | if (len < size) |
| 1207 | break; | 1210 | break; |
| 1208 | memset(&ifr, 0, sizeof(struct ifreq)); | 1211 | memset(&ifr, 0, sizeof(struct ifreq)); |
| 1209 | strcpy(ifr.ifr_name, ifa->ifa_label); | 1212 | strcpy(ifr.ifr_name, ifa->ifa_label); |
| @@ -1212,13 +1215,12 @@ static int inet_gifconf(struct net_device *dev, char __user *buf, int len) | |||
| 1212 | (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = | 1215 | (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = |
| 1213 | ifa->ifa_local; | 1216 | ifa->ifa_local; |
| 1214 | 1217 | ||
| 1215 | if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { | 1218 | if (copy_to_user(buf + done, &ifr, size)) { |
| 1216 | done = -EFAULT; | 1219 | done = -EFAULT; |
| 1217 | break; | 1220 | break; |
| 1218 | } | 1221 | } |
| 1219 | buf += sizeof(struct ifreq); | 1222 | len -= size; |
| 1220 | len -= sizeof(struct ifreq); | 1223 | done += size; |
| 1221 | done += sizeof(struct ifreq); | ||
| 1222 | } | 1224 | } |
| 1223 | out: | 1225 | out: |
| 1224 | return done; | 1226 | return done; |
