4 * RADIUS protocol accounting logger module for GNU Gatekeeper.
6 * Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz
8 * This work is published under the GNU Public License (GPL)
9 * see file COPYING for details.
10 * We also explicitely grant the right to link this code
11 * with the OpenH323 library.
13 * $Log: radacct.cxx,v $
14 * Revision 1.20 2006/12/06 16:34:04 zvision
15 * Handle RADIUS Class attribute correctly
17 * Revision 1.19 2006/07/06 15:25:13 willamowius
18 * set all deleted pointers to NULL (most probably more than needed)
20 * Revision 1.18 2006/04/14 13:56:19 willamowius
21 * call failover code merged
23 * Revision 1.1.1.1 2005/11/21 20:19:58 willamowius
26 * Revision 1.4 2005/11/15 19:52:56 jan
27 * Michal v1 (works, but on in routed, not proxy mode)
29 * Revision 1.17 2005/04/24 16:39:44 zvision
30 * MSVC6.0 compatibility fixed
32 * Revision 1.16 2005/01/05 15:42:41 willamowius
33 * new accounting event 'connect', parameter substitution unified in parent class
35 * Revision 1.15 2005/01/04 16:47:12 willamowius
38 * Revision 1.14 2004/11/15 23:57:42 zvision
39 * Ability to choose between the original and the rewritten dialed number
41 * Revision 1.13 2004/11/10 18:30:41 zvision
42 * Ability to customize timestamp strings
44 * Revision 1.12 2004/07/26 12:19:41 zvision
45 * New faster Radius implementation, thanks to Pavel Pavlov for ideas!
47 * Revision 1.11.2.2 2004/07/07 23:11:07 zvision
48 * Faster and more elegant handling of Cisco VSA
50 * Revision 1.11.2.1 2004/07/07 20:50:14 zvision
51 * New, faster, Radius client implementation. Thanks to Pavel Pavlov for ideas!
53 * Revision 1.11 2004/06/25 13:33:18 zvision
54 * Better Username, Calling-Station-Id and Called-Station-Id handling.
55 * New SetupUnreg option in Gatekeeper::Auth section.
57 * Revision 1.10 2004/06/17 10:47:13 zvision
58 * New h323-ivr-out=h323-call-id accounting attribute
60 * Revision 1.9 2004/04/17 11:43:43 zvision
61 * Auth/acct API changes.
62 * Header file usage more consistent.
64 * Revision 1.8 2004/03/17 00:00:38 zvision
65 * Conditional compilation to allow to control RADIUS on Windows just by setting HA_RADIUS macro
67 * Revision 1.7 2003/10/31 00:01:24 zvision
68 * Improved accounting modules stacking control, optimized radacct/radauth a bit
70 * Revision 1.6 2003/10/15 10:16:57 zvision
71 * Fixed VC6 compiler warnings. Thanks to Hu Yuxin.
73 * Revision 1.5 2003/10/08 12:40:48 zvision
74 * Realtime accounting updates added
76 * Revision 1.4 2003/09/17 19:23:01 zvision
77 * Removed unnecessary setup-time double check.
78 * Added h323-connect-time to AcctUpdate packets.
80 * Revision 1.3 2003/09/14 21:10:34 zvision
81 * Changes due to accounting API redesign.
83 * Revision 1.2 2003/09/12 16:31:16 zvision
84 * Accounting initially added to the 2.2 branch
86 * Revision 1.1.2.5 2003/08/21 15:28:58 zvision
87 * Fixed double h323-setup-time sent in Acct-Stop
89 * Revision 1.1.2.4 2003/08/17 20:05:39 zvision
90 * Added h323-setup-time attribute to Acct-Start packets (Cisco compatibility).
92 * Revision 1.1.2.3 2003/07/31 22:58:48 zvision
93 * Added Framed-IP-Address attribute and improved h323-disconnect-cause handling
95 * Revision 1.1.2.2 2003/07/03 15:30:39 zvision
96 * Added cvs Log keyword
101 #if defined(_WIN32) && (_MSC_VER <= 1200)
102 #pragma warning(disable:4786) // warning about too long debug symbol off
103 #pragma warning(disable:4284)
108 #include "gk_const.h"
109 #include "h323util.h"
113 #include "radproto.h"
120 const char* moduleName,
121 const char* cfgSecName
124 GkAcctLogger(moduleName, cfgSecName),
125 m_nasIdentifier(Toolkit::Instance()->GKName()),
126 m_radiusClient(NULL),
127 m_attrH323CallOrigin(RadiusAttr::CiscoVSA_h323_call_origin, false,
129 m_attrH323CallType(RadiusAttr::CiscoVSA_h323_call_type, false,
132 // it is very important to set what type of accounting events
133 // are supported for each accounting module, otherwise Log method
134 // will no get called
135 SetSupportedEvents(RadAcctEvents);
137 PConfig* cfg = GetConfig();
138 const PString& cfgSec = GetConfigSectionName();
140 m_radiusClient = new RadiusClient(*cfg, cfgSec);
142 m_nasIpAddress = m_radiusClient->GetLocalAddress();
143 if (m_nasIpAddress == INADDR_ANY) {
144 vector<PIPSocket::Address> interfaces;
145 Toolkit::Instance()->GetGKHome(interfaces);
146 if (!interfaces.empty())
147 m_nasIpAddress = interfaces.front();
149 PTRACE(1, "RADACCT\t" << GetName() << " cannot determine "
154 m_appendCiscoAttributes = Toolkit::AsBool(cfg->GetString(
155 cfgSec, "AppendCiscoAttributes", "1"
157 m_fixedUsername = cfg->GetString(cfgSec, "FixedUsername", "");
159 m_timestampFormat = cfg->GetString(cfgSec, "TimestampFormat", "");
161 m_useDialedNumber = Toolkit::AsBool(cfg->GetString(
162 cfgSec, "UseDialedNumber", "0"
165 m_attrNasIdentifier = RadiusAttr(RadiusAttr::NasIdentifier, m_nasIdentifier);
166 m_attrH323GwId = RadiusAttr(RadiusAttr::CiscoVSA_h323_gw_id, false, m_nasIdentifier);
171 delete m_radiusClient;
174 GkAcctLogger::Status RadAcct::Log(
175 GkAcctLogger::AcctEvent evt,
179 // a workaround to prevent processing end on "sufficient" module
180 // if it is not interested in this event type
181 if ((evt & GetEnabledEvents() & GetSupportedEvents()) == 0)
184 if (m_radiusClient == NULL) {
185 PTRACE(1,"RADACCT\t"<<GetName()<<" - null RADIUS client instance");
189 if ((evt & (AcctStart | AcctStop | AcctUpdate)) && (!call)) {
190 PTRACE(1,"RADACCT\t"<<GetName()<<" - missing call info for event "<<evt);
194 // build RADIUS Accounting-Request
195 RadiusPDU* const pdu = new RadiusPDU(RadiusPDU::AccountingRequest);
197 pdu->AppendAttr(RadiusAttr::AcctStatusType,
198 (evt & AcctStart) ? RadiusAttr::AcctStatus_Start
199 : ((evt & AcctStop) ? RadiusAttr::AcctStatus_Stop
200 : ((evt & AcctUpdate) ? RadiusAttr::AcctStatus_InterimUpdate
201 : ((evt & AcctOn) ? RadiusAttr::AcctStatus_AccountingOn
202 : ((evt & AcctOff) ? RadiusAttr::AcctStatus_AccountingOff : 0)
205 PIPSocket::Address addr;
208 // Gk works as NAS point, so append NAS IP
209 pdu->AppendAttr(RadiusAttr::NasIpAddress, m_nasIpAddress);
210 pdu->AppendAttr(m_attrNasIdentifier);
211 pdu->AppendAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual);
213 if (evt & (AcctStart | AcctStop | AcctUpdate)) {
214 pdu->AppendAttr(RadiusAttr::ServiceType, RadiusAttr::ST_Login);
215 pdu->AppendAttr(RadiusAttr::AcctSessionId, call->GetAcctSessionId());
217 PBYTEArray classAttr(call->GetRADIUSClass());
218 if (classAttr.GetSize() > 0)
219 pdu->AppendAttr(RadiusAttr::AttrTypeClass, (const BYTE*)classAttr, classAttr.GetSize());
221 endptr callingEP = call->GetCallingParty();
222 PIPSocket::Address callerIP(0);
225 call->GetSrcSignalAddr(callerIP, callerPort);
227 const PString username = GetUsername(call);
228 if (username.IsEmpty() && m_fixedUsername.IsEmpty())
229 PTRACE(3,"RADACCT\t"<<GetName()<<" could not determine User-Name"
230 <<" for the call no. "<<call->GetCallNumber()
233 pdu->AppendAttr(RadiusAttr::UserName,
234 m_fixedUsername.IsEmpty() ? username : m_fixedUsername
237 if (callerIP.IsValid())
238 pdu->AppendAttr(RadiusAttr::FramedIpAddress, callerIP);
240 if ((evt & AcctStart) == 0)
241 pdu->AppendAttr(RadiusAttr::AcctSessionTime, call->GetDuration());
243 PString stationId = GetCallingStationId(call);
245 pdu->AppendAttr(RadiusAttr::CallingStationId, stationId);
247 PTRACE(3,"RADACCT\t"<<GetName()<<" could not determine"
248 <<" Calling-Station-Id for the call "<<call->GetCallNumber()
251 stationId = m_useDialedNumber ? GetDialedNumber(call) : GetCalledStationId(call);
253 pdu->AppendAttr(RadiusAttr::CalledStationId, stationId);
255 PTRACE(3,"RADACCT\t"<<GetName()<<" could not determine"
256 <<" Called-Station-Id for the call no. "<<call->GetCallNumber()
259 if (m_appendCiscoAttributes) {
260 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_conf_id,
261 GetGUIDString(call->GetConferenceIdentifier())
264 pdu->AppendAttr(m_attrH323GwId);
265 pdu->AppendAttr(m_attrH323CallOrigin);
266 pdu->AppendAttr(m_attrH323CallType);
268 Toolkit* const toolkit = Toolkit::Instance();
270 time_t tm = call->GetSetupTime();
272 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_setup_time,
273 toolkit->AsString(PTime(tm), m_timestampFormat)
276 if (evt & (AcctStop | AcctUpdate)) {
277 tm = call->GetConnectTime();
279 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_connect_time,
280 toolkit->AsString(PTime(tm), m_timestampFormat)
284 if (evt & AcctStop) {
285 tm = call->GetDisconnectTime();
287 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_disconnect_time,
288 toolkit->AsString(PTime(tm), m_timestampFormat)
291 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_disconnect_cause,
292 PString(PString::Unsigned, (long)(call->GetDisconnectCause()), 16)
296 if (call->GetDestSignalAddr(addr,port))
297 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_remote_address,
301 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair,
302 PString("h323-ivr-out=h323-call-id:")
303 + GetGUIDString(call->GetCallIdentifier().m_guid),
308 pdu->AppendAttr(RadiusAttr::AcctDelayTime, 0);
311 // send request and wait for response
312 RadiusPDU* response = NULL;
313 bool result = OnSendPDU(*pdu, evt, call);
315 // accounting updates must be fast, so we are just sending
316 // the request to the server and are not waiting for a response
318 if (evt & AcctUpdate)
319 result = m_radiusClient->SendRequest(*pdu);
321 result = m_radiusClient->MakeRequest(*pdu, response) && (response != NULL);
331 // check if Access-Request has been accepted
332 result = (response->GetCode() == RadiusPDU::AccountingResponse);
334 result = OnReceivedPDU(*response, evt, call);
336 PTRACE(4, "RADACCT\t" << GetName() << " - received response is not "
337 " an AccountingResponse, event " << evt << ", call no. "
338 << (call ? call->GetCallNumber() : 0)
342 return result ? Ok : Fail;
345 bool RadAcct::OnSendPDU(
347 GkAcctLogger::AcctEvent /*evt*/,
348 const callptr& /*call*/
354 bool RadAcct::OnReceivedPDU(
356 GkAcctLogger::AcctEvent /*evt*/,
357 const callptr& /*call*/
364 // append RADIUS based accounting logger to the global list of loggers
365 GkAcctLoggerCreator<RadAcct> RadAcctCreator("RadAcct");
368 #endif /* HAS_RADIUS */