summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToke Høiland-Jørgensen <toke@toke.dk>2021-04-15 20:15:53 +0200
committerOndrej Zajicek (work) <santiago@crfreenet.org>2021-06-06 16:28:18 +0200
commit69d10132a6020e00ea2e8f899fdebf8128329699 (patch)
tree1f384463f1726a6cd26354ece4b3b4041ae8fa35
parent589f7d1e4f3aaca3fec6c38474bb962a9c578ebe (diff)
Babel: Refactor TLV parsing code for easier reuse
In preparation for adding authentication checks, refactor the TLV walking code so it can be reused for a separate pass of the packet for authentication checks.
-rw-r--r--proto/babel/packets.c171
1 files changed, 107 insertions, 64 deletions
diff --git a/proto/babel/packets.c b/proto/babel/packets.c
index 415ac3f9..1d2f5f5b 100644
--- a/proto/babel/packets.c
+++ b/proto/babel/packets.c
@@ -120,8 +120,19 @@ struct babel_subtlv_source_prefix {
#define BABEL_UF_DEF_PREFIX 0x80
#define BABEL_UF_ROUTER_ID 0x40
+struct babel_parse_state;
+struct babel_write_state;
+
+struct babel_tlv_data {
+ u8 min_length;
+ int (*read_tlv)(struct babel_tlv *hdr, union babel_msg *m, struct babel_parse_state *state);
+ uint (*write_tlv)(struct babel_tlv *hdr, union babel_msg *m, struct babel_write_state *state, uint max_len);
+ void (*handle_tlv)(union babel_msg *m, struct babel_iface *ifa);
+};
struct babel_parse_state {
+ const struct babel_tlv_data* (*get_tlv_data)(u8 type);
+ const struct babel_tlv_data* (*get_subtlv_data)(u8 type);
struct babel_proto *proto;
struct babel_iface *ifa;
ip_addr saddr;
@@ -167,6 +178,37 @@ struct babel_write_state {
#define NET_SIZE(n) BYTES(net_pxlen(n))
+
+/* Helper macros to loop over a series of TLVs.
+ * @start pointer to first TLV (void * or struct babel_tlv *)
+ * @end byte * pointer to TLV stream end
+ * @tlv struct babel_tlv pointer used as iterator
+ * @frame_err boolean (u8) that will be set to 1 if a frame error occurred
+ * @saddr source addr for use in log output
+ * @ifname ifname for use in log output
+ */
+#define WALK_TLVS(start, end, tlv, frame_err, saddr, ifname) \
+ for (tlv = start; \
+ (byte *)tlv < end; \
+ tlv = NEXT_TLV(tlv)) \
+ { \
+ byte *loop_pos; \
+ /* Ugly special case */ \
+ if (tlv->type == BABEL_TLV_PAD1) \
+ continue; \
+ \
+ /* The end of the common TLV header */ \
+ loop_pos = (byte *)tlv + sizeof(struct babel_tlv); \
+ if ((loop_pos > end) || (loop_pos + tlv->length > end)) \
+ { \
+ LOG_PKT("Bad TLV from %I via %s type %d pos %d - framing error", \
+ saddr, ifname, tlv->type, (byte *)tlv - (byte *)start); \
+ frame_err = 1; \
+ break; \
+ }
+
+#define WALK_TLVS_END }
+
static inline uint
bytes_equal(u8 *b1, u8 *b2, uint maxlen)
{
@@ -255,13 +297,6 @@ static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *ms
static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len);
-struct babel_tlv_data {
- u8 min_length;
- int (*read_tlv)(struct babel_tlv *hdr, union babel_msg *m, struct babel_parse_state *state);
- uint (*write_tlv)(struct babel_tlv *hdr, union babel_msg *m, struct babel_write_state *state, uint max_len);
- void (*handle_tlv)(union babel_msg *m, struct babel_iface *ifa);
-};
-
static const struct babel_tlv_data tlv_data[BABEL_TLV_MAX] = {
[BABEL_TLV_ACK_REQ] = {
sizeof(struct babel_tlv_ack_req),
@@ -319,6 +354,30 @@ static const struct babel_tlv_data tlv_data[BABEL_TLV_MAX] = {
},
};
+static const struct babel_tlv_data *get_packet_tlv_data(u8 type)
+{
+ return type < sizeof(tlv_data) / sizeof(*tlv_data) ? &tlv_data[type] : NULL;
+}
+
+static const struct babel_tlv_data source_prefix_tlv_data = {
+ sizeof(struct babel_subtlv_source_prefix),
+ babel_read_source_prefix,
+ NULL,
+ NULL
+};
+
+static const struct babel_tlv_data *get_packet_subtlv_data(u8 type)
+{
+ switch(type)
+ {
+ case BABEL_SUBTLV_SOURCE_PREFIX:
+ return &source_prefix_tlv_data;
+
+ default:
+ return NULL;
+ }
+}
+
static int
babel_read_ack_req(struct babel_tlv *hdr, union babel_msg *m,
struct babel_parse_state *state)
@@ -1083,69 +1142,65 @@ babel_write_source_prefix(struct babel_tlv *hdr, net_addr *n, uint max_len)
return len;
}
-
static inline int
babel_read_subtlvs(struct babel_tlv *hdr,
union babel_msg *msg,
struct babel_parse_state *state)
{
+ const struct babel_tlv_data *tlv_data;
+ struct babel_proto *p = state->proto;
struct babel_tlv *tlv;
- byte *pos, *end = (byte *) hdr + TLV_LENGTH(hdr);
+ byte *end = (byte *) hdr + TLV_LENGTH(hdr);
+ u8 frame_err = 0;
int res;
- for (tlv = (void *) hdr + state->current_tlv_endpos;
- (byte *) tlv < end;
- tlv = NEXT_TLV(tlv))
+ WALK_TLVS((void *)hdr + state->current_tlv_endpos, end, tlv, frame_err,
+ state->saddr, state->ifa->ifname)
{
- /* Ugly special case */
- if (tlv->type == BABEL_TLV_PAD1)
+ if (tlv->type == BABEL_SUBTLV_PADN)
continue;
- /* The end of the common TLV header */
- pos = (byte *)tlv + sizeof(struct babel_tlv);
- if ((pos > end) || (pos + tlv->length > end))
- return PARSE_ERROR;
-
- /*
- * The subtlv type space is non-contiguous (due to the mandatory bit), so
- * use a switch for dispatch instead of the mapping array we use for TLVs
- */
- switch (tlv->type)
+ if (!state->get_subtlv_data ||
+ !(tlv_data = state->get_subtlv_data(tlv->type)) ||
+ !tlv_data->read_tlv)
{
- case BABEL_SUBTLV_SOURCE_PREFIX:
- res = babel_read_source_prefix(tlv, msg, state);
- if (res != PARSE_SUCCESS)
- return res;
- break;
-
- case BABEL_SUBTLV_PADN:
- default:
/* Unknown mandatory subtlv; PARSE_IGNORE ignores the whole TLV */
if (tlv->type >= 128)
- return PARSE_IGNORE;
- break;
+ return PARSE_IGNORE;
+ continue;
}
+
+ res = tlv_data->read_tlv(tlv, msg, state);
+ if (res != PARSE_SUCCESS)
+ return res;
}
+ WALK_TLVS_END;
- return PARSE_SUCCESS;
+ return frame_err ? PARSE_ERROR : PARSE_SUCCESS;
}
-static inline int
+static int
babel_read_tlv(struct babel_tlv *hdr,
union babel_msg *msg,
struct babel_parse_state *state)
{
+ const struct babel_tlv_data *tlv_data;
+
if ((hdr->type <= BABEL_TLV_PADN) ||
- (hdr->type >= BABEL_TLV_MAX) ||
- !tlv_data[hdr->type].read_tlv)
+ (hdr->type >= BABEL_TLV_MAX))
return PARSE_IGNORE;
- if (TLV_LENGTH(hdr) < tlv_data[hdr->type].min_length)
+ tlv_data = state->get_tlv_data(hdr->type);
+
+ if (!tlv_data || !tlv_data->read_tlv)
+ return PARSE_IGNORE;
+
+ if (TLV_LENGTH(hdr) < tlv_data->min_length)
return PARSE_ERROR;
- state->current_tlv_endpos = tlv_data[hdr->type].min_length;
+ state->current_tlv_endpos = tlv_data->min_length;
- int res = tlv_data[hdr->type].read_tlv(hdr, msg, state);
+ int res = tlv_data->read_tlv(hdr, msg, state);
if (res != PARSE_SUCCESS)
return res;
@@ -1330,6 +1385,7 @@ static void
babel_process_packet(struct babel_pkt_header *pkt, int len,
ip_addr saddr, struct babel_iface *ifa)
{
+ u8 frame_err UNUSED = 0;
struct babel_proto *p = ifa->proto;
struct babel_tlv *tlv;
struct babel_msg_node *msg;
@@ -1337,15 +1393,16 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
int res;
int plen = sizeof(struct babel_pkt_header) + get_u16(&pkt->length);
- byte *pos;
byte *end = (byte *)pkt + plen;
struct babel_parse_state state = {
- .proto = p,
- .ifa = ifa,
- .saddr = saddr,
- .next_hop_ip6 = saddr,
- .sadr_enabled = babel_sadr_enabled(p),
+ .get_tlv_data = &get_packet_tlv_data,
+ .get_subtlv_data = &get_packet_subtlv_data,
+ .proto = p,
+ .ifa = ifa,
+ .saddr = saddr,
+ .next_hop_ip6 = saddr,
+ .sadr_enabled = babel_sadr_enabled(p),
};
if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))
@@ -1369,23 +1426,8 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
/* First pass through the packet TLV by TLV, parsing each into internal data
structures. */
- for (tlv = FIRST_TLV(pkt);
- (byte *)tlv < end;
- tlv = NEXT_TLV(tlv))
+ WALK_TLVS(FIRST_TLV(pkt), end, tlv, frame_err, saddr, ifa->iface->name)
{
- /* Ugly special case */
- if (tlv->type == BABEL_TLV_PAD1)
- continue;
-
- /* The end of the common TLV header */
- pos = (byte *)tlv + sizeof(struct babel_tlv);
- if ((pos > end) || (pos + tlv->length > end))
- {
- LOG_PKT("Bad TLV from %I via %s type %d pos %d - framing error",
- saddr, ifa->iface->name, tlv->type, (byte *)tlv - (byte *)pkt);
- break;
- }
-
msg = sl_allocz(p->msg_slab);
res = babel_read_tlv(tlv, &msg->msg, &state);
if (res == PARSE_SUCCESS)
@@ -1405,8 +1447,9 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
break;
}
}
+ WALK_TLVS_END;
- /* Parsing done, handle all parsed TLVs */
+ /* Parsing done, handle all parsed TLVs, regardless of any errors */
WALK_LIST_FIRST(msg, msgs)
{
if (tlv_data[msg->msg.type].handle_tlv)