2 * BIRD -- Bidirectional Forwarding Detection (BFD)
4 * Can be freely distributed and used under the terms of the GNU GPL.
13 u8 vdiag
; /* Version and diagnostic */
14 u8 flags
; /* State and flags */
16 u8 length
; /* Whole packet length */
17 u32 snd_id
; /* Sender ID, aka 'my discriminator' */
18 u32 rcv_id
; /* Receiver ID, aka 'your discriminator' */
21 u32 req_min_echo_rx_int
;
26 u8 type
; /* Authentication type (BFD_AUTH_*) */
27 u8 length
; /* Authentication section length */
30 struct bfd_simple_auth
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 */
38 #define BFD_MAX_PASSWORD_LENGTH 16
40 struct bfd_crypto_auth
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 */
50 #define BFD_BASE_LEN sizeof(struct bfd_ctl_packet)
51 #define BFD_MAX_LEN 64
53 #define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0)
55 #define LOG_PKT(msg, args...) \
56 log(L_REMOTE "%s: " msg, p->p.name, args)
58 #define LOG_PKT_AUTH(msg, args...) \
59 log(L_AUTH "%s: " msg, p->p.name, args)
62 static inline u8
bfd_pack_vdiag(u8 version
, u8 diag
)
63 { return (version
<< 5) | diag
; }
65 static inline u8
bfd_pack_flags(u8 state
, u8 flags
)
66 { return (state
<< 6) | flags
; }
68 static inline u8
bfd_pkt_get_version(struct bfd_ctl_packet
*pkt
)
69 { return pkt
->vdiag
>> 5; }
71 static inline u8
bfd_pkt_get_diag(struct bfd_ctl_packet
*pkt
)
72 { return pkt
->vdiag
& 0x1f; }
75 static inline u8
bfd_pkt_get_state(struct bfd_ctl_packet
*pkt
)
76 { return pkt
->flags
>> 6; }
78 static inline void UNUSED
bfd_pkt_set_state(struct bfd_ctl_packet
*pkt
, u8 val
)
79 { pkt
->flags
= val
<< 6; }
83 bfd_format_flags(u8 flags
, char *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';
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
,
108 /* Fill authentication section and modifies final length in control section packet */
110 bfd_fill_authentication(struct bfd_proto
*p
, struct bfd_session
*s
, struct bfd_ctl_packet
*pkt
)
112 struct bfd_iface_config
*cf
= s
->ifa
->cf
;
113 struct password_item
*pass
= password_find(cf
->passwords
, 0);
118 /* FIXME: This should not happen */
119 log(L_ERR
"%s: No suitable password found for authentication", p
->p
.name
);
123 switch (cf
->auth_type
)
125 case BFD_AUTH_SIMPLE
:
127 struct bfd_simple_auth
*auth
= (void *) (pkt
+ 1);
128 uint pass_len
= MIN(pass
->length
, BFD_MAX_PASSWORD_LENGTH
);
130 auth
->type
= BFD_AUTH_SIMPLE
;
131 auth
->length
= sizeof(struct bfd_simple_auth
) + pass_len
;
132 auth
->key_id
= pass
->id
;
134 pkt
->flags
|= BFD_FLAG_AP
;
135 pkt
->length
+= auth
->length
;
137 memcpy(auth
->password
, pass
->password
, pass_len
);
141 case BFD_AUTH_METICULOUS_KEYED_MD5
:
142 case BFD_AUTH_METICULOUS_KEYED_SHA1
:
146 case BFD_AUTH_KEYED_MD5
:
147 case BFD_AUTH_KEYED_SHA1
:
149 struct bfd_crypto_auth
*auth
= (void *) (pkt
+ 1);
150 uint hash_alg
= bfd_auth_type_to_hash_alg
[cf
->auth_type
];
151 uint hash_len
= mac_type_length(pass
->alg
);
153 /* Increase CSN about one time per second */
154 u32 new_time
= (u64
) current_time() >> 20;
155 if ((new_time
!= s
->tx_csn_time
) || meticulous
)
158 s
->tx_csn_time
= new_time
;
161 DBG("[%I] CSN: %u\n", s
->addr
, s
->last_tx_csn
);
163 auth
->type
= cf
->auth_type
;
164 auth
->length
= sizeof(struct bfd_crypto_auth
) + hash_len
;
165 auth
->key_id
= pass
->id
;
167 auth
->csn
= htonl(s
->tx_csn
);
169 pkt
->flags
|= BFD_FLAG_AP
;
170 pkt
->length
+= auth
->length
;
172 strncpy(auth
->data
, pass
->password
, hash_len
);
173 mac_fill(hash_alg
, NULL
, 0, (byte
*) pkt
, pkt
->length
, auth
->data
);
180 bfd_check_authentication(struct bfd_proto
*p
, struct bfd_session
*s
, struct bfd_ctl_packet
*pkt
)
182 struct bfd_iface_config
*cf
= s
->ifa
->cf
;
183 const char *err_dsc
= NULL
;
188 if (pkt
->flags
& BFD_FLAG_AP
)
190 struct bfd_auth
*auth
= (void *) (pkt
+ 1);
192 if ((pkt
->length
< (BFD_BASE_LEN
+ sizeof(struct bfd_auth
))) ||
193 (pkt
->length
< (BFD_BASE_LEN
+ auth
->length
)))
194 DROP("packet length mismatch", pkt
->length
);
196 /* Zero is reserved, we use it as BFD_AUTH_NONE internally */
198 DROP("reserved authentication type", 0);
200 auth_type
= auth
->type
;
203 if (auth_type
!= cf
->auth_type
)
204 DROP("authentication method mismatch", auth_type
);
211 case BFD_AUTH_SIMPLE
:
213 struct bfd_simple_auth
*auth
= (void *) (pkt
+ 1);
215 if (auth
->length
< sizeof(struct bfd_simple_auth
))
216 DROP("wrong authentication length", auth
->length
);
218 struct password_item
*pass
= password_find_by_id(cf
->passwords
, auth
->key_id
);
220 DROP("no suitable password found", auth
->key_id
);
222 uint pass_len
= MIN(pass
->length
, BFD_MAX_PASSWORD_LENGTH
);
223 uint auth_len
= sizeof(struct bfd_simple_auth
) + pass_len
;
225 if ((auth
->length
!= auth_len
) || memcmp(auth
->password
, pass
->password
, pass_len
))
226 DROP("wrong password", pass
->id
);
231 case BFD_AUTH_METICULOUS_KEYED_MD5
:
232 case BFD_AUTH_METICULOUS_KEYED_SHA1
:
236 case BFD_AUTH_KEYED_MD5
:
237 case BFD_AUTH_KEYED_SHA1
:
239 struct bfd_crypto_auth
*auth
= (void *) (pkt
+ 1);
240 uint hash_alg
= bfd_auth_type_to_hash_alg
[cf
->auth_type
];
241 uint hash_len
= mac_type_length(hash_alg
);
243 if (auth
->length
!= (sizeof(struct bfd_crypto_auth
) + hash_len
))
244 DROP("wrong authentication length", auth
->length
);
246 struct password_item
*pass
= password_find_by_id(cf
->passwords
, auth
->key_id
);
248 DROP("no suitable password found", auth
->key_id
);
250 /* BFD CSNs are in 32-bit circular number space */
251 u32 csn
= ntohl(auth
->csn
);
252 if (s
->rx_csn_known
&&
253 (((csn
- s
->rx_csn
) > (3 * (uint
) s
->detect_mult
)) ||
254 (meticulous
&& (csn
== s
->rx_csn
))))
256 /* We want to report both new and old CSN */
257 LOG_PKT_AUTH("Authentication failed for %I - "
258 "wrong sequence number (rcv %u, old %u)",
259 s
->addr
, csn
, s
->rx_csn
);
263 byte
*auth_data
= alloca(hash_len
);
264 memcpy(auth_data
, auth
->data
, hash_len
);
265 strncpy(auth
->data
, pass
->password
, hash_len
);
267 if (!mac_verify(hash_alg
, NULL
, 0, (byte
*) pkt
, pkt
->length
, auth_data
))
268 DROP("wrong authentication code", pass
->id
);
278 LOG_PKT_AUTH("Authentication failed for %I - %s (%u)",
279 s
->addr
, err_dsc
, err_val
);
284 bfd_send_ctl(struct bfd_proto
*p
, struct bfd_session
*s
, int final
)
286 sock
*sk
= s
->ifa
->sk
;
287 struct bfd_ctl_packet
*pkt
;
293 pkt
= (struct bfd_ctl_packet
*) sk
->tbuf
;
294 pkt
->vdiag
= bfd_pack_vdiag(1, s
->loc_diag
);
295 pkt
->flags
= bfd_pack_flags(s
->loc_state
, 0);
296 pkt
->detect_mult
= s
->detect_mult
;
297 pkt
->length
= BFD_BASE_LEN
;
298 pkt
->snd_id
= htonl(s
->loc_id
);
299 pkt
->rcv_id
= htonl(s
->rem_id
);
300 pkt
->des_min_tx_int
= htonl(s
->des_min_tx_new
);
301 pkt
->req_min_rx_int
= htonl(s
->req_min_rx_new
);
302 pkt
->req_min_echo_rx_int
= 0;
305 pkt
->flags
|= BFD_FLAG_FINAL
;
306 else if (s
->poll_active
)
307 pkt
->flags
|= BFD_FLAG_POLL
;
309 if (s
->ifa
->cf
->auth_type
)
310 bfd_fill_authentication(p
, s
, pkt
);
312 if (sk
->tbuf
!= sk
->tpos
)
313 log(L_WARN
"%s: Old packet overwritten in TX buffer", p
->p
.name
);
315 TRACE(D_PACKETS
, "Sending CTL to %I [%s%s]", s
->addr
,
316 bfd_state_names
[s
->loc_state
], bfd_format_flags(pkt
->flags
, fb
));
318 sk_send_to(sk
, pkt
->length
, s
->addr
, sk
->dport
);
322 bfd_rx_hook(sock
*sk
, uint len
)
324 struct bfd_proto
*p
= sk
->data
;
325 struct bfd_ctl_packet
*pkt
= (struct bfd_ctl_packet
*) sk
->rbuf
;
326 const char *err_dsc
= NULL
;
330 if ((sk
->sport
== BFD_CONTROL_PORT
) && (sk
->rcv_ttl
< 255))
331 DROP("wrong TTL", sk
->rcv_ttl
);
333 if (len
< BFD_BASE_LEN
)
334 DROP("too short", len
);
336 u8 version
= bfd_pkt_get_version(pkt
);
338 DROP("version mismatch", version
);
340 if ((pkt
->length
< BFD_BASE_LEN
) || (pkt
->length
> len
))
341 DROP("length mismatch", pkt
->length
);
343 if (pkt
->detect_mult
== 0)
344 DROP("invalid detect mult", 0);
346 if ((pkt
->flags
& BFD_FLAG_MULTIPOINT
) ||
347 ((pkt
->flags
& BFD_FLAG_POLL
) && (pkt
->flags
& BFD_FLAG_FINAL
)))
348 DROP("invalid flags", pkt
->flags
);
350 if (pkt
->snd_id
== 0)
351 DROP("invalid my discriminator", 0);
353 struct bfd_session
*s
;
354 u32 id
= ntohl(pkt
->rcv_id
);
358 s
= bfd_find_session_by_id(p
, id
);
361 DROP("unknown session id", id
);
365 u8 ps
= bfd_pkt_get_state(pkt
);
366 if (ps
> BFD_STATE_DOWN
)
367 DROP("invalid init state", ps
);
369 s
= bfd_find_session_by_addr(p
, sk
->faddr
);
371 /* FIXME: better session matching and message */
376 /* bfd_check_authentication() has its own error logging */
377 if (!bfd_check_authentication(p
, s
, pkt
))
380 u32 old_tx_int
= s
->des_min_tx_int
;
381 u32 old_rx_int
= s
->rem_min_rx_int
;
383 s
->rem_id
= ntohl(pkt
->snd_id
);
384 s
->rem_state
= bfd_pkt_get_state(pkt
);
385 s
->rem_diag
= bfd_pkt_get_diag(pkt
);
386 s
->rem_demand_mode
= pkt
->flags
& BFD_FLAG_DEMAND
;
387 s
->rem_min_tx_int
= ntohl(pkt
->des_min_tx_int
);
388 s
->rem_min_rx_int
= ntohl(pkt
->req_min_rx_int
);
389 s
->rem_detect_mult
= pkt
->detect_mult
;
391 TRACE(D_PACKETS
, "CTL received from %I [%s%s]", sk
->faddr
,
392 bfd_state_names
[s
->rem_state
], bfd_format_flags(pkt
->flags
, fb
));
394 bfd_session_process_ctl(s
, pkt
->flags
, old_tx_int
, old_rx_int
);
398 LOG_PKT("Bad packet from %I - %s (%u)", sk
->faddr
, err_dsc
, err_val
);
403 bfd_err_hook(sock
*sk
, int err
)
405 struct bfd_proto
*p
= sk
->data
;
406 log(L_ERR
"%s: Socket error: %m", p
->p
.name
, err
);
410 bfd_open_rx_sk(struct bfd_proto
*p
, int multihop
, int af
)
412 sock
*sk
= sk_new(p
->tpool
);
415 sk
->sport
= !multihop
? BFD_CONTROL_PORT
: BFD_MULTI_CTL_PORT
;
418 sk
->rbsize
= BFD_MAX_LEN
;
419 sk
->rx_hook
= bfd_rx_hook
;
420 sk
->err_hook
= bfd_err_hook
;
422 /* TODO: configurable ToS and priority */
423 sk
->tos
= IP_PREC_INTERNET_CONTROL
;
424 sk
->priority
= sk_priority_control
;
425 sk
->flags
= SKF_THREAD
| SKF_LADDR_RX
| (!multihop
? SKF_TTL_RX
: 0);
434 sk_log_error(sk
, p
->p
.name
);
440 bfd_open_tx_sk(struct bfd_proto
*p
, ip_addr local
, struct iface
*ifa
)
442 sock
*sk
= sk_new(p
->tpool
);
445 sk
->dport
= ifa
? BFD_CONTROL_PORT
: BFD_MULTI_CTL_PORT
;
449 sk
->tbsize
= BFD_MAX_LEN
;
450 sk
->err_hook
= bfd_err_hook
;
452 /* TODO: configurable ToS, priority and TTL security */
453 sk
->tos
= IP_PREC_INTERNET_CONTROL
;
454 sk
->priority
= sk_priority_control
;
455 sk
->ttl
= ifa
? 255 : -1;
456 sk
->flags
= SKF_THREAD
| SKF_BIND
| SKF_HIGH_PORT
;
465 sk_log_error(sk
, p
->p
.name
);