📄 packet.c
字号:
}static int packet_entry_cmp(const void *one, const void *two){ const RADIUS_PACKET * const *a = one; const RADIUS_PACKET * const *b = two; return fr_packet_cmp(*a, *b);}/* * A particular socket can have 256 RADIUS ID's outstanding to * any one destination IP/port. So we have a structure that * manages destination IP & port, and has an array of 256 ID's. * * The only magic here is that we map the socket number (0..256) * into an "internal" socket number 0..31, that we use to set * bits in the ID array. If a bit is 1, then that ID is in use * for that socket, and the request MUST be in the packet hash! * * Note that as a minor memory leak, we don't have an API to free * this structure, except when we discard the whole packet list. * This means that if destinations are added and removed, they * won't be removed from this tree. */typedef struct fr_packet_dst2id_t { fr_ipaddr_t dst_ipaddr; int dst_port; uint32_t id[1]; /* really id[256] */} fr_packet_dst2id_t;static uint32_t packet_dst2id_hash(const void *data){ uint32_t hash; const fr_packet_dst2id_t *pd = data; hash = fr_hash(&pd->dst_port, sizeof(pd->dst_port)); switch (pd->dst_ipaddr.af) { case AF_INET: hash = fr_hash_update(&pd->dst_ipaddr.ipaddr.ip4addr, sizeof(pd->dst_ipaddr.ipaddr.ip4addr), hash); break; case AF_INET6: hash = fr_hash_update(&pd->dst_ipaddr.ipaddr.ip6addr, sizeof(pd->dst_ipaddr.ipaddr.ip6addr), hash); break; default: break; } return hash;}static int packet_dst2id_cmp(const void *one, const void *two){ const fr_packet_dst2id_t *a = one; const fr_packet_dst2id_t *b = two; if (a->dst_port < b->dst_port) return -1; if (a->dst_port > b->dst_port) return +1; return fr_ipaddr_cmp(&a->dst_ipaddr, &b->dst_ipaddr);}static void packet_dst2id_free(void *data){ free(data);}void fr_packet_list_free(fr_packet_list_t *pl){ if (!pl) return; fr_hash_table_free(pl->ht); fr_hash_table_free(pl->dst2id_ht); free(pl);}/* * Caller is responsible for managing the packet entries. */fr_packet_list_t *fr_packet_list_create(int alloc_id){ int i; fr_packet_list_t *pl; pl = malloc(sizeof(*pl)); if (!pl) return NULL; memset(pl, 0, sizeof(*pl)); pl->ht = fr_hash_table_create(packet_entry_hash, packet_entry_cmp, NULL); if (!pl->ht) { fr_packet_list_free(pl); return NULL; } for (i = 0; i < MAX_SOCKETS; i++) { pl->sockets[i].sockfd = -1; } if (alloc_id) { pl->alloc_id = 1; pl->dst2id_ht = fr_hash_table_create(packet_dst2id_hash, packet_dst2id_cmp, packet_dst2id_free); if (!pl->dst2id_ht) { fr_packet_list_free(pl); return NULL; } } return pl;}/* * If pl->alloc_id is set, then fr_packet_list_id_alloc() MUST * be called before inserting the packet into the list! */int fr_packet_list_insert(fr_packet_list_t *pl, RADIUS_PACKET **request_p){ if (!pl || !request_p || !*request_p) return 0; (*request_p)->hash = fr_request_packet_hash(*request_p); return fr_hash_table_insert(pl->ht, request_p);}RADIUS_PACKET **fr_packet_list_find(fr_packet_list_t *pl, RADIUS_PACKET *request){ if (!pl || !request) return 0; return fr_hash_table_finddata(pl->ht, &request);}/* * This presumes that the reply has dst_ipaddr && dst_port set up * correctly (i.e. real IP, or "*"). */RADIUS_PACKET **fr_packet_list_find_byreply(fr_packet_list_t *pl, RADIUS_PACKET *reply){ RADIUS_PACKET my_request, *request; fr_packet_socket_t *ps; if (!pl || !reply) return NULL; ps = fr_socket_find(pl, reply->sockfd); if (!ps) return NULL; /* * Initialize request from reply, AND from the source * IP & port of this socket. The client may have bound * the socket to 0, in which case it's some random port, * that is NOT in the original request->src_port. */ my_request.sockfd = reply->sockfd; my_request.id = reply->id; if (ps->inaddr_any) { my_request.src_ipaddr = ps->ipaddr; } else { my_request.src_ipaddr = reply->dst_ipaddr; } my_request.src_port = ps->port;; my_request.dst_ipaddr = reply->src_ipaddr; my_request.dst_port = reply->src_port; my_request.hash = 0; request = &my_request; return fr_hash_table_finddata(pl->ht, &request);}RADIUS_PACKET **fr_packet_list_yank(fr_packet_list_t *pl, RADIUS_PACKET *request){ if (!pl || !request) return NULL; return fr_hash_table_yank(pl->ht, &request);}int fr_packet_list_num_elements(fr_packet_list_t *pl){ if (!pl) return 0; return fr_hash_table_num_elements(pl->ht);}/* * 1 == ID was allocated & assigned * 0 == error allocating memory * -1 == all ID's are used, caller should open a new socket. * * Note that this ALSO assigns a socket to use, and updates * packet->request->src_ipaddr && packet->request->src_port * * In multi-threaded systems, the calls to id_alloc && id_free * should be protected by a mutex. This does NOT have to be * the same mutex as the one protecting the insert/find/yank * calls! */int fr_packet_list_id_alloc(fr_packet_list_t *pl, RADIUS_PACKET *request){ int i, id, start; uint32_t free_mask; fr_packet_dst2id_t my_pd, *pd; fr_packet_socket_t *ps; if (!pl || !pl->alloc_id || !request) return 0; my_pd.dst_ipaddr = request->dst_ipaddr; my_pd.dst_port = request->dst_port; pd = fr_hash_table_finddata(pl->dst2id_ht, &my_pd); if (!pd) { pd = malloc(sizeof(*pd) + 255 * sizeof(pd->id[0])); if (!pd) return 0; memset(pd, 0, sizeof(*pd) + 255 * sizeof(pd->id[0])); pd->dst_ipaddr = request->dst_ipaddr; pd->dst_port = request->dst_port; if (!fr_hash_table_insert(pl->dst2id_ht, pd)) { free(pd); return 0; } } /* * FIXME: Go to an LRU system. This prevents ID re-use * for as long as possible. The main problem with that * approach is that it requires us to populate the * LRU/FIFO when we add a new socket, or a new destination, * which can be expensive. * * The LRU can be avoided if the caller takes care to free * Id's only when all responses have been received, OR after * a timeout. */ id = start = (int) fr_rand() & 0xff; while (pd->id[id] == pl->mask) { /* all sockets are using this ID */ id++; id &= 0xff; if (id == start) return 0; } free_mask = ~((~pd->id[id]) & pl->mask); start = -1; for (i = 0; i < MAX_SOCKETS; i++) { if (pl->sockets[i].sockfd == -1) continue; /* paranoia */ if ((free_mask & (1 << i)) == 0) { start = i; break; } } if (start < 0) return 0; /* bad error */ pd->id[id] |= (1 << start); ps = &pl->sockets[start]; ps->num_outgoing++; pl->num_outgoing++; /* * Set the ID, source IP, and source port. */ request->id = id; request->sockfd = ps->sockfd; request->src_ipaddr = ps->ipaddr; request->src_port = ps->port; return 1;}/* * Should be called AFTER yanking it from the list, so that * any newly inserted entries don't collide with this one. */int fr_packet_list_id_free(fr_packet_list_t *pl, RADIUS_PACKET *request){ fr_packet_socket_t *ps; fr_packet_dst2id_t my_pd, *pd; if (!pl || !request) return 0; ps = fr_socket_find(pl, request->sockfd); if (!ps) return 0; my_pd.dst_ipaddr = request->dst_ipaddr; my_pd.dst_port = request->dst_port; pd = fr_hash_table_finddata(pl->dst2id_ht, &my_pd); if (!pd) return 0; pd->id[request->id] &= ~(1 << ps->offset); request->hash = 0; /* invalidate the cached hash */ ps->num_outgoing--; pl->num_outgoing--; return 1;}int fr_packet_list_walk(fr_packet_list_t *pl, void *ctx, fr_hash_table_walk_t callback){ if (!pl || !callback) return 0; return fr_hash_table_walk(pl->ht, callback, ctx);}int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set){ int i, maxfd; if (!pl || !set) return 0; maxfd = -1; for (i = 0; i < MAX_SOCKETS; i++) { if (pl->sockets[i].sockfd == -1) continue; FD_SET(pl->sockets[i].sockfd, set); if (pl->sockets[i].sockfd > maxfd) { maxfd = pl->sockets[i].sockfd; } } if (maxfd < 0) return -1; return maxfd + 1;}/* * Round-robins the receivers, without priority. * * FIXME: Add sockfd, if -1, do round-robin, else do sockfd * IF in fdset. */RADIUS_PACKET *fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set){ int start; RADIUS_PACKET *packet; if (!pl || !set) return NULL; start = pl->last_recv; do { start++; start &= SOCKOFFSET_MASK; if (pl->sockets[start].sockfd == -1) continue; if (!FD_ISSET(pl->sockets[start].sockfd, set)) continue; packet = rad_recv(pl->sockets[start].sockfd, 0); if (!packet) continue; /* * Call fr_packet_list_find_byreply(). If it * doesn't find anything, discard the reply. */ pl->last_recv = start; return packet; } while (start != pl->last_recv); return NULL;}int fr_packet_list_num_incoming(fr_packet_list_t *pl){ int num_elements; if (!pl) return 0; num_elements = fr_hash_table_num_elements(pl->ht); if (num_elements < pl->num_outgoing) return 0; /* panic! */ return num_elements - pl->num_outgoing;}int fr_packet_list_num_outgoing(fr_packet_list_t *pl){ if (!pl) return 0; return pl->num_outgoing;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -