vm: fix potential null deref
[minix.git] / commands / rarpd / rarpd.c
blobd989a5ad958201154cbc0ec8073429138290d062
1 /*
2 rarpd.c
4 Created: Nov 12, 1992 by Philip Homburg
6 Changed: May 13, 1995 by Kees J. Bot
7 Rewrite to handle multiple ethernets.
9 Changed: Jul 18, 1995 by Kees J. Bot
10 Do RARP requests (formerly inet's job)
12 Changed: Dec 14, 1996 by Kees J. Bot
13 Query the netmask
15 Changed: Dec 11, 2000 by Kees J. Bot
16 Dressed down to be only a RARP server, giving the floor to DHCP
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <signal.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/asynchio.h>
29 #include <net/hton.h>
30 #include <net/gen/socket.h>
31 #include <net/gen/netdb.h>
32 #include <net/gen/in.h>
33 #include <net/gen/inet.h>
34 #include <net/gen/ether.h>
35 #include <net/gen/eth_io.h>
36 #include <net/gen/if_ether.h>
37 #include <net/gen/ip_io.h>
38 #include <net/gen/nameser.h>
40 #define MAX_RARP_RETRIES 5
41 #define RARP_TIMEOUT 5
43 #ifdef __NBSD_LIBC
44 #undef HTONS
45 #define HTONS htons
46 #endif
48 typedef struct rarp46
50 ether_addr_t a46_dstaddr;
51 ether_addr_t a46_srcaddr;
52 ether_type_t a46_ethtype;
53 u16_t a46_hdr, a46_pro;
54 u8_t a46_hln, a46_pln;
55 u16_t a46_op;
56 ether_addr_t a46_sha;
57 u8_t a46_spa[4];
58 ether_addr_t a46_tha;
59 u8_t a46_tpa[4];
60 char a46_padding[ETH_MIN_PACK_SIZE - (4*6 + 2*4 + 4*2 + 2*1)];
61 } rarp46_t;
63 #define ETH_RARP_PROTO 0x8035
65 #define RARP_ETHERNET 1
67 #define RARP_REQUEST 3
68 #define RARP_REPLY 4
70 static char *program;
71 static unsigned debug;
73 #define between(a, c, z) ((unsigned) (c) - (a) <= (unsigned) (z) - (a))
75 static void report(const char *label)
77 fprintf(stderr, "%s: %s: %s\n", program, label, strerror(errno));
80 static void fatal(const char *label)
82 report(label);
83 exit(1);
86 static void *allocate(size_t size)
88 void *mem;
90 if ((mem= malloc(size)) == NULL) fatal("Can't allocate memory");
91 return mem;
94 static char *ethdev(int n)
96 static char an_ethdev[]= "/dev/ethNNN";
98 sprintf(an_ethdev + sizeof(an_ethdev)-4, "%d", n);
99 return an_ethdev;
102 static char *ipdev(int n)
104 static char an_ipdev[]= "/dev/ipNNN";
106 sprintf(an_ipdev + sizeof(an_ipdev)-4, "%d", n);
107 return an_ipdev;
110 typedef struct ethernet {
111 int n; /* Network number. */
112 int eth_fd; /* Open low level ethernet device. */
113 ether_addr_t eth_addr; /* Ethernet address of this net. */
114 char packet[ETH_MAX_PACK_SIZE]; /* Incoming packet. */
115 ipaddr_t ip_addr; /* IP address of this net. */
116 ipaddr_t ip_mask; /* Associated netmask. */
117 } ethernet_t;
119 static ethernet_t *ethernets;
121 static void onsig(int sig)
123 switch (sig) {
124 case SIGUSR1: debug++; break;
125 case SIGUSR2: debug= 0; break;
129 static void rarp_reply(ethernet_t *ep, char *hostname, ipaddr_t ip_addr,
130 ether_addr_t eth_addr)
132 rarp46_t rarp46;
134 /* Construct a RARP reply packet and send it. */
135 rarp46.a46_dstaddr= eth_addr;
136 rarp46.a46_hdr= HTONS(RARP_ETHERNET);
137 rarp46.a46_pro= HTONS(ETH_IP_PROTO);
138 rarp46.a46_hln= 6;
139 rarp46.a46_pln= 4;
140 rarp46.a46_op= HTONS(RARP_REPLY);
141 rarp46.a46_sha= ep->eth_addr;
142 memcpy(rarp46.a46_spa, &ep->ip_addr, sizeof(ipaddr_t));
143 rarp46.a46_tha= eth_addr;
144 memcpy(rarp46.a46_tpa, &ip_addr, sizeof(ipaddr_t));
146 if (debug >= 1) {
147 printf("%s: Replying %s (%s) to %s\n",
148 ethdev(ep->n), inet_ntoa(ip_addr), hostname, ether_ntoa(&eth_addr));
150 (void) write(ep->eth_fd, &rarp46, sizeof(rarp46));
153 static int addhostname(char *addname, char *hostname, int n)
155 /* Create an additional hostname for a given hostname by adding "-n" to
156 * the first part. E.g. given "wombat.cs.vu.nl" and n=2 return
157 * "wombat-2.cs.vu.nl". This is useful for VU practical work where
158 * people get a few extra ethernet addresses on a machine and are asked
159 * to build a TCP/IP stack on it.
161 char *dot;
163 if (strlen(hostname) + 4 >= 1024) return 0;
164 if ((dot= strchr(hostname, '.')) == NULL) dot= strchr(hostname, 0);
165 sprintf(addname, "%.*s-%d%s", (dot - hostname), hostname, n, dot);
166 return 1;
169 static void usage(void)
171 fprintf(stderr, "Usage: %s [-d[level]] network-name ...\n", program);
172 exit(1);
175 static int ifname2n(const char *name)
177 /* Translate an interface name, ip0, ip1, etc, to a number. */
178 const char *np;
179 char *end;
180 unsigned long n;
182 np= name;
183 if (*np++ != 'i' || *np++ != 'p') usage();
184 n= strtoul(np, &end, 10);
185 if (end == np || *end != 0) usage();
186 if (n >= 1000) {
187 fprintf(stderr, "%s: Network number of \"%s\" is a bit large\n",
188 program, name);
189 exit(1);
191 return n;
194 int main(int argc, char **argv)
196 int i;
197 ethernet_t *ep;
198 nwio_ethopt_t ethopt;
199 nwio_ethstat_t ethstat;
200 char hostname[1024];
201 struct hostent *hostent;
202 struct sigaction sa;
203 nwio_ipconf_t ipconf;
204 asynchio_t asyn;
205 ssize_t n;
206 ipaddr_t ip_addr;
207 rarp46_t rarp46;
208 int fd;
209 int n_eths;
211 program= argv[0];
212 asyn_init(&asyn);
214 debug= 0;
215 i= 1;
216 while (i < argc && argv[i][0] == '-') {
217 char *opt= argv[i++]+1;
219 if (opt[0] == '-' && opt[1] == 0) break; /* -- */
221 while (*opt != 0) switch (*opt++) {
222 case 'd':
223 debug= 1;
224 if (between('0', *opt, '9')) debug= strtoul(opt, &opt, 10);
225 break;
226 default:
227 usage();
231 if ((n_eths= (argc - i)) == 0) usage();
233 #if __minix_vmd
234 /* Minix-vmd can handle all nets at once using async I/O. */
235 ethernets= allocate(n_eths * sizeof(ethernets[0]));
236 for (i= 0; i < n_eths; i++) {
237 ethernets[i].n= ifname2n(argv[argc - n_eths + i]);
239 #else
240 /* Minix forks n-1 times to handle each net in a process each. */
241 for (i= 0; i < n_eths; i++) {
242 if (i+1 < n_eths) {
243 switch (fork()) {
244 case -1: fatal("fork()");
245 case 0: break;
246 default: continue;
249 ethernets= allocate(1 * sizeof(ethernets[0]));
250 ethernets[0].n= ifname2n(argv[argc - n_eths + i]);
252 n_eths= 1;
253 #endif
255 sa.sa_handler= onsig;
256 sigemptyset(&sa.sa_mask);
257 sa.sa_flags= 0;
258 sigaction(SIGUSR1, &sa, NULL);
259 sigaction(SIGUSR2, &sa, NULL);
261 for (i= 0; i < n_eths; i++) {
262 ep= &ethernets[i];
263 if ((ep->eth_fd= open(ethdev(ep->n), O_RDWR)) < 0) fatal(ethdev(ep->n));
265 if (ioctl(ep->eth_fd, NWIOGETHSTAT, &ethstat) < 0) {
266 fprintf(stderr, "%s: %s: Unable to get eth statistics: %s\n",
267 program, ethdev(ep->n), strerror(errno));
268 exit(1);
270 ep->eth_addr= ethstat.nwes_addr;
271 if (debug >= 1) {
272 printf("%s: Ethernet address is %s\n",
273 ethdev(ep->n), ether_ntoa(&ep->eth_addr));
276 ethopt.nweo_flags= NWEO_COPY | NWEO_EN_LOC | NWEO_EN_BROAD |
277 NWEO_TYPESPEC;
278 ethopt.nweo_type= HTONS(ETH_RARP_PROTO);
280 if (ioctl(ep->eth_fd, NWIOSETHOPT, &ethopt) < 0) {
281 fprintf(stderr, "%s: %s: Unable to set eth options: %s\n",
282 program, ethdev(ep->n), strerror(errno));
283 exit(1);
286 /* What are my address and netmask? */
287 if ((fd= open(ipdev(ep->n), O_RDWR)) < 0) fatal(ipdev(ep->n));
288 if (ioctl(fd, NWIOGIPCONF, &ipconf) < 0) fatal(ipdev(ep->n));
290 ep->ip_addr= ipconf.nwic_ipaddr;
291 ep->ip_mask= ipconf.nwic_netmask;
292 close(fd);
293 if (debug >= 1) {
294 printf("%s: IP address is %s / ",
295 ipdev(ep->n), inet_ntoa(ep->ip_addr));
296 printf("%s\n", inet_ntoa(ep->ip_mask));
300 /* Wait for RARP requests, reply, repeat. */
301 for(;;) {
302 fflush(NULL);
304 /* Wait for a RARP request. */
305 for (i= 0; i < n_eths; i++) {
306 ep= &ethernets[i];
308 n= asyn_read(&asyn, ep->eth_fd, ep->packet, sizeof(ep->packet));
309 if (n != -1) break;
310 if (errno != EINPROGRESS) {
311 report(ethdev(ep->n));
312 sleep(10);
316 /* RARP request? */
317 if (i < n_eths
318 && n >= sizeof(rarp46)
319 && (memcpy(&rarp46, ep->packet, sizeof(rarp46)), 1)
320 && rarp46.a46_hdr == HTONS(RARP_ETHERNET)
321 && rarp46.a46_pro == HTONS(ETH_IP_PROTO)
322 && rarp46.a46_hln == 6
323 && rarp46.a46_pln == 4
324 && rarp46.a46_op == HTONS(RARP_REQUEST)
326 if ((ether_ntohost(hostname, &rarp46.a46_tha) == 0
327 || (rarp46.a46_tha.ea_addr[0] == 'v'
328 && (memcpy(&ip_addr, rarp46.a46_tha.ea_addr+2, 4), 1)
329 && (hostent= gethostbyaddr((char*) &ip_addr,
330 4, AF_INET)) != NULL
331 && addhostname(hostname, hostent->h_name,
332 rarp46.a46_tha.ea_addr[1])))
333 && (hostent= gethostbyname(hostname)) != NULL
334 && hostent->h_addrtype == AF_INET
336 /* Host is found in the ethers file and the DNS, or the
337 * ethernet address denotes a special additional address
338 * used for implementing a TCP/IP stack in user space.
340 for (i= 0; hostent->h_addr_list[i] != NULL; i++) {
341 memcpy(&ip_addr, hostent->h_addr_list[i], sizeof(ipaddr_t));
343 /* Check if the address is on this network. */
344 if (((ip_addr ^ ep->ip_addr) & ep->ip_mask) == 0) break;
347 if (hostent->h_addr_list[i] != NULL) {
348 rarp_reply(ep, hostname, ip_addr, rarp46.a46_tha);
349 } else {
350 if (debug >= 2) {
351 printf("%s: Host '%s' (%s) is on the wrong net\n",
352 ethdev(ep->n),
353 hostname, ether_ntoa(&rarp46.a46_tha));
356 } else {
357 if (debug >= 2) {
358 printf("%s: RARP request from unknown host '%s'\n",
359 ethdev(ep->n), ether_ntoa(&rarp46.a46_tha));
364 /* Wait for another request. */
365 if (asyn_wait(&asyn, 0, NULL) < 0) {
366 report("asyn_wait()");
367 sleep(10);