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>