// 802.1x thingy // // what do we have to do: // // be able to send/receive EAPOL frames // implement Supplicant state machine and sub-machine // set wep keys when applicable // // 802.1x thingy // // // our job: // // get access tocard interface // read/write eapol frames // be able to set wep keys // // supplicant state machine // key receival state machine // auth interaction #include #include #include #include #include "dat.h" #include "fns.h" typedef enum PortControl { Auto, ForceUnauthorized, ForceAuthorized, } PortControl; static char * sPortControl[] = { "Auto", "ForceUnauthorized", "ForceAuthorized", }; typedef enum AuthState { Unauthorized, Authorized, } AuthState; static char *sAuthState[] = { "Unauthorized", "Authorized", }; // Supplicant PAE state machine (8.2.11) states enum { Logoff, Disconnected, Connecting, Authenticating, Held, Authenticated, Restart, ForceAuth, ForceUnauth, }; static char *pnames[] = { [Logoff] "Logoff", [Disconnected] "Disconnected", [Connecting] "Connecting", [Authenticating] "Authenticating", [Held] "Held", [Authenticated] "Authenticated", [Restart] "Restart", [ForceAuth] "ForceAuth", [ForceUnauth] "ForceUnauth", }; // Supplicant Backend state machine (8.2.12) states enum { Request, Response, Success, Fail, Timeout, Idle, Initialize, Receive, }; static char *bnames[] = { [Request] "Request", [Response] "Response", [Success] "Success", [Fail] "Fail", [Timeout] "Timeout", [Idle] "Idle", [Initialize] "Initialize", [Receive] "Receive", }; typedef struct backendstate { // Supplicant Backend state machine constants (sect 8.2.12.1.2) int authPeriod; int bState; // Backend state // Supplicant Backend state machine variables (sect 8.2.12.1.1) int eapNoResp; int eapReq; int eapResp; // Timers (sect 8.2.2.1) Timer *authWhile; } backendstate; typedef struct PAEstate { // Supplicant PAE state machine constants (sect 8.2.11.1.2) int heldPeriod; int startPeriod; int maxStart; // Supplicant PAE state machine variables (sect 8.2.11.1) int eapRestart; int logoffSent; PortControl portMode; // sPortMode; int startCount; int userLogoff; int pState; // PAE state // Timers (sect 8.2.2.1) Timer *heldWhile; Timer *startWhen; backendstate; Etherstate *e; Phasestate *p; // Global variables (sect 8.2.2.2) int eapolEap; int initialize; PortControl portControl; int portEnabled; AuthState portStatus; int portValid; // needs work. happens if we cannot see the AP; ifstats shows ap = 4444444 or so int suppAbort; int suppFail; AuthState suppPortStatus; int suppSuccess; int suppTimeout; // other int expectTtlsStart; Packet *rxEtherEap; Packet *txEtherEap; char *prompt; char *options; Channel *tickchan; Channel *portchan; Channel *backstart; Channel *backdone; vlong paeTime; vlong backTime; vlong startTime; vlong restartTime; vlong eapidTime; vlong noteTime; } PAEstate; // other static char buf[2048]; // ========== Port timers 'state machine' (8.2.3) // see timer.c // ========== static PAEstate theState; // ========== receive eapol frames // see ether.c:/^etherproc // ========== Key receive 'state machine' (8.2.7) // XXX do we do this in a separate thread/proc, or in the main one? // see key.c:/^handleKey // ========== Supplicant backend state machine // clean up/initialize static void abortSupp(PAEstate *s) { s->e->eapSuccess = 0; s->e->eapFail = 0; s->eapNoResp = 0; s->eapReq = 0; s->eapResp = 0; s->suppAbort = 0; // abortTTLS(); } static void build_eap(Eap*t, int code, int id, int datalen) { t->code = code; t->id = id; hnputs(t->ln, EAPHDR + datalen); } static void show_notification(PAEstate *s, uchar *m, int l) { // should do better: rfc3748 says: // s contains UTF-8 encoded ISO 10646 [RFC2279]. s->noteTime = nsec(); memset(buf, 0, sizeof(m)); memcpy(buf, m, l); logall("notification: %s", buf); appendlog(getNotesbuf(), 0, "%s", buf); } static void getSuppRsp(PAEstate *s) { // handle rxEtherEap // build txEtherEap Packet *rx, *tx; uchar *p, *beyond; char *ident, *prompt; int len; int tlssucces, tlsfailed; loglog("getSuppRsp %p", s->rxEtherEap); if (s->eapResp || s->eapNoResp) logall("oops... getSuppRsp called while result previous of prev call pending"); rx = s->rxEtherEap; tx = s->e->pktt; s->txEtherEap = tx; memset(s->txEtherEap->b, 0, Pktlen); switch(rx->eap->code) { case EapRequest: if (debug) print("getSuppRsp EapRequest: %d \n", rx->eap->data[0]); switch(rx->eap->data[0]) { case EapTpIdentity: // data format: [ prompt ] [ '\0' piggy-backed-options ] // show prompt? extract options? beyond = rx->eapol->data + nhgets(rx->eap->ln); p = &rx->eap->data[1]; prompt = (char*)p; for (; *p != '\0' && p+1 < beyond; p++) ; memset(buf, 0, sizeof(buf)); if (*p == '\0' && p+1 < beyond) { memcpy(buf, p+1, beyond - (p+1)); logall("received EAP Identity request, prompt=\"%s\" options=\"%s\"", prompt, buf); free(s->prompt); s->prompt = strdup(prompt); free(s->options); s->options = strdup(buf); // while ((p = strchr(buf, ',')) != nil) // *p = '\n'; } else { memcpy(buf, &rx->eap->data[1], beyond - &rx->eap->data[1]); logall("received EAP Identity request, data=\"%s\"", buf); free(s->prompt); s->prompt = strdup(buf); free(s->options); s->options = nil; } // the following is a HACK. // but: SNT macosX notes only mention config of // internal username and password (for TTLS-PAP), // and allow leaving external identity blank. // rfc3748 specifically says to _not_ include the // username in the external identity if ((ident = strchr(myId, '@')) == nil) ident = ""; tx->eap->data[0] = EapTpIdentity; memcpy(&tx->eap->data[1], ident, strlen(ident)); build_eap(tx->eap, EapResponse, rx->eap->id, 1+strlen(ident)); s->eapResp = 1; s->expectTtlsStart = 1; break; case EapTpNotification: tx->eap->data[0] = EapTpNotification; build_eap(tx->eap, EapResponse, rx->eap->id, 1); s->eapResp = 1; show_notification(s, &rx->eap->data[1] , nhgets(rx->eap->ln)-EAPHDR+1); break; case EapTpTtls: tlssucces = 0; tlsfailed = 0; rx->p = rx->eap->data; rx->n = nhgets(rx->eap->ln)-EAPHDR; tx->p = tx->eap->data; tx->n = ETHERMAXTU-ETHERHDR-EAPOLHDR-EAPHDR; len = processTTLS(rx, tx, s->expectTtlsStart, &tlssucces, &tlsfailed); if (tlsfailed) loglog("processTTLS failed"); s->expectTtlsStart = 0; if (len > 0) { build_eap(tx->eap, EapResponse, rx->eap->id, len); s->eapResp = 1; } else s->eapNoResp = 1; break; case EapTpNak: // only allowed in responses case EapTpExtp: case EapTpExus: default: // tell we can't deal with this type; tell we can only do ttls tx->eap->data[0] = EapTpNak; tx->eap->data[1] = EapTpTtls; build_eap(tx->eap, EapResponse, rx->eap->id, 1+1); s->eapResp = 1; break; } break; default: logall("getSuppRsp unexpected eap type %d", rx->eap->code); break; } if (s->eapResp) { memcpy(tx->ether->s, rx->ether->d, 6); memcpy(tx->ether->d, rx->ether->s, 6); memcpy(tx->ether->t, rx->ether->t, 2); tx->eapol->ver = rx->eapol->ver; tx->eapol->tp = rx->eapol->tp; memcpy(tx->eapol->ln, tx->eap->ln, 2); tx->e = tx->eapol->data + nhgets(tx->eap->ln); } if (!(s->eapResp || s->eapNoResp || s->e->eapSuccess || s->e->eapFail)) logall("internal error - no eap result set\n"); // prepare for reuse memset(rx->b, 0, Pktlen); rx->e = 0; // or rx->e = rx->b; ??? s->eapReq = 0; } // transmit EAP-Packet EAPOL frame to Authenticator static void txSuppRsp(PAEstate *s) { int n, l, len; Packet *p; p = s->txEtherEap; len = p->e - p->b; l = (len > ETHERMINTU) ? len : ETHERMINTU; if (debug) print("txSuppRsp writing to ether l=%d L=%d\n", l, len); if (p->eapol->tp == EapolTpEap && p->eap->code == EapResponse && p->eap->data[0] == EapTpIdentity) { logall("sending eap external identity"); s->eapidTime = nsec(); } n = write(s->e->fd, p->b, l); if (n != l) logall("txSuppRsp: written %d of %d: %r", n, l); loglog("txSuppRsp: written %d", n); } static void btrans(PAEstate *s, int new) { loglog("back trans: %s -> %s",bnames[s->bState], bnames[new]); s->bState = new; s->backTime = nsec(); } static int back(PAEstate *s) { Packet *rx; int done; Alt a[] = { /* c v op */ {s->e->packetc, &rx, CHANRCV}, {s->tickchan, nil, CHANRCV}, {s->e->statusc, nil, CHANRCV}, {nil, nil, CHANEND}, }; if (s->bState != Initialize && (s->initialize || s->suppAbort)) btrans(s, Initialize); switch(s->bState) { case Initialize: abortSupp(s); s->suppAbort = 0; if (!s->initialize && !s->suppAbort) { } else logall("back Initialize: should not happen"); btrans(s, Idle); break; case Idle: send(s->backdone, nil); recv(s->backstart, nil); if (s->eapolEap) btrans(s, Request); else if(s->e->eapSuccess) btrans(s, Success); else if(s->e->eapFail) btrans(s, Fail); else if(s->suppAbort ) btrans(s, Initialize); else logfatal(0, "back Idle: should not happen"); break; case Request: s->eapReq = 1; getSuppRsp(s); if (s->eapResp) btrans(s, Response); else if (s->eapNoResp) btrans(s, Receive); else if (s->e->eapFail) btrans(s, Fail); else if (s->e->eapSuccess) btrans(s, Success); else if(s->suppAbort ) btrans(s, Initialize); else logfatal(0, "back Request: should not happen"); break; case Response: txSuppRsp(s); s->eapResp = 0; btrans(s, Receive); break; case Receive: s->eapolEap = 0; startTimer(s->authWhile, s->authPeriod); done = 0; while(!done) switch(alt(a)) { case 0: /* eap received */ loglog("back Receive eap received"); done = 1; s->rxEtherEap = rx; s->eapolEap = 1; btrans(s, Request); break; case 1: /* timer tick */ // loglog("back Receive timer tick"); tickTimer(s->authWhile); if (s->authWhile->counter == 0) { logall("authWhile timer expired"); done = 1; btrans(s, Timeout); } break; case 2: /* eapSuccess or eapFail */ loglog("back Receive eapSuccess or eapFail"); done = 1; if (s->e->eapFail) btrans(s, Fail); else if (s->e->eapSuccess) btrans(s, Success); else logfatal(0, "back Receive 2: should not happen"); break; default: logfatal(0, "back Receive: can't happen"); } resetTimer(s->authWhile); s->eapNoResp = 0; break; case Success: // try to avoid race: first set vars, then unset s->eapSuccess s->suppSuccess = 1; s->e->keyRun=1; s->portValid = 1; // we should actually check this s->e->eapSuccess = 0; btrans(s, Idle); break; case Fail: s->suppFail = 1; s->e->eapFail = 0; btrans(s, Idle); break; case Timeout: s->suppTimeout = 1; btrans(s, Idle); break; } return s->bState; } void backproc(void *arg) { PAEstate *s; s = arg; for(;;) { back(s); } } // ========== Supplicant PAE state machine static void waitUntilUserLoggedOn(PAEstate *s) { s->userLogoff = 0; } static void waitUntilPortEnabled(PAEstate *s) { s->portEnabled = 1; } static void acknowledgeStart(PAEstate *s) { loglog("------ restarting ------"); syslog(0, logname, "restarting"); s->restartTime = nsec(); } // build EAPOL-Start frame and transmit to Authenticator static void txStart(PAEstate *s) { Packet *tx; // get fresh ap mac - we may have roamed if (apetheraddr(s->e->apmac, s->e->dir) < 0) logfatal(0, "could not read access point ether address from %s", s->e->dir); loglog("sending EAPOL Start frame to %E", s->e->apmac); tx = s->e->pktt; s->txEtherEap = tx; memset(s->txEtherEap->b, 0, Pktlen); tx->eapol->ver = EapolVersion; tx->eapol->tp = EapolTpStart; memset(tx->eapol->ln, 0, 2); memcpy(tx->ether->s, s->e->ourmac, 6); memcpy(tx->ether->d, s->e->apmac, 6); hnputs(tx->ether->t, ETEAPOL); tx->e = tx->eapol->data; txSuppRsp(s); } // build EAPOL-Logoff frame and transmit to Authenticator static void txLogoff(PAEstate *s) { USED(s); } static void ptrans(PAEstate *s, int new) { loglog("pae trans: %s -> %s", pnames[s->pState], pnames[new]); s->pState = new; s->paeTime = nsec(); } static int pae(PAEstate *s) { int val, res; Packet *rx; int done; Alt a_c[] = { /* c v op */ {s->e->packetc, &rx, CHANRCV}, {s->tickchan, nil, CHANRCV}, {s->e->statusc, nil, CHANRCV}, {nil, nil, CHANEND}, }; Alt a_h[] = { /* c v op */ {s->e->packetc, &rx, CHANRCV}, {s->tickchan, nil, CHANRCV}, {nil, nil, CHANEND}, }; Alt a_a[] = { /* c v op */ {s->e->packetc, &rx, CHANRCV}, {s->portchan, &val, CHANRCV}, {nil, nil, CHANEND}, }; if (s->pState != Logoff && s->portEnabled && !s->initialize && s->userLogoff && !s->logoffSent) ptrans(s, Logoff); else if (s->pState != Disconnected && (s->initialize || !s->portEnabled || (s->portControl == Auto && s->portMode != s->portControl))) ptrans(s, Disconnected); else if (s->pState != ForceAuth && s->portEnabled && !s->initialize && s->portControl == ForceAuthorized && s->portMode != s->portControl) ptrans(s, ForceAuth); else if (s->pState != ForceUnauth && s->portEnabled && !s->initialize && s->portControl == ForceUnauthorized && s->portMode != s->portControl) ptrans(s, ForceUnauth); switch(s->pState) { case Logoff: txLogoff(s); s->logoffSent = 1; s->suppPortStatus = Unauthorized; waitUntilUserLoggedOn(s); // s->userLogoff = 0 ptrans(s, Disconnected); break; case Disconnected: s->portMode = Auto; s->startCount = 0; s->logoffSent = 0; s->suppPortStatus = Unauthorized; s->suppAbort = 1; send(s->backstart, nil); recv(s->backdone, nil); waitUntilPortEnabled(s); // s->portEnabled = 1 ptrans(s, Connecting); break; case Connecting: s->startCount ++; s->eapolEap = 0; clearlog(getNotesbuf()); // when should we do this??? txStart(s); startTimer(s->startWhen, s->startPeriod); done = 0; while(!done) switch(res = alt(a_c)) { case 0: /* eap received */ loglog("pae Connecting eap received"); done = 1; s->rxEtherEap = rx; s->eapolEap = 1; ptrans(s, Restart); break; case 1: /* timer tick */ // loglog("pae Connecting startWhen timer tick"); tickTimer(s->startWhen); if (s->startWhen->counter == 0) { logall("startWhen timer expired"); done = 1; if (s->startCount < s->maxStart) ptrans(s, Connecting); else if (s->startCount >= s->maxStart && s->portValid) { logall("startCount >= maxStart (==%d), assume auth-ed", s->maxStart); ptrans(s, Authenticated); } else if (s->startCount >= s->maxStart) { logall("startCount >= maxStart (==%d), but port not valid", s->maxStart); ptrans(s, Held); } else logfatal(0, "pae Connecting 0: should not happen"); } break; case 2: /* eapSuccess or eapFail */ loglog("pae Connecting eapSuccess or eapFail"); done = 1; if (s->e->eapSuccess || s->e->eapFail) ptrans(s, Authenticating); else logfatal(0, "pae Connecting 3: should not happen"); break; default: logfatal(0, "pae Connecting can't happen:%d", res); } resetTimer(s->startWhen); break; case Authenticating: s->startCount = 0; s->suppSuccess = 0; s->suppFail = 0; s->suppTimeout = 0; s->e->keyRun = 0; s->e->keyDone = 0; send(s->backstart, nil); recv(s->backdone, nil); if (s->suppSuccess && s->portValid) ptrans(s, Authenticated); else if(s->suppSuccess) USED(s); // ??? else if (s->suppFail || (s->e->keyDone && !s->portValid)) ptrans(s, Held); else if (s->suppTimeout) ptrans(s, Connecting); else logfatal(0, "pae Authenticating: should not happen"); break; case Held: startTimer(s->heldWhile, s->heldPeriod); s->suppPortStatus = Unauthorized; done = 0; while(!done) switch(res = alt(a_h)) { case 0: /* eap received */ loglog("pae Held eap received"); done = 1; s->rxEtherEap = rx; s->eapolEap = 1; ptrans(s, Restart); break; case 1: /* timer tick */ // loglog("pae Held timer tick"); tickTimer(s->heldWhile); if (s->heldWhile->counter == 0) { logall("heldWhile timer expired"); done = 1; ptrans(s, Connecting); } break; default: logfatal(0, "pae Held can't happen:%d", res); } resetTimer(s->heldWhile); break; case Authenticated: s->suppPortStatus = Authorized; switch(res = alt(a_a)) { case 0: /* eap received */ loglog("pae Authenticated eap received"); if (s->portValid) { s->rxEtherEap = rx; s->eapolEap = 1; ptrans(s, Restart); } break; case 1: /* port validity changed */ loglog("pae Authenticated port validity changed"); s->portValid = val; if (!s->portValid) ptrans(s, Disconnected); break; default: logfatal(0, "pae Authenticated can't happen:%d", res); } break; case Restart: acknowledgeStart(s); ptrans(s, Authenticating); break; case ForceAuth: s->suppPortStatus = Authorized; s->portMode = ForceAuthorized; break; case ForceUnauth: s->suppPortStatus = Unauthorized; s->portMode = ForceUnauthorized; // no check?? txLogoff(s); s->logoffSent = 1; break; } //print("pae return: %s\n", pnames[s->pState]); return s->pState; } // ========== run state machine void paeproc(void *arg) { PAEstate *s; s = arg; for(;;) { pae(s); } } // ========== fs support typedef struct timeInfo { int id; vlong time; } timeInfo; static int cmptimenfo(void*a, void*b) { timeInfo *ta, *tb; vlong td; ta = a ; tb = b; if (ta->time == 0 && tb->time > 0) return 1; if (ta->time > 0 && tb->time == 0) return -1; td = ta->time - tb->time; if (td > 0) return 1; if (td < 0) return -1; return ta->id - tb->id; } enum { InitTime = 0, StartTime, PaeTime, BackTime, eapidTime, verdictTime, ph1startTime, ph1doneTime, ph2startTime, ph2doneTime, KeyTime, NoteTime, NTime, }; static char *timeNames[] = { [InitTime] "Init", [StartTime] "Start", [PaeTime] "Pae", [BackTime] "Back", [eapidTime] "Eapid", [verdictTime] "Verdict", [ph1startTime] "Ph1start", [ph1doneTime] "Ph1done", [ph2startTime] "Ph2start", [ph2doneTime] "Ph2done", [KeyTime] "Key", [NoteTime] "Note", [NTime] "", }; static timeInfo t[NTime]; static timeInfo* getPAETimes(PAEstate *s) { t[InitTime].time = s->startTime; t[InitTime].id = InitTime; t[StartTime].time = s->restartTime; t[StartTime].id = StartTime; t[PaeTime].time = s->paeTime; t[PaeTime].id = PaeTime; t[BackTime].time = s->backTime; t[BackTime].id = BackTime; t[eapidTime].time = s->eapidTime; t[eapidTime].id = eapidTime; t[verdictTime].time = s->e->verdictTime; t[verdictTime].id = verdictTime; t[ph1startTime].time = s->p[0].startTime; t[ph1startTime].id = ph1startTime; t[ph1doneTime].time = s->p[0].doneTime; t[ph1doneTime].id = ph1doneTime; t[ph2startTime].time = s->p[1].startTime; t[ph2startTime].id = ph2startTime; t[ph2doneTime].time = s->p[1].doneTime; t[ph2doneTime].id = ph2doneTime; t[KeyTime].time = s->e->keyTime; t[KeyTime].id = KeyTime; t[NoteTime].time = s->noteTime; t[NoteTime].id = NoteTime; t[NTime].time = 0; t[NTime].id = NTime; qsort(t, NTime, sizeof(timeInfo), cmptimenfo); return t; } void getPAEStatus(char *b, int n) { PAEstate *s; timeInfo *t; int i; s = &theState; *b = '\0'; seprint(b+strlen(b), b+n, "Verdict:\t%s\n", ((s->e->eapSuccess || s->suppSuccess) ? "success" : ((s->e->eapFail || s->suppFail) ? "fail" : ""))); seprint(b+strlen(b), b+n, "AccessPoint: %E\n", s->e->apmac); seprint(b+strlen(b), b+n, "EapId Prompt: %s\n", getstring(s->prompt)); seprint(b+strlen(b), b+n, "EapId Options: %s\n", getstring(s->options)); seprint(b+strlen(b), b+n, "Ph1type: %s\n", getstring(s->p[0].type)); seprint(b+strlen(b), b+n, "Ph2type: %s\n", getstring(s->p[1].type)); seprint(b+strlen(b), b+n, "PaeState: %s\n", pnames[s->pState]); seprint(b+strlen(b), b+n, "EapRestart: %d\n", s->eapRestart); seprint(b+strlen(b), b+n, "LogoffSent: %d\n", s->logoffSent); seprint(b+strlen(b), b+n, "UserLogoff: %d\n", s->userLogoff); seprint(b+strlen(b), b+n, "SPortMode: %s\n", sPortControl[s->portMode]); seprint(b+strlen(b), b+n, "BackState: %s\n", bnames[s->bState]); seprint(b+strlen(b), b+n, "EapNoResp: %d\n", s->eapNoResp); seprint(b+strlen(b), b+n, "EapReq: %d\n", s->eapReq); seprint(b+strlen(b), b+n, "EapResp: %d\n", s->eapResp); seprint(b+strlen(b), b+n, "EapNoResp: %d\n", s->eapNoResp); seprint(b+strlen(b), b+n, "EapFail: %d\n", s->e->eapFail); seprint(b+strlen(b), b+n, "EapSuccess: %d\n", s->e->eapSuccess); seprint(b+strlen(b), b+n, "Initialize: %d\n", s->initialize); seprint(b+strlen(b), b+n, "PortControl: %s\n", sPortControl[s->portControl]); seprint(b+strlen(b), b+n, "PortEnabled: %d\n", s->portEnabled); seprint(b+strlen(b), b+n, "PortStatus: %s\n", sAuthState[s->portStatus]); seprint(b+strlen(b), b+n, "SuppPortStatus: %s\n", sAuthState[s->suppPortStatus]); seprint(b+strlen(b), b+n, "PortValid: %d\n", s->portValid); seprint(b+strlen(b), b+n, "SuppSuccess: %d\n", s->suppSuccess); seprint(b+strlen(b), b+n, "SuppFail: %d\n", s->suppFail); seprint(b+strlen(b), b+n, "SuppTimeout: %d\n", s->suppTimeout); seprint(b+strlen(b), b+n, "KeyRun: %d\n", s->e->keyRun); seprint(b+strlen(b), b+n, "KeyDone: %d\n", s->e->keyDone); t = getPAETimes(s); for (i=0; i < NTime; i++) seprint(b+strlen(b), b+n, "%s:\t%s\n", timeNames[t[i].id], nsctime(t[i].time)); seprint(b+strlen(b), b+n, "StartCount:\t%d\n", s->startCount); seprint(b+strlen(b), b+n, "MaxStart:\t%d\n", s->maxStart); seprint(b+strlen(b), b+n, "HeldWhile:\t%d\n", timerVal(s->heldWhile)); seprint(b+strlen(b), b+n, "HeldPeriod:\t%d\n", s->heldPeriod); seprint(b+strlen(b), b+n, "StartWhen:\t%d\n", timerVal(s->startWhen)); seprint(b+strlen(b), b+n, "StartPeriod:\t%d\n", s->startPeriod); seprint(b+strlen(b), b+n, "AuthWhile:\t%d\n", timerVal(s->authWhile)); seprint(b+strlen(b), b+n, "AuthPeriod:\t%d\n", s->authPeriod); } long getChangetime(int qid) { PAEstate *s; s = &theState; switch(qid){ case Qstatus: if (s->paeTime > s->backTime) return nsec2sec(s->paeTime); else return nsec2sec(s->backTime); case Qkeys: return nsec2sec(s->e->keyTime); case Qnote: return nsec2sec(s->noteTime); default: return 0; } } // ========== main thing PAEstate* init8021x(Etherstate *e, Timers *t, Phasestate *p) { PAEstate *s; s = &theState; memset(s, 0, sizeof(PAEstate)); s->e = e; s->p = p; s->heldPeriod = 60; //seconds s->startPeriod = 30; //seconds s->authPeriod = 30; //seconds s->maxStart = 3; s->heldWhile = addTimer(t, "heldWhile"); s->startWhen = addTimer(t, "startWhen"); s->authWhile = addTimer(t, "authWhile"); s->pState = Disconnected; s->bState = Initialize; s->startTime = nsec(); s->paeTime = s->startTime; s->backTime = s->startTime; // s->noteTime = s->startTime; // s->restartTime = nsec(); s->portchan = chancreate(sizeof(int), 0); s->backstart = chancreate(sizeof(int), 0); s->backdone = chancreate(sizeof(int), 0); s->tickchan = t->chan; return s; } void start8021xprocs(PAEstate *s) { procrfork(backproc, s, STACK, RFNAMEG|RFNOTEG); recv(s->backdone, nil); s->portEnabled = 1; procrfork(paeproc, s, STACK, RFNAMEG|RFNOTEG); }