Plan 9 from Bell Labs’s /usr/web/sources/contrib/axel/8021x/v216/8021x.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


// 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 <u.h>
#include <libc.h>
#include <thread.h>
#include <ip.h>
#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);

}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.