This implements support for HMAC verification in Babel, as specified by draft-do-babel-hmac-00.
Signed-off-by: Toke Høiland-Jørgensen <t...@toke.dk> --- proto/babel/babel.c | 80 ++++++++++ proto/babel/babel.h | 58 +++++++- proto/babel/config.Y | 34 ++++ proto/babel/packets.c | 375 ++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 533 insertions(+), 14 deletions(-) diff --git a/proto/babel/babel.c b/proto/babel/babel.c index afa482bb..6f00d381 100644 --- a/proto/babel/babel.c +++ b/proto/babel/babel.c @@ -36,6 +36,7 @@ */ #include <stdlib.h> +#include <sys/random.h> #include "babel.h" @@ -1377,6 +1378,79 @@ babel_handle_seqno_request(union babel_msg *m, struct babel_iface *ifa) } } +void +babel_hmac_send_challenge(struct babel_iface *ifa, struct babel_neighbor *n) +{ + struct babel_proto *p = ifa->proto; + union babel_msg msg = {}; + + if (n->hmac_next_challenge > current_time()) + { + TRACE(D_PACKETS, "Not sending HMAC challenge to %I due to rate limiting"); + return; + } + + TRACE(D_PACKETS, "Sending HMAC challenge to %I on iface %s", + n->addr, ifa->ifname); + + getrandom(n->hmac_nonce, BABEL_HMAC_NONCE_LEN, 0); + n->hmac_nonce_expiry = current_time() + BABEL_HMAC_CHALLENGE_TIMEOUT; + n->hmac_next_challenge = current_time() + BABEL_HMAC_CHALLENGE_INTERVAL; + + msg.type = BABEL_TLV_CHALLENGE_REQ; + msg.challenge.nonce_len = BABEL_HMAC_NONCE_LEN; + msg.challenge.nonce = n->hmac_nonce; + + babel_send_unicast(&msg, ifa, n->addr); +} + +void +babel_hmac_reset_index(struct babel_iface *ifa) +{ + getrandom(ifa->hmac_index, BABEL_HMAC_INDEX_LEN, 0); + ifa->hmac_pc = 1; +} + +int +babel_handle_hmac(union babel_msg *m, struct babel_iface *ifa) +{ + struct babel_proto *p = ifa->proto; + struct babel_msg_hmac *msg = &m->hmac; + + TRACE(D_PACKETS, "Handling HMAC check on %s from %I", ifa->ifname, msg->sender); + + if (msg->index_len > BABEL_HMAC_INDEX_LEN || !msg->pc_seen) + return 0; + + struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender); + + /* On successful challenge, update PC and index to current values */ + if (msg->challenge_seen && + n->hmac_nonce_expiry && + n->hmac_nonce_expiry >= current_time() && + !memcmp(msg->challenge, n->hmac_nonce, BABEL_HMAC_NONCE_LEN)) + { + n->hmac_index_len = msg->index_len; + memcpy(n->hmac_index, msg->index, msg->index_len); + n->hmac_pc = msg->pc; + } + + /* If index differs, send challenge */ + if (n->hmac_index_len != msg->index_len || + memcmp(n->hmac_index, msg->index, msg->index_len)) + { + babel_hmac_send_challenge(ifa, n); + return 0; + } + + /* Index matches; only accept if PC is greater than last */ + if (n->hmac_pc >= msg->pc) + return 0; + + n->hmac_pc = msg->pc; + return 1; +} + /* * Babel interfaces @@ -1589,6 +1663,9 @@ babel_add_iface(struct babel_proto *p, struct iface *new, struct babel_iface_con init_list(&ifa->neigh_list); ifa->hello_seqno = 1; + if (ic->auth_type == BABEL_AUTH_HMAC) + babel_hmac_reset_index(ifa); + ifa->timer = tm_new_init(ifa->pool, babel_iface_timer, ifa, 0, 0); init_list(&ifa->msg_queue); @@ -1685,6 +1762,9 @@ babel_reconfigure_iface(struct babel_proto *p, struct babel_iface *ifa, struct b ifa->next_hop_ip4 = ipa_nonzero(new->next_hop_ip4) ? new->next_hop_ip4 : addr4; ifa->next_hop_ip6 = ipa_nonzero(new->next_hop_ip6) ? new->next_hop_ip6 : ifa->addr; + if (new->auth_type == BABEL_AUTH_HMAC && old->auth_type != BABEL_AUTH_HMAC) + babel_hmac_reset_index(ifa); + if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel) log(L_WARN "%s: Missing IPv4 next hop address for %s", p->p.name, ifa->ifname); diff --git a/proto/babel/babel.h b/proto/babel/babel.h index 14765c60..9ca4d339 100644 --- a/proto/babel/babel.h +++ b/proto/babel/babel.h @@ -19,6 +19,7 @@ #include "nest/route.h" #include "nest/protocol.h" #include "nest/locks.h" +#include "nest/password.h" #include "lib/resource.h" #include "lib/lists.h" #include "lib/socket.h" @@ -60,6 +61,13 @@ #define BABEL_OVERHEAD (IP6_HEADER_LENGTH+UDP_HEADER_LENGTH) #define BABEL_MIN_MTU (512 + BABEL_OVERHEAD) +#define BABEL_AUTH_NONE 0 +#define BABEL_AUTH_HMAC 1 +#define BABEL_HMAC_NONCE_LEN 10 /* 80 bit random nonce */ +#define BABEL_HMAC_INDEX_LEN 10 /* 80 bit indexes allowed */ +#define BABEL_HMAC_CHALLENGE_TIMEOUT (30 S_) +#define BABEL_HMAC_CHALLENGE_INTERVAL (300 MS_) + enum babel_tlv_type { BABEL_TLV_PAD1 = 0, @@ -73,13 +81,10 @@ enum babel_tlv_type { BABEL_TLV_UPDATE = 8, BABEL_TLV_ROUTE_REQUEST = 9, BABEL_TLV_SEQNO_REQUEST = 10, - /* extensions - not implemented - BABEL_TLV_TS_PC = 11, - BABEL_TLV_HMAC = 12, - BABEL_TLV_SS_UPDATE = 13, - BABEL_TLV_SS_REQUEST = 14, - BABEL_TLV_SS_SEQNO_REQUEST = 15, - */ + BABEL_TLV_CRYPTO_SEQNO = 121, + BABEL_TLV_HMAC = 122, + BABEL_TLV_CHALLENGE_REQ = 123, + BABEL_TLV_CHALLENGE_RESP = 124, BABEL_TLV_MAX }; @@ -137,6 +142,11 @@ struct babel_iface_config { ip_addr next_hop_ip4; ip_addr next_hop_ip6; + + u8 auth_type; /* Authentication type (BABEL_AUTH_*) */ + uint hmac_num_keys; /* Number of configured HMAC keys */ + uint hmac_total_len; /* Total digest length for all configured keys */ + list *passwords; /* Passwords for authentication */ }; struct babel_proto { @@ -184,6 +194,9 @@ struct babel_iface { u16 hello_seqno; /* To be increased on each hello */ + u32 hmac_pc; + u8 hmac_index[BABEL_HMAC_INDEX_LEN]; + btime next_hello; btime next_regular; btime next_triggered; @@ -207,6 +220,14 @@ struct babel_neighbor { u16 hello_map; u16 next_hello_seqno; uint last_hello_int; + + u32 hmac_pc; + u8 hmac_index_len; + u8 hmac_index[BABEL_HMAC_INDEX_LEN]; + u8 hmac_nonce[BABEL_HMAC_NONCE_LEN]; + btime hmac_nonce_expiry; + btime hmac_next_challenge; + /* expiry timers */ btime hello_expiry; btime ihu_expiry; @@ -339,6 +360,23 @@ struct babel_msg_seqno_request { ip_addr sender; }; +struct babel_msg_challenge { + u8 type; + u8 nonce_len; + u8 *nonce; +}; + +struct babel_msg_hmac { + u8 type; + ip_addr sender; + u32 pc; + u8 pc_seen; + u8 index_len; + u8 *index; + u8 challenge_seen; + u8 challenge[BABEL_HMAC_NONCE_LEN]; +}; + union babel_msg { u8 type; struct babel_msg_ack_req ack_req; @@ -348,6 +386,8 @@ union babel_msg { struct babel_msg_update update; struct babel_msg_route_request route_request; struct babel_msg_seqno_request seqno_request; + struct babel_msg_challenge challenge; + struct babel_msg_hmac hmac; }; struct babel_msg_node { @@ -367,6 +407,10 @@ void babel_handle_router_id(union babel_msg *msg, struct babel_iface *ifa); void babel_handle_update(union babel_msg *msg, struct babel_iface *ifa); void babel_handle_route_request(union babel_msg *msg, struct babel_iface *ifa); void babel_handle_seqno_request(union babel_msg *msg, struct babel_iface *ifa); +void babel_handle_hmac_challenge_req(union babel_msg *m, struct babel_iface *ifa); + +int babel_handle_hmac(union babel_msg *m, struct babel_iface *ifa); +void babel_hmac_reset_index(struct babel_iface *ifa); void babel_show_interfaces(struct proto *P, char *iff); void babel_show_neighbors(struct proto *P, char *iff); diff --git a/proto/babel/config.Y b/proto/babel/config.Y index 3af79fd6..36608bf4 100644 --- a/proto/babel/config.Y +++ b/proto/babel/config.Y @@ -25,7 +25,7 @@ CF_DECLS CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK, NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS, - ENTRIES, RANDOMIZE, ROUTER, ID) + ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, HMAC) CF_GRAMMAR @@ -91,6 +91,35 @@ babel_iface_finish: BABEL_IFACE->ihu_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_IHU_INTERVAL_FACTOR, BABEL_MAX_INTERVAL); BABEL_CFG->hold_time = MAX_(BABEL_CFG->hold_time, BABEL_IFACE->update_interval*BABEL_HOLD_TIME_FACTOR); + + BABEL_IFACE->passwords = get_passwords(); + + if (!BABEL_IFACE->auth_type != !BABEL_IFACE->passwords) + log(L_WARN "Authentication and password options should be used together"); + + if (BABEL_IFACE->passwords) + { + struct password_item *pass; + uint len = 0, i = 0; + WALK_LIST(pass, *BABEL_IFACE->passwords) + { + /* Set default crypto algorithm (HMAC-SHA256) */ + if (!pass->alg && (BABEL_IFACE->auth_type == BABEL_AUTH_HMAC)) + pass->alg = ALG_HMAC_SHA256; + + if (!(pass->alg & ALG_HMAC)) + cf_error("Only HMAC algorithms are supported"); + + if (BABEL_IFACE->auth_type != BABEL_AUTH_HMAC) + cf_error("Password setting requires HMAC authentication"); + + len += mac_type_length(pass->alg); + i++; + } + BABEL_IFACE->hmac_num_keys = i; + BABEL_IFACE->hmac_total_len = len; + } + }; @@ -109,6 +138,9 @@ babel_iface_item: | CHECK LINK bool { BABEL_IFACE->check_link = $3; } | NEXT HOP IPV4 ipa { BABEL_IFACE->next_hop_ip4 = $4; if (!ipa_is_ip4($4)) cf_error("Must be an IPv4 address"); } | NEXT HOP IPV6 ipa { BABEL_IFACE->next_hop_ip6 = $4; if (!ipa_is_ip6($4)) cf_error("Must be an IPv6 address"); } + | AUTHENTICATION NONE { BABEL_IFACE->auth_type = BABEL_AUTH_NONE; } + | AUTHENTICATION HMAC { BABEL_IFACE->auth_type = BABEL_AUTH_HMAC; } + | password_list { } ; babel_iface_opts: diff --git a/proto/babel/packets.c b/proto/babel/packets.c index 9c767874..a587be5c 100644 --- a/proto/babel/packets.c +++ b/proto/babel/packets.c @@ -11,6 +11,7 @@ */ #include "babel.h" +#include "lib/mac.h" struct babel_pkt_header { @@ -105,6 +106,26 @@ struct babel_tlv_seqno_request { u8 addr[0]; } PACKED; +struct babel_tlv_crypto_seqno { + u8 type; + u8 length; + u32 pc; + u8 index[0]; +} PACKED; + +struct babel_tlv_hmac { + u8 type; + u8 length; + u8 hmac[0]; +} PACKED; + +struct babel_tlv_challenge { + u8 type; + u8 length; + u8 nonce[0]; +} PACKED; + + struct babel_subtlv_source_prefix { u8 type; u8 length; @@ -135,6 +156,16 @@ struct babel_parse_state { u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */ u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */ u8 sadr_enabled; + u8 is_unicast; + /* HMAC data */ + u32 hmac_pc; + u8 hmac_pc_seen; + u8 hmac_index_len; + u8 *hmac_index; + u8 hmac_challenge_resp_seen; + u8 hmac_challenge_resp[BABEL_HMAC_NONCE_LEN]; + u8 hmac_challenge_len; + u8 *hmac_challenge; }; enum parse_result { @@ -152,6 +183,13 @@ struct babel_write_state { u8 def_ip6_pxlen; }; +struct babel_hmac_pseudohdr { + u8 src_addr[16]; + u16 src_port; + u8 dst_addr[16]; + u16 dst_port; +} PACKED; + #define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0) #define DROP1(DSC) do { err_dsc = DSC; goto drop; } while(0) @@ -270,6 +308,9 @@ static int babel_read_update(struct babel_tlv *hdr, union babel_msg *msg, struct static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); +static int babel_read_crypto_seqno(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); +static int babel_read_challenge_req(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); +static int babel_read_challenge_resp(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); @@ -277,6 +318,7 @@ static uint babel_write_ihu(struct babel_tlv *hdr, union babel_msg *msg, struct static uint babel_write_update(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); +static uint babel_write_challenge(struct babel_tlv *hdr, union babel_msg *m, struct babel_write_state *state UNUSED, uint max_len); static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len); struct babel_tlv_data { @@ -341,6 +383,24 @@ static const struct babel_tlv_data tlv_data[BABEL_TLV_MAX] = { babel_write_seqno_request, babel_handle_seqno_request }, + [BABEL_TLV_CRYPTO_SEQNO] = { + sizeof(struct babel_tlv_crypto_seqno), + babel_read_crypto_seqno, + NULL, + NULL, + }, + [BABEL_TLV_CHALLENGE_REQ] = { + sizeof(struct babel_tlv_challenge), + babel_read_challenge_req, + babel_write_challenge, + NULL, + }, + [BABEL_TLV_CHALLENGE_RESP] = { + sizeof(struct babel_tlv_challenge), + babel_read_challenge_resp, + babel_write_challenge, + NULL, + }, }; static int @@ -1107,6 +1167,73 @@ babel_write_source_prefix(struct babel_tlv *hdr, net_addr *n, uint max_len) return len; } +static int +babel_read_crypto_seqno(struct babel_tlv *hdr, union babel_msg *m UNUSED, + struct babel_parse_state *state) +{ + struct babel_tlv_crypto_seqno *tlv = (void *) hdr; + + if (!state->hmac_pc_seen) { + state->hmac_pc_seen = 1; + state->hmac_pc = get_u32(&tlv->pc); + state->hmac_index_len = tlv->length - sizeof(state->hmac_pc); + state->hmac_index = tlv->index; + } + + return PARSE_IGNORE; +} + +static int +babel_read_challenge_req(struct babel_tlv *hdr, union babel_msg *m UNUSED, + struct babel_parse_state *state) +{ + struct babel_tlv_challenge *tlv = (void *) hdr; + + if (!state->is_unicast) { + DBG("Ignoring non-unicast challenge request from %I", state->saddr); + return PARSE_IGNORE; + } + + if (!state->hmac_challenge_len) { + state->hmac_challenge_len = tlv->length; + state->hmac_challenge = tlv->nonce; + } + + return PARSE_IGNORE; +} + +static int +babel_read_challenge_resp(struct babel_tlv *hdr, union babel_msg *m UNUSED, + struct babel_parse_state *state) +{ + struct babel_tlv_challenge *tlv = (void *) hdr; + + if (tlv->length == BABEL_HMAC_NONCE_LEN && !state->hmac_challenge_resp_seen) + { + state->hmac_challenge_resp_seen = 1; + memcpy(state->hmac_challenge_resp, tlv->nonce, BABEL_HMAC_NONCE_LEN); + } + + return PARSE_IGNORE; +} + +static uint +babel_write_challenge(struct babel_tlv *hdr, union babel_msg *m, + struct babel_write_state *state UNUSED, uint max_len) +{ + struct babel_tlv_challenge *tlv = (void *) hdr; + struct babel_msg_challenge *msg = &m->challenge; + + uint len = sizeof(struct babel_tlv_challenge) + msg->nonce_len; + + if (len > max_len) + return 0; + + TLV_HDR(tlv, msg->type, len); + memcpy(tlv->nonce, msg->nonce, msg->nonce_len); + + return len; +} static inline int babel_read_subtlvs(struct babel_tlv *hdr, @@ -1189,6 +1316,210 @@ babel_write_tlv(struct babel_tlv *hdr, } +static int +babel_hmac_hash(struct password_item *pass, + struct babel_pkt_header *pkt, + ip_addr saddr, u16 sport, + ip_addr daddr, u16 dport, + byte *buf, uint *buf_len) +{ + struct mac_context ctx; + struct babel_hmac_pseudohdr phdr = {}; + int pkt_len = get_u16(&pkt->length) + sizeof(struct babel_pkt_header); + + put_ip6(phdr.src_addr, saddr); + put_u16(&phdr.src_port, sport); + put_ip6(phdr.dst_addr, daddr); + put_u16(&phdr.dst_port, dport); + DBG("HMAC pseudo-header: %I %d %I %d\n", saddr, sport, daddr, dport); + + if (mac_type_length(pass->alg) > *buf_len) + return 1; + + mac_init(&ctx, pass->alg, pass->password, pass->length); + mac_update(&ctx, (byte *)&phdr, sizeof(phdr)); + mac_update(&ctx, (byte *)pkt, pkt_len); + + *buf_len = mac_get_length(&ctx); + memcpy(buf, mac_final(&ctx), *buf_len); + + mac_cleanup(&ctx); + + return 0; +} + +static int +babel_hmac_check(struct babel_iface *ifa, + struct babel_pkt_header *pkt, + ip_addr saddr, u16 sport, + ip_addr daddr, u16 dport, + byte *start, uint len) +{ + struct babel_proto *p = ifa->proto; + struct password_item *pass; + struct babel_tlv *tlv; + struct babel_tlv_hmac *hmac; + btime now_ = current_real_time(); + byte *end = start + len; + + TRACE(D_PACKETS, "Checking packet HMAC signature"); + + if (len < sizeof(*tlv)) + return 1; + + WALK_LIST(pass, *ifa->cf->passwords) + { + byte mac_res[MAX_HASH_SIZE]; + uint mac_len = MAX_HASH_SIZE; + int res = 0; + + if (pass->accfrom > now_ || pass->accto < now_) + continue; + + if (babel_hmac_hash(pass, pkt, + saddr, sport, + daddr, dport, + mac_res, &mac_len)) + continue; + + LOOP_TLVS(start, end, tlv) + { + if (tlv->type != BABEL_TLV_HMAC) + continue; + + hmac = (void *)tlv; + + res = (tlv->length == mac_len && !memcmp(hmac->hmac, mac_res, mac_len)); + if (res) + break; + + DBG("HMAC mismatch key id %d pos %d len %d/%d\n", + pass->id, (byte *)tlv - (byte *)pkt, mac_len, tlv->length); + } + LOOP_TLVS_END; + + if (res) { + TRACE(D_PACKETS, "HMAC signature OK"); + return 0; + } + } + + TRACE(D_PACKETS, "No matching HMAC key found"); + return 1; + +frame_err: + DBG("HMAC trailer TLV framing error\n"); + return 1; +} + +static int +babel_hmac_check_state(struct babel_iface *ifa, struct babel_parse_state *state) +{ + struct babel_proto *p = ifa->proto; + union babel_msg msg = {}; + int ret; + + if (state->hmac_challenge_len) + { + union babel_msg resp = {}; + TRACE(D_PACKETS, "Sending HMAC challenge response to %I", state->saddr); + resp.type = BABEL_TLV_CHALLENGE_RESP; + resp.challenge.nonce_len = state->hmac_challenge_len; + resp.challenge.nonce = state->hmac_challenge; + babel_send_unicast(&resp, ifa, state->saddr); + } + + msg.type = BABEL_TLV_HMAC; + msg.hmac.sender = state->saddr; + msg.hmac.pc = state->hmac_pc; + msg.hmac.pc_seen = state->hmac_pc_seen; + msg.hmac.index_len = state->hmac_index_len; + msg.hmac.index = state->hmac_index; + msg.hmac.challenge_seen = state->hmac_challenge_resp_seen; + memcpy(msg.hmac.challenge, + state->hmac_challenge_resp, + BABEL_HMAC_NONCE_LEN); + + ret = babel_handle_hmac(&msg, ifa); + + return ret; +} + +static int +babel_hmac_add_pc(struct babel_iface *ifa, struct babel_tlv *tlv, int max_len) +{ + struct babel_proto *p = ifa->proto; + + struct babel_tlv_crypto_seqno *msg = (void *)tlv; + int len = sizeof(*msg) + BABEL_HMAC_INDEX_LEN; + + if (len > max_len) { + LOG_PKT("Warning: Insufficient space to add HMAC seqno TLV on iface %s: %d < %d", + ifa->ifname, max_len, len); + return 0; + } + + msg->type = BABEL_TLV_CRYPTO_SEQNO; + msg->length = len - sizeof(struct babel_tlv); + put_u32(&msg->pc, ifa->hmac_pc++); + memcpy(msg->index, ifa->hmac_index, BABEL_HMAC_INDEX_LEN); + + /* Reset index on overflow to 0 */ + if (!ifa->hmac_pc) + babel_hmac_reset_index(ifa); + + return len; +} + +static int +babel_hmac_sign(struct babel_iface *ifa, ip_addr dest) +{ + struct babel_proto *p = ifa->proto; + sock *sk = ifa->sk; + + struct babel_pkt_header *hdr = (void *) sk->tbuf; + int len = get_u16(&hdr->length) + sizeof(struct babel_pkt_header); + + byte *pos = (byte *)hdr + len; + byte *end = (byte *)hdr + ifa->tx_length; + struct babel_tlv *tlv = (void *)pos; + struct password_item *pass; + btime now_ = current_real_time(); + int tot_len = 0, i = 0; + + WALK_LIST(pass, *ifa->cf->passwords) + { + struct babel_tlv_hmac *msg = (void *)tlv; + uint buf_len = (uint) (end - (byte *)msg - sizeof(*msg)); + + if (pass->genfrom > now_ || pass->gento < now_) + continue; + + if (babel_hmac_hash(pass, hdr, + sk->saddr, sk->fport, + dest, sk->dport, + msg->hmac, &buf_len)) + { + LOG_PKT("Warning: Insufficient space for HMAC signatures on iface %s dest %I", + ifa->ifname, dest); + break; + } + + msg->type = BABEL_TLV_HMAC; + msg->length = buf_len; + + tlv = NEXT_TLV(tlv); + tot_len += buf_len + sizeof(*msg); + i++; + } + + TRACE(D_PACKETS, "Added %d HMAC signatures (%d bytes) on ifa %s for dest %I", + i, tot_len, ifa->ifname, dest); + + return tot_len; +} + + /* * Packet RX/TX functions */ @@ -1200,6 +1531,9 @@ babel_send_to(struct babel_iface *ifa, ip_addr dest) struct babel_pkt_header *hdr = (void *) sk->tbuf; int len = get_u16(&hdr->length) + sizeof(struct babel_pkt_header); + if (ifa->cf->auth_type == BABEL_AUTH_HMAC) + len += babel_hmac_sign(ifa, dest); + DBG("Babel: Sending %d bytes to %I\n", len, dest); return sk_send_to(sk, len, dest, 0); } @@ -1223,12 +1557,19 @@ babel_write_queue(struct babel_iface *ifa, list *queue) { struct babel_proto *p = ifa->proto; struct babel_write_state state = { .next_hop_ip6 = ifa->addr }; + int tx_length = ifa->tx_length; if (EMPTY_LIST(*queue)) return 0; + if (ifa->cf->auth_type == BABEL_AUTH_HMAC) + tx_length -= (sizeof(struct babel_tlv_crypto_seqno) + + BABEL_HMAC_INDEX_LEN + + ifa->cf->hmac_total_len + + sizeof(struct babel_tlv_hmac) * ifa->cf->hmac_num_keys); + byte *pos = ifa->sk->tbuf; - byte *end = pos + ifa->tx_length; + byte *end = pos + tx_length; struct babel_pkt_header *pkt = (void *) pos; pkt->magic = BABEL_MAGIC; @@ -1252,6 +1593,9 @@ babel_write_queue(struct babel_iface *ifa, list *queue) sl_free(p->msg_slab, msg); } + if (ifa->cf->auth_type == BABEL_AUTH_HMAC) + pos += babel_hmac_add_pc(ifa, (struct babel_tlv *) pos, end-pos); + uint plen = pos - (byte *) pkt; put_u16(&pkt->length, plen - sizeof(struct babel_pkt_header)); @@ -1329,10 +1673,13 @@ babel_enqueue(union babel_msg *msg, struct babel_iface *ifa) /** * babel_process_packet - process incoming data packet + * @ifa: Interface packet was received on. * @pkt: Pointer to the packet data * @len: Length of received packet * @saddr: Address of packet sender - * @ifa: Interface packet was received on. + * @sport: Packet source port + * @daddr: Destination address of packet + * @dport: Packet destination port * * This function is the main processing hook of incoming Babel packets. It * checks that the packet header is well-formed, then processes the TLVs @@ -1344,14 +1691,17 @@ babel_enqueue(union babel_msg *msg, struct babel_iface *ifa) * order. */ static void -babel_process_packet(struct babel_pkt_header *pkt, int len, - ip_addr saddr, struct babel_iface *ifa) +babel_process_packet(struct babel_iface *ifa, + struct babel_pkt_header *pkt, int len, + ip_addr saddr, u16 sport, + ip_addr daddr, u16 dport) { struct babel_proto *p = ifa->proto; struct babel_tlv *tlv; struct babel_msg_node *msg; list msgs; int res; + int hmac_ok = 1; int plen = sizeof(struct babel_pkt_header) + get_u16(&pkt->length); byte *end = (byte *)pkt + plen; @@ -1362,6 +1712,7 @@ babel_process_packet(struct babel_pkt_header *pkt, int len, .saddr = saddr, .next_hop_ip6 = saddr, .sadr_enabled = babel_sadr_enabled(p), + .is_unicast = !(ipa_classify(daddr) & IADDR_MULTICAST), }; if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION)) @@ -1381,6 +1732,11 @@ babel_process_packet(struct babel_pkt_header *pkt, int len, TRACE(D_PACKETS, "Packet received from %I via %s", saddr, ifa->iface->name); + /* First HMAC check - verify signature */ + if (ifa->cf->auth_type == BABEL_AUTH_HMAC && + babel_hmac_check(ifa, pkt, saddr, sport, daddr, dport, end, len-plen)) + return; + init_list(&msgs); /* First pass through the packet TLV by TLV, parsing each into internal data @@ -1410,10 +1766,14 @@ babel_process_packet(struct babel_pkt_header *pkt, int len, frame_err: + /* Second HMAC check - verify sequence counter state */ + if (ifa->cf->auth_type == BABEL_AUTH_HMAC) + hmac_ok = babel_hmac_check_state(ifa, &state); + /* Parsing done, handle all parsed TLVs */ WALK_LIST_FIRST(msg, msgs) { - if (tlv_data[msg->msg.type].handle_tlv) + if (hmac_ok && tlv_data[msg->msg.type].handle_tlv) tlv_data[msg->msg.type].handle_tlv(&msg->msg, ifa); rem_node(NODE msg); sl_free(p->msg_slab, msg); @@ -1473,7 +1833,10 @@ babel_rx_hook(sock *sk, uint len) if (sk->flags & SKF_TRUNCATED) DROP("truncated", len); - babel_process_packet((struct babel_pkt_header *) sk->rbuf, len, sk->faddr, ifa); + babel_process_packet(ifa, + (struct babel_pkt_header *) sk->rbuf, len, + sk->faddr, sk->fport, + sk->laddr, sk->dport); return 1; drop: