The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 47dee0495a110ff0de0eac1c21d151dae1d25678 $
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  */
40 int 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  */
113 fr_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 (%zu) 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.
#define ETH_ADDR_LEN
Definition: dhcpv4.h:120
fr_packet_t * fr_dhcpv4_packet_alloc(uint8_t const *data, ssize_t data_len)
Definition: packet.c:401
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
union fr_ipaddr_t::@121 addr
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
IPv4/6 prefix.
Definition: merged_model.c:272
unsigned short uint16_t
Definition: merged_model.c:31
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
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:215
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:894
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:1259