Browse Source

siputils: bug fix for sip_p_charging_vector

- Bug fix for #3929 and further improvements.
- New buffer usage for P-Charging-Vector handling.
- Added $pcv(status) pseudo-var.
- Added return values for sip_p_charging_vector().
- Much improved error case handling.
- Use send interface for icid-genearted-at:
  The icid-generated-at parameter should carry the ip adress of the
  egress interface and not of the interface where the message was received.
  This is in accordance with other SBC solutions.

(cherry picked from commit d7c8bc5c58f672004894733d0e6d2a7cb00efe7a)
Eik Rentzow 11 months ago
parent
commit
8c3eb992e0
1 changed files with 246 additions and 154 deletions
  1. 246 154
      src/modules/siputils/chargingvector.c

+ 246 - 154
src/modules/siputils/chargingvector.c

@@ -31,29 +31,48 @@
 
 
 #define SIZE_CONF_ID 16
 #define SIZE_CONF_ID 16
 #define P_CHARGING_VECTOR "P-Charging-Vector"
 #define P_CHARGING_VECTOR "P-Charging-Vector"
+#define P_CHARGING_VECTOR_HEADERNAME_LEN (sizeof(P_CHARGING_VECTOR) - 1)
+#define P_CHARGING_VECTOR_PREFIX_LEN (P_CHARGING_VECTOR_HEADERNAME_LEN + 2)
 #define LOOPBACK_IP 16777343
 #define LOOPBACK_IP 16777343
 
 
 #define PCV_BUF_SIZE 256
 #define PCV_BUF_SIZE 256
 static char _siputils_pcv_buf[PCV_BUF_SIZE];
 static char _siputils_pcv_buf[PCV_BUF_SIZE];
 static str _siputils_pcv = {_siputils_pcv_buf, 0};
 static str _siputils_pcv = {_siputils_pcv_buf, 0};
-static str _siputils_pcv_id = {NULL, 0};
-static str _siputils_pcv_host = {NULL, 0};
-static str _siputils_pcv_orig = {NULL, 0};
-static str _siputils_pcv_term = {NULL, 0};
+static str _siputils_pcv_id = STR_NULL;
+static str _siputils_pcv_genaddr = STR_NULL;
+static str _siputils_pcv_orig = STR_NULL;
+static str _siputils_pcv_term = STR_NULL;
 static uint64_t _siputils_pcv_counter = 0;
 static uint64_t _siputils_pcv_counter = 0;
+static struct hdr_field *_siputils_pcv_hf_pcv = NULL;
 
 
 
 
 enum PCV_Status
 enum PCV_Status
 {
 {
 	PCV_NONE = 0,
 	PCV_NONE = 0,
 	PCV_PARSED = 1,
 	PCV_PARSED = 1,
-	PCV_GENERATED = 2
+	PCV_ICID_MISSING = 2,
+	PCV_GENERATED = 3,
+	PCV_DELETED = 4,
+	PCV_REPLACED = 5,
+	PCV_NOP = PCV_PARSED
+};
+static const char *sstatus[] = {
+		"NONE", "PARSED", "ICID_MISSING", "GENERATED", "DELETED", "REPLACED"};
+
+enum PCV_Parameter
+{
+	PCV_PARAM_ALL = 1,
+	PCV_PARAM_GENADDR = 2,
+	PCV_PARAM_ID = 3,
+	PCV_PARAM_ORIG = 4,
+	PCV_PARAM_TERM = 5,
+	PCV_PARAM_STATUS = 6
 };
 };
 
 
 static enum PCV_Status _siputils_pcv_status = PCV_NONE;
 static enum PCV_Status _siputils_pcv_status = PCV_NONE;
 static unsigned int _siputils_pcv_current_msg_id = (unsigned int)-1;
 static unsigned int _siputils_pcv_current_msg_id = (unsigned int)-1;
 
 
-static void sip_generate_charging_vector(char *pcv)
+static void sip_generate_charging_vector(char *pcv, const unsigned int maxsize)
 {
 {
 	char s[PATH_MAX] = {0};
 	char s[PATH_MAX] = {0};
 	struct hostent *host = NULL;
 	struct hostent *host = NULL;
@@ -66,8 +85,15 @@ static void sip_generate_charging_vector(char *pcv)
 	struct in_addr *in = NULL;
 	struct in_addr *in = NULL;
 	static struct in_addr ip = {0};
 	static struct in_addr ip = {0};
 	unsigned char newConferenceIdentifier[SIZE_CONF_ID] = {0};
 	unsigned char newConferenceIdentifier[SIZE_CONF_ID] = {0};
+	int len = SIZE_CONF_ID;
+
+	/* if supplied buffer cannot carry 16 (SIZE_CONF_ID) hex characters and a null
+		terminator (=33 bytes), then reduce length of generated icid-value */
+	if(maxsize < (len * 2 + 1)) {
+		LM_WARN("generator buffer too small for new pcv icid-value!\n");
+		len = (maxsize - 1) / 2;
+	}
 
 
-	memset(pcv, 0, SIZE_CONF_ID);
 	pid = getpid();
 	pid = getpid();
 
 
 	if(ip.s_addr == 0) {
 	if(ip.s_addr == 0) {
@@ -117,19 +143,18 @@ static void sip_generate_charging_vector(char *pcv)
 		idx++;
 		idx++;
 	}
 	}
 	LM_DBG("PCV generate\n");
 	LM_DBG("PCV generate\n");
-	int i = 0;
-	pcv[0] = '\0';
-	for(i = 0; i < SIZE_CONF_ID; i++) {
-		char hex[4] = {0};
+	char *ptr = pcv;
+	const char *endptr = ptr + maxsize - 1;
 
 
-		snprintf(hex, 4, "%02X", newConferenceIdentifier[i]);
-		strcat(pcv, hex);
+	for(int i = 0; i < len && ptr < endptr; i++) {
+		ptr += (snprintf(ptr, 3, "%02X", newConferenceIdentifier[i]) - 1);
 	}
 	}
 }
 }
 
 
-static unsigned int sip_param_end(const char *s, unsigned int len)
+static unsigned int sip_param_end(const char *s, const char *end)
 {
 {
 	unsigned int i;
 	unsigned int i;
+	unsigned int len = end - s;
 
 
 	for(i = 0; i < len; i++) {
 	for(i = 0; i < len; i++) {
 		if(s[i] == '\0' || s[i] == ' ' || s[i] == ';' || s[i] == ','
 		if(s[i] == '\0' || s[i] == ' ' || s[i] == ';' || s[i] == ','
@@ -140,61 +165,78 @@ static unsigned int sip_param_end(const char *s, unsigned int len)
 	return len;
 	return len;
 }
 }
 
 
+
+static inline void sip_initialize_parse_buffers()
+{
+	_siputils_pcv_id = (str)STR_NULL;
+	_siputils_pcv_genaddr = (str)STR_NULL;
+	_siputils_pcv_orig = (str)STR_NULL;
+	_siputils_pcv_term = (str)STR_NULL;
+}
+
+static inline void sip_initialize_pcv_buffers()
+{
+	memset(_siputils_pcv.s, 0, sizeof(_siputils_pcv_buf));
+	_siputils_pcv.len = 0;
+	sip_initialize_parse_buffers();
+}
+
 static int sip_parse_charging_vector(const char *pcv_value, unsigned int len)
 static int sip_parse_charging_vector(const char *pcv_value, unsigned int len)
 {
 {
 	/* now point to each PCV component */
 	/* now point to each PCV component */
-	LM_DBG("parsing PCV header [%s]\n", pcv_value);
+	LM_DBG("parsing PCV header [%.*s]\n", len, pcv_value);
 
 
 	char *s = NULL;
 	char *s = NULL;
+	const char *pcv_value_end = pcv_value + len;
 
 
 	s = strstr(pcv_value, "icid-value=");
 	s = strstr(pcv_value, "icid-value=");
 	if(s != NULL) {
 	if(s != NULL) {
 		_siputils_pcv_id.s = s + strlen("icid-value=");
 		_siputils_pcv_id.s = s + strlen("icid-value=");
-		_siputils_pcv_id.len = sip_param_end(_siputils_pcv_id.s, len);
+		_siputils_pcv_id.len = sip_param_end(_siputils_pcv_id.s, pcv_value_end);
 		LM_DBG("parsed P-Charging-Vector icid-value=%.*s\n",
 		LM_DBG("parsed P-Charging-Vector icid-value=%.*s\n",
-				_siputils_pcv_id.len, _siputils_pcv_id.s);
+				STR_FMT(&_siputils_pcv_id));
 	} else {
 	} else {
 		LM_WARN("mandatory icid-value not found\n");
 		LM_WARN("mandatory icid-value not found\n");
-		_siputils_pcv_id.s = NULL;
-		_siputils_pcv_id.len = 0;
+		_siputils_pcv_id = (str)STR_NULL;
 	}
 	}
 
 
 	s = strstr(pcv_value, "icid-generated-at=");
 	s = strstr(pcv_value, "icid-generated-at=");
 	if(s != NULL) {
 	if(s != NULL) {
-		_siputils_pcv_host.s = s + strlen("icid-generated-at=");
-		_siputils_pcv_host.len = sip_param_end(_siputils_pcv_host.s, len);
+		_siputils_pcv_genaddr.s = s + strlen("icid-generated-at=");
+		_siputils_pcv_genaddr.len =
+				sip_param_end(_siputils_pcv_genaddr.s, pcv_value_end);
 		LM_DBG("parsed P-Charging-Vector icid-generated-at=%.*s\n",
 		LM_DBG("parsed P-Charging-Vector icid-generated-at=%.*s\n",
-				_siputils_pcv_host.len, _siputils_pcv_host.s);
+				STR_FMT(&_siputils_pcv_genaddr));
 	} else {
 	} else {
 		LM_DBG("icid-generated-at not found\n");
 		LM_DBG("icid-generated-at not found\n");
-		_siputils_pcv_host.s = NULL;
-		_siputils_pcv_host.len = 0;
+		_siputils_pcv_genaddr = (str)STR_NULL;
 	}
 	}
 
 
 	s = strstr(pcv_value, "orig-ioi=");
 	s = strstr(pcv_value, "orig-ioi=");
 	if(s != NULL) {
 	if(s != NULL) {
 		_siputils_pcv_orig.s = s + strlen("orig-ioi=");
 		_siputils_pcv_orig.s = s + strlen("orig-ioi=");
-		_siputils_pcv_orig.len = sip_param_end(_siputils_pcv_orig.s, len);
+		_siputils_pcv_orig.len =
+				sip_param_end(_siputils_pcv_orig.s, pcv_value_end);
 		LM_INFO("parsed P-Charging-Vector orig-ioi=%.*s\n",
 		LM_INFO("parsed P-Charging-Vector orig-ioi=%.*s\n",
-				_siputils_pcv_orig.len, _siputils_pcv_orig.s);
+				STR_FMT(&_siputils_pcv_orig));
 	} else {
 	} else {
-		_siputils_pcv_orig.s = NULL;
-		_siputils_pcv_orig.len = 0;
+		_siputils_pcv_orig = (str)STR_NULL;
 	}
 	}
 
 
 	s = strstr(pcv_value, "term-ioi=");
 	s = strstr(pcv_value, "term-ioi=");
 	if(s != NULL) {
 	if(s != NULL) {
 		_siputils_pcv_term.s = s + strlen("term-ioi=");
 		_siputils_pcv_term.s = s + strlen("term-ioi=");
-		_siputils_pcv_term.len = sip_param_end(_siputils_pcv_term.s, len);
+		_siputils_pcv_term.len =
+				sip_param_end(_siputils_pcv_term.s, pcv_value_end);
 		LM_INFO("parsed P-Charging-Vector term-ioi=%.*s\n",
 		LM_INFO("parsed P-Charging-Vector term-ioi=%.*s\n",
-				_siputils_pcv_term.len, _siputils_pcv_term.s);
+				STR_FMT(&_siputils_pcv_term));
 	} else {
 	} else {
-		_siputils_pcv_term.s = NULL;
-		_siputils_pcv_term.len = 0;
+		_siputils_pcv_term = (str)STR_NULL;
 	}
 	}
 
 
 	// only icid-value is mandatory, log anyway when missing icid-generated-at
 	// only icid-value is mandatory, log anyway when missing icid-generated-at
-	if(_siputils_pcv_host.s == NULL && _siputils_pcv_id.s != NULL && len > 0) {
+	if(_siputils_pcv_genaddr.s == NULL && _siputils_pcv_id.s != NULL
+			&& len > 0) {
 		LM_WARN("icid-generated-at is missing %.*s\n", len, pcv_value);
 		LM_WARN("icid-generated-at is missing %.*s\n", len, pcv_value);
 	}
 	}
 
 
@@ -205,8 +247,9 @@ static int sip_get_charging_vector(
 		struct sip_msg *msg, struct hdr_field **hf_pcv)
 		struct sip_msg *msg, struct hdr_field **hf_pcv)
 {
 {
 	struct hdr_field *hf;
 	struct hdr_field *hf;
-	char *hdrname_cstr = P_CHARGING_VECTOR;
-	str hdrname = {hdrname_cstr, strlen(hdrname_cstr)};
+	int hf_body_len;
+
+	str hdrname = STR_STATIC_INIT(P_CHARGING_VECTOR);
 
 
 	/* we need to be sure we have parsed all headers */
 	/* we need to be sure we have parsed all headers */
 	if(parse_headers(msg, HDR_EOH_F, 0) < 0) {
 	if(parse_headers(msg, HDR_EOH_F, 0) < 0) {
@@ -214,52 +257,55 @@ static int sip_get_charging_vector(
 		return -1;
 		return -1;
 	}
 	}
 
 
+	sip_initialize_pcv_buffers();
+
 	for(hf = msg->headers; hf; hf = hf->next) {
 	for(hf = msg->headers; hf; hf = hf->next) {
 		if(hf->name.s[0] != 'P') {
 		if(hf->name.s[0] != 'P') {
 			continue;
 			continue;
 		}
 		}
 
 
 		if(cmp_hdrname_str(&hf->name, &hdrname) == 0) {
 		if(cmp_hdrname_str(&hf->name, &hdrname) == 0) {
-			/*
-			 * append p charging vector values after the header name "P-Charging-Vector" and
-			 * the ": " (+2)
-			 */
-			char *pcv_body = _siputils_pcv_buf + strlen(P_CHARGING_VECTOR) + 2;
+
+			char *pcv_body = _siputils_pcv_buf;
+			*hf_pcv = hf;
 
 
 			if(hf->body.len > 0) {
 			if(hf->body.len > 0) {
-				memcpy(pcv_body, hf->body.s, hf->body.len);
-				_siputils_pcv.len =
-						hf->body.len + strlen(P_CHARGING_VECTOR) + 2;
-				pcv_body[hf->body.len] = '\0';
-				if(sip_parse_charging_vector(pcv_body, hf->body.len) == 0) {
+				if(hf->body.len > sizeof(_siputils_pcv_buf)) {
+					LM_WARN("received charging vector header is longer than "
+							"reserved buffer - truncating.");
+					hf_body_len = sizeof(_siputils_pcv_buf);
+				} else
+					hf_body_len = hf->body.len;
+
+				memcpy(pcv_body, hf->body.s, hf_body_len);
+				_siputils_pcv.len = hf_body_len;
+
+				if(sip_parse_charging_vector(pcv_body, hf_body_len) == 0) {
 					LM_ERR("P-Charging-Vector header found but failed to parse "
 					LM_ERR("P-Charging-Vector header found but failed to parse "
 						   "value [%s].\n",
 						   "value [%s].\n",
 							pcv_body);
 							pcv_body);
-					_siputils_pcv_status = PCV_NONE;
-					_siputils_pcv.s = NULL;
-					_siputils_pcv.len = 0;
+					_siputils_pcv_status = PCV_ICID_MISSING;
+					sip_initialize_parse_buffers();
 				} else {
 				} else {
 					_siputils_pcv_status = PCV_PARSED;
 					_siputils_pcv_status = PCV_PARSED;
-					_siputils_pcv.s = hf->body.s;
-					_siputils_pcv.len = hf->body.len;
 				}
 				}
 				return 2;
 				return 2;
 			} else {
 			} else {
-				_siputils_pcv_id.s = 0;
-				_siputils_pcv_id.len = 0;
-				_siputils_pcv_host.s = 0;
-				_siputils_pcv_host.len = 0;
-				LM_WARN("P-Charging-Vector header found but no value.\n");
+				LM_WARN("P-Charging-Vector header found but has no body.\n");
+				_siputils_pcv_status = PCV_ICID_MISSING;
+				return 1;
 			}
 			}
-			*hf_pcv = hf;
 		}
 		}
 	}
 	}
 	LM_DBG("No valid P-Charging-Vector header found.\n");
 	LM_DBG("No valid P-Charging-Vector header found.\n");
+	_siputils_pcv_status = PCV_NONE;
+	*hf_pcv = NULL;
 	return 1;
 	return 1;
 }
 }
 
 
 // Remove PCV if it is in the inbound request (if it was found by sip_get_charging_vector)
 // Remove PCV if it is in the inbound request (if it was found by sip_get_charging_vector)
-static int sip_remove_charging_vector(struct sip_msg *msg, struct hdr_field *hf)
+static int sip_remove_charging_vector(
+		struct sip_msg *msg, struct hdr_field *hf, struct lump **anchor)
 {
 {
 	struct lump *l;
 	struct lump *l;
 
 
@@ -269,36 +315,58 @@ static int sip_remove_charging_vector(struct sip_msg *msg, struct hdr_field *hf)
 			LM_ERR("no memory\n");
 			LM_ERR("no memory\n");
 			return -1;
 			return -1;
 		}
 		}
+		if(anchor != NULL) {
+			*anchor = l;
+		}
 		return 2;
 		return 2;
 	} else {
 	} else {
 		return 1;
 		return 1;
 	}
 	}
 }
 }
 
 
-static int sip_add_charging_vector(struct sip_msg *msg)
+static int sip_add_charging_vector(
+		struct sip_msg *msg, const str *pcv_hf, struct lump *anchor)
 {
 {
-	struct lump *anchor;
-	char *s;
+	str buf = STR_NULL;
 
 
-	anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0);
-	if(anchor == 0) {
-		LM_ERR("can't get anchor\n");
-		return -1;
+	if(anchor == NULL) {
+		LM_DBG("add pcv with new lump\n");
+		anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0);
+		if(anchor == 0) {
+			LM_ERR("can't get anchor\n");
+			return -1;
+		}
 	}
 	}
 
 
-	s = (char *)pkg_malloc(_siputils_pcv.len);
-	if(!s) {
-		PKG_MEM_ERROR;
-		return -1;
-	}
-	memcpy(s, _siputils_pcv.s, _siputils_pcv.len);
+	buf.s = (char *)pkg_malloc(pcv_hf->len);
+	if(!buf.s)
+		goto out3;
+	memcpy(buf.s, pcv_hf->s, pcv_hf->len);
+
+	if((anchor = insert_new_lump_before(anchor, buf.s, pcv_hf->len, 0)) == 0)
+		goto out1;
+
+	if((anchor = insert_subst_lump_before(anchor, SUBST_SND_IP, 0)) == 0)
+		goto out2;
+
+	buf.s = (char *)pkg_malloc(CRLF_LEN + 1);
+	if(!buf.s)
+		goto out3;
+
+	buf.len = sprintf(buf.s, CRLF);
+	if((anchor = insert_new_lump_before(anchor, buf.s, buf.len, 0)) == 0)
+		goto out1;
 
 
-	if(insert_new_lump_before(anchor, s, _siputils_pcv.len, 0) == 0) {
-		LM_ERR("can't insert lump\n");
-		pkg_free(s);
-		return -1;
-	}
 	return 1;
 	return 1;
+
+out1:
+	pkg_free(buf.s);
+out2:
+	LM_ERR("can't insert lump\n");
+	return -1;
+out3:
+	PKG_MEM_ERROR;
+	return -1;
 }
 }
 
 
 int sip_handle_pcv(struct sip_msg *msg, char *flags, char *str2)
 int sip_handle_pcv(struct sip_msg *msg, char *flags, char *str2)
@@ -307,13 +375,12 @@ int sip_handle_pcv(struct sip_msg *msg, char *flags, char *str2)
 	int remove_pcv = 0;
 	int remove_pcv = 0;
 	int replace_pcv = 0;
 	int replace_pcv = 0;
 	int i;
 	int i;
-	str flag_str;
-	struct hdr_field *hf_pcv = NULL;
+	int action = PCV_NOP;
 
 
-	_siputils_pcv.len = 0;
-	_siputils_pcv_status = PCV_NONE;
+	str flag_str;
+	struct lump *deleted_pcv_lump = NULL;
 
 
-	if(fixup_get_svalue(msg, (gparam_p)flags, &flag_str) < 0) {
+	if(get_str_fparam(&flag_str, msg, (gparam_p)flags) < 0) {
 		LM_ERR("failed to retrieve parameter value\n");
 		LM_ERR("failed to retrieve parameter value\n");
 		return -1;
 		return -1;
 	}
 	}
@@ -342,16 +409,40 @@ int sip_handle_pcv(struct sip_msg *msg, char *flags, char *str2)
 		}
 		}
 	}
 	}
 
 
-	sip_get_charging_vector(msg, &hf_pcv);
+	if(_siputils_pcv_current_msg_id != msg->id
+			|| _siputils_pcv_status == PCV_NONE) {
+		if(sip_get_charging_vector(msg, &_siputils_pcv_hf_pcv) > 0) {
+			_siputils_pcv_current_msg_id = msg->id;
+		}
+	}
+	switch(_siputils_pcv_status) {
+		case PCV_GENERATED:
+			LM_WARN("P-Charging-Vector can't be changed after generation. "
+					"Skipping command '%.*s'!",
+					STR_FMT(&flag_str));
+			return PCV_NOP;
+		case PCV_DELETED:
+			/* be consistent with the return value in this case */
+			if(remove_pcv)
+				return PCV_NOP;
+		default:
+			break;
+	}
 
 
 	/*
 	/*
 	 * We need to remove the original PCV if it was present and either
 	 * We need to remove the original PCV if it was present and either
 	 * we were asked to remove it or we were asked to replace it
 	 * we were asked to remove it or we were asked to replace it
+	 * or when it was broken anyway
 	 */
 	 */
-	if(_siputils_pcv_status == PCV_PARSED && (replace_pcv || remove_pcv)) {
-		i = sip_remove_charging_vector(msg, hf_pcv);
+	if((_siputils_pcv_status == PCV_PARSED && (replace_pcv || remove_pcv))
+			|| _siputils_pcv_status == PCV_ICID_MISSING) {
+		i = sip_remove_charging_vector(
+				msg, _siputils_pcv_hf_pcv, &deleted_pcv_lump);
 		if(i <= 0)
 		if(i <= 0)
 			return (i == 0) ? -1 : i;
 			return (i == 0) ? -1 : i;
+		sip_initialize_pcv_buffers();
+		_siputils_pcv_status = PCV_DELETED;
+		action = PCV_DELETED;
 	}
 	}
 
 
 	/* Generate PCV if
 	/* Generate PCV if
@@ -361,103 +452,98 @@ int sip_handle_pcv(struct sip_msg *msg, char *flags, char *str2)
 	if(replace_pcv
 	if(replace_pcv
 			|| (generate_pcv && _siputils_pcv_status != PCV_GENERATED
 			|| (generate_pcv && _siputils_pcv_status != PCV_GENERATED
 					&& _siputils_pcv_status != PCV_PARSED)) {
 					&& _siputils_pcv_status != PCV_PARSED)) {
-		strcpy(_siputils_pcv_buf, P_CHARGING_VECTOR);
-		strcat(_siputils_pcv_buf, ": ");
-
-		char *pcv_body = _siputils_pcv_buf + 19;
-		char pcv_value[40];
-
-		/* We use the IP address of the interface that received the message as generated-at */
-		if(msg->rcv.bind_address == NULL
-				|| msg->rcv.bind_address->address_str.s == NULL) {
-			LM_ERR("No IP address for message. Failed to generate charging "
-				   "vector.\n");
-			return -2;
-		}
+		char generated_pcv_buf[PCV_BUF_SIZE] = {0};
+		str generated_pcv = {generated_pcv_buf, 0};
+
+		strcpy(generated_pcv_buf, P_CHARGING_VECTOR);
+		strcat(generated_pcv_buf, ": ");
 
 
-		sip_generate_charging_vector(pcv_value);
+		char *pcv_body = generated_pcv_buf + P_CHARGING_VECTOR_PREFIX_LEN;
+		int body_len = 0;
+		char pcv_value[40] = {0};
 
 
-		_siputils_pcv.len = snprintf(pcv_body, PCV_BUF_SIZE - 19,
-				"icid-value=%.*s; icid-generated-at=%.*s\r\n", 32, pcv_value,
-				msg->rcv.bind_address->address_str.len,
-				msg->rcv.bind_address->address_str.s);
-		_siputils_pcv.len += 19;
+		sip_generate_charging_vector(pcv_value, sizeof(pcv_value));
 
 
-		_siputils_pcv_status = PCV_GENERATED;
+		body_len = snprintf(pcv_body,
+				PCV_BUF_SIZE - P_CHARGING_VECTOR_PREFIX_LEN,
+				"icid-value=%.*s;icid-generated-at=", (int)sizeof(pcv_value),
+				pcv_value);
+		generated_pcv.len = body_len + P_CHARGING_VECTOR_PREFIX_LEN;
 
 
-		/* if generated, reparse it */
-		sip_parse_charging_vector(pcv_body, _siputils_pcv.len - 19);
 		/* if it was generated, we need to send it out as a header */
 		/* if it was generated, we need to send it out as a header */
-		LM_INFO("Generated PCV header %.*s\n", _siputils_pcv.len - 2,
-				_siputils_pcv_buf);
-		i = sip_add_charging_vector(msg);
+		LM_INFO("Generated new PCV header %.*s\n", PCV_BUF_SIZE,
+				generated_pcv_buf);
+
+		i = sip_add_charging_vector(msg, &generated_pcv, deleted_pcv_lump);
 		if(i <= 0) {
 		if(i <= 0) {
 			LM_ERR("Failed to add P-Charging-Vector header\n");
 			LM_ERR("Failed to add P-Charging-Vector header\n");
 			return (i == 0) ? -1 : i;
 			return (i == 0) ? -1 : i;
 		}
 		}
+
+		/* if generated and added, copy buffer and reparse it */
+		sip_initialize_pcv_buffers();
+		_siputils_pcv.len = body_len - CRLF_LEN;
+		memcpy(_siputils_pcv.s, pcv_body, _siputils_pcv.len);
+		if(sip_parse_charging_vector(
+				   _siputils_pcv_buf, sizeof(_siputils_pcv_buf))) {
+			action = (_siputils_pcv_status == PCV_DELETED) ? PCV_REPLACED
+														   : PCV_GENERATED;
+			_siputils_pcv_status = PCV_GENERATED;
+		}
 	}
 	}
 
 
 	_siputils_pcv_current_msg_id = msg->id;
 	_siputils_pcv_current_msg_id = msg->id;
-	return 1;
+	LM_DBG("Charging vector status is now %s\n", sstatus[_siputils_pcv_status]);
+	return action;
 }
 }
 
 
 
 
 int pv_get_charging_vector(
 int pv_get_charging_vector(
 		struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
 		struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
 {
 {
-	str pcv_pv;
+	str pcv_pv = STR_NULL;
 
 
 	if(_siputils_pcv_current_msg_id != msg->id
 	if(_siputils_pcv_current_msg_id != msg->id
 			|| _siputils_pcv_status == PCV_NONE) {
 			|| _siputils_pcv_status == PCV_NONE) {
-		struct hdr_field *hf_pcv = NULL;
-		if(sip_get_charging_vector(msg, &hf_pcv) > 0) {
+		if(sip_get_charging_vector(msg, &_siputils_pcv_hf_pcv) > 0) {
 			_siputils_pcv_current_msg_id = msg->id;
 			_siputils_pcv_current_msg_id = msg->id;
 		}
 		}
-		LM_DBG("Parsed charging vector for pseudo-var\n");
+		LM_DBG("Parsed charging vector for pseudo-var, current state is %s\n",
+				sstatus[_siputils_pcv_status]);
 	} else {
 	} else {
-		LM_DBG("Charging vector is in state %d for pseudo-var\n",
-				_siputils_pcv_status);
+		LM_DBG("Charging vector is in state %s for pseudo-var and buffered.",
+				sstatus[_siputils_pcv_status]);
 	}
 	}
 
 
-	switch(_siputils_pcv_status) {
-		case PCV_GENERATED:
-			LM_DBG("pcv_status==PCV_GENERATED\n");
-		case PCV_PARSED:
-			LM_DBG("pcv_status==PCV_PARSED\n");
-			switch(param->pvn.u.isname.name.n) {
-				case 5:
-					pcv_pv = _siputils_pcv_term;
-					break;
-				case 4:
-					pcv_pv = _siputils_pcv_orig;
-					break;
-				case 2:
-					pcv_pv = _siputils_pcv_host;
-					break;
-
-				case 3:
-					pcv_pv = _siputils_pcv_id;
-					break;
-
-				case 1:
-				default:
-					pcv_pv = _siputils_pcv;
-					break;
-			}
-
-			if(pcv_pv.len > 0)
-				return pv_get_strval(msg, param, res, &pcv_pv);
-			else
-				LM_WARN("No value for pseudo-var $pcv but status was %d.\n",
-						_siputils_pcv_status);
-
+	switch(param->pvn.u.isname.name.n) {
+		case PCV_PARAM_TERM:
+			pcv_pv = _siputils_pcv_term;
 			break;
 			break;
-
-		case PCV_NONE:
+		case PCV_PARAM_ORIG:
+			pcv_pv = _siputils_pcv_orig;
+			break;
+		case PCV_PARAM_GENADDR:
+			pcv_pv = _siputils_pcv_genaddr;
+			break;
+		case PCV_PARAM_ID:
+			pcv_pv = _siputils_pcv_id;
+			break;
+		case PCV_PARAM_STATUS:
+			return pv_get_sintval(msg, param, res, _siputils_pcv_status);
+			break;
+		case PCV_PARAM_ALL:
 		default:
 		default:
+			pcv_pv = _siputils_pcv;
 			break;
 			break;
 	}
 	}
 
 
+	if(pcv_pv.len > 0)
+		return pv_get_strval(msg, param, res, &pcv_pv);
+	else if(param->pvn.u.isname.name.n == PCV_PARAM_ID
+			|| param->pvn.u.isname.name.n <= PCV_PARAM_ALL)
+		LM_WARN("No value for pseudo-var $pcv but status was %s.\n",
+				sstatus[_siputils_pcv_status]);
+
 	return pv_get_null(msg, param, res);
 	return pv_get_null(msg, param, res);
 }
 }
 
 
@@ -469,27 +555,33 @@ int pv_parse_charging_vector_name(pv_spec_p sp, str *in)
 	switch(in->len) {
 	switch(in->len) {
 		case 3:
 		case 3:
 			if(strncmp(in->s, "all", 3) == 0)
 			if(strncmp(in->s, "all", 3) == 0)
-				sp->pvp.pvn.u.isname.name.n = 1;
+				sp->pvp.pvn.u.isname.name.n = PCV_PARAM_ALL;
 			else
 			else
 				goto error;
 				goto error;
 			break;
 			break;
 		case 4:
 		case 4:
 			if(strncmp(in->s, "orig", 4) == 0)
 			if(strncmp(in->s, "orig", 4) == 0)
-				sp->pvp.pvn.u.isname.name.n = 4;
+				sp->pvp.pvn.u.isname.name.n = PCV_PARAM_ORIG;
 			else if(strncmp(in->s, "term", 4) == 0)
 			else if(strncmp(in->s, "term", 4) == 0)
-				sp->pvp.pvn.u.isname.name.n = 5;
+				sp->pvp.pvn.u.isname.name.n = PCV_PARAM_TERM;
 			else
 			else
 				goto error;
 				goto error;
 			break;
 			break;
 		case 5:
 		case 5:
 			if(strncmp(in->s, "value", 5) == 0)
 			if(strncmp(in->s, "value", 5) == 0)
-				sp->pvp.pvn.u.isname.name.n = 3;
+				sp->pvp.pvn.u.isname.name.n = PCV_PARAM_ID;
+			else
+				goto error;
+			break;
+		case 6:
+			if(strncmp(in->s, "status", 6) == 0)
+				sp->pvp.pvn.u.isname.name.n = PCV_PARAM_STATUS;
 			else
 			else
 				goto error;
 				goto error;
 			break;
 			break;
 		case 7:
 		case 7:
 			if(strncmp(in->s, "genaddr", 7) == 0)
 			if(strncmp(in->s, "genaddr", 7) == 0)
-				sp->pvp.pvn.u.isname.name.n = 2;
+				sp->pvp.pvn.u.isname.name.n = PCV_PARAM_GENADDR;
 			else
 			else
 				goto error;
 				goto error;
 			break;
 			break;