Remove WPA2 and WPA3 service together
[platform/upstream/connman.git] / src / ippool.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2013  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2012-2014  BMW Car IT GmbH.
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 <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/socket.h>
32
33 #include "connman.h"
34
35 struct address_info {
36         int index;
37         uint32_t start;
38         uint32_t end;
39
40         unsigned int use_count;
41         struct connman_ippool *pool;
42 };
43
44 struct connman_ippool {
45         struct address_info *info;
46
47         char *gateway;
48         char *broadcast;
49         char *start_ip;
50         char *end_ip;
51         char *subnet_mask;
52
53         ippool_collision_cb_t collision_cb;
54         void *user_data;
55 };
56
57 GSList *allocated_blocks;
58
59 static uint32_t last_block;
60 static uint32_t block_16_bits;
61 static uint32_t block_20_bits;
62 static uint32_t block_24_bits;
63 static uint32_t subnet_mask_24;
64
65 void __connman_ippool_free(struct connman_ippool *pool)
66 {
67         if (!pool)
68                 return;
69
70         if (pool->info) {
71                 allocated_blocks = g_slist_remove(allocated_blocks, pool->info);
72                 g_free(pool->info);
73         }
74
75         g_free(pool->gateway);
76         g_free(pool->broadcast);
77         g_free(pool->start_ip);
78         g_free(pool->end_ip);
79         g_free(pool->subnet_mask);
80
81         g_free(pool);
82 }
83
84 static char *get_ip(uint32_t ip)
85 {
86         struct in_addr addr;
87
88         addr.s_addr = htonl(ip);
89
90         return g_strdup(inet_ntoa(addr));
91 }
92
93 static uint32_t next_block(uint32_t block)
94 {
95         uint32_t next;
96
97         /*
98          * Return the next IP block within the private IP range
99          *
100          * 16-bit block 192.168.0.0 – 192.168.255.255
101          * 20-bit block  172.16.0.0 –  172.31.255.255
102          * 24-bit block    10.0.0.0 –  10.255.255.255
103          */
104
105         next = (block & 0x0000ff00) >> 8;
106         next += 1;
107
108         if (next == 255) {
109                 if ((block & 0xffff0000) == block_16_bits) {
110                         /*
111                          * Reached the end of the 16 bit block, switch
112                          * to the 20-bit block.
113                          */
114                         return block_20_bits;
115                 }
116
117                 if ((block & 0xffff0000) >= block_20_bits) {
118                         next = (block & 0x00ff0000) >> 16;
119                         if (next >= 16 && next < 32)
120                                 next += 1;
121
122                         if (next == 32) {
123                                 /*
124                                  * Reached the end of the 20 bit
125                                  * block, switch to the 24-bit block.
126                                  */
127                                 return block_24_bits;
128                         }
129
130                         return (block & 0xff000000) |
131                                 ((next << 16) & 0x00ff0000);
132                 }
133
134                 if ((block & 0xff000000) == block_24_bits) {
135                         next = (block & 0x00ff0000) >> 16;
136                         if (next < 255)
137                                 next += 1;
138
139                         if (next == 255) {
140                                 /*
141                                  * Reached the end of the 24 bit
142                                  * block, switch to the 16-bit block.
143                                  */
144                                 return block_16_bits;
145                         }
146
147                         return (block & 0xff000000) |
148                                 ((next << 16) & 0x00ff0000);
149                 }
150         }
151
152         return (block & 0xffff0000) | ((next << 8) & 0x0000ff00);
153 }
154
155 static uint32_t get_free_block(unsigned int size)
156 {
157         struct address_info *info;
158         uint32_t block;
159         GSList *list;
160         bool collision;
161
162         /*
163          * Instead starting always from the 16 bit block, we start
164          * from the last assigned block. This is a simple optimimazion
165          * for the case where a lot of blocks have been assigned, e.g.
166          * the first half of the private IP pool is in use and a new
167          * we need to find a new block.
168          *
169          * To only thing we have to make sure is that we terminated if
170          * there is no block left.
171          */
172         if (last_block)
173                 block = last_block;
174         else
175                 block = block_16_bits;
176
177         do {
178                 collision = false;
179                 for (list = allocated_blocks; list; list = list->next) {
180                         info = list->data;
181
182                         if (info->start <= block && block <= info->end) {
183                                 collision = true;
184                                 break;
185                         }
186                 }
187
188                 if (!collision)
189                         return block;
190
191                 block = next_block(block);
192         } while (block != last_block);
193
194         return 0;
195 }
196
197 static struct address_info *lookup_info(int index, uint32_t start)
198 {
199         GSList *list;
200
201         for (list = allocated_blocks; list; list = list->next) {
202                 struct address_info *info = list->data;
203
204                 if (info->index == index && info->start == start)
205                         return info;
206         }
207
208         return NULL;
209 }
210
211 static bool is_private_address(uint32_t address)
212 {
213         unsigned int a, b;
214
215         a = (address & 0xff000000) >> 24;
216         b = (address & 0x00ff0000) >> 16;
217
218         if (a == 10 || (a == 192 && b == 168) ||
219                                         (a == 172 && (b >= 16 && b <= 31)))
220                 return true;
221
222         return false;
223 }
224
225 void __connman_ippool_newaddr(int index, const char *address,
226                                 unsigned char prefixlen)
227 {
228         struct address_info *info, *it;
229         struct in_addr inp;
230         uint32_t start, end, mask;
231         GSList *list;
232
233         if (inet_aton(address, &inp) == 0)
234                 return;
235
236         start = ntohl(inp.s_addr);
237         if (!is_private_address(start))
238                 return;
239
240         if (prefixlen >= 32)
241                 mask = 0xffffffff;
242         else
243                 mask = ~(0xffffffff >> prefixlen);
244
245         start = start & mask;
246         end = start | ~mask;
247
248         info = lookup_info(index, start);
249         if (info)
250                 goto update;
251
252         info = g_try_new0(struct address_info, 1);
253         if (!info)
254                 return;
255
256         info->index = index;
257         info->start = start;
258         info->end = end;
259
260         allocated_blocks = g_slist_prepend(allocated_blocks, info);
261
262 update:
263         info->use_count = info->use_count + 1;
264
265         if (info->use_count > 1 || info->pool) {
266                 /*
267                  * We need only to check for the first IP in a block for
268                  * collisions.
269                  */
270                 return;
271         }
272
273         for (list = allocated_blocks; list; list = list->next) {
274                 it = list->data;
275
276                 if (it == info)
277                         continue;
278
279                 if (!(info->start >= it->start && info->start <= it->end))
280                         continue;
281
282                 if (it->pool && it->pool->collision_cb)
283                         it->pool->collision_cb(it->pool, it->pool->user_data);
284
285                 return;
286         }
287 }
288
289 void __connman_ippool_deladdr(int index, const char *address,
290                                 unsigned char prefixlen)
291 {
292         struct address_info *info;
293         struct in_addr inp;
294         uint32_t start, mask;
295
296         if (inet_aton(address, &inp) == 0)
297                 return;
298
299         start = ntohl(inp.s_addr);
300         if (!is_private_address(start))
301                 return;
302
303         mask = ~(0xffffffff >> prefixlen);
304         start = start & mask;
305
306         info = lookup_info(index, start);
307         if (!info) {
308                 /* In theory this should never happen */
309                 connman_error("Inconsistent IP pool management (start not found)");
310                 return;
311         }
312
313         info->use_count = info->use_count - 1;
314         if (info->pool)
315                 return;
316
317         if (info->use_count > 0)
318                 return;
319
320         allocated_blocks = g_slist_remove(allocated_blocks, info);
321         g_free(info);
322 }
323
324 struct connman_ippool *__connman_ippool_create(int index,
325                                         unsigned int start,
326                                         unsigned int range,
327                                         ippool_collision_cb_t collision_cb,
328                                         void *user_data)
329 {
330         struct connman_ippool *pool;
331         struct address_info *info;
332         uint32_t block;
333
334         DBG("");
335
336         /*
337          * The range is at max 255 and we don't support overlapping
338          * blocks.
339          */
340         if (start + range > 254) {
341                 connman_error("IP pool does not support pool size larger than 254");
342                 return NULL;
343         }
344
345         block = get_free_block(start + range);
346         if (block == 0) {
347                 connman_warn("Could not find a free IP block");
348                 return NULL;
349         }
350
351         pool = g_try_new0(struct connman_ippool, 1);
352         if (!pool)
353                 return NULL;
354
355         info = g_try_new0(struct address_info, 1);
356         if (!info) {
357                 g_free(pool);
358                 return NULL;
359         }
360
361         last_block = block;
362
363         info->index = index;
364         info->start = block;
365         info->end = block + range;
366
367         pool->info = info;
368         pool->collision_cb = collision_cb;
369         pool->user_data = user_data;
370
371         info->pool = pool;
372
373         if (range == 0)
374                 range = 1;
375
376         pool->gateway = get_ip(info->start + 1);
377         pool->broadcast = get_ip(info->start + 255);
378         pool->subnet_mask = get_ip(subnet_mask_24);
379         pool->start_ip = get_ip(block + start);
380         pool->end_ip = get_ip(block + start + range);
381
382         allocated_blocks = g_slist_prepend(allocated_blocks, info);
383
384         return pool;
385 }
386
387 const char *__connman_ippool_get_gateway(struct connman_ippool *pool)
388 {
389         return pool->gateway;
390 }
391
392 const char *__connman_ippool_get_broadcast(struct connman_ippool *pool)
393 {
394         return pool->broadcast;
395 }
396
397 const char *__connman_ippool_get_start_ip(struct connman_ippool *pool)
398 {
399         return pool->start_ip;
400 }
401
402 const char *__connman_ippool_get_end_ip(struct connman_ippool *pool)
403 {
404         return pool->end_ip;
405 }
406
407 const char *__connman_ippool_get_subnet_mask(struct connman_ippool *pool)
408 {
409         return pool->subnet_mask;
410 }
411
412 int __connman_ippool_init(void)
413 {
414         DBG("");
415
416         block_16_bits = ntohl(inet_addr("192.168.0.0"));
417         block_20_bits = ntohl(inet_addr("172.16.0.0"));
418         block_24_bits = ntohl(inet_addr("10.0.0.0"));
419         subnet_mask_24 = ntohl(inet_addr("255.255.255.0"));
420
421         return 0;
422 }
423
424 void __connman_ippool_cleanup(void)
425 {
426         DBG("");
427
428         g_slist_free_full(allocated_blocks, g_free);
429         last_block = 0;
430         allocated_blocks = NULL;
431 }