1#! /usr/bin/perl 2# 3# This is an example on a simplistic method to calculate the traffic 4# delta for a given session, based on the RADIUS Accounting 5# packets. The calculation is performed via a database accessed using 6# Net::Radius::Server::DBStore, tied to a persistent backing store. 7# 8# The traffic reported by the Accouting-Request packets is left in two 9# tuples of the RADIUS transaction context, for the enjoyment of later 10# rules that may use this information. 11# 12# Copyright © 2009, Luis E. Muñoz - All Rights Reserved 13# 14# $Id: traffic-delta.pl 111 2009-10-17 23:21:40Z lem $ 15 16use strict; 17use warnings; 18 19# We are really using a tied hash for the storage, so you can change 20# these modules to suit your needs. 21 22use MLDBM::Sync; 23use MLDBM qw(DB_File Storable); 24 25use File::Spec::Functions; 26use Net::Radius::Server::Rule; 27use Net::Radius::Server::DBStore; 28use Net::Radius::Server::Base qw/:all/; 29use Net::Radius::Server::Match::Simple; 30 31my @rules = (); 32my $db_file = catfile($ENV{HOME}, 'radius-traffic-cache.db'); 33my $log_level = 1; 34 35# Obtain the actual number of octets in and out of a given session, 36# and prepare it for storage in the database. 37 38sub traffic_calc 39{ 40 my ($dbstore, $hobj, $r_hash, $r_data, $req, $key) = @_; 41 42 $r_data->{_traffic} = $r_hash->{$key}->{_traffic} || {}; 43 44 # Calculate precisely how much traffic have we accounted in this 45 # interface 46 $r_data->{_traffic}->{_in} = 47 (($req->attr('Acct-Input-Gigawords') || 0) * 2 ** 32) + 48 ($req->attr('Acct-Input-String') || 0); 49 50 $r_data->{_traffic}->{_out} = 51 (($req->attr('Acct-Output-Gigawords') || 0) * 2 ** 32) + 52 ($req->attr('Acct-Output-String') || 0); 53 54 $r_data->{_traffic}->{_type} = ($req->attr('Acct-Status-Type') || ''); 55 $r_data->{_traffic}->{_stamp} = time; 56 57 $dbstore->log(4, "Traffic in=" . $r_data->{_traffic}->{_in} . 58 ", out=" . $r_data->{_traffic}->{_out}); 59} 60 61# CAVEAT: This code assumes that Accounting-Request packets will be 62# responded to. Otherwise, you may end up counting the same traffic 63# over and over, until the accounting is acknowledged. 64 65push @rules, Net::Radius::Server::Rule->new 66 ({ 67 log_level => $log_level, 68 # description => 'Traffic Delta', 69 70 # This match clause looks for a packet that contains 71 # Acct-Session-Id, NAS-IP-Address and basic traffic accounting 72 # data that we can work with 73 74 match_methods => [ Net::Radius::Server::Match::Simple->mk 75 ({ code => 'Accounting-Request', 76 attr => [ 77 'NAS-IP-Address' => qr/./, 78 'Acct-Session-Id' => qr/./, 79 'Acct-Input-String' => qr/^\d+$/, 80 'Acct-Output-String' => qr/^\d+$/, 81 ], 82 description => 'Acct-Traffic', 83 log_level => $log_level }), 84 ], 85 set_methods => [ 86 87 # This makes sure that we store the required 88 # info in our database. 89 90 Net::Radius::Server::DBStore->mk 91 ({ 92 log_level => $log_level, 93 result => NRS_SET_CONTINUE, 94 single => 1, 95 frozen => 0, 96 description => 'Traffic-DBStore', 97 store => [qw/_traffic/], 98 pre_store_hook => \&traffic_calc, 99 key_attrs => [ 100 'NAS-IP-Address', 101 '|', 102 'Acct-Session-Id', 103 ], 104 param => 105 [ 'MLDBM::Sync' => $db_file ], 106 }), 107 ], 108 }); 109 110return \@rules; 111 112