The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 2cd68bb0efe3e52d6e127636adce7bd5cb5a5cb8 $
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
51 /* fill in Ethernet layer (L2) */
52 eth_hdr = (ethernet_header_t *)dhcp_packet;
53 memcpy(eth_hdr->src_addr, pcap->ether_addr, ETH_ADDR_LEN);
54 memcpy(eth_hdr->dst_addr, dst_ether_addr, ETH_ADDR_LEN);
55 eth_hdr->ether_type = htons(ETH_TYPE_IP);
56 end += ETH_ADDR_LEN + ETH_ADDR_LEN + sizeof(eth_hdr->ether_type);
57
58 /* fill in IP layer (L3) */
59 ip_hdr = (ip_header_t *)(end);
60 ip_hdr->ip_vhl = IP_VHL(4, 5);
61 ip_hdr->ip_tos = 0;
62 ip_hdr->ip_len = htons(IP_HDR_SIZE + UDP_HDR_SIZE + packet->data_len);
63 ip_hdr->ip_id = 0;
64 ip_hdr->ip_off = 0;
65 ip_hdr->ip_ttl = 64;
66 ip_hdr->ip_p = 17;
67 ip_hdr->ip_sum = 0; /* Filled later */
68
69 ip_hdr->ip_src.s_addr = packet->socket.inet.src_ipaddr.addr.v4.s_addr;
70 ip_hdr->ip_dst.s_addr = packet->socket.inet.dst_ipaddr.addr.v4.s_addr;
71
72 /* IP header checksum */
73 ip_hdr->ip_sum = fr_ip_header_checksum((uint8_t const *)ip_hdr, 5);
74 end += IP_HDR_SIZE;
75
76 /* fill in UDP layer (L4) */
77 udp_hdr = (udp_header_t *)end;
78
79 udp_hdr->src = htons(packet->socket.inet.src_port);
80 udp_hdr->dst = htons(packet->socket.inet.dst_port);
81 l4_len = (UDP_HDR_SIZE + packet->data_len);
82 udp_hdr->len = htons(l4_len);
83 udp_hdr->checksum = 0; /* UDP checksum will be done after dhcp header */
84 end += UDP_HDR_SIZE;
85
86 /* DHCP layer (L7) */
87 /* just copy what FreeRADIUS has encoded for us. */
88 memcpy(end, packet->data, packet->data_len);
89
90 /* UDP checksum is done here */
91 udp_hdr->checksum = fr_udp_checksum((uint8_t const *)udp_hdr, l4_len, udp_hdr->checksum,
92 packet->socket.inet.src_ipaddr.addr.v4,
93 packet->socket.inet.dst_ipaddr.addr.v4);
94
95 ret = pcap_inject(pcap->handle, dhcp_packet, (end - dhcp_packet + packet->data_len));
96 if (ret < 0) {
97 fr_strerror_printf("Error sending packet with pcap: %d, %s", ret, pcap_geterr(pcap->handle));
98 return -1;
99 }
100
101 return 0;
102}
103
104/** Receive DHCP packet using PCAP
105 *
106 * @param pcap handle
107 * @return
108 * - pointer to fr_packet_t if successful.
109 * - NULL if failed.
110 */
111fr_packet_t *fr_dhcpv4_pcap_recv(fr_pcap_t *pcap)
112{
113 int ret;
114
115 uint8_t const *data;
116 ssize_t data_len;
117 fr_ipaddr_t src_ipaddr, dst_ipaddr;
118 uint16_t src_port, dst_port;
119 struct pcap_pkthdr *header;
120 ssize_t link_len, len;
121 fr_packet_t *packet;
122
123 /*
124 * Pointers into the packet data we just received
125 */
126 uint8_t const *p;
127
128 ip_header_t const *ip = NULL; /* The IP header */
129 udp_header_t const *udp; /* The UDP header */
130 uint8_t version; /* IP header version */
131
132 ret = pcap_next_ex(pcap->handle, &header, &data);
133 if (ret == 0) {
134 fr_strerror_const("No packet received from libpcap");
135 return NULL; /* no packet */
136 }
137 if (ret < 0) {
138 fr_strerror_printf("Error requesting next packet, got (%i): %s", ret, pcap_geterr(pcap->handle));
139 return NULL;
140 }
141
142 link_len = fr_pcap_link_layer_offset(data, header->caplen, pcap->link_layer);
143 if (link_len < 0) {
144 fr_strerror_const_push("Failed determining link layer header offset");
145 return NULL;
146 }
147
148 p = data;
149
150 /* Skip ethernet header */
151 p += link_len;
152
153 version = (p[0] & 0xf0) >> 4;
154 switch (version) {
155 case 4:
156 ip = (ip_header_t const *)p;
157 len = (0x0f & ip->ip_vhl) * 4; /* ip_hl specifies length in 32bit words */
158 p += len;
159 break;
160
161 case 6:
162 fr_strerror_const("IPv6 packets not supported by DHCPv4");
163 return NULL;
164
165 default:
166 fr_strerror_printf("Invalid IP version field (%i)", version);
167 return NULL;
168 }
169
170 /* Check IPv4 layer data (L3) */
171 if (ip->ip_p != IPPROTO_UDP) {
172 fr_strerror_printf("Expected IP protocol field value UDP (%i), got field value %i",
173 IPPROTO_UDP, ip->ip_p);
174 return NULL;
175 }
176
177 /*
178 * End of variable length bits, do basic check now to see if packet looks long enough
179 */
180 len = (p - data) + UDP_HDR_SIZE; /* length value */
181 if ((size_t) len > header->caplen) {
182 fr_strerror_printf("Payload (%zd) smaller than required for layers 2+3+4", len);
183 return NULL;
184 }
185
186 /*
187 * UDP header validation.
188 */
189 /* coverity[tainted_data] */
190 ret = fr_udp_header_check(p, (header->caplen - (p - data)), ip);
191 if (ret < 0) return NULL;
192
193 udp = (udp_header_t const *)p;
194 p += sizeof(udp_header_t);
195
196 data_len = ntohs(udp->len);
197
198 dst_port = ntohs(udp->dst);
199 src_port = ntohs(udp->src);
200
201 src_ipaddr.af = AF_INET;
202 src_ipaddr.addr.v4 = ip->ip_src;
203 src_ipaddr.prefix = 32;
204 src_ipaddr.scope_id = 0;
205 dst_ipaddr.af = AF_INET;
206 dst_ipaddr.addr.v4 = ip->ip_dst;
207 dst_ipaddr.prefix = 32;
208 dst_ipaddr.scope_id = 0;
209
210 if (!fr_dhcpv4_ok(p, data_len, NULL, NULL)) return NULL;
211
212 packet = fr_dhcpv4_packet_alloc(p, data_len);
213 if (!packet) return NULL;
214
215 packet->socket.inet.dst_port = dst_port;
216 packet->socket.inet.src_port = src_port;
217
218 packet->socket.inet.src_ipaddr = src_ipaddr;
219 packet->socket.inet.dst_ipaddr = dst_ipaddr;
220 packet->socket.inet.ifindex = pcap->ifindex;
221
222 packet->data = talloc_memdup(packet, p, packet->data_len);
223 packet->timestamp = fr_time_from_timeval(&header->ts);
224
225 return packet;
226}
227#endif /* HAVE_LIBPCAP */
Implementation of the DHCPv4 protocol.
fr_packet_t * fr_dhcpv4_packet_alloc(uint8_t const *data, ssize_t data_len)
Definition packet.c:414
#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:69
int af
Address family.
Definition inet.h:64
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:70
union fr_ipaddr_t::@136 addr
IPv4/6 prefix.
unsigned short uint16_t
long int ssize_t
unsigned char uint8_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:241
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:1290