📄 packet.c
字号:
/* * packet.c Generic packet manipulation functions. * * Version: $Id: packet.c,v 1.20 2008/01/01 17:29:12 aland Exp $ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2000-2006 The FreeRADIUS server project */#include <freeradius-devel/ident.h>RCSID("$Id: packet.c,v 1.20 2008/01/01 17:29:12 aland Exp $")#include <freeradius-devel/libradius.h>#ifdef WITH_UDPFROMTO#include <freeradius-devel/udpfromto.h>#endif/* * Take the key fields of a request packet, and convert it to a * hash. */uint32_t fr_request_packet_hash(const RADIUS_PACKET *packet){ uint32_t hash; if (packet->hash) return packet->hash; hash = fr_hash(&packet->sockfd, sizeof(packet->sockfd)); hash = fr_hash_update(&packet->src_port, sizeof(packet->src_port), hash); hash = fr_hash_update(&packet->dst_port, sizeof(packet->dst_port), hash); hash = fr_hash_update(&packet->src_ipaddr.af, sizeof(packet->src_ipaddr.af), hash); /* * The caller ensures that src & dst AF are the same. */ switch (packet->src_ipaddr.af) { case AF_INET: hash = fr_hash_update(&packet->src_ipaddr.ipaddr.ip4addr, sizeof(packet->src_ipaddr.ipaddr.ip4addr), hash); hash = fr_hash_update(&packet->dst_ipaddr.ipaddr.ip4addr, sizeof(packet->dst_ipaddr.ipaddr.ip4addr), hash); break; case AF_INET6: hash = fr_hash_update(&packet->src_ipaddr.ipaddr.ip6addr, sizeof(packet->src_ipaddr.ipaddr.ip6addr), hash); hash = fr_hash_update(&packet->dst_ipaddr.ipaddr.ip6addr, sizeof(packet->dst_ipaddr.ipaddr.ip6addr), hash); break; default: break; } return fr_hash_update(&packet->id, sizeof(packet->id), hash);}/* * Take the key fields of a reply packet, and convert it to a * hash. * * i.e. take a reply packet, and find the hash of the request packet * that asked for the reply. To do this, we hash the reverse fields * of the request. e.g. where the request does (src, dst), we do * (dst, src) */uint32_t fr_reply_packet_hash(const RADIUS_PACKET *packet){ uint32_t hash; hash = fr_hash(&packet->sockfd, sizeof(packet->sockfd)); hash = fr_hash_update(&packet->id, sizeof(packet->id), hash); hash = fr_hash_update(&packet->src_port, sizeof(packet->src_port), hash); hash = fr_hash_update(&packet->dst_port, sizeof(packet->dst_port), hash); hash = fr_hash_update(&packet->src_ipaddr.af, sizeof(packet->src_ipaddr.af), hash); /* * The caller ensures that src & dst AF are the same. */ switch (packet->src_ipaddr.af) { case AF_INET: hash = fr_hash_update(&packet->dst_ipaddr.ipaddr.ip4addr, sizeof(packet->dst_ipaddr.ipaddr.ip4addr), hash); hash = fr_hash_update(&packet->src_ipaddr.ipaddr.ip4addr, sizeof(packet->src_ipaddr.ipaddr.ip4addr), hash); break; case AF_INET6: hash = fr_hash_update(&packet->dst_ipaddr.ipaddr.ip6addr, sizeof(packet->dst_ipaddr.ipaddr.ip6addr), hash); hash = fr_hash_update(&packet->src_ipaddr.ipaddr.ip6addr, sizeof(packet->src_ipaddr.ipaddr.ip6addr), hash); break; default: break; } return fr_hash_update(&packet->id, sizeof(packet->id), hash);}/* * See if two packets are identical. * * Note that we do NOT compare the authentication vectors. * That's because if the authentication vector is different, * it means that the NAS has given up on the earlier request. */int fr_packet_cmp(const RADIUS_PACKET *a, const RADIUS_PACKET *b){ int rcode; if (a->sockfd < b->sockfd) return -1; if (a->sockfd > b->sockfd) return +1; if (a->id < b->id) return -1; if (a->id > b->id) return +1; if (a->src_port < b->src_port) return -1; if (a->src_port > b->src_port) return +1; if (a->dst_port < b->dst_port) return -1; if (a->dst_port > b->dst_port) return +1; rcode = fr_ipaddr_cmp(&a->dst_ipaddr, &b->dst_ipaddr); if (rcode != 0) return rcode; return fr_ipaddr_cmp(&a->src_ipaddr, &b->src_ipaddr);}/* * Create a fake "request" from a reply, for later lookup. */void fr_request_from_reply(RADIUS_PACKET *request, const RADIUS_PACKET *reply){ request->sockfd = reply->sockfd; request->id = reply->id; request->src_port = reply->dst_port; request->dst_port = reply->src_port; request->src_ipaddr = reply->dst_ipaddr; request->dst_ipaddr = reply->src_ipaddr;}/* * Open a socket on the given IP and port. */int fr_socket(fr_ipaddr_t *ipaddr, int port){ int sockfd; struct sockaddr_storage salocal; socklen_t salen; if ((port < 0) || (port > 65535)) { librad_log("Port %d is out of allowed bounds", port); return -1; } sockfd = socket(ipaddr->af, SOCK_DGRAM, 0); if (sockfd < 0) { return sockfd; }#ifdef WITH_UDPFROMTO /* * Initialize udpfromto for all sockets. */ if (udpfromto_init(sockfd) != 0) { close(sockfd); return -1; }#endif memset(&salocal, 0, sizeof(salocal)); if (ipaddr->af == AF_INET) { struct sockaddr_in *sa; sa = (struct sockaddr_in *) &salocal; sa->sin_family = AF_INET; sa->sin_addr = ipaddr->ipaddr.ip4addr; sa->sin_port = htons((uint16_t) port); salen = sizeof(*sa);#ifdef HAVE_STRUCT_SOCKADDR_IN6 } else if (ipaddr->af == AF_INET6) { struct sockaddr_in6 *sa; sa = (struct sockaddr_in6 *) &salocal; sa->sin6_family = AF_INET6; sa->sin6_addr = ipaddr->ipaddr.ip6addr; sa->sin6_port = htons((uint16_t) port); salen = sizeof(*sa);#if 1 /* * Listening on '::' does NOT get you IPv4 to * IPv6 mapping. You've got to listen on an IPv4 * address, too. This makes the rest of the server * design a little simpler. */#ifdef IPV6_V6ONLY if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr->ipaddr.ip6addr)) { int on = 1; setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof(on)); }#endif /* IPV6_V6ONLY */#endif#endif /* HAVE_STRUCT_SOCKADDR_IN6 */ } else { return sockfd; /* don't bind it */ } if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) { close(sockfd); return -1; } return sockfd;}/* * We need to keep track of the socket & it's IP/port. */typedef struct fr_packet_socket_t { int sockfd; int num_outgoing; int offset; /* 0..31 */ int inaddr_any; fr_ipaddr_t ipaddr; int port;} fr_packet_socket_t;#define FNV_MAGIC_PRIME (0x01000193)#define MAX_SOCKETS (32)#define SOCKOFFSET_MASK (MAX_SOCKETS - 1)#define SOCK2OFFSET(sockfd) ((sockfd * FNV_MAGIC_PRIME) & SOCKOFFSET_MASK)#define MAX_QUEUES (8)/* * Structure defining a list of packets (incoming or outgoing) * that should be managed. */struct fr_packet_list_t { fr_hash_table_t *ht; fr_hash_table_t *dst2id_ht; int alloc_id; int num_outgoing; uint32_t mask; int last_recv; fr_packet_socket_t sockets[MAX_SOCKETS];};/* * Ugh. Doing this on every sent/received packet is not nice. */static fr_packet_socket_t *fr_socket_find(fr_packet_list_t *pl, int sockfd){ int i, start; i = start = SOCK2OFFSET(sockfd); do { /* make this hack slightly more efficient */ if (pl->sockets[i].sockfd == sockfd) return &pl->sockets[i]; i = (i + 1) & SOCKOFFSET_MASK; } while (i != start); return NULL;}int fr_packet_list_socket_remove(fr_packet_list_t *pl, int sockfd){ fr_packet_socket_t *ps; if (!pl) return 0; ps = fr_socket_find(pl, sockfd); if (!ps) return 0; /* * FIXME: Allow the caller forcibly discard these? */ if (ps->num_outgoing != 0) return 0; ps->sockfd = -1; pl->mask &= ~(1 << ps->offset); return 1;}int fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd){ int i, start; struct sockaddr_storage src; socklen_t sizeof_src = sizeof(src); fr_packet_socket_t *ps; if (!pl) return 0; ps = NULL; i = start = SOCK2OFFSET(sockfd); do { if (pl->sockets[i].sockfd == -1) { ps = &pl->sockets[i]; start = i; break; } i = (i + 1) & SOCKOFFSET_MASK; } while (i != start); if (!ps) { return 0; } memset(ps, 0, sizeof(*ps)); ps->sockfd = sockfd; ps->offset = start; /* * Get address family, etc. first, so we know if we * need to do udpfromto. * * FIXME: udpfromto also does this, but it's not * a critical problem. */ memset(&src, 0, sizeof_src); if (getsockname(sockfd, (struct sockaddr *) &src, &sizeof_src) < 0) { return 0; } /* * Grab IP addresses & ports from the sockaddr. */ ps->ipaddr.af = src.ss_family; if (src.ss_family == AF_INET) { struct sockaddr_in *s4; s4 = (struct sockaddr_in *)&src; ps->ipaddr.ipaddr.ip4addr = s4->sin_addr; ps->port = ntohs(s4->sin_port); if (ps->ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY) { ps->inaddr_any = 1; }#ifdef HAVE_STRUCT_SOCKADDR_IN6 } else if (src.ss_family == AF_INET6) { struct sockaddr_in6 *s6; s6 = (struct sockaddr_in6 *)&src; ps->ipaddr.ipaddr.ip6addr = s6->sin6_addr; ps->port = ntohs(s6->sin6_port); if (IN6_IS_ADDR_UNSPECIFIED(&ps->ipaddr.ipaddr.ip6addr)) { ps->inaddr_any = 1; }#endif } else { return 0; } pl->mask |= (1 << ps->offset); return 1;}static uint32_t packet_entry_hash(const void *data){ return fr_request_packet_hash(*(const RADIUS_PACKET * const *) data);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -