Added udpfromto support for sending, too
[freeradius.git] / src / lib / dhcp.c
1 /*
2  * dhcp.c       Functions to send/receive dhcp packets.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program 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
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2008 The FreeRADIUS server project
21  * Copyright 2008 Alan DeKok <aland@deployingradius.com>
22  */
23
24 #include        <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/libradius.h>
28 #include <freeradius-devel/udpfromto.h>
29 #include <freeradius-devel/dhcp.h>
30
31 #ifdef WITH_DHCP
32 #define DHCP_CHADDR_LEN (16)
33 #define DHCP_SNAME_LEN  (64)
34 #define DHCP_FILE_LEN   (128)
35 #define DHCP_VEND_LEN   (308)
36 #define DHCP_OPTION_MAGIC_NUMBER (0x63825363)
37
38 #ifndef INADDR_BROADCAST
39 #define INADDR_BROADCAST INADDR_NONE
40 #endif
41
42 typedef struct dhcp_packet_t {
43         uint8_t         opcode;
44         uint8_t         htype;
45         uint8_t         hlen;
46         uint8_t         hops;
47         uint32_t        xid;    /* 4 */
48         uint16_t        secs;   /* 8 */
49         uint16_t        flags;
50         uint32_t        ciaddr; /* 12 */
51         uint32_t        yiaddr; /* 16 */
52         uint32_t        siaddr; /* 20 */
53         uint32_t        giaddr; /* 24 */
54         uint8_t         chaddr[DHCP_CHADDR_LEN]; /* 28 */
55         char            sname[DHCP_SNAME_LEN]; /* 44 */
56         char            file[DHCP_FILE_LEN]; /* 108 */
57         uint32_t        option_format; /* 236 */
58         uint8_t         options[DHCP_VEND_LEN];
59 } dhcp_packet_t;
60
61 /*
62  *      INADDR_ANY : 68 -> INADDR_BROADCAST : 67        DISCOVER
63  *      INADDR_BROADCAST : 68 <- SERVER_IP : 67         OFFER
64  *      INADDR_ANY : 68 -> INADDR_BROADCAST : 67        REQUEST
65  *      INADDR_BROADCAST : 68 <- SERVER_IP : 67         ACK
66  */
67 static const char *dhcp_header_names[] = {
68         "DHCP-Opcode",
69         "DHCP-Hardware-Type",
70         "DHCP-Hardware-Address-Length",
71         "DHCP-Hop-Count",
72         "DHCP-Transaction-Id",
73         "DHCP-Number-of-Seconds",
74         "DHCP-Flags",
75         "DHCP-Client-IP-Address",
76         "DHCP-Your-IP-Address",
77         "DHCP-Server-IP-Address",
78         "DHCP-Gateway-IP-Address",
79         "DHCP-Client-Hardware-Address",
80         "DHCP-Server-Host-Name",
81         "DHCP-Boot-Filename",
82
83         NULL
84 };
85
86 static const char *dhcp_message_types[] = {
87         "invalid",
88         "DHCP-Discover",
89         "DHCP-Offer",
90         "DHCP-Request",
91         "DHCP-Decline",
92         "DHCP-Ack",
93         "DHCP-NAK",
94         "DHCP-Release",
95         "DHCP-Inform",
96         "DHCP-Force-Renew",
97 };
98
99 static int dhcp_header_sizes[] = {
100         1, 1, 1, 1,
101         4, 2, 2, 4,
102         4, 4, 4,
103         DHCP_CHADDR_LEN,
104         DHCP_SNAME_LEN,
105         DHCP_FILE_LEN
106 };
107
108
109 /*
110  *      Some clients silently ignore responses less than 300 bytes.
111  */
112 #define MIN_PACKET_SIZE (244)
113 #define DEFAULT_PACKET_SIZE (300)
114 #define MAX_PACKET_SIZE (1500 - 40)
115
116 static int getcode(const uint8_t *data, size_t data_len, unsigned int *code)
117 {
118         const uint8_t *end, *p;
119
120         end = data + data_len;
121
122         while (p < end) {
123                 /*
124                  *      End of packet or end of options
125                  */
126                 if ((p[0] == 0) || (p[0] == 255)) return 0;
127
128                 /*
129                  *      Not enough room for 0x3501cc
130                  */
131                 if ((end - p) < 3) return 0; /* t l v */
132
133                 /*
134                  *      Option is larger than the packet.
135                  */
136                 if ((p + p[1] + 2) > end) return 0;
137
138                 /*
139                  *      Found it.  Ensure it's well formed.
140                  */
141                 if (p[0] == 53) {
142                         if ((p[1] != 1) || (p[2] == 0) || (p[2] > 8)) {
143                                 return 0;
144                         }
145                         *code = p[2];
146                         return 1;
147                 }
148
149                 p += 2 + p[1];
150         }
151
152         return 0;
153 }
154
155 /*
156  *      DHCPv4 is only for IPv4.  Broadcast only works if udpfromto is
157  *      defined.
158  */
159 RADIUS_PACKET *fr_dhcp_recv(int sockfd)
160 {
161         uint32_t                magic;
162         struct sockaddr_storage src;
163         struct sockaddr_storage dst;
164         socklen_t               sizeof_src;
165         socklen_t               sizeof_dst;
166         RADIUS_PACKET           *packet;
167         int port;
168
169         packet = rad_alloc(0);
170         if (!packet) return NULL;
171         memset(packet, 0, sizeof(packet));
172
173         packet->data = malloc(MAX_PACKET_SIZE);
174         if (!packet->data) {
175                 rad_free(&packet);
176                 return NULL;
177         }
178
179         packet->sockfd = sockfd;
180         sizeof_src = sizeof(src);
181 #ifdef WITH_UDPFROMTO
182         sizeof_dst = sizeof(dst);
183         packet->data_len = recvfromto(sockfd, packet->data, MAX_PACKET_SIZE, 0,
184                                       (struct sockaddr *)&src, &sizeof_src,
185                                       (struct sockaddr *)&dst, &sizeof_dst);
186 #else
187         packet->data_len = recvfrom(sockfd, packet->data, MAX_PACKET_SIZE, 0,
188                                     (struct sockaddr *)&src, &sizeof_src);
189 #endif
190
191         if (packet->data_len <= 0) {
192                 fprintf(stderr, "Failed reading DHCP socket: %s", strerror(errno));
193                 rad_free(&packet);
194                 return NULL;
195         }
196
197         if (packet->data_len < MIN_PACKET_SIZE) {
198                 fprintf(stderr, "DHCP packet is too small (%d < %d)",
199                       packet->data_len, MIN_PACKET_SIZE);
200                 rad_free(&packet);
201                 return NULL;
202         }
203
204         if (packet->data[0] != 1) {
205                 fprintf(stderr, "Cannot receive DHCP server messages");
206                 rad_free(&packet);
207                 return NULL;
208         }
209
210         if (packet->data[1] != 1) {
211                 fprintf(stderr, "DHCP can only receive ethernet requests, not type %02x",
212                       packet->data[1]);
213                 rad_free(&packet);
214                 return NULL;
215         }
216
217         if (packet->data[2] != 6) {
218                 fprintf(stderr, "Ethernet HW length is wrong length %d\n",
219                         packet->data[2]);
220                 rad_free(&packet);
221                 return NULL;
222         }
223
224         memcpy(&magic, packet->data + 236, 4);
225         magic = ntohl(magic);
226         if (magic != DHCP_OPTION_MAGIC_NUMBER) {
227                 fprintf(stderr, "Cannot do BOOTP\n");
228                 rad_free(&packet);
229                 return NULL;
230         }
231
232         /*
233          *      Create unique keys for the packet.
234          */
235         memcpy(&magic, packet->data + 4, 4);
236         packet->id = ntohl(magic);
237
238         /*
239          *      Check that it's a known packet type.
240          */
241         if ((packet->data[240] != 53) ||
242             (packet->data[241] != 1) ||
243             (packet->data[242] == 0) ||
244             (packet->data[242] > 8)) {
245                 /*
246                  *      Some clients send the packet type buried
247                  *      inside of the packet...
248                  */
249                 if (!getcode(packet->data + 240, packet->data_len - 240,
250                              &packet->code)) {
251                         fprintf(stderr, "Unknown, or badly formatted DHCP packet\n");
252                         rad_free(&packet);
253                         return NULL;
254                 }
255         } else {
256                 packet->code = packet->data[242];
257         }
258         packet->code |= PW_DHCP_OFFSET;
259
260         /*
261          *      Create a unique vector from the MAC address and the
262          *      DHCP opcode.  This is a hack for the RADIUS
263          *      infrastructure in the rest of the server.
264          *
265          *      Note: packet->data[2] == 6, which is smaller than
266          *      sizeof(packet->vector)
267          *
268          *      FIXME:  Look for client-identifier in packet,
269          *      and use that, too?
270          */
271         memset(packet->vector, 0, sizeof(packet->vector));
272         memcpy(packet->vector, packet->data + 28, packet->data[2]);
273         packet->vector[packet->data[2]] = packet->code & 0xff;
274
275         /*
276          *      FIXME: for DISCOVER / REQUEST: src_port == dst_port + 1
277          *      FIXME: for OFFER / ACK       : src_port = dst_port - 1
278          */
279
280         /*
281          *      Unique keys are xid, client mac, and client ID?
282          */
283
284         /*
285          *      FIXME: More checks, like DHCP packet type?
286          */
287
288         sizeof_dst = sizeof(dst);
289
290 #ifndef WITH_UDPFROMTO
291         /*
292          *      This should never fail...
293          */
294         getsockname(sockfd, (struct sockaddr *) &dst, &sizeof_dst);
295 #endif
296         
297         fr_sockaddr2ipaddr(&dst, sizeof_dst, &packet->dst_ipaddr, &port);
298         packet->dst_port = port;
299
300         fr_sockaddr2ipaddr(&src, sizeof_src, &packet->src_ipaddr, &port);
301         packet->src_port = port;
302
303         if (fr_debug_flag > 1) {
304                 char type_buf[64];
305                 const char *name = type_buf;
306                 char src_ip_buf[256], dst_ip_buf[256];
307                 
308                 if ((packet->code >= PW_DHCP_DISCOVER) &&
309                     (packet->code <= PW_DHCP_INFORM)) {
310                         name = dhcp_message_types[packet->code - PW_DHCP_OFFSET];
311                 } else {
312                         snprintf(type_buf, sizeof(type_buf), "%d",
313                                  packet->code - PW_DHCP_OFFSET);
314                 }
315
316                 printf("Received %s of id %08x from %s:%d to %s:%d\n",
317                        name, (unsigned int) packet->id,
318                        inet_ntop(packet->src_ipaddr.af,
319                                  &packet->src_ipaddr.ipaddr,
320                                  src_ip_buf, sizeof(src_ip_buf)),
321                        packet->src_port,
322                        inet_ntop(packet->dst_ipaddr.af,
323                                  &packet->dst_ipaddr.ipaddr,
324                                  dst_ip_buf, sizeof(dst_ip_buf)),
325                        packet->dst_port);
326                 fflush(stdout);
327         }
328
329         return packet;
330 }
331
332
333 /*
334  *      Send a DHCP packet.
335  */
336 int fr_dhcp_send(RADIUS_PACKET *packet)
337 {
338         struct sockaddr_storage dst;
339         socklen_t               sizeof_dst;
340 #ifdef WITH_UDPFROMTO
341         struct sockaddr_storage src;
342         socklen_t               sizeof_src;
343 #endif
344
345         fr_ipaddr2sockaddr(&packet->dst_ipaddr, packet->dst_port,
346                            &dst, &sizeof_dst);
347
348 #ifndef WITH_UDPFROMTO
349         /*
350          *      Assume that the packet is encoded before sending it.
351          */
352         return sendto(packet->sockfd, packet->data, packet->data_len, 0,
353                       (struct sockaddr *)&dst, sizeof_dst);
354 #else
355         fr_ipaddr2sockaddr(&packet->src_ipaddr, packet->src_port,
356                            &src, &sizeof_src);
357
358         return sendfromto(packet->sockfd,
359                           packet->data, packet->data_len, 0,
360                           (struct sockaddr *)&src, sizeof_src,
361                           (struct sockaddr *)&dst, sizeof_dst);
362 #endif
363 }
364
365
366 int fr_dhcp_decode(RADIUS_PACKET *packet)
367 {
368         int i;
369         ssize_t total;
370         uint8_t *p;
371         uint32_t giaddr;
372         VALUE_PAIR *head, *vp, **tail;
373         VALUE_PAIR *maxms, *mtu;
374         char buffer[2048];
375
376         head = NULL;
377         tail = &head;
378         p = packet->data;
379         
380         if ((fr_debug_flag > 2) && fr_log_fp) {
381                 for (i = 0; i < packet->data_len; i++) {
382                         if ((i & 0x0f) == 0x00) fprintf(stderr, "%d: ", i);
383                         fprintf(fr_log_fp, "%02x ", packet->data[i]);
384                         if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n");
385                 }
386                 fprintf(fr_log_fp, "\n");
387         }
388
389         if (packet->data[1] != 1) {
390                 fprintf(stderr, "Packet is not Ethernet: %u\n",
391                       packet->data[1]);
392                 return -1;
393         }
394
395         /*
396          *      Decode the header.
397          */
398         for (i = 0; i < 14; i++) {
399                 vp = pairmake(dhcp_header_names[i], NULL, T_OP_EQ);
400                 if (!vp) {
401                         fprintf(stderr, "Parse error %s\n", fr_strerror());
402                         pairfree(&head);
403                         return -1;
404                 }
405
406
407                 if ((i == 11) && 
408                     (packet->data[1] == 1) &&
409                     (packet->data[2] == 6)) {
410                         vp->type = PW_TYPE_ETHERNET;
411                 }
412
413                 switch (vp->type) {
414                 case PW_TYPE_BYTE:
415                         vp->vp_integer = p[0];
416                         vp->length = 1;
417                         break;
418                         
419                 case PW_TYPE_SHORT:
420                         vp->vp_integer = (p[0] << 8) | p[1];
421                         vp->length = 2;
422                         break;
423                         
424                 case PW_TYPE_INTEGER:
425                         memcpy(&vp->vp_integer, p, 4);
426                         vp->vp_integer = ntohl(vp->vp_integer);
427                         vp->length = 4;
428                         break;
429                         
430                 case PW_TYPE_IPADDR:
431                         memcpy(&vp->vp_ipaddr, p, 4);
432                         vp->length = 4;
433                         break;
434                         
435                 case PW_TYPE_STRING:
436                         memcpy(vp->vp_strvalue, p, dhcp_header_sizes[i]);
437                         vp->vp_strvalue[dhcp_header_sizes[i]] = '\0';
438                         vp->length = strlen(vp->vp_strvalue);
439                         if (vp->length == 0) {
440                                 pairfree(&vp);
441                         }
442                         break;
443                         
444                 case PW_TYPE_OCTETS:
445                         memcpy(vp->vp_octets, p, packet->data[2]);
446                         vp->length = packet->data[2];
447                         break;
448                         
449                 case PW_TYPE_ETHERNET:
450                         memcpy(vp->vp_ether, p, sizeof(vp->vp_ether));
451                         vp->length = sizeof(vp->vp_ether);
452                         break;
453                         
454                 default:
455                         fprintf(stderr, "BAD TYPE %d\n", vp->type);
456                         pairfree(&vp);
457                         break;
458                 }
459                 p += dhcp_header_sizes[i];
460
461                 if (!vp) continue;
462                 
463                 if (fr_debug_flag > 1) {
464                         vp_prints(buffer, sizeof(buffer), vp);
465                         fprintf(stderr, "\t%s\n", buffer);
466                 }
467                 *tail = vp;
468                 tail = &vp->next;
469         }
470         
471         /*
472          *      Loop over the options.
473          */
474         p = packet->data + 240;
475         total = packet->data_len - 240;
476
477         while (total > 0) {
478                 int num_entries, alen;
479                 DICT_ATTR *da;
480
481                 if (*p == 0) break;
482                 if (*p == 255) break; /* end of options signifier */
483
484                 if (p[1] >= 253) {
485                         fprintf(stderr, "Attribute too long %u %u\n",
486                               p[0], p[1]);
487                         goto do_next;
488                 }
489                                 
490                 da = dict_attrbyvalue(DHCP2ATTR(p[0]));
491                 if (!da) {
492                         fprintf(stderr, "Attribute not in our dictionary: %u\n",
493                               p[0]);
494                 do_next:
495                         total -= 2;
496                         total -= p[1];
497                         p += p[1];
498                         p += 2;
499                         continue;
500                 }
501
502                 vp = NULL;
503                 num_entries = 1;
504                 alen = p[1];
505                 p += 2;
506
507                 if (da->flags.array) {
508                         switch (da->type) {
509                         case PW_TYPE_BYTE:
510                                 num_entries = alen;
511                                 alen = 1;
512                                 break;
513
514                         case PW_TYPE_SHORT:
515                                 if ((alen & 0x01) != 0) goto raw;
516                                 num_entries = alen / 2;
517                                 alen = 2;
518                                 break;
519
520                         case PW_TYPE_IPADDR:
521                         case PW_TYPE_INTEGER:
522                         case PW_TYPE_DATE:
523                                 if ((alen & 0x03) != 0) goto raw;
524                                 num_entries = alen / 4;
525                                 alen = 4;
526                                 break;
527
528                         default:
529                                 break; /* really an internal sanity failure */
530                         }
531                 } else {
532                         num_entries = 1;
533
534                         switch (da->type) {
535                         case PW_TYPE_BYTE:
536                                 if (alen != 1) goto raw;
537                                 break;
538
539                         case PW_TYPE_SHORT:
540                                 if (alen != 2) goto raw;
541                                 break;
542
543                         case PW_TYPE_IPADDR:
544                         case PW_TYPE_INTEGER:
545                         case PW_TYPE_DATE:
546                                 if (alen != 4) goto raw;
547                                 break;
548
549                         default:
550                                 break;
551                         }
552                 }
553
554                 for (i = 0; i < num_entries; i++) {
555                         vp = pairmake(da->name, NULL, T_OP_EQ);
556                         if (!vp) {
557                                 fprintf(stderr, "Cannot build attribute %s\n",
558                                         fr_strerror());
559                                 pairfree(&head);
560                                 return -1;
561                         }
562
563                         /*
564                          *      Hacks for ease of use.
565                          */
566                         if ((da->attr == DHCP2ATTR(0x3d)) &&
567                             !da->flags.array &&
568                             (alen == 7) && (*p == 1) && (num_entries == 1)) {
569                                 vp->type = PW_TYPE_ETHERNET;
570                                 memcpy(vp->vp_octets, p + 1, 6);
571                         } else
572
573                                 switch (vp->type) {
574                                 case PW_TYPE_BYTE:
575                                         vp->vp_integer = p[0];
576                                         break;
577                                 
578                                 case PW_TYPE_SHORT:
579                                         vp->vp_integer = (p[0] << 8) | p[1];
580                                         break;
581
582                                 case PW_TYPE_INTEGER:
583                                         memcpy(&vp->vp_integer, p, 4);
584                                         vp->vp_integer = ntohl(vp->vp_integer);
585                                         break;
586
587                                 case PW_TYPE_IPADDR:
588                                         memcpy(&vp->vp_ipaddr, p , 4);
589                                         vp->length = 4;
590                                         break;
591
592                                 case PW_TYPE_STRING:
593                                         memcpy(vp->vp_strvalue, p , alen);
594                                         vp->vp_strvalue[alen] = '\0';
595                                         break;
596
597                                 raw:
598                                         vp = pairmake(da->name, NULL, T_OP_EQ);
599                                         if (!vp) {
600                                                 fprintf(stderr, "Cannot build attribute %s\n", fr_strerror());
601                                                 pairfree(&head);
602                                                 return -1;
603                                         }
604
605                                         vp->type = PW_TYPE_OCTETS;
606                                 
607                                 case PW_TYPE_OCTETS:
608                                         memcpy(vp->vp_octets, p, alen);
609                                         break;
610                                 
611                                 default:
612                                         fprintf(stderr, "Internal sanity check %d %d\n", vp->type, __LINE__);
613                                         pairfree(&vp);
614                                         break;
615                                 } /* switch over type */
616                                 
617                         vp->length = alen;
618
619                         if (fr_debug_flag > 1) {
620                                 vp_prints(buffer, sizeof(buffer), vp);
621                                 fprintf(stderr, "\t%s\n", buffer);
622                         }
623
624                         *tail = vp;
625                         tail = &vp->next;
626                         p += alen;
627                 } /* loop over array entries */
628                 
629                 total -= 2;
630                 total -= (alen * num_entries);
631         }
632
633         /*
634          *      If DHCP request, set ciaddr to zero.
635          */
636
637         /*
638          *      Set broadcast flag for broken vendors, but only if
639          *      giaddr isn't set.
640          */
641         memcpy(&giaddr, packet->data + 24, sizeof(giaddr));
642         if (giaddr == htonl(INADDR_ANY)) {
643                 /*
644                  *      DHCP Opcode is request
645                  */
646                 vp = pairfind(head, DHCP2ATTR(256));
647                 if (vp && vp->lvalue == 3) {
648                         /*
649                          *      Vendor is "MSFT 98"
650                          */
651                         vp = pairfind(head, DHCP2ATTR(63));
652                         if (vp && (strcmp(vp->vp_strvalue, "MSFT 98") == 0)) {
653                                 vp = pairfind(head, DHCP2ATTR(262));
654
655                                 /*
656                                  *      Reply should be broadcast.
657                                  */
658                                 if (vp) vp->lvalue |= 0x8000;
659                                 packet->data[10] |= 0x80;                       
660                         }
661                 }
662         }
663
664         /*
665          *      FIXME: Nuke attributes that aren't used in the normal
666          *      header for discover/requests.
667          */
668         packet->vps = head;
669
670         /*
671          *      Client can request a LARGER size, but not a smaller
672          *      one.  They also cannot request a size larger than MTU.
673          */
674         maxms = pairfind(packet->vps, DHCP2ATTR(57));
675         mtu = pairfind(packet->vps, DHCP2ATTR(26));
676
677         if (mtu && (mtu->vp_integer < DEFAULT_PACKET_SIZE)) {
678                 fprintf(stderr, "DHCP Fatal: Client says MTU is smaller than minimum permitted by the specification.");
679                 return -1;
680         }
681
682         if (maxms && (maxms->vp_integer < DEFAULT_PACKET_SIZE)) {
683                 fprintf(stderr, "DHCP WARNING: Client says maximum message size is smaller than minimum permitted by the specification: fixing it");
684                 maxms->vp_integer = DEFAULT_PACKET_SIZE;
685         }
686
687         if (maxms && mtu && (maxms->vp_integer > mtu->vp_integer)) {
688                 fprintf(stderr, "DHCP WARNING: Client says MTU is smaller than maximum message size: fixing it");
689                 maxms->vp_integer = mtu->vp_integer;
690         }
691
692         if (fr_debug_flag) fflush(stdout);
693
694         return 0;
695 }
696
697
698 static int attr_cmp(const void *one, const void *two)
699 {
700         const VALUE_PAIR * const *a = one;
701         const VALUE_PAIR * const *b = two;
702
703         /*
704          *      DHCP-Message-Type is first, for simplicity.
705          */
706         if (((*a)->attribute == DHCP2ATTR(53)) &&
707             (*b)->attribute != DHCP2ATTR(53)) return -1;
708
709         /*
710          *      Relay-Agent is last
711          */
712         if (((*a)->attribute == DHCP2ATTR(82)) &&
713             (*b)->attribute != DHCP2ATTR(82)) return +1;
714
715         return ((*a)->attribute - (*b)->attribute);
716 }
717
718
719 static size_t fr_dhcp_vp2attr(VALUE_PAIR *vp, uint8_t *p, size_t room)
720 {
721         size_t length;
722         uint32_t lvalue;
723
724         /*
725          *      FIXME: Check room!
726          */
727         room = room;            /* -Wunused */
728
729         /*
730          *      Search for all attributes of the same
731          *      type, and pack them into the same
732          *      attribute.
733          */
734         switch (vp->type) {
735         case PW_TYPE_BYTE:
736                 length = 1;
737                 *p = vp->vp_integer & 0xff;
738                 break;
739                 
740         case PW_TYPE_SHORT:
741                 length = 2;
742                 p[0] = (vp->vp_integer >> 8) & 0xff;
743                 p[1] = vp->vp_integer & 0xff;
744                 break;
745                 
746         case PW_TYPE_INTEGER:
747                 length = 4;
748                 lvalue = htonl(vp->vp_integer);
749                 memcpy(p, &lvalue, 4);
750                 break;
751                 
752         case PW_TYPE_IPADDR:
753                 length = 4;
754                 memcpy(p, &vp->vp_ipaddr, 4);
755                 break;
756                 
757         case PW_TYPE_ETHERNET:
758                 length = 6;
759                 memcpy(p, &vp->vp_ether, 6);
760                 break;
761                 
762         case PW_TYPE_STRING:
763                 memcpy(p, vp->vp_strvalue, vp->length);
764                 length = vp->length;
765                 break;
766                 
767         case PW_TYPE_OCTETS:
768                 memcpy(p, vp->vp_octets, vp->length);
769                 length = vp->length;
770                 break;
771                 
772         default:
773                 fprintf(stderr, "BAD TYPE2 %d\n", vp->type);
774                 length = 0;
775                 break;
776         }
777
778         return length;
779 }
780
781 int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
782 {
783         int i, num_vps;
784         uint8_t *p;
785         VALUE_PAIR *vp;
786         uint32_t lvalue, mms;
787         size_t dhcp_size, length;
788         dhcp_packet_t *dhcp;
789         char buffer[1024];
790
791         if (packet->data) return 0;
792
793         packet->data = malloc(MAX_PACKET_SIZE);
794         if (!packet->data) return -1;
795
796         packet->data_len = MAX_PACKET_SIZE;
797
798         if (packet->code == 0) packet->code = PW_DHCP_NAK;
799
800         /*
801          *      FIXME: allow it to send client packets.
802          */
803         if (!original) {
804                 fr_strerror_printf("Need original to send response!");
805                 return -1;
806         }
807
808         packet->dst_ipaddr.af = AF_INET;
809         packet->src_ipaddr.af = AF_INET;
810
811         packet->dst_port = original->src_port;
812         packet->src_port = original->dst_port;
813
814         /*
815          *      Note that for DHCP, we NEVER send the response to the
816          *      source IP address of the request.  It may have
817          *      traversed multiple relays, and we need to send the request
818          *      to the relay closest to the client.
819          *
820          *      if giaddr, send to giaddr.
821          *      if NAK, send broadcast packet
822          *      if ciaddr, unicast to ciaddr
823          *      if flags & 0x8000, broadcast (client request)
824          *      if sent from 0.0.0.0, broadcast response
825          *      unicast to client yiaddr
826          */
827
828         /*
829          *      FIXME: alignment issues.  We likely don't want to
830          *      de-reference the packet structure directly..
831          */
832         dhcp = (dhcp_packet_t *) original->data;
833
834         if (dhcp->giaddr != htonl(INADDR_ANY)) {
835                 packet->dst_ipaddr.ipaddr.ip4addr.s_addr = dhcp->giaddr;
836
837                 if (dhcp->giaddr != htonl(INADDR_LOOPBACK)) {
838                         packet->dst_port = original->dst_port;
839                 } else {
840                         packet->dst_port = original->src_port; /* debugging */
841                 }
842
843         } else if (packet->code == PW_DHCP_NAK) {
844                 packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
845                 
846         } else if (dhcp->ciaddr != htonl(INADDR_ANY)) {
847                 packet->dst_ipaddr.ipaddr.ip4addr.s_addr = dhcp->ciaddr;
848
849         } else if ((dhcp->flags & 0x8000) != 0) {
850                 packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
851
852         } else if (packet->dst_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY)) {
853                 packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
854
855         } else {
856                 packet->dst_ipaddr.ipaddr.ip4addr.s_addr = dhcp->yiaddr;
857         }
858
859         /*
860          *      Rewrite the source IP to be our own, if we know it.
861          */
862         if (packet->src_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_BROADCAST)) {
863                 packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_ANY);
864         }
865
866         if (fr_debug_flag > 1) {
867                 char type_buf[64];
868                 const char *name = type_buf;
869                 char src_ip_buf[256], dst_ip_buf[256];
870                 
871                 if ((packet->code >= PW_DHCP_DISCOVER) &&
872                     (packet->code <= PW_DHCP_INFORM)) {
873                         name = dhcp_message_types[packet->code - PW_DHCP_OFFSET];
874                 } else {
875                         snprintf(type_buf, sizeof(type_buf), "%d",
876                                  packet->code - PW_DHCP_OFFSET);
877                 }
878
879                 printf("Sending %s of id %08x from %s:%d to %s:%d\n",
880                        name, (unsigned int) packet->id,
881                        inet_ntop(packet->src_ipaddr.af,
882                                  &packet->src_ipaddr.ipaddr,
883                                  src_ip_buf, sizeof(src_ip_buf)),
884                        packet->src_port,
885                        inet_ntop(packet->dst_ipaddr.af,
886                                  &packet->dst_ipaddr.ipaddr,
887                                  dst_ip_buf, sizeof(dst_ip_buf)),
888                        packet->dst_port);
889                 fflush(stdout);
890         }
891
892         p = packet->data;
893
894         mms = DEFAULT_PACKET_SIZE; /* maximum message size */
895
896         /*
897          *      Client can request a LARGER size, but not a smaller
898          *      one.  They also cannot request a size larger than MTU.
899          */
900         vp = pairfind(original->vps, DHCP2ATTR(57));
901         if (vp && (vp->vp_integer > mms)) {
902                 mms = vp->vp_integer;
903                 
904                 if (mms > MAX_PACKET_SIZE) mms = MAX_PACKET_SIZE;
905         }
906
907         /*
908          *      RFC 3118: Authentication option.
909          */
910         vp = pairfind(packet->vps, DHCP2ATTR(90));
911         if (vp) {
912                 if (vp->length < 2) {
913                         memset(vp->vp_octets + vp->length, 0,
914                                2 - vp->length);
915                         vp->length = 2;
916                 }
917
918                 if (vp->length < 3) {
919                         struct timeval tv;
920
921                         gettimeofday(&tv, NULL);
922                         vp->vp_octets[2] = 0;
923                         timeval2ntp(&tv, vp->vp_octets + 3);
924                         vp->length = 3 + 8;
925                 }
926
927                 /*
928                  *      Configuration token (clear-text token)
929                  */
930                 if (vp->vp_octets[0] == 0) {
931                         VALUE_PAIR *pass;
932                         vp->vp_octets[1] = 0;
933
934                         pass = pairfind(packet->vps, PW_CLEARTEXT_PASSWORD);
935                         if (pass) {
936                                 length = pass->length;
937                                 if ((length + 11) > sizeof(vp->vp_octets)) {
938                                         length -= ((length + 11) - sizeof(vp->vp_octets));
939                                 }
940                                 memcpy(vp->vp_octets + 11, pass->vp_strvalue,
941                                        length);
942                                 vp->length = length + 11;
943                         } else {
944                                 vp->length = 11 + 8;
945                                 memset(vp->vp_octets + 11, 8, 0);
946                                 vp->length = 11 + 8;
947                         }
948                 } else {        /* we don't support this type! */
949                         fprintf(stderr, "DHCP-Authentication %d unsupported\n",
950                                 vp->vp_octets[0]);
951                 }
952         }
953
954         if (!original) {
955                 *p++ = 1;       /* client message */
956         } else {
957                 *p++ = 2;       /* server message */
958         }
959         *p++ = 1;               /* hardware type = ethernet */
960         *p++ = original->data[2];
961         *p++ = 0;               /* hops */
962
963         if (!original) {        /* Xid */
964                 lvalue = fr_rand();
965                 memcpy(p, &lvalue, 4);
966         } else {
967                 memcpy(p, original->data + 4, 4);
968         }
969         p += 4;
970
971         memset(p, 0, 2);        /* secs are zero */
972         p += 2;
973
974         memcpy(p, original->data + 10, 6); /* copy flags && ciaddr */
975
976         /*
977          *      Allow the admin to set the broadcast flag.
978          */
979         vp = pairfind(packet->vps, DHCP2ATTR(262));
980         if (vp) {
981                 p[0] |= (vp->vp_integer & 0xff00) >> 8;
982                 p[1] |= (vp->vp_integer & 0xff);
983         }
984
985         p += 6;
986
987         /*
988          *      Set client IP address.
989          */
990         vp = pairfind(packet->vps, DHCP2ATTR(264)); /* Your IP address */
991         if (vp) {
992                 lvalue = vp->vp_ipaddr;
993         } else {
994                 lvalue = htonl(INADDR_ANY);
995         }
996         memcpy(p, &lvalue, 4);  /* your IP address */
997         p += 4;
998
999         memset(p, 0, 4);        /* siaddr is zero */
1000         p += 4;
1001
1002         memcpy(p, original->data + 24, 4); /* copy gateway IP address */
1003         p += 4;
1004
1005         memcpy(p, original->data + 28, DHCP_CHADDR_LEN);
1006         p += DHCP_CHADDR_LEN;
1007
1008         memset(p, 0, 192);      /* bootp legacy */
1009         p += 192;
1010
1011         lvalue = htonl(DHCP_OPTION_MAGIC_NUMBER); /* DHCP magic number */
1012         memcpy(p, &lvalue, 4);
1013         p += 4;
1014
1015         /*
1016          *      Print the header.
1017          */
1018         if (fr_debug_flag > 1) {
1019                 uint8_t *pp = p;
1020
1021                 p = packet->data;
1022
1023                 for (i = 0; i < 14; i++) {
1024                         vp = pairmake(dhcp_header_names[i], NULL, T_OP_EQ);
1025                         if (!vp) {
1026                                 fprintf(stderr, "Parse error %s\n", fr_strerror());
1027                                 return -1;
1028                         }
1029                         
1030                         switch (vp->type) {
1031                         case PW_TYPE_BYTE:
1032                                 vp->vp_integer = p[0];
1033                                 vp->length = 1;
1034                                 break;
1035                                 
1036                         case PW_TYPE_SHORT:
1037                                 vp->vp_integer = (p[0] << 8) | p[1];
1038                                 vp->length = 2;
1039                                 break;
1040                                 
1041                         case PW_TYPE_INTEGER:
1042                                 memcpy(&vp->vp_integer, p, 4);
1043                                 vp->vp_integer = ntohl(vp->vp_integer);
1044                                 vp->length = 4;
1045                                 break;
1046                                 
1047                         case PW_TYPE_IPADDR:
1048                                 memcpy(&vp->vp_ipaddr, p, 4);
1049                                 vp->length = 4;
1050                                 break;
1051                                 
1052                         case PW_TYPE_STRING:
1053                                 memcpy(vp->vp_strvalue, p, dhcp_header_sizes[i]);
1054                                 vp->vp_strvalue[dhcp_header_sizes[i]] = '\0';
1055                                 vp->length = strlen(vp->vp_strvalue);
1056                                 break;
1057                                 
1058                         case PW_TYPE_OCTETS: /* only for Client HW Address */
1059                                 memcpy(vp->vp_octets, p, packet->data[2]);
1060                                 vp->length = packet->data[2];
1061                                 break;
1062                                 
1063                         case PW_TYPE_ETHERNET: /* only for Client HW Address */
1064                                 memcpy(vp->vp_ether, p, sizeof(vp->vp_ether));
1065                                 vp->length = sizeof(vp->vp_ether);
1066                                 break;
1067                                 
1068                         default:
1069                                 fprintf(stderr, "Internal sanity check failed %d %d\n", vp->type, __LINE__);
1070                                 pairfree(&vp);
1071                                 break;
1072                         }
1073                         
1074                         p += dhcp_header_sizes[i];
1075                         
1076                         vp_prints(buffer, sizeof(buffer), vp);
1077                         fprintf(stderr, "\t%s\n", buffer);
1078                         pairfree(&vp);
1079                 }
1080
1081                 /*
1082                  *      Jump over DHCP magic number, response, etc.
1083                  */
1084                 p = pp;
1085         }
1086
1087         /*
1088          *      Before packing the attributes, re-order them so that
1089          *      the array ones are all contiguous.  This simplifies
1090          *      the later code.
1091          */
1092         num_vps = 0;
1093         for (vp = packet->vps; vp != NULL; vp = vp->next) {
1094                 num_vps++;
1095         }
1096         if (num_vps > 1) {
1097                 VALUE_PAIR **array, **last;
1098
1099                 array = malloc(num_vps * sizeof(VALUE_PAIR *));
1100                 
1101                 i = 0;
1102                 for (vp = packet->vps; vp != NULL; vp = vp->next) {
1103                         array[i++] = vp;
1104                 }
1105                 
1106                 /*
1107                  *      Sort the attributes.
1108                  */
1109                 qsort(array, (size_t) num_vps, sizeof(VALUE_PAIR *),
1110                       attr_cmp);
1111                 
1112                 last = &packet->vps;
1113                 for (i = 0; i < num_vps; i++) {
1114                         *last = array[i];
1115                         array[i]->next = NULL;
1116                         last = &(array[i]->next);
1117                 }
1118                 free(array);
1119         }
1120
1121         p[0] = 0x35;            /* DHCP-Message-Type */
1122         p[1] = 1;
1123         p[2] = packet->code - PW_DHCP_OFFSET;
1124         p += 3;
1125
1126         /*
1127          *      Pack in the attributes.
1128          */
1129         vp = packet->vps;
1130         while (vp) {
1131                 int num_entries = 1;
1132                 
1133                 VALUE_PAIR *same;
1134                 uint8_t *plength, *pattr;
1135
1136                 if (!IS_DHCP_ATTR(vp)) goto next;
1137                 if (vp->attribute == DHCP2ATTR(53)) goto next; /* already done */
1138                 if (((vp->attribute & 0xffff) > 255) &&
1139                     (DHCP_BASE_ATTR(vp->attribute) != PW_DHCP_OPTION_82)) goto next;
1140
1141                 length = vp->length;
1142
1143                 for (same = vp->next; same != NULL; same = same->next) {
1144                         if (same->attribute != vp->attribute) break;
1145                         num_entries++;
1146                 }
1147
1148                 /*
1149                  *      For client-identifier
1150                  */
1151                 if ((vp->type == PW_TYPE_ETHERNET) &&
1152                     (vp->length == 6) &&
1153                     (num_entries == 1)) {
1154                         vp->type = PW_TYPE_OCTETS;
1155                         memmove(vp->vp_octets + 1, vp->vp_octets, 6);
1156                         vp->vp_octets[0] = 1;
1157                 }
1158
1159                 pattr = p;
1160                 *(p++) = vp->attribute & 0xff;
1161                 plength = p;
1162                 *(p++) = 0;     /* header isn't included in attr length */
1163
1164                 if (DHCP_BASE_ATTR(vp->attribute) == PW_DHCP_OPTION_82) {
1165                         *(p++) = DHCP_UNPACK_OPTION1(vp->attribute);
1166                         *(p++) = 0;
1167                         *plength = 2;
1168                 }
1169
1170                 for (i = 0; i < num_entries; i++) {
1171                         if (fr_debug_flag > 1) {
1172                                 vp_prints(buffer, sizeof(buffer), vp);
1173                                 fprintf(stderr, "\t%s\n", buffer);
1174                         }
1175
1176                         length = fr_dhcp_vp2attr(vp, p, 0);
1177
1178                         /*
1179                          *      This will never happen due to FreeRADIUS
1180                          *      limitations: sizeof(vp->vp_octets) < 255
1181                          */
1182                         if (length > 255) {
1183                                 fprintf(stderr, "WARNING Ignoring too long attribute %s!\n", vp->name);
1184                                 break;
1185                         }
1186
1187                         /*
1188                          *      More than one attribute of the same type
1189                          *      in a row: they are packed together
1190                          *      into the same TLV.  If we overflow,
1191                          *      go bananas!
1192                          */
1193                         if ((*plength + length) > 255) {
1194                                 fprintf(stderr, "WARNING Ignoring too long attribute %s!\n", vp->name);
1195                                 break;
1196                         }
1197                         
1198                         *plength += length;
1199                         p += length;
1200
1201                         if (vp->next &&
1202                             (vp->next->attribute == vp->attribute))
1203                                 vp = vp->next;
1204                 } /* loop over num_entries */
1205
1206                 if (DHCP_BASE_ATTR(vp->attribute) == PW_DHCP_OPTION_82) {
1207                         plength[2] = plength[0] - 2;
1208                 }
1209
1210         next:
1211                 vp = vp->next;
1212         }
1213
1214         p[0] = 0xff;            /* end of option option */
1215         p[1] = 0x00;
1216         p += 2;
1217         dhcp_size = p - packet->data;
1218
1219         /*
1220          *      FIXME: if (dhcp_size > mms),
1221          *        then we put the extra options into the "sname" and "file"
1222          *        fields, AND set the "end option option" in the "options"
1223          *        field.  We also set the "overload option",
1224          *        and put options into the "file" field, followed by
1225          *        the "sname" field.  Where each option is completely
1226          *        enclosed in the "file" and/or "sname" field, AND
1227          *        followed by the "end of option", and MUST be followed
1228          *        by padding option.
1229          *
1230          *      Yuck.  That sucks...
1231          */
1232         packet->data_len = dhcp_size;
1233
1234         /*
1235          *      FIXME: This may set it to broadcast, which we don't
1236          *      want.  Instead, set it to the real address of the
1237          *      socket.
1238          */
1239         packet->src_ipaddr = original->dst_ipaddr;
1240
1241         packet->sockfd = original->sockfd;
1242
1243         if (packet->data_len < DEFAULT_PACKET_SIZE) {
1244                 memset(packet->data + packet->data_len, 0,
1245                        DEFAULT_PACKET_SIZE - packet->data_len);
1246                 packet->data_len = DEFAULT_PACKET_SIZE;
1247         }
1248
1249         if ((fr_debug_flag > 2) && fr_log_fp) {
1250                 for (i = 0; i < packet->data_len; i++) {
1251                         if ((i & 0x0f) == 0x00) fprintf(fr_log_fp, "%d: ", i);
1252                         fprintf(fr_log_fp, "%02x ", packet->data[i]);
1253                         if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n");
1254                 }
1255                 fprintf(fr_log_fp, "\n");
1256         }
1257
1258         return 0;
1259 }
1260 #endif /* WITH_DHCP */