Radius/simfony.pl
2025-04-24 07:47:49 +05:30

769 lines
37 KiB
Perl

use warnings;
use DBI;
use REST::Client;
use JSON;
use threads;
use Sys::Hostname;
use Switch;
use Data::Dumper;
use Math::BigInt;
use feature qw(switch);
use Smart::Match;
use constant false => 0;
use constant true => 1;
use Data::Dumper;
use Sys::Hostname;
use Data::Validate::IP qw(is_ipv4 is_ipv6);
use Time::HiRes qw(gettimeofday);
use constant RLM_MODULE_REJECT => 0; # /* immediately reject the request */
use constant RLM_MODULE_FAIL => 1; # /* module failed, don't reply */
use constant RLM_MODULE_OK => 2; # /* the module is OK, continue */
use constant RLM_MODULE_HANDLED => 3; # /* the module handled the request, so stop. */
use constant RLM_MODULE_INVALID => 4; # /* the module considers the request invalid. */
use constant RLM_MODULE_USERLOCK => 5; # /* reject the request (user is locked out) */
use constant RLM_MODULE_NOTFOUND => 6; # /* user not found */
use constant RLM_MODULE_NOOP => 7; # /* module succeeded without doing anything */
use constant RLM_MODULE_UPDATED => 8; # /* OK (pairs modified) */
use constant RLM_MODULE_NUMCODES => 9; # /* How many return codes there are */
use constant RAD_LOG_DEBUG => 0;
use constant RAD_LOG_AUTH => 1;
use constant RAD_LOG_PROXY => 2;
use constant RAD_LOG_INFO => 3;
use constant RAD_LOG_ERROR => 4;
use vars qw(%RAD_REQUEST %RAD_REPLY %RAD_CHECK
$databaseConnection
$subscriberProfileSelector $subscriberProfileSelectorMultiIMSI $subscriberQuery $subscriberMultiIMSIQuery $updatedHlrPsInfoQuery $hlrZoneLookupQuery $hlrZoneLookupSgsnQuery $hlrRoamingSteeringQuery
$hlrZoneLookupProfileQuery $hlrZoneLookupProfileQueryV2 $updatedHlrPsInfoCustomZoneQuery $checkIpAvailability $temporaryMultiImsiInsert $updateSessionId $restClient $technical_imsi $multi_imsi $test_data $donor $country $operatorName
$conn_valid %conf);
sub read_conf {
$conf{'db_name'} = "mobility";
$conf{'db_user'} = "root";
$conf{'db_host'} = "10.5.48.28";
$conf{'db_port'} = "3307";
$conf{'db_passwd'} = "xM9A4!exJbQNhqJ";
$conf{'hook_host'} = "http://notification-v1.simfony:5200/api/notification/";
}
sub conn_db {
&read_conf;
$databaseConnection->disconnect() if defined $databaseConnection;
$databaseConnection = DBI->connect("DBI:mysql:database=$conf{'db_name'};host=$conf{'db_host'};port=$conf{'db_port'}", $conf{'db_user'}, $conf{'db_passwd'}) or die ({ &radiusd::radlog(RAD_LOG_ERROR, "key: AAA-MobDB, status: 0, type: SEVERE, message: AAA->MobDB service is down: $conf{'db_host'}:$conf{'db_port'} $conf{'db_name'}")});
#$databaseConnection = DBI->connect("DBI:mysql:database=$conf{'db_name'};host=$conf{'db_host'}", $conf{'db_user'}, $conf{'db_passwd'});
if ($DBI::err) {&radiusd::radlog(RAD_LOG_ERROR, "DB Connect Error. $DBI::errstr");}
else {
$subscriberQuery = $databaseConnection->prepare("SELECT s.subscriber_id, s.service_allowed, aaa.AccessPointName, aaa.FramedIPAddress, aaa.FramedIPNetmask, aaa.PrimaryDNS, aaa.SecondaryDNS, ent.ENTITLEMENT_VALUE AS SVC_DATA_BLOCKED, saaaprof.Username, saaaprof.`Password`, s.ICCID, ent2.ENTITLEMENT_VALUE AS SVC_DATA_AUTHENTICATE_CREDENTIALS, hps.OPERATOR_NAME, hps.OPERATOR_COUNTRY, s.core_account_id, s.core_seller_id, s.ccs_profile_id, ccsprofile.type, d.code FROM subscriber s
LEFT JOIN aaa_addresspool aaa ON s.subscriber_id = aaa.subscriber_id
LEFT JOIN hlr_ps_roaming_info hps ON hps.IMSI = s.IMSI
LEFT JOIN subscriber_entitlements ent ON s.subscriber_id = ent.subscriber_id AND ent.ENTITLEMENT_NAME = 'SVC_DATA_BLOCKED'
LEFT JOIN subscriber_entitlements ent2 ON s.subscriber_id = ent2.subscriber_id AND ent2.ENTITLEMENT_NAME = 'SVC_DATA_AUTHENTICATE_CREDENTIALS'
LEFT JOIN subscriber_aaa_profile saaaprof ON s.subscriber_id = saaaprof.subscriber_id
LEFT JOIN ccs_profile ccsprofile ON s.ccs_profile_id = ccsprofile.id
LEFT JOIN donor d on s.donor_id = d.id
WHERE s.IMSI LIKE ?");
$subscriberMultiIMSIQuery = $databaseConnection->prepare("SELECT s.subscriber_id, s.service_allowed, aaa.AccessPointName, aaa.FramedIPAddress, aaa.FramedIPNetmask, aaa.PrimaryDNS, aaa.SecondaryDNS, ent.ENTITLEMENT_VALUE AS SVC_DATA_BLOCKED, saaaprof.Username, saaaprof.`Password`, s.ICCID, ent2.ENTITLEMENT_VALUE AS SVC_DATA_AUTHENTICATE_CREDENTIALS, hps.OPERATOR_NAME, hps.OPERATOR_COUNTRY, s.core_account_id, s.core_seller_id, s.ccs_profile_id, ccsprofile.type, d.code FROM mobility.subscriber s
LEFT JOIN subscriber_has_multi_imsi shmi on s.SUBSCRIBER_ID = shmi.subscriber_id
LEFT JOIN aaa_addresspool aaa ON s.subscriber_id = aaa.subscriber_id
LEFT JOIN hlr_ps_roaming_info hps ON hps.IMSI = s.IMSI
LEFT JOIN subscriber_entitlements ent ON s.subscriber_id = ent.subscriber_id AND ent.ENTITLEMENT_NAME = 'SVC_DATA_BLOCKED'
LEFT JOIN subscriber_entitlements ent2 ON s.subscriber_id = ent2.subscriber_id AND ent2.ENTITLEMENT_NAME = 'SVC_DATA_AUTHENTICATE_CREDENTIALS'
LEFT JOIN subscriber_aaa_profile saaaprof ON s.subscriber_id = saaaprof.subscriber_id
LEFT JOIN ccs_profile ccsprofile ON s.ccs_profile_id = ccsprofile.id
LEFT JOIN donor d on s.donor_id = d.id
WHERE shmi.IMSI LIKE ?");
$hlrZoneLookupSgsnQuery = $databaseConnection->prepare("SELECT * FROM hlr_zone_lookup_by_sgsn
WHERE inet_aton(?) between inet_aton(SUBNET_START) and inet_aton(SUBNET_END) limit 1;");
$hlrZoneLookupProfileQuery = $databaseConnection->prepare("SELECT hzl.TADIG, hzl.OPERATOR_COUNTRY, hzl.OPERATOR_NAME, IF(po.allow, 1, 0), IF(po.ZONE IS NULL,hzl.zone,po.ZONE), po.id FROM profile_operators po
LEFT JOIN hlr_zone_lookup hzl ON hzl.ID = po.hlr_zone_lookup_id WHERE CONCAT(hzl.mcc, hzl.mnc) = ?
AND po.profile_id = (select s.PROFILE_ID from subscriber s where imsi = ?) limit 1;");
$hlrZoneLookupQuery = $databaseConnection->prepare("select OPERATOR_COUNTRY, OPERATOR_NAME,ROAMING_ENABLED from hlr_zone_lookup where concat(mcc,mnc) like ? limit 1;");
$hlrRoamingSteeringQuery = $databaseConnection->prepare("SELECT hzl.OPERATOR_COUNTRY, hzl.OPERATOR_NAME, hzl.ROAMING_ENABLED FROM hlr_roaming_steering hrs
left join hlr_zone_lookup hzl on hzl.GTPREFIX like concat(hrs.GTPREFIX,'%')
where hrs.IMSI = ? and concat(hzl.mcc,hzl.mnc) like ? limit 1;");
$updatedHlrPsInfoQuery = $databaseConnection->prepare("UPDATE hlr_ps_roaming_info AS ps_profile,
(SELECT TADIG, OPERATOR_COUNTRY, OPERATOR_NAME, ZONE FROM hlr_zone_lookup WHERE concat (mcc,mnc) like ? limit 1 ) AS src SET
ps_profile.LAST_UPDATE = NOW(),
ps_profile.TADIG = src.TADIG,
ps_profile.OPERATOR_COUNTRY = src.OPERATOR_COUNTRY,
ps_profile.OPERATOR_NAME = src.OPERATOR_NAME, ps_profile.ZONE = src.ZONE
WHERE ps_profile.IMSI = ?");
$updatedHlrPsInfoCustomZoneQuery = $databaseConnection->prepare("UPDATE hlr_ps_roaming_info AS ps_profile,
(SELECT TADIG, OPERATOR_COUNTRY, OPERATOR_NAME FROM hlr_zone_lookup WHERE concat (mcc,mnc) like ? limit 1 ) AS src SET
ps_profile.LAST_UPDATE = NOW(),
ps_profile.TADIG = src.TADIG,
ps_profile.OPERATOR_COUNTRY =
src.OPERATOR_COUNTRY,
ps_profile.OPERATOR_NAME = src.OPERATOR_NAME,
ps_profile.ZONE = ?
WHERE ps_profile.IMSI = ?");
$subscriberProfileSelector = $databaseConnection->prepare("SELECT s.iccid, s.profile_id, p.code, s.core_account_id, s.core_seller_id FROM SUBSCRIBER s LEFT JOIN provider p on s.provider_id = p.id WHERE IMSI LIKE ? LIMIT 1;");
$subscriberProfileSelectorMultiIMSI = $databaseConnection->prepare("SELECT s.iccid, s.profile_id, p.code, s.core_account_id, s.core_seller_id, s.eid, s.eid_32, s.imsi FROM SUBSCRIBER s LEFT JOIN provider p on s.provider_id = p.id LEFT JOIN subscriber_has_multi_imsi shmi on s.SUBSCRIBER_ID = shmi.subscriber_id WHERE shmi.IMSI LIKE ? LIMIT 1;");
$checkIpAvailability = $databaseConnection->prepare("SELECT COUNT(imsi) FROM temporary_multi_imsi_session WHERE ip_address = ?;");
$temporaryMultiImsiInsert = $databaseConnection->prepare("INSERT INTO temporary_multi_imsi_session(imsi, ip_address, start_date, source_system) VALUE (?, ?, NOW(), ?) ON DUPLICATE KEY UPDATE ip_address = ?, start_date= NOW(), source_system = ?;");
$updateSessionId = $databaseConnection->prepare("UPDATE temporary_multi_imsi_session SET session_id = ? WHERE imsi = ?;");
$hlrZoneLookupProfileQueryV2 = $databaseConnection->prepare("select hzl.TADIG, hzl.OPERATOR_COUNTRY, hzl.OPERATOR_NAME, IF(hzl.ROAMING_ENABLED, 0, 1), IF(phz.custom_zone IS NULL,z.code,phz.custom_zone), p.id
from hlr_zone_lookup hzl
left join zone_has_hlr_zone_lookup zhhzl on hzl.ID = zhhzl.hlr_zone_lookup_id and CONCAT(hzl.mcc, hzl.mnc) = ?
left join zone z on zhhzl.zone_id = z.id
left join profile_has_zone phz on z.id = phz.zone_id
left join profile p on p.id = phz.profile_id
where p.id = (select s.PROFILE_ID from subscriber s where imsi = ?) limit 1;");
}
$conn_valid = (!$DBI::err);
&radiusd::radlog(RAD_LOG_INFO, "key: AAA-MobDB, status: 1, type: CLEAR, message: AAA->MobDB service is up: $conf{'db_host'}:$conf{'db_port'} $conf{'db_name'}")
}
sub sendPlatformNotification {
my ($imsi, $apn, $username, $outcome, $reason, $operatorName, $country, $iccid, $rat_type, $provider,
$accountId, $sellerId, $decodedUserLocationInfo, $technical_imsi, $multi_imsi) = @_;
# my $actualResponse = {};
# for my $key (keys %RAD_REPLY) {
# $actualResponse->{$key} = $RAD_REPLY{$key};
# }
# # TODO filter out passwords for PAP / CHAP authentication @Tudor
# my $actualRequest = {};
# for my $key (keys %RAD_REQUEST) {
# $actualRequest->{$key} = $RAD_REQUEST{$key};
# }
my $eventJson = {
eventTimestamp => int(gettimeofday() * 1000),
sourceSystem => hostname,
accountId => $accountId,
sellerId => $sellerId,
# radiusRequest => $actualRequest,
# radiusResponse => $actualResponse,
simCardCharacteristicCollection => [
{
simCardCharacteristicName => 'IMSI',
simCardCharacteristicValue => $imsi
},
{
simCardCharacteristicName => 'APN_NAME',
simCardCharacteristicValue => $apn
},
{
simCardCharacteristicName => 'APN_USERNAME',
simCardCharacteristicValue => $username
},
{
simCardCharacteristicName => 'OUTCOME',
simCardCharacteristicValue => $outcome
},
{
simCardCharacteristicName => 'REASON',
simCardCharacteristicValue => $reason
},
{
simCardCharacteristicName => 'OPERATOR_NAME',
simCardCharacteristicValue => $operatorName
},
{
simCardCharacteristicName => 'COUNTRY',
simCardCharacteristicValue => $country
},
{
simCardCharacteristicName => 'RAT_TYPE',
simCardCharacteristicValue => $rat_type
},
{
simCardCharacteristicName => 'TECHNICAL_IMSI',
simCardCharacteristicValue => $technical_imsi
},
{
simCardCharacteristicName => 'MULTI_IMSI',
simCardCharacteristicValue => $multi_imsi
},
{
simCardCharacteristicName => 'LOCATION_INFO',
simCardCharacteristicValue => encode_json($decodedUserLocationInfo)
}
],
eventName => 'DATA_SESSION_AUTHORIZATION',
provider => $provider,
};
if ($sellerId ne "") {
if (!defined($restClient)) {
$restClient = REST::Client->new();
}
&radiusd::radlog(RAD_LOG_INFO, $conf{'hook_host'} . $iccid . " -> " . encode_json($eventJson));
#$restClient->POST($conf{'hook_host'} . $iccid, encode_json($eventJson), { 'Content-type' => 'application/json' });
}
undef $imsi, $apn, $username, $outcome, $reason, $operatorName, $country, $iccid, $rat_type, $provider, $accountId, $sellerId, $decodedUserLocationInfo, $technical_imsi, $multi_imsi, $test_data, $eventJson;
}
sub authorize {
&log_request_attributes;
# my $congestion = int(rand(9))+1;
my $congestion = 1;
#
#congestion > 1, only 10% is being accepted, rest of 90% rejected
#
if ($congestion > 4) {
&radiusd::radlog(RAD_LOG_ERROR, "$RAD_REQUEST{'3GPP-IMSI'} -> CONGESTION REACHED ${congestion}");
undef $congestion;
return RLM_MODULE_REJECT;
}
if (!$conn_valid) {
&radiusd::radlog(RAD_LOG_INFO, "$RAD_REQUEST{'3GPP-IMSI'} -> NEW DB CONNECTION");
&conn_db;
}
unless ($databaseConnection->ping) {
&radiusd::radlog(RAD_LOG_INFO, "$RAD_REQUEST{'3GPP-IMSI'} -> NEW DB CONNECTION ON PING");
&conn_db;
}
my $visitedMccMnc = $RAD_REQUEST{'3GPP-SGSN-MCC-MNC'};
my $imsi = $RAD_REQUEST{'3GPP-IMSI'};
my $technical_imsi = $RAD_REQUEST{'3GPP-IMSI'};
my $multi_imsi = false;
my $test_data = false;
my $userLocationInfo = $RAD_REQUEST{'3GPP-User-Location-Info'};
my $decodedUserLocationInfo;
# check if mcc mnc is pressent in request
if ($visitedMccMnc eq "") {
#if mcc mnc is missing start query by sgsn address
my $visitedIp = $RAD_REQUEST{'3GPP-SGSN-Address'};
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> no 3GPP-SGSN-MCC-MNC -> check by ip addr ${visitedIp}");
$hlrZoneLookupSgsnQuery->execute($visitedIp);
my @hlrsgsn = $hlrZoneLookupSgsnQuery->fetchrow_array;
if (scalar(@hlrsgsn) != 0) {
# set visitedMccMnc to value from hlr_zone_lookup_by_sgsn table
$visitedMccMnc = @hlrsgsn[4];
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> ${visitedIp} -> ${visitedMccMnc}");
}
undef $visitedIp, @hlrsgsn;
}
if ($userLocationInfo) {
$decodedUserLocationInfo = getDecodedLocationInfo($userLocationInfo);
$decodedUserLocationInfo->{MCC} = substr($visitedMccMnc, 0, 3);
$decodedUserLocationInfo->{MNC} = substr($visitedMccMnc, 3, length($visitedMccMnc) - 3);
}
# check if subscriber has a custom profile
$subscriberProfileSelector->execute($imsi);
my @iccidAndProfileId = $subscriberProfileSelector->fetchrow_array;
if (scalar(@iccidAndProfileId) == 0) {
$subscriberProfileSelectorMultiIMSI->execute($imsi);
@iccidAndProfileId = $subscriberProfileSelectorMultiIMSI->fetchrow_array;
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> MAPPED TO @iccidAndProfileId[7], CONTINUING WITH @iccidAndProfileId[7]");
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> MAPPED TO ALL: " . join(", ", @iccidAndProfileId));
$imsi = @iccidAndProfileId[7];
$multi_imsi = true;
}
my $customZone = '';
my $isZonedDeny;
if ((index(@iccidAndProfileId[2], 'v2_') == 0) or (index(@iccidAndProfileId[2], 'dev') == 0)) {
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> v2 engine check");
$hlrZoneLookupProfileQueryV2->execute($visitedMccMnc, $imsi);
my @profileZone = $hlrZoneLookupProfileQueryV2->fetchrow_array;
if (scalar(@profileZone) != 0) {
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> {@profileZone}");
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> CUSTOM PROFILE OPERATOR: TADIG: {@profileZone[0]} COUNTRY: {@profileZone[1]} OPERATOR: {@profileZone[2]} IS_ALLOW: {@profileZone[3]} ZONE: {@profileZone[4]}");
$customZone = @profileZone[4];
$isZonedDeny = @profileZone[3] == 1 ? 0 : 1;
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> CUSTOM PROFILE OPERATOR: TADIG: {@profileZone[0]} COUNTRY: {@profileZone[1]} OPERATOR: {@profileZone[2]} IS_DENY: ${isZonedDeny} ZONE: ${customZone}");
$country = @profileZone[1];
$operatorName = @profileZone[2];
undef @profileZone;
}
else {
#maybe do another select here to actually show the country and operator where the session was denied
&radiusd::radlog(RAD_LOG_ERROR, "$imsi -> NO PROFILE OPERATOR FOUND: IMSI= $imsi, PROFILE_ID = {@iccidAndProfileId[1]} MCC-MNC ${visitedMccMnc} ");
&sendPlatformNotification($imsi, $RAD_REQUEST{'Called-Station-Id'}, $RAD_REQUEST{'User-Name'}, 'REJECT', 'Data session not allowed by current profile', "N/A", "N/A", @iccidAndProfileId[0], $RAD_REQUEST{'3GPP-RAT-Type'}, @iccidAndProfileId[2], @iccidAndProfileId[3], @iccidAndProfileId[4], $decodedUserLocationInfo);
&radiusd::radlog(RAD_LOG_ERROR, "$imsi -> REJECTED ");
undef $visitedMccMnc, $imsi, @iccidAndProfileId, $customZone, $isZonedDeny, $decodedUserLocationInfo, $operatorName, $country, $donor;
return RLM_MODULE_REJECT;
}
}
else {
if (@iccidAndProfileId[1] != "") {
&radiusd::radlog(RAD_LOG_INFO, "$imsi has custom profile: {@iccidAndProfileId[1]} for MccMnc $visitedMccMnc");
$hlrZoneLookupProfileQuery->execute($visitedMccMnc, $imsi);
my @profileZone = $hlrZoneLookupProfileQuery->fetchrow_array;
# if custom profile is find for request mcc mnc set if is allowed and custom zone
if (scalar(@profileZone) != 0) {
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> {@profileZone}");
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> CUSTOM PROFILE OPERATOR: TADIG: {@profileZone[0]} COUNTRY: {@profileZone[1]} OPERATOR: {@profileZone[2]} IS_ALLOW: {@profileZone[3]} ZONE: {@profileZone[4]}");
$customZone = @profileZone[4];
$isZonedDeny = @profileZone[3] == 1 ? 0 : 1;
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> CUSTOM PROFILE OPERATOR: TADIG: {@profileZone[0]} COUNTRY: {@profileZone[1]} OPERATOR: {@profileZone[2]} IS_DENY: ${isZonedDeny} ZONE: ${customZone}");
$country = @profileZone[1];
$operatorName = @profileZone[2];
undef @profileZone;
}
else {
&radiusd::radlog(RAD_LOG_ERROR, "$imsi -> NO PROFILE OPERATOR FOUND: PROFILE_ID = {@iccidAndProfileId[1]} MCC-MNC ${visitedMccMnc} ");
#TODO FIX THIS
&sendPlatformNotification($imsi, $RAD_REQUEST{'Called-Station-Id'}, $RAD_REQUEST{'User-Name'}, 'REJECT', 'Data session not allowed by current profile', "N/A", "N/A", @iccidAndProfileId[0], $RAD_REQUEST{'3GPP-RAT-Type'}, @iccidAndProfileId[2], @iccidAndProfileId[3], @iccidAndProfileId[4], $decodedUserLocationInfo);
&radiusd::radlog(RAD_LOG_ERROR, "$imsi -> REJECTED ");
undef $visitedMccMnc, $imsi, $multi_imsi, $technical_imsi, $test_data, $donor, @iccidAndProfileId, $customZone, $isZonedDeny, $decodedUserLocationInfo, $operatorName, $country;
return RLM_MODULE_REJECT;
}
}
}
#find subscriber
$subscriberQuery->execute($imsi);
my @ref = $subscriberQuery->fetchrow_array;
if (scalar(@ref) == 0) {
$subscriberMultiIMSIQuery->execute($imsi);
@ref = $subscriberMultiIMSIQuery->fetchrow_array;
}
my $nasIP = $RAD_REQUEST{'NAS-IP-Address'};
if (scalar(@ref) == 0 && $nasIP != '172.29.1.20') {
$imsi = $RAD_REQUEST{'3GPP-IMSI'};
&radiusd::radlog(RAD_LOG_ERROR, "IMSI = ${imsi} NOT_FOUND TRYING TO FALLBACK TO MULTI IMSI SCENARIO");
my $randomIp = generateRandomIP($nasIP);
$checkIpAvailability->execute($randomIp);
my $counter = $checkIpAvailability->fetchrow_array;
while ($counter != 0) {
$randomIp = generateRandomIP($nasIP);
$checkIpAvailability->execute($randomIp);
$counter = $checkIpAvailability->fetchrow_array;
}
if ($counter == 0 && is_ipv4($randomIp)) {
&radiusd::radlog(RAD_LOG_INFO, "MULTI IMSI $imsi ASSIGN TEMPORARY IP $randomIp");
$temporaryMultiImsiInsert->bind_param(1, $imsi);
$temporaryMultiImsiInsert->bind_param(2, $randomIp);
$temporaryMultiImsiInsert->bind_param(3, $nasIP);
$temporaryMultiImsiInsert->bind_param(4, $randomIp);
$temporaryMultiImsiInsert->bind_param(5, $nasIP);
$temporaryMultiImsiInsert->execute();
$RAD_REPLY{'Framed-IP-Address'} = $randomIp;
$RAD_REPLY{'Framed-IP-Netmask'} = '255.255.192.0';
$RAD_REPLY{'MS-Primary-DNS-Server'} = '8.8.8.8';
$RAD_REPLY{'MS-Secondary-DNS-Server'} = '8.8.4.4';
$RAD_REPLY{'Session-Timeout'} = '604800';
&radiusd::radlog(RAD_LOG_INFO, "IMSI = ${imsi} SUCCESS");
undef $visitedMccMnc, $imsi, @iccidAndProfileId, $customZone, $isZonedDeny, @ref, $randomIp, $counter;
&log_response_success($imsi, "");
return RLM_MODULE_OK;
}
undef $visitedMccMnc, $imsi, @iccidAndProfileId, $customZone, $isZonedDeny, @ref, $counter, $randomIp;
&radiusd::radlog(RAD_LOG_ERROR, "IMSI = ${imsi} MULTI IMSI SCENARIO: IP COULD NOT BE GENERATED BASED ON NAS-IP-Address");
return RLM_MODULE_REJECT;
}
my $donor = @ref[18];
#how can we change the @ref entry as it contains the old stuff, or do we just change the notification
# update ps roaming info for subscriber without ss7 signaling for now only iBasis SIM s
if (index($donor, 'IBasis') == 0) {
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> DONOR IS ${donor}");
if (length($customZone) == 0) {
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> UPDATE PS PROFILE ${visitedMccMnc}");
$updatedHlrPsInfoQuery->bind_param(1, $visitedMccMnc);
$updatedHlrPsInfoQuery->bind_param(2, $imsi);
$updatedHlrPsInfoQuery->execute();
}
else {
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> UPDATE PS PROFILE ${visitedMccMnc} CUSTOM ZONE ${customZone}");
#if custom zone is not empty make a custom update with values from hlr_zone_lookup and $customZone
$updatedHlrPsInfoCustomZoneQuery->bind_param(1, $visitedMccMnc);
$updatedHlrPsInfoCustomZoneQuery->bind_param(2, $customZone);
$updatedHlrPsInfoCustomZoneQuery->bind_param(3, $imsi);
$updatedHlrPsInfoCustomZoneQuery->execute();
}
}
my $iccid = @ref[10];
#if $isZonedDeny is empty profile dosen't contains values for this mcc mcn so get value from hlr_zone_lookup
if (!defined $isZonedDeny) {
&radiusd::radlog(RAD_LOG_DEBUG, "ICCID = ${iccid} IMSI = ${imsi} ZONE IS DENIAL IS NOT DEFINED CHECK IN HLR ZONE LOOKUP");
$hlrZoneLookupQuery->execute($visitedMccMnc);
my @hlr = $hlrZoneLookupQuery->fetchrow_array;
$isZonedDeny = @hlr[2];
undef @hlr;
}
if ($isZonedDeny != 0) {
#TODO FIX THIS
&radiusd::radlog(RAD_LOG_ERROR, "ICCID = ${iccid} IMSI = ${imsi} DATA_SESSION_NOT_ALLOWED - ZONE DENIAL");
&sendPlatformNotification($imsi, $RAD_REQUEST{'Called-Station-Id'}, $RAD_REQUEST{'User-Name'}, 'REJECT', 'Data session not allowed', ($operatorName ? $operatorName : @ref[12]), ($country ? $country : @ref[13]), $iccid, $RAD_REQUEST{'3GPP-RAT-Type'}, @iccidAndProfileId[2], @ref[14], @ref[15], $decodedUserLocationInfo, $technical_imsi, $multi_imsi);
&radiusd::radlog(RAD_LOG_ERROR, "$imsi -> REJECTED ");
undef $visitedMccMnc, $imsi, $iccid, @iccidAndProfileId, $customZone, $isZonedDeny, @ref, $decodedUserLocationInfo;
return RLM_MODULE_REJECT;
}
$hlrRoamingSteeringQuery->execute($imsi, $visitedMccMnc);
my @hrs = $hlrRoamingSteeringQuery->fetchrow_array;
if (scalar(@hrs) != 0) {
&radiusd::radlog(RAD_LOG_ERROR, "ICCID = ${iccid} IMSI = ${imsi} DATA_SESSION_BLOCKED_BY_USER");
&sendPlatformNotification($imsi, $RAD_REQUEST{'Called-Station-Id'}, $RAD_REQUEST{'User-Name'}, 'REJECT', 'Mobile Data blocked by user for ' . @hrs[1], @hrs[1], @hrs[0], $iccid, $RAD_REQUEST{'3GPP-RAT-Type'}, @iccidAndProfileId[2], @ref[14], @ref[15], $decodedUserLocationInfo, $technical_imsi, $multi_imsi);
&radiusd::radlog(RAD_LOG_ERROR, "$imsi -> REJECTED ");
undef $visitedMccMnc, $imsi, $iccid, @iccidAndProfileId, $customZone, $isZonedDeny, @ref, @hrs, $decodedUserLocationInfo;
return RLM_MODULE_REJECT;
}
# check if subscriber is suspended
if (@ref[1] == 1) {
# check if subscriber is suspended and has access to test data
# if (@ref[1] == 1)
my $ccs_profile_id = @ref[16];
my $ccs_profile_type = @ref[17];
&radiusd::radlog(RAD_LOG_ERROR, "ccs_profile_id = ${ccs_profile_id}, ccs_profile_type = ${ccs_profile_type}");
if ($ccs_profile_type eq 'TEST_DATA') {
&radiusd::radlog(RAD_LOG_ERROR, "ICCID = ${iccid} IMSI = ${imsi} SUBSCRIBER_SUSPENDED BUT ALLOWING TEST DATA");
$test_data = true; #continue
}
else {
&radiusd::radlog(RAD_LOG_ERROR, "ICCID = ${iccid} IMSI = ${imsi} SUBSCRIBER_SUSPENDED");
&sendPlatformNotification($imsi, $RAD_REQUEST{'Called-Station-Id'}, $RAD_REQUEST{'User-Name'}, 'REJECT', 'Subscriber is suspended', ($operatorName ? $operatorName : @ref[12]), ($country ? $country : @ref[13]), $iccid, $RAD_REQUEST{'3GPP-RAT-Type'}, @iccidAndProfileId[2], @ref[14], @ref[15], $decodedUserLocationInfo, $technical_imsi, $multi_imsi);
&radiusd::radlog(RAD_LOG_ERROR, "$imsi -> REJECTED ");
undef $visitedMccMnc, $imsi, $iccid, @iccidAndProfileId, $customZone, $isZonedDeny, @ref, @hrs, $decodedUserLocationInfo, $ccs_profile_id, $ccs_profile_type;
return RLM_MODULE_REJECT;
}
}
# check if mobile data is blocked by user
if (@ref[7] eq 'true') {
&radiusd::radlog(RAD_LOG_ERROR, "ICCID = ${iccid} IMSI = ${imsi} MOBILE_DATA_BLOCKED_BY_USER");
&sendPlatformNotification($imsi, $RAD_REQUEST{'Called-Station-Id'}, $RAD_REQUEST{'User-Name'}, 'REJECT', 'Mobile Data blocked by user', ($operatorName ? $operatorName : @ref[12]), ($country ? $country : @ref[13]), $iccid, $RAD_REQUEST{'3GPP-RAT-Type'}, @iccidAndProfileId[2], @ref[14], @ref[15], $decodedUserLocationInfo, $technical_imsi, $multi_imsi);
&radiusd::radlog(RAD_LOG_ERROR, "$imsi -> REJECTED ");
undef $visitedMccMnc, $imsi, $iccid, @iccidAndProfileId, $customZone, $isZonedDeny, @ref, @hrs, $decodedUserLocationInfo;
return RLM_MODULE_REJECT;
}
# check if apn credentials match
if ((@ref[8] ne $RAD_REQUEST{'User-Name'} || @ref[9] ne $RAD_REQUEST{'User-Password'}) && @ref[11] eq 'true') {
&radiusd::radlog(RAD_LOG_ERROR, "ICCID = ${iccid} IMSI = ${imsi} INVALID_APN_CREDENTIALS");
&sendPlatformNotification($imsi, $RAD_REQUEST{'Called-Station-Id'}, $RAD_REQUEST{'User-Name'}, 'REJECT', 'APN Authentication failed', ($operatorName ? $operatorName : @ref[12]), ($country ? $country : @ref[13]), $iccid, $RAD_REQUEST{'3GPP-RAT-Type'}, @iccidAndProfileId[2], @ref[14], @ref[15], $decodedUserLocationInfo, $technical_imsi, $multi_imsi);
&radiusd::radlog(RAD_LOG_ERROR, "$imsi -> REJECTED ");
undef $visitedMccMnc, $imsi, $iccid, @iccidAndProfileId, $customZone, $isZonedDeny, @ref, @hrs, $decodedUserLocationInfo;
return RLM_MODULE_REJECT;
}
# check if apn match
# if (not lc($RAD_REQUEST{'Called-Station-Id'}) eq lc($ref[2])) {
#if (index(lc $RAD_REQUEST{'Called-Station-Id'}, lc @ref[2]) != 0)
my @apns = split(/,/, lc @ref[2]);
my $found = 0;
foreach my $apn (@apns) {
$apn =~ s/^\s+|\s+$//g; # trimming whitespace from start and end
if (index(lc $RAD_REQUEST{'Called-Station-Id'}, $apn) != -1) {
$found = 1;
last;
}
}
unless ($found) {
&radiusd::radlog(RAD_LOG_ERROR, "ICCID = ${iccid} IMSI = ${imsi} INVALID_APN {@ref[2]} REQUESTED {$RAD_REQUEST{'Called-Station-Id'}}");
&sendPlatformNotification($imsi, $RAD_REQUEST{'Called-Station-Id'}, $RAD_REQUEST{'User-Name'}, 'REJECT', 'Invalid APN', ($operatorName ? $operatorName : @ref[12]), ($country ? $country : @ref[13]), $iccid, $RAD_REQUEST{'3GPP-RAT-Type'}, @iccidAndProfileId[2], @ref[14], @ref[15], $decodedUserLocationInfo, $technical_imsi, $multi_imsi);
&radiusd::radlog(RAD_LOG_ERROR, "$imsi -> REJECTED ");
undef $visitedMccMnc, $imsi, $iccid, @iccidAndProfileId, $customZone, $isZonedDeny, @ref, @hrs, $decodedUserLocationInfo;
return RLM_MODULE_REJECT;
}
# success
&radiusd::radlog(RAD_LOG_INFO, "!!!!!!!!{@ref[12]} ICCID = ${iccid} IMSI = ${imsi} SUCCESS 0");
my $ip = parallelIPProcedure($imsi, $RAD_REQUEST{'NAS-IP-Address'}, @ref[3]);
$RAD_REPLY{'Framed-IP-Address'} = $ip;
$RAD_REPLY{'Framed-IP-Netmask'} = @ref[4];
$RAD_REPLY{'MS-Primary-DNS-Server'} = @ref[5];
$RAD_REPLY{'MS-Secondary-DNS-Server'} = @ref[6];
$RAD_REPLY{'Session-Timeout'} = '604800';
#$RAD_REPLY{'Framed-Route'} = '45.148.166.176/29 45.148.166.161 1 2 -1 3 400';
&log_response_success($imsi, $iccid);
&sendPlatformNotification(
$imsi,
$RAD_REQUEST{'Called-Station-Id'},
$RAD_REQUEST{'User-Name'},
'ACCEPT',
'Mobile Data Session Authorized',
($operatorName ? $operatorName : @ref[12]),
($country ? $country : @ref[13]),
$iccid,
$RAD_REQUEST{'3GPP-RAT-Type'},
@iccidAndProfileId[2],
@ref[14],
@ref[15],
$decodedUserLocationInfo,
$technical_imsi,
$multi_imsi,
$RAD_REPLY);
undef $visitedMccMnc, $imsi, $test_data, $donor, $multi_imsi, $technical_imsi, @iccidAndProfileId, $customZone, $isZonedDeny, @ref, $iccid, @hrs, $decodedUserLocationInfo, $ip, $operatorName, $country;
return RLM_MODULE_OK;
}
# Function to handle authenticate
sub authenticate {
return RLM_MODULE_OK;
}
# Function to handle preacct
sub preacct {
# For debugging purposes only
# &log_request_attributes;
return RLM_MODULE_OK;
}
# Function to handle accounting
sub accounting {
&log_request_attributes;
my $sessionId = $RAD_REQUEST{'Acct-Session-Id'};
my $imsi = $RAD_REQUEST{'3GPP-IMSI'};
$updateSessionId->bind_param(1, $sessionId);
$updateSessionId->bind_param(2, $imsi);
$updateSessionId->execute();
undef $sessionId, $imsi;
return RLM_MODULE_OK;
}
# Function to handle checksimul
sub checksimul {
# For debugging purposes only
# &log_request_attributes;
return RLM_MODULE_OK;
}
# Function to handle pre_proxy
sub pre_proxy {
# For debugging purposes only
# &log_request_attributes;
return RLM_MODULE_OK;
}
# Function to handle post_proxy
sub post_proxy {
# For debugging purposes only
# &log_request_attributes;
return RLM_MODULE_OK;
}
# Function to handle post_auth
sub post_auth {
# For debugging purposes only
# &log_request_attributes;
return RLM_MODULE_OK;
}
# Function to handle detach
sub detach {
# For debugging purposes only
# &log_request_attributes;
# Do some logging.
&radiusd::radlog(0, "rlm_perl::Detaching. Reloading. Done.");
}
sub log_request_attributes {
my $logJson = {};
for my $key (keys %RAD_REQUEST) {
$logJson->{$key} = $RAD_REQUEST{$key};
}
&radiusd::radlog(RAD_LOG_INFO, 'REQUEST: ' . encode_json($logJson));
undef $logJson;
}
sub log_response_success {
my ($imsi, $iccid) = @_;
my $logJson = {};
for my $key (keys %RAD_REPLY) {
$logJson->{$key} = $RAD_REPLY{$key};
}
&radiusd::radlog(RAD_LOG_INFO, "ICCID = ${iccid} IMSI = ${imsi} RESPONSE: " . encode_json($logJson));
undef $logJson;
}
sub getDecodedLocationInfo {
my $type = 'not_set';
my $decodedLocationInfo = {};
my @hex = ($_[0] =~ /(..)/g);
my @dec = map {hex($_)} @hex;
my @bytes = map {pack('C', $_)} @dec;
my $intType = $dec[1];
given ($intType) {
when (0) {
$type = "CGI";
$decodedLocationInfo->{type} = $type;
$decodedLocationInfo->{LAC} = Math::BigInt->new('0x' . join('', map sprintf('%.2x', $_), @dec[5 .. 6]))->{value}[0];
$decodedLocationInfo->{CI} = Math::BigInt->new('0x' . join('', map sprintf('%.2x', $_), @dec[7 .. 8]))->{value}[0]
}
when (1) {
$type = "SAI";
$decodedLocationInfo->{type} = $type;
$decodedLocationInfo->{LAC} = Math::BigInt->new('0x' . join('', map sprintf('%.2x', $_), @dec[5 .. 6]))->{value}[0];
$decodedLocationInfo->{SAC} = Math::BigInt->new('0x' . join('', map sprintf('%.2x', $_), @dec[7 .. 8]))->{value}[0]
}
when (2) {
$type = "RAI";
$decodedLocationInfo->{type} = $type;
$decodedLocationInfo->{LAC} = Math::BigInt->new('0x' . join('', map sprintf('%.2x', $_), @dec[5 .. 6]))->{value}[0];
$decodedLocationInfo->{RAC} = Math::BigInt->new('0x' . join('', map sprintf('%.2x', $_), @dec[7 .. 7]))->{value}[0]
}
when (128) {
$type = "TAI";
$decodedLocationInfo->{type} = $type;
$decodedLocationInfo->{LAC} = "NOT IMPLEMENTED";
$decodedLocationInfo->{CI} = "NOT IMPLEMENTED"
}
when (129) {
$type = "ECGI";
$decodedLocationInfo->{type} = $type;
$decodedLocationInfo->{LAC} = "NOT IMPLEMENTED";
$decodedLocationInfo->{CI} = "NOT IMPLEMENTED"
}
when (130) {
$type = "TAI & ECGI";
$decodedLocationInfo->{type} = $type;
$decodedLocationInfo->{TAC} = Math::BigInt->new('0x' . join('', map sprintf('%.2x', $_), @dec[5 .. 6]))->{value}[0];
$decodedLocationInfo->{ECI} = Math::BigInt->new('0x' . join('', map sprintf('%.2x', $_), @dec[10 .. 13]))->{value}[0]
}
}
return $decodedLocationInfo;
}
sub generateRandomIP {
&radiusd::radlog(0, "rlm_perl::generateRandomIP start");
my ($nasIP) = @_;
my @digits = "";
if ($nasIP eq "216.168.191.35") {
@digits = "10.192.0";
}
if ($nasIP eq "216.168.191.33") {
@digits = "10.193.0";
}
if ($nasIP eq "10.5.146.35") {
@digits = "10.187.0";
}
if ($nasIP eq "202.63.22.8") {
@digits = "10.192.0";
}
push @digits, int(rand(253)) + 1;
&radiusd::radlog(0, "rlm_perl::generateRandomIP stop");
return join '.', @digits;
}
sub parallelIPProcedure() {
&radiusd::radlog(0, "rlm_perl::parallelIPProcedure start");
my ($imsi, $nasIP, $ip) = @_;
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> $nasIP -> $ip");
my @spl = split('\.', $ip);
my $ipMask = @spl[0] . "." . @spl[1];
my $ipTermination = @spl[2] . "." . @spl[3];
my $query = "";
if ($nasIP eq "216.168.191.33") {
$query = "SELECT SUBSTRING_INDEX(DAL, '.', 2), SUBSTRING_INDEX(AMS, '.', 2) FROM aaa_parallel_ranges WHERE SUBSTRING_INDEX(AMS, '.', 2) = " . $ipMask . " OR SUBSTRING_INDEX(DAL, '.', 2) = " . $ipMask . ";";
my $aaaIP = $databaseConnection->prepare($query);
$aaaIP->execute;
my @result = $aaaIP->fetchrow_array;
if (scalar(@result) == 0) {
&radiusd::radlog(RAD_LOG_ERROR, "NO RESULT FOUND FOR $ipMask IN AMS");
}
else {
if (@result[0] ne $ipMask) {
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> $ip will change into @result[0].$ipTermination");
$ip = @result[0] . "." . $ipTermination
}
}
undef $query, $aaaIP, $ipTermination, $ipMask, @spl, $imsi, $nasIP;
}
if ($nasIP eq "216.168.191.35") {
$query = "SELECT SUBSTRING_INDEX(DAL, '.', 2), SUBSTRING_INDEX(AMS, '.', 2) FROM aaa_parallel_ranges WHERE SUBSTRING_INDEX(AMS, '.', 2) = " . $ipMask . " OR SUBSTRING_INDEX(DAL, '.', 2) = " . $ipMask . ";";
my $aaaIP = $databaseConnection->prepare($query);
$aaaIP->execute;
my @result = $aaaIP->fetchrow_array;
if (scalar(@result) == 0) {
&radiusd::radlog(RAD_LOG_ERROR, "NO RESULT FOUND FOR $ipMask IN AMS");
}
else {
if (@result[1] ne $ipMask) {
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> $ip will change into @result[1].$ipTermination");
$ip = @result[1] . "." . $ipTermination
}
}
undef $query, $aaaIP, $ipTermination, $ipMask, @spl, $imsi, $nasIP;
}
if($nasIP eq "202.63.22.8"){
$query = "SELECT SUBSTRING_INDEX(DAL, '.', 2), SUBSTRING_INDEX(AMS, '.', 2) FROM aaa_parallel_ranges WHERE SUBSTRING_INDEX(AMS, '.', 2) = " . $ipMask . " OR SUBSTRING_INDEX(DAL, '.', 2) = " . $ipMask . ";";
my $aaaIP = $databaseConnection->prepare($query);
$aaaIP->execute;
my @result = $aaaIP->fetchrow_array;
if (scalar(@result) == 0) {
&radiusd::radlog(RAD_LOG_ERROR, "NO RESULT FOUND FOR $ipMask IN AMS");
} else {
if(@result[1] ne $ipMask){
&radiusd::radlog(RAD_LOG_INFO, "$imsi -> $ip will change into @result[ 1].$ipTermination");
$ip = @result[1] . "." . $ipTermination
}
}
undef $query, $aaaIP, $ipTermination, $ipMask, @spl, $imsi, $nasIP;
}
&radiusd::radlog(0, "rlm_perl::parallelIPProcedure stop");
return $ip;
}