The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 6e27fac8d8033e27152d3b777dff2b6f030f8239 $
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/protocol.h>
27 #include <freeradius-devel/util/trie.h>
28 #include <freeradius-devel/io/application.h>
29 #include <freeradius-devel/io/listen.h>
30 #include <freeradius-devel/io/schedule.h>
31 
32 #include "proto_arp.h"
33 
35 
36 typedef struct {
37  char const *name; //!< socket name
38  fr_pcap_t *pcap; //!< PCAP handler
40 
41 typedef 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  */
51 static conf_parser_t const arp_listen_config[] = {
53  interface), .dflt = "eth0" },
54 
55  { FR_CONF_OFFSET("filter", proto_arp_ethernet_t, filter) },
56 
58 };
59 
60 static ssize_t mod_read(fr_listen_t *li, UNUSED void **packet_ctx, fr_time_t *recv_time_p, uint8_t *buffer, size_t buffer_len, size_t *leftover)
61 {
62  proto_arp_ethernet_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_arp_ethernet_thread_t);
63  int ret;
64  uint8_t const *data;
65  struct pcap_pkthdr *header;
66  uint8_t const *p, *end;
67  ssize_t len;
68 
69  *leftover = 0; /* always for message oriented protocols */
70 
71  ret = pcap_next_ex(thread->pcap->handle, &header, &data);
72  if (ret == 0) return 0;
73  if (ret < 0) {
74  DEBUG("Failed getting next PCAP packet");
75  return 0;
76  }
77 
78  p = data;
79  end = data + header->caplen;
80 
81  len = fr_pcap_link_layer_offset(data, header->caplen, thread->pcap->link_layer);
82  if (len < 0) {
83  DEBUG("Failed determining link layer header offset");
84  return 0;
85  }
86  p += len;
87 
88  if ((end - p) < FR_ARP_PACKET_SIZE) {
89  DEBUG("Packet is too small (%d) to be ARP", (int) (end - p));
90  return 0;
91  }
92 
93  /*
94  * Shouldn't happen.
95  */
96  if (buffer_len < FR_ARP_PACKET_SIZE) {
97  return 0;
98  }
99 
100  memcpy(buffer, p, FR_ARP_PACKET_SIZE);
101 
102  // @todo - talloc packet_ctx which is the ethernet header, so we know what kind of VLAN, etc. to encode?
103 
104  *recv_time_p = fr_time();
105  return FR_ARP_PACKET_SIZE;
106 }
107 
108 
109 static ssize_t mod_write(fr_listen_t *li, UNUSED void *packet_ctx, UNUSED fr_time_t request_time,
110  uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
111 {
112  proto_arp_ethernet_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_arp_ethernet_thread_t);
113 
114  int ret;
115  uint8_t arp_packet[64] = { 0 };
116  ethernet_header_t *eth_hdr;
117  fr_arp_packet_t *arp;
118  /* Pointer to the current position in the frame */
119  uint8_t *end = arp_packet;
120 
121  /*
122  * Don't write anything.
123  */
124  if (buffer_len == 1) return buffer_len;
125 
126  /* fill in Ethernet layer (L2) */
127  eth_hdr = (ethernet_header_t *)arp_packet;
128  eth_hdr->ether_type = htons(ETH_TYPE_ARP);
129  end += ETHER_ADDR_LEN + ETHER_ADDR_LEN + sizeof(eth_hdr->ether_type);
130 
131  /*
132  * Just copy what FreeRADIUS has encoded for us.
133  */
134  arp = (fr_arp_packet_t *) end;
135  memcpy(arp, buffer, buffer_len);
136 
137  /*
138  * Set our MAC address as the ethernet source.
139  *
140  * Set the destination MAC as the target address from
141  * ARP.
142  */
143  memcpy(eth_hdr->src_addr, thread->pcap->ether_addr, ETHER_ADDR_LEN);
144  memcpy(eth_hdr->dst_addr, arp->tha, ETHER_ADDR_LEN);
145 
146  /*
147  * If we fail injecting the reply, just ignore it.
148  * Returning <0 means "close the socket", which is likely
149  * not what we want.
150  */
151  ret = pcap_inject(thread->pcap->handle, arp_packet, (end - arp_packet + buffer_len));
152  if (ret < 0) {
153  fr_strerror_printf("Error sending packet with pcap: %d, %s", ret, pcap_geterr(thread->pcap->handle));
154  return 0;
155  }
156 
157  /*
158  * @todo - mirror src/protocols/dhcpv4/pcap.c for ARP send / receive.
159  * We will need that functionality for rlm_arp, too.
160  */
161 
162  return FR_ARP_PACKET_SIZE;
163 }
164 
165 /** Open a pcap file for ARP
166  *
167  */
168 static int mod_open(fr_listen_t *li)
169 {
171  proto_arp_ethernet_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_arp_ethernet_thread_t);
172 
173  char const *filter;
174  char *our_filter = NULL;
175 
176  thread->pcap = fr_pcap_init(thread, inst->interface, PCAP_INTERFACE_IN);
177  if (!thread->pcap) {
178  PERROR("Failed initializing pcap handle.");
179  return -1;
180  }
181 
182  if (fr_pcap_open(thread->pcap) < 0) {
183  PERROR("Failed opening interface %s", inst->interface);
184  return -1;
185  }
186 
187  /*
188  * Ensure that we only get ARP, and an optional additional filter.
189  */
190  if (!inst->filter) {
191  filter = "arp";
192  } else {
193  MEM(filter = our_filter = talloc_asprintf(li, "arp and %s", inst->filter));
194  }
195 
196  if (fr_pcap_apply_filter(thread->pcap, filter) < 0) {
197  PERROR("Failed applying pcap filter '%s'", filter);
198  talloc_free(our_filter);
199  return -1;
200  }
201  talloc_free(our_filter);
202 
203  li->fd = thread->pcap->fd;
204 
205  fr_assert(cf_parent(inst->cs) != NULL); /* listen { ... } */
206 
207  thread->name = talloc_asprintf(thread, "arp on interface %s", inst->interface);
208  return 0;
209 }
210 
211 static char const *mod_name(fr_listen_t *li)
212 {
213  proto_arp_ethernet_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_arp_ethernet_thread_t);
214 
215  return thread->name;
216 }
217 
218 
219 static int mod_bootstrap(module_inst_ctx_t const *mctx)
220 {
221  proto_arp_ethernet_t *inst = talloc_get_type_abort(mctx->inst->data, proto_arp_ethernet_t);
222 
223  inst->cs = mctx->inst->conf;
224 
225  return 0;
226 }
227 
228 
230  .common = {
231  .magic = MODULE_MAGIC_INIT,
232  .name = "arp_ethernet",
233  .config = arp_listen_config,
234  .inst_size = sizeof(proto_arp_ethernet_t),
235  .thread_inst_size = sizeof(proto_arp_ethernet_thread_t),
236  .bootstrap = mod_bootstrap,
237  },
238  .default_message_size = FR_ARP_PACKET_SIZE,
239 
240  .open = mod_open,
241  .read = mod_read,
242  .write = mod_write,
243  .get_name = mod_name,
244 };
static int const char char buffer[256]
Definition: acutest.h:574
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:59
#define FR_ARP_PACKET_SIZE
Definition: arp.h:37
#define UNUSED
Definition: build.h:313
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#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:268
#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:256
@ CONF_FLAG_NOT_EMPTY
CONF_PAIR is required to have a non zero length value.
Definition: cf_parse.h:421
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:563
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
#define cf_parent(_cf)
Definition: cf_util.h:98
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
CONF_SECTION *_CONST conf
Module's instance configuration.
Definition: dl_module.h:166
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
void const * app_io_instance
I/O path configuration context.
Definition: listen.h:32
void * thread_instance
thread / socket context
Definition: listen.h:33
int fd
file descriptor for this socket - set by open
Definition: listen.h:28
#define PERROR(_fmt,...)
Definition: log.h:228
talloc_free(reap)
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:51
#define ETHER_ADDR_LEN
Definition: net.h:64
char const * name
socket name
static ssize_t mod_write(fr_listen_t *li, UNUSED void *packet_ctx, UNUSED fr_time_t request_time, uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
char const * filter
Additional PCAP filter.
static int mod_bootstrap(module_inst_ctx_t const *mctx)
CONF_SECTION * cs
our configuration
static conf_parser_t const arp_listen_config[]
How to parse an ARP listen section.
static int mod_open(fr_listen_t *li)
Open a pcap file for ARP.
fr_pcap_t * pcap
PCAP handler.
static char const * mod_name(fr_listen_t *li)
char const * interface
Interface to bind to.
static ssize_t mod_read(fr_listen_t *li, UNUSED void **packet_ctx, fr_time_t *recv_time_p, uint8_t *buffer, size_t buffer_len, size_t *leftover)
fr_app_io_t proto_arp_ethernet
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition: state_test.c:8
#define talloc_get_type_abort_const
Definition: talloc.h:270
"server local" time.
Definition: time.h:69
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
static fr_slen_t data
Definition: value.h:1259