diff options
Diffstat (limited to 'proto/ospf')
-rw-r--r-- | proto/ospf/config.Y | 14 | ||||
-rw-r--r-- | proto/ospf/hello.c | 7 | ||||
-rw-r--r-- | proto/ospf/iface.c | 28 | ||||
-rw-r--r-- | proto/ospf/lsalib.c | 2 | ||||
-rw-r--r-- | proto/ospf/lsupd.c | 8 | ||||
-rw-r--r-- | proto/ospf/ospf.h | 23 | ||||
-rw-r--r-- | proto/ospf/packet.c | 115 | ||||
-rw-r--r-- | proto/ospf/rt.c | 4 | ||||
-rw-r--r-- | proto/ospf/topology.c | 5 |
9 files changed, 116 insertions, 90 deletions
diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 297774b5..72928875 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -42,6 +42,20 @@ ospf_iface_finish(void) if ((ip->autype == OSPF_AUTH_NONE) && (ip->passwords != NULL)) log(L_WARN "Password option without authentication option does not make sense"); + + if (ip->passwords) + { + struct password_item *pass; + WALK_LIST(pass, *ip->passwords) + { + if (pass->alg && (ip->autype != OSPF_AUTH_CRYPT)) + cf_error("Password algorithm option requires cryptographic authentication"); + + /* Set default OSPF crypto algorithms */ + if (!pass->alg && (ip->autype == OSPF_AUTH_CRYPT)) + pass->alg = ospf_cfg_is_v2() ? ALG_MD5 : ALG_HMAC_SHA256; + } + } } static void diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index 3fbb6167..2c55155d 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -105,7 +105,7 @@ ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) } i = 0; - max = (ospf_pkt_maxsize(p, ifa) - length) / sizeof(u32); + max = (ospf_pkt_maxsize(ifa) - length) / sizeof(u32); /* Fill all neighbors */ if (kind != OHS_SHUTDOWN) @@ -222,9 +222,12 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, rcv_priority = ps->priority; int pxlen = u32_masklen(ntohl(ps->netmask)); + if (pxlen < 0) + DROP("prefix garbled", ntohl(ps->netmask)); + if ((ifa->type != OSPF_IT_VLINK) && (ifa->type != OSPF_IT_PTP) && - (pxlen != ifa->addr->prefix.pxlen)) + ((uint) pxlen != ifa->addr->prefix.pxlen)) DROP("prefix length mismatch", pxlen); neighbors = ps->neighbors; diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 6ef24ffe..675cf76d 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -9,6 +9,7 @@ */ #include "ospf.h" +#include "nest/password.h" const char *ospf_is_names[] = { @@ -52,6 +53,20 @@ ifa_tx_length(struct ospf_iface *ifa) } static inline uint +ifa_tx_hdrlen(struct ospf_iface *ifa) +{ + struct ospf_proto *p = ifa->oa->po; + + uint hlen = ospf_is_v2(p) ? IP4_HEADER_LENGTH : IP6_HEADER_LENGTH; + + /* Relevant just for OSPFv2 */ + if (ifa->autype == OSPF_AUTH_CRYPT) + hlen += max_mac_length(ifa->passwords); + + return hlen; +} + +static inline uint ifa_bufsize(struct ospf_iface *ifa) { uint bsize = ifa->cf->rx_buffer ?: ifa->iface->mtu; @@ -67,13 +82,7 @@ ifa_flood_queue_size(struct ospf_iface *ifa) int ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen) { - struct ospf_proto *p = ifa->oa->po; - - plen += ospf_is_v2(p) ? IP4_HEADER_LENGTH : IP6_HEADER_LENGTH; - - /* This is relevant just for OSPFv2 */ - if (ifa->autype == OSPF_AUTH_CRYPT) - plen += OSPF_AUTH_CRYPT_SIZE; + plen += ifa->tx_hdrlen; if (plen <= ifa->sk->tbsize) return 0; @@ -569,6 +578,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i ifa->stub = ospf_iface_stubby(ip, addr); ifa->ioprob = OSPF_I_OK; ifa->tx_length = ifa_tx_length(ifa); + ifa->tx_hdrlen = ifa_tx_hdrlen(ifa); ifa->check_link = ip->check_link; ifa->ecmp_weight = ip->ecmp_weight; ifa->check_ttl = (ip->ttl_security == 1); @@ -680,6 +690,7 @@ ospf_iface_new_vlink(struct ospf_proto *p, struct ospf_iface_patt *ip) ifa->deadint = ip->deadint; ifa->inftransdelay = ip->inftransdelay; ifa->tx_length = ospf_is_v2(p) ? IP4_MIN_MTU : IP6_MIN_MTU; + ifa->tx_hdrlen = ifa_tx_hdrlen(ifa); ifa->autype = ip->autype; ifa->passwords = ip->passwords; ifa->instance_id = ip->instance_id; @@ -820,6 +831,9 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) /* Update passwords */ ifa->passwords = new->passwords; + /* Update header length */ + ifa->tx_hdrlen = ifa_tx_hdrlen(ifa); + /* Remaining options are just for proper interfaces */ if (ifa->type == OSPF_IT_VLINK) return 1; diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c index b4d2faba..b88a114d 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -461,7 +461,7 @@ lsa_validate_sum3_net(struct ospf_lsa_header *lsa, struct ospf_lsa_sum3_net *bod } static int -lsa_validate_sum3_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_sum3_rt *body) +lsa_validate_sum3_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_sum3_rt *body UNUSED) { if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum3_rt))) return 0; diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 65ad8e3d..157d9628 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -105,7 +105,7 @@ invalid: static inline void -ospf_lsa_lsrq_down(struct top_hash_entry *req, struct ospf_neighbor *n, struct ospf_neighbor *from) +ospf_lsa_lsrq_down(struct top_hash_entry *req, struct ospf_neighbor *n) { if (req == n->lsrqi) n->lsrqi = SNODE_NEXT(req); @@ -188,7 +188,7 @@ ospf_enqueue_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_if { /* If we already have full queue, we send some packets */ uint sent = ospf_flood_lsupd(p, ifa->flood_queue, ifa->flood_queue_used, ifa->flood_queue_used / 2, ifa); - int i; + uint i; for (i = 0; i < sent; i++) ifa->flood_queue[i]->ret_count--; @@ -275,7 +275,7 @@ ospf_flood_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neig /* If same or newer, remove LSA from the link state request list */ if (cmp > CMP_OLDER) - ospf_lsa_lsrq_down(req, n, from); + ospf_lsa_lsrq_down(req, n); /* If older or same, skip processing of this neighbor */ if (cmp < CMP_NEWER) @@ -330,7 +330,7 @@ ospf_prepare_lsupd(struct ospf_proto *p, struct ospf_iface *ifa, pkt = ospf_tx_buffer(ifa); hlen = ospf_lsupd_hdrlen(p); - maxsize = ospf_pkt_maxsize(p, ifa); + maxsize = ospf_pkt_maxsize(ifa); ospf_pkt_fill_hdr(ifa, pkt, LSUPD_P); pos = hlen; diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 4f445f07..e3eae2b5 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -171,11 +171,7 @@ struct ospf_iface_patt #define OSPF_RXBUF_MINSIZE 256 /* Minimal allowed size */ u8 instance_id; - u8 autype; /* Not really used in OSPFv3 */ -#define OSPF_AUTH_NONE 0 -#define OSPF_AUTH_SIMPLE 1 -#define OSPF_AUTH_CRYPT 2 -#define OSPF_AUTH_CRYPT_SIZE 16 + u8 autype; /* OSPF_AUTH_*, not really used in OSPFv3 */ u8 strictnbma; u8 check_link; u8 ecmp_weight; @@ -325,6 +321,7 @@ struct ospf_iface u8 marked; /* Used in OSPF reconfigure, 2 for force restart */ u16 rxbuf; /* Buffer size */ u16 tx_length; /* Soft TX packet length limit, usually MTU */ + u16 tx_hdrlen; /* Expected packet header length, less than tx_length */ u8 check_link; /* Whether iface link change is used */ u8 ecmp_weight; /* Weight used for ECMP */ u8 link_lsa_suppression; /* Suppression of Link-LSA origination */ @@ -415,6 +412,11 @@ struct ospf_neighbor #define ISM_UNLOOP 5 /* Link up */ #define ISM_DOWN 6 /* Interface down */ +/* OSPF authentication types */ +#define OSPF_AUTH_NONE 0 +#define OSPF_AUTH_SIMPLE 1 +#define OSPF_AUTH_CRYPT 2 + /* OSPF neighbor states */ #define NEIGHBOR_DOWN 0 @@ -446,7 +448,6 @@ struct ospf_neighbor #define TRANS_WAIT 2 /* Waiting before the end of translation */ - /* Generic option flags */ #define OPT_V6 0x01 /* OSPFv3, LSA relevant for IPv6 routing calculation */ #define OPT_E 0x02 /* Related to AS-external LSAs */ @@ -482,7 +483,7 @@ struct ospf_packet u8 autype; /* Undefined for OSPFv3 */ }; -struct ospf_md5 +struct ospf_auth_crypto { u16 zero; u8 keyid; @@ -493,7 +494,7 @@ struct ospf_md5 union ospf_auth { u8 password[8]; - struct ospf_md5 md5; + struct ospf_auth_crypto c32; }; /* Packet types */ @@ -890,8 +891,7 @@ void ospf_sh_neigh_info(struct ospf_neighbor *n); /* packet.c */ void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type); -uint ospf_pkt_maxsize(struct ospf_proto *p, struct ospf_iface *ifa); -int ospf_rx_hook(sock * sk, int size); +int ospf_rx_hook(sock * sk, uint size); // void ospf_tx_hook(sock * sk); void ospf_err_hook(sock * sk, int err); void ospf_verr_hook(sock *sk, int err); @@ -899,6 +899,9 @@ void ospf_send_to(struct ospf_iface *ifa, ip_addr ip); void ospf_send_to_agt(struct ospf_iface *ifa, u8 state); void ospf_send_to_bdr(struct ospf_iface *ifa); +static inline uint ospf_pkt_maxsize(struct ospf_iface *ifa) +{ return ifa->tx_length - ifa->tx_hdrlen; } + static inline void ospf_send_to_all(struct ospf_iface *ifa) { ospf_send_to(ifa, ifa->all_routers); } diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index 7ce6d99f..b84780d3 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -11,6 +11,7 @@ #include "ospf.h" #include "nest/password.h" #include "lib/md5.h" +#include "lib/mac.h" #include "lib/socket.h" void @@ -23,7 +24,7 @@ ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) pkt->version = ospf_get_version(p); pkt->type = h_type; - pkt->length = htons(ospf_pkt_maxsize(p, ifa)); + pkt->length = htons(ospf_pkt_maxsize(ifa)); pkt->routerid = htonl(p->router_id); pkt->areaid = htonl(ifa->oa->areaid); pkt->checksum = 0; @@ -31,25 +32,12 @@ ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) pkt->autype = ifa->autype; } -uint -ospf_pkt_maxsize(struct ospf_proto *p, struct ospf_iface *ifa) -{ - uint headers = ospf_is_v2(p) ? IP4_HEADER_LENGTH : IP6_HEADER_LENGTH; - - /* Relevant just for OSPFv2 */ - if (ifa->autype == OSPF_AUTH_CRYPT) - headers += OSPF_AUTH_CRYPT_SIZE; - - return ifa->tx_length - headers; -} - /* We assume OSPFv2 in ospf_pkt_finalize() */ static void -ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) +ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen) { - struct password_item *passwd = NULL; + struct password_item *pass = NULL; union ospf_auth *auth = (void *) (pkt + 1); - uint plen = ntohs(pkt->length); pkt->checksum = 0; pkt->autype = ifa->autype; @@ -61,25 +49,25 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) switch (ifa->autype) { case OSPF_AUTH_SIMPLE: - passwd = password_find(ifa->passwords, 1); - if (!passwd) + pass = password_find(ifa->passwords, 1); + if (!pass) { log(L_ERR "No suitable password found for authentication"); return; } - strncpy(auth->password, passwd->password, sizeof(auth->password)); + strncpy(auth->password, pass->password, sizeof(auth->password)); case OSPF_AUTH_NONE: { void *body = (void *) (auth + 1); - uint blen = plen - sizeof(struct ospf_packet) - sizeof(union ospf_auth); + uint blen = *plen - sizeof(struct ospf_packet) - sizeof(union ospf_auth); pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet), body, blen, NULL); } break; case OSPF_AUTH_CRYPT: - passwd = password_find(ifa->passwords, 0); - if (!passwd) + pass = password_find(ifa->passwords, 0); + if (!pass) { log(L_ERR "No suitable password found for authentication"); return; @@ -100,20 +88,25 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) ifa->csn_use = now; - auth->md5.zero = 0; - auth->md5.keyid = passwd->id; - auth->md5.len = OSPF_AUTH_CRYPT_SIZE; - auth->md5.csn = htonl(ifa->csn); + uint auth_len = mac_type_length(pass->alg); + byte *auth_tail = ((byte *) pkt + *plen); + *plen += auth_len; + + ASSERT(*plen < ifa->sk->tbsize); - void *tail = ((void *) pkt) + plen; - char password[OSPF_AUTH_CRYPT_SIZE]; - strncpy(password, passwd->password, sizeof(password)); + auth->c32.zero = 0; + auth->c32.keyid = pass->id; + auth->c32.len = auth_len; + auth->c32.csn = htonl(ifa->csn); - struct md5_context ctx; - md5_init(&ctx); - md5_update(&ctx, (char *) pkt, plen); - md5_update(&ctx, password, OSPF_AUTH_CRYPT_SIZE); - memcpy((byte *) tail, md5_final(&ctx), MD5_SIZE); + /* Append key for keyed hash, append padding for HMAC (RFC 5709 3.3) */ + if (pass->alg < ALG_HMAC) + strncpy(auth_tail, pass->password, auth_len); + else + memset32(auth_tail, HMAC_MAGIC, auth_len / 4); + + mac_fill(pass->alg, pass->password, pass->length, + (byte *) pkt, *plen, auth_tail); break; default: @@ -124,7 +117,7 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) /* We assume OSPFv2 in ospf_pkt_checkauth() */ static int -ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, int len) +ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, uint len) { struct ospf_proto *p = ifa->oa->po; union ospf_auth *auth = (void *) (pkt + 1); @@ -154,13 +147,19 @@ ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_ return 1; case OSPF_AUTH_CRYPT: - if (auth->md5.len != OSPF_AUTH_CRYPT_SIZE) - DROP("invalid MD5 digest length", auth->md5.len); + pass = password_find_by_id(ifa->passwords, auth->c32.keyid); + if (!pass) + DROP("no suitable password found", auth->c32.keyid); + + uint auth_len = mac_type_length(pass->alg); - if (plen + OSPF_AUTH_CRYPT_SIZE > len) - DROP("length mismatch", len); + if (plen + auth->c32.len > len) + DROP("packet length mismatch", len); - u32 rcv_csn = ntohl(auth->md5.csn); + if (auth->c32.len != auth_len) + DROP("wrong authentication length", auth->c32.len); + + u32 rcv_csn = ntohl(auth->c32.csn); if (n && (rcv_csn < n->csn)) // DROP("lower sequence number", rcv_csn); { @@ -171,22 +170,19 @@ ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_ return 0; } - pass = password_find_by_id(ifa->passwords, auth->md5.keyid); - if (!pass) - DROP("no suitable password found", auth->md5.keyid); - - byte *tail = ((byte *) pkt) + plen; - char received[OSPF_AUTH_CRYPT_SIZE]; - memcpy(received, tail, OSPF_AUTH_CRYPT_SIZE); - strncpy(tail, pass->password, OSPF_AUTH_CRYPT_SIZE); + byte *auth_tail = ((byte *) pkt) + plen; + byte *auth_data = alloca(auth_len); + memcpy(auth_data, auth_tail, auth_len); - struct md5_context ctx; - md5_init(&ctx); - md5_update(&ctx, (byte *) pkt, plen + OSPF_AUTH_CRYPT_SIZE); - char *computed = md5_final(&ctx); + /* Append key for keyed hash, append padding for HMAC (RFC 5709 3.3) */ + if (pass->alg < ALG_HMAC) + strncpy(auth_tail, pass->password, auth_len); + else + memset32(auth_tail, HMAC_MAGIC, auth_len / 4); - if (memcmp(received, computed, OSPF_AUTH_CRYPT_SIZE)) - DROP("wrong MD5 digest", pass->id); + if (!mac_verify(pass->alg, pass->password, pass->length, + (byte *) pkt, plen + auth_len, auth_data)) + DROP("wrong authentication code", pass->id); if (n) n->csn = rcv_csn; @@ -214,7 +210,7 @@ drop: * non generic functions. */ int -ospf_rx_hook(sock *sk, int len) +ospf_rx_hook(sock *sk, uint len) { /* We want just packets from sk->iface. Unfortunately, on BSD we cannot filter out other packets at kernel level and we receive all packets on all sockets */ @@ -472,15 +468,10 @@ ospf_send_to(struct ospf_iface *ifa, ip_addr dst) { sock *sk = ifa->sk; struct ospf_packet *pkt = (struct ospf_packet *) sk->tbuf; - int plen = ntohs(pkt->length); + uint plen = ntohs(pkt->length); if (ospf_is_v2(ifa->oa->po)) - { - if (ifa->autype == OSPF_AUTH_CRYPT) - plen += OSPF_AUTH_CRYPT_SIZE; - - ospf_pkt_finalize(ifa, pkt); - } + ospf_pkt_finalize(ifa, pkt, &plen); int done = sk_send_to(sk, plen, dst, 0); if (!done) diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index 5538f4c8..c5c54783 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -671,7 +671,7 @@ link_back(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry which may be later used as the next hop. */ /* In OSPFv2, en->lb is set here. In OSPFv3, en->lb is just cleared here, - it is set in process_prefixes() to any global addres in the area */ + it is set in process_prefixes() to any global address in the area */ en->lb = IPA_NONE; en->lb_id = 0; @@ -923,7 +923,7 @@ ospf_rt_sum_tr(struct ospf_area *oa) } } -/* Decide about originating or flushing summary LSAs for condended area networks */ +/* Decide about originating or flushing summary LSAs for condensed area networks */ static int decide_anet_lsa(struct ospf_area *oa, struct area_net *anet, struct ospf_area *anet_oa) { diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 86e39d75..aaaf2e8e 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -513,6 +513,7 @@ ospf_update_lsadb(struct ospf_proto *p) } } + static u32 ort_to_lsaid(struct ospf_proto *p, ort *nf) { @@ -610,7 +611,7 @@ lsab_offset(struct ospf_proto *p, uint offset) return ((byte *) p->lsab) + offset; } -static inline void * +static inline void * UNUSED lsab_end(struct ospf_proto *p) { return ((byte *) p->lsab) + p->lsab_used; @@ -1558,7 +1559,7 @@ static void add_link_lsa(struct ospf_proto *p, struct ospf_lsa_link *ll, int offset, int *pxc) { u32 *pxb = ll->rest; - int j; + uint j; for (j = 0; j < ll->pxcount; pxb = prefix_advance(pxb), j++) { |