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 <time.h>
|
---|
14 | #include <sys/ioctl.h>
|
---|
15 | #include <sys/asynchio.h>
|
---|
16 | #include <net/hton.h>
|
---|
17 | #include <net/gen/in.h>
|
---|
18 | #include <net/gen/ether.h>
|
---|
19 | #include <net/gen/eth_hdr.h>
|
---|
20 | #include <net/gen/eth_io.h>
|
---|
21 | #include <net/gen/ip_hdr.h>
|
---|
22 | #include <net/gen/ip_io.h>
|
---|
23 | #include <net/gen/udp.h>
|
---|
24 | #include <net/gen/udp_hdr.h>
|
---|
25 | #include <net/gen/udp_io.h>
|
---|
26 | #include <net/gen/dhcp.h>
|
---|
27 | #include "dhcpd.h"
|
---|
28 |
|
---|
29 | void get_buf(buf_t **bp)
|
---|
30 | {
|
---|
31 | /* Allocate and return a buffer pointer iff *bp == nil. */
|
---|
32 | if (*bp != nil) {
|
---|
33 | /* Already has one. */
|
---|
34 | } else {
|
---|
35 | /* Get one from the heap. */
|
---|
36 | buf_t *new= allocate(sizeof(*new));
|
---|
37 | new->dhcp= (dhcp_t *) (new->buf + sizeof(eth_hdr_t)
|
---|
38 | + sizeof(ip_hdr_t) + sizeof(udp_hdr_t));
|
---|
39 | new->udpio= ((udp_io_hdr_t *) new->dhcp) - 1;
|
---|
40 | new->udp= ((udp_hdr_t *) new->dhcp) - 1;
|
---|
41 | new->ip= ((ip_hdr_t *) new->udp) - 1;
|
---|
42 | new->eth= ((eth_hdr_t *) new->ip) - 1;
|
---|
43 | *bp= new;
|
---|
44 | }
|
---|
45 | }
|
---|
46 |
|
---|
47 | void put_buf(buf_t **bp)
|
---|
48 | {
|
---|
49 | /* Return a buffer to the heap. */
|
---|
50 | if (*bp != nil) {
|
---|
51 | free(*bp);
|
---|
52 | *bp= nil;
|
---|
53 | }
|
---|
54 | }
|
---|
55 |
|
---|
56 | void give_buf(buf_t **dbp, buf_t **sbp)
|
---|
57 | {
|
---|
58 | /* Hand over a buffer to another variable. */
|
---|
59 | put_buf(dbp);
|
---|
60 | *dbp= *sbp;
|
---|
61 | *sbp= nil;
|
---|
62 | }
|
---|
63 |
|
---|
64 | #if __minix_vmd
|
---|
65 | #define N_FDS 16 /* Minix-vmd can go async on many fds. */
|
---|
66 | #else
|
---|
67 | #define N_FDS 1 /* Minix doesn't have async I/O. */
|
---|
68 | #endif
|
---|
69 |
|
---|
70 | static fd_t fds[N_FDS]; /* List of open descriptors. */
|
---|
71 | static struct network *fdwaitq; /* Queue of nets waiting for fds. */
|
---|
72 |
|
---|
73 | network_t *newnetwork(void)
|
---|
74 | {
|
---|
75 | /* Create and initialize a network structure. */
|
---|
76 | network_t *new;
|
---|
77 |
|
---|
78 | new= allocate(sizeof(*new));
|
---|
79 | memset(new, 0, sizeof(*new));
|
---|
80 | new->hostname= nil;
|
---|
81 | new->solicit= NEVER;
|
---|
82 | new->sol_ct= -1;
|
---|
83 | return new;
|
---|
84 | }
|
---|
85 |
|
---|
86 | void closefd(fd_t *fdp)
|
---|
87 | {
|
---|
88 | /* Close a descriptor. */
|
---|
89 | if (fdp->fdtype != FT_CLOSED) {
|
---|
90 | asyn_close(&asyn, fdp->fd);
|
---|
91 | close(fdp->fd);
|
---|
92 | fdp->fdtype= FT_CLOSED;
|
---|
93 | fdp->since= 0;
|
---|
94 | put_buf(&fdp->bp);
|
---|
95 | if (debug >= 3) printf("%s: Closed\n", fdp->device);
|
---|
96 | }
|
---|
97 | }
|
---|
98 |
|
---|
99 | int opendev(network_t *np, fdtype_t fdtype, int compete)
|
---|
100 | {
|
---|
101 | /* Make sure that a network has the proper device open and configured.
|
---|
102 | * Return true if this is made so, or false if the device doesn't exist.
|
---|
103 | * If compete is true then the caller competes for old descriptors.
|
---|
104 | * The errno value is EAGAIN if we're out of descriptors.
|
---|
105 | */
|
---|
106 | fd_t *fdp, *fdold;
|
---|
107 | time_t oldest;
|
---|
108 | nwio_ethstat_t ethstat;
|
---|
109 | nwio_ethopt_t ethopt;
|
---|
110 | nwio_ipopt_t ipopt;
|
---|
111 | nwio_udpopt_t udpopt;
|
---|
112 | network_t **pqp;
|
---|
113 | static char devbytype[][4] = { "", "eth", "ip", "udp", "udp" };
|
---|
114 |
|
---|
115 | /* Don't attempt to open higher level devices if not bound. */
|
---|
116 | if (!(np->flags & NF_BOUND) && fdtype > FT_ETHERNET) {
|
---|
117 | errno= EAGAIN;
|
---|
118 | return 0;
|
---|
119 | }
|
---|
120 |
|
---|
121 | /* Check if already open / Find the oldest descriptor. */
|
---|
122 | fdold= nil;
|
---|
123 | oldest= NEVER;
|
---|
124 | for (fdp= fds; fdp < arraylimit(fds); fdp++) {
|
---|
125 | if (fdp->n == np->n && fdp->fdtype == fdtype) {
|
---|
126 | /* Already open. */
|
---|
127 | np->fdp= fdp;
|
---|
128 | return 1;
|
---|
129 | }
|
---|
130 | if (fdp->since <= oldest) { fdold= fdp; oldest= fdp->since; }
|
---|
131 | }
|
---|
132 |
|
---|
133 | /* None free? Then wait for one to get old if so desired. */
|
---|
134 | if (fdold->fdtype != FT_CLOSED && !compete) {
|
---|
135 | errno= EAGAIN;
|
---|
136 | return 0;
|
---|
137 | }
|
---|
138 |
|
---|
139 | if (!(np->flags & NF_WAIT)) {
|
---|
140 | for (pqp= &fdwaitq; *pqp != nil; pqp= &(*pqp)->wait) {}
|
---|
141 | *pqp= np;
|
---|
142 | np->wait= nil;
|
---|
143 | np->flags |= NF_WAIT;
|
---|
144 | }
|
---|
145 |
|
---|
146 | /* We allow a net to keep a descriptor for half of the fast period. */
|
---|
147 | oldest += DELTA_FAST/2;
|
---|
148 |
|
---|
149 | if (fdwaitq != np || (fdold->fdtype != FT_CLOSED && oldest > now)) {
|
---|
150 | /* This net is not the first in the queue, or the oldest isn't
|
---|
151 | * old enough. Forget it for now.
|
---|
152 | */
|
---|
153 | if (oldest < event) event= oldest;
|
---|
154 | errno= EAGAIN;
|
---|
155 | return 0;
|
---|
156 | }
|
---|
157 |
|
---|
158 | /* The oldest is mine. */
|
---|
159 | np->flags &= ~NF_WAIT;
|
---|
160 | fdwaitq= np->wait;
|
---|
161 | closefd(fdold);
|
---|
162 |
|
---|
163 | /* Open the proper device in the proper mode. */
|
---|
164 | fdp= fdold;
|
---|
165 | fdp->n= np->n;
|
---|
166 | sprintf(fdp->device, "/dev/%s%d", devbytype[fdtype], np->n);
|
---|
167 | np->fdp= fdp;
|
---|
168 |
|
---|
169 | if ((fdp->fd= open(fdp->device, O_RDWR)) < 0) {
|
---|
170 | if (errno == ENOENT || errno == ENODEV || errno == ENXIO) return 0;
|
---|
171 | fatal(fdp->device);
|
---|
172 | }
|
---|
173 |
|
---|
174 | switch (fdtype) {
|
---|
175 | case FT_ETHERNET:
|
---|
176 | fcntl(np->fdp->fd, F_SETFL, fcntl(np->fdp->fd, F_GETFL) | O_NONBLOCK);
|
---|
177 | if (ioctl(np->fdp->fd, NWIOGETHSTAT, ðstat) < 0) {
|
---|
178 | /* Not an Ethernet. */
|
---|
179 | close(fdp->fd);
|
---|
180 | return 0;
|
---|
181 | }
|
---|
182 | np->eth= ethstat.nwes_addr;
|
---|
183 | ethopt.nweo_flags= NWEO_COPY | NWEO_EN_LOC | NWEO_EN_BROAD
|
---|
184 | | NWEO_REMANY | NWEO_TYPEANY | NWEO_RWDATALL;
|
---|
185 |
|
---|
186 | if (ioctl(fdp->fd, NWIOSETHOPT, ðopt) < 0) {
|
---|
187 | fprintf(stderr, "%s: %s: Unable to set eth options: %s\n",
|
---|
188 | program, fdp->device, strerror(errno));
|
---|
189 | exit(1);
|
---|
190 | }
|
---|
191 | break;
|
---|
192 |
|
---|
193 | case FT_ICMP:
|
---|
194 | ipopt.nwio_flags= NWIO_COPY | NWIO_EN_LOC | NWIO_EN_BROAD
|
---|
195 | | NWIO_REMANY | NWIO_PROTOSPEC
|
---|
196 | | NWIO_HDR_O_SPEC | NWIO_RWDATALL;
|
---|
197 | ipopt.nwio_tos= 0;
|
---|
198 | ipopt.nwio_ttl= 1;
|
---|
199 | ipopt.nwio_df= 0;
|
---|
200 | ipopt.nwio_hdropt.iho_opt_siz= 0;
|
---|
201 | ipopt.nwio_proto= IPPROTO_ICMP;
|
---|
202 |
|
---|
203 | if (ioctl(fdp->fd, NWIOSIPOPT, &ipopt) < 0) {
|
---|
204 | fprintf(stderr, "%s: %s: Unable to set IP options: %s\n",
|
---|
205 | program, fdp->device, strerror(errno));
|
---|
206 | exit(1);
|
---|
207 | }
|
---|
208 | break;
|
---|
209 |
|
---|
210 | case FT_BOOTPC:
|
---|
211 | udpopt.nwuo_flags= NWUO_COPY | NWUO_EN_LOC | NWUO_EN_BROAD
|
---|
212 | | NWUO_RP_ANY | NWUO_RA_ANY | NWUO_RWDATALL
|
---|
213 | | NWUO_DI_IPOPT | NWUO_LP_SET;
|
---|
214 | udpopt.nwuo_locport= port_client;
|
---|
215 | goto udp;
|
---|
216 |
|
---|
217 | case FT_BOOTPS:
|
---|
218 | udpopt.nwuo_flags= NWUO_EXCL | NWUO_EN_LOC | NWUO_EN_BROAD
|
---|
219 | | NWUO_RP_ANY | NWUO_RA_ANY | NWUO_RWDATALL
|
---|
220 | | NWUO_DI_IPOPT | NWUO_LP_SET;
|
---|
221 | udpopt.nwuo_locport= port_server;
|
---|
222 | udp:
|
---|
223 | if (ioctl(fdp->fd, NWIOSUDPOPT, &udpopt) == -1) {
|
---|
224 | fprintf(stderr, "%s: %s: Unable to set UDP options: %s\n",
|
---|
225 | program, fdp->device, strerror(errno));
|
---|
226 | exit(1);
|
---|
227 | }
|
---|
228 | break;
|
---|
229 |
|
---|
230 | default:;
|
---|
231 | }
|
---|
232 |
|
---|
233 | fdp->fdtype= fdtype;
|
---|
234 | fdp->since= now;
|
---|
235 | if (debug >= 3) printf("%s: Opened\n", fdp->device);
|
---|
236 | return 1;
|
---|
237 | }
|
---|
238 |
|
---|
239 | void closedev(network_t *np, fdtype_t fdtype)
|
---|
240 | {
|
---|
241 | /* We no longer need a given type of device to be open. */
|
---|
242 | fd_t *fdp;
|
---|
243 |
|
---|
244 | for (fdp= fds; fdp < arraylimit(fds); fdp++) {
|
---|
245 | if (fdp->n == np->n && (fdp->fdtype == fdtype || fdtype == FT_ALL)) {
|
---|
246 | closefd(fdp);
|
---|
247 | }
|
---|
248 | }
|
---|
249 | }
|
---|
250 |
|
---|
251 | char *ipdev(int n)
|
---|
252 | {
|
---|
253 | /* IP device for network #n. */
|
---|
254 | static char device[sizeof("/dev/ipNNN")];
|
---|
255 |
|
---|
256 | sprintf(device, "/dev/ip%d", n);
|
---|
257 | return device;
|
---|
258 | }
|
---|
259 |
|
---|
260 | void set_ipconf(char *device, ipaddr_t ip, ipaddr_t mask, unsigned mtu)
|
---|
261 | {
|
---|
262 | /* Set IP address and netmask of an IP device. */
|
---|
263 | int fd;
|
---|
264 | nwio_ipconf_t ipconf;
|
---|
265 |
|
---|
266 | if (test > 0) return;
|
---|
267 |
|
---|
268 | if ((fd= open(device, O_RDWR)) < 0) fatal(device);
|
---|
269 | ipconf.nwic_flags= NWIC_IPADDR_SET | NWIC_NETMASK_SET;
|
---|
270 | ipconf.nwic_ipaddr= ip;
|
---|
271 | ipconf.nwic_netmask= mask;
|
---|
272 | #ifdef NWIC_MTU_SET
|
---|
273 | if (mtu != 0) {
|
---|
274 | ipconf.nwic_flags |= NWIC_MTU_SET;
|
---|
275 | ipconf.nwic_mtu= mtu;
|
---|
276 | }
|
---|
277 | #endif
|
---|
278 | if (ioctl(fd, NWIOSIPCONF, &ipconf) < 0) fatal(device);
|
---|
279 | close(fd);
|
---|
280 | }
|
---|