Added preliminary VMPS support. The code can receive packets
[freeradius.git] / src / lib / vqp.c
1 /*
2  * vqp.c        Functions to send/receive VQP packets.
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2007 Alan DeKok <aland@deployingradius.com>
21  */
22
23 #include        <freeradius-devel/ident.h>
24 RCSID("$Id$");
25
26 #include        <freeradius-devel/libradius.h>
27 #include        <freeradius-devel/vqp.h>
28
29 /*
30  *  http://www.openbsd.org/cgi-bin/cvsweb/src/usr.sbin/tcpdump/print-vqp.c
31  *
32  *  Some of how it works:
33  *
34  *  http://www.hackingciscoexposed.com/pdf/chapter12.pdf
35  *
36  * VLAN Query Protocol (VQP)
37  *
38  *    0                   1                   2                   3
39  *    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
40  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41  *   |    Version    |    Opcode     | Response Code |  Data Count   |
42  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43  *   |                         Transaction ID                        |
44  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45  *   |                            Type (1)                           |
46  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47  *   |             Length            |            Data               /
48  *   /                                                               /
49  *   /                                                               /
50  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
51  *   |                            Type (n)                           |
52  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53  *   |             Length            |            Data               /
54  *   /                                                               /
55  *   /                                                               /
56  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57  *
58  * VQP is layered over UDP.  The default destination port is 1589.
59  *
60  */
61 #define VQP_HDR_LEN (8)
62 #define VQP_VERSION (1)
63 #define VQP_MAX_ATTRIBUTES (12)
64
65
66 /*
67  *      Wrapper for sendto which handles sendfromto, IPv6, and all
68  *      possible combinations.
69  *
70  *      FIXME:  This is just a copy of rad_sendto().
71  *      Duplicate code is bad.
72  */
73 static int vqp_sendto(int sockfd, void *data, size_t data_len, int flags,
74                       lrad_ipaddr_t *src_ipaddr, lrad_ipaddr_t *dst_ipaddr,
75                       int dst_port)
76 {
77         struct sockaddr_storage dst;
78         socklen_t               sizeof_dst = sizeof(dst);
79
80 #ifdef WITH_UDPFROMTO
81         struct sockaddr_storage src;
82         socklen_t               sizeof_src = sizeof(src);
83
84         memset(&src, 0, sizeof(src));
85 #endif
86         memset(&dst, 0, sizeof(dst));
87
88         /*
89          *      IPv4 is supported.
90          */
91         if (dst_ipaddr->af == AF_INET) {
92                 struct sockaddr_in      *s4;
93
94                 s4 = (struct sockaddr_in *)&dst;
95                 sizeof_dst = sizeof(struct sockaddr_in);
96
97                 s4->sin_family = AF_INET;
98                 s4->sin_addr = dst_ipaddr->ipaddr.ip4addr;
99                 s4->sin_port = htons(dst_port);
100
101 #ifdef WITH_UDPFROMTO
102                 s4 = (struct sockaddr_in *)&src;
103                 sizeof_src = sizeof(struct sockaddr_in);
104
105                 s4->sin_family = AF_INET;
106                 s4->sin_addr = src_ipaddr->ipaddr.ip4addr;
107 #endif
108
109         /*
110          *      IPv6 MAY be supported.
111          */
112 #ifdef HAVE_STRUCT_SOCKADDR_IN6
113         } else if (dst_ipaddr->af == AF_INET6) {
114                 struct sockaddr_in6     *s6;
115
116                 s6 = (struct sockaddr_in6 *)&dst;
117                 sizeof_dst = sizeof(struct sockaddr_in6);
118                 
119                 s6->sin6_family = AF_INET6;
120                 s6->sin6_addr = dst_ipaddr->ipaddr.ip6addr;
121                 s6->sin6_port = htons(dst_port);
122
123 #ifdef WITH_UDPFROMTO
124                 return -1;      /* UDPFROMTO && IPv6 are not supported */
125 #if 0
126                 s6 = (struct sockaddr_in6 *)&src;
127                 sizeof_src = sizeof(struct sockaddr_in6);
128
129                 s6->sin6_family = AF_INET6;
130                 s6->sin6_addr = src_ipaddr->ipaddr.ip6addr;
131 #endif /* #if 0 */
132 #endif /* WITH_UDPFROMTO */
133 #endif /* HAVE_STRUCT_SOCKADDR_IN6 */
134         } else return -1;   /* Unknown address family, Die Die Die! */
135
136 #ifdef WITH_UDPFROMTO
137         /*
138          *      Only IPv4 is supported for udpfromto.
139          *
140          *      And if they don't specify a source IP address, don't
141          *      use udpfromto.
142          */
143         if ((dst_ipaddr->af == AF_INET) ||
144             (src_ipaddr->af != AF_UNSPEC)) {
145                 return sendfromto(sockfd, data, data_len, flags,
146                                   (struct sockaddr *)&src, sizeof_src, 
147                                   (struct sockaddr *)&dst, sizeof_dst);
148         }
149 #else
150         src_ipaddr = src_ipaddr; /* -Wunused */
151 #endif
152
153         /*
154          *      No udpfromto, OR an IPv6 socket, fail gracefully.
155          */
156         return sendto(sockfd, data, data_len, flags, 
157                       (struct sockaddr *)&dst, sizeof_dst);
158 }
159
160 /*
161  *      Wrapper for recvfrom, which handles recvfromto, IPv6, and all
162  *      possible combinations.
163  *
164  *      FIXME:  This is copied from rad_recvfrom, with minor edits.
165  */
166 static ssize_t vqp_recvfrom(int sockfd, uint8_t **pbuf, int flags,
167                             lrad_ipaddr_t *src_ipaddr, uint16_t *src_port,
168                             lrad_ipaddr_t *dst_ipaddr, uint16_t *dst_port)
169 {
170         struct sockaddr_storage src;
171         struct sockaddr_storage dst;
172         socklen_t               sizeof_src = sizeof(src);
173         socklen_t               sizeof_dst = sizeof(dst);
174         ssize_t                 data_len;
175         uint8_t                 header[4];
176         void                    *buf;
177         size_t                  len;
178
179         memset(&src, 0, sizeof_src);
180         memset(&dst, 0, sizeof_dst);
181
182         /*
183          *      Get address family, etc. first, so we know if we
184          *      need to do udpfromto.
185          *
186          *      FIXME: udpfromto also does this, but it's not
187          *      a critical problem.
188          */
189         if (getsockname(sockfd, (struct sockaddr *)&dst,
190                         &sizeof_dst) < 0) return -1;
191
192         /*
193          *      Read the length of the packet, from the packet.
194          *      This lets us allocate the buffer to use for
195          *      reading the rest of the packet.
196          */
197         data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK,
198                             (struct sockaddr *)&src, &sizeof_src);
199         if (data_len < 0) return -1;
200
201         /*
202          *      Too little data is available, discard the packet.
203          */
204         if (data_len < 4) {
205                 recvfrom(sockfd, header, sizeof(header), flags, 
206                          (struct sockaddr *)&src, &sizeof_src);
207                 return 0;
208
209                 /*
210                  *      Invalid version, packet type, or too many
211                  *      attributes.  Die.
212                  */
213         } else if ((header[0] != VQP_VERSION) ||
214                    (header[1] < 1) ||
215                    (header[1] > 4) ||
216                    (header[3] > VQP_MAX_ATTRIBUTES)) {
217                 recvfrom(sockfd, header, sizeof(header), flags,
218                          (struct sockaddr *)&src, &sizeof_src);
219                 return 0;
220
221         } else {                /* we got 4 bytes of data. */
222                 /*
223                  *      We don't care about the contents for now...
224                  */
225 #if 0
226                 /*
227                  *      How many attributes are in the packet.
228                  */
229                 len = header[3];
230
231                 if ((header[1] == 1) || (header[1] == 3)) {
232                         if (len != VQP_MAX_ATTRIBUTES) {
233                                 recvfrom(sockfd, header, sizeof(header), 0,
234                                          (struct sockaddr *)&src, &sizeof_src);
235                                 return 0;
236                         }
237                         /*
238                          *      Maximum length we support.
239                          */
240                         len = (12 * (4 + 4 + 253));
241
242                 } else {
243                         if (len != 2) {
244                                 recvfrom(sockfd, header, sizeof(header), 0, 
245                                  (struct sockaddr *)&src, &sizeof_src);
246                                 return 0;
247                         }
248                         /*
249                          *      Maximum length we support.
250                          */
251                         len = (12 * (4 + 4 + 253));
252                 }
253 #endif
254         }
255
256         /*
257          *      For now, be generous.
258          */
259         len = (12 * (4 + 4 + 253));
260
261         buf = malloc(len);
262         if (!buf) return -1;
263
264         /*
265          *      Receive the packet.  The OS will discard any data in the
266          *      packet after "len" bytes.
267          */
268 #ifdef WITH_UDPFROMTO
269         if (dst.ss_family == AF_INET) {
270                 data_len = recvfromto(sockfd, buf, len, flags,
271                                       (struct sockaddr *)&src, &sizeof_src, 
272                                       (struct sockaddr *)&dst, &sizeof_dst);
273         } else
274 #endif
275                 /*
276                  *      No udpfromto, OR an IPv6 socket.  Fail gracefully.
277                  */
278                 data_len = recvfrom(sockfd, buf, len, flags, 
279                                     (struct sockaddr *)&src, &sizeof_src);
280         if (data_len < 0) {
281                 free(buf);
282                 return data_len;
283         }
284
285         /*
286          *      Check address families, and update src/dst ports, etc.
287          */
288         if (src.ss_family == AF_INET) {
289                 struct sockaddr_in      *s4;
290
291                 s4 = (struct sockaddr_in *)&src;
292                 src_ipaddr->af = AF_INET;
293                 src_ipaddr->ipaddr.ip4addr = s4->sin_addr;
294                 *src_port = ntohs(s4->sin_port);
295
296                 s4 = (struct sockaddr_in *)&dst;
297                 dst_ipaddr->af = AF_INET;
298                 dst_ipaddr->ipaddr.ip4addr = s4->sin_addr;
299                 *dst_port = ntohs(s4->sin_port);
300
301 #ifdef HAVE_STRUCT_SOCKADDR_IN6
302         } else if (src.ss_family == AF_INET6) {
303                 struct sockaddr_in6     *s6;
304
305                 s6 = (struct sockaddr_in6 *)&src;
306                 src_ipaddr->af = AF_INET6;
307                 src_ipaddr->ipaddr.ip6addr = s6->sin6_addr;
308                 *src_port = ntohs(s6->sin6_port);
309
310                 s6 = (struct sockaddr_in6 *)&dst;
311                 dst_ipaddr->af = AF_INET6;
312                 dst_ipaddr->ipaddr.ip6addr = s6->sin6_addr;
313                 *dst_port = ntohs(s6->sin6_port);
314 #endif
315         } else {
316                 free(buf);
317                 return -1;      /* Unknown address family, Die Die Die! */
318         }
319         
320         /*
321          *      Different address families should never happen.
322          */
323         if (src.ss_family != dst.ss_family) {
324                 free(buf);
325                 return -1;
326         }
327
328         /*
329          *      Tell the caller about the data
330          */
331         *pbuf = buf;
332
333         return data_len;
334 }
335
336 RADIUS_PACKET *vqp_recv(int sockfd)
337 {
338         uint8_t *ptr;
339         ssize_t length;
340         uint32_t id;
341         RADIUS_PACKET *packet;
342
343         /*
344          *      Allocate the new request data structure
345          */
346         if ((packet = malloc(sizeof(*packet))) == NULL) {
347                 librad_log("out of memory");
348                 return NULL;
349         }
350         memset(packet, 0, sizeof(*packet));
351
352         packet->data_len = vqp_recvfrom(sockfd, &packet->data, 0,
353                                         &packet->src_ipaddr, &packet->src_port,
354                                         &packet->dst_ipaddr, &packet->dst_port);
355
356         /*
357          *      Check for socket errors.
358          */
359         if (packet->data_len < 0) {
360                 librad_log("Error receiving packet: %s", strerror(errno));
361                 /* packet->data is NULL */
362                 free(packet);
363                 return NULL;
364         }
365
366
367         /*
368          *      We can only receive packets formatted in a way we
369          *      expect.  However, we accept MORE attributes in a
370          *      packet than normal implementations may send.
371          */
372         if (packet->data_len < VQP_HDR_LEN) {
373                 librad_log("VQP packet is too short");
374                 rad_free(&packet);
375                 return NULL;
376         }
377
378         ptr = packet->data;
379
380         if (0) {
381                 int i;
382                 for (i = 0; i < packet->data_len; i++) {
383                         if ((i & 0x0f) == 0) fprintf(stderr, "%02x: ", i);
384                         fprintf(stderr, "%02x ", ptr[i]);
385                         if ((i & 0x0f) == 0x0f) fprintf(stderr, "\n");
386                 }
387           
388         }
389
390         if (ptr[3] > VQP_MAX_ATTRIBUTES) {
391                 librad_log("Too many VQP attributes");
392                 rad_free(&packet);
393                 return NULL;
394         }
395
396         if (packet->data_len > VQP_HDR_LEN) {
397                 int attrlen;
398
399                 /*
400                  *      Skip the header.
401                  */
402                 ptr += VQP_HDR_LEN;
403                 length = packet->data_len - VQP_HDR_LEN;
404
405                 while (length > 0) {
406                         if (length < 7) {
407                                 librad_log("Packet contains malformed attribute");
408                                 rad_free(&packet);
409                                 return NULL;
410                         }
411
412                         /*
413                          *      Attributes are 4 bytes
414                          *      0x00000c01 ... 0x00000c08
415                          */
416                         if ((ptr[0] != 0) || (ptr[1] != 0) ||
417                             (ptr[2] != 0x0c) || (ptr[3] < 1) || (ptr[3] > 8)) {
418                                 librad_log("Packet contains invalid attribute");
419                                 rad_free(&packet);
420                                 return NULL;
421                         }
422                         
423                         /*
424                          *      Length is 2 bytes
425                          *
426                          *      We support lengths 1..253, for internal
427                          *      server reasons.  Also, there's no reason
428                          *      for bigger lengths to exist... admins
429                          *      won't be typing in a 32K vlan name.
430                          */
431                         if ((ptr[4] != 0) || (ptr[5] > 253)) {
432                                 librad_log("Packet contains attribute with invalid length %02x %02x", ptr[4], ptr[5]);
433                                 rad_free(&packet);
434                                 return NULL;
435                         }
436                         attrlen = ptr[5];
437                         ptr += 6 + attrlen;
438                         length -= (6 + attrlen);
439                 }
440         }
441
442         packet->sockfd = sockfd;
443         packet->vps = NULL;
444
445         /*
446          *      This is more than a bit of a hack.
447          */
448         packet->code = PW_AUTHENTICATION_REQUEST;
449
450         memcpy(&id, packet->data + 4, 4);
451         packet->id = ntohl(id);
452
453         return packet;
454 }
455
456 /*
457  *      We do NOT  mirror the old-style RADIUS code  that does encode,
458  *      sign && send in one function.  For VQP, the caller MUST perform
459  *      each task manually, and separately.
460  */
461 int vqp_send(RADIUS_PACKET *packet)
462 {
463         if (!packet || !packet->data || (packet->data_len < 8)) return -1;
464
465         /*
466          *      Don't print out the attributes, they were printed out
467          *      when it was encoded.
468          */
469
470         /*
471          *      And send it on it's way.
472          */
473         return vqp_sendto(packet->sockfd, packet->data, packet->data_len, 0,
474                           &packet->src_ipaddr, &packet->dst_ipaddr,
475                           packet->dst_port);
476 }
477
478
479 int vqp_decode(RADIUS_PACKET *packet)
480 {
481         uint8_t *ptr, *end;
482         int attribute, length;
483         VALUE_PAIR *vp, **tail;
484
485         if (!packet || !packet->data) return -1;
486
487         if (packet->data_len < VQP_HDR_LEN) return -1;
488
489         tail = &packet->vps;
490
491         vp = paircreate(PW_VQP_PACKET_TYPE, PW_TYPE_OCTETS);
492         if (!vp) {
493                 librad_log("No memory");
494                 return -1;
495         }
496         vp->lvalue = packet->data[1];
497         debug_pair(vp);
498
499         *tail = vp;
500         tail = &(vp->next);
501
502         vp = paircreate(PW_VQP_ERROR_CODE, PW_TYPE_OCTETS);
503         if (!vp) {
504                 librad_log("No memory");
505                 return -1;
506         }
507         vp->lvalue = packet->data[2];
508         debug_pair(vp);
509
510         *tail = vp;
511         tail = &(vp->next);
512
513         vp = paircreate(PW_VQP_SEQUENCE_NUMBER, PW_TYPE_OCTETS);
514         if (!vp) {
515                 librad_log("No memory");
516                 return -1;
517         }
518         vp->lvalue = packet->id; /* already set by vqp_recv */
519         debug_pair(vp);
520
521         *tail = vp;
522         tail = &(vp->next);
523
524         ptr = packet->data + VQP_HDR_LEN;
525         end = packet->data + packet->data_len;
526
527         /*
528          *      Note that vqp_recv() MUST ensure that the packet is
529          *      formatted in a way we expect, and that vqp_recv() MUST
530          *      be called before vqp_decode().
531          */
532         while (ptr < end) {
533                 attribute = (ptr[2] << 8) | ptr[3];
534                 length = ptr[5];
535                 ptr += 6;
536
537                 /*
538                  *      Hack to get the dictionaries to work correctly.
539                  */
540                 attribute |= 0x2000;
541                 vp = paircreate(attribute, PW_TYPE_OCTETS);
542                 if (!vp) {
543                         pairfree(&packet->vps);
544
545                         librad_log("No memory");
546                         return -1;
547                 }
548
549                 switch (vp->type) {
550                 case PW_TYPE_IPADDR:
551                         if (length == 4) {
552                                 memcpy(&vp->vp_ipaddr, ptr, 4);
553                                 vp->length = 4;
554                                 break;
555                         }
556                         vp->type = PW_TYPE_OCTETS;
557                         /* FALL-THROUGH */
558
559                 default:
560                 case PW_TYPE_STRING:
561                 case PW_TYPE_OCTETS:
562                         memcpy(vp->vp_octets, ptr, length);
563                         vp->length = length;
564                         break;
565                 }
566                 ptr += length;
567                 debug_pair(vp);
568
569                 *tail = vp;
570                 tail = &(vp->next);
571         }
572
573         /*
574          *      FIXME: Map attributes to Calling-Station-Id, etc...
575          */
576
577         return 0;
578 }
579
580 /*
581  *      These are the MUST HAVE contents for a VQP packet.
582  *
583  *      We don't allow the caller to give less than these, because
584  *      it won't work.  We don't encode more than these, because the
585  *      clients will ignore it.
586  *
587  *      FIXME: Be more generous?  Look for CISCO + VQP attributes?
588  */
589 static int contents[5][VQP_MAX_ATTRIBUTES] = {
590         { 0,      0,      0,      0,      0,      0 },
591         { 0x0c01, 0x0c02, 0x0c03, 0x0c04, 0x0c07, 0x0c05 }, /* Join request */
592         { 0x0c03, 0x0c08, 0,      0,      0,      0 },  /* Join Response */
593         { 0x0c01, 0x0c02, 0x0c03, 0x0c04, 0x0c07, 0x0c08 }, /* Reconfirm */
594         { 0x0c03, 0x0c08, 0,      0,      0,      0 }
595 };
596
597 int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
598 {
599         int i, code, length;
600         VALUE_PAIR *vp;
601         uint8_t *ptr;
602         VALUE_PAIR      *vps[VQP_MAX_ATTRIBUTES];
603
604         if (!packet) {
605                 librad_log("Failed encoding VQP");
606                 return -1;
607         }
608
609         if (packet->data) return 0;
610
611         vp = pairfind(packet->vps, PW_VQP_PACKET_TYPE);
612         if (!vp) {
613                 librad_log("Failed to find VQP-Packet-Type in response packet");
614                 return -1;
615         }
616
617         code = vp->lvalue;
618         if ((code < 1) || (code > 4)) {
619                 librad_log("Invalid value %d for VQP-Packet-Type", code);
620                 return -1;
621         }
622
623         length = VQP_HDR_LEN;
624         memset(vps, 0, sizeof(vps));
625
626         vp = pairfind(packet->vps, PW_VQP_ERROR_CODE);
627
628         /*
629          *      FIXME: Map attributes from calling-station-Id, etc.
630          *
631          *      Maybe do this via rlm_vqp?  That's probably the
632          *      best place to add the code...
633          */
634
635         /*
636          *      No error: encode attributes.
637          */
638         if (!vp) for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
639                 if (!contents[code][i]) break;
640
641                 vps[i] = pairfind(packet->vps, contents[code][i] | 0x2000);
642
643                 /*
644                  *      FIXME: Print the name...
645                  */
646                 if (!vps[i]) {
647                         librad_log("Failed to find VQP attribute %02x",
648                                    contents[code][i]);
649                         return -1;
650                 }
651
652                 length += 6;
653                 length += vps[i]->length;
654         }
655
656         packet->data = malloc(length);
657         if (!packet->data) {
658                 librad_log("No memory");
659                 return -1;
660         }
661         packet->data_len = length;
662
663         ptr = packet->data;
664
665         ptr[0] = VQP_VERSION;
666         ptr[1] = code;
667
668         if (!vp) {
669                 ptr[2] = 0;
670         } else {
671                 ptr[2] = vp->lvalue & 0xff;
672                 return 0;
673         }
674
675         /*
676          *      The number of attributes is hard-coded.
677          */
678         if ((code == 1) || (code == 3)) {
679                 uint32_t sequence;
680
681                 ptr[3] = VQP_MAX_ATTRIBUTES;
682
683                 sequence = htonl(packet->id);
684                 memcpy(ptr + 4, &sequence, 4);
685         } else {
686                 if (!original) {
687                         librad_log("Cannot send VQP response without request");
688                         return -1;
689                 }
690
691                 /*
692                  *      Packet Sequence Number
693                  */
694                 memcpy(ptr + 4, original->data + 4, 4);
695
696                 ptr[3] = 2;
697         }
698
699         ptr += 8;
700
701         /*
702          *      Encode the VP's.
703          */
704         for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
705                 if (!vps[i]) break;
706                 vp = vps[i];
707
708                 debug_pair(vp);
709
710                 /*
711                  *      Type.  Note that we look at only the lower 8
712                  *      bits, as the upper 8 bits have been hacked.
713                  *      See also dictionary.vqp
714                  */
715                 ptr[0] = 0;
716                 ptr[1] = 0;
717                 ptr[2] = 0x0c;
718                 ptr[3] = vp->attribute & 0xff;
719
720                 /* Length */
721                 ptr[4] = 0;
722                 ptr[5] = vp->length & 0xff;
723
724                 ptr += 6;
725
726                 /* Data */
727                 switch (vp->type) {
728                 case PW_TYPE_IPADDR:
729                         memcpy(ptr, &vp->vp_ipaddr, 4);
730                         break;
731
732                 default:
733                 case PW_TYPE_OCTETS:
734                 case PW_TYPE_STRING:
735                         memcpy(ptr, vp->vp_octets, vp->length);
736                         break;
737                 }
738                 ptr += vp->length;
739         }
740
741         return 0;
742 }