Annotation of embedaddon/bird2/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: /* fallthrough */
145:
146: case BFD_AUTH_KEYED_MD5:
147: case BFD_AUTH_KEYED_SHA1:
148: {
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);
152:
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)
156: {
157: s->tx_csn++;
158: s->tx_csn_time = new_time;
159: }
160:
161: DBG("[%I] CSN: %u\n", s->addr, s->last_tx_csn);
162:
163: auth->type = cf->auth_type;
164: auth->length = sizeof(struct bfd_crypto_auth) + hash_len;
165: auth->key_id = pass->id;
166: auth->zero = 0;
167: auth->csn = htonl(s->tx_csn);
168:
169: pkt->flags |= BFD_FLAG_AP;
170: pkt->length += auth->length;
171:
172: strncpy(auth->data, pass->password, hash_len);
173: mac_fill(hash_alg, NULL, 0, (byte *) pkt, pkt->length, auth->data);
174: return;
175: }
176: }
177: }
178:
179: static int
180: bfd_check_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
181: {
182: struct bfd_iface_config *cf = s->ifa->cf;
183: const char *err_dsc = NULL;
184: uint err_val = 0;
185: uint auth_type = 0;
186: uint meticulous = 0;
187:
188: if (pkt->flags & BFD_FLAG_AP)
189: {
190: struct bfd_auth *auth = (void *) (pkt + 1);
191:
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);
195:
196: /* Zero is reserved, we use it as BFD_AUTH_NONE internally */
197: if (auth->type == 0)
198: DROP("reserved authentication type", 0);
199:
200: auth_type = auth->type;
201: }
202:
203: if (auth_type != cf->auth_type)
204: DROP("authentication method mismatch", auth_type);
205:
206: switch (auth_type)
207: {
208: case BFD_AUTH_NONE:
209: return 1;
210:
211: case BFD_AUTH_SIMPLE:
212: {
213: struct bfd_simple_auth *auth = (void *) (pkt + 1);
214:
215: if (auth->length < sizeof(struct bfd_simple_auth))
216: DROP("wrong authentication length", auth->length);
217:
218: struct password_item *pass = password_find_by_id(cf->passwords, auth->key_id);
219: if (!pass)
220: DROP("no suitable password found", auth->key_id);
221:
222: uint pass_len = MIN(pass->length, BFD_MAX_PASSWORD_LENGTH);
223: uint auth_len = sizeof(struct bfd_simple_auth) + pass_len;
224:
225: if ((auth->length != auth_len) || memcmp(auth->password, pass->password, pass_len))
226: DROP("wrong password", pass->id);
227:
228: return 1;
229: }
230:
231: case BFD_AUTH_METICULOUS_KEYED_MD5:
232: case BFD_AUTH_METICULOUS_KEYED_SHA1:
233: meticulous = 1;
234: /* fallthrough */
235:
236: case BFD_AUTH_KEYED_MD5:
237: case BFD_AUTH_KEYED_SHA1:
238: {
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);
242:
243: if (auth->length != (sizeof(struct bfd_crypto_auth) + hash_len))
244: DROP("wrong authentication length", auth->length);
245:
246: struct password_item *pass = password_find_by_id(cf->passwords, auth->key_id);
247: if (!pass)
248: DROP("no suitable password found", auth->key_id);
249:
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))))
255: {
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);
260: return 0;
261: }
262:
263: byte *auth_data = alloca(hash_len);
264: memcpy(auth_data, auth->data, hash_len);
265: strncpy(auth->data, pass->password, hash_len);
266:
267: if (!mac_verify(hash_alg, NULL, 0, (byte *) pkt, pkt->length, auth_data))
268: DROP("wrong authentication code", pass->id);
269:
270: s->rx_csn = csn;
271: s->rx_csn_known = 1;
272:
273: return 1;
274: }
275: }
276:
277: drop:
278: LOG_PKT_AUTH("Authentication failed for %I - %s (%u)",
279: s->addr, err_dsc, err_val);
280: return 0;
281: }
282:
283: void
284: bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final)
285: {
286: sock *sk = s->ifa->sk;
287: struct bfd_ctl_packet *pkt;
288: char fb[8];
289:
290: if (!sk)
291: return;
292:
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;
303:
304: if (final)
305: pkt->flags |= BFD_FLAG_FINAL;
306: else if (s->poll_active)
307: pkt->flags |= BFD_FLAG_POLL;
308:
309: if (s->ifa->cf->auth_type)
310: bfd_fill_authentication(p, s, pkt);
311:
312: if (sk->tbuf != sk->tpos)
313: log(L_WARN "%s: Old packet overwritten in TX buffer", p->p.name);
314:
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));
317:
318: sk_send_to(sk, pkt->length, s->addr, sk->dport);
319: }
320:
321: static int
322: bfd_rx_hook(sock *sk, uint len)
323: {
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;
327: uint err_val = 0;
328: char fb[8];
329:
330: if ((sk->sport == BFD_CONTROL_PORT) && (sk->rcv_ttl < 255))
331: DROP("wrong TTL", sk->rcv_ttl);
332:
333: if (len < BFD_BASE_LEN)
334: DROP("too short", len);
335:
336: u8 version = bfd_pkt_get_version(pkt);
337: if (version != 1)
338: DROP("version mismatch", version);
339:
340: if ((pkt->length < BFD_BASE_LEN) || (pkt->length > len))
341: DROP("length mismatch", pkt->length);
342:
343: if (pkt->detect_mult == 0)
344: DROP("invalid detect mult", 0);
345:
346: if ((pkt->flags & BFD_FLAG_MULTIPOINT) ||
347: ((pkt->flags & BFD_FLAG_POLL) && (pkt->flags & BFD_FLAG_FINAL)))
348: DROP("invalid flags", pkt->flags);
349:
350: if (pkt->snd_id == 0)
351: DROP("invalid my discriminator", 0);
352:
353: struct bfd_session *s;
354: u32 id = ntohl(pkt->rcv_id);
355:
356: if (id)
357: {
358: s = bfd_find_session_by_id(p, id);
359:
360: if (!s)
361: DROP("unknown session id", id);
362: }
363: else
364: {
365: u8 ps = bfd_pkt_get_state(pkt);
366: if (ps > BFD_STATE_DOWN)
367: DROP("invalid init state", ps);
368:
369: s = bfd_find_session_by_addr(p, sk->faddr);
370:
371: /* FIXME: better session matching and message */
372: if (!s)
373: return 1;
374: }
375:
376: /* bfd_check_authentication() has its own error logging */
377: if (!bfd_check_authentication(p, s, pkt))
378: return 1;
379:
380: u32 old_tx_int = s->des_min_tx_int;
381: u32 old_rx_int = s->rem_min_rx_int;
382:
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;
390:
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));
393:
394: bfd_session_process_ctl(s, pkt->flags, old_tx_int, old_rx_int);
395: return 1;
396:
397: drop:
398: LOG_PKT("Bad packet from %I - %s (%u)", sk->faddr, err_dsc, err_val);
399: return 1;
400: }
401:
402: static void
403: bfd_err_hook(sock *sk, int err)
404: {
405: struct bfd_proto *p = sk->data;
406: log(L_ERR "%s: Socket error: %m", p->p.name, err);
407: }
408:
409: sock *
410: bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
411: {
412: sock *sk = sk_new(p->tpool);
413: sk->type = SK_UDP;
414: sk->subtype = af;
415: sk->sport = !multihop ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
416: sk->vrf = p->p.vrf;
417: sk->data = p;
418:
419: sk->rbsize = BFD_MAX_LEN;
420: sk->rx_hook = bfd_rx_hook;
421: sk->err_hook = bfd_err_hook;
422:
423: /* TODO: configurable ToS and priority */
424: sk->tos = IP_PREC_INTERNET_CONTROL;
425: sk->priority = sk_priority_control;
426: sk->flags = SKF_THREAD | SKF_LADDR_RX | (!multihop ? SKF_TTL_RX : 0);
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->vrf = p->p.vrf;
449: sk->data = p;
450:
451: sk->tbsize = BFD_MAX_LEN;
452: sk->err_hook = bfd_err_hook;
453:
454: /* TODO: configurable ToS, priority and TTL security */
455: sk->tos = IP_PREC_INTERNET_CONTROL;
456: sk->priority = sk_priority_control;
457: sk->ttl = ifa ? 255 : -1;
458: sk->flags = SKF_THREAD | SKF_BIND | SKF_HIGH_PORT;
459:
460: if (sk_open(sk) < 0)
461: goto err;
462:
463: sk_start(sk);
464: return sk;
465:
466: err:
467: sk_log_error(sk, p->p.name);
468: rfree(sk);
469: return NULL;
470: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>