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: ebbdd5697a299c23b6939816f572922123bdf3a8 $
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 dhcp_packet_t *dhcp;
48 /* Pointer to the current position in the frame */
49 uint8_t *end = dhcp_packet;
50 uint16_t l4_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 dhcp = (dhcp_packet_t *)end;
89 /* just copy what FreeRADIUS has encoded for us. */
90 memcpy(dhcp, packet->data, packet->data_len);
91
92 /* UDP checksum is done here */
93 udp_hdr->checksum = fr_udp_checksum((uint8_t const *)udp_hdr, l4_len, udp_hdr->checksum,
94 packet->socket.inet.src_ipaddr.addr.v4,
95 packet->socket.inet.dst_ipaddr.addr.v4);
96
97 ret = pcap_inject(pcap->handle, dhcp_packet, (end - dhcp_packet + packet->data_len));
98 if (ret < 0) {
99 fr_strerror_printf("Error sending packet with pcap: %d, %s", ret, pcap_geterr(pcap->handle));
100 return -1;
101 }
102
103 return 0;
104}
105
106/** Receive DHCP packet using PCAP
107 *
108 * @param pcap handle
109 * @return
110 * - pointer to fr_packet_t if successful.
111 * - NULL if failed.
112 */
113fr_packet_t *fr_dhcpv4_pcap_recv(fr_pcap_t *pcap)
114{
115 int ret;
116
117 uint8_t const *data;
118 ssize_t data_len;
119 fr_ipaddr_t src_ipaddr, dst_ipaddr;
120 uint16_t src_port, dst_port;
121 struct pcap_pkthdr *header;
122 ssize_t link_len, len;
123 fr_packet_t *packet;
124
125 /*
126 * Pointers into the packet data we just received
127 */
128 uint8_t const *p;
129
130 ip_header_t const *ip = NULL; /* The IP header */
131 udp_header_t const *udp; /* The UDP header */
132 uint8_t version; /* IP header version */
133
134 ret = pcap_next_ex(pcap->handle, &header, &data);
135 if (ret == 0) {
136 fr_strerror_const("No packet received from libpcap");
137 return NULL; /* no packet */
138 }
139 if (ret < 0) {
140 fr_strerror_printf("Error requesting next packet, got (%i): %s", ret, pcap_geterr(pcap->handle));
141 return NULL;
142 }
143
144 link_len = fr_pcap_link_layer_offset(data, header->caplen, pcap->link_layer);
145 if (link_len < 0) {
146 fr_strerror_const_push("Failed determining link layer header offset");
147 return NULL;
148 }
149
150 p = data;
151
152 /* Skip ethernet header */
153 p += link_len;
154
155 version = (p[0] & 0xf0) >> 4;
156 switch (version) {
157 case 4:
158 ip = (ip_header_t const *)p;
159 len = (0x0f & ip->ip_vhl) * 4; /* ip_hl specifies length in 32bit words */
160 p += len;
161 break;
162
163 case 6:
164 fr_strerror_const("IPv6 packets not supported by DHCPv4");
165 return NULL;
166
167 default:
168 fr_strerror_printf("Invalid IP version field (%i)", version);
169 return NULL;
170 }
171
172 /* Check IPv4 layer data (L3) */
173 if (ip->ip_p != IPPROTO_UDP) {
174 fr_strerror_printf("Expected IP protocol field value UDP (%i), got field value %i",
175 IPPROTO_UDP, ip->ip_p);
176 return NULL;
177 }
178
179 /*
180 * End of variable length bits, do basic check now to see if packet looks long enough
181 */
182 len = (p - data) + UDP_HDR_SIZE; /* length value */
183 if ((size_t) len > header->caplen) {
184 fr_strerror_printf("Payload (%zd) smaller than required for layers 2+3+4", len);
185 return NULL;
186 }
187
188 /*
189 * UDP header validation.
190 */
191 /* coverity[tainted_data] */
192 ret = fr_udp_header_check(p, (header->caplen - (p - data)), ip);
193 if (ret < 0) return NULL;
194
195 udp = (udp_header_t const *)p;
196 p += sizeof(udp_header_t);
197
198 data_len = ntohs(udp->len);
199
200 dst_port = ntohs(udp->dst);
201 src_port = ntohs(udp->src);
202
203 src_ipaddr.af = AF_INET;
204 src_ipaddr.addr.v4 = ip->ip_src;
205 src_ipaddr.prefix = 32;
206 src_ipaddr.scope_id = 0;
207 dst_ipaddr.af = AF_INET;
208 dst_ipaddr.addr.v4 = ip->ip_dst;
209 dst_ipaddr.prefix = 32;
210 dst_ipaddr.scope_id = 0;
211
212 if (!fr_dhcpv4_ok(p, data_len, NULL, NULL)) return NULL;
213
214 packet = fr_dhcpv4_packet_alloc(p, data_len);
215 if (!packet) return NULL;
216
217 packet->socket.inet.dst_port = dst_port;
218 packet->socket.inet.src_port = src_port;
219
220 packet->socket.inet.src_ipaddr = src_ipaddr;
221 packet->socket.inet.dst_ipaddr = dst_ipaddr;
222 packet->socket.inet.ifindex = pcap->ifindex;
223
224 packet->data = talloc_memdup(packet, p, packet->data_len);
225 packet->timestamp = fr_time_from_timeval(&header->ts);
226
227 return packet;
228}
229#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:395
#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::@130 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:240
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:1265