2 * packet.c Generic packet manipulation functions.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2000-2006 The FreeRADIUS server project
23 static const char rcsid[] = "$Id$";
25 #include <freeradius-devel/autoconf.h>
26 #include <freeradius-devel/libradius.h>
31 * Take the key fields of a request packet, and convert it to a
34 uint32_t lrad_request_packet_hash(const RADIUS_PACKET *packet)
38 hash = lrad_hash(&packet->src_port, sizeof(packet->src_port));
39 hash = lrad_hash_update(&packet->dst_port,
40 sizeof(packet->dst_port), hash);
43 * The caller ensures that src & dst AF are the same.
45 switch (packet->src_ipaddr.af) {
47 hash = lrad_hash_update(&packet->src_ipaddr.ipaddr.ip4addr,
48 sizeof(packet->src_ipaddr.ipaddr.ip4addr),
50 hash = lrad_hash_update(&packet->dst_ipaddr.ipaddr.ip4addr,
51 sizeof(packet->dst_ipaddr.ipaddr.ip4addr),
55 hash = lrad_hash_update(&packet->src_ipaddr.ipaddr.ip6addr,
56 sizeof(packet->src_ipaddr.ipaddr.ip6addr),
58 hash = lrad_hash_update(&packet->dst_ipaddr.ipaddr.ip6addr,
59 sizeof(packet->dst_ipaddr.ipaddr.ip6addr),
67 return lrad_hash_update(&packet->id, sizeof(packet->id), hash);
72 * Take the key fields of a reply packet, and convert it to a
75 * i.e. take a reply packet, and find the hash of the request packet
76 * that asked for the reply. To do this, we hash the reverse fields
77 * of the request. e.g. where the request does (src, dst), we do
80 uint32_t lrad_reply_packet_hash(const RADIUS_PACKET *packet)
84 hash = lrad_hash(&packet->src_port, sizeof(packet->src_port));
85 hash = lrad_hash_update(&packet->dst_port,
86 sizeof(packet->dst_port), hash);
89 * The caller ensures that src & dst AF are the same.
91 switch (packet->src_ipaddr.af) {
93 hash = lrad_hash_update(&packet->dst_ipaddr.ipaddr.ip4addr,
94 sizeof(packet->dst_ipaddr.ipaddr.ip4addr),
96 hash = lrad_hash_update(&packet->src_ipaddr.ipaddr.ip4addr,
97 sizeof(packet->src_ipaddr.ipaddr.ip4addr),
101 hash = lrad_hash_update(&packet->dst_ipaddr.ipaddr.ip6addr,
102 sizeof(packet->dst_ipaddr.ipaddr.ip6addr),
104 hash = lrad_hash_update(&packet->src_ipaddr.ipaddr.ip6addr,
105 sizeof(packet->src_ipaddr.ipaddr.ip6addr),
113 return lrad_hash_update(&packet->id, sizeof(packet->id), hash);
118 * See if two packets are identical.
120 * Note that we do NOT compare the authentication vectors.
121 * That's because if the authentication vector is different,
122 * it means that the NAS has given up on the earlier request.
124 int lrad_packet_cmp(const RADIUS_PACKET *a, const RADIUS_PACKET *b)
128 if (a->sockfd < b->sockfd) return -1;
129 if (a->sockfd > b->sockfd) return +1;
131 if (a->src_ipaddr.af < b->dst_ipaddr.af) return -1;
132 if (a->src_ipaddr.af > b->dst_ipaddr.af) return +1;
134 if (a->id < b->id) return -1;
135 if (a->id > b->id) return +1;
137 if (a->src_port < b->src_port) return -1;
138 if (a->src_port > b->src_port) return +1;
140 if (a->dst_port < b->dst_port) return -1;
141 if (a->dst_port > b->dst_port) return +1;
143 switch (a->dst_ipaddr.af) {
145 rcode = memcmp(&a->dst_ipaddr.ipaddr.ip4addr,
146 &b->dst_ipaddr.ipaddr.ip4addr,
147 sizeof(a->dst_ipaddr.ipaddr.ip4addr));
148 if (rcode != 0) return rcode;
149 rcode = memcmp(&a->src_ipaddr.ipaddr.ip4addr,
150 &b->src_ipaddr.ipaddr.ip4addr,
151 sizeof(a->src_ipaddr.ipaddr.ip4addr));
152 if (rcode != 0) return rcode;
155 rcode = memcmp(&a->dst_ipaddr.ipaddr.ip6addr,
156 &b->dst_ipaddr.ipaddr.ip6addr,
157 sizeof(a->dst_ipaddr.ipaddr.ip6addr));
158 if (rcode != 0) return rcode;
159 rcode = memcmp(&a->src_ipaddr.ipaddr.ip6addr,
160 &b->src_ipaddr.ipaddr.ip6addr,
161 sizeof(a->src_ipaddr.ipaddr.ip6addr));
162 if (rcode != 0) return rcode;
170 * Everything's equal. Say so.
177 * Create a fake "request" from a reply, for later lookup.
179 void lrad_request_from_reply(RADIUS_PACKET *request,
180 const RADIUS_PACKET *reply)
182 request->sockfd = reply->sockfd;
183 request->id = reply->id;
184 request->src_port = reply->dst_port;
185 request->dst_port = reply->src_port;
186 request->src_ipaddr = reply->dst_ipaddr;
187 request->dst_ipaddr = reply->src_ipaddr;
192 * Open a socket on the given IP and port.
194 int lrad_socket(lrad_ipaddr_t *ipaddr, int port)
197 struct sockaddr salocal;
200 if ((port < 0) || (port > 65535)) {
201 librad_log("Port %d is out of allowed bounds", port);
205 sockfd = socket(ipaddr->af, SOCK_DGRAM, 0);
207 librad_log("Failed opening socket: %s", strerror(errno));
211 #ifdef WITH_UDPFROMTO
213 * Initialize udpfromto for all sockets.
215 if (udpfromto_init(sockfd) != 0) {
221 if (ipaddr->af == AF_INET) {
222 struct sockaddr_in *sa;
224 sa = (struct sockaddr_in *) &salocal;
225 memset(sa, 0, sizeof(salocal));
226 sa->sin_family = AF_INET;
227 sa->sin_addr = ipaddr->ipaddr.ip4addr;
228 sa->sin_port = htons((uint16_t) port);
231 #ifdef HAVE_STRUCT_SOCKADDR_IN6
232 } else if (ipaddr->af == AF_INET6) {
233 struct sockaddr_in6 *sa;
235 sa = (struct sockaddr_in6 *) &salocal;
236 memset(sa, 0, sizeof(salocal));
237 sa->sin6_family = AF_INET6;
238 sa->sin6_addr = ipaddr->ipaddr.ip6addr;
239 sa->sin6_port = htons((uint16_t) port);
243 * Listening on '::' does NOT get you IPv4 to
244 * IPv6 mapping. You've got to listen on an IPv4
245 * address, too. This makes the rest of the server
246 * design a little simpler.
249 if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr->ipaddr.ip6addr)) {
252 setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
253 (char *)&on, sizeof(on));
255 #endif /* IPV6_V6ONLY */
256 #endif /* HAVE_STRUCT_SOCKADDR_IN6 */
258 return sockfd; /* don't bind it */
261 if (bind(sockfd, &salocal, salen) < 0) {
262 librad_log("Bind to address failed: %s", strerror(errno));