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: f3b58faa30befebf3f9fbb5d56834cfd6c9c07ab $
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: f3b58faa30befebf3f9fbb5d56834cfd6c9c07ab $")
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
42
44
47 { .out = &dict_arp, .proto = "arp" },
48 { NULL }
49};
50
52
55 { .out = &attr_arp_packet, .name = "arp", .type = FR_TYPE_STRUCT, .dict = &dict_arp },
56 { NULL }
57};
58
59
60/*
61 * grep VALUE share/dictionary/arp/dictionary.rfc826 | grep Op | awk '{print "[" $4 "] = \"" $3 "\","}'
62 */
64 [1] = "Request",
65 [2] = "Reply",
66 [3] = "Reverse-Request",
67 [4] = "Reverse-Reply",
68#if 0
69 [5] = "DRARP-Request",
70 [6] = "DRARP-Reply",
71 [7] = "DRARP-Error",
72 [8] = "InARP-Request",
73 [9] = "InARP-Reply",
74 [10] = "ARP-NAK",
75 [11] = "MARS-Request",
76 [12] = "MARS-Multi",
77 [13] = "MARS-MServ",
78 [14] = "MARS-Join",
79 [15] = "MARS-Leave",
80 [16] = "MARS-NAK",
81 [17] = "MARS-Unserv",
82 [18] = "MARS-SJoin",
83 [19] = "MARS-SLeave",
84 [20] = "MARS-Grouplist-Request",
85 [21] = "MARS-Grouplist-Reply",
86 [22] = "MARS-Redirect-MAP",
87 [23] = "MAPOS-UNARP",
88 [24] = "OP_EXP1",
89 [25] = "OP_EXP2",
90#endif
91};
92
93static uint8_t const zeros[6] = { 0 };
94
95#ifdef SIOCSARP
96/** Forcibly add an ARP entry so we can send unicast packets to hosts that don't have IP addresses yet
97 *
98 * @param[in] fd to add arp entry on.
99 * @param[in] interface to add arp entry on.
100 * @param[in] ipaddr to insert into ARP table.
101 * @param[in] macaddr to insert into ARP table.
102 * @return
103 * - 0 on success.
104 * - -1 on failure.
105 */
106int fr_arp_entry_add(int fd, char const *interface, uint8_t ipaddr[static 4], uint8_t macaddr[static 6])
107{
108 struct sockaddr_in *sin;
109 struct arpreq req;
110
111 if (!interface) {
112 fr_strerror_const("No interface specified. Cannot update ARP table");
113 return -1;
114 }
115
116 memset(&req, 0, sizeof(req));
117 sin = (struct sockaddr_in *) &req.arp_pa;
118 sin->sin_family = AF_INET;
119 memcpy(&sin->sin_addr.s_addr, ipaddr, 4);
120
121 strlcpy(req.arp_dev, interface, sizeof(req.arp_dev));
122
123 memcpy(&req.arp_ha.sa_data, macaddr, 6);
124
125 req.arp_flags = ATF_COM;
126 if (ioctl(fd, SIOCSARP, &req) < 0) {
127 fr_strerror_printf("Failed to add entry in ARP cache: %s (%d)", fr_syserror(errno), errno);
128 return -1;
129 }
130
131 return 0;
132}
133#else
134int fr_arp_entry_add(UNUSED int fd, UNUSED char const *interface,
135 UNUSED uint8_t ipaddr[static 4], UNUSED uint8_t macaddr[static 6])
136{
137 fr_strerror_const("Adding ARP entry is unsupported on this system");
138 return -1;
139}
140#endif
141
142
143/** Encode VPS into a raw ARP packet.
144 *
145 */
147{
148 ssize_t slen;
149 fr_pair_t *vp;
150 fr_arp_packet_t *arp;
151 fr_dcursor_t cursor;
152 fr_da_stack_t da_stack;
153 fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
154
155 if (fr_pair_list_empty(vps)) {
156 fr_strerror_const("Cannot encode empty packet");
157 return -1;
158 }
159
160 /*
161 * Get a cursor over the ARP attributes.
162 */
164
165 /*
166 * For simplicity, we allow the caller to omit things
167 * that they don't care about.
168 */
169 if (!vp) {
170 fr_strerror_const("No ARP attributes in the attribute list");
171 return -1;
172 }
173
175 FR_PROTO_STACK_PRINT(&da_stack, 0);
176
177 /*
178 * Call the struct encoder to do the actual work.
179 */
180 slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, &cursor, NULL, NULL, NULL);
181 if (slen <= 0) return slen;
182
183 if (slen != FR_ARP_PACKET_SIZE) return slen;
184
185 /*
186 * Hard-code fields which can be omitted.
187 */
188 arp = (fr_arp_packet_t *)fr_dbuff_start(&work_dbuff);
189
190 if ((arp->htype[0] == 0) && (arp->htype[1] == 0)) arp->htype[1] = FR_HARDWARE_FORMAT_VALUE_ETHERNET;
191 if ((arp->ptype[0] == 0) && (arp->ptype[1] == 0)) arp->ptype[0] = (FR_PROTOCOL_FORMAT_VALUE_IPV4 >> 8); /* 0x0800 */
192 if (arp->hlen == 0) arp->hlen = 6;
193 if (arp->plen == 0) arp->plen = 4;
194
195 /*
196 * The reply generally swaps Sender / Target addresses,
197 * BUT fills out the Sender-Hardware-Address with the
198 * queried MAC address. Ensure that the admin doesn't
199 * have to fill out all of the fields.
200 */
201 if (original) {
202 fr_arp_packet_t const *our_original = (fr_arp_packet_t const *) original;
203
204#define COPY(_a, _b) do { \
205 if (memcmp(arp->_a, zeros, sizeof(arp->_a)) == 0) { \
206 memcpy(arp->_a, our_original->_b, sizeof(arp->_a)); \
207 } \
208 } while (0)
209
210 COPY(spa, tpa); /* answer is about the asked-for target */
211 COPY(tha, sha); /* answer is sent to the requestor hardware address */
212 COPY(tpa, spa); /* answer is sent to the requestor protocol address */
213 }
214
215 return fr_dbuff_set(dbuff, &work_dbuff);
216}
217
218/** Decode a raw ARP packet into VPs
219 *
220 */
221ssize_t fr_arp_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *packet, size_t packet_len)
222{
223 fr_arp_packet_t const *arp;
224
225 if (packet_len < FR_ARP_PACKET_SIZE) {
226 fr_strerror_printf("Packet is too small (%d) to be ARP", (int) packet_len);
227 return -1;
228 }
229
230 /*
231 * Check that the fields have the desired values.
232 */
233 arp = (fr_arp_packet_t const *) packet;
234 if ((arp->htype[0] != 0) || (arp->htype[1] != 1)) {
235 fr_strerror_const("Hardware-Format != Ethernet");
236 return -1;
237 }
238
239 if ((arp->ptype[0] != 8) || (arp->ptype[1] != 0)) {
240 fr_strerror_const("Protocol-Format != IPv4");
241 return -1;
242 }
243
244 if (arp->hlen != 6) {
245 fr_strerror_const("Hardware-Length != 6");
246 return -1;
247 }
248
249 if (arp->plen != 4) {
250 fr_strerror_const("Protocol-Length != 4");
251 return -1;
252 }
253
254 /*
255 * If the packet is too long, we discard any extra data.
256 */
258 NULL, NULL, NULL);
259}
260
262{
263 if (instance_count > 0) {
265 return 0;
266 }
267
269
271 fail:
273 return -1;
274 }
275
278 goto fail;
279 }
280
281 return 0;
282}
283
285{
287
288 if (--instance_count > 0) return;
289
291}
292
295 .name = "arp",
296 .default_type_size = 4,
297 .default_type_length = 0,
298
299 .init = fr_arp_global_init,
300 .free = fr_arp_global_free,
301};
302
303
304typedef struct {
305 bool tmp;
307
308static int encode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict)
309{
310 fr_arp_ctx_t *test_ctx;
311
313
314 test_ctx = talloc_zero(ctx, fr_arp_ctx_t);
315 if (!test_ctx) return -1;
316
317 *out = test_ctx;
318
319 return 0;
320}
321
322
323/*
324 * Test points for protocol encode / decode
325 *
326 * Because ARP has no TLVs, we don't have test points for pair
327 * encode / decode.
328 */
329static 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)
330{
331 return fr_arp_encode(&FR_DBUFF_TMP(data, data_len), NULL, vps);
332}
333
339
341 uint8_t const *data, size_t data_len, UNUSED void *proto_ctx)
342{
343 return fr_arp_decode(ctx, out, data, data_len);
344}
345
uint8_t hlen
Length of hardware address.
Definition arp.h:54
uint8_t plen
Length of protocol address.
Definition arp.h:55
uint8_t htype[2]
Format of hardware address.
Definition arp.h:52
uint8_t ptype[2]
Format of protocol address.
Definition arp.h:53
@ FR_ARP_CODE_MAX
Definition arp.h:69
#define FR_ARP_PACKET_SIZE
Definition arp.h:37
char const * fr_arp_packet_codes[FR_ARP_CODE_MAX]
Definition base.c:63
#define RCSID(id)
Definition build.h:483
#define UNUSED
Definition build.h:315
#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:852
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:268
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:281
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:4094
#define fr_dict_autoload(_to_load)
Definition dict.h:849
char const * name
name of this protocol
Definition dict.h:429
Specifies an attribute which must be present for the module to function.
Definition dict.h:267
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:280
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition dict.h:428
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
static fr_dict_t const * dict_arp
Definition base.c:28
#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:329
void fr_arp_global_free(void)
Definition base.c:284
int fr_arp_global_init(void)
Definition base.c:261
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:134
fr_dict_attr_t const * attr_arp_packet
Definition base.c:51
static uint8_t const zeros[6]
Definition base.c:93
fr_dict_protocol_t libfreeradius_arp_dict_protocol
Definition base.c:294
static int encode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict)
Definition base.c:308
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:221
fr_dict_autoload_t libfreeradius_arp_dict[]
Definition base.c:46
fr_dict_attr_autoload_t libfreeradius_arp_dict_attr[]
Definition base.c:54
bool tmp
Definition base.c:305
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:340
fr_test_point_proto_encode_t arp_tp_encode_proto
Definition base.c:335
fr_test_point_proto_decode_t arp_tp_decode_proto
Definition base.c:347
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:146
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:470
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:33
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:646
#define FR_PROTO_STACK_PRINT(_stack, _depth)
Definition proto.h:43
Structure for holding the stack of dictionary attributes being encoded.
Definition proto.h:54
#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:1265
static size_t char ** out
Definition value.h:997