Statistics
| Branch: | Revision:

iof-bird / bird-2.0.1 / proto / bfd / packets.c @ 6b3f1a54

History | View | Annotate | Download (11.9 KB)

1
/*
2
 *        BIRD -- Bidirectional Forwarding Detection (BFD)
3
 *
4
 *        Can be freely distributed and used under the terms of the GNU GPL.
5
 */
6

    
7
#include "bfd.h"
8
#include "lib/mac.h"
9

    
10

    
11
struct bfd_ctl_packet
12
{
13
  u8 vdiag;                                /* Version and diagnostic */
14
  u8 flags;                                /* State and flags */
15
  u8 detect_mult;
16
  u8 length;                                /* Whole packet length */
17
  u32 snd_id;                                /* Sender ID, aka 'my discriminator' */
18
  u32 rcv_id;                                /* Receiver ID, aka 'your discriminator' */
19
  u32 des_min_tx_int;
20
  u32 req_min_rx_int;
21
  u32 req_min_echo_rx_int;
22
};
23

    
24
struct bfd_auth
25
{
26
  u8 type;                                /* Authentication type (BFD_AUTH_*) */
27
  u8 length;                                /* Authentication section length */
28
};
29

    
30
struct bfd_simple_auth
31
{
32
  u8 type;                                /* BFD_AUTH_SIMPLE */
33
  u8 length;                                /* Length of bfd_simple_auth + pasword length */
34
  u8 key_id;                                /* Key ID */
35
  byte password[0];                        /* Password itself, variable length */
36
};
37

    
38
#define BFD_MAX_PASSWORD_LENGTH 16
39

    
40
struct bfd_crypto_auth
41
{
42
  u8 type;                                /* BFD_AUTH_*_MD5 or BFD_AUTH_*_SHA1 */
43
  u8 length;                                /* Length of bfd_crypto_auth + hash length */
44
  u8 key_id;                                /* Key ID */
45
  u8 zero;                                /* Reserved, zero on transmit */
46
  u32 csn;                                /* Cryptographic sequence number */
47
  byte data[0];                                /* Authentication key/hash, length 16 or 20 */
48
};
49

    
50
#define BFD_BASE_LEN        sizeof(struct bfd_ctl_packet)
51
#define BFD_MAX_LEN        64
52

    
53
#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0)
54

    
55
#define LOG_PKT(msg, args...) \
56
  log(L_REMOTE "%s: " msg, p->p.name, args)
57

    
58
#define LOG_PKT_AUTH(msg, args...) \
59
  log(L_AUTH "%s: " msg, p->p.name, args)
60

    
61

    
62
static inline u8 bfd_pack_vdiag(u8 version, u8 diag)
63
{ return (version << 5) | diag; }
64

    
65
static inline u8 bfd_pack_flags(u8 state, u8 flags)
66
{ return (state << 6) | flags; }
67

    
68
static inline u8 bfd_pkt_get_version(struct bfd_ctl_packet *pkt)
69
{ return pkt->vdiag >> 5; }
70

    
71
static inline u8 bfd_pkt_get_diag(struct bfd_ctl_packet *pkt)
72
{ return pkt->vdiag & 0x1f; }
73

    
74

    
75
static inline u8 bfd_pkt_get_state(struct bfd_ctl_packet *pkt)
76
{ return pkt->flags >> 6; }
77

    
78
static inline void UNUSED bfd_pkt_set_state(struct bfd_ctl_packet *pkt, u8 val)
79
{ pkt->flags = val << 6; }
80

    
81

    
82
char *
83
bfd_format_flags(u8 flags, char *buf)
84
{
85
  char *bp = buf;
86
  if (flags & BFD_FLAGS)        *bp++ = ' ';
87
  if (flags & BFD_FLAG_POLL)        *bp++ = 'P';
88
  if (flags & BFD_FLAG_FINAL)        *bp++ = 'F';
89
  if (flags & BFD_FLAG_CPI)        *bp++ = 'C';
90
  if (flags & BFD_FLAG_AP)        *bp++ = 'A';
91
  if (flags & BFD_FLAG_DEMAND)        *bp++ = 'D';
92
  if (flags & BFD_FLAG_MULTIPOINT) *bp++ = 'M';
93
  *bp = 0;
94

    
95
  return buf;
96
}
97

    
98
const u8 bfd_auth_type_to_hash_alg[] = {
99
    [BFD_AUTH_NONE] =                         ALG_UNDEFINED,
100
    [BFD_AUTH_SIMPLE] =                 ALG_UNDEFINED,
101
    [BFD_AUTH_KEYED_MD5] =                 ALG_MD5,
102
    [BFD_AUTH_METICULOUS_KEYED_MD5] =         ALG_MD5,
103
    [BFD_AUTH_KEYED_SHA1] =                 ALG_SHA1,
104
    [BFD_AUTH_METICULOUS_KEYED_SHA1] =         ALG_SHA1,
105
};
106

    
107

    
108
/* Fill authentication section and modifies final length in control section packet */
109
static void
110
bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
111
{
112
  struct bfd_iface_config *cf = s->ifa->cf;
113
  struct password_item *pass = password_find(cf->passwords, 0);
114
  uint meticulous = 0;
115

    
116
  if (!pass)
117
  {
118
    /* FIXME: This should not happen */
119
    log(L_ERR "%s: No suitable password found for authentication", p->p.name);
120
    return;
121
  }
122

    
123
  switch (cf->auth_type)
124
  {
125
  case BFD_AUTH_SIMPLE:
126
  {
127
    struct bfd_simple_auth *auth = (void *) (pkt + 1);
128
    uint pass_len = MIN(pass->length, BFD_MAX_PASSWORD_LENGTH);
129

    
130
    auth->type = BFD_AUTH_SIMPLE;
131
    auth->length = sizeof(struct bfd_simple_auth) + pass_len;
132
    auth->key_id = pass->id;
133

    
134
    pkt->flags |= BFD_FLAG_AP;
135
    pkt->length += auth->length;
136

    
137
    memcpy(auth->password, pass->password, pass_len);
138
    return;
139
  }
140

    
141
  case BFD_AUTH_METICULOUS_KEYED_MD5:
142
  case BFD_AUTH_METICULOUS_KEYED_SHA1:
143
    meticulous = 1;
144

    
145
  case BFD_AUTH_KEYED_MD5:
146
  case BFD_AUTH_KEYED_SHA1:
147
  {
148
    struct bfd_crypto_auth *auth = (void *) (pkt + 1);
149
    uint hash_alg = bfd_auth_type_to_hash_alg[cf->auth_type];
150
    uint hash_len = mac_type_length(pass->alg);
151

    
152
    /* Increase CSN about one time per second */
153
    u32  new_time = (u64) current_time() >> 20;
154
    if ((new_time != s->tx_csn_time) || meticulous)
155
    {
156
      s->tx_csn++;
157
      s->tx_csn_time = new_time;
158
    }
159

    
160
    DBG("[%I] CSN: %u\n", s->addr, s->last_tx_csn);
161

    
162
    auth->type = cf->auth_type;
163
    auth->length = sizeof(struct bfd_crypto_auth) + hash_len;
164
    auth->key_id = pass->id;
165
    auth->zero = 0;
166
    auth->csn = htonl(s->tx_csn);
167

    
168
    pkt->flags |= BFD_FLAG_AP;
169
    pkt->length += auth->length;
170

    
171
    strncpy(auth->data, pass->password, hash_len);
172
    mac_fill(hash_alg, NULL, 0, (byte *) pkt, pkt->length, auth->data);
173
    return;
174
  }
175
  }
176
}
177

    
178
static int
179
bfd_check_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
180
{
181
  struct bfd_iface_config *cf = s->ifa->cf;
182
  const char *err_dsc = NULL;
183
  uint err_val = 0;
184
  uint auth_type = 0;
185
  uint meticulous = 0;
186

    
187
  if (pkt->flags & BFD_FLAG_AP)
188
  {
189
    struct bfd_auth *auth = (void *) (pkt + 1);
190

    
191
    if ((pkt->length < (BFD_BASE_LEN + sizeof(struct bfd_auth))) ||
192
        (pkt->length < (BFD_BASE_LEN + auth->length)))
193
      DROP("packet length mismatch", pkt->length);
194

    
195
    /* Zero is reserved, we use it as BFD_AUTH_NONE internally */
196
    if (auth->type == 0)
197
      DROP("reserved authentication type", 0);
198

    
199
    auth_type = auth->type;
200
  }
201

    
202
  if (auth_type != cf->auth_type)
203
    DROP("authentication method mismatch", auth_type);
204

    
205
  switch (auth_type)
206
  {
207
  case BFD_AUTH_NONE:
208
    return 1;
209

    
210
  case BFD_AUTH_SIMPLE:
211
  {
212
    struct bfd_simple_auth *auth = (void *) (pkt + 1);
213

    
214
    if (auth->length < sizeof(struct bfd_simple_auth))
215
      DROP("wrong authentication length", auth->length);
216

    
217
    struct password_item *pass = password_find_by_id(cf->passwords, auth->key_id);
218
    if (!pass)
219
      DROP("no suitable password found", auth->key_id);
220

    
221
    uint pass_len = MIN(pass->length, BFD_MAX_PASSWORD_LENGTH);
222
    uint auth_len = sizeof(struct bfd_simple_auth) + pass_len;
223

    
224
    if ((auth->length != auth_len) || memcmp(auth->password, pass->password, pass_len))
225
      DROP("wrong password", pass->id);
226

    
227
    return 1;
228
  }
229

    
230
  case BFD_AUTH_METICULOUS_KEYED_MD5:
231
  case BFD_AUTH_METICULOUS_KEYED_SHA1:
232
    meticulous = 1;
233

    
234
  case BFD_AUTH_KEYED_MD5:
235
  case BFD_AUTH_KEYED_SHA1:
236
  {
237
    struct bfd_crypto_auth *auth = (void *) (pkt + 1);
238
    uint hash_alg = bfd_auth_type_to_hash_alg[cf->auth_type];
239
    uint hash_len = mac_type_length(hash_alg);
240

    
241
    if (auth->length != (sizeof(struct bfd_crypto_auth) + hash_len))
242
      DROP("wrong authentication length", auth->length);
243

    
244
    struct password_item *pass = password_find_by_id(cf->passwords, auth->key_id);
245
    if (!pass)
246
      DROP("no suitable password found", auth->key_id);
247

    
248
    /* BFD CSNs are in 32-bit circular number space */
249
    u32 csn = ntohl(auth->csn);
250
    if (s->rx_csn_known &&
251
        (((csn - s->rx_csn) > (3 * (uint) s->detect_mult)) ||
252
         (meticulous && (csn == s->rx_csn))))
253
    {
254
      /* We want to report both new and old CSN */
255
      LOG_PKT_AUTH("Authentication failed for %I - "
256
                   "wrong sequence number (rcv %u, old %u)",
257
                   s->addr, csn, s->rx_csn);
258
      return 0;
259
    }
260

    
261
    byte *auth_data = alloca(hash_len);
262
    memcpy(auth_data, auth->data, hash_len);
263
    strncpy(auth->data, pass->password, hash_len);
264

    
265
    if (!mac_verify(hash_alg, NULL, 0, (byte *) pkt, pkt->length, auth_data))
266
      DROP("wrong authentication code", pass->id);
267

    
268
    s->rx_csn = csn;
269
    s->rx_csn_known = 1;
270

    
271
    return 1;
272
  }
273
  }
274

    
275
drop:
276
  LOG_PKT_AUTH("Authentication failed for %I - %s (%u)",
277
               s->addr, err_dsc, err_val);
278
  return 0;
279
}
280

    
281
void
282
bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final)
283
{
284
  sock *sk = s->ifa->sk;
285
  struct bfd_ctl_packet *pkt;
286
  char fb[8];
287

    
288
  if (!sk)
289
    return;
290

    
291
  pkt = (struct bfd_ctl_packet *) sk->tbuf;
292
  pkt->vdiag = bfd_pack_vdiag(1, s->loc_diag);
293
  pkt->flags = bfd_pack_flags(s->loc_state, 0);
294
  pkt->detect_mult = s->detect_mult;
295
  pkt->length = BFD_BASE_LEN;
296
  pkt->snd_id = htonl(s->loc_id);
297
  pkt->rcv_id = htonl(s->rem_id);
298
  pkt->des_min_tx_int = htonl(s->des_min_tx_new);
299
  pkt->req_min_rx_int = htonl(s->req_min_rx_new);
300
  pkt->req_min_echo_rx_int = 0;
301

    
302
  if (final)
303
    pkt->flags |= BFD_FLAG_FINAL;
304
  else if (s->poll_active)
305
    pkt->flags |= BFD_FLAG_POLL;
306

    
307
  if (s->ifa->cf->auth_type)
308
    bfd_fill_authentication(p, s, pkt);
309

    
310
  if (sk->tbuf != sk->tpos)
311
    log(L_WARN "%s: Old packet overwritten in TX buffer", p->p.name);
312

    
313
  TRACE(D_PACKETS, "Sending CTL to %I [%s%s]", s->addr,
314
        bfd_state_names[s->loc_state], bfd_format_flags(pkt->flags, fb));
315

    
316
  sk_send_to(sk, pkt->length, s->addr, sk->dport);
317
}
318

    
319
static int
320
bfd_rx_hook(sock *sk, uint len)
321
{
322
  struct bfd_proto *p =  sk->data;
323
  struct bfd_ctl_packet *pkt = (struct bfd_ctl_packet *) sk->rbuf;
324
  const char *err_dsc = NULL;
325
  uint err_val = 0;
326
  char fb[8];
327

    
328
  if ((sk->sport == BFD_CONTROL_PORT) && (sk->rcv_ttl < 255))
329
    DROP("wrong TTL", sk->rcv_ttl);
330

    
331
  if (len < BFD_BASE_LEN)
332
    DROP("too short", len);
333

    
334
  u8 version = bfd_pkt_get_version(pkt);
335
  if (version != 1)
336
    DROP("version mismatch", version);
337

    
338
  if ((pkt->length < BFD_BASE_LEN) || (pkt->length > len))
339
    DROP("length mismatch", pkt->length);
340

    
341
  if (pkt->detect_mult == 0)
342
    DROP("invalid detect mult", 0);
343

    
344
  if ((pkt->flags & BFD_FLAG_MULTIPOINT) ||
345
      ((pkt->flags & BFD_FLAG_POLL) && (pkt->flags & BFD_FLAG_FINAL)))
346
    DROP("invalid flags", pkt->flags);
347

    
348
  if (pkt->snd_id == 0)
349
    DROP("invalid my discriminator", 0);
350

    
351
  struct bfd_session *s;
352
  u32 id = ntohl(pkt->rcv_id);
353

    
354
  if (id)
355
  {
356
    s = bfd_find_session_by_id(p, id);
357

    
358
    if (!s)
359
      DROP("unknown session id", id);
360
  }
361
  else
362
  {
363
    u8 ps = bfd_pkt_get_state(pkt);
364
    if (ps > BFD_STATE_DOWN)
365
      DROP("invalid init state", ps);
366

    
367
    s = bfd_find_session_by_addr(p, sk->faddr);
368

    
369
    /* FIXME: better session matching and message */
370
    if (!s)
371
      return 1;
372
  }
373

    
374
  /* bfd_check_authentication() has its own error logging */
375
  if (!bfd_check_authentication(p, s, pkt))
376
    return 1;
377

    
378
  u32 old_tx_int = s->des_min_tx_int;
379
  u32 old_rx_int = s->rem_min_rx_int;
380

    
381
  s->rem_id= ntohl(pkt->snd_id);
382
  s->rem_state = bfd_pkt_get_state(pkt);
383
  s->rem_diag = bfd_pkt_get_diag(pkt);
384
  s->rem_demand_mode = pkt->flags & BFD_FLAG_DEMAND;
385
  s->rem_min_tx_int = ntohl(pkt->des_min_tx_int);
386
  s->rem_min_rx_int = ntohl(pkt->req_min_rx_int);
387
  s->rem_detect_mult = pkt->detect_mult;
388

    
389
  TRACE(D_PACKETS, "CTL received from %I [%s%s]", sk->faddr,
390
        bfd_state_names[s->rem_state], bfd_format_flags(pkt->flags, fb));
391

    
392
  bfd_session_process_ctl(s, pkt->flags, old_tx_int, old_rx_int);
393
  return 1;
394

    
395
drop:
396
  LOG_PKT("Bad packet from %I - %s (%u)", sk->faddr, err_dsc, err_val);
397
  return 1;
398
}
399

    
400
static void
401
bfd_err_hook(sock *sk, int err)
402
{
403
  struct bfd_proto *p = sk->data;
404
  log(L_ERR "%s: Socket error: %m", p->p.name, err);
405
}
406

    
407
sock *
408
bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
409
{
410
  sock *sk = sk_new(p->tpool);
411
  sk->type = SK_UDP;
412
  sk->subtype = af;
413
  sk->sport = !multihop ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
414
  sk->data = p;
415

    
416
  sk->rbsize = BFD_MAX_LEN;
417
  sk->rx_hook = bfd_rx_hook;
418
  sk->err_hook = bfd_err_hook;
419

    
420
  /* TODO: configurable ToS and priority */
421
  sk->tos = IP_PREC_INTERNET_CONTROL;
422
  sk->priority = sk_priority_control;
423
  sk->flags = SKF_THREAD | SKF_LADDR_RX | (!multihop ? SKF_TTL_RX : 0);
424

    
425
  if (sk_open(sk) < 0)
426
    goto err;
427

    
428
  sk_start(sk);
429
  return sk;
430

    
431
 err:
432
  sk_log_error(sk, p->p.name);
433
  rfree(sk);
434
  return NULL;
435
}
436

    
437
sock *
438
bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
439
{
440
  sock *sk = sk_new(p->tpool);
441
  sk->type = SK_UDP;
442
  sk->saddr = local;
443
  sk->dport = ifa ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
444
  sk->iface = ifa;
445
  sk->data = p;
446

    
447
  sk->tbsize = BFD_MAX_LEN;
448
  sk->err_hook = bfd_err_hook;
449

    
450
  /* TODO: configurable ToS, priority and TTL security */
451
  sk->tos = IP_PREC_INTERNET_CONTROL;
452
  sk->priority = sk_priority_control;
453
  sk->ttl = ifa ? 255 : -1;
454
  sk->flags = SKF_THREAD | SKF_BIND | SKF_HIGH_PORT;
455

    
456
  if (sk_open(sk) < 0)
457
    goto err;
458

    
459
  sk_start(sk);
460
  return sk;
461

    
462
 err:
463
  sk_log_error(sk, p->p.name);
464
  rfree(sk);
465
  return NULL;
466
}