The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 1ae2e63121095920132e006e3fe93da6b7d77768 $
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 
26 RCSID("$Id: 1ae2e63121095920132e006e3fe93da6b7d77768 $")
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 
93 static 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  */
106 int 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
134 int 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  */
146 ssize_t fr_arp_encode(fr_dbuff_t *dbuff, uint8_t const *original, fr_pair_list_t *vps)
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  */
221 ssize_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  */
257  return fr_struct_from_network(ctx, out, attr_arp_packet, packet, FR_ARP_PACKET_SIZE, true,
258  NULL, NULL, NULL);
259 }
260 
262 {
263  if (instance_count > 0) {
264  instance_count++;
265  return 0;
266  }
267 
268  instance_count++;
269 
271  fail:
272  instance_count--;
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 
304 typedef struct {
305  bool tmp;
306 } fr_arp_ctx_t;
307 
308 static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
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  */
329 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)
330 {
331  return fr_arp_encode(&FR_DBUFF_TMP(data, data_len), NULL, vps);
332 }
333 
337  .func = fr_arp_encode_proto
338 };
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 
349  .func = fr_arp_decode_proto
350 };
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
#define RCSID(id)
Definition: build.h:444
#define UNUSED
Definition: build.h:313
#define fr_dbuff_start(_dbuff_or_marker)
Return the 'start' position of a dbuff or marker.
Definition: dbuff.h:893
#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:509
#define fr_dict_autofree(_to_free)
Definition: dict.h:674
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:250
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:263
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:3647
#define fr_dict_autoload(_to_load)
Definition: dict.h:671
char const * name
name of this protocol
Definition: dict.h:342
Specifies an attribute which must be present for the module to function.
Definition: dict.h:249
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:262
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition: dict.h:341
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
Definition: merged_model.c:119
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
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
#define COPY(_a, _b)
void fr_arp_global_free(void)
Definition: base.c:284
static uint32_t instance_count
Definition: base.c:41
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
fr_dict_t const * dict_arp
Definition: base.c:43
static uint8_t const zeros[6]
Definition: base.c:93
fr_dict_protocol_t libfreeradius_arp_dict_protocol
Definition: base.c:294
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
static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
Definition: base.c:308
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
char const * fr_arp_packet_codes[FR_ARP_CODE_MAX]
Definition: base.c:63
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.
fr_assert(0)
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:478
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, bool nested, 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:74
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition: test_point.h:66
Entry point for protocol decoders.
Definition: test_point.h:65
Entry point for protocol encoders.
Definition: test_point.h:73
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
Definition: pair_inline.c:125
#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:645
#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
return fr_dbuff_set(dbuff, &our_dbuff)
static fr_slen_t data
Definition: value.h:1259
static size_t char ** out
Definition: value.h:984