1 /* $Id$ */
2 /*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 #include <pj/sock_qos.h>
20 #include <pj/assert.h>
21 #include <pj/errno.h>
22 #include <pj/string.h>
23 #include <pj/compat/socket.h>
24
25 /* This is the implementation of QoS with BSD socket's setsockopt(),
26 * using Darwin-specific SO_NET_SERVICE_TYPE if available, and IP_TOS/
27 * IPV6_TCLASS as fallback.
28 */
29 #if defined(PJ_QOS_IMPLEMENTATION) && PJ_QOS_IMPLEMENTATION==PJ_QOS_DARWIN
30
31 #include <sys/socket.h>
32
33 #ifdef SO_NET_SERVICE_TYPE
sock_set_net_service_type(pj_sock_t sock,int val)34 static pj_status_t sock_set_net_service_type(pj_sock_t sock, int val)
35 {
36 pj_status_t status;
37
38 status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), SO_NET_SERVICE_TYPE,
39 &val, sizeof(val));
40 if (status == PJ_STATUS_FROM_OS(OSERR_ENOPROTOOPT))
41 status = PJ_ENOTSUP;
42
43 return status;
44 }
45 #endif
46
sock_set_net_service_type_type(pj_sock_t sock,pj_qos_type type)47 static pj_status_t sock_set_net_service_type_type(pj_sock_t sock,
48 pj_qos_type type)
49 {
50 #ifdef SO_NET_SERVICE_TYPE
51 int val = NET_SERVICE_TYPE_BE;
52
53 switch (type) {
54 case PJ_QOS_TYPE_BEST_EFFORT:
55 val = NET_SERVICE_TYPE_BE;
56 break;
57 case PJ_QOS_TYPE_BACKGROUND:
58 val = NET_SERVICE_TYPE_BK;
59 break;
60 case PJ_QOS_TYPE_SIGNALLING:
61 val = NET_SERVICE_TYPE_SIG;
62 break;
63 case PJ_QOS_TYPE_VIDEO:
64 val = NET_SERVICE_TYPE_VI;
65 break;
66 case PJ_QOS_TYPE_VOICE:
67 case PJ_QOS_TYPE_CONTROL:
68 default:
69 val = NET_SERVICE_TYPE_VO;
70 break;
71 }
72
73 return sock_set_net_service_type(sock, val);
74 #else
75 return PJ_ENOTSUP;
76 #endif
77 }
78
sock_set_net_service_type_params(pj_sock_t sock,pj_qos_params * param)79 static pj_status_t sock_set_net_service_type_params(pj_sock_t sock,
80 pj_qos_params *param)
81 {
82 #ifdef SO_NET_SERVICE_TYPE
83 pj_status_t status;
84 int val = -1;
85
86 PJ_ASSERT_RETURN(param, PJ_EINVAL);
87
88 /*
89 * Sources:
90 * - IETF draft-szigeti-tsvwg-ieee-802-11e-01
91 * - iOS 10 SDK, sys/socket.h
92 */
93 if (val == -1 && param->flags & PJ_QOS_PARAM_HAS_DSCP) {
94 if (param->dscp_val == 0) /* DF */
95 val = NET_SERVICE_TYPE_BE;
96 else if (param->dscp_val < 0x10) /* CS1, AF11, AF12, AF13 */
97 val = NET_SERVICE_TYPE_BK;
98 else if (param->dscp_val == 0x10) /* CS2 */
99 val = NET_SERVICE_TYPE_OAM;
100 else if (param->dscp_val < 0x18) /* AF21, AF22, AF23 */
101 val = NET_SERVICE_TYPE_RD;
102 else if (param->dscp_val < 0x20) /* CS3, AF31, AF32, AF33 */
103 val = NET_SERVICE_TYPE_AV;
104 else if (param->dscp_val == 0x20) /* CS4 */
105 val = NET_SERVICE_TYPE_RD;
106 else if (param->dscp_val < 0x28) /* AF41, AF42, AF43 */
107 val = NET_SERVICE_TYPE_VI;
108 else if (param->dscp_val == 0x28) /* CS5 */
109 val = NET_SERVICE_TYPE_SIG;
110 else
111 val = NET_SERVICE_TYPE_VO; /* VOICE-ADMIT, EF, CS6, etc. */
112 }
113
114 if (val == -1 && param->flags & PJ_QOS_PARAM_HAS_WMM) {
115 switch (param->wmm_prio) {
116 case PJ_QOS_WMM_PRIO_BULK_EFFORT:
117 val = NET_SERVICE_TYPE_BE;
118 break;
119 case PJ_QOS_WMM_PRIO_BULK:
120 val = NET_SERVICE_TYPE_BK;
121 break;
122 case PJ_QOS_WMM_PRIO_VIDEO:
123 val = NET_SERVICE_TYPE_VI;
124 break;
125 case PJ_QOS_WMM_PRIO_VOICE:
126 val = NET_SERVICE_TYPE_VO;
127 break;
128 }
129 }
130
131 if (val == -1) {
132 pj_qos_type type;
133
134 status = pj_qos_get_type(param, &type);
135
136 if (status == PJ_SUCCESS)
137 return sock_set_net_service_type_type(sock, type);
138
139 val = NET_SERVICE_TYPE_BE;
140 }
141
142 return sock_set_net_service_type(sock, val);
143 #else
144 return PJ_ENOTSUP;
145 #endif
146 }
147
sock_set_ip_ds(pj_sock_t sock,pj_qos_params * param)148 static pj_status_t sock_set_ip_ds(pj_sock_t sock, pj_qos_params *param)
149 {
150 pj_status_t status = PJ_SUCCESS;
151
152 PJ_ASSERT_RETURN(param, PJ_EINVAL);
153
154 if (param->flags & PJ_QOS_PARAM_HAS_DSCP) {
155 /* We need to know if the socket is IPv4 or IPv6 */
156 pj_sockaddr sa;
157 int salen = sizeof(salen);
158
159 /* Value is dscp_val << 2 */
160 int val = (param->dscp_val << 2);
161
162 status = pj_sock_getsockname(sock, &sa, &salen);
163 if (status != PJ_SUCCESS)
164 return status;
165
166 if (sa.addr.sa_family == pj_AF_INET()) {
167 /* In IPv4, the DS field goes in the TOS field */
168 status = pj_sock_setsockopt(sock, pj_SOL_IP(), pj_IP_TOS(),
169 &val, sizeof(val));
170 } else if (sa.addr.sa_family == pj_AF_INET6()) {
171 /* In IPv6, the DS field goes in the Traffic Class field */
172 status = pj_sock_setsockopt(sock, pj_SOL_IPV6(),
173 pj_IPV6_TCLASS(),
174 &val, sizeof(val));
175 } else
176 status = PJ_EINVAL;
177
178 if (status != PJ_SUCCESS) {
179 param->flags &= ~(PJ_QOS_PARAM_HAS_DSCP);
180 }
181 }
182
183 return status;
184 }
185
pj_sock_set_qos_params(pj_sock_t sock,pj_qos_params * param)186 PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock,
187 pj_qos_params *param)
188 {
189 pj_status_t status;
190
191 PJ_ASSERT_RETURN(param, PJ_EINVAL);
192
193 /* No op? */
194 if (!param->flags)
195 return PJ_SUCCESS;
196
197 /* Clear prio field since we don't support it */
198 param->flags &= ~(PJ_QOS_PARAM_HAS_SO_PRIO);
199
200 /* Try SO_NET_SERVICE_TYPE */
201 status = sock_set_net_service_type_params(sock, param);
202 if (status == PJ_SUCCESS)
203 return status;
204
205 if (status != PJ_ENOTSUP) {
206 /* SO_NET_SERVICE_TYPE sets both DSCP and WMM */
207 param->flags &= ~(PJ_QOS_PARAM_HAS_DSCP);
208 param->flags &= ~(PJ_QOS_PARAM_HAS_WMM);
209 return status;
210 }
211
212 /* Fall back to IP_TOS/IPV6_TCLASS */
213 return sock_set_ip_ds(sock, param);
214 }
215
pj_sock_set_qos_type(pj_sock_t sock,pj_qos_type type)216 PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock,
217 pj_qos_type type)
218 {
219 pj_status_t status;
220 pj_qos_params param;
221
222 /* Try SO_NET_SERVICE_TYPE */
223 status = sock_set_net_service_type_type(sock, type);
224 if (status == PJ_SUCCESS || status != PJ_ENOTSUP)
225 return status;
226
227 /* Fall back to IP_TOS/IPV6_TCLASS */
228 status = pj_qos_get_params(type, ¶m);
229 if (status != PJ_SUCCESS)
230 return status;
231
232 return sock_set_ip_ds(sock, ¶m);
233 }
234
235 #ifdef SO_NET_SERVICE_TYPE
sock_get_net_service_type(pj_sock_t sock,int * p_val)236 static pj_status_t sock_get_net_service_type(pj_sock_t sock, int *p_val)
237 {
238 pj_status_t status;
239 int optlen = sizeof(*p_val);
240
241 PJ_ASSERT_RETURN(p_val, PJ_EINVAL);
242
243 status = pj_sock_getsockopt(sock, pj_SOL_SOCKET(), SO_NET_SERVICE_TYPE,
244 p_val, &optlen);
245 if (status == PJ_STATUS_FROM_OS(OSERR_ENOPROTOOPT))
246 status = PJ_ENOTSUP;
247
248 return status;
249 }
250 #endif
251
sock_get_net_service_type_type(pj_sock_t sock,pj_qos_type * p_type)252 static pj_status_t sock_get_net_service_type_type(pj_sock_t sock,
253 pj_qos_type *p_type)
254 {
255 #ifdef SO_NET_SERVICE_TYPE
256 pj_status_t status;
257 int val;
258
259 PJ_ASSERT_RETURN(p_type, PJ_EINVAL);
260
261 status = sock_get_net_service_type(sock, &val);
262 if (status == PJ_SUCCESS) {
263 switch (val) {
264 default:
265 case NET_SERVICE_TYPE_BE:
266 *p_type = PJ_QOS_TYPE_BEST_EFFORT;
267 break;
268 case NET_SERVICE_TYPE_BK:
269 *p_type = PJ_QOS_TYPE_BACKGROUND;
270 break;
271 case NET_SERVICE_TYPE_SIG:
272 *p_type = PJ_QOS_TYPE_SIGNALLING;
273 break;
274 case NET_SERVICE_TYPE_VI:
275 case NET_SERVICE_TYPE_RV:
276 case NET_SERVICE_TYPE_AV:
277 case NET_SERVICE_TYPE_OAM:
278 case NET_SERVICE_TYPE_RD:
279 *p_type = PJ_QOS_TYPE_VIDEO;
280 break;
281 case NET_SERVICE_TYPE_VO:
282 *p_type = PJ_QOS_TYPE_VOICE;
283 break;
284 }
285 }
286
287 return status;
288 #else
289 return PJ_ENOTSUP;
290 #endif
291 }
292
sock_get_net_service_type_params(pj_sock_t sock,pj_qos_params * p_param)293 static pj_status_t sock_get_net_service_type_params(pj_sock_t sock,
294 pj_qos_params *p_param)
295 {
296 #ifdef SO_NET_SERVICE_TYPE
297 pj_status_t status;
298 int val;
299
300 PJ_ASSERT_RETURN(p_param, PJ_EINVAL);
301
302 status = sock_get_net_service_type(sock, &val);
303 if (status == PJ_SUCCESS) {
304 pj_bzero(p_param, sizeof(*p_param));
305
306 /* Note: these are just educated guesses, chosen for symmetry with
307 * sock_set_net_service_type_params: we can't know the actual values
308 * chosen by the OS, or even if DSCP/WMM are used at all.
309 *
310 * The source for mapping DSCP to WMM is:
311 * - IETF draft-szigeti-tsvwg-ieee-802-11e-01
312 */
313 switch (val) {
314 default:
315 case NET_SERVICE_TYPE_BE:
316 p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
317 p_param->dscp_val = 0; /* DF */
318 p_param->wmm_prio = PJ_QOS_WMM_PRIO_BULK_EFFORT; /* AC_BE */
319 break;
320 case NET_SERVICE_TYPE_BK:
321 p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
322 p_param->dscp_val = 0x08; /* CS1 */
323 p_param->wmm_prio = PJ_QOS_WMM_PRIO_BULK; /* AC_BK */
324 break;
325 case NET_SERVICE_TYPE_SIG:
326 p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
327 p_param->dscp_val = 0x28; /* CS5 */
328 p_param->wmm_prio = PJ_QOS_WMM_PRIO_VIDEO; /* AC_VI */
329 break;
330 case NET_SERVICE_TYPE_VI:
331 p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
332 p_param->dscp_val = 0x22; /* AF41 */
333 p_param->wmm_prio = PJ_QOS_WMM_PRIO_VIDEO; /* AC_VI */
334 break;
335 case NET_SERVICE_TYPE_VO:
336 p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
337 p_param->dscp_val = 0x30; /* CS6 */
338 p_param->wmm_prio = PJ_QOS_WMM_PRIO_VOICE; /* AC_VO */
339 break;
340 case NET_SERVICE_TYPE_RV:
341 p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
342 p_param->dscp_val = 0x22; /* AF41 */
343 p_param->wmm_prio = PJ_QOS_WMM_PRIO_VIDEO; /* AC_VI */
344 break;
345 case NET_SERVICE_TYPE_AV:
346 p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
347 p_param->dscp_val = 0x18; /* CS3 */
348 p_param->wmm_prio = PJ_QOS_WMM_PRIO_VIDEO; /* AC_VI */
349 break;
350 case NET_SERVICE_TYPE_OAM:
351 p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
352 p_param->dscp_val = 0x10; /* CS2 */
353 p_param->wmm_prio = PJ_QOS_WMM_PRIO_BULK_EFFORT; /* AC_BE */
354 break;
355 case NET_SERVICE_TYPE_RD:
356 p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
357 p_param->dscp_val = 0x20; /* CS4 */
358 p_param->wmm_prio = PJ_QOS_WMM_PRIO_VIDEO; /* AC_VI */
359 break;
360 }
361 }
362
363 return status;
364 #else
365 return PJ_ENOTSUP;
366 #endif
367 }
368
pj_sock_get_qos_params(pj_sock_t sock,pj_qos_params * p_param)369 PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock,
370 pj_qos_params *p_param)
371 {
372 pj_status_t status;
373 int val, optlen;
374 pj_sockaddr sa;
375 int salen = sizeof(salen);
376
377 PJ_ASSERT_RETURN(p_param, PJ_EINVAL);
378
379 pj_bzero(p_param, sizeof(*p_param));
380
381 /* Try SO_NET_SERVICE_TYPE */
382 status = sock_get_net_service_type_params(sock, p_param);
383 if (status != PJ_ENOTSUP)
384 return status;
385
386 /* Fall back to IP_TOS/IPV6_TCLASS */
387 status = pj_sock_getsockname(sock, &sa, &salen);
388 if (status != PJ_SUCCESS)
389 return status;
390
391 optlen = sizeof(val);
392 if (sa.addr.sa_family == pj_AF_INET()) {
393 status = pj_sock_getsockopt(sock, pj_SOL_IP(), pj_IP_TOS(),
394 &val, &optlen);
395 } else if (sa.addr.sa_family == pj_AF_INET6()) {
396 status = pj_sock_getsockopt(sock, pj_SOL_IPV6(), pj_IPV6_TCLASS(),
397 &val, &optlen);
398 } else
399 status = PJ_EINVAL;
400 if (status == PJ_SUCCESS) {
401 p_param->flags |= PJ_QOS_PARAM_HAS_DSCP;
402 p_param->dscp_val = (pj_uint8_t)(val >> 2);
403 }
404
405 return status;
406 }
407
pj_sock_get_qos_type(pj_sock_t sock,pj_qos_type * p_type)408 PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock,
409 pj_qos_type *p_type)
410 {
411 pj_qos_params param;
412 pj_status_t status;
413
414 PJ_ASSERT_RETURN(p_type, PJ_EINVAL);
415
416 status = sock_get_net_service_type_type(sock, p_type);
417 if (status != PJ_ENOTSUP)
418 return status;
419
420 status = pj_sock_get_qos_params(sock, ¶m);
421 if (status != PJ_SUCCESS)
422 return status;
423
424 return pj_qos_get_type(¶m, p_type);
425 }
426
427 #endif /* PJ_QOS_IMPLEMENTATION */
428