The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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 */
58int 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 */
101int 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 */
169fr_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)",
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
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
#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
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
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
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
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