The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
packet.c
Go to the documentation of this file.
1 /*
2  * This library is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU Lesser General Public
4  * License as published by the Free Software Foundation; either
5  * version 2.1 of the License, or (at your option) any later version.
6  *
7  * This library is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10  * Lesser General Public License for more details.
11  *
12  * You should have received a copy of the GNU Lesser General Public
13  * License along with this library; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: bb142c503ae4ff298514bdba616bd522b4892585 $
19  *
20  * @file protocols/dhcpv4/packet.c
21  * @brief Functions to encode/decode DHCP packets.
22  *
23  * @copyright 2008,2017 The FreeRADIUS server project
24  * @copyright 2008 Alan DeKok (aland@deployingradius.com)
25  */
26 #include <freeradius-devel/util/pair.h>
27 #include <freeradius-devel/util/rand.h>
28 #include <freeradius-devel/protocol/dhcpv4/rfc2131.h>
29 
30 #include "dhcpv4.h"
31 #include "attrs.h"
32 
33 /** Retrieve a DHCP option from a raw packet buffer
34  *
35  *
36  */
37 uint8_t const *fr_dhcpv4_packet_get_option(dhcp_packet_t const *packet, size_t packet_size, fr_dict_attr_t const *da)
38 {
39  int overload = 0;
40  int field = DHCP_OPTION_FIELD;
41  size_t where, size;
42  uint8_t const *data;
43 
44  if (packet_size < MIN_PACKET_SIZE) return NULL;
45 
46  where = 0;
47  size = packet_size - offsetof(dhcp_packet_t, options);
48  data = &packet->options[where];
49 
50  while (where < size) {
51  if (data[0] == 0) return NULL; /* padding */
52 
53  if (data[0] == 255) { /* end of options */
54  if ((field == DHCP_OPTION_FIELD) && (overload & DHCP_FILE_FIELD)) {
55  data = packet->file;
56  where = 0;
57  size = sizeof(packet->file);
58  field = DHCP_FILE_FIELD;
59  continue;
60 
61  } else if ((field == DHCP_FILE_FIELD || field == DHCP_OPTION_FIELD) && (overload & DHCP_SNAME_FIELD)) {
62  data = packet->sname;
63  where = 0;
64  size = sizeof(packet->sname);
65  field = DHCP_SNAME_FIELD;
66  continue;
67  }
68 
69  return NULL;
70  }
71 
72  /*
73  * We MUST have a real option here.
74  */
75  if ((where + 2) > size) {
76  fr_strerror_printf("Options overflow field at %u",
77  (unsigned int) (data - (uint8_t const *) packet));
78  return NULL;
79  }
80 
81  if ((where + 2 + data[1]) > size) {
82  fr_strerror_printf("Option length overflows field at %u",
83  (unsigned int) (data - (uint8_t const *) packet));
84  return NULL;
85  }
86 
87  if (data[0] == da->attr) return data;
88 
89  if ((data[0] == 52) && (data[1] > 0)) { /* overload sname and/or file */
90  overload = data[2];
91  }
92 
93  where += data[1] + 2;
94  data += data[1] + 2;
95  }
96 
97  return NULL;
98 }
99 
100 int fr_dhcpv4_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, unsigned int *code)
101 {
102  size_t i;
103  uint8_t const *p = data;
104  uint32_t giaddr;
105  fr_pair_list_t tmp;
106  fr_pair_t *vp;
107  fr_pair_t *maxms, *mtu, *netaddr;
108  fr_value_box_t box;
109  fr_dhcpv4_ctx_t *packet_ctx;
110 
111  fr_pair_list_init(&tmp);
112 
113  if (data[1] > 1) {
114  fr_strerror_printf("Packet is not Ethernet: %u",
115  data[1]);
116  return -1;
117  }
118 
119  packet_ctx = talloc_zero(ctx, fr_dhcpv4_ctx_t);
120  if (!packet_ctx) return -1;
121  packet_ctx->tmp_ctx = talloc(packet_ctx, uint8_t);
122 
123  /*
124  * Decode the header.
125  */
126  for (i = 0; i < dhcp_header_attrs_len; i++) {
127  fr_dict_attr_t const *da = *dhcp_header_attrs[i];
128 
129  vp = fr_pair_afrom_da(ctx, da);
130  if (!vp) {
131  fr_strerror_const_push("Cannot decode packet due to internal error");
132  error:
133  talloc_free(vp);
134  fr_pair_list_free(&tmp);
135  talloc_free(packet_ctx);
136  return -1;
137  }
138 
139  switch (vp->vp_type) {
140  case FR_TYPE_STRING:
141  /*
142  * According to RFC 2131, these are null terminated strings.
143  * We don't trust everyone to abide by the RFC, though.
144  */
145  if (*p != '\0') {
146  uint8_t *q;
147 
148  q = memchr(p, '\0', dhcp_header_sizes[i]);
149  fr_pair_value_bstrndup(vp, (char const *)p, q ? q - p : dhcp_header_sizes[i], true);
150  } else {
151  TALLOC_FREE(vp);
152  }
153  break;
154 
155  /*
156  * The DHCP header size for CHADDR is not
157  * 6, so the value_box function doesn't
158  * like it. Just do the copy manually.
159  */
160  case FR_TYPE_ETHERNET:
161  if ((data[1] != 1) || (data[2] != 6)) {
162  TALLOC_FREE(vp);
163  break;
164  }
165 
166  memcpy(vp->vp_ether, p, sizeof(vp->vp_ether));
167  break;
168 
169  default:
170  if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, vp->da,
171  &FR_DBUFF_TMP(p, (size_t)dhcp_header_sizes[i]),
172  dhcp_header_sizes[i], true) < 0) goto error;
173  break;
174  }
175  p += dhcp_header_sizes[i];
176 
177  if (!vp) continue;
178 
179  fr_pair_append(&tmp, vp);
180  }
181 
182  /*
183  * Nothing uses tail after this call, if it does in the future
184  * it'll need to find the new tail...
185  */
186  {
187  uint8_t const *end;
188  ssize_t len;
189 
190  p = data + 240;
191  end = p + (data_len - 240);
192 
193  /*
194  * Loop over all the options data
195  */
196  while (p < end) {
197  if (p[0] == 0) break; /* padding */
198 
199  len = fr_dhcpv4_decode_option(ctx, &tmp, p, (end - p), packet_ctx);
200  if (len <= 0) {
201  fail:
202  fr_pair_list_free(&tmp);
203  talloc_free(packet_ctx);
204  return len;
205  }
206  p += len;
207  }
208 
209  if (code) {
211  if (vp) {
212  *code = vp->vp_uint8;
213  }
214  }
215 
216  /*
217  * If option Overload is present in the 'options' field, then fields 'file' and/or 'sname'
218  * are used to hold more options. They are partitioned and must be interpreted in sequence.
219  */
221  if (vp) {
222  if ((vp->vp_uint8 & 1) == 1) {
223  /*
224  * The 'file' field is used to hold options.
225  * It must be interpreted before 'sname'.
226  */
227  p = data + 44;
228  end = p + 64;
229  while (p < end) {
230  if (p[0] == 0) break; /* padding */
231 
232  len = fr_dhcpv4_decode_option(ctx, &tmp,
233  p, end - p, packet_ctx);
234  if (len <= 0) goto fail;
235  p += len;
236  }
238  }
239  if ((vp->vp_uint8 & 2) == 2) {
240  /*
241  * The 'sname' field is used to hold options.
242  */
243  p = data + 108;
244  end = p + 128;
245  while (p < end) {
246  if (p[0] == 0) break; /* padding */
247 
248  len = fr_dhcpv4_decode_option(ctx, &tmp,
249  p, end - p, packet_ctx);
250  if (len <= 0) goto fail;
251  p += len;
252  }
254  }
255  }
256  }
257 
258  /*
259  * If DHCP request, set ciaddr to zero.
260  */
261 
262  /*
263  * Set broadcast flag for broken vendors, but only if
264  * giaddr isn't set.
265  */
266  memcpy(&giaddr, data + 24, sizeof(giaddr));
267  if (giaddr == htonl(INADDR_ANY)) {
268  /*
269  * DHCP Opcode is request
270  */
271  vp = fr_pair_find_by_da(&tmp, NULL, attr_dhcp_opcode);
272  if (vp && vp->vp_uint8 == 1) {
273  /*
274  * Vendor is "MSFT 98"
275  */
277  if (vp && (vp->vp_length == 7) && (memcmp(vp->vp_strvalue, "MSFT 98", 7) == 0)) {
278  vp = fr_pair_find_by_da(&tmp, NULL, attr_dhcp_flags);
279 
280  /*
281  * Reply should be broadcast.
282  */
283  if (vp) vp->vp_uint16 |= 0x8000;
284  }
285  }
286  }
287 
288  /*
289  * Determine the address to use in looking up which subnet the
290  * client belongs to based on packet data. The sequence here
291  * is based on ISC DHCP behaviour and RFCs 3527 and 3011. We
292  * store the found address in an internal attribute of
293  * Network-Subnet
294  *
295  *
296  * All of these options / fields are type "ipv4addr", so
297  * we need to decode them as that. And then cast it to
298  * "ipv4prefix".
299  */
301  if (!vp) return -1;
302 
303  /*
304  * First look for Relay-Link-Selection
305  */
307  if (!netaddr) {
308  /*
309  * Next try Subnet-Selection-Option
310  */
312  }
313 
314  if (netaddr) {
315  /*
316  * Store whichever address we found from options and ensure
317  * the data type matches the pair, i.e address to prefix
318  * conversion.
319  */
320  if (fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->da, &netaddr->data) < 0) return -1;
321 
322  } else if (giaddr != htonl(INADDR_ANY)) {
323  /*
324  * Gateway address is set - use that one
325  */
327  &FR_DBUFF_TMP(data + 24, 4), 4, true) < 0) return -1;
328  if (fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->da, &box) < 0) return -1;
329 
330  } else {
331  /*
332  * else, store client address whatever it is
333  */
335  &FR_DBUFF_TMP(data + 12, 4), 4, true) < 0) return -1;
336  if (fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->da, &box) < 0) return -1;
337  }
338 
339  fr_pair_append(&tmp, vp);
340 
341  /*
342  * Client can request a LARGER size, but not a smaller
343  * one. They also cannot request a size larger than MTU.
344  */
347 
348  if (mtu && (mtu->vp_uint16 < DEFAULT_PACKET_SIZE)) {
349  fr_strerror_const("Client says MTU is smaller than minimum permitted by the specification");
350  return -1;
351  }
352 
353  /*
354  * Client says maximum message size is smaller than minimum permitted
355  * by the specification: fixing it.
356  */
357  if (maxms && (maxms->vp_uint16 < DEFAULT_PACKET_SIZE)) maxms->vp_uint16 = DEFAULT_PACKET_SIZE;
358 
359  /*
360  * Client says MTU is smaller than maximum message size: fixing it
361  */
362  if (maxms && mtu && (maxms->vp_uint16 > mtu->vp_uint16)) maxms->vp_uint16 = mtu->vp_uint16;
363 
364  /*
365  * FIXME: Nuke attributes that aren't used in the normal
366  * header for discover/requests.
367  */
368  fr_pair_list_append(out, &tmp);
369 
370  return 0;
371 }
372 
374 {
375  ssize_t len;
376  fr_pair_t *vp;
377 
378  if (packet->data) return 0;
379 
380  packet->data_len = MAX_PACKET_SIZE;
381  packet->data = talloc_zero_array(packet, uint8_t, packet->data_len);
382 
383  /* XXX Ugly ... should be set by the caller */
384  if (packet->code == 0) packet->code = FR_DHCP_NAK;
385 
386  /* store xid */
387  if ((vp = fr_pair_find_by_da(list, NULL, attr_dhcp_transaction_id))) {
388  packet->id = vp->vp_uint32;
389  } else {
390  packet->id = fr_rand();
391  }
392 
393  len = fr_dhcpv4_encode(packet->data, packet->data_len, NULL, packet->code, packet->id, list);
394  if (len < 0) return -1;
395 
396  packet->data_len = len;
397 
398  return 0;
399 }
400 
402 {
403  fr_packet_t *packet;
404  uint32_t magic;
405  uint8_t const *code;
406 
408  if (!code) return NULL;
409 
410  if (data_len < MIN_PACKET_SIZE) return NULL;
411 
412  /* Now that checks are done, allocate packet */
413  packet = fr_packet_alloc(NULL, false);
414  if (!packet) {
415  fr_strerror_const("Failed allocating packet");
416  return NULL;
417  }
418 
419  /*
420  * Get XID.
421  */
422  memcpy(&magic, data + 4, 4);
423 
424  packet->data_len = data_len;
425  packet->code = code[2];
426  packet->id = ntohl(magic);
427 
428  /*
429  * FIXME: for DISCOVER / REQUEST: src_port == dst_port + 1
430  * FIXME: for OFFER / ACK : src_port = dst_port - 1
431  */
432 
433  /*
434  * Unique keys are xid, client mac, and client ID?
435  */
436  return packet;
437 }
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:509
static fr_dict_attr_t const * attr_dhcp_message_type
Definition: dhcpclient.c:90
Implementation of the DHCPv4 protocol.
TALLOC_CTX * tmp_ctx
for temporary things cleaned up during decoding
Definition: dhcpv4.h:154
#define DEFAULT_PACKET_SIZE
Definition: dhcpv4.h:105
#define MIN_PACKET_SIZE
Definition: dhcpv4.h:104
#define DHCP_OPTION_FIELD
Definition: dhcpv4.h:109
#define DHCP_SNAME_FIELD
Definition: dhcpv4.h:111
uint8_t options[DHCP_VEND_LEN]
Definition: dhcpv4.h:98
#define DHCP_FILE_FIELD
Definition: dhcpv4.h:110
uint8_t file[DHCP_FILE_LEN]
Definition: dhcpv4.h:96
@ FR_DHCP_NAK
Definition: dhcpv4.h:50
#define MAX_PACKET_SIZE
Definition: dhcpv4.h:106
uint8_t sname[DHCP_SNAME_LEN]
Definition: dhcpv4.h:95
Used as the decoder ctx.
Definition: dhcpv4.h:152
talloc_free(reap)
fr_packet_t * fr_packet_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new fr_packet_t.
Definition: packet.c:38
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
Definition: merged_model.c:86
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
Definition: merged_model.c:93
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:688
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition: pair.c:278
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition: pair.c:1340
int fr_pair_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da)
Delete matching pairs from the specified list.
Definition: pair.c:1684
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition: pair.c:46
int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Copy data into a "string" type value pair.
Definition: pair.c:2781
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition: pair.c:765
HIDDEN fr_dict_attr_t const * attr_dhcp_overload
Definition: base.c:66
HIDDEN fr_dict_attr_t const * attr_dhcp_boot_filename
Definition: base.c:48
HIDDEN fr_dict_attr_t const * attr_dhcp_opcode
Definition: base.c:57
HIDDEN fr_dict_attr_t const * attr_dhcp_interface_mtu_size
Definition: base.c:63
HIDDEN fr_dict_attr_t const * attr_dhcp_subnet_selection_option
Definition: base.c:69
HIDDEN fr_dict_attr_t const * attr_dhcp_network_subnet
Definition: base.c:70
HIDDEN fr_dict_attr_t const * attr_dhcp_relay_link_selection
Definition: base.c:68
HIDDEN fr_dict_attr_t const * attr_dhcp_transaction_id
Definition: base.c:60
HIDDEN fr_dict_attr_t const * attr_dhcp_dhcp_maximum_msg_size
Definition: base.c:62
HIDDEN fr_dict_attr_t const * attr_dhcp_vendor_class_identifier
Definition: base.c:67
HIDDEN fr_dict_attr_t const * attr_dhcp_flags
Definition: base.c:51
HIDDEN fr_dict_attr_t const * attr_dhcp_server_host_name
Definition: base.c:58
size_t dhcp_header_attrs_len
Definition: base.c:122
int dhcp_header_sizes[]
Definition: base.c:145
ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original, int code, uint32_t xid, fr_pair_list_t *vps)
Definition: base.c:311
fr_dict_attr_t const ** dhcp_header_attrs[]
Definition: base.c:106
ssize_t fr_dhcpv4_decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, void *decode_ctx)
Decode DHCP option.
Definition: decode.c:538
fr_packet_t * fr_dhcpv4_packet_alloc(uint8_t const *data, ssize_t data_len)
Definition: packet.c:401
uint8_t const * fr_dhcpv4_packet_get_option(dhcp_packet_t const *packet, size_t packet_size, fr_dict_attr_t const *da)
Retrieve a DHCP option from a raw packet buffer.
Definition: packet.c:37
int fr_dhcpv4_packet_encode(fr_packet_t *packet, fr_pair_list_t *list)
Definition: packet.c:373
int fr_dhcpv4_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, unsigned int *code)
Definition: packet.c:100
VQP attributes.
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: rand.c:106
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
unsigned int code
Packet code (type).
Definition: packet.h:61
int id
Packet ID (used to link requests/responses).
Definition: packet.h:60
uint8_t * data
Packet data (body).
Definition: packet.h:63
size_t data_len
Length of packet data.
Definition: packet.h:64
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
Definition: pair_inline.c:113
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
Definition: pair_inline.c:182
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
#define fr_strerror_const_push(_msg)
Definition: strerror.h:227
#define fr_strerror_const(_msg)
Definition: strerror.h:223
ssize_t fr_value_box_from_network(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t type, fr_dict_attr_t const *enumv, fr_dbuff_t *dbuff, size_t len, bool tainted)
Decode a fr_value_box_t from serialized binary data.
Definition: value.c:1709
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition: value.c:3301
static fr_slen_t data
Definition: value.h:1259
static size_t char ** out
Definition: value.h:984