2 * vqp.c Functions to send/receive VQP packets.
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 2007 Alan DeKok <aland@deployingradius.com>
25 #include <freeradius-devel/libradius.h>
26 #include <freeradius-devel/udpfromto.h>
30 #define MAX_VMPS_LEN (MAX_STRING_LEN - 1)
32 /* @todo: this is a hack */
33 # define DEBUG if (fr_debug_flag && fr_log_fp) fr_printf_log
34 void fr_strerror_printf(char const *fmt, ...);
35 # define debug_pair(vp) do { if (fr_debug_flag && fr_log_fp) { \
36 vp_print(fr_log_fp, vp); \
40 * http://www.openbsd.org/cgi-bin/cvsweb/src/usr.sbin/tcpdump/print-vqp.c
42 * Some of how it works:
44 * http://www.hackingciscoexposed.com/pdf/chapter12.pdf
46 * VLAN Query Protocol (VQP)
49 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
50 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
51 * | Version | Opcode | Response Code | Data Count |
52 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
56 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
62 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
66 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68 * VQP is layered over UDP. The default destination port is 1589.
71 #define VQP_HDR_LEN (8)
72 #define VQP_VERSION (1)
73 #define VQP_MAX_ATTRIBUTES (12)
77 * Wrapper for sendto which handles sendfromto, IPv6, and all
78 * possible combinations.
80 * FIXME: This is just a copy of rad_sendto().
81 * Duplicate code is bad.
83 static int vqp_sendto(int sockfd, void *data, size_t data_len, int flags,
85 fr_ipaddr_t *src_ipaddr,
87 UNUSED fr_ipaddr_t *src_ipaddr,
89 fr_ipaddr_t *dst_ipaddr,
92 struct sockaddr_storage dst;
96 struct sockaddr_storage src;
99 if (!fr_ipaddr2sockaddr(src_ipaddr, 0, &src, &sizeof_src)) {
100 return -1; /* Unknown address family, Die Die Die! */
104 if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &dst, &sizeof_dst)) {
105 return -1; /* Unknown address family, Die Die Die! */
108 #ifdef WITH_UDPFROMTO
110 * Only IPv4 is supported for udpfromto.
112 * And if they don't specify a source IP address, don't
115 if ((dst_ipaddr->af == AF_INET) &&
116 (src_ipaddr->af != AF_UNSPEC)) {
117 return sendfromto(sockfd, data, data_len, flags,
118 (struct sockaddr *)&src, sizeof_src,
119 (struct sockaddr *)&dst, sizeof_dst);
124 * No udpfromto, OR an IPv6 socket, fail gracefully.
126 return sendto(sockfd, data, data_len, flags,
127 (struct sockaddr *)&dst, sizeof_dst);
131 * Wrapper for recvfrom, which handles recvfromto, IPv6, and all
132 * possible combinations.
134 * FIXME: This is copied from rad_recvfrom, with minor edits.
136 static ssize_t vqp_recvfrom(int sockfd, RADIUS_PACKET *packet, int flags,
137 fr_ipaddr_t *src_ipaddr, uint16_t *src_port,
138 fr_ipaddr_t *dst_ipaddr, uint16_t *dst_port)
140 struct sockaddr_storage src;
141 struct sockaddr_storage dst;
142 socklen_t sizeof_src = sizeof(src);
143 socklen_t sizeof_dst = sizeof(dst);
149 memset(&src, 0, sizeof_src);
150 memset(&dst, 0, sizeof_dst);
153 * Get address family, etc. first, so we know if we
154 * need to do udpfromto.
156 * FIXME: udpfromto also does this, but it's not
157 * a critical problem.
159 if (getsockname(sockfd, (struct sockaddr *)&dst,
160 &sizeof_dst) < 0) return -1;
163 * Read the length of the packet, from the packet.
164 * This lets us allocate the buffer to use for
165 * reading the rest of the packet.
167 data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK,
168 (struct sockaddr *)&src, &sizeof_src);
169 if (data_len < 0) return -1;
172 * Too little data is available, discard the packet.
175 rad_recv_discard(sockfd);
180 * Invalid version, packet type, or too many
183 } else if ((header[0] != VQP_VERSION) ||
186 (header[3] > VQP_MAX_ATTRIBUTES)) {
187 rad_recv_discard(sockfd);
191 } else { /* we got 4 bytes of data. */
193 * We don't care about the contents for now...
197 * How many attributes are in the packet.
201 if ((header[1] == 1) || (header[1] == 3)) {
202 if (len != VQP_MAX_ATTRIBUTES) {
203 rad_recv_discard(sockfd);
208 * Maximum length we support.
210 len = (12 * (4 + 4 + MAX_VMPS_LEN));
214 rad_recv_discard(sockfd);
219 * Maximum length we support.
221 len = (12 * (4 + 4 + MAX_VMPS_LEN));
227 * For now, be generous.
229 len = (12 * (4 + 4 + MAX_VMPS_LEN));
231 packet->data = talloc_array(packet, uint8_t, len);
232 if (!packet->data) return -1;
235 * Receive the packet. The OS will discard any data in the
236 * packet after "len" bytes.
238 #ifdef WITH_UDPFROMTO
239 if (dst.ss_family == AF_INET) {
240 data_len = recvfromto(sockfd, packet->data, len, flags,
241 (struct sockaddr *)&src, &sizeof_src,
242 (struct sockaddr *)&dst, &sizeof_dst);
246 * No udpfromto, OR an IPv6 socket. Fail gracefully.
248 data_len = recvfrom(sockfd, packet->data, len, flags,
249 (struct sockaddr *)&src, &sizeof_src);
254 if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, &port)) {
255 return -1; /* Unknown address family, Die Die Die! */
259 fr_sockaddr2ipaddr(&dst, sizeof_dst, dst_ipaddr, &port);
263 * Different address families should never happen.
265 if (src.ss_family != dst.ss_family) {
272 RADIUS_PACKET *vqp_recv(int sockfd)
277 RADIUS_PACKET *packet;
280 * Allocate the new request data structure
282 packet = rad_alloc(NULL, 0);
284 fr_strerror_printf("out of memory");
288 length = vqp_recvfrom(sockfd, packet, 0,
289 &packet->src_ipaddr, &packet->src_port,
290 &packet->dst_ipaddr, &packet->dst_port);
293 * Check for socket errors.
296 fr_strerror_printf("Error receiving packet: %s", strerror(errno));
297 /* packet->data is NULL */
301 packet->data_len = length; /* unsigned vs signed */
304 * We can only receive packets formatted in a way we
305 * expect. However, we accept MORE attributes in a
306 * packet than normal implementations may send.
308 if (packet->data_len < VQP_HDR_LEN) {
309 fr_strerror_printf("VQP packet is too short");
318 for (i = 0; i < packet->data_len; i++) {
319 if ((i & 0x0f) == 0) fprintf(stderr, "%02x: ", (int) i);
320 fprintf(stderr, "%02x ", ptr[i]);
321 if ((i & 0x0f) == 0x0f) fprintf(stderr, "\n");
326 if (ptr[3] > VQP_MAX_ATTRIBUTES) {
327 fr_strerror_printf("Too many VQP attributes");
332 if (packet->data_len > VQP_HDR_LEN) {
339 length = packet->data_len - VQP_HDR_LEN;
343 fr_strerror_printf("Packet contains malformed attribute");
349 * Attributes are 4 bytes
350 * 0x00000c01 ... 0x00000c08
352 if ((ptr[0] != 0) || (ptr[1] != 0) ||
353 (ptr[2] != 0x0c) || (ptr[3] < 1) || (ptr[3] > 8)) {
354 fr_strerror_printf("Packet contains invalid attribute");
362 * We support lengths 1..253, for internal
363 * server reasons. Also, there's no reason
364 * for bigger lengths to exist... admins
365 * won't be typing in a 32K vlan name.
367 * Except for received ethernet frames...
368 * they get chopped to 253 internally.
371 ((ptr[4] != 0) || (ptr[5] > MAX_VMPS_LEN))) {
372 fr_strerror_printf("Packet contains attribute with invalid length %02x %02x", ptr[4], ptr[5]);
376 attrlen = (ptr[4] << 8) | ptr[5];
378 length -= (6 + attrlen);
382 packet->sockfd = sockfd;
386 * This is more than a bit of a hack.
388 packet->code = PW_AUTHENTICATION_REQUEST;
390 memcpy(&id, packet->data + 4, 4);
391 packet->id = ntohl(id);
394 * FIXME: Create a fake "request authenticator", to
395 * avoid duplicates? Or is the VQP sequence number
396 * adequate for this purpose?
403 * We do NOT mirror the old-style RADIUS code that does encode,
404 * sign && send in one function. For VQP, the caller MUST perform
405 * each task manually, and separately.
407 int vqp_send(RADIUS_PACKET *packet)
409 if (!packet || !packet->data || (packet->data_len < 8)) return -1;
412 * Don't print out the attributes, they were printed out
413 * when it was encoded.
417 * And send it on it's way.
419 return vqp_sendto(packet->sockfd, packet->data, packet->data_len, 0,
420 &packet->src_ipaddr, &packet->dst_ipaddr,
425 int vqp_decode(RADIUS_PACKET *packet)
428 int attribute, length;
432 if (!packet || !packet->data) return -1;
434 if (packet->data_len < VQP_HDR_LEN) return -1;
436 paircursor(&cursor, &packet->vps);
437 vp = paircreate(packet, PW_VQP_PACKET_TYPE, 0);
439 fr_strerror_printf("No memory");
442 vp->vp_integer = packet->data[1];
444 pairinsert(&cursor, vp);
446 vp = paircreate(packet, PW_VQP_ERROR_CODE, 0);
448 fr_strerror_printf("No memory");
451 vp->vp_integer = packet->data[2];
453 pairinsert(&cursor, vp);
455 vp = paircreate(packet, PW_VQP_SEQUENCE_NUMBER, 0);
457 fr_strerror_printf("No memory");
460 vp->vp_integer = packet->id; /* already set by vqp_recv */
462 pairinsert(&cursor, vp);
464 ptr = packet->data + VQP_HDR_LEN;
465 end = packet->data + packet->data_len;
468 * Note that vqp_recv() MUST ensure that the packet is
469 * formatted in a way we expect, and that vqp_recv() MUST
470 * be called before vqp_decode().
475 attribute = (ptr[2] << 8) | ptr[3];
476 length = (ptr[4] << 8) | ptr[5];
480 * Hack to get the dictionaries to work correctly.
483 vp = paircreate(packet, attribute, 0);
485 pairfree(&packet->vps);
487 fr_strerror_printf("No memory");
491 switch (vp->da->type) {
494 memcpy(&vp->vp_ipaddr, ptr, 4);
500 * Value doesn't match the type we have for the
501 * valuepair so we must change it's da to an
504 vp->da = dict_attrunknown(vp->da->attr, vp->da->vendor,
510 pairmemcpy(vp, ptr, length);
515 vp->vp_strvalue = p = talloc_array(vp, char, vp->length + 1);
517 memcpy(p, ptr, vp->length);
518 p[vp->length] = '\0';
523 pairinsert(&cursor, vp);
527 * FIXME: Map attributes to Calling-Station-Id, etc...
534 * These are the MUST HAVE contents for a VQP packet.
536 * We don't allow the caller to give less than these, because
537 * it won't work. We don't encode more than these, because the
538 * clients will ignore it.
540 * FIXME: Be more generous? Look for CISCO + VQP attributes?
542 static int contents[5][VQP_MAX_ATTRIBUTES] = {
543 { 0, 0, 0, 0, 0, 0 },
544 { 0x0c01, 0x0c02, 0x0c03, 0x0c04, 0x0c07, 0x0c05 }, /* Join request */
545 { 0x0c03, 0x0c08, 0, 0, 0, 0 }, /* Join Response */
546 { 0x0c01, 0x0c02, 0x0c03, 0x0c04, 0x0c07, 0x0c08 }, /* Reconfirm */
547 { 0x0c03, 0x0c08, 0, 0, 0, 0 }
550 int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
555 VALUE_PAIR *vps[VQP_MAX_ATTRIBUTES];
558 fr_strerror_printf("Failed encoding VQP");
562 if (packet->data) return 0;
564 vp = pairfind(packet->vps, PW_VQP_PACKET_TYPE, 0, TAG_ANY);
566 fr_strerror_printf("Failed to find VQP-Packet-Type in response packet");
570 code = vp->vp_integer;
571 if ((code < 1) || (code > 4)) {
572 fr_strerror_printf("Invalid value %d for VQP-Packet-Type", code);
576 length = VQP_HDR_LEN;
577 memset(vps, 0, sizeof(vps));
579 vp = pairfind(packet->vps, PW_VQP_ERROR_CODE, 0, TAG_ANY);
582 * FIXME: Map attributes from calling-station-Id, etc.
584 * Maybe do this via rlm_vqp? That's probably the
585 * best place to add the code...
589 * No error: encode attributes.
591 if (!vp) for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
592 if (!contents[code][i]) break;
594 vps[i] = pairfind(packet->vps, contents[code][i] | 0x2000, 0, TAG_ANY);
597 * FIXME: Print the name...
600 fr_strerror_printf("Failed to find VQP attribute %02x",
606 length += vps[i]->length;
609 packet->data = talloc_array(packet, uint8_t, length);
611 fr_strerror_printf("No memory");
614 packet->data_len = length;
618 ptr[0] = VQP_VERSION;
624 ptr[2] = vp->vp_integer & 0xff;
629 * The number of attributes is hard-coded.
631 if ((code == 1) || (code == 3)) {
634 ptr[3] = VQP_MAX_ATTRIBUTES;
636 sequence = htonl(packet->id);
637 memcpy(ptr + 4, &sequence, 4);
640 fr_strerror_printf("Cannot send VQP response without request");
645 * Packet Sequence Number
647 memcpy(ptr + 4, original->data + 4, 4);
657 for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
659 if (ptr >= (packet->data + packet->data_len)) break;
666 * Type. Note that we look at only the lower 8
667 * bits, as the upper 8 bits have been hacked.
668 * See also dictionary.vqp
673 ptr[3] = vp->da->attr & 0xff;
677 ptr[5] = vp->length & 0xff;
682 switch (vp->da->type) {
684 memcpy(ptr, &vp->vp_ipaddr, 4);
690 memcpy(ptr, vp->vp_octets, vp->length);