The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
proto_arp_ethernet.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program 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
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; 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: 7996776ccd2780d4c2a8559661641cafb4377c64 $
19 * @file proto_arp_ethernet.c
20 * @brief ARP handler for ethernet
21 *
22 * @copyright 2016 The FreeRADIUS server project.
23 * @copyright 2016 Alan DeKok (aland@deployingradius.com)
24 */
25#include <netdb.h>
26#include <freeradius-devel/server/util.h>
27#include <freeradius-devel/util/trie.h>
28#include <freeradius-devel/util/pcap.h>
29#include <freeradius-devel/io/application.h>
30#include <freeradius-devel/io/schedule.h>
31
32#include "proto_arp.h"
33
35
36typedef struct {
37 char const *name; //!< socket name
38 fr_pcap_t *pcap; //!< PCAP handler
40
41typedef struct {
42 CONF_SECTION *cs; //!< our configuration
43 char const *interface; //!< Interface to bind to.
44 char const *filter; //!< Additional PCAP filter
46
47
48/** How to parse an ARP listen section
49 *
50 */
53 interface), .dflt = "eth0" },
54
55 { FR_CONF_OFFSET("filter", proto_arp_ethernet_t, filter) },
56
58};
59
61 size_t buffer_len, size_t *leftover)
62{
64 int ret;
65 uint8_t const *data;
66 struct pcap_pkthdr *header;
67 uint8_t const *p, *end;
69
70 *leftover = 0; /* always for message oriented protocols */
71
72 ret = pcap_next_ex(thread->pcap->handle, &header, &data);
73 if (ret == 0) return 0;
74 if (ret < 0) {
75 DEBUG("Failed getting next PCAP packet");
76 return 0;
77 }
78
79 p = data;
80 end = data + header->caplen;
81
82 len = fr_pcap_link_layer_offset(data, header->caplen, thread->pcap->link_layer);
83 if (len < 0) {
84 DEBUG("Failed determining link layer header offset");
85 return 0;
86 }
87 p += len;
88
89 if ((end - p) < FR_ARP_PACKET_SIZE) {
90 DEBUG("Packet is too small (%d) to be ARP", (int) (end - p));
91 return 0;
92 }
93
94 /*
95 * Shouldn't happen.
96 */
97 if (buffer_len < FR_ARP_PACKET_SIZE) {
98 return 0;
99 }
100
101 memcpy(buffer, p, FR_ARP_PACKET_SIZE);
102
103 // @todo - talloc packet_ctx which is the ethernet header, so we know what kind of VLAN, etc. to encode?
104
105 *recv_time_p = fr_time();
106 return FR_ARP_PACKET_SIZE;
107}
108
109
112 size_t buffer_len, UNUSED size_t written)
113{
114 proto_arp_ethernet_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_arp_ethernet_thread_t);
115
116 int ret;
117 uint8_t arp_packet[64] = { 0 };
118 ethernet_header_t *eth_hdr;
119 fr_arp_packet_t *arp;
120 /* Pointer to the current position in the frame */
121 uint8_t *end = arp_packet;
122
123 /*
124 * Don't write anything.
125 */
126 if (buffer_len == 1) return buffer_len;
127
128 /* fill in Ethernet layer (L2) */
129 eth_hdr = (ethernet_header_t *)arp_packet;
130 eth_hdr->ether_type = htons(ETH_TYPE_ARP);
131 end += ETHER_ADDR_LEN + ETHER_ADDR_LEN + sizeof(eth_hdr->ether_type);
132
133 /*
134 * Just copy what FreeRADIUS has encoded for us.
135 */
136 arp = (fr_arp_packet_t *) end;
137 memcpy(arp, buffer, buffer_len);
138
139 /*
140 * Set our MAC address as the ethernet source.
141 *
142 * Set the destination MAC as the target address from
143 * ARP.
144 */
145 memcpy(eth_hdr->src_addr, thread->pcap->ether_addr, ETHER_ADDR_LEN);
146 memcpy(eth_hdr->dst_addr, arp->tha, ETHER_ADDR_LEN);
147
148 /*
149 * If we fail injecting the reply, just ignore it.
150 * Returning <0 means "close the socket", which is likely
151 * not what we want.
152 */
153 ret = pcap_inject(thread->pcap->handle, arp_packet, (end - arp_packet + buffer_len));
154 if (ret < 0) {
155 fr_strerror_printf("Error sending packet with pcap: %d, %s", ret, pcap_geterr(thread->pcap->handle));
156 return 0;
157 }
158
159 /*
160 * @todo - mirror src/protocols/dhcpv4/pcap.c for ARP send / receive.
161 * We will need that functionality for rlm_arp, too.
162 */
163
164 return FR_ARP_PACKET_SIZE;
165}
166
167/** Open a pcap file for ARP
168 *
169 */
171{
174 proto_arp_ethernet_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_arp_ethernet_thread_t);
175
176 char const *filter;
177 char *our_filter = NULL;
178 int rcode;
179
180 thread->pcap = fr_pcap_init(thread, inst->interface, PCAP_INTERFACE_IN);
181 if (!thread->pcap) {
182 cf_log_err(li->listen->cs, "Failed initializing pcap - %s", fr_strerror());
183 return -1;
184 }
185
186 rad_suid_up();
187 rcode = fr_pcap_open(thread->pcap);
189 if (rcode < 0) {
190 cf_log_err(li->listen->cs, "Failed opening interface %s - %s", inst->interface, fr_strerror());
191 return -1;
192 }
193
194 /*
195 * Ensure that we only get ARP, and an optional additional filter.
196 */
197 if (!inst->filter) {
198 filter = "arp";
199 } else {
200 MEM(filter = our_filter = talloc_asprintf(li, "arp and %s", inst->filter));
201 }
202
203 if (fr_pcap_apply_filter(thread->pcap, filter) < 0) {
204 cf_log_err(li->listen->cs, "Failed applying pcap filter '%s' - %s", filter, fr_strerror());
205 talloc_free(our_filter);
206 return -1;
207 }
208 talloc_free(our_filter);
209
210 li->fd = thread->pcap->fd;
211
212 /*
213 * Replies are injected whole, so there is nothing to wait
214 * for. A BPF device also refuses a kqueue write filter.
215 */
216 li->listen->no_write_callback = true;
217
218 fr_assert(cf_parent(inst->cs) != NULL); /* listen { ... } */
219
220 thread->name = talloc_asprintf(thread, "arp on interface %s", inst->interface);
221 return 0;
222}
223
224static char const *mod_name(fr_listen_instance_t *li)
225{
226 proto_arp_ethernet_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_arp_ethernet_thread_t);
227
228 return thread->name;
229}
230
231
232static int mod_instantiate(module_inst_ctx_t const *mctx)
233{
234 proto_arp_ethernet_t *inst = talloc_get_type_abort(mctx->mi->data, proto_arp_ethernet_t);
235
236 inst->cs = mctx->mi->conf;
237
238 return 0;
239}
240
242 .common = {
243 .magic = MODULE_MAGIC_INIT,
244 .name = "arp_ethernet",
246 .inst_size = sizeof(proto_arp_ethernet_t),
247 .thread_inst_size = sizeof(proto_arp_ethernet_thread_t),
248 .bootstrap = mod_instantiate
249 },
250 .default_message_size = FR_ARP_PACKET_SIZE,
251
252 .open = mod_open,
253 .read = mod_read,
254 .write = mod_write,
255 .get_name = mod_name,
256};
static int const char char buffer[256]
Definition acutest.h:641
module_t common
Common fields to all loadable modules.
Definition app_io.h:34
Public structure describing an I/O path for a protocol.
Definition app_io.h:33
#define ETH_TYPE_ARP
Definition arp.h:38
uint8_t tha[ETHER_ADDR_LEN]
Target hardware address.
Definition arp.h:63
#define FR_ARP_PACKET_SIZE
Definition arp.h:37
static int packet_ctx
the tests pass only the address of packet_ctx
#define UNUSED
Definition build.h:415
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:722
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:280
@ CONF_FLAG_NOT_EMPTY
CONF_PAIR is required to have a non zero length value.
Definition cf_parse.h:484
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:268
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:649
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:340
#define cf_parent(_cf)
Definition cf_util.h:115
#define MEM(x)
Definition debug.h:38
#define DEBUG(fmt,...)
Definition dhcpclient.c:38
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
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
talloc_free(hp)
void rad_suid_up(void)
Definition util.c:767
void rad_suid_down(void)
Definition util.c:771
#define fr_time()
Definition event.c:58
fr_listen_t * listen
the listener this is a socket of.
Definition listen.h:65
bool no_write_callback
sometimes we don't need to do writes
Definition listen.h:43
int fd
file descriptor for this socket - set by open
Definition listen.h:67
void const * app_io_instance
I/O path configuration context.
Definition listen.h:34
CONF_SECTION * cs
of this listener
Definition listen.h:39
void * thread_instance
thread / socket context
Definition listen.h:69
long int ssize_t
unsigned char uint8_t
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
#define ETHER_ADDR_LEN
Definition net.h:64
char const * name
socket name
char const * filter
Additional PCAP filter.
static int mod_open(fr_listen_instance_t *li)
Open a pcap file for ARP.
CONF_SECTION * cs
our configuration
static conf_parser_t const arp_listen_config[]
How to parse an ARP listen section.
fr_pcap_t * pcap
PCAP handler.
char const * interface
Interface to bind to.
static char const * mod_name(fr_listen_instance_t *li)
fr_app_io_t proto_arp_ethernet
static ssize_t mod_write(fr_listen_instance_t *li, UNUSED void *packet_ctx, UNUSED fr_time_t request_time, uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
static ssize_t mod_read(fr_listen_instance_t *li, UNUSED void **packet_ctx, fr_time_t *recv_time_p, uint8_t *buffer, size_t buffer_len, size_t *leftover)
static int mod_instantiate(module_inst_ctx_t const *mctx)
#define fr_assert(_expr)
Definition rad_assert.h:37
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:355
void * data
Module's instance data.
Definition module.h:297
conf_parser_t const * config
How to convert a CONF_SECTION to a module instance.
Definition module.h:207
eap_aka_sim_process_conf_t * inst
#define talloc_get_type_abort_const
Definition talloc.h:117
#define talloc_asprintf
Definition talloc.h:151
"server local" time.
Definition time.h:69
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
static fr_slen_t data
Definition value.h:1366
static fr_sbuff_err_t char size_t * len
Definition value.h:1061