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:
parent
8750ed5f77
commit
794c9bbc80
@ -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
|
||||
|
||||
|
@ -136,6 +136,7 @@ struct bundle {
|
||||
|
||||
#ifndef NORADIUS
|
||||
struct radius radius; /* Info retrieved from radius server */
|
||||
struct radacct radacct;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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 ?
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user