Annotation of embedaddon/bird/proto/bfd/packets.c, revision 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>