Return to packets.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / proto / bfd |
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: }