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