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
|
#ifndef NORADIUS
|
||||||
/* Tell the radius server the bad news */
|
/* Tell the radius server the bad news */
|
||||||
|
log_Printf(LogDEBUG, "Radius: Destroy called from bundle_Destroy\n");
|
||||||
radius_Destroy(&bundle->radius);
|
radius_Destroy(&bundle->radius);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -136,6 +136,7 @@ struct bundle {
|
|||||||
|
|
||||||
#ifndef NORADIUS
|
#ifndef NORADIUS
|
||||||
struct radius radius; /* Info retrieved from radius server */
|
struct radius radius; /* Info retrieved from radius server */
|
||||||
|
struct radacct radacct;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -957,6 +957,12 @@ IpcpLayerDown(struct fsm *fp)
|
|||||||
s = "Interface configuration error !";
|
s = "Interface configuration error !";
|
||||||
log_Printf(LogIPCP, "%s: LayerDown: %s\n", fp->link->name, s);
|
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
|
* XXX this stuff should really live in the FSM. Our config should
|
||||||
* associate executable sections in files with events.
|
* associate executable sections in files with events.
|
||||||
@ -1009,6 +1015,11 @@ IpcpLayerUp(struct fsm *fp)
|
|||||||
if (!ipcp_InterfaceUp(ipcp))
|
if (!ipcp_InterfaceUp(ipcp))
|
||||||
return 0;
|
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
|
* XXX this stuff should really live in the FSM. Our config should
|
||||||
* associate executable sections in files with events.
|
* associate executable sections in files with events.
|
||||||
|
@ -117,6 +117,12 @@ radius_Process(struct radius *r, int got)
|
|||||||
rad_close(r->cx.rad);
|
rad_close(r->cx.rad);
|
||||||
return;
|
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:
|
case -1:
|
||||||
log_Printf(LogPHASE, "radius: %s\n", rad_strerror(r->cx.rad));
|
log_Printf(LogPHASE, "radius: %s\n", rad_strerror(r->cx.rad));
|
||||||
auth_Failure(r->cx.auth);
|
auth_Failure(r->cx.auth);
|
||||||
@ -325,6 +331,7 @@ radius_Init(struct radius *r)
|
|||||||
r->desc.Read = radius_Read;
|
r->desc.Read = radius_Read;
|
||||||
r->desc.Write = radius_Write;
|
r->desc.Write = radius_Write;
|
||||||
memset(&r->cx.timer, '\0', sizeof r->cx.timer);
|
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)
|
radius_Destroy(struct radius *r)
|
||||||
{
|
{
|
||||||
r->valid = 0;
|
r->valid = 0;
|
||||||
|
log_Printf(LogDEBUG, "Radius: radius_Destroy\n");
|
||||||
timer_Stop(&r->cx.timer);
|
timer_Stop(&r->cx.timer);
|
||||||
route_DeleteAll(&r->routes);
|
route_DeleteAll(&r->routes);
|
||||||
if (r->cx.fd != -1) {
|
if (r->cx.fd != -1) {
|
||||||
@ -368,8 +376,8 @@ radius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
|
|||||||
|
|
||||||
radius_Destroy(r);
|
radius_Destroy(r);
|
||||||
|
|
||||||
if ((r->cx.rad = rad_open()) == NULL) {
|
if ((r->cx.rad = rad_auth_open()) == NULL) {
|
||||||
log_Printf(LogERROR, "rad_open: %s\n", strerror(errno));
|
log_Printf(LogERROR, "rad_auth_open: %s\n", strerror(errno));
|
||||||
return;
|
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 ?
|
* How do things look at the moment ?
|
||||||
*/
|
*/
|
||||||
|
@ -45,6 +45,16 @@ struct radius {
|
|||||||
} cfg;
|
} 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) \
|
#define descriptor2radius(d) \
|
||||||
((d)->type == RADIUS_DESCRIPTOR ? (struct radius *)(d) : NULL)
|
((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_Show(struct radius *, struct prompt *);
|
||||||
extern void radius_Authenticate(struct radius *, struct authinfo *,
|
extern void radius_Authenticate(struct radius *, struct authinfo *,
|
||||||
const char *, const char *, const char *);
|
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
|
void
|
||||||
throughput_init(struct pppThroughput *t, int period)
|
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->SamplePeriod = period;
|
||||||
t->in.SampleOctets = (long long *)
|
t->in.SampleOctets = (long long *)
|
||||||
calloc(period, sizeof *t->in.SampleOctets);
|
calloc(period, sizeof *t->in.SampleOctets);
|
||||||
@ -111,6 +111,8 @@ throughput_disp(struct pppThroughput *t, struct prompt *prompt)
|
|||||||
divisor = secs_up ? secs_up : 1;
|
divisor = secs_up ? secs_up : 1;
|
||||||
prompt_Printf(prompt, "%llu octets in, %llu octets out\n",
|
prompt_Printf(prompt, "%llu octets in, %llu octets out\n",
|
||||||
t->OctetsIn, t->OctetsOut);
|
t->OctetsIn, t->OctetsOut);
|
||||||
|
prompt_Printf(prompt, "%llu packets in, %llu packets out\n",
|
||||||
|
t->PacketsIn, t->PacketsOut);
|
||||||
if (t->rolling) {
|
if (t->rolling) {
|
||||||
prompt_Printf(prompt, " overall %6qu bytes/sec\n",
|
prompt_Printf(prompt, " overall %6qu bytes/sec\n",
|
||||||
(t->OctetsIn + t->OctetsOut) / divisor);
|
(t->OctetsIn + t->OctetsOut) / divisor);
|
||||||
@ -137,8 +139,10 @@ throughput_log(struct pppThroughput *t, int level, const char *title)
|
|||||||
if (title == NULL)
|
if (title == NULL)
|
||||||
title = "";
|
title = "";
|
||||||
log_Printf(level, "%s%sConnect time: %d secs: %llu octets in, %llu octets"
|
log_Printf(level, "%s%sConnect time: %d secs: %llu octets in, %llu octets"
|
||||||
" out\n", title, *title ? ": " : "", secs_up, t->OctetsIn,
|
" out\n", title, *title ? ": " : "", secs_up, t->OctetsIn,
|
||||||
t->OctetsOut);
|
t->OctetsOut);
|
||||||
|
log_Printf(level, "%s%s: %llu packets in, %llu packets out\n",
|
||||||
|
title, *title ? ": " : "", t->PacketsIn, t->PacketsOut);
|
||||||
if (secs_up == 0)
|
if (secs_up == 0)
|
||||||
secs_up = 1;
|
secs_up = 1;
|
||||||
if (t->rolling)
|
if (t->rolling)
|
||||||
@ -235,12 +239,14 @@ void
|
|||||||
throughput_addin(struct pppThroughput *t, long long n)
|
throughput_addin(struct pppThroughput *t, long long n)
|
||||||
{
|
{
|
||||||
t->OctetsIn += n;
|
t->OctetsIn += n;
|
||||||
|
t->PacketsIn++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
throughput_addout(struct pppThroughput *t, long long n)
|
throughput_addout(struct pppThroughput *t, long long n)
|
||||||
{
|
{
|
||||||
t->OctetsOut += n;
|
t->OctetsOut += n;
|
||||||
|
t->PacketsOut++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -37,6 +37,8 @@ struct pppThroughput {
|
|||||||
time_t uptime, downtime;
|
time_t uptime, downtime;
|
||||||
unsigned long long OctetsIn;
|
unsigned long long OctetsIn;
|
||||||
unsigned long long OctetsOut;
|
unsigned long long OctetsOut;
|
||||||
|
unsigned long long PacketsIn;
|
||||||
|
unsigned long long PacketsOut;
|
||||||
int SamplePeriod;
|
int SamplePeriod;
|
||||||
struct {
|
struct {
|
||||||
unsigned long long *SampleOctets;
|
unsigned long long *SampleOctets;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user