Browse code

ims_ipsec_pcscf: new config param for ipsec

- added a new config param - ipsec_reuse_server_port - reuse or not
PCSCF server port for UA Re-registration.
- added description for the new parameter in ims_ipsec_pcscf_admin.xml.
parameter ipsec_reuse_server_port.
- in ipsec_forward() add supported and require secagree headers only
for Register reply with code 200.
- in fill_contact() for Request messages set received host, port and
proto from request uri if alias is presented.

Aleksandar Yosifov authored on 04/12/2019 09:12:21
Showing 3 changed files
... ...
@@ -62,8 +62,7 @@
62 62
 
63 63
 extern str ipsec_listen_addr;
64 64
 extern str ipsec_listen_addr6;
65
-extern int ipsec_server_port;
66
-extern int ipsec_client_port;
65
+extern int ipsec_reuse_server_port;
67 66
 
68 67
 extern int spi_id_start;
69 68
 
... ...
@@ -141,6 +140,7 @@ static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
141 141
     contact_body_t* cb = NULL;
142 142
     struct via_body* vb = NULL;
143 143
     struct sip_msg* req = NULL;
144
+	char* srcip = NULL;
144 145
 
145 146
     if(!ci) {
146 147
         LM_ERR("called with null ptr\n");
... ...
@@ -150,14 +150,17 @@ static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
150 150
     memset(ci, 0, sizeof(struct pcontact_info));
151 151
 
152 152
     if(m->first_line.type == SIP_REQUEST) {
153
-        struct sip_uri uri;
154
-        memset(&uri, 0, sizeof(struct sip_uri));
153
+		char* alias_start;
154
+		struct sip_uri uri;
155
+		memset(&uri, 0, sizeof(struct sip_uri));
155 156
 
156 157
         if(parse_uri(m->first_line.u.request.uri.s, m->first_line.u.request.uri.len, &uri)) {
157 158
             LM_ERR("Can't parse the request URI from first line\n");
158 159
             return -1;
159 160
         }
160 161
 
162
+		req = m;
163
+
161 164
         // populate host,port, aor in CI
162 165
         ci->via_host = uri.host;
163 166
         ci->via_port = uri.port_no ? uri.port_no : 5060;
... ...
@@ -165,8 +168,76 @@ static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
165 165
         ci->aor = m->first_line.u.request.uri;
166 166
         ci->searchflag = SEARCH_NORMAL;
167 167
 
168
-        req = m;
169
-    }
168
+		if(ci->via_host.s == NULL || ci->via_host.len == 0){
169
+			// no host included in RURI
170
+			vb = cscf_get_ue_via(m);
171
+			if (!vb) {
172
+				LM_ERR("Reply No via body headers\n");
173
+				return -1;
174
+			}
175
+
176
+			// populate CI with bare minimum
177
+			ci->via_host = vb->host;
178
+			ci->via_port = vb->port;
179
+			ci->via_prot = vb->proto;
180
+		}
181
+
182
+		if (uri.params.len > 6 && (alias_start = _strnistr(uri.params.s, "alias=", uri.params.len)) != NULL) {
183
+			char *p, *port_s, *proto_s;
184
+			char portbuf[5];
185
+			str alias_s;
186
+
187
+			LM_DBG("contact has an alias [%.*s] - we can use that as the received\n", uri.params.len, uri.params.s);
188
+
189
+			alias_s.len = uri.params.len - (alias_start - uri.params.s) - 6;
190
+			alias_s.s = alias_start + 6;
191
+
192
+			p = _strnistr(alias_s.s, "~", alias_s.len);
193
+			if (p!=NULL) {
194
+				ci->received_host.len = p - alias_s.s;
195
+
196
+				if(ci->received_host.len > IP6_MAX_STR_SIZE + 2){
197
+					LM_ERR("Invalid length for source IP address\n");
198
+					return -1;
199
+				}
200
+
201
+				if((srcip = pkg_malloc(50)) == NULL) {
202
+					LM_ERR("Error allocating memory for source IP address\n");
203
+					return -1;
204
+				}
205
+
206
+				memcpy(srcip, alias_s.s, ci->received_host.len);
207
+				ci->received_host.s = srcip;
208
+
209
+				port_s = p+1;
210
+				p = _strnistr(port_s, "~", alias_s.len - ci->received_host.len);
211
+				if (p!=NULL) {
212
+					memset(portbuf, 0, 5);
213
+					memcpy(portbuf, port_s, (p-port_s));
214
+					ci->received_port = atoi(portbuf);
215
+
216
+					proto_s = p + 1;
217
+					memset(portbuf, 0, 5);
218
+					memcpy(portbuf, proto_s, 1);
219
+					ci->received_proto = atoi(portbuf);
220
+
221
+					ci->searchflag = SEARCH_RECEIVED;
222
+				}
223
+
224
+				LM_DBG("parsed alias [%d://%.*s:%d]\n", ci->received_proto, ci->received_host.len, ci->received_host.s, ci->received_port);
225
+			}
226
+		}else{
227
+			if((srcip = pkg_malloc(50)) == NULL) {
228
+				LM_ERR("Error allocating memory for source IP address\n");
229
+				return -1;
230
+			}
231
+
232
+			ci->received_host.len = ip_addr2sbuf(&req->rcv.src_ip, srcip, 50);
233
+			ci->received_host.s = srcip;
234
+			ci->received_port = req->rcv.src_port;
235
+			ci->received_proto = req->rcv.proto;
236
+		}
237
+	}
170 238
     else if(m->first_line.type == SIP_REPLY) {
171 239
         struct cell *t = tmb.t_gett();
172 240
         if (!t || t == (void*) -1) {
... ...
@@ -194,24 +265,22 @@ static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
194 194
         ci->via_prot = vb->proto;
195 195
         ci->aor = cb->contacts->uri;
196 196
         ci->searchflag = SEARCH_RECEIVED;
197
+
198
+		if((srcip = pkg_malloc(50)) == NULL) {
199
+			LM_ERR("Error allocating memory for source IP address\n");
200
+			return -1;
201
+		}
202
+
203
+		ci->received_host.len = ip_addr2sbuf(&req->rcv.src_ip, srcip, 50);
204
+		ci->received_host.s = srcip;
205
+		ci->received_port = req->rcv.src_port;
206
+		ci->received_proto = req->rcv.proto;
197 207
     }
198 208
     else {
199 209
         LM_ERR("Unknown first line type: %d\n", m->first_line.type);
200 210
         return -1;
201 211
     }
202 212
 
203
-
204
-    char* srcip = NULL;
205
-    if((srcip = pkg_malloc(50)) == NULL) {
206
-        LM_ERR("Error allocating memory for source IP address\n");
207
-        return -1;
208
-    }
209
-
210
-    ci->received_host.len = ip_addr2sbuf(&req->rcv.src_ip, srcip, 50);
211
-    ci->received_host.s = srcip;
212
-    ci->received_port = req->rcv.src_port;
213
-    ci->received_proto = req->rcv.proto;
214
-
215 213
     LM_DBG("SIP %s fill contact with AOR [%.*s], VIA [%d://%.*s:%d], received_host [%d://%.*s:%d]\n",
216 214
             m->first_line.type == SIP_REQUEST ? "REQUEST" : "REPLY",
217 215
             ci->aor.len, ci->aor.s, ci->via_prot, ci->via_host.len, ci->via_host.s, ci->via_port,
... ...
@@ -249,7 +318,7 @@ static int get_ck_ik(const struct sip_msg* m, str* ck, str* ik)
249 249
     return 0;
250 250
 }
251 251
 
252
-static int update_contact_ipsec_params(ipsec_t* s, const struct sip_msg* m)
252
+static int update_contact_ipsec_params(ipsec_t* s, const struct sip_msg* m, ipsec_t* s_old)
253 253
 {
254 254
     // Get CK and IK
255 255
     str ck, ik;
... ...
@@ -310,21 +379,26 @@ static int update_contact_ipsec_params(ipsec_t* s, const struct sip_msg* m)
310 310
         return -1;
311 311
     }
312 312
 
313
-    if((s->port_ps = acquire_sport()) == 0){
314
-        LM_ERR("No free server port for IPSEC tunnel creation\n");
315
-		shm_free(s->ck.s);
316
-		s->ck.s = NULL; s->ck.len = 0;
317
-		shm_free(s->ik.s);
318
-		s->ik.s = NULL; s->ik.len = 0;
313
+	// use the same P-CSCF server port if it is present
314
+	if(s_old){
315
+		s->port_ps = s_old->port_ps;
316
+	}else{
317
+		if((s->port_ps = acquire_sport()) == 0){
318
+			LM_ERR("No free server port for IPSEC tunnel creation\n");
319
+			shm_free(s->ck.s);
320
+			s->ck.s = NULL; s->ck.len = 0;
321
+			shm_free(s->ik.s);
322
+			s->ik.s = NULL; s->ik.len = 0;
319 323
 
320
-		release_cport(s->port_pc);
324
+			release_cport(s->port_pc);
321 325
 
322
-		release_spi(s->spi_pc);
323
-		release_spi(s->spi_ps);
324
-        return -1;
325
-    }
326
+			release_spi(s->spi_pc);
327
+			release_spi(s->spi_ps);
328
+			return -1;
329
+		}
330
+	}
326 331
 
327
-    return 0;
332
+	return 0;
328 333
 }
329 334
 
330 335
 static int create_ipsec_tunnel(const struct ip_addr *remote_addr, ipsec_t* s)
... ...
@@ -494,7 +568,7 @@ int add_supported_secagree_header(struct sip_msg* m)
494 494
     if(cscf_add_header(m, supported, HDR_SUPPORTED_T) != 1) {
495 495
 		pkg_free(supported->s);
496 496
 		pkg_free(supported);
497
-        LM_ERR("Error adding security header to reply!\n");
497
+        LM_ERR("Error adding supported header to reply!\n");
498 498
         return -1;
499 499
     }
500 500
     pkg_free(supported);
... ...
@@ -502,6 +576,38 @@ int add_supported_secagree_header(struct sip_msg* m)
502 502
     return 0;
503 503
 }
504 504
 
505
+int add_require_secagree_header(struct sip_msg* m)
506
+{
507
+	// Add require sec-agree header in the reply
508
+	const char* require_sec_agree = "Require: sec-agree\r\n";
509
+	const int require_sec_agree_len = 20;
510
+
511
+	str* require = NULL;
512
+	if((require = pkg_malloc(sizeof(str))) == NULL) {
513
+		LM_ERR("Error allocating pkg memory for require header\n");
514
+		return -1;
515
+	}
516
+
517
+	if((require->s = pkg_malloc(require_sec_agree_len)) == NULL) {
518
+		LM_ERR("Error allcationg pkg memory for require header str\n");
519
+		pkg_free(require);
520
+		return -1;
521
+	}
522
+
523
+	memcpy(require->s, require_sec_agree, require_sec_agree_len);
524
+	require->len = require_sec_agree_len;
525
+
526
+	if(cscf_add_header(m, require, HDR_REQUIRE_T) != 1) {
527
+		pkg_free(require->s);
528
+		pkg_free(require);
529
+		LM_ERR("Error adding require header to reply!\n");
530
+		return -1;
531
+	}
532
+
533
+	pkg_free(require);
534
+	return 0;
535
+}
536
+
505 537
 int add_security_server_header(struct sip_msg* m, ipsec_t* s)
506 538
 {
507 539
     // allocate memory for the header itself
... ...
@@ -590,7 +696,8 @@ int ipsec_create(struct sip_msg* m, udomain_t* d)
590 590
 
591 591
         ipsec_t* s = pcontact->security_temp->data.ipsec;
592 592
 
593
-        if(update_contact_ipsec_params(s, m) != 0) {
593
+		// for initial Registration use a new P-CSCF server port
594
+        if(update_contact_ipsec_params(s, m, NULL) != 0) {
594 595
             goto cleanup;
595 596
         }
596 597
 
... ...
@@ -633,7 +740,8 @@ int ipsec_create(struct sip_msg* m, udomain_t* d)
633 633
             goto cleanup;
634 634
         }
635 635
 
636
-        if(update_contact_ipsec_params(req_sec_params->data.ipsec, m) != 0) {
636
+		// for Re-Registration use the same P-CSCF server port if 'ipsec reuse server port' is enabled
637
+        if(update_contact_ipsec_params(req_sec_params->data.ipsec, m, ipsec_reuse_server_port ? pcontact->security_temp->data.ipsec : NULL) != 0) {
637 638
             goto cleanup;
638 639
         }
639 640
 
... ...
@@ -803,13 +911,15 @@ int ipsec_forward(struct sip_msg* m, udomain_t* d, int _cflags)
803 803
 
804 804
     ret = IPSEC_CMD_SUCCESS; // all good, return SUCCESS
805 805
 
806
-    if(add_supported_secagree_header(m) != 0) {
807
-        goto cleanup;
808
-    }
809
-
810
-    if(add_security_server_header(m, s) != 0) {
811
-        goto cleanup;
812
-    }
806
+	if( m->first_line.type == SIP_REPLY && m->first_line.u.reply.statuscode == 200 &&
807
+		req->first_line.u.request.method_value == METHOD_REGISTER){
808
+		if(add_supported_secagree_header(m) != 0){
809
+			goto cleanup;
810
+		}
811
+		if(add_require_secagree_header(m) != 0){
812
+			goto cleanup;
813
+		}
814
+	}
813 815
 
814 816
     ret = IPSEC_CMD_SUCCESS;    // all good, set ret to SUCCESS, and exit
815 817
 
... ...
@@ -136,6 +136,28 @@ modparam("ims_ipsec_pcscf", "ipsec_max_connections", 10)
136 136
     </section>
137 137
 
138 138
     <section>
139
+      <title><varname>ipsec_reuse_server_port</varname> (int)</title>
140
+
141
+      <para>Reuse (1) or not (0) the P-CSCF Server port for Re-registration for one UA.
142
+      When set to 0 - During Re-registration P-CSCF will distribute new P-CSCF client and
143
+      P-CSCF server ports.
144
+      When set to 1 - During Re-registration P-CSCF will reuse the old P-CSCF server port and
145
+      will distribute a new P-CSCF client port.</para>
146
+
147
+      <para><emphasis>Default value is 1.</emphasis></para>
148
+
149
+      <example>
150
+        <title><varname>ipsec_reuse_server_port</varname> parameter usage</title>
151
+
152
+        <programlisting format="linespecific">
153
+...
154
+modparam("ims_ipsec_pcscf", "ipsec_reuse_server_port", 1)
155
+...
156
+        </programlisting>
157
+      </example>
158
+    </section>
159
+
160
+    <section>
139 161
       <title><varname>ipsec_spi_id_start</varname> (int)</title>
140 162
 
141 163
       <para>Each IPSec tunnel has a unique system-wide identifier. This and the following option
... ...
@@ -41,6 +41,7 @@ str ipsec_listen_addr = STR_NULL;
41 41
 str ipsec_listen_addr6 = STR_NULL;
42 42
 int ipsec_client_port =  5062;
43 43
 int ipsec_server_port =  5063;
44
+int ipsec_reuse_server_port = 1;
44 45
 int ipsec_max_connections = 2;
45 46
 int spi_id_start = 100;
46 47
 int spi_id_range = 1000;
... ...
@@ -83,6 +84,7 @@ static param_export_t params[] = {
83 83
 	{"ipsec_listen_addr6",  	PARAM_STR, &ipsec_listen_addr6		},
84 84
 	{"ipsec_client_port",		INT_PARAM, &ipsec_client_port		},
85 85
 	{"ipsec_server_port",		INT_PARAM, &ipsec_server_port		},
86
+	{"ipsec_reuse_server_port",	INT_PARAM, &ipsec_reuse_server_port	},
86 87
 	{"ipsec_max_connections",	INT_PARAM, &ipsec_max_connections	},
87 88
 	{"ipsec_spi_id_start",		INT_PARAM, &spi_id_start			},
88 89
 	{"ipsec_spi_id_range",		INT_PARAM, &spi_id_range			},