085d06c51de2b8d6d15ceb9a87c444ba4415e1b5
[platform/upstream/connman.git] / src / ippool.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2012  BMW Car IT GmbH. All rights reserved.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <getopt.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/errno.h>
33 #include <sys/socket.h>
34
35 #include "connman.h"
36
37 struct connman_ippool {
38         unsigned int refcount;
39
40         uint32_t block;
41
42         char *gateway;
43         char *broadcast;
44         char *start_ip;
45         char *end_ip;
46         char *subnet_mask;
47 };
48
49 static GHashTable *hash_pool;
50 static uint32_t last_block;
51 static uint32_t block_16_bits;
52 static uint32_t block_20_bits;
53 static uint32_t block_24_bits;
54 static uint32_t subnet_mask_24;
55
56 struct connman_ippool *
57 __connman_ippool_ref_debug(struct connman_ippool *pool,
58                                 const char *file, int line, const char *caller)
59 {
60         DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount + 1,
61                 file, line, caller);
62
63         __sync_fetch_and_add(&pool->refcount, 1);
64
65         return pool;
66 }
67
68 void __connman_ippool_unref_debug(struct connman_ippool *pool,
69                                 const char *file, int line, const char *caller)
70 {
71         if (pool == NULL)
72                 return;
73
74         DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount - 1,
75                 file, line, caller);
76
77         if (__sync_fetch_and_sub(&pool->refcount, 1) != 1)
78                 return;
79
80         g_hash_table_remove(hash_pool, GUINT_TO_POINTER(pool->block));
81 }
82
83 static char *get_ip(uint32_t ip)
84 {
85         struct in_addr addr;
86
87         addr.s_addr = htonl(ip);
88
89         return g_strdup(inet_ntoa(addr));
90 }
91
92 static uint32_t next_block(uint32_t block)
93 {
94         uint32_t next;
95
96         /*
97          * Return the next IP block within the private IP range
98          *
99          * 16-bit block 192.168.0.0 – 192.168.255.255
100          * 20-bit block  172.16.0.0 –  172.31.255.255
101          * 24-bit block    10.0.0.0 –  10.255.255.255
102          */
103
104         next = (block & 0x0000ff00) >> 8;
105         next += 1;
106
107         if (next == 255) {
108                 if ((block & 0xffffff00) == block_16_bits) {
109                         /*
110                          * Reached the end of the 16 bit block, switch
111                          * to the 20-bit block.
112                          */
113                         return block_20_bits;
114                 }
115
116                 if ((block & 0xffff0000) >= block_20_bits) {
117                         next = (block & 0x00ff0000) >> 16;
118                         if (next >= 16 && next < 32)
119                                 next += 1;
120
121                         if (next == 32) {
122                                 /*
123                                  * Reached the end of the 20 bit
124                                  * block, switch to the 24-bit block.
125                                  */
126                                 return block_24_bits;
127                         }
128
129                         return (block & 0xff000000) |
130                                 ((next << 16) & 0x00ff0000);
131                 }
132
133                 if ((block & 0xff000000) == block_24_bits) {
134                         next = (block & 0x00ff0000) >> 16;
135                         if (next < 255)
136                                 next += 1;
137
138                         if (next == 255) {
139                                 /*
140                                  * Reached the end of the 24 bit
141                                  * block, switch to the 16-bit block.
142                                  */
143                                 return block_16_bits;
144                         }
145
146                         return (block & 0xff000000) |
147                                 ((next << 16) & 0x00ff0000);
148                 }
149         }
150
151         return (block & 0xffff0000) | ((next << 8) & 0x0000ff00);
152 }
153
154 static uint32_t find_free_block()
155 {
156         struct connman_ippool *pool;
157         uint32_t start;
158         uint32_t block;
159
160         if (last_block == 0)
161                 return block_16_bits;
162
163         /*
164          * Instead starting always from the 16 bit block, we start
165          * from the last assigned block. This is a simple optimimazion
166          * for the case where a lot of blocks have been assigned, e.g.
167          * the first half of the private IP pool is in use and a new
168          * we need to find a new block.
169          *
170          * To only thing we have to make sure is that we terminated if
171          * there is no block left.
172          */
173         start = last_block;
174
175         block = next_block(start);
176         while (start != block) {
177                 block = next_block(block);
178
179                 pool = g_hash_table_lookup(hash_pool, GUINT_TO_POINTER(block));
180                 if (pool != NULL)
181                         continue;
182
183                 return block;
184         }
185
186         return 0;
187 }
188
189 struct connman_ippool *__connman_ippool_create(unsigned int start,
190                                                 unsigned int range)
191 {
192         struct connman_ippool *pool;
193         uint32_t block;
194
195         /*
196          * The range is at max 255 and we don't support overlapping
197          * blocks.
198          */
199         if (start + range > 254)
200                 return NULL;
201
202         block = find_free_block();
203         if (block == 0)
204                 return NULL;
205
206         pool = g_try_new0(struct connman_ippool, 1);
207         if (pool == NULL)
208                 return NULL;
209
210         pool->refcount = 1;
211         pool->block = block;
212
213         last_block = block;
214
215         if (range == 0)
216                 range = 1;
217
218         pool->gateway = get_ip(block + 1);
219         pool->broadcast = get_ip(block + 255);
220         pool->subnet_mask = get_ip(subnet_mask_24);
221         pool->start_ip = get_ip(block + start);
222         pool->end_ip = get_ip(block + start + range);
223
224         g_hash_table_insert(hash_pool, GUINT_TO_POINTER(pool->block), pool);
225
226         return pool;
227 }
228
229 const char *__connman_ippool_get_gateway(struct connman_ippool *pool)
230 {
231         return pool->gateway;
232 }
233
234 const char *__connman_ippool_get_broadcast(struct connman_ippool *pool)
235 {
236         return pool->broadcast;
237 }
238
239 const char *__connman_ippool_get_start_ip(struct connman_ippool *pool)
240 {
241         return pool->start_ip;
242 }
243
244 const char *__connman_ippool_get_end_ip(struct connman_ippool *pool)
245 {
246         return pool->end_ip;
247 }
248
249 const char *__connman_ippool_get_subnet_mask(struct connman_ippool *pool)
250 {
251         return pool->subnet_mask;
252 }
253
254 static void pool_free(gpointer data)
255 {
256         struct connman_ippool *pool = data;
257
258         g_free(pool->gateway);
259         g_free(pool->broadcast);
260         g_free(pool->start_ip);
261         g_free(pool->end_ip);
262         g_free(pool->subnet_mask);
263
264         g_free(pool);
265 }
266
267 int __connman_ippool_init(void)
268 {
269         DBG("");
270
271         /* We start at 254 by default to avoid common addresses */
272         block_16_bits = ntohl(inet_addr("192.168.254.0"));
273         block_20_bits = ntohl(inet_addr("172.16.0.0"));
274         block_24_bits = ntohl(inet_addr("10.0.0.0"));
275         subnet_mask_24 = ntohl(inet_addr("255.255.255.0"));
276
277         hash_pool = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
278                                                 pool_free);
279
280         return 0;
281 }
282
283 void __connman_ippool_cleanup(void)
284 {
285         DBG("");
286
287         g_hash_table_destroy(hash_pool);
288         hash_pool = NULL;
289 }