Coverage Report

Created: 2026-05-11 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/protocols/bfd/base.c
Line
Count
Source
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: 1147d41d3af04d286891788b82baca5f2106cd0c $
19
 *
20
 * @file protocols/bfd/base.c
21
 * @brief Functions to send/receive BFD packets.
22
 *
23
 * @copyright 2023 Network RADIUS SAS (legal@networkradius.com)
24
 */
25
26
RCSID("$Id: 1147d41d3af04d286891788b82baca5f2106cd0c $")
27
28
#include <fcntl.h>
29
#include <ctype.h>
30
31
#include "attrs.h"
32
33
#include <freeradius-devel/io/pair.h>
34
#include <freeradius-devel/util/net.h>
35
#include <freeradius-devel/util/proto.h>
36
#include <freeradius-devel/util/udp.h>
37
38
static uint32_t instance_count = 0;
39
static bool instantiated = false;
40
41
fr_dict_t const *dict_freeradius;
42
fr_dict_t const *dict_bfd;
43
44
extern fr_dict_autoload_t libfreeradius_bfd_dict[];
45
fr_dict_autoload_t libfreeradius_bfd_dict[] = {
46
  { .out = &dict_freeradius, .proto = "freeradius" },
47
  { .out = &dict_bfd, .proto = "bfd" },
48
  DICT_AUTOLOAD_TERMINATOR
49
};
50
51
fr_dict_attr_t const *attr_packet_type;
52
fr_dict_attr_t const *attr_bfd_packet;
53
fr_dict_attr_t const *attr_bfd_additional_data;
54
55
extern fr_dict_attr_autoload_t libfreeradius_bfd_dict_attr[];
56
fr_dict_attr_autoload_t libfreeradius_bfd_dict_attr[] = {
57
  { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_bfd },
58
  { .out = &attr_bfd_packet, .name = "Packet", .type = FR_TYPE_STRUCT, .dict = &dict_bfd },
59
  { .out = &attr_bfd_additional_data, .name = "Additional-Data", .type = FR_TYPE_GROUP, .dict = &dict_bfd },
60
61
  DICT_AUTOLOAD_TERMINATOR
62
};
63
64
char const *fr_bfd_packet_names[FR_BFD_CODE_MAX] = {
65
  "Admin-Down",
66
  "Down",
67
  "Init",
68
  "Up",
69
};
70
71
fr_table_num_ordered_t const bfd_auth_type_table[] = {
72
  { L("none"),    BFD_AUTH_RESERVED   },
73
  { L("simple"),    BFD_AUTH_SIMPLE     },
74
  { L("keyed-md5"), BFD_AUTH_KEYED_MD5    },
75
  { L("met-keyed-md5"), BFD_AUTH_MET_KEYED_MD5    },
76
  { L("keyed-sha1"),  BFD_AUTH_KEYED_SHA1   },
77
  { L("met-keyed-sha1"),  BFD_AUTH_MET_KEYED_SHA1   },
78
};
79
size_t const bfd_auth_type_table_len = NUM_ELEMENTS(bfd_auth_type_table);
80
81
/*
82
 *  Perform basic packet checks as per the first part of RFC 5880 Section 6.8.6.
83
 */
84
bool fr_bfd_packet_ok(char const **err, uint8_t const *packet, size_t packet_len)
85
0
{
86
0
  bfd_packet_t const *bfd;
87
0
  char const *msg = NULL;
88
89
0
  if (packet_len < FR_BFD_HEADER_LENGTH) {
90
0
    msg = "Packet is too short to be BFD";
91
0
  fail:
92
0
    if (err) *err = msg;
93
0
    return false;
94
0
  }
95
96
0
  bfd = (bfd_packet_t const *) packet;
97
98
  /*
99
   *  If the version number is not correct (1), the packet MUST be
100
   *  discarded.
101
   */
102
0
  if (bfd->version != 1) {
103
0
    msg = "Packet has wrong version - should be 1";
104
0
    goto fail;
105
0
  }
106
107
  /*
108
   *  If the Length field is less than the minimum correct value (24 if
109
   *  the A bit is clear, or 26 if the A bit is set), the packet MUST be
110
   *  discarded.
111
   */
112
0
  if (bfd->length < FR_BFD_HEADER_LENGTH) {
113
0
    msg = "Header length is too small";
114
0
    goto fail;
115
0
  }
116
117
  /*
118
   *  If the Length field is greater than the payload of the
119
   *  encapsulating protocol, the packet MUST be discarded.
120
   *
121
   *  Addendum: if the Length field is smaller than the
122
   *  received data, that's bad, too.
123
   */
124
0
  if (bfd->length > packet_len) {
125
0
    msg = "Header length is not the same as the amount of data we read";
126
0
    goto fail;
127
0
  }
128
129
  /*
130
   *  If the Length field is less than the minimum correct value (24 if
131
   *  the A bit is clear, or 26 if the A bit is set), the packet MUST be
132
   *  discarded.
133
   *
134
   *  Addendum: if the Length field is not equal to 24 plus Auth-Len field,
135
   *  then the packet is discarded.
136
   */
137
0
  if (bfd->auth_present) {
138
0
    if (bfd->length < (FR_BFD_HEADER_LENGTH + 2)) { /* auth-type and auth-len */
139
0
      msg = "Header length is not enough for auth-type and auth-len";
140
0
      goto fail;
141
0
    }
142
143
0
    if (bfd->length != FR_BFD_HEADER_LENGTH + bfd->auth.basic.auth_len) {
144
0
      msg = "Header length mismatch with auth-len and amount of received data";
145
0
      goto fail;
146
147
0
    }
148
149
0
    switch (bfd->auth.basic.auth_type) {
150
0
    case BFD_AUTH_SIMPLE:
151
0
      if ((bfd->auth.basic.auth_len < (3 + 1)) || (bfd->auth.basic.auth_len > (3 + 16))) {
152
0
        msg = "Auth-Type Simple has invalid value for password length";
153
0
        goto fail;
154
0
      }
155
0
      break;
156
157
0
    case BFD_AUTH_KEYED_MD5:
158
0
    case BFD_AUTH_MET_KEYED_MD5:
159
0
      if (bfd->auth.basic.auth_len != 24) {
160
0
        msg = "Auth-Type MD5 has invalid value for digest length";
161
0
        goto fail;
162
0
      }
163
0
      break;
164
165
0
    case BFD_AUTH_KEYED_SHA1:
166
0
    case BFD_AUTH_MET_KEYED_SHA1:
167
0
      if (bfd->auth.basic.auth_len != 28) {
168
0
        msg = "Auth-Type SHA1 has invalid value for digest length";
169
0
        goto fail;
170
0
      }
171
0
      break;
172
173
0
    default:
174
0
      msg = "Invalid Auth-Type";
175
0
      goto fail;
176
0
    }
177
0
  }
178
179
  /*
180
   *  If the Detect Mult field is zero, the packet MUST be discarded.
181
   */
182
0
  if (bfd->detect_multi == 0) {
183
0
    msg = "Packet has invalid detect-multi == 0";
184
0
    goto fail;
185
0
  }
186
187
  /*
188
   *  If the Multipoint (M) bit is nonzero, the packet MUST be
189
   *  discarded.
190
   */
191
0
  if (bfd->multipoint != 0) {
192
0
    msg = "Packet has invalid multipoint != 0";
193
0
    goto fail;
194
0
  }
195
196
  /*
197
   *  If the My Discriminator field is zero, the packet MUST be
198
   *  discarded.
199
   */
200
0
  if (bfd->my_disc == 0) {
201
0
    msg = "Packet has invalid my-discriminator == 0";
202
0
    goto fail;
203
0
  }
204
205
  /*
206
   *  If the Your Discriminator field is zero and the State field is not
207
   *  Down or AdminDown, the packet MUST be discarded.
208
   */
209
0
  if ((bfd->your_disc == 0) &&
210
0
      !((bfd->state == BFD_STATE_DOWN) ||
211
0
        (bfd->state == BFD_STATE_ADMIN_DOWN))) {
212
0
    msg = "Packet has your-discrimator==0, but state is not down or admin-down";
213
0
    goto fail;
214
0
  }
215
216
0
  if (err) *err = NULL;
217
0
  return true;
218
0
}
219
220
221
222
int fr_bfd_global_init(void)
223
4
{
224
4
  if (instance_count > 0) {
225
2
    instance_count++;
226
2
    return 0;
227
2
  }
228
229
2
  instance_count++;
230
231
2
  if (fr_dict_autoload(libfreeradius_bfd_dict) < 0) {
232
0
  fail:
233
0
    instance_count--;
234
0
    return -1;
235
0
  }
236
237
2
  if (fr_dict_attr_autoload(libfreeradius_bfd_dict_attr) < 0) {
238
0
    fr_dict_autofree(libfreeradius_bfd_dict);
239
0
    goto fail;
240
0
  }
241
242
2
  instantiated = true;  
243
244
2
  return 0;
245
2
}
246
247
void fr_bfd_global_free(void)
248
4
{
249
4
  if (!instantiated) return;
250
251
4
  if (--instance_count > 0) return;
252
253
2
  fr_dict_autofree(libfreeradius_bfd_dict);
254
255
2
  instantiated = false;
256
2
}
257
258
extern fr_dict_protocol_t libfreeradius_bfd_dict_protocol;
259
fr_dict_protocol_t libfreeradius_bfd_dict_protocol = {
260
  .name = "bfd",
261
  .default_type_size = 1,
262
  .default_type_length = 1,
263
264
  .init = fr_bfd_global_init,
265
  .free = fr_bfd_global_free,
266
};