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>
23 #include <freeradius-devel/ident.h>
26 #include <freeradius-devel/libradius.h>
27 #include <freeradius-devel/vqp.h>
32 * http://www.openbsd.org/cgi-bin/cvsweb/src/usr.sbin/tcpdump/print-vqp.c
34 * Some of how it works:
36 * http://www.hackingciscoexposed.com/pdf/chapter12.pdf
38 * VLAN Query Protocol (VQP)
41 * 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
42 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43 * | Version | Opcode | Response Code | Data Count |
44 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
58 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60 * VQP is layered over UDP. The default destination port is 1589.
63 #define VQP_HDR_LEN (8)
64 #define VQP_VERSION (1)
65 #define VQP_MAX_ATTRIBUTES (12)
69 * Wrapper for sendto which handles sendfromto, IPv6, and all
70 * possible combinations.
72 * FIXME: This is just a copy of rad_sendto().
73 * Duplicate code is bad.
75 static int vqp_sendto(int sockfd, void *data, size_t data_len, int flags,
76 lrad_ipaddr_t *src_ipaddr, lrad_ipaddr_t *dst_ipaddr,
79 struct sockaddr_storage dst;
80 socklen_t sizeof_dst = sizeof(dst);
83 struct sockaddr_storage src;
84 socklen_t sizeof_src = sizeof(src);
86 memset(&src, 0, sizeof(src));
88 memset(&dst, 0, sizeof(dst));
93 if (dst_ipaddr->af == AF_INET) {
94 struct sockaddr_in *s4;
96 s4 = (struct sockaddr_in *)&dst;
97 sizeof_dst = sizeof(struct sockaddr_in);
99 s4->sin_family = AF_INET;
100 s4->sin_addr = dst_ipaddr->ipaddr.ip4addr;
101 s4->sin_port = htons(dst_port);
103 #ifdef WITH_UDPFROMTO
104 s4 = (struct sockaddr_in *)&src;
105 sizeof_src = sizeof(struct sockaddr_in);
107 s4->sin_family = AF_INET;
108 s4->sin_addr = src_ipaddr->ipaddr.ip4addr;
112 * IPv6 MAY be supported.
114 #ifdef HAVE_STRUCT_SOCKADDR_IN6
115 } else if (dst_ipaddr->af == AF_INET6) {
116 struct sockaddr_in6 *s6;
118 s6 = (struct sockaddr_in6 *)&dst;
119 sizeof_dst = sizeof(struct sockaddr_in6);
121 s6->sin6_family = AF_INET6;
122 s6->sin6_addr = dst_ipaddr->ipaddr.ip6addr;
123 s6->sin6_port = htons(dst_port);
125 #ifdef WITH_UDPFROMTO
126 return -1; /* UDPFROMTO && IPv6 are not supported */
128 s6 = (struct sockaddr_in6 *)&src;
129 sizeof_src = sizeof(struct sockaddr_in6);
131 s6->sin6_family = AF_INET6;
132 s6->sin6_addr = src_ipaddr->ipaddr.ip6addr;
134 #endif /* WITH_UDPFROMTO */
135 #endif /* HAVE_STRUCT_SOCKADDR_IN6 */
136 } else return -1; /* Unknown address family, Die Die Die! */
138 #ifdef WITH_UDPFROMTO
140 * Only IPv4 is supported for udpfromto.
142 * And if they don't specify a source IP address, don't
145 if ((dst_ipaddr->af == AF_INET) ||
146 (src_ipaddr->af != AF_UNSPEC)) {
147 return sendfromto(sockfd, data, data_len, flags,
148 (struct sockaddr *)&src, sizeof_src,
149 (struct sockaddr *)&dst, sizeof_dst);
152 src_ipaddr = src_ipaddr; /* -Wunused */
156 * No udpfromto, OR an IPv6 socket, fail gracefully.
158 return sendto(sockfd, data, data_len, flags,
159 (struct sockaddr *)&dst, sizeof_dst);
163 * Wrapper for recvfrom, which handles recvfromto, IPv6, and all
164 * possible combinations.
166 * FIXME: This is copied from rad_recvfrom, with minor edits.
168 static ssize_t vqp_recvfrom(int sockfd, uint8_t **pbuf, int flags,
169 lrad_ipaddr_t *src_ipaddr, uint16_t *src_port,
170 lrad_ipaddr_t *dst_ipaddr, uint16_t *dst_port)
172 struct sockaddr_storage src;
173 struct sockaddr_storage dst;
174 socklen_t sizeof_src = sizeof(src);
175 socklen_t sizeof_dst = sizeof(dst);
181 memset(&src, 0, sizeof_src);
182 memset(&dst, 0, sizeof_dst);
185 * Get address family, etc. first, so we know if we
186 * need to do udpfromto.
188 * FIXME: udpfromto also does this, but it's not
189 * a critical problem.
191 if (getsockname(sockfd, (struct sockaddr *)&dst,
192 &sizeof_dst) < 0) return -1;
195 * Read the length of the packet, from the packet.
196 * This lets us allocate the buffer to use for
197 * reading the rest of the packet.
199 data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK,
200 (struct sockaddr *)&src, &sizeof_src);
201 if (data_len < 0) return -1;
204 * Too little data is available, discard the packet.
207 recvfrom(sockfd, header, sizeof(header), flags,
208 (struct sockaddr *)&src, &sizeof_src);
212 * Invalid version, packet type, or too many
215 } else if ((header[0] != VQP_VERSION) ||
218 (header[3] > VQP_MAX_ATTRIBUTES)) {
219 recvfrom(sockfd, header, sizeof(header), flags,
220 (struct sockaddr *)&src, &sizeof_src);
223 } else { /* we got 4 bytes of data. */
225 * We don't care about the contents for now...
229 * How many attributes are in the packet.
233 if ((header[1] == 1) || (header[1] == 3)) {
234 if (len != VQP_MAX_ATTRIBUTES) {
235 recvfrom(sockfd, header, sizeof(header), 0,
236 (struct sockaddr *)&src, &sizeof_src);
240 * Maximum length we support.
242 len = (12 * (4 + 4 + 253));
246 recvfrom(sockfd, header, sizeof(header), 0,
247 (struct sockaddr *)&src, &sizeof_src);
251 * Maximum length we support.
253 len = (12 * (4 + 4 + 253));
259 * For now, be generous.
261 len = (12 * (4 + 4 + 253));
267 * Receive the packet. The OS will discard any data in the
268 * packet after "len" bytes.
270 #ifdef WITH_UDPFROMTO
271 if (dst.ss_family == AF_INET) {
272 data_len = recvfromto(sockfd, buf, len, flags,
273 (struct sockaddr *)&src, &sizeof_src,
274 (struct sockaddr *)&dst, &sizeof_dst);
278 * No udpfromto, OR an IPv6 socket. Fail gracefully.
280 data_len = recvfrom(sockfd, buf, len, flags,
281 (struct sockaddr *)&src, &sizeof_src);
288 * Check address families, and update src/dst ports, etc.
290 if (src.ss_family == AF_INET) {
291 struct sockaddr_in *s4;
293 s4 = (struct sockaddr_in *)&src;
294 src_ipaddr->af = AF_INET;
295 src_ipaddr->ipaddr.ip4addr = s4->sin_addr;
296 *src_port = ntohs(s4->sin_port);
298 s4 = (struct sockaddr_in *)&dst;
299 dst_ipaddr->af = AF_INET;
300 dst_ipaddr->ipaddr.ip4addr = s4->sin_addr;
301 *dst_port = ntohs(s4->sin_port);
303 #ifdef HAVE_STRUCT_SOCKADDR_IN6
304 } else if (src.ss_family == AF_INET6) {
305 struct sockaddr_in6 *s6;
307 s6 = (struct sockaddr_in6 *)&src;
308 src_ipaddr->af = AF_INET6;
309 src_ipaddr->ipaddr.ip6addr = s6->sin6_addr;
310 *src_port = ntohs(s6->sin6_port);
312 s6 = (struct sockaddr_in6 *)&dst;
313 dst_ipaddr->af = AF_INET6;
314 dst_ipaddr->ipaddr.ip6addr = s6->sin6_addr;
315 *dst_port = ntohs(s6->sin6_port);
319 return -1; /* Unknown address family, Die Die Die! */
323 * Different address families should never happen.
325 if (src.ss_family != dst.ss_family) {
331 * Tell the caller about the data
338 RADIUS_PACKET *vqp_recv(int sockfd)
343 RADIUS_PACKET *packet;
346 * Allocate the new request data structure
348 if ((packet = malloc(sizeof(*packet))) == NULL) {
349 librad_log("out of memory");
352 memset(packet, 0, sizeof(*packet));
354 packet->data_len = vqp_recvfrom(sockfd, &packet->data, 0,
355 &packet->src_ipaddr, &packet->src_port,
356 &packet->dst_ipaddr, &packet->dst_port);
359 * Check for socket errors.
361 if (packet->data_len < 0) {
362 librad_log("Error receiving packet: %s", strerror(errno));
363 /* packet->data is NULL */
370 * We can only receive packets formatted in a way we
371 * expect. However, we accept MORE attributes in a
372 * packet than normal implementations may send.
374 if (packet->data_len < VQP_HDR_LEN) {
375 librad_log("VQP packet is too short");
384 for (i = 0; i < packet->data_len; i++) {
385 if ((i & 0x0f) == 0) fprintf(stderr, "%02x: ", i);
386 fprintf(stderr, "%02x ", ptr[i]);
387 if ((i & 0x0f) == 0x0f) fprintf(stderr, "\n");
392 if (ptr[3] > VQP_MAX_ATTRIBUTES) {
393 librad_log("Too many VQP attributes");
398 if (packet->data_len > VQP_HDR_LEN) {
405 length = packet->data_len - VQP_HDR_LEN;
409 librad_log("Packet contains malformed attribute");
415 * Attributes are 4 bytes
416 * 0x00000c01 ... 0x00000c08
418 if ((ptr[0] != 0) || (ptr[1] != 0) ||
419 (ptr[2] != 0x0c) || (ptr[3] < 1) || (ptr[3] > 8)) {
420 librad_log("Packet contains invalid attribute");
428 * We support lengths 1..253, for internal
429 * server reasons. Also, there's no reason
430 * for bigger lengths to exist... admins
431 * won't be typing in a 32K vlan name.
433 if ((ptr[4] != 0) || (ptr[5] > 253)) {
434 librad_log("Packet contains attribute with invalid length %02x %02x", ptr[4], ptr[5]);
440 length -= (6 + attrlen);
444 packet->sockfd = sockfd;
448 * This is more than a bit of a hack.
450 packet->code = PW_AUTHENTICATION_REQUEST;
452 memcpy(&id, packet->data + 4, 4);
453 packet->id = ntohl(id);
456 * FIXME: Create a fake "request authenticator", to
457 * avoid duplicates? Or is the VQP sequence number
458 * adequate for this purpose?
465 * We do NOT mirror the old-style RADIUS code that does encode,
466 * sign && send in one function. For VQP, the caller MUST perform
467 * each task manually, and separately.
469 int vqp_send(RADIUS_PACKET *packet)
471 if (!packet || !packet->data || (packet->data_len < 8)) return -1;
474 * Don't print out the attributes, they were printed out
475 * when it was encoded.
479 * And send it on it's way.
481 return vqp_sendto(packet->sockfd, packet->data, packet->data_len, 0,
482 &packet->src_ipaddr, &packet->dst_ipaddr,
487 int vqp_decode(RADIUS_PACKET *packet)
490 int attribute, length;
491 VALUE_PAIR *vp, **tail;
493 if (!packet || !packet->data) return -1;
495 if (packet->data_len < VQP_HDR_LEN) return -1;
499 vp = paircreate(PW_VQP_PACKET_TYPE, PW_TYPE_OCTETS);
501 librad_log("No memory");
504 vp->lvalue = packet->data[1];
510 vp = paircreate(PW_VQP_ERROR_CODE, PW_TYPE_OCTETS);
512 librad_log("No memory");
515 vp->lvalue = packet->data[2];
521 vp = paircreate(PW_VQP_SEQUENCE_NUMBER, PW_TYPE_OCTETS);
523 librad_log("No memory");
526 vp->lvalue = packet->id; /* already set by vqp_recv */
532 ptr = packet->data + VQP_HDR_LEN;
533 end = packet->data + packet->data_len;
536 * Note that vqp_recv() MUST ensure that the packet is
537 * formatted in a way we expect, and that vqp_recv() MUST
538 * be called before vqp_decode().
541 attribute = (ptr[2] << 8) | ptr[3];
546 * Hack to get the dictionaries to work correctly.
549 vp = paircreate(attribute, PW_TYPE_OCTETS);
551 pairfree(&packet->vps);
553 librad_log("No memory");
560 memcpy(&vp->vp_ipaddr, ptr, 4);
564 vp->type = PW_TYPE_OCTETS;
570 memcpy(vp->vp_octets, ptr, length);
582 * FIXME: Map attributes to Calling-Station-Id, etc...
589 * These are the MUST HAVE contents for a VQP packet.
591 * We don't allow the caller to give less than these, because
592 * it won't work. We don't encode more than these, because the
593 * clients will ignore it.
595 * FIXME: Be more generous? Look for CISCO + VQP attributes?
597 static int contents[5][VQP_MAX_ATTRIBUTES] = {
598 { 0, 0, 0, 0, 0, 0 },
599 { 0x0c01, 0x0c02, 0x0c03, 0x0c04, 0x0c07, 0x0c05 }, /* Join request */
600 { 0x0c03, 0x0c08, 0, 0, 0, 0 }, /* Join Response */
601 { 0x0c01, 0x0c02, 0x0c03, 0x0c04, 0x0c07, 0x0c08 }, /* Reconfirm */
602 { 0x0c03, 0x0c08, 0, 0, 0, 0 }
605 int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
610 VALUE_PAIR *vps[VQP_MAX_ATTRIBUTES];
613 librad_log("Failed encoding VQP");
617 if (packet->data) return 0;
619 vp = pairfind(packet->vps, PW_VQP_PACKET_TYPE);
621 librad_log("Failed to find VQP-Packet-Type in response packet");
626 if ((code < 1) || (code > 4)) {
627 librad_log("Invalid value %d for VQP-Packet-Type", code);
631 length = VQP_HDR_LEN;
632 memset(vps, 0, sizeof(vps));
634 vp = pairfind(packet->vps, PW_VQP_ERROR_CODE);
637 * FIXME: Map attributes from calling-station-Id, etc.
639 * Maybe do this via rlm_vqp? That's probably the
640 * best place to add the code...
644 * No error: encode attributes.
646 if (!vp) for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
647 if (!contents[code][i]) break;
649 vps[i] = pairfind(packet->vps, contents[code][i] | 0x2000);
652 * FIXME: Print the name...
655 librad_log("Failed to find VQP attribute %02x",
661 length += vps[i]->length;
664 packet->data = malloc(length);
666 librad_log("No memory");
669 packet->data_len = length;
673 ptr[0] = VQP_VERSION;
679 ptr[2] = vp->lvalue & 0xff;
684 * The number of attributes is hard-coded.
686 if ((code == 1) || (code == 3)) {
689 ptr[3] = VQP_MAX_ATTRIBUTES;
691 sequence = htonl(packet->id);
692 memcpy(ptr + 4, &sequence, 4);
695 librad_log("Cannot send VQP response without request");
700 * Packet Sequence Number
702 memcpy(ptr + 4, original->data + 4, 4);
712 for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
719 * Type. Note that we look at only the lower 8
720 * bits, as the upper 8 bits have been hacked.
721 * See also dictionary.vqp
726 ptr[3] = vp->attribute & 0xff;
730 ptr[5] = vp->length & 0xff;
737 memcpy(ptr, &vp->vp_ipaddr, 4);
743 memcpy(ptr, vp->vp_octets, vp->length);