| 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 | } | 
|---|