The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
base.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: e117e926279ba26426a55d52e293b23a72a8e86f $
19 *
20 * @file protocols/arp/base.c
21 * @brief Functions to send/receive ARP packets.
22 *
23 * @copyright 2020 Network RADIUS SAS <legal@networkradius.com?
24 */
25
26RCSID("$Id: e117e926279ba26426a55d52e293b23a72a8e86f $")
27
28#include <freeradius-devel/util/net.h>
29#include <freeradius-devel/util/struct.h>
30#include <freeradius-devel/util/syserror.h>
31#include <freeradius-devel/io/test_point.h>
32#include "attrs.h"
33
34#ifdef HAVE_LINUX_IF_PACKET_H
35# include <linux/if_packet.h>
36# include <linux/if_ether.h>
37#endif
38
39#include <net/if_arp.h>
40
41#ifdef __FreeBSD__
42#include <net/ethernet.h>
43#include <net/if_dl.h>
44#include <netlink/netlink.h>
45#include <netlink/netlink_route.h>
46#include <netlink/netlink_snl.h>
47#include <netlink/netlink_snl_route.h>
48#include <sys/param.h>
49#include <sys/linker.h>
50#include <sys/module.h>
51#endif
52
54
56
59 { .out = &dict_arp, .proto = "arp" },
60 { NULL }
61};
62
64
67 { .out = &attr_arp_packet, .name = "arp", .type = FR_TYPE_STRUCT, .dict = &dict_arp },
68 { NULL }
69};
70
71
72/*
73 * grep VALUE share/dictionary/arp/dictionary.rfc826 | grep Op | awk '{print "[" $4 "] = \"" $3 "\","}'
74 */
76 [1] = "Request",
77 [2] = "Reply",
78 [3] = "Reverse-Request",
79 [4] = "Reverse-Reply",
80#if 0
81 [5] = "DRARP-Request",
82 [6] = "DRARP-Reply",
83 [7] = "DRARP-Error",
84 [8] = "InARP-Request",
85 [9] = "InARP-Reply",
86 [10] = "ARP-NAK",
87 [11] = "MARS-Request",
88 [12] = "MARS-Multi",
89 [13] = "MARS-MServ",
90 [14] = "MARS-Join",
91 [15] = "MARS-Leave",
92 [16] = "MARS-NAK",
93 [17] = "MARS-Unserv",
94 [18] = "MARS-SJoin",
95 [19] = "MARS-SLeave",
96 [20] = "MARS-Grouplist-Request",
97 [21] = "MARS-Grouplist-Reply",
98 [22] = "MARS-Redirect-MAP",
99 [23] = "MAPOS-UNARP",
100 [24] = "OP_EXP1",
101 [25] = "OP_EXP2",
102#endif
103};
104
105static uint8_t const zeros[6] = { 0 };
106
107#ifdef SIOCSARP
108/** Forcibly add an ARP entry so we can send unicast packets to hosts that don't have IP addresses yet
109 *
110 * @param[in] fd to add arp entry on.
111 * @param[in] interface to add arp entry on.
112 * @param[in] ipaddr to insert into ARP table.
113 * @param[in] macaddr to insert into ARP table.
114 * @return
115 * - 0 on success.
116 * - -1 on failure.
117 */
118int fr_arp_entry_add(int fd, char const *interface, uint8_t ipaddr[static 4], uint8_t macaddr[static 6])
119{
120 struct sockaddr_in *sin;
121 struct arpreq req;
122
123 if (!interface) {
124 fr_strerror_const("No interface specified. Cannot update ARP table");
125 return -1;
126 }
127
128 memset(&req, 0, sizeof(req));
129 sin = (struct sockaddr_in *) &req.arp_pa;
130 sin->sin_family = AF_INET;
131 memcpy(&sin->sin_addr.s_addr, ipaddr, 4);
132
133 strlcpy(req.arp_dev, interface, sizeof(req.arp_dev));
134
135 memcpy(&req.arp_ha.sa_data, macaddr, 6);
136
137 req.arp_flags = ATF_COM;
138 if (ioctl(fd, SIOCSARP, &req) < 0) {
139 fr_strerror_printf("Failed to add entry in ARP cache: %s (%d)", fr_syserror(errno), errno);
140 return -1;
141 }
142
143 return 0;
144}
145#else
146int fr_arp_entry_add(UNUSED int fd, UNUSED char const *interface,
147 UNUSED uint8_t ipaddr[static 4], UNUSED uint8_t macaddr[static 6])
148{
149 fr_strerror_const("Adding ARP entry is unsupported on this system");
150 return -1;
151}
152#endif
153
154#ifdef __FreeBSD__
155/** Use FreeBSD netlink API to add ARP entries
156 */
157int fr_bsd_arp_entry_add(uint32_t ifindex, uint8_t ipaddr[static 4], uint8_t macaddr[static 6])
158{
159 struct sockaddr_in sin;
160 struct sockaddr_dl sdl;
161 struct snl_state state;
162 struct snl_writer nw;
163 struct nlmsghdr *msghdr;
164 struct ndmsg *msg;
165 struct snl_errmsg_data errmsg = {};
166
167 if (ifindex == 0) {
168 fr_strerror_const("Missing interface index");
169 return -1;
170 }
171
172 sin.sin_family = AF_INET;
173 memcpy(&sin.sin_addr.s_addr, ipaddr, 4);
174
175 sdl = (struct sockaddr_dl){
176 .sdl_len = sizeof(sdl),
177 .sdl_family = AF_LINK,
178 .sdl_alen = ETHER_ADDR_LEN
179 };
180 memcpy(LLADDR(&sdl), macaddr, ETHER_ADDR_LEN);
181
182 if (!snl_init(&state, NETLINK_ROUTE)) {
183 if (modfind("netlink") == -1 && errno == ENOENT) {
184 if (kldload("netlink") == -1) {
185 fr_strerror_const("netlink is not loaded and load attempt failed");
186 return -1;
187 }
188 } else {
189 open_error:
190 fr_strerror_const("Unable to open netlink socket");
191 }
192 if (!snl_init(&state, NETLINK_ROUTE)) goto open_error;
193 }
194
195 snl_init_writer(&state, &nw);
196 msghdr = snl_create_msg_request(&nw, RTM_NEWNEIGH);
197 msghdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
198
199 msg = snl_reserve_msg_object(&nw, struct ndmsg);
200 if (!msg) {
201 fr_strerror_const("Failed reserving message");
202 error:
203 snl_free(&state);
204 return -1;
205 }
206 msg->ndm_family = AF_INET;
207 msg->ndm_ifindex = ifindex;
208 msg->ndm_state = NUD_PERMANENT;
209 msg->ndm_flags = NTF_STICKY;
210
211 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)&sin);
212 snl_add_msg_attr(&nw, NDA_LLADDR, sdl.sdl_alen, LLADDR(&sdl));
213
214 if (!(msghdr = snl_finalize_msg(&nw)) || !snl_send_message(&state, msghdr)) {
215 fr_strerror_const("Failed sending netlink message.");
216 goto error;
217 }
218
219 snl_read_reply_code(&state, msghdr->nlmsg_seq, &errmsg);
220 if (errmsg.error != 0) {
221 fr_strerror_printf("Failed adding ARP table entry: %s (%s)", strerror(errmsg.error), errmsg.error_str);
222 goto error;
223 }
224
225 snl_free(&state);
226
227 return 0;
228}
229#endif
230
231/** Encode VPS into a raw ARP packet.
232 *
233 */
235{
236 ssize_t slen;
237 fr_pair_t *vp;
238 fr_arp_packet_t *arp;
239 fr_dcursor_t cursor;
240 fr_da_stack_t da_stack;
241 fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
242
243 if (fr_pair_list_empty(vps)) {
244 fr_strerror_const("Cannot encode empty packet");
245 return -1;
246 }
247
248 /*
249 * Get a cursor over the ARP attributes.
250 */
252
253 /*
254 * For simplicity, we allow the caller to omit things
255 * that they don't care about.
256 */
257 if (!vp) {
258 fr_strerror_const("No ARP attributes in the attribute list");
259 return -1;
260 }
261
263 FR_PROTO_STACK_PRINT(&da_stack, 0);
264
265 /*
266 * Call the struct encoder to do the actual work.
267 */
268 slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, &cursor, NULL, NULL, NULL);
269 if (slen <= 0) return slen;
270
271 if (slen != FR_ARP_PACKET_SIZE) return slen;
272
273 /*
274 * Hard-code fields which can be omitted.
275 */
276 arp = (fr_arp_packet_t *)fr_dbuff_start(&work_dbuff);
277
278 if ((arp->htype[0] == 0) && (arp->htype[1] == 0)) arp->htype[1] = FR_HARDWARE_FORMAT_VALUE_ETHERNET;
279 if ((arp->ptype[0] == 0) && (arp->ptype[1] == 0)) arp->ptype[0] = (FR_PROTOCOL_FORMAT_VALUE_IPV4 >> 8); /* 0x0800 */
280 if (arp->hlen == 0) arp->hlen = 6;
281 if (arp->plen == 0) arp->plen = 4;
282
283 /*
284 * The reply generally swaps Sender / Target addresses,
285 * BUT fills out the Sender-Hardware-Address with the
286 * queried MAC address. Ensure that the admin doesn't
287 * have to fill out all of the fields.
288 */
289 if (original) {
290 fr_arp_packet_t const *our_original = (fr_arp_packet_t const *) original;
291
292#define COPY(_a, _b) do { \
293 if (memcmp(arp->_a, zeros, sizeof(arp->_a)) == 0) { \
294 memcpy(arp->_a, our_original->_b, sizeof(arp->_a)); \
295 } \
296 } while (0)
297
298 COPY(spa, tpa); /* answer is about the asked-for target */
299 COPY(tha, sha); /* answer is sent to the requestor hardware address */
300 COPY(tpa, spa); /* answer is sent to the requestor protocol address */
301 }
302
303 return fr_dbuff_set(dbuff, &work_dbuff);
304}
305
306/** Decode a raw ARP packet into VPs
307 *
308 */
309ssize_t fr_arp_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *packet, size_t packet_len)
310{
311 fr_arp_packet_t const *arp;
312
313 if (packet_len < FR_ARP_PACKET_SIZE) {
314 fr_strerror_printf("Packet is too small (%d) to be ARP", (int) packet_len);
315 return -1;
316 }
317
318 /*
319 * Check that the fields have the desired values.
320 */
321 arp = (fr_arp_packet_t const *) packet;
322 if ((arp->htype[0] != 0) || (arp->htype[1] != 1)) {
323 fr_strerror_const("Hardware-Format != Ethernet");
324 return -1;
325 }
326
327 if ((arp->ptype[0] != 8) || (arp->ptype[1] != 0)) {
328 fr_strerror_const("Protocol-Format != IPv4");
329 return -1;
330 }
331
332 if (arp->hlen != 6) {
333 fr_strerror_const("Hardware-Length != 6");
334 return -1;
335 }
336
337 if (arp->plen != 4) {
338 fr_strerror_const("Protocol-Length != 4");
339 return -1;
340 }
341
342 /*
343 * If the packet is too long, we discard any extra data.
344 */
346 NULL, NULL, NULL);
347}
348
350{
351 if (instance_count > 0) {
353 return 0;
354 }
355
357
359 fail:
361 return -1;
362 }
363
366 goto fail;
367 }
368
369 return 0;
370}
371
373{
375
376 if (--instance_count > 0) return;
377
379}
380
383 .name = "arp",
384 .default_type_size = 4,
385 .default_type_length = 0,
386
387 .init = fr_arp_global_init,
388 .free = fr_arp_global_free,
389};
390
391
392typedef struct {
393 bool tmp;
395
396static int encode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict)
397{
398 fr_arp_ctx_t *test_ctx;
399
401
402 test_ctx = talloc_zero(ctx, fr_arp_ctx_t);
403 if (!test_ctx) return -1;
404
405 *out = test_ctx;
406
407 return 0;
408}
409
410
411/*
412 * Test points for protocol encode / decode
413 *
414 * Because ARP has no TLVs, we don't have test points for pair
415 * encode / decode.
416 */
417static ssize_t fr_arp_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, UNUSED void *proto_ctx)
418{
419 return fr_arp_encode(&FR_DBUFF_TMP(data, data_len), NULL, vps);
420}
421
427
429 uint8_t const *data, size_t data_len, UNUSED void *proto_ctx)
430{
431 return fr_arp_decode(ctx, out, data, data_len);
432}
433
log_entry msg
Definition acutest.h:794
uint8_t hlen
Length of hardware address.
Definition arp.h:58
uint8_t plen
Length of protocol address.
Definition arp.h:59
uint8_t htype[2]
Format of hardware address.
Definition arp.h:56
uint8_t ptype[2]
Format of protocol address.
Definition arp.h:57
@ FR_ARP_CODE_MAX
Definition arp.h:73
#define FR_ARP_PACKET_SIZE
Definition arp.h:37
char const * fr_arp_packet_codes[FR_ARP_CODE_MAX]
Definition base.c:75
#define RCSID(id)
Definition build.h:485
#define UNUSED
Definition build.h:317
#define fr_dbuff_set(_dst, _src)
Set the 'current' position in a dbuff or marker using another dbuff or marker, a char pointer,...
Definition dbuff.h:1004
#define fr_dbuff_start(_dbuff_or_marker)
Return the 'start' position of a dbuff or marker.
Definition dbuff.h:898
#define FR_DBUFF(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition dbuff.h:222
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:514
#define fr_dict_autofree(_to_free)
Definition dict.h:870
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:274
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:287
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition dict_util.c:4134
#define fr_dict_autoload(_to_load)
Definition dict.h:867
char const * name
name of this protocol
Definition dict.h:435
Specifies an attribute which must be present for the module to function.
Definition dict.h:273
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:286
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition dict.h:434
Structures and functions for parsing ethernet headers.
#define AF_LINK
Definition inet.c:1616
static uint32_t instance_count
Definition base.c:44
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
#define ETHER_ADDR_LEN
Definition net.h:64
static fr_dict_t const * dict_arp
Definition base.c:29
#define COPY(_attr)
void fr_proto_da_stack_build(fr_da_stack_t *stack, fr_dict_attr_t const *da)
Build a complete DA stack from the da back to the root.
Definition proto.c:118
static ssize_t fr_arp_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, UNUSED void *proto_ctx)
Definition base.c:417
void fr_arp_global_free(void)
Definition base.c:372
int fr_arp_global_init(void)
Definition base.c:349
int fr_arp_entry_add(UNUSED int fd, UNUSED char const *interface, UNUSED uint8_t ipaddr[static 4], UNUSED uint8_t macaddr[static 6])
Definition base.c:146
fr_dict_attr_t const * attr_arp_packet
Definition base.c:63
static uint8_t const zeros[6]
Definition base.c:105
fr_dict_protocol_t libfreeradius_arp_dict_protocol
Definition base.c:382
static int encode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict)
Definition base.c:396
ssize_t fr_arp_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *packet, size_t packet_len)
Decode a raw ARP packet into VPs.
Definition base.c:309
fr_dict_autoload_t libfreeradius_arp_dict[]
Definition base.c:58
fr_dict_attr_autoload_t libfreeradius_arp_dict_attr[]
Definition base.c:66
bool tmp
Definition base.c:393
static ssize_t fr_arp_decode_proto(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, UNUSED void *proto_ctx)
Definition base.c:428
fr_test_point_proto_encode_t arp_tp_encode_proto
Definition base.c:423
fr_test_point_proto_decode_t arp_tp_decode_proto
Definition base.c:435
ssize_t fr_arp_encode(fr_dbuff_t *dbuff, uint8_t const *original, fr_pair_list_t *vps)
Encode VPS into a raw ARP packet.
Definition base.c:234
VQP attributes.
#define fr_assert(_expr)
Definition rad_assert.h:38
fr_pair_t * vp
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
ssize_t fr_struct_to_network(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *parent_cursor, void *encode_ctx, fr_encode_dbuff_t encode_value, fr_encode_dbuff_t encode_pair)
Definition struct.c:469
ssize_t fr_struct_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len, void *decode_ctx, fr_pair_decode_value_t decode_value, fr_pair_decode_value_t decode_tlv)
Convert a STRUCT to one or more VPs.
Definition struct.c:32
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:75
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:67
Entry point for protocol decoders.
Definition test_point.h:66
Entry point for protocol encoders.
Definition test_point.h:74
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
#define fr_pair_dcursor_by_ancestor_init(_cursor, _list, _da)
Initialise a cursor that will return only attributes descended from the specified fr_dict_attr_t.
Definition pair.h:642
#define FR_PROTO_STACK_PRINT(_stack, _depth)
Definition proto.h:44
Structure for holding the stack of dictionary attributes being encoded.
Definition proto.h:55
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1291
static size_t char ** out
Definition value.h:1023