From 52ce8b5cdd5d8cd45d7bf94966b0a6344b524a76 Mon Sep 17 00:00:00 2001 From: nickvsnetworking Date: Sun, 20 Aug 2023 12:56:58 +1000 Subject: [PATCH 01/11] prom_diam_result_code --- diameter.py | 94 ++++++++++++++++++++++++++++++++------------------ lib/logtool.py | 1 + 2 files changed, 62 insertions(+), 33 deletions(-) diff --git a/diameter.py b/diameter.py index 26dbee5..e757425 100644 --- a/diameter.py +++ b/diameter.py @@ -225,9 +225,37 @@ def __init__(self, OriginHost, OriginRealm, ProductName, MNC, MCC): #Generates an AVP with inputs provided (AVP Code, AVP Flags, AVP Content, Padding) #AVP content must already be in HEX - This can be done with binascii.hexlify(avp_content.encode()) - def generate_avp(self, avp_code, avp_flags, avp_content): + def generate_avp(self, avp_code, avp_flags, avp_content, avps=None, packet_vars=None): + if avp_code == 268 or avp_code == 298: + + + try: + DiameterLogger.debug("Incrementing Prometheus Stats for prom_diam_result_code") + try: + imsi = self.get_avp_data(avps, 1)[0] #Get IMSI from User-Name AVP in request + imsi = binascii.unhexlify(imsi).decode('utf-8') #Convert IMSI + except: + imsi = '' + + try: + OriginHost = self.get_avp_data(avps, 264)[0] #Get OriginHost from AVP + OriginHost = binascii.unhexlify(OriginHost).decode('utf-8') #Format it + except: + OriginHost = '' + DiameterLogger.debug("Generating result code: " + str(int(avp_content, 16)) + " for OriginHost: " + str(OriginHost) + " and IMSI: " + str(imsi)) + #Turn result code into int + prom_diam_result_code.labels( + diameter_application_id = packet_vars['ApplicationId'], + diameter_cmd_code = packet_vars['command_code'], + result_code = int(avp_content, 16), + endpoint = OriginHost, + imsi = imsi, + ).inc() + DiameterLogger.debug("Incremented Prometheus Stats for prom_diam_result_code") + except Exception as E: + DiameterLogger.debug("Failed to increment Prometheus Stats for prom_diam_result_code") + DiameterLogger.debug(E) avp_code = format(avp_code,"x").zfill(8) - avp_length = 1 ##This is a placeholder that's overwritten later #AVP Must always be a multiple of 4 - Round up to nearest multiple of 4 and fill remaining bits with padding @@ -517,7 +545,7 @@ def Generate_Prom_Stats(self): #Capabilities Exchange Answer def Answer_257(self, packet_vars, avps, recv_ip): avp = '' #Initiate empty var AVP - avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4)) #Result Code (DIAMETER_SUCCESS (2001)) + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) avp += self.generate_avp(264, 40, self.OriginHost) #Origin Host avp += self.generate_avp(296, 40, self.OriginRealm) #Origin Realm for avps_to_check in avps: #Only include AVP 278 (Origin State) if inital request included it @@ -557,7 +585,7 @@ def Answer_257(self, packet_vars, avps, recv_ip): def Answer_280(self, packet_vars, avps): avp = '' #Initiate empty var AVP - avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4)) #Result Code (DIAMETER_SUCCESS (2001)) + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) avp += self.generate_avp(264, 40, self.OriginHost) #Origin Host avp += self.generate_avp(296, 40, self.OriginRealm) #Origin Realm for avps_to_check in avps: #Only include AVP 278 (Origin State) if inital request included it @@ -574,7 +602,7 @@ def Answer_282(self, packet_vars, avps): avp = '' #Initiate empty var AVP avp += self.generate_avp(264, 40, self.OriginHost) #Origin Host avp += self.generate_avp(296, 40, self.OriginRealm) #Origin Realm - avp += self.generate_avp(268, 40, "000007d1") #Result Code (DIAMETER_SUCCESS (2001)) + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) response = self.generate_diameter_packet("01", "00", 282, 0, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet DiameterLogger.debug("Successfully Generated DPA") return response @@ -613,7 +641,7 @@ def Answer_16777251_316(self, packet_vars, avps): DiameterLogger.error("failed to get data backfrom database for imsi " + str(imsi)) DiameterLogger.error("Error is " + str(e)) DiameterLogger.error("Responding with DIAMETER_ERROR_USER_UNKNOWN") - avp += self.generate_avp(268, 40, self.int_to_hex(5030, 4)) + avp += self.generate_avp(268, 40, self.int_to_hex(5030, 4), avps=avps, packet_vars=packet_vars) #Result Code response = self.generate_diameter_packet("01", "40", 316, 16777251, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet DiameterLogger.info("Diameter user unknown - Sending ULA with DIAMETER_ERROR_USER_UNKNOWN") return response @@ -644,7 +672,7 @@ def Answer_16777251_316(self, packet_vars, avps): #Boilerplate AVPs - avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4)) #Result Code (DIAMETER_SUCCESS (2001)) + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) avp += self.generate_avp(277, 40, "00000001") #Auth-Session-State avp += self.generate_vendor_avp(1406, "c0", 10415, "00000001") #ULA Flags @@ -825,7 +853,7 @@ def Answer_16777251_318(self, packet_vars, avps): #Experimental Result AVP(Response Code for Failure) avp_experimental_result = '' avp_experimental_result += self.generate_vendor_avp(266, 40, 10415, '') #AVP Vendor ID - avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(5001, 4)) #AVP Experimental-Result-Code: DIAMETER_ERROR_USER_UNKNOWN (5001) + avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(5001, 4), avps=avps, packet_vars=packet_vars) #AVP Experimental-Result-Code: DIAMETER_ERROR_USER_UNKNOWN (5001) avp += self.generate_avp(297, 40, avp_experimental_result) #AVP Experimental-Result(297) avp += self.generate_avp(277, 40, "00000001") #Auth-Session-State @@ -893,7 +921,7 @@ def Answer_16777251_318(self, packet_vars, avps): avp += self.generate_vendor_avp(1413, "c0", 10415, eutranvector_complete) #Authentication-Info (3GPP) avp += self.generate_avp(264, 40, self.OriginHost) #Origin Host avp += self.generate_avp(296, 40, self.OriginRealm) #Origin Realm - avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4)) #Result Code (DIAMETER_SUCCESS (2001)) + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) avp += self.generate_avp(277, 40, "00000001") #Auth-Session-State avp += self.generate_avp(260, 40, "0000010a4000000c000028af000001024000000c01000023") #avp += self.generate_avp(260, 40, "000001024000000c" + format(int(16777251),"x").zfill(8) + "0000010a4000000c000028af") #Vendor-Specific-Application-ID (S6a) @@ -912,7 +940,7 @@ def Answer_16777251_321(self, packet_vars, avps): avp = '' session_id = self.get_avp_data(avps, 263)[0] #Get Session-ID avp += self.generate_avp(263, 40, session_id) #Session-ID AVP set - avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4)) #Result Code (DIAMETER_SUCCESS (2001)) + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) avp += self.generate_avp(260, 40, "000001024000000c" + format(int(16777251),"x").zfill(8) + "0000010a4000000c000028af") #Vendor-Specific-Application-ID (S6a) avp += self.generate_avp(277, 40, "00000001") #Auth-Session-State (No state maintained) @@ -942,7 +970,7 @@ def Answer_16777251_323(self, packet_vars, avps): avp = '' session_id = self.get_avp_data(avps, 263)[0] #Get Session-ID avp += self.generate_avp(263, 40, session_id) #Session-ID AVP set - avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4)) #Result Code (DIAMETER_SUCCESS (2001)) + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) avp += self.generate_avp(260, 40, "000001024000000c" + format(int(16777251),"x").zfill(8) + "0000010a4000000c000028af") #Vendor-Specific-Application-ID (S6a) avp += self.generate_avp(277, 40, "00000001") #Auth-Session-State (No state maintained) @@ -1098,7 +1126,7 @@ def Answer_16777238_272(self, packet_vars, avps): avp += self.generate_avp(264, 40, self.OriginHost) #Origin Host avp += self.generate_avp(296, 40, self.OriginRealm) #Origin Realm - avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4)) #Result Code (DIAMETER_SUCCESS (2001)) + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) response = self.generate_diameter_packet("01", "40", 272, 16777238, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet return response @@ -1148,7 +1176,7 @@ def Answer_16777216_300(self, packet_vars, avps): #Experimental Result AVP avp_experimental_result = '' avp_experimental_result += self.generate_vendor_avp(266, 40, 10415, '') #AVP Vendor ID - avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(result_code, 4)) #AVP Experimental-Result-Code + avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(result_code, 4), avps=avps, packet_vars=packet_vars) #AVP Experimental-Result-Code avp += self.generate_avp(297, 40, avp_experimental_result) #AVP Experimental-Result(297) response = self.generate_diameter_packet("01", "40", 300, 16777217, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet return response @@ -1164,7 +1192,7 @@ def Answer_16777216_300(self, packet_vars, avps): database.Update_Serving_CSCF(imsi, serving_cscf=None) #Populate S-CSCF Address avp += self.generate_vendor_avp(602, "c0", 10415, str(binascii.hexlify(str.encode(ims_subscriber_details['scscf'])),'ascii')) - avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4)) #Result Code (DIAMETER_SUCCESS (2001)) + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) response = self.generate_diameter_packet("01", "40", 300, 16777216, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet return response @@ -1176,7 +1204,7 @@ def Answer_16777216_300(self, packet_vars, avps): avp += self.generate_vendor_avp(602, "c0", 10415, str(binascii.hexlify(str.encode(ims_subscriber_details['scscf'])),'ascii')) experimental_avp = '' experimental_avp += experimental_avp + self.generate_avp(266, 40, format(int(10415),"x").zfill(8)) #3GPP Vendor ID - experimental_avp = experimental_avp + self.generate_avp(298, 40, format(int(2002),"x").zfill(8)) #DIAMETER_SUBSEQUENT_REGISTRATION (2002) + experimental_avp = experimental_avp + self.generate_avp(298, 40, format(int(2002),"x").zfill(8), avps=avps, packet_vars=packet_vars) #DIAMETER_SUBSEQUENT_REGISTRATION (2002) avp += self.generate_avp(297, 40, experimental_avp) #Expermental-Result else: DiameterLogger.debug("No SCSCF Assigned from DB") @@ -1193,7 +1221,7 @@ def Answer_16777216_300(self, packet_vars, avps): DiameterLogger.info("Using generated S-CSCF Address as none set in scscf_pool in config") experimental_avp = '' experimental_avp += experimental_avp + self.generate_avp(266, 40, format(int(10415),"x").zfill(8)) #3GPP Vendor ID - experimental_avp = experimental_avp + self.generate_avp(298, 40, format(int(2001),"x").zfill(8)) #DIAMETER_FIRST_REGISTRATION (2001) + experimental_avp = experimental_avp + self.generate_avp(298, 40, format(int(2001),"x").zfill(8), avps=avps, packet_vars=packet_vars) #DIAMETER_FIRST_REGISTRATION (2001) avp += self.generate_avp(297, 40, experimental_avp) #Expermental-Result response = self.generate_diameter_packet("01", "40", 300, 16777216, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet @@ -1239,7 +1267,7 @@ def Answer_16777216_301(self, packet_vars, avps): #Experimental Result AVP avp_experimental_result = '' avp_experimental_result += self.generate_vendor_avp(266, 40, 10415, '') #AVP Vendor ID - avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(result_code, 4)) #AVP Experimental-Result-Code + avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(result_code, 4), avps=avps, packet_vars=packet_vars) #AVP Experimental-Result-Code avp += self.generate_avp(297, 40, avp_experimental_result) #AVP Experimental-Result(297) response = self.generate_diameter_packet("01", "40", 301, 16777217, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet return response @@ -1279,7 +1307,7 @@ def Answer_16777216_301(self, packet_vars, avps): DiameterLogger.debug("SAR is not Register") database.Update_Serving_CSCF(imsi, serving_cscf=None) - avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4)) #Result Code (DIAMETER_SUCCESS (2001)) + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) response = self.generate_diameter_packet("01", "40", 301, 16777216, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet return response @@ -1330,12 +1358,12 @@ def Answer_16777216_302(self, packet_vars, avps): #Experimental Result AVP avp_experimental_result = '' avp_experimental_result += self.generate_vendor_avp(266, 40, 10415, '') #AVP Vendor ID - avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(result_code, 4)) #AVP Experimental-Result-Code + avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(result_code, 4), avps=avps, packet_vars=packet_vars) #AVP Experimental-Result-Code avp += self.generate_avp(297, 40, avp_experimental_result) #AVP Experimental-Result(297) response = self.generate_diameter_packet("01", "40", 302, 16777217, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet return response - avp += self.generate_avp(268, 40, "000007d1") #DIAMETER_SUCCESS + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) response = self.generate_diameter_packet("01", "40", 302, 16777216, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet return response @@ -1370,7 +1398,7 @@ def Answer_16777216_303(self, packet_vars, avps): event='Unknown User', imsi_prefix = str(username[0:6]), ).inc() - experimental_result = self.generate_avp(298, 40, self.int_to_hex(5001, 4)) #Result Code (DIAMETER ERROR - User Unknown) + experimental_result = self.generate_avp(298, 40, self.int_to_hex(5001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER ERROR - User Unknown) experimental_result = experimental_result + self.generate_vendor_avp(266, 40, 10415, "") #Experimental Result (297) avp += self.generate_avp(297, 40, experimental_result) @@ -1444,7 +1472,7 @@ def Answer_16777216_303(self, packet_vars, avps): avp += self.generate_vendor_avp(607, "c0", 10415, "00000001") #3GPP-SIP-Number-Auth-Items - avp += self.generate_avp(268, 40, "000007d1") #DIAMETER_SUCCESS + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) response = self.generate_diameter_packet("01", "40", 303, 16777216, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet return response @@ -1459,19 +1487,19 @@ def Respond_ResultCode(self, packet_vars, avps, result_code): session_id = self.get_avp_data(avps, 263)[0] #Get Session-ID avp += self.generate_avp(263, 40, session_id) #Set session ID to received session ID except: - DiameterLogger.info("Failed to add SessionID into error") + DiameterLogger.info("Respond_ResultCode: Failed to add SessionID into error") for avps_to_check in avps: #Only include AVP 260 (Vendor-Specific-Application-ID) if inital request included it if avps_to_check['avp_code'] == 260: concat_subavp = '' for sub_avp in avps_to_check['misc_data']: concat_subavp += self.generate_avp(sub_avp['avp_code'], sub_avp['avp_flags'], sub_avp['misc_data']) avp += self.generate_avp(260, 40, concat_subavp) #Vendor-Specific-Application-ID - avp += self.generate_avp(268, 40, self.int_to_hex(result_code, 4)) #Response Code + avp += self.generate_avp(268, 40, self.int_to_hex(result_code, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) #Experimental Result AVP(Response Code for Failure) avp_experimental_result = '' avp_experimental_result += self.generate_vendor_avp(266, 40, 10415, '') #AVP Vendor ID - avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(result_code, 4)) #AVP Experimental-Result-Code: DIAMETER_ERROR_USER_UNKNOWN (5001) + avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(result_code, 4), avps=avps, packet_vars=packet_vars) #AVP Experimental-Result-Code: DIAMETER_ERROR_USER_UNKNOWN (5001) avp += self.generate_avp(297, 40, avp_experimental_result) #AVP Experimental-Result(297) response = self.generate_diameter_packet("01", "60", int(packet_vars['command_code']), int(packet_vars['ApplicationId']), packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet @@ -1487,7 +1515,7 @@ def Answer_16777216_304(self, packet_vars, avps): auth_application_id = self.generate_avp(248, 40, self.int_to_hex(16777252, 8)) DiameterLogger.debug("auth_application_id: " + auth_application_id) avp += self.generate_avp(260, 40, "0000010a4000000c000028af000001024000000c01000000") #Vendor-Specific-Application-ID for Cx - avp += self.generate_avp(268, 40, "000007d1") #Result Code - DIAMETER_SUCCESS + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) avp += self.generate_avp(277, 40, "00000001") #Auth Session State avp += self.generate_avp(264, 40, self.OriginHost) #Origin Host avp += self.generate_avp(296, 40, self.OriginRealm) #Origin Realm @@ -1538,7 +1566,7 @@ def Answer_16777217_306(self, packet_vars, avps): #Experimental Result AVP avp_experimental_result = '' avp_experimental_result += self.generate_vendor_avp(266, 40, 10415, '') #AVP Vendor ID - avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(result_code, 4)) #AVP Experimental-Result-Code + avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(result_code, 4), avps=avps, packet_vars=packet_vars) #AVP Experimental-Result-Code avp += self.generate_avp(297, 40, avp_experimental_result) #AVP Experimental-Result(297) response = self.generate_diameter_packet("01", "40", 306, 16777217, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet return response @@ -1566,7 +1594,7 @@ def Answer_16777217_306(self, packet_vars, avps): xmlbody = template.render(Sh_template_vars=subscriber_details) # this is where to put args to the template renderer avp += self.generate_vendor_avp(702, "c0", 10415, str(binascii.hexlify(str.encode(xmlbody)),'ascii')) - avp += self.generate_avp(268, 40, "000007d1") #DIAMETER_SUCCESS + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) response = self.generate_diameter_packet("01", "40", 306, 16777217, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet @@ -1637,8 +1665,8 @@ def Answer_16777252_324(self, packet_vars, avps): #Experimental Result AVP(Response Code for Failure) avp_experimental_result = '' avp_experimental_result += self.generate_vendor_avp(266, 'c0', 10415, '') #AVP Vendor ID - avp_experimental_result += self.generate_avp(298, 'c0', self.int_to_hex(2001, 4)) #AVP Experimental-Result-Code: SUCESS (2001) - avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4)) #Result Code (DIAMETER_SUCCESS (2001)) + avp_experimental_result += self.generate_avp(298, 'c0', self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #AVP Experimental-Result-Code: SUCESS (2001) + avp += self.generate_avp(268, 40, self.int_to_hex(2001, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) #Equipment-Status EquipmentStatus = database.Check_EIR(imsi=imsi, imei=imei) @@ -1710,7 +1738,7 @@ def Answer_16777291_8388622(self, packet_vars, avps): DiameterLogger.error("No MSISDN or IMSI returned in Answer_16777291_8388622 input") DiameterLogger.error("Error is " + str(E)) DiameterLogger.error("Responding with DIAMETER_ERROR_USER_UNKNOWN") - avp += self.generate_avp(268, 40, self.int_to_hex(5030, 4)) + avp += self.generate_avp(268, 40, self.int_to_hex(5030, 4), avps=avps, packet_vars=packet_vars) #Result Code (DIAMETER_SUCCESS (2001)) response = self.generate_diameter_packet("01", "40", 8388622, 16777291, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet DiameterLogger.info("Diameter user unknown - Sending ULA with DIAMETER_ERROR_USER_UNKNOWN") return response @@ -1730,7 +1758,7 @@ def Answer_16777291_8388622(self, packet_vars, avps): avp_experimental_result = '' avp_experimental_result += self.generate_vendor_avp(266, 40, 10415, '') #AVP Vendor ID - avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(result_code, 4)) #AVP Experimental-Result-Code + avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(result_code, 4), avps=avps, packet_vars=packet_vars) #AVP Experimental-Result-Code avp += self.generate_avp(297, 40, avp_experimental_result) #AVP Experimental-Result(297) response = self.generate_diameter_packet("01", "40", 8388622, 16777291, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet @@ -1747,7 +1775,7 @@ def Answer_16777291_8388622(self, packet_vars, avps): #Set Result-Code result_code = 2001 #Diameter Success - avp += self.generate_avp(268, 40, self.int_to_hex(result_code, 4)) #Result Code - DIAMETER_SUCCESS + avp += self.generate_avp(268, 40, self.int_to_hex(result_code, 4), avps=avps, packet_vars=packet_vars) #Result Code response = self.generate_diameter_packet("01", "40", 8388622, 16777291, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet return response diff --git a/lib/logtool.py b/lib/logtool.py index ac341a2..7fff24b 100644 --- a/lib/logtool.py +++ b/lib/logtool.py @@ -50,6 +50,7 @@ prom_http_geored = Counter('prom_http_geored', 'Number of Geored Pushes', ['geored_host', 'endpoint', 'http_response_code', 'error']) prom_flask_http_geored_endpoints = Counter('prom_flask_http_geored_endpoints', 'Number of Geored Pushes Received', ['geored_host', 'endpoint']) +prom_diam_result_code = Counter('prom_diam_result_code', 'Prometheus Result Codes', ['result_code', 'diameter_application_id', 'diameter_cmd_code', 'endpoint', 'imsi']) prom_pcrf_subs = Gauge('prom_pcrf_subs', 'Number of attached PCRF Subscribers') prom_mme_subs = Gauge('prom_mme_subs', 'Number of attached MME Subscribers') From 4d6e8236859e5d70e7757a75864e5709a0f8af69 Mon Sep 17 00:00:00 2001 From: nickvsnetworking Date: Sun, 20 Aug 2023 15:48:37 +1000 Subject: [PATCH 02/11] Ignore DWR in Response Count --- diameter.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/diameter.py b/diameter.py index e757425..18b511d 100644 --- a/diameter.py +++ b/diameter.py @@ -226,9 +226,7 @@ def __init__(self, OriginHost, OriginRealm, ProductName, MNC, MCC): #Generates an AVP with inputs provided (AVP Code, AVP Flags, AVP Content, Padding) #AVP content must already be in HEX - This can be done with binascii.hexlify(avp_content.encode()) def generate_avp(self, avp_code, avp_flags, avp_content, avps=None, packet_vars=None): - if avp_code == 268 or avp_code == 298: - - + if avp_code == 268 or avp_code == 298 and packet_vars['command_code'] != 280: try: DiameterLogger.debug("Incrementing Prometheus Stats for prom_diam_result_code") try: From 2d4de73b5b5d29762a1b2f27f582041643706d9e Mon Sep 17 00:00:00 2001 From: nickvsnetworking Date: Sun, 20 Aug 2023 20:29:21 +1000 Subject: [PATCH 03/11] Stop printing packet hex --- diameter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/diameter.py b/diameter.py index 18b511d..ae1823a 100644 --- a/diameter.py +++ b/diameter.py @@ -312,7 +312,6 @@ def generate_diameter_packet(self, packet_version, packet_flags, packet_command_ return packet_hex def decode_diameter_packet(self, data): - print(data) packet_vars = {} avps = [] From ccaa789b9f3a161336de5bceb6d89836bf199fc4 Mon Sep 17 00:00:00 2001 From: nickvsnetworking Date: Mon, 21 Aug 2023 08:32:13 +1000 Subject: [PATCH 04/11] Possible fix to crashes in CCR-I --- database.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database.py b/database.py index b4773a5..8029efd 100755 --- a/database.py +++ b/database.py @@ -1211,6 +1211,7 @@ def CreateObj(obj_type, json_data, disable_logging=False, operation_id=None): raise ValueError(E) session.refresh(newObj) result = newObj.__dict__ + DBLogger.debug("Created new object OK") result.pop('_sa_instance_state') handle_external_webhook(result, 'CREATE') return result @@ -1776,7 +1777,7 @@ def Update_Serving_APN(imsi, apn, pcrf_session_id, serving_pgw, subscriber_routi except Exception as E: DBLogger.info("Failed to update existing APN " + str(E)) #Create if does not exist - CreateObj(SERVING_APN, json_data, True) + ServingAPN = CreateObj(SERVING_APN, json_data, True) objectData = GetObj(SERVING_APN, ServingAPN['serving_apn_id']) handle_external_webhook(objectData, 'CREATE') From e2633430d78dea2508d14de1bc025b0290fc4ead Mon Sep 17 00:00:00 2001 From: davidkneipp Date: Mon, 21 Aug 2023 11:24:09 +1000 Subject: [PATCH 05/11] Fix for Get_Serving_Apn --- database.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/database.py b/database.py index b4773a5..806bf62 100755 --- a/database.py +++ b/database.py @@ -1759,6 +1759,7 @@ def Update_Serving_APN(imsi, apn, pcrf_session_id, serving_pgw, subscriber_routi #Check if already a serving APN on record DBLogger.debug("Checking to see if subscriber id " + str(subscriber_id) + " already has an active PCRF profile on APN id " + str(apn_id)) ServingAPN = Get_Serving_APN(subscriber_id=subscriber_id, apn_id=apn_id) + assert(ServingAPN is not None) DBLogger.debug("Existing Serving APN ID on record, updating") try: assert(type(serving_pgw) == str) @@ -1808,15 +1809,18 @@ def Get_Serving_APN(subscriber_id, apn_id): try: result = session.query(SERVING_APN).filter_by(subscriber_id=subscriber_id, apn=apn_id).first() + if result is None: + return result + result = result.__dict__ + result.pop('_sa_instance_state') + except Exception as E: DBLogger.debug(E) safe_close(session) raise ValueError(E) - result = result.__dict__ - result.pop('_sa_instance_state') safe_close(session) - return result + return result def Get_Charging_Rule(charging_rule_id): DBLogger.debug("Called Get_Charging_Rule() for charging_rule_id " + str(charging_rule_id)) From 9f1bd55f9e22cbffed824fce354ce446cf61d9c0 Mon Sep 17 00:00:00 2001 From: nickvsnetworking Date: Sun, 3 Sep 2023 10:47:24 +1000 Subject: [PATCH 06/11] Handle toggle user enabled / disabled via API --- PyHSS_API.py | 25 +++++++++++++++++++++++++ database.py | 10 +++++----- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/PyHSS_API.py b/PyHSS_API.py index a2a7d63..3d54ff4 100644 --- a/PyHSS_API.py +++ b/PyHSS_API.py @@ -438,6 +438,31 @@ def patch(self, subscriber_id): operation_id = args.get('operation_id', None) data = database.UpdateObj(SUBSCRIBER, json_data, subscriber_id, False, operation_id) + + #If the "enabled" flag on the subscriber is now disabled, trigger a CLR + if 'enabled' in json_data and json_data['enabled'] == False: + print("Subscriber is now disabled, checking to see if we need to trigger a CLR") + #See if we have a serving MME set + if json_data['serving_mme']: + print("Serving MME set - Sending CLR") + import diameter + diameter = diameter.Diameter( + OriginHost=yaml_config['hss']['OriginHost'], + OriginRealm=yaml_config['hss']['OriginRealm'], + MNC=yaml_config['hss']['MNC'], + MCC=yaml_config['hss']['MCC'], + ProductName='PyHSS-API-Disabled-CLR' + ) + diam_hex = diameter.Request_16777251_317( + imsi=json_data['imsi'], + DestinationHost=json_data['serving_mme'], + DestinationRealm=json_data['serving_mme_realm'], + CancellationType=1 + ) + logObj = logtool.LogTool() + logObj.Async_SendRequest(diam_hex, str(json_data['serving_mme_peer'].split(';')[1])) + else: + print("No serving MME set - Not sending CLR") print("Updated object") print(data) return data, 200 diff --git a/database.py b/database.py index ee24368..c62853e 100755 --- a/database.py +++ b/database.py @@ -1325,6 +1325,11 @@ def Get_Subscriber(**kwargs): result = Sanitize_Datetime(result) result.pop('_sa_instance_state') + #If subscriber enabled is set to false then return error + if result['enabled'] == False: + safe_close(session) + raise ValueError("Subscriber is disabled") + if 'get_attributes' in kwargs: if kwargs['get_attributes'] == True: attributes = Get_Subscriber_Attributes(result['subscriber_id']) @@ -1375,7 +1380,6 @@ def Get_Subscriber_Attributes(subscriber_id): safe_close(session) return final_res - def Get_Served_Subscribers(get_local_users_only=False): DBLogger.debug("Getting all subscribers served by this HSS") @@ -1419,7 +1423,6 @@ def Get_Served_Subscribers(get_local_users_only=False): safe_close(session) return Served_Subs - def Get_Served_IMS_Subscribers(get_local_users_only=False): DBLogger.debug("Getting all subscribers served by this IMS-HSS") Session = sessionmaker(bind=engine) @@ -1463,7 +1466,6 @@ def Get_Served_IMS_Subscribers(get_local_users_only=False): safe_close(session) return Served_Subs - def Get_Served_PCRF_Subscribers(get_local_users_only=False): DBLogger.debug("Getting all subscribers served by this PCRF") Session = sessionmaker(bind=engine) @@ -1668,7 +1670,6 @@ def Update_Serving_MME(imsi, serving_mme, serving_mme_realm=None, serving_mme_pe finally: safe_close(session) - def Update_Serving_CSCF(imsi, serving_cscf, scscf_realm=None, scscf_peer=None, propagate=True): DBLogger.debug("Update_Serving_CSCF for sub " + str(imsi) + " to SCSCF " + str(serving_cscf) + " with realm " + str(scscf_realm) + " and peer " + str(scscf_peer)) Session = sessionmaker(bind = engine) @@ -1712,7 +1713,6 @@ def Update_Serving_CSCF(imsi, serving_cscf, scscf_realm=None, scscf_peer=None, p finally: safe_close(session) - def Update_Serving_APN(imsi, apn, pcrf_session_id, serving_pgw, subscriber_routing, serving_pgw_realm=None, serving_pgw_peer=None, propagate=True): DBLogger.debug("Called Update_Serving_APN() for imsi " + str(imsi) + " with APN " + str(apn)) DBLogger.debug("PCRF Session ID " + str(pcrf_session_id) + " and serving PGW " + str(serving_pgw) + " and subscriber routing " + str(subscriber_routing)) From d73a08206be67d8827e7c1ed17e5d66eea4b3e00 Mon Sep 17 00:00:00 2001 From: nickvsnetworking Date: Sun, 3 Sep 2023 11:07:32 +1000 Subject: [PATCH 07/11] Print more to log --- PyHSS_API.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PyHSS_API.py b/PyHSS_API.py index 3d54ff4..a5fb353 100644 --- a/PyHSS_API.py +++ b/PyHSS_API.py @@ -460,7 +460,9 @@ def patch(self, subscriber_id): CancellationType=1 ) logObj = logtool.LogTool() - logObj.Async_SendRequest(diam_hex, str(json_data['serving_mme_peer'].split(';')[1])) + serving_hss = str(json_data['serving_mme_peer'].split(';')[1] + logObj.Async_SendRequest(diam_hex, serving_hss)) + print("Sent CLR via Peer " + str(serving_hss)) else: print("No serving MME set - Not sending CLR") print("Updated object") From 5fed6ea109e40eb704642f530f68065f7c93c96f Mon Sep 17 00:00:00 2001 From: nickvsnetworking Date: Sun, 3 Sep 2023 11:15:57 +1000 Subject: [PATCH 08/11] Updated routing for CLR --- PyHSS_API.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PyHSS_API.py b/PyHSS_API.py index a5fb353..76c9dfa 100644 --- a/PyHSS_API.py +++ b/PyHSS_API.py @@ -460,9 +460,9 @@ def patch(self, subscriber_id): CancellationType=1 ) logObj = logtool.LogTool() - serving_hss = str(json_data['serving_mme_peer'].split(';')[1] - logObj.Async_SendRequest(diam_hex, serving_hss)) - print("Sent CLR via Peer " + str(serving_hss)) + diameter_next_hop = str(json_data['serving_mme_peer'].split(';')[0] + logObj.Async_SendRequest(diam_hex, diameter_next_hop)) + print("Sent CLR via Peer " + str(diameter_next_hop)) else: print("No serving MME set - Not sending CLR") print("Updated object") From f08dad09945e95ee32332ec41d3f4432c20a2004 Mon Sep 17 00:00:00 2001 From: nickvsnetworking Date: Sun, 3 Sep 2023 11:21:53 +1000 Subject: [PATCH 09/11] Fix invalid syntax --- PyHSS_API.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PyHSS_API.py b/PyHSS_API.py index 76c9dfa..0e5e1ae 100644 --- a/PyHSS_API.py +++ b/PyHSS_API.py @@ -460,8 +460,8 @@ def patch(self, subscriber_id): CancellationType=1 ) logObj = logtool.LogTool() - diameter_next_hop = str(json_data['serving_mme_peer'].split(';')[0] - logObj.Async_SendRequest(diam_hex, diameter_next_hop)) + diameter_next_hop = str(json_data['serving_mme_peer']).split(';')[0] + logObj.Async_SendRequest(diam_hex, diameter_next_hop) print("Sent CLR via Peer " + str(diameter_next_hop)) else: print("No serving MME set - Not sending CLR") From 9e376f2b7544d7fc43b519dbb7bb69b1f5054ea3 Mon Sep 17 00:00:00 2001 From: nickvsnetworking Date: Wed, 6 Sep 2023 08:59:01 +1000 Subject: [PATCH 10/11] Better handle disabled subs --- database.py | 5 ----- diameter.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/database.py b/database.py index c62853e..cf78d87 100755 --- a/database.py +++ b/database.py @@ -1325,11 +1325,6 @@ def Get_Subscriber(**kwargs): result = Sanitize_Datetime(result) result.pop('_sa_instance_state') - #If subscriber enabled is set to false then return error - if result['enabled'] == False: - safe_close(session) - raise ValueError("Subscriber is disabled") - if 'get_attributes' in kwargs: if kwargs['get_attributes'] == True: attributes = Get_Subscriber_Attributes(result['subscriber_id']) diff --git a/diameter.py b/diameter.py index ae1823a..67d8573 100644 --- a/diameter.py +++ b/diameter.py @@ -634,6 +634,23 @@ def Answer_16777251_316(self, packet_vars, avps): try: subscriber_details = database.Get_Subscriber(imsi=imsi) #Get subscriber details DiameterLogger.debug("Got back subscriber_details: " + str(subscriber_details)) + + if subscriber_details['enabled'] == 0: + DiameterLogger.info("Subscriber is disabled") + + + + #Experimental Result AVP(Response Code for Failure) + avp_experimental_result = '' + avp_experimental_result += self.generate_vendor_avp(266, 40, 10415, '') #AVP Vendor ID + avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(5001, 4), avps=avps, packet_vars=packet_vars) #AVP Experimental-Result-Code: DIAMETER_ERROR_USER_UNKNOWN (5001) + avp += self.generate_avp(297, 40, avp_experimental_result) #AVP Experimental-Result(297) + + avp += self.generate_avp(277, 40, "00000001") #Auth-Session-State + DiameterLogger.debug("Successfully Generated ULA for disabled Sub") + response = self.generate_diameter_packet("01", "40", 316, 16777251, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) + return response + except ValueError as e: DiameterLogger.error("failed to get data backfrom database for imsi " + str(imsi)) DiameterLogger.error("Error is " + str(e)) @@ -826,9 +843,37 @@ def Answer_16777251_318(self, packet_vars, avps): imsi = self.get_avp_data(avps, 1)[0] #Get IMSI from User-Name AVP in request imsi = binascii.unhexlify(imsi).decode('utf-8') #Convert IMSI plmn = self.get_avp_data(avps, 1407)[0] #Get PLMN from User-Name AVP in request - + avp = '' try: subscriber_details = database.Get_Subscriber(imsi=imsi) #Get subscriber details + + if subscriber_details['enabled'] == 0: + DiameterLogger.info("Subscriber is disabled") + avp += self.generate_avp(268, 40, self.int_to_hex(5001, 4), avps=avps, packet_vars=packet_vars) #Result Code + prom_diam_auth_event_count.labels( + diameter_application_id = 16777251, + diameter_cmd_code = 318, + event='Disabled User', + imsi_prefix = str(imsi[0:6]), + ).inc() + session_id = self.get_avp_data(avps, 263)[0] #Get Session-ID + avp += self.generate_avp(263, 40, session_id) #Session-ID AVP set + avp += self.generate_avp(264, 40, self.OriginHost) #Origin Host + avp += self.generate_avp(296, 40, self.OriginRealm) #Origin Realm + + #Experimental Result AVP(Response Code for Failure) + avp_experimental_result = '' + avp_experimental_result += self.generate_vendor_avp(266, 40, 10415, '') #AVP Vendor ID + avp_experimental_result += self.generate_avp(298, 40, self.int_to_hex(5001, 4), avps=avps, packet_vars=packet_vars) #AVP Experimental-Result-Code: DIAMETER_ERROR_USER_UNKNOWN (5001) + avp += self.generate_avp(297, 40, avp_experimental_result) #AVP Experimental-Result(297) + + avp += self.generate_avp(277, 40, "00000001") #Auth-Session-State + avp += self.generate_avp(260, 40, "000001024000000c" + format(int(16777251),"x").zfill(8) + "0000010a4000000c000028af") #Vendor-Specific-Application-ID (S6a) + response = self.generate_diameter_packet("01", "40", 318, 16777251, packet_vars['hop-by-hop-identifier'], packet_vars['end-to-end-identifier'], avp) #Generate Diameter packet + DiameterLogger.debug("Successfully Generated AIA for disabled Sub") + DiameterLogger.debug(response) + return response + except ValueError as e: DiameterLogger.info("Minor getting subscriber details for IMSI " + str(imsi)) DiameterLogger.info(e) @@ -841,7 +886,6 @@ def Answer_16777251_318(self, packet_vars, avps): ).inc() DiameterLogger.info("Subscriber " + str(imsi) + " is unknown in database") - avp = '' session_id = self.get_avp_data(avps, 263)[0] #Get Session-ID avp += self.generate_avp(263, 40, session_id) #Session-ID AVP set avp += self.generate_avp(264, 40, self.OriginHost) #Origin Host @@ -1032,7 +1076,6 @@ def Answer_16777238_272(self, packet_vars, avps): DiameterLogger.debug(E) DiameterLogger.debug("Subscriber " + str(imsi) + " unknown in HSS for CCR - Check Charging Rule assigned to APN is set and exists") - if int(CC_Request_Type) == 1: DiameterLogger.info("Request type for CCA is 1 - Initial") From 267176057aa3da2445db40796d04eb08987c5196 Mon Sep 17 00:00:00 2001 From: nickvsnetworking Date: Thu, 7 Sep 2023 07:38:19 +1000 Subject: [PATCH 11/11] Error handling on update API sub to disabled --- PyHSS_API.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PyHSS_API.py b/PyHSS_API.py index 0e5e1ae..0e74941 100644 --- a/PyHSS_API.py +++ b/PyHSS_API.py @@ -443,7 +443,8 @@ def patch(self, subscriber_id): if 'enabled' in json_data and json_data['enabled'] == False: print("Subscriber is now disabled, checking to see if we need to trigger a CLR") #See if we have a serving MME set - if json_data['serving_mme']: + try: + assert(json_data['serving_mme']) print("Serving MME set - Sending CLR") import diameter diameter = diameter.Diameter( @@ -463,7 +464,7 @@ def patch(self, subscriber_id): diameter_next_hop = str(json_data['serving_mme_peer']).split(';')[0] logObj.Async_SendRequest(diam_hex, diameter_next_hop) print("Sent CLR via Peer " + str(diameter_next_hop)) - else: + except: print("No serving MME set - Not sending CLR") print("Updated object") print(data)