1 /* devices.c - Handle network devices.
15 #include <sys/ioctl.h>
16 #include <sys/asynchio.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>
32 void get_buf(buf_t
**bp
)
34 /* Allocate and return a buffer pointer iff *bp == nil. */
36 /* Already has one. */
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;
50 void put_buf(buf_t
**bp
)
52 /* Return a buffer to the heap. */
59 void give_buf(buf_t
**dbp
, buf_t
**sbp
)
61 /* Hand over a buffer to another variable. */
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. */
77 new= allocate(sizeof(*new));
78 memset(new, 0, sizeof(*new));
85 void closefd(fd_t
*fdp
)
87 /* Close a descriptor. */
88 if (fdp
->fdtype
!= FT_CLOSED
) {
89 asyn_close(&asyn
, fdp
->fd
);
91 fdp
->fdtype
= FT_CLOSED
;
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.
113 nwio_ethstat_t ethstat
;
114 nwio_ethopt_t ethopt
;
116 nwio_udpopt_t udpopt
;
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
) {
126 /* Check if already open / Find the oldest descriptor. */
129 for (fdp
= fds
; fdp
< arraylimit(fds
); fdp
++) {
130 if (fdp
->n
== np
->n
&& fdp
->fdtype
== fdtype
) {
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
) {
144 if (!(np
->flags
& NF_WAIT
)) {
145 for (pqp
= &fdwaitq
; *pqp
!= nil
; pqp
= &(*pqp
)->wait
) {}
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
;
163 /* The oldest is mine. */
164 np
->flags
&= ~NF_WAIT
;
168 /* Open the proper device in the proper mode. */
171 if (lwip
&& (fdtype
== FT_ETHERNET
|| fdtype
== FT_ICMP
))
172 sprintf(fdp
->device
, "/dev/ip");
174 sprintf(fdp
->device
, "/dev/%s%d", devbytype
[fdtype
], np
->n
);
177 if ((fdp
->fd
= open(fdp
->device
, O_RDWR
)) < 0) {
178 if (errno
== ENOENT
|| errno
== ENODEV
|| errno
== ENXIO
) return 0;
190 sprintf(ethdev
, "/dev/eth%d", np
->n
);
192 if ((efd
= open(fdp
->device
, O_RDWR
)) < 0) {
193 if (errno
== ENOENT
|| errno
== ENODEV
||
199 if (ioctl(efd
, NWIOGETHSTAT
, ðstat
) < 0) {
200 /* Not an Ethernet. */
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
);
212 perror("ioctl (NWIOSIPOPT)"), exit(1);
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
, ðstat
) < 0) {
224 /* Not an Ethernet. */
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
, ðopt
) < 0) {
234 fprintf(stderr
, "%s: %s: Unable to set eth options: %s\n",
235 program
, fdp
->device
, strerror(errno
));
241 ipopt
.nwio_flags
= NWIO_COPY
| NWIO_EN_LOC
| NWIO_EN_BROAD
242 | NWIO_REMANY
| NWIO_PROTOSPEC
243 | NWIO_HDR_O_SPEC
| NWIO_RWDATALL
;
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
));
259 struct sockaddr_in si_me
;
262 fdp
->fd
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
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) {
272 printf("DHCP : cannot bind client socket to port %d\n",
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
;
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
;
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
));
302 if (debug
>= 3) printf("%s: Opened\n", fdp
->device
);
306 void closedev(network_t
*np
, fdtype_t fdtype
)
308 /* We no longer need a given type of device to be open. */
311 for (fdp
= fds
; fdp
< arraylimit(fds
); fdp
++) {
312 if (fdp
->n
== np
->n
&& (fdp
->fdtype
== fdtype
|| fdtype
== FT_ALL
)) {
320 /* IP device for network #n. */
321 static char device
[sizeof("/dev/ipNNN")];
323 sprintf(device
, "/dev/ip%d", n
);
327 void set_ipconf(char *device
, ipaddr_t ip
, ipaddr_t mask
, unsigned mtu
)
329 /* Set IP address and netmask of an IP device. */
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
;
341 ipconf
.nwic_flags
|= NWIC_MTU_SET
;
342 ipconf
.nwic_mtu
= mtu
;
345 if (ioctl(fd
, NWIOSIPCONF
, &ipconf
) < 0) fatal(device
);