Annotation of embedaddon/bird2/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:     /* fallthrough */
        !           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;
        !           234:     /* fallthrough */
        !           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 * (uint) 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, int af)
        !           411: {
        !           412:   sock *sk = sk_new(p->tpool);
        !           413:   sk->type = SK_UDP;
        !           414:   sk->subtype = af;
        !           415:   sk->sport = !multihop ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
        !           416:   sk->vrf = p->p.vrf;
        !           417:   sk->data = p;
        !           418: 
        !           419:   sk->rbsize = BFD_MAX_LEN;
        !           420:   sk->rx_hook = bfd_rx_hook;
        !           421:   sk->err_hook = bfd_err_hook;
        !           422: 
        !           423:   /* TODO: configurable ToS and priority */
        !           424:   sk->tos = IP_PREC_INTERNET_CONTROL;
        !           425:   sk->priority = sk_priority_control;
        !           426:   sk->flags = SKF_THREAD | SKF_LADDR_RX | (!multihop ? SKF_TTL_RX : 0);
        !           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->vrf = p->p.vrf;
        !           449:   sk->data = p;
        !           450: 
        !           451:   sk->tbsize = BFD_MAX_LEN;
        !           452:   sk->err_hook = bfd_err_hook;
        !           453: 
        !           454:   /* TODO: configurable ToS, priority and TTL security */
        !           455:   sk->tos = IP_PREC_INTERNET_CONTROL;
        !           456:   sk->priority = sk_priority_control;
        !           457:   sk->ttl = ifa ? 255 : -1;
        !           458:   sk->flags = SKF_THREAD | SKF_BIND | SKF_HIGH_PORT;
        !           459: 
        !           460:   if (sk_open(sk) < 0)
        !           461:     goto err;
        !           462: 
        !           463:   sk_start(sk);
        !           464:   return sk;
        !           465: 
        !           466:  err:
        !           467:   sk_log_error(sk, p->p.name);
        !           468:   rfree(sk);
        !           469:   return NULL;
        !           470: }

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