akaros/kern/src/net/ethermedium.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
  41typedef struct Etherhdr Etherhdr;
  42struct Etherhdr {
  43        uint8_t d[6];
  44        uint8_t s[6];
  45        uint8_t t[2];
  46};
  47
  48static uint8_t ipbroadcast[IPaddrlen] = {
  49        0xff, 0xff, 0xff, 0xff,
  50        0xff, 0xff, 0xff, 0xff,
  51        0xff, 0xff, 0xff, 0xff,
  52        0xff, 0xff, 0xff, 0xff,
  53};
  54
  55static uint8_t etherbroadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  56
  57static void etherread4(void *a);
  58static void etherread6(void *a);
  59static void etherbind(struct Ipifc *ifc, int argc, char **argv);
  60static void etherunbind(struct Ipifc *ifc);
  61static void etherbwrite(struct Ipifc *ifc, struct block *bp, int version,
  62                        uint8_t *ip);
  63static void etheraddmulti(struct Ipifc *ifc, uint8_t * a, uint8_t * ia);
  64static void etherremmulti(struct Ipifc *ifc, uint8_t * a, uint8_t * ia);
  65static struct block *multicastarp(struct Fs *f, struct arpent *a,
  66                                  struct medium *m, uint8_t *mac);
  67static void sendarp(struct Ipifc *ifc, struct arpent *a);
  68static void sendgarp(struct Ipifc *ifc, uint8_t * unused_uint8_p_t);
  69static int multicastea(uint8_t * ea, uint8_t * ip);
  70static void recvarpproc(void *);
  71static void resolveaddr6(struct Ipifc *ifc, struct arpent *a);
  72static void etherpref2addr(uint8_t * pref, uint8_t * ea);
  73
  74struct medium ethermedium = {
  75        .name = "ether",
  76        .hsize = 14,
  77        .mintu = 60,
  78        .maxtu = 1514,
  79        .maclen = 6,
  80        .bind = etherbind,
  81        .unbind = etherunbind,
  82        .bwrite = etherbwrite,
  83        .addmulti = etheraddmulti,
  84        .remmulti = etherremmulti,
  85        .ares = arpenter,
  86        .areg = sendgarp,
  87        .pref2addr = etherpref2addr,
  88};
  89
  90struct medium trexmedium = {
  91        .name = "trex",
  92        .hsize = 14,
  93        .mintu = 60,
  94        .maxtu = 1514,
  95        .maclen = 6,
  96        .bind = etherbind,
  97        .unbind = etherunbind,
  98        .bwrite = etherbwrite,
  99        .addmulti = etheraddmulti,
 100        .remmulti = etherremmulti,
 101        .ares = arpenter,
 102        .areg = sendgarp,
 103        .pref2addr = etherpref2addr,
 104};
 105
 106typedef struct Etherrock Etherrock;
 107struct Etherrock {
 108        struct Fs *f;                   /* file system we belong to */
 109        struct proc *arpp;              /* arp process */
 110        struct proc *read4p;            /* reading process (v4) */
 111        struct proc *read6p;            /* reading process (v6) */
 112        struct chan *mchan4;            /* Data channel for v4 */
 113        struct chan *achan;             /* Arp channel */
 114        struct chan *cchan4;            /* Control channel for v4 */
 115        struct chan *mchan6;            /* Data channel for v6 */
 116        struct chan *cchan6;            /* Control channel for v6 */
 117};
 118
 119/*
 120 *  ethernet arp request
 121 */
 122enum {
 123        ETARP = 0x0806,
 124        ETIP4 = 0x0800,
 125        ETIP6 = 0x86DD,
 126        ARPREQUEST = 1,
 127        ARPREPLY = 2,
 128};
 129
 130typedef struct Etherarp Etherarp;
 131struct Etherarp {
 132        uint8_t d[6];
 133        uint8_t s[6];
 134        uint8_t type[2];
 135        uint8_t hrd[2];
 136        uint8_t pro[2];
 137        uint8_t hln;
 138        uint8_t pln;
 139        uint8_t op[2];
 140        uint8_t sha[6];
 141        uint8_t spa[4];
 142        uint8_t tha[6];
 143        uint8_t tpa[4];
 144};
 145
 146static char *nbmsg = "nonblocking";
 147
 148static unsigned int parsefeat(char *ptr)
 149{
 150        unsigned int feat = 0;
 151
 152        if (strstr(ptr, "ipck"))
 153                feat |= NETF_IPCK;
 154        if (strstr(ptr, "udpck"))
 155                feat |= NETF_UDPCK;
 156        if (strstr(ptr, "tcpck"))
 157                feat |= NETF_TCPCK;
 158        if (strstr(ptr, "padmin"))
 159                feat |= NETF_PADMIN;
 160        if (strstr(ptr, "sg"))
 161                feat |= NETF_SG;
 162        if (strstr(ptr, "tso"))
 163                feat |= NETF_TSO;
 164        if (strstr(ptr, "lro"))
 165                feat |= NETF_LRO;
 166        if (strstr(ptr, "rxcsum"))
 167                feat |= NETF_RXCSUM;
 168        return feat;
 169}
 170
 171/*
 172 *  called to bind an IP ifc to an ethernet device
 173 *  called with ifc wlock'd
 174 */
 175static void etherbind(struct Ipifc *ifc, int argc, char **argv)
 176{
 177        ERRSTACK(1);
 178        struct chan *mchan4, *cchan4, *achan, *mchan6, *cchan6;
 179        char *addr, *dir, *buf;
 180        int fd, cfd, n;
 181        char *ptr;
 182        Etherrock *er;
 183
 184        if (argc < 2)
 185                error(EINVAL, ERROR_FIXME);
 186
 187        addr = kmalloc(Maxpath, MEM_WAIT);      //char addr[2*KNAMELEN];
 188        dir = kmalloc(Maxpath, MEM_WAIT);       //char addr[2*KNAMELEN];
 189        mchan4 = cchan4 = achan = mchan6 = cchan6 = NULL;
 190        buf = NULL;
 191        if (waserror()) {
 192                if (mchan4 != NULL)
 193                        cclose(mchan4);
 194                if (cchan4 != NULL)
 195                        cclose(cchan4);
 196                if (achan != NULL)
 197                        cclose(achan);
 198                if (mchan6 != NULL)
 199                        cclose(mchan6);
 200                if (cchan6 != NULL)
 201                        cclose(cchan6);
 202                if (buf != NULL)
 203                        kfree(buf);
 204                kfree(addr);
 205                kfree(dir);
 206                nexterror();
 207        }
 208
 209        /*
 210         *  open ip converstation
 211         *
 212         *  the dial will fail if the type is already open on
 213         *  this device.
 214         */
 215        snprintf(addr, Maxpath, "%s!0x800", argv[2]);
 216        fd = kdial(addr, NULL, dir, &cfd);
 217        if (fd < 0)
 218                error(EFAIL, "dial 0x800 failed: %s", get_cur_errbuf());
 219        mchan4 = commonfdtochan(fd, O_RDWR, 0, 1);
 220        cchan4 = commonfdtochan(cfd, O_RDWR, 0, 1);
 221        sysclose(fd);
 222        sysclose(cfd);
 223
 224        /*
 225         *  make it non-blocking
 226         */
 227        devtab[cchan4->type].write(cchan4, nbmsg, strlen(nbmsg), 0);
 228
 229        /*
 230         *  get mac address and speed
 231         */
 232        snprintf(addr, Maxpath, "%s/stats", dir);
 233        fd = sysopen(addr, O_READ);
 234        if (fd < 0)
 235                error(EFAIL, "can't open ether stats: %s", get_cur_errbuf());
 236
 237        buf = kzmalloc(512, 0);
 238        n = sysread(fd, buf, 511);
 239        sysclose(fd);
 240        if (n <= 0)
 241                error(EIO, ERROR_FIXME);
 242        buf[n] = 0;
 243
 244        ptr = strstr(buf, "addr: ");
 245        if (!ptr)
 246                error(EIO, ERROR_FIXME);
 247        ptr += 6;
 248        parsemac(ifc->mac, ptr, 6);
 249
 250        ptr = strstr(buf, "feat: ");
 251        if (ptr) {
 252                ptr += 6;
 253                ifc->feat = parsefeat(ptr);
 254        } else {
 255                ifc->feat = 0;
 256        }
 257        /*
 258         *  open arp conversation
 259         */
 260        snprintf(addr, Maxpath, "%s!0x806", argv[2]);
 261        fd = kdial(addr, NULL, NULL, NULL);
 262        if (fd < 0)
 263                error(EFAIL, "dial 0x806 failed: %s", get_cur_errbuf());
 264        achan = commonfdtochan(fd, O_RDWR, 0, 1);
 265        sysclose(fd);
 266
 267        /*
 268         *  open ip conversation
 269         *
 270         *  the dial will fail if the type is already open on
 271         *  this device.
 272         */
 273        snprintf(addr, Maxpath, "%s!0x86DD", argv[2]);
 274        fd = kdial(addr, NULL, dir, &cfd);
 275        if (fd < 0)
 276                error(EFAIL, "dial 0x86DD failed: %s", get_cur_errbuf());
 277        mchan6 = commonfdtochan(fd, O_RDWR, 0, 1);
 278        cchan6 = commonfdtochan(cfd, O_RDWR, 0, 1);
 279        sysclose(fd);
 280        sysclose(cfd);
 281
 282        /*
 283         *  make it non-blocking
 284         */
 285        devtab[cchan6->type].write(cchan6, nbmsg, strlen(nbmsg), 0);
 286
 287        er = kzmalloc(sizeof(*er), 0);
 288        er->mchan4 = mchan4;
 289        er->cchan4 = cchan4;
 290        er->achan = achan;
 291        er->mchan6 = mchan6;
 292        er->cchan6 = cchan6;
 293        er->f = ifc->conv->p->f;
 294        ifc->arg = er;
 295
 296        kfree(buf);
 297        kfree(addr);
 298        kfree(dir);
 299        poperror();
 300
 301        ktask("etherread4", etherread4, ifc);
 302        ktask("recvarpproc", recvarpproc, ifc);
 303        ktask("etherread6", etherread6, ifc);
 304}
 305
 306/*
 307 *  called with ifc wlock'd
 308 */
 309static void etherunbind(struct Ipifc *ifc)
 310{
 311        Etherrock *er = ifc->arg;
 312        printk("[kernel] etherunbind not supported yet!\n");
 313
 314        // we'll need to tell the ktasks to exit, maybe via flags and a wakeup
 315#if 0
 316        if (er->read4p)
 317                postnote(er->read4p, 1, "unbind", 0);
 318        if (er->read6p)
 319                postnote(er->read6p, 1, "unbind", 0);
 320        if (er->arpp)
 321                postnote(er->arpp, 1, "unbind", 0);
 322#endif
 323
 324        /* wait for readers to die */
 325        while (er->arpp != 0 || er->read4p != 0 || er->read6p != 0)
 326                cpu_relax();
 327        kthread_usleep(300 * 1000);
 328
 329        if (er->mchan4 != NULL)
 330                cclose(er->mchan4);
 331        if (er->achan != NULL)
 332                cclose(er->achan);
 333        if (er->cchan4 != NULL)
 334                cclose(er->cchan4);
 335        if (er->mchan6 != NULL)
 336                cclose(er->mchan6);
 337        if (er->cchan6 != NULL)
 338                cclose(er->cchan6);
 339
 340        kfree(er);
 341}
 342
 343/*
 344 * copy ethernet address
 345 */
 346static inline void etherfilladdr(uint16_t *pkt, uint16_t *dst, uint16_t *src)
 347{
 348        *pkt++ = *dst++;
 349        *pkt++ = *dst++;
 350        *pkt++ = *dst++;
 351        *pkt++ = *src++;
 352        *pkt++ = *src++;
 353        *pkt = *src;
 354}
 355
 356/*
 357 *  called by ipoput with a single block to write with ifc rlock'd
 358 */
 359static void etherbwrite(struct Ipifc *ifc, struct block *bp, int version,
 360                        uint8_t *ip)
 361{
 362        Etherhdr *eh;
 363        struct arpent *a;
 364        uint8_t mac[6];
 365        Etherrock *er = ifc->arg;
 366
 367        ipifc_trace_block(ifc, bp);
 368        /* get mac address of destination.
 369         *
 370         * Locking is tricky here.  If we get arpent 'a' back, the f->arp is
 371         * qlocked.  if multicastarp returns bp, then it unlocked it for us.  if
 372         * not, sendarp or resolveaddr6 unlocked it for us.  yikes. */
 373        a = arpget(er->f->arp, bp, version, ifc, ip, mac);
 374        if (a) {
 375                /* check for broadcast or multicast.  if it is either, this
 376                 * sorts that out and returns the bp for the first packet on the
 377                 * arp's hold list.*/
 378                bp = multicastarp(er->f, a, ifc->m, mac);
 379                if (bp == NULL) {
 380                        switch (version) {
 381                                case V4:
 382                                        sendarp(ifc, a);
 383                                        break;
 384                                case V6:
 385                                        resolveaddr6(ifc, a);
 386                                        break;
 387                                default:
 388                                        panic("etherbwrite: version %d",
 389                                              version);
 390                        }
 391                        return;
 392                }
 393        }
 394
 395        /* make it a single block with space for the ether header */
 396        bp = padblock(bp, ifc->m->hsize);
 397        if (bp->next)
 398                bp = concatblock(bp);
 399        eh = (Etherhdr *) bp->rp;
 400
 401        /* copy in mac addresses and ether type */
 402        etherfilladdr((uint16_t *)bp->rp, (uint16_t *)mac,
 403                      (uint16_t *)ifc->mac);
 404
 405        switch (version) {
 406        case V4:
 407                eh->t[0] = 0x08;
 408                eh->t[1] = 0x00;
 409                devtab[er->mchan4->type].bwrite(er->mchan4, bp, 0);
 410                break;
 411        case V6:
 412                eh->t[0] = 0x86;
 413                eh->t[1] = 0xDD;
 414                devtab[er->mchan6->type].bwrite(er->mchan6, bp, 0);
 415                break;
 416        default:
 417                panic("etherbwrite2: version %d", version);
 418        }
 419        ifc->out++;
 420}
 421
 422/*
 423 *  process to read from the ethernet
 424 */
 425static void etherread4(void *a)
 426{
 427        ERRSTACK(2);
 428        struct Ipifc *ifc;
 429        struct block *bp;
 430        Etherrock *er;
 431
 432        ifc = a;
 433        er = ifc->arg;
 434        er->read4p = current;   /* hide identity under a rock for unbind */
 435        if (waserror()) {
 436                er->read4p = 0;
 437                poperror();
 438                warn("etherread4 returns, probably unexpectedly\n");
 439                return;
 440        }
 441        for (;;) {
 442                bp = devtab[er->mchan4->type].bread(er->mchan4, 128 * 1024, 0);
 443                if (!canrlock(&ifc->rwlock)) {
 444                        freeb(bp);
 445                        continue;
 446                }
 447                if (waserror()) {
 448                        runlock(&ifc->rwlock);
 449                        nexterror();
 450                }
 451                ifc->in++;
 452                bp->rp += ifc->m->hsize;
 453                if (ifc->lifc == NULL) {
 454                        freeb(bp);
 455                } else {
 456                        ipifc_trace_block(ifc, bp);
 457                        ipiput4(er->f, ifc, bp);
 458                }
 459                runlock(&ifc->rwlock);
 460                poperror();
 461        }
 462        poperror();
 463}
 464
 465/*
 466 *  process to read from the ethernet, IPv6
 467 */
 468static void etherread6(void *a)
 469{
 470        ERRSTACK(2);
 471        struct Ipifc *ifc;
 472        struct block *bp;
 473        Etherrock *er;
 474
 475        ifc = a;
 476        er = ifc->arg;
 477        er->read6p = current;   /* hide identity under a rock for unbind */
 478        if (waserror()) {
 479                er->read6p = 0;
 480                warn("etherread6 returns, probably unexpectedly\n");
 481                poperror();
 482                return;
 483        }
 484        for (;;) {
 485                bp = devtab[er->mchan6->type].bread(er->mchan6, ifc->maxtu, 0);
 486                if (!canrlock(&ifc->rwlock)) {
 487                        freeb(bp);
 488                        continue;
 489                }
 490                if (waserror()) {
 491                        runlock(&ifc->rwlock);
 492                        nexterror();
 493                }
 494                ifc->in++;
 495                bp->rp += ifc->m->hsize;
 496                if (ifc->lifc == NULL) {
 497                        freeb(bp);
 498                } else {
 499                        ipifc_trace_block(ifc, bp);
 500                        ipiput6(er->f, ifc, bp);
 501                }
 502                runlock(&ifc->rwlock);
 503                poperror();
 504        }
 505        poperror();
 506}
 507
 508static void etheraddmulti(struct Ipifc *ifc, uint8_t * a, uint8_t * unused)
 509{
 510        uint8_t mac[6];
 511        char buf[64];
 512        Etherrock *er = ifc->arg;
 513        int version;
 514
 515        version = multicastea(mac, a);
 516        snprintf(buf, sizeof(buf), "addmulti %E", mac);
 517        switch (version) {
 518        case V4:
 519                devtab[er->cchan4->type].write(er->cchan4, buf, strlen(buf), 0);
 520                break;
 521        case V6:
 522                devtab[er->cchan6->type].write(er->cchan6, buf, strlen(buf), 0);
 523                break;
 524        default:
 525                panic("etheraddmulti: version %d", version);
 526        }
 527}
 528
 529static void etherremmulti(struct Ipifc *ifc, uint8_t * a, uint8_t * unused)
 530{
 531        uint8_t mac[6];
 532        char buf[64];
 533        Etherrock *er = ifc->arg;
 534        int version;
 535
 536        version = multicastea(mac, a);
 537        snprintf(buf, sizeof(buf), "remmulti %E", mac);
 538        switch (version) {
 539        case V4:
 540                devtab[er->cchan4->type].write(er->cchan4, buf, strlen(buf), 0);
 541                break;
 542        case V6:
 543                devtab[er->cchan6->type].write(er->cchan6, buf, strlen(buf), 0);
 544                break;
 545        default:
 546                panic("etherremmulti: version %d", version);
 547        }
 548}
 549
 550/*
 551 *  send an ethernet arp
 552 *  (only v4, v6 uses the neighbor discovery, rfc1970)
 553 *
 554 * May drop packets on stale arps. */
 555static void sendarp(struct Ipifc *ifc, struct arpent *a)
 556{
 557        int n;
 558        struct block *bp;
 559        Etherarp *e;
 560        Etherrock *er = ifc->arg;
 561
 562        /* don't do anything if it's been less than a second since the last.
 563         * ctime is set to 0 for the first time through.  we hold the f->arp
 564         * qlock, so there shouldn't be a problem with another arp request for
 565         * this same arpent coming down til we update ctime again. */
 566        if (NOW - a->ctime < 1000) {
 567                arprelease(er->f->arp, a);
 568                return;
 569        }
 570
 571        /* remove all but the last message.  brho: this might be unnecessary.
 572         * we'll eventually send them.  but they should be quite stale at this
 573         * point. */
 574        while ((bp = a->hold) != NULL) {
 575                if (bp == a->last)
 576                        break;
 577                a->hold = bp->list;
 578                freeblist(bp);
 579        }
 580
 581        /* update last sent time */
 582        a->ctime = NOW;
 583        arprelease(er->f->arp, a);
 584
 585        n = sizeof(Etherarp);
 586        if (n < a->type->mintu)
 587                n = a->type->mintu;
 588        bp = block_alloc(n, MEM_WAIT);
 589        memset(bp->rp, 0, n);
 590        e = (Etherarp *) bp->rp;
 591        memmove(e->tpa, a->ip + IPv4off, sizeof(e->tpa));
 592        ipv4local(ifc, e->spa);
 593        memmove(e->sha, ifc->mac, sizeof(e->sha));
 594        memset(e->d, 0xff, sizeof(e->d));       /* ethernet broadcast */
 595        memmove(e->s, ifc->mac, sizeof(e->s));
 596
 597        hnputs(e->type, ETARP);
 598        hnputs(e->hrd, 1);
 599        hnputs(e->pro, ETIP4);
 600        e->hln = sizeof(e->sha);
 601        e->pln = sizeof(e->spa);
 602        hnputs(e->op, ARPREQUEST);
 603        bp->wp += n;
 604
 605        n = devtab[er->achan->type].bwrite(er->achan, bp, 0);
 606        if (n < 0)
 607                printd("arp: send: %r\n");
 608}
 609
 610static void resolveaddr6(struct Ipifc *ifc, struct arpent *a)
 611{
 612        int sflag;
 613        struct block *bp;
 614        Etherrock *er = ifc->arg;
 615        uint8_t ipsrc[IPaddrlen];
 616
 617        /* don't do anything if it's been less than a second since the last */
 618        if (NOW - a->ctime < ReTransTimer) {
 619                arprelease(er->f->arp, a);
 620                return;
 621        }
 622
 623        /* remove all but the last message */
 624        while ((bp = a->hold) != NULL) {
 625                if (bp == a->last)
 626                        break;
 627                a->hold = bp->list;
 628                freeblist(bp);
 629        }
 630
 631        /* try to keep it around for a second more */
 632        a->ctime = NOW;
 633        a->rtime = NOW + ReTransTimer;
 634        if (a->rxtsrem <= 0) {
 635                arprelease(er->f->arp, a);
 636                return;
 637        }
 638
 639        a->rxtsrem--;
 640        arprelease(er->f->arp, a);
 641
 642        if ((sflag = ipv6anylocal(ifc, ipsrc)))
 643                icmpns(er->f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac);
 644}
 645
 646/*
 647 *  send a gratuitous arp to refresh arp caches
 648 */
 649static void sendgarp(struct Ipifc *ifc, uint8_t * ip)
 650{
 651        int n;
 652        struct block *bp;
 653        Etherarp *e;
 654        Etherrock *er = ifc->arg;
 655
 656        /* don't arp for our initial non address */
 657        if (ipcmp(ip, IPnoaddr) == 0)
 658                return;
 659
 660        n = sizeof(Etherarp);
 661        if (n < ifc->m->mintu)
 662                n = ifc->m->mintu;
 663        bp = block_alloc(n, MEM_WAIT);
 664        memset(bp->rp, 0, n);
 665        e = (Etherarp *) bp->rp;
 666        memmove(e->tpa, ip + IPv4off, sizeof(e->tpa));
 667        memmove(e->spa, ip + IPv4off, sizeof(e->spa));
 668        memmove(e->sha, ifc->mac, sizeof(e->sha));
 669        memset(e->d, 0xff, sizeof(e->d));       /* ethernet broadcast */
 670        memmove(e->s, ifc->mac, sizeof(e->s));
 671
 672        hnputs(e->type, ETARP);
 673        hnputs(e->hrd, 1);
 674        hnputs(e->pro, ETIP4);
 675        e->hln = sizeof(e->sha);
 676        e->pln = sizeof(e->spa);
 677        hnputs(e->op, ARPREQUEST);
 678        bp->wp += n;
 679
 680        n = devtab[er->achan->type].bwrite(er->achan, bp, 0);
 681        if (n < 0)
 682                printd("garp: send: %r\n");
 683}
 684
 685static void recvarp(struct Ipifc *ifc)
 686{
 687        int n;
 688        struct block *ebp, *rbp;
 689        Etherarp *e, *r;
 690        uint8_t ip[IPaddrlen];
 691        static uint8_t eprinted[4];
 692        Etherrock *er = ifc->arg;
 693
 694        ebp = devtab[er->achan->type].bread(er->achan, ifc->maxtu, 0);
 695        if (ebp == NULL) {
 696                printd("arp: rcv: %r\n");
 697                return;
 698        }
 699
 700        e = (Etherarp *) ebp->rp;
 701        switch (nhgets(e->op)) {
 702        default:
 703                break;
 704
 705        case ARPREPLY:
 706                /* check for machine using my ip address */
 707                v4tov6(ip, e->spa);
 708                if (iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)) {
 709                        if (memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0) {
 710                                printd("arprep: 0x%E/0x%E also has ip addr %V\n",
 711                                           e->s, e->sha, e->spa);
 712                                break;
 713                        }
 714                }
 715
 716                /* make sure we're not entering broadcast addresses */
 717                if (ipcmp(ip, ipbroadcast) == 0 ||
 718                    !memcmp(e->sha, etherbroadcast, sizeof(e->sha))) {
 719                        printd("arprep: 0x%E/0x%E cannot register broadcast address %I\n",
 720                               e->s, e->sha, e->spa);
 721                        break;
 722                }
 723
 724                arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 0);
 725                break;
 726
 727        case ARPREQUEST:
 728                /* don't answer arps till we know who we are */
 729                if (ifc->lifc == 0)
 730                        break;
 731
 732                /* check for machine using my ip or ether address */
 733                v4tov6(ip, e->spa);
 734                if (iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)) {
 735                        if (memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0) {
 736                                if (memcmp(eprinted, e->spa, sizeof(e->spa))) {
 737                                        /* print only once */
 738                                        printd("arpreq: 0x%E also has ip addr %V\n",
 739                                               e->sha, e->spa);
 740                                        memmove(eprinted, e->spa,
 741                                                sizeof(e->spa));
 742                                }
 743                        }
 744                } else {
 745                        if (memcmp(e->sha, ifc->mac, sizeof(e->sha)) == 0) {
 746                                printd("arpreq: %V also has ether addr %E\n",
 747                                       e->spa, e->sha);
 748                                break;
 749                        }
 750                }
 751
 752                /* refresh what we know about sender */
 753                arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 1);
 754
 755                /* answer only requests for our address or systems we're
 756                 * proxying for */
 757                v4tov6(ip, e->tpa);
 758                if (!iplocalonifc(ifc, ip))
 759                        if (!ipproxyifc(er->f, ifc, ip))
 760                                break;
 761
 762                n = sizeof(Etherarp);
 763                if (n < ifc->mintu)
 764                        n = ifc->mintu;
 765                rbp = block_alloc(n, MEM_WAIT);
 766                r = (Etherarp *) rbp->rp;
 767                memset(r, 0, sizeof(Etherarp));
 768                hnputs(r->type, ETARP);
 769                hnputs(r->hrd, 1);
 770                hnputs(r->pro, ETIP4);
 771                r->hln = sizeof(r->sha);
 772                r->pln = sizeof(r->spa);
 773                hnputs(r->op, ARPREPLY);
 774                memmove(r->tha, e->sha, sizeof(r->tha));
 775                memmove(r->tpa, e->spa, sizeof(r->tpa));
 776                memmove(r->sha, ifc->mac, sizeof(r->sha));
 777                memmove(r->spa, e->tpa, sizeof(r->spa));
 778                memmove(r->d, e->sha, sizeof(r->d));
 779                memmove(r->s, ifc->mac, sizeof(r->s));
 780                rbp->wp += n;
 781
 782                n = devtab[er->achan->type].bwrite(er->achan, rbp, 0);
 783                if (n < 0)
 784                        printd("arp: write: %r\n");
 785        }
 786        freeb(ebp);
 787}
 788
 789static void recvarpproc(void *v)
 790{
 791        ERRSTACK(1);
 792        struct Ipifc *ifc = v;
 793        Etherrock *er = ifc->arg;
 794
 795        er->arpp = current;
 796        if (waserror()) {
 797                er->arpp = 0;
 798                warn("recvarpproc returns, probably unexpectedly\n");
 799                poperror();
 800                return;
 801        }
 802        for (;;)
 803                recvarp(ifc);
 804        poperror();
 805}
 806
 807static int multicastea(uint8_t * ea, uint8_t * ip)
 808{
 809        int x;
 810
 811        switch (x = ipismulticast(ip)) {
 812        case V4:
 813                ea[0] = 0x01;
 814                ea[1] = 0x00;
 815                ea[2] = 0x5e;
 816                ea[3] = ip[13] & 0x7f;
 817                ea[4] = ip[14];
 818                ea[5] = ip[15];
 819                break;
 820        case V6:
 821                ea[0] = 0x33;
 822                ea[1] = 0x33;
 823                ea[2] = ip[12];
 824                ea[3] = ip[13];
 825                ea[4] = ip[14];
 826                ea[5] = ip[15];
 827                break;
 828        }
 829        return x;
 830}
 831
 832/*
 833 *  fill in an arp entry for broadcast or multicast
 834 *  addresses.  Return the first queued packet for the
 835 *  IP address.
 836 */
 837static struct block *multicastarp(struct Fs *f, struct arpent *a,
 838                                  struct medium *medium, uint8_t *mac)
 839{
 840        /* is it broadcast? */
 841        switch (ipforme(f, a->ip)) {
 842        case Runi:
 843                return NULL;
 844        case Rbcast:
 845                memset(mac, 0xff, 6);
 846                return arpresolve(f->arp, a, medium, mac);
 847        default:
 848                break;
 849        }
 850
 851        /* if multicast, fill in mac */
 852        switch (multicastea(mac, a->ip)) {
 853        case V4:
 854        case V6:
 855                return arpresolve(f->arp, a, medium, mac);
 856        }
 857
 858        /* let arp take care of it */
 859        return NULL;
 860}
 861
 862linker_func_4(ethermediumlink)
 863{
 864        addipmedium(&ethermedium);
 865        addipmedium(&trexmedium);
 866}
 867
 868static void etherpref2addr(uint8_t * pref, uint8_t * ea)
 869{
 870        pref[8] = ea[0] | 0x2;
 871        pref[9] = ea[1];
 872        pref[10] = ea[2];
 873        pref[11] = 0xFF;
 874        pref[12] = 0xFE;
 875        pref[13] = ea[3];
 876        pref[14] = ea[4];
 877        pref[15] = ea[5];
 878}
 879