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>