| /* |
| * This file is part of the UCB release of Plan 9. It is subject to the license |
| * terms in the LICENSE file found in the top-level directory of this |
| * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No |
| * part of the UCB release of Plan 9, including this file, may be copied, |
| * modified, propagated, or distributed except according to the terms contained |
| * in the LICENSE file. |
| */ |
| #include <stdlib.h> |
| |
| #include <iplib/iplib.h> |
| #include <parlib/parlib.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| static uint8_t loopbacknet[IPaddrlen] = { |
| 0, 0, 0, 0, |
| 0, 0, 0, 0, |
| 0, 0, 0xff, 0xff, |
| 127, 0, 0, 0 |
| }; |
| static uint8_t loopbackmask[IPaddrlen] = { |
| 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0, 0, 0 |
| }; |
| |
| #define NFIELD 200 |
| #define nelem(x) (sizeof(x) / sizeof(x[0])) |
| static struct ipifc **_readoldipifc(char *buf, struct ipifc **l, int index) |
| { |
| char *f[NFIELD]; |
| int i, n; |
| struct ipifc *ifc; |
| struct iplifc *lifc, **ll; |
| |
| /* allocate new interface */ |
| *l = ifc = calloc(sizeof(struct ipifc), 1); |
| if (ifc == NULL) |
| return l; |
| l = &ifc->next; |
| ifc->index = index; |
| |
| n = tokenize(buf, f, NFIELD); |
| if (n < 2) |
| return l; |
| |
| strncpy(ifc->dev, f[0], sizeof ifc->dev); |
| ifc->dev[sizeof(ifc->dev) - 1] = 0; |
| ifc->mtu = strtoul(f[1], NULL, 10); |
| |
| ll = &ifc->lifc; |
| for (i = 2; n - i >= 7; i += 7) { |
| /* allocate new local address */ |
| *ll = lifc = calloc(sizeof(struct iplifc), 1); |
| ll = &lifc->next; |
| |
| parseip(lifc->ip, f[i]); |
| parseipmask(lifc->mask, f[i + 1]); |
| parseip(lifc->net, f[i + 2]); |
| ifc->pktin = strtoul(f[i + 3], NULL, 10); |
| ifc->pktout = strtoul(f[i + 4], NULL, 10); |
| ifc->errin = strtoul(f[i + 5], NULL, 10); |
| ifc->errout = strtoul(f[i + 6], NULL, 10); |
| } |
| return l; |
| } |
| |
| static char *findfield(char *name, char **f, int n) |
| { |
| int i; |
| |
| for (i = 0; i < n - 1; i++) |
| if (strcmp(f[i], name) == 0) |
| return f[i + 1]; |
| return ""; |
| } |
| |
| static struct ipifc **_readipifc(char *file, struct ipifc **l, int index) |
| { |
| int i, n, fd, lines; |
| char buf[4 * 1024]; |
| char *line[32]; |
| char *f[64]; |
| struct ipifc *ifc, **l0; |
| struct iplifc *lifc, **ll; |
| |
| /* read the file */ |
| fd = open(file, O_RDONLY); |
| if (fd < 0) |
| return l; |
| n = 0; |
| while ((i = read(fd, buf + n, sizeof(buf) - 1 - n)) > 0 && |
| n < sizeof(buf) - 1) |
| n += i; |
| buf[n] = 0; |
| close(fd); |
| |
| if (strncmp(buf, "device", 6) != 0) |
| return _readoldipifc(buf, l, index); |
| /* ignore ifcs with no associated device */ |
| if (strncmp(buf + 6, " ", 2) == 0) |
| return l; |
| /* allocate new interface */ |
| *l = ifc = calloc(sizeof(struct ipifc), 1); |
| if (ifc == NULL) |
| return l; |
| l0 = l; |
| l = &ifc->next; |
| ifc->index = index; |
| |
| lines = getfields(buf, line, nelem(line), 1, "\n"); |
| |
| /* pick off device specific info(first line) */ |
| n = tokenize(line[0], f, nelem(f)); |
| if (n % 2 != 0) |
| goto lose; |
| strncpy(ifc->dev, findfield("device", f, n), sizeof(ifc->dev)); |
| ifc->dev[sizeof(ifc->dev) - 1] = 0; |
| if (ifc->dev[0] == 0) { |
| lose: |
| free(ifc); |
| *l0 = NULL; |
| return l; |
| } |
| ifc->mtu = strtoul(findfield("maxtu", f, n), NULL, 10); |
| ifc->sendra6 = atoi(findfield("sendra", f, n)); |
| ifc->recvra6 = atoi(findfield("recvra", f, n)); |
| ifc->rp.mflag = atoi(findfield("mflag", f, n)); |
| ifc->rp.oflag = atoi(findfield("oflag", f, n)); |
| ifc->rp.maxraint = atoi(findfield("maxraint", f, n)); |
| ifc->rp.minraint = atoi(findfield("minraint", f, n)); |
| ifc->rp.linkmtu = atoi(findfield("linkmtu", f, n)); |
| ifc->rp.reachtime = atoi(findfield("reachtime", f, n)); |
| ifc->rp.rxmitra = atoi(findfield("rxmitra", f, n)); |
| ifc->rp.ttl = atoi(findfield("ttl", f, n)); |
| ifc->rp.routerlt = atoi(findfield("routerlt", f, n)); |
| ifc->pktin = strtoul(findfield("pktin", f, n), NULL, 10); |
| ifc->pktout = strtoul(findfield("pktout", f, n), NULL, 10); |
| ifc->errin = strtoul(findfield("errin", f, n), NULL, 10); |
| ifc->errout = strtoul(findfield("errout", f, n), NULL, 10); |
| |
| /* now read the addresses */ |
| ll = &ifc->lifc; |
| for (i = 1; i < lines; i++) { |
| n = tokenize(line[i], f, nelem(f)); |
| if (n < 5) |
| break; |
| |
| /* allocate new local address */ |
| *ll = lifc = calloc(sizeof(struct iplifc), 1); |
| ll = &lifc->next; |
| |
| parseip(lifc->ip, f[0]); |
| parseipmask(lifc->mask, f[1]); |
| parseip(lifc->net, f[2]); |
| |
| lifc->validlt = strtoul(f[3], NULL, 10); |
| lifc->preflt = strtoul(f[4], NULL, 10); |
| } |
| |
| return l; |
| } |
| |
| void free_ipifc(struct ipifc *ifc) |
| { |
| struct ipifc *next; |
| struct iplifc *lnext, *lifc; |
| |
| if (ifc == NULL) |
| return; |
| for (; ifc; ifc = next) { |
| next = ifc->next; |
| for (lifc = ifc->lifc; lifc; lifc = lnext) { |
| lnext = lifc->next; |
| free(lifc); |
| } |
| free(ifc); |
| } |
| } |
| |
| /* This will free @ifc when passed in. Some old Plan 9 programs might rely on |
| * it still (like our ping, netstat, ipconfig). It usually ends up be a |
| * thread-unsafe disaster. */ |
| struct ipifc *readipifc(char *net, struct ipifc *ifc, int index) |
| { |
| int fd, i, n; |
| struct dir *dir; |
| char directory[128]; |
| char buf[128]; |
| struct ipifc **l; |
| |
| free_ipifc(ifc); |
| |
| l = &ifc; |
| ifc = NULL; |
| |
| if (net == 0) |
| net = "/net"; |
| snprintf(directory, sizeof(directory), "%s/ipifc", net); |
| |
| if (index >= 0) { |
| snprintf(buf, sizeof(buf), "%s/%d/status", directory, index); |
| _readipifc(buf, l, index); |
| } else { |
| DIR *d; |
| struct dirent *de; |
| d = opendir(directory); |
| if (!d) |
| return NULL; |
| |
| while (de = readdir(d)) { |
| if (strcmp(de->d_name, "clone") == 0) |
| continue; |
| if (strcmp(de->d_name, "stats") == 0) |
| continue; |
| snprintf(buf, sizeof(buf), "%s/%s/status", directory, |
| de->d_name); |
| l = _readipifc(buf, l, atoi(de->d_name)); |
| } |
| closedir(d); |
| } |
| |
| return ifc; |
| } |
| |
| /* Gets the local interface that isn't the friggin' loopback address. When |
| * you're done, free_ipifc(ifc). Returns 0 on failure. */ |
| struct iplifc *get_first_noloop_iplifc(char *net, struct ipifc **ifc) |
| { |
| struct ipifc *nifc; |
| struct iplifc *lifc; |
| uint8_t mynet[IPaddrlen]; |
| |
| *ifc = readipifc(net, NULL, -1); |
| for (nifc = *ifc; nifc; nifc = nifc->next) { |
| for (lifc = nifc->lifc; lifc; lifc = lifc->next) { |
| maskip(lifc->ip, loopbackmask, mynet); |
| if (ipcmp(mynet, loopbacknet) == 0) |
| continue; |
| if (ipcmp(lifc->ip, IPnoaddr) != 0) |
| return lifc; |
| } |
| } |
| free_ipifc(*ifc); |
| return 0; |
| } |