1/**
2 * \file
3 * \brief Port allocator for netd
4 *
5 * This file is part of the net "daemon"
6 */
7
8/*
9 * Copyright (c) 2007, 2008, 2009, 2010 ETH Zurich.
10 * All rights reserved.
11 *
12 * This file is distributed under the terms in the attached LICENSE file.
13 * If you do not find this file, copies can be found by writing to:
14 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: NetOS Group.
15 */
16
17#include <assert.h>
18#include <stdlib.h>
19#include <stdio.h>
20#include <barrelfish/barrelfish.h>
21#include <if/net_ports_defs.h>
22#include "portalloc.h"
23#include "device_manager_debug.h"
24
25/**
26 * The arrays storing the current port allocation state
27 */
28static uint64_t free_tcp_ports[TCP_ARRAY_SIZE];
29static uint64_t free_udp_ports[UDP_ARRAY_SIZE];
30
31
32/**
33 * @brief Initialization code for port allocator
34 */
35void init_free_ports(void)
36{
37    uint64_t i;
38
39    for (i = 0; i < TCP_ARRAY_SIZE; i++) {
40        free_tcp_ports[i] = M64;
41    }
42    for (i = 0; i < UDP_ARRAY_SIZE; i++) {
43        free_udp_ports[i] = M64;
44    }
45}
46
47
48/**
49 * @brief Allocates the first free port found starting from pstart
50 * @param free_ports the array of port allocation data
51 * @param type UDP or TCP
52 * @param pstart defines the port it starts searching from
53 *
54 * @return the proposed port number or 0 in case all ports are allocated
55 */
56static uint16_t alloc_port(uint64_t * free_ports, net_ports_port_type_t type,
57                           uint16_t pstart)
58{
59    //k: asq did this to be generic?
60    //uint32_t v32;
61    //uint16_t v16;
62    //uint8_t v8;
63    int bitnr = 0;
64    uint64_t m = M32, v;
65    int s = 32;
66    uint64_t len;
67    uint64_t start;
68
69    if (type == net_ports_PORT_TCP) {
70        len = TCP_ARRAY_SIZE;
71        start = TCP_LOCAL_PORT_RANGE_START / 64;
72    } else {
73        len = UDP_ARRAY_SIZE;
74        start = UDP_LOCAL_PORT_RANGE_START / 64;
75    }
76
77    for (int i = start; i < len; i++) {
78        //find a 64bit word which has at least 1 bit set (=1 free port)
79        if (free_ports[i]) {
80            v = free_ports[i];
81            //binary search the 1-bit
82            while (m > 0) {
83                if (v & m) {
84                    v = v & m;
85                } else {
86                    v = (v >> s) & m;
87                    bitnr += s;
88                }
89                if (s != 1) {
90                    s /= 2;
91                }
92                m >>= s;
93            }
94            //bitnr is now the bitposition within the current 64 bit word which
95            //will be the allocated portnummer
96            assert(bitnr >= 0 && bitnr <= 63);
97            //mark the port as allocated
98            free_ports[i] &= ~(1 << bitnr);
99            //return the port number
100
101            return (bitnr + i * sizeof(uint64_t) * 8 + pstart);
102        }
103    }
104    return (0);                 //no port could be allocated
105}
106
107/**
108 * @brief allocates a tcp port
109 *
110 */
111uint16_t alloc_tcp_port(void)
112{
113    return alloc_port(free_tcp_ports, net_ports_PORT_TCP,
114                      TCP_LOCAL_PORT_RANGE_START);
115}
116
117/**
118 * @brief allocates a tcp port
119 *
120 */
121uint16_t alloc_udp_port(void)
122{
123    return alloc_port(free_udp_ports, net_ports_PORT_UDP,
124                      UDP_LOCAL_PORT_RANGE_START);
125}
126
127/**
128 * @brief checks to see whether a given port is free for use
129 *
130 * @param free_ports the array of either tcp or udp allocation state
131 * @param port the port number to be checked
132 *
133 * @return true in case the port is free and otherwise false
134 */
135static inline bool check_free(uint64_t * free_ports, uint16_t port)
136{
137    uint16_t pidx = port;
138
139    return (free_ports[pidx / 64] & (1 << (pidx % 64)));
140}
141
142/**
143 * @brief allocates a specific port. this is the backend function for bind
144 *
145 * @param port the port number
146 * @param type UDP or TCP
147 *
148 * @return 0 in case port is in use and the port number other wise
149 */
150inline uint16_t alloc_specific_port(uint16_t port, net_ports_port_type_t type)
151{
152    uint16_t pidx = port;
153    uint64_t *free_ports;
154
155    NDM_DEBUG("allocating port %u with type %d\n", port, type);
156    if (type == net_ports_PORT_TCP) {
157        free_ports = free_tcp_ports;
158    } else {
159        free_ports = free_udp_ports;
160    }
161    if (free_ports[pidx / 64] & (1 << (pidx % 64))) {
162        free_ports[pidx / 64] &= ~(1 << (pidx % 64));
163        return port;
164    } else {
165        return (0);
166    }
167}
168
169/**
170 * @brief frees a port and does not care whether it is allocated before or not!
171 *
172 * @param port the port number
173 * @param type UDP or TCP
174 */
175inline void free_port(uint16_t port, net_ports_port_type_t type)
176{
177    uint64_t *free_ports =
178      (type == net_ports_PORT_TCP) ? free_tcp_ports : free_udp_ports;
179    free_ports[port / 64] |= (1 << (port % 64));
180}
181