The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
raw.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: b06c0d30bd24f71474616adfa5caed0d5795f6c9 $
19  *
20  * @file protocols/dhcpv4/raw.c
21  * @brief Send/recv DHCP packets using raw sockets.
22  *
23  * @copyright 2008,2017 The FreeRADIUS server project
24  * @copyright 2008 Alan DeKok (aland@deployingradius.com)
25  */
26 #include "attrs.h"
27 #include "dhcpv4.h"
28 
29 #include <freeradius-devel/util/net.h>
30 #include <freeradius-devel/util/pair.h>
31 #include <freeradius-devel/util/proto.h>
32 #include <freeradius-devel/util/syserror.h>
33 #include <freeradius-devel/util/udpfromto.h>
34 
35 #include <sys/ioctl.h>
36 
37 #ifdef HAVE_SYS_SOCKET_H
38 #endif
39 #ifdef HAVE_SYS_TYPES_H
40 #endif
41 
42 #ifdef HAVE_LINUX_IF_PACKET_H
43 # include <linux/if_ether.h>
44 #endif
45 
46 #include <net/if_arp.h>
47 
48 #ifdef HAVE_LINUX_IF_PACKET_H
49 /** Open a raw socket to read/write packets from/to
50  *
51  * @param[out] link_layer A sockaddr_ll struct to populate. Must be passed to other raw
52  * functions.
53  * @param[in] ifindex of the interface we're binding to.
54  * @return
55  * - >= 0 a file descriptor to read/write packets on.
56  * - <0 an error occurred.
57  */
58 int fr_dhcpv4_raw_socket_open(struct sockaddr_ll *link_layer, int ifindex)
59 {
60  int fd;
61 
62  /*
63  * PF_PACKET - packet interface on device level.
64  * using a raw socket allows packet data to be unchanged by the device driver.
65  */
66  fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
67  if (fd < 0) {
68  fr_strerror_printf("Cannot open socket: %s", fr_syserror(errno));
69  return fd;
70  }
71 
72  /* Set link layer parameters */
73  memset(link_layer, 0, sizeof(struct sockaddr_ll));
74 
75  link_layer->sll_family = AF_PACKET;
76  link_layer->sll_protocol = htons(ETH_P_ALL);
77  link_layer->sll_ifindex = ifindex;
78  link_layer->sll_hatype = ARPHRD_ETHER;
79  link_layer->sll_pkttype = PACKET_OTHERHOST;
80  link_layer->sll_halen = 6;
81 
82  if (bind(fd, (struct sockaddr *)link_layer, sizeof(struct sockaddr_ll)) < 0) {
83  close(fd);
84  fr_strerror_printf("Cannot bind raw socket: %s", fr_syserror(errno));
85  return -1;
86  }
87 
88  return fd;
89 }
90 
91 /** Create the requisite L2/L3 headers, and write a DHCPv4 packet to a raw socket
92  *
93  * @param[in] sockfd to write to.
94  * @param[in] link_layer information, as returned by fr_dhcpv4_raw_socket_open.
95  * @param[in] packet to write.
96  * @param[in] list to send.
97  * @return
98  * - 0 on success.
99  * - -1 on failure.
100  */
101 int fr_dhcpv4_raw_packet_send(int sockfd, struct sockaddr_ll *link_layer,
102  fr_packet_t *packet, fr_pair_list_t *list)
103 {
104  uint8_t dhcp_packet[1518] = { 0 };
105  ethernet_header_t *eth_hdr = (ethernet_header_t *)dhcp_packet;
106  ip_header_t *ip_hdr = (ip_header_t *)(dhcp_packet + ETH_HDR_SIZE);
107  udp_header_t *udp_hdr = (udp_header_t *) (dhcp_packet + ETH_HDR_SIZE + IP_HDR_SIZE);
108  dhcp_packet_t *dhcp = (dhcp_packet_t *)(dhcp_packet + ETH_HDR_SIZE + IP_HDR_SIZE + UDP_HDR_SIZE);
109 
110  uint16_t l4_len = (UDP_HDR_SIZE + packet->data_len);
111  fr_pair_t *vp;
112 
113  /* set ethernet source address to our MAC address (Client-Hardware-Address). */
114  uint8_t dhmac[ETH_ADDR_LEN] = { 0 };
116  if (vp->vp_type == FR_TYPE_ETHERNET) memcpy(dhmac, vp->vp_ether, sizeof(vp->vp_ether));
117  }
118 
119  /* fill in Ethernet layer (L2) */
120  memcpy(eth_hdr->dst_addr, eth_bcast, ETH_ADDR_LEN);
121  memcpy(eth_hdr->src_addr, dhmac, ETH_ADDR_LEN);
122  eth_hdr->ether_type = htons(ETH_TYPE_IP);
123 
124  /* fill in IP layer (L3) */
125  ip_hdr->ip_vhl = IP_VHL(4, 5);
126  ip_hdr->ip_tos = 0;
127  ip_hdr->ip_len = htons(IP_HDR_SIZE + UDP_HDR_SIZE + packet->data_len);
128  ip_hdr->ip_id = 0;
129  ip_hdr->ip_off = 0;
130  ip_hdr->ip_ttl = 64;
131  ip_hdr->ip_p = 17;
132  ip_hdr->ip_sum = 0; /* Filled later */
133 
134  /* saddr: packet src IP addr (default: 0.0.0.0). */
135  ip_hdr->ip_src.s_addr = packet->socket.inet.src_ipaddr.addr.v4.s_addr;
136 
137  /* daddr: packet destination IP addr (should be 255.255.255.255 for broadcast). */
138  ip_hdr->ip_dst.s_addr = packet->socket.inet.dst_ipaddr.addr.v4.s_addr;
139 
140  /* IP header checksum */
141  ip_hdr->ip_sum = fr_ip_header_checksum((uint8_t const *)ip_hdr, 5);
142 
143  udp_hdr->src = htons(packet->socket.inet.src_port);
144  udp_hdr->dst = htons(packet->socket.inet.dst_port);
145 
146  udp_hdr->len = htons(l4_len);
147  udp_hdr->checksum = 0; /* UDP checksum will be done after dhcp header */
148 
149  /* DHCP layer (L7) */
150 
151  /* just copy what FreeRADIUS has encoded for us. */
152  memcpy(dhcp, packet->data, packet->data_len);
153 
154  /* UDP checksum is done here */
155  udp_hdr->checksum = fr_udp_checksum((uint8_t const *)(dhcp_packet + ETH_HDR_SIZE + IP_HDR_SIZE),
156  l4_len, udp_hdr->checksum,
157  packet->socket.inet.src_ipaddr.addr.v4, packet->socket.inet.dst_ipaddr.addr.v4);
158 
159  return sendto(sockfd, dhcp_packet, (ETH_HDR_SIZE + IP_HDR_SIZE + UDP_HDR_SIZE + packet->data_len),
160  0, (struct sockaddr *) link_layer, sizeof(struct sockaddr_ll));
161 }
162 
163 /*
164  * For a client, receive a DHCP packet from a raw packet
165  * socket. Make sure it matches the ongoing request.
166  *
167  * FIXME: split this into two, recv_raw_packet, and verify(packet, original)
168  */
169 fr_packet_t *fr_dhcpv4_raw_packet_recv(int sockfd, struct sockaddr_ll *link_layer,
170  fr_packet_t *request, fr_pair_list_t *list)
171 {
172  fr_pair_t *vp;
173  fr_packet_t *packet;
174  dhcp_packet_t *dhcp_data;
175  uint8_t const *code;
176  uint32_t magic, xid;
177  ssize_t data_len;
178 
179  uint8_t *raw_packet;
180  ethernet_header_t *eth_hdr;
181  ip_header_t *ip_hdr;
182  udp_header_t *udp_hdr;
183  dhcp_packet_t *dhcp_hdr;
184  uint16_t udp_src_port;
185  uint16_t udp_dst_port;
186  size_t dhcp_data_len;
187  socklen_t sock_len;
188  uint8_t data_offset;
189 
190  packet = fr_packet_alloc(NULL, false);
191  if (!packet) {
192  fr_strerror_const("Failed allocating packet");
193  return NULL;
194  }
195 
196  raw_packet = talloc_zero_array(packet, uint8_t, MAX_PACKET_SIZE);
197  if (!raw_packet) {
198  fr_strerror_const("Out of memory");
199  fr_packet_free(&packet);
200  return NULL;
201  }
202 
203  packet->socket.fd = sockfd;
204 
205  /* a packet was received (but maybe it is not for us) */
206  sock_len = sizeof(struct sockaddr_ll);
207  data_len = recvfrom(sockfd, raw_packet, MAX_PACKET_SIZE, 0, (struct sockaddr *)link_layer, &sock_len);
208 
209  data_offset = ETH_HDR_SIZE + IP_HDR_SIZE + UDP_HDR_SIZE; /* DHCP data after Ethernet, IP, UDP */
210 
211  if (data_len <= data_offset) DISCARD_RP("Payload (%d) smaller than required for layers 2+3+4", (int)data_len);
212 
213  /* map raw packet to packet header of the different layers (Ethernet, IP, UDP) */
214  eth_hdr = (ethernet_header_t *)raw_packet;
215 
216  /*
217  * Check Ethernet layer data (L2)
218  */
219  if (ntohs(eth_hdr->ether_type) != ETH_TYPE_IP) DISCARD_RP("Ethernet type (%d) != IP",
220  ntohs(eth_hdr->ether_type));
221 
222  /*
223  * If Ethernet destination is not broadcast (ff:ff:ff:ff:ff:ff)
224  * Check if it matches the source HW address used (Client-Hardware-Address = 267)
225  */
226  if ((memcmp(&eth_bcast, &eth_hdr->dst_addr, ETH_ADDR_LEN) != 0) &&
228  ((vp->vp_type == FR_TYPE_ETHERNET) && (memcmp(vp->vp_ether, &eth_hdr->dst_addr, ETH_ADDR_LEN) != 0))) {
229 
230  /* No match. */
231  DISCARD_RP("Ethernet destination (%pV) is not broadcast and doesn't match request source (%pV)",
232  fr_box_ether(eth_hdr->dst_addr), &vp->data);
233  }
234 
235  /*
236  * Ethernet is OK. Now look at IP.
237  */
238  ip_hdr = (ip_header_t *)(raw_packet + ETH_HDR_SIZE);
239 
240  /*
241  * Check IPv4 layer data (L3)
242  */
243  if (ip_hdr->ip_p != IPPROTO_UDP) DISCARD_RP("IP protocol (%d) != UDP", ip_hdr->ip_p);
244 
245  /*
246  * note: checking the destination IP address is not
247  * useful (it would be the offered IP address - which we
248  * don't know beforehand, or the broadcast address).
249  */
250 
251  /*
252  * Now check UDP.
253  */
254  udp_hdr = (udp_header_t *)(raw_packet + ETH_HDR_SIZE + IP_HDR_SIZE);
255 
256  /*
257  * Check UDP layer data (L4)
258  */
259  udp_src_port = ntohs(udp_hdr->src);
260  udp_dst_port = ntohs(udp_hdr->dst);
261 
262  /*
263  * Check DHCP layer data
264  */
265  dhcp_data_len = data_len - data_offset;
266 
267  if (dhcp_data_len < MIN_PACKET_SIZE) DISCARD_RP("DHCP packet is too small (%zu < %i)",
268  dhcp_data_len, MIN_PACKET_SIZE);
269  if (dhcp_data_len > MAX_PACKET_SIZE) DISCARD_RP("DHCP packet is too large (%zu > %i)",
270  dhcp_data_len, MAX_PACKET_SIZE);
271 
272  dhcp_hdr = (dhcp_packet_t *)(raw_packet + ETH_HDR_SIZE + IP_HDR_SIZE + UDP_HDR_SIZE);
273 
274  if (dhcp_hdr->htype != 1) DISCARD_RP("DHCP hardware type (%d) != Ethernet (1)", dhcp_hdr->htype);
275  if (dhcp_hdr->hlen != 6) DISCARD_RP("DHCP hardware address length (%d) != 6", dhcp_hdr->hlen);
276 
277  magic = ntohl(dhcp_hdr->option_format);
278 
279  if (magic != DHCP_OPTION_MAGIC_NUMBER) DISCARD_RP("DHCP magic cookie (0x%04x) != DHCP (0x%04x)",
280  magic, DHCP_OPTION_MAGIC_NUMBER);
281 
282  /*
283  * Reply transaction id must match value from request.
284  */
285  xid = ntohl(dhcp_hdr->xid);
286  if (xid != (uint32_t)request->id) DISCARD_RP("DHCP transaction ID (0x%04x) != xid from request (0x%04x)",
287  xid, request->id)
288 
289  /*
290  * all checks ok! this is a DHCP reply we're interested in.
291  *
292  * dhcp_data is present to avoid what appears to coverity
293  * to be a cast from a less aligned type to a more aligned
294  * type in the fr_dhcpv4_packet_get_option() call, even though
295  * talloc_memdup() returns a pointer aligned to TALLOC_ALIGN
296  * bytes.
297  */
298  packet->data_len = dhcp_data_len;
299  dhcp_data = talloc_memdup(packet, raw_packet + data_offset, dhcp_data_len);
300  packet->data = (uint8_t *) dhcp_data;
301  TALLOC_FREE(raw_packet);
302  packet->id = xid;
303 
304  code = fr_dhcpv4_packet_get_option((dhcp_packet_t const *)dhcp_data,
305  packet->data_len, attr_dhcp_message_type);
306  if (!code) {
307  fr_strerror_const("No message-type option was found in the packet");
308  fr_packet_free(&packet);
309  return NULL;
310  }
311 
312  if ((code[1] < 1) || (code[2] == 0) || (code[2] > 8)) {
313  fr_strerror_const("Unknown value for message-type option");
314  fr_packet_free(&packet);
315  return NULL;
316  }
317 
318  packet->code = code[2];
319 
320  packet->socket.inet.src_port = udp_src_port;
321  packet->socket.inet.dst_port = udp_dst_port;
322 
323  packet->socket.inet.src_ipaddr.af = AF_INET;
324  packet->socket.inet.src_ipaddr.addr.v4.s_addr = ip_hdr->ip_src.s_addr;
325  packet->socket.inet.dst_ipaddr.af = AF_INET;
326  packet->socket.inet.dst_ipaddr.addr.v4.s_addr = ip_hdr->ip_dst.s_addr;
327 
328  return packet;
329 }
330 #endif
static fr_dict_attr_t const * attr_dhcp_message_type
Definition: dhcpclient.c:90
static int sockfd
Definition: dhcpclient.c:56
Implementation of the DHCPv4 protocol.
#define MIN_PACKET_SIZE
Definition: dhcpv4.h:88
#define ETH_ADDR_LEN
Definition: dhcpv4.h:103
#define MAX_PACKET_SIZE
Definition: dhcpv4.h:90
uint32_t option_format
Definition: dhcpv4.h:81
uint8_t htype
Definition: dhcpv4.h:68
#define DHCP_OPTION_MAGIC_NUMBER
Definition: dhcpv4.h:41
uint8_t hlen
Definition: dhcpv4.h:69
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
uint32_t xid
Definition: dhcpv4.h:71
uint8_t dst_addr[ETHER_ADDR_LEN]
Definition: ethernet.h:79
uint16_t ether_type
Definition: ethernet.h:81
uint8_t src_addr[ETHER_ADDR_LEN]
Definition: ethernet.h:80
Structure of a DEC/Intel/Xerox or 802.3 Ethernet header.
Definition: ethernet.h:78
if(rcode > 0)
Definition: fd_read.h:9
fr_packet_t * fr_packet_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new fr_packet_t.
Definition: packet.c:38
void fr_packet_free(fr_packet_t **packet_p)
Free a fr_packet_t.
Definition: packet.c:89
unsigned short uint16_t
Definition: merged_model.c:31
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
Definition: merged_model.c:93
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
uint16_t fr_udp_checksum(uint8_t const *data, uint16_t len, uint16_t checksum, struct in_addr const src_addr, struct in_addr const dst_addr)
Calculate UDP checksum.
Definition: net.c:119
uint16_t fr_ip_header_checksum(uint8_t const *data, uint8_t ihl)
Calculate IP header checksum.
Definition: net.c:154
uint8_t ip_tos
Type of service.
Definition: net.h:107
uint16_t ip_sum
Checksum.
Definition: net.h:114
uint16_t ip_len
Total length.
Definition: net.h:108
uint16_t ip_id
identification.
Definition: net.h:109
uint8_t ip_p
Protocol.
Definition: net.h:113
uint8_t ip_ttl
Time To Live.
Definition: net.h:112
uint16_t len
UDP length.
Definition: net.h:141
uint16_t ip_off
Fragment offset field.
Definition: net.h:110
uint16_t dst
Destination port.
Definition: net.h:140
uint16_t src
Source port.
Definition: net.h:139
uint16_t checksum
UDP checksum.
Definition: net.h:142
struct in_addr ip_src ip_dst
Src and Dst address.
Definition: net.h:115
#define IP_VHL(v, hl)
Definition: net.h:95
uint8_t ip_vhl
Header length, version.
Definition: net.h:105
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:693
HIDDEN fr_dict_attr_t const * attr_dhcp_client_hardware_address
Definition: base.c:49
uint8_t eth_bcast[ETH_ADDR_LEN]
Definition: base.c:164
VQP attributes.
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
close(uq->fd)
unsigned int code
Packet code (type).
Definition: packet.h:61
fr_socket_t socket
This packet was received on.
Definition: packet.h:57
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
int af
AF_INET, AF_INET6, or AF_UNIX.
Definition: socket.h:78
int fd
File descriptor if this is a live socket.
Definition: socket.h:81
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
#define fr_strerror_const(_msg)
Definition: strerror.h:223
static fr_slen_t data
Definition: value.h:1265
#define fr_box_ether(_val)
Definition: value.h:306