1 /*
2  * Copyright (c) 2004-2006 Maxim Sobolev <sobomax@FreeBSD.org>
3  * Copyright (c) 2006-2016 Sippy Software, Inc., http://www.sippysoft.com
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #include <pthread.h>
30 #include <stddef.h>
31 #include <stdint.h>
32 #include <stdlib.h>
33 
34 #include "rtpp_types.h"
35 #include "rtpp_mallocs.h"
36 #include "rtpp_refcnt.h"
37 #include "rtpp_port_table.h"
38 #include "rtpp_port_table_fin.h"
39 
40 struct rtpp_ptbl_priv {
41     struct rtpp_port_table pub;
42     pthread_mutex_t lock;
43     int port_table_len;
44     int port_table_idx;
45     uint16_t *port_table;
46     uint16_t port_ctl;
47     int seq_ports;
48 };
49 
50 static void rtpp_ptbl_dtor(struct rtpp_ptbl_priv *);
51 static int rtpp_ptbl_get_port(struct rtpp_port_table *, rtpp_pt_use_t, void *);
52 
53 #define PUB2PVT(pubp) \
54   ((struct rtpp_ptbl_priv *)((char *)(pubp) - offsetof(struct rtpp_ptbl_priv, pub)))
55 
56 struct rtpp_port_table *
rtpp_port_table_ctor(int port_min,int port_max,int seq_ports,uint16_t port_ctl)57 rtpp_port_table_ctor(int port_min, int port_max, int seq_ports, uint16_t port_ctl)
58 {
59     struct rtpp_ptbl_priv *pvt;
60     struct rtpp_refcnt *rcnt;
61     int i, j;
62     uint16_t portnum;
63 
64     pvt = rtpp_rzmalloc(sizeof(struct rtpp_ptbl_priv), &rcnt);
65     if (pvt == NULL) {
66         goto e0;
67     }
68     pvt->pub.rcnt = rcnt;
69     if (pthread_mutex_init(&pvt->lock, NULL) != 0) {
70         goto e1;
71     }
72     pvt->port_table_len = ((port_max - port_min) / 2) + 1;
73     pvt->port_table = malloc(sizeof(uint16_t) * pvt->port_table_len);
74     if (pvt->port_table == NULL) {
75         goto e2;
76     }
77     pvt->port_ctl = port_ctl;
78 
79     /* Generate linear table */
80     portnum = port_min;
81     for (i = 0; i < pvt->port_table_len; i += 1) {
82         pvt->port_table[i] = portnum;
83         portnum += 2;
84     }
85     if (seq_ports == 0) {
86         /* Shuffle elements ramdomly */
87         for (i = 0; i < pvt->port_table_len; i += 1) {
88             j = random() % pvt->port_table_len;
89             portnum = pvt->port_table[i];
90             pvt->port_table[i] = pvt->port_table[j];
91             pvt->port_table[j] = portnum;
92         }
93     }
94     pvt->seq_ports = seq_ports;
95     /* Set the last used element to be the last element */
96     pvt->port_table_idx = pvt->port_table_len - 1;
97 
98     pvt->pub.get_port = &rtpp_ptbl_get_port;
99     CALL_SMETHOD(pvt->pub.rcnt, attach, (rtpp_refcnt_dtor_t)&rtpp_ptbl_dtor,
100       pvt);
101     return ((&pvt->pub));
102 
103 e2:
104     pthread_mutex_destroy(&pvt->lock);
105 e1:
106     CALL_SMETHOD(pvt->pub.rcnt, decref);
107     free(pvt);
108 e0:
109     return (NULL);
110 }
111 
112 static void
rtpp_ptbl_dtor(struct rtpp_ptbl_priv * pvt)113 rtpp_ptbl_dtor(struct rtpp_ptbl_priv *pvt)
114 {
115 
116     rtpp_port_table_fin(&pvt->pub);
117     pthread_mutex_destroy(&pvt->lock);
118     free(pvt->port_table);
119     free(pvt);
120 }
121 
122 static int
rtpp_ptbl_get_port(struct rtpp_port_table * self,rtpp_pt_use_t use_port,void * uarg)123 rtpp_ptbl_get_port(struct rtpp_port_table *self, rtpp_pt_use_t use_port, void *uarg)
124 {
125     struct rtpp_ptbl_priv *pvt;
126     int i, j, idx, rval;
127     uint16_t port;
128 
129     pvt = PUB2PVT(self);
130 
131     pthread_mutex_lock(&pvt->lock);
132     for (i = 1; i < pvt->port_table_len; i++) {
133         idx = (pvt->port_table_idx + i) % pvt->port_table_len;
134         port = pvt->port_table[idx];
135         if (port == pvt->port_ctl || port == (pvt->port_ctl - 1))
136             continue;
137         rval = use_port(port, uarg);
138         if (!pvt->seq_ports) {
139             /* Shuffle table as we go, so we are not easy to outguess */
140             j = random() % pvt->port_table_len;
141             pvt->port_table[idx] = pvt->port_table[j];
142             pvt->port_table[j] = port;
143         }
144         if (rval == RTPP_PTU_OK) {
145             pvt->port_table_idx = idx;
146             pthread_mutex_unlock(&pvt->lock);
147             return 0;
148         }
149         if (rval != RTPP_PTU_ONEMORE) {
150             pvt->port_table_idx = idx;
151             break;
152         }
153     }
154     pthread_mutex_unlock(&pvt->lock);
155     return -1;
156 }
157