File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / proto / bfd / packets.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 19:50:23 2021 UTC (3 years, 4 months ago) by misho
Branches: bird, MAIN
CVS tags: v1_6_8p3, HEAD
bird 1.6.8

    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 * 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;
  415:   sk->vrf = p->p.vrf;
  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;
  451:   sk->vrf = p->p.vrf;
  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>