]> git.ipfire.org Git - thirdparty/bird.git/blame - proto/bfd/packets.c
Merge branch 'master' into int-new
[thirdparty/bird.git] / proto / bfd / packets.c
CommitLineData
6a8d3f1c
OZ
1/*
2 * BIRD -- Bidirectional Forwarding Detection (BFD)
3 *
4 * Can be freely distributed and used under the terms of the GNU GPL.
5 */
bf139664 6
6a8d3f1c 7#include "bfd.h"
e03dc6a9 8#include "lib/mac.h"
bf139664
OZ
9
10
11struct bfd_ctl_packet
12{
e03dc6a9
OZ
13 u8 vdiag; /* Version and diagnostic */
14 u8 flags; /* State and flags */
bf139664 15 u8 detect_mult;
e03dc6a9
OZ
16 u8 length; /* Whole packet length */
17 u32 snd_id; /* Sender ID, aka 'my discriminator' */
18 u32 rcv_id; /* Receiver ID, aka 'your discriminator' */
bf139664
OZ
19 u32 des_min_tx_int;
20 u32 req_min_rx_int;
21 u32 req_min_echo_rx_int;
22};
23
e03dc6a9
OZ
24struct bfd_auth
25{
26 u8 type; /* Authentication type (BFD_AUTH_*) */
27 u8 length; /* Authentication section length */
28};
29
30struct 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
40struct 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
0e175f9f
OZ
50#define BFD_BASE_LEN sizeof(struct bfd_ctl_packet)
51#define BFD_MAX_LEN 64
bf139664 52
e03dc6a9
OZ
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
6a8d3f1c 62static inline u8 bfd_pack_vdiag(u8 version, u8 diag)
bf139664
OZ
63{ return (version << 5) | diag; }
64
6a8d3f1c
OZ
65static inline u8 bfd_pack_flags(u8 state, u8 flags)
66{ return (state << 6) | flags; }
bf139664
OZ
67
68static inline u8 bfd_pkt_get_version(struct bfd_ctl_packet *pkt)
69{ return pkt->vdiag >> 5; }
70
71static inline u8 bfd_pkt_get_diag(struct bfd_ctl_packet *pkt)
8ce9a877 72{ return pkt->vdiag & 0x1f; }
bf139664
OZ
73
74
75static inline u8 bfd_pkt_get_state(struct bfd_ctl_packet *pkt)
76{ return pkt->flags >> 6; }
77
3e236955 78static inline void UNUSED bfd_pkt_set_state(struct bfd_ctl_packet *pkt, u8 val)
bf139664
OZ
79{ pkt->flags = val << 6; }
80
81
0e175f9f
OZ
82char *
83bfd_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
e03dc6a9
OZ
98const 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 */
109static void
110bfd_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
178static int
179bfd_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 &&
734e9fb8 251 (((csn - s->rx_csn) > (3 * (uint) s->detect_mult)) ||
e03dc6a9
OZ
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
275drop:
276 LOG_PKT_AUTH("Authentication failed for %I - %s (%u)",
277 s->addr, err_dsc, err_val);
278 return 0;
279}
280
bf139664
OZ
281void
282bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final)
283{
1ec52253 284 sock *sk = s->ifa->sk;
ffa398b8 285 struct bfd_ctl_packet *pkt;
0e175f9f 286 char fb[8];
bf139664 287
ffa398b8
OZ
288 if (!sk)
289 return;
290
291 pkt = (struct bfd_ctl_packet *) sk->tbuf;
bf139664
OZ
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;
6a8d3f1c 295 pkt->length = BFD_BASE_LEN;
bf139664
OZ
296 pkt->snd_id = htonl(s->loc_id);
297 pkt->rcv_id = htonl(s->rem_id);
6a8d3f1c
OZ
298 pkt->des_min_tx_int = htonl(s->des_min_tx_new);
299 pkt->req_min_rx_int = htonl(s->req_min_rx_new);
bf139664
OZ
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
e03dc6a9
OZ
307 if (s->ifa->cf->auth_type)
308 bfd_fill_authentication(p, s, pkt);
309
6a8d3f1c 310 if (sk->tbuf != sk->tpos)
0e175f9f
OZ
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));
6a8d3f1c
OZ
315
316 sk_send_to(sk, pkt->length, s->addr, sk->dport);
bf139664
OZ
317}
318
6a8d3f1c 319static int
3e236955 320bfd_rx_hook(sock *sk, uint len)
bf139664 321{
6a8d3f1c
OZ
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;
0e175f9f
OZ
326 char fb[8];
327
05476c4d
OZ
328 if ((sk->sport == BFD_CONTROL_PORT) && (sk->rcv_ttl < 255))
329 DROP("wrong TTL", sk->rcv_ttl);
bf139664
OZ
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
0e175f9f
OZ
344 if ((pkt->flags & BFD_FLAG_MULTIPOINT) ||
345 ((pkt->flags & BFD_FLAG_POLL) && (pkt->flags & BFD_FLAG_FINAL)))
bf139664
OZ
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)
0e175f9f 359 DROP("unknown session id", id);
bf139664
OZ
360 }
361 else
362 {
363 u8 ps = bfd_pkt_get_state(pkt);
364 if (ps > BFD_STATE_DOWN)
365 DROP("invalid init state", ps);
ffa398b8 366
6a8d3f1c 367 s = bfd_find_session_by_addr(p, sk->faddr);
bf139664
OZ
368
369 /* FIXME: better session matching and message */
1ec52253 370 if (!s)
6a8d3f1c 371 return 1;
bf139664
OZ
372 }
373
e03dc6a9
OZ
374 /* bfd_check_authentication() has its own error logging */
375 if (!bfd_check_authentication(p, s, pkt))
376 return 1;
bf139664 377
6a8d3f1c
OZ
378 u32 old_tx_int = s->des_min_tx_int;
379 u32 old_rx_int = s->rem_min_rx_int;
bf139664 380
1ec52253 381 s->rem_id= ntohl(pkt->snd_id);
bf139664 382 s->rem_state = bfd_pkt_get_state(pkt);
6a8d3f1c 383 s->rem_diag = bfd_pkt_get_diag(pkt);
bf139664
OZ
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
0e175f9f
OZ
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
6a8d3f1c 392 bfd_session_process_ctl(s, pkt->flags, old_tx_int, old_rx_int);
bf139664
OZ
393 return 1;
394
e03dc6a9
OZ
395drop:
396 LOG_PKT("Bad packet from %I - %s (%u)", sk->faddr, err_dsc, err_val);
bf139664
OZ
397 return 1;
398}
399
6a8d3f1c
OZ
400static void
401bfd_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
bf139664 407sock *
08b3a24d 408bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
bf139664 409{
6a8d3f1c 410 sock *sk = sk_new(p->tpool);
bf139664 411 sk->type = SK_UDP;
08b3a24d 412 sk->subtype = af;
bf139664
OZ
413 sk->sport = !multihop ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
414 sk->data = p;
415
0e175f9f 416 sk->rbsize = BFD_MAX_LEN;
bf139664
OZ
417 sk->rx_hook = bfd_rx_hook;
418 sk->err_hook = bfd_err_hook;
6a8d3f1c
OZ
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
bf139664
OZ
425 if (sk_open(sk) < 0)
426 goto err;
6a8d3f1c
OZ
427
428 sk_start(sk);
429 return sk;
430
431 err:
05476c4d 432 sk_log_error(sk, p->p.name);
6a8d3f1c
OZ
433 rfree(sk);
434 return NULL;
bf139664
OZ
435}
436
1ec52253 437sock *
bf139664
OZ
438bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
439{
6a8d3f1c 440 sock *sk = sk_new(p->tpool);
bf139664
OZ
441 sk->type = SK_UDP;
442 sk->saddr = local;
6a8d3f1c
OZ
443 sk->dport = ifa ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
444 sk->iface = ifa;
bf139664
OZ
445 sk->data = p;
446
0e175f9f 447 sk->tbsize = BFD_MAX_LEN;
bf139664 448 sk->err_hook = bfd_err_hook;
bf139664 449
6a8d3f1c
OZ
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;
9c89560e 454 sk->flags = SKF_THREAD | SKF_BIND | SKF_HIGH_PORT;
6a8d3f1c 455
bf139664
OZ
456 if (sk_open(sk) < 0)
457 goto err;
458
6a8d3f1c
OZ
459 sk_start(sk);
460 return sk;
461
462 err:
05476c4d 463 sk_log_error(sk, p->p.name);
6a8d3f1c
OZ
464 rfree(sk);
465 return NULL;
bf139664 466}