Annotation of embedaddon/bird/proto/bfd/packets.c, revision 1.1.1.1
1.1 misho 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 * 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)
409: {
410: sock *sk = sk_new(p->tpool);
411: sk->type = SK_UDP;
412: sk->sport = !multihop ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
413: sk->data = p;
414:
415: sk->rbsize = BFD_MAX_LEN;
416: sk->rx_hook = bfd_rx_hook;
417: sk->err_hook = bfd_err_hook;
418:
419: /* TODO: configurable ToS and priority */
420: sk->tos = IP_PREC_INTERNET_CONTROL;
421: sk->priority = sk_priority_control;
422: sk->flags = SKF_THREAD | SKF_LADDR_RX | (!multihop ? SKF_TTL_RX : 0);
423:
424: #ifdef IPV6
425: sk->flags |= SKF_V6ONLY;
426: #endif
427:
428: if (sk_open(sk) < 0)
429: goto err;
430:
431: sk_start(sk);
432: return sk;
433:
434: err:
435: sk_log_error(sk, p->p.name);
436: rfree(sk);
437: return NULL;
438: }
439:
440: sock *
441: bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
442: {
443: sock *sk = sk_new(p->tpool);
444: sk->type = SK_UDP;
445: sk->saddr = local;
446: sk->dport = ifa ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
447: sk->iface = ifa;
448: sk->data = p;
449:
450: sk->tbsize = BFD_MAX_LEN;
451: sk->err_hook = bfd_err_hook;
452:
453: /* TODO: configurable ToS, priority and TTL security */
454: sk->tos = IP_PREC_INTERNET_CONTROL;
455: sk->priority = sk_priority_control;
456: sk->ttl = ifa ? 255 : -1;
457: sk->flags = SKF_THREAD | SKF_BIND | SKF_HIGH_PORT;
458:
459: #ifdef IPV6
460: sk->flags |= SKF_V6ONLY;
461: #endif
462:
463: if (sk_open(sk) < 0)
464: goto err;
465:
466: sk_start(sk);
467: return sk;
468:
469: err:
470: sk_log_error(sk, p->p.name);
471: rfree(sk);
472: return NULL;
473: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>