]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Babel: Parse sub-TLVs and skip TLVs with mandatory sub-TLV
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Fri, 9 Jun 2017 12:33:06 +0000 (14:33 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Fri, 9 Jun 2017 12:33:06 +0000 (14:33 +0200)
RFC6126bis formally introduces sub-TLVs to the Babel protocol, including
mandatory sub-TLVs. This adds support for parsing sub-TLVs to the Babel
protocol and skips TLVs that contain mandatory sub-TLVs, as per the spec.

For details, see section 4.4 of
https://tools.ietf.org/html/draft-ietf-babel-rfc6126bis-02

Thanks to Toke Høiland-Jørgensen <toke@toke.dk> for the patch.

proto/babel/babel.h
proto/babel/packets.c

index 26f52455ced31b36116f1a1f32e235651edeea07..fccb60c9e563c874f45448ce873584d3bf0bfa77 100644 (file)
@@ -78,6 +78,11 @@ enum babel_tlv_type {
   BABEL_TLV_MAX
 };
 
+enum babel_subtlv_type {
+  BABEL_SUBTLV_PAD1            = 0,
+  BABEL_SUBTLV_PADN            = 1
+};
+
 enum babel_iface_type {
   /* In practice, UNDEF and WIRED give equivalent behaviour */
   BABEL_IFACE_TYPE_UNDEF       = 0,
index 72ac4f2948cf21fdaee00b12724a6ab494ccb51a..3564c703c86cba877aa117bcc51bf99c8ca7b1b0 100644 (file)
@@ -120,6 +120,7 @@ struct babel_parse_state {
   u8 router_id_seen;           /* router_id field is valid */
   u8 def_ip6_prefix_seen;      /* def_ip6_prefix is valid */
   u8 def_ip4_prefix_seen;      /* def_ip4_prefix is valid */
+  u8 current_tlv_endpos;       /* End of self-terminating TLVs (offset from start) */
 };
 
 enum parse_result {
@@ -379,14 +380,33 @@ babel_read_ihu(struct babel_tlv *hdr, union babel_msg *m,
   if (msg->ae >= BABEL_AE_MAX)
     return PARSE_IGNORE;
 
-  // We handle link-local IPs. In every other case, the addr field will be 0 but
-  // validation will succeed. The handler takes care of these cases.
-  if (msg->ae == BABEL_AE_IP6_LL)
+  /*
+   * We only actually read link-local IPs. In every other case, the addr field
+   * will be 0 but validation will succeed. The handler takes care of these
+   * cases. We handle them here anyway because we need the length for parsing
+   * subtlvs.
+   */
+  switch (msg->ae)
   {
+  case BABEL_AE_IP4:
+    if (TLV_OPT_LENGTH(tlv) < 4)
+      return PARSE_ERROR;
+    state->current_tlv_endpos += 4;
+    break;
+
+  case BABEL_AE_IP6:
+    if (TLV_OPT_LENGTH(tlv) < 16)
+      return PARSE_ERROR;
+    state->current_tlv_endpos += 16;
+    break;
+
+  case BABEL_AE_IP6_LL:
     if (TLV_OPT_LENGTH(tlv) < 8)
       return PARSE_ERROR;
 
     msg->addr = ipa_from_ip6(get_ip6_ll(&tlv->addr));
+    state->current_tlv_endpos += 8;
+    break;
   }
 
   return PARSE_SUCCESS;
@@ -463,6 +483,7 @@ babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *m UNUSED,
       return PARSE_ERROR;
 
     state->next_hop_ip4 = ipa_from_ip4(get_ip4(&tlv->addr));
+    state->current_tlv_endpos += sizeof(ip4_addr);
     return PARSE_IGNORE;
 
   case BABEL_AE_IP6:
@@ -470,6 +491,7 @@ babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *m UNUSED,
       return PARSE_ERROR;
 
     state->next_hop_ip6 = ipa_from_ip6(get_ip6(&tlv->addr));
+    state->current_tlv_endpos += sizeof(ip6_addr);
     return PARSE_IGNORE;
 
   case BABEL_AE_IP6_LL:
@@ -477,6 +499,7 @@ babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *m UNUSED,
       return PARSE_ERROR;
 
     state->next_hop_ip6 = ipa_from_ip6(get_ip6_ll(&tlv->addr));
+    state->current_tlv_endpos += 8;
     return PARSE_IGNORE;
 
   default:
@@ -639,6 +662,7 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
 
   msg->router_id = state->router_id;
   msg->sender = state->saddr;
+  state->current_tlv_endpos += len;
 
   return PARSE_SUCCESS;
 }
@@ -765,6 +789,7 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m,
       return PARSE_ERROR;
 
     read_ip4_px(&msg->net, tlv->addr, tlv->plen);
+    state->current_tlv_endpos += BYTES(tlv->plen);
     return PARSE_SUCCESS;
 
   case BABEL_AE_IP6:
@@ -775,6 +800,7 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m,
       return PARSE_ERROR;
 
     read_ip6_px(&msg->net, tlv->addr, tlv->plen);
+    state->current_tlv_endpos += BYTES(tlv->plen);
     return PARSE_SUCCESS;
 
   case BABEL_AE_IP6_LL:
@@ -851,6 +877,7 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
       return PARSE_ERROR;
 
     read_ip4_px(&msg->net, tlv->addr, tlv->plen);
+    state->current_tlv_endpos += BYTES(tlv->plen);
     return PARSE_SUCCESS;
 
   case BABEL_AE_IP6:
@@ -861,6 +888,7 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
       return PARSE_ERROR;
 
     read_ip6_px(&msg->net, tlv->addr, tlv->plen);
+    state->current_tlv_endpos += BYTES(tlv->plen);
     return PARSE_SUCCESS;
 
   case BABEL_AE_IP6_LL:
@@ -907,6 +935,42 @@ babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
   return len;
 }
 
+static inline int
+babel_read_subtlvs(struct babel_tlv *hdr,
+                  union babel_msg *msg UNUSED,
+                  struct babel_parse_state *state)
+{
+  struct babel_tlv *tlv;
+
+  for (tlv = (void *) hdr + state->current_tlv_endpos;
+       tlv < hdr + TLV_LENGTH(hdr);
+       tlv = NEXT_TLV(tlv))
+  {
+    /*
+     * 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)
+    {
+    case BABEL_SUBTLV_PAD1:
+    case BABEL_SUBTLV_PADN:
+      /* FIXME: Framing errors in PADN are silently ignored, see babel_process_packet() */
+      break;
+
+    default:
+      /* Unknown mandatory subtlv; PARSE_IGNORE ignores the whole TLV */
+      if (tlv->type > 128)
+      {
+       DBG("Babel: Mandatory subtlv %d found; skipping TLV\n", tlv->type);
+       return PARSE_IGNORE;
+      }
+      break;
+    }
+  }
+
+  return PARSE_SUCCESS;
+}
+
 static inline int
 babel_read_tlv(struct babel_tlv *hdr,
                union babel_msg *msg,
@@ -920,8 +984,14 @@ babel_read_tlv(struct babel_tlv *hdr,
   if (TLV_LENGTH(hdr) < tlv_data[hdr->type].min_length)
     return PARSE_ERROR;
 
+  state->current_tlv_endpos = tlv_data[hdr->type].min_length;
   memset(msg, 0, sizeof(*msg));
-  return tlv_data[hdr->type].read_tlv(hdr, msg, state);
+
+  int res = tlv_data[hdr->type].read_tlv(hdr, msg, state);
+  if (res != PARSE_SUCCESS)
+    return res;
+
+  return babel_read_subtlvs(hdr, msg, state);
 }
 
 static uint