/src/open5gs/lib/gtp/util.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2019 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 "ogs-gtp.h" |
21 | | |
22 | | int ogs_gtpu_parse_header( |
23 | | ogs_gtp2_header_desc_t *header_desc, ogs_pkbuf_t *pkbuf) |
24 | 272 | { |
25 | 272 | ogs_gtp2_header_t *gtp_h = NULL; |
26 | 272 | ogs_gtp2_extension_header_t ext_hdesc; |
27 | 272 | uint8_t *ext_h = NULL; |
28 | 272 | uint16_t len = 0; |
29 | 272 | int i; |
30 | | |
31 | 272 | ogs_assert(pkbuf); |
32 | 272 | ogs_assert(pkbuf->data); |
33 | | |
34 | 272 | gtp_h = (ogs_gtp2_header_t *)pkbuf->data; |
35 | | |
36 | 272 | if (header_desc) { |
37 | 136 | memset(header_desc, 0, sizeof(*header_desc)); |
38 | | |
39 | 136 | header_desc->flags = gtp_h->flags; |
40 | 136 | header_desc->type = gtp_h->type; |
41 | 136 | header_desc->teid = be32toh(gtp_h->teid); |
42 | 136 | } |
43 | | |
44 | 272 | len = OGS_GTPV1U_HEADER_LEN; |
45 | 272 | if (pkbuf->len < len) { |
46 | 0 | ogs_error("the length of the packet is insufficient[%d:%d]", |
47 | 0 | pkbuf->len, len); |
48 | 0 | return -1; |
49 | 0 | } |
50 | | |
51 | 272 | if (gtp_h->flags & OGS_GTPU_FLAGS_E) { |
52 | | |
53 | 232 | len += OGS_GTPV1U_EXTENSION_HEADER_LEN; |
54 | 232 | if (pkbuf->len < len) { |
55 | 6 | ogs_error("the length of the packet is insufficient[%d:%d]", |
56 | 6 | pkbuf->len, len); |
57 | 6 | return -1; |
58 | 6 | } |
59 | | |
60 | | /* |
61 | | * TS29.281 |
62 | | * 5.2.1 General format of the GTP-U Extension Header |
63 | | * |
64 | | * If no such Header follows, |
65 | | * then the value of the Next Extension Header Type shall be 0. */ |
66 | | |
67 | 226 | i = 0; |
68 | 3.92k | while (*(ext_h = (((uint8_t *)gtp_h) + len - 1)) && |
69 | 3.88k | i < OGS_GTP2_NUM_OF_EXTENSION_HEADER) { |
70 | | /* |
71 | | * The length of the Extension header shall be defined |
72 | | * in a variable length of 4 octets, i.e. m+1 = n*4 octets, |
73 | | * where n is a positive integer. |
74 | | */ |
75 | | /* The Extension Header Length octet must be in the packet */ |
76 | 3.84k | if (pkbuf->len < len + 1) { |
77 | 54 | ogs_error("the length of the packet is insufficient[%d:%d]", |
78 | 54 | pkbuf->len, len + 1); |
79 | 54 | return -1; |
80 | 54 | } |
81 | | |
82 | 3.78k | len += (*(++ext_h)) * 4; |
83 | 3.78k | if (*ext_h == 0) { |
84 | 16 | ogs_error("No length in the Extension header"); |
85 | 16 | return -1; |
86 | 16 | } |
87 | | |
88 | 3.77k | if (((*ext_h) * 4) > OGS_GTP2_MAX_EXTENSION_HEADER_LEN) { |
89 | 57 | ogs_error("Overflow length : %d", (*ext_h)); |
90 | 57 | return -1; |
91 | 57 | } |
92 | | |
93 | 3.71k | if (pkbuf->len < len) { |
94 | 15 | ogs_error("the length of the packet is insufficient[%d:%d]", |
95 | 15 | pkbuf->len, len); |
96 | 15 | return -1; |
97 | 15 | } |
98 | | |
99 | 3.69k | if (!header_desc) /* Skip to extract header content */ |
100 | 3.25k | continue; |
101 | | |
102 | | /* Copy Header Content */ |
103 | 439 | memcpy(&ext_hdesc.array[i], ext_h-1, (*ext_h) * 4); |
104 | | |
105 | 439 | switch (ext_hdesc.array[i].type) { |
106 | 95 | case OGS_GTP2_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER: |
107 | 95 | header_desc->pdu_type = ext_hdesc.array[i].pdu_type; |
108 | 95 | if (ext_hdesc.array[i].pdu_type == |
109 | 95 | OGS_GTP2_EXTENSION_HEADER_PDU_TYPE_UL_PDU_SESSION_INFORMATION) { |
110 | 23 | header_desc->qos_flow_identifier = |
111 | 23 | ext_hdesc.array[i].qos_flow_identifier; |
112 | 23 | ogs_trace(" QFI [0x%x]", |
113 | 23 | header_desc->qos_flow_identifier); |
114 | 23 | } |
115 | 95 | break; |
116 | 39 | case OGS_GTP2_EXTENSION_HEADER_TYPE_UDP_PORT: |
117 | 39 | header_desc->udp.presence = true; |
118 | 39 | header_desc->udp.port = be16toh(ext_hdesc.array[i].udp_port); |
119 | | |
120 | 39 | ogs_trace(" UDP Port [%d]", header_desc->udp.port); |
121 | 39 | break; |
122 | 27 | case OGS_GTP2_EXTENSION_HEADER_TYPE_PDCP_NUMBER: |
123 | 27 | header_desc->pdcp_number_presence = true; |
124 | 27 | header_desc->pdcp_number = |
125 | 27 | be16toh(ext_hdesc.array[i].pdcp_number); |
126 | | |
127 | 27 | ogs_trace(" PDCP Number [%d]", header_desc->pdcp_number); |
128 | 27 | break; |
129 | 278 | default: |
130 | 278 | break; |
131 | 439 | } |
132 | | |
133 | 439 | i++; |
134 | 439 | } |
135 | | |
136 | 84 | if (i >= OGS_GTP2_NUM_OF_EXTENSION_HEADER) { |
137 | 41 | ogs_error("The number of extension headers is limited to [%d]", i); |
138 | 41 | return -1; |
139 | 41 | } |
140 | | |
141 | 84 | } else if (gtp_h->flags & (OGS_GTPU_FLAGS_S|OGS_GTPU_FLAGS_PN)) { |
142 | | /* |
143 | | * If and only if one or more of these three flags are set, |
144 | | * the fields Sequence Number, N-PDU and Extension Header |
145 | | * shall be present. The sender shall set all the bits of |
146 | | * the unused fields to zero. The receiver shall not evaluate |
147 | | * the unused fields. |
148 | | * For example, if only the E flag is set to 1, then |
149 | | * the N-PDU Number and Sequence Number fields shall also be present, |
150 | | * but will not have meaningful values and shall not be evaluated. |
151 | | */ |
152 | 22 | len += 4; |
153 | 22 | } |
154 | | |
155 | 83 | if (pkbuf->len < len) { |
156 | 8 | ogs_error("the length of the packet is insufficient[%d:%d]", |
157 | 8 | pkbuf->len, len); |
158 | 8 | return -1; |
159 | 8 | } |
160 | | |
161 | 75 | return len; |
162 | 83 | } |
163 | | |
164 | | uint16_t ogs_in_cksum(uint16_t *addr, int len) |
165 | 0 | { |
166 | 0 | int nleft = len; |
167 | 0 | uint32_t sum = 0; |
168 | 0 | uint16_t *w = addr; |
169 | 0 | uint16_t answer = 0; |
170 | | |
171 | | // Adding 16 bits sequentially in sum |
172 | 0 | while (nleft > 1) { |
173 | 0 | sum += *w; |
174 | 0 | nleft -= 2; |
175 | 0 | w++; |
176 | 0 | } |
177 | | |
178 | | // If an odd byte is left |
179 | 0 | if (nleft == 1) { |
180 | 0 | *(uint8_t *) (&answer) = *(uint8_t *) w; |
181 | 0 | sum += answer; |
182 | 0 | } |
183 | |
|
184 | 0 | sum = (sum >> 16) + (sum & 0xffff); |
185 | 0 | sum += (sum >> 16); |
186 | 0 | answer = ~sum; |
187 | |
|
188 | 0 | return answer; |
189 | 0 | } |
190 | | |
191 | | void ogs_gtp2_sender_f_teid( |
192 | | ogs_gtp2_sender_f_teid_t *sender_f_teid, ogs_gtp2_message_t *message) |
193 | 0 | { |
194 | 0 | ogs_gtp2_tlv_f_teid_t *tlv_f_teid = NULL; |
195 | 0 | ogs_gtp2_f_teid_t *f_teid = NULL; |
196 | |
|
197 | 0 | ogs_assert(sender_f_teid); |
198 | 0 | ogs_assert(message); |
199 | | |
200 | 0 | memset(sender_f_teid, 0, sizeof(*sender_f_teid)); |
201 | |
|
202 | 0 | switch (message->h.type) { |
203 | 0 | case OGS_GTP2_CREATE_SESSION_REQUEST_TYPE: |
204 | 0 | tlv_f_teid = &message->create_session_request. |
205 | 0 | sender_f_teid_for_control_plane; |
206 | 0 | break; |
207 | 0 | case OGS_GTP2_CREATE_SESSION_RESPONSE_TYPE: |
208 | 0 | tlv_f_teid = &message->create_session_response. |
209 | 0 | sender_f_teid_for_control_plane; |
210 | 0 | break; |
211 | 0 | case OGS_GTP2_MODIFY_BEARER_REQUEST_TYPE: |
212 | 0 | tlv_f_teid = &message->modify_bearer_request. |
213 | 0 | sender_f_teid_for_control_plane; |
214 | 0 | break; |
215 | 0 | case OGS_GTP2_DELETE_SESSION_REQUEST_TYPE: |
216 | 0 | tlv_f_teid = &message->delete_session_request. |
217 | 0 | sender_f_teid_for_control_plane; |
218 | 0 | break; |
219 | 0 | case OGS_GTP2_MODIFY_BEARER_COMMAND_TYPE: |
220 | 0 | tlv_f_teid = &message->modify_bearer_command. |
221 | 0 | sender_f_teid_for_control_plane; |
222 | 0 | break; |
223 | 0 | case OGS_GTP2_DELETE_BEARER_COMMAND_TYPE: |
224 | 0 | tlv_f_teid = &message->delete_bearer_command. |
225 | 0 | sender_f_teid_for_control_plane; |
226 | 0 | break; |
227 | 0 | case OGS_GTP2_BEARER_RESOURCE_COMMAND_TYPE: |
228 | 0 | tlv_f_teid = &message->bearer_resource_command. |
229 | 0 | sender_f_teid_for_control_plane; |
230 | 0 | break; |
231 | 0 | case OGS_GTP2_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST_TYPE: |
232 | 0 | tlv_f_teid = &message->create_indirect_data_forwarding_tunnel_request. |
233 | 0 | sender_f_teid_for_control_plane; |
234 | 0 | break; |
235 | 0 | case OGS_GTP2_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE: |
236 | 0 | tlv_f_teid = &message->create_indirect_data_forwarding_tunnel_response. |
237 | 0 | sender_f_teid_for_control_plane; |
238 | 0 | break; |
239 | 0 | case OGS_GTP2_DOWNLINK_DATA_NOTIFICATION_TYPE: |
240 | 0 | tlv_f_teid = &message->downlink_data_notification. |
241 | 0 | sender_f_teid_for_control_plane; |
242 | 0 | break; |
243 | 0 | case OGS_GTP2_MODIFY_ACCESS_BEARERS_REQUEST_TYPE: |
244 | 0 | tlv_f_teid = &message->modify_access_bearers_request. |
245 | 0 | sender_f_teid_for_control_plane; |
246 | 0 | default: |
247 | 0 | break; |
248 | 0 | } |
249 | | |
250 | 0 | if (tlv_f_teid && tlv_f_teid->presence && (f_teid = tlv_f_teid->data)) { |
251 | 0 | sender_f_teid->teid_presence = true; |
252 | | sender_f_teid->teid = be32toh(f_teid->teid); |
253 | 0 | } |
254 | 0 | } |