/src/open5gs/lib/pfcp/context.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com> |
3 | | * |
4 | | * This file is part of Open5GS. |
5 | | * |
6 | | * This program is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU Affero General Public License as published by |
8 | | * the Free Software Foundation, either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | #include "app/ogs-app.h" |
21 | | #include "ogs-pfcp.h" |
22 | | |
23 | | int __ogs_pfcp_domain; |
24 | | static ogs_pfcp_context_t self; |
25 | | static int context_initialized = 0; |
26 | | |
27 | | static OGS_POOL(ogs_pfcp_node_pool, ogs_pfcp_node_t); |
28 | | |
29 | | static OGS_POOL(ogs_pfcp_far_pool, ogs_pfcp_far_t); |
30 | | static OGS_POOL(ogs_pfcp_urr_pool, ogs_pfcp_urr_t); |
31 | | static OGS_POOL(ogs_pfcp_qer_pool, ogs_pfcp_qer_t); |
32 | | static OGS_POOL(ogs_pfcp_bar_pool, ogs_pfcp_bar_t); |
33 | | |
34 | | static OGS_POOL(ogs_pfcp_pdr_pool, ogs_pfcp_pdr_t); |
35 | | static OGS_POOL(ogs_pfcp_pdr_teid_pool, ogs_pool_id_t); |
36 | | static ogs_pool_id_t *pdr_random_to_index; |
37 | | |
38 | | static OGS_POOL(ogs_pfcp_rule_pool, ogs_pfcp_rule_t); |
39 | | |
40 | | static OGS_POOL(ogs_pfcp_dev_pool, ogs_pfcp_dev_t); |
41 | | static OGS_POOL(ogs_pfcp_subnet_pool, ogs_pfcp_subnet_t); |
42 | | |
43 | | void ogs_pfcp_context_init(void) |
44 | 0 | { |
45 | 0 | int i; |
46 | 0 | ogs_assert(context_initialized == 0); |
47 | | |
48 | | /* Initialize SMF context */ |
49 | 0 | memset(&self, 0, sizeof(ogs_pfcp_context_t)); |
50 | |
|
51 | 0 | self.local_recovery = ogs_time_ntp32_now(); |
52 | |
|
53 | 0 | ogs_log_install_domain(&__ogs_pfcp_domain, "pfcp", ogs_core()->log.level); |
54 | |
|
55 | 0 | ogs_pool_init(&ogs_pfcp_node_pool, ogs_app()->pool.nf); |
56 | | |
57 | 0 | ogs_pool_init(&ogs_pfcp_far_pool, |
58 | 0 | ogs_app()->pool.sess * OGS_MAX_NUM_OF_FAR); |
59 | 0 | ogs_pool_init(&ogs_pfcp_urr_pool, |
60 | 0 | ogs_app()->pool.sess * OGS_MAX_NUM_OF_URR); |
61 | 0 | ogs_pool_init(&ogs_pfcp_qer_pool, |
62 | 0 | ogs_app()->pool.sess * OGS_MAX_NUM_OF_QER); |
63 | 0 | ogs_pool_init(&ogs_pfcp_bar_pool, |
64 | 0 | ogs_app()->pool.sess * OGS_MAX_NUM_OF_BAR); |
65 | | |
66 | 0 | ogs_pool_init(&ogs_pfcp_pdr_pool, |
67 | 0 | ogs_app()->pool.sess * OGS_MAX_NUM_OF_PDR); |
68 | 0 | ogs_pool_init(&ogs_pfcp_pdr_teid_pool, ogs_pfcp_pdr_pool.size); |
69 | 0 | ogs_pool_random_id_generate(&ogs_pfcp_pdr_teid_pool); |
70 | |
|
71 | 0 | pdr_random_to_index = ogs_calloc( |
72 | 0 | sizeof(ogs_pool_id_t), ogs_pfcp_pdr_pool.size+1); |
73 | 0 | ogs_assert(pdr_random_to_index); |
74 | 0 | for (i = 0; i < ogs_pfcp_pdr_pool.size; i++) |
75 | 0 | pdr_random_to_index[ogs_pfcp_pdr_teid_pool.array[i]] = i; |
76 | |
|
77 | 0 | ogs_pool_init(&ogs_pfcp_rule_pool, |
78 | 0 | ogs_app()->pool.sess * |
79 | 0 | OGS_MAX_NUM_OF_PDR * OGS_MAX_NUM_OF_FLOW_IN_PDR); |
80 | | |
81 | 0 | ogs_pool_init(&ogs_pfcp_dev_pool, OGS_MAX_NUM_OF_DEV); |
82 | 0 | ogs_pool_init(&ogs_pfcp_subnet_pool, OGS_MAX_NUM_OF_SUBNET); |
83 | | |
84 | 0 | self.object_teid_hash = ogs_hash_make(); |
85 | 0 | ogs_assert(self.object_teid_hash); |
86 | 0 | self.far_f_teid_hash = ogs_hash_make(); |
87 | 0 | ogs_assert(self.far_f_teid_hash); |
88 | 0 | self.far_teid_hash = ogs_hash_make(); |
89 | 0 | ogs_assert(self.far_teid_hash); |
90 | | |
91 | 0 | context_initialized = 1; |
92 | 0 | } |
93 | | |
94 | | void ogs_pfcp_context_final(void) |
95 | 0 | { |
96 | 0 | ogs_assert(context_initialized == 1); |
97 | | |
98 | 0 | ogs_assert(self.object_teid_hash); |
99 | 0 | ogs_hash_destroy(self.object_teid_hash); |
100 | 0 | ogs_assert(self.far_f_teid_hash); |
101 | 0 | ogs_hash_destroy(self.far_f_teid_hash); |
102 | 0 | ogs_assert(self.far_teid_hash); |
103 | 0 | ogs_hash_destroy(self.far_teid_hash); |
104 | |
|
105 | 0 | ogs_pfcp_dev_remove_all(); |
106 | 0 | ogs_pfcp_subnet_remove_all(); |
107 | |
|
108 | 0 | ogs_pool_final(&ogs_pfcp_dev_pool); |
109 | 0 | ogs_pool_final(&ogs_pfcp_subnet_pool); |
110 | 0 | ogs_pool_final(&ogs_pfcp_rule_pool); |
111 | | |
112 | 0 | ogs_pool_final(&ogs_pfcp_pdr_pool); |
113 | 0 | ogs_pool_final(&ogs_pfcp_pdr_teid_pool); |
114 | 0 | ogs_free(pdr_random_to_index); |
115 | |
|
116 | 0 | ogs_pool_final(&ogs_pfcp_far_pool); |
117 | 0 | ogs_pool_final(&ogs_pfcp_urr_pool); |
118 | 0 | ogs_pool_final(&ogs_pfcp_qer_pool); |
119 | 0 | ogs_pool_final(&ogs_pfcp_bar_pool); |
120 | | |
121 | 0 | ogs_pfcp_node_remove_all(&self.pfcp_peer_list); |
122 | |
|
123 | 0 | ogs_pool_final(&ogs_pfcp_node_pool); |
124 | | |
125 | 0 | context_initialized = 0; |
126 | 0 | } |
127 | | |
128 | | ogs_pfcp_context_t *ogs_pfcp_self(void) |
129 | 0 | { |
130 | 0 | return &self; |
131 | 0 | } |
132 | | |
133 | | static int ogs_pfcp_context_prepare(void) |
134 | 0 | { |
135 | 0 | self.pfcp_port = OGS_PFCP_UDP_PORT; |
136 | |
|
137 | 0 | self.tun_ifname = "ogstun"; |
138 | |
|
139 | 0 | return OGS_OK; |
140 | 0 | } |
141 | | |
142 | | static int ogs_pfcp_check_subnet_overlapping(void) |
143 | 0 | { |
144 | 0 | ogs_pfcp_subnet_t *subnet = NULL; |
145 | 0 | ogs_pfcp_subnet_t *next_subnet = NULL; |
146 | 0 | char buf1[OGS_ADDRSTRLEN]; |
147 | 0 | char buf2[OGS_ADDRSTRLEN]; |
148 | 0 | int rv = OGS_OK; |
149 | |
|
150 | 0 | ogs_list_for_each(&self.subnet_list, subnet){ |
151 | 0 | for (next_subnet = ogs_list_next(subnet); (next_subnet); |
152 | 0 | next_subnet = ogs_list_next(next_subnet)) { |
153 | 0 | if ((strlen(subnet->dnn) == 0 || |
154 | 0 | strlen(next_subnet->dnn) == 0 || |
155 | 0 | (strcmp(subnet->dnn, next_subnet->dnn)) == 0) && |
156 | 0 | subnet->gw.family == next_subnet->gw.family) { |
157 | 0 | uint32_t *addr1 = subnet->sub.sub; |
158 | 0 | uint32_t *addr2 = next_subnet->sub.sub; |
159 | 0 | uint32_t mask[4]; |
160 | 0 | int i; |
161 | | /* Get smaller subnet mask for IPv4 or IPv6 */ |
162 | 0 | for (i = 0; i < 4 ; i++) { |
163 | 0 | mask[i] = (subnet->sub.mask[i] & next_subnet->sub.mask[i]); |
164 | 0 | } |
165 | | /* Compare masked subnets if they overlap */ |
166 | 0 | if (subnet->gw.family == AF_INET) { |
167 | 0 | if ((addr1[0] & mask[0]) == (addr2[0] & mask[0])) { |
168 | 0 | ogs_error("Overlapping subnets in SMF configuration file: %s/%d and %s/%d", |
169 | 0 | OGS_INET_NTOP(&subnet->gw.sub[0], buf1), |
170 | 0 | subnet->prefixlen, |
171 | 0 | OGS_INET_NTOP(&next_subnet->gw.sub[0], buf2), |
172 | 0 | next_subnet->prefixlen); |
173 | 0 | rv = OGS_ERROR; |
174 | 0 | } |
175 | 0 | } else if (subnet->gw.family == AF_INET6) { |
176 | 0 | if (((addr1[0] & mask[0]) == (addr2[0] & mask[0])) && |
177 | 0 | ((addr1[1] & mask[1]) == (addr2[1] & mask[1])) && |
178 | 0 | ((addr1[2] & mask[2]) == (addr2[2] & mask[2])) && |
179 | 0 | ((addr1[3] & mask[3]) == (addr2[3] & mask[3]))) { |
180 | 0 | ogs_error("Overlapping subnets in SMF configuration file: %s/%d and %s/%d", |
181 | 0 | OGS_INET6_NTOP(&subnet->gw.sub[0], buf1), |
182 | 0 | subnet->prefixlen, |
183 | 0 | OGS_INET6_NTOP(&next_subnet->gw.sub[0], buf2), |
184 | 0 | next_subnet->prefixlen); |
185 | 0 | rv = OGS_ERROR; |
186 | 0 | } |
187 | 0 | } else { |
188 | 0 | ogs_error("Invalid family in subnet configuration [%d]", |
189 | 0 | subnet->gw.family); |
190 | 0 | rv = OGS_ERROR; |
191 | 0 | ogs_assert_if_reached(); |
192 | 0 | } |
193 | 0 | } |
194 | 0 | } |
195 | 0 | } |
196 | | |
197 | 0 | return rv; |
198 | 0 | } |
199 | | |
200 | | static int ogs_pfcp_context_validation(const char *local) |
201 | 0 | { |
202 | 0 | if (ogs_list_first(&self.pfcp_list) == NULL && |
203 | 0 | ogs_list_first(&self.pfcp_list6) == NULL) { |
204 | 0 | ogs_error("No %s.pfcp.address: in '%s'", local, ogs_app()->file); |
205 | 0 | return OGS_ERROR; |
206 | 0 | } |
207 | 0 | if (ogs_pfcp_check_subnet_overlapping() != OGS_OK) |
208 | 0 | return OGS_ERROR; |
209 | | |
210 | 0 | return OGS_OK; |
211 | 0 | } |
212 | | |
213 | | int ogs_pfcp_context_parse_config(const char *local, const char *remote) |
214 | 0 | { |
215 | 0 | int rv; |
216 | 0 | yaml_document_t *document = NULL; |
217 | 0 | ogs_yaml_iter_t root_iter; |
218 | 0 | int idx = 0; |
219 | |
|
220 | 0 | document = ogs_app()->document; |
221 | 0 | ogs_assert(document); |
222 | | |
223 | 0 | rv = ogs_pfcp_context_prepare(); |
224 | 0 | if (rv != OGS_OK) return rv; |
225 | | |
226 | 0 | ogs_yaml_iter_init(&root_iter, document); |
227 | 0 | while (ogs_yaml_iter_next(&root_iter)) { |
228 | 0 | const char *root_key = ogs_yaml_iter_key(&root_iter); |
229 | 0 | ogs_assert(root_key); |
230 | 0 | if ((!strcmp(root_key, local)) && |
231 | 0 | idx++ == ogs_app()->config_section_id) { |
232 | 0 | ogs_yaml_iter_t local_iter; |
233 | 0 | ogs_yaml_iter_recurse(&root_iter, &local_iter); |
234 | 0 | while (ogs_yaml_iter_next(&local_iter)) { |
235 | 0 | const char *local_key = ogs_yaml_iter_key(&local_iter); |
236 | 0 | ogs_assert(local_key); |
237 | 0 | if (!strcmp(local_key, "pfcp")) { |
238 | 0 | ogs_yaml_iter_t pfcp_iter; |
239 | 0 | ogs_yaml_iter_recurse(&local_iter, &pfcp_iter); |
240 | 0 | while (ogs_yaml_iter_next(&pfcp_iter)) { |
241 | 0 | const char *pfcp_key = ogs_yaml_iter_key(&pfcp_iter); |
242 | 0 | ogs_assert(pfcp_key); |
243 | 0 | if (!strcmp(pfcp_key, "server")) { |
244 | 0 | ogs_yaml_iter_t server_iter, server_array; |
245 | 0 | ogs_yaml_iter_recurse(&pfcp_iter, &server_array); |
246 | 0 | do { |
247 | 0 | int family = AF_UNSPEC; |
248 | 0 | int i, num = 0; |
249 | 0 | const char *hostname[OGS_MAX_NUM_OF_HOSTNAME]; |
250 | 0 | int num_of_advertise = 0; |
251 | 0 | const char *advertise[OGS_MAX_NUM_OF_HOSTNAME]; |
252 | 0 | uint16_t port = self.pfcp_port; |
253 | 0 | const char *dev = NULL; |
254 | 0 | ogs_sockaddr_t *addr = NULL; |
255 | |
|
256 | 0 | ogs_sockopt_t option; |
257 | 0 | bool is_option = false; |
258 | |
|
259 | 0 | if (ogs_yaml_iter_type(&server_array) == |
260 | 0 | YAML_MAPPING_NODE) { |
261 | 0 | memcpy(&server_iter, &server_array, |
262 | 0 | sizeof(ogs_yaml_iter_t)); |
263 | 0 | } else if (ogs_yaml_iter_type(&server_array) == |
264 | 0 | YAML_SEQUENCE_NODE) { |
265 | 0 | if (!ogs_yaml_iter_next(&server_array)) |
266 | 0 | break; |
267 | 0 | ogs_yaml_iter_recurse( |
268 | 0 | &server_array, &server_iter); |
269 | 0 | } else if (ogs_yaml_iter_type(&server_array) == |
270 | 0 | YAML_SCALAR_NODE) { |
271 | 0 | break; |
272 | 0 | } else |
273 | 0 | ogs_assert_if_reached(); |
274 | | |
275 | 0 | while (ogs_yaml_iter_next(&server_iter)) { |
276 | 0 | const char *server_key = |
277 | 0 | ogs_yaml_iter_key(&server_iter); |
278 | 0 | ogs_assert(server_key); |
279 | 0 | if (!strcmp(server_key, "family")) { |
280 | 0 | const char *v = |
281 | 0 | ogs_yaml_iter_value(&server_iter); |
282 | 0 | if (v) family = atoi(v); |
283 | 0 | if (family != AF_UNSPEC && |
284 | 0 | family != AF_INET && |
285 | 0 | family != AF_INET6) { |
286 | 0 | ogs_warn("Ignore family(%d) : " |
287 | 0 | "AF_UNSPEC(%d), " |
288 | 0 | "AF_INET(%d), AF_INET6(%d) ", |
289 | 0 | family, |
290 | 0 | AF_UNSPEC, AF_INET, AF_INET6); |
291 | 0 | family = AF_UNSPEC; |
292 | 0 | } |
293 | 0 | } else if (!strcmp(server_key, "address")) { |
294 | 0 | ogs_yaml_iter_t hostname_iter; |
295 | 0 | ogs_yaml_iter_recurse(&server_iter, |
296 | 0 | &hostname_iter); |
297 | 0 | ogs_assert(ogs_yaml_iter_type( |
298 | 0 | &hostname_iter) != |
299 | 0 | YAML_MAPPING_NODE); |
300 | | |
301 | 0 | do { |
302 | 0 | if (ogs_yaml_iter_type( |
303 | 0 | &hostname_iter) == |
304 | 0 | YAML_SEQUENCE_NODE) { |
305 | 0 | if (!ogs_yaml_iter_next( |
306 | 0 | &hostname_iter)) |
307 | 0 | break; |
308 | 0 | } |
309 | | |
310 | 0 | ogs_assert(num < |
311 | 0 | OGS_MAX_NUM_OF_HOSTNAME); |
312 | 0 | hostname[num++] = |
313 | 0 | ogs_yaml_iter_value( |
314 | 0 | &hostname_iter); |
315 | 0 | } while (ogs_yaml_iter_type( |
316 | 0 | &hostname_iter) == |
317 | 0 | YAML_SEQUENCE_NODE); |
318 | 0 | } else if (!strcmp( |
319 | 0 | server_key, "advertise")) { |
320 | 0 | ogs_yaml_iter_t hostname_iter; |
321 | 0 | ogs_yaml_iter_recurse(&server_iter, |
322 | 0 | &hostname_iter); |
323 | 0 | ogs_assert(ogs_yaml_iter_type( |
324 | 0 | &hostname_iter) != |
325 | 0 | YAML_MAPPING_NODE); |
326 | | |
327 | 0 | do { |
328 | 0 | if (ogs_yaml_iter_type( |
329 | 0 | &hostname_iter) == |
330 | 0 | YAML_SEQUENCE_NODE) { |
331 | 0 | if (!ogs_yaml_iter_next( |
332 | 0 | &hostname_iter)) |
333 | 0 | break; |
334 | 0 | } |
335 | | |
336 | 0 | ogs_assert(num < |
337 | 0 | OGS_MAX_NUM_OF_HOSTNAME); |
338 | 0 | advertise[num_of_advertise++] = |
339 | 0 | ogs_yaml_iter_value( |
340 | 0 | &hostname_iter); |
341 | 0 | } while (ogs_yaml_iter_type( |
342 | 0 | &hostname_iter) == |
343 | 0 | YAML_SEQUENCE_NODE); |
344 | 0 | } else if (!strcmp(server_key, "port")) { |
345 | 0 | const char *v = |
346 | 0 | ogs_yaml_iter_value(&server_iter); |
347 | 0 | if (v) { |
348 | 0 | port = atoi(v); |
349 | 0 | self.pfcp_port = port; |
350 | 0 | } |
351 | 0 | } else if (!strcmp(server_key, "dev")) { |
352 | 0 | dev = ogs_yaml_iter_value(&server_iter); |
353 | 0 | } else if (!strcmp(server_key, "option")) { |
354 | 0 | rv = ogs_app_parse_sockopt_config( |
355 | 0 | &server_iter, &option); |
356 | 0 | if (rv != OGS_OK) { |
357 | 0 | ogs_error("ogs_app_parse_sockopt_" |
358 | 0 | "config() failed"); |
359 | 0 | return rv; |
360 | 0 | } |
361 | 0 | is_option = true; |
362 | 0 | } else if (!strcmp(server_key, "apn") || |
363 | 0 | !strcmp(server_key, "dnn")) { |
364 | | /* Skip */ |
365 | 0 | } else |
366 | 0 | ogs_warn("unknown key `%s`", |
367 | 0 | server_key); |
368 | 0 | } |
369 | | |
370 | | /* Add address information */ |
371 | 0 | addr = NULL; |
372 | 0 | for (i = 0; i < num; i++) { |
373 | 0 | rv = ogs_addaddrinfo(&addr, |
374 | 0 | family, hostname[i], port, 0); |
375 | 0 | ogs_assert(rv == OGS_OK); |
376 | 0 | } |
377 | | |
378 | | /* Add each address as a separate socknode */ |
379 | 0 | if (addr) { |
380 | 0 | ogs_sockaddr_t *current = addr; |
381 | 0 | while (current) { |
382 | 0 | if (current->ogs_sa_family == |
383 | 0 | AF_INET && |
384 | 0 | ogs_global_conf()-> |
385 | 0 | parameter.no_ipv4 == 0) { |
386 | 0 | ogs_socknode_add(&self.pfcp_list, |
387 | 0 | AF_INET, current, |
388 | 0 | is_option ? |
389 | 0 | &option : NULL); |
390 | 0 | } |
391 | 0 | if (current->ogs_sa_family == |
392 | 0 | AF_INET6 && |
393 | 0 | ogs_global_conf()-> |
394 | 0 | parameter.no_ipv6 == 0) { |
395 | 0 | ogs_socknode_add(&self.pfcp_list6, |
396 | 0 | AF_INET6, current, |
397 | 0 | is_option ? |
398 | 0 | &option : NULL); |
399 | 0 | } |
400 | 0 | current = current->next; |
401 | 0 | } |
402 | 0 | ogs_freeaddrinfo(addr); |
403 | 0 | } |
404 | | |
405 | | /* Process advertise addresses if needed */ |
406 | 0 | addr = NULL; |
407 | 0 | for (i = 0; i < num_of_advertise; i++) { |
408 | 0 | rv = ogs_addaddrinfo(&addr, |
409 | 0 | family, advertise[i], port, 0); |
410 | 0 | ogs_assert(rv == OGS_OK); |
411 | 0 | } |
412 | | |
413 | 0 | if (addr) { |
414 | 0 | if (ogs_global_conf()-> |
415 | 0 | parameter.no_ipv4 == 0 && |
416 | 0 | !self.pfcp_advertise) { |
417 | 0 | ogs_copyaddrinfo( |
418 | 0 | &self.pfcp_advertise, addr); |
419 | 0 | ogs_filteraddrinfo( |
420 | 0 | &self.pfcp_advertise, AF_INET); |
421 | 0 | } |
422 | 0 | if (ogs_global_conf()-> |
423 | 0 | parameter.no_ipv6 == 0 && |
424 | 0 | !self.pfcp_advertise6) { |
425 | 0 | ogs_copyaddrinfo( |
426 | 0 | &self.pfcp_advertise6, addr); |
427 | 0 | ogs_filteraddrinfo( |
428 | 0 | &self.pfcp_advertise6, |
429 | 0 | AF_INET6); |
430 | 0 | } |
431 | 0 | ogs_freeaddrinfo(addr); |
432 | 0 | } |
433 | | |
434 | | /* Bind to device if specified */ |
435 | 0 | if (dev) { |
436 | 0 | rv = ogs_socknode_probe( |
437 | 0 | ogs_global_conf()-> |
438 | 0 | parameter.no_ipv4 ? |
439 | 0 | NULL : &self.pfcp_list, |
440 | 0 | ogs_global_conf()-> |
441 | 0 | parameter.no_ipv6 ? |
442 | 0 | NULL : &self.pfcp_list6, |
443 | 0 | dev, self.pfcp_port, |
444 | 0 | is_option ? &option : NULL); |
445 | 0 | ogs_assert(rv == OGS_OK); |
446 | 0 | } |
447 | |
|
448 | 0 | } while (ogs_yaml_iter_type(&server_array) == |
449 | 0 | YAML_SEQUENCE_NODE); |
450 | 0 | } else if (!strcmp(pfcp_key, "client")) { |
451 | 0 | ogs_yaml_iter_t client_iter; |
452 | 0 | ogs_yaml_iter_recurse(&pfcp_iter, &client_iter); |
453 | 0 | while (ogs_yaml_iter_next(&client_iter)) { |
454 | 0 | const char *client_key = |
455 | 0 | ogs_yaml_iter_key(&client_iter); |
456 | 0 | ogs_assert(client_key); |
457 | 0 | if (!strcmp(client_key, remote)) { |
458 | 0 | ogs_yaml_iter_t remote_array, remote_iter; |
459 | 0 | ogs_yaml_iter_recurse( |
460 | 0 | &client_iter, &remote_array); |
461 | 0 | do { |
462 | 0 | ogs_pfcp_node_t *node = NULL; |
463 | 0 | ogs_sockaddr_t *addr = NULL; |
464 | 0 | int family = AF_UNSPEC; |
465 | 0 | int i, num = 0; |
466 | 0 | const char *hostname[ |
467 | 0 | OGS_MAX_NUM_OF_HOSTNAME]; |
468 | 0 | uint16_t port = self.pfcp_port; |
469 | 0 | uint16_t tac[OGS_MAX_NUM_OF_TAI] = {0,}; |
470 | 0 | int num_of_tac = 0; |
471 | 0 | const char *dnn[OGS_MAX_NUM_OF_DNN]; |
472 | 0 | int num_of_dnn = 0; |
473 | 0 | uint32_t e_cell_id[ |
474 | 0 | OGS_MAX_NUM_OF_CELL_ID] = {0,}; |
475 | 0 | int num_of_e_cell_id = 0; |
476 | 0 | uint64_t nr_cell_id[ |
477 | 0 | OGS_MAX_NUM_OF_CELL_ID] = {0,}; |
478 | 0 | int num_of_nr_cell_id = 0; |
479 | |
|
480 | 0 | if (ogs_yaml_iter_type(&remote_array) == |
481 | 0 | YAML_MAPPING_NODE) { |
482 | 0 | memcpy(&remote_iter, &remote_array, |
483 | 0 | sizeof(ogs_yaml_iter_t)); |
484 | 0 | } else if (ogs_yaml_iter_type( |
485 | 0 | &remote_array) == |
486 | 0 | YAML_SEQUENCE_NODE) { |
487 | 0 | if (!ogs_yaml_iter_next( |
488 | 0 | &remote_array)) |
489 | 0 | break; |
490 | 0 | ogs_yaml_iter_recurse(&remote_array, |
491 | 0 | &remote_iter); |
492 | 0 | } else if (ogs_yaml_iter_type( |
493 | 0 | &remote_array) == |
494 | 0 | YAML_SCALAR_NODE) { |
495 | 0 | break; |
496 | 0 | } else |
497 | 0 | ogs_assert_if_reached(); |
498 | | |
499 | 0 | while (ogs_yaml_iter_next( |
500 | 0 | &remote_iter)) { |
501 | 0 | const char *remote_key = |
502 | 0 | ogs_yaml_iter_key(&remote_iter); |
503 | 0 | ogs_assert(remote_key); |
504 | 0 | if (!strcmp(remote_key, "family")) { |
505 | 0 | const char *v = |
506 | 0 | ogs_yaml_iter_value( |
507 | 0 | &remote_iter); |
508 | 0 | if (v) family = atoi(v); |
509 | 0 | if (family != AF_UNSPEC && |
510 | 0 | family != AF_INET && |
511 | 0 | family != AF_INET6) { |
512 | 0 | ogs_warn( |
513 | 0 | "Ignore family(%d) : " |
514 | 0 | "AF_UNSPEC(%d), " |
515 | 0 | "AF_INET(%d), " |
516 | 0 | "AF_INET6(%d) ", |
517 | 0 | family, AF_UNSPEC, |
518 | 0 | AF_INET, AF_INET6); |
519 | 0 | family = AF_UNSPEC; |
520 | 0 | } |
521 | 0 | } else if (!strcmp(remote_key, |
522 | 0 | "address")) { |
523 | 0 | ogs_yaml_iter_t hostname_iter; |
524 | 0 | ogs_yaml_iter_recurse( |
525 | 0 | &remote_iter, |
526 | 0 | &hostname_iter); |
527 | 0 | ogs_assert(ogs_yaml_iter_type( |
528 | 0 | &hostname_iter) != |
529 | 0 | YAML_MAPPING_NODE); |
530 | | |
531 | 0 | do { |
532 | 0 | if (ogs_yaml_iter_type( |
533 | 0 | &hostname_iter) == |
534 | 0 | YAML_SEQUENCE_NODE) { |
535 | 0 | if (!ogs_yaml_iter_next( |
536 | 0 | &hostname_iter)) |
537 | 0 | break; |
538 | 0 | } |
539 | | |
540 | 0 | ogs_assert(num < |
541 | 0 | OGS_MAX_NUM_OF_HOSTNAME); |
542 | 0 | hostname[num++] = |
543 | 0 | ogs_yaml_iter_value( |
544 | 0 | &hostname_iter); |
545 | 0 | } while (ogs_yaml_iter_type( |
546 | 0 | &hostname_iter) == |
547 | 0 | YAML_SEQUENCE_NODE); |
548 | 0 | } else if (!strcmp(remote_key, |
549 | 0 | "advertise")) { |
550 | | /* Nothing in client */ |
551 | 0 | } else if (!strcmp(remote_key, |
552 | 0 | "port")) { |
553 | 0 | const char *v = |
554 | 0 | ogs_yaml_iter_value( |
555 | 0 | &remote_iter); |
556 | 0 | if (v) port = atoi(v); |
557 | 0 | } else if (!strcmp(remote_key, |
558 | 0 | "tac")) { |
559 | 0 | ogs_yaml_iter_t tac_iter; |
560 | 0 | ogs_yaml_iter_recurse( |
561 | 0 | &remote_iter, |
562 | 0 | &tac_iter); |
563 | 0 | ogs_assert(ogs_yaml_iter_type( |
564 | 0 | &tac_iter) != |
565 | 0 | YAML_MAPPING_NODE); |
566 | | |
567 | 0 | do { |
568 | 0 | const char *v = NULL; |
569 | |
|
570 | 0 | ogs_assert(num_of_tac < |
571 | 0 | OGS_MAX_NUM_OF_TAI); |
572 | 0 | if (ogs_yaml_iter_type( |
573 | 0 | &tac_iter) == |
574 | 0 | YAML_SEQUENCE_NODE) { |
575 | 0 | if (!ogs_yaml_iter_next( |
576 | 0 | &tac_iter)) |
577 | 0 | break; |
578 | 0 | } |
579 | | |
580 | 0 | v = ogs_yaml_iter_value( |
581 | 0 | &tac_iter); |
582 | 0 | if (v) { |
583 | 0 | tac[num_of_tac] = |
584 | 0 | atoi(v); |
585 | 0 | num_of_tac++; |
586 | 0 | } |
587 | 0 | } while (ogs_yaml_iter_type( |
588 | 0 | &tac_iter) == |
589 | 0 | YAML_SEQUENCE_NODE); |
590 | 0 | } else if (!strcmp( |
591 | 0 | remote_key, "apn") || |
592 | 0 | !strcmp( |
593 | 0 | remote_key, "dnn")) { |
594 | 0 | ogs_yaml_iter_t dnn_iter; |
595 | 0 | ogs_yaml_iter_recurse( |
596 | 0 | &remote_iter, |
597 | 0 | &dnn_iter); |
598 | 0 | ogs_assert(ogs_yaml_iter_type( |
599 | 0 | &dnn_iter) != |
600 | 0 | YAML_MAPPING_NODE); |
601 | | |
602 | 0 | do { |
603 | 0 | const char *v = NULL; |
604 | |
|
605 | 0 | ogs_assert(num_of_dnn < |
606 | 0 | OGS_MAX_NUM_OF_DNN); |
607 | 0 | if (ogs_yaml_iter_type( |
608 | 0 | &dnn_iter) == |
609 | 0 | YAML_SEQUENCE_NODE) { |
610 | 0 | if (!ogs_yaml_iter_next( |
611 | 0 | &dnn_iter)) |
612 | 0 | break; |
613 | 0 | } |
614 | | |
615 | 0 | v = ogs_yaml_iter_value( |
616 | 0 | &dnn_iter); |
617 | 0 | if (v) { |
618 | 0 | dnn[num_of_dnn] = v; |
619 | 0 | num_of_dnn++; |
620 | 0 | } |
621 | 0 | } while (ogs_yaml_iter_type( |
622 | 0 | &dnn_iter) == |
623 | 0 | YAML_SEQUENCE_NODE); |
624 | 0 | } else if (!strcmp(remote_key, |
625 | 0 | "e_cell_id")) { |
626 | 0 | ogs_yaml_iter_t e_cell_id_iter; |
627 | 0 | ogs_yaml_iter_recurse( |
628 | 0 | &remote_iter, |
629 | 0 | &e_cell_id_iter); |
630 | 0 | ogs_assert(ogs_yaml_iter_type( |
631 | 0 | &e_cell_id_iter) != |
632 | 0 | YAML_MAPPING_NODE); |
633 | | |
634 | 0 | do { |
635 | 0 | const char *v = NULL; |
636 | |
|
637 | 0 | ogs_assert( |
638 | 0 | num_of_e_cell_id < |
639 | 0 | OGS_MAX_NUM_OF_ENB_ID); |
640 | 0 | if (ogs_yaml_iter_type( |
641 | 0 | &e_cell_id_iter) == |
642 | 0 | YAML_SEQUENCE_NODE) { |
643 | 0 | if (!ogs_yaml_iter_next( |
644 | 0 | &e_cell_id_iter)) |
645 | 0 | break; |
646 | 0 | } |
647 | | |
648 | 0 | v = ogs_yaml_iter_value( |
649 | 0 | &e_cell_id_iter); |
650 | 0 | if (v) { |
651 | 0 | e_cell_id[ |
652 | 0 | num_of_e_cell_id] = |
653 | 0 | ogs_uint64_from_string_hexadecimal( |
654 | 0 | (char*)v); |
655 | 0 | num_of_e_cell_id++; |
656 | 0 | } |
657 | 0 | } while (ogs_yaml_iter_type( |
658 | 0 | &e_cell_id_iter) == |
659 | 0 | YAML_SEQUENCE_NODE); |
660 | 0 | } else if (!strcmp(remote_key, |
661 | 0 | "nr_cell_id")) { |
662 | 0 | ogs_yaml_iter_t nr_cell_id_iter; |
663 | 0 | ogs_yaml_iter_recurse( |
664 | 0 | &remote_iter, |
665 | 0 | &nr_cell_id_iter); |
666 | 0 | ogs_assert(ogs_yaml_iter_type( |
667 | 0 | &nr_cell_id_iter) != |
668 | 0 | YAML_MAPPING_NODE); |
669 | | |
670 | 0 | do { |
671 | 0 | const char *v = NULL; |
672 | |
|
673 | 0 | ogs_assert( |
674 | 0 | num_of_nr_cell_id < |
675 | 0 | OGS_MAX_NUM_OF_ENB_ID); |
676 | 0 | if (ogs_yaml_iter_type( |
677 | 0 | &nr_cell_id_iter) == |
678 | 0 | YAML_SEQUENCE_NODE) { |
679 | 0 | if (!ogs_yaml_iter_next( |
680 | 0 | &nr_cell_id_iter)) |
681 | 0 | break; |
682 | 0 | } |
683 | | |
684 | 0 | v = ogs_yaml_iter_value( |
685 | 0 | &nr_cell_id_iter); |
686 | 0 | if (v) { |
687 | 0 | nr_cell_id[ |
688 | 0 | num_of_nr_cell_id] = |
689 | 0 | ogs_uint64_from_string_hexadecimal( |
690 | 0 | (char*)v); |
691 | 0 | num_of_nr_cell_id++; |
692 | 0 | } |
693 | 0 | } while (ogs_yaml_iter_type( |
694 | 0 | &nr_cell_id_iter) == |
695 | 0 | YAML_SEQUENCE_NODE); |
696 | 0 | } else |
697 | 0 | ogs_warn("unknown key `%s`", |
698 | 0 | remote_key); |
699 | 0 | } |
700 | | |
701 | 0 | addr = NULL; |
702 | 0 | for (i = 0; i < num; i++) { |
703 | 0 | rv = ogs_addaddrinfo(&addr, family, |
704 | 0 | hostname[i], port, 0); |
705 | 0 | ogs_assert(rv == OGS_OK); |
706 | 0 | } |
707 | | |
708 | 0 | ogs_filter_ip_version(&addr, |
709 | 0 | ogs_global_conf()->parameter. |
710 | 0 | no_ipv4, |
711 | 0 | ogs_global_conf()->parameter. |
712 | 0 | no_ipv6, |
713 | 0 | ogs_global_conf()->parameter. |
714 | 0 | prefer_ipv4); |
715 | |
|
716 | 0 | if (addr == NULL) continue; |
717 | | |
718 | 0 | node = ogs_pfcp_node_new(addr); |
719 | 0 | ogs_assert(node); |
720 | 0 | ogs_list_add( |
721 | 0 | &self.pfcp_peer_list, node); |
722 | |
|
723 | 0 | node->num_of_tac = num_of_tac; |
724 | 0 | if (num_of_tac != 0) |
725 | 0 | memcpy(node->tac, |
726 | 0 | tac, sizeof(node->tac)); |
727 | |
|
728 | 0 | node->num_of_dnn = num_of_dnn; |
729 | 0 | if (num_of_dnn != 0) |
730 | 0 | memcpy(node->dnn, |
731 | 0 | dnn, sizeof(node->dnn)); |
732 | |
|
733 | 0 | node->num_of_e_cell_id = |
734 | 0 | num_of_e_cell_id; |
735 | 0 | if (num_of_e_cell_id != 0) |
736 | 0 | memcpy(node->e_cell_id, e_cell_id, |
737 | 0 | sizeof(node->e_cell_id)); |
738 | |
|
739 | 0 | node->num_of_nr_cell_id = |
740 | 0 | num_of_nr_cell_id; |
741 | 0 | if (num_of_nr_cell_id != 0) |
742 | 0 | memcpy(node->nr_cell_id, nr_cell_id, |
743 | 0 | sizeof(node->nr_cell_id)); |
744 | |
|
745 | 0 | } while (ogs_yaml_iter_type( |
746 | 0 | &remote_array) == |
747 | 0 | YAML_SEQUENCE_NODE); |
748 | 0 | } |
749 | 0 | } |
750 | 0 | } else |
751 | 0 | ogs_warn("unknown key `%s`", pfcp_key); |
752 | 0 | } |
753 | 0 | } else if (!strcmp(local_key, "session")) { |
754 | 0 | ogs_yaml_iter_t subnet_array, subnet_iter; |
755 | 0 | ogs_yaml_iter_recurse(&local_iter, &subnet_array); |
756 | |
|
757 | 0 | do { |
758 | 0 | ogs_pfcp_subnet_t *subnet = NULL; |
759 | 0 | const char *ipstr = NULL; |
760 | 0 | const char *gateway = NULL; |
761 | 0 | const char *mask_or_numbits = NULL; |
762 | 0 | const char *dnn = NULL; |
763 | 0 | const char *dev = self.tun_ifname; |
764 | 0 | const char *low[OGS_MAX_NUM_OF_SUBNET_RANGE]; |
765 | 0 | const char *high[OGS_MAX_NUM_OF_SUBNET_RANGE]; |
766 | 0 | int i, num = 0; |
767 | |
|
768 | 0 | memset(low, 0, sizeof(low)); |
769 | 0 | memset(high, 0, sizeof(high)); |
770 | |
|
771 | 0 | if (ogs_yaml_iter_type(&subnet_array) == |
772 | 0 | YAML_MAPPING_NODE) { |
773 | 0 | memcpy(&subnet_iter, &subnet_array, |
774 | 0 | sizeof(ogs_yaml_iter_t)); |
775 | 0 | } else if (ogs_yaml_iter_type(&subnet_array) == |
776 | 0 | YAML_SEQUENCE_NODE) { |
777 | 0 | if (!ogs_yaml_iter_next(&subnet_array)) |
778 | 0 | break; |
779 | 0 | ogs_yaml_iter_recurse(&subnet_array, &subnet_iter); |
780 | 0 | } else if (ogs_yaml_iter_type(&subnet_array) == |
781 | 0 | YAML_SCALAR_NODE) { |
782 | 0 | break; |
783 | 0 | } else |
784 | 0 | ogs_assert_if_reached(); |
785 | | |
786 | 0 | while (ogs_yaml_iter_next(&subnet_iter)) { |
787 | 0 | const char *subnet_key = |
788 | 0 | ogs_yaml_iter_key(&subnet_iter); |
789 | 0 | ogs_assert(subnet_key); |
790 | 0 | if (!strcmp(subnet_key, "subnet")) { |
791 | 0 | char *v = |
792 | 0 | (char *)ogs_yaml_iter_value(&subnet_iter); |
793 | 0 | if (v) { |
794 | 0 | ipstr = (const char *)strsep(&v, "/"); |
795 | 0 | if (ipstr) { |
796 | 0 | mask_or_numbits = (const char *)v; |
797 | 0 | } |
798 | 0 | } |
799 | 0 | } else if (!strcmp(subnet_key, "gateway")) { |
800 | 0 | gateway = ogs_yaml_iter_value(&subnet_iter); |
801 | 0 | } else if (!strcmp(subnet_key, "apn") || |
802 | 0 | !strcmp(subnet_key, "dnn")) { |
803 | 0 | dnn = ogs_yaml_iter_value(&subnet_iter); |
804 | 0 | } else if (!strcmp(subnet_key, "dev")) { |
805 | 0 | dev = ogs_yaml_iter_value(&subnet_iter); |
806 | 0 | } else if (!strcmp(subnet_key, "range")) { |
807 | 0 | ogs_yaml_iter_t range_iter; |
808 | 0 | ogs_yaml_iter_recurse( |
809 | 0 | &subnet_iter, &range_iter); |
810 | 0 | ogs_assert(ogs_yaml_iter_type(&range_iter) != |
811 | 0 | YAML_MAPPING_NODE); |
812 | 0 | do { |
813 | 0 | char *v = NULL; |
814 | |
|
815 | 0 | if (ogs_yaml_iter_type(&range_iter) == |
816 | 0 | YAML_SEQUENCE_NODE) { |
817 | 0 | if (!ogs_yaml_iter_next(&range_iter)) |
818 | 0 | break; |
819 | 0 | } |
820 | | |
821 | 0 | v = (char *) |
822 | 0 | ogs_yaml_iter_value(&range_iter); |
823 | 0 | if (v) { |
824 | 0 | ogs_assert(num < |
825 | 0 | OGS_MAX_NUM_OF_SUBNET_RANGE); |
826 | 0 | low[num] = |
827 | 0 | (const char *)strsep(&v, "-"); |
828 | 0 | if (low[num] && strlen(low[num]) == 0) |
829 | 0 | low[num] = NULL; |
830 | |
|
831 | 0 | high[num] = (const char *)v; |
832 | 0 | if (high[num] && strlen(high[num]) == 0) |
833 | 0 | high[num] = NULL; |
834 | 0 | } |
835 | | |
836 | 0 | if (low[num] || high[num]) num++; |
837 | 0 | } while ( |
838 | 0 | ogs_yaml_iter_type(&range_iter) == |
839 | 0 | YAML_SEQUENCE_NODE); |
840 | 0 | } else |
841 | 0 | ogs_warn("unknown key `%s`", subnet_key); |
842 | 0 | } |
843 | | |
844 | 0 | subnet = ogs_pfcp_subnet_add( |
845 | 0 | ipstr, mask_or_numbits, gateway, dnn, dev); |
846 | 0 | ogs_assert(subnet); |
847 | | |
848 | 0 | subnet->num_of_range = num; |
849 | 0 | for (i = 0; i < subnet->num_of_range; i++) { |
850 | 0 | subnet->range[i].low = low[i]; |
851 | 0 | subnet->range[i].high = high[i]; |
852 | 0 | } |
853 | |
|
854 | 0 | } while (ogs_yaml_iter_type(&subnet_array) == |
855 | 0 | YAML_SEQUENCE_NODE); |
856 | 0 | } |
857 | 0 | } |
858 | 0 | } |
859 | 0 | } |
860 | | |
861 | 0 | rv = ogs_pfcp_context_validation(local); |
862 | 0 | if (rv != OGS_OK) return rv; |
863 | | |
864 | 0 | return OGS_OK; |
865 | 0 | } |
866 | | |
867 | | /****************************************************************************** |
868 | | * ogs_pfcp_node_new() |
869 | | * - Initialize node_id.type to OGS_PFCP_NODE_ID_UNKNOWN |
870 | | * - So the node can later be updated with a real Node ID via |
871 | | * ogs_pfcp_node_merge() once we learn it from PFCP messages. |
872 | | ******************************************************************************/ |
873 | | ogs_pfcp_node_t *ogs_pfcp_node_new(ogs_sockaddr_t *config_addr) |
874 | 0 | { |
875 | 0 | int rv; |
876 | 0 | ogs_pfcp_node_t *node = NULL; |
877 | |
|
878 | 0 | ogs_pool_alloc(&ogs_pfcp_node_pool, &node); |
879 | 0 | if (!node) { |
880 | 0 | ogs_error("No memory: ogs_pool_alloc() failed [%s]", |
881 | 0 | ogs_sockaddr_to_string_static(config_addr)); |
882 | 0 | return NULL; |
883 | 0 | } |
884 | 0 | memset(node, 0, sizeof(ogs_pfcp_node_t)); |
885 | | |
886 | | /* Store config_addr, if any */ |
887 | 0 | node->config_addr = config_addr; |
888 | | |
889 | | /* If config_addr is given, copy it immediately into addr_list */ |
890 | 0 | if (config_addr) { |
891 | 0 | rv = ogs_copyaddrinfo(&node->addr_list, config_addr); |
892 | 0 | if (rv != OGS_OK) { |
893 | 0 | ogs_error("ogs_copyaddrinfo() failed"); |
894 | 0 | ogs_pool_free(&ogs_pfcp_node_pool, node); |
895 | 0 | return NULL; |
896 | 0 | } |
897 | 0 | } |
898 | | |
899 | | /* |
900 | | * Initialize node->node_id to UNKNOWN, meaning "no Node ID assigned yet". |
901 | | */ |
902 | 0 | memset(&node->node_id, 0, sizeof(node->node_id)); |
903 | 0 | node->node_id.type = OGS_PFCP_NODE_ID_UNKNOWN; |
904 | |
|
905 | 0 | ogs_list_init(&node->local_list); |
906 | 0 | ogs_list_init(&node->remote_list); |
907 | |
|
908 | 0 | return node; |
909 | 0 | } |
910 | | |
911 | | void ogs_pfcp_node_free(ogs_pfcp_node_t *node) |
912 | 0 | { |
913 | 0 | ogs_assert(node); |
914 | | |
915 | 0 | ogs_pfcp_xact_delete_all(node); |
916 | |
|
917 | 0 | ogs_freeaddrinfo(node->config_addr); |
918 | 0 | ogs_freeaddrinfo(node->addr_list); |
919 | |
|
920 | 0 | ogs_pool_free(&ogs_pfcp_node_pool, node); |
921 | 0 | } |
922 | | |
923 | | /****************************************************************************** |
924 | | * ogs_pfcp_node_add() |
925 | | * - Create a new PFCP node, then call ogs_pfcp_node_merge() to handle |
926 | | * IPv4/IPv6 or FQDN logic. |
927 | | ******************************************************************************/ |
928 | | ogs_pfcp_node_t *ogs_pfcp_node_add(ogs_list_t *list, |
929 | | ogs_pfcp_node_id_t *node_id, ogs_sockaddr_t *from) |
930 | 0 | { |
931 | 0 | ogs_pfcp_node_t *node = NULL; |
932 | |
|
933 | 0 | ogs_assert(list); |
934 | 0 | ogs_assert(node_id && from); |
935 | | |
936 | | /* |
937 | | * We only handle IPv4, IPv6, and FQDN types here. If the incoming |
938 | | * node_id has any other type, we treat it as invalid. This ensures |
939 | | * we do not merge a node with an unsupported PFCP Node ID. |
940 | | */ |
941 | 0 | if (node_id->type != OGS_PFCP_NODE_ID_IPV4 && |
942 | 0 | node_id->type != OGS_PFCP_NODE_ID_IPV6 && |
943 | 0 | node_id->type != OGS_PFCP_NODE_ID_FQDN) { |
944 | 0 | ogs_error("Invalid PFCP Node Type = %d", node_id->type); |
945 | 0 | return NULL; |
946 | 0 | } |
947 | | |
948 | | /* Create node with no config_addr initially */ |
949 | 0 | node = ogs_pfcp_node_new(NULL); |
950 | 0 | if (!node) { |
951 | 0 | ogs_error("No memory: ogs_pfcp_node_add() failed node_id:%s from:%s", |
952 | 0 | ogs_pfcp_node_id_to_string_static(node_id), |
953 | 0 | ogs_sockaddr_to_string_static(from)); |
954 | 0 | return NULL; |
955 | 0 | } |
956 | | |
957 | | /* Set node->node_id, reset last_dns_refresh. */ |
958 | 0 | memcpy(&node->node_id, node_id, sizeof(node->node_id)); |
959 | 0 | node->last_dns_refresh = 0; |
960 | | |
961 | | /* Merge addresses => fill node->addr_list if conditions are met */ |
962 | 0 | if (ogs_pfcp_node_merge(node, node_id, from) != OGS_OK) { |
963 | 0 | ogs_error("ogs_pfcp_node_merge() failed node_id [%s] from [%s]", |
964 | 0 | ogs_pfcp_node_id_to_string_static(node_id), |
965 | 0 | ogs_sockaddr_to_string_static(from)); |
966 | 0 | ogs_pool_free(&ogs_pfcp_node_pool, node); |
967 | 0 | return NULL; |
968 | 0 | } |
969 | | |
970 | 0 | ogs_list_add(list, node); |
971 | |
|
972 | 0 | return node; |
973 | 0 | } |
974 | | |
975 | | /****************************************************************************** |
976 | | * ogs_pfcp_node_find() |
977 | | * - No DNS logic here. Merely finds a node by node_id (if provided) and |
978 | | * checks if 'from' address is in node->addr_list. |
979 | | ******************************************************************************/ |
980 | | ogs_pfcp_node_t *ogs_pfcp_node_find(ogs_list_t *list, |
981 | | ogs_pfcp_node_id_t *node_id, ogs_sockaddr_t *from) |
982 | 0 | { |
983 | 0 | ogs_pfcp_node_t *cur; |
984 | |
|
985 | 0 | ogs_assert(list); |
986 | 0 | ogs_assert(node_id || from); |
987 | | |
988 | 0 | ogs_list_for_each(list, cur) { |
989 | | /* |
990 | | * If the node currently has a known Node ID (not UNKNOWN) |
991 | | * and the caller provided a node_id to match, then compare them. |
992 | | * If they do not match, skip this node. This allows config-based nodes |
993 | | * (with an UNKNOWN node_id) to be found by IP address alone, |
994 | | * while nodes with a definite ID must match the incoming node_id. |
995 | | */ |
996 | 0 | if (cur->node_id.type != OGS_PFCP_NODE_ID_UNKNOWN && node_id) { |
997 | 0 | if (!ogs_pfcp_node_id_compare(&cur->node_id, node_id)) |
998 | 0 | continue; |
999 | 0 | } |
1000 | 0 | if (!from) |
1001 | 0 | return cur; |
1002 | | |
1003 | | /* Check if 'from' is in cur->addr_list. */ |
1004 | 0 | if (ogs_sockaddr_check_any_match(cur->addr_list, NULL, |
1005 | 0 | from, /* compare_port= */ true)) { |
1006 | 0 | return cur; |
1007 | 0 | } |
1008 | 0 | } |
1009 | | |
1010 | | /* No match found. */ |
1011 | 0 | return NULL; |
1012 | 0 | } |
1013 | | |
1014 | | /****************************************************************************** |
1015 | | * ogs_pfcp_node_merge(): |
1016 | | * - If node_id changes to FQDN, we check last_dns_refresh. |
1017 | | * => If 0, do an immediate DNS resolution (first time). |
1018 | | * => If >= 300 seconds passed, do a periodic refresh. |
1019 | | * => Otherwise, skip. |
1020 | | * - If node_id changes to IPv4/IPv6, convert IP addresses immediately. |
1021 | | * - Merge the 'from' address into addr_list if provided. |
1022 | | ******************************************************************************/ |
1023 | | int ogs_pfcp_node_merge(ogs_pfcp_node_t *node, |
1024 | | ogs_pfcp_node_id_t *node_id, ogs_sockaddr_t *from) |
1025 | 0 | { |
1026 | 0 | ogs_sockaddr_t single; |
1027 | 0 | ogs_sockaddr_t *tmp_list = NULL; |
1028 | |
|
1029 | 0 | ogs_assert(node); |
1030 | 0 | ogs_assert(node_id || from); |
1031 | | |
1032 | 0 | if (node_id) { |
1033 | | /* |
1034 | | * We only handle IPv4, IPv6, and FQDN types here. If the incoming |
1035 | | * node_id has any other type, we treat it as invalid. This ensures |
1036 | | * we do not merge a node with an unsupported PFCP Node ID. |
1037 | | */ |
1038 | 0 | if (node_id->type != OGS_PFCP_NODE_ID_IPV4 && |
1039 | 0 | node_id->type != OGS_PFCP_NODE_ID_IPV6 && |
1040 | 0 | node_id->type != OGS_PFCP_NODE_ID_FQDN) { |
1041 | 0 | ogs_error("Invalid PFCP Node Type = %d", node_id->type); |
1042 | 0 | return OGS_ERROR; |
1043 | 0 | } |
1044 | | |
1045 | | /* Check if node_id is different from node->node_id. */ |
1046 | 0 | if (!ogs_pfcp_node_id_compare(&node->node_id, node_id)) { |
1047 | | /* Update the node's ID and reset the refresh timestamp. */ |
1048 | 0 | memcpy(&node->node_id, node_id, sizeof(node->node_id)); |
1049 | 0 | node->last_dns_refresh = 0; |
1050 | 0 | } |
1051 | | |
1052 | | /* If FQDN, do a DNS lookup (immediate or periodic). */ |
1053 | 0 | if (node->node_id.type == OGS_PFCP_NODE_ID_FQDN) { |
1054 | | /* |
1055 | | * We perform a DNS resolution if 'last_dns_refresh' is zero, which means |
1056 | | * this FQDN node has never been resolved yet (first-time resolution), or if |
1057 | | * at least 300 seconds have passed since the last refresh. Without checking |
1058 | | * '== 0', a newly created FQDN node might skip resolution if 'now' is less |
1059 | | * than the 300-second threshold. |
1060 | | */ |
1061 | 0 | ogs_time_t now = ogs_time_now(); |
1062 | | |
1063 | | /* For 300-second refresh interval in microseconds. */ |
1064 | 0 | #define OGS_PFCP_NODE_DNS_REFRESH_INTERVAL \ |
1065 | 0 | ((ogs_time_t)(300) * OGS_USEC_PER_SEC) |
1066 | 0 | if (node->last_dns_refresh == 0 || |
1067 | 0 | (now - node->last_dns_refresh) >= |
1068 | 0 | OGS_PFCP_NODE_DNS_REFRESH_INTERVAL) { |
1069 | |
|
1070 | 0 | tmp_list = ogs_pfcp_node_id_to_addrinfo(&node->node_id); |
1071 | 0 | if (!tmp_list) { |
1072 | 0 | ogs_error("DNS resolution failed for FQDN [%s]", |
1073 | 0 | node->node_id.fqdn); |
1074 | 0 | return OGS_ERROR; |
1075 | 0 | } |
1076 | | |
1077 | 0 | ogs_freeaddrinfo(node->addr_list); |
1078 | 0 | node->addr_list = tmp_list; |
1079 | 0 | node->last_dns_refresh = now; |
1080 | 0 | node->current_addr = NULL; |
1081 | 0 | tmp_list = NULL; |
1082 | 0 | } |
1083 | 0 | } |
1084 | | /* If IPv4/IPv6, convert immediately. */ |
1085 | 0 | else if (node->node_id.type == OGS_PFCP_NODE_ID_IPV4 || |
1086 | 0 | node->node_id.type == OGS_PFCP_NODE_ID_IPV6) { |
1087 | 0 | tmp_list = ogs_pfcp_node_id_to_addrinfo(&node->node_id); |
1088 | 0 | if (!tmp_list) { |
1089 | 0 | ogs_error("Failed to convert node ID to address info"); |
1090 | 0 | return OGS_ERROR; |
1091 | 0 | } |
1092 | 0 | ogs_merge_addrinfo(&node->addr_list, tmp_list); |
1093 | 0 | ogs_freeaddrinfo(tmp_list); |
1094 | 0 | tmp_list = NULL; |
1095 | 0 | } |
1096 | 0 | else { |
1097 | | /* Not IPv4, IPv6, or FQDN => invalid node type. */ |
1098 | 0 | ogs_error("Invalid Node ID type [%d]", node->node_id.type); |
1099 | 0 | return OGS_ERROR; |
1100 | 0 | } |
1101 | 0 | } |
1102 | | |
1103 | | /* Merge 'from' into addr_list if provided. */ |
1104 | 0 | if (from) { |
1105 | 0 | memcpy(&single, from, sizeof(single)); |
1106 | 0 | single.next = NULL; |
1107 | 0 | ogs_merge_addrinfo(&node->addr_list, &single); |
1108 | 0 | } |
1109 | |
|
1110 | 0 | return OGS_OK; |
1111 | 0 | } |
1112 | | |
1113 | | void ogs_pfcp_node_remove(ogs_list_t *list, ogs_pfcp_node_t *node) |
1114 | 0 | { |
1115 | 0 | ogs_assert(list); |
1116 | 0 | ogs_assert(node); |
1117 | | |
1118 | 0 | ogs_list_remove(list, node); |
1119 | 0 | ogs_pfcp_node_free(node); |
1120 | 0 | } |
1121 | | |
1122 | | void ogs_pfcp_node_remove_all(ogs_list_t *list) |
1123 | 0 | { |
1124 | 0 | ogs_pfcp_node_t *node = NULL, *next_node = NULL; |
1125 | |
|
1126 | 0 | ogs_assert(list); |
1127 | | |
1128 | 0 | ogs_list_for_each_safe(list, next_node, node) |
1129 | 0 | ogs_pfcp_node_remove(list, node); |
1130 | 0 | } |
1131 | | |
1132 | | /****************************************************************************** |
1133 | | * Compare two node IDs for equality. Returns true if they match, else false. |
1134 | | ******************************************************************************/ |
1135 | | bool ogs_pfcp_node_id_compare( |
1136 | | const ogs_pfcp_node_id_t *id1, const ogs_pfcp_node_id_t *id2) |
1137 | 0 | { |
1138 | 0 | if (id1->type != id2->type) |
1139 | 0 | return false; /* Types do not match */ |
1140 | | |
1141 | 0 | switch (id1->type) { |
1142 | 0 | case OGS_PFCP_NODE_ID_IPV4: |
1143 | 0 | if (id1->addr != id2->addr) return false; |
1144 | 0 | return true; |
1145 | | |
1146 | 0 | case OGS_PFCP_NODE_ID_IPV6: |
1147 | 0 | if (memcmp(id1->addr6, id2->addr6, OGS_IPV6_LEN) != 0) |
1148 | 0 | return false; |
1149 | 0 | return true; |
1150 | | |
1151 | 0 | case OGS_PFCP_NODE_ID_FQDN: |
1152 | 0 | if (strcmp(id1->fqdn, id2->fqdn) != 0) |
1153 | 0 | return false; |
1154 | 0 | return true; |
1155 | | |
1156 | 0 | default: |
1157 | 0 | ogs_error("Unexpected Node Type [%d]", id1->type); |
1158 | 0 | ogs_abort(); |
1159 | 0 | return false; /* Unknown types do not match */ |
1160 | 0 | } |
1161 | 0 | } |
1162 | | |
1163 | | ogs_gtpu_resource_t *ogs_pfcp_find_gtpu_resource(ogs_list_t *list, |
1164 | | char *dnn, ogs_pfcp_interface_t source_interface) |
1165 | 0 | { |
1166 | 0 | ogs_gtpu_resource_t *resource = NULL; |
1167 | |
|
1168 | 0 | ogs_assert(list); |
1169 | | |
1170 | 0 | ogs_list_for_each(list, resource) { |
1171 | 0 | bool match = true; |
1172 | |
|
1173 | 0 | if (resource->info.assoni && |
1174 | 0 | strlen(resource->info.network_instance) && |
1175 | 0 | dnn && strlen(dnn) && |
1176 | 0 | ogs_strcasecmp(dnn, resource->info.network_instance) != 0) { |
1177 | 0 | match = false; |
1178 | 0 | } |
1179 | |
|
1180 | 0 | if (resource->info.assosi && |
1181 | 0 | resource->info.source_interface <= OGS_PFCP_INTERFACE_LI_FUNCTION && |
1182 | 0 | source_interface <= OGS_PFCP_INTERFACE_LI_FUNCTION && |
1183 | 0 | source_interface != resource->info.source_interface) { |
1184 | 0 | match = false; |
1185 | 0 | } |
1186 | |
|
1187 | 0 | if (match == true) return resource; |
1188 | 0 | } |
1189 | | |
1190 | 0 | return NULL; |
1191 | 0 | } |
1192 | | |
1193 | | /* |
1194 | | * GTP-U peer nodes in gtpu_peer_list are created on demand by |
1195 | | * ogs_pfcp_setup_far_gtpu_node()/ogs_pfcp_setup_pdr_gtpu_node() and |
1196 | | * shared by all FARs/PDRs carrying the same IP. gnode->gtpu_ref_count |
1197 | | * counts those FARs/PDRs; when the last one is removed, the peer is |
1198 | | * removed from gtpu_peer_list and returned to the pool. Without this, |
1199 | | * a peer whose eNB/gNB was renumbered would occupy a pool entry until |
1200 | | * the process exits and repeated IP changes would eventually exhaust |
1201 | | * `global.max.gtp_peer`. |
1202 | | */ |
1203 | | static void gtpu_peer_release(ogs_gtp_node_t *gnode) |
1204 | 0 | { |
1205 | 0 | ogs_assert(gnode); |
1206 | 0 | ogs_assert(gnode->gtpu_ref_count > 0); |
1207 | | |
1208 | 0 | if (--gnode->gtpu_ref_count == 0) |
1209 | 0 | ogs_gtp_node_remove(&ogs_gtp_self()->gtpu_peer_list, gnode); |
1210 | 0 | } |
1211 | | |
1212 | | int ogs_pfcp_setup_far_gtpu_node(ogs_pfcp_far_t *far) |
1213 | 0 | { |
1214 | 0 | int rv; |
1215 | 0 | ogs_ip_t ip; |
1216 | 0 | ogs_gtp_node_t *gnode = NULL; |
1217 | |
|
1218 | 0 | ogs_assert(far); |
1219 | | |
1220 | 0 | ogs_pfcp_outer_header_creation_to_ip(&far->outer_header_creation, &ip); |
1221 | | |
1222 | | /* No Outer Header Creation */ |
1223 | 0 | if (ip.len == 0) return OGS_DONE; |
1224 | | |
1225 | 0 | gnode = ogs_gtp_node_find_by_ip(&ogs_gtp_self()->gtpu_peer_list, &ip); |
1226 | 0 | if (!gnode) { |
1227 | 0 | gnode = ogs_gtp_node_add_by_ip( |
1228 | 0 | &ogs_gtp_self()->gtpu_peer_list, &ip, ogs_gtp_self()->gtpu_port); |
1229 | 0 | if (!gnode) { |
1230 | 0 | ogs_error("Cannot allocate GTP-U peer " |
1231 | 0 | "[FAR-ID:%u TEID:0x%x]", |
1232 | 0 | far->id, far->outer_header_creation.teid); |
1233 | 0 | return OGS_ERROR; |
1234 | 0 | } |
1235 | | |
1236 | 0 | rv = ogs_gtp_connect( |
1237 | 0 | ogs_gtp_self()->gtpu_sock, ogs_gtp_self()->gtpu_sock6, gnode); |
1238 | 0 | if (rv != OGS_OK) { |
1239 | 0 | ogs_error("Cannot connect GTP-U peer " |
1240 | 0 | "[FAR-ID:%u TEID:0x%x rv:%d]", |
1241 | 0 | far->id, far->outer_header_creation.teid, rv); |
1242 | | /* |
1243 | | * ogs_gtp_node_new() zeroes gnode->addr, and only a successful |
1244 | | * ogs_gtp_connect() fills it in. On failure the node stays in |
1245 | | * gtpu_peer_list with ogs_sa_family == 0, so the next request |
1246 | | * carrying the same IP address finds it through |
1247 | | * ogs_gtp_node_find_by_ip(), skips ogs_gtp_connect() entirely |
1248 | | * and reaches ogs_pfcp_far_f_teid_hash_set(), which aborts: |
1249 | | * |
1250 | | * [pfcp] FATAL: Unknown family(0) (../lib/pfcp/context.c) |
1251 | | * |
1252 | | * OGS_SETUP_GTP_NODE() has not run yet, so no FAR references |
1253 | | * this node and removing it here is safe. |
1254 | | */ |
1255 | 0 | ogs_gtp_node_remove(&ogs_gtp_self()->gtpu_peer_list, gnode); |
1256 | 0 | return rv; |
1257 | 0 | } |
1258 | 0 | } |
1259 | | |
1260 | | /* Update FAR can move to another peer; keep the count per pointer */ |
1261 | 0 | if (far->gnode != gnode) { |
1262 | 0 | ogs_warn("Set FAR-ID[%u] GTP-U peer [TEID:0x%x]", |
1263 | 0 | far->id, far->outer_header_creation.teid); |
1264 | 0 | gnode->gtpu_ref_count++; |
1265 | 0 | if (far->gnode) |
1266 | 0 | gtpu_peer_release(far->gnode); |
1267 | 0 | OGS_SETUP_GTP_NODE(far, gnode); |
1268 | 0 | } |
1269 | | |
1270 | 0 | return OGS_OK; |
1271 | 0 | } |
1272 | | |
1273 | | int ogs_pfcp_setup_pdr_gtpu_node(ogs_pfcp_pdr_t *pdr) |
1274 | 0 | { |
1275 | 0 | int rv; |
1276 | 0 | ogs_ip_t ip; |
1277 | 0 | ogs_gtp_node_t *gnode = NULL; |
1278 | |
|
1279 | 0 | ogs_assert(pdr); |
1280 | | |
1281 | | /* No F-TEID */ |
1282 | 0 | if (pdr->f_teid_len == 0) return OGS_DONE; |
1283 | | |
1284 | 0 | rv = ogs_pfcp_f_teid_to_ip(&pdr->f_teid, &ip); |
1285 | 0 | if (rv != OGS_OK) { |
1286 | 0 | ogs_error("ogs_pfcp_f_teid_to_ip() failed"); |
1287 | 0 | return rv; |
1288 | 0 | } |
1289 | | |
1290 | 0 | gnode = ogs_gtp_node_find_by_ip(&ogs_gtp_self()->gtpu_peer_list, &ip); |
1291 | 0 | if (!gnode) { |
1292 | 0 | gnode = ogs_gtp_node_add_by_ip( |
1293 | 0 | &ogs_gtp_self()->gtpu_peer_list, &ip, ogs_gtp_self()->gtpu_port); |
1294 | 0 | if (!gnode) { |
1295 | 0 | ogs_error("ogs_gtp_node_add_by_ip() failed"); |
1296 | 0 | return OGS_ERROR; |
1297 | 0 | } |
1298 | | |
1299 | 0 | rv = ogs_gtp_connect( |
1300 | 0 | ogs_gtp_self()->gtpu_sock, ogs_gtp_self()->gtpu_sock6, gnode); |
1301 | 0 | if (rv != OGS_OK) { |
1302 | 0 | ogs_error("ogs_gtp_connect() failed"); |
1303 | | /* Same rollback as ogs_pfcp_setup_far_gtpu_node() */ |
1304 | 0 | ogs_gtp_node_remove(&ogs_gtp_self()->gtpu_peer_list, gnode); |
1305 | 0 | return rv; |
1306 | 0 | } |
1307 | 0 | } |
1308 | | |
1309 | | /* Update PDR can move to another peer; keep the count per pointer */ |
1310 | 0 | if (pdr->gnode != gnode) { |
1311 | 0 | gnode->gtpu_ref_count++; |
1312 | 0 | if (pdr->gnode) |
1313 | 0 | gtpu_peer_release(pdr->gnode); |
1314 | 0 | OGS_SETUP_GTP_NODE(pdr, gnode); |
1315 | 0 | } |
1316 | | |
1317 | 0 | return OGS_OK; |
1318 | 0 | } |
1319 | | |
1320 | | void ogs_pfcp_sess_clear(ogs_pfcp_sess_t *sess) |
1321 | 0 | { |
1322 | 0 | ogs_pfcp_pdr_remove_all(sess); |
1323 | 0 | ogs_pfcp_far_remove_all(sess); |
1324 | 0 | ogs_pfcp_urr_remove_all(sess); |
1325 | 0 | ogs_pfcp_qer_remove_all(sess); |
1326 | 0 | if (sess->bar) ogs_pfcp_bar_delete(sess->bar); |
1327 | 0 | } |
1328 | | |
1329 | | static bool mark_has_id( |
1330 | | const uint32_t *marked_id, int num_of_marked, uint32_t id) |
1331 | 0 | { |
1332 | 0 | int i; |
1333 | |
|
1334 | 0 | for (i = 0; i < num_of_marked; i++) { |
1335 | 0 | if (marked_id[i] == id) |
1336 | 0 | return true; |
1337 | 0 | } |
1338 | | |
1339 | 0 | return false; |
1340 | 0 | } |
1341 | | |
1342 | | /* |
1343 | | * Record IDs, not object addresses: a freed pool slot may later be reused. |
1344 | | * This is an inventory for cleanup, not a copy of the rules' contents. |
1345 | | */ |
1346 | | void ogs_pfcp_sess_mark(ogs_pfcp_sess_t *sess, ogs_pfcp_sess_mark_t *mark) |
1347 | 0 | { |
1348 | 0 | ogs_pfcp_pdr_t *pdr = NULL; |
1349 | 0 | ogs_pfcp_far_t *far = NULL; |
1350 | 0 | ogs_pfcp_urr_t *urr = NULL; |
1351 | 0 | ogs_pfcp_qer_t *qer = NULL; |
1352 | |
|
1353 | 0 | ogs_assert(sess); |
1354 | 0 | ogs_assert(mark); |
1355 | | |
1356 | 0 | memset(mark, 0, sizeof(*mark)); |
1357 | |
|
1358 | 0 | ogs_list_for_each(&sess->pdr_list, pdr) { |
1359 | 0 | ogs_assert(mark->num_of_pdr < OGS_ARRAY_SIZE(mark->pdr_id)); |
1360 | 0 | mark->pdr_id[mark->num_of_pdr++] = pdr->id; |
1361 | 0 | } |
1362 | 0 | ogs_list_for_each(&sess->far_list, far) { |
1363 | 0 | ogs_assert(mark->num_of_far < OGS_ARRAY_SIZE(mark->far_id)); |
1364 | 0 | mark->far_id[mark->num_of_far++] = far->id; |
1365 | 0 | } |
1366 | 0 | ogs_list_for_each(&sess->urr_list, urr) { |
1367 | 0 | ogs_assert(mark->num_of_urr < OGS_ARRAY_SIZE(mark->urr_id)); |
1368 | 0 | mark->urr_id[mark->num_of_urr++] = urr->id; |
1369 | 0 | } |
1370 | 0 | ogs_list_for_each(&sess->qer_list, qer) { |
1371 | 0 | ogs_assert(mark->num_of_qer < OGS_ARRAY_SIZE(mark->qer_id)); |
1372 | 0 | mark->qer_id[mark->num_of_qer++] = qer->id; |
1373 | 0 | } |
1374 | | |
1375 | 0 | mark->has_bar = (sess->bar != NULL); |
1376 | |
|
1377 | 0 | ogs_info("Mark rules [PDR:%d FAR:%d URR:%d QER:%d BAR:%d]", |
1378 | 0 | mark->num_of_pdr, mark->num_of_far, |
1379 | 0 | mark->num_of_urr, mark->num_of_qer, mark->has_bar); |
1380 | 0 | } |
1381 | | |
1382 | | /* |
1383 | | * Remove rules whose IDs were not present at ogs_pfcp_sess_mark(). |
1384 | | * Modification handlers reject duplicate PDR/FAR/URR/QER Create IDs first, |
1385 | | * so a marked ID cannot be replaced by a Create in the same request. |
1386 | | * |
1387 | | * Delete PDRs before the rules they reference. Existing PDRs cannot acquire |
1388 | | * new references through Update PDR, and referenced-rule Removes are rejected. |
1389 | | * Applied Updates and Removes are not restored; retained counts can therefore |
1390 | | * be smaller than the mark without indicating a cleanup failure. |
1391 | | * PFCPSMReq-Flags are request-local and must be cleared on surviving FARs. |
1392 | | * Callers log the SEIDs and the final rule counts around this cleanup. |
1393 | | */ |
1394 | | void ogs_pfcp_sess_clear_since_mark( |
1395 | | ogs_pfcp_sess_t *sess, const ogs_pfcp_sess_mark_t *mark) |
1396 | 0 | { |
1397 | 0 | ogs_pfcp_pdr_t *pdr = NULL, *next_pdr = NULL; |
1398 | 0 | ogs_pfcp_far_t *far = NULL, *next_far = NULL; |
1399 | 0 | ogs_pfcp_urr_t *urr = NULL, *next_urr = NULL; |
1400 | 0 | ogs_pfcp_qer_t *qer = NULL, *next_qer = NULL; |
1401 | |
|
1402 | 0 | ogs_assert(sess); |
1403 | 0 | ogs_assert(mark); |
1404 | | |
1405 | 0 | ogs_list_for_each_safe(&sess->pdr_list, next_pdr, pdr) { |
1406 | 0 | if (!mark_has_id(mark->pdr_id, mark->num_of_pdr, pdr->id)) { |
1407 | 0 | ogs_error("Rejected modification: remove new PDR-ID[%u]", |
1408 | 0 | pdr->id); |
1409 | 0 | ogs_pfcp_pdr_remove(pdr); |
1410 | 0 | } |
1411 | 0 | } |
1412 | 0 | ogs_list_for_each_safe(&sess->far_list, next_far, far) { |
1413 | 0 | if (!mark_has_id(mark->far_id, mark->num_of_far, far->id)) { |
1414 | 0 | ogs_error("Rejected modification: remove new FAR-ID[%u]", |
1415 | 0 | far->id); |
1416 | 0 | ogs_pfcp_far_remove(far); |
1417 | 0 | } else { |
1418 | | /* |
1419 | | * A failed flags pass skips normal End Marker processing. |
1420 | | * Do not carry that request's flags into the next request. |
1421 | | */ |
1422 | 0 | if (far->smreq_flags.value) |
1423 | 0 | ogs_info("Rejected modification: clear FAR-ID[%u] " |
1424 | 0 | "request flags[0x%x]", |
1425 | 0 | far->id, far->smreq_flags.value); |
1426 | 0 | far->smreq_flags.value = 0; |
1427 | 0 | } |
1428 | 0 | } |
1429 | 0 | ogs_list_for_each_safe(&sess->urr_list, next_urr, urr) { |
1430 | 0 | if (!mark_has_id(mark->urr_id, mark->num_of_urr, urr->id)) { |
1431 | 0 | ogs_error("Rejected modification: remove new URR-ID[%u]", |
1432 | 0 | urr->id); |
1433 | 0 | ogs_pfcp_urr_remove(urr); |
1434 | 0 | } |
1435 | 0 | } |
1436 | 0 | ogs_list_for_each_safe(&sess->qer_list, next_qer, qer) { |
1437 | 0 | if (!mark_has_id(mark->qer_id, mark->num_of_qer, qer->id)) { |
1438 | 0 | ogs_error("Rejected modification: remove new QER-ID[%u]", |
1439 | 0 | qer->id); |
1440 | 0 | ogs_pfcp_qer_remove(qer); |
1441 | 0 | } |
1442 | 0 | } |
1443 | | |
1444 | | /* An existing BAR may have been replaced; that change is retained. */ |
1445 | 0 | if (!mark->has_bar && sess->bar) { |
1446 | 0 | ogs_error("Rejected modification: remove new BAR-ID[%u]", |
1447 | 0 | sess->bar->id); |
1448 | 0 | ogs_pfcp_bar_delete(sess->bar); |
1449 | 0 | } |
1450 | 0 | } |
1451 | | |
1452 | | static void pdr_log_state(ogs_pfcp_sess_t *sess, const char *reason) |
1453 | 0 | { |
1454 | 0 | ogs_pfcp_pdr_t *pdr = NULL; |
1455 | |
|
1456 | 0 | ogs_assert(sess); |
1457 | 0 | ogs_assert(reason); |
1458 | | |
1459 | 0 | ogs_error("%s [PDR:%d/%d] [PDR-ID-available:%lld] " |
1460 | 0 | "[PDR-pool-available:%lld] [PDR-TEID-pool-available:%lld]", |
1461 | 0 | reason, ogs_list_count(&sess->pdr_list), OGS_MAX_NUM_OF_PDR, |
1462 | 0 | (long long)sess->pdr_id_pool.avail, |
1463 | 0 | (long long)ogs_pfcp_pdr_pool.avail, |
1464 | 0 | (long long)ogs_pfcp_pdr_teid_pool.avail); |
1465 | |
|
1466 | 0 | ogs_list_for_each(&sess->pdr_list, pdr) { |
1467 | 0 | ogs_error(" PDR [id:%u] [teid:0x%x] [src-if:%d] [FAR-id:%u]", |
1468 | 0 | (unsigned)pdr->id, pdr->teid, pdr->src_if, |
1469 | 0 | pdr->far ? (unsigned)pdr->far->id : 0); |
1470 | 0 | } |
1471 | 0 | } |
1472 | | |
1473 | | static int precedence_compare(ogs_pfcp_pdr_t *pdr1, ogs_pfcp_pdr_t *pdr2) |
1474 | 0 | { |
1475 | 0 | if (pdr1->precedence == pdr2->precedence) |
1476 | 0 | return 0; |
1477 | 0 | else if (pdr1->precedence < pdr2->precedence) |
1478 | 0 | return -1; |
1479 | 0 | else |
1480 | 0 | return 1; |
1481 | 0 | } |
1482 | | |
1483 | | ogs_pfcp_pdr_t *ogs_pfcp_pdr_add(ogs_pfcp_sess_t *sess) |
1484 | 0 | { |
1485 | 0 | ogs_pfcp_pdr_t *pdr = NULL; |
1486 | |
|
1487 | 0 | ogs_assert(sess); |
1488 | | |
1489 | 0 | ogs_pool_alloc(&ogs_pfcp_pdr_pool, &pdr); |
1490 | 0 | if (pdr == NULL) { |
1491 | 0 | pdr_log_state(sess, "PDR pool exhausted"); |
1492 | 0 | return NULL; |
1493 | 0 | } |
1494 | 0 | memset(pdr, 0, sizeof *pdr); |
1495 | |
|
1496 | 0 | pdr->obj.type = OGS_PFCP_OBJ_PDR_TYPE; |
1497 | 0 | pdr->src_if = OGS_PFCP_INTERFACE_UNKNOWN; |
1498 | | |
1499 | | /* Set TEID */ |
1500 | 0 | ogs_pool_alloc(&ogs_pfcp_pdr_teid_pool, &pdr->teid_node); |
1501 | 0 | if (pdr->teid_node == NULL) { |
1502 | 0 | ogs_pool_free(&ogs_pfcp_pdr_pool, pdr); |
1503 | 0 | pdr_log_state(sess, "PDR TEID pool exhausted"); |
1504 | 0 | return NULL; |
1505 | 0 | } |
1506 | | |
1507 | 0 | pdr->teid = *(pdr->teid_node); |
1508 | | |
1509 | | /* Set PDR-ID */ |
1510 | 0 | ogs_pool_alloc(&sess->pdr_id_pool, &pdr->id_node); |
1511 | 0 | if (pdr->id_node == NULL) { |
1512 | 0 | ogs_pool_free(&ogs_pfcp_pdr_teid_pool, pdr->teid_node); |
1513 | 0 | ogs_pool_free(&ogs_pfcp_pdr_pool, pdr); |
1514 | 0 | pdr_log_state(sess, "PDR ID pool exhausted"); |
1515 | 0 | return NULL; |
1516 | 0 | } |
1517 | | |
1518 | 0 | pdr->id = *(pdr->id_node); |
1519 | 0 | ogs_assert(pdr->id > 0 && pdr->id <= OGS_MAX_NUM_OF_PDR); |
1520 | | |
1521 | 0 | pdr->sess = sess; |
1522 | 0 | ogs_list_add(&sess->pdr_list, pdr); |
1523 | |
|
1524 | 0 | return pdr; |
1525 | 0 | } |
1526 | | |
1527 | | ogs_pfcp_pdr_t *ogs_pfcp_pdr_find( |
1528 | | ogs_pfcp_sess_t *sess, ogs_pfcp_pdr_id_t id) |
1529 | 0 | { |
1530 | 0 | ogs_pfcp_pdr_t *pdr = NULL; |
1531 | |
|
1532 | 0 | ogs_assert(sess); |
1533 | | |
1534 | 0 | ogs_list_for_each(&sess->pdr_list, pdr) |
1535 | 0 | if (pdr->id == id) return pdr; |
1536 | | |
1537 | 0 | return NULL; |
1538 | 0 | } |
1539 | | |
1540 | | ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_or_add( |
1541 | | ogs_pfcp_sess_t *sess, ogs_pfcp_pdr_id_t id) |
1542 | 0 | { |
1543 | 0 | ogs_pfcp_pdr_t *pdr = NULL; |
1544 | |
|
1545 | 0 | ogs_assert(sess); |
1546 | | |
1547 | 0 | pdr = ogs_pfcp_pdr_find(sess, id); |
1548 | 0 | if (!pdr) { |
1549 | 0 | pdr = ogs_pfcp_pdr_add(sess); |
1550 | 0 | if (!pdr) { |
1551 | 0 | ogs_error("ogs_pfcp_pdr_add() failed"); |
1552 | 0 | return NULL; |
1553 | 0 | } |
1554 | 0 | pdr->id = id; |
1555 | 0 | } |
1556 | | |
1557 | 0 | return pdr; |
1558 | 0 | } |
1559 | | |
1560 | | int ogs_pfcp_pdr_swap_teid(ogs_pfcp_pdr_t *pdr) |
1561 | 0 | { |
1562 | 0 | int i = 0; |
1563 | |
|
1564 | 0 | ogs_assert(pdr); |
1565 | 0 | ogs_assert(pdr->f_teid_len > 0); |
1566 | 0 | ogs_assert(!pdr->f_teid.ch); |
1567 | | |
1568 | | /* |
1569 | | * Issues #3747, #3574 |
1570 | | * |
1571 | | * This code validates the F-TEID (Fully encapsulated TEID) information |
1572 | | * element within a PDR structure before further processing the PFCP |
1573 | | * message. The validation ensures that the F-TEID is present and |
1574 | | * within acceptable limits defined by the system. |
1575 | | */ |
1576 | 0 | if (pdr->f_teid.teid > 0 && |
1577 | 0 | pdr->f_teid.teid <= ogs_pfcp_pdr_teid_pool.size) { |
1578 | | /* PASS OK */ |
1579 | 0 | } else { |
1580 | 0 | ogs_error("PDR-ID[%d] F-TEID LEN[%d] TEID[0x%x]", |
1581 | 0 | pdr->id, pdr->f_teid_len, pdr->f_teid.teid); |
1582 | 0 | return OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; |
1583 | 0 | } |
1584 | | |
1585 | | /* Find out the Array Index for the restored TEID. */ |
1586 | 0 | i = pdr_random_to_index[pdr->f_teid.teid]; |
1587 | 0 | ogs_assert(i < ogs_pfcp_pdr_teid_pool.size); |
1588 | | |
1589 | 0 | ogs_assert(pdr->teid_node); |
1590 | | /* |
1591 | | * If SWAP has already done this, it will not try this again. |
1592 | | * This situation can occur when multiple PDRs are restored |
1593 | | * with the same TEID. |
1594 | | */ |
1595 | 0 | if (pdr->f_teid.teid == ogs_pfcp_pdr_teid_pool.array[i]) { |
1596 | 0 | ogs_pfcp_pdr_teid_pool.array[i] = *(pdr->teid_node); |
1597 | 0 | *(pdr->teid_node) = pdr->f_teid.teid; |
1598 | 0 | } |
1599 | |
|
1600 | 0 | return OGS_PFCP_CAUSE_REQUEST_ACCEPTED; |
1601 | 0 | } |
1602 | | |
1603 | | static void pdr_teid_hash_register( |
1604 | | ogs_pfcp_object_type_e type, ogs_pfcp_pdr_t *pdr) |
1605 | 0 | { |
1606 | 0 | void *object = NULL; |
1607 | |
|
1608 | 0 | ogs_assert(type == OGS_PFCP_OBJ_PDR_TYPE || |
1609 | 0 | type == OGS_PFCP_OBJ_SESS_TYPE); |
1610 | 0 | ogs_assert(pdr); |
1611 | 0 | ogs_assert(pdr->sess); |
1612 | 0 | ogs_assert(!pdr->hash.teid.registered); |
1613 | | |
1614 | | /* ogs_hash keeps this key's address; only the registered PDR owns it. */ |
1615 | 0 | pdr->hash.teid.key = pdr->f_teid.teid; |
1616 | 0 | pdr->hash.teid.type = type; |
1617 | 0 | pdr->hash.teid.registered = true; |
1618 | |
|
1619 | 0 | object = type == OGS_PFCP_OBJ_PDR_TYPE ? |
1620 | 0 | (void *)pdr : (void *)pdr->sess; |
1621 | 0 | ogs_hash_set(self.object_teid_hash, |
1622 | 0 | &pdr->hash.teid.key, sizeof(pdr->hash.teid.key), object); |
1623 | 0 | ogs_info("Register local F-TEID[0x%x] [PDR-ID:%u type:%d]", |
1624 | 0 | pdr->hash.teid.key, pdr->id, type); |
1625 | 0 | } |
1626 | | |
1627 | | /* |
1628 | | * ogs_hash retains the address of the key instead of copying it. |
1629 | | * When removing or changing the registered PDR, move a shared TEID |
1630 | | * to a live PDR so other QFIs can still use the tunnel (#2003). |
1631 | | */ |
1632 | | static void pdr_teid_hash_remove(ogs_pfcp_pdr_t *pdr) |
1633 | 0 | { |
1634 | 0 | ogs_pfcp_pdr_t *other = NULL; |
1635 | 0 | ogs_pfcp_object_type_e type; |
1636 | 0 | uint32_t teid; |
1637 | |
|
1638 | 0 | ogs_assert(pdr); |
1639 | 0 | ogs_assert(pdr->sess); |
1640 | | |
1641 | 0 | if (!pdr->hash.teid.registered) |
1642 | | /* New PDRs and non-owners of a shared TEID have nothing to remove. */ |
1643 | 0 | return; |
1644 | | |
1645 | 0 | type = pdr->hash.teid.type; |
1646 | 0 | teid = pdr->hash.teid.key; |
1647 | |
|
1648 | 0 | ogs_info("Remove local F-TEID[0x%x] registration [PDR-ID:%u]", |
1649 | 0 | teid, pdr->id); |
1650 | | /* NULL removes the entry; it does not leave a NULL-valued lookup. */ |
1651 | 0 | ogs_hash_set(self.object_teid_hash, |
1652 | 0 | &pdr->hash.teid.key, sizeof(pdr->hash.teid.key), NULL); |
1653 | 0 | pdr->hash.teid.registered = false; |
1654 | |
|
1655 | 0 | ogs_list_for_each(&pdr->sess->pdr_list, other) { |
1656 | 0 | if (other != pdr && !other->hash.teid.registered && |
1657 | 0 | other->f_teid_len && |
1658 | 0 | other->f_teid.teid == teid) { |
1659 | 0 | ogs_warn("Transfer local F-TEID[0x%x] [PDR-ID:%u -> %u]", |
1660 | 0 | teid, pdr->id, other->id); |
1661 | 0 | pdr_teid_hash_register(type, other); |
1662 | 0 | break; |
1663 | 0 | } |
1664 | 0 | } |
1665 | 0 | } |
1666 | | |
1667 | | uint8_t ogs_pfcp_object_teid_hash_set( |
1668 | | ogs_pfcp_object_type_e type, ogs_pfcp_pdr_t *pdr) |
1669 | 0 | { |
1670 | 0 | ogs_pfcp_object_t *object = NULL; |
1671 | 0 | ogs_pfcp_sess_t *owner_sess = NULL; |
1672 | |
|
1673 | 0 | ogs_assert(type == OGS_PFCP_OBJ_PDR_TYPE || |
1674 | 0 | type == OGS_PFCP_OBJ_SESS_TYPE); |
1675 | 0 | ogs_assert(pdr); |
1676 | 0 | ogs_assert(pdr->sess); |
1677 | | |
1678 | 0 | if (ogs_pfcp_self()->up_function_features.ftup && pdr->f_teid.ch) { |
1679 | |
|
1680 | 0 | ogs_pfcp_pdr_t *choosed_pdr = NULL; |
1681 | |
|
1682 | 0 | if (pdr->f_teid.chid) { |
1683 | 0 | choosed_pdr = ogs_pfcp_pdr_find_by_choose_id( |
1684 | 0 | pdr->sess, pdr->f_teid.choose_id); |
1685 | 0 | if (!choosed_pdr) { |
1686 | 0 | pdr->chid = true; |
1687 | 0 | pdr->choose_id = pdr->f_teid.choose_id; |
1688 | 0 | } |
1689 | 0 | } |
1690 | |
|
1691 | 0 | if (choosed_pdr) { |
1692 | 0 | pdr->f_teid_len = choosed_pdr->f_teid_len; |
1693 | 0 | memcpy(&pdr->f_teid, &choosed_pdr->f_teid, pdr->f_teid_len); |
1694 | |
|
1695 | 0 | } else { |
1696 | 0 | ogs_gtpu_resource_t *resource = NULL; |
1697 | 0 | resource = ogs_pfcp_find_gtpu_resource( |
1698 | 0 | &ogs_gtp_self()->gtpu_resource_list, |
1699 | 0 | pdr->dnn, pdr->src_if); |
1700 | 0 | if (resource) { |
1701 | 0 | if (!((resource->info.v4 && pdr->f_teid.ipv4) || |
1702 | 0 | (resource->info.v6 && pdr->f_teid.ipv6))) { |
1703 | 0 | ogs_error("CH F-TEID address family mismatch: " |
1704 | 0 | "resource[v4:%d v6:%d] fteid[v4:%d v6:%d]", |
1705 | 0 | resource->info.v4, resource->info.v6, |
1706 | 0 | pdr->f_teid.ipv4, pdr->f_teid.ipv6); |
1707 | 0 | return OGS_PFCP_CAUSE_REQUEST_REJECTED; |
1708 | 0 | } |
1709 | 0 | if (OGS_OK != ogs_pfcp_user_plane_ip_resource_info_to_f_teid( |
1710 | 0 | &resource->info, &pdr->f_teid, &pdr->f_teid_len)) { |
1711 | 0 | ogs_error("user_plane_ip_resource_info_to_f_teid() " |
1712 | 0 | "failed in CH path"); |
1713 | 0 | return OGS_PFCP_CAUSE_REQUEST_REJECTED; |
1714 | 0 | } |
1715 | 0 | if (resource->info.teidri) |
1716 | 0 | pdr->f_teid.teid = OGS_PFCP_GTPU_INDEX_TO_TEID( |
1717 | 0 | pdr->teid, resource->info.teidri, |
1718 | 0 | resource->info.teid_range); |
1719 | 0 | else |
1720 | 0 | pdr->f_teid.teid = pdr->teid; |
1721 | 0 | } else { |
1722 | 0 | if (!((ogs_gtp_self()->gtpu_addr && pdr->f_teid.ipv4) || |
1723 | 0 | (ogs_gtp_self()->gtpu_addr6 && pdr->f_teid.ipv6))) { |
1724 | 0 | ogs_error("CH F-TEID address family mismatch with local " |
1725 | 0 | "GTP-U address: gtpu[v4:%p v6:%p] " |
1726 | 0 | "fteid[v4:%d v6:%d]", |
1727 | 0 | ogs_gtp_self()->gtpu_addr, |
1728 | 0 | ogs_gtp_self()->gtpu_addr6, |
1729 | 0 | pdr->f_teid.ipv4, pdr->f_teid.ipv6); |
1730 | 0 | return OGS_PFCP_CAUSE_REQUEST_REJECTED; |
1731 | 0 | } |
1732 | 0 | if (OGS_OK != ogs_pfcp_sockaddr_to_f_teid( |
1733 | 0 | pdr->f_teid.ipv4 ? |
1734 | 0 | ogs_gtp_self()->gtpu_addr : NULL, |
1735 | 0 | pdr->f_teid.ipv6 ? |
1736 | 0 | ogs_gtp_self()->gtpu_addr6 : NULL, |
1737 | 0 | &pdr->f_teid, &pdr->f_teid_len)) { |
1738 | 0 | ogs_error("ogs_pfcp_sockaddr_to_f_teid() " |
1739 | 0 | "failed in CH path"); |
1740 | 0 | return OGS_PFCP_CAUSE_REQUEST_REJECTED; |
1741 | 0 | } |
1742 | 0 | pdr->f_teid.teid = pdr->teid; |
1743 | 0 | } |
1744 | 0 | } |
1745 | 0 | } |
1746 | | |
1747 | | /* |
1748 | | * Validate the new TEID before removing the old registration. A rejected |
1749 | | * Update can then restore its fields while its old lookup remains valid. |
1750 | | * Same-session sharing is allowed; another session's entry is untouched. |
1751 | | */ |
1752 | 0 | object = ogs_pfcp_object_find_by_teid(pdr->f_teid.teid); |
1753 | 0 | if (object) { |
1754 | 0 | switch (object->type) { |
1755 | 0 | case OGS_PFCP_OBJ_PDR_TYPE: |
1756 | 0 | owner_sess = ((ogs_pfcp_pdr_t *)object)->sess; |
1757 | 0 | break; |
1758 | 0 | case OGS_PFCP_OBJ_SESS_TYPE: |
1759 | 0 | owner_sess = (ogs_pfcp_sess_t *)object; |
1760 | 0 | break; |
1761 | 0 | default: |
1762 | 0 | ogs_fatal("Unknown type [%d]", object->type); |
1763 | 0 | ogs_assert_if_reached(); |
1764 | 0 | } |
1765 | | |
1766 | 0 | if (owner_sess != pdr->sess) { |
1767 | 0 | ogs_error("Reject PDR-ID[%u] F-TEID[0x%x]: " |
1768 | 0 | "registered by another session", |
1769 | 0 | pdr->id, pdr->f_teid.teid); |
1770 | 0 | return OGS_PFCP_CAUSE_REQUEST_REJECTED; |
1771 | 0 | } |
1772 | | |
1773 | 0 | ogs_assert(object->type == type); |
1774 | 0 | } |
1775 | | |
1776 | 0 | if (pdr->hash.teid.registered && |
1777 | 0 | pdr->hash.teid.key == pdr->f_teid.teid) |
1778 | 0 | return OGS_PFCP_CAUSE_REQUEST_ACCEPTED; |
1779 | | |
1780 | 0 | pdr_teid_hash_remove(pdr); |
1781 | | |
1782 | | /* A same-session PDR already owns this shared TEID. */ |
1783 | 0 | if (!object) |
1784 | 0 | pdr_teid_hash_register(type, pdr); |
1785 | |
|
1786 | 0 | return OGS_PFCP_CAUSE_REQUEST_ACCEPTED; |
1787 | 0 | } |
1788 | | |
1789 | | ogs_pfcp_object_t *ogs_pfcp_object_find_by_teid(uint32_t teid) |
1790 | 0 | { |
1791 | 0 | return (ogs_pfcp_object_t *)ogs_hash_get( |
1792 | 0 | self.object_teid_hash, &teid, sizeof(teid)); |
1793 | 0 | } |
1794 | | |
1795 | | ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_by_choose_id( |
1796 | | ogs_pfcp_sess_t *sess, uint8_t choose_id) |
1797 | 0 | { |
1798 | 0 | ogs_pfcp_pdr_t *pdr = NULL; |
1799 | |
|
1800 | 0 | ogs_assert(sess); |
1801 | | |
1802 | 0 | ogs_list_for_each(&sess->pdr_list, pdr) |
1803 | 0 | if (pdr->chid == true && pdr->choose_id == choose_id) |
1804 | 0 | return pdr; |
1805 | | |
1806 | 0 | return NULL; |
1807 | 0 | } |
1808 | | |
1809 | | void ogs_pfcp_pdr_reorder_by_precedence( |
1810 | | ogs_pfcp_pdr_t *pdr, ogs_pfcp_precedence_t precedence) |
1811 | 0 | { |
1812 | 0 | ogs_pfcp_sess_t *sess = NULL; |
1813 | |
|
1814 | 0 | ogs_assert(pdr); |
1815 | 0 | sess = pdr->sess; |
1816 | 0 | ogs_assert(sess); |
1817 | | |
1818 | 0 | ogs_list_remove(&sess->pdr_list, pdr); |
1819 | |
|
1820 | 0 | pdr->precedence = precedence; |
1821 | 0 | ogs_list_insert_sorted(&sess->pdr_list, pdr, precedence_compare); |
1822 | 0 | } |
1823 | | |
1824 | | void ogs_pfcp_pdr_associate_far(ogs_pfcp_pdr_t *pdr, ogs_pfcp_far_t *far) |
1825 | 0 | { |
1826 | 0 | ogs_assert(pdr); |
1827 | 0 | ogs_assert(far); |
1828 | | |
1829 | 0 | pdr->far = far; |
1830 | 0 | } |
1831 | | void ogs_pfcp_pdr_associate_urr(ogs_pfcp_pdr_t *pdr, ogs_pfcp_urr_t *urr) |
1832 | 0 | { |
1833 | 0 | ogs_assert(pdr); |
1834 | 0 | ogs_assert(urr); |
1835 | 0 | ogs_assert(pdr->num_of_urr < OGS_ARRAY_SIZE(pdr->urr)); |
1836 | 0 | int i; |
1837 | | |
1838 | | /* Avoid storing duplicate pointers */ |
1839 | 0 | for (i = 0; i < pdr->num_of_urr; i++) { |
1840 | 0 | if (pdr->urr[i]->id == urr->id) |
1841 | 0 | return; |
1842 | 0 | } |
1843 | | |
1844 | 0 | pdr->urr[pdr->num_of_urr++] = urr; |
1845 | 0 | } |
1846 | | void ogs_pfcp_pdr_associate_qer(ogs_pfcp_pdr_t *pdr, ogs_pfcp_qer_t *qer) |
1847 | 0 | { |
1848 | 0 | ogs_assert(pdr); |
1849 | 0 | ogs_assert(qer); |
1850 | | |
1851 | 0 | pdr->qer = qer; |
1852 | 0 | } |
1853 | | |
1854 | | void ogs_pfcp_pdr_remove(ogs_pfcp_pdr_t *pdr) |
1855 | 0 | { |
1856 | 0 | int i; |
1857 | |
|
1858 | 0 | ogs_assert(pdr); |
1859 | 0 | ogs_assert(pdr->sess); |
1860 | | |
1861 | 0 | pdr_teid_hash_remove(pdr); |
1862 | |
|
1863 | 0 | ogs_list_remove(&pdr->sess->pdr_list, pdr); |
1864 | |
|
1865 | 0 | ogs_pfcp_rule_remove_all(pdr); |
1866 | |
|
1867 | 0 | if (pdr->gnode) { |
1868 | 0 | gtpu_peer_release(pdr->gnode); |
1869 | 0 | pdr->gnode = NULL; |
1870 | 0 | } |
1871 | |
|
1872 | 0 | if (pdr->dnn) |
1873 | 0 | ogs_free(pdr->dnn); |
1874 | |
|
1875 | 0 | if (pdr->id_node) |
1876 | 0 | ogs_pool_free(&pdr->sess->pdr_id_pool, pdr->id_node); |
1877 | |
|
1878 | 0 | if (pdr->ipv4_framed_routes) { |
1879 | 0 | for (i = 0; i < OGS_MAX_NUM_OF_FRAMED_ROUTES_IN_PDI; i++) { |
1880 | 0 | if (!pdr->ipv4_framed_routes[i]) |
1881 | 0 | break; |
1882 | 0 | ogs_free(pdr->ipv4_framed_routes[i]); |
1883 | 0 | } |
1884 | 0 | ogs_free(pdr->ipv4_framed_routes); |
1885 | 0 | } |
1886 | |
|
1887 | 0 | if (pdr->ipv6_framed_routes) { |
1888 | 0 | for (i = 0; i < OGS_MAX_NUM_OF_FRAMED_ROUTES_IN_PDI; i++) { |
1889 | 0 | if (!pdr->ipv6_framed_routes[i]) |
1890 | 0 | break; |
1891 | 0 | ogs_free(pdr->ipv6_framed_routes[i]); |
1892 | 0 | } |
1893 | 0 | ogs_free(pdr->ipv6_framed_routes); |
1894 | 0 | } |
1895 | |
|
1896 | 0 | ogs_pool_free(&ogs_pfcp_pdr_teid_pool, pdr->teid_node); |
1897 | 0 | ogs_pool_free(&ogs_pfcp_pdr_pool, pdr); |
1898 | 0 | } |
1899 | | |
1900 | | void ogs_pfcp_pdr_remove_all(ogs_pfcp_sess_t *sess) |
1901 | 0 | { |
1902 | 0 | ogs_pfcp_pdr_t *pdr = NULL, *next_pdr = NULL; |
1903 | |
|
1904 | 0 | ogs_assert(sess); |
1905 | 0 | ogs_list_for_each_safe(&sess->pdr_list, next_pdr, pdr) |
1906 | 0 | ogs_pfcp_pdr_remove(pdr); |
1907 | 0 | } |
1908 | | |
1909 | | ogs_pfcp_far_t *ogs_pfcp_far_add(ogs_pfcp_sess_t *sess) |
1910 | 0 | { |
1911 | 0 | ogs_pfcp_far_t *far = NULL; |
1912 | |
|
1913 | 0 | ogs_assert(sess); |
1914 | | |
1915 | 0 | ogs_pool_alloc(&ogs_pfcp_far_pool, &far); |
1916 | 0 | if (far == NULL) { |
1917 | 0 | ogs_error("far_pool() failed"); |
1918 | 0 | return NULL; |
1919 | 0 | } |
1920 | 0 | memset(far, 0, sizeof *far); |
1921 | |
|
1922 | 0 | ogs_pool_alloc(&sess->far_id_pool, &far->id_node); |
1923 | 0 | if (far->id_node == NULL) { |
1924 | 0 | ogs_error("far_id_pool() failed"); |
1925 | 0 | ogs_pool_free(&ogs_pfcp_far_pool, far); |
1926 | 0 | return NULL; |
1927 | 0 | } |
1928 | | |
1929 | 0 | far->id = *(far->id_node); |
1930 | 0 | ogs_assert(far->id > 0 && far->id <= OGS_MAX_NUM_OF_FAR); |
1931 | | |
1932 | 0 | far->dst_if = OGS_PFCP_INTERFACE_UNKNOWN; |
1933 | |
|
1934 | 0 | far->sess = sess; |
1935 | 0 | ogs_list_add(&sess->far_list, far); |
1936 | |
|
1937 | 0 | return far; |
1938 | 0 | } |
1939 | | |
1940 | | ogs_pfcp_far_t *ogs_pfcp_far_find( |
1941 | | ogs_pfcp_sess_t *sess, ogs_pfcp_far_id_t id) |
1942 | 0 | { |
1943 | 0 | ogs_pfcp_far_t *far = NULL; |
1944 | |
|
1945 | 0 | ogs_assert(sess); |
1946 | | |
1947 | 0 | ogs_list_for_each(&sess->far_list, far) |
1948 | 0 | if (far->id == id) return far; |
1949 | | |
1950 | 0 | return NULL; |
1951 | 0 | } |
1952 | | |
1953 | | ogs_pfcp_far_t *ogs_pfcp_far_find_or_add( |
1954 | | ogs_pfcp_sess_t *sess, ogs_pfcp_far_id_t id) |
1955 | 0 | { |
1956 | 0 | ogs_pfcp_far_t *far = NULL; |
1957 | |
|
1958 | 0 | ogs_assert(sess); |
1959 | | |
1960 | 0 | far = ogs_pfcp_far_find(sess, id); |
1961 | 0 | if (!far) { |
1962 | 0 | far = ogs_pfcp_far_add(sess); |
1963 | 0 | if (!far) { |
1964 | 0 | ogs_error("ogs_pfcp_far_add() failed"); |
1965 | 0 | return NULL; |
1966 | 0 | } |
1967 | 0 | far->id = id; |
1968 | 0 | } |
1969 | | |
1970 | 0 | return far; |
1971 | 0 | } |
1972 | | |
1973 | | static int far_f_teid_hash_key( |
1974 | | ogs_pfcp_far_t *far, ogs_pfcp_far_hash_f_teid_t *key) |
1975 | 0 | { |
1976 | 0 | int len; |
1977 | 0 | int family; |
1978 | 0 | ogs_gtp_node_t *gnode = NULL; |
1979 | 0 | ogs_sockaddr_t *addr = NULL; |
1980 | |
|
1981 | 0 | ogs_assert(far); |
1982 | 0 | ogs_assert(key); |
1983 | | |
1984 | 0 | gnode = far->gnode; |
1985 | 0 | ogs_assert(gnode); |
1986 | 0 | addr = &gnode->addr; |
1987 | |
|
1988 | 0 | memset(key, 0, sizeof(*key)); |
1989 | 0 | key->teid = far->outer_header_creation.teid; |
1990 | 0 | len = sizeof(key->teid); |
1991 | |
|
1992 | 0 | family = addr->ogs_sa_family; |
1993 | 0 | switch (family) { |
1994 | 0 | case AF_INET: |
1995 | 0 | memcpy(key->addr, &addr->sin.sin_addr, OGS_IPV4_LEN); |
1996 | 0 | len += OGS_IPV4_LEN; |
1997 | 0 | break; |
1998 | 0 | case AF_INET6: |
1999 | 0 | memcpy(key->addr, &addr->sin6.sin6_addr, OGS_IPV6_LEN); |
2000 | 0 | len += OGS_IPV6_LEN; |
2001 | 0 | break; |
2002 | 0 | default: |
2003 | 0 | ogs_fatal("Unknown family(%d) [FAR-ID:%u TEID:0x%x]", |
2004 | 0 | family, far->id, far->outer_header_creation.teid); |
2005 | 0 | ogs_abort(); |
2006 | 0 | } |
2007 | | |
2008 | 0 | return len; |
2009 | 0 | } |
2010 | | |
2011 | | void ogs_pfcp_far_f_teid_hash_set(ogs_pfcp_far_t *far) |
2012 | 0 | { |
2013 | 0 | ogs_pfcp_far_hash_f_teid_t key; |
2014 | 0 | int len; |
2015 | |
|
2016 | 0 | ogs_assert(far); |
2017 | | |
2018 | 0 | len = far_f_teid_hash_key(far, &key); |
2019 | 0 | if (far->hash.f_teid.len == len && |
2020 | 0 | memcmp(&far->hash.f_teid.key, &key, len) == 0) |
2021 | 0 | return; |
2022 | | |
2023 | | /* |
2024 | | * len is nonzero only for the FAR that registered the entry. Remove its |
2025 | | * old key before testing the new one: a collision must not leave Error |
2026 | | * Indications for the old tunnel pointing to an already updated FAR. |
2027 | | */ |
2028 | 0 | if (far->hash.f_teid.len) { |
2029 | 0 | ogs_hash_set(self.far_f_teid_hash, |
2030 | 0 | &far->hash.f_teid.key, far->hash.f_teid.len, NULL); |
2031 | 0 | far->hash.f_teid.len = 0; |
2032 | 0 | } |
2033 | | |
2034 | | /* |
2035 | | * QoS flows may share a remote tunnel. This lookup is only for Error |
2036 | | * Indications, so keep the first FAR without rejecting the request. |
2037 | | * The final GTP node pass repeats this check on every accepted |
2038 | | * modification, so this is expected for each additional QoS flow. |
2039 | | */ |
2040 | 0 | if (ogs_hash_get(self.far_f_teid_hash, &key, len)) |
2041 | 0 | return; |
2042 | | |
2043 | 0 | memcpy(&far->hash.f_teid.key, &key, len); |
2044 | 0 | far->hash.f_teid.len = len; |
2045 | 0 | ogs_hash_set(self.far_f_teid_hash, |
2046 | 0 | &far->hash.f_teid.key, len, far); |
2047 | 0 | ogs_info("Register Error Indication F-TEID[0x%x] [FAR-ID:%u]", |
2048 | 0 | key.teid, far->id); |
2049 | 0 | } |
2050 | | |
2051 | | ogs_pfcp_far_t *ogs_pfcp_far_find_by_gtpu_error_indication(ogs_pkbuf_t *pkbuf) |
2052 | 0 | { |
2053 | 0 | ogs_pfcp_far_hash_f_teid_t hashkey; |
2054 | 0 | int hashkey_len; |
2055 | |
|
2056 | 0 | uint32_t teid; |
2057 | 0 | uint16_t len; |
2058 | 0 | unsigned char *p = NULL; |
2059 | |
|
2060 | 0 | ogs_assert(pkbuf); |
2061 | | |
2062 | 0 | p = pkbuf->data; |
2063 | 0 | ogs_assert(p); |
2064 | | |
2065 | | /* |
2066 | | * 8.3 Tunnel Endpoint Identifier Data I |
2067 | | * |
2068 | | * Octet 1 : Type = 16 (Decimal) |
2069 | | * Octet 2-5 : Tunnel Endpoint Identitifer Data I |
2070 | | */ |
2071 | 0 | if (*p != 16) { |
2072 | 0 | ogs_error("Unknown Type [%d]", *p); |
2073 | 0 | return NULL; |
2074 | 0 | } |
2075 | 0 | p += 1; |
2076 | |
|
2077 | 0 | memcpy(&teid, p, 4); |
2078 | 0 | teid = be32toh(teid); |
2079 | 0 | p += 4; |
2080 | | |
2081 | | /* |
2082 | | * 8.4 GTP-U Peer Address |
2083 | | * |
2084 | | * Octet 1 : Type = 133 (Decimal) |
2085 | | * Octet 2-3 : Length |
2086 | | * Octet 4-n : IPv4 or IPv6 Address |
2087 | | */ |
2088 | 0 | if (*p != 133) { |
2089 | 0 | ogs_error("Unknown Type [%d]", *p); |
2090 | 0 | return NULL; |
2091 | 0 | } |
2092 | 0 | p += 1; |
2093 | |
|
2094 | 0 | memcpy(&len, p, 2); |
2095 | 0 | len = be16toh(len); |
2096 | 0 | p += 2; |
2097 | |
|
2098 | 0 | if (len == OGS_IPV4_LEN) { |
2099 | 0 | } else if (len == OGS_IPV6_LEN) { |
2100 | 0 | } else { |
2101 | 0 | ogs_error("Invalid Length [%d]", len); |
2102 | 0 | return NULL; |
2103 | 0 | } |
2104 | | |
2105 | 0 | hashkey.teid = teid; |
2106 | 0 | memcpy(hashkey.addr, p, len); |
2107 | 0 | hashkey_len = 4 + len; |
2108 | |
|
2109 | 0 | return (ogs_pfcp_far_t *)ogs_hash_get( |
2110 | 0 | self.far_f_teid_hash, &hashkey, hashkey_len); |
2111 | 0 | } |
2112 | | |
2113 | | ogs_pfcp_far_t *ogs_pfcp_far_find_by_pfcp_session_report( |
2114 | | ogs_pfcp_sess_t *sess, |
2115 | | ogs_pfcp_tlv_error_indication_report_t *error_indication_report) |
2116 | 0 | { |
2117 | 0 | ogs_pfcp_far_t *far = NULL; |
2118 | 0 | ogs_pfcp_f_teid_t *remote_f_teid = NULL; |
2119 | |
|
2120 | 0 | uint32_t teid; |
2121 | 0 | uint16_t len; /* OGS_IPV4_LEN or OGS_IPV6_LEN */ |
2122 | 0 | uint32_t addr[4]; |
2123 | |
|
2124 | 0 | ogs_assert(sess); |
2125 | 0 | ogs_assert(error_indication_report); |
2126 | | |
2127 | 0 | if (error_indication_report->presence == 0) { |
2128 | 0 | ogs_error("No Error Indication Report"); |
2129 | 0 | return NULL; |
2130 | 0 | } |
2131 | | |
2132 | 0 | if (error_indication_report->remote_f_teid.presence == 0) { |
2133 | 0 | ogs_error("No Remote F-TEID"); |
2134 | 0 | return NULL; |
2135 | 0 | } |
2136 | | |
2137 | 0 | remote_f_teid = error_indication_report->remote_f_teid.data; |
2138 | 0 | ogs_assert(remote_f_teid); |
2139 | | |
2140 | 0 | teid = be32toh(remote_f_teid->teid); |
2141 | 0 | if (remote_f_teid->ipv4 && remote_f_teid->ipv6) { |
2142 | 0 | ogs_error("User plane should not set both IPv4 and IPv6"); |
2143 | 0 | return NULL; |
2144 | 0 | } else if (remote_f_teid->ipv4) { |
2145 | 0 | len = OGS_IPV4_LEN; |
2146 | 0 | memcpy(addr, &remote_f_teid->addr, len); |
2147 | 0 | } else if (remote_f_teid->ipv6) { |
2148 | 0 | len = OGS_IPV6_LEN; |
2149 | 0 | memcpy(addr, remote_f_teid->addr6, len); |
2150 | 0 | } else { |
2151 | 0 | ogs_error("No IPv4 and IPv6"); |
2152 | 0 | return NULL; |
2153 | 0 | } |
2154 | | |
2155 | 0 | ogs_list_for_each(&sess->far_list, far) { |
2156 | 0 | if (teid == far->outer_header_creation.teid) |
2157 | 0 | return far; |
2158 | 0 | } |
2159 | | |
2160 | 0 | ogs_error("Cannot find the session context " |
2161 | 0 | "[TEID:0x%x,LEN:%d,ADDR:%08x %08x %08x %08x]", |
2162 | 0 | teid, len, be32toh(addr[0]), be32toh(addr[1]), |
2163 | 0 | be32toh(addr[2]), be32toh(addr[3])); |
2164 | |
|
2165 | 0 | return NULL; |
2166 | 0 | } |
2167 | | |
2168 | | void ogs_pfcp_far_teid_hash_set(ogs_pfcp_far_t *far) |
2169 | 0 | { |
2170 | 0 | ogs_assert(far); |
2171 | | |
2172 | 0 | if (far->hash.teid.len) |
2173 | 0 | ogs_hash_set(self.far_teid_hash, |
2174 | 0 | &far->hash.teid.key, far->hash.teid.len, NULL); |
2175 | |
|
2176 | 0 | far->hash.teid.key = far->outer_header_creation.teid; |
2177 | 0 | far->hash.teid.len = sizeof(far->hash.teid.key); |
2178 | |
|
2179 | 0 | ogs_hash_set(self.far_teid_hash, |
2180 | 0 | &far->hash.teid.key, far->hash.teid.len, far); |
2181 | 0 | } |
2182 | | |
2183 | | ogs_pfcp_far_t *ogs_pfcp_far_find_by_teid(uint32_t teid) |
2184 | 0 | { |
2185 | 0 | return (ogs_pfcp_far_t *)ogs_hash_get( |
2186 | 0 | self.far_teid_hash, &teid, sizeof(teid)); |
2187 | 0 | } |
2188 | | |
2189 | | void ogs_pfcp_far_remove(ogs_pfcp_far_t *far) |
2190 | 0 | { |
2191 | 0 | int i; |
2192 | 0 | ogs_pfcp_sess_t *sess = NULL; |
2193 | |
|
2194 | 0 | ogs_assert(far); |
2195 | 0 | sess = far->sess; |
2196 | 0 | ogs_assert(sess); |
2197 | | |
2198 | 0 | ogs_list_remove(&sess->far_list, far); |
2199 | |
|
2200 | 0 | if (far->hash.teid.len) |
2201 | 0 | ogs_hash_set(self.far_teid_hash, |
2202 | 0 | &far->hash.teid.key, far->hash.teid.len, NULL); |
2203 | | |
2204 | | /* |
2205 | | * Unlike the PDR data-path lookup, this Error Indication mapping ends |
2206 | | * with its registered FAR. Unregistered sharers have len == 0, so their |
2207 | | * removal cannot erase another FAR's entry. There is no FAR handoff. |
2208 | | */ |
2209 | 0 | if (far->hash.f_teid.len) { |
2210 | 0 | ogs_info("Remove Error Indication F-TEID[0x%x] " |
2211 | 0 | "[FAR-ID:%u]", far->hash.f_teid.key.teid, far->id); |
2212 | 0 | ogs_hash_set(self.far_f_teid_hash, |
2213 | 0 | &far->hash.f_teid.key, far->hash.f_teid.len, NULL); |
2214 | 0 | } |
2215 | |
|
2216 | 0 | if (far->gnode) { |
2217 | 0 | gtpu_peer_release(far->gnode); |
2218 | 0 | far->gnode = NULL; |
2219 | 0 | } |
2220 | |
|
2221 | 0 | if (far->dnn) |
2222 | 0 | ogs_free(far->dnn); |
2223 | |
|
2224 | 0 | for (i = 0; i < far->num_of_buffered_gtpu; i++) |
2225 | 0 | ogs_pkbuf_free(far->buffered_gtpu[i]); |
2226 | |
|
2227 | 0 | if (far->id_node) |
2228 | 0 | ogs_pool_free(&far->sess->far_id_pool, far->id_node); |
2229 | |
|
2230 | 0 | ogs_pool_free(&ogs_pfcp_far_pool, far); |
2231 | 0 | } |
2232 | | |
2233 | | void ogs_pfcp_far_remove_all(ogs_pfcp_sess_t *sess) |
2234 | 0 | { |
2235 | 0 | ogs_pfcp_far_t *far = NULL, *next_far = NULL; |
2236 | |
|
2237 | 0 | ogs_assert(sess); |
2238 | | |
2239 | 0 | ogs_list_for_each_safe(&sess->far_list, next_far, far) |
2240 | 0 | ogs_pfcp_far_remove(far); |
2241 | 0 | } |
2242 | | |
2243 | | ogs_pfcp_urr_t *ogs_pfcp_urr_add(ogs_pfcp_sess_t *sess) |
2244 | 0 | { |
2245 | 0 | ogs_pfcp_urr_t *urr = NULL; |
2246 | |
|
2247 | 0 | ogs_assert(sess); |
2248 | | |
2249 | 0 | ogs_pool_alloc(&ogs_pfcp_urr_pool, &urr); |
2250 | 0 | if (urr == NULL) { |
2251 | 0 | ogs_error("urr_pool() failed"); |
2252 | 0 | return NULL; |
2253 | 0 | } |
2254 | 0 | memset(urr, 0, sizeof *urr); |
2255 | |
|
2256 | 0 | ogs_pool_alloc(&sess->urr_id_pool, &urr->id_node); |
2257 | 0 | if (urr->id_node == NULL) { |
2258 | 0 | ogs_error("urr_id_pool() failed"); |
2259 | 0 | ogs_pool_free(&ogs_pfcp_urr_pool, urr); |
2260 | 0 | return NULL; |
2261 | 0 | } |
2262 | | |
2263 | 0 | urr->id = *(urr->id_node); |
2264 | 0 | ogs_assert(urr->id > 0 && urr->id <= OGS_MAX_NUM_OF_URR); |
2265 | | |
2266 | 0 | urr->sess = sess; |
2267 | 0 | ogs_list_add(&sess->urr_list, urr); |
2268 | |
|
2269 | 0 | return urr; |
2270 | 0 | } |
2271 | | |
2272 | | ogs_pfcp_urr_t *ogs_pfcp_urr_find( |
2273 | | ogs_pfcp_sess_t *sess, ogs_pfcp_urr_id_t id) |
2274 | 0 | { |
2275 | 0 | ogs_pfcp_urr_t *urr = NULL; |
2276 | |
|
2277 | 0 | ogs_assert(sess); |
2278 | | |
2279 | 0 | ogs_list_for_each(&sess->urr_list, urr) |
2280 | 0 | if (urr->id == id) return urr; |
2281 | | |
2282 | 0 | return NULL; |
2283 | 0 | } |
2284 | | |
2285 | | ogs_pfcp_urr_t *ogs_pfcp_urr_find_or_add( |
2286 | | ogs_pfcp_sess_t *sess, ogs_pfcp_urr_id_t id) |
2287 | 0 | { |
2288 | 0 | ogs_pfcp_urr_t *urr = NULL; |
2289 | |
|
2290 | 0 | ogs_assert(sess); |
2291 | | |
2292 | 0 | urr = ogs_pfcp_urr_find(sess, id); |
2293 | 0 | if (!urr) { |
2294 | 0 | urr = ogs_pfcp_urr_add(sess); |
2295 | 0 | if (!urr) { |
2296 | 0 | ogs_error("ogs_pfcp_urr_add() failed"); |
2297 | 0 | return NULL; |
2298 | 0 | } |
2299 | 0 | urr->id = id; |
2300 | 0 | } |
2301 | | |
2302 | 0 | return urr; |
2303 | 0 | } |
2304 | | |
2305 | | void ogs_pfcp_urr_remove(ogs_pfcp_urr_t *urr) |
2306 | 0 | { |
2307 | 0 | ogs_pfcp_sess_t *sess = NULL; |
2308 | |
|
2309 | 0 | ogs_assert(urr); |
2310 | 0 | sess = urr->sess; |
2311 | 0 | ogs_assert(sess); |
2312 | | |
2313 | 0 | ogs_list_remove(&sess->urr_list, urr); |
2314 | |
|
2315 | 0 | if (urr->id_node) |
2316 | 0 | ogs_pool_free(&urr->sess->urr_id_pool, urr->id_node); |
2317 | |
|
2318 | 0 | ogs_pool_free(&ogs_pfcp_urr_pool, urr); |
2319 | 0 | } |
2320 | | |
2321 | | void ogs_pfcp_urr_remove_all(ogs_pfcp_sess_t *sess) |
2322 | 0 | { |
2323 | 0 | ogs_pfcp_urr_t *urr = NULL, *next_urr = NULL; |
2324 | |
|
2325 | 0 | ogs_assert(sess); |
2326 | | |
2327 | 0 | ogs_list_for_each_safe(&sess->urr_list, next_urr, urr) |
2328 | 0 | ogs_pfcp_urr_remove(urr); |
2329 | 0 | } |
2330 | | |
2331 | | ogs_pfcp_qer_t *ogs_pfcp_qer_add(ogs_pfcp_sess_t *sess) |
2332 | 0 | { |
2333 | 0 | ogs_pfcp_qer_t *qer = NULL; |
2334 | |
|
2335 | 0 | ogs_assert(sess); |
2336 | | |
2337 | 0 | ogs_pool_alloc(&ogs_pfcp_qer_pool, &qer); |
2338 | 0 | if (qer == NULL) { |
2339 | 0 | ogs_error("qer_pool() failed"); |
2340 | 0 | return NULL; |
2341 | 0 | } |
2342 | 0 | memset(qer, 0, sizeof *qer); |
2343 | |
|
2344 | 0 | ogs_pool_alloc(&sess->qer_id_pool, &qer->id_node); |
2345 | 0 | if (qer->id_node == NULL) { |
2346 | 0 | ogs_error("qer_id_pool() failed"); |
2347 | 0 | ogs_pool_free(&ogs_pfcp_qer_pool, qer); |
2348 | 0 | return NULL; |
2349 | 0 | } |
2350 | | |
2351 | 0 | qer->id = *(qer->id_node); |
2352 | 0 | ogs_assert(qer->id > 0 && qer->id <= OGS_MAX_NUM_OF_QER); |
2353 | | |
2354 | 0 | qer->sess = sess; |
2355 | 0 | ogs_list_add(&sess->qer_list, qer); |
2356 | |
|
2357 | 0 | return qer; |
2358 | 0 | } |
2359 | | |
2360 | | ogs_pfcp_qer_t *ogs_pfcp_qer_find( |
2361 | | ogs_pfcp_sess_t *sess, ogs_pfcp_qer_id_t id) |
2362 | 0 | { |
2363 | 0 | ogs_pfcp_qer_t *qer = NULL; |
2364 | |
|
2365 | 0 | ogs_assert(sess); |
2366 | | |
2367 | 0 | ogs_list_for_each(&sess->qer_list, qer) |
2368 | 0 | if (qer->id == id) return qer; |
2369 | | |
2370 | 0 | return NULL; |
2371 | 0 | } |
2372 | | |
2373 | | ogs_pfcp_qer_t *ogs_pfcp_qer_find_or_add( |
2374 | | ogs_pfcp_sess_t *sess, ogs_pfcp_qer_id_t id) |
2375 | 0 | { |
2376 | 0 | ogs_pfcp_qer_t *qer = NULL; |
2377 | |
|
2378 | 0 | ogs_assert(sess); |
2379 | | |
2380 | 0 | qer = ogs_pfcp_qer_find(sess, id); |
2381 | 0 | if (!qer) { |
2382 | 0 | qer = ogs_pfcp_qer_add(sess); |
2383 | 0 | if (!qer) { |
2384 | 0 | ogs_error("ogs_pfcp_qer_add() failed"); |
2385 | 0 | return NULL; |
2386 | 0 | } |
2387 | 0 | qer->id = id; |
2388 | 0 | } |
2389 | | |
2390 | 0 | return qer; |
2391 | 0 | } |
2392 | | |
2393 | | void ogs_pfcp_qer_remove(ogs_pfcp_qer_t *qer) |
2394 | 0 | { |
2395 | 0 | ogs_pfcp_sess_t *sess = NULL; |
2396 | |
|
2397 | 0 | ogs_assert(qer); |
2398 | 0 | sess = qer->sess; |
2399 | 0 | ogs_assert(sess); |
2400 | | |
2401 | 0 | ogs_list_remove(&sess->qer_list, qer); |
2402 | |
|
2403 | 0 | if (qer->id_node) |
2404 | 0 | ogs_pool_free(&qer->sess->qer_id_pool, qer->id_node); |
2405 | |
|
2406 | 0 | ogs_pool_free(&ogs_pfcp_qer_pool, qer); |
2407 | 0 | } |
2408 | | |
2409 | | void ogs_pfcp_qer_remove_all(ogs_pfcp_sess_t *sess) |
2410 | 0 | { |
2411 | 0 | ogs_pfcp_qer_t *qer = NULL, *next_qer = NULL; |
2412 | |
|
2413 | 0 | ogs_assert(sess); |
2414 | | |
2415 | 0 | ogs_list_for_each_safe(&sess->qer_list, next_qer, qer) |
2416 | 0 | ogs_pfcp_qer_remove(qer); |
2417 | 0 | } |
2418 | | |
2419 | | ogs_pfcp_bar_t *ogs_pfcp_bar_new(ogs_pfcp_sess_t *sess) |
2420 | 0 | { |
2421 | 0 | ogs_pfcp_bar_t *bar = NULL; |
2422 | |
|
2423 | 0 | ogs_assert(sess); |
2424 | 0 | ogs_assert(sess->bar == NULL); /* Only One BAR is supported */ |
2425 | | |
2426 | 0 | ogs_pool_alloc(&ogs_pfcp_bar_pool, &bar); |
2427 | 0 | ogs_assert(bar); |
2428 | 0 | memset(bar, 0, sizeof *bar); |
2429 | |
|
2430 | 0 | ogs_pool_alloc(&sess->bar_id_pool, &bar->id_node); |
2431 | 0 | ogs_assert(bar->id_node); |
2432 | | |
2433 | 0 | bar->id = *(bar->id_node); |
2434 | 0 | ogs_assert(bar->id > 0 && bar->id <= OGS_MAX_NUM_OF_BAR); |
2435 | | |
2436 | 0 | bar->sess = sess; |
2437 | 0 | sess->bar = bar; |
2438 | |
|
2439 | 0 | return bar; |
2440 | 0 | } |
2441 | | |
2442 | | void ogs_pfcp_bar_delete(ogs_pfcp_bar_t *bar) |
2443 | 0 | { |
2444 | 0 | ogs_pfcp_sess_t *sess = NULL; |
2445 | |
|
2446 | 0 | ogs_assert(bar); |
2447 | 0 | sess = bar->sess; |
2448 | 0 | ogs_assert(sess); |
2449 | | |
2450 | 0 | if (bar->id_node) |
2451 | 0 | ogs_pool_free(&bar->sess->bar_id_pool, bar->id_node); |
2452 | |
|
2453 | 0 | bar->sess = NULL; |
2454 | 0 | sess->bar = NULL; |
2455 | |
|
2456 | 0 | ogs_pool_free(&ogs_pfcp_bar_pool, bar); |
2457 | 0 | } |
2458 | | |
2459 | | ogs_pfcp_rule_t *ogs_pfcp_rule_add(ogs_pfcp_pdr_t *pdr) |
2460 | 0 | { |
2461 | 0 | ogs_pfcp_rule_t *rule = NULL; |
2462 | |
|
2463 | 0 | ogs_assert(pdr); |
2464 | | |
2465 | 0 | ogs_pool_alloc(&ogs_pfcp_rule_pool, &rule); |
2466 | 0 | ogs_assert(rule); |
2467 | 0 | memset(rule, 0, sizeof *rule); |
2468 | |
|
2469 | 0 | rule->pdr = pdr; |
2470 | 0 | ogs_list_add(&pdr->rule_list, rule); |
2471 | |
|
2472 | 0 | return rule; |
2473 | 0 | } |
2474 | | |
2475 | | ogs_pfcp_rule_t *ogs_pfcp_rule_find_by_sdf_filter_id( |
2476 | | ogs_pfcp_sess_t *sess, uint32_t sdf_filter_id) |
2477 | 0 | { |
2478 | 0 | ogs_pfcp_pdr_t *pdr = NULL; |
2479 | 0 | ogs_pfcp_rule_t *rule = NULL; |
2480 | |
|
2481 | 0 | ogs_assert(sess); |
2482 | | |
2483 | 0 | ogs_list_for_each(&sess->pdr_list, pdr) { |
2484 | 0 | ogs_list_for_each(&pdr->rule_list, rule) { |
2485 | 0 | if (rule->bid && rule->sdf_filter_id == sdf_filter_id) |
2486 | 0 | return rule; |
2487 | 0 | } |
2488 | 0 | } |
2489 | | |
2490 | 0 | return NULL; |
2491 | 0 | } |
2492 | | |
2493 | | void ogs_pfcp_rule_remove(ogs_pfcp_rule_t *rule) |
2494 | 0 | { |
2495 | 0 | ogs_pfcp_pdr_t *pdr = NULL; |
2496 | |
|
2497 | 0 | ogs_assert(rule); |
2498 | 0 | pdr = rule->pdr; |
2499 | 0 | ogs_assert(pdr); |
2500 | | |
2501 | 0 | ogs_list_remove(&pdr->rule_list, rule); |
2502 | 0 | ogs_pool_free(&ogs_pfcp_rule_pool, rule); |
2503 | 0 | } |
2504 | | |
2505 | | void ogs_pfcp_rule_remove_all(ogs_pfcp_pdr_t *pdr) |
2506 | 0 | { |
2507 | 0 | ogs_pfcp_rule_t *rule = NULL, *next_rule = NULL; |
2508 | |
|
2509 | 0 | ogs_assert(pdr); |
2510 | | |
2511 | 0 | ogs_list_for_each_safe(&pdr->rule_list, next_rule, rule) |
2512 | 0 | ogs_pfcp_rule_remove(rule); |
2513 | 0 | } |
2514 | | |
2515 | | int ogs_pfcp_ue_pool_generate(void) |
2516 | 0 | { |
2517 | 0 | int i, rv; |
2518 | 0 | ogs_pfcp_subnet_t *subnet = NULL; |
2519 | |
|
2520 | 0 | ogs_list_for_each(&self.subnet_list, subnet) { |
2521 | 0 | int maxbytes = 0; |
2522 | 0 | int lastindex = 0; |
2523 | 0 | uint32_t start[4], end[4], broadcast[4]; |
2524 | 0 | int rangeindex, num_of_range; |
2525 | 0 | int poolindex; |
2526 | 0 | int inc; |
2527 | |
|
2528 | 0 | if (subnet->family == AF_INET) { |
2529 | 0 | maxbytes = 4; |
2530 | 0 | lastindex = 0; |
2531 | 0 | } else if (subnet->family == AF_INET6) { |
2532 | 0 | maxbytes = 8; /* Default Prefixlen 64bits */ |
2533 | 0 | lastindex = 1; |
2534 | 0 | } else { |
2535 | | /* subnet->family might be AF_UNSPEC. So, skip it */ |
2536 | 0 | continue; |
2537 | 0 | } |
2538 | | |
2539 | 0 | for (i = 0; i < 4; i++) { |
2540 | 0 | broadcast[i] = subnet->sub.sub[i] + ~subnet->sub.mask[i]; |
2541 | 0 | } |
2542 | |
|
2543 | 0 | num_of_range = subnet->num_of_range; |
2544 | 0 | if (!num_of_range) num_of_range = 1; |
2545 | |
|
2546 | 0 | poolindex = 0; |
2547 | 0 | for (rangeindex = 0; rangeindex < num_of_range; rangeindex++) { |
2548 | |
|
2549 | 0 | if (subnet->num_of_range && |
2550 | 0 | subnet->range[rangeindex].low) { |
2551 | 0 | ogs_ipsubnet_t low; |
2552 | 0 | rv = ogs_ipsubnet(&low, subnet->range[rangeindex].low, NULL); |
2553 | 0 | ogs_assert(rv == OGS_OK); |
2554 | 0 | memcpy(start, low.sub, maxbytes); |
2555 | 0 | } else { |
2556 | 0 | memcpy(start, subnet->sub.sub, maxbytes); |
2557 | 0 | } |
2558 | | |
2559 | 0 | if (subnet->num_of_range && |
2560 | 0 | subnet->range[rangeindex].high) { |
2561 | 0 | ogs_ipsubnet_t high; |
2562 | 0 | rv = ogs_ipsubnet(&high, subnet->range[rangeindex].high, NULL); |
2563 | 0 | ogs_assert(rv == OGS_OK); |
2564 | 0 | high.sub[lastindex] += htobe32(1); |
2565 | 0 | memcpy(end, high.sub, maxbytes); |
2566 | 0 | } else { |
2567 | 0 | memcpy(end, broadcast, maxbytes); |
2568 | 0 | } |
2569 | | |
2570 | 0 | inc = 0; |
2571 | 0 | while(poolindex < ogs_app()->pool.sess) { |
2572 | 0 | ogs_pfcp_ue_ip_t *ue_ip = NULL; |
2573 | |
|
2574 | 0 | ue_ip = &subnet->pool.array[poolindex]; |
2575 | 0 | ogs_assert(ue_ip); |
2576 | 0 | memset(ue_ip, 0, sizeof *ue_ip); |
2577 | 0 | ue_ip->subnet = subnet; |
2578 | |
|
2579 | 0 | memcpy(ue_ip->addr, start, maxbytes); |
2580 | 0 | ue_ip->addr[lastindex] += htobe32(inc); |
2581 | 0 | inc++; |
2582 | |
|
2583 | 0 | if (memcmp(ue_ip->addr, end, maxbytes) == 0) |
2584 | 0 | break; |
2585 | | |
2586 | | /* Exclude Network Address */ |
2587 | 0 | if (memcmp(ue_ip->addr, subnet->sub.sub, maxbytes) == 0) |
2588 | 0 | continue; |
2589 | | |
2590 | | /* Exclude TUN IP Address */ |
2591 | 0 | if (memcmp(ue_ip->addr, subnet->gw.sub, maxbytes) == 0) |
2592 | 0 | continue; |
2593 | | |
2594 | | /* Allocate Full IPv6 Address */ |
2595 | 0 | if (lastindex == 1) |
2596 | 0 | ue_ip->addr[3] += htobe32(inc); |
2597 | |
|
2598 | 0 | ogs_trace("[%d] - %x:%x:%x:%x", |
2599 | 0 | poolindex, |
2600 | 0 | ue_ip->addr[0], ue_ip->addr[1], |
2601 | 0 | ue_ip->addr[2], ue_ip->addr[3]); |
2602 | |
|
2603 | 0 | poolindex++; |
2604 | 0 | } |
2605 | 0 | } |
2606 | 0 | subnet->pool.size = subnet->pool.avail = poolindex; |
2607 | 0 | } |
2608 | | |
2609 | 0 | return OGS_OK; |
2610 | 0 | } |
2611 | | |
2612 | | ogs_pfcp_ue_ip_t *ogs_pfcp_ue_ip_alloc( |
2613 | | uint8_t *cause_value, int family, const char *dnn, uint8_t *addr) |
2614 | 0 | { |
2615 | 0 | ogs_pfcp_subnet_t *subnet = NULL; |
2616 | 0 | ogs_pfcp_ue_ip_t *ue_ip = NULL; |
2617 | |
|
2618 | 0 | uint8_t zero[16]; |
2619 | 0 | size_t maxbytes = 0; |
2620 | |
|
2621 | 0 | memset(zero, 0, sizeof zero); |
2622 | 0 | if (family == AF_INET) { |
2623 | 0 | maxbytes = 4; |
2624 | 0 | } else if (family == AF_INET6) { |
2625 | 0 | maxbytes = 16; |
2626 | 0 | } else { |
2627 | 0 | ogs_error("Invalid family[%d]", family); |
2628 | 0 | ogs_assert_if_reached(); |
2629 | 0 | return NULL; |
2630 | 0 | } |
2631 | | |
2632 | 0 | if (dnn) |
2633 | 0 | subnet = ogs_pfcp_find_subnet_by_dnn(family, dnn); |
2634 | 0 | else |
2635 | 0 | subnet = ogs_pfcp_find_subnet(family); |
2636 | |
|
2637 | 0 | if (subnet == NULL) { |
2638 | 0 | ogs_error("All IP addresses in all subnets are occupied"); |
2639 | 0 | *cause_value = OGS_PFCP_CAUSE_NO_RESOURCES_AVAILABLE; |
2640 | 0 | return NULL; |
2641 | 0 | } |
2642 | | |
2643 | | /* if assigning a static IP, do so. If not, assign dynamically! */ |
2644 | 0 | if (memcmp(addr, zero, maxbytes) != 0) { |
2645 | 0 | ue_ip = ogs_calloc(1, sizeof(ogs_pfcp_ue_ip_t)); |
2646 | 0 | if (!ue_ip) { |
2647 | 0 | ogs_error("All dynamic addresses are occupied"); |
2648 | 0 | *cause_value = OGS_PFCP_CAUSE_ALL_DYNAMIC_ADDRESS_ARE_OCCUPIED; |
2649 | 0 | return NULL; |
2650 | 0 | } |
2651 | | |
2652 | 0 | ue_ip->subnet = subnet; |
2653 | 0 | ue_ip->static_ip = true; |
2654 | 0 | memcpy(ue_ip->addr, addr, maxbytes); |
2655 | 0 | } else { |
2656 | 0 | ogs_pool_alloc(&subnet->pool, &ue_ip); |
2657 | 0 | if (!ue_ip) { |
2658 | 0 | ogs_error("No resources available"); |
2659 | 0 | *cause_value = OGS_PFCP_CAUSE_NO_RESOURCES_AVAILABLE; |
2660 | 0 | return NULL; |
2661 | 0 | } |
2662 | 0 | } |
2663 | | |
2664 | 0 | return ue_ip; |
2665 | 0 | } |
2666 | | |
2667 | | void ogs_pfcp_ue_ip_free(ogs_pfcp_ue_ip_t *ue_ip) |
2668 | 0 | { |
2669 | 0 | ogs_pfcp_subnet_t *subnet = NULL; |
2670 | |
|
2671 | 0 | ogs_assert(ue_ip); |
2672 | 0 | subnet = ue_ip->subnet; |
2673 | |
|
2674 | 0 | ogs_assert(subnet); |
2675 | | |
2676 | 0 | if (ue_ip->static_ip) { |
2677 | 0 | ogs_free(ue_ip); |
2678 | 0 | } else { |
2679 | 0 | ogs_pool_free(&subnet->pool, ue_ip); |
2680 | 0 | } |
2681 | 0 | } |
2682 | | |
2683 | | ogs_pfcp_dev_t *ogs_pfcp_dev_add(const char *ifname) |
2684 | 0 | { |
2685 | 0 | ogs_pfcp_dev_t *dev = NULL; |
2686 | |
|
2687 | 0 | ogs_assert(ifname); |
2688 | | |
2689 | 0 | ogs_pool_alloc(&ogs_pfcp_dev_pool, &dev); |
2690 | 0 | ogs_assert(dev); |
2691 | 0 | memset(dev, 0, sizeof *dev); |
2692 | |
|
2693 | 0 | ogs_cpystrn(dev->ifname, ifname, OGS_MAX_IFNAME_LEN-1); |
2694 | |
|
2695 | 0 | ogs_list_add(&self.dev_list, dev); |
2696 | |
|
2697 | 0 | return dev; |
2698 | 0 | } |
2699 | | |
2700 | | void ogs_pfcp_dev_remove(ogs_pfcp_dev_t *dev) |
2701 | 0 | { |
2702 | 0 | ogs_assert(dev); |
2703 | | |
2704 | 0 | ogs_list_remove(&self.dev_list, dev); |
2705 | 0 | ogs_pool_free(&ogs_pfcp_dev_pool, dev); |
2706 | 0 | } |
2707 | | |
2708 | | void ogs_pfcp_dev_remove_all(void) |
2709 | 0 | { |
2710 | 0 | ogs_pfcp_dev_t *dev = NULL, *next_dev = NULL; |
2711 | |
|
2712 | 0 | ogs_list_for_each_safe(&self.dev_list, next_dev, dev) |
2713 | 0 | ogs_pfcp_dev_remove(dev); |
2714 | 0 | } |
2715 | | |
2716 | | ogs_pfcp_dev_t *ogs_pfcp_dev_find_by_ifname(const char *ifname) |
2717 | 0 | { |
2718 | 0 | ogs_pfcp_dev_t *dev = NULL; |
2719 | |
|
2720 | 0 | ogs_assert(ifname); |
2721 | | |
2722 | 0 | ogs_list_for_each(&ogs_pfcp_self()->dev_list, dev) { |
2723 | 0 | if (ogs_strcasecmp(dev->ifname, ifname) == 0) |
2724 | 0 | return dev; |
2725 | 0 | } |
2726 | | |
2727 | 0 | return OGS_OK; |
2728 | 0 | } |
2729 | | |
2730 | | ogs_pfcp_subnet_t *ogs_pfcp_subnet_add( |
2731 | | const char *ipstr, const char *mask_or_numbits, |
2732 | | const char *gateway, const char *dnn, const char *ifname) |
2733 | 0 | { |
2734 | 0 | int rv; |
2735 | 0 | ogs_pfcp_dev_t *dev = NULL; |
2736 | 0 | ogs_pfcp_subnet_t *subnet = NULL; |
2737 | |
|
2738 | 0 | ogs_assert(ifname); |
2739 | | |
2740 | 0 | dev = ogs_pfcp_dev_find_by_ifname(ifname); |
2741 | 0 | if (!dev) |
2742 | 0 | dev = ogs_pfcp_dev_add(ifname); |
2743 | 0 | ogs_assert(dev); |
2744 | | |
2745 | 0 | ogs_pool_alloc(&ogs_pfcp_subnet_pool, &subnet); |
2746 | 0 | ogs_assert(subnet); |
2747 | 0 | memset(subnet, 0, sizeof *subnet); |
2748 | |
|
2749 | 0 | subnet->family = AF_UNSPEC; |
2750 | 0 | subnet->dev = dev; |
2751 | |
|
2752 | 0 | if (ipstr && mask_or_numbits) { |
2753 | 0 | rv = ogs_ipsubnet(&subnet->gw, ipstr, NULL); |
2754 | 0 | ogs_assert(rv == OGS_OK); |
2755 | | |
2756 | 0 | rv = ogs_ipsubnet(&subnet->sub, ipstr, mask_or_numbits); |
2757 | 0 | ogs_assert(rv == OGS_OK); |
2758 | | |
2759 | 0 | subnet->family = subnet->gw.family; |
2760 | 0 | subnet->prefixlen = atoi(mask_or_numbits); |
2761 | |
|
2762 | 0 | if (memcmp(subnet->gw.sub, subnet->sub.sub, |
2763 | 0 | sizeof(subnet->gw.sub)) != 0) { |
2764 | 0 | char *subnet_string = NULL; |
2765 | |
|
2766 | 0 | if (subnet->family == AF_INET) { |
2767 | 0 | subnet_string = ogs_ipv4_to_string(subnet->sub.sub[0]); |
2768 | 0 | ogs_assert(subnet_string); |
2769 | 0 | } else if (subnet->family == AF_INET6) { |
2770 | 0 | subnet_string = ogs_ipv6addr_to_string( |
2771 | 0 | (uint8_t*)&subnet->sub.sub[0]); |
2772 | 0 | ogs_assert(subnet_string); |
2773 | 0 | } |
2774 | | |
2775 | 0 | ogs_warn("Please change the configuration files of " |
2776 | 0 | "smf.yaml and upf.yaml as below."); |
2777 | 0 | ogs_log_print(OGS_LOG_WARN, "\n<OLD Format>\n"); |
2778 | 0 | ogs_log_print(OGS_LOG_WARN, "smf:\n"); |
2779 | 0 | ogs_log_print(OGS_LOG_WARN, " session:\n"); |
2780 | 0 | ogs_log_print(OGS_LOG_WARN, " - subnet: %s/%s\n", |
2781 | 0 | ipstr, mask_or_numbits); |
2782 | 0 | ogs_log_print(OGS_LOG_WARN, "\n<NEW Format>\n"); |
2783 | 0 | ogs_log_print(OGS_LOG_WARN, "smf:\n"); |
2784 | 0 | ogs_log_print(OGS_LOG_WARN, " session:\n"); |
2785 | 0 | ogs_log_print(OGS_LOG_WARN, " - subnet: %s/%s\n", |
2786 | 0 | subnet_string ? subnet_string : "Unknown", mask_or_numbits); |
2787 | 0 | ogs_log_print(OGS_LOG_WARN, " gateway: %s\n\n\n", ipstr); |
2788 | |
|
2789 | 0 | ogs_free(subnet_string); |
2790 | 0 | } |
2791 | 0 | } |
2792 | | |
2793 | 0 | if (gateway) { |
2794 | 0 | rv = ogs_ipsubnet(&subnet->gw, gateway, NULL); |
2795 | 0 | ogs_assert(rv == OGS_OK); |
2796 | 0 | } |
2797 | | |
2798 | 0 | if (dnn) |
2799 | 0 | ogs_cpystrn(subnet->dnn, dnn, OGS_MAX_DNN_LEN); |
2800 | |
|
2801 | 0 | ogs_pool_init(&subnet->pool, ogs_app()->pool.sess); |
2802 | | |
2803 | 0 | ogs_list_add(&self.subnet_list, subnet); |
2804 | |
|
2805 | 0 | return subnet; |
2806 | 0 | } |
2807 | | |
2808 | | void ogs_pfcp_subnet_remove(ogs_pfcp_subnet_t *subnet) |
2809 | 0 | { |
2810 | 0 | ogs_assert(subnet); |
2811 | | |
2812 | 0 | ogs_list_remove(&self.subnet_list, subnet); |
2813 | |
|
2814 | 0 | ogs_pool_final(&subnet->pool); |
2815 | | |
2816 | 0 | ogs_pool_free(&ogs_pfcp_subnet_pool, subnet); |
2817 | 0 | } |
2818 | | |
2819 | | void ogs_pfcp_subnet_remove_all(void) |
2820 | 0 | { |
2821 | 0 | ogs_pfcp_subnet_t *subnet = NULL, *next_subnet = NULL; |
2822 | |
|
2823 | 0 | ogs_list_for_each_safe(&self.subnet_list, next_subnet, subnet) |
2824 | 0 | ogs_pfcp_subnet_remove(subnet); |
2825 | 0 | } |
2826 | | |
2827 | | ogs_pfcp_subnet_t *ogs_pfcp_find_subnet(int family) |
2828 | 0 | { |
2829 | 0 | ogs_pfcp_subnet_t *subnet = NULL; |
2830 | |
|
2831 | 0 | ogs_assert(family == AF_INET || family == AF_INET6); |
2832 | | |
2833 | 0 | ogs_list_for_each(&self.subnet_list, subnet) { |
2834 | 0 | if ((subnet->family == AF_UNSPEC || subnet->family == family) && |
2835 | 0 | (strlen(subnet->dnn) == 0) && |
2836 | 0 | subnet->pool.avail) |
2837 | 0 | break; |
2838 | 0 | } |
2839 | |
|
2840 | 0 | return subnet; |
2841 | 0 | } |
2842 | | |
2843 | | ogs_pfcp_subnet_t *ogs_pfcp_find_subnet_by_dnn(int family, const char *dnn) |
2844 | 0 | { |
2845 | 0 | ogs_pfcp_subnet_t *subnet = NULL; |
2846 | |
|
2847 | 0 | ogs_assert(dnn); |
2848 | 0 | ogs_assert(family == AF_INET || family == AF_INET6); |
2849 | | |
2850 | 0 | ogs_list_for_each(&self.subnet_list, subnet) { |
2851 | 0 | if ((subnet->family == AF_UNSPEC || subnet->family == family) && |
2852 | 0 | (strlen(subnet->dnn) == 0 || |
2853 | 0 | (strlen(subnet->dnn) && ogs_strcasecmp(subnet->dnn, dnn) == 0)) && |
2854 | 0 | subnet->pool.avail) |
2855 | 0 | break; |
2856 | 0 | } |
2857 | |
|
2858 | 0 | return subnet; |
2859 | 0 | } |
2860 | | |
2861 | | void ogs_pfcp_pool_init(ogs_pfcp_sess_t *sess) |
2862 | 0 | { |
2863 | 0 | ogs_assert(sess); |
2864 | | |
2865 | 0 | sess->obj.type = OGS_PFCP_OBJ_SESS_TYPE; |
2866 | |
|
2867 | 0 | ogs_pool_create(&sess->pdr_id_pool, OGS_MAX_NUM_OF_PDR); |
2868 | 0 | ogs_pool_create(&sess->far_id_pool, OGS_MAX_NUM_OF_FAR); |
2869 | 0 | ogs_pool_create(&sess->urr_id_pool, OGS_MAX_NUM_OF_URR); |
2870 | 0 | ogs_pool_create(&sess->qer_id_pool, OGS_MAX_NUM_OF_QER); |
2871 | 0 | ogs_pool_create(&sess->bar_id_pool, OGS_MAX_NUM_OF_BAR); |
2872 | | |
2873 | 0 | ogs_pool_sequence_id_generate(&sess->pdr_id_pool); |
2874 | 0 | ogs_pool_sequence_id_generate(&sess->far_id_pool); |
2875 | 0 | ogs_pool_sequence_id_generate(&sess->urr_id_pool); |
2876 | 0 | ogs_pool_sequence_id_generate(&sess->qer_id_pool); |
2877 | 0 | ogs_pool_sequence_id_generate(&sess->bar_id_pool); |
2878 | 0 | } |
2879 | | void ogs_pfcp_pool_final(ogs_pfcp_sess_t *sess) |
2880 | 0 | { |
2881 | 0 | ogs_assert(sess); |
2882 | | |
2883 | 0 | ogs_pool_destroy(&sess->pdr_id_pool); |
2884 | 0 | ogs_pool_destroy(&sess->far_id_pool); |
2885 | 0 | ogs_pool_destroy(&sess->urr_id_pool); |
2886 | 0 | ogs_pool_destroy(&sess->qer_id_pool); |
2887 | 0 | ogs_pool_destroy(&sess->bar_id_pool); |
2888 | 0 | } |