1/* See LICENSE file for copyright and license details. */
2
3#include <arpa/inet.h>
4#include <ctype.h>
5#include <errno.h>
6#include <net/if.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/ioctl.h>
11#include <sys/socket.h>
12#include <sys/types.h>
13#include <unistd.h>
14
15#include "util.h"
16
17int net_get_interfaces(struct NetInterface **, int *);
18int net_get_stats(const char *, struct NetStats *);
19int net_set_txqueuelen(const char *, int);
20
21static void
22usage(void)
23{
24 eprintf("usage: %s [-a] [interface [action ...]]\n", argv0);
25}
26
27static void
28display_interface(const struct NetInterface *iface)
29{
30 struct NetStats stats;
31 char ipv6_str[INET6_ADDRSTRLEN];
32
33 printf("%-9s Link encap:", iface->name);
34 if (iface->has_mac) {
35 printf(
36 "Ethernet HWaddr %02x:%02x:%02x:%02x:%02x:%02x\n",
37 iface->mac[0],
38 iface->mac[1],
39 iface->mac[2],
40 iface->mac[3],
41 iface->mac[4],
42 iface->mac[5]
43 );
44 } else if (iface->flags & IFF_LOOPBACK) {
45 printf("Local Loopback\n");
46 } else {
47 printf("UNSPEC\n");
48 }
49
50 if (iface->has_ipv4) {
51 printf(" inet addr:%s", inet_ntoa(iface->ipv4_addr.sin_addr));
52 printf(" Bcast:%s", inet_ntoa(iface->ipv4_brd.sin_addr));
53 printf(" Mask:%s\n", inet_ntoa(iface->ipv4_mask.sin_addr));
54 }
55
56 if (iface->has_ipv6) {
57 if (inet_ntop(AF_INET6, &iface->ipv6_addr.sin6_addr, ipv6_str, sizeof(ipv6_str))) {
58 printf(" inet6 addr: %s\n", ipv6_str);
59 }
60 }
61
62 printf(" ");
63 if (iface->flags & IFF_UP)
64 printf("UP ");
65 if (iface->flags & IFF_BROADCAST)
66 printf("BROADCAST ");
67 if (iface->flags & IFF_DEBUG)
68 printf("DEBUG ");
69 if (iface->flags & IFF_LOOPBACK)
70 printf("LOOPBACK ");
71 if (iface->flags & IFF_POINTOPOINT)
72 printf("POINTOPOINT ");
73 if (iface->flags & IFF_RUNNING)
74 printf("RUNNING ");
75 if (iface->flags & IFF_NOARP)
76 printf("NOARP ");
77 if (iface->flags & IFF_PROMISC)
78 printf("PROMISC ");
79 if (iface->flags & IFF_ALLMULTI)
80 printf("ALLMULTI ");
81 if (iface->flags & IFF_MULTICAST)
82 printf("MULTICAST ");
83
84 printf(" MTU:%d Metric:%d\n", iface->mtu, iface->metric);
85
86 if (net_get_stats(iface->name, &stats) >= 0) {
87 printf(
88 " RX packets:%llu errors:%llu dropped:%llu "
89 "overruns:0 frame:0\n",
90 stats.rx_packets,
91 stats.rx_errs,
92 stats.rx_drop
93 );
94 printf(
95 " TX packets:%llu errors:%llu dropped:%llu "
96 "overruns:0 carrier:0\n",
97 stats.tx_packets,
98 stats.tx_errs,
99 stats.tx_drop
100 );
101 printf(" RX bytes:%llu TX bytes:%llu\n", stats.rx_bytes, stats.tx_bytes);
102 }
103 printf("\n");
104}
105
106static void
107list_interfaces(int all)
108{
109 struct NetInterface *ifaces = NULL;
110 int count = 0;
111 int i;
112
113 if (net_get_interfaces(&ifaces, &count) < 0)
114 eprintf("net_get_interfaces:");
115
116 for (i = 0; i < count; i++) {
117 if (all || (ifaces[i].flags & IFF_UP)) {
118 display_interface(&ifaces[i]);
119 }
120 }
121 free(ifaces);
122}
123
124// ?man ifconfig: configure network interfaces
125// ?man arguments: interface [action ...]
126// ?man configure network interface parameters and view stats
127int
128main(int argc, char *argv[])
129{
130 struct NetInterface *ifaces = NULL;
131 const struct NetInterface *iface = NULL;
132 struct ifreq ifr;
133 struct sockaddr_in *sin;
134 char *name;
135 char *arg;
136 char *slash;
137 int aflag = 0;
138 int sock = -1;
139 int i = 1;
140 int prefix;
141 int count = 0;
142 unsigned int mask;
143
144 ARGBEGIN
145 {
146 // ?man -a: print or show all entries
147 case 'a':
148 aflag = 1;
149 break;
150 default:
151 usage();
152 }
153 ARGEND
154
155 if (argc == 0) {
156 list_interfaces(aflag);
157 return 0;
158 }
159
160 name = argv[0];
161
162 if (net_get_interfaces(&ifaces, &count) < 0)
163 eprintf("net_get_interfaces:");
164
165 for (prefix = 0; prefix < count; prefix++) {
166 if (strcmp(ifaces[prefix].name, name) == 0) {
167 iface = &ifaces[prefix];
168 break;
169 }
170 }
171
172 if (argc == 1) {
173 if (!iface)
174 eprintf("interface %s not found\n", name);
175 display_interface(iface);
176 free(ifaces);
177 return 0;
178 }
179
180 sock = socket(AF_INET, SOCK_DGRAM, 0);
181 if (sock < 0)
182 eprintf("socket:");
183
184 memset(&ifr, 0, sizeof(ifr));
185 strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
186
187 while (i < argc) {
188 arg = argv[i];
189
190 if (strcmp(arg, "up") == 0) {
191 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
192 eprintf("ioctl SIOCGIFFLAGS:");
193 ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
194 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
195 eprintf("ioctl SIOCSIFFLAGS:");
196 i++;
197 } else if (strcmp(arg, "down") == 0) {
198 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
199 eprintf("ioctl SIOCGIFFLAGS:");
200 ifr.ifr_flags &= ~IFF_UP;
201 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
202 eprintf("ioctl SIOCSIFFLAGS:");
203 i++;
204 } else if (strcmp(arg, "netmask") == 0) {
205 if (i + 1 >= argc)
206 eprintf("netmask needs an address\n");
207 sin = (struct sockaddr_in *)&ifr.ifr_netmask;
208 sin->sin_family = AF_INET;
209 if (inet_pton(AF_INET, argv[i + 1], &sin->sin_addr) <= 0)
210 eprintf("invalid address: %s\n", argv[i + 1]);
211 if (ioctl(sock, SIOCSIFNETMASK, &ifr) < 0)
212 eprintf("ioctl SIOCSIFNETMASK:");
213 i += 2;
214 } else if (strcmp(arg, "broadcast") == 0) {
215 if (i + 1 >= argc)
216 eprintf("broadcast needs an address\n");
217 sin = (struct sockaddr_in *)&ifr.ifr_broadaddr;
218 sin->sin_family = AF_INET;
219 if (inet_pton(AF_INET, argv[i + 1], &sin->sin_addr) <= 0)
220 eprintf("invalid address: %s\n", argv[i + 1]);
221 if (ioctl(sock, SIOCSIFBRDADDR, &ifr) < 0)
222 eprintf("ioctl SIOCSIFBRDADDR:");
223 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
224 eprintf("ioctl SIOCGIFFLAGS:");
225 ifr.ifr_flags |= IFF_BROADCAST;
226 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
227 eprintf("ioctl SIOCSIFFLAGS:");
228 i += 2;
229 } else if (strcmp(arg, "mtu") == 0) {
230 if (i + 1 >= argc)
231 eprintf("mtu needs a value\n");
232 ifr.ifr_mtu = atoi(argv[i + 1]);
233 if (ioctl(sock, SIOCSIFMTU, &ifr) < 0)
234 eprintf("ioctl SIOCSIFMTU:");
235 i += 2;
236 } else if (strcmp(arg, "metric") == 0) {
237 if (i + 1 >= argc)
238 eprintf("metric needs a value\n");
239 ifr.ifr_metric = atoi(argv[i + 1]);
240 if (ioctl(sock, SIOCSIFMETRIC, &ifr) < 0)
241 eprintf("ioctl SIOCSIFMETRIC:");
242 i += 2;
243 } else if (strcmp(arg, "txqueuelen") == 0) {
244 if (i + 1 >= argc)
245 eprintf("txqueuelen needs a value\n");
246 if (net_set_txqueuelen(name, atoi(argv[i + 1])) < 0)
247 eprintf("net_set_txqueuelen:");
248 i += 2;
249 } else if (strcmp(arg, "dstaddr") == 0 || strcmp(arg, "pointopoint") == 0) {
250 if (i + 1 >= argc)
251 eprintf("%s needs an address\n", arg);
252 sin = (struct sockaddr_in *)&ifr.ifr_dstaddr;
253 sin->sin_family = AF_INET;
254 if (inet_pton(AF_INET, argv[i + 1], &sin->sin_addr) <= 0)
255 eprintf("invalid address: %s\n", argv[i + 1]);
256 if (ioctl(sock, SIOCSIFDSTADDR, &ifr) < 0)
257 eprintf("ioctl SIOCSIFDSTADDR:");
258 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
259 eprintf("ioctl SIOCGIFFLAGS:");
260 ifr.ifr_flags |= IFF_POINTOPOINT;
261 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
262 eprintf("ioctl SIOCSIFFLAGS:");
263 i += 2;
264 } else if (strcmp(arg, "arp") == 0) {
265 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
266 eprintf("ioctl SIOCGIFFLAGS:");
267 ifr.ifr_flags &= ~IFF_NOARP;
268 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
269 eprintf("ioctl SIOCSIFFLAGS:");
270 i++;
271 } else if (strcmp(arg, "-arp") == 0) {
272 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
273 eprintf("ioctl SIOCGIFFLAGS:");
274 ifr.ifr_flags |= IFF_NOARP;
275 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
276 eprintf("ioctl SIOCSIFFLAGS:");
277 i++;
278 } else if (strcmp(arg, "promisc") == 0) {
279 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
280 eprintf("ioctl SIOCGIFFLAGS:");
281 ifr.ifr_flags |= IFF_PROMISC;
282 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
283 eprintf("ioctl SIOCSIFFLAGS:");
284 i++;
285 } else if (strcmp(arg, "-promisc") == 0) {
286 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
287 eprintf("ioctl SIOCGIFFLAGS:");
288 ifr.ifr_flags &= ~IFF_PROMISC;
289 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
290 eprintf("ioctl SIOCSIFFLAGS:");
291 i++;
292 } else if (strcmp(arg, "allmulti") == 0) {
293 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
294 eprintf("ioctl SIOCGIFFLAGS:");
295 ifr.ifr_flags |= IFF_ALLMULTI;
296 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
297 eprintf("ioctl SIOCSIFFLAGS:");
298 i++;
299 } else if (strcmp(arg, "-allmulti") == 0) {
300 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
301 eprintf("ioctl SIOCGIFFLAGS:");
302 ifr.ifr_flags &= ~IFF_ALLMULTI;
303 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
304 eprintf("ioctl SIOCSIFFLAGS:");
305 i++;
306 } else if (strcmp(arg, "multicast") == 0) {
307 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
308 eprintf("ioctl SIOCGIFFLAGS:");
309 ifr.ifr_flags |= IFF_MULTICAST;
310 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
311 eprintf("ioctl SIOCSIFFLAGS:");
312 i++;
313 } else if (strcmp(arg, "-multicast") == 0) {
314 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
315 eprintf("ioctl SIOCGIFFLAGS:");
316 ifr.ifr_flags &= ~IFF_MULTICAST;
317 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
318 eprintf("ioctl SIOCSIFFLAGS:");
319 i++;
320 } else {
321 sin = (struct sockaddr_in *)&ifr.ifr_addr;
322 sin->sin_family = AF_INET;
323 slash = strchr(arg, '/');
324 prefix = -1;
325 if (slash) {
326 *slash = '\0';
327 prefix = atoi(slash + 1);
328 }
329 if (inet_pton(AF_INET, arg, &sin->sin_addr) <= 0) {
330 eprintf(
331 "unknown action or invalid address: "
332 "%s\n",
333 arg
334 );
335 }
336 if (ioctl(sock, SIOCSIFADDR, &ifr) < 0)
337 eprintf("ioctl SIOCSIFADDR:");
338
339 if (prefix >= 0) {
340 mask = 0;
341 if (prefix > 0)
342 mask = htonl(~0U << (32 - prefix));
343 sin = (struct sockaddr_in *)&ifr.ifr_netmask;
344 sin->sin_family = AF_INET;
345 sin->sin_addr.s_addr = mask;
346 if (ioctl(sock, SIOCSIFNETMASK, &ifr) < 0)
347 eprintf("ioctl SIOCSIFNETMASK:");
348 }
349
350 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
351 eprintf("ioctl SIOCGIFFLAGS:");
352 ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
353 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
354 eprintf("ioctl SIOCSIFFLAGS:");
355
356 i++;
357 }
358 }
359
360 free(ifaces);
361 close(sock);
362
363 if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
364 return 1;
365
366 return 0;
367}