Annotation of embedaddon/bird/proto/bfd/packets.c, revision 1.1.1.2

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>