coverity appeasement
[minix.git] / commands / dhcpd / devices.c
blob7b095372db657f42399e4b81eca3f6493fabf43f
1 /* devices.c - Handle network devices.
2 * Author: Kees J. Bot
3 * 11 Jun 1999
4 */
5 #include <sys/types.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <errno.h>
11 #include <string.h>
12 #include <limits.h>
13 #include <signal.h>
14 #include <time.h>
15 #include <sys/ioctl.h>
16 #include <sys/asynchio.h>
17 #include <net/hton.h>
18 #include <net/gen/in.h>
19 #include <netinet/in.h>
20 #include <sys/socket.h>
21 #include <net/gen/ether.h>
22 #include <net/gen/eth_hdr.h>
23 #include <net/gen/eth_io.h>
24 #include <net/gen/ip_hdr.h>
25 #include <net/gen/ip_io.h>
26 #include <net/gen/udp.h>
27 #include <net/gen/udp_hdr.h>
28 #include <net/gen/udp_io.h>
29 #include <net/gen/dhcp.h>
30 #include "dhcpd.h"
32 void get_buf(buf_t **bp)
34 /* Allocate and return a buffer pointer iff *bp == nil. */
35 if (*bp != nil) {
36 /* Already has one. */
37 } else {
38 /* Get one from the heap. */
39 buf_t *new= allocate(sizeof(*new));
40 new->dhcp= (dhcp_t *) (new->buf + sizeof(eth_hdr_t)
41 + sizeof(ip_hdr_t) + sizeof(udp_hdr_t));
42 new->udpio= ((udp_io_hdr_t *) new->dhcp) - 1;
43 new->udp= ((udp_hdr_t *) new->dhcp) - 1;
44 new->ip= ((ip_hdr_t *) new->udp) - 1;
45 new->eth= ((eth_hdr_t *) new->ip) - 1;
46 *bp= new;
50 void put_buf(buf_t **bp)
52 /* Return a buffer to the heap. */
53 if (*bp != nil) {
54 free(*bp);
55 *bp= nil;
59 void give_buf(buf_t **dbp, buf_t **sbp)
61 /* Hand over a buffer to another variable. */
62 put_buf(dbp);
63 *dbp= *sbp;
64 *sbp= nil;
67 #define N_FDS 16 /* Minix can go async on many fds. */
69 static fd_t fds[N_FDS]; /* List of open descriptors. */
70 static struct network *fdwaitq; /* Queue of nets waiting for fds. */
72 network_t *newnetwork(void)
74 /* Create and initialize a network structure. */
75 network_t *new;
77 new= allocate(sizeof(*new));
78 memset(new, 0, sizeof(*new));
79 new->hostname= nil;
80 new->solicit= NEVER;
81 new->sol_ct= -1;
82 return new;
85 void closefd(fd_t *fdp)
87 /* Close a descriptor. */
88 if (fdp->fdtype != FT_CLOSED) {
89 asyn_close(&asyn, fdp->fd);
90 close(fdp->fd);
91 fdp->fdtype= FT_CLOSED;
92 fdp->since= 0;
93 put_buf(&fdp->bp);
94 if (debug >= 3) printf("%s: Closed\n", fdp->device);
98 static void timeout(int signum)
100 /* nothing to do, ioctl will be aborted automatically */
101 if (alarm(1) < 0) fatal("alarm(1)");
104 int opendev(network_t *np, fdtype_t fdtype, int compete)
106 /* Make sure that a network has the proper device open and configured.
107 * Return true if this is made so, or false if the device doesn't exist.
108 * If compete is true then the caller competes for old descriptors.
109 * The errno value is EAGAIN if we're out of descriptors.
111 fd_t *fdp, *fdold;
112 time_t oldest;
113 nwio_ethstat_t ethstat;
114 nwio_ethopt_t ethopt;
115 nwio_ipopt_t ipopt;
116 nwio_udpopt_t udpopt;
117 network_t **pqp;
118 static char devbytype[][4] = { "", "eth", "ip", "udp", "udp" };
120 /* Don't attempt to open higher level devices if not bound. */
121 if (!(np->flags & NF_BOUND) && fdtype > FT_ETHERNET) {
122 errno= EAGAIN;
123 return 0;
126 /* Check if already open / Find the oldest descriptor. */
127 fdold= nil;
128 oldest= NEVER;
129 for (fdp= fds; fdp < arraylimit(fds); fdp++) {
130 if (fdp->n == np->n && fdp->fdtype == fdtype) {
131 /* Already open. */
132 np->fdp= fdp;
133 return 1;
135 if (fdp->since <= oldest) { fdold= fdp; oldest= fdp->since; }
138 /* None free? Then wait for one to get old if so desired. */
139 if (fdold->fdtype != FT_CLOSED && !compete) {
140 errno= EAGAIN;
141 return 0;
144 if (!(np->flags & NF_WAIT)) {
145 for (pqp= &fdwaitq; *pqp != nil; pqp= &(*pqp)->wait) {}
146 *pqp= np;
147 np->wait= nil;
148 np->flags |= NF_WAIT;
151 /* We allow a net to keep a descriptor for half of the fast period. */
152 oldest += DELTA_FAST/2;
154 if (fdwaitq != np || (fdold->fdtype != FT_CLOSED && oldest > now)) {
155 /* This net is not the first in the queue, or the oldest isn't
156 * old enough. Forget it for now.
158 if (oldest < event) event= oldest;
159 errno= EAGAIN;
160 return 0;
163 /* The oldest is mine. */
164 np->flags &= ~NF_WAIT;
165 fdwaitq= np->wait;
166 closefd(fdold);
168 /* Open the proper device in the proper mode. */
169 fdp= fdold;
170 fdp->n= np->n;
171 if (lwip && (fdtype == FT_ETHERNET || fdtype == FT_ICMP))
172 sprintf(fdp->device, "/dev/ip");
173 else
174 sprintf(fdp->device, "/dev/%s%d", devbytype[fdtype], np->n);
175 np->fdp= fdp;
177 if ((fdp->fd= open(fdp->device, O_RDWR)) < 0) {
178 if (errno == ENOENT || errno == ENODEV || errno == ENXIO) return 0;
179 fatal(fdp->device);
182 switch (fdtype) {
183 case FT_ETHERNET:
184 if (lwip) {
185 nwio_ipopt_t ipopt;
186 int result;
187 char ethdev[64];
188 int efd;
190 sprintf(ethdev, "/dev/eth%d", np->n);
192 if ((efd = open(fdp->device, O_RDWR)) < 0) {
193 if (errno == ENOENT || errno == ENODEV ||
194 errno == ENXIO)
195 return 0;
196 fatal(ethdev);
199 if (ioctl(efd, NWIOGETHSTAT, &ethstat) < 0) {
200 /* Not an Ethernet. */
201 close(efd);
202 return 0;
204 close(efd);
206 np->eth= ethstat.nwes_addr;
208 ipopt.nwio_flags= NWIO_COPY | NWIO_PROTOSPEC;
209 ipopt.nwio_proto= 17; /* UDP */
210 result= ioctl (fdp->fd, NWIOSIPOPT, &ipopt);
211 if (result<0)
212 perror("ioctl (NWIOSIPOPT)"), exit(1);
214 break;
216 /* Cannot use NWIOGETHSTAT in non-blocking mode due to a race between
217 * the reply from the ethernet driver and the cancel message from VFS
218 * for reaching inet. Hence, a signal is used to interrupt NWIOGETHSTAT
219 * in case the driver isn't ready yet.
221 if (signal(SIGALRM, timeout) == SIG_ERR) fatal("signal(SIGALRM)");
222 if (alarm(1) < 0) fatal("alarm(1)");
223 if (ioctl(np->fdp->fd, NWIOGETHSTAT, &ethstat) < 0) {
224 /* Not an Ethernet. */
225 close(fdp->fd);
226 return 0;
228 if (alarm(0) < 0) fatal("alarm(0)");
229 np->eth= ethstat.nwes_addr;
230 ethopt.nweo_flags= NWEO_COPY | NWEO_EN_LOC | NWEO_EN_BROAD
231 | NWEO_REMANY | NWEO_TYPEANY | NWEO_RWDATALL;
233 if (ioctl(fdp->fd, NWIOSETHOPT, &ethopt) < 0) {
234 fprintf(stderr, "%s: %s: Unable to set eth options: %s\n",
235 program, fdp->device, strerror(errno));
236 exit(1);
238 break;
240 case FT_ICMP:
241 ipopt.nwio_flags= NWIO_COPY | NWIO_EN_LOC | NWIO_EN_BROAD
242 | NWIO_REMANY | NWIO_PROTOSPEC
243 | NWIO_HDR_O_SPEC | NWIO_RWDATALL;
244 ipopt.nwio_tos= 0;
245 ipopt.nwio_ttl= 1;
246 ipopt.nwio_df= 0;
247 ipopt.nwio_hdropt.iho_opt_siz= 0;
248 ipopt.nwio_proto= IPPROTO_ICMP;
250 if (ioctl(fdp->fd, NWIOSIPOPT, &ipopt) < 0) {
251 fprintf(stderr, "%s: %s: Unable to set IP options: %s\n",
252 program, fdp->device, strerror(errno));
253 exit(1);
255 break;
257 case FT_BOOTPC:
258 if (lwip) {
259 struct sockaddr_in si_me;
261 close(fdp->fd);
262 fdp->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
263 if (fdp->fd < 0)
264 return 0;
265 memset((char *) &si_me, 0, sizeof(si_me));
266 si_me.sin_family = AF_INET;
267 si_me.sin_addr.s_addr = htonl(INADDR_ANY);
268 si_me.sin_port = htons(port_client);
269 if (bind(fdp->fd, (struct sockaddr *) &si_me,
270 sizeof(si_me)) == -1) {
271 close(fdp->fd);
272 printf("DHCP : cannot bind client socket to port %d\n",
273 port_client);
274 return 0;
276 break;
278 udpopt.nwuo_flags= NWUO_COPY | NWUO_EN_LOC | NWUO_EN_BROAD
279 | NWUO_RP_ANY | NWUO_RA_ANY | NWUO_RWDATALL
280 | NWUO_DI_IPOPT | NWUO_LP_SET;
281 udpopt.nwuo_locport= port_client;
282 goto udp;
284 case FT_BOOTPS:
285 udpopt.nwuo_flags= NWUO_EXCL | NWUO_EN_LOC | NWUO_EN_BROAD
286 | NWUO_RP_ANY | NWUO_RA_ANY | NWUO_RWDATALL
287 | NWUO_DI_IPOPT | NWUO_LP_SET;
288 udpopt.nwuo_locport= port_server;
289 udp:
290 if (ioctl(fdp->fd, NWIOSUDPOPT, &udpopt) == -1) {
291 fprintf(stderr, "%s: %s: Unable to set UDP options: %s\n",
292 program, fdp->device, strerror(errno));
293 exit(1);
295 break;
297 default:;
300 fdp->fdtype= fdtype;
301 fdp->since= now;
302 if (debug >= 3) printf("%s: Opened\n", fdp->device);
303 return 1;
306 void closedev(network_t *np, fdtype_t fdtype)
308 /* We no longer need a given type of device to be open. */
309 fd_t *fdp;
311 for (fdp= fds; fdp < arraylimit(fds); fdp++) {
312 if (fdp->n == np->n && (fdp->fdtype == fdtype || fdtype == FT_ALL)) {
313 closefd(fdp);
318 char *ipdev(int n)
320 /* IP device for network #n. */
321 static char device[sizeof("/dev/ipNNN")];
323 sprintf(device, "/dev/ip%d", n);
324 return device;
327 void set_ipconf(char *device, ipaddr_t ip, ipaddr_t mask, unsigned mtu)
329 /* Set IP address and netmask of an IP device. */
330 int fd;
331 nwio_ipconf_t ipconf;
333 if (test > 0) return;
335 if ((fd= open(device, O_RDWR)) < 0) fatal(device);
336 ipconf.nwic_flags= NWIC_IPADDR_SET | NWIC_NETMASK_SET;
337 ipconf.nwic_ipaddr= ip;
338 ipconf.nwic_netmask= mask;
339 #ifdef NWIC_MTU_SET
340 if (mtu != 0) {
341 ipconf.nwic_flags |= NWIC_MTU_SET;
342 ipconf.nwic_mtu= mtu;
344 #endif
345 if (ioctl(fd, NWIOSIPCONF, &ipconf) < 0) fatal(device);
346 close(fd);