Support radius accounting, and add a packet count to throughput

statistics as a side effect.

Submitted by: Marcin Cieslak <saper@system.pl>

with some tweaks to RAD_ACCT_SESSION_ID and
RAD_ACCT_MULTI_SESSION_ID generation by me.
This commit is contained in:
Brian Somers 2000-08-28 22:44:54 +00:00
parent 8750ed5f77
commit 794c9bbc80
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=65178
7 changed files with 206 additions and 5 deletions

View File

@ -902,6 +902,7 @@ bundle_Destroy(struct bundle *bundle)
#ifndef NORADIUS
/* Tell the radius server the bad news */
log_Printf(LogDEBUG, "Radius: Destroy called from bundle_Destroy\n");
radius_Destroy(&bundle->radius);
#endif

View File

@ -136,6 +136,7 @@ struct bundle {
#ifndef NORADIUS
struct radius radius; /* Info retrieved from radius server */
struct radacct radacct;
#endif
};

View File

@ -957,6 +957,12 @@ IpcpLayerDown(struct fsm *fp)
s = "Interface configuration error !";
log_Printf(LogIPCP, "%s: LayerDown: %s\n", fp->link->name, s);
#ifndef NORADIUS
radius_Account(&fp->bundle->radius, &fp->bundle->radacct,
fp->bundle->links, RAD_STOP, &ipcp->peer_ip, &ipcp->ifmask,
&ipcp->throughput);
#endif
/*
* XXX this stuff should really live in the FSM. Our config should
* associate executable sections in files with events.
@ -1009,6 +1015,11 @@ IpcpLayerUp(struct fsm *fp)
if (!ipcp_InterfaceUp(ipcp))
return 0;
#ifndef NORADIUS
radius_Account(&fp->bundle->radius, &fp->bundle->radacct, fp->bundle->links,
RAD_START, &ipcp->peer_ip, &ipcp->ifmask, &ipcp->throughput);
#endif
/*
* XXX this stuff should really live in the FSM. Our config should
* associate executable sections in files with events.

View File

@ -117,6 +117,12 @@ radius_Process(struct radius *r, int got)
rad_close(r->cx.rad);
return;
case RAD_ACCOUNTING_RESPONSE:
log_Printf(LogPHASE, "Radius: Accounting response received\n");
/* No further processing for accounting requests, please */
rad_close(r->cx.rad);
return;
case -1:
log_Printf(LogPHASE, "radius: %s\n", rad_strerror(r->cx.rad));
auth_Failure(r->cx.auth);
@ -325,6 +331,7 @@ radius_Init(struct radius *r)
r->desc.Read = radius_Read;
r->desc.Write = radius_Write;
memset(&r->cx.timer, '\0', sizeof r->cx.timer);
log_Printf(LogDEBUG, "Radius: radius_Init\n");
}
/*
@ -334,6 +341,7 @@ void
radius_Destroy(struct radius *r)
{
r->valid = 0;
log_Printf(LogDEBUG, "Radius: radius_Destroy\n");
timer_Stop(&r->cx.timer);
route_DeleteAll(&r->routes);
if (r->cx.fd != -1) {
@ -368,8 +376,8 @@ radius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
radius_Destroy(r);
if ((r->cx.rad = rad_open()) == NULL) {
log_Printf(LogERROR, "rad_open: %s\n", strerror(errno));
if ((r->cx.rad = rad_auth_open()) == NULL) {
log_Printf(LogERROR, "rad_auth_open: %s\n", strerror(errno));
return;
}
@ -461,6 +469,159 @@ radius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
}
}
/*
* Send an accounting request to the RADIUS server
*/
void
radius_Account(struct radius *r, struct radacct *ac, struct datalink *dl,
int acct_type, struct in_addr *peer_ip, struct in_addr *netmask,
struct pppThroughput *stats)
{
struct ttyent *ttyp;
struct timeval tv;
int got, slot;
char hostname[MAXHOSTNAMELEN];
struct hostent *hp;
struct in_addr hostaddr;
if (!*r->cfg.file)
return;
if (r->cx.fd != -1)
/*
* We assume that our name/key/challenge is the same as last time,
* and just continue to wait for the RADIUS server(s).
*/
return;
radius_Destroy(r);
if ((r->cx.rad = rad_auth_open()) == NULL) {
log_Printf(LogERROR, "rad_auth_open: %s\n", strerror(errno));
return;
}
if (rad_config(r->cx.rad, r->cfg.file) != 0) {
log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
rad_close(r->cx.rad);
return;
}
if (rad_create_request(r->cx.rad, RAD_ACCOUNTING_REQUEST) != 0) {
log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
rad_close(r->cx.rad);
return;
}
/* Grab some accounting data and initialize structure */
if (acct_type == RAD_START) {
ac->rad_parent = r;
/* Fetch username from datalink */
strncpy(ac->user_name, dl->peer.authname, sizeof ac->user_name);
ac->user_name[AUTHLEN-1] = '\0';
ac->authentic = 2; /* Assume RADIUS verified auth data */
/* Generate a session ID */
snprintf(ac->session_id, sizeof ac->session_id, "%s%d-%s%lu",
dl->bundle->cfg.auth.name, (int)getpid(),
dl->peer.authname, (unsigned long)stats->uptime);
/* And grab our MP socket name */
snprintf(ac->multi_session_id, sizeof ac->multi_session_id, "%s",
dl->bundle->ncp.mp.active ?
dl->bundle->ncp.mp.server.socket.sun_path : "");
/* Fetch IP, netmask from IPCP */
memcpy(&ac->ip, peer_ip, sizeof(ac->ip));
memcpy(&ac->mask, netmask, sizeof(ac->mask));
};
if (rad_put_string(r->cx.rad, RAD_USER_NAME, ac->user_name) != 0 ||
rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0 ||
rad_put_addr(r->cx.rad, RAD_FRAMED_IP_ADDRESS, ac->ip) != 0 ||
rad_put_addr(r->cx.rad, RAD_FRAMED_IP_NETMASK, ac->mask) != 0) {
log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
rad_close(r->cx.rad);
return;
}
if (gethostname(hostname, sizeof hostname) != 0)
log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno));
else {
if ((hp = gethostbyname(hostname)) != NULL) {
hostaddr.s_addr = *(u_long *)hp->h_addr;
if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) {
log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
rad_strerror(r->cx.rad));
rad_close(r->cx.rad);
return;
}
}
if (rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 0) {
log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
rad_strerror(r->cx.rad));
rad_close(r->cx.rad);
return;
}
}
if (dl->physical->handler &&
dl->physical->handler->type == TTY_DEVICE) {
setttyent();
for (slot = 1; (ttyp = getttyent()); ++slot)
if (!strcmp(ttyp->ty_name, dl->physical->name.base)) {
if(rad_put_int(r->cx.rad, RAD_NAS_PORT, slot) != 0) {
log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
rad_strerror(r->cx.rad));
rad_close(r->cx.rad);
endttyent();
return;
}
break;
}
endttyent();
}
if (rad_put_int(r->cx.rad, RAD_ACCT_STATUS_TYPE, acct_type) != 0 ||
rad_put_string(r->cx.rad, RAD_ACCT_SESSION_ID, ac->session_id) != 0 ||
rad_put_string(r->cx.rad, RAD_ACCT_MULTI_SESSION_ID,
ac->multi_session_id) != 0 ||
rad_put_int(r->cx.rad, RAD_ACCT_DELAY_TIME, 0) != 0) {
/* XXX ACCT_DELAY_TIME should be increased each time a packet is waiting */
log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
rad_close(r->cx.rad);
return;
}
if (acct_type == RAD_STOP)
/* Show some statistics */
if (rad_put_int(r->cx.rad, RAD_ACCT_INPUT_OCTETS, stats->OctetsIn) != 0 ||
rad_put_int(r->cx.rad, RAD_ACCT_INPUT_PACKETS, stats->PacketsIn) != 0 ||
rad_put_int(r->cx.rad, RAD_ACCT_OUTPUT_OCTETS, stats->OctetsOut) != 0 ||
rad_put_int(r->cx.rad, RAD_ACCT_OUTPUT_PACKETS, stats->PacketsOut)
!= 0 ||
rad_put_int(r->cx.rad, RAD_ACCT_SESSION_TIME, throughput_uptime(stats))
!= 0) {
log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
rad_close(r->cx.rad);
return;
}
if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
radius_Process(r, got);
else {
log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout);
r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
r->cx.timer.func = radius_Timeout;
r->cx.timer.name = "radius";
r->cx.timer.arg = r;
r->cx.auth = NULL; /* Not valid for accounting requests */
timer_Start(&r->cx.timer);
}
}
/*
* How do things look at the moment ?
*/

View File

@ -45,6 +45,16 @@ struct radius {
} cfg;
};
struct radacct {
struct radius *rad_parent; /* "Parent" struct radius stored in bundle */
char user_name[AUTHLEN]; /* Session User-Name */
char session_id[256]; /* Unique session ID */
char multi_session_id[51]; /* Unique MP session ID */
int authentic; /* How the session has been authenticated */
struct in_addr ip;
struct in_addr mask;
};
#define descriptor2radius(d) \
((d)->type == RADIUS_DESCRIPTOR ? (struct radius *)(d) : NULL)
@ -56,3 +66,12 @@ extern void radius_Destroy(struct radius *);
extern void radius_Show(struct radius *, struct prompt *);
extern void radius_Authenticate(struct radius *, struct authinfo *,
const char *, const char *, const char *);
extern void radius_Account(struct radius *, struct radacct *,
struct datalink *, int, struct in_addr *,
struct in_addr *, struct pppThroughput *);
/* An (int) parameter to radius_Account, from radlib.h */
#if !defined(RAD_START)
#define RAD_START 1
#define RAD_STOP 2
#endif

View File

@ -44,7 +44,7 @@
void
throughput_init(struct pppThroughput *t, int period)
{
t->OctetsIn = t->OctetsOut = 0;
t->OctetsIn = t->OctetsOut = t->PacketsIn = t->PacketsOut = 0;
t->SamplePeriod = period;
t->in.SampleOctets = (long long *)
calloc(period, sizeof *t->in.SampleOctets);
@ -111,6 +111,8 @@ throughput_disp(struct pppThroughput *t, struct prompt *prompt)
divisor = secs_up ? secs_up : 1;
prompt_Printf(prompt, "%llu octets in, %llu octets out\n",
t->OctetsIn, t->OctetsOut);
prompt_Printf(prompt, "%llu packets in, %llu packets out\n",
t->PacketsIn, t->PacketsOut);
if (t->rolling) {
prompt_Printf(prompt, " overall %6qu bytes/sec\n",
(t->OctetsIn + t->OctetsOut) / divisor);
@ -137,8 +139,10 @@ throughput_log(struct pppThroughput *t, int level, const char *title)
if (title == NULL)
title = "";
log_Printf(level, "%s%sConnect time: %d secs: %llu octets in, %llu octets"
" out\n", title, *title ? ": " : "", secs_up, t->OctetsIn,
t->OctetsOut);
" out\n", title, *title ? ": " : "", secs_up, t->OctetsIn,
t->OctetsOut);
log_Printf(level, "%s%s: %llu packets in, %llu packets out\n",
title, *title ? ": " : "", t->PacketsIn, t->PacketsOut);
if (secs_up == 0)
secs_up = 1;
if (t->rolling)
@ -235,12 +239,14 @@ void
throughput_addin(struct pppThroughput *t, long long n)
{
t->OctetsIn += n;
t->PacketsIn++;
}
void
throughput_addout(struct pppThroughput *t, long long n)
{
t->OctetsOut += n;
t->PacketsOut++;
}
void

View File

@ -37,6 +37,8 @@ struct pppThroughput {
time_t uptime, downtime;
unsigned long long OctetsIn;
unsigned long long OctetsOut;
unsigned long long PacketsIn;
unsigned long long PacketsOut;
int SamplePeriod;
struct {
unsigned long long *SampleOctets;