akaros/kern/src/net/netif.c
<<
>>
Prefs
   1/* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
   2 * Portions Copyright © 1997-1999 Vita Nuova Limited
   3 * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
   4 *                                (www.vitanuova.com)
   5 * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
   6 *
   7 * Modified for the Akaros operating system:
   8 * Copyright (c) 2013-2014 The Regents of the University of California
   9 * Copyright (c) 2013-2015 Google Inc.
  10 *
  11 * Permission is hereby granted, free of charge, to any person obtaining a copy
  12 * of this software and associated documentation files (the "Software"), to deal
  13 * in the Software without restriction, including without limitation the rights
  14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15 * copies of the Software, and to permit persons to whom the Software is
  16 * furnished to do so, subject to the following conditions:
  17 *
  18 * The above copyright notice and this permission notice shall be included in
  19 * all copies or substantial portions of the Software.
  20 *
  21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  27 * SOFTWARE. */
  28
  29#include <slab.h>
  30#include <kmalloc.h>
  31#include <kref.h>
  32#include <string.h>
  33#include <stdio.h>
  34#include <assert.h>
  35#include <error.h>
  36#include <cpio.h>
  37#include <pmap.h>
  38#include <smp.h>
  39#include <net/ip.h>
  40
  41static int netown(struct netfile *, char *unused_char_p_t, int);
  42static int openfile(struct ether *, int);
  43static char *matchtoken(char *unused_char_p_t, char *);
  44static char *netmulti(struct ether *, struct netfile *,
  45                      uint8_t *unused_uint8_p_t, int);
  46static int parseaddr(uint8_t *unused_uint8_p_t, char *unused_char_p_t, int);
  47
  48/*
  49 *  set up a new network interface
  50 */
  51void netifinit(struct ether *nif, char *name, int nfile, uint32_t limit)
  52{
  53        qlock_init(&nif->qlock);
  54        strlcpy(nif->name, name, KNAMELEN);
  55        nif->nfile = nfile;
  56        nif->f = kzmalloc(nfile * sizeof(struct netfile *), 0);
  57        if (nif->f)
  58                memset(nif->f, 0, nfile * sizeof(struct netfile *));
  59        else
  60                nif->nfile = 0;
  61        nif->limit = limit;
  62}
  63
  64/*
  65 *  generate a 3 level directory
  66 */
  67static int netifgen(struct chan *c, char *unused_char_p_t, struct dirtab *vp,
  68                    int unused_int, int i, struct dir *dp)
  69{
  70        struct qid q;
  71        struct ether *nif = (struct ether *)vp;
  72        struct netfile *f;
  73        int perm;
  74        char *o;
  75
  76        q.type = QTFILE;
  77        q.vers = 0;
  78
  79        /* top level directory contains the name of the network */
  80        if (c->qid.path == 0) {
  81                switch (i) {
  82                case DEVDOTDOT:
  83                        q.path = 0;
  84                        q.type = QTDIR;
  85                        devdir(c, q, ".", 0, eve.name, 0555, dp);
  86                        break;
  87                case 0:
  88                        q.path = N2ndqid;
  89                        q.type = QTDIR;
  90                        strlcpy(get_cur_genbuf(), nif->name, GENBUF_SZ);
  91                        devdir(c, q, get_cur_genbuf(), 0, eve.name, 0555, dp);
  92                        break;
  93                default:
  94                        return -1;
  95                }
  96                return 1;
  97        }
  98
  99        /* second level contains clone plus all the conversations.
 100         *
 101         * This ancient comment is from plan9.  Inferno and nxm both had issues
 102         * here.  You couldn't ls /net/ether0/ when it didn't have any convs.
 103         * There were also issues with nxm where you couldn't stat
 104         * ether0/x/stats properly.
 105         *
 106         * The issue is that if we handle things like Nstatqid, then we will
 107         * never pass it down to the third level. And since we just set the path
 108         * == Nstatqid, we won't have the NETID muxed in. If someone isn't
 109         * trying to generate a chan, but instead is looking it up (devwalk
 110         * generates, devstat already has the chan), then they are also looking
 111         * for a devdir with path containing ID << 5. So if you stat
 112         * ether0/1/ifstats, devstat is looking for path 41, but we return path
 113         * 9 (41 = 32 + 9). (these numbers are before we tracked NETID + 1).
 114         *
 115         * We (akaros and plan9) had a big if here, that would catch things that
 116         * do not exist in the subdirs of a netif. Things like clone make sense
 117         * here.  I guess addr too, though that seems to be added since the
 118         * original comment. You can see what the 3rd level was expecting to
 119         * parse by looking farther down in the code.
 120         *
 121         * The root of the problem was that the old code couldn't tell the
 122         * difference between no netid and netid 0. Now, we determine if we're
 123         * at the second level by the lack of a netid, instead of trying to
 124         * enumerate the qid types that the second level could have. The latter
 125         * approach allowed for something like ether0/1/stats, but we couldn't
 126         * actually devstat ether0/stats directly. It's worth noting that there
 127         * is no difference to the content of ether0/stats and ether0/x/stats
 128         * (when you read), but they have different chan qids.
 129         *
 130         * Here's the old if block:
 131         t = NETTYPE(c->qid.path);
 132         if (t == N2ndqid || t == Ncloneqid || t == Naddrqid) {
 133         */
 134        if (NETID(c->qid.path) == -1) {
 135                switch (i) {
 136                case DEVDOTDOT:
 137                        q.type = QTDIR;
 138                        q.path = 0;
 139                        devdir(c, q, ".", 0, eve.name, DMDIR | 0555, dp);
 140                        break;
 141                case 0:
 142                        q.path = Ncloneqid;
 143                        devdir(c, q, "clone", 0, eve.name, 0666, dp);
 144                        break;
 145                case 1:
 146                        q.path = Naddrqid;
 147                        devdir(c, q, "addr", 0, eve.name, 0666, dp);
 148                        break;
 149                case 2:
 150                        q.path = Nstatqid;
 151                        devdir(c, q, "stats", 0, eve.name, 0444, dp);
 152                        break;
 153                case 3:
 154                        q.path = Nifstatqid;
 155                        devdir(c, q, "ifstats", 0, eve.name, 0444, dp);
 156                        break;
 157                default:
 158                        i -= 4;
 159                        if (i >= nif->nfile)
 160                                return -1;
 161                        if (nif->f[i] == 0)
 162                                return 0;
 163                        q.type = QTDIR;
 164                        q.path = NETQID(i, N3rdqid);
 165                        snprintf(get_cur_genbuf(), GENBUF_SZ, "%d", i);
 166                        devdir(c, q, get_cur_genbuf(), 0, eve.name,
 167                               DMDIR | 0555, dp);
 168                        break;
 169                }
 170                return 1;
 171        }
 172
 173        /* third level */
 174        f = nif->f[NETID(c->qid.path)];
 175        if (f == 0)
 176                return 0;
 177        if (*f->owner) {
 178                o = f->owner;
 179                perm = f->mode;
 180        } else {
 181                o = eve.name;
 182                perm = 0666;
 183        }
 184        switch (i) {
 185        case DEVDOTDOT:
 186                q.type = QTDIR;
 187                q.path = N2ndqid;
 188                strlcpy(get_cur_genbuf(), nif->name, GENBUF_SZ);
 189                devdir(c, q, get_cur_genbuf(), 0, eve.name, DMDIR | 0555, dp);
 190                break;
 191        case 0:
 192                q.path = NETQID(NETID(c->qid.path), Ndataqid);
 193                devdir(c, q, "data", 0, o, perm, dp);
 194                break;
 195        case 1:
 196                q.path = NETQID(NETID(c->qid.path), Nctlqid);
 197                devdir(c, q, "ctl", 0, o, perm, dp);
 198                break;
 199        case 2:
 200                q.path = NETQID(NETID(c->qid.path), Nstatqid);
 201                devdir(c, q, "stats", 0, eve.name, 0444, dp);
 202                break;
 203        case 3:
 204                q.path = NETQID(NETID(c->qid.path), Ntypeqid);
 205                devdir(c, q, "type", 0, eve.name, 0444, dp);
 206                break;
 207        case 4:
 208                q.path = NETQID(NETID(c->qid.path), Nifstatqid);
 209                devdir(c, q, "ifstats", 0, eve.name, 0444, dp);
 210                break;
 211        default:
 212                return -1;
 213        }
 214        return 1;
 215}
 216
 217struct walkqid *netifwalk(struct ether *nif, struct chan *c, struct chan *nc,
 218                                                  char **name, int nname)
 219{
 220        return devwalk(c, nc, name, nname, (struct dirtab *)nif, 0, netifgen);
 221}
 222
 223struct chan *netifopen(struct ether *nif, struct chan *c, int omode)
 224{
 225        int id;
 226        struct netfile *f;
 227
 228        id = 0;
 229        if (c->qid.type & QTDIR) {
 230                if (omode & O_WRITE)
 231                        error(EPERM, ERROR_FIXME);
 232        } else {
 233                switch (NETTYPE(c->qid.path)) {
 234                case Ndataqid:
 235                case Nctlqid:
 236                        id = NETID(c->qid.path);
 237                        openfile(nif, id);
 238                        break;
 239                case Ncloneqid:
 240                        id = openfile(nif, -1);
 241                        c->qid.path = NETQID(id, Nctlqid);
 242                        break;
 243                default:
 244                        if (omode & O_WRITE)
 245                                error(EINVAL, ERROR_FIXME);
 246                }
 247                switch (NETTYPE(c->qid.path)) {
 248                case Ndataqid:
 249                case Nctlqid:
 250                        f = nif->f[id];
 251                        if (netown(f, current->user.name, omode & 7) < 0)
 252                                error(EPERM, ERROR_FIXME);
 253                        break;
 254                }
 255        }
 256        c->mode = openmode(omode);
 257        c->flag |= COPEN;
 258        c->offset = 0;
 259        c->iounit = qiomaxatomic;
 260        return c;
 261}
 262
 263/* Helper for building the features for netifread */
 264static int feature_appender(int features, char *p, int sofar)
 265{
 266        if (features & NETF_IPCK)
 267                sofar += snprintf(p + sofar, READSTR - sofar, "ipck ");
 268        if (features & NETF_UDPCK)
 269                sofar += snprintf(p + sofar, READSTR - sofar, "udpck ");
 270        if (features & NETF_TCPCK)
 271                sofar += snprintf(p + sofar, READSTR - sofar, "tcpck ");
 272        if (features & NETF_PADMIN)
 273                sofar += snprintf(p + sofar, READSTR - sofar, "padmin ");
 274        if (features & NETF_SG)
 275                sofar += snprintf(p + sofar, READSTR - sofar, "sg ");
 276        if (features & NETF_TSO)
 277                sofar += snprintf(p + sofar, READSTR - sofar, "tso ");
 278        if (features & NETF_LRO)
 279                sofar += snprintf(p + sofar, READSTR - sofar, "lro ");
 280        if (features & NETF_RXCSUM)
 281                sofar += snprintf(p + sofar, READSTR - sofar, "rxcsum ");
 282        return sofar;
 283}
 284
 285long netifread(struct ether *nif, struct chan *c, void *a, long n,
 286               uint32_t offset)
 287{
 288        int i, j;
 289        struct netfile *f;
 290        char *p;
 291
 292        if (c->qid.type & QTDIR)
 293                return devdirread(c, a, n, (struct dirtab *)nif, 0, netifgen);
 294
 295        switch (NETTYPE(c->qid.path)) {
 296        case Ndataqid:
 297                f = nif->f[NETID(c->qid.path)];
 298                return qread(f->in, a, n);
 299        case Nctlqid:
 300                return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE);
 301        case Nstatqid:
 302                p = kzmalloc(READSTR, 0);
 303                if (p == NULL)
 304                        return 0;
 305                j = 0;
 306                j += snprintf(p + j, READSTR - j, "driver: %s\n",
 307                              nif->drv_name);
 308                j += snprintf(p + j, READSTR - j, "in: %d\n", nif->inpackets);
 309                j += snprintf(p + j, READSTR - j, "link: %d\n", nif->link);
 310                j += snprintf(p + j, READSTR - j, "out: %d\n", nif->outpackets);
 311                j += snprintf(p + j, READSTR - j, "crc errs: %d\n", nif->crcs);
 312                j += snprintf(p + j, READSTR - j, "overflows: %d\n",
 313                              nif->overflows);
 314                j += snprintf(p + j, READSTR - j, "soft overflows: %d\n",
 315                              nif->soverflows);
 316                j += snprintf(p + j, READSTR - j, "framing errs: %d\n",
 317                              nif->frames);
 318                j += snprintf(p + j, READSTR - j, "buffer errs: %d\n",
 319                              nif->buffs);
 320                j += snprintf(p + j, READSTR - j, "output errs: %d\n",
 321                              nif->oerrs);
 322                j += snprintf(p + j, READSTR - j, "prom: %d\n", nif->prom);
 323                j += snprintf(p + j, READSTR - j, "mbps: %d\n", nif->mbps);
 324                j += snprintf(p + j, READSTR - j, "addr: ");
 325                for (i = 0; i < nif->alen; i++)
 326                        j += snprintf(p + j, READSTR - j, "%02.2x",
 327                                      nif->addr[i]);
 328                j += snprintf(p + j, READSTR - j, "\n");
 329
 330                j += snprintf(p + j, READSTR - j, "feat: ");
 331                j = feature_appender(nif->feat, p, j);
 332                j += snprintf(p + j, READSTR - j, "\n");
 333
 334                j += snprintf(p + j, READSTR - j, "hw_features: ");
 335                j = feature_appender(nif->hw_features, p, j);
 336                j += snprintf(p + j, READSTR - j, "\n");
 337
 338                n = readstr(offset, a, n, p);
 339                kfree(p);
 340                return n;
 341        case Naddrqid:
 342                p = kzmalloc(READSTR, 0);
 343                if (p == NULL)
 344                        return 0;
 345                j = 0;
 346                for (i = 0; i < nif->alen; i++)
 347                        j += snprintf(p + j, READSTR - j, "%02.2x",
 348                                      nif->addr[i]);
 349                n = readstr(offset, a, n, p);
 350                kfree(p);
 351                return n;
 352        case Ntypeqid:
 353                f = nif->f[NETID(c->qid.path)];
 354                return readnum(offset, a, n, f->type, NUMSIZE);
 355        case Nifstatqid:
 356                return 0;
 357        }
 358        error(EINVAL, ERROR_FIXME);
 359        return -1;      /* not reached */
 360}
 361
 362struct block *netifbread(struct ether *nif, struct chan *c, long n,
 363                                                 uint32_t offset)
 364{
 365        if ((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid)
 366                return devbread(c, n, offset);
 367
 368        return qbread(nif->f[NETID(c->qid.path)]->in, n);
 369}
 370
 371/*
 372 *  make sure this type isn't already in use on this device
 373 */
 374static int typeinuse(struct ether *nif, int type)
 375{
 376        struct netfile *f, **fp, **efp;
 377
 378        if (type <= 0)
 379                return 0;
 380
 381        efp = &nif->f[nif->nfile];
 382        for (fp = nif->f; fp < efp; fp++) {
 383                f = *fp;
 384                if (f == 0)
 385                        continue;
 386                if (f->type == type)
 387                        return 1;
 388        }
 389        return 0;
 390}
 391
 392/*
 393 *  the devxxx.c that calls us handles writing data, it knows best
 394 */
 395long netifwrite(struct ether *nif, struct chan *c, void *a, long n)
 396{
 397        ERRSTACK(1);
 398        struct netfile *f;
 399        int type;
 400        char *p, buf[64];
 401        uint8_t binaddr[Nmaxaddr];
 402
 403        if (NETTYPE(c->qid.path) != Nctlqid)
 404                error(EPERM, ERROR_FIXME);
 405
 406        if (n >= sizeof(buf))
 407                n = sizeof(buf) - 1;
 408        memmove(buf, a, n);
 409        buf[n] = 0;
 410
 411        qlock(&nif->qlock);
 412        if (waserror()) {
 413                qunlock(&nif->qlock);
 414                nexterror();
 415        }
 416
 417        f = nif->f[NETID(c->qid.path)];
 418        if ((p = matchtoken(buf, "connect")) != 0) {
 419                /* We'd like to not use the NIC until it has come up fully -
 420                 * auto-negotiation is done and packets will get sent out.  This
 421                 * is about the best place to do it. */
 422                netif_wait_for_carrier(nif);
 423                type = strtol(p, 0, 0);
 424                if (typeinuse(nif, type))
 425                        error(EBUSY, ERROR_FIXME);
 426                f->type = type;
 427                if (f->type < 0)
 428                        nif->all++;
 429        } else if (matchtoken(buf, "promiscuous")) {
 430                if (f->prom == 0) {
 431                        /* Note that promisc has two meanings: put the NIC into
 432                         * promisc mode, and record our outbound traffic.  See
 433                         * etheroq(). */
 434                        /* TODO: consider porting linux's interface for
 435                         * set_rx_mode. */
 436                        if (nif->prom == 0 && nif->promiscuous != NULL)
 437                                nif->promiscuous(nif->arg, 1);
 438                        f->prom = 1;
 439                        nif->prom++;
 440                }
 441        } else if ((p = matchtoken(buf, "scanbs")) != 0) {
 442                /* scan for base stations */
 443                if (f->scan == 0) {
 444                        type = strtol(p, 0, 0);
 445                        if (type < 5)
 446                                type = 5;
 447                        if (nif->scanbs != NULL)
 448                                nif->scanbs(nif->arg, type);
 449                        f->scan = type;
 450                        nif->scan++;
 451                }
 452        } else if (matchtoken(buf, "bridge")) {
 453                f->bridge = 1;
 454        } else if (matchtoken(buf, "headersonly")) {
 455                f->headersonly = 1;
 456        } else if ((p = matchtoken(buf, "addmulti")) != 0) {
 457                if (parseaddr(binaddr, p, nif->alen) < 0)
 458                        error(EFAIL, "bad address");
 459                p = netmulti(nif, f, binaddr, 1);
 460                if (p)
 461                        error(EFAIL, p);
 462        } else if ((p = matchtoken(buf, "remmulti")) != 0) {
 463                if (parseaddr(binaddr, p, nif->alen) < 0)
 464                        error(EFAIL, "bad address");
 465                p = netmulti(nif, f, binaddr, 0);
 466                if (p)
 467                        error(EFAIL, p);
 468        } else if (matchtoken(buf, "oneblock")) {
 469                /* Qmsg + Qcoal = one block at a time. */
 470                q_toggle_qmsg(f->in, TRUE);
 471                q_toggle_qcoalesce(f->in, TRUE);
 472        } else
 473                n = -1;
 474        qunlock(&nif->qlock);
 475        poperror();
 476        return n;
 477}
 478
 479int netifwstat(struct ether *nif, struct chan *c, uint8_t * db, int n)
 480{
 481        struct dir *dir;
 482        struct netfile *f;
 483        int m;
 484
 485        f = nif->f[NETID(c->qid.path)];
 486        if (f == 0)
 487                error(ENOENT, ERROR_FIXME);
 488
 489        if (netown(f, current->user.name, O_WRITE) < 0)
 490                error(EPERM, ERROR_FIXME);
 491
 492        dir = kzmalloc(sizeof(struct dir) + n, 0);
 493        m = convM2D(db, n, &dir[0], (char *)&dir[1]);
 494        if (m == 0) {
 495                kfree(dir);
 496                error(ENODATA, ERROR_FIXME);
 497        }
 498        if (!emptystr(dir[0].uid))
 499                strlcpy(f->owner, dir[0].uid, KNAMELEN);
 500        if (dir[0].mode != -1)
 501                f->mode = dir[0].mode;
 502        kfree(dir);
 503        return m;
 504}
 505
 506int netifstat(struct ether *nif, struct chan *c, uint8_t * db, int n)
 507{
 508        return devstat(c, db, n, (struct dirtab *)nif, 0, netifgen);
 509}
 510
 511void netifclose(struct ether *nif, struct chan *c)
 512{
 513        struct netfile *f;
 514        int t;
 515        struct netaddr *ap;
 516
 517        if ((c->flag & COPEN) == 0)
 518                return;
 519
 520        t = NETTYPE(c->qid.path);
 521        if (t != Ndataqid && t != Nctlqid)
 522                return;
 523
 524        f = nif->f[NETID(c->qid.path)];
 525        qlock(&f->qlock);
 526        if (--(f->inuse) == 0) {
 527                if (f->prom) {
 528                        qlock(&nif->qlock);
 529                        if (--(nif->prom) == 0 && nif->promiscuous != NULL)
 530                                nif->promiscuous(nif->arg, 0);
 531                        qunlock(&nif->qlock);
 532                        f->prom = 0;
 533                }
 534                if (f->scan) {
 535                        qlock(&nif->qlock);
 536                        if (--(nif->scan) == 0 && nif->scanbs != NULL)
 537                                nif->scanbs(nif->arg, 0);
 538                        qunlock(&nif->qlock);
 539                        f->prom = 0;
 540                        f->scan = 0;
 541                }
 542                if (f->nmaddr) {
 543                        qlock(&nif->qlock);
 544                        t = 0;
 545                        for (ap = nif->maddr; ap; ap = ap->next) {
 546                                if (f->maddr[t / 8] & (1 << (t % 8)))
 547                                        netmulti(nif, f, ap->addr, 0);
 548                        }
 549                        qunlock(&nif->qlock);
 550                        f->nmaddr = 0;
 551                }
 552                if (f->type < 0) {
 553                        qlock(&nif->qlock);
 554                        --(nif->all);
 555                        qunlock(&nif->qlock);
 556                }
 557                f->owner[0] = 0;
 558                f->type = 0;
 559                f->bridge = 0;
 560                f->headersonly = 0;
 561                qclose(f->in);
 562        }
 563        qunlock(&f->qlock);
 564}
 565
 566spinlock_t netlock = SPINLOCK_INITIALIZER;
 567
 568static int netown(struct netfile *p, char *o, int omode)
 569{
 570        int mode;
 571        int rwx;
 572
 573        spin_lock(&netlock);
 574        if (*p->owner) {
 575                if (strncmp(o, p->owner, KNAMELEN) == 0)
 576                        mode = p->mode;
 577                else if (strncmp(o, eve.name, KNAMELEN) == 0)
 578                        mode = p->mode << 3;
 579                else
 580                        mode = p->mode << 6;
 581
 582                rwx = omode_to_rwx(omode);
 583                if ((rwx & mode) == rwx) {
 584                        spin_unlock(&netlock);
 585                        return 0;
 586                } else {
 587                        spin_unlock(&netlock);
 588                        return -1;
 589                }
 590        }
 591        strlcpy(p->owner, o, KNAMELEN);
 592        p->mode = 0660;
 593        spin_unlock(&netlock);
 594        return 0;
 595}
 596
 597/*
 598 *  Increment the reference count of a network device.
 599 *  If id < 0, return an unused ether device.
 600 */
 601static int openfile(struct ether *nif, int id)
 602{
 603        ERRSTACK(1);
 604        struct netfile *f, **fp, **efp;
 605
 606        if (id >= 0) {
 607                f = nif->f[id];
 608                if (f == 0)
 609                        error(ENODEV, ERROR_FIXME);
 610                qlock(&f->qlock);
 611                qreopen(f->in);
 612                f->inuse++;
 613                qunlock(&f->qlock);
 614                return id;
 615        }
 616
 617        qlock(&nif->qlock);
 618        if (waserror()) {
 619                qunlock(&nif->qlock);
 620                nexterror();
 621        }
 622        efp = &nif->f[nif->nfile];
 623        for (fp = nif->f; fp < efp; fp++) {
 624                f = *fp;
 625                if (f == 0) {
 626                        f = kzmalloc(sizeof(struct netfile), 0);
 627                        if (f == 0)
 628                                exhausted("memory");
 629                        /* since we lock before netifinit (if we ever call
 630                         * that...) */
 631                        qlock_init(&f->qlock);
 632                        f->in = qopen(nif->limit, Qmsg, 0, 0);
 633                        if (f->in == NULL) {
 634                                kfree(f);
 635                                exhausted("memory");
 636                        }
 637                        *fp = f;
 638                        qlock(&f->qlock);
 639                } else {
 640                        qlock(&f->qlock);
 641                        if (f->inuse) {
 642                                qunlock(&f->qlock);
 643                                continue;
 644                        }
 645                }
 646                f->inuse = 1;
 647                qreopen(f->in);
 648                netown(f, current->user.name, 0);
 649                qunlock(&f->qlock);
 650                qunlock(&nif->qlock);
 651                poperror();
 652                return fp - nif->f;
 653        }
 654        error(ENODEV, ERROR_FIXME);
 655        return -1;      /* not reached */
 656}
 657
 658/*
 659 *  look for a token starting a string,
 660 *  return a pointer to first non-space char after it
 661 */
 662static char *matchtoken(char *p, char *token)
 663{
 664        int n;
 665
 666        n = strlen(token);
 667        if (strncmp(p, token, n))
 668                return 0;
 669        p += n;
 670        if (*p == 0)
 671                return p;
 672        if (*p != ' ' && *p != '\t' && *p != '\n')
 673                return 0;
 674        while (*p == ' ' || *p == '\t' || *p == '\n')
 675                p++;
 676        return p;
 677}
 678
 679static uint32_t hash(uint8_t * a, int len)
 680{
 681        uint32_t sum = 0;
 682
 683        while (len-- > 0)
 684                sum = (sum << 1) + *a++;
 685        return sum % Nmhash;
 686}
 687
 688int activemulti(struct ether *nif, uint8_t * addr, int alen)
 689{
 690        struct netaddr *hp;
 691
 692        for (hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext)
 693                if (memcmp(addr, hp->addr, alen) == 0) {
 694                        if (hp->ref)
 695                                return 1;
 696                        else
 697                                break;
 698                }
 699        return 0;
 700}
 701
 702static int parseaddr(uint8_t *to, char *from, int alen)
 703{
 704        char nip[4];
 705        char *p;
 706        int i;
 707
 708        p = from;
 709        for (i = 0; i < alen; i++) {
 710                if (*p == 0)
 711                        return -1;
 712                nip[0] = *p++;
 713                if (*p == 0)
 714                        return -1;
 715                nip[1] = *p++;
 716                nip[2] = 0;
 717                to[i] = strtoul(nip, 0, 16);
 718                if (*p == ':')
 719                        p++;
 720        }
 721        return 0;
 722}
 723
 724/*
 725 *  keep track of multicast addresses
 726 */
 727static char *netmulti(struct ether *nif, struct netfile *f, uint8_t *addr,
 728                      int add)
 729{
 730        struct netaddr **l, *ap;
 731        int i;
 732        uint32_t h;
 733
 734        if (nif->multicast == NULL)
 735                return "interface does not support multicast";
 736
 737        l = &nif->maddr;
 738        i = 0;
 739        for (ap = *l; ap; ap = *l) {
 740                if (memcmp(addr, ap->addr, nif->alen) == 0)
 741                        break;
 742                i++;
 743                l = &ap->next;
 744        }
 745
 746        if (add) {
 747                if (ap == 0) {
 748                        /* TODO: AFAIK, this never gets freed.  if we fix that,
 749                         * we can use a kref too (instead of int ap->ref). */
 750                        *l = ap = kzmalloc(sizeof(*ap), 0);
 751                        memmove(ap->addr, addr, nif->alen);
 752                        ap->next = 0;
 753                        ap->ref = 1;
 754                        h = hash(addr, nif->alen);
 755                        ap->hnext = nif->mhash[h];
 756                        nif->mhash[h] = ap;
 757                } else {
 758                        ap->ref++;
 759                }
 760                if (ap->ref == 1) {
 761                        nif->nmaddr++;
 762                        nif->multicast(nif->arg, addr, 1);
 763                }
 764                if (i < 8 * sizeof(f->maddr)) {
 765                        if ((f->maddr[i / 8] & (1 << (i % 8))) == 0)
 766                                f->nmaddr++;
 767                        f->maddr[i / 8] |= 1 << (i % 8);
 768                }
 769        } else {
 770                if (ap == 0 || ap->ref == 0)
 771                        return 0;
 772                ap->ref--;
 773                if (ap->ref == 0) {
 774                        nif->nmaddr--;
 775                        nif->multicast(nif->arg, addr, 0);
 776                }
 777                if (i < 8 * sizeof(f->maddr)) {
 778                        if ((f->maddr[i / 8] & (1 << (i % 8))) != 0)
 779                                f->nmaddr--;
 780                        f->maddr[i / 8] &= ~(1 << (i % 8));
 781                }
 782        }
 783        return 0;
 784}
 785
 786/* Prints the contents of stats to [va + offset, va + offset + amt). */
 787ssize_t linux_ifstat(struct netif_stats *stats, void *va, size_t amt,
 788                     off_t offset)
 789{
 790        char *p;
 791        size_t sofar = 0;
 792        ssize_t ret;
 793
 794        p = kzmalloc(READSTR, MEM_WAIT);
 795        sofar += snprintf(p + sofar, READSTR - sofar,
 796                          "rx pkts            : %lu\n", stats->rx_packets);
 797        sofar += snprintf(p + sofar, READSTR - sofar,
 798                          "tx pkts            : %lu\n", stats->tx_packets);
 799        sofar += snprintf(p + sofar, READSTR - sofar,
 800                          "rx bytes           : %lu\n", stats->rx_bytes);
 801        sofar += snprintf(p + sofar, READSTR - sofar,
 802                          "tx bytes           : %lu\n", stats->tx_bytes);
 803        sofar += snprintf(p + sofar, READSTR - sofar,
 804                          "rx errors          : %lu\n", stats->rx_errors);
 805        sofar += snprintf(p + sofar, READSTR - sofar,
 806                          "tx errors          : %lu\n", stats->tx_errors);
 807        sofar += snprintf(p + sofar, READSTR - sofar,
 808                          "rx dropped         : %lu\n", stats->rx_dropped);
 809        sofar += snprintf(p + sofar, READSTR - sofar,
 810                          "tx dropped         : %lu\n", stats->tx_dropped);
 811        sofar += snprintf(p + sofar, READSTR - sofar,
 812                          "multicast          : %lu\n", stats->multicast);
 813        sofar += snprintf(p + sofar, READSTR - sofar,
 814                          "collisions         : %lu\n", stats->collisions);
 815        sofar += snprintf(p + sofar, READSTR - sofar, "\n");
 816
 817        sofar += snprintf(p + sofar, READSTR - sofar,
 818                          "rx length errors   : %lu\n",
 819                          stats->rx_length_errors);
 820        sofar += snprintf(p + sofar, READSTR - sofar,
 821                          "rx over errors     : %lu\n", stats->rx_over_errors);
 822        sofar += snprintf(p + sofar, READSTR - sofar,
 823                          "rx crc errors      : %lu\n", stats->rx_crc_errors);
 824        sofar += snprintf(p + sofar, READSTR - sofar,
 825                          "rx frame errors    : %lu\n", stats->rx_frame_errors);
 826        sofar += snprintf(p + sofar, READSTR - sofar,
 827                          "rx fifo errors     : %lu\n", stats->rx_fifo_errors);
 828        sofar += snprintf(p + sofar, READSTR - sofar,
 829                          "rx missed errors   : %lu\n",
 830                          stats->rx_missed_errors);
 831        sofar += snprintf(p + sofar, READSTR - sofar, "\n");
 832
 833        sofar += snprintf(p + sofar, READSTR - sofar,
 834                          "tx aborted errors  : %lu\n",
 835                          stats->tx_aborted_errors);
 836        sofar += snprintf(p + sofar, READSTR - sofar,
 837                          "tx carrier errors  : %lu\n",
 838                          stats->tx_carrier_errors);
 839        sofar += snprintf(p + sofar, READSTR - sofar,
 840                          "tx fifo errors     : %lu\n", stats->tx_fifo_errors);
 841        sofar += snprintf(p + sofar, READSTR - sofar,
 842                          "tx heartbeat errors: %lu\n",
 843                          stats->tx_heartbeat_errors);
 844        sofar += snprintf(p + sofar, READSTR - sofar,
 845                          "tx window errors   : %lu\n",
 846                          stats->tx_window_errors);
 847        sofar += snprintf(p + sofar, READSTR - sofar, "\n");
 848
 849        sofar += snprintf(p + sofar, READSTR - sofar,
 850                          "rx compressed      : %lu\n", stats->rx_compressed);
 851        sofar += snprintf(p + sofar, READSTR - sofar,
 852                          "tx compressed      : %lu\n", stats->tx_compressed);
 853        sofar += snprintf(p + sofar, READSTR - sofar,
 854                          "rx nohandler       : %lu\n", stats->rx_nohandler);
 855        ret = readstr(offset, va, amt, p);
 856        kfree(p);
 857        return ret;
 858}
 859