The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
pcap.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: 1ab982f156b3ebe1715cf00248a73055c2dab306 $
19 *
20 * @file protocols/dhcpv4/pcap.c
21 * @brief Alternative mechanism to send/recv DHCP packets using libpcap.
22 *
23 * @copyright 2008,2017 The FreeRADIUS server project
24 * @copyright 2008 Alan DeKok (aland@deployingradius.com)
25 */
26
27#ifdef HAVE_LIBPCAP
28#include <freeradius-devel/util/pcap.h>
29#include "dhcpv4.h"
30
31/** Send DHCP packet using PCAP
32 *
33 * @param pcap handle
34 * @param dst_ether_addr MAC address to send packet to
35 * @param packet to send
36 * @return
37 * - -1 on failure.
38 * - 0 on success.
39 */
40int fr_dhcpv4_pcap_send(fr_pcap_t *pcap, uint8_t *dst_ether_addr, fr_packet_t *packet)
41{
42 int ret;
43 uint8_t dhcp_packet[1518] = { 0 };
44 ethernet_header_t *eth_hdr;
45 ip_header_t *ip_hdr;
46 udp_header_t *udp_hdr;
47 /* Pointer to the current position in the frame */
48 uint8_t *end = dhcp_packet;
49 uint16_t l4_len;
50 size_t header_len;
51
52 /* fill in Ethernet layer (L2) */
53 eth_hdr = (ethernet_header_t *)dhcp_packet;
54 memcpy(eth_hdr->src_addr, pcap->ether_addr, ETH_ADDR_LEN);
55 memcpy(eth_hdr->dst_addr, dst_ether_addr, ETH_ADDR_LEN);
56 eth_hdr->ether_type = htons(ETH_TYPE_IP);
57 end += ETH_ADDR_LEN + ETH_ADDR_LEN + sizeof(eth_hdr->ether_type);
58
59 /* fill in IP layer (L3) */
60 ip_hdr = (ip_header_t *)(end);
61 ip_hdr->ip_vhl = IP_VHL(4, 5);
62 ip_hdr->ip_tos = 0;
63 ip_hdr->ip_len = htons(IP_HDR_SIZE + UDP_HDR_SIZE + packet->data_len);
64 ip_hdr->ip_id = 0;
65 ip_hdr->ip_off = 0;
66 ip_hdr->ip_ttl = 64;
67 ip_hdr->ip_p = 17;
68 ip_hdr->ip_sum = 0; /* Filled later */
69
70 ip_hdr->ip_src.s_addr = packet->socket.inet.src_ipaddr.addr.v4.s_addr;
71 ip_hdr->ip_dst.s_addr = packet->socket.inet.dst_ipaddr.addr.v4.s_addr;
72
73 /* IP header checksum */
74 ip_hdr->ip_sum = fr_ip_header_checksum((uint8_t const *)ip_hdr, 5);
75 end += IP_HDR_SIZE;
76
77 /* fill in UDP layer (L4) */
78 udp_hdr = (udp_header_t *)end;
79
80 udp_hdr->src = htons(packet->socket.inet.src_port);
81 udp_hdr->dst = htons(packet->socket.inet.dst_port);
82 l4_len = (UDP_HDR_SIZE + packet->data_len);
83 udp_hdr->len = htons(l4_len);
84 udp_hdr->checksum = 0; /* UDP checksum will be done after dhcp header */
85 end += UDP_HDR_SIZE;
86
87 /* DHCP layer (L7) */
88 /* just copy what FreeRADIUS has encoded for us. */
89 header_len = (size_t)(end - dhcp_packet);
90 if (packet->data_len > sizeof(dhcp_packet) - header_len) {
91 fr_strerror_printf("DHCP packet too large (%zu bytes), maximum %zu bytes",
92 packet->data_len, sizeof(dhcp_packet) - header_len);
93 return -1;
94 }
95 memcpy(end, packet->data, packet->data_len);
96
97 /* UDP checksum is done here */
98 udp_hdr->checksum = fr_udp_checksum((uint8_t const *)udp_hdr, l4_len, udp_hdr->checksum,
99 packet->socket.inet.src_ipaddr.addr.v4,
100 packet->socket.inet.dst_ipaddr.addr.v4);
101
102 ret = pcap_inject(pcap->handle, dhcp_packet, (end - dhcp_packet + packet->data_len));
103 if (ret < 0) {
104 fr_strerror_printf("Error sending packet with pcap: %d, %s", ret, pcap_geterr(pcap->handle));
105 return -1;
106 }
107
108 return 0;
109}
110
111/** Receive DHCP packet using PCAP
112 *
113 * @param pcap handle
114 * @return
115 * - pointer to fr_packet_t if successful.
116 * - NULL if failed.
117 */
118fr_packet_t *fr_dhcpv4_pcap_recv(fr_pcap_t *pcap)
119{
120 int ret;
121
122 uint8_t const *data;
123 size_t data_len;
124 fr_ipaddr_t src_ipaddr, dst_ipaddr;
125 uint16_t src_port, dst_port;
126 struct pcap_pkthdr *header;
127 ssize_t link_len, len;
128 fr_packet_t *packet;
129
130 /*
131 * Pointers into the packet data we just received
132 */
133 uint8_t const *p;
134
135 ip_header_t const *ip = NULL; /* The IP header */
136 udp_header_t const *udp; /* The UDP header */
137 uint8_t version; /* IP header version */
138
139 ret = pcap_next_ex(pcap->handle, &header, &data);
140 if (ret == 0) {
141 fr_strerror_const("No packet received from libpcap");
142 return NULL; /* no packet */
143 }
144 if (ret < 0) {
145 fr_strerror_printf("Error requesting next packet, got (%i): %s", ret, pcap_geterr(pcap->handle));
146 return NULL;
147 }
148
149 link_len = fr_pcap_link_layer_offset(data, header->caplen, pcap->link_layer);
150 if (link_len < 0) {
151 fr_strerror_const_push("Failed determining link layer header offset");
152 return NULL;
153 }
154
155 p = data;
156
157 /* Skip ethernet header */
158 p += link_len;
159
160 if (p >= (data + header->caplen)) {
161 fr_strerror_const("No IP protocol packet after link layer header");
162 return NULL;
163 }
164
165 version = (p[0] & 0xf0) >> 4;
166 switch (version) {
167 case 4:
168 ip = (ip_header_t const *)p;
169 len = (0x0f & ip->ip_vhl) * 4; /* ip_hl specifies length in 32bit words */
170 p += len;
171 break;
172
173 case 6:
174 fr_strerror_const("IPv6 packets not supported by DHCPv4");
175 return NULL;
176
177 default:
178 fr_strerror_printf("Invalid IP version field (%i)", version);
179 return NULL;
180 }
181
182 /* Check IPv4 layer data (L3) */
183 if (ip->ip_p != IPPROTO_UDP) {
184 fr_strerror_printf("Expected IP protocol field value UDP (%i), got field value %i",
185 IPPROTO_UDP, ip->ip_p);
186 return NULL;
187 }
188
189 /*
190 * End of variable length bits, do basic check now to see if packet looks long enough
191 */
192 len = (p - data) + UDP_HDR_SIZE; /* length value */
193 if ((size_t) len > header->caplen) {
194 fr_strerror_printf("Payload (%zd) smaller than required for layers 2+3+4", len);
195 return NULL;
196 }
197
198 /*
199 * UDP header validation.
200 */
201 /* coverity[tainted_data] */
202 ret = fr_udp_header_check(p, (header->caplen - (p - data)), ip);
203 if (ret < 0) return NULL;
204
205 udp = (udp_header_t const *)p;
206 p += sizeof(udp_header_t);
207
208 data_len = ntohs(udp->len);
209 if (data_len <= sizeof(udp_header_t)) {
210 fr_strerror_printf("UDP header length (%zd) smaller than required for layers 2+3+4", data_len);
211 return NULL;
212 }
213 data_len -= sizeof(udp_header_t);
214
215 dst_port = ntohs(udp->dst);
216 src_port = ntohs(udp->src);
217
218 src_ipaddr.af = AF_INET;
219 src_ipaddr.addr.v4 = ip->ip_src;
220 src_ipaddr.prefix = 32;
221 src_ipaddr.scope_id = 0;
222 dst_ipaddr.af = AF_INET;
223 dst_ipaddr.addr.v4 = ip->ip_dst;
224 dst_ipaddr.prefix = 32;
225 dst_ipaddr.scope_id = 0;
226
227 if (!fr_dhcpv4_ok(p, data_len, NULL, NULL)) return NULL;
228
229 packet = fr_dhcpv4_packet_alloc(p, data_len);
230 if (!packet) return NULL;
231
232 packet->socket.inet.dst_port = dst_port;
233 packet->socket.inet.src_port = src_port;
234
235 packet->socket.inet.src_ipaddr = src_ipaddr;
236 packet->socket.inet.dst_ipaddr = dst_ipaddr;
237 packet->socket.inet.ifindex = pcap->ifindex;
238
239 packet->data = talloc_memdup(packet, p, packet->data_len);
240 packet->timestamp = fr_time_from_timeval(&header->ts);
241
242 return packet;
243}
244#endif /* HAVE_LIBPCAP */
Implementation of the DHCPv4 protocol.
fr_packet_t * fr_dhcpv4_packet_alloc(uint8_t const *data, size_t data_len)
Definition packet.c:418
#define ETH_ADDR_LEN
Definition dhcpv4.h:103
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
uint8_t prefix
Prefix length - Between 0-32 for IPv4 and 0-128 for IPv6.
Definition inet.h:68
int af
Address family.
Definition inet.h:63
uint32_t scope_id
A host may have multiple link-local interfaces the scope ID allows the application to specify which o...
Definition inet.h:69
union fr_ipaddr_t::@137 addr
IPv4/6 prefix.
unsigned short uint16_t
long int ssize_t
unsigned char uint8_t
unsigned long int size_t
int fr_udp_header_check(uint8_t const *data, uint16_t remaining, ip_header_t const *ip)
Check UDP header is valid.
Definition net.c:64
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
bool fr_dhcpv4_ok(uint8_t const *data, ssize_t data_len, uint8_t *message_type, uint32_t *xid)
Check received DHCP request is valid and build fr_packet_t structure if it is.
Definition base.c:248
static fr_time_t fr_time_from_timeval(struct timeval const *when_tv)
Convert a timeval (wallclock time) to a fr_time_t (internal time)
Definition time.h:896
fr_socket_t socket
This packet was received on.
Definition packet.h:57
uint8_t * data
Packet data (body).
Definition packet.h:63
size_t data_len
Length of packet data.
Definition packet.h:64
fr_time_t timestamp
When we received the packet.
Definition packet.h:58
#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
static fr_slen_t data
Definition value.h:1340